smtp-client
SMTP Client C Library
mailx.c File Reference

POSIX mailx utility. More...

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "smtp.h"
+ Include dependency graph for mailx.c:

Go to the source code of this file.

Data Structures

struct  mailx_address
 
struct  mailx_attachment
 
struct  mailx
 

Macros

#define _POSIX_C_SOURCE   200809L
 

Functions

static char * smtp_ffile_get_contents (FILE *stream, size_t *bytes_read)
 
static void mailx_address_append (struct mailx *const mailx, enum smtp_address_type address_type, const char *const email)
 
static void mailx_send (struct mailx *const mailx)
 
static void mailx_append_attachment (struct mailx *const mailx, const char *const filename, const char *const path)
 
static void mailx_append_attachment_arg (struct mailx *const mailx, const char *const attach_arg)
 
static void mailx_parse_smtp_option (struct mailx *const mailx, const char *const option)
 
static void mailx_init_default_values (struct mailx *const mailx)
 
static void mailx_free (const struct mailx *const mailx)
 
int main (int argc, char *argv[])
 

Detailed Description

POSIX mailx utility.

Author
James Humphrey (mail@.nosp@m.somn.nosp@m.isoft.nosp@m..com)
Version
1.00

Implementation of POSIX mailx utility in send mode.

mailx [-s subject] [[-S option]...] [[-a attachment]...] address...

This software has been placed into the public domain using CC0.

Definition in file mailx.c.

Macro Definition Documentation

◆ _POSIX_C_SOURCE

#define _POSIX_C_SOURCE   200809L

Required on some POSIX systems to include some standard functions.

Definition at line 17 of file mailx.c.

Function Documentation

◆ mailx_address_append()

static void mailx_address_append ( struct mailx *const  mailx,
enum smtp_address_type  address_type,
const char *const  email 
)
static

Append this email to the list of email addresses to send to.

Parameters
[in]mailxAppend the email address into this mailx context.
[in]address_typeSee smtp_address_type.
[in]emailEmail address to send to.

Definition at line 200 of file mailx.c.

References mailx::address_list, mailx_address::address_type, mailx_address::email, mailx::num_address, and realloc.

Referenced by main().

202  {
203  struct mailx_address *new_address;
204  size_t new_address_list_sz;
205 
206  mailx->num_address += 1;
207  new_address_list_sz = mailx->num_address * sizeof(*mailx->address_list);
208  if((mailx->address_list = realloc(mailx->address_list,
209  new_address_list_sz)) == NULL){
210  err(1, "realloc");
211  }
212 
213  new_address = &mailx->address_list[mailx->num_address - 1];
214  new_address->address_type = address_type;
215  strncpy(new_address->email, email, sizeof(new_address->email));
216  new_address->email[sizeof(new_address->email) - 1] = '\0';
217 }
#define realloc
Definition: seams.h:161
enum smtp_address_type address_type
Definition: mailx.c:34
char email[1000]
Definition: mailx.c:39
struct mailx_address * address_list
Definition: mailx.c:123
size_t num_address
Definition: mailx.c:128
+ Here is the caller graph for this function:

◆ mailx_append_attachment()

static void mailx_append_attachment ( struct mailx *const  mailx,
const char *const  filename,
const char *const  path 
)
static

Attach a file to the mailx context.

Parameters
[in]mailxStore the attachment details into this mailx context.
[in]filenameFile name to display to the recipient.
[in]pathLocal path of file to attach.

Definition at line 270 of file mailx.c.

References mailx::attachment_list, mailx_attachment::name, mailx::num_attachment, mailx_attachment::path, and realloc.

Referenced by mailx_append_attachment_arg().

272  {
273  struct mailx_attachment *new_attachment;
274  size_t new_attachment_list_sz;
275 
276  if(filename == NULL || path == NULL){
277  errx(1, "must provide attachment with valid name:path");
278  }
279 
280  new_attachment_list_sz = (mailx->num_attachment + 1) *
281  sizeof(*mailx->attachment_list);
282  if((mailx->attachment_list = realloc(mailx->attachment_list,
283  new_attachment_list_sz)) == NULL){
284  err(1, "realloc: attachment list");
285  }
286  new_attachment = &mailx->attachment_list[mailx->num_attachment];
287  mailx->num_attachment += 1;
288 
289  strncpy(new_attachment->name, filename, sizeof(new_attachment->name));
290  new_attachment->name[sizeof(new_attachment->name) - 1] = '\0';
291 
292  strncpy(new_attachment->path, path, sizeof(new_attachment->path));
293  new_attachment->path[sizeof(new_attachment->path) - 1] = '\0';
294 }
char path[1000]
Definition: mailx.c:55
#define realloc
Definition: seams.h:161
struct mailx_attachment * attachment_list
Definition: mailx.c:133
size_t num_attachment
Definition: mailx.c:138
char name[1000]
Definition: mailx.c:50
+ Here is the caller graph for this function:

◆ mailx_append_attachment_arg()

static void mailx_append_attachment_arg ( struct mailx *const  mailx,
const char *const  attach_arg 
)
static

Parse the file name and path and attach it to the mailx context.

Parameters
[in]mailxStore the attachment details into this mailx context.
[in]attach_argString with format: 'filename:filepath'.

Definition at line 303 of file mailx.c.

References mailx_append_attachment().

Referenced by main().

304  {
305  char *attach_arg_dup;
306  char *filename;
307  char *filepath;
308 
309  if((attach_arg_dup = strdup(attach_arg)) == NULL){
310  err(1, "strdup: %s", attach_arg);
311  }
312 
313  filename = strtok(attach_arg_dup, ":");
314  filepath = strtok(NULL, ":");
315 
316  mailx_append_attachment(mailx, filename, filepath);
317 
318  free(attach_arg_dup);
319 }
static void mailx_append_attachment(struct mailx *const mailx, const char *const filename, const char *const path)
Definition: mailx.c:270
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mailx_free()

static void mailx_free ( const struct mailx *const  mailx)
static

Frees the allocated memory associated with the mailx context.

Parameters
[in]mailxThe mailx context to free.

Definition at line 452 of file mailx.c.

References mailx::body, mailx::from, mailx::pass, mailx::port, mailx::server, and mailx::user.

Referenced by main().

452  {
453  free(mailx->body);
454  free(mailx->server);
455  free(mailx->port);
456  free(mailx->user);
457  free(mailx->pass);
458  free(mailx->from);
459 }
char * user
Definition: mailx.c:91
char * from
Definition: mailx.c:101
char * body
Definition: mailx.c:76
char * port
Definition: mailx.c:86
char * server
Definition: mailx.c:81
char * pass
Definition: mailx.c:96
+ Here is the caller graph for this function:

◆ mailx_init_default_values()

static void mailx_init_default_values ( struct mailx *const  mailx)
static

Initialize and set the default options in the mailx context.

See description of -S argument in main for more details.

Parameters
[in]mailxThe mailx content to initialize.

Definition at line 439 of file mailx.c.

References mailx::auth_method, mailx::connection_security, SMTP_AUTH_NONE, SMTP_SECURITY_NONE, and mailx::subject.

Referenced by main().

439  {
440  memset(mailx, 0, sizeof(*mailx));
441  mailx->subject = "";
443  mailx->auth_method = SMTP_AUTH_NONE;
444 }
const char * subject
Definition: mailx.c:71
enum smtp_connection_security connection_security
Definition: mailx.c:106
enum smtp_authentication_method auth_method
Definition: mailx.c:111
+ Here is the caller graph for this function:

◆ mailx_parse_smtp_option()

static void mailx_parse_smtp_option ( struct mailx *const  mailx,
const char *const  option 
)
static

Parses the -S option which contains a key/value pair separated by an '=' character.

Parameters
[in]mailxStore the results of the option parsing into the relevant field in this mailx context.
[in]optionString containing key/value option to parse.

Definition at line 330 of file mailx.c.

References mailx::auth_method, mailx::connection_security, mailx::from, mailx::pass, mailx::port, mailx::server, SMTP_AUTH_CRAM_MD5, SMTP_AUTH_LOGIN, SMTP_AUTH_NONE, SMTP_AUTH_PLAIN, SMTP_DEBUG, mailx::smtp_flags, SMTP_NO_CERT_VERIFY, SMTP_SECURITY_NONE, SMTP_SECURITY_STARTTLS, SMTP_SECURITY_TLS, and mailx::user.

Referenced by main().

