之前一直不知道怎么把这个记录给删掉,烦死了。可不能留下私密信息在这里阿,就下载了Empathy源码下来看一下检查一下。好像Empathy的文档没有有,我没仔细看,反正网上的文档搜到的文档说的位置是错的,可能因为Empathy 更新了吧。
根据代码,Empathy使用的是 Telepathy 框架,所以这个 聊天记录使用的也是“Telepathy Logger”这个模块来作的。Telepathy Logger的文档在 http://telepathy.freedesktop.org/wiki/Logger ,提到
--------------
	It stores its logs to
${XDG_DATA_HOME}/share/TpLogger
which usually expands to at
 ${HOME}/.local/share/TpLogger/
	--------------------
我到我自己的 home 目录 /home/widebright/.local/share/TpLogger/logs
	接下来一看,果然聊天记录都在这里,yahoo messager 和spark的记录都保存在 帐号名字/联系人名字/聊天日期.log 这样的子目录的文件里面,明文 的xml格式的。
	我把把这接下来的东西都清空了。然后在Empathy 里面设置了不保存密码。这下终于放心了。
看代码的时候,发现这个Empathy的聊天记录界面,还是用webkit来做的。很有意思,有的树型节点的操作、显示那些都是写在html的javascript里面,然后在gtk里面调用的,很有意思。一旦想在gtk窗口上面嵌入一个浏览器窗口作显示的,也可以参考这个代码吧。我之前就不知道这个webkit还能这样用的。
-------------------------------
	./empathy-main-window.c
static void
	main_window_view_history_cb (GtkAction         *action,
	                 EmpathyMainWindow *window)
	{
	    empathy_log_window_show (NULL, NULL, FALSE, GTK_WINDOW (window));
	}
-------------------------------------------------
	./libempathy-gtk/empathy-log-window.c:empathy_log_window_show (TpAccount *account,
GtkWidget *
	empathy_log_window_show (TpAccount *account,
	     const gchar *chat_id,
	     gboolean is_chatroom,
	     GtkWindow *parent)
	{
	  log_window = g_object_new (EMPATHY_TYPE_LOG_WINDOW, NULL);
gtk_window_present (GTK_WINDOW (log_window));
  if (account != NULL && chat_id != NULL)
	    select_account_once_ready (log_window, account, chat_id, is_chatroom);
  if (parent != NULL)
	    gtk_window_set_transient_for (GTK_WINDOW (log_window),
	        GTK_WINDOW (parent));
  return GTK_WIDGET (log_window);
	}
static void
	empathy_log_window_init (EmpathyLogWindow *self)
	{
	  EmpathyAccountChooser *account_chooser;
	  GtkBuilder *gui;
	  gchar *filename;
	  GFile *gfile;
	  GtkWidget *vbox, *accounts, *search, *label, *closeitem;
	  GtkWidget *scrolledwindow_events;
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
	      EMPATHY_TYPE_LOG_WINDOW, EmpathyLogWindowPriv);
self->priv->chain = _tpl_action_chain_new_async (NULL, NULL, NULL);
self->priv->camera_monitor = empathy_camera_monitor_dup_singleton ();
self->priv->log_manager = tpl_log_manager_dup_singleton ();
  self->priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
	  self->priv->gsettings_desktop = g_settings_new (
	      EMPATHY_PREFS_DESKTOP_INTERFACE_SCHEMA);
  gtk_window_set_title (GTK_WINDOW (self), _("History"));
	  gtk_widget_set_can_focus (GTK_WIDGET (self), FALSE);
	  gtk_window_set_default_size (GTK_WINDOW (self), 800, 600);
  filename = empathy_file_lookup ("empathy-log-window.ui", "libempathy-gtk");
	  gui = empathy_builder_get_file (filename,
	      "vbox1", &self->priv->vbox,
	      "toolbutton_profile", &self->priv->button_profile,
	      "toolbutton_chat", &self->priv->button_chat,
	      "toolbutton_call", &self->priv->button_call,
	      "toolbutton_video", &self->priv->button_video,
	      "toolbutton_accounts", &accounts,
	      "toolbutton_search", &search,
	      "imagemenuitem_close", &closeitem,
	      "treeview_who", &self->priv->treeview_who,
	      "treeview_what", &self->priv->treeview_what,
	      "treeview_when", &self->priv->treeview_when,
	      "scrolledwindow_events", &scrolledwindow_events,
	      "notebook", &self->priv->notebook,
	      "spinner", &self->priv->spinner,
	      NULL);
	  g_free (filename);
  empathy_builder_connect (gui, self,
	      "toolbutton_profile", "clicked", toolbutton_profile_clicked,
	      "toolbutton_chat", "clicked", toolbutton_chat_clicked,
	      "toolbutton_call", "clicked", toolbutton_av_clicked,
	      "toolbutton_video", "clicked", toolbutton_av_clicked,
	      "imagemenuitem_delete", "activate", log_window_delete_menu_clicked_cb,
	      NULL);
gtk_container_add (GTK_CONTAINER (self), self->priv->vbox);
g_object_unref (gui);
  g_signal_connect_swapped (closeitem, "activate",
	      G_CALLBACK (gtk_widget_destroy), self);
  /* Account chooser for chats */
	  vbox = gtk_vbox_new (FALSE, 3);
  self->priv->account_chooser = empathy_account_chooser_new ();
	  account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
	  empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
	  empathy_account_chooser_set_filter (account_chooser,
	      empathy_account_chooser_filter_has_logs, NULL);
	  empathy_account_chooser_set_all (account_chooser);
  gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->account_chooser),
	                               GTK_STYLE_CLASS_RAISED);
  g_signal_connect (self->priv->account_chooser, "changed",
	      G_CALLBACK (log_window_chats_accounts_changed_cb),
	      self);
