2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2024 the Claws Mail team and Match Grun
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "claws-features.h"
33 #include <glib/gi18n.h>
34 #include <gdk/gdkkeysyms.h>
39 #include "stock_pixmap.h"
40 #include "prefs_common.h"
41 #include "browseldap.h"
43 #include "addrindex.h"
44 #include "manage_window.h"
46 #include "ldapquery.h"
47 #include "ldapserver.h"
48 #include "ldaplocate.h"
56 #define BROWSELDAP_WIDTH 450
57 #define BROWSELDAP_HEIGHT 420
59 #define COL_WIDTH_NAME 140
60 #define COL_WIDTH_VALUE 140
62 static struct _LDAPEntry_dlg
{
64 GtkWidget
*label_server
;
65 GtkWidget
*label_address
;
73 static GList
*_displayQueue_
= NULL
;
76 * Mutex to protect callback from multiple threads.
78 static pthread_mutex_t _browseMutex_
= PTHREAD_MUTEX_INITIALIZER
;
83 static gint _queryID_
= 0;
88 static guint _browseIdleID_
= 0;
91 * Search complete indicator.
93 static gboolean _searchComplete_
= FALSE
;
96 * Callback entry point for each LDAP entry processed. The background thread
97 * (if any) appends the address list to the display queue.
99 * \param qry LDAP query object.
100 * \param queryID Query ID of search request.
101 * \param listEMail List of zero of more email objects that met search
103 * \param data User data.
105 static gint
browse_callback_entry(
106 LdapQuery
*qry
, gint queryID
, GList
*listValues
, gpointer data
)
111 debug_print("browse_callback_entry...\n");
112 pthread_mutex_lock( & _browseMutex_
);
113 /* Append contents to end of display queue */
116 nvp
= ( NameValuePair
* ) node
->data
;
117 debug_print("adding to list: %s->%s\n",
118 nvp
->name
?nvp
->name
:"null",
119 nvp
->value
?nvp
->value
:"null");
120 _displayQueue_
= g_list_append( _displayQueue_
, nvp
);
122 node
= g_list_next( node
);
124 pthread_mutex_unlock( & _browseMutex_
);
125 /* g_print( "browse_callback_entry...done\n" ); */
131 * Callback entry point for end of LDAP locate search.
133 * \param qry LDAP query object.
134 * \param queryID Query ID of search request.
135 * \param status Status/error code.
136 * \param data User data.
138 static gint
browse_callback_end(
139 LdapQuery
*qry
, gint queryID
, gint status
, gpointer data
)
141 debug_print("search completed\n");
142 _searchComplete_
= TRUE
;
147 * Clear the display queue.
149 static void browse_clear_queue( void ) {
150 /* Clear out display queue */
151 pthread_mutex_lock( & _browseMutex_
);
153 ldapqry_free_list_name_value( _displayQueue_
);
154 g_list_free( _displayQueue_
);
155 _displayQueue_
= NULL
;
157 pthread_mutex_unlock( & _browseMutex_
);
161 * Close window callback.
162 * \param widget Widget.
163 * \param event Event.
164 * \param cancelled Cancelled flag.
166 static gint
browse_delete_event(
167 GtkWidget
*widget
, GdkEventAny
*event
, gboolean
*cancelled
)
174 * Respond to key press in window.
175 * \param widget Widget.
176 * \param event Event.
177 * \param cancelled Cancelled flag.
179 static void browse_key_pressed(
180 GtkWidget
*widget
, GdkEventKey
*event
, gboolean
*cancelled
)
182 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
188 * Callback to close window.
189 * \param widget Widget.
190 * \param cancelled Cancelled flag.
192 static void browse_close( GtkWidget
*widget
, gboolean
*cancelled
) {
197 * Create the window to display data.
199 static void browse_create( void ) {
204 GtkWidget
*label_server
;
205 GtkWidget
*label_addr
;
209 GtkWidget
*close_btn
;
210 GtkWidget
*content_area
;
213 GtkTreeSelection
*sel
;
214 GtkCellRenderer
*rdr
;
215 GtkTreeViewColumn
*col
;
217 debug_print("creating browse widget\n");
218 window
= gtk_dialog_new();
219 gtk_widget_set_size_request( window
, BROWSELDAP_WIDTH
, BROWSELDAP_HEIGHT
);
220 gtk_container_set_border_width( GTK_CONTAINER(window
), 0 );
221 gtk_window_set_title( GTK_WINDOW(window
), _("Browse Directory Entry") );
222 gtk_window_set_position( GTK_WINDOW(window
), GTK_WIN_POS_MOUSE
);
223 g_signal_connect(G_OBJECT(window
), "delete_event",
224 G_CALLBACK(browse_delete_event
), NULL
);
225 g_signal_connect(G_OBJECT(window
), "key_press_event",
226 G_CALLBACK(browse_key_pressed
), NULL
);
228 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 8);
229 content_area
= gtk_dialog_get_content_area(GTK_DIALOG(window
));
230 gtk_box_pack_start(GTK_BOX(content_area
), vbox
, TRUE
, TRUE
, 0);
231 gtk_container_set_border_width( GTK_CONTAINER(vbox
), 8 );
233 table
= gtk_grid_new();
234 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
235 gtk_container_set_border_width( GTK_CONTAINER(table
), 8 );
236 gtk_grid_set_row_spacing(GTK_GRID(table
), 8);
237 gtk_grid_set_column_spacing(GTK_GRID(table
), 8);
240 label
= gtk_label_new(_("Server Name:"));
241 gtk_grid_attach(GTK_GRID(table
), label
, 0, 0, 1, 1);
242 gtk_label_set_xalign(GTK_LABEL(label
), 1.0);
244 label_server
= gtk_label_new("");
245 gtk_label_set_xalign(GTK_LABEL(label_server
), 0.0);
246 gtk_grid_attach(GTK_GRID(table
), label
, 1, 0, 1, 1);
247 gtk_widget_set_hexpand(label_server
, TRUE
);
248 gtk_widget_set_halign(label_server
, GTK_ALIGN_FILL
);
251 label
= gtk_label_new(_("Distinguished Name (dn):"));
252 gtk_label_set_xalign(GTK_LABEL(label
), 1.0);
253 gtk_grid_attach(GTK_GRID(table
), label
, 0, 1, 1, 1);
255 label_addr
= gtk_label_new("");
256 gtk_label_set_xalign(GTK_LABEL(label_addr
), 0.0);
257 gtk_grid_attach(GTK_GRID(table
), label_addr
, 1, 1, 1, 1);
258 gtk_widget_set_hexpand(label_addr
, TRUE
);
259 gtk_widget_set_halign(label_addr
, GTK_ALIGN_FILL
);
261 /* Address book/folder tree */
262 vlbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 8);
263 gtk_box_pack_start(GTK_BOX(vbox
), vlbox
, TRUE
, TRUE
, 0);
264 gtk_container_set_border_width( GTK_CONTAINER(vlbox
), 8 );
266 tree_win
= gtk_scrolled_window_new( NULL
, NULL
);
267 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(tree_win
),
268 GTK_POLICY_AUTOMATIC
,
269 GTK_POLICY_AUTOMATIC
);
270 gtk_box_pack_start( GTK_BOX(vlbox
), tree_win
, TRUE
, TRUE
, 0 );
272 store
= gtk_list_store_new(N_COLS
,
273 G_TYPE_STRING
, G_TYPE_STRING
,
276 view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
277 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), TRUE
);
278 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(view
), FALSE
);
279 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
280 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_NONE
);
282 rdr
= gtk_cell_renderer_text_new();
283 col
= gtk_tree_view_column_new_with_attributes(_("LDAP Name"), rdr
,
284 "markup", COL_NAME
, NULL
);
285 gtk_tree_view_column_set_min_width(col
, COL_WIDTH_NAME
);
286 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), col
);
288 rdr
= gtk_cell_renderer_text_new();
289 col
= gtk_tree_view_column_new_with_attributes(_("Attribute Value"), rdr
,
290 "markup", COL_VALUE
, NULL
);
291 gtk_tree_view_column_set_min_width(col
, COL_WIDTH_VALUE
);
292 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), col
);
294 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store
),
295 COL_NAME
, GTK_SORT_ASCENDING
);
296 g_object_unref(store
);
298 gtk_container_add( GTK_CONTAINER(tree_win
), view
);
301 gtkut_stock_button_set_create(&hbbox
, &close_btn
, "window-close", _("_Close"),
302 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
303 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
304 gtk_container_set_border_width( GTK_CONTAINER(hbbox
), 0 );
306 g_signal_connect(G_OBJECT(close_btn
), "clicked",
307 G_CALLBACK(browse_close
), NULL
);
308 gtk_widget_grab_default(close_btn
);
310 gtk_widget_show_all(vbox
);
312 browseldap_dlg
.window
= window
;
313 browseldap_dlg
.label_server
= label_server
;
314 browseldap_dlg
.label_address
= label_addr
;
315 browseldap_dlg
.list_view
= view
;
316 browseldap_dlg
.close_btn
= close_btn
;
318 gtk_widget_show_all( window
);
323 * Idler function. This function is called by the main (UI) thread during UI
324 * idle time while an address search is in progress. Items from the display
325 * queue are processed and appended to the address list.
327 * \param data Target data object.
328 * \return <i>TRUE</i> to ensure that idle event do not get ignored.
330 static gboolean
browse_idle( gpointer data
) {
333 GtkWidget
*view
= browseldap_dlg
.list_view
;
334 GtkListStore
*store
= GTK_LIST_STORE(
335 gtk_tree_view_get_model(GTK_TREE_VIEW(view
)));
338 /* Process all entries in display queue */
339 pthread_mutex_lock( & _browseMutex_
);
340 if( _displayQueue_
) {
341 node
= _displayQueue_
;
343 /* Add entry into list */
344 nvp
= ( NameValuePair
* ) node
->data
;
345 debug_print("Adding row to list: %s->%s\n",
346 nvp
->name
?nvp
->name
:"null",
347 nvp
->value
?nvp
->value
:"null");
348 gtk_list_store_append(store
, &iter
);
349 gtk_list_store_set(store
, &iter
,
351 COL_VALUE
, nvp
->value
,
355 ldapqry_free_name_value( nvp
);
357 node
= g_list_next( node
);
359 g_list_free( _displayQueue_
);
360 _displayQueue_
= NULL
;
362 pthread_mutex_unlock( & _browseMutex_
);
364 if( _searchComplete_
) {
366 if( _browseIdleID_
!= 0 ) {
367 g_source_remove( _browseIdleID_
);
376 * Main entry point to browse LDAP entries.
377 * \param ds Data source to process.
378 * \param dn Distinguished name to retrieve.
379 * \return <code>TRUE</code>
381 gboolean
browseldap_entry( AddressDataSource
*ds
, const gchar
*dn
) {
389 server
= ds
->rawDataSource
;
391 if( ! browseldap_dlg
.window
) browse_create();
392 gtk_widget_grab_focus(browseldap_dlg
.close_btn
);
393 gtk_widget_show(browseldap_dlg
.window
);
394 manage_window_set_transient(GTK_WINDOW(browseldap_dlg
.window
));
395 gtk_window_set_modal(GTK_WINDOW(browseldap_dlg
.window
), TRUE
);
396 gtk_widget_show(browseldap_dlg
.window
);
398 gtk_label_set_text( GTK_LABEL(browseldap_dlg
.label_address
), "" );
401 GTK_LABEL(browseldap_dlg
.label_address
), dn
);
404 GTK_LABEL(browseldap_dlg
.label_server
),
405 ldapsvr_get_name( server
) );
407 debug_print("browsing server: %s\n", ldapsvr_get_name(server
));
409 _searchComplete_
= FALSE
;
410 _queryID_
= ldaplocate_search_setup(
411 server
, dn
, browse_callback_entry
, browse_callback_end
);
412 debug_print("query id: %d\n", _queryID_
);
413 _browseIdleID_
= g_idle_add( (GSourceFunc
) browse_idle
, NULL
);
416 debug_print("starting search\n");
417 ldaplocate_search_start( _queryID_
);
421 gtk_widget_hide( browseldap_dlg
.window
);
422 gtk_window_set_modal(GTK_WINDOW(browseldap_dlg
.window
), FALSE
);
424 debug_print("stopping search\n");
425 ldaplocate_search_stop( _queryID_
);
427 if( _browseIdleID_
!= 0 ) {
428 g_source_remove( _browseIdleID_
);
431 browse_clear_queue();
433 view
= browseldap_dlg
.list_view
;
434 store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(view
)));
435 gtk_list_store_clear(store
);
440 #endif /* USE_LDAP */