+2003-03-20
+
+ * src/session.[ch]: session_start_tls(): new.
+ session_recv_msg(): made it static.
+
+2003-03-20
+
+ * implemented asynchronous network I/O.
+ * src/session.[ch]: the interface to communicate with servers through
+ child process.
+ * src/smtp.[ch]
+ src/send_message.[ch]: rewrote using new I/O system.
+ * src/socket.[ch]: renamed *_write() to *_write_all(), and made
+ *_write() as just a simple wrapper for write().
+ * src/utils.c: get_outgoing_rfc2822_str(): new. It modifies the
+ message to send with SMTP or NNTP.
+ file_read_stream_to_str(): new.
+
2003-03-12
* src/textview.c: textview_button_pressed(): corrected the range of
+2003-03-20 [paul] 0.8.11claws31
+
+ * sync with 0.8.11cvs6
+ see ChangeLog 2003-03-20
+
2003-03-19 [match] 0.8.11claws30
* src/addritem.[ch]
+2003-03-20
+
+ * src/session.[ch]: session_start_tls(): ¿·µ¬¡£
+ session_recv_msg(): static ¤Ë¤·¤¿¡£
+
+2003-03-20
+
+ * È󯱴ü¥Í¥Ã¥È¥ï¡¼¥¯ I/O ¤ò¼ÂÁõ¡£
+ * src/session.[ch]: »Ò¥×¥í¥»¥¹¤òÄ̤·¤Æ¥µ¡¼¥Ð¤ÈÄÌ¿®¤¹¤ë¤¿¤á¤Î
+ ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¡£
+ * src/smtp.[ch]
+ src/send_message.[ch]: ¿· I/O ¥·¥¹¥Æ¥à¤ò»È¤Ã¤Æ½ñ¤Ä¾¤·¤¿¡£
+ * src/socket.[ch]: *_write() ¤ò *_write_all() ¤Ë̾¾ÎÊѹ¹¤·¡¢
+ *_write() ¤òñ¤Ê¤ë write() ¤Î¥é¥Ã¥Ñ¡¼¤È¤·¤¿¡£
+ * src/utils.c: get_outgoing_rfc2822_str(): ¿·µ¬¡£¥á¥Ã¥»¡¼¥¸¤ò SMTP
+ ¤Þ¤¿¤Ï NNTP ¤ÇÁ÷¿®¤¹¤ë¤¿¤á¤Ë½¤Àµ¤¹¤ë¡£
+ file_read_stream_to_str(): ¿·µ¬¡£
+
2003-03-12
* src/textview.c: textview_button_pressed(): URI ¤Î¥ê¥ó¥¯¤ÎÈϰϤò
MICRO_VERSION=11
INTERFACE_AGE=0
BINARY_AGE=0
-EXTRA_VERSION=claws30
+EXTRA_VERSION=claws31
VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
dnl set $target
pop.c pop.h \
mh.c mh.h \
mbox.c mbox.h \
- send.c send.h \
+ send_message.c send_message.h \
recv.c recv.h \
inc.c inc.h \
import.c import.h \
{
gint ok;
gchar buf[NNTPBUFSIZE];
+ gchar *msg;
ok = nntp_gen_command(sock, buf, "POST");
if (ok != NN_SUCCESS)
return ok;
- while (fgets(buf, sizeof(buf), fp) != NULL) {
- strretchomp(buf);
- if (buf[0] == '.') {
- if (sock_write(sock->sock, ".", 1) < 0) {
- log_warning(_("Error occurred while posting\n"));
- return NN_SOCKET;
- }
- }
-
- if (sock_puts(sock->sock, buf) < 0) {
- log_warning(_("Error occurred while posting\n"));
- return NN_SOCKET;
- }
+ msg = get_outgoing_rfc2822_str(fp);
+ if (sock_write_all(sock->sock, msg, strlen(msg)) < 0) {
+ log_warning(_("Error occurred while posting\n"));
+ g_free(msg);
+ return NN_SOCKET;
}
+ g_free(msg);
- sock_write(sock->sock, ".\r\n", 3);
+ sock_write_all(sock->sock, ".\r\n", 3);
if ((ok = nntp_ok(sock, buf)) != NN_SUCCESS)
return ok;
}
strcat(buf, "\r\n");
- sock_write(sock->sock, buf, strlen(buf));
+ sock_write_all(sock->sock, buf, strlen(buf));
}
static gint nntp_gen_recv(NNTPSockInfo *sock, gchar *buf, gint size)
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2002 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
*
* 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
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "defs.h"
+
#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/signal.h>
+#include <sys/wait.h>
+
#include "session.h"
+#include "utils.h"
+
+static gint session_close (Session *session);
+
+static gchar *session_recv_msg (Session *session);
+
+static guchar *session_read_data (Session *session,
+ guint size);
+
+static gint session_send_data_to_sock (Session *session,
+ const guchar *data,
+ guint size);
+static guchar *session_recv_data_from_sock (Session *session,
+ guint size);
+
+gboolean session_parent_input_cb (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data);
+
+gboolean session_child_input (Session *session);
+
+
+gint session_connect(Session *session, const gchar *server, gushort port)
+{
+ pid_t pid;
+ gint pipe_fds1[2], pipe_fds2[2];
+ SockInfo *sock;
+ gchar *str;
+
+ session->server = g_strdup(server);
+ session->port = port;
+
+ if (pipe(pipe_fds1) < 0) {
+ perror("pipe");
+ return -1;
+ }
+ if (pipe(pipe_fds2) < 0) {
+ perror("pipe");
+ close(pipe_fds1[0]);
+ close(pipe_fds1[1]);
+ return -1;
+ }
+
+ if ((pid = fork()) < 0) {
+ perror("fork");
+ return -1;
+ }
+
+ if (pid != 0) {
+ session->child_pid = pid;
+ session->read_ch = g_io_channel_unix_new(pipe_fds2[0]);
+ session->write_ch = g_io_channel_unix_new(pipe_fds1[1]);
+ close(pipe_fds1[0]);
+ close(pipe_fds2[1]);
+ session->read_tag = g_io_add_watch(session->read_ch, G_IO_IN,
+ session_parent_input_cb,
+ session);
+ return 0;
+ }
+
+ /* child process */
+
+ session->read_ch = g_io_channel_unix_new(pipe_fds1[0]);
+ session->write_ch = g_io_channel_unix_new(pipe_fds2[1]);
+ close(pipe_fds1[1]);
+ close(pipe_fds2[0]);
+
+ g_print("child: connecting to %s:%d ...\n", server, port);
+
+ if ((sock = sock_connect(server, port)) == NULL) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "can't connect to server.");
+ session_close(session);
+ _exit(1);
+ }
+
+#if USE_OPENSSL
+ if (session->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "can't initialize SSL.");
+ session_close(session);
+ _exit(1);
+ }
+#endif
+
+ g_print("child: connected\n");
+
+ session->sock = sock;
+ session->state = SESSION_RECV;
+
+ if ((str = sock_getline(sock)) == NULL) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "can't get server response.");
+ session_close(session);
+ _exit(1);
+ }
+ strretchomp(str);
+ session_send_msg(session, SESSION_MSG_NORMAL, str);
+ g_free(str);
+
+ while (session_child_input(session) == TRUE)
+ ;
+
+ session_close(session);
+
+ g_print("child: disconnected\n");
+
+ _exit(0);
+}
+
+gint session_disconnect(Session *session)
+{
+ g_print("%s: session_disconnect()\n", session->child_pid == 0 ? "child" : "parent");
+ session_send_msg(session, SESSION_MSG_CONTROL, "DISCONNECT");
+ return 0;
+}
void session_destroy(Session *session)
{
g_return_if_fail(session != NULL);
g_return_if_fail(session->destroy != NULL);
+ g_print("session_destroy()\n");
+ session_close(session);
session->destroy(session);
g_free(session->server);
g_free(session);
}
+
+void session_set_recv_message_notify(Session *session,
+ RecvMsgNotify notify_func, gpointer data)
+{
+ session->recv_msg_notify = notify_func;
+ session->recv_msg_notify_data = data;
+}
+
+void session_set_recv_data_progressive_notify
+ (Session *session,
+ RecvDataProgressiveNotify notify_func,
+ gpointer data)
+{
+ session->recv_data_progressive_notify = notify_func,
+ session->recv_data_progressive_notify_data = data;
+}
+
+void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
+ gpointer data)
+{
+ session->recv_data_notify = notify_func;
+ session->recv_data_notify_data = data;
+}
+
+void session_set_send_data_progressive_notify
+ (Session *session,
+ SendDataProgressiveNotify notify_func,
+ gpointer data)
+{
+ session->send_data_progressive_notify = notify_func;
+ session->send_data_progressive_notify_data = data;
+}
+
+void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
+ gpointer data)
+{
+ session->send_data_notify = notify_func;
+ session->send_data_notify_data = data;
+}
+
+static gint session_close(Session *session)
+{
+ g_return_val_if_fail(session != NULL, -1);
+
+ g_print("%s: session_close()\n", session->child_pid == 0 ? "child" : "parent");
+
+ if (session->read_tag > 0) {
+ g_source_remove(session->read_tag);
+ session->read_tag = 0;
+ }
+
+ if (session->read_ch) {
+ g_io_channel_close(session->read_ch);
+ g_io_channel_unref(session->read_ch);
+ session->read_ch = NULL;
+ }
+ if (session->write_ch) {
+ g_io_channel_close(session->write_ch);
+ g_io_channel_unref(session->write_ch);
+ session->write_ch = NULL;
+ }
+
+ if (session->sock) {
+ sock_close(session->sock);
+ session->sock = NULL;
+ session->state = SESSION_DISCONNECTED;
+ }
+
+ if (session->child_pid) {
+ kill(session->child_pid, SIGTERM);
+ waitpid(session->child_pid, NULL, 0);
+ session->child_pid = 0;
+ }
+
+ return 0;
+}
+
+gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
+{
+ gchar *prefix;
+ gchar *str;
+ guint size;
+ guint bytes_written;
+
+ switch (type) {
+ case SESSION_MSG_NORMAL:
+ prefix = "MESSAGE"; break;
+ case SESSION_MSG_SEND_DATA:
+ prefix = "SENDDATA"; break;
+ case SESSION_MSG_CONTROL:
+ prefix = "CONTROL"; break;
+ case SESSION_MSG_ERROR:
+ prefix = "ERROR"; break;
+ default:
+ return -1;
+ }
+
+ str = g_strdup_printf("%s %s\n", prefix, msg);
+ g_print("%s: sending message: %s", session->child_pid == 0 ? "child" : "parent", str);
+ size = strlen(str);
+
+ while (size > 0) {
+ if (g_io_channel_write(session->write_ch, str, size,
+ &bytes_written)
+ != G_IO_ERROR_NONE || bytes_written == 0) {
+ g_warning("%s: sending message failed.\n",
+ session->child_pid == 0 ? "child" : "parent");
+ return -1;
+ }
+ size -= bytes_written;
+ }
+
+ return 0;
+}
+
+static gchar *session_recv_msg(Session *session)
+{
+ gchar buf[BUFFSIZE];
+ gchar *str = NULL;
+ guint size = 1;
+ guint bytes_read;
+
+ for (;;) {
+ if (g_io_channel_read(session->read_ch, buf, sizeof(buf) - 1,
+ &bytes_read)
+ != G_IO_ERROR_NONE || bytes_read == 0) {
+ g_warning("%s: receiving message failed.\n",
+ session->child_pid == 0 ? "child" : "parent");
+ g_free(str);
+ str = NULL;
+ break;
+ }
+
+ size += bytes_read;
+ buf[bytes_read] = '\0';
+
+ if (!str)
+ str = g_strdup(buf);
+ else {
+ str = g_realloc(str, size);
+ strcat(str, buf);
+ }
+ if (str[size - 2] == '\n') {
+ str[size - 2] = '\0';
+
+ g_print("%s: received message: %s\n", session->child_pid == 0 ? "child" : "parent", str);
+
+ break;
+ }
+ }
+
+ return str;
+}
+
+#if USE_OPENSSL
+gint session_start_tls(Session *session)
+{
+ gchar *ctl_msg;
+
+ session_send_msg(session, SESSION_MSG_CONTROL, "STARTTLS");
+ ctl_msg = session_recv_msg(session);
+ if (!ctl_msg || strcmp(ctl_msg, "CONTROL STARTTLSOK") != 0) {
+ g_free(ctl_msg);
+ return -1;
+ }
+ g_free(ctl_msg);
+
+ return 0;
+}
+#endif
+
+gint session_send_data(Session *session, const guchar *data, guint size)
+{
+ gchar *msg;
+ guint bytes_written;
+ GIOError err;
+
+ session_send_msg(session, SESSION_MSG_SEND_DATA, itos(size));
+ if ((msg = session_recv_msg(session)) == NULL)
+ return -1;
+ g_free(msg);
+
+ while (size > 0) {
+ if ((err = g_io_channel_write(session->write_ch, (guchar *)data,
+ size, &bytes_written))
+ != G_IO_ERROR_NONE || bytes_written == 0) {
+ g_warning("%s: sending data failed: %d\n",
+ session->child_pid == 0 ? "child" : "parent",
+ err);
+ return -1;
+ }
+ size -= bytes_written;
+ g_print("%s: sent %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_written);
+ }
+
+ return 0;
+}
+
+static guchar *session_read_data(Session *session, guint size)
+{
+ guchar *data;
+ guchar *cur;
+ guint bytes_read;
+ GIOError err;
+
+ cur = data = g_malloc(size);
+
+ while (size > 0) {
+ if ((err = g_io_channel_read(session->read_ch, cur, size,
+ &bytes_read))
+ != G_IO_ERROR_NONE || bytes_read == 0) {
+ g_warning("%s: reading data failed: %d\n",
+ session->child_pid == 0 ? "child" : "parent",
+ err);
+ g_free(data);
+ return NULL;
+ }
+ size -= bytes_read;
+ cur += bytes_read;
+ g_print("%s: received %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_read);
+ }
+
+ return data;
+}
+
+#define MAX_CHUNK_SIZE 4096
+
+static gint session_send_data_to_sock(Session *session, const guchar *data,
+ guint size)
+{
+ const guchar *cur = data;
+ gint bytes_written;
+ gint total_write_len = 0;
+ guint left = size;
+ gchar buf[BUFFSIZE];
+ gchar *msg;
+
+ while (left > 0) {
+ bytes_written = sock_write(session->sock, cur,
+ MIN(left, MAX_CHUNK_SIZE));
+ if (bytes_written <= 0)
+ return -1;
+ left -= bytes_written;
+ cur += bytes_written;
+ total_write_len += bytes_written;
+ if (left > 0) {
+ g_snprintf(buf, sizeof(buf), "DATASENDINPROG %d %d",
+ total_write_len, size);
+ session_send_msg(session, SESSION_MSG_CONTROL, buf);
+ if ((msg = session_recv_msg(session)) == NULL)
+ return -1;
+ g_free(msg);
+ }
+ }
+
+ return 0;
+}
+
+static guchar *session_recv_data_from_sock(Session *session, guint size)
+{
+ guchar *data;
+ guchar *cur;
+ gint bytes_read;
+ gint total_read_len = 0;
+ guint left = size;
+ gchar buf[BUFFSIZE];
+ gchar *msg;
+
+ cur = data = g_malloc(size);
+
+ while (left > 0) {
+ bytes_read = sock_read(session->sock, cur, left);
+ if (bytes_read <= 0) {
+ g_free(data);
+ return NULL;
+ }
+ g_print("child: received %d bytes of data from sock\n", bytes_read);
+ left -= bytes_read;
+ cur += bytes_read;
+ total_read_len += bytes_read;
+ g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
+ total_read_len, size);
+ session_send_msg(session, SESSION_MSG_CONTROL, buf);
+ if ((msg = session_recv_msg(session)) == NULL) {
+ g_free(data);
+ return NULL;
+ }
+ g_free(msg);
+ }
+
+ return data;
+}
+
+static SessionMsgType session_get_msg_type(const gchar *str)
+{
+ if (!strncmp(str, "MESSAGE ", 8))
+ return SESSION_MSG_NORMAL;
+ else if (!strncmp(str, "SENDDATA ", 9))
+ return SESSION_MSG_SEND_DATA;
+ else if (!strncmp(str, "RECVDATA ", 9))
+ return SESSION_MSG_RECV_DATA;
+ else if (!strncmp(str, "CONTROL ", 8))
+ return SESSION_MSG_CONTROL;
+ else if (!strncmp(str, "ERROR ", 6))
+ return SESSION_MSG_ERROR;
+ else
+ return SESSION_MSG_UNKNOWN;
+}
+
+gboolean session_parent_input_cb(GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ Session *session = SESSION(data);
+ gchar *msg;
+ gchar *msg_data;
+ gint len;
+ gint total;
+ guchar *recv_data;
+ guint size;
+ gint ret;
+
+ if ((msg = session_recv_msg(session)) == NULL) {
+ session->state = SESSION_ERROR;
+ return FALSE;
+ }
+
+ switch (session_get_msg_type(msg)) {
+ case SESSION_MSG_NORMAL:
+ msg_data = msg + strlen("MESSAGE ");
+ ret = session->recv_msg(session, msg_data);
+ if (ret <= 0)
+ session->recv_msg_notify(session, msg_data,
+ session->recv_msg_notify_data);
+ else
+ session_send_msg(session, SESSION_MSG_CONTROL,
+ "CONTINUE");
+ if (ret < 0) {
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ break;
+ case SESSION_MSG_SEND_DATA:
+ msg_data = msg + strlen("SENDDATA ");
+ size = atoi(msg_data);
+ session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
+ recv_data = session_read_data(session, size);
+ if (!recv_data) {
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ session->recv_data_finished(session, recv_data, size);
+ g_free(recv_data);
+ break;
+ case SESSION_MSG_RECV_DATA:
+ break;
+ case SESSION_MSG_CONTROL:
+ msg_data = msg + strlen("CONTROL ");
+ if (!strncmp(msg_data, "DATARECVINPROG ", 15)) {
+ ret = sscanf(msg_data,
+ "DATARECVINPROG %d %d", &len, &total);
+ if (ret != 2) {
+ g_warning("wrong control message: %s\n", msg);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ session_send_msg(session, SESSION_MSG_CONTROL,
+ "CONTINUE");
+ session->recv_data_progressive_notify
+ (session, len, total,
+ session->recv_data_progressive_notify_data);
+ } else if (!strncmp(msg_data, "DATASENDINPROG ", 15)) {
+ ret = sscanf(msg_data,
+ "DATASENDINPROG %d %d", &len, &total);
+ if (ret != 2) {
+ g_warning("wrong control message: %s\n", msg);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ session_send_msg(session, SESSION_MSG_CONTROL,
+ "CONTINUE");
+ session->send_data_progressive_notify
+ (session, len, total,
+ session->send_data_progressive_notify_data);
+ } else if (!strncmp(msg_data, "DATASENT ", 9)) {
+ len = atoi(msg_data + 9);
+ ret = session->send_data_finished(session, len);
+ session->send_data_notify
+ (session, len, session->send_data_notify_data);
+ } else if (!strcmp(msg_data, "DISCONNECTED")) {
+ session->state = SESSION_DISCONNECTED;
+ g_free(msg);
+ return FALSE;
+ } else {
+ g_warning("wrong control message: %s\n", msg);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ break;
+ case SESSION_MSG_ERROR:
+ default:
+ g_warning("error from child: %s\n", msg + strlen("ERROR "));
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+
+ g_free(msg);
+ return TRUE;
+}
+
+gboolean session_child_input(Session *session)
+{
+ gchar buf[BUFFSIZE];
+ gchar *msg;
+ gchar *msg_data;
+ gchar *str;
+ guchar *send_data;
+ guchar *recv_data;
+ guint size;
+
+ if ((msg = session_recv_msg(session)) == NULL) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "receiving message failed.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ return FALSE;
+ }
+
+ switch (session_get_msg_type(msg)) {
+ case SESSION_MSG_NORMAL:
+ msg_data = msg + strlen("MESSAGE ");
+ session->state = SESSION_SEND;
+ sock_puts(session->sock, msg_data);
+ session->state = SESSION_RECV;
+ str = sock_getline(session->sock);
+ if (!str) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "receiving message failed.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ strretchomp(str);
+ session_send_msg(session, SESSION_MSG_NORMAL, str);
+ g_free(str);
+ break;
+ case SESSION_MSG_SEND_DATA:
+ msg_data = msg + strlen("SENDDATA ");
+ size = atoi(msg_data);
+ session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
+ send_data = session_read_data(session, size);
+ if (!send_data) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "sending data failed.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ session->state = SESSION_SEND;
+ if (session_send_data_to_sock(session, send_data, size) < 0) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "sending data failed.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(send_data);
+ g_free(msg);
+ return FALSE;
+ }
+ g_free(send_data);
+ g_snprintf(buf, sizeof(buf), "DATASENT %d", size);
+ session_send_msg(session, SESSION_MSG_CONTROL, buf);
+ break;
+ case SESSION_MSG_RECV_DATA:
+ msg_data = msg + strlen("RECVDATA ");
+ size = atoi(msg_data);
+ session->state = SESSION_RECV;
+ recv_data = session_recv_data_from_sock(session, size);
+ if (!recv_data) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "receiving data failed.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ if (session_send_data(session, recv_data, size) < 0) {
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(recv_data);
+ g_free(msg);
+ return FALSE;
+ }
+ g_free(recv_data);
+ break;
+ case SESSION_MSG_CONTROL:
+ msg_data = msg + strlen("CONTROL ");
+ if (!strcmp(msg_data, "CONTINUE")) {
+ session->state = SESSION_RECV;
+ str = sock_getline(session->sock);
+ if (!str) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "receiving message failed.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ strretchomp(str);
+ session_send_msg(session, SESSION_MSG_NORMAL, str);
+ g_free(str);
+ break;
+#if USE_OPENSSL
+ } else if (!strcmp(msg_data, "STARTTLS")) {
+ if (!ssl_init_socket_with_method(session->sock,
+ SSL_METHOD_TLSv1)) {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "can't start TLS session.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ session_send_msg(session, SESSION_MSG_CONTROL,
+ "STARTTLSOK");
+ break;
+#endif
+ } else if (!strcmp(msg_data, "DISCONNECT")) {
+ sock_close(session->sock);
+ session->sock = NULL;
+ session->state = SESSION_DISCONNECTED;
+ session_send_msg(session, SESSION_MSG_CONTROL,
+ "DISCONNECTED");
+ g_free(msg);
+ return FALSE;
+ } else {
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "wrong control message.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+ break;
+ case SESSION_MSG_ERROR:
+ default:
+ session_send_msg(session, SESSION_MSG_ERROR,
+ "error received from parent.");
+ session_close(session);
+ session->state = SESSION_ERROR;
+ g_free(msg);
+ return FALSE;
+ }
+
+ g_free(msg);
+ return TRUE;
+}
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2002 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
*
* 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
#ifndef __SESSION_H__
#define __SESSION_H__
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#include <glib.h>
+
#include <time.h>
+#include <unistd.h>
#include "socket.h"
typedef enum {
SESSION_IMAP,
SESSION_NEWS,
- SESSION_SMTP
+ SESSION_SMTP,
+ SESSION_POP3
} SessionType;
typedef enum {
SESSION_READY,
SESSION_SEND,
- SESSION_RECV
-} SessionPhase;
+ SESSION_RECV,
+ SESSION_ERROR,
+ SESSION_DISCONNECTED
+} SessionState;
-typedef void (*SessionUIFunc) (gpointer data,
- gint state);
+typedef enum
+{
+ SESSION_MSG_NORMAL,
+ SESSION_MSG_SEND_DATA,
+ SESSION_MSG_RECV_DATA,
+ SESSION_MSG_CONTROL,
+ SESSION_MSG_ERROR,
+ SESSION_MSG_UNKNOWN
+} SessionMsgType;
+
+typedef gint (*RecvMsgNotify) (Session *session,
+ const gchar *msg,
+ gpointer user_data);
+typedef gint (*RecvDataProgressiveNotify) (Session *session,
+ guint cur_len,
+ guint total_len,
+ gpointer user_data);
+typedef gint (*RecvDataNotify) (Session *session,
+ guint len,
+ gpointer user_data);
+typedef gint (*SendDataProgressiveNotify) (Session *session,
+ guint cur_len,
+ guint total_len,
+ gpointer user_data);
+typedef gint (*SendDataNotify) (Session *session,
+ guint len,
+ gpointer user_data);
struct _Session
{
SessionType type;
SockInfo *sock;
+
gchar *server;
+ gushort port;
+
+#if USE_OPENSSL
+ SSLType ssl_type;
+#endif
- gboolean connected;
- SessionPhase phase;
+ SessionState state;
time_t last_access_time;
- gpointer data;
+ pid_t child_pid;
+
+ /* pipe I/O */
+ GIOChannel *read_ch;
+ GIOChannel *write_ch;
- void (*destroy) (Session *session);
+ gint read_tag;
- SessionUIFunc ui_func;
+ gpointer data;
+
+ /* virtual methods to parse server responses */
+ gint (*recv_msg) (Session *session,
+ const gchar *msg);
+
+ gint (*recv_data_finished) (Session *session,
+ guchar *data,
+ guint len);
+ gint (*send_data_finished) (Session *session,
+ guint len);
+
+ void (*destroy) (Session *session);
+
+ /* notification functions */
+ RecvMsgNotify recv_msg_notify;
+ RecvDataProgressiveNotify recv_data_progressive_notify;
+ RecvDataNotify recv_data_notify;
+ SendDataProgressiveNotify send_data_progressive_notify;
+ SendDataNotify send_data_notify;
+
+ gpointer recv_msg_notify_data;
+ gpointer recv_data_progressive_notify_data;
+ gpointer recv_data_notify_data;
+ gpointer send_data_progressive_notify_data;
+ gpointer send_data_notify_data;
};
+gint session_connect (Session *session,
+ const gchar *server,
+ gushort port);
+gint session_disconnect (Session *session);
void session_destroy (Session *session);
+void session_set_recv_message_notify (Session *session,
+ RecvMsgNotify notify_func,
+ gpointer data);
+void session_set_recv_data_progressive_notify
+ (Session *session,
+ RecvDataProgressiveNotify notify_func,
+ gpointer data);
+void session_set_recv_data_notify (Session *session,
+ RecvDataNotify notify_func,
+ gpointer data);
+void session_set_send_data_progressive_notify
+ (Session *session,
+ SendDataProgressiveNotify notify_func,
+ gpointer data);
+void session_set_send_data_notify (Session *session,
+ SendDataNotify notify_func,
+ gpointer data);
+
+gint session_send_msg (Session *session,
+ SessionMsgType type,
+ const gchar *msg);
+gint session_send_data (Session *session,
+ const guchar *data,
+ guint size);
+
+#if USE_OPENSSL
+gint session_start_tls (Session *session);
+#endif
+
#endif /* __SESSION_H__ */
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2002 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
*
* 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
#include "intl.h"
#include "smtp.h"
-#include "socket.h"
#include "md5.h"
#include "base64.h"
#include "utils.h"
#include "log.h"
-static gint verbose = 1;
+static void smtp_session_destroy(Session *session);
-#define UI_UPDATE(session, phase) \
-{ \
- if (SESSION(session)->ui_func) \
- SESSION(session)->ui_func(SESSION(session), phase); \
-}
+static gint smtp_from(SMTPSession *session);
+static gint smtp_auth(SMTPSession *session);
static gint smtp_starttls(SMTPSession *session);
-static gint smtp_auth_cram_md5(SMTPSession *session, gchar *buf, gint len);
-static gint smtp_auth_login(SMTPSession *session, gchar *buf, gint len);
+static gint smtp_auth_cram_md5(SMTPSession *session);
+static gint smtp_auth_login(SMTPSession *session);
+
+static gint smtp_ehlo(SMTPSession *session);
+static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg);
+
+static gint smtp_helo(SMTPSession *session);
+static gint smtp_rcpt(SMTPSession *session);
+static gint smtp_data(SMTPSession *session);
+static gint smtp_send_data(SMTPSession *session);
+static gint smtp_rset(SMTPSession *session);
+static gint smtp_quit(SMTPSession *session);
+static gint smtp_eom(SMTPSession *session);
+
+static gint smtp_session_recv_msg(Session *session, const gchar *msg);
+static gint smtp_session_send_data_finished(Session *session, guint len);
-static gint smtp_ok(SockInfo *sock, gchar *buf, gint len);
Session *smtp_session_new(void)
{
session = g_new0(SMTPSession, 1);
SESSION(session)->type = SESSION_SMTP;
SESSION(session)->server = NULL;
+ SESSION(session)->port = 0;
SESSION(session)->sock = NULL;
- SESSION(session)->connected = FALSE;
- SESSION(session)->phase = SESSION_READY;
- SESSION(session)->last_access_time = 0;
+ SESSION(session)->state = SESSION_READY;
SESSION(session)->data = NULL;
- SESSION(session)->destroy = smtp_session_destroy;
- SESSION(session)->ui_func = NULL;
-
- session->avail_auth_type = 0;
- session->user = NULL;
- session->pass = NULL;
-
- return SESSION(session);
-}
-
-void smtp_session_destroy(Session *session)
-{
- sock_close(session->sock);
- session->sock = NULL;
+ SESSION(session)->recv_msg = smtp_session_recv_msg;
- g_free(SMTP_SESSION(session)->user);
- g_free(SMTP_SESSION(session)->pass);
-}
+ SESSION(session)->recv_data_finished = NULL;
+ SESSION(session)->send_data_finished = smtp_session_send_data_finished;
-#if USE_OPENSSL
-gint smtp_connect(SMTPSession *session, const gchar *server, gushort port,
- const gchar *domain, const gchar *user, const gchar *pass,
- SSLType ssl_type)
-#else
-gint smtp_connect(SMTPSession *session, const gchar *server, gushort port,
- const gchar *domain, const gchar *user, const gchar *pass)
-#endif
-{
- SockInfo *sock;
- gboolean use_esmtp;
- SMTPAuthType avail_auth_type = 0;
- gint val;
+ SESSION(session)->destroy = smtp_session_destroy;
- g_return_val_if_fail(session != NULL, SM_ERROR);
- g_return_val_if_fail(server != NULL, SM_ERROR);
+ session->state = SMTP_READY;
#if USE_OPENSSL
- use_esmtp = user != NULL || ssl_type == SSL_STARTTLS;
-#else
- use_esmtp = user != NULL;
+ session->tls_init_done = FALSE;
#endif
- SESSION(session)->server = g_strdup(server);
- session->user = user ? g_strdup(user) : NULL;
- session->pass = pass ? g_strdup(pass) : user ? g_strdup("") : NULL;
-
- UI_UPDATE(session, SMTP_CONNECT);
+ session->hostname = NULL;
+ session->user = NULL;
+ session->pass = NULL;
- if ((sock = sock_connect(server, port)) == NULL) {
- log_warning(_("Can't connect to SMTP server: %s:%d\n"),
- server, port);
- return SM_ERROR;
- }
+ session->from = NULL;
+ session->to_list = NULL;
+ session->cur_to = NULL;
-#if USE_OPENSSL
- if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
- log_warning(_("SSL connection failed"));
- sock_close(sock);
- return SM_ERROR;
- }
-#endif
+ session->send_data = NULL;
+ session->send_data_len = 0;
- if (smtp_ok(sock, NULL, 0) != SM_OK) {
- log_warning(_("Error occurred while connecting to %s:%d\n"),
- server, port);
- sock_close(sock);
- return SM_ERROR;
- }
-
- SESSION(session)->sock = sock;
- SESSION(session)->connected = TRUE;
-
- if (!domain)
- domain = get_domain_name();
+ session->avail_auth_type = 0;
+ session->forced_auth_type = 0;
+ session->auth_type = 0;
- if (use_esmtp)
- val = smtp_ehlo(session, domain, &avail_auth_type);
- else
- val = smtp_helo(session, domain);
- if (val != SM_OK) {
- log_warning(use_esmtp? _("Error occurred while sending EHLO\n"):
- _("Error occurred while sending HELO\n"));
- return val;
- }
+ return SESSION(session);
+}
-#if USE_OPENSSL
- /* if we have a user to authenticate and no auth methods, but starttls,
- try to starttls */
- if (ssl_type == SSL_NONE && avail_auth_type == SMTPAUTH_TLS_AVAILABLE
- && user != NULL)
- ssl_type = SSL_STARTTLS;
-
- if (ssl_type == SSL_STARTTLS) {
- val = smtp_starttls(session);
- if (val != SM_OK) {
- log_warning(_("Error occurred while sending STARTTLS\n"));
- return val;
- }
- if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
- return SM_ERROR;
- }
- val = smtp_ehlo(session, domain, &avail_auth_type);
- if (val != SM_OK) {
- log_warning(_("Error occurred while sending EHLO\n"));
- return val;
- }
- }
-#endif
+static void smtp_session_destroy(Session *session)
+{
+ SMTPSession *smtp_session = SMTP_SESSION(session);
- session->avail_auth_type = avail_auth_type;
+ g_free(smtp_session->hostname);
+ g_free(smtp_session->user);
+ g_free(smtp_session->pass);
+ g_free(smtp_session->from);
- return 0;
+ g_free(smtp_session->send_data);
}
-gint smtp_from(SMTPSession *session, const gchar *from)
+static gint smtp_from(SMTPSession *session)
{
gchar buf[MSGBUFSIZE];
- g_return_val_if_fail(session != NULL, SM_ERROR);
- g_return_val_if_fail(from != NULL, SM_ERROR);
+ g_return_val_if_fail(session->from != NULL, SM_ERROR);
- UI_UPDATE(session, SMTP_FROM);
+ session->state = SMTP_FROM;
- if (strchr(from, '<'))
- g_snprintf(buf, sizeof(buf), "MAIL FROM: %s", from);
+ if (strchr(session->from, '<'))
+ g_snprintf(buf, sizeof(buf), "MAIL FROM: %s", session->from);
else
- g_snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", from);
+ g_snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", session->from);
- sock_printf(SESSION(session)->sock, "%s\r\n", buf);
- if (verbose)
- log_print("SMTP> %s\n", buf);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+ log_print("SMTP> %s\n", buf);
- return smtp_ok(SESSION(session)->sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_auth(SMTPSession *session, SMTPAuthType forced_auth_type)
+static gint smtp_auth(SMTPSession *session)
{
- gchar buf[MSGBUFSIZE];
- SMTPAuthType authtype = 0;
- guchar hexdigest[33];
- gchar *challenge, *response, *response64;
- gint challengelen;
- SockInfo *sock;
- g_return_val_if_fail(session != NULL, SM_ERROR);
g_return_val_if_fail(session->user != NULL, SM_ERROR);
- UI_UPDATE(session, SMTP_AUTH);
+ session->state = SMTP_AUTH;
- sock = SESSION(session)->sock;
-
- if ((forced_auth_type == SMTPAUTH_CRAM_MD5 ||
- (forced_auth_type == 0 &&
- (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0)) &&
- smtp_auth_cram_md5(session, buf, sizeof(buf)) == SM_OK)
- authtype = SMTPAUTH_CRAM_MD5;
- else if ((forced_auth_type == SMTPAUTH_LOGIN ||
- (forced_auth_type == 0 &&
- (session->avail_auth_type & SMTPAUTH_LOGIN) != 0)) &&
- smtp_auth_login(session, buf, sizeof(buf)) == SM_OK)
- authtype = SMTPAUTH_LOGIN;
+ if (session->forced_auth_type == SMTPAUTH_CRAM_MD5 ||
+ (session->forced_auth_type == 0 &&
+ (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0))
+ smtp_auth_cram_md5(session);
+ else if (session->forced_auth_type == SMTPAUTH_LOGIN ||
+ (session->forced_auth_type == 0 &&
+ (session->avail_auth_type & SMTPAUTH_LOGIN) != 0))
+ smtp_auth_login(session);
else {
log_warning(_("SMTP AUTH not available\n"));
return SM_AUTHFAIL;
}
- switch (authtype) {
- case SMTPAUTH_LOGIN:
- if (!strncmp(buf, "334 ", 4))
- base64_encode(buf, session->user, strlen(session->user));
- else
- /* Server rejects AUTH */
- g_snprintf(buf, sizeof(buf), "*");
+ return SM_OK;
+}
- sock_printf(sock, "%s\r\n", buf);
- if (verbose) log_print("ESMTP> [USERID]\n");
+static gint smtp_auth_recv(SMTPSession *session, const gchar *msg)
+{
+ gchar buf[MSGBUFSIZE];
- smtp_ok(sock, buf, sizeof(buf));
+ switch (session->auth_type) {
+ case SMTPAUTH_LOGIN:
+ session->state = SMTP_AUTH_LOGIN_USER;
- if (!strncmp(buf, "334 ", 4))
- base64_encode(buf, session->pass, strlen(session->pass));
- else
- /* Server rejects AUTH */
- g_snprintf(buf, sizeof(buf), "*");
+ if (!strncmp(msg, "334 ", 4)) {
+ base64_encode(buf, session->user, strlen(session->user));
- sock_printf(sock, "%s\r\n", buf);
- if (verbose) log_print("ESMTP> [PASSWORD]\n");
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+ buf);
+ log_print("ESMTP> [USERID]\n");
+ } else {
+ /* Server rejects AUTH */
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+ "*");
+ log_print("ESMTP> *\n");
+ }
break;
case SMTPAUTH_CRAM_MD5:
+ session->state = SMTP_AUTH_CRAM_MD5;
+
if (!strncmp(buf, "334 ", 4)) {
+ gchar *response;
+ gchar *response64;
+ gchar *challenge;
+ gint challengelen;
+ guchar hexdigest[33];
+
challenge = g_malloc(strlen(buf + 4) + 1);
challengelen = base64_decode(challenge, buf + 4, -1);
challenge[challengelen] = '\0';
- if (verbose)
- log_print("ESMTP< [Decoded: %s]\n", challenge);
+ log_print("ESMTP< [Decoded: %s]\n", challenge);
g_snprintf(buf, sizeof(buf), "%s", session->pass);
md5_hex_hmac(hexdigest, challenge, challengelen,
response = g_strdup_printf
("%s %s", session->user, hexdigest);
- if (verbose)
- log_print("ESMTP> [Encoded: %s]\n", response);
+ log_print("ESMTP> [Encoded: %s]\n", response);
response64 = g_malloc((strlen(response) + 3) * 2 + 1);
base64_encode(response64, response, strlen(response));
g_free(response);
- sock_printf(sock, "%s\r\n", response64);
- if (verbose) log_print("ESMTP> %s\n", response64);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+ response64);
+ log_print("ESMTP> %s\n", response64);
g_free(response64);
} else {
/* Server rejects AUTH */
- g_snprintf(buf, sizeof(buf), "*");
- sock_printf(sock, "%s\r\n", buf);
- if (verbose)
- log_print("ESMTP> %s\n", buf);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+ "*");
+ log_print("ESMTP> *\n");
}
break;
case SMTPAUTH_DIGEST_MD5:
default:
/* stop smtp_auth when no correct authtype */
- g_snprintf(buf, sizeof(buf), "*");
- sock_printf(sock, "%s\r\n", buf);
- if (verbose) log_print("ESMTP> %s\n", buf);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*");
+ log_print("ESMTP> *\n");
break;
}
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_ehlo(SMTPSession *session, const gchar *hostname,
- SMTPAuthType *avail_auth_type)
+static gint smtp_auth_login_user_recv(SMTPSession *session, const gchar *msg)
{
- SockInfo *sock;
gchar buf[MSGBUFSIZE];
- UI_UPDATE(session, SMTP_EHLO);
-
- sock = SESSION(session)->sock;
-
- *avail_auth_type = 0;
-
- sock_printf(sock, "EHLO %s\r\n", hostname);
- if (verbose)
- log_print("ESMTP> EHLO %s\n", hostname);
-
- while ((sock_gets(sock, buf, sizeof(buf) - 1)) != -1) {
- if (strlen(buf) < 4)
- return SM_ERROR;
- strretchomp(buf);
-
- if (verbose)
- log_print("ESMTP< %s\n", buf);
-
- if (strncmp(buf, "250-", 4) == 0) {
- gchar *p = buf;
- p += 4;
- if (g_strncasecmp(p, "AUTH", 4) == 0) {
- p += 5;
- if (strcasestr(p, "LOGIN"))
- *avail_auth_type |= SMTPAUTH_LOGIN;
- if (strcasestr(p, "CRAM-MD5"))
- *avail_auth_type |= SMTPAUTH_CRAM_MD5;
- if (strcasestr(p, "DIGEST-MD5"))
- *avail_auth_type |= SMTPAUTH_DIGEST_MD5;
- } else if (g_strncasecmp(p, "STARTTLS", 8) == 0) {
- p += 9;
- *avail_auth_type |= SMTPAUTH_TLS_AVAILABLE;
- }
- } else if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
- (buf[3] == ' ' || buf[3] == '\0'))
- return SM_OK;
- else if (buf[3] != '-')
- return SM_ERROR;
- else if (buf[0] == '5' && buf[1] == '0' &&
- (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
- return SM_ERROR;
- }
+ session->state = SMTP_AUTH_LOGIN_PASS;
+
+ if (!strncmp(msg, "334 ", 4))
+ base64_encode(buf, session->pass, strlen(session->pass));
+ else
+ /* Server rejects AUTH */
+ g_snprintf(buf, sizeof(buf), "*");
+
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+ log_print("ESMTP> [PASSWORD]\n");
- return SM_UNRECOVERABLE;
+ return SM_OK;
}
-static gint smtp_starttls(SMTPSession *session)
+static gint smtp_ehlo(SMTPSession *session)
{
- SockInfo *sock;
+ gchar buf[MSGBUFSIZE];
- UI_UPDATE(session, SMTP_STARTTLS);
+ session->state = SMTP_EHLO;
- sock = SESSION(session)->sock;
+ session->avail_auth_type = 0;
- sock_printf(sock, "STARTTLS\r\n");
- if (verbose)
- log_print("ESMTP> STARTTLS\n");
+ g_snprintf(buf, sizeof(buf), "EHLO %s",
+ session->hostname ? session->hostname : get_domain_name());
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+ log_print("ESMTP> %s\n", buf);
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-static gint smtp_auth_cram_md5(SMTPSession *session, gchar *buf, gint len)
+static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
{
- SockInfo *sock;
+ if (strncmp(msg, "250", 3) == 0) {
+ const gchar *p = msg;
+ p += 3;
+ if (*p == '-' || *p == ' ') p++;
+ if (g_strncasecmp(p, "AUTH", 4) == 0) {
+ p += 5;
+ if (strcasestr(p, "LOGIN"))
+ session->avail_auth_type |= SMTPAUTH_LOGIN;
+ if (strcasestr(p, "CRAM-MD5"))
+ session->avail_auth_type |= SMTPAUTH_CRAM_MD5;
+ if (strcasestr(p, "DIGEST-MD5"))
+ session->avail_auth_type |= SMTPAUTH_DIGEST_MD5;
+ }
+ return SM_OK;
+ } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') &&
+ (msg[3] == ' ' || msg[3] == '\0'))
+ return SM_OK;
+ else if (msg[0] == '5' && msg[1] == '0' &&
+ (msg[2] == '4' || msg[2] == '3' || msg[2] == '1'))
+ return SM_ERROR;
- UI_UPDATE(session, SMTP_AUTH);
+ return SM_ERROR;
+}
- sock = SESSION(session)->sock;
+static gint smtp_starttls(SMTPSession *session)
+{
+ session->state = SMTP_STARTTLS;
- sock_printf(sock, "AUTH CRAM-MD5\r\n");
- if (verbose)
- log_print("ESMTP> AUTH CRAM-MD5\n");
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS");
+ log_print("ESMTP> STARTTLS\n");
- return smtp_ok(sock, buf, len);
+ return SM_OK;
}
-static gint smtp_auth_login(SMTPSession *session, gchar *buf, gint len)
+static gint smtp_auth_cram_md5(SMTPSession *session)
{
- SockInfo *sock;
+ session->state = SMTP_AUTH;
+ session->auth_type = SMTPAUTH_CRAM_MD5;
- UI_UPDATE(session, SMTP_AUTH);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5");
+ log_print("ESMTP> AUTH CRAM-MD5\n");
- sock = SESSION(session)->sock;
+ return SM_OK;
+}
- sock_printf(sock, "AUTH LOGIN\r\n");
- if (verbose)
- log_print("ESMTP> AUTH LOGIN\n");
+static gint smtp_auth_login(SMTPSession *session)
+{
+ session->state = SMTP_AUTH;
+ session->auth_type = SMTPAUTH_LOGIN;
+
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN");
+ log_print("ESMTP> AUTH LOGIN\n");
- return smtp_ok(sock, buf, len);
+ return SM_OK;
}
-gint smtp_helo(SMTPSession *session, const gchar *hostname)
+static gint smtp_helo(SMTPSession *session)
{
- SockInfo *sock;
-
- UI_UPDATE(session, SMTP_HELO);
+ gchar buf[MSGBUFSIZE];
- sock = SESSION(session)->sock;
+ session->state = SMTP_HELO;
- sock_printf(sock, "HELO %s\r\n", hostname);
- if (verbose)
- log_print("SMTP> HELO %s\n", hostname);
+ g_snprintf(buf, sizeof(buf), "HELO %s",
+ session->hostname ? session->hostname : get_domain_name());
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+ log_print("SMTP> %s\n", buf);
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_rcpt(SMTPSession *session, const gchar *to)
+static gint smtp_rcpt(SMTPSession *session)
{
- SockInfo *sock;
gchar buf[MSGBUFSIZE];
+ gchar *to;
- UI_UPDATE(session, SMTP_RCPT);
+ g_return_val_if_fail(session->cur_to != NULL, SM_ERROR);
- sock = SESSION(session)->sock;
+ session->state = SMTP_RCPT;
+
+ to = (gchar *)session->cur_to->data;
if (strchr(to, '<'))
g_snprintf(buf, sizeof(buf), "RCPT TO: %s", to);
else
g_snprintf(buf, sizeof(buf), "RCPT TO: <%s>", to);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+ log_print("SMTP> %s\n", buf);
- sock_printf(sock, "%s\r\n", buf);
- if (verbose)
- log_print("SMTP> %s\n", buf);
+ session->cur_to = session->cur_to->next;
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_data(SMTPSession *session)
+static gint smtp_data(SMTPSession *session)
{
- SockInfo *sock;
-
- UI_UPDATE(session, SMTP_DATA);
+ session->state = SMTP_DATA;
- sock = SESSION(session)->sock;
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA");
+ log_print("SMTP> DATA\n");
- sock_printf(sock, "DATA\r\n");
- if (verbose)
- log_print("SMTP> DATA\n");
-
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_rset(SMTPSession *session)
+static gint smtp_send_data(SMTPSession *session)
{
- SockInfo *sock;
+ session->state = SMTP_SEND_DATA;
- UI_UPDATE(session, SMTP_RSET);
+ session_send_data(SESSION(session), session->send_data,
+ session->send_data_len);
- sock = SESSION(session)->sock;
+ return SM_OK;
+}
- sock_printf(sock, "RSET\r\n");
- if (verbose)
- log_print("SMTP> RSET\n");
+static gint smtp_rset(SMTPSession *session)
+{
+ session->state = SMTP_RSET;
+
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET");
+ log_print("SMTP> RSET\n");
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_quit(SMTPSession *session)
+static gint smtp_quit(SMTPSession *session)
{
- SockInfo *sock;
+ session->state = SMTP_QUIT;
- UI_UPDATE(session, SMTP_QUIT);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "QUIT");
+ log_print("SMTP> QUIT\n");
- sock = SESSION(session)->sock;
+ return SM_OK;
+}
- sock_printf(sock, "QUIT\r\n");
- if (verbose)
- log_print("SMTP> QUIT\n");
+static gint smtp_eom(SMTPSession *session)
+{
+ session->state = SMTP_EOM;
- return smtp_ok(sock, NULL, 0);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".");
+ log_print("SMTP> . (EOM)\n");
+
+ return SM_OK;
}
-gint smtp_eom(SMTPSession *session)
+static gint smtp_session_recv_msg(Session *session, const gchar *msg)
{
- SockInfo *sock;
-
- UI_UPDATE(session, SMTP_EOM);
+ SMTPSession *smtp_session = SMTP_SESSION(session);
+ gboolean cont = FALSE;
- sock = SESSION(session)->sock;
+ if (strlen(msg) < 4)
+ return -1;
- sock_printf(sock, ".\r\n");
- if (verbose)
- log_print("SMTP> . (EOM)\n");
+ log_print("SMTP< %s\n", msg);
- return smtp_ok(sock, NULL, 0);
-}
+ if (msg[0] == '5' && msg[1] == '0' &&
+ (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) {
+ smtp_session->state = SMTP_ERROR;
+ return -1;
+ }
-static gint smtp_ok(SockInfo *sock, gchar *buf, gint len)
-{
- gchar tmpbuf[MSGBUFSIZE];
+ if (msg[0] != '1' && msg[0] != '2' && msg[0] != '3') {
+ smtp_session->state = SMTP_ERROR;
+ return -1;
+ }
- if (!buf) {
- buf = tmpbuf;
- len = sizeof(tmpbuf);
+ if (msg[3] == '-')
+ cont = TRUE;
+ else if (msg[3] != ' ' && msg[3] != '\0') {
+ smtp_session->state = SMTP_ERROR;
+ return -1;
}
- while ((sock_gets(sock, buf, len - 1)) != -1) {
- if (strlen(buf) < 4)
- return SM_ERROR;
- strretchomp(buf);
-
- if (verbose)
- log_print("SMTP< %s\n", buf);
-
- if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
- (buf[3] == ' ' || buf[3] == '\0'))
- return SM_OK;
- else if (buf[3] != '-')
- return SM_ERROR;
- else if (buf[0] == '5' && buf[1] == '0' &&
- (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
- return SM_ERROR;
+ switch (smtp_session->state) {
+ case SMTP_READY:
+ case SMTP_CONNECTED:
+#if USE_OPENSSL
+ if (smtp_session->user || session->ssl_type != SSL_NONE)
+#else
+ if (smtp_session->user)
+#endif
+ smtp_ehlo(smtp_session);
+ else
+ smtp_helo(smtp_session);
+ break;
+ case SMTP_HELO:
+ smtp_from(smtp_session);
+ break;
+ case SMTP_EHLO:
+ smtp_ehlo_recv(smtp_session, msg);
+ if (cont == TRUE)
+ break;
+#if USE_OPENSSL
+ if (session->ssl_type == SSL_STARTTLS &&
+ smtp_session->tls_init_done == FALSE) {
+ smtp_starttls(smtp_session);
+ break;
+ }
+#endif
+ if (smtp_session->user) {
+ if (smtp_auth(smtp_session) != SM_OK)
+ smtp_from(smtp_session);
+ } else
+ smtp_from(smtp_session);
+ break;
+ case SMTP_STARTTLS:
+#if USE_OPENSSL
+ if (session_start_tls(session) < 0)
+ return -1;
+ smtp_session->tls_init_done = TRUE;
+ smtp_ehlo(smtp_session);
+#endif
+ break;
+ case SMTP_AUTH:
+ smtp_auth_recv(smtp_session, msg);
+ break;
+ case SMTP_AUTH_LOGIN_USER:
+ smtp_auth_login_user_recv(smtp_session, msg);
+ break;
+ case SMTP_AUTH_LOGIN_PASS:
+ case SMTP_AUTH_CRAM_MD5:
+ smtp_from(smtp_session);
+ break;
+ case SMTP_FROM:
+ if (smtp_session->cur_to)
+ smtp_rcpt(smtp_session);
+ break;
+ case SMTP_RCPT:
+ if (smtp_session->cur_to)
+ smtp_rcpt(smtp_session);
+ else
+ smtp_data(smtp_session);
+ break;
+ case SMTP_DATA:
+ smtp_send_data(smtp_session);
+ break;
+ case SMTP_EOM:
+ smtp_quit(smtp_session);
+ break;
+ case SMTP_QUIT:
+ session_disconnect(session);
+ break;
+ case SMTP_ERROR:
+ default:
+ return -1;
}
- return SM_UNRECOVERABLE;
+ if (cont)
+ return 1;
+
+ return 0;
+}
+
+static gint smtp_session_send_data_finished(Session *session, guint len)
+{
+ smtp_eom(SMTP_SESSION(session));
+ return 0;
}
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2002 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
*
* 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
#include <glib.h>
-#include "socket.h"
-#if USE_OPENSSL
-# include "ssl.h"
-#endif
#include "session.h"
typedef struct _SMTPSession SMTPSession;
typedef enum
{
- SMTP_CONNECT,
+ SMTP_READY,
+ SMTP_CONNECTED,
SMTP_HELO,
SMTP_EHLO,
SMTP_STARTTLS,
SMTP_FROM,
SMTP_AUTH,
+ SMTP_AUTH_LOGIN_USER,
+ SMTP_AUTH_LOGIN_PASS,
+ SMTP_AUTH_CRAM_MD5,
SMTP_RCPT,
SMTP_DATA,
+ SMTP_SEND_DATA,
+ SMTP_EOM,
SMTP_RSET,
SMTP_QUIT,
- SMTP_EOM,
+ SMTP_ERROR,
+ SMTP_DISCONNECTED,
N_SMTP_PHASE
-} SMTPPhase;
+} SMTPState;
struct _SMTPSession
{
Session session;
- SMTPAuthType avail_auth_type;
+ SMTPState state;
+
+#if USE_OPENSSL
+ gboolean tls_init_done;
+#endif
+
+ gchar *hostname;
+
gchar *user;
gchar *pass;
-};
-Session *smtp_session_new (void);
-void smtp_session_destroy (Session *session);
+ gchar *from;
+ GSList *to_list;
+ GSList *cur_to;
-#if USE_OPENSSL
-gint smtp_connect (SMTPSession *session,
- const gchar *server,
- gushort port,
- const gchar *domain,
- const gchar *user,
- const gchar *pass,
- SSLType ssl_type);
-#else
-gint smtp_connect (SMTPSession *session,
- const gchar *server,
- gushort port,
- const gchar *domain,
- const gchar *user,
- const gchar *pass);
-#endif
+ guchar *send_data;
+ guint send_data_len;
-gint smtp_from (SMTPSession *session,
- const gchar *from);
-gint smtp_auth (SMTPSession *session,
- SMTPAuthType forced_auth_type);
-
-gint smtp_ehlo (SMTPSession *session,
- const gchar *hostname,
- SMTPAuthType *avail_auth_type);
-
-gint smtp_helo (SMTPSession *session,
- const gchar *hostname);
-gint smtp_rcpt (SMTPSession *session,
- const gchar *to);
-gint smtp_data (SMTPSession *session);
-gint smtp_rset (SMTPSession *session);
-gint smtp_quit (SMTPSession *session);
-gint smtp_eom (SMTPSession *session);
+ SMTPAuthType avail_auth_type;
+ SMTPAuthType forced_auth_type;
+ SMTPAuthType auth_type;
+};
+
+Session *smtp_session_new (void);
#endif /* __SMTP_H__ */
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2002 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
*
* 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
g_vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
- return sock_write(sock, buf, strlen(buf));
+ return sock_write_all(sock, buf, strlen(buf));
}
gint sock_read(SockInfo *sock, gchar *buf, gint len)
}
gint fd_write(gint fd, const gchar *buf, gint len)
+{
+ if (fd_check_io(fd, G_IO_OUT) < 0)
+ return -1;
+
+ return write(fd, buf, len);
+}
+
+#if USE_OPENSSL
+gint ssl_write(SSL *ssl, const gchar *buf, gint len)
+{
+ return SSL_write(ssl, buf, len);
+}
+#endif
+
+gint sock_write_all(SockInfo *sock, const gchar *buf, gint len)
+{
+ g_return_val_if_fail(sock != NULL, -1);
+
+#if USE_OPENSSL
+ if (sock->ssl)
+ return ssl_write_all(sock->ssl, buf, len);
+#endif
+ return fd_write_all(sock->sock, buf, len);
+}
+
+gint fd_write_all(gint fd, const gchar *buf, gint len)
{
gint n, wrlen = 0;
}
#if USE_OPENSSL
-gint ssl_write(SSL *ssl, const gchar *buf, gint len)
+gint ssl_write_all(SSL *ssl, const gchar *buf, gint len)
{
gint n, wrlen = 0;
{
gint ret;
- if ((ret = sock_write(sock, buf, strlen(buf))) < 0)
+ if ((ret = sock_write_all(sock, buf, strlen(buf))) < 0)
return ret;
- return sock_write(sock, "\r\n", 2);
+ return sock_write_all(sock, "\r\n", 2);
}
/* peek at the next socket character without actually reading it */
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2001 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
*
* 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
G_GNUC_PRINTF(2, 3);
gint sock_read (SockInfo *sock, gchar *buf, gint len);
gint sock_write (SockInfo *sock, const gchar *buf, gint len);
+gint sock_write_all (SockInfo *sock, const gchar *buf, gint len);
gint sock_gets (SockInfo *sock, gchar *buf, gint len);
gchar *sock_getline (SockInfo *sock);
gint sock_puts (SockInfo *sock, const gchar *buf);
gint fd_read (gint sock, gchar *buf, gint len);
gint fd_write (gint sock, const gchar *buf, gint len);
+gint fd_write_all (gint sock, const gchar *buf, gint len);
gint fd_gets (gint sock, gchar *buf, gint len);
gchar *fd_getline (gint sock);
gint fd_close (gint sock);
/* Functions for SSL */
-#if USE_OPENSSL
-gint ssl_read(SSL *ssl, gchar *buf, gint len);
-gint ssl_write(SSL *ssl, const gchar *buf, gint len);
-gint ssl_gets(SSL *ssl, gchar *buf, gint len);
-gchar *ssl_getline(SSL *ssl);
+#if USE_SSL
+gint ssl_read (SSL *ssl, gchar *buf, gint len);
+gint ssl_write (SSL *ssl, const gchar *buf, gint len);
+gint ssl_write_all (SSL *ssl, const gchar *buf, gint len);
+gint ssl_gets (SSL *ssl, gchar *buf, gint len);
+gchar *ssl_getline (SSL *ssl);
#endif
#endif /* __SOCKET_H__ */
return 0;
}
+gchar *get_outgoing_rfc2822_str(FILE *fp)
+{
+ gchar buf[BUFFSIZE];
+ GString *str;
+ gchar *ret;
+
+ str = g_string_new(NULL);
+
+ /* output header part */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ strretchomp(buf);
+ if (!g_strncasecmp(buf, "Bcc:", 4)) {
+ gint next;
+
+ for (;;) {
+ next = fgetc(fp);
+ if (next == EOF)
+ break;
+ else if (next != ' ' && next != '\t') {
+ ungetc(next, fp);
+ break;
+ }
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ break;
+ }
+ } else {
+ g_string_append(str, buf);
+ g_string_append(str, "\r\n");
+ if (buf[0] == '\0')
+ break;
+ }
+ }
+
+ /* output body part */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ strretchomp(buf);
+ if (buf[0] == '.')
+ g_string_append_c(str, '.');
+ g_string_append(str, buf);
+ g_string_append(str, "\r\n");
+ }
+
+ ret = str->str;
+ g_string_free(str, FALSE);
+
+ return ret;
+}
+
gint change_file_mode_rw(FILE *fp, const gchar *file)
{
#if HAVE_FCHMOD
gchar *file_read_to_str(const gchar *file)
{
- GByteArray *array;
FILE *fp;
- gchar buf[BUFSIZ];
- gint n_read;
gchar *str;
g_return_val_if_fail(file != NULL, NULL);
return NULL;
}
+ str = file_read_stream_to_str(fp);
+
+ fclose(fp);
+
+ return str;
+}
+
+gchar *file_read_stream_to_str(FILE *fp)
+{
+ GByteArray *array;
+ gchar buf[BUFSIZ];
+ gint n_read;
+ gchar *str;
+
+ g_return_val_if_fail(fp != NULL, NULL);
+
array = g_byte_array_new();
while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
}
if (ferror(fp)) {
- FILE_OP_ERROR(file, "fread");
- fclose(fp);
+ FILE_OP_ERROR("file stream", "fread");
g_byte_array_free(array, TRUE);
return NULL;
}
- fclose(fp);
-
buf[0] = '\0';
g_byte_array_append(array, buf, 1);
str = (gchar *)array->data;
const gchar *dest);
gint uncanonicalize_file_replace(const gchar *file);
+gchar *get_outgoing_rfc2822_str (FILE *fp);
+
gint change_file_mode_rw (FILE *fp,
const gchar *file);
FILE *my_tmpfile (void);
gint str_write_to_file (const gchar *str,
const gchar *file);
gchar *file_read_to_str (const gchar *file);
+gchar *file_read_stream_to_str (FILE *fp);
/* process execution */
gint execute_async (gchar *const argv[]);
#include "procmsg.h"
#include "menu.h"
#include "stock_pixmap.h"
-#include "send.h"
+#include "send_message.h"
#include "imap.h"
#include "news.h"
#include "customheader.h"
close(pipe_fds[0]);
if (compose_write_body_to_file(compose, tmp) < 0) {
- fd_write(pipe_fds[1], "2\n", 2);
+ fd_write_all(pipe_fds[1], "2\n", 2);
_exit(1);
}
pid_ed = compose_exec_ext_editor_real(tmp);
if (pid_ed < 0) {
- fd_write(pipe_fds[1], "1\n", 2);
+ fd_write_all(pipe_fds[1], "1\n", 2);
_exit(1);
}
/* wait until editor is terminated */
waitpid(pid_ed, NULL, 0);
- fd_write(pipe_fds[1], "0\n", 2);
+ fd_write_all(pipe_fds[1], "0\n", 2);
close(pipe_fds[1]);
_exit(0);
SESSION(session)->type = SESSION_IMAP;
SESSION(session)->server = g_strdup(account->recv_server);
SESSION(session)->sock = imap_sock;
- SESSION(session)->connected = TRUE;
- SESSION(session)->phase = SESSION_READY;
SESSION(session)->last_access_time = time(NULL);
SESSION(session)->data = NULL;
} else
log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
- sock_write(sock, buf, strlen(buf));
+ sock_write_all(sock, buf, strlen(buf));
}
static gint imap_cmd_gen_recv(SockInfo *sock, gchar **buf)
debug_print("another Sylpheed is already running.\n");
if (cmd.receive_all)
- fd_write(uxsock, "receive_all\n", 12);
+ fd_write_all(uxsock, "receive_all\n", 12);
else if (cmd.receive)
- fd_write(uxsock, "receive\n", 8);
+ fd_write_all(uxsock, "receive\n", 8);
else if (cmd.compose && cmd.attach_files) {
gchar *str, *compose_str;
gint i;
else
compose_str = g_strdup("compose_attach\n");
- fd_write(uxsock, compose_str, strlen(compose_str));
+ fd_write_all(uxsock, compose_str, strlen(compose_str));
g_free(compose_str);
for (i = 0; i < cmd.attach_files->len; i++) {
str = g_ptr_array_index(cmd.attach_files, i);
- fd_write(uxsock, str, strlen(str));
- fd_write(uxsock, "\n", 1);
+ fd_write_all(uxsock, str, strlen(str));
+ fd_write_all(uxsock, "\n", 1);
}
- fd_write(uxsock, ".\n", 2);
+ fd_write_all(uxsock, ".\n", 2);
} else if (cmd.compose) {
gchar *compose_str;
else
compose_str = g_strdup("compose\n");
- fd_write(uxsock, compose_str, strlen(compose_str));
+ fd_write_all(uxsock, compose_str, strlen(compose_str));
g_free(compose_str);
} else if (cmd.send) {
- fd_write(uxsock, "send\n", 5);
+ fd_write_all(uxsock, "send\n", 5);
} else if (cmd.online_mode == ONLINE_MODE_ONLINE) {
fd_write(uxsock, "online\n", 6);
} else if (cmd.online_mode == ONLINE_MODE_OFFLINE) {
} else if (cmd.status) {
gchar buf[BUFFSIZE];
- fd_write(uxsock, "status\n", 7);
+ fd_write_all(uxsock, "status\n", 7);
fd_gets(uxsock, buf, sizeof(buf));
fputs(buf, stdout);
} else
- fd_write(uxsock, "popup\n", 6);
+ fd_write_all(uxsock, "popup\n", 6);
fd_close(uxsock);
return -1;
folder_count_total_msgs(&new, &unread, &unreadmarked, &total);
g_snprintf(buf, sizeof(buf), "%d %d %d %d\n", new, unread, unreadmarked, total);
- fd_write(sock, buf, strlen(buf));
+ fd_write_all(sock, buf, strlen(buf));
}
fd_close(sock);
#include "about.h"
#include "account.h"
#include "alertpanel.h"
-#include "send.h"
+#include "send_message.h"
#include "pgptext.h"
#include "menu.h"
#include "stock_pixmap.h"
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2002 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
*
* 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
SESSION(session)->server = g_strdup(server);
session->nntp_sock = nntp_sock;
SESSION(session)->sock = nntp_sock->sock;
- SESSION(session)->connected = TRUE;
- SESSION(session)->phase = SESSION_READY;
SESSION(session)->last_access_time = time(NULL);
SESSION(session)->data = NULL;
log_print("POP3> %s\n", buf);
strcat(buf, "\r\n");
- sock_write(sock, buf, strlen(buf));
+ sock_write_all(sock, buf, strlen(buf));
}
static gint pop3_gen_recv(SockInfo *sock, gchar *buf, gint size)
#include "utils.h"
#include "procmsg.h"
#include "procheader.h"
-#include "send.h"
+#include "send_message.h"
#include "procmime.h"
#include "statusbar.h"
#include "folder.h"
--- /dev/null
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkclist.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include "intl.h"
+#include "send_message.h"
+#include "session.h"
+#include "ssl.h"
+#include "smtp.h"
+#include "prefs_common.h"
+#include "prefs_account.h"
+#include "procheader.h"
+#include "account.h"
+#include "progressdialog.h"
+#include "inputdialog.h"
+#include "manage_window.h"
+#include "utils.h"
+#include "gtkutils.h"
+#include "statusbar.h"
+#include "inc.h"
+#include "log.h"
+
+typedef struct _SendProgressDialog SendProgressDialog;
+
+struct _SendProgressDialog
+{
+ ProgressDialog *dialog;
+ Session *session;
+ gboolean cancelled;
+};
+#if 0
+static gint send_message_local (const gchar *command,
+ FILE *fp);
+static gint send_message_smtp (PrefsAccount *ac_prefs,
+ GSList *to_list,
+ FILE *fp);
+#endif
+
+static gint send_recv_message (Session *session,
+ const gchar *msg,
+ gpointer data);
+static gint send_send_data_progressive (Session *session,
+ guint cur_len,
+ guint total_len,
+ gpointer data);
+static gint send_send_data_finished (Session *session,
+ guint len,
+ gpointer data);
+
+static SendProgressDialog *send_progress_dialog_create (void);
+static void send_progress_dialog_destroy (SendProgressDialog *dialog);
+
+static void send_cancel_button_cb (GtkWidget *widget,
+ gpointer data);
+
+gint send_message(const gchar *file, PrefsAccount *ac_prefs, GSList *to_list)
+{
+ FILE *fp;
+ gint val;
+
+ g_return_val_if_fail(file != NULL, -1);
+ g_return_val_if_fail(ac_prefs != NULL, -1);
+ g_return_val_if_fail(to_list != NULL, -1);
+
+ if ((fp = fopen(file, "rb")) == NULL) {
+ FILE_OP_ERROR(file, "fopen");
+ return -1;
+ }
+
+ if (ac_prefs->use_mail_command && ac_prefs->mail_command &&
+ (*ac_prefs->mail_command)) {
+ val = send_message_local(ac_prefs->mail_command, fp);
+ fclose(fp);
+ return val;
+ }
+ else if (prefs_common.use_extsend && prefs_common.extsend_cmd) {
+ val = send_message_local(prefs_common.extsend_cmd, fp);
+ fclose(fp);
+ return val;
+ }
+ else {
+ val = send_message_smtp(ac_prefs, to_list, fp);
+
+ fclose(fp);
+ return val;
+ }
+}
+
+enum
+{
+ Q_SENDER = 0,
+ Q_SMTPSERVER = 1,
+ Q_RECIPIENTS = 2,
+ Q_ACCOUNT_ID = 3
+};
+
+#if 0
+gint send_message_queue(const gchar *file)
+{
+ static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
+ {"SSV:", NULL, FALSE},
+ {"R:", NULL, FALSE},
+ {"AID:", NULL, FALSE},
+ {NULL, NULL, FALSE}};
+ FILE *fp;
+ gint val = 0;
+ gchar *from = NULL;
+ gchar *server = NULL;
+ GSList *to_list = NULL;
+ gchar buf[BUFFSIZE];
+ gint hnum;
+ glong fpos;
+ PrefsAccount *ac = NULL, *mailac = NULL, *newsac = NULL;
+
+ g_return_val_if_fail(file != NULL, -1);
+
+ if ((fp = fopen(file, "rb")) == NULL) {
+ FILE_OP_ERROR(file, "fopen");
+ return -1;
+ }
+
+ while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
+ != -1) {
+ gchar *p;
+
+ p = buf + strlen(qentry[hnum].name);
+
+ switch (hnum) {
+ case Q_SENDER:
+ if (!from) from = g_strdup(p);
+ break;
+ case Q_SMTPSERVER:
+ if (!server) server = g_strdup(p);
+ break;
+ case Q_RECIPIENTS:
+ to_list = address_list_append(to_list, p);
+ break;
+ case Q_ACCOUNT_ID:
+ ac = account_find_from_id(atoi(p));
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (((!ac || (ac && ac->protocol != A_NNTP)) && !to_list) || !from) {
+ g_warning("Queued message header is broken.\n");
+ val = -1;
+ } else if (prefs_common.use_extsend && prefs_common.extsend_cmd) {
+ val = send_message_local(prefs_common.extsend_cmd, fp);
+ } else {
+ if (ac && ac->protocol == A_NNTP) {
+ newsac = ac;
+
+ /* search mail account */
+ mailac = account_find_from_address(from);
+ if (!mailac) {
+ if (cur_account &&
+ cur_account->protocol != A_NNTP)
+ mailac = cur_account;
+ else {
+ mailac = account_get_default();
+ if (mailac->protocol == A_NNTP)
+ mailac = NULL;
+ }
+ }
+ } else if (ac) {
+ mailac = ac;
+ } else {
+ ac = account_find_from_smtp_server(from, server);
+ if (!ac) {
+ g_warning("Account not found. "
+ "Using current account...\n");
+ ac = cur_account;
+ if (ac && ac->protocol != A_NNTP)
+ mailac = ac;
+ }
+ }
+
+ fpos = ftell(fp);
+ if (to_list) {
+ if (mailac)
+ val = send_message_smtp(mailac, to_list, fp);
+ else {
+ PrefsAccount tmp_ac;
+
+ g_warning("Account not found.\n");
+
+ memset(&tmp_ac, 0, sizeof(PrefsAccount));
+ tmp_ac.address = from;
+ tmp_ac.smtp_server = server;
+ tmp_ac.smtpport = SMTP_PORT;
+ val = send_message_smtp(&tmp_ac, to_list, fp);
+ }
+ }
+
+ if (val == 0 && newsac) {
+ fseek(fp, fpos, SEEK_SET);
+ val = news_post_stream(FOLDER(newsac->folder), fp);
+ }
+ }
+
+ slist_free_strings(to_list);
+ g_slist_free(to_list);
+ g_free(from);
+ g_free(server);
+ fclose(fp);
+
+ return val;
+}
+#endif
+
+gint send_message_local(const gchar *command, FILE *fp)
+{
+ FILE *pipefp;
+ gchar buf[BUFFSIZE];
+ int r;
+ sigset_t osig, mask;
+
+ g_return_val_if_fail(command != NULL, -1);
+ g_return_val_if_fail(fp != NULL, -1);
+
+ pipefp = popen(command, "w");
+ if (!pipefp) {
+ g_warning("Can't execute external command: %s\n", command);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ strretchomp(buf);
+ fputs(buf, pipefp);
+ fputc('\n', pipefp);
+ }
+
+ /* we need to block SIGCHLD, otherwise pspell's handler will wait()
+ * the pipecommand away and pclose will return -1 because of its
+ * failed wait4().
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &mask, &osig);
+
+ r = pclose(pipefp);
+
+ sigprocmask(SIG_SETMASK, &osig, NULL);
+ if (r != 0) {
+ g_warning("external command `%s' failed with code `%i'\n", command, r);
+ return -1;
+ }
+
+ return 0;
+}
+
+gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp)
+{
+ Session *session;
+ SMTPSession *smtp_session;
+ gushort port;
+ SendProgressDialog *dialog;
+ GtkCList *clist;
+ const gchar *text[3];
+ gchar buf[BUFFSIZE];
+ gint ret = 0;
+
+ g_return_val_if_fail(ac_prefs != NULL, -1);
+ g_return_val_if_fail(ac_prefs->address != NULL, -1);
+ g_return_val_if_fail(ac_prefs->smtp_server != NULL, -1);
+ g_return_val_if_fail(to_list != NULL, -1);
+ g_return_val_if_fail(fp != NULL, -1);
+
+ session = smtp_session_new();
+ smtp_session = SMTP_SESSION(session);
+
+ smtp_session->hostname =
+ ac_prefs->set_domain ? g_strdup(ac_prefs->domain) : NULL;
+
+ if (ac_prefs->use_smtp_auth) {
+ if (ac_prefs->smtp_userid) {
+ smtp_session->user = g_strdup(ac_prefs->smtp_userid);
+ if (ac_prefs->smtp_passwd)
+ smtp_session->pass =
+ g_strdup(ac_prefs->smtp_passwd);
+ else if (ac_prefs->tmp_smtp_pass)
+ smtp_session->pass =
+ g_strdup(ac_prefs->tmp_smtp_pass);
+ else {
+ smtp_session->pass =
+ input_dialog_query_password
+ (ac_prefs->smtp_server,
+ smtp_session->user);
+ if (!smtp_session->pass)
+ smtp_session->pass = g_strdup("");
+ ac_prefs->tmp_smtp_pass =
+ g_strdup(smtp_session->pass);
+ }
+ } else {
+ smtp_session->user = g_strdup(ac_prefs->userid);
+ if (ac_prefs->passwd)
+ smtp_session->pass = g_strdup(ac_prefs->passwd);
+ else if (ac_prefs->tmp_pass)
+ smtp_session->pass =
+ g_strdup(ac_prefs->tmp_pass);
+ else {
+ smtp_session->pass =
+ input_dialog_query_password
+ (ac_prefs->smtp_server,
+ smtp_session->user);
+ if (!smtp_session->pass)
+ smtp_session->pass = g_strdup("");
+ ac_prefs->tmp_pass =
+ g_strdup(smtp_session->pass);
+ }
+ }
+ } else {
+ smtp_session->user = NULL;
+ smtp_session->pass = NULL;
+ }
+
+ smtp_session->from = g_strdup(ac_prefs->address);
+ smtp_session->to_list = to_list;
+ smtp_session->cur_to = to_list;
+ smtp_session->send_data = get_outgoing_rfc2822_str(fp);
+ smtp_session->send_data_len = strlen(smtp_session->send_data);
+
+#if USE_SSL
+ port = ac_prefs->set_smtpport ? ac_prefs->smtpport :
+ ac_prefs->ssl_smtp == SSL_TUNNEL ? SSMTP_PORT : SMTP_PORT;
+ session->ssl_type = ac_prefs->ssl_smtp;
+#else
+ port = ac_prefs->set_smtpport ? ac_prefs->smtpport : SMTP_PORT;
+#endif
+
+ dialog = send_progress_dialog_create();
+ dialog->session = session;
+
+ text[0] = NULL;
+ text[1] = ac_prefs->smtp_server;
+ text[2] = _("Connecting");
+ clist = GTK_CLIST(dialog->dialog->clist);
+ gtk_clist_append(clist, (gchar **)text);
+
+ if (ac_prefs->pop_before_smtp
+ && (ac_prefs->protocol == A_APOP || ac_prefs->protocol == A_POP3)
+ && (time(NULL) - ac_prefs->last_pop_login_time) > (60 * ac_prefs->pop_before_smtp_timeout)) {
+ g_snprintf(buf, sizeof(buf), _("Doing POP before SMTP..."));
+ log_message(buf);
+ progress_dialog_set_label(dialog->dialog, buf);
+ gtk_clist_set_text(clist, 0, 2, _("POP before SMTP"));
+ GTK_EVENTS_FLUSH();
+ inc_pop_before_smtp(ac_prefs);
+ }
+
+ g_snprintf(buf, sizeof(buf), _("Connecting to SMTP server: %s ..."),
+ ac_prefs->smtp_server);
+ progress_dialog_set_label(dialog->dialog, buf);
+ log_message("%s\n", buf);
+
+ session_set_recv_message_notify(session, send_recv_message, dialog);
+ session_set_send_data_progressive_notify
+ (session, send_send_data_progressive, dialog);
+ session_set_send_data_notify(session, send_send_data_finished, dialog);
+
+ if (session_connect(session, ac_prefs->smtp_server, port) < 0) {
+ session_destroy(session);
+ send_progress_dialog_destroy(dialog);
+ return -1;
+ }
+
+ g_print("parent: begin event loop\n");
+
+ while (session->state != SESSION_DISCONNECTED &&
+ session->state != SESSION_ERROR)
+ gtk_main_iteration();
+
+ if (session->state == SESSION_ERROR ||
+ SMTP_SESSION(session)->state == SMTP_ERROR)
+ ret = -1;
+ else if (dialog->cancelled == TRUE)
+ ret = -1;
+
+ session_destroy(session);
+ send_progress_dialog_destroy(dialog);
+
+ statusbar_verbosity_set(FALSE);
+ return ret;
+}
+
+static gint send_recv_message(Session *session, const gchar *msg, gpointer data)
+{
+ SMTPSession *smtp_session = SMTP_SESSION(session);
+ SendProgressDialog *dialog = (SendProgressDialog *)data;
+ gchar buf[BUFFSIZE];
+ gchar *state_str = NULL;
+
+ switch (smtp_session->state) {
+ case SMTP_READY:
+ case SMTP_CONNECTED:
+ return 0;
+ case SMTP_HELO:
+ g_snprintf(buf, sizeof(buf), _("Sending HELO..."));
+ state_str = _("Authenticating");
+ break;
+ case SMTP_EHLO:
+ g_snprintf(buf, sizeof(buf), _("Sending EHLO..."));
+ state_str = _("Authenticating");
+ break;
+ case SMTP_AUTH:
+ g_snprintf(buf, sizeof(buf), _("Authenticating..."));
+ state_str = _("Authenticating");
+ break;
+ case SMTP_FROM:
+ g_snprintf(buf, sizeof(buf), _("Sending MAIL FROM..."));
+ state_str = _("Sending");
+ break;
+ case SMTP_RCPT:
+ g_snprintf(buf, sizeof(buf), _("Sending RCPT TO..."));
+ state_str = _("Sending");
+ break;
+ case SMTP_DATA:
+ case SMTP_EOM:
+ g_snprintf(buf, sizeof(buf), _("Sending DATA..."));
+ state_str = _("Sending");
+ break;
+ case SMTP_QUIT:
+ g_snprintf(buf, sizeof(buf), _("Quitting..."));
+ state_str = _("Quitting");
+ break;
+ case SMTP_ERROR:
+ g_warning("send: error: %s\n", msg);
+ return 0;
+ default:
+ return 0;
+ }
+
+ progress_dialog_set_label(dialog->dialog, buf);
+ gtk_clist_set_text(GTK_CLIST(dialog->dialog->clist), 0, 2, state_str);
+
+ return 0;
+}
+
+static gint send_send_data_progressive(Session *session, guint cur_len,
+ guint total_len, gpointer data)
+{
+ SendProgressDialog *dialog = (SendProgressDialog *)data;
+ gchar buf[BUFFSIZE];
+
+ g_snprintf(buf, sizeof(buf), _("Sending message (%d / %d bytes)"),
+ cur_len, total_len);
+ progress_dialog_set_label(dialog->dialog, buf);
+ progress_dialog_set_percentage
+ (dialog->dialog, (gfloat)cur_len / (gfloat)total_len);
+
+ return 0;
+}
+
+static gint send_send_data_finished(Session *session, guint len, gpointer data)
+{
+ SendProgressDialog *dialog = (SendProgressDialog *)data;
+ gchar buf[BUFFSIZE];
+
+ g_snprintf(buf, sizeof(buf), _("Sending message (%d / %d bytes)"),
+ len, len);
+ progress_dialog_set_label(dialog->dialog, buf);
+ progress_dialog_set_percentage(dialog->dialog, 1.0);
+
+ return 0;
+}
+
+static SendProgressDialog *send_progress_dialog_create(void)
+{
+ SendProgressDialog *dialog;
+ ProgressDialog *progress;
+
+ dialog = g_new0(SendProgressDialog, 1);
+
+ progress = progress_dialog_create();
+ gtk_window_set_title(GTK_WINDOW(progress->window),
+ _("Sending message"));
+ gtk_signal_connect(GTK_OBJECT(progress->cancel_btn), "clicked",
+ GTK_SIGNAL_FUNC(send_cancel_button_cb), dialog);
+ gtk_signal_connect(GTK_OBJECT(progress->window), "delete_event",
+ GTK_SIGNAL_FUNC(gtk_true), NULL);
+ gtk_window_set_modal(GTK_WINDOW(progress->window), TRUE);
+ manage_window_set_transient(GTK_WINDOW(progress->window));
+
+ progress_dialog_set_value(progress, 0.0);
+
+ if (prefs_common.send_dialog_mode == SEND_DIALOG_ALWAYS) {
+ gtk_widget_show_now(progress->window);
+ }
+
+ dialog->dialog = progress;
+
+ return dialog;
+}
+
+static void send_progress_dialog_destroy(SendProgressDialog *dialog)
+{
+ g_return_if_fail(dialog != NULL);
+ if (prefs_common.send_dialog_mode == SEND_DIALOG_ALWAYS) {
+ progress_dialog_destroy(dialog->dialog);
+ }
+ g_free(dialog);
+}
+
+static void send_cancel_button_cb(GtkWidget *widget, gpointer data)
+{
+ SendProgressDialog *dialog = (SendProgressDialog *)data;
+ Session *session = dialog->session;
+
+ session->state = SESSION_DISCONNECTED;
+ dialog->cancelled = TRUE;
+}
--- /dev/null
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef __SEND_MESSAGE_H__
+#define __SEND_MESSAGE_H__
+
+#include <glib.h>
+
+#include "prefs_account.h"
+
+#define SMTP_PORT 25
+#if USE_OPENSSL
+#define SSMTP_PORT 465
+#endif
+
+gint send_message (const gchar *file,
+ PrefsAccount *ac_prefs,
+ GSList *to_list);
+#if 0
+gint send_message_queue (const gchar *file);
+#endif
+gint send_message_local (const gchar *command,
+ FILE *fp);
+gint send_message_smtp (PrefsAccount *ac_prefs,
+ GSList *to_list,
+ FILE *fp);
+
+#endif /* __SEND_H__ */