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

task-headers.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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <pan/base/article.h>
#include <pan/base/debug.h>
#include <pan/base/file-headers.h>
#include <pan/base/log.h>
#include <pan/base/pan-glib-extensions.h>
#include <pan/base/pan-i18n.h>

#include <pan/nntp.h>
#include <pan/sockets.h>
#include <pan/task-headers.h>
#include <pan/rules/rule-manager.h>

/*********************
**********************  Defines / Enumerated types
*********************/

#define SAMPLE_SIZE_DEFAULT 150

/*********************
**********************  Macros
*********************/

/*********************
**********************  Structures / Typedefs
*********************/

/*********************
**********************  Private Function Prototypes
*********************/

static void task_headers_run_download (Task * task, PanSocket * sock);

static char* task_headers_describe (const StatusItem *item);

static TaskStateEnum task_get_header_range (TaskHeaders   * task,
                                            PanSocket     * sock,
                                            gulong        * setme_lo_article_in_group,
                                            gulong        * setme_hi_article_in_group,
                                            gulong        * setme_lo_article_to_fetch,
                                            gulong        * setme_hi_article_to_fetch,
                                            gulong        * setme_total_in_group,
                                            const char   ** setme_progress_fmt);

/*********************
**********************  Variables
*********************/

/***********
************  Extern
***********/

/***********
************  Public
***********/

/***********
************  Private
***********/

/*********************
**********************  BEGINNING OF SOURCE
*********************/

/************
*************  PUBLIC ROUTINES
************/

/*****
******  TASK LIFE CYCLE
*****/

static void
task_headers_destructor (PanObject* o)
{
      task_destructor (o);
}

static PanObject*
real_task_headers_new (Group                * group,
                   HeaderDownloadType     dl_type,
                   gboolean               download_bodies,
                       int                    sample_size)
{
      TaskHeaders * item;
      debug_enter ("real_task_headers_new");

      g_return_val_if_fail (group_is_valid(group), NULL);
      g_return_val_if_fail (!group_is_folder(group), NULL);
      g_return_val_if_fail (dl_type==HEADERS_ALL || dl_type==HEADERS_NEW || dl_type==HEADERS_SAMPLE, NULL);

            item = g_new0 (TaskHeaders, 1);
      debug1 (DEBUG_PAN_OBJECT, "task_headers_new: %p", item);

      task_constructor (TASK(item),
                        task_headers_destructor,
                        task_headers_describe,
                        group->server,
                        TRUE); /* high priority */
      task_state_set_work_need_socket (&TASK(item)->state, group->server, task_headers_run_download);
      TASK(item)->type = TASK_TYPE_HEADERS;
      item->group = group;
      item->download_type = dl_type;
      item->download_bodies = download_bodies;
      item->body_index = 0;
      item->sample_size = sample_size;

      debug_exit ("real_task_headers_new");
      return PAN_OBJECT(item);
}

PanObject*
task_headers_new (Group               * group,
                  HeaderDownloadType    dl_type)
{
      return real_task_headers_new (group,
                                    dl_type,
                                    FALSE,
                                    SAMPLE_SIZE_DEFAULT);
}

PanObject*
task_headers_new_with_bodies   (Group               * group,
                        HeaderDownloadType    dl_type)
{
      return real_task_headers_new (group,
                                    dl_type,
                                    TRUE,
                                    SAMPLE_SIZE_DEFAULT);
}

PanObject*
task_headers_new_sample (Group     * group,
                   guint       sample_size,
                         gboolean    download_bodies)
{
      return real_task_headers_new (group,
                                    HEADERS_SAMPLE,
                                    download_bodies,
                                    sample_size);
}

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

static void
mark_all_articles_old (Group * group, Article ** articles, guint article_qty, gpointer user_data)
{
      if (article_qty > 0u)
            articles_set_new (articles, article_qty, FALSE);
}

static void
mark_read_articles_old (Group * group, Article ** articles, guint article_qty, gpointer user_data)
{
      guint i;
      GPtrArray * read;

      /* get a list of read articles */
      read = g_ptr_array_new ();
      for (i=0; i<article_qty; ++i)
            if (article_is_read (articles[i]))
                  g_ptr_array_add (read, articles[i]);

      /* mark those articles as old */
      if (read->len > 0)
            articles_set_new ((Article**)read->pdata, read->len, FALSE);

      /* cleanup */
      g_ptr_array_free (read, TRUE);
}

