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