diff --git a/configure.ac b/configure.ac
index e32b4ca..7b5760f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -442,6 +442,7 @@ if test "x$enable_meego" != "xno"; then
 
     if test "x$have_meego" = "xyes"; then
        AC_DEFINE(HAVE_MEEGO, 1, [Define if you have meego])
+       chain_enable_favourite_contacts="yes"
     fi
 else
    have_meego="no"
@@ -456,6 +457,36 @@ AC_SUBST(MEEGO_CFLAGS)
 AC_SUBST(MEEGO_LIBS)
 
 # -----------------------------------------------------------
+# favourite contacts support
+# -----------------------------------------------------------
+AC_ARG_ENABLE(favourite_contacts,
+              AS_HELP_STRING([--enable-favourite-contacts=@<:no/yes@:>@],
+                             [Enable favourite contacts]), ,
+                             enable_favourite_contacts=no)
+
+if test "x$enable_favourite_contacts" != "xno" -o "x$chain_enable_favourite_contacts" == "xyes"; then
+    PKG_CHECK_MODULES(TELEPATHY_LOGGER,
+    [
+       telepathy-logger
+    ], have_telepathy_logger="yes", have_telepathy_logger="no")
+
+    if test "x$have_telepathy_logger" = "xyes"; then
+       have_favourite_contacts="yes"
+       AC_DEFINE(HAVE_FAVOURITE_CONTACTS, 1, [Define if you want favourite contacts support])
+    fi
+else
+   have_favourite_contacts="no"
+fi
+
+if test "x$enable_favourite_contacts" = "xyes" -a "x$have_favourite_contacts" != "xyes"; then
+   AC_MSG_ERROR([Couldn't find favourite contacts dependencies.])
+fi
+
+AM_CONDITIONAL(HAVE_FAVOURITE_CONTACTS, test "x$have_telepathy_logger" = "xyes")
+AC_SUBST(FAVOURITE_CONTACTS_CFLAGS)
+AC_SUBST(FAVOURITE_CONTACTS_LIBS)
+
+# -----------------------------------------------------------
 # nautilus-sendto
 # -----------------------------------------------------------
 AC_ARG_ENABLE(nautilus-sendto,
@@ -551,6 +582,7 @@ Configure summary:
 	Location awareness (Geoclue):  ${have_geoclue}
 	Adium themes (Webkit).......:  ${have_webkit}
 	Meego widgets ..............:  ${have_meego}
+	Favourite contacts .........:  ${have_favourite_contacts}
 	Control center embedding....:  ${have_control_center_embedding}
 
     Logging:
diff --git a/extensions/Logger.xml b/extensions/Logger.xml
new file mode 100644
index 0000000..a377829
--- /dev/null
+++ b/extensions/Logger.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" ?>
+<node name="/Logger"
+  xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <tp:copyright>Copyright © 2009 Collabora Ltd.</tp:copyright>
+  <tp:license xmlns="http://www.w3.org/1999/xhtml">
+    <p>This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.</p>
+
+<p>This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.</p>
+
+<p>You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p>
+  </tp:license>
+  <interface name="org.freedesktop.Telepathy.Logger.DRAFT"
+    tp:causes-havoc="experimental">
+    <tp:added version="0.1">(as a draft)</tp:added>
+
+    <tp:docstring>
+      An interface for requesting information from the Telepathy Logger
+      service.
+    </tp:docstring>
+
+    <tp:struct name="Chat_Message" array-name="Chat_Message_List">
+      <tp:member type="s" name="Sender">
+        <tp:docstring>
+          The identifier of the contact who originated this message.
+        </tp:docstring>
+      </tp:member>
+
+      <tp:member type="s" name="Message">
+        <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+        The body of the message sent to a 1-1 Chat or a Chat Room.
+        </tp:docstring>
+      </tp:member>
+
+      <tp:member type="x" name="Timestamp" tp:type="Unix_Timestamp64">
+        <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+        Unix timestamp for the log entry
+        </tp:docstring>
+      </tp:member>
+
+      <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+        <p>It represents a single message, received or sent.</p>
+        <p>The receiver is implicit in the request: if the sender is the user,
+        the receiver is the identifier in chat_id paramenter, if the sender is
+        not the user, the user is consequently the receiver of the message</p>
+      </tp:docstring>
+    </tp:struct>
+
+    <method name="GetRecentMessages"
+            tp:name-for-bindings="Get_Recent_Messages">
+      <arg direction="in" name="Account" type="o" tp:type="Account">
+        <tp:docstring>
+        The account path for the TpAccount to which the conversation is related
+        </tp:docstring>
+      </arg>
+
+      <arg direction="in" name="Identifier" type="s">
+        <tp:docstring>
+        The buddy's identifier for the conversation
+        </tp:docstring>
+      </arg>
+
+      <arg direction="in" name="Is_Chatroom" type="b">
+        <tp:docstring>
+        Whether the conversation is a chatroom (i.e., XMPP MUC) or not
+        </tp:docstring>
+      </arg>
+
+      <arg direction="in" name="Lines" type="u">
+        <tp:docstring>
+        how many entries will be returned.
+
+        NOTE: to retreive information that would generate high traffic, use
+        the library API approach instead of DBus.
+        </tp:docstring>
+      </arg>
+
+      <arg direction="out" name="Messages" type="a(ssx)"
+           tp:type="Chat_Message[]" />
+
+      <tp:docstring>
+        Request the last Lines entries of logs for the specified couple
+        Account+Identifier.
+      </tp:docstring>
+    </method>
+
+    <method name="GetFavouriteContacts"
+            tp:name-for-bindings="Get_Favourite_Contacts">
+      <arg direction="out" name="Favourite_Contacts" type="a(oas)">
+        <tp:docstring>
+        The favourite contacts, as an array of TpAccounts and their contact
+        identifiers.
+        </tp:docstring>
+      </arg>
+
+      <tp:docstring>
+        Returns the favourite contacts.
+      </tp:docstring>
+    </method>
+
+    <method name="AddFavouriteContact"
+            tp:name-for-bindings="Add_Favourite_Contact">
+      <arg direction="in" name="Account" type="o" tp:type="Account">
+        <tp:docstring>
+        The object path for the TpAccount to which the contact belongs
+        </tp:docstring>
+      </arg>
+
+      <arg direction="in" name="Identifier" type="s">
+        <tp:docstring>
+        The favourite contact's identifier
+        </tp:docstring>
+      </arg>
+
+      <tp:docstring>
+        Add a contact's designation as a favourite. This method may not be
+        called until the service is ready. See the <tp:dbus-ref
+        namespace="org.freedesktop.Telepathy.Logger.DRAFT">FavouriteContactsReady</tp:dbus-ref> signal and <tp:dbus-ref
+        namespace="org.freedesktop.Telepathy.Logger.DRAFT">FavouriteContactsIsReady</tp:dbus-ref> property.
+      </tp:docstring>
+    </method>
+
+    <method name="RemoveFavouriteContact"
+            tp:name-for-bindings="Remove_Favourite_Contact">
+      <arg direction="in" name="Account" type="o" tp:type="Account">
+        <tp:docstring>
+        The object path for the TpAccount to which the contact belongs
+        </tp:docstring>
+      </arg>
+
+      <arg direction="in" name="Identifier" type="s">
+        <tp:docstring>
+        The favourite contact's identifier
+        </tp:docstring>
+      </arg>
+
+      <tp:docstring>
+        Remove a contact's designation as a favourite. This method may not be
+        called until the service is ready. See the <tp:dbus-ref
+        namespace="org.freedesktop.Telepathy.Logger.DRAFT">FavouriteContactsReady</tp:dbus-ref> signal and <tp:dbus-ref
+        namespace="org.freedesktop.Telepathy.Logger.DRAFT">FavouriteContactsIsReady</tp:dbus-ref> property.
+      </tp:docstring>
+    </method>
+
+    <signal name="FavouriteContactsChanged"
+      tp:name-for-bindings="Favourite_Contacts_Changed">
+      <tp:docstring>
+        The set of favourite contacts has changed.
+      </tp:docstring>
+
+      <arg name="Account" type="o" tp:type="Account">
+        <tp:docstring>
+          An account associated with the contact.
+        </tp:docstring>
+      </arg>
+
+      <arg name="Added" type="as">
+        <tp:docstring>
+        List of contact identifiers of contacts which are now favourites.
+        </tp:docstring>
+      </arg>
+
+      <arg name="Removed" type="as">
+        <tp:docstring>
+        List of contact identifiers of contacts which are no longer favourites.
+        </tp:docstring>
+      </arg>
+    </signal>
+
+  </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index fc77796..8ea05ae 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -13,7 +13,9 @@ EXTRA_DIST = \
     generic-types.xml \
     misc.xml \
     Debug.xml \
-    Channel_Interface_Conference.xml
+    Channel_Interface_Conference.xml \
+    Logger.xml \
+    $(NULL)
 
 noinst_LTLIBRARIES = libemp-extensions.la
 
@@ -114,7 +116,7 @@ _gen/cli-misc-body.h _gen/cli-misc.h: _gen/misc.xml \
 		--subclass=TpProxy \
 		--subclass-assert=TP_IS_PROXY \
 		--iface-quark-prefix=EMP_IFACE_QUARK \
-		--tp-proxy-api=0.7.6 \
+		--tp-proxy-api=0.10.0 \
 		$< Emp_Cli _gen/cli-misc
 
 _gen/svc-misc.c _gen/svc-misc.h: _gen/misc.xml \
diff --git a/extensions/all.xml b/extensions/all.xml
index 1def2fb..d0b9173 100644
--- a/extensions/all.xml
+++ b/extensions/all.xml
@@ -14,6 +14,8 @@
     from="Telepathy specification"/>
   <tp:external-type name="Handle" type="u"
     from="Telepathy specification"/>
+  <tp:external-type name="Account" type="o"
+    from="Telepathy specification"/>
 </tp:generic-types>
 
 
diff --git a/extensions/generic-types.xml b/extensions/generic-types.xml
index fe27d36..fdab14b 100644
--- a/extensions/generic-types.xml
+++ b/extensions/generic-types.xml
@@ -7,10 +7,10 @@
       (1970-01-01T00:00:00Z)</tp:docstring>
   </tp:simple-type>
 
-  <tp:simple-type name="Unix_Timestamp64" type="t">
-    <tp:docstring>An unsigned 64-bit integer representing time as the number
+  <tp:simple-type name="Unix_Timestamp64" type="x">
+    <tp:docstring>An signed 64-bit integer representing time as the number
       of seconds elapsed since the Unix epoch
-      (1970-01-01T00:00:00Z)</tp:docstring>
+      (1970-01-01T00:00:00Z); negative for times before the epoch</tp:docstring>
 
     <tp:rationale>The Text interface is the only user of Unix_Timestamp so
       far, and we'd like to be Y2038 compatible in future
diff --git a/extensions/misc.xml b/extensions/misc.xml
index f866663..6fe06d8 100644
--- a/extensions/misc.xml
+++ b/extensions/misc.xml
@@ -6,5 +6,6 @@
 
 <xi:include href="Debug.xml" />
 <xi:include href="Channel_Interface_Conference.xml" />
+<xi:include href="Logger.xml" />
 
 </tp:spec>
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
index 6ec319e..2df4001 100644
--- a/libempathy-gtk/Makefile.am
+++ b/libempathy-gtk/Makefile.am
@@ -11,6 +11,7 @@ AM_CPPFLAGS =                                           \
 	$(LIBNOTIFY_CFLAGS)				\
 	$(ENCHANT_CFLAGS)				\
 	$(LIBCHAMPLAIN_CFLAGS)				\
+	$(FAVOURITE_CONTACTS_CFLAGS)			\
 	$(GEOCLUE_CFLAGS)				\
 	$(MEEGO_CFLAGS)				\
 	$(WEBKIT_CFLAGS)				\
@@ -135,6 +136,7 @@ libempathy_gtk_la_LIBADD =			\
 	$(TPL_LIBS)				\
 	$(ENCHANT_LIBS)				\
 	$(LIBCHAMPLAIN_LIBS)			\
+	$(FAVOURITE_CONTACTS_LIBS)		\
 	$(GEOCLUE_LIBS)				\
 	$(MEEGO_LIBS)				\
 	$(WEBKIT_LIBS)				\
diff --git a/libempathy-gtk/empathy-contact-list-store.c b/libempathy-gtk/empathy-contact-list-store.c
index 53a0934..3ebeacb 100644
--- a/libempathy-gtk/empathy-contact-list-store.c
+++ b/libempathy-gtk/empathy-contact-list-store.c
@@ -28,6 +28,7 @@
 #include <string.h>
 
 #include <glib.h>
+#include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
 
 #include <telepathy-glib/util.h>
@@ -105,6 +106,10 @@ static void             contact_list_store_members_changed_cb        (EmpathyCon
 								      gchar                         *message,
 								      gboolean                       is_member,
 								      EmpathyContactListStore       *store);
+static void             contact_list_store_favourites_changed_cb     (EmpathyContactList            *list_iface,
+								      EmpathyContact                *contact,
+								      gboolean                       is_favourite,
+								      EmpathyContactListStore       *store);
 static void             contact_list_store_member_renamed_cb         (EmpathyContactList            *list_iface,
 								      EmpathyContact                *old_contact,
 								      EmpathyContact                *new_contact,
@@ -142,7 +147,8 @@ static void             contact_list_store_get_group                 (EmpathyCon
 								      const gchar                   *name,
 								      GtkTreeIter                   *iter_group_to_set,
 								      GtkTreeIter                   *iter_separator_to_set,
-								      gboolean                      *created);
+								      gboolean                      *created,
+								      gboolean                      is_fake_group);
 static gint             contact_list_store_state_sort_func           (GtkTreeModel                  *model,
 								      GtkTreeIter                   *iter_a,
 								      GtkTreeIter                   *iter_b,
@@ -192,6 +198,10 @@ contact_list_store_iface_setup (gpointer user_data)
 			  G_CALLBACK (contact_list_store_members_changed_cb),
 			  store);
 	g_signal_connect (priv->list,
+			  "favourites-changed",
+			  G_CALLBACK (contact_list_store_favourites_changed_cb),
+			  store);
+	g_signal_connect (priv->list,
 			  "groups-changed",
 			  G_CALLBACK (contact_list_store_groups_changed_cb),
 			  store);
@@ -338,6 +348,9 @@ contact_list_store_dispose (GObject *object)
 					      G_CALLBACK (contact_list_store_members_changed_cb),
 					      object);
 	g_signal_handlers_disconnect_by_func (priv->list,
+					      G_CALLBACK (contact_list_store_favourites_changed_cb),
+					      object);
+	g_signal_handlers_disconnect_by_func (priv->list,
 					      G_CALLBACK (contact_list_store_groups_changed_cb),
 					      object);
 	g_object_unref (priv->list);
@@ -824,6 +837,7 @@ contact_list_store_setup (EmpathyContactListStore *store)
 		G_TYPE_BOOLEAN,       /* Can make audio calls */
 		G_TYPE_BOOLEAN,       /* Can make video calls */
 		EMPATHY_TYPE_CONTACT_LIST_FLAGS, /* Flags */
+		G_TYPE_BOOLEAN,       /* Is a fake group */
 	};
 
 	priv = GET_PRIV (store);
@@ -917,6 +931,25 @@ contact_list_store_members_changed_cb (EmpathyContactList      *list_iface,
 }
 
 static void
+contact_list_store_favourites_changed_cb (EmpathyContactList      *list_iface,
+					  EmpathyContact          *contact,
+					  gboolean                 is_favourite,
+					  EmpathyContactListStore *store)
+{
+	EmpathyContactListStorePriv *priv;
+
+	priv = GET_PRIV (store);
+
+	DEBUG ("Contact %s (%d) is %s a favourite",
+		empathy_contact_get_id (contact),
+		empathy_contact_get_handle (contact),
+		is_favourite ? "now" : "no longer");
+
+	contact_list_store_remove_contact (store, contact);
+	contact_list_store_add_contact (store, contact);
+}
+
+static void
 contact_list_store_member_renamed_cb (EmpathyContactList      *list_iface,
 				      EmpathyContact          *old_contact,
 				      EmpathyContact          *new_contact,
@@ -969,6 +1002,27 @@ contact_list_store_groups_changed_cb (EmpathyContactList      *list_iface,
 }
 
 static void
+add_contact_to_store (GtkTreeStore *store,
+		      GtkTreeIter *iter,
+		      EmpathyContact *contact,
+		      EmpathyContactListFlags flags)
+{
+	gtk_tree_store_set (store, iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact),
+			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE,
+			    EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL,
+			      empathy_contact_get_capabilities (contact) &
+			        EMPATHY_CAPABILITIES_AUDIO,
+			    EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL,
+			      empathy_contact_get_capabilities (contact) &
+			        EMPATHY_CAPABILITIES_VIDEO,
+			    EMPATHY_CONTACT_LIST_STORE_COL_FLAGS, flags,
+			    -1);
+}
+
+static void
 contact_list_store_add_contact (EmpathyContactListStore *store,
 				EmpathyContact          *contact)
 {
@@ -994,8 +1048,18 @@ contact_list_store_add_contact (EmpathyContactListStore *store,
 		flags = empathy_contact_manager_get_flags_for_connection (
 			EMPATHY_CONTACT_MANAGER (priv->list), connection);
 	}
-	/* If no groups just add it at the top level. */
 	if (!groups) {
+#if HAVE_FAVOURITE_CONTACTS
+		GtkTreeIter iter_group;
+
+		contact_list_store_get_group (store, EMPATHY_CONTACT_LIST_STORE_UNGROUPED,
+			&iter_group, NULL, NULL, TRUE);
+
+		gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter,
+					     &iter_group, NULL);
+#else
+		/* FIXME: remove this in 2.31.x */
+		/* If no groups just add it at the top level. */
 		GtkTreeModel *model = GTK_TREE_MODEL (store);
 
 		if (gtk_tree_model_get_iter_first (model, &iter)) do {
@@ -1014,48 +1078,41 @@ contact_list_store_add_contact (EmpathyContactListStore *store,
 		} while (gtk_tree_model_iter_next (model, &iter));
 
 		gtk_tree_store_append (GTK_TREE_STORE (store), &iter, NULL);
-		gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
-				    EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact),
-				    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact,
-				    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE,
-				    EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE,
-				    EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL,
-				      empathy_contact_get_capabilities (contact) &
-				        EMPATHY_CAPABILITIES_AUDIO,
-				    EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL,
-				      empathy_contact_get_capabilities (contact) &
-				        EMPATHY_CAPABILITIES_VIDEO,
-				    EMPATHY_CONTACT_LIST_STORE_COL_FLAGS, flags,
-				    -1);
+#endif
+
+		add_contact_to_store (GTK_TREE_STORE (store), &iter, contact, flags);
 	}
 
 	/* Else add to each group. */
 	for (l = groups; l; l = l->next) {
 		GtkTreeIter iter_group;
 
-		contact_list_store_get_group (store, l->data, &iter_group, NULL, NULL);
+		contact_list_store_get_group (store, l->data, &iter_group, NULL, NULL, FALSE);
 
 		gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter,
 					     &iter_group, NULL);
