Libmail

    -- Sebastian Pawlak, 2005.


Zaprezentowana funkcja umożliwia wysyłanie e-maila według protokołu SMTP. Obsługiwane jest logowanie typu: LOGIN, PLAIN i DIGEST-MD5. Do działania wymaga plików md5.c i md5.h dostępnych w internecie.


Kod źródłowy pliku "libmail.h":

/* libmail: biblioteka z funkcja wysylania e-maila SMTP (RFC821)
 *
 * Sebastian Pawlak, 2005.
 */

#ifndef _LIB_MAIL_H_
#define _LIB_MAIL_H_

#define DEBUG

int sendEmail(const char *smtpIP, int port,
              const char *domain, const char *from, const char *to,
              const char *subject, const char *body,
              const char *auth, const char *login, const char *passwd);

#endif

Kod źródłowy pliku "libmail.c":

/* libmail: biblioteka z funkcja wysylania e-maila SMTP (RFC821)
 *
 * Obsluguje logowanie: PLAIN, LOGIN, DIGEST-MD5
 *
 * Wymaga plikow md5.c i md5.h autorstwa L. Peter Deutsch
 *
 * Sebastian Pawlak, 2005.
 */

#include <stdio.h>
#include <stdlib.h>       /* dla strtol() */
#include <fcntl.h>        /* dla O_RDONLY */
#include <ctype.h>        /* dla isdigit() */
#include <unistd.h>       /* dla close() */
#include <arpa/inet.h>    /* dla inet_pton() */
#include <sys/socket.h>   /* dla socket(), recv(), connect(), AF_INET, ... */ 
#include <setjmp.h>       /* dla sigsetjmp(), siglongjmp() */
#include <sys/time.h>     /* dla setitimer(), ITIMER_REAL, struct itimerval */
#include <sys/wait.h>     /* dla SIGALR */
#include <errno.h>
#include "md5.h"
#include "libmail.h"

enum {
    STATE_WAIT_220 = 0,          /* oczekiwanie na pierwszy pakiet od serwera */
    STATE_SEND_HELO,             /* wyslanie HELO */
    STATE_WAIT_HELO_ANSWER,      /* oczekiwanie na odpowiedz serwera na HELO */
    STATE_SEND_AUTH,             /* wyslanie AUTH */
    STATE_WAIT_AUTH_ANSWER,      /* oczekiwanie na odpowiedz serwera na AUTH */
    STATE_SEND_LOGIN,            /* wyslanie loginu */
    STATE_WAIT_LOGIN_ANSWER,     /* oczekiwanie na odpowiedz serwera na login */
    STATE_SEND_PASSWD,           /* wyslanie hasla */
    STATE_WAIT_PASSWD_ANSWER,    /* oczekiwanie na odpowiedz serwera na haslo */
    STATE_SEND_MAIL,             /* wyslanie MAIL */
    STATE_WAIT_MAIL_ANSWER,      /* oczekiwanie na odpowiedz serwera na MAIL */
    STATE_SEND_RCPT,             /* wyslanie RCPT */
    STATE_WAIT_RCPT_ANSWER,      /* oczekiwanie na odpowiedz serwera na PCPT */
    STATE_SEND_DATA,             /* wyslanie DATA */
    STATE_WAIT_DATA_ANSWER,      /* oczekiwanie na odpowiedz serwera na DATA */
    STATE_SEND_DATA_BODY,        /* wyslanie tresci e-maila */
    STATE_WAIT_DATA_BODY_ANSWER, /* oczekiwanie na odpowiedz serwera na '.' */
    STATE_SEND_QUIT,             /* wyslanie QUIT */
    STATE_WAIT_QUIT_ANSWER,      /* oczekiwanie na odpowiedz serwera na QUIT */

    MAX_LEN = 256,

    DEFAULT_CONNECT_TIMEOUT = 5,    /* timeout wyslania e-maila */
};


void closeConnection(int sockfd, int state, const char *answer);
int recvData(int sockfd, int *number, int *firstDigit, char *answer);
int sendData(int sockfd, const char *data);
int connectSlave(const char *ip, int port);
void encodeBase64(const unsigned char *s, unsigned char *t, int len);
void decodeBase64(const unsigned char *s, unsigned char *t, int len);
int makeDigestMD5response(const char *s, char *t,
                          const char *login, const char *passwd);


static sigjmp_buf jmpBuf;
static sig_atomic_t jmpOk = 0;
                                                                                
