1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
6 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
38 #include <sys/socket.h>
39 #include <sys/utsname.h>
53 #include "pixmaps/available.xpm"
54 #include "pixmaps/available-away.xpm"
55 #include "pixmaps/available-chat.xpm"
56 #include "pixmaps/available-xa.xpm"
57 #include "pixmaps/available-dnd.xpm"
59 /* The priv member of gjconn's is a gaim_connection for now. */
60 #define GJ_GC(x) ((struct gaim_connection *)(x)->priv)
62 #define IQID_AUTH "__AUTH__"
68 #define UC_AWAY (0x02 | UC_UNAVAILABLE)
70 #define UC_XA (0x08 | UC_UNAVAILABLE)
71 #define UC_DND (0x10 | UC_UNAVAILABLE)
73 #define DEFAULT_SERVER "jabber.org"
74 #define DEFAULT_GROUPCHAT "conference.jabber.org"
75 #define DEFAULT_PORT 5222
77 #define USEROPT_PORT 0
79 typedef struct gjconn_struct
{
81 pool p
; /* Memory allocation pool */
82 int state
; /* Connection state flag */
83 int fd
; /* Connection file descriptor */
84 jid user
; /* User info */
85 char *pass
; /* User passwd */
88 int id
; /* id counter for jab_getid() function */
89 char idbuf
[9]; /* temporary storage for jab_getid() */
90 char *sid
; /* stream id from server, for digest auth */
91 XML_Parser parser
; /* Parser instance */
92 xmlnode current
; /* Current node in parsing instance.. */
94 /* Event callback ptrs */
95 void (*on_state
)(struct gjconn_struct
*gjc
, int state
);
96 void (*on_packet
)(struct gjconn_struct
*gjc
, jpacket p
);
98 GHashTable
*queries
; /* query tracker */
102 } *gjconn
, gjconn_struct
;
104 typedef void (*gjconn_state_h
)(gjconn gjc
, int state
);
105 typedef void (*gjconn_packet_h
)(gjconn gjc
, jpacket p
);
107 static gjconn
gjab_new(char *user
, char *pass
, void *priv
);
108 static void gjab_delete(gjconn gjc
);
109 static void gjab_state_handler(gjconn gjc
, gjconn_state_h h
);
110 static void gjab_packet_handler(gjconn gjc
, gjconn_packet_h h
);
111 static void gjab_start(gjconn gjc
);
112 static void gjab_stop(gjconn gjc
);
114 static int gjab_getfd(gjconn gjc);
115 static jid gjab_getjid(gjconn gjc);
116 static char *gjab_getsid(gjconn gjc);
118 static char *gjab_getid(gjconn gjc
);
119 static void gjab_send(gjconn gjc
, xmlnode x
);
120 static void gjab_send_raw(gjconn gjc
, const char *str
);
121 static void gjab_recv(gjconn gjc
);
122 static void gjab_auth(gjconn gjc
);
125 * It is *this* to which we point the gaim_connection proto_data
137 * Jabber "chat group" info. Pointers to these go in jabber_data
138 * pending and existing chats lists.
142 struct gaim_connection
*gc
;
143 struct conversation
*b
;
149 * Jabber chat states...
151 * Note: due to a bug in one version of the Jabber server, subscriptions
152 * to chat groups aren't (always?) properly removed at the server. The
153 * result is clients receive Jabber "presence" notifications for JIDs
154 * they no longer care about. The problem with such vestigial notifies is
155 * that we really have no way of telling if it's vestigial or if it's a
156 * valid "buddy" presence notification. So we keep jabber_chat structs
157 * around after leaving a chat group and simply mark them "closed." That
158 * way we can test for such errant presence notifications. I.e.: if we
159 * get a presence notfication from a JID that matches a chat group JID,
162 #define JCS_PENDING 1 /* pending */
163 #define JCS_ACTIVE 2 /* active */
164 #define JCS_CLOSED 3 /* closed */
167 static char *jabber_name()
172 #define STATE_EVT(arg) if(gjc->on_state) { (gjc->on_state)(gjc, (arg) ); }
174 static void jabber_handlevcard(gjconn
, xmlnode
, char *);
176 static char *create_valid_jid(const char *given
, char *server
, char *resource
)
180 if (!strchr(given
, '@'))
181 valid
= g_strdup_printf("%s@%s/%s", given
, server
, resource
);
182 else if (!strchr(strchr(given
, '@'), '/'))
183 valid
= g_strdup_printf("%s/%s", given
, resource
);
185 valid
= g_strdup(given
);
190 static gjconn
gjab_new(char *user
, char *pass
, void *priv
)
201 gjc
= pmalloc_x(p
, sizeof(gjconn_struct
), 0);
203 pool_free(p
); /* no need for this anymore! */
208 gjc
->user
= jid_new(p
, user
);
209 gjc
->pass
= pstrdup(p
, pass
);
211 gjc
->state
= JCONN_STATE_OFF
;
220 static void gjab_delete(gjconn gjc
)
229 static void gjab_state_handler(gjconn gjc
, gjconn_state_h h
)
237 static void gjab_packet_handler(gjconn gjc
, gjconn_packet_h h
)
245 static void gjab_stop(gjconn gjc
)
247 if (!gjc
|| gjc
->state
== JCONN_STATE_OFF
)
250 gjab_send_raw(gjc
, "</stream:stream>");
251 gjc
->state
= JCONN_STATE_OFF
;
254 XML_ParserFree(gjc
->parser
);
259 static int gjab_getfd(gjconn gjc)
267 static jid gjab_getjid(gjconn gjc)
275 static char *gjab_getsid(gjconn gjc)
284 static char *gjab_getid(gjconn gjc
)
286 snprintf(gjc
->idbuf
, 8, "%d", gjc
->id
++);
287 return &gjc
->idbuf
[0];
290 static void gjab_send(gjconn gjc
, xmlnode x
)
292 if (gjc
&& gjc
->state
!= JCONN_STATE_OFF
) {
293 char *buf
= xmlnode2str(x
);
295 write(gjc
->fd
, buf
, strlen(buf
));
296 debug_printf("gjab_send: %s\n", buf
);
300 static void gjab_send_raw(gjconn gjc
, const char *str
)
302 if (gjc
&& gjc
->state
!= JCONN_STATE_OFF
) {
304 * JFIXME: No error detection?!?!
306 if(write(gjc
->fd
, str
, strlen(str
)) < 0) {
307 fprintf(stderr
, "DBG: Problem sending. Error: %d\n", errno
);
310 debug_printf("gjab_send_raw: %s\n", str
);
314 static void gjab_reqroster(gjconn gjc
)
318 x
= jutil_iqnew(JPACKET__GET
, NS_ROSTER
);
319 xmlnode_put_attrib(x
, "id", gjab_getid(gjc
));
325 static void gjab_reqauth(gjconn gjc
)
333 x
= jutil_iqnew(JPACKET__GET
, NS_AUTH
);
334 xmlnode_put_attrib(x
, "id", IQID_AUTH
);
335 y
= xmlnode_get_tag(x
, "query");
337 user
= gjc
->user
->user
;
340 z
= xmlnode_insert_tag(y
, "username");
341 xmlnode_insert_cdata(z
, user
, -1);
348 static void gjab_auth(gjconn gjc
)
356 x
= jutil_iqnew(JPACKET__SET
, NS_AUTH
);
357 xmlnode_put_attrib(x
, "id", IQID_AUTH
);
358 y
= xmlnode_get_tag(x
, "query");
360 user
= gjc
->user
->user
;
363 z
= xmlnode_insert_tag(y
, "username");
364 xmlnode_insert_cdata(z
, user
, -1);
367 z
= xmlnode_insert_tag(y
, "resource");
368 xmlnode_insert_cdata(z
, gjc
->user
->resource
, -1);
371 debug_printf("digest authentication (sid %s)\n", gjc
->sid
);
372 z
= xmlnode_insert_tag(y
, "digest");
373 hash
= pmalloc(x
->p
, strlen(gjc
->sid
) + strlen(gjc
->pass
) + 1);
374 strcpy(hash
, gjc
->sid
);
375 strcat(hash
, gjc
->pass
);
376 hash
= shahash(hash
);
377 xmlnode_insert_cdata(z
, hash
, 40);
379 z
= xmlnode_insert_tag(y
, "password");
380 xmlnode_insert_cdata(z
, gjc
->pass
, -1);
389 static void gjab_recv(gjconn gjc
)
391 static char buf
[4096];
394 if (!gjc
|| gjc
->state
== JCONN_STATE_OFF
)
397 if ((len
= read(gjc
->fd
, buf
, sizeof(buf
) - 1))) {
398 struct jabber_data
*jd
= GJ_GC(gjc
)->proto_data
;
400 debug_printf("input (len %d): %s\n", len
, buf
);
401 XML_Parse(gjc
->parser
, buf
, len
, 0);
404 } else if (len
<= 0) {
405 STATE_EVT(JCONN_STATE_OFF
)
409 static void startElement(void *userdata
, const char *name
, const char **attribs
)
412 gjconn gjc
= (gjconn
) userdata
;
415 /* Append the node to the current one */
416 x
= xmlnode_insert_tag(gjc
->current
, name
);
417 xmlnode_put_expat_attribs(x
, attribs
);
421 x
= xmlnode_new_tag(name
);
422 xmlnode_put_expat_attribs(x
, attribs
);
423 if (strcmp(name
, "stream:stream") == 0) {
424 /* special case: name == stream:stream */
425 /* id attrib of stream is stored for digest auth */
426 gjc
->sid
= g_strdup(xmlnode_get_attrib(x
, "id"));
427 /* STATE_EVT(JCONN_STATE_AUTH) */
435 static void endElement(void *userdata
, const char *name
)
437 gjconn gjc
= (gjconn
) userdata
;
441 if (gjc
->current
== NULL
) {
442 /* we got </stream:stream> */
443 STATE_EVT(JCONN_STATE_OFF
)
447 x
= xmlnode_get_parent(gjc
->current
);
450 /* it is time to fire the event */
451 p
= jpacket_new(gjc
->current
);
454 (gjc
->on_packet
) (gjc
, p
);
456 xmlnode_free(gjc
->current
);
462 static void jabber_callback(gpointer data
, gint source
, GaimInputCondition condition
)
464 struct gaim_connection
*gc
= (struct gaim_connection
*)data
;
465 struct jabber_data
*jd
= (struct jabber_data
*)gc
->proto_data
;
470 static void charData(void *userdata
, const char *s
, int slen
)
472 gjconn gjc
= (gjconn
) userdata
;
475 xmlnode_insert_cdata(gjc
->current
, s
, slen
);
478 static void gjab_connected(gpointer data
, gint source
, GaimInputCondition cond
)
482 struct gaim_connection
*gc
= data
;
483 struct jabber_data
*jd
;
486 if (!g_slist_find(connections
, gc
)) {
494 if (gjc
->fd
!= source
)
498 STATE_EVT(JCONN_STATE_OFF
)
502 gjc
->state
= JCONN_STATE_CONNECTED
;
503 STATE_EVT(JCONN_STATE_CONNECTED
)
506 x
= jutil_header(NS_CLIENT
, gjc
->user
->server
);
508 /* this is ugly, we can create the string here instead of jutil_header */
509 /* what do you think about it? -madcat */
510 t2
= strstr(t
, "/>");
513 gjab_send_raw(gjc
, "<?xml version='1.0'?>");
514 gjab_send_raw(gjc
, t
);
517 gjc
->state
= JCONN_STATE_ON
;
518 STATE_EVT(JCONN_STATE_ON
);
521 gc
->inpa
= gaim_input_add(gjc
->fd
, GAIM_INPUT_READ
, jabber_callback
, gc
);
524 static void gjab_start(gjconn gjc
)
526 struct aim_user
*user
;
529 if (!gjc
|| gjc
->state
!= JCONN_STATE_OFF
)
532 user
= GJ_GC(gjc
)->user
;
533 port
= user
->proto_opt
[USEROPT_PORT
][0] ? atoi(user
->proto_opt
[USEROPT_PORT
]) : DEFAULT_PORT
;
535 gjc
->parser
= XML_ParserCreate(NULL
);
536 XML_SetUserData(gjc
->parser
, (void *)gjc
);
537 XML_SetElementHandler(gjc
->parser
, startElement
, endElement
);
538 XML_SetCharacterDataHandler(gjc
->parser
, charData
);
540 gjc
->fd
= proxy_connect(gjc
->user
->server
, port
, gjab_connected
, GJ_GC(gjc
));
541 if (!user
->gc
|| (gjc
->fd
< 0)) {
542 STATE_EVT(JCONN_STATE_OFF
)
548 * Find chat by chat group name
550 static struct conversation
*find_chat(struct gaim_connection
*gc
, char *name
)
552 GSList
*bcs
= gc
->buddy_chats
;
553 struct conversation
*b
= NULL
;
554 char *chat
= g_strdup(normalize(name
));
558 if (!strcasecmp(normalize(b
->name
), chat
))
569 * Find chat by "chat id"
571 * Returns: 0 on success and jabber_chat pointer set
572 * or -EINVAL on error and jabber_chat pointer is
575 * TBD: Slogging through the buddy_chats list seems
576 * redundant since the chat i.d. is mirrored in the
577 * jabber_chat struct list. But that's the way it
578 * was, so that's the way I'm leaving it--for now.
580 static int jabber_find_chat_by_convo_id(struct gaim_connection
*gc
, int id
, struct jabber_chat
**jc
)
582 GSList
*bcs
= gc
->buddy_chats
;
583 struct conversation
*b
= NULL
;
584 struct jabber_data
*jd
= gc
->proto_data
;
597 while (bcs
!= NULL
) {
599 if ((*jc
)->state
== JCS_ACTIVE
&& (*jc
)->b
== b
)
605 return(bcs
== NULL
? -EINVAL
: 0);
611 static struct jabber_chat
*find_any_chat(struct gaim_connection
*gc
, jid chat
)
613 GSList
*jcs
= ((struct jabber_data
*)gc
->proto_data
)->chats
;
614 struct jabber_chat
*jc
= NULL
;
618 if (!jid_cmpx(chat
, jc
->Jid
, JID_USER
| JID_SERVER
))
629 * Find existing/active Jabber chat
631 static struct jabber_chat
*find_existing_chat(struct gaim_connection
*gc
, jid chat
)
633 GSList
*jcs
= ((struct jabber_data
*)gc
->proto_data
)->chats
;
634 struct jabber_chat
*jc
= NULL
;
638 if (jc
->state
== JCS_ACTIVE
&& !jid_cmpx(chat
, jc
->Jid
, JID_USER
| JID_SERVER
))
650 static struct jabber_chat
*find_pending_chat(struct gaim_connection
*gc
, jid chat
)
652 GSList
*jcs
= ((struct jabber_data
*)gc
->proto_data
)->chats
;
653 struct jabber_chat
*jc
= NULL
;
657 if (jc
->state
== JCS_PENDING
&& !jid_cmpx(chat
, jc
->Jid
, JID_USER
| JID_SERVER
))
666 static gboolean
find_chat_buddy(struct conversation
*b
, char *name
)
668 GList
*m
= b
->in_room
;
671 if (!strcmp(m
->data
, name
))
680 * keep track of away msg same as yahoo plugin
682 static void jabber_track_away(gjconn gjc
, jpacket p
, char *name
)
684 struct jabber_data
*jd
= GJ_GC(gjc
)->proto_data
;
685 gpointer val
= g_hash_table_lookup(jd
->hash
, name
);
691 if((show
= xmlnode_get_tag_data(p
->x
, "show")) != NULL
) {
692 if (!strcasecmp(show
, "away")) {
694 } else if (!strcasecmp(show
, "chat")) {
696 } else if (!strcasecmp(show
, "xa")) {
697 vshow
= _("Extended Away");
698 } else if (!strcasecmp(show
, "dnd")) {
699 vshow
= _("Do Not Disturb");
703 status
= xmlnode_get_tag_data(p
->x
, "status");
705 if(vshow
!= NULL
|| status
!= NULL
) {
706 /* kinda hokey, but it works :-) */
707 msg
= g_strdup_printf("%s%s%s",
708 (vshow
== NULL
? "" : vshow
),
709 (vshow
== NULL
|| status
== NULL
? "" : ": "),
710 (status
== NULL
? "" : status
));
715 g_hash_table_insert(jd
->hash
, name
, msg
);
717 g_hash_table_insert(jd
->hash
, g_strdup(name
), msg
);
721 static void jabber_handlemessage(gjconn gjc
, jpacket p
)
723 xmlnode y
, xmlns
, subj
;
725 char *from
= NULL
, *msg
= NULL
, *type
= NULL
, *topic
= NULL
;
726 char m
[BUF_LONG
* 2];
728 type
= xmlnode_get_attrib(p
->x
, "type");
730 if (!type
|| !strcasecmp(type
, "normal") || !strcasecmp(type
, "chat")) {
732 /* XXX namespaces could be handled better. (mid) */
733 if ((xmlns
= xmlnode_get_tag(p
->x
, "x")))
734 type
= xmlnode_get_attrib(xmlns
, "xmlns");
736 from
= jid_full(p
->from
);
738 if ((y = xmlnode_get_tag(p->x, "html"))) {
739 msg = xmlnode_get_data(y);
742 if ((y
= xmlnode_get_tag(p
->x
, "body"))) {
743 msg
= xmlnode_get_data(y
);
746 msg
= utf8_to_str(msg
);
751 if (type
&& !strcasecmp(type
, "jabber:x:conference")) {
756 room
= xmlnode_get_attrib(xmlns
, "jid");
757 data
= g_strsplit(room
, "@", 2);
758 m
= g_list_append(m
, g_strdup(data
[0]));
759 m
= g_list_append(m
, g_strdup(data
[1]));
760 m
= g_list_append(m
, g_strdup(gjc
->user
->user
));
763 serv_got_chat_invite(GJ_GC(gjc
), room
, from
, msg
, m
);
764 } else if (msg
) { /* whisper */
765 struct jabber_chat
*jc
;
766 g_snprintf(m
, sizeof(m
), "%s", msg
);
767 if (((jc
= find_existing_chat(GJ_GC(gjc
), p
->from
)) != NULL
) && jc
->b
)
768 serv_got_chat_in(GJ_GC(gjc
), jc
->b
->id
, p
->from
->resource
, 1, m
, time(NULL
));
771 if (xmlnode_get_tag(p
->x
, "gaim"))
772 flags
= IM_FLAG_GAIMUSER
;
773 if (find_conversation(jid_full(p
->from
)))
774 serv_got_im(GJ_GC(gjc
), jid_full(p
->from
), m
, flags
, time(NULL
), -1);
777 from
= g_strdup_printf("%s@%s", p
->from
->user
, p
->from
->server
);
779 /* server message? */
780 from
= g_strdup(p
->from
->server
);
782 serv_got_im(GJ_GC(gjc
), from
, m
, flags
, time(NULL
), -1);
791 } else if (!strcasecmp(type
, "error")) {
792 if ((y
= xmlnode_get_tag(p
->x
, "error"))) {
793 type
= xmlnode_get_attrib(y
, "code");
794 msg
= xmlnode_get_data(y
);
798 from
= g_strdup_printf("Error %s", type
? type
: "");
799 do_error_dialog(msg
, from
);
802 } else if (!strcasecmp(type
, "groupchat")) {
803 struct jabber_chat
*jc
;
807 if ((y = xmlnode_get_tag(p->x, "html"))) {
808 msg = xmlnode_get_data(y);
811 if ((y
= xmlnode_get_tag(p
->x
, "body"))) {
812 msg
= xmlnode_get_data(y
);
815 msg
= utf8_to_str(msg
);
817 if ((subj
= xmlnode_get_tag(p
->x
, "subject"))) {
818 topic
= xmlnode_get_data(subj
);
820 topic
= utf8_to_str(topic
);
822 jc
= find_existing_chat(GJ_GC(gjc
), p
->from
);
824 /* we're not in this chat. are we supposed to be? */
825 if ((jc
= find_pending_chat(GJ_GC(gjc
), p
->from
)) != NULL
) {
826 /* yes, we're supposed to be. so now we are. */
827 jc
->b
= serv_got_joined_chat(GJ_GC(gjc
), i
++, p
->from
->user
);
829 jc
->state
= JCS_ACTIVE
;
831 /* no, we're not supposed to be. */
836 if (p
->from
->resource
) {
838 if (!find_chat_buddy(jc
->b
, p
->from
->resource
)) {
839 add_chat_buddy(jc
->b
, p
->from
->resource
);
840 } else if ((y
= xmlnode_get_tag(p
->x
, "status"))) {
843 g_snprintf(buf
, sizeof(buf
), "%s@%s/%s",
844 p
->from
->user
, p
->from
->server
, p
->from
->resource
);
845 jabber_track_away(gjc
, p
, buf
);
848 } else if (jc
->b
&& msg
) {
853 g_snprintf(tbuf
, sizeof(tbuf
), "%s", topic
);
854 chat_set_topic(jc
->b
, p
->from
->resource
, tbuf
);
858 g_snprintf(buf
, sizeof(buf
), "%s", msg
);
859 serv_got_chat_in(GJ_GC(gjc
), jc
->b
->id
, p
->from
->resource
, 0, buf
, time(NULL
));
861 } else { /* message from the server */
864 g_snprintf(tbuf
, sizeof(tbuf
), "%s", topic
);
865 chat_set_topic(jc
->b
, "", tbuf
);
873 debug_printf("unhandled message %s\n", type
);
877 static void jabber_handlepresence(gjconn gjc
, jpacket p
)
879 char *to
, *from
, *type
;
880 struct buddy
*b
= NULL
;
888 struct conversation
*cnv
= NULL
;
889 struct jabber_chat
*jc
= NULL
;
891 to
= xmlnode_get_attrib(p
->x
, "to");
892 from
= xmlnode_get_attrib(p
->x
, "from");
893 type
= xmlnode_get_attrib(p
->x
, "type");
895 if ((y
= xmlnode_get_tag(p
->x
, "show"))) {
896 show
= xmlnode_get_data(y
);
899 } else if (!strcasecmp(show
, "away")) {
901 } else if (!strcasecmp(show
, "chat")) {
903 } else if (!strcasecmp(show
, "xa")) {
905 } else if (!strcasecmp(show
, "dnd")) {
912 who
= jid_new(gjc
->p
, from
);
913 if (who
->user
== NULL
) {
914 /* FIXME: transport */
918 buddy
= g_strdup_printf("%s@%s", who
->user
, who
->server
);
920 /* um. we're going to check if it's a chat. if it isn't, and there are pending
921 * chats, create the chat. if there aren't pending chats and we don't have the
922 * buddy on our list, simply bail out. */
923 if ((cnv
= find_chat(GJ_GC(gjc
), who
->user
)) == NULL
) {
925 if ((jc
= find_pending_chat(GJ_GC(gjc
), who
)) != NULL
) {
926 jc
->b
= cnv
= serv_got_joined_chat(GJ_GC(gjc
), i
++, who
->user
);
928 jc
->state
= JCS_ACTIVE
;
929 } else if ((b
= find_buddy(GJ_GC(gjc
), buddy
)) == NULL
) {
936 resources
= b
->proto_data
;
940 if (!strcmp(res
, resources
->data
))
942 resources
= resources
->next
;
945 if (type
&& (strcasecmp(type
, "unavailable") == 0)) {
947 g_free(resources
->data
);
948 b
->proto_data
= g_slist_remove(b
->proto_data
, resources
->data
);
950 if (!b
->proto_data
) {
951 serv_got_update(GJ_GC(gjc
), buddy
, 0, 0, 0, 0, 0, 0);
954 /* keep track of away msg same as yahoo plugin */
955 jabber_track_away(gjc
, p
, normalize(b
->name
));
958 b
->proto_data
= g_slist_append(b
->proto_data
, g_strdup(res
));
961 serv_got_update(GJ_GC(gjc
), buddy
, 1, 0, 0, 0, state
, 0);
966 if (type
&& !strcasecmp(type
, "unavailable")) {
967 struct jabber_data
*jd
;
968 if (!jc
&& !(jc
= find_existing_chat(GJ_GC(gjc
), who
))) {
972 jd
= jc
->gc
->proto_data
;
973 /* if it's not ourselves...*/
974 if (strcmp(who
->resource
, jc
->Jid
->resource
) && jc
->b
) {
975 remove_chat_buddy(jc
->b
, who
->resource
, NULL
);
980 jc
->state
= JCS_CLOSED
;
981 serv_got_chat_left(GJ_GC(gjc
), jc
->id
);
983 * TBD: put back some day?
984 jd->chats = g_slist_remove(jd->chats, jc);
988 if ((!jc
&& !(jc
= find_existing_chat(GJ_GC(gjc
), who
))) || !jc
->b
) {
992 if (!find_chat_buddy(jc
->b
, who
->resource
)) {
993 add_chat_buddy(jc
->b
, who
->resource
);
996 g_snprintf(buf
, sizeof(buf
), "%s@%s/%s",
997 who
->user
, who
->server
, who
->resource
);
998 jabber_track_away(gjc
, p
, buf
);
1009 static void jabber_handles10n(gjconn gjc
, jpacket p
)
1012 char *Jid
= xmlnode_get_attrib(p
->x
, "from");
1013 char *ask
= xmlnode_get_attrib(p
->x
, "type");
1015 g
= xmlnode_new_tag("presence");
1016 xmlnode_put_attrib(g
, "to", Jid
);
1017 if (!strcmp(ask
, "subscribe"))
1018 xmlnode_put_attrib(g
, "type", "subscribed");
1019 else if (!strcmp(ask
, "unsubscribe"))
1020 xmlnode_put_attrib(g
, "type", "unsubscribed");
1031 * Pending subscription to a buddy?
1033 #define BUD_SUB_TO_PEND(sub, ask) ((!strcasecmp((sub), "none") || !strcasecmp((sub), "from")) && \
1034 (ask) != NULL && !strcasecmp((ask), "subscribe"))
1037 * Subscribed to a buddy?
1039 #define BUD_SUBD_TO(sub, ask) ((!strcasecmp((sub), "to") || !strcasecmp((sub), "both")) && \
1040 ((ask) == NULL || !strcasecmp((ask), "subscribe")))
1043 * Pending unsubscription to a buddy?
1045 #define BUD_USUB_TO_PEND(sub, ask) ((!strcasecmp((sub), "to") || !strcasecmp((sub), "both")) && \
1046 (ask) != NULL && !strcasecmp((ask), "unsubscribe"))
1049 * Unsubscribed to a buddy?
1051 #define BUD_USUBD_TO(sub, ask) ((!strcasecmp((sub), "none") || !strcasecmp((sub), "from")) && \
1052 ((ask) == NULL || !strcasecmp((ask), "unsubscribe")))
1055 * If a buddy is added or removed from the roster on another resource
1056 * jabber_handlebuddy is called
1058 * Called with roster item node.
1060 static void jabber_handlebuddy(gjconn gjc
, xmlnode x
)
1063 char *Jid
, *name
, *sub
, *ask
;
1065 struct buddy
*b
= NULL
;
1066 char *buddyname
, *groupname
;
1068 Jid
= xmlnode_get_attrib(x
, "jid");
1069 name
= xmlnode_get_attrib(x
, "name");
1070 sub
= xmlnode_get_attrib(x
, "subscription");
1071 ask
= xmlnode_get_attrib(x
, "ask");
1072 who
= jid_new(gjc
->p
, Jid
);
1074 /* JFIXME: jabber_handleroster() had a "FIXME: transport" at this
1075 * equivilent point. So...
1077 * We haven't allocated any memory or done anything interesting to
1078 * this point, so we'll violate Good Coding Structure here by
1079 * simply bailing out.
1081 if (!who
|| !who
->user
) {
1085 buddyname
= g_strdup_printf("%s@%s", who
->user
, who
->server
);
1087 if((g
= xmlnode_get_tag(x
, "group")) == NULL
|| (groupname
= xmlnode_get_data(g
)) == NULL
) {
1088 groupname
= _("Buddies");
1092 if (BUD_SUB_TO_PEND(sub
, ask
) || BUD_SUBD_TO(sub
, ask
)) {
1093 if ((b
= find_buddy(GJ_GC(gjc
), buddyname
)) == NULL
) {
1094 debug_printf("adding buddy [4]: %s\n", buddyname
);
1095 b
= add_buddy(GJ_GC(gjc
), groupname
, buddyname
,
1096 name
? name
: buddyname
);
1097 do_export(GJ_GC(gjc
));
1100 * TBD: this is what we *would* do if we could. But the
1101 * remove_buddy() code doesn't update the UI. And if the
1102 * user selects and tries to remove a buddy already removed
1103 * via here: *ka-boom*!
1105 } else if (BUD_USUB_TO_PEND(sub, ask) || BUD_USUBD_TO(sub, ask) || !strcasecmp(sub, "remove")) {
1106 if ((b = find_buddy(GJ_GC(gjc), buddyname)) != NULL) {
1107 struct group *group;
1109 group = find_group_by_buddy(GJ_GC(gjc), buddyname);
1110 debug_printf("removing buddy [1]: %s, from group: %s\n",
1111 buddyname, group->name);
1112 remove_buddy(GJ_GC(gjc), group, b);
1113 do_export(GJ_GC(gjc));
1121 static void jabber_handleroster(gjconn gjc
, xmlnode querynode
)
1125 x
= xmlnode_get_firstchild(querynode
);
1127 jabber_handlebuddy(gjc
, x
);
1128 x
= xmlnode_get_nextsibling(x
);
1131 x
= jutil_presnew(0, NULL
, "Online");
1136 static void jabber_handleauthresp(gjconn gjc
, jpacket p
)
1138 if (jpacket_subtype(p
) == JPACKET__RESULT
) {
1139 if (xmlnode_has_children(p
->x
)) {
1140 xmlnode query
= xmlnode_get_tag(p
->x
, "query");
1141 set_login_progress(GJ_GC(gjc
), 4, _("Authenticating"));
1142 if (!xmlnode_get_tag(query
, "digest")) {
1148 debug_printf("auth success\n");
1150 account_online(GJ_GC(gjc
));
1151 serv_finish_login(GJ_GC(gjc
));
1153 if (bud_list_cache_exists(GJ_GC(gjc
)))
1154 do_import(GJ_GC(gjc
), NULL
);
1156 ((struct jabber_data
*)GJ_GC(gjc
)->proto_data
)->did_import
= TRUE
;
1158 gjab_reqroster(gjc
);
1162 char *errmsg
= NULL
;
1164 struct jabber_data
*jd
= GJ_GC(gjc
)->proto_data
;
1166 debug_printf("auth failed\n");
1167 xerr
= xmlnode_get_tag(p
->x
, "error");
1170 errmsg
= xmlnode_get_data(xerr
);
1171 if (xmlnode_get_attrib(xerr
, "code")) {
1172 errcode
= atoi(xmlnode_get_attrib(xerr
, "code"));
1173 g_snprintf(msg
, sizeof(msg
), "Error %d: %s", errcode
, errmsg
);
1175 g_snprintf(msg
, sizeof(msg
), "%s", errmsg
);
1176 hide_login_progress(GJ_GC(gjc
), msg
);
1178 hide_login_progress(GJ_GC(gjc
), _("Unknown login error"));
1185 static void jabber_handleversion(gjconn gjc
, xmlnode iqnode
) {
1186 xmlnode querynode
, x
;
1189 struct utsname osinfo
;
1192 g_snprintf(os
, sizeof os
, "%s %s %s", osinfo
.sysname
, osinfo
.release
, osinfo
.machine
);
1194 id
= xmlnode_get_attrib(iqnode
, "id");
1195 from
= xmlnode_get_attrib(iqnode
, "from");
1197 x
= jutil_iqnew(JPACKET__RESULT
, NS_VERSION
);
1199 xmlnode_put_attrib(x
, "to", from
);
1200 xmlnode_put_attrib(x
, "id", id
);
1201 querynode
= xmlnode_get_tag(x
, "query");
1202 xmlnode_insert_cdata(xmlnode_insert_tag(querynode
, "name"), PACKAGE
, -1);
1203 xmlnode_insert_cdata(xmlnode_insert_tag(querynode
, "version"), VERSION
, -1);
1204 xmlnode_insert_cdata(xmlnode_insert_tag(querynode
, "os"), os
, -1);
1211 static void jabber_handletime(gjconn gjc
, xmlnode iqnode
) {
1212 xmlnode querynode
, x
;
1219 now
= localtime(&now_t
);
1221 id
= xmlnode_get_attrib(iqnode
, "id");
1222 from
= xmlnode_get_attrib(iqnode
, "from");
1224 x
= jutil_iqnew(JPACKET__RESULT
, NS_TIME
);
1226 xmlnode_put_attrib(x
, "to", from
);
1227 xmlnode_put_attrib(x
, "id", id
);
1228 querynode
= xmlnode_get_tag(x
, "query");
1230 strftime(buf
, 1024, "%Y%m%dT%T", now
);
1231 xmlnode_insert_cdata(xmlnode_insert_tag(querynode
, "utc"), buf
, -1);
1232 strftime(buf
, 1024, "%Z", now
);
1233 xmlnode_insert_cdata(xmlnode_insert_tag(querynode
, "tz"), buf
, -1);
1234 strftime(buf
, 1024, "%d %b %Y %T", now
);
1235 xmlnode_insert_cdata(xmlnode_insert_tag(querynode
, "display"), buf
, -1);
1242 static void jabber_handlelast(gjconn gjc
, xmlnode iqnode
) {
1243 xmlnode x
, querytag
;
1245 struct jabber_data
*jd
= GJ_GC(gjc
)->proto_data
;
1248 id
= xmlnode_get_attrib(iqnode
, "id");
1249 from
= xmlnode_get_attrib(iqnode
, "from");
1251 x
= jutil_iqnew(JPACKET__RESULT
, "jabber:iq:last");
1253 xmlnode_put_attrib(x
, "to", from
);
1254 xmlnode_put_attrib(x
, "id", id
);
1255 querytag
= xmlnode_get_tag(x
, "query");
1256 g_snprintf(idle_time
, sizeof idle_time
, "%ld", jd
->idle
? time(NULL
) - jd
->idle
: 0);
1257 xmlnode_put_attrib(querytag
, "seconds", idle_time
);
1264 * delete == TRUE: delete found entry
1266 * returns pointer to (local) copy of value if found, NULL otherwise
1268 * Note: non-reentrant! Local static storage re-used on subsequent calls.
1269 * If you're going to need to keep the returned value, make a copy!
1271 static gchar
*jabber_track_queries(GHashTable
*queries
, gchar
*key
, gboolean
delete)
1273 gpointer my_key
, my_val
;
1274 static gchar
*ret_val
= NULL
;
1276 if(ret_val
!= NULL
) {
1281 /* self-protection */
1282 if(queries
!= NULL
&& key
!= NULL
) {
1283 if(g_hash_table_lookup_extended(queries
, key
, &my_key
, &my_val
)) {
1284 ret_val
= g_strdup((gchar
*) my_val
);
1286 g_hash_table_remove(queries
, key
);
1296 static void jabber_handlepacket(gjconn gjc
, jpacket p
)
1300 case JPACKET_MESSAGE
:
1301 jabber_handlemessage(gjc
, p
);
1303 case JPACKET_PRESENCE
:
1304 jabber_handlepresence(gjc
, p
);
1307 debug_printf("jpacket_subtype: %d\n", jpacket_subtype(p
));
1309 id
= xmlnode_get_attrib(p
->x
, "id");
1310 if (id
!= NULL
&& !strcmp(id
, IQID_AUTH
)) {
1311 jabber_handleauthresp(gjc
, p
);
1315 if (jpacket_subtype(p
) == JPACKET__SET
) {
1317 querynode
= xmlnode_get_tag(p
->x
, "query");
1318 if (NSCHECK(querynode
, "jabber:iq:roster")) {
1319 jabber_handlebuddy(gjc
, xmlnode_get_firstchild(querynode
));
1321 } else if (jpacket_subtype(p
) == JPACKET__GET
) {
1323 querynode
= xmlnode_get_tag(p
->x
, "query");
1324 if (NSCHECK(querynode
, NS_VERSION
)) {
1325 jabber_handleversion(gjc
, p
->x
);
1326 } else if (NSCHECK(querynode
, NS_TIME
)) {
1327 jabber_handletime(gjc
, p
->x
);
1328 } else if (NSCHECK(querynode
, "jabber:iq:last")) {
1329 jabber_handlelast(gjc
, p
->x
);
1331 } else if (jpacket_subtype(p
) == JPACKET__RESULT
) {
1332 xmlnode querynode
, vcard
;
1336 * TBD: ISTM maybe this part could use a serious re-work?
1338 from
= xmlnode_get_attrib(p
->x
, "from");
1339 querynode
= xmlnode_get_tag(p
->x
, "query");
1340 vcard
= xmlnode_get_tag(p
->x
, "vCard");
1342 vcard
= xmlnode_get_tag(p
->x
, "VCARD");
1344 if (NSCHECK(querynode
, NS_ROSTER
)) {
1345 jabber_handleroster(gjc
, querynode
);
1346 } else if (NSCHECK(querynode
, NS_VCARD
)) {
1347 jabber_track_queries(gjc
->queries
, id
, TRUE
); /* delete query track */
1348 jabber_handlevcard(gjc
, querynode
, from
);
1350 jabber_track_queries(gjc
->queries
, id
, TRUE
); /* delete query track */
1351 jabber_handlevcard(gjc
, vcard
, from
);
1352 } else if((xmlns
= xmlnode_get_attrib(querynode
, "xmlns")) != NULL
) {
1353 debug_printf("jabber:iq:query: %s\n", xmlns
);
1357 debug_printf("jabber:iq: %s\n", xmlnode2str(p
->x
));
1359 /* handle "null" query results */
1360 if((val
= jabber_track_queries(gjc
->queries
, id
, TRUE
)) != NULL
) {
1361 if(strcmp((char *) val
, "vCard") == 0) {
1363 * No actual vCard, but there's other stuff. This
1364 * way the user always gets some kind of response.
1366 jabber_handlevcard(gjc
, NULL
, from
);
1371 } else if (jpacket_subtype(p
) == JPACKET__ERROR
) {
1373 char *from
, *errmsg
= NULL
;
1376 from
= xmlnode_get_attrib(p
->x
, "from");
1377 xerr
= xmlnode_get_tag(p
->x
, "error");
1379 errmsg
= xmlnode_get_data(xerr
);
1380 if (xmlnode_get_attrib(xerr
, "code"))
1381 errcode
= atoi(xmlnode_get_attrib(xerr
, "code"));
1384 from
= g_strdup_printf("Error %d (%s)", errcode
, from
);
1385 do_error_dialog(errmsg
, from
);
1393 jabber_handles10n(gjc
, p
);
1396 debug_printf("jabber: packet type %d (%s)\n", p
->type
, xmlnode2str(p
->x
));
1404 static void jabber_handlestate(gjconn gjc
, int state
)
1407 case JCONN_STATE_OFF
:
1408 hide_login_progress(GJ_GC(gjc
), _("Unable to connect"));
1409 signoff(GJ_GC(gjc
));
1411 case JCONN_STATE_CONNECTED
:
1412 set_login_progress(GJ_GC(gjc
), 2, _("Connected"));
1414 case JCONN_STATE_ON
:
1415 set_login_progress(GJ_GC(gjc
), 3, _("Requesting Authentication Method"));
1419 debug_printf("state change: %d\n", state
);
1424 static void jabber_login(struct aim_user
*user
)
1426 struct gaim_connection
*gc
= new_gaim_conn(user
);
1427 struct jabber_data
*jd
= gc
->proto_data
= g_new0(struct jabber_data
, 1);
1428 char *loginname
= create_valid_jid(user
->username
, DEFAULT_SERVER
, "GAIM");
1430 jd
->hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
1431 jd
->chats
= NULL
; /* we have no chats yet */
1433 set_login_progress(gc
, 1, _("Connecting"));
1435 if (!(jd
->gjc
= gjab_new(loginname
, user
->password
, gc
))) {
1437 debug_printf("jabber: unable to connect (jab_new failed)\n");
1438 hide_login_progress(gc
, _("Unable to connect"));
1444 gjab_state_handler(jd
->gjc
, jabber_handlestate
);
1445 gjab_packet_handler(jd
->gjc
, jabber_handlepacket
);
1446 jd
->gjc
->queries
= g_hash_table_new(g_str_hash
, g_str_equal
);
1447 gjab_start(jd
->gjc
);
1450 static gboolean
jabber_destroy_hash(gpointer key
, gpointer val
, gpointer data
) {
1456 static gboolean
jabber_free(gpointer data
)
1458 struct jabber_data
*jd
= data
;
1460 gjab_delete(jd
->gjc
);
1461 g_free(jd
->gjc
->sid
);
1468 static void jabber_close(struct gaim_connection
*gc
)
1470 struct jabber_data
*jd
= gc
->proto_data
;
1473 GSList
*jcs
= jd
->chats
;
1475 /* Free-up the jabber_chat struct allocs and the list */
1480 g_slist_free(jd
->chats
);
1482 /* Free-up the away status memories and the list */
1483 if(jd
->hash
!= NULL
) {
1484 g_hash_table_foreach_remove(jd
->hash
, jabber_destroy_hash
, NULL
);
1485 g_hash_table_destroy(jd
->hash
);
1489 /* Free-up the pending queries memories and the list */
1490 if(jd
->gjc
->queries
!= NULL
) {
1491 g_hash_table_foreach_remove(jd
->gjc
->queries
, jabber_destroy_hash
, NULL
);
1492 g_hash_table_destroy(jd
->gjc
->queries
);
1493 jd
->gjc
->queries
= NULL
;
1497 gaim_input_remove(gc
->inpa
);
1500 g_timeout_add(50, jabber_free
, jd
);
1501 xmlnode_free(jd
->gjc
->current
);
1503 gc
->proto_data
= NULL
;
1506 static int jabber_send_im(struct gaim_connection
*gc
, char *who
, char *message
, int len
, int flags
)
1510 gjconn gjc
= ((struct jabber_data
*)gc
->proto_data
)->gjc
;
1512 if (!who
|| !message
)
1515 x
= xmlnode_new_tag("message");
1516 /* Bare username and "username" not the server itself? */
1517 if (!strchr(who
, '@') && strcmp(who
, gjc
->user
->server
) != 0)
1518 realwho
= g_strdup_printf("%s@%s", who
, gjc
->user
->server
);
1520 realwho
= g_strdup(who
);
1521 xmlnode_put_attrib(x
, "to", realwho
);
1524 xmlnode_insert_tag(x
, "gaim");
1525 xmlnode_put_attrib(x
, "type", "chat");
1527 if (message
&& strlen(message
)) {
1528 char *utf8
= str_to_utf8(message
);
1529 y
= xmlnode_insert_tag(x
, "body");
1530 xmlnode_insert_cdata(y
, utf8
, -1);
1534 gjab_send(((struct jabber_data
*)gc
->proto_data
)->gjc
, x
);
1539 static void jabber_add_buddy(struct gaim_connection
*gc
, char *name
)
1543 gjconn gjc
= ((struct jabber_data
*)gc
->proto_data
)->gjc
;
1544 struct buddy
*buddy
= NULL
;
1545 struct group
*buddy_group
= NULL
;
1547 if (!((struct jabber_data
*)gc
->proto_data
)->did_import
)
1553 if (!strcmp(gc
->username
, name
))
1556 if (!strchr(name
, '@'))
1557 realwho
= g_strdup_printf("%s@%s", name
, gjc
->user
->server
);
1559 jid who
= jid_new(gjc
->p
, name
);
1560 if (who
->user
== NULL
) {
1561 /* FIXME: transport */
1564 realwho
= g_strdup_printf("%s@%s", who
->user
, who
->server
);
1568 x
= jutil_iqnew(JPACKET__SET
, NS_ROSTER
);
1569 y
= xmlnode_insert_tag(xmlnode_get_tag(x
, "query"), "item");
1570 xmlnode_put_attrib(y
, "jid", realwho
);
1572 /* If we can find the buddy, there's an alias for him and
1573 * it's not 0-length, add the "name" attribute.
1575 if((buddy
= find_buddy(gc
, realwho
)) != NULL
&&
1576 buddy
->show
!= NULL
&& (buddy
->show
)[0] != '\0') {
1578 xmlnode_put_attrib(y
, "name", buddy
->show
);
1582 * Find out what group the buddy's in and send that along
1583 * with the roster item.
1585 if((buddy_group
= find_group_by_buddy(gc
, realwho
)) != NULL
) {
1587 z
= xmlnode_insert_tag(y
, "group");
1588 xmlnode_insert_cdata(z
, buddy_group
->name
, -1);
1591 gjab_send(((struct jabber_data
*)gc
->proto_data
)->gjc
, x
);
1594 x
= xmlnode_new_tag("presence");
1595 xmlnode_put_attrib(x
, "to", realwho
);
1596 xmlnode_put_attrib(x
, "type", "subscribe");
1597 gjab_send(((struct jabber_data
*)gc
->proto_data
)->gjc
, x
);
1603 static void jabber_remove_buddy(struct gaim_connection
*gc
, char *name
, char *group
)
1607 gjconn gjc
= ((struct jabber_data
*)gc
->proto_data
)->gjc
;
1612 if (!strchr(name
, '@'))
1613 realwho
= g_strdup_printf("%s@%s", name
, gjc
->user
->server
);
1615 realwho
= g_strdup(name
);
1617 x
= xmlnode_new_tag("presence");
1618 xmlnode_put_attrib(x
, "to", realwho
);
1619 xmlnode_put_attrib(x
, "type", "unsubscribe");
1620 gjab_send(((struct jabber_data
*)gc
->proto_data
)->gjc
, x
);
1625 static char **jabber_list_icon(int uc
)
1629 return available_away_xpm
;
1631 return available_chat_xpm
;
1633 return available_xa_xpm
;
1635 return available_dnd_xpm
;
1637 return available_xpm
;
1641 static GList
*jabber_chat_info(struct gaim_connection
*gc
)
1643 gjconn gjc
= ((struct jabber_data
*)gc
->proto_data
)->gjc
;
1645 static char *confserv
= NULL
; /* this pointer must be persistent */
1649 struct proto_chat_entry
*pce
;
1651 /* This is a scientific wild-ass guess...
1653 * If there are more than two "components" to the current server name,
1654 * lop-off the left-most component and replace with "conference."
1656 if(confserv
!= NULL
) {
1657 g_free(confserv
); /* dispose of the old value */
1660 if((server
= g_strdup(gjc
->user
->server
)) == NULL
) {
1661 confserv
= g_strdup(DEFAULT_GROUPCHAT
);
1663 gchar
**splits
, **index
;
1668 index
= splits
= g_strsplit(server
, ".", -1); /* split the connected server */
1670 while(*(index
++)) /* index to the end--counting the parts */
1674 * If we've more than two parts, point to the second part. Else point
1683 /* Put it together */
1684 confserv
= g_strjoin(".", "conference", (tmp
= g_strjoinv(".", index
)), NULL
);
1686 g_free(server
); /* we don't need this stuff no more */
1691 pce
= g_new0(struct proto_chat_entry
, 1);
1692 pce
->label
= _("Room:");
1693 m
= g_list_append(m
, pce
);
1695 pce
= g_new0(struct proto_chat_entry
, 1);
1696 pce
->label
= _("Server:");
1697 pce
->def
= confserv
;
1698 m
= g_list_append(m
, pce
);
1700 pce
= g_new0(struct proto_chat_entry
, 1);
1701 pce
->label
= _("Handle:");
1702 pce
->def
= gjc
->user
->user
;
1703 m
= g_list_append(m
, pce
);
1708 static void jabber_join_chat(struct gaim_connection
*gc
, GList
*data
)
1712 gjconn gjc
= ((struct jabber_data
*)gc
->proto_data
)->gjc
;
1713 GSList
*jcs
= ((struct jabber_data
*)gc
->proto_data
)->chats
;
1714 struct jabber_chat
*jc
;
1717 if (!data
|| !data
->next
|| !data
->next
->next
)
1720 realwho
= create_valid_jid(data
->data
, data
->next
->data
,
1721 data
->next
->next
->data
);
1722 debug_printf("%s\n", realwho
);
1724 Jid
= jid_new(gjc
->p
, realwho
);
1726 if((jc
= find_any_chat(gc
, Jid
)) != NULL
) {
1727 free(Jid
); /* don't need it, after all */
1730 debug_printf("attempt to re-join already pending Jabber chat! (ignoring)\n");
1731 g_free(realwho
); /* yuck! */
1734 debug_printf("attempt to re-join already active Jabber chat! (ignoring)\n");
1735 g_free(realwho
); /* yuck! */
1738 debug_printf("rejoining previously closed Jabber chat\n");
1741 debug_printf("found Jabber chat in unknown state! (ignoring)\n");
1742 g_free(realwho
); /* yuck! */
1746 debug_printf("joining completely new Jabber chat\n");
1747 jc
= g_new0(struct jabber_chat
, 1);
1750 ((struct jabber_data
*)gc
->proto_data
)->chats
= g_slist_append(jcs
, jc
);
1753 jc
->state
= JCS_PENDING
;
1755 x
= jutil_presnew(0, realwho
, NULL
);
1761 static void jabber_chat_invite(struct gaim_connection
*gc
, int id
, char *message
, char *name
)
1764 struct jabber_data
*jd
= gc
->proto_data
;
1765 gjconn gjc
= jd
->gjc
;
1766 struct jabber_chat
*jc
= NULL
;
1767 char *realwho
, *subject
;
1772 /* find which chat we're inviting to */
1773 if(jabber_find_chat_by_convo_id(gc
, id
, &jc
) != 0)
1776 x
= xmlnode_new_tag("message");
1777 if (!strchr(name
, '@'))
1778 realwho
= g_strdup_printf("%s@%s", name
, gjc
->user
->server
);
1780 realwho
= g_strdup(name
);
1781 xmlnode_put_attrib(x
, "to", realwho
);
1784 y
= xmlnode_insert_tag(x
, "x");
1785 xmlnode_put_attrib(y
, "xmlns", "jabber:x:conference");
1786 subject
= g_strdup_printf("%s@%s", jc
->Jid
->user
, jc
->Jid
->server
);
1787 xmlnode_put_attrib(y
, "jid", subject
);
1790 if (message
&& strlen(message
)) {
1791 char *utf8
= str_to_utf8(message
);
1792 y
= xmlnode_insert_tag(x
, "body");
1793 xmlnode_insert_cdata(y
, utf8
, -1);
1797 gjab_send(((struct jabber_data
*)gc
->proto_data
)->gjc
, x
);
1801 static void jabber_chat_leave(struct gaim_connection
*gc
, int id
)
1803 struct jabber_data
*jd
= gc
->proto_data
;
1804 gjconn gjc
= jd
->gjc
;
1805 struct jabber_chat
*jc
= NULL
;
1809 /* Find out which chat we're leaving */
1810 if(jabber_find_chat_by_convo_id(gc
, id
, &jc
) != 0)
1813 realwho
= g_strdup_printf("%s@%s", jc
->Jid
->user
, jc
->Jid
->server
);
1814 x
= jutil_presnew(0, realwho
, NULL
);
1816 xmlnode_put_attrib(x
, "type", "unavailable");
1822 static int jabber_chat_send(struct gaim_connection
*gc
, int id
, char *message
)
1825 struct jabber_chat
*jc
= NULL
;
1829 /* Find out which chat we're sending to */
1830 if((retval
= jabber_find_chat_by_convo_id(gc
, id
, &jc
)) != 0)
1833 x
= xmlnode_new_tag("message");
1834 xmlnode_put_attrib(x
, "from", jid_full(jc
->Jid
));
1835 chatname
= g_strdup_printf("%s@%s", jc
->Jid
->user
, jc
->Jid
->server
);
1836 xmlnode_put_attrib(x
, "to", chatname
);
1838 xmlnode_put_attrib(x
, "type", "groupchat");
1840 if (message
&& strlen(message
) > strlen("/topic ") &&
1841 !g_strncasecmp(message
, "/topic ", strlen("/topic "))) {
1843 char *utf8
= str_to_utf8(message
+ strlen("/topic "));
1844 y
= xmlnode_insert_tag(x
, "subject");
1845 xmlnode_insert_cdata(y
, utf8
, -1);
1846 y
= xmlnode_insert_tag(x
, "body");
1847 g_snprintf(buf
, sizeof(buf
), "/me has changed the subject to: %s", utf8
);
1848 xmlnode_insert_cdata(y
, buf
, -1);
1850 } else if (message
&& strlen(message
)) {
1851 char *utf8
= str_to_utf8(message
);
1852 y
= xmlnode_insert_tag(x
, "body");
1853 xmlnode_insert_cdata(y
, utf8
, -1);
1857 gjab_send(((struct jabber_data
*)gc
->proto_data
)->gjc
, x
);
1862 static void jabber_chat_whisper(struct gaim_connection
*gc
, int id
, char *who
, char *message
)
1865 struct jabber_chat
*jc
= NULL
;
1868 /* Find out which chat we're whispering to */
1869 if(jabber_find_chat_by_convo_id(gc
, id
, &jc
) != 0)
1872 x
= xmlnode_new_tag("message");
1873 xmlnode_put_attrib(x
, "from", jid_full(jc
->Jid
));
1874 chatname
= g_strdup_printf("%s@%s/%s", jc
->Jid
->user
, jc
->Jid
->server
, who
);
1875 xmlnode_put_attrib(x
, "to", chatname
);
1877 xmlnode_put_attrib(x
, "type", "normal");
1879 if (message
&& strlen(message
)) {
1880 char *utf8
= str_to_utf8(message
);
1881 y
= xmlnode_insert_tag(x
, "body");
1882 xmlnode_insert_cdata(y
, utf8
, -1);
1886 gjab_send(((struct jabber_data
*)gc
->proto_data
)->gjc
, x
);
1890 static char *jabber_normalize(const char *s
)
1892 static char buf
[BUF_LEN
];
1896 g_return_val_if_fail((s
!= NULL
), NULL
);
1898 /* Somebody called us with s == NULL once... */
1902 u
= t
= g_strdup(s
);
1906 while (*t
&& (x
< BUF_LEN
- 1)) {
1914 if (!strchr(buf
, '@')) {
1915 strcat(buf
, "@jabber.org"); /* this isn't always right, but eh */
1916 } else if ((u
= strchr(strchr(buf
, '@'), '/')) != NULL
) {
1924 static void jabber_get_info(struct gaim_connection
*gc
, char *who
) {
1928 struct jabber_data
*jd
= gc
->proto_data
;
1929 gjconn gjc
= jd
->gjc
;
1931 x
= jutil_iqnew(JPACKET__GET
, NS_VCARD
);
1932 /* Bare username? */
1933 if (!strchr(who
, '@')) {
1934 realwho
= g_strdup_printf("%s@%s", who
, gjc
->user
->server
);
1936 realwho
= g_strdup(who
);
1938 xmlnode_put_attrib(x
, "to", realwho
);
1941 id
= gjab_getid(gjc
);
1942 xmlnode_put_attrib(x
, "id", id
);
1944 g_hash_table_insert(jd
->gjc
->queries
, g_strdup(id
), g_strdup("vCard"));
1952 static void jabber_get_away_msg(struct gaim_connection
*gc
, char *who
) {
1953 struct jabber_data
*jd
= gc
->proto_data
;
1954 gjconn gjc
= jd
->gjc
;
1957 /* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */
1958 gchar
**str_arr
= (gchar
**) g_new(gpointer
, 3);
1959 gchar
**ap
= str_arr
;
1960 gchar
*realwho
, *final
;
1962 /* Bare username? */
1963 if (!strchr(who
, '@')) {
1964 realwho
= g_strdup_printf("%s@%s", who
, gjc
->user
->server
);
1966 realwho
= g_strdup(who
);
1968 *ap
++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho
);
1970 if((status
= g_hash_table_lookup(jd
->hash
, realwho
)) == NULL
) {
1971 status
= _("Online");
1973 *ap
++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status
);
1977 final
= g_strjoinv(NULL
, str_arr
);
1978 g_strfreev(str_arr
);
1980 g_show_info_text(gc
, realwho
, 2, final
, NULL
);
1986 static void jabber_get_cb_info(struct gaim_connection
*gc
, int cid
, char *who
) {
1987 struct jabber_chat
*jc
= NULL
;
1990 /* Find out which chat */
1991 if(jabber_find_chat_by_convo_id(gc
, cid
, &jc
) != 0)
1994 realwho
= g_strdup_printf("%s@%s/%s", jc
->Jid
->user
, jc
->Jid
->server
, who
);
1996 jabber_get_info(gc
, realwho
);
2000 static void jabber_get_cb_away_msg(struct gaim_connection
*gc
, int cid
, char *who
) {
2001 struct jabber_chat
*jc
= NULL
;
2004 /* Find out which chat */
2005 if(jabber_find_chat_by_convo_id(gc
, cid
, &jc
) != 0)
2008 realwho
= g_strdup_printf("%s@%s/%s", jc
->Jid
->user
, jc
->Jid
->server
, who
);
2010 jabber_get_away_msg(gc
, realwho
);
2016 static GList
*jabber_buddy_menu(struct gaim_connection
*gc
, char *who
) {
2018 struct proto_buddy_menu
*pbm
;
2020 pbm
= g_new0(struct proto_buddy_menu
, 1);
2021 pbm
->label
= _("Get Info");
2022 pbm
->callback
= jabber_get_info
;
2024 m
= g_list_append(m
, pbm
);
2025 pbm
= g_new0(struct proto_buddy_menu
, 1);
2026 pbm
->label
= _("Get Away Msg");
2027 pbm
->callback
= jabber_get_away_msg
;
2029 m
= g_list_append(m
, pbm
);
2034 static GList
*jabber_away_states(struct gaim_connection
*gc
) {
2037 m
= g_list_append(m
, "Online");
2038 m
= g_list_append(m
, "Chatty");
2039 m
= g_list_append(m
, "Away");
2040 m
= g_list_append(m
, "Extended Away");
2041 m
= g_list_append(m
, "Do Not Disturb");
2046 static void jabber_set_away(struct gaim_connection
*gc
, char *state
, char *message
)
2049 struct jabber_data
*jd
= gc
->proto_data
;
2050 gjconn gjc
= jd
->gjc
;
2052 gc
->away
= NULL
; /* never send an auto-response */
2054 x
= xmlnode_new_tag("presence");
2056 if (!strcmp(state
, GAIM_AWAY_CUSTOM
)) {
2057 /* oh goody. Gaim is telling us what to do. */
2059 /* Gaim wants us to be away */
2060 y
= xmlnode_insert_tag(x
, "show");
2061 xmlnode_insert_cdata(y
, "away", -1);
2062 y
= xmlnode_insert_tag(x
, "status");
2063 xmlnode_insert_cdata(y
, message
, -1);
2066 /* Gaim wants us to not be away */
2067 /* but for Jabber, we can just send presence with no other information. */
2070 /* state is one of our own strings. it won't be NULL. */
2071 if (!strcmp(state
, "Online")) {
2072 /* once again, we don't have to put anything here */
2073 } else if (!strcmp(state
, "Chatty")) {
2074 y
= xmlnode_insert_tag(x
, "show");
2075 xmlnode_insert_cdata(y
, "chat", -1);
2076 } else if (!strcmp(state
, "Away")) {
2077 y
= xmlnode_insert_tag(x
, "show");
2078 xmlnode_insert_cdata(y
, "away", -1);
2080 } else if (!strcmp(state
, "Extended Away")) {
2081 y
= xmlnode_insert_tag(x
, "show");
2082 xmlnode_insert_cdata(y
, "xa", -1);
2084 } else if (!strcmp(state
, "Do Not Disturb")) {
2085 y
= xmlnode_insert_tag(x
, "show");
2086 xmlnode_insert_cdata(y
, "dnd", -1);
2095 static void jabber_set_idle(struct gaim_connection
*gc
, int idle
) {
2096 struct jabber_data
*jd
= (struct jabber_data
*)gc
->proto_data
;
2097 debug_printf("jabber_set_idle: setting idle %i\n", idle
);
2098 jd
->idle
= idle
? time(NULL
) - idle
: idle
;
2101 static void jabber_keepalive(struct gaim_connection
*gc
) {
2102 struct jabber_data
*jd
= (struct jabber_data
*)gc
->proto_data
;
2103 gjab_send_raw(jd
->gjc
, " \t ");
2106 static GList
*jabber_user_opts()
2109 struct proto_user_opt
*puo
;
2111 puo
= g_new0(struct proto_user_opt
, 1);
2112 puo
->label
= "Port:";
2114 puo
->pos
= USEROPT_PORT
;
2115 m
= g_list_append(m
, puo
);
2120 static void jabber_buddy_free(struct buddy
*b
)
2122 while (b
->proto_data
) {
2123 g_free(((GSList
*)b
->proto_data
)->data
);
2124 b
->proto_data
= g_slist_remove(b
->proto_data
, ((GSList
*)b
->proto_data
)->data
);
2128 /*---------------------------------------*/
2129 /* Jabber "set info" (vCard) support */
2130 /*---------------------------------------*/
2135 * <vCard prodid='' version='' xmlns=''>
2165 * http://docs.jabber.org/proto/html/vcard-temp.html
2166 * http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd
2170 * Cross-reference user-friendly V-Card entry labels to vCard XML tags
2173 * Order is (or should be) unimportant. For example: we have no way of
2174 * knowing in what order real data will arrive.
2176 * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag
2177 * name, XML tag's parent tag "path" (relative to vCard node).
2179 * List is terminated by a NULL label pointer.
2181 * Entries with no label text, but with XML tag and parent tag
2182 * entries, are used by V-Card XML construction routines to
2183 * "automagically" construct the appropriate XML node tree.
2185 * Thoughts on future direction/expansion
2187 * This is a "simple" vCard.
2189 * It is possible for nodes other than the "vCard" node to have
2190 * attributes. Should that prove necessary/desirable, add an
2191 * "attributes" pointer to the vcard_template struct, create the
2192 * necessary tag_attr structs, and add 'em to the vcard_dflt_data
2195 * The above changes will (obviously) require changes to the vCard
2196 * construction routines.
2199 struct vcard_template
{
2200 char *label
; /* label text pointer */
2201 char *text
; /* entry text pointer */
2202 int visible
; /* should entry field be "visible?" */
2203 int editable
; /* should entry field be editable? */
2204 char *tag
; /* tag text */
2205 char *ptag
; /* parent tag "path" text */
2206 char *url
; /* vCard display format if URL */
2207 } vcard_template_data
[] = {
2208 {N_("Full Name"), NULL
, TRUE
, TRUE
, "FN", NULL
, NULL
},
2209 {N_("Family Name"), NULL
, TRUE
, TRUE
, "FAMILY", "N", NULL
},
2210 {N_("Given Name"), NULL
, TRUE
, TRUE
, "GIVEN", "N", NULL
},
2211 {N_("Nickname"), NULL
, TRUE
, TRUE
, "NICKNAME", NULL
, NULL
},
2212 {N_("URL"), NULL
, TRUE
, TRUE
, "URL", NULL
, "<A HREF=\"%s\">%s</A>"},
2213 {N_("Street Address"), NULL
, TRUE
, TRUE
, "STREET", "ADR", NULL
},
2214 {N_("Extended Address"), NULL
, TRUE
, TRUE
, "EXTADD", "ADR", NULL
},
2215 {N_("Locality"), NULL
, TRUE
, TRUE
, "LOCALITY", "ADR", NULL
},
2216 {N_("Region"), NULL
, TRUE
, TRUE
, "REGION", "ADR", NULL
},
2217 {N_("Postal Code"), NULL
, TRUE
, TRUE
, "PCODE", "ADR", NULL
},
2218 {N_("Country"), NULL
, TRUE
, TRUE
, "COUNTRY", "ADR", NULL
},
2219 {N_("Telephone"), NULL
, TRUE
, TRUE
, "TELEPHONE", NULL
, NULL
},
2220 {N_("Email"), NULL
, TRUE
, TRUE
, "EMAIL", NULL
, "<A HREF=\"mailto:%s\">%s</A>"},
2221 {N_("Organization Name"), NULL
, TRUE
, TRUE
, "ORGNAME", "ORG", NULL
},
2222 {N_("Organization Unit"), NULL
, TRUE
, TRUE
, "ORGUNIT", "ORG", NULL
},
2223 {N_("Title"), NULL
, TRUE
, TRUE
, "TITLE", NULL
, NULL
},
2224 {N_("Role"), NULL
, TRUE
, TRUE
, "ROLE", NULL
, NULL
},
2225 {N_("Birthday"), NULL
, TRUE
, TRUE
, "BDAY", NULL
, NULL
},
2226 {N_("Description"), NULL
, TRUE
, TRUE
, "DESC", NULL
, NULL
},
2227 {"", NULL
, TRUE
, TRUE
, "N", NULL
, NULL
},
2228 {"", NULL
, TRUE
, TRUE
, "ADR", NULL
, NULL
},
2229 {"", NULL
, TRUE
, TRUE
, "ORG", NULL
, NULL
},
2230 {NULL
, NULL
, 0, 0, NULL
, NULL
, NULL
}
2234 * The "vCard" tag's attibute list...
2239 } vcard_tag_attr_list
[] = {
2240 {"prodid", "-//HandGen//NONSGML vGen v1.0//EN"},
2241 {"version", "2.0", },
2242 {"xmlns", "vcard-temp", },
2248 * V-Card user instructions
2250 static char *multi_entry_instructions
=
2251 N_("All items below are optional. Enter only the information with which you feel comfortable");
2252 static char *entries_title
= N_("User Identity");
2255 * Used by routines to parse an XML-encoded string into an xmlnode tree
2260 } *xmlstr2xmlnode_parser
, xmlstr2xmlnode_parser_struct
;
2264 * Display a Jabber vCard
2266 static void jabber_handlevcard(gjconn gjc
, xmlnode querynode
, char *from
)
2268 struct gaim_connection
*gc
= GJ_GC(gjc
);
2269 struct jabber_data
*jd
= GJ_GC(gjc
)->proto_data
;
2270 jid who
= jid_new(gjc
->p
, from
);
2271 char *cdata
, *status
;
2273 struct vcard_template
*vc_tp
= vcard_template_data
;
2275 /* space for all vCard elements + Jabber I.D. + "status" + NULL (list terminator) */
2276 gchar
**str_arr
= (gchar
**) g_new(gpointer
,
2277 (sizeof(vcard_template_data
)/sizeof(struct vcard_template
)) + 3);
2278 gchar
**ap
= str_arr
;
2279 gchar
*buddy
, *final
;
2281 if(who
->resource
!= NULL
&& (who
->resource
)[0] != '\0') {
2282 buddy
= g_strdup_printf("%s@%s/%s", who
->user
, who
->server
, who
->resource
);
2284 buddy
= g_strdup_printf("%s@%s", who
->user
, who
->server
);
2286 *ap
++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", buddy
);
2288 for(vc_tp
= vcard_template_data
; vc_tp
->label
!= NULL
; ++vc_tp
) {
2289 if(strcmp(vc_tp
->tag
, "DESC") == 0)
2290 continue; /* special handling later */
2291 if(vc_tp
->ptag
== NULL
) {
2292 cdata
= xmlnode_get_tag_data(querynode
, vc_tp
->tag
);
2294 gchar
*tag
= g_strdup_printf("%s/%s", vc_tp
->ptag
, vc_tp
->tag
);
2295 cdata
= xmlnode_get_tag_data(querynode
, tag
);
2299 if(vc_tp
->url
== NULL
) {
2300 *ap
++ = g_strdup_printf("<B>%s:</B> %s<BR>\n", vc_tp
->label
, cdata
);
2302 gchar
*fmt
= g_strdup_printf("<B>%%s:</B> %s<BR>\n", vc_tp
->url
);
2303 *ap
++ = g_strdup_printf(fmt
, vc_tp
->label
, cdata
, cdata
);
2309 if((status
= g_hash_table_lookup(jd
->hash
, buddy
)) == NULL
) {
2310 status
= _("Online");
2312 *ap
++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status
);
2315 * "Description" handled as a special case: get a copy of the
2316 * string and HTML-ize.
2318 if((cdata
= xmlnode_get_tag_data(querynode
, "DESC")) != NULL
) {
2319 gchar
*tmp
= g_strdup_printf("<HR>%s<BR>", cdata
);
2320 *ap
++ = strdup_withhtml(tmp
);
2326 final
= g_strjoinv(NULL
, str_arr
);
2327 g_strfreev(str_arr
);
2329 g_show_info_text(gc
, buddy
, 2, final
, NULL
);
2335 * Used by XML_Parse on parsing CDATA
2337 static void xmlstr2xmlnode_charData(void *userdata
, const char *s
, int slen
)
2339 xmlstr2xmlnode_parser xmlp
= (xmlstr2xmlnode_parser
) userdata
;
2342 xmlnode_insert_cdata(xmlp
->current
, s
, slen
);
2346 * Used by XML_Parse to start or append to an xmlnode
2348 static void xmlstr2xmlnode_startElement(void *userdata
, const char *name
, const char **attribs
)
2351 xmlstr2xmlnode_parser xmlp
= (xmlstr2xmlnode_parser
) userdata
;
2353 if (xmlp
->current
) {
2354 /* Append the node to the current one */
2355 x
= xmlnode_insert_tag(xmlp
->current
, name
);
2356 xmlnode_put_expat_attribs(x
, attribs
);
2360 x
= xmlnode_new_tag(name
);
2361 xmlnode_put_expat_attribs(x
, attribs
);
2367 * Used by XML_Parse to end an xmlnode
2369 static void xmlstr2xmlnode_endElement(void *userdata
, const char *name
)
2371 xmlstr2xmlnode_parser xmlp
= (xmlstr2xmlnode_parser
) userdata
;
2374 if (xmlp
->current
!= NULL
&& (x
= xmlnode_get_parent(xmlp
->current
)) != NULL
) {
2380 * Parse an XML-encoded string into an xmlnode tree
2382 * Caller is responsible for freeing the returned xmlnode
2384 static xmlnode
xmlstr2xmlnode(char *xmlstring
)
2386 xmlstr2xmlnode_parser my_parser
= g_new(xmlstr2xmlnode_parser_struct
, 1);
2389 my_parser
->parser
= XML_ParserCreate(NULL
);
2390 my_parser
->current
= NULL
;
2392 XML_SetUserData(my_parser
->parser
, (void *)my_parser
);
2393 XML_SetElementHandler(my_parser
->parser
, xmlstr2xmlnode_startElement
, xmlstr2xmlnode_endElement
);
2394 XML_SetCharacterDataHandler(my_parser
->parser
, xmlstr2xmlnode_charData
);
2395 XML_Parse(my_parser
->parser
, xmlstring
, strlen(xmlstring
), 0);
2397 x
= my_parser
->current
;
2399 XML_ParserFree(my_parser
->parser
);
2406 * Insert a tag node into an xmlnode tree, recursively inserting parent tag
2407 * nodes as necessary
2409 * Returns pointer to inserted node
2411 * Note to hackers: this code is designed to be re-entrant (it's recursive--it
2412 * calls itself), so don't put any "static"s in here!
2414 static xmlnode
insert_tag_to_parent_tag(xmlnode start
, const char *parent_tag
, const char *new_tag
)
2419 * If the parent tag wasn't specified, see if we can get it
2420 * from the vCard template struct.
2422 if(parent_tag
== NULL
) {
2423 struct vcard_template
*vc_tp
= vcard_template_data
;
2425 while(vc_tp
->label
!= NULL
) {
2426 if(strcmp(vc_tp
->tag
, new_tag
) == 0) {
2427 parent_tag
= vc_tp
->ptag
;
2435 * If we have a parent tag...
2437 if(parent_tag
!= NULL
) {
2439 * Try to get the parent node for a tag
2441 if((x
= xmlnode_get_tag(start
, parent_tag
)) == NULL
) {
2445 char *grand_parent
= strcpy(g_malloc(strlen(parent_tag
) + 1), parent_tag
);
2448 if((parent
= strrchr(grand_parent
, '/')) != NULL
) {
2450 x
= insert_tag_to_parent_tag(start
, grand_parent
, parent
);
2452 x
= xmlnode_insert_tag(start
, grand_parent
);
2454 g_free(grand_parent
);
2457 * We found *something* to be the parent node.
2458 * Note: may be the "root" node!
2461 if((y
= xmlnode_get_tag(x
, new_tag
)) != NULL
) {
2468 * insert the new tag into its parent node
2470 return(xmlnode_insert_tag((x
== NULL
? start
: x
), new_tag
));
2474 * Find the tag name for a label
2476 * Returns NULL on not found
2478 static char *tag_for_label(const char *label
)
2480 struct vcard_template
*vc_tp
= vcard_template_data
;
2483 for(vc_tp
= vcard_template_data
; vc_tp
->label
!= NULL
; ++vc_tp
) {
2484 if(strcmp(label
, vc_tp
->label
) == 0) {
2494 * Send vCard info to Jabber server
2496 static void jabber_set_info(struct gaim_connection
*gc
, char *info
)
2500 struct jabber_data
*jd
= gc
->proto_data
;
2501 gjconn gjc
= jd
->gjc
;
2503 x
= xmlnode_new_tag("iq");
2504 xmlnode_put_attrib(x
,"type","set");
2506 id
= gjab_getid(gjc
);
2508 xmlnode_put_attrib(x
, "id", id
);
2511 * Send only if there's actually any *information* to send
2513 if((vc_node
= xmlstr2xmlnode(info
)) != NULL
&& xmlnode_get_name(vc_node
) != NULL
&&
2514 g_strncasecmp(xmlnode_get_name(vc_node
), "vcard", 5) == 0) {
2515 xmlnode_insert_tag_node(x
, vc_node
);
2516 debug_printf("jabber: vCard packet: %s\n", xmlnode2str(x
));
2524 * This is the callback from the "ok clicked" for "set vCard"
2526 * Formats GSList data into XML-encoded string and returns a pointer
2529 * g_free()'ing the returned string space is the responsibility of
2532 static gchar
*jabber_format_info(MultiEntryDlg
*b
)
2536 MultiEntryData
*med
;
2540 struct tag_attr
*tag_attr
;
2542 vc_node
= xmlnode_new_tag("vCard");
2544 for(tag_attr
= vcard_tag_attr_list
; tag_attr
->attr
!= NULL
; ++tag_attr
)
2545 xmlnode_put_attrib(vc_node
, tag_attr
->attr
, tag_attr
->value
);
2547 for(list
= b
->multi_entry_items
; list
!= NULL
; list
= list
->next
) {
2548 med
= (MultiEntryData
*) list
->data
;
2549 if(med
->label
!= NULL
&& med
->text
!= NULL
&& (med
->text
)[0] != '\0') {
2550 if((p
= tag_for_label(med
->label
)) != NULL
) {
2552 if((xp
= insert_tag_to_parent_tag(vc_node
, NULL
, p
)) != NULL
) {
2553 xmlnode_insert_cdata(xp
, med
->text
, -1);
2559 for(list
= b
->multi_text_items
; list
!= NULL
; list
= list
->next
) {
2560 mtd
= (MultiTextData
*) list
->data
;
2561 if(mtd
->label
!= NULL
&& mtd
->text
!= NULL
&& (mtd
->text
)[0] != '\0') {
2562 if((p
= tag_for_label(mtd
->label
)) != NULL
) {
2564 if((xp
= insert_tag_to_parent_tag(vc_node
, NULL
, p
)) != NULL
) {
2565 xmlnode_insert_cdata(xp
, mtd
->text
, -1);
2572 p
= g_strdup(xmlnode2str(vc_node
));
2573 xmlnode_free(vc_node
);
2579 * This gets executed by the proto action
2581 * Creates a new MultiEntryDlg struct, gets the XML-formatted user_info
2582 * string (if any) into GSLists for the (multi-entry) edit dialog and
2583 * calls the set_vcard dialog.
2585 static void jabber_setup_set_info(struct gaim_connection
*gc
)
2587 MultiEntryData
*data
;
2588 const struct vcard_template
*vc_tp
;
2590 MultiEntryDlg
*b
= multi_entry_dialog_new();
2592 xmlnode x_vc_data
= NULL
;
2593 struct aim_user
*tmp
= gc
->user
;
2598 * Get existing, XML-formatted, user info
2600 if((user_info
= g_malloc(strlen(tmp
->user_info
) + 1)) != NULL
) {
2601 strcpy(user_info
, tmp
->user_info
);
2602 x_vc_data
= xmlstr2xmlnode(user_info
);
2606 * Set up GSLists for edit with labels from "template," data from user info
2608 for(vc_tp
= vcard_template_data
; vc_tp
->label
!= NULL
; ++vc_tp
) {
2609 if((vc_tp
->label
)[0] == '\0')
2611 if(vc_tp
->ptag
== NULL
) {
2612 cdata
= xmlnode_get_tag_data(x_vc_data
, vc_tp
->tag
);
2614 gchar
*tag
= g_strdup_printf("%s/%s", vc_tp
->ptag
, vc_tp
->tag
);
2615 cdata
= xmlnode_get_tag_data(x_vc_data
, tag
);
2618 if(strcmp(vc_tp
->tag
, "DESC") == 0) {
2619 multi_text_list_update(&(b
->multi_text_items
),
2620 vc_tp
->label
, cdata
, TRUE
);
2622 data
= multi_entry_list_update(&(b
->multi_entry_items
),
2623 vc_tp
->label
, cdata
, TRUE
);
2624 data
->visible
= vc_tp
->visible
;
2625 data
->editable
= vc_tp
->editable
;
2630 if(x_vc_data
!= NULL
) {
2631 xmlnode_free(x_vc_data
);
2634 * Early Beta versions had a different user_info storage format--let's
2635 * see if that works.
2637 * This goes away RSN.
2639 const char *record_separator
= "<BR>";
2640 const char *field_separator
= ": ";
2641 gchar
**str_list
, **str_list_ptr
, **str_list2
;
2643 if((str_list
= g_strsplit(user_info
, record_separator
, 0)) != NULL
) {
2644 for(str_list_ptr
= str_list
; *str_list_ptr
!= NULL
; ++str_list_ptr
) {
2645 str_list2
= g_strsplit(*str_list_ptr
, field_separator
, 2);
2646 if(str_list2
[0] != NULL
&& str_list2
[1] != NULL
) {
2647 g_strstrip(str_list2
[0]);
2648 g_strstrip(str_list2
[1]);
2649 /* this is ugly--so far */
2650 if(strcmp(str_list2
[0], "Description") == 0) {
2651 multi_text_list_update(&(b
->multi_text_items
),
2652 str_list2
[0], str_list2
[1], FALSE
);
2654 multi_entry_list_update(&(b
->multi_entry_items
),
2655 str_list2
[0], str_list2
[1], FALSE
);
2658 g_strfreev(str_list2
);
2660 g_strfreev(str_list
);
2664 if(user_info
!= NULL
) {
2668 b
->title
= _("Gaim - Edit Jabber vCard");
2669 b
->wmclass_name
= "set_info";
2670 b
->wmclass_class
= "Gaim";
2671 b
->instructions
->text
= g_strdup(multi_entry_instructions
);
2672 b
->entries_title
= g_strdup(entries_title
);
2674 b
->custom
= (void *) jabber_format_info
;
2679 /*---------------------------------------*/
2680 /* End Jabber "set info" (vCard) support */
2681 /*---------------------------------------*/
2683 /*----------------------------------------*/
2684 /* Jabber "user registration" support */
2685 /*----------------------------------------*/
2688 * Three of the following four functions duplicate much of what
2691 * jabber_handleregresp()
2693 * jabber_handle_registration_state()
2695 * It may be that an additional flag could be added to one of
2696 * the "local" structs and the duplicated code modified to
2697 * account for it--thus eliminating the duplication. Then again:
2698 * doing it the way it is may be much cleaner.
2700 * TBD: Code to support requesting additional information server
2701 * wants at registration--incl. dialog.
2705 * Like jabber_handlepacket(), only different
2707 static void jabber_handleregresp(gjconn gjc
, jpacket p
)
2709 if (jpacket_subtype(p
) == JPACKET__RESULT
) {
2712 if((querynode
= xmlnode_get_tag(p
->x
, "query")) != NULL
) {
2715 /* we damn well *better* have this! */
2716 if((xmlns
= xmlnode_get_attrib(querynode
, "xmlns")) != NULL
&&
2717 strcmp(xmlns
, NS_REGISTER
) == 0) {
2720 xmlnode child
= xmlnode_get_firstchild(querynode
);
2722 debug_printf("got registration requirments response!\n");
2724 while(child
!= NULL
) {
2725 if((tag
= xmlnode_get_name(child
)) != NULL
) {
2728 fprintf(stderr
, "DBG: got node: \"%s\"\n", tag
);
2731 if((data
= xmlnode_get_data(child
)) != NULL
) {
2732 fprintf(stderr
, "DBG: got data: \"%s\"\n", data
);
2736 child
= xmlnode_get_nextsibling(child
);
2740 debug_printf("registration successful!\n");
2742 hide_login_progress_notice(GJ_GC(gjc
), _("Server Registration successful!"));
2744 * TBD: is this the correct way to do away with a
2745 * gaim_connection and all its associated memory
2748 signoff(GJ_GC(gjc
));
2753 char *errmsg
= NULL
;
2755 struct jabber_data
*jd
= GJ_GC(gjc
)->proto_data
;
2757 debug_printf("registration failed\n");
2758 xerr
= xmlnode_get_tag(p
->x
, "error");
2761 errmsg
= xmlnode_get_data(xerr
);
2762 if (xmlnode_get_attrib(xerr
, "code")) {
2763 errcode
= atoi(xmlnode_get_attrib(xerr
, "code"));
2764 g_snprintf(msg
, sizeof(msg
), "Error %d: %s", errcode
, errmsg
);
2766 g_snprintf(msg
, sizeof(msg
), "%s", errmsg
);
2767 hide_login_progress(GJ_GC(gjc
), msg
);
2769 hide_login_progress(GJ_GC(gjc
), _("Unknown registration error"));
2777 * Like gjab_reqauth(), only different
2779 static void gjab_reqreg(gjconn gjc
)
2787 x
= jutil_iqnew(JPACKET__SET
, NS_REGISTER
);
2788 y
= xmlnode_get_tag(x
, "query");
2790 user
= gjc
->user
->user
;
2793 z
= xmlnode_insert_tag(y
, "username");
2794 xmlnode_insert_cdata(z
, user
, -1);
2796 z
= xmlnode_insert_tag(y
, "password");
2797 xmlnode_insert_cdata(z
, gjc
->pass
, -1);
2799 debug_printf("jabber: registration packet: %s\n", xmlnode2str(x
));
2805 * Like jabber_handlestate(), only different
2807 static void jabber_handle_registration_state(gjconn gjc
, int state
)
2810 case JCONN_STATE_OFF
:
2811 hide_login_progress(GJ_GC(gjc
), _("Unable to connect"));
2812 signoff(GJ_GC(gjc
));
2814 case JCONN_STATE_CONNECTED
:
2817 set_login_progress(GJ_GC(gjc), 2, _("Connected"));
2820 case JCONN_STATE_ON
:
2823 set_login_progress(GJ_GC(gjc), 3, _("Requesting Authentication Method"));
2827 * TBD: A work-in-progress
2828 gjab_reqregreqs(gjc);
2832 debug_printf("state change: %d\n", state
);
2838 * Like jabber_login(), only different
2840 void jabber_register_user(struct aim_user
*au
)
2842 struct gaim_connection
*gc
= new_gaim_conn(au
);
2843 struct jabber_data
*jd
= gc
->proto_data
= g_new0(struct jabber_data
, 1);
2844 char *loginname
= create_valid_jid(au
->username
, DEFAULT_SERVER
, "GAIM");
2847 * These do nothing during registration
2852 if ((jd
->gjc
= gjab_new(loginname
, au
->password
, gc
)) == NULL
) {
2854 debug_printf("jabber: unable to connect (jab_new failed)\n");
2855 hide_login_progress(gc
, _("Unable to connect"));
2858 gjab_state_handler(jd
->gjc
, jabber_handle_registration_state
);
2859 gjab_packet_handler(jd
->gjc
, jabber_handleregresp
);
2860 jd
->gjc
->queries
= NULL
;
2861 gjab_start(jd
->gjc
);
2867 /*----------------------------------------*/
2868 /* End Jabber "user registration" support */
2869 /*----------------------------------------*/
2871 static void jabber_do_action(struct gaim_connection
*gc
, char *act
)
2873 if (!strcmp(act
, _("Set User Info"))) {
2874 jabber_setup_set_info(gc
);
2876 } else if (!strcmp(act, _("Set Dir Info"))) {
2878 } else if (!strcmp(act, _("Change Password"))) {
2879 show_change_passwd(gc);
2884 static GList
*jabber_actions()
2888 m
= g_list_append(m
, _("Set User Info"));
2890 m = g_list_append(m, _("Set Dir Info"));
2891 m = g_list_append(m, _("Change Password"));
2897 static struct prpl
*my_protocol
= NULL
;
2899 void jabber_init(struct prpl
*ret
)
2901 /* the NULL's aren't required but they're nice to have */
2902 ret
->protocol
= PROTO_JABBER
;
2903 ret
->options
= OPT_PROTO_UNIQUE_CHATNAME
| OPT_PROTO_CHAT_TOPIC
;
2904 ret
->name
= jabber_name
;
2905 ret
->list_icon
= jabber_list_icon
;
2906 ret
->away_states
= jabber_away_states
;
2907 ret
->actions
= jabber_actions
;
2908 ret
->do_action
= jabber_do_action
;
2909 ret
->buddy_menu
= jabber_buddy_menu
;
2910 ret
->user_opts
= jabber_user_opts
;
2911 ret
->login
= jabber_login
;
2912 ret
->close
= jabber_close
;
2913 ret
->send_im
= jabber_send_im
;
2914 ret
->set_info
= jabber_set_info
;
2915 ret
->get_info
= jabber_get_info
;
2916 ret
->get_cb_info
= jabber_get_cb_info
;
2917 ret
->get_cb_away
= jabber_get_cb_away_msg
;
2918 ret
->set_away
= jabber_set_away
;
2919 ret
->set_dir
= NULL
;
2920 ret
->get_dir
= NULL
;
2921 ret
->dir_search
= NULL
;
2922 ret
->set_idle
= jabber_set_idle
;
2923 ret
->change_passwd
= NULL
;
2924 ret
->add_buddy
= jabber_add_buddy
;
2925 ret
->add_buddies
= NULL
;
2926 ret
->remove_buddy
= jabber_remove_buddy
;
2927 ret
->add_permit
= NULL
;
2928 ret
->add_deny
= NULL
;
2929 ret
->rem_permit
= NULL
;
2930 ret
->rem_deny
= NULL
;
2931 ret
->set_permit_deny
= NULL
;
2933 ret
->chat_info
= jabber_chat_info
;
2934 ret
->join_chat
= jabber_join_chat
;
2935 ret
->chat_invite
= jabber_chat_invite
;
2936 ret
->chat_leave
= jabber_chat_leave
;
2937 ret
->chat_whisper
= jabber_chat_whisper
;
2938 ret
->chat_send
= jabber_chat_send
;
2939 ret
->keepalive
= jabber_keepalive
;
2940 ret
->normalize
= jabber_normalize
;
2941 ret
->buddy_free
= jabber_buddy_free
;
2942 ret
->register_user
= jabber_register_user
;
2949 char *gaim_plugin_init(GModule
*handle
)
2951 load_protocol(jabber_init
, sizeof(struct prpl
));
2955 void gaim_plugin_remove()
2957 struct prpl
*p
= find_prpl(PROTO_JABBER
);
2958 if (p
== my_protocol
)
2969 return PRPL_DESC("Jabber");