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

message-identifier.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
 */

#include <string.h>

#include <pan/base/debug.h>
#include <pan/base/message-identifier.h>
#include <pan/base/pan-glib-extensions.h>

#include <pan/base/serverlist.h>
#include <pan/base/group.h>
#include <pan/base/article.h>

/**
***  GObject stuff
**/

static GObjectClass * parent_class = NULL;

static void
pan_message_identifier_init (MessageIdentifier * mid, MessageIdentifierClass * klass)
{
      mid->line_qty = 0ul;
      mid->byte_qty = 0ul;
      mid->message_id = PSTRING_INIT;
      mid->readable_name = NULL;
      mid->message_sources = g_ptr_array_new ();
}

static void
pan_message_identifier_finalize (GObject * object)
{
      int i;
      MessageIdentifier * mid = (MessageIdentifier*) object;

      for (i=0; i<mid->message_sources->len; ++i)
      {
            MessageSource * source = (MessageSource*) g_ptr_array_index (mid->message_sources, i);
            pstring_clear (&source->server_name);
            pstring_clear (&source->group_name);
            g_free (source);
      }
      g_ptr_array_free (mid->message_sources, TRUE);
      g_free (mid->readable_name);
      pstring_clear (&mid->message_id);

      mid->message_sources = NULL;

      G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
pan_message_identifier_class_init (MessageIdentifierClass * klass)
{
      GObjectClass * object_class = G_OBJECT_CLASS (klass);

      parent_class = g_type_class_ref (G_TYPE_OBJECT);

      object_class->finalize = pan_message_identifier_finalize;
}

GType
pan_message_identifier_get_type (void)
{
      static GType type = 0;

      if (!type)
      {
            static const GTypeInfo info =
            {
                  sizeof (MessageIdentifierClass),
                  NULL, /* base_class_init */
                  NULL, /* base_class_finalize */
                  (GClassInitFunc) pan_message_identifier_class_init,
                  NULL, /* class_finalize */
                  NULL, /* class_data */
                  sizeof (MessageIdentifier),
                  16,   /* n_preallocs */
                  (GInstanceInitFunc) pan_message_identifier_init
            };

            type = g_type_register_static (G_TYPE_OBJECT, "MessageIdentifier", &info, 0);
      }

      return type;
}

/**
***  MessageIdentifier Stuff
**/

MessageIdentifier*
message_identifier_new (const char * message_id)
{
      MessageIdentifier * mid;

      mid = g_object_new (PAN_TYPE_MESSAGE_IDENTIFIER, NULL, NULL);
      pstring_set (&mid->message_id, message_id, strlen(message_id));

      return mid;
}

MessageIdentifier*
message_identifier_new_from_article (const Article * article)
{
      MessageIdentifier * mid;

      g_return_val_if_fail (article_is_valid(article), NULL);

      mid = g_object_new (PAN_TYPE_MESSAGE_IDENTIFIER, NULL, NULL);
      pstring_copy (&mid->message_id, &article->message_id);
      message_identifier_set_readable_name (mid, article_get_subject(article));
      message_identifier_add_source_from_article (mid, article);

      return mid;
}

static MessageSource*
message_identifier_get_source_for_group (MessageIdentifier   * mid,
                                         const PString       * server_name,
                                         const PString       * group_name)
{
      guint i;
      MessageSource * source = NULL;

      /* sanity clause */
      g_return_val_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mid), NULL);
      g_return_val_if_fail (pstring_is_set (server_name), NULL);
      g_return_val_if_fail (pstring_is_set (group_name), NULL);

      /* find the source matching this server+group */
      for (i=0; source==NULL && i<mid->message_sources->len; ++i) {
            MessageSource * it = (MessageSource*) g_ptr_array_index (mid->message_sources, i);
            if (pstring_equal (server_name, &it->server_name) && pstring_equal (group_name, &it->group_name)) {
                  source = it;
            }
      }

      return source;
}

void
message_identifier_add_source (MessageIdentifier  * mid,
                             const PString      * server,
                             const PString      * group,
                             gulong               number)
{
      MessageSource * source;

      /* sanity clause */
      g_return_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mid));
      g_return_if_fail (pstring_is_set (server));
      g_return_if_fail (pstring_is_set (group));
      g_return_if_fail (message_identifier_get_source_for_group (mid, server, group) == NULL);

      source = g_new (MessageSource, 1);
      source->server_name = PSTRING_INIT;
      source->group_name = PSTRING_INIT;
      pstring_copy (&source->server_name, server);
      pstring_copy (&source->group_name, group);
      source->number = number;
      g_ptr_array_add (mid->message_sources, source);
}

