Code Monkey home page Code Monkey logo

smtp-client's Introduction

smtp-client

This is an SMTP client library written in C which can get included directly into another program.

This library has been released into the public domain using CC0.

Official repository location: www.somnisoft.com/smtp-client

Feature list

  • C89
  • Cross-platform (POSIX, BSD, MacOS, Windows)
  • Send attachments
  • Send custom email headers
  • Specify multiple TO, CC, and BCC recipients
  • Simple API and simple error handling (see Examples section below)
  • Optional OpenSSL TLS connection and authentication methods
  • Test cases with 100% code and branch coverage
  • Doxygen with 100% documentation including the test code
  • Free software (permissive - CC0)

Supports the following connection methods:

  • No encryption
  • STARTTLS (requires OpenSSL)
  • TLS direct connection (requires OpenSSL)

Supports the following authentication methods:

  • none
  • PLAIN
  • LOGIN
  • CRAM-MD5 (requires OpenSSL)

To include the library into your application, simply copy the src/smtp.h and src/smtp.c files into your project directory. Then include the smtp.h header into your C file, compile smtp.c, and include the resulting object file into the build system.

Examples

The following example code demonstrates how to use the library.

#include <stdio.h>
#include "smtp.h"
#define MAIL_SERVER              "mail.example.com"
#define MAIL_PORT                "587"
#define MAIL_CONNECTION_SECURITY SMTP_SECURITY_STARTTLS
#define MAIL_FLAGS               (SMTP_DEBUG         | \
                                  SMTP_NO_CERT_VERIFY) /* Do not verify cert. */
#define MAIL_CAFILE              NULL
#define MAIL_AUTH                SMTP_AUTH_PLAIN
#define MAIL_USER                "[email protected]"
#define MAIL_PASS                "password"
#define MAIL_FROM                "[email protected]"
#define MAIL_FROM_NAME           "From Name"
#define MAIL_SUBJECT             "Subject Line"
#define MAIL_BODY                "Email Body"
#define MAIL_TO                  "[email protected]"
#define MAIL_TO_NAME             "To Name"
int main(void)
{
  struct smtp *smtp;
  int rc;
  rc = smtp_open(MAIL_SERVER,
                 MAIL_PORT,
                 MAIL_CONNECTION_SECURITY,
                 MAIL_FLAGS,
                 MAIL_CAFILE,
                 &smtp);
  rc = smtp_auth(smtp,
                 MAIL_AUTH,
                 MAIL_USER,
                 MAIL_PASS);
  rc = smtp_address_add(smtp,
                        SMTP_ADDRESS_FROM,
                        MAIL_FROM,
                        MAIL_FROM_NAME);
  rc = smtp_address_add(smtp,
                        SMTP_ADDRESS_TO,
                        MAIL_TO,
                        MAIL_TO_NAME);
  rc = smtp_header_add(smtp,
                       "Subject",
                       MAIL_SUBJECT);
  rc = smtp_attachment_add_mem(smtp,
                               "test.txt",
                               "Test email attachment.",
                               -1);
  rc = smtp_mail(smtp,
                 MAIL_BODY);
  rc = smtp_close(smtp);
  if(rc != SMTP_STATUS_OK){
    fprintf(stderr, "smtp failed: %s\n", smtp_status_code_errstr(rc));
    return 1;
  }
  return 0;
}

Place the code snippet above into a file named 'test.c' and change each #define to the appropriate values for your mail server. Then copy smtp.c and smtp.h into the same directory and run the following commands to compile with OpenSSL support.

cc -DSMTP_OPENSSL smtp.c -c -o smtp.o

cc -DSMTP_OPENSSL test.c -c -o test.o

cc test.o smtp.o -o smtp_test -lssl -lcrypto

If you do not need OpenSSL support, remove the -DSMTP_OPENSSL and the -lssl and -lcrypto arguments. The commands as above should create an executable called 'smtp_test' which can send a test email using the specified mail server.

A minimum amount of error checking has been included. Note that in the above example, the program checks for an error at the end of the mail session after calling smtp_close. We can do this because if an error occurs in any of the prior functions, the error will continue to propagate through any future function calls until either closing the SMTP context using smtp_close or by resetting the error condition using smtp_status_code_clear.

The following example demonstrates how to send an HTML email by overriding the Content-Type header. When overriding this header, any attachments added using the smtp_attachment_add* functions will get ignored. The application must generate the appropriate MIME sections (if needed) when overriding this header.

