diff -rN -u old-telepathy-glib.param-setter/telepathy-glib/base-connection-manager.c new-telepathy-glib.param-setter/telepathy-glib/base-connection-manager.c
--- old-telepathy-glib.param-setter/telepathy-glib/base-connection-manager.c	2007-08-21 21:52:16.000000000 +0100
+++ new-telepathy-glib.param-setter/telepathy-glib/base-connection-manager.c	2007-08-21 21:52:17.000000000 +0100
@@ -201,42 +201,42 @@
 }
 
 static GValue *
-param_default_value (const TpCMParamSpec *params, int i)
+param_default_value (const TpCMParamSpec *param)
 {
   GValue *value;
 
-  value = tp_g_value_slice_new (params[i].gtype);
+  value = tp_g_value_slice_new (param->gtype);
 
   /* If HAS_DEFAULT is false, we don't really care what the value is, so we'll
    * just use whatever's in the user-supplied param spec. As long as we're
    * careful to accept NULL, that should be fine. */
 
-  switch (params[i].dtype[0])
+  switch (param->dtype[0])
     {
       case DBUS_TYPE_STRING:
-        g_assert (params[i].gtype == G_TYPE_STRING);
-        if (params[i].def == NULL)
+        g_assert (param->gtype == G_TYPE_STRING);
+        if (param->def == NULL)
           g_value_set_static_string (value, "");
         else
-          g_value_set_static_string (value, (const gchar *) params[i].def);
+          g_value_set_static_string (value, (const gchar *) param->def);
         break;
       case DBUS_TYPE_INT16:
       case DBUS_TYPE_INT32:
-        g_assert (params[i].gtype == G_TYPE_INT);
-        g_value_set_int (value, GPOINTER_TO_INT (params[i].def));
+        g_assert (param->gtype == G_TYPE_INT);
+        g_value_set_int (value, GPOINTER_TO_INT (param->def));
         break;
       case DBUS_TYPE_UINT16:
       case DBUS_TYPE_UINT32:
-        g_assert (params[i].gtype == G_TYPE_UINT);
-        g_value_set_uint (value, GPOINTER_TO_UINT (params[i].def));
+        g_assert (param->gtype == G_TYPE_UINT);
+        g_value_set_uint (value, GPOINTER_TO_UINT (param->def));
         break;
       case DBUS_TYPE_BOOLEAN:
-        g_assert (params[i].gtype == G_TYPE_BOOLEAN);
-        g_value_set_boolean (value, GPOINTER_TO_INT (params[i].def));
+        g_assert (param->gtype == G_TYPE_BOOLEAN);
+        g_value_set_boolean (value, GPOINTER_TO_INT (param->def));
         break;
       default:
         g_error ("parameter_defaults: encountered unknown type %s on "
-            "argument %s", params[i].dtype, params[i].name);
+            "argument %s", param->dtype, param->name);
     }
 
   return value;
