1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* Copyright (C) 2001-2004 Novell, Inc.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU Lesser General Public
7 * License as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "e2k-properties.h"
25 #include "e2k-propnames.h"
26 #include "e2k-utils.h"
32 #include <libxml/xmlmemory.h>
34 struct E2kProperties {
35 GHashTable *set, *removed;
40 const char *namespace;
41 const char *short_name;
47 static GHashTable *known_properties;
52 * Creates a new (empty) #E2kProperties structure
54 * Return value: the structure
57 e2k_properties_new (void)
61 props = g_new0 (E2kProperties, 1);
62 props->set = g_hash_table_new (g_str_hash, g_str_equal);
63 props->removed = g_hash_table_new (g_str_hash, g_str_equal);
69 copy_prop (gpointer key, gpointer value, gpointer data)
71 const char *name = key;
72 GHashTable *props_copy = data;
76 pi = g_hash_table_lookup (known_properties, name);
78 case E2K_PROP_TYPE_BINARY_ARRAY:
80 GPtrArray *orig = value, *copy;
81 GByteArray *new, *old;
84 copy = g_ptr_array_new ();
85 for (i = 0; i < orig->len; i++) {
87 new = g_byte_array_new ();
88 g_byte_array_append (new, old->data, old->len);
89 g_ptr_array_add (copy, new);
95 case E2K_PROP_TYPE_STRING_ARRAY:
97 GPtrArray *orig = value, *copy;
100 copy = g_ptr_array_new ();
101 for (i = 0; i < orig->len; i++)
102 g_ptr_array_add (copy, g_strdup (orig->pdata[i]));
107 case E2K_PROP_TYPE_BINARY:
109 GByteArray *orig = value, *copy;
111 copy = g_byte_array_new ();
112 g_byte_array_append (copy, orig->data, orig->len);
117 case E2K_PROP_TYPE_XML:
118 value_copy = xmlCopyNode (value, TRUE);
121 case E2K_PROP_TYPE_STRING:
123 value_copy = g_strdup (value);
127 g_hash_table_insert (props_copy, pi->name, value_copy);
131 * e2k_properties_copy:
132 * @props: an #E2kProperties
134 * Performs a deep copy of @props
136 * Return value: a new copy of @props
139 e2k_properties_copy (E2kProperties *props)
143 g_return_val_if_fail (props != NULL, NULL);
145 copy = e2k_properties_new ();
146 g_hash_table_foreach (props->set, copy_prop, copy->set);
147 g_hash_table_foreach (props->removed, copy_prop, copy->removed);
152 free_prop (E2kPropInfo *pi, gpointer value)
158 case E2K_PROP_TYPE_BINARY_ARRAY:
160 GPtrArray *array = value;
163 for (i = 0; i < array->len; i++)
164 g_byte_array_free (array->pdata[i], TRUE);
165 g_ptr_array_free (array, TRUE);
169 case E2K_PROP_TYPE_STRING_ARRAY:
170 case E2K_PROP_TYPE_INT_ARRAY:
172 GPtrArray *array = value;
175 for (i = 0; i < array->len; i++)
176 g_free (array->pdata[i]);
177 g_ptr_array_free (array, TRUE);
181 case E2K_PROP_TYPE_BINARY:
182 g_byte_array_free (value, TRUE);
185 case E2K_PROP_TYPE_XML:
189 case E2K_PROP_TYPE_STRING:
197 properties_free_cb (gpointer key, gpointer value, gpointer data)
201 pi = g_hash_table_lookup (known_properties, key);
203 free_prop (pi, value);
207 * e2k_properties_free:
208 * @props: an #E2kProperties
210 * Frees @props and all of the properties it contains.
213 e2k_properties_free (E2kProperties *props)
215 g_return_if_fail (props != NULL);
217 g_hash_table_foreach (props->set, properties_free_cb, NULL);
218 g_hash_table_destroy (props->set);
219 g_hash_table_destroy (props->removed);
224 * e2k_properties_get_prop:
225 * @props: an #E2kProperties
226 * @propname: a property name
228 * Retrieves the value of @propname in @props.
230 * Return value: the value of @propname in @props, or %NULL if it is
231 * not set. The caller should not free the value; it is owned by
235 e2k_properties_get_prop (E2kProperties *props, const char *propname)
237 g_return_val_if_fail (props != NULL, NULL);
239 return g_hash_table_lookup (props->set, propname);
243 * e2k_properties_empty:
244 * @props: an #E2kProperties
246 * Tests if @props is empty.
248 * Return value: %TRUE if @props has no properties set, %FALSE if it
249 * has at least one value set.
252 e2k_properties_empty (E2kProperties *props)
254 g_return_val_if_fail (props != NULL, TRUE);
256 return g_hash_table_size (props->set) == 0;
260 extern char e2k_des_key[8];
263 get_propinfo (const char *propname, E2kPropType type)
267 if (!known_properties)
268 known_properties = g_hash_table_new (g_str_hash, g_str_equal);
270 pi = g_hash_table_lookup (known_properties, propname);
272 if (pi->type == E2K_PROP_TYPE_UNKNOWN)
277 pi = g_new (E2kPropInfo, 1);
278 pi->name = g_strdup (propname);
279 pi->namespace = e2k_prop_namespace_name (pi->name);
280 pi->short_name = e2k_prop_property_name (pi->name);
283 if (pi->short_name[0] == 'x')
284 pi->proptag = strtoul (pi->short_name + 1, NULL, 16);
288 g_hash_table_insert (known_properties, pi->name, pi);
294 * e2k_properties_set_string:
295 * @props: an #E2kProperties
296 * @propname: the name of a property
297 * @value: an allocated string
299 * Sets @propname in @props to @value. @props assumes ownership of
304 * e2k_properties_set_string_array:
305 * @props: an #E2kProperties
306 * @propname: the name of a property
307 * @value: an array of allocated strings
309 * Sets @propname in @props to @value. @props assumes ownership of
314 * e2k_properties_set_binary:
315 * @props: an #E2kProperties
316 * @propname: the name of a property
317 * @value: a byte array
319 * Sets @propname in @props to @value. @props assumes ownership of
324 * e2k_properties_set_binary_array:
325 * @props: an #E2kProperties
326 * @propname: the name of a property
327 * @value: an array of byte arrays
329 * Sets @propname in @props to @value. @props assumes ownership of
334 * e2k_properties_set_xml:
335 * @props: an #E2kProperties
336 * @propname: the name of a property
337 * @value: an #xmlNode
339 * Sets @propname in @props to @value. @props assumes ownership of
344 * e2k_properties_set_int:
345 * @props: an #E2kProperties
346 * @propname: the name of a property
349 * Sets @propname in @props to @value.
353 * e2k_properties_set_int_array:
354 * @props: an #E2kProperties
355 * @propname: the name of a property
356 * @value: an array of integers
358 * Sets @propname in @props to @value. @props assumes ownership of
363 * e2k_properties_set_float:
364 * @props: an #E2kProperties
365 * @propname: the name of a property
366 * @value: a floating-point value
368 * Sets @propname in @props to @value.
372 * e2k_properties_set_bool:
373 * @props: an #E2kProperties
374 * @propname: the name of a property
375 * @value: a boolean value
377 * Sets @propname in @props to @value.
381 * e2k_properties_set_date:
382 * @props: an #E2kProperties
383 * @propname: the name of a property
384 * @value: an allocated string containing an Exchange timestamp
386 * Sets @propname in @props to @value. @props assumes ownership of
390 #define E2K_PROPERTIES_SETTER(fname, valuetype, pitype, data) \
392 e2k_properties_set_ ## fname (E2kProperties *props, \
393 const char *propname, \
398 pi = get_propinfo (propname, E2K_PROP_TYPE_ ## pitype); \
399 free_prop (pi, g_hash_table_lookup (props->set, pi->name)); \
400 g_hash_table_insert (props->set, pi->name, data); \
401 g_hash_table_remove (props->removed, pi->name); \
404 E2K_PROPERTIES_SETTER (string, char *, STRING, value)
405 E2K_PROPERTIES_SETTER (string_array, GPtrArray *, STRING_ARRAY, value)
406 E2K_PROPERTIES_SETTER (binary, GByteArray *, BINARY, value)
407 E2K_PROPERTIES_SETTER (binary_array, GPtrArray *, BINARY_ARRAY, value)
408 E2K_PROPERTIES_SETTER (xml, xmlNode *, XML, value)
410 E2K_PROPERTIES_SETTER (int, int, INT, g_strdup_printf ("%d", value))
411 E2K_PROPERTIES_SETTER (int_array, GPtrArray *, INT_ARRAY, value)
412 E2K_PROPERTIES_SETTER (float, float, FLOAT, g_strdup_printf ("%f", value))
413 E2K_PROPERTIES_SETTER (bool, gboolean, BOOL, g_strdup_printf ("%d", value != FALSE))
414 E2K_PROPERTIES_SETTER (date, char *, DATE, value)
419 * e2k_properties_set_type_as_string:
420 * @props: an #E2kProperties
421 * @propname: the name of a property
422 * @type: the type of @value
423 * @value: an allocated string
425 * Sets @propname in @props to @value, but with type @type. @props
426 * assumes ownership of @value.
430 * e2k_properties_set_type_as_string_array:
431 * @props: an #E2kProperties
432 * @propname: the name of a property
433 * @type: the type of @value
434 * @value: an array of allocated strings
436 * Sets @propname in @props to @value, but with type @type. @props
437 * assumes ownership of @value.
440 #define E2K_PROPERTIES_SETTER_AS(fname, valuetype) \
442 e2k_properties_set_type_as_ ## fname (E2kProperties *props, \
443 const char *propname, \
449 pi = get_propinfo (propname, type); \
450 free_prop (pi, g_hash_table_lookup (props->set, pi->name)); \
451 g_hash_table_insert (props->set, pi->name, value); \
452 g_hash_table_remove (props->removed, pi->name); \
455 E2K_PROPERTIES_SETTER_AS (string, char *)
456 E2K_PROPERTIES_SETTER_AS (string_array, GPtrArray *)
459 * e2k_properties_remove:
460 * @props: an #E2kProperties
461 * @propname: the name of a property
463 * Marks @propname removed in @props, so that the corresponding
464 * property will be removed from the object on the server if @props is
465 * used in a PROPPATCH. If the property was formerly set in @props,
466 * this frees the old value.
469 e2k_properties_remove (E2kProperties *props, const char *propname)
473 pi = get_propinfo (propname, E2K_PROP_TYPE_UNKNOWN);
474 free_prop (pi, g_hash_table_lookup (props->set, pi->name));
475 g_hash_table_remove (props->set, pi->name);
476 g_hash_table_insert (props->removed, pi->name, NULL);
479 struct foreach_data {
480 E2kPropertiesForeachFunc callback;
485 foreach_callback (gpointer key, gpointer value, gpointer data)
487 struct foreach_data *fd = data;
490 pi = g_hash_table_lookup (known_properties, key);
492 fd->callback (pi->name, pi->type, value, fd->user_data);
496 * e2k_properties_foreach:
497 * @props: an #E2kProperties
498 * @callback: callback function to call for each set property
499 * @user_data: data to pass to @callback
501 * Calls @callback once for each property that is set in @props (in
502 * unspecified order), passing it the name of the property, the
503 * property's type, its value, and @user_data.
506 e2k_properties_foreach (E2kProperties *props,
507 E2kPropertiesForeachFunc callback,
510 struct foreach_data fd;
512 g_return_if_fail (props != NULL);
514 fd.callback = callback;
515 fd.user_data = user_data;
517 g_hash_table_foreach (props->set, foreach_callback, &fd);
521 * e2k_properties_foreach_removed:
522 * @props: an #E2kProperties
523 * @callback: callback function to call for each set property
524 * @user_data: data to pass to @callback
526 * Calls @callback once for each property marked removed in @props (in
527 * unspecified order), passing it the name of the property, the
528 * property's type (if known), a %NULL value, and @user_data.
531 e2k_properties_foreach_removed (E2kProperties *props,
532 E2kPropertiesForeachFunc callback,
535 struct foreach_data fd;
537 g_return_if_fail (props != NULL);
539 fd.callback = callback;
540 fd.user_data = user_data;
542 g_hash_table_foreach (props->removed, foreach_callback, &fd);
545 struct foreach_namespace_data {
546 E2kPropertiesForeachNamespaceFunc callback;
548 gboolean need_array_namespace, need_type_namespace;
549 GHashTable *seen_namespaces;
553 foreach_namespace_callback (gpointer key, gpointer value, gpointer data)
555 struct foreach_namespace_data *fnd = data;
559 pi = g_hash_table_lookup (known_properties, key);
563 name = e2k_prop_namespace_name (pi->name);
564 if (!g_hash_table_lookup (fnd->seen_namespaces, name)) {
565 g_hash_table_insert (fnd->seen_namespaces,
566 (char *)name, (char *)name);
567 fnd->callback (name, e2k_prop_namespace_abbrev (pi->name),
572 case E2K_PROP_TYPE_STRING_ARRAY:
573 case E2K_PROP_TYPE_BINARY_ARRAY:
574 case E2K_PROP_TYPE_INT_ARRAY:
575 fnd->need_array_namespace = TRUE;
578 case E2K_PROP_TYPE_BINARY:
579 case E2K_PROP_TYPE_INT:
580 case E2K_PROP_TYPE_BOOL:
581 case E2K_PROP_TYPE_FLOAT:
582 case E2K_PROP_TYPE_DATE:
583 fnd->need_type_namespace = TRUE;
592 * e2k_properties_foreach_namespace:
593 * @props: an #E2kProperties
594 * @callback: callback function to call for each namespace
595 * @user_data: data to pass to @callback
597 * Calls @callback once for each unique namespace used by the
598 * properties (set or removed) in @props, passing it the name of the
599 * namespace, its standard abbreviation, and @user_data.
602 e2k_properties_foreach_namespace (E2kProperties *props,
603 E2kPropertiesForeachNamespaceFunc callback,
606 struct foreach_namespace_data fnd;
608 g_return_if_fail (props != NULL);
610 fnd.callback = callback;
611 fnd.user_data = user_data;
612 fnd.need_array_namespace = FALSE;
613 fnd.need_type_namespace = FALSE;
614 fnd.seen_namespaces = g_hash_table_new (NULL, NULL);
616 g_hash_table_foreach (props->set, foreach_namespace_callback, &fnd);
617 g_hash_table_foreach (props->removed, foreach_namespace_callback, &fnd);
619 if (fnd.need_type_namespace)
620 callback (E2K_NS_TYPE, 'T', user_data);
621 if (fnd.need_array_namespace)
622 callback ("xml:", 'X', user_data);
624 g_hash_table_destroy (fnd.seen_namespaces);
628 static GHashTable *namespaces;
629 static int next_namespace = 'a';
633 get_div (const char *propname)
637 div = strrchr (propname, '/');
640 return strrchr (propname, ':');
644 prop_equal (gconstpointer v1, gconstpointer v2)
646 const char *s1 = (const char *)v1, *s2 = (const char *)v2;
647 const char *d1 = get_div (s1), *d2 = get_div (s2);
649 return (d1 - s1 == d2 - s2) && !g_ascii_strncasecmp (s1, s2, d1 - s1);
653 prop_hash (gconstpointer v)
655 const char *d = get_div (v);
657 guint h = g_ascii_tolower (*p);
659 for (p += 1; p < d; p++)
660 h = (h << 5) - h + *p;
665 setup_namespaces (void)
667 namespaces = g_hash_table_new (prop_hash, prop_equal);
668 g_hash_table_insert (namespaces, "DAV", GINT_TO_POINTER ('D'));
672 * e2k_prop_namespace_name:
673 * @prop: the name of a property
675 * Splits out the namespace portion of @prop
677 * Return value: the URI of @prop's namespace
680 e2k_prop_namespace_name (const char *prop)
682 const char *div = get_div (prop);
689 if (g_hash_table_lookup_extended (namespaces, prop, &key, &value))
692 name = g_strndup (prop, div - prop + 1);
693 g_hash_table_insert (namespaces, name, GINT_TO_POINTER (next_namespace));
699 * e2k_prop_namespace_abbrev:
700 * @prop: the name of a property
702 * Splits out the namespace portion of @prop and assigns a unique
703 * abbreviation for it.
705 * Return value: the abbreviation used for prop's namespace
708 e2k_prop_namespace_abbrev (const char *prop)
710 const char *div = get_div (prop);
717 if (g_hash_table_lookup_extended (namespaces, prop, &key, &value))
718 return GPOINTER_TO_INT (value);
720 name = g_strndup (prop, div - prop + 1);
721 g_hash_table_insert (namespaces, name, GINT_TO_POINTER (next_namespace));
722 return next_namespace++;
726 * e2k_prop_property_name:
727 * @prop: the name of a property
729 * Splits out the non-namespace portion of @prop
731 * Return value: the non-namespaced name of @prop
734 e2k_prop_property_name (const char *prop)
736 return get_div (prop) + 1;
741 * @prop: the name of a MAPI property
743 * Computes the MAPI proptag value of @prop, which must be the name
744 * of a MAPI property.
746 * Return value: the MAPI proptag value
749 e2k_prop_proptag (const char *prop)
753 pi = get_propinfo (prop, E2K_PROP_TYPE_UNKNOWN);
759 * @proptag: a MAPI property
761 * Computes the WebDAV property name of the property with the
764 * Return value: the WebDAV property name associated with @proptag
767 e2k_proptag_prop (guint32 proptag)
772 tmpname = g_strdup_printf (E2K_NS_MAPI_PROPTAG "x%08x",
775 pi = get_propinfo (tmpname, E2K_PROP_TYPE_UNKNOWN);