Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
richwalm committed Dec 31, 2014
1 parent 34b56fb commit 1e38f3e
Show file tree
Hide file tree
Showing 8 changed files with 1,379 additions and 0 deletions.
73 changes: 73 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
Simple SMTP Mailer
Written by Richard Walmsley <richwalm@gmail.com>
WWW: http://walmsley.gen.nz/

Description
===========
A simple API used to send out e-mails. It supports attachments but lacking authentication as it wasn't required for my use.
It's Windows only due to the use of its DNS lookup functions, although outside of those, it should be easy to port but as there's far more advance tools out there, it's not worth the use.
No tested but should be thread-safe.

Included an example.

API Usage
=========
All functions return SMTP_ERR_SUCCESS (0) on success.

The error codes are as follows;
* SMTP_ERR_INVALID_STATE - Function called without a successful call to a prerequisite function.
* SMTP_ERR_FAILURE - Server returned a non-success code or unable to connect to server.
* SMTP_ERR_BUFFER - Out of memory or static buffer too small. The latter shouldn't happen with a valid e-mail address.
* SMTP_ERR_PROTOCOL - Protocol error. Server not following the spec or there's a transfer error.
* SMTP_ERR_DATA - The data passed to the function is invalid.

The socket used has a receive timeout of 15 seconds. This can be adjusted by defining SMTP_BLOCKING_TIME before including ssmtp.h. The value is in milliseconds.

int SMTPConnect(SMTPConn *Conn, const char *Domain, const char *HeloLine);
--------------------------------------------------------------------------
Attempts to connects to the most suitable mail server.

Before calling this function for the first use of a SMTPConn, ensure that SMTPConn->State is set to 0 otherwise the function may fail with SMTP_ERR_INVALID_STATE. It only needs to be set once per SMTPConn as it is then handled internally.

This looks up the MX records for the domain passed and then attempts to connect to them sorted by their preference. If one fails, it'll continue to the next one. If none of them work, it'll try to connect to the server's A records as per the spec.

Once connected, it'll send through the HELO line using the passed string. If the server returns an unsuccessful code, it'll disconnect and continue through the list.

int SMTPAddress(SMTPConn *Conn, int Type, const char *Address);
---------------------------------------------------------------
Adds a single e-mail address to the buffer.

The types are as follows;
* SMTP_ADDRESS_FROM
* SMTP_ADDRESS_TO
* SMTP_ADDRESS_CC
* SMTP_ADDRESS_BCC

There's no limit other then the available memory. BCC addresses aren't included into its buffer.
The senders' address always needs to be sent first otherwise the function will fail with SMTP_ERR_INVALID_STATE.

The address can be in either format; 'test@example.org' or '"Testing Account" <test@example.org>'.
There is no checking to see if an address has been added twice.

int SMTPData(SMTPConn *Conn, const char *Subject, const char *Body, SMTPAttach *Attachments);
---------------------------------------------------------------------------------------------
Sends off the e-mail.

Before calling this, we must have called SMTPAddress() at least twice returning successful. First with a sender and then again with a receiver.

The subject line is optional. If Attachments is NULL, the e-mail will be a standard e-mail, without MIME.
See the included example for further infomation on attachments.

int SMTPReset(SMTPConn *Conn);
------------------------------
Sends the SMTP RSET command. This clears any addresses that may have been added to the buffer. As if starting with a fresh connection.

int SMTPDisconnect(SMTPConn *Conn);
-----------------------------------
Disconnects from the mail server.

This will free any memory used by the address buffer. The SMTPConn can then be reused.

License
=======
Distributed under the MIT License. See the included LICENSE for details.
106 changes: 106 additions & 0 deletions base64.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
An error-free in-memory Base64 encoder.
Simple SMTP Mailer.
Copyright (C) 2013 Richard Walmsley <richwalm@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <string.h> /* For memcpy() */

#include "base64.h"

static const char B64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static void EncodeBlock(unsigned char In[BASE64_IN_SIZE], unsigned char Out[BASE64_OUT_SIZE], unsigned int Length)
{
unsigned int Index;

for (Index = Length; Index < BASE64_IN_SIZE; Index++)
In[Index] = 0;

Out[0] = B64[In[0] >> 2];
Out[1] = B64[((In[0] & 0x03) << 4) | ((In[1] & 0xF0) >> 4)];
Out[2] = (unsigned char)(Length > 1 ? B64[((In[1] & 0x0F) << 2) | ((In[2] & 0xC0) >> 6)] : '=');
Out[3] = (unsigned char)(Length > 2 ? B64[In[2] & 0x3F] : '=');

return;
}

void InitEncode64(B64Stream *Stream)
{
Stream->AvailIn = Stream->AvailOut = \
Stream->TotalIn = Stream->TotalOut = \
Stream->BlockSize = Stream->BlockOut = 0;

return;
}

