summaryrefslogtreecommitdiff
path: root/src/libwebvi/pipecomponent.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libwebvi/pipecomponent.c')
-rw-r--r--src/libwebvi/pipecomponent.c689
1 files changed, 689 insertions, 0 deletions
diff --git a/src/libwebvi/pipecomponent.c b/src/libwebvi/pipecomponent.c
new file mode 100644
index 0000000..a0743da
--- /dev/null
+++ b/src/libwebvi/pipecomponent.c
@@ -0,0 +1,689 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include "pipecomponent.h"
+#include "menubuilder.h"
+#include "linkextractor.h"
+#include "mainmenu.h"
+#include "webvicontext.h"
+#include "version.h"
+
+#define WEBVID_USER_AGENT "libwebvi/" LIBWEBVI_VERSION " libcurl/" LIBCURL_VERSION
+
+#define INITIALIZE_PIPE(pipetype, process, finish, delete) \
+ pipetype *self = malloc(sizeof(pipetype)); \
+ memset(self, 0, sizeof(pipetype)); \
+ pipe_component_initialize(&self->pipe_data, (process), (finish), (delete))
+
+#define INITIALIZE_PIPE_WITH_FDSET(pipetype, process, finish, delete, fdset, handle_socket) \
+ pipetype *self = malloc(sizeof(pipetype)); \
+ memset(self, 0, sizeof(pipetype)); \
+ pipe_component_initialize_fdset(&self->pipe_data, (process), (finish), (delete), (fdset), (handle_socket))
+
+struct PipeDownloader {
+ PipeComponent pipe_data;
+ CURL *curl;
+ CURLM *curlmulti;
+};
+
+struct PipeLinkExtractor {
+ PipeComponent pipe_data;
+ LinkExtractor *link_extractor;
+};
+
+struct PipeCallbackWrapper {
+ PipeComponent pipe_data;
+ void *write_data;
+ void *finish_data;
+ ssize_t (*write_callback)(const char *, size_t, void *);
+ void (*finish_callback)(RequestState, void *);
+};
+
+struct PipeMainMenuDownloader {
+ PipeComponent pipe_data;
+ const WebviContext *context; /* borrowed reference */
+};
+
+struct PipeExternalDownloader {
+ PipeComponent pipe_data;
+ gchar *url;
+ int fd;
+};
+
+struct PipeLocalFile {
+ PipeComponent pipe_data;
+ gchar *filename;
+ int fd;
+};
+
+struct PipeLibquvi {
+ PipeComponent pipe_data;
+ gchar *url;
+ GPid pid;
+ gint quvi_output;
+ xmlParserCtxtPtr parser;
+};
+
+static void pipe_component_delete(PipeComponent *self);
+
+static gboolean pipe_link_extractor_append(PipeComponent *instance, char *buf, size_t len);
+static void pipe_link_extractor_finished(PipeComponent *instance, RequestState state);
+static void pipe_link_extractor_delete(PipeComponent *instance);
+
+static gboolean pipe_callback_wrapper_process(PipeComponent *instance,
+ char *buf, size_t len);
+static void pipe_callback_wrapper_finished(PipeComponent *instance,
+ RequestState state);
+static void pipe_callback_wrapper_delete(PipeComponent *instance);
+
+static void pipe_downloader_finished(PipeComponent *instance, RequestState state);
+static void pipe_downloader_delete(PipeComponent *instance);
+static size_t curl_write_wrapper(char *ptr, size_t size, size_t nmemb, void *userdata);
+
+static void pipe_mainmenu_downloader_delete(PipeComponent *instance);
+
+static void pipe_local_file_fdset(PipeComponent *instance, fd_set *readfd,
+ fd_set *writefd, fd_set *excfd, int *maxfd);
+static gboolean pipe_local_file_handle_socket(PipeComponent *instance,
+ int fd, int bitmask);
+static void pipe_local_file_delete(PipeComponent *instance);
+
+static void pipe_external_downloader_fdset(PipeComponent *instance,
+ fd_set *readfd, fd_set *writefd,
+ fd_set *excfd, int *maxfd);
+static gboolean pipe_external_downloader_handle_socket(
+ PipeComponent *instance, int fd, int bitmask);
+static void pipe_external_downloader_delete(PipeComponent *instance);
+
+static void pipe_libquvi_fdset(PipeComponent *instance, fd_set *readfd,
+ fd_set *writefd, fd_set *excfd, int *maxfd);
+static gboolean pipe_libquvi_handle_socket(PipeComponent *instance,
+ int fd, int bitmask);
+static gboolean pipe_libquvi_parse(PipeComponent *instance, char *buf, size_t len);
+static void pipe_libquvi_finished(PipeComponent *instance, RequestState state);
+static xmlChar *quvi_xml_get_stream_url(xmlDoc *doc);
+static xmlChar *quvi_xml_get_stream_title(xmlDoc *doc);
+static void pipe_libquvi_delete(PipeComponent *instance);
+
+static CURL *start_curl(const char *url, CURLM *curlmulti,
+ PipeComponent *instance);
+static void append_to_fdset(int fd, fd_set *fdset, int *maxfd);
+static gboolean read_from_fd_to_pipe(PipeComponent *instance,
+ int *instance_fd_ptr, int fd, int bitmask);
+
+
+void pipe_component_initialize(PipeComponent *self,
+ gboolean (*process_cb)(PipeComponent *, char *, size_t),
+ void (*done_cb)(PipeComponent *, RequestState),
+ void (*delete_cb)(PipeComponent *))
+{
+ g_assert(self);
+ g_assert(delete_cb);
+
+ memset(self, 0, sizeof(PipeComponent));
+ self->process = process_cb;
+ self->finished = done_cb;
+ self->delete = delete_cb;
+ self->state = WEBVISTATE_NOT_FINISHED;
+}
+
+void pipe_component_initialize_fdset(PipeComponent *self,
+ gboolean (*process_cb)(PipeComponent *, char *, size_t),
+ void (*done_cb)(PipeComponent *, RequestState),
+ void (*delete_cb)(PipeComponent *),
+ void (*fdset_cb)(PipeComponent *, fd_set *, fd_set *, fd_set *, int *),
+ gboolean (*handle_socket_cb)(PipeComponent *, int, int))
+{
+ pipe_component_initialize(self, process_cb, done_cb, delete_cb);
+ self->fdset = fdset_cb;
+ self->handle_socket = handle_socket_cb;
+}
+
+void pipe_component_set_next(PipeComponent *self, PipeComponent *next) {
+ g_assert(!self->next);
+ self->next = next;
+}
+
+void pipe_component_append(PipeComponent *self, char *buf, size_t length) {
+ if (self->state == WEBVISTATE_NOT_FINISHED) {
+ gboolean propagate = TRUE;
+ if (self->process)
+ propagate = self->process(self, buf, length);
+ if (propagate && self->next)
+ pipe_component_append(self->next, buf, length);
+ }
+}
+
+void pipe_component_finished(PipeComponent *self, RequestState state) {
+ if (self->state == WEBVISTATE_NOT_FINISHED) {
+ self->state = state;
+ if (self->finished)
+ self->finished(self, state);
+ if (self->next)
+ pipe_component_finished(self->next, state);
+ }
+}
+
+RequestState pipe_component_get_state(const PipeComponent *self) {
+ return self->state;
+}
+
+void pipe_fdset(PipeComponent *head, fd_set *readfd,
+ fd_set *writefd, fd_set *excfd, int *max_fd)
+{
+ PipeComponent *component = head;
+ PipeComponent *next;
+ while (component) {
+ next = component->next;
+ if (component->fdset) {
+ component->fdset(component, readfd, writefd, excfd, max_fd);
+ }
+ component = next;
+ }
+}
+
+gboolean pipe_handle_socket(PipeComponent *head, int sockfd, int ev_bitmask) {
+ PipeComponent *component = head;
+ PipeComponent *next;
+ while (component) {
+ next = component->next;
+ if (component->handle_socket) {
+ if (component->handle_socket(component, sockfd, ev_bitmask)) {
+ return TRUE;
+ }
+ }
+ component = next;
+ }
+ return FALSE;
+}
+
+void pipe_component_delete(PipeComponent *self) {
+ if (self && self->delete)
+ self->delete(self);
+}
+
+void pipe_delete_full(PipeComponent *head) {
+ PipeComponent *component = head;
+ PipeComponent *next;
+ while (component) {
+ next = component->next;
+ pipe_component_delete(component);
+ component = next;
+ }
+}
+
+
+/***** PipeLinkExtractor *****/
+
+
+PipeLinkExtractor *pipe_link_extractor_create(
+ const LinkTemplates *link_templates, const gchar *baseurl)
+{
+ INITIALIZE_PIPE(PipeLinkExtractor, pipe_link_extractor_append,
+ pipe_link_extractor_finished, pipe_link_extractor_delete);
+ self->link_extractor = link_extractor_create(link_templates, baseurl);
+ return self;
+}
+
+void pipe_link_extractor_delete(PipeComponent *instance) {
+ PipeLinkExtractor *self = (PipeLinkExtractor *)instance;
+ link_extractor_delete(self->link_extractor);
+ free(self);
+}
+
+gboolean pipe_link_extractor_append(PipeComponent *instance, char *buf, size_t len) {
+ PipeLinkExtractor *self = (PipeLinkExtractor *)instance;
+ link_extractor_append(self->link_extractor, buf, len);
+ return FALSE;
+}
+
+void pipe_link_extractor_finished(PipeComponent *instance,
+ RequestState state)
+{
+ PipeLinkExtractor *self = (PipeLinkExtractor *)instance;
+ if (state == WEBVISTATE_FINISHED_OK) {
+ GPtrArray *links = link_extractor_get_links(self->link_extractor);
+ MenuBuilder *menu_builder = menu_builder_create();
+ menu_builder_append_link_list(menu_builder, links);
+ char *menu = menu_builder_to_string(menu_builder);
+ if (self->pipe_data.next) {
+ pipe_component_append(self->pipe_data.next, menu, strlen(menu));
+ pipe_component_finished(self->pipe_data.next, state);
+ }
+ menu_builder_delete(menu_builder);
+ free(menu);
+ g_ptr_array_free(links, TRUE);
+ }
+}
+
+
+/***** PipeDownloader *****/
+
+
+PipeDownloader *pipe_downloader_create(const char *url, CURLM *curlmulti) {
+ INITIALIZE_PIPE(PipeDownloader, NULL, pipe_downloader_finished,
+ pipe_downloader_delete);
+ self->curl = start_curl(url, curlmulti, (PipeComponent *)self);
+ if (!self->curl) {
+ pipe_downloader_delete((PipeComponent *)self);
+ return NULL;
+ }
+ self->curlmulti = curlmulti;
+ return self;
+}
+
+void pipe_downloader_start(PipeDownloader *self) {
+ CURLMcode mcode = curl_multi_add_handle(self->curlmulti, self->curl);
+ if (mcode != CURLM_OK) {
+ pipe_component_finished(&self->pipe_data, WEBVISTATE_INTERNAL_ERROR);
+ }
+}
+
+size_t curl_write_wrapper(char *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ PipeComponent *pipedata = (PipeComponent *)userdata;
+ if (pipe_component_get_state(pipedata) == WEBVISTATE_NOT_FINISHED) {
+ pipe_component_append(pipedata, ptr, size*nmemb);
+ return size*nmemb;
+ } else {
+ return 0;
+ }
+}
+
+void pipe_downloader_finished(PipeComponent *instance, RequestState state) {
+ PipeDownloader *self = (PipeDownloader *)instance;
+ curl_multi_remove_handle(self->curlmulti, self->curl);
+}
+
+void pipe_downloader_delete(PipeComponent *instance) {
+ PipeDownloader *self = (PipeDownloader *)instance;
+ if (self->pipe_data.state == WEBVISTATE_NOT_FINISHED) {
+ curl_multi_remove_handle(self->curlmulti, self->curl);
+ }
+ curl_easy_cleanup(self->curl);
+ free(instance);
+}
+
+
+/***** PipeMainMenuDownloader *****/
+
+
+PipeMainMenuDownloader *pipe_mainmenu_downloader_create(WebviContext *context) {
+ INITIALIZE_PIPE(PipeMainMenuDownloader, NULL, NULL,
+ pipe_mainmenu_downloader_delete);
+ self->context = context;
+ return self;
+}
+
+void pipe_mainmenu_downloader_start(PipeMainMenuDownloader *self) {
+ char *mainmenu = build_mainmenu(webvi_context_get_template_path(self->context));
+ if (!mainmenu) {
+ pipe_component_finished(&self->pipe_data, WEBVISTATE_INTERNAL_ERROR);
+ return;
+ }
+
+ pipe_component_append(&self->pipe_data, mainmenu, strlen(mainmenu));
+ pipe_component_finished(&self->pipe_data, WEBVISTATE_FINISHED_OK);
+
+ g_free(mainmenu);
+}
+
+void pipe_mainmenu_downloader_delete(PipeComponent *instance) {
+ PipeMainMenuDownloader *self = (PipeMainMenuDownloader *)instance;
+ free(self);
+}
+
+
+/***** PipeCallbackWrapper *****/
+
+
+PipeCallbackWrapper *pipe_callback_wrapper_create(
+ ssize_t (*write_callback)(const char *, size_t, void *),
+ void *writedata,
+ void (*finish_callback)(RequestState, void *),
+ void *finishdata)
+{
+ INITIALIZE_PIPE(PipeCallbackWrapper,
+ pipe_callback_wrapper_process,
+ pipe_callback_wrapper_finished,
+ pipe_callback_wrapper_delete);
+ self->write_data = writedata;
+ self->finish_data = finishdata;
+ self->write_callback = write_callback;
+ self->finish_callback = finish_callback;
+ return self;
+}
+
+gboolean pipe_callback_wrapper_process(PipeComponent *instance,
+ char *buf, size_t len)
+{
+ PipeCallbackWrapper *self = (PipeCallbackWrapper *)instance;
+ if (self->write_callback)
+ self->write_callback(buf, len, self->write_data);
+ return TRUE;
+}
+
+void pipe_callback_wrapper_finished(PipeComponent *instance,
+ RequestState state)
+{
+ PipeCallbackWrapper *self = (PipeCallbackWrapper *)instance;
+ if (self->finish_callback)
+ self->finish_callback(state, self->finish_data);
+}
+
+void pipe_callback_wrapper_delete(PipeComponent *instance) {
+ PipeCallbackWrapper *self = (PipeCallbackWrapper *)instance;
+ free(self);
+}
+
+PipeLocalFile *pipe_local_file_create(const gchar *filename) {
+ INITIALIZE_PIPE_WITH_FDSET(PipeLocalFile, NULL, NULL,
+ pipe_local_file_delete,
+ pipe_local_file_fdset,
+ pipe_local_file_handle_socket);
+ self->filename = g_strdup(filename);
+ self->fd = -1;
+ return self;
+}
+
+void pipe_local_file_start(PipeLocalFile *self) {
+ if (!self->filename) {
+ pipe_component_finished((PipeComponent *)self, WEBVISTATE_NOT_FOUND);
+ }
+
+ self->fd = open(self->filename, O_RDONLY);
+ if (self->fd == -1) {
+ pipe_component_finished((PipeComponent *)self, WEBVISTATE_NOT_FOUND);
+ }
+}
+
+void pipe_local_file_fdset(PipeComponent *instance, fd_set *readfd,
+ fd_set *writefd, fd_set *excfd, int *maxfd)
+{
+ PipeLocalFile *self = (PipeLocalFile *)instance;
+ append_to_fdset(self->fd, readfd, maxfd);
+}
+
+gboolean pipe_local_file_handle_socket(PipeComponent *instance, int fd, int bitmask) {
+ PipeLocalFile *self = (PipeLocalFile *)instance;
+ return read_from_fd_to_pipe(instance, &self->fd, fd, bitmask);
+}
+
+void pipe_local_file_delete(PipeComponent *instance) {
+ PipeLocalFile *self = (PipeLocalFile *)instance;
+ g_free(self->filename);
+ if (self->fd != -1)
+ close(self->fd);
+ free(self);
+}
+
+
+/***** PipeExternalDownloader *****/
+
+
+PipeExternalDownloader *pipe_external_downloader_create(const gchar *url,
+ const gchar *command)
+{
+ INITIALIZE_PIPE_WITH_FDSET(PipeExternalDownloader, NULL, NULL,
+ pipe_external_downloader_delete,
+ pipe_external_downloader_fdset,
+ pipe_external_downloader_handle_socket);
+ self->url = g_strdup(url);
+ self->fd = -1;
+
+ g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
+ "external downloader:\nurl: %s\n%s", url, command);
+
+ return self;
+}
+
+void pipe_external_downloader_start(PipeExternalDownloader *self) {
+ // FIXME
+ pipe_component_finished((PipeComponent *)self, WEBVISTATE_INTERNAL_ERROR);
+}
+
+void pipe_external_downloader_fdset(PipeComponent *instance, fd_set *readfd,
+ fd_set *writefd, fd_set *excfd, int *maxfd)
+{
+ PipeExternalDownloader *self = (PipeExternalDownloader *)instance;
+ append_to_fdset(self->fd, readfd, maxfd);
+}
+
+gboolean pipe_external_downloader_handle_socket(PipeComponent *instance,
+ int fd, int bitmask)
+{
+ PipeExternalDownloader *self = (PipeExternalDownloader *)instance;
+ return read_from_fd_to_pipe(instance, &self->fd, fd, bitmask);
+}
+
+void pipe_external_downloader_delete(PipeComponent *instance) {
+ PipeExternalDownloader *self = (PipeExternalDownloader *)instance;
+ if (self->fd != -1)
+ close(self->fd);
+ g_free(self->url);
+ g_free(self);
+}
+
+
+/***** PipeLibquvi *****/
+
+
+PipeLibquvi *pipe_libquvi_create(const gchar *url) {
+ INITIALIZE_PIPE_WITH_FDSET(PipeLibquvi,
+ pipe_libquvi_parse,
+ pipe_libquvi_finished,
+ pipe_libquvi_delete,
+ pipe_libquvi_fdset,
+ pipe_libquvi_handle_socket);
+ self->url = g_strdup(url);
+ self->pid = -1;
+ self->quvi_output = -1;
+ return self;
+}
+
+void pipe_libquvi_start(PipeLibquvi *self) {
+ GError *error = NULL;
+ gchar *argv[] = {"quvi", "--xml", self->url};
+
+ g_spawn_async_with_pipes(NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL, &self->pid, NULL, &self->quvi_output,
+ NULL, &error);
+ if (error) {
+ g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+ "Calling quvi failed: %s", error->message);
+ pipe_component_finished((PipeComponent *)self, WEBVISTATE_SUBPROCESS_FAILED);
+ }
+}
+
+void pipe_libquvi_fdset(PipeComponent *instance, fd_set *readfd,
+ fd_set *writefd, fd_set *excfd, int *maxfd)
+{
+ PipeLibquvi *self = (PipeLibquvi *)instance;
+ append_to_fdset(self->quvi_output, readfd, maxfd);
+}
+
+gboolean pipe_libquvi_handle_socket(PipeComponent *instance,
+ int fd, int bitmask)
+{
+ PipeLibquvi *self = (PipeLibquvi *)instance;
+ return read_from_fd_to_pipe(instance, &self->quvi_output, fd, bitmask);
+}
+
+gboolean pipe_libquvi_parse(PipeComponent *instance, char *buf, size_t len) {
+ PipeLibquvi *self = (PipeLibquvi *)instance;
+ if (!self->parser) {
+ self->parser = xmlCreatePushParserCtxt(NULL, NULL, buf, len, "quvioutput.xml");
+ g_assert(self->parser);
+ } else {
+ xmlParseChunk(self->parser, buf, len, 0);
+ }
+
+ return FALSE;
+}
+
+void pipe_libquvi_finished(PipeComponent *instance, RequestState state) {
+ if (state != WEBVISTATE_FINISHED_OK) {
+ pipe_component_finished(instance, state);
+ return;
+ }
+
+ PipeLibquvi *self = (PipeLibquvi *)instance;
+ if (!self->parser) {
+ g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "No output from quvi!");
+ pipe_component_finished(instance, WEBVISTATE_SUBPROCESS_FAILED);
+ return;
+ }
+
+ xmlParseChunk(self->parser, NULL, 0, 1);
+ xmlDoc *doc = self->parser->myDoc;
+
+ xmlChar *dump;
+ int dumpLen;
+ xmlDocDumpMemory(doc, &dump, &dumpLen);
+ g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "quvi output:\n%s", dump);
+ xmlFree(dump);
+ dump = NULL;
+
+ xmlChar *encoded_url = quvi_xml_get_stream_url(doc);
+ if (!encoded_url) {
+ g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "No url in quvi output!");
+ pipe_component_finished(instance, WEBVISTATE_SUBPROCESS_FAILED);
+ return;
+ }
+
+ /* URLs in quvi XML output are encoded by curl_easy_escape */
+ char *url = curl_unescape((char *)encoded_url, strlen((char *)encoded_url));
+ xmlChar *title = quvi_xml_get_stream_title(doc);
+
+ MenuBuilder *menu_builder = menu_builder_create();
+ menu_builder_set_title(menu_builder, (char *)title);
+ menu_builder_append_link_plain(menu_builder, url, (char *)title, NULL);
+ char *menu = menu_builder_to_string(menu_builder);
+ if (self->pipe_data.next) {
+ pipe_component_append(self->pipe_data.next, menu, strlen(menu));
+ pipe_component_finished(self->pipe_data.next, state);
+ }
+
+ free(menu);
+ menu_builder_delete(menu_builder);
+ xmlFree(title);
+ curl_free(url);
+ xmlFree(encoded_url);
+}
+
+xmlChar *quvi_xml_get_stream_url(xmlDoc *doc) {
+ xmlNode *root = xmlDocGetRootElement(doc);
+ xmlNode *node = root->children;
+ while (node) {
+ if (xmlStrEqual(node->name, BAD_CAST "link")) {
+ xmlNode *link_child = node->children;
+ while (link_child) {
+ if (xmlStrEqual(link_child->name, BAD_CAST "url")) {
+ return xmlNodeGetContent(link_child);
+ }
+ link_child = link_child->next;
+ }
+ }
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+xmlChar *quvi_xml_get_stream_title(xmlDoc *doc) {
+ xmlNode *root = xmlDocGetRootElement(doc);
+ xmlNode *node = root->children;
+ while (node) {
+ if (xmlStrEqual(node->name, BAD_CAST "page_title")) {
+ return xmlNodeGetContent(node);
+ }
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+void pipe_libquvi_delete(PipeComponent *instance) {
+ PipeLibquvi *self = (PipeLibquvi *)instance;
+ if (self->quvi_output != -1) {
+ close(self->quvi_output);
+ }
+ if (self->pid != -1) {
+ g_spawn_close_pid(self->pid);
+ }
+ if (self->parser) {
+ xmlFreeParserCtxt(self->parser);
+ }
+ g_free(self->url);
+ free(self);
+}
+
+
+/***** Utility functions *****/
+
+
+CURL *start_curl(const char *url, CURLM *curlmulti, PipeComponent *instance) {
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "curl initialization failed");
+ return NULL;
+ }
+
+ g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Downloading %s", url);
+
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, WEBVID_USER_AGENT);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_wrapper);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, instance);
+ if (url)
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_PRIVATE, instance);
+
+ // FIXME: headers, cookies
+
+ return curl;
+}
+
+void append_to_fdset(int fd, fd_set *fdset, int *maxfd) {
+ if (fd != -1) {
+ FD_SET(fd, fdset);
+ if (fd > *maxfd)
+ *maxfd = fd;
+ }
+}
+
+gboolean read_from_fd_to_pipe(PipeComponent *instance, int *instance_fd_ptr,
+ int fd, int bitmask)
+{
+ const int buflen = 4096;
+ char buf[buflen];
+ ssize_t numbytes;
+
+ gboolean owned_socket = (fd == -1) || (fd == *instance_fd_ptr);
+ gboolean read_operation = ((bitmask & WEBVI_SELECT_READ) != 0) ||
+ (bitmask == WEBVI_SELECT_CHECK);
+
+ if (owned_socket && read_operation) {
+ numbytes = read(fd, buf, buflen);
+ if (numbytes < 0) {
+ /* error */
+ pipe_component_finished(instance, WEBVISTATE_IO_ERROR);
+ } else if (numbytes == 0) {
+ /* end of file */
+ pipe_component_finished(instance, WEBVISTATE_FINISHED_OK);
+ close(fd);
+ *instance_fd_ptr = -1;
+ } else {
+ pipe_component_append(instance, buf, numbytes);
+ }
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}