# Send email with c++ on FreeBSD



## alinciolte (Dec 31, 2015)

Can you tell me a code to implement in a c++ core that send backup/system information to a mail / gmail account?Those should be done on FreBSD 9.2+


----------



## obsigna (Dec 31, 2015)

Here comes a simple SMTP sender that I wrote some years ago. It is in C, however it should be easily possible to make this into a sub-routine of a C++ project. The right to freely use it on your own risk with your project is granted herewith.


```
//  SMTPSend.c
//
//  Created by Dr. Rolf Jansen on 2011-05-04.
//  Copyright 2011-2014 Dr. Rolf Jansen. All rights reserved.
//
// cc SMTPSend.c -std=c99 -Os -o SMTPSend

// Standard C includes
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>

// header files which must be included for
// high level DNS services and socket connections
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// constants
#define defaultBufferSize  4096


short doSMTPTransaction(int smtpSocket, char *sendBuffer, long sendBufferSize, char *receiveBuffer)
{
   long receiveSize = 0;

   if (sendBuffer && send(smtpSocket, sendBuffer, sendBufferSize, 0) == -1)
      return 1000;

   else if (receiveBuffer && (receiveSize = recv(smtpSocket, receiveBuffer, defaultBufferSize - 1, 0)) < 0)
      return 2000;

   if (receiveSize != 0)
   {
      receiveBuffer[receiveSize] = '\0';
      return strtol(receiveBuffer, NULL, 10);
   }

   else
      return 0;
}


// argv[1]:    fully qualified sender address
// argv[2..n]: fully qualified recipient addresses
int main(int argc, const char *argv[])
{
   int    i, smtpCode = 0, resultCode = 1;
   long   smtpSocket, sendBufferSize;
   char   c = 0;
   char   transferBuffer[defaultBufferSize];

   struct sockaddr_in  address;
   struct hostent     *host = NULL;

   char  *smtpRelayHostName = "smtp.example.com";                          // replace with the IP or address of the relay server
   char  *smtpAuthLoginID   = "YXVzZXJpZEBleGFtcGxlLmNvbQo=\r\n";          // replace with your base 64 encoded SMTP login ID
   char  *smtpAuthPassword  = "dGhlbW9zdHNlY3JldHBhc3N3b3JkZXZlcgo=\r\n";  // replace with your base 64 encoded SMTP auth password

   if (!inet_aton(smtpRelayHostName, &address.sin_addr))
      if (host = gethostbyname(smtpRelayHostName))
         address.sin_addr = *(struct in_addr*)host->h_addr;
      else
         goto finish;

   address.sin_port   = htons(587);
   address.sin_family = AF_INET;

   if ((smtpSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)
   {
      if (connect(smtpSocket, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) != -1)
      {
         if ((smtpCode = doSMTPTransaction(smtpSocket, NULL, 0, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "EHLO %s\r\n", argv[1]);
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = sprintf(transferBuffer, "AUTH LOGIN\r\n");
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 334)
            goto closeSession;

         if ((smtpCode = doSMTPTransaction(smtpSocket, smtpAuthLoginID, strlen(smtpAuthLoginID), transferBuffer)) != 334)
            goto closeSession;

         if ((smtpCode = doSMTPTransaction(smtpSocket, smtpAuthPassword, strlen(smtpAuthPassword), transferBuffer)) != 235)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "MAIL FROM:<%s>\r\n", argv[1]);
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;

         for (i = 2; i < argc; i++)
         {
            sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "RCPT TO:<%s>\r\n", argv[ i ]);
            if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)
               goto closeSession;
         }

         sendBufferSize = sprintf(transferBuffer, "DATA\r\n");
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) != 354)
            goto closeSession;

         while (c != EOF)
         {
            for (i = 0; i < defaultBufferSize && (c = fgetc(stdin)) != EOF; i++)
               transferBuffer[ i ] = c;
            smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, i, NULL);
         }

         sendBufferSize = sprintf(transferBuffer, "\r\n.\r\nQUIT\r\n");
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) == 250);
            resultCode = 0;

      closeSession:
         close(smtpSocket);
      }
   }

finish:
   if (resultCode)
      syslog(LOG_ERR, "SMTPSend from %s to %s failed.", argv[1], argv[2]);

   return resultCode;
}
```