static void
task_headers_run_download (Task * task, PanSocket * sock)
{
      StatusItem * status = STATUS_ITEM(task);
      TaskHeaders * task_h = TASK_HEADERS(task);
      gulong low_in_group, high_in_group;
      gulong low_to_fetch, high_to_fetch;
      gulong high_reached = 0ul;
      gulong total_in_group;
      const char * progress_fmt;
      gboolean reffed = FALSE;
      gboolean state_set = FALSE;
      TaskStateEnum val;
      TaskStateEnum state = TASK_OK;
      debug_enter ("task_headers_run");

      /* get up-to-date information about the group */
      progress_fmt = NULL;
      low_in_group = high_in_group = low_to_fetch = high_to_fetch = 0;
      val = task_get_header_range (task_h, sock,
                                   &low_in_group,
                                   &high_in_group,
                                   &low_to_fetch,
                                   &high_to_fetch,
                                   &total_in_group,
                                   &progress_fmt);
      if (val != TASK_OK) {
            state_set = TRUE;
            state = val;
      }

      /* where to start downloading? */
      if (task_h->download_type == HEADERS_NEW)
            low_to_fetch = MAX (low_to_fetch, task_h->group->article_high+1);

      /* no articles to fetch */
      if (!state_set && (high_to_fetch==0 || low_to_fetch>high_to_fetch)) {
            state_set = TRUE;
            state = TASK_OK;
      }

      /* if articles to fetch, try fetching them */
      if (!state_set)
      {
            int steps;
            GPtrArray * articles;

            /* load the existing headers */
            if (!reffed) {
                  group_ref_articles (task_h->group, status);
                  group_expire_articles_not_in_range (task_h->group, low_in_group, high_in_group); 
                  reffed = TRUE;
            }

            /* init the status item for the header download */
            switch (task_h->download_type) {
                  case HEADERS_ALL:
                  case HEADERS_NEW:
                        steps = high_to_fetch - low_to_fetch;
                        if (steps < 0)
                              steps = 0;
                        break;
                  case HEADERS_SAMPLE:
                        steps = task_h->sample_size;
                        break;
                  default:
                        steps = 0;
                        pan_warn_if_reached();
            }
            status_item_emit_init_steps (status, steps);

            /* get the article headers... */
            articles = g_ptr_array_new ();
            val = nntp_download_headers (status,
                                         sock,
                                         task_h->group,
                                         low_to_fetch,
                                         high_to_fetch,
                                         &task->hint_abort,
                                         progress_fmt,
                                         &high_reached,
                                         articles);

            if (val != TASK_OK) {
                  state_set = TRUE;
                  state = val;
            }

            /* if we've read the group since the last fetch, then mark some
               articles as old.  If there are new articles in this fetch,
               then mark all of them as old.  Otherwise just mark the read
               ones as old. */
            if (task_h->group->loaded_since_last_fetch)
            {
                  task_h->group->loaded_since_last_fetch = FALSE;

                  if (articles->len)
                        group_article_forall (task_h->group, mark_all_articles_old, NULL);
                  else
                        group_article_forall (task_h->group, mark_read_articles_old, NULL);
            }

            /* if we got new articles, process them */
            if (articles->len)
            {
                  /* log */
                  log_add_va (LOG_INFO, _("Fetched %u headers for \"%s\""),
                        articles->len,
                        group_get_name(task_h->group));

                  /* add the articles to the group */
                  group_mark_new_article_number (task_h->group, task_h->group->article_high);
                  group_add_articles_remove_unused (task_h->group, articles, status);
                  group_set_article_range (task_h->group, low_in_group, high_reached);

                  /* apply the rules to the new headers */
                  rule_manager_process_incoming_articles (articles);

                  /* do we need to download these? */
                  if (task_h->download_bodies)
                  {
                        guint i;

                        for (i=0; i<articles->len; ++i) {
                              Article * a = ARTICLE(g_ptr_array_index(articles,i));
                              MessageIdentifier * mid = message_identifier_new_from_article (a);
                              task_add_identifiers (task, &mid, 1);
                              g_object_unref (mid);
                        }
                  }
            }

            /* cleanup */
            g_ptr_array_free (articles, TRUE);
      }

      /* Download bodies, if any */
      if (!state_set && task->identifiers->len!=0)
      {
            /* refresh the ui */
            status_item_emit_init_steps (status, task->identifiers->len);
            status_item_emit_set_step (status, task_h->body_index);

            /* try downloading the bodies */
            val = nntp_download_bodies (status,
                                        sock,
                                        &task->hint_abort,
                                        (MessageIdentifier**) task->identifiers->pdata,
                                        task->identifiers->len,
                                        &task_h->body_index,
                                        FALSE);
            if (val != TASK_OK) {
                  state_set = TRUE;
                  state = val;
            }
      }

      /* cleanup */
      status_item_emit_progress (status, 0);
      if (reffed) {
            file_headers_save (task_h->group, status);
            group_unref_articles (task_h->group, status);
      }

      /* if we made it this far without fail, return success. */
      if (!state_set)
            state = TASK_OK;

      task_state_set_health (&task->state, state);

      if (state==TASK_OK)
            task_state_set_work_completed (&task->state);
      else
            task_state_set_work_need_socket (&task->state, task->server, task_headers_run_download);

      debug_exit ("task_headers_run");
}

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

