Logo Search packages:      
Sourcecode: pan version File versions  Download package

task-xml.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Pan - A Newsreader for Gtk+
 * Copyright (C) 2002  Charles Kerr <charles@rebelbase.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * 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 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
 */

/*********************
**********************  Includes
*********************/

#include <config.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <libxml/parser.h>
#include <libxml/xmlmemory.h>

#include <pan/base/log.h>
#include <pan/base/article.h>
#include <pan/base/group.h>
#include <pan/base/server.h>
#include <pan/base/serverlist.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/pan-glib-extensions.h>
#include <pan/base/util-file.h>

#include <pan/task.h>
#include <pan/task-bodies.h>
#include <pan/task-save.h>
#include <pan/task-headers.h>
#include <pan/task-xml.h>

/************
*************  UTIL
************/

static void
newline_depth (GString * appendme, int depth)
{
      int i;
      g_string_append_c (appendme, '\n');
      for (i=0; i<depth; ++i)
            g_string_append_c (appendme, '\t');
}

/*****
******  
******  
******    WRITING
******  
******  
*****/

static void
write_pcdata (GString * appendme, int depth, const char * name, const char * pcdata, int pcdata_len)
{
      char * escaped = g_strndup (pcdata, pcdata_len>=0 ? pcdata_len : strlen(pcdata));
      replace_gstr (&escaped, pan_str_escape (escaped));
      newline_depth (appendme, depth);
      g_string_append_printf (appendme, "<%s>%s</%s>", name, escaped, name);
      g_free (escaped);
}

static void
write_server (GString * appendme, int depth, const Server * server)
{
      g_return_if_fail (appendme!=NULL);
      g_return_if_fail (server_is_valid(server));

      write_pcdata (appendme, depth, "server", server->name.str, server->name.len);
}

static void
write_group (GString * appendme, int depth, const Group * group)
{
      g_return_if_fail (appendme!=NULL);
      g_return_if_fail (group_is_valid(group));

      write_pcdata (appendme, depth, "group", group->name.str, group->name.len);
}

static void
write_identifier (GString * appendme, int depth, const MessageIdentifier * mid)
{
      int i;
      newline_depth (appendme, depth);
      g_string_append (appendme, "<message_identifier>");

      write_pcdata (appendme, depth+1, "message_id", mid->message_id.str, mid->message_id.len);
      newline_depth (appendme, depth+1);
      write_pcdata (appendme, depth+1, "readable_name", mid->readable_name, -1);
      newline_depth (appendme, depth+1);
      g_string_append_printf (appendme, "<lines>%lu</lines>", mid->line_qty);
      newline_depth (appendme, depth+1);
      g_string_append_printf (appendme, "<bytes>%lu</bytes>", mid->byte_qty);

      for (i=0; i<mid->message_sources->len; ++i)
      {
            const MessageSource * source = (const MessageSource*) g_ptr_array_index (mid->message_sources, i);
            newline_depth (appendme, depth+1);
            g_string_append (appendme, "<source>");

            write_pcdata (appendme, depth+2, "server", source->server_name.str, source->server_name.len);
            write_pcdata (appendme, depth+2, "group", source->group_name.str, source->group_name.len);
            newline_depth (appendme, depth+2);
            g_string_append_printf (appendme, "<number>%lu</number>", source->number);

            newline_depth (appendme, depth+1);
            g_string_append (appendme, "</source>");
      }

      newline_depth (appendme, depth);
      g_string_append (appendme, "</message_identifier>");
}

static void
write_identifiers (GString * appendme, int depth, const MessageIdentifier ** mids, int mid_qty)
{
      int i;
      for (i=0; i<mid_qty; ++i)
            write_identifier (appendme, depth, mids[i]);
}

static void
write_headers_task (GString * appendme, int depth, const TaskHeaders * task)
{
      const char * pch;

      /* sanity clause */
      g_return_if_fail (appendme != NULL);
      g_return_if_fail (depth>=0);
      g_return_if_fail (task!=NULL);

      /* write the headers task */
      newline_depth (appendme, depth);
      g_string_append (appendme, "<headers");
      switch (task->download_type) {
            case HEADERS_ALL: pch = "all"; break;
            case HEADERS_SAMPLE: pch = "sample"; break;
            case HEADERS_NEW: default: pch = "new"; break;
      }
      g_string_append_printf (appendme, " mode=\"%s\"", pch);
      if (task->download_type==HEADERS_SAMPLE)
            g_string_append_printf (appendme, " maximum=\"%d\"", task->sample_size);
      if (task->download_bodies)
            g_string_append (appendme, " bodies=\"t\"");
      g_string_append (appendme, ">");
      write_server (appendme, depth+1, TASK(task)->server);
      write_group (appendme, depth+1, task->group);
      newline_depth (appendme, depth);
      g_string_append (appendme, "</headers>");
}

