ekiga r7189 - in trunk: . lib/engine/addressbook/ldap src/gui



Author: jpuydt
Date: Fri Oct 10 19:31:28 2008
New Revision: 7189
URL: http://svn.gnome.org/viewvc/ekiga?rev=7189&view=rev

Log:
Applied a series of patches from Howard Chu for a better LDAP authentication support.

Modified:
   trunk/AUTHORS
   trunk/ChangeLog
   trunk/lib/engine/addressbook/ldap/ldap-book.cpp
   trunk/lib/engine/addressbook/ldap/ldap-book.h
   trunk/lib/engine/addressbook/ldap/ldap-contact.cpp
   trunk/lib/engine/addressbook/ldap/ldap-contact.h
   trunk/lib/engine/addressbook/ldap/ldap-main.cpp
   trunk/lib/engine/addressbook/ldap/ldap-source.cpp
   trunk/lib/engine/addressbook/ldap/ldap-source.h
   trunk/src/gui/callbacks.cpp

Modified: trunk/AUTHORS
==============================================================================
--- trunk/AUTHORS	(original)
+++ trunk/AUTHORS	Fri Oct 10 19:31:28 2008
@@ -23,6 +23,7 @@
 Stefan Bruens <lurchi gmx li>
 Eskil Bylund <eskil_bylund fastmail fm>
 Michael Carlson <corfe83dev gmail com>
+Howard Chu <hyc symas com>
 FrÃdÃric Crozat <fcrozat mandriva com>
 Rodney Dawes <dobey ximian org>
 Eugen Dedu <eugen dedu pu-pm univ-fcomte fr>

Modified: trunk/lib/engine/addressbook/ldap/ldap-book.cpp
==============================================================================
--- trunk/lib/engine/addressbook/ldap/ldap-book.cpp	(original)
+++ trunk/lib/engine/addressbook/ldap/ldap-book.cpp	Fri Oct 10 19:31:28 2008
@@ -30,6 +30,7 @@
  *                         ldap-book.cpp  -  description
  *                         ------------------------------------------
  *   begin                : written in 2007 by Julien Puydt
+ *                        : completed in 2008 by Howard Chu
  *   copyright            : (c) 2007 by Julien Puydt
  *   description          : implementation of a LDAP book
  *
@@ -42,13 +43,13 @@
 #include <sys/time.h>
 #include <string.h>
 
-#include <ldap.h>
 #include <glib.h>
 
 #include "config.h"
 
+#include <sasl/sasl.h>
+
 #include "ldap-book.h"
-#include "form-request-simple.h"
 #include "robust-xml.h"
 
 /* little helper function... can probably be made more complete */
@@ -73,54 +74,51 @@
 }
 
 /* parses a message to construct a nice contact */
