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: }