@@ -244,63 +244,32 @@
 
 static void
 set_param_from_default (const TpCMParamSpec *paramspec,
-                        gpointer params)
+                        gpointer params,
+                        TpCMParamSetter set_param)
 {
-  switch (paramspec->dtype[0])
+  GValue *value = param_default_value (paramspec);
+  GError *error = NULL;
+  g_assert (set_param);
+  if (!set_param (params, paramspec, value, &error))
     {
-    case DBUS_TYPE_STRING:
-      {
-        gchar **save_to = (gchar **) (params + paramspec->offset);
-        g_assert (paramspec->gtype == G_TYPE_STRING);
-        g_assert (paramspec->def != NULL);
-
-        *save_to = g_strdup ((const gchar *) (paramspec->def));
-        DEBUG ("%s = \"%s\"", paramspec->name, *save_to);
-      }
-      break;
-    case DBUS_TYPE_INT16:
-    case DBUS_TYPE_INT32:
-      {
-        gint *save_to = (gint *) (params + paramspec->offset);
-        g_assert (paramspec->gtype == G_TYPE_INT);
-
-        *save_to = GPOINTER_TO_INT (paramspec->def);
-        DEBUG ("%s = %d = 0x%x", paramspec->name, *save_to, *save_to);
-      }
-      break;
-    case DBUS_TYPE_UINT16:
-    case DBUS_TYPE_UINT32:
-      {
-        guint *save_to = (guint *) (params + paramspec->offset);
-        g_assert (paramspec->gtype == G_TYPE_UINT);
-
-        *save_to = GPOINTER_TO_UINT (paramspec->def);
-        DEBUG ("%s = %u = 0x%x", paramspec->name, *save_to, *save_to);
-      }
-      break;
-    case DBUS_TYPE_BOOLEAN:
-      {
-        gboolean *save_to = (gboolean *) (params + paramspec->offset);
-        g_assert (paramspec->gtype == G_TYPE_BOOLEAN);
-        g_assert (paramspec->def == GINT_TO_POINTER (TRUE) || paramspec->def == GINT_TO_POINTER (FALSE));
-
-        *save_to = GPOINTER_TO_INT (paramspec->def);
-        DEBUG ("%s = %s", paramspec->name, *save_to ? "TRUE" : "FALSE");
-      }
-      break;
-    default:
-      g_error ("%s: encountered unhandled D-Bus type %s "
-               "on argument %s", G_STRFUNC, paramspec->dtype, paramspec->name);
-      g_assert_not_reached ();
+      if (error)
+        g_error (error->message);
+      else
+        g_error ("set_param failed for '%s', but didn't say why",
+          paramspec->name);
     }
+  tp_g_value_slice_free (value);
 }
 
 static gboolean
 set_param_from_value (const TpCMParamSpec *paramspec,
                       GValue *value,
                       void *params,
+                      TpCMParamSetter set_param,
                       GError **error)
 {
+  g_assert (set_param);
+
   if (G_VALUE_TYPE (value) != paramspec->gtype)
     {
       DEBUG ("expected type %s for parameter %s, got %s",
@@ -326,72 +295,7 @@
       g_return_val_if_fail (G_VALUE_TYPE (value) == paramspec->gtype, FALSE);
     }
 
-  switch (paramspec->dtype[0])
-    {
-      case DBUS_TYPE_STRING:
-        {
-          gchar **save_to = (gchar **) (params + paramspec->offset);
-          const gchar *str;
-
-          g_assert (paramspec->gtype == G_TYPE_STRING);
-          str = g_value_get_string (value);
-          g_free (*save_to);
-          if (str == NULL)
-            {
-              *save_to = g_strdup ("");
-            }
-          else
-            {
-              *save_to = g_value_dup_string (value);
-            }
-          if (DEBUGGING)
-            {
-              if (strstr (paramspec->name, "password") != NULL)
-                DEBUG ("%s = <hidden>", paramspec->name);
-              else
-                DEBUG ("%s = \"%s\"", paramspec->name, *save_to);
-            }
-        }
-        break;
-      case DBUS_TYPE_INT16:
-      case DBUS_TYPE_INT32:
-        {
-          gint i = g_value_get_int (value);
-
-          g_assert (paramspec->gtype == G_TYPE_INT);
-          *((gint *) (params + paramspec->offset)) = i;
-          DEBUG ("%s = %d = 0x%x", paramspec->name, i, i);
-        }
-        break;
-      case DBUS_TYPE_UINT16:
-      case DBUS_TYPE_UINT32:
-        {
-          guint i = g_value_get_uint (value);
-
-          g_assert (paramspec->gtype == G_TYPE_UINT);
-          *((guint *) (params + paramspec->offset)) = i;
-          DEBUG ("%s = %u = 0x%x", paramspec->name, i, i);
-        }
-        break;
-      case DBUS_TYPE_BOOLEAN:
-        {
-          gboolean b = g_value_get_boolean (value);
-
-          g_assert (paramspec->gtype == G_TYPE_BOOLEAN);
-          *((gboolean *) (params + paramspec->offset)) = b;
-          DEBUG ("%s = %s", paramspec->name, b ? "TRUE" : "FALSE");
-        }
-        break;
-      default:
-        g_error ("set_param_from_value: encountered unhandled D-Bus type %s "
-                 "on argument %s", paramspec->dtype, paramspec->name);
-        g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
-            "encountered unhandled D-Bus type %s for account parameter %s",
-            paramspec->dtype, paramspec->name);
-        return FALSE;
-    }
-
-  return TRUE;
+  return (set_param (params, paramspec, value, error));
 }
 
 static void