void Encode64(B64Stream *Stream, int Finished)
{
unsigned char OutBlock[BASE64_OUT_SIZE];

/* Loop until we have no more input or unable to output. */
while (Stream->AvailIn != 0 || Stream->BlockSize != 0) {

/* If anything is in our block for output, dump it. */
if (Stream->BlockOut && Stream->BlockSize != 0) {

for (; Stream->BlockSize != 0; Stream->BlockSize--) {

if (Stream->AvailOut == 0)
return;

*Stream->NextOut = Stream->Block[BASE64_OUT_SIZE - Stream->BlockSize];
Stream->NextOut++;
Stream->TotalOut++;
Stream->AvailOut--;
}

}

/* Now that our buffer is empty, fill the input. */
Stream->BlockOut = 0;
for (; Stream->BlockSize < BASE64_IN_SIZE; Stream->BlockSize++) {

/* Out of input. This may be expected if there is no data left. */
if (Stream->AvailIn == 0) {
if (!Finished || Stream->BlockSize == 0)
return;
break;
}

Stream->Block[Stream->BlockSize] = *Stream->NextIn;
Stream->NextIn++;
Stream->TotalIn++;
Stream->AvailIn--;
}

/* Encode the input we have. */
EncodeBlock(Stream->Block, OutBlock, Stream->BlockSize);

Stream->BlockSize = BASE64_OUT_SIZE;
memcpy(Stream->Block, OutBlock, Stream->BlockSize);

Stream->BlockOut = 1;
}

return;
}
25 changes: 25 additions & 0 deletions base64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef BASE64_H
#define BASE64_H

#define BASE64_OUT_SIZE 4
#define BASE64_IN_SIZE 3

typedef struct B64Stream {
unsigned int AvailIn;
unsigned int TotalIn;
unsigned char *NextIn;

unsigned int AvailOut;
unsigned int TotalOut;
char *NextOut;

/* Internal cache. */
unsigned int BlockSize;
int BlockOut; /* Direction of block. (Input or Output) */
unsigned char Block[BASE64_OUT_SIZE];
} B64Stream;

void InitEncode64(B64Stream *Stream);
void Encode64(B64Stream *Stream, int Finished);

#endif
113 changes: 113 additions & 0 deletions cbuffer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
Simple set of functions used to prevent sending small amounts of data.
Simple SMTP Mailer.
Copyright (C) 2013 Richard Walmsley <richwalm@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <string.h>
#include <stdarg.h>

#include "cbuffer.h"

int CFlush(CSendBuffer *Buffer)
{
int Return;

if (Buffer->Cursor > 0) {

Return = Buffer->Callback(Buffer->CallbackData, Buffer->Data, Buffer->Cursor);
if (Return != 0)
return Return;
Buffer->Cursor = 0;

}

return 0;
}

int CSend(CSendBuffer *Buffer, const char *Data, unsigned int Size)
{
unsigned int BufferAvailable;
int Return;

while (Size > 0) {

BufferAvailable = Buffer->Size - Buffer->Cursor;
if (BufferAvailable > Size)
BufferAvailable = Size;

memcpy(&Buffer->Data[Buffer->Cursor], Data, BufferAvailable);

Size -= BufferAvailable;
Data += BufferAvailable;

Buffer->Cursor += BufferAvailable;
if (Buffer->Cursor >= Buffer->Size) {

Return = Buffer->Callback(Buffer->CallbackData, Buffer->Data, Buffer->Cursor);
if (Return != 0)
return Return;
Buffer->Cursor = 0;
}

}

return 0;
}

int CSendStrings(CSendBuffer *Buffer, ...)
{
va_list Args;
char *Arg;
size_t Length;
int Return;

va_start(Args, Buffer);

Arg = va_arg(Args, char*);
while (Arg) {

Length = strlen(Arg);
Return = CSend(Buffer, Arg, Length);
if (Return != 0) {
va_end(Args);
return Return;
}
Arg = va_arg(Args, char*);

}

va_end(Args);
return 0;
}

void CInit(CSendBuffer *Buffer, char *Data, unsigned int Size, int (*Callback)(void *, char *, unsigned int), void *CallbackData)
{
Buffer->Data = Data;
Buffer->Size = Size;
Buffer->Cursor = 0;

Buffer->Callback = Callback;
Buffer->CallbackData = CallbackData;

return;
}
16 changes: 16 additions & 0 deletions cbuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef CBUFFER_H
#define CBUFFER_H

typedef struct CSendBuffer {
char *Data;
unsigned int Size, Cursor;
void *CallbackData;
int (*Callback)(void *, char *, unsigned int);
} CSendBuffer;

int CFlush(CSendBuffer *Buffer);
int CSend(CSendBuffer *Buffer, const char *Data, unsigned int Size);
int CSendStrings(CSendBuffer *Buffer, ...);
void CInit(CSendBuffer *Buffer, char *Data, unsigned int Size, int (*Callback)(void *, char *, unsigned int), void *CallbackData);

#endif
Loading

0 comments on commit 1e38f3e

Please sign in to comment.