summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Kasik <mkasik@redhat.com>2012-03-02 13:58:24 +0100
committerVincent Untz <vuntz@novell.com>2012-03-02 14:22:25 +0100
commitc03420571c22187627ddfff52c3dcfff4495c6c3 (patch)
tree71421557dd70a22d43e768e419d3368b22d19031
parent3bc57bf29ab31e7790bc81717e111c3160f265d9 (diff)
Add PrinterAddOption D-Bus method
New method to set options, without a "-default" suffix. https://bugs.freedesktop.org/show_bug.cgi?id=45304
-rw-r--r--src/cups-pk-helper-mechanism.c28
-rw-r--r--src/cups-pk-helper-mechanism.xml8
-rw-r--r--src/cups.c234
-rw-r--r--src/cups.h5
4 files changed, 275 insertions, 0 deletions
diff --git a/src/cups-pk-helper-mechanism.c b/src/cups-pk-helper-mechanism.c
index 083bdda..b20d078 100644
--- a/src/cups-pk-helper-mechanism.c
+++ b/src/cups-pk-helper-mechanism.c
@@ -1186,6 +1186,30 @@ cph_mechanism_printer_delete_option_default (CphIfaceMechanism *object,
}
static gboolean
+cph_mechanism_printer_add_option (CphIfaceMechanism *object,
+ GDBusMethodInvocation *context,
+ const char *name,
+ const char *option,
+ const char *const *values)
+{
+ CphMechanism *mechanism = CPH_MECHANISM (object);
+ gboolean ret;
+
+ _cph_mechanism_emit_called (mechanism);
+
+ if (!_check_polkit_for_printer_class (mechanism, context, name))
+ return TRUE;
+
+ ret = cph_cups_printer_class_set_option (mechanism->priv->cups,
+ name, option, values);
+
+ cph_iface_mechanism_complete_printer_add_option (
+ object, context,
+ _cph_mechanism_return_error (mechanism, !ret));
+ return TRUE;
+}
+
+static gboolean
cph_mechanism_job_cancel_purge (CphIfaceMechanism *object,
GDBusMethodInvocation *context,
int id,
@@ -1420,6 +1444,10 @@ cph_mechanism_connect_signals (CphMechanism *mechanism)
G_CALLBACK (cph_mechanism_printer_add_option_default),
NULL);
g_signal_connect (mechanism,
+ "handle-printer-add-option",
+ G_CALLBACK (cph_mechanism_printer_add_option),
+ NULL);
+ g_signal_connect (mechanism,
"handle-printer-add-with-ppd-file",
G_CALLBACK (cph_mechanism_printer_add_with_ppd_file),
NULL);
diff --git a/src/cups-pk-helper-mechanism.xml b/src/cups-pk-helper-mechanism.xml
index c9915da..b3c5f7f 100644
--- a/src/cups-pk-helper-mechanism.xml
+++ b/src/cups-pk-helper-mechanism.xml
@@ -194,6 +194,14 @@
<arg name="error" direction="out" type="s"/>
</method>
+ <method name="PrinterAddOption">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s"/>
+ <arg name="option" direction="in" type="s"/>
+ <arg name="values" direction="in" type="as"/>
+ <arg name="error" direction="out" type="s"/>
+ </method>
+
<!-- Methods for jobs -->
<!-- JobCancel is deprecated; JobCancelPurge should be used instead -->
diff --git a/src/cups.c b/src/cups.c
index 4d85a69..f119f8d 100644
--- a/src/cups.c
+++ b/src/cups.c
@@ -43,6 +43,7 @@
#include <cups/cups.h>
#include <cups/http.h>
#include <cups/ipp.h>
+#include <cups/ppd.h>
#include "cups.h"
@@ -506,6 +507,18 @@ _cph_cups_add_job_uri (ipp_t *request,
}
static void
+_cph_cups_add_requesting_user_name (ipp_t *request,
+ const char *username)
+{
+ if (username)
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, username);
+ else
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser ());
+}
+
+static void
_cph_cups_set_internal_status (CphCups *cups,
const char *status)
{
@@ -2132,6 +2145,227 @@ out:
return retval;
}
+/*
+ * This function sets given options to specified values in file 'ppdfile'.
+ * This needs to be done because of applications which use content of PPD files
+ * instead of IPP attributes.
+ * CUPS doesn't do this automatically (but hopefully will since 1.6)
+ */
+static gchar *
+_cph_cups_prepare_ppd_for_options (CphCups *cups,
+ const gchar *ppdfile,
+ cups_option_t *options,
+ gint num_options)
+{
+ ppd_choice_t *choice;
+ cups_file_t *in = NULL;
+ cups_file_t *out = NULL;
+ ppd_file_t *ppd;
+ const char *value;
+ gboolean ppdchanged = FALSE;
+ gchar *result = NULL;
+ gchar *error;
+ char newppdfile[1024];
+ char line[1024];
+ char keyword[1024];
+ char *keyptr;
+
+ ppd = ppdOpenFile (ppdfile);
+ if (!ppd) {
+ error = g_strdup_printf ("Unable to open PPD file \"%s\" - %s", ppdfile, strerror (errno));
+ _cph_cups_set_internal_status (cups, error);
+ g_free (error);
+
+ goto out;
+ }
+
+ out = cupsTempFile2 (newppdfile, sizeof (newppdfile));
+ if (!out) {
+ _cph_cups_set_internal_status (cups, "Unable to create temporary file");
+
+ goto out;
+ }
+
+ in = cupsFileOpen (ppdfile, "r");
+ if (!in) {
+ error = g_strdup_printf ("Unable to open PPD file \"%s\" - %s", ppdfile, strerror (errno));
+ _cph_cups_set_internal_status (cups, error);
+ g_free (error);
+
+ goto out;
+ }
+
+ /*
+ * Mark default values and values of options we are changing.
+ */
+ ppdMarkDefaults (ppd);
+ cupsMarkOptions (ppd, num_options, options);
+
+ while (cupsFileGets (in, line, sizeof (line))) {
+ if (!g_str_has_prefix (line, "*Default")) {
+ cupsFilePrintf (out, "%s\n", line);
+ } else {
+ /*
+ * This part parses lines with *Default on their beginning.
+ * Example:
+ * "*DefaultResolution: 1200dpi" becomes:
+ * - keyword: Resolution
+ * - keyptr: 1200dpi
+ */
+ g_strlcpy (keyword, line + strlen ("*Default"), sizeof (keyword));
+
+ for (keyptr = keyword; *keyptr; keyptr++)
+ if (*keyptr == ':' || isspace (*keyptr & 255))
+ break;
+
+ *keyptr++ = '\0';
+ while (isspace (*keyptr & 255))
+ keyptr++;
+
+ /*
+ * We have to change PageSize if any of PageRegion, PageSize,
+ * PaperDimension or ImageableArea changes. We change PageRegion
+ * if PageSize is not available.
+ */
+ if (g_str_equal (keyword, "PageRegion") ||
+ g_str_equal (keyword, "PageSize") ||
+ g_str_equal (keyword, "PaperDimension") ||
+ g_str_equal (keyword, "ImageableArea")) {
+ choice = ppdFindMarkedChoice (ppd, "PageSize");
+ if (!choice)
+ choice = ppdFindMarkedChoice (ppd, "PageRegion");
+ } else {
+ choice = ppdFindMarkedChoice (ppd, keyword);
+ }
+
+ if (choice && !g_str_equal (choice->choice, keyptr)) {
+ if (!g_str_equal (choice->choice, "Custom")) {
+ cupsFilePrintf (out, "*Default%s: %s\n", keyword, choice->choice);
+ ppdchanged = TRUE;
+ } else {
+ /*
+ * We have to set the value in PPD manually if a custom value was passed in
+ * because cupsMarkOptions() marks just "Custom" choice but we need the input
+ * value to be set.
+ * Custom values has "Custom." prefix and has to be enabled.
+ */
+ value = cupsGetOption (keyword, num_options, options);
+ if (value) {
+ cupsFilePrintf (out, "*Default%s: %s\n", keyword, value);
+ ppdchanged = TRUE;
+ } else {
+ cupsFilePrintf (out, "%s\n", line);
+ }
+ }
+ } else {
+ cupsFilePrintf (out, "%s\n", line);
+ }
+ }
+ }
+
+ if (ppdchanged)
+ result = g_strdup (newppdfile);
+
+out:
+
+ if (in)
+ cupsFileClose (in);
+ if (out)
+ cupsFileClose (out);
+ if (ppd)
+ ppdClose (ppd);
+
+ return result;
+}
+
+gboolean
+cph_cups_printer_class_set_option (CphCups *cups,
+ const char *printer_name,
+ const char *option,
+ const char *const *values)
+{
+ ipp_attribute_t *attr;
+ cups_option_t *options;
+ gboolean retval;
+ gboolean is_class;
+ ipp_t *request;
+ char *ppdfile = NULL;
+ char *newppdfile = NULL;
+ gint length = 0;
+ gint num_options = 0;
+ gint i;
+
+ g_return_val_if_fail (CPH_IS_CUPS (cups), FALSE);
+
+ if (!_cph_cups_is_printer_name_valid (cups, printer_name))
+ return FALSE;
+
+ if (!_cph_cups_is_option_valid (cups, option))
+ return FALSE;
+
+ /* check the validity of values, and get the length of the array at the
+ * same time */
+ if (values) {
+ while (values[length] != NULL) {
+ if (!_cph_cups_is_option_value_valid (cups,
+ values[length]))
+ return FALSE;
+ length++;
+ }
+ }
+
+ if (length == 0)
+ return FALSE;
+
+ is_class = cph_cups_is_class (cups, printer_name);
+
+ /*
+ * We permit only one value to change in PPD file because we are setting
+ * default value in it.
+ */
+ if (!is_class && length == 1) {
+ num_options = cupsAddOption (option, values[0], num_options, &options);
+
+ ppdfile = g_strdup (cupsGetPPD (printer_name));
+
+ newppdfile = _cph_cups_prepare_ppd_for_options (cups, ppdfile, options, num_options);
+
+ g_unlink (ppdfile);
+ g_free (ppdfile);
+ cupsFreeOptions (num_options, options);
+ }
+
+ if (is_class) {
+ request = ippNewRequest (CUPS_ADD_MODIFY_CLASS);
+ _cph_cups_add_class_uri (request, printer_name);
+ } else {
+ request = ippNewRequest (CUPS_ADD_MODIFY_PRINTER);
+ _cph_cups_add_printer_uri (request, printer_name);
+ }
+ _cph_cups_add_requesting_user_name (request, NULL);
+
+ if (length == 1) {
+ ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ option, NULL, values[0]);
+ } else {
+ attr = ippAddStrings (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ option, length, NULL, NULL);
+
+ for (i = 0; i < length; i++)
+ attr->values[i].string.text = g_strdup (values[i]);
+ }
+
+ if (newppdfile) {
+ retval = _cph_cups_post_request (cups, request, newppdfile, CPH_RESOURCE_ADMIN);
+ g_unlink (newppdfile);
+ g_free (newppdfile);
+ } else {
+ retval = _cph_cups_send_request (cups, request, CPH_RESOURCE_ADMIN);
+ }
+
+ return retval;
+}
+
/* Functions that work on jobs */
gboolean
diff --git a/src/cups.h b/src/cups.h
index a555299..2832533 100644
--- a/src/cups.h
+++ b/src/cups.h
@@ -173,6 +173,11 @@ gboolean cph_cups_printer_class_set_option_default (CphCups *cups,
const char *option,
const char *const *values);
+gboolean cph_cups_printer_class_set_option (CphCups *cups,
+ const char *printer_name,
+ const char *option,
+ const char *const *values);
+
gboolean cph_cups_job_cancel (CphCups *cups,
int job_id,
gboolean purge_job,