-		gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
-				    EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact),
-				    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact,
-				    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE,
-				    EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE,
-				    EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL,
-				      empathy_contact_get_capabilities (contact) &
-				        EMPATHY_CAPABILITIES_AUDIO,
-				    EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL,
-				      empathy_contact_get_capabilities (contact) &
-				        EMPATHY_CAPABILITIES_VIDEO,
-				    EMPATHY_CONTACT_LIST_STORE_COL_FLAGS, flags,
-				    -1);
+
+		add_contact_to_store (GTK_TREE_STORE (store), &iter, contact, flags);
 		g_free (l->data);
 	}
 	g_list_free (groups);
 
-	contact_list_store_contact_update (store, contact);
+#ifdef HAVE_FAVOURITE_CONTACTS
+	if (empathy_contact_list_is_favourite (priv->list, contact)) {
+	/* Add contact to the fake 'Favorites' group */
+		GtkTreeIter iter_group;
+
+		contact_list_store_get_group (store, EMPATHY_CONTACT_LIST_STORE_FAVORITE,
+			&iter_group, NULL, NULL, TRUE);
+
+		gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter,
+					     &iter_group, NULL);
 
+		add_contact_to_store (GTK_TREE_STORE (store), &iter, contact, flags);
+	}
+#endif
+
+	contact_list_store_contact_update (store, contact);
 }
 
 static void