PS: Need to fix two array indexes that were spoiled because of the use of BB coding for the formatting of the forum messages. Namely index i in sqaure brackets is interpreted as italic formatting BB tag, while in the above code it was meant to be index i of the respective C array. I resolved this by putting spaces around the i, which is still valid C syntax.


----------



## alinciolte (Dec 31, 2015)

obsigna said:


> Here comes a simple SMTP sender that I wrote some years ago. It is in C, however it should be easily possible to make this into a sub-routine of a C++ project. The right to freely use it on your own risk with your project is granted herewith.
> 
> 
> ```
> ...


 Thx man, I will test and came back with a reply


----------



## obsigna (Dec 31, 2015)

alinciolte said:


> Thx man, I will test and came back with a reply


Wait a moment, I just realized, that by copying and pasting my code into the forum message editor, all the square brackets with i as the array index were removed. Please use the file, that I attached to this message.

(remove the .txt extension, the forum does not allow .c-files to be uploaded)


----------



## alinciolte (Dec 31, 2015)

obsigna said:


> Wait a moment, I just realized, that by copying and pasting my code into the forum message editor, all the square brackets with i as the array index were removed. Please use the file, that I attached to this message.
> 
> (remove the .txt extension, the forum does not allow .c-files to be uploaded)


Thx again.Wheel the good news are that if I rename mail in mainMail ( to not have duplicate main definition) I get the core.Now the problem is how I can use it to send files/information at 6 hours to my mail?

and also for username/pw you added to both of them at the end of encode of base 64  "\r\n" that doesn't affect the decodind,so I should add that too?


----------



## obsigna (Dec 31, 2015)

alinciolte said:


> Thx again.Wheel the good news are that if I rename mail in mainMail ( to not have duplicate main definition) I get the core.Now the problem is how I can use it to send files/information at 6 hours to my mail?



In the case of the standalone tool, the usage would be:
`./SMTPsend sender@example.com receiver@example.com < email-body.txt`

Command line parameter 1 should be the fully qualified sender address, and parameter 2 should be the fully qualified address of the receiver. The tool reads then the email body that it should send from standard input. In the above case the file email-body.txt is loaded into stdin of the tool.

In order to adapt this behaviour for a subroutine, you might want to pass the fully qualified addresses as  const char * parameters and for example you could read the email-body from file and not from stdin.


```
int mainMail(const char *senderMailAddress, const char *receiverMailAddress)
{
...
...
FILE *mail_body_file = fopen("/path/to/email-body.txt", "r");
...
         while (c != EOF)
         {
            for (i = 0; i < defaultBufferSize && (c = fgetc(mail_body_file)) != EOF; i++)
               transferBuffer[ i ] = c;
            smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, i, NULL);
         }

...
   fclose(mail_body_file);
