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

smtp.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 <config.h>

#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <glib.h>

#if defined(HAVE_GNET_1)
#include <gnet/gnet.h>
#define gnet_inetaddr_get_host_name gnet_inetaddr_gethostname 
#elif defined (HAVE_GNET_2)
#include <gnet.h>
#else
#error must have GNet 1 or GNet 2
#endif

#include <gmime/gmime-utils.h>

#include <pan/base/gnksa.h>
#include <pan/base/pan-glib-extensions.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/status-item.h>

#include <pan/identities/identity.h>
#include <pan/identities/identity-manager.h>

#include <pan/prefs.h>
#include <pan/smtp.h>
#include <pan/sockets.h>
#include <pan/task-post.h>

extern const char* sockread_err_msg;
extern const char* sockwrite_err_msg;

/**
 * examine response from server.
 *
 * If a read error occurs, a sockread_err is emitted by the StatusItem.
 * If expected is >0 and differs from the return value, an error is emitted.
 * 
 * @param item the StatusItem to whom errors are emitted
 * @param sock the socket to read from.
 * @param expected the expected numeric value.
 * @return the received numeric value.
 */
static int
smtp_get_response (StatusItem   * item,
                   PanSocket    * sock,
                   int            expected)
{
      const char *buf = NULL;
      int retval = 0;

      if (pan_socket_getline (sock, &buf)) {
            status_item_emit_error (item, sockread_err_msg);
            return -1;
      }

      retval = atoi(buf);
      if (expected>0 && retval!=expected)
            status_item_emit_error_va (
                  item,
                  _("Got unexpected response from mail server: "
                    "expected %d; got %s"),
                  expected,
                  buf);

      return retval;
}

/**
 * send a line to the server.
 *
 * If a write error occurs, a sockwrite_err is emitted from StatusItem.
 *
 * @param item the StatusItem to whom errors are emitted
 * @param sock the socket to write to.
 * @param string a string to write.
 * @return zero on success (no write errors), nonzero on failure.
 */
static TaskStateEnum
smtp_send_line (StatusItem   * item,
                PanSocket    * sock,
                const char   * str)
{
      TaskStateEnum val;

      if (pan_mute)
      {
            g_message ("(MUTE) to smtp: [%s]", str);
            val = TASK_OK;
      }
      else
      {
            val = pan_socket_putline (sock, str);
            if (val != TASK_OK)
                  status_item_emit_error (item, sockwrite_err_msg);
      }
      return val;
}


/**
 * send a line to the server.
 *
 * If a write error occurs, a sockwrite_err is emitted from StatusItem.
 *
 * @param item the StatusItem to whom errors are emitted
 * @param sock the socket to write to.
 * @param fmt a printf-style format string
 * @return zero on success (no write errors), nonzero on failure.
 */
static int
smtp_send_line_va (StatusItem  * item,
                   PanSocket   * sock,
                   const char  * fmt,
                   ...)
{
      va_list ap;
      int retval = 0;
      char *line = NULL;

      va_start (ap, fmt);
      line = g_strdup_vprintf (fmt, ap);
      va_end (ap);
      retval = smtp_send_line (item, sock, line);
      g_free (line);

      return retval;
}


/**
 * Sends a message to the specified smtp socket.
 *
 * Possible emissions:
 * * If the "From" line is empty and there's no fallback in the
 *     Pan config file, a "No Sender Specified" error is emitted.
 * * If the server doesn't know any of the recipients, a "No Known Recipients"
 *     error is emitted.
 * * If no recipients are specified, a "No Recipients!" error is emitted.  
 * * If a socket write error occurs, a sockwrite_err is emitted.
 * * If a socket read error occurs, a sockread_err is emitted.
 *
 * @param item the StatusItem through which emissions are made.
 * @param socket the smtp socket where the message is mailed.
 * @param msg the message being sent.
 * @return zero on success, nonzero on failure
 */