331  {
332  char *optdup;
333  char *opt_key;
334  char *opt_value;
335  int rc;
336 
337  rc = 0;
338 
339  if((optdup = strdup(option)) == NULL){
340  err(1, "strdup: option: %s", option);
341  }
342 
343  if((opt_key = strtok(optdup, "=")) == NULL){
344  errx(1, "strtok: %s", optdup);
345  }
346 
347  opt_value = strtok(NULL, "=");
348 
349  if(strcmp(opt_key, "smtp-security") == 0){
350  if(strcmp(opt_value, "none") == 0){
352  }
353 #ifdef SMTP_OPENSSL
354  else if(strcmp(opt_value, "tls") == 0){
356  }
357  else if(strcmp(opt_value, "starttls") == 0){
359  }
360 #endif /* SMTP_OPENSSL */
361  else{
362  rc = -1;
363  }
364  }
365  else if(strcmp(opt_key, "smtp-auth") == 0){
366  if(strcmp(opt_value, "none") == 0){
367  mailx->auth_method = SMTP_AUTH_NONE;
368  }
369  else if(strcmp(opt_value, "plain") == 0){
370  mailx->auth_method = SMTP_AUTH_PLAIN;
371  }
372  else if(strcmp(opt_value, "login") == 0){
373  mailx->auth_method = SMTP_AUTH_LOGIN;
374  }
375 #ifdef SMTP_OPENSSL
376  else if(strcmp(opt_value, "cram-md5") == 0){
378  }
379 #endif /* SMTP_OPENSSL */
380  else{
381  rc = -1;
382  }
383  }
384  else if(strcmp(opt_key, "smtp-flag") == 0){
385  if(strcmp(opt_value, "debug") == 0){
386  mailx->smtp_flags |= SMTP_DEBUG;
387  }
388  else if(strcmp(opt_value, "no-cert-verify") == 0){
390  }
391  else{
392  rc = -1;
393  }
394  }
395  else if(strcmp(opt_key, "smtp-server") == 0){
396  if((mailx->server = strdup(opt_value)) == NULL){
397  err(1, "strdup");
398  }
399  }
400  else if(strcmp(opt_key, "smtp-port") == 0){
401  if((mailx->port = strdup(opt_value)) == NULL){
402  err(1, "strdup");
403  }
404  }
405  else if(strcmp(opt_key, "smtp-user") == 0){
406  if((mailx->user = strdup(opt_value)) == NULL){
407  err(1, "strdup");
408  }
409  }
410  else if(strcmp(opt_key, "smtp-pass") == 0){
411  if((mailx->pass = strdup(opt_value)) == NULL){
412  err(1, "strdup");
413  }
414  }
415  else if(strcmp(opt_key, "smtp-from") == 0){
416  if((mailx->from = strdup(opt_value)) == NULL){
417  err(1, "strdup");
418  }
419  }
420  else{
421  rc = -1;
422  }
423 
424  free(optdup);
425 
426  if(rc < 0){
427  errx(1, "invalid argument: %s", option);
428  }
429 }
char * user
Definition: mailx.c:91
enum smtp_connection_security connection_security
Definition: mailx.c:106
enum smtp_authentication_method auth_method
Definition: mailx.c:111
char * from
Definition: mailx.c:101
char * port
Definition: mailx.c:86
char * server
Definition: mailx.c:81
enum smtp_flag smtp_flags
Definition: mailx.c:118
char * pass
Definition: mailx.c:96
+ Here is the caller graph for this function:

◆ mailx_send()

static void mailx_send ( struct mailx *const  mailx)
static

Send the email using the configuration options in the mailx context.

Parameters
[in]mailxEmail context.

Definition at line 225 of file mailx.c.

References mailx::address_list, mailx_address::address_type, mailx::attachment_list, mailx::auth_method, mailx::body, mailx::connection_security, mailx_address::email, mailx_attachment::name, mailx::num_address, mailx::num_attachment, mailx::pass, mailx_attachment::path, mailx::port, mailx::server, mailx::smtp, smtp_address_add(), smtp_attachment_add_path(), smtp_auth(), smtp_close(), mailx::smtp_flags, smtp_header_add(), smtp_mail(), smtp_open(), smtp_status_code_errstr(), SMTP_STATUS_OK, mailx::subject, and mailx::user.

Referenced by main().