...
}
```

With that in place you would replace every instance of argv[1] with senderMailAddress, and every instance of argv[1] with receiverMailAddress. You could also pass the mail body as a char array and its size to the function doSMTPTransaction():

```
...
int mainMail(const char *senderMailAddress, const char *receiverMailAddress, const char *mailBody, int mailBodySize)
{
...
         smtpCode = doSMTPTransaction(smtpSocket, mailBody, mailBodySize, NULL);
```



alinciolte said:


> ... and also for username/pw you added to both of them at the end of encode of base 64  "\r\n" that doesn't affect the decodind,so I should add that too?



Yes, that's the way how it works. The \r\n is part of the mail transfer protocol, und in order to facilitate things, I appended it directly to the content before passing in to transaction function.

On FreeBSD you might want to install the tool converters/base64 from the ports. With that it is easy to encode the required parameters (don't forget the -n switch in the echo command):

Username: `echo -n "mailadmin@example.com" | base64 -e` --> bWFpbGFkbWluQGV4YW1wbGUuY29t
Password: `echo -n "a_totally_secret_password" | base64 -e` --> YV90b3RhbGx5X3NlY3JldF9wYXNzd29yZA==

Note, this must be the credentials of the user who is allowed to submit emails to the SMTP server. Note also, that the LOGIN authentication is not encrypted and many SMTP servers allow unsafe authentication on TLS connections only. If this is the case, you would need to turn the plain socket into a SSL one.


----------



## alinciolte (Jan 1, 2016)

Thanks a lot ,so I make some changes and came back with a feedback.Also how I can make this function to auto repeat every 6 hours?

Also why you defined
const char *senderMailAddress
as a pointer?A simple const char is not better?
Also I want the email to be sent from me to me,the user and pw that I put there are from FreeBSD host,not from gmail acc?


----------



## alinciolte (Jan 1, 2016)

The new code


```
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>

// header files which must be included for
// high level DNS services and socket connections
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// constants
#define defaultBufferSize  4096


short doSMTPTransaction(int smtpSocket, char *sendBuffer, long sendBufferSize, char *receiveBuffer)
{
   long receiveSize = 0;

   if (sendBuffer && send(smtpSocket, sendBuffer, sendBufferSize, 0) == -1)
      return 1000;

   else if (receiveBuffer && (receiveSize = recv(smtpSocket, receiveBuffer, defaultBufferSize - 1, 0)) < 0)
      return 2000;

   if (receiveSize != 0)
   {
      receiveBuffer[receiveSize] = '\0';
      return strtol(receiveBuffer, NULL, 10);
   }

   else
      return 0;
}


// argv[2..n]: fully qualified recipient addresses
int mainMail() //no need (int argc, const char *argv[]) 
{
   int    i, smtpCode = 0, resultCode = 1;
   long   smtpSocket, sendBufferSize;
   char   c = 0;
   char   transferBuffer[defaultBufferSize];
    const char *senderMailAddress="test@gmail.com";
   const char *receiverMailAddress="test@gmail.com";
   
   struct sockaddr_in  address;
   struct hostent     *host = NULL;

   char  *smtpRelayHostName = "mail.google.com";                          
   char  *smtpAuthLoginID   = "base64encoded\r\n"; 
   char  *smtpAuthPassword  = "base64encoded\r\n";    
   
    FILE *mail_body_file = fopen("/path/to/email-body.txt", "r");
    while (c != EOF)
    {
        for (i = 0; i < defaultBufferSize && (c = fgetc(mail_body_file)) != EOF; i++)
            transferBuffer[ i ] = c;
        smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, i, NULL);
    }
   
    fclose(mail_body_file);
   if (!inet_aton(smtpRelayHostName, &address.sin_addr))
      if (host = gethostbyname(smtpRelayHostName))
         address.sin_addr = *(struct in_addr*)host->h_addr;
    //  else
       //  goto finish;

   address.sin_port   = htons(587);
   address.sin_family = AF_INET;

   if ((smtpSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)
   {
      if (connect(smtpSocket, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) != -1)
      {
         if ((smtpCode = doSMTPTransaction(smtpSocket, NULL, 0, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "EHLO %s\r\n", senderMailAddress);
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = sprintf(transferBuffer, "AUTH LOGIN\r\n");
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 334)
            goto closeSession;

         if ((smtpCode = doSMTPTransaction(smtpSocket, smtpAuthLoginID, strlen(smtpAuthLoginID), transferBuffer)) != 334)
            goto closeSession;

         if ((smtpCode = doSMTPTransaction(smtpSocket, smtpAuthPassword, strlen(smtpAuthPassword), transferBuffer)) != 235)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "MAIL FROM:<%s>\r\n", senderMailAddress);
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;
        sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "RCPT TO:<%s>\r\n", receiverMailAddress);
       
        if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;
        /*
         for (i = 3; i < argc; i++)
         {
            sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "RCPT TO:<%s>\r\n", argv[i]);
            if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) > 299)
               goto closeSession;
         }
*/
         sendBufferSize = sprintf(transferBuffer, "DATA\r\n");
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) != 354)
            goto closeSession;

         while (c != EOF)
         {
            for (i = 0; i < defaultBufferSize && (c = fgetc(stdin)) != EOF; i++)
               transferBuffer[i] = c;
            smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, i, NULL);
         }

         sendBufferSize = sprintf(transferBuffer, "\r\n.\r\nQUIT\r\n");
         if ((smtpCode = doSMTPTransaction(smtpSocket, transferBuffer, sendBufferSize, transferBuffer)) == 250);
            resultCode = 0;

      closeSession:
         close(smtpSocket);
      }
   }