label = gtk_label_new (_("Show"));
  gtk_box_pack_start (GTK_BOX (vbox),
	      self->priv->account_chooser,
	      FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox),
	      label,
	      FALSE, FALSE, 0);
  gtk_widget_show_all (vbox);
	  gtk_container_add (GTK_CONTAINER (accounts), vbox);
  /* Search entry */
	  vbox = gtk_vbox_new (FALSE, 3);
  self->priv->search_entry = gtk_entry_new ();
	  gtk_entry_set_icon_from_icon_name (GTK_ENTRY (self->priv->search_entry),
	      GTK_ENTRY_ICON_SECONDARY, "edit-find-symbolic");
	  gtk_entry_set_icon_sensitive (GTK_ENTRY (self->priv->search_entry),
	                                GTK_ENTRY_ICON_SECONDARY, FALSE);
label = gtk_label_new (_("Search"));
  gtk_box_pack_start (GTK_BOX (vbox),
	      self->priv->search_entry,
	      FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox),
	      label,
	      FALSE, FALSE, 0);
  gtk_widget_show_all (vbox);
	  gtk_container_add (GTK_CONTAINER (search), vbox);
  g_signal_connect (self->priv->search_entry, "changed",
	      G_CALLBACK (log_window_search_entry_changed_cb),
	      self);
  g_signal_connect (self->priv->search_entry, "activate",
	      G_CALLBACK (log_window_search_entry_activate_cb),
	      self);
  g_signal_connect (self->priv->search_entry, "icon-press",
	      G_CALLBACK (log_window_search_entry_icon_pressed_cb),
	      self);
  /* Contacts */
	  log_window_events_setup (self);
	  log_window_who_setup (self);
	  log_window_what_setup (self);
	  log_window_when_setup (self);
log_window_create_observer (self);
log_window_who_populate (self);
  /* events */
	  self->priv->webview = webkit_web_view_new ();
	  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow_events),
	      GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	  gtk_container_add (GTK_CONTAINER (scrolledwindow_events),
	      self->priv->webview);
	  gtk_widget_show (self->priv->webview);
  empathy_webkit_bind_font_setting (WEBKIT_WEB_VIEW (self->priv->webview),
	      self->priv->gsettings_desktop,
	      EMPATHY_PREFS_DESKTOP_INTERFACE_FONT_NAME);
  filename = empathy_file_lookup ("empathy-log-window.html", "data");
	  gfile = g_file_new_for_path (filename);
	  g_free (filename);
  webkit_web_view_load_uri (WEBKIT_WEB_VIEW (self->priv->webview),
	      g_file_get_uri (gfile));
	  g_object_unref (gfile);
  /* handle all navigation externally */
	  g_signal_connect (self->priv->webview, "navigation-policy-decision-requested",
	      G_CALLBACK (events_webview_handle_navigation), self);
  /* listen to changes to the treemodel */
	  g_signal_connect (self->priv->store_events, "row-inserted",
	      G_CALLBACK (store_events_row_inserted), self);
	  g_signal_connect (self->priv->store_events, "row-changed",
	      G_CALLBACK (store_events_row_changed), self);
	  g_signal_connect (self->priv->store_events, "row-deleted",
	      G_CALLBACK (store_events_row_deleted), self);
	  g_signal_connect (self->priv->store_events, "rows-reordered",
	      G_CALLBACK (store_events_rows_reordered), self);
	  g_signal_connect (self->priv->store_events, "row-has-child-toggled",
	      G_CALLBACK (store_events_has_child_rows), self);
  /* track clicked row */
	  g_signal_connect (self->priv->webview, "button-press-event",
	      G_CALLBACK (log_window_events_button_press_event), self);
  log_window_update_buttons_sensitivity (self);
	  gtk_widget_show (GTK_WIDGET (self));
	}
