Merge branch 'master' of ssh://69.25.196.28:822/srv/git/moonshot-ui
authorSam Thursfield <samthursfield@codethink.co.uk>
Wed, 20 Jul 2011 17:01:59 +0000 (18:01 +0100)
committerSam Thursfield <samthursfield@codethink.co.uk>
Wed, 20 Jul 2011 17:01:59 +0000 (18:01 +0100)
Makefile.am
examples/service-selection.c [new file with mode: 0644]
src/moonshot-id.vala
src/moonshot-identities-manager.vala
src/moonshot-identity-request.vala
src/moonshot-server.vala
src/moonshot-window.vala
webprovisioning/complex-test.msht [new file with mode: 0644]

index 33bebef..a8f83eb 100644 (file)
@@ -48,11 +48,11 @@ src_moonshot_webp_SOURCES = \
         src/moonshot-webp-parser.vala \
         src/moonshot-id.vala
 
-src_moonshot_VALAFLAGS = --pkg gtk+-2.0 $(AM_VALAFLAGS)
+src_moonshot_VALAFLAGS = --pkg gdk-2.0 --pkg gtk+-2.0 $(AM_VALAFLAGS)
 src_moonshot_CPPFLAGS = $(moonshot_CFLAGS) $(AM_CPPFLAGS)
 src_moonshot_LDADD = $(moonshot_LIBS)
 
-src_moonshot_webp_VALAFLAGS = --vapidir=$(top_srcdir)/libmoonshot --pkg libmoonshot $(AM_VALAFLAGS)
+src_moonshot_webp_VALAFLAGS = --vapidir=$(top_srcdir)/libmoonshot --pkg gdk-2.0 --pkg libmoonshot $(AM_VALAFLAGS)
 src_moonshot_webp_CPPFLAGS = $(moonshot_CFLAGS) $(AM_CPPFLAGS)
 src_moonshot_webp_LDADD = $(moonshot_LIBS) ${top_builddir}/libmoonshot/libmoonshot.la
 
@@ -143,8 +143,13 @@ EXTRA_DIST = webprovisioning/moonshot.xml $(dbusservice_in_files)
 
 noinst_PROGRAMS = \
         examples/client \
+        examples/service-selection \
         tests/basic
 
+examples_service_selection_SOURCES = examples/service-selection.c
+examples_service_selection_CPPFLAGS = $(libmoonshot_CFLAGS) $(AM_CPPFLAGS)
+examples_service_selection_LDADD = ${top_builddir}/libmoonshot/libmoonshot.la
+
 examples_client_SOURCES = examples/client.c
 examples_client_CPPFLAGS = $(libmoonshot_CFLAGS) $(AM_CPPFLAGS)
 examples_client_LDADD = ${top_builddir}/libmoonshot/libmoonshot.la
diff --git a/examples/service-selection.c b/examples/service-selection.c
new file mode 100644 (file)
index 0000000..2acb743
--- /dev/null
@@ -0,0 +1,37 @@
+#include <glib.h>
+#include "libmoonshot.h"
+
+int main (int argc, char *argv[])
+{
+    MoonshotError *error;
+    gboolean        success;
+
+    char *nai,
+         *password,
+         *server_certificate_hash,
+         *ca_certificate,
+         *subject_name_constraint,
+         *subject_alt_name_constraint;
+
+    success = moonshot_get_identity ("",
+                                     "",
+                                     "email@project-moonshot.org",
+                                     &nai,
+                                     &password,
+                                     &server_certificate_hash,
+                                     &ca_certificate,
+                                     &subject_name_constraint,
+                                     &subject_alt_name_constraint,
+                                     &error);
+
+    if (success)
+        g_debug ("Got id: %s %s\n", nai, password);
+
+
+    moonshot_free (nai);
+    moonshot_free (password);
+    moonshot_free (server_certificate_hash);
+    moonshot_free (ca_certificate);
+    moonshot_free (subject_name_constraint);
+    moonshot_free (subject_alt_name_constraint);
+}
index c372ad3..58af2d7 100644 (file)
@@ -1,9 +1,9 @@
 public class TrustAnchor : Object
 {
-  public string ca_cert {get; set; default = null;}
-  public string subject {get; set; default = null;}
-  public string subject_alt  {get; set; default = null;}
-  public string server_cert  {get; set; default = null;}
+  public string ca_cert {get; set; default = "";}
+  public string subject {get; set; default = "";}
+  public string subject_alt  {get; set; default = "";}
+  public string server_cert  {get; set; default = "";}
 }
 
 public struct Rule
