1 /* GTK - The GIMP Toolkit
2 * gtkcupsutils.h: Statemachine implementation of POST and GET
3 * cups calls which can be used to create a non-blocking cups API
4 * Copyright (C) 2006, 2007 Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 #include "gtkcupsutils.h"
26 #include <sys/types.h>
31 #include <sys/socket.h>
33 typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
35 static void _connect (GtkCupsRequest *request);
36 static void _post_send (GtkCupsRequest *request);
37 static void _post_write_request (GtkCupsRequest *request);
38 static void _post_write_data (GtkCupsRequest *request);
39 static void _post_check (GtkCupsRequest *request);
40 static void _post_auth (GtkCupsRequest *request);
41 static void _post_read_response (GtkCupsRequest *request);
43 static void _get_send (GtkCupsRequest *request);
44 static void _get_check (GtkCupsRequest *request);
45 static void _get_auth (GtkCupsRequest *request);
46 static void _get_read_data (GtkCupsRequest *request);
52 GtkCupsErrorType error_type;
54 /* some error types like HTTP_ERROR have a status and a code */
59 guint is_ipp_response : 1;
63 #define _GTK_CUPS_MAX_ATTEMPTS 10
64 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
66 static GtkCupsRequestStateFunc post_states[] = {
76 static GtkCupsRequestStateFunc get_states[] = {
84 #ifndef HAVE_CUPS_API_1_6
85 #define ippSetOperation(ipp_request, ipp_op_id) ipp_request->request.op.operation_id = ipp_op_id
86 #define ippSetRequestId(ipp_request, ipp_rq_id) ipp_request->request.op.request_id = ipp_rq_id
87 #define ippSetState(ipp_request, ipp_state) ipp_request->state = ipp_state
88 #define ippGetString(attr, index, foo) attr->values[index].string.text
89 #define ippGetCount(attr) attr->num_values
93 gtk_cups_result_set_error (GtkCupsResult *result,
94 GtkCupsErrorType error_type,
97 const char *error_msg,
102 result->is_ipp_response = FALSE;
103 result->is_error = TRUE;
104 result->error_type = error_type;
105 result->error_status = error_status;
106 result->error_code = error_code;
108 va_start (args, error_msg);
109 result->error_msg = g_strdup_vprintf (error_msg, args);
114 gtk_cups_request_new_with_username (http_t *connection,
115 GtkCupsRequestType req_type,
119 const char *resource,
120 const char *username)
122 GtkCupsRequest *request;
123 cups_lang_t *language;
125 request = g_new0 (GtkCupsRequest, 1);
126 request->result = g_new0 (GtkCupsResult, 1);
128 request->result->error_msg = NULL;
129 request->result->ipp_response = NULL;
131 request->result->is_error = FALSE;
132 request->result->is_ipp_response = FALSE;
134 request->type = req_type;
135 request->state = GTK_CUPS_REQUEST_START;
137 request->password_state = GTK_CUPS_PASSWORD_NONE;
140 request->server = g_strdup (server);
142 request->server = g_strdup (cupsServer ());
146 request->resource = g_strdup (resource);
148 request->resource = g_strdup ("/");
150 if (connection != NULL)
152 request->http = connection;
153 request->own_http = FALSE;
157 request->http = NULL;
158 request->http = httpConnectEncrypt (request->server,
163 httpBlocking (request->http, 0);
165 request->own_http = TRUE;
168 request->last_status = HTTP_CONTINUE;
170 request->attempts = 0;
171 request->data_io = data_io;
173 request->ipp_request = ippNew ();
174 ippSetOperation (request->ipp_request, operation_id);
175 ippSetRequestId (request->ipp_request, 1);
177 language = cupsLangDefault ();
179 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
180 "attributes-charset",
183 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
184 "attributes-natural-language",
185 NULL, language->language);
187 if (username != NULL)
188 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
189 "requesting-user-name",
192 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
193 "requesting-user-name",
196 request->auth_info_required = NULL;
197 request->auth_info = NULL;
198 request->need_auth_info = FALSE;
200 cupsLangFree (language);
206 gtk_cups_request_new (http_t *connection,
207 GtkCupsRequestType req_type,
211 const char *resource)
213 return gtk_cups_request_new_with_username (connection,
223 gtk_cups_result_free (GtkCupsResult *result)
225 g_free (result->error_msg);
227 if (result->ipp_response)
228 ippDelete (result->ipp_response);
234 gtk_cups_request_free (GtkCupsRequest *request)
236 if (request->own_http)
239 httpClose (request->http);
242 if (request->ipp_request)
243 ippDelete (request->ipp_request);
245 g_free (request->server);
246 g_free (request->resource);
247 if (request->password != NULL)
249 memset (request->password, 0, strlen (request->password));
250 g_free (request->password);
253 g_free (request->username);
254 g_strfreev (request->auth_info_required);
256 gtk_cups_result_free (request->result);
262 gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
264 if (connect_only && request->state != GTK_CUPS_REQUEST_START)
269 if (request->type == GTK_CUPS_POST)
270 post_states[request->state] (request);
271 else if (request->type == GTK_CUPS_GET)
272 get_states[request->state] (request);
274 if (gtk_cups_result_is_error (request->result))
275 request->state = GTK_CUPS_REQUEST_DONE;
277 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
278 request->state != GTK_CUPS_REQUEST_DONE)
280 /* TODO: should add a status or error code for too many failed attempts */
281 gtk_cups_result_set_error (request->result,
282 GTK_CUPS_ERROR_GENERAL,
285 "Too many failed attempts");
287 request->state = GTK_CUPS_REQUEST_DONE;
290 if (request->state == GTK_CUPS_REQUEST_DONE)
292 request->poll_state = GTK_CUPS_HTTP_IDLE;
296 /* We need to recheck using httpCheck if the poll_state is read, because
297 * Cups has an internal read buffer. And if this buffer is filled, we may
298 * never get a poll event again. */
299 while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http));
305 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
307 return request->poll_state;
313 gtk_cups_request_get_result (GtkCupsRequest *request)
315 return request->result;
319 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
326 ippAddString (request->ipp_request,
335 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
341 const char *const *values)
343 ippAddStrings (request->ipp_request,
353 gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
357 ipp_attribute_t *attribute = NULL;
359 if (request != NULL && request->ipp_request != NULL)
360 attribute = ippFindAttribute (request->ipp_request,
364 if (attribute != NULL && ippGetCount (attribute) > 0)
365 return ippGetString (attribute, 0, NULL);
377 static const ipp_option_t ipp_options[] = {
378 { "blackplot", IPP_TAG_BOOLEAN },
379 { "brightness", IPP_TAG_INTEGER },
380 { "columns", IPP_TAG_INTEGER },
381 { "copies", IPP_TAG_INTEGER },
382 { "finishings", IPP_TAG_ENUM },
383 { "fitplot", IPP_TAG_BOOLEAN },
384 { "gamma", IPP_TAG_INTEGER },
385 { "hue", IPP_TAG_INTEGER },
386 { "job-k-limit", IPP_TAG_INTEGER },
387 { "job-page-limit", IPP_TAG_INTEGER },
388 { "job-priority", IPP_TAG_INTEGER },
389 { "job-quota-period", IPP_TAG_INTEGER },
390 { "landscape", IPP_TAG_BOOLEAN },
391 { "media", IPP_TAG_KEYWORD },
392 { "mirror", IPP_TAG_BOOLEAN },
393 { "natural-scaling", IPP_TAG_INTEGER },
394 { "number-up", IPP_TAG_INTEGER },
395 { "orientation-requested", IPP_TAG_ENUM },
396 { "page-bottom", IPP_TAG_INTEGER },
397 { "page-left", IPP_TAG_INTEGER },
398 { "page-ranges", IPP_TAG_RANGE },
399 { "page-right", IPP_TAG_INTEGER },
400 { "page-top", IPP_TAG_INTEGER },
401 { "penwidth", IPP_TAG_INTEGER },
402 { "ppi", IPP_TAG_INTEGER },
403 { "prettyprint", IPP_TAG_BOOLEAN },
404 { "printer-resolution", IPP_TAG_RESOLUTION },
405 { "print-quality", IPP_TAG_ENUM },
406 { "saturation", IPP_TAG_INTEGER },
407 { "scaling", IPP_TAG_INTEGER },
408 { "sides", IPP_TAG_KEYWORD },
409 { "wrap", IPP_TAG_BOOLEAN },
410 { "number-up-layout", IPP_TAG_INTEGER }
415 _find_option_tag (const gchar *option)
417 int lower_bound, upper_bound, num_options;
421 result = IPP_TAG_ZERO;
424 upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
429 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
431 match = strcasecmp (option, ipp_options[current_option].name);
434 result = ipp_options[current_option].value_tag;
439 upper_bound = current_option - 1;
443 lower_bound = current_option + 1;
446 if (upper_bound == lower_bound && upper_bound == current_option)
452 if (lower_bound > num_options)
455 if (upper_bound < lower_bound)
461 * Note that this function uses IPP_TAG_JOB, so it is
462 * only suitable for IPP Group 2 attributes.
466 gtk_cups_request_encode_option (GtkCupsRequest *request,
470 ipp_tag_t option_tag;
472 g_return_if_fail (option != NULL);
473 g_return_if_fail (value != NULL);
475 option_tag = _find_option_tag (option);
477 if (option_tag == IPP_TAG_ZERO)
479 option_tag = IPP_TAG_NAME;
480 if (strcasecmp (value, "true") == 0 ||
481 strcasecmp (value, "false") == 0)
483 option_tag = IPP_TAG_BOOLEAN;
489 case IPP_TAG_INTEGER:
491 ippAddInteger (request->ipp_request,
495 strtol (value, NULL, 0));
498 case IPP_TAG_BOOLEAN:
502 if (strcasecmp (value, "true") == 0 ||
503 strcasecmp (value, "on") == 0 ||
504 strcasecmp (value, "yes") == 0)
509 ippAddBoolean (request->ipp_request,
529 lower = strtol (value, &s, 0);
534 upper = strtol (s + 1, NULL, 0);
541 ippAddRange (request->ipp_request,
550 case IPP_TAG_RESOLUTION:
557 xres = strtol (value, &s, 0);
560 yres = strtol (s + 1, &s, 0);
564 if (strcasecmp (s, "dpc") == 0)
565 units = IPP_RES_PER_CM;
567 units = IPP_RES_PER_INCH;
569 ippAddResolution (request->ipp_request,
587 values = g_strdup (value);
591 for (s = values, next = s; *s != '\0'; s++)
593 if (in_quotes != 2 && *s == '\'')
595 /* skip quoted value */
601 else if (in_quotes != 1 && *s == '\"')
603 /* skip quoted value */
609 else if (in_quotes == 0 && *s == ',')
611 /* found delimiter, add to value array */
614 strings = g_ptr_array_new ();
615 g_ptr_array_add (strings, next);
618 else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
620 /* skip escaped character */
628 ippAddString (request->ipp_request,
637 /* multiple values */
640 g_ptr_array_add (strings, next);
642 ippAddStrings (request->ipp_request,
648 (const char **) strings->pdata);
649 g_ptr_array_free (strings, TRUE);
661 _connect (GtkCupsRequest *request)
663 request->poll_state = GTK_CUPS_HTTP_IDLE;
664 request->bytes_received = 0;
666 if (request->http == NULL)
668 request->http = httpConnectEncrypt (request->server,
672 if (request->http == NULL)
676 httpBlocking (request->http, 0);
678 request->own_http = TRUE;
682 request->attempts = 0;
685 /* we always write to the socket after we get
687 request->poll_state = GTK_CUPS_HTTP_WRITE;
692 _post_send (GtkCupsRequest *request)
695 struct stat data_info;
698 g_print ("CUPS Backend: %s\n", G_STRFUNC));
700 request->poll_state = GTK_CUPS_HTTP_WRITE;
702 if (request->data_io != NULL)
704 fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
705 sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
708 sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
710 httpClearFields (request->http);
711 httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
712 httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
713 #ifdef HAVE_HTTPGETAUTHSTRING
714 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
716 #ifdef HAVE_HTTP_AUTHSTRING
717 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
721 if (httpPost (request->http, request->resource))
723 if (httpReconnect (request->http))
725 request->state = GTK_CUPS_POST_DONE;
726 request->poll_state = GTK_CUPS_HTTP_IDLE;
728 /* TODO: should add a status or error code for failed post */
729 gtk_cups_result_set_error (request->result,
730 GTK_CUPS_ERROR_GENERAL,
740 request->attempts = 0;
742 request->state = GTK_CUPS_POST_WRITE_REQUEST;
743 ippSetState (request->ipp_request, IPP_IDLE);
747 _post_write_request (GtkCupsRequest *request)
749 ipp_state_t ipp_status;
752 g_print ("CUPS Backend: %s\n", G_STRFUNC));
754 request->poll_state = GTK_CUPS_HTTP_WRITE;
756 ipp_status = ippWrite (request->http, request->ipp_request);
758 if (ipp_status == IPP_ERROR)
760 int cups_error = cupsLastError ();
761 request->state = GTK_CUPS_POST_DONE;
762 request->poll_state = GTK_CUPS_HTTP_IDLE;
764 gtk_cups_result_set_error (request->result,
769 ippErrorString (cups_error));
773 if (ipp_status == IPP_DATA)
775 if (request->data_io != NULL)
776 request->state = GTK_CUPS_POST_WRITE_DATA;
779 request->state = GTK_CUPS_POST_CHECK;
780 request->poll_state = GTK_CUPS_HTTP_READ;
786 _post_write_data (GtkCupsRequest *request)
789 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
790 http_status_t http_status;
793 g_print ("CUPS Backend: %s\n", G_STRFUNC));
795 request->poll_state = GTK_CUPS_HTTP_WRITE;
797 if (httpCheck (request->http))
798 http_status = httpUpdate (request->http);
800 http_status = request->last_status;
802 request->last_status = http_status;
805 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
814 g_io_channel_read_chars (request->data_io,
816 _GTK_CUPS_MAX_CHUNK_SIZE,
820 if (io_status == G_IO_STATUS_ERROR)
822 request->state = GTK_CUPS_POST_DONE;
823 request->poll_state = GTK_CUPS_HTTP_IDLE;
825 gtk_cups_result_set_error (request->result,
829 "Error reading from cache file: %s",
832 g_error_free (error);
835 else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
837 request->state = GTK_CUPS_POST_CHECK;
838 request->poll_state = GTK_CUPS_HTTP_READ;
840 request->attempts = 0;
845 if (httpWrite2 (request->http, buffer, bytes) < bytes)
849 http_errno = httpError (request->http);
851 request->state = GTK_CUPS_POST_DONE;
852 request->poll_state = GTK_CUPS_HTTP_IDLE;
854 gtk_cups_result_set_error (request->result,
858 "Error writing to socket in Post %s",
859 g_strerror (http_errno));
863 else if (http_status == HTTP_UNAUTHORIZED)
865 request->state = GTK_CUPS_POST_CHECK;
866 request->poll_state = GTK_CUPS_HTTP_READ;
868 request->attempts = 0;
878 _post_auth (GtkCupsRequest *request)
880 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
882 if (request->password == NULL)
884 request->state = GTK_CUPS_POST_DONE;
885 request->poll_state = GTK_CUPS_HTTP_IDLE;
887 gtk_cups_result_set_error (request->result,
894 request->state = GTK_CUPS_POST_CHECK;
899 _get_auth (GtkCupsRequest *request)
901 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
903 if (request->password == NULL)
905 request->state = GTK_CUPS_GET_DONE;
906 request->poll_state = GTK_CUPS_HTTP_IDLE;
908 gtk_cups_result_set_error (request->result,
915 request->state = GTK_CUPS_GET_CHECK;
919 /* Very ugly hack: cups has a stupid synchronous password callback
920 * that doesn't even take the request or user data parameters, so
921 * we have to use a static variable to pass the password to it.
923 * The callback sets cups_password to NULL to signal that the
924 * password has been used.
926 static char *cups_password = NULL;
927 static char *cups_username = NULL;
930 passwordCB (const char *prompt)
932 char *pwd = cups_password;
933 cups_password = NULL;
935 cupsSetUser (cups_username);
941 _post_check (GtkCupsRequest *request)
943 http_status_t http_status;
945 http_status = request->last_status;
948 g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
950 request->poll_state = GTK_CUPS_HTTP_READ;
952 if (http_status == HTTP_CONTINUE)
956 else if (http_status == HTTP_UNAUTHORIZED)
958 int auth_result = -1;
959 httpFlush (request->http);
961 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
963 request->poll_state = GTK_CUPS_HTTP_IDLE;
964 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
965 request->state = GTK_CUPS_POST_AUTH;
966 request->need_password = TRUE;
972 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
974 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
976 /* Basic, BasicDigest, Digest and PeerCred */
979 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
981 cups_username = request->username;
982 cupsSetPasswordCB (passwordCB);
984 /* This call success for PeerCred authentication */
985 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
987 if (auth_result != 0)
989 /* move to AUTH state to let the backend
992 request->poll_state = GTK_CUPS_HTTP_IDLE;
993 request->state = GTK_CUPS_POST_AUTH;
994 request->need_password = TRUE;
1001 cups_password = request->password;
1002 cups_username = request->username;
1004 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
1006 if (cups_password != NULL)
1009 if (request->password != NULL)
1011 memset (request->password, 0, strlen (request->password));
1012 g_free (request->password);
1013 request->password = NULL;
1016 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1021 httpReconnect (request->http))
1023 /* if the password has been used, reset password_state
1024 * so that we ask for a new one next time around
1026 if (cups_password == NULL)
1027 request->password_state = GTK_CUPS_PASSWORD_NONE;
1029 request->state = GTK_CUPS_POST_DONE;
1030 request->poll_state = GTK_CUPS_HTTP_IDLE;
1031 gtk_cups_result_set_error (request->result,
1032 GTK_CUPS_ERROR_AUTH,
1039 if (request->data_io != NULL)
1040 g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
1042 request->state = GTK_CUPS_POST_CONNECT;
1043 request->poll_state = GTK_CUPS_HTTP_WRITE;
1045 else if (http_status == HTTP_ERROR)
1047 int error = httpError (request->http);
1049 if (error != WSAENETDOWN && error != WSAENETUNREACH)
1051 if (error != ENETDOWN && error != ENETUNREACH)
1052 #endif /* G_OS_WIN32 */
1054 request->attempts++;
1059 request->state = GTK_CUPS_POST_DONE;
1060 request->poll_state = GTK_CUPS_HTTP_IDLE;
1062 gtk_cups_result_set_error (request->result,
1063 GTK_CUPS_ERROR_HTTP,
1066 "Unknown HTTP error");
1071 else if (http_status == HTTP_UPGRADE_REQUIRED)
1073 /* Flush any error message... */
1074 httpFlush (request->http);
1076 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1077 request->state = GTK_CUPS_POST_CONNECT;
1080 httpReconnect (request->http);
1082 /* Upgrade with encryption... */
1083 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1085 request->attempts++;
1088 else if (http_status != HTTP_OK)
1092 http_errno = httpError (request->http);
1094 if (http_errno == EPIPE)
1095 request->state = GTK_CUPS_POST_CONNECT;
1098 request->state = GTK_CUPS_POST_DONE;
1099 gtk_cups_result_set_error (request->result,
1100 GTK_CUPS_ERROR_HTTP,
1103 "HTTP Error in POST %s",
1104 g_strerror (http_errno));
1105 request->poll_state = GTK_CUPS_HTTP_IDLE;
1107 httpFlush (request->http);
1111 request->poll_state = GTK_CUPS_HTTP_IDLE;
1113 httpFlush (request->http);
1115 request->last_status = HTTP_CONTINUE;
1116 httpClose (request->http);
1117 request->http = NULL;
1122 request->state = GTK_CUPS_POST_READ_RESPONSE;
1127 http_status = HTTP_CONTINUE;
1129 if (httpCheck (request->http))
1130 http_status = httpUpdate (request->http);
1132 request->last_status = http_status;
1136 _post_read_response (GtkCupsRequest *request)
1138 ipp_state_t ipp_status;
1141 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1143 request->poll_state = GTK_CUPS_HTTP_READ;
1145 if (request->result->ipp_response == NULL)
1146 request->result->ipp_response = ippNew();
1148 ipp_status = ippRead (request->http,
1149 request->result->ipp_response);
1151 if (ipp_status == IPP_ERROR)
1153 int ipp_error = cupsLastError ();
1154 gtk_cups_result_set_error (request->result,
1159 ippErrorString (ipp_error));
1161 ippDelete (request->result->ipp_response);
1162 request->result->ipp_response = NULL;
1164 request->state = GTK_CUPS_POST_DONE;
1165 request->poll_state = GTK_CUPS_HTTP_IDLE;
1167 else if (ipp_status == IPP_DATA)
1169 request->state = GTK_CUPS_POST_DONE;
1170 request->poll_state = GTK_CUPS_HTTP_IDLE;
1175 _get_send (GtkCupsRequest *request)
1178 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1180 request->poll_state = GTK_CUPS_HTTP_WRITE;
1182 if (request->data_io == NULL)
1184 gtk_cups_result_set_error (request->result,
1187 G_IO_CHANNEL_ERROR_FAILED,
1188 "Get requires an open io channel");
1190 request->state = GTK_CUPS_GET_DONE;
1191 request->poll_state = GTK_CUPS_HTTP_IDLE;
1196 httpClearFields (request->http);
1197 #ifdef HAVE_HTTPGETAUTHSTRING
1198 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
1200 #ifdef HAVE_HTTP_AUTHSTRING
1201 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
1205 if (httpGet (request->http, request->resource))
1207 if (httpReconnect (request->http))
1209 request->state = GTK_CUPS_GET_DONE;
1210 request->poll_state = GTK_CUPS_HTTP_IDLE;
1212 /* TODO: should add a status or error code for failed GET */
1213 gtk_cups_result_set_error (request->result,
1214 GTK_CUPS_ERROR_GENERAL,
1220 request->attempts++;
1224 if (httpCheck (request->http))
1225 request->last_status = httpUpdate (request->http);
1227 request->attempts = 0;
1229 request->state = GTK_CUPS_GET_CHECK;
1230 request->poll_state = GTK_CUPS_HTTP_READ;
1232 ippSetState (request->ipp_request, IPP_IDLE);
1236 _get_check (GtkCupsRequest *request)
1238 http_status_t http_status;
1241 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1243 http_status = request->last_status;
1245 request->poll_state = GTK_CUPS_HTTP_READ;
1247 if (http_status == HTTP_CONTINUE)
1251 else if (http_status == HTTP_UNAUTHORIZED)
1253 int auth_result = -1;
1254 httpFlush (request->http);
1256 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
1258 request->poll_state = GTK_CUPS_HTTP_IDLE;
1259 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
1260 request->state = GTK_CUPS_GET_AUTH;
1261 request->need_password = TRUE;
1267 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
1269 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1271 /* Basic, BasicDigest, Digest and PeerCred */
1274 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
1276 cups_username = request->username;
1277 cupsSetPasswordCB (passwordCB);
1279 /* This call success for PeerCred authentication */
1280 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1282 if (auth_result != 0)
1284 /* move to AUTH state to let the backend
1285 * ask for a password
1287 request->poll_state = GTK_CUPS_HTTP_IDLE;
1288 request->state = GTK_CUPS_GET_AUTH;
1289 request->need_password = TRUE;
1296 cups_password = request->password;
1297 cups_username = request->username;
1299 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1301 if (cups_password != NULL)
1304 if (request->password != NULL)
1306 memset (request->password, 0, strlen (request->password));
1307 g_free (request->password);
1308 request->password = NULL;
1311 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1316 httpReconnect (request->http))
1318 /* if the password has been used, reset password_state
1319 * so that we ask for a new one next time around
1321 if (cups_password == NULL)
1322 request->password_state = GTK_CUPS_PASSWORD_NONE;
1324 request->state = GTK_CUPS_GET_DONE;
1325 request->poll_state = GTK_CUPS_HTTP_IDLE;
1326 gtk_cups_result_set_error (request->result,
1327 GTK_CUPS_ERROR_AUTH,
1334 request->state = GTK_CUPS_GET_CONNECT;
1335 request->last_status = HTTP_CONTINUE;
1339 else if (http_status == HTTP_UPGRADE_REQUIRED)
1341 /* Flush any error message... */
1342 httpFlush (request->http);
1344 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1345 request->state = GTK_CUPS_GET_CONNECT;
1348 httpReconnect (request->http);
1350 /* Upgrade with encryption... */
1351 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1353 request->attempts++;
1356 else if (http_status != HTTP_OK)
1360 http_errno = httpError (request->http);
1362 if (http_errno == EPIPE)
1363 request->state = GTK_CUPS_GET_CONNECT;
1366 request->state = GTK_CUPS_GET_DONE;
1367 gtk_cups_result_set_error (request->result,
1368 GTK_CUPS_ERROR_HTTP,
1371 "HTTP Error in GET %s",
1372 g_strerror (http_errno));
1373 request->poll_state = GTK_CUPS_HTTP_IDLE;
1374 httpFlush (request->http);
1379 request->poll_state = GTK_CUPS_HTTP_IDLE;
1380 httpFlush (request->http);
1381 httpClose (request->http);
1382 request->last_status = HTTP_CONTINUE;
1383 request->http = NULL;
1389 request->state = GTK_CUPS_GET_READ_DATA;
1394 http_status = HTTP_CONTINUE;
1396 if (httpCheck (request->http))
1397 http_status = httpUpdate (request->http);
1399 request->last_status = http_status;
1404 _get_read_data (GtkCupsRequest *request)
1406 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1408 gsize bytes_written;
1409 GIOStatus io_status;
1413 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1417 request->poll_state = GTK_CUPS_HTTP_READ;
1419 bytes = httpRead2 (request->http, buffer, sizeof (buffer));
1420 request->bytes_received += bytes;
1423 g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes));
1426 g_io_channel_write_chars (request->data_io,
1432 if (io_status == G_IO_STATUS_ERROR)
1434 request->state = GTK_CUPS_GET_DONE;
1435 request->poll_state = GTK_CUPS_HTTP_IDLE;
1437 gtk_cups_result_set_error (request->result,
1442 g_error_free (error);
1445 /* Stop if we do not expect any more data or EOF was received. */
1446 if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
1448 request->state = GTK_CUPS_GET_DONE;
1449 request->poll_state = GTK_CUPS_HTTP_IDLE;
1456 gtk_cups_request_is_done (GtkCupsRequest *request)
1458 return (request->state == GTK_CUPS_REQUEST_DONE);
1462 gtk_cups_result_is_error (GtkCupsResult *result)
1464 return result->is_error;
1468 gtk_cups_result_get_response (GtkCupsResult *result)
1470 return result->ipp_response;
1474 gtk_cups_result_get_error_type (GtkCupsResult *result)
1476 return result->error_type;
1480 gtk_cups_result_get_error_status (GtkCupsResult *result)
1482 return result->error_status;
1486 gtk_cups_result_get_error_code (GtkCupsResult *result)
1488 return result->error_code;
1492 gtk_cups_result_get_error_string (GtkCupsResult *result)
1494 return result->error_msg;
1497 /* This function allocates new instance of GtkCupsConnectionTest() and creates
1498 * a socket for communication with a CUPS server 'server'.
1500 GtkCupsConnectionTest *
1501 gtk_cups_connection_test_new (const char *server)
1503 GtkCupsConnectionTest *result = NULL;
1504 gchar *port_str = NULL;
1506 result = g_new (GtkCupsConnectionTest, 1);
1508 port_str = g_strdup_printf ("%d", ippPort ());
1511 result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
1513 result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
1517 result->socket = -1;
1518 result->current_addr = NULL;
1519 result->last_wrong_addr = NULL;
1520 result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1522 result->at_init = gtk_cups_connection_test_get_state (result);
1528 /* A non-blocking test whether it is possible to connect to a CUPS server specified
1529 * inside of GtkCupsConnectionTest structure.
1530 * - you need to check it more then once.
1531 * The connection is closed after a successful connection.
1533 GtkCupsConnectionState
1534 gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
1536 GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1537 http_addrlist_t *iter;
1543 return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1545 if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
1547 test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1548 return GTK_CUPS_CONNECTION_AVAILABLE;
1552 if (test->socket == -1)
1554 if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
1555 iter = test->last_wrong_addr->next;
1558 test->last_wrong_addr = NULL;
1559 iter = test->addrlist;
1564 test->socket = socket (iter->addr.addr.sa_family,
1568 if (test->socket >= 0)
1570 flags = fcntl (test->socket, F_GETFL);
1573 flags |= O_NONBLOCK;
1575 fcntl (test->socket, F_SETFL, flags);
1577 test->current_addr = iter;
1585 if (test->socket >= 0)
1587 code = connect (test->socket,
1588 &test->current_addr->addr.addr,
1589 httpAddrLength (&test->current_addr->addr));
1593 if (code == 0 || error_code == EISCONN)
1595 close (test->socket);
1597 test->current_addr = NULL;
1598 result = GTK_CUPS_CONNECTION_AVAILABLE;
1602 if (error_code == EALREADY || error_code == EINPROGRESS)
1603 result = GTK_CUPS_CONNECTION_IN_PROGRESS;
1606 close (test->socket);
1608 test->last_wrong_addr = test->current_addr;
1609 result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1618 /* This function frees memory used by the GtkCupsConnectionTest structure.
1621 gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
1626 test->current_addr = NULL;
1627 test->last_wrong_addr = NULL;
1628 httpAddrFreeList (test->addrlist);
1629 if (test->socket != -1)
1631 close (test->socket);