diff -r 000000000000 -r f9d778bb88a2 branches/gmyth-0.1b/src/gmyth_http.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/branches/gmyth-0.1b/src/gmyth_http.c Wed Feb 07 20:38:39 2007 +0000 @@ -0,0 +1,343 @@ +/** + * GMyth Library + * + * @file gmyth/gmyth_http.c + * + * @brief

GMythHttp class provides a wrapper to access + * data from the database using http+xml + * + * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia. + * @author Artur Duque de Souza <@indt.org.br> + * + */ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "gmyth_http.h" +#include "gmyth_debug.h" +#include "gmyth_socket.h" + + +xmlXPathObjectPtr +getnodeset(xmlDocPtr doc, xmlChar *xpath) +{ + + xmlXPathContextPtr context; + xmlXPathObjectPtr result; + + context = xmlXPathNewContext(doc); + result = xmlXPathEvalExpression(xpath, context); + + if(xmlXPathNodeSetIsEmpty(result->nodesetval)) + { + g_fprintf(stderr, "Error: No result at XPath\n"); + return NULL; + } + + xmlXPathFreeContext(context); + return result; +} + + +xmlDocPtr XMLParse (const char *content, int length) +{ + xmlDocPtr doc; /* the resulting document tree */ + + doc = xmlReadMemory(content, length, NULL, NULL, 0); + if (doc == NULL) + { + g_fprintf(stderr, "Error: Failed to parse XML document\n"); + return NULL; + } + + return doc; +} + +xmlXPathObjectPtr getXPath (xmlChar *xpath, xmlDocPtr doc) +{ + xmlXPathObjectPtr result; + result = getnodeset(doc, xpath); + return result; +} + +/** Retrieves the Progam List from the Channel + * + * @param nodeTab A pointer to a node inside the XML + * @return A GSList containing a list of all the programs + */ +GSList* get_Program_List(xmlNodePtr node) +{ + GSList* program_list = NULL; + + while (node != NULL) + { + if (g_ascii_strcasecmp((char *)node->name, "text") != 0) + { + GMythProgram* program = (GMythProgram*)g_malloc(sizeof(struct _GMythProgram)); + + program->title = g_string_new((char *)xmlGetProp(node, (xmlChar *)"title")); + program->subtitle = g_string_new((char *)xmlGetProp(node, (xmlChar *)"subtitle")); + program->catType = g_string_new((char *)xmlGetProp(node, (xmlChar *)"catType")); + program->category = g_string_new((char *)xmlGetProp(node, (xmlChar *)"category")); + + sscanf ((char *)xmlGetProp(node, (xmlChar *)"repeat"), "%d", &(program->repeat)); + + program->startTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(node, \ + (xmlChar *)"startTime")); + program->endTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(node, \ + (xmlChar *)"endTime")); + + program_list = g_slist_append(program_list, program); + } + + node = node->next; + } + + return program_list; +} + +/** Retrieves the Channel List from the ProgramGuide + * + * @param node A pointer to a node inside the XML + * @param epg The struct where is the current epg + * @return The epg from "param" updated + */ +void get_Channel_List (xmlNodePtr node, GMythEpg* epg) +{ + int i; + epg->channelList = NULL; + + for (i=1; i <= epg->numOfChannels; i++) + { + GMythChannel* channel = (GMythChannel*)g_malloc(sizeof(struct _GMythChannel)); + + channel->channelName = g_string_new((char *)xmlGetProp(node, (xmlChar *)"channelName")); + channel->chanNum = g_string_new((char *)xmlGetProp(node, (xmlChar *)"chanNum")); + + sscanf ((char *)xmlGetProp(node, (xmlChar *)"chanId"), "%d", &(channel->chanId)); + sscanf ((char *)xmlGetProp(node, (xmlChar *)"callSign"), "%d", &(channel->callSign)); + + channel->programList = get_Program_List(node->children); + + epg->channelList = g_slist_append(epg->channelList, channel); + } +} + +/** Retrieves the properties from the ProgramGuide + * + * @param nodeTab A pointer to a node inside the XML + * @param epg The struct where is the current epg + * @return The epg from "param" updated + */ +void get_ProgramGuide_Properties (xmlNodePtr nodeTab, GMythEpg* epg) +{ + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"startChanId"), "%d", &(epg->startChanId)); + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"endChanId"), "%d", &(epg->endChanId)); + + epg->version = g_string_new((char *)xmlGetProp(nodeTab, (xmlChar *)"version")); + + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"protoVer"), "%d", &(epg->protoVer)); + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"totalCount"), "%d", &(epg->totalCount)); + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"numOfChannels"), "%d", &(epg->numOfChannels)); + + epg->asOf = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"asOf")); + epg->startTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"startTime")); + epg->endTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"endTime")); + + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"details"), "%d", &(epg->details)); + + // go to Channel section and retrieve Channels and Programs + get_Channel_List(nodeTab->children->next->children->next, epg); +} + +/** Aux function to retrieve the Eletronic Program Guide + * + * @param doc An XML document (xmlDocPtr) + * @return The epg + */ +void getEpg (xmlDocPtr doc, GMythEpg* epg) +{ + xmlXPathObjectPtr result; + xmlNodeSetPtr nodeset; + xmlChar *keyword; + + int i; + result = getXPath((xmlChar *)"/*", doc); + + if (result) + { + nodeset = result->nodesetval; + for (i=0; i < nodeset->nodeNr; i++) + { + keyword = (xmlChar*)nodeset->nodeTab[i]->name; + if (g_ascii_strcasecmp((char *)keyword, "ProgramGuide") == 0) + get_ProgramGuide_Properties(nodeset->nodeTab[i], epg); + } + xmlXPathFreeObject (result); + } + +} + +/** Retrieves the Eletronic Program Guide from the backend + * + * @param doc An XML document (xmlDocPtr) + * @return The epg + */ +GMythEpg retrieve_epg (GMythBackendInfo *backend_info, int port, \ + GTimeVal* StartTime, GTimeVal* EndTime, \ + gint StartChanId, gint NumOfChannels, \ + gchar* Details) +{ + GMythEpg epg; + MemoryStruct chunk; + + chunk.memory=NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + + gchar* starttime = (gchar*)g_malloc(sizeof(gchar)*20); + starttime = gmyth_util_time_to_mythformat_from_time_val(StartTime); + + gchar* endtime = (gchar*)g_malloc(sizeof(gchar)*20); + endtime = gmyth_util_time_to_mythformat_from_time_val(EndTime); + + GString* command = g_string_new(""); + g_string_printf(command, "getProgramGuide?StartTime=%s&EndTime=%s&StartChanId=%d" + "&NumOfChannels=%d&Details=%s", starttime, endtime, \ + StartChanId, NumOfChannels, Details); + + chunk = gmyth_http_request(backend_info, command); + xmlDocPtr doc = XMLParse(chunk.memory, strlen(chunk.memory)); + getEpg(doc, &epg); + free(chunk.memory); + + return epg; +} + +/* Aux functions got from libcurl */ +void *myrealloc (void *ptr, size_t size) +{ + /* There might be a realloc() out there that doesn't like reallocing + NULL pointers, so we take care of it here */ + if(ptr) + return realloc(ptr, size); + else + return malloc(size); +} + +size_t +WriteMemoryCallback (void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + MemoryStruct *mem = (struct _MemoryStruct *)data; + + mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1); + if (mem->memory) + { + memcpy(&(mem->memory[mem->size]), ptr, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + + return realsize; +} + +/** Create a String containing the URL with the command + * + * @param command A string with the command + * @param variables Vars to use with their values \ + * MUST FINISH WITH NULL!!! + * @return The response + */ +GString* gmyth_http_create_command (GString *command, ...) +{ + va_list args; + va_start(args, command); + gchar* s = NULL; + + g_string_append(command, "?"); + + /* Sintax: var, value */ + while ( (s = va_arg(args, gchar *)) != NULL ) + { + g_string_append(command, s); + g_string_append(command, "="); + s = va_arg(args, gchar *); + g_string_append(command, s); + g_string_append(command, "&"); + } + + free(s); + va_end(args); + + return command; +} + +/** Send HTTP Command and receives the result of it + * + * @return A string with the response from the server + * NULL if there is no response. + */ +MemoryStruct gmyth_http_request (GMythBackendInfo *backend_info, GString *command) +{ + LIBXML_TEST_VERSION + + size_t size = strlen(backend_info->hostname) + strlen(command->str) + 13; + gchar *URL = (gchar *)g_malloc(sizeof(gchar)*size); + g_snprintf(URL, size+1, "http://%s:6544/%s", backend_info->hostname, command->str); + + CURL *curl_handle; + + MemoryStruct chunk; + + chunk.memory=NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + + curl_global_init(CURL_GLOBAL_ALL); + + /* init the curl session */ + curl_handle = curl_easy_init(); + + /* specify URL to get */ + curl_easy_setopt(curl_handle, CURLOPT_URL, URL); + + /* send all data to this function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + + /* some servers don't like requests that are made without a user-agent + field, so we provide one */ + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + /* get it! */ + curl_easy_perform(curl_handle); + + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + + return chunk; +}