//finish:
 //  if (resultCode)
    //     syslog(LOG_ERR, "SMTPSend from %s to %s failed.", senderMailAddress, argv[2]);

   return resultCode;
}

[/i][/i]
```
_

http://prntscr.com/9kv5dj_


----------



## obsigna (Jan 1, 2016)

alinciolte said:


> Thanks a lot ,so I make some changes and came back with a feedback.Also how I can make this function to auto repeat every 6 hours?


Are you really sure, that you want this SMTP sender being part of your C++ program. Why don't you simply use it as a standalone tool and add a line to /etc/crontab:

```
...
0     */6       *       *       *       root    /usr/local/bin/SMTPSend sender@gmail.com rcvr1@gmail.com rcvr2@gmail.com rcvr3@gmail.com < /path/to/email-body.txt
```
In this case your C++ program would write diagnostic messages to the file, and SMTPSend would be launched every 6 hours as a cronjob and would send the diagnostics to the receiver address(es).

If this must be part of your C++ program, then I would create a POSIX thread running an infinite loop, whose body would call the SMTPSend() function and then would go to sleep(3) for 6 hours.



alinciolte said:


> Also why you defined
> const char *senderMailAddress
> as a pointer? A simple const char is not better?



const char would be a single character, and this is definitely not what is needed here.



alinciolte said:


> Also I want the email to be sent from me to me, the user and pw that I put there are from FreeBSD host, not from gmail acc?



The user would be the fully qualified gmail address, and the password would be that of your gmail account. BTW, the GMAIL SMTP server requires TLS, and for this reason the plain SMTPSend tool does not work with gmail. See here: https://support.google.com/a/answer/176600?hl=en

Here comes the source code of a TLS enabled version of SMTPSend:

```
//  SMTPSend.c
//
//  Created by Dr. Rolf Jansen on 2011-05-04.
//  Copyright 2011-2016 Dr. Rolf Jansen. All rights reserved.
//
// cc SMTPSend.c -std=c11 -Os -g0 -Wno-parentheses -Wno-dangling-else -Wno-empty-body -lcrypto -lssl -o SMTPSend


// Standard C includes
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>

// header files which must be included for
// high level DNS services and socket connections
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// OpenSSL header
#include <openssl/ssl.h>


// constants
#define defaultBufferSize 65536


short tlsSMTPTransaction(SSL *ssl, char *sendBuffer, long sendBufferSize, char *receiveBuffer)
{
   long receiveSize = 0;

   if (sendBuffer && SSL_write(ssl, sendBuffer, sendBufferSize) < 0)
      return 1000;

   else if (receiveBuffer && (receiveSize = SSL_read(ssl, receiveBuffer, defaultBufferSize - 1)) < 0)
      return 2000;

   if (receiveSize != 0)
   {
      receiveBuffer[receiveSize] = '\0';
      return strtol(receiveBuffer, NULL, 10);
   }

   else
      return 0;
}