#include <stdio.h>
#include "smtp.h"
#define MAIL_SERVER              "localhost"
#define MAIL_PORT                "25"
#define MAIL_CONNECTION_SECURITY SMTP_SECURITY_NONE
#define MAIL_FLAGS               SMTP_DEBUG
#define MAIL_CAFILE              NULL
#define MAIL_AUTH                SMTP_AUTH_NONE
#define MAIL_USER                "[email protected]"
#define MAIL_PASS                "password"
#define MAIL_FROM                "[email protected]"
#define MAIL_FROM_NAME           "From Name"
#define MAIL_SUBJECT             "Subject Line"
#define MAIL_TO                  "[email protected]"
#define MAIL_TO_NAME             "To Name"
int main(void){
  struct smtp *smtp;
  enum smtp_status_code rc;
  const char *const email_body =
  "<html>\n"
  " <head><title>HTML Email</title></head>\n"
  " <body>\n"
  "  <h1>H1</h1>\n"
  "  <h2>H2</h1>\n"
  "  <h3>H3</h1>\n"
  "  <h4>H4</h1>\n"
  "  <h5>H5</h1>\n"
  "  <h6>H6</h1>\n"
  " </body>\n"
  "</html>\n";
  smtp_open(MAIL_SERVER,
            MAIL_PORT,
            MAIL_CONNECTION_SECURITY,
            MAIL_FLAGS,
            MAIL_CAFILE,
            &smtp);
  smtp_auth(smtp,
            MAIL_AUTH,
            MAIL_USER,
            MAIL_PASS);
  smtp_address_add(smtp,
                   SMTP_ADDRESS_FROM,
                   MAIL_FROM,
                   MAIL_FROM_NAME);
  smtp_address_add(smtp,
                   SMTP_ADDRESS_TO,
                   MAIL_TO,
                   MAIL_TO_NAME);
  smtp_header_add(smtp,
                  "Subject",
                  MAIL_SUBJECT);
  smtp_header_add(smtp,
                  "Content-Type",
                  "text/html");
  smtp_mail(smtp, email_body);
  rc = smtp_close(smtp);
  if(rc != SMTP_STATUS_OK){
    fprintf(stderr, "smtp failed: %s\n", smtp_status_code_errstr(rc));
    return 1;
  }
  return 0;
}

Technical Documentation

See the Technical Documentation generated from Doxygen.

smtp-client's People

Contributors

somnisoft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

smtp-client's Issues

Incorrect SMTPUTF8 support

The behavior of SMTPUTF8 support of the library isn't compliant with RFC 6531.

If one of recipients uses i18n email address, the smtp connection will fail.

sample code (based on example_sample.c):

#include <stdio.h>
#include "smtp.h"
#define MAIL_SERVER              "localhost"
#define MAIL_PORT                "25"
#define MAIL_CONNECTION_SECURITY SMTP_SECURITY_NONE
#define MAIL_FLAGS               (SMTP_DEBUG         | \
                                  SMTP_NO_CERT_VERIFY) /* Do not verify cert. */
#define MAIL_CAFILE              NULL
#define MAIL_AUTH                SMTP_AUTH_PLAIN
#define MAIL_USER                "[email protected]"
#define MAIL_PASS                "password"
#define MAIL_FROM                "[email protected]"
#define MAIL_FROM_NAME           "Kingsley"
#define MAIL_SUBJECT             "Test for SMTPUTF8"
#define MAIL_BODY                "This is a test text."
#define MAIL_TO                  "绲斤铐@example.com"
#define MAIL_TO_NAME             "Admin"
int main(void)
{
  struct smtp *smtp;
  int rc;
  rc = smtp_open(MAIL_SERVER,
                 MAIL_PORT,
                 MAIL_CONNECTION_SECURITY,
                 MAIL_FLAGS,
                 MAIL_CAFILE,
                 &smtp);
  rc = smtp_address_add(smtp,
                        SMTP_ADDRESS_FROM,
                        MAIL_FROM,
                        MAIL_FROM_NAME);
  rc = smtp_address_add(smtp,
                        SMTP_ADDRESS_TO,
                        MAIL_TO,
                        MAIL_TO_NAME);
  rc = smtp_header_add(smtp,
                       "Subject",
                       MAIL_SUBJECT);
  rc = smtp_mail(smtp,
                 MAIL_BODY);
  rc = smtp_close(smtp);
  if(rc != SMTP_STATUS_OK){
    fprintf(stderr, "smtp failed: %s\n", smtp_status_code_errstr(rc));
    return 1;
  }
  return 0;
}

Postfix v3.6 is running background and if we ran the test, we have following smtp logs

