Chat-O-Matic/protocols/xmpp/JabberHandler.cpp
Jaidyn Ann 48d0b7bc96 Create Conversation class, use it instead of Contact for chats
This is a commit with it's foot in a lot of places, but:

The Conversation class was created as the abstraction of chats: All
ImMessages that are relevant to a conversation get routed through it,
meta-data on chats is stored in it (even if right now that's basically
limited to the user list and ID).

Server was given more methods to help accessing contacts―
ContactById(BString) and AddContact(Contact*). This better allows
Conversations to add and fetch Contacts as necessary. Right now, all
users in chats are treated as Contacts, so in the future creating an
independent userlist for Server (fUserMap?) would be useful.

Server also now stores all Conversations (fChatMap) and has some
convenience methods like for Contacts: Conversations(),
ConversationById(BString), and AddConversation(Conversation*).

CayaRenderView has been changed to not store user nicks, and will use
the appropriate nick of any arbitrarily-numbered user.

Users also have a map of all Conversations they are a part of
(fChatMap).

The Remove* methods of KeyMap now return the removed item.
2021-05-24 01:47:21 -05:00

1082 lines
28 KiB
C++

/*
* Copyright 2010, Pier Luigi Fiorini. All rights reserved.
* Distributed under the terms of the GPL v2 License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <FindDirectory.h>
#include <List.h>
#include <CayaProtocolMessages.h>
#include <libsupport/SHA1.h>
#include <gloox/chatstatefilter.h>
#include <gloox/messageeventfilter.h>
#include "JabberHandler.h"
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->HandleError(e);
return B_OK;
}
JabberHandler::JabberHandler()
:
fClient(NULL),
fVCardManager(NULL),
fSession(NULL)
{
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(CayaProtocolMessengerInterface* 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 CAYA_ONLINE:
// Log in if we still need to
resume_thread(fRecvThread);
break;
case CAYA_OFFLINE:
kill_thread(fRecvThread);
break;
default:
break;
}
break;
}
case IM_SEND_MESSAGE: {
const char* id = msg->FindString("chat_id");
const char* subject = msg->FindString("subject");
const char* body = msg->FindString("body");
if (!id || !body)
return B_ERROR;
// Send JabberHandler message
gloox::Message jm(gloox::Message::Chat, gloox::JID(id),
body, (subject ? subject : gloox::EmptyString));
fClient->send(jm);
// Tell Caya we actually sent the message
_MessageSent(id, subject, body);
break;
}
default:
return B_ERROR;
}
return B_OK;
}
status_t
JabberHandler::Shutdown()
{
if (fVCardManager)
fVCardManager->cancelVCardOperations(this);
if (fClient) {
fClient->disposeMessageSession(fSession);
fClient->disconnect();
}
return B_OK;
}
void
JabberHandler::SetPath(BPath path)
{
fPath = path;
}
BPath
JabberHandler::Path()
{
return fPath;
}
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;
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 gloox::Client(fJid, fPassword.String());
fClient->setServer(fServer.String());
if (fPort > 0)
fClient->setPort(fPort);
fClient->registerConnectionListener(this);
fClient->registerMessageSessionHandler(this);
fClient->rosterManager()->registerRosterListener(this);
fClient->disco()->setVersion("Caya", VERSION);
fClient->disco()->setIdentity("client", "caya");
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;
}
CayaProtocolMessengerInterface*
JabberHandler::MessengerInterface() const
{
return fServerMessenger;
}
gloox::Client*
JabberHandler::Client() const
{
return fClient;
}
void
JabberHandler::HandleError(gloox::ConnectionError& e)
{
// Handle error
BMessage errMsg(IM_ERROR);
switch (e) {
case gloox::ConnStreamError:
{
gloox::StreamError streamError = fClient->streamError();
errMsg.AddString("error", "Bad or malformed XML stream.");
switch (streamError) {
case gloox::StreamErrorBadFormat:
errMsg.AddString("detail", "The entity has sent XML that "
"cannot be processed");
break;
case gloox::StreamErrorBadNamespacePrefix:
errMsg.AddString("detail", "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", "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", "The entity has not generated any "
"traffic over the stream for some period of time.");
break;
case gloox::StreamErrorHostGone:
errMsg.AddString("detail", "The host initially used corresponds "
"to a hostname that is no longer hosted by the server.");
break;
case gloox::StreamErrorHostUnknown:
errMsg.AddString("detail", "The host initially used does not "
"correspond to a hostname that is hosted by the server.");
break;
case gloox::StreamErrorImproperAddressing:
errMsg.AddString("detail", "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", "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", "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", "The stream ID or dialback ID is invalid "
"or does not match and ID previously provdided.");
break;
case gloox::StreamErrorInvalidNamespace:
errMsg.AddString("detail", "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", "The entity has sent invalid XML over the "
"stream to a server that performs validation.");
break;
case gloox::StreamErrorNotAuthorized:
errMsg.AddString("detail", "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", "The entity has violated some local service "
"policy; the server may choose to specify the policy in the <text/> "
"element or an application-specific condition element.");
break;
case gloox::StreamErrorRemoteConnectionFailed:
errMsg.AddString("detail", "The server is unable to properly connect to "
"a remote entit that is required for authentication.");
break;
case gloox::StreamErrorResourceConstraint:
errMsg.AddString("detail", "The server lacks the system resources necessary "
"to service the stream.");
break;
case gloox::StreamErrorRestrictedXml:
errMsg.AddString("detail", "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", "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 <see-other-host/> element.");
break;
case gloox::StreamErrorSystemShutdown:
errMsg.AddString("detail", "The server is being shut down and all active streams "
"are being closed.");
break;
case gloox::StreamErrorUndefinedCondition:
errMsg.AddString("detail", "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", "The initiating entity has encoded the stream in an "
"encoding that is not supported by the server.");
break;
case gloox::StreamErrorUnsupportedStanzaType:
errMsg.AddString("detail", "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", "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 <text/> element.");
break;
case gloox::StreamErrorXmlNotWellFormed:
errMsg.AddString("detail", "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", "The incoming stream's version is not "
"supported.");
break;
case gloox::ConnStreamClosed:
errMsg.AddString("detail", "The stream has been closed by the server.");
break;
case gloox::ConnProxyAuthRequired:
errMsg.AddString("detail", "The HTTP/SOCKS5 proxy requires authentication.");
break;
case gloox::ConnProxyAuthFailed:
errMsg.AddString("detail", "HTTP/SOCKS5 proxy authentication failed.");
break;
case gloox::ConnProxyNoSupportedAuth:
errMsg.AddString("detail", "The HTTP/SOCKS5 proxy requires an unsupported "
"authentication mechanism.");
break;
case gloox::ConnIoError:
errMsg.AddString("detail", "Input/output error.");
break;
case gloox::ConnParseError:
errMsg.AddString("detail", "A XML parse error occurred.");
break;
case gloox::ConnConnectionRefused:
errMsg.AddString("detail", "The connection was refused by the server "
"on the socket level.");
break;
case gloox::ConnDnsError:
errMsg.AddString("detail", "Server's hostname resolution failed.");
break;
case gloox::ConnOutOfMemory:
errMsg.AddString("detail", "Out of memory.");
break;
case gloox::ConnTlsFailed:
errMsg.AddString("detail", "The server's certificate could not be verified or "
"the TLS handshake did not complete successfully.");
break;
case gloox::ConnTlsNotAvailable:
errMsg.AddString("detail", "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", "Negotiating or initializing compression failed.");
break;
case gloox::ConnAuthenticationFailed:
{
gloox::AuthenticationError authError = fClient->authError();
errMsg.AddString("error", "Authentication failed. Username or password wrong "
"or account does not exist.");
switch (authError) {
case gloox::SaslAborted:
errMsg.AddString("detail", "The receiving entity acknowledges an <abort/> "
"element sent by initiating entity; sent in reply to the <abort/> "
"element.");
break;
case gloox::SaslIncorrectEncoding:
errMsg.AddString("detail", "The data provided by the initiating entity "
"could not be processed because the base64 encoding is incorrect.");
break;
case gloox::SaslInvalidAuthzid:
errMsg.AddString("detail", "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 <response/> element or an <auth/> element with "
"initial response data.");
break;
case gloox::SaslInvalidMechanism:
errMsg.AddString("detail", "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 <auth/> element.");
break;
case gloox::SaslMalformedRequest:
errMsg.AddString("detail", "The request is malformed (e.g., the <auth/> "
"element includes an initial response but the mechanism does not "
"allow that); sent in reply to an <abort/>, <auth/>, <challenge/>, or "
"<response/> element.");
break;
case gloox::SaslMechanismTooWeak:
errMsg.AddString("detail", "The mechanism requested by the initiating entity "
"is weaker than server policy permits for that initiating entity; sent in "
"reply to a <response/> element or an <auth/> element with initial "
"response data.");
break;
case gloox::SaslNotAuthorized:
errMsg.AddString("detail", "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 "
"<response/> element or an <auth/> element with initial response data.");
break;
case gloox::SaslTemporaryAuthFailure:
errMsg.AddString("detail", "The authentication failed because of a temporary "
"error condition within the receiving entity; sent in reply to an "
"<auth/> element or <response/> element.");
break;
case gloox::NonSaslConflict:
errMsg.AddString("detail", "Resource conflict, see XEP-0078.");
break;
case gloox::NonSaslNotAcceptable:
errMsg.AddString("detail", "Required information not provided, "
"see XEP-0078.");
break;
case gloox::NonSaslNotAuthorized:
errMsg.AddString("detail", "Incorrect credentials.");
break;
default:
break;
}
break;
}
case gloox::ConnNotConnected:
errMsg.AddString("error", "There is no active connection.");
break;
default:
break;
}
_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", id);
msg.AddString("chat_id", id);
msg.AddString("subject", subject);
msg.AddString("body", body);
_SendMessage(&msg);
}
status_t
JabberHandler::_SetupAvatarCache()
{
if (fAvatarCachePath.InitCheck() == B_OK)
return B_OK;
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return B_ERROR;
path.Append("Caya");
path.Append("Cache");
path.Append(Signature());
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 Caya?
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_AVATAR_SET);
msg.AddString("user_id", id);
}
msg.AddRef("ref", &ref);
_SendMessage(&msg);
}
CayaStatus
JabberHandler::_GlooxStatusToCaya(gloox::Presence::PresenceType type)
{
switch (type) {
case gloox::Presence::Available:
case gloox::Presence::Chat:
return CAYA_ONLINE;
case gloox::Presence::Away:
return CAYA_AWAY;
case gloox::Presence::XA:
return CAYA_CUSTOM_STATUS;
case gloox::Presence::DND:
return CAYA_DO_NOT_DISTURB;
case gloox::Presence::Unavailable:
return CAYA_OFFLINE;
default:
break;
}
return CAYA_OFFLINE;
}
BMessage
JabberHandler::_SettingsTemplate(const char* username, bool serverOption)
{
BMessage stemplate('IMst');
BMessage usernameText;
usernameText.AddString("name", "username");
usernameText.AddString("description", username);
usernameText.AddInt32("type", 'CSTR');
stemplate.AddMessage("setting", &usernameText);
BMessage passwordText;
passwordText.AddString("name", "password");
passwordText.AddString("description", "Password");
passwordText.AddInt32("type", 'CSTR');
passwordText.AddBool("is_secret", true);
stemplate.AddMessage("setting", &passwordText);
BMessage serverText;
serverText.AddString("name", "server");
serverText.AddString("description", "Server");
serverText.AddInt32("type", 'CSTR');
if (serverOption == true)
stemplate.AddMessage("setting", &serverText);
BMessage resourceText;
resourceText.AddString("name", "resource");
resourceText.AddString("description", "Resource");
resourceText.AddInt32("type", 'CSTR');
resourceText.AddString("default", "Caya");
stemplate.AddMessage("setting", &resourceText);
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", CAYA_ONLINE);
_SendMessage(&msg);
// Notify our online status
BString content(fUsername);
content << " has logged in!";
_Notify(B_INFORMATION_NOTIFICATION, "Connected",
content.String());
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", CAYA_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
HandleError(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<BMessage> msgs;
BMessage contactListMsg(IM_MESSAGE);
contactListMsg.AddInt32("im_what", IM_CONTACT_LIST);
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("name", name);
infoMsg.AddInt32("subscription", subscription);
infoMsg.AddInt32("status", CAYA_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<BMessage>::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);
}
}
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;
// 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)
{
printf("------ %d\n", state);
// We're interested only in some states
if (state == gloox::ChatStateActive || state == gloox::ChatStateInvalid)
return;
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_CONTACT_STARTED_TYPING);
break;
case gloox::ChatStatePaused:
msg.AddInt32("im_what", IM_CONTACT_STOPPED_TYPING);
break;
case gloox::ChatStateGone:
// TODO
break;
default:
break;
}
_SendMessage(&msg);
}
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)
{
BMessage msg(IM_MESSAGE);
msg.AddInt32("im_what", IM_STATUS_SET);
msg.AddString("user_id", item.jidJID().full().c_str());
msg.AddInt32("status", _GlooxStatusToCaya(type));
msg.AddString("resource", resource.c_str());
msg.AddString("message", presenceMsg.c_str());
_SendMessage(&msg);
}
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("name", item.name().c_str());
msg.AddInt32("subscription", item.subscription());
msg.AddInt32("status", _GlooxStatusToCaya(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)
printf("%s\n", msg.c_str());
}
void
JabberHandler::handleVCard(const gloox::JID& jid, const gloox::VCard* card)
{
if (!card)
return;
gloox::VCard::Name name = card->name();
gloox::VCard::Photo photo = card->photo();
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("nick", card->nickname().c_str());
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
}