Bug 1257046 - Upgrade sipe to 1.20.1. rs=aleth
authorPatrick Cloke <clokep@gmail.com>
Wed, 16 Mar 2016 08:49:05 -0400
changeset 784 69428cd068a15eb4c8b828b049308481b2426f2f
parent 783 270af9e2de0ea92f71772897dcf1586bc57cfa8e
child 785 d8a03ccd5d668cbb0ed3f2dcc468f3ef091fcf5d
push id60
push userclokep@gmail.com
push dateWed, 16 Mar 2016 12:52:02 +0000
reviewersaleth
bugs1257046
Bug 1257046 - Upgrade sipe to 1.20.1. rs=aleth
libpurple/protocols/sipe/api/sipe-backend.h
libpurple/protocols/sipe/api/sipe-core.h
libpurple/protocols/sipe/core/sip-sec-basic.c
libpurple/protocols/sipe/core/sip-sec-gssapi.c
libpurple/protocols/sipe/core/sip-sec-mech.h
libpurple/protocols/sipe/core/sip-sec-negotiate.c
libpurple/protocols/sipe/core/sip-sec-ntlm.c
libpurple/protocols/sipe/core/sip-sec-sspi.c
libpurple/protocols/sipe/core/sip-sec-tls-dsk.c
libpurple/protocols/sipe/core/sip-sec.c
libpurple/protocols/sipe/core/sip-sec.h
libpurple/protocols/sipe/core/sip-soap.c
libpurple/protocols/sipe/core/sip-transport.c
libpurple/protocols/sipe/core/sipe-buddy.c
libpurple/protocols/sipe/core/sipe-buddy.h
libpurple/protocols/sipe/core/sipe-cal.c
libpurple/protocols/sipe/core/sipe-cal.h
libpurple/protocols/sipe/core/sipe-chat.c
libpurple/protocols/sipe/core/sipe-conf.c
libpurple/protocols/sipe/core/sipe-conf.h
libpurple/protocols/sipe/core/sipe-core-private.h
libpurple/protocols/sipe/core/sipe-core.c
libpurple/protocols/sipe/core/sipe-crypt-nss.c
libpurple/protocols/sipe/core/sipe-crypt.h
libpurple/protocols/sipe/core/sipe-domino.c
libpurple/protocols/sipe/core/sipe-ews-autodiscover.c
libpurple/protocols/sipe/core/sipe-ews.c
libpurple/protocols/sipe/core/sipe-groupchat.c
libpurple/protocols/sipe/core/sipe-http-request.c
libpurple/protocols/sipe/core/sipe-http-transport.c
libpurple/protocols/sipe/core/sipe-http.h
libpurple/protocols/sipe/core/sipe-notify.c
libpurple/protocols/sipe/core/sipe-ocs2005.c
libpurple/protocols/sipe/core/sipe-ocs2007.c
libpurple/protocols/sipe/core/sipe-ocs2007.h
libpurple/protocols/sipe/core/sipe-session.h
libpurple/protocols/sipe/core/sipe-status.c
libpurple/protocols/sipe/core/sipe-status.h
libpurple/protocols/sipe/core/sipe-svc.c
libpurple/protocols/sipe/core/sipe-svc.h
libpurple/protocols/sipe/core/sipe-tls-tester.c
libpurple/protocols/sipe/core/sipe-tls.c
libpurple/protocols/sipe/core/sipe-ucs.c
libpurple/protocols/sipe/core/sipe-ucs.h
libpurple/protocols/sipe/core/sipe-utils.c
libpurple/protocols/sipe/core/sipe-utils.h
libpurple/protocols/sipe/core/sipe-webticket.c
libpurple/protocols/sipe/core/sipmsg.c
libpurple/protocols/sipe/core/sipmsg.h
libpurple/protocols/sipe/purple-buddy.c
libpurple/protocols/sipe/purple-chat.c
libpurple/protocols/sipe/purple-debug.c
libpurple/protocols/sipe/purple-ft.c
libpurple/protocols/sipe/purple-im.c
libpurple/protocols/sipe/purple-notify.c
libpurple/protocols/sipe/purple-plugin.c
libpurple/protocols/sipe/purple-private.h
libpurple/protocols/sipe/purple-search.c
libpurple/protocols/sipe/purple-status.c
libpurple/protocols/sipe/purple-transport.c
libpurple/protocols/sipe/purple-user.c
--- a/libpurple/protocols/sipe/api/sipe-backend.h
+++ b/libpurple/protocols/sipe/api/sipe-backend.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-backend.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -61,17 +61,16 @@ struct sipe_media;
 gchar *sipe_backend_version(void);
 
 /** DEBUGGING ****************************************************************/
 
 typedef enum {
 	SIPE_DEBUG_LEVEL_INFO,
 	SIPE_DEBUG_LEVEL_WARNING,
 	SIPE_DEBUG_LEVEL_ERROR,
-	SIPE_DEBUG_LEVEL_FATAL,
 }  sipe_debug_level;
 
 /**
  * Output debug information without formatting
  *
  * Shouldn't be used directly. Instead use SIPE_DEBUG_xxx() macros
  *
  * @param level  debug level
@@ -94,18 +93,16 @@ void sipe_backend_debug(sipe_debug_level
 
 /* Convenience macros */
 #define SIPE_DEBUG_INFO(fmt, ...)        sipe_backend_debug(SIPE_DEBUG_LEVEL_INFO,    fmt, __VA_ARGS__)
 #define SIPE_DEBUG_INFO_NOFORMAT(msg)    sipe_backend_debug_literal(SIPE_DEBUG_LEVEL_INFO,    msg)
 #define SIPE_DEBUG_WARNING(fmt, ...)     sipe_backend_debug(SIPE_DEBUG_LEVEL_WARNING, fmt, __VA_ARGS__)
 #define SIPE_DEBUG_WARNING_NOFORMAT(msg) sipe_backend_debug_literal(SIPE_DEBUG_LEVEL_WARNING, msg)
 #define SIPE_DEBUG_ERROR(fmt, ...)       sipe_backend_debug(SIPE_DEBUG_LEVEL_ERROR,   fmt, __VA_ARGS__)
 #define SIPE_DEBUG_ERROR_NOFORMAT(msg)   sipe_backend_debug_literal(SIPE_DEBUG_LEVEL_ERROR,   msg)
-#define SIPE_DEBUG_FATAL(fmt, ...)       sipe_backend_debug(SIPE_DEBUG_LEVEL_FATAL,   fmt, __VA_ARGS__)
-#define SIPE_DEBUG_FATAL_NOFORMAT(msg)   sipe_backend_debug_literal(SIPE_DEBUG_LEVEL_FATAL,   msg)
 
 /**
  * Check backend debugging status
  *
  * @return TRUE if debugging is enabled
  */
 gboolean sipe_backend_debug_enabled(void);
 
@@ -333,33 +330,51 @@ typedef enum {
 } SipeComponentType;
 
 typedef enum {
 	SIPE_MEDIA_AUDIO,
 	SIPE_MEDIA_VIDEO
 } SipeMediaType;
 
 typedef enum {
+	SIPE_NETWORK_PROTOCOL_UDP,
 	SIPE_NETWORK_PROTOCOL_TCP_ACTIVE,
 	SIPE_NETWORK_PROTOCOL_TCP_PASSIVE,
-	SIPE_NETWORK_PROTOCOL_UDP
+	SIPE_NETWORK_PROTOCOL_TCP_SO,
 } SipeNetworkProtocol;
 
+typedef enum {
+	SIPE_ENCRYPTION_POLICY_REJECTED,
+	SIPE_ENCRYPTION_POLICY_OPTIONAL,
+	SIPE_ENCRYPTION_POLICY_REQUIRED,
+	SIPE_ENCRYPTION_POLICY_OBEY_SERVER
+} SipeEncryptionPolicy;
+
 struct sipe_media_call;
 struct sipe_backend_media;
 struct sipe_backend_codec;
 struct sipe_backend_candidate;
-struct sipe_backend_stream;
+struct sipe_backend_media_stream;
 struct sipe_backend_media_relays;
 
+struct sipe_media_stream {
+	struct sipe_backend_media_stream *backend_private;
+
+	gchar *id;
+};
+
 struct sipe_media_call {
 	struct sipe_backend_media *backend_private;
 
+	gchar *with;
+
 	void (*stream_initialized_cb)(struct sipe_media_call *,
-				      struct sipe_backend_stream *);
+				      struct sipe_media_stream *);
+	void (*stream_end_cb)(struct sipe_media_call *,
+			      struct sipe_media_stream *);
 	void (*media_end_cb)(struct sipe_media_call *);
 	void (*call_accept_cb)(struct sipe_media_call *, gboolean local);
 	void (*call_reject_cb)(struct sipe_media_call *, gboolean local);
 	void (*call_hold_cb)  (struct sipe_media_call *, gboolean local,
 			       gboolean state);
 	void (*call_hangup_cb)(struct sipe_media_call *, gboolean local);
 	void (*error_cb)(struct sipe_media_call *, gchar *message);
 };
@@ -380,71 +395,72 @@ void sipe_backend_media_free(struct sipe
 
 void sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname);
 
 struct sipe_backend_media_relays * sipe_backend_media_relays_convert(GSList *media_relays,
 								     gchar *username,
 								     gchar *password);
 void sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays);
 
-struct sipe_backend_stream *sipe_backend_media_add_stream(struct sipe_backend_media *media,
+struct sipe_backend_media_stream *sipe_backend_media_add_stream(struct sipe_media_call *media,
 							  const gchar *id,
 							  const gchar *participant,
 							  SipeMediaType type,
 							  SipeIceVersion ice_version,
 							  gboolean initiator,
 							  struct sipe_backend_media_relays *media_relays);
-void sipe_backend_media_remove_stream(struct sipe_backend_media *media,
-				      struct sipe_backend_stream *stream);
-GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media);
-struct sipe_backend_stream *sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
-								const gchar *id);
-void sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
-					      struct sipe_backend_stream *stream,
+void sipe_backend_media_add_remote_candidates(struct sipe_media_call *media,
+					      struct sipe_media_stream *stream,
 					      GList *candidates);
-gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
-					 struct sipe_backend_stream *stream);
+gboolean sipe_backend_media_is_initiator(struct sipe_media_call *media,
+					 struct sipe_media_stream *stream);
 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media);
-gboolean sipe_backend_stream_initialized(struct sipe_backend_media *media,
-					 struct sipe_backend_stream *stream);
-GList *sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
-						      struct sipe_backend_stream *stream);
-GList *sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
-						       struct sipe_backend_stream *stream);
+gboolean sipe_backend_stream_initialized(struct sipe_media_call *media,
+					 struct sipe_media_stream *stream);
+GList *sipe_backend_media_get_active_local_candidates(struct sipe_media_call *media,
+						      struct sipe_media_stream *stream);
+GList *sipe_backend_media_get_active_remote_candidates(struct sipe_media_call *media,
+						       struct sipe_media_stream *stream);
+void sipe_backend_media_set_encryption_keys(struct sipe_media_call *media,
+					    struct sipe_media_stream *stream,
+					    const guchar *encryption_key,
+					    const guchar *decryption_key);
 
 /* Stream handling */
-const gchar *sipe_backend_stream_get_id(struct sipe_backend_stream *stream);
-void sipe_backend_stream_hold(struct sipe_backend_media *media,
-			      struct sipe_backend_stream *stream,
+void sipe_backend_stream_hold(struct sipe_media_call *media,
+			      struct sipe_media_stream *stream,
 			      gboolean local);
-void sipe_backend_stream_unhold(struct sipe_backend_media *media,
-				struct sipe_backend_stream *stream,
+void sipe_backend_stream_unhold(struct sipe_media_call *media,
+				struct sipe_media_stream *stream,
 				gboolean local);
-gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream);
+gboolean sipe_backend_stream_is_held(struct sipe_media_stream *stream);
+void sipe_backend_media_stream_end(struct sipe_media_call *media,
+				   struct sipe_media_stream *stream);
+void sipe_backend_media_stream_free(struct sipe_backend_media_stream *stream);
 
 /* Codec handling */
 struct sipe_backend_codec *sipe_backend_codec_new(int id,
 						  const char *name,
 						  SipeMediaType type, guint clock_rate);
 void sipe_backend_codec_free(struct sipe_backend_codec *codec);
 int sipe_backend_codec_get_id(struct sipe_backend_codec *codec);
 /**
  * @return codec name. Will be g_free'd() by the core.
  */
 gchar *sipe_backend_codec_get_name(struct sipe_backend_codec *codec);
 guint sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec);
 void sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
 					       const gchar *name,
 					       const gchar *value);
 GList *sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec);
-gboolean sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
-					struct sipe_backend_stream *stream,
+gboolean sipe_backend_set_remote_codecs(struct sipe_media_call *media,
+					struct sipe_media_stream *stream,
 					GList *codecs);
-GList* sipe_backend_get_local_codecs(struct sipe_backend_media *media,
-				     struct sipe_backend_stream *stream);
+GList* sipe_backend_get_local_codecs(struct sipe_media_call *media,
+				     struct sipe_media_stream *stream);
 
 /* Candidate handling */
 struct sipe_backend_candidate * sipe_backend_candidate_new(const gchar *foundation,
 							   SipeComponentType component,
 							   SipeCandidateType type,
 							   SipeNetworkProtocol proto,
 							   const gchar *ip, guint port,
 							   const gchar *username,
@@ -472,18 +488,18 @@ guint sipe_backend_candidate_get_port(st
  */
 gchar *sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate);
 guint sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate);
 guint32 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate);
 void sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority);
 SipeComponentType sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate);
 SipeCandidateType sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate);
 SipeNetworkProtocol sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate);
-GList* sipe_backend_get_local_candidates(struct sipe_backend_media *media,
-					 struct sipe_backend_stream *stream);
+GList* sipe_backend_get_local_candidates(struct sipe_media_call *media,
+					 struct sipe_media_stream *stream);
 void sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local);
 void sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local);
 void sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local);
 
 /** NETWORK ******************************************************************/
 
 const gchar *sipe_backend_network_ip_address(struct sipe_core_public *sipe_public);
 
@@ -568,16 +584,26 @@ const gchar *sipe_backend_setting(struct
 				  sipe_setting type);
 
 /** STATUS *******************************************************************/
 
 guint sipe_backend_status(struct sipe_core_public *sipe_public);
 gboolean sipe_backend_status_changed(struct sipe_core_public *sipe_public,
 				     guint activity,
 				     const gchar *message);
+
+/**
+ * Update user client with new status and note received from server
+ *
+ * NOTE: this must *NOT* trigger a call to @c sipe_core_status_set()!
+ *
+ * @param sipe_public   The handle representing the protocol instance
+ * @param activity      New activity
+ * @param message       New note text
+ */
 void sipe_backend_status_and_note(struct sipe_core_public *sipe_public,
 				  guint activity,
 				  const gchar *message);
 
 /** TRANSPORT ****************************************************************/
 
 typedef void transport_connected_cb(struct sipe_transport_connection *conn);
 typedef void transport_input_cb(struct sipe_transport_connection *conn);
@@ -1010,16 +1036,18 @@ struct sipe_backend_buddy_menu *sipe_bac
 struct sipe_backend_buddy_menu *sipe_backend_buddy_menu_separator(struct sipe_core_public *sipe_public,
 								  struct sipe_backend_buddy_menu *menu,
 								  const gchar *label);
 struct sipe_backend_buddy_menu *sipe_backend_buddy_sub_menu_add(struct sipe_core_public *sipe_public,
 								struct sipe_backend_buddy_menu *menu,
 								const gchar *label,
 								struct sipe_backend_buddy_menu *sub);
 
+SipeEncryptionPolicy sipe_backend_media_get_encryption_policy(struct sipe_core_public *sipe_public);
+
 #ifdef __cplusplus
 }
 #endif
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
--- a/libpurple/protocols/sipe/api/sipe-core.h
+++ b/libpurple/protocols/sipe/api/sipe-core.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-core.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -89,31 +89,37 @@ struct sipe_backend_private;
  * SIP transport authentication scheme
  */
 #define SIPE_AUTHENTICATION_TYPE_UNSET     0
 #define SIPE_AUTHENTICATION_TYPE_BASIC     1 /* internal use only */
 #define SIPE_AUTHENTICATION_TYPE_NTLM      2
 #define SIPE_AUTHENTICATION_TYPE_KERBEROS  3
 #define SIPE_AUTHENTICATION_TYPE_NEGOTIATE 4 /* internal use only */
 #define SIPE_AUTHENTICATION_TYPE_TLS_DSK   5
+#define SIPE_AUTHENTICATION_TYPE_AUTOMATIC 6 /* always last */
 
 /**
  * Flags
  */
 /* user disabled calendar information publishing */
 #define SIPE_CORE_FLAG_DONT_PUBLISH 0x00000001
 
 #define SIPE_CORE_FLAG_IS(flag)    \
 	((sipe_public->flags & SIPE_CORE_FLAG_ ## flag) == SIPE_CORE_FLAG_ ## flag)
 #define SIPE_CORE_FLAG_SET(flag)   \
 	(sipe_public->flags |= SIPE_CORE_FLAG_ ## flag)
 #define SIPE_CORE_FLAG_UNSET(flag) \
 	(sipe_public->flags &= ~SIPE_CORE_FLAG_ ## flag)
 
 /**
+ * Byte length of cryptographic key for call encryption.
+ */
+#define SIPE_SRTP_KEY_LEN 30
+
+/**
  * Public part of the Sipe data structure
  *
  * This part contains the information needed by the core and the backend.
  */
 struct sipe_core_public {
 	/**
 	 * This points to the private data for the backend.
 	 * The backend is responsible to allocate and free it.
@@ -276,17 +282,16 @@ void sipe_core_group_set_alias(struct si
 			       const gchar *who,
 			       const gchar *alias);
 
 /**
  * Setup core data
  */
 struct sipe_core_public *sipe_core_allocate(const gchar *signin_name,
 					    gboolean sso,
-					    const gchar *login_domain,
 					    const gchar *login_account,
 					    const gchar *password,
 					    const gchar *email,
 					    const gchar *email_url,
 					    const gchar **errmsg);
 void sipe_core_deallocate(struct sipe_core_public *sipe_public);
 
 /**
@@ -316,16 +321,22 @@ void sipe_core_transport_sip_connect(str
  *
  * @param sipe_public Sipe core public data structure
  *
  * @return server host name (may be @c NULL if not fully connected yet)
  */
 const gchar *sipe_core_transport_sip_server_name(struct sipe_core_public *sipe_public);
 
 /**
+ * Get chat ID, f.ex. group chat URI
+ */
+const gchar *sipe_core_chat_id(struct sipe_core_public *sipe_public,
+			       struct sipe_chat_session *chat_session);
+
+/**
  * Invite to chat
  */
 void sipe_core_chat_invite(struct sipe_core_public *sipe_public,
 			   struct sipe_chat_session *chat_session,
 			   const char *name);
 
 /**
  * Rejoin a chat after connection re-establishment
@@ -364,22 +375,19 @@ void sipe_core_chat_modify_lock(struct s
 				struct sipe_chat_session *chat_session,
 				const gboolean locked);
 
 /**
  * Create new session with Focus URI
  *
  * @param sipe_public (in) SIPE core data.
  * @param focus_uri (in) focus URI string
- *
- * @return new SIP session
  */
-struct sip_session *
-sipe_core_conf_create(struct sipe_core_public *sipe_public,
-		      const gchar *focus_uri);
+void sipe_core_conf_create(struct sipe_core_public *sipe_public,
+			   const gchar *focus_uri);
 
 /* buddy menu callback: parameter == chat_session */
 void sipe_core_conf_make_leader(struct sipe_core_public *sipe_public,
 				gpointer parameter,
 				const gchar *buddy_name);
 void sipe_core_conf_remove_from(struct sipe_core_public *sipe_public,
 				gpointer parameter,
 				const gchar *buddy_name);
@@ -387,16 +395,20 @@ void sipe_core_conf_remove_from(struct s
 /* call control (CSTA) */
 void sipe_core_buddy_make_call(struct sipe_core_public *sipe_public,
 			       const gchar *phone);
 
 /* media */
 void sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
 				   const char *participant,
 				   gboolean with_video);
+struct sipe_media_call;
+struct sipe_media_stream *
+sipe_core_media_get_stream_by_id(struct sipe_media_call *call, const gchar *id);
+
 /**
  * Connects to a conference call specified by given chat session
  *
  * @param sipe_public (in) SIPE core data.
  * @param chat_session (in) chat session structure
  */
 void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public,
 					struct sipe_chat_session *chat_session);
@@ -479,16 +491,17 @@ void sipe_core_buddy_group(struct sipe_c
 			   const gchar *new_group_name);
 
 struct sipe_backend_search_token;
 void sipe_core_buddy_search(struct sipe_core_public *sipe_public,
 			    struct sipe_backend_search_token *token,
 			    const gchar *given_name,
 			    const gchar *surname,
 			    const gchar *email,
+			    const gchar *sipid,
 			    const gchar *company,
 			    const gchar *country);
 
 void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public,
 			      const gchar *who);
 
 void sipe_core_buddy_new_chat(struct sipe_core_public *sipe_public,
 			      const gchar *who);
@@ -497,21 +510,30 @@ void sipe_core_buddy_send_email(struct s
 
 struct sipe_backend_buddy_menu;
 struct sipe_backend_buddy_menu *sipe_core_buddy_create_menu(struct sipe_core_public *sipe_public,
 							    const gchar *buddy_name,
 							    struct sipe_backend_buddy_menu *menu);
 
 void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public);
 
-/* status */
+/**
+ * User/Machine has changed the user status
+ *
+ * NOTE: must *NEVER* be triggered by @c sipe_backend_status_and_note()!
+ *
+ * @param sipe_public   The handle representing the protocol instance
+ * @param set_by_user   @c TRUE if status has been changed by user
+ * @param activity      New activity
+ * @param message       New note text
+ */
 void sipe_core_status_set(struct sipe_core_public *sipe_public,
+			  gboolean set_by_user,
 			  guint activity,
 			  const gchar *note);
-void sipe_core_status_idle(struct sipe_core_public *sipe_public);
 
 #ifdef __cplusplus
 }
 #endif
 
 /*
   Local Variables:
   mode: c
--- a/libpurple/protocols/sipe/core/sip-sec-basic.c
+++ b/libpurple/protocols/sipe/core/sip-sec-basic.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec-basic.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -40,17 +40,16 @@ typedef struct _context_basic {
 	gchar *token;
 	guint length;
 } *context_basic;
 
 /* sip-sec-mech.h API implementation for Basic */
 
 static gboolean
 sip_sec_acquire_cred__basic(SipSecContext context,
-			    SIPE_UNUSED_PARAMETER const gchar *domain,
 			    const gchar *username,
 			    const gchar *password)
 {
 	context_basic ctx = (context_basic) context;
 
 	SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__basic: entering");
 
 	if (!username || !password)
--- a/libpurple/protocols/sipe/core/sip-sec-gssapi.c
+++ b/libpurple/protocols/sipe/core/sip-sec-gssapi.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec-gssapi.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -270,90 +270,97 @@ static void drop_gssapi_context(SipSecCo
 	ctx->ctx_gssapi = GSS_C_NO_CONTEXT;
 	context->flags &= ~SIP_SEC_FLAG_COMMON_READY;
 }
 
 /* sip-sec-mech.h API implementation for Kerberos/GSSAPI */
 
 static gboolean
 sip_sec_acquire_cred__gssapi(SipSecContext context,
-			     const gchar *domain,
 			     const gchar *username,
 			     const gchar *password)
 {
 	context_gssapi ctx = (context_gssapi) context;
 
 	SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__gssapi: started");
 
 	/* this is the first time we are allowed to set private flags */
 	if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) &&
 	    (context->type == SIPE_AUTHENTICATION_TYPE_NTLM))
 		context->flags |= SIP_SEC_FLAG_GSSAPI_SIP_NTLM;
 
 	/* With SSO we use the default credentials */
 	if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) {
 #ifdef HAVE_GSSAPI_PASSWORD_SUPPORT
-		gchar *username_new;
+		gchar *username_new = NULL;
 		OM_uint32 ret;
 		OM_uint32 minor, minor_ignore;
 		gss_OID_set mechs_set;
 		gss_cred_id_t credentials;
 		gss_buffer_desc input_name_buffer;
 		gss_name_t user_name;
 
 		/* Without SSO we need user name and password */
-		if (!username || !password) {
+		if (is_empty(username) || is_empty(password)) {
 			SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__gssapi: no valid authentication information provided");
 			return(FALSE);
 		}
 
 		mechs_set = create_mechs_set(context->type);
 		if (mechs_set == GSS_C_NO_OID_SET)
 			return(FALSE);
 
-		/* Construct user name to acquire credentials for */
-		if (!is_empty(domain)) {
-			/* User specified a domain */
-			gchar *realm = g_ascii_strup(domain, -1);
+		if (!SIP_SEC_USERNAME_IS_ENTERPRISE) {
+			SIP_SEC_USERNAME_SPLIT_START;
+
+			/* Construct user name to acquire credentials for */
+			if (SIP_SEC_USERNAME_HAS_DOMAIN) {
+				/* User specified a domain */
+				gchar *realm = g_ascii_strup(SIP_SEC_USERNAME_DOMAIN,
+							     -1);
 
-			username_new = g_strdup_printf("%s@%s",
-						       username,
-						       realm);
-			g_free(realm);
+				username_new = g_strdup_printf("%s@%s",
+							       SIP_SEC_USERNAME_ACCOUNT,
+							       realm);
+				g_free(realm);
 
-		} else if (strchr(username, '@')) {
-			/* No domain, username matches XXX@YYY */
-			gchar **user_realm = g_strsplit(username, "@", 2);
-			gchar *realm       = g_ascii_strup(user_realm[1], -1);
+			} else if (strchr(username, '@')) {
+				/* No domain, username matches XXX@YYY */
+				gchar **user_realm = g_strsplit(username, "@", 2);
+				gchar *realm       = g_ascii_strup(user_realm[1], -1);
 
-			/*
-			 * We should escape the "@" to generate a enterprise
-			 * principal, i.e. XXX\@YYY
-			 *
-			 * But krb5 libraries currently don't support this:
-			 *
-			 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=7729
-			 *
-			 * username_new = g_strdup_printf("%s\\@%s",
-			 */
-			username_new = g_strdup_printf("%s@%s",
-						       user_realm[0],
-						       realm);
-			g_free(realm);
-			g_strfreev(user_realm);
-		} else {
-			/* Otherwise use username as is */
-			username_new = g_strdup(username);
+				/*
+				 * We should escape the "@" to generate a enterprise
+				 * principal, i.e. XXX\@YYY
+				 *
+				 * But krb5 libraries currently don't support this:
+				 *
+				 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=7729
+				 *
+				 * username_new = g_strdup_printf("%s\\@%s",
+				 */
+				username_new = g_strdup_printf("%s@%s",
+							       user_realm[0],
+							       realm);
+				g_free(realm);
+				g_strfreev(user_realm);
+			}
+
+			SIP_SEC_USERNAME_SPLIT_END;
 		}
+
+		if (username_new)
+			username = username_new;
+
 		SIPE_DEBUG_INFO("sip_sec_acquire_cred__gssapi: username '%s'",
-				username_new);
+				username);
 
 		/* Import user name into GSS format */
-		input_name_buffer.value  = (void *) username_new;
-		input_name_buffer.length = strlen(username_new) + 1;
+		input_name_buffer.value  = (void *) username;
+		input_name_buffer.length = strlen(username) + 1;
 
 		ret = gss_import_name(&minor,
 				      &input_name_buffer,
 				      (gss_OID) GSS_C_NT_USER_NAME,
 				      &user_name);
 		g_free(username_new);
 
 		if (GSS_ERROR(ret)) {
@@ -386,17 +393,16 @@ sip_sec_acquire_cred__gssapi(SipSecConte
 
 		ctx->cred_gssapi = credentials;
 
 #else
 		/*
 		 * non-SSO support requires gss_acquire_cred_with_password()
 		 * which is not available on older GSSAPI releases.
 		 */
-		(void) domain;   /* keep compiler happy */
 		(void) username; /* keep compiler happy */
 		(void) password; /* keep compiler happy */
 		(void) ctx;      /* keep compiler happy */
 		SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__gssapi: non-SSO mode not supported");
 		return(FALSE);
 #endif
 	}
 #ifdef HAVE_GSSAPI_ONLY
@@ -773,17 +779,17 @@ sip_sec_create_context__gssapi(SIPE_UNUS
 	context->ctx_gssapi  = GSS_C_NO_CONTEXT;
 	context->target_name = GSS_C_NO_NAME;
 
 	return((SipSecContext) context);
 }
 
 gboolean sip_sec_password__gssapi(void)
 {
-	/* Kerberos supports Single-Sign On */
+	/* GSSAPI supports Single-Sign On */
 	return(FALSE);
 }
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
--- a/libpurple/protocols/sipe/core/sip-sec-mech.h
+++ b/libpurple/protocols/sipe/core/sip-sec-mech.h
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec-mech.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -33,17 +33,16 @@ typedef struct {
 	guint8 *value;
 } SipSecBuffer;
 
 typedef SipSecContext
 (*sip_sec_create_context_func)(guint type);
 
 typedef gboolean
 (*sip_sec_acquire_cred_func)(SipSecContext context,
-			     const gchar *domain,
 			     const gchar *username,
 			     const gchar *password);
 
 typedef gboolean
 (*sip_sec_init_context_func)(SipSecContext context,
 			     SipSecBuffer in_buff,
 			     SipSecBuffer *out_buff,
 			     const gchar *service_name);
@@ -85,8 +84,19 @@ struct sip_sec_context {
  * 0x00000001 - 0x00008000: common flags
  * 0x00010000 - 0x80000000: mechanism private flags
  *
  * NOTE: private flags must be set in acquire_cred_func()!
  */
 #define SIP_SEC_FLAG_COMMON_SSO   0x00000001
 #define SIP_SEC_FLAG_COMMON_HTTP  0x00000002
 #define SIP_SEC_FLAG_COMMON_READY 0x00000004
+
+/**
+ * Helper macro for parsing username into domain & account parts
+ */
+#define SIP_SEC_USERNAME_ENTERPRISE_STRING "\\@"
+#define SIP_SEC_USERNAME_IS_ENTERPRISE     (strstr(username, SIP_SEC_USERNAME_ENTERPRISE_STRING) != NULL)
+#define SIP_SEC_USERNAME_SPLIT_START       gchar **_domain_user = g_strsplit_set(username, "/\\", 2)
+#define SIP_SEC_USERNAME_SPLIT_END         g_strfreev(_domain_user)
+#define SIP_SEC_USERNAME_DOMAIN            _domain_user[0]
+#define SIP_SEC_USERNAME_ACCOUNT           _domain_user[1]
+#define SIP_SEC_USERNAME_HAS_DOMAIN        (SIP_SEC_USERNAME_ACCOUNT != NULL)
--- a/libpurple/protocols/sipe/core/sip-sec-negotiate.c
+++ b/libpurple/protocols/sipe/core/sip-sec-negotiate.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec-negotiate.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -34,17 +34,16 @@
 #include "sip-sec-negotiate.h"
 #include "sip-sec-ntlm.h"
 #include "sipe-backend.h"
 #include "sipe-core.h"
 
 /* Security context for Negotiate */
 typedef struct _context_negotiate {
 	struct sip_sec_context common;
-	const gchar *domain;
 	const gchar *username;
 	const gchar *password;
 	SipSecContext krb5;
 	SipSecContext ntlm;
 } *context_negotiate;
 
 #define SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK 0x80000000
 
@@ -77,42 +76,38 @@ static gboolean sip_sec_negotiate_ntlm_f
 		SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_negotiate_ntlm_fallback: forbidden");
 		return(FALSE);
 	}
 
 	sip_sec_negotiate_drop_krb5(context);
 	sip_sec_negotiate_copy_flags(context, context->ntlm);
 
 	return(context->ntlm->acquire_cred_func(context->ntlm,
-						context->domain,
 						context->username,
 						context->password));
 }
 
 /* sip-sec-mech.h API implementation for Negotiate */
 
 static gboolean
 sip_sec_acquire_cred__negotiate(SipSecContext context,
-				const gchar *domain,
 				const gchar *username,
 				const gchar *password)
 {
 	context_negotiate ctx = (context_negotiate) context;
 	gboolean ret;
 
 	SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: entering");
 
-	ctx->domain   = domain;
 	ctx->username = username;
 	ctx->password = password;
 
 	context = ctx->krb5;
 	sip_sec_negotiate_copy_flags(ctx, context);
 	ret = context->acquire_cred_func(context,
-					 domain,
 					 username,
 					 password);
 	if (!ret) {
 		/* Kerberos failed -> fall back to NTLM immediately */
 		SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: fallback to NTLM");
 		ret = sip_sec_negotiate_ntlm_fallback(ctx);
 	}
 
--- a/libpurple/protocols/sipe/core/sip-sec-ntlm.c
+++ b/libpurple/protocols/sipe/core/sip-sec-ntlm.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec-ntlm.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009, 2010 pier11 <pier11@operamail.com>
  * Copyright (C) 2008 Novell, Inc.
  * Modify        2007, Anibal Avelar <avelar@gmail.com>
  * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
  *
  * Implemented with reference to the follow documentation:
  *   - http://davenport.sourceforge.net/ntlm.html
  *   - MS-NLMP: http://msdn.microsoft.com/en-us/library/cc207842.aspx
@@ -1698,50 +1698,64 @@ sip_sec_ntlm_message_describe(SipSecBuff
 /* Analyzer only needs the _describe() functions */
 #ifndef _SIPE_COMPILING_ANALYZER
 
 /* sip-sec-mech.h API implementation for NTLM */
 
 /* Security context for NTLM */
 typedef struct _context_ntlm {
 	struct sip_sec_context common;
-	const gchar *domain;
-	const gchar *username;
+	gchar *domain;
+	gchar *username;
 	const gchar *password;
 	guchar *client_sign_key;
 	guchar *server_sign_key;
 	guchar *client_seal_key;
 	guchar *server_seal_key;
 	guint32 flags;
 } *context_ntlm;
 
 #define SIP_SEC_FLAG_NTLM_INITIAL  0x00010000
 
 
 static gboolean
 sip_sec_acquire_cred__ntlm(SipSecContext context,
-			   const gchar *domain,
 			   const gchar *username,
 			   const gchar *password)
 {
 	context_ntlm ctx = (context_ntlm)context;
 
 	/*
 	 * Our NTLM implementation does not support Single Sign-On.
 	 * Thus username & password are required.
-	 * NULL or empty domain is OK.
 	 */
-	if (is_empty(username) || is_empty(password))
+	if (is_empty(username) || is_empty(password)) {
+		SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__ntlm: no valid authentication information provided");
 		return FALSE;
+	}
 
 	/* this is the first time we are allowed to set private flags */
 	context->flags |= SIP_SEC_FLAG_NTLM_INITIAL;
 
-	ctx->domain   = domain ? domain : "";
-	ctx->username = username;
+	if (SIP_SEC_USERNAME_IS_ENTERPRISE) {
+		/* use username as-is, just replace enterprise marker with @ */
+		ctx->username = sipe_utils_str_replace(username,
+						       SIP_SEC_USERNAME_ENTERPRISE_STRING,
+						       "@");
+	} else {
+		SIP_SEC_USERNAME_SPLIT_START;
+		if (SIP_SEC_USERNAME_HAS_DOMAIN) {
+			ctx->domain   = g_strdup(SIP_SEC_USERNAME_DOMAIN);
+			ctx->username = g_strdup(SIP_SEC_USERNAME_ACCOUNT);
+		} else {
+			ctx->username = g_strdup(username);
+		}
+		SIP_SEC_USERNAME_SPLIT_END;
+	}
+
 	ctx->password = password;
 
 	return TRUE;
 }
 
 static gboolean
 sip_sec_init_sec_context__ntlm(SipSecContext context,
 			       SipSecBuffer in_buff,
@@ -1805,17 +1819,17 @@ sip_sec_init_sec_context__ntlm(SipSecCon
 		res = sip_sec_ntlm_gen_authenticate(
 					      &client_sign_key,
 					      &server_sign_key,
 					      &client_seal_key,
 					      &server_seal_key,
 					      ctx->username,
 					      ctx->password,
 					      (tmp = g_ascii_strup(g_get_host_name(), -1)),
-					      ctx->domain,
+					      ctx->domain ? ctx->domain : "",
 					      server_challenge,
 					      time_val,
 					      target_info,
 					      target_info_len,
 					      context->flags & SIP_SEC_FLAG_COMMON_HTTP,
 					      out_buff,
 					      &flags);
 		g_free(server_challenge);
@@ -1905,16 +1919,18 @@ static void
 sip_sec_destroy_sec_context__ntlm(SipSecContext context)
 {
 	context_ntlm ctx = (context_ntlm) context;
 
 	g_free(ctx->client_sign_key);
 	g_free(ctx->server_sign_key);
 	g_free(ctx->client_seal_key);
 	g_free(ctx->server_seal_key);
+	g_free(ctx->domain);
+	g_free(ctx->username);
 	g_free(ctx);
 }
 
 static const gchar *
 sip_sec_context_name__ntlm(SIPE_UNUSED_PARAMETER SipSecContext context)
 {
 	return("NTLM");
 }
--- a/libpurple/protocols/sipe/core/sip-sec-sspi.c
+++ b/libpurple/protocols/sipe/core/sip-sec-sspi.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec-sspi.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -48,16 +48,17 @@
 /* Mechanism names */
 static const gchar * const mech_names[] = {
 	"",          /* SIPE_AUTHENTICATION_TYPE_UNSET     */
 	"",          /* SIPE_AUTHENTICATION_TYPE_BASIC     */
 	"NTLM",      /* SIPE_AUTHENTICATION_TYPE_NTLM      */
 	"Kerberos",  /* SIPE_AUTHENTICATION_TYPE_KERBEROS  */
 	"Negotiate", /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
 	"",          /* SIPE_AUTHENTICATION_TYPE_TLS_DSK   */
+	"",          /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */
 };
 
 #ifndef ISC_REQ_IDENTIFY
 #define ISC_REQ_IDENTIFY               0x00002000
 #endif
 
 typedef struct _context_sspi {
 	struct sip_sec_context common;
@@ -127,62 +128,78 @@ sip_sec_destroy_sspi_context(context_ssp
 		context->cred_sspi = NULL;
 	}
 }
 
 /* sip-sec-mech.h API implementation for SSPI - Kerberos, NTLM and Negotiate */
 
 static gboolean
 sip_sec_acquire_cred__sspi(SipSecContext context,
-			   const gchar *domain,
 			   const gchar *username,
 			   const gchar *password)
 {
 	SECURITY_STATUS ret;
 	TimeStamp expiry;
 	SEC_WINNT_AUTH_IDENTITY auth_identity;
 	context_sspi ctx = (context_sspi)context;
+	gchar *domain_tmp = NULL;
+	gchar *user_tmp = NULL;
 
 	/* this is the first time we are allowed to set private flags */
 	if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) &&
 	    (context->type == SIPE_AUTHENTICATION_TYPE_NTLM))
 		context->flags |= SIP_SEC_FLAG_SSPI_SIP_NTLM;
 
 	if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) {
-		if (!username || !password) {
+		if (is_empty(username) || is_empty(password)) {
+			SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__sspi: no valid authentication information provided");
 			return FALSE;
 		}
 
 		memset(&auth_identity, 0, sizeof(auth_identity));
 		auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
 
-		if (!is_empty(domain)) {
-			auth_identity.Domain = (unsigned char*)domain;
-			auth_identity.DomainLength = strlen(domain);
+		if (SIP_SEC_USERNAME_IS_ENTERPRISE) {
+			/* use username as-is, just replace enterprise marker with @ */
+			user_tmp = sipe_utils_str_replace(username,
+							  SIP_SEC_USERNAME_ENTERPRISE_STRING,
+							  "@");
+		} else {
+			SIP_SEC_USERNAME_SPLIT_START;
+			if (SIP_SEC_USERNAME_HAS_DOMAIN) {
+				domain_tmp                 = g_strdup(SIP_SEC_USERNAME_DOMAIN);
+				user_tmp                   = g_strdup(SIP_SEC_USERNAME_ACCOUNT);
+				auth_identity.Domain       = (unsigned char *)domain_tmp;
+				auth_identity.DomainLength = strlen(domain_tmp);
+			}
+			SIP_SEC_USERNAME_SPLIT_END;
 		}
 
-		auth_identity.User = (unsigned char*)username;
-		auth_identity.UserLength = strlen(username);
+		auth_identity.User           = (unsigned char *)(user_tmp ? user_tmp : username);
+		auth_identity.UserLength     = strlen((char *) auth_identity.User);
 
-		auth_identity.Password = (unsigned char*)password;
+		auth_identity.Password       = (unsigned char *)password;
 		auth_identity.PasswordLength = strlen(password);
 	}
 
 	ctx->cred_sspi = g_malloc0(sizeof(CredHandle));
 
 	ret = AcquireCredentialsHandleA(NULL,
 					(SEC_CHAR *)mech_names[context->type],
 					SECPKG_CRED_OUTBOUND,
 					NULL,
 					(context->flags & SIP_SEC_FLAG_COMMON_SSO) ? NULL : &auth_identity,
 					NULL,
 					NULL,
 					ctx->cred_sspi,
 					&expiry);
 
+	g_free(user_tmp);
+	g_free(domain_tmp);
+
 	if (ret != SEC_E_OK) {
 		sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret);
 		g_free(ctx->cred_sspi);
 		ctx->cred_sspi = NULL;
 		return FALSE;
 	} else {
 		return TRUE;
 	}
--- a/libpurple/protocols/sipe/core/sip-sec-tls-dsk.c
+++ b/libpurple/protocols/sipe/core/sip-sec-tls-dsk.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec-tls-dsk.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -54,17 +54,16 @@ typedef struct _context_tls_dsk {
 	guchar *server_key;
 	gsize key_length;
 } *context_tls_dsk;
 
 /* sip-sec-mech.h API implementation for TLS-DSK */
 
 static gboolean
 sip_sec_acquire_cred__tls_dsk(SipSecContext context,
-			      SIPE_UNUSED_PARAMETER const gchar *domain,
 			      SIPE_UNUSED_PARAMETER const gchar *username,
 			      const gchar *password)
 {
 	context_tls_dsk ctx = (context_tls_dsk) context;
 
 	return((ctx->state = sipe_tls_start((gpointer) password)) != NULL);
 }
 
@@ -89,28 +88,18 @@ sip_sec_init_sec_context__tls_dsk(SipSec
 			/* copy key pair */
 			ctx->algorithm  = state->algorithm;
 			ctx->key_length = state->key_length;
 			ctx->client_key = g_memdup(state->client_key,
 						   state->key_length);
 			ctx->server_key = g_memdup(state->server_key,
 						   state->key_length);
 
-			/* [MS-SIPAE] Section 3.2.2 Timers
-			 *
-			 * ... For an SA established using the TLS-DSK
-			 * authentication protocol, the client MUST
-			 * retrieve the expiration time of its certificate.
-			 * The expiration timer value is the lesser of the
-			 * interval to the certificate expiration and eight
-			 * hours, ...
-			 */
+			/* extract certicate expiration time */
 			ctx->common.expires = sipe_tls_expires(state);
-			if (ctx->common.expires > (8 * 60 * 60))
-				ctx->common.expires = 8 * 60 * 60;
 
 			SIPE_DEBUG_INFO("sip_sec_init_sec_context__tls_dsk: handshake completed, algorithm %d, key length %" G_GSIZE_FORMAT ", expires %d",
 					ctx->algorithm, ctx->key_length, ctx->common.expires);
 
 			sipe_tls_free(state);
 			ctx->state = NULL;
 		} else {
 			out_buff->value  = state->out_buffer;
--- a/libpurple/protocols/sipe/core/sip-sec.c
+++ b/libpurple/protocols/sipe/core/sip-sec.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -105,41 +105,42 @@
 
 /* Dummy initialization hook */
 static SipSecContext
 sip_sec_create_context__NONE(SIPE_UNUSED_PARAMETER guint type)
 {
 	return(NULL);
 }
 
-/* Dummy SIP password hook */
+/* Dummy SIP password hooks */
 static gboolean sip_sec_password__NONE(void)
 {
-	return(TRUE);
+	/* Don't ask for a password */
+	return(FALSE);
 }
 
 /* sip_sec API methods */
 SipSecContext
 sip_sec_create_context(guint type,
 		       gboolean sso,
 		       gboolean http,
-		       const gchar *domain,
 		       const gchar *username,
 		       const gchar *password)
 {
 	SipSecContext context = NULL;
 
 	/* Map authentication type to module initialization hook & name */
 	static sip_sec_create_context_func const auth_to_hook[] = {
 		sip_sec_create_context__NONE,      /* SIPE_AUTHENTICATION_TYPE_UNSET     */
 		sip_sec_create_context__Basic,     /* SIPE_AUTHENTICATION_TYPE_BASIC     */
 		sip_sec_create_context__NTLM,      /* SIPE_AUTHENTICATION_TYPE_NTLM      */
 		sip_sec_create_context__Kerberos,  /* SIPE_AUTHENTICATION_TYPE_KERBEROS  */
 		sip_sec_create_context__Negotiate, /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
 		sip_sec_create_context__TLS_DSK,   /* SIPE_AUTHENTICATION_TYPE_TLS_DSK   */
+		sip_sec_create_context__NONE,      /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */
 	};
 
 	SIPE_DEBUG_INFO("sip_sec_create_context: type: %d, Single Sign-On: %s, protocol: %s",
 			type, sso ? "yes" : "no", http ? "HTTP" : "SIP");
 
 	context = (*(auth_to_hook[type]))(type);
 	if (context) {
 
@@ -149,17 +150,17 @@ sip_sec_create_context(guint type,
 		context->flags = 0;
 
 		/* set common flags */
 		if (sso)
 			context->flags |= SIP_SEC_FLAG_COMMON_SSO;
 		if (http)
 			context->flags |= SIP_SEC_FLAG_COMMON_HTTP;
 
-		if (!(*context->acquire_cred_func)(context, domain, username, password)) {
+		if (!(*context->acquire_cred_func)(context, username, password)) {
 			SIPE_DEBUG_INFO_NOFORMAT("ERROR: sip_sec_create_context: failed to acquire credentials.");
 			(*context->destroy_context_func)(context);
 			context = NULL;
 		}
 	}
 
 	return(context);
 }
@@ -275,16 +276,17 @@ gboolean sip_sec_requires_password(guint
 	/* Map authentication type to module initialization hook & name */
 	static sip_sec_password_func const auth_to_hook[] = {
 		sip_sec_password__NONE,      /* SIPE_AUTHENTICATION_TYPE_UNSET     */
 		sip_sec_password__Basic,     /* SIPE_AUTHENTICATION_TYPE_BASIC     */
 		sip_sec_password__NTLM,      /* SIPE_AUTHENTICATION_TYPE_NTLM      */
 		sip_sec_password__Kerberos,  /* SIPE_AUTHENTICATION_TYPE_KERBEROS  */
 		sip_sec_password__Negotiate, /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
 		sip_sec_password__TLS_DSK,   /* SIPE_AUTHENTICATION_TYPE_TLS_DSK   */
+		sip_sec_password__NONE,      /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */
 	};
 
 	/* If Single-Sign On is disabled then a password is required */
 	if (!sso)
 		return(TRUE);
 
 	/* Check if authentation method supports Single-Sign On */
 	return((*(auth_to_hook[authentication]))());
--- a/libpurple/protocols/sipe/core/sip-sec.h
+++ b/libpurple/protocols/sipe/core/sip-sec.h
@@ -1,14 +1,14 @@
 /**
  * @file sip-sec.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -30,27 +30,25 @@ typedef struct sip_sec_context *SipSecCo
 /**
  * Initializes Sipe security context.
  * Obtains cashed initial credentials (TGT for Kerberos) or requests new ones if required.
  * In former case domain/username/password information is unnecessary.
  *
  * @param type     (in) authentication type
  * @param sso      (in) @c TRUE if Single Sign-On should be used
  * @param http     (in) @c TRUE if HTTP, @c FALSE for SIP
- * @param domain   (in) NTLM Domain/Kerberos Realm (ignored for SSO)
  * @param username (in) user name (can be NULL)    (ignored for SSO)
  * @param password (in) password (can be NULL)     (ignored for SSO)
  *
  * @return context security context to store and pass between security method invocations
  */
 SipSecContext
 sip_sec_create_context(guint type,
 		       gboolean sso,
 		       gboolean http,
-		       const gchar *domain,
 		       const gchar *username,
 		       const gchar *password);
 
 /**
  * Obtains Service ticket (for Kerberos), base64 encodes it and provide as output.
  *
  * @param context (in) security context to pass between security method invocations
  * @param target (in) security target. Service principal name on case of Kerberos.
--- a/libpurple/protocols/sipe/core/sip-soap.c
+++ b/libpurple/protocols/sipe/core/sip-soap.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-soap.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -29,16 +29,17 @@
  *   - [MS-PRES]: http://msdn.microsoft.com/en-us/library/cc431501.aspx
  *
  */
 
 #include <glib.h>
 
 #include "sip-soap.h"
 #include "sip-transport.h"
+#include "sipe-backend.h"
 #include "sipe-core.h"
 #include "sipe-core-private.h"
 #include "sipe-utils.h"
 
 void sip_soap_raw_request_cb(struct sipe_core_private *sipe_private,
 			     const gchar *from,
 			     const gchar *soap,
 			     SoapTransCallback callback,
--- a/libpurple/protocols/sipe/core/sip-transport.c
+++ b/libpurple/protocols/sipe/core/sip-transport.c
@@ -1,14 +1,14 @@
 /**
  * @file sip-transport.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -41,16 +41,19 @@
  *
  * This module should support redirect internally. No escalations to higher
  * layers needed.
  *
  * NO SIP-messages (headers) composing and processing should be outside of
  * this module (!) Like headers: Via, Route, Contact, Authorization, etc.
  * It's all irrelevant to higher layer responsibilities.
  *
+ * Specification references:
+ *
+ *   - [MS-SIPAE]:    http://msdn.microsoft.com/en-us/library/cc431510.aspx
  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
 #include <stdlib.h>
 #include <string.h>
@@ -84,16 +87,17 @@ struct sip_auth {
 	const gchar *protocol;
 	gchar *realm;
 	gchar *sts_uri;
 	gchar *target;
 	guint version;
 	guint retries;
 	guint ntlm_num;
 	guint expires;
+	gboolean can_retry;
 };
 
 /* sip-transport.c private data */
 struct sip_transport {
 	struct sipe_transport_connection *connection;
 
 	gchar *server_name;
 	guint  server_port;
@@ -109,16 +113,17 @@ struct sip_transport {
 	guint cseq;
 	guint register_attempt;
 
 	guint keepalive_timeout;
 	time_t last_message;
 
 	gboolean processing_input;   /* whether full header received */
 	gboolean auth_incomplete;    /* whether authentication not completed */
+	gboolean auth_retry;         /* whether next authentication should be tried */
 	gboolean reregister_set;     /* whether reregister timer set */
 	gboolean reauthenticate_set; /* whether reauthenticate timer set */
 	gboolean subscribed;         /* whether subscribed to events, except buddies presence */
 	gboolean deregister;         /* whether in deregistration */
 };
 
 /* Keep in sync with sipe_transport_type! */
 static const char *transport_descriptor[] = { "", "tls", "tcp"};
@@ -141,16 +146,17 @@ static void sipe_auth_free(struct sip_au
 	g_free(auth->sts_uri);
 	auth->sts_uri = NULL;
 	g_free(auth->target);
 	auth->target = NULL;
 	auth->version = 0;
 	auth->type = SIPE_AUTHENTICATION_TYPE_UNSET;
 	auth->retries = 0;
 	auth->expires = 0;
+	auth->can_retry = FALSE;
 	g_free(auth->gssapi_data);
 	auth->gssapi_data = NULL;
 	sip_sec_destroy_context(auth->gssapi_context);
 	auth->gssapi_context = NULL;
 }
 
 static void sipe_make_signature(struct sipe_core_private *sipe_private,
 				struct sipmsg *msg)
@@ -163,44 +169,78 @@ static void sipe_make_signature(struct s
 		sipmsg_breakdown_parse(&msgbd, transport->registrar.realm, transport->registrar.target,
 				       transport->registrar.protocol);
 		msgbd.rand = g_strdup_printf("%08x", g_random_int());
 		transport->registrar.ntlm_num++;
 		msgbd.num = g_strdup_printf("%d", transport->registrar.ntlm_num);
 		signature_input_str = sipmsg_breakdown_get_string(transport->registrar.version, &msgbd);
 		if (signature_input_str != NULL) {
 			char *signature_hex = sip_sec_make_signature(transport->registrar.gssapi_context, signature_input_str);
+			g_free(msg->signature);
 			msg->signature = signature_hex;
+			g_free(msg->rand);
 			msg->rand = g_strdup(msgbd.rand);
+			g_free(msg->num);
 			msg->num = g_strdup(msgbd.num);
 			g_free(signature_input_str);
 		}
 		sipmsg_breakdown_free(&msgbd);
 	}
 }
 
 static const gchar *const auth_type_to_protocol[] = {
 	NULL,       /* SIPE_AUTHENTICATION_TYPE_UNSET     */
 	NULL,       /* SIPE_AUTHENTICATION_TYPE_BASIC     */
 	"NTLM",     /* SIPE_AUTHENTICATION_TYPE_NTLM      */
 	"Kerberos", /* SIPE_AUTHENTICATION_TYPE_KERBEROS  */
 	NULL,       /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
 	"TLS-DSK",  /* SIPE_AUTHENTICATION_TYPE_TLS_DSK   */
+	NULL,       /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */
 };
 #define AUTH_PROTOCOLS (sizeof(auth_type_to_protocol)/sizeof(gchar *))
 
 static gchar *msg_signature_to_auth(struct sip_auth *auth,
 				    struct sipmsg *msg)
 {
 	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));
 }
 
+static gboolean auth_can_retry(struct sip_transport *transport,
+			       const struct sip_auth *auth)
+{
+	/* NTLM is the scheme with lowest priority - don't retry */
+	gboolean retry =
+		auth->can_retry &&
+		(auth->type != SIPE_AUTHENTICATION_TYPE_NTLM);
+	if (retry)
+		transport->auth_retry = TRUE;
+	return(retry);
+}
+
+static void initialize_auth_retry(struct sipe_core_private *sipe_private,
+				  struct sip_auth *auth)
+{
+	struct sip_transport *transport = sipe_private->transport;
+
+	if (auth_can_retry(transport, auth)) {
+		if (auth->gssapi_context) {
+			/* need to drop context for retry */
+			sip_sec_destroy_context(auth->gssapi_context);
+			auth->gssapi_context = NULL;
+		}
+	} else {
+		sipe_backend_connection_error(SIPE_CORE_PUBLIC,
+					      SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+					      _("Failed to authenticate to server"));
+	}
+}
+
 static gchar *initialize_auth_context(struct sipe_core_private *sipe_private,
 				      struct sip_auth *auth,
 				      struct sipmsg *msg)
 {
 	struct sip_transport *transport = sipe_private->transport;
 	gchar *ret;
 	gchar *gssapi_data = NULL;
 	gchar *sign_str;
@@ -225,19 +265,17 @@ static gchar *initialize_auth_context(st
 							    &gssapi_data,
 							    &auth->expires);
 
 		/* If authentication is completed gssapi_data can be NULL */
 		if (!(status &&
 		      (sip_sec_context_is_ready(auth->gssapi_context) || gssapi_data))) {
 			SIPE_DEBUG_ERROR_NOFORMAT("initialize_auth_context: security context continuation failed");
 			g_free(gssapi_data);
-			sipe_backend_connection_error(SIPE_CORE_PUBLIC,
-						      SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
-						      _("Failed to authenticate to server"));
+			initialize_auth_retry(sipe_private, auth);
 			return NULL;
 		}
 
 	} else {
 		/* Create security context */
 		gpointer password = sipe_private->password;
 
 		/* For TLS-DSK the "password" is a certificate */
@@ -273,33 +311,31 @@ static gchar *initialize_auth_context(st
 				SIPE_DEBUG_INFO("initialize_auth_context: TLS-DSK certificate for target '%s' found.",
 						auth->target);
 			}
 		}
 
 		auth->gssapi_context = sip_sec_create_context(auth->type,
 							      SIPE_CORE_PRIVATE_FLAG_IS(SSO),
 							      FALSE, /* connection-less for SIP */
-							      sipe_private->authdomain ? sipe_private->authdomain : "",
 							      sipe_private->authuser,
 							      password);
 
 		if (auth->gssapi_context) {
 			sip_sec_init_context_step(auth->gssapi_context,
 						  auth->target,
 						  NULL,
 						  &gssapi_data,
 						  &(auth->expires));
 		}
 
-		if (!gssapi_data || !auth->gssapi_context) {
-			g_free(gssapi_data);
-			sipe_backend_connection_error(SIPE_CORE_PUBLIC,
-						      SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
-						      _("Failed to authenticate to server"));
+		/* if auth->gssapi_context is NULL then gssapi_data is still NULL */
+		if (!gssapi_data) {
+			SIPE_DEBUG_ERROR_NOFORMAT("initialize_auth_context: security context initialization failed");
+			initialize_auth_retry(sipe_private, auth);
 			return NULL;
 		}
 	}
 
 	if ((auth->version > 3) &&
 	    sip_sec_context_is_ready(auth->gssapi_context)) {
 		sipe_make_signature(sipe_private, msg);
 		sign_str = g_strdup_printf(", crand=\"%s\", cnum=\"%s\", response=\"%s\"",
@@ -567,17 +603,17 @@ void sip_transport_response(struct sipe_
 			    const char *text,
 			    const char *body)
 {
 	gchar *name;
 	gchar *value;
 	GString *outstr = g_string_new("");
 	gchar *contact;
 	GSList *tmp;
-	const gchar *keepers[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL };
+	static const gchar *keepers[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL };
 
 	/* Can return NULL! */
 	contact = get_contact(sipe_private);
 	if (contact) {
 		sipmsg_add_header(msg, "Contact", contact);
 		g_free(contact);
 	}
 
@@ -928,20 +964,22 @@ void sip_transport_update(struct sipe_co
 			      dialog->with,
 			      NULL,
 			      NULL,
 			      dialog,
 			      callback);
 }
 
 static const gchar *get_auth_header(struct sipe_core_private *sipe_private,
-				    struct sip_auth *auth,
+				    guint type,
 				    struct sipmsg *msg)
 {
-	auth->type     = sipe_private->authentication_type;
+	struct sip_auth *auth = &sipe_private->transport->registrar;
+
+	auth->type     = type;
 	auth->protocol = auth_type_to_protocol[auth->type];
 
 	return(sipmsg_find_auth_header(msg, auth->protocol));
 }
 
 static void do_register(struct sipe_core_private *sipe_private,
 			gboolean deregister);
 
@@ -952,16 +990,17 @@ static void do_reauthenticate_cb(struct 
 
 	/* register again when security token expires */
 	/* we have to start a new authentication as the security token
 	 * is almost expired by sending a not signed REGISTER message */
 	SIPE_DEBUG_INFO_NOFORMAT("do a full reauthentication");
 	sipe_auth_free(&transport->registrar);
 	sipe_auth_free(&transport->proxy);
 	sipe_schedule_cancel(sipe_private, "<registration>");
+	transport->auth_retry     = TRUE;
 	transport->reregister_set = FALSE;
 	transport->register_attempt = 0;
 	do_register(sipe_private, FALSE);
 	transport->reauthenticate_set = FALSE;
 }
 
 static void sip_transport_default_contact(struct sipe_core_private *sipe_private)
 {
@@ -1015,58 +1054,78 @@ static gboolean process_register_respons
 				const gchar *contact_hdr;
 				const gchar *auth_hdr;
 				gchar *gruu = NULL;
 				gchar *uuid;
 				gchar *timeout;
 				const gchar *server_hdr = sipmsg_find_header(msg, "Server");
 
 				if (!transport->reregister_set) {
+					/* Schedule re-register 30 seconds before expiration */
+					if (expires > 30)
+						expires -= 30;
 					sip_transport_set_reregister(sipe_private,
 								     expires);
 					transport->reregister_set = TRUE;
 				}
 
 				if (server_hdr && !transport->server_version) {
 					transport->server_version = g_strdup(server_hdr);
 					g_free(transport->user_agent);
 					transport->user_agent = NULL;
 				}
 
-				auth_hdr = get_auth_header(sipe_private, &transport->registrar, msg);
+				auth_hdr = sipmsg_find_auth_header(msg,
+								   transport->registrar.protocol);
 				if (auth_hdr) {
 					SIPE_DEBUG_INFO("process_register_response: Auth header: %s", auth_hdr);
 					fill_auth(auth_hdr, &transport->registrar);
 				}
 
 				if (!transport->reauthenticate_set) {
+					/* [MS-SIPAE] Section 3.2.2 Timers
+					 *
+					 * When the ... authentication handshake completes
+					 * and the SA enters the "established" state, the
+					 * SIP protocol client MUST start an SA expiration
+					 * timer.
+					 * ...
+					 * The expiration timer value is the lesser of
+					 *
+					 *   - Kerberos: the service ticket expiry time
+					 *   - TLS-DSK:  the certificate expiration time
+					 *
+					 * and eight hours, further reduced by some buffer
+					 * time.
+					 * ...
+					 * The protocol client MUST choose a sufficient
+					 * buffer time to allow for the ... authentication
+					 * handshake that reestablishes the SA to complete
+					 * ... This value SHOULD be five (5) minutes or
+					 * longer.
+					 */
 					guint reauth_timeout = transport->registrar.expires;
 
 					SIPE_DEBUG_INFO_NOFORMAT("process_register_response: authentication handshake completed successfully");
 
-					/* Does authentication scheme provide valid expiration time? */
-					if (reauth_timeout == 0) {
-						SIPE_DEBUG_INFO_NOFORMAT("process_register_response: no expiration time - using default of 8 hours");
+					if ((reauth_timeout == 0) ||
+					    (reauth_timeout >  8 * 60 * 60))
 						reauth_timeout = 8 * 60 * 60;
-					}
-
-					/* schedule reauthentication 5 minutes before expiration */
 					if (reauth_timeout > 5 * 60)
 						reauth_timeout -= 5 * 60;
+
 					sipe_schedule_seconds(sipe_private,
 							      "<+reauthentication>",
 							      NULL,
 							      reauth_timeout,
 							      do_reauthenticate_cb,
 							      NULL);
 					transport->reauthenticate_set = TRUE;
 				}
 
-				sipe_backend_connection_completed(SIPE_CORE_PUBLIC);
-
 				uuid = get_uuid(sipe_private);
 
 				// There can be multiple Contact headers (one per location where the user is logged in) so
 				// make sure to only get the one for this uuid
 				for (i = 0; (contact_hdr = sipmsg_find_header_instance (msg, "Contact", i)); i++) {
 					gchar * valid_contact = sipmsg_find_part_of_header (contact_hdr, uuid, NULL, NULL);
 					if (valid_contact) {
 						gruu = sipmsg_find_part_of_header(contact_hdr, "gruu=\"", "\"", NULL);
@@ -1120,16 +1179,18 @@ static gboolean process_register_respons
 							SIPE_CORE_PRIVATE_FLAG_SET(REMOTE_USER);
 							SIPE_DEBUG_INFO_NOFORMAT("ms-user-logon-data: RemoteUser (connected "
 										 "via Edge Server)");
 						}
 					}
                                         hdr = g_slist_next(hdr);
                                 }
 
+				sipe_backend_connection_completed(SIPE_CORE_PUBLIC);
+
 				/* rejoin open chats to be able to use them by continue to send messages */
 				sipe_backend_chat_rejoin_all(SIPE_CORE_PUBLIC);
 
 				/* subscriptions, done only once */
 				if (!transport->subscribed) {
 					sipe_subscription_self_events(sipe_private);
 					transport->subscribed = TRUE;
 				}
@@ -1189,35 +1250,93 @@ static gboolean process_register_respons
 					SIPE_DEBUG_INFO("process_register_response: redirected to host %s port %d transport %d",
 							hostname, port, transport_type);
 				}
 				g_free(redirect);
 			}
 			break;
 		case 401:
 		        {
-				const char *auth_hdr;
+				const char *auth_hdr = NULL;
 
 				SIPE_DEBUG_INFO("process_register_response: REGISTER retries %d", transport->registrar.retries);
 
 				if (transport->reauthenticate_set) {
 					SIPE_DEBUG_ERROR_NOFORMAT("process_register_response: RE-REGISTER rejected, triggering re-authentication");
 					do_reauthenticate_cb(sipe_private, NULL);
 					return TRUE;
 				}
 
 				if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) {
-					SIPE_DEBUG_INFO_NOFORMAT("process_register_response: authentication handshake failed - giving up.");
-					sipe_backend_connection_error(SIPE_CORE_PUBLIC,
-								      SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
-								      _("Authentication failed"));
-					return TRUE;
+					struct sip_auth *auth = &transport->registrar;
+
+					/* NTLM is the scheme with lowest priority - don't retry */
+					if (auth_can_retry(transport, auth)) {
+						guint failed = auth->type;
+						SIPE_DEBUG_INFO_NOFORMAT("process_register_response: authentication handshake failed - trying next authentication scheme.");
+						sipe_auth_free(auth);
+						auth->type = failed;
+					} else {
+						SIPE_DEBUG_INFO_NOFORMAT("process_register_response: authentication handshake failed - giving up.");
+						sipe_backend_connection_error(SIPE_CORE_PUBLIC,
+									      SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+									      _("Authentication failed"));
+						return TRUE;
+					}
 				}
 
-				auth_hdr = get_auth_header(sipe_private, &transport->registrar, msg);
+				if (sipe_private->authentication_type == SIPE_AUTHENTICATION_TYPE_AUTOMATIC) {
+					struct sip_auth *auth = &transport->registrar;
+					guint try             = auth->type;
+
+					while (!auth_hdr) {
+						/*
+						 * Determine next authentication
+						 * scheme in priority order
+						 */
+						if (transport->auth_retry)
+							switch (try) {
+							case SIPE_AUTHENTICATION_TYPE_UNSET:
+								try = SIPE_AUTHENTICATION_TYPE_TLS_DSK;
+								break;
+
+							case SIPE_AUTHENTICATION_TYPE_TLS_DSK:
+#if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI)
+								try = SIPE_AUTHENTICATION_TYPE_KERBEROS;
+								break;
+
+							case SIPE_AUTHENTICATION_TYPE_KERBEROS:
+#endif
+								try = SIPE_AUTHENTICATION_TYPE_NTLM;
+								break;
+
+							default:
+								try = SIPE_AUTHENTICATION_TYPE_UNSET;
+								break;
+							}
+
+						auth->can_retry = (try != SIPE_AUTHENTICATION_TYPE_UNSET);
+
+						if (!auth->can_retry) {
+							SIPE_DEBUG_INFO_NOFORMAT("process_register_response: no more authentication schemes to try");
+							break;
+						}
+
+						auth_hdr = get_auth_header(sipe_private,
+									   try,
+									   msg);
+					}
+
+					transport->auth_retry = FALSE;
+
+				} else
+					auth_hdr = get_auth_header(sipe_private,
+								   sipe_private->authentication_type,
+								   msg);
+
 				if (!auth_hdr) {
 					sipe_backend_connection_error(SIPE_CORE_PUBLIC,
 								      SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
 								      _("Incompatible authentication scheme chosen"));
 					return TRUE;
 				}
 				SIPE_DEBUG_INFO("process_register_response: Auth header: %s", auth_hdr);
 				fill_auth(auth_hdr, &transport->registrar);
@@ -1624,28 +1743,36 @@ static void sip_transport_input(struct s
 			msg->body = dummy;
 			cur += msg->bodylen;
 			sipe_utils_message_debug("SIP",
 						 conn->buffer,
 						 msg->body,
 						 FALSE);
 			sipe_utils_shrink_buffer(conn, cur);
 		} else {
-			if (msg){
+			if (msg) {
 				SIPE_DEBUG_INFO("sipe_transport_input: body too short (%d < %d, strlen %d) - ignoring message", remainder, msg->bodylen, (int)strlen(conn->buffer));
 				sipmsg_free(msg);
                         }
 
 			/* restore header for next try */
 			cur[-2] = '\r';
 			return;
 		}
 
-		// Verify the signature before processing it
-		if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) {
+		/* Fatal header parse error? */
+		if (msg->response == SIPMSG_RESPONSE_FATAL_ERROR) {
+			/* can't proceed -> drop connection */
+			sipe_backend_connection_error(SIPE_CORE_PUBLIC,
+						      SIPE_CONNECTION_ERROR_NETWORK,
+						      _("Corrupted message received"));
+			transport->processing_input = FALSE;
+
+		/* Verify the signature before processing it */
+		} else if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) {
 			struct sipmsg_breakdown msgbd;
 			gchar *signature_input_str;
 			gchar *rspauth;
 			msgbd.msg = msg;
 			sipmsg_breakdown_parse(&msgbd, transport->registrar.realm, transport->registrar.target,
 					       transport->registrar.protocol);
 			signature_input_str = sipmsg_breakdown_get_string(transport->registrar.version, &msgbd);
 
@@ -1656,16 +1783,17 @@ static void sip_transport_input(struct s
 					SIPE_DEBUG_INFO_NOFORMAT("sip_transport_input: signature of incoming message validated");
 					process_input_message(sipe_private, msg);
 					/* transport is invalid after redirect */
 				} else {
 					SIPE_DEBUG_INFO_NOFORMAT("sip_transport_input: signature of incoming message is invalid.");
 					sipe_backend_connection_error(SIPE_CORE_PUBLIC,
 								      SIPE_CONNECTION_ERROR_NETWORK,
 								      _("Invalid message signature received"));
+					transport->processing_input = FALSE;
 				}
 			} else if ((msg->response == 401) ||
 				   sipe_strequal(msg->method, "REGISTER")) {
 				/* a) Retry non-REGISTER requests with updated authentication */
 				/* b) We must always process REGISTER responses */
 				process_input_message(sipe_private, msg);
 			} else {
 				/* OCS sends provisional messages that are *not* signed */
@@ -1747,16 +1875,17 @@ static void sipe_server_register(struct 
 		(type == SIPE_TRANSPORT_TLS) ? 5061 : 5060,
 		sipe_private,
 		sip_transport_connected,
 		sip_transport_input,
 		sip_transport_error
 	};
 	struct sip_transport *transport = g_new0(struct sip_transport, 1);
 
+	transport->auth_retry   = TRUE;
 	transport->server_name  = server_name;
 	transport->server_port  = setup.server_port;
 	transport->connection   = sipe_backend_transport_connect(SIPE_CORE_PUBLIC,
 								 &setup);
 	sipe_private->transport = transport;
 }
 
 struct sip_service_data {
--- a/libpurple/protocols/sipe/core/sipe-buddy.c
+++ b/libpurple/protocols/sipe/core/sipe-buddy.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-buddy.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -514,36 +514,41 @@ void sipe_buddy_update_finish(struct sip
 }
 
 gchar *sipe_core_buddy_status(struct sipe_core_public *sipe_public,
 			      const gchar *uri,
 			      guint activity,
 			      const gchar *status_text)
 {
 	struct sipe_buddy *sbuddy;
-	const char *activity_str;
+	GString *status;
 
 	if (!sipe_public) return NULL; /* happens on pidgin exit */
 
 	sbuddy = sipe_buddy_find_by_uri(SIPE_CORE_PRIVATE, uri);
 	if (!sbuddy) return NULL;
 
-	activity_str = sbuddy->activity ? sbuddy->activity :
-		(activity == SIPE_ACTIVITY_BUSY) || (activity == SIPE_ACTIVITY_BRB) ?
-		status_text : NULL;
+	status = g_string_new(sbuddy->activity ? sbuddy->activity :
+			      (activity == SIPE_ACTIVITY_BUSY) || (activity == SIPE_ACTIVITY_BRB) ?
+			      status_text : NULL);
 
-	if (activity_str && sbuddy->note) {
-		return g_strdup_printf("%s - <i>%s</i>", activity_str, sbuddy->note);
-	} else if (activity_str) {
-		return g_strdup(activity_str);
-	} else if (sbuddy->note) {
-		return g_strdup_printf("<i>%s</i>", sbuddy->note);
-	} else {
-		return NULL;
+	if (sbuddy->is_mobile) {
+		if (status->len)
+			g_string_append(status, " - ");
+		g_string_append(status, _("Mobile"));
 	}
+
+	if (sbuddy->note) {
+		if (status->len)
+			g_string_append(status, " - ");
+		g_string_append(status, sbuddy->note);
+	}
+
+	/* return NULL instead of empty status text */
+	return(g_string_free(status, status->len ? FALSE : TRUE));
 }
 
 gchar *sipe_buddy_get_alias(struct sipe_core_private *sipe_private,
 			    const gchar *with)
 {
 	sipe_backend_buddy pbuddy;
 	gchar *alias = NULL;
 	if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) {
@@ -903,19 +908,24 @@ struct ms_dlx_data {
 	struct sipe_svc_session *session;
 	gchar *wsse_security;
 	struct sipe_backend_search_token *token;
 	/* must call ms_dlx_free() */
 	void (*failed_callback)(struct sipe_core_private *sipe_private,
 				struct ms_dlx_data *mdd);
 };
 
+static void free_search_rows(GSList *search_rows)
+{
+	sipe_utils_slist_free_full(search_rows, g_free);
+}
+
 static void ms_dlx_free(struct ms_dlx_data *mdd)
 {
-	sipe_utils_slist_free_full(mdd->search_rows, g_free);
+	free_search_rows(mdd->search_rows);
 	sipe_svc_session_close(mdd->session);
 	g_free(mdd->other);
 	g_free(mdd->wsse_security);
 	g_free(mdd);
 }
 
 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
 #define DLX_SEARCH_ITEM							\
@@ -927,27 +937,41 @@ static void ms_dlx_free(struct ms_dlx_da
 static gchar * prepare_buddy_search_query(GSList *query_rows, gboolean use_dlx) {
 	gchar **attrs = g_new(gchar *, (g_slist_length(query_rows) / 2) + 1);
 	guint i = 0;
 	gchar *query = NULL;
 
 	while (query_rows) {
 		gchar *attr;
 		gchar *value;
+		gchar *tmp = NULL;
 
 		attr = query_rows->data;
 		query_rows = g_slist_next(query_rows);
 		value = query_rows->data;
 		query_rows = g_slist_next(query_rows);
 
-		if (!attr || !value)
+		if (!value)
 			break;
 
+		/*
+		 * Special value for SIP ID
+		 *
+		 * Active Directory seems only to be able to search for
+		 * SIP URIs. Make sure search string starts with "sip:".
+		 */
+		if (!attr) {
+			attr = "msRTCSIP-PrimaryUserAddress";
+			if (!use_dlx)
+				value = tmp = sip_uri(value);
+		}
+
 		attrs[i++] = g_markup_printf_escaped(use_dlx ? DLX_SEARCH_ITEM : SIPE_SOAP_SEARCH_ROW,
 						     attr, value);
+		g_free(tmp);
 	}
 	attrs[i] = NULL;
 
 	if (i) {
 		query = g_strjoinv(NULL, attrs);
 		SIPE_DEBUG_INFO("prepare_buddy_search_query: rows:\n%s",
 				query ? query : "");
 	}
@@ -962,38 +986,58 @@ static void ms_dlx_webticket(struct sipe
 			     const gchar *auth_uri,
 			     const gchar *wsse_security,
 			     SIPE_UNUSED_PARAMETER const gchar *failure_msg,
 			     gpointer callback_data)
 {
 	struct ms_dlx_data *mdd = callback_data;
 
 	if (wsse_security) {
-		gchar *query = prepare_buddy_search_query(mdd->search_rows, TRUE);
+		guint length = g_slist_length(mdd->search_rows);
+		gchar *search;
 
 		SIPE_DEBUG_INFO("ms_dlx_webticket: got ticket for %s",
 				base_uri);
 
+		if (length > 0) {
+			/* complex search */
+			gchar *query = prepare_buddy_search_query(mdd->search_rows, TRUE);
+			search = g_strdup_printf("<ChangeSearch xmlns:q1=\"DistributionListExpander\" soapenc:arrayType=\"q1:AbEntryRequest.ChangeSearchQuery[%d]\">"
+						 " %s"
+						 "</ChangeSearch>",
+						 length / 2,
+						 query);
+			g_free(query);
+		} else {
+			/* simple search */
+			search = g_strdup_printf("<BasicSearch>"
+						 " <SearchList>c,company,displayName,givenName,mail,mailNickname,msRTCSIP-PrimaryUserAddress,sn</SearchList>"
+						 " <Value>%s</Value>"
+						 " <Verb>BeginsWith</Verb>"
+						 "</BasicSearch>",
+						 mdd->other);
+		}
+
 		if (sipe_svc_ab_entry_request(sipe_private,
 					      mdd->session,
 					      auth_uri,
 					      wsse_security,
-					      query,
-					      g_slist_length(mdd->search_rows) / 2,
+					      search,
 					      mdd->max_returns,
 					      mdd->callback,
 					      mdd)) {
 
 			/* keep webticket security token for potential further use */
+			g_free(mdd->wsse_security);
 			mdd->wsse_security = g_strdup(wsse_security);
 
 			/* callback data passed down the line */
 			mdd = NULL;
 		}
-		g_free(query);
+		g_free(search);
 
 	} else {
 		/* no ticket: this will show the minmum information */
 		SIPE_DEBUG_ERROR("ms_dlx_webticket: no web ticket for %s",
 				 base_uri);
 	}
 
 	if (mdd)
@@ -1010,20 +1054,20 @@ static void ms_dlx_webticket_request(str
 				    ms_dlx_webticket,
 				    mdd)) {
 		SIPE_DEBUG_ERROR("ms_dlx_webticket_request: couldn't request webticket for %s",
 				 sipe_private->dlx_uri);
 		mdd->failed_callback(sipe_private, mdd);
 	}
 }
 
-static void search_contacts_finalize(struct sipe_core_private *sipe_private,
-				     struct sipe_backend_search_results *results,
-				     guint match_count,
-				     gboolean more)
+void sipe_buddy_search_contacts_finalize(struct sipe_core_private *sipe_private,
+					 struct sipe_backend_search_results *results,
+					 guint match_count,
+					 gboolean more)
 {
 	gchar *secondary = g_strdup_printf(
 		dngettext(PACKAGE_NAME,
 			  "Found %d contact%s:",
 			  "Found %d contacts%s:", match_count),
 		match_count, more ? _(" (more matched your query)") : "");
 
 	sipe_backend_search_results_finalize(SIPE_CORE_PUBLIC,
@@ -1047,22 +1091,38 @@ static void search_ab_entry_response(str
 		GHashTable *found;
 
 		SIPE_DEBUG_INFO("search_ab_entry_response: received valid SOAP message from service %s",
 				uri);
 
 		/* any matches? */
 		node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry");
 		if (!node) {
-			SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches");
-			sipe_backend_search_failed(SIPE_CORE_PUBLIC,
-						   mdd->token,
-						   _("No contacts found"));
-			ms_dlx_free(mdd);
-			return;
+			/* try again with simple search, if possible */
+			if (mdd->other && mdd->search_rows) {
+				SIPE_DEBUG_INFO_NOFORMAT("search_ab_entry_response: no matches, retrying with simple search");
+
+				/* throw away original search query */
+				free_search_rows(mdd->search_rows);
+				mdd->search_rows = NULL;
+
+				ms_dlx_webticket_request(sipe_private, mdd);
+
+				/* callback data passed down the line */
+				return;
+
+			} else {
+				SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: no matches");
+
+				sipe_backend_search_failed(SIPE_CORE_PUBLIC,
+							   mdd->token,
+							   _("No contacts found"));
+				ms_dlx_free(mdd);
+				return;
+			}
 		}
 
 		/* OK, we found something - show the results to the user */
 		results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC,
 							    mdd->token);
 		if (!results) {
 			SIPE_DEBUG_ERROR_NOFORMAT("search_ab_entry_response: Unable to display the search results.");
 			sipe_backend_search_failed(SIPE_CORE_PUBLIC,
@@ -1137,19 +1197,19 @@ static void search_ab_entry_response(str
 
 			g_free(email);
 			g_free(country);
 			g_free(company);
 			g_free(displayname);
 			g_free(sip_uri);
 		}
 
-		search_contacts_finalize(sipe_private, results,
-					 g_hash_table_size(found),
-					 FALSE);
+		sipe_buddy_search_contacts_finalize(sipe_private, results,
+						    g_hash_table_size(found),
+						    FALSE);
 		g_hash_table_destroy(found);
 		ms_dlx_free(mdd);
 
 	} else {
 		mdd->failed_callback(sipe_private, mdd);
 	}
 }
 
@@ -1225,91 +1285,133 @@ static gboolean process_search_contact_r
 	}
 
 	if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
 		char *data = sipe_xml_data(mrow);
 		more = (g_ascii_strcasecmp(data, "true") == 0);
 		g_free(data);
 	}
 
-	search_contacts_finalize(sipe_private, results, match_count, more);
+	sipe_buddy_search_contacts_finalize(sipe_private, results, match_count, more);
 	sipe_xml_free(searchResults);
 
 	return(TRUE);
 }
 
 static void search_soap_request(struct sipe_core_private *sipe_private,
-				struct sipe_backend_search_token *token,
+				GDestroyNotify destroy,
+				void *data,
+				guint max,
+				SoapTransCallback callback,
 				GSList *search_rows)
 {
 	gchar *query = prepare_buddy_search_query(search_rows, FALSE);
 	struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
 
-	payload->data = token;
+	payload->destroy = destroy;
+	payload->data    = data;
 
 	sip_soap_directory_search(sipe_private,
-				  100,
+				  max,
 				  query,
-				  process_search_contact_response,
+				  callback,
 				  payload);
 	g_free(query);
 }
 
 static void search_ab_entry_failed(struct sipe_core_private *sipe_private,
 				   struct ms_dlx_data *mdd)
 {
 	/* error using [MS-DLX] server, retry using Active Directory */
-	search_soap_request(sipe_private, mdd->token, mdd->search_rows);
+	if (mdd->search_rows)
+		search_soap_request(sipe_private,
+				    NULL,
+				    mdd->token,
+				    100,
+				    process_search_contact_response,
+				    mdd->search_rows);
 	ms_dlx_free(mdd);
 }
 
 void sipe_core_buddy_search(struct sipe_core_public *sipe_public,
 			    struct sipe_backend_search_token *token,
 			    const gchar *given_name,
 			    const gchar *surname,
 			    const gchar *email,
+			    const gchar *sipid,
 			    const gchar *company,
 			    const gchar *country)
 {
-	GSList *query_rows = NULL;
+	struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
 
-#define ADD_QUERY_ROW(attr, val)                                               \
-	if (val) {                                                             \
-		query_rows = g_slist_append(query_rows, g_strdup(attr));       \
-		query_rows = g_slist_append(query_rows, g_strdup(val));        \
-	}
+	/* Lync 2013 or newer: use UCS if contacts are migrated */
+	if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) &&
+	    sipe_ucs_is_migrated(sipe_private)) {
 
-	ADD_QUERY_ROW("givenName", given_name);
-	ADD_QUERY_ROW("sn",        surname);
-	ADD_QUERY_ROW("mail",      email);
-	ADD_QUERY_ROW("company",   company);
-	ADD_QUERY_ROW("c",         country);
+		sipe_ucs_search(sipe_private,
+				token,
+				given_name,
+				surname,
+				email,
+				sipid,
+				company,
+				country);
 
-	if (query_rows) {
-		if (SIPE_CORE_PRIVATE->dlx_uri != NULL) {
-			struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
+	} else {
+		GSList *query_rows  = NULL;
+		guint count         = 0;
+		const gchar *simple = NULL;
+
+#define ADD_QUERY_ROW(attr, val)                                                 \
+		if (val) {                                                       \
+			query_rows = g_slist_append(query_rows, g_strdup(attr)); \
+			query_rows = g_slist_append(query_rows, g_strdup(val));  \
+			simple = val;                                            \
+			count++;                                                 \
+		}
 
-			mdd->search_rows     = query_rows;
-			mdd->max_returns     = 100;
-			mdd->callback        = search_ab_entry_response;
-			mdd->failed_callback = search_ab_entry_failed;
-			mdd->session         = sipe_svc_session_start();
-			mdd->token           = token;
+		ADD_QUERY_ROW("givenName", given_name);
+		ADD_QUERY_ROW("sn",        surname);
+		ADD_QUERY_ROW("mail",      email);
+		/* prepare_buddy_search_query() interprets NULL as SIP ID */
+		ADD_QUERY_ROW(NULL,        sipid);
+		ADD_QUERY_ROW("company",   company);
+		ADD_QUERY_ROW("c",         country);
 
-			ms_dlx_webticket_request(SIPE_CORE_PRIVATE, mdd);
+		if (query_rows) {
+			if (sipe_private->dlx_uri != NULL) {
+				struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
 
-		} else {
-			/* no [MS-DLX] server, use Active Directory search instead */
-			search_soap_request(SIPE_CORE_PRIVATE, token, query_rows);
-			sipe_utils_slist_free_full(query_rows, g_free);
-		}
-	} else
-		sipe_backend_search_failed(sipe_public,
-					   token,
-					   _("Invalid contact search query"));
+				mdd->search_rows     = query_rows;
+				/* user entered only one search string, remember that one */
+				if (count == 1)
+					mdd->other   = g_strdup(simple);
+				mdd->max_returns     = 100;
+				mdd->callback        = search_ab_entry_response;
+				mdd->failed_callback = search_ab_entry_failed;
+				mdd->session         = sipe_svc_session_start();
+				mdd->token           = token;
+
+				ms_dlx_webticket_request(sipe_private, mdd);
+
+			} else {
+				/* no [MS-DLX] server, use Active Directory search instead */
+				search_soap_request(sipe_private,
+						    NULL,
+						    token,
+						    100,
+						    process_search_contact_response,
+						    query_rows);
+				free_search_rows(query_rows);
+			}
+		} else
+			sipe_backend_search_failed(sipe_public,
+						   token,
+						   _("Invalid contact search query"));
+	}
 }
 
 static void get_info_finalize(struct sipe_core_private *sipe_private,
 			      struct sipe_backend_buddy_info *info,
 			      const gchar *uri,
 			      const gchar *server_alias,
 			      const gchar *email)
 {
@@ -1609,71 +1711,60 @@ static gboolean process_get_info_respons
 
 	return TRUE;
 }
 
 static void get_info_ab_entry_failed(struct sipe_core_private *sipe_private,
 				     struct ms_dlx_data *mdd)
 {
 	/* error using [MS-DLX] server, retry using Active Directory */
-	gchar *query = prepare_buddy_search_query(mdd->search_rows, FALSE);
-	struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
-
-	payload->destroy = g_free;
-	payload->data = mdd->other;
+	search_soap_request(sipe_private,
+			    g_free,
+			    mdd->other,
+			    1,
+			    process_get_info_response,
+			    mdd->search_rows);
 	mdd->other = NULL;
+	ms_dlx_free(mdd);
+}
 
-	sip_soap_directory_search(sipe_private,
-							  1,
-							  query,
-							  process_get_info_response,
-							  payload);
-
-	ms_dlx_free(mdd);
-	g_free(query);
+static GSList *search_rows_for_uri(const gchar *uri)
+{
+	/* prepare_buddy_search_query() interprets NULL as SIP ID */
+	GSList *l = g_slist_append(NULL, NULL);
+	return(g_slist_append(l, g_strdup(uri)));
 }
 
 void sipe_core_buddy_get_info(struct sipe_core_public *sipe_public,
 			      const gchar *who)
 {
 	struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
+	GSList *search_rows = search_rows_for_uri(who);
 
 	if (sipe_private->dlx_uri) {
 		struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
 
-		mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
-		mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(who));
-
+		mdd->search_rows     = search_rows;
 		mdd->other           = g_strdup(who);
 		mdd->max_returns     = 1;
 		mdd->callback        = get_info_ab_entry_response;
 		mdd->failed_callback = get_info_ab_entry_failed;
 		mdd->session         = sipe_svc_session_start();
 
 		ms_dlx_webticket_request(sipe_private, mdd);
 
 	} else {
 		/* no [MS-DLX] server, use Active Directory search instead */
-		gchar *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW,
-						     "msRTCSIP-PrimaryUserAddress",
-						     who);
-		struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
-
-		SIPE_DEBUG_INFO("sipe_core_buddy_get_info: row: %s",
-				row ? row : "");
-
-		payload->destroy = g_free;
-		payload->data = g_strdup(who);
-
-		sip_soap_directory_search(sipe_private,
-					  1,
-					  row,
-					  process_get_info_response,
-					  payload);
-		g_free(row);
+		search_soap_request(sipe_private,
+				    g_free,
+				    g_strdup(who),
+				    1,
+				    process_get_info_response,
+				    search_rows);
+		free_search_rows(search_rows);
 	}
 }
 
 static void photo_response_data_free(struct photo_response_data *data)
 {
 	g_free(data->who);
 	g_free(data->photo_hash);
 	if (data->request) {
@@ -1733,27 +1824,59 @@ static gchar *create_x_ms_webticket_head
 			wsse_security_base64);
 
 	g_free(assertion);
 	g_free(wsse_security_base64);
 
 	return x_ms_webticket_header;
 }
 
+void sipe_buddy_update_photo(struct sipe_core_private *sipe_private,
+			     const gchar *uri,
+			     const gchar *photo_hash,
+			     const gchar *photo_url,
+			     const gchar *headers)
+{
+	const gchar *photo_hash_old =
+		sipe_backend_buddy_get_photo_hash(SIPE_CORE_PUBLIC, uri);
+
+	if (!sipe_strequal(photo_hash, photo_hash_old)) {
+		struct photo_response_data *data = g_new(struct photo_response_data, 1);
+
+		SIPE_DEBUG_INFO("sipe_buddy_update_photo: who '%s' url '%s' hash '%s'",
+				uri, photo_url, photo_hash);
+
+		data->who        = g_strdup(uri);
+		data->photo_hash = g_strdup(photo_hash);
+
+		data->request = sipe_http_request_get(sipe_private,
+						      photo_url,
+						      headers,
+						      process_buddy_photo_response,
+						      data);
+
+		if (data->request) {
+			sipe_private->buddies->pending_photo_requests =
+				g_slist_append(sipe_private->buddies->pending_photo_requests, data);
+			sipe_http_request_ready(data->request);
+		} else {
+			photo_response_data_free(data);
+		}
+	}
+}
+
 static void get_photo_ab_entry_response(struct sipe_core_private *sipe_private,
 					const gchar *uri,
 					SIPE_UNUSED_PARAMETER const gchar *raw,
 					sipe_xml *soap_body,
 					gpointer callback_data)
 {
 	struct ms_dlx_data *mdd = callback_data;
 	gchar *photo_rel_path = NULL;
 	gchar *photo_hash = NULL;
-	const gchar *photo_hash_old =
-		sipe_backend_buddy_get_photo_hash(SIPE_CORE_PUBLIC, mdd->other);
 
 	if (soap_body) {
 		const sipe_xml *node;
 
 		SIPE_DEBUG_INFO("get_photo_ab_entry_response: received valid SOAP message from service %s",
 				uri);
 
 		for (node = sipe_xml_child(soap_body, "Body/SearchAbEntryResponse/SearchAbEntryResult/Items/AbEntry/Attributes/Attribute");
@@ -1774,40 +1897,26 @@ static void get_photo_ab_entry_response(
 				}
 			}
 
 			g_free(value);
 			g_free(name);
 		}
 	}
 
-	if (sipe_private->addressbook_uri && photo_rel_path &&
-	    photo_hash && !sipe_strequal(photo_hash, photo_hash_old)) {
+	if (sipe_private->addressbook_uri && photo_rel_path && photo_hash) {
 		gchar *photo_url = g_strdup_printf("%s/%s",
 				sipe_private->addressbook_uri, photo_rel_path);
 		gchar *x_ms_webticket_header = create_x_ms_webticket_header(mdd->wsse_security);
 
-		struct photo_response_data *data = g_new(struct photo_response_data, 1);
-		data->who = g_strdup(mdd->other);
-		data->photo_hash = photo_hash;
-		photo_hash = NULL;
-
-		data->request = sipe_http_request_get(sipe_private,
-						      photo_url,
-						      x_ms_webticket_header,
-						      process_buddy_photo_response,
-						      data);
-
-		if (data->request) {
-			sipe_private->buddies->pending_photo_requests =
-				g_slist_append(sipe_private->buddies->pending_photo_requests, data);
-			sipe_http_request_ready(data->request);
-		} else {
-			photo_response_data_free(data);
-		}
+		sipe_buddy_update_photo(sipe_private,
+					mdd->other,
+					photo_hash,
+					photo_url,
+					x_ms_webticket_header);
 
 		g_free(x_ms_webticket_header);
 		g_free(photo_url);
 	}
 
 	g_free(photo_rel_path);
 	g_free(photo_hash);
 	ms_dlx_free(mdd);
@@ -1819,29 +1928,28 @@ static void get_photo_ab_entry_failed(SI
 	ms_dlx_free(mdd);
 }
 
 static void buddy_fetch_photo(struct sipe_core_private *sipe_private,
 			      const gchar *uri)
 {
         if (sipe_backend_uses_photo()) {
 
-		/* Lync 2013 or newer: use UCS */
-		if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013)) {
+		/* Lync 2013 or newer: use UCS if contacts are migrated */
+		if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) &&
+		    sipe_ucs_is_migrated(sipe_private)) {
 
 			sipe_ucs_get_photo(sipe_private, uri);
 
 		/* Lync 2010: use [MS-DLX] */
 		} else if (sipe_private->dlx_uri         &&
 			   sipe_private->addressbook_uri) {
 			struct ms_dlx_data *mdd = g_new0(struct ms_dlx_data, 1);
 
-			mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup("msRTCSIP-PrimaryUserAddress"));
-			mdd->search_rows = g_slist_append(mdd->search_rows, g_strdup(uri));
-
+			mdd->search_rows     = search_rows_for_uri(uri);
 			mdd->other           = g_strdup(uri);
 			mdd->max_returns     = 1;
 			mdd->callback        = get_photo_ab_entry_response;
 			mdd->failed_callback = get_photo_ab_entry_failed;
 			mdd->session         = sipe_svc_session_start();
 
 			ms_dlx_webticket_request(sipe_private, mdd);
 		}
--- a/libpurple/protocols/sipe/core/sipe-buddy.h
+++ b/libpurple/protocols/sipe/core/sipe-buddy.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-buddy.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2014 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,16 +16,17 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 /* Forward declarations */
+struct sipe_backend_search_results;
 struct sipe_cal_working_hours;
 struct sipe_core_private;
 struct sipe_group;
 
 struct sipe_buddy {
 	gchar *name;
 	gchar *exchange_key;
 	gchar *change_key;
@@ -33,16 +34,17 @@ struct sipe_buddy {
 	gchar *meeting_subject;
 	gchar *meeting_location;
 	/* Sipe internal format for Note is HTML.
 	 * All incoming plain text should be html-escaped
 	 * for example by g_markup_escape_text()
 	 */
 	gchar *note;
 	gboolean is_oof_note;
+	gboolean is_mobile;
 	time_t note_since;
 
 	/* Calendar related fields */
 	gchar *cal_start_time;
 	int cal_granularity;
 	gchar *cal_free_busy_base64;
 	gchar *cal_free_busy;
 	time_t cal_free_busy_published;
@@ -220,23 +222,52 @@ gchar *sipe_buddy_get_alias(struct sipe_
  * @param property_value new value for the property
  */
 void sipe_buddy_update_property(struct sipe_core_private *sipe_private,
 				const gchar *uri,
 				sipe_buddy_info_fields propkey,
 				gchar *property_value);
 
 /**
+ * Update the buddy photo with given SIP URI. If hash is the same
+ * as the cached one then the fetching of the photo is skipped.
+ *
+ * @param sipe_private SIPE core data
+ * @param uri          a SIP URI
+ * @param photo_hash   hash value for the photo data
+ * @param photo_url    HTTP URL where to get the photo data
+ * @param headers      additional HTTP headers (may be @c NULL)
+ */
+void sipe_buddy_update_photo(struct sipe_core_private *sipe_private,
+			     const gchar *uri,
+			     const gchar *photo_hash,
+			     const gchar *photo_url,
+			     const gchar *headers);
+
+/**
  * Triggers a download of all buddy photos that were changed on the server.
  *
  * @param sipe_private SIPE core data
  */
 void sipe_buddy_refresh_photos(struct sipe_core_private *sipe_private);
 
 /**
+ * Finalize the search results and display results to user.
+ *
+ * @param sipe_private SIPE core data
+ * @param results      opaque results handle for backend
+ * @param match_count  number of matches found
+ * @param more         @c TRUE if there are more matches available
+ */
+void sipe_buddy_search_contacts_finalize(struct sipe_core_private *sipe_private,
+					 struct sipe_backend_search_results *results,
+					 guint match_count,
+					 gboolean more);
+
+/**
  * Number of buddies
  *
  * @param sipe_private SIPE core data
  */
 guint sipe_buddy_count(struct sipe_core_private *sipe_private);
 
 /**
  * Initialize buddy data
--- a/libpurple/protocols/sipe/core/sipe-cal.c
+++ b/libpurple/protocols/sipe/core/sipe-cal.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-cal.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -213,40 +213,43 @@ sipe_cal_calendar_init(struct sipe_core_
 			cal->oof_url = g_strdup(value);
 			cal->domino_url  = g_strdup(value);
 		}
 	}
 	return;
 }
 
 
-char *
-sipe_cal_event_describe(struct sipe_cal_event* cal_event)
+void
+sipe_cal_event_debug(const struct sipe_cal_event *cal_event,
+		     const gchar *label)
 {
 	GString* str = g_string_new(NULL);
-	const char *status = "";
+	const gchar *status = "";
 
 	switch(cal_event->cal_status) {
 		case SIPE_CAL_FREE:		status = "SIPE_CAL_FREE";	break;
 		case SIPE_CAL_TENTATIVE:	status = "SIPE_CAL_TENTATIVE";	break;
 		case SIPE_CAL_BUSY:		status = "SIPE_CAL_BUSY";	break;
 		case SIPE_CAL_OOF:		status = "SIPE_CAL_OOF";	break;
 		case SIPE_CAL_NO_DATA:		status = "SIPE_CAL_NO_DATA";	break;
 	}
 
-	g_string_append_printf(str, "\t%s: %s",   "start_time",
-		IS(cal_event->start_time) ? asctime(localtime(&cal_event->start_time)) : "\n");
-	g_string_append_printf(str, "\t%s: %s",   "end_time  ",
-		IS(cal_event->end_time) ? asctime(localtime(&cal_event->end_time)) : "\n");
-	g_string_append_printf(str, "\t%s: %s\n", "cal_status",  status);
-	g_string_append_printf(str, "\t%s: %s\n", "subject   ",  cal_event->subject ? cal_event->subject : "");
-	g_string_append_printf(str, "\t%s: %s\n", "location  ",  cal_event->location ? cal_event->location : "");
-	g_string_append_printf(str, "\t%s: %s\n", "is_meeting",  cal_event->is_meeting ? "TRUE" : "FALSE");
+	g_string_append_printf(str, "\tstart_time: %s\n",
+		IS(cal_event->start_time) ? sipe_utils_time_to_debug_str(localtime(&cal_event->start_time)) : "");
+	g_string_append_printf(str, "\tend_time  : %s\n",
+		IS(cal_event->end_time) ? sipe_utils_time_to_debug_str(localtime(&cal_event->end_time)) : "");
+	g_string_append_printf(str, "\tcal_status: %s\n", status);
+	g_string_append_printf(str, "\tsubject   : %s\n", cal_event->subject ? cal_event->subject : "");
+	g_string_append_printf(str, "\tlocation  : %s\n", cal_event->location ? cal_event->location : "");
+	/* last line must have no line break */
+	g_string_append_printf(str, "\tis_meeting: %s", cal_event->is_meeting ? "TRUE" : "FALSE");
 
-	return g_string_free(str, FALSE);
+	SIPE_DEBUG_INFO("%s%s", label, str->str);
+	g_string_free(str, TRUE);
 }
 
 char *
 sipe_cal_event_hash(struct sipe_cal_event* event)
 {
 	/* no end_time as it dos not get published */
 	/* no cal_status as it can change on publication */
 	return g_strdup_printf("<%d><%s><%s><%d>",
@@ -936,36 +939,36 @@ sipe_cal_get_description(struct sipe_bud
 	switch_time = sipe_cal_get_switch_time(free_busy, cal_start, buddy->cal_granularity, index, current_cal_state, &to_state);
 
 	SIPE_DEBUG_INFO_NOFORMAT("\n* Calendar *");
 	if (buddy->cal_working_hours) {
 		sipe_cal_get_today_work_hours(buddy->cal_working_hours, &start, &end, &next_start);
 
 		SIPE_DEBUG_INFO("Remote now timezone : %s", sipe_cal_get_tz(buddy->cal_working_hours, now));
 		SIPE_DEBUG_INFO("std.switch_time(GMT): %s",
-				IS((*buddy->cal_working_hours).std.switch_time) ? asctime(gmtime(&((*buddy->cal_working_hours).std.switch_time))) : "");
+				IS((*buddy->cal_working_hours).std.switch_time) ? sipe_utils_time_to_debug_str(gmtime(&((*buddy->cal_working_hours).std.switch_time))) : "");
 		SIPE_DEBUG_INFO("dst.switch_time(GMT): %s",
-				IS((*buddy->cal_working_hours).dst.switch_time) ? asctime(gmtime(&((*buddy->cal_working_hours).dst.switch_time))) : "");
+				IS((*buddy->cal_working_hours).dst.switch_time) ? sipe_utils_time_to_debug_str(gmtime(&((*buddy->cal_working_hours).dst.switch_time))) : "");
 		SIPE_DEBUG_INFO("Remote now time     : %s",
-			asctime(sipe_localtime_tz(&now, sipe_cal_get_tz(buddy->cal_working_hours, now))));
+			sipe_utils_time_to_debug_str(sipe_localtime_tz(&now, sipe_cal_get_tz(buddy->cal_working_hours, now))));
 		SIPE_DEBUG_INFO("Remote start time   : %s",
-			IS(start) ? asctime(sipe_localtime_tz(&start, sipe_cal_get_tz(buddy->cal_working_hours, start))) : "");
+			IS(start) ? sipe_utils_time_to_debug_str(sipe_localtime_tz(&start, sipe_cal_get_tz(buddy->cal_working_hours, start))) : "");
 		SIPE_DEBUG_INFO("Remote end time     : %s",
-			IS(end) ? asctime(sipe_localtime_tz(&end, sipe_cal_get_tz(buddy->cal_working_hours, end))) : "");
+			IS(end) ? sipe_utils_time_to_debug_str(sipe_localtime_tz(&end, sipe_cal_get_tz(buddy->cal_working_hours, end))) : "");
 		SIPE_DEBUG_INFO("Rem. next_start time: %s",
-			IS(next_start) ? asctime(sipe_localtime_tz(&next_start, sipe_cal_get_tz(buddy->cal_working_hours, next_start))) : "");
+			IS(next_start) ? sipe_utils_time_to_debug_str(sipe_localtime_tz(&next_start, sipe_cal_get_tz(buddy->cal_working_hours, next_start))) : "");
 		SIPE_DEBUG_INFO("Remote switch time  : %s",
-			IS(switch_time) ? asctime(sipe_localtime_tz(&switch_time, sipe_cal_get_tz(buddy->cal_working_hours, switch_time))) : "");
+			IS(switch_time) ? sipe_utils_time_to_debug_str(sipe_localtime_tz(&switch_time, sipe_cal_get_tz(buddy->cal_working_hours, switch_time))) : "");
 	} else {
 		SIPE_DEBUG_INFO("Local now time      : %s",
-			asctime(localtime(&now)));
+			sipe_utils_time_to_debug_str(localtime(&now)));
 		SIPE_DEBUG_INFO("Local switch time   : %s",
-			IS(switch_time) ? asctime(localtime(&switch_time)) : "");
+			IS(switch_time) ? sipe_utils_time_to_debug_str(localtime(&switch_time)) : "");
 	}
-	SIPE_DEBUG_INFO("Calendar End (GMT)  : %s", asctime(gmtime(&cal_end)));
+	SIPE_DEBUG_INFO("Calendar End (GMT)  : %s", sipe_utils_time_to_debug_str(gmtime(&cal_end)));
 	SIPE_DEBUG_INFO("current cal state   : %s", cal_states[current_cal_state]);
 	SIPE_DEBUG_INFO("switch  cal state   : %s", cal_states[to_state]         );
 
 	/* Calendar: string calculations */
 
 	/*
 	ALGORITHM (don't delete)
 	(c)2009,2010 pier11 <pier11@operamail.com>
@@ -1053,51 +1056,62 @@ sipe_cal_get_description(struct sipe_bud
 			res = g_strdup_printf(_("%s. %s at %.2d:%.2d"), tmp, cal_states[to_state], until_tm->tm_hour, until_tm->tm_min);
 			g_free(tmp);
 			return res;
 		}
 	}
 	/* End of - Calendar: string calculations */
 }
 
-#define UPDATE_CALENDAR_INTERVAL	30*60	/* 30 min */
+#define UPDATE_CALENDAR_INTERVAL (15*60) /* 15 min, default granularity for Exchange */
+#define UPDATE_CALENDAR_OFFSET       30  /* 30 seconds before next interval starts */
 
 void sipe_core_update_calendar(struct sipe_core_public *sipe_public)
 {
+	time_t now, offset;
+
 	SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: started.");
 
 	/* Do in parallel.
 	 * If failed, the branch will be disabled for subsequent calls.
 	 * Can't rely that user turned the functionality on in account settings.
 	 */
 	sipe_ews_update_calendar(SIPE_CORE_PRIVATE);
 #ifdef _WIN32
 	/* @TODO: UNIX integration missing */
 	sipe_domino_update_calendar(SIPE_CORE_PRIVATE);
 #endif
 
-	/* schedule repeat */
+	/* how long, in seconds, until the next calendar interval starts? */
+	now    = time(NULL);
+	offset = (now / UPDATE_CALENDAR_INTERVAL + 1) * UPDATE_CALENDAR_INTERVAL - now;
+
+	/* ensure that the update after the initial one is not too soon */
+	if (offset <= (UPDATE_CALENDAR_INTERVAL / 2))
+		offset += UPDATE_CALENDAR_INTERVAL;
+
+	/* schedule next update before a new calendar interval starts */
 	sipe_schedule_seconds(SIPE_CORE_PRIVATE,
 			      "<+update-calendar>",
 			      NULL,
-			      UPDATE_CALENDAR_INTERVAL,
+			      offset - UPDATE_CALENDAR_OFFSET,
 			      (sipe_schedule_action)sipe_core_update_calendar,
 			      NULL);
 
 	SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished.");
 }
 
 void sipe_cal_presence_publish(struct sipe_core_private *sipe_private,
 			       gboolean do_publish_calendar)
 {
 	if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
 		if (do_publish_calendar)
 			sipe_ocs2007_presence_publish(sipe_private, NULL);
 		else
-			sipe_ocs2007_category_publish(sipe_private);
+			sipe_ocs2007_category_publish(sipe_private, FALSE);
 	} else {
 		sipe_ocs2005_presence_publish(sipe_private,
 					      do_publish_calendar);
 	}
 }
 
 void sipe_cal_delayed_calendar_update(struct sipe_core_private *sipe_private)
 {
--- a/libpurple/protocols/sipe/core/sipe-cal.h
+++ b/libpurple/protocols/sipe/core/sipe-cal.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-cal.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -58,17 +58,16 @@ struct sipe_calendar {
 
 	int state;
 	char *email;
 	char *legacy_dn;
 	int is_ews_disabled;
 	int is_domino_disabled;
 	int is_updated;
 	gboolean retry;
-	gboolean ews_autodiscover_triggered;
 
 	char *as_url;
 	char *oof_url;
 	char *oab_url;
 	char *domino_url;
 
 	char *oof_state; /* Enabled, Disabled, Scheduled */
 	char *oof_note;
@@ -103,22 +102,21 @@ sipe_cal_calendar_init(struct sipe_core_
  * Returns hash of Calendar Event for comparison.
  *
  * Must be g_free()'d after use.
  */
 char *
 sipe_cal_event_hash(struct sipe_cal_event* event);
 
 /**
- * Describes Calendar event in human readable form.
- *
- * Must be g_free()'d after use.
+ * Dump calendar event in human readable form to debug log.
  */
-char *
-sipe_cal_event_describe(struct sipe_cal_event* cal_event);
+void
+sipe_cal_event_debug(const struct sipe_cal_event *cal_event,
+		     const gchar *label);
 
 /**
  * Converts struct tm to Epoch time_t considering timezone.
  *
  * @param tz as defined for TZ environment variable.
  *
  * Reference: see timegm(3) - Linux man page
  */
--- a/libpurple/protocols/sipe/core/sipe-chat.c
+++ b/libpurple/protocols/sipe/core/sipe-chat.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-chat.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2009-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2009-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -88,16 +88,22 @@ void sipe_chat_destroy(void)
 	while (chat_sessions) {
 		struct sipe_chat_session *chat_session = chat_sessions->data;
 		SIPE_DEBUG_INFO("sipe_chat_destroy: '%s' (%s)",
 				chat_session->title, chat_session->id);
 		sipe_chat_remove_session(chat_session);
 	}
 }
 
+const gchar *sipe_core_chat_id(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
+			       struct sipe_chat_session *chat_session)
+{
+	return(chat_session->id);
+}
+
 void sipe_core_chat_invite(struct sipe_core_public *sipe_public,
 			   struct sipe_chat_session *chat_session,
 			   const char *name)
 {
 	struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
 
 	SIPE_DEBUG_INFO("sipe_core_chat_create: who '%s'", name);
 
--- a/libpurple/protocols/sipe/core/sipe-conf.c
+++ b/libpurple/protocols/sipe/core/sipe-conf.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-conf.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -17,16 +17,25 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+/**
+ * Documentation references:
+ *
+ * Microsoft DevNet: [MS-CONFIM]: Centralized Conference Control Protocol:
+ *                                Instant Messaging Extensions
+ *  <http://msdn.microsoft.com/en-us/library/cc431500%28v=office.12%29.aspx>
+ *
+ */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
@@ -37,163 +46,144 @@
 #include "sip-transport.h"
 #include "sipe-backend.h"
 #include "sipe-buddy.h"
 #include "sipe-chat.h"
 #include "sipe-conf.h"
 #include "sipe-core.h"
 #include "sipe-core-private.h"
 #include "sipe-dialog.h"
+#include "sipe-http.h"
 #include "sipe-im.h"
 #include "sipe-nls.h"
 #include "sipe-session.h"
 #include "sipe-subscriptions.h"
 #include "sipe-user.h"
 #include "sipe-utils.h"
 #include "sipe-xml.h"
 
-#ifdef HAVE_VV
-#define ENTITY_VIEW_AUDIO_VIDEO "<msci:entity-view entity=\"audio-video\"/>"
-#else
-#define ENTITY_VIEW_AUDIO_VIDEO
-#endif
-
-/**
- * Add Conference request to FocusFactory.
- * @param focus_factory_uri (%s) Ex.: sip:bob7@boston.local;gruu;opaque=app:conf:focusfactory
- * @param from		    (%s) Ex.: sip:bob7@boston.local
- * @param request_id	    (%d) Ex.: 1094520
- * @param conference_id	    (%s) Ex.: 8386E6AEAAA41E4AA6627BA76D43B6D1
- * @param expiry_time	    (%s) Ex.: 2009-07-13T17:57:09Z , Default duration: 7 hours
- */
-#define SIPE_SEND_CONF_ADD \
-"<?xml version=\"1.0\"?>"\
-"<request xmlns=\"urn:ietf:params:xml:ns:cccp\" "\
-	"xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
-	"C3PVersion=\"1\" "\
-	"to=\"%s\" "\
-	"from=\"%s\" "\
-	"requestId=\"%d\">"\
-	"<addConference>"\
-		"<ci:conference-info xmlns:ci=\"urn:ietf:params:xml:ns:conference-info\" entity=\"\" xmlns:msci=\"http://schemas.microsoft.com/rtc/2005/08/confinfoextensions\">"\
-			"<ci:conference-description>"\
-				"<ci:subject/>"\
-				"<msci:conference-id>%s</msci:conference-id>"\
-				"<msci:expiry-time>%s</msci:expiry-time>"\
-				"<msci:admission-policy>openAuthenticated</msci:admission-policy>"\
-			"</ci:conference-description>"\
-			"<msci:conference-view>"\
-				"<msci:entity-view entity=\"chat\"/>"\
-				ENTITY_VIEW_AUDIO_VIDEO \
-			"</msci:conference-view>"\
-		"</ci:conference-info>"\
-	"</addConference>"\
-"</request>"
-
-/**
- * AddUser request to Focus.
- * Params:
- * focus_URI, from, request_id, focus_URI, from, endpoint_GUID
- */
-#define SIPE_SEND_CONF_ADD_USER \
-"<?xml version=\"1.0\"?>"\
-"<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
-	"C3PVersion=\"1\" "\
-	"to=\"%s\" "\
-	"from=\"%s\" "\
-	"requestId=\"%d\">"\
-	"<addUser>"\
-		"<conferenceKeys confEntity=\"%s\"/>"\
-		"<ci:user xmlns:ci=\"urn:ietf:params:xml:ns:conference-info\" entity=\"%s\">"\
-			"<ci:roles>"\
-				"<ci:entry>attendee</ci:entry>"\
-			"</ci:roles>"\
-			"<ci:endpoint entity=\"{%s}\" xmlns:msci=\"http://schemas.microsoft.com/rtc/2005/08/confinfoextensions\"/>"\
-		"</ci:user>"\
-	"</addUser>"\
-"</request>"
-
-/**
- * ModifyUserRoles request to Focus. Makes user a leader.
- * @param focus_uri (%s)
- * @param from (%s)
- * @param request_id (%d)
- * @param focus_uri (%s)
- * @param who (%s)
- */
-#define SIPE_SEND_CONF_MODIFY_USER_ROLES \
-"<?xml version=\"1.0\"?>"\
-"<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
-	"C3PVersion=\"1\" "\
-	"to=\"%s\" "\
-	"from=\"%s\" "\
-	"requestId=\"%d\">"\
-	"<modifyUserRoles>"\
-		"<userKeys confEntity=\"%s\" userEntity=\"%s\"/>"\
-		"<user-roles xmlns=\"urn:ietf:params:xml:ns:conference-info\">"\
-			"<entry>presenter</entry>"\
-		"</user-roles>"\
-	"</modifyUserRoles>"\
-"</request>"
-
-/**
- * ModifyConferenceLock request to Focus. Locks/unlocks conference.
- * @param focus_uri (%s)
- * @param from (%s)
- * @param request_id (%d)
- * @param focus_uri (%s)
- * @param locked (%s) "true" or "false" values applicable
- */
-#define SIPE_SEND_CONF_MODIFY_CONF_LOCK \
-"<?xml version=\"1.0\"?>"\
-"<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
-	"C3PVersion=\"1\" "\
-	"to=\"%s\" "\
-	"from=\"%s\" "\
-	"requestId=\"%d\">"\
-	"<modifyConferenceLock>"\
-		"<conferenceKeys confEntity=\"%s\"/>"\
-		"<locked>%s</locked>"\
-	"</modifyConferenceLock>"\
-"</request>"
-
-/**
- * ModifyConferenceLock request to Focus. Locks/unlocks conference.
- * @param focus_uri (%s)
- * @param from (%s)
- * @param request_id (%d)
- * @param focus_uri (%s)
- * @param who (%s)
- */
-#define SIPE_SEND_CONF_DELETE_USER \
-"<?xml version=\"1.0\"?>"\
-"<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
-	"C3PVersion=\"1\" "\
-	"to=\"%s\" "\
-	"from=\"%s\" "\
-	"requestId=\"%d\">"\
-	"<deleteUser>"\
-		"<userKeys confEntity=\"%s\" userEntity=\"%s\"/>"\
-	"</deleteUser>"\
-"</request>"
-
 /**
  * Invite counterparty to join conference.
  * @param focus_uri (%s)
  * @param subject (%s) of conference
  */
 #define SIPE_SEND_CONF_INVITE \
 "<Conferencing version=\"2.0\">"\
 	"<focus-uri>%s</focus-uri>"\
 	"<subject>%s</subject>"\
 	"<im available=\"true\">"\
 		"<first-im/>"\
 	"</im>"\
 "</Conferencing>"
 
+static struct transaction *
+cccp_request(struct sipe_core_private *sipe_private, const gchar *method,
+	     const gchar *with, struct sip_dialog *dialog,
+	     TransCallback callback, const gchar *body, ...)
+{
+	gchar *headers;
+	gchar *request;
+	gchar *request_body;
+
+	gchar *self = sip_uri_self(sipe_private);
+
+	va_list args;
+
+	struct transaction *trans;
+
+	headers = g_strdup_printf(
+		"Supported: ms-sender\r\n"
+		"Contact: %s\r\n"
+		"Content-Type: application/cccp+xml\r\n",
+		sipe_private->contact);
+
+	/* TODO: put request_id to queue to further compare with incoming one */
+	request = g_strdup_printf(
+		"<?xml version=\"1.0\"?>"
+		"<request xmlns=\"urn:ietf:params:xml:ns:cccp\" "
+		"xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "
+			"C3PVersion=\"1\" "
+			"to=\"%s\" "
+			"from=\"%s\" "
+			"requestId=\"%d\">"
+			"%s"
+		"</request>",
+		with,
+		self,
+		sipe_private->cccp_request_id++,
+		body);
+	g_free(self);
+
+	va_start(args, body);
+	request_body = g_strdup_vprintf(request, args);
+	va_end(args);
+
+	g_free(request);
+
+	trans = sip_transport_request(sipe_private,
+				      method,
+				      with,
+				      with,
+				      headers,
+				      request_body,
+				      dialog,
+				      callback);
+
+	g_free(headers);
+	g_free(request_body);
+
+	return trans;
+}
+
+static gboolean
+process_conf_get_capabilities(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
+			      struct sipmsg *msg,
+			      SIPE_UNUSED_PARAMETER struct transaction *trans)
+{
+	if (msg->response >= 400) {
+		SIPE_DEBUG_INFO_NOFORMAT("process_conf_get_capabilities: "
+				"getConferencingCapabilities failed.");
+		return FALSE;
+	}
+	if (msg->response == 200) {
+		sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen);
+
+		if (sipe_strequal("success", sipe_xml_attribute(xn_response, "code"))) {
+			const sipe_xml *node = sipe_xml_child(xn_response, "getConferencingCapabilities/mcu-types/mcuType");
+			for (;node; node = sipe_xml_twin(node)) {
+				sipe_private->conf_mcu_types =
+						g_slist_append(sipe_private->conf_mcu_types,
+							       sipe_xml_data(node));
+			}
+		}
+
+		sipe_xml_free(xn_response);
+	}
+
+	return TRUE;
+}
+
+void
+sipe_conf_get_capabilities(struct sipe_core_private *sipe_private)
+{
+	cccp_request(sipe_private, "SERVICE",
+		     sipe_private->focus_factory_uri,
+		     NULL,
+		     process_conf_get_capabilities,
+		     "<getConferencingCapabilities />");
+}
+
+gboolean
+sipe_conf_supports_mcu_type(struct sipe_core_private *sipe_private,
+			    const gchar *type)
+{
+	return g_slist_find_custom(sipe_private->conf_mcu_types, type,
+				   sipe_strcompare) != NULL;
+}
+
 /**
  * Generates random GUID.
  * This method is borrowed from pidgin's msnutils.c
  */
 static char *
 rand_guid()
 {
 	return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
@@ -319,118 +309,195 @@ parse_lync_join_url(const gchar *uri)
 	} else if (g_str_has_prefix(uri, "http://")) {
 		uri += 7;
 	}
 
 	parts = g_strsplit(uri, "/", 0);
 
 	for (parts_count = 0; parts[parts_count]; ++parts_count);
 	if (parts_count >= 3) {
-		gchar *base_url = parts[0];
-		gchar *conference_id = parts[parts_count - 1];
-		gchar *organizer_alias = parts[parts_count - 2];
+		const gchar *conference_id   = parts[parts_count - 1];
+		const gchar *organizer_alias = parts[parts_count - 2];
+
+		gchar **domain_parts = g_strsplit(parts[0], ".", 2);
 
-		gchar **url_parts = g_strsplit(base_url, ".", 0);
-		int url_parts_count = 0;
-		for (url_parts_count = 0; url_parts[url_parts_count]; ++url_parts_count);
-
-		if (url_parts_count >= 3) {
-			focus_uri = g_strdup_printf("sip:%s@%s.%s;gruu;opaque=app:conf:focus:id:%s",
-					organizer_alias,
-					url_parts[url_parts_count - 2], url_parts[url_parts_count - 1],
-					conference_id);
+		/* we need to drop the first sub-domain from the URL */
+		if (domain_parts[0] && domain_parts[1]) {
+			focus_uri = g_strdup_printf("sip:%s@%s;gruu;opaque=app:conf:focus:id:%s",
+						    organizer_alias,
+						    domain_parts[1],
+						    conference_id);
 		}
 
-		g_strfreev(url_parts);
+		g_strfreev(domain_parts);
 	}
 
 	g_strfreev(parts);
 
 	return focus_uri;
 }
 
-struct sip_session *
-sipe_core_conf_create(struct sipe_core_public *sipe_public,
-		      const gchar *uri)
+static void sipe_conf_error(struct sipe_core_private *sipe_private,
+			    const gchar *uri)
+{
+	gchar *error = g_strdup_printf(_("\"%s\" is not a valid conference URI"),
+				       uri ? uri : "");
+	sipe_backend_notify_error(SIPE_CORE_PUBLIC,
+				  _("Failed to join the conference"),
+				  error);
+	g_free(error);
+}
+
+static void sipe_conf_lync_url_cb(struct sipe_core_private *sipe_private,
+				  guint status,
+				  SIPE_UNUSED_PARAMETER GSList *headers,
+				  const gchar *body,
+				  gpointer callback_data)
 {
-	gchar *uri_ue = sipe_utils_uri_unescape(uri);
-	gchar *focus_uri;
-	struct sip_session *session = NULL;
+	gchar *uri = callback_data;
+
+	if (status != (guint) SIPE_HTTP_STATUS_ABORTED) {
+		gchar *focus_uri = NULL;
+
+		if (body) {
+			/*
+			 * Extract focus URI from HTML, e.g.
+			 *
+			 *  <a ... href="conf&#58;sip&#58;...ABCDEF&#37;3Frequired..." ... >
+			 */
+			const gchar *start = g_strstr_len(body,
+							  -1,
+							  "href=\"conf");
+			if (start) {
+				const gchar *end;
+
+				start += 6;
+				end = strchr(start, '"');
+
+				if (end) {
+					gchar *html = g_strndup(start,
+								end - start);
 
-	focus_uri = parse_ocs_focus_uri(uri_ue);
-	if (!focus_uri) {
-		focus_uri = parse_lync_join_url(uri_ue);
+					/* decode HTML entities */
+					gchar *html_unescaped = sipe_backend_markup_strip_html(html);
+					g_free(html);
+
+					if (!is_empty(html_unescaped)) {
+						gchar *uri_unescaped = sipe_utils_uri_unescape(html_unescaped);
+						SIPE_DEBUG_INFO("sipe_conf_lync_url_cb: found focus URI '%s'",
+								uri_unescaped);
+						focus_uri = parse_ocs_focus_uri(uri_unescaped);
+						g_free(uri_unescaped);
+					}
+					g_free(html_unescaped);
+				}
+			}
+		}
+
+		/* If we can't find a focus URI then fall back to URL parser */
+		if (!focus_uri) {
+			SIPE_DEBUG_INFO("sipe_conf_lync_url_cb: no focus URI found. Falling back to parsing Lync URL '%s'",
+					uri);
+			focus_uri = parse_lync_join_url(uri);
+		}
+
+		if (focus_uri) {
+			sipe_conf_create(sipe_private, NULL, focus_uri);
+			g_free(focus_uri);
+		} else {
+			sipe_conf_error(sipe_private, uri);
+		}
 	}
 
-	if (focus_uri) {
-		session = sipe_conf_create(SIPE_CORE_PRIVATE, NULL, focus_uri);
-		g_free(focus_uri);
-	} else {
-		gchar *error = g_strdup_printf(_("\"%s\" is not a valid conference URI"),
-					       uri ? uri : "");
-		sipe_backend_notify_error(sipe_public,
-					  _("Failed to join the conference"),
-					  error);
-		g_free(error);
+	g_free(uri);
+}
+
+static gboolean sipe_conf_check_for_lync_url(struct sipe_core_private *sipe_private,
+					     gchar *uri)
+{
+	if (!(g_str_has_prefix(uri, "https://") ||
+	      g_str_has_prefix(uri, "http://")))
+		return(FALSE);
+
+	/* URL points to a HTML page with the conference focus URI */
+	return(sipe_http_request_get(sipe_private,
+				     uri,
+				     NULL,
+				     sipe_conf_lync_url_cb,
+				     uri)
+	       != NULL);
+}
+
+void sipe_core_conf_create(struct sipe_core_public *sipe_public,
+			   const gchar *uri)
+{
+	struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
+	gchar *uri_ue = sipe_utils_uri_unescape(uri);
+
+	SIPE_DEBUG_INFO("sipe_core_conf_create: URI '%s' unescaped '%s'",
+			uri    ? uri    : "<UNDEFINED>",
+			uri_ue ? uri_ue : "<UNDEFINED>");
+
+	/* takes ownership of "uri_ue" if successful */
+	if (!sipe_conf_check_for_lync_url(sipe_private, uri_ue)) {
+		gchar *focus_uri = parse_ocs_focus_uri(uri_ue);
+
+		if (focus_uri) {
+			sipe_conf_create(sipe_private, NULL, focus_uri);
+			g_free(focus_uri);
+		} else {
+			sipe_conf_error(sipe_private, uri);
+		}
+
+		g_free(uri_ue);
 	}
-
-	g_free(uri_ue);
-
-	return session;
 }
 
 /** Create new session with Focus URI */
 struct sip_session *
 sipe_conf_create(struct sipe_core_private *sipe_private,
 		 struct sipe_chat_session *chat_session,
 		 const gchar *focus_uri)
 {
-	gchar *hdr;
-	gchar *contact;
-	gchar *body;
+	/* addUser request to the focus.
+	 *
+	 * focus_URI, from, endpoint_GUID
+	 */
+	static const gchar CCCP_ADD_USER[] =
+		"<addUser>"
+			"<conferenceKeys confEntity=\"%s\"/>"
+			"<ci:user xmlns:ci=\"urn:ietf:params:xml:ns:conference-info\" entity=\"%s\">"
+				"<ci:roles>"
+					"<ci:entry>attendee</ci:entry>"
+				"</ci:roles>"
+				"<ci:endpoint entity=\"{%s}\" "
+					      "xmlns:msci=\"http://schemas.microsoft.com/rtc/2005/08/confinfoextensions\"/>"
+			"</ci:user>"
+		"</addUser>";
+
 	gchar *self;
 	struct sip_session *session = sipe_session_add_chat(sipe_private,
 							    chat_session,
 							    FALSE,
 							    focus_uri);
 
 	session->focus_dialog = g_new0(struct sip_dialog, 1);
 	session->focus_dialog->callid = gencallid();
 	session->focus_dialog->with = g_strdup(session->chat_session->id);
 	session->focus_dialog->endpoint_GUID = rand_guid();
 	session->focus_dialog->ourtag = gentag();
 
-	contact = get_contact(sipe_private);
-	hdr = g_strdup_printf(
-		"Supported: ms-sender\r\n"
-		"Contact: %s\r\n"
-		"Content-Type: application/cccp+xml\r\n",
-		contact);
-	g_free(contact);
-
-	/* @TODO put request_id to queue to further compare with incoming one */
-	/* focus_URI, from, request_id, focus_URI, from, endpoint_GUID */
 	self = sip_uri_self(sipe_private);
-	body = g_strdup_printf(
-		SIPE_SEND_CONF_ADD_USER,
-		session->focus_dialog->with,
-		self,
-		session->request_id++,
-		session->focus_dialog->with,
-		self,
-		session->focus_dialog->endpoint_GUID);
-
 	session->focus_dialog->outgoing_invite =
-		sip_transport_invite(sipe_private,
-				     hdr,
-				     body,
-				     session->focus_dialog,
-				     process_invite_conf_focus_response);
-	g_free(body);
-	g_free(hdr);
+		cccp_request(sipe_private, "INVITE",
+			     session->focus_dialog->with, session->focus_dialog,
+			     process_invite_conf_focus_response,
+			     CCCP_ADD_USER,
+			     session->focus_dialog->with, self,
+			     session->focus_dialog->endpoint_GUID);
 
 	/* Rejoin existing session? */
 	if (chat_session) {
 		SIPE_DEBUG_INFO("sipe_conf_create: rejoin '%s' (%s)",
 				chat_session->title,
 				chat_session->id);
 		sipe_backend_chat_rejoin(SIPE_CORE_PUBLIC,
 					 chat_session->backend,
@@ -443,46 +510,38 @@ sipe_conf_create(struct sipe_core_privat
 }
 
 /** Modify User Role */
 void
 sipe_conf_modify_user_role(struct sipe_core_private *sipe_private,
 			   struct sip_session *session,
 			   const gchar* who)
 {
-	gchar *hdr;
-	gchar *body;
-	gchar *self;
+	/* modifyUserRoles request to the focus. Makes user a leader.
+	 *
+	 * focus_uri (%s)
+	 * who (%s)
+	 */
+	static const gchar CCCP_MODIFY_USER_ROLES[] =
+		"<modifyUserRoles>"
+			"<userKeys confEntity=\"%s\" userEntity=\"%s\"/>"
+			"<user-roles xmlns=\"urn:ietf:params:xml:ns:conference-info\">"
+				"<entry>presenter</entry>"
+			"</user-roles>"
+		"</modifyUserRoles>";
 
 	if (!session->focus_dialog || !session->focus_dialog->is_established) {
 		SIPE_DEBUG_INFO_NOFORMAT("sipe_conf_modify_user_role: no dialog with focus, exiting.");
 		return;
 	}
 
-	hdr = g_strdup(
-		"Content-Type: application/cccp+xml\r\n");
-
-	/* @TODO put request_id to queue to further compare with incoming one */
-	self = sip_uri_self(sipe_private);
-	body = g_strdup_printf(
-		SIPE_SEND_CONF_MODIFY_USER_ROLES,
-		session->focus_dialog->with,
-		self,
-		session->request_id++,
-		session->focus_dialog->with,
-		who);
-	g_free(self);
-
-	sip_transport_info(sipe_private,
-			   hdr,
-			   body,
-			   session->focus_dialog,
-			   NULL);
-	g_free(body);
-	g_free(hdr);
+	cccp_request(sipe_private, "INFO", session->focus_dialog->with,
+		     session->focus_dialog, NULL,
+		     CCCP_MODIFY_USER_ROLES,
+		     session->focus_dialog->with, who);
 }
 
 /**
  * Check conference lock status
  */
 sipe_chat_lock_status sipe_core_chat_lock_status(struct sipe_core_public *sipe_public,
 						 struct sipe_chat_session *chat_session)
 {
@@ -515,89 +574,70 @@ sipe_chat_lock_status sipe_core_chat_loc
  * Sends request to Focus.
  * INFO method is a carrier of application/cccp+xml
  */
 void
 sipe_core_chat_modify_lock(struct sipe_core_public *sipe_public,
 			   struct sipe_chat_session *chat_session,
 			   const gboolean locked)
 {
+	/* modifyConferenceLock request to the focus. Locks/unlocks conference.
+	 *
+	 * focus_uri (%s)
+	 * locked (%s) "true" or "false" values applicable
+	 */
+	static const gchar CCCP_MODIFY_CONFERENCE_LOCK[] =
+		"<modifyConferenceLock>"
+			"<conferenceKeys confEntity=\"%s\"/>"
+			"<locked>%s</locked>"
+		"</modifyConferenceLock>";
+
 	struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
-	gchar *hdr;
-	gchar *body;
-	gchar *self;
 
 	struct sip_session *session = sipe_session_find_chat(sipe_private,
 							     chat_session);
 
 	if (!session) return;
 	if (!session->focus_dialog || !session->focus_dialog->is_established) {
 		SIPE_DEBUG_INFO_NOFORMAT("sipe_conf_modify_conference_lock: no dialog with focus, exiting.");
 		return;
 	}
 
-	hdr = g_strdup(
-		"Content-Type: application/cccp+xml\r\n");
-
-	/* @TODO put request_id to queue to further compare with incoming one */
-	self = sip_uri_self(sipe_private);
-	body = g_strdup_printf(
-		SIPE_SEND_CONF_MODIFY_CONF_LOCK,
-		session->focus_dialog->with,
-		self,
-		session->request_id++,
-		session->focus_dialog->with,
-		locked ? "true" : "false");
-	g_free(self);
-
-	sip_transport_info(sipe_private,
-			   hdr,
-			   body,
-			   session->focus_dialog,
-			   NULL);
-	g_free(body);
-	g_free(hdr);
+	cccp_request(sipe_private, "INFO", session->focus_dialog->with,
+		     session->focus_dialog, NULL,
+		     CCCP_MODIFY_CONFERENCE_LOCK,
+		     session->focus_dialog->with,
+		     locked ? "true" : "false");
 }
 
 /** Modify Delete User */
 void
 sipe_conf_delete_user(struct sipe_core_private *sipe_private,
 		      struct sip_session *session,
 		      const gchar* who)
 {
-	gchar *hdr;
-	gchar *body;
-	gchar *self;
+	/* deleteUser request to the focus. Removes a user from the conference.
+	 *
+	 * focus_uri (%s)
+	 * who (%s)
+	 */
+	static const gchar CCCP_DELETE_USER[] =
+		"<deleteUser>"
+			"<userKeys confEntity=\"%s\" userEntity=\"%s\"/>"
+		"</deleteUser>";
 
 	if (!session->focus_dialog || !session->focus_dialog->is_established) {
 		SIPE_DEBUG_INFO_NOFORMAT("sipe_conf_delete_user: no dialog with focus, exiting.");
 		return;
 	}
 
-	hdr = g_strdup(
-		"Content-Type: application/cccp+xml\r\n");
-
-	/* @TODO put request_id to queue to further compare with incoming one */
-	self = sip_uri_self(sipe_private);
-	body = g_strdup_printf(
-		SIPE_SEND_CONF_DELETE_USER,
-		session->focus_dialog->with,
-		self,
-		session->request_id++,
-		session->focus_dialog->with,
-		who);
-	g_free(self);
-
-	sip_transport_info(sipe_private,
-			   hdr,
-			   body,
-			   session->focus_dialog,
-			   NULL);
-	g_free(body);
-	g_free(hdr);
+	cccp_request(sipe_private, "INFO", session->focus_dialog->with,
+		     session->focus_dialog, NULL,
+		     CCCP_DELETE_USER,
+		     session->focus_dialog->with, who);
 }
 
 /** Invite counterparty to join conference callback */
 static gboolean
 process_invite_conf_response(struct sipe_core_private *sipe_private,
 			     struct sipmsg *msg,
 			     SIPE_UNUSED_PARAMETER struct transaction *trans)
 {
@@ -722,63 +762,76 @@ process_conf_add_response(struct sipe_co
 
 /**
  * Creates conference.
  */
 void
 sipe_conf_add(struct sipe_core_private *sipe_private,
 	      const gchar* who)
 {
-	gchar *hdr;
 	gchar *conference_id;
-	gchar *contact;
-	gchar *body;
-	gchar *self;
 	struct transaction *trans;
-	struct sip_dialog *dialog = NULL;
 	time_t expiry = time(NULL) + 7*60*60; /* 7 hours */
 	char *expiry_time;
 	struct transaction_payload *payload;
 
-	contact = get_contact(sipe_private);
-	hdr = g_strdup_printf(
-		"Supported: ms-sender\r\n"
-		"Contact: %s\r\n"
-		"Content-Type: application/cccp+xml\r\n",
-		contact);
-	g_free(contact);
+	/* addConference request to the focus factory.
+	 *
+	 * conference_id	(%s) Ex.: 8386E6AEAAA41E4AA6627BA76D43B6D1
+	 * expiry_time		(%s) Ex.: 2009-07-13T17:57:09Z
+	 * conference_view	(%s) Ex.: <msci:entity-view entity="chat"/>
+	 */
+	static const gchar CCCP_ADD_CONFERENCE[] =
+		"<addConference>"
+			"<ci:conference-info xmlns:ci=\"urn:ietf:params:xml:ns:conference-info\" "
+					     "entity=\"\" "
+					     "xmlns:msci=\"http://schemas.microsoft.com/rtc/2005/08/confinfoextensions\">"
+				"<ci:conference-description>"
+					"<ci:subject/>"
+					"<msci:conference-id>%s</msci:conference-id>"
+					"<msci:expiry-time>%s</msci:expiry-time>"
+					"<msci:admission-policy>openAuthenticated</msci:admission-policy>"
+				"</ci:conference-description>"
+				"<msci:conference-view>%s</msci:conference-view>"
+			"</ci:conference-info>"
+		"</addConference>";
+
+	static const gchar *DESIRED_MCU_TYPES[] = {
+		"chat",
+#ifdef HAVE_VV
+		"audio-video",
+#endif
+		NULL
+	};
+
+	GString *conference_view = g_string_new("");
+	const gchar **type;
+
+	for (type = DESIRED_MCU_TYPES; *type; ++type ) {
+		if (sipe_conf_supports_mcu_type(sipe_private, *type)) {
+			g_string_append(conference_view, "<msci:entity-view entity=\"");
+			g_string_append(conference_view, *type);
+			g_string_append(conference_view, "\"/>");
+		}
+	}
 
 	expiry_time = sipe_utils_time_to_str(expiry);
-	self = sip_uri_self(sipe_private);
 	conference_id = genconfid();
-	body = g_strdup_printf(
-		SIPE_SEND_CONF_ADD,
-		sipe_private->focus_factory_uri,
-		self,
-		rand(),
-		conference_id,
-		expiry_time);
-	g_free(self);
+	trans = cccp_request(sipe_private, "SERVICE", sipe_private->focus_factory_uri,
+			     NULL, process_conf_add_response,
+			     CCCP_ADD_CONFERENCE,
+			     conference_id, expiry_time, conference_view->str);
 	g_free(conference_id);
 	g_free(expiry_time);
-
-	trans = sip_transport_service(sipe_private,
-				      sipe_private->focus_factory_uri,
-				      hdr,
-				      body,
-				      process_conf_add_response);
+	g_string_free(conference_view, TRUE);
 
 	payload = g_new0(struct transaction_payload, 1);
 	payload->destroy = g_free;
 	payload->data = g_strdup(who);
 	trans->payload = payload;
-
-	sipe_dialog_free(dialog);
-	g_free(body);
-	g_free(hdr);
 }
 
 static void
 accept_incoming_invite_conf(struct sipe_core_private *sipe_private,
 			    gchar *focus_uri,
 			    gboolean audio,
 			    struct sipmsg *msg)
 {
@@ -1212,17 +1265,27 @@ sipe_process_imdn(struct sipe_core_priva
 	message_id = sipe_xml_data(sipe_xml_child(xn_imdn, "message-id"));
 
 	message = g_hash_table_lookup(session->conf_unconfirmed_messages, message_id);
 
 	/* recipient */
 	for (node = sipe_xml_child(xn_imdn, "recipient"); node; node = sipe_xml_twin(node)) {
 		gchar *tmp = parse_from(sipe_xml_attribute(node, "uri"));
 		gchar *uri = parse_from(tmp);
-		sipe_user_present_message_undelivered(sipe_private, session, -1, -1, uri, message);
+		gchar *status = sipe_xml_data(sipe_xml_child(node, "status"));
+		guint error = status ? g_ascii_strtoull(status, NULL, 10) : 0;
+		/* default to error if missing or conversion failed */
+		if ((error == 0) || (error >= 300))
+			sipe_user_present_message_undelivered(sipe_private,
+							      session,
+							      error,
+							      -1,
+							      uri,
+							      message);
+		g_free(status);
 		g_free(tmp);
 		g_free(uri);
 	}
 
 	sipe_xml_free(xn_imdn);
 
 	g_hash_table_remove(session->conf_unconfirmed_messages, message_id);
 	SIPE_DEBUG_INFO("sipe_process_imdn: removed message %s from conf_unconfirmed_messages(count=%d)",
--- a/libpurple/protocols/sipe/core/sipe-conf.h
+++ b/libpurple/protocols/sipe/core/sipe-conf.h
@@ -21,16 +21,31 @@
  */
 
 /* Forward declarations */
 struct sipmsg;
 struct sip_session;
 struct sipe_core_private;
 
 /**
+ * Obtains conferencing capabilities enabled on the server.
+ *
+ * @param sipe_private SIPE core data
+ */
+void
+sipe_conf_get_capabilities(struct sipe_core_private *sipe_private);
+
+/**
+ * Checks whether given Multipoint Control Unit type is supported by the server.
+ */
+gboolean
+sipe_conf_supports_mcu_type(struct sipe_core_private *sipe_private,
+			    const gchar *type);
+
+/**
  * Creates conference.
  */
 void
 sipe_conf_add(struct sipe_core_private *sipe_private,
 	      const gchar* who);
 
 /**
  * Processes incoming INVITE with
--- a/libpurple/protocols/sipe/core/sipe-core-private.h
+++ b/libpurple/protocols/sipe/core/sipe-core-private.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-core-private.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -54,21 +54,19 @@ struct sipe_core_private {
 	struct sip_transport *transport;
 	const struct sip_service_data *service_data; /* autodiscovery SRV records */
 	const struct sip_address_data *address_data; /* autodiscovery A records */
 	guint transport_type;
 	guint authentication_type;
 
 	/* Account information */
 	gchar *username;
-	gchar *authdomain; /* NULL when SSO is enabled */
 	gchar *authuser;   /* NULL when SSO is enabled */
 	gchar *password;   /* NULL when SSO is enabled */
 	gchar *email;
-	gchar *email_authdomain;
 	gchar *email_authuser;   /* NULL -> use default authentication */
 	gchar *email_password;
 
 	/* SIPE protocol information */
 	gchar *contact;
 	gchar *register_callid;
 	gchar *epid;
 	gchar *focus_factory_uri;
@@ -78,18 +76,17 @@ struct sipe_core_private {
 	 *  we're allowed to subscribe to
 	 */
 	GSList *allowed_events;
 
 	/* Presence */
 	gchar *status;
 	gchar *note;
 	time_t note_since;
-	time_t idle_switch;
-	time_t do_not_publish[SIPE_ACTIVITY_NUM_TYPES];
+	gboolean status_set_by_user;
 
 	/* [MS-SIP] deltaNum counters */
 	guint deltanum_contacts;
 	guint deltanum_acl;      /* setACE (OCS2005 only) */
 
 	/* [MS-PRES] */
 	GSList *containers;
 	GSList *our_publication_keys;
@@ -132,16 +129,17 @@ struct sipe_core_private {
 	/**
 	 *  Provides the necessary information on where we can obtain
 	 *  credentials for the A/V Edge server service.
 	 */
 	gchar *mras_uri;
 	gchar *media_relay_username;
 	gchar *media_relay_password;
 	GSList *media_relays;
+	SipeEncryptionPolicy server_av_encryption_policy;
 
 	/* Group chat */
 	struct sipe_groupchat *groupchat;
 	gchar *persistentChatPool_uri;
 
 	/* buddy menu memory allocation */
 	GSList *blist_menu_containers;
 
@@ -161,16 +159,21 @@ struct sipe_core_private {
 	/* Unified Contact Store */
 	struct sipe_ucs *ucs;
 
 	/* [MS-DLX] server URI */
 	gchar *dlx_uri;
 
 	/* Addressbook server URI */
 	gchar *addressbook_uri;
+
+	/* [MS-CONFPRO] CCCP request ID counter */
+	guint cccp_request_id;
+
+	GSList *conf_mcu_types;
 };
 
 /**
  * Flags - stored in sipe_core_public.flags but names not exported
  */
 /* server is OCS2007+ */
 #define SIPE_CORE_PRIVATE_FLAG_OCS2007            0x80000000
 /* we are connected from outside the enterprise network boundary
--- a/libpurple/protocols/sipe/core/sipe-core.c
+++ b/libpurple/protocols/sipe/core/sipe-core.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-core.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -139,16 +139,17 @@ gchar *sipe_core_about(void)
 		/*
 		 * Non-translatable parts, like markup, are hard-coded
 		 * into the format string. This requires more translatable
 		 * texts but it makes the translations less error prone.
 		 */
 		"<b><font size=\"+1\">SIPE " SIPE_CORE_VERSION " </font></b><br/>"
 		"<br/>"
 		/* 1 */   "%s:<br/>"
+		" - Skype for Business<br/>"
 		" - Microsoft Office 365<br/>"
 		" - Microsoft Business Productivity Online Suite (BPOS)<br/>"
 		" - Microsoft Lync Server<br/>"
 		" - Microsoft Office Communications Server 2007 R2<br/>"
 		" - Microsoft Office Communications Server 2007<br/>"
 		" - Microsoft Live Communications Server 2005<br/>"
 		" - Microsoft Live Communications Server 2003<br/>"
 		" - Reuters Messaging<br/>"
@@ -160,18 +161,18 @@ gchar *sipe_core_about(void)
 		/* 8 */   "%s: GPLv2+<br/>"
 		"<br/>"
 		/* 9 (REMOVED) */
 		/* 10,11 */ "%s<a href=\"" SIPE_TRANSLATIONS_URL "\">Transifex.com</a>%s.<br/>"
 		"<br/>"
 		/* 12 */  "<b>%s:</b><br/>"
 		" - Stefan Becker<br/>"
 		" - Jakub Adam<br/>"
-		" - Jochen De Smet (Miranda port)<br/>"
-		" - Michael Lamb (Adium port)<br/>"
+		" - Jochen De Smet (retired, Miranda port)<br/>"
+		" - Michael Lamb (retired, Adium port)<br/>"
 		" - Anibal Avelar (retired)<br/>"
 		" - Gabriel Burt (retired)<br/>"
 		" - pier11 (retired)<br/>"
 		" - Tomáš Hrabčík (retired)<br/>"
 		"<br/>"
 		/* 13 */  "%s<br/>"
 		,
 		/* The next 13 texts make up the SIPE about note text */
@@ -205,17 +206,16 @@ gchar *sipe_core_about(void)
 		/* but write something similar to the following sentence: */
 		/* "Localization for <language name> (<language code>): <name>" */
 		_("Original texts in English (en): SIPE developers")
 		);
 }
 
 struct sipe_core_public *sipe_core_allocate(const gchar *signin_name,
 					    gboolean sso,
-					    const gchar *login_domain,
 					    const gchar *login_account,
 					    const gchar *password,
 					    const gchar *email,
 					    const gchar *email_url,
 					    const gchar **errmsg)
 {
 	struct sipe_core_private *sipe_private;
 	gchar **user_domain;
@@ -231,19 +231,20 @@ struct sipe_core_public *sipe_core_alloc
 	/* ensure that sign-in name format is name@domain */
 	if (!strchr(signin_name, '@') ||
 	    g_str_has_prefix(signin_name, "@") ||
 	    g_str_has_suffix(signin_name, "@")) {
 		*errmsg = _("User name should be a valid SIP URI\nExample: user@company.com");
 		return NULL;
 	}
 
-	/* ensure that Login & Password are valid when SSO is not selected */
-	if (!sso && (is_empty(login_account) || is_empty(password))) {
-		*errmsg = _("Login and password are required when Single Sign-On is not enabled");
+
+	/* ensure that Password is valid when SSO is not selected */
+	if (!sso && is_empty(password)) {
+		*errmsg = _("Password is required when Single Sign-On is not enabled");
 		return NULL;
 	}
 
 	/* ensure that email format is name@domain (if provided) */
 	if (!is_empty(email) &&
 	    (!strchr(email, '@') ||
 	     g_str_has_prefix(email, "@") ||
 	     g_str_has_suffix(email, "@")))
@@ -274,25 +275,28 @@ struct sipe_core_public *sipe_core_alloc
 			*errmsg = _("Email services URL should be valid if provided\n"
 				    "Example: https://exchange.corp.com/EWS/Exchange.asmx\n"
 				    "Example: https://domino.corp.com/maildatabase.nsf");
 			return NULL;
 		}
 		g_free(tmp);
 	}
 
+	/* re-use sign-in name if login is empty */
+	if (is_empty(login_account))
+		login_account = signin_name;
+
 	sipe_private = g_new0(struct sipe_core_private, 1);
 	SIPE_CORE_PRIVATE_FLAG_UNSET(SUBSCRIBED_BUDDIES);
 	SIPE_CORE_PRIVATE_FLAG_UNSET(INITIAL_PUBLISH);
 	SIPE_CORE_PRIVATE_FLAG_UNSET(SSO);
 	if (sso)
 		SIPE_CORE_PRIVATE_FLAG_SET(SSO);
 	sipe_private->username   = g_strdup(signin_name);
 	sipe_private->email      = is_empty(email) ? g_strdup(signin_name) : g_strdup(email);
-	sipe_private->authdomain = sso             ? NULL                  : g_strdup(login_domain);
 	sipe_private->authuser   = sso             ? NULL                  : g_strdup(login_account);
 	sipe_private->password   = sso             ? NULL                  : g_strdup(password);
 	sipe_private->public.sip_name   = g_strdup(user_domain[0]);
 	sipe_private->public.sip_domain = g_strdup(user_domain[1]);
 	g_strfreev(user_domain);
 
 	sipe_group_init(sipe_private);
 	sipe_buddy_init(sipe_private);
@@ -310,25 +314,19 @@ void sipe_core_backend_initialized(struc
 {
 	const gchar *value;
 
 	sipe_private->authentication_type = authentication;
 
 	/* user specified email login? */
 	value = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_EMAIL_LOGIN);
 	if (!is_empty(value)) {
-		/* Allowed domain-account separators are / or \ */
-		gchar **domain_user = g_strsplit_set(value, "/\\", 2);
-		gboolean has_domain = domain_user[1] != NULL;
-
-		sipe_private->email_authdomain = has_domain ? g_strdup(domain_user[0]) : NULL;
-		sipe_private->email_authuser   = g_strdup(domain_user[has_domain ? 1 : 0]);
-		sipe_private->email_password   = g_strdup(sipe_backend_setting(SIPE_CORE_PUBLIC,
-									       SIPE_SETTING_EMAIL_PASSWORD));
-		g_strfreev(domain_user);
+		sipe_private->email_authuser = g_strdup(value);
+		sipe_private->email_password = g_strdup(sipe_backend_setting(SIPE_CORE_PUBLIC,
+									     SIPE_SETTING_EMAIL_PASSWORD));
 	}
 }
 
 void sipe_core_connection_cleanup(struct sipe_core_private *sipe_private)
 {
 	g_free(sipe_private->epid);
 	sipe_private->epid = NULL;
 
@@ -397,20 +395,18 @@ void sipe_core_deallocate(struct sipe_co
 	sipe_cal_calendar_free(sipe_private->calendar);
 	sipe_certificate_free(sipe_private);
 
 	g_free(sipe_private->public.sip_name);
 	g_free(sipe_private->public.sip_domain);
 	g_free(sipe_private->username);
 	g_free(sipe_private->email_password);
 	g_free(sipe_private->email_authuser);
-	g_free(sipe_private->email_authdomain);
 	g_free(sipe_private->email);
 	g_free(sipe_private->password);
-	g_free(sipe_private->authdomain);
 	g_free(sipe_private->authuser);
 	g_free(sipe_private->status);
 	g_free(sipe_private->note);
 	g_free(sipe_private->ocs2005_user_states);
 
 	sipe_buddy_free(sipe_private);
 	g_hash_table_destroy(sipe_private->our_publications);
 	g_hash_table_destroy(sipe_private->user_state_publications);
@@ -427,25 +423,25 @@ void sipe_core_deallocate(struct sipe_co
 	g_free(sipe_private->media_relay_username);
 	g_free(sipe_private->media_relay_password);
 	sipe_media_relay_list_free(sipe_private->media_relays);
 #endif
 
 	g_free(sipe_private->persistentChatPool_uri);
 	g_free(sipe_private->addressbook_uri);
 	g_free(sipe_private->dlx_uri);
+	sipe_utils_slist_free_full(sipe_private->conf_mcu_types, g_free);
 	g_free(sipe_private);
 }
 
 void sipe_core_email_authentication(struct sipe_core_private *sipe_private,
 				    struct sipe_http_request *request)
 {
 	if (sipe_private->email_authuser) {
 		sipe_http_request_authentication(request,
-						 sipe_private->email_authdomain,
 						 sipe_private->email_authuser,
 						 sipe_private->email_password);
 	}
 }
 
 /*
   Local Variables:
   mode: c
--- a/libpurple/protocols/sipe/core/sipe-crypt-nss.c
+++ b/libpurple/protocols/sipe/core/sipe-crypt-nss.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-crypt-nss.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2010 pier11 <pier11@operamail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -70,17 +70,19 @@ void sipe_crypto_shutdown(void)
 	 * We don't want accedently switch off NSS possibly used by other plugin -
 	 * ssl-nss in Pidgin for example.
 	 */
 }
 
 /* PRIVATE methods */
 
 static PK11Context*
-sipe_crypt_ctx_create(CK_MECHANISM_TYPE cipherMech, const guchar *key, gsize key_length)
+sipe_crypt_ctx_create(CK_MECHANISM_TYPE cipherMech,
+		      const guchar *key, gsize key_length,
+		      const guchar *iv, gsize iv_length)
 {
 	PK11SlotInfo* slot;
 	SECItem keyItem;
 	SECItem ivItem;
 	PK11SymKey* SymKey;
 	SECItem *SecParam;
 	PK11Context* EncContext;
 
@@ -90,18 +92,18 @@ sipe_crypt_ctx_create(CK_MECHANISM_TYPE 
 	keyItem.type = siBuffer;
 	keyItem.data = (unsigned char *)key;
 	keyItem.len = key_length;
 
 	SymKey = PK11_ImportSymKey(slot, cipherMech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL);
 
 	/* Parameter for crypto context */
 	ivItem.type = siBuffer;
-	ivItem.data = NULL;
-	ivItem.len = 0;
+	ivItem.data = (unsigned char *)iv;
+	ivItem.len = iv_length;
 	SecParam = PK11_ParamFromIV(cipherMech, &ivItem);
 
 	EncContext = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, SymKey, SecParam);
 
 	PK11_FreeSymKey(SymKey);
 	SECITEM_FreeItem(SecParam, PR_TRUE);
 	PK11_FreeSlot(slot);
 
@@ -125,17 +127,17 @@ sipe_crypt_ctx_destroy(PK11Context* EncC
 static void
 sipe_crypt(CK_MECHANISM_TYPE cipherMech,
 	   const guchar *key, gsize key_length,
 	   const guchar *plaintext, gsize plaintext_length,
 	   guchar *encrypted_text)
 {
 	void *EncContext;
 
-	EncContext = sipe_crypt_ctx_create(cipherMech, key, key_length);
+	EncContext = sipe_crypt_ctx_create(cipherMech, key, key_length, NULL, 0);
 	sipe_crypt_ctx_encrypt(EncContext, plaintext, plaintext_length, encrypted_text);
 	sipe_crypt_ctx_destroy(EncContext);
 }
 
 
 /* PUBLIC methods */
 
 void
@@ -224,17 +226,17 @@ gboolean sipe_crypt_verify_rsa(gpointer 
   return(PK11_Verify(public, &sigItem, &digItem, NULL) == SECSuccess);
 }
 
 
 /* Stream RC4 cipher for file transfer */
 gpointer
 sipe_crypt_ft_start(const guchar *key)
 {
-	return sipe_crypt_ctx_create(CKM_RC4, key, 16);
+	return sipe_crypt_ctx_create(CKM_RC4, key, 16, NULL, 0);
 }
 
 void
 sipe_crypt_ft_stream(gpointer context,
 		     const guchar *in, gsize length,
 		     guchar *out)
 {
 	sipe_crypt_ctx_encrypt(context, in, length, out);
@@ -248,31 +250,46 @@ sipe_crypt_ft_destroy(gpointer context)
 
 /*
  * Stream RC4 cipher for TLS
  *
  * basically the same as for FT, but with variable key length
  */
 gpointer sipe_crypt_tls_start(const guchar *key, gsize key_length)
 {
-	return sipe_crypt_ctx_create(CKM_RC4, key, key_length);
+	return sipe_crypt_ctx_create(CKM_RC4, key, key_length, NULL, 0);
 }
 
 void sipe_crypt_tls_stream(gpointer context,
 			   const guchar *in, gsize length,
 			   guchar *out)
 {
 	sipe_crypt_ctx_encrypt(context, in, length, out);
 }
 
 void sipe_crypt_tls_destroy(gpointer context)
 {
 	sipe_crypt_ctx_destroy(context);
 }
 
+/* Block AES-CBC cipher for TLS */
+void sipe_crypt_tls_block(const guchar *key, gsize key_length,
+			  const guchar *iv, gsize iv_length,
+			  const guchar *in, gsize length,
+			  guchar *out)
+{
+	PK11Context* context = sipe_crypt_ctx_create(CKM_AES_CBC,
+						     key, key_length,
+						     iv, iv_length);
+	if (context) {
+		sipe_crypt_ctx_encrypt(context, in, length, out);
+		sipe_crypt_ctx_destroy(context);
+	}
+}
+
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
   tab-width: 8
   End:
 */
--- a/libpurple/protocols/sipe/core/sipe-crypt.h
+++ b/libpurple/protocols/sipe/core/sipe-crypt.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-crypt.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -60,8 +60,14 @@ void sipe_crypt_ft_stream(gpointer conte
 void sipe_crypt_ft_destroy(gpointer context);
 
 /* Stream RC4 cipher for TLS */
 gpointer sipe_crypt_tls_start(const guchar *key, gsize key_length);
 void sipe_crypt_tls_stream(gpointer context,
 			   const guchar *in, gsize length,
 			   guchar *out);
 void sipe_crypt_tls_destroy(gpointer context);
+
+/* Block AES-CBC cipher for TLS */
+void sipe_crypt_tls_block(const guchar *key, gsize key_length,
+			  const guchar *iv, gsize iv_length,
+			  const guchar *in, gsize length,
+			  guchar *out);
--- a/libpurple/protocols/sipe/core/sipe-domino.c
+++ b/libpurple/protocols/sipe/core/sipe-domino.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-domino.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2010 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -226,17 +226,17 @@ static void sipe_domino_process_calendar
 					time_t time_val = sipe_utils_str_to_time(tmp);
 
 					if (sipe_strequal(name, VIEWENTITY_START_TIME)) {
 						cal_event->start_time = time_val;
 					} else if (sipe_strequal(name, VIEWENTITY_END_TIME)) {
 						cal_event->end_time = time_val;
 					}
 
-					SIPE_DEBUG_INFO("\t\tdatetime=%s", asctime(gmtime(&time_val)));
+					SIPE_DEBUG_INFO("\t\tdatetime=%s", sipe_utils_time_to_debug_str(gmtime(&time_val)));
 					g_free(tmp);
 				} else if (sipe_strequal(name, VIEWENTITY_TEXT_LIST)) {
 					int i = 0;
 
 					/* test */
 					for (node3 = sipe_xml_child(node2, "textlist/text");
 					     node3;
 					     node3 = sipe_xml_twin(node3))
--- a/libpurple/protocols/sipe/core/sipe-ews-autodiscover.c
+++ b/libpurple/protocols/sipe/core/sipe-ews-autodiscover.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-ews-autodiscover.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2013-2014 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -19,16 +19,18 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Specification references:
  *
  *   - POX: plain old XML autodiscover
  *   - [MS-OXDSCLI]:     http://msdn.microsoft.com/en-us/library/cc463896.aspx
+ *   - Autdiscover for Exchange:
+ *                       http://msdn.microsoft.com/en-us/library/office/jj900169.aspx
  *   - POX autodiscover: http://msdn.microsoft.com/en-us/library/office/aa581522.aspx
  *   - POX redirect:     http://msdn.microsoft.com/en-us/library/office/dn467392.aspx
  */
 
 #include <string.h>
 
 #include <glib.h>
 
@@ -41,22 +43,27 @@
 #include "sipe-utils.h"
 #include "sipe-xml.h"
 
 struct sipe_ews_autodiscover_cb {
 	sipe_ews_autodiscover_callback *cb;
 	gpointer cb_data;
 };
 
+struct autodiscover_method {
+	const gchar *template;
+	gboolean redirect;
+};
+
 struct sipe_ews_autodiscover {
 	struct sipe_ews_autodiscover_data *data;
 	struct sipe_http_request *request;
 	GSList *callbacks;
 	gchar *email;
-	const gchar * const *method;
+	const struct autodiscover_method *method;
 	gboolean retry;
 	gboolean completed;
 };
 
 static void sipe_ews_autodiscover_complete(struct sipe_core_private *sipe_private,
 					   struct sipe_ews_autodiscover_data *ews_data)
 {
 	struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
@@ -100,34 +107,34 @@ static void sipe_ews_autodiscover_parse(
 			if (tmp)
 				ews_data->legacy_dn = g_strstrip(tmp);
 
 			/* extract settings */
 			for (; node; node = sipe_xml_twin(node)) {
 				gchar *type = sipe_xml_data(sipe_xml_child(node,
 									   "Type"));
 
-				if (sipe_strequal("EXCH", type)) {
-					g_free(type);
+				/* Exchange or Office 365 */
+				if (sipe_strequal("EXCH", type) ||
+				    sipe_strequal("EXPR", type)) {
 
 #define _URL(name, field) \
-			{ \
+			if (!ews_data->field) {	\
 				ews_data->field = sipe_xml_data(sipe_xml_child(node, #name)); \
 				SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: " #field " = '%s'", \
 						ews_data->field ? ews_data->field : "<NOT FOUND>"); \
 			}
 
+					/* use first entry */
 					_URL(ASUrl,  as_url);
 					_URL(EwsUrl, ews_url);
 					_URL(OABUrl, oab_url);
 					_URL(OOFUrl, oof_url);
 #undef _URL
 
-					break;
-
 				}
 				g_free(type);
 			}
 
 		/* POX autodiscover redirect to new email address? */
 		} else if ((node = sipe_xml_child(account, "RedirectAddr")) != NULL) {
 			gchar *addr = sipe_xml_data(node);
 
@@ -173,27 +180,29 @@ static void sipe_ews_autodiscover_parse(
 	sipe_xml_free(xml);
 
 	if (complete)
 		sipe_ews_autodiscover_complete(sipe_private, ews_data);
 }
 
 static void sipe_ews_autodiscover_response(struct sipe_core_private *sipe_private,
 					   guint status,
-					   SIPE_UNUSED_PARAMETER GSList *headers,
+					   GSList *headers,
 					   const gchar *body,
 					   gpointer data)
 {
 	struct sipe_ews_autodiscover *sea = data;
+	const gchar *type = sipe_utils_nameval_find(headers, "Content-Type");
 
 	sea->request = NULL;
 
 	switch (status) {
 	case SIPE_HTTP_STATUS_OK:
-		if (body)
+		/* only accept XML responses */
+		if (body && g_str_has_prefix(type, "text/xml"))
 			sipe_ews_autodiscover_parse(sipe_private, body);
 		else
 			sipe_ews_autodiscover_request(sipe_private, TRUE);
 		break;
 
 	case SIPE_HTTP_STATUS_CLIENT_FORBIDDEN:
 		/*
 		 * Authentication succeeded but we still weren't allowed to
@@ -227,17 +236,17 @@ static gboolean sipe_ews_autodiscover_ur
 				      " </Request>"
 				      "</Autodiscover>",
 				      sea->email);
 
 	SIPE_DEBUG_INFO("sipe_ews_autodiscover_url: trying '%s'", url);
 
 	sea->request = sipe_http_request_post(sipe_private,
 					      url,
-					      NULL,
+					      "Accept: text/xml\r\n",
 					      body,
 					      "text/xml",
 					      sipe_ews_autodiscover_response,
 					      sea);
 	g_free(body);
 
 	if (sea->request) {
 		sipe_core_email_authentication(sipe_private,
@@ -245,39 +254,89 @@ static gboolean sipe_ews_autodiscover_ur
 		sipe_http_request_allow_redirect(sea->request);
 		sipe_http_request_ready(sea->request);
 		return(TRUE);
 	}
 
 	return(FALSE);
 }
 
+static void sipe_ews_autodiscover_redirect_response(struct sipe_core_private *sipe_private,
+						    guint status,
+						    GSList *headers,
+						    SIPE_UNUSED_PARAMETER const gchar *body,
+						    gpointer data)
+{
+	struct sipe_ews_autodiscover *sea = data;
+	gboolean failed = (status != (guint) SIPE_HTTP_STATUS_ABORTED);
+
+	sea->request = NULL;
+
+	/* Start attempt with URL from redirect (3xx) response */
+	if ((status >= SIPE_HTTP_STATUS_REDIRECTION) &&
+	    (status <  SIPE_HTTP_STATUS_CLIENT_ERROR)) {
+		const gchar *location = sipe_utils_nameval_find_instance(headers,
+									 "Location",
+									 0);
+		if (location)
+			failed = !sipe_ews_autodiscover_url(sipe_private,
+							    location);
+	}
+
+	if (failed)
+		sipe_ews_autodiscover_request(sipe_private, TRUE);
+}
+
+static gboolean sipe_ews_autodiscover_redirect(struct sipe_core_private *sipe_private,
+					       const gchar *url)
+{
+	struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
+
+	SIPE_DEBUG_INFO("sipe_ews_autodiscover_redirect: trying '%s'", url);
+
+	sea->request = sipe_http_request_get(sipe_private,
+					     url,
+					     NULL,
+					     sipe_ews_autodiscover_redirect_response,
+					     sea);
+
+	if (sea->request) {
+		sipe_http_request_ready(sea->request);
+		return(TRUE);
+	}
+
+	return(FALSE);
+}
+
 static void sipe_ews_autodiscover_request(struct sipe_core_private *sipe_private,
 					  gboolean next_method)
 {
 	struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
-	static const gchar * const methods[] = {
-		"https://Autodiscover.%s/Autodiscover/Autodiscover.xml",
-		"http://Autodiscover.%s/Autodiscover/Autodiscover.xml",
-		"https://%s/Autodiscover/Autodiscover.xml",
-		NULL
+	static const struct autodiscover_method methods[] = {
+		{ "https://Autodiscover.%s/Autodiscover/Autodiscover.xml", FALSE },
+		{ "http://Autodiscover.%s/Autodiscover/Autodiscover.xml",  TRUE  },
+		{ "http://Autodiscover.%s/Autodiscover/Autodiscover.xml",  FALSE },
+		{ "https://%s/Autodiscover/Autodiscover.xml",              FALSE },
+		{ NULL,                                                    FALSE },
 	};
 
 	sea->retry = next_method;
 	if (sea->method) {
 		if (next_method)
 			sea->method++;
 	} else
 		sea->method = methods;
 
-	if (*sea->method) {
-		gchar *url = g_strdup_printf(*sea->method,
+	if (sea->method->template) {
+		gchar *url = g_strdup_printf(sea->method->template,
 					     strstr(sea->email, "@") + 1);
 
-		if (!sipe_ews_autodiscover_url(sipe_private, url))
+		if (!(sea->method->redirect ?
+		      sipe_ews_autodiscover_redirect(sipe_private, url) :
+		      sipe_ews_autodiscover_url(sipe_private, url)))
 			sipe_ews_autodiscover_request(sipe_private, TRUE);
 
 		g_free(url);
 
 	} else {
 		SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_autodiscover_request: no more methods to try!");
 		sipe_ews_autodiscover_complete(sipe_private, NULL);
 	}
--- a/libpurple/protocols/sipe/core/sipe-ews.c
+++ b/libpurple/protocols/sipe/core/sipe-ews.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-ews.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2010, 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -126,18 +126,18 @@ be great to implement too.
         "</t:TimeWindow>"\
         "<t:MergedFreeBusyIntervalInMinutes>15</t:MergedFreeBusyIntervalInMinutes>"\
         "<t:RequestedView>DetailedMerged</t:RequestedView>"\
       "</t:FreeBusyViewOptions>"\
     "</GetUserAvailabilityRequest>"\
   "</soap:Body>"\
 "</soap:Envelope>"
 
-#define SIPE_EWS_STATE_NONE			 0
-#define SIPE_EWS_STATE_IDLE			 1
+#define SIPE_EWS_STATE_IDLE			 0
+#define SIPE_EWS_STATE_AUTODISCOVER_TRIGGERED	 1
 #define SIPE_EWS_STATE_AVAILABILITY_SUCCESS	 2
 #define SIPE_EWS_STATE_AVAILABILITY_FAILURE	-2
 #define SIPE_EWS_STATE_OOF_SUCCESS		 3
 #define SIPE_EWS_STATE_OOF_FAILURE		-3
 
 char *
 sipe_ews_get_oof_note(struct sipe_calendar *cal)
 {
@@ -428,16 +428,19 @@ sipe_ews_run_state_machine(struct sipe_c
 	switch (cal->state) {
 	case SIPE_EWS_STATE_AVAILABILITY_FAILURE:
 	case SIPE_EWS_STATE_OOF_FAILURE:
 		cal->is_ews_disabled = TRUE;
 		break;
 	case SIPE_EWS_STATE_IDLE:
 		sipe_ews_do_avail_request(cal);
 		break;
+	case SIPE_EWS_STATE_AUTODISCOVER_TRIGGERED:
+		/* do nothing */
+		break;
 	case SIPE_EWS_STATE_AVAILABILITY_SUCCESS:
 		sipe_ews_do_oof_request(cal);
 		break;
 	case SIPE_EWS_STATE_OOF_SUCCESS:
 		{
 			struct sipe_core_private *sipe_private = cal->sipe_private;
 
 			cal->state = SIPE_EWS_STATE_IDLE;
@@ -474,18 +477,18 @@ void sipe_ews_update_calendar(struct sip
 
 	SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_update_calendar: started.");
 
 	sipe_cal_calendar_init(sipe_private);
 	cal = sipe_private->calendar;
 
 	if (cal->is_ews_disabled) {
 		SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_update_calendar: disabled, exiting.");
-	} else if (!cal->as_url && !cal->ews_autodiscover_triggered) {
-		cal->ews_autodiscover_triggered = TRUE;
+	} else if (!cal->as_url && (cal->state != SIPE_EWS_STATE_AUTODISCOVER_TRIGGERED)) {
+		cal->state = SIPE_EWS_STATE_AUTODISCOVER_TRIGGERED;
 		sipe_ews_autodiscover_start(sipe_private,
 					    sipe_calendar_ews_autodiscover_cb,
 					    cal);
 	} else {
 		sipe_ews_run_state_machine(cal);
 		SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_update_calendar: finished.");
 	}
 }
--- a/libpurple/protocols/sipe/core/sipe-groupchat.c
+++ b/libpurple/protocols/sipe/core/sipe-groupchat.c
@@ -372,20 +372,22 @@ static gboolean groupchat_expired_sessio
 	struct sipe_groupchat *groupchat = sipe_private->groupchat;
 
 	/* 481 Call Leg Does Not Exist -> server dropped session */
 	if (msg->response == 481) {
 		struct sip_session *session = groupchat->session;
 		struct sip_dialog *dialog = sipe_dialog_find(session,
 							     session->with);
 
-		/* close dialog from our side */
-		sip_transport_bye(sipe_private, dialog);
-		sipe_dialog_remove(session, session->with);
-		/* dialog is no longer valid */
+		if (dialog) {
+			/* close dialog from our side */
+			sip_transport_bye(sipe_private, dialog);
+			sipe_dialog_remove(session, session->with);
+			/* dialog is no longer valid */
+		}
 
 		/* re-initialize groupchat session */
 		groupchat->session = NULL;
 		groupchat->connected = FALSE;
 		sipe_groupchat_init(sipe_private);
 	} else {
 		sipe_schedule_seconds(sipe_private,
 				      "<+groupchat-expires>",
@@ -535,34 +537,37 @@ static gboolean chatserver_command_respo
 	}
 	return TRUE;
 }
 
 static struct sipe_groupchat_msg *chatserver_command(struct sipe_core_private *sipe_private,
 						     const gchar *cmd)
 {
 	struct sipe_groupchat *groupchat = sipe_private->groupchat;
-	struct sip_dialog *dialog = sipe_dialog_find(groupchat->session,
-						     groupchat->session->with);
 	struct sipe_groupchat_msg *msg = NULL;
 
-	if (dialog) {
-		struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
-		struct transaction *trans;
+	if (groupchat->session) {
+		struct sip_dialog *dialog = sipe_dialog_find(groupchat->session,
+							     groupchat->session->with);
+
+		if (dialog) {
+			struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
+			struct transaction *trans;
 
-		msg = generate_xccos_message(groupchat, cmd);
-		trans = sip_transport_info(sipe_private,
-					   "Content-Type: text/plain\r\n",
-					   msg->xccos,
-					   dialog,
-					   chatserver_command_response);
+			msg = generate_xccos_message(groupchat, cmd);
+			trans = sip_transport_info(sipe_private,
+						   "Content-Type: text/plain\r\n",
+						   msg->xccos,
+						   dialog,
+						   chatserver_command_response);
 
-		payload->destroy = sipe_groupchat_msg_remove;
-		payload->data    = msg;
-		trans->payload   = payload;
+			payload->destroy = sipe_groupchat_msg_remove;
+			payload->data    = msg;
+			trans->payload   = payload;
+		}
 	}
 
 	return(msg);
 }
 
 static void chatserver_response_uri(struct sipe_core_private *sipe_private,
 				    struct sip_session *session,
 				    SIPE_UNUSED_PARAMETER guint result,
--- a/libpurple/protocols/sipe/core/sipe-http-request.c
+++ b/libpurple/protocols/sipe/core/sipe-http-request.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-http-request.c
 *
  * pidgin-sipe
  *
- * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -61,17 +61,16 @@ struct sipe_http_request {
 	struct sipe_http_session *session;
 
 	gchar *path;
 	gchar *headers;
 	gchar *body;           /* NULL for GET */
 	gchar *content_type;   /* NULL if body == NULL */
 	gchar *authorization;
 
-	const gchar *domain;   /* not copied */
 	const gchar *user;     /* not copied */
 	const gchar *password; /* not copied */
 
 	sipe_http_response_callback *cb;
 	gpointer cb_data;
 
 	guint32 flags;
 };
@@ -281,16 +280,31 @@ static gboolean sipe_http_request_respon
 		type   = sip_sec_context_type(conn_public->context);
 
 		if (!header) {
 			SIPE_DEBUG_INFO("sipe_http_request_response_unauthorized: expected authentication scheme %s not found",
 					name);
 			return(failed);
 		}
 
+		if (conn_public->cached_authorization) {
+			/*
+			 * The "Basic" scheme doesn't have any state.
+			 *
+			 * If we enter here then we have already tried "Basic"
+			 * authentication once for this request and it was
+			 * rejected by the server. As all future requests will
+			 * also be rejected, we need to abort here in order to
+			 * prevent an endless request/401/request/... loop.
+			 */
+			SIPE_DEBUG_INFO("sipe_http_request_response_unauthorized: Basic authentication has failed for host '%s', please check user name and password!",
+					conn_public->host);
+			return(failed);
+		}
+
 	} else {
 #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI)
 #define DEBUG_STRING ", NTLM and Negotiate"
 		/* Use "Negotiate" unless the user requested "NTLM" */
 		if (sipe_private->authentication_type != SIPE_AUTHENTICATION_TYPE_NTLM)
 			header = sipmsg_find_auth_header(msg, "Negotiate");
 		if (header) {
 			type   = SIPE_AUTHENTICATION_TYPE_NEGOTIATE;
@@ -312,17 +326,16 @@ static gboolean sipe_http_request_respon
 	}
 
 	if (header) {
 		if (!conn_public->context) {
 			gboolean valid = req->flags & SIPE_HTTP_REQUEST_FLAG_AUTHDATA;
 			conn_public->context = sip_sec_create_context(type,
 								      !valid, /* Single Sign-On flag */
 								      TRUE,   /* connection-based for HTTP */
-								      valid ? req->domain   : NULL,
 								      valid ? req->user     : NULL,
 								      valid ? req->password : NULL);
 		}
 
 		if (conn_public->context) {
 			gchar **parts = g_strsplit(header, " ", 0);
 			gchar *spn    = g_strdup_printf("HTTP/%s", conn_public->host);
 			gchar *token_out;
@@ -547,17 +560,16 @@ struct sipe_http_request *sipe_http_requ
 	if (body) {
 		req->body         = g_strdup(body);
 		req->content_type = g_strdup(content_type);
 	}
 
 	/* default authentication */
 	if (!SIPE_CORE_PRIVATE_FLAG_IS(SSO))
 		sipe_http_request_authentication(req,
-						 sipe_private->authdomain,
 						 sipe_private->authuser,
 						 sipe_private->password);
 
 	sipe_http_request_enqueue(sipe_private, req, parsed_uri);
 
 	return(req);
 }
 
@@ -605,22 +617,20 @@ void sipe_http_request_session(struct si
 }
 
 void sipe_http_request_allow_redirect(struct sipe_http_request *request)
 {
 	request->flags |= SIPE_HTTP_REQUEST_FLAG_REDIRECT;
 }
 
 void sipe_http_request_authentication(struct sipe_http_request *request,
-				      const gchar *domain,
 				      const gchar *user,
 				      const gchar *password)
 {
 	request->flags   |= SIPE_HTTP_REQUEST_FLAG_AUTHDATA;
-	request->domain   = domain;
 	request->user     = user;
 	request->password = password;
 }
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
--- a/libpurple/protocols/sipe/core/sipe-http-transport.c
+++ b/libpurple/protocols/sipe/core/sipe-http-transport.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-http-transport.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2014 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -106,19 +106,26 @@ static void sipe_http_transport_free(gpo
 static void sipe_http_transport_drop(struct sipe_http *http,
 				     struct sipe_http_connection *conn,
 				     const gchar *message)
 {
 	SIPE_DEBUG_INFO("sipe_http_transport_drop: dropping connection '%s': %s",
 			conn->host_port,
 			message ? message : "REASON UNKNOWN");
 
-	/* this triggers sipe_http_transport_free */
-	g_hash_table_remove(http->connections,
-			    conn->host_port);
+#if GLIB_CHECK_VERSION(2,30,0)
+	/* this triggers sipe_http_transport_free() */
+	g_hash_table_remove(http->connections, conn->host_port);
+#else
+	/* GLIB < 2.30 calls destroy notifiers *before* removing the entry */
+	/* which can cause a race condition with sipe_http_transport_new() */
+	g_hash_table_steal(http->connections, conn->host_port);
+	sipe_http_transport_free(conn);
+#endif
+	/* conn is no longer valid */
 }
 
 static void start_timer(struct sipe_core_private *sipe_private,
 			time_t current_time);
 static void sipe_http_transport_timeout(struct sipe_core_private *sipe_private,
 					gpointer data)
 {
 	struct sipe_http *http = sipe_private->http;
@@ -263,18 +270,20 @@ static void sipe_http_transport_input(st
 
 	/* according to the RFC remove CRLF at the beginning */
 	while (*current == '\r' || *current == '\n') {
 		current++;
 	}
 	if (current != connection->buffer)
 		sipe_utils_shrink_buffer(connection, current);
 
-	if ((current = strstr(connection->buffer, "\r\n\r\n")) != NULL) {
+	if (conn->connection &&
+	    (current = strstr(connection->buffer, "\r\n\r\n")) != NULL) {
 		struct sipmsg *msg;
+		gboolean drop = FALSE;
 		gboolean next;
 
 		current += 2;
 		current[0] = '\0';
 		msg = sipmsg_parse_header(connection->buffer);
 		if (!msg) {
 			/* restore header for next try */
 			current[0] = '\r';
@@ -382,23 +391,31 @@ static void sipe_http_transport_input(st
 
 				/* restore header for next try */
 				sipmsg_free(msg);
 				current[0] = '\r';
 				return;
 			}
 		}
 
+		if (msg->response == SIPMSG_RESPONSE_FATAL_ERROR) {
+			/* fatal header parse error */
+			msg->response = SIPE_HTTP_STATUS_SERVER_ERROR;
+			drop          = TRUE;
+		} else if (sipe_strcase_equal(sipmsg_find_header(msg, "Connection"), "close")) {
+			SIPE_DEBUG_INFO("sipe_http_transport_input: server requested close '%s'",
+					conn->host_port);
+			drop          = TRUE;
+		}
+
 		sipe_http_request_response(SIPE_HTTP_CONNECTION_PUBLIC, msg);
 		next = sipe_http_request_pending(SIPE_HTTP_CONNECTION_PUBLIC);
 
-		if (sipe_strcase_equal(sipmsg_find_header(msg, "Connection"), "close")) {
+		if (drop) {
 			/* drop backend connection */
-			SIPE_DEBUG_INFO("sipe_http_transport_input: server requested close '%s'",
-					conn->host_port);
 			sipe_backend_transport_disconnect(conn->connection);
 			conn->connection       = NULL;
 			conn->public.connected = FALSE;
 
 			/* if we have pending requests we need to trigger re-connect */
 			if (next)
 				sipe_http_transport_new(conn->public.sipe_private,
 							conn->public.host,
--- a/libpurple/protocols/sipe/core/sipe-http.h
+++ b/libpurple/protocols/sipe/core/sipe-http.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-http.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -150,16 +150,14 @@ void sipe_http_request_session(struct si
  * @param request pointer to opaque HTTP request data structure
  */
 void sipe_http_request_allow_redirect(struct sipe_http_request *request);
 
 /**
  * Provide authentication information for HTTP request
  *
  * @param request  pointer to opaque HTTP request data structure
- * @param domain   domain name (MUST stay valid for duration of request!)
  * @param user     user name   (MUST stay valid for duration of request!)
  * @param password Password    (MUST stay valid for duration of request!)
  */
 void sipe_http_request_authentication(struct sipe_http_request *request,
-				      const gchar *domain,
 				      const gchar *user,
 				      const gchar *password);
--- a/libpurple/protocols/sipe/core/sipe-notify.c
+++ b/libpurple/protocols/sipe/core/sipe-notify.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-notify.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -93,16 +93,17 @@ static void sipe_process_provisioning_v2
 		const gchar *node_name = sipe_xml_attribute(node, "name");
 
 		/* ServerConfiguration */
 		if (sipe_strequal("ServerConfiguration", node_name)) {
 			const gchar *dlx_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
 					"dlxExternalUrl" : "dlxInternalUrl";
 			const gchar *addressbook_uri_str = SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ?
 					"absExternalServerUrl" : "absInternalServerUrl";
+			gchar *ucPC2PCAVEncryption = NULL;
 
 			g_free(sipe_private->focus_factory_uri);
 			sipe_private->focus_factory_uri = sipe_xml_data(sipe_xml_child(node, "focusFactoryUri"));
 			SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
 					sipe_private->focus_factory_uri ? sipe_private->focus_factory_uri : "");
 
 			g_free(sipe_private->dlx_uri);
 			sipe_private->dlx_uri = sipe_xml_data(sipe_xml_child(node, dlx_uri_str));
@@ -124,16 +125,27 @@ static void sipe_process_provisioning_v2
 			sipe_private->mras_uri = g_strstrip(sipe_xml_data(sipe_xml_child(node, "mrasUri")));
 			SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
 					sipe_private->mras_uri ? sipe_private->mras_uri : "");
 
 			if (sipe_private->mras_uri)
 					sipe_media_get_av_edge_credentials(sipe_private);
 #endif
 
+			ucPC2PCAVEncryption = g_strstrip(sipe_xml_data(sipe_xml_child(node, "ucPC2PCAVEncryption")));
+			if (sipe_strequal(ucPC2PCAVEncryption, "SupportEncryption")) {
+				sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_OPTIONAL;
+			} else if (sipe_strequal(ucPC2PCAVEncryption, "DoNotSupportEncryption")) {
+				sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_REJECTED;
+			} else {
+				// "RequireEncryption" or any unknown value.
+				sipe_private->server_av_encryption_policy = SIPE_ENCRYPTION_POLICY_REQUIRED;
+			}
+			g_free(ucPC2PCAVEncryption);
+
 		/* persistentChatConfiguration */
 		} else if (sipe_strequal("persistentChatConfiguration", node_name)) {
 			const sipe_xml *property;
 			gboolean enabled = FALSE;
 			gchar *uri = NULL;
 
 			for (property = sipe_xml_child(node, "propertyEntryList/property");
 			     property;
@@ -166,16 +178,22 @@ static void sipe_process_provisioning_v2
 
 	if (sipe_private->dlx_uri && sipe_private->addressbook_uri) {
 		/* Some buddies might have been added before we received this
 		 * provisioning notify with DLX and addressbook URIs. Now we can
 		 * trigger an update of their photos. */
 		sipe_buddy_refresh_photos(sipe_private);
 	}
 
+	if (sipe_private->focus_factory_uri) {
+		/* Fill the list of conferencing capabilities enabled on
+		 * the server. */
+		sipe_conf_get_capabilities(sipe_private);
+	}
+
 	if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
 		/* persistentChatPool_uri has been set at this point */
 		sipe_groupchat_init(sipe_private);
 }
 
 static void process_incoming_notify_rlmi_resub(struct sipe_core_private *sipe_private,
 					       const gchar *data, unsigned len)
 {
@@ -667,16 +685,39 @@ static void process_incoming_notify_rlmi
 						g_free(city);
 						g_free(state);
 						g_free(zipcode);
 						g_free(country_code);
 
 						break;
 					}
 				}
+				/* photo */
+				for (node = sipe_xml_child(card, "photo");
+				     node;
+				     node = sipe_xml_twin(node)) {
+					gchar *photo_url = sipe_xml_data(sipe_xml_child(node, "uri"));
+					gchar *hash = sipe_xml_data(sipe_xml_child(node, "hash"));
+					gboolean found = FALSE;
+
+					if (!is_empty(uri) && !is_empty(hash)) {
+						sipe_buddy_update_photo(sipe_private,
+									uri,
+									hash,
+									photo_url,
+									NULL);
+						found = TRUE;
+					}
+
+					g_free(hash);
+					g_free(photo_url);
+
+					if (found)
+						break;
+				}
 			}
 		}
 		/* note */
 		else if (sipe_strequal(attrVar, "note"))
 		{
 			if (!has_note_cleaned) {
 				has_note_cleaned = TRUE;
 
@@ -713,32 +754,41 @@ static void process_incoming_notify_rlmi
 		}
 		/* state */
 		else if(sipe_strequal(attrVar, "state"))
 		{
 			char *tmp;
 			int availability;
 			const sipe_xml *xn_availability;
 			const sipe_xml *xn_activity;
+			const sipe_xml *xn_device;
 			const sipe_xml *xn_meeting_subject;
 			const sipe_xml *xn_meeting_location;
 			const gchar *legacy_activity;
 
 			xn_node = sipe_xml_child(xn_category, "state");
 			if (!xn_node) continue;
 			xn_availability = sipe_xml_child(xn_node, "availability");
 			if (!xn_availability) continue;
 			xn_activity = sipe_xml_child(xn_node, "activity");
 			xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
 			xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
 
 			tmp = sipe_xml_data(xn_availability);
 			availability = atoi(tmp);
 			g_free(tmp);
 
+			sbuddy->is_mobile = FALSE;
+			xn_device = sipe_xml_child(xn_node, "device");
+			if (xn_device) {
+				tmp = sipe_xml_data(xn_device);
+				sbuddy->is_mobile = !g_ascii_strcasecmp(tmp, "Mobile");
+				g_free(tmp);
+			}
+
 			/* activity */
 			g_free(sbuddy->activity);
 			sbuddy->activity = NULL;
 			if (xn_activity) {
 				const char *token = sipe_xml_attribute(xn_activity, "token");
 				const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
 
 				/* from token */
--- a/libpurple/protocols/sipe/core/sipe-ocs2005.c
+++ b/libpurple/protocols/sipe/core/sipe-ocs2005.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-ocs2005.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -293,18 +293,18 @@ static void send_presence_soap(struct si
 	gchar *from = sip_uri_self(sipe_private);
 	time_t now = time(NULL);
 	gchar *since_time_str = sipe_utils_time_to_str(now);
 	const gchar *oof_note = cal ? sipe_ews_get_oof_note(cal) : NULL;
 	const char *user_input;
 	gboolean pub_oof = cal && oof_note && (!sipe_private->note || cal->updated > sipe_private->note_since);
 
 	if (oof_note && sipe_private->note) {
-		SIPE_DEBUG_INFO("cal->oof_start           : %s", asctime(localtime(&(cal->oof_start))));
-		SIPE_DEBUG_INFO("sipe_private->note_since : %s", asctime(localtime(&(sipe_private->note_since))));
+		SIPE_DEBUG_INFO("cal->oof_start           : %s", sipe_utils_time_to_debug_str(localtime(&(cal->oof_start))));
+		SIPE_DEBUG_INFO("sipe_private->note_since : %s", sipe_utils_time_to_debug_str(localtime(&(sipe_private->note_since))));
 	}
 
 	SIPE_DEBUG_INFO("sipe_private->note  : %s", sipe_private->note ? sipe_private->note : "");
 
 	if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH) ||
 	    do_reset_status)
 		sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
 
@@ -331,17 +331,17 @@ static void send_presence_soap(struct si
 		/* to protocol internal plain text format */
 		tmp = sipe_backend_markup_strip_html(note_pub);
 		res_note = g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML, tmp);
 		g_free(tmp);
 	}
 
 	/* User State */
 	if (!do_reset_status) {
-		if (sipe_status_changed_by_user(sipe_private) &&
+		if (sipe_private->status_set_by_user &&
 		    !do_publish_calendar &&
 		    SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
 			const gchar *activity_token;
 			int avail_2007 = sipe_ocs2007_availability_from_status(sipe_private->status,
 									       &activity_token);
 
 			states = g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES,
 						avail_2007,
@@ -368,17 +368,17 @@ static void send_presence_soap(struct si
 		calendar_data = g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR,
 						!is_empty(cal->legacy_dn) ? cal->legacy_dn : cal->email,
 						fb_start_str,
 						free_busy_base64);
 		g_free(fb_start_str);
 		g_free(free_busy_base64);
 	}
 
-	user_input = (sipe_status_changed_by_user(sipe_private) ||
+	user_input = (sipe_private->status_set_by_user ||
 		      sipe_is_user_available(sipe_private)) ?
 		"active" : "idle";
 
 	/* generate XML */
 	body = g_strdup_printf(SIPE_SOAP_SET_PRESENCE,
 			       sipe_private->username,
 			       sipe_ocs2005_availability_from_status(sipe_private),
 			       sipe_ocs2005_activity_from_status(sipe_private),
@@ -423,17 +423,17 @@ void sipe_ocs2005_apply_calendar_status(
 	int cal_status = sipe_cal_get_status(sbuddy, time(NULL), &cal_avail_since);
 	int avail;
 	gchar *self_uri;
 
 	if (!sbuddy) return;
 
 	if (cal_status < SIPE_CAL_NO_DATA) {
 		SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status      : %d for %s", cal_status, sbuddy->name);
-		SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since)));
+		SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", sipe_utils_time_to_debug_str(localtime(&cal_avail_since)));
 	}
 
 	/* scheduled Cal update call */
 	if (!status_id) {
 		status_id = sbuddy->last_non_cal_status_id;
 		g_free(sbuddy->activity);
 		sbuddy->activity = g_strdup(sbuddy->last_non_cal_activity);
 	}
@@ -441,28 +441,28 @@ void sipe_ocs2005_apply_calendar_status(
 	if (!status_id) {
 		SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
 				sbuddy->name ? sbuddy->name : "" );
 		return;
 	}
 
 	/* adjust to calendar status */
 	if (cal_status != SIPE_CAL_NO_DATA) {
-		SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy->user_avail_since)));
+		SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", sipe_utils_time_to_debug_str(localtime(&sbuddy->user_avail_since)));
 
 		if ((cal_status == SIPE_CAL_BUSY) &&
 		    (cal_avail_since > sbuddy->user_avail_since) &&
 		    sipe_ocs2007_status_is_busy(status_id)) {
 			status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
 			g_free(sbuddy->activity);
 			sbuddy->activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_MEETING));
 		}
 		avail = sipe_ocs2007_availability_from_status(status_id, NULL);
 
-		SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since  : %s", asctime(localtime(&sbuddy->activity_since)));
+		SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since  : %s", sipe_utils_time_to_debug_str(localtime(&sbuddy->activity_since)));
 		if (cal_avail_since > sbuddy->activity_since) {
 			if ((cal_status == SIPE_CAL_OOF) &&
 			    sipe_ocs2007_availability_is_away(avail)) {
 				g_free(sbuddy->activity);
 				sbuddy->activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
 			}
 		}
 	}
@@ -521,19 +521,19 @@ void sipe_ocs2005_schedule_status_update
 					 time_t calculate_from)
 {
 #define SCHEDULE_INTERVAL 15 * 60 /* 15 min */
 
 	/* start of the beginning of closest 15 min interval. */
 	time_t next_start = (calculate_from / SCHEDULE_INTERVAL + 1) * SCHEDULE_INTERVAL;
 
 	SIPE_DEBUG_INFO("sipe_ocs2005_schedule_status_update: calculate_from time: %s",
-			asctime(localtime(&calculate_from)));
+			sipe_utils_time_to_debug_str(localtime(&calculate_from)));
 	SIPE_DEBUG_INFO("sipe_ocs2005_schedule_status_update: next start time    : %s",
-			asctime(localtime(&next_start)));
+			sipe_utils_time_to_debug_str(localtime(&next_start)));
 
 	sipe_schedule_seconds(sipe_private,
 			      "<+2005-cal-status>",
 			      NULL,
 			      next_start - time(NULL),
 			      update_calendar_status,
 			      NULL);
 }
--- a/libpurple/protocols/sipe/core/sipe-ocs2007.c
+++ b/libpurple/protocols/sipe/core/sipe-ocs2007.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-ocs2007.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -710,19 +710,19 @@ void sipe_core_change_access_level_for_d
 static void schedule_publish_update(struct sipe_core_private *sipe_private,
 				    time_t calculate_from)
 {
 	int interval = 5*60;
 	/** start of the beginning of closest 5 min interval. */
 	time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
 
 	SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
-			asctime(localtime(&calculate_from)));
+			sipe_utils_time_to_debug_str(localtime(&calculate_from)));
 	SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time    : %s",
-			asctime(localtime(&next_start)));
+			sipe_utils_time_to_debug_str(localtime(&next_start)));
 
 	sipe_schedule_seconds(sipe_private,
 			      "<+2007-cal-status>",
 			      NULL,
 			      next_start - time(NULL),
 			      sipe_ocs2007_presence_publish,
 			      NULL);
 }
@@ -967,17 +967,18 @@ static gchar *sipe_publish_get_category_
  *
  * @param note a note in Sipe internal HTML format
  * @param note_type either personal or OOF
  */
 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
 					     const char *note, /* html */
 					     const char *note_type,
 					     time_t note_start,
-					     time_t note_end)
+					     time_t note_end,
+					     gboolean force_publish)
 {
 	guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
 	/* key is <category><instance><container> */
 	gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
 	gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
 	gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
 
 	struct sipe_publication *publication_note_200 =
@@ -996,17 +997,17 @@ static gchar *sipe_publish_get_category_
 
 	g_free(tmp);
 	tmp = NULL;
 	g_free(key_note_200);
 	g_free(key_note_300);
 	g_free(key_note_400);
 
 	/* we even need to republish empty note */
-	if (sipe_strequal(n1, n2))
+	if (!force_publish && sipe_strequal(n1, n2))
 	{
 		SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
 		g_free(n1);
 		return NULL; /* nothing to update */
 	}
 
 	start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
 	g_free(tmp);
@@ -1453,33 +1454,34 @@ static gchar *sipe_publish_get_category_
 
 /**
  * A service method - use
  * - send_publish_get_category_state_machine and
  * - send_publish_get_category_state_user instead.
  * Must be g_free'd after use.
  */
 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
+					      gboolean force_publish,
 					      gboolean is_user_state)
 {
 	int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
 	guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
 					 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
 	/* key is <category><instance><container> */
 	gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
 	gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
 	struct sipe_publication *publication_2 =
 		g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_2);
 	struct sipe_publication *publication_3 =
 		g_hash_table_lookup(g_hash_table_lookup(sipe_private->our_publications, "state"), key_3);
 
 	g_free(key_2);
 	g_free(key_3);
 
-	if (publication_2 && (publication_2->availability == availability))
+	if (!force_publish && publication_2 && (publication_2->availability == availability))
 	{
 		SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
 		return NULL; /* nothing to update */
 	}
 
 	return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
 				instance,
 				publication_2 ? publication_2->version : 0,
@@ -1488,56 +1490,61 @@ static gchar *sipe_publish_get_category_
 				publication_3 ? publication_3->version : 0,
 				availability);
 }
 
 /**
  * Returns 'machineState' XML part for publication.
  * Must be g_free'd after use.
  */
-static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private)
+static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private,
+						      gboolean force_publish)
 {
-	return sipe_publish_get_category_state(sipe_private, FALSE);
+	return sipe_publish_get_category_state(sipe_private, force_publish, FALSE);
 }
 
 /**
  * Returns 'userState' XML part for publication.
  * Must be g_free'd after use.
  */
-static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private)
+static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private,
+						   gboolean force_publish)
 {
-	return sipe_publish_get_category_state(sipe_private, TRUE);
+	return sipe_publish_get_category_state(sipe_private, force_publish, TRUE);
 }
 
 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
 {
 	gchar *pub_device   = sipe_publish_get_category_device(sipe_private);
 	gchar *pub_machine;
 	gchar *publications;
 
 	sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);
 
-	pub_machine  = sipe_publish_get_category_state_machine(sipe_private);
+	pub_machine  = sipe_publish_get_category_state_machine(sipe_private,
+							       TRUE);
 	publications = g_strdup_printf("%s%s",
 				       pub_device,
 				       pub_machine ? pub_machine : "");
 	g_free(pub_device);
 	g_free(pub_machine);
 
 	send_presence_publish(sipe_private, publications);
 	g_free(publications);
 }
 
 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
 								struct sipmsg *msg,
 								struct transaction *trans)
 {
 	const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
 
-	if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
+	if (msg->response == 200 && g_str_has_prefix(contenttype, "application/vnd-microsoft-roaming-self+xml")) {
+		sipe_ocs2007_process_roaming_self(sipe_private, msg);
+	} else if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
 		sipe_xml *xml;
 		const sipe_xml *node;
 		gchar *fault_code;
 		GHashTable *faults;
 		int index_our;
 		gboolean has_device_publication = FALSE;
 
 		xml = sipe_xml_parse(msg->body, msg->bodylen);
@@ -1623,17 +1630,17 @@ static gboolean process_send_presence_ca
 		}
 		sipe_xml_free(xml);
 		g_hash_table_destroy(faults);
 
 		/* rebublishing with right versions */
 		if (has_device_publication) {
 			send_publish_category_initial(sipe_private);
 		} else {
-			sipe_status_update(sipe_private, NULL);
+			sipe_ocs2007_category_publish(sipe_private, TRUE);
 		}
 	}
 	return TRUE;
 }
 
 /**
  * Publishes categories.
  * @param uri		(%s) Self URI. Ex.: sip:alice7@boston.local
@@ -1698,22 +1705,20 @@ void sipe_ocs2007_presence_publish(struc
 		return;
 	}
 
 	SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
 	if (cal->cal_events) {
 		event = sipe_cal_get_event(cal->cal_events, time(NULL));
 	}
 
-	if (!event) {
-		SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
+	if (event) {
+		sipe_cal_event_debug(event, "publish_calendar_status_self: current event is:\n");
 	} else {
-		char *desc = sipe_cal_event_describe(event);
-		SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc ? desc : "");
-		g_free(desc);
+		SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
 	}
 
 	/* Logic
 	if OOF
 		OOF publish, Busy clean
 	ilse if Busy
 		OOF clean, Busy publish
 	else
@@ -1730,17 +1735,17 @@ void sipe_ocs2007_presence_publish(struc
 		pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL,  cal->email, SIPE_CAL_BUSY);
 	}
 
 	oof_note = sipe_ews_get_oof_note(cal);
 	if (sipe_strequal("Scheduled", cal->oof_state)) {
 		oof_start = cal->oof_start;
 		oof_end = cal->oof_end;
 	}
-	pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end);
+	pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end, FALSE);
 
 	pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
 	pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
 
 	if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
 		SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
 	} else {
 		gchar *publications = g_strdup_printf("%s%s%s%s%s",
@@ -1759,42 +1764,55 @@ void sipe_ocs2007_presence_publish(struc
 	g_free(pub_calendar);
 	g_free(pub_calendar2);
 	g_free(pub_oof_note);
 
 	/* repeat scheduling */
 	schedule_publish_update(sipe_private, time(NULL));
 }
 
-void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private)
+void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private,
+				   gboolean force_publish)
 {
-	gchar *pub_state = sipe_status_changed_by_user(sipe_private) ?
-				sipe_publish_get_category_state_user(sipe_private) :
-				sipe_publish_get_category_state_machine(sipe_private);
-	gchar *pub_note = sipe_publish_get_category_note(sipe_private,
-							 sipe_private->note,
-							 SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
-							 0,
-							 0);
-	gchar *publications;
+	GString *publications = g_string_new("");
+	gchar *tmp;
 
-	if (!pub_state && !pub_note) {
-		SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
-		return;
+	if (force_publish || sipe_private->status_set_by_user) {
+		tmp = sipe_publish_get_category_state_user(sipe_private,
+							   force_publish);
+		if (tmp) {
+			g_string_append(publications, tmp);
+			g_free(tmp);
+		}
 	}
 
-	publications = g_strdup_printf("%s%s",
-				       pub_state ? pub_state : "",
-				       pub_note ? pub_note : "");
+	tmp = sipe_publish_get_category_state_machine(sipe_private,
+						      force_publish);
+	if (tmp) {
+		g_string_append(publications, tmp);
+		g_free(tmp);
+	}
 
-	g_free(pub_state);
-	g_free(pub_note);
+	tmp = sipe_publish_get_category_note(sipe_private,
+					     sipe_private->note,
+					     SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
+					     0,
+					     0,
+					     force_publish);
+	if (tmp) {
+		g_string_append(publications, tmp);
+		g_free(tmp);
+	}
 
-	send_presence_publish(sipe_private, publications);
-	g_free(publications);
+	if (publications->len)
+		send_presence_publish(sipe_private, publications->str);
+	else
+		SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
+
+	g_string_free(publications, TRUE);
 }
 
 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
 {
 	gchar *publications = NULL;
 	guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
 
 	/* key is <category><instance><container> */
--- a/libpurple/protocols/sipe/core/sipe-ocs2007.h
+++ b/libpurple/protocols/sipe/core/sipe-ocs2007.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-ocs2007.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -44,17 +44,18 @@ gboolean sipe_ocs2007_status_is_busy(con
 gboolean sipe_ocs2007_availability_is_away(guint availability);
 
 /**
  * Publish status (OCS2007+)
  */
 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
 				   gpointer unused);
 void sipe_ocs2007_free(struct sipe_core_private *sipe_private);
-void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private);
+void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private,
+				   gboolean force_publish);
 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private);
 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private);
 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
 				       struct sipmsg *msg);
 
 /**
  * OCS2007 Access Levels
  */
--- a/libpurple/protocols/sipe/core/sipe-session.h
+++ b/libpurple/protocols/sipe/core/sipe-session.h
@@ -60,17 +60,16 @@ struct sip_session {
 	GSList *pending_invite_queue;
 
 	/*
 	 * Conference related fields
 	 */
 	gchar *im_mcu_uri;
 	gchar *subject;
 	gboolean locked;
-	guint request_id;
 	struct sip_dialog *focus_dialog;
 	/** Key is Message-Id */
 	GHashTable *conf_unconfirmed_messages;
 
 	/*
 	 * Media call related fields
 	 */
 	gboolean is_call;
--- a/libpurple/protocols/sipe/core/sipe-status.c
+++ b/libpurple/protocols/sipe/core/sipe-status.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-status.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-12 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -36,18 +36,16 @@
 #include "sipe-core-private.h"
 #include "sipe-nls.h"
 #include "sipe-ocs2005.h"
 #include "sipe-ocs2007.h"
 #include "sipe-schedule.h"
 #include "sipe-status.h"
 #include "sipe-utils.h"
 
-#define SIPE_IDLE_SET_DELAY 1 /* seconds */
-
 static struct
 {
 	const gchar *status_id;
 	const gchar *desc;
 } const sipe_activity_map[SIPE_ACTIVITY_NUM_TYPES] = {
 /*
  * This has nothing to do with Availability numbers, like 3500 (online).
  * Just a mapping of Communicator Activities to tokens/translations
@@ -144,130 +142,55 @@ void sipe_status_and_note(struct sipe_co
 
 	SIPE_DEBUG_INFO("sipe_status_and_note: switch to '%s' for the account", status_id);
 
 	activity = sipe_status_token_to_activity(status_id);
 	if (sipe_backend_status_changed(SIPE_CORE_PUBLIC,
 					activity,
 					sipe_private->note)) {
 		/* status has changed */
-		sipe_private->do_not_publish[activity] = time(NULL);
-		SIPE_DEBUG_INFO("sipe_status_and_note: do_not_publish[%s]=%d [now]",
-				status_id,
-				(int) sipe_private->do_not_publish[activity]);
+		SIPE_DEBUG_INFO_NOFORMAT("sipe_status_and_note: updating backend status");
 
 		/* update backend status */
 		sipe_backend_status_and_note(SIPE_CORE_PUBLIC,
 					     activity,
 					     sipe_private->note);
 	}
 }
 
-void sipe_status_update(struct sipe_core_private *sipe_private,
-			SIPE_UNUSED_PARAMETER gpointer unused)
-{
-	guint activity = sipe_backend_status(SIPE_CORE_PUBLIC);
-
-	if (activity == SIPE_ACTIVITY_UNSET) return;
-
-	SIPE_DEBUG_INFO("sipe_status_update: status: %s (%s)",
-			sipe_status_activity_to_token(activity),
-			sipe_status_changed_by_user(sipe_private) ? "USER" : "MACHINE");
-
-	sipe_cal_presence_publish(sipe_private, FALSE);
-}
-
 void sipe_core_status_set(struct sipe_core_public *sipe_public,
+			  gboolean set_by_user,
 			  guint activity,
 			  const gchar *note)
 {
 	struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
-	gchar *action_name;
 	gchar *tmp;
-	time_t now = time(NULL);
 	const gchar *status_id = sipe_status_activity_to_token(activity);
-	gboolean do_not_publish = ((now - sipe_private->do_not_publish[activity]) <= 2);
 
-	/* when other point of presence clears note, but we are keeping
-	 * state if OOF note.
-	 */
-	if (do_not_publish &&
-	    !note &&
-	    sipe_private->calendar &&
-	    sipe_private->calendar->oof_note) {
-		SIPE_DEBUG_INFO_NOFORMAT("sipe_core_status_set: enabling publication as OOF note keepers.");
-		do_not_publish = FALSE;
-	}
+	SIPE_DEBUG_INFO("sipe_core_status_set: status: %s (%s)",
+			status_id,
+			set_by_user ? "USER" : "MACHINE");
 
-	SIPE_DEBUG_INFO("sipe_core_status_set: was: sipe_private->do_not_publish[%s]=%d [?] now(time)=%d",
-			status_id, (int)sipe_private->do_not_publish[activity], (int)now);
-
-	sipe_private->do_not_publish[activity] = 0;
-	SIPE_DEBUG_INFO("sipe_core_status_set: set: sipe_private->do_not_publish[%s]=%d [0]",
-			status_id, (int)sipe_private->do_not_publish[activity]);
-
-	if (do_not_publish) {
-		SIPE_DEBUG_INFO_NOFORMAT("sipe_core_status_set: publication was switched off, exiting.");
-		return;
-	}
+	sipe_private->status_set_by_user = set_by_user;
 
 	sipe_status_set_token(sipe_private, status_id);
 
 	/* hack to escape apostrof before comparison */
 	tmp = note ? sipe_utils_str_replace(note, "'", "&apos;") : NULL;
 
 	/* this will preserve OOF flag as well */
 	if (!sipe_strequal(tmp, sipe_private->note)) {
 		SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
 		g_free(sipe_private->note);
 		sipe_private->note = g_strdup(note);
 		sipe_private->note_since = time(NULL);
 	}
 	g_free(tmp);
 
-	/* schedule 2 sec to capture idle flag */
-	action_name = g_strdup("<+set-status>");
-	sipe_schedule_seconds(sipe_private,
-			      action_name,
-			      NULL,
-			      SIPE_IDLE_SET_DELAY,
-			      sipe_status_update,
-			      NULL);
-	g_free(action_name);
-}
-
-/**
- * Whether user manually changed status or
- * it was changed automatically due to user
- * became inactive/active again
- */
-gboolean sipe_status_changed_by_user(struct sipe_core_private *sipe_private)
-{
-	gboolean res;
-	time_t now = time(NULL);
-
-	SIPE_DEBUG_INFO("sipe_status_changed_by_user: sipe_private->idle_switch : %s",
-			asctime(localtime(&(sipe_private->idle_switch))));
-	SIPE_DEBUG_INFO("sipe_status_changed_by_user: now              : %s",
-			asctime(localtime(&now)));
-
-	res = ((now - SIPE_IDLE_SET_DELAY * 2) >= sipe_private->idle_switch);
-
-	SIPE_DEBUG_INFO("sipe_status_changed_by_user: res  = %s",
-			res ? "USER" : "MACHINE");
-	return res;
-}
-
-void sipe_core_status_idle(struct sipe_core_public *sipe_public)
-{
-	struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
-
-	sipe_private->idle_switch = time(NULL);
-	SIPE_DEBUG_INFO("sipe_core_status_idle: sipe_private->idle_switch : %s",
-			asctime(localtime(&(sipe_private->idle_switch))));
+	sipe_cal_presence_publish(sipe_private, FALSE);
 }
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
   tab-width: 8
--- a/libpurple/protocols/sipe/core/sipe-status.h
+++ b/libpurple/protocols/sipe/core/sipe-status.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-status.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -33,19 +33,16 @@ const gchar *sipe_status_activity_to_tok
 guint sipe_status_token_to_activity(const gchar *token);
 
 void sipe_status_set_token(struct sipe_core_private *sipe_private,
 			   const gchar *status_id);
 void sipe_status_set_activity(struct sipe_core_private *sipe_private,
 			      guint activity);
 void sipe_status_and_note(struct sipe_core_private *sipe_private,
 			  const gchar *status_id);
-void sipe_status_update(struct sipe_core_private *sipe_private,
-			gpointer unused);
-gboolean sipe_status_changed_by_user(struct sipe_core_private *sipe_private);
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
   tab-width: 8
   End:
--- a/libpurple/protocols/sipe/core/sipe-svc.c
+++ b/libpurple/protocols/sipe/core/sipe-svc.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-svc.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2014 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -387,39 +387,35 @@ gboolean sipe_svc_get_and_publish_cert(s
 	return(ret);
 }
 
 gboolean sipe_svc_ab_entry_request(struct sipe_core_private *sipe_private,
 				   struct sipe_svc_session *session,
 				   const gchar *uri,
 				   const gchar *wsse_security,
 				   const gchar *search,
-				   guint entries,
 				   guint max_returns,
 				   sipe_svc_callback *callback,
 				   gpointer callback_data)
 {
 	gboolean ret;
 	gchar *soap_body = g_strdup_printf("<SearchAbEntry"
 					   " xmlns=\"DistributionListExpander\""
 					   " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
 					   " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\""
 					   ">"
 					   " <AbEntryRequest>"
-					   "  <ChangeSearch xmlns:q1=\"DistributionListExpander\" soapenc:arrayType=\"q1:AbEntryRequest.ChangeSearchQuery[%d]\">"
-					   "   %s"
-					   "  </ChangeSearch>"
+					   "  %s"
 					   "  <Metadata>"
 					   "   <FromDialPad>false</FromDialPad>"
 					   "   <MaxResultNum>%d</MaxResultNum>"
 					   "   <ReturnList>displayName,msRTCSIP-PrimaryUserAddress,title,telephoneNumber,homePhone,mobile,otherTelephone,mail,company,country,photoRelPath,photoSize,photoHash</ReturnList>"
 					   "  </Metadata>"
 					   " </AbEntryRequest>"
 					   "</SearchAbEntry>",
-					   entries,
 					   search,
 					   max_returns);
 
 	ret = new_soap_req(sipe_private,
 			   session,
 			   uri,
 			   "DistributionListExpander/IAddressBook/SearchAbEntry",
 			   wsse_security,
@@ -471,28 +467,27 @@ static gboolean request_passport(struct 
 
 	return(ret);
 }
 
 static gboolean request_user_password(struct sipe_core_private *sipe_private,
 				      struct sipe_svc_session *session,
 				      const gchar *service_uri,
 				      const gchar *auth_uri,
-				      const gchar *authuser,
 				      const gchar *content_type,
 				      const gchar *request_extension,
 				      sipe_svc_callback *callback,
 				      gpointer callback_data)
 {
 	/* Only cleartext passwords seem to be accepted... */
 	gchar *wsse_security = g_markup_printf_escaped("<wsse:UsernameToken>"
 						       " <wsse:Username>%s</wsse:Username>"
 						       " <wsse:Password>%s</wsse:Password>"
 						       "</wsse:UsernameToken>",
-						       authuser,
+						       sipe_private->authuser ? sipe_private->authuser : sipe_private->username,
 						       sipe_private->password ? sipe_private->password : "");
 
 	gboolean ret = request_passport(sipe_private,
 					session,
 					service_uri,
 					auth_uri,
 					wsse_security,
 					content_type,
@@ -509,17 +504,16 @@ gboolean sipe_svc_webticket_adfs(struct 
 				 const gchar *adfs_uri,
 				 sipe_svc_callback *callback,
 				 gpointer callback_data)
 {
 	return(request_user_password(sipe_private,
 				     session,
 				     "urn:federation:MicrosoftOnline",
 				     adfs_uri,
-				     sipe_private->username,
 				     /* ADFS is special, *sigh* */
 				     "application/soap+xml; charset=utf-8",
 				     "<wst:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</wst:KeyType>",
 				     callback,
 				     callback_data));
 }
 
 #define LMC_URI "https://login.microsoftonline.com:443/RST2.srf"
@@ -529,17 +523,16 @@ gboolean sipe_svc_webticket_lmc(struct s
 				const gchar *service_uri,
 				sipe_svc_callback *callback,
 				gpointer callback_data)
 {
 	return(request_user_password(sipe_private,
 				     session,
 				     service_uri,
 				     LMC_URI,
-				     sipe_private->authuser ? sipe_private->authuser : sipe_private->username,
 				     NULL,
 				     NULL,
 				     callback,
 				     callback_data));
 }
 
 gboolean sipe_svc_webticket_lmc_federated(struct sipe_core_private *sipe_private,
 					  struct sipe_svc_session *session,
@@ -623,18 +616,23 @@ static void sipe_svc_metadata_response(s
 	}
 }
 
 gboolean sipe_svc_realminfo(struct sipe_core_private *sipe_private,
 			    struct sipe_svc_session *session,
 			    sipe_svc_callback *callback,
 			    gpointer callback_data)
 {
+	/*
+	 * For some users RealmInfo response is different for authuser and
+	 * username. Use authuser, but only if it looks like "user@domain".
+	 */
 	gchar *realminfo_uri = g_strdup_printf("https://login.microsoftonline.com/getuserrealm.srf?login=%s&xml=1",
-					       sipe_private->username);
+					       sipe_private->authuser && strchr(sipe_private->authuser, '@') ?
+					       sipe_private->authuser : sipe_private->username);
 	gboolean ret = sipe_svc_https_request(sipe_private,
 					      session,
 					      realminfo_uri,
 					      NULL,
 					      NULL,
 					      NULL,
 					      sipe_svc_metadata_response,
 					      callback,
--- a/libpurple/protocols/sipe/core/sipe-svc.h
+++ b/libpurple/protocols/sipe/core/sipe-svc.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-svc.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-12 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2014 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -84,29 +84,27 @@ gboolean sipe_svc_get_and_publish_cert(s
 
 /**
  * Trigger [MS-DLX] address book entry search
  *
  * @param sipe_private  SIPE core private data
  * @param session       opaque session pointer
  * @param uri           service URI
  * @param wsse_security predefined authentication token
- * @param search        [MS-DLX] AbEntryRequest.ChangeSearchQuery in XML
- * @param entries       array entries in search XML string
+ * @param search        [MS-DLX] AbEntryRequest.ChangeSearchQuery/BasicSearch in XML
  * @param max_returns   how many entries to return
  * @param callback      callback function
  * @param callback_data callback data
  * @return              @c TRUE if search was triggered
  */
 gboolean sipe_svc_ab_entry_request(struct sipe_core_private *sipe_private,
 				   struct sipe_svc_session *session,
 				   const gchar *uri,
 				   const gchar *wsse_security,
 				   const gchar *search,
-				   guint entries,
 				   guint max_returns,
 				   sipe_svc_callback *callback,
 				   gpointer callback_data);
 
 /**
  * Trigger fetch of WebTicket security token
  *
  * @param sipe_private  SIPE core private data
--- a/libpurple/protocols/sipe/core/sipe-tls-tester.c
+++ b/libpurple/protocols/sipe/core/sipe-tls-tester.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-tls-tester.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-12 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -29,17 +29,19 @@
  *
  *    $ openssl req -new -keyout server.pem -out server.req
  *    $ openssl x509 -req -in server.req -signkey server.pem -out server.cert
  *
  * - Running the test server in one shell with same parameters used by Lync:
  *
  *    $ openssl s_server -accept 8443 -debug -msg \
  *              -cert server.cert -key server.pem \
- *              -tls1 -verify 0 -cipher RC4-SHA
+ *              -tls1 -verify 0 [ -cipher <c1>[:<c2>...] ]
+ *
+ *   ciphers: RC4-MD5, RC4-SHA, AES128-SHA, AES256-SHA
  *
  * - Running the test program in another shell:
  *
  *    $ sipe_tls_tester
  *
  *   You can add <host>[:<port>] to connect to a server on another machine
  */
 
@@ -173,25 +175,27 @@ static guchar *read_tls_record(int fd,
 
 	*in_length = length;
 	return(merged);
 }
 
 static void tls_handshake(struct sipe_tls_state *state,
 			  int fd)
 {
+	gboolean success = FALSE;
+
 	printf("TLS handshake starting...\n");
 
 	/* generate next handshake message */
 	while (sipe_tls_next(state)) {
 		int sent;
 
 		/* handshake completed? */
 		if (!state->out_buffer) {
-			printf("Handshake completed.\n");
+			success = TRUE;
 			break;
 		}
 
 		/* send buffer to server */
 		sent = write(fd, state->out_buffer, state->out_length);
 		if (sent < 0) {
 			printf("write to server failed: %s\n",
 			       strerror(errno));
@@ -208,17 +212,17 @@ static void tls_handshake(struct sipe_tl
 
 		state->in_buffer = read_tls_record(fd, &state->in_length);
 		if (!state->in_buffer) {
 			printf("end of data.\n");
 			break;
 		}
 	}
 
-	printf("TLS handshake done.\n");
+	printf("TLS handshake %s.\n", success ? "SUCCESSFUL" : "FAILED");
 }
 
 
 static int tls_connect(const gchar *param)
 {
 	gchar **parts = g_strsplit(param, ":", 2);
 	int fd = -1;
 
--- a/libpurple/protocols/sipe/core/sipe-tls.c
+++ b/libpurple/protocols/sipe/core/sipe-tls.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-tls.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-12 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -16,26 +16,27 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  *
- * TLS Protocol Version 1.0/1.1 - Handshake Messages
+ * TLS Protocol Version 1.0/1.1/1.2 - Handshake Messages
  *
  * TLS-DSK uses the handshake messages during authentication and session key
  * exchange. This module *ONLY* implements this part of the TLS specification!
  *
  * Specification references:
  *
  *   - RFC2246: http://www.ietf.org/rfc/rfc2246.txt
  *   - RFC3546: http://www.ietf.org/rfc/rfc3546.txt
  *   - RFC4346: http://www.ietf.org/rfc/rfc4346.txt
+ *   - RFC5246: http://www.ietf.org/rfc/rfc5246.txt
  */
 
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
 
 #include <glib.h>
 
@@ -76,40 +77,49 @@ struct tls_internal_state {
 	gsize key_length;
 	guchar *master_secret;
 	guchar *key_block;
 	guchar *tls_dsk_key_block;
 	const guchar *client_write_mac_secret;
 	const guchar *server_write_mac_secret;
 	const guchar *client_write_secret;
 	const guchar *server_write_secret;
+	const guchar *client_write_iv;
+	const guchar *server_write_iv;
 	void (*mac_func)(const guchar *key, gsize key_length,
 			 const guchar *data, gsize data_length,
 			 guchar *digest);
 	gpointer cipher_context;
 	guint64 sequence_number;
+	gboolean stream_cipher;
 	gboolean encrypted;
+	gboolean expected;
 };
 
 /*
  * TLS messages & layout descriptors
  */
 
 /* constants */
 #define TLS_VECTOR_MAX8       255 /* 2^8  - 1 */
 #define TLS_VECTOR_MAX16    65535 /* 2^16 - 1 */
 #define TLS_VECTOR_MAX24 16777215 /* 2^24 - 1 */
 
 #define TLS_PROTOCOL_VERSION_1_0 0x0301
 #define TLS_PROTOCOL_VERSION_1_1 0x0302
+#define TLS_PROTOCOL_VERSION_1_2 0x0303
 
 /* CipherSuites */
 #define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003
 #define TLS_RSA_WITH_RC4_128_MD5       0x0004
 #define TLS_RSA_WITH_RC4_128_SHA       0x0005
+#define TLS_RSA_WITH_AES_128_CBC_SHA   0x002F
+#define TLS_RSA_WITH_AES_256_CBC_SHA   0x0035
+
+#define TLS_AES_CBC_BLOCK_LENGTH 16 /* bytes */
 
 /* CompressionMethods */
 #define TLS_COMP_METHOD_NULL 0
 
 /* various array lengths */
 #define TLS_ARRAY_RANDOM_LENGTH        32
 #define TLS_ARRAY_MASTER_SECRET_LENGTH 48
 #define TLS_ARRAY_VERIFY_LENGTH        12
@@ -198,17 +208,17 @@ struct tls_compile_vector {
 };
 
 struct tls_compile_sessionid {
 	gsize elements; /* VECTOR */
 };
 
 struct tls_compile_cipher {
 	gsize elements; /* VECTOR */
-	guint suites[3];
+	guint suites[5];
 };
 
 struct tls_compile_compression {
 	gsize elements; /* VECTOR */
 	guint methods[1];
 };
 
 /* compiled message */
@@ -271,16 +281,19 @@ static void debug_hex(struct tls_interna
 	g_string_append(str, "\n");
 }
 
 #define debug_print(state, string) \
 	if (state->debug) g_string_append(state->debug, string)
 #define debug_printf(state, format, ...) \
 	if (state->debug) g_string_append_printf(state->debug, format, __VA_ARGS__)
 
+/* Analyzer only needs the debugging functions */
+#ifndef _SIPE_COMPILING_ANALYZER
+
 static void debug_secrets(struct tls_internal_state *state,
 			  const gchar *label,
 			  const guchar *secret,
 			  gsize secret_length)
 {
 	if (state->debug && secret) {
 		g_string_append_printf(state->debug, "%s (%3" G_GSIZE_FORMAT ") = ",
 				       label, secret_length);
@@ -466,16 +479,18 @@ static guchar *sipe_tls_prf(SIPE_UNUSED_
 #ifdef __SIPE_TLS_CRYPTO_DEBUG
 	debug_secrets(state, "sipe_tls_prf: PRF()                     ",
 		      md5,  output_length);
 #endif
 
 	return(md5);
 }
 
+#endif /* !_SIPE_COMPILING_ANALYZER */
+
 /*
  * TLS data parsers
  *
  * Low-level data conversion routines
  *
  *  - host alignment agnostic, i.e. can fetch a word from uneven address
  *  - TLS -> host endianess conversion
  *  - no length check, caller has to do it
@@ -534,16 +549,20 @@ static gboolean parse_integer(struct tls
 
 static gboolean parse_array(struct tls_internal_state *state,
 			    const struct layout_descriptor *desc)
 {
 	if (!msg_remainder_check(state, desc->label, desc->max))
 		return(FALSE);
 	debug_printf(state, "%s/ARRAY[%" G_GSIZE_FORMAT "]\n",
 		     desc->label, desc->max);
+#ifdef _SIPE_COMPILING_ANALYZER
+	if (desc->max)
+		debug_hex(state, desc->max);
+#endif
 	if (state->data) {
 		struct tls_parsed_array *save = g_malloc0(sizeof(struct tls_parsed_array) +
 							  desc->max);
 		save->length = desc->max;
 		memcpy((guchar *)save->data, state->msg_current, desc->max);
 		g_hash_table_insert(state->data, (gpointer) desc->label, save);
 
 	}
@@ -562,16 +581,20 @@ static gboolean parse_vector(struct tls_
 				 &length))
 		return(FALSE);
 	if (length < desc->min) {
 		SIPE_DEBUG_ERROR("parse_vector: '%s' too short %d, expected %" G_GSIZE_FORMAT,
 				 desc->label, length, desc->min);
 		return(FALSE);
 	}
 	debug_printf(state, "%s/VECTOR<%d>\n", desc->label, length);
+#ifdef _SIPE_COMPILING_ANALYZER
+	if (length)
+		debug_hex(state, length);
+#endif
 	if (state->data) {
 		struct tls_parsed_array *save = g_malloc0(sizeof(struct tls_parsed_array) +
 							  length);
 		save->length = length;
 		memcpy((guchar *)save->data, state->msg_current, length);
 		g_hash_table_insert(state->data, (gpointer) desc->label, save);
 	}
 	state->msg_current   += length;
@@ -768,17 +791,18 @@ static const struct msg_descriptor Finis
 	&CertificateVerify_m, "Finished", Finished_l, TLS_HANDSHAKE_TYPE_FINISHED
 };
 
 #define HANDSHAKE_MSG_DESCRIPTORS &Finished_m
 
 /*
  * TLS message parsers
  */
-static gboolean handshake_parse(struct tls_internal_state *state)
+static gboolean handshake_parse(struct tls_internal_state *state,
+				guint expected_type)
 {
 	const guchar *bytes = state->msg_current;
 	gsize length        = state->msg_remainder;
 	gboolean success    = FALSE;
 
 	while (length > 0) {
 		const struct msg_descriptor *desc;
 		gsize msg_length;
@@ -804,23 +828,27 @@ static gboolean handshake_parse(struct t
 		     desc;
 		     desc = desc->next)
 			if (msg_type == desc->type)
 				break;
 
 		debug_printf(state, "TLS handshake (%" G_GSIZE_FORMAT " bytes) (%d)",
 			     msg_length, msg_type);
 
+		if (msg_type == expected_type)
+			state->expected = TRUE;
+
 		state->msg_current   = (guchar *) bytes + TLS_HANDSHAKE_HEADER_LENGTH;
 		state->msg_remainder = msg_length;
 
 		if (desc && desc->layouts) {
 			const struct layout_descriptor *ldesc = desc->layouts;
 
 			debug_printf(state, "%s\n", desc->description);
+
 			while (TLS_LAYOUT_IS_VALID(ldesc)) {
 				success = ldesc->parser(state, ldesc);
 				if (!success)
 					break;
 				ldesc++;
 			}
 			if (!success)
 				break;
@@ -846,32 +874,42 @@ static void free_parse_data(struct tls_i
 {
 	if (state->data) {
 		g_hash_table_destroy(state->data);
 		state->data = NULL;
 	}
 }
 
 static gboolean tls_record_parse(struct tls_internal_state *state,
-				 gboolean incoming)
+				 gboolean incoming,
+				 guint expected)
 {
 	const guchar *bytes  = incoming ? state->common.in_buffer : state->common.out_buffer;
 	gsize length         = incoming ? state->common.in_length : state->common.out_length;
 	guint version;
 	const gchar *version_str;
 	gsize record_length;
 	gboolean success = TRUE;
 
+	/* reject empty incoming messages */
+	if (incoming && (length == 0)) {
+		SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: empty TLS message received");
+		return(FALSE);
+	}
+
+#ifndef _SIPE_COMPILING_ANALYZER
 	debug_printf(state, "TLS MESSAGE %s\n", incoming ? "INCOMING" : "OUTGOING");
+#endif
 
 	/* Collect parser data for incoming messages */
 	if (incoming)
 		state->data = g_hash_table_new_full(g_str_hash, g_str_equal,
 						    NULL, g_free);
 
+	state->expected = FALSE;
 	while (success && (length > 0)) {
 
 		/* truncated header check */
 		if (length < TLS_RECORD_HEADER_LENGTH) {
 			SIPE_DEBUG_ERROR("tls_record_parse: too short TLS record header (%" G_GSIZE_FORMAT " bytes)",
 					 length);
 			success = FALSE;
 			break;
@@ -886,16 +924,19 @@ static gboolean tls_record_parse(struct 
 		}
 		switch (version) {
 		case TLS_PROTOCOL_VERSION_1_0:
 			version_str = "1.0 (RFC2246)";
 			break;
 		case TLS_PROTOCOL_VERSION_1_1:
 			version_str = "1.1 (RFC4346)";
 			break;
+		case TLS_PROTOCOL_VERSION_1_2:
+			version_str = "1.2 (RFC5246)";
+			break;
 		default:
 			version_str = "<future protocol version>";
 			break;
 		}
 
 		/* record length check */
 		record_length = TLS_RECORD_HEADER_LENGTH +
 			lowlevel_integer_to_host(bytes + TLS_RECORD_OFFSET_LENGTH, 2);
@@ -906,63 +947,80 @@ static gboolean tls_record_parse(struct 
 		}
 
 		/* TLS record header OK */
 		debug_printf(state, "TLS %s record (%" G_GSIZE_FORMAT " bytes)\n",
 			     version_str, record_length);
 		state->msg_current   = (guchar *) bytes + TLS_RECORD_HEADER_LENGTH;
 		state->msg_remainder = record_length - TLS_RECORD_HEADER_LENGTH;
 
+/* Analyzer only needs the debugging functions */
+#ifndef _SIPE_COMPILING_ANALYZER
 		/* Add incoming message contents to digest contexts */
 		if (incoming) {
 			sipe_digest_md5_update(state->md5_context,
 					       state->msg_current,
 					       state->msg_remainder);
 			sipe_digest_sha1_update(state->sha1_context,
 						state->msg_current,
 						state->msg_remainder);
 		}
+#endif /* !_SIPE_COMPILING_ANALYZER */
 
 		switch (bytes[TLS_RECORD_OFFSET_TYPE]) {
 		case TLS_RECORD_TYPE_CHANGE_CIPHER_SPEC:
 			debug_print(state, "Change Cipher Spec\n");
-			if (incoming) state->encrypted = TRUE;
+			if (incoming)
+				state->encrypted = TRUE;
+			if (expected == TLS_RECORD_TYPE_CHANGE_CIPHER_SPEC)
+				state->expected = TRUE;
 			break;
 
 		case TLS_RECORD_TYPE_HANDSHAKE:
 			if (incoming && state->encrypted) {
 				debug_print(state, "Encrypted handshake message\n");
 				debug_hex(state, 0);
 			} else {
-				success = handshake_parse(state);
+				success = handshake_parse(state, expected);
 			}
 			break;
 
 		default:
 			debug_print(state, "Unsupported TLS message\n");
 			debug_hex(state, 0);
 			break;
 		}
 
 		/* next fragment */
 		bytes  += record_length;
 		length -= record_length;
 	}
 
+#ifndef _SIPE_COMPILING_ANALYZER
+	if (incoming && !state->expected) {
+		SIPE_DEBUG_ERROR("tls_record_parse: did not find expected msg type %d",
+				 expected);
+		success = FALSE;
+	}
+#endif
+
 	if (!success)
 		free_parse_data(state);
 
 	if (state->debug) {
 		SIPE_DEBUG_INFO_NOFORMAT(state->debug->str);
 		g_string_truncate(state->debug, 0);
 	}
 
 	return(success);
 }
 
+/* Analyzer only needs the debugging functions */
+#ifndef _SIPE_COMPILING_ANALYZER
+
 /*
  * TLS message compiler
  */
 static void compile_tls_record(struct tls_internal_state *state,
 			       ...)
 {
 	gsize total_size = 0;
 	guchar *current;
@@ -1002,41 +1060,50 @@ static void compile_tls_record(struct tl
 	}
 	va_end(ap);
 }
 
 static void compile_encrypted_tls_record(struct tls_internal_state *state,
 					 const struct tls_compiled_message *msg)
 {
 	guchar *plaintext;
-	gsize plaintext_length;
+	gsize plaintext_length; /* header + content        */
 	guchar *mac;
 	gsize mac_length;
 	guchar *message;
 	guchar *encrypted;
-	gsize encrypted_length;
+	gsize message_length;   /* header + content + MAC  */
+	gsize padding_length;   /* for block cipher        */
+	gsize encrypted_length; /* header + encrypted data */
 
 	/* Create plaintext TLS record */
 	compile_tls_record(state, msg, NULL);
 	plaintext        = state->common.out_buffer;
 	plaintext_length = state->common.out_length;
 	if (plaintext_length == 0) /* make Coverity happy */
 		return;
 
 	/* Prepare encryption buffer */
-	encrypted_length = plaintext_length + state->mac_length;
+	message_length = plaintext_length + state->mac_length;
+	if (state->stream_cipher) {
+		padding_length   = 0;
+		encrypted_length = message_length;
+	} else {
+		padding_length   = TLS_AES_CBC_BLOCK_LENGTH - (message_length - TLS_RECORD_HEADER_LENGTH + 1) % TLS_AES_CBC_BLOCK_LENGTH;
+		encrypted_length = message_length + padding_length + 1;
+	}
 	SIPE_DEBUG_INFO("compile_encrypted_tls_record: total size %" G_GSIZE_FORMAT,
 			encrypted_length - TLS_RECORD_HEADER_LENGTH);
-	message          = g_malloc(encrypted_length);
+	message = g_malloc(message_length);
 	memcpy(message, plaintext, plaintext_length);
 	lowlevel_integer_to_tls(message + TLS_RECORD_OFFSET_LENGTH, 2,
 				encrypted_length - TLS_RECORD_HEADER_LENGTH);
 
 	/*
-	 * Calculate MAC
+	 * Calculate MAC and append to message
 	 *
 	 * HMAC_hash(client_write_mac_secret,
 	 *           sequence_number + type + version + length + fragment)
 	 *                             \---  == original TLS record  ---/
 	 */
 	mac_length = sizeof(guint64) + plaintext_length;
 	mac        = g_malloc(mac_length);
 	lowlevel_integer_to_tls(mac,
@@ -1046,23 +1113,46 @@ static void compile_encrypted_tls_record
 	g_free(plaintext);
 	state->mac_func(state->client_write_mac_secret,
 			state->mac_length,
 			mac,
 			mac_length,
 			message + plaintext_length);
 	g_free(mac);
 
-	/* Encrypt message + MAC */
 	encrypted = g_malloc(encrypted_length);
+	/* header (unencrypted) */
 	memcpy(encrypted, message, TLS_RECORD_HEADER_LENGTH);
-	sipe_crypt_tls_stream(state->cipher_context,
-			      message + TLS_RECORD_HEADER_LENGTH,
-			      encrypted_length - TLS_RECORD_HEADER_LENGTH,
-			      encrypted + TLS_RECORD_HEADER_LENGTH);
+	if (state->stream_cipher) {
+		/* ENCRYPT(content + MAC) */
+		sipe_crypt_tls_stream(state->cipher_context,
+				      message + TLS_RECORD_HEADER_LENGTH,
+				      encrypted_length - TLS_RECORD_HEADER_LENGTH,
+				      encrypted + TLS_RECORD_HEADER_LENGTH);
+	} else {
+		/* TLS 1.0 GenericBlockCipher */
+		/* content + MAC */
+		memcpy(encrypted + TLS_RECORD_HEADER_LENGTH,
+		       message + TLS_RECORD_HEADER_LENGTH,
+		       message_length - TLS_RECORD_HEADER_LENGTH);
+
+		/* padding + padding_length */
+		memset(encrypted + message_length,
+		       padding_length,
+		       padding_length + 1);
+
+		/* ENCRYPT(content + MAC + padding + padding_length) */
+		sipe_crypt_tls_block(state->client_write_secret,
+				     state->key_length,
+				     state->client_write_iv,
+				     TLS_AES_CBC_BLOCK_LENGTH,
+				     encrypted + TLS_RECORD_HEADER_LENGTH,
+				     encrypted_length - TLS_RECORD_HEADER_LENGTH,
+				     encrypted + TLS_RECORD_HEADER_LENGTH);
+	}
 	g_free(message);
 
 	/* swap buffers */
 	state->common.out_buffer = encrypted;
 	state->common.out_length = encrypted_length;
 }
 
 static struct tls_compiled_message *compile_handshake_msg(struct tls_internal_state *state,
@@ -1149,64 +1239,93 @@ static struct tls_compiled_message *tls_
 
 	return(msg);
 }
 
 static gboolean check_cipher_suite(struct tls_internal_state *state)
 {
 	struct tls_parsed_integer *cipher_suite = g_hash_table_lookup(state->data,
 								      "CipherSuite");
-	const gchar *label = NULL;
+	const gchar *label_mac    = NULL;
+	const gchar *label_cipher = NULL;
 
 	if (!cipher_suite) {
 		SIPE_DEBUG_ERROR_NOFORMAT("check_cipher_suite: server didn't specify the cipher suite");
 		return(FALSE);
 	}
 
 	switch (cipher_suite->value) {
 	case TLS_RSA_EXPORT_WITH_RC4_40_MD5:
-		state->mac_length = SIPE_DIGEST_HMAC_MD5_LENGTH;
-		state->key_length = 40 / 8;
-		state->mac_func   = sipe_digest_hmac_md5;
-		label             = "MD5";
+		state->mac_length       = SIPE_DIGEST_HMAC_MD5_LENGTH;
+		state->key_length       = 40 / 8;
+		state->mac_func         = sipe_digest_hmac_md5;
+		state->stream_cipher    = TRUE;
+		label_mac               = "MD5";
+		label_cipher            = "RC4 stream";
 		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_MD5;
 		break;
 
 	case TLS_RSA_WITH_RC4_128_MD5:
-		state->mac_length = SIPE_DIGEST_HMAC_MD5_LENGTH;
-		state->key_length = 128 / 8;
-		state->mac_func   = sipe_digest_hmac_md5;
-		label             = "MD5";
+		state->mac_length       = SIPE_DIGEST_HMAC_MD5_LENGTH;
+		state->key_length       = 128 / 8;
+		state->mac_func         = sipe_digest_hmac_md5;
+		state->stream_cipher    = TRUE;
+		label_mac               = "MD5";
+		label_cipher            = "RC4 stream";
 		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_MD5;
 		break;
 
 	case TLS_RSA_WITH_RC4_128_SHA:
-		state->mac_length = SIPE_DIGEST_HMAC_SHA1_LENGTH;
-		state->key_length = 128 / 8;
-		state->mac_func   = sipe_digest_hmac_sha1;
-		label             = "SHA-1";
+		state->mac_length       = SIPE_DIGEST_HMAC_SHA1_LENGTH;
+		state->key_length       = 128 / 8;
+		state->mac_func         = sipe_digest_hmac_sha1;
+		state->stream_cipher    = TRUE;
+		label_mac               = "SHA-1";
+		label_cipher            = "RC4 stream";
+		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_SHA1;
+		break;
+
+	case TLS_RSA_WITH_AES_128_CBC_SHA:
+		state->mac_length       = SIPE_DIGEST_HMAC_SHA1_LENGTH;
+		state->key_length       = 128 / 8;
+		state->mac_func         = sipe_digest_hmac_sha1;
+		state->stream_cipher    = FALSE;
+		label_mac               = "SHA-1";
+		label_cipher            = "AES-CBC block";
+		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_SHA1;
+		break;
+
+	case TLS_RSA_WITH_AES_256_CBC_SHA:
+		state->mac_length       = SIPE_DIGEST_HMAC_SHA1_LENGTH;
+		state->key_length       = 256 / 8;
+		state->mac_func         = sipe_digest_hmac_sha1;
+		state->stream_cipher    = FALSE;
+		label_mac               = "SHA-1";
+		label_cipher            = "AES-CBC block";
 		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_SHA1;
 		break;
 
 	default:
 		SIPE_DEBUG_ERROR("check_cipher_suite: unsupported cipher suite %d",
 				 cipher_suite->value);
 		break;
 	}
 
-	if (label)
-		SIPE_DEBUG_INFO("check_cipher_suite: KEY(stream cipher RC4) %" G_GSIZE_FORMAT ", MAC(%s) %" G_GSIZE_FORMAT,
-				state->key_length, label, state->mac_length);
+	if (label_cipher && label_mac)
+		SIPE_DEBUG_INFO("check_cipher_suite: KEY(%s cipher) %" G_GSIZE_FORMAT ", MAC(%s) %" G_GSIZE_FORMAT,
+				label_cipher, state->key_length,
+				label_mac, state->mac_length);
 
-	return(label != NULL);
+	return(label_cipher && label_mac);
 }
 
 static void tls_calculate_secrets(struct tls_internal_state *state)
 {
-	gsize length = 2 * (state->mac_length + state->key_length);
+	gsize length = 2 * (state->mac_length + state->key_length +
+			    (state->stream_cipher ? 0 : TLS_AES_CBC_BLOCK_LENGTH));
 	guchar *random;
 
 	/* Generate pre-master secret */
 	sipe_tls_fill_random(&state->pre_master_secret,
 			     TLS_ARRAY_MASTER_SECRET_LENGTH * 8); /* bits */
 	lowlevel_integer_to_tls(state->pre_master_secret.buffer, 2,
 				TLS_PROTOCOL_VERSION_1_0);
 	debug_secrets(state, "tls_calculate_secrets: pre-master secret",
@@ -1267,19 +1386,24 @@ static void tls_calculate_secrets(struct
 		      state->key_block, length);
 
 	/* partition key block */
 	state->client_write_mac_secret = state->key_block;
 	state->server_write_mac_secret = state->key_block + state->mac_length;
 	state->client_write_secret     = state->key_block + 2 * state->mac_length;
 	state->server_write_secret     = state->key_block + 2 * state->mac_length + state->key_length;
 
-	/* initialize cipher context */
-	state->cipher_context = sipe_crypt_tls_start(state->client_write_secret,
-						     state->key_length);
+	/* initialize stream cipher context */
+	if (state->stream_cipher) {
+		state->cipher_context = sipe_crypt_tls_start(state->client_write_secret,
+							     state->key_length);
+	} else {
+		state->client_write_iv = state->key_block + 2 * (state->mac_length + state->key_length);
+		state->server_write_iv = state->key_block + 2 * (state->mac_length + state->key_length) + TLS_AES_CBC_BLOCK_LENGTH;
+	}
 }
 
 #if 0 /* NOT NEEDED? */
 /* signing */
 static guchar *tls_pkcs1_private_padding(SIPE_UNUSED_PARAMETER struct tls_internal_state *state,
 					 const guchar *data,
 					 gsize data_length,
 					 gsize buffer_length)
@@ -1516,20 +1640,22 @@ static struct tls_compiled_message *tls_
 static gboolean tls_client_hello(struct tls_internal_state *state)
 {
 	guint32 now   = time(NULL);
 	guint32 now_N = GUINT32_TO_BE(now);
 	struct ClientHello_host msg = {
 		{ TLS_PROTOCOL_VERSION_1_0 },
 		{ 0, { 0 } },
 		{ 0 /* empty SessionID */ },
-		{ 3,
+		{ 5,
 		  {
 			  TLS_RSA_WITH_RC4_128_MD5,
 			  TLS_RSA_WITH_RC4_128_SHA,
+			  TLS_RSA_WITH_AES_128_CBC_SHA,
+			  TLS_RSA_WITH_AES_256_CBC_SHA,
 			  TLS_RSA_EXPORT_WITH_RC4_40_MD5
 		  }
 		},
 		{ 1,
 		  {
 			  TLS_COMP_METHOD_NULL
 		  }
 		}
@@ -1546,39 +1672,39 @@ static gboolean tls_client_hello(struct 
 	cmsg = compile_handshake_msg(state, &ClientHello_m, &msg, sizeof(msg));
         compile_tls_record(state, cmsg, NULL);
 	g_free(cmsg);
 
 	if (sipe_backend_debug_enabled())
 		state->debug = g_string_new("");
 
 	state->state = TLS_HANDSHAKE_STATE_SERVER_HELLO;
-	return(tls_record_parse(state, FALSE));
+	return(tls_record_parse(state, FALSE, 0));
 }
 
 static gboolean tls_server_hello(struct tls_internal_state *state)
 {
 	struct tls_compiled_message *certificate = NULL;
 	struct tls_compiled_message *exchange    = NULL;
 	struct tls_compiled_message *verify      = NULL;
 	struct tls_compiled_message *finished    = NULL;
 	gboolean success = FALSE;
 
-	if (!tls_record_parse(state, TRUE))
+	if (!tls_record_parse(state, TRUE, TLS_HANDSHAKE_TYPE_SERVER_HELLO))
 		return(FALSE);
 
 	if (((certificate = tls_client_certificate(state))  != NULL) &&
 	    ((exchange    = tls_client_key_exchange(state)) != NULL) &&
 	    ((verify      = tls_certificate_verify(state))  != NULL) &&
 	    ((finished    = tls_client_finished(state))     != NULL)) {
 
 		/* Part 1 */
 		compile_tls_record(state, certificate, exchange, verify, NULL);
 
-		success = tls_record_parse(state, FALSE);
+		success = tls_record_parse(state, FALSE,  0);
 		if (success) {
 			guchar *part1      = state->common.out_buffer;
 			gsize part1_length = state->common.out_length;
 			guchar *part3;
 			gsize part3_length;
 			guchar *merged;
 			gsize length;
 			/* ChangeCipherSpec is always the same */
@@ -1623,17 +1749,17 @@ static gboolean tls_server_hello(struct 
 
 	return(success);
 }
 
 static gboolean tls_finished(struct tls_internal_state *state)
 {
 	guchar *random;
 
-	if (!tls_record_parse(state, TRUE))
+	if (!tls_record_parse(state, TRUE, TLS_RECORD_TYPE_CHANGE_CIPHER_SPEC))
 		return(FALSE);
 
 	/* we don't need the data */
 	free_parse_data(state);
 
 	/*
 	 * Calculate session keys [MS-SIPAE section 3.2.5.1]
 	 *
@@ -1777,16 +1903,18 @@ void sipe_tls_free(struct sipe_tls_state
 		if (internal->sha1_context)
 			sipe_digest_sha1_destroy(internal->sha1_context);
 		sipe_cert_crypto_destroy(internal->server_certificate);
 		g_free(state->out_buffer);
 		g_free(state);
 	}
 }
 
+#endif /* !_SIPE_COMPILING_ANALYZER */
+
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
   tab-width: 8
   End:
 */
--- a/libpurple/protocols/sipe/core/sipe-ucs.c
+++ b/libpurple/protocols/sipe/core/sipe-ucs.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-ucs.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -18,32 +18,41 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  *
  * Implementation for Unified Contact Store [MS-OXWSCOS]
  *  <http://msdn.microsoft.com/en-us/library/jj194130.aspx>
+ * EWS Reference
+ *  <http://msdn.microsoft.com/en-us/library/office/bb204119.aspx>
+ * Photo Web Service Protocol [MS-OXWSPHOTO]
+ *  <http://msdn.microsoft.com/en-us/library/jj194353.aspx>
+ * GetUserPhoto operation
+ *  <http://msdn.microsoft.com/en-us/library/office/jj900502.aspx>
+ * FindPeople operation
+ *  <http://msdn.microsoft.com/en-us/library/office/jj191039.aspx>
  */
 
 #include <string.h>
 
 #include <glib.h>
 #include <time.h>
 
 #include "sipe-backend.h"
 #include "sipe-buddy.h"
 #include "sipe-common.h"
 #include "sipe-core.h"
 #include "sipe-core-private.h"
 #include "sipe-digest.h"
 #include "sipe-ews-autodiscover.h"
 #include "sipe-group.h"
 #include "sipe-http.h"
+#include "sipe-nls.h"
 #include "sipe-subscriptions.h"
 #include "sipe-ucs.h"
 #include "sipe-utils.h"
 #include "sipe-xml.h"
 
 struct sipe_ucs_transaction {
 	GSList *pending_requests;
 };
@@ -288,16 +297,170 @@ void sipe_ucs_get_photo(struct sipe_core
 	if (!sipe_ucs_http_request(sipe_private,
 				   NULL,
 				   body,
 				   sipe_ucs_get_user_photo_response,
 				   payload))
 		g_free(payload);
 }
 
+static void sipe_ucs_search_response(struct sipe_core_private *sipe_private,
+				     SIPE_UNUSED_PARAMETER struct sipe_ucs_transaction *trans,
+				     const sipe_xml *body,
+				     gpointer callback_data)
+{
+	const sipe_xml *persona_node;
+	struct sipe_backend_search_results *results = NULL;
+	guint match_count = 0;
+
+	for (persona_node = sipe_xml_child(body,
+					   "FindPeopleResponse/People/Persona");
+	     persona_node;
+	     persona_node = sipe_xml_twin(persona_node)) {
+		const sipe_xml *address = sipe_xml_child(persona_node,
+							 "ImAddress");
+
+		/* only display Persona nodes which have an "ImAddress" node */
+		if (address) {
+			gchar *uri;
+			gchar *displayname;
+			gchar *company;
+			gchar *email;
+
+			/* OK, we found something - show the results to the user */
+			match_count++;
+			if (!results) {
+				results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC,
+									    callback_data);
+				if (!results) {
+					SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_search_response: Unable to display the search results.");
+					sipe_backend_search_failed(SIPE_CORE_PUBLIC,
+								   callback_data,
+								   _("Unable to display the search results"));
+					return;
+				}
+			}
+
+			uri         = sipe_xml_data(address);
+			displayname = sipe_xml_data(sipe_xml_child(persona_node,
+								   "DisplayName"));
+			company     = sipe_xml_data(sipe_xml_child(persona_node,
+								   "CompanyName"));
+			email       = sipe_xml_data(sipe_xml_child(persona_node,
+								   "EmailAddress/EmailAddress"));
+
+			sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
+							results,
+							sipe_get_no_sip_uri(uri),
+							displayname,
+							company,
+							NULL,
+							email);
+
+			g_free(email);
+			g_free(company);
+			g_free(displayname);
+			g_free(uri);
+		}
+	}
+
+	if (match_count > 0)
+		sipe_buddy_search_contacts_finalize(sipe_private,
+						    results,
+						    match_count,
+						    FALSE);
+	else
+		sipe_backend_search_failed(SIPE_CORE_PUBLIC,
+					   callback_data,
+					   _("No contacts found"));
+}
+
+void sipe_ucs_search(struct sipe_core_private *sipe_private,
+		     struct sipe_backend_search_token *token,
+		     const gchar *given_name,
+		     const gchar *surname,
+		     const gchar *email,
+		     const gchar *sipid,
+		     const gchar *company,
+		     const gchar *country)
+{
+	guint count    = 0;
+	GString *query = g_string_new(NULL);
+
+	/*
+	 * Search GAL for matching entries
+	 *
+	 * QueryString should support field properties and quoting ("")
+	 * according to the specification. But in my trials I couldn't get
+	 * them to work. Concatenate all query words to a single string.
+	 * Only items that match ALL words will be returned by this query.
+	 */
+#define ADD_QUERY_VALUE(val)			       \
+	if (val) {				       \
+		if (count++)			       \
+			g_string_append_c(query, ' '); \
+		g_string_append(query, val);	       \
+	}
+
+	ADD_QUERY_VALUE(given_name);
+	ADD_QUERY_VALUE(surname);
+	ADD_QUERY_VALUE(email);
+	ADD_QUERY_VALUE(sipid);
+	ADD_QUERY_VALUE(company);
+	ADD_QUERY_VALUE(country);
+
+	if (count > 0) {
+		gchar *body = g_markup_printf_escaped("<m:FindPeople>"
+						      " <m:PersonaShape>"
+						      "  <t:BaseShape>IdOnly</t:BaseShape>"
+						      "  <t:AdditionalProperties>"
+						      "   <t:FieldURI FieldURI=\"persona:CompanyName\"/>"
+						      "   <t:FieldURI FieldURI=\"persona:DisplayName\"/>"
+						      "   <t:FieldURI FieldURI=\"persona:EmailAddress\"/>"
+						      "   <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
+						      /* Locations doesn't seem to work
+						      "   <t:FieldURI FieldURI=\"persona:Locations\"/>"
+						      */
+						      "  </t:AdditionalProperties>"
+						      " </m:PersonaShape>"
+						      " <m:IndexedPageItemView BasePoint=\"Beginning\" MaxEntriesReturned=\"100\" Offset=\"0\"/>"
+						      /*
+						       * I have no idea why Exchnage doesn't accept this
+						       * FieldURI for restrictions. Without it the search
+						       * will return users that don't have an ImAddress
+						       * and we need to filter them out ourselves :-(
+						      " <m:Restriction>"
+						      "  <t:Exists>"
+						      "   <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
+						      "  </t:Exists>"
+						      " </m:Restriction>"
+						      */
+						      " <m:ParentFolderId>"
+						      "  <t:DistinguishedFolderId Id=\"directory\"/>"
+						      " </m:ParentFolderId>"
+						      " <m:QueryString>%s</m:QueryString>"
+						      "</m:FindPeople>",
+						      query->str);
+
+		if (!sipe_ucs_http_request(sipe_private,
+					   NULL,
+					   body,
+					   sipe_ucs_search_response,
+					   token))
+			sipe_backend_search_failed(SIPE_CORE_PUBLIC,
+						   token,
+						   _("Contact search failed"));
+	} else
+		sipe_backend_search_failed(SIPE_CORE_PUBLIC,
+					   token,
+					   _("Invalid contact search query"));
+
+	g_string_free(query, TRUE);
+}
+
 static void sipe_ucs_ignore_response(struct sipe_core_private *sipe_private,
 				     SIPE_UNUSED_PARAMETER struct sipe_ucs_transaction *trans,
 				     SIPE_UNUSED_PARAMETER const sipe_xml *body,
 				     SIPE_UNUSED_PARAMETER gpointer callback_data)
 {
 	SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_ignore_response: done");
 	sipe_private->ucs->last_response = time(NULL);
 }
@@ -550,16 +713,34 @@ void sipe_ucs_group_remove(struct sipe_c
 
 	sipe_ucs_http_request(sipe_private,
 			      NULL,
 			      body,
 			      sipe_ucs_ignore_response,
 			      NULL);
 }
 
+static void ucs_init_failure(struct sipe_core_private *sipe_private)
+{
+	/* Did the user specify any email settings? */
+	gboolean default_settings =
+		is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC,
+					      SIPE_SETTING_EMAIL_URL))   &&
+		is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC,
+					      SIPE_SETTING_EMAIL_LOGIN)) &&
+		is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC,
+					      SIPE_SETTING_EMAIL_PASSWORD));
+
+	sipe_backend_notify_error(SIPE_CORE_PUBLIC,
+				  _("UCS initialization failed!"),
+				  default_settings ?
+				  _("Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n\nYou'll need to provide Email settings in the account setup.") :
+				  _("Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n\nPlease correct your Email settings."));
+}
+
 static void sipe_ucs_get_im_item_list_response(struct sipe_core_private *sipe_private,
 					       SIPE_UNUSED_PARAMETER struct sipe_ucs_transaction *trans,
 					       const sipe_xml *body,
 					       SIPE_UNUSED_PARAMETER gpointer callback_data)
 {
 	const sipe_xml *node = sipe_xml_child(body,
 					      "GetImItemListResponse/ImItemList");
 
@@ -645,51 +826,64 @@ static void sipe_ucs_get_im_item_list_re
 		if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES)) {
 			sipe_buddy_update_finish(sipe_private);
 			sipe_group_update_finish(sipe_private);
 		} else {
 			sipe_buddy_cleanup_local_list(sipe_private);
 			sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC);
 			sipe_subscribe_presence_initial(sipe_private);
 		}
+	} else if (sipe_private->ucs) {
+		SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_get_im_item_list_response: query failed, contact list operations will not work!");
+		ucs_init_failure(sipe_private);
 	}
 }
 
 static void ucs_get_im_item_list(struct sipe_core_private *sipe_private)
 {
 	if (sipe_private->ucs->migrated)
 		sipe_ucs_http_request(sipe_private,
 				      /* prioritize over pending default requests */
 				      sipe_ucs_transaction(sipe_private),
 				      g_strdup("<m:GetImItemList/>"),
 				      sipe_ucs_get_im_item_list_response,
 				      NULL);
 }
 
+static void ucs_set_ews_url(struct sipe_core_private *sipe_private,
+		      const gchar *ews_url)
+{
+	struct sipe_ucs *ucs = sipe_private->ucs;
+
+	SIPE_DEBUG_INFO("ucs_set_ews_url: '%s'", ews_url);
+	ucs->ews_url = g_strdup(ews_url);
+
+	/* this will trigger sending of the first deferred request */
+	ucs_get_im_item_list(sipe_private);
+}
+
 static void ucs_ews_autodiscover_cb(struct sipe_core_private *sipe_private,
 				    const struct sipe_ews_autodiscover_data *ews_data,
 				    SIPE_UNUSED_PARAMETER gpointer callback_data)
 {
 	struct sipe_ucs *ucs = sipe_private->ucs;
-	const gchar *ews_url;
+	const gchar *ews_url = NULL;
 
-	if (!ucs || !ews_data)
+	if (!ucs)
 		return;
 
-	ews_url = ews_data->ews_url;
+	if (ews_data)
+		ews_url = ews_data->ews_url;
+
 	if (is_empty(ews_url)) {
 		SIPE_DEBUG_ERROR_NOFORMAT("ucs_ews_autodiscover_cb: can't detect EWS URL, contact list operations will not work!");
-		return;
+		ucs_init_failure(sipe_private);
+	} else {
+		ucs_set_ews_url(sipe_private, ews_url);
 	}
-
-	SIPE_DEBUG_INFO("ucs_ews_autodiscover_cb: EWS URL '%s'", ews_url);
-	ucs->ews_url = g_strdup(ews_url);
-
-	/* this will trigger sending of the first deferred request */
-	ucs_get_im_item_list(sipe_private);
 }
 
 gboolean sipe_ucs_is_migrated(struct sipe_core_private *sipe_private)
 {
 	return(sipe_private->ucs ? sipe_private->ucs->migrated : FALSE);
 }
 
 void sipe_ucs_init(struct sipe_core_private *sipe_private,
@@ -714,25 +908,33 @@ void sipe_ucs_init(struct sipe_core_priv
 				SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_init: ignoring this contact list update - triggered by our last change");
 		}
 
 		ucs->last_response = 0;
 		return;
 	}
 
 	sipe_private->ucs = ucs = g_new0(struct sipe_ucs, 1);
-	ucs->migrated            = migrated;
+	ucs->migrated           = migrated;
 
 	/* create default transaction */
 	sipe_ucs_transaction(sipe_private);
 	ucs->default_transaction = ucs->transactions;
 
-	sipe_ews_autodiscover_start(sipe_private,
-				    ucs_ews_autodiscover_cb,
-				    NULL);
+	if (migrated) {
+		/* user specified a service URL? */
+		const gchar *ews_url = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_EMAIL_URL);
+
+		if (is_empty(ews_url))
+			sipe_ews_autodiscover_start(sipe_private,
+						    ucs_ews_autodiscover_cb,
+						    NULL);
+		else
+			ucs_set_ews_url(sipe_private, ews_url);
+	}
 }
 
 void sipe_ucs_free(struct sipe_core_private *sipe_private)
 {
 	struct sipe_ucs *ucs = sipe_private->ucs;
 	GSList *entry;
 
 	if (!ucs)
--- a/libpurple/protocols/sipe/core/sipe-ucs.h
+++ b/libpurple/protocols/sipe/core/sipe-ucs.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-ucs.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2014 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -17,16 +17,17 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 /* Forward declarations */
+struct sipe_backend_search_token;
 struct sipe_buddy;
 struct sipe_core_private;
 struct sipe_group;
 struct sipe_ucs_transaction;
 
 /**
  * Get buddy photo
  *
@@ -34,16 +35,39 @@ struct sipe_ucs_transaction;
  *
  * @param sipe_private SIPE core private data
  * @param uri          SIP URI of the user
  */
 void sipe_ucs_get_photo(struct sipe_core_private *sipe_private,
 			const gchar *uri);
 
 /**
+ * Search EWS contact list
+ *
+ * This is not directly related to UCS, but we can reuse the code.
+ *
+ * @param sipe_private SIPE core private data
+ * @param token        opaque search token provided by backend
+ * @param given_name   search parameters provided by the user...
+ * @param surname
+ * @param email
+ * @param sipid
+ * @param company
+ * @param country
+ */
+void sipe_ucs_search(struct sipe_core_private *sipe_private,
+		     struct sipe_backend_search_token *token,
+		     const gchar *given_name,
+		     const gchar *surname,
+		     const gchar *email,
+		     const gchar *sipid,
+		     const gchar *company,
+		     const gchar *country);
+
+/**
  * Has contact list been migrated to UCS?
  *
  * @param sipe_private SIPE core private data
  *
  * @return @c TRUE if contact list has been migrated
  */
 gboolean sipe_ucs_is_migrated(struct sipe_core_private *sipe_private);
 
--- a/libpurple/protocols/sipe/core/sipe-utils.c
+++ b/libpurple/protocols/sipe/core/sipe-utils.c
@@ -1,14 +1,14 @@
 /**
  * @file sipe-utils.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2009-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2009-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,16 +19,17 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
+#include <time.h>
 
 #include <glib.h>
 
 #include "sipe-backend.h"
 #include "sipe-core.h"    /* to ensure same API for backends */
 #include "sipe-core-private.h"
 #include "sipe-utils.h"
 #include "uuid.h"
@@ -273,17 +274,17 @@ sipe_is_bad_alias(const char *uri,
 
 gboolean
 is_empty(const char *st)
 {
 	if (!st || strlen(st) == 0)
 	{
 		return TRUE;
 	}
-	/* suspecious leading or trailing staces */
+	/* suspicious leading or trailing spaces */
 	else if (isspace((unsigned char) *st) ||
 		 isspace((unsigned char) *(st + strlen(st) - 1)))
 	{
 		/* to not modify original string */
 		char *dup = g_strdup(st);
 		if (strlen(g_strstrip(dup)) == 0) {
 			g_free(dup);
 			return TRUE;
@@ -390,16 +391,32 @@ sipe_utils_str_to_time(const gchar *time
 
 gchar *
 sipe_utils_time_to_str(time_t timestamp)
 {
 	GTimeVal time = { timestamp, 0 };
 	return g_time_val_to_iso8601(&time);
 }
 
+const gchar *sipe_utils_time_to_debug_str(const struct tm *tm)
+{
+	gchar *buffer = asctime(tm);
+	size_t length;
+
+	if (!buffer)
+		return("");
+
+	/* asctime() appends "\n" to the resulting string -> strip it */
+	length = strlen(buffer);
+	if (length)
+		buffer[length - 1] = '\0';
+
+	return(buffer);
+}
+
 size_t
 hex_str_to_buff(const char *hex_str, guint8 **buff)
 {
 	char two_digits[3];
 	size_t length;
 	size_t i;
 
 	if (!buff) return 0;
--- a/libpurple/protocols/sipe/core/sipe-utils.h
+++ b/libpurple/protocols/sipe/core/sipe-utils.h
@@ -1,14 +1,14 @@
 /**
  * @file sipe-utils.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2009-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2009-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -324,16 +324,25 @@ sipe_utils_str_to_time(const gchar *time
  *
  * Must be g_free()'d after use.
  *
  * Example: 2010-02-03T23:59:59Z
  */
 gchar *
 sipe_utils_time_to_str(time_t timestamp);
 
+/**
+ * Converts struct tm to human readable string
+ *
+ * Example: Sat Feb 28 11:07:35 2015
+ *
+ * @return pointer to static buffer. Will never return @c NULL.
+ */
+const gchar *sipe_utils_time_to_debug_str(const struct tm *tm);
+
 struct sipnameval {
 	gchar *name;
 	gchar *value;
 };
 
 /**
  * Parses string of hex digits to buffer.
  * Allocates memory.
--- a/libpurple/protocols/sipe/core/sipe-webticket.c
+++ b/libpurple/protocols/sipe/core/sipe-webticket.c
@@ -289,25 +289,36 @@ static void generate_federation_wsse(str
 	gchar *keydata   = sipe_xml_extract_raw(raw, "saml:Assertion", TRUE);
 
 	/* try alternative names */
 	if (!timestamp)
 		timestamp = generate_timestamp(raw, "wst:Lifetime");
 	if (!keydata)
 		keydata   = sipe_xml_extract_raw(raw, "saml1:Assertion", TRUE);
 
+	/* try other alternative names */
+	if (!timestamp)
+		timestamp = generate_timestamp(raw, "Lifetime");
+	if (!keydata)
+		keydata   = sipe_xml_extract_raw(raw, "ns1:Assertion", TRUE);
+
 	/* clear old ADFS token */
 	g_free(webticket->adfs_token);
 	webticket->adfs_token = NULL;
 
 	if (timestamp && keydata) {
 		gchar *expires_string = sipe_xml_extract_raw(timestamp,
 							     "wsu:Expires",
 							     FALSE);
 
+		/* try alternative names */
+		if (!expires_string)
+			expires_string = sipe_xml_extract_raw(timestamp,
+							      "ns3:Expires",
+							      FALSE);
 		if (expires_string) {
 
 			SIPE_DEBUG_INFO("generate_federation_wsse: found timestamp & keydata, expires %s",
 					expires_string);
 
 			/* cache ADFS token */
 			webticket->adfs_token         = g_strconcat(timestamp,
 								    keydata,
@@ -543,23 +554,37 @@ static void webticket_token(struct sipe_
 			break;
 		}
 
 		/* end of: switch (wcd->token_state) { */
 		}
 
 	} else if (uri) {
 		/* Retry with federated authentication? */
-		if (wcd->webticket_fedbearer_uri && !wcd->tried_fedbearer) {
-			SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
-					uri);
+		if (wcd->webticket_fedbearer_uri) {
+
+			/* Authentication against ADFS failed? */
+			if (wcd->token_state == TOKEN_STATE_FEDERATION) {
+				struct sipe_webticket *webticket = sipe_private->webticket;
+
+				SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ADFS authentication failed - assuming Multi-Factor Authentication (MFA)");
 
-			if (initiate_fedbearer(sipe_private, wcd)) {
-				/* callback data passed down the line */
-				wcd = NULL;
+				/* forget ADFS URI */
+				g_free(webticket->webticket_adfs_uri);
+				webticket->webticket_adfs_uri = NULL;
+			}
+
+			if (!wcd->tried_fedbearer) {
+				SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
+						uri);
+
+				if (initiate_fedbearer(sipe_private, wcd)) {
+					/* callback data passed down the line */
+					wcd = NULL;
+				}
 			}
 		}
 	}
 
 	if (wcd) {
 		if (failed) {
 			gchar *failure_msg = NULL;
 
@@ -687,26 +712,25 @@ static void realminfo(struct sipe_core_p
 
 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
 				   struct webticket_callback_data *wcd)
 {
 	gboolean success;
 
 	if (sipe_private->webticket->retrieved_realminfo) {
 		/* skip retrieval and go to authentication */
+		wcd->tried_fedbearer = TRUE;
 		success = fedbearer_authentication(sipe_private, wcd);
 	} else {
 		success = sipe_svc_realminfo(sipe_private,
 					     wcd->session,
 					     realminfo,
 					     wcd);
 	}
 
-	wcd->tried_fedbearer = TRUE;
-
 	return(success);
 }
 
 static void webticket_metadata(struct sipe_core_private *sipe_private,
 			       const gchar *uri,
 			       SIPE_UNUSED_PARAMETER const gchar *raw,
 			       sipe_xml *metadata,
 			       gpointer callback_data)
--- a/libpurple/protocols/sipe/core/sipmsg.c
+++ b/libpurple/protocols/sipe/core/sipmsg.c
@@ -1,14 +1,14 @@
 /**
  * @file sipmsg.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2014 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -86,20 +86,29 @@ struct sipmsg *sipmsg_parse_header(const
 	if (contentlength) {
 		msg->bodylen = strtol(contentlength,NULL,10);
 	} else {
 		const gchar *tmp = sipmsg_find_header(msg, "Transfer-Encoding");
 		if (tmp && sipe_strcase_equal(tmp, "chunked")) {
 			msg->bodylen = SIPMSG_BODYLEN_CHUNKED;
 		} else {
 			tmp = sipmsg_find_header(msg, "Content-Type");
-			if (tmp)
-				SIPE_DEBUG_FATAL_NOFORMAT("sipmsg_parse_header(): Content-Length header not found");
-			else
+			if (tmp) {
+				/*
+				 * This is a fatal error situation: the message
+				 * is corrupted and we can't proceed. Set the
+				 * response code to a special value so that the
+				 * caller can abort correctly.
+				 */
+				SIPE_DEBUG_ERROR_NOFORMAT("sipmsg_parse_header: Content-Length header not found. Aborting!");
+				msg->response = SIPMSG_RESPONSE_FATAL_ERROR;
+				return(msg);
+			} else {
 				msg->bodylen = 0;
+			}
 		}
 	}
 	if(msg->response) {
 		const gchar *tmp;
 		tmp = sipmsg_find_header(msg, "CSeq");
 		if(!tmp) {
 			/* SHOULD NOT HAPPEN */
 			msg->method = 0;
@@ -382,17 +391,17 @@ void sipmsg_parse_p_asserted_identity(co
 	*tel_uri = NULL;
 
 	if (g_ascii_strncasecmp(header, "tel:", 4) == 0) {
 		*tel_uri = g_strdup(header);
 		return;
 	}
 
 	parts = g_strsplit(header, ",", 0);
-	
+
 	for (p = parts; *p; p++) {
 		gchar *uri = sipmsg_find_part_of_header(*p, "<", ">", NULL);
 		if (!uri)
 			continue;
 
 		if (g_ascii_strncasecmp(uri, "sip:", 4) == 0) {
 			if (*sip_uri) {
 				SIPE_DEBUG_WARNING_NOFORMAT("More than one "
@@ -423,33 +432,40 @@ void sipmsg_parse_p_asserted_identity(co
  *
  *  Use this function when you want to look for a specific authentication
  *  method such as NTLM or Kerberos
  */
 
 const gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
 	GSList *tmp;
 	struct sipnameval *elem;
-	int name_len = strlen(name);
+	int name_len;
+
+	if (!name) {
+		SIPE_DEBUG_INFO_NOFORMAT("sipmsg_find_auth_header: no authentication scheme specified");
+		return NULL;
+	}
+
+	name_len = strlen(name);
 	tmp = msg->headers;
 	while(tmp) {
 		elem = tmp->data;
 		/* SIPE_DEBUG_INFO("Current header: %s", elem->value); */
 		if (elem && elem->name &&
 		    (sipe_strcase_equal(elem->name,"WWW-Authenticate") ||
 		     sipe_strcase_equal(elem->name,"Authentication-Info")) ) {
 			if (!g_ascii_strncasecmp((gchar *)elem->value, name, name_len)) {
 				/* SIPE_DEBUG_INFO("elem->value: %s", elem->value); */
 				return elem->value;
 			}
 		}
 		/* SIPE_DEBUG_INFO_NOFORMAT("moving to next header"); */
 		tmp = g_slist_next(tmp);
 	}
-	SIPE_DEBUG_INFO("auth header '%s' not found.", name);
+	SIPE_DEBUG_INFO("sipmsg_find_auth_header: '%s' not found", name);
 	return NULL;
 }
 
 /**
  * Parses headers-like 'msgr' attribute of INVITE's 'ms_text_format' header.
  * Then retrieves value of 'X-MMS-IM-Format'.
 
  * 'msgr' typically looks like:
@@ -867,16 +883,39 @@ sipe_parse_html(const char *html, char *
 	gboolean has_italic = FALSE;
 	gboolean has_underline = FALSE;
 	gboolean has_strikethrough = FALSE;
 
 	g_return_if_fail(html       != NULL);
 	g_return_if_fail(attributes != NULL);
 	g_return_if_fail(message    != NULL);
 
+#define _HTML_UNESCAPE \
+	if (!g_ascii_strncasecmp(c, "&lt;", 4)) { \
+		msg[retcount++] = '<'; \
+		c += 4; \
+	} else if (!g_ascii_strncasecmp(c, "&gt;", 4)) { \
+		msg[retcount++] = '>'; \
+		c += 4; \
+	} else if (!g_ascii_strncasecmp(c, "&nbsp;", 6)) { \
+		msg[retcount++] = ' '; \
+		c += 6; \
+	} else if (!g_ascii_strncasecmp(c, "&quot;", 6)) { \
+		msg[retcount++] = '"'; \
+		c += 6; \
+	} else if (!g_ascii_strncasecmp(c, "&amp;", 5)) { \
+		msg[retcount++] = '&'; \
+		c += 5; \
+	} else if (!g_ascii_strncasecmp(c, "&apos;", 6)) { \
+		msg[retcount++] = '\''; \
+		c += 6; \
+	} else { \
+		msg[retcount++] = *c++; \
+	}
+
 	len = strlen(html);
 	msg = g_malloc0(len + 1);
 
 	memset(fontcolor, 0, sizeof(fontcolor));
 	strcat(fontcolor, "0");
 	memset(fonteffect, 0, sizeof(fonteffect));
 
 	for (c = html; *c != '\0';)
@@ -928,17 +967,20 @@ sipe_parse_html(const char *html, char *
 			else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
 			{
 				c += 9;
 
 				if (!g_ascii_strncasecmp(c, "mailto:", 7))
 					c += 7;
 
 				while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
-					msg[retcount++] = *c++;
+					if (*c == '&') {
+						_HTML_UNESCAPE;
+					} else
+						msg[retcount++] = *c++;
 
 				if (*c != '\0')
 					c += 2;
 
 				/* ignore descriptive string */
 				while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
 					c++;
 
@@ -1041,62 +1083,33 @@ sipe_parse_html(const char *html, char *
 				while ((*c != '\0') && (*c != '>'))
 					c++;
 				if (*c != '\0')
 					c++;
 			}
 		}
 		else if (*c == '&')
 		{
-			if (!g_ascii_strncasecmp(c, "&lt;", 4))
-			{
-				msg[retcount++] = '<';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&gt;", 4))
-			{
-				msg[retcount++] = '>';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
-			{
-				msg[retcount++] = ' ';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&quot;", 6))
-			{
-				msg[retcount++] = '"';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&amp;", 5))
-			{
-				msg[retcount++] = '&';
-				c += 5;
-			}
-			else if (!g_ascii_strncasecmp(c, "&apos;", 6))
-			{
-				msg[retcount++] = '\'';
-				c += 6;
-			}
-			else
-				msg[retcount++] = *c++;
+			_HTML_UNESCAPE;
 		}
 		else
 			msg[retcount++] = *c++;
 	}
 
 	if (fontface == NULL)
 		fontface = g_strdup("MS Sans Serif");
 
 	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
 								  encode_spaces(fontface),
 								  fonteffect, fontcolor, direction);
 	*message = msg;
 
 	g_free(fontface);
+
+#undef _HTML_UNESCAPE
 }
 // End of TEMP
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
--- a/libpurple/protocols/sipe/core/sipmsg.h
+++ b/libpurple/protocols/sipe/core/sipmsg.h
@@ -1,14 +1,14 @@
 /**
  * @file sipmsg.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2014 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -31,17 +31,18 @@
 /* Avoid conflicting with functions defined in simple to statically link SIPE. */
 #define sipmsg_parse_msg sipemsg_parse_msg
 #define sipmsg_parse_header sipemsg_parse_header
 #define sipmsg_free sipemsg_free
 #define sipmsg_find_header sipemsg_find_header
 #define sipmsg_add_header sipemsg_add_header
 #define sipmsg_to_string sipemsg_to_string
 
-#define SIPMSG_BODYLEN_CHUNKED -1
+#define SIPMSG_RESPONSE_FATAL_ERROR -1
+#define SIPMSG_BODYLEN_CHUNKED      -1
 
 struct sipmsg {
 	int response; /* 0 means request, otherwise response code */
 	gchar *responsestr;
 	gchar *method;
 	gchar *target;
 	GSList *headers;
 	GSList *new_headers;
--- a/libpurple/protocols/sipe/purple-buddy.c
+++ b/libpurple/protocols/sipe/purple-buddy.c
@@ -1,14 +1,14 @@
 /**
  * @file purple-buddy.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2014 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -308,20 +308,40 @@ void sipe_backend_buddy_set_blocked_stat
 								 who));
 }
 
 void sipe_backend_buddy_set_status(struct sipe_core_public *sipe_public,
 				   const gchar *who,
 				   guint activity)
 {
 	struct sipe_backend_private *purple_private = sipe_public->backend_private;
+	PurpleBuddy *buddy = NULL;
+	PurpleStatus *status = NULL;
+	gchar *tmp = NULL;
 
-	purple_prpl_got_user_status(purple_private->account, who,
-				    sipe_purple_activity_to_token(activity),
-				    NULL);
+	buddy = purple_blist_find_buddy(purple_private->account, who);
+	if (buddy)
+		status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
+
+	if (status)
+		tmp = sipe_core_buddy_status(PURPLE_BUDDY_TO_SIPE_CORE_PUBLIC,
+					     purple_buddy_get_name(buddy),
+					     sipe_purple_token_to_activity(purple_status_get_id(status)),
+					     purple_status_get_name(status));
+
+	if (tmp) {
+		purple_prpl_got_user_status(purple_private->account, who,
+					    sipe_purple_activity_to_token(activity),
+					    SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE, tmp,
+					    NULL);
+		g_free(tmp);
+	} else
+		purple_prpl_got_user_status(purple_private->account, who,
+					    sipe_purple_activity_to_token(activity),
+					    NULL);
 }
 
 gboolean sipe_backend_uses_photo(void)
 {
 	return TRUE;
 }
 
 void sipe_backend_buddy_set_photo(struct sipe_core_public *sipe_public,
@@ -458,16 +478,19 @@ void sipe_purple_add_buddy(PurpleConnect
 					    purple_buddy_get_name(buddy),
 					    purple_group_get_name(group));
 		} else {
 			SIPE_DEBUG_ERROR_NOFORMAT("sipe_purple_add_buddy[CB]: buddy name is invalid for URI");
 			purple_blist_remove_buddy(buddy);
 			purple_notify_error(gc, NULL,
 					    _("User name should be a valid SIP URI\nExample: user@company.com"),
 					    NULL
+#if PURPLE_VERSION_CHECK(3,0,0)
+					    , NULL
+#endif
 					    );
 		}
 	}
 }
 
 void sipe_purple_remove_buddy(PurpleConnection *gc,
 			      PurpleBuddy *buddy,
 			      PurpleGroup *group)
@@ -589,19 +612,16 @@ static void sipe_purple_ask_access_domai
 static void sipe_purple_buddy_add_new_domain_cb(PurpleBuddy *buddy,
 						SIPE_UNUSED_PARAMETER gpointer parameter)
 {
 	PurpleAccount *account = purple_buddy_get_account(buddy);
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *g;
 	PurpleRequestField *f;
-#if PURPLE_VERSION_CHECK(3,0,0)
-	PurpleRequestCommonParameters *cpar = purple_request_cpar_from_account(account);
-#endif
 
 	fields = purple_request_fields_new();
 
 	g = purple_request_field_group_new(NULL);
 	f = purple_request_field_string_new("access_domain",
 					    _("Domain"),
 					    "partner-company.com",
 					    FALSE);
@@ -631,21 +651,21 @@ static void sipe_purple_buddy_add_new_do
 
 	purple_request_fields_add_group(fields, g);
 
 	purple_request_fields(gc, _("Add new domain"),
 			      _("Add new domain"), NULL, fields,
 			      _("Add"), G_CALLBACK(sipe_purple_ask_access_domain_cb),
 			      _("Cancel"), NULL,
 #if PURPLE_VERSION_CHECK(3,0,0)
-			      cpar, gc);
-	purple_request_cpar_unref(cpar);
+			      purple_request_cpar_from_account(account),
 #else
-			      account, NULL, NULL, gc);
+			      account, NULL, NULL,
 #endif
+			      gc);
 }
 
 typedef void (*buddy_menu_callback)(PurpleBuddy *buddy,
 				    gpointer parameter);
 static const buddy_menu_callback callback_map[SIPE_BUDDY_MENU_TYPES] = {
 /* SIPE_BUDDY_MENU_MAKE_CHAT_LEADER    */ sipe_purple_buddy_make_chat_leader_cb,
 /* SIPE_BUDDY_MENU_REMOVE_FROM_CHAT    */ sipe_purple_buddy_remove_from_chat_cb,
 /* SIPE_BUDDY_MENU_INVITE_TO_CHAT      */ sipe_purple_buddy_invite_to_chat_cb,
@@ -743,16 +763,17 @@ static void sipe_purple_buddy_copy_to_cb
 			purple_presence_set_status_active(purple_buddy_get_presence(clone),
 							  tmp,
 							  TRUE);
 
 			/* update UI */
 			purple_prpl_got_user_status(purple_buddy_get_account(clone),
 						    purple_buddy_get_name(clone),
 						    tmp,
+						    SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE, tmp,
 						    NULL);
 		}
 	}
 
 	if (clone && group)
 		sipe_core_buddy_add(sipe_public,
 				    purple_buddy_get_name(clone),
 				    purple_group_get_name(group));
@@ -764,18 +785,18 @@ static GList *sipe_purple_copy_to_menu(G
 	GList *menu_groups = NULL;
 	PurpleGroup *gr_parent = purple_buddy_get_group(buddy);
 	PurpleBlistNode *g_node;
 
 	for (g_node = purple_blist_get_root(); g_node; g_node = g_node->next) {
 		PurpleGroup *group = (PurpleGroup *)g_node;
 		PurpleMenuAction *act;
 
-		if ((PURPLE_IS_GROUP(g_node)) ||
-		    (group == gr_parent)      ||
+		if ((!PURPLE_IS_GROUP(g_node)) ||
+		    (group == gr_parent)       ||
 		    purple_blist_find_buddy_in_group(purple_buddy_get_account(buddy),
 						     purple_buddy_get_name(buddy),
 						     group))
 			continue;
 
 		act = purple_menu_action_new(purple_group_get_name(group),
 					     PURPLE_CALLBACK(sipe_purple_buddy_copy_to_cb),
 					     (gpointer) purple_group_get_name(group),
--- a/libpurple/protocols/sipe/purple-chat.c
+++ b/libpurple/protocols/sipe/purple-chat.c
@@ -1,14 +1,14 @@
 /**
  * @file purple-chat.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2009 pier11 <pier11@operamail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
@@ -53,16 +53,18 @@
 #define purple_chat_conversation_get_id(c)               purple_conv_chat_get_id(c)
 #define purple_chat_conversation_remove_user(c, n, s)    purple_conv_chat_remove_user(c, n, s)
 #define purple_chat_conversation_set_nick(c, n)          purple_conv_chat_set_nick(c, n)
 #define purple_chat_conversation_set_topic(c, n, s)      purple_conv_chat_set_topic(c, n, s)
 #define purple_chat_get_components(chat)                 chat->components
 #define purple_conversations_find_chat(g, n)             purple_find_chat(g, n)
 #define purple_conversations_get_chats                   purple_get_chats
 #define purple_conversation_get_connection(c)            purple_conversation_get_gc(c)
+#define purple_serv_got_chat_in(c, i, w, f, m, t)        serv_got_chat_in(c, i, w, f, m, t)
+#define purple_serv_got_joined_chat(c, i, n)             serv_got_joined_chat(c, i, n)
 #define BACKEND_SESSION_TO_PURPLE_CONV_CHAT(s)           (PURPLE_CONV_CHAT(((PurpleConversation *)s)))
 #define PURPLE_CHAT_USER_NONE                            PURPLE_CBFLAGS_NONE
 #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC                  ((struct sipe_core_public *) conv->account->gc->proto_data)
 #define PURPLE_CONVERSATION_UPDATE_TOPIC                 PURPLE_CONV_UPDATE_TOPIC
 #endif
 
 #include "sipe-common.h"
 #include "sipe-backend.h"
@@ -148,17 +150,17 @@
  *
  *    HAS: rejoin_chats (GList *)
  *         created on login() for existing chats
  *         initiate re-join calls to core (sipe_backend_chat_rejoin_all)
  */
 
 #define SIPE_PURPLE_KEY_CHAT_SESSION "sipe"
 
-static struct sipe_chat_session *sipe_purple_chat_get_session(PurpleConversation *conv)
+struct sipe_chat_session *sipe_purple_chat_get_session(PurpleConversation *conv)
 {
 	return(
 #if PURPLE_VERSION_CHECK(3,0,0)
 		g_object_get_data(G_OBJECT(conv),
 #else
 		purple_conversation_get_data(conv,
 #endif
 				  SIPE_PURPLE_KEY_CHAT_SESSION));
@@ -212,22 +214,31 @@ void sipe_purple_chat_leave(PurpleConnec
 	struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
 	if (!session) return;
 
 	sipe_core_chat_leave(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session);
 }
 
 int sipe_purple_chat_send(PurpleConnection *gc,
 			  int id,
+#if PURPLE_VERSION_CHECK(3,0,0)
+			  PurpleMessage *msg)
+#else
 			  const char *what,
 			  SIPE_UNUSED_PARAMETER PurpleMessageFlags flags)
+#endif
 {
 	struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
 	if (!session) return -ENOTCONN;
-	sipe_core_chat_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, what);
+	sipe_core_chat_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session,
+#if PURPLE_VERSION_CHECK(3,0,0)
+			purple_message_get_contents(msg));
+#else
+			what);
+#endif
 	return 1;
 }
 
 static void sipe_purple_chat_menu_unlock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
 					    PurpleConversation *conv)
 {
 	struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
 	struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
@@ -342,23 +353,31 @@ static int sipe_purple_chat_id(PurpleCon
 
 struct sipe_backend_chat_session *sipe_backend_chat_create(struct sipe_core_public *sipe_public,
 							   struct sipe_chat_session *session,
 							   const gchar *title,
 							   const gchar *nick)
 {
 	struct sipe_backend_private *purple_private = sipe_public->backend_private;
 #if PURPLE_VERSION_CHECK(3,0,0)
-	PurpleChatConversation *conv =
+	PurpleChatConversation *conv;
 #else
-	PurpleConversation *conv =
+	PurpleConversation *conv;
 #endif
-		serv_got_joined_chat(purple_private->gc,
-				     sipe_purple_chat_id(purple_private->gc),
-				     title);
+
+	/*
+	 * Adium calls back into SIPE code during execution of the following
+	 * libpurple API. That code needs access to "session". As "conv" is
+	 * still being initialized we can't use sipe_purple_chat_get_session().
+	 */
+	purple_private->adium_chat_session = session;
+	conv = purple_serv_got_joined_chat(purple_private->gc,
+					   sipe_purple_chat_id(purple_private->gc),
+					   title);
+	purple_private->adium_chat_session = NULL;
 #if PURPLE_VERSION_CHECK(3,0,0)
 	g_object_set_data(G_OBJECT(conv),
 #else
 	purple_conversation_set_data(conv,
 #endif
 				     SIPE_PURPLE_KEY_CHAT_SESSION,
 				     session);
 	purple_chat_conversation_set_nick(PURPLE_CONV_CHAT(conv), nick);
@@ -395,22 +414,22 @@ gboolean sipe_backend_chat_is_operator(s
 
 void sipe_backend_chat_message(struct sipe_core_public *sipe_public,
 			       struct sipe_backend_chat_session *backend_session,
 			       const gchar *from,
 			       time_t when,
 			       const gchar *html)
 {
 	struct sipe_backend_private *purple_private = sipe_public->backend_private;
-	serv_got_chat_in(purple_private->gc,
-			 purple_chat_conversation_get_id(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session)),
-			 from,
-			 PURPLE_MESSAGE_RECV,
-			 html,
-			 when ? when : time(NULL));
+	purple_serv_got_chat_in(purple_private->gc,
+				purple_chat_conversation_get_id(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session)),
+				from,
+				PURPLE_MESSAGE_RECV,
+				html,
+				when ? when : time(NULL));
 }
 
 void sipe_backend_chat_operator(struct sipe_backend_chat_session *backend_session,
 				const gchar *uri)
 {
 #if PURPLE_VERSION_CHECK(3,0,0)
 	purple_chat_user_set_flags(
 		purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
@@ -436,19 +455,19 @@ void sipe_backend_chat_rejoin(struct sip
 	PurpleConvChat *chat =         BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session);
 	PurpleConversation *new;
 #endif
 
 	/**
 	 * As the chat is marked as "left", serv_got_joined_chat() will
 	 * do a "rejoin cleanup" and return the same conversation.
 	 */
-	new = serv_got_joined_chat(purple_private->gc,
-				   purple_chat_conversation_get_id(chat),
-				   title);
+	new = purple_serv_got_joined_chat(purple_private->gc,
+					  purple_chat_conversation_get_id(chat),
+					  title);
 	SIPE_DEBUG_INFO("sipe_backend_chat_rejoin: old %p (%p) == new %p (%p)",
 			backend_session, chat,
 			new, PURPLE_CONV_CHAT(new));
 	purple_chat_conversation_set_nick(chat, nick);
 }
 
 /**
  * Connection re-established: tell core what chats need to be rejoined
--- a/libpurple/protocols/sipe/purple-debug.c
+++ b/libpurple/protocols/sipe/purple-debug.c
@@ -1,14 +1,14 @@
 /**
  * @file purple-debug.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -24,61 +24,71 @@
 
 #include "glib.h"
 #include "debug.h"
 
 #include "sipe-backend.h"
 
 #define purple_debug_is_enabled() 0
 
+#ifdef ADIUM
+/*
+ * libpurple uses g_print() and PurpleDebugUiOps->debug() when
+ * purple_debug_is_enabled() returns TRUE. Both are redirected
+ * by Adium to AILog(). To avoid duplicated log lines Adium
+ * therefore never calls purple_debug_set_enabled(TRUE).
+ */
+gboolean AIDebugLoggingIsEnabled(void);
+#define SIPE_PURPLE_DEBUG_IS_ENABLED AIDebugLoggingIsEnabled()
+#else
+#define SIPE_PURPLE_DEBUG_IS_ENABLED purple_debug_is_enabled()
+#endif
+
 void sipe_backend_debug_literal(sipe_debug_level level,
 				const gchar *msg)
 {
-	if (purple_debug_is_enabled()) {
+	if (SIPE_PURPLE_DEBUG_IS_ENABLED) {
 
 		/* purple_debug doesn't have a vprintf-like API call :-( */
 		switch (level) {
 		case SIPE_DEBUG_LEVEL_INFO:
 			purple_debug_info("sipe", "%s\n", msg);
 			break;
 		case SIPE_DEBUG_LEVEL_WARNING:
 			purple_debug_warning("sipe", "%s\n", msg);
 			break;
 		case SIPE_DEBUG_LEVEL_ERROR:
 			purple_debug_error("sipe", "%s\n", msg);
 			break;
-		case SIPE_DEBUG_LEVEL_FATAL:
-			purple_debug_fatal("sipe", "%s\n", msg);
-			break;
 		}
 	}
 }
 
 void sipe_backend_debug(sipe_debug_level level,
 			const gchar *format,
 			...)
 {
 	va_list ap;
 
 	va_start(ap, format);
 
-	if (purple_debug_is_enabled()) {
+	if (SIPE_PURPLE_DEBUG_IS_ENABLED) {
 
 		/* purple_debug doesn't have a vprintf-like API call :-( */
 		gchar *msg = g_strdup_vprintf(format, ap);
 		sipe_backend_debug_literal(level, msg);
 		g_free(msg);
 	}
 
 	va_end(ap);
 }
 
 gboolean sipe_backend_debug_enabled(void)
 {
-	return purple_debug_is_enabled();
+	return SIPE_PURPLE_DEBUG_IS_ENABLED;
 }
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
   tab-width: 8
--- a/libpurple/protocols/sipe/purple-ft.c
+++ b/libpurple/protocols/sipe/purple-ft.c
@@ -329,35 +329,39 @@ void sipe_purple_ft_send_file(PurpleConn
 			purple_xfer_request(xfer);
 	}
 }
 
 PurpleXfer *sipe_purple_ft_new_xfer(PurpleConnection *gc, const char *who)
 {
 	PurpleXfer *xfer = NULL;
 
-	if (PURPLE_CONNECTION_IS_VALID(gc)) {
-		xfer = purple_xfer_new(purple_connection_get_account(gc),
-				       PURPLE_XFER_TYPE_SEND,
-				       who);
+#if !PURPLE_VERSION_CHECK(3,0,0)
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		return NULL;
+	}
+#endif
 
-		if (xfer) {
-			struct sipe_file_transfer *ft = sipe_core_ft_allocate(PURPLE_GC_TO_SIPE_CORE_PUBLIC);
+	xfer = purple_xfer_new(purple_connection_get_account(gc),
+					   PURPLE_XFER_TYPE_SEND,
+					   who);
 
-			ft->backend_private = (struct sipe_backend_file_transfer *)xfer;
-			purple_xfer_set_protocol_data(xfer, ft);
+	if (xfer) {
+		struct sipe_file_transfer *ft = sipe_core_ft_allocate(PURPLE_GC_TO_SIPE_CORE_PUBLIC);
+
+		ft->backend_private = (struct sipe_backend_file_transfer *)xfer;
+		purple_xfer_set_protocol_data(xfer, ft);
 
-			purple_xfer_set_init_fnc(xfer, ft_outgoing_init);
-			purple_xfer_set_request_denied_fnc(xfer, ft_request_denied);
-			purple_xfer_set_cancel_send_fnc(xfer, ft_free_xfer_struct);
-			purple_xfer_set_cancel_recv_fnc(xfer, ft_free_xfer_struct);
-			purple_xfer_set_start_fnc(xfer, tftp_outgoing_start);
-			purple_xfer_set_end_fnc(xfer, tftp_outgoing_stop);
-			purple_xfer_set_write_fnc(xfer, tftp_write);
-		}
+		purple_xfer_set_init_fnc(xfer, ft_outgoing_init);
+		purple_xfer_set_request_denied_fnc(xfer, ft_request_denied);
+		purple_xfer_set_cancel_send_fnc(xfer, ft_free_xfer_struct);
+		purple_xfer_set_cancel_recv_fnc(xfer, ft_free_xfer_struct);
+		purple_xfer_set_start_fnc(xfer, tftp_outgoing_start);
+		purple_xfer_set_end_fnc(xfer, tftp_outgoing_stop);
+		purple_xfer_set_write_fnc(xfer, tftp_write);
 	}
 
 	return xfer;
 }
 
 gboolean
 sipe_backend_ft_is_incoming(struct sipe_file_transfer *ft)
 {
--- a/libpurple/protocols/sipe/purple-im.c
+++ b/libpurple/protocols/sipe/purple-im.c
@@ -28,30 +28,32 @@
 
 #include <glib.h>
 
 #include "server.h"
 
 #include "version.h"
 #if PURPLE_VERSION_CHECK(3,0,0)
 #include "conversations.h"
+#else
+#define purple_serv_got_im(c, w, m, f, t)	serv_got_im(c, w, m, f, t)
 #endif
 
 #include "purple-private.h"
 
 #include "sipe-backend.h"
 #include "sipe-core.h"
 #include "sipe-nls.h"
 
 void sipe_backend_im_message(struct sipe_core_public *sipe_public,
 			     const gchar *from,
 			     const gchar *html)
 {
 	struct sipe_backend_private *purple_private = sipe_public->backend_private;
-	serv_got_im(purple_private->gc,
+	purple_serv_got_im(purple_private->gc,
 		    from,
 		    html,
 		    0,
 		    time(NULL));
 }
 
 void sipe_backend_im_topic(struct sipe_core_public *sipe_public,
 			   const gchar *with,
--- a/libpurple/protocols/sipe/purple-notify.c
+++ b/libpurple/protocols/sipe/purple-notify.c
@@ -52,19 +52,24 @@ static void notify_message(struct sipe_c
 #if PURPLE_VERSION_CHECK(3,0,0)
 		conv = (PurpleConversation *) purple_conversations_find_im_with_account(
 #else
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
 #endif
 							     who,
 							     purple_private->account);
 	}
-	if (conv)
+	if (conv) {
+#if PURPLE_VERSION_CHECK(3,0,0)
+		purple_conversation_write_system_message(conv, message, flags);
+#else
 		purple_conversation_write(conv, NULL, message, flags,
 					  time(NULL));
+#endif
+	}
 }
 
 void sipe_backend_notify_message_error(struct sipe_core_public *sipe_public,
 				       struct sipe_backend_chat_session *backend_session,
 				       const gchar *who,
 				       const gchar *message)
 {
 	notify_message(sipe_public, PURPLE_MESSAGE_ERROR,
@@ -82,16 +87,19 @@ void sipe_backend_notify_message_info(st
 
 void sipe_backend_notify_error(struct sipe_core_public *sipe_public,
 			       const gchar *title,
 			       const gchar *msg)
 {
 	struct sipe_backend_private *purple_private = sipe_public->backend_private;
 
 	purple_notify_error(purple_private->gc, NULL, title, msg
+#if PURPLE_VERSION_CHECK(3,0,0)
+			    , NULL
+#endif
 			    );
 }
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
--- a/libpurple/protocols/sipe/purple-plugin.c
+++ b/libpurple/protocols/sipe/purple-plugin.c
@@ -1,14 +1,14 @@
 /**
  * @file purple-plugin.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -103,17 +103,21 @@
  *  - is Kerberos supported
  */
 #if defined(HAVE_GSSAPI_GSSAPI_H) || defined(HAVE_SSPI)
 #define PURPLE_SIPE_SSO_AND_KERBEROS 1
 #else
 #define PURPLE_SIPE_SSO_AND_KERBEROS 0
 #endif
 
-/* Sipe core activity <-> Purple status mapping */
+/*
+ * SIPE core activity <-> Purple status mapping
+ *
+ * NOTE: this needs to be kept in sync with sipe_purple_status_types()
+ */
 static const gchar * const activity_to_purple_map[SIPE_ACTIVITY_NUM_TYPES] = {
 /* SIPE_ACTIVITY_UNSET       */ "unset",     /* == purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) */
 /* SIPE_ACTIVITY_AVAILABLE   */ "available", /* == purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) */
 /* SIPE_ACTIVITY_ONLINE      */ "online",
 /* SIPE_ACTIVITY_INACTIVE    */ "idle",
 /* SIPE_ACTIVITY_BUSY        */ "busy",
 /* SIPE_ACTIVITY_BUSYIDLE    */ "busyidle",
 /* SIPE_ACTIVITY_DND         */ "do-not-disturb",
@@ -122,16 +126,17 @@ static const gchar * const activity_to_p
 /* SIPE_ACTIVITY_LUNCH       */ "out-to-lunch",
 /* SIPE_ACTIVITY_INVISIBLE   */ "invisible", /* == purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) */
 /* SIPE_ACTIVITY_OFFLINE     */ "offline",   /* == purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) */
 /* SIPE_ACTIVITY_ON_PHONE    */ "on-the-phone",
 /* SIPE_ACTIVITY_IN_CONF     */ "in-a-conference",
 /* SIPE_ACTIVITY_IN_MEETING  */ "in-a-meeting",
 /* SIPE_ACTIVITY_OOF         */ "out-of-office",
 /* SIPE_ACTIVITY_URGENT_ONLY */ "urgent-interruptions-only",
+/* SIPE_ACTIVIY_NUM_TYPES == 17 -> compare to sipe_purple_status_types() */
 };
 
 GHashTable *purple_token_map;
 
 static void sipe_purple_activity_init(void)
 {
 	guint index;
 
@@ -193,78 +198,137 @@ static void sipe_purple_tooltip_text(Pur
 				     (struct sipe_backend_buddy_tooltip *) user_info);
 }
 
 static GList *sipe_purple_status_types(SIPE_UNUSED_PARAMETER PurpleAccount *acc)
 {
 	PurpleStatusType *type;
 	GList *types = NULL;
 
-	/* Macros to reduce code repetition.
+	/* Macro to reduce code repetition
 	   Translators: noun */
-#define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
-		prim, id, name,             \
+#define SIPE_ADD_STATUS(prim,activity,user) type = purple_status_type_new_with_attrs( \
+		prim, \
+		sipe_purple_activity_to_token(activity), \
+		sipe_core_activity_description(activity), \
 		TRUE, user, FALSE,          \
 		SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
 		NULL);                      \
 	types = g_list_append(types, type);
 
-	/* Online */
+	/*
+	 * NOTE: needs to be kept in sync with activity_to_purple_map[],
+	 *       i.e. for each SIPE_ACTIVITY_xxx value there must be an
+	 *       entry on this list.
+	 *
+	 * NOTE: the following code is sorted by purple primitive type not
+	 *       by SIPE_ACTIVITY_xxx value.
+	 */
+
+	/*  1: Unset - special case: no entry needed */
+
+	/*
+	 * Status list entries for primitive type AVAILABLE
+	 *
+	 *  2: Available */
 	SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE,
-			NULL,
-			NULL,
+			SIPE_ACTIVITY_AVAILABLE,
 			TRUE);
 
-	/* Busy */
+	/*  3: Online */
+	SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE,
+			SIPE_ACTIVITY_ONLINE,
+			FALSE);
+
+	/*  4: Inactive (Idle) */
+	SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE,
+			SIPE_ACTIVITY_INACTIVE,
+			FALSE);
+
+	/*
+	 * Status list entries for primitive type UNAVAILABLE
+	 *
+	 *  5: Busy */
 	SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE,
-			sipe_purple_activity_to_token(SIPE_ACTIVITY_BUSY),
-			sipe_core_activity_description(SIPE_ACTIVITY_BUSY),
+			SIPE_ACTIVITY_BUSY,
 			TRUE);
 
-	/* Do Not Disturb */
+	/*  6: Busy-Idle */
 	SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE,
-			sipe_purple_activity_to_token(SIPE_ACTIVITY_DND),
-			NULL,
+			SIPE_ACTIVITY_BUSYIDLE,
+			FALSE);
+
+	/*  7: Do Not Disturb */
+	SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE,
+			SIPE_ACTIVITY_DND,
 			TRUE);
 
-	/* In a call */
+	/*  8: In a call */
 	SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE,
-			sipe_purple_activity_to_token(SIPE_ACTIVITY_ON_PHONE),
-			sipe_core_activity_description(SIPE_ACTIVITY_ON_PHONE),
+			SIPE_ACTIVITY_ON_PHONE,
 			FALSE);
 
-	/* In a conference call  */
+	/*  9: In a conference call */
 	SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE,
-			sipe_purple_activity_to_token(SIPE_ACTIVITY_IN_CONF),
-			sipe_core_activity_description(SIPE_ACTIVITY_IN_CONF),
+			SIPE_ACTIVITY_IN_CONF,
+			FALSE);
+
+	/* 10: In a meeting */
+	SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE,
+			SIPE_ACTIVITY_IN_MEETING,
 			FALSE);
 
-	/* Away */
-	/* Goes first in the list as
-	 * purple picks the first status with the AWAY type
-	 * for idle.
+	/* 11: Urgent interruptions only */
+	SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE,
+			SIPE_ACTIVITY_URGENT_ONLY,
+			FALSE);
+
+	/*
+	 * Status list entries for primitive type AWAY
+	 *
+	 * 12: Away - special case: needs to go first in the list as purple
+	 *            picks the first status with primitive type AWAY for idle
 	 */
 	SIPE_ADD_STATUS(PURPLE_STATUS_AWAY,
-			NULL,
-			NULL,
+			SIPE_ACTIVITY_AWAY,
+			TRUE);
+
+	/* 13: Be Right Back */
+	SIPE_ADD_STATUS(PURPLE_STATUS_AWAY,
+			SIPE_ACTIVITY_BRB,
 			TRUE);
 
-	/* Be Right Back */
+	/* 14: Out to lunch */
 	SIPE_ADD_STATUS(PURPLE_STATUS_AWAY,
-			sipe_purple_activity_to_token(SIPE_ACTIVITY_BRB),
-			sipe_core_activity_description(SIPE_ACTIVITY_BRB),
+			SIPE_ACTIVITY_LUNCH,
+			FALSE);
+
+	/*
+	 * Status list entries for primitive type EXTENDED_AWAY
+	 *
+	 * 15: Out of office */
+	SIPE_ADD_STATUS(PURPLE_STATUS_EXTENDED_AWAY,
+			SIPE_ACTIVITY_OOF,
+			FALSE);
+
+	/*
+	 * Status list entries for primitive type INVISIBLE
+	 *
+	 * 16: Appear Offline */
+	SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE,
+			SIPE_ACTIVITY_INVISIBLE,
 			TRUE);
 
-	/* Appear Offline */
-	SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE,
-			NULL,
-			NULL,
-			TRUE);
-
-	/* Offline */
+	/*
+	 * Status list entries for primitive type OFFLINE
+	 *
+	 * NOTE: this is always the last entry. Compare the number
+	 *       with the comment in activity_to_purple_map[].
+	 *
+	 * 17: Offline - special case: no message text */
 	type = purple_status_type_new(PURPLE_STATUS_OFFLINE,
 				      NULL,
 				      NULL,
 				      TRUE);
 	types = g_list_append(types, type);
 
 	return types;
 }
@@ -282,22 +346,25 @@ static GList *sipe_purple_blist_node_men
 		return NULL;
 	}
 }
 
 static guint get_authentication_type(PurpleAccount *account)
 {
 	const gchar *auth = purple_account_get_string(account, "authentication", "ntlm");
 
-	/* map option list to type - default is NTLM */
-	guint authentication_type = SIPE_AUTHENTICATION_TYPE_NTLM;
+	/* map option list to type - default is automatic */
+	guint authentication_type = SIPE_AUTHENTICATION_TYPE_AUTOMATIC;
+	if (sipe_strequal(auth, "ntlm")) {
+		authentication_type = SIPE_AUTHENTICATION_TYPE_NTLM;
+	} else
 #if PURPLE_SIPE_SSO_AND_KERBEROS
 	if (sipe_strequal(auth, "krb5")) {
 		authentication_type = SIPE_AUTHENTICATION_TYPE_KERBEROS;
-	}
+	} else
 #endif
 	if (sipe_strequal(auth, "tls-dsk")) {
 		authentication_type = SIPE_AUTHENTICATION_TYPE_TLS_DSK;
 	}
 
 	return(authentication_type);
 }
 
@@ -326,56 +393,31 @@ static void connect_to_core(PurpleConnec
 			    const gchar *password)
 {
 	const gchar *username  = purple_account_get_username(account);
 	const gchar *email     = purple_account_get_string(account, "email", NULL);
 	const gchar *email_url = purple_account_get_string(account, "email_url", NULL);
 	const gchar *transport = purple_account_get_string(account, "transport", "auto");
 	struct sipe_core_public *sipe_public;
 	gchar **username_split;
-	gchar *login_domain = NULL;
-	gchar *login_account = NULL;
 	const gchar *errmsg;
 	guint transport_type;
 	struct sipe_backend_private *purple_private;
-	gboolean sso = get_sso_flag(account);
 
 	/* username format: <username>,[<optional login>] */
 	SIPE_DEBUG_INFO("sipe_purple_login: username '%s'", username);
 	username_split = g_strsplit(username, ",", 2);
 
-	/* login name is ignored when SSO has been selected */
-	if (!sso) {
-		/* login name specified? */
-		if (username_split[1] && strlen(username_split[1])) {
-			/* Allowed domain-account separators are / or \ */
-			gchar **domain_user = g_strsplit_set(username_split[1], "/\\", 2);
-			gboolean has_domain = domain_user[1] != NULL;
-			SIPE_DEBUG_INFO("sipe_purple_login: login '%s'", username_split[1]);
-			login_domain  = has_domain ? g_strdup(domain_user[0]) : NULL;
-			login_account = g_strdup(domain_user[has_domain ? 1 : 0]);
-			SIPE_DEBUG_INFO("sipe_purple_login: auth domain '%s' user '%s'",
-					login_domain ? login_domain : "",
-					login_account);
-			g_strfreev(domain_user);
-		} else {
-			/* No -> duplicate username */
-			login_account = g_strdup(username_split[0]);
-		}
-	}
-
 	sipe_public = sipe_core_allocate(username_split[0],
-					 sso,
-					 login_domain, login_account,
+					 get_sso_flag(account),
+					 username_split[1],
 					 password,
 					 email,
 					 email_url,
 					 &errmsg);
-	g_free(login_domain);
-	g_free(login_account);
 	g_strfreev(username_split);
 
 	if (!sipe_public) {
 		purple_connection_error(gc,
 					PURPLE_CONNECTION_ERROR_INVALID_USERNAME,
 					errmsg);
 		return;
 	}
@@ -418,31 +460,37 @@ static void connect_to_core(PurpleConnec
 					username_split[0],
 					username_split[0] ? username_split[1] : NULL);
 	g_strfreev(username_split);
 }
 
 static void password_required_cb(PurpleConnection *gc,
 				 SIPE_UNUSED_PARAMETER PurpleRequestFields *fields)
 {
-        if (!PURPLE_CONNECTION_IS_VALID(gc))
-                return;
+#if !PURPLE_VERSION_CHECK(3,0,0)
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		return;
+	}
+#endif
 
 	purple_connection_error(gc,
 				PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
 				_("Password required"));
 }
 
 static void password_ok_cb(PurpleConnection *gc,
 			   PurpleRequestFields *fields)
 {
 	const gchar *password;
 
-        if (!PURPLE_CONNECTION_IS_VALID(gc))
-                return;
+#if !PURPLE_VERSION_CHECK(3,0,0)
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		return;
+	}
+#endif
 
 	password = purple_request_fields_get_string(fields, "password");
 
 	if (password && strlen(password)) {
 		PurpleAccount *account = purple_connection_get_account(gc);
 
 		if (purple_request_fields_get_bool(fields, "remember"))
 			purple_account_set_remember_password(account, TRUE);
@@ -491,29 +539,44 @@ static void sipe_purple_close(PurpleConn
 
 		/* anything left after that must be in pending state... */
 		sipe_purple_dns_query_cancel_all(purple_private);
 		sipe_purple_transport_close_all(purple_private);
 
 		if (purple_private->roomlist_map)
 			g_hash_table_destroy(purple_private->roomlist_map);
 		sipe_purple_chat_destroy_rejoin(purple_private);
+
+		if (purple_private->deferred_status_timeout)
+			purple_timeout_remove(purple_private->deferred_status_timeout);
+		g_free(purple_private->deferred_status_note);
+
 		g_free(purple_private);
 		purple_connection_set_protocol_data(gc, NULL);
 	}
 }
 
+#if PURPLE_VERSION_CHECK(3,0,0)
+static int sipe_purple_send_im(PurpleConnection *gc, PurpleMessage *msg)
+{
+	sipe_core_im_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC,
+			purple_message_get_recipient(msg),
+			purple_message_get_contents(msg));
+	return 1;
+}
+#else
 static int sipe_purple_send_im(PurpleConnection *gc,
 			       const char *who,
 			       const char *what,
 			       SIPE_UNUSED_PARAMETER PurpleMessageFlags flags)
 {
 	sipe_core_im_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, who, what);
 	return 1;
 }
+#endif
 
 static unsigned int sipe_purple_send_typing(PurpleConnection *gc,
 					    const char *who,
 					    PurpleIMTypingState state)
 {
 	gboolean typing = (state == PURPLE_IM_TYPING);
 
 	/* only enable this debug output while testing
@@ -631,18 +694,18 @@ static PurpleMediaCaps sipe_purple_get_m
  * it. But that means that the compilation of this structure can fail if the
  * newer API has added additional plugin callbacks. For the benefit of the
  * user we downgrade it to a warning here.
  *
  * Diagnostic #pragma was added in GCC 4.2.0
  * Diagnostic push/pop was added in GCC 4.6.0
  */
 #ifdef __GNUC__
-#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 2)
-#if __GNUC_MINOR__ >= 6
+#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 5)
+#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ >= 5)
 #pragma GCC diagnostic push
 #endif
 #pragma GCC diagnostic warning "-Wmissing-field-initializers"
 #endif
 #endif
 static PurplePluginProtocolInfo sipe_prpl_info =
 {
 #if PURPLE_VERSION_CHECK(3,0,0)
@@ -679,17 +742,19 @@ static PurplePluginProtocolInfo sipe_prp
 	sipe_purple_add_deny,			/* rem_permit */
 	sipe_purple_add_permit,			/* rem_deny */
 	NULL,					/* set_permit_deny */
 	sipe_purple_chat_join,			/* join_chat */
 	NULL,					/* reject_chat */
 	NULL,					/* get_chat_name */
 	sipe_purple_chat_invite,		/* chat_invite */
 	sipe_purple_chat_leave,			/* chat_leave */
+#if !PURPLE_VERSION_CHECK(3,0,0)
 	NULL,					/* chat_whisper */
+#endif
 	sipe_purple_chat_send,			/* chat_send */
 	NULL,					/* keepalive */
 	NULL,					/* register_user */
 	NULL,					/* get_cb_info */	// deprecated
 #if !PURPLE_VERSION_CHECK(3,0,0)
 	NULL,					/* get_cb_away */	// deprecated
 #endif
 	sipe_purple_alias_buddy,		/* alias_buddy */
@@ -737,23 +802,24 @@ static PurplePluginProtocolInfo sipe_prp
 	NULL,					/* get_moods */
 	NULL,					/* set_public_alias */
 	NULL,					/* get_public_alias */
 #if PURPLE_VERSION_CHECK(2,8,0)
 	NULL,					/* add_buddy_with_invite */
 	NULL,					/* add_buddies_with_invite */
 #elif PURPLE_VERSION_CHECK(3,0,0)
 	NULL,					/* get_max_message_size */
+	NULL,					/* media_send_dtmf */
 #endif
 #endif
 #endif
 #endif
 };
 #ifdef __GNUC__
-#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6)
+#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ >= 5)
 #pragma GCC diagnostic pop
 #endif
 #endif
 /* Original GCC error checking restored from here on... (see above) */
 
 /* PurplePluginInfo function calls & data structure */
 static gboolean sipe_purple_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin *plugin)
 {
@@ -802,99 +868,16 @@ static void sipe_purple_plugin_destroy(S
 static void sipe_purple_show_about_plugin(PurplePluginAction *action)
 {
 	gchar *tmp = sipe_core_about();
 	purple_notify_formatted((PurpleConnection *) action->context,
 				NULL, " ", NULL, tmp, NULL, NULL);
 	g_free(tmp);
 }
 
-static void sipe_purple_find_contact_cb(PurpleConnection *gc,
-					PurpleRequestFields *fields)
-{
-	GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data);
-	const gchar *given_name = NULL;
-	const gchar *surname    = NULL;
-	const gchar *email      = NULL;
-	const gchar *company    = NULL;
-	const gchar *country    = NULL;
-
-	while (entries) {
-		PurpleRequestField *field = entries->data;
-		const char *id = purple_request_field_get_id(field);
-		const char *value = purple_request_field_string_get_value(field);
-
-		SIPE_DEBUG_INFO("sipe_purple_find_contact_cb: %s = '%s'", id, value ? value : "");
-
-		if (value) {
-			if (strcmp(id, "given") == 0) {
-				given_name = value;
-			} else if (strcmp(id, "surname") == 0) {
-				surname = value;
-			} else if (strcmp(id, "email") == 0) {
-				email = value;
-			} else if (strcmp(id, "company") == 0) {
-				company = value;
-			} else if (strcmp(id, "country") == 0) {
-				country = value;
-			}
-		}
-
-		entries = g_list_next(entries);
-	};
-
-	sipe_core_buddy_search(PURPLE_GC_TO_SIPE_CORE_PUBLIC,
-			       NULL,
-			       given_name,
-			       surname,
-			       email,
-			       company,
-			       country);
-}
-
-static void sipe_purple_show_find_contact(PurplePluginAction *action)
-{
-	PurpleConnection *gc = (PurpleConnection *) action->context;
-	PurpleRequestFields *fields;
-	PurpleRequestFieldGroup *group;
-	PurpleRequestField *field;
-#if PURPLE_VERSION_CHECK(3,0,0)
-	PurpleRequestCommonParameters *cpar = purple_request_cpar_from_connection(gc);
-#endif
-
-	fields = purple_request_fields_new();
-	group = purple_request_field_group_new(NULL);
-	purple_request_fields_add_group(fields, group);
-
-	field = purple_request_field_string_new("given", _("First name"), NULL, FALSE);
-	purple_request_field_group_add_field(group, field);
-	field = purple_request_field_string_new("surname", _("Last name"), NULL, FALSE);
-	purple_request_field_group_add_field(group, field);
-	field = purple_request_field_string_new("email", _("Email"), NULL, FALSE);
-	purple_request_field_group_add_field(group, field);
-	field = purple_request_field_string_new("company", _("Company"), NULL, FALSE);
-	purple_request_field_group_add_field(group, field);
-	field = purple_request_field_string_new("country", _("Country"), NULL, FALSE);
-	purple_request_field_group_add_field(group, field);
-
-	purple_request_fields(gc,
-			      _("Search"),
-			      _("Search for a contact"),
-			      _("Enter the information for the person you wish to find. Empty fields will be ignored."),
-			      fields,
-			      _("_Search"), G_CALLBACK(sipe_purple_find_contact_cb),
-			      _("_Cancel"), NULL,
-#if PURPLE_VERSION_CHECK(3,0,0)
-			      cpar, gc);
-	purple_request_cpar_unref(cpar);
-#else
-			      purple_connection_get_account(gc), NULL, NULL, gc);
-#endif
-}
-
 static void sipe_purple_join_conference_cb(PurpleConnection *gc,
 					   PurpleRequestFields *fields)
 {
 	GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data);
 
 	if (entries) {
 		PurpleRequestField *field = entries->data;
 		const char *id = purple_request_field_get_id(field);
@@ -927,58 +910,52 @@ static void sipe_purple_phone_call_cb(Pu
 }
 
 static void sipe_purple_phone_call(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *group;
 	PurpleRequestField *field;
-#if PURPLE_VERSION_CHECK(3,0,0)
-	PurpleRequestCommonParameters *cpar = purple_request_cpar_from_connection(gc);
-#endif
 
 	fields = purple_request_fields_new();
 	group = purple_request_field_group_new(NULL);
 	purple_request_fields_add_group(fields, group);
 
 	field = purple_request_field_string_new("phoneNumber", _("Phone number"), NULL, FALSE);
 	purple_request_field_group_add_field(group, field);
 
 	purple_request_fields(gc,
 			      _("Call a phone number"),
 			      _("Call a phone number"),
 			      NULL,
 			      fields,
 			      _("_Call"), G_CALLBACK(sipe_purple_phone_call_cb),
 			      _("_Cancel"), NULL,
 #if PURPLE_VERSION_CHECK(3,0,0)
-			      cpar, gc);
-	purple_request_cpar_unref(cpar);
+			      purple_request_cpar_from_connection(gc),
 #else
-			      purple_connection_get_account(gc), NULL, NULL, gc);
+			      purple_connection_get_account(gc), NULL, NULL,
 #endif
+			      gc);
 }
 
 static void sipe_purple_test_call(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	sipe_core_media_test_call(PURPLE_GC_TO_SIPE_CORE_PUBLIC);
 }
 #endif
 
 static void sipe_purple_show_join_conference(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *group;
 	PurpleRequestField *field;
-#if PURPLE_VERSION_CHECK(3,0,0)
-	PurpleRequestCommonParameters *cpar = purple_request_cpar_from_connection(gc);
-#endif
 
 	fields = purple_request_fields_new();
 	group = purple_request_field_group_new(NULL);
 	purple_request_fields_add_group(fields, group);
 
 	field = purple_request_field_string_new("meetingLocation", _("Meeting location"), NULL, FALSE);
 	purple_request_field_group_add_field(group, field);
 
@@ -991,21 +968,21 @@ static void sipe_purple_show_join_confer
 				"meet:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n"
 				"conf:sip:someone@company.com;gruu;opaque=app:conf:focus:id:abcdef1234\n"
 				"or\n"
 				"https://meet.company.com/someone/abcdef1234"),
 			      fields,
 			      _("_Join"), G_CALLBACK(sipe_purple_join_conference_cb),
 			      _("_Cancel"), NULL,
 #if PURPLE_VERSION_CHECK(3,0,0)
-			      cpar, gc);
-	purple_request_cpar_unref(cpar);
+			      purple_request_cpar_from_connection(gc),
 #else
-			      purple_connection_get_account(gc), NULL, NULL, gc);
+			      purple_connection_get_account(gc), NULL, NULL,
 #endif
+			      gc);
 }
 
 static void sipe_purple_republish_calendar(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	PurpleAccount *account = purple_connection_get_account(gc);
 
 	if (get_dont_publish_flag(account)) {
@@ -1130,16 +1107,17 @@ static void sipe_purple_init_plugin(Purp
 
 	/*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
 	sipe_prpl_info.protocol_options = g_list_append(sipe_prpl_info.protocol_options, option);*/
 
 	option = purple_account_option_string_new(_("User Agent"), "useragent", "");
 	sipe_prpl_info.protocol_options = g_list_append(sipe_prpl_info.protocol_options, option);
 
 	option = purple_account_option_list_new(_("Authentication scheme"), "authentication", NULL);
+	purple_account_option_add_list_item(option, _("Auto"), "auto");
 	purple_account_option_add_list_item(option, _("NTLM"), "ntlm");
 #if PURPLE_SIPE_SSO_AND_KERBEROS
 	purple_account_option_add_list_item(option, _("Kerberos"), "krb5");
 #endif
 	purple_account_option_add_list_item(option, _("TLS-DSK"), "tls-dsk");
 	sipe_prpl_info.protocol_options = g_list_append(sipe_prpl_info.protocol_options, option);
 
 #if PURPLE_SIPE_SSO_AND_KERBEROS
@@ -1183,16 +1161,25 @@ static void sipe_purple_init_plugin(Purp
 	purple_account_option_string_set_masked(option, TRUE);
 	sipe_prpl_info.protocol_options = g_list_append(sipe_prpl_info.protocol_options, option);
 
 	/** Example (federated domain): company.com      (i.e. ocschat@company.com)
 	 *  Example (non-default user): user@company.com
 	 */
 	option = purple_account_option_string_new(_("Group Chat Proxy\n   company.com  or  user@company.com\n(leave empty to determine from Username)"), "groupchat_user", "");
 	sipe_prpl_info.protocol_options = g_list_append(sipe_prpl_info.protocol_options, option);
+
+#ifdef HAVE_SRTP
+	option = purple_account_option_list_new(_("Media encryption"), "encryption-policy", NULL);
+	purple_account_option_add_list_item(option, _("Obey server policy"), "obey-server");
+	purple_account_option_add_list_item(option, _("Always"), "required");
+	purple_account_option_add_list_item(option, _("Optional"), "optional");
+	purple_account_option_add_list_item(option, _("Disabled"), "disabled");
+	sipe_prpl_info.protocol_options = g_list_append(sipe_prpl_info.protocol_options, option);
+#endif
 }
 
 /* This macro makes the code a purple plugin */
 PURPLE_INIT_PLUGIN(sipe, sipe_purple_init_plugin, sipe_purple_info);
 
 /*
   Local Variables:
   mode: c
--- a/libpurple/protocols/sipe/purple-private.h
+++ b/libpurple/protocols/sipe/purple-private.h
@@ -1,14 +1,14 @@
 /**
  * @file purple-private.h
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,39 +18,54 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "version.h"
 
 /* Forward declarations */
+struct sipe_chat_session;
 struct sipe_core_public;
 struct _PurpleAccount;
 struct _PurpleBuddy;
 struct _PurpleChat;
 struct _PurpleConnection;
+struct _PurpleConversation;
 struct _PurpleGroup;
+struct _PurpleMessage;
+struct _PurplePluginAction;
 struct _PurpleRoomlist;
 struct _PurpleStatus;
 struct _PurpleXfer;
 
 #ifndef _PurpleMessageFlags
 #define _PurpleMessageFlags int
 #endif
 
 struct sipe_backend_private {
 	struct sipe_core_public *public;
 	struct _PurpleConnection *gc;
 	struct _PurpleAccount *account;
 	struct _PurpleRoomlist *roomlist;
+	/* see sipe_backend_chat_create() */
+	struct sipe_chat_session *adium_chat_session;
 	GHashTable *roomlist_map; /* name -> uri */
 	GList *rejoin_chats;
 	GSList *transports;
 	GSList *dns_queries;
+
+	/* work around broken libpurple idle notification */
+	gchar *deferred_status_note;
+	guint  deferred_status_activity;
+	guint  deferred_status_timeout;
+
+	/* flags */
+	gboolean status_changed_by_core; /* status changed by core */
+	gboolean user_is_not_idle;       /* user came back online */
 };
 
 struct sipe_backend_fd {
 	int fd;
 };
 
 /* Status attributes */
 #define SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE "message"
@@ -80,27 +95,32 @@ void sipe_purple_ft_send_file(struct _Pu
  * @param who remote participant in the file transfer session
  */
 struct _PurpleXfer *sipe_purple_ft_new_xfer(struct _PurpleConnection *gc,
 					    const char *who);
 
 /* libpurple chat callbacks */
 #define SIPE_PURPLE_COMPONENT_KEY_CONVERSATION "_conv"
 
+struct sipe_chat_session *sipe_purple_chat_get_session(struct _PurpleConversation *conv);
 void sipe_purple_chat_setup_rejoin(struct sipe_backend_private *purple_private);
 void sipe_purple_chat_destroy_rejoin(struct sipe_backend_private *purple_private);
 void sipe_purple_chat_invite(struct _PurpleConnection *gc,
 			     int id,
 			     const char *message,
 			     const char *name);
 void sipe_purple_chat_leave(struct _PurpleConnection *gc, int id);
 int sipe_purple_chat_send(struct _PurpleConnection *gc,
 			  int id,
+#if PURPLE_VERSION_CHECK(3,0,0)
+			  struct _PurpleMessage *msg);
+#else
 			  const char *what,
 			  _PurpleMessageFlags flags);
+#endif
 GList *sipe_purple_chat_menu(struct _PurpleChat *chat);
 
 /* libpurple chat room callbacks */
 GList *sipe_purple_chat_info(struct _PurpleConnection *gc);
 GHashTable *sipe_purple_chat_info_defaults(struct _PurpleConnection *gc,
 					   const char *chat_name);
 void sipe_purple_chat_join(struct _PurpleConnection *gc, GHashTable *data);
 struct _PurpleRoomlist *sipe_purple_roomlist_get_list(struct _PurpleConnection *gc);
@@ -120,16 +140,19 @@ void sipe_purple_remove_buddy(struct _Pu
 			      struct _PurpleBuddy *buddy,
 			      struct _PurpleGroup *group);
 void sipe_purple_group_buddy(struct _PurpleConnection *gc,
 			     const char *who,
 			     const char *old_group_name,
 			     const char *new_group_name);
 GList *sipe_purple_buddy_menu(struct _PurpleBuddy *buddy);
 
+/* libpurple search callbacks */
+void sipe_purple_show_find_contact(struct _PurplePluginAction *action);
+
 /* libpurple status callbacks */
 void sipe_purple_set_status(struct _PurpleAccount *account,
 			    struct _PurpleStatus *status);
 void sipe_purple_set_idle(struct _PurpleConnection *gc,
 			  int interval);
 
 /* media */
 void capture_pipeline(const gchar *label);
--- a/libpurple/protocols/sipe/purple-search.c
+++ b/libpurple/protocols/sipe/purple-search.c
@@ -1,14 +1,14 @@
 /**
  * @file purple-search.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2014 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,19 +19,22 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
+#include <string.h>
+
 #include <glib.h>
 
 #include "notify.h"
+#include "request.h"
 
 #include "version.h"
 #if PURPLE_VERSION_CHECK(3,0,0)
 #include "conversations.h"
 #endif
 
 #include "sipe-common.h"
 #include "sipe-backend.h"
@@ -146,16 +149,102 @@ void sipe_backend_search_results_finaliz
 				    NULL,
 				    description,
 				    r,
 				    NULL,
 				    NULL);
 
 }
 
+static void sipe_purple_find_contact_cb(PurpleConnection *gc,
+					PurpleRequestFields *fields)
+{
+	GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data);
+	const gchar *given_name = NULL;
+	const gchar *surname    = NULL;
+	const gchar *email      = NULL;
+	const gchar *sipid      = NULL;
+	const gchar *company    = NULL;
+	const gchar *country    = NULL;
+
+	while (entries) {
+		PurpleRequestField *field = entries->data;
+		const char *id = purple_request_field_get_id(field);
+		const char *value = purple_request_field_string_get_value(field);
+
+		SIPE_DEBUG_INFO("sipe_purple_find_contact_cb: %s = '%s'", id, value ? value : "");
+
+		if (value && strlen(value)) {
+			if (strcmp(id, "given") == 0) {
+				given_name = value;
+			} else if (strcmp(id, "surname") == 0) {
+				surname = value;
+			} else if (strcmp(id, "email") == 0) {
+				email = value;
+			} else if (strcmp(id, "sipid") == 0) {
+				sipid = value;
+			} else if (strcmp(id, "company") == 0) {
+				company = value;
+			} else if (strcmp(id, "country") == 0) {
+				country = value;
+			}
+		}
+
+		entries = g_list_next(entries);
+	};
+
+	sipe_core_buddy_search(PURPLE_GC_TO_SIPE_CORE_PUBLIC,
+			       NULL,
+			       given_name,
+			       surname,
+			       email,
+			       sipid,
+			       company,
+			       country);
+}
+
+void sipe_purple_show_find_contact(PurplePluginAction *action)
+{
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+
+	field = purple_request_field_string_new("given", _("First name"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("surname", _("Last name"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("email", _("Email"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("sipid", _("SIP ID"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("company", _("Company"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("country", _("Country"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+
+	purple_request_fields(gc,
+			      _("Search"),
+			      _("Search for a contact"),
+			      _("Enter the information for the person you wish to find. Empty fields will be ignored."),
+			      fields,
+			      _("_Search"), G_CALLBACK(sipe_purple_find_contact_cb),
+			      _("_Cancel"), NULL,
+#if PURPLE_VERSION_CHECK(3,0,0)
+			      purple_request_cpar_from_connection(gc),
+#else
+			      purple_connection_get_account(gc), NULL, NULL,
+#endif
+			      gc);
+}
+
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
   tab-width: 8
   End:
 */
--- a/libpurple/protocols/sipe/purple-status.c
+++ b/libpurple/protocols/sipe/purple-status.c
@@ -1,14 +1,14 @@
 /**
  * @file purple-status.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -42,34 +42,32 @@ guint sipe_backend_status(struct sipe_co
 
 gboolean sipe_backend_status_changed(struct sipe_core_public *sipe_public,
 				     guint activity,
 				     const gchar *message)
 {
 #if 0
 /* This is only used by sipe_backend_status_and_note and requires savedstatus.h,
    which is Pidgin specific. */
-	struct sipe_backend_private *purple_private = sipe_public->backend_private;
-	PurpleStatus *status = purple_account_get_active_status(purple_private->account);
-	const gchar *status_id = sipe_purple_activity_to_token(activity);
-	gboolean changed = TRUE;
+	gboolean result = FALSE;
 
-	if (g_str_equal(status_id, purple_status_get_id(status)) &&
-	    sipe_strequal(message,
-			  purple_status_get_attr_string(status,
-							SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE)))
-	{
-		changed = FALSE;
+	if ((activity == SIPE_ACTIVITY_AWAY) && purple_savedstatus_is_idleaway()) {
+		SIPE_DEBUG_INFO_NOFORMAT("sipe_backend_status_changed: user is already idle-away");
+	} else {
+		struct sipe_backend_private *purple_private = sipe_public->backend_private;
+		PurpleStatus *status = purple_account_get_active_status(purple_private->account);
+		const gchar *status_id = sipe_purple_activity_to_token(activity);
+
+		result = !(g_str_equal(status_id, purple_status_get_id(status)) &&
+			   sipe_strequal(message,
+					 purple_status_get_attr_string(status,
+								       SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE)));
 	}
 
-	if (purple_savedstatus_is_idleaway()) {
-		changed = FALSE;
-	}
-
-	return(changed);
+	return(result);
 #else
 	return FALSE;
 #endif
 }
 
 /**
  * This method motivates Purple's Host (e.g. Pidgin) to update its UI
  * by using standard Purple's means of signals and saved statuses.
@@ -89,67 +87,155 @@ void sipe_backend_status_and_note(struct
 	PurpleAccount *account = purple_private->account;
 	const gchar *status_id = sipe_purple_activity_to_token(activity);
 	PurpleSavedStatus *saved_status;
 	const PurpleStatusType *acct_status_type =
 		purple_status_type_find_with_id(purple_account_get_status_types(account),
 						status_id);
 	PurpleStatusPrimitive primitive = purple_status_type_get_primitive(acct_status_type);
 
+	/* code adapted from: pidgin/gtkstatusbox.c */
 	saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, message);
 	if (saved_status) {
 		purple_savedstatus_set_substatus(saved_status, account, acct_status_type, message);
-	}
+	} else {
+		/* This type+message is unique then create a new transient saved status */
+		GList *entry;
+		GList *active_accts = purple_accounts_get_all_active();
 
-	/* If this type+message is unique then create a new transient saved status
-	 * Ref: gtkstatusbox.c
-	 */
-	if (!saved_status) {
-		GList *tmp;
-		GList *active_accts = purple_accounts_get_all_active();
+		SIPE_DEBUG_INFO("sipe_backend_status_and_note: creating new saved status %s '%s'",
+				status_id, message ? message : "(null)");
 
 		saved_status = purple_savedstatus_new(NULL, primitive);
 		purple_savedstatus_set_message(saved_status, message);
 
-		for (tmp = active_accts; tmp != NULL; tmp = tmp->next) {
+		for (entry = active_accts; entry != NULL; entry = entry->next)
 			purple_savedstatus_set_substatus(saved_status,
-							 (PurpleAccount *)tmp->data, acct_status_type, message);
-		}
+							 (PurpleAccount *) entry->data,
+							 acct_status_type,
+							 message);
 		g_list_free(active_accts);
 	}
 
 	/* Set the status for each account */
+	purple_private->status_changed_by_core = TRUE;
 	purple_savedstatus_activate(saved_status);
 #endif
 }
 
+/**
+ * Work around broken libpurple idle notification
+ *
+ * (1) user changes the status
+ *      sipe_purple_set_status()
+ *      -> user changed state
+ *
+ * (2) client detects that user is idle
+ *      sipe_purple_set_status()      [sometimes omitted?!?!?]
+ *      sipe_purple_set_idle( != 0 )
+ *      -> machine changed state
+ *
+ * (3) client detects that user is no longer idle
+ *      sipe_purple_set_idle(0)
+ *      sipe_purple_set_status()
+ *      -> user changed state
+ *
+ * (4) core sends a status change
+ *      sipe_backend_status_and_note()
+ *      purple_savedstatus_activate()
+ *      sipe_purple_set_status()
+ *      -> status change must be ignored
+ *
+ * Cases (1) and (2) can only be differentiated by deferring the update.
+ */
+static void sipe_purple_status_deferred_update(struct sipe_backend_private *purple_private,
+					       gboolean changed_by_user)
+{
+	gchar *note = purple_private->deferred_status_note;
+
+	purple_private->deferred_status_note    = NULL;
+	purple_private->deferred_status_timeout = 0;
+
+	sipe_core_status_set(purple_private->public,
+			     changed_by_user,
+			     purple_private->deferred_status_activity,
+			     note);
+	g_free(note);
+}
+
+static gboolean sipe_purple_status_timeout(gpointer data)
+{
+	/* timeout expired -> no idle indication -> state changed by user */
+	sipe_purple_status_deferred_update(data, TRUE);
+	return(FALSE);
+}
+
 void sipe_purple_set_status(PurpleAccount *account,
 			    PurpleStatus *status)
 {
-	SIPE_DEBUG_INFO("sipe_purple_set_status[CB]: status=%s",
-			purple_status_get_id(status));
+	if (purple_account_get_connection(account) &&
+	    purple_status_is_active(status)) {
+		struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC;
+		struct sipe_backend_private *purple_private = sipe_public->backend_private;
+		const gchar *status_id = purple_status_get_id(status);
+		guint activity = sipe_purple_token_to_activity(status_id);
+		const gchar *note = purple_status_get_attr_string(status,
+								  SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE);
 
-	if (!purple_status_is_active(status))
-		return;
+		SIPE_DEBUG_INFO("sipe_purple_set_status[CB]: '%s'",
+				status_id);
+
+		if (purple_private->status_changed_by_core) {
+			SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_set_status[CB]: triggered by core - ignoring");
 
-	if (purple_account_get_connection(account)) {
-		const gchar *status_id = purple_status_get_id(status);
-		const gchar *note      = purple_status_get_attr_string(status,
-								       SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE);
-		sipe_core_status_set(PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC,
-				     sipe_purple_token_to_activity(status_id),
-				     note);
+		} else if (purple_private->user_is_not_idle) {
+			sipe_core_status_set(sipe_public,
+					     TRUE,
+					     activity,
+					     note);
+
+		} else {
+			if (purple_private->deferred_status_timeout)
+				purple_timeout_remove(purple_private->deferred_status_timeout);
+			g_free(purple_private->deferred_status_note);
+
+			SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_set_status[CB]: defer status update");
+
+			purple_private->deferred_status_note     = g_strdup(note);
+			purple_private->deferred_status_activity = activity;
+			purple_private->deferred_status_timeout  = purple_timeout_add_seconds(1,
+											      sipe_purple_status_timeout,
+											      purple_private);
+		}
+
+		/* reset flags */
+		purple_private->status_changed_by_core = FALSE;
+		purple_private->user_is_not_idle       = FALSE;
 	}
 }
 
 void sipe_purple_set_idle(PurpleConnection *gc,
 			  int interval)
 {
-	SIPE_DEBUG_INFO("sipe_purple_set_idle[CB]: interval=%d", interval);
-	if (gc) sipe_core_status_idle(PURPLE_GC_TO_SIPE_CORE_PUBLIC);
+	if (gc) {
+		struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC;
+		struct sipe_backend_private *purple_private = sipe_public->backend_private;
+
+		purple_private->user_is_not_idle = interval == 0;
+
+		SIPE_DEBUG_INFO("sipe_purple_set_idle[CB]: user is %sidle",
+				purple_private->user_is_not_idle ? "not " : "");
+
+		if (!purple_private->user_is_not_idle) {
+			/* timeout not expired -> state changed by machine */
+			if (purple_private->deferred_status_timeout)
+				purple_timeout_remove(purple_private->deferred_status_timeout);
+			sipe_purple_status_deferred_update(purple_private, FALSE);
+		}
+	}
 }
 
 /*
   Local Variables:
   mode: c
   c-file-style: "bsd"
   indent-tabs-mode: t
   tab-width: 8
--- a/libpurple/protocols/sipe/purple-transport.c
+++ b/libpurple/protocols/sipe/purple-transport.c
@@ -1,14 +1,14 @@
 /**
  * @file purple-transport.c
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -267,17 +267,25 @@ sipe_backend_transport_connect(struct si
 				     _("Could not create SSL context"));
 			sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
 			return(NULL);
 		}
 	} else if (setup->type == SIPE_TRANSPORT_TCP) {
 		/* TCP case */
 		SIPE_DEBUG_INFO_NOFORMAT("using TCP");
 
-		if ((transport->proxy = purple_proxy_connect(gc, account,
+		/*
+		 * NOTE: during shutdown libpurple calls
+		 *
+		 *    purple_proxy_connect_cancel_with_handle(gc);
+		 *
+		 * before our cleanup code. Therefore we can't use "gc" as
+		 * handle. We are not using it for anything thus NULL is fine.
+		 */
+		if ((transport->proxy = purple_proxy_connect(NULL, account,
 							     setup->server_name,
 							     setup->server_port,
 							     transport_tcp_connected,
 							     transport)) == NULL) {
 			setup->error(SIPE_TRANSPORT_CONNECTION,
 				     _("Could not create socket"));
 			sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
 			return(NULL);
--- a/libpurple/protocols/sipe/purple-user.c
+++ b/libpurple/protocols/sipe/purple-user.c
@@ -17,76 +17,72 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include <glib.h>
 
+#include "server.h"
 #include "request.h"
-#include "server.h"
 
 #include "purple-private.h"
 
 #if PURPLE_VERSION_CHECK(3,0,0)
 #else
+#define purple_serv_got_typing(c, n, t, s)	serv_got_typing(c, n, t, s)
+#define purple_serv_got_typing_stopped(c, n)	serv_got_typing_stopped(c, n)
 #define PURPLE_IM_TYPING PURPLE_TYPING
 #endif
 
 #include "sipe-backend.h"
 #include "sipe-core.h"
 
 #define SIPE_TYPING_RECV_TIMEOUT 6
 
 void sipe_backend_user_feedback_typing(struct sipe_core_public *sipe_public,
 				       const gchar *from)
 {
 	struct sipe_backend_private *purple_private = sipe_public->backend_private;
-	serv_got_typing(purple_private->gc, from,
-			SIPE_TYPING_RECV_TIMEOUT,
-			PURPLE_IM_TYPING);
+	purple_serv_got_typing(purple_private->gc, from,
+			       SIPE_TYPING_RECV_TIMEOUT,
+			       PURPLE_IM_TYPING);
 }
 
 void sipe_backend_user_feedback_typing_stop(struct sipe_core_public *sipe_public,
 					    const gchar *from)
 {
 	struct sipe_backend_private *purple_private = sipe_public->backend_private;
-	serv_got_typing_stopped(purple_private->gc, from);
+	purple_serv_got_typing_stopped(purple_private->gc, from);
 }
 
 static void ask_cb(gpointer key, int choice)
 {
 	sipe_core_user_ask_cb(key, choice == 1);
 }
 
 void sipe_backend_user_ask(struct sipe_core_public *sipe_public,
 			   const gchar *message,
 			   const gchar *accept_label,
 			   const gchar *decline_label,
 			   gpointer key)
 {
 	struct sipe_backend_private *purple_private = sipe_public->backend_private;
-#if PURPLE_VERSION_CHECK(3,0,0)
-	PurpleRequestCommonParameters *cpar = purple_request_cpar_from_account(purple_private->account);
-#endif
 
 	purple_request_action(key, "Office Communicator", message,
 			      NULL, 0,
 #if PURPLE_VERSION_CHECK(3,0,0)
-			      cpar,
+			      purple_request_cpar_from_account(purple_private->account),
 #else
 			      purple_private->account, NULL, NULL,
 #endif
 			      key, 2,
 			      accept_label, (PurpleRequestActionCb) ask_cb,
 			      decline_label, (PurpleRequestActionCb) ask_cb);
-#if PURPLE_VERSION_CHECK(3,0,0)
-	purple_request_cpar_unref(cpar);
-#endif
 }
 
 void sipe_backend_user_close_ask(gpointer key)
 {
 	purple_request_close_with_handle(key);
 }
 
 /*