/**
* 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;
}