1 /*
2  * Contact
3  *
4  * Copyright (C) 2001 Barnaby Gray <barnaby@beedesign.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  *
20  */
21 
22 #include "Contact.h"
23 
24 #include "sstream_fix.h"
25 #include <time.h>
26 #include <ctype.h>
27 
28 #include "userinfoconstants.h"
29 #include "Capabilities.h"
30 
31 #include "events.h"
32 
33 using std::string;
34 using std::ostringstream;
35 using std::istringstream;
36 
37 namespace ICQ2000 {
38 
39   /**
40    *  Do not use this constructor outside the library.
41    *  It constructs a contact that is virtual, but doesn't have
42    *  an imaginary UIN allocated to it.
43    */
Contact()44   Contact::Contact()
45     : count(0), m_virtualcontact(true), m_uin(nextImaginaryUIN()), m_status(STATUS_OFFLINE),
46       m_invisible(false), m_seqnum(0xffff)
47   {
48     Init();
49   }
50 
Contact(unsigned int uin)51   Contact::Contact(unsigned int uin)
52     : count(0), m_virtualcontact(false), m_uin(uin), m_status(STATUS_OFFLINE),
53       m_invisible(false), m_capabilities( new Capabilities() ), m_seqnum(0xffff)
54   {
55     m_main_home_info.alias = UINtoString(m_uin);
56     Init();
57   }
58 
Contact(const string & a)59   Contact::Contact(const string& a)
60     : count(0), m_virtualcontact(true), m_uin(nextImaginaryUIN()),
61       m_status(STATUS_OFFLINE), m_invisible(false), m_capabilities( new Capabilities() ),
62       m_seqnum(0xffff)
63   {
64     m_main_home_info.alias = a;
65     Init();
66   }
67 
~Contact()68   Contact::~Contact()
69   {
70     delete m_capabilities;
71   }
72 
Init()73   void Contact::Init()
74   {
75     m_tcp_version = 0;
76     m_dc_cookie = 0;
77     m_ext_ip = 0;
78     m_lan_ip = 0;
79     m_ext_port = 0;
80     m_lan_port = 0;
81     m_direct = true;
82     m_bday = false;
83     m_signon_time = 0;
84     m_last_online_time = 0;
85     m_last_status_change_time = 0;
86     m_last_message_time = 0;
87     m_last_away_msg_check_time = 0;
88     m_group_id = 0;
89     m_tag_id = 0;
90     m_server_based = false;
91     m_authreq = false;
92     m_authawait = false;
93   }
94 
getUIN() const95   unsigned int Contact::getUIN() const { return m_uin; }
96 
setUIN(unsigned int uin)97   void Contact::setUIN(unsigned int uin) {
98     m_uin = uin;
99     m_virtualcontact = false;
100     userinfo_change_emit();
101   }
102 
getStringUIN() const103   string Contact::getStringUIN() const { return UINtoString(m_uin); }
104 
getAlias() const105   string Contact::getAlias() const { return m_main_home_info.alias; }
106 
getNameAlias() const107   string Contact::getNameAlias() const
108   {
109     string ret = getAlias();
110     if (ret.empty()) {
111       ret = getFirstName();
112       if (!ret.empty() && !getLastName().empty()) ret += " ";
113       ret += getLastName();
114     }
115     if (ret.empty()) {
116       if (isICQContact()) ret = getStringUIN();
117       else ret = getMobileNo();
118     }
119 
120     return ret;
121   }
122 
getStatus() const123   Status Contact::getStatus() const { return m_status; }
124 
getMobileNo() const125   string Contact::getMobileNo() const { return m_main_home_info.getMobileNo(); }
126 
getNormalisedMobileNo() const127   string Contact::getNormalisedMobileNo() const { return m_main_home_info.getNormalisedMobileNo(); }
128 
getFirstName() const129   string Contact::getFirstName() const { return m_main_home_info.firstname; }
130 
getLastName() const131   string Contact::getLastName() const { return m_main_home_info.lastname; }
132 
getEmail() const133   string Contact::getEmail() const { return m_main_home_info.email; }
134 
getExtIP() const135   unsigned int Contact::getExtIP() const { return m_ext_ip; }
136 
getLanIP() const137   unsigned int Contact::getLanIP() const { return m_lan_ip; }
138 
getExtPort() const139   unsigned short Contact::getExtPort() const { return m_ext_port; }
140 
getLanPort() const141   unsigned short Contact::getLanPort() const { return m_lan_port; }
142 
getTCPVersion() const143   unsigned char Contact::getTCPVersion() const { return m_tcp_version; }
144 
getDCCookie() const145   unsigned int Contact::getDCCookie() const { return m_dc_cookie; }
146 
getDirect() const147   bool Contact::getDirect() const { return (m_direct && m_status != STATUS_OFFLINE); }
148 
setDirect(bool b)149   void Contact::setDirect(bool b) {
150     m_direct = b;
151     userinfo_change_emit(true);
152   }
153 
get_accept_adv_msgs() const154   bool Contact::get_accept_adv_msgs() const {
155     return (m_status != STATUS_OFFLINE && m_capabilities->get_accept_adv_msgs());
156   }
157 
get_capabilities() const158   Capabilities Contact::get_capabilities() const { return * m_capabilities; }
159 
get_signon_time() const160   unsigned int Contact::get_signon_time() const { return m_signon_time; }
161 
get_last_online_time() const162   unsigned int Contact::get_last_online_time() const
163   {
164     if (m_status == STATUS_ONLINE) {
165       return time(NULL);
166     } else {
167       return m_last_online_time;
168     }
169   }
170 
get_last_status_change_time() const171   unsigned int Contact::get_last_status_change_time() const { return m_last_status_change_time; }
172 
get_last_message_time() const173   unsigned int Contact::get_last_message_time() const { return m_last_message_time; }
174 
get_last_away_msg_check_time() const175   unsigned int Contact::get_last_away_msg_check_time() const { return m_last_away_msg_check_time; }
176 
isInvisible() const177   bool Contact::isInvisible() const { return m_invisible; }
178 
getAuthReq() const179   bool Contact::getAuthReq() const { return m_authreq; }
180 
getAuthAwait() const181   bool Contact::getAuthAwait() const { return m_authawait; }
182 
getServerBased() const183   bool Contact::getServerBased() const { return m_server_based; }
184 
setMobileNo(const string & mn)185   void Contact::setMobileNo(const string& mn) {
186     m_main_home_info.setMobileNo(mn);
187     userinfo_change_emit();
188   }
189 
setAlias(const string & al)190   void Contact::setAlias(const string& al) {
191     m_main_home_info.alias = al;
192     userinfo_change_emit();
193   }
194 
setFirstName(const string & fn)195   void Contact::setFirstName(const string& fn) {
196     m_main_home_info.firstname = fn;
197     userinfo_change_emit();
198   }
199 
setLastName(const string & ln)200   void Contact::setLastName(const string& ln) {
201     m_main_home_info.lastname = ln;
202     userinfo_change_emit();
203   }
204 
setEmail(const string & em)205   void Contact::setEmail(const string& em) {
206     m_main_home_info.email = em;
207     userinfo_change_emit();
208   }
209 
setStatus(Status st)210   void Contact::setStatus(Status st) {
211     setStatus(st, m_invisible);
212   }
213 
setStatus(Status st,bool i)214   void Contact::setStatus(Status st, bool i) {
215     if (m_status == st && m_invisible == i) return;
216 
217     StatusChangeEvent sev(this, st, m_status);
218 
219     m_status = st;
220     m_invisible = i;
221 
222     m_last_status_change_time = time(NULL);
223 
224     // clear dynamic fields on going OFFLINE
225     if (m_status == STATUS_OFFLINE)
226     {
227       m_ext_ip = 0;
228       m_lan_ip = 0;
229       m_ext_port = 0;
230       m_lan_port = 0;
231       m_tcp_version = 0;
232       m_capabilities->clear();
233       m_last_online_time = time(NULL);
234     }
235 
236     status_change_signal.emit( &sev );
237   }
238 
userinfo_change_emit()239   void Contact::userinfo_change_emit()
240   {
241     userinfo_change_emit(false);
242   }
243 
userinfo_change_emit(bool is_transient_detail)244   void Contact::userinfo_change_emit(bool is_transient_detail)
245   {
246     UserInfoChangeEvent ev(this, is_transient_detail);
247     userinfo_change_signal.emit(&ev);
248   }
249 
setInvisible(bool inv)250   void Contact::setInvisible(bool inv) {
251     setStatus(m_status, inv);
252   }
253 
setAuthReq(bool b)254   void Contact::setAuthReq(bool b) {
255     m_authreq = b;
256     userinfo_change_emit(true);
257   }
258 
setAuthAwait(bool b)259   void Contact::setAuthAwait(bool b) {
260     m_authawait = b;
261     userinfo_change_emit(true);
262   }
263 
setServerBased(bool b)264   void Contact::setServerBased(bool b) {
265     m_server_based = b;
266     userinfo_change_emit(true);
267   }
268 
isICQContact() const269   bool Contact::isICQContact() const { return !m_virtualcontact; }
270 
isVirtualContact() const271   bool Contact::isVirtualContact() const { return m_virtualcontact; }
272 
isSMSable() const273   bool Contact::isSMSable() const
274   {
275     // ' SMS' suffix might be better way, but isn't reliable really
276     return !m_main_home_info.getNormalisedMobileNo().empty();
277   }
278 
setExtIP(unsigned int ip)279   void Contact::setExtIP(unsigned int ip) {
280     m_ext_ip = ip;
281     userinfo_change_emit(true);
282   }
283 
setLanIP(unsigned int ip)284   void Contact::setLanIP(unsigned int ip) {
285     m_lan_ip = ip;
286     userinfo_change_emit(true);
287   }
288 
setExtPort(unsigned short port)289   void Contact::setExtPort(unsigned short port) {
290     m_ext_port = port;
291     userinfo_change_emit(true);
292   }
293 
setLanPort(unsigned short port)294   void Contact::setLanPort(unsigned short port) {
295     m_lan_port = port;
296     userinfo_change_emit(true);
297   }
298 
setTCPVersion(unsigned char v)299   void Contact::setTCPVersion(unsigned char v) {
300     m_tcp_version = v;
301     userinfo_change_emit(true);
302   }
303 
setDCCookie(unsigned int cookie)304   void Contact::setDCCookie(unsigned int cookie) {
305     m_dc_cookie = cookie;
306     userinfo_change_emit(true);
307   }
308 
setServerSideInfo(unsigned short group_id,unsigned short tag_id)309   void Contact::setServerSideInfo(unsigned short group_id, unsigned short tag_id)
310   {
311     m_group_id = group_id;
312     m_tag_id = tag_id;
313     userinfo_change_emit(true);
314   }
315 
getServerSideGroupID() const316   unsigned short Contact::getServerSideGroupID() const {
317     return m_group_id;
318   }
319 
getServerSideID() const320   unsigned short Contact::getServerSideID() const {
321     unsigned short r = m_tag_id;
322     if(!r) {
323       r = m_uin;
324     if(r > 50000) {
325       r -= r-1000;
326       r += rand()%1000;
327     }
328     }
329 
330     return r;
331   }
332 
set_capabilities(const Capabilities & c)333   void Contact::set_capabilities(const Capabilities& c)
334   {
335     (*m_capabilities) = c;
336   }
337 
set_signon_time(unsigned int t)338   void Contact::set_signon_time(unsigned int t)
339   {
340     m_signon_time = t;
341     userinfo_change_emit(true);
342   }
343 
set_last_online_time(unsigned int t)344   void Contact::set_last_online_time(unsigned int t)
345   {
346     m_last_online_time = t;
347     userinfo_change_emit(true);
348   }
349 
set_last_status_change_time(unsigned int t)350   void Contact::set_last_status_change_time(unsigned int t)
351   {
352     m_last_status_change_time = t;
353     userinfo_change_emit(true);
354   }
355 
set_last_message_time(unsigned int t)356   void Contact::set_last_message_time(unsigned int t)
357   {
358     m_last_message_time = t;
359     userinfo_change_emit(true);
360   }
361 
set_last_away_msg_check_time(unsigned int t)362   void Contact::set_last_away_msg_check_time(unsigned int t)
363   {
364     m_last_away_msg_check_time = t;
365     userinfo_change_emit(true);
366   }
367 
setMainHomeInfo(const MainHomeInfo & s)368   void Contact::setMainHomeInfo(const MainHomeInfo& s) {
369     m_main_home_info = s;
370     userinfo_change_emit();
371   }
372 
setHomepageInfo(const HomepageInfo & s)373   void Contact::setHomepageInfo(const HomepageInfo& s) {
374     m_homepage_info = s;
375     userinfo_change_emit();
376   }
377 
setEmailInfo(const EmailInfo & s)378   void Contact::setEmailInfo(const EmailInfo& s) {
379     m_email_info = s;
380     userinfo_change_emit();
381   }
382 
setWorkInfo(const WorkInfo & s)383   void Contact::setWorkInfo(const WorkInfo& s) {
384     m_work_info = s;
385     userinfo_change_emit();
386   }
387 
setInterestInfo(const PersonalInterestInfo & s)388   void Contact::setInterestInfo(const PersonalInterestInfo& s) {
389     m_personal_interest_info = s;
390     userinfo_change_emit();
391   }
392 
setBackgroundInfo(const BackgroundInfo & b)393   void Contact::setBackgroundInfo(const BackgroundInfo& b) {
394     m_background_info = b;
395     userinfo_change_emit();
396   }
397 
setAboutInfo(const string & about)398   void Contact::setAboutInfo(const string& about) {
399     m_about = about;
400     userinfo_change_emit();
401   }
402 
getMainHomeInfo()403   Contact::MainHomeInfo& Contact::getMainHomeInfo() { return m_main_home_info; }
404 
getHomepageInfo()405   Contact::HomepageInfo& Contact::getHomepageInfo() { return m_homepage_info; }
406 
getWorkInfo()407   Contact::WorkInfo& Contact::getWorkInfo() { return m_work_info; }
408 
getPersonalInterestInfo()409   Contact::PersonalInterestInfo& Contact::getPersonalInterestInfo() { return m_personal_interest_info; }
410 
getBackgroundInfo()411   Contact::BackgroundInfo& Contact::getBackgroundInfo() { return m_background_info; }
412 
getEmailInfo()413   Contact::EmailInfo& Contact::getEmailInfo() { return m_email_info; }
414 
getAboutInfo() const415   const string& Contact::getAboutInfo() const { return m_about; }
416 
nextSeqNum()417   unsigned short Contact::nextSeqNum() {
418     return --m_seqnum;
419   }
420 
UINtoString(unsigned int uin)421   string Contact::UINtoString(unsigned int uin) {
422     ostringstream ostr;
423     ostr << uin;
424     return ostr.str();
425   }
426 
StringtoUIN(const string & s)427   unsigned int Contact::StringtoUIN(const string& s) {
428     istringstream istr(s);
429     unsigned int uin = 0;
430     istr >> uin;
431     return uin;
432   }
433 
434   unsigned int Contact::imag_uin = 0;
435 
nextImaginaryUIN()436   unsigned int Contact::nextImaginaryUIN() {
437     return (--imag_uin);
438   }
439 
440 
441 
MapStatusToICQStatus(Status st,bool inv)442   unsigned short Contact::MapStatusToICQStatus(Status st, bool inv) {
443     unsigned short s;
444 
445     switch(st) {
446     case STATUS_ONLINE:
447       s = 0x0000;
448       break;
449     case STATUS_AWAY:
450       s = 0x0001;
451       break;
452     case STATUS_NA:
453       s = 0x0005;
454       break;
455     case STATUS_OCCUPIED:
456       s = 0x0011;
457       break;
458     case STATUS_DND:
459       s = 0x0013;
460       break;
461     case STATUS_FREEFORCHAT:
462       s = 0x0020;
463       break;
464     default:
465       s = 0x0000;
466     }
467 
468     if (inv) s |= STATUS_FLAG_INVISIBLE;
469     return s;
470   }
471 
MapICQStatusToStatus(unsigned short st)472   Status Contact::MapICQStatusToStatus(unsigned short st) {
473     if (st & STATUS_FLAG_DND) return STATUS_DND;
474     else if (st & STATUS_FLAG_NA) return STATUS_NA;
475     else if (st & STATUS_FLAG_OCCUPIED) return STATUS_OCCUPIED;
476     else if (st & STATUS_FLAG_FREEFORCHAT) return STATUS_FREEFORCHAT;
477     else if (st & STATUS_FLAG_AWAY) return STATUS_AWAY;
478     else return STATUS_ONLINE;
479   }
480 
MapICQStatusToInvisible(unsigned short st)481   bool Contact::MapICQStatusToInvisible(unsigned short st) {
482     return (st & STATUS_FLAG_INVISIBLE);
483   }
484 
485   // Extra Detailed info class implementations;
486 
MainHomeInfo()487   Contact::MainHomeInfo::MainHomeInfo()
488     : country(COUNTRY_UNKNOWN), timezone(TIMEZONE_UNKNOWN) { }
489 
getCountry() const490   Country Contact::MainHomeInfo::getCountry() const
491   {
492     return country;
493   }
494 
getMobileNo() const495   string Contact::MainHomeInfo::getMobileNo() const
496   {
497     return cellular;
498   }
499 
setMobileNo(const string & s)500   void Contact::MainHomeInfo::setMobileNo(const string& s)
501   {
502     cellular = s;
503     normaliseMobileNo();
504   }
505 
getNormalisedMobileNo() const506   string Contact::MainHomeInfo::getNormalisedMobileNo() const
507   {
508     return normalised_cellular;
509   }
510 
normaliseMobileNo()511   void Contact::MainHomeInfo::normaliseMobileNo()
512   {
513     normalised_cellular.erase();
514     string::iterator curr = cellular.begin();
515     while (curr != cellular.end()) {
516       if (isdigit(*curr)) normalised_cellular += *curr;
517       ++curr;
518     }
519   }
520 
HomepageInfo()521   Contact::HomepageInfo::HomepageInfo()
522     : age(0), sex(SEX_UNKNOWN),
523       lang1(LANGUAGE_UNKNOWN), lang2(LANGUAGE_UNKNOWN), lang3(LANGUAGE_UNKNOWN),
524       birth_year(0), birth_month(0), birth_day(0)
525   { }
526 
527   /**
528    *  Get a string representation of the birthdate.
529    *
530    * @deprecated This function is not internationalized - it does not
531    * return utf-8, and returns the hard-coded "Unspecified" also.
532    */
getBirthDate() const533   string Contact::HomepageInfo::getBirthDate() const
534   {
535     if (birth_day == 0 || birth_year == 0) return "Unspecified";
536 
537     struct tm birthdate;
538     birthdate.tm_sec = 0;
539     birthdate.tm_min = 0;
540     birthdate.tm_hour = 0;
541     birthdate.tm_mday = birth_day;
542     birthdate.tm_mon = birth_month-1;
543     birthdate.tm_year = birth_year - 1900;
544     birthdate.tm_isdst = 0;
545     mktime(&birthdate);
546     char bday[255];
547     strftime(bday, 255, "%B %e, %G", &birthdate);
548     return string(bday);
549   }
550 
getLanguage(int l) const551   Language Contact::HomepageInfo::getLanguage(int l) const
552   {
553     Language ret = LANGUAGE_UNKNOWN;
554 
555     switch(l)
556     {
557     case 1:
558       ret = lang1;
559       break;
560     case 2:
561       ret = lang2;
562       break;
563     case 3:
564       ret = lang3;
565       break;
566     }
567 
568     return ret;
569   }
570 
EmailInfo()571   Contact::EmailInfo::EmailInfo() { }
572 
addEmailAddress(const string & e)573   void Contact::EmailInfo::addEmailAddress(const string& e) {
574     emails.push_back(e);
575   }
576 
WorkInfo()577   Contact::WorkInfo::WorkInfo()
578     : country(0) { }
579 
BackgroundInfo()580   Contact::BackgroundInfo::BackgroundInfo() { }
581 
addSchool(unsigned short cat,const string & s)582   void Contact::BackgroundInfo::addSchool(unsigned short cat, const string& s) {
583     schools.push_back(School(cat, s));
584   }
585 
PersonalInterestInfo()586   Contact::PersonalInterestInfo::PersonalInterestInfo() { }
587 
addInterest(unsigned short cat,const string & s)588   void Contact::PersonalInterestInfo::addInterest(unsigned short cat, const string& s) {
589     interests.push_back(Interest(cat, s));
590   }
591 
getBirthday() const592   bool Contact::getBirthday() const { return m_bday; }
593 
setBirthday(bool b)594   void Contact::setBirthday(bool b) {
595     m_bday = b;
596     userinfo_change_emit(true);
597   }
598 }
599