/* connectTimeoutHandler: na potrzeby obslugi timeout'u dla funkcji sendEmail()
 */
static void connectTimeoutHandler(int sig)
{
    if (jmpOk == 0)
        return;
                                                                                
     siglongjmp(jmpBuf, 1);
}


/* sendEmail: wysyla e-mail do serwera SMTP
 *            umozliwia autoryzacje LOGIN, PLAIN, DIGEST-MD5
 */
int sendEmail(const char *smtpIP, int port,
              const char *domain, const char *from, const char *to,
              const char *subject, const char *body,
              const char *auth, const char *login, const char *passwd)
{
    struct sigaction act, oldact;
    struct itimerval timval;
    int sockfd, state, number, firstDigit;
    char s[1024];
    char answer[MAX_LEN] = "\0";
    int ret = 0;

    errno = 0;
    
    /* jesli e-mail nie zostanie przeslany w pewnym czasie, przerywamy */
    jmpOk = 0;
    memset(&act, 0, sizeof (act));
    act.sa_handler = &connectTimeoutHandler;
    sigaction(SIGALRM, &act, &oldact);
                                                                                
    timval.it_interval.tv_usec = 0;
    timval.it_interval.tv_sec = 0;
    timval.it_value.tv_usec = 0;
    timval.it_value.tv_sec = DEFAULT_CONNECT_TIMEOUT;
    setitimer(ITIMER_REAL, &timval, 0);
                                                                                
    if (sigsetjmp(jmpBuf, 1) == 0) {
        jmpOk = 1;

        /* tworzenie polaczenia */
        if ((sockfd = connectSlave(smtpIP, port)) < 0) {
            fprintf(stderr,"Problem z nawiazaniem polaczenia "
                           "z serwerem SMTP!\n");
            ret = -1;
        }


        /* wysylanie e-maila, automat stanow */
        state = STATE_WAIT_220;

        while (ret == 0) {
    
            #ifdef DEBUG
            fprintf(stdout, "stan: %d\n", state);
            #endif

            switch (state) {
                case STATE_WAIT_220:    
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 220)
                            state = STATE_SEND_HELO;
                        else if (number == 554) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
                    break;

                case STATE_SEND_HELO:
                    sprintf(s, "HELO %s\r\n", domain);

                    if (sendData(sockfd, s) == 0)
                        state = STATE_WAIT_HELO_ANSWER;
                    break;

                case STATE_WAIT_HELO_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 250)
                            state = STATE_SEND_AUTH;
                        else if (firstDigit == 5) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
                    break;

                case STATE_SEND_AUTH:
                    if (auth[0] == '\0') {
                        #ifdef DEBUG
                        fprintf(stdout, "Bez logowania\n");
                        #endif

                        state = STATE_SEND_MAIL;
                        break;
                    }

                    if (!strcmp(auth, "PLAIN")) {
                        char t[256];

                        sprintf(s, "%s %s %s", login, login, passwd);
                        s[strlen(login)] = '\0';
                        s[2 * strlen(login) + 1] = '\0';
                        encodeBase64(s, t,
                                     2 * strlen(login) + strlen(passwd) + 2);
                        sprintf(s, "AUTH PLAIN %s\r\n", t);

                        #ifdef DEBUG
                        fprintf(stdout, "%s\n", s);
                        #endif

                    } else if (!strcmp(auth, "LOGIN"))
                        sprintf(s, "AUTH %s\r\n", auth);

                    else if (!strcmp(auth, "DIGEST-MD5")) {
                        sprintf(s, "AUTH %s\r\n", auth);
                    }

                    if (sendData(sockfd, s) == 0) {
                        if (!strcmp(auth, "PLAIN"))
                            state = STATE_WAIT_PASSWD_ANSWER;
                        else
                            state = STATE_WAIT_AUTH_ANSWER;
                    }
                    break;

                case STATE_WAIT_AUTH_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 334)
                            state = STATE_SEND_LOGIN;
                        else if ((firstDigit == 5) || (firstDigit == 4)) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }

                    if (!strcmp(auth, "DIGEST-MD5")) {
                        decodeBase64(answer + 4, s, strlen(answer));

                        #ifdef DEBUG
                        fprintf(stdout, "zdekodowane: %s\n\n", s);
                        #endif
                    }
                    break;

                case STATE_SEND_LOGIN:
                    if (!strcmp(auth, "DIGEST-MD5")) {
                        char t[1024];

                        decodeBase64(answer + 4, s, strlen(answer));

                        #ifdef DEBUG
                        fprintf(stdout, "zdekodowane: %s\n\n", s);
                        #endif

                        makeDigestMD5response(s, t, login, passwd);
                        encodeBase64(t, s, strlen(t));
                        sprintf(s, "%s\r\n", s);
                    } else {
		
                        encodeBase64(login, s, strlen(login));
                        strcat(s, "\r\n");
                    }

                    if (sendData(sockfd, s) == 0)
                        state = STATE_WAIT_LOGIN_ANSWER;

                    break;

                case STATE_WAIT_LOGIN_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 334)
                            state = STATE_SEND_PASSWD;
                        else if ((firstDigit == 5) || (firstDigit == 4)) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
                    break;

                case STATE_SEND_PASSWD:
                    /* DIGEST-MD5 wymaga przeslania czegos, po autoryzacji */
                    if (!strcmp(auth, "DIGEST-MD5"))
                        strcpy(s, "\r\n");
                    else {
                        encodeBase64(passwd, s, strlen(passwd));
                        strcat(s, "\r\n");
                    }

                    if (sendData(sockfd, s) == 0)
                        state = STATE_WAIT_PASSWD_ANSWER;

                    break;

                case STATE_WAIT_PASSWD_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 235)
                            state = STATE_SEND_MAIL;
                        else if ((firstDigit == 5) || (firstDigit == 4)) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
                    break;
		
                case STATE_SEND_MAIL:
                    sprintf(s, "MAIL FROM:<%s>\r\n", from);

                    if (sendData(sockfd, s) == 0)
                        state = STATE_WAIT_MAIL_ANSWER;
                    break;

                case STATE_WAIT_MAIL_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 250)
                            state = STATE_SEND_RCPT;
                        else if ((firstDigit == 5) || (firstDigit == 4)) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
                    break;

                case STATE_SEND_RCPT:
                    sprintf(s, "RCPT TO:<%s>\r\n", to);

                    if (sendData(sockfd, s) == 0)
                        state = STATE_WAIT_RCPT_ANSWER;

                    break;

                case STATE_WAIT_RCPT_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if ((number == 250) || (number == 251))
                            state = STATE_SEND_DATA;
                        else if ((firstDigit == 5) || (firstDigit == 4)) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
                    break;

                case STATE_SEND_DATA:
                    if (sendData(sockfd, "DATA\r\n") == 0)
                        state = STATE_WAIT_DATA_ANSWER;

                break;

                case STATE_WAIT_DATA_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 354)
                            state = STATE_SEND_DATA_BODY;
                        else if ((firstDigit == 5) || (firstDigit == 4)) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
                    break;

                case STATE_SEND_DATA_BODY:
                    sprintf(s, "From: %s\r\nTo: %s\r\nSubject: "
                               "%s\r\n%s\r\n.\r\n",
                               from, to, subject, body);
	   
                    if (sendData(sockfd, s) == 0)
                        state = STATE_WAIT_DATA_BODY_ANSWER;

                break;

                case STATE_WAIT_DATA_BODY_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 250)
                            state = STATE_SEND_QUIT;
                        else if ((firstDigit == 5) || (firstDigit == 4)) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
	            break;

                case STATE_SEND_QUIT:
                    if (sendData(sockfd, "QUIT\r\n") == 0)
                        state = STATE_WAIT_QUIT_ANSWER;
                    break;

                case STATE_WAIT_QUIT_ANSWER:
                    if (recvData(sockfd, &number, &firstDigit, answer) == 0) {
                        if (number == 221) {
                            closeConnection(sockfd, state, answer);
                            ret = 1;    /* e-mail zostal wyslany poprawnie */
                        } else if ((firstDigit == 5) || (firstDigit == 4)) {
                            closeConnection(sockfd, state, answer);
                            ret = -1;
                        }
                    }
                    break;
            }
        }

    } else {
        errno = ETIMEDOUT;
        fprintf(stderr, "sendEmail() timeout!\n");
    }

    timval.it_interval.tv_usec = 0;
    timval.it_interval.tv_sec = 0;
    timval.it_value.tv_usec = 0;
    timval.it_value.tv_sec = 0;
    setitimer(ITIMER_REAL, &timval, 0);
    sigaction(SIGALRM, &oldact, NULL);


    if (ret != 1) {
        fprintf(stderr, "Problem z wyslaniem e-maila!\n");
        return -1;
    } else if (ret == 1) {
        #ifdef DEBUG
        fprintf(stdout, "E-mail wyslany poprawnie\n");
        #endif

        return 0;
    }

    return -1;    /* byl timeout */
}


