Mercurial > pidgin
comparison src/protocols/jabber/jabber.c @ 3311:522f37c9f9d2
[gaim-migrate @ 3329]
Added typing notification and improved support for Jabber resources.
(Thanks, Nathan Walp)
Fixed problem with Gaim crashing on non-ASCII buddy alias (Jabber "name"
attribute) chars. (Thanks, Ho-seok Lee)
Plugged memory leaks and fixed problem with away status not being
propagated to conference rooms for jabberd 1.4.2 and above.
committer: Tailor Script <tailor@pidgin.im>
author | Jim Seymour <jseymour> |
---|---|
date | Sat, 15 Jun 2002 17:24:07 +0000 |
parents | 778646c2adbc |
children | 52db87ab4083 |
comparison
equal
deleted
inserted
replaced
3310:3d20b3e7f840 | 3311:522f37c9f9d2 |
---|---|
76 #define DEFAULT_GROUPCHAT "conference.jabber.org" | 76 #define DEFAULT_GROUPCHAT "conference.jabber.org" |
77 #define DEFAULT_PORT 5222 | 77 #define DEFAULT_PORT 5222 |
78 | 78 |
79 #define USEROPT_PORT 0 | 79 #define USEROPT_PORT 0 |
80 | 80 |
81 #define JABBER_TYPING_NOTIFY_INT 15 /* Delay (in seconds) between sending typing notifications */ | |
82 | |
81 /* | 83 /* |
82 * Note: "was_connected" may seem redundant, but it was needed and I | 84 * Note: "was_connected" may seem redundant, but it was needed and I |
83 * didn't want to touch the Jabber state stuff not specific to Gaim. | 85 * didn't want to touch the Jabber state stuff not specific to Gaim. |
84 */ | 86 */ |
85 typedef struct gjconn_struct { | 87 typedef struct gjconn_struct { |
133 */ | 135 */ |
134 struct jabber_data { | 136 struct jabber_data { |
135 gjconn gjc; | 137 gjconn gjc; |
136 gboolean did_import; | 138 gboolean did_import; |
137 GSList *chats; | 139 GSList *chats; |
138 GHashTable *hash; | |
139 time_t idle; | 140 time_t idle; |
140 gboolean die; | 141 gboolean die; |
142 GHashTable *buddies; | |
141 }; | 143 }; |
144 | |
145 /* | |
146 * It is *this* to which we point the buddy proto_data | |
147 */ | |
148 struct jabber_buddy_data { | |
149 GSList *resources; | |
150 char *error_msg; | |
151 }; | |
152 | |
153 /* | |
154 * per-resource info | |
155 */ | |
156 typedef struct jabber_resource_info { | |
157 char *name; | |
158 int priority; | |
159 int state; | |
160 char *away_msg; | |
161 char *thread_id; | |
162 gboolean has_composing; | |
163 } *jab_res_info; | |
164 | |
165 /* | |
166 * For our own jid handling | |
167 * | |
168 * We do our own so we can cleanly parse buddy names | |
169 * (user@server/resource) and rid ourselves of the | |
170 * struct when we're done with it. The Jabber lib | |
171 * structs last the life of the pool--we frequently | |
172 * don't want that. | |
173 * | |
174 * We use the real jid structs so we can make use of | |
175 * jid_safe(), jid_cmp() and some others. | |
176 * | |
177 * BE CAREFUL using the Jabber lib routines. | |
178 * Many of them assume pool use and are not | |
179 * amenable to use with our own! | |
180 * | |
181 * We give them special names so we know, throughout | |
182 * the code, that they're not alloc'd out of pool | |
183 * memory and we can, and must, dispose of them when | |
184 * we're done with 'em. | |
185 */ | |
186 #define gaim_jid_struct jid_struct | |
187 typedef struct gaim_jid_struct *gaim_jid; | |
142 | 188 |
143 /* | 189 /* |
144 * Jabber "chat group" info. Pointers to these go in jabber_data | 190 * Jabber "chat group" info. Pointers to these go in jabber_data |
145 * pending and existing chats lists. | 191 * pending and existing chats lists. |
146 */ | 192 */ |
147 struct jabber_chat { | 193 struct jabber_chat { |
148 jid Jid; | 194 gaim_jid gjid; |
149 struct gaim_connection *gc; | 195 struct gaim_connection *gc; |
150 struct conversation *b; | 196 struct conversation *b; |
151 int id; | 197 int id; |
152 int state; | 198 int state; |
153 }; | 199 }; |
190 valid = g_strdup_printf("%s/%s", given, resource); | 236 valid = g_strdup_printf("%s/%s", given, resource); |
191 else | 237 else |
192 valid = g_strdup(given); | 238 valid = g_strdup(given); |
193 | 239 |
194 return valid; | 240 return valid; |
241 } | |
242 | |
243 /* | |
244 * Dispose of a gaim_jid_struct | |
245 */ | |
246 static void gaim_jid_free(gaim_jid gjid) | |
247 { | |
248 if(gjid) { | |
249 if(gjid->resource) | |
250 free(gjid->resource); | |
251 if(gjid->user) | |
252 free(gjid->user); | |
253 if(gjid->server) | |
254 free(gjid->server); | |
255 if(gjid->full) | |
256 free(gjid->full); | |
257 free(gjid); | |
258 } | |
259 } | |
260 | |
261 /* | |
262 * Create a new gjid struct | |
263 * | |
264 * Unlike jid_new(), also creates "full." | |
265 * | |
266 * Shamelessly copied, in part, from jid.c: jid_new() | |
267 * | |
268 * Caller is responsible for freeing the space allocated by this via | |
269 * gaim_jid_free(). | |
270 * | |
271 * JFIXME: Has a local declaration for jid.c:jid_safe(). I've put in a | |
272 * request to have that added to libjabber's lib.h file. (JSeymour) | |
273 */ | |
274 static gaim_jid gaim_jid_new(char *name) | |
275 { | |
276 extern jid jid_safe(jid); /* *retch* */ | |
277 | |
278 gaim_jid gjid = NULL; | |
279 | |
280 if(name && strlen(name)) { | |
281 char *server, *resource, *type, *str; | |
282 int full_len = 0; | |
283 | |
284 /* user@server/resource */ | |
285 | |
286 str = strdup(name); /* we mangle a copy */ | |
287 | |
288 gjid = calloc(1, sizeof(struct gaim_jid_struct)); | |
289 | |
290 if((resource = strstr(str, "/")) != NULL) { | |
291 *resource = '\0'; | |
292 ++resource; | |
293 if((full_len = strlen(resource)) > 0) { | |
294 gjid->resource = strdup(resource); | |
295 ++full_len; /* for later "/" addition */ | |
296 } | |
297 } else { | |
298 resource = str + strlen(str); /* point to end */ | |
299 } | |
300 | |
301 type = strstr(str, ":"); | |
302 if(type != NULL && type < resource) { | |
303 *type = '\0'; | |
304 ++type; | |
305 str = type; /* ignore the type: prefix */ | |
306 } | |
307 | |
308 server = strstr(str, "@"); | |
309 | |
310 /* | |
311 * if there's no @, it's just the server address | |
312 */ | |
313 if(server == NULL || server > resource) { | |
314 gjid->server = strdup(str); | |
315 full_len += strlen(str); | |
316 } else { | |
317 *server = '\0'; | |
318 ++server; | |
319 gjid->server = strdup(server); | |
320 full_len += strlen(server) + 1; /* account for later "@" */ | |
321 if(strlen(str) > 0) { | |
322 gjid->user = strdup(str); | |
323 full_len += strlen(str); | |
324 } | |
325 } | |
326 | |
327 free(str); | |
328 | |
329 if(!jid_safe(gjid)) { | |
330 gaim_jid_free(gjid); | |
331 gjid = NULL; | |
332 } else { | |
333 if(full_len) { | |
334 char *s = gjid->full = malloc(++full_len); | |
335 | |
336 if(gjid->user) { | |
337 strcpy(s, gjid->user); | |
338 s += strlen(gjid->user); | |
339 } | |
340 if(gjid->server) { | |
341 if(s > gjid->full) | |
342 *(s++) = '@'; | |
343 strcpy(s, gjid->server); | |
344 s += strlen(gjid->server); | |
345 } | |
346 if(gjid->resource) { | |
347 *(s++) = '/'; | |
348 strcpy(s, gjid->resource); | |
349 } | |
350 } | |
351 } | |
352 } | |
353 | |
354 return gjid; | |
355 } | |
356 | |
357 /* | |
358 * Get a "username@server" from unadorned "username" | |
359 * | |
360 * If there's no "@server" part and "who" doesn't match the | |
361 * gjconn server (which would indicate that "who" *is* the | |
362 * server in case of server messages), the gjconn server is | |
363 * appended. | |
364 * | |
365 * If incl_resource is TRUE (non-0), the returned string | |
366 * includes the "/resource" part (if it exists), otherwise not. | |
367 * | |
368 * Allocates space for returned string. Caller is | |
369 * responsible for freeing it with g_free(). | |
370 * | |
371 * If "gjid" is non-null, sets that as well. Caller is | |
372 * reponsible for freeing that via gaim_jid_free() when done | |
373 * with it. | |
374 */ | |
375 static gchar *get_realwho(gjconn gjc, char *who, int incl_resource, gaim_jid *gjid) | |
376 { | |
377 gaim_jid my_gjid; | |
378 gchar *my_who; | |
379 gchar *realwho = NULL; | |
380 | |
381 if(!(who && who[0])) { | |
382 return NULL; | |
383 } | |
384 | |
385 /* | |
386 * Bare username and "username" not the server itself? | |
387 */ | |
388 if(!strchr(who, '@') && strcasecmp(who, gjc->user->server)) { | |
389 my_who = g_strdup_printf("%s@%s", who, gjc->user->server); | |
390 } else { | |
391 my_who = g_strdup(who); | |
392 } | |
393 | |
394 if((my_gjid = gaim_jid_new(my_who)) != NULL) { | |
395 /* | |
396 * If there's no "user" part, "who" was just the server or perhaps a transport (?) | |
397 */ | |
398 if(my_gjid->user) { | |
399 /* | |
400 * Include "/resource" bit? | |
401 */ | |
402 if(incl_resource) { | |
403 realwho = g_strdup(my_gjid->full); | |
404 } else { | |
405 realwho = g_strdup_printf("%s@%s", my_gjid->user, my_gjid->server); | |
406 } | |
407 } else { | |
408 realwho = g_strdup(my_gjid->server); | |
409 } | |
410 } | |
411 | |
412 g_free(my_who); | |
413 | |
414 if(gjid) { | |
415 *gjid = my_gjid; | |
416 } else { | |
417 gaim_jid_free(my_gjid); | |
418 } | |
419 | |
420 return realwho; | |
195 } | 421 } |
196 | 422 |
197 static gjconn gjab_new(char *user, char *pass, void *priv) | 423 static gjconn gjab_new(char *user, char *pass, void *priv) |
198 { | 424 { |
199 pool p; | 425 pool p; |
627 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; | 853 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; |
628 struct jabber_chat *jc = NULL; | 854 struct jabber_chat *jc = NULL; |
629 | 855 |
630 while (jcs) { | 856 while (jcs) { |
631 jc = jcs->data; | 857 jc = jcs->data; |
632 if (!jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) | 858 if (!jid_cmpx(chat, jc->gjid, JID_USER | JID_SERVER)) |
633 break; | 859 break; |
634 jc = NULL; | 860 jc = NULL; |
635 jcs = jcs->next; | 861 jcs = jcs->next; |
636 } | 862 } |
637 | 863 |
647 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; | 873 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; |
648 struct jabber_chat *jc = NULL; | 874 struct jabber_chat *jc = NULL; |
649 | 875 |
650 while (jcs) { | 876 while (jcs) { |
651 jc = jcs->data; | 877 jc = jcs->data; |
652 if (jc->state == JCS_ACTIVE && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) | 878 if (jc->state == JCS_ACTIVE && !jid_cmpx(chat, jc->gjid, JID_USER | JID_SERVER)) |
653 break; | 879 break; |
654 jc = NULL; | 880 jc = NULL; |
655 jcs = jcs->next; | 881 jcs = jcs->next; |
656 } | 882 } |
657 | 883 |
666 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; | 892 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; |
667 struct jabber_chat *jc = NULL; | 893 struct jabber_chat *jc = NULL; |
668 | 894 |
669 while (jcs) { | 895 while (jcs) { |
670 jc = jcs->data; | 896 jc = jcs->data; |
671 if (jc->state == JCS_PENDING && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) | 897 if (jc->state == JCS_PENDING && !jid_cmpx(chat, jc->gjid, JID_USER | JID_SERVER)) |
672 break; | 898 break; |
673 jc = NULL; | 899 jc = NULL; |
674 jcs = jcs->next; | 900 jcs = jcs->next; |
675 } | 901 } |
676 | 902 |
744 | 970 |
745 gjab_send(gjc, x); | 971 gjab_send(gjc, x); |
746 xmlnode_free(x); | 972 xmlnode_free(x); |
747 } | 973 } |
748 } | 974 } |
975 | |
976 static struct jabber_buddy_data* jabber_find_buddy(struct gaim_connection *gc, char *buddy) | |
977 { | |
978 struct jabber_data *jd = gc->proto_data; | |
979 gpointer val; | |
980 char *realwho; | |
981 | |
982 if((realwho = get_realwho(jd->gjc, buddy, FALSE, NULL)) == NULL) | |
983 return NULL; | |
984 | |
985 val = g_hash_table_lookup(jd->buddies, realwho); | |
986 if(val) { | |
987 g_free(realwho); | |
988 return (struct jabber_buddy_data *)val; | |
989 | |
990 } else { | |
991 struct jabber_buddy_data *jbd = g_new0(struct jabber_buddy_data, 1); | |
992 jbd->error_msg = NULL; | |
993 jbd->resources = NULL; | |
994 g_hash_table_insert(jd->buddies, g_strdup(realwho), jbd); | |
995 g_free(realwho); | |
996 return jbd; | |
997 } | |
998 } | |
749 | 999 |
750 /* | 1000 /* |
751 * keep track of away msg same as yahoo plugin | 1001 * find a resource by name, or if no name given, return the "default" resource |
752 */ | 1002 * default being the highest priority one. |
753 static void jabber_track_away(gjconn gjc, jpacket p, char *name, char *type) | 1003 */ |
754 { | 1004 |
755 struct jabber_data *jd = GJ_GC(gjc)->proto_data; | 1005 static jab_res_info jabber_find_resource(struct gaim_connection *gc, char *who) |
756 gpointer val = g_hash_table_lookup(jd->hash, name); | 1006 { |
1007 GSList *resources; | |
1008 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, who); | |
1009 jab_res_info jri = NULL; | |
1010 char *res = strstr(who, "/"); | |
1011 | |
1012 if(res) | |
1013 res++; | |
1014 | |
1015 if(jbd) | |
1016 { | |
1017 resources = jbd->resources; | |
1018 while(resources) | |
1019 { | |
1020 if(!jri && !res) { | |
1021 jri = (jab_res_info) resources->data; | |
1022 } else if(!res) { /* we're looking for the default priority, so... */ | |
1023 if(((jab_res_info) resources->data)->priority >= jri->priority) | |
1024 jri = (jab_res_info) resources->data; | |
1025 } else { | |
1026 if(!strcasecmp(((jab_res_info) resources->data)->name, res)) { | |
1027 jri = (jab_res_info) resources->data; | |
1028 break; | |
1029 } | |
1030 } | |
1031 resources = resources->next; | |
1032 } | |
1033 } | |
1034 | |
1035 return jri; | |
1036 } | |
1037 | |
1038 /* | |
1039 * if the resource doesn't exist, create it. otherwise, just update the priority | |
1040 */ | |
1041 static void jabber_track_resource(struct gaim_connection *gc, | |
1042 char *buddy, | |
1043 char *res, | |
1044 int priority, | |
1045 int state) | |
1046 { | |
1047 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, buddy); | |
1048 | |
1049 if(jbd && res) { | |
1050 char *who = g_strdup_printf("%s/%s", buddy, res); | |
1051 jab_res_info jri = jabber_find_resource(gc, who); | |
1052 g_free(who); | |
1053 if(!jri) { | |
1054 jri = g_new0(struct jabber_resource_info, 1); | |
1055 jri->name = g_strdup(res); | |
1056 jri->away_msg = NULL; | |
1057 jbd->resources = g_slist_append(jbd->resources, jri); | |
1058 } | |
1059 jri->priority = priority; | |
1060 jri->state = state; | |
1061 } | |
1062 } | |
1063 | |
1064 /* | |
1065 * remove the resource, if it exists | |
1066 */ | |
1067 static void jabber_remove_resource(struct gaim_connection *gc, char *buddy, char *res) | |
1068 { | |
1069 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, buddy); | |
1070 if(jbd && res) { | |
1071 char *who = g_strdup_printf("%s/%s", buddy, res); | |
1072 jab_res_info jri = jabber_find_resource(gc, who); | |
1073 g_free(who); | |
1074 if(jri) { | |
1075 g_free(jri->name); | |
1076 if(jri->away_msg) | |
1077 g_free(jri->away_msg); | |
1078 jbd->resources = g_slist_remove(jbd->resources, jri); | |
1079 g_free(jri); | |
1080 } | |
1081 } | |
1082 } | |
1083 | |
1084 /* | |
1085 * grab the away message for the default resource | |
1086 */ | |
1087 static char *jabber_lookup_away(gjconn gjc, char *name) | |
1088 { | |
1089 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), name); | |
1090 | |
1091 if(!jri || !jri->away_msg) | |
1092 return _("Unknown"); | |
1093 | |
1094 return jri->away_msg; | |
1095 } | |
1096 | |
1097 static void jabber_track_away(gjconn gjc, jpacket p, char *type) | |
1098 { | |
757 char *show; | 1099 char *show; |
758 char *vshow = NULL; | 1100 char *vshow = NULL; |
759 char *status = NULL; | 1101 char *status = NULL; |
760 char *msg = NULL; | 1102 char *msg = NULL; |
761 | 1103 jab_res_info jri = NULL; |
1104 | |
1105 if(!p || !p->from || !p->from->resource || !p->from->user) | |
1106 return; | |
1107 | |
1108 jri = jabber_find_resource(GJ_GC(gjc), jid_full(p->from)); | |
1109 | |
1110 if(!jri) | |
1111 return; | |
1112 | |
762 if (type && (strcasecmp(type, "unavailable") == 0)) { | 1113 if (type && (strcasecmp(type, "unavailable") == 0)) { |
763 vshow = _("Unavailable"); | 1114 vshow = _("Unavailable"); |
764 } else { | 1115 } else { |
765 if((show = xmlnode_get_tag_data(p->x, "show")) != NULL) { | 1116 if((show = xmlnode_get_tag_data(p->x, "show")) != NULL) { |
766 if (!strcasecmp(show, "away")) { | 1117 if (!strcasecmp(show, "away")) { |
773 vshow = _("Do Not Disturb"); | 1124 vshow = _("Do Not Disturb"); |
774 } | 1125 } |
775 } | 1126 } |
776 } | 1127 } |
777 | 1128 |
778 if (type && (strcasecmp(type, "error") == 0)) { | 1129 status = g_strdup(xmlnode_get_tag_data(p->x, "status")); |
779 xmlnode enode; | |
780 | |
781 if((enode = xmlnode_get_tag(p->x, "error")) != NULL) { | |
782 status = g_strdup_printf(_("Error %s: %s"), | |
783 xmlnode_get_attrib(enode, "code"), xmlnode_get_data(enode)); | |
784 } else { | |
785 status = g_strdup(_("Unknown Error in presence")); | |
786 } | |
787 } else { | |
788 status = g_strdup(xmlnode_get_tag_data(p->x, "status")); | |
789 } | |
790 | 1130 |
791 if(vshow != NULL || status != NULL ) { | 1131 if(vshow != NULL || status != NULL ) { |
792 /* kinda hokey, but it works :-) */ | 1132 /* kinda hokey, but it works :-) */ |
793 msg = g_strdup_printf("%s%s%s", | 1133 msg = g_strdup_printf("%s%s%s", |
794 (vshow == NULL? "" : vshow), | 1134 (vshow == NULL? "" : vshow), |
798 msg = g_strdup(_("Online")); | 1138 msg = g_strdup(_("Online")); |
799 } | 1139 } |
800 | 1140 |
801 g_free(status); | 1141 g_free(status); |
802 | 1142 |
803 if (val) { | 1143 if(jri->away_msg) |
804 g_free(val); | 1144 g_free(jri->away_msg); |
805 g_hash_table_insert(jd->hash, name, msg); | 1145 |
806 } else { | 1146 jri->away_msg = msg; |
807 g_hash_table_insert(jd->hash, g_strdup(name), msg); | 1147 } |
808 } | 1148 |
809 } | 1149 static void jabber_convo_closed(struct gaim_connection *gc, char *name) |
1150 { | |
1151 jab_res_info jri = jabber_find_resource(gc, name); | |
1152 | |
1153 if(jri) { | |
1154 if(jri->thread_id) | |
1155 g_free(jri->thread_id); | |
1156 | |
1157 jri->thread_id = NULL; | |
1158 } | |
1159 } | |
1160 | |
1161 static void jabber_track_convo_thread(gjconn gjc, char *name, char *thread_id) | |
1162 { | |
1163 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), name); | |
1164 | |
1165 if(jri) { | |
1166 if(jri->thread_id) | |
1167 g_free(jri->thread_id); | |
1168 | |
1169 jri->thread_id = g_strdup(thread_id); | |
1170 } | |
1171 } | |
1172 | |
1173 static char *jabber_get_convo_thread(gjconn gjc, char *name) | |
1174 { | |
1175 char *ct = NULL; | |
1176 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), name); | |
1177 | |
1178 if(jri) { | |
1179 if(jri->thread_id) | |
1180 ct = g_strdup(jri->thread_id); | |
1181 } | |
1182 | |
1183 return ct; | |
1184 } | |
1185 | |
810 | 1186 |
811 static time_t iso8601_to_time(char *timestamp) | 1187 static time_t iso8601_to_time(char *timestamp) |
812 { | 1188 { |
813 struct tm t; | 1189 struct tm t; |
814 time_t retval = 0; | 1190 time_t retval = 0; |
815 | 1191 |
816 if(sscanf(timestamp,"%04d%02d%02dT%02d:%02d:%02d", | 1192 if(sscanf(timestamp, "%04d%02d%02dT%02d:%02d:%02d", |
817 &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec)) | 1193 &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec)) |
818 { | 1194 { |
819 t.tm_year -= 1900; | 1195 t.tm_year -= 1900; |
820 t.tm_mon -= 1; | 1196 t.tm_mon -= 1; |
821 t.tm_isdst = 0; | 1197 t.tm_isdst = 0; |
833 return retval; | 1209 return retval; |
834 } | 1210 } |
835 | 1211 |
836 static void jabber_handlemessage(gjconn gjc, jpacket p) | 1212 static void jabber_handlemessage(gjconn gjc, jpacket p) |
837 { | 1213 { |
838 xmlnode y, xmlns, subj, z; | 1214 xmlnode y, subj; |
839 time_t time_sent = time(NULL); | 1215 time_t time_sent = time(NULL); |
1216 gboolean typing = FALSE; | |
840 | 1217 |
841 char *from = NULL, *msg = NULL, *type = NULL, *topic = NULL; | 1218 char *from = NULL, *msg = NULL, *type = NULL, *topic = NULL; |
1219 char *thread_id = NULL; | |
1220 char *conference_room = NULL; | |
842 char m[BUF_LONG * 2]; | 1221 char m[BUF_LONG * 2]; |
843 | 1222 |
844 type = xmlnode_get_attrib(p->x, "type"); | 1223 type = xmlnode_get_attrib(p->x, "type"); |
845 | 1224 |
846 z = xmlnode_get_firstchild(p->x); | 1225 if ((y = xmlnode_get_tag(p->x, "thread"))) |
847 | 1226 thread_id = xmlnode_get_data(y); |
848 while(z) | 1227 |
849 { | 1228 y = xmlnode_get_firstchild(p->x); |
850 if(NSCHECK(z,NS_DELAY)) | 1229 |
851 { | 1230 while(y) { |
852 char *timestamp = xmlnode_get_attrib(z,"stamp"); | 1231 if(NSCHECK(y, NS_DELAY)) { |
853 time_sent = iso8601_to_time(timestamp); | 1232 char *timestamp = xmlnode_get_attrib(y, "stamp"); |
854 } | 1233 time_sent = iso8601_to_time(timestamp); |
855 z = xmlnode_get_nextsibling(z); | 1234 } else if(NSCHECK(y, "jabber:x:event")) { |
1235 if(xmlnode_get_tag(y, "composing")) | |
1236 typing = TRUE; | |
1237 } else if(NSCHECK(y, "jabber:x:conference")) { | |
1238 conference_room = xmlnode_get_attrib(y, "jid"); | |
1239 } | |
1240 y = xmlnode_get_nextsibling(y); | |
856 } | 1241 } |
857 | 1242 |
858 if (!type || !strcasecmp(type, "normal") || !strcasecmp(type, "chat")) { | 1243 if (!type || !strcasecmp(type, "normal") || !strcasecmp(type, "chat")) { |
859 | |
860 /* XXX namespaces could be handled better. (mid) */ | |
861 if ((xmlns = xmlnode_get_tag(p->x, "x"))) | |
862 type = xmlnode_get_attrib(xmlns, "xmlns"); | |
863 | 1244 |
864 from = jid_full(p->from); | 1245 from = jid_full(p->from); |
865 /* | 1246 /* |
866 if ((y = xmlnode_get_tag(p->x, "html"))) { | 1247 if ((y = xmlnode_get_tag(p->x, "html"))) { |
867 msg = xmlnode_get_data(y); | 1248 msg = xmlnode_get_data(y); |
874 msg = utf8_to_str(msg); | 1255 msg = utf8_to_str(msg); |
875 | 1256 |
876 if (!from) | 1257 if (!from) |
877 return; | 1258 return; |
878 | 1259 |
879 if (type && !strcasecmp(type, "jabber:x:conference")) { | 1260 if (conference_room) { |
880 char *room; | |
881 GList *m = NULL; | 1261 GList *m = NULL; |
882 char **data; | 1262 char **data; |
883 | 1263 |
884 room = xmlnode_get_attrib(xmlns, "jid"); | 1264 data = g_strsplit(conference_room, "@", 2); |
885 data = g_strsplit(room, "@", 2); | |
886 m = g_list_append(m, g_strdup(data[0])); | 1265 m = g_list_append(m, g_strdup(data[0])); |
887 m = g_list_append(m, g_strdup(data[1])); | 1266 m = g_list_append(m, g_strdup(data[1])); |
888 m = g_list_append(m, g_strdup(gjc->user->user)); | 1267 m = g_list_append(m, g_strdup(gjc->user->user)); |
889 g_strfreev(data); | 1268 g_strfreev(data); |
890 | 1269 |
891 serv_got_chat_invite(GJ_GC(gjc), room, from, msg, m); | 1270 serv_got_chat_invite(GJ_GC(gjc), conference_room, from, msg, m); |
892 } else if (msg) { /* whisper */ | 1271 } else if (msg) { /* whisper */ |
893 struct jabber_chat *jc; | 1272 struct jabber_chat *jc; |
894 g_snprintf(m, sizeof(m), "%s", msg); | 1273 g_snprintf(m, sizeof(m), "%s", msg); |
895 if (((jc = find_existing_chat(GJ_GC(gjc), p->from)) != NULL) && jc->b) | 1274 if (((jc = find_existing_chat(GJ_GC(gjc), p->from)) != NULL) && jc->b) |
896 serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 1, m, time_sent); | 1275 serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 1, m, |
1276 time_sent); | |
897 else { | 1277 else { |
898 int flags = 0; | 1278 int flags = 0; |
1279 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), jid_full(p->from)); | |
1280 if(jri && typing) | |
1281 jri->has_composing = TRUE; | |
899 if (xmlnode_get_tag(p->x, "gaim")) | 1282 if (xmlnode_get_tag(p->x, "gaim")) |
900 flags = IM_FLAG_GAIMUSER; | 1283 flags = IM_FLAG_GAIMUSER; |
1284 if ((y = xmlnode_get_tag(p->x, "thread"))) | |
1285 jabber_track_convo_thread(gjc, jid_full(p->from), | |
1286 xmlnode_get_data(y)); | |
901 if (find_conversation(jid_full(p->from))) | 1287 if (find_conversation(jid_full(p->from))) |
902 serv_got_im(GJ_GC(gjc), jid_full(p->from), m, flags, time_sent, -1); | 1288 serv_got_im(GJ_GC(gjc), jid_full(p->from), m, flags, |
1289 time_sent, -1); | |
903 else { | 1290 else { |
904 if(p->from->user) { | 1291 if(p->from->user) { |
905 from = g_strdup_printf("%s@%s", p->from->user, p->from->server); | 1292 from = g_strdup_printf("%s@%s", p->from->user, |
1293 p->from->server); | |
906 } else { | 1294 } else { |
907 /* server message? */ | 1295 /* server message? */ |
908 from = g_strdup(p->from->server); | 1296 from = g_strdup(p->from->server); |
909 } | 1297 } |
910 serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1); | 1298 serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1); |
911 g_free(from); | 1299 g_free(from); |
912 } | 1300 } |
913 } | 1301 } |
1302 } else { | |
1303 /* a non-message message! */ | |
1304 from = g_strdup_printf("%s@%s", p->from->user, p->from->server); | |
1305 if(typing) | |
1306 serv_got_typing(GJ_GC(gjc), from, 0); | |
1307 else | |
1308 serv_got_typing_stopped(GJ_GC(gjc), from); | |
1309 g_free(from); | |
914 } | 1310 } |
915 | 1311 |
916 if (msg) | 1312 if (msg) |
917 g_free(msg); | 1313 g_free(msg); |
918 | 1314 |
964 if (p->from->resource) { | 1360 if (p->from->resource) { |
965 if (!y) { | 1361 if (!y) { |
966 if (!find_chat_buddy(jc->b, p->from->resource)) { | 1362 if (!find_chat_buddy(jc->b, p->from->resource)) { |
967 add_chat_buddy(jc->b, p->from->resource); | 1363 add_chat_buddy(jc->b, p->from->resource); |
968 } else if ((y = xmlnode_get_tag(p->x, "status"))) { | 1364 } else if ((y = xmlnode_get_tag(p->x, "status"))) { |
969 char *buf; | 1365 jabber_track_away(gjc, p, NULL); |
970 | |
971 buf = g_strdup_printf("%s@%s/%s", | |
972 p->from->user, p->from->server, p->from->resource); | |
973 jabber_track_away(gjc, p, buf, NULL); | |
974 g_free(buf); | |
975 | |
976 } | 1366 } |
977 } else if (jc->b && msg) { | 1367 } else if (jc->b && msg) { |
978 char buf[8192]; | 1368 char buf[8192]; |
979 | 1369 |
980 if (topic) { | 1370 if (topic) { |
981 char tbuf[8192]; | 1371 char tbuf[8192]; |
982 g_snprintf(tbuf, sizeof(tbuf), "%s", topic); | 1372 g_snprintf(tbuf, sizeof(tbuf), "%s", topic); |
983 chat_set_topic(jc->b, p->from->resource, tbuf); | 1373 chat_set_topic(jc->b, p->from->resource, tbuf); |
984 } | 1374 } |
985 | |
986 | 1375 |
987 g_snprintf(buf, sizeof(buf), "%s", msg); | 1376 g_snprintf(buf, sizeof(buf), "%s", msg); |
988 serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 0, buf, time_sent); | 1377 serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 0, buf, |
1378 time_sent); | |
989 } | 1379 } |
990 } else { /* message from the server */ | 1380 } else { /* message from the server */ |
991 if(jc->b && topic) { | 1381 if(jc->b && topic) { |
992 char tbuf[8192]; | 1382 char tbuf[8192]; |
993 g_snprintf(tbuf, sizeof(tbuf), "%s", topic); | 1383 g_snprintf(tbuf, sizeof(tbuf), "%s", topic); |
1005 | 1395 |
1006 static void jabber_handlepresence(gjconn gjc, jpacket p) | 1396 static void jabber_handlepresence(gjconn gjc, jpacket p) |
1007 { | 1397 { |
1008 char *to, *from, *type; | 1398 char *to, *from, *type; |
1009 struct buddy *b = NULL; | 1399 struct buddy *b = NULL; |
1010 jid who; | 1400 gaim_jid gjid; |
1011 char *buddy; | 1401 char *buddy; |
1012 xmlnode y; | 1402 xmlnode y; |
1013 char *show; | 1403 char *show; |
1014 int state = 0; | 1404 int state = 0; |
1015 GSList *resources; | |
1016 char *res; | |
1017 struct conversation *cnv = NULL; | 1405 struct conversation *cnv = NULL; |
1018 struct jabber_chat *jc = NULL; | 1406 struct jabber_chat *jc = NULL; |
1019 | 1407 int priority = 0; |
1408 struct jabber_buddy_data *jbd; | |
1409 | |
1020 to = xmlnode_get_attrib(p->x, "to"); | 1410 to = xmlnode_get_attrib(p->x, "to"); |
1021 from = xmlnode_get_attrib(p->x, "from"); | 1411 from = xmlnode_get_attrib(p->x, "from"); |
1022 type = xmlnode_get_attrib(p->x, "type"); | 1412 type = xmlnode_get_attrib(p->x, "type"); |
1023 | 1413 |
1414 if((buddy = get_realwho(gjc, from, FALSE, &gjid)) == NULL) | |
1415 return; | |
1416 | |
1417 if (gjid->user == NULL) { | |
1418 /* FIXME: transport */ | |
1419 g_free(buddy); | |
1420 gaim_jid_free(gjid); | |
1421 return; | |
1422 } | |
1423 | |
1424 jbd = jabber_find_buddy(GJ_GC(gjc), buddy); | |
1425 | |
1426 if(jbd->error_msg) { | |
1427 g_free(jbd->error_msg); | |
1428 jbd->error_msg = NULL; | |
1429 } | |
1430 | |
1024 if(type && !strcasecmp(type, "error")) { | 1431 if(type && !strcasecmp(type, "error")) { |
1025 state = UC_ERROR; | 1432 state = UC_ERROR; |
1433 if((y = xmlnode_get_tag(p->x, "error")) != NULL) { | |
1434 jbd->error_msg = g_strdup_printf(_("Error %s: %s"), | |
1435 xmlnode_get_attrib(y, "code"), xmlnode_get_data(y)); | |
1436 } else { | |
1437 jbd->error_msg = g_strdup(_("Unknown Error in presence")); | |
1438 } | |
1026 } else { | 1439 } else { |
1027 if ((y = xmlnode_get_tag(p->x, "show"))) { | 1440 if ((y = xmlnode_get_tag(p->x, "show"))) { |
1028 show = xmlnode_get_data(y); | 1441 show = xmlnode_get_data(y); |
1029 if (!show) { | 1442 if (!show) { |
1030 state = 0; | 1443 state = 0; |
1040 } else { | 1453 } else { |
1041 state = 0; | 1454 state = 0; |
1042 } | 1455 } |
1043 } | 1456 } |
1044 | 1457 |
1045 who = jid_new(gjc->p, from); | 1458 if ((y = xmlnode_get_tag(p->x, "priority"))) |
1046 if (who->user == NULL) { | 1459 priority = atoi(xmlnode_get_data(y)); |
1047 /* FIXME: transport */ | |
1048 return; | |
1049 } | |
1050 | |
1051 buddy = g_strdup_printf("%s@%s", who->user, who->server); | |
1052 | 1460 |
1053 /* um. we're going to check if it's a chat. if it isn't, and there are pending | 1461 /* um. we're going to check if it's a chat. if it isn't, and there are pending |
1054 * chats, create the chat. if there aren't pending chats and we don't have the | 1462 * chats, create the chat. if there aren't pending chats and we don't have the |
1055 * buddy on our list, simply bail out. */ | 1463 * buddy on our list, simply bail out. */ |
1056 if ((cnv = find_chat(GJ_GC(gjc), who->user)) == NULL) { | 1464 if ((cnv = find_chat(GJ_GC(gjc), gjid->user)) == NULL) { |
1057 static int i = 0x70; | 1465 static int i = 0x70; |
1058 if ((jc = find_pending_chat(GJ_GC(gjc), who)) != NULL) { | 1466 if ((jc = find_pending_chat(GJ_GC(gjc), gjid)) != NULL) { |
1059 jc->b = cnv = serv_got_joined_chat(GJ_GC(gjc), i++, who->user); | 1467 jc->b = cnv = serv_got_joined_chat(GJ_GC(gjc), i++, gjid->user); |
1060 jc->id = jc->b->id; | 1468 jc->id = jc->b->id; |
1061 jc->state = JCS_ACTIVE; | 1469 jc->state = JCS_ACTIVE; |
1062 } else if ((b = find_buddy(GJ_GC(gjc), buddy)) == NULL) { | 1470 } else if ((b = find_buddy(GJ_GC(gjc), buddy)) == NULL) { |
1063 g_free(buddy); | 1471 g_free(buddy); |
1472 gaim_jid_free(gjid); | |
1064 return; | 1473 return; |
1065 } | 1474 } |
1066 } | 1475 } |
1067 | 1476 |
1477 if (type && (strcasecmp(type, "unavailable") == 0)) | |
1478 jabber_remove_resource(GJ_GC(gjc), buddy, gjid->resource); | |
1479 else { | |
1480 jabber_track_resource(GJ_GC(gjc), buddy, gjid->resource, priority, state); | |
1481 | |
1482 /* keep track of away msg somewhat the same as the yahoo plugin */ | |
1483 jabber_track_away(gjc, p, type); | |
1484 } | |
1485 | |
1486 | |
1068 if (!cnv) { | 1487 if (!cnv) { |
1069 resources = b->proto_data; | 1488 /* this is where we handle presence information for "regular" buddies */ |
1070 res = who->resource; | 1489 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), buddy); |
1071 if (res) | 1490 if(jri) { |
1072 while (resources) { | 1491 serv_got_update(GJ_GC(gjc), buddy, 1, 0, b->signon, b->idle, jri->state, 0); |
1073 if (resources->data && !strcmp(res, resources->data)) | 1492 } else |
1074 break; | 1493 serv_got_update(GJ_GC(gjc), buddy, 0, 0, 0, 0, 0, 0); |
1075 resources = resources->next; | 1494 |
1076 } | |
1077 | |
1078 /* keep track of away msg same as yahoo plugin */ | |
1079 jabber_track_away(gjc, p, normalize(b->name), type); | |
1080 | |
1081 if (type && (strcasecmp(type, "unavailable") == 0)) { | |
1082 if (resources) { | |
1083 g_free(resources->data); | |
1084 b->proto_data = g_slist_remove(b->proto_data, resources->data); | |
1085 } | |
1086 if (!b->proto_data) { | |
1087 serv_got_update(GJ_GC(gjc), buddy, 0, 0, 0, 0, 0, 0); | |
1088 } | |
1089 } else { | |
1090 if (res && !resources) { | |
1091 b->proto_data = g_slist_append(b->proto_data, g_strdup(res)); | |
1092 } | |
1093 | |
1094 serv_got_update(GJ_GC(gjc), buddy, 1, 0, b->signon, b->idle, state, 0); | |
1095 | |
1096 } | |
1097 } else { | 1495 } else { |
1098 if (who->resource) { | 1496 if (gjid->resource) { |
1099 char *buf; | |
1100 | |
1101 buf = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource); | |
1102 jabber_track_away(gjc, p, buf, type); | |
1103 g_free(buf); | |
1104 | |
1105 if (type && (!strcasecmp(type, "unavailable"))) { | 1497 if (type && (!strcasecmp(type, "unavailable"))) { |
1106 struct jabber_data *jd; | 1498 struct jabber_data *jd; |
1107 if (!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) { | 1499 if (!jc && !(jc = find_existing_chat(GJ_GC(gjc), gjid))) { |
1108 g_free(buddy); | 1500 g_free(buddy); |
1501 gaim_jid_free(gjid); | |
1109 return; | 1502 return; |
1110 } | 1503 } |
1111 jd = jc->gc->proto_data; | 1504 jd = jc->gc->proto_data; |
1112 /* if it's not ourselves...*/ | 1505 /* if it's not ourselves...*/ |
1113 if (strcmp(who->resource, jc->Jid->resource) && jc->b) { | 1506 if (strcmp(gjid->resource, jc->gjid->resource) && jc->b) { |
1114 remove_chat_buddy(jc->b, who->resource, NULL); | 1507 remove_chat_buddy(jc->b, gjid->resource, NULL); |
1115 g_free(buddy); | 1508 g_free(buddy); |
1509 gaim_jid_free(gjid); | |
1116 return; | 1510 return; |
1117 } | 1511 } |
1118 | 1512 |
1119 jc->state = JCS_CLOSED; | 1513 jc->state = JCS_CLOSED; |
1120 serv_got_chat_left(GJ_GC(gjc), jc->id); | 1514 serv_got_chat_left(GJ_GC(gjc), jc->id); |
1122 * TBD: put back some day? | 1516 * TBD: put back some day? |
1123 jd->chats = g_slist_remove(jd->chats, jc); | 1517 jd->chats = g_slist_remove(jd->chats, jc); |
1124 g_free(jc); | 1518 g_free(jc); |
1125 */ | 1519 */ |
1126 } else { | 1520 } else { |
1127 if ((!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) || !jc->b) { | 1521 if ((!jc && !(jc = find_existing_chat(GJ_GC(gjc), gjid))) || !jc->b) { |
1128 g_free(buddy); | 1522 g_free(buddy); |
1523 gaim_jid_free(gjid); | |
1129 return; | 1524 return; |
1130 } | 1525 } |
1131 if (!find_chat_buddy(jc->b, who->resource)) { | 1526 if (!find_chat_buddy(jc->b, gjid->resource)) { |
1132 add_chat_buddy(jc->b, who->resource); | 1527 add_chat_buddy(jc->b, gjid->resource); |
1133 } | 1528 } |
1134 } | 1529 } |
1135 } | 1530 } |
1136 } | 1531 } |
1137 | 1532 |
1138 g_free(buddy); | 1533 g_free(buddy); |
1534 gaim_jid_free(gjid); | |
1139 | 1535 |
1140 return; | 1536 return; |
1141 } | 1537 } |
1142 | 1538 |
1143 /* | 1539 /* |
1279 * Called with roster item node. | 1675 * Called with roster item node. |
1280 */ | 1676 */ |
1281 static void jabber_handlebuddy(gjconn gjc, xmlnode x) | 1677 static void jabber_handlebuddy(gjconn gjc, xmlnode x) |
1282 { | 1678 { |
1283 xmlnode g; | 1679 xmlnode g; |
1284 char *Jid, *name, *sub, *ask; | 1680 char *who, *name, *sub, *ask; |
1285 jid who; | 1681 gaim_jid gjid; |
1286 struct buddy *b = NULL; | 1682 struct buddy *b = NULL; |
1287 char *buddyname, *groupname = NULL; | 1683 char *buddyname, *groupname = NULL; |
1288 | 1684 |
1289 Jid = xmlnode_get_attrib(x, "jid"); | 1685 who = xmlnode_get_attrib(x, "jid"); |
1290 name = xmlnode_get_attrib(x, "name"); | 1686 name = xmlnode_get_attrib(x, "name"); |
1291 sub = xmlnode_get_attrib(x, "subscription"); | 1687 sub = xmlnode_get_attrib(x, "subscription"); |
1292 ask = xmlnode_get_attrib(x, "ask"); | 1688 ask = xmlnode_get_attrib(x, "ask"); |
1293 who = jid_new(gjc->p, Jid); | 1689 |
1690 if((buddyname = get_realwho(gjc, who, FALSE, &gjid)) == NULL) | |
1691 return; | |
1294 | 1692 |
1295 /* JFIXME: jabber_handleroster() had a "FIXME: transport" at this | 1693 /* JFIXME: jabber_handleroster() had a "FIXME: transport" at this |
1296 * equivilent point. So... | 1694 * equivilent point. So... |
1297 * | 1695 * |
1298 * We haven't allocated any memory or done anything interesting to | 1696 * We haven't done anything interesting to this point, so we'll |
1299 * this point, so we'll violate Good Coding Structure here by | 1697 * violate Good Coding Structure here by simply bailing out. |
1300 * simply bailing out. | |
1301 */ | 1698 */ |
1302 if (!who || !who->user) { | 1699 if(!gjid->user) { |
1700 g_free(buddyname); | |
1701 gaim_jid_free(gjid); | |
1303 return; | 1702 return; |
1304 } | 1703 } |
1305 | 1704 gaim_jid_free(gjid); |
1306 buddyname = g_strdup_printf("%s@%s", who->user, who->server); | |
1307 | 1705 |
1308 if((g = xmlnode_get_tag(x, "group")) != NULL) { | 1706 if((g = xmlnode_get_tag(x, "group")) != NULL) { |
1309 groupname = xmlnode_get_data(g); | 1707 groupname = xmlnode_get_data(g); |
1310 } | 1708 } |
1311 | 1709 |
1336 remove_buddy(GJ_GC(gjc), c_grp, b); | 1734 remove_buddy(GJ_GC(gjc), c_grp, b); |
1337 b = add_buddy(GJ_GC(gjc), groupname, buddyname, | 1735 b = add_buddy(GJ_GC(gjc), groupname, buddyname, |
1338 name ? name : buddyname); | 1736 name ? name : buddyname); |
1339 do_export(GJ_GC(gjc)); | 1737 do_export(GJ_GC(gjc)); |
1340 if(present) { | 1738 if(present) { |
1341 serv_got_update(GJ_GC(gjc), buddyname, 1, 0, signon, idle, uc, 0); | 1739 serv_got_update(GJ_GC(gjc), buddyname, 1, 0, signon, idle, |
1740 uc, 0); | |
1342 } | 1741 } |
1343 } else if(name != NULL && strcmp(b->show, name)) { | 1742 } else if(name != NULL && strcmp(b->show, name)) { |
1344 strncpy(b->show, name, BUDDY_ALIAS_MAXLEN); | 1743 strncpy(b->show, name, BUDDY_ALIAS_MAXLEN); |
1345 b->show[BUDDY_ALIAS_MAXLEN - 1] = '\0'; /* cheap safety feature */ | 1744 b->show[BUDDY_ALIAS_MAXLEN - 1] = '\0'; /* cheap safety feature */ |
1346 handle_buddy_rename(b, buddyname); | 1745 handle_buddy_rename(b, buddyname); |
1599 * way the user always gets some kind of response. | 1998 * way the user always gets some kind of response. |
1600 */ | 1999 */ |
1601 jabber_handlevcard(gjc, NULL, from); | 2000 jabber_handlevcard(gjc, NULL, from); |
1602 } else if(!strcmp((char *) val, "change_password")) { | 2001 } else if(!strcmp((char *) val, "change_password")) { |
1603 char buf[BUF_LONG]; | 2002 char buf[BUF_LONG]; |
1604 sprintf(buf,_("Password successfully changed.")); | 2003 sprintf(buf, _("Password successfully changed.")); |
1605 | 2004 |
1606 do_error_dialog(buf, _("Password Change")); | 2005 do_error_dialog(buf, _("Password Change")); |
1607 } | 2006 } |
1608 } | 2007 } |
1609 } | 2008 } |
1669 { | 2068 { |
1670 struct gaim_connection *gc = new_gaim_conn(user); | 2069 struct gaim_connection *gc = new_gaim_conn(user); |
1671 struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1); | 2070 struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1); |
1672 char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "GAIM"); | 2071 char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "GAIM"); |
1673 | 2072 |
1674 jd->hash = g_hash_table_new(g_str_hash, g_str_equal); | 2073 jd->buddies = g_hash_table_new(g_str_hash, g_str_equal); |
1675 jd->chats = NULL; /* we have no chats yet */ | 2074 jd->chats = NULL; /* we have no chats yet */ |
1676 | 2075 |
1677 set_login_progress(gc, 1, _("Connecting")); | 2076 set_login_progress(gc, 1, _("Connecting")); |
1678 | 2077 |
1679 if (!(jd->gjc = gjab_new(loginname, user->password, gc))) { | 2078 if (!(jd->gjc = gjab_new(loginname, user->password, gc))) { |
1695 g_free(key); | 2094 g_free(key); |
1696 g_free(val); | 2095 g_free(val); |
1697 return TRUE; | 2096 return TRUE; |
1698 } | 2097 } |
1699 | 2098 |
2099 static gboolean jabber_destroy_buddy_hash(gpointer key, gpointer val, gpointer data) { | |
2100 struct jabber_buddy_data *jbd = val; | |
2101 while (jbd->resources) { | |
2102 g_free(((jab_res_info) ((GSList *)jbd->resources)->data)->name); | |
2103 if(((jab_res_info) ((GSList *)jbd->resources)->data)->away_msg) | |
2104 g_free(((jab_res_info) ((GSList *)jbd->resources)->data)->away_msg); | |
2105 g_free(((GSList *)jbd->resources)->data); | |
2106 jbd->resources = g_slist_remove(jbd->resources, ((GSList *)jbd->resources)->data); | |
2107 | |
2108 } | |
2109 if(jbd->error_msg) | |
2110 g_free(jbd->error_msg); | |
2111 g_free(key); | |
2112 g_free(jbd); | |
2113 return TRUE; | |
2114 } | |
2115 | |
2116 | |
1700 static gboolean jabber_free(gpointer data) | 2117 static gboolean jabber_free(gpointer data) |
1701 { | 2118 { |
1702 struct jabber_data *jd = data; | 2119 struct jabber_data *jd = data; |
1703 | 2120 |
1704 if(jd->gjc != NULL) { | 2121 if(jd->gjc != NULL) { |
1718 if(jd) { | 2135 if(jd) { |
1719 GSList *jcs = jd->chats; | 2136 GSList *jcs = jd->chats; |
1720 | 2137 |
1721 /* Free-up the jabber_chat struct allocs and the list */ | 2138 /* Free-up the jabber_chat struct allocs and the list */ |
1722 while (jcs) { | 2139 while (jcs) { |
2140 gaim_jid_free(((struct jabber_chat *)jcs->data)->gjid); | |
1723 g_free(jcs->data); | 2141 g_free(jcs->data); |
1724 jcs = jcs->next; | 2142 jcs = jcs->next; |
1725 } | 2143 } |
1726 g_slist_free(jd->chats); | 2144 g_slist_free(jd->chats); |
1727 | 2145 |
1728 /* Free-up the away status memories and the list */ | 2146 /* Free-up the buddy data hash */ |
1729 if(jd->hash != NULL) { | 2147 if(jd->buddies != NULL) |
1730 g_hash_table_foreach_remove(jd->hash, jabber_destroy_hash, NULL); | 2148 { |
1731 g_hash_table_destroy(jd->hash); | 2149 g_hash_table_foreach_remove(jd->buddies, jabber_destroy_buddy_hash, NULL); |
1732 jd->hash = NULL; | 2150 g_hash_table_destroy(jd->buddies); |
2151 jd->buddies = NULL; | |
1733 } | 2152 } |
1734 | 2153 |
1735 /* Free-up the pending queries memories and the list */ | 2154 /* Free-up the pending queries memories and the list */ |
1736 if(jd->gjc != NULL && jd->gjc->queries != NULL) { | 2155 if(jd->gjc != NULL && jd->gjc->queries != NULL) { |
1737 g_hash_table_foreach_remove(jd->gjc->queries, jabber_destroy_hash, NULL); | 2156 g_hash_table_foreach_remove(jd->gjc->queries, jabber_destroy_hash, NULL); |
1748 xmlnode_free(jd->gjc->current); | 2167 xmlnode_free(jd->gjc->current); |
1749 } | 2168 } |
1750 gc->proto_data = NULL; | 2169 gc->proto_data = NULL; |
1751 } | 2170 } |
1752 | 2171 |
1753 static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags) | 2172 static int jabber_send_typing(struct gaim_connection *gc, char *who, int typing) |
1754 { | 2173 { |
1755 xmlnode x, y; | 2174 xmlnode x, y; |
1756 char *realwho; | 2175 char *realwho; |
1757 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; | 2176 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
2177 jab_res_info jri = jabber_find_resource(gc, who); | |
2178 | |
2179 if(!jri || !jri->has_composing) | |
2180 return 0; | |
2181 | |
2182 if((realwho = get_realwho(gjc, who, FALSE, NULL)) == NULL) | |
2183 return 0; | |
2184 | |
2185 x = xmlnode_new_tag("message"); | |
2186 xmlnode_put_attrib(x, "to", realwho); | |
2187 xmlnode_insert_tag(x, "gaim"); | |
2188 | |
2189 y = xmlnode_insert_tag(x, "x"); | |
2190 xmlnode_put_attrib(y, "xmlns", "jabber:x:event"); | |
2191 | |
2192 if(typing) | |
2193 xmlnode_insert_tag(y, "composing"); | |
2194 | |
2195 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); | |
2196 xmlnode_free(x); | |
2197 g_free(realwho); | |
2198 return JABBER_TYPING_NOTIFY_INT; | |
2199 } | |
2200 | |
2201 static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags) | |
2202 { | |
2203 xmlnode x, y; | |
2204 char *realwho; | |
2205 char *thread_id = NULL; | |
2206 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; | |
1758 | 2207 |
1759 if (!who || !message) | 2208 if (!who || !message) |
1760 return 0; | 2209 return 0; |
1761 | 2210 |
2211 if((realwho = get_realwho(gjc, who, FALSE, NULL)) == NULL) | |
2212 return 0; | |
2213 | |
1762 x = xmlnode_new_tag("message"); | 2214 x = xmlnode_new_tag("message"); |
1763 /* Bare username and "username" not the server itself? */ | |
1764 if (!strchr(who, '@') && strcmp(who, gjc->user->server) != 0) | |
1765 realwho = g_strdup_printf("%s@%s", who, gjc->user->server); | |
1766 else | |
1767 realwho = g_strdup(who); | |
1768 xmlnode_put_attrib(x, "to", realwho); | 2215 xmlnode_put_attrib(x, "to", realwho); |
2216 | |
2217 thread_id = jabber_get_convo_thread(gjc, realwho); | |
2218 if(thread_id) | |
2219 { | |
2220 y = xmlnode_insert_tag(x, "thread"); | |
2221 xmlnode_insert_cdata(y, thread_id, -1); | |
2222 g_free(thread_id); | |
2223 } | |
2224 | |
1769 g_free(realwho); | 2225 g_free(realwho); |
1770 | 2226 |
1771 xmlnode_insert_tag(x, "gaim"); | 2227 xmlnode_insert_tag(x, "gaim"); |
1772 xmlnode_put_attrib(x, "type", "chat"); | 2228 xmlnode_put_attrib(x, "type", "chat"); |
2229 | |
2230 /* let other clients know we support typing notification */ | |
2231 y = xmlnode_insert_tag(x, "x"); | |
2232 xmlnode_put_attrib(y, "xmlns", "jabber:x:event"); | |
2233 xmlnode_insert_tag(y, "composing"); | |
1773 | 2234 |
1774 if (message && strlen(message)) { | 2235 if (message && strlen(message)) { |
1775 char *utf8 = str_to_utf8(message); | 2236 char *utf8 = str_to_utf8(message); |
1776 y = xmlnode_insert_tag(x, "body"); | 2237 y = xmlnode_insert_tag(x, "body"); |
1777 xmlnode_insert_cdata(y, utf8, -1); | 2238 xmlnode_insert_cdata(y, utf8, -1); |
1793 gjconn gjc; | 2254 gjconn gjc; |
1794 struct buddy *buddy = NULL; | 2255 struct buddy *buddy = NULL; |
1795 struct group *buddy_group = NULL; | 2256 struct group *buddy_group = NULL; |
1796 | 2257 |
1797 if(gc && gc->proto_data && ((struct jabber_data *)gc->proto_data)->gjc && name) { | 2258 if(gc && gc->proto_data && ((struct jabber_data *)gc->proto_data)->gjc && name) { |
2259 gaim_jid gjid; | |
1798 gjc = ((struct jabber_data *)gc->proto_data)->gjc; | 2260 gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
1799 | 2261 |
1800 if (!strchr(name, '@')) | 2262 if((realwho = get_realwho(gjc, name, FALSE, &gjid)) == NULL) |
1801 realwho = g_strdup_printf("%s@%s", name, gjc->user->server); | 2263 return; |
1802 else { | 2264 |
1803 jid who = jid_new(gjc->p, name); | 2265 /* FIXME: transport */ |
1804 if (who->user == NULL) { | 2266 if(gjid->user == NULL) { |
1805 /* FIXME: transport */ | 2267 g_free(realwho); |
1806 return; | 2268 gaim_jid_free(gjid); |
1807 } | 2269 return; |
1808 realwho = g_strdup_printf("%s@%s", who->user, who->server); | 2270 } |
1809 } | 2271 gaim_jid_free(gjid); |
1810 | |
1811 | 2272 |
1812 x = jutil_iqnew(JPACKET__SET, NS_ROSTER); | 2273 x = jutil_iqnew(JPACKET__SET, NS_ROSTER); |
1813 y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item"); | 2274 y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item"); |
1814 xmlnode_put_attrib(y, "jid", realwho); | 2275 xmlnode_put_attrib(y, "jid", realwho); |
1815 | 2276 |
1816 | 2277 |
1817 /* If we can find the buddy, there's an alias for him, it's not 0-length | 2278 /* If we can find the buddy, there's an alias for him, it's not 0-length |
1818 * and it doesn't match his JID, add the "name" attribute. | 2279 * and it doesn't match his JID, add the "name" attribute. |
1819 */ | 2280 */ |
1820 if((buddy = find_buddy(gc, realwho)) != NULL && | 2281 if((buddy = find_buddy(gc, realwho)) != NULL && |
1821 buddy->show != NULL && buddy->show[0] != '\0' && strcmp(realwho, buddy->show)) { | 2282 buddy->show != NULL && buddy->show[0] != '\0' && strcmp(realwho, buddy->show)) |
1822 | 2283 { |
1823 xmlnode_put_attrib(y, "name", buddy->show); | 2284 char *utf8 = str_to_utf8(buddy->show); |
2285 xmlnode_put_attrib(y, "name", utf8); | |
2286 g_free(utf8); | |
1824 } | 2287 } |
1825 | 2288 |
1826 /* | 2289 /* |
1827 * Find out what group the buddy's in and send that along | 2290 * Find out what group the buddy's in and send that along |
1828 * with the roster item. | 2291 * with the roster item. |
1853 static void jabber_add_buddy(struct gaim_connection *gc, char *name) | 2316 static void jabber_add_buddy(struct gaim_connection *gc, char *name) |
1854 { | 2317 { |
1855 xmlnode x; | 2318 xmlnode x; |
1856 char *realwho; | 2319 char *realwho; |
1857 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; | 2320 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
2321 gaim_jid gjid; | |
1858 | 2322 |
1859 if (!((struct jabber_data *)gc->proto_data)->did_import) | 2323 if (!((struct jabber_data *)gc->proto_data)->did_import) |
1860 return; | 2324 return; |
1861 | 2325 |
1862 if (!name) | 2326 /* |
2327 * If there's no name or the name is ourself | |
2328 */ | |
2329 if(!name || !strcmp(gc->username, name)) | |
1863 return; | 2330 return; |
1864 | 2331 |
1865 if (!strcmp(gc->username, name)) | 2332 if((realwho = get_realwho(gjc, name, FALSE, &gjid)) == NULL) { |
2333 char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), name); | |
2334 do_error_dialog(msg, _("Jabber Error")); | |
2335 g_free(msg); | |
2336 jabber_remove_gaim_buddy(gc, name); | |
1866 return; | 2337 return; |
1867 | 2338 } |
1868 if (!strchr(name, '@')) | 2339 |
1869 realwho = g_strdup_printf("%s@%s", name, gjc->user->server); | 2340 /* FIXME: transport */ |
1870 else { | 2341 if(gjid->user == NULL) { |
1871 jid who; | 2342 g_free(realwho); |
1872 | 2343 gaim_jid_free(gjid); |
1873 if((who = jid_new(gjc->p, name)) == NULL) { | 2344 return; |
1874 char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), name); | 2345 } |
1875 do_error_dialog(msg, _("Jabber Error")); | 2346 gaim_jid_free(gjid); |
1876 g_free(msg); | |
1877 jabber_remove_gaim_buddy(gc, name); | |
1878 return; | |
1879 } | |
1880 if (who->user == NULL) { | |
1881 /* FIXME: transport */ | |
1882 return; | |
1883 } | |
1884 realwho = g_strdup_printf("%s@%s", who->user, who->server); | |
1885 } | |
1886 | 2347 |
1887 x = xmlnode_new_tag("presence"); | 2348 x = xmlnode_new_tag("presence"); |
1888 xmlnode_put_attrib(x, "to", realwho); | 2349 xmlnode_put_attrib(x, "to", realwho); |
1889 xmlnode_put_attrib(x, "type", "subscribe"); | 2350 xmlnode_put_attrib(x, "type", "subscribe"); |
1890 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); | 2351 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); |
1899 { | 2360 { |
1900 xmlnode x; | 2361 xmlnode x; |
1901 char *realwho; | 2362 char *realwho; |
1902 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; | 2363 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
1903 | 2364 |
1904 if (!name) | 2365 if(!name || (realwho = get_realwho(gjc, name, FALSE, NULL)) == NULL) |
1905 return; | 2366 return; |
1906 | |
1907 if (!strchr(name, '@')) | |
1908 realwho = g_strdup_printf("%s@%s", name, gjc->user->server); | |
1909 else | |
1910 realwho = g_strdup(name); | |
1911 | 2367 |
1912 x = xmlnode_new_tag("presence"); | 2368 x = xmlnode_new_tag("presence"); |
1913 xmlnode_put_attrib(x, "to", realwho); | 2369 xmlnode_put_attrib(x, "to", realwho); |
1914 xmlnode_put_attrib(x, "type", "unsubscribe"); | 2370 xmlnode_put_attrib(x, "type", "unsubscribe"); |
1915 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); | 2371 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); |
2007 xmlnode x; | 2463 xmlnode x; |
2008 char *realwho; | 2464 char *realwho; |
2009 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; | 2465 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
2010 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; | 2466 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; |
2011 struct jabber_chat *jc; | 2467 struct jabber_chat *jc; |
2012 jid Jid; | 2468 gaim_jid gjid; |
2013 | 2469 |
2014 if (!data || !data->next || !data->next->next) | 2470 if (!data || !data->next || !data->next->next) |
2015 return; | 2471 return; |
2016 | 2472 |
2017 realwho = create_valid_jid(data->data, data->next->data, | 2473 realwho = create_valid_jid(data->data, data->next->data, data->next->next->data); |
2018 data->next->next->data); | |
2019 debug_printf("%s\n", realwho); | 2474 debug_printf("%s\n", realwho); |
2020 | 2475 |
2021 if((Jid = jid_new(gjc->p, realwho)) == NULL) { | 2476 if((gjid = gaim_jid_new(realwho)) == NULL) { |
2022 char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), realwho); | 2477 char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), realwho); |
2023 do_error_dialog(msg, _("Jabber Error")); | 2478 do_error_dialog(msg, _("Jabber Error")); |
2024 g_free(msg); | 2479 g_free(msg); |
2480 g_free(realwho); | |
2025 return; | 2481 return; |
2026 } | 2482 } |
2027 | 2483 |
2028 if((jc = find_any_chat(gc, Jid)) != NULL) { | 2484 if((jc = find_any_chat(gc, gjid)) != NULL) { |
2029 free(Jid); /* don't need it, after all */ | |
2030 switch(jc->state) { | 2485 switch(jc->state) { |
2031 case JCS_PENDING: | 2486 case JCS_PENDING: |
2032 debug_printf("attempt to re-join already pending Jabber chat! (ignoring)\n"); | 2487 debug_printf("attempt to re-join already pending Jabber chat! (ignoring)\n"); |
2033 g_free(realwho); /* yuck! */ | 2488 g_free(realwho); /* yuck! */ |
2489 gaim_jid_free(gjid); | |
2034 return; | 2490 return; |
2035 case JCS_ACTIVE: | 2491 case JCS_ACTIVE: |
2036 debug_printf("attempt to re-join already active Jabber chat! (ignoring)\n"); | 2492 debug_printf("attempt to re-join already active Jabber chat! (ignoring)\n"); |
2037 g_free(realwho); /* yuck! */ | 2493 g_free(realwho); /* yuck! */ |
2494 gaim_jid_free(gjid); | |
2038 return; | 2495 return; |
2039 case JCS_CLOSED: | 2496 case JCS_CLOSED: |
2040 debug_printf("rejoining previously closed Jabber chat\n"); | 2497 debug_printf("rejoining previously closed Jabber chat\n"); |
2041 break; | 2498 break; |
2042 default: | 2499 default: |
2043 debug_printf("found Jabber chat in unknown state! (ignoring)\n"); | 2500 debug_printf("found Jabber chat in unknown state! (ignoring)\n"); |
2044 g_free(realwho); /* yuck! */ | 2501 g_free(realwho); /* yuck! */ |
2502 gaim_jid_free(gjid); | |
2045 return; | 2503 return; |
2046 } | 2504 } |
2047 } else { | 2505 } else { |
2048 debug_printf("joining completely new Jabber chat\n"); | 2506 debug_printf("joining completely new Jabber chat\n"); |
2049 jc = g_new0(struct jabber_chat, 1); | 2507 jc = g_new0(struct jabber_chat, 1); |
2050 jc->Jid = Jid; | 2508 jc->gjid = gjid; |
2051 jc->gc = gc; | 2509 jc->gc = gc; |
2052 ((struct jabber_data *)gc->proto_data)->chats = g_slist_append(jcs, jc); | 2510 ((struct jabber_data *)gc->proto_data)->chats = g_slist_append(jcs, jc); |
2511 add_buddy(gc, _("Chats"), realwho, realwho); | |
2053 } | 2512 } |
2054 | 2513 |
2055 jc->state = JCS_PENDING; | 2514 jc->state = JCS_PENDING; |
2056 | 2515 |
2057 x = jutil_presnew(0, realwho, NULL); | 2516 x = jutil_presnew(0, realwho, NULL); |
2066 struct jabber_data *jd = gc->proto_data; | 2525 struct jabber_data *jd = gc->proto_data; |
2067 gjconn gjc = jd->gjc; | 2526 gjconn gjc = jd->gjc; |
2068 struct jabber_chat *jc = NULL; | 2527 struct jabber_chat *jc = NULL; |
2069 char *realwho, *subject; | 2528 char *realwho, *subject; |
2070 | 2529 |
2071 if (!name) | 2530 if(!name || (realwho = get_realwho(gjc, name, FALSE, NULL)) == NULL) |
2072 return; | 2531 return; |
2073 | 2532 |
2074 /* find which chat we're inviting to */ | 2533 /* find which chat we're inviting to */ |
2075 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) | 2534 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) |
2076 return; | 2535 return; |
2077 | 2536 |
2078 x = xmlnode_new_tag("message"); | 2537 x = xmlnode_new_tag("message"); |
2079 if (!strchr(name, '@')) | |
2080 realwho = g_strdup_printf("%s@%s", name, gjc->user->server); | |
2081 else | |
2082 realwho = g_strdup(name); | |
2083 xmlnode_put_attrib(x, "to", realwho); | 2538 xmlnode_put_attrib(x, "to", realwho); |
2539 | |
2084 g_free(realwho); | 2540 g_free(realwho); |
2085 | 2541 |
2086 y = xmlnode_insert_tag(x, "x"); | 2542 y = xmlnode_insert_tag(x, "x"); |
2087 xmlnode_put_attrib(y, "xmlns", "jabber:x:conference"); | 2543 xmlnode_put_attrib(y, "xmlns", "jabber:x:conference"); |
2088 subject = g_strdup_printf("%s@%s", jc->Jid->user, jc->Jid->server); | 2544 subject = g_strdup_printf("%s@%s", jc->gjid->user, jc->gjid->server); |
2089 xmlnode_put_attrib(y, "jid", subject); | 2545 xmlnode_put_attrib(y, "jid", subject); |
2090 g_free(subject); | 2546 g_free(subject); |
2091 | 2547 |
2092 if (message && strlen(message)) { | 2548 if (message && strlen(message)) { |
2093 char *utf8 = str_to_utf8(message); | 2549 char *utf8 = str_to_utf8(message); |
2103 static void jabber_chat_leave(struct gaim_connection *gc, int id) | 2559 static void jabber_chat_leave(struct gaim_connection *gc, int id) |
2104 { | 2560 { |
2105 struct jabber_data *jd = gc->proto_data; | 2561 struct jabber_data *jd = gc->proto_data; |
2106 gjconn gjc = jd->gjc; | 2562 gjconn gjc = jd->gjc; |
2107 struct jabber_chat *jc = NULL; | 2563 struct jabber_chat *jc = NULL; |
2108 char *realwho; | 2564 char *chatname; |
2109 xmlnode x; | 2565 xmlnode x; |
2110 | 2566 |
2111 /* Find out which chat we're leaving */ | 2567 /* Find out which chat we're leaving */ |
2112 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) | 2568 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) |
2113 return; | 2569 return; |
2114 | 2570 |
2115 realwho = g_strdup_printf("%s@%s", jc->Jid->user, jc->Jid->server); | 2571 chatname = g_strdup_printf("%s@%s", jc->gjid->user, jc->gjid->server); |
2116 x = jutil_presnew(0, realwho, NULL); | 2572 x = jutil_presnew(0, chatname, NULL); |
2117 g_free(realwho); | 2573 g_free(chatname); |
2118 xmlnode_put_attrib(x, "type", "unavailable"); | 2574 xmlnode_put_attrib(x, "type", "unavailable"); |
2119 gjab_send(gjc, x); | 2575 gjab_send(gjc, x); |
2120 xmlnode_free(x); | 2576 xmlnode_free(x); |
2121 jc->b = NULL; | 2577 jc->b = NULL; |
2122 } | 2578 } |
2131 /* Find out which chat we're sending to */ | 2587 /* Find out which chat we're sending to */ |
2132 if((retval = jabber_find_chat_by_convo_id(gc, id, &jc)) != 0) | 2588 if((retval = jabber_find_chat_by_convo_id(gc, id, &jc)) != 0) |
2133 return(retval); | 2589 return(retval); |
2134 | 2590 |
2135 x = xmlnode_new_tag("message"); | 2591 x = xmlnode_new_tag("message"); |
2136 xmlnode_put_attrib(x, "from", jid_full(jc->Jid)); | 2592 xmlnode_put_attrib(x, "from", jc->gjid->full); |
2137 chatname = g_strdup_printf("%s@%s", jc->Jid->user, jc->Jid->server); | 2593 chatname = g_strdup_printf("%s@%s", jc->gjid->user, jc->gjid->server); |
2138 xmlnode_put_attrib(x, "to", chatname); | 2594 xmlnode_put_attrib(x, "to", chatname); |
2139 g_free(chatname); | 2595 g_free(chatname); |
2140 xmlnode_put_attrib(x, "type", "groupchat"); | 2596 xmlnode_put_attrib(x, "type", "groupchat"); |
2141 | 2597 |
2142 if (message && strlen(message) > strlen("/topic ") && | 2598 if (message && strlen(message) > strlen("/topic ") && |
2170 /* Find out which chat we're whispering to */ | 2626 /* Find out which chat we're whispering to */ |
2171 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) | 2627 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) |
2172 return; | 2628 return; |
2173 | 2629 |
2174 x = xmlnode_new_tag("message"); | 2630 x = xmlnode_new_tag("message"); |
2175 xmlnode_put_attrib(x, "from", jid_full(jc->Jid)); | 2631 xmlnode_put_attrib(x, "from", jc->gjid->full); |
2176 chatname = g_strdup_printf("%s@%s/%s", jc->Jid->user, jc->Jid->server, who); | 2632 chatname = g_strdup_printf("%s@%s/%s", jc->gjid->user, jc->gjid->server, who); |
2177 xmlnode_put_attrib(x, "to", chatname); | 2633 xmlnode_put_attrib(x, "to", chatname); |
2178 g_free(chatname); | 2634 g_free(chatname); |
2179 xmlnode_put_attrib(x, "type", "normal"); | 2635 xmlnode_put_attrib(x, "type", "normal"); |
2180 | 2636 |
2181 if (message && strlen(message)) { | 2637 if (message && strlen(message)) { |
2228 char *id; | 2684 char *id; |
2229 char *realwho; | 2685 char *realwho; |
2230 struct jabber_data *jd = gc->proto_data; | 2686 struct jabber_data *jd = gc->proto_data; |
2231 gjconn gjc = jd->gjc; | 2687 gjconn gjc = jd->gjc; |
2232 | 2688 |
2689 if((realwho = get_realwho(gjc, who, TRUE, NULL)) == NULL) | |
2690 return; | |
2691 | |
2233 x = jutil_iqnew(JPACKET__GET, NS_VCARD); | 2692 x = jutil_iqnew(JPACKET__GET, NS_VCARD); |
2234 /* Bare username? */ | |
2235 if (!strchr(who, '@')) { | |
2236 realwho = g_strdup_printf("%s@%s", who, gjc->user->server); | |
2237 } else { | |
2238 realwho = g_strdup(who); | |
2239 } | |
2240 xmlnode_put_attrib(x, "to", realwho); | 2693 xmlnode_put_attrib(x, "to", realwho); |
2694 | |
2241 g_free(realwho); | 2695 g_free(realwho); |
2242 | 2696 |
2243 id = gjab_getid(gjc); | 2697 id = gjab_getid(gjc); |
2244 xmlnode_put_attrib(x, "id", id); | 2698 xmlnode_put_attrib(x, "id", id); |
2245 | 2699 |
2246 g_hash_table_insert(jd->gjc->queries, g_strdup(id), g_strdup("vCard")); | 2700 g_hash_table_insert(jd->gjc->queries, g_strdup(id), g_strdup("vCard")); |
2247 | 2701 |
2248 gjab_send(gjc, x); | 2702 gjab_send(gjc, x); |
2249 | 2703 |
2250 xmlnode_free(x); | 2704 xmlnode_free(x); |
2705 } | |
2706 | |
2707 static void jabber_get_error_msg(struct gaim_connection *gc, char *who) { | |
2708 struct jabber_data *jd = gc->proto_data; | |
2709 gjconn gjc = jd->gjc; | |
2710 gchar **str_arr = (gchar **) g_new(gpointer, 3); | |
2711 gchar **ap = str_arr; | |
2712 gchar *realwho, *final; | |
2713 struct jabber_buddy_data *jbd; | |
2714 | |
2715 if((realwho = get_realwho(gjc, who, FALSE, NULL)) == NULL) { | |
2716 g_strfreev(str_arr); | |
2717 return; | |
2718 } | |
2719 | |
2720 jbd = jabber_find_buddy(gc, realwho); | |
2721 | |
2722 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho); | |
2723 *ap++ = g_strdup_printf("<B>Error:</B> %s<BR>\n", jbd->error_msg); | |
2724 *ap = NULL; | |
2251 | 2725 |
2726 final= g_strjoinv(NULL, str_arr); | |
2727 | |
2728 g_strfreev(str_arr); | |
2729 | |
2730 g_show_info_text(gc, realwho, 2, final, NULL); | |
2731 g_free(realwho); | |
2732 g_free(final); | |
2252 } | 2733 } |
2253 | 2734 |
2254 static void jabber_get_away_msg(struct gaim_connection *gc, char *who) { | 2735 static void jabber_get_away_msg(struct gaim_connection *gc, char *who) { |
2255 struct jabber_data *jd = gc->proto_data; | 2736 struct jabber_data *jd = gc->proto_data; |
2256 gjconn gjc = jd->gjc; | 2737 gjconn gjc = jd->gjc; |
2257 char *status; | 2738 int num_resources; |
2739 gaim_jid gjid; | |
2740 char *buddy = get_realwho(gjc, who, FALSE, &gjid); | |
2741 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, buddy); | |
2742 gchar **str_arr; | |
2743 gchar **ap; | |
2744 gchar *realwho, *final; | |
2745 GSList *resources; | |
2746 int i; | |
2747 | |
2748 if(!buddy) | |
2749 return; | |
2750 | |
2751 if(!gjid->resource) { | |
2752 num_resources = g_slist_length(jbd->resources); | |
2753 resources = jbd->resources; | |
2754 } else { | |
2755 num_resources = 1; | |
2756 resources = jbd->resources; | |
2757 while(strcasecmp(((jab_res_info)resources->data)->name, gjid->resource)) | |
2758 resources = resources->next; | |
2759 } | |
2760 | |
2761 gaim_jid_free(gjid); | |
2258 | 2762 |
2259 /* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */ | 2763 /* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */ |
2260 gchar **str_arr = (gchar **) g_new(gpointer, 3); | 2764 str_arr = (gchar **) g_new(gpointer, num_resources*2 + 1); |
2261 gchar **ap = str_arr; | 2765 ap = str_arr; |
2262 gchar *realwho, *final; | 2766 |
2263 | 2767 for(i=0; i<num_resources; i++) |
2264 /* Bare username? */ | 2768 { |
2265 if (!strchr(who, '@')) { | 2769 jab_res_info jri = resources->data; |
2266 realwho = g_strdup_printf("%s@%s", who, gjc->user->server); | 2770 realwho = g_strdup_printf("%s/%s", buddy, jri->name); |
2267 } else { | 2771 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho); |
2268 realwho = g_strdup(who); | 2772 *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", jabber_lookup_away(gjc, realwho)); |
2269 } | 2773 g_free(realwho); |
2270 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho); | 2774 resources = resources->next; |
2271 | 2775 } |
2272 if((status = g_hash_table_lookup(jd->hash, realwho)) == NULL) { | |
2273 status = _("Unknown"); | |
2274 } | |
2275 *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status); | |
2276 | 2776 |
2277 *ap = NULL; | 2777 *ap = NULL; |
2778 | |
2779 g_free(buddy); | |
2278 | 2780 |
2279 final= g_strjoinv(NULL, str_arr); | 2781 final= g_strjoinv(NULL, str_arr); |
2280 g_strfreev(str_arr); | 2782 g_strfreev(str_arr); |
2281 | 2783 |
2282 g_show_info_text(gc, realwho, 2, final, NULL); | 2784 g_show_info_text(gc, who, 2, final, NULL); |
2283 g_free(realwho); | |
2284 g_free(final); | 2785 g_free(final); |
2285 | 2786 |
2286 } | 2787 } |
2287 | 2788 |
2288 static void jabber_get_cb_info(struct gaim_connection *gc, int cid, char *who) { | 2789 static void jabber_get_cb_info(struct gaim_connection *gc, int cid, char *who) { |
2291 | 2792 |
2292 /* Find out which chat */ | 2793 /* Find out which chat */ |
2293 if(jabber_find_chat_by_convo_id(gc, cid, &jc) != 0) | 2794 if(jabber_find_chat_by_convo_id(gc, cid, &jc) != 0) |
2294 return; | 2795 return; |
2295 | 2796 |
2296 realwho = g_strdup_printf("%s@%s/%s", jc->Jid->user, jc->Jid->server, who); | 2797 realwho = g_strdup_printf("%s@%s/%s", jc->gjid->user, jc->gjid->server, who); |
2297 | 2798 |
2298 jabber_get_info(gc, realwho); | 2799 jabber_get_info(gc, realwho); |
2299 g_free(realwho); | 2800 g_free(realwho); |
2300 } | 2801 } |
2301 | 2802 |
2305 | 2806 |
2306 /* Find out which chat */ | 2807 /* Find out which chat */ |
2307 if(jabber_find_chat_by_convo_id(gc, cid, &jc) != 0) | 2808 if(jabber_find_chat_by_convo_id(gc, cid, &jc) != 0) |
2308 return; | 2809 return; |
2309 | 2810 |
2310 realwho = g_strdup_printf("%s@%s/%s", jc->Jid->user, jc->Jid->server, who); | 2811 realwho = g_strdup_printf("%s@%s/%s", jc->gjid->user, jc->gjid->server, who); |
2311 | 2812 |
2312 jabber_get_away_msg(gc, realwho); | 2813 jabber_get_away_msg(gc, realwho); |
2313 | |
2314 g_free(realwho); | 2814 g_free(realwho); |
2315 | 2815 |
2316 } | 2816 } |
2317 | 2817 |
2318 static GList *jabber_buddy_menu(struct gaim_connection *gc, char *who) { | 2818 static GList *jabber_buddy_menu(struct gaim_connection *gc, char *who) { |
2319 GList *m = NULL; | 2819 GList *m = NULL; |
2320 struct proto_buddy_menu *pbm; | 2820 struct proto_buddy_menu *pbm; |
2321 | 2821 struct buddy *b = find_buddy(gc, who); |
2322 pbm = g_new0(struct proto_buddy_menu, 1); | 2822 |
2323 pbm->label = _("Get Info"); | 2823 if(b->uc == UC_ERROR) |
2324 pbm->callback = jabber_get_info; | 2824 { |
2325 pbm->gc = gc; | 2825 pbm = g_new0(struct proto_buddy_menu, 1); |
2326 m = g_list_append(m, pbm); | 2826 pbm->label = _("View Error Msg"); |
2327 pbm = g_new0(struct proto_buddy_menu, 1); | 2827 pbm->callback = jabber_get_error_msg; |
2328 pbm->label = _("Get Away Msg"); | 2828 pbm->gc = gc; |
2329 pbm->callback = jabber_get_away_msg; | 2829 m = g_list_append(m, pbm); |
2330 pbm->gc = gc; | 2830 } else { |
2331 m = g_list_append(m, pbm); | 2831 pbm = g_new0(struct proto_buddy_menu, 1); |
2832 pbm->label = _("Get Info"); | |
2833 pbm->callback = jabber_get_info; | |
2834 pbm->gc = gc; | |
2835 m = g_list_append(m, pbm); | |
2836 pbm = g_new0(struct proto_buddy_menu, 1); | |
2837 pbm->label = _("Get Away Msg"); | |
2838 pbm->callback = jabber_get_away_msg; | |
2839 pbm->gc = gc; | |
2840 m = g_list_append(m, pbm); | |
2841 } | |
2332 | 2842 |
2333 return m; | 2843 return m; |
2334 } | 2844 } |
2335 | 2845 |
2336 static GList *jabber_away_states(struct gaim_connection *gc) { | 2846 static GList *jabber_away_states(struct gaim_connection *gc) { |
2348 static void jabber_set_away(struct gaim_connection *gc, char *state, char *message) | 2858 static void jabber_set_away(struct gaim_connection *gc, char *state, char *message) |
2349 { | 2859 { |
2350 xmlnode x, y; | 2860 xmlnode x, y; |
2351 struct jabber_data *jd = gc->proto_data; | 2861 struct jabber_data *jd = gc->proto_data; |
2352 gjconn gjc = jd->gjc; | 2862 gjconn gjc = jd->gjc; |
2863 GSList *jcs; | |
2864 struct jabber_chat *jc; | |
2865 char *chatname; | |
2353 | 2866 |
2354 gc->away = NULL; /* never send an auto-response */ | 2867 gc->away = NULL; /* never send an auto-response */ |
2355 | 2868 |
2356 x = xmlnode_new_tag("presence"); | 2869 x = xmlnode_new_tag("presence"); |
2357 | 2870 |
2388 xmlnode_insert_cdata(y, "dnd", -1); | 2901 xmlnode_insert_cdata(y, "dnd", -1); |
2389 gc->away = ""; | 2902 gc->away = ""; |
2390 } | 2903 } |
2391 } | 2904 } |
2392 | 2905 |
2393 gjab_send(gjc, x); | 2906 gjab_send(gjc, x); /* Notify "individuals" */ |
2907 | |
2908 /* | |
2909 * As of jabberd-1.4.2: simply sending presence to the server doesn't result in | |
2910 * it being propagated to conference rooms. So we wade thru the list of chats, | |
2911 * sending our new presence status to each and every one. | |
2912 */ | |
2913 for(jcs = jd->chats; jcs; jcs = jcs->next) { | |
2914 jc = jcs->data; | |
2915 if(jc->state == JCS_ACTIVE) { | |
2916 xmlnode_put_attrib(x, "from", jc->gjid->full); | |
2917 chatname = g_strdup_printf("%s@%s", jc->gjid->user, jc->gjid->server); | |
2918 xmlnode_put_attrib(x, "to", chatname); | |
2919 gjab_send(gjc, x); | |
2920 g_free(chatname); | |
2921 } | |
2922 } | |
2923 | |
2394 xmlnode_free(x); | 2924 xmlnode_free(x); |
2395 } | 2925 } |
2396 | 2926 |
2397 static void jabber_set_idle(struct gaim_connection *gc, int idle) { | 2927 static void jabber_set_idle(struct gaim_connection *gc, int idle) { |
2398 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; | 2928 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; |
2415 puo->def = "5222"; | 2945 puo->def = "5222"; |
2416 puo->pos = USEROPT_PORT; | 2946 puo->pos = USEROPT_PORT; |
2417 m = g_list_append(m, puo); | 2947 m = g_list_append(m, puo); |
2418 | 2948 |
2419 return m; | 2949 return m; |
2420 } | |
2421 | |
2422 static void jabber_buddy_free(struct buddy *b) | |
2423 { | |
2424 while (b->proto_data) { | |
2425 g_free(((GSList *)b->proto_data)->data); | |
2426 b->proto_data = g_slist_remove(b->proto_data, ((GSList *)b->proto_data)->data); | |
2427 } | |
2428 } | 2950 } |
2429 | 2951 |
2430 /*---------------------------------------*/ | 2952 /*---------------------------------------*/ |
2431 /* Jabber "set info" (vCard) support */ | 2953 /* Jabber "set info" (vCard) support */ |
2432 /*---------------------------------------*/ | 2954 /*---------------------------------------*/ |
2566 * Display a Jabber vCard | 3088 * Display a Jabber vCard |
2567 */ | 3089 */ |
2568 static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from) | 3090 static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from) |
2569 { | 3091 { |
2570 struct gaim_connection *gc = GJ_GC(gjc); | 3092 struct gaim_connection *gc = GJ_GC(gjc); |
2571 struct jabber_data *jd = GJ_GC(gjc)->proto_data; | |
2572 jid who = jid_new(gjc->p, from); | |
2573 char *cdata, *status; | 3093 char *cdata, *status; |
2574 | |
2575 struct vcard_template *vc_tp = vcard_template_data; | 3094 struct vcard_template *vc_tp = vcard_template_data; |
2576 | 3095 |
2577 /* space for all vCard elements + Jabber I.D. + "status" + NULL (list terminator) */ | 3096 /* space for all vCard elements + Jabber I.D. + "status" + NULL (list terminator) */ |
2578 gchar **str_arr = (gchar **) g_new(gpointer, | 3097 gchar **str_arr = (gchar **) g_new(gpointer, |
2579 (sizeof(vcard_template_data)/sizeof(struct vcard_template)) + 3); | 3098 (sizeof(vcard_template_data)/sizeof(struct vcard_template)) + 3); |
2580 gchar **ap = str_arr; | 3099 gchar **ap = str_arr; |
2581 gchar *buddy, *final; | 3100 gchar *buddy, *final; |
2582 | 3101 |
2583 if(who->resource != NULL && (who->resource)[0] != '\0') { | 3102 if((buddy = get_realwho(gjc, from, TRUE, NULL)) == NULL) { |
2584 buddy = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource); | 3103 g_strfreev(str_arr); |
2585 } else { | 3104 return; |
2586 buddy = g_strdup_printf("%s@%s", who->user, who->server); | 3105 } |
2587 } | 3106 |
2588 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", buddy); | 3107 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", buddy); |
2589 | 3108 |
2590 for(vc_tp = vcard_template_data; vc_tp->label != NULL; ++vc_tp) { | 3109 for(vc_tp = vcard_template_data; vc_tp->label != NULL; ++vc_tp) { |
2591 if(strcmp(vc_tp->tag, "DESC") == 0) | 3110 if(strcmp(vc_tp->tag, "DESC") == 0) |
2592 continue; /* special handling later */ | 3111 continue; /* special handling later */ |
2606 g_free(fmt); | 3125 g_free(fmt); |
2607 } | 3126 } |
2608 } | 3127 } |
2609 } | 3128 } |
2610 | 3129 |
2611 if((status = g_hash_table_lookup(jd->hash, buddy)) == NULL) { | 3130 status = jabber_lookup_away(gjc, buddy); |
2612 status = _("Unknown"); | 3131 |
2613 } | |
2614 *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status); | 3132 *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status); |
2615 | 3133 |
2616 /* | 3134 /* |
2617 * "Description" handled as a special case: get a copy of the | 3135 * "Description" handled as a special case: get a copy of the |
2618 * string and HTML-ize. | 3136 * string and HTML-ize. |
2801 char *id; | 3319 char *id; |
2802 struct jabber_data *jd = gc->proto_data; | 3320 struct jabber_data *jd = gc->proto_data; |
2803 gjconn gjc = jd->gjc; | 3321 gjconn gjc = jd->gjc; |
2804 | 3322 |
2805 x = xmlnode_new_tag("iq"); | 3323 x = xmlnode_new_tag("iq"); |
2806 xmlnode_put_attrib(x,"type","set"); | 3324 xmlnode_put_attrib(x, "type", "set"); |
2807 | 3325 |
2808 id = gjab_getid(gjc); | 3326 id = gjab_getid(gjc); |
2809 | 3327 |
2810 xmlnode_put_attrib(x, "id", id); | 3328 xmlnode_put_attrib(x, "id", id); |
2811 | 3329 |
3151 char *loginname = create_valid_jid(au->username, DEFAULT_SERVER, "GAIM"); | 3669 char *loginname = create_valid_jid(au->username, DEFAULT_SERVER, "GAIM"); |
3152 | 3670 |
3153 /* | 3671 /* |
3154 * These do nothing during registration | 3672 * These do nothing during registration |
3155 */ | 3673 */ |
3156 jd->hash = NULL; | 3674 jd->buddies = NULL; |
3157 jd->chats = NULL; | 3675 jd->chats = NULL; |
3158 | 3676 |
3159 if ((jd->gjc = gjab_new(loginname, au->password, gc)) == NULL) { | 3677 if ((jd->gjc = gjab_new(loginname, au->password, gc)) == NULL) { |
3160 g_free(loginname); | 3678 g_free(loginname); |
3161 debug_printf("jabber: unable to connect (jab_new failed)\n"); | 3679 debug_printf("jabber: unable to connect (jab_new failed)\n"); |
3243 ret->chat_leave = jabber_chat_leave; | 3761 ret->chat_leave = jabber_chat_leave; |
3244 ret->chat_whisper = jabber_chat_whisper; | 3762 ret->chat_whisper = jabber_chat_whisper; |
3245 ret->chat_send = jabber_chat_send; | 3763 ret->chat_send = jabber_chat_send; |
3246 ret->keepalive = jabber_keepalive; | 3764 ret->keepalive = jabber_keepalive; |
3247 ret->normalize = jabber_normalize; | 3765 ret->normalize = jabber_normalize; |
3248 ret->buddy_free = jabber_buddy_free; | |
3249 ret->register_user = jabber_register_user; | 3766 ret->register_user = jabber_register_user; |
3250 ret->alias_buddy = jabber_roster_update; | 3767 ret->alias_buddy = jabber_roster_update; |
3251 ret->group_buddy = jabber_group_change; | 3768 ret->group_buddy = jabber_group_change; |
3769 ret->send_typing = jabber_send_typing; | |
3770 ret->convo_closed = jabber_convo_closed; | |
3252 | 3771 |
3253 my_protocol = ret; | 3772 my_protocol = ret; |
3254 } | 3773 } |
3255 | 3774 |
3256 #ifndef STATIC | 3775 #ifndef STATIC |
3277 { | 3796 { |
3278 return PRPL_DESC("Jabber"); | 3797 return PRPL_DESC("Jabber"); |
3279 } | 3798 } |
3280 | 3799 |
3281 #endif | 3800 #endif |
3801 | |
3802 /* | |
3803 * Local variables: | |
3804 * c-indentation-style: k&r | |
3805 * c-basic-offset: 8 | |
3806 * indent-tabs-mode: notnil | |
3807 * End: | |
3808 * | |
3809 * vim: shiftwidth=8: | |
3810 */ |