1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/branches/gmyth-0.1b/src/gmyth_http.c Wed Feb 07 20:38:39 2007 +0000
1.3 @@ -0,0 +1,343 @@
1.4 +/**
1.5 + * GMyth Library
1.6 + *
1.7 + * @file gmyth/gmyth_http.c
1.8 + *
1.9 + * @brief <p> GMythHttp class provides a wrapper to access
1.10 + * data from the database using http+xml
1.11 + *
1.12 + * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
1.13 + * @author Artur Duque de Souza <@indt.org.br>
1.14 + *
1.15 + */
1.16 +/*
1.17 + *
1.18 + * This program is free software; you can redistribute it and/or modify
1.19 + * it under the terms of the GNU Lesser General Public License as published by
1.20 + * the Free Software Foundation; either version 2 of the License, or
1.21 + * (at your option) any later version.
1.22 + *
1.23 + * This program is distributed in the hope that it will be useful,
1.24 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.25 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.26 + * GNU General Public License for more details.
1.27 + *
1.28 + * You should have received a copy of the GNU Lesser General Public License
1.29 + * along with this program; if not, write to the Free Software
1.30 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1.31 + */
1.32 +
1.33 +#ifdef HAVE_CONFIG_H
1.34 +#include "config.h"
1.35 +#endif
1.36 +
1.37 +#include <assert.h>
1.38 +#include <libxml/parser.h>
1.39 +#include <libxml/tree.h>
1.40 +#include <libxml/xpath.h>
1.41 +
1.42 +#include "gmyth_http.h"
1.43 +#include "gmyth_debug.h"
1.44 +#include "gmyth_socket.h"
1.45 +
1.46 +
1.47 +xmlXPathObjectPtr
1.48 +getnodeset(xmlDocPtr doc, xmlChar *xpath)
1.49 +{
1.50 +
1.51 + xmlXPathContextPtr context;
1.52 + xmlXPathObjectPtr result;
1.53 +
1.54 + context = xmlXPathNewContext(doc);
1.55 + result = xmlXPathEvalExpression(xpath, context);
1.56 +
1.57 + if(xmlXPathNodeSetIsEmpty(result->nodesetval))
1.58 + {
1.59 + g_fprintf(stderr, "Error: No result at XPath\n");
1.60 + return NULL;
1.61 + }
1.62 +
1.63 + xmlXPathFreeContext(context);
1.64 + return result;
1.65 +}
1.66 +
1.67 +
1.68 +xmlDocPtr XMLParse (const char *content, int length)
1.69 +{
1.70 + xmlDocPtr doc; /* the resulting document tree */
1.71 +
1.72 + doc = xmlReadMemory(content, length, NULL, NULL, 0);
1.73 + if (doc == NULL)
1.74 + {
1.75 + g_fprintf(stderr, "Error: Failed to parse XML document\n");
1.76 + return NULL;
1.77 + }
1.78 +
1.79 + return doc;
1.80 +}
1.81 +
1.82 +xmlXPathObjectPtr getXPath (xmlChar *xpath, xmlDocPtr doc)
1.83 +{
1.84 + xmlXPathObjectPtr result;
1.85 + result = getnodeset(doc, xpath);
1.86 + return result;
1.87 +}
1.88 +
1.89 +/** Retrieves the Progam List from the Channel
1.90 + *
1.91 + * @param nodeTab A pointer to a node inside the XML
1.92 + * @return A GSList containing a list of all the programs
1.93 + */
1.94 +GSList* get_Program_List(xmlNodePtr node)
1.95 +{
1.96 + GSList* program_list = NULL;
1.97 +
1.98 + while (node != NULL)
1.99 + {
1.100 + if (g_ascii_strcasecmp((char *)node->name, "text") != 0)
1.101 + {
1.102 + GMythProgram* program = (GMythProgram*)g_malloc(sizeof(struct _GMythProgram));
1.103 +
1.104 + program->title = g_string_new((char *)xmlGetProp(node, (xmlChar *)"title"));
1.105 + program->subtitle = g_string_new((char *)xmlGetProp(node, (xmlChar *)"subtitle"));
1.106 + program->catType = g_string_new((char *)xmlGetProp(node, (xmlChar *)"catType"));
1.107 + program->category = g_string_new((char *)xmlGetProp(node, (xmlChar *)"category"));
1.108 +
1.109 + sscanf ((char *)xmlGetProp(node, (xmlChar *)"repeat"), "%d", &(program->repeat));
1.110 +
1.111 + program->startTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(node, \
1.112 + (xmlChar *)"startTime"));
1.113 + program->endTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(node, \
1.114 + (xmlChar *)"endTime"));
1.115 +
1.116 + program_list = g_slist_append(program_list, program);
1.117 + }
1.118 +
1.119 + node = node->next;
1.120 + }
1.121 +
1.122 + return program_list;
1.123 +}
1.124 +
1.125 +/** Retrieves the Channel List from the ProgramGuide
1.126 + *
1.127 + * @param node A pointer to a node inside the XML
1.128 + * @param epg The struct where is the current epg
1.129 + * @return The epg from "param" updated
1.130 + */
1.131 +void get_Channel_List (xmlNodePtr node, GMythEpg* epg)
1.132 +{
1.133 + int i;
1.134 + epg->channelList = NULL;
1.135 +
1.136 + for (i=1; i <= epg->numOfChannels; i++)
1.137 + {
1.138 + GMythChannel* channel = (GMythChannel*)g_malloc(sizeof(struct _GMythChannel));
1.139 +
1.140 + channel->channelName = g_string_new((char *)xmlGetProp(node, (xmlChar *)"channelName"));
1.141 + channel->chanNum = g_string_new((char *)xmlGetProp(node, (xmlChar *)"chanNum"));
1.142 +
1.143 + sscanf ((char *)xmlGetProp(node, (xmlChar *)"chanId"), "%d", &(channel->chanId));
1.144 + sscanf ((char *)xmlGetProp(node, (xmlChar *)"callSign"), "%d", &(channel->callSign));
1.145 +
1.146 + channel->programList = get_Program_List(node->children);
1.147 +
1.148 + epg->channelList = g_slist_append(epg->channelList, channel);
1.149 + }
1.150 +}
1.151 +
1.152 +/** Retrieves the properties from the ProgramGuide
1.153 + *
1.154 + * @param nodeTab A pointer to a node inside the XML
1.155 + * @param epg The struct where is the current epg
1.156 + * @return The epg from "param" updated
1.157 + */
1.158 +void get_ProgramGuide_Properties (xmlNodePtr nodeTab, GMythEpg* epg)
1.159 +{
1.160 + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"startChanId"), "%d", &(epg->startChanId));
1.161 + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"endChanId"), "%d", &(epg->endChanId));
1.162 +
1.163 + epg->version = g_string_new((char *)xmlGetProp(nodeTab, (xmlChar *)"version"));
1.164 +
1.165 + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"protoVer"), "%d", &(epg->protoVer));
1.166 + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"totalCount"), "%d", &(epg->totalCount));
1.167 + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"numOfChannels"), "%d", &(epg->numOfChannels));
1.168 +
1.169 + epg->asOf = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"asOf"));
1.170 + epg->startTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"startTime"));
1.171 + epg->endTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"endTime"));
1.172 +
1.173 + sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"details"), "%d", &(epg->details));
1.174 +
1.175 + // go to Channel section and retrieve Channels and Programs
1.176 + get_Channel_List(nodeTab->children->next->children->next, epg);
1.177 +}
1.178 +
1.179 +/** Aux function to retrieve the Eletronic Program Guide
1.180 + *
1.181 + * @param doc An XML document (xmlDocPtr)
1.182 + * @return The epg
1.183 + */
1.184 +void getEpg (xmlDocPtr doc, GMythEpg* epg)
1.185 +{
1.186 + xmlXPathObjectPtr result;
1.187 + xmlNodeSetPtr nodeset;
1.188 + xmlChar *keyword;
1.189 +
1.190 + int i;
1.191 + result = getXPath((xmlChar *)"/*", doc);
1.192 +
1.193 + if (result)
1.194 + {
1.195 + nodeset = result->nodesetval;
1.196 + for (i=0; i < nodeset->nodeNr; i++)
1.197 + {
1.198 + keyword = (xmlChar*)nodeset->nodeTab[i]->name;
1.199 + if (g_ascii_strcasecmp((char *)keyword, "ProgramGuide") == 0)
1.200 + get_ProgramGuide_Properties(nodeset->nodeTab[i], epg);
1.201 + }
1.202 + xmlXPathFreeObject (result);
1.203 + }
1.204 +
1.205 +}
1.206 +
1.207 +/** Retrieves the Eletronic Program Guide from the backend
1.208 + *
1.209 + * @param doc An XML document (xmlDocPtr)
1.210 + * @return The epg
1.211 + */
1.212 +GMythEpg retrieve_epg (GMythBackendInfo *backend_info, int port, \
1.213 + GTimeVal* StartTime, GTimeVal* EndTime, \
1.214 + gint StartChanId, gint NumOfChannels, \
1.215 + gchar* Details)
1.216 +{
1.217 + GMythEpg epg;
1.218 + MemoryStruct chunk;
1.219 +
1.220 + chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
1.221 + chunk.size = 0; /* no data at this point */
1.222 +
1.223 + gchar* starttime = (gchar*)g_malloc(sizeof(gchar)*20);
1.224 + starttime = gmyth_util_time_to_mythformat_from_time_val(StartTime);
1.225 +
1.226 + gchar* endtime = (gchar*)g_malloc(sizeof(gchar)*20);
1.227 + endtime = gmyth_util_time_to_mythformat_from_time_val(EndTime);
1.228 +
1.229 + GString* command = g_string_new("");
1.230 + g_string_printf(command, "getProgramGuide?StartTime=%s&EndTime=%s&StartChanId=%d"
1.231 + "&NumOfChannels=%d&Details=%s", starttime, endtime, \
1.232 + StartChanId, NumOfChannels, Details);
1.233 +
1.234 + chunk = gmyth_http_request(backend_info, command);
1.235 + xmlDocPtr doc = XMLParse(chunk.memory, strlen(chunk.memory));
1.236 + getEpg(doc, &epg);
1.237 + free(chunk.memory);
1.238 +
1.239 + return epg;
1.240 +}
1.241 +
1.242 +/* Aux functions got from libcurl */
1.243 +void *myrealloc (void *ptr, size_t size)
1.244 +{
1.245 + /* There might be a realloc() out there that doesn't like reallocing
1.246 + NULL pointers, so we take care of it here */
1.247 + if(ptr)
1.248 + return realloc(ptr, size);
1.249 + else
1.250 + return malloc(size);
1.251 +}
1.252 +
1.253 +size_t
1.254 +WriteMemoryCallback (void *ptr, size_t size, size_t nmemb, void *data)
1.255 +{
1.256 + size_t realsize = size * nmemb;
1.257 + MemoryStruct *mem = (struct _MemoryStruct *)data;
1.258 +
1.259 + mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
1.260 + if (mem->memory)
1.261 + {
1.262 + memcpy(&(mem->memory[mem->size]), ptr, realsize);
1.263 + mem->size += realsize;
1.264 + mem->memory[mem->size] = 0;
1.265 + }
1.266 +
1.267 + return realsize;
1.268 +}
1.269 +
1.270 +/** Create a String containing the URL with the command
1.271 + *
1.272 + * @param command A string with the command
1.273 + * @param variables Vars to use with their values \
1.274 + * MUST FINISH WITH NULL!!!
1.275 + * @return The response
1.276 + */
1.277 +GString* gmyth_http_create_command (GString *command, ...)
1.278 +{
1.279 + va_list args;
1.280 + va_start(args, command);
1.281 + gchar* s = NULL;
1.282 +
1.283 + g_string_append(command, "?");
1.284 +
1.285 + /* Sintax: var, value */
1.286 + while ( (s = va_arg(args, gchar *)) != NULL )
1.287 + {
1.288 + g_string_append(command, s);
1.289 + g_string_append(command, "=");
1.290 + s = va_arg(args, gchar *);
1.291 + g_string_append(command, s);
1.292 + g_string_append(command, "&");
1.293 + }
1.294 +
1.295 + free(s);
1.296 + va_end(args);
1.297 +
1.298 + return command;
1.299 +}
1.300 +
1.301 +/** Send HTTP Command and receives the result of it
1.302 + *
1.303 + * @return A string with the response from the server
1.304 + * NULL if there is no response.
1.305 + */
1.306 +MemoryStruct gmyth_http_request (GMythBackendInfo *backend_info, GString *command)
1.307 +{
1.308 + LIBXML_TEST_VERSION
1.309 +
1.310 + size_t size = strlen(backend_info->hostname) + strlen(command->str) + 13;
1.311 + gchar *URL = (gchar *)g_malloc(sizeof(gchar)*size);
1.312 + g_snprintf(URL, size+1, "http://%s:6544/%s", backend_info->hostname, command->str);
1.313 +
1.314 + CURL *curl_handle;
1.315 +
1.316 + MemoryStruct chunk;
1.317 +
1.318 + chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
1.319 + chunk.size = 0; /* no data at this point */
1.320 +
1.321 + curl_global_init(CURL_GLOBAL_ALL);
1.322 +
1.323 + /* init the curl session */
1.324 + curl_handle = curl_easy_init();
1.325 +
1.326 + /* specify URL to get */
1.327 + curl_easy_setopt(curl_handle, CURLOPT_URL, URL);
1.328 +
1.329 + /* send all data to this function */
1.330 + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
1.331 +
1.332 + /* we pass our 'chunk' struct to the callback function */
1.333 + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
1.334 +
1.335 + /* some servers don't like requests that are made without a user-agent
1.336 + field, so we provide one */
1.337 + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
1.338 +
1.339 + /* get it! */
1.340 + curl_easy_perform(curl_handle);
1.341 +
1.342 + /* cleanup curl stuff */
1.343 + curl_easy_cleanup(curl_handle);
1.344 +
1.345 + return chunk;
1.346 +}