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