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

task-post.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 <stdlib.h>
#include <string.h>

#include <glib.h>
#include <gmime/gmime.h>

#include <pan/base/acache.h>
#include <pan/base/article.h>
#include <pan/base/debug.h>
#include <pan/base/gnksa.h>
#include <pan/base/log.h>
#include <pan/base/message-check.h>
#include <pan/base/serverlist.h>
#include <pan/base/pan-glib-extensions.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/util-mime.h>

#include <pan/globals.h>
#include <pan/message-check-ui.h>
#include <pan/nntp.h>
#include <pan/queue.h>
#include <pan/smtp.h>
#include <pan/task-post.h>

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

enum MessageSendResult
{
      SMTP_FAILED     = (1<<0),
      NNTP_FAILED     = (1<<1)
};

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

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

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

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

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

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

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

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

/************
*************  PRIVATE ROUTINES
************/

/**
 * Populate a hashtable of internal headers in a message
 */
static void
build_nntp_message_foreach_header (const char * name, const char * value, gpointer user_data)
{
      if (article_header_is_internal (name))
            g_ptr_array_add (((GPtrArray*)(user_data)), (gpointer)name);
}

static gssize
write_header_nofold (GMimeStream *stream, const char *name, const char *value)
{
      gssize nwritten;
      char * val;

      val = g_strdup_printf ("%s: %s\n", name, value);
      nwritten = g_mime_stream_write_string (stream, val);
      g_free (val);

      return nwritten;
}


/**
 * Build the message that will be passed to the news server.
 * Error-checking is done elsewhere; by the time we reach this
 * point we assume it's in the clear and only work on building
 * the message.
 */
char*
build_nntp_message (GMimeMessage * message)
{
      char * tmp;
      GMimeMessage * clone;
      debug_enter ("build_nntp_message");

      /* sanity checks... */
      g_return_val_if_fail (GMIME_IS_MESSAGE(message), NULL);

      /* clone the message so that we can fuck with it
       * without harming the original. */
      if (1) {
            char * str;
            GMimeStream * stream;
            GMimeParser * parser;

            g_mime_header_set_default_write_func (GMIME_OBJECT(message)->headers,
                                                  write_header_nofold);

            str = g_mime_message_to_string (message);
            message = NULL;

            parser = g_mime_parser_new ();
            stream = g_mime_stream_mem_new_with_buffer (str, strlen(str));
            g_mime_parser_init_with_stream (parser, stream);
                clone = g_mime_parser_construct_message (parser);

            g_object_unref (parser);
            g_object_unref (stream);
            g_free (str);
      }

      /* update the message's date -- it may have been in sendlater for awhile */
      if (1) {
            time_t now = time (0);
            const int offset_secs = tzoffset_sec (&now);
            g_mime_message_set_date (clone, now, (offset_secs/3600)*100);
      }

      /* maybe remove the message-id */
      if (g_mime_message_get_header(clone, PAN_NO_MESSAGE_ID) != NULL)
            g_mime_object_remove_header (GMIME_OBJECT(clone), HEADER_MESSAGE_ID);

      /* turn off folding. */
      g_mime_header_set_default_write_func (GMIME_OBJECT(clone)->headers,
                                            write_header_nofold);


      /* remove the internal headers */
      if (1) {
            guint i;
            GPtrArray * a = g_ptr_array_new ();
            GMimeHeader * h = GMIME_OBJECT(clone)->headers;
            g_mime_header_foreach (h, build_nntp_message_foreach_header, a);
            for (i=0; i<a->len; ++i)
                  g_mime_header_remove (h, (const char*)g_ptr_array_index(a,i));
            g_ptr_array_free (a, TRUE);
      }

      /* make sure each line ends in \r\n */
      tmp = g_mime_message_to_string (clone);
      replace_gstr (&tmp, pan_substitute (tmp, "\n.", "\n.."));
      replace_gstr (&tmp, pan_substitute (tmp, "\n", "\r\n"));
      replace_gstr (&tmp, pan_substitute (tmp, "\r\r\n.", "\r\n"));

      /* cleanup */
      g_object_unref (clone);
      debug_exit ("build_nntp_message");
      return tmp;
}

