1 /*
2 * This file implements the contact list and basic operations on it.
3 *
4 * climm Copyright (C) © 2001-2010 Rüdiger Kuhlmann
5 *
6 * climm is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 dated June, 1991.
9 *
10 * climm is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
13 * License for more details.
14 *
15 * In addition, as a special exception permission is granted to link the
16 * code of this release of climm with the OpenSSL project's "OpenSSL"
17 * library, and distribute the linked executables. You must obey the GNU
18 * General Public License in all respects for all of the code used other
19 * than "OpenSSL". If you modify this file, you may extend this exception
20 * to your version of the file, but you are not obligated to do so. If you
21 * do not wish to do so, delete this exception statement from your version
22 * of this file.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this package; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * $Id: contact.c 2864 2010-03-16 22:48:25Z kuhlmann $
30 */
31
32 #include <ctype.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <assert.h>
36
37 #include "climm.h"
38 #include "contact.h"
39 #include "connection.h"
40 #include "util_ui.h" /* for Debug() and DEB_CONTACT */
41 #include "conv.h" /* for meta data file recoding */
42 #include "util_io.h" /* for UtilIOReadline() */
43 #include "packet.h" /* for capabilities */
44 #include "buildmark.h" /* for versioning */
45 #include "preferences.h" /* for BASEDIR */
46 #include "oscar_base.h" /* for IcqIsUIN */
47 #include "util_parse.h"
48
49 static ContactGroup **cnt_groups = NULL;
50 static int cnt_count = 0;
51
52 #define MAX_ENTRIES 32
53
54 #define CONTACTGROUP_GLOBAL (cnt_groups[0])
55 #define CONTACTGROUP_NONCONTACTS (cnt_groups[1])
56
57 /*
58 * Initializes the contact group table.
59 */
ContactGroupInit(void)60 static void ContactGroupInit (void)
61 {
62 if (!cnt_groups)
63 {
64 cnt_groups = calloc (cnt_count = 32, sizeof (ContactGroup *));
65 cnt_groups[0] = calloc (1, sizeof (ContactGroup));
66 cnt_groups[1] = calloc (1, sizeof (ContactGroup));
67 }
68 }
69
70 #undef ContactGroupC
ContactGroupC(Server * serv,UWORD id,const char * name DEBUGPARAM)71 ContactGroup *ContactGroupC (Server *serv, UWORD id, const char *name DEBUGPARAM)
72 {
73 ContactGroup *cg;
74 int i, j;
75
76 if (!cnt_groups)
77 ContactGroupInit ();
78
79 for (i = 1; i < cnt_count; i++)
80 if (!cnt_groups[i])
81 break;
82
83 if (i >= cnt_count - 1)
84 {
85 ContactGroup **ncgp = realloc (cnt_groups, sizeof (ContactGroup *) * (2 * cnt_count));
86 if (!ncgp)
87 return NULL;
88 cnt_groups = ncgp;
89 cnt_count *= 2;
90 for (j = i; j < cnt_count; j++)
91 cnt_groups[j] = NULL;
92 }
93
94 if (!(cg = calloc (1, sizeof (ContactGroup))))
95 return NULL;
96
97 cnt_groups[i + 1] = NULL;
98 cnt_groups[i] = cg;
99 cg->serv = serv;
100 cg->id = id;
101 s_repl (&cg->name, name);
102 Debug (DEB_CONTACT, "grpadd #%d %p %p '%s'", i, cg, serv, cg->name);
103 return cg;
104 }
105
106 /*
107 * Iterates through the contact groups.
108 */
ContactGroupIndex(int i)109 ContactGroup *ContactGroupIndex (int i)
110 {
111 if (!cnt_groups)
112 ContactGroupInit ();
113 if (i >= 0 && i + 1 < cnt_count)
114 return cnt_groups[i + 1];
115 return NULL;
116 }
117
118 /*
119 * Finds and/or creates a contact group.
120 */
ContactGroupFind(Server * serv,UWORD id,const char * name)121 ContactGroup *ContactGroupFind (Server *serv, UWORD id, const char *name)
122 {
123 ContactGroup *cg;
124 int i;
125
126 if (!cnt_groups)
127 ContactGroupInit ();
128 for (i = 1; i < cnt_count; i++)
129 if ( (cg = cnt_groups[i])
130 && (!id || cg->id == id)
131 && (!serv || cg->serv == serv)
132 && (!name || !strcmp (cg->name, name)))
133 {
134 return cg;
135 }
136 return NULL;
137 }
138
139 /*
140 * Finds and/or creates a contact group.
141 */
ContactGroupFor(Contact * cont,ContactGroup * except)142 ContactGroup *ContactGroupFor (Contact *cont, ContactGroup *except)
143 {
144 ContactGroup *cg;
145 int i;
146
147 if (cont->group != cont->serv->contacts && cont->group != except)
148 return cont->group;
149 if (!cont->serv)
150 return NULL;
151 for (i = 0; (cg = ContactGroupIndex (i)); i++)
152 if (cg->serv == cont->serv && cg != except && cg != cont->serv->contacts && ContactHas (cg, cont))
153 return cg;
154 return cont->serv->contacts;
155 }
156
157 /*
158 * Returns the group id, if necessary create one
159 */
ContactGroupID(ContactGroup * group)160 UWORD ContactGroupID (ContactGroup *group)
161 {
162 while (!group->id)
163 {
164 ContactGroup *cg;
165 int i;
166
167 group->id = 16 + rand() % 0x7fef;
168 for (i = 0; (cg = ContactGroupIndex (i)); i++)
169 if (cg->id == group->id && cg != group)
170 group->id = 0;
171 }
172 return group->id;
173 }
174
175 /*
176 * Count the number of contacts in this list.
177 */
ContactGroupCount(ContactGroup * group)178 UDWORD ContactGroupCount (ContactGroup *group)
179 {
180 UDWORD c = 0;
181 while (group)
182 {
183 c += group->used;
184 group = group->more;
185 }
186 return c;
187 }
188
189 /*
190 * Remove a contact group.
191 */
192 #undef ContactGroupD
ContactGroupD(ContactGroup * group DEBUGPARAM)193 void ContactGroupD (ContactGroup *group DEBUGPARAM)
194 {
195 ContactGroup *tmp;
196 int i;
197
198 if (!cnt_groups)
199 ContactGroupInit ();
200 if (!group)
201 return;
202 /* remove cached positions that will become invalid */
203 ContactIndex (group, 0);
204 for (i = 1; i < cnt_count && cnt_groups[i]; i++)
205 if (cnt_groups[i] == group)
206 {
207 Debug (DEB_CONTACT, "grprem #%d %p", i, group);
208 memmove (cnt_groups + i, cnt_groups + i + 1, (cnt_count - i - 1) * sizeof (ContactGroup *));
209 s_repl (&group->name, NULL);
210 while (group)
211 {
212 tmp = group->more;
213 free (group);
214 group = tmp;
215 }
216 break;
217 }
218 }
219
ContactGroupSort(ContactGroup * group,contact_sort_func_t sort,int mode)220 void ContactGroupSort (ContactGroup *group, contact_sort_func_t sort, int mode)
221 {
222 ContactGroup *orig;
223 int i, j;
224 int found = 1, where = 0, res;
225 Contact **a = NULL, **b = NULL, *m;
226
227 if (!group)
228 {
229 if (!cnt_groups)
230 ContactGroupInit ();
231 group = CONTACTGROUP_GLOBAL;
232 }
233 /* remove cached positions that will become invalid */
234 ContactIndex (group, 0);
235 orig = group;
236 while (found)
237 {
238 found = 0;
239 j = 0;
240 i = 0;
241 b = NULL;
242 group = orig;
243 while (group)
244 {
245 if (i >= group->used)
246 {
247 i = 0;
248 group = group->more;
249 continue;
250 }
251 if (where && j > where)
252 break;
253 a = b;
254 b = &group->contacts[i];
255 i++;
256 j++;
257 if (!a)
258 continue;
259 res = sort (*a, *b, mode);
260 if (res < 0)
261 {
262 m = *b;
263 *b = *a;
264 *a = m;
265 found = j;
266 }
267 }
268 where = found;
269 }
270 }
271
ContactGroupAdd(ContactGroup * group,ContactGroup * add)272 void ContactGroupAdd (ContactGroup *group, ContactGroup *add)
273 {
274 Contact *cont;
275 int i;
276
277 assert (group);
278 for (i = 0; (cont = ContactIndex (add, i)); i++)
279 if (!ContactHas (group, cont))
280 ContactAdd (group, cont);
281 }
282
ContactGroupSub(ContactGroup * group,ContactGroup * sub)283 void ContactGroupSub (ContactGroup *group, ContactGroup *sub)
284 {
285 Contact *cont;
286 int i;
287
288 if (!sub)
289 return;
290 for (i = 0; (cont = ContactIndex (sub, i)); i++)
291 ContactRem (group, cont);
292 }
293
294 /*
295 * Iterate through contacts on a contact group
296 */
ContactIndex(ContactGroup * group,int i)297 Contact *ContactIndex (ContactGroup *group, int i)
298 {
299 static ContactGroup *old = NULL, *oldpos = NULL;
300 static int oldoff = 0;
301 ContactGroup *orig;
302 int j = 0;
303
304 if (!group)
305 {
306 if (!cnt_groups)
307 ContactGroupInit ();
308 group = CONTACTGROUP_GLOBAL;
309 }
310 orig = group;
311 if (old && old == group)
312 {
313 if (oldoff <= i)
314 {
315 group = oldpos;
316 i -= oldoff;
317 j = oldoff;
318 }
319 else
320 old = NULL;
321 }
322 while (group && group->used <= i)
323 {
324 i -= group->used;
325 j += group->used;
326 group = group->more;
327 }
328 if (!group)
329 return NULL;
330 if (j)
331 {
332 old = orig;
333 oldpos = group;
334 oldoff = j;
335 }
336 return group->contacts[i];
337 }
338
339 #undef ContactCUIN
ContactCUIN(Server * serv,UDWORD uin DEBUGPARAM)340 static Contact *ContactCUIN (Server *serv, UDWORD uin DEBUGPARAM)
341 {
342 Contact *cont;
343
344 assert (uin);
345 cont = calloc (1, sizeof (Contact));
346 if (!cont)
347 return NULL;
348
349 cont->ids = NULL;
350 cont->status = ims_offline;
351 cont->serv = serv;
352
353 cont->uin = uin;
354 s_repl (&cont->screen, s_sprintf ("%ld", UD2UL (uin)));
355 s_repl (&cont->nick, cont->screen);
356
357 Debug (DEB_CONTACT, "new %p UIN %s %p %p", cont, cont->screen, cont, serv);
358
359 if (!cnt_groups)
360 ContactGroupInit ();
361 ContactAdd (CONTACTGROUP_GLOBAL, cont);
362
363 return cont;
364 }
365
366 #undef ContactCScreen
367 static Contact *ContactCScreen (Server *serv, const char *screen DEBUGPARAM);
ContactCScreen(Server * serv,const char * screen DEBUGPARAM)368 static Contact *ContactCScreen (Server *serv, const char *screen DEBUGPARAM)
369 {
370 Contact *cont;
371
372 cont = calloc (1, sizeof (Contact));
373 if (!cont)
374 return NULL;
375
376 cont->ids = NULL;
377 cont->status = ims_offline;
378 cont->serv = serv;
379
380 cont->uin = 0;
381 s_repl (&cont->screen, screen);
382 s_repl (&cont->nick, cont->screen);
383
384 Debug (DEB_CONTACT, "new %p UIN '%s' %p %p", cont, cont->screen, cont, serv);
385
386 if (!cnt_groups)
387 ContactGroupInit ();
388 ContactAdd (CONTACTGROUP_GLOBAL, cont);
389
390 return cont;
391 }
392
393 /*
394 * Finds a contact on a contact group by UIN
395 */
ContactFindUIN(ContactGroup * group,UDWORD uin)396 Contact *ContactFindUIN (ContactGroup *group, UDWORD uin)
397 {
398 ContactGroup *tmp;
399 Contact *cont;
400 int i;
401
402 for (tmp = group; tmp; tmp = tmp->more)
403 {
404 for (i = 0; i < tmp->used; i++)
405 {
406 cont = tmp->contacts[i];
407 if (uin == cont->uin)
408 return cont;
409 }
410 }
411 return NULL;
412 }
413
414 /*
415 * Finds a contact on a contact group by screen name
416 */
ContactFindScreen(ContactGroup * group,const char * screen)417 Contact *ContactFindScreen (ContactGroup *group, const char *screen)
418 {
419 ContactGroup *tmp;
420 Contact *cont;
421 int i;
422
423 if (!screen)
424 return NULL;
425
426 for (tmp = group; tmp; tmp = tmp->more)
427 {
428 for (i = 0; i < tmp->used; i++)
429 {
430 cont = tmp->contacts[i];
431 if (!strcasecmp (screen, cont->screen))
432 return cont;
433 }
434 }
435 return NULL;
436 }
437
438 #if ENABLE_CONT_HIER
439 /*
440 * Finds a contact on a contact group by screen name under a parent
441 */
ContactFindScreenP(ContactGroup * group,Contact * parent,const char * screen)442 Contact *ContactFindScreenP (ContactGroup *group, Contact *parent, const char *screen)
443 {
444 ContactGroup *tmp;
445 Contact *cont;
446 int i;
447
448 if (!screen)
449 return NULL;
450
451 for (tmp = group; tmp; tmp = tmp->more)
452 {
453 for (i = 0; i < tmp->used; i++)
454 {
455 cont = tmp->contacts[i];
456 if (cont->parent == parent && !strcasecmp (screen, cont->screen))
457 return cont;
458 }
459 }
460 return NULL;
461 }
462 #endif
463
464 /*
465 * Finds a contact on a contact group by UIN
466 */
ContactFindSUIN(ContactGroup * group,Server * serv,UDWORD uin)467 static Contact *ContactFindSUIN (ContactGroup *group, Server *serv, UDWORD uin)
468 {
469 ContactGroup *tmp;
470 Contact *cont;
471 int i;
472
473 for (tmp = group; tmp; tmp = tmp->more)
474 {
475 for (i = 0; i < tmp->used; i++)
476 {
477 cont = tmp->contacts[i];
478 if (uin == cont->uin && serv == cont->serv)
479 return cont;
480 }
481 }
482 return NULL;
483 }
484
485 /*
486 * Finds a contact on a contact group by screen name
487 */
ContactFindSScreen(ContactGroup * group,Server * serv,const char * screen)488 static Contact *ContactFindSScreen (ContactGroup *group, Server *serv, const char *screen)
489 {
490 ContactGroup *tmp;
491 Contact *cont;
492 int i;
493
494 if (!screen)
495 return NULL;
496
497 for (tmp = group; tmp; tmp = tmp->more)
498 {
499 for (i = 0; i < tmp->used; i++)
500 {
501 cont = tmp->contacts[i];
502 if (serv == cont->serv && !strcasecmp (screen, cont->screen))
503 return cont;
504 }
505 }
506 return NULL;
507 }
508
509 #if ENABLE_CONT_HIER
510 /*
511 * Finds a contact on a contact group by screen name under a parent
512 */
ContactFindSScreenP(ContactGroup * group,Server * serv,Contact * parent,const char * screen)513 static Contact *ContactFindSScreenP (ContactGroup *group, Server *serv, Contact *parent, const char *screen)
514 {
515 ContactGroup *tmp;
516 Contact *cont;
517 int i;
518
519 if (!screen)
520 return NULL;
521
522 for (tmp = group; tmp; tmp = tmp->more)
523 {
524 for (i = 0; i < tmp->used; i++)
525 {
526 cont = tmp->contacts[i];
527 if (serv == cont->serv && cont->parent == parent && !strcasecmp (screen, cont->screen))
528 return cont;
529 }
530 }
531 return NULL;
532 }
533 #endif
534
535 /*
536 * Finds a contact for a connection
537 */
538 #undef ContactScreen
ContactScreen(Server * serv,const char * screen DEBUGPARAM)539 Contact *ContactScreen (Server *serv, const char *screen DEBUGPARAM)
540 {
541 Contact *cont;
542 UDWORD uin;
543
544 if (!serv->contacts || !*screen)
545 return NULL;
546
547 if (!cnt_groups)
548 ContactGroupInit ();
549
550 uin = IcqIsUIN (screen);
551 if (uin && serv->type & TYPEF_HAVEUIN)
552 return ContactUIN (serv, uin);
553
554 if ((cont = ContactFindScreen (serv->contacts, screen)))
555 return cont;
556
557 if ((cont = ContactFindSScreen (CONTACTGROUP_NONCONTACTS, serv, screen)))
558 return cont;
559
560 cont = ContactCScreen (serv, screen DEBUGFOR);
561 if (!cont)
562 return NULL;
563
564 ContactAdd (CONTACTGROUP_NONCONTACTS, cont);
565 return cont;
566 }
567
568 #if ENABLE_CONT_HIER
569 /*
570 * Finds a contact for a connection under a parent
571 */
572 #undef ContactScreenP
ContactScreenP(Server * serv,Contact * parent,const char * screen DEBUGPARAM)573 Contact *ContactScreenP (Server *serv, Contact *parent, const char *screen DEBUGPARAM)
574 {
575 Contact *cont;
576 UDWORD uin;
577
578 if (!serv->contacts || !*screen)
579 return NULL;
580
581 if (!cnt_groups)
582 ContactGroupInit ();
583
584 uin = IcqIsUIN (screen);
585 if (uin && serv->type & TYPEF_HAVEUIN)
586 return ContactUIN (serv, uin);
587
588 if ((cont = ContactFindScreenP (serv->contacts, parent, screen)))
589 return cont;
590
591 if ((cont = ContactFindSScreenP (CONTACTGROUP_NONCONTACTS, serv, parent, screen)))
592 return cont;
593
594 cont = ContactCScreen (serv, screen DEBUGFOR);
595 if (!cont)
596 return NULL;
597 cont->parent = parent;
598
599 ContactAdd (CONTACTGROUP_NONCONTACTS, cont);
600 return cont;
601 }
602 #endif
603
604 /*
605 * Finds a contact for a connection
606 */
607 #undef ContactUIN
ContactUIN(Server * serv,UDWORD uin DEBUGPARAM)608 Contact *ContactUIN (Server *serv, UDWORD uin DEBUGPARAM)
609 {
610 Contact *cont;
611
612 assert (serv->contacts);
613
614 if (!cnt_groups)
615 ContactGroupInit ();
616
617 if ((cont = ContactFindUIN (serv->contacts, uin)))
618 return cont;
619
620 if ((cont = ContactFindSUIN (CONTACTGROUP_NONCONTACTS, serv, uin)))
621 return cont;
622
623 cont = ContactCUIN (serv, uin DEBUGFOR);
624 if (!cont)
625 return cont;
626
627 ContactAdd (CONTACTGROUP_NONCONTACTS, cont);
628 return cont;
629 }
630
631 /*
632 * Finds an alias of a contact
633 */
ContactFindAlias(Contact * cont,const char * nick)634 const char *ContactFindAlias (Contact *cont, const char *nick)
635 {
636 ContactAlias *ca;
637
638 if (!strcasecmp (nick, cont->screen))
639 return cont->screen;
640 if (!strcasecmp (nick, cont->nick))
641 return cont->nick;
642 for (ca = cont->alias; ca; ca = ca->more)
643 if (!strcmp (nick, ca->alias))
644 return ca->alias;
645 return NULL;
646 }
647
648
649 /*
650 * Finds a contact by nick
651 */
ContactFind(Server * serv,const char * nick)652 Contact *ContactFind (Server *serv, const char *nick)
653 {
654 ContactGroup *tmp;
655 ContactAlias *ca;
656 Contact *cont;
657 int i;
658
659 for (tmp = serv->contacts; tmp; tmp = tmp->more)
660 {
661 for (i = 0; i < tmp->used; i++)
662 {
663 cont = tmp->contacts[i];
664 if (!strcasecmp (nick, cont->screen) || !strcasecmp (nick, cont->nick))
665 return cont;
666 for (ca = cont->alias; ca; ca = ca->more)
667 if (!strcmp (nick, ca->alias))
668 return cont;
669 }
670 }
671 return NULL;
672 }
673
674 #undef ContactCreate
ContactCreate(Server * serv,Contact * cont DEBUGPARAM)675 void ContactCreate (Server *serv, Contact *cont DEBUGPARAM)
676 {
677 assert (cont->serv == serv);
678 if (cont->group)
679 return;
680
681 if (!cnt_groups)
682 ContactGroupInit ();
683 ContactRem (CONTACTGROUP_NONCONTACTS, cont);
684 ContactAdd (serv->contacts, cont);
685 cont->group = serv->contacts;
686 Debug (DEB_CONTACT, "accc #%d %s '%s' %p in %p", 0, cont->screen, cont->nick, cont, serv->contacts);
687 }
688
689 /*
690 * Delete a contact by hiding it as good as possible.
691 */
692 #undef ContactD
ContactD(Contact * cont DEBUGPARAM)693 void ContactD (Contact *cont DEBUGPARAM)
694 {
695 ContactAlias *ca, *cao;
696 ContactGroup *cg;
697 ContactIDs *ids, *tids;
698 int i;
699
700 for (ca = cont->alias; ca; ca = cao)
701 {
702 cao = ca->more;
703 free (ca->alias);
704 free (ca);
705 }
706
707 for (ids = cont->ids; ids; ids = tids)
708 {
709 tids = ids->next;
710 free (ids);
711 }
712 cont->ids = NULL;
713 cont->alias = NULL;
714 s_repl (&cont->nick, cont->screen);
715
716 for (i = 0; (cg = ContactGroupIndex (i)); i++)
717 ContactRem (cg, cont);
718 if (!cnt_groups)
719 ContactGroupInit ();
720 ContactAdd (CONTACTGROUP_NONCONTACTS, cont);
721 cont->group = NULL;
722 Debug (DEB_CONTACT, "del #%d %s %p", 0, cont->screen, cont);
723 }
724
725
726 /*
727 * Adds a contact to a contact group.
728 */
729 #undef ContactAdd
ContactAdd(ContactGroup * group,Contact * cont DEBUGPARAM)730 BOOL ContactAdd (ContactGroup *group, Contact *cont DEBUGPARAM)
731 {
732 ContactGroup *orig = group;
733 assert (group);
734 assert (cont);
735 assert (!group->serv || group->serv == cont->serv);
736
737 /* remove cached positions that now will become invalid */
738 ContactIndex (group, 0);
739
740 while (group->used == MAX_ENTRIES && group->more)
741 group = group->more;
742 if (group->used == MAX_ENTRIES)
743 {
744 group->more = calloc (1, sizeof (ContactGroup));
745 if (!group->more)
746 return FALSE;
747 group = group->more;
748 }
749 group->contacts[group->used++] = cont;
750 Debug (DEB_CONTACT, "add #%d %s '%s' %p to %p", 0, cont->screen, cont->nick, cont, orig);
751 return TRUE;
752 }
753
754 /*
755 * Removes a contact from a contact group.
756 */
ContactHas(ContactGroup * group,Contact * cont)757 BOOL ContactHas (ContactGroup *group, Contact *cont)
758 {
759 int i;
760
761 while (group)
762 {
763 for (i = 0; i < group->used; i++)
764 if (group->contacts[i] == cont)
765 return TRUE;
766 group = group->more;
767 }
768 return FALSE;
769 }
770
771 /*
772 * Removes a contact from a contact group.
773 */
774 #undef ContactRem
ContactRem(ContactGroup * group,Contact * cont DEBUGPARAM)775 BOOL ContactRem (ContactGroup *group, Contact *cont DEBUGPARAM)
776 {
777 ContactGroup *orig = group;
778 int i;
779
780 /* remove cached positions that will become invalid */
781 ContactIndex (group, 0);
782 while (group)
783 {
784 for (i = 0; i < group->used; i++)
785 if (group->contacts[i] == cont)
786 {
787 if (orig->temp)
788 group->contacts[i] = NULL;
789 else
790 {
791 group->contacts[i] = group->contacts[--group->used];
792 group->contacts[group->used] = NULL;
793 }
794 if (cont->group == orig && orig)
795 {
796 if (cont->group->serv)
797 cont->group = cont->group->serv->contacts;
798 else
799 cont->group = NULL;
800 }
801 Debug (DEB_CONTACT, "rem #%d %s '%s' %p to %p", 0, cont->screen, cont->nick, cont, orig);
802 return TRUE;
803 }
804 group = group->more;
805 }
806 return FALSE;
807 }
808
809 /*
810 * Adds an alias to a contact.
811 */
812 #undef ContactAddAlias
ContactAddAlias(Contact * cont,const char * nick DEBUGPARAM)813 int ContactAddAlias (Contact *cont, const char *nick DEBUGPARAM)
814 {
815 ContactAlias **caref;
816 ContactAlias *ca;
817
818 if (!strcmp (cont->nick, nick))
819 return 1;
820
821 for (caref = &cont->alias; *caref; caref = &(*caref)->more)
822 if (!strcmp ((*caref)->alias, nick))
823 return 1;
824
825 if (!strcmp (cont->nick, cont->screen))
826 {
827 s_repl (&cont->nick, nick);
828 return 2;
829 }
830
831 if (!strcmp (nick, cont->screen))
832 return 1;
833
834 ca = calloc (1, sizeof (ContactAlias));
835 if (!ca)
836 return 0;
837
838 ca->alias = strdup (nick);
839 if (!ca->alias)
840 {
841 free (ca);
842 return 0;
843 }
844 *caref = ca;
845 Debug (DEB_CONTACT, "addal #%d %s '%s' A'%s'", 0, cont->screen, cont->nick, nick);
846 return 1;
847 }
848
849
850 /*
851 * Removes an alias from a contact.
852 */
853 #undef ContactRemAlias
ContactRemAlias(Contact * cont,const char * nick DEBUGPARAM)854 int ContactRemAlias (Contact *cont, const char *nick DEBUGPARAM)
855 {
856 ContactAlias **caref;
857 ContactAlias *ca;
858
859 if (!strcmp (cont->nick, nick))
860 {
861 char *nn;
862
863 if (cont->alias)
864 {
865 ca = cont->alias;
866 nn = cont->alias->alias;
867 cont->alias = cont->alias->more;
868 free (ca);
869 }
870 else
871 nn = strdup (cont->screen);
872 free (cont->nick);
873 cont->nick = nn;
874 Debug (DEB_CONTACT, "remal #%d %s N'%s' '%s'", 0, cont->screen, cont->nick, nick);
875 return 2;
876 }
877
878 for (caref = &cont->alias; *caref; caref = &(*caref)->more)
879 if (!strcmp ((*caref)->alias, nick))
880 {
881 free ((*caref)->alias);
882 ca = *caref;
883 *caref = (*caref)->more;
884 free (ca);
885 Debug (DEB_CONTACT, "remal #%d %s '%s' X'%s'", 0, cont->screen, cont->nick, nick);
886 return 1;
887 }
888
889 return 0;
890 }
891 /* -1 0 1 */
ContactStatusCmp(status_t a,status_t b)892 int ContactStatusCmp (status_t a, status_t b)
893 {
894 status_noi_t aa, bb;
895 aa = ContactClearInv (a);
896 bb = ContactClearInv (b);
897 if (aa == bb)
898 {
899 if (a == b)
900 return 0;
901 if (ContactIsInv (a) == ContactIsInv (b))
902 return 0;
903 return ContactIsInv (a) ? 1 : -1;
904 }
905 else
906 {
907 if (aa == imr_ffc) return -1;
908 if (bb == imr_ffc) return 1;
909 /* assumes further states are ordered by absentibility */
910 if (aa < bb) return -1;
911 return 1;
912 }
913 }
914
915 /*
916 * Returns the contact id for type, if available
917 */
ContactIDHas(Contact * cont,UWORD type)918 ContactIDs *ContactIDHas (Contact *cont, UWORD type)
919 {
920 ContactIDs *ids;
921
922 for (ids = cont->ids; ids; ids = ids->next)
923 if (ids->type == type)
924 return ids;
925
926 return NULL;
927 }
928
929 /*
930 * Returns the contact id for type, if necessary create one
931 */
ContactID(Contact * cont,UWORD type)932 ContactIDs *ContactID (Contact *cont, UWORD type)
933 {
934 ContactIDs **ids;
935 ContactIDs *id;
936
937 for (ids = &cont->ids; *ids; ids = &((*ids)->next))
938 if ((*ids)->type == type)
939 break;
940
941 if (!(id = *ids))
942 {
943 id = *ids = calloc (1, sizeof (ContactIDs));
944 id->type = type;
945 }
946 return id;
947 }
948
949 /*
950 * Returns the contact id for type, if necessary create one
951 */
ContactIDGet(Contact * cont,UWORD type)952 UWORD ContactIDGet (Contact *cont, UWORD type)
953 {
954 ContactIDs *id, *idt;
955
956 id = ContactID (cont, type);
957 while (!id->id)
958 {
959 Contact *c;
960 UWORD newid;
961 int i;
962
963 newid = 16 + rand() % 0x7fef;
964 for (i = 0; (c = ContactIndex (NULL, i)); i++)
965 for (idt = c->ids; idt; idt = idt->next)
966 if (idt->id == id->id)
967 id->id = 0;
968 id->id = newid;
969 }
970 id->tag = 0;
971 id->issbl = 0;
972 return id->id;
973 }
974
975 /*
976 * Set the contact id for type, if necessary create one
977 */
ContactIDSet(Contact * cont,UWORD type,UWORD id,UWORD tag)978 void ContactIDSet (Contact *cont, UWORD type, UWORD id, UWORD tag)
979 {
980 ContactIDs *idp, *idt;
981 Contact *c;
982 int i;
983
984 idp = ContactID (cont, type);
985 for (i = 0; (c = ContactIndex (NULL, i)); i++)
986 for (idt = c->ids; idt; idt = idt->next)
987 if (idt->id == id && idt->tag == tag)
988 {
989 idt->id = 0;
990 idt->tag = 0;
991 idt->issbl = 0;
992 }
993 idp->id = id;
994 idp->tag = tag;
995 idp->issbl = 1;
996 }
997
998 /*
999 * Save the contact's meta data to disc.
1000 */
ContactMetaSave(Contact * cont)1001 BOOL ContactMetaSave (Contact *cont)
1002 {
1003 ContactMeta *m;
1004 FILE *f;
1005
1006 if (!(f = fopen (s_sprintf ("%scontacts" _OS_PATHSEPSTR "icq-%s", PrefUserDir (prG), cont->screen), "w")))
1007 {
1008 mkdir (s_sprintf ("%scontacts", PrefUserDir (prG)), 0700);
1009 if (!(f = fopen (s_sprintf ("%scontacts" _OS_PATHSEPSTR "icq-%s", PrefUserDir (prG), cont->screen), "w")))
1010 return FALSE;
1011 }
1012 fprintf (f, "#\n# Meta data for contact %s.\n#\n\n", cont->screen);
1013 fprintf (f, "encoding UTF-8\n");
1014 fprintf (f, "format 1\n\n");
1015 fprintf (f, "b_uin %s\n", cont->screen);
1016 fprintf (f, "b_nick %s\n", s_quote (cont->nick));
1017 if (cont->meta_about)
1018 fprintf (f, "b_about %s\n", s_quote (cont->meta_about));
1019 if (cont->meta_general)
1020 {
1021 MetaGeneral *mg = cont->meta_general;
1022 fprintf (f, "g_nick %s\n", s_quote (mg->nick));
1023 fprintf (f, "g_first %s\n", s_quote (mg->first));
1024 fprintf (f, "g_last %s\n", s_quote (mg->last));
1025 fprintf (f, "g_email %s\n", s_quote (mg->email));
1026 fprintf (f, "g_city %s\n", s_quote (mg->city));
1027 fprintf (f, "g_state %s\n", s_quote (mg->state));
1028 fprintf (f, "g_phone %s\n", s_quote (mg->phone));
1029 fprintf (f, "g_fax %s\n", s_quote (mg->fax));
1030 fprintf (f, "g_zip %s\n", s_quote (mg->zip));
1031 fprintf (f, "g_street %s\n", s_quote (mg->street));
1032 fprintf (f, "g_cell %s\n", s_quote (mg->cellular));
1033 fprintf (f, "g_country %u\n", mg->country);
1034 fprintf (f, "g_tz %d\n", mg->tz);
1035 fprintf (f, "g_flags %u\n", (mg->auth ? 1 : 0)
1036 + (mg->webaware ? 2 : 0));
1037 }
1038 if (cont->meta_work)
1039 {
1040 MetaWork *mw = cont->meta_work;
1041 fprintf (f, "w_city %s\n", s_quote (mw->wcity));
1042 fprintf (f, "w_state %s\n", s_quote (mw->wstate));
1043 fprintf (f, "w_phone %s\n", s_quote (mw->wphone));
1044 fprintf (f, "w_fax %s\n", s_quote (mw->wfax));
1045 fprintf (f, "w_address %s\n", s_quote (mw->waddress));
1046 fprintf (f, "w_zip %s\n", s_quote (mw->wzip));
1047 fprintf (f, "w_company %s\n", s_quote (mw->wcompany));
1048 fprintf (f, "w_depart %s\n", s_quote (mw->wdepart));
1049 fprintf (f, "w_position %s\n", s_quote (mw->wposition));
1050 fprintf (f, "w_homepage %s\n", s_quote (mw->whomepage));
1051 fprintf (f, "w_country %u\n", mw->wcountry);
1052 fprintf (f, "w_occup %u\n", mw->woccupation);
1053 }
1054 if (cont->meta_more)
1055 {
1056 MetaMore *mm = cont->meta_more;
1057 fprintf (f, "m_homepage %s\n", s_quote (mm->homepage));
1058 fprintf (f, "m_age %u\n", mm->age);
1059 fprintf (f, "m_year %u\n", mm->year);
1060 fprintf (f, "m_unknown %u\n", mm->unknown);
1061 fprintf (f, "m_sex %u\n", mm->sex);
1062 fprintf (f, "m_month %u\n", mm->month);
1063 fprintf (f, "m_day %u\n", mm->day);
1064 fprintf (f, "m_lang1 %u\n", mm->lang1);
1065 fprintf (f, "m_lang2 %u\n", mm->lang2);
1066 fprintf (f, "m_lang3 %u\n", mm->lang3);
1067 }
1068 if (cont->meta_obsolete)
1069 fprintf (f, "obsolete %u %u %u %s\n", cont->meta_obsolete->given,
1070 cont->meta_obsolete->empty, cont->meta_obsolete->unknown,
1071 s_quote (cont->meta_obsolete->description));
1072 for (m = cont->meta_email; m; m = m->next)
1073 fprintf (f, "email %u %s\n", m->data, s_quote (m->text));
1074 for (m = cont->meta_interest; m; m = m->next)
1075 fprintf (f, "interest %u %s\n", m->data, s_quote (m->text));
1076 for (m = cont->meta_background; m; m = m->next)
1077 fprintf (f, "background %u %s\n", m->data, s_quote (m->text));
1078 for (m = cont->meta_affiliation; m; m = m->next)
1079 fprintf (f, "affiliation %u %s\n", m->data, s_quote (m->text));
1080 if (fclose (f))
1081 return FALSE;
1082 cont->updated |= UPF_DISC;
1083 return TRUE;
1084 }
1085
1086 /*
1087 * Destruct a contact meta list
1088 */
ContactMetaD(ContactMeta * m)1089 void ContactMetaD (ContactMeta *m)
1090 {
1091 ContactMeta *mm;
1092 while (m)
1093 {
1094 if (m->text)
1095 free (m->text);
1096 mm = m->next;
1097 free (m);
1098 m = mm;
1099 }
1100 }
1101
1102 /*
1103 * Add an entry to a contact meta list
1104 */
ContactMetaAdd(ContactMeta ** m,UWORD val,const char * text)1105 void ContactMetaAdd (ContactMeta **m, UWORD val, const char *text)
1106 {
1107 while (*m)
1108 m = &(*m)->next;
1109 *m = calloc (4 + sizeof (ContactMeta), 1);
1110 (*m)->data = val;
1111 (*m)->text = strdup (text);
1112 }
1113
1114
1115 /*
1116 *
1117 */
ContactMetaLoad(Contact * cont)1118 BOOL ContactMetaLoad (Contact *cont)
1119 {
1120 UBYTE enc;
1121 FILE *f;
1122 char *cmd;
1123 strc_t par, lline;
1124 const char *line;
1125 UDWORD i;
1126
1127 if (!(f = fopen (s_sprintf ("%scontacts" _OS_PATHSEPSTR "icq-%s", PrefUserDir (prG), cont->screen), "r")))
1128 return FALSE;
1129
1130 cont->updated = 0;
1131 ContactMetaD (cont->meta_email);
1132 ContactMetaD (cont->meta_interest);
1133 ContactMetaD (cont->meta_background);
1134 ContactMetaD (cont->meta_affiliation);
1135 cont->meta_email = NULL;
1136 cont->meta_interest = NULL;
1137 cont->meta_background = NULL;
1138 cont->meta_affiliation = NULL;
1139
1140 enc = ENC_UTF8;
1141 while ((lline = UtilIOReadline (f)))
1142 {
1143 if (!lline->len || (lline->txt[0] == '#'))
1144 continue;
1145 line = ConvFrom (lline, enc)->txt;
1146 if (!(par = s_parse (&line)))
1147 continue;
1148 cmd = par->txt;
1149 if (!strcmp (cmd, "encoding"))
1150 {
1151 if (!(par = s_parse (&line)))
1152 return FALSE;
1153 enc = ConvEnc (par->txt);
1154 if (enc & ENC_FERR && (enc ^ prG->enc_loc) & ~ENC_FLAGS)
1155 return FALSE;
1156 enc &= ~ENC_FLAGS;
1157 }
1158 else if (!enc)
1159 return FALSE;
1160 else if (!strncmp (cmd, "b_", 2))
1161 {
1162 if (!strcmp (cmd, "b_uin")) { if (s_parseint (&line, &i) && i != cont->uin) return FALSE; }
1163 else if (!strcmp (cmd, "b_id")) { s_parseint (&line, &i); /* deprecated */ }
1164 else if (!strcmp (cmd, "b_nick")) { s_parse (&line); /* ignore for now */ }
1165 else if (!strcmp (cmd, "b_alias")) { s_parse (&line); /* deprecated */ }
1166 else if (!strcmp (cmd, "b_enc")) { s_parse (&line); /* deprecated */ }
1167 else if (!strcmp (cmd, "b_flags")) { s_parseint (&line, &i); /* ignore for compatibility */ }
1168 else if (!strcmp (cmd, "b_about")) { if ((par = s_parse (&line))) s_repl (&cont->meta_about, par->txt); }
1169 else if (!strcmp (cmd, "b_seen")) { s_parseint (&line, &i); /* deprecated */ }
1170 else if (!strcmp (cmd, "b_micq")) { s_parseint (&line, &i); /* deprecated */ }
1171 }
1172 else if (!strncmp (cmd, "g_", 2))
1173 {
1174 MetaGeneral *mg = CONTACT_GENERAL (cont);
1175 if (!strcmp (cmd, "g_nick")) { if ((par = s_parse (&line))) s_repl (&mg->first, par->txt); }
1176 else if (!strcmp (cmd, "g_first")) { if ((par = s_parse (&line))) s_repl (&mg->first, par->txt); }
1177 else if (!strcmp (cmd, "g_last")) { if ((par = s_parse (&line))) s_repl (&mg->last, par->txt); }
1178 else if (!strcmp (cmd, "g_email")) { if ((par = s_parse (&line))) s_repl (&mg->email, par->txt); }
1179 else if (!strcmp (cmd, "g_city")) { if ((par = s_parse (&line))) s_repl (&mg->city, par->txt); }
1180 else if (!strcmp (cmd, "g_state")) { if ((par = s_parse (&line))) s_repl (&mg->state, par->txt); }
1181 else if (!strcmp (cmd, "g_phone")) { if ((par = s_parse (&line))) s_repl (&mg->phone, par->txt); }
1182 else if (!strcmp (cmd, "g_fax")) { if ((par = s_parse (&line))) s_repl (&mg->fax, par->txt); }
1183 else if (!strcmp (cmd, "g_zip")) { if ((par = s_parse (&line))) s_repl (&mg->zip, par->txt); }
1184 else if (!strcmp (cmd, "g_street")) { if ((par = s_parse (&line))) s_repl (&mg->street, par->txt); }
1185 else if (!strcmp (cmd, "g_cell")) { if ((par = s_parse (&line))) s_repl (&mg->cellular, par->txt); }
1186 else if (!strcmp (cmd, "g_country")) { if (s_parseint (&line, &i)) mg->country = i; }
1187 else if (!strcmp (cmd, "g_tz")) { if (s_parseint (&line, &i)) mg->tz = i; }
1188 else if (!strcmp (cmd, "g_flags")) { if (s_parseint (&line, &i))
1189 {
1190 mg->auth = i & 1 ? 1 : 0;
1191 mg->webaware = i & 2 ? 1 : 0;
1192 }}
1193 }
1194 else if (!strncmp (cmd, "w_", 2))
1195 {
1196 MetaWork *mw = CONTACT_WORK (cont);
1197 if (!strcmp (cmd, "w_city")) { if ((par = s_parse (&line))) s_repl (&mw->wcity, par->txt); }
1198 else if (!strcmp (cmd, "w_state")) { if ((par = s_parse (&line))) s_repl (&mw->wstate, par->txt); }
1199 else if (!strcmp (cmd, "w_phone")) { if ((par = s_parse (&line))) s_repl (&mw->wphone, par->txt); }
1200 else if (!strcmp (cmd, "w_fax")) { if ((par = s_parse (&line))) s_repl (&mw->wfax, par->txt); }
1201 else if (!strcmp (cmd, "w_address")) { if ((par = s_parse (&line))) s_repl (&mw->waddress, par->txt); }
1202 else if (!strcmp (cmd, "w_zip")) { if ((par = s_parse (&line))) s_repl (&mw->wzip, par->txt); }
1203 else if (!strcmp (cmd, "w_company")) { if ((par = s_parse (&line))) s_repl (&mw->wcompany, par->txt); }
1204 else if (!strcmp (cmd, "w_depart")) { if ((par = s_parse (&line))) s_repl (&mw->wdepart, par->txt); }
1205 else if (!strcmp (cmd, "w_position")) { if ((par = s_parse (&line))) s_repl (&mw->wposition, par->txt); }
1206 else if (!strcmp (cmd, "w_homepage")) { if ((par = s_parse (&line))) s_repl (&mw->whomepage, par->txt); }
1207 else if (!strcmp (cmd, "w_country")) { if (s_parseint (&line, &i)) mw->wcountry = i; }
1208 else if (!strcmp (cmd, "w_occup")) { if (s_parseint (&line, &i)) mw->woccupation = i; }
1209 }
1210 else if (!strncmp (cmd, "m_", 2))
1211 {
1212 MetaMore *mm = CONTACT_MORE (cont);
1213 if (!strcmp (cmd, "m_homepage")) { if ((par = s_parse (&line))) s_repl (&mm->homepage, par->txt); }
1214 else if (!strcmp (cmd, "m_age")) { if (s_parseint (&line, &i)) mm->age = i; }
1215 else if (!strcmp (cmd, "m_year")) { if (s_parseint (&line, &i)) mm->year = i; }
1216 else if (!strcmp (cmd, "m_unknown")) { if (s_parseint (&line, &i)) mm->unknown = i; }
1217 else if (!strcmp (cmd, "m_sex")) { if (s_parseint (&line, &i)) mm->sex = i; }
1218 else if (!strcmp (cmd, "m_month")) { if (s_parseint (&line, &i)) mm->month = i; }
1219 else if (!strcmp (cmd, "m_day")) { if (s_parseint (&line, &i)) mm->day = i; }
1220 else if (!strcmp (cmd, "m_lang1")) { if (s_parseint (&line, &i)) mm->lang1 = i; }
1221 else if (!strcmp (cmd, "m_lang2")) { if (s_parseint (&line, &i)) mm->lang2 = i; }
1222 else if (!strcmp (cmd, "m_lang3")) { if (s_parseint (&line, &i)) mm->lang3 = i; }
1223 }
1224 else if (!strcmp (cmd, "obsolete"))
1225 {
1226 MetaObsolete *mo = CONTACT_OBSOLETE (cont);
1227 if (s_parseint (&line, &i)) mo->given = i;
1228 if (s_parseint (&line, &i)) mo->empty = i;
1229 if (s_parseint (&line, &i)) mo->unknown = i;
1230 if ((par = s_parse (&line))) s_repl (&mo->description, par->txt);
1231 }
1232 else if (!strcmp (cmd, "email"))
1233 {
1234 if (s_parseint (&line, &i) && (par = s_parse (&line)))
1235 ContactMetaAdd (&cont->meta_email, i, par->txt);
1236 }
1237 else if (!strcmp (cmd, "interest"))
1238 {
1239 if (s_parseint (&line, &i) && (par = s_parse (&line)))
1240 ContactMetaAdd (&cont->meta_interest, i, par->txt);
1241 }
1242 else if (!strcmp (cmd, "background"))
1243 {
1244 if (s_parseint (&line, &i) && (par = s_parse (&line)))
1245 ContactMetaAdd (&cont->meta_background, i, par->txt);
1246 }
1247 else if (!strcmp (cmd, "affiliation"))
1248 {
1249 if (s_parseint (&line, &i) && (par = s_parse (&line)))
1250 ContactMetaAdd (&cont->meta_affiliation, i, par->txt);
1251 }
1252 else if (!strcmp (cmd, "format"))
1253 {
1254 s_parseint (&line, &i); /* ignored for now */
1255 }
1256 #ifdef WIP
1257 if ((par = s_parse (&line)))
1258 rl_printf ("FIXMEWIP: Ignored trailing stuff: '%s' from '%s'.\n", par->txt, line);
1259 #endif
1260 }
1261 if (fclose (f))
1262 return FALSE;
1263 cont->updated |= UPF_DISC;
1264 return TRUE;
1265 }
1266
1267 /*
1268 * Query an option for a contact group.
1269 */
ContactGroupPrefVal(ContactGroup * cg,UDWORD flag)1270 val_t ContactGroupPrefVal (ContactGroup *cg, UDWORD flag)
1271 {
1272 val_t res = 0;
1273
1274 if (cg)
1275 {
1276 if (OptGetVal (&cg->copts, flag, &res))
1277 return res;
1278 if (cg->serv && OptGetVal (&cg->serv->copts, flag, &res))
1279 return res;
1280 }
1281
1282 if (OptGetVal (&prG->copts, flag, &res))
1283 return res;
1284 return 0;
1285 }
1286
1287 /*
1288 * Query an option for a contact.
1289 */
ContactPrefVal(Contact * ocont,UDWORD flag)1290 val_t ContactPrefVal (Contact *ocont, UDWORD flag)
1291 {
1292 Contact *cont;
1293 val_t res = 0;
1294
1295 #if ENABLE_CONT_HIER
1296 for (cont = ocont; cont; cont = cont->parent)
1297 #else
1298 for (cont = ocont; cont; cont = NULL)
1299 #endif
1300 if (OptGetVal (&cont->copts, flag, &res))
1301 return res;
1302 #if ENABLE_CONT_HIER
1303 for (cont = ocont; cont; cont = cont->parent)
1304 #else
1305 for (cont = ocont; cont; cont = NULL)
1306 #endif
1307 if (cont->group)
1308 {
1309 if (OptGetVal (&cont->group->copts, flag, &res))
1310 return res;
1311 if (cont->group->serv && OptGetVal (&cont->group->serv->copts, flag, &res))
1312 return res;
1313 }
1314 if (OptGetVal (&prG->copts, flag, &res))
1315 return res;
1316 return 0;
1317 }
1318
1319 /*
1320 * Query an option for a contact.
1321 */
ContactPrefValCg(Contact * cont,ContactGroup * cg,UDWORD flag)1322 val_t ContactPrefValCg (Contact *cont, ContactGroup *cg, UDWORD flag)
1323 {
1324 ContactGroup *cgo = cont->group;
1325 val_t res;
1326 cont->group = cg;
1327 res = ContactPrefVal (cont, flag);
1328 cont->group = cgo;
1329 return res;
1330 }
1331
1332
1333 /*
1334 * Query a string option for a contact.
1335 */
ContactPrefStr(Contact * ocont,UDWORD flag)1336 const char *ContactPrefStr (Contact *ocont, UDWORD flag)
1337 {
1338 Contact *cont;
1339 const char *res = NULL;
1340
1341 #if ENABLE_CONT_HIER
1342 for (cont = ocont; cont; cont = cont->parent)
1343 #else
1344 for (cont = ocont; cont; cont = NULL)
1345 #endif
1346 if (OptGetStr (&cont->copts, flag, &res))
1347 return res;
1348 #if ENABLE_CONT_HIER
1349 for (cont = ocont; cont; cont = cont->parent)
1350 #else
1351 for (cont = ocont; cont; cont = NULL)
1352 #endif
1353 if (cont->group)
1354 {
1355 if (OptGetStr (&cont->group->copts, flag, &res))
1356 return res;
1357 if (cont->group->serv && OptGetStr (&cont->group->serv->copts, flag, &res))
1358 return res;
1359 }
1360 if (OptGetStr (&prG->copts, flag, &res))
1361 return res;
1362 if (~flag & COF_COLOR || flag == CO_COLORNONE)
1363 return "";
1364 #if ENABLE_CONT_HIER
1365 for (cont = ocont; cont; cont = cont->parent)
1366 #else
1367 for (cont = ocont; cont; cont = NULL)
1368 #endif
1369 if (OptGetStr (&cont->copts, CO_COLORNONE, &res))
1370 return res;
1371 #if ENABLE_CONT_HIER
1372 for (cont = ocont; cont; cont = cont->parent)
1373 #else
1374 for (cont = ocont; cont; cont = NULL)
1375 #endif
1376 if (cont->group)
1377 {
1378 if (OptGetStr (&cont->group->copts, CO_COLORNONE, &res))
1379 return res;
1380 if (cont->group->serv && OptGetStr (&cont->group->serv->copts, CO_COLORNONE, &res))
1381 return res;
1382 }
1383 if (OptGetStr (&prG->copts, CO_COLORNONE, &res))
1384 return res;
1385 return "";
1386 }
1387
1388 /*
1389 * Set a capability for the contact.
1390 */
ContactSetCap(Contact * cont,Cap * cap)1391 void ContactSetCap (Contact *cont, Cap *cap)
1392 {
1393 if (!cap->id)
1394 return;
1395 if (cap->var && cap->id == CAP_SIM)
1396 {
1397 UBYTE ver;
1398
1399 ver = cap->var[15];
1400 if (ver >> 6) /* old SIM */
1401 {
1402 cont->v1 = (ver >> 6) - 1;
1403 cont->v2 = ver & 0x1f;
1404 cont->v3 = cont->v4 = 0;
1405 if (ver <= 0x48)
1406 CLR_CAP (cont->caps, CAP_UTF8);
1407 }
1408 else /* old KOPETE */
1409 {
1410 cont->v1 = 0;
1411 cont->v3 = ver & 0x1f;
1412 cont->v2 = cont->v4 = 0;
1413 if (ver <= 1)
1414 CLR_CAP (cont->caps, CAP_UTF8);
1415 }
1416 }
1417 else if (cap->var && (cap->id == CAP_MICQ || cap->id == CAP_CLIMM || cap->id == CAP_SIMNEW
1418 || cap->id == CAP_KOPETE || cap->id == CAP_LICQNEW))
1419 {
1420 cont->v1 = cap->var[12];
1421 cont->v2 = cap->var[13];
1422 cont->v3 = cap->var[14];
1423 cont->v4 = cap->var[15];
1424 }
1425 else if (cap->var && (cap->id == CAP_MIRANDA))
1426 {
1427 cont->v1 = cap->var[8];
1428 cont->v2 = cap->var[9];
1429 cont->v3 = cap->var[10];
1430 cont->v4 = cap->var[11];
1431 }
1432 else if (cap->var && (cap->id == CAP_ARQ))
1433 {
1434 cont->v1 = cap->var[9];
1435 cont->v2 = cap->var[10];
1436 cont->v3 = cap->var[11];
1437 cont->v4 = cap->var[12];
1438 }
1439 SET_CAP (cont->caps, cap->id);
1440 }
1441
1442 /*
1443 * Guess the contacts client from time stamps.
1444 */
1445 #define BUILD_LICQ 0x7d000000UL
1446 #define BUILD_SSL 0x00800000UL
1447
1448 #define BUILD_TRILLIAN_ID1 0x3b75ac09UL
1449 #define BUILD_TRILLIAN_ID2 0x3bae70b6UL
1450 #define BUILD_TRILLIAN_ID3 0x3b744adbUL
1451
1452 #define BUILD_LIBICQ2K_ID1 0x3aa773eeUL
1453 #define BUILD_LIBICQ2K_ID2 0x3aa66380UL
1454 #define BUILD_LIBICQ2K_ID3 0x3a877a42UL
1455
1456 #define BUILD_KXICQ_ID1 0x3b4c4c0cUL
1457 #define BUILD_KXICQ_ID2 0UL
1458 #define BUILD_KXICQ_ID3 0x3b7248edUL
1459
1460 #define BUILD_KXICQ2_ID1 0x3aa773eeUL
1461 #define BUILD_KXICQ2_ID2 0x3aa66380UL
1462 #define BUILD_KXICQ2_ID3 0x3a877a42UL
1463
ContactSetVersion(Contact * cont)1464 void ContactSetVersion (Contact *cont)
1465 {
1466 char buf[100];
1467 char *new = NULL, *tail = NULL;
1468 unsigned int ver;
1469 ContactDC *dc;
1470
1471 if (!(dc = cont->dc))
1472 {
1473 s_repl (&cont->version, NULL);
1474 return;
1475 }
1476
1477 ver = dc->id1 & 0xffff;
1478
1479 if (HAS_CAP (cont->caps, CAP_CLIMM))
1480 {
1481 new = "climm";
1482 OptSetVal (&cont->copts, CO_TIMECLIMM, time (NULL));
1483 if (cont->v1 & 0x80)
1484 tail = " svn";
1485 cont->v1 &= ~0x80;
1486 }
1487 else if (HAS_CAP (cont->caps, CAP_MICQ))
1488 {
1489 new = "mICQ";
1490 OptSetVal (&cont->copts, CO_TIMECLIMM, time (NULL));
1491 if (cont->v1 & 0x80)
1492 tail = " cvs";
1493 cont->v1 &= ~0x80;
1494 }
1495 else if (HAS_CAP (cont->caps, CAP_SIMNEW))
1496 {
1497 new = "SIM";
1498 if (cont->v4 & 0x80)
1499 tail = "/w32";
1500 else if (cont->v4 & 0x40)
1501 tail = "/OSX";
1502 }
1503 else if (HAS_CAP (cont->caps, CAP_LICQNEW))
1504 {
1505 new = "licq";
1506 if (cont->v2 / 100 == cont->v1)
1507 cont->v2 %= 100; /* bug in licq 1.3.0 */
1508 if (cont->v4 == 1)
1509 tail = "/SSL";
1510 }
1511 else if (HAS_CAP (cont->caps, CAP_KOPETE))
1512 new = "Kopete";
1513 else if (HAS_CAP (cont->caps, CAP_MIRANDA))
1514 {
1515 new = "Miranda";
1516 if (((cont->v1 << 24) | (cont->v2 << 16) | (cont->v3 << 8) | cont->v4) <= 0x00010202 && dc->version >= 8)
1517 dc->version = 7;
1518 if (cont->v1 & 0x80)
1519 tail = " cvs";
1520 cont->v1 &= ~0x80;
1521 }
1522 else if (HAS_CAP (cont->caps, CAP_SIM))
1523 {
1524 if (cont->v1 || cont->v2)
1525 new = "SIM";
1526 else
1527 new = "Kopete";
1528 }
1529 else if (HAS_CAP (cont->caps, CAP_ARQ))
1530 new = "&RQ";
1531 else if ((cont->v1 = cont->v2 = cont->v3 = cont->v4 = 0))
1532 assert (0);
1533 else if (HAS_CAP (cont->caps, CAP_TRILL_CRYPT) || HAS_CAP (cont->caps, CAP_TRILL_2))
1534 {
1535 if (HAS_CAP (cont->caps, CAP_RTFMSGS))
1536 new = "Trillian v3";
1537 else
1538 new = "Trillian";
1539 }
1540 else if (HAS_CAP (cont->caps, CAP_LICQ))
1541 new = "licq";
1542 else if (HAS_CAP (cont->caps, CAP_MACICQ))
1543 new = "ICQ for Mac";
1544 else if (HAS_CAP (cont->caps, CAP_KXICQ))
1545 new = "KXicq2";
1546 else if (HAS_CAP (cont->caps, CAP_IM2))
1547 new = "IM2";
1548 else if (HAS_CAP (cont->caps, CAP_QIP))
1549 new = "QIP";
1550 else if ((dc->id1 & 0xff7f0000UL) == BUILD_LICQ && ver > 1000)
1551 {
1552 new = "licq";
1553 if (dc->id1 & BUILD_SSL)
1554 tail = "/SSL";
1555 cont->v1 = ver / 1000;
1556 cont->v2 = (ver / 10) % 100;
1557 cont->v3 = ver % 10;
1558 cont->v4 = 0;
1559 }
1560 else if ((dc->id1 & 0xffff0000UL) == 0xffff0000UL)
1561 {
1562 cont->v1 = (dc->id2 & 0x7f000000) >> 24;
1563 cont->v2 = (dc->id2 & 0xff0000) >> 16;
1564 cont->v3 = (dc->id2 & 0xff00) >> 8;
1565 cont->v4 = dc->id2 & 0xff;
1566 switch ((UDWORD)dc->id1)
1567 {
1568 case 0xffffffffUL:
1569 if (((UDWORD)dc->id2) == 0xffffffffUL)
1570 new = "Gaim";
1571 else if (!dc->id2 && dc->version == 7)
1572 new = "WebICQ";
1573 else
1574 {
1575 if (dc->id2 <= 0x00010202 && dc->version >= 8)
1576 dc->version = 7;
1577 new = "Miranda";
1578 if (dc->id2 & 0x80000000)
1579 tail = " cvs";
1580 }
1581 break;
1582 case 0xfffffffeUL:
1583 new = "MobICQ";
1584 break;
1585 case 0xffffff8fUL:
1586 new = "StrICQ";
1587 break;
1588 case BUILD_CLIMM:
1589 OptSetVal (&cont->copts, CO_TIMECLIMM, time (NULL));
1590 new = (cont->v1 || cont->v2 >= 6) ? "climm" : "mICQ";
1591 if (dc->id2 & 0x80000000)
1592 tail = " cvs";
1593 break;
1594 case 0xffffffabUL:
1595 new = "YSM";
1596 if ((cont->v1 | cont->v2 | cont->v3 | cont->v4) & 0x80)
1597 cont->v1 = cont->v2 = cont->v3 = cont->v4 = 0;
1598 break;
1599 case 0xffffff7fUL:
1600 new = "&RQ";
1601 break;
1602 case 0xffffffbeUL:
1603 new = "alicq";
1604 break;
1605 default:
1606 snprintf (buf, sizeof (buf), "%08lx", (long)dc->id1);
1607 new = buf;
1608 }
1609 }
1610 else if (dc->id1 == 0x04031980UL)
1611 {
1612 cont->v1 = 0;
1613 cont->v2 = 43;
1614 cont->v3 = dc->id2 & 0xffff;
1615 cont->v4 = (dc->id2 & 0x7fff0000) >> 16;
1616 new = "vICQ";
1617 }
1618 else if (dc->id1 == BUILD_TRILLIAN_ID1 &&
1619 dc->id2 == BUILD_TRILLIAN_ID2 &&
1620 dc->id3 == BUILD_TRILLIAN_ID3)
1621 {
1622 new = "Trillian";
1623 /* Trillian only understands unicode in type-1 messages */
1624 CLR_CAP (cont->caps, CAP_UTF8);
1625 }
1626 else if (dc->id1 == BUILD_LIBICQ2K_ID1 &&
1627 dc->id2 == BUILD_LIBICQ2K_ID2 &&
1628 dc->id3 == BUILD_LIBICQ2K_ID3)
1629 {
1630 if (HAS_CAP (cont->caps, CAP_RTFMSGS))
1631 new = "centericq";
1632 else if (HAS_CAP (cont->caps, CAP_UTF8))
1633 new = "IcyJuice";
1634 else
1635 new = "libicq2000";
1636 }
1637 else if (dc->id1 == BUILD_KXICQ_ID1 &&
1638 dc->id2 == BUILD_KXICQ_ID2 &&
1639 dc->id3 == BUILD_KXICQ_ID3)
1640 {
1641 new = "KXicq2";
1642 }
1643 else if (dc->id1 == BUILD_KXICQ2_ID1 &&
1644 dc->id2 == BUILD_KXICQ2_ID2 &&
1645 dc->id3 == BUILD_KXICQ2_ID3)
1646 {
1647 new = "KXicq2 > 0.7.6";
1648 }
1649 else if (dc->id1 == 0x3FF19BEBUL &&
1650 dc->id2 == 0x3FF19BEBUL)
1651 new = "IM2";
1652 else if (dc->id1 == dc->id2 && dc->id2 == dc->id3 && dc->id1 == -1)
1653 new = "vICQ/GAIM(?)";
1654 else if (dc->version == 7 && HAS_CAP (cont->caps, CAP_RTFMSGS))
1655 new = "GnomeICU";
1656 else if (dc->version == 7 && HAS_CAP (cont->caps, CAP_SRVRELAY))
1657 new = "ICQ 2000";
1658 else if (dc->version == 7 && HAS_CAP (cont->caps, CAP_TYPING))
1659 new = "ICQ2go (Java)";
1660 else if (dc->version == 8 && HAS_CAP (cont->caps, CAP_STR_2002) && HAS_CAP (cont->caps, CAP_UTF8))
1661 new = "ICQ 2002";
1662 else if (dc->version == 8 && HAS_CAP (cont->caps, CAP_STR_2001) && HAS_CAP (cont->caps, CAP_IS_2001) && !dc->id1 && !dc->id2 && !dc->id3)
1663 new = "ICQ for Pocket PC";
1664 else if (dc->version == 8 && HAS_CAP (cont->caps, CAP_STR_2001) && HAS_CAP (cont->caps, CAP_IS_2001))
1665 new = "ICQ 2001";
1666 else if (dc->version == 8 && HAS_CAP (cont->caps, CAP_SRVRELAY) && HAS_CAP (cont->caps, CAP_RTFMSGS))
1667 new = "ICQ 2002/2003a";
1668 else if (dc->version == 8 && HAS_CAP (cont->caps, CAP_UTF8) && HAS_CAP (cont->caps, CAP_RTFMSGS))
1669 new = "ICQ 2002/2003a";
1670 else if (dc->version == 9 && HAS_CAP (cont->caps, CAP_TYPING) && HAS_CAP (cont->caps, CAP_XTRAZ) && HAS_CAP (cont->caps, CAP_AIM_SFILE))
1671 new = "ICQ5";
1672 else if (dc->version == 9 && HAS_CAP (cont->caps, CAP_TYPING) && HAS_CAP (cont->caps, CAP_XTRAZ))
1673 new = "ICQ Lite";
1674 else if (dc->version == 10 && HAS_CAP (cont->caps, CAP_STR_2002) && HAS_CAP (cont->caps, CAP_UTF8))
1675 new = "ICQ 2003b";
1676 else if (dc->version == 10 && HAS_CAP (cont->caps, CAP_STR_2002) && HAS_CAP (cont->caps, CAP_RTFMSGS))
1677 new = "ICQ 2003b";
1678 else if (dc->version == 10 && HAS_CAP (cont->caps, CAP_UTF8))
1679 new = "ICQ 2003b (?)";
1680 else if (dc->version == 10 && HAS_CAP (cont->caps, CAP_RTFMSGS))
1681 new = "ICQ 2003b (?)";
1682 else if (dc->version == 10)
1683 {
1684 CLR_CAP (cont->caps, CAP_SRVRELAY);
1685 new = "QNext";
1686 }
1687 else if (HAS_CAP (cont->caps, CAP_AIM_SFILE) && HAS_CAP(cont->caps, CAP_AIM_IMIMAGE)
1688 && HAS_CAP (cont->caps, CAP_AIM_BUDICON) && HAS_CAP(cont->caps, CAP_UTF8))
1689 new = "GAIM (ICQ) (?)";
1690 else if (dc->version == 9 && HAS_CAP (cont->caps, CAP_TYPING))
1691 new = "ICQ Lite (?)";
1692 else if (dc->version == 8 && HAS_CAP (cont->caps, CAP_UTF8))
1693 new = "ICQ 2002 (?)";
1694 else if (dc->version == 8 && HAS_CAP (cont->caps, CAP_IS_2001))
1695 new = "ICQ 2001 (?)";
1696 else if (dc->version == 7)
1697 new = "ICQ2go (?)";
1698 else if (HAS_CAP (cont->caps, CAP_AIM_CHAT))
1699 new = "AIM(?)";
1700 else if (!dc->version && HAS_CAP (cont->caps, CAP_UTF8))
1701 new = "ICQ 2002 (?)";
1702 else if (!dc->version && HAS_CAP (cont->caps, CAP_IS_2001))
1703 new = "ICQ 2001 (?)";
1704 else if (HAS_CAP (cont->caps, CAP_AIM_CHAT))
1705 new = "AIM(?)";
1706 else if (!dc->version && !HAS_CAP (cont->caps, CAP_RTFMSGS))
1707 new = "ICQ 2000 (?)";
1708 else if (dc->version == 8)
1709 new = "ICQ 2001 (?)";
1710 else if (dc->version == 9)
1711 new = "ICQ Lite (""?""?)";
1712 else if (dc->version == 6)
1713 new = "ICQ99";
1714 else if (dc->version == 4)
1715 new = "ICQ98";
1716 else
1717 new = "??";
1718
1719 if (new)
1720 {
1721 if (new != buf)
1722 strcpy (buf, new);
1723 if (cont->v4)
1724 snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf),
1725 " %d.%d.%d.%d", cont->v1, cont->v2, cont->v3, cont->v4);
1726 else if (cont->v3)
1727 snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf),
1728 " %d.%d.%d", cont->v1, cont->v2, cont->v3);
1729 else if (cont->v1 || cont->v2)
1730 snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf),
1731 " %d.%d", cont->v1, cont->v2);
1732 if (tail)
1733 snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf),
1734 "%s", tail);
1735 }
1736 else
1737 buf[0] = '\0';
1738 s_repl (&cont->version, strlen (buf) ? buf : NULL);
1739 }
1740
ContactStatus(const char ** args,status_t * stat)1741 BOOL ContactStatus (const char **args, status_t *stat)
1742 {
1743 if (s_parsekey (args, "inv")) *stat = ims_inv;
1744 else if (s_parsekey (args, "inv online")) *stat = ims_inv;
1745 else if (s_parsekey (args, "inv ffc")) *stat = ims_inv_ffc;
1746 else if (s_parsekey (args, "inv away")) *stat = ims_inv_away;
1747 else if (s_parsekey (args, "inv na")) *stat = ims_inv_na;
1748 else if (s_parsekey (args, "inv occ")) *stat = ims_inv_occ;
1749 else if (s_parsekey (args, "inv dnd")) *stat = ims_inv_dnd;
1750 else if (s_parsekey (args, "online")) *stat = ims_online;
1751 else if (s_parsekey (args, "ffc")) *stat = ims_ffc;
1752 else if (s_parsekey (args, "away")) *stat = ims_away;
1753 else if (s_parsekey (args, "na")) *stat = ims_na;
1754 else if (s_parsekey (args, "occ")) *stat = ims_occ;
1755 else if (s_parsekey (args, "dnd")) *stat = ims_dnd;
1756 else if (s_parsekey (args, "offline")) *stat = ims_offline;
1757 else if (s_parsekey (args, "online")) *stat = ims_online;
1758 else
1759 return FALSE;
1760 return TRUE;
1761 }
1762
ContactStatusStr(status_t status)1763 const char *ContactStatusStr (status_t status)
1764 {
1765 switch (status)
1766 {
1767 case ims_offline: return "offline";
1768 case ims_online: return "online";
1769 case ims_ffc: return "ffc";
1770 case ims_away: return "away";
1771 case ims_na: return "na";
1772 case ims_occ: return "occ";
1773 case ims_dnd: return "dnd";
1774 case ims_inv: return "inv online";
1775 case ims_inv_ffc: return "inv ffc";
1776 case ims_inv_away: return "inv away";
1777 case ims_inv_na: return "inv na";
1778 case ims_inv_occ: return "inv occ";
1779 case ims_inv_dnd: return "inv dnd";
1780 }
1781 assert (0);
1782 }
1783