forked from ermine/sulci
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sulci.ml
236 lines (208 loc) · 6.46 KB
/
sulci.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
(*
* (c) 2004-2012 Anastasia Gornostaeva
*)
open StreamError
open JID
open Hooks
open Config
let _ = Printexc.record_backtrace true
module SimpleTransport =
struct
type 'a z = 'a UnitMonad.t
type fd = unit
type socket = {
inc : in_channel;
outc : out_channel;
}
let can_tls = false
let can_compress = false
let open_connection sockaddr =
let inc, outc = Unix.open_connection sockaddr in
{inc;
outc
}
let read s buf start len =
let size = input s.inc buf start len in
size
let write s str =
output_string s.outc str;
flush s.outc
let close s = close_in s.inc
end
module LogTraffic (T : XMPPClient.Socket)
(L : sig val logfile : out_channel end) =
struct
open UnitMonad
type t = T.t
let socket = T.socket
let read s buf start len =
let size = T.read s buf start len in
if size = 0 then (
output_string L.logfile "IN CLOSED\n";
flush L.logfile;
size
) else (
output_string L.logfile "IN: ";
output_string L.logfile (String.sub buf start size);
output_string L.logfile "\n";
flush L.logfile;
size
)
let write s str =
output_string L.logfile "OUT: ";
output_string L.logfile str;
output_string L.logfile "\n";
flush L.logfile;
T.write s str
let close = T.close
end
open XMPPClient
let session xmpp =
log#info "Connected to %s!" xmpp.myjid.domain;
XMPPClient.register_stanza_handler xmpp (ns_client, "message")
(XMPPClient.parse_message ~callback:message_callback
~callback_error:message_error);
XMPPClient.register_stanza_handler xmpp (ns_client, "presence")
(XMPPClient.parse_presence ~callback:presence_callback
~callback_error:presence_error);
Iq.features xmpp;
(* workaround for wildfire *)
send_presence xmpp ();
List.iter (fun proc -> try proc xmpp with exn ->
log#error "sulci.ml: %s" (Printexc.to_string exn);
log#debug "%s" (Printexc.get_backtrace ())
) (List.rev xmpp.user_data.on_connect)
let run account =
let myjid =
if account.resource = "" then
account.jid
else
replace_resource account.jid account.resource
in
let () = log#info "Creating a token for %s" (string_of_jid myjid) in
let host, port =
(if account.ip = "" then account.jid.domain else account.ip),
(match account.port with
| None -> 5222
| Some i -> i
)
in
let inet_addr =
try Unix.inet_addr_of_string host
with Failure("inet_addr_of_string") ->
(Unix.gethostbyname host).Unix.h_addr_list.(0) in
let sockaddr = Unix.ADDR_INET (inet_addr, port) in
let user_data = {
Hooks.deflang = !Lang.deflang;
Hooks.max_stanza_length = account.Config.max_stanza_length;
Hooks.max_message_length = account.Config.max_message_length;
on_connect = [];
on_disconnect = [];
presence_hooks = [];
message_hooks = [];
skey = "abc"
} in
Hooks.run_for_token [] user_data;
let rec reconnect times =
if times >= 0 then
let socket_data = SimpleTransport.open_connection sockaddr in
let module Socket_module =
struct
type t = SimpleTransport.socket
let socket = socket_data
include SimpleTransport
end in
let socket_module =
if account.rawxml_log = "" then
(module Socket_module : XMPPClient.Socket)
else
let module Socket_module =
struct
include LogTraffic(Socket_module)
(struct let logfile = open_out account.rawxml_log end)
end in
(module Socket_module : XMPPClient.Socket)
in
try
XMPPClient.setup_session
~user_data
~myjid
~plain_socket:socket_module
~password:account.password session >>= fun session_data ->
XMPPClient.parse session_data >>=
(fun () ->
let module S = (val session_data.socket : Socket) in
S.close S.socket
)
with
(*
| Unix.Unix_error (code, "connect", _) ->
log#info "Unable to connect to %s:%d: %s"
host port (Unix.error_message code);
if times > 0 then (
Unix.sleep reconnect_interval;
log#info "Reconnecting. Attempts remains: %d" times;
);
reconnect (times - 1)
*)
| Sasl.Failure cond ->
log#info "Auth.Failure: %s" cond;
(match cond with
| "non-authorized" ->
()
| _ ->
()
)
| Sasl.Error reason ->
log#crit "Authorization failed: %s" reason;
Pervasives.exit 127
| End_of_file ->
log#info"The connection to the server is lost";
List.iter (fun proc -> proc ()) (List.rev user_data.on_disconnect);
reconnect times
| StreamError err -> (
match err.err_condition with
| ERR_CONFLICT ->
log#info "Connection to the server closed: %s" err.err_text
| _ ->
log#info "The server reject us: %s: %s"
(string_of_condition err.err_condition) err.err_text
);
Pervasives.exit 127
| exn ->
log#error "sulci.ml: %s" (Printexc.to_string exn);
log#error "Probably it is a bug, please send me a bugreport";
log#debug "%s" (Printexc.get_backtrace ());
Pervasives.exit 127
in
reconnect account.reconnect_times
let rec launch r =
let pid = Unix.fork () in
if pid = 0 then
r ()
else
Printf.printf "Process %d detached" pid
let main accounts plugins () =
let () = Plugin.load_plugins plugins in
let account = List.hd accounts in
run account
let () =
let daemon, ((langdir, deflang), accounts, plugins, logging) =
Config.get_config () in
let () =
match logging with
| None -> ()
| Some (level, dst) ->
Hooks.log#set_max_level level;
Hooks.log#set_destination dst in
Lang.langdir := langdir;
Lang.deflang := deflang;
if accounts <> [] then
if daemon then (
ignore (Unix.setsid ());
launch (main accounts plugins)
)
else
main accounts plugins ()
else
Printf.eprintf "no account available"