// argv[1]:    fully qualified sender address
// argv[2..n]: fully qualified recipient addresses
int main(int argc, const char *argv[])
{
   int    k, smtpCode = 0, resultCode = 1;
   long   smtpSocket, sendBufferSize;
   char   c = 0;
   char   transferBuffer[defaultBufferSize];

   struct sockaddr_in  address;
   struct hostent     *host = NULL;

   char  *smtpRelayHostName = "smtp.gmail.com";                // replace with the IP or address of the relay server
   char  *smtpAuthLoginID   = "base64LoginID\r\n";             // replace with your base 64 encoded SMTP login ID
   char  *smtpAuthPassword  = "base64Password\r\n";            // replace with your base 64 encoded SMTP auth password

   if (!inet_aton(smtpRelayHostName, &address.sin_addr))
      if (host = gethostbyname(smtpRelayHostName))
         address.sin_addr = *(struct in_addr*)host->h_addr;
      else
         goto finish;

   address.sin_port   = htons(465);
   address.sin_family = AF_INET;

   if ((smtpSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)
   {
      if (connect(smtpSocket, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) != -1)
      {
         // TLS preparation
         SSL_library_init();
         OpenSSL_add_all_algorithms();
         SSL_load_error_strings();
         SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_client_method());
         if (!ctx)
            goto closeSocket;

         SSL *ssl = SSL_new(ctx);
         SSL_set_fd(ssl, smtpSocket);
         if (SSL_connect(ssl) == -1)
            goto closeSession;

         if ((smtpCode = tlsSMTPTransaction(ssl, NULL, 0, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "EHLO %s\r\n", argv[1]);
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = sprintf(transferBuffer, "AUTH LOGIN\r\n");
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) > 334)
            goto closeSession;

         if ((smtpCode = tlsSMTPTransaction(ssl, smtpAuthLoginID, strlen(smtpAuthLoginID), transferBuffer)) != 334)
            goto closeSession;

         if ((smtpCode = tlsSMTPTransaction(ssl, smtpAuthPassword, strlen(smtpAuthPassword), transferBuffer)) != 235)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "MAIL FROM:<%s>\r\n", argv[1]);
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;

         for (k = 2; k < argc; k++)
         {
            sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "RCPT TO:<%s>\r\n", argv[k]);
            if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) > 299)
               goto closeSession;
         }

         sendBufferSize = sprintf(transferBuffer, "DATA\r\n");
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) != 354)
            goto closeSession;

         while (c != EOF)
         {
            for (k = 0; k < defaultBufferSize && (c = fgetc(stdin)) != EOF; k++)
               transferBuffer[k] = c;
            smtpCode = tlsSMTPTransaction(ssl, transferBuffer, k, NULL);
         }

         sendBufferSize = sprintf(transferBuffer, "\r\n.\r\nQUIT\r\n");
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) == 250);
            resultCode = 0;


      closeSession:
         SSL_free(ssl);
         SSL_CTX_free(ctx);

      closeSocket:
         close(smtpSocket);
      }
   }

finish:
   if (resultCode)
      syslog(LOG_ERR, "SMTPSend from %s to %s failed.", argv[1], argv[2]);

   return resultCode;
}
```


----------



## alinciolte (Jan 2, 2016)

obsigna said:


> Are you really sure, that you want this SMTP sender being part of your C++ program. Why don't you simply use it as a standalone tool and add a line to /etc/crontab:
> 
> ```
> ...
> ...


Thanks again for your time and support,I want it to run inside a game core so I can push the logs directly to email and would be more easy and I could have more options inside than outside,for this reason I wanted to know if for that  TLS enabled version of SMTPSend code I can just say from begin that the sender/receiver is me and the function just to be like 
tlsSMTPTransaction(logs)


----------



## obsigna (Jan 2, 2016)

alinciolte said:


> ... I wanted to know if for that  TLS enabled version of SMTPSend code I can just say from begin that the sender/receiver is me ...


Yes!



alinciolte said:


> ... and the function just to be like
> tlsSMTPTransaction(logs)


No, you must not omit the minimum SMTP protocol sequence consisting of EHLO, AUTH LOGIN, MAIL FROM:, RCPT TO:, DATA. Your log message shall come only after a successful DATA transaction.

