]> git.0d.be Git - empathy.git/blob - src/empathy.c
Only permit to have one running call at any time. Fixes bug #527970.
[empathy.git] / src / empathy.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  * 
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include <config.h>
24
25 #include <stdlib.h>
26 #include <errno.h>
27
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include <gtk/gtk.h>
31 #include <gdk/gdkx.h>
32
33 #include <libebook/e-book.h>
34
35 #include <telepathy-glib/util.h>
36 #include <libmissioncontrol/mc-account.h>
37 #include <libmissioncontrol/mission-control.h>
38
39 #include <libempathy/empathy-idle.h>
40 #include <libempathy/empathy-utils.h>
41 #include <libempathy/empathy-dispatcher.h>
42 #include <libempathy/empathy-tp-chat.h>
43 #include <libempathy/empathy-tp-group.h>
44
45 #include <libempathy-gtk/empathy-conf.h>
46
47 #include "empathy-main-window.h"
48 #include "empathy-status-icon.h"
49 #include "empathy-call-window.h"
50 #include "empathy-chat-window.h"
51 #include "bacon-message-connection.h"
52
53 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
54 #include <libempathy/empathy-debug.h>
55
56 static BaconMessageConnection *connection = NULL;
57
58 static void
59 dispatch_channel_cb (EmpathyDispatcher *dispatcher,
60                      TpChannel         *channel,
61                      gpointer           user_data)
62 {
63         gchar *channel_type;
64
65         g_object_get (channel, "channel-type", &channel_type, NULL);
66         if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
67                 EmpathyTpChat *tp_chat;
68                 EmpathyChat   *chat = NULL;
69                 const gchar   *id;
70
71                 tp_chat = empathy_tp_chat_new (channel);
72                 empathy_run_until_ready (tp_chat);
73
74                 id = empathy_tp_chat_get_id (tp_chat);
75                 if (!id) {
76                         EmpathyContact *contact;
77
78                         contact = empathy_tp_chat_get_remote_contact (tp_chat);
79                         if (contact) {
80                                 id = empathy_contact_get_id (contact);
81                         }
82                 }
83
84                 if (id) {
85                         McAccount *account;
86
87                         account = empathy_tp_chat_get_account (tp_chat);
88                         chat = empathy_chat_window_find_chat (account, id);
89                 }
90
91                 if (chat) {
92                         empathy_chat_set_tp_chat (chat, tp_chat);
93                 } else {
94                         chat = empathy_chat_new (tp_chat);
95                 }
96
97                 empathy_chat_window_present_chat (chat);
98                 g_object_unref (tp_chat);
99         }
100         else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) {
101                 empathy_call_window_new (channel);
102         }
103 }
104
105 static void
106 service_ended_cb (MissionControl *mc,
107                   gpointer        user_data)
108 {
109         DEBUG ("Mission Control stopped");
110 }
111
112 static void
113 operation_error_cb (MissionControl *mc,
114                     guint           operation_id,
115                     guint           error_code,
116                     gpointer        user_data)
117 {
118         const gchar *message;
119
120         switch (error_code) {
121         case MC_DISCONNECTED_ERROR:
122                 message = _("Disconnected");
123                 break;
124         case MC_INVALID_HANDLE_ERROR:
125                 message = _("Invalid handle");
126                 break;
127         case MC_NO_MATCHING_CONNECTION_ERROR:
128                 message = _("No matching connection");
129                 break;
130         case MC_INVALID_ACCOUNT_ERROR:
131                 message = _("Invalid account");
132                 break;
133         case MC_PRESENCE_FAILURE_ERROR:
134                 message = _("Presence failure");
135                 break;
136         case MC_NO_ACCOUNTS_ERROR:
137                 message = _("No accounts");
138                 break;
139         case MC_NETWORK_ERROR:
140                 message = _("Network error");
141                 break;
142         case MC_CONTACT_DOES_NOT_SUPPORT_VOICE_ERROR:
143                 message = _("Contact does not support voice");
144                 break;
145         case MC_LOWMEM_ERROR:
146                 message = _("Lowmem");
147                 break;
148         case MC_CHANNEL_REQUEST_GENERIC_ERROR:
149                 message = _("Channel request generic error");
150                 break;
151         case MC_CHANNEL_BANNED_ERROR:
152                 message = _("Channel banned");
153                 break;
154         case MC_CHANNEL_FULL_ERROR:
155                 message = _("Channel full");
156                 break;
157         case MC_CHANNEL_INVITE_ONLY_ERROR:
158                 message = _("Channel invite only");
159                 break;
160         default:
161                 message = _("Unknown error code");
162         }
163
164         DEBUG ("Error during operation %d: %s", operation_id, message);
165 }
166
167 static void
168 use_nm_notify_cb (EmpathyConf *conf,
169                   const gchar *key,
170                   gpointer     user_data)
171 {
172         EmpathyIdle *idle = user_data;
173         gboolean     use_nm;
174
175         if (empathy_conf_get_bool (conf, key, &use_nm)) {
176                 empathy_idle_set_use_nm (idle, use_nm);
177         }
178 }
179
180 static void
181 create_salut_account (void)
182 {
183         McProfile  *profile;
184         McProtocol *protocol;
185         gboolean    salut_created = FALSE;
186         McAccount  *account;
187         GList      *accounts;
188         EBook      *book;
189         EContact   *contact;
190         gchar      *nickname = NULL;
191         gchar      *first_name = NULL;
192         gchar      *last_name = NULL;
193         gchar      *email = NULL;
194         gchar      *jid = NULL;
195         GError     *error = NULL;
196
197         /* Check if we already created a salut account */
198         empathy_conf_get_bool (empathy_conf_get(),
199                                EMPATHY_PREFS_SALUT_ACCOUNT_CREATED,
200                                &salut_created);
201         if (salut_created) {
202                 return;
203         }
204
205         DEBUG ("Try to add a salut account...");
206
207         /* Check if the salut CM is installed */
208         profile = mc_profile_lookup ("salut");
209         protocol = mc_profile_get_protocol (profile);
210         if (!protocol) {
211                 DEBUG ("Salut not installed");
212                 g_object_unref (profile);
213                 return;
214         }
215         g_object_unref (protocol);
216
217         /* Get self EContact from EDS */
218         if (!e_book_get_self (&contact, &book, &error)) {
219                 DEBUG ("Failed to get self econtact: %s",
220                         error ? error->message : "No error given");
221                 g_clear_error (&error);
222                 g_object_unref (profile);
223                 return;
224         }
225
226         empathy_conf_set_bool (empathy_conf_get (),
227                                EMPATHY_PREFS_SALUT_ACCOUNT_CREATED,
228                                TRUE);
229
230         /* Check if there is already a salut account */
231         accounts = mc_accounts_list_by_profile (profile);
232         if (accounts) {
233                 DEBUG ("There is already a salut account");
234                 mc_accounts_list_free (accounts);
235                 g_object_unref (profile);
236                 return;
237         }
238
239         account = mc_account_create (profile);
240         mc_account_set_display_name (account, _("People nearby"));
241         
242         nickname = e_contact_get (contact, E_CONTACT_NICKNAME);
243         first_name = e_contact_get (contact, E_CONTACT_GIVEN_NAME);
244         last_name = e_contact_get (contact, E_CONTACT_FAMILY_NAME);
245         email = e_contact_get (contact, E_CONTACT_EMAIL_1);
246         jid = e_contact_get (contact, E_CONTACT_IM_JABBER_HOME_1);
247         
248         if (!tp_strdiff (nickname, "nickname")) {
249                 g_free (nickname);
250                 nickname = NULL;
251         }
252
253         DEBUG ("Salut account created:\nnickname=%s\nfirst-name=%s\n"
254                 "last-name=%s\nemail=%s\njid=%s\n",
255                 nickname, first_name, last_name, email, jid);
256
257         mc_account_set_param_string (account, "nickname", nickname ? nickname : "");
258         mc_account_set_param_string (account, "first-name", first_name ? first_name : "");
259         mc_account_set_param_string (account, "last-name", last_name ? last_name : "");
260         mc_account_set_param_string (account, "email", email ? email : "");
261         mc_account_set_param_string (account, "jid", jid ? jid : "");
262
263         g_free (nickname);
264         g_free (first_name);
265         g_free (last_name);
266         g_free (email);
267         g_free (jid);
268         g_object_unref (account);
269         g_object_unref (profile);
270         g_object_unref (contact);
271         g_object_unref (book);
272 }
273
274 /* The code that handles single-instance and startup notification is
275  * copied from gedit.
276  *
277  * Copyright (C) 2005 - Paolo Maggi 
278  */
279 static void
280 on_bacon_message_received (const char *message,
281                            gpointer    data)
282 {
283         GtkWidget *window = data;
284         guint32    startup_timestamp;
285
286         g_return_if_fail (message != NULL);
287
288         DEBUG ("Other instance launched, presenting the main window. message='%s'",
289                 message);
290
291         startup_timestamp = atoi (message);
292
293         /* Set the proper interaction time on the window.
294          * Fall back to roundtripping to the X server when we
295          * don't have the timestamp, e.g. when launched from
296          * terminal. We also need to make sure that the window
297          * has been realized otherwise it will not work. lame. */
298         if (startup_timestamp == 0) {
299                 /* Work if launched from the terminal */
300                 DEBUG ("Using X server timestamp as a fallback");
301
302                 if (!GTK_WIDGET_REALIZED (window)) {
303                         gtk_widget_realize (GTK_WIDGET (window));
304                 }
305
306                 startup_timestamp = gdk_x11_get_server_time (window->window);
307         }
308
309         gtk_window_present_with_time (GTK_WINDOW (window), startup_timestamp);
310 }
311
312 static guint32
313 get_startup_timestamp ()
314 {
315         const gchar *startup_id_env;
316         gchar       *startup_id = NULL;
317         gchar       *time_str;
318         gchar       *end;
319         gulong       retval = 0;
320
321         /* we don't unset the env, since startup-notification
322          * may still need it */
323         startup_id_env = g_getenv ("DESKTOP_STARTUP_ID");
324         if (startup_id_env == NULL) {
325                 goto out;
326         }
327
328         startup_id = g_strdup (startup_id_env);
329
330         time_str = g_strrstr (startup_id, "_TIME");
331         if (time_str == NULL) {
332                 goto out;
333         }
334
335         errno = 0;
336
337         /* Skip past the "_TIME" part */
338         time_str += 5;
339
340         retval = strtoul (time_str, &end, 0);
341         if (end == time_str || errno != 0)
342                 retval = 0;
343
344  out:
345         g_free (startup_id);
346
347         return (retval > 0) ? retval : 0;
348 }
349
350 int
351 main (int argc, char *argv[])
352 {
353         guint32            startup_timestamp;
354         EmpathyStatusIcon *icon;
355         EmpathyDispatcher *dispatcher;
356         GtkWidget         *window;
357         MissionControl    *mc;
358         EmpathyIdle       *idle;
359         gboolean           autoconnect = TRUE;
360         gboolean           no_connect = FALSE; 
361         GError            *error = NULL;
362         GOptionEntry       options[] = {
363                 { "no-connect", 'n',
364                   0, G_OPTION_ARG_NONE, &no_connect,
365                   N_("Don't connect on startup"),
366                   NULL },
367                 { NULL }
368         };
369
370         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
371         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
372         textdomain (GETTEXT_PACKAGE);
373
374         startup_timestamp = get_startup_timestamp ();
375
376         if (!gtk_init_with_args (&argc, &argv,
377                                  _("- Empathy Instant Messenger"),
378                                  options, GETTEXT_PACKAGE, &error)) {
379                 g_warning ("Error in gtk init: %s", error->message);
380                 return EXIT_FAILURE;
381         }
382
383         if (g_getenv ("EMPATHY_TIMING") != NULL) {
384                 g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL);
385         }
386         empathy_debug_set_flags (g_getenv ("EMPATHY_DEBUG"));
387         tp_debug_divert_messages (g_getenv ("EMPATHY_LOGFILE"));
388
389         g_set_application_name (PACKAGE_NAME);
390
391         gtk_window_set_default_icon_name ("empathy");
392         gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
393                                            PKGDATADIR G_DIR_SEPARATOR_S "icons");
394
395         /* Setting up the bacon connection */
396         connection = bacon_message_connection_new ("empathy");
397         if (connection != NULL) {
398                 if (!bacon_message_connection_get_is_server (connection)) {
399                         gchar *message;
400
401                         DEBUG ("Activating existing instance");
402
403                         message = g_strdup_printf ("%" G_GUINT32_FORMAT,
404                                                    startup_timestamp);
405                         bacon_message_connection_send (connection, message);
406
407                         /* We never popup a window, so tell startup-notification
408                          * that we are done. */
409                         gdk_notify_startup_complete ();
410
411                         g_free (message);
412                         bacon_message_connection_free (connection);
413
414                         return EXIT_SUCCESS;
415                 }
416         } else {
417                 g_warning ("Cannot create the 'empathy' bacon connection.");
418         }
419
420         /* Setting up MC */
421         mc = empathy_mission_control_new ();
422         g_signal_connect (mc, "ServiceEnded",
423                           G_CALLBACK (service_ended_cb),
424                           NULL);
425         g_signal_connect (mc, "Error",
426                           G_CALLBACK (operation_error_cb),
427                           NULL);
428
429         /* Setting up Idle */
430         idle = empathy_idle_new ();
431         empathy_idle_set_auto_away (idle, TRUE);
432         use_nm_notify_cb (empathy_conf_get (), EMPATHY_PREFS_USE_NM, idle);
433         empathy_conf_notify_add (empathy_conf_get (), EMPATHY_PREFS_USE_NM,
434                                  use_nm_notify_cb, idle);
435
436         /* Autoconnect */
437         empathy_conf_get_bool (empathy_conf_get(),
438                                EMPATHY_PREFS_AUTOCONNECT,
439                                &autoconnect);
440         if (autoconnect && ! no_connect &&
441             empathy_idle_get_state (idle) <= MC_PRESENCE_OFFLINE) {
442                 empathy_idle_set_state (idle, MC_PRESENCE_AVAILABLE);
443         }
444         
445         create_salut_account ();
446
447         /* Setting up UI */
448         window = empathy_main_window_show ();
449         icon = empathy_status_icon_new (GTK_WINDOW (window));
450
451         if (connection) {
452                 /* We se the callback here because we need window */
453                 bacon_message_connection_set_callback (connection,
454                                                        on_bacon_message_received,
455                                                        window);
456         }
457
458         /* Handle channels */
459         dispatcher = empathy_dispatcher_new ();
460         g_signal_connect (dispatcher, "dispatch-channel",
461                           G_CALLBACK (dispatch_channel_cb),
462                           NULL);
463
464         gtk_main ();
465
466         empathy_idle_set_state (idle, MC_PRESENCE_OFFLINE);
467
468         g_object_unref (mc);
469         g_object_unref (idle);
470         g_object_unref (icon);
471         g_object_unref (dispatcher);
472
473         return EXIT_SUCCESS;
474 }
475