-static OPENLDAP::Contact *
-parse_result (Ekiga::ServiceCore &core,
-	      struct ldap *context,
+OPENLDAP::Contact *
+OPENLDAP::Book::parse_result (
 	      LDAPMessage *message)
 {
   OPENLDAP::Contact *result = NULL;
   BerElement *ber = NULL;
-  char *attribute = NULL;
-  struct berval **values = NULL;
-  std::string name;
-  std::string call_address;
-
-  attribute = ldap_first_attribute (context, message, &ber);
-
-  while (attribute != NULL) {
-
-    if (strncmp("givenName",attribute, 9) == 0) {
-
-      values = ldap_get_values_len (context, message, attribute);
-      if (values[0] != NULL) {
-	name = std::string (values[0]->bv_val, values[0]->bv_len);
-        ldap_value_free_len (values);
+  struct berval bv, *bvals;
+  std::string username;
+  std::map<std::string, std::string> call_addresses;
+  char **attributes = bookinfo.urld->lud_attrs;
+  int i, rc;
+
+  /* skip past entry DN */
+  rc = ldap_get_dn_ber (ldap_context, message, &ber, &bv);
+
+  while (rc == LDAP_SUCCESS) {
+    rc = ldap_get_attribute_ber (ldap_context, message, ber, &bv, &bvals);
+    if (bv.bv_val == NULL) break;
+    if (!strcasecmp(bv.bv_val, attributes[0])) {
+      username = std::string (bvals[0].bv_val, bvals[0].bv_len);
+    } else {
+      for (i=1; attributes[i]; i++) {
+        if (!strcasecmp(bv.bv_val,attributes[i]) && bvals && bvals[0].bv_val ) {
+	  call_addresses[attributes[i]] =
+	    /* FIXME: next line is annoying */
+	  	std::string ("sip:") +
+		  std::string (bvals[0].bv_val, bvals[0].bv_len);
+        }
       }
     }
-    if (strncmp ("telephoneNumber", attribute, 15) == 0) {
-
-      values = ldap_get_values_len (context, message, attribute);
-      if (values[0] != NULL) {
-	call_address = std::string (values[0]->bv_val, values[0]->bv_len);
-        ldap_value_free_len (values);
-      }
+    if (bvals) ber_memfree(bvals);
     }
-    ldap_memfree (attribute);
-    attribute = ldap_next_attribute (context, message, ber);
-  }
+
   ber_free (ber, 0);
 
-  if (!name.empty () && !call_address.empty ()) {
+  if (!username.empty () && !call_addresses.empty()) {
 
-    result = new OPENLDAP::Contact (core, fix_to_utf8 (name),
-				    /* FIXME: next line is annoying */
-				    std::string ("sip:") + call_address);
+    result = new OPENLDAP::Contact (core, fix_to_utf8 (username), call_addresses);
   }
 
   return result;
 }
 
 
+#if 0  /* seems to be unused / unneeded */
 /* this allows us to do the refreshing in a thread: we put all needed
  * data in this structure, let everything happen elsewhere, then push back
  * into the main thread
@@ -130,17 +128,17 @@
 
   RefreshData (Ekiga::ServiceCore &_core,
 	       const std::string _name,
-	       const std::string _hostname,
-	       int _port,
+	       const std::string _uri,
 	       const std::string _base,
 	       const std::string _scope,
 	       const std::string _call_attribute,
+	       const std::string _authcID,
 	       const std::string _password,
 	       const std::string _search_string,
 	       sigc::slot<void, std::vector<OPENLDAP::Contact *> > _publish_results):
-    core(_core), name(_name), hostname(_hostname), port(_port),
+    core(_core), name(_name), uri(_uri),
     base(_base), scope(_scope), call_attribute(_call_attribute),
-    password(_password), search_string(_search_string),
+    authcID(_authcID), password(_password), search_string(_search_string),
     error(false), publish_results (_publish_results)
   {
   }
@@ -148,11 +146,11 @@
   /* search data */
   Ekiga::ServiceCore &core;
   std::string name;
-  std::string hostname;
-  int port;
+  std::string uri;
   std::string base;
   std::string scope;
   std::string call_attribute;
+  std::string authcID;
   std::string password;
   std::string search_string;
 
@@ -164,20 +162,36 @@
   /* callback */
   sigc::slot<void, std::vector<OPENLDAP::Contact *> > publish_results;
 };
+#endif
 
 /* actual implementation */
 
 OPENLDAP::Book::Book (Ekiga::ServiceCore &_core,
 		      xmlNodePtr _node):
-  core(_core), node(_node), name_node(NULL), hostname_node(NULL),
-  port_node(NULL), base_node(NULL), scope_node(NULL), call_attribute_node(NULL),
-  password_node(NULL), ldap_context(NULL), patience(0),
+  saslform(NULL), core(_core), node(_node),
+  name_node(NULL), uri_node(NULL), authcID_node(NULL), password_node(NULL),
+  ldap_context(NULL), patience(0),
   runtime (*(dynamic_cast<Ekiga::Runtime *>(core.get ("runtime"))))
 {
   xmlChar *xml_str;
+  bool upgrade_config = false;
+
+  /* for previous config */
+  std::string hostname="", port="", base="", scope="", call_attribute="";
+  xmlNodePtr hostname_node = NULL, port_node = NULL, base_node = NULL,
+    scope_node = NULL, call_attribute_node = NULL;
 
   contact_core = dynamic_cast<Ekiga::ContactCore *>(core.get ("contact-core"));
 
+  bookinfo.name = "";
+  bookinfo.uri = "";
+  bookinfo.authcID = "";
+  bookinfo.password = "";
+  bookinfo.saslMech = "";
+  bookinfo.urld = NULL;
+  bookinfo.sasl = false;
+  bookinfo.starttls = false;
+
   for (xmlNodePtr child = node->children ;
        child != NULL;
        child = child->next) {
@@ -188,9 +202,19 @@
       if (xmlStrEqual (BAD_CAST ("name"), child->name)) {
 
 	xml_str = xmlNodeGetContent (child);
-	name = (const char *)xml_str;
+	bookinfo.name = (const char *)xml_str;
+	xmlFree (xml_str);
 	name_node = child;
+	continue;
+      }
+
+      if (xmlStrEqual (BAD_CAST ("uri"), child->name)) {
+
+	xml_str = xmlNodeGetContent (child);
+	bookinfo.uri = (const char *)xml_str;
 	xmlFree (xml_str);
+	uri_node = child;
+	continue;
       }
 
       if (xmlStrEqual (BAD_CAST ("hostname"), child->name)) {
@@ -199,14 +223,17 @@
 	hostname = (const char *)xml_str;
 	hostname_node = child;
 	xmlFree (xml_str);
+	upgrade_config = true;
+	continue;
       }
-
       if (xmlStrEqual (BAD_CAST ("port"), child->name)) {
 
 	xml_str = xmlNodeGetContent (child);
-	port = std::atoi ((const char *)xml_str);
+	port = (const char *)xml_str;
 	port_node = child;
 	xmlFree (xml_str);
+	upgrade_config = true;
+	continue;
       }
 
       if (xmlStrEqual (BAD_CAST ("base"), child->name)) {
@@ -215,6 +242,8 @@
 	base = (const char *)xml_str;
 	base_node = child;
 	xmlFree (xml_str);
+	upgrade_config = true;
+	continue;
       }
 
       if (xmlStrEqual (BAD_CAST ("scope"), child->name)) {
@@ -223,6 +252,8 @@
 	scope = (const char *)xml_str;
 	scope_node = child;
 	xmlFree (xml_str);
+	upgrade_config = true;
+	continue;
       }
 
       if (xmlStrEqual (BAD_CAST ("call_attribute"), child->name)) {
@@ -231,87 +262,146 @@
 	call_attribute = (const char *)xml_str;
 	call_attribute_node = child;
 	xmlFree (xml_str);
+	upgrade_config = true;
+	continue;
+      }
+
+      if (xmlStrEqual (BAD_CAST ("authcID"), child->name)) {
+
+	xml_str = xmlNodeGetContent (child);
+	bookinfo.authcID = (const char *)xml_str;
+	authcID_node = child;
+	xmlFree (xml_str);
+	continue;
       }
 
       if (xmlStrEqual (BAD_CAST ("password"), child->name)) {
 
 	xml_str = xmlNodeGetContent (child);
-	password = (const char *)xml_str;
+	bookinfo.password = (const char *)xml_str;
 	password_node = child;
 	xmlFree (xml_str);
+	continue;
       }
     }
   }
+  if (upgrade_config) {
+    if (!uri_node) {
+      LDAPURLDesc *url_tmp = NULL;
+      char *url_str;
+      std::string new_uri;
+      if (hostname.empty())
+        hostname="localhost";
+      new_uri = std::string("ldap://";) + hostname;
+      if (!port.empty())
+        new_uri += std::string(":") + port;
+      new_uri += "/?" + call_attribute + "?" +
+        scope;
+      ldap_url_parse (new_uri.c_str(), &url_tmp);
+      url_tmp->lud_dn = (char *)base.c_str();
+      url_str = ldap_url_desc2str (url_tmp);
+      bookinfo.uri = std::string(url_str);
+      ldap_memfree (url_str);
+      robust_xmlNodeSetContent (node, &uri_node, "uri", bookinfo.uri);
+      url_tmp->lud_dn = NULL;
+      ldap_free_urldesc (url_tmp);
+    }
+    if (hostname_node) {
+	xmlUnlinkNode (hostname_node);
+        xmlFreeNode (hostname_node);
+    }
+    if (port_node) {
+	xmlUnlinkNode (port_node);
+        xmlFreeNode (port_node);
+    }
+    if (base_node) {
+	xmlUnlinkNode (base_node);
+        xmlFreeNode (base_node);
+    }
+    if (scope_node) {
+	xmlUnlinkNode (scope_node);
+        xmlFreeNode (scope_node);
+    }
+    if (call_attribute_node) {
+	xmlUnlinkNode (call_attribute_node);
+        xmlFreeNode (call_attribute_node);
+    }
+    trigger_saving.emit ();
+  }
+  OPENLDAP::BookInfoParse (bookinfo);
 }
 
 OPENLDAP::Book::Book (Ekiga::ServiceCore &_core,
-		      const std::string _name,
-		      const std::string _hostname,
-		      int _port,
-		      const std::string _base,
-		      const std::string _scope,
-		      const std::string _call_attribute,
-		      const std::string _password):
-  core(_core), name(_name), name_node(NULL),
-  hostname(_hostname), hostname_node(NULL), port(_port), port_node(NULL),
-  base(_base), base_node(NULL), scope(_scope), scope_node(NULL),
-  call_attribute(_call_attribute), call_attribute_node(NULL),
-  password (_password), password_node(NULL), ldap_context(NULL), patience(0),
+		      OPENLDAP::BookInfo _bookinfo):
+  saslform(NULL), core(_core), name_node(NULL),
+  uri_node(NULL), authcID_node(NULL), password_node(NULL),
+  ldap_context(NULL), patience(0),
   runtime (*(dynamic_cast<Ekiga::Runtime *>(core.get ("runtime"))))
 {
   contact_core = dynamic_cast<Ekiga::ContactCore *>(core.get ("contact-core"));
 
   node = xmlNewNode (NULL, BAD_CAST "server");
 
+  bookinfo = _bookinfo;
+
   name_node = xmlNewChild (node, NULL,
 			   BAD_CAST "name",
 			   BAD_CAST robust_xmlEscape (node->doc,
-						      name).c_str ());
+						      bookinfo.name).c_str ());
 
-  hostname_node = xmlNewChild (node, NULL,
-			       BAD_CAST "hostname",
+  uri_node = xmlNewChild (node, NULL,
+			       BAD_CAST "uri",
 			       BAD_CAST robust_xmlEscape (node->doc,
-							  hostname).c_str ());
-
-  { // Snark hates C++ -- and there isn't only a reason for it -- but many
-    std::stringstream strm;
-    std::string port_string;
-    strm << port;
-    strm >> port_string;
-    port_node = xmlNewChild (node, NULL,
-			     BAD_CAST "port",
-			     BAD_CAST port_string.c_str ());
-  }
+							  bookinfo.uri).c_str ());
 