[smtp Server]: 220 temporary.example.com ESMTP Postfix
[smtp Client]: EHLO smtp
[smtp Server]: 250-temporary.example.com
[smtp Server]: 250-PIPELINING
[smtp Server]: 250-SIZE 10240000
[smtp Server]: 250-VRFY
[smtp Server]: 250-ETRN
[smtp Server]: 250-STARTTLS
[smtp Server]: 250-ENHANCEDSTATUSCODES
[smtp Server]: 250-8BITMIME
[smtp Server]: 250-DSN
[smtp Server]: 250-SMTPUTF8
[smtp Server]: 250 CHUNKING
[smtp Client]: MAIL FROM:<[email protected]>
[smtp Server]: 250 2.1.0 Ok
[smtp Client]: RCPT TO:<绲斤铐@example.com> SMTPUTF8            <== (1)
[smtp Server]: 555 5.5.4 Unsupported option: SMTPUTF8          <== (2)
[smtp Client]: DATA
[smtp Server]: 554 5.5.1 Error: no valid recipients
[smtp Client]: QUIT
smtp failed: SMTP server sent back an unexpected status code
  1. RFC 6531 allows SMTPUTF8 option only in command MAIL, VRFY and EXPN, see https://datatracker.ietf.org/doc/html/rfc6531#section-3.1
  2. Postfix supports SMTPUTF8 option in MAIL and VRFY commands https://www.postfix.org/SMTPUTF8_README.html

Neither of them allows using the option in RCPT TO; thats why the Postfix would complain the unsupported option in response.


I checked the implementation and found the root cause of this issue is in the function smtp_mail_envelope_header(): the lib should append SMTPUTF8 only in MAIL FROM.

Maybe we need to maintain a boolean to indicate if there is a need to use UTF-8 support because any non-ascii address used in sender or recipients would require UTF-8 support.

Add an image in email content

Hi,

Your library is working great and I am very happy with it.
I am trying to do more complicated things like adding an image to the content of the email.

I found this example online.
Can we do something like this?
Especially the bold part onwards.
Thanks Andreas

To: [email protected]
Subject: ...
Content-Type: multipart/related;
boundary="------------090303020209010600070908"

This is a multi-part message in MIME format.
--------------090303020209010600070908
Content-Type: text/html; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-15">
</head>
<body bgcolor="#ffffff" text="#000000">
<img src="cid:part1.06090408.01060107" alt="">
</body>
</html>

--------------090303020209010600070908
Content-Type: image/png;
name="moz-screenshot.png"

Content-Transfer-Encoding: base64
Content-ID: <part1.06090408.01060107>
Content-Disposition: inline;
filename="moz-screenshot.png"

doxygen compilation issues

System is ubuntu 20.04, though I am not sure its important.

After ./configure, make stops with an error when doxygen reports that there is no @return for functions that are defined as static void and no @param for functions with void parameter. If you add those, clang reports these as an error later in the process. Problem is resolved (i.e. compilation is accomplished) when WARN_AS_ERROR is changed from YES to NO in doc.cfg. I'm not really great with this stuff, so I am reporting this in case there is a more elegant way (i.e. a different flag to be set in doc.cfg) so it isn't expecting @return & @param for voids. Great work!

Compiling for Windows.

Can you please give some instructions about compiling for windows using Visual Studio??

Thanks

Corrupted pdf attachments

Hi there,

This is a great smtp-client and thanks :)
I have been trying to add .pdf attachments to the email.
I am using this:
smtp_attachment_add_path(this->smtp, "test.pdf", "source\test.pdf");

If I try to attach a ".txt" file it works great but doing it with a pdf the emails are being sent but the attached pdfs are corrupted and cannot open.

Thanks,
Andreas

ERR_load_BIO_strings' is deprecated

Thanks for this piece of code, so good!
However, I've found some deprecation warnings when compiling on OSX with openssl 3.0.5, installed with brew:

gcc -DSMTP_OPENSSL -I/usr/local/Cellar/openssl@3/3.0.5/include smtp/smtp.c -c -o smtp.o
smtp/smtp.c:1757:3: warning: 'ERR_load_BIO_strings' is deprecated [-Wdeprecated-declarations]
  ERR_load_BIO_strings();
  ^
/usr/local/Cellar/openssl@3/3.0.5/include/openssl/cryptoerr_legacy.h:31:1: note: 'ERR_load_BIO_strings' has been explicitly marked deprecated here
OSSL_DEPRECATEDIN_3_0 int ERR_load_BIO_strings(void);
^
/usr/local/Cellar/openssl@3/3.0.5/include/openssl/macros.h:182:49: note: expanded from macro 'OSSL_DEPRECATEDIN_3_0'
#   define OSSL_DEPRECATEDIN_3_0                OSSL_DEPRECATED(3.0)
                                                ^
