Erlang SMTP发送邮件
https://github.com/Leptune/erlang_email
如果使用QQ邮箱 要先开通IMAP/SMTP 服务 获取授权码 秘密就填写获取到的授权码
email.erl
-module(email). -include("email.hrl"). -export([send/1]). %-compile(export_all). -define(MAX_SIZE, 1024). -define(DE, io:format("~p:~p~n", [?FILE, ?LINE])). %% send email by email record send(Email) when undefined =/= Email#email.server_ip, undefined =/= Email#email.account, undefined =/= Email#email.to_emails, undefined =/= Email#email.password -> ServerPort = case Email#email.server_port of undefined -> case Email#email.ssl of true -> ?SSL_SERV_PORT_DEF; false -> ?NOT_SSL_SERV_PORT_DEF end; Any -> Any end, Sock = case Email#email.ssl of false -> {ok, Socket} = gen_tcp:connect(Email#email.server_ip, ServerPort, [binary, {active, false}, {packet, 0}]), #socket{type = tcp, sock = Socket}; true -> ok = ssl:start(), {ok, Socket} = ssl:connect(Email#email.server_ip, ServerPort, [binary, {active, false}, {packet, 0}], infinity), #socket{type = ssl, sock = Socket} end, connect_email(Sock, Email), send_email_head(Sock, Email), send_email_info(Sock, Email), send_email_data(Sock, Email), end_email(Sock), case Sock#socket.type of ssl -> ssl:close(Sock#socket.sock), ssl:stop(); tcp -> gen_tcp:close(Sock#socket.sock) end. %% connect your email connect_email(Sock, Email) -> send_socket(Sock, "HELO " ++ Email#email.account ++ "\r\n"), recv_socket(Sock), send_socket(Sock, "AUTH LOGIN\r\n"), recv_socket(Sock), send_socket(Sock, base64:encode(Email#email.account)), send_socket(Sock, "\r\n"), recv_socket(Sock), send_socket(Sock, base64:encode(Email#email.password)), send_socket(Sock, "\r\n"), recv_socket(Sock). %% send email head send_email_head(Sock, Email) -> send_socket(Sock, "MAIL FROM <" ++ Email#email.account ++ ">\r\n"), recv_socket(Sock), rcpt_to_emails(Sock, Email#email.to_emails), recv_socket(Sock). %% send email info send_email_info(Sock, Email) -> send_socket(Sock, "DATA\r\n"), recv_socket(Sock), send_socket(Sock, "FROM:<" ++ Email#email.account ++ ">\r\n"), recv_socket(Sock), Subject = unicode:characters_to_list(Email#email.subject), send_socket(Sock, "SUBJECT:"++ Subject ++ "\r\n"). %% send email data send_email_data(Sock, Email) when Email#email.text =/= undefined; Email#email.html =/= undefined; Email#email.attachment =/= undefined -> send_socket(Sock, "MIME-VERSION: 1.0\r\n"), send_socket(Sock, "CONTENT-TYPE: multipart/mixed; BOUNDARY=\"#BOUNDARY#\"\r\n"), send_socket(Sock, "\r\n"), case Email#email.text of undefined -> nothing_to_do; _ -> send_email_text("text/plain", Email#email.text, Sock) end, case Email#email.html of undefined -> nothing_to_do; _ -> send_email_text("text/html", Email#email.html, Sock) end, case Email#email.attachment of undefined -> nothing_to_do; _ -> send_email_attachment("application/msword", Email#email.attachment, Sock) end; send_email_data(_Sock, _Email) -> ok. end_email(Sock) -> send_socket(Sock, "\r\n.\r\n"), recv_socket(Sock), send_socket(Sock, "QUIT\r\n"), recv_socket(Sock). %% send email text send_email_text(Type, FilePath, Sock) -> send_socket(Sock, "--#BOUNDARY#\r\n"), send_socket(Sock, "CONTENT-TYPE: "), send_socket(Sock, Type), send_socket(Sock, "\r\n\r\n"), {ok, Fd} = file:open(FilePath, [binary, read]), send_file_to_email(Sock, Fd, -1), ok = file:close(Fd), send_socket(Sock, "\r\n\r\n"). %% send email other type send_email_attachment(_Type, [], _Sock) -> nothing_to_return; send_email_attachment(Type, [FilePath | Rest], Sock) -> send_socket(Sock, "--#BOUNDARY#\r\n"), send_socket(Sock, "CONTENT-TYPE: "), send_socket(Sock, Type), send_socket(Sock, "; NAME="), send_socket(Sock, misc:basename(FilePath)), send_socket(Sock, "\r\n"), send_socket(Sock, "CONTENT-TRANSFER-ENCODING: base64\r\n"), send_socket(Sock, "\r\n"), {ok, Fd} = file:open(FilePath, [binary, read]), io:format("Client: Send ~p to server....~n", [FilePath]), send_file_to_email(Sock, Fd, 0), ok = file:close(Fd), send_socket(Sock, "\r\n\r\n"), send_email_attachment(Type, Rest, Sock). %% send file send_file_to_email(Sock, Fd, Base64Flag) -> case file:read(Fd, ?MAX_SIZE) of {ok, Data} -> case Base64Flag of -1 -> ok = send(Sock, Data); 0 -> ok = send(Sock, base64:encode(Data)) end, send_file_to_email(Sock, Fd, Base64Flag); eof -> eof; {error, Reason} -> io:format("read failed: ~p~n", [Reason]) end. %% her email address rcpt_to_emails(_Sock, []) -> ok; rcpt_to_emails(Sock, [ToEmail | Rest]) -> send_socket(Sock, "RCPT TO <" ++ ToEmail ++ ">\r\n"), rcpt_to_emails(Sock, Rest). %% send socket send_socket(Sock, Data) when is_list(Data)-> send_socket(Sock, unicode:characters_to_binary(Data)); send_socket(Sock, Data) when is_binary(Data)-> io:format("Client: ~p~n", [Data]), ok = send(Sock, Data). %% recv socket recv_socket(Sock) -> case recv(Sock, 0) of {ok , Packet} -> io:format("Server: ~p~n", [binary_to_list(Packet)]); {error, Reason} -> io:format("Server: recv failed: ~p~n", [Reason]) end. %% send data to server via tcp or ssl send(Sock, Data) when Sock#socket.type =:= tcp -> gen_tcp:send(Sock#socket.sock, Data); send(Sock, Data) when Sock#socket.type =:= ssl -> ssl:send(Sock#socket.sock, Data). %% recv data to server via tcp or ssl recv(Sock, Opinion) when Sock#socket.type =:= tcp -> gen_tcp:recv(Sock#socket.sock, Opinion); recv(Sock, Opinion) when Sock#socket.type =:= ssl -> ssl:recv(Sock#socket.sock, Opinion).
-record(email, { server_ip , % (必填) 邮件服务器ip(如: "smtp.qq.com") account , % (必填) 你自己的邮箱名(如: "[email protected]") password , % (必填) 密码 to_emails , % (必填) 要发往的邮箱(格式:["","",...])(如: ["[email protected]", "[email protected]"]) server_port , % (可选) 要传整数。如ssl为true,则默认端口为465, 否则默认端口为25,也可以手动指定 ssl = true , % (可选) 是否需要ssl加密(true 或 false) subject = "", % (可选) 邮件标题 text , % (可选) (正文)将文本内容发往邮箱,text的值为存放该内容的文件路径(路径以'/'分隔,不要以'\\'分隔) html , % (可选) (正文)将网页内容发往邮箱,html值为该网页文件路径(注: 两个正文最多只能选一项。都选了只显示html) attachment % (可选) (附件)要发往邮箱的文件路径(格式:["", "",...],如:["test.doc","test.tar"])(注意后缀名要对,否则不能预览) }). -record(socket, {type, sock}). -define(SSL_SERV_PORT_DEF, 465). -define(NOT_SSL_SERV_PORT_DEF, 25).
-module(misc). -export([index/2, split_binary_by_digit/2, basename/1]). %% 取出二进制或列表里对应下标(从0开始)的值 index(Data, Index) when is_binary(Data) -> index(binary_to_list(Data), Index); index(Data, Index) when is_list(Data), is_integer(Index), Index >= 0, Index < length(Data) -> index(Data, Index, 0). index([D0 | _DRest], GivenIndex, CurrIndex) when GivenIndex =:= CurrIndex -> D0; index([_D0 | DRest], GivenIndex, CurrIndex) -> index(DRest, GivenIndex, CurrIndex + 1). %% 将二进制Bin按数字Digit分隔成左右两个二进制数 split_binary_by_digit(Bin, Digit) when is_binary(Bin),is_integer(Digit) -> split_binary_by_digit(Bin, Digit, <<>>). split_binary_by_digit(<<>>, _Digit, _Left) -> {error, unmatched}; split_binary_by_digit(<<N, Rest/binary>>, Digit, Left) when N =/= Digit -> split_binary_by_digit(Rest, Digit, <<Left/binary, N>>); split_binary_by_digit(<<_N, Rest/binary>>, _Digit, Left) -> {ok, Left, Rest}. %% 取出文件路径中的文件名 basename(FilePath) when is_list(FilePath) -> case lists:member($/, FilePath) of false -> FilePath; true -> basename(lists:reverse(FilePath), []) end. basename([C | _Rest], BaseName) when C =:= $/ -> BaseName; basename([C | Rest], BaseName) -> basename(Rest, [C | BaseName]).
示例
%%TarMail 发送到的邮箱地址 ,Title 邮件标题 , Context 邮件内容 send_mail(TarMail,Title,Context) -> FileName = "lastEmailContext.txt", {ok,NowPath}=file:get_cwd(), FileDir = NowPath ++"/../include/", FilePath = FileDir++FileName, case filelib:is_dir(FileDir) of false-> ?Log([FileDir,"does not exist"]); true -> case file:open(FilePath, write) of {ok,Conf} -> io:format(Conf, "~s", [Context]), file:close(Conf); _ -> ?Log(["open file failed",FilePath]) end end, email:send(#email{server_ip = "smtp.qq.com", account = "[email protected]", password = ">XXXXXXX", subject = Title, text = FilePath, %html = "testfiles/test.html", %attachment = ["testfiles/test.doc", "testfiles/test.html", "testfiles/test.tar", "testfiles/test.txt"], to_emails = [TarMail] }).