当前位置 博文首页 > 积水成渊:(原创) linux 下给多人发送邮件源码(SMTP协议)
?直接贴代码吧~~~~
1. mail.c
///
//mail.c:
#include "mail.h"
#include "base64.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
static char
*months [] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static char
*days [] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char * encode_mime_time (long date, long time);
void local_to_gmt (long date, long time, long *gmt_date, long *gmt_time);
time_t date_to_timer (long date, long time);
long timer_to_gmdate (time_t time_secs);
long timer_to_gmtime (time_t time_secs);
int day_of_week (long date);
long time_now (void);
long date_now (void);
#define GET_MONTH(d) (int) (((d) % 10000L) / 100)
#define GET_DAY(d) (int) ( (d) % 100)
#define GET_CCYEAR(d) (int) ( (d) / 10000L)
#define GET_YEAR(d) (int) (((d) % 1000000L) / 10000L)
#define GET_HOUR(t) (int) ( (t) / 1000000L)
#define GET_MINUTE(t) (int) (((t) % 1000000L) / 10000L)
#define GET_SECOND(t) (int) (((t) % 10000L) / 100)
#define MAKE_DATE(c,y,m,d) (long) (c) * 1000000L + \
(long) (y) * 10000L + \
(long) (m) * 100 + (d)
#define MAKE_TIME(h,m,s,c) (long) (h) * 1000000L + \
(long) (m) * 10000L + \
(long) (s) * 100 + (c)
/* ---------------------------------------------------------------------[<]-
Function: encode_mime_time
Synopsis: Encode date and time (in long format) in Mime RFC1123 date
format, e.g. Mon, 12 Jan 1995 12:05:01 GMT. The supplied date and time
are in local time. Returns the date/time string if the date was legal,
else returns "?". Returned string is in a static buffer.
---------------------------------------------------------------------[>]-*/
char * encode_mime_time (long date, long time)
{
int day_week, /* Day of week number (0 is sunday) */
month; /* Month number */
static char buffer [50];
local_to_gmt (date, time, &date, &time);
day_week = day_of_week (date);
month = GET_MONTH (date);
if (day_week >= 0 && day_week < 7 && month > 0 && month < 13)
{
sprintf (buffer, "%s, %02d %s %04d %02d:%02d:%02d GMT",
days [day_week],
GET_DAY (date),
months [month - 1],
GET_CCYEAR (date),
GET_HOUR (time),
GET_MINUTE (time),
GET_SECOND (time)
);
return (buffer);
}
else
return ("?");
} //end proc encode_mime_time()
/* ---------------------------------------------------------------------[<]-
Function: local_to_gmt
Synopsis: Converts the specified date and time to GMT. Returns the GMT
date and time in two arguments.
---------------------------------------------------------------------[>]-*/
void local_to_gmt (long date, long time, long *gmt_date, long *gmt_time)
{
time_t time_value;
time_value = date_to_timer (date, time);
*gmt_date = timer_to_gmdate (time_value);
*gmt_time = timer_to_gmtime (time_value);
} //end proc local_to_gmt()
/* ---------------------------------------------------------------------[<]-
Function: date_to_timer
Synopsis: Converts the supplied date and time into a time_t timer value.
This is the number of non-leap seconds since 00:00:00 GMT Jan 1, 1970.
Function was rewritten by Bruce Walter <walter@fortean.com>. If the
input date and time are invalid, returns 0.
---------------------------------------------------------------------[>]-*/
time_t date_to_timer (long date, long time)
{
struct tm time_struct;
time_t timer;
time_struct.tm_sec = GET_SECOND (time);
time_struct.tm_min = GET_MINUTE (time);
time_struct.tm_hour = GET_HOUR (time);
time_struct.tm_mday = GET_DAY (date);
time_struct.tm_mon = GET_MONTH (date) - 1;
time_struct.tm_year = GET_CCYEAR (date) - 1900;
time_struct.tm_isdst = -1;
timer = mktime (&time_struct);
if (timer == -1)
return (0);
else
return (timer);
} //end proc date_to_timer()
/* ---------------------------------------------------------------------[<]-
Function: timer_to_date
Synopsis: Converts the supplied timer value into a long date value.
Dates are stored as long values: CCYYMMDD. If the supplied value is
zero, returns zero. If the supplied value is out of range, returns
1 January, 1970 (19700101). The timer value is assumed to be UTC (GMT).
---------------------------------------------------------------------[>]-*/
long timer_to_date (time_t time_secs)
{
struct tm *time_struct;
if (time_secs == 0)
return (0);
else
{
/* Convert into a long value CCYYMMDD */
time_struct = localtime (&time_secs);
if (time_struct)
{
time_struct-> tm_year += 1900;
return (MAKE_DATE (time_struct-> tm_year / 100,
time_struct-> tm_year % 100,
time_struct-> tm_mon + 1,
time_struct-> tm_mday));
}
else
return (19700101);
}
} //end proc timer_to_date()
/* ---------------------------------------------------------------------[<]-
Function: timer_to_time
Synopsis: Converts the supplied timer value into a long time value.
Times are stored as long values: HHMMSS00. Since the timer value does
not hold centiseconds, these are set to zero. If the supplied value
was zero or invalid, returns zero. The timer value is assumed to be UTC
(GMT).
---------------------------------------------------------------------[>]-*/
long timer_to_time (time_t time_secs)
{
struct tm *time_struct;
if (time_secs == 0)
return (0);
else
{
/* Convert into a long value HHMMSS00 */
time_struct = localtime (&time_secs);
if (time_struct)
return (MAKE_TIME (time_struct-> tm_hour,
time_struct-> tm_min,
time_struct-> tm_sec,
0));
else
return (0);
}
} //end proc timer_to_time()
/* ---------------------------------------------------------------------[<]-
Function: timer_to_gmdate
Synopsis: Converts the supplied timer value into a long date value in
Greenwich Mean Time (GMT). Dates are stored as long values: CCYYMMDD.
If the supplied value is zero, returns zero. If the supplied value is
out of range, returns 1 January, 1970 (19700101).
---------------------------------------------------------------------[>]-*/
long timer_to_gmdate (time_t time_secs)
{
struct tm *time_struct;
if (time_secs == 0)
return (0);
else
{
/* Convert into a long value CCYYMMDD */
time_struct = gmtime (&time_secs);
if (time_struct == NULL) /* If gmtime is not implemented */
time_struct = localtime (&time_secs);
if (time_struct)
{
time_struct-> tm_year += 1900;
return (MAKE_DATE (time_struct-> tm_year / 100,
time_struct-> tm_year % 100,
time_struct-> tm_mon + 1,
time_struct-> tm_mday));
}
else
return (19700101); /* We had an invalid date */
}
} //end proc timer_to_gmdate()
/* ---------------------------------------------------------------------[<]-
Function: timer_to_gmtime
Synopsis: Converts the supplied timer value into a long time value in
Greenwich Mean Time (GMT). Times are stored as long values: HHMMSS00.
On most systems the clock does not return centiseconds, so these are
set to zero. If the supplied value is zero or invalid, returns zero.
---------------------------------------------------------------------[>]-*/
long timer_to_gmtime (time_t time_secs)
{
struct tm *time_struct;
if (time_secs == 0)
return (0);
else
{
/* Convert into a long value HHMMSS00 */
time_struct = gmtime (&time_secs);
if (time_struct == NULL) /* If gmtime is not implemented */
time_struct = localtime (&time_secs);
if (time_struct)
return (MAKE_TIME (time_struct-> tm_hour,
time_struct-> tm_min,
time_struct-> tm_sec,
0));
else
return (0);
}
} //end proc timer_to_gmtime()
/* ---------------------------------------------------------------------[<]-
Function: day_of_week
Synopsis: Returns the day of the week where 0 is Sunday, 1 is Monday,
... 6 is Saturday. Uses Zeller's Congurence algorithm.
---------------------------------------------------------------------[>]-*/
int day_of_week (long date)
{
int year = GET_CCYEAR (date),
month = GET_MONTH (date),
day = GET_DAY (date);
if (month > 2)
month -= 2;
else
{
month += 10;
year--;
}
day = ((13 * month - 1) / 5) + day + (year % 100) +
((year % 100) / 4) + ((year / 100) / 4) - 2 *
(year / 100) + 77;
return (day - 7 * (day / 7));
} //end proc day_of_week()
/* ---------------------------------------------------------------------[<]-
Function: time_now
Synopsis: Returns the current time as a long value (HHMMSSCC). If the
system clock does not return centiseconds, these are set to zero.
---------------------------------------------------------------------[>]-*/
long time_now (void)
{
/* The BSD gettimeofday function returns seconds and microseconds */
struct timeval
time_struct;
gettimeofday (&time_struct, 0);
return (timer_to_time (time_struct.tv_sec)
+ time_struct.tv_usec / 10000);
} //end proc time_now()
/* ---------------------------------------------------------------------[<]-
Function: date_now
Synopsis: Returns the current date as a long value (CCYYMMDD). Since
most system clocks do not return a century, this function assumes that
all years 80 and above are in the 20th century, and all years 00 to 79
are in the 21st century. For best results, consume before 1 Jan 2080.
---------------------------------------------------------------------[>]-*/
long date_now (void)
{
return (timer_to_date (time (NULL)));
} //end proc date_now()
/* 判断str开始的字符串是不是start*/
static int start_str(char *str,const char *start)
{
if(strstr(str,start)==str)
return 1;
else
return 0;
}
/*将命令command发送到SMTP服务器,如果收到响应号为code,则返回true */
static int do_act(const char *code, mail_data_s *mail)
{
if(write(mail->sock,mail->lastact,strlen(mail->lastact))<0)
{
perror("write");
return 0;
}
//printf("send : %s\n", mail->lastact);
memset(mail->lastmessage, 0, sizeof(mail->lastmessage));
if(read(mail->sock,mail->lastmessage ,255)<0)
{
perror("read");
return 0;
}
//printf("recv : %s\n", mail->lastmessage);
if(start_str(mail->lastmessage,code))
return 1;
else
return 0;
}
/* 发送email */
static int send_mail(mail_data_s *mail, account_s *user)
{
int tocnt = 0;
char head[255];
char to[255];
char *tolist[32];
char *ptr = NULL;
struct sockaddr_in sa;
struct hostent *he;
sa.sin_family = AF_INET;
sa.sin_port = htons(user->port);
if((he=gethostbyname(user->smtp_server))!=NULL)
bcopy(he->h_addr,&sa.sin_addr,he->h_length);
else
{
perror("gethostbyname");
return -1;
}
//strcpy(mail->lastact,"connect");
mail->sock=socket(AF_INET,SOCK_STREAM,0);
if ( mail->sock )
{
if(connect(mail->sock,(struct sockaddr*)&sa,sizeof(struct sockaddr))<0)
{
perror("conncect");
return -1;
}
read(mail->sock,mail->lastmessage,512);
if ( !start_str(mail->lastmessage,"220"))
{
return -1;
}
else
{
/* Now, we must send chat command to smtp server. */
strcpy(mail->lastact,"HELO ");
strcat(mail->lastact,mail->welcome);
strcat(mail->lastact,"\r\n");
fflush(stdout);
if(!do_act("250", mail))
{
printf("HELO\n");
close(mail->sock);
return -1;
}
strcpy(mail->lastact,"AUTH LOGIN\r\n");
if(!do_act("334", mail))
{
printf("AUTH LOGIN\n");
close(mail->sock);
return -1;
}
char *code64=NULL;
code64=base64_encode(user->username,strlen(user->username));
strcpy(mail->lastact,code64);
strcat(mail->lastact,"\r\n");
if(!do_act("334", mail))
{
printf("user name\n");
close(mail->sock);
return -1;
}
//code64=NULL;
//code64=base64_encode(user->passwd,strlen(user->passwd));
// strcpy(mail->lastact,code64);
strcpy(mail->lastact, user->passwd);
strcat(mail->lastact,"\r\n");
if(!do_act("235", mail))
{
printf("passwd\n");
close(mail->sock);
return -1;
}
strcpy(mail->lastact,"MAIL FROM: ");
strcat(mail->lastact,mail->from);
strcat(mail->lastact,"\r\n");
if(!do_act("250", mail))
{
printf("MAIL FROM");
close(mail->sock);
return -1;
}
/*
strcpy(mail->lastact,"RCPT TO: ");
strcat(mail->lastact,mail->to);
strcat(mail->lastact,"\r\n");
if(!do_act("250", mail))
{
close(mail->sock);
return -1;
}
*/
/* split mail to address */
strcpy(to, mail->to);
ptr = strtok(to, " ");
tolist[tocnt++] = ptr;
while((ptr = strtok(NULL, " ")))
{
tolist[tocnt++] = ptr;
}
tolist[tocnt] = NULL;
tocnt = 0;
/* Send peer mail address to smtp server */
while(tolist[tocnt])
{
strcpy(mail->lastact,"RCPT TO: ");
strcat(mail->lastact, tolist[tocnt++]);
strcat(mail->lastact,"\r\n");
printf("%s \n", mail->lastact);
if(!do_act("250", mail))
{
close(mail->sock);
return -1;
}
}
/* Send mail data to smtp server */
strcpy(mail->lastact,"DATA\r\n");
if(!do_act("354", mail))
{
close(mail->sock);
return -1;
}
/* Send mail subject to smtp server */
strcpy(head,"Subject: ");
strcat(head,mail->subject);
strcat(head,"\r\n");
if(strlen(mail->subject)>0)
{
write(mail->sock,head,strlen(head));
}
/* Send mail sender address to smtp server */
strcpy(head,"From: ");
strcat(head,mail->from);
strcat(head,"\r\n");
if(strlen(mail->from)>0)
{
write(mail->sock,head,strlen(head));
}
/*
strcpy(head,"To: ");
//strncat(head,mail->to+1, strlen(mail->to)-2);
strcat(head,mail->to);
strcat(head,"\r\n");
if(strlen(head)>0)
{
write(mail->sock,head,strlen(head));
}
*/
/* Send mail peer address infomation to smtp server */
tocnt = 0;
while(tolist[tocnt])
{
strcpy(head,"To: ");
strncat(head, tolist[tocnt] + 1, strlen(tolist[tocnt]) - 2);
strcat(head, " ");
strcat(head, tolist[tocnt++]);
strcat(head,"\r\n");
printf("%s \n", head);
if(strlen(head)>0)
{
write(mail->sock,head,strlen(head));
}
}
strcpy (head, "\r\n\r\n");
if(strlen(head)>0)
{
write(mail->sock, head, strlen(head));
}
//code64 = NULL;
//strcpy(head,message);
//strcat(head,"\r\n");
//code64=base64_encode(head,strlen(head));
//strcpy(message,code64);
/* Send mail message to smtp server */
strcpy(head, mail->message);
strcat(head, "\r\n");
write(mail->sock, head, strlen(head));
strcpy(head, "Date: ");
strcat(head, encode_mime_time (date_now (), time_now ()));
strcat(head, "\r\n");
write(mail->sock, head, strlen(head));
strcpy(head, "\r\n.\r\n");
write(mail->sock, head, strlen(head));
/* Send quit command to smtp server, then chatting end. */
strcpy(mail->lastact,"QUIT\r\n");
if(!do_act("250", mail))
{
close(mail->sock);
return -1;
}
}
return 0;
}
else
{
printf("Connect failed!");
return -1;
}
}
/* Create a mail struct */
mail_s *new_mail( char *to, char *from, char *welcome, char *subject, char *message)
{
mail_s *mail = NULL;
if((mail = (mail_s *)calloc(1, sizeof(mail_s))) == NULL)
return NULL;
if(strlen(to)<=0)
{
printf("Mail to cannt be NULL");
goto NEW_FAIL;
}
else
{
if((mail->data.to = (char *)calloc(1, sizeof(char)*(strlen(to) + 1))))
strcpy(mail->data.to, to);
else
goto NEW_FAIL;
}
if(strlen(from)<=0)
{
printf("Mail from cannt be NULL");
goto PARAM_FAIL;
}
else
strcpy(mail->data.from,from);
if(strlen(subject)<=0)
{
strcpy(mail->data.subject,"Test");
}
else
strcpy(mail->data.subject,subject);
if(strlen(welcome)<=0)
{
strcpy(mail->data.welcome,"localhost");
}
else
strcpy(mail->data.welcome,welcome);
strcpy(mail->data.lastmessage,"");
strcpy(mail->data.lastact,"");
mail->data.messagelen = strlen(message);
if((mail->data.message = (char *)calloc(1, sizeof(char)*mail->data.messagelen)) == NULL)
goto PARAM_FAIL;
strcpy(mail->data.message, message);
mail->send = &send_mail;
return mail;
PARAM_FAIL:
free(mail->data.to);
NEW_FAIL:
free(mail);
return NULL;
}
/* Free mail struct */
void delete_mail( mail_s *mail )
{
free(mail->data.message);
free(mail->data.to);
free(mail);
mail = NULL;
}
/* Create sender user account struct */
account_s *new_account(char *username, char *passwd, char *smtp_server, int port)
{
char *code64 = NULL;
account_s *user = NULL;
if((user = (account_s *)calloc(1, sizeof(account_s))) == NULL)
return NULL;
memcpy(user->username, username, strlen(username));
code64 = base64_encode(passwd, strlen(passwd));
strcpy(user->passwd, code64);
//memcpy(user->passwd, passwd, strlen(passwd));
memcpy(user->smtp_server, smtp_server, strlen(smtp_server));
user->port = port;
return user;
}
/* Free account struct */
void delete_account( account_s *user )
{
free(user);
user = NULL;
}
/* Test main */
int main()
{
mail_s *mail = NULL;
account_s *user = NULL;
user = new_account("xxx@163.com", "passwd", "smtp.163.com", 25);
if(!user)
{
printf("Alloc space for account fail.\n");
return 0;
}
mail = new_mail("<xxx@qq.com> <xxx@163.com>",
"<xxx@163.com>", "xxx", "test", "Hello, this is a test letter!");
if(!mail)
{
printf("Alloc space for mail fail.\n");
goto FREE_USER;
}
if(mail->send(&mail->data, user) == 0)
{
printf("发送成功!\n");
}
else
{
printf("发送失败!\n");
}
delete_mail(mail);
FREE_USER:
delete_account(user);
return 0;
}
///
//mail.h:
typedef struct {
char username[32]; //username
char passwd[32]; // password
char smtp_server[64]; //smtp服务器地址
int port; //smtp端口号,25
}account_s;
typedef struct user_list{
account_s user;
struct user_list *next;
}user_list;
typedef struct {
char lastmessage[255]; //记录最后返回的响应信息
char lastact[255]; //最后的动作,字符串形式
char welcome[255]; //用在HELO后面,欢迎用户
char from[64]; //the mail from where
char *to; //mail send to where
char subject[64]; // mail subject
char *message; // send message
int messagelen; //message length
int sock; //socket句柄
}mail_data_s;
typedef struct {
mail_data_s data;
int (*send)(mail_data_s *, account_s *);
} mail_s;
mail_s *new_mail(char *, char *, char *, char *, char *);
void delete_mail( mail_s *mail );
account_s *new_account(char *, char *, char *, int);
void delete_account( account_s *user );
///
//base64.h
//base64编码与解码
#include <stdlib.h>
#include <string.h>
static char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
static signed char index_64[128] = {
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
-1, 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,-1, -1,-1,-1,-1,
-1,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,-1, -1,-1,-1,-1
} ;
char *base64_encode(const char *value, int vlen) {
unsigned char oval = 0 ;
char *result = (char *)malloc((vlen * 4) / 3 + 5) ;
char *out = result;
while (vlen >= 3) {
*out++ = basis_64[value[0] >> 2];
*out++ = basis_64[((value[0] << 4) & 0x30) | (value[1] >> 4)];
*out++ = basis_64[((value[1] << 2) & 0x3C) | (value[2] >> 6)];
*out++ = basis_64[value[2] & 0x3F];
value += 3;
vlen -= 3;
}
if (vlen > 0) {
*out++ = basis_64[value[0] >> 2];
oval = (value[0] << 4) & 0x30 ;
if (vlen > 1) oval |= value[1] >> 4;
*out++ = basis_64[oval];
*out++ = (vlen < 2) ? '=' : basis_64[(value[1] << 2) & 0x3C];
*out++ = '=';
}
*out = '\0';
return result;
}
unsigned char *base64_decode(const char *value, int *rlen)
{
int c1, c2, c3, c4;
int vlen = strlen(value);
unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
unsigned char *out = result;
*rlen = 0;
while (1) {
if (value[0]==0) {
*out = '\0' ;
return result;
}
c1 = value[0];
if (CHAR64(c1) == -1) goto base64_decode_error;
c2 = value[1];
if (CHAR64(c2) == -1) goto base64_decode_error;
c3 = value[2];
if ((c3 != '=') && (CHAR64(c3) == -1)) goto base64_decode_error;
c4 = value[3];
if ((c4 != '=') && (CHAR64(c4) == -1)) goto base64_decode_error;
value += 4;
*out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
*rlen += 1;
if (c3 != '=') {
*out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
*rlen += 1;
if (c4 != '=') {
*out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
*rlen += 1;
}
}
}
base64_decode_error:
*result = 0;
*rlen = 0;
return result;
}