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