@@ -408,6 +312,7 @@
                   GHashTable *provided,
                   TpIntSet *params_present,
                   void *params,
+                  TpCMParamSetter set_param,
                   GError **error)
 {
   int i;
@@ -444,7 +349,7 @@
           else if (paramspec[i].flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT)
             {
               /* FIXME: Should we add it to params_present? */
-              set_param_from_default (&paramspec[i], params);
+              set_param_from_default (&paramspec[i], params, set_param);
             }
           else
             {
@@ -454,7 +359,8 @@
         }
       else
         {
-          if (!set_param_from_value (&paramspec[i], value, params, error))
+          if (!set_param_from_value (&paramspec[i], value, params, set_param,
+                error))
             {
               return FALSE;
             }
@@ -524,7 +430,7 @@
       g_value_set_static_boxed (&param,
         dbus_g_type_specialized_construct (TP_TYPE_PARAM));
 
-      def_value = param_default_value (protospec->parameters, i);
+      def_value = param_default_value (&(protospec->parameters[i]));
       dbus_g_type_struct_set (&param,
         0, protospec->parameters[i].name,
         1, protospec->parameters[i].flags,
@@ -604,6 +510,7 @@
   void *params = NULL;
   TpIntSet *params_present = NULL;
   const TpCMProtocolSpec *protospec = NULL;
+  TpCMParamSetter set_param = NULL;
 
   g_assert (TP_IS_BASE_CONNECTION_MANAGER (iface));
   g_assert (cls->new_connection != NULL);
@@ -621,8 +528,12 @@
   params_present = tp_intset_new ();
   params = protospec->params_new ();
 
+  set_param = protospec->set_param;
+  if (!set_param)
+      set_param = tp_cm_param_setter_by_offset;
+
   if (!parse_parameters (protospec->parameters, parameters, params_present,
-        params, &error))
+        params, set_param, &error))
     {
       goto ERROR;
     }
@@ -763,3 +674,77 @@
     }
   return TRUE;
 }
