1 ! Copyright (C) 2007 Elie CHAFTARI
2 ! See http://factorcode.org/license.txt for BSD license.
4 ! cram-md5 auth code contributed by Dirk Vleugels <dvl@2scale.net>
6 USING: alien alien.c-types combinators crypto.common crypto.hmac base64
7 kernel io io.sockets namespaces sequences splitting ;
11 ! =========================================================
12 ! smtp.factor implementation
13 ! =========================================================
15 ! Connection default values
16 : default-port 25 ; inline
17 : read-timeout 60000 ; inline
18 : esmtp t ; inline ! t = ehlo
19 : domain "localhost.localdomain" ; inline
25 TUPLE: session address port timeout domain esmtp ;
27 : <session> ( address -- session )
28 default-port read-timeout domain esmtp
29 session construct-boa ;
31 ! =========================================================
32 ! Initialization routines
33 ! =========================================================
35 : initialize ( address -- )
38 : set-port ( port -- )
39 sess get set-session-port ;
41 : set-read-timeout ( timeout -- )
42 sess get set-session-timeout ;
44 : set-esmtp ( esmtp -- )
45 sess get set-session-esmtp ;
48 host-name sess get set-session-domain ;
51 sess get [ session-address ] keep session-port <inet> <client>
52 dup conn set [ sess get session-timeout swap set-timeout ]
53 keep stream-readln print ;
55 ! =========================================================
57 ! =========================================================
59 : check-response ( response -- )
61 { [ dup "220" head? ] [ print ] }
62 { [ dup "235" swap subseq? ] [ print ] }
63 { [ dup "250" head? ] [ print ] }
64 { [ dup "221" head? ] [ print ] }
65 { [ dup "bye" head? ] [ print ] }
66 { [ dup "4" head? ] [ "server busy" throw ] }
67 { [ dup "334" head? ] [ " " split 1 swap nth base64> challenge set ] }
68 { [ dup "354" head? ] [ print ] }
69 { [ dup "50" head? ] [ print "syntax error" throw ] }
70 { [ dup "53" head? ] [ print "invalid authentication data" throw ] }
71 { [ dup "55" head? ] [ print "fatal error" throw ] }
72 { [ t ] [ "unknow error" throw ] }
77 : multiline? ( response -- boolean )
78 CHAR: - swap index 3 = ;
80 : process-multiline ( -- response )
81 conn get stream-readln dup
82 multiline get " " append head? [
85 check-response process-multiline
88 : recv-response ( -- response )
89 conn get stream-readln
91 dup 3 head multiline set process-multiline
94 : get-ok ( command -- )
95 >r conn get r> over stream-write stream-flush
96 recv-response check-response ;
99 "HELO " sess get session-domain append "\r\n" append get-ok ;
102 "EHLO " sess get session-domain append "\r\n" append get-ok ;
104 : mailfrom ( fromaddr -- )
105 "MAIL FROM:<" swap append ">\r\n" append get-ok ;
108 "RCPT TO:<" swap append ">\r\n" append get-ok ;
110 : (cram-md5-auth) ( -- response )
112 string>md5-hmac hex-string
113 " " swap append append
116 : cram-md5-auth ( key login -- )
117 "AUTH CRAM-MD5\r\n" get-ok
118 (cram-md5-auth) "\r\n" append get-ok ;
124 set-domain ! replaces localhost.localdomain with hostname
126 sess get session-esmtp [
132 : send-message ( msg -- )
134 "\r\n" join conn get swap "\r\n" append over stream-write
135 stream-flush ".\r\n" get-ok ;