Update copyright message
[nautilus-actions.git] / src / core / na-icontext.c
blob7d2323b0d9c59abd81b99b8dc006a93c81eade4c
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 void convert_allfiles_mimetype( NAIContext *context );
66 static gboolean v_is_candidate( NAIContext *object, guint target, GList *selection );
68 static gboolean is_candidate_for_target( const NAIContext *object, guint target, GList *files );
69 static gboolean is_candidate_for_show_in( const NAIContext *object, guint target, GList *files );
70 static gboolean is_candidate_for_try_exec( const NAIContext *object, guint target, GList *files );
71 static gboolean is_candidate_for_show_if_registered( const NAIContext *object, guint target, GList *files );
72 static gboolean is_candidate_for_show_if_true( const NAIContext *object, guint target, GList *files );
73 static gboolean is_candidate_for_show_if_running( const NAIContext *object, guint target, GList *files );
74 static gboolean is_candidate_for_mimetypes( const NAIContext *object, guint target, GList *files );
75 static gboolean is_mimetype_of( const gchar *file_type, const gchar *group, const gchar *subgroup );
76 static void split_mimetype( const gchar *mimetype, gchar **group, gchar **subgroup );
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_isfiledir( const NAIContext *object );
87 static gboolean is_valid_schemes( const NAIContext *object );
88 static gboolean is_valid_folders( const NAIContext *object );
90 static gboolean is_positive_assertion( const gchar *assertion );
92 /**
93 * na_icontext_get_type:
95 * Returns: the #GType type of this interface.
97 GType
98 na_icontext_get_type( void )
100 static GType type = 0;
102 if( !type ){
103 type = register_type();
106 return( type );
110 * na_icontext_register_type:
112 * Registers this interface.
114 static GType
115 register_type( void )
117 static const gchar *thisfn = "na_icontext_register_type";
118 GType type;
120 static const GTypeInfo info = {
121 sizeof( NAIContextInterface ),
122 ( GBaseInitFunc ) interface_base_init,
123 ( GBaseFinalizeFunc ) interface_base_finalize,
124 NULL,
125 NULL,
126 NULL,
129 NULL
132 g_debug( "%s", thisfn );
134 type = g_type_register_static( G_TYPE_INTERFACE, "NAIContext", &info, 0 );
136 g_type_interface_add_prerequisite( type, G_TYPE_OBJECT );
138 return( type );
141 static void
142 interface_base_init( NAIContextInterface *klass )
144 static const gchar *thisfn = "na_icontext_interface_base_init";
146 if( !st_initialized ){
148 g_debug( "%s: klass%p (%s)", thisfn, ( void * ) klass, G_OBJECT_CLASS_NAME( klass ));
150 klass->private = g_new0( NAIContextInterfacePrivate, 1 );
152 st_initialized = TRUE;
156 static void
157 interface_base_finalize( NAIContextInterface *klass )
159 static const gchar *thisfn = "na_icontext_interface_base_finalize";
161 if( st_initialized && !st_finalized ){
163 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
165 st_finalized = TRUE;
167 g_free( klass->private );
172 * na_icontext_is_candidate:
173 * @context: a #NAIContext to be checked.
174 * @target: the current target.
175 * @selection: the currently selected items, as a #GList of NASelectedInfo items.
177 * Determines if the given object may be candidate to be displayed in
178 * the Nautilus context menu, depending of the list of currently selected
179 * items.
181 * This function is called by <methodname>build_nautilus_menus</methodname>
182 * plugin function for each item found in NAPivot items list, and, when this
183 * an action, for each profile of this action.
185 * Returns: %TRUE if this @context succeeds to all tests and is so a
186 * valid candidate to be displayed in Nautilus context menu, %FALSE
187 * else.
189 * Since: 2.30
191 gboolean
192 na_icontext_is_candidate( const NAIContext *context, guint target, GList *selection )
194 static const gchar *thisfn = "na_icontext_is_candidate";
195 gboolean is_candidate;
197 g_return_val_if_fail( NA_IS_ICONTEXT( context ), FALSE );
199 g_debug( "%s: object=%p (%s), target=%d, selection=%p (count=%d)",
200 thisfn, ( void * ) context, G_OBJECT_TYPE_NAME( context ), target, (void * ) selection, g_list_length( selection ));
202 is_candidate = v_is_candidate( NA_ICONTEXT( context ), target, selection );
204 if( is_candidate ){
205 is_candidate =
206 is_candidate_for_target( context, target, selection ) &&
207 is_candidate_for_show_in( context, target, selection ) &&
208 is_candidate_for_try_exec( context, target, selection ) &&
209 is_candidate_for_show_if_registered( context, target, selection ) &&
210 is_candidate_for_show_if_true( context, target, selection ) &&
211 is_candidate_for_show_if_running( context, target, selection ) &&
212 is_candidate_for_mimetypes( context, target, selection ) &&
213 is_candidate_for_basenames( context, target, selection ) &&
214 is_candidate_for_selection_count( context, target, selection ) &&
215 is_candidate_for_schemes( context, target, selection ) &&
216 is_candidate_for_folders( context, target, selection ) &&
217 is_candidate_for_capabilities( context, target, selection );
220 return( is_candidate );
224 * na_icontext_is_valid:
225 * @context: the #NAObjectProfile to be checked.
227 * Returns: %TRUE if this @context is valid, %FALSE else.
229 * This function is part of <methodname>NAIDuplicable::check_status</methodname>
230 * and is called by #NAIDuplicable objects which also implement #NAIContext
231 * interface. It so doesn't make sense of asking the object for its
232 * validity status as it has already been checked before calling the
233 * function.
235 * Since: 2.30
237 gboolean
238 na_icontext_is_valid( const NAIContext *context )
240 gboolean is_valid;
242 g_return_val_if_fail( NA_IS_ICONTEXT( context ), FALSE );
244 is_valid =
245 is_valid_basenames( context ) &&
246 is_valid_mimetypes( context ) &&
247 is_valid_isfiledir( context ) &&
248 is_valid_schemes( context ) &&
249 is_valid_folders( context );
251 return( is_valid );
255 * na_icontext_is_all_mimetypes:
256 * @context: the #NAIContext object to be checked.
258 * Returns: %TRUE if this @context is valid for all mimetypes, %FALSE else.
260 * Since: 2.30
262 gboolean
263 na_icontext_is_all_mimetypes( const NAIContext *context )
265 gboolean is_all;
266 GSList *mimetypes, *im;
268 g_return_val_if_fail( NA_IS_ICONTEXT( context ), FALSE );
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 continue;
277 const gchar *imtype = ( const gchar * ) im->data;
278 if( !strcmp( imtype, "*" ) ||
279 !strcmp( imtype, "*/*" ) ||
280 !strcmp( imtype, "all" ) ||
281 !strcmp( imtype, "all/*" ) ||
282 !strcmp( imtype, "all/all" )){
283 continue;
285 is_all = FALSE;
286 break;
289 na_core_utils_slist_free( mimetypes );
291 return( is_all );
295 * na_icontext_read_done:
296 * @context: the #NAIContext to be prepared.
298 * Prepares the specified #NAIContext just after it has been readen.
300 * <itemizedlist>
301 * <listitem>
302 * <para>
303 * This converts a 'all/allfiles' mimetype to 'all/all' + 'file' scheme.
304 * </para>
305 * </listitem>
306 * <listitem>
307 * <para>
308 * This setup an internal flag when mimetypes is like 'all/all'
309 * in order to optimize computation time;
310 * </para>
311 * </listitem>
312 * </itemizedlist>
314 * Since: 2.30
316 void
317 na_icontext_read_done( NAIContext *context )
319 convert_allfiles_mimetype( context );
321 na_object_set_all_mimetypes( context, na_icontext_is_all_mimetypes( context ));
325 * na_icontext_set_scheme:
326 * @context: the #NAIContext to be updated.
327 * @scheme: name of the scheme.
328 * @selected: whether this scheme is candidate to this @context.
330 * Sets the status of a @scheme relative to this @context.
332 * Since: 2.30
334 void
335 na_icontext_set_scheme( NAIContext *context, const gchar *scheme, gboolean selected )
337 GSList *schemes;
339 g_return_if_fail( NA_IS_ICONTEXT( context ));
341 schemes = na_object_get_schemes( context );
342 schemes = na_core_utils_slist_setup_element( schemes, scheme, selected );
343 na_object_set_schemes( context, schemes );
344 na_core_utils_slist_free( schemes );
348 * na_icontext_set_only_desktop:
349 * @context: the #NAIContext to be updated.
350 * @desktop: name of the desktop environment.
351 * @selected: whether this @desktop is candidate to this @context.
353 * Sets the status of the @desktop relative to this @context for the OnlyShowIn list.
355 * Since: 2.30
357 void
358 na_icontext_set_only_desktop( NAIContext *context, const gchar *desktop, gboolean selected )
360 GSList *desktops;
362 g_return_if_fail( NA_IS_ICONTEXT( context ));
364 desktops = na_object_get_only_show_in( context );
365 desktops = na_core_utils_slist_setup_element( desktops, desktop, selected );
366 na_object_set_only_show_in( context, desktops );
367 na_core_utils_slist_free( desktops );
371 * na_icontext_set_only_desktop:
372 * @context: the #NAIContext to be updated.
373 * @desktop: name of the desktop environment.
374 * @selected: whether this @desktop is candidate to this @context.
376 * Sets the status of the @desktop relative to this @context for the NotShowIn list.
378 * Since: 2.30
380 void
381 na_icontext_set_not_desktop( NAIContext *context, const gchar *desktop, gboolean selected )
383 GSList *desktops;
385 g_return_if_fail( NA_IS_ICONTEXT( context ));
387 desktops = na_object_get_not_show_in( context );
388 desktops = na_core_utils_slist_setup_element( desktops, desktop, selected );
389 na_object_set_not_show_in( context, desktops );
390 na_core_utils_slist_free( desktops );
394 * na_icontext_replace_folder:
395 * @context: the #NAIContext to be updated.
396 * @old: the old uri.
397 * @new: the new uri.
399 * Replaces the @old URI by the @new one.
401 * Since: 2.30
403 void
404 na_icontext_replace_folder( NAIContext *context, const gchar *old, const gchar *new )
406 GSList *folders;
408 g_return_if_fail( NA_IS_ICONTEXT( context ));
410 folders = na_object_get_folders( context );
411 folders = na_core_utils_slist_remove_utf8( folders, old );
412 folders = g_slist_append( folders, ( gpointer ) g_strdup( new ));
413 na_object_set_folders( context, folders );
414 na_core_utils_slist_free( folders );
418 * Convert 'all/allfiles' mimetype to 'all/all' + 'file' scheme.
419 * This takes into account
420 * - all/allfiles, allfiles, allfiles/ * and allfiles/all
421 * - negated assertions.
423 static void
424 convert_allfiles_mimetype( NAIContext *context )
426 GSList *mimetypes, *im;
427 GSList *schemes;
428 gchar *tmp;
429 gboolean modified;
431 modified = FALSE;
432 mimetypes = na_object_get_mimetypes( context );
433 schemes = na_object_get_schemes( context );
435 for( im = mimetypes ; im ; im = im->next ){
436 if( !im->data || !strlen( im->data )){
437 continue;
440 gchar *imtype = ( gchar * ) im->data;
441 gboolean positive = is_positive_assertion( imtype );
442 guint i = ( positive ? 0 : 1 );
444 if( !strcmp( imtype+i, "allfiles" ) ||
445 !strcmp( imtype+i, "allfiles/*" ) ||
446 !strcmp( imtype+i, "allfiles/all" ) ||
447 !strcmp( imtype+i, "all/allfiles" )){
449 g_free( im->data );
450 im->data = g_strdup( "all/all" );
451 tmp = g_strdup_printf( "%sfile", positive ? "" : "!" );
452 schemes = g_slist_prepend( schemes, tmp );
453 modified = TRUE;
457 if( modified ){
458 na_object_set_mimetypes( context, mimetypes );
459 na_object_set_schemes( context, schemes );
462 na_core_utils_slist_free( mimetypes );
463 na_core_utils_slist_free( schemes );
466 static gboolean
467 v_is_candidate( NAIContext *context, guint target, GList *selection )
469 gboolean is_candidate;
471 is_candidate = TRUE;
473 if( NA_ICONTEXT_GET_INTERFACE( context )->is_candidate ){
474 is_candidate = NA_ICONTEXT_GET_INTERFACE( context )->is_candidate( context, target, selection );
477 return( is_candidate );
481 * whether the given NAIContext object is candidate for this target
482 * target is context menu for location, context menu for selection or toolbar for location
483 * only actions are concerned by this check
485 static gboolean
486 is_candidate_for_target( const NAIContext *object, guint target, GList *files )
488 static const gchar *thisfn = "na_icontext_is_candidate_for_target";
489 gboolean ok = TRUE;
491 if( NA_IS_OBJECT_ACTION( object )){
492 switch( target ){
493 case ITEM_TARGET_LOCATION:
494 ok = na_object_is_target_location( object );
495 break;
497 case ITEM_TARGET_TOOLBAR:
498 ok = na_object_is_target_toolbar( object );
499 break;
501 case ITEM_TARGET_SELECTION:
502 ok = na_object_is_target_selection( object );
503 break;
505 case ITEM_TARGET_ANY:
506 ok = TRUE;
507 break;
509 default:
510 g_warning( "%s: unknonw target=%d", thisfn, target );
511 ok = FALSE;
515 if( !ok ){
516 g_debug( "%s: object is not candidate because target doesn't match (asked=%d)", thisfn, target );
517 /*na_object_dump( object );*/
520 return( ok );
524 * only show in / not show in
525 * only one of these two data may be set
527 static gboolean
528 is_candidate_for_show_in( const NAIContext *object, guint target, GList *files )
530 static const gchar *thisfn = "na_icontext_is_candidate_for_show_in";
531 gboolean ok = TRUE;
532 GSList *only_in = na_object_get_only_show_in( object );
533 GSList *not_in = na_object_get_not_show_in( object );
534 gchar *environment;
536 /* TODO: how-to get current running environment ? */
537 environment = g_strdup( "GNOME" );
539 if( environment && strlen( environment )){
540 if( only_in && g_slist_length( only_in )){
541 ok = ( na_core_utils_slist_count( only_in, environment ) > 0 );
542 } else if( not_in && g_slist_length( not_in )){
543 ok = ( na_core_utils_slist_count( not_in, environment ) == 0 );
547 if( !ok ){
548 gchar *only_str = na_core_utils_slist_to_text( only_in );
549 gchar *not_str = na_core_utils_slist_to_text( not_in );
550 g_debug( "%s: object is not candidate because OnlyShowIn=%s, NotShowIn=%s", thisfn, only_str, not_str );
551 g_free( not_str );
552 g_free( only_str );
555 g_free( environment );
556 na_core_utils_slist_free( not_in );
557 na_core_utils_slist_free( only_in );
559 return( ok );
563 * if the data is set, it should be the path of an executable file
565 static gboolean
566 is_candidate_for_try_exec( const NAIContext *object, guint target, GList *files )
568 static const gchar *thisfn = "na_icontext_is_candidate_for_try_exec";
569 gboolean ok = TRUE;
570 GError *error = NULL;
571 gchar *tryexec = na_object_get_try_exec( object );
573 if( tryexec && strlen( tryexec )){
574 ok = FALSE;
575 GFile *file = g_file_new_for_path( tryexec );
576 GFileInfo *info = g_file_query_info( file, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, G_FILE_QUERY_INFO_NONE, NULL, &error );
577 if( error ){
578 g_debug( "%s: %s", thisfn, error->message );
579 g_error_free( error );
581 } else {
582 ok = g_file_info_get_attribute_boolean( info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE );
585 if( info ){
586 g_object_unref( info );
589 g_object_unref( file );
592 if( !ok ){
593 g_debug( "%s: object is not candidate because TryExec=%s", thisfn, tryexec );
596 g_free( tryexec );
598 return( ok );
601 static gboolean
602 is_candidate_for_show_if_registered( const NAIContext *object, guint target, GList *files )
604 static const gchar *thisfn = "na_icontext_is_candidate_for_show_if_registered";
605 gboolean ok = TRUE;
606 gchar *name = na_object_get_show_if_registered( object );
608 if( name && strlen( name )){
609 ok = FALSE;
610 GError *error = NULL;
611 DBusGConnection *connection = dbus_g_bus_get( DBUS_BUS_SESSION, &error );
613 if( !connection ){
614 if( error ){
615 g_warning( "%s: %s", thisfn, error->message );
616 g_error_free( error );
619 } else {
620 DBusGProxy *proxy = dbus_g_proxy_new_for_name( connection, name, NULL, NULL );
621 ok = ( proxy != NULL );
622 dbus_g_connection_unref( connection );
626 if( !ok ){
627 g_debug( "%s: object is not candidate because ShowIfRegistered=%s", thisfn, name );
630 g_free( name );
632 return( ok );
635 static gboolean
636 is_candidate_for_show_if_true( const NAIContext *object, guint target, GList *files )
638 static const gchar *thisfn = "na_icontext_is_candidate_for_show_if_true";
639 gboolean ok = TRUE;
640 gchar *command = na_object_get_show_if_true( object );
642 if( command && strlen( command )){
643 ok = FALSE;
644 gchar *stdout = NULL;
645 g_spawn_command_line_sync( command, &stdout, NULL, NULL, NULL );
647 if( stdout && !strcmp( stdout, "true" )){
648 ok = TRUE;
651 g_free( stdout );
654 if( !ok ){
655 g_debug( "%s: object is not candidate because ShowIfTrue=%s", thisfn, command );
658 g_free( command );
660 return( ok );
663 static gboolean
664 is_candidate_for_show_if_running( const NAIContext *object, guint target, GList *files )
666 static const gchar *thisfn = "na_icontext_is_candidate_for_show_if_running";
667 gboolean ok = TRUE;
668 gchar *searched;
669 glibtop_proclist proclist;
670 glibtop_proc_state procstate;
671 pid_t *pid_list;
672 guint i;
673 gchar *running = na_object_get_show_if_running( object );
675 if( running && strlen( running )){
676 ok = FALSE;
677 searched = g_path_get_basename( running );
678 pid_list = glibtop_get_proclist( &proclist, GLIBTOP_KERN_PROC_ALL, 0 );
680 for( i=0 ; i<proclist.number && !ok ; ++i ){
681 glibtop_get_proc_state( &procstate, pid_list[i] );
682 /*g_debug( "%s: i=%d, cmd=%s", thisfn, i, procstate.cmd );*/
683 if( strcmp( procstate.cmd, searched ) == 0 ){
684 g_debug( "%s: i=%d, cmd=%s", thisfn, i, procstate.cmd );
685 ok = TRUE;
689 g_free( pid_list );
690 g_free( searched );
693 if( !ok ){
694 g_debug( "%s: object is not candidate because ShowIfRunning=%s", thisfn, running );
697 g_free( running );
699 return( ok );
703 * object may embed a list of - possibly negated - mimetypes
704 * each file of the selection must satisfy all conditions of this list
705 * an empty list is considered the same as '*' or '* / *'
707 * most time, we will just have '*' - so try to optimize the code to
708 * be as fast as possible when we don't filter on mimetype
710 * mimetypes are of type : "image/ *; !image/jpeg"
711 * file mimetype must be compatible with at least one positive assertion
712 * (they are ORed), while not being of any negative assertions (they are
713 * ANDed)
715 * here, for each mimetype of the selection, do
716 * . match is FALSE while this mimetype has not matched a positive assertion
717 * . nomatch is FALSE while this mimetype has not matched a negative assertion
718 * we are going to check each mimetype filter
719 * while we have not found a match among positive conditions (i.e. while !match)
720 * we have to check all negative conditions to verify that the current
721 * examined mimetype never match these
723 static gboolean
724 is_candidate_for_mimetypes( const NAIContext *object, guint target, GList *files )
726 static const gchar *thisfn = "na_icontext_is_candidate_for_mimetypes";
727 gboolean ok = TRUE;
728 gboolean all = na_object_get_all_mimetypes( object );
730 g_debug( "%s: all=%s", thisfn, all ? "True":"False" );
732 if( !all ){
733 GSList *mimetypes = na_object_get_mimetypes( object );
734 GSList *im;
735 GList *it;
737 for( it = files ; it && ok ; it = it->next ){
738 gchar *ftype, *fgroup, *fsubgroup;
739 gboolean match, positive;
741 match = FALSE;
742 ftype = na_selected_info_get_mime_type( NA_SELECTED_INFO( it->data ));
744 if( ftype ){
745 split_mimetype( ftype, &fgroup, &fsubgroup );
747 for( im = mimetypes ; im && ok ; im = im->next ){
748 const gchar *imtype = ( const gchar * ) im->data;
749 positive = is_positive_assertion( imtype );
751 if( !positive || !match ){
752 if( is_mimetype_of( positive ? imtype : imtype+1, fgroup, fsubgroup )){
753 g_debug( "%s: condition=%s, positive=%s, ftype=%s, fgroup=%s, fsubgroup=%s, matched",
754 thisfn, imtype, positive ? "True":"False", ftype, fgroup, fsubgroup );
755 if( positive ){
756 match = TRUE;
757 } else {
758 ok = FALSE;
764 if( !match ){
765 gchar *mimetypes_str = na_core_utils_slist_to_text( mimetypes );
766 g_debug( "%s: no positive match found for Mimetypes=%s", thisfn, mimetypes_str );
767 g_free( mimetypes_str );
768 ok = FALSE;
771 } else {
772 gchar *uri = na_selected_info_get_uri( NA_SELECTED_INFO( it->data ));
773 g_debug( "%s: null mimetype found for %s", thisfn, uri );
774 g_free( uri );
775 ok = FALSE;
778 g_free( fsubgroup );
779 g_free( fgroup );
780 g_free( ftype );
783 na_core_utils_slist_free( mimetypes );
786 return( ok );
790 * does the file fgroup/fsubgrop have a mimetype which is 'a sort of'
791 * mimetype specified one ?
792 * for example, "image/jpeg" is clearly a sort of "image/ *"
793 * but how to check to see if "msword/xml" is a sort of "application/xml" ??
795 static gboolean
796 is_mimetype_of( const gchar *mimetype, const gchar *fgroup, const gchar *fsubgroup )
798 gboolean is_type_of;
799 gchar *mgroup, *msubgroup;
801 if( !strcmp( mimetype, "*" ) || !strcmp( mimetype, "*/*" )){
802 return( TRUE );
805 split_mimetype( mimetype, &mgroup, &msubgroup );
807 is_type_of = ( strcmp( fgroup, mgroup ) == 0 );
808 if( is_type_of ){
809 if( strcmp( msubgroup, "*" ) != 0 ){
810 is_type_of = ( strcmp( fsubgroup, msubgroup ) == 0 );
814 g_free( mgroup );
815 g_free( msubgroup );
817 return( is_type_of );
821 * split the given mimetype to its group and subgroup components
823 static void
824 split_mimetype( const gchar *mimetype, gchar **group, gchar **subgroup )
826 GSList *slist = na_core_utils_slist_from_split( mimetype, "/" );
827 GSList *is = slist;
828 *group = g_strdup(( const gchar * ) is->data );
829 is = is->next;
830 *subgroup = g_strdup(( const gchar * ) is->data );
831 na_core_utils_slist_free( slist );
834 static gboolean
835 is_candidate_for_basenames( const NAIContext *object, guint target, GList *files )
837 static const gchar *thisfn = "na_icontext_is_candidate_for_basenames";
838 gboolean ok = TRUE;
839 GSList *basenames = na_object_get_basenames( object );
841 if( basenames ){
842 if( strcmp( basenames->data, "*" ) != 0 || g_slist_length( basenames ) > 1 ){
843 gboolean matchcase = na_object_get_matchcase( object );
844 GSList *ib;
845 GList *it;
846 gchar *tmp;
848 for( it = files ; it && ok ; it = it->next ){
849 gchar *pattern, *bname, *bname_utf8;
850 gboolean match, positive;
851 gchar *pattern_utf8;
853 bname = na_selected_info_get_basename( NA_SELECTED_INFO( it->data ));
854 bname_utf8 = g_filename_to_utf8( bname, -1, NULL, NULL, NULL );
855 if( !matchcase ){
856 tmp = g_ascii_strdown( bname_utf8, g_utf8_strlen( bname_utf8, -1 ));
857 g_free( bname_utf8 );
858 bname_utf8 = tmp;
860 match = FALSE;
862 for( ib = basenames ; ib && ok ; ib = ib->next ){
863 pattern = matchcase ?
864 g_strdup(( gchar * ) ib->data ) :
865 g_ascii_strdown(( gchar * ) ib->data, strlen(( gchar * ) ib->data ));
866 positive = is_positive_assertion( pattern );
867 pattern_utf8 = g_filename_to_utf8( positive ? pattern : pattern+1, -1, NULL, NULL, NULL );
869 if( !positive || !match ){
870 if( g_pattern_match_simple( pattern_utf8, bname_utf8 )){
871 g_debug( "%s: condition=%s, positive=%s, basename=%s: matched",
872 thisfn, pattern_utf8, positive ? "True":"False", bname_utf8 );
873 if( positive ){
874 match = TRUE;
875 } else {
876 ok = FALSE;
881 g_free( pattern_utf8 );
882 g_free( pattern );
885 if( !match ){
886 gchar *basenames_str = na_core_utils_slist_to_text( basenames );
887 g_debug( "%s: no positive match found for Basenames=%s", thisfn, basenames_str );
888 g_free( basenames_str );
889 ok = FALSE;
892 g_free( bname_utf8 );
893 g_free( bname );
897 na_core_utils_slist_free( basenames );
900 return( ok );
903 static gboolean
904 is_candidate_for_selection_count( const NAIContext *object, guint target, GList *files )
906 static const gchar *thisfn = "na_icontext_is_candidate_for_selection_count";
907 gboolean ok = TRUE;
908 gint limit;
909 guint count;
910 gchar *selection_count = na_object_get_selection_count( object );
912 if( selection_count && strlen( selection_count )){
913 limit = atoi( selection_count+1 );
914 count = g_list_length( files );
915 ok = FALSE;
917 switch( selection_count[0] ){
918 case '<':
919 ok = ( count < limit );
920 break;
921 case '=':
922 ok = ( count == limit );
923 break;
924 case '>':
925 ok = ( count > limit );
926 break;
927 default:
928 break;
932 if( !ok ){
933 g_debug( "%s: object is not candidate because SelectionCount=%s", thisfn, selection_count );
936 g_free( selection_count );
938 return( ok );
942 * it is likely that all selected items have the same scheme, because they
943 * are all in the same location and the scheme mainly depends on location
944 * so we have here a great possible optimization by only testing the
945 * first selected item.
946 * note that this optimization may be wrong, for example when ran from the
947 * command-line with a random set of pseudo-selected items
948 * so we take care of only checking _distincts_ schemes of provided selection
949 * against schemes conditions.
951 static gboolean
952 is_candidate_for_schemes( const NAIContext *object, guint target, GList *files )
954 static const gchar *thisfn = "na_icontext_is_candidate_for_schemes";
955 gboolean ok = TRUE;
956 GSList *schemes = na_object_get_schemes( object );
958 if( schemes ){
959 if( strcmp( schemes->data, "*" ) != 0 || g_slist_length( schemes ) > 1 ){
960 GSList *distincts = NULL;
961 GList *it;
963 for( it = files ; it && ok ; it = it->next ){
964 gchar *scheme = na_selected_info_get_uri_scheme( NA_SELECTED_INFO( it->data ));
966 if( na_core_utils_slist_count( distincts, scheme ) == 0 ){
967 GSList *is;
968 gchar *pattern;
969 gboolean match, positive;
971 match = FALSE;
972 distincts = g_slist_prepend( distincts, g_strdup( scheme ));
974 for( is = schemes ; is && ok ; is = is->next ){
975 pattern = ( gchar * ) is->data;
976 positive = is_positive_assertion( pattern );
978 if( !positive || !match ){
979 if( is_compatible_scheme( positive ? pattern : pattern+1, scheme )){
980 if( positive ){
981 match = TRUE;
982 } else {
983 ok = FALSE;
989 ok &= match;
992 g_free( scheme );
995 na_core_utils_slist_free( distincts );
998 if( !ok ){
999 gchar *schemes_str = na_core_utils_slist_to_text( schemes );
1000 g_debug( "%s: object is not candidate because Schemes=%s", thisfn, schemes_str );
1001 g_free( schemes_str );
1004 na_core_utils_slist_free( schemes );
1007 return( ok );
1010 static gboolean
1011 is_compatible_scheme( const gchar *pattern, const gchar *scheme )
1013 gboolean compatible;
1015 compatible = FALSE;
1017 if( strcmp( pattern, "*" )){
1018 compatible = TRUE;
1019 } else {
1020 compatible = ( strcmp( pattern, scheme ) == 0 );
1023 return( compatible );
1027 * assumuing here the same sort of optimization than for schemes
1028 * i.e. we assume that all selected items are must probably located
1029 * in the same dirname
1030 * so we take care of only checking _distinct_ dirnames against folder
1031 * conditions
1033 static gboolean
1034 is_candidate_for_folders( const NAIContext *object, guint target, GList *files )
1036 static const gchar *thisfn = "na_icontext_is_candidate_for_folders";
1037 gboolean ok = TRUE;
1038 GSList *folders = na_object_get_folders( object );
1040 if( folders ){
1041 if( strcmp( folders->data, "/" ) != 0 || g_slist_length( folders ) > 1 ){
1042 GSList *distincts = NULL;
1043 GList *it;
1045 for( it = files ; it && ok ; it = it->next ){
1046 gchar *dirname = na_selected_info_get_dirname( NA_SELECTED_INFO( it->data ));
1048 if( na_core_utils_slist_count( distincts, dirname ) == 0 ){
1049 g_debug( "%s: distinct dirname=%s", thisfn, dirname );
1051 GSList *id;
1052 gchar *dirname_utf8, *pattern_utf8;
1053 const gchar *pattern;
1054 gboolean match, positive;
1055 gboolean has_pattern;
1057 distincts = g_slist_prepend( distincts, g_strdup( dirname ));
1058 dirname_utf8 = g_filename_to_utf8( dirname, -1, NULL, NULL, NULL );
1059 match = FALSE;
1061 for( id = folders ; id && ok ; id = id->next ){
1062 pattern = ( const gchar * ) id->data;
1063 positive = is_positive_assertion( pattern );
1064 pattern_utf8 = g_filename_to_utf8( positive ? pattern : pattern+1, -1, NULL, NULL, NULL );
1065 has_pattern = ( g_strstr_len( pattern_utf8, -1, "*" ) != NULL );
1067 if( !positive || !match ){
1068 if(( has_pattern && g_pattern_match_simple( pattern_utf8, dirname_utf8 )) || g_str_has_prefix( dirname_utf8, pattern_utf8 )){
1069 g_debug( "%s: condition=%s, positive=%s: matched",
1070 thisfn, pattern, positive ? "True":"False" );
1071 if( positive ){
1072 match = TRUE;
1073 } else {
1074 ok = FALSE;
1076 /*} else {
1077 g_debug( "%s: condition=%s, positive=%s: not matched",
1078 thisfn, pattern_utf8, positive ? "True":"False" );*/
1082 g_free( pattern_utf8 );
1085 ok &= match;
1087 g_free( dirname_utf8 );
1090 g_free( dirname );
1093 na_core_utils_slist_free( distincts );
1096 if( !ok ){
1097 gchar *folders_str = na_core_utils_slist_to_text( folders );
1098 g_debug( "%s: object is not candidate because Folders=%s", thisfn, folders_str );
1099 g_free( folders_str );
1102 na_core_utils_slist_free( folders );
1105 return( ok );
1108 static gboolean
1109 is_candidate_for_capabilities( const NAIContext *object, guint target, GList *files )
1111 static const gchar *thisfn = "na_icontext_is_candidate_for_capabilities";
1112 gboolean ok = TRUE;
1113 GSList *capabilities = na_object_get_capabilities( object );
1115 if( capabilities ){
1116 GSList *ic;
1117 GList *it;
1118 const gchar *cap;
1119 gboolean match, positive;
1121 for( it = files ; it && ok ; it = it->next ){
1122 for( ic = capabilities ; ic && ok ; ic = ic->next ){
1123 cap = ( const gchar * ) ic->data;
1124 positive = is_positive_assertion( cap );
1125 match = FALSE;
1127 if( !strcmp( positive ? cap : cap+1, "Owner" )){
1128 match = na_selected_info_is_owner( NA_SELECTED_INFO( it->data ), getlogin());
1130 } else if( !strcmp( positive ? cap : cap+1, "Readable" )){
1131 match = na_selected_info_is_readable( NA_SELECTED_INFO( it->data ));
1133 } else if( !strcmp( positive ? cap : cap+1, "Writable" )){
1134 match = na_selected_info_is_writable( NA_SELECTED_INFO( it->data ));
1136 } else if( !strcmp( positive ? cap : cap+1, "Executable" )){
1137 match = na_selected_info_is_executable( NA_SELECTED_INFO( it->data ));
1139 } else if( !strcmp( positive ? cap : cap+1, "Local" )){
1140 match = na_selected_info_is_local( NA_SELECTED_INFO( it->data ));
1142 } else {
1143 g_warning( "%s: unknown capability %s", thisfn, cap );
1146 ok &= (( positive && match ) || ( !positive && !match ));
1150 if( !ok ){
1151 gchar *capabilities_str = na_core_utils_slist_to_text( capabilities );
1152 g_debug( "%s: object is not candidate because Capabilities=%s", thisfn, capabilities_str );
1153 g_free( capabilities_str );
1156 na_core_utils_slist_free( capabilities );
1159 return( ok );
1162 static gboolean
1163 is_valid_basenames( const NAIContext *object )
1165 gboolean valid;
1166 GSList *basenames;
1168 basenames = na_object_get_basenames( object );
1169 valid = basenames && g_slist_length( basenames ) > 0;
1170 na_core_utils_slist_free( basenames );
1172 if( !valid ){
1173 na_object_debug_invalid( object, "basenames" );
1176 return( valid );
1179 static gboolean
1180 is_valid_mimetypes( const NAIContext *object )
1182 gboolean valid;
1183 GSList *mimetypes;
1185 mimetypes = na_object_get_mimetypes( object );
1186 valid = mimetypes && g_slist_length( mimetypes ) > 0;
1187 na_core_utils_slist_free( mimetypes );
1189 if( !valid ){
1190 na_object_debug_invalid( object, "mimetypes" );
1193 return( valid );
1196 static gboolean
1197 is_valid_isfiledir( const NAIContext *object )
1199 gboolean valid;
1200 gboolean isfile, isdir;
1202 isfile = na_object_is_file( object );
1203 isdir = na_object_is_dir( object );
1205 valid = isfile || isdir;
1207 if( !valid ){
1208 na_object_debug_invalid( object, "isfiledir" );
1211 return( valid );
1214 static gboolean
1215 is_valid_schemes( const NAIContext *object )
1217 gboolean valid;
1218 GSList *schemes;
1220 schemes = na_object_get_schemes( object );
1221 valid = schemes && g_slist_length( schemes ) > 0;
1222 na_core_utils_slist_free( schemes );
1224 if( !valid ){
1225 na_object_debug_invalid( object, "schemes" );
1228 return( valid );
1231 static gboolean
1232 is_valid_folders( const NAIContext *object )
1234 gboolean valid;
1235 GSList *folders;
1237 folders = na_object_get_folders( object );
1238 valid = folders && g_slist_length( folders ) > 0;
1239 na_core_utils_slist_free( folders );
1241 if( !valid ){
1242 na_object_debug_invalid( object, "folders" );
1245 return( valid );
1249 * "image/ *" is a positive assertion
1250 * "!image/jpeg" is a negative one
1252 static gboolean
1253 is_positive_assertion( const gchar *assertion )
1255 gboolean positive = TRUE;
1257 if( assertion ){
1258 gchar *dupped = g_strdup( assertion );
1259 const gchar *stripped = g_strstrip( dupped );
1260 if( stripped ){
1261 positive = ( stripped[0] != '!' );
1263 g_free( dupped );
1266 return( positive );