How to send email from MySQL 5.1
Asked Answered
S

4

25

I'm trying to send an email to several recipients when a new row is inserted into a table. The list of recipients varies. I would like to be able to set this list using a select statement. I also have installed Navicat which allows me to send email notifications but only to a predetermined set of people.

Thanks.

Saidee answered 22/12, 2008 at 21:54 Comment(0)
A
27

I would be very concerned about putting the load of sending e-mails on my database server (small though it may be). I might suggest one of these alternatives:

  1. Have application logic detect the need to send an e-mail and send it.
  2. Have a MySQL trigger populate a table that queues up the e-mails to be sent and have a process monitor that table and send the e-mails.
Accelerator answered 22/12, 2008 at 22:2 Comment(5)
Thanks for the response! About option number 2, how would you implement it?Saidee
You create a table that can hold the email message you're sending (maybe columns for subject line and body; but maybe you'll just refer to "form email, id=2") and then you have a program that checks this table every so often for new rows, and emails out if it finds any.Peloria
Just putting it out there, but Microsoft has their Database Mail component which runs on the same server as the database (in normal scenarios at least). It's a different process/thread but on the same server. Just sayin'.Hygrometer
@colithium, That's what 99% of LAMP setups do as well. One server, everything on it.Sax
yep, and docker-compose, with absolutely everything running on one server : post-fix, dovecot, scala api, java api, c# api, apache, tomcat, nginx, node api, MySQL, python, mongodb, zabbix-agent, ssh, lolUpheaval
J
28

If you have an SMTP service running, you can outfile to the drop directory. If you have high volume, you may result with duplicate file names, but there are ways to avoid that.

Otherwise, you will need to create a UDF.

Here's a sample trigger solution:

CREATE TRIGGER test.autosendfromdrop BEFORE INSERT ON test.emaildrop
FOR EACH ROW BEGIN
      /* START THE WRITING OF THE EMAIL FILE HERE*/      
      SELECT  concat("To: ",NEW.To),
              concat("From: ",NEW.From),
              concat("Subject: ",NEW.Subject),
              NEW.Body
          INTO OUTFILE 
                   "C:\\inetpub\\mailroot\\pickup\\mail.txt" 
              FIELDS TERMINATED by '\r\n' ESCAPED BY '';            
END;

To markup the message body you will need something like this...

CREATE FUNCTION `HTMLBody`(Msg varchar(8192)) 
    RETURNS varchar(17408) CHARSET latin1 DETERMINISTIC
BEGIN
  declare tmpMsg varchar(17408);
  set tmpMsg = cast(concat(
      'Date: ',date_format(NOW(),'%e %b %Y %H:%i:%S -0600'),'\r\n',
      'MIME-Version: 1.0','\r\n',
      'Content-Type: multipart/alternative;','\r\n',
      ' boundary=\"----=_NextPart_000_0000_01CA4B3F.8C263EE0\"','\r\n',
      'Content-Class: urn:content-classes:message','\r\n',
      'Importance: normal','\r\n',
      'Priority: normal','\r\n','','\r\n','','\r\n',
      'This is a multi-part message in MIME format.','\r\n','','\r\n',
      '------=_NextPart_000_0000_01CA4B3F.8C263EE0','\r\n',
      'Content-Type: text/plain;','\r\n',
      '  charset=\"iso-8859-1\"','\r\n',
      'Content-Transfer-Encoding: 7bit','\r\n','','\r\n','','\r\n',
      Msg,
      '\r\n','','\r\n','','\r\n',
      '------=_NextPart_000_0000_01CA4B3F.8C263EE0','\r\n',
      'Content-Type: text/html','\r\n',
      'Content-Transfer-Encoding: 7bit','\r\n','','\r\n',
      Msg,
      '\r\n','------=_NextPart_000_0000_01CA4B3F.8C263EE0--'
      ) as char);
  RETURN tmpMsg;
END ;
Josi answered 4/11, 2010 at 21:57 Comment(0)
A
27

I would be very concerned about putting the load of sending e-mails on my database server (small though it may be). I might suggest one of these alternatives:

  1. Have application logic detect the need to send an e-mail and send it.
  2. Have a MySQL trigger populate a table that queues up the e-mails to be sent and have a process monitor that table and send the e-mails.
