/* * Copyright 2010, Pier Luigi Fiorini. All rights reserved. * Copyright 2021, Jaidyn Levesque. All rights reserved. * Distributed under the terms of the GPL v2 License. * * Authors: * Pier Luigi Fiorini, pierluigi.fiorini@gmail.com * Jaidyn Levesque, jadedctrl@teknik.io */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "JabberHandler.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler" static status_t connect_thread(void* data) { JabberHandler* handler = (JabberHandler*)data; if (!handler) return B_BAD_VALUE; gloox::Client* client = handler->Client(); if (!client) return B_BAD_VALUE; gloox::ConnectionError e; while ((e = client->recv(10000000)) == gloox::ConnNoError); if (e != gloox::ConnUserDisconnected) handler->HandleConnectionError(e); return B_OK; } JabberHandler::JabberHandler() : fClient(NULL), fVCardManager(NULL), fSession(NULL), fLastOwnVCard(0) { fAvatars = new BList(); } JabberHandler::~JabberHandler() { Shutdown(); BString* item = NULL; for (int32 i = 0; (item = (BString*)fAvatars->ItemAt(i)); i++) delete item; delete fAvatars; } status_t JabberHandler::Init(ChatProtocolMessengerInterface* messenger) { fServerMessenger = messenger; return B_OK; } status_t JabberHandler::Process(BMessage* msg) { if (msg->what != IM_MESSAGE) return B_ERROR; int32 im_what = 0; msg->FindInt32("im_what", &im_what); switch (im_what) { case IM_SET_OWN_STATUS: { int32 status = msg->FindInt32("status"); BString status_msg = msg->FindString("message"); switch (status) { case STATUS_ONLINE: // Log in if we still need to resume_thread(fRecvThread); break; case STATUS_OFFLINE: kill_thread(fRecvThread); break; default: break; } break; } case IM_SET_OWN_NICKNAME: { const char* nick = msg->FindString("user_name"); if (nick != NULL) for (int i = 0; i < fRooms.CountItems(); i++) fRooms.ValueAt(i)->setNick(nick); break; } case IM_SEND_MESSAGE: { const char* id = msg->FindString("chat_id"); const char* subject = msg->FindString("subject"); const char* body = msg->FindString("body"); gloox::MUCRoom* room = fRooms.ValueFor(id); if (!id || !body) return B_ERROR; // Send JabberHandler message gloox::Message jm(gloox::Message::Chat, gloox::JID(id), body, (subject ? subject : gloox::EmptyString)); if (room != NULL) { room->send(body); break; } else fClient->send(jm); // If non-MUC, tell Caya we actually sent the message // (An MUC should echo the message back to us later, see // handleMUCMessage) _MessageSent(id, subject, body); break; } case IM_CREATE_CHAT: { const char* user_id = msg->FindString("user_id"); // TODO: Contact validation, make sure permssion is granted if (!user_id) return B_ERROR; _EnsureUserChat(user_id); _ChatCreatedMsg(user_id); break; } case IM_CREATE_ROOM: { BString chat_id; if (msg->FindString("chat_id", &chat_id) != B_OK) break; _JoinRoom(chat_id); break; } case IM_JOIN_ROOM: { BString chat_id; if (msg->FindString("chat_id", &chat_id) == B_OK) _JoinRoom(chat_id.String()); break; } case IM_LEAVE_ROOM: { BString chat_id = msg->FindString("chat_id"); gloox::MUCRoom* room = fRooms.ValueFor(chat_id); // MUCs are special, one-on-ones we can just drop if (room != NULL) room->leave(); // We've gotta let Caya know! BMessage left(IM_MESSAGE); left.AddInt32("im_what", IM_ROOM_LEFT); left.AddString("chat_id", chat_id); _SendMessage(&left); break; } case IM_ROOM_INVITE_ACCEPT: { BString chat_id; if (msg->FindString("chat_id", &chat_id) != B_OK) break; BStringList splitAtPassword; chat_id.Split("#", false, splitAtPassword); chat_id = splitAtPassword.StringAt(0); _JoinRoom(chat_id.String()); break; } case IM_ROOM_SEND_INVITE: { BString chat_id = msg->FindString("chat_id"); gloox::MUCRoom* room = fRooms.ValueFor(chat_id); BString user_id; if (room == NULL || msg->FindString("user_id", &user_id) != B_OK) break; room->invite(gloox::JID(user_id.String()), ""); break; } case IM_GET_ROOM_PARTICIPANTS: { BString chat_id = msg->FindString("chat_id"); gloox::MUCRoom* room = fRooms.ValueFor(chat_id); if (room != NULL) room->getRoomItems(); else if (fUserChats.HasString(chat_id) == true) { BMessage users(IM_MESSAGE); users.AddInt32("im_what", IM_ROOM_PARTICIPANTS); users.AddString("user_id", chat_id); } break; } case IM_GET_ROOM_METADATA: { BString chat_id = msg->FindString("chat_id"); gloox::MUCRoom* room = fRooms.ValueFor(chat_id); if (room != NULL) room->getRoomInfo(); if (fUserChats.HasString(chat_id) == true) { BMessage metadata(IM_MESSAGE); metadata.AddInt32("im_what", IM_ROOM_METADATA); metadata.AddString("chat_id", chat_id); metadata.AddInt32("room_default_flags", 0 | ROOM_LOG_LOCALLY | ROOM_POPULATE_LOGS | ROOM_NOTIFY_DM); metadata.AddInt32("room_disallowed_flags", 0 | ROOM_AUTOJOIN | ROOM_AUTOCREATE); _SendMessage(&metadata); } break; } case IM_SET_ROOM_SUBJECT: { BString chat_id = msg->FindString("chat_id"); gloox::MUCRoom* room = fRooms.ValueFor(chat_id); if (room != NULL) room->setSubject(msg->GetString("subject", "")); break; } case IM_GET_EXTENDED_CONTACT_INFO: { BString user_id; if (msg->FindString("user_id", &user_id) == B_OK) fVCardManager->fetchVCard(gloox::JID(user_id.String()), this); break; } case IM_ROSTER_ADD_CONTACT: { BString user_name = msg->FindString("user_name"); BString user_id; if (msg->FindString("user_id", &user_id) != B_OK) break; fClient->rosterManager()->add(gloox::JID(user_id.String()), user_name.String(), gloox::StringList()); fClient->rosterManager()->subscribe(gloox::JID(user_id.String()), user_name.String()); fClient->rosterManager()->synchronize(); break; } case IM_ROSTER_REMOVE_CONTACT: { BString user_id; if (msg->FindString("user_id", &user_id) != B_OK) break; fClient->rosterManager()->remove(gloox::JID(user_id.String())); fClient->rosterManager()->unsubscribe(gloox::JID(user_id.String())); fClient->rosterManager()->synchronize(); BMessage rm(IM_MESSAGE); rm.AddInt32("im_what", IM_ROSTER_CONTACT_REMOVED); rm.AddString("user_id", user_id); _SendMessage(&rm); break; } case IM_ROSTER_EDIT_CONTACT: { BString user_id; BString user_name = msg->FindString("user_name"); if (msg->FindString("user_id", &user_id) != B_OK) break; gloox::JID jid(user_id.String()); gloox::RosterItem* item = fClient->rosterManager()->getRosterItem(jid); if (item != NULL && user_name.IsEmpty() == false) { item->setName(user_name.String()); fClient->rosterManager()->synchronize(); } break; } case IM_ROOM_KICK_PARTICIPANT: case IM_ROOM_BAN_PARTICIPANT: case IM_ROOM_UNBAN_PARTICIPANT: case IM_ROOM_MUTE_PARTICIPANT: case IM_ROOM_UNMUTE_PARTICIPANT: _MUCModeration(msg); break; default: return B_ERROR; } return B_OK; } status_t JabberHandler::Shutdown() { if (fVCardManager) fVCardManager->cancelVCardOperations(this); if (fClient) fClient->disposeMessageSession(fSession); kill_thread(fRecvThread); return B_OK; } void JabberHandler::SetAddOnPath(BPath path) { fPath = path; } BPath JabberHandler::AddOnPath() { return fPath; } void JabberHandler::SetName(const char* name) { fName = name; } const char* JabberHandler::GetName() { return fName.String(); } BObjectList JabberHandler::Commands() { return BObjectList(); } BObjectList JabberHandler::ChatPopUpItems() { return BObjectList(); } BObjectList JabberHandler::UserPopUpItems() { return BObjectList(); } BObjectList JabberHandler::MenuBarItems() { return BObjectList(); } status_t JabberHandler::UpdateSettings(BMessage* msg) { const char* username = msg->FindString("username"); const char* password = msg->FindString("password"); const char* server = msg->FindString("server"); const char* resource = msg->FindString("resource"); // Sanity check if (!username || !password || !resource) return B_ERROR; // Store settings fUsername = username; fNick = username; fPassword = password; fServer = server; fResource = resource; fPort = 5222; // Give the add-on a change to override settings OverrideSettings(); // Compose JID BString jid = ComposeJID(); fJid.setJID(jid.String()); // Register our client fClient = new OurClient(fJid, fPassword.String()); fClient->setServer(fServer.String()); if (fPort > 0) fClient->setPort(fPort); fClient->registerConnectionListener(this); fClient->registerMessageSessionHandler(this); fClient->registerMUCInvitationHandler(new InviteHandler(fClient, this)); fClient->rosterManager()->registerRosterListener(this); fClient->disco()->setVersion("Chat-O-Matic", VERSION); fClient->disco()->setIdentity("client", "chat-o-matic"); fClient->logInstance().registerLogHandler(gloox::LogLevelDebug, gloox::LogAreaAll, this); // Register vCard manager fVCardManager = new gloox::VCardManager(fClient); // Connect to the server fClient->connect(false); // Read data from another thread fRecvThread = spawn_thread(connect_thread, "connect_thread", B_NORMAL_PRIORITY, (void*)this); if (fRecvThread < B_OK) return B_ERROR; return B_OK; } uint32 JabberHandler::GetEncoding() { return 0xffff; } ChatProtocolMessengerInterface* JabberHandler::MessengerInterface() const { return fServerMessenger; } gloox::Client* JabberHandler::Client() const { return fClient; } void JabberHandler::HandleConnectionError(gloox::ConnectionError& e) { #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler ― Connection errors" // Handle error BMessage errMsg(IM_ERROR); switch (e) { case gloox::ConnStreamError: { #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler ― Connection stream errors" gloox::StreamError streamError = fClient->streamError(); errMsg.AddString("error", B_TRANSLATE("Bad or malformed XML stream.")); switch (streamError) { case gloox::StreamErrorBadFormat: errMsg.AddString("detail", B_TRANSLATE("The entity has " "sent XML that cannot be processed")); break; case gloox::StreamErrorBadNamespacePrefix: errMsg.AddString("detail", B_TRANSLATE("The entity has " "sent a namespace prefix which is not supported, " "or has sent no namespace prefix on an element " "that requires such prefix.")); break; case gloox::StreamErrorConflict: errMsg.AddString("detail", B_TRANSLATE("The server is " "closing the active stream for this entity " "because a new stream has been initiated that " "conflicts with the existing stream.")); break; case gloox::StreamErrorConnectionTimeout: errMsg.AddString("detail", B_TRANSLATE("The entity has " "not generated any traffic over the stream for " "some period of time.")); break; case gloox::StreamErrorHostGone: errMsg.AddString("detail", B_TRANSLATE("The host initially " "used corresponds to a hostname that is no longer " "hosted by the server.")); break; case gloox::StreamErrorHostUnknown: errMsg.AddString("detail", B_TRANSLATE("The host initially " "used does not correspond to a hostname that is " "hosted by the server.")); break; case gloox::StreamErrorImproperAddressing: errMsg.AddString("detail", B_TRANSLATE("A stanza sent " "between two servers lacks a 'to' or 'from' " "attribute (or the attribute has no value.")); break; case gloox::StreamErrorInternalServerError: errMsg.AddString("detail", B_TRANSLATE("The server has " "experienced a misconfiguration or an " "otherwise-undefined internal error that prevents " "it from servicing the stream.")); break; case gloox::StreamErrorInvalidFrom: errMsg.AddString("detail", B_TRANSLATE("The JID or " "hostname provided in a 'from' address does not " "match an authorized JID or validated domain " "negotiation between servers via SASL or dialback, " "or between a client and a server via " "authentication and resource binding.")); break; case gloox::StreamErrorInvalidId: errMsg.AddString("detail", B_TRANSLATE("The stream ID or " "dialback ID is invalid or does not match and ID " "previously provdided.")); break; case gloox::StreamErrorInvalidNamespace: errMsg.AddString("detail", B_TRANSLATE("The streams " "namespace name is something other than " "\"http://etherx.jabber.org/streams\" or the " "dialback namespace name is something other than " "\"jabber:server:dialback\".")); break; case gloox::StreamErrorInvalidXml: errMsg.AddString("detail", B_TRANSLATE("The entity has " "sent invalid XML over the stream to a server that " "performs validation.")); break; case gloox::StreamErrorNotAuthorized: errMsg.AddString("detail", B_TRANSLATE("The entity has " "attempted to send data before the stream has been " "authenticated, or otherwise is not authorized to " "perform an action related to stream negotiation; " "the receiving entity must not process the " "offending stanza before sending the stream " "error.")); break; case gloox::StreamErrorPolicyViolation: errMsg.AddString("detail", B_TRANSLATE("The entity has " "violated some local service policy; the server " "may choose to specify the policy in the " "element or an application-specific condition " "element.")); break; case gloox::StreamErrorRemoteConnectionFailed: errMsg.AddString("detail", B_TRANSLATE("The server is " "unable to properly connect to a remote entity " "that is required for authentication.")); break; case gloox::StreamErrorResourceConstraint: errMsg.AddString("detail", B_TRANSLATE("The server lacks " "the system resources necessary to service the " "stream.")); break; case gloox::StreamErrorRestrictedXml: errMsg.AddString("detail", B_TRANSLATE("The entity has " "attempted to send restricted XML features such as " "a comment, processing instruction, DTD, entity " "reference or unescaped character.")); break; case gloox::StreamErrorSeeOtherHost: errMsg.AddString("detail", B_TRANSLATE("The server will " "not provide service to the initiating entity but " "is redirecting traffic to another host; the " "server should specify the alternate hostname or " "IP address (which MUST be a valid domain " "identifier) as the XML characted data of the " " element.")); break; case gloox::StreamErrorSystemShutdown: errMsg.AddString("detail", B_TRANSLATE("The server is " "being shut down and all active streams are being " "closed.")); break; case gloox::StreamErrorUndefinedCondition: errMsg.AddString("detail", B_TRANSLATE("The error " "condition is not one of those defined by the " "other condition in this list; this error " "condition should be used only in conjunction with " "an application-specific condition.")); break; case gloox::StreamErrorUnsupportedEncoding: errMsg.AddString("detail", B_TRANSLATE("The initiating " "entity has encoded the stream in an encoding " "that is not supported by the server.")); break; case gloox::StreamErrorUnsupportedStanzaType: errMsg.AddString("detail", B_TRANSLATE("The initiating " "entity has sent a first-level child of the stream " "that is not supported by the server.")); break; case gloox::StreamErrorUnsupportedVersion: errMsg.AddString("detail", B_TRANSLATE("The value of the " "'version' attribute provided by the initiating " "entity in the stream header specifies a version " "of XMPP that is not supported by the server; the " "server may specify the version(s) it supports in " "the element.")); break; case gloox::StreamErrorXmlNotWellFormed: errMsg.AddString("detail", B_TRANSLATE("The initiating " "entity has sent XML that is not well-formed as " "defined by XML.")); break; default: break; } break; } case gloox::ConnStreamVersionError: errMsg.AddString("detail", B_TRANSLATE("The incoming stream's " "version is not supported.")); break; case gloox::ConnStreamClosed: errMsg.AddString("detail", B_TRANSLATE("The stream has been closed " "by the server.")); break; case gloox::ConnProxyAuthRequired: errMsg.AddString("detail", B_TRANSLATE("The HTTP/SOCKS5 proxy " "requires authentication.")); break; case gloox::ConnProxyAuthFailed: errMsg.AddString("detail", B_TRANSLATE("HTTP/SOCKS5 proxy " "authentication failed.")); break; case gloox::ConnProxyNoSupportedAuth: errMsg.AddString("detail", B_TRANSLATE("The HTTP/SOCKS5 proxy " "requires an unsupported authentication mechanism.")); break; case gloox::ConnIoError: errMsg.AddString("detail", B_TRANSLATE("Input/output error.")); break; case gloox::ConnParseError: errMsg.AddString("detail", B_TRANSLATE("A XML parse error " "occurred.")); break; case gloox::ConnConnectionRefused: errMsg.AddString("detail", B_TRANSLATE("The connection was refused " "by the server on the socket level.")); break; case gloox::ConnDnsError: errMsg.AddString("detail", B_TRANSLATE("Server's hostname " "resolution failed.")); break; case gloox::ConnOutOfMemory: errMsg.AddString("detail", B_TRANSLATE("Out of memory.")); break; case gloox::ConnTlsFailed: errMsg.AddString("detail", B_TRANSLATE("The server's certificate " "could not be verified or the TLS handshake did not " "complete successfully.")); break; case gloox::ConnTlsNotAvailable: errMsg.AddString("detail", B_TRANSLATE("The server didn't offer " "TLS while it was set to be required, or TLS was not " "compiled in.")); break; case gloox::ConnCompressionFailed: errMsg.AddString("detail", B_TRANSLATE("Negotiating or " "initializing compression failed.")); break; case gloox::ConnAuthenticationFailed: { #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler ― Connection authentication errors" gloox::AuthenticationError authError = fClient->authError(); errMsg.AddString("error", B_TRANSLATE("Authentication failed. " "Username or password wrong or account does not " "exist.")); switch (authError) { case gloox::SaslAborted: errMsg.AddString("detail", B_TRANSLATE("The receiving " "entity acknowledges an element sent by " "initiating entity; sent in reply to the " "element.")); break; case gloox::SaslIncorrectEncoding: errMsg.AddString("detail", B_TRANSLATE("The data provided " "by the initiating entity could not be processed " "because the base64 encoding is incorrect.")); break; case gloox::SaslInvalidAuthzid: errMsg.AddString("detail", B_TRANSLATE("The authid " "provided by the initiating entity is invalid, " "either because it is incorrectly formatted or " "because the initiating entity does not have " "permissions to authorize that ID; sent in reply " "to a element or an element " "with initial response data.")); break; case gloox::SaslInvalidMechanism: errMsg.AddString("detail", B_TRANSLATE("The initiating " "element did not provide a mechanism or requested " "a mechanism that is not supported by the " "receiving entity; sent in reply to an " "element.")); break; case gloox::SaslMalformedRequest: errMsg.AddString("detail", B_TRANSLATE("The request is " "malformed (e.g., the element includes an " "initial response but the mechanism does not " "allow that); sent in reply to an , " ", , or element.")); break; case gloox::SaslMechanismTooWeak: errMsg.AddString("detail", B_TRANSLATE("The mechanism " "requested by the initiating entity is weaker " "than server policy permits for that initiating " "entity; sent in reply to a element or " "an element with initial response data.")); break; case gloox::SaslNotAuthorized: errMsg.AddString("detail", B_TRANSLATE("The authentication " "failed because the initiating entity did not " "provide valid credentials (this includes but is " "not limited to the case of an unknown username); " "sent in reply to a element or an " " element with initial response data.")); break; case gloox::SaslTemporaryAuthFailure: errMsg.AddString("detail", B_TRANSLATE("The authentication " "failed because of a temporary error condition " "within the receiving entity; sent in reply to an " " element or element.")); break; case gloox::NonSaslConflict: errMsg.AddString("detail", B_TRANSLATE("Resource conflict, " "see XEP-0078.")); break; case gloox::NonSaslNotAcceptable: errMsg.AddString("detail", B_TRANSLATE("Required " "information not provided, see XEP-0078.")); break; case gloox::NonSaslNotAuthorized: errMsg.AddString("detail", B_TRANSLATE("Incorrect " "credentials.")); break; default: break; } break; } case gloox::ConnNotConnected: { BMessage disconnect(IM_MESSAGE); disconnect.AddInt32("im_what", IM_PROTOCOL_DISABLE); _SendMessage(&disconnect); break; } default: break; } _SendMessage(&errMsg); } void JabberHandler::HandleStanzaError(gloox::StanzaError error) { #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler ― Stanza errors" BMessage errMsg(IM_ERROR); errMsg.AddString("error", B_TRANSLATE("Stanza-related error")); BString detail; switch (error) { case gloox::StanzaErrorBadRequest: detail = B_TRANSLATE("The sender has sent XML that is malformed or " "that cannot be processed."); break; case gloox::StanzaErrorConflict: detail = B_TRANSLATE("Access cannot be granted because an existing " "resource or session exists with the same name or address."); break; case gloox::StanzaErrorFeatureNotImplemented: detail = B_TRANSLATE("This feature hasn't been implemented by the " "recipient or by the server."); break; case gloox::StanzaErrorForbidden: detail = B_TRANSLATE("You don't have permssion to do this."); break; case gloox::StanzaErrorGone: detail = B_TRANSLATE("The recipient or server can no longer be " "contacted at this address. Try again later, or with a " "different address."); break; case gloox::StanzaErrorInternalServerError: detail = B_TRANSLATE("The server could not process the stanza " "because of a misconfiguration or an otherwise-undefined " "internal server error."); break; case gloox::StanzaErrorItemNotFound: detail = B_TRANSLATE("The addressed JID or item requested cannot " "be found."); break; case gloox::StanzaErrorJidMalformed: detail = B_TRANSLATE("An invalid XMPP address or identifier was " "given. If you can, please try a different one."); break; case gloox::StanzaErrorNotAcceptable: detail = B_TRANSLATE("The server or user refuses to accept this, " "because some criteria hasn't been met (e.g., a local policy " "regarding acceptable words in messages)."); break; case gloox::StanzaErrorNotAllowed: detail = B_TRANSLATE("You aren't allowed to do this by the server " "or recepient."); break; case gloox::StanzaErrorNotAuthorized: detail = B_TRANSLATE("You need to be properily authenticated " "before doing this."); case gloox::StanzaErrorNotModified: detail = B_TRANSLATE("The item requested has not changed since it " "was last requested."); case gloox::StanzaErrorPaymentRequired: detail = B_TRANSLATE("The server refuses to offer service, because " "payment is required."); break; case gloox::StanzaErrorRecipientUnavailable: detail = B_TRANSLATE("The recipient is temporarily unavailable."); break; case gloox::StanzaErrorRedirect: detail = B_TRANSLATE("The recipient or server is redirecting " "requests for this information to another entity, usually " "temporarily."); break; case gloox::StanzaErrorRegistrationRequired: detail = B_TRANSLATE("You can't do this before registration! Be " "sure your finished registering for your account."); break; case gloox::StanzaErrorRemoteServerNotFound: detail = B_TRANSLATE("That user's server doesn't exist."); break; case gloox::StanzaErrorRemoteServerTimeout: detail = B_TRANSLATE("Connection to that user's server has timed " "out."); break; case gloox::StanzaErrorResourceConstraint: detail = B_TRANSLATE("The server or recipient are too busy right " "now; try again later."); break; case gloox::StanzaErrorServiceUnavailable: detail = B_TRANSLATE("The server or recipient don't provide this " "service."); break; case gloox::StanzaErrorSubscribtionRequired: detail = B_TRANSLATE("You can't access this unless you are " "subscribed."); break; } errMsg.AddString("detail", detail); _SendMessage(&errMsg); } void JabberHandler::_SendMessage(BMessage* msg) { // Skip invalid messages if (!msg) return; msg->AddString("protocol", Signature()); fServerMessenger->SendMessage(msg); } void JabberHandler::_Notify(notification_type type, const char* title, const char* message) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_NOTIFICATION); msg.AddInt32("type", (int32)type); msg.AddString("title", title); msg.AddString("message", message); _SendMessage(&msg); } void JabberHandler::_NotifyProgress(const char* title, const char* message, float progress) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_PROGRESS); msg.AddString("title", title); msg.AddString("message", message); msg.AddFloat("progress", progress); _SendMessage(&msg); } void JabberHandler::_MessageSent(const char* id, const char* subject, const char* body) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_MESSAGE_SENT); msg.AddString("user_id", fJid.bare().c_str()); msg.AddString("chat_id", id); msg.AddString("subject", subject); msg.AddString("body", body); _SendMessage(&msg); } void JabberHandler::_ChatCreatedMsg(const char* id) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_CHAT_CREATED); msg.AddString("chat_id", id); msg.AddString("user_id", id); _SendMessage(&msg); } void JabberHandler::_RoleChangedMsg(BString chat_id, BString user_id, gloox::MUCRoomRole role, gloox::MUCRoomAffiliation aff) { BMessage roleMsg(IM_MESSAGE); roleMsg.AddInt32("im_what", IM_ROOM_ROLECHANGED); roleMsg.AddString("user_id", user_id); roleMsg.AddString("chat_id", chat_id); roleMsg.AddString("role_title", _RoleTitle(role, aff)); roleMsg.AddInt32("role_perms", _RolePerms(role, aff)); roleMsg.AddInt32("role_priority", _RolePriority(role, aff)); _SendMessage(&roleMsg); } void JabberHandler::_UserLeftMsg(BString chat_id, gloox::MUCRoomParticipant participant) { BString user_id; const char* nick = participant.nick->resource().c_str(); bool isSelf = _MUCUserId(chat_id, nick, &user_id); if (user_id.IsEmpty() == true) return; int32 im_what = IM_ROOM_PARTICIPANT_LEFT; int flags = participant.flags; if (flags & gloox::UserBanned) im_what = IM_ROOM_PARTICIPANT_BANNED; else if (flags & gloox::UserKicked) im_what = IM_ROOM_PARTICIPANT_KICKED; BMessage leftMsg(IM_MESSAGE); leftMsg.AddInt32("im_what", im_what); leftMsg.AddString("chat_id", chat_id); leftMsg.AddString("user_id", user_id); leftMsg.AddString("user_name", nick); if (participant.reason.empty() == false) leftMsg.AddString("body", participant.reason.c_str()); _SendMessage(&leftMsg); } void JabberHandler::_StatusSetMsg(const char* user_id, gloox::Presence::PresenceType type, const char* message, const char* resource) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_USER_STATUS_SET); msg.AddString("user_id", user_id); msg.AddInt32("status", _GlooxStatusToApp(type)); if (BString(resource).IsEmpty() == false) msg.AddString("resource", resource); if (BString(message).IsEmpty() == false) msg.AddString("message", message); _SendMessage(&msg); } void JabberHandler::_EnsureUserChat(const char* chat_id) { if (fUserChats.HasString(BString(chat_id)) == false) fUserChats.Add(BString(chat_id)); } status_t JabberHandler::_SetupAvatarCache() { if (fAvatarCachePath.InitCheck() == B_OK) return B_OK; BPath path; if (find_directory(B_SYSTEM_TEMP_DIRECTORY, &path) != B_OK) return B_ERROR; path.Append(GetName()); if (create_directory(path.Path(), 0755) != B_OK) return B_ERROR; fCachePath = path; path.Append("avatar-cache"); BFile file(path.Path(), B_READ_ONLY); fAvatarCache.Unflatten(&file); fAvatarCachePath = path; return B_OK; } status_t JabberHandler::_SaveAvatarCache() { if (fAvatarCachePath.InitCheck() != B_OK) return B_ERROR; BFile file(fAvatarCachePath.Path(), B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE); return fAvatarCache.Flatten(&file); } void JabberHandler::_CacheAvatar(const char* id, const char* binimage, size_t length) { if (!id || !binimage || length <= 0) return; // Calculate avatar hash CSHA1 s1; char hash[256]; s1.Reset(); s1.Update((uchar*)binimage, length); s1.Final(); s1.ReportHash(hash, CSHA1::REPORT_HEX); BString sha1; sha1.SetTo(hash, 256); BString oldSha1; if (fAvatarCache.FindString(id, &oldSha1) != B_OK || oldSha1 == "" || sha1 != oldSha1) { // Replace old hash and save cache fAvatarCache.RemoveName(id); fAvatarCache.AddString(id, sha1); _SaveAvatarCache(); if (oldSha1 != "") { BPath path(fCachePath); path.Append(oldSha1); // Remove old image file BEntry entry(path.Path()); entry.Remove(); // Remove old hash from the list BString* item = NULL; for (int32 i = 0; (item = (BString*)fAvatars->ItemAt(i)); i++) { if (item->Compare(oldSha1) == 0) { fAvatars->RemoveItem(item); break; } } } } // Determine file path BPath path(fCachePath); path.Append(sha1); BEntry entry(path.Path()); if (!entry.Exists()) { // Save to file BFile file(path.Path(), B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE); file.Write(binimage, length); } // Do we need to notify the app? bool found = false; BString* item = NULL; for (int32 i = 0; (item = (BString*)fAvatars->ItemAt(i)); i++) { if (item->Compare(sha1) == 0) { found = true; break; } } if (!found) { // Add new hash to the list if needed fAvatars->AddItem(new BString(sha1)); // Notify Caya that the avatar has changed _AvatarChanged(id, path.Path()); } } void JabberHandler::_AvatarChanged(const char* id, const char* filename) { entry_ref ref; if (get_ref_for_path(filename, &ref) != B_OK) return; BMessage msg(IM_MESSAGE); if (fJid.bare() == id) msg.AddInt32("im_what", IM_OWN_AVATAR_SET); else { msg.AddInt32("im_what", IM_USER_AVATAR_SET); msg.AddString("user_id", id); } msg.AddRef("ref", &ref); _SendMessage(&msg); } UserStatus JabberHandler::_GlooxStatusToApp(gloox::Presence::PresenceType type) { switch (type) { case gloox::Presence::Available: case gloox::Presence::Chat: return STATUS_ONLINE; case gloox::Presence::Away: return STATUS_AWAY; case gloox::Presence::XA: return STATUS_CUSTOM_STATUS; case gloox::Presence::DND: return STATUS_DO_NOT_DISTURB; case gloox::Presence::Unavailable: return STATUS_OFFLINE; default: break; } return STATUS_OFFLINE; } BString JabberHandler::_MUCChatId(gloox::MUCRoom* room) { BString chat_id(room->name().c_str()); chat_id << "@" << room->service().c_str(); BStringList parts; chat_id.Split("/", false, parts); return parts.StringAt(0); } bool JabberHandler::_MUCUserId(BString chat_id, const char* nick, BString* id) { BString chat(chat_id); chat << "/" << nick; // If sent from own user, use normal ID if (nick == fNick) { *id = fJid.bare().c_str(); return true; } *id = chat; return false; } void JabberHandler::_JoinRoom(const char* chat_id) { BString join_id(chat_id); join_id << "/" << fNick; gloox::MUCRoom* room = fRooms.ValueFor(chat_id); if (room == NULL) room = new gloox::MUCRoom(fClient, gloox::JID(join_id.String()), this, this); room->join(); fRooms.AddItem(BString(chat_id), room); } void JabberHandler::_MUCModeration(BMessage* msg) { BString chat_id = msg->FindString("chat_id"); BString user_id; BString body = msg->FindString("body"); gloox::MUCRoom* room = fRooms.ValueFor(chat_id); if (room == NULL || msg->FindString("user_id", &user_id) != B_OK) return; std::string nick = gloox::JID(user_id.String()).resource(); switch (msg->FindInt32("im_what")) { case IM_ROOM_KICK_PARTICIPANT: room->kick(nick, body.String()); break; case IM_ROOM_BAN_PARTICIPANT: room->ban(nick, body.String()); break; case IM_ROOM_MUTE_PARTICIPANT: room->revokeVoice(nick, body.String()); break; case IM_ROOM_UNMUTE_PARTICIPANT: room->grantVoice(nick, body.String()); break; } } const char* JabberHandler::_RoleTitle(gloox::MUCRoomRole role, gloox::MUCRoomAffiliation aff) { #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler ― User roles" switch (role) { case gloox::RoleVisitor: return B_TRANSLATE("Visitor"); case gloox::RoleParticipant: return B_TRANSLATE("Member"); case gloox::RoleModerator: if (aff == gloox::AffiliationOwner) return B_TRANSLATE("Owner"); return B_TRANSLATE("Moderator"); } return B_TRANSLATE("Invalid"); } int32 JabberHandler::_RolePerms(gloox::MUCRoomRole role, gloox::MUCRoomAffiliation aff) { switch (role) { case gloox::RoleVisitor: return 0 | PERM_READ | PERM_NICK; case gloox::RoleParticipant: return 0 | PERM_READ | PERM_WRITE | PERM_ROOM_SUBJECT; case gloox::RoleModerator: { int32 perm = 0 | PERM_READ | PERM_WRITE | PERM_ROOM_SUBJECT | PERM_KICK | PERM_MUTE; if (aff == gloox::AffiliationOwner) perm = perm | PERM_ROLECHANGE | PERM_BAN; return perm; } } return 0; } int32 JabberHandler::_RolePriority(gloox::MUCRoomRole role, gloox::MUCRoomAffiliation aff) { switch (role) { case gloox::RoleParticipant: return 1; case gloox::RoleModerator: if (aff == gloox::AffiliationOwner) return 3; return 2; } return 0; } BMessage JabberHandler::_SettingsTemplate(const char* username, bool serverOption) { #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler ― Settings template" BMessage stemplate('IMst'); BMessage usernameText; usernameText.AddString("name", "username"); usernameText.AddString("description", username); usernameText.AddString("error", B_TRANSLATE("You can't log into an account " "without a username.\nPlease fill in your username for the given " "server.")); usernameText.AddInt32("type", 'CSTR'); stemplate.AddMessage("setting", &usernameText); BMessage passwordText; passwordText.AddString("name", "password"); passwordText.AddString("description", B_TRANSLATE("Password:")); passwordText.AddString("error", B_TRANSLATE("You can't log into an account " "without a password.\nPlease fill in your password for the given " "account.")); passwordText.AddInt32("type", 'CSTR'); passwordText.AddBool("is_secret", true); stemplate.AddMessage("setting", &passwordText); BMessage serverText; serverText.AddString("name", "server"); serverText.AddString("description", B_TRANSLATE("Server:")); serverText.AddString("error", B_TRANSLATE("You can't add an account " "without a server.\nPlease add a valid XMPP server.")); serverText.AddInt32("type", 'CSTR'); if (serverOption == true) stemplate.AddMessage("setting", &serverText); BMessage resourceText; resourceText.AddString("name", "resource"); resourceText.AddString("description", B_TRANSLATE("Resource:")); resourceText.AddInt32("type", 'CSTR'); resourceText.AddString("default", "Chat-O-Matic"); resourceText.AddString("error", B_TRANSLATE("You can't add an account " "without a resource.\nDon't worry― it can be whatever string you " "want.")); stemplate.AddMessage("setting", &resourceText); return stemplate; } BMessage JabberHandler::_RoomTemplate() { #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler ― Room template" BMessage stemplate('IMst'); BMessage roomIdentifier; roomIdentifier.AddString("name", "chat_id"); roomIdentifier.AddString("description", B_TRANSLATE("Room ID:")); roomIdentifier.AddString("error", B_TRANSLATE("You can't have a room " "without a JID!\nUse the \"name@server\" format.")); roomIdentifier.AddInt32("type", 'CSTR'); stemplate.AddMessage("setting", &roomIdentifier); return stemplate; } BMessage JabberHandler::_RosterTemplate() { #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "JabberHandler ― Roster template" BMessage stemplate('IMst'); BMessage user_id; user_id.AddString("name", "user_id"); user_id.AddString("description", B_TRANSLATE("User ID:")); user_id.AddString("error", B_TRANSLATE("You can't befriend an IDless " "miscreant!\nPlease use the \"name@server\" format.")); user_id.AddInt32("type", 'CSTR'); stemplate.AddMessage("setting", &user_id); BMessage user_name; user_name.AddString("name", "user_name"); user_name.AddString("description", B_TRANSLATE("Nickname:")); user_name.AddInt32("type", 'CSTR'); stemplate.AddMessage("setting", &user_name); return stemplate; } /*********************************************************************** * gloox callbacks **********************************************************************/ void JabberHandler::onConnect() { // We are online BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_OWN_STATUS_SET); msg.AddInt32("status", STATUS_ONLINE); _SendMessage(&msg); fVCardManager->fetchVCard(fJid, this); } void JabberHandler::onDisconnect(gloox::ConnectionError e) { // We are offline BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_OWN_STATUS_SET); msg.AddInt32("status", STATUS_OFFLINE); _SendMessage(&msg); if (e == gloox::ConnNoError) { // Notify our offline status BString content(fUsername); content << " has logged out!"; _Notify(B_INFORMATION_NOTIFICATION, "Disconnected", content.String()); return; } // Handle error HandleConnectionError(e); } bool JabberHandler::onTLSConnect(const gloox::CertInfo& info) { return true; } void JabberHandler::onResourceBindError(const gloox::Error* error) { } void JabberHandler::handleRoster(const gloox::Roster& roster) { std::list msgs; BMessage contactListMsg(IM_MESSAGE); contactListMsg.AddInt32("im_what", IM_ROSTER); gloox::Roster::const_iterator it = roster.begin(); for (; it != roster.end(); ++it) { const char* jid = (*it).second->jidJID().full().c_str(); const char* name = (*it).second->name().c_str(); int32 subscription = (*it).second->subscription(); // Add jid to the server based contact list message contactListMsg.AddString("user_id", jid); // Contact information message BMessage infoMsg(IM_MESSAGE); infoMsg.AddInt32("im_what", IM_CONTACT_INFO); infoMsg.AddString("user_id", jid); infoMsg.AddString("user_name", name); infoMsg.AddInt32("status", STATUS_OFFLINE); // Groups gloox::StringList g = (*it).second->groups(); gloox::StringList::const_iterator it_g = g.begin(); for (; it_g != g.end(); ++it_g) infoMsg.AddString("group", (*it_g).c_str()); // Resources gloox::RosterItem::ResourceMap::const_iterator rit = (*it).second->resources().begin(); for (; rit != (*it).second->resources().end(); ++rit) infoMsg.AddString("resource", (*rit).first.c_str()); // Store contact info message to be sent later msgs.push_back(infoMsg); } // Send server based contact list _SendMessage(&contactListMsg); // Contact list and vCard request std::list::iterator msgsIt; for (msgsIt = msgs.begin(); msgsIt != msgs.end(); ++msgsIt) { BMessage msg = (*msgsIt); const char* jid = msg.FindString("user_id"); _SendMessage(&msg); fVCardManager->fetchVCard(gloox::JID(jid), this); } // This is a safe spot to say "READY", since this is called immediately // after logging in, whether the user has friends in the roster or not― // especially since loading all the users from the roster can take a while. BMessage readyMsg(IM_MESSAGE); readyMsg.AddInt32("im_what", IM_PROTOCOL_READY); _SendMessage(&readyMsg); } void JabberHandler::handleMessageSession(gloox::MessageSession* session) { // Delete previous session if (fSession) fClient->disposeMessageSession(fSession); fSession = session; // Register message handler fSession->registerMessageHandler(this); // Message event filter gloox::MessageEventFilter* messageFilter = new gloox::MessageEventFilter(fSession); messageFilter->registerMessageEventHandler(this); // Chat state filter gloox::ChatStateFilter* chatFilter = new gloox::ChatStateFilter(fSession); chatFilter->registerChatStateHandler(this); } void JabberHandler::handleMessage(const gloox::Message& m, gloox::MessageSession*) { // Only chat messages are handled now if (m.subtype() != gloox::Message::Chat) return; // We need a body if (m.body() == "") return; _EnsureUserChat(m.from().bare().c_str()); // Notify that a chat message was received BMessage msg(IM_MESSAGE); msg.AddString("user_id", m.from().bare().c_str()); msg.AddString("chat_id", m.from().bare().c_str()); msg.AddInt32("im_what", IM_MESSAGE_RECEIVED); if (m.subject() != "") msg.AddString("subject", m.subject().c_str()); msg.AddString("body", m.body().c_str()); _SendMessage(&msg); } void JabberHandler::handleMessageEvent(const gloox::JID& from, gloox::MessageEventType event) { } void JabberHandler::handleChatState(const gloox::JID& from, gloox::ChatStateType state) { // We're interested only in some states if (state == gloox::ChatStateActive || state == gloox::ChatStateInvalid) return; _EnsureUserChat(from.bare().c_str()); BMessage msg(IM_MESSAGE); msg.AddString("user_id", from.bare().c_str()); msg.AddString("chat_id", from.bare().c_str()); switch (state) { case gloox::ChatStateComposing: msg.AddInt32("im_what", IM_ROOM_PARTICIPANT_STARTED_TYPING); break; case gloox::ChatStatePaused: msg.AddInt32("im_what", IM_ROOM_PARTICIPANT_STOPPED_TYPING); break; case gloox::ChatStateGone: // TODO break; default: break; } _SendMessage(&msg); } void JabberHandler::handleMUCParticipantPresence(gloox::MUCRoom *room, const gloox::MUCRoomParticipant participant, const gloox::Presence &presence) { // participant.flags, particpiant.role // 0-0 (left), 0-* (joined/rolechange) gloox::MUCRoomRole role = participant.role; gloox::MUCRoomAffiliation aff = participant.affiliation; const char* nick = participant.nick->resource().c_str(); BString user_id; BString chat_id = _MUCChatId(room); bool isSelf = _MUCUserId(chat_id, nick, &user_id); if (chat_id.IsEmpty() == true || user_id.IsEmpty() == true) return; if (participant.flags & gloox::UserSelf) { if ((participant.flags & gloox::UserNickChanged) || (participant.flags & gloox::UserNickAssigned)) { BMessage nickChanged(IM_MESSAGE); nickChanged.AddInt32("im_what", IM_OWN_NICKNAME_SET); nickChanged.AddString("user_name", participant.newNick.c_str()); _SendMessage(&nickChanged); fNick = participant.newNick.c_str(); } else { int im_what = IM_ROOM_JOINED; if (presence.presence() == 5) im_what = IM_ROOM_LEFT; BMessage joinedMsg(IM_MESSAGE); joinedMsg.AddInt32("im_what", im_what); joinedMsg.AddString("chat_id", chat_id); _SendMessage(&joinedMsg); _RoleChangedMsg(chat_id, user_id, role, aff); } return; } _StatusSetMsg(user_id.String(), presence.presence(), presence.status().c_str(), ""); // If unavailable (disconnected/left chat) if (presence.presence() == 5) { _UserLeftMsg(chat_id, participant); return; } BMessage joinMsg(IM_MESSAGE); joinMsg.AddInt32("im_what", IM_ROOM_PARTICIPANT_JOINED); joinMsg.AddString("user_id", user_id); joinMsg.AddString("user_name", nick); joinMsg.AddString("chat_id", chat_id); _SendMessage(&joinMsg); _RoleChangedMsg(chat_id, user_id, role, aff); } void JabberHandler::handleMUCMessage(gloox::MUCRoom *room, const gloox::Message &m, bool priv) { BString user_id; BString chat_id = _MUCChatId(room); bool isSelf = _MUCUserId(chat_id, m.from().resource().c_str(), &user_id); if (chat_id.IsEmpty() == true || user_id.IsEmpty() == true) return; int32 im_what = IM_MESSAGE_RECEIVED; // We need a body if (m.body() == "") return; // If sent from own user, then IM_MESSAGE_SENT if (isSelf == true) im_what = IM_MESSAGE_SENT; // when() is only nonzero when sending backdated messages (logs) if (m.when() != 0) im_what = IM_LOGS_RECEIVED; // Notify that a chat message was received BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", im_what); msg.AddString("user_id", user_id); msg.AddString("chat_id", chat_id); if (m.subject() != "") msg.AddString("subject", m.subject().c_str()); msg.AddString("body", m.body().c_str()); _SendMessage(&msg); } bool JabberHandler::handleMUCRoomCreation(gloox::MUCRoom* room) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_ROOM_CREATED); msg.AddString("chat_id", _MUCChatId(room)); _SendMessage(&msg); return true; } void JabberHandler::handleMUCSubject(gloox::MUCRoom *room, const std::string &nick, const std::string &subject) { BString user_id; BString chat_id = _MUCChatId(room); bool isSelf = _MUCUserId(chat_id, nick.c_str(), &user_id); if (chat_id.IsEmpty() == true) return; BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_ROOM_SUBJECT_SET); msg.AddString("subject", subject.c_str()); msg.AddString("chat_id", chat_id); if (user_id.IsEmpty() == false) msg.AddString("user_id", user_id); _SendMessage(&msg); } void JabberHandler::handleMUCInviteDecline(gloox::MUCRoom *room, const gloox::JID &invitee, const std::string &reason) { } void JabberHandler::handleMUCError(gloox::MUCRoom *room, gloox::StanzaError error) { HandleStanzaError(error); } void JabberHandler::handleMUCInfo(gloox::MUCRoom *room, int features, const std::string &name, const gloox::DataForm *infoForm) { BString chat_id = _MUCChatId(room); BMessage metadata(IM_MESSAGE); metadata.AddInt32("im_what", IM_ROOM_METADATA); metadata.AddString("chat_id", chat_id); metadata.AddString("chat_name", name.c_str()); metadata.AddInt32("room_default_flags", 0 | ROOM_AUTOJOIN | ROOM_LOG_LOCALLY | ROOM_POPULATE_LOGS | ROOM_NOTIFY_DM); metadata.AddInt32("room_disallowed_flags", 0 | ROOM_AUTOCREATE); _SendMessage(&metadata); } void JabberHandler::handleMUCItems(gloox::MUCRoom *room, const gloox::Disco::ItemList &items) { BString chat_id = _MUCChatId(room); BStringList nicks; BStringList ids; if (chat_id.IsEmpty() == true) return; for (auto item: items) { BString nick = item->jid().resource().c_str(); nicks.Add(nick); // Unless it's the user, derive ID from room resource if (fNick != nick) { BString user_id(chat_id); user_id << "/" << nick; ids.Add(user_id); } else ids.Add(BString(fJid.bare().c_str())); } BMessage msg(IM_MESSAGE); msg.AddStrings("user_id", ids); msg.AddStrings("user_name", nicks); msg.AddString("chat_id", chat_id); msg.AddInt32("im_what", IM_ROOM_PARTICIPANTS); _SendMessage(&msg); } void JabberHandler::handleMUCConfigList(gloox::MUCRoom* room, const gloox::MUCListItemList &items, gloox::MUCOperation operation) { } void JabberHandler::handleMUCConfigForm(gloox::MUCRoom* room, const gloox::DataForm &form) { } void JabberHandler::handleMUCConfigResult(gloox::MUCRoom* room, bool success, gloox::MUCOperation operation) { } void JabberHandler::handleMUCRequest(gloox::MUCRoom* room, const gloox::DataForm &form) { } void JabberHandler::handleItemAdded(const gloox::JID&) { } void JabberHandler::handleItemSubscribed(const gloox::JID&) { } void JabberHandler::handleItemUnsubscribed(const gloox::JID&) { } void JabberHandler::handleItemRemoved(const gloox::JID&) { } void JabberHandler::handleItemUpdated(const gloox::JID&) { } void JabberHandler::handleRosterPresence(const gloox::RosterItem& item, const std::string& resource, gloox::Presence::PresenceType type, const std::string& presenceMsg) { _StatusSetMsg(item.jidJID().full().c_str(), type, presenceMsg.c_str(), resource.c_str()); } void JabberHandler::handleSelfPresence(const gloox::RosterItem& item, const std::string&, gloox::Presence::PresenceType type, const std::string& presenceMsg) { BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_OWN_CONTACT_INFO); msg.AddString("protocol", Signature()); msg.AddString("user_id", item.jidJID().full().c_str()); msg.AddString("user_name", fNick.String()); msg.AddInt32("subscription", item.subscription()); msg.AddInt32("status", _GlooxStatusToApp(type)); msg.AddString("message", presenceMsg.c_str()); // Groups gloox::StringList g = item.groups(); gloox::StringList::const_iterator it_g = g.begin(); for (; it_g != g.end(); ++it_g) msg.AddString("group", (*it_g).c_str()); // Resources gloox::RosterItem::ResourceMap::const_iterator rit = item.resources().begin(); for (; rit != item.resources().end(); ++rit) msg.AddString("resource", (*rit).first.c_str()); _SendMessage(&msg); } bool JabberHandler::handleSubscriptionRequest(const gloox::JID&, const std::string&) { return true; } bool JabberHandler::handleUnsubscriptionRequest(const gloox::JID&, const std::string&) { return true; } void JabberHandler::handleNonrosterPresence(const gloox::Presence&) { } void JabberHandler::handleRosterError(const gloox::IQ&) { } void JabberHandler::handleLog(gloox::LogLevel level, gloox::LogArea, const std::string& msg) { if (level >= gloox::LogLevelWarning) std::cout << msg << std::endl; } void JabberHandler::handleVCard(const gloox::JID& jid, const gloox::VCard* card) { if (!card) return; // Throttles updates to your own VCard― ime some servers spam for no reason if (jid.bare() == fJid.bare()) { bigtime_t last = fLastOwnVCard; fLastOwnVCard = time(NULL); if (fLastOwnVCard - last < 60) return; } gloox::VCard::Name name = card->name(); gloox::VCard::Photo photo = card->photo(); BString nick(card->nickname().c_str()); gloox::RosterItem* item = fClient->rosterManager()->getRosterItem(jid); if (item != NULL) nick = item->name().c_str(); std::string fullName = name.family + " " + name.given; BMessage msg(IM_MESSAGE); msg.AddInt32("im_what", IM_EXTENDED_CONTACT_INFO); msg.AddString("user_id", jid.bare().c_str()); msg.AddString("user_name", nick); msg.AddString("family_name", name.family.c_str()); msg.AddString("given_name", name.given.c_str()); msg.AddString("middle_name", name.middle.c_str()); msg.AddString("prefix", name.prefix.c_str()); msg.AddString("suffix", name.suffix.c_str()); msg.AddString("full_name", fullName.c_str()); _SendMessage(&msg); // Return if there's no avatar icon if (!photo.binval.c_str()) return; if (_SetupAvatarCache() == B_OK) // Cache avatar icon and notify the change _CacheAvatar(jid.bare().c_str(), photo.binval.c_str(), photo.binval.length()); } void JabberHandler::handleVCardResult(gloox::VCardHandler::VCardContext context, const gloox::JID& jid, gloox::StanzaError) { //if (context == gloox::VCardHandler::FetchVCard) //else } OurClient::OurClient(const gloox::JID jid, const char* password) : gloox::Client(jid, password) { } void OurClient::handleTag(gloox::Tag* tag) { if (DEBUG_ENABLED) std::cerr << "Tag\t" << tag->xml() << std::endl; gloox::Client::handleTag(tag); } InviteHandler::InviteHandler(gloox::ClientBase* client, JabberHandler* handler) : gloox::MUCInvitationHandler(client), fHandler(handler) { } void InviteHandler::handleMUCInvitation(const gloox::JID& room, const gloox::JID& from, const std::string& reason, const std::string& body, const std::string& password, bool cont, const std::string& thread) { std::string chat_name = room.resource().c_str(); BString chat_id = room.full().c_str(); if (chat_name.empty() == true) chat_name = chat_id.String(); if (password.empty() == false) chat_id << "#" << password.c_str(); BMessage invite(IM_MESSAGE); invite.AddInt32("im_what", IM_ROOM_INVITE_RECEIVED); invite.AddString("chat_id", chat_id); invite.AddString("chat_name", chat_name.c_str()); invite.AddString("user_id", from.bare().c_str()); if (reason.empty() == false) invite.AddString("body", reason.c_str()); invite.AddString("protocol", fHandler->Signature()); fHandler->MessengerInterface()->SendMessage(new BMessage(invite)); }