-  base_node = xmlNewChild (node, NULL,
-			   BAD_CAST "base",
-			   BAD_CAST robust_xmlEscape (node->doc,
-						      base).c_str ());
-
-  scope_node = xmlNewChild (node, NULL,
-			    BAD_CAST "scope",
-			    BAD_CAST robust_xmlEscape (node->doc,
-						       scope).c_str ());
-
-  call_attribute_node = xmlNewChild (node, NULL,
-				     BAD_CAST "call_attribute",
-				     BAD_CAST robust_xmlEscape (node->doc,
-								call_attribute).c_str ());
+  authcID_node = xmlNewChild (node, NULL,
+			       BAD_CAST "authcID",
+			       BAD_CAST robust_xmlEscape (node->doc,
+			       		bookinfo.authcID).c_str ());
 
   password_node = xmlNewChild (node, NULL,
 			       BAD_CAST "password",
 			       BAD_CAST robust_xmlEscape (node->doc,
-							  password).c_str ());
+							  bookinfo.password).c_str ());
+  OPENLDAP::BookInfoParse (bookinfo);
 }
 
 OPENLDAP::Book::~Book ()
 {
+  if (bookinfo.urld) ldap_free_urldesc(bookinfo.urld);
+}
+
+void
+OPENLDAP::BookInfoParse (struct BookInfo &info)
+{
+  LDAPURLDesc *url_tmp;
+  std::string uri;
+  size_t pos;
+
+  ldap_url_parse (info.uri.c_str(), &url_tmp);
+  if (url_tmp->lud_exts) {
+    for (int i=0; url_tmp->lud_exts[i]; i++) {
+      if (!strcasecmp(url_tmp->lud_exts[i], "StartTLS")) {
+        info.starttls = true;
+      } else if (!strncasecmp(url_tmp->lud_exts[i], "SASL", 4)) {
+        info.sasl = true;
+	if (url_tmp->lud_exts[i][4] == '=')
+	  info.saslMech = std::string(url_tmp->lud_exts[i]+5);
+      }
+    }
+  }
+  info.urld = url_tmp;
+  pos = info.uri.find ('/', strlen(info.urld->lud_scheme) + 3);
+  if (pos != std::string::npos)
+    info.uri_host = info.uri.substr (0,pos);
+  else
+    info.uri_host = info.uri;
 }
 
 const std::string
 OPENLDAP::Book::get_name () const
 {
-  return name;
+  return bookinfo.name;
 }
 
 bool
@@ -374,19 +464,195 @@
 }
 
 void
