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
92 ippSetVersion (ipp_t *ipp,
96 if (!ipp || major < 0 || minor < 0)
99 ipp->request.any.version[0] = major;
100 ipp->request.any.version[1] = minor;
107 gtk_cups_result_set_error (GtkCupsResult *result,
108 GtkCupsErrorType error_type,
111 const char *error_msg,
116 result->is_ipp_response = FALSE;
117 result->is_error = TRUE;
118 result->error_type = error_type;
119 result->error_status = error_status;
120 result->error_code = error_code;
122 va_start (args, error_msg);
123 result->error_msg = g_strdup_vprintf (error_msg, args);
128 gtk_cups_request_new_with_username (http_t *connection,
129 GtkCupsRequestType req_type,
133 const char *resource,
134 const char *username)
136 GtkCupsRequest *request;
137 cups_lang_t *language;
139 request = g_new0 (GtkCupsRequest, 1);
140 request->result = g_new0 (GtkCupsResult, 1);
142 request->result->error_msg = NULL;
143 request->result->ipp_response = NULL;
145 request->result->is_error = FALSE;
146 request->result->is_ipp_response = FALSE;
148 request->type = req_type;
149 request->state = GTK_CUPS_REQUEST_START;
151 request->password_state = GTK_CUPS_PASSWORD_NONE;
154 request->server = g_strdup (server);
156 request->server = g_strdup (cupsServer ());
160 request->resource = g_strdup (resource);
162 request->resource = g_strdup ("/");
164 if (connection != NULL)
166 request->http = connection;
167 request->own_http = FALSE;
171 request->http = NULL;
172 request->http = httpConnectEncrypt (request->server,
177 httpBlocking (request->http, 0);
179 request->own_http = TRUE;
182 request->last_status = HTTP_CONTINUE;
184 request->attempts = 0;
185 request->data_io = data_io;
187 request->ipp_request = ippNew ();
188 ippSetOperation (request->ipp_request, operation_id);
189 ippSetRequestId (request->ipp_request, 1);
191 language = cupsLangDefault ();
193 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
194 "attributes-charset",
197 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
198 "attributes-natural-language",
199 NULL, language->language);
201 if (username != NULL)
202 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
203 "requesting-user-name",
206 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
207 "requesting-user-name",
210 request->auth_info_required = NULL;
211 request->auth_info = NULL;
212 request->need_auth_info = FALSE;
214 cupsLangFree (language);
220 gtk_cups_request_new (http_t *connection,
221 GtkCupsRequestType req_type,
225 const char *resource)
227 return gtk_cups_request_new_with_username (connection,
237 gtk_cups_result_free (GtkCupsResult *result)
239 g_free (result->error_msg);
241 if (result->ipp_response)
242 ippDelete (result->ipp_response);
248 gtk_cups_request_free (GtkCupsRequest *request)
250 if (request->own_http)
253 httpClose (request->http);
256 if (request->ipp_request)
257 ippDelete (request->ipp_request);
259 g_free (request->server);
260 g_free (request->resource);
261 if (request->password != NULL)
263 memset (request->password, 0, strlen (request->password));
264 g_free (request->password);
267 g_free (request->username);
268 g_strfreev (request->auth_info_required);
270 gtk_cups_result_free (request->result);
276 gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
278 if (connect_only && request->state != GTK_CUPS_REQUEST_START)
283 if (request->type == GTK_CUPS_POST)
284 post_states[request->state] (request);
285 else if (request->type == GTK_CUPS_GET)
286 get_states[request->state] (request);
288 if (gtk_cups_result_is_error (request->result))
289 request->state = GTK_CUPS_REQUEST_DONE;
291 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
292 request->state != GTK_CUPS_REQUEST_DONE)
294 /* TODO: should add a status or error code for too many failed attempts */
295 gtk_cups_result_set_error (request->result,
296 GTK_CUPS_ERROR_GENERAL,
299 "Too many failed attempts");
301 request->state = GTK_CUPS_REQUEST_DONE;
304 if (request->state == GTK_CUPS_REQUEST_DONE)
306 request->poll_state = GTK_CUPS_HTTP_IDLE;
310 /* We need to recheck using httpCheck if the poll_state is read, because
311 * Cups has an internal read buffer. And if this buffer is filled, we may
312 * never get a poll event again. */
313 while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http));
319 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
321 return request->poll_state;
327 gtk_cups_request_get_result (GtkCupsRequest *request)
329 return request->result;
333 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
340 ippAddString (request->ipp_request,
349 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
355 const char *const *values)
357 ippAddStrings (request->ipp_request,
367 gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
371 ipp_attribute_t *attribute = NULL;
373 if (request != NULL && request->ipp_request != NULL)
374 attribute = ippFindAttribute (request->ipp_request,
378 if (attribute != NULL && ippGetCount (attribute) > 0)
379 return ippGetString (attribute, 0, NULL);
391 static const ipp_option_t ipp_options[] = {
392 { "blackplot", IPP_TAG_BOOLEAN },
393 { "brightness", IPP_TAG_INTEGER },
394 { "columns", IPP_TAG_INTEGER },
395 { "copies", IPP_TAG_INTEGER },
396 { "finishings", IPP_TAG_ENUM },
397 { "fitplot", IPP_TAG_BOOLEAN },
398 { "gamma", IPP_TAG_INTEGER },
399 { "hue", IPP_TAG_INTEGER },
400 { "job-k-limit", IPP_TAG_INTEGER },
401 { "job-page-limit", IPP_TAG_INTEGER },
402 { "job-priority", IPP_TAG_INTEGER },
403 { "job-quota-period", IPP_TAG_INTEGER },
404 { "landscape", IPP_TAG_BOOLEAN },
405 { "media", IPP_TAG_KEYWORD },
406 { "mirror", IPP_TAG_BOOLEAN },
407 { "natural-scaling", IPP_TAG_INTEGER },
408 { "number-up", IPP_TAG_INTEGER },
409 { "orientation-requested", IPP_TAG_ENUM },
410 { "page-bottom", IPP_TAG_INTEGER },
411 { "page-left", IPP_TAG_INTEGER },
412 { "page-ranges", IPP_TAG_RANGE },
413 { "page-right", IPP_TAG_INTEGER },
414 { "page-top", IPP_TAG_INTEGER },
415 { "penwidth", IPP_TAG_INTEGER },
416 { "ppi", IPP_TAG_INTEGER },
417 { "prettyprint", IPP_TAG_BOOLEAN },
418 { "printer-resolution", IPP_TAG_RESOLUTION },
419 { "print-quality", IPP_TAG_ENUM },
420 { "saturation", IPP_TAG_INTEGER },
421 { "scaling", IPP_TAG_INTEGER },
422 { "sides", IPP_TAG_KEYWORD },
423 { "wrap", IPP_TAG_BOOLEAN },
424 { "number-up-layout", IPP_TAG_INTEGER }
429 _find_option_tag (const gchar *option)
431 int lower_bound, upper_bound, num_options;
435 result = IPP_TAG_ZERO;
438 upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
443 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
445 match = strcasecmp (option, ipp_options[current_option].name);
448 result = ipp_options[current_option].value_tag;
453 upper_bound = current_option - 1;
457 lower_bound = current_option + 1;
460 if (upper_bound == lower_bound && upper_bound == current_option)
466 if (lower_bound > num_options)
469 if (upper_bound < lower_bound)
475 * Note that this function uses IPP_TAG_JOB, so it is
476 * only suitable for IPP Group 2 attributes.
480 gtk_cups_request_encode_option (GtkCupsRequest *request,
484 ipp_tag_t option_tag;
486 g_return_if_fail (option != NULL);
487 g_return_if_fail (value != NULL);
489 option_tag = _find_option_tag (option);
491 if (option_tag == IPP_TAG_ZERO)
493 option_tag = IPP_TAG_NAME;
494 if (strcasecmp (value, "true") == 0 ||
495 strcasecmp (value, "false") == 0)
497 option_tag = IPP_TAG_BOOLEAN;
503 case IPP_TAG_INTEGER:
505 ippAddInteger (request->ipp_request,
509 strtol (value, NULL, 0));
512 case IPP_TAG_BOOLEAN:
516 if (strcasecmp (value, "true") == 0 ||
517 strcasecmp (value, "on") == 0 ||
518 strcasecmp (value, "yes") == 0)
523 ippAddBoolean (request->ipp_request,
543 lower = strtol (value, &s, 0);
548 upper = strtol (s + 1, NULL, 0);
555 ippAddRange (request->ipp_request,
564 case IPP_TAG_RESOLUTION:
571 xres = strtol (value, &s, 0);
574 yres = strtol (s + 1, &s, 0);
578 if (strcasecmp (s, "dpc") == 0)
579 units = IPP_RES_PER_CM;
581 units = IPP_RES_PER_INCH;
583 ippAddResolution (request->ipp_request,
601 values = g_strdup (value);
605 for (s = values, next = s; *s != '\0'; s++)
607 if (in_quotes != 2 && *s == '\'')
609 /* skip quoted value */
615 else if (in_quotes != 1 && *s == '\"')
617 /* skip quoted value */
623 else if (in_quotes == 0 && *s == ',')
625 /* found delimiter, add to value array */
628 strings = g_ptr_array_new ();
629 g_ptr_array_add (strings, next);
632 else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
634 /* skip escaped character */
642 ippAddString (request->ipp_request,
651 /* multiple values */
654 g_ptr_array_add (strings, next);
656 ippAddStrings (request->ipp_request,
662 (const char **) strings->pdata);
663 g_ptr_array_free (strings, TRUE);
674 gtk_cups_request_set_ipp_version (GtkCupsRequest *request,
678 ippSetVersion (request->ipp_request, major, minor);
682 _connect (GtkCupsRequest *request)
684 request->poll_state = GTK_CUPS_HTTP_IDLE;
685 request->bytes_received = 0;
687 if (request->http == NULL)
689 request->http = httpConnectEncrypt (request->server,
693 if (request->http == NULL)
697 httpBlocking (request->http, 0);
699 request->own_http = TRUE;
703 request->attempts = 0;
706 /* we always write to the socket after we get
708 request->poll_state = GTK_CUPS_HTTP_WRITE;
713 _post_send (GtkCupsRequest *request)
716 struct stat data_info;
719 g_print ("CUPS Backend: %s\n", G_STRFUNC));
721 request->poll_state = GTK_CUPS_HTTP_WRITE;
723 if (request->data_io != NULL)
725 fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
726 sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
729 sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
731 httpClearFields (request->http);
732 httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
733 httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
734 #ifdef HAVE_HTTPGETAUTHSTRING
735 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
737 #ifdef HAVE_HTTP_AUTHSTRING
738 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
742 if (httpPost (request->http, request->resource))
744 if (httpReconnect (request->http))
746 request->state = GTK_CUPS_POST_DONE;
747 request->poll_state = GTK_CUPS_HTTP_IDLE;
749 /* TODO: should add a status or error code for failed post */
750 gtk_cups_result_set_error (request->result,
751 GTK_CUPS_ERROR_GENERAL,
761 request->attempts = 0;
763 request->state = GTK_CUPS_POST_WRITE_REQUEST;
764 ippSetState (request->ipp_request, IPP_IDLE);
768 _post_write_request (GtkCupsRequest *request)
770 ipp_state_t ipp_status;
773 g_print ("CUPS Backend: %s\n", G_STRFUNC));
775 request->poll_state = GTK_CUPS_HTTP_WRITE;
777 ipp_status = ippWrite (request->http, request->ipp_request);
779 if (ipp_status == IPP_ERROR)
781 int cups_error = cupsLastError ();
782 request->state = GTK_CUPS_POST_DONE;
783 request->poll_state = GTK_CUPS_HTTP_IDLE;
785 gtk_cups_result_set_error (request->result,
790 ippErrorString (cups_error));
794 if (ipp_status == IPP_DATA)
796 if (request->data_io != NULL)
797 request->state = GTK_CUPS_POST_WRITE_DATA;
800 request->state = GTK_CUPS_POST_CHECK;
801 request->poll_state = GTK_CUPS_HTTP_READ;
807 _post_write_data (GtkCupsRequest *request)
810 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
811 http_status_t http_status;
814 g_print ("CUPS Backend: %s\n", G_STRFUNC));
816 request->poll_state = GTK_CUPS_HTTP_WRITE;
818 if (httpCheck (request->http))
819 http_status = httpUpdate (request->http);
821 http_status = request->last_status;
823 request->last_status = http_status;
826 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
835 g_io_channel_read_chars (request->data_io,
837 _GTK_CUPS_MAX_CHUNK_SIZE,
841 if (io_status == G_IO_STATUS_ERROR)
843 request->state = GTK_CUPS_POST_DONE;
844 request->poll_state = GTK_CUPS_HTTP_IDLE;
846 gtk_cups_result_set_error (request->result,
850 "Error reading from cache file: %s",
853 g_error_free (error);
856 else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
858 request->state = GTK_CUPS_POST_CHECK;
859 request->poll_state = GTK_CUPS_HTTP_READ;
861 request->attempts = 0;
866 if (httpWrite2 (request->http, buffer, bytes) < bytes)
870 http_errno = httpError (request->http);
872 request->state = GTK_CUPS_POST_DONE;
873 request->poll_state = GTK_CUPS_HTTP_IDLE;
875 gtk_cups_result_set_error (request->result,
879 "Error writing to socket in Post %s",
880 g_strerror (http_errno));
884 else if (http_status == HTTP_UNAUTHORIZED)
886 request->state = GTK_CUPS_POST_CHECK;
887 request->poll_state = GTK_CUPS_HTTP_READ;
889 request->attempts = 0;
899 _post_auth (GtkCupsRequest *request)
901 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
903 if (request->password == NULL)
905 request->state = GTK_CUPS_POST_DONE;
906 request->poll_state = GTK_CUPS_HTTP_IDLE;
908 gtk_cups_result_set_error (request->result,
915 request->state = GTK_CUPS_POST_CHECK;
920 _get_auth (GtkCupsRequest *request)
922 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
924 if (request->password == NULL)
926 request->state = GTK_CUPS_GET_DONE;
927 request->poll_state = GTK_CUPS_HTTP_IDLE;
929 gtk_cups_result_set_error (request->result,
936 request->state = GTK_CUPS_GET_CHECK;
940 /* Very ugly hack: cups has a stupid synchronous password callback
941 * that doesn't even take the request or user data parameters, so
942 * we have to use a static variable to pass the password to it.
944 * The callback sets cups_password to NULL to signal that the
945 * password has been used.
947 static char *cups_password = NULL;
948 static char *cups_username = NULL;
951 passwordCB (const char *prompt)
953 char *pwd = cups_password;
954 cups_password = NULL;
956 cupsSetUser (cups_username);
962 _post_check (GtkCupsRequest *request)
964 http_status_t http_status;
966 http_status = request->last_status;
969 g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
971 request->poll_state = GTK_CUPS_HTTP_READ;
973 if (http_status == HTTP_CONTINUE)
977 else if (http_status == HTTP_UNAUTHORIZED)
979 int auth_result = -1;
980 httpFlush (request->http);
982 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
984 request->poll_state = GTK_CUPS_HTTP_IDLE;
985 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
986 request->state = GTK_CUPS_POST_AUTH;
987 request->need_password = TRUE;
993 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
995 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
997 /* Basic, BasicDigest, Digest and PeerCred */
1000 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
1002 cups_username = request->username;
1003 cupsSetPasswordCB (passwordCB);
1005 /* This call success for PeerCred authentication */
1006 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
1008 if (auth_result != 0)
1010 /* move to AUTH state to let the backend
1011 * ask for a password
1013 request->poll_state = GTK_CUPS_HTTP_IDLE;
1014 request->state = GTK_CUPS_POST_AUTH;
1015 request->need_password = TRUE;
1022 cups_password = request->password;
1023 cups_username = request->username;
1025 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
1027 if (cups_password != NULL)
1030 if (request->password != NULL)
1032 memset (request->password, 0, strlen (request->password));
1033 g_free (request->password);
1034 request->password = NULL;
1037 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1042 httpReconnect (request->http))
1044 /* if the password has been used, reset password_state
1045 * so that we ask for a new one next time around
1047 if (cups_password == NULL)
1048 request->password_state = GTK_CUPS_PASSWORD_NONE;
1050 request->state = GTK_CUPS_POST_DONE;
1051 request->poll_state = GTK_CUPS_HTTP_IDLE;
1052 gtk_cups_result_set_error (request->result,
1053 GTK_CUPS_ERROR_AUTH,
1060 if (request->data_io != NULL)
1061 g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
1063 request->state = GTK_CUPS_POST_CONNECT;
1064 request->poll_state = GTK_CUPS_HTTP_WRITE;
1066 else if (http_status == HTTP_ERROR)
1068 int error = httpError (request->http);
1070 if (error != WSAENETDOWN && error != WSAENETUNREACH)
1072 if (error != ENETDOWN && error != ENETUNREACH)
1073 #endif /* G_OS_WIN32 */
1075 request->attempts++;
1080 request->state = GTK_CUPS_POST_DONE;
1081 request->poll_state = GTK_CUPS_HTTP_IDLE;
1083 gtk_cups_result_set_error (request->result,
1084 GTK_CUPS_ERROR_HTTP,
1087 "Unknown HTTP error");
1092 else if (http_status == HTTP_UPGRADE_REQUIRED)
1094 /* Flush any error message... */
1095 httpFlush (request->http);
1097 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1098 request->state = GTK_CUPS_POST_CONNECT;
1101 httpReconnect (request->http);
1103 /* Upgrade with encryption... */
1104 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1106 request->attempts++;
1109 else if (http_status != HTTP_OK)
1113 http_errno = httpError (request->http);
1115 if (http_errno == EPIPE)
1116 request->state = GTK_CUPS_POST_CONNECT;
1119 request->state = GTK_CUPS_POST_DONE;
1120 gtk_cups_result_set_error (request->result,
1121 GTK_CUPS_ERROR_HTTP,
1124 "HTTP Error in POST %s",
1125 g_strerror (http_errno));
1126 request->poll_state = GTK_CUPS_HTTP_IDLE;
1128 httpFlush (request->http);
1132 request->poll_state = GTK_CUPS_HTTP_IDLE;
1134 httpFlush (request->http);
1136 request->last_status = HTTP_CONTINUE;
1137 httpClose (request->http);
1138 request->http = NULL;
1143 request->state = GTK_CUPS_POST_READ_RESPONSE;
1148 http_status = HTTP_CONTINUE;
1150 if (httpCheck (request->http))
1151 http_status = httpUpdate (request->http);
1153 request->last_status = http_status;
1157 _post_read_response (GtkCupsRequest *request)
1159 ipp_state_t ipp_status;
1162 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1164 request->poll_state = GTK_CUPS_HTTP_READ;
1166 if (request->result->ipp_response == NULL)
1167 request->result->ipp_response = ippNew();
1169 ipp_status = ippRead (request->http,
1170 request->result->ipp_response);
1172 if (ipp_status == IPP_ERROR)
1174 int ipp_error = cupsLastError ();
1175 gtk_cups_result_set_error (request->result,
1180 ippErrorString (ipp_error));
1182 ippDelete (request->result->ipp_response);
1183 request->result->ipp_response = NULL;
1185 request->state = GTK_CUPS_POST_DONE;
1186 request->poll_state = GTK_CUPS_HTTP_IDLE;
1188 else if (ipp_status == IPP_DATA)
1190 request->state = GTK_CUPS_POST_DONE;
1191 request->poll_state = GTK_CUPS_HTTP_IDLE;
1196 _get_send (GtkCupsRequest *request)
1199 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1201 request->poll_state = GTK_CUPS_HTTP_WRITE;
1203 if (request->data_io == NULL)
1205 gtk_cups_result_set_error (request->result,
1208 G_IO_CHANNEL_ERROR_FAILED,
1209 "Get requires an open io channel");
1211 request->state = GTK_CUPS_GET_DONE;
1212 request->poll_state = GTK_CUPS_HTTP_IDLE;
1217 httpClearFields (request->http);
1218 #ifdef HAVE_HTTPGETAUTHSTRING
1219 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
1221 #ifdef HAVE_HTTP_AUTHSTRING
1222 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
1226 if (httpGet (request->http, request->resource))
1228 if (httpReconnect (request->http))
1230 request->state = GTK_CUPS_GET_DONE;
1231 request->poll_state = GTK_CUPS_HTTP_IDLE;
1233 /* TODO: should add a status or error code for failed GET */
1234 gtk_cups_result_set_error (request->result,
1235 GTK_CUPS_ERROR_GENERAL,
1241 request->attempts++;
1245 if (httpCheck (request->http))
1246 request->last_status = httpUpdate (request->http);
1248 request->attempts = 0;
1250 request->state = GTK_CUPS_GET_CHECK;
1251 request->poll_state = GTK_CUPS_HTTP_READ;
1253 ippSetState (request->ipp_request, IPP_IDLE);
1257 _get_check (GtkCupsRequest *request)
1259 http_status_t http_status;
1262 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1264 http_status = request->last_status;
1266 request->poll_state = GTK_CUPS_HTTP_READ;
1268 if (http_status == HTTP_CONTINUE)
1272 else if (http_status == HTTP_UNAUTHORIZED)
1274 int auth_result = -1;
1275 httpFlush (request->http);
1277 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
1279 request->poll_state = GTK_CUPS_HTTP_IDLE;
1280 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
1281 request->state = GTK_CUPS_GET_AUTH;
1282 request->need_password = TRUE;
1288 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
1290 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1292 /* Basic, BasicDigest, Digest and PeerCred */
1295 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
1297 cups_username = request->username;
1298 cupsSetPasswordCB (passwordCB);
1300 /* This call success for PeerCred authentication */
1301 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1303 if (auth_result != 0)
1305 /* move to AUTH state to let the backend
1306 * ask for a password
1308 request->poll_state = GTK_CUPS_HTTP_IDLE;
1309 request->state = GTK_CUPS_GET_AUTH;
1310 request->need_password = TRUE;
1317 cups_password = request->password;
1318 cups_username = request->username;
1320 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1322 if (cups_password != NULL)
1325 if (request->password != NULL)
1327 memset (request->password, 0, strlen (request->password));
1328 g_free (request->password);
1329 request->password = NULL;
1332 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1337 httpReconnect (request->http))
1339 /* if the password has been used, reset password_state
1340 * so that we ask for a new one next time around
1342 if (cups_password == NULL)
1343 request->password_state = GTK_CUPS_PASSWORD_NONE;
1345 request->state = GTK_CUPS_GET_DONE;
1346 request->poll_state = GTK_CUPS_HTTP_IDLE;
1347 gtk_cups_result_set_error (request->result,
1348 GTK_CUPS_ERROR_AUTH,
1355 request->state = GTK_CUPS_GET_CONNECT;
1356 request->last_status = HTTP_CONTINUE;
1360 else if (http_status == HTTP_UPGRADE_REQUIRED)
1362 /* Flush any error message... */
1363 httpFlush (request->http);
1365 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1366 request->state = GTK_CUPS_GET_CONNECT;
1369 httpReconnect (request->http);
1371 /* Upgrade with encryption... */
1372 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1374 request->attempts++;
1377 else if (http_status != HTTP_OK)
1381 http_errno = httpError (request->http);
1383 if (http_errno == EPIPE)
1384 request->state = GTK_CUPS_GET_CONNECT;
1387 request->state = GTK_CUPS_GET_DONE;
1388 gtk_cups_result_set_error (request->result,
1389 GTK_CUPS_ERROR_HTTP,
1392 "HTTP Error in GET %s",
1393 g_strerror (http_errno));
1394 request->poll_state = GTK_CUPS_HTTP_IDLE;
1395 httpFlush (request->http);
1400 request->poll_state = GTK_CUPS_HTTP_IDLE;
1401 httpFlush (request->http);
1402 httpClose (request->http);
1403 request->last_status = HTTP_CONTINUE;
1404 request->http = NULL;
1410 request->state = GTK_CUPS_GET_READ_DATA;
1415 http_status = HTTP_CONTINUE;
1417 if (httpCheck (request->http))
1418 http_status = httpUpdate (request->http);
1420 request->last_status = http_status;
1425 _get_read_data (GtkCupsRequest *request)
1427 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1429 gsize bytes_written;
1430 GIOStatus io_status;
1434 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1438 request->poll_state = GTK_CUPS_HTTP_READ;
1440 bytes = httpRead2 (request->http, buffer, sizeof (buffer));
1441 request->bytes_received += bytes;
1444 g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes));
1447 g_io_channel_write_chars (request->data_io,
1453 if (io_status == G_IO_STATUS_ERROR)
1455 request->state = GTK_CUPS_GET_DONE;
1456 request->poll_state = GTK_CUPS_HTTP_IDLE;
1458 gtk_cups_result_set_error (request->result,
1463 g_error_free (error);
1466 /* Stop if we do not expect any more data or EOF was received. */
1467 if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
1469 request->state = GTK_CUPS_GET_DONE;
1470 request->poll_state = GTK_CUPS_HTTP_IDLE;
1477 gtk_cups_request_is_done (GtkCupsRequest *request)
1479 return (request->state == GTK_CUPS_REQUEST_DONE);
1483 gtk_cups_result_is_error (GtkCupsResult *result)
1485 return result->is_error;
1489 gtk_cups_result_get_response (GtkCupsResult *result)
1491 return result->ipp_response;
1495 gtk_cups_result_get_error_type (GtkCupsResult *result)
1497 return result->error_type;
1501 gtk_cups_result_get_error_status (GtkCupsResult *result)
1503 return result->error_status;
1507 gtk_cups_result_get_error_code (GtkCupsResult *result)
1509 return result->error_code;
1513 gtk_cups_result_get_error_string (GtkCupsResult *result)
1515 return result->error_msg;
1518 /* This function allocates new instance of GtkCupsConnectionTest() and creates
1519 * a socket for communication with a CUPS server 'server'.
1521 GtkCupsConnectionTest *
1522 gtk_cups_connection_test_new (const char *server)
1524 GtkCupsConnectionTest *result = NULL;
1525 gchar *port_str = NULL;
1527 result = g_new (GtkCupsConnectionTest, 1);
1529 port_str = g_strdup_printf ("%d", ippPort ());
1532 result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
1534 result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
1538 result->socket = -1;
1539 result->current_addr = NULL;
1540 result->last_wrong_addr = NULL;
1541 result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1543 result->at_init = gtk_cups_connection_test_get_state (result);
1549 /* A non-blocking test whether it is possible to connect to a CUPS server specified
1550 * inside of GtkCupsConnectionTest structure.
1551 * - you need to check it more then once.
1552 * The connection is closed after a successful connection.
1554 GtkCupsConnectionState
1555 gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
1557 GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1558 http_addrlist_t *iter;
1564 return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1566 if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
1568 test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1569 return GTK_CUPS_CONNECTION_AVAILABLE;
1573 if (test->socket == -1)
1575 if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
1576 iter = test->last_wrong_addr->next;
1579 test->last_wrong_addr = NULL;
1580 iter = test->addrlist;
1585 test->socket = socket (iter->addr.addr.sa_family,
1589 if (test->socket >= 0)
1591 flags = fcntl (test->socket, F_GETFL);
1594 flags |= O_NONBLOCK;
1596 fcntl (test->socket, F_SETFL, flags);
1598 test->current_addr = iter;
1606 if (test->socket >= 0)
1608 code = connect (test->socket,
1609 &test->current_addr->addr.addr,
1610 httpAddrLength (&test->current_addr->addr));
1614 if (code == 0 || error_code == EISCONN)
1616 close (test->socket);
1618 test->current_addr = NULL;
1619 result = GTK_CUPS_CONNECTION_AVAILABLE;
1623 if (error_code == EALREADY || error_code == EINPROGRESS)
1624 result = GTK_CUPS_CONNECTION_IN_PROGRESS;
1627 close (test->socket);
1629 test->last_wrong_addr = test->current_addr;
1630 result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1639 /* This function frees memory used by the GtkCupsConnectionTest structure.
1642 gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
1647 test->current_addr = NULL;
1648 test->last_wrong_addr = NULL;
1649 httpAddrFreeList (test->addrlist);
1650 if (test->socket != -1)
1652 close (test->socket);