/* closeConnection: zamykanie polaczenia z serwerem SMTP
 */
void closeConnection(int sockfd, int state, const char *answer)
{
    close(sockfd);

    #ifdef DEBUG
    fprintf(stdout, "Rozlaczenie (ostatni stan: %d, ostatnia odpowiedz: %s)\n",
            state, answer);
    #endif
}


/* recvData: odczytuje dane od serwera SMTP
 */
int recvData(int sockfd, int *number, int *firstDigit, char *answer)
{
    int n;

    /* odczyt z sieci (odczyt blokujacy) */
    if ((n = recv(sockfd, answer, MAX_LEN - 1, 0)) == -1) {
        fprintf(stderr, "Problem z odczytem danych z sieci!\n");
        return -1;
    }

    if (n < 3)
        return -2;
    
    answer[n] = '\0';

    #ifdef DEBUG
    fprintf(stdout, "odebrano: %s\n", answer);
    #endif
    
    /* obsluga odebranych danych */
    if ((*number = strtol(answer, NULL, 10)) == 0)
        return -3;

    if (isdigit(answer[0]))
        *firstDigit = answer[0] - '0';
    else
        return -4;

    return 0;
}


/* sendData: wysyla dane do serwera SMTP
 */
int sendData(int sockfd, const char *data)
{
    #ifdef DEBUG
    fprintf(stdout, "wysylanie: %s\n", data);
    #endif
	
    /* wysylanie danych do serwera */
    if (send(sockfd, data, strlen(data), 0) != strlen(data)) {
        fprintf(stderr, "Problem z wyslaniem danych do sieci!\n");
        return -1;
    }

    return 0;
}


