Mercurial > pidgin
annotate src/protocols/jabber/chat.c @ 7971:6fca0d9cc98b
[gaim-migrate @ 8648]
this particular work of art is topic changing support for jabber, and
support for setting the topic by just changing the text in the chat window.
hopefully someone less lazy than I will implement the right function for
IRC, and any other chats that do topics.
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Fri, 02 Jan 2004 06:16:44 +0000 |
parents | 119a22025818 |
children | ac01b7d67ff9 |
rev | line source |
---|---|
7014 | 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 #include "debug.h" | |
23 #include "multi.h" /* for proto_chat_entry */ | |
7310 | 24 #include "notify.h" |
7971 | 25 #include "util.h" |
7014 | 26 |
27 #include "chat.h" | |
7895 | 28 #include "iq.h" |
7014 | 29 #include "message.h" |
7073 | 30 #include "presence.h" |
7923 | 31 #include "xdata.h" |
7014 | 32 |
33 GList *jabber_chat_info(GaimConnection *gc) | |
34 { | |
35 GList *m = NULL; | |
36 struct proto_chat_entry *pce; | |
37 JabberStream *js = gc->proto_data; | |
38 | |
39 pce = g_new0(struct proto_chat_entry, 1); | |
7841 | 40 pce->label = _("_Room:"); |
7014 | 41 pce->identifier = "room"; |
42 m = g_list_append(m, pce); | |
43 | |
44 /* we're gonna default to a conference server I know is true, until | |
45 * I can figure out how to disco for a chat server */ | |
46 pce = g_new0(struct proto_chat_entry, 1); | |
7841 | 47 pce->label = _("_Server:"); |
7014 | 48 pce->identifier = "server"; |
49 pce->def = "conference.jabber.org"; | |
50 m = g_list_append(m, pce); | |
51 | |
52 pce = g_new0(struct proto_chat_entry, 1); | |
7841 | 53 pce->label = _("_Handle:"); |
7014 | 54 pce->identifier = "handle"; |
55 pce->def = js->user->node; | |
56 m = g_list_append(m, pce); | |
57 | |
58 pce = g_new0(struct proto_chat_entry, 1); | |
7841 | 59 pce->label = _("_Password:"); |
7014 | 60 pce->identifier = "password"; |
61 pce->secret = TRUE; | |
62 m = g_list_append(m, pce); | |
63 | |
64 return m; | |
65 } | |
66 | |
67 JabberChat *jabber_chat_find(JabberStream *js, const char *room, | |
68 const char *server) | |
69 { | |
70 JabberChat *chat; | |
71 char *room_jid; | |
72 | |
73 room_jid = g_strdup_printf("%s@%s", room, server); | |
74 | |
7322 | 75 chat = g_hash_table_lookup(js->chats, jabber_normalize(NULL, room_jid)); |
7014 | 76 g_free(room_jid); |
77 | |
78 return chat; | |
79 } | |
80 | |
81 struct _find_by_id_data { | |
82 int id; | |
83 JabberChat *chat; | |
84 }; | |
85 | |
86 void find_by_id_foreach_cb(gpointer key, gpointer value, gpointer user_data) | |
87 { | |
88 JabberChat *chat = value; | |
89 struct _find_by_id_data *fbid = user_data; | |
90 | |
91 if(chat->id == fbid->id) | |
92 fbid->chat = chat; | |
93 } | |
94 | |
95 JabberChat *jabber_chat_find_by_id(JabberStream *js, int id) | |
96 { | |
97 JabberChat *chat; | |
98 struct _find_by_id_data *fbid = g_new0(struct _find_by_id_data, 1); | |
7073 | 99 fbid->id = id; |
7014 | 100 g_hash_table_foreach(js->chats, find_by_id_foreach_cb, fbid); |
101 chat = fbid->chat; | |
102 g_free(fbid); | |
103 return chat; | |
104 } | |
105 | |
106 void jabber_chat_invite(GaimConnection *gc, int id, const char *msg, | |
107 const char *name) | |
108 { | |
109 JabberStream *js = gc->proto_data; | |
110 JabberChat *chat; | |
111 xmlnode *message, *body, *x, *invite; | |
112 char *room_jid; | |
113 | |
114 chat = jabber_chat_find_by_id(js, id); | |
115 if(!chat) | |
116 return; | |
117 | |
118 message = xmlnode_new("message"); | |
119 | |
120 room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); | |
121 | |
122 if(chat->muc) { | |
123 xmlnode_set_attrib(message, "to", room_jid); | |
124 x = xmlnode_new_child(message, "x"); | |
125 xmlnode_set_attrib(x, "xmlns", "http://jabber.org/protocol/muc#user"); | |
126 invite = xmlnode_new_child(x, "invite"); | |
127 xmlnode_set_attrib(invite, "to", name); | |
128 body = xmlnode_new_child(invite, "reason"); | |
129 xmlnode_insert_data(body, msg, -1); | |
130 } else { | |
131 xmlnode_set_attrib(message, "to", name); | |
132 body = xmlnode_new_child(message, "body"); | |
133 xmlnode_insert_data(body, msg, -1); | |
134 x = xmlnode_new_child(message, "x"); | |
135 xmlnode_set_attrib(x, "jid", room_jid); | |
136 xmlnode_set_attrib(x, "xmlns", "jabber:x:conference"); | |
137 } | |
138 | |
139 jabber_send(js, message); | |
140 xmlnode_free(message); | |
141 g_free(room_jid); | |
142 } | |
143 | |
144 void jabber_chat_join(GaimConnection *gc, GHashTable *data) | |
145 { | |
146 JabberChat *chat; | |
147 char *room, *server, *handle, *passwd; | |
148 xmlnode *presence, *x; | |
7262 | 149 char *tmp, *room_jid, *full_jid; |
7014 | 150 JabberStream *js = gc->proto_data; |
151 | |
152 room = g_hash_table_lookup(data, "room"); | |
153 server = g_hash_table_lookup(data, "server"); | |
154 handle = g_hash_table_lookup(data, "handle"); | |
155 passwd = g_hash_table_lookup(data, "password"); | |
156 | |
157 if(!room || !server || !handle) | |
158 return; | |
159 | |
7310 | 160 if(!jabber_nodeprep_validate(room)) { |
161 char *buf = g_strdup_printf(_("%s is not a valid room name"), room); | |
162 gaim_notify_error(gc, _("Invalid Room Name"), _("Invalid Room Name"), | |
163 buf); | |
164 g_free(buf); | |
165 return; | |
166 } else if(!jabber_nameprep_validate(server)) { | |
167 char *buf = g_strdup_printf(_("%s is not a valid server name"), server); | |
168 gaim_notify_error(gc, _("Invalid Server Name"), | |
169 _("Invalid Server Name"), buf); | |
170 g_free(buf); | |
171 return; | |
172 } else if(!jabber_resourceprep_validate(handle)) { | |
173 char *buf = g_strdup_printf(_("%s is not a valid room handle"), handle); | |
174 gaim_notify_error(gc, _("Invalid Room Handle"), | |
175 _("Invalid Room Handle"), buf); | |
176 } | |
177 | |
7014 | 178 if(jabber_chat_find(js, room, server)) |
179 return; | |
180 | |
7262 | 181 tmp = g_strdup_printf("%s@%s", room, server); |
7322 | 182 room_jid = g_strdup(jabber_normalize(NULL, tmp)); |
7262 | 183 g_free(tmp); |
7014 | 184 |
185 chat = g_new0(JabberChat, 1); | |
186 chat->js = gc->proto_data; | |
187 | |
188 chat->room = g_strdup(room); | |
189 chat->server = g_strdup(server); | |
190 chat->nick = g_strdup(handle); | |
191 | |
192 g_hash_table_insert(js->chats, room_jid, chat); | |
193 | |
7073 | 194 presence = jabber_presence_create(gc->away_state, gc->away); |
7014 | 195 full_jid = g_strdup_printf("%s/%s", room_jid, handle); |
196 xmlnode_set_attrib(presence, "to", full_jid); | |
197 g_free(full_jid); | |
198 | |
199 x = xmlnode_new_child(presence, "x"); | |
200 xmlnode_set_attrib(x, "xmlns", "http://jabber.org/protocol/muc"); | |
201 | |
202 if(passwd && *passwd) { | |
203 xmlnode *password = xmlnode_new_child(x, "password"); | |
204 xmlnode_insert_data(password, passwd, -1); | |
205 } | |
206 | |
207 jabber_send(js, presence); | |
208 xmlnode_free(presence); | |
209 } | |
210 | |
211 void jabber_chat_leave(GaimConnection *gc, int id) | |
212 { | |
213 JabberStream *js = gc->proto_data; | |
214 JabberChat *chat = jabber_chat_find_by_id(js, id); | |
215 char *room_jid; | |
216 xmlnode *presence; | |
217 | |
218 if(!chat) | |
219 return; | |
220 | |
221 room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); | |
222 gaim_debug(GAIM_DEBUG_INFO, "jabber", "%s is leaving chat %s\n", | |
223 chat->nick, room_jid); | |
224 presence = xmlnode_new("presence"); | |
225 xmlnode_set_attrib(presence, "to", room_jid); | |
226 xmlnode_set_attrib(presence, "type", "unavailable"); | |
227 jabber_send(js, presence); | |
228 xmlnode_free(presence); | |
229 } | |
230 | |
231 void jabber_chat_destroy(JabberChat *chat) | |
232 { | |
233 JabberStream *js = chat->js; | |
234 char *room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); | |
235 | |
7322 | 236 g_hash_table_remove(js->chats, jabber_normalize(NULL, room_jid)); |
7014 | 237 g_free(room_jid); |
238 | |
239 g_free(chat->room); | |
240 g_free(chat->server); | |
241 g_free(chat->nick); | |
242 g_free(chat); | |
243 } | |
244 | |
245 gboolean jabber_chat_find_buddy(GaimConversation *conv, const char *name) | |
246 { | |
7118
bf630f7dfdcd
[gaim-migrate @ 7685]
Christian Hammond <chipx86@chipx86.com>
parents:
7073
diff
changeset
|
247 GList *m = gaim_conv_chat_get_users(GAIM_CONV_CHAT(conv)); |
7014 | 248 |
249 while(m) { | |
250 if(!strcmp(m->data, name)) | |
251 return TRUE; | |
252 m = m->next; | |
253 } | |
254 | |
255 return FALSE; | |
256 } | |
257 | |
7398 | 258 char *jabber_chat_buddy_real_name(GaimConnection *gc, int id, const char *who) |
259 { | |
260 JabberStream *js = gc->proto_data; | |
261 JabberChat *chat; | |
262 | |
263 chat = jabber_chat_find_by_id(js, id); | |
264 | |
265 if(!chat) | |
266 return NULL; | |
267 | |
268 return g_strdup_printf("%s@%s/%s", chat->room, chat->server, who); | |
269 } | |
7895 | 270 |
7923 | 271 static void jabber_chat_room_configure_x_data_cb(JabberStream *js, xmlnode *result, gpointer data) |
272 { | |
273 JabberChat *chat = data; | |
274 xmlnode *query; | |
275 JabberIq *iq; | |
276 char *to = g_strdup_printf("%s@%s", chat->room, chat->server); | |
277 | |
278 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "http://jabber.org/protocol/muc#owner"); | |
279 xmlnode_set_attrib(iq->node, "to", to); | |
280 g_free(to); | |
281 | |
282 query = xmlnode_get_child(iq->node, "query"); | |
283 | |
284 xmlnode_insert_child(query, result); | |
285 | |
286 jabber_iq_send(iq); | |
287 } | |
288 | |
289 static void jabber_chat_room_configure_cb(JabberStream *js, xmlnode *packet, gpointer data) | |
290 { | |
291 xmlnode *query, *x; | |
292 const char *type = xmlnode_get_attrib(packet, "type"); | |
293 const char *from = xmlnode_get_attrib(packet, "from"); | |
7926 | 294 char *msg; |
7923 | 295 JabberChat *chat; |
296 JabberID *jid; | |
297 | |
298 if(!type || !from) | |
299 return; | |
300 | |
301 | |
7926 | 302 |
7923 | 303 if(!strcmp(type, "result")) { |
304 jid = jabber_id_new(from); | |
305 | |
306 if(!jid) | |
307 return; | |
308 | |
309 chat = jabber_chat_find(js, jid->node, jid->domain); | |
310 jabber_id_free(jid); | |
311 | |
312 if(!chat) | |
313 return; | |
314 | |
315 if(!(query = xmlnode_get_child(packet, "query"))) | |
316 return; | |
317 | |
318 for(x = query->child; x; x = x->next) { | |
319 const char *xmlns; | |
320 if(strcmp(x->name, "x")) | |
321 continue; | |
322 | |
323 if(!(xmlns = xmlnode_get_attrib(x, "xmlns"))) | |
324 continue; | |
325 | |
326 if(!strcmp(xmlns, "jabber:x:data")) { | |
327 jabber_x_data_request(js, x, jabber_chat_room_configure_x_data_cb, chat); | |
328 return; | |
329 } | |
330 } | |
7926 | 331 } else if(!strcmp(type, "error")) { |
332 xmlnode *errnode = xmlnode_get_child(packet, "error"); | |
333 const char *code = NULL; | |
334 char *code_txt = NULL; | |
335 char *msg; | |
336 char *text = NULL; | |
337 | |
338 if(errnode) { | |
339 code = xmlnode_get_attrib(errnode, "code"); | |
340 text = xmlnode_get_data(errnode); | |
341 } | |
342 | |
343 if(code) | |
344 code_txt = g_strdup_printf(_(" (Code %s)"), code); | |
345 | |
346 msg = g_strdup_printf("%s%s", text ? text : "", code_txt ? code_txt : ""); | |
347 gaim_notify_error(js->gc, _("Configuration error"), _("Configuration error"), msg); | |
348 | |
349 g_free(msg); | |
350 if(code_txt) | |
351 g_free(code_txt); | |
352 | |
353 return; | |
7923 | 354 } |
355 | |
7926 | 356 msg = g_strdup_printf("Unable to configure room %s", from); |
357 | |
358 gaim_notify_info(js->gc, _("Unable to configure"), _("Unable to configure"), msg); | |
359 g_free(msg); | |
7923 | 360 |
361 } | |
362 | |
363 void jabber_chat_request_room_configure(JabberChat *chat) { | |
364 JabberIq *iq; | |
365 xmlnode *query; | |
366 char *room_jid; | |
367 | |
7895 | 368 if(!chat) |
369 return; | |
370 | |
7955 | 371 if(!chat->muc) { |
372 gaim_notify_error(chat->js->gc, _("Room Configuration Error"), _("Room Configuration Error"), | |
373 _("This room is not capable of being configured")); | |
374 return; | |
375 } | |
376 | |
7923 | 377 iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET, |
378 "http://jabber.org/protocol/muc#owner"); | |
379 query = xmlnode_get_child(iq->node, "query"); | |
380 room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); | |
7895 | 381 |
7923 | 382 xmlnode_set_attrib(iq->node, "to", room_jid); |
383 | |
384 jabber_iq_set_callback(iq, jabber_chat_room_configure_cb, NULL); | |
385 | |
386 jabber_iq_send(iq); | |
387 | |
388 g_free(room_jid); | |
7895 | 389 } |
390 | |
391 void jabber_chat_create_instant_room(JabberChat *chat) { | |
392 JabberIq *iq; | |
393 xmlnode *query, *x; | |
394 char *room_jid; | |
395 | |
396 if(!chat) | |
397 return; | |
398 | |
399 iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET, | |
400 "http://jabber.org/protocol/muc#owner"); | |
401 query = xmlnode_get_child(iq->node, "query"); | |
402 x = xmlnode_new_child(query, "x"); | |
403 room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); | |
404 | |
405 xmlnode_set_attrib(iq->node, "to", room_jid); | |
406 xmlnode_set_attrib(x, "xmlns", "jabber:x:data"); | |
407 xmlnode_set_attrib(x, "type", "submit"); | |
408 | |
409 jabber_iq_send(iq); | |
410 | |
411 g_free(room_jid); | |
412 } | |
7955 | 413 |
414 static void jabber_chat_register_x_data_result_cb(JabberStream *js, xmlnode *packet, gpointer data) | |
415 { | |
416 const char *type = xmlnode_get_attrib(packet, "type"); | |
417 | |
418 if(type && !strcmp(type, "error")) { | |
419 /* XXX: handle an error (you'll get a 409 if the nick is already registered) */ | |
420 } | |
421 } | |
422 | |
423 static void jabber_chat_register_x_data_cb(JabberStream *js, xmlnode *result, gpointer data) | |
424 { | |
425 JabberChat *chat = data; | |
426 xmlnode *query; | |
427 JabberIq *iq; | |
428 char *to = g_strdup_printf("%s@%s", chat->room, chat->server); | |
429 | |
430 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register"); | |
431 xmlnode_set_attrib(iq->node, "to", to); | |
432 g_free(to); | |
433 | |
434 query = xmlnode_get_child(iq->node, "query"); | |
435 | |
436 xmlnode_insert_child(query, result); | |
437 | |
438 jabber_iq_set_callback(iq, jabber_chat_register_x_data_result_cb, NULL); | |
439 | |
440 jabber_iq_send(iq); | |
441 } | |
442 | |
443 static void jabber_chat_register_cb(JabberStream *js, xmlnode *packet, gpointer data) | |
444 { | |
445 xmlnode *query, *x; | |
446 const char *type = xmlnode_get_attrib(packet, "type"); | |
447 const char *from = xmlnode_get_attrib(packet, "from"); | |
448 char *msg; | |
449 JabberChat *chat; | |
450 JabberID *jid; | |
451 | |
452 if(!type || !from) | |
453 return; | |
454 | |
455 if(!strcmp(type, "result")) { | |
456 jid = jabber_id_new(from); | |
457 | |
458 if(!jid) | |
459 return; | |
460 | |
461 chat = jabber_chat_find(js, jid->node, jid->domain); | |
462 jabber_id_free(jid); | |
463 | |
464 if(!chat) | |
465 return; | |
466 | |
467 if(!(query = xmlnode_get_child(packet, "query"))) | |
468 return; | |
469 | |
470 for(x = query->child; x; x = x->next) { | |
471 const char *xmlns; | |
472 if(strcmp(x->name, "x")) | |
473 continue; | |
474 | |
475 if(!(xmlns = xmlnode_get_attrib(x, "xmlns"))) | |
476 continue; | |
477 | |
478 if(!strcmp(xmlns, "jabber:x:data")) { | |
479 jabber_x_data_request(js, x, jabber_chat_register_x_data_cb, chat); | |
480 return; | |
481 } | |
482 } | |
483 } else if(!strcmp(type, "error")) { | |
484 /* XXX: how many places is this code duplicated? Fix it, immediately */ | |
485 xmlnode *errnode = xmlnode_get_child(packet, "error"); | |
486 const char *code = NULL; | |
487 char *code_txt = NULL; | |
488 char *msg; | |
489 char *text = NULL; | |
490 | |
491 if(errnode) { | |
492 code = xmlnode_get_attrib(errnode, "code"); | |
493 text = xmlnode_get_data(errnode); | |
494 } | |
495 | |
496 if(code) | |
497 code_txt = g_strdup_printf(_(" (Code %s)"), code); | |
498 | |
499 msg = g_strdup_printf("%s%s", text ? text : "", code_txt ? code_txt : ""); | |
500 gaim_notify_error(js->gc, _("Registration error"), _("Registration error"), msg); | |
501 | |
502 g_free(msg); | |
503 if(code_txt) | |
504 g_free(code_txt); | |
505 | |
506 return; | |
507 } | |
508 | |
509 msg = g_strdup_printf("Unable to configure room %s", from); | |
510 | |
511 gaim_notify_info(js->gc, _("Unable to configure"), _("Unable to configure"), msg); | |
512 g_free(msg); | |
513 | |
514 } | |
515 | |
516 void jabber_chat_register(JabberChat *chat) | |
517 { | |
518 JabberIq *iq; | |
519 char *room_jid; | |
520 | |
521 if(!chat) | |
522 return; | |
523 | |
524 room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); | |
525 | |
526 iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET, "jabber:iq:register"); | |
527 xmlnode_set_attrib(iq->node, "to", room_jid); | |
528 g_free(room_jid); | |
529 | |
530 jabber_iq_set_callback(iq, jabber_chat_register_cb, NULL); | |
531 | |
532 jabber_iq_send(iq); | |
533 } | |
534 | |
7971 | 535 /* merge this with the function below when we get everyone on the same page wrt /commands */ |
536 void jabber_chat_change_topic(JabberChat *chat, const char *topic) | |
537 { | |
538 if(topic && *topic) { | |
539 JabberMessage *jm; | |
540 jm = g_new0(JabberMessage, 1); | |
541 jm->js = chat->js; | |
542 jm->type = JABBER_MESSAGE_GROUPCHAT; | |
543 jm->subject = gaim_markup_strip_html(topic); | |
544 jm->to = g_strdup_printf("%s@%s", chat->room, chat->server); | |
545 jabber_message_send(jm); | |
546 jabber_message_free(jm); | |
547 } else { | |
548 const char *cur = gaim_conv_chat_get_topic(GAIM_CONV_CHAT(chat->conv)); | |
549 char *buf; | |
7955 | 550 |
7971 | 551 if(cur) |
552 buf = g_strdup_printf(_("current topic is: %s"), topic); | |
553 else | |
554 buf = g_strdup(_("No topic is set")); | |
555 gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "", buf, | |
556 GAIM_MESSAGE_SYSTEM | GAIM_MESSAGE_NO_LOG, time(NULL)); | |
557 g_free(buf); | |
558 } | |
559 | |
560 } | |
561 | |
562 void jabber_chat_set_topic(GaimConnection *gc, int id, const char *topic) | |
563 { | |
564 JabberStream *js = gc->proto_data; | |
565 JabberChat *chat = jabber_chat_find_by_id(js, id); | |
566 | |
567 if(!chat) | |
568 return; | |
569 | |
570 jabber_chat_change_topic(chat, topic); | |
571 } | |
572 | |
573 |