@@ -16,19 +16,19 @@ public class IdCard : Object
 {
   private string _nai;
   
-  public string display_name { get; set; default = null; }
+  public string display_name { get; set; default = ""; }
   
-  public string username { get; set; default = null; }
+  public string username { get; set; default = ""; }
   public string password { get; set; default = null; }
 
-  public string issuer { get; set; default = null; }
+  public string issuer { get; set; default = ""; }
   
   public Rule[] rules {get; set; default = {};}
   public string[] services { get; set; default = {}; }
 
-
   public TrustAnchor trust_anchor  { get; set; default = new TrustAnchor (); }
   
-  //TODO: Set the getter and remove the setter/default
-  public unowned string nai { get {  _nai = username + "@" + password; return _nai;}}
+  public Gdk.Pixbuf pixbuf { get; set; default = null; }    
+
+  public unowned string nai { get {  _nai = username + "@" + issuer; return _nai;}}
 }
index 124f25b..90bbec9 100644 (file)
@@ -35,6 +35,29 @@ class IdentitiesManager : Object {
                 id_card.services = key_file.get_string_list (identity, "Services");
                 id_card.display_name = key_file.get_string (identity, "DisplayName");
                 id_card.set_data ("pixbuf", find_icon ("avatar-default", 48));
+                
+                
+                if (key_file.has_key (identity, "Rules-Patterns") &&
+                    key_file.has_key (identity, "Rules-AlwaysConfirm"))
+                {
+                    string [] rules_patterns =    key_file.get_string_list (identity, "Rules-Patterns");
+                    string [] rules_always_conf = key_file.get_string_list (identity, "Rules-AlwaysConfirm");
+                    
+                    if (rules_patterns.length == rules_always_conf.length)
+                    {
+                      Rule[] rules = new Rule[rules_patterns.length];
+                      for (int i = 0; i < rules_patterns.length; i++)
+                      {
+                        rules[i] = {rules_patterns[i], rules_always_conf[i]};
+                      }
+                      id_card.rules = rules;
+                    }
+                }
+                // Trust anchor 
+                id_card.trust_anchor.ca_cert = key_file.get_string (identity, "CA-Cert");
+                id_card.trust_anchor.subject = key_file.get_string (identity, "Subject");
+                id_card.trust_anchor.subject_alt = key_file.get_string (identity, "SubjectAlt");
+                id_card.trust_anchor.server_cert = key_file.get_string (identity, "ServerCert");
 
                 id_card_list.prepend (id_card);
             }
index 17f8b5e..8415048 100644 (file)
@@ -6,21 +6,21 @@ class IdentityRequest : Object {
     public bool select_default = false;
 
     private MainWindow main_window;
-    private string nai;
-    private string password;
-    private string certificate;
+    public string nai;
+    public string password;
+    public string service;
 
     ReturnIdentityCallback callback = null;
 
     public IdentityRequest (MainWindow                   main_window,
                             string                       nai,
                             string                       password,
-                            string                       certificate)
+                            string                       service)
     {
         this.main_window = main_window;
         this.nai = nai;
         this.password = password;
-        this.certificate = certificate;
+        this.service = service;
     }
 
     public IdentityRequest.default (MainWindow main_window)