@@ -1389,7 +1446,8 @@ contact_list_store_get_group (EmpathyContactListStore *store,
 			      const gchar            *name,
 			      GtkTreeIter            *iter_group_to_set,
 			      GtkTreeIter            *iter_separator_to_set,
-			      gboolean               *created)
+			      gboolean               *created,
+			      gboolean               is_fake_group)
 {
 	EmpathyContactListStorePriv *priv;
 	GtkTreeModel                *model;
@@ -1420,6 +1478,7 @@ contact_list_store_get_group (EmpathyContactListStore *store,
 				    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, TRUE,
 				    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, FALSE,
 				    EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE,
+				    EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, is_fake_group,
 				    -1);
 
 		if (iter_group_to_set) {
@@ -1462,44 +1521,77 @@ contact_list_store_get_group (EmpathyContactListStore *store,
 }
 
 static gint
+compare_separator_and_groups (gboolean is_separator_a,
+			      gboolean is_separator_b,
+			      const gchar *name_a,
+			      const gchar *name_b,
+			      EmpathyContact *contact_a,
+			      EmpathyContact *contact_b,
+			      gboolean fake_group_a,
+			      gboolean fake_group_b)
+{
+	if (is_separator_a || is_separator_b) {
+		/* We have at least one separator */
+		if (is_separator_a) {
+			return -1;
+		} else if (is_separator_b) {
+			return 1;
+		}
+	}
+
+	/* One group and one contact */
+	if (!contact_a && contact_b) {
+		return 1;
+	} else if (contact_a && !contact_b) {
+		return -1;
+	} else if (!contact_a && !contact_b) {
+		/* Two groups. The 'Ungrouped' fake group is display at the bottom of the
+		 * contact list and the 'Favorites' at the top. */
+		if (fake_group_a && !tp_strdiff (name_a, EMPATHY_CONTACT_LIST_STORE_UNGROUPED))
+			return 1;
+		else if (fake_group_b && !tp_strdiff (name_b, EMPATHY_CONTACT_LIST_STORE_UNGROUPED))
+			return -1;
+		else if (fake_group_a && !tp_strdiff (name_a, EMPATHY_CONTACT_LIST_STORE_FAVORITE))
+			return -1;
+		else if (fake_group_b && !tp_strdiff (name_b, EMPATHY_CONTACT_LIST_STORE_FAVORITE))
+			return 1;
+		else
+			return g_utf8_collate (name_a, name_b);
+	}
+
+	/* Two contacts, ordering depends of the sorting policy */
+	return 0;
+}
+
+static gint
 contact_list_store_state_sort_func (GtkTreeModel *model,
 				    GtkTreeIter  *iter_a,
 				    GtkTreeIter  *iter_b,
 				    gpointer      user_data)
 {
-	gint            ret_val = 0;
+	gint            ret_val;
 	gchar          *name_a, *name_b;
 	gboolean        is_separator_a, is_separator_b;
 	EmpathyContact *contact_a, *contact_b;
+	gboolean       fake_group_a, fake_group_b;
 
 	gtk_tree_model_get (model, iter_a,
 			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a,
 			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a,
 			    EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake_group_a,
 			    -1);
 	gtk_tree_model_get (model, iter_b,
 			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b,
 			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b,
 			    EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake_group_b,
 			    -1);
 
-	/* Separator or group? */
-	if (is_separator_a || is_separator_b) {
-		if (is_separator_a) {
-			ret_val = -1;
-		} else if (is_separator_b) {
-			ret_val = 1;
-		}
-	} else if (!contact_a && contact_b) {
-		ret_val = 1;
-	} else if (contact_a && !contact_b) {
-		ret_val = -1;
-	} else if (!contact_a && !contact_b) {
-		/* Handle groups */
-		ret_val = g_utf8_collate (name_a, name_b);
-	}
+	ret_val = compare_separator_and_groups (is_separator_a, is_separator_b,
+		name_a, name_b, contact_a, contact_b, fake_group_a, fake_group_b);
 
-	if (ret_val) {
+	if (ret_val != 0) {
 		goto free_and_out;
 	}
 
@@ -1538,38 +1630,28 @@ contact_list_store_name_sort_func (GtkTreeModel *model,
 {
 	gchar         *name_a, *name_b;
 	EmpathyContact *contact_a, *contact_b;
-	gboolean       is_separator_a, is_separator_b;
+	gboolean       is_separator_a = FALSE, is_separator_b = FALSE;
 	gint           ret_val;
+	gboolean       fake_group_a, fake_group_b;
 
 	gtk_tree_model_get (model, iter_a,
 			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a,
 			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a,
 			    EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake_group_a,
 			    -1);
 	gtk_tree_model_get (model, iter_b,
 			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b,
 			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b,
 			    EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake_group_b,
 			    -1);
 
-	/* If contact is NULL it means it's a group. */
+	ret_val = compare_separator_and_groups (is_separator_a, is_separator_b,
+		name_a, name_b, contact_a, contact_b, fake_group_a, fake_group_b);
 
-	if (is_separator_a || is_separator_b) {
-		if (is_separator_a) {
-			ret_val = -1;
-		} else if (is_separator_b) {
-			ret_val = 1;
-		}
-	} else if (!contact_a && contact_b) {
-		ret_val = 1;
-	} else if (contact_a && !contact_b) {
-		ret_val = -1;
-	} else {
+	if (ret_val == 0)
 		ret_val = g_utf8_collate (name_a, name_b);
-	}
-
-	g_free (name_a);
-	g_free (name_b);
 
 	if (contact_a) {
 		g_object_unref (contact_a);
diff --git a/libempathy-gtk/empathy-contact-list-store.h b/libempathy-gtk/empathy-contact-list-store.h
index afefd28..a787b83 100644
--- a/libempathy-gtk/empathy-contact-list-store.h
+++ b/libempathy-gtk/empathy-contact-list-store.h
@@ -63,9 +63,13 @@ typedef enum {
 	EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL,
 	EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL,
 	EMPATHY_CONTACT_LIST_STORE_COL_FLAGS,
-	EMPATHY_CONTACT_LIST_STORE_COL_COUNT
+	EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP,
+	EMPATHY_CONTACT_LIST_STORE_COL_COUNT,
 } EmpathyContactListStoreCol;
 
+#define EMPATHY_CONTACT_LIST_STORE_UNGROUPED _("Ungrouped")
+#define EMPATHY_CONTACT_LIST_STORE_FAVORITE  _("Favorites People")
+
 struct _EmpathyContactListStore {
 	GtkTreeStore parent;
 	gpointer priv;
diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c
index 705494e..7165837 100644
--- a/libempathy-gtk/empathy-contact-list-view.c
+++ b/libempathy-gtk/empathy-contact-list-view.c
@@ -879,6 +879,43 @@ contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn     *tree_column,
 }
 
 static void
+contact_list_view_group_icon_cell_data_func (GtkTreeViewColumn     *tree_column,
+					     GtkCellRenderer       *cell,
+					     GtkTreeModel          *model,
+					     GtkTreeIter           *iter,
+					     EmpathyContactListView *view)
+{
+	GdkPixbuf *pixbuf = NULL;
+	gboolean is_group;
+	gchar *name;
+
+	gtk_tree_model_get (model, iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
+			    -1);
+
+	if (!is_group)
+		goto out;;
+
+	if (tp_strdiff (name, EMPATHY_CONTACT_LIST_STORE_FAVORITE))
+		goto out;;
+
+	pixbuf = empathy_pixbuf_from_icon_name ("emblem-favorite",
+		GTK_ICON_SIZE_MENU);
+
+out:
+	g_object_set (cell,
+		      "visible", pixbuf != NULL,
+		      "pixbuf", pixbuf,
+		      NULL);
+
+	if (pixbuf != NULL)
+		g_object_unref (pixbuf);
+
+	g_free (name);
+}
+
+static void
 contact_list_view_audio_call_cell_data_func (
 				       GtkTreeViewColumn      *tree_column,
 				       GtkCellRenderer        *cell,
@@ -1115,6 +1152,22 @@ contact_list_view_setup (EmpathyContactListView *view)
 		      "visible", FALSE,
 		      NULL);
 
+	/* Group icon */
+	cell = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_start (col, cell, FALSE);
+	gtk_tree_view_column_set_cell_data_func (
+		col, cell,
+		(GtkTreeCellDataFunc) contact_list_view_group_icon_cell_data_func,
+		view, NULL);
+
+	g_object_set (cell,
+		      "xpad", 0,
+		      "ypad", 0,
+		      "visible", FALSE,
+		      "width", 16,
+		      "height", 16,
+		      NULL);
+
 	/* Name */
 	cell = empathy_cell_renderer_text_new ();
 	gtk_tree_view_column_pack_start (col, cell, TRUE);
@@ -1457,7 +1510,8 @@ empathy_contact_list_view_get_flags (EmpathyContactListView *view)
 }
 
 gchar *
-empathy_contact_list_view_get_selected_group (EmpathyContactListView *view)
+empathy_contact_list_view_get_selected_group (EmpathyContactListView *view,
+					      gboolean *is_fake_group)
 {
 	EmpathyContactListViewPriv *priv;
 	GtkTreeSelection          *selection;
@@ -1465,6 +1519,7 @@ empathy_contact_list_view_get_selected_group (EmpathyContactListView *view)
 	GtkTreeModel              *model;
 	gboolean                   is_group;
 	gchar                     *name;
+	gboolean                   fake;
 
 	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
 
@@ -1478,6 +1533,7 @@ empathy_contact_list_view_get_selected_group (EmpathyContactListView *view)
 	gtk_tree_model_get (model, &iter,
 			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
 			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake,
 			    -1);
 
 	if (!is_group) {
@@ -1485,6 +1541,9 @@ empathy_contact_list_view_get_selected_group (EmpathyContactListView *view)
 		return NULL;
 	}
 
+	if (is_fake_group != NULL)
+		*is_fake_group = fake;
+
 	return name;
 }
 
@@ -1521,7 +1580,7 @@ contact_list_view_group_remove_activate_cb (GtkMenuItem            *menuitem,
 	EmpathyContactListViewPriv *priv = GET_PRIV (view);
 	gchar                      *group;
 
-	group = empathy_contact_list_view_get_selected_group (view);
+	group = empathy_contact_list_view_get_selected_group (view, NULL);
 	if (group) {
 		gchar     *text;
 		GtkWindow *parent;
@@ -1549,6 +1608,7 @@ empathy_contact_list_view_get_group_menu (EmpathyContactListView *view)
 	GtkWidget                  *menu;
 	GtkWidget                  *item;
 	GtkWidget                  *image;
+	gboolean                   is_fake_group;
 
 	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
 
@@ -1557,8 +1617,9 @@ empathy_contact_list_view_get_group_menu (EmpathyContactListView *view)
 		return NULL;
 	}
 
-	group = empathy_contact_list_view_get_selected_group (view);
-	if (!group) {
+	group = empathy_contact_list_view_get_selected_group (view, &is_fake_group);
+	if (!group || is_fake_group) {
+		/* We can't alter fake groups */
 		return NULL;
 	}
 
diff --git a/libempathy-gtk/empathy-contact-list-view.h b/libempathy-gtk/empathy-contact-list-view.h
index 17de523..41b968d 100644
--- a/libempathy-gtk/empathy-contact-list-view.h
+++ b/libempathy-gtk/empathy-contact-list-view.h
@@ -73,7 +73,8 @@ EmpathyContactListView *   empathy_contact_list_view_new                (Empathy
 								         EmpathyContactFeatureFlags      contact_features);
 EmpathyContact *           empathy_contact_list_view_dup_selected       (EmpathyContactListView         *view);
 EmpathyContactListFlags    empathy_contact_list_view_get_flags          (EmpathyContactListView         *view);
-gchar *                    empathy_contact_list_view_get_selected_group (EmpathyContactListView         *view);
+gchar *                    empathy_contact_list_view_get_selected_group (EmpathyContactListView         *view,
+									 gboolean *is_fake_group);
 GtkWidget *                empathy_contact_list_view_get_contact_menu   (EmpathyContactListView         *view);
 GtkWidget *                empathy_contact_list_view_get_group_menu     (EmpathyContactListView         *view);
 
diff --git a/libempathy-gtk/empathy-contact-menu.c b/libempathy-gtk/empathy-contact-menu.c
index 54bb4b2..23522d4 100644
--- a/libempathy-gtk/empathy-contact-menu.c
+++ b/libempathy-gtk/empathy-contact-menu.c
@@ -134,6 +134,13 @@ empathy_contact_menu_new (EmpathyContact             *contact,
 		gtk_widget_show (item);
 	}
 
+#if HAVE_FAVOURITE_CONTACTS
+	/* Favorite checkbox */
+	item = empathy_contact_favourite_menu_item_new (contact);
+	gtk_menu_shell_append (shell, item);
+	gtk_widget_show (item);
+#endif
+
 	return menu;
 }
 
@@ -396,6 +403,48 @@ empathy_contact_share_my_desktop_menu_item_new (EmpathyContact *contact)
 	return item;
 }
 
+#if HAVE_FAVOURITE_CONTACTS
+static void
+favourite_menu_item_toggled_cb (GtkCheckMenuItem *item,
+	EmpathyContact *contact)
+{
+	EmpathyContactManager *manager;
+	EmpathyContactList *list;
+
+	manager = empathy_contact_manager_dup_singleton ();
+	list = EMPATHY_CONTACT_LIST (manager);
+
+	if (gtk_check_menu_item_get_active (item)) {
+		empathy_contact_list_add_to_favourites (list, contact);
+	} else {
+		empathy_contact_list_remove_from_favourites (list, contact);
+	}
+
+	g_object_unref (manager);
+}
+
+GtkWidget *
+empathy_contact_favourite_menu_item_new (EmpathyContact *contact)
+{
+	GtkWidget *item;
+	EmpathyContactManager *manager;
+
+	item = gtk_check_menu_item_new_with_label (_("Favorite"));
+
+	manager = empathy_contact_manager_dup_singleton ();
+	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
+		empathy_contact_list_is_favourite (EMPATHY_CONTACT_LIST (manager),
+						   contact));
+
+	g_signal_connect (item, "toggled",
+			  G_CALLBACK (favourite_menu_item_toggled_cb),
+			  contact);
+
+	g_object_unref (manager);
+	return item;
+}
+#endif
+
 static void
 contact_info_menu_item_activate_cb (EmpathyContact *contact)
 {
diff --git a/libempathy-gtk/empathy-contact-menu.h b/libempathy-gtk/empathy-contact-menu.h
index 2c623e6..25f18e0 100644
--- a/libempathy-gtk/empathy-contact-menu.h
+++ b/libempathy-gtk/empathy-contact-menu.h
@@ -50,6 +50,7 @@ GtkWidget * empathy_contact_edit_menu_item_new (EmpathyContact             *cont
 GtkWidget * empathy_contact_invite_menu_item_new (EmpathyContact *contact);
 GtkWidget * empathy_contact_file_transfer_menu_item_new (EmpathyContact    *contact);
 GtkWidget * empathy_contact_share_my_desktop_menu_item_new (EmpathyContact *contact);
+GtkWidget * empathy_contact_favourite_menu_item_new (EmpathyContact *contact);
 
 G_END_DECLS
 
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 1f14239..057203b 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -10,6 +10,7 @@ AM_CPPFLAGS =                                           \
 	-DG_LOG_DOMAIN=\"empathy\"			\
 	$(TPL_CFLAGS)					\
 	$(LIBEMPATHY_CFLAGS)				\
+	$(FAVOURITE_CONTACTS_CFLAGS)			\
 	$(GEOCLUE_CFLAGS)				\
 	$(NETWORK_MANAGER_CFLAGS)			\
 	$(CONNMAN_CFLAGS)				\
@@ -119,6 +120,7 @@ nodist_libempathy_la_SOURCES =\
 libempathy_la_LIBADD =		\
 	$(top_builddir)/extensions/libemp-extensions.la \
 	$(LIBEMPATHY_LIBS) \
+	$(FAVOURITE_CONTACTS_LIBS) \
 	$(GEOCLUE_LIBS) \
 	$(TPL_LIBS) \
 	$(NETWORK_MANAGER_LIBS) \
diff --git a/libempathy/empathy-contact-list.c b/libempathy/empathy-contact-list.c
index d485921..0a3d361 100644
--- a/libempathy/empathy-contact-list.c
+++ b/libempathy/empathy-contact-list.c
@@ -73,6 +73,15 @@ contact_list_base_init (gpointer klass)
 			      5, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT,
 			      G_TYPE_UINT, G_TYPE_STRING, G_TYPE_BOOLEAN);
 
+		g_signal_new ("favourites-changed",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      _empathy_marshal_VOID__OBJECT_BOOLEAN,
+			      G_TYPE_NONE,
+			      2, EMPATHY_TYPE_CONTACT, G_TYPE_BOOLEAN);
+
 		g_signal_new ("pendings-changed",
 			      G_TYPE_FROM_CLASS (klass),
 			      G_SIGNAL_RUN_LAST,
@@ -249,3 +258,41 @@ empathy_contact_list_get_flags (EmpathyContactList *list)
 		return 0;
 	}
 }
+
+gboolean
+empathy_contact_list_is_favourite (EmpathyContactList *list,
+                                   EmpathyContact     *contact)
+{
+#if HAVE_FAVOURITE_CONTACTS
+	if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->is_favourite) {
+		return EMPATHY_CONTACT_LIST_GET_IFACE (list)->is_favourite (
+			list, contact);
+	}
+#endif /* HAVE_FAVOURITE_CONTACTS */
+
+	return FALSE;
+}
+
+void
+empathy_contact_list_add_to_favourites (EmpathyContactList *list,
+                                        EmpathyContact     *contact)
+{
+#if HAVE_FAVOURITE_CONTACTS
+	if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->add_favourite) {
+		EMPATHY_CONTACT_LIST_GET_IFACE (list)->add_favourite (list,
+			contact);
+	}
+#endif /* HAVE_FAVOURITE_CONTACTS */
+}
+
+void
+empathy_contact_list_remove_from_favourites (EmpathyContactList *list,
+                                             EmpathyContact     *contact)
+{
+#if HAVE_FAVOURITE_CONTACTS
+	if (EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove_favourite) {
+		EMPATHY_CONTACT_LIST_GET_IFACE (list)->remove_favourite (list,
+			contact);
+	}
+#endif /* HAVE_FAVOURITE_CONTACTS */
+}
diff --git a/libempathy/empathy-contact-list.h b/libempathy/empathy-contact-list.h
index 28238e4..8d2f445 100644
--- a/libempathy/empathy-contact-list.h
+++ b/libempathy/empathy-contact-list.h
@@ -73,6 +73,12 @@ struct _EmpathyContactListIface {
 			 (*get_monitor)       (EmpathyContactList *list);
 	EmpathyContactListFlags
 			 (*get_flags)         (EmpathyContactList *list);
+	gboolean         (*is_favourite)      (EmpathyContactList *list,
+					       EmpathyContact     *contact);
+	void             (*add_favourite)     (EmpathyContactList *list,
+					       EmpathyContact     *contact);
+	void             (*remove_favourite)  (EmpathyContactList *list,
+					       EmpathyContact     *contact);
 };
 
 GType    empathy_contact_list_get_type          (void) G_GNUC_CONST;
@@ -104,6 +110,17 @@ EmpathyContactMonitor *
 EmpathyContactListFlags
          empathy_contact_list_get_flags		(EmpathyContactList *list);
 
+gboolean empathy_contact_list_is_favourite      (EmpathyContactList *list,
+						 EmpathyContact     *contact);
+
+void     empathy_contact_list_add_to_favourites (EmpathyContactList *list,
+						 EmpathyContact     *contact);
+
+void     empathy_contact_list_remove_from_favourites
+                                                (EmpathyContactList *list,
+						 EmpathyContact     *contact);
+
+
 G_END_DECLS
 
 #endif /* __EMPATHY_CONTACT_LIST_H__ */
diff --git a/libempathy/empathy-contact-manager.c b/libempathy/empathy-contact-manager.c
index ee3b705..fd7b08b 100644
--- a/libempathy/empathy-contact-manager.c
+++ b/libempathy/empathy-contact-manager.c
@@ -25,6 +25,10 @@
 
 #include <telepathy-glib/account-manager.h>
 #include <telepathy-glib/enums.h>
+#include <telepathy-glib/proxy-subclass.h>
+#include <telepathy-glib/util.h>
+
+#include <extensions/extensions.h>
 
 #include "empathy-contact-manager.h"
 #include "empathy-contact-monitor.h"
@@ -39,6 +43,11 @@ typedef struct {
 	GHashTable     *lists;
 	TpAccountManager *account_manager;
 	EmpathyContactMonitor *contact_monitor;
+	TpProxy *logger;
+	/* account object path (gchar *) => GHashTable containing favorite contacts
+	 * (contact ID (gchar *) => TRUE) */
+	GHashTable *favourites;
+	TpProxySignalConnection *favourite_contacts_changed_signal;
 } EmpathyContactManagerPriv;
 
 static void contact_manager_iface_init         (EmpathyContactListIface    *iface);
@@ -188,15 +197,192 @@ contact_manager_validity_changed_cb (TpAccountManager *account_manager,
 	}
 }
 
+static gboolean
+contact_manager_is_favourite (EmpathyContactList *manager,
+			      EmpathyContact     *contact)
+{
+	EmpathyContactManagerPriv *priv;
+	TpAccount *account;
+	const gchar *account_name;
+	GHashTable *contact_hash;
+
+	g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), FALSE);
+	g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
+
+	priv = GET_PRIV (manager);
+
+	account = empathy_contact_get_account (contact);
+	account_name = tp_proxy_get_object_path (TP_PROXY (account));
+	contact_hash = g_hash_table_lookup (priv->favourites, account_name);
+
+	if (contact_hash != NULL) {
+		const gchar *contact_id = empathy_contact_get_id (contact);
+
+		if (g_hash_table_lookup (contact_hash, contact_id) != NULL)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+add_favourite_contact_cb (TpProxy *proxy,
+			  const GError *error,
+			  gpointer user_data,
+			  GObject *weak_object)
+{
+	if (error != NULL)
+		DEBUG ("AddFavouriteContact failed: %s", error->message);
+}
+
+static void
+contact_manager_add_favourite (EmpathyContactList *manager,
+			       EmpathyContact *contact)
+{
+	EmpathyContactManagerPriv *priv;
+	TpAccount *account;
+	const gchar *account_name;
+
+	g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
+	g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+
+	priv = GET_PRIV (manager);
+
+	account = empathy_contact_get_account (contact);
+	account_name = tp_proxy_get_object_path (TP_PROXY (account));
+
+	emp_cli_logger_call_add_favourite_contact (priv->logger, -1,
+						   account_name,
+						   empathy_contact_get_id (contact),
+						   add_favourite_contact_cb, NULL, NULL, G_OBJECT (manager));
+}
+
+static void
+remove_favourite_contact_cb (TpProxy *proxy,
+			     const GError *error,
+			     gpointer user_data,
+			     GObject *weak_object)
+{
+	if (error != NULL)
+		DEBUG ("RemoveFavouriteContact failed: %s", error->message);
+}
+
+static void
+contact_manager_remove_favourite (EmpathyContactList *manager,
+				  EmpathyContact *contact)
+{
+	EmpathyContactManagerPriv *priv;
+	TpAccount *account;
+	const gchar *account_name;
+
+	g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
+	g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+
+	priv = GET_PRIV (manager);
+
+	account = empathy_contact_get_account (contact);
+	account_name = tp_proxy_get_object_path (TP_PROXY (account));
+
+	emp_cli_logger_call_remove_favourite_contact (priv->logger, -1,
+						      account_name,
+						      empathy_contact_get_id (contact),
+						      remove_favourite_contact_cb, NULL, NULL, G_OBJECT (manager));
+}
+
+static void
+add_contacts_to_favourites (EmpathyContactManager *self,
+			    const gchar *account,
+			    const gchar **contacts)
+{
+	EmpathyContactManagerPriv *priv = GET_PRIV (self);
+	guint j;
+	GHashTable *contact_hash;
+
+	contact_hash = g_hash_table_lookup (priv->favourites, account);
+	if (contact_hash == NULL) {
+		contact_hash = g_hash_table_new_full (g_str_hash,
+						      g_str_equal,
+						      g_free, NULL);
+
+		g_hash_table_insert (priv->favourites,
+				     g_strdup (account),
+				     contact_hash);
+	}
+
+	for (j = 0; contacts && contacts[j] != NULL; j++) {
+		g_hash_table_insert (contact_hash,
+				     g_strdup (contacts[j]),
+				     GINT_TO_POINTER (1));
+	}
+}
+
+static void
+logger_favourite_contacts_add_from_value_array (GValueArray           *va,
+						EmpathyContactManager *manager)
+{
+	guint i;
+
+	for (i = 0; i < va->n_values; i++) {
+		GValue *account_value;
+		const gchar *account;
+		GValue *contacts_value;
+		const gchar **contacts;
+
+		account_value = g_value_array_get_nth (va, 0);
+		contacts_value = g_value_array_get_nth (va, 1);
+
+		account = g_value_get_boxed (account_value);
+		contacts = g_value_get_boxed (contacts_value);
+
+		add_contacts_to_favourites (manager, account, contacts);
+	}
+}
+
+static void
+logger_favourite_contacts_get_cb (TpProxy         *proxy,
+				  const GPtrArray *result,
+				  const GError    *error,
+				  gpointer         user_data,
+				  GObject         *weak_object)
+{
+	EmpathyContactManager *manager = EMPATHY_CONTACT_MANAGER (user_data);
+
+	if (error == NULL) {
+		g_ptr_array_foreach ((GPtrArray *) result,
+				(GFunc)
+				logger_favourite_contacts_add_from_value_array,
+				manager);
+	} else {
+		DEBUG ("Failed to get the FavouriteContacts property: %s",
+				error->message);
+	}
+}
+
+static void
+logger_favourite_contacts_setup (EmpathyContactManager *manager)
+{
+	EmpathyContactManagerPriv *priv = GET_PRIV (manager);
+
+	emp_cli_logger_call_get_favourite_contacts (priv->logger, -1,
+			logger_favourite_contacts_get_cb, manager, NULL,
+			G_OBJECT (manager));
+}
+
 static void
 contact_manager_finalize (GObject *object)
 {
 	EmpathyContactManagerPriv *priv = GET_PRIV (object);
 
+	tp_proxy_signal_connection_disconnect (priv->favourite_contacts_changed_signal);
+
+	if (priv->logger != NULL)
+		g_object_unref (priv->logger);
+
 	g_hash_table_foreach (priv->lists,
 			      contact_manager_disconnect_foreach,
 			      object);
 	g_hash_table_destroy (priv->lists);
+	g_hash_table_destroy (priv->favourites);
 
 	g_object_unref (priv->account_manager);
 
@@ -291,22 +477,150 @@ account_manager_prepared_cb (GObject *source_object,
 			     G_OBJECT (manager));
 }
 
+static EmpathyContact *
+contact_manager_lookup_contact (EmpathyContactManager *manager,
+				const gchar           *account_name,
+				const gchar           *contact_id)
+{
+	EmpathyContact *retval = NULL;
+	GList *members, *l;
+
+	/* XXX: any more efficient way to do this (other than having to build
+	 * and maintain a hash)? */
+	members = empathy_contact_list_get_members (
+			EMPATHY_CONTACT_LIST (manager));
+	for (l = members; l; l = l->next) {
+		EmpathyContact *contact = l->data;
+		TpAccount *account = empathy_contact_get_account (contact);
+		const gchar *id_cur;
+		const gchar *name_cur;
+
+		id_cur = empathy_contact_get_id (contact);
+		name_cur = tp_proxy_get_object_path (TP_PROXY (account));
+
+		if (!tp_strdiff (contact_id, id_cur) &&
+			!tp_strdiff (account_name, name_cur)) {
+			retval = contact;
+		}
+
+		g_object_unref (contact);
+	}
+
+	g_list_free (members);
+
+	return retval;
+}
+
+static void
+logger_favourite_contacts_changed_cb (TpProxy      *proxy,
+				      const gchar  *account_name,
+				      const gchar **added,
+				      const gchar **removed,
+				      gpointer      user_data,
+				      GObject      *weak_object)
+{
+	EmpathyContactManagerPriv *priv;
+	EmpathyContactManager *manager = EMPATHY_CONTACT_MANAGER (weak_object);
+	GHashTable *contact_hash;
+	EmpathyContact *contact;
+	gint i;
+
+	priv = GET_PRIV (manager);
+
+	contact_hash = g_hash_table_lookup (priv->favourites, account_name);
+
+	/* XXX: note that, at the time of this comment, there will always be
+	 * exactly one contact amongst added and removed, so the linear lookup
+	 * of each contact isn't as painful as it appears */
+
+	add_contacts_to_favourites (manager, account_name, added);
+
+	for (i = 0; added && added[i]; i++) {
+		contact = contact_manager_lookup_contact (manager, account_name,
+							  added[i]);
+		if (contact != NULL)
+			g_signal_emit_by_name (manager, "favourites-changed",
+					       contact, TRUE);
+		else
+			DEBUG ("failed to find contact for account %s, contact "
+			       "id %s", account_name, added[i]);
+	}
+
+	for (i = 0; removed && removed[i]; i++) {
+		contact_hash = g_hash_table_lookup (priv->favourites,
+						    account_name);
+
+		if (contact_hash != NULL) {
+			g_hash_table_remove (contact_hash, removed[i]);
+
+			if (g_hash_table_size (contact_hash) < 1) {
+				g_hash_table_remove (priv->favourites,
+						     account_name);
+			}
+		}
+
+		contact = contact_manager_lookup_contact (manager, account_name,
+							  removed[i]);
+		if (contact != NULL)
+			g_signal_emit_by_name (manager, "favourites-changed",
+					       contact, FALSE);
+		else
+			DEBUG ("failed to find contact for account %s, contact "
+			       "id %s", account_name, removed[i]);
+	}
+}
+
 static void
 empathy_contact_manager_init (EmpathyContactManager *manager)
 {
 	EmpathyContactManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
 		EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv);
+	TpDBusDaemon *bus;
+	GError *error = NULL;
 
 	manager->priv = priv;
 	priv->lists = g_hash_table_new_full (empathy_proxy_hash,
 					     empathy_proxy_equal,
 					     (GDestroyNotify) g_object_unref,
 					     (GDestroyNotify) g_object_unref);
+
+	priv->favourites = g_hash_table_new_full (g_str_hash, g_str_equal,
+						  (GDestroyNotify) g_free,
+						  (GDestroyNotify)
+						  g_hash_table_unref);
+
 	priv->account_manager = tp_account_manager_dup ();
 	priv->contact_monitor = NULL;
 
 	tp_account_manager_prepare_async (priv->account_manager, NULL,
 	    account_manager_prepared_cb, manager);
+
+	bus = tp_dbus_daemon_dup (&error);
+
+	if (error == NULL) {
+		priv->logger = g_object_new (TP_TYPE_PROXY,
+				"bus-name", "org.freedesktop.Telepathy.Logger",
+				"object-path",
+					"/org/freedesktop/Telepathy/Logger",
+				"dbus-daemon", bus,
+				NULL);
+		g_object_unref (bus);
+
+		tp_proxy_add_interface_by_id (priv->logger,
+				EMP_IFACE_QUARK_LOGGER);
+
+		logger_favourite_contacts_setup (manager);
+
+		priv->favourite_contacts_changed_signal =
+			emp_cli_logger_connect_to_favourite_contacts_changed (
+				priv->logger,
+				logger_favourite_contacts_changed_cb, NULL,
+				NULL, G_OBJECT (manager), NULL);
+	} else {
+		DEBUG ("Failed to get telepathy-logger proxy: %s",
+				error->message);
+		g_clear_error (&error);
+	}
 }
 
 EmpathyContactManager *
@@ -590,6 +904,9 @@ contact_manager_iface_init (EmpathyContactListIface *iface)
 	iface->remove_from_group = contact_manager_remove_from_group;
 	iface->rename_group      = contact_manager_rename_group;
 	iface->remove_group	 = contact_manager_remove_group;
+	iface->is_favourite      = contact_manager_is_favourite;
+	iface->remove_favourite  = contact_manager_remove_favourite;
+	iface->add_favourite     = contact_manager_add_favourite;
 }
 
 EmpathyContactListFlags