+OPENLDAP::Book::on_sasl_form_submitted (Ekiga::Form &result)
+{
+  result.visit (*saslform);
+}
+
+extern "C" {
+
+typedef struct interctx {
+  OPENLDAP::Book *book;
+  std::string authcID;
+  std::string password;
+  std::list<std::string> results;
+} interctx;
+
+static int
+book_saslinter(LDAP *ld, unsigned flags __attribute__((unused)),
+  void *def, void *inter)
+{
+  sasl_interact_t *in = (sasl_interact_t *)inter;
+  interctx *ctx = (interctx *)def;
+  struct berval p;
+  int i, nprompts = 0;
+
+  /* Fill in the prompts we have info for; count
+   * how many we're missing.
+   */
+  for (;in->id != SASL_CB_LIST_END;in++)
+  {
+    p.bv_val = NULL;
+    switch(in->id)
+    {
+      case SASL_CB_GETREALM:
+        ldap_get_option(ld, LDAP_OPT_X_SASL_REALM, &p.bv_val);
+        if (p.bv_val) p.bv_len = strlen(p.bv_val);
+        break;
+      case SASL_CB_AUTHNAME:
+	p.bv_len = ctx->authcID.length();
+	if (p.bv_len)
+          p.bv_val = (char *)ctx->authcID.c_str();
+        break;
+      case SASL_CB_USER:
+        /* If there was a default authcID, just ignore the authzID */
+        if (ctx->authcID.length()) {
+	  p.bv_val = (char *)"";
+	  p.bv_len = 0;
+	}
+	break;
+      case SASL_CB_PASS:
+	p.bv_len = ctx->password.length();
+	if (p.bv_len)
+          p.bv_val = (char *)ctx->password.c_str();
+        break;
+      default:
+        break;
+    }
+    if (p.bv_val)
+    {
+      in->result = p.bv_val;
+      in->len = p.bv_len;
+    } else
+    {
+      nprompts++;
+      in->result = NULL;
+    }
+  }
+
+  /* If there are missing items, try to get them all in one dialog */
+  if (nprompts) {
+    Ekiga::FormRequestSimple request;
+    Ekiga::FormBuilder result;
+    std::string prompt;
+    std::string ctxt = "";
+    char resbuf[32];
+
+    request.title (_("LDAP SASL Interaction"));
+
+    for (i=0, in = (sasl_interact_t *)inter;
+      in->id != SASL_CB_LIST_END;in++)
+    {
+      bool noecho = false, challenge = false;
+
+      if (in->result) continue;
+
+      /* Give each dialog item a unique name */
+      sprintf(resbuf, "res%02x", i);
+      i++;
+
+      /* Check for prompts that need special handling */
+      switch(in->id)
+      {
+      case SASL_CB_PASS:
+        noecho = true;
+	break;
+      case SASL_CB_NOECHOPROMPT:
+        noecho = true;
+        challenge = true;
+	break;
+      case SASL_CB_ECHOPROMPT:
+        challenge = true;
+	break;
+      default:
+        break;
+      }
+
+      /* accumulate any challenge strings */
+      if (challenge && in->challenge) {
+        ctxt += std::string (_("Challenge: ")) +
+	  std::string (in->challenge) +"\n";
+      }
+
+      /* use the provided prompt text, or our default? */
+      if (in->prompt)
+        prompt = std::string (in->prompt);
+      else
+        prompt = std::string (_("Interact"));
+
+      /* private text or not? */
+      if (noecho) {
+        request.private_text (std::string (resbuf), prompt, "");
+      } else {
+        std::string dflt;
+	if (in->defresult)
+	  dflt = std::string (in->defresult);
+	else
+	  dflt = "";
+	request.text (std::string(resbuf), prompt, dflt);
+      }
+    }
+
+    /* If we had any challenge text, set it now */
+    if (!ctxt.empty())
+      request.instructions (ctxt);
+
+    /* Save a pointer for storing the form result */
+    ctx->book->saslform = &result;
+    request.submitted.connect (sigc::mem_fun (ctx->book,
+					    &OPENLDAP::Book::on_sasl_form_submitted));
+    if (!ctx->book->questions.handle_request (&request)) {
+      return LDAP_LOCAL_ERROR;
+    }
+
+    /* Extract answers from the result form */
+    for (i=0, in = (sasl_interact_t *)inter;
+      in->id != SASL_CB_LIST_END;in++)
+    {
+      bool noecho = false;
+
+      if (in->result) continue;
+
+      sprintf(resbuf, "res%02x", i);
+      i++;
+      switch(in->id)
+      {
+      case SASL_CB_PASS:
+      case SASL_CB_NOECHOPROMPT:
+        noecho = true;
+	break;
+      default:
+        break;
+      }
+      if (noecho)
+        prompt = result.private_text (std::string (resbuf));
+      else
+        prompt = result.text (std::string (resbuf));
+
+      /* Save the answers so they don't disappear before our
+       * caller can see them; return the saved copies.
+       */
+      ctx->results.push_back (prompt);
+      in->result = ctx->results.back().c_str();
+      in->len = ctx->results.back().length();
+    }
+  }
+  return LDAP_SUCCESS;
+}
+
+} /* extern "C" */
+
+void
 OPENLDAP::Book::refresh_start ()
 {
   int msgid = -1;
   int result = LDAP_SUCCESS;
   int ldap_version = LDAP_VERSION3;
-  char *ldap_uri = NULL;
 
   status = std::string (_("Refreshing"));
   updated.emit ();
 
-  ldap_uri = g_strdup_printf ("ldap://%s:%d";, hostname.c_str (), port);
-  result = ldap_initialize (&ldap_context, ldap_uri);
-  g_free (ldap_uri);
+  result = ldap_initialize (&ldap_context, bookinfo.uri_host.c_str());
   if (result != LDAP_SUCCESS) {
 
     status = std::string (_("Could not initialize server"));
@@ -400,29 +666,56 @@
   (void)ldap_set_option (ldap_context,
 			 LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
 
-  if (password.empty ()) {
+  if (bookinfo.starttls) {
+    result = ldap_start_tls_s (ldap_context, NULL, NULL);
+    if (result != LDAP_SUCCESS) {
+      status = std::string (_("LDAP Error: ")) +
+        std::string (ldap_err2string (result));
+      updated.emit ();
+      ldap_unbind_ext (ldap_context, NULL, NULL);
+      ldap_context = NULL;
+      return;
+    }
+  }
+
+  if (bookinfo.sasl) {
+    interctx ctx;
+    
+    ctx.book = this;
+    ctx.authcID = bookinfo.authcID;
+    ctx.password = bookinfo.password;
+    result = ldap_sasl_interactive_bind_s (ldap_context, NULL,
+      bookinfo.saslMech.c_str(), NULL, NULL, LDAP_SASL_QUIET,
+      book_saslinter, &ctx);
 
-    result = ldap_sasl_bind (ldap_context, NULL,
-			     LDAP_SASL_SIMPLE, NULL,
-			     NULL, NULL,
-			     &msgid);
   } else {
+    /* Simple Bind */
+    if (bookinfo.password.empty ()) {
+      struct berval bv={0,NULL};
+
+      result = ldap_sasl_bind (ldap_context, NULL,
+			       LDAP_SASL_SIMPLE, &bv,
+			       NULL, NULL,
+			       &msgid);
+    } else {
+
+      struct berval passwd = { 0, NULL };
+      passwd.bv_val = g_strdup (bookinfo.password.c_str ());
+      passwd.bv_len = bookinfo.password.length();
+
+      result = ldap_sasl_bind (ldap_context, bookinfo.authcID.c_str(),
+			       LDAP_SASL_SIMPLE, &passwd,
+			       NULL, NULL,
+			       &msgid);
 
-    struct berval passwd = { 0, NULL };
-    passwd.bv_val = g_strdup (password.c_str ());
-    passwd.bv_len = strlen (password.c_str ());
-
-    result = ldap_sasl_bind (ldap_context, NULL,
-			     LDAP_SASL_SIMPLE, &passwd,
-			     NULL, NULL,
-			     &msgid);
-
-    g_free (passwd.bv_val);
+      g_free (passwd.bv_val);
+    }
   }
 
   if (result != LDAP_SUCCESS) {
 
-    status = std::string (_("Could not contact server"));
+    status = std::string (_("LDAP Error: ")) +
+      std::string (ldap_err2string (result));
     updated.emit ();
 
     ldap_unbind_ext (ldap_context, NULL, NULL);
@@ -444,10 +737,12 @@
   struct timeval timeout = { 1, 0}; /* block 1s */
   LDAPMessage *msg_entry = NULL;
   int msgid;
-  std::vector<std::string> attributes_vector;
-  char **attributes = NULL;
-  int iscope = LDAP_SCOPE_SUBTREE;
-  std::string filter;
+  std::string filter, fterm;
+  const char *fstr;
+  size_t pos;
+
+  if (bookinfo.sasl)
+    goto sasl_bound;
 
   result = ldap_result (ldap_context, LDAP_RES_ANY, LDAP_MSG_ALL,
 			&timeout, &msg_entry);
@@ -481,40 +776,35 @@
   }
   (void) ldap_msgfree (msg_entry);
 
-  attributes_vector.push_back ("givenname");
-  attributes_vector.push_back (call_attribute);
-
-  if (scope == "sub")
-    iscope = LDAP_SCOPE_SUBTREE;
-  else
-    iscope = LDAP_SCOPE_ONELEVEL;
-
-  attributes = (char **)malloc ((attributes_vector.size ()+1)*sizeof(char *));
-  for (unsigned int i = 0; i < attributes_vector.size (); i++)
-    attributes[i] = strdup (attributes_vector[i].c_str ());
-  attributes[attributes_vector.size ()] = NULL;// don't ask how I remembered...
-
-  /* FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=424459
-   * here things should be done with givenname=..., but for this bug
-   */
-  if (!search_filter.empty ())
-    filter = "(cn=*" + search_filter + "*)";
-  else
-    filter = "(cn=*)";
+sasl_bound:
+  if (!search_filter.empty ()) {
+    if (search_filter[0] == '(' &&
+        search_filter[search_filter.length()-1] == ')') {
+      fstr = search_filter.c_str();
+      goto do_search;
+    }
+    fterm = "*" + search_filter + "*";
+  } else {
+    fterm = "*";
+  }
+  filter = std::string (bookinfo.urld->lud_filter);
+  pos = 0;
+  while ((pos=filter.find('$', pos)) != std::string::npos) {
+    filter.replace (pos, 1, fterm);
+    pos += fterm.length();
+  }
+  fstr = filter.c_str();
 
+do_search:
   msgid = ldap_search_ext (ldap_context,
-			   base.c_str (),
-			   iscope,
-			   filter.c_str (),
-			   attributes,
+			   bookinfo.urld->lud_dn,
+			   bookinfo.urld->lud_scope,
+			   fstr,
+			   bookinfo.urld->lud_attrs,
 			   0, /* attrsonly */
 			   NULL, NULL,
 			   NULL, 0, &msgid);
 
-  for (unsigned int i = 0; i < attributes_vector.size (); i++)
-    free (attributes[i]);
-  free (attributes);
-
   if (msgid == -1) {
 
     status = std::string (_("Could not search"));
@@ -583,7 +873,7 @@
 
     if (ldap_msgtype (msg_result) == LDAP_RES_SEARCH_ENTRY) {
 
-      contact = parse_result (core, ldap_context, msg_result);
+      contact = parse_result (msg_result);
       if (contact != NULL) {
 	add_contact (*contact);
         nbr++;
@@ -606,36 +896,66 @@
 }
 
 void
-OPENLDAP::Book::edit ()
+OPENLDAP::BookForm (Ekiga::FormRequestSimple &request, struct BookInfo &info,
+  std::string title)
 {
-  Ekiga::FormRequestSimple request;
+  std::string callAttr = "";
 
-  request.title (_("Edit LDAP directory"));
+  request.title (title);
 
   request.instructions (_("Please edit the following fields"));
 
-  request.text ("name", _("_Name"), name);
-  request.text ("hostname", _("_Hostname"), hostname);
-  { // Snark hates C++ -- and there isn't only a reason for it -- but many
-    std::stringstream strm;
-    std::string port_string;
-    strm << port;
-    strm >> port_string;
-    request.text ("port", _("_Port"), port_string);
-  }
-  request.text ("base", _("_Base DN"), base);
+  request.text ("name", _("Book _Name"), info.name);
+  request.text ("uri", _("Server _URI"), info.uri_host);
+  request.text ("base", _("_Base DN"), info.urld->lud_dn);
 
   {
     std::map<std::string, std::string> choices;
+    std::string scopes[]= {"base","one","sub"};
 
     choices["sub"] = _("Subtree");
-    choices["single"] = _("Single Level");
-    request.single_choice ("scope", _("_Scope"), scope, choices);
+    choices["onelevel"] = _("Single Level");
+    request.single_choice ("scope", _("_Search Scope"),
+      scopes[info.urld->lud_scope], choices);
+  }
+
+  /* attrs[0] is the name attribute */
+  for (int i=1; info.urld->lud_attrs[i]; i++) {
+    if (i>1) callAttr += ",";
+    callAttr += std::string(info.urld->lud_attrs[i]);
+  }
+  request.text ("nameAttr", _("_DisplayName Attribute"), info.urld->lud_attrs[0]);
+  request.text ("callAttr", _("Call _Attributes"), callAttr);
+  request.text ("filter", _("_Filter Template"), info.urld->lud_filter);
+
+  request.text ("authcID", _("Bind _ID"), info.authcID);
+  request.private_text ("password", _("_Password"), info.password);
+  request.boolean ("startTLS", _("Use TLS"), info.starttls);
+  request.boolean ("sasl", _("Use SASL"), info.sasl);
+  {
+    std::map<std::string, std::string> mechs;
+    const char **mechlist;
+
+    mechlist = sasl_global_listmech();
+
+    mechs[""] = "<default>";
+    if (mechlist) {
+      for (int i=0; mechlist[i]; i++) {
+        std::string mech = std::string(mechlist[i]);
+        mechs[mech] = mech;
+      }
+    }
+    request.single_choice ("saslMech", _("SASL _Mechanism"),
+      info.saslMech, mechs);
   }
+}
 
-  request.text ("call-attribute", _("Call _Attribute"), call_attribute);
+void
+OPENLDAP::Book::edit ()
+{
+  Ekiga::FormRequestSimple request;
 
-  request.private_text ("password", _("_Password"), password);
+  OPENLDAP::BookForm (request, bookinfo, std::string(_("Edit LDAP directory")));
 
   request.submitted.connect (sigc::mem_fun (this,
 					    &OPENLDAP::Book::on_edit_form_submitted));
@@ -650,42 +970,130 @@
   }
 }
 
+int
+OPENLDAP::BookFormInfo (Ekiga::Form &result, struct BookInfo &bookinfo,
+  std::string &errmsg)
+{
+  LDAPURLDesc *url_base = NULL, *url_host = NULL;
+  char *url_str;
+
+  std::string name = result.text ("name");
+  std::string uri = result.text ("uri");
+  std::string nameAttr = result.text ("nameAttr");
+  std::string callAttr = result.text ("callAttr");
+  std::string filter = result.text ("filter");
+
+  errmsg = "";
+
+  if (name.empty())
+    errmsg += _("Please provide a Book Name for this directory\n");
+
+  if (uri.empty())
+    errmsg += _("Please provide a Server URI\n");
+
+  if (nameAttr.empty())
+    errmsg += _("Please provide a DisplayName Attribute\n");
+
+  if (callAttr.empty())
+    errmsg += _("Please provide a Call Attribute\n");
+
+  if (ldap_url_parse (uri.c_str(), &url_host))
+    errmsg += _("Invalid Server URI\n");
+
+  if (!errmsg.empty()) {
+    return -1;
+  }
+
+  if (filter.empty())
+    filter = "(cn=$)";
+
+  bookinfo.name = name;
+  std::string base = result.text ("base");
+  std::string new_bits = "ldap:///?"; +
+    result.text ("nameAttr") + "," +
+    result.text ("callAttr") + "?" +
+    result.single_choice ("scope") + "?" +
+    result.text ("filter");
+  bookinfo.authcID = result.text ("authcID");
+  bookinfo.password = result.private_text ("password");
+  bookinfo.starttls = result.boolean ("startTLS");
+  bookinfo.sasl = result.boolean ("sasl");
+  bookinfo.saslMech = result.single_choice ("saslMech");
+
+  if (bookinfo.sasl || bookinfo.starttls) {
+    new_bits += "?";
+    if (bookinfo.starttls)
+      new_bits += "StartTLS";
+    if (bookinfo.sasl) {
+      if (bookinfo.starttls)
+        new_bits += ",";
+      new_bits += "SASL";
+      if (!bookinfo.saslMech.empty())
+        new_bits += "=" + bookinfo.saslMech;
+    }
+  }
+
+  ldap_url_parse (new_bits.c_str(), &url_base);
+  url_host->lud_dn = ldap_strdup (base.c_str());
+  url_host->lud_attrs = url_base->lud_attrs;
+  url_host->lud_scope = url_base->lud_scope;
+  url_host->lud_filter = url_base->lud_filter;
+  if (!url_host->lud_exts) {
+    url_host->lud_exts = url_base->lud_exts;
+    url_base->lud_exts = NULL;
+  }
+  url_base->lud_attrs = NULL;
+  url_base->lud_filter = NULL;
+  ldap_free_urldesc (url_base);
+
+  if (bookinfo.urld) ldap_free_urldesc (bookinfo.urld);
+  bookinfo.urld = url_host;
+  url_str = ldap_url_desc2str (url_host);
+  bookinfo.uri = std::string(url_str);
+  ldap_memfree (url_str);
+
+  {
+    size_t pos;
+    pos = bookinfo.uri.find ('/', strlen(url_host->lud_scheme) + 3);
+    if (pos != std::string::npos)
+      bookinfo.uri_host = bookinfo.uri.substr (0,pos);
+    else
+      bookinfo.uri_host = bookinfo.uri;
+  }
+  return 0;
+}
+
 void
 OPENLDAP::Book::on_edit_form_submitted (Ekiga::Form &result)
 {
   try {
+    std::string errmsg;
+    if (OPENLDAP::BookFormInfo (result, bookinfo, errmsg)) {
+      Ekiga::FormRequestSimple request;
+
+      result.visit (request);
+      request.error (errmsg);
+      request.submitted.connect (sigc::mem_fun (this,
+					    &OPENLDAP::Book::on_edit_form_submitted));
+
+      if (!questions.handle_request (&request)) {
+
+        // FIXME: better error reporting
+#ifdef __GNUC__
+        std::cout << "Unhandled form request in "
+	          << __PRETTY_FUNCTION__ << std::endl;
+#endif
+      }
+      return;
+    }
+
+    robust_xmlNodeSetContent (node, &name_node, "name", bookinfo.name);
 
-    std::string new_name = result.text ("name");
-    std::string new_hostname = result.text ("hostname");
-    std::string new_port_string = result.text ("port");
-    std::string new_base = result.text ("base");
-    std::string new_scope = result.single_choice ("scope");
-    std::string new_call_attribute = result.text ("call-attribute");
-    std::string new_password = result.private_text ("password");
-    int new_port = std::atoi (new_port_string.c_str ());
-
-    name = new_name;
-    robust_xmlNodeSetContent (node, &name_node, "name", name);
-
-    hostname = new_hostname;
-    robust_xmlNodeSetContent (node, &hostname_node, "hostname", hostname);
-
-    port = new_port;
-    robust_xmlNodeSetContent (node, &port_node, "port", new_port_string);
-
-    base = new_base;
-    robust_xmlNodeSetContent (node, &base_node, "base", base);
-
-    scope = new_scope;
-    robust_xmlNodeSetContent (node, &scope_node, "scope", scope);
-
-    call_attribute = new_call_attribute;
-    robust_xmlNodeSetContent (node, &call_attribute_node,
-			      "call_attribute", call_attribute);
+    robust_xmlNodeSetContent (node, &uri_node, "uri", bookinfo.uri);
 
-    password = new_password;
-    robust_xmlNodeSetContent (node, &password_node, "password", password);
+    robust_xmlNodeSetContent (node, &authcID_node, "authcID", bookinfo.authcID);
 
+    robust_xmlNodeSetContent (node, &password_node, "password", bookinfo.password);
     updated.emit ();
     trigger_saving.emit ();
 

Modified: trunk/lib/engine/addressbook/ldap/ldap-book.h
==============================================================================
--- trunk/lib/engine/addressbook/ldap/ldap-book.h	(original)
+++ trunk/lib/engine/addressbook/ldap/ldap-book.h	Fri Oct 10 19:31:28 2008
@@ -30,6 +30,7 @@
  *                         ldap-book.h  -  description
  *                         ------------------------------------------
  *   begin                : written in 2007 by Julien Puydt
+ *                        : completed in 2008 by Howard Chu
  *   copyright            : (c) 2007 by Julien Puydt
  *   description          : declaration of a LDAP book
  *
@@ -44,12 +45,35 @@
 #include "runtime.h"
 #include "book-impl.h"
 #include "form.h"
+#include "form-request-simple.h"
 
 #include "ldap-contact.h"
 
+#include <ldap.h>
+
 namespace OPENLDAP
 {
 
+  struct BookInfo {
+    std::string name;
+    std::string uri;
+    std::string uri_host;
+    std::string authcID;
+    std::string password;
+    std::string saslMech;
+    LDAPURLDesc *urld;
+    bool sasl;
+    bool starttls;
+  };
+
+  void BookForm (Ekiga::FormRequestSimple &req, struct BookInfo &info,
+  	std::string title );
+
+  int BookFormInfo (Ekiga::Form &result, struct BookInfo &info,
+  	std::string &errmsg);
+
+  void BookInfoParse (struct BookInfo &info);
+
 /**
  * @addtogroup contacts
  * @internal
@@ -65,13 +89,7 @@
 	  xmlNodePtr node);
 
     Book (Ekiga::ServiceCore &_core,
-	  const std::string _name,
-	  const std::string _hostname,
-	  int _port,
-	  const std::string _base,
-	  const std::string _scope,
-	  const std::string _call_attribute,
-	  const std::string _password);
+    	  OPENLDAP::BookInfo _bookinfo);
 
     ~Book ();
 
@@ -94,12 +112,20 @@
 
     sigc::signal<void> trigger_saving;
 
+    /* public for access from C */
+    void on_sasl_form_submitted (Ekiga::Form &);
+    Ekiga::FormBuilder *saslform;
+
   private:
 
     void refresh_start ();
     void refresh_bound ();
     void refresh_result ();
 
+    OPENLDAP::Contact *parse_result(struct ldapmsg *);
+
+    void parse_uri();
+
     void edit ();
     void on_edit_form_submitted (Ekiga::Form &);
 
@@ -107,27 +133,13 @@
     Ekiga::ContactCore *contact_core;
     xmlNodePtr node;
 
-    std::string name;
     xmlNodePtr name_node;
-
-    std::string hostname;
-    xmlNodePtr hostname_node;
-
-    int port;
-    xmlNodePtr port_node;
-
-    std::string base;
-    xmlNodePtr base_node;
-
-    std::string scope;
-    xmlNodePtr scope_node;
-
-    std::string call_attribute;
-    xmlNodePtr call_attribute_node;
-
-    std::string password;
+    xmlNodePtr uri_node;
+    xmlNodePtr authcID_node;
     xmlNodePtr password_node;
 
+    struct BookInfo bookinfo;
+
     struct ldap *ldap_context;
     unsigned int patience;
 

Modified: trunk/lib/engine/addressbook/ldap/ldap-contact.cpp
==============================================================================
--- trunk/lib/engine/addressbook/ldap/ldap-contact.cpp	(original)
+++ trunk/lib/engine/addressbook/ldap/ldap-contact.cpp	Fri Oct 10 19:31:28 2008
@@ -30,6 +30,7 @@
  *                         ldap-contact.cpp  -  description
  *                         ------------------------------------------
  *   begin                : written in 2007 by Julien Puydt
+ *                        : completed in 2008 by Howard Chu
  *   copyright            : (c) 2007 by Julien Puydt
  *   description          : implementation of a LDAP contact
  *
@@ -38,12 +39,12 @@
 #include <iostream>
 
 #include "ldap-contact.h"
-
+#include "menu-builder-tools.h"
 
 OPENLDAP::Contact::Contact (Ekiga::ServiceCore &_core,
 			    const std::string _name,
-			    const std::string _uri)
-  : core(_core), name(_name), uri(_uri)
+			    const std::map<std::string, std::string> _uris)
+  : core(_core), name(_name), uris(_uris)
 {
 }
 
@@ -73,7 +74,20 @@
    * (before or after the uri-specific actions)
    */
 
-  return contact_core->populate_contact_menu (*this, uri, builder);
+  Ekiga::TemporaryMenuBuilder tmp_builder;
+
+  bool result = false;
+  for (std::map<std::string, std::string>::const_iterator iter
+    = uris.begin ();
+    iter != uris.end ();
+    iter++) {
+      if (contact_core->populate_contact_menu (*this, iter->second, tmp_builder)) {
+	  builder.add_ghost ("", iter->first);
+	  tmp_builder.populate_menu (builder);
+	  result = true;
+    }
+  }
+  return result;
 }
 
 bool

Modified: trunk/lib/engine/addressbook/ldap/ldap-contact.h
==============================================================================
--- trunk/lib/engine/addressbook/ldap/ldap-contact.h	(original)
+++ trunk/lib/engine/addressbook/ldap/ldap-contact.h	Fri Oct 10 19:31:28 2008
@@ -30,6 +30,7 @@
  *                         ldap-contact.h  -  description
  *                         ------------------------------------------
  *   begin                : written in 2007 by Julien Puydt
+ *                        : completed in 2008 by Howard Chu
  *   copyright            : (c) 2007 by Julien Puydt
  *   description          : declaration of a LDAP contact
  *
@@ -55,7 +56,7 @@
 
     Contact (Ekiga::ServiceCore &_core,
 	     const std::string _name,
-	     const std::string _uri);
+    	     const std::map<std::string, std::string> _uris);
 
     ~Contact ();
 
@@ -72,8 +73,7 @@
     Ekiga::ServiceCore &core;
 
     std::string name;
-
-    std::string uri;
+    std::map<std::string, std::string> uris;
   };
 
 /**

Modified: trunk/lib/engine/addressbook/ldap/ldap-main.cpp
==============================================================================
--- trunk/lib/engine/addressbook/ldap/ldap-main.cpp	(original)
+++ trunk/lib/engine/addressbook/ldap/ldap-main.cpp	Fri Oct 10 19:31:28 2008
@@ -40,6 +40,8 @@
 #include "ldap-main.h"
 #include "ldap-source.h"
 
+#include <sasl/sasl.h>
+
 bool
 ldap_init (Ekiga::ServiceCore &core,
 	   int */*argc*/,
@@ -56,6 +58,7 @@
     service = new OPENLDAP::Source (core);
     core.add (*service);
     contact_core->add_source (*service);
+    sasl_client_init (NULL);
     result = true;
   }
 

Modified: trunk/lib/engine/addressbook/ldap/ldap-source.cpp
==============================================================================
--- trunk/lib/engine/addressbook/ldap/ldap-source.cpp	(original)
+++ trunk/lib/engine/addressbook/ldap/ldap-source.cpp	Fri Oct 10 19:31:28 2008
@@ -30,6 +30,7 @@
  *                         ldap-source.cpp  -  description
  *                         ------------------------------------------
  *   begin                : written in 2007 by Julien Puydt
+ *                        : completed in 2008 by Howard Chu
  *   copyright            : (c) 2007 by Julien Puydt
  *   description          : implementation of a LDAP source
  *
@@ -42,7 +43,6 @@
 #include "config.h"
 
 #include "gmconf.h"
-#include "form-request-simple.h"
 
 #include "ldap-source.h"
 
@@ -105,20 +105,13 @@
 }
 
 void
-OPENLDAP::Source::add (const std::string name,
-		       const std::string hostname,
-		       int port,
-		       const std::string base,
-		       const std::string scope,
-		       const std::string call_attribute,
-		       const std::string password)
+OPENLDAP::Source::add ()
 {
   Book *book = NULL;
   xmlNodePtr root;
 
   root = xmlDocGetRootElement (doc);
-  book = new Book (core, name, hostname, port, base,
-		   scope, call_attribute, password);
+  book = new Book (core, bookinfo);
 
   xmlAddChild (root, book->get_node ());
 
@@ -150,24 +143,17 @@
 {
   Ekiga::FormRequestSimple request;
 
-  request.title (_("Create LDAP directory"));
+  bookinfo.name = "";
+  bookinfo.uri = "ldap://localhost/dc=net?cn,telephoneNumber?sub?(cn=$)",
+  bookinfo.authcID = "";
+  bookinfo.password = "";
+  bookinfo.saslMech = "";
+  bookinfo.urld = NULL;
+  bookinfo.sasl = false;
+  bookinfo.starttls = false;
 
-  request.instructions (_("Please edit the following fields"));
-
-  request.text ("name", _("_Name:"), "");
-  request.text ("hostname", _("_Hostname:"), "");
-  request.text ("port", _("_Port:"), "389");
-  request.text ("base", _("_Base DN:"), "dc=net");
-  {
-    std::map<std::string, std::string> choices;
-
-    choices["sub"] = _("_Subtree");
-    choices["single"] = _("Single _Level");
-    request.single_choice ("scope", _("_Scope"), "sub", choices);
-  }
-
-  request.text ("call-attribute", _("Call _Attribute"), "telephoneNumber");
-  request.private_text ("password", _("Password"), "");
+  OPENLDAP::BookInfoParse (bookinfo);
+  OPENLDAP::BookForm (request, bookinfo, _("Create LDAP directory"));
 
   request.submitted.connect (sigc::mem_fun (this,
 					    &OPENLDAP::Source::on_new_book_form_submitted));
@@ -185,12 +171,17 @@
 void
 OPENLDAP::Source::new_ekiga_net_book ()
 {
-  add (_("Ekiga.net Directory"),
-       "ekiga.net", 389,
-       "dc=ekiga,dc=net",
-       "sub",
-       "telephoneNumber", 
-       "");
+  bookinfo.name = _("Ekiga.net Directory");
+  bookinfo.uri =
+    "ldap://ekiga.net/dc=ekiga,dc=net?cn,telephoneNumber?sub?(cn=$)";
+  bookinfo.authcID = "";
+  bookinfo.password = "";
+  bookinfo.saslMech = "";
+  bookinfo.urld = NULL;
+  bookinfo.sasl = false;
+  bookinfo.starttls = false;
+
+  add ();
   save ();
 }
 
@@ -198,17 +189,28 @@
 OPENLDAP::Source::on_new_book_form_submitted (Ekiga::Form &result)
 {
   try {
+    std::string errmsg;
+
+    if (OPENLDAP::BookFormInfo (result, bookinfo, errmsg)) {
+      Ekiga::FormRequestSimple request;
 
-    std::string name = result.text ("name");
-    std::string hostname = result.text ("hostname");
-    std::string port_string = result.text ("port");
-    std::string base = result.text ("base");
-    std::string scope = result.single_choice ("scope");
-    std::string call_attribute = result.text ("call-attribute");
-    std::string password = result.private_text ("password");
-    int port = atoi (port_string.c_str ());
+      result.visit (request);
+      request.error (errmsg);
+      request.submitted.connect (sigc::mem_fun (this,
+					    &OPENLDAP::Source::on_new_book_form_submitted));
+
+      if (!questions.handle_request (&request)) {
+
+        // FIXME: better error reporting
+#ifdef __GNUC__
+        std::cout << "Unhandled form request in "
+	          << __PRETTY_FUNCTION__ << std::endl;
+#endif
+      }
+      return;
+    }
 
-    add (name, hostname, port, base, scope, call_attribute, password);
+    add ();
     save ();
 
   } catch (Ekiga::Form::not_found) {

Modified: trunk/lib/engine/addressbook/ldap/ldap-source.h
==============================================================================
--- trunk/lib/engine/addressbook/ldap/ldap-source.h	(original)
+++ trunk/lib/engine/addressbook/ldap/ldap-source.h	Fri Oct 10 19:31:28 2008
@@ -30,6 +30,7 @@
  *                         ldap-source.h  -  description
  *                         ------------------------------------------
  *   begin                : written in 2007 by Julien Puydt
+ *                        : completed in 2008 by Howard Chu
  *   copyright            : (c) 2007 by Julien Puydt
  *   description          : interface of a LDAP source
  *
@@ -77,15 +78,11 @@
     Ekiga::ServiceCore &core;
     xmlDocPtr doc;
 
+    struct BookInfo bookinfo;
+
     void add (xmlNodePtr node);
 
-    void add (const std::string name,
-	      const std::string hostname,
-	      int port,
-	      const std::string base,
-	      const std::string scope,
-	      const std::string call_attribute,
-	      const std::string password);
+    void add ();
 
     void common_add (Book &book);
 

Modified: trunk/src/gui/callbacks.cpp
==============================================================================
--- trunk/src/gui/callbacks.cpp	(original)
+++ trunk/src/gui/callbacks.cpp	Fri Oct 10 19:31:28 2008
@@ -128,6 +128,7 @@
       "Damien Sandras <dsandras seconix com>",
       "",
       N_("Contributors:"),
+      "Howard Chu <hyc symas com>",
       "Yannick Defais <sevmek free fr>",
       "Kilian Krause <kk verfaction de>", 
       "Vincent Luba <luba novacom be>",



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]