1 /* $Id: im_request.c 2832 2009-09-03 19:43:48Z kuhlmann $ */
2 /* Copyright: This file may be distributed under version 2 of the GPL licence. */
3
4 #include "climm.h"
5 #include <fcntl.h>
6 #include <stdarg.h>
7 #include <ctype.h>
8 #include <assert.h>
9 #include <limits.h>
10 #include "util_ui.h"
11 #include "datatype.h"
12 #include "im_request.h"
13 #include "util.h"
14 #include "im_response.h"
15 #include "oscar_snac.h"
16 #include "oscar_icbm.h"
17 #include "oscar_oldicq.h"
18 #include "oscar_bos.h"
19 #include "cmd_user.h"
20 #include "contact.h"
21 #include "connection.h"
22 #include "preferences.h"
23 #include "oscar_dc.h"
24 #include "packet.h"
25 #include "conv.h"
26 #include "oscar_service.h"
27 #include "oscar_roster.h"
28 #include "xmpp_base.h"
29 #include "oscar_base.h"
30 #include "msn_base.h"
31 #include "util_otr.h"
32 #include "remote.h"
33
34 static void CallbackMeta (Event *event);
35 static void CallbackTogvis (Event *event);
36
CallbackTogvis(Event * event)37 static void CallbackTogvis (Event *event)
38 {
39 if (!event)
40 return;
41 if (!event->cont || !event->conn || ContactPrefVal (event->cont, CO_INTIMATE)
42 || !ContactIsInv (event->conn->serv->status))
43 {
44 EventD (event);
45 return;
46 }
47 SnacCliRemvisible (event->conn->serv, event->cont);
48 EventD (event);
49 }
50
MsgC()51 Message *MsgC ()
52 {
53 Message *msg;
54
55 msg = calloc (1, sizeof *msg);
56 DebugH (DEB_MSG, "M" STR_DOT STR_DOT STR_DOT "> %p:", msg);
57 return msg;
58 }
59
MsgD(Message * msg)60 void MsgD (Message *msg)
61 {
62 char *s, *t;
63 if (!msg)
64 return;
65 DebugH (DEB_MSG, "M<" STR_DOT STR_DOT STR_DOT " %p [%s] %lu %lu %lu %u %u (%s) (%s) ",
66 msg, msg->cont ? msg->cont->screen : "",
67 UD2UL (msg->type), UD2UL (msg->origin), UD2UL (msg->trans), msg->otrinjected, msg->force,
68 msg->send_message ? msg->send_message : "(null)",
69 msg->plain_message ? msg->plain_message : "(null)");
70
71 s = msg->send_message;
72 t = msg->plain_message;
73 msg->send_message = NULL;
74 msg->plain_message = NULL;
75 free (msg);
76 s_free (s);
77 s_free (t);
78 }
79
cb_msg_log(Message * msg)80 Message *cb_msg_log (Message *msg)
81 {
82 putlog (msg->cont->serv, NOW, msg->cont, ims_online, 0,
83 msg->type == MSG_AUTO ? LOG_AUTO : LOG_SENT, msg->type,
84 msg->plain_message ? msg->plain_message : msg->send_message);
85 return msg;
86 }
87
88 #ifdef ENABLE_OTR
cb_msg_otr(Message * msg)89 Message *cb_msg_otr (Message *msg)
90 {
91 /* process outgoing messages for OTR */
92 if (msg->type != MSG_NORM || msg->otrinjected || !libotr_is_present)
93 return msg;
94
95 return OTRMsgOut (msg);
96 }
97 #endif
98
cb_msg_revealinv(Message * msg)99 Message *cb_msg_revealinv (Message *msg)
100 {
101 UDWORD reveal_time;
102 Event *event;
103
104 if (msg->cont->serv->type != TYPE_SERVER)
105 return msg;
106 if (msg->type & MSGF_GETAUTO)
107 return msg;
108 if (!ContactIsInv (msg->cont->serv->status))
109 return msg;
110 if (ContactPrefVal (msg->cont, CO_INTIMATE) || ContactPrefVal (msg->cont, CO_HIDEFROM))
111 return msg;
112 if (!(reveal_time = ContactPrefVal (msg->cont, CO_REVEALTIME)))
113 return msg;
114
115 event = QueueDequeue2 (msg->cont->serv->conn, QUEUE_TOGVIS, 0, msg->cont);
116 if (event)
117 EventD (event);
118 else
119 SnacCliAddvisible (msg->cont->serv, msg->cont);
120 QueueEnqueueData (msg->cont->serv->conn, QUEUE_TOGVIS, 0, time (NULL) + reveal_time, NULL, msg->cont, NULL, CallbackTogvis);
121 return msg;
122 }
123
124
IMCliMsg(Contact * cont,UDWORD type,const char * text,Opt * opt)125 UBYTE IMCliMsg (Contact *cont, UDWORD type, const char *text, Opt *opt)
126 {
127 Message *msg;
128 UBYTE ret = RET_FAIL;
129 UDWORD m_trans, m_force, m_otrinject;
130
131 if (!cont || !cont->serv || !(msg = MsgC ()))
132 {
133 OptD (opt);
134 return RET_FAIL;
135 }
136 if (!opt || !OptGetVal (opt, CO_MSGTRANS, &m_trans))
137 m_trans = CV_MSGTRANS_ANY;
138 if (!opt || !OptGetVal (opt, CO_FORCE, &m_force))
139 m_force = 0;
140 if (!opt || !OptGetVal (opt, CO_OTRINJECT, &m_otrinject))
141 m_otrinject = 0;
142 OptD (opt);
143
144 msg->cont = cont;
145 msg->type = type;
146 msg->send_message = strdup (text);
147 msg->trans = m_trans;
148 msg->force = m_force;
149 msg->otrinjected = m_otrinject;
150
151 #ifdef ENABLE_OTR
152 msg = cb_msg_otr (msg);
153 if (!msg)
154 return RET_FAIL;
155 #endif
156 msg = cb_msg_log (msg);
157 if (!msg)
158 return RET_FAIL;
159 ret = IMCliReMsg (cont, msg);
160 while (!RET_IS_OK(ret) && msg->maxsize && !msg->otrinjected && !msg->otrencrypted)
161 {
162 strc_t str = s_split (&text, msg->maxenc, msg->maxsize);
163 s_repl (&msg->send_message, str->txt);
164 msg->maxsize = 0;
165 msg->trans = m_trans;
166 ret = IMCliReMsg (cont, msg);
167 if (!RET_IS_OK (ret))
168 break;
169 msg = MsgC ();
170 if (!msg)
171 return RET_FAIL;
172 msg->cont = cont;
173 msg->type = type;
174 msg->send_message = strdup (text);
175 msg->trans = m_trans;
176 msg->force = m_force;
177 msg->otrinjected = m_otrinject;
178 ret = IMCliReMsg (cont, msg);
179 }
180 if (!RET_IS_OK(ret) && msg->maxsize && (msg->otrinjected || msg->otrencrypted))
181 {
182 str_s str = { NULL, 0, 0 };
183 UDWORD fragments, frag;
184 char *message = msg->send_message;
185
186 fragments = strlen (message) / msg->maxsize + 1;
187 frag = 1;
188 assert (fragments < 65536);
189 while (frag <= fragments)
190 {
191
192 s_init (&str, "", msg->maxsize);
193 s_catf (&str, "?OTR,%hu,%hu,", (unsigned short)frag, (unsigned short)fragments);
194 s_catn (&str, message, strlen (message) / (fragments + 1 - frag));
195 s_catc (&str, ',');
196 message += strlen (message) / (fragments + 1 - frag);
197 if (frag < fragments)
198 {
199 Message *msg2 = MsgC ();
200 if (!msg2)
201 {
202 MsgD (msg);
203 s_done (&str);
204 return RET_FAIL;
205 }
206 msg2->cont = cont;
207 msg2->type = type;
208 msg2->send_message = strdup (str.txt);
209 msg2->trans = CV_MSGTRANS_ANY;
210 msg2->otrinjected = 1;
211 ret = IMCliReMsg (cont, msg2);
212 if (!RET_IS_OK (ret))
213 {
214 MsgD (msg2);
215 MsgD (msg);
216 s_done (&str);
217 return RET_FAIL;
218 }
219 }
220 else
221 {
222 s_repl (&msg->send_message, str.txt);
223 msg->trans = m_trans;
224 msg->otrinjected = 0;
225 ret = IMCliReMsg (cont, msg);
226 if (!RET_IS_OK (ret))
227 {
228 MsgD (msg);
229 s_done (&str);
230 return RET_FAIL;
231 }
232 }
233 frag++;
234 }
235 s_done (&str);
236 return RET_OK;
237 }
238
239 if (!RET_IS_OK (ret))
240 MsgD (msg);
241 return ret;
242 }
243
IMCliReMsg(Contact * cont,Message * msg)244 UBYTE IMCliReMsg (Contact *cont, Message *msg)
245 {
246 UBYTE ret = RET_FAIL;
247
248 assert (cont);
249 assert (msg);
250 assert (msg->cont == cont);
251 assert (msg->send_message);
252
253 msg = cb_msg_revealinv (msg);
254 if (!msg)
255 return RET_FAIL;
256
257 #ifdef ENABLE_PEER2PEER
258 if (msg->trans & CV_MSGTRANS_DC)
259 {
260 if (cont->serv->oscar_dc)
261 if (RET_IS_OK (ret = PeerSendMsgFat (cont->serv->oscar_dc, cont, msg)))
262 return ret;
263 msg->trans &= ~CV_MSGTRANS_DC;
264 }
265 #endif
266 if (msg->trans & CV_MSGTRANS_TYPE2)
267 {
268 if (cont->serv->conn->connect & CONNECT_OK && cont->serv->type == TYPE_SERVER)
269 if (RET_IS_OK (ret = SnacCliSendmsg (cont->serv, cont, 2, msg)))
270 return ret;
271 msg->trans &= ~CV_MSGTRANS_TYPE2;
272 }
273 if (msg->trans & CV_MSGTRANS_ICQv8)
274 {
275 if (cont->serv->conn->connect & CONNECT_OK && cont->serv->type == TYPE_SERVER)
276 {
277 if (RET_IS_OK (ret = SnacCliSendmsg (cont->serv, cont, 1, msg)))
278 return ret;
279 if (RET_IS_OK (ret = SnacCliSendmsg (cont->serv, cont, 4, msg)))
280 return ret;
281 }
282 msg->trans &= ~CV_MSGTRANS_ICQv8;
283 }
284 #if ENABLE_XMPP
285 if (msg->trans & CV_MSGTRANS_XMPP)
286 if (cont->serv->conn->connect & CONNECT_OK && cont->serv->type == TYPE_XMPP_SERVER)
287 if (RET_IS_OK (ret = XMPPSendmsg (cont->serv, cont, msg)))
288 return ret;
289 #endif
290 return ret;
291 }
292
IMCliInfo(Server * serv,Contact * cont,int group)293 void IMCliInfo (Server *serv, Contact *cont, int group)
294 {
295 UDWORD ref = 0;
296 if (cont)
297 {
298 cont->updated = 0;
299 if (cont->serv->type == TYPE_SERVER)
300 ref = SnacCliMetareqinfo (cont->serv, cont);
301 }
302 else
303 {
304 if (serv->type == TYPE_SERVER)
305 ref = SnacCliSearchrandom (serv, group);
306 }
307 if (ref)
308 QueueEnqueueData (serv->conn, QUEUE_REQUEST_META, ref, time (NULL) + 60, NULL,
309 cont, NULL, &CallbackMeta);
310 }
311
CallbackMeta(Event * event)312 static void CallbackMeta (Event *event)
313 {
314 Contact *cont;
315
316 assert (event && event->conn && event->conn->serv && event->conn == event->conn->serv->conn);
317
318 cont = event->cont;
319 if (!cont)
320 EventD (event);
321 else if ((cont->updated != UP_INFO) && !(event->flags & QUEUE_FLAG_CONSIDERED))
322 QueueEnqueue (event);
323 else
324 {
325 UtilUIDisplayMeta (cont);
326 if (~cont->oldflags & CONT_ISEDITED)
327 ContactMetaSave (cont);
328 else
329 cont->updated &= ~UPF_DISC;
330 EventD (event);
331 }
332 }
333
IMSetStatus(Server * serv,Contact * cont,status_t status,const char * msg)334 void IMSetStatus (Server *serv, Contact *cont, status_t status, const char *msg)
335 {
336 assert (serv || cont);
337 if (cont)
338 {
339 #if ENABLE_XMPP
340 if (cont->serv->type == TYPE_XMPP_SERVER)
341 XMPPSetstatus (cont->serv, cont, status, msg);
342 #endif
343 }
344 else
345 {
346 if (~serv->conn->connect & CONNECT_OK)
347 {
348 serv->status = status;
349 serv->nativestatus = 0;
350 }
351 else if (serv->type == TYPE_SERVER)
352 SnacCliSetstatus (serv, status, 1);
353 #if ENABLE_XMPP
354 else if (serv->type == TYPE_XMPP_SERVER)
355 XMPPSetstatus (serv, NULL, status, msg);
356 #endif
357 }
358 }
359
IMCliAuth(Contact * cont,const char * text,auth_t how)360 void IMCliAuth (Contact *cont, const char *text, auth_t how)
361 {
362 assert (cont);
363 assert (cont->serv);
364 if (~cont->serv->conn->connect & CONNECT_OK)
365 return;
366
367 if (cont->serv->type == TYPE_SERVER)
368 {
369 UDWORD msgtype;
370 switch (how)
371 {
372 case auth_deny:
373 if (!text) /* FIXME: let it untranslated? */
374 text = "Authorization refused\n";
375 msgtype = MSG_AUTH_DENY;
376 break;
377 case auth_req:
378 if (!text) /* FIXME: let it untranslated? */
379 text = "Please authorize my request and add me to your Contact List\n";
380 text = s_sprintf ("\xfe%s", text);
381 msgtype = MSG_AUTH_REQ;
382 break;
383 case auth_add:
384 if (!text)
385 text = "\x03";
386 msgtype = MSG_AUTH_ADDED;
387 break;
388 default:
389 case auth_grant:
390 if (!text)
391 text = "\x03";
392 msgtype = MSG_AUTH_GRANT;
393 }
394 if (cont->uin)
395 {
396 Message *msg = MsgC ();
397 if (!msg)
398 return;
399 msg->cont = cont;
400 msg->type = msgtype;
401 msg->force = 1;
402 msg->send_message = strdup (text);
403 if (!RET_IS_OK (SnacCliSendmsg (cont->serv, cont, 4, msg)))
404 MsgD (msg);
405 }
406 {
407 if (how == auth_deny)
408 SnacCliAuthorize (cont->serv, cont, 0, text);
409 else if (how == auth_req)
410 SnacCliReqauth (cont->serv, cont, text);
411 else if (how == auth_add)
412 SnacCliGrantauth (cont->serv, cont);
413 else if (how == auth_grant)
414 SnacCliAuthorize (cont->serv, cont, 1, NULL);
415 }
416 }
417 #if ENABLE_XMPP
418 else if (cont->serv->type == TYPE_XMPP_SERVER)
419 XMPPAuthorize (cont->serv, cont, how, text);
420 #endif
421 }
422
IMLogin(Server * serv)423 Event *IMLogin (Server *serv)
424 {
425 switch (serv->type)
426 {
427 case TYPE_SERVER: return OscarLogin (serv);
428 #ifdef ENABLE_MSN
429 case TYPE_MSN_SERVER: return MSNLogin (serv);
430 #endif
431 #ifdef ENABLE_XMPP
432 case TYPE_XMPP_SERVER: return XMPPLogin (serv);
433 #endif
434 default: return NULL;
435 }
436 }
437
IMCallBackDoReconn(Event * event)438 static void IMCallBackDoReconn (Event *event)
439 {
440 if (!event || !event->conn)
441 {
442 EventD (event);
443 return;
444 }
445 assert (event->conn->serv);
446 QueueEnqueue (event);
447 IMLogin (event->conn->serv);
448 }
449
IMCallBackReconn(Connection * conn)450 void IMCallBackReconn (Connection *conn)
451 {
452 ContactGroup *cg;
453 Server *serv;
454 Event *event;
455 Contact *cont;
456 int i;
457
458 assert (conn);
459 assert (conn->serv);
460 assert (conn->serv->conn == conn);
461
462 serv = conn->serv;
463 cg = serv->contacts;
464
465 if (!(cont = conn->cont))
466 return;
467
468 if (!(event = QueueDequeue2 (conn, QUEUE_DEP_WAITLOGIN, 0, NULL)))
469 {
470 IMLogin (serv);
471 return;
472 }
473
474 conn->connect = 0;
475 rl_log_for (cont->nick, COLCONTACT);
476 if (event->attempts < 5)
477 {
478 rl_printf (i18n (2032, "Scheduling v8 reconnect in %d seconds.\n"), 10 << event->attempts);
479 event->due = time (NULL) + (10 << event->attempts);
480 event->callback = &IMCallBackDoReconn;
481 QueueEnqueue (event);
482 }
483 else
484 {
485 rl_print (i18n (2031, "Connecting failed too often, giving up.\n"));
486 EventD (event);
487 }
488 for (i = 0; (cont = ContactIndex (cg, i)); i++)
489 cont->status = ims_offline;
490 }
491
IMConnOpen(Connection * conn)492 void IMConnOpen (Connection *conn)
493 {
494 #ifdef ENABLE_REMOTECONTROL
495 if (conn->type == TYPE_REMOTE) ScriptingOpen (conn);
496 else
497 #endif
498 if (conn->type == TYPE_MSGDIRECT) ConnectionInitPeer (conn);
499 else if (conn->serv && conn->serv->conn == conn)
500 IMLogin (conn->serv);
501 else
502 rl_printf (i18n (2740, "Don't know how to open connection type %s for %s.\n"),
503 ConnectionStrType (conn), conn->cont->screen);
504 }
505
IMLogout(Server * serv)506 void IMLogout (Server *serv)
507 {
508 rl_printf (i18n (2741, "Logging off %s.\n"), serv->conn->cont->screen);
509 switch (serv->type)
510 {
511 case TYPE_SERVER: return OscarLogout (serv);
512 #ifdef ENABLE_MSN
513 case TYPE_MSN_SERVER: return MSNLogout (serv);
514 #endif
515 #ifdef ENABLE_XMPP
516 case TYPE_XMPP_SERVER: return XMPPLogout (serv);
517 #endif
518 default:
519 return;
520 }
521 }
522
523