static void
write_bodies_task (GString * appendme, int depth, const TaskBodies * task)
{
      Task * t = TASK(task);

      /* sanity clause */
      g_return_if_fail (appendme != NULL);
      g_return_if_fail (depth>=0);
      g_return_if_fail (task!=NULL);

      /* write the bodies task */
      newline_depth (appendme, depth);
      g_string_append (appendme, "<bodies>");
      newline_depth (appendme, depth+1);
      write_server (appendme, depth+1, TASK(task)->server);
      write_identifiers (appendme, depth+1, (const MessageIdentifier**)t->identifiers->pdata, t->identifiers->len);
      newline_depth (appendme, depth);
      g_string_append (appendme, "</bodies>");
}

static void
write_save_task (GString * appendme, int depth, const TaskSave * task)
{
      Task * t = TASK(task);

      /* sanity clause */
      g_return_if_fail (appendme != NULL);
      g_return_if_fail (depth>=0);
      g_return_if_fail (task!=NULL);

      /* write the save task */
      newline_depth (appendme, depth);
      g_string_append (appendme, "<save>");
      write_server (appendme, depth+1, TASK(task)->server);
      if (is_nonempty_string (task->path_attachments))
            write_pcdata (appendme, depth+1, "path", task->path_attachments, -1);
      if (is_nonempty_string (task->filename_attachments))
            write_pcdata (appendme, depth+1, "filename", task->filename_attachments, -1);
      write_identifiers (appendme, depth+1, (const MessageIdentifier**)t->identifiers->pdata, t->identifiers->len);
      newline_depth (appendme, depth);
      g_string_append (appendme, "</save>");
}

static void
write_task (GString * appendme, int depth, const Task * task)
{
      /* sanity clause */
      g_return_if_fail (appendme);
      g_return_if_fail (depth>=0);
      g_return_if_fail (task!=NULL);

      /* write the task */
      switch (task->type)
      {
            case TASK_TYPE_SAVE:
                  write_save_task (appendme, depth, TASK_SAVE(task));
                  break;
            case TASK_TYPE_HEADERS:
                  write_headers_task (appendme, depth, TASK_HEADERS(task));
                  break;
            case TASK_TYPE_BODIES:
                  write_bodies_task (appendme, depth, TASK_BODIES(task));
                  break;
            default:
                  break;
      }
}

char*
tasks_to_xml_string  (const Task    ** tasks,
                      int              task_qty)
{
      int i;
      GString * str;

      /* sanity clause */
      g_return_val_if_fail (tasks!=NULL, NULL);
      g_return_val_if_fail (task_qty>0, NULL);

      /* write the tasks to a buf */
      str = g_string_sized_new (4096);
      for (i=0; i<task_qty; ++i) {
            write_task (str, 1, tasks[i]);
            if (i!=task_qty)
                  g_string_append (str, "\n");
      }

      g_string_prepend (str, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
                             "<!DOCTYPE rules SYSTEM \"tasks.dtd\">\n"
                             "<tasks>\n");
      g_string_append (str, "\n</tasks>\n");

      /* return the string */
      return g_string_free (str, FALSE);
}


static gboolean
task_xml_fwrite (FILE        * fp,
                 const Task ** tasks,
                 int           task_qty)
{
      char * pch;
      size_t to_write;
      size_t written;

      /* sanity clause */
      g_return_val_if_fail (fp!=NULL, 0);
      g_return_val_if_fail (tasks!=NULL, 0);
      g_return_val_if_fail (task_qty>0, 0);

      pch = tasks_to_xml_string (tasks, task_qty);
      to_write = strlen (pch);
      written = fwrite (pch, sizeof(char), to_write, fp);
      g_free (pch);

      return to_write == written;
}



void
task_xml_write (const char   * filename,
                const Task  ** tasks,
                int            task_qty)
{
      /* sanity clause */
      g_return_if_fail (is_nonempty_string(filename));
      g_return_if_fail (task_qty>=0);

      /* write the file */
      if (task_qty == 0)
            unlink (filename);
      else {
            FILE * fp;
            char * tmp_path = pan_file_make_temp (&fp);
            gboolean ok = fp ? TRUE : FALSE;
            if (ok) {
                  ok = task_xml_fwrite (fp, tasks, task_qty);
                  fclose (fp);
            }
            if (ok)
                  ok = pan_file_rename (tmp_path, filename);
            if (!ok)
                  log_add_va (LOG_ERROR, _("Can't write to \"%s\": %s"), filename, g_strerror(errno));
            g_free (tmp_path);
      }

}










