From 1fabce9c17628ba69b6d85d8216506655a8595da Mon Sep 17 00:00:00 2001 From: mkasik Date: Tue, 7 Apr 2009 15:00:44 +0000 Subject: [PATCH] - Add authentication support to GtkPrintBackend. --- gtk2.spec | 12 +- print_authentication.patch | 1389 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1400 insertions(+), 1 deletion(-) create mode 100644 print_authentication.patch diff --git a/gtk2.spec b/gtk2.spec index 2b7b25a..7eb8127 100644 --- a/gtk2.spec +++ b/gtk2.spec @@ -17,7 +17,7 @@ Summary: The GIMP ToolKit (GTK+), a library for creating GUIs for X Name: gtk2 Version: %{base_version} -Release: 1%{?dist} +Release: 2%{?dist} License: LGPLv2+ Group: System Environment/Libraries Source: http://download.gnome.org/sources/gtk+/2.16/gtk+-%{version}.tar.bz2 @@ -31,6 +31,8 @@ Patch0: gtk+-2.13.5-lib64.patch Patch2: workaround.patch # http://bugzilla.redhat.com/show_bug.cgi?id=478400 Patch3: default_printer.patch +# http://bugzilla.gnome.org/show_bug.cgi?id=384940 +Patch4: print_authentication.patch BuildRequires: atk-devel >= %{atk_version} BuildRequires: pango-devel >= %{pango_version} @@ -135,7 +137,12 @@ This package contains developer documentation for the GTK+ widget toolkit. %patch0 -p1 -b .lib64 %patch2 -p1 -b .workaround %patch3 -p0 -b .default-printer +%patch4 -p0 -b .print-authentication +# make sure that gtkmarshalers.{c, h} get regenerated during the build +# - caused by print_authentication.patch +rm --force ./gtk/gtkmarshalers.c +rm --force ./gtk/gtkmarshalers.h %build libtoolize --force --copy @@ -335,6 +342,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Tue Apr 7 2009 Marek Kasik - 2.16.0-2 +- Add authentication support to GtkPrintBackend. + * Fri Mar 13 2009 Matthias Clasen - 2.16.0-1 - Update to 2.16.0 diff --git a/print_authentication.patch b/print_authentication.patch new file mode 100644 index 0000000..19b809c --- /dev/null +++ b/print_authentication.patch @@ -0,0 +1,1389 @@ +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 ("%s", 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 \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);