branches/gmyth-0.1b/src/gmyth_http.c
author rosfran
Tue Feb 06 00:33:35 2007 +0000 (2007-02-06)
branchtrunk
changeset 326 81b1f3006eb2
permissions -rw-r--r--
[svn r328] Some fixes on program info, and some memory clean ups.
     1 /**
     2  * GMyth Library
     3  *
     4  * @file gmyth/gmyth_http.c
     5  * 
     6  * @brief <p> GMythHttp class provides a wrapper to access
     7  * data from the database using http+xml
     8  *
     9  * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
    10  * @author Artur Duque de Souza <@indt.org.br>
    11  *
    12  */
    13 /*
    14  * 
    15  * This program is free software; you can redistribute it and/or modify
    16  * it under the terms of the GNU Lesser General Public License as published by
    17  * the Free Software Foundation; either version 2 of the License, or
    18  * (at your option) any later version.
    19  *
    20  * This program is distributed in the hope that it will be useful,
    21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    23  * GNU General Public License for more details.
    24  *
    25  * You should have received a copy of the GNU Lesser General Public License
    26  * along with this program; if not, write to the Free Software
    27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    28  */
    29  
    30 #ifdef HAVE_CONFIG_H
    31 #include "config.h"
    32 #endif
    33 
    34 #include <assert.h>
    35 #include <libxml/parser.h>
    36 #include <libxml/tree.h>
    37 #include <libxml/xpath.h>
    38 
    39 #include "gmyth_http.h"
    40 #include "gmyth_debug.h"
    41 #include "gmyth_socket.h"
    42 
    43 
    44 xmlXPathObjectPtr
    45 getnodeset(xmlDocPtr doc, xmlChar *xpath)
    46 {
    47 
    48     xmlXPathContextPtr context;
    49     xmlXPathObjectPtr result;
    50 
    51     context = xmlXPathNewContext(doc);
    52     result = xmlXPathEvalExpression(xpath, context);
    53 
    54     if(xmlXPathNodeSetIsEmpty(result->nodesetval))
    55     {
    56         g_fprintf(stderr, "Error: No result at XPath\n");
    57         return NULL;
    58     }
    59 
    60     xmlXPathFreeContext(context);
    61     return result;
    62 }
    63 
    64 
    65 xmlDocPtr XMLParse (const char *content, int length) 
    66 {
    67     xmlDocPtr doc; /* the resulting document tree */
    68 
    69     doc = xmlReadMemory(content, length, NULL, NULL, 0);
    70     if (doc == NULL) 
    71     {
    72         g_fprintf(stderr, "Error: Failed to parse XML document\n");
    73         return NULL;
    74     }
    75 
    76     return doc;
    77 }
    78 
    79 xmlXPathObjectPtr getXPath (xmlChar *xpath, xmlDocPtr doc)
    80 {
    81     xmlXPathObjectPtr result;
    82     result = getnodeset(doc, xpath);
    83     return result;
    84 }
    85 
    86 /** Retrieves the Progam List from the Channel
    87  *
    88  * @param nodeTab A pointer to a node inside the XML
    89  * @return A GSList containing a list of all the programs
    90  */
    91 GSList* get_Program_List(xmlNodePtr node)
    92 {
    93 	GSList* program_list = NULL;
    94 	
    95 	while (node != NULL)
    96 	{
    97 		if (g_ascii_strcasecmp((char *)node->name, "text") != 0)
    98 		{
    99 			GMythProgram* program = (GMythProgram*)g_malloc(sizeof(struct _GMythProgram));
   100 
   101 			program->title = g_string_new((char *)xmlGetProp(node, (xmlChar *)"title"));
   102 			program->subtitle = g_string_new((char *)xmlGetProp(node, (xmlChar *)"subtitle"));
   103 			program->catType = g_string_new((char *)xmlGetProp(node, (xmlChar *)"catType"));
   104     		program->category = g_string_new((char *)xmlGetProp(node, (xmlChar *)"category"));
   105 			
   106 			sscanf ((char *)xmlGetProp(node, (xmlChar *)"repeat"), "%d", &(program->repeat));
   107 
   108 			program->startTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(node, \
   109 																		(xmlChar *)"startTime"));
   110 			program->endTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(node, \
   111 																		(xmlChar *)"endTime"));
   112 
   113 			program_list = g_slist_append(program_list, program);
   114 		}
   115 
   116 		node = node->next;
   117 	}
   118 
   119 	return program_list;
   120 }
   121 
   122 /** Retrieves the Channel List from the ProgramGuide
   123  *
   124  * @param node A pointer to a node inside the XML
   125  * @param epg The struct where is the current epg
   126  * @return The epg from "param" updated
   127  */
   128 void get_Channel_List (xmlNodePtr node, GMythEpg* epg)
   129 {
   130 	int i;
   131 	epg->channelList = NULL;
   132 
   133 	for (i=1; i <= epg->numOfChannels; i++)
   134 	{
   135 		GMythChannel* channel = (GMythChannel*)g_malloc(sizeof(struct _GMythChannel));
   136 		
   137 		channel->channelName = g_string_new((char *)xmlGetProp(node, (xmlChar *)"channelName"));
   138 		channel->chanNum = g_string_new((char *)xmlGetProp(node, (xmlChar *)"chanNum"));
   139 
   140 		sscanf ((char *)xmlGetProp(node, (xmlChar *)"chanId"), "%d", &(channel->chanId));
   141 		sscanf ((char *)xmlGetProp(node, (xmlChar *)"callSign"), "%d", &(channel->callSign));
   142 
   143 		channel->programList = get_Program_List(node->children);
   144 
   145 		epg->channelList = g_slist_append(epg->channelList, channel);
   146 	}
   147 }
   148 
   149 /** Retrieves the properties from the ProgramGuide
   150  *
   151  * @param nodeTab A pointer to a node inside the XML
   152  * @param epg The struct where is the current epg
   153  * @return The epg from "param" updated
   154  */
   155 void get_ProgramGuide_Properties (xmlNodePtr nodeTab, GMythEpg* epg)
   156 {
   157 	sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"startChanId"), "%d", &(epg->startChanId));
   158 	sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"endChanId"), "%d", &(epg->endChanId));
   159 
   160 	epg->version = g_string_new((char *)xmlGetProp(nodeTab, (xmlChar *)"version"));
   161 
   162 	sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"protoVer"), "%d", &(epg->protoVer));
   163 	sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"totalCount"), "%d", &(epg->totalCount));
   164 	sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"numOfChannels"), "%d", &(epg->numOfChannels));
   165 
   166 	epg->asOf = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"asOf"));
   167 	epg->startTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"startTime"));
   168 	epg->endTime = gmyth_util_string_to_time_val ((char *)xmlGetProp(nodeTab, (xmlChar *)"endTime"));
   169 
   170 	sscanf ((char *)xmlGetProp(nodeTab, (xmlChar *)"details"), "%d", &(epg->details));
   171 
   172 	// go to Channel section and retrieve Channels and Programs
   173 	get_Channel_List(nodeTab->children->next->children->next, epg);
   174 }
   175 
   176 /** Aux function to retrieve the Eletronic Program Guide
   177  *
   178  * @param doc An XML document (xmlDocPtr)
   179  * @return The epg
   180  */
   181 void getEpg (xmlDocPtr doc, GMythEpg* epg)
   182 {
   183     xmlXPathObjectPtr result;
   184     xmlNodeSetPtr nodeset;
   185     xmlChar *keyword;
   186 
   187     int i;
   188     result = getXPath((xmlChar *)"/*", doc);
   189 
   190     if (result) 
   191     {
   192         nodeset = result->nodesetval;
   193         for (i=0; i < nodeset->nodeNr; i++) 
   194 		{
   195 			keyword = (xmlChar*)nodeset->nodeTab[i]->name;
   196 			if (g_ascii_strcasecmp((char *)keyword, "ProgramGuide") == 0)
   197 				get_ProgramGuide_Properties(nodeset->nodeTab[i], epg);
   198 		}
   199         xmlXPathFreeObject (result);
   200     }
   201 
   202 }
   203 
   204 /** Retrieves the Eletronic Program Guide from the backend
   205  *
   206  * @param doc An XML document (xmlDocPtr)
   207  * @return The epg
   208  */
   209 GMythEpg retrieve_epg (GMythBackendInfo *backend_info, int port, \
   210 						GTimeVal* StartTime, GTimeVal* EndTime, \
   211 						gint StartChanId, gint NumOfChannels, \
   212 						gchar* Details)
   213 {
   214 	GMythEpg epg;
   215 	MemoryStruct chunk;
   216 
   217     chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
   218     chunk.size = 0;    /* no data at this point */
   219 
   220 	gchar* starttime = (gchar*)g_malloc(sizeof(gchar)*20);
   221 	starttime = gmyth_util_time_to_mythformat_from_time_val(StartTime);
   222 
   223 	gchar* endtime = (gchar*)g_malloc(sizeof(gchar)*20);
   224 	endtime = gmyth_util_time_to_mythformat_from_time_val(EndTime);
   225 
   226 	GString* command = g_string_new("");
   227 	g_string_printf(command, "getProgramGuide?StartTime=%s&EndTime=%s&StartChanId=%d"
   228 							 "&NumOfChannels=%d&Details=%s", starttime, endtime, \
   229 							 StartChanId, NumOfChannels, Details);
   230 
   231 	chunk = gmyth_http_request(backend_info, command);
   232     xmlDocPtr doc = XMLParse(chunk.memory, strlen(chunk.memory));
   233     getEpg(doc, &epg);
   234     free(chunk.memory);
   235 
   236 	return epg;
   237 }
   238 
   239 /* Aux functions got from libcurl */
   240 void *myrealloc (void *ptr, size_t size)
   241 {
   242     /* There might be a realloc() out there that doesn't like reallocing
   243         NULL pointers, so we take care of it here */
   244     if(ptr)
   245         return realloc(ptr, size);
   246     else
   247         return malloc(size);
   248 }
   249 
   250 size_t
   251 WriteMemoryCallback (void *ptr, size_t size, size_t nmemb, void *data)
   252 {
   253 	size_t realsize = size * nmemb;
   254 	MemoryStruct *mem = (struct _MemoryStruct *)data;
   255 
   256 	mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
   257 	if (mem->memory) 
   258 	{
   259 		memcpy(&(mem->memory[mem->size]), ptr, realsize);
   260 		mem->size += realsize;
   261 		mem->memory[mem->size] = 0;
   262 	}
   263 
   264 	return realsize;
   265 }
   266 
   267 /** Create a String containing the URL with the command
   268  *
   269  * @param command A string with the command
   270  * @param variables Vars to use with their values \
   271  *                    MUST FINISH WITH NULL!!!
   272  * @return The response
   273  */
   274 GString* gmyth_http_create_command (GString *command, ...)
   275 {
   276     va_list args;
   277     va_start(args, command);
   278     gchar* s = NULL;
   279 
   280     g_string_append(command, "?");
   281 
   282     /* Sintax: var, value */
   283     while ( (s = va_arg(args, gchar *)) != NULL ) 
   284     {
   285         g_string_append(command, s);
   286         g_string_append(command, "=");
   287         s = va_arg(args, gchar *);
   288         g_string_append(command, s);
   289         g_string_append(command, "&");
   290     }
   291 
   292     free(s);
   293     va_end(args);
   294 
   295     return command;
   296 }
   297 
   298 /** Send HTTP Command and receives the result of it
   299  *
   300  * @return A string with the response from the server
   301  *          NULL if there is no response.
   302  */
   303 MemoryStruct gmyth_http_request (GMythBackendInfo *backend_info, GString *command)
   304 {
   305     LIBXML_TEST_VERSION
   306 
   307     size_t size = strlen(backend_info->hostname) + strlen(command->str) + 13;
   308     gchar *URL = (gchar *)g_malloc(sizeof(gchar)*size);
   309     g_snprintf(URL, size+1, "http://%s:6544/%s", backend_info->hostname, command->str);
   310 
   311     CURL *curl_handle;
   312     
   313     MemoryStruct chunk;
   314     
   315     chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
   316     chunk.size = 0;    /* no data at this point */
   317     
   318     curl_global_init(CURL_GLOBAL_ALL);
   319     
   320     /* init the curl session */
   321     curl_handle = curl_easy_init();
   322     
   323     /* specify URL to get */
   324     curl_easy_setopt(curl_handle, CURLOPT_URL, URL);
   325     
   326     /* send all data to this function  */
   327     curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
   328     
   329     /* we pass our 'chunk' struct to the callback function */
   330     curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
   331     
   332     /* some servers don't like requests that are made without a user-agent
   333         field, so we provide one */
   334     curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
   335     
   336     /* get it! */
   337     curl_easy_perform(curl_handle);
   338     
   339     /* cleanup curl stuff */
   340     curl_easy_cleanup(curl_handle);
   341 
   342     return chunk;
   343 }