/*****
******  
******  
******    READING
******  
******  
*****/

static char*
get_node_value (xmlDoc * doc, xmlNodePtr node)
{
      char * retval;
      xmlChar * str;

      g_return_val_if_fail (doc!=NULL, NULL);
      g_return_val_if_fail (node!=NULL, NULL);
      g_return_val_if_fail (node->xmlChildrenNode!=NULL, NULL);

      str = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
      retval = g_strdup (str);
      xmlFree (str);
      return retval;
}

static Server*
parse_server_node (xmlDoc * doc, xmlNodePtr node)
{
      char * pch;
      PString name;
      Server * retval;

      g_return_val_if_fail (doc!=NULL, NULL);
      g_return_val_if_fail (node!=NULL, NULL);

      pch = get_node_value (doc, node);
      name = pstring_shallow (pch, -1);
      g_return_val_if_fail (pstring_is_set (&name), NULL);

      retval = serverlist_get_named_server (&name);
      g_free (pch);
      return retval;
}

static Group*
parse_group_node (xmlDoc * doc, xmlNodePtr node, Server * server)
{
      char * pch;
      PString name;
      Group * retval;

      g_return_val_if_fail (doc!=NULL, NULL);
      g_return_val_if_fail (node!=NULL, NULL);
      g_return_val_if_fail (server_is_valid(server), NULL);

      pch = get_node_value (doc, node);
      name = pstring_shallow (pch, -1);
      g_return_val_if_fail (pstring_is_set (&name), NULL);

      retval = server_get_named_group (server, &name);
      g_free (pch);
      return retval;
}

static xmlNodePtr
get_child_node (xmlDoc * doc, xmlNodePtr node, const char * name)
{
      /* pump the info out of the tree */
      for (node=node->xmlChildrenNode; node!=NULL; node=node->next)
            if ((!xmlStrcmp (node->name, (const xmlChar*)name)))
                  break;

      return node;
}

static char*
get_child_node_value (xmlDoc * doc, xmlNodePtr node, const char * name)
{
      char * retval = NULL;
      xmlNodePtr tmp = get_child_node (doc, node, name);
      if (tmp != NULL)
            retval = get_node_value (doc, tmp);
      return retval;
}


static MessageIdentifier*
parse_message_identifier (xmlDoc * doc, xmlNodePtr node)
{
      char * pch;
      char * message_id = NULL;
      MessageIdentifier * mid;
      xmlNodePtr tmp;

      /* pump the info out of the tree */
      tmp = get_child_node (doc, node, "message_id");
      message_id = get_node_value (doc, tmp);
      mid = message_identifier_new (message_id);

      pch = get_child_node_value (doc, node, "readable_name");
      if (pch != NULL) {
            message_identifier_set_readable_name (mid, pch);
            g_free (pch);
      }

      pch = get_child_node_value (doc, node, "lines");
      if (pch != NULL) {
            mid->line_qty = strtoul (pch, NULL, 10);
            g_free (pch);
      }

      pch = get_child_node_value (doc, node, "bytes");
      if (pch != NULL) {
            mid->byte_qty = strtoul (pch, NULL, 10);
            g_free (pch);
      }

      /* add the sources */
      for (tmp=node->xmlChildrenNode; tmp!=NULL; tmp=tmp->next) {
            if ((!xmlStrcmp (tmp->name, (const xmlChar*)"source"))) {
                  Server * server = parse_server_node (doc, get_child_node (doc, tmp, "server"));
                  Group * group = parse_group_node (doc, get_child_node (doc, tmp, "group"), server);
                  char * pch = get_node_value (doc, get_child_node (doc, tmp, "number"));
                  const gulong number = strtoul (pch, NULL, 10);
                  if (server!=NULL && group!=NULL)
                        message_identifier_add_source (mid, &server->name, &group->name, number);
                  g_free (pch);
            }
      }

      /* cleanup */
      g_free (message_id);
      return mid;
}

/***
****
***/

static Task*
create_task_bodies (xmlDoc * doc, xmlNodePtr node)
{
      PanObject * o;
      Server * server;
      GPtrArray * mids;

      /* get server */
      server = parse_server_node (doc, get_child_node (doc, node, "server"));
      g_return_val_if_fail (server!=NULL, NULL);

      /* get message identifiers */
            mids = g_ptr_array_sized_new (32);
      for (node=node->xmlChildrenNode; node!=NULL; node=node->next) {
            if ((!xmlStrcmp (node->name, (const xmlChar*)"message_identifier"))) {
                  MessageIdentifier * mid = parse_message_identifier (doc, node);
                  if (mid != NULL)
                        g_ptr_array_add (mids, mid);
            }
      }

      /* create a new bodies task */
      o = task_bodies_new (server, (MessageIdentifier**)mids->pdata, mids->len);

      /* cleanup */
      pan_g_ptr_array_foreach (mids, (GFunc)g_object_unref, NULL);
      g_ptr_array_free (mids, TRUE);
      return TASK(o);
}

