diff --git a/calexium_lib/mail/lexers/enhanced_status.anubis b/calexium_lib/mail/lexers/enhanced_status.anubis new file mode 100644 index 0000000..d308513 --- /dev/null +++ b/calexium_lib/mail/lexers/enhanced_status.anubis @@ -0,0 +1,355 @@ +/* + * + * User: David RENE + * Date: 30/05/2008 + * Time: 03:30 + * (c) Calexium + * + */ + + + + +read tools/basis.anubis + + + We want to test email addresses. Below is a regular expression for that + purpose. Actually, this expression is too nave. A real one would be more complicated. + + +read tools/streams.anubis +read system/string.anubis + +type LM_TokenOrError_EnhancedStatus: + end_of_file, + token(List(Word8)), + error. + +type LM_LexerState_EnhancedStatus: ... + +type LM_Match_EnhancedStatus: + match(List(Word8) characters, + (LM_LexerState_EnhancedStatus,List(Word8)) -> (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) action, + Bool aeol, + Bool abol). + +type LM_LexerState_EnhancedStatus: + lexer_state(Stream input, + List(Word8) unput, // in natural order + List(Word8) more, // in reverse order + LM_LexerState_EnhancedStatus -> (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) lexer, + Bool at_end_of_line, + Bool at_beginning_of_line, + Maybe(LM_Match_EnhancedStatus) match). + +define LM_LexerState_EnhancedStatus + lm_initial_state + ( + Stream input, + LM_LexerState_EnhancedStatus -> (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) lexer + ) = + lexer_state(input,[],[],lexer,false,true,failure). + +define (LM_LexerState_EnhancedStatus,Word8) + lm_next_char + ( + LM_LexerState_EnhancedStatus ls + ) = + if ls is lexer_state(input,unput,more,lex,aeol,abol,match) then + if unput is + { + [ ] then + if read_byte(input) is + { + failure then + (lexer_state(input, + [], + more, + lex, + true, + aeol, + match), + -1), + + success(c) then + (lexer_state(input, + [], + [c . more], + lex, + c = '\n', + aeol, + match), + c), + }, + + [h . t] then + (lexer_state(input, + t, + [h . more], + lex, + h = '\n', + aeol, + match), + h) + }. + +define LM_LexerState_EnhancedStatus + lm_remember_match + ( + (LM_LexerState_EnhancedStatus,List(Word8)) -> (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) action, + LM_LexerState_EnhancedStatus ls + ) = + if ls is lexer_state(input,unput,more,lex,aeol,abol,m) then + with chars = if m is + { + failure then [], + success(match) then + if match is match(chars,_,_,_) then chars + }, + lexer_state(input, + unput, + [], + lex, + aeol, + abol, + success(match(append(more,chars),action,aeol,abol))). + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + lm_find_match + ( + LM_LexerState_EnhancedStatus ls + ) = + if ls is lexer_state(input,unput,more,lex,_,_,mb_m) then + if mb_m is + { + failure then (ls,error), + success(m) then + if m is match(chars,action,aeol,abol) then + action(lexer_state(input, + append(more,unput), + [], + lex, + aeol, + abol, + failure), + reverse(chars)) + }. + +define LM_LexerState_EnhancedStatus + change_lexer + ( + LM_LexerState_EnhancedStatus ls, + LM_LexerState_EnhancedStatus -> (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) lex + ) = + if ls is lexer_state(input,unput,more,_,aeol,abol,mb_m) then + lexer_state(input,unput,more,lex,aeol,abol,mb_m). + +define LM_LexerState_EnhancedStatus + clear_abol + ( + LM_LexerState_EnhancedStatus ls + ) = + if ls is lexer_state(input,unput,more,lex,aeol,_,mb_m) then + lexer_state(input,unput,more,lex,aeol,false,mb_m). + + Declarations of all lexer states. + + +public define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_state_1(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_state_2(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_state_3(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_state_4(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_state_5(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_state_6(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_state_7(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_state_8(LM_LexerState_EnhancedStatus ls). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_action_0 + (LM_LexerState_EnhancedStatus ls, List(Word8) text). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_action_1 + (LM_LexerState_EnhancedStatus ls, List(Word8) text). +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) enhanced_status_tester_action_2 + (LM_LexerState_EnhancedStatus ls, List(Word8) text). + + + Lexer states. + + +public define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester + ( + LM_LexerState_EnhancedStatus ls, + ) = + if at_beginning_of_line(ls) + then enhanced_status_tester_state_3(clear_abol(ls)) else + if lm_next_char(ls) is (ls,c) then + if c = -1 then enhanced_status_tester_state_1(ls) else + if c = 50 then enhanced_status_tester_state_4(ls) else + if (52 +=< c & c +=< 53) then enhanced_status_tester_state_4(ls) else + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_state_1 + ( + LM_LexerState_EnhancedStatus ls, + ) = + with ls = lm_remember_match(enhanced_status_tester_action_2,ls), + if lm_next_char(ls) is (ls,c) then + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_state_2 + ( + LM_LexerState_EnhancedStatus ls, + ) = + if lm_next_char(ls) is (ls,c) then + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_state_3 + ( + LM_LexerState_EnhancedStatus ls, + ) = + with ls = lm_remember_match(enhanced_status_tester_action_1,ls), + if lm_next_char(ls) is (ls,c) then + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_state_4 + ( + LM_LexerState_EnhancedStatus ls, + ) = + if lm_next_char(ls) is (ls,c) then + if c = 46 then enhanced_status_tester_state_5(ls) else + if c = 50 then enhanced_status_tester_state_4(ls) else + if (52 +=< c & c +=< 53) then enhanced_status_tester_state_4(ls) else + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_state_5 + ( + LM_LexerState_EnhancedStatus ls, + ) = + if lm_next_char(ls) is (ls,c) then + if c = 46 then enhanced_status_tester_state_5(ls) else + if (48 +=< c & c +=< 57) then enhanced_status_tester_state_6(ls) else + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_state_6 + ( + LM_LexerState_EnhancedStatus ls, + ) = + if lm_next_char(ls) is (ls,c) then + if c = 46 then enhanced_status_tester_state_7(ls) else + if (48 +=< c & c +=< 57) then enhanced_status_tester_state_6(ls) else + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_state_7 + ( + LM_LexerState_EnhancedStatus ls, + ) = + if lm_next_char(ls) is (ls,c) then + if c = 46 then enhanced_status_tester_state_7(ls) else + if (48 +=< c & c +=< 57) then enhanced_status_tester_state_8(ls) else + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_state_8 + ( + LM_LexerState_EnhancedStatus ls, + ) = + with ls = lm_remember_match(enhanced_status_tester_action_0,ls), + if lm_next_char(ls) is (ls,c) then + if (48 +=< c & c +=< 57) then enhanced_status_tester_state_8(ls) else + lm_find_match(ls). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_action_0 + ( + LM_LexerState_EnhancedStatus ls, + List(Word8) text + ) = + (ls,token(text)). + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_action_1 + ( + LM_LexerState_EnhancedStatus ls, + List(Word8) text + ) = + /* default action */ enhanced_status_tester(ls) . + + +define (LM_LexerState_EnhancedStatus,LM_TokenOrError_EnhancedStatus) + enhanced_status_tester_action_2 + ( + LM_LexerState_EnhancedStatus ls, + List(Word8) text + ) = + /* default action */ (ls,end_of_file) . + + + Since '@' is a normal character, a string needs to contain exactly one '@' for being + accepted. What is accepted before and after this '@' is described by: + + [a-zA-Z]+(\.[a-zA-Z]+)* + + The first part: [a-zA-Z]+ means ``at least one letter''. The last part: (\.[a-zA-Z]+)* + means: ``a dot followed by at least one letter, and this may be repeated any number of + times (including zero)''. + + + This part of the source file is the 'postambule' (just Anubis text, which is copied 'as + is' to the lexer_maker output file). + + The above stuff produces a function named 'email_tester' into the lexer_maker output + file. This function is used below: + +define Maybe(String) + _extract_enhanced_status + ( + List(String) lines + ) = + if lines is + { + [ ] then failure, + [h . t] then + with ls = lexer_state(make_stream(h),[],[],enhanced_status_tester,true,false,failure), + if enhanced_status_tester(ls) is (_,result) then + if result is + { + end_of_file then _extract_enhanced_status(t), + token(tok) then success(implode(tok)) + error then _extract_enhanced_status(t) + } + }. + +public define Maybe(String) + extract_enhanced_status + ( + List(String) lines + ) = + if lines is + { + [ ] then failure, + [h . t] then + if _extract_enhanced_status(split_by_token(h, ' ')) is + { + failure then extract_enhanced_status(t), + success(r) then success(r) + } + }. + + diff --git a/calexium_lib/mail/send_mail.anubis b/calexium_lib/mail/send_mail.anubis new file mode 100644 index 0000000..894a070 --- /dev/null +++ b/calexium_lib/mail/send_mail.anubis @@ -0,0 +1,537 @@ +/* + * Created by PyramIDE. + * User: ricard + * Date: 22/12/2008 + * Time: 17:49 + * + */ + + +read tools/basis.anubis +read tools/connections.anubis +read tools/findstring.anubis +read tools/printable_tree.anubis +read data_base/sqlite.anubis +read network/tools.anubis +read system/files.anubis +read system/string.anubis +read system/data_io.anubis +read system/logger.anubis + +read calexium_lib/net_services_protocols/logger_service.anubis + +read lexers/enhanced_status.anubis +read smtp_server_extensions.anubis + +public define String send_mail_log = "SendMail". +public define LogMask send_mail_mask = logMask("send_mail"). + +define Int smtp_time_out = 300. //5 minutes of timeout +define ByteArray crlf_dot_crlf = to_byte_array(implode([13,10,'.',13,10])). + +public type SendMailResult: + ok, + error, + reply(Int code, String enhanced_status, List(String) lines). + +public type Send_Status: + ready, //ready to send + finished, //the mail is delivered correctly + timeout, //the mail was not sent in enough time, this is a permanent error + general_error, + error(Int code, String enhanced_status, String text), //this is SMTP permanent error 5xx, we can't deliver the mail + warning(Int code, String enhanced_status, String text), //this is SMTP temporary error 4xx, we can retry with same mail later + in_progress. //We are in sending process, it's like a lock + +public type Str_HostName: + str_host_name(String str). + +public type Str_Sender: + str_sender(String str). + +public type Str_Recipient: + str_recipient(String str). + +public type Str_UID: + str_UID(String str). + +public type Send_Auth: + none, + login( String login, + String password). + +public type Send_Param: + send_param( Str_HostName host_name, + Str_Sender sender, + Str_Recipient recipient, + List(Data_IO) mail_parts, + Send_Auth auth, + Str_UID mail_uid + ). + +public type Send_Timeout: + send_timeout(Int when). + +public type Send_Tries: + send_tries(Int number). + + + + +public type Send_Mail_Session: + send_mail_session( + Bool enhanced_status, + Int size + ). + +define Maybe(String) + receive_command + ( + RWStream conn, + List(Word8) so_far, + Word8 previous + ) = + if read_network_byte(weaken(conn), smtp_time_out) is + { + failure then logTrace(send_mail_log, send_mail_mask, "receive_command read_network_byte 0 failure");failure, + timeout then logWarning(send_mail_log, "[send_mail] receive_command timeout"); failure, + success(c) then + if c = 10 & previous = 13 then // + with result = implode(reverse(so_far)), + logTrace(send_mail_log, send_mail_mask,"<-S-"+result); + success(result) + else + if previous = 13 then + receive_command(conn, so_far, c) + else + receive_command(conn, [previous . so_far], c) + }. + +define Maybe(String) + receive_command + ( + RWStream conn, + List(Word8) so_far, + ) = + if read_network_byte(weaken(conn), smtp_time_out) is + { + failure then logTrace(send_mail_log, send_mail_mask, "receive_command read_network_byte 0 failure"); failure, + timeout then logWarning(send_mail_log, "[send_mail] receive_command timeout"); failure, + success(c) then receive_command(conn,[], c) + }. + + +type Reply_Result: + error, + reply(Int code, List(String) lines). + +define SendMailResult + reply_handling + ( + Int code, + List(String) lines, + Bool enhanced_status + ) = + with result = + if enhanced_status then + if extract_enhanced_status(lines) is + { + failure then "", + success(enh) then enh + } + else + "", + reply(code, result, lines). + + +define Reply_Result + receive_reply + ( + RWStream conn, + List(String) so_far + ) = + if receive_command(conn, []) is + { + failure then logError(send_mail_log, "receive_reply: error receiving command"); error, + success(line) then + //check if we must read another line by presence of hyphen after the reply code + //220-bla bla bla + //220 end of bla bla + if length(line) =< 3 then + if decimal_scan(line) is + { + failure then logError(send_mail_log, "receive_reply: can't extract reply code from '" + line + "'"); error, + success(code) then reply(code, reverse(so_far)) + } + else if nth(3, line) is + { + failure then logError(send_mail_log, "receive_reply: error getting 4th character from '" + line +"'"); error, + success(char) then + if char = '-' then + receive_reply(conn, [line . so_far]) + else + //decode the code + if sub_string(line, 0, 3) is + { + failure then logError(send_mail_log, "receive_reply: error extracting reply code from '" + line+"'"); error, // should never occure + success(code_str) then + if decimal_scan(code_str) is + { + failure then logError(send_mail_log, "receive_reply: can't extract reply code from '" + code_str + "'"); error, //unreadable code + success(code) then reply(code , reverse([line . so_far])) + } + } + } + }. + +define Reply_Result + receive_reply + ( + RWStream conn, + ) = + receive_reply(conn, []). + + + Sending a piece of text (String) from the begining. + +define Maybe(One) + smtp_send_line + ( + RWStream conn, + String text + ) = + if reliable_write(tcp(conn),[text + crlf]) is + { + failure then logError(send_mail_log, "smtp_send_line: error writing '" + text + "'"); failure, + success(_) then + logTrace(send_mail_log,send_mail_mask, "-C->"+text);success(unique) + } . + + Because of attachements, we may have to manipulate very big pieces of text. It would be + unreasonable to concatenate all pieces into a single String. Hence, we use the + following type: + +type StringTree: + [ ], + str_tree(String,StringTree), + ba_tree(ByteArray,StringTree), + tree_tree(StringTree,StringTree). + +define StringTree [String s . StringTree t] = str_tree(s,t). +define StringTree [ByteArray s . StringTree t] = ba_tree(s,t). +define StringTree [StringTree s . StringTree t] = tree_tree(s,t). + + Try to send the 'HELO' command and get the reply. Return 'true' if you cannot. + +define Reply_Result + send_ehlo + ( + RWStream conn, + String our_host_name + ) = + if smtp_send_line(conn,"EHLO "+our_host_name) is + { + failure then logError(send_mail_log, "send_ehlo: error sending EHLO"); error, + success(_) then + with rep = receive_reply(conn), + if rep is + { + error then logError(send_mail_log, "send_ehlo: can't get reply"); error, + reply(code, lines) then + //we manage the 500 error, that mean the remote server is not ESMTP + //hence we try we with HELO, the old manner RFC 821 + if code = 500 | code = 502 then + if smtp_send_line(conn,"HELO "+our_host_name) is + { + failure then logError(send_mail_log, "send_ehlo: error sending HELO"); error, + success(_) then receive_reply(conn) + } + else + rep + } + }. + + The same one for 'MAIL FROM': + +define Reply_Result + send_mail_from + ( + RWStream conn, + String sender + ) = + if smtp_send_line(conn,"MAIL FROM:"+sender) is + { + failure then logError(send_mail_log, "error sending 'MAIL FROM:" + sender + "'"); error, + success(_) then receive_reply(conn) + }. + + The same one for 'RCPT TO': + +define Reply_Result + send_recipient + ( + RWStream conn, + String recipient, + ) = + if smtp_send_line(conn,"RCPT TO:<"+recipient+">") is + { + failure then logError(send_mail_log, "error sending 'RCPT TO:<"+recipient+">'"); error, + success(_) then receive_reply(conn) + }. + + + Try to send 'DATA' and get the reply. Answer 'true' if you cannot. + +define Reply_Result + send_data + ( + RWStream conn + ) = + if smtp_send_line(conn,"DATA") is + { + failure then logError(send_mail_log, "error sending DATA"); error, + success(_) then receive_reply(conn) + }. + + + + The same for the content of the message. We need base64 encoding. + +read tools/base64.anubis + +define Reply_Result + send_content + ( + RWStream conn, + List(Data_IO) mail_part + ) = + //open the message file from the drive + if copy_Data_IO_List_to_Stream(mail_part, weaken(conn)) is copy_ok(_) then + if reliable_write(tcp(conn), [crlf_dot_crlf]) is + { + failure then logError(send_mail_log, "send_content: error writing CRLF.CRLF"); error, + success(_) then receive_reply(conn) + } + else + logError(send_mail_log, "send_content: error sending data"); error. + + + Almost the same for 'QUIT': + +define One + send_quit + ( + RWStream conn + ) = + if smtp_send_line(conn,"QUIT") is + { + failure then logWarning(send_mail_log, "error sending QUIT"), + success(_) then forget(receive_reply(conn)) + }. + +define SendMailResult + sm_MAIL_FROM + ( + RWStream conn, + Send_Param param, + Send_Mail_Session sm_session + ) = + with enhanced_status = sm_session.enhanced_status, + //send MAIL FROM + if send_mail_from(conn, param.sender.str) is + { + error then error, // already logged + reply(code, lines) then + if code = 250 then + //send RCPT TO + if send_recipient(conn,param.recipient.str) is + { + error then error, // already logged + reply(code, lines) then + if code = 250 then + //send DATA + if send_data(conn) is + { + error then error, // already logged + reply(code, lines) then + if code = 354 then + if send_content(conn, param.mail_parts) is + { + error then error, // already logged + reply(code, lines) then + if code = 250 then + send_quit(conn); + logInfo(send_mail_log,"SENT Mail FROM "+param.sender.str+" TO "+param.recipient.str); + ok + else + reply_handling(code, lines, enhanced_status) + } + else + reply_handling(code, lines, enhanced_status) + } + else + reply_handling(code, lines, enhanced_status) + } + else + reply_handling(code, lines, enhanced_status) + } +. + +define List(String) + get_auth_method + ( + List(String) lines, + List(String) so_far + )= + if lines is + { + [] then so_far, + [h . t] then + if sub_string(h, 4, 4) is + { + failure then get_auth_method(t, so_far), + success(s) then + with current = if insensitive_equal(s, "AUTH") then + force_Type(list_word(h,9),[]) + else + [], + get_auth_method(t, current + so_far) + } + }. + +define SendMailResult + do_auth_plain + ( + RWStream conn, + String login, + String password, + Bool enhanced_status + )= + + with plain_str = "AUTH PLAIN "+ to_string(base64_encode(to_byte_array(login)+ + constant_byte_array(1,0)+ + to_byte_array(login)+ + constant_byte_array(1,0)+ + to_byte_array(password))), + if smtp_send_line(conn, plain_str) is + { + failure then error, // already logged + success(_) then + if receive_reply(conn) is + { + error then error // already logged + reply(code, lines) then + if code = 235 then + ok + else + reply_handling(code,lines,enhanced_status) + } + }. + +define SendMailResult + do_login + ( + RWStream conn, //tcp connection + List(String) auth_list, //list of available authentication method + String login, + String password, + Bool enhanced_status + )= + + if member(auth_list, "PLAIN", insensitive_equal) then + do_auth_plain(conn, login, password, enhanced_status) + else + logError(send_mail_log, "do_login no PLAIN method available"); error. + +define SendMailResult + do_auth + ( + RWStream conn, //tcp connection + List(String) lines, //this is the reply lines provided in response to EHLO command + Send_Auth auth, //authentication method to use for that session + Bool enhanced_status + )= + if auth is + { + none then ok, //no need to authenticate, this is a "panties festival" + login(user, password) then + with auth_list = get_auth_method(lines,[]), + if auth_list is + { + [] then logError(send_mail_log, "do_auth error");error, + [_ . _] then do_login(conn, auth_list, user, password, enhanced_status) + } + }. + + /** + * make_sm_session + */ +define Send_Mail_Session + make_sm_session + ( + List(String) lines, //lines given in ehlo stage + Send_Param param + )= + //looking for ENHANCEDSTATUSCODES + with session_enhanced_status = has_smtp_extension(lines, enhanced_status_codes), + //looking for SIZE + with session_size = if get_smtp_extension_value(lines, size) is + { + failure then 0, + success(str_value) then + if decimal_scan(str_value) is + { + failure then 0, + success(value) then + logDebug(send_mail_log, "smtp server with SIZE "+value); + value + } + }, + send_mail_session(session_enhanced_status, session_size). + + Apply the whole protocol: + + //TODO we can have a list of part file in param type. This can be very useful when we want + //to send file as mailing. For the mailing, in each mail, only the header is different of the + //other mails. The body part is the same. Then, the mailing sender can generate the header in one + //file and keep the body in other file wich can be given to send_mail function as last file in the list +public define SendMailResult + send_mail + ( + RWStream conn, + Send_Param param, + (Send_Mail_Session, Send_Param) -> Result(SendMailResult, Send_Param) prepare_mail_callback, + ) = + // + // Our connection to the SMTP server is opened. We just have to apply the protocol. + // + if receive_reply(conn) is + { + error then error,// already logged + reply(code, lines) then + if code = 220 then + //send EHLO + if send_ehlo(conn, param.host_name.str) is //"mail."+force_Type(get_main_domain(db), "mailfountain.net")) is + { + error then error, // already logged + reply(code, lines) then + if code = 250 then + // parse all available options here, and build a smtp context + with sm_session = make_sm_session(lines, param), + with auth_result = do_auth(conn, lines, param.auth, sm_session.enhanced_status), + if auth_result = ok then + if prepare_mail_callback(sm_session, param) is + { + error(result) then result, + ok(new_param) then sm_MAIL_FROM(conn, new_param, sm_session) + } + else + auth_result + else + reply_handling(code,lines,true) + } + else + //some server answer "550 5.7.1 Client host rejected: cannot find your reverse hostname, [88.181.64.17]" + //before anything, then we try to extract the enhanced status if exists for relaying the mail with the ISP + reply_handling(code,lines,true) + }. diff --git a/calexium_lib/mail/smtp_server_extensions.anubis b/calexium_lib/mail/smtp_server_extensions.anubis new file mode 100644 index 0000000..3482d35 --- /dev/null +++ b/calexium_lib/mail/smtp_server_extensions.anubis @@ -0,0 +1,90 @@ +/* + * + * User: David RENE + * Date: 30/05/2008 + * Time: 01:18 + * (c) Calexium + * + */ + +read tools/basis.anubis +read system/string.anubis + +public type SMTP_Extension_verb: + smtp_extension_verb + ( + String verb + ). + +public define SMTP_Extension_verb enhanced_status_codes = smtp_extension_verb("ENHANCEDSTATUSCODES"). +public define SMTP_Extension_verb size = smtp_extension_verb("SIZE"). + +define Bool + has_smtp_extension + ( + List(String) lines, //left lines given in ehlo stage + Int length, + String search_ext + )= + if lines is + { + [] then false, + [h . t] then + if sub_string(h, 4, length) is + { + failure then has_smtp_extension(t, length, search_ext), + success(s) then + if insensitive_equal(s, search_ext) then + true + else + has_smtp_extension(t, length, search_ext), + } + }. + +public define Bool + has_smtp_extension + ( + List(String) lines, //left lines given in ehlo stage + SMTP_Extension_verb search_ext + )= + has_smtp_extension(lines, length(search_ext.verb), search_ext.verb). + +define Maybe(String) + get_smtp_extension_value + ( + List(String) lines, //left lines given in ehlo stage + Int len, + String search_ext + )= + if lines is + { + [] then failure, + [h . t] then + if sub_string(h, 4, len) is + { + failure then get_smtp_extension_value(t, len, search_ext), + success(s) then + if insensitive_equal(s, search_ext) then + sub_string(h, 5+len, length(h)-(5+len)) + else + get_smtp_extension_value(t, len, search_ext), + } + }. + + /** + * return the value of wanted smtp extension name + * for "SIZE 10000" it return 10000. + * if the smtp verb doesn't exist, it return failure + */ + +public define Maybe(String) + get_smtp_extension_value + ( + List(String) lines, //left lines given in ehlo stage + SMTP_Extension_verb search_ext + )= + if has_smtp_extension(lines, length(search_ext.verb), search_ext.verb) then + get_smtp_extension_value(lines, length(search_ext.verb), search_ext.verb) + else + failure. + -- libgit2 0.21.4