diff --git a/accounts-service/com.endlessm.ParentalControls.AppFilter.xml b/accounts-service/com.endlessm.ParentalControls.AppFilter.xml
index af73110..8d4d554 100644
--- a/accounts-service/com.endlessm.ParentalControls.AppFilter.xml
+++ b/accounts-service/com.endlessm.ParentalControls.AppFilter.xml
@@ -59,5 +59,18 @@
+
+
+
+
+
diff --git a/libeos-parental-controls/app-filter.c b/libeos-parental-controls/app-filter.c
index d462d8f..8d8e085 100644
--- a/libeos-parental-controls/app-filter.c
+++ b/libeos-parental-controls/app-filter.c
@@ -58,6 +58,7 @@ struct _EpcAppFilter
EpcAppFilterListType app_list_type;
GVariant *oars_ratings; /* (type a{ss}) (owned non-floating) */
+ gboolean allow_app_installation;
};
G_DEFINE_BOXED_TYPE (EpcAppFilter, epc_app_filter,
@@ -254,6 +255,8 @@ epc_app_filter_get_oars_sections (EpcAppFilter *filter)
* section, inclusive. Any app with a more intense value for this section must
* be hidden from the user whose @filter this is.
*
+ * This does not factor in epc_app_filter_is_app_installation_allowed().
+ *
* Returns: an #EpcAppFilterOarsValue
* Since: 0.1.0
*/
@@ -284,6 +287,28 @@ epc_app_filter_get_oars_value (EpcAppFilter *filter,
return EPC_APP_FILTER_OARS_VALUE_UNKNOWN;
}
+/**
+ * epc_app_filter_is_app_installation_allowed:
+ * @filter: an #EpcAppFilter
+ *
+ * Get whether app installation is allowed at all for the user. This should be
+ * queried in addition to the OARS values (epc_app_filter_get_oars_value()) — if
+ * it returns %FALSE, the OARS values should be ignored and app installation
+ * should be unconditionally disallowed.
+ *
+ * Returns: %TRUE if app installation is allowed in general for this user;
+ * %FALSE if it is unconditionally disallowed for this user
+ * Since: 0.1.0
+ */
+gboolean
+epc_app_filter_is_app_installation_allowed (EpcAppFilter *filter)
+{
+ g_return_val_if_fail (filter != NULL, FALSE);
+ g_return_val_if_fail (filter->ref_count >= 1, FALSE);
+
+ return filter->allow_app_installation;
+}
+
/**
* _epc_app_filter_build_app_filter_variant:
* @filter: an #EpcAppFilter
@@ -420,6 +445,7 @@ epc_get_app_filter (GDBusConnection *connection,
const gchar *content_rating_kind;
g_autoptr(GVariant) oars_variant = NULL;
g_autoptr(GHashTable) oars_map = NULL;
+ gboolean allow_app_installation;
g_return_val_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
@@ -491,6 +517,13 @@ epc_get_app_filter (GDBusConnection *connection,
return NULL;
}
+ if (!g_variant_lookup (properties, "allow-app-installation", "b",
+ &allow_app_installation))
+ {
+ /* Default value. */
+ allow_app_installation = TRUE;
+ }
+
/* Success. Create an #EpcAppFilter object to contain the results. */
app_filter = g_new0 (EpcAppFilter, 1);
app_filter->ref_count = 1;
@@ -499,6 +532,7 @@ epc_get_app_filter (GDBusConnection *connection,
app_filter->app_list_type =
is_whitelist ? EPC_APP_FILTER_LIST_WHITELIST : EPC_APP_FILTER_LIST_BLACKLIST;
app_filter->oars_ratings = g_steal_pointer (&oars_variant);
+ app_filter->allow_app_installation = allow_app_installation;
return g_steal_pointer (&app_filter);
}
@@ -643,8 +677,10 @@ epc_set_app_filter (GDBusConnection *connection,
g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) app_filter_variant = NULL;
g_autoptr(GVariant) oars_filter_variant = NULL;
+ g_autoptr(GVariant) allow_app_installation_variant = NULL;
g_autoptr(GVariant) app_filter_result_variant = NULL;
g_autoptr(GVariant) oars_filter_result_variant = NULL;
+ g_autoptr(GVariant) allow_app_installation_result_variant = NULL;
g_autoptr(GError) local_error = NULL;
g_return_val_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection), FALSE);
@@ -667,6 +703,7 @@ epc_set_app_filter (GDBusConnection *connection,
app_filter_variant = _epc_app_filter_build_app_filter_variant (app_filter);
oars_filter_variant = g_variant_new ("(s@a{ss})", "oars-1.1",
app_filter->oars_ratings);
+ allow_app_installation_variant = g_variant_new_boolean (app_filter->allow_app_installation);
app_filter_result_variant =
g_dbus_connection_call_sync (connection,
@@ -714,6 +751,29 @@ epc_set_app_filter (GDBusConnection *connection,
return FALSE;
}
+ allow_app_installation_result_variant =
+ g_dbus_connection_call_sync (connection,
+ "org.freedesktop.Accounts",
+ object_path,
+ "org.freedesktop.DBus.Properties",
+ "Set",
+ g_variant_new ("(ssv)",
+ "com.endlessm.ParentalControls.AppFilter",
+ "allow-app-installation",
+ g_steal_pointer (&allow_app_installation_variant)),
+ G_VARIANT_TYPE ("()"),
+ allow_interactive_authorization
+ ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
+ : G_DBUS_CALL_FLAGS_NONE,
+ -1, /* timeout, ms */
+ cancellable,
+ &local_error);
+ if (local_error != NULL)
+ {
+ g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
+ return FALSE;
+ }
+
return TRUE;
}
@@ -845,6 +905,7 @@ typedef struct
{
GPtrArray *paths_blacklist; /* (nullable) (owned) (element-type filename) */
GHashTable *oars; /* (nullable) (owned) (element-type utf8 EpcAppFilterOarsValue) */
+ gboolean allow_app_installation;
/*< private >*/
gpointer padding[2];
@@ -959,6 +1020,7 @@ epc_app_filter_builder_copy (EpcAppFilterBuilder *builder)
_copy->paths_blacklist = g_ptr_array_ref (_builder->paths_blacklist);
if (_builder->oars != NULL)
_copy->oars = g_hash_table_ref (_builder->oars);
+ _copy->allow_app_installation = _builder->allow_app_installation;
return g_steal_pointer (©);
}
@@ -1042,6 +1104,7 @@ epc_app_filter_builder_end (EpcAppFilterBuilder *builder)
app_filter->app_list = (gchar **) g_ptr_array_free (g_steal_pointer (&_builder->paths_blacklist), FALSE);
app_filter->app_list_type = EPC_APP_FILTER_LIST_BLACKLIST;
app_filter->oars_ratings = g_steal_pointer (&oars_variant);
+ app_filter->allow_app_installation = _builder->allow_app_installation;
epc_app_filter_builder_clear (builder);
@@ -1130,3 +1193,27 @@ epc_app_filter_builder_set_oars_value (EpcAppFilterBuilder *builder,
g_hash_table_insert (_builder->oars, g_strdup (oars_section),
GUINT_TO_POINTER (value));
}
+
+/**
+ * epc_app_filter_builder_set_allow_app_installation:
+ * @builder: an initialised #EpcAppFilterBuilder
+ * @allow_app_installation: %TRUE to allow app installation; %FALSE to
+ * unconditionally disallow it
+ *
+ * Set whether app installation is allowed in general for the user. If this is
+ * %TRUE, app installation is still subject to the OARS values
+ * (epc_app_filter_builder_set_oars_value()). If it is %FALSE, app installation
+ * is unconditionally disallowed for this user.
+ *
+ * Since: 0.1.0
+ */
+void
+epc_app_filter_builder_set_allow_app_installation (EpcAppFilterBuilder *builder,
+ gboolean allow_app_installation)
+{
+ EpcAppFilterBuilderReal *_builder = (EpcAppFilterBuilderReal *) builder;
+
+ g_return_if_fail (_builder != NULL);
+
+ _builder->allow_app_installation = allow_app_installation;
+}
diff --git a/libeos-parental-controls/app-filter.h b/libeos-parental-controls/app-filter.h
index 5abb28a..ab6280a 100644
--- a/libeos-parental-controls/app-filter.h
+++ b/libeos-parental-controls/app-filter.h
@@ -107,6 +107,8 @@ const gchar **epc_app_filter_get_oars_sections (EpcAppFilter *filter);
EpcAppFilterOarsValue epc_app_filter_get_oars_value (EpcAppFilter *filter,
const gchar *oars_section);
+gboolean epc_app_filter_is_app_installation_allowed (EpcAppFilter *filter);
+
EpcAppFilter *epc_get_app_filter (GDBusConnection *connection,
uid_t user_id,
gboolean allow_interactive_authorization,
@@ -152,6 +154,7 @@ typedef struct
/*< private >*/
gpointer p0;
gpointer p1;
+ gboolean b0;
gpointer p2;
gpointer p3;
} EpcAppFilterBuilder;
@@ -174,7 +177,8 @@ GType epc_app_filter_builder_get_type (void);
#define EPC_APP_FILTER_BUILDER_INIT() \
{ \
g_ptr_array_new_with_free_func (g_free), \
- g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL) \
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL), \
+ TRUE, \
}
void epc_app_filter_builder_init (EpcAppFilterBuilder *builder);
@@ -199,4 +203,7 @@ void epc_app_filter_builder_set_oars_value (EpcAppFilterBuilder *builde
const gchar *oars_section,
EpcAppFilterOarsValue value);
+void epc_app_filter_builder_set_allow_app_installation (EpcAppFilterBuilder *builder,
+ gboolean allow_app_installation);
+
G_END_DECLS
diff --git a/libeos-parental-controls/tests/app-filter.c b/libeos-parental-controls/tests/app-filter.c
index 6e2eadc..2bbbb63 100644
--- a/libeos-parental-controls/tests/app-filter.c
+++ b/libeos-parental-controls/tests/app-filter.c
@@ -125,6 +125,7 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture,
EPC_APP_FILTER_OARS_VALUE_MILD);
epc_app_filter_builder_set_oars_value (fixture->builder, "language-humor",
EPC_APP_FILTER_OARS_VALUE_MODERATE);
+ epc_app_filter_builder_set_allow_app_installation (fixture->builder, FALSE);
filter = epc_app_filter_builder_end (fixture->builder);
@@ -147,6 +148,8 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture,
sections = epc_app_filter_get_oars_sections (filter);
const gchar * const expected_sections[] = { "drugs-alcohol", "language-humor", NULL };
assert_strv_equal ((const gchar * const *) sections, expected_sections);
+
+ g_assert_false (epc_app_filter_is_app_installation_allowed (filter));
}
/* Test building an empty #EpcAppFilter using an #EpcAppFilterBuilder. */
@@ -178,6 +181,8 @@ test_app_filter_builder_empty (BuilderFixture *fixture,
sections = epc_app_filter_get_oars_sections (filter);
const gchar * const expected_sections[] = { NULL };
assert_strv_equal ((const gchar * const *) sections, expected_sections);
+
+ g_assert_true (epc_app_filter_is_app_installation_allowed (filter));
}
/* Check that copying a cleared #EpcAppFilterBuilder works, and the copy can
@@ -198,6 +203,8 @@ test_app_filter_builder_copy_empty (void)
g_assert_true (epc_app_filter_is_path_allowed (filter, "/bin/false"));
g_assert_false (epc_app_filter_is_path_allowed (filter, "/bin/true"));
+
+ g_assert_true (epc_app_filter_is_app_installation_allowed (filter));
}
/* Check that copying a filled #EpcAppFilterBuilder works, and the copy can be
@@ -210,11 +217,13 @@ test_app_filter_builder_copy_full (void)
g_autoptr(EpcAppFilter) filter = NULL;
epc_app_filter_builder_blacklist_path (builder, "/bin/true");
+ epc_app_filter_builder_set_allow_app_installation (builder, FALSE);
builder_copy = epc_app_filter_builder_copy (builder);
filter = epc_app_filter_builder_end (builder_copy);
g_assert_true (epc_app_filter_is_path_allowed (filter, "/bin/false"));
g_assert_false (epc_app_filter_is_path_allowed (filter, "/bin/true"));
+ g_assert_false (epc_app_filter_is_app_installation_allowed (filter));
}
int