index 75e64ec..c5cae18 100644 (file)
@@ -41,11 +41,23 @@ public class MoonshotServer : Object {
             nai_out = id_card.nai;
             password_out = id_card.password;
 
-            server_certificate_hash = "certificate";
-
-            // User should have been prompted if there was no p/w.
-            return_if_fail (nai_out != null);
-            return_if_fail (password_out != null);
+            server_certificate_hash = id_card.trust_anchor.server_cert;
+            ca_certificate = id_card.trust_anchor.ca_cert;
+            subject_name_constraint = id_card.trust_anchor.subject;
+            subject_alt_name_constraint = id_card.trust_anchor.subject_alt;
+
+            if (nai_out == null)
+                nai_out = "";
+            if (password_out == null)
+                password_out = "";
+            if (server_certificate_hash == null)
+                server_certificate_hash = "";
+            if (ca_certificate == null)
+                ca_certificate = "";
+            if (subject_name_constraint == null)
+                subject_name_constraint = "";
+            if (subject_alt_name_constraint == null)
+                subject_alt_name_constraint = "";
 
             return true;
         }
@@ -77,11 +89,23 @@ public class MoonshotServer : Object {
             nai_out = request.id_card.nai;
             password_out = request.id_card.password;
 
-            server_certificate_hash = "certificate";
-
-            // User should have been prompted if there was no p/w.
-            return_val_if_fail (nai_out != null, false);
-            return_val_if_fail (password_out != null, false);
+            server_certificate_hash = request.id_card.trust_anchor.server_cert;
+            ca_certificate = request.id_card.trust_anchor.ca_cert;
+            subject_name_constraint = request.id_card.trust_anchor.subject;
+            subject_alt_name_constraint = request.id_card.trust_anchor.subject_alt;
+
+            if (nai_out == null)
+                nai_out = "";
+            if (password_out == null)
+                password_out = "";
+            if (server_certificate_hash == null)
+                server_certificate_hash = "";
+            if (ca_certificate == null)
+                ca_certificate = "";
+            if (subject_name_constraint == null)
+                subject_name_constraint = "";
+            if (subject_alt_name_constraint == null)
+                subject_alt_name_constraint = "";
 
             return true;
         }
index d40e1d7..794130e 100644 (file)
@@ -18,6 +18,7 @@ class MainWindow : Window
     private TreeModelFilter filter;
 
     public IdentitiesManager identities_manager;
+    private SList<IdCard>    candidates;
 
     private MoonshotServer ipc_server;
 
@@ -67,27 +68,75 @@ class MainWindow : Window
         connect_signals();
         init_ipc_server();
     }
+    
+    public void add_candidate (IdCard idcard)
+    {
+        candidates.append (idcard);
+    }
 
     private bool visible_func (TreeModel model, TreeIter iter)
     {
-        string issuer;
-        string search_text;
-        string issuer_casefold;
-        string search_text_casefold;
+        IdCard id_card;
 
         model.get (iter,
-                   Columns.ISSUER_COL, out issuer);
-        search_text = this.search_entry.get_text ();
+                   Columns.IDCARD_COL, out id_card);
 
-        if (issuer == null || search_text == null)
+        if (id_card == null)
             return false;
+        
+        if (candidates != null)
+        {
+            bool is_candidate = false;
+            foreach (IdCard candidate in candidates)
+            {
+                if (candidate == id_card)
+                    is_candidate = true;
+            }
+            if (!is_candidate)
+                return false;
+        }
+        
+        string entry_text = search_entry.get_text ();
+        if (entry_text == null || entry_text == "")
+        {
+            return true;
+        }
 
-        issuer_casefold = issuer.casefold ();
-        search_text_casefold = search_text.casefold ();
+        foreach (string search_text in entry_text.split(" "))
+        {
+            if (search_text == "")
+                continue;
+         
 
-        if (issuer_casefold.contains (search_text_casefold))
-            return true;
+            string search_text_casefold = search_text.casefold ();
+
+            if (id_card.issuer != null)
+            {
+              string issuer_casefold = id_card.issuer;
+
+              if (issuer_casefold.contains (search_text_casefold))
+                  return true;
+            }
+
+            if (id_card.display_name != null)
+            {
+                string display_name_casefold = id_card.display_name.casefold ();
+              
+                if (display_name_casefold.contains (search_text_casefold))
+                    return true;
+            }
+            
+            if (id_card.services.length > 0)
+            {
+                foreach (string service in id_card.services)
+                {
+                    string service_casefold = service.casefold ();
 
+                    if (service_casefold.contains (search_text_casefold))
+                        return true;
+                }
+            }
+        }
         return false;
     }
 