/usr/local/Cellar/openssl@3/3.0.5/include/openssl/macros.h:62:52: note: expanded from macro 'OSSL_DEPRECATED'
#     define OSSL_DEPRECATED(since) __attribute__((deprecated))
                                                   ^
1 warning generated.

Content-Type header overwritten

I tried setting the Content-Type header with smtp_header_add(sender->smtp, "Content-Type", "text/html"); but it seems that the Content-Type header is overwritten inside smtp_print_mime_header_and_body.

authentication problem

smtp_auth was failing for me. When I traced through the smtp_parse_cmd_line calls in a debugger, I saw several 250 lines like "250-CHUNKING", and finally "250 HELP". Since that does not have a hyphen, cmd->more is set to 0, so we never look at the next line. When I forced cmd->more = 1, I found that the next line was "235 Authentication succeeded". So I tried a hack at the end of smtp_parse_cmd_line:

if ( (cmd->code == SMTP_DONE) && (0 == strcmp( cmd->text, "HELP" )) )
{
    cmd->more = 1;
}

Then everything seemed to work. I don't know if this makes any sense in terms of what the protocol is supposed to be doing.

Warnings when compiling smtp.c with -O2

On Ubuntu 18.04 x86_64 witg gcc 7.3.0 I get this:

cc -O2 -DSMTP_OPENSSL smtp.c -c -o smtp.o

smtp.c: In function ‘smtp_mail’:
smtp.c:1882:47: warning: ‘%0+5ld’ directive writing between 5 and 19 bytes into a region of size 15 [-Wformat-overflow=]
"%s, %02d %s %d %02d:%02d:%02d %0+5ld",
^~~~~~
smtp.c:1882:16: note: directive argument in the range [-256204778801521500, 256204778801521500]
"%s, %02d %s %d %02d:%02d:%02d %0+5ld",
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/stdio.h:862:0,
from /usr/include/openssl/bio.h:16,
from smtp.c:54:
/usr/include/x86_64-linux-gnu/bits/stdio2.h:33:10: note: ‘__builtin___sprintf_chk’ output 23 or more bytes (assuming 37) into a destination of size 32
return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__bos (__s), __fmt, __va_arg_pack ());

Compilation flags on rasbperry pi

On raspberry pi gcc has version 6.3.0 20170516 (Raspbian 6.3.0-18+rpi1+deb9u1)
and flags -Wrestrict -Wduplicated-branches -Wstringop-overflow=4 are not recognized.

gcc: error: unrecognized command line option ‘-Wduplicated-branches’; did you mean ‘-Wduplicated-cond’?
gcc: error: unrecognized command line option ‘-Wrestrict’; did you mean ‘-mrestrict-it’?
gcc: error: unrecognized command line option ‘-Wstringop-overflow=4’; did you mean ‘-Wstrict-overflow=’?

connect(...) blocks for quite a while

I'm calling smtp_open with these parameters:

smtp_open(
    "smtpout.secureserver.net",
    "25",
    SMTP_SECURITY_STARTTLS,
    (smtp_flag) (SMTP_DEBUG | SMTP_NO_CERT_VERIFY),
    0,
    &sender->smtp
);

Eventually it returns successfully but more often than not it takes 2 minutes or so. I did some digging to find out where exactly in the code it gets stuck and it is in connect(smtp->sock, res->ai_addr, res->ai_addrlen) inside smtp_connect.

Based on my experience using the python smtp client library I'm guessing the connection can be faster (even instantaneous), but I'm not a socket guy so I don't know exactly how.

Edit: I don't know if this is related or not, but the blocking happens especially if I just smtp_close'd the client. It also happens if it is the first time opening the connection, but less often. Maybe it is waiting for the last connection to the server to be closed properly? I don't know, just throwing ideas.

Microsoft compiler warning C4389 using warning level 4

I had to disable warning warning C4389: '==': signed/unsigned mismatch
around the macro FD_SET.

This is the new code to disable this warning.

#ifdef SMTP_IS_WINDOWS
#pragma warning( push )
#pragma warning( disable : 4389 ) //warning C4389: '==': signed/unsigned mismatch
FD_SET(smtp->sock, &readfds);
#pragma warning( pop )
#else
FD_SET(smtp->sock, &readfds);
#endif

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.