/* connectSlave: laczy sie z odleglym serwerem SMTP po TCP pod adresem "ip" i
 *               na porcie "port"
 */
int connectSlave(const char *ip, int port)
{
    struct sockaddr_in address;
    int sockfd;

    #ifdef DEBUG
    fprintf(stdout, "connectSlave(): IP: %s, port: %d.\n", ip, port);
    #endif

    /* tworzenie gniazda TCP */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        fprintf(stderr, "Problem z socket() w connectSlave()!\n");
        return -1;
    }

    bzero(&address, sizeof (address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);       /* IP */
    address.sin_port = htons(port);                  /* port */

    /* proby nawiazywania polaczenia z odleglym serwerem */
    if (connect(sockfd, (struct sockaddr *)&address, sizeof (address)) == -1) {
        fprintf(stderr, "Problem z connect() w connectSlave()!\n");
        return -2;
    }

    #ifdef DEBUG
    fprintf(stdout, "Polaczenie z odleglym serwerem zostalo nawiazane.\n");
    #endif

    return sockfd;
}


const unsigned char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                               "abcdefghijklmnopqrstuvwxyz"
                               "0123456789"
                               "+/";

/* encodeBlock: koduje blok trzech znakow na cztery znaki z tablicy base64
 */
void encodeBlock(const unsigned char in[3], unsigned char out[4], int len)
{
    out[0] = base64[in[0] >> 2];
    out[1] = base64[((in[0] & 0x03) << 4) | (in[1] >> 4)];
    out[2] = len > 1 ? base64[((in[1] & 0x0f) << 2) | (in[2] >> 6)] : '=';
    out[3] = len > 2 ? base64[in[2] & 0x3f] : '=';
}


/* encodeBase64: koduje zadany lancuch znakow na Base64
 */
void encodeBase64(const unsigned char *s, unsigned char *t, int len)
{
    int i;

    for (i = 0; i < len / 3; i++)
        encodeBlock(&s[i * 3], &t[i * 4], 3);

    if (len - i * 3 > 0)
        encodeBlock(&s[i * 3], &t[i * 4], len - i * 3),
        i++;

    t[i * 4] = '\0';
}


/* decodeBlock: odkodowuje blok czterech znakow na trzy znaki
 */
void decodeBlock(const unsigned char in[4], unsigned char out[3])
{
    unsigned char inTmp[4] = { 0, 0, 0 };
    int i;
    unsigned char *p;

    for (i = 0; i <= 3; i++)
        if ((p = strchr(base64, in[i])) != NULL)
            inTmp[i] = p - base64;

    out[0] = (inTmp[0] << 2) | (inTmp[1] >> 4);
    out[1] = (inTmp[1] << 4) | (inTmp[2] >> 2);
    out[2] = (inTmp[2] << 6) | (inTmp[3]);
}


/* decodeBase64: odkodowuje zadany lancuch z Base64
 */
void decodeBase64(const unsigned char *s, unsigned char *t, int len)
{
    int i;

    for (i = 0; i < len / 4; i++)
        decodeBlock(&s[i * 4], &t[i * 3]);

    /* "t" musi miec wielkosc rowna wielokrotnosci trojki + 1 */
    t[i * 3] = '\0';
}


/* extractToken: ekstraktuje wartosc dla podanego tokenu z lancucha znakow
 */
int extractToken(const char *s, const char *token, char *value, int valLen)
{
    char *p = NULL, *q = NULL;

    if ((p = strstr(s, token)) != NULL) {
        p += strlen(token);                          /* += strlen("realm="); */
        if (*p == '\"') {
            if ((q = strchr(p + 1, '\"')) == NULL)   /* zamykajacy " */
                return -1;

            strncpy(value, p + 1, q - p - 1 >= valLen ? valLen - 1 : q - p - 1);

	} else {
            if ((q = strchr(p, ',')) == NULL)        /* przecinek */
                q += strlen(p);

	    strncpy(value, p, q - p >= valLen ? valLen - 1 : q - p);
        }
    }
}


/* toHex: konwertuje 16-bitowy ciag znakow na 32-bitowy ciag znakow hex
 */
void toHex(const unsigned char bin[16], unsigned char hex[33])
{
    int i;
    unsigned char c;

    for (i = 0; i < 32; i++) {
        if (i % 2 == 0)
            c = (bin[i / 2] >> 4) & 0x0f;
        else
            c = (bin[i / 2]) & 0x0f;

	if (c <= 9)
            hex[i] = c + '0';
        else
            hex[i] = c + 'a' - 10;
    }
    hex[32] = '\0';
}


/* makeDigestMD5response: tworzy lancuch z odpowiedzia dla serwera
 *                        podczas autoryzacji Digest-MD5
 */
int makeDigestMD5response(const char *s, char *t,
                          const char *login, const char *passwd)
{
    char realm[256] = "\0",
         nonce[64] = "\0",
         cnonce[32] = "\0",
         qop[64] = "\0",
         uri[261] = "\0",
         w[32],
	 *p = NULL, *q = NULL;
    int f;
    md5_state_t state;
    md5_byte_t digest[17];
    char hexA1[33], hexA2[33], resp[33];

    printf("%s\n", s);

    /* ekstrakcja pewnych wartosci z lancucha (digest-challenge, RFC2831)
     * otrzymanego od serwera
     * */
    extractToken(s, "realm=", realm, sizeof (realm));
    extractToken(s, "nonce=", nonce, sizeof (nonce));
    extractToken(s, "qop=", qop, sizeof (qop));

    sprintf(uri, "smtp/%s", realm);

    /* generowanie losowego "cnonce" */
    if ((f = open("/dev/urandom", O_RDONLY)) == -1)
        return -1;

    if (read(f, w, 12) != 12) {
        close(f);
        return -2;
    }

    close(f);

    encodeBase64(w, cnonce, 12);
    cnonce[16] = '\0';

    /* tworzenie pola "response"
     *
     * HEX( KD ( HEX(H(A1)),
     *       { nonce-value, ":" nc-value, ":",
     *         cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))
     *
     * A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
     *      ":", nonce-value, ":", cnonce-value }
     *
     * A2 = { "AUTHENTICATE:", digest-uri-value }
     * 
     * If the "qop" value is "auth-int" or "auth-conf" then A2 is:
     *
     * A2 = { "AUTHENTICATE:", digest-uri-value,
     *    ":00000000000000000000000000000000" }
     */

    /* A1 */
    md5_init(&state);
    md5_append(&state, (const md5_byte_t *)login, strlen(login));
    md5_append(&state, ":", 1);
    md5_append(&state, (const md5_byte_t *)realm, strlen(realm));
    md5_append(&state, ":", 1);
    md5_append(&state, (const md5_byte_t *)passwd, strlen(passwd));
    md5_finish(&state, digest);

    md5_init(&state);
    md5_append(&state, digest, 16);
    md5_append(&state, ":", 1);
    md5_append(&state, nonce, strlen(nonce));
    md5_append(&state, ":", 1);
    md5_append(&state, cnonce, strlen(cnonce));
    md5_finish(&state, digest);
    toHex(digest, hexA1);

    #ifdef DEBUG
    fprintf(stdout, "A1: %s\n", hexA1);
    #endif
    /* A2 */
    md5_init(&state);
    md5_append(&state, "AUTHENTICATE:", sizeof ("AUTHENTICATE:") - 1);
    md5_append(&state, uri, strlen(uri));
    if (!strcmp(qop, "auth-int"))
        md5_append(&state, ":00000000000000000000000000000000",
                   sizeof (":00000000000000000000000000000000") - 1);
    md5_finish(&state, digest);
    toHex(digest, hexA2);

    #ifdef DEBUG
    fprintf(stdout, "A2: %s\n", hexA2);
    #endif

    /* response */
    md5_init(&state);
    md5_append(&state, hexA1, 32);
    md5_append(&state, ":", 1);
    md5_append(&state, nonce, strlen(nonce));
    md5_append(&state, ":00000001:", sizeof (":00000001:") - 1);
    md5_append(&state, cnonce, strlen(cnonce));
    md5_append(&state, ":", 1);
    md5_append(&state, qop, strlen(qop));
    md5_append(&state, ":", 1);
    md5_append(&state, hexA2, 32);
    md5_finish(&state, digest);
    toHex(digest, resp);
    resp[32] = '\0';

    #ifdef DEBUG
    fprintf(stdout, "Response: %s\n", resp);
    #endif

    /* tworzenie lancucha wyjsciowego (digest-response) */
    sprintf(t, "charset=utf-8,username=\"%s\",realm=\"%s\",nonce=\"%s\","
               "nc=00000001,cnonce=\"%s\",digest-uri=\"%s\",qop=\"%s\","
               "response=%s",
               login, realm, nonce, cnonce, uri, qop, resp);

    #ifdef DEBUG
    fprintf(stdout, "response: %s\n", t);
    #endif

    return 0;
}

Kod źródłowy pliku "main.c":

#include <stdio.h>
#include "libmail.h"

int main(void)
{
    sendEmail("213.180.130.20", 25, "smtp.poczta.onet.pl",
              "s_pawlak@op.pl", "s_pawlak@op.pl",
              "libmail", "Test libmail.", "LOGIN", "s_pawlak@op.pl",
              "*********");    /* wstaw haslo zamiast gwiazdek */

    return 0;
}



Funkcja wyświetla komunikaty diagnostyczne. A oto przykładowy wynik działania programu.


Kod źródłowy pliku "wynik":

connectSlave(): IP: 213.180.130.20, port: 25.
Polaczenie z odleglym serwerem zostalo nawiazane.
stan: 0
odebrano: 220 smtp.poczta.onet.pl ESMTP (7) our local time is now Wed, 14 Dec 2005 00:43:40 +0100

stan: 1
wysylanie: HELO smtp.poczta.onet.pl

stan: 2
odebrano: 250 smtp.poczta.onet.pl expected "HELO pcXXX.warszawa.sdi.tpnet.pl"

stan: 3
wysylanie: AUTH LOGIN

stan: 4
odebrano: 334 VXNlcm5hbWU6

stan: 5
wysylanie: c19wYXdsYWtAb3AucGw=

stan: 6
odebrano: 334 UGFzc3dvcmQ6

stan: 7
wysylanie: bWlaaXgyemlw

stan: 8
odebrano: 235 Authentication successful.

stan: 9
wysylanie: MAIL FROM:<s_pawlak@op.pl>

stan: 10
odebrano: 250 2.1.0 Sender syntax Ok

stan: 11
wysylanie: RCPT TO:<s_pawlak@op.pl>

stan: 12
odebrano: 250 2.1.5 Recipient address syntax Ok; rcpt=<s_pawlak@op.pl>

stan: 13
wysylanie: DATA

stan: 14
odebrano: 354 Start mail input; end with <CRLF>.<CRLF>

stan: 15
wysylanie: From: s_pawlak@op.pl
To: s_pawlak@op.pl
Subject: libmail
Test libmail.
.

stan: 16
odebrano: 250 2.6.0 Message accepted.

stan: 17
wysylanie: QUIT

stan: 18
odebrano: 221 2.0.0 smtp.poczta.onet.pl Out

Rozlaczenie (ostatni stan: 18, ostatnia odpowiedz: 221 2.0.0 smtp.poczta.onet.pl Out
)
E-mail wyslany poprawnie
w3cw3c
automatyka przemysłowa