static int
smtp_send_message (StatusItem    * item,
                   PanSocket     * sock,
                   GMimeMessage  * message,
                   const char    * leader)
{
      gboolean okay = TRUE;
      gboolean maybe_okay = FALSE;

      /* sanity checks */
      g_return_val_if_fail (item!=NULL, -1);
      g_return_val_if_fail (sock!=NULL, -1);
      g_return_val_if_fail (GMIME_IS_MESSAGE(message), -1);

      /**
      *** start sending a letter
      **/
      if (okay)
            status_item_emit_status (item, _("Sending mail"));

      if (okay)
      {
            const char * sender = g_mime_message_get_sender (message);
            InternetAddressList * list = internet_address_parse_string (sender);
            InternetAddress * ia = NULL;
            if (list != NULL)
                  ia = list->address;
            okay = ia!=NULL && ia->type==INTERNET_ADDRESS_NAME;
            if (okay)
                  okay = !smtp_send_line_va (item, sock, "MAIL FROM: <%s>\r\n", ia->value.addr);
            internet_address_list_destroy (list);
      }
      if (okay)
            okay = pan_mute || smtp_get_response(item, sock, 250)==250;

      /**
      *** tell the server who we're sending the letter to
      **/

      maybe_okay = FALSE;
      if (okay)
      {
            InternetAddressList * addresses = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO);
            InternetAddressList * march;

            for (march=addresses; march!=NULL; march=march->next)
            {
                  if (okay && march->address->type==INTERNET_ADDRESS_NAME)
                  {
                        const char * fmt = "RCPT TO: <%s>\r\n";
                        okay = !smtp_send_line_va (item, sock, fmt, march->address->value.addr);
                  }
                  if (okay)
                  {
                        const int retval = pan_mute
                              ? 250
                              : smtp_get_response(item, sock, -1);

                        /* >=1 known recepient */
                        maybe_okay = retval==250;

                        /* keep going even if one recipient is unknown */
                        okay = retval==250 || retval==550;
                  }
            }
      }

      /* don't proceed unless we have at least one known recipient */
      if (okay) {
            okay = maybe_okay;
            if (!okay)
                  status_item_emit_error(item,
                        _("No known recipients"));
      }

      /**
      *** tell the server we're about to start sending a letter
      **/

      if (okay)
            okay = !smtp_send_line (item, sock, "DATA\r\n");
      if (okay)
            okay = pan_mute || smtp_get_response (item, sock, 354)==354;

      /**
      *** send the message
      **/

      {
            char * pch = build_nntp_message (message);
            const char * march = pch;
            PString line = PSTRING_INIT;

            while (get_next_token_pstring (march, '\n', &march, &line))
                  okay = okay && !smtp_send_line_va (item, sock, "%*.*s\n", line.len, line.len, line.str);

            g_free (pch);
      }

      if (okay)
            okay = !smtp_send_line (item, sock, "\r\n.\r\n");
      if (okay)
            okay = pan_mute || smtp_get_response (item, sock, 250)==250;
      if (okay)
            status_item_emit_status (item, _("Mail sent!"));

      return okay ? 0 : -1;
}


/* begin a SMTP session */
static int
smtp_handshake (StatusItem   * item,
                PanSocket    * sock)
{
      char * my_name = gnet_inetaddr_get_host_name ();

      status_item_emit_status (item, _("Handshaking with mail server"));

      if (smtp_send_line_va (item, sock, "HELO %s\r\n", my_name ? my_name : ""))
            return -1;

      if (!pan_mute) {
            for (;;) {
                  gint val = smtp_get_response (item, sock, 250);
                  if (val == 220) /* apparently the welcome message is wordy */
                        continue;
                  return val==250 ? 0 : -1;
            }
      }

      return 0;
}


/* end a SMTP session */
static void
smtp_disconnect (StatusItem   * item,
                 PanSocket    * sock)
{
      if (sock != NULL)
      {
            smtp_send_line (item, sock, "QUIT\r\n");

            if (!pan_mute)
                  smtp_get_response (item, sock, 221);
      }
}

/* open a connection to a mail server */
static PanSocket*
smtp_get_connection (StatusItem *item)
{
      const PString mail_server = pstring_shallow ("mail_server", -1);
      const PString mail_address = pstring_shallow (mail_server_address, -1);
      PanSocket *sock = NULL;

      /* sanity clause */
      g_return_val_if_fail (pstring_is_set (&mail_server), NULL);
      g_return_val_if_fail (mail_server_port>0, NULL);

      /* get smtp address/port */
      status_item_emit_status (item, _("Connecting to mail server"));

      /* get the socket... */
      sock = pan_socket_new (&mail_server, &mail_address, mail_server_port);
      if (sock == NULL) {
            status_item_emit_error_va (item,
                  _("Unable to connect to mail server \"%*.*s\""),
                  mail_address.len, mail_address.len, mail_address.str);
            pan_object_unref (PAN_OBJECT(sock));
            sock = NULL;
      }

      /* get the "ready" message from the socket... */
      if (sock!=NULL && smtp_get_response (item, sock, 220) != 220) {
            status_item_emit_error_va (item, _("Mail server not ready"));
            pan_object_unref (PAN_OBJECT(sock));
            sock = NULL;
      }

      /* handshake */
      if (sock!=NULL && smtp_handshake (item, sock)) {
            pan_object_unref (PAN_OBJECT(sock));
            sock = NULL;
      }

      return sock;
}

/***
****
****  PUBLIC FUNCTIONS
****
***/

int
smtp_send (StatusItem      * item,
           GMimeMessage    * message,
           const char      * leader)
{
      PanSocket * sock = NULL;
      int retval;

      /* sanity checks */
      g_return_val_if_fail (item!=NULL, -1);
      g_return_val_if_fail (GMIME_IS_MESSAGE (message), -1);

      /* connect to the smtp server */
      sock = smtp_get_connection (item);
      if (!sock)
            return -1;

      /* send the message */
      retval = smtp_send_message (item, sock, message, leader);

      /* say goodbye and disconnect */
      smtp_disconnect (item, sock);
      pan_object_unref (PAN_OBJECT(sock));
      return retval;
}




Generated by  Doxygen 1.6.0   Back to index