]> git.0d.be Git - empathy.git/blob - src/empathy.c
Merge branch 'external-accounts'
[empathy.git] / src / empathy.c
1 /*
2  * Copyright (C) 2007-2009 Collabora Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA  02110-1301  USA
18  *
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <string.h>
27
28 #include <glib.h>
29 #include <glib/gstdio.h>
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkx.h>
33 #include <unique/unique.h>
34
35 #ifdef HAVE_LIBCHAMPLAIN
36 #include <clutter-gtk/clutter-gtk.h>
37 #endif
38
39 #include <libnotify/notify.h>
40
41 #include <telepathy-glib/account-manager.h>
42 #include <telepathy-glib/dbus.h>
43 #include <telepathy-glib/debug-sender.h>
44 #include <telepathy-glib/util.h>
45 #include <telepathy-glib/connection-manager.h>
46 #include <telepathy-glib/interfaces.h>
47
48 #include <telepathy-logger/log-manager.h>
49
50 #include <libempathy/empathy-idle.h>
51 #include <libempathy/empathy-utils.h>
52 #include <libempathy/empathy-chatroom-manager.h>
53 #include <libempathy/empathy-account-settings.h>
54 #include <libempathy/empathy-connectivity.h>
55 #include <libempathy/empathy-connection-managers.h>
56 #include <libempathy/empathy-dispatcher.h>
57 #include <libempathy/empathy-ft-factory.h>
58 #include <libempathy/empathy-gsettings.h>
59 #include <libempathy/empathy-tp-chat.h>
60
61 #include <libempathy-gtk/empathy-ui-utils.h>
62 #include <libempathy-gtk/empathy-location-manager.h>
63
64 #include "empathy-main-window.h"
65 #include "empathy-accounts-common.h"
66 #include "empathy-accounts-dialog.h"
67 #include "empathy-chat-manager.h"
68 #include "empathy-status-icon.h"
69 #include "empathy-ft-manager.h"
70
71 #include "extensions/extensions.h"
72
73 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
74 #include <libempathy/empathy-debug.h>
75
76 static gboolean start_hidden = FALSE;
77 static gboolean no_connect = FALSE;
78
79 static void account_manager_ready_cb (GObject *source_object,
80     GAsyncResult *result,
81     gpointer user_data);
82
83 static void
84 use_conn_notify_cb (GSettings *gsettings,
85     const gchar *key,
86     gpointer     user_data)
87 {
88   EmpathyConnectivity *connectivity = user_data;
89
90   empathy_connectivity_set_use_conn (connectivity,
91       g_settings_get_boolean (gsettings, key));
92 }
93
94 static void
95 migrate_config_to_xdg_dir (void)
96 {
97   gchar *xdg_dir, *old_dir, *xdg_filename, *old_filename;
98   int i;
99   GFile *xdg_file, *old_file;
100   static const gchar* filenames[] = {
101     "geometry.ini",
102     "irc-networks.xml",
103     "chatrooms.xml",
104     "contact-groups.xml",
105     "status-presets.xml",
106     "accels.txt",
107     NULL
108   };
109
110   xdg_dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
111   if (g_file_test (xdg_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
112     {
113       /* xdg config dir already exists */
114       g_free (xdg_dir);
115       return;
116     }
117
118   old_dir = g_build_filename (g_get_home_dir (), ".gnome2",
119       PACKAGE_NAME, NULL);
120   if (!g_file_test (old_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
121     {
122       /* old config dir didn't exist */
123       g_free (xdg_dir);
124       g_free (old_dir);
125       return;
126     }
127
128   if (g_mkdir_with_parents (xdg_dir, (S_IRUSR | S_IWUSR | S_IXUSR)) == -1)
129     {
130       DEBUG ("Failed to create configuration directory; aborting migration");
131       g_free (xdg_dir);
132       g_free (old_dir);
133       return;
134     }
135
136   for (i = 0; filenames[i]; i++)
137     {
138       old_filename = g_build_filename (old_dir, filenames[i], NULL);
139       if (!g_file_test (old_filename, G_FILE_TEST_EXISTS))
140         {
141           g_free (old_filename);
142           continue;
143         }
144       xdg_filename = g_build_filename (xdg_dir, filenames[i], NULL);
145       old_file = g_file_new_for_path (old_filename);
146       xdg_file = g_file_new_for_path (xdg_filename);
147
148       if (!g_file_move (old_file, xdg_file, G_FILE_COPY_NONE,
149           NULL, NULL, NULL, NULL))
150         DEBUG ("Failed to migrate %s", filenames[i]);
151
152       g_free (old_filename);
153       g_free (xdg_filename);
154       g_object_unref (old_file);
155       g_object_unref (xdg_file);
156     }
157
158   g_free (xdg_dir);
159   g_free (old_dir);
160 }
161
162 static void
163 show_accounts_ui (GdkScreen *screen,
164     gboolean if_needed)
165 {
166   empathy_accounts_dialog_show_application (screen,
167       NULL, if_needed, start_hidden);
168 }
169
170 static UniqueResponse
171 unique_app_message_cb (UniqueApp *unique_app,
172     gint command,
173     UniqueMessageData *data,
174     guint timestamp,
175     gpointer user_data)
176 {
177   GtkWindow *window = user_data;
178   TpAccountManager *account_manager;
179
180   DEBUG ("Other instance launched, presenting the main window. "
181       "Command=%d, timestamp %u", command, timestamp);
182
183   /* XXX: the standalone app somewhat breaks this case, since
184    * communicating it would be a pain */
185
186   /* We're requested to show stuff again, disable the start hidden global
187    * in case the accounts wizard wants to pop up.
188    */
189   start_hidden = FALSE;
190
191   gtk_window_set_screen (GTK_WINDOW (window),
192       unique_message_data_get_screen (data));
193   gtk_window_set_startup_id (GTK_WINDOW (window),
194       unique_message_data_get_startup_id (data));
195   gtk_window_present_with_time (GTK_WINDOW (window), timestamp);
196   gtk_window_set_skip_taskbar_hint (window, FALSE);
197
198   account_manager = tp_account_manager_dup ();
199   tp_account_manager_prepare_async (account_manager, NULL,
200       account_manager_ready_cb, NULL);
201   g_object_unref (account_manager);
202
203   return UNIQUE_RESPONSE_OK;
204 }
205
206 static gboolean show_version_cb (const char *option_name,
207     const char *value,
208     gpointer data,
209     GError **error) G_GNUC_NORETURN;
210
211 static gboolean
212 show_version_cb (const char *option_name,
213     const char *value,
214     gpointer data,
215     GError **error)
216 {
217   g_print ("%s\n", PACKAGE_STRING);
218
219   exit (EXIT_SUCCESS);
220 }
221
222 static void
223 new_incoming_transfer_cb (EmpathyFTFactory *factory,
224     EmpathyFTHandler *handler,
225     GError *error,
226     gpointer user_data)
227 {
228   if (error)
229     empathy_ft_manager_display_error (handler, error);
230   else
231     empathy_receive_file_with_file_chooser (handler);
232 }
233
234 static void
235 new_ft_handler_cb (EmpathyFTFactory *factory,
236     EmpathyFTHandler *handler,
237     GError *error,
238     gpointer user_data)
239 {
240   if (error)
241     empathy_ft_manager_display_error (handler, error);
242   else
243     empathy_ft_manager_add_handler (handler);
244
245   g_object_unref (handler);
246 }
247
248 static void
249 account_manager_ready_cb (GObject *source_object,
250     GAsyncResult *result,
251     gpointer user_data)
252 {
253   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
254   GError *error = NULL;
255   EmpathyIdle *idle;
256   EmpathyConnectivity *connectivity;
257   TpConnectionPresenceType presence;
258   GSettings *gsettings = g_settings_new (EMPATHY_PREFS_SCHEMA);
259
260   if (!tp_account_manager_prepare_finish (manager, result, &error))
261     {
262       GtkWidget *dialog;
263
264       DEBUG ("Failed to prepare account manager: %s", error->message);
265
266       dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
267           GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
268           _("Error contacting the Account Manager"));
269       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
270           _("There was an error while trying to connect to the Telepathy "
271             "Account Manager. The error was:\n\n%s"),
272           error->message);
273
274       gtk_dialog_run (GTK_DIALOG (dialog));
275       gtk_widget_destroy (dialog);
276
277       g_error_free (error);
278       return;
279     }
280
281   /* Autoconnect */
282   idle = empathy_idle_dup_singleton ();
283   connectivity = empathy_connectivity_dup_singleton ();
284
285   presence = tp_account_manager_get_most_available_presence (manager, NULL,
286       NULL);
287
288   if (g_settings_get_boolean (gsettings, EMPATHY_PREFS_AUTOCONNECT) &&
289       !no_connect &&
290       tp_connection_presence_type_cmp_availability
291           (presence, TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
292             <= 0)
293       /* if current state is Offline, then put it online */
294       empathy_idle_set_state (idle, TP_CONNECTION_PRESENCE_TYPE_AVAILABLE);
295
296   /* Pop up the accounts dialog if we don't have any account */
297   if (!empathy_accounts_has_accounts (manager))
298     show_accounts_ui (gdk_screen_get_default (), TRUE);
299
300   g_object_unref (idle);
301   g_object_unref (connectivity);
302   g_object_unref (gsettings);
303 }
304
305 static void
306 account_status_changed_cb (TpAccount *account,
307     guint old_status,
308     guint new_status,
309     guint reason,
310     gchar *dbus_error_name,
311     GHashTable *details,
312     EmpathyChatroom *room)
313 {
314   if (new_status != TP_CONNECTION_STATUS_CONNECTED)
315     return;
316
317   empathy_dispatcher_join_muc (account,
318       empathy_chatroom_get_room (room), TP_USER_ACTION_TIME_NOT_USER_ACTION);
319 }
320
321 static void
322 account_manager_chatroom_ready_cb (GObject *source_object,
323     GAsyncResult *result,
324     gpointer user_data)
325 {
326   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
327   EmpathyChatroomManager *chatroom_manager = user_data;
328   GList *accounts, *l;
329   GError *error = NULL;
330
331   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
332     {
333       DEBUG ("Failed to prepare account manager: %s", error->message);
334       g_error_free (error);
335       return;
336     }
337
338   accounts = tp_account_manager_get_valid_accounts (account_manager);
339
340   for (l = accounts; l != NULL; l = g_list_next (l))
341     {
342       TpAccount *account = TP_ACCOUNT (l->data);
343       TpConnection *conn;
344       GList *chatrooms, *p;
345
346       conn = tp_account_get_connection (account);
347
348       chatrooms = empathy_chatroom_manager_get_chatrooms (
349           chatroom_manager, account);
350
351       for (p = chatrooms; p != NULL; p = p->next)
352         {
353           EmpathyChatroom *room = EMPATHY_CHATROOM (p->data);
354
355           if (!empathy_chatroom_get_auto_connect (room))
356             continue;
357
358           if (conn == NULL)
359             {
360               g_signal_connect (G_OBJECT (account), "status-changed",
361                   G_CALLBACK (account_status_changed_cb), room);
362             }
363           else
364             {
365               empathy_dispatcher_join_muc (account,
366                   empathy_chatroom_get_room (room),
367                   TP_USER_ACTION_TIME_NOT_USER_ACTION);
368             }
369         }
370
371       g_list_free (chatrooms);
372     }
373
374   g_list_free (accounts);
375 }
376
377 static void
378 chatroom_manager_ready_cb (EmpathyChatroomManager *chatroom_manager,
379     GParamSpec *pspec,
380     gpointer user_data)
381 {
382   TpAccountManager *account_manager = user_data;
383
384   tp_account_manager_prepare_async (account_manager, NULL,
385       account_manager_chatroom_ready_cb, chatroom_manager);
386 }
387
388 static void
389 empathy_idle_set_auto_away_cb (GSettings *gsettings,
390                                 const gchar *key,
391                                 gpointer user_data)
392 {
393         EmpathyIdle *idle = user_data;
394
395         empathy_idle_set_auto_away (idle,
396       g_settings_get_boolean (gsettings, key));
397 }
398
399 int
400 main (int argc, char *argv[])
401 {
402 #ifdef HAVE_GEOCLUE
403   EmpathyLocationManager *location_manager = NULL;
404 #endif
405   EmpathyStatusIcon *icon;
406   EmpathyDispatcher *dispatcher;
407   TpAccountManager *account_manager;
408   TplLogManager *log_manager;
409   EmpathyChatroomManager *chatroom_manager;
410   EmpathyFTFactory  *ft_factory;
411   GtkWidget *window;
412   EmpathyIdle *idle;
413   EmpathyConnectivity *connectivity;
414   EmpathyChatManager *chat_manager;
415   GError *error = NULL;
416   UniqueApp *unique_app;
417   gboolean chatroom_manager_ready;
418   gboolean autoaway = TRUE;
419 #ifdef ENABLE_DEBUG
420   TpDebugSender *debug_sender;
421 #endif
422   GSettings *gsettings;
423
424   GOptionContext *optcontext;
425   GOptionEntry options[] = {
426       { "no-connect", 'n',
427         0, G_OPTION_ARG_NONE, &no_connect,
428         N_("Don't connect on startup"),
429         NULL },
430       { "start-hidden", 'h',
431         0, G_OPTION_ARG_NONE, &start_hidden,
432         N_("Don't display the contact list or any other dialogs on startup"),
433         NULL },
434       { "version", 'v',
435         G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_version_cb,
436         NULL, NULL },
437       { NULL }
438   };
439
440   /* Init */
441   g_thread_init (NULL);
442
443 #ifdef HAVE_LIBCHAMPLAIN
444   gtk_clutter_init (&argc, &argv);
445 #endif
446
447   empathy_init ();
448
449   optcontext = g_option_context_new (N_("- Empathy IM Client"));
450   g_option_context_add_group (optcontext, gtk_get_option_group (TRUE));
451   g_option_context_add_main_entries (optcontext, options, GETTEXT_PACKAGE);
452
453   if (!g_option_context_parse (optcontext, &argc, &argv, &error)) {
454     g_print ("%s\nRun '%s --help' to see a full list of available command line options.\n",
455         error->message, argv[0]);
456     g_warning ("Error in empathy init: %s", error->message);
457     return EXIT_FAILURE;
458   }
459
460   g_option_context_free (optcontext);
461
462   empathy_gtk_init ();
463   g_set_application_name (_(PACKAGE_NAME));
464
465   gtk_window_set_default_icon_name ("empathy");
466   textdomain (GETTEXT_PACKAGE);
467
468 #ifdef ENABLE_DEBUG
469   /* Set up debug sender */
470   debug_sender = tp_debug_sender_dup ();
471   g_log_set_default_handler (tp_debug_sender_log_handler, G_LOG_DOMAIN);
472 #endif
473
474   unique_app = unique_app_new ("org.gnome."PACKAGE_NAME, NULL);
475
476   if (unique_app_is_running (unique_app))
477     {
478       if (unique_app_send_message (unique_app, UNIQUE_ACTIVATE, NULL) ==
479           UNIQUE_RESPONSE_OK)
480         {
481           g_object_unref (unique_app);
482           return EXIT_SUCCESS;
483         }
484     }
485
486   notify_init (_(PACKAGE_NAME));
487
488   /* Setting up Idle */
489   idle = empathy_idle_dup_singleton ();
490
491   gsettings = g_settings_new (EMPATHY_PREFS_SCHEMA);
492   autoaway = g_settings_get_boolean (gsettings, EMPATHY_PREFS_AUTOAWAY);
493
494   g_signal_connect (gsettings,
495       "changed::" EMPATHY_PREFS_AUTOAWAY,
496       G_CALLBACK (empathy_idle_set_auto_away_cb), idle);
497
498   empathy_idle_set_auto_away (idle, autoaway);
499
500   /* Setting up Connectivity */
501   connectivity = empathy_connectivity_dup_singleton ();
502   use_conn_notify_cb (gsettings, EMPATHY_PREFS_USE_CONN,
503       connectivity);
504   g_signal_connect (gsettings,
505       "changed::" EMPATHY_PREFS_USE_CONN,
506       G_CALLBACK (use_conn_notify_cb), connectivity);
507
508   /* account management */
509   account_manager = tp_account_manager_dup ();
510   tp_account_manager_prepare_async (account_manager, NULL,
511       account_manager_ready_cb, NULL);
512
513   /* The EmpathyDispatcher doesn't dispatch anything any more but we have to
514    * keep it around as we still use it to request channels */
515   dispatcher = empathy_dispatcher_dup_singleton ();
516
517   migrate_config_to_xdg_dir ();
518
519   /* Setting up UI */
520   window = empathy_main_window_dup ();
521   gtk_widget_show (window);
522   icon = empathy_status_icon_new (GTK_WINDOW (window), start_hidden);
523
524   /* Chat manager */
525   chat_manager = empathy_chat_manager_dup_singleton ();
526
527   g_signal_connect (unique_app, "message-received",
528       G_CALLBACK (unique_app_message_cb), window);
529
530   /* Logging */
531   log_manager = tpl_log_manager_dup_singleton ();
532
533   chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
534
535   g_object_get (chatroom_manager, "ready", &chatroom_manager_ready, NULL);
536   if (!chatroom_manager_ready)
537     {
538       g_signal_connect (G_OBJECT (chatroom_manager), "notify::ready",
539           G_CALLBACK (chatroom_manager_ready_cb), account_manager);
540     }
541   else
542     {
543       chatroom_manager_ready_cb (chatroom_manager, NULL, account_manager);
544     }
545
546   /* Create the FT factory */
547   ft_factory = empathy_ft_factory_dup_singleton ();
548   g_signal_connect (ft_factory, "new-ft-handler",
549       G_CALLBACK (new_ft_handler_cb), NULL);
550   g_signal_connect (ft_factory, "new-incoming-transfer",
551       G_CALLBACK (new_incoming_transfer_cb), NULL);
552
553   if (!empathy_ft_factory_register (ft_factory, &error))
554     {
555       g_warning ("Failed to register FileTransfer handler: %s", error->message);
556       g_error_free (error);
557     }
558
559   /* Location mananger */
560 #ifdef HAVE_GEOCLUE
561   location_manager = empathy_location_manager_dup_singleton ();
562 #endif
563
564   gtk_main ();
565
566   empathy_idle_set_state (idle, TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
567
568 #ifdef ENABLE_DEBUG
569   g_object_unref (debug_sender);
570 #endif
571
572   g_object_unref (chat_manager);
573   g_object_unref (idle);
574   g_object_unref (connectivity);
575   g_object_unref (icon);
576   g_object_unref (account_manager);
577   g_object_unref (log_manager);
578   g_object_unref (dispatcher);
579   g_object_unref (chatroom_manager);
580 #ifdef HAVE_GEOCLUE
581   g_object_unref (location_manager);
582 #endif
583   g_object_unref (ft_factory);
584   g_object_unref (unique_app);
585   g_object_unref (gsettings);
586   gtk_widget_destroy (window);
587
588   notify_uninit ();
589   xmlCleanupParser ();
590
591   return EXIT_SUCCESS;
592 }