gtk2/print_authentication.patch

1390 lines
47 KiB
Diff

Index: gtk/gtkmarshalers.list
===================================================================
--- gtk/gtkmarshalers.list (revision 22586)
+++ gtk/gtkmarshalers.list (working copy)
@@ -97,6 +97,7 @@
VOID:STRING
VOID:STRING,BOXED
VOID:STRING,STRING
+VOID:STRING,STRING,STRING
VOID:STRING,INT,POINTER
VOID:STRING,UINT,FLAGS
VOID:STRING,UINT,FLAGS,UINT
Index: gtk/gtkprintbackend.c
===================================================================
--- gtk/gtkprintbackend.c (revision 22586)
+++ gtk/gtkprintbackend.c (working copy)
@@ -25,6 +25,7 @@
#include "gtkintl.h"
#include "gtkmodules.h"
+#include "gtkmarshalers.h"
#include "gtkprivate.h"
#include "gtkprintbackend.h"
#include "gtkprinter-private.h"
@@ -49,6 +50,9 @@
guint printer_list_requested : 1;
guint printer_list_done : 1;
GtkPrintBackendStatus status;
+ char *hostname;
+ char *username;
+ char *password;
};
enum {
@@ -57,6 +61,7 @@
PRINTER_ADDED,
PRINTER_REMOVED,
PRINTER_STATUS_CHANGED,
+ REQUEST_PASSWORD,
LAST_SIGNAL
};
@@ -353,6 +358,10 @@
static GList * fallback_printer_list_papers (GtkPrinter *printer);
static GtkPageSetup * fallback_printer_get_default_page_size (GtkPrinter *printer);
static GtkPrintCapabilities fallback_printer_get_capabilities (GtkPrinter *printer);
+static void request_password (GtkPrintBackend *backend,
+ const gchar *hostname,
+ const gchar *username,
+ const gchar *prompt);
static void
gtk_print_backend_class_init (GtkPrintBackendClass *class)
@@ -372,6 +381,7 @@
class->printer_list_papers = fallback_printer_list_papers;
class->printer_get_default_page_size = fallback_printer_get_default_page_size;
class->printer_get_capabilities = fallback_printer_get_capabilities;
+ class->request_password = request_password;
g_object_class_install_property (object_class,
PROP_STATUS,
@@ -425,6 +435,14 @@
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GTK_TYPE_PRINTER);
+ signals[REQUEST_PASSWORD] =
+ g_signal_new (I_("request-password"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkPrintBackendClass, request_password),
+ NULL, NULL,
+ _gtk_marshal_VOID__STRING_STRING_STRING,
+ G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
}
static void
@@ -437,6 +455,9 @@
priv->printers = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_object_unref);
+ priv->hostname = NULL;
+ priv->username = NULL;
+ priv->password = NULL;
}
static void
@@ -640,6 +661,167 @@
dnotify);
}
+void
+gtk_print_backend_set_password (GtkPrintBackend *backend,
+ const gchar *hostname,
+ const gchar *username,
+ const gchar *password)
+{
+ g_return_if_fail (GTK_IS_PRINT_BACKEND (backend));
+
+ if (GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password)
+ GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password (backend, hostname, username, password);
+}
+
+static void
+store_password (GtkEntry *entry,
+ GtkPrintBackend *backend)
+{
+ GtkPrintBackendPrivate *priv = backend->priv;
+
+ if (priv->password != NULL)
+ {
+ memset (priv->password, 0, strlen (priv->password));
+ g_free (priv->password);
+ }
+
+ priv->password = g_strdup (gtk_entry_get_text (entry));
+}
+
+static void
+store_username (GtkEntry *entry,
+ GtkPrintBackend *backend)
+{
+ GtkPrintBackendPrivate *priv = backend->priv;
+
+ g_free (priv->username);
+ priv->username = g_strdup (gtk_entry_get_text (entry));
+}
+
+static void
+password_dialog_response (GtkWidget *dialog,
+ gint response_id,
+ GtkPrintBackend *backend)
+{
+ GtkPrintBackendPrivate *priv = backend->priv;
+
+ if (response_id == GTK_RESPONSE_OK)
+ gtk_print_backend_set_password (backend, priv->hostname, priv->username, priv->password);
+ else
+ gtk_print_backend_set_password (backend, priv->hostname, priv->username, NULL);
+
+ if (priv->password != NULL)
+ {
+ memset (priv->password, 0, strlen (priv->password));
+ g_free (priv->password);
+ priv->password = NULL;
+ }
+
+ g_free (priv->username);
+ priv->username = NULL;
+
+ gtk_widget_destroy (dialog);
+
+ g_object_unref (backend);
+}
+
+static void
+request_password (GtkPrintBackend *backend,
+ const gchar *hostname,
+ const gchar *username,
+ const gchar *prompt)
+{
+ GtkPrintBackendPrivate *priv = backend->priv;
+ GtkWidget *dialog, *username_box, *password_box, *main_box, *label, *icon, *vbox,
+ *password_prompt, *username_prompt,
+ *password_entry, *username_entry;
+ gchar *markup;
+
+ dialog = gtk_dialog_new_with_buttons ( _("Authentication"), NULL, GTK_DIALOG_MODAL,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+
+ main_box = gtk_hbox_new (FALSE, 0);
+
+ /* Left */
+ icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
+ gtk_misc_set_padding (GTK_MISC (icon), 6, 6);
+
+
+ /* Right */
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_size_request (GTK_WIDGET (vbox), 320, -1);
+
+ /* Right - 1. */
+ label = gtk_label_new (NULL);
+ markup = g_markup_printf_escaped ("<span weight=\"bold\" size=\"large\">%s</span>", prompt);
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_widget_set_size_request (GTK_WIDGET (label), 320, -1);
+ g_free (markup);
+
+
+ /* Right - 2. */
+ username_box = gtk_hbox_new (TRUE, 0);
+
+ username_prompt = gtk_label_new (_("Username:"));
+ gtk_misc_set_alignment (GTK_MISC (username_prompt), 0.0, 0.5);
+
+ username_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (username_entry), username);
+
+
+ /* Right - 3. */
+ password_box = gtk_hbox_new (TRUE, 0);
+
+ password_prompt = gtk_label_new (_("Password:"));
+ gtk_misc_set_alignment (GTK_MISC (password_prompt), 0.0, 0.5);
+
+ password_entry = gtk_entry_new ();
+ gtk_entry_set_visibility (GTK_ENTRY (password_entry), FALSE);
+ gtk_entry_set_activates_default (GTK_ENTRY (password_entry), TRUE);
+
+
+ /* Packing */
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), main_box, TRUE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (main_box), icon, FALSE, FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (main_box), vbox, FALSE, FALSE, 6);
+
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), username_box, FALSE, TRUE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), password_box, FALSE, TRUE, 6);
+
+ gtk_box_pack_start (GTK_BOX (username_box), username_prompt, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (username_box), username_entry, TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (password_box), password_prompt, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (password_box), password_entry, TRUE, TRUE, 0);
+
+
+ gtk_widget_grab_focus (password_entry);
+
+ priv->hostname = g_strdup (hostname);
+ priv->username = g_strdup (username);
+
+ g_signal_connect (password_entry, "changed",
+ G_CALLBACK (store_password), backend);
+
+ g_signal_connect (username_entry, "changed",
+ G_CALLBACK (store_username), backend);
+
+ g_object_ref (backend);
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (password_dialog_response), backend);
+
+ gtk_widget_show_all (dialog);
+}
+
void
gtk_print_backend_destroy (GtkPrintBackend *print_backend)
{
Index: gtk/gtkprintbackend.h
===================================================================
--- gtk/gtkprintbackend.h (revision 22586)
+++ gtk/gtkprintbackend.h (working copy)
@@ -120,14 +120,22 @@
GtkPrinter *printer);
void (*printer_status_changed) (GtkPrintBackend *backend,
GtkPrinter *printer);
+ void (*request_password) (GtkPrintBackend *backend,
+ const gchar *hostname,
+ const gchar *username,
+ const gchar *prompt);
+ /* not a signal */
+ void (*set_password) (GtkPrintBackend *backend,
+ const gchar *hostname,
+ const gchar *username,
+ const gchar *password);
+
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
void (*_gtk_reserved4) (void);
- void (*_gtk_reserved5) (void);
- void (*_gtk_reserved6) (void);
};
GType gtk_print_backend_get_type (void) G_GNUC_CONST;
@@ -144,6 +152,10 @@
GDestroyNotify dnotify);
GList * gtk_print_backend_load_modules (void);
void gtk_print_backend_destroy (GtkPrintBackend *print_backend);
+void gtk_print_backend_set_password (GtkPrintBackend *backend,
+ const gchar *hostname,
+ const gchar *username,
+ const gchar *password);
/* Backend-only functions for GtkPrintBackend */
Index: gtk/gtkprintunixdialog.c
===================================================================
--- gtk/gtkprintunixdialog.c (revision 22586)
+++ gtk/gtkprintunixdialog.c (working copy)
@@ -757,7 +757,10 @@
priv->print_backends = gtk_print_backend_load_modules ();
for (node = priv->print_backends; node != NULL; node = node->next)
- printer_list_initialize (dialog, GTK_PRINT_BACKEND (node->data));
+ {
+ GtkPrintBackend *backend = node->data;
+ printer_list_initialize (dialog, backend);
+ }
}
static void
Index: modules/printbackends/cups/gtkcupsutils.c
===================================================================
--- modules/printbackends/cups/gtkcupsutils.c (revision 22586)
+++ modules/printbackends/cups/gtkcupsutils.c (working copy)
@@ -39,10 +39,12 @@
static void _post_write_request (GtkCupsRequest *request);
static void _post_write_data (GtkCupsRequest *request);
static void _post_check (GtkCupsRequest *request);
+static void _post_auth (GtkCupsRequest *request);
static void _post_read_response (GtkCupsRequest *request);
static void _get_send (GtkCupsRequest *request);
static void _get_check (GtkCupsRequest *request);
+static void _get_auth (GtkCupsRequest *request);
static void _get_read_data (GtkCupsRequest *request);
struct _GtkCupsResult
@@ -69,6 +71,7 @@
_post_write_request,
_post_write_data,
_post_check,
+ _post_auth,
_post_read_response
};
@@ -76,6 +79,7 @@
_connect,
_get_send,
_get_check,
+ _get_auth,
_get_read_data
};
@@ -101,12 +105,13 @@
}
GtkCupsRequest *
-gtk_cups_request_new (http_t *connection,
- GtkCupsRequestType req_type,
- gint operation_id,
- GIOChannel *data_io,
- const char *server,
- const char *resource)
+gtk_cups_request_new_with_username (http_t *connection,
+ GtkCupsRequestType req_type,
+ gint operation_id,
+ GIOChannel *data_io,
+ const char *server,
+ const char *resource,
+ const char *username)
{
GtkCupsRequest *request;
cups_lang_t *language;
@@ -123,6 +128,8 @@
request->type = req_type;
request->state = GTK_CUPS_REQUEST_START;
+ request->password_state = GTK_CUPS_PASSWORD_NONE;
+
if (server)
request->server = g_strdup (server);
else
@@ -171,15 +178,37 @@
"attributes-natural-language",
NULL, language->language);
- gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
- "requesting-user-name",
- NULL, cupsUser ());
-
+ if (username != NULL)
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name",
+ NULL, username);
+ else
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name",
+ NULL, cupsUser ());
+
cupsLangFree (language);
return request;
}
+GtkCupsRequest *
+gtk_cups_request_new (http_t *connection,
+ GtkCupsRequestType req_type,
+ gint operation_id,
+ GIOChannel *data_io,
+ const char *server,
+ const char *resource)
+{
+ return gtk_cups_request_new_with_username (connection,
+ req_type,
+ operation_id,
+ data_io,
+ server,
+ resource,
+ NULL);
+}
+
static void
gtk_cups_result_free (GtkCupsResult *result)
{
@@ -205,7 +234,14 @@
g_free (request->server);
g_free (request->resource);
+ if (request->password != NULL)
+ {
+ memset (request->password, 0, strlen (request->password));
+ g_free (request->password);
+ }
+ g_free (request->username);
+
gtk_cups_result_free (request->result);
g_free (request);
@@ -290,8 +326,25 @@
values);
}
+const char *
+gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
+ ipp_tag_t tag,
+ const char *name)
+{
+ ipp_attribute_t *attribute = NULL;
+ if (request != NULL && request->ipp_request != NULL)
+ attribute = ippFindAttribute (request->ipp_request,
+ name,
+ tag);
+ if (attribute != NULL && attribute->values != NULL)
+ return attribute->values[0].string.text;
+ else
+ return NULL;
+}
+
+
typedef struct
{
const char *name;
@@ -786,12 +839,83 @@
return;
}
}
- else
+ else if (http_status == HTTP_UNAUTHORIZED)
{
+ request->state = GTK_CUPS_POST_CHECK;
+ request->poll_state = GTK_CUPS_HTTP_READ;
+
+ request->attempts = 0;
+ return;
+ }
+ else
+ {
request->attempts++;
}
}
+static void
+_post_auth (GtkCupsRequest *request)
+{
+ if (request->password_state == GTK_CUPS_PASSWORD_HAS)
+ {
+ if (request->password == NULL)
+ {
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result,
+ GTK_CUPS_ERROR_AUTH,
+ 0,
+ 1,
+ "Canceled by user");
+ }
+ else
+ request->state = GTK_CUPS_POST_CHECK;
+ }
+}
+
+static void
+_get_auth (GtkCupsRequest *request)
+{
+ if (request->password_state == GTK_CUPS_PASSWORD_HAS)
+ {
+ if (request->password == NULL)
+ {
+ request->state = GTK_CUPS_GET_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result,
+ GTK_CUPS_ERROR_AUTH,
+ 0,
+ 1,
+ "Canceled by user");
+ }
+ else
+ request->state = GTK_CUPS_GET_CHECK;
+ }
+}
+
+/* Very ugly hack: cups has a stupid synchronous password callback
+ * that doesn't even take the request or user data parameters, so
+ * we have to use a static variable to pass the password to it.
+ * Not threadsafe !
+ * The callback sets cups_password to NULL to signal that the
+ * password has been used.
+ */
+static char *cups_password;
+static char *cups_username;
+
+static const char *
+passwordCB (const char *prompt)
+{
+ char *pwd = cups_password;
+ cups_password = NULL;
+
+ cupsSetUser (cups_username);
+
+ return pwd;
+}
+
static void
_post_check (GtkCupsRequest *request)
{
@@ -810,18 +934,91 @@
}
else if (http_status == HTTP_UNAUTHORIZED)
{
- /* TODO: callout for auth */
- g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
- request->state = GTK_CUPS_POST_DONE;
- request->poll_state = GTK_CUPS_HTTP_IDLE;
+ int auth_result = -1;
+ httpFlush (request->http);
+
+ if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
+ {
+ request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
+ request->state = GTK_CUPS_POST_AUTH;
+ request->need_password = TRUE;
+
+ return;
+ }
+
+ /* Negotiate */
+ if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
+ {
+ auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+ }
+ /* Basic, BasicDigest, Digest and PeerCred */
+ else
+ {
+ if (request->password_state == GTK_CUPS_PASSWORD_NONE)
+ {
+ cups_password = g_strdup ("");
+ cups_username = request->username;
+ cupsSetPasswordCB (passwordCB);
+
+ /* This call success for PeerCred authentication */
+ auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+
+ if (auth_result != 0)
+ {
+ /* move to AUTH state to let the backend
+ * ask for a password
+ */
+ request->state = GTK_CUPS_POST_AUTH;
+ request->need_password = TRUE;
+
+ return;
+ }
+ }
+ else
+ {
+ cups_password = request->password;
+ cups_username = request->username;
+
+ auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+
+ if (cups_password != NULL)
+ return;
+
+ if (request->password != NULL)
+ {
+ memset (request->password, 0, strlen (request->password));
+ g_free (request->password);
+ request->password = NULL;
+ }
+
+ request->password_state = GTK_CUPS_PASSWORD_APPLIED;
+ }
+ }
+
+ if (auth_result ||
+ httpReconnect (request->http))
+ {
+ /* if the password has been used, reset password_state
+ * so that we ask for a new one next time around
+ */
+ if (cups_password == NULL)
+ request->password_state = GTK_CUPS_PASSWORD_NONE;
+
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+ gtk_cups_result_set_error (request->result,
+ GTK_CUPS_ERROR_AUTH,
+ 0,
+ 0,
+ "Not authorized");
+ return;
+ }
- /* TODO: create a not implemented error code */
- gtk_cups_result_set_error (request->result,
- GTK_CUPS_ERROR_GENERAL,
- 0,
- 0,
- "Can't prompt for authorization");
- return;
+ if (request->data_io != NULL)
+ g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
+
+ request->state = GTK_CUPS_POST_CONNECT;
+ request->poll_state = GTK_CUPS_HTTP_WRITE;
}
else if (http_status == HTTP_ERROR)
{
@@ -883,7 +1080,7 @@
http_errno,
"HTTP Error in POST %s",
g_strerror (http_errno));
- request->poll_state = GTK_CUPS_HTTP_IDLE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
httpFlush (request->http);
return;
@@ -975,9 +1172,13 @@
}
httpClearFields (request->http);
+#ifdef HAVE_HTTPGETAUTHSTRING
+ httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
+#else
#ifdef HAVE_HTTP_AUTHSTRING
httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
#endif
+#endif
if (httpGet (request->http, request->resource))
{
@@ -997,6 +1198,9 @@
request->attempts++;
return;
}
+
+ if (httpCheck (request->http))
+ request->last_status = httpUpdate (request->http);
request->attempts = 0;
@@ -1024,18 +1228,90 @@
}
else if (http_status == HTTP_UNAUTHORIZED)
{
- /* TODO: callout for auth */
- g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
- request->state = GTK_CUPS_GET_DONE;
- request->poll_state = GTK_CUPS_HTTP_IDLE;
+ int auth_result = -1;
+ httpFlush (request->http);
- /* TODO: should add a status or error code for not implemented */
- gtk_cups_result_set_error (request->result,
- GTK_CUPS_ERROR_GENERAL,
- 0,
- 0,
- "Can't prompt for authorization");
- return;
+ if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
+ {
+ request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
+ request->state = GTK_CUPS_GET_AUTH;
+ request->need_password = TRUE;
+
+ return;
+ }
+
+ /* Negotiate */
+ if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
+ {
+ auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+ }
+ /* Basic, BasicDigest, Digest and PeerCred */
+ else
+ {
+ if (request->password_state == GTK_CUPS_PASSWORD_NONE)
+ {
+ cups_password = g_strdup ("");
+ cups_username = request->username;
+ cupsSetPasswordCB (passwordCB);
+
+ /* This call success for PeerCred authentication */
+ auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+
+ if (auth_result != 0)
+ {
+ /* move to AUTH state to let the backend
+ * ask for a password
+ */
+ request->state = GTK_CUPS_GET_AUTH;
+ request->need_password = TRUE;
+
+ return;
+ }
+ }
+ else
+ {
+ cups_password = request->password;
+ cups_username = request->username;
+
+ auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+
+ if (cups_password != NULL)
+ return;
+
+ if (request->password != NULL)
+ {
+ memset (request->password, 0, strlen (request->password));
+ g_free (request->password);
+ request->password = NULL;
+ }
+
+ request->password_state = GTK_CUPS_PASSWORD_APPLIED;
+ }
+ }
+
+ if (auth_result ||
+ httpReconnect (request->http))
+ {
+ /* if the password has been used, reset password_state
+ * so that we ask for a new one next time around
+ */
+ if (cups_password == NULL)
+ request->password_state = GTK_CUPS_PASSWORD_NONE;
+
+ request->state = GTK_CUPS_GET_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+ gtk_cups_result_set_error (request->result,
+ GTK_CUPS_ERROR_AUTH,
+ 0,
+ 0,
+ "Not authorized");
+ return;
+ }
+
+ request->state = GTK_CUPS_GET_SEND;
+ request->last_status = HTTP_CONTINUE;
+
+ return;
}
else if (http_status == HTTP_UPGRADE_REQUIRED)
{
@@ -1043,7 +1319,7 @@
httpFlush (request->http);
cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
- request->state = GTK_CUPS_POST_CONNECT;
+ request->state = GTK_CUPS_GET_CONNECT;
/* Reconnect... */
httpReconnect (request->http);
@@ -1143,7 +1419,7 @@
if (io_status == G_IO_STATUS_ERROR)
{
- request->state = GTK_CUPS_POST_DONE;
+ request->state = GTK_CUPS_GET_DONE;
request->poll_state = GTK_CUPS_HTTP_IDLE;
gtk_cups_result_set_error (request->result,
Index: modules/printbackends/cups/gtkprintbackendcups.c
===================================================================
--- modules/printbackends/cups/gtkprintbackendcups.c (revision 22586)
+++ modules/printbackends/cups/gtkprintbackendcups.c (working copy)
@@ -118,6 +118,11 @@
char *default_cover_before;
char *default_cover_after;
int number_of_covers;
+
+ GList *requests;
+ GHashTable *auth;
+ gchar *username;
+ gboolean authentication_lock;
};
static GObjectClass *backend_parent_class;
@@ -176,7 +181,14 @@
gdouble height,
GIOChannel *cache_io);
+static void gtk_print_backend_cups_set_password (GtkPrintBackend *backend,
+ const gchar *hostname,
+ const gchar *username,
+ const gchar *password);
+void overwrite_and_free (gpointer data);
+static gboolean is_address_local (const gchar *address);
+
static void
gtk_print_backend_cups_register_type (GTypeModule *module)
{
@@ -271,6 +283,7 @@
backend_class->printer_get_default_page_size = cups_printer_get_default_page_size;
backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
backend_class->printer_get_capabilities = cups_printer_get_capabilities;
+ backend_class->set_password = gtk_print_backend_cups_set_password;
}
static cairo_status_t
@@ -511,12 +524,13 @@
cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
settings = gtk_print_job_get_settings (job);
- request = gtk_cups_request_new (NULL,
- GTK_CUPS_POST,
- IPP_PRINT_JOB,
- data_io,
- NULL,
- cups_printer->device_uri);
+ request = gtk_cups_request_new_with_username (NULL,
+ GTK_CUPS_POST,
+ IPP_PRINT_JOB,
+ data_io,
+ NULL,
+ cups_printer->device_uri,
+ GTK_PRINT_BACKEND_CUPS (print_backend)->username);
#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
httpAssembleURIf (HTTP_URI_CODING_ALL,
@@ -561,7 +575,17 @@
(GDestroyNotify)cups_free_print_stream_data);
}
+void overwrite_and_free (gpointer data)
+{
+ gchar *password = (gchar *) data;
+ if (password != NULL)
+ {
+ memset (password, 0, strlen (password));
+ g_free (password);
+ }
+}
+
static void
gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
{
@@ -569,6 +593,10 @@
backend_cups->got_default_printer = FALSE;
backend_cups->list_printers_pending = FALSE;
+ backend_cups->requests = NULL;
+ backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free);
+ backend_cups->authentication_lock = FALSE;
+
backend_cups->covers = NULL;
backend_cups->default_cover_before = NULL;
backend_cups->default_cover_after = NULL;
@@ -577,6 +605,8 @@
backend_cups->default_printer_poll = 0;
backend_cups->cups_connection_test = NULL;
+ backend_cups->username = NULL;
+
cups_get_local_default_printer (backend_cups);
}
@@ -602,6 +632,10 @@
gtk_cups_connection_test_free (backend_cups->cups_connection_test);
backend_cups->cups_connection_test = NULL;
+ g_hash_table_destroy (backend_cups->auth);
+
+ g_free (backend_cups->username);
+
backend_parent_class->finalize (object);
}
@@ -626,8 +660,157 @@
backend_parent_class->dispose (object);
}
+static gboolean
+is_address_local (const gchar *address)
+{
+ if (address[0] == '/' ||
+ strcmp (address, "127.0.0.1") == 0 ||
+ strcmp (address, "[::1]") == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+static void
+gtk_print_backend_cups_set_password (GtkPrintBackend *backend,
+ const gchar *hostname,
+ const gchar *username,
+ const gchar *password)
+{
+ GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
+ GList *l;
+ char dispatch_hostname[HTTP_MAX_URI];
+ gchar *key;
+
+ key = g_strconcat (username, "@", hostname, NULL);
+ g_hash_table_insert (cups_backend->auth, key, g_strdup (password));
+
+ g_free (cups_backend->username);
+ cups_backend->username = g_strdup (username);
+
+ GTK_NOTE (PRINTING,
+ g_print ("CUPS backend: storing password for %s\n", key));
+
+ for (l = cups_backend->requests; l; l = l->next)
+ {
+ GtkPrintCupsDispatchWatch *dispatch = l->data;
+
+ httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname));
+ if (is_address_local (dispatch_hostname))
+ strcpy (dispatch_hostname, "localhost");
+
+ if (strcmp (hostname, dispatch_hostname) == 0)
+ {
+ overwrite_and_free (dispatch->request->password);
+ dispatch->request->password = g_strdup (password);
+ g_free (dispatch->request->username);
+ dispatch->request->username = g_strdup (username);
+ dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
+ dispatch->backend->authentication_lock = FALSE;
+ }
+ }
+}
+
static gboolean
+request_password (gpointer data)
+{
+ GtkPrintCupsDispatchWatch *dispatch = data;
+ const gchar *username;
+ gchar *password;
+ gchar *prompt = NULL;
+ gchar *key = NULL;
+ char hostname[HTTP_MAX_URI];
+
+ if (dispatch->backend->authentication_lock)
+ return FALSE;
+
+ httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
+ if (is_address_local (hostname))
+ strcpy (hostname, "localhost");
+
+ if (dispatch->backend->username != NULL)
+ username = dispatch->backend->username;
+ else
+ username = cupsUser ();
+
+ key = g_strconcat (username, "@", hostname, NULL);
+ password = g_hash_table_lookup (dispatch->backend->auth, key);
+
+ if (password && dispatch->request->password_state != GTK_CUPS_PASSWORD_NOT_VALID)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("CUPS backend: using stored password for %s\n", key));
+
+ overwrite_and_free (dispatch->request->password);
+ dispatch->request->password = g_strdup (password);
+ g_free (dispatch->request->username);
+ dispatch->request->username = g_strdup (username);
+ dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
+ }
+ else
+ {
+ const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
+ const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
+ char *printer_name = NULL;
+
+ if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
+ printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
+
+ if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID)
+ g_hash_table_remove (dispatch->backend->auth, key);
+
+ dispatch->request->password_state = GTK_CUPS_PASSWORD_REQUESTED;
+
+ dispatch->backend->authentication_lock = TRUE;
+
+ switch (dispatch->request->ipp_request->request.op.operation_id)
+ {
+ case 0:
+ prompt = g_strdup_printf ( _("Authentication is required to get a file from %s"), hostname);
+ break;
+ case IPP_PRINT_JOB:
+ if (job_title != NULL && printer_name != NULL)
+ prompt = g_strdup_printf ( _("Authentication is required to print document '%s' on printer %s"), job_title, printer_name);
+ else
+ prompt = g_strdup_printf ( _("Authentication is required to print a document on %s"), hostname);
+ break;
+ case IPP_GET_JOB_ATTRIBUTES:
+ if (job_title != NULL)
+ prompt = g_strdup_printf ( _("Authentication is required to get attributes of job '%s'"), job_title);
+ else
+ prompt = g_strdup ( _("Authentication is required to get attributes of a job"));
+ break;
+ case IPP_GET_PRINTER_ATTRIBUTES:
+ if (printer_name != NULL)
+ prompt = g_strdup_printf ( _("Authentication is required to get attributes of printer %s"), printer_name);
+ else
+ prompt = g_strdup ( _("Authentication is required to get attributes of a printer"));
+ break;
+ case CUPS_GET_DEFAULT:
+ prompt = g_strdup_printf ( _("Authentication is required to get default printer of %s"), hostname);
+ break;
+ case CUPS_GET_PRINTERS:
+ prompt = g_strdup_printf ( _("Authentication is required to get printers from %s"), hostname);
+ break;
+ default:
+ prompt = g_strdup_printf ( _("Authentication is required on %s"), hostname);
+ break;
+ }
+
+ g_free (printer_name);
+
+ g_signal_emit_by_name (dispatch->backend, "request-password",
+ hostname, username, prompt);
+
+ g_free (prompt);
+ }
+
+ g_free (key);
+
+ return FALSE;
+}
+
+static gboolean
cups_dispatch_watch_check (GSource *source)
{
GtkPrintCupsDispatchWatch *dispatch;
@@ -665,7 +848,7 @@
#endif
}
- if (poll_state != GTK_CUPS_HTTP_IDLE)
+ if (poll_state != GTK_CUPS_HTTP_IDLE && !dispatch->request->need_password)
if (!(dispatch->data_poll->revents & dispatch->data_poll->events))
return FALSE;
@@ -676,6 +859,13 @@
g_free (dispatch->data_poll);
dispatch->data_poll = NULL;
}
+
+ if (dispatch->request->need_password && dispatch->request->password_state != GTK_CUPS_PASSWORD_REQUESTED)
+ {
+ dispatch->request->need_password = FALSE;
+ g_idle_add (request_password, dispatch);
+ result = FALSE;
+ }
return result;
}
@@ -735,12 +925,39 @@
cups_dispatch_watch_finalize (GSource *source)
{
GtkPrintCupsDispatchWatch *dispatch;
+ GtkCupsResult *result;
GTK_NOTE (PRINTING,
g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
dispatch = (GtkPrintCupsDispatchWatch *) source;
+ result = gtk_cups_request_get_result (dispatch->request);
+ if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH)
+ {
+ const gchar *username;
+ gchar hostname[HTTP_MAX_URI];
+ gchar *key;
+
+ httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
+ if (is_address_local (hostname))
+ strcpy (hostname, "localhost");
+
+ if (dispatch->backend->username != NULL)
+ username = dispatch->backend->username;
+ else
+ username = cupsUser ();
+
+ key = g_strconcat (username, "@", hostname, NULL);
+ GTK_NOTE (PRINTING,
+ g_print ("CUPS backend: removing stored password for %s\n", key));
+ g_hash_table_remove (dispatch->backend->auth, key);
+ g_free (key);
+
+ if (dispatch->backend)
+ dispatch->backend->authentication_lock = FALSE;
+ }
+
gtk_cups_request_free (dispatch->request);
if (dispatch->backend)
@@ -754,6 +971,10 @@
* of print backends. See _gtk_print_backend_create for the
* disabling.
*/
+
+ dispatch->backend->requests = g_list_remove (dispatch->backend->requests, dispatch);
+
+
g_object_unref (dispatch->backend);
dispatch->backend = NULL;
}
@@ -788,6 +1009,8 @@
dispatch->backend = g_object_ref (print_backend);
dispatch->data_poll = NULL;
+ print_backend->requests = g_list_prepend (print_backend->requests, dispatch);
+
g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
g_source_attach ((GSource *) dispatch, NULL);
@@ -890,12 +1113,13 @@
"job-sheets-default"
};
- request = gtk_cups_request_new (NULL,
- GTK_CUPS_POST,
- IPP_GET_PRINTER_ATTRIBUTES,
- NULL,
- NULL,
- NULL);
+ request = gtk_cups_request_new_with_username (NULL,
+ GTK_CUPS_POST,
+ IPP_GET_PRINTER_ATTRIBUTES,
+ NULL,
+ NULL,
+ NULL,
+ print_backend->username);
printer_uri = g_strdup_printf ("ipp://localhost/printers/%s",
printer_name);
@@ -1029,12 +1253,13 @@
GtkCupsRequest *request;
gchar *job_uri;
- request = gtk_cups_request_new (NULL,
- GTK_CUPS_POST,
- IPP_GET_JOB_ATTRIBUTES,
- NULL,
- NULL,
- NULL);
+ request = gtk_cups_request_new_with_username (NULL,
+ GTK_CUPS_POST,
+ IPP_GET_JOB_ATTRIBUTES,
+ NULL,
+ NULL,
+ NULL,
+ data->print_backend->username);
job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
@@ -1121,9 +1346,20 @@
if (gtk_cups_result_is_error (result))
{
GTK_NOTE (PRINTING,
- g_warning ("CUPS Backend: Error getting printer list: %s",
- gtk_cups_result_get_error_string (result)));
+ g_warning ("CUPS Backend: Error getting printer list: %s %d %d",
+ gtk_cups_result_get_error_string (result),
+ gtk_cups_result_get_error_type (result),
+ gtk_cups_result_get_error_code (result)));
+ if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
+ gtk_cups_result_get_error_code (result) == 1)
+ {
+ /* Canceled by user, stop popping up more password dialogs */
+ if (cups_backend->list_printers_poll > 0)
+ g_source_remove (cups_backend->list_printers_poll);
+ cups_backend->list_printers_poll = 0;
+ }
+
goto done;
}
@@ -1609,12 +1845,13 @@
cups_backend->list_printers_pending = TRUE;
- request = gtk_cups_request_new (NULL,
- GTK_CUPS_POST,
- CUPS_GET_PRINTERS,
- NULL,
- NULL,
- NULL);
+ request = gtk_cups_request_new_with_username (NULL,
+ GTK_CUPS_POST,
+ CUPS_GET_PRINTERS,
+ NULL,
+ NULL,
+ NULL,
+ cups_backend->username);
gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", G_N_ELEMENTS (pattrs),
@@ -1776,28 +2013,30 @@
resource = g_strdup_printf ("/printers/%s.ppd",
gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
- request = gtk_cups_request_new (data->http,
- GTK_CUPS_GET,
- 0,
- data->ppd_io,
- cups_printer->hostname,
- resource);
+ print_backend = gtk_printer_get_backend (printer);
+ request = gtk_cups_request_new_with_username (data->http,
+ GTK_CUPS_GET,
+ 0,
+ data->ppd_io,
+ cups_printer->hostname,
+ resource,
+ GTK_PRINT_BACKEND_CUPS (print_backend)->username);
+
GTK_NOTE (PRINTING,
g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
- g_free (resource);
- g_free (ppd_filename);
cups_printer->reading_ppd = TRUE;
- print_backend = gtk_printer_get_backend (printer);
-
cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
request,
(GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
data,
(GDestroyNotify)get_ppd_data_free);
+
+ g_free (resource);
+ g_free (ppd_filename);
}
/* Ordering matters for default preference */
@@ -2018,6 +2257,20 @@
GDK_THREADS_ENTER ();
+ if (gtk_cups_result_is_error (result))
+ {
+ if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
+ gtk_cups_result_get_error_code (result) == 1)
+ {
+ /* Canceled by user, stop popping up more password dialogs */
+ if (print_backend->list_printers_poll > 0)
+ g_source_remove (print_backend->list_printers_poll);
+ print_backend->list_printers_poll = 0;
+ }
+
+ return;
+ }
+
response = gtk_cups_result_get_response (result);
if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
@@ -2056,12 +2309,13 @@
if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
return TRUE;
- request = gtk_cups_request_new (NULL,
- GTK_CUPS_POST,
- CUPS_GET_DEFAULT,
- NULL,
- NULL,
- NULL);
+ request = gtk_cups_request_new_with_username (NULL,
+ GTK_CUPS_POST,
+ CUPS_GET_DEFAULT,
+ NULL,
+ NULL,
+ NULL,
+ print_backend->username);
cups_request_execute (print_backend,
request,
Index: modules/printbackends/cups/gtkcupsutils.h
===================================================================
--- modules/printbackends/cups/gtkcupsutils.h (revision 22586)
+++ modules/printbackends/cups/gtkcupsutils.h (working copy)
@@ -37,6 +37,7 @@
GTK_CUPS_ERROR_HTTP,
GTK_CUPS_ERROR_IPP,
GTK_CUPS_ERROR_IO,
+ GTK_CUPS_ERROR_AUTH,
GTK_CUPS_ERROR_GENERAL
} GtkCupsErrorType;
@@ -66,6 +67,15 @@
GTK_CUPS_CONNECTION_IN_PROGRESS
} GtkCupsConnectionState;
+typedef enum
+{
+ GTK_CUPS_PASSWORD_NONE,
+ GTK_CUPS_PASSWORD_REQUESTED,
+ GTK_CUPS_PASSWORD_HAS,
+ GTK_CUPS_PASSWORD_APPLIED,
+ GTK_CUPS_PASSWORD_NOT_VALID
+} GtkCupsPasswordState;
+
struct _GtkCupsRequest
{
GtkCupsRequestType type;
@@ -84,7 +94,12 @@
gint state;
GtkCupsPollState poll_state;
- gint own_http : 1;
+ gchar *password;
+ gchar *username;
+
+ gint own_http : 1;
+ gint need_password : 1;
+ GtkCupsPasswordState password_state;
};
struct _GtkCupsConnectionTest
@@ -108,6 +123,7 @@
GTK_CUPS_POST_WRITE_REQUEST,
GTK_CUPS_POST_WRITE_DATA,
GTK_CUPS_POST_CHECK,
+ GTK_CUPS_POST_AUTH,
GTK_CUPS_POST_READ_RESPONSE,
GTK_CUPS_POST_DONE = GTK_CUPS_REQUEST_DONE
};
@@ -118,10 +134,18 @@
GTK_CUPS_GET_CONNECT = GTK_CUPS_REQUEST_START,
GTK_CUPS_GET_SEND,
GTK_CUPS_GET_CHECK,
+ GTK_CUPS_GET_AUTH,
GTK_CUPS_GET_READ_DATA,
GTK_CUPS_GET_DONE = GTK_CUPS_REQUEST_DONE
};
+GtkCupsRequest * gtk_cups_request_new_with_username (http_t *connection,
+ GtkCupsRequestType req_type,
+ gint operation_id,
+ GIOChannel *data_io,
+ const char *server,
+ const char *resource,
+ const char *username);
GtkCupsRequest * gtk_cups_request_new (http_t *connection,
GtkCupsRequestType req_type,
gint operation_id,
@@ -141,6 +165,9 @@
int num_values,
const char *charset,
const char * const *values);
+const char * gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
+ ipp_tag_t tag,
+ const char *name);
gboolean gtk_cups_request_read_write (GtkCupsRequest *request);
GtkCupsPollState gtk_cups_request_get_poll_state (GtkCupsRequest *request);
void gtk_cups_request_free (GtkCupsRequest *request);