+
+gboolean
+tp_cm_param_setter_by_offset (gpointer params,
+                              const TpCMParamSpec *paramspec,
+                              const GValue *value,
+                              GError **error)
+{
+  switch (paramspec->dtype[0])
+    {
+      case DBUS_TYPE_STRING:
+        {
+          gchar **save_to = (gchar **) (params + paramspec->offset);
+          const gchar *str;
+
+          g_assert (paramspec->gtype == G_TYPE_STRING);
+          str = g_value_get_string (value);
+          g_free (*save_to);
+          if (str == NULL)
+            {
+              *save_to = g_strdup ("");
+            }
+          else
+            {
+              *save_to = g_value_dup_string (value);
+            }
+          if (DEBUGGING)
+            {
+              if (strstr (paramspec->name, "password") != NULL)
+                DEBUG ("%s = <hidden>", paramspec->name);
+              else
+                DEBUG ("%s = \"%s\"", paramspec->name, *save_to);
+            }
+        }
+        break;
+      case DBUS_TYPE_INT16:
+      case DBUS_TYPE_INT32:
+        {
+          gint i = g_value_get_int (value);
+
+          g_assert (paramspec->gtype == G_TYPE_INT);
+          *((gint *) (params + paramspec->offset)) = i;
+          DEBUG ("%s = %d = 0x%x", paramspec->name, i, i);
+        }
+        break;
+      case DBUS_TYPE_UINT16:
+      case DBUS_TYPE_UINT32:
+        {
+          guint i = g_value_get_uint (value);
+
+          g_assert (paramspec->gtype == G_TYPE_UINT);
+          *((guint *) (params + paramspec->offset)) = i;
+          DEBUG ("%s = %u = 0x%x", paramspec->name, i, i);
+        }
+        break;
+      case DBUS_TYPE_BOOLEAN:
+        {
+          gboolean b = g_value_get_boolean (value);
+
+          g_assert (paramspec->gtype == G_TYPE_BOOLEAN);
+          *((gboolean *) (params + paramspec->offset)) = b;
+          DEBUG ("%s = %s", paramspec->name, b ? "TRUE" : "FALSE");
+        }
+        break;
+      default:
+        g_error ("set_param_from_value: encountered unhandled D-Bus type %s "
+                 "on argument %s", paramspec->dtype, paramspec->name);
+        g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+            "encountered unhandled D-Bus type %s for account parameter %s",
+            paramspec->dtype, paramspec->name);
+        return FALSE;
+    }
+
+  return TRUE;
+}
diff -rN -u old-telepathy-glib.param-setter/telepathy-glib/base-connection-manager.h new-telepathy-glib.param-setter/telepathy-glib/base-connection-manager.h
--- old-telepathy-glib.param-setter/telepathy-glib/base-connection-manager.h	2007-08-21 21:52:16.000000000 +0100
+++ new-telepathy-glib.param-setter/telepathy-glib/base-connection-manager.h	2007-08-21 21:52:16.000000000 +0100
@@ -139,12 +139,32 @@
     TpCMParamFilter filter;
     const gpointer filter_data;
 
+    const gpointer setter_data;
+
     /*<private>*/
-    gpointer _future1;
     gpointer _future2;
 };
 
 /**
+ * TpCMParamSetter: XXX
+ * @params:
+ * @paramspec: The parameter specification. The setter is likely to use
+ *  name and either offset or setter_data.
+ * @value: The value for that parameter provided by the user.
+ *
+ * Signature of a callback used to validate and/or normalize user-provided
+ * CM parameter values.
+ *
+ * Returns: %TRUE to accept, %FALSE (with @error set) to reject
+ */
+typedef gboolean (*TpCMParamSetter) (gpointer params,
+    const TpCMParamSpec *paramspec, const GValue *value, GError **error);
+
+/** XXX document */
+gboolean tp_cm_param_setter_by_offset (gpointer params,
+    const TpCMParamSpec *paramspec, const GValue *value, GError **error);
+
+/**
  * TpCMProtocolSpec:
  * @name: The name which should be passed to RequestConnection for this
  *        protocol.
@@ -158,10 +178,14 @@
  * @params_free: A function which deallocates the opaque data structure
  *               provided by #params_new, including deallocating its
  *               data members (currently, only strings) if necessary.
+ * @set_param: A function to set the given parameter to the given value
+ *             within the passed opaque structure.  This may be left %NULL,
+ *             in which case XXX will be used (as in older versions of
+ *             telepathy-glib).
  *
  * Structure representing a connection manager protocol.
  *
- * In addition to the fields documented here, there are four gpointer fields
+ * In addition to the fields documented here, there are three gpointer fields
  * which must currently be %NULL. A meaning may be defined for these in a
  * future version of telepathy-glib.
  */
@@ -171,8 +195,9 @@
     gpointer (*params_new) (void);
     void (*params_free) (gpointer);
 
+    TpCMParamSetter set_param;
+
     /*<private>*/
-    gpointer _future1;
     gpointer _future2;
     gpointer _future3;
     gpointer _future4;

