物联网-手机远程控制家里的摄像头(2) - POP3和SMTP的C语言精简实现
在上一篇博客里面,使用了Python来发送、接收mail,但是实际测试中出现了一些不稳定的
情况,而且Python和即将使用的opencv会不兼容,使用进程间通讯或者其他方法会让整个系统
显得复杂而且可能不稳定,于是尝试用c或者C++实现邮件的发送和接收。
首先考虑的是上网找一个邮件库,找到了VMime库,于是开始安装。在简单看了一下它的文档之后
开始搭建它的环境,可惜要装的东西太多,搭建许久后放弃,而且它里面用了各种C++的特性,使用起来显得眼花缭乱
而且整个库太完整了,显得不够精简。
于是继续上github搜索邮件库,最后找到了两个邮件库:
https://github.com/leolanchas/Simple-POP3-Client
和 https://github.com/kenchowcn/smtp
其中,Simple-POP3-Client里面带有SMTP功能,可惜不支持附件,但是里面
的POP3功能可以跑,所以后续定制了一下。
smtp这份代码拿到后很惊喜,可以发送附件,于是测试之后就觉得可以直接用了(将它当做一个函数)
其中我设定的整个系统需求POP3的功能带有邮件检测,SMTP只要负责发邮件就可以了,所以进行了POP3的定制:
其中这两个库都是c库,改成C++的形式也是很容易的(解除一些错误即可),不行的话就用extern C来解决。
这里已经改好了POP3库,支持C++,其中里面主要分为两个文件:
pop3.cpp 这里是核心功能,负责定期检测邮件,根据邮件主题来传送一个
命令码给回调函数。
#include "pop3.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <assert.h> #include <ctype.h> #define error printf #define CRLF "\x0d\x0a.\x0d\x0a" #define CR "\x0d\x0a" #define SA struct sockaddr #define MAXLINE 8192 #define POP3_PORT 110 #define XIMAGE 100 #define XVIDEO 101 #define IMAGE 102 int checkConn( char * inServer, int port ) { // // Check that the username and password are valid // Display error message or that they have accessed correctly // int sockfd; struct sockaddr_in servaddr; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons( port ); inet_pton(AF_INET, inServer, &servaddr.sin_addr); if ( !connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) ) return sockfd; else return -1; } ssize_t readline(int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c; char *ptr; ptr = (char *)vptr; for (n = 1; n < maxlen; n++) { if ( (rc = recv(fd, &c, 1, 0)) == 1) { *ptr++ = c; if (c == '\n') break; } else if (rc == 0) { if (n == 1) return(0); /* EOF, no data read */ else break; /* EOF, some data was read */ } else return(-1); /* error */ } *ptr = 0; return(n); } ssize_t Readline(int fd, void *ptr, size_t maxlen) { ssize_t n; if ( (n = readline(fd, ptr, maxlen)) == -1) error("readline error"); return(n); } int checkUser( char * User, char * Pass, int sockfd) { char recvline[MAXLINE], cmdUser [ 60 ]; bzero(cmdUser,60); strcat( cmdUser, "user " ); strcat( cmdUser, User ); strcat( cmdUser, "\n" ); char cmdPass [ 60 ]; bzero(cmdPass,60); strcat( cmdPass, "pass " ); strcat( cmdPass, Pass ); strcat( cmdPass, "\n" ); if (Readline(sockfd, recvline, MAXLINE) == 0) error("checkUser: server terminated prematurely"); send(sockfd, cmdUser, strlen(cmdUser),0); if (Readline(sockfd, recvline, MAXLINE) == 0) error("checkUser: server terminated prematurely"); send(sockfd, cmdPass, strlen(cmdPass), 0 ); if (Readline(sockfd, recvline, MAXLINE) == 0) error("checkUser: server terminated prematurely"); if ( recvline[ 0 ] == '-' ) { fputs ( "\nUsuario o Contraseña incorrectos\n\n", stdout ); return 0; } return 1; } static char encode(unsigned char u) { if(u < 26) return 'A'+u; if(u < 52) return 'a'+(u-26); if(u < 62) return '0'+(u-52); if(u == 62) return '+'; return '/'; } void reverse(char s[]) { int i, j; char c; for (i = 0, j = strlen(s)-1; i<j; i++, j--) { c = s[i]; s[i] = s[j]; s[j] = c; } } void itoa(int n, char s[]) { int i, sign; if ((sign = n) < 0) /* record sign */ n = -n; /* make n positive */ i = 0; do { /* generate digits in reverse order */ s[i++] = n % 10 + '0'; /* get next digit */ } while ((n /= 10) > 0); /* delete it */ if (sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); } int getNumberOfRemoteMsgs( int sockfd ) { char recvline[ MAXLINE ]; bzero( recvline, MAXLINE ); char cmd[ MAXLINE ]; bzero( cmd, MAXLINE ); strcat( cmd, "stat\x0d\x0a" ); send( sockfd, cmd, strlen( cmd ), 0 ); if ( Readline( sockfd, recvline, MAXLINE ) == 0 ) error("getNumberOfMsgs: terminated prematurely"); int i = 0, j = 0; char number[ 6 ]; bzero( number, 6 ); //recvline example: "+OK 6 146378 while ( recvline[ i++ ] != ' ' ); while ( recvline[ i ] != ' ' ) number[ j++ ] = recvline[ i++ ]; return atoi( number ); } void parseRemoteHeaders(int sockfd, int *remote_command, int *delete_index) { const int NofMessages = getNumberOfRemoteMsgs( sockfd ); if( ! NofMessages ) { printf( "\n\tNo new mail\n" ); return; } char recvline[ MAXLINE ]; bzero( recvline, MAXLINE ); char cmd[ MAXLINE ]; bzero( cmd, MAXLINE ); char Subject[ MAXLINE ], Date[ MAXLINE ], From[ MAXLINE ]; int index = 1; char number[ 6 ]; int b = 1; while ( index != NofMessages + 1 ) { bzero( number, 6 ); itoa( index, number ); bzero( cmd, MAXLINE ); strcat( cmd, "top " ); strcat( cmd, number ); strcat( cmd, " 0\x0d\x0a" ); if ( b ) { send( sockfd, cmd, strlen( cmd ), 0 ); b = 0; } if ( Readline( sockfd, recvline, MAXLINE ) == 0 ) error("getHeaders: server terminated prematurely"); if (NULL != strstr(recvline, "Subject:")) if (strncmp(recvline, "Subject:", strlen("Subject:")) == 0) { printf("%s", recvline); if (NULL != strstr(recvline, "ximage")) { printf("will del:%s", recvline); *remote_command = XIMAGE; *delete_index = index; } else if (NULL != strstr(recvline, "xvideo")) { printf("will del:%s", recvline); *remote_command = XVIDEO; *delete_index = index; } else if (NULL != strstr(recvline, "image")) { printf("will del:%s", recvline); *remote_command = IMAGE; *delete_index = index; } } if (recvline[0] == '.') { index++; b = 1; } } } int delFromServer( int sockfd, int option ) { int N = getNumberOfRemoteMsgs( sockfd ); if ( ! N ) { printf( "Warning: inbox is empty !!!" ); return 0; } if ( N < option ) { printf( "There are just %d, Messages", N ); return 0; } char cmd[ 15 ], number[ 6 ], recvline[ MAXLINE ]; bzero( number, 6 ); bzero( cmd, 15 ); bzero( recvline, MAXLINE ); itoa( option, number ); strcat( cmd, "dele " ); strcat( cmd, number ); strcat( cmd, CR ); send( sockfd, cmd, strlen( cmd ), 0 ); if ( Readline( sockfd, recvline, MAXLINE ) == 0 ) error("delFromServer: terminated prematurely"); if ( recvline[ 0 ] == '+' ) printf ( "Message %d will be deleted when the session is ended\n", option ); else printf( "There have been errors when trying to delete the message" ); return 1; } int pop3_main(void (*event_handler)(int)) { printf( "Welcome to the pop3 client modified by tanhangbo\n\n" ); struct hostent *h; int delete_index = 0; int remote_command = 0; char pop3_server[] = POP3_SERVER; char smtp_server[] = SMTP_SERVER; char user_name[] = USER_NAME; char pass_word[] = PASS_WORD; char inServer[3*4 + 4 +1];//111.111.111.111\0 char outServer[3*4 + 4 + 1]; h = gethostbyname(pop3_server); strcpy(inServer , inet_ntoa(*((struct in_addr *)h->h_addr))); h = gethostbyname(smtp_server); strcpy(outServer , inet_ntoa(*((struct in_addr *)h->h_addr))); printf("POP3 IP:%s\n", inServer); printf("SMTP IP:%s\n", outServer); while(1) { delete_index = 0; remote_command = 0; int sockfd = checkConn( inServer, POP3_PORT ); if( sockfd == -1 ) return 0; if(!checkUser(user_name, pass_word, sockfd)) { printf("login fail!\n"); } parseRemoteHeaders(sockfd, &remote_command, &delete_index); if (0 != remote_command) event_handler(remote_command);//handle here!!!!!!! if (0 != delete_index) delFromServer(sockfd, delete_index); send( sockfd, "QUIT\x0d\x0a", strlen( "QUIT\x0d\x0a" ), 0 ); close(sockfd); sleep(1); } return 0; }
main.cpp
这是一个简单的sample:
#include "stdio.h" #include "pop3.h" void handlee(int remote_command) { //process command here printf("I want to handle %d\n", remote_command); } int main() { pop3_main(&handlee); }
这样设计的目的是为了将其放到opencv相关的代码里面,同时这个回调函数
会做一些相关的处理
当然还有一个头文件:
#ifndef POP3_TANHANGBO_H #define POP3_TANHANGBO_H #define POP3_SERVER "pop3.163.com" #define SMTP_SERVER "smtp.163.com" #define USER_NAME "user_" #define PASS_WORD "pass_" int pop3_main(void (*event_handler)(int)); #endif
测试的log:
tan@tan-desktop:~/app/pop3$ g++ main.cpp pop3.cpp tan@tan-desktop:~/app/pop3$ ./a.out Welcome to the pop3 client modified by tanhangbo POP3 IP:123.125.50.29 SMTP IP:123.58.178.203 Subject: warning Subject: test Subject: warning Subject: test Subject: warning Subject: test Subject: warning Subject: test Subject: warning Subject: test Subject: warning Subject: test Subject: warning Subject: test Subject: warning Subject: test Subject: warning Subject: test Subject: image will del:Subject: image I want to handle 102 Message 3 will be deleted when the session is ended Subject: warning Subject: test Subject: warning
目前的进度是已经完成了Opencv的动作检测代码,接下来会将这个邮件
功能和Opencv的代码连接起来,做一个稳定的基本功能,后续再进行功能的扩展。
另外在找邮件库的时候找到了MailCore: libmailcore.com
后续如果做得比较完美,可以考虑做一个ios客户端,这样就更方便了。