@@ -125,7 +174,6 @@ class MainWindow : Window
         this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, has_text);
 
         this.vbox_right.set_visible (false);
-        this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
     }
 
     private bool search_entry_key_press_event_cb (Gdk.EventKey e)
@@ -402,23 +450,132 @@ class MainWindow : Window
 
         if (request.select_default)
         {
-            identity = this.default_id_card;
+            identity = default_id_card;
         }
 
-        /* Automatic service matching rules can go here */
-
         if (identity == null)
         {
-            // Resort to manual selection
-            this.show ();
-        }
-        else
-        {
-            // Send back the identity (we can't directly run the
-            // callback because we may be being called from a 'yield')
-            Idle.add (() => { send_identity_cb (identity); return false; });
-            return;
+            bool has_nai = request.nai != null && request.nai != "";
+            bool has_srv = request.service != null && request.service != "";
+            bool confirm = false;
+            IdCard nai_provided = null;
+
+            foreach (IdCard id in identities_manager.id_card_list)
+            {
+                /* If NAI matches we add id card to the candidate list */
+                if (has_nai && request.nai == id.nai)
+                {
+                    nai_provided = id;
+                    add_candidate (id);
+                    continue;
+                }
+
+                /* If any service matches we add id card to the candidate list */
+                if (has_srv)
+                {
+                    foreach (string srv in id.services)
+                    {
+                        if (request.service == srv)
+                        {
+                            add_candidate (id);
+                            continue;
+                        }
+                    }
+                }
+            }
+
+            /* If more than one candidate we dissasociate service from all ids */
+            if (has_srv && candidates.length() > 1)
+            {
+                foreach (IdCard id in candidates)
+                {
+                    int i = 0;
+                    SList<string> services_list = null;
+                    bool has_service = false;
+
+                    foreach (string srv in id.services)
+                    {
+                        if (srv == request.service)
+                        {
+                            has_service = true;
+                            continue;
+                        }
+                        services_list.append (srv);
+                    }
+                    
+                    if (!has_service)
+                        continue;
+
+                    if (services_list.length () == 0)
+                    {
+                        id.services = {};
+                        continue;
+                    }
+
+                    string[] services = new string[services_list.length ()];
+                    foreach (string srv in services_list)
+                    {
+                        services[i] = srv;
+                        i++;
+                    }
+
+                    id.services = services;
+                }
+            }
+
+            identities_manager.store_id_cards ();
+
+            /* If there are no candidates we use the service matching rules */
+            if (candidates.length () == 0)
+            {
+                foreach (IdCard id in identities_manager.id_card_list)
+                {
+                    foreach (Rule rule in id.rules)
+                    {
+                        if (!match_service_pattern (request.service, rule.pattern))
+                            continue;
+
+                        candidates.append (id);
+
+                        if (rule.always_confirm == "true")
+                            confirm = true;
+                    }
+                }
+            }
+            
+            if (candidates.length () > 1)
+            {
+                if (has_nai && nai_provided != null)
+                {
+                    identity = nai_provided;
+                    confirm = false;
+                }
+                else
+                    confirm = true;
+            }
+            else
+                identity = candidates.nth_data (0);
+
+            /* TODO: If candidate list empty return fail */
+            
+            if (confirm)
+            {
+                filter.refilter();
+                redraw_id_card_widgets ();
+                show ();
+                return;
+            }
         }
+        // Send back the identity (we can't directly run the
+        // callback because we may be being called from a 'yield')
+        Idle.add (() => { send_identity_cb (identity); return false; });
+        return;
+    }
+    
+    private bool match_service_pattern (string service, string pattern)
+    {
+        var pspec = new PatternSpec (pattern);
+        return pspec.match_string (service);
     }
 
     public void send_identity_cb (IdCard identity)
