Go to the first, previous, next, last section, table of contents.
Wherever the mail is and whatever format it is stored in, the same operations to manipulate emails are common. To unified the C API, GNU mailutils offers a heteroclite set of objects that work in aggregation to do operations on emails. Each object do a specific task and delegates non related tasks to others. The object comes alive by specifying a URL parameter when created, it will indicate the storage format or protocol (POP3, IMAP4, MH, MAILDIR, etc ..).
folder_t url_t -/var/mail- +- .. ->+-----------------+ +-->+------------+ ( alain *-)-+ | | url_t *-|---+ | port | ----------- | | |-----------------| | hostname | ( jakob *-)-+--+ | auth_t *-|---+ | file | ----------- | |-----------------| | | ... | ( jeff *-)-+ | stream_t | | +------------+ ----------- | |-----------------| | ( shaleh*-)-+ | ..... | | auth_t ---------- |-----------------| +-->+------------+ +---|-* mailbox_t[] | | ticket_t | mailbox_t | +-----------------+ +------------+ +----------------+<-+ | locker_t *--|-------------+ |----------------| | | url_t | | locker_t |----------------| +-------->+---------+ | stream_t | | lock | |----------------| | unlock | | message_t[] *-|-------+ +---------+ +----------------+ | envelope_t | +-------->+-----------+ message_t | | | date | +----------------+<------+ | | from | | envelope_t *-|------------------+ | to | |----------------| header_t +-----------+ | header_t *-|------------>+--------------+ |----------------| | stream_t | | body_t *-|----+ +--------------+ +----------------+ | body_t +-->+--------------+ | stream_t | +--------------+
For example writing a simple from
command that will list the
From and Subject headers of every mail in a mailbox.
/* sfrom, Simple From */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <mailutils/registrar.h> #include <mailutils/list.h> #include <mailutils/mailbox.h> #define BUFFER_SIZE 64 int main (int argc, const char **argv) { char from[BUFFER_SIZE]; char subject[BUFFER_SIZE]; char *mail; mailbox_t mbox; int status size_t msgno, total = 0; mail = (argc == 2) ? argv[1] : getenv ("MAIL"); /* Register the type of mailbox. IMAP4, POP3 and local format */ { list_t registrar; registrar_get_list (®istrar); list_append (registrar, imap_record); list_append (registrar, path_record); list_append (registrar, pop_record); } status = mailbox_create (&mbox, mail); if (status != 0) { fprintf (stderr, "mailbox_create: %s\n", strerror (status)); exit (EXIT_FAILURE); } status = mailbox_open (mbox, MU_STREAM_READ); if (status != 0) { fprintf (stderr, "mailbox_open: %s\n", strerror (status)); exit (EXIT_FAILURE); } mailbox_messages_count (mbox, &total); for (msgno = 1; msgno <= total; msgno++) { message_t msg; header_t hdr; if ((status = mailbox_get_message (mbox, msgno, &msg)) != 0 || (status = message_get_header (msg, &hdr)) != 0) { fprintf (stderr, "Error message:%s\n", strerror (status)); exit (EXIT_FAILURE); } status = header_get_value (hdr, MU_HEADER_FROM, from, sizeof (from), NULL); if (status != 0) strcpy (from, "(NO FROM)"); status = header_get_value (hdr, MU_HEADER_SUBJECT, subject, sizeof (subject), NULL); if (status != 0) strcpy (subject, "(NO SUBJECT)"); printf ("%s\t%s\n", from, subject); } mailbox_close (mbox); mailbox_destroy (&mbox); return 0; }
% MAIL=pop://alain@localhost ./sfrom Passwd: xxxx Jim Meyering <meyering@foo.org> fetish(shellutils) beta Fran@,{c}ois Pinard <pinard@bar.org> recode new alpha ...
/* Prefix folder_ is reserve */
#include <mailutils/folder.h>
folder_t url_t -/var/mail- +---//---->/-----------------\ +-->/-----------\ ( alain *-)-+ | | url_t *-|----+ | port | ----------- | | |-----------------+ | hostname | ( jakob *-)-+--+ | observer_t *-| | file | ----------- | |-----------------+ | ... | ( jeff *-)-+ | stream_t | \-----------/ ----------- | |-----------------| ( sean *-)-+ | auth_t | ---------- |-----------------| | mailbox_t(1) | |-----------------| | mailbox_t(2) | | ...... | | mailbox_t(n) | \-----------------/
/* Prefix mailbox_ is reserved */
#include <mailutils/mailbox.h>
mailbox_t
object is used to hold information and it is an opaque
data structure to the user. Functions are provided to retrieve information
from the data structure.
mailbox_t url_t -/var/mail- +---//---->/-----------------\ +-->/-----------\ ( alain ) | | url_t *-|----+ | port | ----------- | |-----------------+ | hostname | ( jakob *-)----+ | observer_t *-| | file | ----------- |-----------------+ | ... | ( jeff ) | stream_t | \-----------/ ----------- |-----------------| ( sean ) | locker_t | ---------- |-----------------| | message_t(1) | |-----------------| | message_t(2) | | ...... | | message_t(n) | \-----------------/
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
mailbox_create
allocates and initializes pmbox.
The concrete mailbox type instantiate is based on the scheme of the url name.
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
ENOMEM
mailbox_create ()
based on the environment
variable $MAIL or the string formed by
_PATH_MAILDIR/user" or $LOGNAME if user is null,
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
ENOMEM
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
ENOMEM
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
ENOMEM
0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
ENOMEM
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
stream_create
for flag's description.
The return value is 0
on success and a code number on error conditions:
EAGAIN
EINPROGRESS
EBUSY
MU_ERROR_INVALID_PARAMETER
ENOMEM
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
ENOMEM
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
auth_t
object on creation.
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
The return value is 0
on success and a code number on error conditions:
MU_ERROR_INVALID_PARAMETER
/* Prefix mailer_ is reserved */
#include <mailutils/mailer.h>
The API is still shaky and undefined.
#include <mailutils/message.h>
/* Prefix message_ is reserve */
The message_t
object is a convenient way to manipulate messages. It
encapsulates the envelope_t
, the header_t
and the body_t
.
mailbox_t __________ message_t (message[1]) +------>+-----------------------+ ---------- | | envelope_t | (message[2]) | |-----------------------| ---------- | | header_t | (message[3])--------+ |-----------------------| ---------- | body_t | (message[n]) |-----------------------| ---------- | attribute_t | |-----------------------| | stream_t | +-----------------------+
/* Prefix envelope_ is reserved */
#include <mailutils/envelope.h>
Get the date that the message was delivered to the mailbox, in something close to ANSI ctime() format: Mon Jul 05 13:08:27 1999.
Get the address that this message was reportedly received from. This would be the "mail from" argument if the message was delivered or received via SMTP, for example.
Primarily for internal use. The implementation of envelope_t depends on the mailbox type, this allows the function which actually gets the sender to be set by the creator of an envelope_t.
Primarily for internal use. The implementation of envelope_t depends on the mailbox type, this allows the function which actually gets the date to be set by the creator of an envelope_t.
/* Prefix header_ is reserved */
#include <mailutils/header.h>
So far we plan support for RFC822 and plan for RFC1522. with RFC1522 non ASCII characters will be encoded.
Some basic macros are already provided for rfc822.
MU_HDR_RETURN_PATH
MU_HDR_RECEIVED
MU_HDR_DATE
MU_HDR_FROM
MU_HDR_RESENT_FROM
MU_HDR_SUBJECT
MU_HDR_SENDER
MU_HDR_RESENT_SENDER
MU_HDR_TO
MU_HDR_RESENT_TO
MU_HDR_CC
MU_HDR_RESENT_CC
MU_HDR_BCC
MU_HDR_RESENT_BCC
MU_HDR_REPLY_TO
MU_HDR_RESENT_REPLY_TO
MU_HDR_MESSAGE_ID
MU_HDR_RESENT_MESSAGE_ID
MU_HDR_IN_REPLY_TO
MU_HDR_ENCRYPTED
MU_HDR_PRECEDENCE
MU_HDR_STATUS
MU_HDR_CONTENT_LENGTH
MU_HDR_CONTENT_TYPE
MU_HDR_MIME_VERSION
/* Prefix body_ is reserved */
#include <mailutils/body.h>
/* Prefix attribute_ is reserved */
#include <mailutils/attribute.h>
#include <mailutils/stream.h>
MU_STREAM_READ
MU_STREAM_WRITE
MU_STREAM_RDWR
MU_STREAM_APPEND
MU_STREAM_CREAT
MU_STREAM_NONBLOCK
MU_STREAM_NO_CHECK
MU_STREAM_STATE_OPEN
stream_open
.
MU_STREAM_STATE_READ
stream_read
or stream_readline
.
MU_STREAM_STATE_WRITE
stream_write
.
MU_STREAM_STATE_CLOSE
stream_close
.
An example using tcp_stream_create
to make a simple web client:
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <sys/select.h> #include <mailutils/io.h> const char *wbuf = "GET / HTTP/1.0\r\n\r\n"; char rbuf[1024]; int main(int argc, char **argv) { int ret, off = 0, fd; stream_t stream; size_t nb; fd_set fds; argc = argc, argv = argv; ret = tcp_stream_create (&stream); if (ret != 0) { fprintf (stderr, "tcp_stream_create: %s\n", mailutils_error(ret)); exit (EXIT_FAILURE); } connect_again: ret = stream_open (stream, "www.netscape.com", 80, MU_STREAM_NONBLOCK); if (ret != 0) { if (ret == MU_ERROR_EAGAIN) { ret = stream_get_fd(stream, &fd); if (ret != 0) { fprintf (stderr, "stream_get_fd: %s\n", mailutils_error(ret)); exit (EXIT_FAILURE); } FD_ZERO (&fds); FD_SET (fd, &fds); select (fd+1, NULL, &fds, NULL, NULL); goto connect_again; } fprintf (stderr, "stream_open: %s\n", mailutils_error (ret)); exit (EXIT_FAILURE); } ret = stream_get_fd (stream, &fd); if (ret != 0) { fprintf(stderr, "stream_get_fd: %s\n", strerror(ret)); exit (EXIT_FAILURE); } write_again: ret = stream_write (stream, wbuf + off, strlen (wbuf), 0, &nb); if (ret != 0 ) { if (ret == EAGAIN) { FD_ZERO (&fds); FD_SET (fd, &fds); select (fd + 1, NULL, &fds, NULL, NULL); off += nb; goto write_again; } fprintf (stderr, "stream_write: %s\n", strerror(ret)); exit (EXIT_FAILURE) } if (nb != strlen (wbuf)) { fprintf(stderr, "stream_write: %s\n", "nb != wbuf length"); exit (EXIT_FAILURE); } do { read_again: ret = stream_read (stream, rbuf, sizeof (rbuf), 0, &nb); if (ret != 0) { if (ret == EAGAIN) { FD_ZERO (&fds); FD_SET (fd, &fds); select (fd + 1, &fds, NULL, NULL, NULL); goto read_again; } fprintf (stderr, "stream_read: %s\n", strerror(ret)); exit(EXIT_FAILURE); } write (2, rbuf, nb); } while (nb); ret = stream_close (stream); if (ret!= 0) { fprintf (stderr, "stream_close: %s\n", strerror(ret)); exit (EXIT_FAILURE); } stream_destroy (&stream, NULL); exit (EXIT_SUCCESS); }
/* Prefix iterator_ is reserved */
#include <mailutils/iterator.h>
/* Prefix auth_ is reserved */
#include <mailutils/auth.h>
There are many ways to authenticate to a server. To be flexible the
authentication process is provided by two objects auth_t
and
ticket_t
. The auth_t
can implement different protocol like
APOP, MD5-AUTH, One Time Passwd etc .. By default if a mailbox
does not understand or know how to authenticate it falls back to
user/passwd authentication. The ticket_t
is a way for
Mailboxes and Mailers provide a way to authenticate when the URL does not
contain enough information. The default action is to call the function
auth_authenticate
which will get the user and passwd
if not set, this function can be overridden by a custom method.
A simple example of an authenticate function:
#include <mailutils/auth.h> #include <stdio.h> #include <string.h> int my_authenticate (auth_t auth, char **user, char **passwd) { char u[128] = ""; char p[128] = ""; /* prompt the user name */ printf ("User: "); fflush (stdout); fgets (u, sizeof (u), stdin); u[strlen (u) - 1] = '\0'; /* nuke the trailing NL */ /* prompt the passwd */ printf ("Passwd: "); fflush (stdout); echo_off (); fgets (p, sizeof(p), stdin); echo_on (); p[strlen (p) - 1] = '\0'; /* duplicate */ *user = strdup (u); *passwd = strdup (p); return 0; }
/* Prefix address_ is reserved */
#include <mailutils/address.h>
The Internet address format is defined in RFC 822. RFC 822 has been updated, and is now superceeded by RFC 2822, which makes some corrections and clarifications. References to RFC 822 here apply equally to RFC 2822.
The RFC 822 format is more flexible than many people realize, here is a quick summary of the syntax this parser implements, see RFC 822 for the details. "[]" pairs mean "optional", "/" means "one or the other", and double-quoted characters are literals.
addr-spec = local-part "@" domain mailbox = addr-spec ["(" display-name ")"] / [display-name] "<" [route] addr-spec ">" mailbox-list = mailbox ["," mailbox-list] group = display-name ":" [mailbox-list] ";" address = mailbox / group / unix-mbox address-list = address ["," address-list]
unix-mbox is a non-standard extension meant to deal with the common practice of using user names as addresses in mail utilities. It allows addresses such as "root" to be parsed correctly. These are NOT valid internet email addresses, they must be qualified before use.
Several address functions have a set of common arguments with consistent semantics, these are described here to avoid repetition.
Since an address-list may contain multiple addresses, they are accessed by a one-based index number, no. The index is one-based because pop, imap, and other message stores commonly use one-based counts to access messages and attributes of messages.
If len is greater than 0
it is the length of the buffer
buf, and as much of the component as possible will be copied
into the buffer. The buffer will be null terminated.
The size of a particular component may be queried by providing 0
for the len of the buffer, in which case the buffer is optional.
In this case, if n is provided *n is assigned the length of
the component string.
@macro ADDRESSENOMEM
null
.
address_t
object is used to hold information about a parsed
RFC822 address list, and is an opaque
data structure to the user. Functions are provided to retrieve information
about an address in the address list.
0
on success and a code number on error conditions:
0
on success and a code number on error conditions:
0
on success and a code number on error conditions:
0
on success and a code number on error conditions:
0
length for a unix-mbox.
The return value is 0
on success and a code number on error conditions:
0
on success and a code number on error conditions:
0
length for a unix-mbox.
The return value is 0
on success and a code number on error conditions:
0
on success and a code number on error conditions:
1
if this address is just the name of a group,
0
otherwise. This is faster than checking if the address has
a non-zero length personal, and a zero-length local_part and domain.
yes can be null
, though that doesn't serve much purpose other
than determining that no refers to an address.
Currently, there is no way to determine the end of the group.
The return value is 0
on success and a code number on error conditions:
0
on success and a code number on error conditions:
null
, the count is 0
. If count is
not null
, the count will be written to *count.
The return value is 0
.
#include <stdio.h> #include <errno.h> #include <mailutils/address.h> #define EPARSE ENOENT static const char* err_name(int e) { struct { int e; const char* s; } map[] = { #define E(e) { e, #e }, E(ENOENT) E(EINVAL) E(ENOMEM) #undef E { 0, NULL } }; static char s[sizeof(int) * 8 + 3]; int i; for(i = 0; map[i].s; i++) { if(map[i].e == e) return map[i].s; } sprintf(s, "[%d]", e); return s; } static int parse(const char* str) { size_t no = 0; size_t pcount = 0; int status; char buf[BUFSIZ]; address_t address = NULL; status = address_create(&address, str); address_get_count(address, &pcount); if(status) { printf("%s=> error %s\n\n", str, err_name(status)); return 0; } else { printf("%s=> pcount %d\n", str, pcount); } for(no = 1; no <= pcount; no++) { size_t got = 0; int isgroup; address_is_group(address, no, &isgroup); printf("%d ", no); if(isgroup) { address_get_personal(address, no, buf, sizeof(buf), &got); printf("group <%s>\n", buf); } else { address_get_email(address, no, buf, sizeof(buf), 0); printf("email <%s>\n", buf); } address_get_personal(address, no, buf, sizeof(buf), &got); if(got && !isgroup) printf(" personal <%s>\n", buf); address_get_comments(address, no, buf, sizeof(buf), &got); if(got) printf(" comments <%s>\n", buf); address_get_local_part(address, no, buf, sizeof(buf), &got); if(got) { printf(" local-part <%s>", buf); address_get_domain(address, no, buf, sizeof(buf), &got); if(got) printf(" domain <%s>", buf); printf("\n"); } address_get_route(address, no, buf, sizeof(buf), &got); if(got) printf(" route <%s>\n", buf); } address_destroy(&address); printf("\n"); return 0; } static int parseinput(void) { char buf[BUFSIZ]; while(fgets(buf, sizeof(buf), stdin) != 0) { buf[strlen(buf) - 1] = 0; parse(buf); } return 0; } int main(int argc, const char *argv[]) { argc = 1; if(!argv[argc]) { return parseinput(); } for(; argv[argc]; argc++) { if(strcmp(argv[argc], "-") == 0) { parseinput(); } else { parse(argv[argc]); } } return 0; }
/* Prefix locker_ is reserved */
#include <mailutils/locker.h>
MU_LOCKER_RDLOCK
MU_LOCKER_WRLOCK
MU_LOCKER_PID
MU_LOCKER_FCNTL
MU_LOCKER_TIME
A mailbox or a mailer can be described in a URL, the string will contain the
necessary information to initialize mailbox_t
, or mailer_t
properly.
The POP URL scheme contains a POP server, optional port number and the authentication mechanism. The general form is
pop://[<user>[;AUTH=<auth>]@]<host>[:<port>]
orpop://[<user>[:<passwd>]@]<host>[:<port>]
If :port is omitted the default value is 110. Different forms of authentication can be specified with ;AUTH=type. The special string ;AUTH=* indicates that the client will use a default scheme base on the capability of the server.
pop://obelix@gaulois.org
pop://asterix;AUTH=*@france.com
pop://falbala;AUTH=+APOP@france.com
pop://obelix;AUTH=+APOP@village.gaulois.org:2000
pop://obelix:menhir@village.gaulois.org:2000
For more complete information see rfc2368.
The IMAP URL scheme contains an IMAP server, optional port number and the authentication mechanism. The general form is
imap://[<user>[;AUTH=<type>]]@<host>[:port][/<mailbox>]
orimap://[<user>[:<passwd>]]@<host>[:port][/<mailbox>]
If :port is omitted the default value is 220. Different forms of authentication can be specified with ;AUTH=type. The special string ;AUTH=* indicates that the client will use a default scheme base on the capability of the server.
imap://obelix@imap.gaulois.org
imap://asterix;AUTH=*@imap.france.com
imap://asterix:potion@imap.france.com
For more complete information see rfc2192.
Local folder should be handle by this URL. It is preferable to let the mailbox recognize the type of mailbox and take the appropriate action.
file://path
file://var/mail/user
file://home/obelix/Mail
For MMDF, MH local mailboxes URLs are provided, but it is preferable to
use file://path
and let the library figure out which one.
mmdf://path
mh://path
After setting a mailer, mailto:
is used to tell the mailer where
and to whom the message is for.
mailto://hostname
Mailto can be used to generate short messages, for example to subscribe to mailing lists.
mailto://bug-mailutils@gnu.org?body=subscribe
mailto://bug-mailutils@gnu.org?Subject=hello&body=subscribe
For more complete information see rfc2368.
Helper functions are provided to retrieve and set the URL fields.
The syntax, condensed from RFC 1738, and extended with the ;auth= of RFC 2384 (for POP) and RFC 2192 (for IMAP) is:
url = scheme ":" = "//" [ user [ ( ":" password ) | ( ";auth=" auth ) ] "@" ] host [ ":" port ] [ ( "/" urlpath ) | ( "?" query ) ]
This is a generalized URL syntax, and may not be exactly appropriate for any particular scheme.
#include <mailutils/url.h> #include <stdio.h> #include <string.h> int main () { char str[1024]; char buffer[1024]; long port = 0; int len = sizeof (buffer); url_t u = NULL; while (fgets (str, sizeof (str), stdin) != NULL) { int rc; str[strlen (str) - 1] = '\0'; /* chop newline */ if(strspn(str, " \t") == strlen(str)) continue; /* skip empty lines */ if ((rc = url_create(&u, str)) != 0) { printf(stderr, "url_create %s ERROR: [%d] %s", str, rc, strerror(rc)); exit (1); } if ((rc = url_parse (u)) != 0) { printf ("%s --> FAILED: [%d] %s\n", str, rc, strerror(rc)); continue; } printf ("%s --> SUCCESS\n", str); url_get_scheme (u, buffer, len, NULL); printf (" scheme <%s>\n", buffer); url_get_user (u, buffer, len, NULL); printf (" user <%s>\n", buffer); url_get_passwd (u, buffer, len, NULL); printf (" passwd <%s>\n", buffer); url_get_auth (u, buffer, len, NULL); printf (" auth <%s>\n", buffer); url_get_host (u, buffer, len, NULL); printf (" host <%s>\n", buffer); url_get_port (u, &port); printf (" port %ld\n", port); url_get_path (u, buffer, len, NULL); printf (" path <%s>\n", buffer); url_get_query (u, buffer, len, NULL); printf (" query <%s>\n", buffer); url_destroy (&u); } return 0; }
Go to the first, previous, next, last section, table of contents.