summaryrefslogtreecommitdiffstats
path: root/panels/printers/pp-printer.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/printers/pp-printer.c')
-rw-r--r--panels/printers/pp-printer.c562
1 files changed, 562 insertions, 0 deletions
diff --git a/panels/printers/pp-printer.c b/panels/printers/pp-printer.c
new file mode 100644
index 0000000..76d0bfc
--- /dev/null
+++ b/panels/printers/pp-printer.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Martin Hatina <mhatina@redhat.com>
+ * Marek Kasik <mkasik@redhat.com>
+ */
+
+#include "pp-printer.h"
+
+#include "pp-job.h"
+
+#if (CUPS_VERSION_MAJOR == 1) && (CUPS_VERSION_MINOR <= 6)
+#define IPP_STATE_IDLE IPP_IDLE
+#endif
+
+struct _PpPrinter
+{
+ GObject parent_instance;
+ gchar *printer_name;
+};
+
+G_DEFINE_TYPE (PpPrinter, pp_printer, G_TYPE_OBJECT)
+
+static void
+pp_printer_dispose (GObject *object)
+{
+ PpPrinter *self = PP_PRINTER (object);
+
+ g_clear_pointer (&self->printer_name, g_free);
+
+ G_OBJECT_CLASS (pp_printer_parent_class)->dispose (object);
+}
+
+static void
+pp_printer_class_init (PpPrinterClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = pp_printer_dispose;
+}
+
+static void
+pp_printer_init (PpPrinter *self)
+{
+}
+
+PpPrinter *
+pp_printer_new (const gchar *name)
+{
+ PpPrinter *self = g_object_new (PP_TYPE_PRINTER, NULL);
+
+ self->printer_name = g_strdup (name);
+
+ return self;
+}
+
+const gchar *
+pp_printer_get_name (PpPrinter *self)
+{
+ g_return_val_if_fail (PP_IS_PRINTER (self), NULL);
+ return self->printer_name;
+}
+
+static void
+printer_rename_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ PpPrinter *self = PP_PRINTER (source_object);
+ gboolean result;
+ const gchar *new_printer_name = task_data;
+
+ result = printer_rename (self->printer_name, new_printer_name);
+
+ if (result)
+ {
+ g_free (self->printer_name);
+ self->printer_name = g_strdup (new_printer_name);
+ }
+
+ g_task_return_boolean (task, result);
+}
+
+static void
+printer_rename_dbus_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ PpPrinter *self;
+ g_autoptr(GVariant) output = NULL;
+ gboolean result = FALSE;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+
+ output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ res,
+ &error);
+
+ if (output != NULL)
+ {
+ const gchar *ret_error;
+
+ self = g_task_get_source_object (task);
+
+ g_variant_get (output, "(&s)", &ret_error);
+ if (ret_error[0] != '\0')
+ {
+ g_warning ("cups-pk-helper: renaming of printer %s failed: %s", self->printer_name, ret_error);
+ }
+ else
+ {
+ result = TRUE;
+ g_free (self->printer_name);
+ self->printer_name = g_strdup (g_task_get_task_data (task));
+ }
+
+ g_task_return_boolean (task, result);
+ }
+ else
+ {
+ if (error->domain == G_DBUS_ERROR &&
+ (error->code == G_DBUS_ERROR_SERVICE_UNKNOWN ||
+ error->code == G_DBUS_ERROR_UNKNOWN_METHOD))
+ {
+ g_warning ("Update cups-pk-helper to at least 0.2.6 please to be able to use PrinterRename method.");
+
+ g_task_run_in_thread (task, printer_rename_thread);
+ }
+ else
+ {
+ g_task_return_boolean (task, FALSE);
+ }
+ }
+}
+
+static void
+get_bus_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ PpPrinter *self;
+ GDBusConnection *bus;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+
+ bus = g_bus_get_finish (res, &error);
+ if (bus != NULL)
+ {
+ self = g_task_get_source_object (task);
+ g_dbus_connection_call (bus,
+ MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ "PrinterRename",
+ g_variant_new ("(ss)",
+ self->printer_name,
+ g_task_get_task_data (task)),
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ g_task_get_cancellable (task),
+ printer_rename_dbus_cb,
+ task);
+ g_steal_pointer (&task);
+ }
+ else
+ {
+ g_warning ("Failed to get system bus: %s", error->message);
+ g_task_return_boolean (task, FALSE);
+ }
+}
+
+void
+pp_printer_rename_async (PpPrinter *self,
+ const gchar *new_printer_name,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ g_return_if_fail (new_printer_name != NULL);
+
+ task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
+ g_task_set_task_data (task, g_strdup (new_printer_name), g_free);
+
+ g_bus_get (G_BUS_TYPE_SYSTEM,
+ cancellable,
+ get_bus_cb,
+ task);
+}
+
+gboolean
+pp_printer_rename_finish (PpPrinter *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+typedef struct
+{
+ gboolean myjobs;
+ gint which_jobs;
+} GetJobsData;
+
+static void
+get_jobs_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ipp_attribute_t *attr = NULL;
+ static gchar *printer_attributes[] = { "auth-info-required" };
+ GetJobsData *get_jobs_data = task_data;
+ cups_job_t *jobs = NULL;
+ PpPrinter *self = PP_PRINTER (source_object);
+ gboolean auth_info_is_required;
+ PpJob *job;
+ ipp_t *job_request;
+ ipp_t *job_response;
+ ipp_t *printer_request;
+ ipp_t *printer_response;
+ gchar **auth_info_required = NULL;
+ g_autofree gchar *printer_name = NULL;
+ g_autoptr(GPtrArray) array = NULL;
+ gint num_jobs;
+ gint i, j;
+
+ num_jobs = cupsGetJobs (&jobs,
+ self->printer_name,
+ get_jobs_data->myjobs ? 1 : 0,
+ get_jobs_data->which_jobs);
+
+ array = g_ptr_array_new_with_free_func (g_object_unref);
+ for (i = 0; i < num_jobs; i++)
+ {
+ auth_info_is_required = FALSE;
+ if (jobs[i].state == IPP_JOB_HELD)
+ {
+ g_autofree gchar *job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", jobs[i].id);
+
+ job_request = ippNewRequest (IPP_GET_JOB_ATTRIBUTES);
+ ippAddString (job_request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "job-uri", NULL, job_uri);
+ ippAddString (job_request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser ());
+ ippAddString (job_request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", NULL, "job-hold-until");
+ job_response = cupsDoRequest (CUPS_HTTP_DEFAULT, job_request, "/");
+
+ if (job_response != NULL)
+ {
+ attr = ippFindAttribute (job_response, "job-hold-until", IPP_TAG_ZERO);
+ if (attr != NULL && g_strcmp0 (ippGetString (attr, 0, NULL), "auth-info-required") == 0)
+ {
+ auth_info_is_required = TRUE;
+
+ if (auth_info_required == NULL)
+ {
+ g_autofree gchar *printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", self->printer_name);
+
+ printer_request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
+ ippAddString (printer_request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+ ippAddString (printer_request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser ());
+ ippAddStrings (printer_request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", 1, NULL, (const char **) printer_attributes);
+ printer_response = cupsDoRequest (CUPS_HTTP_DEFAULT, printer_request, "/");
+
+ if (printer_response != NULL)
+ {
+ attr = ippFindAttribute (printer_response, "auth-info-required", IPP_TAG_ZERO);
+ if (attr != NULL)
+ {
+ auth_info_required = g_new0 (gchar *, ippGetCount (attr) + 1);
+ for (j = 0; j < ippGetCount (attr); j++)
+ auth_info_required[j] = g_strdup (ippGetString (attr, j, NULL));
+ }
+
+ ippDelete (printer_response);
+ }
+ }
+ }
+
+ ippDelete (job_response);
+ }
+ }
+
+ job = pp_job_new (jobs[i].id, jobs[i].title, jobs[i].state, jobs[i].priority, auth_info_is_required ? auth_info_required : NULL);
+ g_ptr_array_add (array, job);
+ }
+
+ g_strfreev (auth_info_required);
+ cupsFreeJobs (num_jobs, jobs);
+
+ if (g_task_set_return_on_cancel (task, FALSE))
+ {
+ g_task_return_pointer (task, g_steal_pointer (&array), (GDestroyNotify) g_ptr_array_unref);
+ }
+}
+
+void
+pp_printer_get_jobs_async (PpPrinter *self,
+ gboolean myjobs,
+ gint which_jobs,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GetJobsData *get_jobs_data;
+ g_autoptr(GTask) task = NULL;
+
+ get_jobs_data = g_new (GetJobsData, 1);
+ get_jobs_data->myjobs = myjobs;
+ get_jobs_data->which_jobs = which_jobs;
+
+ task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
+ g_task_set_task_data (task, get_jobs_data, g_free);
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_run_in_thread (task, get_jobs_thread);
+}
+
+GPtrArray *
+pp_printer_get_jobs_finish (PpPrinter *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (res, self), NULL);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+pp_printer_delete_dbus_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ PpPrinter *self;
+ g_autoptr(GVariant) output = NULL;
+ gboolean result = FALSE;
+ g_autoptr(GError) error = NULL;
+ GTask *task = user_data;
+
+ output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ res,
+ &error);
+
+ if (output != NULL)
+ {
+ const gchar *ret_error;
+
+ self = g_task_get_source_object (task);
+
+ g_variant_get (output, "(&s)", &ret_error);
+ if (ret_error[0] != '\0')
+ g_warning ("cups-pk-helper: removing of printer %s failed: %s", self->printer_name, ret_error);
+ else
+ result = TRUE;
+
+ g_task_return_boolean (task, result);
+ }
+ else
+ {
+ g_warning ("%s", error->message);
+
+ g_task_return_boolean (task, FALSE);
+ }
+}
+
+static void
+pp_printer_delete_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ PpPrinter *self;
+ GDBusConnection *bus;
+ g_autoptr(GError) error = NULL;
+ GTask *task = user_data;
+
+ bus = g_bus_get_finish (res, &error);
+ if (bus != NULL)
+ {
+ self = g_task_get_source_object (task);
+
+ g_dbus_connection_call (bus,
+ MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ "PrinterDelete",
+ g_variant_new ("(s)", self->printer_name),
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ g_task_get_cancellable (task),
+ pp_printer_delete_dbus_cb,
+ task);
+ }
+ else
+ {
+ g_warning ("Failed to get system bus: %s", error->message);
+ g_task_return_boolean (task, FALSE);
+ }
+}
+
+void
+pp_printer_delete_async (PpPrinter *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
+
+ g_bus_get (G_BUS_TYPE_SYSTEM,
+ cancellable,
+ pp_printer_delete_cb,
+ task);
+}
+
+gboolean
+pp_printer_delete_finish (PpPrinter *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+typedef struct
+{
+ gchar *filename;
+ gchar *job_name;
+} PrintFileData;
+
+static void
+print_file_data_free (PrintFileData *print_file_data)
+{
+ g_free (print_file_data->filename);
+ g_free (print_file_data->job_name);
+
+ g_slice_free (PrintFileData, print_file_data);
+}
+
+static void
+print_file_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ PpPrinter *self = PP_PRINTER (source_object);
+ PrintFileData *print_file_data;
+ cups_ptype_t type = 0;
+ cups_dest_t *dest = NULL;
+ const gchar *printer_type = NULL;
+ gboolean ret = FALSE;
+ g_autofree gchar *printer_uri = NULL;
+ g_autofree gchar *resource = NULL;
+ ipp_t *response = NULL;
+ ipp_t *request;
+
+ dest = cupsGetNamedDest (CUPS_HTTP_DEFAULT, self->printer_name, NULL);
+ if (dest != NULL)
+ {
+ printer_type = cupsGetOption ("printer-type",
+ dest->num_options,
+ dest->options);
+ cupsFreeDests (1, dest);
+
+ if (printer_type)
+ type = atoi (printer_type);
+ }
+
+ if (type & CUPS_PRINTER_CLASS)
+ {
+ printer_uri = g_strdup_printf ("ipp://localhost/classes/%s", self->printer_name);
+ resource = g_strdup_printf ("/classes/%s", self->printer_name);
+ }
+ else
+ {
+ printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", self->printer_name);
+ resource = g_strdup_printf ("/printers/%s", self->printer_name);
+ }
+
+ print_file_data = g_task_get_task_data (task);
+
+ request = ippNewRequest (IPP_PRINT_JOB);
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser ());
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "job-name", NULL, print_file_data->job_name);
+ response = cupsDoFileRequest (CUPS_HTTP_DEFAULT, request, resource, print_file_data->filename);
+
+ if (response != NULL)
+ {
+ if (ippGetState (response) == IPP_ERROR)
+ g_warning ("An error has occurred during printing of test page.");
+ if (ippGetState (response) == IPP_STATE_IDLE)
+ ret = TRUE;
+
+ ippDelete (response);
+ }
+
+ if (g_task_set_return_on_cancel (task, FALSE))
+ {
+ g_task_return_boolean (task, ret);
+ }
+}
+
+void
+pp_printer_print_file_async (PpPrinter *self,
+ const gchar *filename,
+ const gchar *job_name,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ PrintFileData *print_file_data;
+ g_autoptr(GTask) task = NULL;
+
+ print_file_data = g_new (PrintFileData, 1);
+ print_file_data->filename = g_strdup (filename);
+ print_file_data->job_name = g_strdup (job_name);
+
+ task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
+
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_set_task_data (task, print_file_data, (GDestroyNotify) print_file_data_free);
+
+ g_task_run_in_thread (task, print_file_thread);
+}
+
+gboolean
+pp_printer_print_file_finish (PpPrinter *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (res), error);
+}