/**
 * At this point we've already gotten a task and socket from the queue
 * and have done all the error-checking with the headers/body.  We're
 * ready to post the message and/or send the mail.
 */
static TaskStateEnum
send_message_now (StatusItem           * item,
                  PanSocket            * sock,
                  Server               * server,
                  GMimeMessage         * message,
                  int                  * failures)
{
      TaskStateEnum val = TASK_OK;
      const char * subject;
      const char * mail_to;
      const char * post_to;
      debug_enter ("send_message_now");

      /* initialize variables */
            subject = g_mime_message_get_subject (message);
            mail_to = g_mime_message_get_header (message, PAN_TO);
            post_to = g_mime_message_get_header (message, PAN_NEWSGROUPS);

      *failures = 0;

      /* mail the message */
      if (is_nonempty_string(mail_to))
      {
            char * leader = NULL;

            if (is_nonempty_string(post_to))
            {
                  leader = g_strdup_printf (
                  _("[This is an email copy of a Usenet post to \"%s\"]"),
                  post_to);
            }

            /* mail the message */
            val = smtp_send (item, message, leader);
            if (val == TASK_OK) {
                  log_add_va (LOG_INFO, _("Email \"%s\" sent."), subject);
                  g_mime_object_remove_header (GMIME_OBJECT(message), PAN_TO);
            } else {
                  log_add_va (LOG_ERROR, _("Email \"%s\" not sent."), subject);
                  *failures |= SMTP_FAILED;
            }

            /* cleanup */
            g_free (leader);
      }

      /* post the message */
      if (is_nonempty_string(post_to))
      {
            char * pch;

            /* build the message. */
            pch = build_nntp_message (message);

            /* let the user know what we're doing */
            status_item_emit_status_va (STATUS_ITEM(item), _("Posting article \"%s\""), subject);

            /* post the part */
            val = nntp_post (item, sock, pch);
            if (val == TASK_OK) {
                  log_add_va (LOG_INFO, _("Article \"%s\" posted."), subject);
                  g_mime_object_remove_header (GMIME_OBJECT(message), PAN_NEWSGROUPS);
            } else {
                  log_add_va (LOG_ERROR, _("Article \"%s\" not posted."), subject);
                  *failures |= NNTP_FAILED;
            }
            
            /* cleanup */
            g_free (pch);
      }


      debug_exit ("send_message_now");
      return val;
}

static void
task_post_destructor (PanObject * obj)
{
      debug_enter ("task_post_destructor");

      /* sanity clause */
      g_return_if_fail (obj!=NULL);

      /* task-post dtor */
      g_object_unref (TASK_POST(obj)->message);

      /* destroy parent class */
      task_destructor (obj);

      debug_exit ("task_post_destructor");
}

static char*
task_post_describe (const StatusItem* si)
{
      GMimeMessage * message = TASK_POST(si)->message;
      const gboolean posting = g_mime_message_get_header (message, PAN_NEWSGROUPS) != NULL;
      const gboolean mailing = g_mime_message_get_header (message, PAN_TO) != NULL;
      const char * fmt;

      if (posting && mailing)
            fmt = _("Posting and mailing article \"%s\"");
      else if (posting)
            fmt = _("Posting article \"%s\"");
      else if (mailing)
            fmt = _("Mailing article \"%s\"");
      else 
            fmt = "BUG!";

      return g_strdup_printf (fmt, g_mime_message_get_subject(message));
}

