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