/*************************************************************************** jabberclient.cpp - description ------------------- begin : Sun Mar 17 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 "jabberclient.h" #include "jabber.h" #include "jabberconfig.h" #include "jabber_ssl.h" #include "jabberadd.h" #include "jabberinfo.h" #include "jabberhomeinfo.h" #include "jabberworkinfo.h" #include "jabberaboutinfo.h" #include "jabberpicture.h" #include "jabbermessage.h" #include "jabberbrowser.h" #include "infoproxy.h" #include "html.h" #include "icons.h" #include "core.h" #include #include #include #include #include #include #include #include #ifndef WIN32 #include #endif #include #ifndef XML_STATUS_OK #define XML_STATUS_OK 1 #define XML_STATUS_ERROR 0 #endif unsigned PING_TIMEOUT = 50; /* typedef struct JabberUserData { char *ID; char *Resource; unsigned Status; char *FirstName; char *Nick; char *Desc; char *Bday; char *Url; char *OrgName; char *OrgUnit; char *Title; char *Role; char *Street; char *City; char *Region; char *PCode; char *Country; } JabberUserData; */ DataDef jabberUserData[] = { { "", DATA_ULONG, 1, DATA(2) }, // Sign { "LastSend", DATA_ULONG, 1, 0 }, { "ID", DATA_UTF, 1, 0 }, { "Node", DATA_UTF, 1, 0 }, { "Resource", DATA_UTF, 1, 0 }, { "Name", DATA_UTF, 1, 0 }, { "", DATA_ULONG, 1, DATA(1) }, // Status { "FirstName", DATA_UTF, 1, 0 }, { "Nick", DATA_UTF, 1, 0 }, { "Desc", DATA_UTF, 1, 0 }, { "BirthDay", DATA_UTF, 1, 0 }, { "Url", DATA_UTF, 1, 0 }, { "OrgName", DATA_UTF, 1, 0 }, { "OrgUnit", DATA_UTF, 1, 0 }, { "Role", DATA_UTF, 1, 0 }, { "Title", DATA_UTF, 1, 0 }, { "Street", DATA_UTF, 1, 0 }, { "ExtAddr", DATA_UTF, 1, 0 }, { "City", DATA_UTF, 1, 0 }, { "Region", DATA_UTF, 1, 0 }, { "PCode", DATA_UTF, 1, 0 }, { "Country", DATA_UTF, 1, 0 }, { "EMail", DATA_UTF, 1, 0 }, { "Phone", DATA_UTF, 1, 0 }, { "StatusTime", DATA_ULONG, 1, 0 }, { "OnlineTime", DATA_ULONG, 1, 0 }, { "Subscribe", DATA_ULONG, 1, 0 }, { "Group", DATA_UTF, 1, 0 }, { "", DATA_BOOL, 1, 0 }, // bChecked { "", DATA_STRING, 1, 0 }, // TypingId { "", DATA_BOOL, 1, 0 }, // SendTypingEvents { "", DATA_BOOL, 1, 0 }, // IsTyping { "", DATA_ULONG, 1, 0 }, // ComposeId { "", DATA_ULONG, 1, DATA(1) }, // richText { "", DATA_BOOL, 1, 0 }, { "PhotoWidth", DATA_ULONG, 1, 0 }, { "PhotoHeight", DATA_ULONG, 1, 0 }, { "LogoWidth", DATA_ULONG, 1, 0 }, { "LogoHeight", DATA_ULONG, 1, 0 }, { "", DATA_ULONG, 1, 0 }, // nResources { "", DATA_STRLIST, 1, 0 }, // Resources { "", DATA_STRLIST, 1, 0 }, // ResourceStatus { "", DATA_STRLIST, 1, 0 }, // ResourceReply { "", DATA_STRLIST, 1, 0 }, // ResourceStatusTime { "", DATA_STRLIST, 1, 0 }, // ResourceOnlineTime { "AutoReply", DATA_UTF, 1, 0 }, { "", DATA_STRLIST, 1, 0 }, // ResourceClientName { "", DATA_STRLIST, 1, 0 }, // ResourceClientVersion { "", DATA_STRLIST, 1, 0 }, // ResourceClientOS { NULL, 0, 0, 0 } }; /* typedef struct JabberClientData { char *ID; char *Server; unsigned Port; unsigned UseSSL; unsigned UsePlain; unsigned UseVHost; unsigned Register; char *ListRequest; } JabberClientData; */ static DataDef jabberClientData[] = { { "Server", DATA_STRING, 1, "jabber.org" }, { "Port", DATA_ULONG, 1, DATA(5222) }, { "UseSSL", DATA_BOOL, 1, 0 }, { "UsePlain", DATA_BOOL, 1, 0 }, { "UseVHost", DATA_BOOL, 1, 0 }, { "", DATA_BOOL, 1, 0 }, { "Priority", DATA_ULONG, 1, DATA(5) }, { "ListRequest", DATA_UTF, 1, 0 }, { "VHost", DATA_UTF, 1, 0 }, { "Typing", DATA_BOOL, 1, DATA(1) }, { "RichText", DATA_BOOL, 1, DATA(1) }, { "UseVersion", DATA_BOOL, 1, DATA(1) }, { "ProtocolIcons", DATA_BOOL, 1, DATA(1) }, { "MinPort", DATA_ULONG, 1, DATA(1024) }, { "MaxPort", DATA_ULONG, 1, DATA(0xFFFF) }, { "Photo", DATA_UTF, 1, 0 }, { "Logo", DATA_UTF, 1, 0 }, { "AutoSubscribe", DATA_BOOL, 1, DATA(1) }, { "AutoAccept", DATA_BOOL, 1, DATA(1) }, { "UseHTTP", DATA_BOOL, 1, 0 }, { "URL", DATA_STRING, 1, 0 }, { "InfoUpdated", DATA_BOOL, 1, 0 }, { "", DATA_STRUCT, sizeof(JabberUserData) / sizeof(Data), DATA(jabberUserData) }, { NULL, 0, 0, 0 } }; JabberClient::JabberClient(JabberProtocol *protocol, Buffer *cfg) : TCPClient(protocol, cfg) { load_data(jabberClientData, &data, cfg); QString jid = QString::fromUtf8(data.owner.ID.ptr); int n = jid.find("@"); if (n > 0){ jid = jid.left(n); set_str(&data.owner.ID.ptr, jid.utf8()); } if (data.owner.Resource.ptr == NULL){ string resource = PACKAGE; set_str(&data.owner.Resource.ptr, resource.c_str()); } QString listRequests = getListRequest(); while (!listRequests.isEmpty()){ QString item = getToken(listRequests, ';', false); JabberListRequest lr; lr.bDelete = false; lr.jid = getToken(item, ',').utf8(); lr.grp = getToken(item, ',').utf8(); if (!item.isEmpty()) lr.bDelete = true; m_listRequests.push_back(lr); } setListRequest(NULL); m_bSSL = false; m_curRequest = NULL; m_msg_id = 0; m_bJoin = false; init(); } JabberClient::~JabberClient() { TCPClient::setStatus(STATUS_OFFLINE, false); free_data(jabberClientData, &data); freeData(); } const DataDef *JabberProtocol::userDataDef() { return jabberUserData; } bool JabberClient::compareData(void *d1, void *d2) { JabberUserData *data1 = (JabberUserData*)d1; JabberUserData *data2 = (JabberUserData*)d2; return strcasecmp(data1->ID.ptr, data2->ID.ptr) == 0; } void JabberClient::setID(const QString &id) { set_str(&data.owner.ID.ptr, id.utf8()); } string JabberClient::getConfig() { QString lr; for (list::iterator it = m_listRequests.begin(); it != m_listRequests.end(); ++it){ if (!lr.isEmpty()) lr += ";"; lr += quoteChars(QString::fromUtf8((*it).jid.c_str()), ",;"); lr += ","; lr += quoteChars(QString::fromUtf8((*it).grp.c_str()), ",;"); if ((*it).bDelete) lr += ",1"; } setListRequest(lr); string res = Client::getConfig(); if (res.length()) res += "\n"; return res += save_data(jabberClientData, &data); } string JabberClient::name() { string res = "Jabber."; if (data.owner.ID.value){ string server; if (getUseVHost()) server = getVHost(); if (server.empty()) server = getServer(); res += data.owner.ID.ptr; res += '@'; res += server; } return res; } QWidget *JabberClient::setupWnd() { return new JabberConfig(NULL, this, false); } bool JabberClient::isMyData(clientData *&_data, Contact *&contact) { if (_data->Sign.value != JABBER_SIGN) return false; string resource; JabberUserData *data = (JabberUserData*)_data; JabberUserData *my_data = findContact(data->ID.ptr, NULL, false, contact, resource); if (my_data){ data = my_data; }else{ contact = NULL; } return true; } bool JabberClient::createData(clientData *&_data, Contact *contact) { JabberUserData *data = (JabberUserData*)_data; JabberUserData *new_data = (JabberUserData*)(contact->clientData.createData(this)); set_str(&new_data->ID.ptr, data->ID.ptr); _data = (clientData*)new_data; return true; } void JabberClient::connect_ready() { if (!getUseSSL() || m_bSSL){ connected(); return; } #ifdef USE_OPENSSL m_bSSL = true; SSLClient *ssl = new JabberSSL(m_socket->socket()); m_socket->setSocket(ssl); if (!ssl->init()){ m_socket->error_state("SSL init error"); return; } ssl->connect(); ssl->process(); #endif } void JabberClient::connected() { m_socket->readBuffer.init(0); m_socket->readBuffer.packetStart(); m_socket->setRaw(true); log(L_DEBUG, "Connect ready"); startHandshake(); TCPClient::connect_ready(); reset(); } void JabberClient::packet_ready() { if (m_socket->readBuffer.writePos() == 0) return; JabberPlugin *plugin = static_cast(protocol()->plugin()); log_packet(m_socket->readBuffer, false, plugin->JabberPacket); if (!parse(m_socket->readBuffer.data(), m_socket->readBuffer.size(), true)) m_socket->error_state("XML parse error"); m_socket->readBuffer.init(0); m_socket->readBuffer.packetStart(); } static bool cmp(const string &str, const char *s) { if (s == NULL) return str.empty(); return str == s; } void *JabberClient::processEvent(Event *e) { TCPClient::processEvent(e); if (e->type() == EventAddContact){ addContact *ac = (addContact*)(e->param()); if (ac->proto && !strcmp(protocol()->description()->text, ac->proto)){ Contact *contact = NULL; string resource; findContact(ac->addr, ac->nick, true, contact, resource); if (contact && contact->getGroup() != ac->group){ contact->setGroup(ac->group); Event e(EventContactChanged, contact); e.process(); } return contact; } return NULL; } if (e->type() == EventDeleteContact){ char *addr = (char*)(e->param()); ContactList::ContactIterator it; Contact *contact; while ((contact = ++it) != NULL){ JabberUserData *data; ClientDataIterator itc(contact->clientData, this); while ((data = (JabberUserData*)(++itc)) != NULL){ if (!strcmp(data->ID.ptr, addr)){ contact->clientData.freeData(data); ClientDataIterator itc(contact->clientData); if (++itc == NULL) delete contact; return e->param(); } } } return NULL; } if (e->type() == EventGoURL){ string url = (const char*)(e->param()); string proto; int n = url.find(':'); if (n < 0) return NULL; proto = url.substr(0, n); if (proto != "jabber") return NULL; url = url.substr(proto.length() + 1); while (url[0] == '/') url = url.substr(1); QString s = unquoteString(QString(url.c_str())); QString jid = getToken(s, '/'); if (!jid.isEmpty()){ Contact *contact; string resource; findContact(jid.utf8(), s.utf8(), true, contact, resource); Command cmd; cmd->id = MessageGeneric; cmd->menu_id = MenuMessage; cmd->param = (void*)(contact->id()); Event eCmd(EventCommandExec, cmd); eCmd.process(); return e->param(); } } if (e->type() == EventTemplateExpanded){ TemplateExpand *t = (TemplateExpand*)(e->param()); setStatus((unsigned long)(t->param), quoteString(t->tmpl, quoteNOBR).utf8()); } if (e->type() == EventContactChanged){ Contact *contact = (Contact*)(e->param()); string grpName; string name; name = contact->getName().utf8(); Group *grp = NULL; if (contact->getGroup()) grp = getContacts()->group(contact->getGroup()); if (grp) grpName = grp->getName().utf8(); ClientDataIterator it(contact->clientData, this); JabberUserData *data; while ((data = (JabberUserData*)(++it)) != NULL){ if (!cmp(grpName, data->Group.ptr)){ listRequest(data, name.c_str(), grpName.c_str(), false); continue; } if (data->Name.ptr && *data->Name.ptr){ if (!cmp(name, data->Name.ptr)) listRequest(data, name.c_str(), grpName.c_str(), false); continue; } if (!cmp(name, data->ID.ptr)) listRequest(data, name.c_str(), grpName.c_str(), false); } return NULL; } if (e->type() == EventContactDeleted){ Contact *contact = (Contact*)(e->param()); ClientDataIterator it(contact->clientData, this); JabberUserData *data; while ((data = (JabberUserData*)(++it)) != NULL){ listRequest(data, NULL, NULL, true); } return NULL; } if (e->type() == EventGroupChanged){ Group *grp = (Group*)(e->param()); string grpName; grpName = grp->getName().utf8(); ContactList::ContactIterator itc; Contact *contact; while ((contact = ++itc) != NULL){ if (contact->getGroup() != grp->id()) continue; ClientDataIterator it(contact->clientData, this); JabberUserData *data; while ((data = (JabberUserData*)(++it)) != NULL){ if (!cmp(grpName, data->Group.ptr)) listRequest(data, contact->getName().utf8(), grpName.c_str(), false); } } } if (e->type() == EventMessageCancel){ Message *msg = (Message*)(e->param()); for (list::iterator it = m_waitMsg.begin(); it != m_waitMsg.end(); ++it){ if ((*it) == msg){ m_waitMsg.erase(it); delete msg; return e->param(); } } return NULL; } if (e->type() == EventMessageAccept){ messageAccept *ma = (messageAccept*)(e->param()); for (list::iterator it = m_ackMsg.begin(); it != m_ackMsg.end(); ++it){ if ((*it)->id() == ma->msg->id()){ JabberFileMessage *msg = static_cast(*it); m_ackMsg.erase(it); Contact *contact; string resource; JabberUserData *data = findContact(msg->getFrom(), NULL, false, contact, resource); if (data){ JabberFileTransfer *ft = new JabberFileTransfer(static_cast(msg), data, this); ft->setDir(QFile::encodeName(ma->dir)); ft->setOverwrite(ma->overwrite); Event e(EventMessageAcked, msg); e.process(); ft->connect(); } Event eDel(EventMessageDeleted, msg); eDel.process(); if (data == NULL) delete msg; return msg; } } return NULL; } if (e->type() == EventMessageDecline){ messageDecline *md = (messageDecline*)(e->param()); for (list::iterator it = m_ackMsg.begin(); it != m_ackMsg.end(); ++it){ if ((*it)->id() == md->msg->id()){ JabberFileMessage *msg = static_cast(*it); m_ackMsg.erase(it); string reason = "File transfer declined"; if (md->reason) reason = md->reason; ServerRequest req(this, "error", NULL, msg->getFrom(), msg->getID()); req.start_element("error"); req.add_attribute("code", "403"); req.add_text(reason.c_str()); req.send(); Event e(EventMessageDeleted, msg); e.process(); delete msg; return msg; } } return NULL; } if (e->type() == EventClientVersion){ ClientVersionInfo* info = static_cast(e->param()); if (!info->jid.isEmpty()){ Contact *contact; string resource; JabberUserData* data = findContact(info->jid.utf8(), NULL, false, contact, resource); if (!data) return NULL; unsigned i; for (i = 1; i <= data->nResources.value; i++){ if (resource == get_str(data->Resources, i)) break; } if (i <= data->nResources.value){ set_str(&data->ResourceClientName, i, info->name); set_str(&data->ResourceClientVersion, i, info->version); set_str(&data->ResourceClientOS, i, info->os); } } return NULL; } return NULL; } void JabberClient::setStatus(unsigned status) { if (getInvisible() && (status != STATUS_OFFLINE)){ if (m_status != status){ m_status = status; Event e(EventClientChanged, static_cast(this)); e.process(); } return; } ARRequest ar; ar.contact = NULL; ar.status = status; ar.receiver = this; ar.param = (void*)(long)status; Event e(EventARRequest, &ar); e.process(); } void JabberClient::setStatus(unsigned status, const char *ar) { if (status != m_status){ time_t now; time(&now); data.owner.StatusTime.value = now; if (m_status == STATUS_OFFLINE) data.owner.OnlineTime.value = now; m_status = status; m_socket->writeBuffer.packetStart(); string priority = number(getPriority()); const char *show = NULL; const char *type = NULL; if (getInvisible()){ type = "invisible"; }else{ switch (status){ case STATUS_AWAY: show = "away"; break; case STATUS_NA: show = "xa"; break; case STATUS_DND: show = "dnd"; break; case STATUS_FFC: show = "chat"; break; case STATUS_OFFLINE: priority = ""; type = "unavailable"; break; } } m_socket->writeBuffer << "writeBuffer << " type=\'" << type << "\'"; m_socket->writeBuffer << ">\n"; if (show && *show) m_socket->writeBuffer << "" << show << "\n"; if (ar && *ar){ m_socket->writeBuffer << "" << ar << "\n"; } if (!priority.empty()) m_socket->writeBuffer << "" << priority.c_str() << "\n"; m_socket->writeBuffer << ""; sendPacket(); Event e(EventClientChanged, static_cast(this)); e.process(); } if (status == STATUS_OFFLINE){ if (m_socket){ m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "\n"; sendPacket(); } Contact *contact; ContactList::ContactIterator it; time_t now; time(&now); data.owner.StatusTime.value = now; while ((contact = ++it) != NULL){ JabberUserData *data; ClientDataIterator it(contact->clientData, this); while ((data = (JabberUserData*)(++it)) != NULL){ if (data->Status.value == STATUS_OFFLINE) continue; data->StatusTime.value = now; setOffline(data); StatusMessage m; m.setContact(contact->id()); m.setClient(dataName(data).c_str()); m.setFlags(MESSAGE_RECEIVED); m.setStatus(STATUS_OFFLINE); Event e(EventMessageReceived, &m); e.process(); } } } } void JabberClient::disconnected() { for (list::iterator it = m_requests.begin(); it != m_requests.end(); ++it) delete *it; m_requests.clear(); if (m_curRequest){ delete m_curRequest; m_curRequest = NULL; } list::iterator itm; for (itm = m_ackMsg.begin(); itm != m_ackMsg.end(); ++itm){ Message *msg = *itm; Event e(EventMessageDeleted, msg); e.process(); delete msg; } for (itm = m_waitMsg.begin(); itm != m_waitMsg.end(); itm = m_waitMsg.begin()){ Message *msg = *itm; msg->setError(I18N_NOOP("Client go offline")); Event e(EventMessageSent, msg); e.process(); delete msg; } m_ackMsg.clear(); init(); } void JabberClient::init() { m_id = ""; m_depth = 0; m_id_seed = 0xAAAA; m_bSSL = false; } void JabberClient::sendPacket() { JabberPlugin *plugin = static_cast(protocol()->plugin()); log_packet(m_socket->writeBuffer, true, plugin->JabberPacket); m_socket->write(); } string JabberClient::get_attr(const char *name, const char **attr) { if (attr == NULL) return ""; for (const char **p = attr; *p; ){ string tag = to_lower(*(p++)); if (tag == name){ return *p; } } return ""; } void JabberClient::element_start(const char *el, const char **attr) { string element = to_lower(el); const char *id = NULL; if (m_depth){ if (m_curRequest){ m_curRequest->element_start(element.c_str(), attr); }else{ if (element == "iq"){ string id = get_attr("id", attr); string type = get_attr("type", attr); if (id.empty() || (type == "set") || (type == "get")){ m_curRequest = new IqRequest(this); m_curRequest->element_start(element.c_str(), attr); }else{ list::iterator it; for (it = m_requests.begin(); it != m_requests.end(); ++it){ if ((*it)->m_id == id) break; } if (it != m_requests.end()){ m_curRequest = *it; m_requests.erase(it); m_curRequest->element_start(element.c_str(), attr); }else{ log(L_WARN, "Packet %s not found", id.c_str()); } } }else if (element == "presence"){ m_curRequest = new PresenceRequest(this); m_curRequest->element_start(element.c_str(), attr); }else if (element == "message"){ m_curRequest = new MessageRequest(this); m_curRequest->element_start(element.c_str(), attr); }else if (element != "a"){ log(L_DEBUG, "Bad tag %s", element.c_str()); } } }else{ if (element == "stream:stream" && attr){ for (const char **p = attr; *p; ){ string tag = to_lower(*(p++)); if (tag == "id"){ id = *p; break; } } } log(L_DEBUG, "Handshake %s (%s)", id, element.c_str()); handshake(id); } m_depth++; } void JabberClient::element_end(const char *el) { m_depth--; if (m_curRequest){ string element = to_lower(el); m_curRequest->element_end(element.c_str()); if (m_depth == 1){ delete m_curRequest; m_curRequest = NULL; } } } void JabberClient::char_data(const char *str, int len) { if (m_curRequest) m_curRequest->char_data(str, len); } string JabberClient::get_unique_id() { char b[10]; sprintf(b, "a%x", m_id_seed); m_id_seed += 0x10; return b; } JabberClient::ServerRequest::ServerRequest(JabberClient *client, const char *type, const char *from, const char *to, const char *id) { m_client = client; if (type == NULL) return; if (id){ m_id = id; }else{ m_id = m_client->get_unique_id(); } if (m_client->m_socket == NULL) return; m_client->m_socket->writeBuffer.packetStart(); m_client->m_socket->writeBuffer << "m_socket->writeBuffer << " from=\'" << from << "\'"; if (to) m_client->m_socket->writeBuffer << " to=\'" << to << "\'"; m_client->m_socket->writeBuffer << ">\n"; } JabberClient::ServerRequest::~ServerRequest() { } void JabberClient::ServerRequest::send() { end_element(false); while (!m_els.empty()){ end_element(false); } m_client->m_socket->writeBuffer << "\n"; m_client->sendPacket(); } void JabberClient::ServerRequest::element_start(const char*, const char**) { } void JabberClient::ServerRequest::element_end(const char*) { } void JabberClient::ServerRequest::char_data(const char*, int) { } void JabberClient::ServerRequest::start_element(const char *name) { end_element(true); m_client->m_socket->writeBuffer << "<" << name; m_element = name; } void JabberClient::ServerRequest::add_attribute(const char *name, const char *value) { m_client->m_socket->writeBuffer << " " << name << "=\'" << JabberClient::encodeXML(QString::fromUtf8(value)).replace('\'', "'") << "\'"; } void JabberClient::ServerRequest::end_element(bool bNewLevel) { if (bNewLevel){ if (m_element.length()){ m_client->m_socket->writeBuffer << ">\n"; m_els.push(m_element); } }else{ if (m_element.length()){ m_client->m_socket->writeBuffer << "/>\n"; }else if (m_els.size()){ m_element = m_els.top(); m_els.pop(); m_client->m_socket->writeBuffer << "\n"; } } m_element = ""; } void JabberClient::ServerRequest::add_text(const char *value) { if (m_element.length()){ m_client->m_socket->writeBuffer << ">"; m_els.push(m_element); m_element = ""; } m_client->m_socket->writeBuffer << JabberClient::encodeXML(QString::fromUtf8(value)); } void JabberClient::ServerRequest::text_tag(const char *name, const char *value) { if ((value == NULL) || (*value == 0)) return; end_element(true); m_client->m_socket->writeBuffer << "<" << name << ">" << JabberClient::encodeXML(QString::fromUtf8(value)) << "\n"; } void JabberClient::ServerRequest::add_condition(const char *condition, bool bXData) { QString cond = QString::fromUtf8(condition); while (cond.length()){ QString item = getToken(cond, ';'); if (item == "x:data"){ bXData = true; start_element("x"); add_attribute("xmlns", "jabber:x:data"); add_attribute("type", "submit"); } QString key = getToken(item, '='); if (bXData){ start_element("field"); add_attribute("var", key.utf8()); text_tag("value", item.utf8()); end_element(); }else{ text_tag(key.utf8(), item.utf8()); } } } const char *JabberClient::ServerRequest::_GET = "get"; const char *JabberClient::ServerRequest::_SET = "set"; const char *JabberClient::ServerRequest::_RESULT = "result"; void JabberClient::startHandshake() { m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "" << "\n"; sendPacket(); } void JabberClient::handshake(const char *id) { if (id == NULL){ m_socket->error_state("Bad session ID"); return; } m_id = id; if (getRegister()){ auth_register(); }else{ #ifdef USE_OPENSSL if (getUsePlain()){ auth_plain(); }else{ auth_digest(); } #else auth_plain(); #endif } } void JabberClient::auth_ok() { if (getRegister()){ setRegister(false); setClientStatus(STATUS_OFFLINE); TCPClient::setStatus(getManualStatus(), getCommonStatus()); return; } setState(Connected); setPreviousPassword(NULL); rosters_request(); if (getInfoUpdated()){ setClientInfo(&data.owner); }else{ info_request(NULL, false); } setStatus(m_logonStatus); QTimer::singleShot(PING_TIMEOUT * 1000, this, SLOT(ping())); } void JabberClient::auth_failed() { m_reconnect = NO_RECONNECT; m_socket->error_state(I18N_NOOP("Login failed"), AuthError); } string JabberClient::to_lower(const char *s) { string res; if (s == NULL) return res; for (; *s; s++) res += (char)tolower(*s); return res; } QCString JabberClient::encodeXML(const QString &str) { return quoteString(str, quoteNOBR).utf8(); } JabberUserData *JabberClient::findContact(const char *_jid, const char *name, bool bCreate, Contact *&contact, string &resource, bool bJoin) { resource = ""; string jid = _jid; int n = jid.find('/'); if (n >= 0){ resource = jid.substr(n + 1); jid = jid.substr(0, n); } ContactList::ContactIterator it; while ((contact = ++it) != NULL){ JabberUserData *data; ClientDataIterator it(contact->clientData, this); while ((data = (JabberUserData*)(++it)) != NULL){ if (QString::fromUtf8(jid.c_str()).upper() != QString::fromUtf8(data->ID.ptr).upper()) continue; if (!resource.empty()) set_str(&data->Resource.ptr, resource.c_str()); if (name) set_str(&data->Name.ptr, name); return data; } } if (!bCreate) return NULL; it.reset(); QString sname; if (name && *name){ sname = QString::fromUtf8(name); }else{ sname = QString::fromUtf8(jid.c_str()); int pos = sname.find('@'); if (pos > 0) sname = sname.left(pos); } if (bJoin){ while ((contact = ++it) != NULL){ if (contact->getName().lower() == sname.lower()){ JabberUserData *data = (JabberUserData*)(contact->clientData.createData(this)); set_str(&data->ID.ptr, jid.c_str()); if (!resource.empty()) set_str(&data->Resource.ptr, resource.c_str()); if (name) set_str(&data->Name.ptr, name); info_request(data, false); Event e(EventContactChanged, contact); e.process(); m_bJoin = true; return data; } } } contact = getContacts()->contact(0, true); JabberUserData *data = (JabberUserData*)(contact->clientData.createData(this)); set_str(&data->ID.ptr, jid.c_str()); if (!resource.empty()) set_str(&data->Resource.ptr, resource.c_str()); if (name) set_str(&data->Name.ptr, name); contact->setName(sname); info_request(data, false); Event e(EventContactChanged, contact); e.process(); return data; } static void addIcon(string *s, const char *icon, const char *statusIcon) { if (s == NULL) return; if (statusIcon && !strcmp(statusIcon, icon)) return; string str = *s; while (!str.empty()){ string item = getToken(str, ','); if (item == icon) return; } if (!s->empty()) *s += ','; *s += icon; } const char *JabberClient::get_icon(JabberUserData *data, unsigned status, bool invisible) { const CommandDef *def = protocol()->statusList(); for (; def->text; def++){ if (def->id == status) break; } if ((def == NULL) || (def->text == NULL)) return "Jabber_offline"; const char *dicon = def->icon; if (invisible) dicon = "Jabber_invisible"; if (getProtocolIcons()){ char *host = strchr(data->ID.ptr, '@'); string h; if (host) h = host + 1; else h = data->ID.ptr; char *p = strchr((char*)(h.c_str()), '.'); if (p) *p = 0; if (strcmp(h.c_str(), "icq") == 0){ if (invisible){ dicon = "ICQ_invisible"; }else{ switch (status){ case STATUS_ONLINE: dicon = "ICQ_online"; break; case STATUS_OFFLINE: dicon = "ICQ_offline"; break; case STATUS_AWAY: dicon = "ICQ_away"; break; case STATUS_NA: dicon = "ICQ_na"; break; case STATUS_DND: dicon = "ICQ_dnd"; break; case STATUS_FFC: dicon = "ICQ_ffc"; break; } } }else if (strcmp(h.c_str(), "aim") == 0){ switch (status){ case STATUS_ONLINE: dicon = "AIM_online"; break; case STATUS_OFFLINE: dicon = "AIM_offline"; break; case STATUS_AWAY: dicon = "AIM_away"; break; } }else if (strcmp(h.c_str(), "msn") == 0){ if (invisible){ dicon = "MSN_invisible"; }else{ switch (status){ case STATUS_ONLINE: dicon = "MSN_online"; break; case STATUS_OFFLINE: dicon = "MSN_offline"; break; case STATUS_AWAY: dicon = "MSN_away"; break; case STATUS_NA: dicon = "MSN_na"; break; case STATUS_DND: dicon = "MSN_dnd"; break; } } }else if (strcmp(h.c_str(), "yahoo") == 0){ switch (status){ case STATUS_ONLINE: dicon = "Yahoo!_online"; break; case STATUS_OFFLINE: dicon = "Yahoo!_offline"; break; case STATUS_AWAY: dicon = "Yahoo!_away"; break; case STATUS_NA: dicon = "Yahoo!_na"; break; case STATUS_DND: dicon = "Yahoo!_dnd"; break; case STATUS_FFC: dicon = "Yahoo!_ffc"; break; } }else if (strcmp(h.c_str(), "sms") == 0){ switch (status){ case STATUS_ONLINE: dicon = "sms_online"; break; case STATUS_OFFLINE: dicon = "sms_offline"; break; case STATUS_AWAY: dicon = "sms_away"; break; case STATUS_NA: dicon = "sms_na"; break; case STATUS_DND: dicon = "sms_dnd"; break; case STATUS_FFC: dicon = "sms_ffc"; break; } }else if ((strcmp(h.c_str(), "x-gadugadu") == 0) || (strcmp(h.c_str(), "gg") == 0)){ switch (status){ case STATUS_ONLINE: dicon = "GG_online"; break; case STATUS_OFFLINE: dicon = "GG_offline"; break; case STATUS_AWAY: dicon = "GG_away"; break; case STATUS_NA: dicon = "GG_na"; break; case STATUS_DND: dicon = "GG_dnd"; break; case STATUS_FFC: dicon = "GG_ffc"; break; } } } return dicon; } void JabberClient::contactInfo(void *_data, unsigned long &curStatus, unsigned &style, const char *&statusIcon, string *icons) { JabberUserData *data = (JabberUserData*)_data; const char *dicon = get_icon(data, data->Status.value, data->invisible.bValue); if (data->Status.value > curStatus){ curStatus = data->Status.value; if (statusIcon && icons){ string iconSave = *icons; *icons = statusIcon; if (iconSave.length()) addIcon(icons, iconSave.c_str(), statusIcon); } statusIcon = dicon; }else{ if (statusIcon){ addIcon(icons, dicon, statusIcon); }else{ statusIcon = dicon; } } for (unsigned i = 1; i <= data->nResources.value; i++){ const char *dicon = get_icon(data, atol(get_str(data->ResourceStatus, i)), false); addIcon(icons, dicon, statusIcon); } if (((data->Subscribe.value & SUBSCRIBE_TO) == 0) && !isAgent(data->ID.ptr)) style |= CONTACT_UNDERLINE; if (icons && data->IsTyping.bValue) addIcon(icons, "typing", statusIcon); } string JabberClient::buildId(JabberUserData *data) { string res = data->ID.ptr; int n = res.find('@'); if (n < 0){ res += "@"; string server; if (getUseVHost()) server = getVHost(); if (server.empty()) server = getServer(); res += server; } return res; } QWidget *JabberClient::searchWindow(QWidget *parent) { if (getState() != Connected) return NULL; return new JabberAdd(this, parent); } void JabberClient::ping() { if (getState() != Connected) return; m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "\n"; sendPacket(); QTimer::singleShot(PING_TIMEOUT * 1000, this, SLOT(ping())); } QString JabberClient::contactName(void *clientData) { QString res = Client::contactName(clientData); res += ": "; JabberUserData *data = (JabberUserData*)clientData; QString name; if (data->ID.ptr) name = QString::fromUtf8(data->ID.ptr); if (data->Nick.ptr && *data->Nick.ptr){ res += QString::fromUtf8(data->Nick.ptr); res += " ("; res += name; res += ")"; }else{ res += name; } return res; } QString JabberClient::contactTip(void *_data) { JabberUserData *data = (JabberUserData*)_data; QString res; if (data->nResources.value == 0){ res = "invisible.bValue); res += "\">"; res += i18n("Offline"); res += "
"; res += "ID: "; res += QString::fromUtf8(data->ID.ptr); if (data->Resource.ptr && *data->Resource.ptr){ res += "/"; res += QString::fromUtf8(data->Resource.ptr); } res += ""; if (data->StatusTime.value){ res += "
"; res += i18n("Last online"); res += ": "; res += formatDateTime(data->StatusTime.value); } const char *reply = data->AutoReply.ptr; if (reply && *reply){ res += "
"; QString r = QString::fromUtf8(reply); r = r.replace(QRegExp("\n"), "
"); res += r; } }else{ for (unsigned i = 1; i <= data->nResources.value; i++){ unsigned status = atol(get_str(data->ResourceStatus, i)); res += ""; QString statusText; for (const CommandDef *cmd = protocol()->statusList(); cmd->text; cmd++){ if (cmd->id == status){ statusText = i18n(cmd->text); res += statusText; break; } } res += "
ID: "; res += QString::fromUtf8(data->ID.ptr); QString resource = get_str(data->Resources, i); if (!resource.isEmpty()){ res += "/"; res += resource; } res += ""; unsigned onlineTime = atol(get_str(data->ResourceOnlineTime, i)); unsigned statusTime = atol(get_str(data->ResourceStatusTime, i)); if (onlineTime){ res += "
"; res += i18n("Online"); res += ": "; res += formatDateTime(onlineTime); } if (statusTime != onlineTime){ res += "
"; res += statusText; res += ": "; res += formatDateTime(statusTime); } QString clientName = get_str(data->ResourceClientName, i); QString clientVersion = get_str(data->ResourceClientVersion, i); QString clientOS = get_str(data->ResourceClientOS, i); if (!clientName.isEmpty()) { res += "
" + clientName + " " + clientVersion; if (!clientOS.isEmpty()) res += " / " + clientOS; } const char *reply = get_str(data->ResourceReply, i); if (reply && *reply){ res += "

"; QString r = QString::fromUtf8(reply); r = r.replace(QRegExp("\n"), "
"); res += r; } if (i < data->nResources.value) res += "
_________
"; } } if (data->LogoWidth.value && data->LogoHeight.value){ QImage img(logoFile(data)); if (!img.isNull()){ QPixmap pict; pict.convertFromImage(img); int w = pict.width(); int h = pict.height(); if (h > w){ if (h > 60){ w = w * 60 / h; h = 60; } }else{ if (w > 60){ h = h * 60 / w; w = 60; } } QString url="pict://jabber.logo."; url += data->ID.ptr; QMimeSourceFactory::defaultFactory()->setPixmap(static_cast(url.utf8()), pict); res += "
(url.utf8()); res += "\" width=\""; res += number(w).c_str(); res += "\" height=\""; res += number(h).c_str(); res += "\">"; } } if (data->PhotoWidth.value && data->PhotoHeight.value){ QImage img(photoFile(data)); if (!img.isNull()){ QPixmap pict; pict.convertFromImage(img); int w = pict.width(); int h = pict.height(); if (h > w){ if (h > 60){ w = w * 60 / h; h = 60; } }else{ if (w > 60){ h = h * 60 / w; w = 60; } } QString url="pict://jabber.photo."; url += data->ID.ptr; QMimeSourceFactory::defaultFactory()->setPixmap(static_cast(url.utf8()), pict); res += "
(url.utf8()); res += "\" width=\""; res += number(w).c_str(); res += "\" height=\""; res += number(h).c_str(); res += "\">"; } } return res; } void JabberClient::setOffline(JabberUserData *data) { data->Status.value = STATUS_OFFLINE; data->composeId.value = 0; clear_list(&data->Resources); clear_list(&data->ResourceReply); clear_list(&data->ResourceStatus); clear_list(&data->ResourceStatusTime); clear_list(&data->ResourceOnlineTime); data->nResources.value = 0; set_str(&data->TypingId.ptr, NULL); if (data->IsTyping.bValue){ data->IsTyping.bValue = false; Contact *contact; string resource; if (findContact(data->ID.ptr, NULL, false, contact, resource)){ Event e(EventContactStatus, contact); e.process(); } } } const unsigned MAIN_INFO = 1; const unsigned HOME_INFO = 2; const unsigned WORK_INFO = 3; const unsigned ABOUT_INFO = 4; const unsigned PHOTO_INFO = 5; const unsigned LOGO_INFO = 6; const unsigned NETWORK = 7; static CommandDef jabberWnd[] = { { MAIN_INFO, "", "Jabber_online", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { HOME_INFO, I18N_NOOP("Home info"), "home", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { WORK_INFO, I18N_NOOP("Work info"), "work", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { ABOUT_INFO, I18N_NOOP("About info"), "info", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { PHOTO_INFO, I18N_NOOP("Photo"), "pict", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { LOGO_INFO, I18N_NOOP("Logo"), "pict", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, }; static CommandDef cfgJabberWnd[] = { { MAIN_INFO, "", "Jabber_online", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { HOME_INFO, I18N_NOOP("Home info"), "home", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { WORK_INFO, I18N_NOOP("Work info"), "work", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { ABOUT_INFO, I18N_NOOP("About info"), "info", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { PHOTO_INFO, I18N_NOOP("Photo"), "pict", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { LOGO_INFO, I18N_NOOP("Logo"), "pict", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { NETWORK, I18N_NOOP("Network"), "network", NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL }, }; CommandDef *JabberClient::infoWindows(Contact*, void *_data) { JabberUserData *data = (JabberUserData*)_data; QString name = i18n(protocol()->description()->text); name += " "; name += QString::fromUtf8(data->ID.ptr); jabberWnd[0].text_wrk = strdup(name.utf8()); return jabberWnd; } CommandDef *JabberClient::configWindows() { QString title = QString::fromUtf8(name().c_str()); int n = title.find("."); if (n > 0) title = title.left(n) + " " + title.mid(n + 1); cfgJabberWnd[0].text_wrk = strdup(title.utf8()); return cfgJabberWnd; } QWidget *JabberClient::infoWindow(QWidget *parent, Contact*, void *_data, unsigned id) { JabberUserData *data = (JabberUserData*)_data; switch (id){ case MAIN_INFO: return new JabberInfo(parent, data, this); case HOME_INFO: return new InfoProxy(parent, new JabberHomeInfo(parent, data, this), i18n("Home info")); case WORK_INFO: return new InfoProxy(parent, new JabberWorkInfo(parent, data, this), i18n("Work info")); case ABOUT_INFO: return new InfoProxy(parent, new JabberAboutInfo(parent, data, this), i18n("About info")); case PHOTO_INFO: return new JabberPicture(parent, data, this, true); case LOGO_INFO: return new JabberPicture(parent, data, this, false); } return NULL; } QWidget *JabberClient::configWindow(QWidget *parent, unsigned id) { switch (id){ case MAIN_INFO: return new JabberInfo(parent, NULL, this); case HOME_INFO: return new InfoProxy(parent, new JabberHomeInfo(parent, NULL, this), i18n("Home info")); case WORK_INFO: return new InfoProxy(parent, new JabberWorkInfo(parent, NULL, this), i18n("Work info")); case ABOUT_INFO: return new InfoProxy(parent, new JabberAboutInfo(parent, NULL, this), i18n("About info")); case PHOTO_INFO: return new JabberPicture(parent, NULL, this, true); case LOGO_INFO: return new JabberPicture(parent, NULL, this, false); case NETWORK: return new JabberConfig(parent, this, true); } return NULL; } void JabberClient::updateInfo(Contact *contact, void *data) { if (getState() != Connected){ Client::updateInfo(contact, data); return; } if (data == NULL) data = &this->data.owner; info_request((JabberUserData*)data, false); } string JabberClient::resources(void *_data) { JabberUserData *data = (JabberUserData*)_data; string resource; if (data->nResources.value > 1){ for (unsigned i = 1; i <= data->nResources.value; i++){ if (!resource.empty()) resource += ";"; const char *dicon = get_icon(data, atol(get_str(data->ResourceStatus, i)), false); resource += number((unsigned long)dicon); resource += ","; resource += quoteChars(get_str(data->Resources, i), ";"); } } return resource; } bool JabberClient::canSend(unsigned type, void *_data) { if ((_data == NULL) || (((clientData*)_data)->Sign.value != JABBER_SIGN)) return false; if (getState() != Connected) return false; JabberUserData *data = (JabberUserData*)_data; switch (type){ case MessageGeneric: case MessageFile: case MessageContacts: case MessageUrl: return true; case MessageAuthRequest: return ((data->Subscribe.value & SUBSCRIBE_TO) == 0); case MessageAuthGranted: return ((data->Subscribe.value & SUBSCRIBE_FROM) == 0); case MessageAuthRefused: return (data->Subscribe.value & SUBSCRIBE_FROM); case MessageJabberOnline: return isAgent(data->ID.ptr) && (data->Status.value == STATUS_OFFLINE); case MessageJabberOffline: return isAgent(data->ID.ptr) && (data->Status.value != STATUS_OFFLINE); } return false; } class JabberImageParser : public HTMLParser { public: JabberImageParser(unsigned bgColor); QString parse(const QString &text); protected: virtual void text(const QString &text); virtual void tag_start(const QString &tag, const list &attrs); virtual void tag_end(const QString &tag); void startBody(const list &attrs); void endBody(); QString res; bool m_bPara; bool m_bBody; unsigned m_bgColor; }; JabberImageParser::JabberImageParser(unsigned bgColor) { m_bPara = false; m_bBody = true; m_bgColor = bgColor; } QString JabberImageParser::parse(const QString &text) { list attrs; startBody(attrs); HTMLParser::parse(text); endBody(); return res; } void JabberImageParser::text(const QString &text) { if (m_bBody) res += quoteString(text); } static const char *_tags[] = { "abbr", "acronym", "address", "blockquote", "cite", "code", "dfn", "div", "em", "h1", "h2", "h3", "h4", "h5", "h6", "kbd", "p", "pre", "q", "samp", "span", "strong", "var", "a", "dl", "dt", "dd", "ol", "ul", "li", NULL }; static const char *_styles[] = { "color", "background-color", "font-family", "font-size", "font-style", "font-weight", "text-align", "text-decoration", NULL }; void JabberImageParser::startBody(const list &attrs) { m_bBody = true; res = ""; list newStyles; list::const_iterator it; for (it = attrs.begin(); it != attrs.end(); ++it){ QString name = *it; ++it; QString value = *it; if (name == "style"){ list styles = parseStyle(value); for (list::iterator it = styles.begin(); it != styles.end(); ++it){ QString name = *it; ++it; QString value = *it; for (const char **s = _styles; *s; s++){ if (name == *s){ newStyles.push_back(name); newStyles.push_back(value); break; } } } } } for (it = newStyles.begin(); it != newStyles.end(); ++it){ QString name = *it; ++it; if (name == "background-color") break; } if (it == newStyles.end()){ char b[15]; sprintf(b, "#%06X", m_bgColor & 0xFFFFFF); newStyles.push_back("background-color"); newStyles.push_back(b); } res += ""; } void JabberImageParser::endBody() { if (m_bBody){ res += ""; m_bBody = false; } } void JabberImageParser::tag_start(const QString &tag, const list &attrs) { if (tag == "html"){ m_bBody = false; res = ""; return; } if (tag == "body"){ startBody(attrs); return; } if (!m_bBody) return; if (tag == "img"){ QString src; QString alt; for (list::const_iterator it = attrs.begin(); it != attrs.end(); ++it){ QString name = *it; ++it; QString value = *it; if (name == "src") src = value; if (name == "alt") alt = value; } if (!alt.isEmpty()){ res += unquoteString(alt); return; } if (src.left(5) == "icon:"){ list smiles = getIcons()->getSmile(src.mid(5).latin1()); if (!smiles.empty()){ res += QString::fromUtf8(smiles.front().c_str()); return; } } text(alt); return; } if (tag == "p"){ if (m_bPara){ res += "
"; m_bPara = false; } return; } if (tag == "br"){ res += "
"; return; } for (const char **t = _tags; *t; t++){ if (tag == *t){ res += "<"; res += tag; for (list::const_iterator it = attrs.begin(); it != attrs.end(); ++it){ QString name = *it; ++it; QString value = *it; if (name == "style"){ list styles = parseStyle(value); list newStyles; for (list::iterator it = styles.begin(); it != styles.end(); ++it){ QString name = *it; ++it; QString value = *it; for (const char **s = _styles; *s; s++){ if (name == *s){ newStyles.push_back(name); newStyles.push_back(value); break; } } } value = makeStyle(newStyles); } if ((name != "style") && (name != "href")) continue; res += " "; res += name; if (!value.isEmpty()){ res += "=\'"; res += quoteString(value); res += "\'"; } } res += ">"; return; } } if (tag == "b"){ res += ""; return; } if (tag == "i"){ res += ""; return; } if (tag == "u"){ res += ""; return; } if (tag == "font"){ res += "::const_iterator it = attrs.begin(); it != attrs.end(); ++it){ QString name = *it; ++it; QString value = *it; if (name == "color"){ if (!style.empty()) style += ";"; style += "color: "; style += value.utf8(); continue; } } if (!style.empty()){ res += " style=\'"; res += style.c_str(); res += "\'"; } res += ">"; return; } return; } void JabberImageParser::tag_end(const QString &tag) { if (tag == "body"){ endBody(); return; } if (!m_bBody) return; if (tag == "p"){ m_bPara = true; return; } for (const char **t = _tags; *t; t++){ if (tag == *t){ res += ""; return; } } if ((tag == "b") || (tag == "i") || (tag == "u") || (tag == "font")){ res += ""; return; } } static QString removeImages(const QString &text, unsigned bgColor) { JabberImageParser p(bgColor); return p.parse(text); } bool JabberClient::send(Message *msg, void *_data) { if ((getState() != Connected) || (_data == NULL)) return false; JabberUserData *data = (JabberUserData*)_data; switch (msg->type()){ case MessageAuthRefused:{ string grp; Group *group = NULL; Contact *contact = getContacts()->contact(msg->contact()); if (contact && contact->getGroup()) group = getContacts()->group(contact->getGroup()); if (group) grp = group->getName().utf8(); listRequest(data, data->Name.ptr, grp.c_str(), false); if (data->Subscribe.value & SUBSCRIBE_FROM){ m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr; m_socket->writeBuffer << "\' type=\'unsubscribed\'>" << (const char*)(quoteString(msg->getPlainText(), quoteNOBR).utf8()) << ""; sendPacket(); if ((msg->getFlags() & MESSAGE_NOHISTORY) == 0){ msg->setClient(dataName(data).c_str()); Event e(EventSent, msg); e.process(); } Event e(EventMessageSent, msg); e.process(); delete msg; return true; } } case MessageGeneric:{ Contact *contact = getContacts()->contact(msg->contact()); if ((contact == NULL) || (data == NULL)) return false; string text; text = msg->getPlainText().utf8(); messageSend ms; ms.msg = msg; ms.text = &text; Event eSend(EventSend, &ms); eSend.process(); m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr; if (!msg->getResource().isEmpty()){ m_socket->writeBuffer << "/" << msg->getResource().utf8(); } if (getTyping()){ data->composeId.value = ++m_msg_id; string msg_id = "msg"; msg_id += number(data->composeId.value); m_socket->writeBuffer <<"\' id=\'"<writeBuffer << "\'>" << (const char*)(quote_nbsp(quoteString(QString::fromUtf8(text.c_str()), quoteNOBR)).utf8()) << ""; if (data->richText.bValue && getRichText() && (msg->getFlags() & MESSAGE_RICHTEXT)){ m_socket->writeBuffer << "" << quote_nbsp(removeImages(msg->getRichText(), msg->getBackground())).utf8() << ""; } if (getTyping()){ m_socket->writeBuffer <<"" <<"" <<""; } m_socket->writeBuffer << ""; sendPacket(); if ((msg->getFlags() & MESSAGE_NOHISTORY) == 0){ if (data->richText.bValue){ msg->setClient(dataName(data).c_str()); Event e(EventSent, msg); e.process(); }else{ Message m(MessageGeneric); m.setContact(msg->contact()); m.setClient(dataName(data).c_str()); m.setText(msg->getPlainText()); Event e(EventSent, msg); e.process(); } } Event e(EventMessageSent, msg); e.process(); delete msg; return true; } case MessageUrl:{ Contact *contact = getContacts()->contact(msg->contact()); if ((contact == NULL) || (data == NULL)) return false; UrlMessage *m = static_cast(msg); m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr; if (!msg->getResource().isEmpty()){ m_socket->writeBuffer << "/" << msg->getResource().utf8(); } m_socket->writeBuffer << "\'>" << (const char*)(quoteString(m->getUrl(), quoteNOBR).utf8()); QString t = m->getPlainText(); if (!t.isEmpty()){ m_socket->writeBuffer << "\n" << (const char*)(quoteString(m->getPlainText(), quoteNOBR).utf8()); } m_socket->writeBuffer << ""; if (data->richText.bValue && getRichText()){ m_socket->writeBuffer << "" << "getUrl(), quoteNOBR).utf8()) << "\'>" << (const char*)(quoteString(m->getUrl(), quoteNOBR).utf8()) << ""; if (!t.isEmpty()){ m_socket->writeBuffer << "
" << removeImages(msg->getRichText(), msg->getBackground()).utf8(); } m_socket->writeBuffer << ""; } m_socket->writeBuffer << "
"; sendPacket(); if ((msg->getFlags() & MESSAGE_NOHISTORY) == 0){ if (data->richText.bValue){ msg->setClient(dataName(data).c_str()); Event e(EventSent, msg); e.process(); }else{ Message m(MessageGeneric); m.setContact(msg->contact()); m.setClient(dataName(data).c_str()); m.setText(msg->getPlainText()); Event e(EventSent, msg); e.process(); } } Event e(EventMessageSent, msg); e.process(); delete msg; return true; } case MessageContacts:{ Contact *contact = getContacts()->contact(msg->contact()); if ((contact == NULL) || (data == NULL)) return false; ContactsMessage *m = static_cast(msg); list jids; list names; QString contacts = m->getContacts(); QString nc; while (!contacts.isEmpty()){ QString item = getToken(contacts, ';'); QString url = getToken(item, ','); QString proto = getToken(url, ':'); if (proto == "sim"){ Contact *contact = getContacts()->contact(atol(url.latin1())); if (contact){ clientData *data; ClientDataIterator it(contact->clientData); while ((data = ++it) != NULL){ Contact *c = contact; if (!isMyData(data, c)) continue; JabberUserData *d = (JabberUserData*)data; jids.push_back(d->ID.ptr); string n; n = c->getName().utf8(); names.push_back(n); if (!nc.isEmpty()) nc += ";"; nc += "jabber:"; nc += d->ID.ptr; nc += ","; if (c->getName() == d->ID.ptr){ nc += d->ID.ptr; }else{ nc += c->getName(); nc += " ("; nc += d->ID.ptr; nc += ")"; } } } } } if (jids.empty()){ msg->setError(I18N_NOOP("No contacts for send")); Event e(EventMessageSent, msg); e.process(); delete msg; return true; } m->setContacts(nc); m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr; if (!msg->getResource().isEmpty()){ m_socket->writeBuffer << "/" << msg->getResource().utf8(); } m_socket->writeBuffer << "\'>"; list::iterator iti = jids.begin(); list::iterator itn = names.begin(); for (; iti != jids.end(); ++iti, ++itn){ m_socket->writeBuffer << ""; } m_socket->writeBuffer << ""; iti = jids.begin(); for (; iti != jids.end(); ++iti, ++itn){ m_socket->writeBuffer << (const char*)(quoteString(QString::fromUtf8((*iti).c_str()), quoteNOBR).utf8()) << "\n"; } m_socket->writeBuffer << ""; if (data->richText.bValue && getRichText()){ m_socket->writeBuffer << ""; iti = jids.begin(); for (; iti != jids.end(); ++iti, ++itn){ m_socket->writeBuffer << (const char*)(quoteString(QString::fromUtf8((*iti).c_str()), quoteNOBR).utf8()) << "
\n"; } m_socket->writeBuffer << ""; } m_socket->writeBuffer << "
"; sendPacket(); if ((msg->getFlags() & MESSAGE_NOHISTORY) == 0){ if (data->richText.bValue){ msg->setClient(dataName(data).c_str()); Event e(EventSent, msg); e.process(); }else{ Message m(MessageGeneric); m.setContact(msg->contact()); m.setClient(dataName(data).c_str()); m.setText(msg->getPlainText()); Event e(EventSent, msg); e.process(); } } Event e(EventMessageSent, msg); e.process(); delete msg; return true; } case MessageFile:{ m_waitMsg.push_back(msg); JabberFileTransfer *ft = static_cast(static_cast(msg)->m_transfer); if (ft == NULL) ft = new JabberFileTransfer(static_cast(msg), data, this); ft->listen(); return true; } case MessageAuthRequest:{ m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr; m_socket->writeBuffer << "\' type=\'subscribe\'>" << (const char*)(quoteString(msg->getPlainText(), quoteNOBR).utf8()) << ""; sendPacket(); if ((msg->getFlags() & MESSAGE_NOHISTORY) == 0){ msg->setClient(dataName(data).c_str()); Event e(EventSent, msg); e.process(); } Event e(EventMessageSent, msg); e.process(); delete msg; return true; } case MessageAuthGranted:{ m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr; m_socket->writeBuffer << "\' type=\'subscribed\'>"; sendPacket(); if ((msg->getFlags() & MESSAGE_NOHISTORY) == 0){ msg->setClient(dataName(data).c_str()); Event e(EventSent, msg); e.process(); } Event e(EventMessageSent, msg); e.process(); delete msg; return true; } case MessageJabberOnline: if (isAgent(data->ID.ptr) && (data->Status.value == STATUS_OFFLINE)){ m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr << "\'>"; sendPacket(); delete msg; return true; } break; case MessageJabberOffline: if (isAgent(data->ID.ptr) && (data->Status.value != STATUS_OFFLINE)){ m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr << "\' type=\'unavailable\'>"; sendPacket(); delete msg; return true; } break; case MessageTypingStart: if (getTyping()){ m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr << "\'>" //<< msg_id.c_str() << (data->TypingId.ptr ? data->TypingId.ptr : "") << ""; sendPacket(); delete msg; return true; } break; case MessageTypingStop: if (getTyping()){ m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr << "\'>" << (data->TypingId.ptr ? data->TypingId.ptr : "")//msg_id.c_str() << ""; sendPacket(); delete msg; return true; } break; } return false; } string JabberClient::dataName(void *_data) { QString res(name().c_str()); JabberUserData *data = (JabberUserData*)_data; res += "+"; res += data->ID.ptr; res = res.replace(QRegExp("/"), "_"); return string(res.utf8()); } void JabberClient::listRequest(JabberUserData *data, const char *name, const char *grp, bool bDelete) { string jid = data->ID.ptr; list::iterator it; for (it = m_listRequests.begin(); it != m_listRequests.end(); ++it){ if (jid == (*it).jid){ m_listRequests.erase(it); break; } } JabberListRequest lr; lr.jid = jid; if (name) lr.name = name; if (grp) lr.grp = grp; lr.bDelete = bDelete; m_listRequests.push_back(lr); processList(); } JabberListRequest *JabberClient::findRequest(const char *jid, bool bRemove) { list::iterator it; for (it = m_listRequests.begin(); it != m_listRequests.end(); ++it){ if ((*it).jid == jid){ if (bRemove){ m_listRequests.erase(it); return NULL; } return &(*it); } } return NULL; } bool JabberClient::isAgent(const char *jid) { if ((QString::fromUtf8(jid)).find("@")==-1) return true; return false; } class JabberClient::JabberAuthMessage : public AuthMessage { public: JabberAuthMessage(std::vector &tempMessages, unsigned type, Buffer *cfg=NULL) : AuthMessage(type, cfg) , tempMessages(tempMessages) { tempMessages.push_back(this); } virtual ~JabberAuthMessage() { remove(tempMessages, this); } static bool remove(std::vector&messages, JabberAuthMessage *value) { std::vector::iterator it = find(messages.begin(), messages.end(), value); if (it != messages.end()) { messages.erase(it); return true; } return false; } private: std::vector &tempMessages; }; void JabberClient::auth_request(const char *jid, unsigned type, const char *text, bool bCreate) { Contact *contact; string resource; JabberUserData *data = findContact(jid, NULL, false, contact, resource); if (isAgent(jid) || ((type == MessageAuthRequest) && getAutoAccept())){ switch (type){ case MessageAuthRequest:{ if (data == NULL) data = findContact(jid, NULL, true, contact, resource); m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr << "\' type=\'subscribed\'>"; sendPacket(); m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << "ID.ptr << "\' type=\'subscribe\'>" << ""; sendPacket(); Event e(EventContactChanged, contact); e.process(); return; } case MessageAuthGranted:{ if (data == NULL) data = findContact(jid, NULL, true, contact, resource); data->Subscribe.value |= SUBSCRIBE_TO; Event e(EventContactChanged, contact); e.process(); return; } } } if ((data == NULL) && bCreate){ data = findContact(jid, NULL, true, contact, resource); contact->setFlags(CONTACT_TEMP); } if (data == NULL) return; if (((type == MessageAuthGranted) || (type ==MessageAuthRefused)) && (contact->getFlags() & CONTACT_TEMP)){ contact->setFlags(contact->getFlags() & ~CONTACT_TEMP); Event e(EventContactChanged, contact); e.process(); return; } //FIXME: In EventMessageReceived handler from filter plugin, there is conditional delete(msg) //that causes crash when filter plugin configured to block Authorisation messages. //So parameter must be pointer to object from heap, but then I don't know how it can be safely //feed if it wasn't in EventMessageReceived. serzh. //12.june.2006 zowers: FIXED. JabberAuthMessage *msg = new JabberAuthMessage(tempAuthMessages, type); msg->setContact(contact->id()); msg->setClient(dataName(data).c_str()); msg->setFlags(MESSAGE_RECEIVED); if (text) msg->setText(unquoteString(QString::fromUtf8(text))); Event e(EventMessageReceived, msg); e.process(); if (JabberAuthMessage::remove(tempAuthMessages, msg)) { delete msg; } if (type == MessageAuthGranted) { data->Subscribe.value |= SUBSCRIBE_TO; Event e(EventContactChanged, contact); e.process(); } else if (type == MessageAuthRefused) { data->Subscribe.value &= ~SUBSCRIBE_TO; Event e(EventContactChanged, contact); e.process(); } } void JabberClient::setInvisible(bool bState) { if (getInvisible() == bState) return; TCPClient::setInvisible(bState); if (getStatus() == STATUS_OFFLINE) return; unsigned status = getStatus(); m_status = STATUS_OFFLINE; if (getInvisible()){ setStatus(status, NULL); return; } setStatus(status); } string JabberClient::VHost() { if (data.UseVHost.bValue && data.VHost.ptr && *data.VHost.ptr) return data.VHost.ptr; return data.Server.ptr; } JabberFileTransfer::JabberFileTransfer(FileMessage *msg, JabberUserData *data, JabberClient *client) : FileTransfer(msg) { m_data = data; m_client = client; m_state = None; m_socket = new ClientSocket(this); m_startPos = 0; m_endPos = 0xFFFFFFFF; } JabberFileTransfer::~JabberFileTransfer() { for (list::iterator it = m_client->m_waitMsg.begin(); it != m_client->m_waitMsg.end(); ++it){ if ((*it) == m_msg){ m_client->m_waitMsg.erase(it); break; } } if (m_socket) delete m_socket; } void JabberFileTransfer::listen() { if (m_file == NULL){ for (;;){ if (!openFile()){ if (FileTransfer::m_state == FileTransfer::Done) m_socket->error_state(""); return; } if (!isDirectory()) break; } } bind(m_client->getMinPort(), m_client->getMaxPort(), m_client); } void JabberFileTransfer::startReceive(unsigned pos) { m_startPos = pos; JabberFileMessage *msg = static_cast(m_msg); m_socket->connect(msg->getHost(), msg->getPort(), m_client); m_state = Connect; FileTransfer::m_state = FileTransfer::Connect; if (m_notify) m_notify->process(); } void JabberFileTransfer::bind_ready(unsigned short port) { if (m_state == None){ m_state = Listen; }else{ m_state = ListenWait; FileTransfer::m_state = FileTransfer::Listen; if (m_notify) m_notify->process(); } QString fname = m_file->name(); fname = fname.replace(QRegExp("\\\\"), "/"); int n = fname.findRev('/'); if (n >= 0) fname = fname.mid(n + 1); m_url = fname.utf8(); m_client->sendFileRequest(m_msg, port, m_data, m_url.c_str(), m_fileSize); } bool JabberFileTransfer::error(const char *err) { error_state(err, 0); return true; } bool JabberFileTransfer::accept(Socket *s, unsigned long) { if (m_state == Listen){ Event e(EventMessageAcked, m_msg); e.process(); m_state = ListenWait; } log(L_DEBUG, "Accept connection"); m_startPos = 0; m_endPos = 0xFFFFFFFF; m_socket->setSocket(s); m_socket->readBuffer.init(0); m_socket->readBuffer.packetStart(); m_socket->setRaw(true); m_answer = 400; return true; } bool JabberFileTransfer::error_state(const char *err, unsigned) { if (m_state == Wait) return false; if (FileTransfer::m_state != FileTransfer::Done){ m_state = None; FileTransfer::m_state = FileTransfer::Error; m_msg->setError(err); } m_msg->m_transfer = NULL; m_msg->setFlags(m_msg->getFlags() & ~MESSAGE_TEMP); Event e(EventMessageSent, m_msg); e.process(); return true; } void JabberFileTransfer::packet_ready() { if (m_socket->readBuffer.writePos() == 0) return; if (m_state != Receive){ JabberPlugin *plugin = static_cast(m_client->protocol()->plugin()); log_packet(m_socket->readBuffer, false, plugin->JabberPacket); for (;;){ string s; if (!m_socket->readBuffer.scan("\n", s)) break; if (!s.empty() && (s[s.length() - 1] == '\r')) s = s.substr(0, s.length() - 1); if (!get_line(s.c_str())) break; } } if (m_state == Receive){ if (m_file == NULL){ m_socket->error_state("", 0); return; } unsigned size = m_socket->readBuffer.size() - m_socket->readBuffer.readPos(); if (size > m_endPos - m_startPos) size = m_endPos - m_startPos; if (size){ m_file->writeBlock(m_socket->readBuffer.data(m_socket->readBuffer.readPos()), size); m_bytes += size; m_totalBytes += size; m_startPos += size; m_transferBytes += size; if (m_startPos == m_endPos){ FileTransfer::m_state = FileTransfer::Done; if (m_notify){ m_notify->transfer(false); m_notify->process(); } m_socket->error_state(""); } if (m_notify) m_notify->process(); } } if (m_socket->readBuffer.readPos() == m_socket->readBuffer.writePos()) m_socket->readBuffer.init(0); } void JabberFileTransfer::connect_ready() { JabberFileMessage *msg = static_cast(m_msg); string line; line = "GET /"; line += msg->getDescription().utf8(); line += " HTTP/1.1\r\n" "Host :"; line += msg->getHost(); line += "\r\n"; if (m_startPos){ line += "Range: "; line += number(m_startPos); line += "-\r\n"; } m_startPos = 0; m_endPos = 0xFFFFFFFF; send_line(line.c_str()); FileTransfer::m_state = FileTransfer::Negotiation; m_socket->readBuffer.init(0); m_socket->readBuffer.packetStart(); m_socket->setRaw(true); } void JabberFileTransfer::write_ready() { if (m_state != Send){ ClientSocketNotify::write_ready(); return; } if (m_transfer){ m_transferBytes += m_transfer; m_transfer = 0; if (m_notify) m_notify->process(); } if (m_startPos >= m_endPos){ if (m_notify) m_notify->transfer(false); m_bytes += m_file->size() - m_endPos; m_totalBytes += m_file->size() - m_endPos; for (;;){ if (!openFile()){ m_state = None; if (FileTransfer::m_state == FileTransfer::Done) m_socket->error_state(""); break; } if (isDirectory()) continue; m_state = Wait; FileTransfer::m_state = FileTransfer::Wait; if (!((Client*)m_client)->send(m_msg, m_data)) error_state(I18N_NOOP("File transfer failed"), 0); break; } if (m_notify) m_notify->process(); m_socket->close(); return; } time_t now = time(NULL); if ((unsigned)now != m_sendTime){ m_sendTime = now; m_sendSize = 0; } if (m_sendSize > (m_speed << 18)){ m_socket->pause(1); return; } char buf[2048]; unsigned tail = sizeof(buf); if (tail > m_endPos - m_startPos) tail = m_endPos - m_startPos; int readn = m_file->readBlock(buf, tail); if (readn <= 0){ m_socket->error_state("Read file error"); return; } m_startPos += readn; m_transfer = readn; m_bytes += readn; m_totalBytes += readn; m_sendSize += readn; m_socket->writeBuffer.pack(buf, readn); m_socket->write(); } bool JabberFileTransfer::get_line(const char *str) { string line = str; if (line.empty()){ if (m_state == Connect){ m_socket->error_state(I18N_NOOP("File transfer failed")); return true; } if (m_state == ReadHeader){ if (m_endPos < m_startPos) m_endPos = m_startPos; if (m_file) m_file->at(m_startPos); m_state = Receive; FileTransfer::m_state = FileTransfer::Read; m_bytes += m_startPos; m_totalBytes += m_startPos; m_fileSize = m_endPos; m_totalSize = m_endPos; if (m_notify){ m_notify->process(); m_notify->transfer(true); } return true; } if (m_file->size() < m_endPos) m_endPos = m_file->size(); if (m_startPos > m_endPos) m_startPos = m_endPos; if ((m_answer == 200) && (m_startPos == m_endPos)) m_answer = 204; if ((m_answer == 200) && ((m_startPos != 0) || (m_endPos < m_file->size()))) m_answer = 206; string s; s = "HTTP/1.0 "; s += number(m_answer); switch (m_answer){ case 200: s += " OK"; break; case 204: s += " No content"; break; case 206: s += " Partial content"; break; case 400: s += " Bad request"; break; case 404: s += " Not found"; break; default: s += " Error"; } send_line(s.c_str()); if ((m_answer == 200) || (m_answer == 206)){ send_line("Content-Type: application/data"); s = "Content-Length: "; s += number(m_endPos - m_startPos); send_line(s.c_str()); } if (m_answer == 206){ s = "Range: "; s += number(m_startPos); s += "-"; s += number(m_endPos); send_line(s.c_str()); } send_line(""); if (m_answer < 300){ m_file->at(m_startPos); FileTransfer::m_state = FileTransfer::Write; m_state = Send; m_bytes = m_startPos; m_totalBytes += m_startPos; if (m_notify){ m_notify->process(); m_notify->transfer(true); } write_ready(); }else{ m_socket->error_state("Bad request"); } return false; } if (m_state == ListenWait){ string t = getToken(line, ' '); if (t == "GET"){ m_answer = 404; t = getToken(line, ' '); if (t[0] == '/'){ if (m_url == (t.c_str() + 1)) m_answer = 200; } } m_state = Header; return true; } if (m_state == Connect){ string t = getToken(line, ' '); t = getToken(t, '/'); if (t != "HTTP"){ m_socket->error_state(I18N_NOOP("File transfer fail")); return true; } unsigned code = atol(getToken(line, ' ').c_str()); switch (code){ case 200: case 206: m_startPos = 0; m_endPos = 0xFFFFFFFF; break; case 204: m_startPos = 0; m_endPos = 0; break; } m_state = ReadHeader; return true; } if (m_state == ReadHeader){ string t = getToken(line, ':'); if (t == "Content-Length"){ const char *p; for (p = line.c_str(); *p; p++) if ((*p > '0') && (*p < '9')) break; m_endPos = m_startPos + strtoul(p, NULL, 10); } if (t == "Range"){ const char *p; for (p = line.c_str(); *p; p++) if ((*p > '0') && (*p < '9')) break; m_startPos = strtoul(p, NULL, 10); for (; *p; p++) if (*p == '-'){ ++p; break; } if ((*p > '0') && (*p < '9')) m_endPos = m_startPos + strtoul(p, NULL, 10); } return true; } string t = getToken(line, ':'); if (t == "Range"){ const char *p = line.c_str(); for (; *p; p++) if (*p != ' ') break; m_startPos = strtoul(p, NULL, 10); for (; *p; p++) if (*p == '-'){ p++; break; } if ((*p >= '0') && (*p <= '9')) m_endPos = strtoul(p, NULL, 10); } return true; } void JabberFileTransfer::send_line(const char *line) { m_socket->writeBuffer.packetStart(); m_socket->writeBuffer << line; m_socket->writeBuffer << "\r\n"; JabberPlugin *plugin = static_cast(m_client->protocol()->plugin()); log_packet(m_socket->writeBuffer, true, plugin->JabberPacket); m_socket->write(); } void JabberFileTransfer::connect() { m_nFiles = 1; if (static_cast(m_msg)->getPort() == 0) m_client->sendFileAccept(m_msg, m_data); if (m_notify) m_notify->createFile(m_msg->getDescription(), 0xFFFFFFFF, false); } #ifdef WIN32 static char PICT_PATH[] = "pictures\\"; #else static char PICT_PATH[] = "pictures/"; #endif QString JabberClient::photoFile(JabberUserData *data) { string f = PICT_PATH; f += "photo."; f += data->ID.ptr; f = user_file(f.c_str()); return QFile::decodeName(f.c_str()); } QString JabberClient::logoFile(JabberUserData *data) { string f = PICT_PATH; f += "logo."; f += data->ID.ptr; f = user_file(f.c_str()); return QFile::decodeName(f.c_str()); } #ifndef _MSC_VER #include "jabberclient.moc" #endif