| 1 /* |
|
| 2 * gaim - Jabber Protocol Plugin |
|
| 3 * |
|
| 4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> |
|
| 5 * |
|
| 6 * This program is free software; you can redistribute it and/or modify |
|
| 7 * it under the terms of the GNU General Public License as published by |
|
| 8 * the Free Software Foundation; either version 2 of the License, or |
|
| 9 * (at your option) any later version. |
|
| 10 * |
|
| 11 * This program is distributed in the hope that it will be useful, |
|
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 * GNU General Public License for more details. |
|
| 15 * |
|
| 16 * You should have received a copy of the GNU General Public License |
|
| 17 * along with this program; if not, write to the Free Software |
|
| 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 19 * |
|
| 20 */ |
|
| 21 #include "internal.h" |
|
| 22 |
|
| 23 #include "debug.h" |
|
| 24 #include "notify.h" |
|
| 25 #include "server.h" |
|
| 26 #include "util.h" |
|
| 27 |
|
| 28 #include "buddy.h" |
|
| 29 #include "chat.h" |
|
| 30 #include "message.h" |
|
| 31 #include "xmlnode.h" |
|
| 32 |
|
| 33 #define JABBER_TYPING_NOTIFY_INT 15 |
|
| 34 |
|
| 35 void jabber_message_free(JabberMessage *jm) |
|
| 36 { |
|
| 37 if(jm->from) |
|
| 38 g_free(jm->from); |
|
| 39 if(jm->to) |
|
| 40 g_free(jm->to); |
|
| 41 if(jm->subject) |
|
| 42 g_free(jm->subject); |
|
| 43 if(jm->body) |
|
| 44 g_free(jm->body); |
|
| 45 if(jm->xhtml) |
|
| 46 g_free(jm->xhtml); |
|
| 47 if(jm->password) |
|
| 48 g_free(jm->password); |
|
| 49 if(jm->etc) |
|
| 50 g_list_free(jm->etc); |
|
| 51 |
|
| 52 g_free(jm); |
|
| 53 } |
|
| 54 |
|
| 55 static void handle_chat(JabberMessage *jm) |
|
| 56 { |
|
| 57 JabberID *jid = jabber_id_new(jm->from); |
|
| 58 char *from; |
|
| 59 |
|
| 60 JabberBuddy *jb; |
|
| 61 JabberBuddyResource *jbr; |
|
| 62 |
|
| 63 if(!jid) |
|
| 64 return; |
|
| 65 |
|
| 66 jb = jabber_buddy_find(jm->js, jm->from, TRUE); |
|
| 67 jbr = jabber_buddy_find_resource(jb, jid->resource); |
|
| 68 |
|
| 69 if(jabber_find_unnormalized_conv(jm->from, jm->js->gc->account)) { |
|
| 70 from = g_strdup(jm->from); |
|
| 71 } else if(jid->node) { |
|
| 72 if(jid->resource) { |
|
| 73 GaimConversation *conv; |
|
| 74 |
|
| 75 from = g_strdup_printf("%s@%s", jid->node, jid->domain); |
|
| 76 conv = jabber_find_unnormalized_conv(from, jm->js->gc->account); |
|
| 77 if(conv) { |
|
| 78 gaim_conversation_set_name(conv, jm->from); |
|
| 79 } |
|
| 80 g_free(from); |
|
| 81 } |
|
| 82 from = g_strdup(jm->from); |
|
| 83 } else { |
|
| 84 from = g_strdup(jid->domain); |
|
| 85 } |
|
| 86 |
|
| 87 if(!jm->xhtml && !jm->body) { |
|
| 88 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) |
|
| 89 serv_got_typing(jm->js->gc, from, 0, GAIM_TYPING); |
|
| 90 else |
|
| 91 serv_got_typing_stopped(jm->js->gc, from); |
|
| 92 } else { |
|
| 93 if(jbr) { |
|
| 94 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) |
|
| 95 jbr->capabilities |= JABBER_CAP_COMPOSING; |
|
| 96 if(jbr->thread_id) |
|
| 97 g_free(jbr->thread_id); |
|
| 98 jbr->thread_id = g_strdup(jbr->thread_id); |
|
| 99 } |
|
| 100 serv_got_im(jm->js->gc, from, jm->xhtml ? jm->xhtml : jm->body, 0, |
|
| 101 jm->sent); |
|
| 102 } |
|
| 103 |
|
| 104 g_free(from); |
|
| 105 jabber_id_free(jid); |
|
| 106 } |
|
| 107 |
|
| 108 static void handle_headline(JabberMessage *jm) |
|
| 109 { |
|
| 110 char *title; |
|
| 111 GString *body = g_string_new(""); |
|
| 112 GList *etc; |
|
| 113 |
|
| 114 title = g_strdup_printf(_("Message from %s"), jm->from); |
|
| 115 |
|
| 116 if(jm->xhtml) |
|
| 117 g_string_append(body, jm->xhtml); |
|
| 118 else if(jm->body) |
|
| 119 g_string_append(body, jm->body); |
|
| 120 |
|
| 121 for(etc = jm->etc; etc; etc = etc->next) { |
|
| 122 xmlnode *x = etc->data; |
|
| 123 const char *xmlns = xmlnode_get_attrib(x, "xmlns"); |
|
| 124 if(xmlns && !strcmp(xmlns, "jabber:x:oob")) { |
|
| 125 xmlnode *url, *desc; |
|
| 126 char *urltxt, *desctxt; |
|
| 127 |
|
| 128 url = xmlnode_get_child(x, "url"); |
|
| 129 desc = xmlnode_get_child(x, "desc"); |
|
| 130 |
|
| 131 if(!url || !desc) |
|
| 132 continue; |
|
| 133 |
|
| 134 urltxt = xmlnode_get_data(url); |
|
| 135 desctxt = xmlnode_get_data(desc); |
|
| 136 |
|
| 137 /* I'm all about ugly hacks */ |
|
| 138 if(body->len && !strcmp(body->str, jm->body)) |
|
| 139 g_string_printf(body, "<a href='%s'>%s</a>", |
|
| 140 urltxt, desctxt); |
|
| 141 else |
|
| 142 g_string_append_printf(body, "<br/><a href='%s'>%s</a>", |
|
| 143 urltxt, desctxt); |
|
| 144 |
|
| 145 g_free(urltxt); |
|
| 146 g_free(desctxt); |
|
| 147 } |
|
| 148 } |
|
| 149 |
|
| 150 gaim_notify_formatted(jm->js->gc, title, jm->subject ? jm->subject : title, |
|
| 151 NULL, body->str, NULL, NULL); |
|
| 152 |
|
| 153 g_free(title); |
|
| 154 g_string_free(body, TRUE); |
|
| 155 } |
|
| 156 |
|
| 157 static void handle_groupchat(JabberMessage *jm) |
|
| 158 { |
|
| 159 JabberID *jid = jabber_id_new(jm->from); |
|
| 160 JabberChat *chat; |
|
| 161 |
|
| 162 if(!jid) |
|
| 163 return; |
|
| 164 |
|
| 165 chat = jabber_chat_find(jm->js, jid->node, jid->domain); |
|
| 166 |
|
| 167 if(!chat) |
|
| 168 return; |
|
| 169 |
|
| 170 if(jm->subject) { |
|
| 171 gaim_conv_chat_set_topic(GAIM_CONV_CHAT(chat->conv), jid->resource, |
|
| 172 jm->subject); |
|
| 173 if(!jm->xhtml && !jm->body) { |
|
| 174 char *msg, *tmp, *tmp2; |
|
| 175 tmp = g_markup_escape_text(jm->subject, -1); |
|
| 176 tmp2 = gaim_markup_linkify(tmp); |
|
| 177 if(jid->resource) |
|
| 178 msg = g_strdup_printf(_("%s has set the topic to: %s"), jid->resource, tmp2); |
|
| 179 else |
|
| 180 msg = g_strdup_printf(_("The topic is: %s"), tmp2); |
|
| 181 gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "", msg, GAIM_MESSAGE_SYSTEM, jm->sent); |
|
| 182 g_free(tmp); |
|
| 183 g_free(tmp2); |
|
| 184 g_free(msg); |
|
| 185 } |
|
| 186 } |
|
| 187 |
|
| 188 if(jm->xhtml || jm->body) { |
|
| 189 if(jid->resource) |
|
| 190 serv_got_chat_in(jm->js->gc, chat->id, jid->resource, |
|
| 191 jm->delayed ? GAIM_MESSAGE_DELAYED : 0, |
|
| 192 jm->xhtml ? jm->xhtml : jm->body, jm->sent); |
|
| 193 else if(chat->muc) |
|
| 194 gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "", |
|
| 195 jm->xhtml ? jm->xhtml : jm->body, |
|
| 196 GAIM_MESSAGE_SYSTEM, jm->sent); |
|
| 197 } |
|
| 198 |
|
| 199 jabber_id_free(jid); |
|
| 200 } |
|
| 201 |
|
| 202 static void handle_groupchat_invite(JabberMessage *jm) |
|
| 203 { |
|
| 204 GHashTable *components; |
|
| 205 JabberID *jid = jabber_id_new(jm->to); |
|
| 206 |
|
| 207 if(!jid) |
|
| 208 return; |
|
| 209 |
|
| 210 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); |
|
| 211 |
|
| 212 g_hash_table_replace(components, g_strdup("room"), g_strdup(jid->node)); |
|
| 213 g_hash_table_replace(components, g_strdup("server"), g_strdup(jid->domain)); |
|
| 214 g_hash_table_replace(components, g_strdup("handle"), |
|
| 215 g_strdup(jm->js->user->node)); |
|
| 216 g_hash_table_replace(components, g_strdup("password"), |
|
| 217 g_strdup(jm->password)); |
|
| 218 |
|
| 219 jabber_id_free(jid); |
|
| 220 serv_got_chat_invite(jm->js->gc, jm->to, jm->from, jm->body, components); |
|
| 221 } |
|
| 222 |
|
| 223 static void handle_error(JabberMessage *jm) |
|
| 224 { |
|
| 225 char *buf; |
|
| 226 |
|
| 227 if(!jm->body) |
|
| 228 return; |
|
| 229 |
|
| 230 buf = g_strdup_printf(_("Message delivery to %s failed: %s"), |
|
| 231 jm->from, jm->error); |
|
| 232 |
|
| 233 gaim_notify_formatted(jm->js->gc, _("Jabber Message Error"), _("Jabber Message Error"), buf, |
|
| 234 jm->xhtml ? jm->xhtml : jm->body, NULL, NULL); |
|
| 235 |
|
| 236 g_free(buf); |
|
| 237 } |
|
| 238 |
|
| 239 void jabber_message_parse(JabberStream *js, xmlnode *packet) |
|
| 240 { |
|
| 241 JabberMessage *jm; |
|
| 242 const char *type; |
|
| 243 xmlnode *child; |
|
| 244 |
|
| 245 if(strcmp(packet->name, "message")) |
|
| 246 return; |
|
| 247 |
|
| 248 jm = g_new0(JabberMessage, 1); |
|
| 249 jm->js = js; |
|
| 250 jm->sent = time(NULL); |
|
| 251 jm->delayed = FALSE; |
|
| 252 |
|
| 253 type = xmlnode_get_attrib(packet, "type"); |
|
| 254 |
|
| 255 if(type) { |
|
| 256 if(!strcmp(type, "normal")) |
|
| 257 jm->type = JABBER_MESSAGE_NORMAL; |
|
| 258 else if(!strcmp(type, "chat")) |
|
| 259 jm->type = JABBER_MESSAGE_CHAT; |
|
| 260 else if(!strcmp(type, "groupchat")) |
|
| 261 jm->type = JABBER_MESSAGE_GROUPCHAT; |
|
| 262 else if(!strcmp(type, "headline")) |
|
| 263 jm->type = JABBER_MESSAGE_HEADLINE; |
|
| 264 else if(!strcmp(type, "error")) |
|
| 265 jm->type = JABBER_MESSAGE_ERROR; |
|
| 266 else |
|
| 267 jm->type = JABBER_MESSAGE_OTHER; |
|
| 268 } else { |
|
| 269 jm->type = JABBER_MESSAGE_NORMAL; |
|
| 270 } |
|
| 271 |
|
| 272 jm->from = g_strdup(xmlnode_get_attrib(packet, "from")); |
|
| 273 jm->to = g_strdup(xmlnode_get_attrib(packet, "to")); |
|
| 274 |
|
| 275 for(child = packet->child; child; child = child->next) { |
|
| 276 if(child->type != XMLNODE_TYPE_TAG) |
|
| 277 continue; |
|
| 278 |
|
| 279 if(!strcmp(child->name, "subject")) { |
|
| 280 if(!jm->subject) |
|
| 281 jm->subject = xmlnode_get_data(child); |
|
| 282 } else if(!strcmp(child->name, "thread")) { |
|
| 283 if(!jm->thread_id) |
|
| 284 jm->thread_id = xmlnode_get_data(child); |
|
| 285 } else if(!strcmp(child->name, "body")) { |
|
| 286 if(!jm->body) |
|
| 287 jm->body = xmlnode_to_str(child, NULL); |
|
| 288 } else if(!strcmp(child->name, "html")) { |
|
| 289 if(!jm->xhtml && xmlnode_get_child(child, "body")) |
|
| 290 jm->xhtml = xmlnode_to_str(child, NULL); |
|
| 291 } else if(!strcmp(child->name, "error")) { |
|
| 292 const char *code = xmlnode_get_attrib(child, "code"); |
|
| 293 char *code_txt = NULL; |
|
| 294 char *text = xmlnode_get_data(child); |
|
| 295 |
|
| 296 if(code) |
|
| 297 code_txt = g_strdup_printf(_(" (Code %s)"), code); |
|
| 298 |
|
| 299 if(!jm->error) |
|
| 300 jm->error = g_strdup_printf("%s%s", text ? text : "", |
|
| 301 code_txt ? code_txt : ""); |
|
| 302 |
|
| 303 g_free(code_txt); |
|
| 304 g_free(text); |
|
| 305 } else if(!strcmp(child->name, "x")) { |
|
| 306 const char *xmlns = xmlnode_get_attrib(child, "xmlns"); |
|
| 307 if(xmlns && !strcmp(xmlns, "jabber:x:event")) { |
|
| 308 if(xmlnode_get_child(child, "composing")) |
|
| 309 jm->events |= JABBER_MESSAGE_EVENT_COMPOSING; |
|
| 310 } else if(xmlns && !strcmp(xmlns, "jabber:x:delay")) { |
|
| 311 const char *timestamp = xmlnode_get_attrib(child, "stamp"); |
|
| 312 jm->delayed = TRUE; |
|
| 313 if(timestamp) |
|
| 314 jm->sent = gaim_str_to_time(timestamp, TRUE); |
|
| 315 } else if(xmlns && !strcmp(xmlns, "jabber:x:conference") && |
|
| 316 jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE && |
|
| 317 jm->type != JABBER_MESSAGE_ERROR) { |
|
| 318 const char *jid = xmlnode_get_attrib(child, "jid"); |
|
| 319 if(jid) { |
|
| 320 jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE; |
|
| 321 g_free(jm->to); |
|
| 322 jm->to = g_strdup(jid); |
|
| 323 } |
|
| 324 } else if(xmlns && !strcmp(xmlns, |
|
| 325 "http://jabber.org/protocol/muc#user") && |
|
| 326 jm->type != JABBER_MESSAGE_ERROR) { |
|
| 327 xmlnode *invite = xmlnode_get_child(child, "invite"); |
|
| 328 if(invite) { |
|
| 329 xmlnode *reason, *password; |
|
| 330 const char *jid = xmlnode_get_attrib(invite, "from"); |
|
| 331 g_free(jm->to); |
|
| 332 jm->to = jm->from; |
|
| 333 jm->from = g_strdup(jid); |
|
| 334 if((reason = xmlnode_get_child(invite, "reason"))) { |
|
| 335 g_free(jm->body); |
|
| 336 jm->body = xmlnode_get_data(reason); |
|
| 337 } |
|
| 338 if((password = xmlnode_get_child(child, "password"))) |
|
| 339 jm->password = xmlnode_get_data(password); |
|
| 340 |
|
| 341 jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE; |
|
| 342 } |
|
| 343 } else { |
|
| 344 jm->etc = g_list_append(jm->etc, child); |
|
| 345 } |
|
| 346 } |
|
| 347 } |
|
| 348 |
|
| 349 switch(jm->type) { |
|
| 350 case JABBER_MESSAGE_NORMAL: |
|
| 351 case JABBER_MESSAGE_CHAT: |
|
| 352 handle_chat(jm); |
|
| 353 break; |
|
| 354 case JABBER_MESSAGE_HEADLINE: |
|
| 355 handle_headline(jm); |
|
| 356 break; |
|
| 357 case JABBER_MESSAGE_GROUPCHAT: |
|
| 358 handle_groupchat(jm); |
|
| 359 break; |
|
| 360 case JABBER_MESSAGE_GROUPCHAT_INVITE: |
|
| 361 handle_groupchat_invite(jm); |
|
| 362 break; |
|
| 363 case JABBER_MESSAGE_ERROR: |
|
| 364 handle_error(jm); |
|
| 365 break; |
|
| 366 case JABBER_MESSAGE_OTHER: |
|
| 367 gaim_debug(GAIM_DEBUG_INFO, "jabber", |
|
| 368 "Received message of unknown type: %s\n", type); |
|
| 369 break; |
|
| 370 } |
|
| 371 jabber_message_free(jm); |
|
| 372 } |
|
| 373 |
|
| 374 void jabber_message_send(JabberMessage *jm) |
|
| 375 { |
|
| 376 xmlnode *message, *child; |
|
| 377 const char *type = NULL; |
|
| 378 |
|
| 379 message = xmlnode_new("message"); |
|
| 380 |
|
| 381 switch(jm->type) { |
|
| 382 case JABBER_MESSAGE_NORMAL: |
|
| 383 type = "normal"; |
|
| 384 break; |
|
| 385 case JABBER_MESSAGE_CHAT: |
|
| 386 case JABBER_MESSAGE_GROUPCHAT_INVITE: |
|
| 387 type = "chat"; |
|
| 388 break; |
|
| 389 case JABBER_MESSAGE_HEADLINE: |
|
| 390 type = "headline"; |
|
| 391 break; |
|
| 392 case JABBER_MESSAGE_GROUPCHAT: |
|
| 393 type = "groupchat"; |
|
| 394 break; |
|
| 395 case JABBER_MESSAGE_ERROR: |
|
| 396 type = "error"; |
|
| 397 break; |
|
| 398 case JABBER_MESSAGE_OTHER: |
|
| 399 type = NULL; |
|
| 400 break; |
|
| 401 } |
|
| 402 |
|
| 403 if(type) |
|
| 404 xmlnode_set_attrib(message, "type", type); |
|
| 405 |
|
| 406 xmlnode_set_attrib(message, "to", jm->to); |
|
| 407 |
|
| 408 if(jm->thread_id) { |
|
| 409 child = xmlnode_new_child(message, "thread"); |
|
| 410 xmlnode_insert_data(child, jm->thread_id, -1); |
|
| 411 } |
|
| 412 |
|
| 413 if(jm->events || (!jm->body && !jm->xhtml && !jm->subject)) { |
|
| 414 child = xmlnode_new_child(message, "x"); |
|
| 415 xmlnode_set_attrib(child, "xmlns", "jabber:x:event"); |
|
| 416 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) |
|
| 417 xmlnode_new_child(child, "composing"); |
|
| 418 } |
|
| 419 |
|
| 420 if(jm->subject) { |
|
| 421 child = xmlnode_new_child(message, "subject"); |
|
| 422 xmlnode_insert_data(child, jm->subject, -1); |
|
| 423 } |
|
| 424 |
|
| 425 if(jm->body) { |
|
| 426 child = xmlnode_new_child(message, "body"); |
|
| 427 xmlnode_insert_data(child, jm->body, -1); |
|
| 428 } |
|
| 429 |
|
| 430 if(jm->xhtml) { |
|
| 431 child = xmlnode_from_str(jm->xhtml, -1); |
|
| 432 if(child) { |
|
| 433 xmlnode_insert_child(message, child); |
|
| 434 } else { |
|
| 435 gaim_debug(GAIM_DEBUG_ERROR, "jabber", |
|
| 436 "XHTML translation/validation failed, returning: %s\n", |
|
| 437 jm->xhtml); |
|
| 438 } |
|
| 439 } |
|
| 440 |
|
| 441 jabber_send(jm->js, message); |
|
| 442 |
|
| 443 xmlnode_free(message); |
|
| 444 } |
|
| 445 |
|
| 446 int jabber_message_send_im(GaimConnection *gc, const char *who, const char *msg, |
|
| 447 GaimMessageFlags flags) |
|
| 448 { |
|
| 449 JabberMessage *jm; |
|
| 450 JabberBuddy *jb; |
|
| 451 JabberBuddyResource *jbr; |
|
| 452 char *buf; |
|
| 453 char *xhtml; |
|
| 454 char *resource; |
|
| 455 |
|
| 456 if(!who || !msg) |
|
| 457 return 0; |
|
| 458 |
|
| 459 resource = jabber_get_resource(who); |
|
| 460 |
|
| 461 jb = jabber_buddy_find(gc->proto_data, who, TRUE); |
|
| 462 jbr = jabber_buddy_find_resource(jb, resource); |
|
| 463 |
|
| 464 g_free(resource); |
|
| 465 |
|
| 466 jm = g_new0(JabberMessage, 1); |
|
| 467 jm->js = gc->proto_data; |
|
| 468 jm->type = JABBER_MESSAGE_CHAT; |
|
| 469 jm->events = JABBER_MESSAGE_EVENT_COMPOSING; |
|
| 470 jm->to = g_strdup(who); |
|
| 471 if(jbr && jbr->thread_id) |
|
| 472 jm->thread_id = jbr->thread_id; |
|
| 473 |
|
| 474 buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg); |
|
| 475 |
|
| 476 gaim_markup_html_to_xhtml(buf, &xhtml, &jm->body); |
|
| 477 g_free(buf); |
|
| 478 |
|
| 479 if(!jbr || jbr->capabilities & JABBER_CAP_XHTML) |
|
| 480 jm->xhtml = xhtml; |
|
| 481 else |
|
| 482 g_free(xhtml); |
|
| 483 |
|
| 484 jabber_message_send(jm); |
|
| 485 jabber_message_free(jm); |
|
| 486 return 1; |
|
| 487 } |
|
| 488 |
|
| 489 int jabber_message_send_chat(GaimConnection *gc, int id, const char *msg, GaimMessageFlags flags) |
|
| 490 { |
|
| 491 JabberChat *chat; |
|
| 492 JabberMessage *jm; |
|
| 493 JabberStream *js; |
|
| 494 char *buf; |
|
| 495 |
|
| 496 if(!msg || !gc) |
|
| 497 return 0; |
|
| 498 |
|
| 499 js = gc->proto_data; |
|
| 500 chat = jabber_chat_find_by_id(js, id); |
|
| 501 |
|
| 502 if(!chat) |
|
| 503 return 0; |
|
| 504 |
|
| 505 jm = g_new0(JabberMessage, 1); |
|
| 506 jm->js = gc->proto_data; |
|
| 507 jm->type = JABBER_MESSAGE_GROUPCHAT; |
|
| 508 jm->to = g_strdup_printf("%s@%s", chat->room, chat->server); |
|
| 509 |
|
| 510 buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg); |
|
| 511 gaim_markup_html_to_xhtml(buf, &jm->xhtml, &jm->body); |
|
| 512 g_free(buf); |
|
| 513 |
|
| 514 if(!chat->xhtml) { |
|
| 515 g_free(jm->xhtml); |
|
| 516 jm->xhtml = NULL; |
|
| 517 } |
|
| 518 |
|
| 519 jabber_message_send(jm); |
|
| 520 jabber_message_free(jm); |
|
| 521 |
|
| 522 return 1; |
|
| 523 } |
|
| 524 |
|
| 525 int jabber_send_typing(GaimConnection *gc, const char *who, int typing) |
|
| 526 { |
|
| 527 JabberMessage *jm; |
|
| 528 JabberBuddy *jb; |
|
| 529 JabberBuddyResource *jbr; |
|
| 530 char *resource = jabber_get_resource(who); |
|
| 531 |
|
| 532 jb = jabber_buddy_find(gc->proto_data, who, TRUE); |
|
| 533 jbr = jabber_buddy_find_resource(jb, resource); |
|
| 534 |
|
| 535 g_free(resource); |
|
| 536 |
|
| 537 if(!jbr || !(jbr->capabilities & JABBER_CAP_COMPOSING)) |
|
| 538 return 0; |
|
| 539 |
|
| 540 jm = g_new0(JabberMessage, 1); |
|
| 541 jm->js = gc->proto_data; |
|
| 542 jm->type = JABBER_MESSAGE_CHAT; |
|
| 543 jm->to = g_strdup(who); |
|
| 544 |
|
| 545 if(typing == GAIM_TYPING) |
|
| 546 jm->events = JABBER_MESSAGE_EVENT_COMPOSING; |
|
| 547 |
|
| 548 jabber_message_send(jm); |
|
| 549 jabber_message_free(jm); |
|
| 550 |
|
| 551 return JABBER_TYPING_NOTIFY_INT; |
|
| 552 } |
|
| 553 |
|