6 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2009 pier11 <pier11@kinozal.tv>
8 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
10 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
13 * Thanks to Google's Summer of Code Program and the helpful mentors
16 * Session-based SIP MESSAGE documentation:
17 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37 #include <sys/types.h>
38 #include <netinet/in.h>
44 #define _LIBC_INTERNAL_
56 #include "accountopt.h"
58 #include "conversation.h"
72 #include "sipe-chat.h"
73 #include "sipe-conf.h"
74 #include "sipe-dialog.h"
76 #include "sipe-session.h"
77 #include "sipe-utils.h"
79 #include "sipe-sign.h"
83 /* Keep in sync with sipe_transport_type! */
84 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
85 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
87 /* Status identifiers (see also: sipe_status_types()) */
88 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
89 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
90 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
91 /* PURPLE_STATUS_UNAVAILABLE: */
92 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
93 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
94 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On The Phone */
95 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
96 /* PURPLE_STATUS_AWAY: */
97 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
98 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
99 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
100 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
101 /* ??? PURPLE_STATUS_MOBILE */
102 /* ??? PURPLE_STATUS_TUNE */
104 /* Action name templates */
105 #define ACTION_NAME_PRESENCE "<presence><%s>"
108 static gchar
*get_epid(struct sipe_account_data
*sip
)
111 gchar
*self_sip_uri
= sip_uri_self(sip
);
112 sip
->epid
= sipe_get_epid(self_sip_uri
,
113 sipe_get_host_name(),
114 purple_network_get_my_ip(-1));
115 g_free(self_sip_uri
);
117 return g_strdup(sip
->epid
);
120 static char *genbranch()
122 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
123 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
124 rand() & 0xFFFF, rand() & 0xFFFF);
127 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
128 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
133 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
135 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
137 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
138 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
141 static void sipe_close(PurpleConnection
*gc
);
143 static void send_presence_status(struct sipe_account_data
*sip
);
145 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
147 static void sipe_keep_alive(PurpleConnection
*gc
)
149 struct sipe_account_data
*sip
= gc
->proto_data
;
150 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
151 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
152 gchar buf
[2] = {0, 0};
153 purple_debug_info("sipe", "sending keep alive\n");
154 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
156 time_t now
= time(NULL
);
157 if ((sip
->keepalive_timeout
> 0) &&
158 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
159 #if PURPLE_VERSION_CHECK(2,4,0)
160 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
163 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
164 sendout_pkt(gc
, "\r\n\r\n");
165 sip
->last_keepalive
= now
;
170 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
172 struct sip_connection
*ret
= NULL
;
173 GSList
*entry
= sip
->openconns
;
176 if (ret
->fd
== fd
) return ret
;
182 static void sipe_auth_free(struct sip_auth
*auth
)
184 g_free(auth
->opaque
);
188 g_free(auth
->target
);
190 auth
->type
= AUTH_TYPE_UNSET
;
193 g_free(auth
->gssapi_data
);
194 auth
->gssapi_data
= NULL
;
195 sip_sec_destroy_context(auth
->gssapi_context
);
196 auth
->gssapi_context
= NULL
;
199 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
201 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
203 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
207 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
209 struct sip_connection
*conn
= connection_find(sip
, fd
);
211 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
212 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
218 static void connection_free_all(struct sipe_account_data
*sip
)
220 struct sip_connection
*ret
= NULL
;
221 GSList
*entry
= sip
->openconns
;
224 connection_remove(sip
, ret
->fd
);
225 entry
= sip
->openconns
;
229 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
232 const char *authuser
= sip
->authuser
;
236 if (!authuser
|| strlen(authuser
) < 1) {
237 authuser
= sip
->username
;
240 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
241 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
243 // If we have a signature for the message, include that
244 if (msg
->signature
) {
245 return g_strdup_printf("%s qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", crand=\"%s\", cnum=\"%s\", response=\"%s\"", auth_protocol
, auth
->opaque
, auth
->realm
, auth
->target
, msg
->rand
, msg
->num
, msg
->signature
);
248 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
249 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
253 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
256 purple_account_get_bool(sip
->account
, "sso", TRUE
),
257 sip
->authdomain
? sip
->authdomain
: "",
262 if (!gssapi_data
|| !auth
->gssapi_context
) {
263 sip
->gc
->wants_to_die
= TRUE
;
264 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
268 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
269 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
275 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
277 } else { /* Digest */
279 /* Calculate new session key */
281 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
282 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
283 authuser
, auth
->realm
, sip
->password
,
284 auth
->gssapi_data
, NULL
);
287 sprintf(noncecount
, "%08d", auth
->nc
++);
288 response
= purple_cipher_http_digest_calculate_response("md5",
289 msg
->method
, msg
->target
, NULL
, NULL
,
290 auth
->gssapi_data
, noncecount
, NULL
,
292 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
294 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->gssapi_data
, msg
->target
, noncecount
, response
);
300 static char *parse_attribute(const char *attrname
, const char *source
)
302 const char *tmp
, *tmp2
;
304 int len
= strlen(attrname
);
306 if (!strncmp(source
, attrname
, len
)) {
308 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
310 retval
= g_strndup(tmp
, tmp2
- tmp
);
312 retval
= g_strdup(tmp
);
318 static void fill_auth(gchar
*hdr
, struct sip_auth
*auth
)
324 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
328 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
329 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
330 auth
->type
= AUTH_TYPE_NTLM
;
333 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
334 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
335 auth
->type
= AUTH_TYPE_KERBEROS
;
339 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
340 auth
->type
= AUTH_TYPE_DIGEST
;
344 parts
= g_strsplit(hdr
, "\", ", 0);
345 for (i
= 0; parts
[i
]; i
++) {
348 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
350 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
351 g_free(auth
->gssapi_data
);
352 auth
->gssapi_data
= tmp
;
354 if (auth
->type
== AUTH_TYPE_NTLM
) {
355 /* NTLM module extracts nonce from gssapi-data */
359 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
360 /* Only used with AUTH_TYPE_DIGEST */
361 g_free(auth
->gssapi_data
);
362 auth
->gssapi_data
= tmp
;
363 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
364 g_free(auth
->opaque
);
366 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
370 if (auth
->type
== AUTH_TYPE_DIGEST
) {
371 /* Throw away old session key */
372 g_free(auth
->opaque
);
377 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
378 g_free(auth
->target
);
387 static void sipe_canwrite_cb(gpointer data
,
388 SIPE_UNUSED_PARAMETER gint source
,
389 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
391 PurpleConnection
*gc
= data
;
392 struct sipe_account_data
*sip
= gc
->proto_data
;
396 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
398 if (max_write
== 0) {
399 if (sip
->tx_handler
!= 0){
400 purple_input_remove(sip
->tx_handler
);
406 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
408 if (written
< 0 && errno
== EAGAIN
)
410 else if (written
<= 0) {
411 /*TODO: do we really want to disconnect on a failure to write?*/
412 purple_connection_error(gc
, _("Could not write"));
416 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
419 static void sipe_canwrite_cb_ssl(gpointer data
,
420 SIPE_UNUSED_PARAMETER gint src
,
421 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
423 PurpleConnection
*gc
= data
;
424 struct sipe_account_data
*sip
= gc
->proto_data
;
428 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
430 if (max_write
== 0) {
431 if (sip
->tx_handler
!= 0) {
432 purple_input_remove(sip
->tx_handler
);
438 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
440 if (written
< 0 && errno
== EAGAIN
)
442 else if (written
<= 0) {
443 /*TODO: do we really want to disconnect on a failure to write?*/
444 purple_connection_error(gc
, _("Could not write"));
448 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
451 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
453 static void send_later_cb(gpointer data
, gint source
,
454 SIPE_UNUSED_PARAMETER
const gchar
*error
)
456 PurpleConnection
*gc
= data
;
457 struct sipe_account_data
*sip
;
458 struct sip_connection
*conn
;
460 if (!PURPLE_CONNECTION_IS_VALID(gc
))
468 purple_connection_error(gc
, _("Could not connect"));
472 sip
= gc
->proto_data
;
474 sip
->connecting
= FALSE
;
475 sip
->last_keepalive
= time(NULL
);
477 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
479 /* If there is more to write now, we need to register a handler */
480 if (sip
->txbuf
->bufused
> 0)
481 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
483 conn
= connection_create(sip
, source
);
484 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
487 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
489 struct sipe_account_data
*sip
;
490 struct sip_connection
*conn
;
492 if (!PURPLE_CONNECTION_IS_VALID(gc
))
494 if (gsc
) purple_ssl_close(gsc
);
498 sip
= gc
->proto_data
;
501 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
502 sip
->connecting
= FALSE
;
503 sip
->last_keepalive
= time(NULL
);
505 conn
= connection_create(sip
, gsc
->fd
);
507 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
512 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
513 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
515 PurpleConnection
*gc
= data
;
516 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
517 if (sip
== NULL
) return;
519 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
521 /* If there is more to write now */
522 if (sip
->txbuf
->bufused
> 0) {
523 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
528 static void sendlater(PurpleConnection
*gc
, const char *buf
)
530 struct sipe_account_data
*sip
= gc
->proto_data
;
532 if (!sip
->connecting
) {
533 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
534 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
535 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
537 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
538 purple_connection_error(gc
, _("Couldn't create socket"));
541 sip
->connecting
= TRUE
;
544 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
545 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
547 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
550 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
552 struct sipe_account_data
*sip
= gc
->proto_data
;
553 time_t currtime
= time(NULL
);
554 int writelen
= strlen(buf
);
556 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
557 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
558 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
559 purple_debug_info("sipe", "could not send packet\n");
568 if (sip
->tx_handler
) {
573 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
575 ret
= write(sip
->fd
, buf
, writelen
);
579 if (ret
< 0 && errno
== EAGAIN
)
581 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
586 if (ret
< writelen
) {
587 if (!sip
->tx_handler
){
589 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
592 sip
->tx_handler
= purple_input_add(sip
->fd
,
593 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
598 /* XXX: is it OK to do this? You might get part of a request sent
599 with part of another. */
600 if (sip
->txbuf
->bufused
> 0)
601 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
603 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
609 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
611 sendout_pkt(gc
, buf
);
615 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
617 GSList
*tmp
= msg
->headers
;
620 GString
*outstr
= g_string_new("");
621 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
623 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
624 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
625 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
626 tmp
= g_slist_next(tmp
);
628 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
629 sendout_pkt(sip
->gc
, outstr
->str
);
630 g_string_free(outstr
, TRUE
);
633 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
637 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
641 if (sip
->registrar
.gssapi_context
) {
642 struct sipmsg_breakdown msgbd
;
643 gchar
*signature_input_str
;
645 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
646 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
647 sip
->registrar
.ntlm_num
++;
648 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
649 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
650 if (signature_input_str
!= NULL
) {
651 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
652 msg
->signature
= signature_hex
;
653 msg
->rand
= g_strdup(msgbd
.rand
);
654 msg
->num
= g_strdup(msgbd
.num
);
655 g_free(signature_input_str
);
657 sipmsg_breakdown_free(&msgbd
);
660 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
661 buf
= auth_header(sip
, &sip
->registrar
, msg
);
663 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
666 } else if (!strcmp(method
,"SUBSCRIBE") || !strcmp(method
,"SERVICE") || !strcmp(method
,"MESSAGE") || !strcmp(method
,"INVITE") || !strcmp(method
, "ACK") || !strcmp(method
, "NOTIFY") || !strcmp(method
, "BYE") || !strcmp(method
, "INFO") || !strcmp(method
, "OPTIONS") || !strcmp(method
, "REFER")) {
667 sip
->registrar
.nc
= 3;
669 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
671 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
674 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
679 buf
= auth_header(sip
, &sip
->registrar
, msg
);
680 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
683 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
689 static char *get_contact_service(struct sipe_account_data *sip)
691 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip->listenport, TRANSPORT_DESCRIPTOR, generateUUIDfromEPID(get_epid()));
692 //return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->listenport, purple_network_get_my_ip(-1), TRANSPORT_DESCRIPTOR);
696 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
697 const char *text
, const char *body
)
701 GString
*outstr
= g_string_new("");
702 struct sipe_account_data
*sip
= gc
->proto_data
;
705 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
707 contact
= get_contact(sip
);
708 sipmsg_add_header(msg
, "Contact", contact
);
713 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
714 sipmsg_add_header(msg
, "Content-Length", len
);
716 sipmsg_add_header(msg
, "Content-Length", "0");
719 msg
->response
= code
;
721 sipmsg_strip_headers(msg
, keepers
);
722 sipmsg_merge_new_headers(msg
);
723 sign_outgoing_message(msg
, sip
, msg
->method
);
725 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
728 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
729 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
731 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
732 tmp
= g_slist_next(tmp
);
734 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
735 sendout_pkt(gc
, outstr
->str
);
736 g_string_free(outstr
, TRUE
);
739 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
741 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
742 if (trans
->msg
) sipmsg_free(trans
->msg
);
747 static struct transaction
*
748 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
750 gchar
*call_id
= NULL
;
752 struct transaction
*trans
= g_new0(struct transaction
, 1);
754 trans
->time
= time(NULL
);
755 trans
->msg
= (struct sipmsg
*)msg
;
756 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
757 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
758 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
759 trans
->callback
= callback
;
760 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
764 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
766 struct transaction
*trans
;
767 GSList
*transactions
= sip
->transactions
;
768 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
769 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
770 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
772 while (transactions
) {
773 trans
= transactions
->data
;
774 if (!g_strcasecmp(trans
->key
, key
)) {
778 transactions
= transactions
->next
;
786 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
787 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
788 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
790 struct sipe_account_data
*sip
= gc
->proto_data
;
791 const char *addh
= "";
794 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
795 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
796 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
797 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
798 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
799 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
800 gchar
*route
= strdup("");
801 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
802 int cseq
= dialog
? ++dialog
->cseq
:
803 /* This breaks OCS2007: own presence, contact search, ?
804 1 .* as Call-Id is new in this case */
806 struct transaction
*trans
;
808 if (dialog
&& dialog
->routes
)
810 GSList
*iter
= dialog
->routes
;
815 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
817 iter
= g_slist_next(iter
);
821 if (!ourtag
&& !dialog
) {
825 if (!strcmp(method
, "REGISTER")) {
826 if (sip
->regcallid
) {
828 callid
= g_strdup(sip
->regcallid
);
830 sip
->regcallid
= g_strdup(callid
);
834 if (addheaders
) addh
= addheaders
;
836 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
837 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
838 "From: <sip:%s>%s%s;epid=%s\r\n"
839 "To: <%s>%s%s%s%s\r\n"
840 "Max-Forwards: 70\r\n"
845 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
847 dialog
&& dialog
->request
? dialog
->request
: url
,
848 TRANSPORT_DESCRIPTOR
,
849 purple_network_get_my_ip(-1),
851 branch
? ";branch=" : "",
852 branch
? branch
: "",
854 ourtag
? ";tag=" : "",
855 ourtag
? ourtag
: "",
858 theirtag
? ";tag=" : "",
859 theirtag
? theirtag
: "",
860 theirepid
? ";epid=" : "",
861 theirepid
? theirepid
: "",
868 body
? (gsize
) strlen(body
) : 0,
872 //printf ("parsing msg buf:\n%s\n\n", buf);
873 msg
= sipmsg_parse_msg(buf
);
884 sign_outgoing_message (msg
, sip
, method
);
886 buf
= sipmsg_to_string (msg
);
888 /* add to ongoing transactions */
889 trans
= transactions_add_buf(sip
, msg
, tc
);
890 sendout_pkt(gc
, buf
);
896 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
898 gchar
*from
= sip_uri_self(sip
);
899 gchar
*contact
= get_contact(sip
);
900 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
901 "Content-Type: application/SOAP+xml\r\n",contact
);
903 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
904 tr
->payload
= payload
;
911 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
913 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
916 static char *get_contact_register(struct sipe_account_data
*sip
)
918 char *epid
= get_epid(sip
);
919 char *uuid
= generateUUIDfromEPID(epid
);
920 char *buf
= g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, TRANSPORT_DESCRIPTOR
, uuid
);
926 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
934 if (!sip
->sipdomain
) return;
936 uri
= sip_uri_from_name(sip
->sipdomain
);
937 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
938 to
= sip_uri_self(sip
);
939 contact
= get_contact_register(sip
);
940 hdr
= g_strdup_printf("Contact: %s\r\n"
941 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
942 "Event: registration\r\n"
943 "Allow-Events: presence\r\n"
944 "ms-keep-alive: UAC;hop-hop=yes\r\n"
945 "%s", contact
, expires
);
949 sip
->registerstatus
= 1;
951 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
952 process_register_response
);
959 static void do_register_cb(struct sipe_account_data
*sip
,
960 SIPE_UNUSED_PARAMETER
void *unused
)
962 do_register_exp(sip
, -1);
963 sip
->reregister_set
= FALSE
;
966 static void do_register(struct sipe_account_data
*sip
)
968 do_register_exp(sip
, -1);
972 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
974 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
975 send_soap_request(sip
, body
);
980 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
983 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
985 purple_debug_info("sipe", "Blocking contact %s\n", who
);
988 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
992 void sipe_auth_user_cb(void * data
)
994 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
997 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1002 void sipe_deny_user_cb(void * data
)
1004 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1007 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1012 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1014 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1015 sipe_contact_allow_deny(sip
, name
, TRUE
);
1019 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1021 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1022 sipe_contact_allow_deny(sip
, name
, FALSE
);
1026 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1028 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1029 sipe_contact_set_acl(sip, name, "");
1033 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1037 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1038 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1040 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1042 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1043 if (!watchers
) return;
1045 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1046 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1047 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1048 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1050 // TODO pull out optional displayName to pass as alias
1052 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1053 job
->who
= remote_user
;
1055 purple_account_request_authorization(
1069 xmlnode_free(watchers
);
1074 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1076 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1077 if (!purple_group
) {
1078 purple_group
= purple_group_new(group
->name
);
1079 purple_blist_add_group(purple_group
, NULL
);
1083 group
->purple_group
= purple_group
;
1084 sip
->groups
= g_slist_append(sip
->groups
, group
);
1085 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1087 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1091 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1093 struct sipe_group
*group
;
1099 entry
= sip
->groups
;
1101 group
= entry
->data
;
1102 if (group
->id
== id
) {
1105 entry
= entry
->next
;
1110 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1112 struct sipe_group
*group
;
1118 entry
= sip
->groups
;
1120 group
= entry
->data
;
1121 if (!strcmp(group
->name
, name
)) {
1124 entry
= entry
->next
;
1130 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1133 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1134 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1135 send_soap_request(sip
, body
);
1137 g_free(group
->name
);
1138 group
->name
= g_strdup(name
);
1142 * Only appends if no such value already stored.
1145 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1146 GSList
* res
= list
;
1147 if (!g_slist_find_custom(list
, data
, func
)) {
1148 res
= g_slist_insert_sorted(list
, data
, func
);
1154 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1155 return group1
->id
- group2
->id
;
1159 * Returns string like "2 4 7 8" - group ids buddy belong to.
1162 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1165 //creating array from GList, converting int to gchar*
1166 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1167 GSList
*entry
= buddy
->groups
;
1169 struct sipe_group
* group
= entry
->data
;
1170 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1171 entry
= entry
->next
;
1175 res
= g_strjoinv(" ", ids_arr
);
1176 g_strfreev(ids_arr
);
1181 * Sends buddy update to server
1184 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1186 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1187 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1189 if (buddy
&& purple_buddy
) {
1190 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1192 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1193 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1195 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1196 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1198 send_soap_request(sip
, body
);
1204 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1206 if (msg
->response
== 200) {
1207 struct sipe_group
*group
;
1208 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1212 struct sipe_buddy
*buddy
;
1214 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1220 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1227 group_id
= xmlnode_get_data(node
);
1234 group
= g_new0(struct sipe_group
, 1);
1235 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1237 group
->name
= ctx
->group_name
;
1239 sipe_group_add(sip
, group
);
1241 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1243 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1246 sipe_group_set_user(sip
, ctx
->user_name
);
1255 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1257 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1259 ctx
->group_name
= g_strdup(name
);
1260 ctx
->user_name
= g_strdup(who
);
1262 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1263 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1268 * Data structure for scheduled actions
1270 typedef void (*Action
) (struct sipe_account_data
*, void *);
1272 struct scheduled_action
{
1275 * Format is <Event>[<Data>...]
1276 * Example: <presence><sip:user@domain.com> or <registration>
1279 guint timeout_handler
;
1280 gboolean repetitive
;
1282 GDestroyNotify destroy
;
1283 struct sipe_account_data
*sip
;
1289 * Should return FALSE if repetitive action is not needed
1291 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1294 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1295 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1296 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1297 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1298 ret
= sched_action
->repetitive
;
1299 if (sched_action
->destroy
) {
1300 (*sched_action
->destroy
)(sched_action
->payload
);
1302 g_free(sched_action
->name
);
1303 g_free(sched_action
);
1308 * Kills action timer effectively cancelling
1311 * @param name of action
1313 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1317 if (!sip
->timeouts
|| !name
) return;
1319 entry
= sip
->timeouts
;
1321 struct scheduled_action
*sched_action
= entry
->data
;
1322 if(!strcmp(sched_action
->name
, name
)) {
1323 GSList
*to_delete
= entry
;
1324 entry
= entry
->next
;
1325 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1326 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1327 purple_timeout_remove(sched_action
->timeout_handler
);
1328 if (sched_action
->destroy
) {
1329 (*sched_action
->destroy
)(sched_action
->payload
);
1331 g_free(sched_action
->name
);
1332 g_free(sched_action
);
1334 entry
= entry
->next
;
1340 sipe_schedule_action0(const gchar
*name
,
1344 GDestroyNotify destroy
,
1345 struct sipe_account_data
*sip
,
1348 struct scheduled_action
*sched_action
;
1350 /* Make sure each action only exists once */
1351 sipe_cancel_scheduled_action(sip
, name
);
1353 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1354 sched_action
= g_new0(struct scheduled_action
, 1);
1355 sched_action
->repetitive
= FALSE
;
1356 sched_action
->name
= g_strdup(name
);
1357 sched_action
->action
= action
;
1358 sched_action
->destroy
= destroy
;
1359 sched_action
->sip
= sip
;
1360 sched_action
->payload
= payload
;
1361 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1362 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1363 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1364 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1368 * Do schedule action for execution in the future.
1369 * Non repetitive execution.
1371 * @param name of action (will be copied)
1372 * @param timeout in seconds
1373 * @action callback function
1374 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1377 sipe_schedule_action(const gchar
*name
,
1380 GDestroyNotify destroy
,
1381 struct sipe_account_data
*sip
,
1384 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1388 * Same as sipe_schedule_action() but timeout is in milliseconds.
1391 sipe_schedule_action_msec(const gchar
*name
,
1394 GDestroyNotify destroy
,
1395 struct sipe_account_data
*sip
,
1398 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1402 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1404 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1405 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
1407 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1409 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1414 static void sipe_subscribe_resource_uri(const char *name
,
1415 SIPE_UNUSED_PARAMETER gpointer value
,
1416 gchar
**resources_uri
)
1418 gchar
*tmp
= *resources_uri
;
1419 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1423 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1425 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1426 if (sbuddy
&& !sbuddy
->resubscribed
) { // Only not resubscribed contacts; the first time everybody are included
1427 gchar
*tmp
= *resources_uri
;
1428 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"><context/></resource>\n", tmp
, name
);
1434 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1435 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1436 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1437 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1438 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1441 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1443 gchar
*contact
= get_contact(sip
);
1446 gchar
*require
= "";
1448 gchar
*autoextend
= "";
1449 gchar
*content_type
;
1451 if (sip
->msrtc_event_categories
) {
1452 require
= ", categoryList";
1453 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1454 content_type
= "application/msrtc-adrl-categorylist+xml";
1455 content
= g_strdup_printf(
1456 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1457 "<action name=\"subscribe\" id=\"63792024\">\n"
1458 "<adhocList>\n%s</adhocList>\n"
1459 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1460 "<category name=\"note\"/>\n"
1461 "<category name=\"state\"/>\n"
1464 "</batchSub>", sip
->username
, resources_uri
);
1466 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1467 content_type
= "application/adrl+xml";
1468 content
= g_strdup_printf(
1469 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1470 "<create xmlns=\"\">\n%s</create>\n"
1471 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1473 g_free(resources_uri
);
1475 request
= g_strdup_printf(
1476 "Require: adhoclist%s\r\n"
1477 "Supported: eventlist\r\n"
1478 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1479 "Supported: ms-piggyback-first-notify\r\n"
1480 "%sSupported: ms-benotify\r\n"
1481 "Proxy-Require: ms-benotify\r\n"
1482 "Event: presence\r\n"
1483 "Content-Type: %s\r\n"
1484 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1487 /* subscribe to buddy presence */
1488 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1489 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1496 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1497 SIPE_UNUSED_PARAMETER
void *unused
)
1499 gchar
*to
= sip_uri_self(sip
);
1500 gchar
*resources_uri
= g_strdup("");
1501 if (sip
->msrtc_event_categories
) {
1502 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1504 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1506 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1509 struct presence_batched_routed
{
1514 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1516 struct presence_batched_routed
*data
= payload
;
1517 GSList
*buddies
= data
->buddies
;
1519 g_free(buddies
->data
);
1520 buddies
= buddies
->next
;
1522 g_slist_free(data
->buddies
);
1527 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1529 struct presence_batched_routed
*data
= payload
;
1530 GSList
*buddies
= data
->buddies
;
1531 gchar
*resources_uri
= g_strdup("");
1533 gchar
*tmp
= resources_uri
;
1534 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1536 buddies
= buddies
->next
;
1538 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1539 g_strdup(data
->host
));
1543 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1544 * The user sends a single SUBSCRIBE request to the subscribed contact.
1545 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1549 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1551 gchar
*to
= sip_uri((char *)buddy_name
);
1552 gchar
*tmp
= get_contact(sip
);
1555 gchar
*autoextend
= "";
1557 if (!sip
->msrtc_event_categories
)
1558 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1560 request
= g_strdup_printf(
1561 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1562 "Supported: ms-piggyback-first-notify\r\n"
1563 "%sSupported: ms-benotify\r\n"
1564 "Proxy-Require: ms-benotify\r\n"
1565 "Event: presence\r\n"
1566 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1567 "Contact: %s\r\n", autoextend
,tmp
);
1569 content
= g_strdup_printf(
1570 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1571 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1572 "<resource uri=\"%s\"/>\n"
1574 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1575 "<category name=\"note\"/>\n"
1576 "<category name=\"state\"/>\n"
1579 "</batchSub>", sip
->username
, to
1584 /* subscribe to buddy presence */
1585 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1592 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1594 if (!purple_status_is_active(status
))
1598 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1601 g_free(sip
->status
);
1602 sip
->status
= g_strdup(purple_status_get_id(status
));
1603 send_presence_status(sip
);
1609 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
1610 SIPE_UNUSED_PARAMETER
const char *alias
)
1612 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1613 sipe_group_set_user(sip
, name
);
1617 sipe_group_buddy(PurpleConnection
*gc
,
1619 const char *old_group_name
,
1620 const char *new_group_name
)
1622 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1623 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1624 struct sipe_group
* old_group
= NULL
;
1625 struct sipe_group
* new_group
;
1627 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1628 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1630 if(!buddy
) { // buddy not in roaming list
1634 if (old_group_name
) {
1635 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1637 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1640 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1641 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1645 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1647 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1648 sipe_group_set_user(sip
, who
);
1652 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1654 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1655 struct sipe_buddy
*b
;
1657 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1659 // Prepend sip: if needed
1660 if (strncmp("sip:", buddy
->name
, 4)) {
1661 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1662 purple_blist_rename_buddy(buddy
, buf
);
1666 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1667 b
= g_new0(struct sipe_buddy
, 1);
1668 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1669 b
->name
= g_strdup(buddy
->name
);
1670 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1671 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1672 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1674 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1678 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1682 * We are calling g_hash_table_foreach_steal(). That means that no
1683 * key/value deallocation functions are called. Therefore the glib
1684 * hash code does not touch the key (buddy->name) or value (buddy)
1685 * of the to-be-deleted hash node at all. It follows that we
1687 * - MUST free the memory for the key ourselves and
1688 * - ARE allowed to do it in this function
1690 * Conclusion: glib must be broken on the Windows platform if sipe
1691 * crashes with SIGTRAP when closing. You'll have to live
1692 * with the memory leak until this is fixed.
1694 g_free(buddy
->name
);
1696 g_free(buddy
->annotation
);
1697 g_free(buddy
->device_name
);
1698 g_slist_free(buddy
->groups
);
1703 * Unassociates buddy from group first.
1704 * Then see if no groups left, removes buddy completely.
1705 * Otherwise updates buddy groups on server.
1707 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1709 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1710 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1711 struct sipe_group
*g
= NULL
;
1713 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1718 g
= sipe_group_find_by_name(sip
, group
->name
);
1722 b
->groups
= g_slist_remove(b
->groups
, g
);
1723 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1726 if (g_slist_length(b
->groups
) < 1) {
1727 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1728 sipe_cancel_scheduled_action(sip
, action_name
);
1729 g_free(action_name
);
1731 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1734 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1735 send_soap_request(sip
, body
);
1741 //updates groups on server
1742 sipe_group_set_user(sip
, b
->name
);
1748 sipe_rename_group(PurpleConnection
*gc
,
1749 const char *old_name
,
1751 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
1753 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1754 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1756 sipe_group_rename(sip
, s_group
, group
->name
);
1758 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1763 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1765 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1766 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1769 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1770 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1771 send_soap_request(sip
, body
);
1774 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1775 g_free(s_group
->name
);
1778 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1782 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
1784 PurpleStatusType
*type
;
1785 GList
*types
= NULL
;
1788 type
= purple_status_type_new_with_attrs(
1789 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1790 // Translators: noun
1791 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1793 types
= g_list_append(types
, type
);
1796 type
= purple_status_type_new_with_attrs(
1797 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1798 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1800 types
= g_list_append(types
, type
);
1802 // Do Not Disturb (not user settable)
1803 type
= purple_status_type_new_with_attrs(
1804 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1805 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1807 types
= g_list_append(types
, type
);
1810 type
= purple_status_type_new_with_attrs(
1811 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1812 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1814 types
= g_list_append(types
, type
);
1817 type
= purple_status_type_new_with_attrs(
1818 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1819 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1821 types
= g_list_append(types
, type
);
1824 type
= purple_status_type_new_with_attrs(
1825 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1826 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1828 types
= g_list_append(types
, type
);
1831 type
= purple_status_type_new_with_attrs(
1832 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1833 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1835 types
= g_list_append(types
, type
);
1838 type
= purple_status_type_new_full(
1839 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1840 types
= g_list_append(types
, type
);
1843 type
= purple_status_type_new_full(
1844 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1845 types
= g_list_append(types
, type
);
1851 * A callback for g_hash_table_foreach
1853 static void sipe_buddy_subscribe_cb(SIPE_UNUSED_PARAMETER
char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1855 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1856 int time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1857 int timeout
= (time_range
* rand()) / RAND_MAX
; /* random period within the range */
1858 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy
->name
));
1862 * Removes entries from purple buddy list
1863 * that does not correspond ones in the roaming contact list.
1865 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1866 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1867 GSList
*entry
= buddies
;
1868 struct sipe_buddy
*buddy
;
1872 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1873 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1876 g
= purple_buddy_get_group(b
);
1877 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1879 gboolean in_sipe_groups
= FALSE
;
1880 GSList
*entry2
= buddy
->groups
;
1882 struct sipe_group
*group
= entry2
->data
;
1883 if (!strcmp(group
->name
, g
->name
)) {
1884 in_sipe_groups
= TRUE
;
1887 entry2
= entry2
->next
;
1889 if(!in_sipe_groups
) {
1890 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1891 purple_blist_remove_buddy(b
);
1894 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1895 purple_blist_remove_buddy(b
);
1897 entry
= entry
->next
;
1901 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1903 int len
= msg
->bodylen
;
1905 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1908 const gchar
*contacts_delta
;
1909 xmlnode
*group_node
;
1910 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1914 /* Convert the contact from XML to Purple Buddies */
1915 isc
= xmlnode_from_str(msg
->body
, len
);
1920 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1921 if (contacts_delta
) {
1922 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1925 if (!strcmp(isc
->name
, "contactList")) {
1928 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1929 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1930 const char *name
= xmlnode_get_attrib(group_node
, "name");
1932 if (!strncmp(name
, "~", 1)) {
1933 name
= _("Other Contacts");
1935 group
->name
= g_strdup(name
);
1936 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1938 sipe_group_add(sip
, group
);
1941 // Make sure we have at least one group
1942 if (g_slist_length(sip
->groups
) == 0) {
1943 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1944 PurpleGroup
*purple_group
;
1945 group
->name
= g_strdup(_("Other Contacts"));
1947 purple_group
= purple_group_new(group
->name
);
1948 purple_blist_add_group(purple_group
, NULL
);
1949 sip
->groups
= g_slist_append(sip
->groups
, group
);
1952 /* Parse contacts */
1953 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1954 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1955 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1956 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1957 gchar
* buddy_name
= sip_uri_from_name(uri
);
1958 gchar
**item_groups
;
1959 struct sipe_group
*group
= NULL
;
1960 struct sipe_buddy
*buddy
= NULL
;
1963 // assign to group Other Contacts if nothing else received
1964 if(!groups
|| !strcmp("", groups
) ) {
1965 group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
1966 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
1969 item_groups
= g_strsplit(groups
, " ", 0);
1971 while (item_groups
[i
]) {
1972 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
1974 // If couldn't find the right group for this contact, just put them in the first group we have
1975 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1976 group
= sip
->groups
->data
;
1979 if (group
!= NULL
) {
1980 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1982 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1983 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1986 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
1987 if (name
!= NULL
&& strlen(name
) != 0) {
1988 purple_blist_alias_buddy(b
, name
);
1993 buddy
= g_new0(struct sipe_buddy
, 1);
1994 buddy
->name
= g_strdup(b
->name
);
1995 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1998 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2000 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2002 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2007 } // while, contact groups
2008 g_strfreev(item_groups
);
2016 sipe_cleanup_local_blist(sip
);
2020 //subscribe to buddies
2021 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2022 if(sip
->batched_support
){
2023 sipe_subscribe_presence_batched(sip
, NULL
);
2026 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2028 sip
->subscribed_buddies
= TRUE
;
2035 * Subscribe roaming contacts
2037 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2039 gchar
*to
= sip_uri_self(sip
);
2040 gchar
*tmp
= get_contact(sip
);
2041 gchar
*hdr
= g_strdup_printf(
2042 "Event: vnd-microsoft-roaming-contacts\r\n"
2043 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2044 "Supported: com.microsoft.autoextend\r\n"
2045 "Supported: ms-benotify\r\n"
2046 "Proxy-Require: ms-benotify\r\n"
2047 "Supported: ms-piggyback-first-notify\r\n"
2048 "Contact: %s\r\n", tmp
);
2051 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2056 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2057 SIPE_UNUSED_PARAMETER
void *unused
)
2059 gchar
*to
= sip_uri_self(sip
);
2060 gchar
*tmp
= get_contact(sip
);
2061 gchar
*hdr
= g_strdup_printf(
2062 "Event: presence.wpending\r\n"
2063 "Accept: text/xml+msrtc.wpending\r\n"
2064 "Supported: com.microsoft.autoextend\r\n"
2065 "Supported: ms-benotify\r\n"
2066 "Proxy-Require: ms-benotify\r\n"
2067 "Supported: ms-piggyback-first-notify\r\n"
2068 "Contact: %s\r\n", tmp
);
2071 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2077 * Fires on deregistration event initiated by server.
2078 * [MS-SIPREGE] SIP extension.
2083 // Content-Type: text/registration-event
2084 // subscription-state: terminated;expires=0
2085 // ms-diagnostics-public: 4141;reason="User disabled"
2087 // deregistered;event=rejected
2089 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2091 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2092 gchar
*event
= NULL
;
2093 gchar
*reason
= NULL
;
2094 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2096 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2097 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2099 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2100 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2101 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2102 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2104 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2108 if (warning
!= NULL
) {
2109 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2110 } else { // for LCS2005
2112 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2113 error_id
= 4140; // [MS-SIPREGE]
2114 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2115 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2116 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2118 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2119 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2121 reason
= g_strdup(_("User moved")); // [MS-OCER]
2125 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2128 sip
->gc
->wants_to_die
= TRUE
;
2129 purple_connection_error(sip
->gc
, warning
);
2134 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2136 xmlnode
*xn_provision_group_list
;
2139 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2141 /* provisionGroup */
2142 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2143 if (!strcmp("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2144 g_free(sip
->focus_factory_uri
);
2145 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2146 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2147 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2151 xmlnode_free(xn_provision_group_list
);
2154 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2156 const gchar
*contacts_delta
;
2159 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2165 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2168 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2175 free_container(struct sipe_container
*container
)
2179 if (!container
) return;
2181 entry
= container
->members
;
2183 g_free(entry
->data
);
2184 entry
= g_slist_remove(entry
, entry
->data
);
2190 * Finds locally stored MS-PRES container member
2192 static struct sipe_container_member
*
2193 sipe_find_container_member(struct sipe_container
*container
,
2197 struct sipe_container_member
*member
;
2200 if (container
== NULL
|| type
== NULL
) {
2204 entry
= container
->members
;
2206 member
= entry
->data
;
2207 if (!g_strcasecmp(member
->type
, type
)
2208 && ((!member
->value
&& !value
)
2209 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2213 entry
= entry
->next
;
2219 * Finds locally stored MS-PRES container by id
2221 static struct sipe_container
*
2222 sipe_find_container(struct sipe_account_data
*sip
,
2225 struct sipe_container
*container
;
2232 entry
= sip
->containers
;
2234 container
= entry
->data
;
2235 if (id
== container
->id
) {
2238 entry
= entry
->next
;
2252 sipe_find_access_level(struct sipe_account_data
*sip
,
2256 guint containers
[] = {32000, 400, 300, 200, 100};
2259 for (i
= 0; i
< 5; i
++) {
2260 struct sipe_container_member
*member
;
2261 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2262 if (!container
) continue;
2264 member
= sipe_find_container_member(container
, type
, value
);
2266 return containers
[i
];
2274 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2276 guint container_version
,
2277 const gchar
* action
,
2281 gchar
*self
= sip_uri_self(sip
);
2282 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2285 gchar
*body
= g_strdup_printf(
2286 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2287 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2288 "</setContainerMembers>",
2296 contact
= get_contact(sip
);
2297 hdr
= g_strdup_printf("Contact: %s\r\n"
2298 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2301 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2310 * When we receive some self (BE) NOTIFY with a new subscriber
2311 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2314 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2321 char *display_name
= NULL
;
2322 PurpleBuddy
*pbuddy
;
2327 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2329 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2332 contact
= get_contact(sip
);
2333 to
= sip_uri_self(sip
);
2336 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2337 guint id
= atoi(xmlnode_get_attrib(node
, "id"));
2338 struct sipe_container
*container
= sipe_find_container(sip
, id
);
2341 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2342 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
2343 free_container(container
);
2345 container
= g_new0(struct sipe_container
, 1);
2347 container
->version
= atoi(xmlnode_get_attrib(node
, "version"));
2348 sip
->containers
= g_slist_append(sip
->containers
, container
);
2349 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
2351 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
2352 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2353 member
->type
= xmlnode_get_attrib(node2
, "type");
2354 member
->value
= xmlnode_get_attrib(node2
, "value");
2355 container
->members
= g_slist_append(container
->members
, member
);
2356 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
2357 member
->type
, member
->value
? member
->value
: "");
2361 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
2362 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
2363 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
2364 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
2365 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
2366 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
2367 /* initial set-up to let counterparties see your status */
2368 if (sameEnterpriseAL
< 0) {
2369 struct sipe_container
*container
= sipe_find_container(sip
, 200);
2370 guint version
= container
? container
->version
: 0;
2371 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
2373 if (federatedAL
< 0) {
2374 struct sipe_container
*container
= sipe_find_container(sip
, 100);
2375 guint version
= container
? container
->version
: 0;
2376 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
2378 sip
->access_level_set
= TRUE
;
2382 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2384 const char *acknowledged
;
2388 user
= xmlnode_get_attrib(node
, "user");
2389 if (!user
) continue;
2390 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2391 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2392 uri_user
= sip_uri_from_name(user
);
2393 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri_user
);
2395 alias
= purple_buddy_get_local_alias(pbuddy
);
2396 uri_alias
= sip_uri_from_name(alias
);
2397 if (display_name
&& !g_ascii_strcasecmp(uri_user
, uri_alias
)) { // 'bad' alias
2398 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri_user
, display_name
);
2399 purple_blist_alias_buddy(pbuddy
, display_name
);
2404 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2405 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2406 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2407 if (!purple_find_buddy(sip
->account
, uri_user
)) {
2408 purple_account_request_add(sip
->account
, uri_user
, _("you"), display_name
, NULL
);
2411 hdr
= g_strdup_printf(
2413 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2415 body
= g_strdup_printf(
2416 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2417 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2418 "</setSubscribers>", user
);
2420 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2424 g_free(display_name
);
2433 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
2435 gchar
*to
= sip_uri_self(sip
);
2436 gchar
*tmp
= get_contact(sip
);
2437 gchar
*hdr
= g_strdup_printf(
2438 "Event: vnd-microsoft-roaming-ACL\r\n"
2439 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2440 "Supported: com.microsoft.autoextend\r\n"
2441 "Supported: ms-benotify\r\n"
2442 "Proxy-Require: ms-benotify\r\n"
2443 "Supported: ms-piggyback-first-notify\r\n"
2444 "Contact: %s\r\n", tmp
);
2447 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2453 * To request for presence information about the user, access level settings that have already been configured by the user
2454 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2455 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2458 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
2460 gchar
*to
= sip_uri_self(sip
);
2461 gchar
*tmp
= get_contact(sip
);
2462 gchar
*hdr
= g_strdup_printf(
2463 "Event: vnd-microsoft-roaming-self\r\n"
2464 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2465 "Supported: ms-benotify\r\n"
2466 "Proxy-Require: ms-benotify\r\n"
2467 "Supported: ms-piggyback-first-notify\r\n"
2469 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2471 gchar
*body
=g_strdup(
2472 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2473 "<roaming type=\"categories\"/>"
2474 "<roaming type=\"containers\"/>"
2475 "<roaming type=\"subscribers\"/></roamingList>");
2478 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2487 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
2489 gchar
*to
= sip_uri_self(sip
);
2490 gchar
*tmp
= get_contact(sip
);
2491 gchar
*hdr
= g_strdup_printf(
2492 "Event: vnd-microsoft-provisioning\r\n"
2493 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
2494 "Supported: com.microsoft.autoextend\r\n"
2495 "Supported: ms-benotify\r\n"
2496 "Proxy-Require: ms-benotify\r\n"
2497 "Supported: ms-piggyback-first-notify\r\n"
2499 "Contact: %s\r\n", tmp
);
2502 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
2507 /** Subscription for provisioning information to help with initial
2508 * configuration. This subscription is a one-time query (denoted by the Expires header,
2509 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2510 * configuration, meeting policies, and policy settings that Communicator must enforce.
2511 * TODO: for what we need this information.
2514 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
2516 gchar
*to
= sip_uri_self(sip
);
2517 gchar
*tmp
= get_contact(sip
);
2518 gchar
*hdr
= g_strdup_printf(
2519 "Event: vnd-microsoft-provisioning-v2\r\n"
2520 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2521 "Supported: com.microsoft.autoextend\r\n"
2522 "Supported: ms-benotify\r\n"
2523 "Proxy-Require: ms-benotify\r\n"
2524 "Supported: ms-piggyback-first-notify\r\n"
2527 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2528 gchar
*body
= g_strdup(
2529 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2530 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2531 "<provisioningGroup name=\"ucPolicy\"/>"
2532 "</provisioningGroupList>");
2535 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2541 /* IM Session (INVITE and MESSAGE methods) */
2543 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2545 get_end_points (struct sipe_account_data
*sip
,
2546 struct sip_session
*session
)
2550 if (session
== NULL
) {
2554 res
= g_strdup_printf("<sip:%s>", sip
->username
);
2556 SIPE_DIALOG_FOREACH
{
2558 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2561 if (dialog
->theirepid
) {
2563 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2566 } SIPE_DIALOG_FOREACH_END
;
2572 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
2574 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2576 gboolean ret
= TRUE
;
2578 if (msg
->response
!= 200) {
2579 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2583 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2589 * Asks UA/proxy about its capabilities.
2591 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2593 gchar
*to
= sip_uri(who
);
2594 gchar
*contact
= get_contact(sip
);
2595 gchar
*request
= g_strdup_printf(
2596 "Accept: application/sdp\r\n"
2597 "Contact: %s\r\n", contact
);
2600 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2607 sipe_notify_user(struct sipe_account_data
*sip
,
2608 struct sip_session
*session
,
2609 PurpleMessageFlags flags
,
2610 const gchar
*message
)
2612 PurpleConversation
*conv
;
2614 if (!session
->conv
) {
2615 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
2617 conv
= session
->conv
;
2619 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
2623 sipe_present_info(struct sipe_account_data
*sip
,
2624 struct sip_session
*session
,
2625 const gchar
*message
)
2627 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
2631 sipe_present_err(struct sipe_account_data
*sip
,
2632 struct sip_session
*session
,
2633 const gchar
*message
)
2635 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
2639 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
2640 struct sip_session
*session
,
2642 const gchar
*message
)
2644 char *msg
, *msg_tmp
;
2646 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2647 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2649 msg_tmp
= g_strdup_printf( _("This message was not delivered to %s because one or more recipients are offline:\n%s") ,
2650 who
? who
: "", msg
? msg
: "");
2651 sipe_present_err(sip
, session
, msg_tmp
);
2657 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
);
2660 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
2661 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2663 gboolean ret
= TRUE
;
2664 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2665 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
2666 struct sip_dialog
*dialog
;
2672 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2677 dialog
= sipe_dialog_find(session
, with
);
2679 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2684 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2685 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
2687 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2689 if (msg
->response
>= 400) {
2690 PurpleBuddy
*pbuddy
;
2691 gchar
*alias
= with
;
2693 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
2695 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
2696 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
2699 sipe_present_message_undelivered_err(sip
, session
, alias
, message
);
2702 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
2704 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
2705 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
2706 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
2709 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2710 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2711 key
, g_hash_table_size(session
->unconfirmed_messages
));
2717 if (ret
) sipe_im_process_queue(sip
, session
);
2722 sipe_is_election_finished(struct sip_session
*session
);
2725 sipe_election_result(struct sipe_account_data
*sip
,
2729 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
2730 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2732 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2733 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2734 struct sip_dialog
*dialog
;
2735 struct sip_session
*session
;
2737 session
= sipe_session_find_chat_by_callid(sip
, callid
);
2739 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
2743 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
2744 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2745 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
2746 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
2748 if (xn_request_rm_response
) {
2749 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
2750 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
2752 dialog
= sipe_dialog_find(session
, with
);
2754 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
2758 if (allow
&& !g_strcasecmp(allow
, "true")) {
2759 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
2760 dialog
->election_vote
= 1;
2761 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
2762 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
2763 dialog
->election_vote
= -1;
2766 if (sipe_is_election_finished(session
)) {
2767 sipe_election_result(sip
, session
);
2770 } else if (xn_set_rm_response
) {
2773 xmlnode_free(xn_action
);
2780 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
2789 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2790 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2792 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2795 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2798 msgr
= g_strdup("");
2801 tmp
= get_contact(sip
);
2802 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2803 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2804 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
2805 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
2809 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
2816 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
2818 GSList
*entry2
= session
->outgoing_message_queue
;
2820 char *queued_msg
= entry2
->data
;
2822 /* for multiparty chat or conference */
2823 if (session
->is_multiparty
|| session
->focus_uri
) {
2824 gchar
*who
= sip_uri_self(sip
);
2825 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
2826 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
2830 SIPE_DIALOG_FOREACH
{
2833 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
2835 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
2836 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2837 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2838 key
, g_hash_table_size(session
->unconfirmed_messages
));
2841 sipe_send_message(sip
, dialog
, queued_msg
);
2842 } SIPE_DIALOG_FOREACH_END
;
2844 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2850 sipe_refer_notify(struct sipe_account_data
*sip
,
2851 struct sip_session
*session
,
2858 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
2860 hdr
= g_strdup_printf(
2862 "Subscription-State: %s\r\n"
2863 "Content-Type: message/sipfrag\r\n",
2864 status
>= 200 ? "terminated" : "active");
2866 body
= g_strdup_printf(
2867 "SIP/2.0 %d %s\r\n",
2870 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
2877 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2879 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2880 struct sip_session
*session
;
2881 struct sip_dialog
*dialog
;
2885 struct sipmsg
*request_msg
= trans
->msg
;
2887 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2890 session
= sipe_session_find_chat_by_callid(sip
, callid
);
2892 session
= sipe_session_find_im(sip
, with
);
2895 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2900 dialog
= sipe_dialog_find(session
, with
);
2902 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2907 sipe_dialog_parse(dialog
, msg
, TRUE
);
2909 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2910 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2912 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2914 if (msg
->response
!= 200) {
2915 PurpleBuddy
*pbuddy
;
2916 gchar
*alias
= with
;
2918 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2920 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
2921 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
2925 sipe_present_message_undelivered_err(sip
, session
, alias
, message
);
2927 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
2928 sipe_present_err(sip
, session
, tmp_msg
);
2932 sipe_dialog_remove(session
, with
);
2940 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
2941 dialog
->outgoing_invite
= NULL
;
2942 dialog
->is_established
= TRUE
;
2944 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
2946 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
2947 g_free(referred_by
);
2950 /* add user to chat if it is a multiparty session */
2951 if (session
->is_multiparty
) {
2952 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
2954 PURPLE_CBFLAGS_NONE
, TRUE
);
2957 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
2958 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2959 if (session
->outgoing_message_queue
) {
2960 char *queued_msg
= session
->outgoing_message_queue
->data
;
2961 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2966 sipe_im_process_queue(sip
, session
);
2968 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2969 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2970 key
, g_hash_table_size(session
->unconfirmed_messages
));
2979 sipe_invite(struct sipe_account_data
*sip
,
2980 struct sip_session
*session
,
2982 const gchar
*msg_body
,
2983 const gchar
*referred_by
,
2984 const gboolean is_triggered
)
2991 char *ms_text_format
= g_strdup("");
2992 gchar
*roster_manager
;
2994 gchar
*referred_by_str
;
2995 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
2997 if (dialog
&& dialog
->is_established
) {
2998 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
3003 dialog
= sipe_dialog_add(session
);
3004 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3005 dialog
->with
= g_strdup(who
);
3008 if (!(dialog
->ourtag
)) {
3009 dialog
->ourtag
= gentag();
3022 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3023 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
3025 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3029 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3033 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3034 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
3039 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3040 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
3041 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
3042 key
, g_hash_table_size(session
->unconfirmed_messages
));
3046 contact
= get_contact(sip
);
3047 end_points
= get_end_points(sip
, session
);
3048 self
= sip_uri_self(sip
);
3049 roster_manager
= g_strdup_printf(
3050 "Roster-Manager: %s\r\n"
3051 "EndPoints: %s\r\n",
3054 referred_by_str
= referred_by
?
3056 "Referred-By: %s\r\n",
3059 hdr
= g_strdup_printf(
3060 "Supported: ms-sender\r\n"
3066 "Content-Type: application/sdp\r\n",
3067 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
3069 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3070 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3073 g_free(ms_text_format
);
3076 body
= g_strdup_printf(
3078 "o=- 0 0 IN IP4 %s\r\n"
3082 "m=message %d sip null\r\n"
3083 "a=accept-types:text/plain text/html image/gif "
3084 "multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3085 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
3087 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
3088 to
, to
, hdr
, body
, dialog
, process_invite_response
);
3091 g_free(roster_manager
);
3093 g_free(referred_by_str
);
3100 sipe_refer(struct sipe_account_data
*sip
,
3101 struct sip_session
*session
,
3106 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
3107 session
->roster_manager
);
3109 contact
= get_contact(sip
);
3110 hdr
= g_strdup_printf(
3112 "Refer-to: <%s>\r\n"
3113 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
3114 "Require: com.microsoft.rtc-multiparty\r\n",
3118 dialog
->ourtag
? ";tag=" : "",
3119 dialog
->ourtag
? dialog
->ourtag
: "",
3122 send_sip_request(sip
->gc
, "REFER",
3123 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
3130 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
3131 struct sip_dialog
*dialog
,
3134 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3136 gchar
*body
= g_strdup_printf(
3137 "<?xml version=\"1.0\"?>\r\n"
3138 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3139 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
3140 sip
->username
, bid
);
3142 send_sip_request(sip
->gc
, "INFO",
3143 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3149 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
3150 struct sip_dialog
*dialog
)
3152 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3154 gchar
*body
= g_strdup_printf(
3155 "<?xml version=\"1.0\"?>\r\n"
3156 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3157 "<SetRM uri=\"sip:%s\"/></action>\r\n",
3160 send_sip_request(sip
->gc
, "INFO",
3161 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3167 sipe_session_close(struct sipe_account_data
*sip
,
3168 struct sip_session
* session
)
3170 if (session
&& session
->focus_uri
) {
3171 conf_session_close(sip
, session
);
3175 SIPE_DIALOG_FOREACH
{
3176 /* @TODO slow down BYE message sending rate */
3177 /* @see single subscription code */
3178 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3179 } SIPE_DIALOG_FOREACH_END
;
3181 sipe_session_remove(sip
, session
);
3186 sipe_session_close_all(struct sipe_account_data
*sip
)
3189 while ((entry
= sip
->sessions
) != NULL
) {
3190 sipe_session_close(sip
, entry
->data
);
3195 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3197 struct sipe_account_data
*sip
= gc
->proto_data
;
3199 purple_debug_info("sipe", "conversation with %s closed\n", who
);
3200 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
3204 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3206 struct sipe_account_data
*sip
= gc
->proto_data
;
3207 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
3209 sipe_session_close(sip
, session
);
3212 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
3213 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3215 struct sipe_account_data
*sip
= gc
->proto_data
;
3216 struct sip_session
*session
;
3217 struct sip_dialog
*dialog
;
3219 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
3221 session
= sipe_session_find_or_add_im(sip
, who
);
3222 dialog
= sipe_dialog_find(session
, who
);
3224 // Queue the message
3225 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
3227 if (dialog
&& dialog
->callid
) {
3228 sipe_im_process_queue(sip
, session
);
3229 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3230 // Need to send the INVITE to get the outgoing dialog setup
3231 sipe_invite(sip
, session
, who
, what
, NULL
, FALSE
);
3237 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
3238 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3240 struct sipe_account_data
*sip
= gc
->proto_data
;
3241 struct sip_session
*session
;
3243 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
3245 session
= sipe_session_find_chat_by_id(sip
, id
);
3247 // Queue the message
3248 if (session
&& session
->dialogs
) {
3249 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
3251 sipe_im_process_queue(sip
, session
);
3257 /* End IM Session (INVITE and MESSAGE methods) */
3259 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3261 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3262 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3263 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3264 struct sip_session
*session
;
3266 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
3268 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3270 session
= sipe_session_find_im(sip
, from
);
3277 if (!strncmp(contenttype
, "application/x-ms-mim", 20)) {
3278 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3279 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
3280 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
3282 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
3284 if (xn_request_rm
) {
3285 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
3286 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
3287 gchar
*body
= g_strdup_printf(
3288 "<?xml version=\"1.0\"?>\r\n"
3289 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3290 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
3292 session
->bid
< bid
? "true" : "false");
3293 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3295 } else if (xn_set_rm
) {
3297 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
3298 g_free(session
->roster_manager
);
3299 session
->roster_manager
= g_strdup(rm
);
3301 body
= g_strdup_printf(
3302 "<?xml version=\"1.0\"?>\r\n"
3303 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3304 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
3306 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3309 xmlnode_free(xn_action
);
3312 /* looks like purple lacks typing notification for chat */
3313 if (!session
->is_multiparty
&& !session
->focus_uri
) {
3314 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3315 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
3317 if (status
&& !strcmp(status
, "type")) {
3318 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3319 } else if (status
&& !strcmp(status
, "idle")) {
3320 serv_got_typing_stopped(sip
->gc
, from
);
3322 xmlnode_free(xn_keyboard_activity
);
3325 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3330 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3332 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3333 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3334 struct sip_session
*session
;
3336 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3338 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3340 session
= sipe_session_find_im(sip
, from
);
3347 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
3348 g_free(session
->roster_manager
);
3349 session
->roster_manager
= NULL
;
3352 /* This what BYE is essentially for - terminating dialog */
3353 sipe_dialog_remove(session
, from
);
3354 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
3355 sipe_conf_immcu_closed(sip
, session
);
3356 } else if (session
->is_multiparty
) {
3357 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
3363 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3365 gchar
*self
= sip_uri_self(sip
);
3366 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3367 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3368 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
3369 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
3370 struct sip_session
*session
;
3371 struct sip_dialog
*dialog
;
3373 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3374 dialog
= sipe_dialog_find(session
, from
);
3376 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
3377 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
3379 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
3381 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
3387 g_free(referred_by
);
3391 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
3393 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
3394 struct sip_session
*session
;
3395 struct sip_dialog
*dialog
;
3397 if (state
== PURPLE_NOT_TYPING
)
3400 session
= sipe_session_find_im(sip
, who
);
3401 dialog
= sipe_dialog_find(session
, who
);
3403 if (session
&& dialog
&& dialog
->is_established
) {
3404 send_sip_request(gc
, "INFO", who
, who
,
3405 "Content-Type: application/xml\r\n",
3406 SIPE_SEND_TYPING
, dialog
, NULL
);
3408 return SIPE_TYPING_SEND_TIMEOUT
;
3411 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
3413 GSList
*tmp
= sip
->transactions
;
3414 time_t currtime
= time(NULL
);
3416 struct transaction
*trans
= tmp
->data
;
3418 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
3419 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
3422 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
3424 sendout_sipmsg(sip
, trans
->msg
);
3431 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
3432 SIPE_UNUSED_PARAMETER
void *unused
)
3434 /* register again when security token expires */
3435 /* we have to start a new authentication as the security token
3436 * is almost expired by sending a not signed REGISTER message */
3437 purple_debug_info("sipe", "do a full reauthentication\n");
3438 sipe_auth_free(&sip
->registrar
);
3439 sipe_auth_free(&sip
->proxy
);
3440 sip
->registerstatus
= 0;
3442 sip
->reauthenticate_set
= FALSE
;
3445 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3449 gboolean found
= FALSE
;
3451 from
= parse_from(sipmsg_find_header(msg
, "From"));
3455 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
3457 contenttype
= sipmsg_find_header(msg
, "Content-Type");
3458 if (!strncmp(contenttype
, "text/plain", 10)
3459 || !strncmp(contenttype
, "text/html", 9)
3460 || !strncmp(contenttype
, "multipart/related", 21))
3462 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3463 gchar
*html
= get_html_message(contenttype
, msg
->body
);
3465 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
3467 session
= sipe_session_find_im(sip
, from
);
3470 if (session
&& session
->focus_uri
) { /* a conference */
3471 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
3472 gchar
*sender
= parse_from(tmp
);
3474 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
3475 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3477 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
3478 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3479 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3481 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3484 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3487 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
3488 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3493 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
3497 state
= xmlnode_get_child(isc
, "state");
3500 purple_debug_info("sipe", "process_incoming_message: no state found\n");
3505 statedata
= xmlnode_get_data(state
);
3507 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
3508 else serv_got_typing_stopped(sip
->gc
, from
);
3513 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3517 purple_debug_info("sipe", "got unknown mime-type");
3518 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
3523 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3525 gchar
*ms_text_format
;
3530 gboolean is_multiparty
= FALSE
;
3531 gboolean is_triggered
= FALSE
;
3532 gboolean was_multiparty
= TRUE
;
3533 gboolean just_joined
= FALSE
;
3535 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3536 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
3537 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
3538 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
3539 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
3540 GSList
*end_points
= NULL
;
3541 struct sip_session
*session
;
3543 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3545 /* Invitation to join conference */
3546 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
3547 process_incoming_invite_conf(sip
, msg
);
3551 /* Only accept text invitations */
3552 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3553 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3557 // TODO There *must* be a better way to clean up the To header to add a tag...
3558 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
3559 oldHeader
= sipmsg_find_header(msg
, "To");
3561 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
3562 sipmsg_remove_header_now(msg
, "To");
3563 sipmsg_add_header_now(msg
, "To", newHeader
);
3566 if (end_points_hdr
) {
3567 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
3569 if (g_slist_length(end_points
) > 2) {
3570 is_multiparty
= TRUE
;
3573 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
3574 is_triggered
= TRUE
;
3575 is_multiparty
= TRUE
;
3578 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3579 /* Convert to multiparty */
3580 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
3581 g_free(session
->with
);
3582 session
->with
= NULL
;
3583 was_multiparty
= FALSE
;
3584 session
->is_multiparty
= TRUE
;
3585 session
->chat_id
= rand();
3588 if (!session
&& is_multiparty
) {
3589 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
3592 from
= parse_from(sipmsg_find_header(msg
, "From"));
3594 session
= sipe_session_find_or_add_im(sip
, from
);
3597 g_free(session
->callid
);
3598 session
->callid
= g_strdup(callid
);
3600 session
->is_multiparty
= is_multiparty
;
3601 if (roster_manager
) {
3602 session
->roster_manager
= g_strdup(roster_manager
);
3605 if (is_multiparty
&& end_points
) {
3606 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
3607 GSList
*entry
= end_points
;
3609 struct sip_dialog
*dialog
;
3610 struct sipendpoint
*end_point
= entry
->data
;
3611 entry
= entry
->next
;
3613 if (!g_strcasecmp(from
, end_point
->contact
) ||
3614 !g_strcasecmp(to
, end_point
->contact
))
3617 dialog
= sipe_dialog_find(session
, end_point
->contact
);
3619 g_free(dialog
->theirepid
);
3620 dialog
->theirepid
= end_point
->epid
;
3621 end_point
->epid
= NULL
;
3623 dialog
= sipe_dialog_add(session
);
3625 dialog
->callid
= g_strdup(session
->callid
);
3626 dialog
->with
= end_point
->contact
;
3627 end_point
->contact
= NULL
;
3628 dialog
->theirepid
= end_point
->epid
;
3629 end_point
->epid
= NULL
;
3633 /* send triggered INVITE */
3634 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
3641 GSList
*entry
= end_points
;
3643 struct sipendpoint
*end_point
= entry
->data
;
3644 entry
= entry
->next
;
3645 g_free(end_point
->contact
);
3646 g_free(end_point
->epid
);
3649 g_slist_free(end_points
);
3653 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
3655 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3657 dialog
= sipe_dialog_add(session
);
3659 dialog
->callid
= g_strdup(session
->callid
);
3660 dialog
->with
= g_strdup(from
);
3661 sipe_dialog_parse(dialog
, msg
, FALSE
);
3663 if (!dialog
->ourtag
) {
3664 dialog
->ourtag
= newTag
;
3671 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3675 if (is_multiparty
&& !session
->conv
) {
3676 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
3677 gchar
*self
= sip_uri_self(sip
);
3678 /* create prpl chat */
3679 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_name
);
3680 session
->chat_name
= g_strdup(chat_name
);
3681 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
3683 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3685 PURPLE_CBFLAGS_NONE
, FALSE
);
3690 if (is_multiparty
&& !was_multiparty
) {
3691 /* add current IM counterparty to chat */
3692 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3693 sipe_dialog_first(session
)->with
, NULL
,
3694 PURPLE_CBFLAGS_NONE
, FALSE
);
3697 /* add inviting party to chat */
3698 if (just_joined
&& session
->conv
) {
3699 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3701 PURPLE_CBFLAGS_NONE
, TRUE
);
3704 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
3705 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3706 if (ms_text_format
) {
3707 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3709 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3711 if (is_multiparty
) {
3712 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3713 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3715 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3718 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3724 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
3725 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3726 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3728 body
= g_strdup_printf(
3730 "o=- 0 0 IN IP4 %s\r\n"
3734 "m=message %d sip sip:%s\r\n"
3735 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3736 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3737 sip
->realport
, sip
->username
);
3738 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3742 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3746 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
3747 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3748 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3750 body
= g_strdup_printf(
3752 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3754 "c=IN IP4 0.0.0.0\r\n"
3756 "m=message %d sip sip:%s\r\n"
3757 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3758 sip
->realport
, sip
->username
);
3759 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3763 static void sipe_connection_cleanup(struct sipe_account_data
*);
3764 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3766 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3769 const gchar
*expires_header
;
3771 GSList
*hdr
= msg
->headers
;
3772 struct siphdrelement
*elem
;
3774 expires_header
= sipmsg_find_header(msg
, "Expires");
3775 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3776 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3778 switch (msg
->response
) {
3781 sip
->registerstatus
= 0;
3783 gchar
*contact_hdr
= NULL
;
3789 if (!sip
->reregister_set
) {
3790 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3791 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
3792 g_free(action_name
);
3793 sip
->reregister_set
= TRUE
;
3796 sip
->registerstatus
= 3;
3799 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3801 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3804 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3808 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3809 fill_auth(tmp
, &sip
->registrar
);
3812 if (!sip
->reauthenticate_set
) {
3813 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3814 guint reauth_timeout
;
3815 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
3816 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
3817 reauth_timeout
= sip
->registrar
.expires
- 300;
3819 /* NTLM: we have to reauthenticate as our security token expires
3820 after eight hours (be five minutes early) */
3821 reauth_timeout
= (8 * 3600) - 300;
3823 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
3824 g_free(action_name
);
3825 sip
->reauthenticate_set
= TRUE
;
3828 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3830 epid
= get_epid(sip
);
3831 uuid
= generateUUIDfromEPID(epid
);
3834 // There can be multiple Contact headers (one per location where the user is logged in) so
3835 // make sure to only get the one for this uuid
3836 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3837 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3838 if (valid_contact
) {
3839 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3840 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3841 g_free(valid_contact
);
3844 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3849 g_free(sip
->contact
);
3851 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3854 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3855 sip
->contact
= g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip
->username
, sip
->listenport
, purple_network_get_my_ip(-1), TRANSPORT_DESCRIPTOR
);
3857 sip
->msrtc_event_categories
= FALSE
;
3858 sip
->batched_support
= FALSE
;
3863 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3864 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
3865 sip
->msrtc_event_categories
= TRUE
;
3866 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->msrtc_event_categories
);
3868 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
3869 sip
->batched_support
= TRUE
;
3870 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->batched_support
);
3873 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3874 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3877 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3878 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3883 hdr
= g_slist_next(hdr
);
3887 if (!sip
->subscribed
) { //do it just once, not every re-register
3889 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
3890 (GCompareFunc
)g_ascii_strcasecmp
)) {
3891 sipe_subscribe_roaming_contacts(sip
);
3894 /* For 2007+ it does not make sence to subscribe to:
3895 * vnd-microsoft-roaming-ACL
3896 * vnd-microsoft-provisioning (not v2)
3898 * These are for backward compatibility.
3900 if (sip
->msrtc_event_categories
)
3902 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
3903 (GCompareFunc
)g_ascii_strcasecmp
)) {
3904 sipe_subscribe_roaming_self(sip
);
3906 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
3907 (GCompareFunc
)g_ascii_strcasecmp
)) {
3908 sipe_subscribe_roaming_provisioning_v2(sip
);
3911 /* For 2005- servers */
3914 //sipe_options_request(sip, sip->sipdomain);
3916 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
3917 (GCompareFunc
)g_ascii_strcasecmp
)) {
3918 sipe_subscribe_roaming_acl(sip
);
3920 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
3921 (GCompareFunc
)g_ascii_strcasecmp
)) {
3922 sipe_subscribe_roaming_provisioning(sip
);
3924 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
3925 (GCompareFunc
)g_ascii_strcasecmp
)) {
3926 sipe_subscribe_presence_wpending(sip
, msg
);
3929 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3930 sip
->subscribed
= TRUE
;
3933 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
3934 "timeout=", ";", NULL
);
3935 if (timeout
!= NULL
) {
3936 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
3937 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
3938 sip
->keepalive_timeout
);
3942 // Should we remove the transaction here?
3943 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3944 transactions_remove(sip
, tc
);
3949 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3951 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3952 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3956 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3959 tmp
= g_strsplit(parts
[0], ":", 0);
3960 hostname
= g_strdup(tmp
[0]);
3961 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3965 tmp
= g_strsplit(parts
[i
], "=", 0);
3967 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3968 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3969 transport
= SIPE_TRANSPORT_TCP
;
3970 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3971 transport
= SIPE_TRANSPORT_UDP
;
3980 /* Close old connection */
3981 sipe_connection_cleanup(sip
);
3983 /* Create new connection */
3984 sip
->transport
= transport
;
3985 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3986 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3987 create_connection(sip
, hostname
, port
);
3993 if (sip
->registerstatus
!= 2) {
3994 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3995 if (sip
->registrar
.retries
> 3) {
3996 sip
->gc
->wants_to_die
= TRUE
;
3997 purple_connection_error(sip
->gc
, _("Wrong Password"));
4001 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4003 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
4006 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4009 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
4010 fill_auth(tmp
, &sip
->registrar
);
4011 sip
->registerstatus
= 2;
4012 if (sip
->account
->disconnecting
) {
4013 do_register_exp(sip
, 0);
4021 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
4022 if (warning
!= NULL
) {
4024 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
4026 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
4027 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
4030 warning
= g_strdup(_("You have been rejected by the server"));
4033 sip
->gc
->wants_to_die
= TRUE
;
4034 purple_connection_error(sip
->gc
, warning
);
4041 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4042 if (warning
!= NULL
) {
4043 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4044 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
4047 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
4050 sip
->gc
->wants_to_die
= TRUE
;
4051 purple_connection_error(sip
->gc
, warning
);
4058 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4059 if (warning
!= NULL
) {
4060 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4061 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
4064 warning
= g_strdup(_("Service unavailable: no reason given"));
4067 sip
->gc
->wants_to_die
= TRUE
;
4068 purple_connection_error(sip
->gc
, warning
);
4077 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4080 xmlnode
*xn_categories
;
4081 xmlnode
*xn_category
;
4083 const char *activity
= NULL
;
4085 xn_categories
= xmlnode_from_str(data
, len
);
4086 uri
= xmlnode_get_attrib(xn_categories
, "uri");
4088 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
4090 xn_category
= xmlnode_get_next_twin(xn_category
) )
4092 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
4094 if (!strcmp(attrVar
, "note"))
4097 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4102 xn_node
= xmlnode_get_child(xn_category
, "note");
4103 if (!xn_node
) continue;
4104 xn_node
= xmlnode_get_child(xn_node
, "body");
4105 if (!xn_node
) continue;
4106 note
= xmlnode_get_data(xn_node
);
4107 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
? note
: "");
4108 g_free(sbuddy
->annotation
);
4109 sbuddy
->annotation
= NULL
;
4110 if (note
) sbuddy
->annotation
= g_strdup(note
);
4116 else if(!strcmp(attrVar
, "state"))
4120 xn_node
= xmlnode_get_child(xn_category
, "state");
4121 if (!xn_node
) continue;
4122 xn_node
= xmlnode_get_child(xn_node
, "availability");
4123 if (!xn_node
) continue;
4125 data
= xmlnode_get_data(xn_node
);
4130 activity
= SIPE_STATUS_ID_UNKNOWN
;
4131 else if (avail
< 4500)
4132 activity
= SIPE_STATUS_ID_AVAILABLE
;
4133 else if (avail
< 6000)
4134 activity
= SIPE_STATUS_ID_BRB
;
4135 else if (avail
< 7500)
4136 activity
= SIPE_STATUS_ID_ONPHONE
;
4137 else if (avail
< 9000)
4138 activity
= SIPE_STATUS_ID_BUSY
;
4139 else if (avail
< 12000)
4140 activity
= SIPE_STATUS_ID_DND
;
4141 else if (avail
< 18000)
4142 activity
= SIPE_STATUS_ID_AWAY
;
4144 activity
= SIPE_STATUS_ID_OFFLINE
;
4148 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
4149 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
4152 xmlnode_free(xn_categories
);
4155 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
4157 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4158 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
4159 payload
->host
= g_strdup(host
);
4160 payload
->buddies
= server
;
4161 sipe_subscribe_presence_batched_routed(sip
, payload
);
4162 sipe_subscribe_presence_batched_routed_free(payload
);
4165 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4168 xmlnode
*xn_resource
;
4169 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4174 xn_list
= xmlnode_from_str(data
, len
);
4176 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
4178 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
4180 const char *uri
, *state
;
4181 xmlnode
*xn_instance
;
4183 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
4184 if (!xn_instance
) continue;
4186 uri
= xmlnode_get_attrib(xn_resource
, "uri");
4187 state
= xmlnode_get_attrib(xn_instance
, "state");
4188 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
4190 if (strstr(state
, "resubscribe")) {
4191 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
4192 struct sipe_buddy
*sbuddy
;
4193 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4194 gchar
*user
= g_strdup(uri
);
4195 host
= g_strdup(poolFqdn
);
4196 server
= g_hash_table_lookup(servers
, host
);
4197 server
= g_slist_append(server
, user
);
4198 g_hash_table_insert(servers
, host
, server
);
4200 sipe_subscribe_presence_single(sip
, (void *) uri
);
4202 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4204 sbuddy
->resubscribed
= TRUE
;
4209 /* Send out any deferred poolFqdn subscriptions */
4210 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
4211 g_hash_table_destroy(servers
);
4213 xmlnode_free(xn_list
);
4216 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4220 gchar
*activity
= NULL
;
4222 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
4223 gboolean isonline
= FALSE
;
4224 xmlnode
*display_name_node
;
4226 pidf
= xmlnode_from_str(data
, len
);
4228 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
4232 uri
= xmlnode_get_attrib(pidf
, "entity");
4234 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
4236 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4237 basicstatus
= xmlnode_get_child(status
, "basic");
4242 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
4247 getbasic
= xmlnode_get_data(basicstatus
);
4249 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
4254 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
4255 if (strstr(getbasic
, "open")) {
4260 display_name_node
= xmlnode_get_child(pidf
, "display-name");
4261 // updating display name if alias was just URI
4262 if (display_name_node
) {
4263 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4264 GSList
*entry
= buddies
;
4265 PurpleBuddy
*p_buddy
;
4266 char * display_name
= xmlnode_get_data(display_name_node
);
4269 const char *server_alias
;
4272 p_buddy
= entry
->data
;
4274 alias
= (char *)purple_buddy_get_alias(p_buddy
);
4275 alias
= alias
? sip_uri_from_name(alias
) : NULL
;
4276 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
4277 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4278 purple_blist_alias_buddy(p_buddy
, display_name
);
4282 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4284 ( (server_alias
&& strcmp(display_name
, server_alias
))
4285 || !server_alias
|| strlen(server_alias
) == 0 )
4287 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4290 entry
= entry
->next
;
4292 g_free(display_name
);
4295 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
4296 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4297 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
4298 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
4299 activity
= xmlnode_get_data(basicstatus
);
4300 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
4307 const gchar
* status_id
= NULL
;
4309 if (strstr(activity
, "busy")) {
4310 status_id
= SIPE_STATUS_ID_BUSY
;
4311 } else if (strstr(activity
, "away")) {
4312 status_id
= SIPE_STATUS_ID_AWAY
;
4317 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4320 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
4321 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
4323 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
4330 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4332 const char *availability
;
4333 const char *activity
;
4334 const char *display_name
= NULL
;
4335 const char *activity_name
= NULL
;
4340 struct sipe_buddy
*sbuddy
;
4342 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
4344 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
4345 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
4346 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
4347 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
4348 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
4349 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
4350 xmlnode
*xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
):NULL
;
4351 const char *avail
= xn_state
? xmlnode_get_attrib(xn_state
, "avail") : NULL
;
4353 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
4354 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
4355 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
4356 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
4357 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
4358 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
4360 name
= xmlnode_get_attrib(xn_presentity
, "uri");
4361 uri
= sip_uri_from_name(name
);
4362 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
4363 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
4365 // updating display name if alias was just URI
4366 if (xn_display_name
) {
4367 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4368 GSList
*entry
= buddies
;
4369 PurpleBuddy
*p_buddy
;
4370 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
4373 const char *email_str
, *server_alias
;
4375 p_buddy
= entry
->data
;
4377 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
4378 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4379 purple_blist_alias_buddy(p_buddy
, display_name
);
4382 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4384 ( (server_alias
&& strcmp(display_name
, server_alias
))
4385 || !server_alias
|| strlen(server_alias
) == 0 )
4387 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4391 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
4392 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
4393 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
4397 entry
= entry
->next
;
4401 avl
= atoi(availability
);
4402 act
= atoi(activity
);
4404 if(sip
->msrtc_event_categories
){
4405 if (act
== 100 && avl
== 0)
4406 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4407 else if (act
== 100 && avl
== 300)
4408 activity_name
= SIPE_STATUS_ID_AWAY
;
4409 else if (act
== 300 && avl
== 300)
4410 activity_name
= SIPE_STATUS_ID_BRB
;
4411 else if (act
== 400 && avl
== 300)
4412 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4413 else if (act
== 500 && act
== 300)
4414 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4415 else if (act
== 600 && avl
== 300)
4416 activity_name
= SIPE_STATUS_ID_BUSY
;
4417 else if (act
== 0 && avl
== 0){ //MSRTC elements are zero
4418 if(avail
){ //Check for LegacyInterop elements
4421 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4422 else if (avl
== 3500)
4423 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4424 else if (avl
== 15500)
4425 activity_name
= SIPE_STATUS_ID_AWAY
;
4426 else if (avl
== 6500)
4427 activity_name
= SIPE_STATUS_ID_BUSY
;
4428 else if (avl
== 12500)
4429 activity_name
= SIPE_STATUS_ID_BRB
;
4434 if(activity_name
== NULL
){
4436 activity_name
= SIPE_STATUS_ID_AWAY
;
4437 else if (act
<= 150)
4438 activity_name
= SIPE_STATUS_ID_LUNCH
;
4439 else if (act
<= 300)
4440 activity_name
= SIPE_STATUS_ID_BRB
;
4441 else if (act
<= 400)
4442 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4443 else if (act
<= 500)
4444 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4445 else if (act
<= 600)
4446 activity_name
= SIPE_STATUS_ID_BUSY
;
4448 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4451 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4454 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4457 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
4458 sbuddy
->annotation
= NULL
;
4459 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
4461 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
4462 sbuddy
->device_name
= NULL
;
4463 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
4466 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
4467 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
4469 xmlnode_free(xn_presentity
);
4473 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4475 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4477 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
4479 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
4480 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
4482 const char *content
= msg
->body
;
4483 unsigned length
= msg
->bodylen
;
4484 PurpleMimeDocument
*mime
= NULL
;
4486 if (strstr(ctype
, "multipart"))
4488 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4489 const char *content_type
;
4491 mime
= purple_mime_document_parse(doc
);
4492 parts
= purple_mime_document_get_parts(mime
);
4494 content
= purple_mime_part_get_data(parts
->data
);
4495 length
= purple_mime_part_get_length(parts
->data
);
4496 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
4497 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
4499 process_incoming_notify_rlmi_resub(sip
, content
, length
);
4501 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
4503 process_incoming_notify_msrtc(sip
, content
, length
);
4507 process_incoming_notify_rlmi(sip
, content
, length
);
4509 parts
= parts
->next
;
4515 purple_mime_document_free(mime
);
4518 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4520 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
4522 else if(strstr(ctype
, "application/rlmi+xml"))
4524 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
4527 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4529 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
4533 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
4537 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
4539 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4540 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4542 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
4545 strstr(ctype
, "multipart") &&
4546 (strstr(ctype
, "application/rlmi+xml") ||
4547 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4548 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4549 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
4550 GList
*parts
= purple_mime_document_get_parts(mime
);
4551 GSList
*buddies
= NULL
;
4552 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4555 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
4556 purple_mime_part_get_length(parts
->data
));
4557 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
4559 buddies
= g_slist_append(buddies
, uri
);
4562 parts
= parts
->next
;
4565 if (mime
) purple_mime_document_free(mime
);
4567 payload
->host
= who
;
4568 payload
->buddies
= buddies
;
4569 sipe_schedule_action(action_name
, timeout
,
4570 sipe_subscribe_presence_batched_routed
,
4571 sipe_subscribe_presence_batched_routed_free
,
4573 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
4576 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, who
);
4577 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
4579 g_free(action_name
);
4583 * Dispatcher for all incoming subscription information
4584 * whether it comes from NOTIFY, BENOTIFY requests or
4585 * piggy-backed to subscription's OK responce.
4587 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4588 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4590 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
4592 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4593 gchar
*event
= sipmsg_find_header(msg
, "Event");
4594 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4597 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
4598 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n\n", subscription_state
? subscription_state
: "");
4600 /* implicit subscriptions */
4601 if (content_type
&& purple_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4602 sipe_process_imdn(sip
, msg
);
4607 const gchar
*expires_header
;
4608 expires_header
= sipmsg_find_header(msg
, "Expires");
4609 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4610 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout
);
4611 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
4614 /* for one off subscriptions (send with Expire: 0) */
4615 if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
4617 sipe_process_provisioning_v2(sip
, msg
);
4620 if (!subscription_state
|| strstr(subscription_state
, "active"))
4622 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
4624 sipe_process_presence(sip
, msg
);
4626 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
4628 sipe_process_roaming_contacts(sip
, msg
);
4630 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
4632 sipe_process_roaming_self(sip
, msg
);
4634 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
4636 sipe_process_roaming_acl(sip
, msg
);
4638 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
4640 sipe_process_presence_wpending(sip
, msg
);
4642 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
4644 sipe_process_conference(sip
, msg
);
4648 //The server sends a (BE)NOTIFY with the status 'terminated'
4649 if (request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
4650 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4651 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
4655 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
4656 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
4657 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
4659 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
4660 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
4661 g_free(action_name);
4663 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
4664 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
4666 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
4667 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
4668 g_free(action_name);
4671 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
4672 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4674 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4675 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
4676 g_free(action_name
);
4678 else if (!g_ascii_strcasecmp(event
, "presence") &&
4679 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4681 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4682 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4683 if(sip
->batched_support
) {
4684 gchar
*my_self
= sip_uri_self(sip
);
4685 if(!g_ascii_strcasecmp(who
, my_self
)){
4686 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_batched
, NULL
, sip
, NULL
);
4687 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout
);
4688 g_free(who
); /* unused */
4691 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
4696 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, who
);
4697 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
,timeout
);
4699 g_free(action_name
);
4700 /* "who" will be freed by the action we just scheduled */
4704 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
4706 sipe_process_registration_notify(sip
, msg
);
4709 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
4710 if (request
&& !benotify
)
4712 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4719 static gchar* gen_xpidf(struct sipe_account_data *sip)
4721 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4723 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
4724 "<display name=\"sip:%s\"/>\r\n"
4725 "<atom id=\"1234\">\r\n"
4726 "<address uri=\"sip:%s\">\r\n"
4727 "<status status=\"%s\"/>\r\n"
4740 static gchar* gen_pidf(struct sipe_account_data *sip)
4742 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4743 "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" xmlns:ep=\"urn:ietf:params:xml:ns:pidf:status:rpid-status\" xmlns:ci=\"urn:ietf:params:xml:ns:pidf:cipid\" entity=\"sip:%s\">\r\n"
4744 "<tuple id=\"0\">\r\n"
4746 "<basic>open</basic>\r\n"
4747 "<ep:activities>\r\n"
4748 " <ep:activity>%s</ep:activity>\r\n"
4752 "<ci:display-name>%s</ci:display-name>\r\n"
4761 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
4763 int availability
= 300; // online
4764 int activity
= 400; // Available
4767 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
4769 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4771 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4773 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4775 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4777 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4779 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
4780 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
4781 availability
= 0; // offline
4784 activity
= 400; // available
4787 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
4788 //@TODO: send user data - state; add hostname in upper case
4789 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
4790 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
4796 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4797 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
4799 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4800 if (msg
->response
== 200) {
4801 sip
->status_version
= 0;
4802 send_presence_status(sip
);
4808 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4809 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
4811 if (msg
->response
== 409) {
4812 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4813 // TODO need to parse the version #'s?
4814 gchar
*uri
= sip_uri_self(sip
);
4815 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
4819 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
4821 tmp
= get_contact(sip
);
4822 hdr
= g_strdup_printf("Contact: %s\r\n"
4823 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4825 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
4835 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
4842 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
4843 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4845 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
4847 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4849 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4851 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4853 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4855 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4858 // Offline or invisible
4862 uri
= sip_uri_self(sip
);
4863 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4864 sip
->status_version
, code
,
4865 sip
->status_version
, code
,
4866 sip
->status_version
, note
? note
: "",
4867 sip
->status_version
, note
? note
: "",
4868 sip
->status_version
, note
? note
: ""
4870 sip
->status_version
++;
4872 tmp
= get_contact(sip
);
4873 hdr
= g_strdup_printf("Contact: %s\r\n"
4874 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4876 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4884 static void send_presence_status(struct sipe_account_data
*sip
)
4886 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4888 if (!status
) return;
4890 note
= purple_status_get_attr_string(status
, "message");
4892 if(sip
->msrtc_event_categories
){
4893 send_presence_category_publish(sip
, note
);
4895 send_presence_soap(sip
, note
);
4899 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4901 gboolean found
= FALSE
;
4902 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4903 if (msg
->response
== 0) { /* request */
4904 if (!strcmp(msg
->method
, "MESSAGE")) {
4905 process_incoming_message(sip
, msg
);
4907 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4908 purple_debug_info("sipe","send->process_incoming_notify\n");
4909 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4911 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4912 purple_debug_info("sipe","send->process_incoming_benotify\n");
4913 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4915 } else if (!strcmp(msg
->method
, "INVITE")) {
4916 process_incoming_invite(sip
, msg
);
4918 } else if (!strcmp(msg
->method
, "REFER")) {
4919 process_incoming_refer(sip
, msg
);
4921 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4922 process_incoming_options(sip
, msg
);
4924 } else if (!strcmp(msg
->method
, "INFO")) {
4925 process_incoming_info(sip
, msg
);
4927 } else if (!strcmp(msg
->method
, "ACK")) {
4928 // ACK's don't need any response
4930 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4931 // LCS 2005 sends us these - just respond 200 OK
4933 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4934 } else if (!strcmp(msg
->method
, "BYE")) {
4935 process_incoming_bye(sip
, msg
);
4938 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4940 } else { /* response */
4941 struct transaction
*trans
= transactions_find(sip
, msg
);
4943 if (msg
->response
== 407) {
4944 gchar
*resend
, *auth
, *ptmp
;
4946 if (sip
->proxy
.retries
> 30) return;
4947 sip
->proxy
.retries
++;
4948 /* do proxy authentication */
4950 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
4952 fill_auth(ptmp
, &sip
->proxy
);
4953 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4954 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4955 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4957 resend
= sipmsg_to_string(trans
->msg
);
4958 /* resend request */
4959 sendout_pkt(sip
->gc
, resend
);
4962 if (msg
->response
== 100 || msg
->response
== 180) {
4963 /* ignore provisional response */
4964 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
4966 sip
->proxy
.retries
= 0;
4967 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
4968 if (msg
->response
== 401)
4970 sip
->registrar
.retries
++;
4974 sip
->registrar
.retries
= 0;
4976 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
4978 if (msg
->response
== 401) {
4979 gchar
*resend
, *auth
, *ptmp
;
4981 if (sip
->registrar
.retries
> 4) return;
4982 sip
->registrar
.retries
++;
4985 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4987 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
4990 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4994 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
4996 fill_auth(ptmp
, &sip
->registrar
);
4997 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
4998 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4999 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
5001 //sipmsg_remove_header_now(trans->msg, "Authorization");
5002 //sipmsg_add_header(trans->msg, "Authorization", auth);
5004 resend
= sipmsg_to_string(trans
->msg
);
5005 /* resend request */
5006 sendout_pkt(sip
->gc
, resend
);
5011 if (trans
->callback
) {
5012 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
5013 /* call the callback to process response*/
5014 (trans
->callback
)(sip
, msg
, trans
);
5016 /* Not sure if this is needed or what needs to be done
5017 but transactions seem to be removed prematurely so
5018 this only removes them if the response is 200 OK */
5019 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
5020 /*Has a bug and it's unneccesary*/
5021 /*transactions_remove(sip, trans);*/
5027 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
5031 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
5035 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
5043 /* according to the RFC remove CRLF at the beginning */
5044 while (*cur
== '\r' || *cur
== '\n') {
5047 if (cur
!= conn
->inbuf
) {
5048 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
5049 conn
->inbufused
= strlen(conn
->inbuf
);
5052 /* Received a full Header? */
5053 sip
->processing_input
= TRUE
;
5054 while (sip
->processing_input
&&
5055 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
5056 time_t currtime
= time(NULL
);
5059 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
5060 msg
= sipmsg_parse_header(conn
->inbuf
);
5063 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
5064 if (restlen
>= msg
->bodylen
) {
5065 dummy
= g_malloc(msg
->bodylen
+ 1);
5066 memcpy(dummy
, cur
, msg
->bodylen
);
5067 dummy
[msg
->bodylen
] = '\0';
5069 cur
+= msg
->bodylen
;
5070 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
5071 conn
->inbufused
= strlen(conn
->inbuf
);
5073 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
5074 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
5080 purple_debug_info("sipe", "body:\n%s", msg->body);
5083 // Verify the signature before processing it
5084 if (sip
->registrar
.gssapi_context
) {
5085 struct sipmsg_breakdown msgbd
;
5086 gchar
*signature_input_str
;
5089 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
5090 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
5092 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
5094 if (rspauth
!= NULL
) {
5095 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
5096 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
5097 process_input_message(sip
, msg
);
5099 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
5100 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
5101 sip
->gc
->wants_to_die
= TRUE
;
5103 } else if (msg
->response
== 401) {
5104 purple_connection_error(sip
->gc
, _("Wrong Password"));
5105 sip
->gc
->wants_to_die
= TRUE
;
5107 g_free(signature_input_str
);
5110 sipmsg_breakdown_free(&msgbd
);
5112 process_input_message(sip
, msg
);
5119 static void sipe_udp_process(gpointer data
, gint source
,
5120 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
5122 PurpleConnection
*gc
= data
;
5123 struct sipe_account_data
*sip
= gc
->proto_data
;
5128 static char buffer
[65536];
5129 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
5131 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
5132 msg
= sipmsg_parse_msg(buffer
);
5133 if (msg
) process_input_message(sip
, msg
);
5137 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
5139 struct sipe_account_data
*sip
= gc
->proto_data
;
5140 PurpleSslConnection
*gsc
= sip
->gsc
;
5142 purple_debug_error("sipe", "%s",debug
);
5143 purple_connection_error(gc
, msg
);
5145 /* Invalidate this connection. Next send will open a new one */
5147 connection_remove(sip
, gsc
->fd
);
5148 purple_ssl_close(gsc
);
5154 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
5155 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5157 PurpleConnection
*gc
= data
;
5158 struct sipe_account_data
*sip
;
5159 struct sip_connection
*conn
;
5161 gboolean firstread
= TRUE
;
5163 /* NOTE: This check *IS* necessary */
5164 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
5165 purple_ssl_close(gsc
);
5169 sip
= gc
->proto_data
;
5170 conn
= connection_find(sip
, gsc
->fd
);
5172 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
5173 gc
->wants_to_die
= TRUE
;
5174 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
5178 /* Read all available data from the SSL connection */
5180 /* Increase input buffer size as needed */
5181 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5182 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5183 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5184 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
5187 /* Try to read as much as there is space left in the buffer */
5188 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
5189 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
5191 if (len
< 0 && errno
== EAGAIN
) {
5192 /* Try again later */
5194 } else if (len
< 0) {
5195 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
5197 } else if (firstread
&& (len
== 0)) {
5198 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
5202 conn
->inbufused
+= len
;
5205 /* Equivalence indicates that there is possibly more data to read */
5206 } while (len
== readlen
);
5208 conn
->inbuf
[conn
->inbufused
] = '\0';
5209 process_input(sip
, conn
);
5213 static void sipe_input_cb(gpointer data
, gint source
,
5214 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5216 PurpleConnection
*gc
= data
;
5217 struct sipe_account_data
*sip
= gc
->proto_data
;
5219 struct sip_connection
*conn
= connection_find(sip
, source
);
5221 purple_debug_error("sipe", "Connection not found!\n");
5225 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5226 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5227 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5230 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
5232 if (len
< 0 && errno
== EAGAIN
)
5234 else if (len
<= 0) {
5235 purple_debug_info("sipe", "sipe_input_cb: read error\n");
5236 connection_remove(sip
, source
);
5237 if (sip
->fd
== source
) sip
->fd
= -1;
5241 conn
->inbufused
+= len
;
5242 conn
->inbuf
[conn
->inbufused
] = '\0';
5244 process_input(sip
, conn
);
5247 /* Callback for new connections on incoming TCP port */
5248 static void sipe_newconn_cb(gpointer data
, gint source
,
5249 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5251 PurpleConnection
*gc
= data
;
5252 struct sipe_account_data
*sip
= gc
->proto_data
;
5253 struct sip_connection
*conn
;
5255 int newfd
= accept(source
, NULL
, NULL
);
5257 conn
= connection_create(sip
, newfd
);
5259 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5262 static void login_cb(gpointer data
, gint source
,
5263 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
5265 PurpleConnection
*gc
= data
;
5266 struct sipe_account_data
*sip
;
5267 struct sip_connection
*conn
;
5269 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5277 purple_connection_error(gc
, _("Could not connect"));
5281 sip
= gc
->proto_data
;
5283 sip
->last_keepalive
= time(NULL
);
5285 conn
= connection_create(sip
, source
);
5289 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5292 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
5293 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5295 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
5296 if (sip
== NULL
) return;
5301 static guint
sipe_ht_hash_nick(const char *nick
)
5303 char *lc
= g_utf8_strdown(nick
, -1);
5304 guint bucket
= g_str_hash(lc
);
5310 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5312 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
5315 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
5317 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5319 sip
->listen_data
= NULL
;
5321 if (listenfd
== -1) {
5322 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5328 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
5329 sip
->listenfd
= sip
->fd
;
5331 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
5333 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
5337 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
5338 SIPE_UNUSED_PARAMETER
const char *error_message
)
5340 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5342 sip
->query_data
= NULL
;
5344 if (!hosts
|| !hosts
->data
) {
5345 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
5349 hosts
= g_slist_remove(hosts
, hosts
->data
);
5350 g_free(sip
->serveraddr
);
5351 sip
->serveraddr
= hosts
->data
;
5352 hosts
= g_slist_remove(hosts
, hosts
->data
);
5354 hosts
= g_slist_remove(hosts
, hosts
->data
);
5355 g_free(hosts
->data
);
5356 hosts
= g_slist_remove(hosts
, hosts
->data
);
5359 /* create socket for incoming connections */
5360 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
5361 sipe_udp_host_resolved_listen_cb
, sip
);
5362 if (sip
->listen_data
== NULL
) {
5363 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5368 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
5369 PurpleSslErrorType error
,
5372 PurpleConnection
*gc
= data
;
5373 struct sipe_account_data
*sip
;
5375 /* If the connection is already disconnected, we don't need to do anything else */
5376 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5379 sip
= gc
->proto_data
;
5384 case PURPLE_SSL_CONNECT_FAILED
:
5385 purple_connection_error(gc
, _("Connection Failed"));
5387 case PURPLE_SSL_HANDSHAKE_FAILED
:
5388 purple_connection_error(gc
, _("SSL Handshake Failed"));
5390 case PURPLE_SSL_CERTIFICATE_INVALID
:
5391 purple_connection_error(gc
, _("SSL Certificate Invalid"));
5397 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
5399 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5400 PurpleProxyConnectData
*connect_data
;
5402 sip
->listen_data
= NULL
;
5404 sip
->listenfd
= listenfd
;
5405 if (sip
->listenfd
== -1) {
5406 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5410 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
5411 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
5412 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
5413 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
5414 sipe_newconn_cb
, sip
->gc
);
5415 purple_debug_info("sipe", "connecting to %s port %d\n",
5416 sip
->realhostname
, sip
->realport
);
5417 /* open tcp connection to the server */
5418 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
5419 sip
->realport
, login_cb
, sip
->gc
);
5421 if (connect_data
== NULL
) {
5422 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
5426 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
5428 PurpleAccount
*account
= sip
->account
;
5429 PurpleConnection
*gc
= sip
->gc
;
5432 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
5435 sip
->realhostname
= hostname
;
5436 sip
->realport
= port
;
5438 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
5441 /* TODO: is there a good default grow size? */
5442 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
5443 sip
->txbuf
= purple_circ_buffer_new(0);
5445 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
5447 if (!purple_ssl_is_supported()) {
5448 gc
->wants_to_die
= TRUE
;
5449 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
5453 purple_debug_info("sipe", "using SSL\n");
5455 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
5456 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
5457 if (sip
->gsc
== NULL
) {
5458 purple_connection_error(gc
, _("Could not create SSL context"));
5461 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
5463 purple_debug_info("sipe", "using UDP\n");
5465 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
5466 if (sip
->query_data
== NULL
) {
5467 purple_connection_error(gc
, _("Could not resolve hostname"));
5471 purple_debug_info("sipe", "using TCP\n");
5472 /* create socket for incoming connections */
5473 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
5474 sipe_tcp_connect_listen_cb
, sip
);
5475 if (sip
->listen_data
== NULL
) {
5476 purple_connection_error(gc
, _("Could not create listen socket"));
5482 /* Service list for autodection */
5483 static const struct sipe_service_data service_autodetect
[] = {
5484 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5485 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5486 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5487 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5491 /* Service list for SSL/TLS */
5492 static const struct sipe_service_data service_tls
[] = {
5493 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5494 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5498 /* Service list for TCP */
5499 static const struct sipe_service_data service_tcp
[] = {
5500 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5501 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5505 /* Service list for UDP */
5506 static const struct sipe_service_data service_udp
[] = {
5507 { "sip", "udp", SIPE_TRANSPORT_UDP
},
5511 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
5512 static void resolve_next_service(struct sipe_account_data
*sip
,
5513 const struct sipe_service_data
*start
)
5516 sip
->service_data
= start
;
5518 sip
->service_data
++;
5519 if (sip
->service_data
->service
== NULL
) {
5521 /* Try connecting to the SIP hostname directly */
5522 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
5523 if (sip
->auto_transport
) {
5524 // If SSL is supported, default to using it; OCS servers aren't configured
5525 // by default to accept TCP
5526 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
5527 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
5528 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
5531 hostname
= g_strdup(sip
->sipdomain
);
5532 create_connection(sip
, hostname
, 0);
5537 /* Try to resolve next service */
5538 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
5539 sip
->service_data
->transport
,
5544 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
5546 struct sipe_account_data
*sip
= data
;
5548 sip
->srv_query_data
= NULL
;
5550 /* find the host to connect to */
5552 gchar
*hostname
= g_strdup(resp
->hostname
);
5553 int port
= resp
->port
;
5554 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
5558 sip
->transport
= sip
->service_data
->type
;
5560 create_connection(sip
, hostname
, port
);
5562 resolve_next_service(sip
, NULL
);
5566 static void sipe_login(PurpleAccount
*account
)
5568 PurpleConnection
*gc
;
5569 struct sipe_account_data
*sip
;
5570 gchar
**signinname_login
, **userserver
;
5571 const char *transport
;
5573 const char *username
= purple_account_get_username(account
);
5574 gc
= purple_account_get_connection(account
);
5576 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
5578 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
5579 gc
->wants_to_die
= TRUE
;
5580 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
5584 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
5585 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
5586 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
5588 sip
->account
= account
;
5589 sip
->reregister_set
= FALSE
;
5590 sip
->reauthenticate_set
= FALSE
;
5591 sip
->subscribed
= FALSE
;
5592 sip
->subscribed_buddies
= FALSE
;
5594 signinname_login
= g_strsplit(username
, ",", 2);
5595 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
5597 if (!strstr(signinname_login
[0], "@") || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
5598 g_strfreev(signinname_login
);
5599 gc
->wants_to_die
= TRUE
;
5600 purple_connection_error(gc
, _("Username should be valid SIP URI\nExample: user@company.com"));
5604 if (signinname_login
[1] && strcmp(signinname_login
[1], "")) {
5605 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
5606 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
5607 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
5608 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) :
5609 (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
5610 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n", sip
->authdomain
, sip
->authuser
);
5611 g_strfreev(domain_user
);
5614 userserver
= g_strsplit(signinname_login
[0], "@", 2);
5615 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
5616 purple_connection_set_display_name(gc
, userserver
[0]);
5617 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
5618 sip
->sipdomain
= g_strdup(userserver
[1]);
5619 g_strfreev(userserver
);
5620 g_strfreev(signinname_login
);
5622 if (strpbrk(sip
->username
, " \t\v\r\n") != NULL
) {
5623 gc
->wants_to_die
= TRUE
;
5624 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
5628 sip
->password
= g_strdup(purple_connection_get_password(gc
));
5630 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5632 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
5634 /* TODO: Set the status correctly. */
5635 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
5637 sip
->auto_transport
= FALSE
;
5638 transport
= purple_account_get_string(account
, "transport", "auto");
5639 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
5640 if (userserver
[0]) {
5641 /* Use user specified server[:port] */
5645 port
= atoi(userserver
[1]);
5647 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
5648 userserver
[0], port
);
5650 if (strcmp(transport
, "auto") == 0) {
5651 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
5652 } else if (strcmp(transport
, "tls") == 0) {
5653 sip
->transport
= SIPE_TRANSPORT_TLS
;
5654 } else if (strcmp(transport
, "tcp") == 0) {
5655 sip
->transport
= SIPE_TRANSPORT_TCP
;
5657 sip
->transport
= SIPE_TRANSPORT_UDP
;
5660 create_connection(sip
, g_strdup(userserver
[0]), port
);
5662 /* Server auto-discovery */
5663 if (strcmp(transport
, "auto") == 0) {
5664 sip
->auto_transport
= TRUE
;
5665 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
5666 } else if (strcmp(transport
, "tls") == 0) {
5667 resolve_next_service(sip
, service_tls
);
5668 } else if (strcmp(transport
, "tcp") == 0) {
5669 resolve_next_service(sip
, service_tcp
);
5671 resolve_next_service(sip
, service_udp
);
5674 g_strfreev(userserver
);
5677 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
5679 connection_free_all(sip
);
5684 if (sip
->query_data
!= NULL
)
5685 purple_dnsquery_destroy(sip
->query_data
);
5686 sip
->query_data
= NULL
;
5688 if (sip
->srv_query_data
!= NULL
)
5689 purple_srv_cancel(sip
->srv_query_data
);
5690 sip
->srv_query_data
= NULL
;
5692 if (sip
->listen_data
!= NULL
)
5693 purple_network_listen_cancel(sip
->listen_data
);
5694 sip
->listen_data
= NULL
;
5696 if (sip
->gsc
!= NULL
)
5697 purple_ssl_close(sip
->gsc
);
5700 sipe_auth_free(&sip
->registrar
);
5701 sipe_auth_free(&sip
->proxy
);
5704 purple_circ_buffer_destroy(sip
->txbuf
);
5707 g_free(sip
->realhostname
);
5708 sip
->realhostname
= NULL
;
5711 purple_input_remove(sip
->listenpa
);
5713 if (sip
->tx_handler
)
5714 purple_input_remove(sip
->tx_handler
);
5715 sip
->tx_handler
= 0;
5716 if (sip
->resendtimeout
)
5717 purple_timeout_remove(sip
->resendtimeout
);
5718 sip
->resendtimeout
= 0;
5719 if (sip
->timeouts
) {
5720 GSList
*entry
= sip
->timeouts
;
5722 struct scheduled_action
*sched_action
= entry
->data
;
5723 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
5724 purple_timeout_remove(sched_action
->timeout_handler
);
5725 if (sched_action
->destroy
) {
5726 (*sched_action
->destroy
)(sched_action
->payload
);
5728 g_free(sched_action
->name
);
5729 g_free(sched_action
);
5730 entry
= entry
->next
;
5733 g_slist_free(sip
->timeouts
);
5735 if (sip
->allow_events
) {
5736 GSList
*entry
= sip
->allow_events
;
5738 g_free(entry
->data
);
5739 entry
= entry
->next
;
5742 g_slist_free(sip
->allow_events
);
5744 if (sip
->containers
) {
5745 GSList
*entry
= sip
->containers
;
5747 free_container((struct sipe_container
*)entry
->data
);
5748 entry
= entry
->next
;
5751 g_slist_free(sip
->containers
);
5754 g_free(sip
->contact
);
5755 sip
->contact
= NULL
;
5757 g_free(sip
->regcallid
);
5758 sip
->regcallid
= NULL
;
5760 if (sip
->serveraddr
)
5761 g_free(sip
->serveraddr
);
5762 sip
->serveraddr
= NULL
;
5764 if (sip
->focus_factory_uri
)
5765 g_free(sip
->focus_factory_uri
);
5766 sip
->focus_factory_uri
= NULL
;
5769 sip
->processing_input
= FALSE
;
5773 * A callback for g_hash_table_foreach_remove
5775 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5776 SIPE_UNUSED_PARAMETER gpointer user_data
)
5778 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5780 /* We must return TRUE as the key/value have already been deleted */
5784 static void sipe_close(PurpleConnection
*gc
)
5786 struct sipe_account_data
*sip
= gc
->proto_data
;
5789 /* leave all conversations */
5790 sipe_session_close_all(sip
);
5791 sipe_session_remove_all(sip
);
5794 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
5795 do_register_exp(sip
, 0);
5798 sipe_connection_cleanup(sip
);
5799 g_free(sip
->sipdomain
);
5800 g_free(sip
->username
);
5801 g_free(sip
->password
);
5802 g_free(sip
->authdomain
);
5803 g_free(sip
->authuser
);
5804 g_free(sip
->status
);
5806 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
5807 g_hash_table_destroy(sip
->buddies
);
5809 g_free(gc
->proto_data
);
5810 gc
->proto_data
= NULL
;
5813 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5814 SIPE_UNUSED_PARAMETER
void *user_data
)
5816 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5817 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5818 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5820 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5821 purple_conversation_present(conv
);
5825 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
5826 SIPE_UNUSED_PARAMETER
void *user_data
)
5829 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5830 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5833 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5834 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
5836 PurpleNotifySearchResults
*results
;
5837 PurpleNotifySearchColumn
*column
;
5838 xmlnode
*searchResults
;
5840 int match_count
= 0;
5841 gboolean more
= FALSE
;
5844 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
5846 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5847 if (!searchResults
) {
5848 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5852 results
= purple_notify_searchresults_new();
5854 if (results
== NULL
) {
5855 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5856 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
5858 xmlnode_free(searchResults
);
5862 column
= purple_notify_searchresults_column_new(_("User Name"));
5863 purple_notify_searchresults_column_add(results
, column
);
5865 column
= purple_notify_searchresults_column_new(_("Name"));
5866 purple_notify_searchresults_column_add(results
, column
);
5868 column
= purple_notify_searchresults_column_new(_("Company"));
5869 purple_notify_searchresults_column_add(results
, column
);
5871 column
= purple_notify_searchresults_column_new(_("Country"));
5872 purple_notify_searchresults_column_add(results
, column
);
5874 column
= purple_notify_searchresults_column_new(_("Email"));
5875 purple_notify_searchresults_column_add(results
, column
);
5877 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
5880 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
5881 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5882 g_strfreev(uri_parts
);
5884 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
5885 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
5886 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
5887 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
5889 purple_notify_searchresults_row_add(results
, row
);
5893 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
5894 char *data
= xmlnode_get_data_unescaped(mrow
);
5895 more
= (g_strcasecmp(data
, "true") == 0);
5899 secondary
= g_strdup_printf(
5900 dngettext(GETTEXT_PACKAGE
,
5901 "Found %d contact%s:",
5902 "Found %d contacts%s:", match_count
),
5903 match_count
, more
? _(" (more matched your query)") : "");
5905 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5906 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5907 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5910 xmlnode_free(searchResults
);
5914 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5916 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5917 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5921 PurpleRequestField
*field
= entries
->data
;
5922 const char *id
= purple_request_field_get_id(field
);
5923 const char *value
= purple_request_field_string_get_value(field
);
5925 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
5927 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5928 } while ((entries
= g_list_next(entries
)) != NULL
);
5932 gchar
*query
= g_strjoinv(NULL
, attrs
);
5933 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5934 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
5935 send_soap_request_with_cb(gc
->proto_data
, body
,
5936 (TransCallback
) process_search_contact_response
, NULL
);
5944 static void sipe_show_find_contact(PurplePluginAction
*action
)
5946 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
5947 PurpleRequestFields
*fields
;
5948 PurpleRequestFieldGroup
*group
;
5949 PurpleRequestField
*field
;
5951 fields
= purple_request_fields_new();
5952 group
= purple_request_field_group_new(NULL
);
5953 purple_request_fields_add_group(fields
, group
);
5955 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
5956 purple_request_field_group_add_field(group
, field
);
5957 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
5958 purple_request_field_group_add_field(group
, field
);
5959 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
5960 purple_request_field_group_add_field(group
, field
);
5961 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
5962 purple_request_field_group_add_field(group
, field
);
5964 purple_request_fields(gc
,
5966 _("Search for a Contact"),
5967 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5969 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
5971 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
5974 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
5975 SIPE_UNUSED_PARAMETER gpointer context
)
5978 PurplePluginAction
*act
;
5980 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
5981 menu
= g_list_prepend(menu
, act
);
5983 menu
= g_list_reverse(menu
);
5988 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
5992 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
5998 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
6004 static char *sipe_status_text(PurpleBuddy
*buddy
)
6006 struct sipe_account_data
*sip
;
6007 struct sipe_buddy
*sbuddy
;
6010 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
6011 if (sip
) //happens on pidgin exit
6013 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
6014 if (sbuddy
&& sbuddy
->annotation
)
6016 text
= g_strdup(sbuddy
->annotation
);
6023 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
6025 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
6026 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
6027 struct sipe_account_data
*sip
;
6028 struct sipe_buddy
*sbuddy
;
6029 char *annotation
= NULL
;
6031 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
6032 if (sip
) //happens on pidgin exit
6034 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
6037 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
6042 if (purple_presence_is_online(presence
))
6044 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
6049 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
6056 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
6059 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
6060 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
6064 static PurpleBuddy
*
6065 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
6068 const gchar
*server_alias
, *email
;
6069 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
6071 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
6073 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
6075 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
6077 purple_blist_server_alias_buddy(clone
, server_alias
);
6080 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6082 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
6085 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6087 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6092 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6094 PurpleBuddy
*buddy
, *b
;
6095 PurpleConnection
*gc
;
6096 PurpleGroup
* group
= purple_find_group(group_name
);
6098 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6100 buddy
= (PurpleBuddy
*)node
;
6102 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
6103 gc
= purple_account_get_connection(buddy
->account
);
6105 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6107 b
= purple_blist_add_buddy_clone(group
, buddy
);
6110 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
6114 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6116 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6118 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6120 /* 2007+ conference */
6121 if (sip
->msrtc_event_categories
)
6123 sipe_conf_add(sip
, buddy
->name
);
6125 else /* 2005- multiparty chat */
6127 gchar
*self
= sip_uri_self(sip
);
6128 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
6129 struct sip_session
*session
;
6131 session
= sipe_session_add_chat(sip
);
6132 session
->roster_manager
= g_strdup(self
);
6134 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, g_strdup(chat_name
));
6135 session
->chat_name
= g_strdup(chat_name
);
6136 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
6137 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
6138 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
6146 sipe_is_election_finished(struct sip_session
*session
)
6148 gboolean res
= TRUE
;
6150 SIPE_DIALOG_FOREACH
{
6151 if (dialog
->election_vote
== 0) {
6155 } SIPE_DIALOG_FOREACH_END
;
6158 session
->is_voting_in_progress
= FALSE
;
6164 sipe_election_start(struct sipe_account_data
*sip
,
6165 struct sip_session
*session
)
6167 int election_timeout
;
6169 if (session
->is_voting_in_progress
) {
6170 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
6173 session
->is_voting_in_progress
= TRUE
;
6175 session
->bid
= rand();
6177 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
6179 SIPE_DIALOG_FOREACH
{
6180 /* reset election_vote for each chat participant */
6181 dialog
->election_vote
= 0;
6183 /* send RequestRM to each chat participant*/
6184 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
6185 } SIPE_DIALOG_FOREACH_END
;
6187 election_timeout
= 15; /* sec */
6188 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
6192 * @param who a URI to whom to invite to chat
6195 sipe_invite_to_chat(struct sipe_account_data
*sip
,
6196 struct sip_session
*session
,
6200 if (session
->focus_uri
)
6202 sipe_invite_conf(sip
, session
, who
);
6204 else /* a multi-party chat */
6206 gchar
*self
= sip_uri_self(sip
);
6207 if (session
->roster_manager
) {
6208 if (!strcmp(session
->roster_manager
, self
)) {
6209 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
6211 sipe_refer(sip
, session
, who
);
6214 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: no RM available\n");
6216 session
->pending_invite_queue
= slist_insert_unique_sorted(
6217 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
6219 sipe_election_start(sip
, session
);
6226 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
6227 struct sip_session
*session
)
6230 GSList
*entry
= session
->pending_invite_queue
;
6233 invitee
= entry
->data
;
6234 sipe_invite_to_chat(sip
, session
, invitee
);
6235 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
6241 sipe_election_result(struct sipe_account_data
*sip
,
6244 struct sip_session
*session
= (struct sip_session
*)sess
;
6246 gboolean has_won
= TRUE
;
6248 if (session
->roster_manager
) {
6249 purple_debug_info("sipe",
6250 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
6254 session
->is_voting_in_progress
= FALSE
;
6256 SIPE_DIALOG_FOREACH
{
6257 if (dialog
->election_vote
< 0) {
6259 rival
= dialog
->with
;
6262 } SIPE_DIALOG_FOREACH_END
;
6265 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
6267 session
->roster_manager
= sip_uri_self(sip
);
6269 SIPE_DIALOG_FOREACH
{
6270 /* send SetRM to each chat participant*/
6271 sipe_send_election_set_rm(sip
, dialog
);
6272 } SIPE_DIALOG_FOREACH_END
;
6274 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
6278 sipe_process_pending_invite_queue(sip
, session
);
6282 * For 2007+ conference only.
6285 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6287 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6288 struct sip_session
*session
;
6290 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
6291 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_name=%s\n", chat_name
);
6293 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6295 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
6299 * For 2007+ conference only.
6302 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6304 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6305 struct sip_session
*session
;
6307 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
6308 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_name=%s\n", chat_name
);
6310 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6312 sipe_conf_delete_user(sip
, session
, buddy
->name
);
6316 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6318 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6319 struct sip_session
*session
;
6321 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6322 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: chat_name=%s\n", chat_name
);
6324 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6326 sipe_invite_to_chat(sip
, session
, buddy
->name
);
6330 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6333 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
6335 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6338 char *mailto
= g_strdup_printf("mailto:%s", email
);
6339 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
6343 char *const parmList
[] = {mailto
, NULL
};
6344 if ((pid
= fork()) == -1)
6346 purple_debug_info("sipe", "fork() error\n");
6350 execvp("xdg-email", parmList
);
6351 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
6359 //@TODO resolve env variable %WINDIR% first
6360 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
6363 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
6372 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
6377 * A menu which appear when right-clicking on buddy in contact list.
6380 sipe_buddy_menu(PurpleBuddy
*buddy
)
6382 PurpleBlistNode
*g_node
;
6383 PurpleGroup
*group
, *gr_parent
;
6384 PurpleMenuAction
*act
;
6386 GList
*menu_groups
= NULL
;
6387 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6388 gchar
*self
= sip_uri_self(sip
);
6390 SIPE_SESSION_FOREACH
{
6391 if (strcmp(self
, buddy
->name
) && session
->chat_name
&& session
->conv
)
6393 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
6395 PurpleConvChatBuddyFlags flags
;
6396 PurpleConvChatBuddyFlags flags_us
;
6398 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
6399 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
6400 if (session
->focus_uri
6401 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
6402 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6404 gchar
*label
= g_strdup_printf(_("Make Leader of '%s'"), session
->chat_name
);
6405 act
= purple_menu_action_new(label
,
6406 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
6407 g_strdup(session
->chat_name
), NULL
);
6409 menu
= g_list_prepend(menu
, act
);
6412 if (session
->focus_uri
6413 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6415 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_name
);
6416 act
= purple_menu_action_new(label
,
6417 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
6418 g_strdup(session
->chat_name
), NULL
);
6420 menu
= g_list_prepend(menu
, act
);
6425 if (!session
->focus_uri
6426 || (session
->focus_uri
&& !session
->locked
))
6428 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_name
);
6429 act
= purple_menu_action_new(label
,
6430 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6431 g_strdup(session
->chat_name
), NULL
);
6433 menu
= g_list_prepend(menu
, act
);
6437 } SIPE_SESSION_FOREACH_END
;
6439 act
= purple_menu_action_new(_("New Chat"),
6440 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6442 menu
= g_list_prepend(menu
, act
);
6444 act
= purple_menu_action_new(_("Send Email..."),
6445 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6447 menu
= g_list_prepend(menu
, act
);
6449 gr_parent
= purple_buddy_get_group(buddy
);
6450 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6451 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6454 group
= (PurpleGroup
*)g_node
;
6455 if (group
== gr_parent
)
6458 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6461 act
= purple_menu_action_new(purple_group_get_name(group
),
6462 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6464 menu_groups
= g_list_prepend(menu_groups
, act
);
6466 menu_groups
= g_list_reverse(menu_groups
);
6468 act
= purple_menu_action_new(_("Copy to"),
6471 menu
= g_list_prepend(menu
, act
);
6472 menu
= g_list_reverse(menu
);
6479 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
6481 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
6482 struct sip_session
*session
;
6484 session
= sipe_session_find_chat_by_name(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6485 sipe_conf_modify_conference_lock(sip
, session
, locked
);
6489 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
6491 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
6492 sipe_conf_modify_lock(chat
, FALSE
);
6496 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
6498 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
6499 sipe_conf_modify_lock(chat
, TRUE
);
6503 sipe_chat_menu(PurpleChat
*chat
)
6505 PurpleMenuAction
*act
;
6506 PurpleConvChatBuddyFlags flags_us
;
6508 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
6509 struct sip_session
*session
;
6510 gchar
*self
= sip_uri_self(sip
);
6512 session
= sipe_session_find_chat_by_name(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6513 if (!session
) return NULL
;
6515 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
6517 if (session
->focus_uri
6518 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6520 if (session
->locked
) {
6521 act
= purple_menu_action_new(_("Unlock Conversation"),
6522 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
6524 menu
= g_list_prepend(menu
, act
);
6526 act
= purple_menu_action_new(_("Lock Conversation"),
6527 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
6529 menu
= g_list_prepend(menu
, act
);
6533 menu
= g_list_reverse(menu
);
6540 sipe_blist_node_menu(PurpleBlistNode
*node
)
6542 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
6543 return sipe_buddy_menu((PurpleBuddy
*) node
);
6544 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
6545 return sipe_chat_menu((PurpleChat
*)node
);
6552 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
6554 gboolean ret
= TRUE
;
6555 char *username
= (char *)trans
->payload
;
6557 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
6558 PurpleBuddy
*pbuddy
;
6559 struct sipe_buddy
*sbuddy
;
6561 char *server_alias
= NULL
;
6563 const char *device_name
= NULL
;
6565 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
6567 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
6568 alias
= purple_buddy_get_local_alias(pbuddy
);
6572 //will query buddy UA's capabilities and send answer to log
6573 sipe_options_request(sip
, username
);
6575 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
6578 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6582 if (msg
->response
!= 200) {
6583 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
6585 xmlnode
*searchResults
;
6588 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
6589 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6590 if (!searchResults
) {
6591 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
6592 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
6593 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
6594 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6595 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
6596 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
6597 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
6598 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
6599 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
6600 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
6601 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
6602 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
6603 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6604 if (!email
|| strcmp("", email
)) {
6605 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
6606 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
6610 xmlnode_free(searchResults
);
6613 purple_notify_user_info_add_section_break(info
);
6615 if (!server_alias
|| !strcmp("", server_alias
)) {
6616 g_free(server_alias
);
6617 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6619 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6623 // same as server alias, do not present
6624 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
6627 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6630 if (!email
|| !strcmp("", email
)) {
6632 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
6634 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6640 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
6643 /* show a buddy's user info in a nice dialog box */
6644 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
6645 username
, /* buddy's username */
6647 NULL
, /* callback called when dialog closed */
6648 NULL
); /* userdata for callback */
6654 * AD search first, LDAP based
6656 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
6658 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
6659 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
6661 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
6662 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
6663 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
6668 static PurplePlugin
*my_protocol
= NULL
;
6670 static PurplePluginProtocolInfo prpl_info
=
6672 OPT_PROTO_PASSWORD_OPTIONAL
| OPT_PROTO_CHAT_TOPIC
,
6673 NULL
, /* user_splits */
6674 NULL
, /* protocol_options */
6675 NO_BUDDY_ICONS
, /* icon_spec */
6676 sipe_list_icon
, /* list_icon */
6677 NULL
, /* list_emblems */
6678 sipe_status_text
, /* status_text */
6679 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
6680 sipe_status_types
, /* away_states */
6681 sipe_blist_node_menu
, /* blist_node_menu */
6682 NULL
, /* chat_info */
6683 NULL
, /* chat_info_defaults */
6684 sipe_login
, /* login */
6685 sipe_close
, /* close */
6686 sipe_im_send
, /* send_im */
6687 NULL
, /* set_info */ // TODO maybe
6688 sipe_send_typing
, /* send_typing */
6689 sipe_get_info
, /* get_info */
6690 sipe_set_status
, /* set_status */
6691 NULL
, /* set_idle */
6692 NULL
, /* change_passwd */
6693 sipe_add_buddy
, /* add_buddy */
6694 NULL
, /* add_buddies */
6695 sipe_remove_buddy
, /* remove_buddy */
6696 NULL
, /* remove_buddies */
6697 sipe_add_permit
, /* add_permit */
6698 sipe_add_deny
, /* add_deny */
6699 sipe_add_deny
, /* rem_permit */
6700 sipe_add_permit
, /* rem_deny */
6701 dummy_permit_deny
, /* set_permit_deny */
6702 NULL
, /* join_chat */
6703 NULL
, /* reject_chat */
6704 NULL
, /* get_chat_name */
6705 sipe_chat_invite
, /* chat_invite */
6706 sipe_chat_leave
, /* chat_leave */
6707 NULL
, /* chat_whisper */
6708 sipe_chat_send
, /* chat_send */
6709 sipe_keep_alive
, /* keepalive */
6710 NULL
, /* register_user */
6711 NULL
, /* get_cb_info */ // deprecated
6712 NULL
, /* get_cb_away */ // deprecated
6713 sipe_alias_buddy
, /* alias_buddy */
6714 sipe_group_buddy
, /* group_buddy */
6715 sipe_rename_group
, /* rename_group */
6716 NULL
, /* buddy_free */
6717 sipe_convo_closed
, /* convo_closed */
6718 purple_normalize_nocase
, /* normalize */
6719 NULL
, /* set_buddy_icon */
6720 sipe_remove_group
, /* remove_group */
6721 NULL
, /* get_cb_real_name */ // TODO?
6722 NULL
, /* set_chat_topic */
6723 NULL
, /* find_blist_chat */
6724 NULL
, /* roomlist_get_list */
6725 NULL
, /* roomlist_cancel */
6726 NULL
, /* roomlist_expand_category */
6727 NULL
, /* can_receive_file */
6728 NULL
, /* send_file */
6729 NULL
, /* new_xfer */
6730 NULL
, /* offline_message */
6731 NULL
, /* whiteboard_prpl_ops */
6732 sipe_send_raw
, /* send_raw */
6733 NULL
, /* roomlist_room_serialize */
6734 NULL
, /* unregister_user */
6735 NULL
, /* send_attention */
6736 NULL
, /* get_attention_types */
6738 sizeof(PurplePluginProtocolInfo
), /* struct_size */
6739 sipe_get_account_text_table
, /* get_account_text_table */
6743 static PurplePluginInfo info
= {
6744 PURPLE_PLUGIN_MAGIC
,
6745 PURPLE_MAJOR_VERSION
,
6746 PURPLE_MINOR_VERSION
,
6747 PURPLE_PLUGIN_PROTOCOL
, /**< type */
6748 NULL
, /**< ui_requirement */
6750 NULL
, /**< dependencies */
6751 PURPLE_PRIORITY_DEFAULT
, /**< priority */
6752 "prpl-sipe", /**< id */
6753 "Microsoft LCS/OCS", /**< name */
6754 VERSION
, /**< version */
6755 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
6756 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
6757 "Anibal Avelar <avelar@gmail.com>, " /**< author */
6758 "Gabriel Burt <gburt@novell.com>", /**< author */
6759 PURPLE_WEBSITE
, /**< homepage */
6760 sipe_plugin_load
, /**< load */
6761 sipe_plugin_unload
, /**< unload */
6762 sipe_plugin_destroy
, /**< destroy */
6763 NULL
, /**< ui_info */
6764 &prpl_info
, /**< extra_info */
6773 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
6777 entry
= prpl_info
.protocol_options
;
6779 purple_account_option_destroy(entry
->data
);
6780 entry
= g_list_delete_link(entry
, entry
);
6782 prpl_info
.protocol_options
= NULL
;
6784 entry
= prpl_info
.user_splits
;
6786 purple_account_user_split_destroy(entry
->data
);
6787 entry
= g_list_delete_link(entry
, entry
);
6789 prpl_info
.user_splits
= NULL
;
6792 static void init_plugin(PurplePlugin
*plugin
)
6794 PurpleAccountUserSplit
*split
;
6795 PurpleAccountOption
*option
;
6800 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
6801 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
6802 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
6803 textdomain(GETTEXT_PACKAGE
);
6806 purple_plugin_register(plugin
);
6808 split
= purple_account_user_split_new(_("Login \n DOMAIN\\user or\n user@company.com "), NULL
, ',');
6809 purple_account_user_split_set_reverse(split
, FALSE
);
6810 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
6812 option
= purple_account_option_string_new(_("Server[:Port]\n(Leave empty for auto-discovery)"), "server", "");
6813 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6815 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
6816 purple_account_option_add_list_item(option
, _("Auto"), "auto");
6817 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
6818 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
6819 purple_account_option_add_list_item(option
, _("UDP"), "udp");
6820 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6822 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
6823 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
6825 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
6826 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6829 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
6830 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6832 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
6833 * No login/password is taken into account if this option present,
6834 * instead used default credentials stored in OS.
6836 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
6837 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6839 my_protocol
= plugin
;
6842 /* I had to redefined the function for it load, but works */
6843 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
6844 plugin
->info
= &(info
);
6845 init_plugin((plugin
));
6846 sipe_plugin_load((plugin
));
6847 return purple_plugin_register(plugin
);