static void
	log_window_who_populate (EmpathyLogWindow *self)
	{
	  EmpathyAccountChooser *account_chooser;
	  TpAccount *account;
	  gboolean all_accounts;
	  GtkTreeView *view;
	  GtkTreeModel *model;
	  GtkTreeSelection *selection;
	  GtkListStore *store;
	  Ctx *ctx;
  if (self->priv->hits != NULL)
	    {
	      populate_entities_from_search_hits ();
	      return;
	    }
  account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
	  account = empathy_account_chooser_dup_account (account_chooser);
	  all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
  view = GTK_TREE_VIEW (self->priv->treeview_who);
	  model = gtk_tree_view_get_model (view);
	  selection = gtk_tree_view_get_selection (view);
	  store = GTK_LIST_STORE (model);
  /* Block signals to stop the logs being retrieved prematurely  */
	  g_signal_handlers_block_by_func (selection,
	      log_window_who_changed_cb,
	      self);
gtk_list_store_clear (store);
  /* Unblock signals */
	  g_signal_handlers_unblock_by_func (selection,
	      log_window_who_changed_cb,
	      self);
  _tpl_action_chain_clear (self->priv->chain);
	  self->priv->count++;
  if (!all_accounts && account == NULL)
	    {
	      return;
	    }
	  else if (!all_accounts)
	    {
	      ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
	      _tpl_action_chain_append (self->priv->chain, get_entities_for_account, ctx);
	    }
	  else
	    {
	      TpAccountManager *manager;
	      GList *accounts, *l;
      manager = empathy_account_chooser_get_account_manager (account_chooser);
	      accounts = tp_account_manager_get_valid_accounts (manager);
      for (l = accounts; l != NULL; l = l->next)
	        {
	          account = l->data;
          ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
	          _tpl_action_chain_append (self->priv->chain,
	              get_entities_for_account, ctx);
	        }
      g_list_free (accounts);
	    }
	  _tpl_action_chain_append (self->priv->chain, select_first_entity, self);
	  _tpl_action_chain_start (self->priv->chain);
	}
static void
	log_window_update_what_sensitivity (EmpathyLogWindow *self)
	{
	  GtkTreeView *view;
	  GtkTreeModel *model;
	  GtkTreeIter iter;
	  GList *accounts, *targets, *acc, *targ;
	  gboolean next;
  if (!log_window_get_selected (self, &accounts, &targets, NULL, NULL,
	      NULL, NULL))
	    return;
  view = GTK_TREE_VIEW (self->priv->treeview_what);
	  model = gtk_tree_view_get_model (view);
  /* For each event type... */
	  for (next = gtk_tree_model_get_iter_first (model, &iter);
	       next;
	       next = gtk_tree_model_iter_next (model, &iter))
	    {
	      TplEventTypeMask type;
      gtk_tree_model_get (model, &iter,
	          COL_WHAT_TYPE, &type,
	          -1);
      /* ...we set the type and its subtypes (if any) unsensitive... */
	      log_window_update_what_iter_sensitivity (model, &iter, FALSE);
      for (acc = accounts, targ = targets;
	           acc != NULL && targ != NULL;
	           acc = acc->next, targ = targ->next)
	        {
	          TpAccount *account = acc->data;
	          TplEntity *target = targ->data;
          if (tpl_log_manager_exists (self->priv->log_manager,
	                  account, target, type))
	            {
	              /* And then we set it (and its subtypes, again, if any)
	               * as sensitive if there are logs of that type. */
	              log_window_update_what_iter_sensitivity (model, &iter, TRUE);
	              break;
	            }
	        }
	    }
  g_list_free_full (accounts, g_object_unref);
	  g_list_free_full (targets, g_object_unref);
	}