static Task*
create_task_save (xmlDoc * doc, xmlNodePtr node)
{
      PanObject * o = NULL;
      Server * server = NULL;
      char * path_attachments = NULL;
      char * filename_attachments = NULL;
      GPtrArray * mids;

      g_return_val_if_fail (node!=NULL, NULL);

            mids = g_ptr_array_sized_new (32);

      /* get the information we need */
      server = parse_server_node (doc, get_child_node (doc, node, "server"));
      path_attachments = get_child_node_value (doc, node, "path");
      filename_attachments = get_child_node_value (doc, node, "filename");
      for (node=node->xmlChildrenNode; node!=NULL; node=node->next) {
            if ((!xmlStrcmp (node->name, (const xmlChar*)"message_identifier"))) {
                  MessageIdentifier * mid = parse_message_identifier (doc, node);
                  if (mid != NULL)
                        g_ptr_array_add (mids, mid);
            }
      }

      /* create a new save task */
      o = task_save_new (server, (MessageIdentifier**)mids->pdata, mids->len);
      if (is_nonempty_string(path_attachments) || is_nonempty_string(filename_attachments))
            task_save_set_attachments (TASK_SAVE(o), path_attachments, filename_attachments);

      /* cleanup */
      g_free (path_attachments);
      g_free (filename_attachments);
      pan_g_ptr_array_foreach (mids, (GFunc)g_object_unref, NULL);
      g_ptr_array_free (mids, TRUE);
      return TASK(o);
}

static Task*
create_task_headers (xmlDoc * doc, xmlNodePtr node)
{
      PanObject * obj = NULL;
      Server * server = NULL;
      Group * group = NULL;
      xmlChar * val;
      int download_type = HEADERS_NEW;
      int maximum = -1;
      gboolean bodies = FALSE;


      server = parse_server_node (doc, get_child_node (doc, node, "server"));

      group = parse_group_node (doc, get_child_node (doc, node, "group"), server);

      /**
      ***  attributes
      **/

      if ((val = xmlGetProp (node, "mode")) != NULL) {
            if (!pan_strcmp (val, "all")) download_type = HEADERS_ALL;
            else if (!pan_strcmp (val, "new")) download_type = HEADERS_NEW;
            else if (!pan_strcmp (val, "sample")) download_type = HEADERS_SAMPLE;
            xmlFree (val);
      }       
      if ((val = xmlGetProp (node, "bodies")) != NULL) {
            bodies = !pan_strcmp (val, "t");
            xmlFree (val);
      }       
      if ((val = xmlGetProp (node, "maximum")) != NULL) {
            maximum = atoi (val);
            xmlFree (val);
      }       

      /**
      ***  make the task
      **/

      if (maximum != -1)
            obj = task_headers_new_sample (group, maximum, bodies);
      else if (bodies)
            obj = task_headers_new_with_bodies (group, download_type);
      else
            obj = task_headers_new (group, download_type);

      return TASK(obj);
}


static Task*
create_task (xmlDoc * doc, xmlNodePtr node)
{
      Task * t = NULL;
      const char * tag = (const char*) node->name;

      if (!pan_strcmp(tag,"save"))
      {
            t = create_task_save (doc, node);
      }
      else if (!pan_strcmp(tag,"bodies"))
      {
            t = create_task_bodies (doc, node);
      }
      else if (!pan_strcmp(tag,"headers"))
      {
            t = create_task_headers (doc, node);
      }

      return t;
}
 
void
task_xml_read (const char  * filename,
               GPtrArray   * appendme_tasks)
{
      /* sanity clause */
      g_return_if_fail (is_nonempty_string(filename));
      g_return_if_fail (appendme_tasks!=NULL);

      if (pan_file_exists (filename))
      {
            xmlDocPtr doc = xmlParseFile (filename);
            if (doc != NULL)
            {
                  xmlNodePtr cur = xmlDocGetRootElement(doc);
                  cur = cur->xmlChildrenNode;

                  for (; cur!=NULL; cur=cur->next)
                  {
                        Task * t= create_task (doc, cur);
                        if (t != NULL)
                              g_ptr_array_add (appendme_tasks, t);
                  }

                  xmlFreeDoc (doc);
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index