Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
John McEleney
/
mailutils
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Commit
7ee2b81a
...
7ee2b81ae5a0c39961542b27037374e538daa231
authored
2007-07-17 20:19:05 +0000
by
Wojciech Polak
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Moved from ../../mailbox
1 parent
2085f447
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1633 additions
and
0 deletions
libproto/mailer/sendmail.c
libproto/mailer/smtp.c
libproto/mailer/sendmail.c
0 → 100644
View file @
7ee2b81
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2004, 2005,
2006, 2007 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef ENABLE_SENDMAIL
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <mailutils/address.h>
#include <mailutils/debug.h>
#include <mailutils/message.h>
#include <mailutils/observer.h>
#include <mailutils/property.h>
#include <mailutils/stream.h>
#include <mailutils/url.h>
#include <mailutils/header.h>
#include <mailutils/body.h>
#include <mailutils/errno.h>
#include <mailer0.h>
#include <registrar0.h>
static
struct
_mu_record
_sendmail_record
=
{
MU_SENDMAIL_PRIO
,
MU_SENDMAIL_SCHEME
,
_url_sendmail_init
,
/* url init. */
NULL
,
/* Mailbox entry. */
_mailer_sendmail_init
,
/* Mailer entry. */
NULL
,
/* Folder entry. */
NULL
,
/* No need for a back pointer. */
NULL
,
/* _is_scheme method. */
NULL
,
/* _get_url method. */
NULL
,
/* _get_mailbox method. */
NULL
,
/* _get_mailer method. */
NULL
/* _get_folder method. */
};
/* We export, url parsing and the initialisation of
the mailbox, via the register entry/record. */
mu_record_t
mu_sendmail_record
=
&
_sendmail_record
;
struct
_sendmail
{
int
dsn
;
char
*
path
;
pid_t
pid
;
off_t
offset
;
int
fd
;
enum
sendmail_state
{
SENDMAIL_CLOSED
,
SENDMAIL_OPEN
,
SENDMAIL_SEND
}
state
;
};
typedef
struct
_sendmail
*
sendmail_t
;
static
void
sendmail_destroy
(
mu_mailer_t
);
static
int
sendmail_open
(
mu_mailer_t
,
int
);
static
int
sendmail_close
(
mu_mailer_t
);
static
int
sendmail_send_message
(
mu_mailer_t
,
mu_message_t
,
mu_address_t
,
mu_address_t
);
int
_mailer_sendmail_init
(
mu_mailer_t
mailer
)
{
sendmail_t
sendmail
;
/* Allocate memory specific to sendmail mailer. */
sendmail
=
mailer
->
data
=
calloc
(
1
,
sizeof
(
*
sendmail
));
if
(
mailer
->
data
==
NULL
)
return
ENOMEM
;
sendmail
->
state
=
SENDMAIL_CLOSED
;
mailer
->
_destroy
=
sendmail_destroy
;
mailer
->
_open
=
sendmail_open
;
mailer
->
_close
=
sendmail_close
;
mailer
->
_send_message
=
sendmail_send_message
;
/* Set our properties. */
{
mu_property_t
property
=
NULL
;
mu_mailer_get_property
(
mailer
,
&
property
);
mu_property_set_value
(
property
,
"TYPE"
,
"SENDMAIL"
,
1
);
}
return
0
;
}
static
void
sendmail_destroy
(
mu_mailer_t
mailer
)
{
sendmail_t
sendmail
=
mailer
->
data
;
if
(
sendmail
)
{
if
(
sendmail
->
path
)
free
(
sendmail
->
path
);
free
(
sendmail
);
mailer
->
data
=
NULL
;
}
}
static
int
sendmail_open
(
mu_mailer_t
mailer
,
int
flags
)
{
sendmail_t
sendmail
=
mailer
->
data
;
int
status
;
char
*
path
;
/* Sanity checks. */
if
(
sendmail
==
NULL
)
return
EINVAL
;
mailer
->
flags
=
flags
;
if
((
status
=
mu_url_aget_path
(
mailer
->
url
,
&
path
)))
return
status
;
if
(
access
(
path
,
X_OK
)
==
-
1
)
{
free
(
path
);
return
errno
;
}
sendmail
->
path
=
path
;
sendmail
->
state
=
SENDMAIL_OPEN
;
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
"sendmail (%s)
\n
"
,
sendmail
->
path
);
return
0
;
}
static
int
sendmail_close
(
mu_mailer_t
mailer
)
{
sendmail_t
sendmail
=
mailer
->
data
;
/* Sanity checks. */
if
(
sendmail
==
NULL
)
return
EINVAL
;
if
(
sendmail
->
path
)
free
(
sendmail
->
path
);
sendmail
->
path
=
NULL
;
sendmail
->
state
=
SENDMAIL_CLOSED
;
return
0
;
}
static
int
mailer_property_is_set
(
mu_mailer_t
mailer
,
const
char
*
name
)
{
mu_property_t
property
=
NULL
;
mu_mailer_get_property
(
mailer
,
&
property
);
return
mu_property_is_set
(
property
,
name
);
}
/* Close FD unless it is part of pipe P */
#define SCLOSE(fd,p) if (p[0]!=fd&&p[1]!=fd) close(fd)
static
int
sendmail_send_message
(
mu_mailer_t
mailer
,
mu_message_t
msg
,
mu_address_t
from
,
mu_address_t
to
)
{
sendmail_t
sendmail
=
mailer
->
data
;
int
status
=
0
;
if
(
sendmail
==
NULL
||
msg
==
NULL
)
return
EINVAL
;
switch
(
sendmail
->
state
)
{
case
SENDMAIL_CLOSED
:
return
EINVAL
;
case
SENDMAIL_OPEN
:
{
int
tunnel
[
2
];
int
argc
=
0
;
const
char
**
argvec
=
NULL
;
size_t
tocount
=
0
;
const
char
*
emailfrom
=
NULL
;
/* Count the length of the arg vec: */
argc
++
;
/* terminating NULL */
argc
++
;
/* sendmail */
argc
++
;
/* -oi (do not treat '.' as message
terminator) */
if
(
from
)
{
if
((
status
=
mu_address_sget_email
(
from
,
1
,
&
emailfrom
))
!=
0
)
goto
OPEN_STATE_CLEANUP
;
if
(
!
emailfrom
)
{
/* the address wasn't fully qualified, choke (for now) */
status
=
EINVAL
;
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
"envelope from (%s) not fully qualifed
\n
"
,
emailfrom
);
goto
OPEN_STATE_CLEANUP
;
}
argc
+=
2
;
/* -f from */
}
if
(
to
)
{
status
=
mu_address_get_email_count
(
to
,
&
tocount
);
assert
(
!
status
);
assert
(
tocount
);
argc
+=
tocount
;
/* 1 per to address */
}
argc
++
;
/* -t */
/* Allocate arg vec: */
if
((
argvec
=
calloc
(
argc
,
sizeof
(
*
argvec
)))
==
0
)
{
status
=
ENOMEM
;
goto
OPEN_STATE_CLEANUP
;
}
argc
=
0
;
argvec
[
argc
++
]
=
sendmail
->
path
;
argvec
[
argc
++
]
=
"-oi"
;
if
(
from
)
{
argvec
[
argc
++
]
=
"-f"
;
argvec
[
argc
++
]
=
emailfrom
;
}
if
(
!
to
||
mailer_property_is_set
(
mailer
,
"READ_RECIPIENTS"
))
{
argvec
[
argc
++
]
=
"-t"
;
}
else
{
int
i
=
1
;
size_t
count
=
0
;
mu_address_get_count
(
to
,
&
count
);
for
(;
i
<=
count
;
i
++
)
{
const
char
*
email
;
if
((
status
=
mu_address_sget_email
(
to
,
i
,
&
email
))
!=
0
)
goto
OPEN_STATE_CLEANUP
;
if
(
!
email
)
{
/* the address wasn't fully qualified, choke (for now) */
status
=
EINVAL
;
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
"envelope to (%s) not fully qualifed
\n
"
,
email
);
goto
OPEN_STATE_CLEANUP
;
}
argvec
[
argc
++
]
=
email
;
}
}
assert
(
argvec
[
argc
]
==
NULL
);
if
(
pipe
(
tunnel
)
==
0
)
{
sendmail
->
fd
=
tunnel
[
1
];
sendmail
->
pid
=
vfork
();
if
(
sendmail
->
pid
==
0
)
/* Child. */
{
SCLOSE
(
STDIN_FILENO
,
tunnel
);
SCLOSE
(
STDOUT_FILENO
,
tunnel
);
SCLOSE
(
STDERR_FILENO
,
tunnel
);
close
(
tunnel
[
1
]);
dup2
(
tunnel
[
0
],
STDIN_FILENO
);
execv
(
sendmail
->
path
,
(
char
**
)
argvec
);
exit
(
errno
);
}
else
if
(
sendmail
->
pid
==
-
1
)
{
status
=
errno
;
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
"vfork() failed: %s
\n
"
,
strerror
(
status
));
}
}
else
{
status
=
errno
;
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
"pipe() failed: %s
\n
"
,
strerror
(
status
));
}
OPEN_STATE_CLEANUP:
MAILER_DEBUG0
(
mailer
,
MU_DEBUG_TRACE
,
"exec argv:"
);
for
(
argc
=
0
;
argvec
&&
argvec
[
argc
];
argc
++
)
{
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
" %s"
,
argvec
[
argc
]);
}
MAILER_DEBUG0
(
mailer
,
MU_DEBUG_TRACE
,
"
\n
"
);
free
(
argvec
);
close
(
tunnel
[
0
]);
if
(
status
!=
0
)
{
close
(
sendmail
->
fd
);
break
;
}
sendmail
->
state
=
SENDMAIL_SEND
;
}
case
SENDMAIL_SEND
:
{
mu_stream_t
stream
=
NULL
;
char
buffer
[
512
];
size_t
len
=
0
;
int
rc
;
size_t
offset
=
0
;
mu_header_t
hdr
;
mu_body_t
body
;
int
found_nl
=
0
;
int
exit_status
;
mu_message_get_header
(
msg
,
&
hdr
);
mu_header_get_stream
(
hdr
,
&
stream
);
MAILER_DEBUG0
(
mailer
,
MU_DEBUG_TRACE
,
"Sending headers...
\n
"
);
while
((
status
=
mu_stream_readline
(
stream
,
buffer
,
sizeof
(
buffer
),
offset
,
&
len
))
==
0
&&
len
!=
0
)
{
if
(
strncasecmp
(
buffer
,
MU_HEADER_FCC
,
sizeof
(
MU_HEADER_FCC
)
-
1
))
{
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_PROT
,
"Header: %s"
,
buffer
);
if
(
write
(
sendmail
->
fd
,
buffer
,
len
)
==
-
1
)
{
status
=
errno
;
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
"write() failed: %s
\n
"
,
strerror
(
status
));
break
;
}
}
found_nl
=
(
len
==
1
&&
buffer
[
0
]
==
'\n'
);
offset
+=
len
;
sendmail
->
offset
+=
len
;
}
if
(
!
found_nl
)
{
if
(
write
(
sendmail
->
fd
,
"
\n
"
,
1
)
==
-
1
)
{
status
=
errno
;
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
"write() failed: %s
\n
"
,
strerror
(
status
));
}
}
mu_message_get_body
(
msg
,
&
body
);
mu_body_get_stream
(
body
,
&
stream
);
MAILER_DEBUG0
(
mailer
,
MU_DEBUG_TRACE
,
"Sending body...
\n
"
);
offset
=
0
;
while
((
status
=
mu_stream_read
(
stream
,
buffer
,
sizeof
(
buffer
),
offset
,
&
len
))
==
0
&&
len
!=
0
)
{
if
(
write
(
sendmail
->
fd
,
buffer
,
len
)
==
-
1
)
{
status
=
errno
;
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_TRACE
,
"write() failed: %s
\n
"
,
strerror
(
status
));
break
;
}
offset
+=
len
;
sendmail
->
offset
+=
len
;
}
if
(
status
==
EAGAIN
)
return
status
;
close
(
sendmail
->
fd
);
rc
=
waitpid
(
sendmail
->
pid
,
&
exit_status
,
0
);
if
(
rc
<
0
)
{
if
(
errno
==
ECHILD
)
status
=
0
;
else
{
status
=
errno
;
MAILER_DEBUG2
(
mailer
,
MU_DEBUG_TRACE
,
"waitpid(%d) failed: %s
\n
"
,
sendmail
->
pid
,
strerror
(
status
));
}
}
else
if
(
WIFEXITED
(
exit_status
))
{
exit_status
=
WEXITSTATUS
(
exit_status
);
MAILER_DEBUG2
(
mailer
,
MU_DEBUG_TRACE
,
"%s exited with: %d
\n
"
,
sendmail
->
path
,
exit_status
);
status
=
(
exit_status
==
0
)
?
0
:
MU_ERR_PROCESS_EXITED
;
}
else
if
(
WIFSIGNALED
(
exit_status
))
status
=
MU_ERR_PROCESS_SIGNALED
;
else
status
=
MU_ERR_PROCESS_UNKNOWN_FAILURE
;
/* Shouldn't this notification only happen on success? */
mu_observable_notify
(
mailer
->
observable
,
MU_EVT_MAILER_MESSAGE_SENT
);
}
default:
break
;
}
sendmail
->
state
=
(
status
==
0
)
?
SENDMAIL_OPEN
:
SENDMAIL_CLOSED
;
return
status
;
}
#else
#include <stdio.h>
#include <registrar0.h>
mu_record_t
mu_sendmail_record
=
NULL
;
#endif
libproto/mailer/smtp.c
0 → 100644
View file @
7ee2b81
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2004, 2005,
2006, 2007 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA */
/** @file smtp.c
@brief an SMTP mailer
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef ENABLE_SMTP
#include <errno.h>
#include <ctype.h>
#include <netdb.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mailutils/address.h>
#include <mailutils/debug.h>
#include <mailutils/errno.h>
#include <mailutils/header.h>
#include <mailutils/body.h>
#include <mailutils/message.h>
#include <mailutils/mutil.h>
#include <mailutils/observer.h>
#include <mailutils/property.h>
#include <mailutils/stream.h>
#include <mailutils/url.h>
#include <mailutils/tls.h>
#include <mailer0.h>
#include <registrar0.h>
static
struct
_mu_record
_smtp_record
=
{
MU_SMTP_PRIO
,
MU_SMTP_SCHEME
,
_url_smtp_init
,
/* url init. */
NULL
,
/* Mailbox init. */
&
_mailer_smtp_init
,
/* Mailer init. */
NULL
,
/* Folder init. */
NULL
,
/* No need for a back pointer. */
NULL
,
/* _is_scheme method. */
NULL
,
/* _get_url method. */
NULL
,
/* _get_mailbox method. */
NULL
,
/* _get_mailer method. */
NULL
/* _get_folder method. */
};
/* We export : url parsing and the initialisation of
the mailbox, via the register entry/record. */
mu_record_t
mu_smtp_record
=
&
_smtp_record
;
struct
_smtp
{
mu_mailer_t
mailer
;
char
*
mailhost
;
char
*
localhost
;
/* IO buffering. */
char
*
buffer
;
/* Must be freed. */
size_t
buflen
;
char
*
ptr
;
char
*
nl
;
off_t
s_offset
;
enum
smtp_state
{
SMTP_NO_STATE
,
SMTP_OPEN
,
SMTP_GREETINGS
,
SMTP_EHLO
,
SMTP_EHLO_ACK
,
SMTP_HELO
,
SMTP_HELO_ACK
,
SMTP_QUIT
,
SMTP_QUIT_ACK
,
SMTP_ENV_FROM
,
SMTP_ENV_RCPT
,
SMTP_MAIL_FROM
,
SMTP_MAIL_FROM_ACK
,
SMTP_RCPT_TO
,
SMTP_RCPT_TO_ACK
,
SMTP_DATA
,
SMTP_DATA_ACK
,
SMTP_SEND
,
SMTP_SEND_ACK
,
SMTP_SEND_DOT
,
SMTP_STARTTLS
,
SMTP_STARTTLS_ACK
}
state
;
int
extended
;
unsigned
long
capa
;
/* Server capabilities */
const
char
*
mail_from
;
mu_address_t
rcpt_to
;
/* Destroy this if not the same as argto below. */
mu_address_t
rcpt_bcc
;
size_t
rcpt_to_count
;
size_t
rcpt_bcc_count
;
size_t
rcpt_index
;
size_t
rcpt_count
;
int
bccing
;
mu_message_t
msg
;
/* Destroy this if not same argmsg. */
off_t
offset
;
/* The mu_mailer_send_message() args. */
mu_message_t
argmsg
;
mu_address_t
argfrom
;
mu_address_t
argto
;
};
typedef
struct
_smtp
*
smtp_t
;
/* ESMTP capabilities */
#define CAPA_STARTTLS 0x00000001
#define CAPA_8BITMIME 0x00000002
static
void
smtp_destroy
(
mu_mailer_t
);
static
int
smtp_open
(
mu_mailer_t
,
int
);
static
int
smtp_close
(
mu_mailer_t
);
static
int
smtp_send_message
(
mu_mailer_t
,
mu_message_t
,
mu_address_t
,
mu_address_t
);
static
int
smtp_writeline
(
smtp_t
smtp
,
const
char
*
format
,
...);
static
int
smtp_readline
(
smtp_t
);
static
int
smtp_read_ack
(
smtp_t
);
static
int
smtp_parse_ehlo_ack
(
smtp_t
);
static
int
smtp_write
(
smtp_t
);
static
int
smtp_starttls
(
smtp_t
);
static
int
_smtp_set_rcpt
(
smtp_t
,
mu_message_t
,
mu_address_t
);
/* Useful little macros, since these are very repetitive. */
static
void
CLEAR_STATE
(
smtp_t
smtp
)
{
smtp
->
ptr
=
NULL
;
smtp
->
nl
=
NULL
;
smtp
->
s_offset
=
0
;
smtp
->
state
=
SMTP_NO_STATE
;
smtp
->
extended
=
0
;
if
(
smtp
->
mail_from
)
smtp
->
mail_from
=
NULL
;
if
(
smtp
->
rcpt_to
!=
smtp
->
argto
)
mu_address_destroy
(
&
smtp
->
rcpt_to
);
smtp
->
rcpt_to
=
NULL
;
mu_address_destroy
(
&
smtp
->
rcpt_bcc
);
smtp
->
rcpt_to_count
=
0
;
smtp
->
rcpt_bcc_count
=
0
;
smtp
->
rcpt_index
=
0
;
smtp
->
rcpt_count
=
0
;
smtp
->
bccing
=
0
;
if
(
smtp
->
msg
!=
smtp
->
argmsg
)
mu_message_destroy
(
&
smtp
->
msg
,
NULL
);
smtp
->
msg
=
NULL
;
smtp
->
offset
=
0
;
smtp
->
argmsg
=
NULL
;
smtp
->
argfrom
=
NULL
;
smtp
->
argto
=
NULL
;
}
/* If we are resuming, we should be resuming the SAME operation
as that which is ongoing. Check this. */
static
int
smtp_check_send_resumption
(
smtp_t
smtp
,
mu_message_t
msg
,
mu_address_t
from
,
mu_address_t
to
)
{
if
(
smtp
->
state
==
SMTP_NO_STATE
)
return
0
;
/* FIXME: state should be one of the "send" states if its not
"no state" */
if
(
msg
!=
smtp
->
argmsg
)
return
MU_ERR_BAD_RESUMPTION
;
if
(
from
!=
smtp
->
argfrom
)
return
MU_ERR_BAD_RESUMPTION
;
if
(
to
!=
smtp
->
argto
)
return
MU_ERR_BAD_RESUMPTION
;
return
0
;
}
#define CHECK_SEND_RESUME(smtp, msg, from, to) \
do { \
if((status = smtp_check_send_resumption(smtp, msg, from, to)) != 0) \
return status; \
} while (0)
/* Clear the state and close the stream. */
#define CHECK_ERROR_CLOSE(mailer, smtp, status) \
do \
{ \
if (status != 0) \
{ \
mu_stream_close (mailer->stream); \
CLEAR_STATE (smtp); \
return status; \
} \
} \
while (0)
/* Clear the state. */
#define CHECK_ERROR(smtp, status) \
do \
{ \
if (status != 0) \
{ \
CLEAR_STATE (smtp); \
return status; \
} \
} \
while (0)
/* Clear the state for non recoverable error. */
#define CHECK_EAGAIN(smtp, status) \
do \
{ \
if (status != 0) \
{ \
if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \
{ \
CLEAR_STATE (smtp); \
} \
return status; \
} \
} \
while (0)
int
_mailer_smtp_init
(
mu_mailer_t
mailer
)
{
smtp_t
smtp
;
/* Allocate memory specific to smtp mailer. */
smtp
=
mailer
->
data
=
calloc
(
1
,
sizeof
(
*
smtp
));
if
(
mailer
->
data
==
NULL
)
return
ENOMEM
;
smtp
->
mailer
=
mailer
;
/* Back pointer. */
smtp
->
state
=
SMTP_NO_STATE
;
mailer
->
_destroy
=
smtp_destroy
;
mailer
->
_open
=
smtp_open
;
mailer
->
_close
=
smtp_close
;
mailer
->
_send_message
=
smtp_send_message
;
/* Set our properties. */
{
mu_property_t
property
=
NULL
;
mu_mailer_get_property
(
mailer
,
&
property
);
mu_property_set_value
(
property
,
"TYPE"
,
"SMTP"
,
1
);
}
return
0
;
}
static
void
smtp_destroy
(
mu_mailer_t
mailer
)
{
smtp_t
smtp
=
mailer
->
data
;
CLEAR_STATE
(
smtp
);
/* Not our responsability to close. */
if
(
smtp
->
mailhost
)
free
(
smtp
->
mailhost
);
if
(
smtp
->
localhost
)
free
(
smtp
->
localhost
);
if
(
smtp
->
buffer
)
free
(
smtp
->
buffer
);
free
(
smtp
);
mailer
->
data
=
NULL
;
}
/** Open an SMTP mailer.
An SMTP mailer must be opened before any messages can be sent.
@param mailer the mailer created by smtp_create()
@param flags the mailer flags
*/
static
int
smtp_open
(
mu_mailer_t
mailer
,
int
flags
)
{
smtp_t
smtp
=
mailer
->
data
;
int
status
;
long
port
;
/* Sanity checks. */
if
(
!
smtp
)
return
EINVAL
;
mailer
->
flags
=
flags
;
if
((
status
=
mu_url_get_port
(
mailer
->
url
,
&
port
))
!=
0
)
return
status
;
switch
(
smtp
->
state
)
{
case
SMTP_NO_STATE
:
if
(
smtp
->
mailhost
)
{
free
(
smtp
->
mailhost
);
smtp
->
mailhost
=
NULL
;
}
/* Fetch the mailer server name and the port in the mu_url_t. */
if
((
status
=
mu_url_aget_host
(
mailer
->
url
,
&
smtp
->
mailhost
))
!=
0
)
return
status
;
if
(
smtp
->
localhost
)
{
free
(
smtp
->
localhost
);
smtp
->
localhost
=
NULL
;
}
/* Fetch our local host name. */
status
=
mu_get_host_name
(
&
smtp
->
localhost
);
if
(
status
!=
0
)
{
/* gethostname failed, abort. */
free
(
smtp
->
mailhost
);
smtp
->
mailhost
=
NULL
;
return
status
;
}
/* allocate a working io buffer. */
if
(
smtp
->
buffer
==
NULL
)
{
smtp
->
buflen
=
512
;
/* Initial guess. */
smtp
->
buffer
=
malloc
(
smtp
->
buflen
+
1
);
if
(
smtp
->
buffer
==
NULL
)
{
CHECK_ERROR
(
smtp
,
ENOMEM
);
}
smtp
->
ptr
=
smtp
->
buffer
;
}
/* Create a TCP stack if one is not given. */
if
(
mailer
->
stream
==
NULL
)
{
status
=
mu_tcp_stream_create
(
&
mailer
->
stream
,
smtp
->
mailhost
,
port
,
mailer
->
flags
);
CHECK_ERROR
(
smtp
,
status
);
mu_stream_setbufsiz
(
mailer
->
stream
,
BUFSIZ
);
}
CHECK_ERROR
(
smtp
,
status
);
smtp
->
state
=
SMTP_OPEN
;
case
SMTP_OPEN
:
MAILER_DEBUG2
(
mailer
,
MU_DEBUG_PROT
,
"smtp_open (host: %s port: %ld)
\n
"
,
smtp
->
mailhost
,
port
);
status
=
mu_stream_open
(
mailer
->
stream
);
CHECK_EAGAIN
(
smtp
,
status
);
smtp
->
state
=
SMTP_GREETINGS
;
case
SMTP_GREETINGS
:
/* Swallow the greetings. */
status
=
smtp_read_ack
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
if
(
smtp
->
buffer
[
0
]
!=
'2'
)
{
mu_stream_close
(
mailer
->
stream
);
return
EACCES
;
}
status
=
smtp_writeline
(
smtp
,
"EHLO %s
\r\n
"
,
smtp
->
localhost
);
CHECK_ERROR
(
smtp
,
status
);
smtp
->
state
=
SMTP_EHLO
;
case
SMTP_EHLO
:
/* We first try Extended SMTP. */
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
smtp
->
state
=
SMTP_EHLO_ACK
;
case
SMTP_EHLO_ACK
:
status
=
smtp_parse_ehlo_ack
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
if
(
smtp
->
buffer
[
0
]
!=
'2'
)
{
smtp
->
extended
=
0
;
status
=
smtp_writeline
(
smtp
,
"HELO %s
\r\n
"
,
smtp
->
localhost
);
CHECK_ERROR
(
smtp
,
status
);
smtp
->
state
=
SMTP_HELO
;
}
else
{
smtp
->
extended
=
1
;
if
(
smtp
->
capa
&
CAPA_STARTTLS
)
smtp
->
state
=
SMTP_STARTTLS
;
else
break
;
}
case
SMTP_STARTTLS
:
case
SMTP_STARTTLS_ACK
:
smtp_starttls
(
smtp
);
break
;
case
SMTP_HELO
:
if
(
!
smtp
->
extended
)
/* FIXME: this will always be false! */
{
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
}
smtp
->
state
=
SMTP_HELO_ACK
;
case
SMTP_HELO_ACK
:
if
(
!
smtp
->
extended
)
{
status
=
smtp_read_ack
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
if
(
smtp
->
buffer
[
0
]
!=
'2'
)
{
mu_stream_close
(
mailer
->
stream
);
CLEAR_STATE
(
smtp
);
return
EACCES
;
}
}
default:
break
;
}
CLEAR_STATE
(
smtp
);
return
0
;
}
static
int
smtp_close
(
mu_mailer_t
mailer
)
{
smtp_t
smtp
=
mailer
->
data
;
int
status
;
switch
(
smtp
->
state
)
{
case
SMTP_NO_STATE
:
status
=
smtp_writeline
(
smtp
,
"QUIT
\r\n
"
);
CHECK_ERROR
(
smtp
,
status
);
smtp
->
state
=
SMTP_QUIT
;
case
SMTP_QUIT
:
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
smtp
->
state
=
SMTP_QUIT_ACK
;
case
SMTP_QUIT_ACK
:
status
=
smtp_read_ack
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
default:
break
;
}
return
mu_stream_close
(
mailer
->
stream
);
}
/*
Client side STARTTLS support.
*/
static
int
smtp_reader
(
void
*
iodata
)
{
int
status
=
0
;
smtp_t
iop
=
iodata
;
status
=
smtp_read_ack
(
iop
);
CHECK_EAGAIN
(
iop
,
status
);
return
status
;
}
static
int
smtp_writer
(
void
*
iodata
,
char
*
buf
)
{
smtp_t
iop
=
iodata
;
int
status
;
if
(
strncasecmp
(
buf
,
"EHLO"
,
4
)
==
0
)
status
=
smtp_writeline
(
iop
,
"%s %s
\r\n
"
,
buf
,
iop
->
localhost
);
else
status
=
smtp_writeline
(
iop
,
"%s
\r\n
"
,
buf
);
CHECK_ERROR
(
iop
,
status
);
status
=
smtp_write
(
iop
);
CHECK_EAGAIN
(
iop
,
status
);
return
status
;
}
static
void
smtp_stream_ctl
(
void
*
iodata
,
mu_stream_t
*
pold
,
mu_stream_t
new
)
{
smtp_t
iop
=
iodata
;
if
(
pold
)
*
pold
=
iop
->
mailer
->
stream
;
if
(
new
)
iop
->
mailer
->
stream
=
new
;
}
static
int
smtp_starttls
(
smtp_t
smtp
)
{
#ifdef WITH_TLS
int
status
;
mu_mailer_t
mailer
=
smtp
->
mailer
;
char
*
keywords
[]
=
{
"STARTTLS"
,
"EHLO"
,
NULL
};
if
(
!
mu_tls_enable
||
!
(
smtp
->
capa
&
CAPA_STARTTLS
))
return
-
1
;
smtp
->
capa
=
0
;
status
=
mu_tls_begin
(
smtp
,
smtp_reader
,
smtp_writer
,
smtp_stream_ctl
,
keywords
);
MAILER_DEBUG1
(
mailer
,
MU_DEBUG_PROT
,
"TLS negotiation %s
\n
"
,
status
==
0
?
"succeeded"
:
"failed"
);
return
status
;
#else
return
-
1
;
#endif
/* WITH_TLS */
}
static
int
message_set_header_value
(
mu_message_t
msg
,
const
char
*
field
,
const
char
*
value
)
{
int
status
=
0
;
mu_header_t
hdr
=
NULL
;
if
((
status
=
mu_message_get_header
(
msg
,
&
hdr
)))
return
status
;
if
((
status
=
mu_header_set_value
(
hdr
,
field
,
value
,
1
)))
return
status
;
return
status
;
}
static
int
message_has_bcc
(
mu_message_t
msg
)
{
int
status
;
mu_header_t
header
=
NULL
;
size_t
bccsz
=
0
;
if
((
status
=
mu_message_get_header
(
msg
,
&
header
)))
return
status
;
status
=
mu_header_get_value
(
header
,
MU_HEADER_BCC
,
NULL
,
0
,
&
bccsz
);
/* MU_ERR_NOENT, or there was a Bcc: field. */
return
status
==
MU_ERR_NOENT
?
0
:
1
;
}
/*
The smtp mailer doesn't deal with mail like:
To: public@com, pub2@com
Bcc: hidden@there, two@there
It just sends the message to all the addresses, making the
"blind" cc not particularly blind.
The correct algorithm is
- open smtp connection
- look as msg, figure out addrto&cc, and addrbcc
- deliver to the to & cc addresses:
- if there are bcc addrs, remove the bcc field
- send the message to to & cc addrs:
mail from: me
rcpt to: public@com
rcpt to: pub2@com
data
...
- deliver to the bcc addrs:
for a in (bccaddrs)
do
- add header field to msg, bcc: $a
- send the msg:
mail from: me
rcpt to: $a
data
...
done
- quit smtp connection
*/
static
int
smtp_send_message
(
mu_mailer_t
mailer
,
mu_message_t
argmsg
,
mu_address_t
argfrom
,
mu_address_t
argto
)
{
smtp_t
smtp
=
NULL
;
int
status
;
if
(
mailer
==
NULL
)
return
EINVAL
;
smtp
=
mailer
->
data
;
if
(
!
smtp
)
return
EINVAL
;
CHECK_SEND_RESUME
(
smtp
,
argmsg
,
argfrom
,
argto
);
switch
(
smtp
->
state
)
{
case
SMTP_NO_STATE
:
if
(
argmsg
==
NULL
||
argfrom
==
NULL
)
return
EINVAL
;
smtp
->
argmsg
=
smtp
->
msg
=
argmsg
;
smtp
->
argfrom
=
argfrom
;
smtp
->
argto
=
argto
;
status
=
mu_address_sget_email
(
smtp
->
argfrom
,
1
,
&
smtp
->
mail_from
);
CHECK_ERROR
(
smtp
,
status
);
status
=
_smtp_set_rcpt
(
smtp
,
smtp
->
argmsg
,
smtp
->
argto
);
CHECK_ERROR
(
smtp
,
status
);
/* Clear the Bcc: field if we found one. */
if
(
message_has_bcc
(
smtp
->
argmsg
))
{
smtp
->
msg
=
NULL
;
status
=
mu_message_create_copy
(
&
smtp
->
msg
,
smtp
->
argmsg
);
CHECK_ERROR
(
smtp
,
status
);
status
=
message_set_header_value
(
smtp
->
msg
,
MU_HEADER_BCC
,
NULL
);
CHECK_ERROR
(
smtp
,
status
);
}
/* Begin bccing if there are not To: recipients. */
if
(
smtp
->
rcpt_to_count
==
0
)
smtp
->
bccing
=
1
;
smtp
->
rcpt_index
=
1
;
smtp
->
state
=
SMTP_ENV_FROM
;
case
SMTP_ENV_FROM
:
ENV_FROM:
{
status
=
smtp_writeline
(
smtp
,
"MAIL FROM: <%s>
\r\n
"
,
smtp
->
mail_from
);
CHECK_ERROR
(
smtp
,
status
);
smtp
->
state
=
SMTP_MAIL_FROM
;
}
/* We use a goto, since we may have multiple messages,
we come back here and doit all over again ... Not pretty. */
case
SMTP_MAIL_FROM
:
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
smtp
->
state
=
SMTP_MAIL_FROM_ACK
;
case
SMTP_MAIL_FROM_ACK
:
status
=
smtp_read_ack
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
if
(
smtp
->
buffer
[
0
]
!=
'2'
)
{
mu_stream_close
(
mailer
->
stream
);
CLEAR_STATE
(
smtp
);
return
EACCES
;
}
/* We use a goto, since we may have multiple recipients,
we come back here and do it all over again ... Not pretty. */
case
SMTP_ENV_RCPT
:
ENV_RCPT:
{
mu_address_t
addr
=
smtp
->
rcpt_to
;
const
char
*
to
=
NULL
;
if
(
smtp
->
bccing
)
addr
=
smtp
->
rcpt_bcc
;
status
=
mu_address_sget_email
(
addr
,
smtp
->
rcpt_index
,
&
to
);
CHECK_ERROR
(
smtp
,
status
);
/* Add the Bcc: field back in for recipient. */
if
(
smtp
->
bccing
)
{
status
=
message_set_header_value
(
smtp
->
msg
,
MU_HEADER_BCC
,
to
);
CHECK_ERROR
(
smtp
,
status
);
}
status
=
smtp_writeline
(
smtp
,
"RCPT TO: <%s>
\r\n
"
,
to
);
CHECK_ERROR
(
smtp
,
status
);
smtp
->
state
=
SMTP_RCPT_TO
;
smtp
->
rcpt_index
++
;
}
case
SMTP_RCPT_TO
:
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
smtp
->
state
=
SMTP_RCPT_TO_ACK
;
case
SMTP_RCPT_TO_ACK
:
status
=
smtp_read_ack
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
if
(
smtp
->
buffer
[
0
]
!=
'2'
)
{
mu_stream_close
(
mailer
->
stream
);
CLEAR_STATE
(
smtp
);
return
MU_ERR_SMTP_RCPT_FAILED
;
}
/* Redo the receipt sequence for every To: and Cc: recipient. */
if
(
!
smtp
->
bccing
&&
smtp
->
rcpt_index
<=
smtp
->
rcpt_to_count
)
goto
ENV_RCPT
;
/* We are done with the rcpt. */
status
=
smtp_writeline
(
smtp
,
"DATA
\r\n
"
);
CHECK_ERROR
(
smtp
,
status
);
smtp
->
state
=
SMTP_DATA
;
case
SMTP_DATA
:
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
smtp
->
state
=
SMTP_DATA_ACK
;
case
SMTP_DATA_ACK
:
status
=
smtp_read_ack
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
if
(
smtp
->
buffer
[
0
]
!=
'3'
)
{
mu_stream_close
(
mailer
->
stream
);
CLEAR_STATE
(
smtp
);
return
EACCES
;
}
smtp
->
offset
=
0
;
smtp
->
state
=
SMTP_SEND
;
if
((
smtp
->
mailer
->
flags
&
MAILER_FLAG_DEBUG_DATA
)
==
0
)
MAILER_DEBUG0
(
smtp
->
mailer
,
MU_DEBUG_PROT
,
"> (data...)
\n
"
);
case
SMTP_SEND
:
{
mu_stream_t
stream
;
size_t
n
=
0
;
char
data
[
256
]
=
""
;
mu_header_t
hdr
;
mu_body_t
body
;
int
found_nl
;
/* We may be here after an EAGAIN so check if we have something
in the buffer and flush it. */
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
mu_message_get_header
(
smtp
->
msg
,
&
hdr
);
mu_header_get_stream
(
hdr
,
&
stream
);
while
((
status
=
mu_stream_readline
(
stream
,
data
,
sizeof
(
data
),
smtp
->
offset
,
&
n
))
==
0
&&
n
>
0
)
{
int
nl
;
found_nl
=
(
n
==
1
&&
data
[
0
]
==
'\n'
);
if
((
nl
=
(
data
[
n
-
1
]
==
'\n'
)))
data
[
n
-
1
]
=
'\0'
;
if
(
data
[
0
]
==
'.'
)
{
status
=
smtp_writeline
(
smtp
,
".%s"
,
data
);
CHECK_ERROR
(
smtp
,
status
);
}
else
if
(
strncasecmp
(
data
,
MU_HEADER_FCC
,
sizeof
(
MU_HEADER_FCC
)
-
1
))
{
status
=
smtp_writeline
(
smtp
,
"%s"
,
data
);
CHECK_ERROR
(
smtp
,
status
);
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
}
else
nl
=
0
;
if
(
nl
)
{
status
=
smtp_writeline
(
smtp
,
"
\r\n
"
);
CHECK_ERROR
(
smtp
,
status
);
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
}
smtp
->
offset
+=
n
;
}
if
(
!
found_nl
)
{
status
=
smtp_writeline
(
smtp
,
"
\r\n
"
);
CHECK_ERROR
(
smtp
,
status
);
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
}
mu_message_get_body
(
smtp
->
msg
,
&
body
);
mu_body_get_stream
(
body
,
&
stream
);
smtp
->
offset
=
0
;
while
((
status
=
mu_stream_readline
(
stream
,
data
,
sizeof
(
data
)
-
1
,
smtp
->
offset
,
&
n
))
==
0
&&
n
>
0
)
{
if
(
data
[
n
-
1
]
==
'\n'
)
data
[
n
-
1
]
=
'\0'
;
if
(
data
[
0
]
==
'.'
)
status
=
smtp_writeline
(
smtp
,
".%s
\r\n
"
,
data
);
else
status
=
smtp_writeline
(
smtp
,
"%s
\r\n
"
,
data
);
CHECK_ERROR
(
smtp
,
status
);
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
smtp
->
offset
+=
n
;
}
smtp
->
offset
=
0
;
status
=
smtp_writeline
(
smtp
,
".
\r\n
"
);
CHECK_ERROR
(
smtp
,
status
);
smtp
->
state
=
SMTP_SEND_DOT
;
}
case
SMTP_SEND_DOT
:
status
=
smtp_write
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
smtp
->
state
=
SMTP_SEND_ACK
;
case
SMTP_SEND_ACK
:
status
=
smtp_read_ack
(
smtp
);
CHECK_EAGAIN
(
smtp
,
status
);
if
(
smtp
->
buffer
[
0
]
!=
'2'
)
{
mu_stream_close
(
mailer
->
stream
);
CLEAR_STATE
(
smtp
);
return
EACCES
;
}
/* Decide whether we need to loop again, to deliver to Bcc:
recipients. */
if
(
!
smtp
->
bccing
)
{
smtp
->
bccing
=
1
;
smtp
->
rcpt_index
=
1
;
}
if
(
smtp
->
rcpt_index
<=
smtp
->
rcpt_bcc_count
)
goto
ENV_FROM
;
mu_observable_notify
(
mailer
->
observable
,
MU_EVT_MAILER_MESSAGE_SENT
);
default:
break
;
}
CLEAR_STATE
(
smtp
);
return
0
;
}
int
smtp_address_add
(
mu_address_t
*
paddr
,
const
char
*
value
)
{
mu_address_t
addr
=
NULL
;
int
status
;
status
=
mu_address_create
(
&
addr
,
value
);
if
(
status
)
return
status
;
status
=
mu_address_union
(
paddr
,
addr
);
mu_address_destroy
(
&
addr
);
return
status
;
}
static
int
_smtp_property_is_set
(
smtp_t
smtp
,
const
char
*
name
)
{
mu_property_t
property
=
NULL
;
mu_mailer_get_property
(
smtp
->
mailer
,
&
property
);
return
mu_property_is_set
(
property
,
name
);
}
static
int
_smtp_set_rcpt
(
smtp_t
smtp
,
mu_message_t
msg
,
mu_address_t
to
)
{
int
status
=
0
;
mu_header_t
header
=
NULL
;
char
*
value
;
/* Get RCPT_TO from TO, or the message. */
if
(
to
)
{
/* Use the specified mu_address_t. */
if
((
status
=
mu_mailer_check_to
(
to
))
!=
0
)
{
MAILER_DEBUG0
(
smtp
->
mailer
,
MU_DEBUG_ERROR
,
"mu_mailer_send_message(): explicit to not valid
\n
"
);
return
status
;
}
smtp
->
rcpt_to
=
to
;
mu_address_get_count
(
smtp
->
rcpt_to
,
&
smtp
->
rcpt_to_count
);
if
(
status
)
return
status
;
}
if
(
!
to
||
_smtp_property_is_set
(
smtp
,
"READ_RECIPIENTS"
))
{
if
((
status
=
mu_message_get_header
(
msg
,
&
header
)))
return
status
;
status
=
mu_header_aget_value
(
header
,
MU_HEADER_TO
,
&
value
);
if
(
status
==
0
)
{
smtp_address_add
(
&
smtp
->
rcpt_to
,
value
);
free
(
value
);
}
else
if
(
status
!=
MU_ERR_NOENT
)
goto
end
;
status
=
mu_header_aget_value
(
header
,
MU_HEADER_CC
,
&
value
);
if
(
status
==
0
)
{
smtp_address_add
(
&
smtp
->
rcpt_to
,
value
);
free
(
value
);
}
else
if
(
status
!=
MU_ERR_NOENT
)
goto
end
;
status
=
mu_header_aget_value
(
header
,
MU_HEADER_BCC
,
&
value
);
if
(
status
==
0
)
{
smtp_address_add
(
&
smtp
->
rcpt_bcc
,
value
);
free
(
value
);
}
else
if
(
status
!=
MU_ERR_NOENT
)
goto
end
;
/* If to or bcc is present, the must be OK. */
if
(
smtp
->
rcpt_to
&&
(
status
=
mu_mailer_check_to
(
smtp
->
rcpt_to
)))
goto
end
;
if
(
smtp
->
rcpt_bcc
&&
(
status
=
mu_mailer_check_to
(
smtp
->
rcpt_bcc
)))
goto
end
;
}
end:
if
(
status
)
{
mu_address_destroy
(
&
smtp
->
rcpt_to
);
mu_address_destroy
(
&
smtp
->
rcpt_bcc
);
}
else
{
if
(
smtp
->
rcpt_to
)
mu_address_get_count
(
smtp
->
rcpt_to
,
&
smtp
->
rcpt_to_count
);
if
(
smtp
->
rcpt_bcc
)
mu_address_get_count
(
smtp
->
rcpt_bcc
,
&
smtp
->
rcpt_bcc_count
);
if
(
smtp
->
rcpt_to_count
+
smtp
->
rcpt_bcc_count
==
0
)
status
=
MU_ERR_MAILER_NO_RCPT_TO
;
}
return
status
;
}
/* C99 says that a conforming implementations of snprintf ()
should return the number of char that would have been call
but many GNU/Linux && BSD implementations return -1 on error.
Worse QNX/Neutrino actually does not put the terminal
null char. So let's try to cope. */
static
int
smtp_writeline
(
smtp_t
smtp
,
const
char
*
format
,
...)
{
int
len
;
va_list
ap
;
int
done
=
1
;
va_start
(
ap
,
format
);
do
{
len
=
vsnprintf
(
smtp
->
buffer
,
smtp
->
buflen
-
1
,
format
,
ap
);
if
(
len
<
0
||
(
len
>=
(
int
)
smtp
->
buflen
)
||
!
memchr
(
smtp
->
buffer
,
'\0'
,
len
+
1
))
{
char
*
buffer
=
NULL
;
size_t
buflen
=
smtp
->
buflen
*
2
;
buffer
=
realloc
(
smtp
->
buffer
,
buflen
);
if
(
smtp
->
buffer
==
NULL
)
return
ENOMEM
;
smtp
->
buffer
=
buffer
;
smtp
->
buflen
=
buflen
;
done
=
0
;
}
else
done
=
1
;
}
while
(
!
done
);
va_end
(
ap
);
smtp
->
ptr
=
smtp
->
buffer
+
len
;
while
(
len
>
0
&&
isspace
(
smtp
->
buffer
[
len
-
1
]))
len
--
;
if
((
smtp
->
state
!=
SMTP_SEND
&&
smtp
->
state
!=
SMTP_SEND_DOT
)
||
smtp
->
mailer
->
flags
&
MAILER_FLAG_DEBUG_DATA
)
{
MAILER_DEBUG2
(
smtp
->
mailer
,
MU_DEBUG_PROT
,
"> %.*s
\n
"
,
len
,
smtp
->
buffer
);
}
return
0
;
}
static
int
smtp_write
(
smtp_t
smtp
)
{
int
status
=
0
;
size_t
len
;
if
(
smtp
->
ptr
>
smtp
->
buffer
)
{
len
=
smtp
->
ptr
-
smtp
->
buffer
;
status
=
mu_stream_write
(
smtp
->
mailer
->
stream
,
smtp
->
buffer
,
len
,
0
,
&
len
);
if
(
status
==
0
)
{
memmove
(
smtp
->
buffer
,
smtp
->
buffer
+
len
,
len
);
smtp
->
ptr
-=
len
;
}
}
else
{
smtp
->
ptr
=
smtp
->
buffer
;
len
=
0
;
}
return
status
;
}
static
int
smtp_read_ack
(
smtp_t
smtp
)
{
int
status
;
int
multi
;
do
{
multi
=
0
;
status
=
smtp_readline
(
smtp
);
if
((
smtp
->
ptr
-
smtp
->
buffer
)
>
4
&&
smtp
->
buffer
[
3
]
==
'-'
)
multi
=
1
;
if
(
status
==
0
)
smtp
->
ptr
=
smtp
->
buffer
;
}
while
(
multi
&&
status
==
0
);
if
(
status
==
0
)
smtp
->
ptr
=
smtp
->
buffer
;
return
status
;
}
static
int
smtp_parse_ehlo_ack
(
smtp_t
smtp
)
{
int
status
;
int
multi
;
do
{
multi
=
0
;
status
=
smtp_readline
(
smtp
);
if
((
smtp
->
ptr
-
smtp
->
buffer
)
>
4
&&
smtp
->
buffer
[
3
]
==
'-'
)
multi
=
1
;
if
(
status
==
0
)
{
smtp
->
ptr
=
smtp
->
buffer
;
if
(
!
strncasecmp
(
smtp
->
buffer
,
"250-STARTTLS"
,
12
))
smtp
->
capa
|=
CAPA_STARTTLS
;
}
}
while
(
multi
&&
status
==
0
);
if
(
status
==
0
)
smtp
->
ptr
=
smtp
->
buffer
;
return
status
;
}
/* Read a complete line form the pop server. Transform CRLF to LF,
put a null in the buffer when done. */
static
int
smtp_readline
(
smtp_t
smtp
)
{
size_t
n
=
0
;
size_t
total
=
smtp
->
ptr
-
smtp
->
buffer
;
int
status
;
/* Must get a full line before bailing out. */
do
{
status
=
mu_stream_readline
(
smtp
->
mailer
->
stream
,
smtp
->
buffer
+
total
,
smtp
->
buflen
-
total
,
smtp
->
s_offset
,
&
n
);
if
(
status
!=
0
)
return
status
;
/* Server went away, consider this like an error. */
if
(
n
==
0
)
return
EIO
;
total
+=
n
;
smtp
->
s_offset
+=
n
;
smtp
->
nl
=
memchr
(
smtp
->
buffer
,
'\n'
,
total
);
if
(
smtp
->
nl
==
NULL
)
/* Do we have a full line. */
{
/* Allocate a bigger buffer ? */
if
(
total
>=
smtp
->
buflen
-
1
)
{
smtp
->
buflen
*=
2
;
smtp
->
buffer
=
realloc
(
smtp
->
buffer
,
smtp
->
buflen
+
1
);
if
(
smtp
->
buffer
==
NULL
)
return
ENOMEM
;
}
}
smtp
->
ptr
=
smtp
->
buffer
+
total
;
}
while
(
smtp
->
nl
==
NULL
);
/* \r\n --> \n\0 */
if
(
smtp
->
nl
>
smtp
->
buffer
)
{
*
(
smtp
->
nl
-
1
)
=
'\n'
;
*
(
smtp
->
nl
)
=
'\0'
;
smtp
->
ptr
=
smtp
->
nl
;
}
MAILER_DEBUG1
(
smtp
->
mailer
,
MU_DEBUG_PROT
,
"< %s"
,
smtp
->
buffer
);
return
0
;
}
#else
#include <stdio.h>
#include <registrar0.h>
mu_record_t
mu_smtp_record
=
NULL
;
#endif
Please
register
or
sign in
to post a comment