NAIContext: replace g_warning with g_debug
[nautilus-actions.git] / src / core / na-icontext.c
blob831e424cad5162489b73336fa789b4e7ed4e6ddd
1 /*
2 * Nautilus-Actions
3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
9 * This Program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * This Program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
24 * Authors:
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
35 #include <dbus/dbus-glib.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <glibtop/proclist.h>
41 #include <glibtop/procstate.h>
43 #include <libnautilus-extension/nautilus-file-info.h>
45 #include <api/na-core-utils.h>
46 #include <api/na-object-api.h>
48 #include "na-gnome-vfs-uri.h"
49 #include "na-selected-info.h"
51 /* private interface data
53 struct _NAIContextInterfacePrivate {
54 void *empty; /* so that gcc -pedantic is happy */
57 static gboolean st_initialized = FALSE;
58 static gboolean st_finalized = FALSE;
60 static GType register_type( void );
61 static void interface_base_init( NAIContextInterface *klass );
62 static void interface_base_finalize( NAIContextInterface *klass );
64 static gboolean v_is_candidate( NAIContext *object, guint target, GList *selection );
66 static gboolean is_candidate_for_target( const NAIContext *object, guint target, GList *files );
67 static gboolean is_candidate_for_show_in( const NAIContext *object, guint target, GList *files );
68 static const gchar *get_running_environment( void );
69 static gboolean is_candidate_for_try_exec( const NAIContext *object, guint target, GList *files );
70 static gboolean is_candidate_for_show_if_registered( const NAIContext *object, guint target, GList *files );
71 static gboolean is_candidate_for_show_if_true( const NAIContext *object, guint target, GList *files );
72 static gboolean is_candidate_for_show_if_running( const NAIContext *object, guint target, GList *files );
73 static gboolean is_candidate_for_mimetypes( const NAIContext *object, guint target, GList *files );
74 static gboolean is_all_mimetype( const gchar *mimetype );
75 static gboolean is_file_mimetype( const gchar *mimetype );
76 static gboolean is_mimetype_of( const gchar *file_type, const gchar *ftype, gboolean is_regular );
77 static gboolean is_candidate_for_basenames( const NAIContext *object, guint target, GList *files );
78 static gboolean is_candidate_for_selection_count( const NAIContext *object, guint target, GList *files );
79 static gboolean is_candidate_for_schemes( const NAIContext *object, guint target, GList *files );
80 static gboolean is_compatible_scheme( const gchar *pattern, const gchar *scheme );
81 static gboolean is_candidate_for_folders( const NAIContext *object, guint target, GList *files );
82 static gboolean is_candidate_for_capabilities( const NAIContext *object, guint target, GList *files );
84 static gboolean is_valid_basenames( const NAIContext *object );
85 static gboolean is_valid_mimetypes( const NAIContext *object );
86 static gboolean is_valid_schemes( const NAIContext *object );
87 static gboolean is_valid_folders( const NAIContext *object );
89 static gboolean is_positive_assertion( const gchar *assertion );
91 /**
92 * na_icontext_get_type:
94 * Returns: the #GType type of this interface.
96 GType
97 na_icontext_get_type( void )
99 static GType type = 0;
101 if( !type ){
102 type = register_type();
105 return( type );
109 * na_icontext_register_type:
111 * Registers this interface.
113 static GType
114 register_type( void )
116 static const gchar *thisfn = "na_icontext_register_type";
117 GType type;
119 static const GTypeInfo info = {
120 sizeof( NAIContextInterface ),
121 ( GBaseInitFunc ) interface_base_init,
122 ( GBaseFinalizeFunc ) interface_base_finalize,
123 NULL,
124 NULL,
125 NULL,
128 NULL
131 g_debug( "%s", thisfn );
133 type = g_type_register_static( G_TYPE_INTERFACE, "NAIContext", &info, 0 );
135 g_type_interface_add_prerequisite( type, G_TYPE_OBJECT );
137 return( type );
140 static void
141 interface_base_init( NAIContextInterface *klass )
143 static const gchar *thisfn = "na_icontext_interface_base_init";
145 if( !st_initialized ){
147 g_debug( "%s: klass%p (%s)", thisfn, ( void * ) klass, G_OBJECT_CLASS_NAME( klass ));
149 klass->private = g_new0( NAIContextInterfacePrivate, 1 );
151 st_initialized = TRUE;
155 static void
156 interface_base_finalize( NAIContextInterface *klass )
158 static const gchar *thisfn = "na_icontext_interface_base_finalize";
160 if( st_initialized && !st_finalized ){
162 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
164 st_finalized = TRUE;
166 g_free( klass->private );
171 * na_icontext_is_candidate:
172 * @context: a #NAIContext to be checked.
173 * @target: the current target.
174 * @selection: the currently selected items, as a #GList of NASelectedInfo items.
176 * Determines if the given object may be candidate to be displayed in
177 * the Nautilus context menu, depending of the list of currently selected
178 * items.
180 * This function is called by <methodname>build_nautilus_menus</methodname>
181 * plugin function for each item found in NAPivot items list, and, when this
182 * an action, for each profile of this action.
184 * Returns: %TRUE if this @context succeeds to all tests and is so a
185 * valid candidate to be displayed in Nautilus context menu, %FALSE
186 * else.
188 * Since: 2.30
190 gboolean
191 na_icontext_is_candidate( const NAIContext *context, guint target, GList *selection )
193 static const gchar *thisfn = "na_icontext_is_candidate";
194 gboolean is_candidate;
196 g_return_val_if_fail( NA_IS_ICONTEXT( context ), FALSE );
198 g_debug( "%s: object=%p (%s), target=%d, selection=%p (count=%d)",
199 thisfn, ( void * ) context, G_OBJECT_TYPE_NAME( context ), target, (void * ) selection, g_list_length( selection ));
201 is_candidate = v_is_candidate( NA_ICONTEXT( context ), target, selection );
203 if( is_candidate ){
204 is_candidate =
205 is_candidate_for_target( context, target, selection ) &&
206 is_candidate_for_show_in( context, target, selection ) &&
207 is_candidate_for_try_exec( context, target, selection ) &&
208 is_candidate_for_show_if_registered( context, target, selection ) &&
209 is_candidate_for_show_if_true( context, target, selection ) &&
210 is_candidate_for_show_if_running( context, target, selection ) &&
211 is_candidate_for_mimetypes( context, target, selection ) &&
212 is_candidate_for_basenames( context, target, selection ) &&
213 is_candidate_for_selection_count( context, target, selection ) &&
214 is_candidate_for_schemes( context, target, selection ) &&
215 is_candidate_for_folders( context, target, selection ) &&
216 is_candidate_for_capabilities( context, target, selection );
219 return( is_candidate );
223 * na_icontext_is_valid:
224 * @context: the #NAObjectProfile to be checked.
226 * Returns: %TRUE if this @context is valid, %FALSE else.
228 * This function is part of <methodname>NAIDuplicable::check_status</methodname>
229 * and is called by #NAIDuplicable objects which also implement #NAIContext
230 * interface. It so doesn't make sense of asking the object for its
231 * validity status as it has already been checked before calling the
232 * function.
234 * Since: 2.30
236 gboolean
237 na_icontext_is_valid( const NAIContext *context )
239 gboolean is_valid;
241 g_return_val_if_fail( NA_IS_ICONTEXT( context ), FALSE );
243 is_valid =
244 is_valid_basenames( context ) &&
245 is_valid_mimetypes( context ) &&
246 is_valid_schemes( context ) &&
247 is_valid_folders( context );
249 return( is_valid );
253 * na_icontext_check_mimetypes:
254 * @context: the #NAIContext object to be checked.
256 * Check the current list of mimetypes to see if it covers all mimetypes,
257 * or all regular files, or something else.
259 * Since: 2.30
261 void
262 na_icontext_check_mimetypes( const NAIContext *context )
264 static const gchar *thisfn = "na_icontext_check_mimetypes";
265 gboolean is_all;
266 GSList *mimetypes, *im;
268 g_return_if_fail( NA_IS_ICONTEXT( context ));
270 is_all = TRUE;
271 mimetypes = na_object_get_mimetypes( context );
273 for( im = mimetypes ; im ; im = im->next ){
274 if( !im->data || !strlen( im->data )){
275 g_debug( "%s: empty mimetype for context=%p", thisfn, ( void * ) context );
276 continue;
278 const gchar *imtype = ( const gchar * ) im->data;
279 if( is_all_mimetype( imtype )){
280 continue;
282 is_all = FALSE;
283 /* do not break here so that we are able to check all mimetypes */
286 na_object_set_all_mimetypes( context, is_all );
288 na_core_utils_slist_free( mimetypes );
292 * na_icontext_read_done:
293 * @context: the #NAIContext to be prepared.
295 * Prepares the specified #NAIContext just after it has been read.
297 * <itemizedlist>
298 * <listitem>
299 * <para>
300 * This setup an internal flag when mimetypes is like 'all/all'
301 * in order to optimize computation time;
302 * </para>
303 * </listitem>
304 * </itemizedlist>
306 * Since: 2.30
308 void
309 na_icontext_read_done( NAIContext *context )
311 na_object_check_mimetypes( context );
315 * na_icontext_set_scheme:
316 * @context: the #NAIContext to be updated.
317 * @scheme: name of the scheme.
318 * @selected: whether this scheme is candidate to this @context.
320 * Sets the status of a @scheme relative to this @context.
322 * Since: 2.30
324 void
325 na_icontext_set_scheme( NAIContext *context, const gchar *scheme, gboolean selected )
327 GSList *schemes;
329 g_return_if_fail( NA_IS_ICONTEXT( context ));
331 schemes = na_object_get_schemes( context );
332 schemes = na_core_utils_slist_setup_element( schemes, scheme, selected );
333 na_object_set_schemes( context, schemes );
334 na_core_utils_slist_free( schemes );
338 * na_icontext_set_only_desktop:
339 * @context: the #NAIContext to be updated.
340 * @desktop: name of the desktop environment.
341 * @selected: whether this @desktop is candidate to this @context.
343 * Sets the status of the @desktop relative to this @context for the OnlyShowIn list.
345 * Since: 2.30
347 void
348 na_icontext_set_only_desktop( NAIContext *context, const gchar *desktop, gboolean selected )
350 GSList *desktops;
352 g_return_if_fail( NA_IS_ICONTEXT( context ));
354 desktops = na_object_get_only_show_in( context );
355 desktops = na_core_utils_slist_setup_element( desktops, desktop, selected );
356 na_object_set_only_show_in( context, desktops );
357 na_core_utils_slist_free( desktops );
361 * na_icontext_set_only_desktop:
362 * @context: the #NAIContext to be updated.
363 * @desktop: name of the desktop environment.
364 * @selected: whether this @desktop is candidate to this @context.
366 * Sets the status of the @desktop relative to this @context for the NotShowIn list.
368 * Since: 2.30
370 void
371 na_icontext_set_not_desktop( NAIContext *context, const gchar *desktop, gboolean selected )
373 GSList *desktops;
375 g_return_if_fail( NA_IS_ICONTEXT( context ));
377 desktops = na_object_get_not_show_in( context );
378 desktops = na_core_utils_slist_setup_element( desktops, desktop, selected );
379 na_object_set_not_show_in( context, desktops );
380 na_core_utils_slist_free( desktops );
384 * na_icontext_replace_folder:
385 * @context: the #NAIContext to be updated.
386 * @old: the old uri.
387 * @new: the new uri.
389 * Replaces the @old URI by the @new one.
391 * Since: 2.30
393 void
394 na_icontext_replace_folder( NAIContext *context, const gchar *old, const gchar *new )
396 GSList *folders;
398 g_return_if_fail( NA_IS_ICONTEXT( context ));
400 folders = na_object_get_folders( context );
401 folders = na_core_utils_slist_remove_utf8( folders, old );
402 folders = g_slist_append( folders, ( gpointer ) g_strdup( new ));
403 na_object_set_folders( context, folders );
404 na_core_utils_slist_free( folders );
407 static gboolean
408 v_is_candidate( NAIContext *context, guint target, GList *selection )
410 gboolean is_candidate;
412 is_candidate = TRUE;
414 if( NA_ICONTEXT_GET_INTERFACE( context )->is_candidate ){
415 is_candidate = NA_ICONTEXT_GET_INTERFACE( context )->is_candidate( context, target, selection );
418 return( is_candidate );
422 * whether the given NAIContext object is candidate for this target
423 * target is context menu for location, context menu for selection or toolbar for location
424 * only actions are concerned by this check
426 static gboolean
427 is_candidate_for_target( const NAIContext *object, guint target, GList *files )
429 static const gchar *thisfn = "na_icontext_is_candidate_for_target";
430 gboolean ok = TRUE;
432 if( NA_IS_OBJECT_ACTION( object )){
433 switch( target ){
434 case ITEM_TARGET_LOCATION:
435 ok = na_object_is_target_location( object );
436 break;
438 case ITEM_TARGET_TOOLBAR:
439 ok = na_object_is_target_toolbar( object );
440 break;
442 case ITEM_TARGET_SELECTION:
443 ok = na_object_is_target_selection( object );
444 break;
446 case ITEM_TARGET_ANY:
447 ok = TRUE;
448 break;
450 default:
451 g_warning( "%s: unknonw target=%d", thisfn, target );
452 ok = FALSE;
456 if( !ok ){
457 g_debug( "%s: object is not candidate because target doesn't match (asked=%d)", thisfn, target );
458 /*na_object_dump( object );*/
461 return( ok );
465 * only show in / not show in
466 * only one of these two data may be set
468 static gboolean
469 is_candidate_for_show_in( const NAIContext *object, guint target, GList *files )
471 static const gchar *thisfn = "na_icontext_is_candidate_for_show_in";
472 gboolean ok = TRUE;
473 GSList *only_in = na_object_get_only_show_in( object );
474 GSList *not_in = na_object_get_not_show_in( object );
475 static const gchar *environment = NULL;
477 if( !environment ){
478 environment = get_running_environment();
479 g_debug( "%s: found %s desktop", thisfn, environment );
482 if( only_in && g_slist_length( only_in )){
483 ok = ( na_core_utils_slist_count( only_in, environment ) > 0 );
484 } else if( not_in && g_slist_length( not_in )){
485 ok = ( na_core_utils_slist_count( not_in, environment ) == 0 );
488 if( !ok ){
489 gchar *only_str = na_core_utils_slist_to_text( only_in );
490 gchar *not_str = na_core_utils_slist_to_text( not_in );
491 g_debug( "%s: object is not candidate because OnlyShowIn=%s, NotShowIn=%s", thisfn, only_str, not_str );
492 g_free( not_str );
493 g_free( only_str );
496 na_core_utils_slist_free( not_in );
497 na_core_utils_slist_free( only_in );
499 return( ok );
503 * Have asked on xdg-list how to identify the currently running desktop environment
504 * (see)
505 * For now, just reproduce the xdg-open algorythm from xdg-utils 1.0
507 #define DESKTOP_KDE "KDE"
508 #define DESKTOP_GNOME "GNOME"
509 #define DESKTOP_XFCE "XFCE"
510 #define DESKTOP_ROX "ROX"
511 #define DESKTOP_LXDE "LXDE"
512 #define DESKTOP_OLD "Old"
514 static const gchar *
515 get_running_environment( void )
517 static const gchar *thisfn = "na_icontext_get_running_environment";
518 const gchar *value;
519 gchar *output_str, *error_str;
520 gint exit_status;
521 GError *error;
522 gboolean ok;
524 value = g_getenv( "KDE_FULL_SESSION" );
525 if( value && !strcmp( value, "true" )){
526 return( DESKTOP_KDE );
529 value = g_getenv( "GNOME_DESKTOP_SESSION_ID" );
530 if( value && strlen( value )){
531 return( DESKTOP_GNOME );
534 output_str = NULL;
535 error_str = NULL;
536 error = NULL;
537 if( g_spawn_command_line_sync(
538 "dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager",
539 &output_str, &error_str, &exit_status, &error )){
540 ok = ( exit_status == 0 && output_str && strlen( output_str ) && ( !error_str || !strlen( error_str )));
541 g_free( output_str );
542 g_free( error_str );
543 if( ok ){
544 return( DESKTOP_GNOME );
547 if( error ){
548 g_warning( "%s: dbus-send: %s", thisfn, error->message );
549 g_error_free( error );
552 output_str = NULL;
553 error_str = NULL;
554 error = NULL;
555 if( g_spawn_command_line_sync(
556 "xprop -root _DT_SAVE_MODE", &output_str, &error_str, &exit_status, &error )){
557 ok = ( exit_status == 0 && output_str && strlen( output_str ) && ( !error_str || !strlen( error_str )));
558 if( ok ){
559 ok = ( g_strstr_len( output_str, -1, "xfce" ) != NULL );
561 g_free( output_str );
562 g_free( error_str );
563 if( ok ){
564 return( DESKTOP_XFCE );
567 if( error ){
568 g_warning( "%s: xprop: %s", thisfn, error->message );
569 g_error_free( error );
572 /* do not know how to identify ROX or LXDE (Hong Jen Yee <pcman.tw (at) gmail.com>)
573 * environments; so other desktops are just identified as 'Old' (legacy systems)
575 return( DESKTOP_OLD );
579 * if the data is set, it should be the path of an executable file
581 static gboolean
582 is_candidate_for_try_exec( const NAIContext *object, guint target, GList *files )
584 static const gchar *thisfn = "na_icontext_is_candidate_for_try_exec";
585 gboolean ok = TRUE;
586 GError *error = NULL;
587 gchar *tryexec = na_object_get_try_exec( object );
589 if( tryexec && strlen( tryexec )){
590 ok = FALSE;
591 GFile *file = g_file_new_for_path( tryexec );
592 GFileInfo *info = g_file_query_info( file, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, G_FILE_QUERY_INFO_NONE, NULL, &error );
593 if( error ){
594 g_debug( "%s: %s", thisfn, error->message );
595 g_error_free( error );
597 } else {
598 ok = g_file_info_get_attribute_boolean( info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE );
601 if( info ){
602 g_object_unref( info );
605 g_object_unref( file );
608 if( !ok ){
609 g_debug( "%s: object is not candidate because TryExec=%s", thisfn, tryexec );
612 g_free( tryexec );
614 return( ok );
617 static gboolean
618 is_candidate_for_show_if_registered( const NAIContext *object, guint target, GList *files )
620 static const gchar *thisfn = "na_icontext_is_candidate_for_show_if_registered";
621 gboolean ok = TRUE;
622 gchar *name = na_object_get_show_if_registered( object );
624 if( name && strlen( name )){
625 ok = FALSE;
626 GError *error = NULL;
627 DBusGConnection *connection = dbus_g_bus_get( DBUS_BUS_SESSION, &error );
629 if( !connection ){
630 if( error ){
631 g_warning( "%s: %s", thisfn, error->message );
632 g_error_free( error );
635 } else {
636 DBusGProxy *proxy = dbus_g_proxy_new_for_name( connection, name, NULL, NULL );
637 ok = ( proxy != NULL );
638 dbus_g_connection_unref( connection );
642 if( !ok ){
643 g_debug( "%s: object is not candidate because ShowIfRegistered=%s", thisfn, name );
646 g_free( name );
648 return( ok );
651 static gboolean
652 is_candidate_for_show_if_true( const NAIContext *object, guint target, GList *files )
654 static const gchar *thisfn = "na_icontext_is_candidate_for_show_if_true";
655 gboolean ok = TRUE;
656 gchar *command = na_object_get_show_if_true( object );
658 if( command && strlen( command )){
659 ok = FALSE;
660 gchar *stdout = NULL;
661 g_spawn_command_line_sync( command, &stdout, NULL, NULL, NULL );
663 if( stdout && !strcmp( stdout, "true" )){
664 ok = TRUE;
667 g_free( stdout );
670 if( !ok ){
671 g_debug( "%s: object is not candidate because ShowIfTrue=%s", thisfn, command );
674 g_free( command );
676 return( ok );
679 static gboolean
680 is_candidate_for_show_if_running( const NAIContext *object, guint target, GList *files )
682 static const gchar *thisfn = "na_icontext_is_candidate_for_show_if_running";
683 gboolean ok = TRUE;
684 gchar *searched;
685 glibtop_proclist proclist;
686 glibtop_proc_state procstate;
687 pid_t *pid_list;
688 guint i;
689 gchar *running = na_object_get_show_if_running( object );
691 if( running && strlen( running )){
692 ok = FALSE;
693 searched = g_path_get_basename( running );
694 pid_list = glibtop_get_proclist( &proclist, GLIBTOP_KERN_PROC_ALL, 0 );
696 for( i=0 ; i<proclist.number && !ok ; ++i ){
697 glibtop_get_proc_state( &procstate, pid_list[i] );
698 /*g_debug( "%s: i=%d, cmd=%s", thisfn, i, procstate.cmd );*/
699 if( strcmp( procstate.cmd, searched ) == 0 ){
700 g_debug( "%s: i=%d, cmd=%s", thisfn, i, procstate.cmd );
701 ok = TRUE;
705 g_free( pid_list );
706 g_free( searched );
709 if( !ok ){
710 g_debug( "%s: object is not candidate because ShowIfRunning=%s", thisfn, running );
713 g_free( running );
715 return( ok );
719 * object may embed a list of - possibly negated - mimetypes
720 * each file of the selection must satisfy all conditions of this list
721 * an empty list is considered the same as '*' or '* / *'
723 * most time, we will just have '*' - so try to optimize the code to
724 * be as fast as possible when we don't filter on mimetype
726 * mimetypes are of type : "image/ *; !image/jpeg"
727 * file mimetype must be compatible with at least one positive assertion
728 * (they are ORed), while not being of any negative assertions (they are
729 * ANDed)
731 * here, for each mimetype of the selection, do
732 * . match is FALSE while this mimetype has not matched a positive assertion
733 * . nomatch is FALSE while this mimetype has not matched a negative assertion
734 * we are going to check each mimetype filter
735 * while we have not found a match among positive conditions (i.e. while !match)
736 * we have to check all negative conditions to verify that the current
737 * examined mimetype never match these
739 static gboolean
740 is_candidate_for_mimetypes( const NAIContext *object, guint target, GList *files )
742 static const gchar *thisfn = "na_icontext_is_candidate_for_mimetypes";
743 gboolean ok = TRUE;
744 gboolean all = na_object_get_all_mimetypes( object );
746 g_debug( "%s: all=%s", thisfn, all ? "True":"False" );
748 if( !all ){
749 GSList *mimetypes = na_object_get_mimetypes( object );
750 GSList *im;
751 GList *it;
753 for( it = files ; it && ok ; it = it->next ){
754 gchar *ftype;
755 gboolean regular, match, positive;
757 match = FALSE;
758 ftype = na_selected_info_get_mime_type( NA_SELECTED_INFO( it->data ));
759 regular = na_selected_info_is_regular( NA_SELECTED_INFO( it->data ));
761 if( ftype ){
762 for( im = mimetypes ; im && ok ; im = im->next ){
763 const gchar *imtype = ( const gchar * ) im->data;
764 positive = is_positive_assertion( imtype );
766 if( !positive || !match ){
767 if( is_mimetype_of( positive ? imtype : imtype+1, ftype, regular )){
768 g_debug( "%s: condition=%s, positive=%s, ftype=%s, matched",
769 thisfn, imtype, positive ? "True":"False", ftype );
770 if( positive ){
771 match = TRUE;
772 } else {
773 ok = FALSE;
779 if( !match ){
780 gchar *mimetypes_str = na_core_utils_slist_to_text( mimetypes );
781 g_debug( "%s: no positive match found for Mimetypes=%s", thisfn, mimetypes_str );
782 g_free( mimetypes_str );
783 ok = FALSE;
786 } else {
787 gchar *uri = na_selected_info_get_uri( NA_SELECTED_INFO( it->data ));
788 g_warning( "%s: null mimetype found for %s", thisfn, uri );
789 g_free( uri );
790 ok = FALSE;
793 g_free( ftype );
796 na_core_utils_slist_free( mimetypes );
799 return( ok );
802 static gboolean
803 is_all_mimetype( const gchar *mimetype )
805 return( !strcmp( mimetype, "*" ) ||
806 !strcmp( mimetype, "*/*" ) ||
807 !strcmp( mimetype, "*/all" ) || /* should be considered as invalid */
808 !strcmp( mimetype, "all" ) ||
809 !strcmp( mimetype, "all/*" ) ||
810 !strcmp( mimetype, "all/all" ));
813 static gboolean
814 is_file_mimetype( const gchar *mimetype )
816 return( !strcmp( mimetype, "allfiles" ) ||
817 !strcmp( mimetype, "*/allfiles" ) || /* should be considered as invalid */
818 !strcmp( mimetype, "allfiles/*" ) ||
819 !strcmp( mimetype, "allfiles/all" ) ||
820 !strcmp( mimetype, "all/allfiles" ));
824 * does the file fgroup/fsubgroup have a mimetype which is 'a sort of'
825 * mimetype specified one ?
826 * for example, "image/jpeg" is clearly a sort of "image/ *"
828 * content type if the same as the mime type in *nix;
829 * this is not true on Win32 platforms
831 static gboolean
832 is_mimetype_of( const gchar *mimetype, const gchar *ftype, gboolean is_regular )
834 static const gchar *thisfn = "na_icontext_is_mimetype_of";
835 gboolean is_type_of;
836 gchar *file_content_type, *def_content_type;
838 if( is_all_mimetype( mimetype )){
839 return( TRUE );
842 if( is_file_mimetype( mimetype ) && is_regular ){
843 return( TRUE );
846 is_type_of = FALSE;
847 file_content_type = g_content_type_from_mime_type( ftype );
848 def_content_type = g_content_type_from_mime_type( mimetype );
850 if( file_content_type && def_content_type ){
851 is_type_of = g_content_type_is_a( file_content_type, def_content_type );
852 g_debug( "%s: def_mimetype=%s content_type=%s file_mimetype=%s content_type=%s is_a=%s",
853 thisfn, mimetype, def_content_type, ftype, file_content_type,
854 is_type_of ? "True":"False" );
857 g_free( file_content_type );
858 g_free( def_content_type );
860 return( is_type_of );
863 static gboolean
864 is_candidate_for_basenames( const NAIContext *object, guint target, GList *files )
866 static const gchar *thisfn = "na_icontext_is_candidate_for_basenames";
867 gboolean ok = TRUE;
868 GSList *basenames = na_object_get_basenames( object );
870 if( basenames ){
871 if( strcmp( basenames->data, "*" ) != 0 || g_slist_length( basenames ) > 1 ){
872 gboolean matchcase = na_object_get_matchcase( object );
873 GSList *ib;
874 GList *it;
875 gchar *tmp;
877 for( it = files ; it && ok ; it = it->next ){
878 gchar *pattern, *bname, *bname_utf8;
879 gboolean match, positive;
880 gchar *pattern_utf8;
882 bname = na_selected_info_get_basename( NA_SELECTED_INFO( it->data ));
883 bname_utf8 = g_filename_to_utf8( bname, -1, NULL, NULL, NULL );
884 if( !matchcase ){
885 tmp = g_utf8_strdown( bname_utf8, -1 );
886 g_free( bname_utf8 );
887 bname_utf8 = tmp;
889 match = FALSE;
891 for( ib = basenames ; ib && ok ; ib = ib->next ){
892 pattern = matchcase ?
893 g_strdup(( gchar * ) ib->data ) :
894 g_utf8_strdown(( gchar * ) ib->data, -1 );
895 positive = is_positive_assertion( pattern );
896 pattern_utf8 = g_filename_to_utf8( positive ? pattern : pattern+1, -1, NULL, NULL, NULL );
898 if( !positive || !match ){
899 if( g_pattern_match_simple( pattern_utf8, bname_utf8 )){
900 g_debug( "%s: condition=%s, positive=%s, basename=%s: matched",
901 thisfn, pattern_utf8, positive ? "True":"False", bname_utf8 );
902 if( positive ){
903 match = TRUE;
904 } else {
905 ok = FALSE;
910 g_free( pattern_utf8 );
911 g_free( pattern );
914 if( !match ){
915 gchar *basenames_str = na_core_utils_slist_to_text( basenames );
916 g_debug( "%s: no positive match found for Basenames=%s", thisfn, basenames_str );
917 g_free( basenames_str );
918 ok = FALSE;
921 g_free( bname_utf8 );
922 g_free( bname );
926 na_core_utils_slist_free( basenames );
929 return( ok );
932 static gboolean
933 is_candidate_for_selection_count( const NAIContext *object, guint target, GList *files )
935 static const gchar *thisfn = "na_icontext_is_candidate_for_selection_count";
936 gboolean ok = TRUE;
937 gint limit;
938 guint count;
939 gchar *selection_count = na_object_get_selection_count( object );
941 if( selection_count && strlen( selection_count )){
942 limit = atoi( selection_count+1 );
943 count = g_list_length( files );
944 ok = FALSE;
946 switch( selection_count[0] ){
947 case '<':
948 ok = ( count < limit );
949 break;
950 case '=':
951 ok = ( count == limit );
952 break;
953 case '>':
954 ok = ( count > limit );
955 break;
956 default:
957 break;
961 if( !ok ){
962 g_debug( "%s: object is not candidate because SelectionCount=%s", thisfn, selection_count );
965 g_free( selection_count );
967 return( ok );
971 * it is likely that all selected items have the same scheme, because they
972 * are all in the same location and the scheme mainly depends on location
973 * so we have here a great possible optimization by only testing the
974 * first selected item.
975 * note that this optimization may be wrong, for example when ran from the
976 * command-line with a random set of pseudo-selected items
977 * so we take care of only checking _distincts_ schemes of provided selection
978 * against schemes conditions.
980 static gboolean
981 is_candidate_for_schemes( const NAIContext *object, guint target, GList *files )
983 static const gchar *thisfn = "na_icontext_is_candidate_for_schemes";
984 gboolean ok = TRUE;
985 GSList *schemes = na_object_get_schemes( object );
987 if( schemes ){
988 if( strcmp( schemes->data, "*" ) != 0 || g_slist_length( schemes ) > 1 ){
989 GSList *distincts = NULL;
990 GList *it;
992 for( it = files ; it && ok ; it = it->next ){
993 gchar *scheme = na_selected_info_get_uri_scheme( NA_SELECTED_INFO( it->data ));
995 if( na_core_utils_slist_count( distincts, scheme ) == 0 ){
996 GSList *is;
997 gchar *pattern;
998 gboolean match, positive;
1000 match = FALSE;
1001 distincts = g_slist_prepend( distincts, g_strdup( scheme ));
1003 for( is = schemes ; is && ok ; is = is->next ){
1004 pattern = ( gchar * ) is->data;
1005 positive = is_positive_assertion( pattern );
1007 if( !positive || !match ){
1008 if( is_compatible_scheme( positive ? pattern : pattern+1, scheme )){
1009 if( positive ){
1010 match = TRUE;
1011 } else {
1012 ok = FALSE;
1018 ok &= match;
1021 g_free( scheme );
1024 na_core_utils_slist_free( distincts );
1027 if( !ok ){
1028 gchar *schemes_str = na_core_utils_slist_to_text( schemes );
1029 g_debug( "%s: object is not candidate because Schemes=%s", thisfn, schemes_str );
1030 g_free( schemes_str );
1033 na_core_utils_slist_free( schemes );
1036 return( ok );
1039 static gboolean
1040 is_compatible_scheme( const gchar *pattern, const gchar *scheme )
1042 gboolean compatible;
1044 compatible = FALSE;
1046 if( strcmp( pattern, "*" )){
1047 compatible = TRUE;
1048 } else {
1049 compatible = ( strcmp( pattern, scheme ) == 0 );
1052 return( compatible );
1056 * assumuing here the same sort of optimization than for schemes
1057 * i.e. we assume that all selected items are must probably located
1058 * in the same dirname
1059 * so we take care of only checking _distinct_ dirnames against folder
1060 * conditions
1062 static gboolean
1063 is_candidate_for_folders( const NAIContext *object, guint target, GList *files )
1065 static const gchar *thisfn = "na_icontext_is_candidate_for_folders";
1066 gboolean ok = TRUE;
1067 GSList *folders = na_object_get_folders( object );
1069 if( folders ){
1070 if( strcmp( folders->data, "/" ) != 0 || g_slist_length( folders ) > 1 ){
1071 GSList *distincts = NULL;
1072 GList *it;
1074 for( it = files ; it && ok ; it = it->next ){
1075 gchar *dirname = na_selected_info_get_dirname( NA_SELECTED_INFO( it->data ));
1077 if( na_core_utils_slist_count( distincts, dirname ) == 0 ){
1078 g_debug( "%s: distinct dirname=%s", thisfn, dirname );
1080 GSList *id;
1081 gchar *dirname_utf8, *pattern_utf8;
1082 const gchar *pattern;
1083 gboolean match, positive;
1084 gboolean has_pattern;
1086 distincts = g_slist_prepend( distincts, g_strdup( dirname ));
1087 dirname_utf8 = g_filename_to_utf8( dirname, -1, NULL, NULL, NULL );
1088 match = FALSE;
1090 for( id = folders ; id && ok ; id = id->next ){
1091 pattern = ( const gchar * ) id->data;
1092 positive = is_positive_assertion( pattern );
1093 pattern_utf8 = g_filename_to_utf8( positive ? pattern : pattern+1, -1, NULL, NULL, NULL );
1094 has_pattern = ( g_strstr_len( pattern_utf8, -1, "*" ) != NULL );
1096 if( !positive || !match ){
1097 if(( has_pattern && g_pattern_match_simple( pattern_utf8, dirname_utf8 )) || g_str_has_prefix( dirname_utf8, pattern_utf8 )){
1098 g_debug( "%s: condition=%s, positive=%s: matched",
1099 thisfn, pattern, positive ? "True":"False" );
1100 if( positive ){
1101 match = TRUE;
1102 } else {
1103 ok = FALSE;
1105 /*} else {
1106 g_debug( "%s: condition=%s, positive=%s: not matched",
1107 thisfn, pattern_utf8, positive ? "True":"False" );*/
1111 g_free( pattern_utf8 );
1114 ok &= match;
1116 g_free( dirname_utf8 );
1119 g_free( dirname );
1122 na_core_utils_slist_free( distincts );
1125 if( !ok ){
1126 gchar *folders_str = na_core_utils_slist_to_text( folders );
1127 g_debug( "%s: object is not candidate because Folders=%s", thisfn, folders_str );
1128 g_free( folders_str );
1131 na_core_utils_slist_free( folders );
1134 return( ok );
1137 static gboolean
1138 is_candidate_for_capabilities( const NAIContext *object, guint target, GList *files )
1140 static const gchar *thisfn = "na_icontext_is_candidate_for_capabilities";
1141 gboolean ok = TRUE;
1142 GSList *capabilities = na_object_get_capabilities( object );
1144 if( capabilities ){
1145 GSList *ic;
1146 GList *it;
1147 const gchar *cap;
1148 gboolean match, positive;
1150 for( it = files ; it && ok ; it = it->next ){
1151 for( ic = capabilities ; ic && ok ; ic = ic->next ){
1152 cap = ( const gchar * ) ic->data;
1153 positive = is_positive_assertion( cap );
1154 match = FALSE;
1156 if( !strcmp( positive ? cap : cap+1, "Owner" )){
1157 match = na_selected_info_is_owner( NA_SELECTED_INFO( it->data ), getlogin());
1159 } else if( !strcmp( positive ? cap : cap+1, "Readable" )){
1160 match = na_selected_info_is_readable( NA_SELECTED_INFO( it->data ));
1162 } else if( !strcmp( positive ? cap : cap+1, "Writable" )){
1163 match = na_selected_info_is_writable( NA_SELECTED_INFO( it->data ));
1165 } else if( !strcmp( positive ? cap : cap+1, "Executable" )){
1166 match = na_selected_info_is_executable( NA_SELECTED_INFO( it->data ));
1168 } else if( !strcmp( positive ? cap : cap+1, "Local" )){
1169 match = na_selected_info_is_local( NA_SELECTED_INFO( it->data ));
1171 } else {
1172 g_warning( "%s: unknown capability %s", thisfn, cap );
1175 ok &= (( positive && match ) || ( !positive && !match ));
1179 if( !ok ){
1180 gchar *capabilities_str = na_core_utils_slist_to_text( capabilities );
1181 g_debug( "%s: object is not candidate because Capabilities=%s", thisfn, capabilities_str );
1182 g_free( capabilities_str );
1185 na_core_utils_slist_free( capabilities );
1188 return( ok );
1191 static gboolean
1192 is_valid_basenames( const NAIContext *object )
1194 gboolean valid;
1195 GSList *basenames;
1197 basenames = na_object_get_basenames( object );
1198 valid = basenames && g_slist_length( basenames ) > 0;
1199 na_core_utils_slist_free( basenames );
1201 if( !valid ){
1202 na_object_debug_invalid( object, "basenames" );
1205 return( valid );
1209 * check
1210 * that the list is not empty
1211 * that there is not empty item
1212 * that no mimetype is coded as '* / something'
1214 static gboolean
1215 is_valid_mimetypes( const NAIContext *object )
1217 static const gchar *thisfn = "na_icontext_is_valid_mimetypes";
1218 gboolean valid;
1219 GSList *mimetypes, *it;
1220 guint count_ok, count_errs;
1221 const gchar *imtype;
1223 mimetypes = na_object_get_mimetypes( object );
1224 count_ok = 0;
1225 count_errs = 0;
1227 for( it = mimetypes ; it ; it = it->next ){
1228 imtype = ( const gchar * ) it->data;
1230 if( !imtype || !strlen( imtype )){
1231 g_debug( "%s: null or empty mimetype", thisfn );
1232 count_errs +=1;
1233 continue;
1236 if( imtype[0] == '*' ){
1237 if( imtype[1] ){
1238 if( imtype[1] != '/' ){
1239 g_debug( "%s: invalid mimetype: %s", thisfn, imtype );
1240 count_errs +=1;
1241 continue;
1243 if( imtype[2] && imtype[2] != '*' ){
1244 g_debug( "%s: invalid mimetype: %s", thisfn, imtype );
1245 count_errs +=1;
1246 continue;
1250 count_ok += 1;
1253 valid = ( count_ok > 0 && count_errs == 0 );
1255 if( !valid ){
1256 na_object_debug_invalid( object, "mimetypes" );
1259 na_core_utils_slist_free( mimetypes );
1261 return( valid );
1264 static gboolean
1265 is_valid_schemes( const NAIContext *object )
1267 gboolean valid;
1268 GSList *schemes;
1270 schemes = na_object_get_schemes( object );
1271 valid = schemes && g_slist_length( schemes ) > 0;
1272 na_core_utils_slist_free( schemes );
1274 if( !valid ){
1275 na_object_debug_invalid( object, "schemes" );
1278 return( valid );
1281 static gboolean
1282 is_valid_folders( const NAIContext *object )
1284 gboolean valid;
1285 GSList *folders;
1287 folders = na_object_get_folders( object );
1288 valid = folders && g_slist_length( folders ) > 0;
1289 na_core_utils_slist_free( folders );
1291 if( !valid ){
1292 na_object_debug_invalid( object, "folders" );
1295 return( valid );
1299 * "image/ *" is a positive assertion
1300 * "!image/jpeg" is a negative one
1302 static gboolean
1303 is_positive_assertion( const gchar *assertion )
1305 gboolean positive = TRUE;
1307 if( assertion ){
1308 gchar *dupped = g_strdup( assertion );
1309 const gchar *stripped = g_strstrip( dupped );
1310 if( stripped ){
1311 positive = ( stripped[0] != '!' );
1313 g_free( dupped );
1316 return( positive );