static TaskStateEnum
task_get_header_range (TaskHeaders   * task,
                       PanSocket     * sock,
                   gulong        * setme_lo_article_in_group,
                   gulong        * setme_hi_article_in_group,
                       gulong        * setme_lo_article_to_fetch,
                   gulong        * setme_hi_article_to_fetch,
                   gulong        * setme_total_in_group,
                   const char   ** setme_progress_fmt)
{
      const gboolean * const abort = &TASK(task)->hint_abort;
      Group *group = TASK_HEADERS(task)->group;
      HeaderDownloadType dl_type = TASK_HEADERS(task)->download_type;
      const char * progress_fmt;
      gulong lo_article_to_fetch;
      gulong hi_article_to_fetch;
      gulong lo_article_in_group;
      gulong hi_article_in_group;
      gulong total_in_group;
      TaskStateEnum val;
      debug_enter ("task_get_header_range");

      /* sanity checks */
      g_return_val_if_fail (sock!=NULL, TASK_FAIL);
      g_return_val_if_fail (group_is_valid(group), TASK_FAIL);

      /* change to the right group */
      lo_article_in_group = 0;
      hi_article_in_group = 0;
      total_in_group = 0;
      val = nntp_get_group_info (STATUS_ITEM(task),
                                 sock,
                                 &group->name,
                                 &total_in_group,
                                 &lo_article_in_group,
                                 &hi_article_in_group,
                                 abort);
      if (val != TASK_OK)
            return val;

      /* figure out which articles to ask for, based on the first/last
       * returned by the server and the dl_type of this task.
       */
      if (dl_type==HEADERS_NEW)
      {
            gulong p_last = group->article_high;
            if (p_last!=0 && p_last>=hi_article_in_group)
            {
                  status_item_emit_status_va (STATUS_ITEM(task),
                        _("No new articles in group \"%s\""),
                          group_get_name(group));
            }

            lo_article_to_fetch = p_last!=0 ? p_last+1 : lo_article_in_group;
            hi_article_to_fetch = hi_article_in_group;
            progress_fmt = _("New %lu of %lu");
      }

      else if (dl_type==HEADERS_SAMPLE)
      {
            const int qty = MIN (total_in_group, task->sample_size);
            status_item_emit_status_va (
                  STATUS_ITEM(task), _("Sampling %d articles"), qty);
            lo_article_to_fetch = hi_article_in_group - qty;
            hi_article_to_fetch = hi_article_in_group;
            progress_fmt = _("Sampling %lu of %lu");
      }
      else if (dl_type==HEADERS_ALL)
      {
            lo_article_to_fetch = lo_article_in_group;
            hi_article_to_fetch = hi_article_in_group;
            progress_fmt = _("All %lu of %lu");
      }
      else
      {
            pan_warn_if_reached ();
            lo_article_to_fetch  = 0;
            hi_article_to_fetch = 0;
            progress_fmt = _("Error");
      }

      *setme_lo_article_in_group = lo_article_in_group;
      *setme_hi_article_in_group = hi_article_in_group;
      *setme_lo_article_to_fetch = lo_article_to_fetch;
      *setme_hi_article_to_fetch = hi_article_to_fetch;
      *setme_total_in_group      = total_in_group;
      *setme_progress_fmt        = progress_fmt;

      debug_exit ("task_get_header_range");
      return TASK_OK;
}

static char*
task_headers_describe (const StatusItem* item)
{
      int type;
      char * retval;
      const char * name;

      /* sanity checks */
      g_return_val_if_fail (item!=NULL, NULL);
      name = group_get_name(TASK_HEADERS(item)->group);
      type = TASK_HEADERS(item)->download_type;

      if (type == HEADERS_ALL)
            retval = g_strdup_printf (_("Getting all headers for \"%s\""), name);
      else if (type == HEADERS_NEW)
            retval = g_strdup_printf (_("Getting new headers for \"%s\""), name);
      else
            retval = g_strdup_printf (_("Sampling headers for \"%s\""), name);

      return retval;
}

Generated by  Doxygen 1.6.0   Back to index