Accelerator answered 22/12, 2008 at 22:2 Comment(5)
Thanks for the response! About option number 2, how would you implement it?Saidee
You create a table that can hold the email message you're sending (maybe columns for subject line and body; but maybe you'll just refer to "form email, id=2") and then you have a program that checks this table every so often for new rows, and emails out if it finds any.Peloria
Just putting it out there, but Microsoft has their Database Mail component which runs on the same server as the database (in normal scenarios at least). It's a different process/thread but on the same server. Just sayin'.Hygrometer
@colithium, That's what 99% of LAMP setups do as well. One server, everything on it.Sax
yep, and docker-compose, with absolutely everything running on one server : post-fix, dovecot, scala api, java api, c# api, apache, tomcat, nginx, node api, MySQL, python, mongodb, zabbix-agent, ssh, lolUpheaval
R
12

I agree with Jim Blizard. The database is not the part of your technology stack that should send emails. For example, what if you send an email but then roll back the change that triggered that email? You can't take the email back.

It's better to send the email in your application code layer, after your app has confirmed that the SQL change was made successfully and committed.

Restful answered 22/12, 2008 at 22:12 Comment(5)
One tricky scenario is how to notify by email when an action occurs as a result of cascading updates, or by a change made inside a trigger.Restful
@BillKarwin please let me know if you have found the solution to your comment, I would be interested in knowing this.Countercharge
@Harbir, I'm still (six years later) of the opinion that the database should not send emails. You should do it in your application after the database change is committed. One strategy for triggered actions is for the trigger to insert a row into a table called SendEmailTo (for example), and then your app reads that table after committing the work. If there's a row in that table, send an email and delete the row.Restful
@BillKarwin How would this be done? This happens to be exactly what I'm looking for (get an update when a specific table is updated). Do you have any examples?Auxiliary
@webfrogs, try #18707290Restful
I
0

If you have vps or dedicated server, You can code your own module using C programming.

para.h

/* 
 * File:   para.h
 * Author: rahul
 *
 * Created on 10 February, 2016, 11:24 AM
 */

#ifndef PARA_H
#define  PARA_H

#ifdef  __cplusplus
extern "C" {
#endif


#define From "<[email protected]>"
#define To "<[email protected]>" 
#define From_header "Rahul<[email protected]>"   
#define TO_header "Mini<[email protected]>"   
#define UID "smtp server account ID"
#define PWD "smtp server account PWD"
#define domain "dfgdfgdfg.com"


#ifdef  __cplusplus
}
#endif

#endif  
/* PARA_H */

main.c

/* 
 * File:   main.c
 * Author: rahul
 *
 * Created on 10 February, 2016, 10:29 AM
 */
#include <my_global.h>
#include <mysql.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "time.h"
#include "para.h"

/*
 * 
 */

my_bool SendEmail_init(UDF_INIT *initid,UDF_ARGS *arg,char *message);
void SendEmail_deinit(UDF_INIT *initid __attribute__((unused)));
char* SendEmail(UDF_INIT *initid, UDF_ARGS *arg,char *result,unsigned long *length, char *is_null,char* error);

/*
 * base64
 */
int Base64encode_len(int len);
int Base64encode(char * coded_dst, const char *plain_src,int len_plain_src);

int Base64decode_len(const char * coded_src);
int Base64decode(char * plain_dst, const char *coded_src);

/* aaaack but it's fast and const should make it shared text page. */
static const unsigned char pr2six[256] =
{
    /* ASCII table */
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};

int Base64decode_len(const char *bufcoded)
{
    int nbytesdecoded;
    register const unsigned char *bufin;
    register int nprbytes;

    bufin = (const unsigned char *) bufcoded;
    while (pr2six[*(bufin++)] <= 63);

    nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
    nbytesdecoded = ((nprbytes + 3) / 4) * 3;

    return nbytesdecoded + 1;
}