static void
	log_window_who_changed_cb (GtkTreeSelection *selection,
	    EmpathyLogWindow *self)
	{
	  GtkTreeView *view;
	  GtkTreeModel *model;
	  GtkTreeIter iter;
DEBUG ("log_window_who_changed_cb");
  view = gtk_tree_selection_get_tree_view (selection);
	  model = gtk_tree_view_get_model (view);
  if (gtk_tree_model_get_iter_first (model, &iter))
	    {
	      /* If 'Anyone' is selected, everything else should be deselected */
	      if (gtk_tree_selection_iter_is_selected (selection, &iter))
	        {
	          g_signal_handlers_block_by_func (selection,
	              log_window_who_changed_cb,
	              self);
          gtk_tree_selection_unselect_all (selection);
	          gtk_tree_selection_select_iter (selection, &iter);
          g_signal_handlers_unblock_by_func (selection,
	              log_window_who_changed_cb,
	              self);
	        }
	    }
  log_window_update_what_sensitivity (self);
	  log_window_update_buttons_sensitivity (self);
  /* The contact changed, so the dates need to be updated */
	  log_window_chats_get_messages (self, TRUE);
	}
static void
	log_manager_got_entities_cb (GObject *manager,
	    GAsyncResult *result,
	    gpointer user_data)
	{
	  Ctx                   *ctx = user_data;
	  GList                 *entities;
	  GList                 *l;
	  GtkTreeView           *view;
	  GtkTreeModel          *model;
	  GtkTreeSelection      *selection;
	  GtkListStore          *store;
	  GtkTreeIter            iter;
	  GError                *error = NULL;
	  gboolean               select_account = FALSE;
  if (log_window == NULL)
	    goto out;
  if (log_window->priv->count != ctx->count)
	    goto out;
  if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
	      result, &entities, &error))
	    {
	      DEBUG ("%s. Aborting", error->message);
	      g_error_free (error);
	      goto out;
	    }
  view = GTK_TREE_VIEW (ctx->self->priv->treeview_who);
	  model = gtk_tree_view_get_model (view);
	  selection = gtk_tree_view_get_selection (view);
	  store = GTK_LIST_STORE (model);
  /* Block signals to stop the logs being retrieved prematurely  */
	  g_signal_handlers_block_by_func (selection,
	      log_window_who_changed_cb, ctx->self);
  for (l = entities; l; l = l->next)
	    {
	      TplEntity *entity = TPL_ENTITY (l->data);
	      TplEntityType type = tpl_entity_get_entity_type (entity);
	      EmpathyContact *contact;
	      const gchar *name;
	      gchar *sort_key;
	      gboolean room = type == TPL_ENTITY_ROOM;
contact = empathy_contact_from_tpl_contact (ctx->account, entity);
      name = empathy_contact_get_alias (contact);
	      sort_key = g_utf8_collate_key (name, -1);
      gtk_list_store_append (store, &iter);
	      gtk_list_store_set (store, &iter,
	          COL_WHO_TYPE, COL_TYPE_NORMAL,
	          COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
	                             : EMPATHY_IMAGE_AVATAR_DEFAULT,
	          COL_WHO_NAME, name,
	          COL_WHO_NAME_SORT_KEY, sort_key,
	          COL_WHO_ID, tpl_entity_get_identifier (entity),
	          COL_WHO_ACCOUNT, ctx->account,
	          COL_WHO_TARGET, entity,
	          -1);
      g_free (sort_key);
	      g_object_unref (contact);
      if (ctx->self->priv->selected_account != NULL &&
	          !tp_strdiff (tp_proxy_get_object_path (ctx->account),
	          tp_proxy_get_object_path (ctx->self->priv->selected_account)))
	        select_account = TRUE;
	    }
	  g_list_free_full (entities, g_object_unref);
  if (gtk_tree_model_get_iter_first (model, &iter))
	    {
	      gint type;
      gtk_tree_model_get (model, &iter,
	          COL_WHO_TYPE, &type,
	          -1);
      if (type != COL_TYPE_ANY)
	        {
	          gtk_list_store_prepend (store, &iter);
	          gtk_list_store_set (store, &iter,
	              COL_WHO_TYPE, COL_TYPE_SEPARATOR,
	              COL_WHO_NAME, "separator",
	              -1);
          gtk_list_store_prepend (store, &iter);
	          gtk_list_store_set (store, &iter,
	              COL_WHO_TYPE, COL_TYPE_ANY,
	              COL_WHO_NAME, _("Anyone"),
	              -1);
	        }
	    }
  /* Unblock signals */
	  g_signal_handlers_unblock_by_func (selection,
	      log_window_who_changed_cb,
	      ctx->self);
  /* We display the selected account if we populate the model with chats from
	   * this account. */
	  if (select_account)
	    log_window_chats_set_selected (ctx->self);
