attempt DBUS_GLIB fix
[moonshot-ui.git] / src / moonshot-identity-manager-app.vala
1 using Gee;
2 using Gtk;
3
4 #if IPC_DBUS
5 [DBus (name = "org.janet.Moonshot")]
6 interface IIdentityManager : GLib.Object {
7 #if IPC_DBUS_GLIB
8     public abstract bool show_ui() throws DBus.Error;
9 #else
10     public abstract bool show_ui() throws IOError;
11 #endif
12 }
13 #endif
14
15 public class IdentityManagerApp {
16     public IdentityManagerModel model;
17     public IdCard default_id_card;
18     public bool explicitly_launched;
19     public IdentityManagerView view;
20     private MoonshotServer ipc_server;
21
22 #if OS_MACOS
23         public OSXApplication osxApp;
24   
25     // the signal handler function.
26     // the current instance of our app class is passed in the 
27     // id_manager_app_instanceparameter 
28         public static bool on_osx_open_files (OSXApplication osx_app_instance, 
29                                         string file_name, 
30                                         IdentityManagerApp id_manager_app_instance ) {
31     int added_cards = id_manager_app_instance.ipc_server.install_from_file(file_name);
32     return true;
33         }
34 #endif
35
36     private const int WINDOW_WIDTH = 400;
37     private const int WINDOW_HEIGHT = 500;
38     public void show() {
39         if (view != null) view.show();    
40     }
41         
42     public IdentityManagerApp (bool headless) {
43         model = new IdentityManagerModel(this);
44         if (!headless)
45             view = new IdentityManagerView(this);
46         LinkedList<IdCard> card_list = model.get_card_list() ;
47         if (card_list.size > 0)
48             this.default_id_card = card_list.first();
49
50         init_ipc_server ();
51
52 #if OS_MACOS
53
54         osxApp = OSXApplication.get_instance();
55         // The 'correct' way of connrcting wont work in Mac OS with Vala 0.12   e.g.    
56         //              osxApp.ns_application_open_file.connect(install_from_file);
57         // so we have to use this old way
58         Signal.connect(osxApp, "NSApplicationOpenFile", (GLib.Callback)(on_osx_open_files), this);
59
60 #endif
61     }
62
63     public bool add_identity (IdCard id) {
64         if (view != null) return view.add_identity(id);
65         model.add_card(id);
66         return true;
67     }
68
69     public void select_identity (IdentityRequest request) {
70         IdCard identity = null;
71
72         if (request.select_default)
73         {
74             identity = default_id_card;
75         }
76
77         if (identity == null)
78         {
79             bool has_nai = request.nai != null && request.nai != "";
80             bool has_srv = request.service != null && request.service != "";
81             bool confirm = false;
82             IdCard nai_provided = null;
83
84             foreach (IdCard id in model.get_card_list())
85             {
86                 /* If NAI matches we add id card to the candidate list */
87                 if (has_nai && request.nai == id.nai)
88                 {
89                     nai_provided = id;
90                     request.candidates.append (id);
91                     continue;
92                 }
93
94                 /* If any service matches we add id card to the candidate list */
95                 if (has_srv)
96                 {
97                     foreach (string srv in id.services)
98                     {
99                         if (request.service == srv)
100                         {
101                             request.candidates.append (id);
102                             continue;
103                         }
104                     }
105                 }
106             }
107
108             /* If more than one candidate we dissasociate service from all ids */
109             if (has_srv && request.candidates.length() > 1)
110             {
111                 foreach (IdCard id in request.candidates)
112                 {
113                     int i = 0;
114                     SList<string> services_list = null;
115                     bool has_service = false;
116
117                     foreach (string srv in id.services)
118                     {
119                         if (srv == request.service)
120                         {
121                             has_service = true;
122                             continue;
123                         }
124                         services_list.append (srv);
125                     }
126                     
127                     if (!has_service)
128                         continue;
129
130                     if (services_list.length () == 0)
131                     {
132                         id.services = {};
133                         continue;
134                     }
135
136                     string[] services = new string[services_list.length ()];
137                     foreach (string srv in services_list)
138                     {
139                         services[i] = srv;
140                         i++;
141                     }
142
143                     id.services = services;
144                 }
145             }
146
147 //            model.store_id_cards ();
148
149             /* If there are no candidates we use the service matching rules */
150             if (request.candidates.length () == 0)
151             {
152                 foreach (IdCard id in model.get_card_list())
153                 {
154                     foreach (Rule rule in id.rules)
155                     {
156                         if (!match_service_pattern (request.service, rule.pattern))
157                             continue;
158
159                         request.candidates.append (id);
160
161                         if (rule.always_confirm == "true")
162                             confirm = true;
163                     }
164                 }
165             }
166             
167             if (request.candidates.length () > 1)
168             {
169                 if (has_nai && nai_provided != null)
170                 {
171                     identity = nai_provided;
172                     confirm = false;
173                 }
174                 else
175                     confirm = true;
176             }
177             if (identity == null)
178                 identity = request.candidates.nth_data (0);
179             if (identity == null)
180                 confirm = true;
181
182             /* TODO: If candidate list empty return fail */
183             
184             if (confirm && (view != null))
185             {
186                 if (!explicitly_launched)
187                     show();
188                 view.queue_identity_request(request);
189                 return;
190             }
191         }
192         // Send back the identity (we can't directly run the
193         // callback because we may be being called from a 'yield')
194         Idle.add(
195             () => {
196                 request.return_identity (identity);
197 // The following occasionally causes the app to exit without sending the dbus
198 // reply, so for now we just don't exit
199 //                if (!explicitly_launched)
200 //                    Idle.add( () => { Gtk.main_quit(); return false; } );
201                 return false;
202             }
203         );
204         return;
205     }
206
207     private bool match_service_pattern (string service, string pattern)
208     {
209         var pspec = new PatternSpec (pattern);
210         return pspec.match_string (service);
211     }   
212     
213 #if IPC_MSRPC
214     private void init_ipc_server ()
215     {
216         // Errors will currently be sent via g_log - ie. to an
217         // obtrusive message box, on Windows
218         //
219         this.ipc_server = MoonshotServer.get_instance ();
220         MoonshotServer.start (this);
221     }
222 #elif IPC_DBUS_GLIB
223     private void init_ipc_server ()
224     {
225         try {
226             var conn = DBus.Bus.get (DBus.BusType.SESSION);
227             dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus",
228                                                        "/org/freedesktop/DBus",
229                                                        "org.freedesktop.DBus");
230
231             // try to register service in session bus
232             uint reply = bus.request_name ("org.janet.Moonshot", (uint) 0);
233             if (reply == DBus.RequestNameReply.PRIMARY_OWNER)
234             {
235                 this.ipc_server = new MoonshotServer (this);
236                 conn.register_object ("/org/janet/moonshot", ipc_server);
237             } else {
238                 bool shown=false;
239                 GLib.Error e;
240                 DBus.Object manager_proxy = conn.get_object ("org.janet.Moonshot",
241                                                              "/org/janet/moonshot",
242                                                              "org.janet.Moonshot");
243                 if (manager_proxy != null)
244                     manager_proxy.call("show_ui", out e, GLib.Type.INVALID, typeof(shown), out shown, GLib.Type.INVALID);
245
246                 if (!shown) {
247                     GLib.error ("Couldn't own name org.janet.Moonshot on dbus or show previously launched identity manager.");
248                 } else {
249                     stdout.printf("Showed previously launched identity manager.\n");
250                     GLib.Process.exit(0);
251                 }
252             }
253         }
254         catch (DBus.Error e)
255         {
256             stderr.printf ("%s\n", e.message);
257         }
258     }
259 #else
260     private void bus_acquired_cb (DBusConnection conn)
261     {
262         try {
263             conn.register_object ("/org/janet/moonshot", ipc_server);
264         }
265         catch (Error e)
266         {
267             stderr.printf ("%s\n", e.message);
268         }
269     }
270
271     private void init_ipc_server ()
272     {
273         this.ipc_server = new MoonshotServer (this);
274         GLib.Bus.own_name (GLib.BusType.SESSION,
275                            "org.janet.Moonshot",
276                            GLib.BusNameOwnerFlags.NONE,
277                            bus_acquired_cb,
278                            (conn, name) => {},
279                            (conn, name) => {
280                                bool shown=false;
281                                try {
282                                    IIdentityManager manager = Bus.get_proxy_sync (BusType.SESSION, name, "/org/janet/moonshot");
283                                    shown = manager.show_ui();
284                                } catch (IOError e) {
285                                }
286                                if (!shown) {
287                                    GLib.error ("Couldn't own name %s on dbus or show previously launched identity manager.", name);
288                                } else {
289                                    stdout.printf("Showed previously launched identity manager.\n");
290                                    GLib.Process.exit(0);
291                                }
292                            });
293     }
294 #endif
295 }
296
297 static bool explicitly_launched = true;
298 const GLib.OptionEntry[] options = {
299     {"DBusLaunch",0,GLib.OptionFlags.REVERSE,GLib.OptionArg.NONE,
300      ref explicitly_launched,"launch for dbus rpc use",null},
301     {null}
302 };
303
304
305 public static int main(string[] args){
306 #if IPC_MSRPC
307         bool headless = false;
308 #else
309         bool headless = GLib.Environment.get_variable("DISPLAY") == null;
310 #endif
311
312         if (headless) {
313             explicitly_launched = false;
314         } else {
315             try {
316                 Gtk.init_with_args(ref args, _(""), options, null);
317             } catch (GLib.Error e) {
318                 stdout.printf(_("error: %s\n"),e.message);
319                 stdout.printf(_("Run '%s --help' to see a full list of available options"), args[0]);
320             }
321         }
322
323 #if OS_WIN32
324         // Force specific theme settings on Windows without requiring a gtkrc file
325         Gtk.Settings settings = Gtk.Settings.get_default ();
326         settings.set_string_property ("gtk-theme-name", "ms-windows", "moonshot");
327         settings.set_long_property ("gtk-menu-images", 0, "moonshot");
328 #endif
329
330         Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
331         Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
332         Intl.textdomain (Config.GETTEXT_PACKAGE);
333        
334            
335         var app = new IdentityManagerApp(headless);
336         app.explicitly_launched = explicitly_launched;
337         
338         if (app.explicitly_launched) {
339             app.show();
340         }
341
342         if (headless) {
343 #if !IPC_MSRPC
344             MainLoop loop = new MainLoop();
345             loop.run();
346 #endif
347         } else {
348             Gtk.main();
349         }
350
351         return 0;
352     }
353