@@ -428,6 +585,20 @@ class MainWindow : Window
         var request = this.request_queue.pop_head ();
         bool reset_password = false;
 
+        if (request.service != null && request.service != "")
+        {
+            string[] services = new string[identity.services.length + 1];
+
+            for (int i = 0; i < identity.services.length; i++)
+                services[i] = identity.services[i];
+
+            services[identity.services.length] = request.service;
+
+            identity.services = services;
+
+            identities_manager.store_id_cards();
+        }
+
         if (identity.password == null)
         {
             var dialog = new AddPasswordDialog ();
@@ -456,6 +627,8 @@ class MainWindow : Window
 
         if (reset_password)
             identity.password = null;
+
+        candidates = null;
     }
 
     private void label_make_bold (Label label)
@@ -515,24 +688,24 @@ class MainWindow : Window
 
                 SList<string> services = new SList<string>();
                 
-                foreach (string srv in id_card.services)
+                foreach (string srv in idcard.services)
                 {
                   if (srv == candidate)
                     continue;
                   services.append (srv);
                 }
                 
-                id_card.services = new string[services.length()];
-                for (int j=0; j<id_card.services.length; j++)
+                idcard.services = new string[services.length()];
+                for (int j=0; j<idcard.services.length; j++)
                 {
-                  id_card.services[j] = services.nth_data(j);
+                  idcard.services[j] = services.nth_data(j);
                 }
                 
                 var children = services_internal_vbox.get_children ();
                 foreach (var hbox in children)
                   hbox.destroy();
                 
-                fill_services_vbox (id_card);
+                fill_services_vbox (idcard);
                 custom_vbox.current_idcard.update_id_card_label ();
               }
               
diff --git a/webprovisioning/complex-test.msht b/webprovisioning/complex-test.msht
new file mode 100644 (file)
index 0000000..2c11172
--- /dev/null
@@ -0,0 +1,69 @@
+<identities>
+  <identity>
+    <display-name>Unique Name</display-name>
+    <user>user1</user>
+    <password></password>
+    <realm>foo.baz</realm>
+    <services>
+      <service>irc@jabber.project-moonshot.org</service>
+      <service>xmpp@jabber.project-moonshot.org</service>
+    </services>
+    <selection-rules>
+      <rule>
+        <pattern>PATTERN</pattern>
+        <always-confirm>true</always-confirm>
+      </rule>
+      <rule>
+        <pattern>imap@*moonshot.org</pattern>
+        <always-confirm>false</always-confirm>
+      </rule>
+    </selection-rules>
+    <trust-anchor>
+      <ca-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</ca-cert>
+      <subject>Foo</subject>
+      <subject-alt>Bar</subject-alt>
+      <!-- Or alternatively -->
+      <server-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</server-cert>
+    </trust-anchor>
+  </identity>
+  <identity>
+    <display-name>Another Unique Name</display-name>
+    <user>user2</user>
+    <password></password>
+    <realm>foo.bar</realm>
+    <services>
+      <service>irc@jabber.project-moonshot.org</service>
+      <service>email@project-moonshot.org</service>
+    </services>
+    <selection-rules>
+      <rule>
+        <pattern>*@project-moonshot.org</pattern>
+        <always-confirm>true</always-confirm>
+      </rule>
+    </selection-rules>
+    <trust-anchor>
+      <ca-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</ca-cert>
+      <subject>Foo</subject>
+      <subject-alt>Bar</subject-alt>
+      <!-- Or alternatively -->
+      <server-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</server-cert>
+    </trust-anchor>
+  </identity>
+    <identity>
+    <display-name>Yet Another Unique Name</display-name>
+    <user>user3</user>
+    <password></password>
+    <realm>foo.com</realm>
+    <services>
+      <service>irc@jabber.project-moonshot.org</service>
+      <service>email@project-moonshot.org</service>
+    </services>
+    <trust-anchor>
+      <ca-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</ca-cert>
+      <subject>Foo</subject>
+      <subject-alt>Bar</subject-alt>
+      <!-- Or alternatively -->
+      <server-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</server-cert>
+    </trust-anchor>
+  </identity>
+</identities>