A reasonably capable C/C++ programmer should be able to turn the source code of the SMTPSend standalone tool into a SMTPSend() function.

Spoon feeding is not a suggested habbit on this forum.


----------



## Juha Nurmela (Jan 2, 2016)

Advertisement for the "new" MSG_NOSIGNAL flag of send(2).

Juha


----------



## obsigna (Jan 2, 2016)

Juha Nurmela said:


> Advertisement for the "new" MSG_NOSIGNAL flag of send(2).


Interesting!

If it is a matter of concern, I do:

```
int optval = 1;
setsockopt(sockdesc, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(int));
```
IMHO, the underlying issue affects mainly server code, because, you don't want your server being brought to DOS by a misbehaving client. However, for the client code of the simple SMTPSend tool, I cannot see how this could be useful. If sending fails for some reason, the tool should quit anyway, since there is not much that can be done to recover from such a situation.

In addition, the TLS enabled version of SMTPSend does not use send(2) anymore, but SSL_write(3).


----------



## Juha Nurmela (Jan 2, 2016)

*SO_NOSIGPIPE*. Excellent, thanks, a new one in turn  I did not mean this particular program would need to protect itself, just said it as a general remark.

There's an extra *;* at the end, btw, when checking the final response.

Juha


----------



## alinciolte (Jan 3, 2016)

So mainMail(sender@gmail.com,rcvr1@gmail.com);  is the called function and should work? I need only a call example and is enough I guess

http://prntscr.com/9lncfo


----------



## obsigna (Jan 3, 2016)

alinciolte said:


> So mainMail(sender@gmail.com,rcvr1@gmail.com);  is the called function and should work? I need only a call example and is enough I guess
> 
> http://prntscr.com/9lncfo



Here comes everything you need:

```
//  SMTPSend.c
//
//  Created by Dr. Rolf Jansen on 2011-05-04.
//  Copyright 2011-2016 Dr. Rolf Jansen. All rights reserved.
//
// cc SMTPSend.c -std=c11 -Os -g0 -Wno-parentheses -Wno-dangling-else -Wno-empty-body -I/usr/local/include -L/usr/local/lib -lcrypto -lssl -o SMTPSend


// Standard C includes
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <time.h>
#include <sys/stat.h>

// header files which must be included for
// high level DNS services and socket connections
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// OpenSSL headers
#include <openssl/ssl.h>


// constants
#define defaultBufferSize 65536


int tlsSMTPTransaction(SSL *ssl, const char *sendBuffer, int sendBufferSize, char *receiveBuffer)
{
   long receiveSize = 0;

   if (sendBuffer && SSL_write(ssl, sendBuffer, sendBufferSize) <= 0)
      return 1000;

   else if (receiveBuffer && (receiveSize = SSL_read(ssl, receiveBuffer, defaultBufferSize - 1)) <= 0)
      return 2000;

   if (receiveSize)
   {
      receiveBuffer[receiveSize] = '\0';
      return strtol(receiveBuffer, NULL, 10);
   }

   else
      return 0;
}


int SMTPSend(const char *sender,
             const char *receiver,
             const char *subject,
             const char *message,
             int     messageSize)
{
   int    smtpCode = 0, resultCode = 1;
   int    smtpSocket, sendBufferSize;
   char   transferBuffer[defaultBufferSize];

   struct sockaddr_in  address;
   struct hostent     *host = NULL;

   char  *smtpRelayHostName = "smtp.gmail.com";             // replace with the IP or address of the relay server
   char  *smtpAuthLoginID   = "dXNlckBnbWFpbC5jb20=\r\n";   // user@gmail.com  -- replace with your base 64 encoded SMTP login ID
   char  *smtpAuthPassword  = "U2VjcmV0IFBhc3N3b3Jk\r\n";   // Secret Password -- replace with your base 64 encoded SMTP auth password

   if (!inet_aton(smtpRelayHostName, &address.sin_addr))
      if (host = gethostbyname(smtpRelayHostName))
         address.sin_addr = *(struct in_addr*)host->h_addr;
      else
         goto finish;

   address.sin_port   = htons(465);
   address.sin_family = AF_INET;

   if ((smtpSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)
   {
      if (connect(smtpSocket, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) != -1)
      {
         // TLS preparation
         SSL_library_init();
         OpenSSL_add_all_algorithms();
         SSL_load_error_strings();
         SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_client_method());
         if (!ctx)
            goto closeSocket;

         SSL *ssl = SSL_new(ctx);
         SSL_set_fd(ssl, smtpSocket);
         if (SSL_connect(ssl) == -1)
            goto closeSession;

         if ((smtpCode = tlsSMTPTransaction(ssl, NULL, 0, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "EHLO %s\r\n", sender);
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = sprintf(transferBuffer, "AUTH LOGIN\r\n");
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) > 334)
            goto closeSession;

         if ((smtpCode = tlsSMTPTransaction(ssl, smtpAuthLoginID, strlen(smtpAuthLoginID), transferBuffer)) != 334)
            goto closeSession;

         if ((smtpCode = tlsSMTPTransaction(ssl, smtpAuthPassword, strlen(smtpAuthPassword), transferBuffer)) != 235)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "MAIL FROM:<%s>\r\n", sender);
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "RCPT TO:<%s>\r\n", receiver);
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) > 299)
            goto closeSession;

         sendBufferSize = sprintf(transferBuffer, "DATA\r\n");
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) != 354)
            goto closeSession;

         time_t tod = time(NULL);
         sendBufferSize = snprintf(transferBuffer, defaultBufferSize, "From: %s\nTo: %s\nDate: %sSubject: %s\n\n", sender, receiver, ctime(&tod), subject);
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, NULL)) == 1000)
            goto closeSession;

         if ((smtpCode = tlsSMTPTransaction(ssl, message, messageSize, NULL)) == 1000)
            goto closeSession;

         sendBufferSize = sprintf(transferBuffer, "\r\n.\r\nQUIT\r\n");
         if ((smtpCode = tlsSMTPTransaction(ssl, transferBuffer, sendBufferSize, transferBuffer)) == 250);
            resultCode = 0;

      closeSession:
         SSL_free(ssl);
         SSL_CTX_free(ctx);

      closeSocket:
         close(smtpSocket);
      }
   }

finish:
   if (resultCode)
      syslog(LOG_ERR, "SMTPSend from %s to %s failed.", sender, receiver);

   return resultCode;
}


int main(int argc, const char *argv[])
{
   int rc = 1;

   struct stat st;
   if (stat(argv[1], &st) == 0)
   {
      FILE *logfile = fopen(argv[1], "r");
      if (logfile)
      {
         char *message = malloc(st.st_size + 1);
         if (message)
         {
            if (fread(message, st.st_size, 1, logfile) == 1)
            {
               // sanity measure, add a trailing nul at the very end of the message
               message[st.st_size] = '\0';

               //             sender address   receiver address      subject                   message size
               rc = SMTPSend("user@gmail.com", "user@gmail.com", "This is a Test.", message, (int)st.st_size);
            }

            free(message);
         }

         fclose(logfile);
      }
   }

   return rc;
}
```


----------



## alinciolte (Jan 4, 2016)

This looks huge better but I have some questions about the main because I can't add that code in my MAIN main cuz this return a function call and mine return always 1

http://prntscr.com/9lvftb
Not good http://prntscr.com/9lvg9g ?

Edit: also I removed " rc ="

http://prntscr.com/9lvvp7
Seems that ssl.h is not found or doesn't have that part


----------



## obsigna (Jan 4, 2016)

main() was *ONLY* meant as the requested *EXAMPLE* on how to use the SMTPSend() function.
--> Remove that main() function completely!


TLSv1_2_client_method() was introduced by OpenSSL 1.0.x.
--> On line 90 replace this with TLSv1_client_method()!


----------



## alinciolte (Jan 4, 2016)