out:
	  _tpl_action_chain_continue (log_window->priv->chain);
	  ctx_free (ctx);
	}
static void
	get_entities_for_account (TplActionChain *chain, gpointer user_data)
	{
	  Ctx *ctx = user_data;
  tpl_log_manager_get_entities_async (ctx->self->priv->log_manager, ctx->account,
	      log_manager_got_entities_cb, ctx);
	}
static void
	select_first_entity (TplActionChain *chain, gpointer user_data)
	{
	  EmpathyLogWindow *self = user_data;
	  GtkTreeView *view;
	  GtkTreeModel *model;
	  GtkTreeSelection *selection;
	  GtkTreeIter iter;
  view = GTK_TREE_VIEW (self->priv->treeview_who);
	  model = gtk_tree_view_get_model (view);
	  selection = gtk_tree_view_get_selection (view);
  if (gtk_tree_model_get_iter_first (model, &iter))
	    gtk_tree_selection_select_iter (selection, &iter);
  _tpl_action_chain_continue (self->priv->chain);
	}
static void
	log_window_who_populate (EmpathyLogWindow *self)
	{
	  EmpathyAccountChooser *account_chooser;
	  TpAccount *account;
	  gboolean all_accounts;
	  GtkTreeView *view;
	  GtkTreeModel *model;
	  GtkTreeSelection *selection;
	  GtkListStore *store;
	  Ctx *ctx;
  if (self->priv->hits != NULL)
	    {
	      populate_entities_from_search_hits ();
	      return;
	    }
  account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
	  account = empathy_account_chooser_dup_account (account_chooser);
	  all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
  view = GTK_TREE_VIEW (self->priv->treeview_who);
	  model = gtk_tree_view_get_model (view);
	  selection = gtk_tree_view_get_selection (view);
	  store = GTK_LIST_STORE (model);
  /* Block signals to stop the logs being retrieved prematurely  */
	  g_signal_handlers_block_by_func (selection,
	      log_window_who_changed_cb,
	      self);
gtk_list_store_clear (store);
  /* Unblock signals */
	  g_signal_handlers_unblock_by_func (selection,
	      log_window_who_changed_cb,
	      self);
  _tpl_action_chain_clear (self->priv->chain);
	  self->priv->count++;
  if (!all_accounts && account == NULL)
	    {
	      return;
	    }
	  else if (!all_accounts)
	    {
	      ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
	      _tpl_action_chain_append (self->priv->chain, get_entities_for_account, ctx);
	    }
	  else
	    {
	      TpAccountManager *manager;
	      GList *accounts, *l;
      manager = empathy_account_chooser_get_account_manager (account_chooser);
	      accounts = tp_account_manager_get_valid_accounts (manager);
      for (l = accounts; l != NULL; l = l->next)
	        {
	          account = l->data;
          ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
	          _tpl_action_chain_append (self->priv->chain,
	              get_entities_for_account, ctx);
	        }
      g_list_free (accounts);
	    }
	  _tpl_action_chain_append (self->priv->chain, select_first_entity, self);
	  _tpl_action_chain_start (self->priv->chain);
	}
------------------------------------------------------------------------
www@www:~/桌面/empathy-3.2.0$ grep TplLogManager -r ./
	./libempathy-gtk/empathy-log-window.c:  TplLogManager *manager = tpl_log_manager_dup_singleton ();
 ----------------------------
	Telepathy Logger
http://telepathy.freedesktop.org/wiki/Logger
It stores its logs to
${XDG_DATA_HOME}/share/TpLogger
which usually expands to at
${HOME}/.local/share/TpLogger/
See specs for more XDG_DATA_HOME details.