static void
add_source_from_article_xreffunc (Server * server, Group * group, gulong number, gpointer user_data)
{
      message_identifier_add_source (MESSAGE_IDENTIFIER(user_data), &server->name, &group->name, number);
}

void
message_identifier_add_source_from_article  (MessageIdentifier  * mid,
                                             const Article      * article)
{
      g_return_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mid));
      g_return_if_fail (article_is_valid(article));

      mid->line_qty = article->linecount;
      mid->byte_qty = article->byte_qty;
      message_identifier_add_source (mid, &article->group->server->name, &article->group->name, article->number);
      article_xref_foreach (article, add_source_from_article_xreffunc, mid, SERVER_GROUPS_ALL, TRUE);
}

gboolean
message_identifier_is_valid (const MessageIdentifier * mid)
{
      g_return_val_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mid), FALSE);
      g_return_val_if_fail (pstring_is_set (&mid->message_id), FALSE);

      return TRUE;
}

gboolean
message_identifiers_are_valid (const MessageIdentifier * const * mids, int qty)
{
      int i;
      g_return_val_if_fail (qty >= 1, FALSE);
      g_return_val_if_fail (mids != NULL, FALSE);
      for (i=0; i<qty; ++i)
            g_return_val_if_fail (message_identifier_is_valid(mids[i]), FALSE);
      return TRUE;
}

MessageSource*
message_identifier_get_source_for_server   (MessageIdentifier  * mid,
                                            const PString      * server)
{
      int i;
      MessageSource * retval = NULL;

      /* sanity clause */
      g_return_val_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mid), NULL);
      g_return_val_if_fail (pstring_is_set(server), NULL);

      /* return the first for the matching that server */
      for (i=0; !retval && i<mid->message_sources->len; ++i) {
            MessageSource * source = (MessageSource*) g_ptr_array_index (mid->message_sources, i);
            if (pstring_equal (server, &source->server_name))
                  retval = source;
      }

      return retval;
}

const PString*
message_identifier_get_primary_group (MessageIdentifier * mid)
{
      const PString* retval = NULL;

      /* sanity clause */
      g_return_val_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mid), NULL);

      /* find the first group name */
      if (mid->message_sources->len) {
            MessageSource * source = (MessageSource*) g_ptr_array_index (mid->message_sources, 0);
            retval = &source->group_name;
      }

      return retval;
}

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

static void
group2art_delete_ghfunc (gpointer key, gpointer val, gpointer user_data)
{
      Group * group = GROUP (key);
      GHashTable * article_hash = (GHashTable*) val;
      GPtrArray * article_array;

            article_array = g_ptr_array_new ();
      pan_hash_to_ptr_array (article_hash, article_array);
      group_remove_articles (group, (Article**)article_array->pdata, article_array->len);
      g_ptr_array_free (article_array, TRUE);

      g_hash_table_destroy (article_hash);
      group_unref_articles (group, NULL);
}

void
message_identifiers_delete (const MessageIdentifier * const * mids,
                            int                        mid_qty,
                      ServerGroupsType           type)
{
      int i;
      GHashTable * group2art;
      debug_enter ("message_identifiers_delete");

      /* sanity clause */
      g_return_if_fail (mids!=NULL);
      g_return_if_fail (mid_qty>0);
      g_return_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mids[0]));

      group2art = g_hash_table_new (g_direct_hash, g_direct_equal);

      /* walk through all the mids */
      for (i=0; i<mid_qty; ++i)
      {
            int j;
            const MessageIdentifier * mid = mids[i];
            for (j=0; j<mid->message_sources->len; ++j)
            {
                  MessageSource * source = (MessageSource*) g_ptr_array_index (mid->message_sources, j);
                  Server * server;
                  Group * group;

                  /* find the source's server */
                  server = serverlist_get_named_server (&source->server_name);
                  if (server == NULL)
                        continue;

                  /* find the source's group */
                  group = server_get_named_group_in_type (server, &source->group_name, type);
                  if (group == NULL)
                        continue;

                  /* if the group doesn't have the articles loaded, then nobody
                   * is listening to the article changing state, so we can poke
                   * the newsrc directly and not need to fire an event.
                   * Otherwise, the articles are loaded, so we need to
                   * use the Article objects. */
                  if (!group_ref_articles_if_loaded (group))
                        group_mark_article_purged (group, source->number);
                  else {
                        Article * a = group_get_article_by_message_id (group, &mid->message_id);
                        if (a != NULL) {
                              /* The Article container we use is a Hash, to weed out duplicates. */
                              GHashTable * art_hash = (GHashTable*) g_hash_table_lookup (group2art, group);
                              if (art_hash == NULL) {
                                    art_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
                                    g_hash_table_insert (group2art, group, art_hash);
                                    group_ref_articles (group, NULL);
                              }

                              g_hash_table_insert (art_hash, a, a);
                        }
                        group_unref_articles (group, NULL);
                  }
            }
      }

      g_hash_table_foreach (group2art, group2art_delete_ghfunc, GINT_TO_POINTER(read));
      g_hash_table_destroy (group2art);

      debug_exit ("message_identifiers_delete");
}

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

