1 /* $Id: im_response.c 2860 2010-03-16 20:36:48Z kuhlmann $ */
2 /* Copyright: This file may be distributed under version 2 of the GPL licence. */
3 
4 #include "climm.h"
5 #include <assert.h>
6 #include "im_response.h"
7 #include "oscar_base.h"
8 #include "util_rl.h"
9 #include "util_tabs.h"
10 #include "contact.h"
11 #include "im_request.h"
12 #include "util.h"
13 #include "conv.h"
14 #include "preferences.h"
15 #include "connection.h"
16 #include "util_otr.h"
17 
18 /** LOGGING **/
cb_status_log(Contact * cont,parentmode_t pm,change_t ch,const char * text)19 static int cb_status_log (Contact *cont, parentmode_t pm, change_t ch, const char *text)
20 {
21 #if ENABLE_CONT_HIER
22     if (cont->parent)
23         return 0;
24 #endif
25     assert (cont->serv);
26     putlog (cont->serv, NOW, cont, cont->status, cont->nativestatus,
27             ch == st_on ? LOG_ONLINE : ch == st_ch ? LOG_CHANGE : LOG_OFFLINE, 0xFFFF, "");
28     return 0;
29 }
30 
cb_srv_msg_log(Contact * cont,parentmode_t pm,time_t stamp,fat_srv_msg_t * msg)31 static int cb_srv_msg_log (Contact *cont, parentmode_t pm, time_t stamp, fat_srv_msg_t *msg)
32 {
33 #if ENABLE_CONT_HIER
34     if (cont->parent)
35         return 0;
36 #endif
37     putlog (cont->serv, stamp, cont, IcqToStatus (msg->nativestatus), msg->nativestatus,
38         (msg->type == MSG_AUTH_ADDED) ? LOG_ADDED : LOG_RECVD, msg->type,
39          msg->msgtext);
40     return 0;
41 }
42 
43 
44 /** SHELL SCRIPTING **/
45 #ifdef MSGEXEC
cb_status_exec(Contact * cont,parentmode_t pm,change_t ch,const char * text)46 static int cb_status_exec (Contact *cont, parentmode_t pm, change_t ch, const char *text)
47 {
48     if (prG->event_cmd && *prG->event_cmd)
49         EventExec (cont, prG->event_cmd, ch == st_on ? ev_on : ch == st_ch ? ev_status : ev_off, cont->nativestatus, cont->status, NULL);
50     return 0;
51 }
52 
cb_srv_msg_exec(Contact * cont,parentmode_t pm,time_t stamp,fat_srv_msg_t * msg)53 static int cb_srv_msg_exec (Contact *cont, parentmode_t pm, time_t stamp, fat_srv_msg_t *msg)
54 {
55     if (prG->event_cmd && *prG->event_cmd)
56         EventExec (cont, prG->event_cmd, ev_msg, msg->type, cont->status, msg->msgtext);
57     return 0;
58 }
59 #endif /* MSGEXEC */
60 
61 
62 /** HISTORY **/
63 #define HISTSIZE 100
64 struct History_s
65 {
66     Server *serv;
67     Contact *cont;
68     time_t stamp;
69     char *msg;
70     UWORD inout;
71 };
72 typedef struct History_s History;
73 
74 static History hist[HISTSIZE];
75 
HistMsg(Server * serv,Contact * cont,time_t stamp,const char * msg,UWORD inout)76 void HistMsg (Server *serv, Contact *cont, time_t stamp, const char *msg, UWORD inout)
77 {
78     int i, j, k;
79 
80     if (hist[HISTSIZE - 1].serv && hist[0].serv)
81     {
82         free (hist[0].msg);
83         for (i = 0; i < HISTSIZE - 1; i++)
84             hist[i] = hist[i + 1];
85         hist[HISTSIZE - 1].serv = NULL;
86     }
87 
88     for (i = k = j = 0; j < HISTSIZE - 1 && hist[j].serv; j++)
89         if (cont == hist[j].cont)
90         {
91             if (!k)
92                 i = j;
93             if (++k == 10)
94             {
95                 free (hist[i].msg);
96                 for ( ; i < HISTSIZE - 1; i++)
97                     hist[i] = hist[i + 1];
98                 hist[HISTSIZE - 1].serv = NULL;
99                 j--;
100             }
101         }
102 
103     hist[j].serv = serv;
104     hist[j].cont = cont;
105     hist[j].stamp = stamp;
106     hist[j].msg = strdup (msg);
107     hist[j].inout = inout;
108 }
109 
HistShow(Contact * cont)110 void HistShow (Contact *cont)
111 {
112     int i;
113 
114     for (i = 0; i < HISTSIZE; i++)
115         if (hist[i].serv && (!cont || hist[i].cont == cont))
116             rl_printf ("%s%s %s %s %s" COLMSGINDENT "%s\n",
117                        COLDEBUG, s_time (&hist[i].stamp),
118                        ReadLinePrintWidth (hist[i].cont->nick,
119                            hist[i].inout == HIST_IN ? COLINCOMING : COLACK,
120                            hist[i].inout == HIST_IN ? "<-" : "->",
121                            &uiG.nick_len),
122                        COLNONE, COLMESSAGE, hist[i].msg);
123 }
cb_int_msg_hist(Contact * cont,parentmode_t pm,time_t stamp,fat_int_msg_t * msg)124 static int cb_int_msg_hist (Contact *cont, parentmode_t pm, time_t stamp, fat_int_msg_t *msg)
125 {
126 #if ENABLE_CONT_HIER
127     if (cont->parent)
128         return 0;
129 #endif
130     HistMsg (cont->serv, cont, stamp, msg->msgtext, HIST_OUT);
131     return 0;
132 }
133 
cb_srv_msg_hist(Contact * cont,parentmode_t pm,time_t stamp,fat_srv_msg_t * msg)134 static int cb_srv_msg_hist (Contact *cont, parentmode_t pm, time_t stamp, fat_srv_msg_t *msg)
135 {
136 #if ENABLE_CONT_HIER
137     if (cont->parent)
138         return 0;
139 #endif
140     switch (msg->type & ~MSGF_MASS)
141     {
142         case MSG_NORM_SUBJ:
143             if (msg->subj)
144             {
145                 HistMsg (cont->serv, cont, stamp, msg->subj, HIST_IN);
146                 HistMsg (cont->serv, cont, stamp, msg->msgtext, HIST_IN);
147                 break;
148             }
149         case MSG_NORM:
150         default:
151             HistMsg (cont->serv, cont, stamp, msg->msgtext, HIST_IN);
152             break;
153         case MSG_FILE:
154             break;
155         case MSG_AUTO:
156             break;
157         case MSGF_GETAUTO | MSG_GET_AWAY:
158             break;
159         case MSGF_GETAUTO | MSG_GET_OCC:
160             break;
161         case MSGF_GETAUTO | MSG_GET_NA:
162             break;
163         case MSGF_GETAUTO | MSG_GET_DND:
164             break;
165         case MSGF_GETAUTO | MSG_GET_FFC:
166             break;
167         case MSGF_GETAUTO | MSG_GET_VER:
168             break;
169         case MSG_URL:
170             HistMsg (cont->serv, cont, stamp, msg->msgtext, HIST_IN);
171             break;
172         case MSG_AUTH_REQ:
173         case MSG_AUTH_DENY:
174         case MSG_AUTH_GRANT:
175         case MSG_AUTH_ADDED:
176         case MSG_AUTH_DONE:
177             break;
178         case MSG_EMAIL:
179         case MSG_WEB:
180             break;
181         case MSG_CONTACT:
182             break;
183     }
184     return 0;
185 }
186 
187 /** AUTOFINGER FEATURE **/
188 
cb_status_auto(Contact * cont,parentmode_t pm,change_t ch,const char * text)189 static int cb_status_auto (Contact *cont, parentmode_t pm, change_t ch, const char *text)
190 {
191     assert (cont->serv);
192     if (ch != st_off && ContactPrefVal (cont, CO_AUTOAUTO))
193     {
194         int cdata = 0;
195 
196         switch (ContactClearInv (cont->status))
197         {
198             case imr_dnd:     cdata = MSGF_GETAUTO | MSG_GET_DND;  break;
199             case imr_occ:     cdata = MSGF_GETAUTO | MSG_GET_OCC;  break;
200             case imr_na:      cdata = MSGF_GETAUTO | MSG_GET_NA;   break;
201             case imr_away:    cdata = MSGF_GETAUTO | MSG_GET_AWAY; break;
202             case imr_ffc:     cdata = MSGF_GETAUTO | MSG_GET_FFC;  break;
203             case imr_online:
204             case imr_offline: cdata = 0;
205         }
206 
207         if (cdata)
208             IMCliMsg (cont, cdata, "\xff", NULL);
209     }
210     return 0;
211 }
212 
cb_srv_msg_auto(Contact * cont,parentmode_t pm,time_t stamp,fat_srv_msg_t * msg)213 static int cb_srv_msg_auto (Contact *cont, parentmode_t pm, time_t stamp, fat_srv_msg_t *msg)
214 {
215     if (prG->flags & FLAG_AUTOFINGER && ~cont->updated & UPF_AUTOFINGER &&
216         ~cont->updated & UPF_SERVER && ~cont->updated & UPF_DISC)
217     {
218         cont->updated |= UPF_AUTOFINGER;
219         IMCliInfo (cont->serv, cont, 0);
220     }
221     return 0;
222 }
223 
224 
225 /** TEXT UI **/
226 
227 #define MSGICQACKSTR         ">>>"
228 #define MSGICQACKSTROTR      "&>>"
229 #define MSGICQACKSTROTRSEC   "%>>"
230 #define MSGICQ5ACKSTR        "> >"
231 #define MSGICQ5ACKSTROTR     "& >"
232 #define MSGICQ5ACKSTROTRSEC  "% >"
233 #define MSGTCPACKSTR         ConvTranslit ("\xc2\xbb\xc2\xbb\xc2\xbb", "}}}")
234 #define MSGTCPACKSTROTR      ConvTranslit ("&\xc2\xbb\xc2\xbb", "&}}")
235 #define MSGTCPACKSTROTRSEC   ConvTranslit ("%\xc2\xbb\xc2\xbb", "%}}")
236 #define MSGSSLACKSTR         ConvTranslit ("\xc2\xbb%\xc2\xbb", "}%}")
237 #define MSGSSLACKSTROTR      ConvTranslit ("&%\xc2\xbb", "&%}")
238 #define MSGSSLACKSTROTRSEC   ConvTranslit ("%%\xc2\xbb", "%%}")
239 #define MSGTYPE2ACKSTR       ConvTranslit (">>\xc2\xbb", ">>}")
240 #define MSGTYPE2ACKSTROTR    ConvTranslit ("&>\xc2\xbb", "&>}")
241 #define MSGTYPE2ACKSTROTRSEC ConvTranslit ("%>\xc2\xbb", "%>}")
242 #define MSGICQRECSTR         "<<<"
243 #define MSGICQRECSTROTR      "<<&"
244 #define MSGICQRECSTROTRSEC   "<<%"
245 #define MSGTCPRECSTR         ConvTranslit ("\xc2\xab\xc2\xab\xc2\xab", "{{{")
246 #define MSGTCPRECSTROTR      ConvTranslit ("\xc2\xab\xc2\xab&", "{{&")
247 #define MSGTCPRECSTROTRSEC   ConvTranslit ("\xc2\xab\xc2\xab%", "{{%")
248 #define MSGSSLRECSTR         ConvTranslit ("\xc2\xab%\xc2\xab", "{%{")
249 #define MSGSSLRECSTROTR      ConvTranslit ("\xc2\xab%&", "{%&")
250 #define MSGSSLRECSTROTRSEC   ConvTranslit ("\xc2\xab%%", "{%%")
251 #define MSGTYPE2RECSTR       ConvTranslit ("\xc2\xab<<", "{<<")
252 #define MSGTYPE2RECSTROTR    ConvTranslit ("\xc2\xab<&", "{<&")
253 #define MSGTYPE2RECSTROTRSEC ConvTranslit ("\xc2\xab<%", "{<%")
254 
255 #if ENABLE_CONT_HIER
cb_tui_tail(Contact * cont)256 static void cb_tui_tail (Contact *cont)
257 {
258     if (!cont->parent)
259         return;
260     cb_tui_tail (cont->parent);
261     rl_printf (i18n (2619, " with %s%s%s"), COLCONTACT, cont->nick, COLNONE);
262 }
263 #endif
264 
cb_status_tui(Contact * cont,parentmode_t pm,change_t ch,const char * text)265 static int cb_status_tui (Contact *cont, parentmode_t pm, change_t ch, const char *text)
266 {
267     Contact *pcont = cont;
268 
269 #if ENABLE_CONT_HIER
270     int dotail = 0;
271     if (pm == pm_parent)
272         return 0;
273 
274     for ( ; pcont->parent; pcont = pcont->parent)
275         if (pcont->parent->firstchild != pcont || pcont->parent->firstchild->next)
276             dotail = 1;
277 #endif
278     rl_log_for (pcont->nick, COLCONTACT);
279 
280     if (ch == st_off)
281         rl_printf ("%s", i18n (1030, "logged off"));
282     else if (ch == st_on)
283         rl_printf (i18n (2213, "logged on (%s)"), s_status (cont->status, cont->nativestatus));
284     else
285         rl_printf (i18n (2212, "changed status to %s"), s_status (cont->status, cont->nativestatus));
286 
287     if (cont->version && ch == st_on)
288         rl_printf (" [%s]", cont->version);
289 
290 /*    if ((cont->flags & imf_birth) && ((~oldf & imf_birth) || ch == st_on)) */
291     if (cont->flags & imf_birth && ch != st_off)
292         rl_printf (" (%s)", i18n (2033, "born today"));
293 
294 #if ENABLE_CONT_HIER
295     if (dotail)
296         cb_tui_tail (cont);
297 #endif
298 
299     if (text && *text)
300     {
301         rl_printf (". %s%s%s", COLQUOTE, COLSINGLE, text);
302         rl_print ("\n");
303     }
304     else
305         rl_print (".\n");
306 
307     if (ch == st_on && cont->dc && prG->verbose)
308     {
309         rl_printf ("    %s %s / ", i18n (1642, "IP:"), s_ip (cont->dc->ip_rem));
310         rl_printf ("%s:%ld    %s %d    %s (%d)\n", s_ip (cont->dc->ip_loc),
311             UD2UL (cont->dc->port), i18n (1453, "TCP version:"), cont->dc->version,
312             cont->dc->type == 4 ? i18n (1493, "Peer-to-Peer") : i18n (1494, "Server Only"),
313             cont->dc->type);
314     }
315     return 0;
316 }
317 
cb_int_msg_tui(Contact * cont,parentmode_t pm,time_t stamp,fat_int_msg_t * msg)318 static int cb_int_msg_tui (Contact *cont, parentmode_t pm, time_t stamp, fat_int_msg_t *msg)
319 {
320     const char *line = "";
321     const char *col = COLCONTACT;
322     char *p, *q;
323     const char *marker = NULL;
324 
325 #if ENABLE_CONT_HIER
326     int dotail = 0;
327     if (pm == pm_parent)
328         return 0;
329 
330     for ( ; cont->parent; cont = cont->parent)
331         if (cont->parent->firstchild != cont || cont->parent->firstchild->next)
332             dotail = 1;
333 #endif
334 
335     switch (msg->type)
336     {
337         case INT_MSGTRY_TYPE2:
338         case INT_MSGTRY_DC:
339         case INT_MSGACK_TYPE2:
340         case INT_MSGACK_DC:
341         case INT_MSGACK_SSL:
342         case INT_MSGACK_V8:
343         case INT_MSGACK_V5:
344         case INT_MSGDISPL:
345         case INT_MSGCOMP:
346         case INT_MSGNOCOMP:
347             if (ContactPrefVal (cont, CO_HIDEACK))
348                 return 1;
349             break;
350         default:
351             ;
352     }
353 
354     switch (msg->type)
355     {
356         case INT_FILE_ACKED:
357             line = s_sprintf (i18n (2462, "File transfer %s to port %s%ld%s."),
358                               s_qquote (msg->opt_text), COLQUOTE, UD2UL (msg->port), COLNONE);
359             break;
360         case INT_FILE_REJED:
361             line = s_sprintf (i18n (2463, "File transfer %s rejected by peer: %s."),
362                               s_qquote (msg->opt_text), s_wordquote (msg->msgtext));
363             break;
364         case INT_FILE_ACKING:
365             line = s_sprintf (i18n (2464, "Accepting file %s (%s%ld%s bytes)."),
366                               s_qquote (msg->opt_text), COLQUOTE, UD2UL (msg->bytes), COLNONE);
367             break;
368         case INT_FILE_REJING:
369             line = s_sprintf (i18n (2465, "Refusing file request %s (%s%ld%s bytes): %s."),
370                               s_qquote (msg->opt_text), COLQUOTE, UD2UL (msg->bytes), COLNONE, s_wordquote (msg->msgtext));
371             break;
372         case INT_CHAR_REJING:
373             line = s_sprintf (i18n (2466, "Refusing chat request (%s/%s) from %s%s%s."),
374                               p = strdup (s_qquote (msg->opt_text)), q = strdup (s_qquote (msg->msgtext)),
375                               COLCONTACT, cont->nick, COLNONE);
376             free (p);
377             free (q);
378             break;
379         case INT_MSGDISPL:      marker = "<displayed>";      break;
380         case INT_MSGCOMP:       marker = "<composing>";      break;
381         case INT_MSGNOCOMP:     marker = "<no composing>";   break;
382         case INT_MSGOFF:        marker = "<offline>";        break;
383         case INT_MSGTRY_TYPE2:  marker = i18n (2293, "--="); break;
384         case INT_MSGTRY_DC:     marker = i18n (2294, "==="); break;
385         case INT_MSGACK_TYPE2:  marker = msg->bytes == 2 ? MSGTYPE2ACKSTROTRSEC
386                                        : msg->bytes == 1 ? MSGTYPE2ACKSTROTR
387                                        :                   MSGTYPE2ACKSTR;     break;
388         case INT_MSGACK_DC:     marker = msg->bytes == 2 ? MSGTCPACKSTROTRSEC
389                                        : msg->bytes == 1 ? MSGTCPACKSTROTR
390                                        :                   MSGTCPACKSTR;       break;
391 #ifdef ENABLE_SSL
392         case INT_MSGACK_SSL:    marker = msg->bytes == 2 ? MSGSSLACKSTROTRSEC
393                                        : msg->bytes == 1 ? MSGSSLACKSTROTR
394                                        :                   MSGSSLACKSTR;       break;
395 #endif
396         case INT_MSGACK_V8:     marker = msg->bytes == 2 ? MSGICQACKSTROTRSEC
397                                        : msg->bytes == 1 ? MSGICQACKSTROTR
398                                        :                   MSGICQACKSTR;       break;
399         case INT_MSGACK_V5:     marker = msg->bytes == 2 ? MSGICQ5ACKSTROTRSEC
400                                        : msg->bytes == 1 ? MSGICQ5ACKSTROTR
401                                        :                   MSGICQ5ACKSTR;      break;
402         default:
403             line = "";
404     }
405 
406     if (marker)
407     {
408         col = COLACK;
409         line = s_sprintf ("%s%s %s", marker, COLSINGLE, msg->msgtext);
410     }
411 
412     for (p = q = strdup (line); *q; q++)
413         if (*q == (char)0xfe)
414             *q = '*';
415 
416     rl_printf ("%s ", s_time (&stamp));
417     rl_printf ("%s", ReadLinePrintCont (cont->nick, col));
418 
419     if (prG->verbose > 1)
420         rl_printf ("<%d> ", msg->type);
421 
422     if (msg->tstatus != ims_offline && (!cont || cont->status == ims_offline || !cont->group))
423         rl_printf ("(%s) ", s_status (msg->tstatus, 0));
424 
425     rl_print (p);
426 #if ENABLE_CONT_HIER
427     if (dotail)
428         cb_tui_tail (cont);
429 #endif
430     rl_print ("\n");
431     free (p);
432 
433     return 0;
434 }
435 
cb_srv_msg_tui(Contact * ocont,parentmode_t pm,time_t stamp,fat_srv_msg_t * msg)436 static int cb_srv_msg_tui (Contact *ocont, parentmode_t pm, time_t stamp, fat_srv_msg_t *msg)
437 {
438     UDWORD j;
439     int i, is_awaycount;
440     status_noi_t noinv;
441     const char *tmp, *tmp2, *tmp3, *carr;
442     Contact *cont = ocont;
443 
444 #if ENABLE_CONT_HIER
445     int dotail = 0;
446     if (pm == pm_parent)
447         return 0;
448 
449     for ( ; cont->parent; cont = cont->parent)
450         if (cont->parent->firstchild != cont || cont->parent->firstchild->next)
451             dotail = 1;
452 #endif
453 
454     is_awaycount = ContactGroupPrefVal (cont->serv->contacts, CO_AWAYCOUNT);
455     noinv = ContactClearInv (cont->serv->status);
456     if (   (is_awaycount && noinv != imr_online && noinv != imr_ffc)
457         || (cont->serv->idle_flag != i_idle)
458         || uiG.idle_msgs)
459     {
460         if ((cont != uiG.last_rcvd) || !uiG.idle_uins || !uiG.idle_msgs)
461             s_repl (&uiG.idle_uins, s_sprintf ("%s %s", uiG.idle_uins && uiG.idle_msgs ? uiG.idle_uins : "", cont->nick));
462 
463         uiG.idle_msgs++;
464         ReadLinePromptSet (s_sprintf ("[%s%ld%s%s]%s%s",
465                            COLINCOMING, UD2UL (uiG.idle_msgs), uiG.idle_uins,
466                            COLNONE, COLSERVER, i18n (2467, "climm>")));
467     }
468 
469     if (!msg->otrencrypted)
470         carr = (msg->origin == CV_ORIGIN_dc) ? MSGTCPRECSTR :
471 #ifdef ENABLE_SSL
472                (msg->origin == CV_ORIGIN_ssl) ? MSGSSLRECSTR :
473 #endif
474                (msg->origin == CV_ORIGIN_v8) ? MSGTYPE2RECSTR : MSGICQRECSTR;
475     else if (msg->otrencrypted == 1)
476         carr = (msg->origin == CV_ORIGIN_dc) ? MSGTCPRECSTROTR :
477 #ifdef ENABLE_SSL
478                (msg->origin == CV_ORIGIN_ssl) ? MSGSSLRECSTROTR :
479 #endif
480                (msg->origin == CV_ORIGIN_v8) ? MSGTYPE2RECSTROTR : MSGICQRECSTROTR;
481     else
482         carr = (msg->origin == CV_ORIGIN_dc) ? MSGTCPRECSTROTRSEC :
483 #ifdef ENABLE_SSL
484                (msg->origin == CV_ORIGIN_ssl) ? MSGSSLRECSTROTRSEC :
485 #endif
486                (msg->origin == CV_ORIGIN_v8) ? MSGTYPE2RECSTROTRSEC : MSGICQRECSTROTRSEC;
487 
488 
489     if (uiG.nick_len < 4)
490         uiG.nick_len = 4;
491     rl_printf ("\a%s %s", s_time (&stamp), ReadLinePrintCont (cont->nick, COLINCOMING));
492 
493 #if ENABLE_CONT_HIER
494     if (dotail)
495         cb_tui_tail (cont);
496 #endif
497 
498     if (msg->nativestatus != -1 && (ocont->status != IcqToStatus (msg->nativestatus) || !cont->group))
499         rl_printf ("(%s) ", s_status (IcqToStatus (msg->nativestatus), msg->nativestatus));
500 
501     if (prG->verbose > 1)
502         rl_printf ("<%ld> ", UD2UL (msg->type));
503 
504     switch (msg->type & ~MSGF_MASS)
505     {
506         case MSGF_MASS: /* not reached here, but quiets compiler warning */
507         while (1)
508         {
509             rl_printf ("(?%lx?) %s" COLMSGINDENT "%s\n", UD2UL (msg->type), COLMESSAGE, msg->orig_data);
510             rl_printf ("    '");
511             for (j = 0; j < strlen (msg->orig_data); j++)
512                 rl_printf ("%c", ((msg->msgtext[j] & 0xe0) && (msg->msgtext[j] != 127)) ? msg->msgtext[j] : '.');
513             rl_print ("'\n");
514             return 1;
515 
516         case MSG_NORM_SUBJ:
517             if (msg->subj)
518             {
519                 rl_printf ("%s \"%s\"\n", carr, s_wordquote (msg->subj));
520                 rl_printf ("%s" COLMSGINDENT "%s\n", COLMESSAGE, msg->msgtext);
521                 break;
522             }
523 
524         case MSG_NORM:
525         default:
526             rl_printf ("%s %s" COLMSGINDENT "%s\n", carr, COLMESSAGE, msg->msgtext);
527             break;
528 
529         case MSG_FILE:
530             rl_printf (i18n (2468, "requests file transfer %s of %s%ld%s bytes (sequence %s%ld%s).\n"),
531                       s_qquote (msg->msgtext), COLQUOTE, UD2UL (msg->bytes), COLNONE, COLQUOTE, UD2UL (msg->ref), COLNONE);
532             break;
533 
534         case MSG_AUTO:
535             rl_printf ("<%s> %s" COLMSGINDENT "%s\n", i18n (2108, "auto"), COLMESSAGE, msg->msgtext);
536             break;
537 
538         case MSGF_GETAUTO | MSG_GET_AWAY:
539             rl_printf ("<%s> %s" COLMSGINDENT "%s\n", i18n (1972, "away"), COLMESSAGE, msg->msgtext);
540             break;
541 
542         case MSGF_GETAUTO | MSG_GET_OCC:
543             rl_printf ("<%s> %s" COLMSGINDENT "%s\n", i18n (1973, "occupied"), COLMESSAGE, msg->msgtext);
544             break;
545 
546         case MSGF_GETAUTO | MSG_GET_NA:
547             rl_printf ("<%s> %s" COLMSGINDENT "%s\n", i18n (1974, "not available"), COLMESSAGE, msg->msgtext);
548             break;
549 
550         case MSGF_GETAUTO | MSG_GET_DND:
551             rl_printf ("<%s> %s" COLMSGINDENT "%s\n", i18n (1971, "do not disturb"), COLMESSAGE, msg->msgtext);
552             break;
553 
554         case MSGF_GETAUTO | MSG_GET_FFC:
555             rl_printf ("<%s> %s" COLMSGINDENT "%s\n", i18n (1976, "free for chat"), COLMESSAGE, msg->msgtext);
556             break;
557 
558         case MSGF_GETAUTO | MSG_GET_VER:
559             rl_printf ("<%s> %s" COLMSGINDENT "%s\n", i18n (2109, "version"), COLMESSAGE, msg->msgtext);
560             break;
561 
562         case MSG_URL:
563             if (!msg->tmp[1]) continue;
564             rl_printf ("%s %s\n%s %*s", carr, s_msgquote (msg->tmp[0]), s_now, uiG.nick_len - 4, "");
565             rl_printf ("%s %s %s\n", i18n (2469, "URL:"), carr, s_wordquote (msg->tmp[1]));
566             break;
567 
568         case MSG_AUTH_REQ:
569             if (msg->tmp[1])
570             {
571                 if (!msg->tmp[5]) continue;
572             }
573             else
574             {
575                 msg->tmp[5] = msg->tmp[0];
576                 msg->tmp[0] = NULL;
577             }
578             if (msg->tmp[5] && *msg->tmp[5])
579                 rl_printf (i18n (2470, "requests authorization: %s\n"), s_msgquote (msg->tmp[5]));
580             else
581                 rl_printf (i18n (2758, "requests authorization.\n"));
582 
583             if (msg->tmp[0] && *msg->tmp[0])
584                 rl_printf ("%-15s %s\n", "???1:", s_wordquote (msg->tmp[0]));
585             if (msg->tmp[1] && *msg->tmp[1])
586                 rl_printf ("%-15s %s\n", i18n (1564, "First name:"), s_wordquote (msg->tmp[1]));
587             if (msg->tmp[2] && *msg->tmp[2])
588                 rl_printf ("%-15s %s\n", i18n (1565, "Last name:"), s_wordquote (msg->tmp[2]));
589             if (msg->tmp[3] && *msg->tmp[3])
590                 rl_printf ("%-15s %s\n", i18n (1566, "Email address:"), s_wordquote (msg->tmp[3]));
591             if (msg->tmp[4] && *msg->tmp[4])
592                 rl_printf ("%-15s %s\n", "???5:", s_wordquote (msg->tmp[4]));
593             break;
594 
595         case MSG_AUTH_DENY:
596             if (msg->msgtext)
597                 rl_printf (i18n (2233, "refused authorization: %s%s%s\n"), COLMESSAGE, COLMSGINDENT, msg->msgtext);
598             else
599                 rl_printf (i18n (2759, "refused authorization.\n"));
600             break;
601 
602         case MSG_AUTH_GRANT:
603             rl_print (i18n (1901, "has authorized you to add them to your contact list.\n"));
604             break;
605 
606         case MSG_AUTH_ADDED:
607             if (!msg->tmp[0])
608             {
609                 rl_print (i18n (1755, "has added you to their contact list.\n"));
610                 break;
611             }
612             if (!msg->tmp[3]) continue;
613             rl_printf ("%s ", s_cquote (msg->tmp[0], COLCONTACT));
614             rl_print  (i18n (1755, "has added you to their contact list.\n"));
615             rl_printf ("%-15s %s\n", i18n (1564, "First name:"), s_wordquote (msg->tmp[1]));
616             rl_printf ("%-15s %s\n", i18n (1565, "Last name:"), s_wordquote (msg->tmp[2]));
617             rl_printf ("%-15s %s\n", i18n (1566, "Email address:"), s_wordquote (msg->tmp[3]));
618             break;
619 
620         case MSG_AUTH_DONE:
621             rl_print (i18n (2760, "has removed you from his contact list.\n"));
622             break;
623 
624         case MSG_EMAIL:
625         case MSG_WEB:
626             if (!msg->tmp[5]) continue;
627             if (msg->type == MSG_EMAIL)
628             {
629                 rl_printf (i18n (2571, "\"%s\" <%s> emailed you a message [%s]: %s\n"),
630                     s_cquote (msg->tmp[0], COLCONTACT), s_cquote (msg->tmp[3], COLCONTACT),
631                     s_cquote (msg->tmp[4], COLCONTACT), s_msgquote (msg->tmp[5]));
632             }
633             else
634             {
635                 rl_printf (i18n (2572, "\"%s\" <%s> sent you a web message [%s]: %s\n"),
636                     s_cquote (msg->tmp[0], COLCONTACT), s_cquote (msg->tmp[3], COLCONTACT),
637                     s_cquote (msg->tmp[4], COLCONTACT), s_msgquote (msg->tmp[5]));
638             }
639             break;
640 
641         case MSG_CONTACT:
642             {
643             tmp = s_msgtok (msg->msgtext); if (!tmp) continue;
644 
645             rl_printf (i18n (1595, "\nContact List.\n============================================\n%d Contacts\n"),
646                      i = atoi (tmp));
647 
648             while (i--)
649             {
650                 tmp2 = s_msgtok (NULL); if (!tmp2) continue;
651                 tmp3 = s_msgtok (NULL); if (!tmp3) continue;
652 
653                 rl_print  (s_cquote (tmp2, COLCONTACT));
654                 rl_printf ("\t\t\t%s\n", s_msgquote (tmp3));
655             }
656             }
657             break;
658         }
659     }
660     return 0;
661 }
662 
663 /** INTERNAL FUNCTIONS **/
664 
665 #define hide_noleaf 4
666 #define hide_cont   2
667 #define hide_hide   1
668 
__IMOnline(Contact * cont,status_t status,statusflag_t flags,UDWORD nativestatus,const char * text,int hide)669 static int __IMOnline (Contact *cont, status_t status, statusflag_t flags, UDWORD nativestatus, const char *text, int hide)
670 {
671     status_t old;
672     statusflag_t oldf;
673 #if ENABLE_CONT_HIER
674     parentmode_t pm = hide & hide_noleaf ? pm_parent : pm_leaf;
675 #else
676 #define pm pm_leaf
677 #endif
678     change_t ch = status == ims_offline ? st_off : cont->status == ims_offline ? st_on : st_ch;
679 
680     if (cont->group)
681         hide |= hide_cont;
682 
683     OptSetVal (&cont->copts, CO_TIMESEEN, time (NULL));
684     old = cont->status;
685     oldf = cont->flags;
686     cont->status = status;
687     if (status != ims_offline)
688         cont->flags = flags;
689     cont->nativestatus = nativestatus;
690     cont->oldflags &= ~CONT_SEENAUTO;
691     s_repl (&cont->status_message, text);
692 
693     cb_status_log (cont, ch, pm, text);
694 
695     if (ContactPrefVal (cont, CO_IGNORE)
696         || (!ContactPrefVal (cont, CO_SHOWONOFF)  && (old == ims_offline || status == ims_offline))
697         || (!ContactPrefVal (cont, CO_SHOWCHANGE) && old != ims_offline && status != ims_offline)
698         || (cont->serv && ~cont->serv->conn->connect & CONNECT_OK))
699         hide |= hide_hide;
700 
701 #if ENABLE_CONT_HIER
702     if (cont->parent)
703     {
704         Contact *pcont = cont;
705 
706         assert (cont->parent->firstchild);
707 
708 #if 0
709         if (status == ims_offline && !cont->group)
710         {
711             Contact **t;
712             for (t = &(cont->parent->firstchild); *t; t=&((*t)->next))
713                 if (*t == cont)
714                 {
715                     *t = cont->next;
716                     cont->next = NULL;
717                     if (!*t)
718                         break;
719                 }
720         }
721 #endif
722 
723         if (ContactStatusCmp (status, cont->parent->status) >= 0)
724         {
725             Contact *tcont;
726             for (tcont = cont->parent->firstchild; tcont; tcont = tcont->next)
727             {
728                 assert (tcont->parent == cont->parent);
729                 if (ContactStatusCmp (tcont->status, pcont->status) < 0)
730                     pcont = tcont;
731             }
732         }
733         s_repl (&cont->parent->version, pcont->version);
734         s_repl (&cont->parent->cap_string, pcont->cap_string);
735         hide |= __IMOnline (cont->parent, pcont->status, pcont->flags, pcont->nativestatus, pcont->status_message, hide | hide_noleaf);
736     }
737 #endif
738 
739     if (hide & hide_hide || ~hide & hide_cont)
740         return hide_hide;
741 
742 #ifdef MSGEXEC
743     cb_status_exec (cont, pm, ch, text);
744 #endif
745     cb_status_tui  (cont, pm, ch, text);
746     cb_status_auto (cont, pm, ch, text);
747 #ifdef ENABLE_TCL
748     cb_status_tcl  (cont, pm, ch, text);
749 #endif
750     return hide;
751 }
752 #undef pm
753 
754 
__IMIntMsg(Contact * cont,time_t stamp,fat_int_msg_t * msg,int hide)755 int __IMIntMsg (Contact *cont, time_t stamp, fat_int_msg_t *msg, int hide)
756 {
757     int noerr = 0;
758 #if ENABLE_CONT_HIER
759     parentmode_t pm = hide & hide_noleaf ? pm_parent : pm_leaf;
760 #else
761 #define pm pm_leaf
762 #endif
763 /*    cb_int_msg_log (cont, pm, stamp, msg); */
764 
765     if (ContactPrefVal (cont, CO_IGNORE))
766         return hide_hide;
767 
768 #if ENABLE_CONT_HIER
769     if (cont->parent)
770     {
771         assert (cont->parent->firstchild);
772         hide |= __IMIntMsg (cont->parent, stamp, msg, hide | hide_noleaf);
773     }
774 #endif
775     if (hide & hide_hide)
776         return hide_hide;
777 
778 #ifdef MSGEXEC
779 /*    if (!cb_int_msg_exec (cont, pm, stamp, msg)) */
780 #endif
781     if (!cb_int_msg_tui  (cont, pm, stamp, msg))
782 /*    if (!cb_int_msg_auto (cont, pm, stamp, msg)) */
783     if (!cb_int_msg_hist (cont, pm, stamp, msg))
784 #ifdef ENABLE_TCL
785     if (!cb_int_msg_tcl  (cont, pm, stamp, msg))
786 #endif
787         noerr = 1;
788     if (!noerr)
789         hide |= hide_hide;
790 
791     return hide;
792 }
793 
__IMSrvMsg(Contact * cont,time_t stamp,fat_srv_msg_t * msg,int hide)794 int __IMSrvMsg (Contact *cont, time_t stamp, fat_srv_msg_t *msg, int hide)
795 {
796     int noerr = 0;
797 #if ENABLE_CONT_HIER
798     parentmode_t pm = hide & hide_noleaf ? pm_parent : pm_leaf;
799 #else
800 #define pm pm_leaf
801 #endif
802     cb_srv_msg_log (cont, pm, stamp, msg);
803 
804     if (ContactPrefVal (cont, CO_IGNORE))
805         return hide_hide;
806 
807 #if ENABLE_CONT_HIER
808     if (cont->parent)
809     {
810         assert (cont->parent->firstchild);
811         hide |= __IMSrvMsg (cont->parent, stamp, msg, hide | hide_noleaf);
812     }
813 #endif
814     if (hide & hide_hide)
815         return hide_hide;
816 
817     s_repl (&cont->last_message, msg->msgtext);
818     cont->last_time = stamp;
819 
820     if (!cb_srv_msg_exec   (cont, pm, stamp, msg))
821     if (!cb_srv_msg_tui    (cont, pm, stamp, msg))
822     if (!cb_srv_msg_auto   (cont, pm, stamp, msg))
823     if (!cb_srv_msg_hist   (cont, pm, stamp, msg))
824 #ifdef ENABLE_TCL
825     if (!cb_srv_msg_tcl    (cont, pm, stamp, msg))
826 #endif
827         noerr = 1;
828     if (!noerr)
829         hide |= hide_hide;
830 
831 #if ENABLE_CONT_HIER
832     while (cont->parent)
833         cont = cont->parent;
834     if (pm == pm_leaf)
835 #endif
836         uiG.last_rcvd = cont;
837     TabAddIn (cont);
838     return hide;
839 }
840 
841 /** FUNCTIONS TO BE CALLED FROM EXTERN **/
842 
843 /*
844  * Inform that a user went offline
845  */
IMOffline(Contact * cont)846 void IMOffline (Contact *cont)
847 {
848     IMOnline (cont, ims_offline, 0, -1, "");
849 }
850 
851 /*
852  * Inform that a user went online
853  */
IMOnline(Contact * cont,status_t status,statusflag_t flags,UDWORD nativestatus,const char * text)854 void IMOnline (Contact *cont, status_t status, statusflag_t flags, UDWORD nativestatus, const char *text)
855 {
856     Event *egevent;
857     int hide = 0;
858 
859     if (!cont)
860         return;
861 
862     if ((egevent = QueueDequeue2 (cont->serv->conn, QUEUE_DEP_WAITLOGIN, 0, 0)))
863     {
864         egevent->due = time (NULL) + 3;
865         QueueEnqueue (egevent);
866         hide = hide_hide;
867     }
868 
869     if (status == cont->status && (status == ims_offline || flags == cont->flags) && (!text || !*text))
870         return;
871 
872     if (cont->status_message && text && !strcmp (cont->status_message, text))
873         return;
874 
875     __IMOnline (cont, status, flags, nativestatus, text, hide);
876 }
877 
878 
879 /*
880  * Central entry point for protocol triggered output.
881  */
IMIntMsg(Contact * cont,time_t stamp,status_t tstatus,int_msg_t type,const char * text)882 void IMIntMsg (Contact *cont, time_t stamp, status_t tstatus, int_msg_t type, const char *text)
883 {
884     IMIntMsgFat (cont, stamp, tstatus, type, text, "", 0, 0);
885 }
886 
IMIntMsgMsg(Message * msg,time_t stamp,status_t tstatus)887 void IMIntMsgMsg (Message *msg, time_t stamp, status_t tstatus)
888 {
889     IMIntMsgFat (msg->cont, stamp, tstatus, msg->type, msg->plain_message ? msg->plain_message : msg->send_message,
890                  NULL, 0, !msg->otrencrypted ? 0 : msg->otrencrypted ? 1 : 2);
891 }
892 
IMIntMsgFat(Contact * cont,time_t stamp,status_t tstatus,int_msg_t type,const char * text,const char * opt_text,UDWORD port,UDWORD bytes)893 void IMIntMsgFat (Contact *cont, time_t stamp, status_t tstatus, int_msg_t type,
894                   const char *text, const char *opt_text, UDWORD port, UDWORD bytes)
895 {
896     fat_int_msg_t msg;
897     char *deleteme = NULL;
898 
899     if (!cont)
900         return;
901 
902     memset (&msg, 0, sizeof msg);
903     if (stamp == NOW)
904         stamp = time (NULL);
905 
906     msg.orig_data = text;
907     msg.msgtext = text;
908     msg.type = type;
909     msg.tstatus = tstatus;
910     msg.opt_text = opt_text ? opt_text : "";
911     msg.port = port;
912     msg.bytes = bytes;
913 
914     if (!strncasecmp (msg.orig_data, "<font ", 6))
915     {
916       char *t = deleteme = strdup (msg.orig_data);
917       while (*t && *t != '>')
918         t++;
919       if (*t)
920       {
921         size_t l = strlen (++t);
922         if (!strcasecmp (t + l - 7, "</font>"))
923           t[l - 7] = 0;
924         msg.msgtext = t;
925       }
926     }
927 
928     __IMIntMsg (cont, stamp, &msg, 0);
929 
930     if (deleteme)
931         s_free (deleteme);
932 }
933 
934 /*
935  * Central entry point for incoming messages.
936  */
IMSrvMsg(Contact * cont,time_t stamp,UDWORD opt_origin,UDWORD opt_type,const char * text)937 void IMSrvMsg (Contact *cont, time_t stamp, UDWORD opt_origin, UDWORD opt_type, const char *text)
938 {
939     IMSrvMsgFat (cont, stamp, OptSetVals (NULL, CO_ORIGIN, opt_origin, CO_MSGTYPE, opt_type, CO_MSGTEXT, text, 0));
940 }
941 
IMSrvMsgFat(Contact * cont,time_t stamp,Opt * opt)942 void IMSrvMsgFat (Contact *cont, time_t stamp, Opt *opt)
943 {
944     fat_srv_msg_t msg;
945     int max_0xff = 0, i;
946 
947     if (!cont)
948     {
949         OptD (opt);
950         return;
951     }
952 
953     memset (&msg, 0, sizeof msg);
954 
955     if (stamp == NOW)
956         stamp = time (NULL);
957 
958     if (!OptGetVal (opt, CO_MSGTYPE, &msg.type))
959         msg.type = MSG_NORM;
960     if (!OptGetVal (opt, CO_ORIGIN, &msg.origin))
961         msg.origin = CV_ORIGIN_v5;
962     if (!OptGetVal (opt, CO_STATUS, &msg.nativestatus))
963         msg.nativestatus = -1;
964     if (!OptGetVal (opt, CO_BYTES, &msg.bytes))
965         msg.bytes = 0;
966     if (OptGetVal (opt, CO_SAMEHTML, &msg.ref))
967         msg.samehtml = 1;
968     else
969         msg.samehtml = 0;
970     if (!OptGetVal (opt, CO_REF, &msg.ref))
971         msg.ref = 0;
972     if (!OptGetStr (opt, CO_SUBJECT, &msg.subj))
973         msg.subj = NULL;
974 
975     if (!OptGetStr (opt, CO_MSGTEXT, &msg.orig_data))
976         msg.orig_data = "";
977     msg.msgtext = strdup (msg.orig_data);
978 
979     OptD (opt);
980     opt = NULL;
981 
982 #ifdef ENABLE_OTR
983     /* process incomming messages for OTR decryption */
984     if (msg.type == MSG_NORM && libotr_is_present)
985         if (OTRMsgIn (cont, &msg))
986         {
987             free (msg.msgtext);
988             return; /* no msg ack/logging? */
989         }
990 #endif /* ENABLE_OTR */
991     while (*msg.msgtext && strchr ("\n\r \t", msg.msgtext[strlen (msg.msgtext) - 1]))
992         msg.msgtext[strlen (msg.msgtext) - 1] = '\0';
993 
994     max_0xff = 0;
995     switch (msg.type & ~MSGF_MASS)
996     {
997         default:
998             break;
999         case MSG_URL:
1000             max_0xff = 2;
1001             break;
1002         case MSG_AUTH_ADDED:
1003             max_0xff = 4;
1004             break;
1005         case MSG_AUTH_REQ:
1006         case MSG_EMAIL:
1007         case MSG_WEB:
1008             max_0xff = 6;
1009             break;
1010     }
1011     for (i = 0; i < max_0xff; i++)
1012         msg.tmp[i] = s_msgtok (i ? NULL : msg.msgtext);
1013 
1014     if (!strncasecmp (msg.msgtext, "<font ", 6))
1015     {
1016       char *t = msg.msgtext;
1017       while (*t && *t != '>')
1018         t++;
1019       if (*t)
1020       {
1021         size_t l = strlen (++t);
1022         if (!strcasecmp (t + l - 7, "</font>"))
1023           t[l - 7] = 0;
1024         t = strdup (t);
1025         free (msg.msgtext);
1026         msg.msgtext = t;
1027       }
1028     }
1029 
1030     __IMSrvMsg (cont, stamp, &msg, 0);
1031 
1032     free (msg.msgtext);
1033 }
1034