1 /*
2  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
3  * Copyright (C) 2011 Vivien Malerba <malerba@gnome-db.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 
20 #include <glib/gi18n-lib.h>
21 #include <libgda/libgda.h>
22 #include "mgr-ldap-entries.h"
23 #include <libgda/gda-tree-node.h>
24 
25 struct _MgrLdapEntriesPriv {
26 	BrowserConnection *bcnc;
27 	gchar             *dn;
28 };
29 
30 static void mgr_ldap_entries_class_init (MgrLdapEntriesClass *klass);
31 static void mgr_ldap_entries_init       (MgrLdapEntries *tmgr1, MgrLdapEntriesClass *klass);
32 static void mgr_ldap_entries_dispose    (GObject *object);
33 
34 /* virtual methods */
35 static GSList *mgr_ldap_entries_update_children (GdaTreeManager *manager, GdaTreeNode *node,
36 						      const GSList *children_nodes,
37 						      gboolean *out_error, GError **error);
38 
39 static GObjectClass *parent_class = NULL;
40 
41 /*
42  * MgrLdapEntries class implementation
43  * @klass:
44  */
45 static void
mgr_ldap_entries_class_init(MgrLdapEntriesClass * klass)46 mgr_ldap_entries_class_init (MgrLdapEntriesClass *klass)
47 {
48 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
49 
50 	parent_class = g_type_class_peek_parent (klass);
51 
52 	/* virtual methods */
53 	((GdaTreeManagerClass*) klass)->update_children = mgr_ldap_entries_update_children;
54 
55 	/* Properties */
56 	object_class->dispose = mgr_ldap_entries_dispose;
57 }
58 
59 static void
mgr_ldap_entries_init(MgrLdapEntries * mgr,G_GNUC_UNUSED MgrLdapEntriesClass * klass)60 mgr_ldap_entries_init (MgrLdapEntries *mgr, G_GNUC_UNUSED MgrLdapEntriesClass *klass)
61 {
62 	g_return_if_fail (MGR_IS_LDAP_ENTRIES (mgr));
63 	mgr->priv = g_new0 (MgrLdapEntriesPriv, 1);
64 }
65 
66 static void
mgr_ldap_entries_dispose(GObject * object)67 mgr_ldap_entries_dispose (GObject *object)
68 {
69 	MgrLdapEntries *mgr = (MgrLdapEntries *) object;
70 
71 	g_return_if_fail (MGR_IS_LDAP_ENTRIES (mgr));
72 
73 	if (mgr->priv) {
74 		if (mgr->priv->bcnc)
75 			g_object_unref (mgr->priv->bcnc);
76 		g_free (mgr->priv->dn);
77 		g_free (mgr->priv);
78 		mgr->priv = NULL;
79 	}
80 
81 	/* chain to parent class */
82 	parent_class->dispose (object);
83 }
84 
85 /**
86  * browser_tree_mgr_select_get_type:
87  *
88  * Returns: the GType
89  */
90 GType
mgr_ldap_entries_get_type(void)91 mgr_ldap_entries_get_type (void)
92 {
93         static GType type = 0;
94 
95         if (G_UNLIKELY (type == 0)) {
96                 static GMutex registering;
97                 static const GTypeInfo info = {
98                         sizeof (MgrLdapEntriesClass),
99                         (GBaseInitFunc) NULL,
100                         (GBaseFinalizeFunc) NULL,
101                         (GClassInitFunc) mgr_ldap_entries_class_init,
102                         NULL,
103                         NULL,
104                         sizeof (MgrLdapEntries),
105                         0,
106                         (GInstanceInitFunc) mgr_ldap_entries_init,
107 			0
108                 };
109 
110                 g_mutex_lock (&registering);
111                 if (type == 0)
112                         type = g_type_register_static (GDA_TYPE_TREE_MANAGER, "MgrLdapEntries", &info, 0);
113                 g_mutex_unlock (&registering);
114         }
115         return type;
116 }
117 
118 /**
119  * mgr_ldap_entries_new:
120  * @cnc: a #BrowserConnection object
121  * @dn: (allow-none): a schema name or %NULL
122  *
123  * Creates a new #BrowserTreeManager object which will list the children of the LDAP entry which Distinguished name
124  * is @dn. If @dn is %NULL, then the tree manager will look in the tree itself for an attribute named "dn" and
125  * use it.
126  *
127  * Returns: (transfer full): a new #BrowserTreeManager object
128  */
129 GdaTreeManager*
mgr_ldap_entries_new(BrowserConnection * bcnc,const gchar * dn)130 mgr_ldap_entries_new (BrowserConnection *bcnc, const gchar *dn)
131 {
132 	MgrLdapEntries *mgr;
133 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
134 
135 	mgr = (MgrLdapEntries*) g_object_new (MGR_TYPE_LDAP_ENTRIES, NULL);
136 
137 	mgr->priv->bcnc = g_object_ref (bcnc);
138 	if (dn)
139 		mgr->priv->dn = g_strdup (dn);
140 
141 	return (GdaTreeManager*) mgr;
142 }
143 
144 typedef struct {
145 	GMainLoop     *loop;
146 	GdaLdapEntry **entries;
147 	GError        *error;
148 } AsyncExecData;
149 
150 static void
update_children_cb(G_GNUC_UNUSED BrowserConnection * bcnc,gpointer out_result,AsyncExecData * data,GError * error)151 update_children_cb (G_GNUC_UNUSED BrowserConnection *bcnc,
152 		    gpointer out_result, AsyncExecData *data, GError *error)
153 {
154 	data->entries = (GdaLdapEntry **) out_result;
155 	if (! data->entries && error)
156 		data->error = g_error_copy (error);
157 	g_main_loop_quit (data->loop);
158 }
159 
160 static gint
lentry_array_sort_func(gconstpointer a,gconstpointer b)161 lentry_array_sort_func (gconstpointer a, gconstpointer b)
162 {
163         GdaLdapEntry *e1, *e2;
164         e1 = *((GdaLdapEntry**) a);
165         e2 = *((GdaLdapEntry**) b);
166 	GdaLdapAttribute *cna1, *cna2;
167 	const gchar *str1 = NULL, *str2 = NULL;
168 
169 	cna1 = g_hash_table_lookup (e1->attributes_hash, "cn");
170 	cna2 = g_hash_table_lookup (e2->attributes_hash, "cn");
171 	if (cna1 && (cna1->nb_values > 0) && (G_VALUE_TYPE (cna1->values[0]) == G_TYPE_STRING))
172 		str1 = g_value_get_string (cna1->values[0]);
173 	else
174 		str1 = e1->dn ? e1->dn : "";
175 	if (cna2 && (cna2->nb_values > 0) && (G_VALUE_TYPE (cna2->values[0]) == G_TYPE_STRING))
176 		str2 = g_value_get_string (cna2->values[0]);
177 	else
178 		str2 = e2->dn ? e2->dn : "";
179 
180 	return strcmp (str2, str1);
181 }
182 
183 static GSList *
mgr_ldap_entries_update_children(GdaTreeManager * manager,GdaTreeNode * node,G_GNUC_UNUSED const GSList * children_nodes,gboolean * out_error,GError ** error)184 mgr_ldap_entries_update_children (GdaTreeManager *manager, GdaTreeNode *node,
185 				       G_GNUC_UNUSED const GSList *children_nodes, gboolean *out_error,
186 				       GError **error)
187 {
188 	MgrLdapEntries *mgr = MGR_LDAP_ENTRIES (manager);
189 	gchar *real_dn = NULL;
190 
191 	g_return_val_if_fail (mgr->priv->bcnc, NULL);
192 
193 	if (mgr->priv->dn)
194 		real_dn = g_strdup (mgr->priv->dn);
195 	else if (node) {
196 		/* looking for a dn in @node's attributes */
197 		const GValue *cvalue;
198 		cvalue = gda_tree_node_fetch_attribute (node, "dn");
199 		if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING))
200 			real_dn = g_value_dup_string (cvalue);
201 	}
202 
203 	AsyncExecData data;
204 	guint id;
205 	data.loop = NULL;
206 	data.entries = NULL;
207 	data.error = NULL;
208 	gchar *attrs[] = {"objectClass", "cn", NULL};
209 
210 	id = browser_connection_ldap_get_entry_children (mgr->priv->bcnc, real_dn, attrs,
211 							 BROWSER_CONNECTION_JOB_CALLBACK (update_children_cb),
212 							 &data, error);
213 	g_free (real_dn);
214 	if (id == 0) {
215 		if (out_error)
216 			*out_error = TRUE;
217 		return NULL;
218 	}
219 	data.loop = g_main_loop_new (NULL, FALSE);
220 	g_main_loop_run (data.loop);
221 	g_main_loop_unref (data.loop);
222 
223 	if (data.entries) {
224 		guint i;
225 		GSList *list = NULL;
226 		GArray *sorted_array;
227 		sorted_array = g_array_new (FALSE, FALSE, sizeof (GdaLdapEntry*));
228 		for (i = 0; data.entries [i]; i++) {
229 			GdaLdapEntry *lentry;
230 			lentry = data.entries [i];
231 			g_array_prepend_val (sorted_array, lentry);
232 		}
233 		g_free (data.entries);
234 
235 		g_array_sort (sorted_array, (GCompareFunc) lentry_array_sort_func);
236 
237 		for (i = 0; i < sorted_array->len; i++) {
238 			GdaTreeNode* snode;
239 			GValue *dnv;
240 			GdaLdapEntry *lentry;
241 
242 			lentry = g_array_index (sorted_array, GdaLdapEntry*, i);
243 			snode = gda_tree_manager_create_node (manager, node, lentry->dn);
244 
245 			/* full DN */
246 			g_value_set_string ((dnv = gda_value_new (G_TYPE_STRING)), lentry->dn);
247 			gda_tree_node_set_node_attribute (snode, "dn", dnv, NULL);
248 			gda_value_free (dnv);
249 
250 			/* RDN */
251                         gchar **array;
252                         array = gda_ldap_dn_split (lentry->dn, FALSE);
253                         if (array) {
254                                 g_value_set_string ((dnv = gda_value_new (G_TYPE_STRING)), array [0]);
255                                 gda_tree_node_set_node_attribute (snode, "rdn", dnv, NULL);
256 				gda_value_free (dnv);
257                                 g_strfreev (array);
258                         }
259 
260 			/* CN */
261 			GdaLdapAttribute *attr;
262 			attr = g_hash_table_lookup (lentry->attributes_hash, "cn");
263 			if (attr && (attr->nb_values >= 1)) {
264 				const GValue *cvalue;
265 				cvalue = attr->values [0];
266 				if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING))
267 					gda_tree_node_set_node_attribute (snode, "cn", cvalue, NULL);
268 			}
269 
270 			/* icon */
271 			GdkPixbuf *pixbuf;
272 			attr = g_hash_table_lookup (lentry->attributes_hash, "objectClass");
273 			pixbuf = browser_connection_ldap_icon_for_class (attr);
274 
275 			dnv = gda_value_new (G_TYPE_OBJECT);
276 			g_value_set_object (dnv, pixbuf);
277 			gda_tree_node_set_node_attribute (snode, "icon", dnv, NULL);
278 			gda_value_free (dnv);
279 
280 			if (gda_tree_manager_get_managers (manager)) {
281 				g_value_set_boolean ((dnv = gda_value_new (G_TYPE_BOOLEAN)), TRUE);
282 				gda_tree_node_set_node_attribute (snode,
283 								  GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN,
284 								  dnv, NULL);
285 				gda_value_free (dnv);
286 			}
287 
288 			list = g_slist_prepend (list, snode);
289 			gda_ldap_entry_free (lentry);
290 		}
291 		g_array_free (sorted_array, TRUE);
292 
293 		if (node)
294 			gda_tree_node_set_node_attribute (node,
295 							  GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN,
296 							  NULL, NULL);
297 
298 		return list;
299 	}
300 	else {
301 		g_propagate_error (error, data.error);
302 		if (out_error)
303 			*out_error = TRUE;
304 		return NULL;
305 	}
306 }
307