static void
group2art_mark_read_ghfunc (gpointer key, gpointer val, gpointer user_data)
{
      Group * group = GROUP (key);
      GHashTable * article_hash = (GHashTable*) val;
      GPtrArray * article_array;
      const gboolean read = user_data != NULL;

            article_array = g_ptr_array_new ();
      pan_hash_to_ptr_array (article_hash, article_array);
      articles_set_read_simple ((Article**)article_array->pdata, article_array->len, read);
      g_ptr_array_free (article_array, TRUE);

      g_hash_table_destroy (article_hash);
      group_unref_articles (group, NULL);
}

void
message_identifiers_mark_read (const MessageIdentifier * const * mids,
                               int                        mid_qty,
                               gboolean                   read,
                         ServerGroupsType           type)
{
      int i;
      GHashTable * group2art;
      debug_enter ("message_identifiers_mark_read");

      /* sanity clause */
      g_return_if_fail (mids!=NULL);
      g_return_if_fail (mid_qty>0);
      g_return_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mids[0]));

      group2art = g_hash_table_new (g_direct_hash, g_direct_equal);

      /* walk through all the mids */
      for (i=0; i<mid_qty; ++i)
      {
            int j;
            const MessageIdentifier * mid = mids[i];
            for (j=0; j<mid->message_sources->len; ++j)
            {
                  MessageSource * source = (MessageSource*) g_ptr_array_index (mid->message_sources, j);
                  Server * server;
                  Group * group;

                  /* find the source's server */
                  server = serverlist_get_named_server (&source->server_name);
                  if (server == NULL)
                        continue;

                  /* find the source's group */
                  group = server_get_named_group_in_type (server, &source->group_name, type);
                  if (group == NULL)
                        continue;

                  /* if the group doesn't have the articles loaded, then nobody
                   * is listening to the article changing state, so we can poke
                   * the newsrc directly and not need to fire an event.
                   * Otherwise, the articles are loaded, so we need to
                   * use the Article objects. */
                  if (!group_ref_articles_if_loaded (group))
                        group_mark_article_read (group, source->number, read);
                  else {
                        Article * a = group_get_article_by_message_id (group, &mid->message_id);
                        if (a != NULL) {
                              /* The Article container we use is a Hash, to weed out duplicates. */
                              GHashTable * art_hash = (GHashTable*) g_hash_table_lookup (group2art, group);
                              if (art_hash == NULL) {
                                    art_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
                                    g_hash_table_insert (group2art, group, art_hash);
                                    group_ref_articles (group, NULL);
                              }

                              g_hash_table_insert (art_hash, a, a);
                        }
                        group_unref_articles (group, NULL);
                  }
            }
      }

      g_hash_table_foreach (group2art, group2art_mark_read_ghfunc, GINT_TO_POINTER(read));
      g_hash_table_destroy (group2art);

      debug_exit ("message_identifiers_mark_read");
}

const char*
message_identifier_get_readable_name (const MessageIdentifier * mid)
{
      g_return_val_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mid), "");

      return mid->readable_name;
}

void
message_identifier_set_readable_name (MessageIdentifier  * mid,
                                      const char         * name)
{
      g_return_if_fail (PAN_IS_MESSAGE_IDENTIFIER(mid));

      replace_gstr (&mid->readable_name, g_strdup(name));
}

const PString**
message_identifiers_get_id_array (const MessageIdentifier * const * mids,
                                  int                               mid_qty)
{
      int i;
      const PString ** retval = NULL;

      g_return_val_if_fail (message_identifiers_are_valid (mids, mid_qty), NULL);

      retval = g_new0 (const PString*, mid_qty);
      for (i=0; i<mid_qty; ++i)
            retval[i] = &mids[i]->message_id;

      return retval;
}

Generated by  Doxygen 1.6.0   Back to index