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, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
24 #include "gtkcupsutils.h"
28 #include <sys/types.h>
33 #include <sys/socket.h>
35 typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
37 static void _connect (GtkCupsRequest *request);
38 static void _post_send (GtkCupsRequest *request);
39 static void _post_write_request (GtkCupsRequest *request);
40 static void _post_write_data (GtkCupsRequest *request);
41 static void _post_check (GtkCupsRequest *request);
42 static void _post_auth (GtkCupsRequest *request);
43 static void _post_read_response (GtkCupsRequest *request);
45 static void _get_send (GtkCupsRequest *request);
46 static void _get_check (GtkCupsRequest *request);
47 static void _get_auth (GtkCupsRequest *request);
48 static void _get_read_data (GtkCupsRequest *request);
54 GtkCupsErrorType error_type;
56 /* some error types like HTTP_ERROR have a status and a code */
61 guint is_ipp_response : 1;
65 #define _GTK_CUPS_MAX_ATTEMPTS 10
66 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
68 static GtkCupsRequestStateFunc post_states[] = {
78 static GtkCupsRequestStateFunc get_states[] = {
87 gtk_cups_result_set_error (GtkCupsResult *result,
88 GtkCupsErrorType error_type,
91 const char *error_msg,
96 result->is_ipp_response = FALSE;
97 result->is_error = TRUE;
98 result->error_type = error_type;
99 result->error_status = error_status;
100 result->error_code = error_code;
102 va_start (args, error_msg);
103 result->error_msg = g_strdup_vprintf (error_msg, args);
108 gtk_cups_request_new_with_username (http_t *connection,
109 GtkCupsRequestType req_type,
113 const char *resource,
114 const char *username)
116 GtkCupsRequest *request;
117 cups_lang_t *language;
119 request = g_new0 (GtkCupsRequest, 1);
120 request->result = g_new0 (GtkCupsResult, 1);
122 request->result->error_msg = NULL;
123 request->result->ipp_response = NULL;
125 request->result->is_error = FALSE;
126 request->result->is_ipp_response = FALSE;
128 request->type = req_type;
129 request->state = GTK_CUPS_REQUEST_START;
131 request->password_state = GTK_CUPS_PASSWORD_NONE;
134 request->server = g_strdup (server);
136 request->server = g_strdup (cupsServer ());
140 request->resource = g_strdup (resource);
142 request->resource = g_strdup ("/");
144 if (connection != NULL)
146 request->http = connection;
147 request->own_http = FALSE;
151 request->http = NULL;
152 request->http = httpConnectEncrypt (request->server,
157 httpBlocking (request->http, 0);
159 request->own_http = TRUE;
162 request->last_status = HTTP_CONTINUE;
164 request->attempts = 0;
165 request->data_io = data_io;
167 request->ipp_request = ippNew ();
168 request->ipp_request->request.op.operation_id = operation_id;
169 request->ipp_request->request.op.request_id = 1;
171 language = cupsLangDefault ();
173 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
174 "attributes-charset",
177 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
178 "attributes-natural-language",
179 NULL, language->language);
181 if (username != NULL)
182 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
183 "requesting-user-name",
186 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
187 "requesting-user-name",
190 request->auth_info_required = NULL;
191 request->auth_info = NULL;
192 request->need_auth_info = FALSE;
194 cupsLangFree (language);
200 gtk_cups_request_new (http_t *connection,
201 GtkCupsRequestType req_type,
205 const char *resource)
207 return gtk_cups_request_new_with_username (connection,
217 gtk_cups_result_free (GtkCupsResult *result)
219 g_free (result->error_msg);
221 if (result->ipp_response)
222 ippDelete (result->ipp_response);
228 gtk_cups_request_free (GtkCupsRequest *request)
230 if (request->own_http)
233 httpClose (request->http);
236 if (request->ipp_request)
237 ippDelete (request->ipp_request);
239 g_free (request->server);
240 g_free (request->resource);
241 if (request->password != NULL)
243 memset (request->password, 0, strlen (request->password));
244 g_free (request->password);
247 g_free (request->username);
248 g_strfreev (request->auth_info_required);
250 gtk_cups_result_free (request->result);
256 gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
258 if (connect_only && request->state != GTK_CUPS_REQUEST_START)
263 if (request->type == GTK_CUPS_POST)
264 post_states[request->state] (request);
265 else if (request->type == GTK_CUPS_GET)
266 get_states[request->state] (request);
268 if (gtk_cups_result_is_error (request->result))
269 request->state = GTK_CUPS_REQUEST_DONE;
271 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
272 request->state != GTK_CUPS_REQUEST_DONE)
274 /* TODO: should add a status or error code for too many failed attempts */
275 gtk_cups_result_set_error (request->result,
276 GTK_CUPS_ERROR_GENERAL,
279 "Too many failed attempts");
281 request->state = GTK_CUPS_REQUEST_DONE;
284 if (request->state == GTK_CUPS_REQUEST_DONE)
286 request->poll_state = GTK_CUPS_HTTP_IDLE;
290 /* We need to recheck using httpCheck if the poll_state is read, because
291 * Cups has an internal read buffer. And if this buffer is filled, we may
292 * never get a poll event again. */
293 while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http));
299 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
301 return request->poll_state;
307 gtk_cups_request_get_result (GtkCupsRequest *request)
309 return request->result;
313 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
320 ippAddString (request->ipp_request,
329 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
335 const char *const *values)
337 ippAddStrings (request->ipp_request,
347 gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
351 ipp_attribute_t *attribute = NULL;
353 if (request != NULL && request->ipp_request != NULL)
354 attribute = ippFindAttribute (request->ipp_request,
358 if (attribute != NULL && attribute->values != NULL)
359 return attribute->values[0].string.text;
371 static const ipp_option_t ipp_options[] = {
372 { "blackplot", IPP_TAG_BOOLEAN },
373 { "brightness", IPP_TAG_INTEGER },
374 { "columns", IPP_TAG_INTEGER },
375 { "copies", IPP_TAG_INTEGER },
376 { "finishings", IPP_TAG_ENUM },
377 { "fitplot", IPP_TAG_BOOLEAN },
378 { "gamma", IPP_TAG_INTEGER },
379 { "hue", IPP_TAG_INTEGER },
380 { "job-k-limit", IPP_TAG_INTEGER },
381 { "job-page-limit", IPP_TAG_INTEGER },
382 { "job-priority", IPP_TAG_INTEGER },
383 { "job-quota-period", IPP_TAG_INTEGER },
384 { "landscape", IPP_TAG_BOOLEAN },
385 { "media", IPP_TAG_KEYWORD },
386 { "mirror", IPP_TAG_BOOLEAN },
387 { "natural-scaling", IPP_TAG_INTEGER },
388 { "number-up", IPP_TAG_INTEGER },
389 { "orientation-requested", IPP_TAG_ENUM },
390 { "page-bottom", IPP_TAG_INTEGER },
391 { "page-left", IPP_TAG_INTEGER },
392 { "page-ranges", IPP_TAG_RANGE },
393 { "page-right", IPP_TAG_INTEGER },
394 { "page-top", IPP_TAG_INTEGER },
395 { "penwidth", IPP_TAG_INTEGER },
396 { "ppi", IPP_TAG_INTEGER },
397 { "prettyprint", IPP_TAG_BOOLEAN },
398 { "printer-resolution", IPP_TAG_RESOLUTION },
399 { "print-quality", IPP_TAG_ENUM },
400 { "saturation", IPP_TAG_INTEGER },
401 { "scaling", IPP_TAG_INTEGER },
402 { "sides", IPP_TAG_KEYWORD },
403 { "wrap", IPP_TAG_BOOLEAN },
404 { "number-up-layout", IPP_TAG_INTEGER }
409 _find_option_tag (const gchar *option)
411 int lower_bound, upper_bound, num_options;
415 result = IPP_TAG_ZERO;
418 upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
423 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
425 match = strcasecmp (option, ipp_options[current_option].name);
428 result = ipp_options[current_option].value_tag;
433 upper_bound = current_option - 1;
437 lower_bound = current_option + 1;
440 if (upper_bound == lower_bound && upper_bound == current_option)
446 if (lower_bound > num_options)
449 if (upper_bound < lower_bound)
455 * Note that this function uses IPP_TAG_JOB, so it is
456 * only suitable for IPP Group 2 attributes.
460 gtk_cups_request_encode_option (GtkCupsRequest *request,
464 ipp_tag_t option_tag;
466 g_return_if_fail (option != NULL);
467 g_return_if_fail (value != NULL);
469 option_tag = _find_option_tag (option);
471 if (option_tag == IPP_TAG_ZERO)
473 option_tag = IPP_TAG_NAME;
474 if (strcasecmp (value, "true") == 0 ||
475 strcasecmp (value, "false") == 0)
477 option_tag = IPP_TAG_BOOLEAN;
483 case IPP_TAG_INTEGER:
485 ippAddInteger (request->ipp_request,
489 strtol (value, NULL, 0));
492 case IPP_TAG_BOOLEAN:
496 if (strcasecmp (value, "true") == 0 ||
497 strcasecmp (value, "on") == 0 ||
498 strcasecmp (value, "yes") == 0)
503 ippAddBoolean (request->ipp_request,
523 lower = strtol (value, &s, 0);
528 upper = strtol (s + 1, NULL, 0);
535 ippAddRange (request->ipp_request,
544 case IPP_TAG_RESOLUTION:
551 xres = strtol (value, &s, 0);
554 yres = strtol (s + 1, &s, 0);
558 if (strcasecmp (s, "dpc") == 0)
559 units = IPP_RES_PER_CM;
561 units = IPP_RES_PER_INCH;
563 ippAddResolution (request->ipp_request,
581 values = g_strdup (value);
585 for (s = values, next = s; *s != '\0'; s++)
587 if (in_quotes != 2 && *s == '\'')
589 /* skip quoted value */
595 else if (in_quotes != 1 && *s == '\"')
597 /* skip quoted value */
603 else if (in_quotes == 0 && *s == ',')
605 /* found delimiter, add to value array */
608 strings = g_ptr_array_new ();
609 g_ptr_array_add (strings, next);
612 else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
614 /* skip escaped character */
622 ippAddString (request->ipp_request,
631 /* multiple values */
634 g_ptr_array_add (strings, next);
636 ippAddStrings (request->ipp_request,
642 (const char **) strings->pdata);
643 g_ptr_array_free (strings, TRUE);
655 _connect (GtkCupsRequest *request)
657 request->poll_state = GTK_CUPS_HTTP_IDLE;
658 request->bytes_received = 0;
660 if (request->http == NULL)
662 request->http = httpConnectEncrypt (request->server,
666 if (request->http == NULL)
670 httpBlocking (request->http, 0);
672 request->own_http = TRUE;
676 request->attempts = 0;
679 /* we always write to the socket after we get
681 request->poll_state = GTK_CUPS_HTTP_WRITE;
686 _post_send (GtkCupsRequest *request)
689 struct stat data_info;
692 g_print ("CUPS Backend: %s\n", G_STRFUNC));
694 request->poll_state = GTK_CUPS_HTTP_WRITE;
696 if (request->data_io != NULL)
698 fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
699 sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
702 sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
704 httpClearFields (request->http);
705 httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
706 httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
707 #ifdef HAVE_HTTPGETAUTHSTRING
708 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
710 #ifdef HAVE_HTTP_AUTHSTRING
711 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
715 if (httpPost (request->http, request->resource))
717 if (httpReconnect (request->http))
719 request->state = GTK_CUPS_POST_DONE;
720 request->poll_state = GTK_CUPS_HTTP_IDLE;
722 /* TODO: should add a status or error code for failed post */
723 gtk_cups_result_set_error (request->result,
724 GTK_CUPS_ERROR_GENERAL,
734 request->attempts = 0;
736 request->state = GTK_CUPS_POST_WRITE_REQUEST;
737 request->ipp_request->state = IPP_IDLE;
741 _post_write_request (GtkCupsRequest *request)
743 ipp_state_t ipp_status;
746 g_print ("CUPS Backend: %s\n", G_STRFUNC));
748 request->poll_state = GTK_CUPS_HTTP_WRITE;
750 ipp_status = ippWrite (request->http, request->ipp_request);
752 if (ipp_status == IPP_ERROR)
754 int cups_error = cupsLastError ();
755 request->state = GTK_CUPS_POST_DONE;
756 request->poll_state = GTK_CUPS_HTTP_IDLE;
758 gtk_cups_result_set_error (request->result,
763 ippErrorString (cups_error));
767 if (ipp_status == IPP_DATA)
769 if (request->data_io != NULL)
770 request->state = GTK_CUPS_POST_WRITE_DATA;
773 request->state = GTK_CUPS_POST_CHECK;
774 request->poll_state = GTK_CUPS_HTTP_READ;
780 _post_write_data (GtkCupsRequest *request)
783 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
784 http_status_t http_status;
787 g_print ("CUPS Backend: %s\n", G_STRFUNC));
789 request->poll_state = GTK_CUPS_HTTP_WRITE;
791 if (httpCheck (request->http))
792 http_status = httpUpdate (request->http);
794 http_status = request->last_status;
796 request->last_status = http_status;
799 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
808 g_io_channel_read_chars (request->data_io,
810 _GTK_CUPS_MAX_CHUNK_SIZE,
814 if (io_status == G_IO_STATUS_ERROR)
816 request->state = GTK_CUPS_POST_DONE;
817 request->poll_state = GTK_CUPS_HTTP_IDLE;
819 gtk_cups_result_set_error (request->result,
823 "Error reading from cache file: %s",
826 g_error_free (error);
829 else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
831 request->state = GTK_CUPS_POST_CHECK;
832 request->poll_state = GTK_CUPS_HTTP_READ;
834 request->attempts = 0;
839 #if HAVE_CUPS_API_1_2
840 if (httpWrite2 (request->http, buffer, bytes) < bytes)
842 if (httpWrite (request->http, buffer, (int) bytes) < bytes)
843 #endif /* HAVE_CUPS_API_1_2 */
847 http_errno = httpError (request->http);
849 request->state = GTK_CUPS_POST_DONE;
850 request->poll_state = GTK_CUPS_HTTP_IDLE;
852 gtk_cups_result_set_error (request->result,
856 "Error writing to socket in Post %s",
857 g_strerror (http_errno));
861 else if (http_status == HTTP_UNAUTHORIZED)
863 request->state = GTK_CUPS_POST_CHECK;
864 request->poll_state = GTK_CUPS_HTTP_READ;
866 request->attempts = 0;
876 _post_auth (GtkCupsRequest *request)
878 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
880 if (request->password == NULL)
882 request->state = GTK_CUPS_POST_DONE;
883 request->poll_state = GTK_CUPS_HTTP_IDLE;
885 gtk_cups_result_set_error (request->result,
892 request->state = GTK_CUPS_POST_CHECK;
897 _get_auth (GtkCupsRequest *request)
899 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
901 if (request->password == NULL)
903 request->state = GTK_CUPS_GET_DONE;
904 request->poll_state = GTK_CUPS_HTTP_IDLE;
906 gtk_cups_result_set_error (request->result,
913 request->state = GTK_CUPS_GET_CHECK;
917 /* Very ugly hack: cups has a stupid synchronous password callback
918 * that doesn't even take the request or user data parameters, so
919 * we have to use a static variable to pass the password to it.
921 * The callback sets cups_password to NULL to signal that the
922 * password has been used.
924 static char *cups_password = NULL;
925 static char *cups_username = NULL;
928 passwordCB (const char *prompt)
930 char *pwd = cups_password;
931 cups_password = NULL;
933 cupsSetUser (cups_username);
939 _post_check (GtkCupsRequest *request)
941 http_status_t http_status;
943 http_status = request->last_status;
946 g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
948 request->poll_state = GTK_CUPS_HTTP_READ;
950 if (http_status == HTTP_CONTINUE)
954 else if (http_status == HTTP_UNAUTHORIZED)
956 int auth_result = -1;
957 httpFlush (request->http);
959 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
961 request->poll_state = GTK_CUPS_HTTP_IDLE;
962 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
963 request->state = GTK_CUPS_POST_AUTH;
964 request->need_password = TRUE;
970 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
972 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
974 /* Basic, BasicDigest, Digest and PeerCred */
977 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
979 cups_username = request->username;
980 cupsSetPasswordCB (passwordCB);
982 /* This call success for PeerCred authentication */
983 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
985 if (auth_result != 0)
987 /* move to AUTH state to let the backend
990 request->poll_state = GTK_CUPS_HTTP_IDLE;
991 request->state = GTK_CUPS_POST_AUTH;
992 request->need_password = TRUE;
999 cups_password = request->password;
1000 cups_username = request->username;
1002 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
1004 if (cups_password != NULL)
1007 if (request->password != NULL)
1009 memset (request->password, 0, strlen (request->password));
1010 g_free (request->password);
1011 request->password = NULL;
1014 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1019 httpReconnect (request->http))
1021 /* if the password has been used, reset password_state
1022 * so that we ask for a new one next time around
1024 if (cups_password == NULL)
1025 request->password_state = GTK_CUPS_PASSWORD_NONE;
1027 request->state = GTK_CUPS_POST_DONE;
1028 request->poll_state = GTK_CUPS_HTTP_IDLE;
1029 gtk_cups_result_set_error (request->result,
1030 GTK_CUPS_ERROR_AUTH,
1037 if (request->data_io != NULL)
1038 g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
1040 request->state = GTK_CUPS_POST_CONNECT;
1041 request->poll_state = GTK_CUPS_HTTP_WRITE;
1043 else if (http_status == HTTP_ERROR)
1045 int error = httpError (request->http);
1047 if (error != WSAENETDOWN && error != WSAENETUNREACH)
1049 if (error != ENETDOWN && error != ENETUNREACH)
1050 #endif /* G_OS_WIN32 */
1052 request->attempts++;
1057 request->state = GTK_CUPS_POST_DONE;
1058 request->poll_state = GTK_CUPS_HTTP_IDLE;
1060 gtk_cups_result_set_error (request->result,
1061 GTK_CUPS_ERROR_HTTP,
1064 "Unknown HTTP error");
1069 else if (http_status == HTTP_UPGRADE_REQUIRED)
1071 /* Flush any error message... */
1072 httpFlush (request->http);
1074 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1075 request->state = GTK_CUPS_POST_CONNECT;
1078 httpReconnect (request->http);
1080 /* Upgrade with encryption... */
1081 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1083 request->attempts++;
1086 else if (http_status != HTTP_OK)
1090 http_errno = httpError (request->http);
1092 if (http_errno == EPIPE)
1093 request->state = GTK_CUPS_POST_CONNECT;
1096 request->state = GTK_CUPS_POST_DONE;
1097 gtk_cups_result_set_error (request->result,
1098 GTK_CUPS_ERROR_HTTP,
1101 "HTTP Error in POST %s",
1102 g_strerror (http_errno));
1103 request->poll_state = GTK_CUPS_HTTP_IDLE;
1105 httpFlush (request->http);
1109 request->poll_state = GTK_CUPS_HTTP_IDLE;
1111 httpFlush (request->http);
1113 request->last_status = HTTP_CONTINUE;
1114 httpClose (request->http);
1115 request->http = NULL;
1120 request->state = GTK_CUPS_POST_READ_RESPONSE;
1125 http_status = HTTP_CONTINUE;
1127 if (httpCheck (request->http))
1128 http_status = httpUpdate (request->http);
1130 request->last_status = http_status;
1134 _post_read_response (GtkCupsRequest *request)
1136 ipp_state_t ipp_status;
1139 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1141 request->poll_state = GTK_CUPS_HTTP_READ;
1143 if (request->result->ipp_response == NULL)
1144 request->result->ipp_response = ippNew();
1146 ipp_status = ippRead (request->http,
1147 request->result->ipp_response);
1149 if (ipp_status == IPP_ERROR)
1151 int ipp_error = cupsLastError ();
1152 gtk_cups_result_set_error (request->result,
1157 ippErrorString (ipp_error));
1159 ippDelete (request->result->ipp_response);
1160 request->result->ipp_response = NULL;
1162 request->state = GTK_CUPS_POST_DONE;
1163 request->poll_state = GTK_CUPS_HTTP_IDLE;
1165 else if (ipp_status == IPP_DATA)
1167 request->state = GTK_CUPS_POST_DONE;
1168 request->poll_state = GTK_CUPS_HTTP_IDLE;
1173 _get_send (GtkCupsRequest *request)
1176 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1178 request->poll_state = GTK_CUPS_HTTP_WRITE;
1180 if (request->data_io == NULL)
1182 gtk_cups_result_set_error (request->result,
1185 G_IO_CHANNEL_ERROR_FAILED,
1186 "Get requires an open io channel");
1188 request->state = GTK_CUPS_GET_DONE;
1189 request->poll_state = GTK_CUPS_HTTP_IDLE;
1194 httpClearFields (request->http);
1195 #ifdef HAVE_HTTPGETAUTHSTRING
1196 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
1198 #ifdef HAVE_HTTP_AUTHSTRING
1199 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
1203 if (httpGet (request->http, request->resource))
1205 if (httpReconnect (request->http))
1207 request->state = GTK_CUPS_GET_DONE;
1208 request->poll_state = GTK_CUPS_HTTP_IDLE;
1210 /* TODO: should add a status or error code for failed GET */
1211 gtk_cups_result_set_error (request->result,
1212 GTK_CUPS_ERROR_GENERAL,
1218 request->attempts++;
1222 if (httpCheck (request->http))
1223 request->last_status = httpUpdate (request->http);
1225 request->attempts = 0;
1227 request->state = GTK_CUPS_GET_CHECK;
1228 request->poll_state = GTK_CUPS_HTTP_READ;
1230 request->ipp_request->state = IPP_IDLE;
1234 _get_check (GtkCupsRequest *request)
1236 http_status_t http_status;
1239 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1241 http_status = request->last_status;
1243 request->poll_state = GTK_CUPS_HTTP_READ;
1245 if (http_status == HTTP_CONTINUE)
1249 else if (http_status == HTTP_UNAUTHORIZED)
1251 int auth_result = -1;
1252 httpFlush (request->http);
1254 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
1256 request->poll_state = GTK_CUPS_HTTP_IDLE;
1257 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
1258 request->state = GTK_CUPS_GET_AUTH;
1259 request->need_password = TRUE;
1265 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
1267 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1269 /* Basic, BasicDigest, Digest and PeerCred */
1272 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
1274 cups_username = request->username;
1275 cupsSetPasswordCB (passwordCB);
1277 /* This call success for PeerCred authentication */
1278 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1280 if (auth_result != 0)
1282 /* move to AUTH state to let the backend
1283 * ask for a password
1285 request->poll_state = GTK_CUPS_HTTP_IDLE;
1286 request->state = GTK_CUPS_GET_AUTH;
1287 request->need_password = TRUE;
1294 cups_password = request->password;
1295 cups_username = request->username;
1297 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1299 if (cups_password != NULL)
1302 if (request->password != NULL)
1304 memset (request->password, 0, strlen (request->password));
1305 g_free (request->password);
1306 request->password = NULL;
1309 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1314 httpReconnect (request->http))
1316 /* if the password has been used, reset password_state
1317 * so that we ask for a new one next time around
1319 if (cups_password == NULL)
1320 request->password_state = GTK_CUPS_PASSWORD_NONE;
1322 request->state = GTK_CUPS_GET_DONE;
1323 request->poll_state = GTK_CUPS_HTTP_IDLE;
1324 gtk_cups_result_set_error (request->result,
1325 GTK_CUPS_ERROR_AUTH,
1332 request->state = GTK_CUPS_GET_CONNECT;
1333 request->last_status = HTTP_CONTINUE;
1337 else if (http_status == HTTP_UPGRADE_REQUIRED)
1339 /* Flush any error message... */
1340 httpFlush (request->http);
1342 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1343 request->state = GTK_CUPS_GET_CONNECT;
1346 httpReconnect (request->http);
1348 /* Upgrade with encryption... */
1349 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1351 request->attempts++;
1354 else if (http_status != HTTP_OK)
1358 http_errno = httpError (request->http);
1360 if (http_errno == EPIPE)
1361 request->state = GTK_CUPS_GET_CONNECT;
1364 request->state = GTK_CUPS_GET_DONE;
1365 gtk_cups_result_set_error (request->result,
1366 GTK_CUPS_ERROR_HTTP,
1369 "HTTP Error in GET %s",
1370 g_strerror (http_errno));
1371 request->poll_state = GTK_CUPS_HTTP_IDLE;
1372 httpFlush (request->http);
1377 request->poll_state = GTK_CUPS_HTTP_IDLE;
1378 httpFlush (request->http);
1379 httpClose (request->http);
1380 request->last_status = HTTP_CONTINUE;
1381 request->http = NULL;
1387 request->state = GTK_CUPS_GET_READ_DATA;
1392 http_status = HTTP_CONTINUE;
1394 if (httpCheck (request->http))
1395 http_status = httpUpdate (request->http);
1397 request->last_status = http_status;
1402 _get_read_data (GtkCupsRequest *request)
1404 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1406 gsize bytes_written;
1407 GIOStatus io_status;
1411 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1415 request->poll_state = GTK_CUPS_HTTP_READ;
1417 #if HAVE_CUPS_API_1_2
1418 bytes = httpRead2 (request->http, buffer, sizeof (buffer));
1420 bytes = httpRead (request->http, buffer, sizeof (buffer));
1421 #endif /* HAVE_CUPS_API_1_2 */
1422 request->bytes_received += bytes;
1425 g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes));
1428 g_io_channel_write_chars (request->data_io,
1434 if (io_status == G_IO_STATUS_ERROR)
1436 request->state = GTK_CUPS_GET_DONE;
1437 request->poll_state = GTK_CUPS_HTTP_IDLE;
1439 gtk_cups_result_set_error (request->result,
1444 g_error_free (error);
1447 /* Stop if we do not expect any more data or EOF was received. */
1448 #if HAVE_CUPS_API_1_2
1449 if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
1451 if (httpGetLength (request->http) <= request->bytes_received || bytes == 0)
1452 #endif /* HAVE_CUPS_API_1_2 */
1454 request->state = GTK_CUPS_GET_DONE;
1455 request->poll_state = GTK_CUPS_HTTP_IDLE;
1462 gtk_cups_request_is_done (GtkCupsRequest *request)
1464 return (request->state == GTK_CUPS_REQUEST_DONE);
1468 gtk_cups_result_is_error (GtkCupsResult *result)
1470 return result->is_error;
1474 gtk_cups_result_get_response (GtkCupsResult *result)
1476 return result->ipp_response;
1480 gtk_cups_result_get_error_type (GtkCupsResult *result)
1482 return result->error_type;
1486 gtk_cups_result_get_error_status (GtkCupsResult *result)
1488 return result->error_status;
1492 gtk_cups_result_get_error_code (GtkCupsResult *result)
1494 return result->error_code;
1498 gtk_cups_result_get_error_string (GtkCupsResult *result)
1500 return result->error_msg;
1503 /* This function allocates new instance of GtkCupsConnectionTest() and creates
1504 * a socket for communication with a CUPS server 'server'.
1506 GtkCupsConnectionTest *
1507 gtk_cups_connection_test_new (const char *server)
1509 GtkCupsConnectionTest *result = NULL;
1510 #ifdef HAVE_CUPS_API_1_2
1511 gchar *port_str = NULL;
1513 result = g_new (GtkCupsConnectionTest, 1);
1515 port_str = g_strdup_printf ("%d", ippPort ());
1518 result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
1520 result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
1524 result->socket = -1;
1525 result->current_addr = NULL;
1526 result->last_wrong_addr = NULL;
1527 result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1529 result->at_init = gtk_cups_connection_test_get_state (result);
1531 result = g_new (GtkCupsConnectionTest, 1);
1538 /* A non-blocking test whether it is possible to connect to a CUPS server specified
1539 * inside of GtkCupsConnectionTest structure.
1540 * - you need to check it more then once.
1541 * The connection is closed after a successful connection.
1543 GtkCupsConnectionState
1544 gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
1546 #ifdef HAVE_CUPS_API_1_2
1547 GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1548 http_addrlist_t *iter;
1554 return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1556 if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
1558 test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1559 return GTK_CUPS_CONNECTION_AVAILABLE;
1563 if (test->socket == -1)
1565 if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
1566 iter = test->last_wrong_addr->next;
1569 test->last_wrong_addr = NULL;
1570 iter = test->addrlist;
1575 test->socket = socket (iter->addr.addr.sa_family,
1579 if (test->socket >= 0)
1581 flags = fcntl (test->socket, F_GETFL);
1584 flags |= O_NONBLOCK;
1586 fcntl (test->socket, F_SETFL, flags);
1588 test->current_addr = iter;
1596 if (test->socket >= 0)
1598 code = connect (test->socket,
1599 &test->current_addr->addr.addr,
1600 httpAddrLength (&test->current_addr->addr));
1604 if (code == 0 || error_code == EISCONN)
1606 close (test->socket);
1608 test->current_addr = NULL;
1609 result = GTK_CUPS_CONNECTION_AVAILABLE;
1613 if (error_code == EALREADY || error_code == EINPROGRESS)
1614 result = GTK_CUPS_CONNECTION_IN_PROGRESS;
1617 close (test->socket);
1619 test->last_wrong_addr = test->current_addr;
1620 result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1628 return GTK_CUPS_CONNECTION_AVAILABLE;
1632 /* This function frees memory used by the GtkCupsConnectionTest structure.
1635 gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
1640 #ifdef HAVE_CUPS_API_1_2
1641 test->current_addr = NULL;
1642 test->last_wrong_addr = NULL;
1643 httpAddrFreeList (test->addrlist);
1644 if (test->socket != -1)
1646 close (test->socket);