1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 mail_subject
: string;
33 smtp_password
: string;
36 let rfc2047_encode h encoding s
=
37 let beginning = "=?" ^ encoding ^
"?q?" in
41 let maxlen = 75 in (* max lenght of a line *)
42 let buf = Buffer.create
1500 in
46 if x
>= 10 then Char.chr
(Char.code 'A'
+ x
- 10)
47 else Char.chr
(Char.code '
0'
+ x
) in
48 let copy tanga
= begin
49 Buffer.add_string
buf tanga
;
50 pos := !pos + String.length tanga
;
62 for i
=0 to (String.length s
)-1 do
63 let l = (!rl * (maxlen-String.length
ending)) - 1 in
64 if l < !pos then newline ();
66 | 'a'
..'z'
| 'A'
..'Z'
| '
0'
..'
9'
->
67 Buffer.add_char
buf s
.[i
]; incr
pos
68 | ' '
-> Buffer.add_char
buf '_'
; incr
pos
70 Buffer.add_char
buf '
='
;
71 Buffer.add_char
buf (hexa_digit (Char.code c
/ 16));
72 Buffer.add_char
buf (hexa_digit (Char.code c
mod 16));
78 let send oc s
= Printf.fprintf oc
"%s\r\n" s
; flush oc
79 let send1 oc s p
= Printf.fprintf oc
"%s %s\r\n" s p
; flush oc
81 let simple_connect hostname port
=
82 let s = socket PF_INET SOCK_STREAM
0 in
83 let h = Ip.from_name hostname
in
84 let addr = Ip.to_inet_addr
h in
86 Unix.connect
s (ADDR_INET
(addr,port
));
88 with e
-> close
s; raise e
90 let last_response = ref ""
93 failwith
(Printf.sprintf
"Bad response [%s]"
94 (String.escaped
!last_response))
96 type response
= int * bool * string list
99 last_response := input_line ic
;
100 if String.length
!last_response <= 3 then bad_response ();
101 if !last_response.[String.length
!last_response - 1] <> '
\r'
then bad_response ();
102 let final = match !last_response.[3] with ' '
-> true | '
-'
-> false | _
-> bad_response () in
103 let code = int_of_string
(String.sub
!last_response 0 3) in
104 let text = String.sub
!last_response 4 (String.length
!last_response - 5) in
107 let read_response ic
=
109 match get_response ic
with
115 let mail_address new_style
s = if new_style
then "<"^
s^
">" else s
117 let make_mail mail new_style
=
118 let mail_date = Date.mail_string
(Unix.time
()) in
120 "From: mldonkey %s\r\nTo: %s\r\n%s\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=utf-8\r\nDate: %s\r\n\r\n%s"
121 (mail_address new_style mail
.mail_from
)
123 (rfc2047_encode "Subject: " "utf-8" mail
.mail_subject
)
128 let len = String.length
s in
129 let rec iter_end s pos =
130 if pos = -1 then s else
131 if s.[pos] = ' '
then iter_end s (pos-1) else
132 iter_begin
s (pos-1) pos
133 and iter_begin
s pos last
=
134 if pos = -1 || s.[pos] = ' '
then
135 String.sub
s (pos+1) (last
- pos)
136 else iter_begin
s (pos-1) last
140 let string_xor s1 s2
=
141 assert (String.length s1
= String.length s2
);
142 let s = String.create
(String.length s1
) in
143 for i
= 0 to String.length
s - 1 do
144 s.[i
] <- Char.chr
(Char.code s1
.[i
] lxor Char.code s2
.[i
]);
148 (* HMAC-MD5, RFC 2104 *)
150 let ipad = String.make
64 '
\x36'
in
151 let opad = String.make
64 '
\x5C'
in
152 let md5 s = Md5.direct_to_string
(Md5.string s) in
153 fun secret challenge
->
154 let secret = if String.length
secret > 64 then md5 secret else secret in
155 let k = String.make
64 '
\x00'
in
156 String.blit
secret 0 k 0 (String.length
secret);
157 md5 (string_xor k opad ^
md5 (string_xor k ipad ^ challenge
))
159 let sendmail smtp_server smtp_port new_style mail
=
160 (* a completely synchronous function (BUG) *)
162 let s = simple_connect smtp_server smtp_port
in
163 (try Unix.setsockopt_float
s Unix.SO_RCVTIMEO
30. with _
-> ());
164 (try Unix.setsockopt_float
s Unix.SO_SNDTIMEO
30. with _
-> ());
165 let ic = in_channel_of_descr
s in
166 let oc = out_channel_of_descr
s in
167 let auth_login_enabled = ref false in
168 let auth_plain_enabled = ref false in
169 let auth_cram_enabled = ref false in
170 let read_response_auth ic =
172 let (n
,final,text) = get_response ic in
173 begin match String2.split_simplify
(String.uppercase
text) ' '
with
174 | ("AUTH"::methods
) ->
176 | "LOGIN" -> auth_login_enabled := true
177 | "PLAIN" -> auth_plain_enabled := true
178 | "CRAM-MD5" -> auth_cram_enabled := true
182 if final then n
else loop ()
188 if read_response ic <> 220 then bad_response ();
190 send1 oc "EHLO" (gethostname
());
191 if read_response_auth ic <> 250 then bad_response ();
193 if mail
.smtp_login
<> "" then
195 if !auth_cram_enabled then (* prefer CRAM-MD5 *)
197 send oc "AUTH CRAM-MD5";
198 match get_response ic with
201 let digest = hmac_md5 mail
.smtp_password
(Base64.decode
s) in
202 send oc (Base64.encode
(Printf.sprintf
"%s %s" mail
.smtp_login
digest));
203 if read_response ic <> 235 then bad_response ()
204 | _
-> bad_response ()
206 else if !auth_login_enabled then
208 send oc "AUTH LOGIN";
209 if read_response ic <> 334 then bad_response ();
211 send oc (Base64.encode mail
.smtp_login
);
212 if read_response ic <> 334 then bad_response ();
214 send oc (Base64.encode mail
.smtp_password
);
215 if read_response ic <> 235 then bad_response ()
217 else if !auth_plain_enabled then
219 let auth = Printf.sprintf
"\x00%s\x00%s" mail
.smtp_login mail
.smtp_password
in
220 send1 oc "AUTH PLAIN" (Base64.encode
auth);
221 if read_response ic <> 235 then bad_response ()
225 send1 oc "MAIL FROM:" (mail_address new_style
(canon_addr mail
.mail_from
));
226 if read_response ic <> 250 then bad_response ();
228 send1 oc "RCPT TO:" (mail_address new_style
(canon_addr mail
.mail_to
));
229 if read_response ic <> 250 then bad_response ();
232 if read_response ic <> 354 then bad_response ();
234 let body = make_mail mail new_style
in
237 if read_response ic <> 250 then bad_response ();
240 if read_response ic <> 221 then bad_response ();
245 if read_response ic <> 221 then bad_response ();
250 lprintf_nl
"Exception %s while sending mail" (Printexc2.to_string e
)