static void
task_post_run (Task * task, PanSocket * sock)
{
      TaskStateEnum val;
      int failures = 0;
      TaskPost * post = TASK_POST(task);
      debug_enter ("task_post_run");

      /* try to send the message */
      val = send_message_now (STATUS_ITEM(task), sock, task->server, post->message,  &failures);

      if (val==TASK_OK && !failures)
      {
            GMimeMessage * message = post->message;
            folder_remove_message (serverlist_get_named_folder(&PAN_SENDLATER), message);
            folder_add_message (serverlist_get_named_folder(&PAN_SENT), message);
      }
      else
      {
            GMimeMessage * message;
            GString * text;

            /* write the message back to pan.sendlater, because
             * PAN_TO or PAN_NEWSGROUPS may have been cleared */
                  message = post->message;
            folder_add_message (serverlist_get_named_folder(&PAN_SENDLATER), message);

                  text = g_string_new (NULL);
            if (failures & SMTP_FAILED)
                  g_string_append (text, _("Email send failed.  "));
            if (failures & NNTP_FAILED)
                  g_string_append (text, _("Usenet posting failed.  "));
            if (text->len != 0) {
                  g_string_append (text, _("Check Tools|Log Viewer for more information.  "));
                  g_string_append (text, _("Your message was saved in the folder \"pan.sendlater\""));
                  log_add (LOG_ERROR|LOG_URGENT, text->str);
            }

            g_string_free (text, TRUE);
      }

      /**
      ***  Update the task state
      **/

      task_state_set_health (&task->state, val);

      if (failures)
            task_state_set_work_need_socket (&TASK(post)->state, task->server, task_post_run);
      else
            task_state_set_work_completed (&task->state);


      debug_exit ("task_post_run");
}

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

PanObject*
task_post_new (Server * server, GMimeMessage * message)
{
      TaskPost * post = NULL;
      debug_enter ("task_post_new");

      /* sanity clause */
      g_return_val_if_fail (server_is_valid(server), NULL);
      g_return_val_if_fail (GMIME_IS_MESSAGE(message), NULL);

      /* create the object */
            post = g_new0 (TaskPost, 1);
        debug1 (DEBUG_PAN_OBJECT, "task_post_new: %p", post);

      /* initialize the parent class */
      task_constructor (TASK(post), task_post_destructor, task_post_describe, server, TRUE);
      task_state_set_work_need_socket (&TASK(post)->state, server, task_post_run);
      TASK(post)->type = TASK_TYPE_POST;

      /* initialize the task-post */
      post->message = message;
      g_object_ref (message);

      debug_exit ("task_post_new");
      return PAN_OBJECT(post);
}

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


/****
*****
*****   UTILITIES
*****
****/

static void
message_checked_cb (Server        * server,
                    GMimeMessage  * message,
                    GoodnessLevel   goodness,
                    gpointer        user_data)
{
      Task * task = NULL;

      if (goodness == OKAY)
            task = TASK (task_post_new (server, message));
      if (task != NULL)
            queue_add (task);
}

void
queue_message_for_posting (Server          * server,
                           GMimeMessage    * message,
                           gboolean          message_check_first)
{
      /* sanity check */
      g_return_if_fail (server_is_valid (server));
      g_return_if_fail (GMIME_IS_MESSAGE (message));

      /* if desired, make sure the message is okay. */
      if (message_check_first)
            message_check_and_prompt (Pan.window, server, message, message_checked_cb, NULL);
      else
            message_checked_cb (server, message, OKAY, NULL);
}

static void
flush_sendlater_foreach (GMimeMessage * message, gpointer user_data)
{
      /* find the server that we're posting with */
      Server * server = NULL;
      const PString server_name = pstring_shallow (g_mime_message_get_header (message, PAN_SERVER), -1);
      if (pstring_is_set (&server_name))
            server = serverlist_get_named_server (&server_name);
      if (server == NULL)
            server = serverlist_get_active_server ();

      /* queue the message */
      queue_message_for_posting (server, message, TRUE);
}

void
flush_sendlater_messages (void)
{
      acache_path_foreach (PAN_SENDLATER.str, flush_sendlater_foreach, NULL);
}

Generated by  Doxygen 1.6.0   Back to index