int Base64decode(char *bufplain, const char *bufcoded)
{
    int nbytesdecoded;
    register const unsigned char *bufin;
    register unsigned char *bufout;
    register int nprbytes;

    bufin = (const unsigned char *) bufcoded;
    while (pr2six[*(bufin++)] <= 63);
    nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
    nbytesdecoded = ((nprbytes + 3) / 4) * 3;

    bufout = (unsigned char *) bufplain;
    bufin = (const unsigned char *) bufcoded;

    while (nprbytes > 4) {
    *(bufout++) =
        (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
    *(bufout++) =
        (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
    *(bufout++) =
        (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
    bufin += 4;
    nprbytes -= 4;
    }

    /* Note: (nprbytes == 1) would be an error, so just ingore that case */
    if (nprbytes > 1) {
    *(bufout++) =
        (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
    }
    if (nprbytes > 2) {
    *(bufout++) =
        (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
    }
    if (nprbytes > 3) {
    *(bufout++) =
        (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
    }

    *(bufout++) = '\0';
    nbytesdecoded -= (4 - nprbytes) & 3;
    return nbytesdecoded;
}

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

int Base64encode_len(int len)
{
    return ((len + 2) / 3 * 4) + 1;
}

int Base64encode(char *encoded, const char *string, int len)
{
    int i;
    char *p;

    p = encoded;
    for (i = 0; i < len - 2; i += 3) {
    *p++ = basis_64[(string[i] >> 2) & 0x3F];
    *p++ = basis_64[((string[i] & 0x3) << 4) |
                    ((int) (string[i + 1] & 0xF0) >> 4)];
    *p++ = basis_64[((string[i + 1] & 0xF) << 2) |
                    ((int) (string[i + 2] & 0xC0) >> 6)];
    *p++ = basis_64[string[i + 2] & 0x3F];
    }
    if (i < len) {
    *p++ = basis_64[(string[i] >> 2) & 0x3F];
    if (i == (len - 1)) {
        *p++ = basis_64[((string[i] & 0x3) << 4)];
        *p++ = '=';
    }
    else {
        *p++ = basis_64[((string[i] & 0x3) << 4) |
                        ((int) (string[i + 1] & 0xF0) >> 4)];
        *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
    }
    *p++ = '=';
    }

    *p++ = '\0';
    return p - encoded;
}

/*
 end of base64
 */

const char* GetIPAddress(const char* target_domain) {
    const char* target_ip;
    struct in_addr *host_address;
    struct hostent *raw_list = gethostbyname(target_domain);
    int i = 0;
    for (i; raw_list->h_addr_list[i] != 0; i++) {
        host_address = raw_list->h_addr_list[i];
        target_ip = inet_ntoa(*host_address);
    }
    return target_ip;
}

char * MailHeader(const char* from, const char* to, const char* subject, const char* mime_type, const char* charset) {

    time_t now;
    time(&now);
    char *app_brand = "Codevlog Test APP";
    char* mail_header = NULL;
    char date_buff[26];
    char Branding[6 + strlen(date_buff) + 2 + 10 + strlen(app_brand) + 1 + 1];
    char Sender[6 + strlen(from) + 1 + 1];
    char Recip[4 + strlen(to) + 1 + 1];
    char Subject[8 + 1 + strlen(subject) + 1 + 1];
    char mime_data[13 + 1 + 3 + 1 + 1 + 13 + 1 + strlen(mime_type) + 1 + 1 + 8 + strlen(charset) + 1 + 1 + 2];

    strftime(date_buff, (33), "%a , %d %b %Y %H:%M:%S", localtime(&now));

    sprintf(Branding, "DATE: %s\r\nX-Mailer: %s\r\n", date_buff, app_brand);
    sprintf(Sender, "FROM: %s\r\n", from);
    sprintf(Recip, "To: %s\r\n", to);
    sprintf(Subject, "Subject: %s\r\n", subject);
    sprintf(mime_data, "MIME-Version: 1.0\r\nContent-type: %s; charset=%s\r\n\r\n", mime_type, charset);

    int mail_header_length = strlen(Branding) + strlen(Sender) + strlen(Recip) + strlen(Subject) + strlen(mime_data) + 10;

    mail_header = (char*) malloc(mail_header_length);

    memcpy(&mail_header[0], &Branding, strlen(Branding));
    memcpy(&mail_header[0 + strlen(Branding)], &Sender, strlen(Sender));
    memcpy(&mail_header[0 + strlen(Branding) + strlen(Sender)], &Recip, strlen(Recip));
    memcpy(&mail_header[0 + strlen(Branding) + strlen(Sender) + strlen(Recip)], &Subject, strlen(Subject));
    memcpy(&mail_header[0 + strlen(Branding) + strlen(Sender) + strlen(Recip) + strlen(Subject)], &mime_data, strlen(mime_data));
    return mail_header;
}

my_bool SendEmail_init(UDF_INIT *initid,UDF_ARGS *arg,char *message){
     if (!(arg->arg_count == 2)) {
        strcpy(message, "Expected two arguments");
        return 1;
    }

    arg->arg_type[0] = STRING_RESULT;// smtp server address
    arg->arg_type[1] = STRING_RESULT;// email body
    initid->ptr = (char*) malloc(2050 * sizeof (char));
    memset(initid->ptr, '\0', sizeof (initid->ptr));
    return 0;
}

void SendEmail_deinit(UDF_INIT *initid __attribute__((unused))){
    if (initid->ptr) {
        free(initid->ptr);
    }
}

char* SendEmail(UDF_INIT *initid, UDF_ARGS *arg,char *result,unsigned long *length, char *is_null,char* error){
   char *header = MailHeader(From_header, TO_header, "Hello Its a test Mail from Codevlog", "text/plain", "US-ASCII");
    int connected_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof (addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(25);
    if (inet_pton(AF_INET, GetIPAddress(arg->args[0]), &addr.sin_addr) == 1) {
        connect(connected_fd, (struct sockaddr*) &addr, sizeof (addr));
    }
    if (connected_fd != -1) {
        int recvd = 0;
        const char recv_buff[4768];
        int sdsd;
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);
        recvd += sdsd;

        char buff[1000];
        strcpy(buff, "EHLO "); //"EHLO sdfsdfsdf.com\r\n"
        strcat(buff, domain);
        strcat(buff, "\r\n");
        send(connected_fd, buff, strlen(buff), 0);
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);
        recvd += sdsd;

        char _cmd2[1000];
        strcpy(_cmd2, "AUTH LOGIN\r\n");
        int dfdf = send(connected_fd, _cmd2, strlen(_cmd2), 0);
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);
        recvd += sdsd;

        char _cmd3[1000];
        Base64encode(&_cmd3, UID, strlen(UID));
        strcat(_cmd3, "\r\n");
        send(connected_fd, _cmd3, strlen(_cmd3), 0);
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);
        recvd += sdsd;

        char _cmd4[1000];
        Base64encode(&_cmd4, PWD, strlen(PWD));
        strcat(_cmd4, "\r\n");
        send(connected_fd, _cmd4, strlen(_cmd4), 0);
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);
        recvd += sdsd;

        char _cmd5[1000];
        strcpy(_cmd5, "MAIL FROM: ");
        strcat(_cmd5, From);
        strcat(_cmd5, "\r\n");
        send(connected_fd, _cmd5, strlen(_cmd5), 0);
        char skip[1000];
        sdsd = recv(connected_fd, skip, sizeof (skip), 0);

        char _cmd6[1000];
        strcpy(_cmd6, "RCPT TO: ");
        strcat(_cmd6, To); //
        strcat(_cmd6, "\r\n");
        send(connected_fd, _cmd6, strlen(_cmd6), 0);
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);
        recvd += sdsd;

        char _cmd7[1000];
        strcpy(_cmd7, "DATA\r\n");
        send(connected_fd, _cmd7, strlen(_cmd7), 0);
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);
        recvd += sdsd;

        send(connected_fd, header, strlen(header), 0);
        send(connected_fd, arg->args[1], strlen(arg->args[1]), 0);
        char _cmd9[1000];
        strcpy(_cmd9, "\r\n.\r\n.");
        send(connected_fd, _cmd9, sizeof (_cmd9), 0);
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);
        recvd += sdsd;

        char _cmd10[1000];
        strcpy(_cmd10, "QUIT\r\n");
        send(connected_fd, _cmd10, sizeof (_cmd10), 0);
        sdsd = recv(connected_fd, recv_buff + recvd, sizeof (recv_buff) - recvd, 0);

        memcpy(initid->ptr, recv_buff, strlen(recv_buff));
        *length = recvd;
    }
    free(header);
    close(connected_fd);
    return initid->ptr;
}

To configure your project go through this video: https://www.youtube.com/watch?v=Zm2pKTW5z98 (Send Email from MySQL on Linux) It will work for any mysql version (5.5, 5.6, 5.7)

I will resolve if any error appear in above code, Just Inform in comment

Indistinguishable answered 4/3, 2016 at 1:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.