PageRenderTime 29ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llxmppclient.cpp

https://bitbucket.org/lindenlab/viewer-xmpp/
C++ | 1070 lines | 743 code | 185 blank | 142 comment | 110 complexity | 55d4ace3b6f781e21f33202d22fe13c9 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause
  1. /**
  2. * @file llxmppclient.cpp
  3. * @brief hooks into strophe xmpp library
  4. *
  5. * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llxmppclient.h"
  28. #include <cctype>
  29. #include <queue>
  30. #include <map>
  31. #include <utility>
  32. #include <iostream>
  33. #include "linden_common.h"
  34. #include "llavatarnamecache.h"
  35. #include "llstatusbar.h"
  36. #include "llbufferstream.h"
  37. #include "llframetimer.h"
  38. #include "llhost.h"
  39. #include "lliosocket.h"
  40. #include "llmemtype.h"
  41. #include "llpumpio.h"
  42. #include "llversionviewer.h"
  43. #include "llapr.h"
  44. #include "apr_poll.h"
  45. #include "apr_pools.h"
  46. #include "apr_portable.h"
  47. #include <strophe/strophe.h>
  48. static std::map<llxmpp_status_t, std::string> llxmpp_status_text;
  49. static S32 MAX_RETRIES = 5;
  50. static S32 BACKOFF_SECONDS = 5;
  51. static bool forbidden(unsigned char c)
  52. {
  53. return c < 0x20 || c >= 0x7f || strchr("&'/:<>@-%", c) != NULL;
  54. }
  55. static std::string sl_name_to_xmpp(const std::string &sl_name)
  56. {
  57. static const char *hex = "0123456789abcdef";
  58. std::string out;
  59. for (unsigned i = 0; i < sl_name.length(); i++)
  60. {
  61. unsigned char c = sl_name[i];
  62. if (forbidden(c)) {
  63. out.push_back('%');
  64. out.push_back(hex[c >> 4]);
  65. out.push_back(hex[c & 0xf]);
  66. }
  67. else if (c == ' ')
  68. out.push_back('-');
  69. else
  70. out.push_back(tolower(c));
  71. }
  72. return out;
  73. }
  74. static int parse_hex(unsigned char c)
  75. {
  76. if (c >= '0' && c <= '9')
  77. return c - '0';
  78. else if (c >= 'a' && c <= 'f')
  79. return c - 'a';
  80. else if (c >= 'A' && c <= 'F')
  81. return c - 'A';
  82. else
  83. return -1;
  84. }
  85. static std::string xmpp_name_to_sl(const std::string &xmpp_name)
  86. {
  87. std::string out;
  88. for (unsigned i = 0; i < xmpp_name.length(); i++)
  89. {
  90. int c = xmpp_name[i];
  91. if (c == '%') {
  92. if (i + 2 < xmpp_name.length()) {
  93. c = parse_hex(xmpp_name[i+1]) << 4 | parse_hex(xmpp_name[i+2]);
  94. i += 2;
  95. }
  96. else
  97. c = -1;
  98. }
  99. else if (c == '-')
  100. c = ' ';
  101. else if (forbidden(c))
  102. c = -1;
  103. if (c != -1)
  104. out.push_back(c);
  105. }
  106. return out;
  107. }
  108. static int version_handler(xmpp_conn_t *const conn,
  109. xmpp_stanza_t *const stanza,
  110. void *const userdata)
  111. {
  112. xmpp_stanza_t *reply, *query, *name, *version, *text;
  113. char *ns;
  114. LLXMPPClient *xmpp_client = (LLXMPPClient *) userdata;
  115. xmpp_ctx_t *ctx = xmpp_client->mCtx;
  116. LL_INFOS("XMPP") << "Received version request from "
  117. << xmpp_stanza_get_attribute (stanza, "from") << llendl;
  118. reply = xmpp_stanza_new (ctx);
  119. xmpp_stanza_set_name (reply, "iq");
  120. xmpp_stanza_set_type (reply, "result");
  121. xmpp_stanza_set_id (reply, xmpp_stanza_get_id (stanza));
  122. xmpp_stanza_set_attribute (reply, "to",
  123. xmpp_stanza_get_attribute (stanza, "from"));
  124. query = xmpp_stanza_new (ctx);
  125. xmpp_stanza_set_name (query, "query");
  126. ns = xmpp_stanza_get_ns (xmpp_stanza_get_children (stanza));
  127. if (ns)
  128. {
  129. xmpp_stanza_set_ns (query, ns);
  130. }
  131. name = xmpp_stanza_new (ctx);
  132. xmpp_stanza_set_name (name, "name");
  133. xmpp_stanza_add_child (query, name);
  134. text = xmpp_stanza_new (ctx);
  135. xmpp_stanza_set_text (text, "secondlife viewer");
  136. xmpp_stanza_add_child (name, text);
  137. version = xmpp_stanza_new (ctx);
  138. xmpp_stanza_set_name (version, "version");
  139. xmpp_stanza_add_child (query, version);
  140. text = xmpp_stanza_new (ctx);
  141. xmpp_stanza_set_text (text, "1.0");
  142. xmpp_stanza_add_child (version, text);
  143. xmpp_stanza_add_child (reply, query);
  144. xmpp_send (conn, reply);
  145. xmpp_stanza_release (reply);
  146. return 1;
  147. }
  148. static llxmpp_presence_t presence_to_enum(char *presence)
  149. {
  150. if (presence)
  151. for (int i = 0; llxmpp_presence_text[i]; ++i)
  152. if (strcmp(presence, llxmpp_presence_text[i]) == 0)
  153. return (llxmpp_presence_t)i;
  154. return LLXMPP_PRESENCE_AVAILABLE;
  155. }
  156. static llxmpp_show_t show_to_enum(char *show)
  157. {
  158. if (show)
  159. for (int i = 0; llxmpp_show_text[i]; ++i)
  160. if (strcmp(show, llxmpp_show_text[i]) == 0)
  161. return (llxmpp_show_t)i;
  162. return LLXMPP_SHOW_AVAILABLE;
  163. }
  164. static int presence_handler(xmpp_conn_t *const conn,
  165. xmpp_stanza_t *const stanza,
  166. void *const userdata)
  167. {
  168. LLXMPPClient *xmpp_client = (LLXMPPClient *) userdata;
  169. presence_info pi = presence_info();
  170. LL_INFOS("XMPP") << "in presence_handler." << llendl;
  171. /* <presence/> stanza attributes */
  172. char *from_jid = xmpp_stanza_get_attribute(stanza, "from");
  173. char *to_jid = xmpp_stanza_get_attribute(stanza, "to");
  174. char *type = xmpp_stanza_get_attribute(stanza, "type");
  175. pi.presence = presence_to_enum(type);
  176. bool online = (pi.presence != LLXMPP_PRESENCE_UNAVAILABLE);
  177. /* <show/> stanza */
  178. xmpp_stanza_t *show_stanza = xmpp_stanza_get_child_by_name(stanza, "show");
  179. if (show_stanza)
  180. {
  181. char *p_show = xmpp_stanza_get_text(show_stanza);
  182. pi.show = show_to_enum(p_show);
  183. xmpp_free(xmpp_client->mCtx, p_show);
  184. }
  185. /* <status/> stanza */
  186. xmpp_stanza_t *status_stanza = xmpp_stanza_get_child_by_name(stanza, "status");
  187. if (status_stanza)
  188. {
  189. char *p_status = xmpp_stanza_get_text(status_stanza);
  190. if (p_status) pi.status = p_status;
  191. xmpp_free(xmpp_client->mCtx, p_status);
  192. }
  193. /* <priority/> stanza */
  194. xmpp_stanza_t *priority_stanza = xmpp_stanza_get_child_by_name(stanza, "priority");
  195. if (priority_stanza)
  196. {
  197. char *p_priority = xmpp_stanza_get_text(priority_stanza);
  198. pi.priority = strtol(p_priority, NULL, 10);
  199. xmpp_free(xmpp_client->mCtx, p_priority);
  200. }
  201. LL_INFOS("XMPP") << " presence pi.show - " << pi.show << llendl;
  202. LL_INFOS("XMPP") << " presence pi.status - " << pi.status << llendl;
  203. LL_INFOS("XMPP") << " presence pi.presence - " << pi.presence << llendl;
  204. LL_INFOS("XMPP") << " presence pi.priority - " << pi.priority << llendl;
  205. /* <x/> and <item/> stanza */
  206. xmpp_stanza_t *x_stanza = xmpp_stanza_get_child_by_ns(stanza, "http://jabber.org/protocol/muc#user");
  207. if (! x_stanza)
  208. return 1;
  209. LL_INFOS("XMPP") << " found x stanza with xmlns \"http://jabber.org/protocol/muc#user\"" << llendl;
  210. xmpp_stanza_t *item_stanza = xmpp_stanza_get_child_by_name(x_stanza, "item");
  211. if (! item_stanza)
  212. return 1;
  213. LL_INFOS("XMPP") << " found item stanza" << llendl;
  214. /* presence information about this other person in the room */
  215. char *p_affiliation = xmpp_stanza_get_attribute(item_stanza, "affiliation");
  216. char *p_role = xmpp_stanza_get_attribute(item_stanza, "role");
  217. /* If p_jid is null, that's because we're in a semi-anonymous room, and we
  218. * aren't a moderator. This use case is not supported in SL. */
  219. char *p_jid = xmpp_stanza_get_attribute(item_stanza, "jid");
  220. /* This is only for presence that we assert about ourselves, not others */
  221. char *code = NULL;
  222. xmpp_stanza_t *x_status_stanza = xmpp_stanza_get_child_by_name(x_stanza, "status");
  223. if (x_status_stanza) {
  224. code = xmpp_stanza_get_attribute(x_status_stanza, "code");
  225. LL_INFOS("XMPP") << " our own presence status code: " << code << llendl;
  226. }
  227. LL_INFOS("XMPP") << " presence from - " << from_jid << llendl;
  228. LL_INFOS("XMPP") << " presence to - " << to_jid << llendl;
  229. LL_INFOS("XMPP") << " presence affiliation - " << p_affiliation << llendl;
  230. LL_INFOS("XMPP") << " presence role - " << p_role << llendl;
  231. LL_INFOS("XMPP") << " presence jid - " << p_jid << llendl;
  232. xmpp_client->handlePresence(from_jid, p_jid, p_affiliation, p_role, pi, online);
  233. return 1;
  234. }
  235. static int message_handler(xmpp_conn_t *const conn,
  236. xmpp_stanza_t *const stanza,
  237. void *const userdata)
  238. {
  239. char *subject = NULL;
  240. char *body = NULL;
  241. LLXMPPClient *xmpp_client = (LLXMPPClient *) userdata;
  242. LL_DEBUGS("XMPP") << "in message_handler." << llendl;
  243. char *type_attr = xmpp_stanza_get_attribute(stanza, "type");
  244. char *from_attr = xmpp_stanza_get_attribute(stanza, "from");
  245. if (! from_attr)
  246. return 1;
  247. LL_DEBUGS("XMPP") << " handling message type " << type_attr
  248. << " from " << from_attr << llendl;
  249. xmpp_stanza_t *subject_stanza = xmpp_stanza_get_child_by_name(stanza, "subject");
  250. if (subject_stanza)
  251. subject = xmpp_stanza_get_text(subject_stanza);
  252. xmpp_stanza_t *body_stanza = xmpp_stanza_get_child_by_name(stanza, "body");
  253. if (body_stanza)
  254. body = xmpp_stanza_get_text(body_stanza);
  255. // Comments are excepted from RFC 3921, Section 2.1.1
  256. if (type_attr && !strcmp(type_attr, "chat"))
  257. {
  258. /* chat -- The message is sent in the context of a one-to-one chat
  259. * conversation. A compliant client SHOULD present the message in an
  260. * interface enabling one-to-one chat between the two parties,
  261. * including an appropriate conversation history. */
  262. xmpp_client->handleChatMessage(from_attr, subject, body);
  263. LL_DEBUGS("XMPP") << " handleChatMessage called" << llendl;
  264. }
  265. else if (type_attr && !strcmp(type_attr, "error"))
  266. {
  267. /* error -- An error has occurred related to a previous message sent by
  268. * the sender (for details regarding stanza error syntax, refer to
  269. * [XMPP‑CORE]). A compliant client SHOULD present an appropriate
  270. * interface informing the sender of the nature of the error. */
  271. /*
  272. <message
  273. from='darkcave@chat.shakespeare.lit'
  274. to='wiccarocks@shakespeare.lit/laptop'
  275. type='error'>
  276. <body>I'll give thee a wind.</body>
  277. <error type='modify'>
  278. <bad-request xmlns=''/>
  279. </error>
  280. </message>
  281. */
  282. char *errtype = NULL;
  283. char *errword = NULL;
  284. xmpp_stanza_t *type_stanza = xmpp_stanza_get_child_by_name(stanza, "error");
  285. if (type_stanza)
  286. errtype = xmpp_stanza_get_attribute(type_stanza, "type");
  287. xmpp_stanza_t *error_stanza = xmpp_stanza_get_child_by_ns(stanza, "urn:ietf:params:xml:ns:xmpp-stanzas");
  288. if (error_stanza)
  289. errword = xmpp_stanza_get_name(error_stanza);
  290. xmpp_client->handleErrorMessage(from_attr, subject, body, errtype, errword);
  291. LL_DEBUGS("XMPP") << " handleErrorMessage called" << llendl;
  292. }
  293. else if (type_attr && !strcmp(type_attr, "groupchat"))
  294. {
  295. /* groupchat -- The message is sent in the context of a multi-user chat
  296. * environment (similar to that of [IRC]). A compliant client SHOULD
  297. * present the message in an interface enabling many-to-many chat
  298. * between the parties, including a roster of parties in the chatroom
  299. * and an appropriate conversation history. Full definition of
  300. * XMPP-based groupchat protocols is out of scope for this memo (for
  301. * details see [JEP‑0045]). */
  302. xmpp_client->handleGroupchatMessage(from_attr, subject, body);
  303. LL_DEBUGS("XMPP") << " handleGroupchatMessage called" << llendl;
  304. }
  305. else if (type_attr && !strcmp(type_attr, "headline"))
  306. {
  307. /* headline -- The message is probably generated by an automated
  308. * service that delivers or broadcasts content (news, sports, market
  309. * information, RSS feeds, etc.). No reply to the message is expected,
  310. * and a compliant client SHOULD present the message in an interface
  311. * that appropriately differentiates the message from standalone
  312. * messages, chat sessions, or groupchat sessions (e.g., by not
  313. * providing the recipient with the ability to reply). */
  314. xmpp_client->handleHeadlineMessage(from_attr, subject, body);
  315. LL_DEBUGS("XMPP") << " handleHeadlineMessage called" << llendl;
  316. }
  317. else // default: if (!strcmp(type_attr, "normal"))
  318. {
  319. /* normal -- The message is a single message that is sent outside the
  320. * context of a one-to-one conversation or groupchat, and to which it
  321. * is expected that the recipient will reply. A compliant client SHOULD
  322. * present the message in an interface enabling the recipient to reply,
  323. * but without a conversation history. */
  324. xmpp_client->handleNormalMessage(from_attr, subject, body);
  325. LL_DEBUGS("XMPP") << " handleNormalMessage called" << llendl;
  326. }
  327. xmpp_free(xmpp_client->mCtx, subject);
  328. xmpp_free(xmpp_client->mCtx, body);
  329. return 1;
  330. }
  331. static void xmpp_connection_handler(xmpp_conn_t *const conn,
  332. const xmpp_conn_event_t status,
  333. const int error,
  334. xmpp_stream_error_t *const stream_error,
  335. void * const userdata)
  336. {
  337. LLXMPPClient *xmpp_client = (LLXMPPClient *) userdata;
  338. LL_INFOS("XMPP") << "xmpp_connection_handler: " << (int)status << llendl;
  339. if (status == XMPP_CONN_CONNECT)
  340. xmpp_client->onConnect ();
  341. else
  342. xmpp_client->onDisconnect ();
  343. }
  344. // see typedef xmpp_log_handler in strophe.h
  345. static void xmpp_logging_handler(void * const userdata,
  346. const xmpp_log_level_t level,
  347. const char * const area,
  348. const char * const msg)
  349. {
  350. LLXMPPClient *xmpp_client = (LLXMPPClient *) userdata;
  351. switch (level) {
  352. case XMPP_LEVEL_DEBUG:
  353. LL_DEBUGS2("XMPP", area) << msg << LL_ENDL;
  354. break;
  355. case XMPP_LEVEL_INFO:
  356. LL_INFOS2("XMPP", area) << msg << LL_ENDL;
  357. // Some connection errors come back at the info level, uh.
  358. xmpp_client->onError(msg);
  359. break;
  360. default:
  361. case XMPP_LEVEL_WARN:
  362. case XMPP_LEVEL_ERROR:
  363. // Nothing in libstrophe at the ERROR level is fatal, but our
  364. // ERR level is, so report these at WARN level.
  365. LL_WARNS2("XMPP", area) << msg << LL_ENDL;
  366. xmpp_client->onError(msg);
  367. break;
  368. }
  369. }
  370. LLXMPPClient::LLXMPPClient(const std::string& server, U16 port,
  371. bool tls,
  372. const std::string& username,
  373. const std::string& password,
  374. apr_pool_t *pool)
  375. :
  376. mConnected(LLXMPP_DISCONNECTED),
  377. mRetryAttempt(0),
  378. mLastRetryTime(0),
  379. mPassword(password),
  380. mServer(server),
  381. mPool(pool)
  382. {
  383. mUsername = sl_name_to_xmpp(username);
  384. llxmpp_status_text[LLXMPP_DISCONNECTED] = "XMPP Disconnected";
  385. llxmpp_status_text[LLXMPP_PENDING] = "XMPP connecting...";
  386. llxmpp_status_text[LLXMPP_CONNECTED] = "XMPP Connected!";
  387. char version[ 40 ];
  388. snprintf(version, sizeof (version), "%d.%d.%d",
  389. LL_VERSION_MAJOR,
  390. LL_VERSION_MINOR,
  391. LL_VERSION_PATCH);
  392. mJid = mUsername + "@" + mServer + "/secondlife-viewer-" + version;
  393. xmpp_initialize ();
  394. mLog.handler = xmpp_logging_handler;
  395. mLog.userdata = this;
  396. connect ();
  397. }
  398. LLXMPPClient::~LLXMPPClient()
  399. {
  400. xmpp_conn_release (mConn);
  401. xmpp_ctx_free (mCtx);
  402. xmpp_shutdown ();
  403. }
  404. void LLXMPPClient::onMaxDisconnects()
  405. {
  406. }
  407. bool LLXMPPClient::connect()
  408. {
  409. if (mRetryAttempt > MAX_RETRIES)
  410. return false;
  411. if (mRetryAttempt == MAX_RETRIES)
  412. {
  413. ++mRetryAttempt; // Push it over the edge to stop error messages
  414. LL_INFOS("XMPP") << "Hit max retries, won't try to connect anymore." << llendl;
  415. onMaxDisconnects();
  416. return false;
  417. }
  418. F64 now = LLFrameTimer::getTotalSeconds();
  419. F64 retry_delay = now - mLastRetryTime;
  420. // If we're on the 2-Nth retry, don't continue until some time has elapsed.
  421. if (mRetryAttempt > 0 && retry_delay < mRetryAttempt * BACKOFF_SECONDS)
  422. return false;
  423. LL_INFOS("XMPP") << "Attempting to connect to XMPP server, backed off " << retry_delay
  424. << " seconds, try number " << mRetryAttempt << llendl;
  425. mLastRetryTime = now;
  426. mCtx = xmpp_ctx_new(NULL, &mLog);
  427. mConn = xmpp_conn_new(mCtx);
  428. xmpp_conn_set_jid(mConn, mJid.c_str());
  429. xmpp_conn_set_pass(mConn, mPassword.c_str());
  430. xmpp_conn_set_tls_cafile(mConn, gDirUtilp->getCAFile().c_str());
  431. if (xmpp_connect_client(mConn,
  432. mServer.c_str(), 5222,
  433. xmpp_connection_handler, this) == 0)
  434. {
  435. mConnected = LLXMPP_PENDING;
  436. return true;
  437. } else {
  438. mConnected = LLXMPP_DISCONNECTED;
  439. return false;
  440. }
  441. }
  442. bool LLXMPPClient::join(const std::string& channel, const LLUUID& session_id)
  443. {
  444. xmpp_group group;
  445. if (getGroupData(channel, group))
  446. {
  447. LL_INFOS("XMPP") << "already present in group chat " << channel
  448. << llendl;
  449. return true;
  450. }
  451. std::string muc = sl_name_to_xmpp(channel);
  452. std::string client_muc_jid =
  453. muc + "@conference." + mServer + "/" + mUsername;
  454. std::string muc_jid = muc + "@conference." + mServer;
  455. LL_INFOS("XMPP") << "joining " << client_muc_jid << llendl;
  456. sendPresence(LLXMPP_PRESENCE_AVAILABLE, mJid, client_muc_jid);
  457. mGroup_name_to_data[muc] = xmpp_group (muc, session_id, muc_jid, client_muc_jid);
  458. return true;
  459. }
  460. bool LLXMPPClient::getGroupData(const std::string& name, xmpp_group& group, bool del)
  461. {
  462. std::string muc = sl_name_to_xmpp(name);
  463. group_name_to_data_map::iterator it = mGroup_name_to_data.find(muc);
  464. if (it == mGroup_name_to_data.end())
  465. return false;
  466. group = it->second;
  467. if (del)
  468. mGroup_name_to_data.erase(it);
  469. return true;
  470. }
  471. static std::string xml_escape(const std::string &xml)
  472. {
  473. std::string out = xml;
  474. size_t pos;
  475. pos = out.find('<');
  476. for ( ;
  477. pos != std::string::npos;
  478. pos = out.find('<', pos))
  479. {
  480. out.replace(pos, 1, "&lt;");
  481. }
  482. pos = out.find('>');
  483. for ( ;
  484. pos != std::string::npos;
  485. pos = out.find('>', pos))
  486. {
  487. out.replace(pos, 1, "&gt;");
  488. }
  489. return out;
  490. }
  491. bool LLXMPPClient::say(const std::string& channel, const std::string& message)
  492. {
  493. xmpp_group group;
  494. if (!getGroupData(channel, group))
  495. {
  496. LL_INFOS("XMPP") << "mGroup_name_to_data didn't have " << channel
  497. << llendl;
  498. return false;
  499. }
  500. xmpp_stanza_t *message_stanza, *body, *text;
  501. message_stanza = xmpp_stanza_new (mCtx);
  502. xmpp_stanza_set_name (message_stanza, "message");
  503. xmpp_stanza_set_type (message_stanza, "groupchat");
  504. LL_INFOS("XMPP") << "say -- muc jid is:" << group.group_jid.c_str()
  505. << llendl;
  506. xmpp_stanza_set_attribute (message_stanza, "to", group.group_jid.c_str());
  507. body = xmpp_stanza_new (mCtx);
  508. xmpp_stanza_set_name (body, "body");
  509. text = xmpp_stanza_new (mCtx);
  510. xmpp_stanza_set_text (text, xml_escape(message).c_str ());
  511. xmpp_stanza_add_child (body, text);
  512. xmpp_stanza_add_child (message_stanza, body);
  513. xmpp_send (mConn, message_stanza);
  514. xmpp_stanza_release (message_stanza);
  515. return true;
  516. }
  517. bool LLXMPPClient::leave(const std::string& channel)
  518. {
  519. xmpp_group group;
  520. if (!getGroupData(channel, group, true))
  521. {
  522. LL_INFOS("XMPP") << "mGroup_name_to_data didn't have " << channel
  523. << llendl;
  524. return false;
  525. }
  526. LL_INFOS("XMPP") << "leaving " << group.group_jid << llendl;
  527. sendPresence(LLXMPP_PRESENCE_UNAVAILABLE, group.group_jid);
  528. return true;
  529. }
  530. bool LLXMPPClient::check_for_inbound_chat()
  531. {
  532. unsigned long timeout = 1; /* millisecond? */
  533. switch (mConnected) {
  534. case LLXMPP_DISCONNECTED:
  535. connect();
  536. break;
  537. case LLXMPP_CONNECTED:
  538. xmpp_run_once (mCtx, timeout);
  539. break;
  540. case LLXMPP_PENDING:
  541. gStatusBar->setXMPPStatus(llxmpp_status_text[mConnected], mServer);
  542. LL_INFOS("XMPP") << "check_for_inbound_chat: connection status still pending" << llendl;
  543. xmpp_run_once (mCtx, timeout);
  544. break;
  545. }
  546. return true;
  547. }
  548. void LLXMPPClient::handleInboundChat(inbound_xmpp_chat *iic,
  549. const LLAvatarNameCache::key& key,
  550. const LLAvatarName& av_name)
  551. {
  552. /* Don't tell our subclass about this bit of chat until we have
  553. all the details. Wait until we have the sender's agent-id. */
  554. LL_INFOS("XMPP") << "in LLXMPPClient::handleInboundChat on callback from LLAvatarNameCache" << llendl;
  555. iic->from_id = av_name.mAgentID;
  556. LL_INFOS("XMPP") << "dque iic, from=" << iic->from << llendl;
  557. LL_INFOS("XMPP") << "dque iic, from_id=" << iic->from_id << llendl;
  558. LL_INFOS("XMPP") << "dque iic, from nick=" << iic->nickname << llendl;
  559. LL_INFOS("XMPP") << "dque iic, recipient=" << iic->recipient << llendl;
  560. LL_INFOS("XMPP") << "dque iic, recipient_id="
  561. << iic->recipient_id << llendl;
  562. LL_INFOS("XMPP") << "dque iic, utf8_text=" << iic->utf8_text << llendl;
  563. if (iic->from_id == LLUUID::null || av_name.mIsTemporaryName)
  564. {
  565. LL_INFOS("XMPP") << "null agent-id or dummy entry for '"
  566. << iic->from << "'" << llendl;
  567. }
  568. else
  569. {
  570. }
  571. delete iic;
  572. }
  573. void LLXMPPClient::displayPresence(const LLUUID& recipient_id,
  574. const roster_item& roster_i,
  575. bool modify_existing)
  576. {
  577. }
  578. void LLXMPPClient::setRosterAgentID(roster_item *roster_i,
  579. const LLAvatarNameCache::key& key,
  580. const LLAvatarName& av_name)
  581. {
  582. LL_DEBUGS("XMPP") << "setRosterAgentID for " << key.asString()
  583. << " to " << av_name.mAgentID << llendl;
  584. roster_i->agent_id = av_name.mAgentID;
  585. }
  586. void LLXMPPClient::handlePresence(char *room_jid, char *real_jid, char *affiliation, char *role, presence_info& pi, bool online)
  587. {
  588. if (!real_jid) {
  589. LL_WARNS("XMPP") << "presence without real jid, that's not cool." << llendl;
  590. return;
  591. }
  592. /* The room name is found on the from jid. */
  593. char *room_name = xmpp_jid_node (mCtx, room_jid);
  594. char *room_nick = xmpp_jid_resource (mCtx, room_jid);
  595. char *real_nick = xmpp_jid_node(mCtx, real_jid);
  596. if (!room_nick)
  597. room_nick = real_nick;
  598. group_name_to_data_map::iterator it = mGroup_name_to_data.find (room_name);
  599. if (it == mGroup_name_to_data.end())
  600. {
  601. LL_INFOS("XMPP") << " presence mGroup_name_to_data didn't have "
  602. << room_name << llendl;
  603. return;
  604. }
  605. room_jid_to_roster_map::iterator it_roster = it->second.roster.find (room_jid);
  606. if (it_roster != it->second.roster.end())
  607. {
  608. it_roster->second.online = online;
  609. it_roster->second.presence = pi;
  610. displayPresence(it->second.sl_session, it_roster->second, false);
  611. }
  612. else
  613. {
  614. it->second.roster[room_jid] = roster_item(affiliation,
  615. role,
  616. real_jid,
  617. room_nick,
  618. pi,
  619. online);
  620. // Look up the agent id for this presence
  621. LLAvatarName av_name;
  622. LLAvatarNameCache::key key(real_nick);
  623. if (LLAvatarNameCache::getKey(key, &av_name)) {
  624. LL_INFOS("XMPP") << "first try, got id for " << key.asString()
  625. << " ... " << av_name.mAgentID << llendl;
  626. it->second.roster[room_jid].agent_id = av_name.mAgentID;
  627. } else {
  628. LL_INFOS("XMPP") << key.asString() << " not found in cache, scheduling a callback if we find it later" << llendl;
  629. LLAvatarNameCache::getKey(key,
  630. boost::bind(&LLXMPPClient::setRosterAgentID,
  631. this, &it->second.roster[room_jid], _1, _2));
  632. }
  633. displayPresence(it->second.sl_session, it->second.roster[room_jid], true);
  634. }
  635. }
  636. /***************************************************/
  637. void LLXMPPClient::onConnect()
  638. {
  639. mConnected = LLXMPP_CONNECTED;
  640. mRetryAttempt = 0;
  641. LL_INFOS("XMPP") << "connected" << llendl;
  642. gStatusBar->setXMPPStatus(llxmpp_status_text[mConnected], mServer);
  643. xmpp_handler_add (mConn, version_handler,
  644. "jabber:iq:version", "iq", NULL, this);
  645. xmpp_handler_add (mConn, message_handler, NULL, "message", NULL, this);
  646. xmpp_handler_add (mConn, presence_handler, NULL, "presence", NULL, this);
  647. /* Send initial <presence/> so that we appear online to contacts */
  648. sendPresence(LLXMPP_PRESENCE_AVAILABLE);
  649. }
  650. void LLXMPPClient::onError(const std::string& message)
  651. {
  652. mLastError = message;
  653. }
  654. // This is for server presence
  655. void LLXMPPClient::sendPresence(llxmpp_presence_t presence)
  656. {
  657. xmpp_stanza_t* pres;
  658. pres = xmpp_stanza_new (mCtx);
  659. xmpp_stanza_set_name (pres, "presence");
  660. if (presence) // AVAILABLE doesn't have a type
  661. xmpp_stanza_set_attribute (pres, "type", llxmpp_presence_text[presence]);
  662. xmpp_send (mConn, pres);
  663. xmpp_stanza_release (pres);
  664. }
  665. // This is for room presence
  666. void LLXMPPClient::sendPresence(llxmpp_presence_t presence,
  667. const std::string& to)
  668. {
  669. xmpp_stanza_t* pres;
  670. pres = xmpp_stanza_new (mCtx);
  671. xmpp_stanza_set_name (pres, "presence");
  672. xmpp_stanza_set_attribute (pres, "to", to.c_str());
  673. if (presence) // AVAILABLE doesn't have a type
  674. xmpp_stanza_set_attribute (pres, "type", llxmpp_presence_text[presence]);
  675. xmpp_send (mConn, pres);
  676. xmpp_stanza_release (pres);
  677. }
  678. // This is for room presence, too
  679. void LLXMPPClient::sendPresence(llxmpp_presence_t presence,
  680. const std::string& from,
  681. const std::string& to)
  682. {
  683. xmpp_stanza_t* pres;
  684. pres = xmpp_stanza_new (mCtx);
  685. xmpp_stanza_set_name (pres, "presence");
  686. xmpp_stanza_set_attribute (pres, "from", from.c_str());
  687. xmpp_stanza_set_attribute (pres, "to", to.c_str());
  688. if (presence) // AVAILABLE doesn't have a type
  689. xmpp_stanza_set_attribute (pres, "type", llxmpp_presence_text[presence]);
  690. xmpp_send (mConn, pres);
  691. xmpp_stanza_release (pres);
  692. }
  693. void LLXMPPClient::onDisconnect()
  694. {
  695. xmpp_stop (mCtx);
  696. mConnected = LLXMPP_DISCONNECTED;
  697. ++mRetryAttempt;
  698. LL_INFOS("XMPP") << "disconnected: " << llendl;
  699. gStatusBar->setXMPPStatus(llxmpp_status_text[mConnected], mServer);
  700. /*
  701. delete conn;
  702. conn = NULL;
  703. delete ctx;
  704. ctx = NULL;
  705. */
  706. }
  707. void LLXMPPClient::handleGroupchatMessage(char *from_jid, char *subject, char *body)
  708. {
  709. LL_INFOS("XMPP") << "Incoming groupchat message from " << from_jid
  710. << " subject " << (subject ? subject : "(null subject)")
  711. << " body " << (body ? body : "(null body)")
  712. << llendl;
  713. /*
  714. Incoming message from
  715. the-lindens@conference.chat.aditi.lindenlab.com/seth.linden
  716. okokok
  717. For not-yet-understood reasons, sometimes a message is sent
  718. with a jid format of 'group-name@chat-host' but no /nick part.
  719. In this case, we'll use the chat room as the nick.
  720. FIXME: There will probably be display name issues with this.
  721. */
  722. char *room_name = xmpp_jid_node (mCtx, from_jid);
  723. char *from_nickname = xmpp_jid_resource (mCtx, from_jid);
  724. if (! from_nickname)
  725. from_nickname = (char *)"";
  726. LL_INFOS ("XMPP") << " group name is: " << room_name << llendl;
  727. LL_INFOS ("XMPP") << " sender jid is: " << from_jid << llendl;
  728. LL_INFOS ("XMPP") << " sender nick is: " << from_nickname << llendl;
  729. group_name_to_data_map::iterator it = mGroup_name_to_data.find (room_name);
  730. if (it == mGroup_name_to_data.end())
  731. {
  732. LL_INFOS("XMPP") << "mGroup_name_to_data didn't have "
  733. << room_name << llendl;
  734. }
  735. else
  736. {
  737. room_jid_to_roster_map::iterator it_roster = it->second.roster.find (from_jid);
  738. if (it_roster == it->second.roster.end())
  739. {
  740. LL_INFOS("XMPP") << "roster does not have presence for "
  741. << from_jid << llendl;
  742. }
  743. else
  744. {
  745. roster_item roster_i = it_roster->second;
  746. char *from_username = xmpp_jid_node(mCtx, roster_i.jid.c_str());
  747. if (!from_username) {
  748. }
  749. if (body && from_username) {
  750. LL_INFOS("XMPP") << "from username is " << from_username << llendl;
  751. inbound_xmpp_chat iic = inbound_xmpp_chat(xmpp_name_to_sl(from_username),
  752. roster_i.agent_id,
  753. from_nickname,
  754. xmpp_name_to_sl(room_name),
  755. it->second.sl_session,
  756. body);
  757. displayGroupchatMessage(iic);
  758. }
  759. if (subject && from_username) {
  760. displayGroupchatSubject(it->second.sl_session, from_username, subject);
  761. }
  762. xmpp_free (mCtx, from_username);
  763. }
  764. }
  765. xmpp_free (mCtx, room_name);
  766. }
  767. void LLXMPPClient::handleChatMessage(char *from_jid, char *subject, char *body)
  768. {
  769. inbound_xmpp_chat *iic = NULL;
  770. // FIXME: super lame, refactor this.
  771. body = body ? body : (char *)"";
  772. subject = subject ? subject : (char *)"";
  773. LL_INFOS("XMPP") << "Incoming private message from " << from_jid
  774. << " subject " << subject << " body " << body << llendl;
  775. char *sender_nick = xmpp_jid_resource (mCtx, from_jid);
  776. LL_INFOS ("XMPP") << " sender nick is: " << sender_nick << llendl;
  777. iic = new inbound_xmpp_chat (sender_nick, "", "", LLUUID::null, body);
  778. if (iic->from.length() > 0)
  779. LLAvatarNameCache::getKey(iic->from,
  780. boost::bind(&LLXMPPClient::handleInboundChat,
  781. this, iic, _1, _2));
  782. else
  783. delete iic;
  784. }
  785. void LLXMPPClient::displayUnusualMessage(const std::string& type,
  786. const std::string& subject,
  787. const std::string& body)
  788. {
  789. }
  790. void LLXMPPClient::displayGroupchatSubject(const LLUUID& session_id,
  791. const std::string& who,
  792. const std::string& subject)
  793. {
  794. }
  795. void LLXMPPClient::displayGroupchatMessage(const inbound_xmpp_chat& iic)
  796. {
  797. }
  798. void LLXMPPClient::displayGroupchatError(const inbound_xmpp_chat& iic)
  799. {
  800. }
  801. void LLXMPPClient::handleHeadlineMessage(char *from_jid, char *subject, char *body)
  802. {
  803. // FIXME: super lame, refactor this.
  804. body = body ? body : (char *)"";
  805. subject = subject ? subject : (char *)"";
  806. LL_INFOS("XMPP") << "Incoming headline message from " << from_jid
  807. << " subject " << subject << " body " << body << llendl;
  808. displayUnusualMessage("headline", subject, body);
  809. }
  810. void LLXMPPClient::handleNormalMessage(char *from_jid, char *subject, char *body)
  811. {
  812. // FIXME: super lame, refactor this.
  813. body = body ? body : (char *)"";
  814. subject = subject ? subject : (char *)"";
  815. LL_INFOS("XMPP") << "Incoming normal message from " << from_jid
  816. << " subject " << subject << " body " << body << llendl;
  817. displayUnusualMessage("normal", subject, body);
  818. }
  819. /* errtype is one of: modify, cancel, auth, wait.
  820. * errword is one of:
  821. * <bad-request/>
  822. * <conflict/>
  823. * <feature-not-implemented/>
  824. * <forbidden/>
  825. * <gone/>
  826. * <internal-server-error/>
  827. * <item-not-found/>
  828. * <jid-malformed/>
  829. * <not-acceptable/>
  830. * <not-allowed/>
  831. * <not-authorized/>
  832. * <payment-required/>
  833. * <recipient-unavailable/>
  834. * <redirect/>
  835. * <registration-required/>
  836. * <remote-server-not-found/>
  837. * <remote-server-timeout/>
  838. * <resource-constraint/>
  839. * <service-unavailable/>
  840. * <subscription-required/>
  841. * <undefined-condition/>
  842. * <unexpected-request/>
  843. */
  844. void LLXMPPClient::handleErrorMessage(char *from_jid, char *subject, char *body,
  845. char *errtype, char *errword)
  846. {
  847. // FIXME: super lame, refactor this.
  848. body = body ? body : (char *)"";
  849. subject = subject ? subject : (char *)"";
  850. errtype = errtype ? errtype : (char *)"";
  851. errword = errword ? errword : (char *)"";
  852. LL_INFOS("XMPP") << "Incoming error message from " << from_jid
  853. << " subject " << subject
  854. << " body " << body
  855. << " errtype " << errtype
  856. << " errword " << errword << llendl;
  857. char *room_name = xmpp_jid_node(mCtx, from_jid);
  858. group_name_to_data_map::iterator it = mGroup_name_to_data.find (room_name);
  859. if (it != mGroup_name_to_data.end())
  860. {
  861. inbound_xmpp_chat iic = inbound_xmpp_chat(xmpp_name_to_sl(room_name),
  862. it->second.sl_session,
  863. xmpp_name_to_sl(room_name),
  864. xmpp_name_to_sl(room_name),
  865. it->second.sl_session,
  866. errword);
  867. displayGroupchatMessage(iic);
  868. }
  869. else
  870. {
  871. displayUnusualMessage("error", subject, body);
  872. }
  873. xmpp_free(mCtx, room_name);
  874. }
  875. /*
  876. Local Variables:
  877. mode:c++
  878. c-file-style:"stroustrup"
  879. c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  880. indent-tabs-mode:t
  881. tab-width:4
  882. fill-column:99
  883. End:
  884. */