225  {
226  int rc;
227  size_t i;
228  const struct mailx_address *address;
229  const struct mailx_attachment *attachment;
230 
231  smtp_open(mailx->server,
232  mailx->port,
233  mailx->connection_security,
234  mailx->smtp_flags,
235  NULL,
236  &mailx->smtp);
237 
238  smtp_auth(mailx->smtp,
239  mailx->auth_method,
240  mailx->user,
241  mailx->pass);
242 
243  for(i = 0; i < mailx->num_address; i++){
244  address = &mailx->address_list[i];
245  smtp_address_add(mailx->smtp, address->address_type, address->email, NULL);
246  }
247 
248  for(i = 0; i < mailx->num_attachment; i++){
249  attachment = &mailx->attachment_list[i];
250  smtp_attachment_add_path(mailx->smtp, attachment->name, attachment->path);
251  }
252  smtp_header_add(mailx->smtp, "Subject", mailx->subject);
253  smtp_mail(mailx->smtp, mailx->body);
254 
255  rc = smtp_close(mailx->smtp);
256 
257  if(rc != SMTP_STATUS_OK){
258  errx(1, "%s", smtp_status_code_errstr(rc));
259  }
260 }
enum smtp_status_code smtp_close(struct smtp *smtp)
Definition: smtp.c:3168
const char * smtp_status_code_errstr(enum smtp_status_code status_code)
Definition: smtp.c:3241
const char * subject
Definition: mailx.c:71
char * user
Definition: mailx.c:91
enum smtp_connection_security connection_security
Definition: mailx.c:106
char path[1000]
Definition: mailx.c:55
enum smtp_status_code smtp_header_add(struct smtp *const smtp, const char *const key, const char *const value)
Definition: smtp.c:3278
enum smtp_authentication_method auth_method
Definition: mailx.c:111
enum smtp_status_code smtp_mail(struct smtp *const smtp, const char *const body)
Definition: smtp.c:3062
struct smtp * smtp
Definition: mailx.c:66
char * body
Definition: mailx.c:76
enum smtp_status_code smtp_attachment_add_path(struct smtp *const smtp, const char *const name, const char *const path)
Definition: smtp.c:3416
char * port
Definition: mailx.c:86
struct mailx_attachment * attachment_list
Definition: mailx.c:133
size_t num_attachment
Definition: mailx.c:138
char * server
Definition: mailx.c:81
enum smtp_address_type address_type
Definition: mailx.c:34
enum smtp_flag smtp_flags
Definition: mailx.c:118
enum smtp_status_code smtp_address_add(struct smtp *const smtp, enum smtp_address_type type, const char *const email, const char *const name)
Definition: smtp.c:3347
char name[1000]
Definition: mailx.c:50
char email[1000]
Definition: mailx.c:39
struct mailx_address * address_list
Definition: mailx.c:123
enum smtp_status_code smtp_auth(struct smtp *const smtp, enum smtp_authentication_method auth_method, const char *const user, const char *const pass)
Definition: smtp.c:3026
size_t num_address
Definition: mailx.c:128
char * pass
Definition: mailx.c:96
enum smtp_status_code smtp_open(const char *const server, const char *const port, enum smtp_connection_security connection_security, enum smtp_flag flags, const char *const cafile, struct smtp **smtp)
Definition: smtp.c:2987
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char *  argv[] 
)

Main program entry point for the mailx utility.

This program supports the following options:

  • -a 'name:path' - Attach a file with name to display to recipient and file path pointing to file location on local storage.
  • -s subject - Email subject line.
  • -S key=value - A key/value pair to set various configuration options, controlling the behavior of the SMTP client connection.

The following list contains possible options for the -S argument:

  • smtp-security - none, tls, starttls
  • smtp-auth - none, plain, login, cram-md5
  • smtp-flag - debug, no-cert-verify
  • smtp-server - server name or IP address
  • smtp-port - server port number
  • smtp-user - server authentication user name
  • smtp-pass - server authentication user password
  • smtp-from - from email account

The following list shows the default option for -S argument if not provided:

  • smtp-security - none
  • smtp-auth - none
  • smtp-flag - none
  • smtp-server - localhost
  • smtp-port - 25
  • smtp-user - none
  • smtp-pass - none
  • smtp-from - none
Parameters
[in]argcNumber of arguments in argv.
[in]argvString array containing the program name and any optional parameters described above.
Return values
0Email has been sent.
1An error occurred while sending email. Although unlikely, an email can still get sent even after returning with this error code.

Definition at line 498 of file mailx.c.

