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