I think that I need to update some libraries
http://prntscr.com/9ly0p8


----------



## obsigna (Jan 4, 2016)

alinciolte said:


> I think that I need to update some libraries
> http://prntscr.com/9ly0p8


No, this comes because you try to compile C code as C++ code, and in cases, C++ exaggerates the nit-picking.

in front of lines 69, 70, and 71 add the specifier const,
so the type declarations read const char *


on lines 90, 94, and 128 remove the type specifiers before the variable declarations,
but leave the assignment (remainder of the line) intact, namely remove:
line 90:   SSL_ctx *
line 94:   SSL *
line 128: time_t


before line 86, add the now missing variable declarations:

```
SSL_CTX *ctx;
         SSL     *ssl;
         time_t   tod;

         // TLS preparation
         ...
```


----------



## alinciolte (Jan 4, 2016)

I also updated the ssl and I got this finnaly


```
main.o: In function `tlsSMTPTransaction(ssl_st*, char const*, int, char*)':
Mail.cpp:96: multiple definition of `tlsSMTPTransaction(ssl_st*, char const*, int, char*)'
Mail.cpp:96: first defined here
main.o: In function `SMTPSend(char const*, char const*, char const*, char const*, int)':
Mail.cpp:121: multiple definition of `SMTPSend(char const*, char const*, char const*, char const*, int)'
Mail.cpp:121: first defined here
Mail.o: In function `tlsSMTPTransaction(ssl_st*, char const*, int, char*)':
Mail.cpp:(.text+0x2f): undefined reference to `SSL_write'
Mail.cpp:(.text+0x52): undefined reference to `SSL_read'
Mail.o: In function `SMTPSend(char const*, char const*, char const*, char const*, int)':
Mail.cpp:(.text+0x16d): undefined reference to `SSL_library_init'
Mail.cpp:(.text+0x177): undefined reference to `OpenSSL_add_all_algorithms'
Mail.cpp:(.text+0x17c): undefined reference to `SSL_load_error_strings'
Mail.cpp:(.text+0x181): undefined reference to `TLSv1_2_client_method'
Mail.cpp:(.text+0x189): undefined reference to `SSL_CTX_new'
Mail.cpp:(.text+0x19b): undefined reference to `SSL_new'
Mail.cpp:(.text+0x1a9): undefined reference to `SSL_set_fd'
Mail.cpp:(.text+0x1b1): undefined reference to `SSL_connect'
Mail.cpp:(.text+0x1ec): undefined reference to `SSL_free'
Mail.cpp:(.text+0x1fa): undefined reference to `SSL_CTX_free'
main.o: In function `tlsSMTPTransaction(ssl_st*, char const*, int, char*)':
main.cpp:(.text+0x73f): undefined reference to `SSL_write'
main.cpp:(.text+0x762): undefined reference to `SSL_read'
main.o: In function `SMTPSend(char const*, char const*, char const*, char const*, int)':
main.cpp:(.text+0x87d): undefined reference to `SSL_library_init'
main.cpp:(.text+0x887): undefined reference to `OpenSSL_add_all_algorithms'
main.cpp:(.text+0x88c): undefined reference to `SSL_load_error_strings'
main.cpp:(.text+0x891): undefined reference to `TLSv1_2_client_method'
main.cpp:(.text+0x899): undefined reference to `SSL_CTX_new'
main.cpp:(.text+0x8ab): undefined reference to `SSL_new'
main.cpp:(.text+0x8b9): undefined reference to `SSL_set_fd'
main.cpp:(.text+0x8c1): undefined reference to `SSL_connect'
main.cpp:(.text+0x8fc): undefined reference to `SSL_free'
main.cpp:(.text+0x90a): undefined reference to `SSL_CTX_free'
```


----------



## obsigna (Jan 4, 2016)

`cc Main.cpp -Wno-parentheses -Wno-dangling-else -Wno-empty-body -I/usr/local/include -L/usr/local/lib -lcrypto -lssl -o SMTPSend`


----------