References mailx::body, mailx::from, mailx_address_append(), mailx_append_attachment_arg(), mailx_free(), mailx_init_default_values(), mailx_parse_smtp_option(), mailx_send(), mailx::port, mailx::server, SMTP_ADDRESS_FROM, SMTP_ADDRESS_TO, smtp_ffile_get_contents(), and mailx::subject.

498  {
499  int rc;
500  int i;
501  struct mailx mailx;
502 
504 
505  while((rc = getopt(argc, argv, "a:s:S:")) != -1){
506  switch(rc){
507  case 'a':
509  break;
510  case 's':
511  mailx.subject = optarg;
512  break;
513  case 'S':
514  mailx_parse_smtp_option(&mailx, optarg);
515  break;
516  default:
517  return 1;
518  }
519  }
520  argc -= optind;
521  argv += optind;
522 
523  if(argc < 1){
524  errx(1, "must provide at least one email destination address");
525  }
526 
527  if(mailx.from == NULL){
528  errx(1, "must provide a FROM address");
529  }
530 
531  if(mailx.server == NULL){
532  if((mailx.server = strdup("localhost")) == NULL){
533  err(1, "strdup");
534  }
535  }
536 
537  if(mailx.port == NULL){
538  if((mailx.port = strdup("25")) == NULL){
539  err(1, "strdup");
540  }
541  }
542 
543  puts("Reading email body from stdin");
544  if((mailx.body = smtp_ffile_get_contents(stdin, NULL)) == NULL){
545  err(1, "failed to read email body from stdin");
546  }
547 
549 
550  for(i = 0; i < argc; i++){
552  }
553 
554  mailx_send(&mailx);
555  mailx_free(&mailx);
556  return 0;
557 }
const char * subject
Definition: mailx.c:71
char * from
Definition: mailx.c:101
static void mailx_parse_smtp_option(struct mailx *const mailx, const char *const option)
Definition: mailx.c:330
char * body
Definition: mailx.c:76
char * port
Definition: mailx.c:86
Definition: mailx.c:62
static void mailx_address_append(struct mailx *const mailx, enum smtp_address_type address_type, const char *const email)
Definition: mailx.c:200
static void mailx_free(const struct mailx *const mailx)
Definition: mailx.c:452
char * server
Definition: mailx.c:81
static void mailx_init_default_values(struct mailx *const mailx)
Definition: mailx.c:439
static void mailx_append_attachment_arg(struct mailx *const mailx, const char *const attach_arg)
Definition: mailx.c:303
static void mailx_send(struct mailx *const mailx)
Definition: mailx.c:225
static char * smtp_ffile_get_contents(FILE *stream, size_t *bytes_read)
Definition: mailx.c:153
+ Here is the call graph for this function:

◆ smtp_ffile_get_contents()

static char* smtp_ffile_get_contents ( FILE *  stream,
size_t *  bytes_read 
)
static

Read the entire contents of a file stream and store the data into a dynamically allocated buffer.

Parameters
[in]streamFile stream already opened by the caller.
[out]bytes_readNumber of bytes stored in the return buffer.
Return values
char*A dynamically allocated buffer which contains the entire contents of stream. The caller must free this memory when done.
NULLMemory allocation or file read error.

Definition at line 153 of file mailx.c.

References ferror, and realloc.

Referenced by main().

154  {
155  char *read_buf;
156  size_t bufsz;
157  char *new_buf;
158  const size_t BUFSZ_INCREMENT = 512;
159 
160  read_buf = NULL;
161  bufsz = 0;
162 
163  if(bytes_read){
164  *bytes_read = 0;
165  }
166 
167  do{
168  size_t bytes_read_loop;
169  if((new_buf = realloc(read_buf, bufsz + BUFSZ_INCREMENT)) == NULL){
170  free(read_buf);
171  return NULL;
172  }
173  read_buf = new_buf;
174  bufsz += BUFSZ_INCREMENT;
175 
176  bytes_read_loop = fread(&read_buf[bufsz - BUFSZ_INCREMENT],
177  sizeof(char),
178  BUFSZ_INCREMENT,
179  stream);
180  if(bytes_read){
181  *bytes_read += bytes_read_loop;
182  }
183  if(ferror(stream)){
184  free(read_buf);
185  return NULL;
186  }
187  } while(!feof(stream));
188 
189  return read_buf;
190 }
#define ferror
Definition: seams.h:113
#define realloc
Definition: seams.h:161
+ Here is the caller graph for this function: