通过这个简单的Demo程序,可以了解BASE64的编码方法和SMTP协议的报文格式,最终实现一个发送邮件的程序。
base64编解码头文件<base64.h>
#ifndef _Base64_H__
#define _Base64_H__
#include <stdio.h>
char *base64_encode(const char *input, const size_t length,char *output);
char *base64_decode(const char *input,char *output);
#endif
base64编解码源文件<base64.c>
#include "base64.h"
const char base64_index[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// 16 * 16
const int base64_decode_map[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 - 31
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32 - 47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48 - 63
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64 - 79
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80 - 95
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96 - 111
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112 - 127
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 - 143
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144 - 159
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 - 175
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176 - 191
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192 - 207
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208 - 223
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224 - 239
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 240 - 255
};
char *base64_encode(const char *input, const size_t length, char *output)
{
*output = '\0';
if (input == NULL || length < 1) return output;
char *p = (char*)input;
char *p_dst = (char*)output;;
char *p_end = (char*)input + length;
int loop_count = 0;
// 0x30 -> 00110000
// 0x3C -> 00111100
// 0x3F -> 00111111
while (p_end - p >= 3) {
*p_dst++ = base64_index[(p[0] >> 2)];
*p_dst++ = base64_index[((p[0] << 4) & 0x30) | (p[1] >> 4)];
*p_dst++ = base64_index[((p[1] << 2) & 0x3C) | (p[2] >> 6)];
*p_dst++ = base64_index[p[2] & 0x3F];
p += 3;
}
if (p_end - p > 0) {
*p_dst++ = base64_index[(p[0] >> 2)];
if (p_end - p == 2) {
*p_dst++ = base64_index[((p[0] << 4) & 0x30) | (p[1] >> 4)];
*p_dst++ = base64_index[(p[1] << 2) & 0x3C];
*p_dst++ = '=';
}
else if (p_end - p == 1) {
*p_dst++ = base64_index[(p[0] << 4) & 0x30];
*p_dst++ = '=';
*p_dst++ = '=';
}
}
*p_dst = '\0';
return output;
}
char *base64_decode(const char *input, char *output)
{
output[0] = '\0';
if (input == NULL || output == NULL)
return output;
int input_len = strlen(input);
if (input_len < 4 || input_len % 4 != 0)
return output;
// 0xFC -> 11111100
// 0x03 -> 00000011
// 0xF0 -> 11110000
// 0x0F -> 00001111
// 0xC0 -> 11000000
char *p = (char*)input;
char *p_out = output;
char *p_end = (char*)input + input_len;
for (; p < p_end; p += 4) {
*p_out++ = ((base64_decode_map[p[0]] << 2) & 0xFC) | ((base64_decode_map[p[1]] >> 4) & 0x03);
*p_out++ = ((base64_decode_map[p[1]] << 4) & 0xF0) | ((base64_decode_map[p[2]] >> 2) & 0x0F);
*p_out++ = ((base64_decode_map[p[2]] << 6) & 0xC0) | (base64_decode_map[p[3]]);
}
if (*(input + input_len - 2) == '=') {
*(p_out - 2) = '\0';
}
else if (*(input + input_len - 1) == '=') {
*(p_out - 1) = '\0';
}
return output;
}
发送邮件主程序<demo.c>
#include <stdio.h>
#include <stdlib.h>
#include "base64.h"
#include <winsock.h>
#pragma comment(lib, "Ws2_32.lib")
int init_socket();
void socket_close();
SOCKET socket_connect(const char * hostname, int port);
void get_sockbuf();
void send_socket(const char *data);
SOCKET sockfd = 0;
int main()
{
char data[1024] = { 0 };
char base64_data[2048] = { 0 };
int i = 0;
init_socket();
sockfd = socket_connect("smtp.qq.com", 25);
//接收服务器信息
get_sockbuf();
//发送helo信息
send_socket("helo smtp.qq.com\r\n");
//接收服务器应答
get_sockbuf();
//发送登录请求
send_socket("auth login\r\n");
//接收服务器应答
get_sockbuf();
//发送编码后的邮箱账号
memset(data, 0, sizeof(data));
strcpy(data, "你自己的QQ号@qq.com");
base64_encode(data, strlen(data), base64_data);
i = strlen(base64_data);
base64_data[i] = '\r';
base64_data[i+1] = '\n';
send_socket(base64_data);
//接收服务器应答
get_sockbuf();
//发送编码后的邮箱密码
memset(data, 0, sizeof(data));
memset(base64_data, 0, sizeof(base64_data));
strcpy(data, "你QQ邮箱的密码");
base64_encode(data, strlen(data), base64_data);
i = strlen(base64_data);
base64_data[i] = '\r';
base64_data[i + 1] = '\n';
send_socket(base64_data);
//接收服务器应答
get_sockbuf();
//mail from
send_socket("mail from:<你自己的QQ号@qq.com>\r\n");
//接收服务器应答
get_sockbuf();
//rcpt to
send_socket("rcpt to:<接收邮件的QQ号@qq.com>\r\n");
//接收服务器应答
get_sockbuf();
//正文
send_socket("data\r\n");
send_socket("subject:test\r\n");
send_socket("\r\nhello!\r\n.\r\n");
//接收服务器应答
get_sockbuf();
//退出
send_socket("quit\r\n");
//接收服务器应答
get_sockbuf();
socket_close();
system("pause");
}
void send_socket(const char *data)
{
if (send(sockfd, data, strlen(data), 0) < 0)
{
printf("发送失败!\n");
return;
}
printf("%s", data);
}
void get_sockbuf()
{
static char buf[1024] = { 0 };
memset(buf, 0, sizeof(buf));
if (recv(sockfd, buf, sizeof(buf), 0) < 0)
{
return;
}
printf("%s", buf);
}
int init_socket()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return -1;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
return 0;
}
SOCKET socket_connect(const char * hostname, int port)//连接套接字
{
struct hostent*host;
unsigned long ip = 0;
if (init_socket() == -1)
return 0;
SOCKET st = socket(AF_INET, SOCK_STREAM, 0);
if (st == 0)
return 0;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
ip = inet_addr(hostname);
if (ip == INADDR_NONE)
{
host = gethostbyname(hostname);
addr.sin_addr = *((struct in_addr*)(host->h_addr_list[0]));
}
else
{
addr.sin_addr.s_addr = inet_addr(hostname);
}
if (connect(st, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
printf("连接失败\n");
return 0;
}
else
{
return st;
}
}
void socket_close()
{
closesocket(sockfd);
WSACleanup();
}
仅仅这三个文件我们就可以实现一个小小的发送邮件的程序了。