/*************************************************************************** contacts.cpp - description ------------------- begin : Sun Mar 10 2002 copyright : (C) 2002 by Vladimir Shutoff email : vovan@shutoff.ru ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include "buffer.h" #include "event.h" #include "log.h" #include "misc.h" #include "unquot.h" #include "contacts.h" namespace SIM { using namespace std; typedef map PACKET_MAP; class ContactListPrivate { public: ContactListPrivate(); ~ContactListPrivate(); void clear(bool bClearAll); unsigned registerUserData(const QString &name, const DataDef *def); void unregisterUserData(unsigned id); void flush(Contact *c, Group *g, const QCString §ion, Buffer *cfg); void flush(Contact *c, Group *g); UserData userData; list userDataDef; Contact *owner; list contacts; vector groups; vector clients; list protocols; PACKET_MAP packets; bool bNoRemove; }; #ifdef __OS2__ char *getDefEncoding() { // default charset is win1251 if cp866 used in OS/2 COUNTRYCODE ccode = {0}; // Country code info (0 = current country) COUNTRYINFO cinfo = {0}; // Buffer for country-specific information ULONG ilen = 0; if ( DosQueryCtryInfo(sizeof(cinfo), &ccode, &cinfo, &ilen) == 0 ) { if ( cinfo.codepage == 866 ) { return "CP 1251"; } } return NULL; } #endif static DataDef contactData[] = { { "Group", DATA_ULONG, 1, 0 }, { "Name", DATA_UTF, 1, 0 }, { "Ignore", DATA_BOOL, 1, 0 }, { "LastActive", DATA_ULONG, 1, 0 }, { "EMails", DATA_UTF, 1, 0 }, { "Phones", DATA_UTF, 1, 0 }, { "PhoneStatus", DATA_ULONG, 1, 0 }, { "FirstName", DATA_UTF, 1, 0 }, { "LastName", DATA_UTF, 1, 0 }, { "Notes", DATA_UTF, 1, 0 }, { "Flags", DATA_ULONG, 1, 0 }, #ifdef __OS2__ { "Encoding", DATA_STRING, 1, getDefEncoding() }, #else { "Encoding", DATA_STRING, 1, 0 }, #endif { NULL, DATA_UNKNOWN, 0, 0 } }; Contact::Contact(unsigned long id, Buffer *cfg) { m_id = id; load_data(contactData, &data, cfg); } Contact::~Contact() { if (!getContacts()->p->bNoRemove){ EventContact e(this, EventContact::eDeleted); e.process(); } free_data(contactData, &data); list &contacts = getContacts()->p->contacts; for (list::iterator it = contacts.begin(); it != contacts.end(); ++it){ if ((*it) == this){ contacts.erase(it); break; } } } const DataDef *Contact::dataDef() { return contactData; } void *Contact::getUserData(unsigned id, bool bCreate) { void *res = userData.getUserData(id, bCreate); if (res) return res; if (bCreate) return userData.getUserData(id, true); Group *group = getContacts()->group(getGroup()); if (group) return group->getUserData(id, false); return getContacts()->getUserData(id); } void Contact::setup() { QString str = getFirstName(); getToken(str, '/'); if (str != "-") setFirstName(QString::null); str = getLastName(); getToken(str, '/'); if (str != '-') setLastName(QString::null); QString res; str = getEMails(); while (!str.isEmpty()){ QString item = getToken(str, ';', false); QString value = getToken(item, '/', false); if (item != "-") continue; if (!res.isEmpty()) res += ';'; res += value; res += "/-"; } setEMails(res); str = getPhones(); while (!str.isEmpty()){ QString item = getToken(str, ';', false); QString value = getToken(item, '/', false); if (item != "-") continue; if (!res.isEmpty()) res += ';'; res += value; res += "/-"; } setPhones(res); ClientDataIterator it(clientData); void *data; while ((data = ++it) != NULL) it.client()->setupContact(this, data); } struct STR_ITEM { QString value; QStringList proto; }; typedef list STR_LIST; /* adds a value / client pair to str_list, *avoids* doubled entries */ static void add_str (STR_LIST & m, const QString & value, const char *client) { STR_LIST::iterator it; if (!client) client = "-"; /* check if value is already in str_list */ for (it = m.begin (); it != m.end (); ++it) { QString v = (*it).value; if (v == value) break; } /* already there */ if (it != m.end ()) { QStringList &proto = (*it).proto; QStringList::iterator itp; /* client == '-' --> ignore */ if (!proto.empty () && !strcmp (client, "-")) return; /* search if already in list */ itp = proto.find(client); if(itp == proto.end()) return; /* search if "-" somewhere in list --> delete it */ itp = proto.find("-"); if(itp != proto.end()) proto.erase (itp); /* add new client */ proto.push_back (client); } else { /* new entry */ STR_ITEM item; item.value = value; item.proto.push_back (client); m.push_back (item); } } static QString addStrings (const QString &old_value, const QString &values, const QString &client) { STR_LIST str_list; /* split old_value */ QString m = old_value; while (m.length ()) { /* str_item == one telephone entry */ QString str_item = getToken (m, ';', false); /* str == telephone entry without clients * str_item == clients */ QString str = getToken (str_item, '/'); /* now split clients */ while (str_item.length ()) { /* split by '/' - this was inserted as this function * doesn't worked correct */ QString proto = getToken (str_item, '/'); /* now split by ',' since this seems to be the correct separator */ while (proto.length ()) { QString proto2 = getToken (proto, ','); /* and add */ add_str (str_list, str, proto2); } } } /* split values */ m = values; while (m.length ()) { QString str_item = getToken (m, ';', false); QString str = getToken (str_item, '/'); while (str_item.length ()) { QString proto = getToken (str_item, '/'); while (proto.length ()) { QString proto2 = getToken (proto, ','); add_str (str_list, str, proto2); } } } /* add new client if new client was given */ if (!client.isEmpty()) { for (STR_LIST::iterator it = str_list.begin (); it != str_list.end (); ++it) { add_str (str_list, (*it).value, client); } } /* now build new string */ QString res; for (STR_LIST::iterator it = str_list.begin (); it != str_list.end (); ++it) { QStringList &proto = (*it).proto; if (proto.size() == 0) continue; if (res.length()) res += ';'; res += quoteChars ((*it).value, ";/"); res += '/'; QString proto_str; for (QStringList::iterator itp = proto.begin (); itp != proto.end (); ++itp) { if (proto_str.length ()) proto_str += ','; proto_str += quoteChars ((*itp), ",;/"); } res += proto_str; } return res; } bool Contact::setEMails(const QString &mail, const QString &client) { return setEMails(addStrings(getEMails(), mail, client)); } bool Contact::setPhones(const QString &phone, const QString &client) { return setPhones(addStrings(getPhones(), phone, client)); } static QString packString(const QString &value, const QString &client) { QString res = quoteChars(value, "/"); res += '/'; if (!client.isEmpty()){ res += client; }else{ res += '-'; } return res; } static QString addString(const QString &oldValue, const QString &newValue, const QString &client) { QString res; if (oldValue.length() == 0){ if (newValue.length() == 0) return res; return packString(newValue, client); } QString value = oldValue; getToken(value, '/'); if (value == client || client.isEmpty()) return packString(newValue, client); return oldValue; } bool Contact::setFirstName(const QString &name, const QString &client) { return setFirstName(addString(getFirstName(), name, client)); } bool Contact::setLastName(const QString &name, const QString &client) { return setLastName(addString(getLastName(), name, client)); } static char tipDiv[] = "
__________
"; QString Contact::tipText() { QString tip; tip += ""; tip += quoteString(getName()); tip += ""; QString firstName = getFirstName(); firstName = getToken(firstName, '/'); firstName = quoteString(firstName); QString lastName = getLastName(); lastName = getToken(lastName, '/'); lastName = quoteString(lastName); if (firstName.length() || lastName.length()){ tip += "
"; if (firstName.length()){ tip += firstName; tip += ' '; } tip += lastName; } bool bFirst = true; QString mails = getEMails(); while (mails.length()){ QString mail = getToken(mails, ';', false); mail = getToken(mail, '/'); if (bFirst){ tip += "
"; bFirst = false; }else{ tip += ", "; } tip += quoteString(mail); } void *data; ClientDataIterator it(clientData); while ((data = ++it) != NULL){ Client *client = clientData.activeClient(data, it.client()); if (client == NULL) continue; QString str = client->contactTip(data); if (str.length()){ tip += tipDiv; tip += str; } } bFirst = true; QString phones = getPhones(); while (phones.length()){ if (bFirst){ tip += tipDiv; bFirst = false; }else{ tip += "
"; } QString phone_item = getToken(phones, ';', false); phone_item = getToken(phone_item, '/', false); QString phone = getToken(phone_item, ','); getToken(phone_item, ','); unsigned phone_type = phone_item.toULong(); QString icon; switch (phone_type){ case PHONE: icon = "phone"; break; case FAX: icon = "fax"; break; case CELLULAR: icon = "cell"; break; case PAGER: icon = "pager"; break; } if (icon.length()){ tip += ""; } tip += ' '; tip += quoteString(phone); } return tip; } struct sortClientData { void *data; Client *client; unsigned nClient; }; static bool cmp_sd(sortClientData p1, sortClientData p2) { if (((clientData*)(p1.data))->LastSend.asULong() > ((clientData*)(p2.data))->LastSend.asULong()) return true; if (((clientData*)(p1.data))->LastSend.asULong() < ((clientData*)(p2.data))->LastSend.asULong()) return false; return p1.nClient < p2.nClient; } unsigned long Contact::contactInfo(unsigned &style, QString &statusIcon, QString *icons) { style = 0; statusIcon = QString::null; if (icons) *icons = QString::null; unsigned long status = STATUS_UNKNOWN; void *data; ClientDataIterator it(clientData, NULL); vector d; while ((data = ++it) != NULL){ sortClientData sd; sd.data = data; sd.client = it.client(); sd.nClient = 0; for (unsigned i = 0; i < getContacts()->nClients(); i++){ if (getContacts()->getClient(i) == sd.client){ sd.nClient = i; break; } } d.push_back(sd); } sort(d.begin(), d.end(), cmp_sd); for (unsigned i = 0; i < d.size(); i++){ void *data = d[i].data; Client *client = clientData.activeClient(data, d[i].client); if (client == NULL) continue; client->contactInfo(data, status, style, statusIcon, icons); } QString phones = getPhones(); bool bCell = false; bool bPager = false; while (phones.length()){ QString phoneItem = getToken(phones, ';', false); phoneItem = getToken(phoneItem, '/', false); getToken(phoneItem, ','); getToken(phoneItem, ','); unsigned n = phoneItem.toULong(); if (n == CELLULAR) bCell = true; if (n == PAGER) bPager = true; } if (bCell){ if (statusIcon){ if (icons){ if (icons->length()) *icons += ','; *icons += "cell"; } }else{ statusIcon = "cell"; } } if (bPager){ if (statusIcon){ if (icons){ if (icons->length()) *icons += ','; *icons += "pager"; } }else{ statusIcon = "pager"; } } if (status == STATUS_UNKNOWN){ if (statusIcon == NULL){ QString mails = getEMails(); if (!mails.isEmpty()) statusIcon = "mail_generic"; } if (statusIcon == NULL) statusIcon = "nonim"; return STATUS_UNKNOWN; } if (statusIcon == NULL) statusIcon = "empty"; return status; } QString Client::resources(void*) { return QString::null; } QString Client::contactName(void*) { return i18n(protocol()->description()->text); } CommandDef *Client::infoWindows(Contact*, void*) { return NULL; } QWidget *Client::infoWindow(QWidget*, Contact*, void*, unsigned) { return NULL; } CommandDef *Client::configWindows() { return NULL; } QWidget *Client::configWindow(QWidget*, unsigned) { return NULL; } QString Client::contactTip(void*) { return QString::null; } void Client::updateInfo(Contact *contact, void *data) { if (data){ EventContact(contact, EventContact::eFetchInfoFailed).process(); }else{ EventClientChanged(this).process(); } } static DataDef groupData[] = { { "Name", DATA_UTF, 1, 0 }, { NULL, DATA_UNKNOWN, 0, 0 } }; Group::Group(unsigned long id, Buffer *cfg) { m_id = id; load_data(groupData, &data, cfg); } Group::~Group() { if (!getContacts()->p->bNoRemove){ Contact *contact; ContactList::ContactIterator itc; while ((contact = ++itc) != NULL){ if (contact->getGroup() != id()) continue; contact->setGroup(0); EventContact e(contact, EventContact::eChanged); e.process(); } EventGroup e(this, EventGroup::eDeleted); e.process(); } free_data(groupData, &data); vector &groups = getContacts()->p->groups; for (vector::iterator it = groups.begin(); it != groups.end(); ++it){ if ((*it) == this){ groups.erase(it); break; } } } void *Group::getUserData(unsigned id, bool bCreate) { void *res = userData.getUserData(id, bCreate); if (res) return res; if (bCreate) return userData.getUserData(id, true); return getContacts()->getUserData(id); } ContactListPrivate::ContactListPrivate() { Group *notInList = new Group(0); owner = new Contact(0); groups.push_back(notInList); bNoRemove = false; } ContactListPrivate::~ContactListPrivate() { clear(true); delete owner; } void ContactListPrivate::clear(bool bClearAll) { bNoRemove = true; for (list::iterator it_c = contacts.begin(); it_c != contacts.end();){ Contact *contact = *it_c; delete contact; it_c = contacts.begin(); } for (vector::iterator it_g = groups.begin(); it_g != groups.end();){ Group *group = *it_g; if (!bClearAll && (group->id() == 0)){ ++it_g; continue; } delete group; it_g = groups.begin(); } bNoRemove = false; } unsigned ContactListPrivate::registerUserData(const QString &name, const DataDef *def) { unsigned id = 0x1000; // must be unique... for (list::iterator it = userDataDef.begin(); it != userDataDef.end(); ++it){ if (id <= (*it).id) id = (*it).id + 1; } UserDataDef d; d.id = id; d.name = name; d.def = def; userDataDef.push_back(d); return id; } void ContactListPrivate::unregisterUserData(unsigned id) { for (list::iterator it_c = contacts.begin(); it_c != contacts.end(); ++it_c){ (*it_c)->userData.freeUserData(id); } for (vector::iterator it_g = groups.begin(); it_g != groups.end(); ++it_g) (*it_g)->userData.freeUserData(id); userData.freeUserData(id); for (list::iterator it = userDataDef.begin(); it != userDataDef.end(); ++it){ if (id != (*it).id) continue; userDataDef.erase(it); break; } } ContactList::ContactList() { p = new ContactListPrivate; } ContactList::~ContactList() { delete p; } unsigned ContactList::registerUserData(const QString &name, const DataDef *def) { return p->registerUserData(name, def); } void ContactList::unregisterUserData(unsigned id) { p->unregisterUserData(id); } Contact *ContactList::owner() { return p->owner; } Contact *ContactList::contact(unsigned long id, bool isNew) { list::iterator it; for (it = p->contacts.begin(); it != p->contacts.end(); ++it){ if ((*it)->id() == id) return (*it); } if (!isNew) return NULL; if (id == 0){ id = 1; for (it = p->contacts.begin(); it != p->contacts.end(); ++it){ if (id <= (*it)->id()) id = (*it)->id() + 1; } } Contact *res = new Contact(id); p->contacts.push_back(res); EventContact e(res, EventContact::eCreated); e.process(); return res; } void ContactList::addContact(Contact *contact) { if (contact->id()) return; unsigned long id = 1; list::iterator it; for (it = p->contacts.begin(); it != p->contacts.end(); ++it){ if (id <= (*it)->id()) id = (*it)->id() + 1; } contact->m_id = id; p->contacts.push_back(contact); EventContact e(contact, EventContact::eCreated); e.process(); } Group *ContactList::group(unsigned long id, bool isNew) { vector::iterator it; if (id || !isNew){ for (it = p->groups.begin(); it != p->groups.end(); ++it){ if ((*it)->id() == id) return (*it); } } if (!isNew) return NULL; if (id == 0){ for (it = p->groups.begin(); it != p->groups.end(); ++it){ if (id <= (*it)->id()) id = (*it)->id() + 1; } } Group *res = new Group(id); p->groups.push_back(res); EventGroup e(res, EventGroup::eAdded); e.process(); return res; } int ContactList::groupIndex(unsigned long id) { for (unsigned i = 0; i < p->groups.size(); i++){ if (p->groups[i]->id() == id) return i; } return -1; } int ContactList::groupCount() { return p->groups.size(); } bool ContactList::moveGroup(unsigned long id, bool bUp) { if (id == 0) return false; for (unsigned i = 1; i < p->groups.size(); i++){ if (p->groups[i]->id() == id){ if (!bUp && (i == p->groups.size() - 1)) return false; if (bUp) i--; if (i == 0) return false; Group *g = p->groups[i]; p->groups[i] = p->groups[i+1]; p->groups[i+1] = g; EventGroup e1(p->groups[i], EventGroup::eChanged); EventGroup e2(p->groups[i+1], EventGroup::eChanged); e1.process(); e2.process(); return true; } } return false; } class GroupIteratorPrivate { public: GroupIteratorPrivate(); Group *operator++(); vector::iterator it; }; GroupIteratorPrivate::GroupIteratorPrivate() { it = getContacts()->p->groups.begin(); } Group *GroupIteratorPrivate::operator++() { if (it != getContacts()->p->groups.end()){ Group *res = (*it); ++it; return res; } return NULL; } ContactList::GroupIterator::GroupIterator() { p = new GroupIteratorPrivate; } void ContactList::GroupIterator::reset() { p->it = getContacts()->p->groups.begin(); } ContactList::GroupIterator::~GroupIterator() { delete p; } Group *ContactList::GroupIterator::operator ++() { return ++(*p); } class ContactIteratorPrivate { public: ContactIteratorPrivate(); Contact *operator++(); list::iterator it; }; ContactIteratorPrivate::ContactIteratorPrivate() { it = getContacts()->p->contacts.begin(); } Contact *ContactIteratorPrivate::operator++() { if (it != getContacts()->p->contacts.end()){ Contact *res = (*it); ++it; return res; } return NULL; } ContactList::ContactIterator::ContactIterator() { p = new ContactIteratorPrivate; } void ContactList::ContactIterator::reset() { p->it = getContacts()->p->contacts.begin(); } ContactList::ContactIterator::~ContactIterator() { delete p; } Contact *ContactList::ContactIterator::operator ++() { return ++(*p); } class ProtocolIteratorPrivate { public: ProtocolIteratorPrivate(); Protocol *operator++(); list::iterator it; }; ProtocolIteratorPrivate::ProtocolIteratorPrivate() { it = getContacts()->p->protocols.begin(); } Protocol *ProtocolIteratorPrivate::operator++() { if (it != getContacts()->p->protocols.end()){ Protocol *res = (*it); ++it; return res; } return NULL; } ContactList::ProtocolIterator::ProtocolIterator() { p = new ProtocolIteratorPrivate; } void ContactList::ProtocolIterator::reset() { p->it = getContacts()->p->protocols.begin(); } ContactList::ProtocolIterator::~ProtocolIterator() { delete p; } Protocol *ContactList::ProtocolIterator::operator ++() { return ++(*p); } Protocol::Protocol(Plugin *plugin) { m_plugin = plugin; ContactListPrivate *p = getContacts()->p; p->protocols.push_back(this); } Protocol::~Protocol() { ContactListPrivate *p = getContacts()->p; for (list::iterator it = p->protocols.begin(); it != p->protocols.end(); ++it){ if ((*it) != this) continue; p->protocols.erase(it); break; } } class PacketIteratorPrivate { public: PacketIteratorPrivate(); PacketType *operator++(); PACKET_MAP::iterator it; }; PacketIteratorPrivate::PacketIteratorPrivate() { it = getContacts()->p->packets.begin(); } PacketType *PacketIteratorPrivate::operator++() { if (it != getContacts()->p->packets.end()){ PacketType *res = (*it).second; ++it; return res; } return NULL; } ContactList::PacketIterator::PacketIterator() { p = new PacketIteratorPrivate; } void ContactList::PacketIterator::reset() { p->it = getContacts()->p->packets.begin(); } ContactList::PacketIterator::~PacketIterator() { delete p; } PacketType *ContactList::PacketIterator::operator ++() { return ++(*p); } PacketType::PacketType(unsigned id, const QString &name, bool bText) { m_id = id; m_name = name; m_bText = bText; } PacketType::~PacketType() { } void ContactList::addPacketType(unsigned id, const QString &name, bool bText) { PACKET_MAP::iterator it = p->packets.find(id); if (it != p->packets.end()) return; p->packets.insert(PACKET_MAP::value_type(id, new PacketType(id, name, bText))); } void ContactList::removePacketType(unsigned id) { PACKET_MAP::iterator it = p->packets.find(id); if (it != p->packets.end()){ delete (*it).second; p->packets.erase(it); } } PacketType *ContactList::getPacketType(unsigned id) { PACKET_MAP::iterator it = p->packets.find(id); if (it == p->packets.end()) return NULL; return (*it).second; } static DataDef _clientData[] = { { "ManualStatus", DATA_ULONG, 1, DATA(1) }, { "CommonStatus", DATA_BOOL, 1, DATA(1) }, { "Password", DATA_UTF, 1, 0 }, { "", DATA_BOOL, 1, DATA(1) }, // SavePassword { "", DATA_UTF, 1, 0 }, // PreviousPassword { "Invisible", DATA_BOOL, 1, 0 }, { "LastSend", DATA_STRLIST, 1, 0 }, { NULL, DATA_UNKNOWN, 0, 0 } }; Client::Client(Protocol *protocol, Buffer *cfg) { load_data(_clientData, &data, cfg); // now uncrypt password somehow QString pswd = getPassword(); if (pswd.length() && (pswd[0] == '$')) { pswd = pswd.mid(1); QString new_pswd; unsigned short temp = 0x4345; QString tmp; do { QString sub_str = getToken(pswd, '$'); temp ^= sub_str.toUShort(0,16); new_pswd += tmp.setUnicodeCodes(&temp,1); temp = sub_str.toUShort(0,16); } while (pswd.length()); setPassword(new_pswd); } m_protocol = protocol; m_status = STATUS_OFFLINE; m_state = Offline; } void Client::setStatus(unsigned status, bool bCommon) { setManualStatus(status); setCommonStatus(bCommon); EventClientChanged(this).process(); } Client::~Client() { freeData(); } void Client::freeData() { ContactListPrivate *p = getContacts()->p; for (vector::iterator it = p->clients.begin(); it != p->clients.end(); ++it){ if ((*it) != this) continue; p->clients.erase(it); if (!getContacts()->p->bNoRemove){ EventClientsChanged e; e.process(); } break; } Group *grp; ContactList::GroupIterator itg; while ((grp = ++itg) != NULL){ if (grp->clientData.size() == 0) continue; grp->clientData.freeClientData(this); if (!getContacts()->p->bNoRemove){ EventGroup e(grp, EventGroup::eChanged); e.process(); } } Contact *contact; ContactList::ContactIterator itc; list forRemove; while ((contact = ++itc) != NULL){ if (contact->clientData.size() == 0) continue; contact->clientData.freeClientData(this); if (contact->clientData.size()){ if (!getContacts()->p->bNoRemove){ contact->setup(); EventContact e(contact, EventContact::eChanged); e.process(); } continue; } forRemove.push_back(contact); } list::iterator itr; for (itr = forRemove.begin(); itr != forRemove.end(); ++itr){ Contact *contact = *itr; delete contact; } free_data(_clientData, &data); } QCString Client::getConfig() { QString real_pswd = getPassword(); QString pswd = getPassword(); // crypt password somehow if (pswd.length()) { QString new_passwd; unsigned short temp = 0x4345; for (int i = 0; i < (int)(pswd.length()); i++) { temp ^= (pswd[i].unicode()); new_passwd += '$'; new_passwd += QString::number(temp,16); } setPassword(new_passwd); } QString prev = getPreviousPassword(); if (!prev.isEmpty()) setPassword(prev); if (!getSavePassword()) setPassword(NULL); QCString res = save_data(_clientData, &data); setPassword(real_pswd); return res; } void Client::setClientInfo(void*) { } bool Client::compareData(void*, void*) { return false; } void Client::setState(State state, const QString &text, unsigned code) { m_state = state; EventClientChanged(this).process(); if (state == Error){ EventError::ClientErrorData d; d.client = this; d.err_str = text; d.code = code; d.args = QString::null; d.flags = EventError::ClientErrorData::E_ERROR; d.options = NULL; d.id = 0; for (unsigned i = 0; i < getContacts()->nClients(); i++){ if (getContacts()->getClient(i) == this){ d.id = i + 1; break; } } EventClientError e(d); e.process(); } } void Client::contactsLoaded() { } bool ContactList::moveClient(Client *client, bool bUp) { unsigned i; for (i = 0; i < p->clients.size(); i++) if (p->clients[i] == client) break; if (!bUp) i++; if ((i == 0) && (i >= p->clients.size())) return false; Client *c = p->clients[i]; p->clients[i] = p->clients[i-1]; p->clients[i-1] = c; EventClientsChanged e; e.process(); Contact *contact; ContactList::ContactIterator it; while ((contact = ++it) != NULL){ contact->clientData.sort(); EventContact e(contact, EventContact::eChanged); e.process(); } return true; } struct _ClientUserData { Client *client; Data *data; }; class ClientUserDataPrivate : public vector<_ClientUserData> { public: ClientUserDataPrivate(); ~ClientUserDataPrivate(); }; ClientUserDataPrivate::ClientUserDataPrivate() { } ClientUserDataPrivate::~ClientUserDataPrivate() { // why do I have to delete something here which is created somehwere else?? for (ClientUserDataPrivate::iterator it = begin(); it != end(); ++it){ _ClientUserData &d = *it; free_data(d.client->protocol()->userDataDef(), d.data); delete[] d.data; } } ClientUserData::ClientUserData() { p = new ClientUserDataPrivate; } ClientUserData::~ClientUserData() { delete p; } unsigned ClientUserData::size() { return p->size(); } QString ClientUserData::property(const char *name) { for (ClientUserDataPrivate::iterator it = p->begin(); it != p->end(); ++it){ _ClientUserData &d = *it; Data *user_data = (Data*)d.data; for (const DataDef *def = d.client->protocol()->userDataDef(); def->name; def++){ if (!strcmp(def->name, name)){ switch (def->type){ case DATA_STRING: case DATA_UTF: if (!user_data->str().isEmpty()) return user_data->str(); case DATA_ULONG: if (user_data->toULong() != (unsigned long)(def->def_value)) return QString::number(user_data->toULong()); case DATA_LONG: if (user_data->toLong() != (long)(def->def_value)) return QString::number(user_data->toLong()); default: break; } } user_data += def->n_values; } } return QString::null; } bool ClientUserData::have(void *data) { for (ClientUserDataPrivate::iterator it = p->begin(); it != p->end(); ++it){ if ((*it).data == data) return true; } return false; } Client *ClientUserData::activeClient(void *&data, Client *client) { ClientUserDataPrivate::iterator it; for (it = p->begin(); it != p->end(); ++it){ if (((*it).client == client) && ((*it).data == data)) break; if (((clientData*)((*it).data))->Sign.toULong() != ((clientData*)data)->Sign.toULong()) continue; if (client->compareData(data, (*it).data)) return NULL; } if (it == p->end()) return NULL; if (client->getState() == Client::Connected) return client; for (++it; it != p->end(); ++it){ if ((*it).client->getState() != Client::Connected) continue; if (((clientData*)((*it).data))->Sign.toULong() != ((clientData*)data)->Sign.toULong()) continue; if (client->compareData(data, (*it).data)){ data = (*it).data; return (*it).client; } } return client; } QCString ClientUserData::save() { QCString res; for (ClientUserDataPrivate::iterator it = p->begin(); it != p->end(); ++it){ _ClientUserData &d = *it; if (d.client->protocol()->description()->flags & PROTOCOL_TEMP_DATA) continue; QCString cfg = save_data(d.client->protocol()->userDataDef(), d.data); if (cfg.length()){ if (res.length()) res += '\n'; res += '['; res += d.client->name().utf8(); res += "]\n"; res += cfg; } } return res; } void ClientUserData::load(Client *client, Buffer *cfg) { for (ClientUserDataPrivate::iterator it = p->begin(); it != p->end(); ++it){ Client *c = (*it).client; if(c == client) return; } _ClientUserData data; data.client = client; const DataDef *def = client->protocol()->userDataDef(); size_t size = 0; for (const DataDef *d = def; d->name; ++d) size += d->n_values; data.data = new Data[size]; load_data(def, data.data, cfg); p->push_back(data); } void *ClientUserData::createData(Client *client) { _ClientUserData data; data.client = client; const DataDef *def = client->protocol()->userDataDef(); size_t size = 0; for (const DataDef *d = def; d->name; ++d) size += d->n_values; data.data = new Data[size]; load_data(def, data.data, NULL); p->push_back(data); return data.data; } void *ClientUserData::getData(Client *client) { for (ClientUserDataPrivate::iterator it = p->begin(); it != p->end(); ++it){ if ((*it).client == client) return (*it).data; } return NULL; } void ClientUserData::freeData(void *_data) { SIM::Data *data = (SIM::Data*)_data; for (ClientUserDataPrivate::iterator it = p->begin(); it != p->end(); ++it){ if ((*it).data == data){ free_data((*it).client->protocol()->userDataDef(), data); delete[] data; p->erase(it); return; } } } void ClientUserData::freeClientData(Client *client) { for (ClientUserDataPrivate::iterator it = p->begin(); it != p->end();){ if ((*it).client != client){ ++it; continue; } free_data((*it).client->protocol()->userDataDef(), (*it).data); delete[] (*it).data; p->erase(it); it = p->begin(); } } void ClientUserData::join(ClientUserData &data) { for (ClientUserDataPrivate::iterator it = data.p->begin(); it != data.p->end(); ++it) p->push_back(*it); data.p->clear(); sort(); } void ClientUserData::join(clientData *cData, ClientUserData &data) { for (ClientUserDataPrivate::iterator it = data.p->begin(); it != data.p->end(); ++it){ if ((*it).data == &(cData->Sign)){ p->push_back(*it); data.p->erase(it); break; } } sort(); } class ClientDataIteratorPrivate { public: ClientDataIteratorPrivate(ClientUserDataPrivate *p, Client *client); void *operator ++(); void reset(); Client *m_lastClient; protected: ClientUserDataPrivate *m_p; ClientUserDataPrivate::iterator m_it; Client *m_client; }; ClientDataIteratorPrivate::ClientDataIteratorPrivate(ClientUserDataPrivate *p, Client *client) { m_p = p; m_client = client; reset(); } void *ClientDataIteratorPrivate::operator ++() { for (; m_it != m_p->end(); ++m_it){ if ((m_client == NULL) || ((*m_it).client == m_client)){ void *res = (*m_it).data; m_lastClient = (*m_it).client; ++m_it; return res; } } return NULL; } void ClientDataIteratorPrivate::reset() { m_lastClient = NULL; m_it = m_p->begin(); } ClientDataIterator::ClientDataIterator(ClientUserData &data, Client *client) { p = new ClientDataIteratorPrivate(data.p, client); } ClientDataIterator::~ClientDataIterator() { delete p; } clientData *ClientDataIterator::operator ++() { return (clientData*)(++(*p)); } void ClientDataIterator::reset() { p->reset(); } Client *ClientDataIterator::client() { return p->m_lastClient; } static bool cmp_client_data(_ClientUserData p1, _ClientUserData p2) { for (unsigned i = 0; i < getContacts()->nClients(); i++){ Client *c = getContacts()->getClient(i); if (c == p1.client){ if (c != p2.client) return true; return p1.data < p2.data; } if (c == p2.client) return false; } return p1.data < p2.data; } void ClientUserData::sort() { std::sort(p->begin(), p->end(), cmp_client_data); } // _____________________________________________________________________________________ class UserDataIteratorPrivate : public list::iterator { public: UserDataIteratorPrivate(const list::iterator &it) : list::iterator(it) {} }; UserDataDef *ContactList::UserDataIterator::operator++() { if ((*p) == getContacts()->p->userDataDef.end()) return NULL; UserDataDef *res = &(**p); ++(*p); return res; } ContactList::UserDataIterator::UserDataIterator() { p = new UserDataIteratorPrivate(getContacts()->p->userDataDef.begin()); } ContactList::UserDataIterator::~UserDataIterator() { delete p; } // ______________________________________________________________________________________ typedef QMap UserDataMap; class UserDataPrivate { public: UserDataMap m_userData; }; UserData::UserData() { d = new UserDataPrivate; } UserData::~UserData() { UserDataMap::Iterator userDataIt; for(userDataIt = d->m_userData.begin(); userDataIt != d->m_userData.end(); ++userDataIt) { list &dataDef = getContacts()->p->userDataDef; for (list::iterator it = dataDef.begin(); it != dataDef.end(); ++it){ if ((*it).id != userDataIt.key()) continue; free_data((*it).def, *userDataIt); break; } delete[] userDataIt.data(); } delete d; } void *UserData::getUserData(unsigned id, bool bCreate) { UserDataMap::Iterator userDataIt = d->m_userData.find(id); if (userDataIt != d->m_userData.end()) return *userDataIt; if (!bCreate) return NULL; list &dataDef = getContacts()->p->userDataDef; list::iterator it; for (it= dataDef.begin(); it != dataDef.end(); ++it) if ((*it).id == id) break; if (it == dataDef.end()) return NULL; size_t size = 0; for (const DataDef *def = (*it).def; def->name; ++def) size += def->n_values; Data* data = new Data[size]; d->m_userData.insert(id, data); load_data((*it).def, data, NULL); return data; } void UserData::freeUserData(unsigned id) { UserDataMap::Iterator userDataIt = d->m_userData.find(id); if (userDataIt != d->m_userData.end()) { list &dataDef = getContacts()->p->userDataDef; for (list::iterator it = dataDef.begin(); it != dataDef.end(); ++it){ if ((*it).id != id) continue; free_data((*it).def, d->m_userData[id]); break; } delete[] userDataIt.data(); d->m_userData.erase(userDataIt); } } QCString UserData::save() { QCString res; UserDataMap::Iterator userDataIt; for(userDataIt = d->m_userData.begin(); userDataIt != d->m_userData.end(); ++userDataIt) { list &d = getContacts()->p->userDataDef; for (list::iterator it = d.begin(); it != d.end(); ++it){ if ((*it).id != userDataIt.key()) continue; QCString cfg = save_data((*it).def, userDataIt.data()); if (cfg.length()){ if (res.length()) res += '\n'; res += '['; res += (*it).name; res += "]\n"; res += cfg; } break; } } return res; } void UserData::load(unsigned long id, const DataDef *def, Buffer *cfg) { void *d = getUserData(id, true); if (d == NULL) return; free_data(def, d); load_data(def, d, cfg); } void ContactList::addClient(Client *client) { p->clients.push_back(client); EventClientsChanged e; e.process(); } static char CONTACTS_CONF[] = "contacts.conf"; static char CONTACT[] = "Contact="; static char GROUP[] = "Group="; static char OWNER[] = "Owner"; static char BACKUP_SUFFIX[] = "~"; void ContactList::save() { QString cfgName = user_file(CONTACTS_CONF); QFile f(cfgName + BACKUP_SUFFIX); // use backup file for this ... if (!f.open(IO_WriteOnly | IO_Truncate)){ log(L_ERROR, "Can't create %s", (const char*)f.name().local8Bit()); return; } QCString line = p->userData.save(); if (line.length()){ line += '\n'; f.writeBlock(line, line.length()); } line = save_data(contactData, &owner()->data); if (line.length()){ QCString cfg = "["; cfg += OWNER; cfg += "]\n"; line += '\n'; f.writeBlock(cfg, cfg.length()); f.writeBlock(line, line.length()); } for (vector::iterator it_g = p->groups.begin(); it_g != p->groups.end(); ++it_g){ Group *grp = *it_g; line = "["; line += GROUP; line += QString::number(grp->id()); line += "]\n"; f.writeBlock(line, line.length()); line = save_data(groupData, &grp->data); if (line.length()){ line += '\n'; f.writeBlock(line, line.length()); } else { /* Group has no name --> Not In List since the load_data seems to have problems with totally empty entries, this must be ...*/ f.writeBlock("Name=\"NIL\"\n", 11); } line = grp->userData.save(); if (line.length()){ line += '\n'; f.writeBlock(line, line.length()); } line = grp->clientData.save(); if (line.length()){ line += '\n'; f.writeBlock(line, line.length()); } } for (list::iterator it_c = p->contacts.begin(); it_c != p->contacts.end(); ++it_c){ Contact *contact = *it_c; if (contact->getFlags() & CONTACT_TEMPORARY) continue; line = "["; line += CONTACT; line += QString::number(contact->id()); line += "]\n"; f.writeBlock(line, line.length()); line = save_data(contactData, &contact->data); if (line.length()){ line += '\n'; f.writeBlock(line, line.length()); } line = contact->userData.save(); if (line.length()){ line += '\n'; f.writeBlock(line, line.length()); } line = contact->clientData.save(); if (line.length()){ line += '\n'; f.writeBlock(line, line.length()); } } f.flush(); // Make shure that file is fully written and we will not get "Disk Full" error on f.close const int status = f.status(); const QString errorMessage = f.errorString(); f.close(); if (status != IO_Ok) { log(L_ERROR, "IO error during writing to file %s : %s", (const char*)f.name().local8Bit(), (const char*)errorMessage.local8Bit()); return; } // rename to normal file QFileInfo fileInfo(f.name()); QString desiredFileName = fileInfo.fileName(); desiredFileName = desiredFileName.left(desiredFileName.length() - strlen(BACKUP_SUFFIX)); #if defined( WIN32 ) || defined( __OS2__ ) fileInfo.dir().remove(desiredFileName); #endif if (!fileInfo.dir().rename(fileInfo.fileName(), desiredFileName)) { log(L_ERROR, "Can't rename file %s to %s", (const char*)fileInfo.fileName().local8Bit(), (const char*)desiredFileName.local8Bit()); return; } } void ContactList::clear() { p->clear(false); } void ContactList::load() { clear(); QString cfgName = user_file(CONTACTS_CONF); QFile f(cfgName); if (!f.open(IO_ReadOnly)){ log(L_ERROR, "Can't open %s", cfgName.local8Bit().data()); return; } Buffer cfg = f.readAll(); Contact *c = NULL; Group *g = NULL; for (;;){ QCString s = cfg.getSection(); QString section = s; // is ascii - ok here if (section.isEmpty()) break; if (section == OWNER){ p->flush(c, g); c = owner(); g = NULL; s = ""; }else if (section.startsWith(GROUP)){ p->flush(c, g); c = NULL; unsigned long id = section.mid(strlen(GROUP)).toLong(); g = group(id, id != 0); s = ""; }else if (section.startsWith(CONTACT)){ p->flush(c, g); g = NULL; unsigned long id = section.mid(strlen(CONTACT)).toLong(); c = contact(id, true); s = ""; } p->flush(c, g, s, &cfg); } p->flush(c, g); // Notify the clients about the newly loaded contact list for (unsigned i = 0; i < nClients(); i++){ Client *client = getClient(i); client->contactsLoaded(); } } void ContactListPrivate::flush(Contact *c, Group *g) { ClientUserData *data = NULL; if (c) data = &c->clientData; if (g) data = &g->clientData; if (data) data->sort(); } void ContactListPrivate::flush(Contact *c, Group *g, const QCString &_section, Buffer *cfg) { if (cfg == NULL) return; // section name is ascii every time QString section(_section); if (section.isEmpty()){ if (c){ free_data(contactData, &c->data); load_data(contactData, &c->data, cfg); return; } if (g){ free_data(groupData, &g->data); load_data(groupData, &g->data, cfg); return; } return; } list::iterator it; for (it = userDataDef.begin(); it != userDataDef.end(); ++it){ if (section != (*it).name) continue; UserData *data = &userData; if (c) data = &c->userData; if (g) data = &g->userData; data->load((*it).id, (*it).def, cfg); return; } for (unsigned i = 0; i < getContacts()->nClients(); i++){ Client *client = getContacts()->getClient(i); if (client->name() != section) continue; ClientUserData *data = NULL; if (c) data = &c->clientData; if (g) data = &g->clientData; if (data) data->load(client, cfg); return; } } unsigned ContactList::nClients() { return p->clients.size(); } Client *ContactList::getClient(unsigned n) { if (n >= p->clients.size()) return NULL; return p->clients[n]; } void ContactList::clearClients() { p->bNoRemove = true; while (!p->clients.empty()) delete p->clients[0]; p->bNoRemove = false; EventClientsChanged eClients; eClients.process(); } void *ContactList::getUserData(unsigned id) { return p->userData.getUserData(id, true); } static QString stripPhone(const QString &phone) { QString res; if (phone == NULL) return res; for (unsigned i = 0; i < phone.length(); i++){ const QChar &c = phone[(int)i]; if ((c < '0') || (c > '9')) continue; res += c; } if (res.length() > 7) res = res.mid(res.length() - 7); return res; } bool ContactList::cmpPhone(const QString &phone1, const QString &phone2) { return stripPhone(phone1) == stripPhone(phone2); } Contact *ContactList::contactByPhone(const QString &_phone) { QString phone = stripPhone(_phone); if (phone.isEmpty()) return NULL; Contact *c; ContactIterator it; while ((c = ++it) != NULL){ QString phones = c->getPhones(); while (!phones.isEmpty()){ QString phoneItem = getToken(phones, ';', false); if (cmpPhone(getToken(phoneItem, ','), _phone)) return c; } } c = contact(0, true); c->setFlags(CONTACT_TEMP); c->setName(_phone); EventContact e(c, EventContact::eChanged); e.process(); return c; } Contact *ContactList::contactByMail(const QString &_mail, const QString &_name) { QString name = _name; Contact *c; ContactIterator it; if (_mail.isEmpty()){ while ((c = ++it) != NULL){ if (c->getName().lower() == name.lower()) return c; } c = contact(0, true); c->setFlags(CONTACT_TEMP); c->setName(name); EventContact e(c, EventContact::eChanged); e.process(); return c; } if (name.isEmpty()) name = _mail; while ((c = ++it) != NULL){ QString mails = c->getEMails(); while (mails.length()){ QString mail = getToken(mails, ';', false); mail = getToken(mail, '/'); if (mail.lower() == _mail.lower()) return c; } } c = contact(0, true); c->setFlags(CONTACT_TEMP); c->setName(name); c->setEMails(_mail + "/-"); EventContact e(c, EventContact::eChanged); e.process(); return c; } EXPORT ContactList *getContacts() { return PluginManager::contacts; } // see also http://www.iana.org/assignments/character-sets static ENCODING encodings[] = { { I18N_NOOP("Unicode"), "UTF-8", 106, 0, 65001, true }, { I18N_NOOP("Arabic"), "ISO 8859-6", 82, 180, 28596, false }, { I18N_NOOP("Arabic"), "CP 1256", 2256, 180, 1256, true }, { I18N_NOOP("Baltic"), "ISO 8859-13", 109, 186, 28594, false }, { I18N_NOOP("Baltic"), "CP 1257", 2257, 186, 1257, true }, { I18N_NOOP("Central European"), "ISO 8859-2", 5, 238, 28592, false }, { I18N_NOOP("Esperanto"), "ISO 8859-3", 6, 238, 28593, false }, { I18N_NOOP("Central European"), "CP 1250", 2250, 238, 1250, true }, { I18N_NOOP("Chinese "), "GBK", 2025, 134, 0, false }, { I18N_NOOP("Chinese Simplified"), "gbk2312",2312, 134, 0, true }, { I18N_NOOP("Chinese Traditional"), "Big5",2026, 136, 0, true }, { I18N_NOOP("Cyrillic"), "ISO 8859-5", 8, 204, 28595, false }, { I18N_NOOP("Cyrillic"), "KOI8-R", 2084, 204, 1251, false }, { I18N_NOOP("Ukrainian"), "KOI8-U", 2088, 204, 1251, false }, { I18N_NOOP("Cyrillic"), "CP 1251", 2251, 204, 1251, true }, { I18N_NOOP("Greek"), "ISO 8859-7", 10, 161, 28597, false }, { I18N_NOOP("Greek"), "CP 1253", 2253, 161, 1253, true }, { I18N_NOOP("Hebrew"), "ISO 8859-8-I", 85, 177, 28598, false }, { I18N_NOOP("Hebrew"), "CP 1255", 2255, 177, 1255, true }, { I18N_NOOP("Japanese"), "JIS7", 16, 128, 0, false }, { I18N_NOOP("Japanese"), "eucJP", 18, 128, 0, false }, { I18N_NOOP("Japanese"), "Shift-JIS", 17, 128, 0, true }, { I18N_NOOP("Korean"), "eucKR", 38, 0, 0, true }, { I18N_NOOP("Western European"), "ISO 8859-1", 4, 0, 28591, false }, { I18N_NOOP("Western European"), "ISO 8859-15", 111, 0, 28605, false }, { I18N_NOOP("Western European"), "CP 1252", 2252, 0, 1252, true }, { I18N_NOOP("Tamil"), "TSCII", 2107, 0, 0, true }, { I18N_NOOP("Thai"), "TIS-620", 2259, 222, 0, true }, { I18N_NOOP("Turkish"), "ISO 8859-9", 12, 162, 28599, false }, { I18N_NOOP("Turkish"), "CP 1254", 2254, 162, 1254, true }, { NULL, NULL, 0, 0, 0, false } }; const ENCODING *ContactList::getEncodings() { return encodings; } QTextCodec *ContactList::getCodecByName(const char *encoding) { QTextCodec *codec = NULL; if (encoding && *encoding){ codec = QTextCodec::codecForName(encoding); if (codec) return codec; } codec = QTextCodec::codecForLocale(); const ENCODING *e; const char *codecName = codec->name(); for (e = encodings; e->language; e++){ if (!strcmp(codecName, e->codec)) break; } if (e->language && !e->bMain){ for (e++; e->language; e++){ if (e->bMain){ codec = QTextCodec::codecForName(e->codec); break; } } } if (codec == NULL) codec= QTextCodec::codecForLocale(); return codec; } QTextCodec *ContactList::getCodec(Contact *contact) { QTextCodec *codec = NULL; if (contact && !contact->getEncoding().isEmpty()){ codec = getCodecByName(contact->getEncoding()); if (codec) return codec; } return getCodecByName(owner()->getEncoding()); } QString ContactList::toUnicode(Contact *contact, const QCString &str, int length) { if (!str.isEmpty()){ if (length < 0) length = str.length(); QString res = getCodec(contact)->toUnicode(str, length); return res.remove('\r'); } return QString::null; } QCString ContactList::fromUnicode(Contact *contact, const QString &str) { if (str.isEmpty()) return ""; QString s = str; s = s.replace(QRegExp("\r?\n"), "\r\n"); return getCodec(contact)->fromUnicode(s); } } // namespace sim EXPORT QString g_i18n(const char *text, SIM::Contact *contact) { QString male = i18n("male", text); if (contact == NULL) return male; QString female = i18n("female", text); if (male == female) return male; QString gender = contact->clientData.property("Gender"); if (gender.toLong() == 1) return female; return male; }