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)
258 if (request->type == GTK_CUPS_POST)
259 post_states[request->state] (request);
260 else if (request->type == GTK_CUPS_GET)
261 get_states[request->state] (request);
263 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
264 request->state != GTK_CUPS_REQUEST_DONE)
266 /* TODO: should add a status or error code for too many failed attempts */
267 gtk_cups_result_set_error (request->result,
268 GTK_CUPS_ERROR_GENERAL,
271 "Too many failed attempts");
273 request->state = GTK_CUPS_REQUEST_DONE;
274 request->poll_state = GTK_CUPS_HTTP_IDLE;
277 if (request->state == GTK_CUPS_REQUEST_DONE)
279 request->poll_state = GTK_CUPS_HTTP_IDLE;
287 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
289 return request->poll_state;
295 gtk_cups_request_get_result (GtkCupsRequest *request)
297 return request->result;
301 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
308 ippAddString (request->ipp_request,
317 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
323 const char *const *values)
325 ippAddStrings (request->ipp_request,
335 gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
339 ipp_attribute_t *attribute = NULL;
341 if (request != NULL && request->ipp_request != NULL)
342 attribute = ippFindAttribute (request->ipp_request,
346 if (attribute != NULL && attribute->values != NULL)
347 return attribute->values[0].string.text;
359 static const ipp_option_t ipp_options[] = {
360 { "blackplot", IPP_TAG_BOOLEAN },
361 { "brightness", IPP_TAG_INTEGER },
362 { "columns", IPP_TAG_INTEGER },
363 { "copies", IPP_TAG_INTEGER },
364 { "finishings", IPP_TAG_ENUM },
365 { "fitplot", IPP_TAG_BOOLEAN },
366 { "gamma", IPP_TAG_INTEGER },
367 { "hue", IPP_TAG_INTEGER },
368 { "job-k-limit", IPP_TAG_INTEGER },
369 { "job-page-limit", IPP_TAG_INTEGER },
370 { "job-priority", IPP_TAG_INTEGER },
371 { "job-quota-period", IPP_TAG_INTEGER },
372 { "landscape", IPP_TAG_BOOLEAN },
373 { "media", IPP_TAG_KEYWORD },
374 { "mirror", IPP_TAG_BOOLEAN },
375 { "natural-scaling", IPP_TAG_INTEGER },
376 { "number-up", IPP_TAG_INTEGER },
377 { "orientation-requested", IPP_TAG_ENUM },
378 { "page-bottom", IPP_TAG_INTEGER },
379 { "page-left", IPP_TAG_INTEGER },
380 { "page-ranges", IPP_TAG_RANGE },
381 { "page-right", IPP_TAG_INTEGER },
382 { "page-top", IPP_TAG_INTEGER },
383 { "penwidth", IPP_TAG_INTEGER },
384 { "ppi", IPP_TAG_INTEGER },
385 { "prettyprint", IPP_TAG_BOOLEAN },
386 { "printer-resolution", IPP_TAG_RESOLUTION },
387 { "print-quality", IPP_TAG_ENUM },
388 { "saturation", IPP_TAG_INTEGER },
389 { "scaling", IPP_TAG_INTEGER },
390 { "sides", IPP_TAG_KEYWORD },
391 { "wrap", IPP_TAG_BOOLEAN },
392 { "number-up-layout", IPP_TAG_INTEGER }
397 _find_option_tag (const gchar *option)
399 int lower_bound, upper_bound, num_options;
403 result = IPP_TAG_ZERO;
406 upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
411 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
413 match = strcasecmp (option, ipp_options[current_option].name);
416 result = ipp_options[current_option].value_tag;
421 upper_bound = current_option - 1;
425 lower_bound = current_option + 1;
428 if (upper_bound == lower_bound && upper_bound == current_option)
434 if (lower_bound > num_options)
437 if (upper_bound < lower_bound)
443 * Note that this function uses IPP_TAG_JOB, so it is
444 * only suitable for IPP Group 2 attributes.
448 gtk_cups_request_encode_option (GtkCupsRequest *request,
452 ipp_tag_t option_tag;
454 g_return_if_fail (option != NULL);
455 g_return_if_fail (value != NULL);
457 option_tag = _find_option_tag (option);
459 if (option_tag == IPP_TAG_ZERO)
461 option_tag = IPP_TAG_NAME;
462 if (strcasecmp (value, "true") == 0 ||
463 strcasecmp (value, "false") == 0)
465 option_tag = IPP_TAG_BOOLEAN;
471 case IPP_TAG_INTEGER:
473 ippAddInteger (request->ipp_request,
477 strtol (value, NULL, 0));
480 case IPP_TAG_BOOLEAN:
484 if (strcasecmp (value, "true") == 0 ||
485 strcasecmp (value, "on") == 0 ||
486 strcasecmp (value, "yes") == 0)
491 ippAddBoolean (request->ipp_request,
511 lower = strtol (value, &s, 0);
516 upper = strtol (s + 1, NULL, 0);
523 ippAddRange (request->ipp_request,
532 case IPP_TAG_RESOLUTION:
539 xres = strtol (value, &s, 0);
542 yres = strtol (s + 1, &s, 0);
546 if (strcasecmp (s, "dpc") == 0)
547 units = IPP_RES_PER_CM;
549 units = IPP_RES_PER_INCH;
551 ippAddResolution (request->ipp_request,
569 values = g_strdup (value);
573 for (s = values, next = s; *s != '\0'; s++)
575 if (in_quotes != 2 && *s == '\'')
577 /* skip quoted value */
583 else if (in_quotes != 1 && *s == '\"')
585 /* skip quoted value */
591 else if (in_quotes == 0 && *s == ',')
593 /* found delimiter, add to value array */
596 strings = g_ptr_array_new ();
597 g_ptr_array_add (strings, next);
600 else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
602 /* skip escaped character */
610 ippAddString (request->ipp_request,
619 /* multiple values */
622 g_ptr_array_add (strings, next);
624 ippAddStrings (request->ipp_request,
630 (const char **) strings->pdata);
631 g_ptr_array_free (strings, TRUE);
643 _connect (GtkCupsRequest *request)
645 request->poll_state = GTK_CUPS_HTTP_IDLE;
647 if (request->http == NULL)
649 request->http = httpConnectEncrypt (request->server,
653 if (request->http == NULL)
657 httpBlocking (request->http, 0);
659 request->own_http = TRUE;
663 request->attempts = 0;
666 /* we always write to the socket after we get
668 request->poll_state = GTK_CUPS_HTTP_WRITE;
673 _post_send (GtkCupsRequest *request)
676 struct stat data_info;
679 g_print ("CUPS Backend: %s\n", G_STRFUNC));
681 request->poll_state = GTK_CUPS_HTTP_WRITE;
683 if (request->data_io != NULL)
685 fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
686 sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
689 sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
691 httpClearFields (request->http);
692 httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
693 httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
694 #ifdef HAVE_HTTPGETAUTHSTRING
695 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
697 #ifdef HAVE_HTTP_AUTHSTRING
698 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
702 if (httpPost (request->http, request->resource))
704 if (httpReconnect (request->http))
706 request->state = GTK_CUPS_POST_DONE;
707 request->poll_state = GTK_CUPS_HTTP_IDLE;
709 /* TODO: should add a status or error code for failed post */
710 gtk_cups_result_set_error (request->result,
711 GTK_CUPS_ERROR_GENERAL,
721 request->attempts = 0;
723 request->state = GTK_CUPS_POST_WRITE_REQUEST;
724 request->ipp_request->state = IPP_IDLE;
728 _post_write_request (GtkCupsRequest *request)
730 ipp_state_t ipp_status;
733 g_print ("CUPS Backend: %s\n", G_STRFUNC));
735 request->poll_state = GTK_CUPS_HTTP_WRITE;
737 ipp_status = ippWrite (request->http, request->ipp_request);
739 if (ipp_status == IPP_ERROR)
741 int cups_error = cupsLastError ();
742 request->state = GTK_CUPS_POST_DONE;
743 request->poll_state = GTK_CUPS_HTTP_IDLE;
745 gtk_cups_result_set_error (request->result,
750 ippErrorString (cups_error));
754 if (ipp_status == IPP_DATA)
756 if (request->data_io != NULL)
757 request->state = GTK_CUPS_POST_WRITE_DATA;
760 request->state = GTK_CUPS_POST_CHECK;
761 request->poll_state = GTK_CUPS_HTTP_READ;
767 _post_write_data (GtkCupsRequest *request)
770 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
771 http_status_t http_status;
774 g_print ("CUPS Backend: %s\n", G_STRFUNC));
776 request->poll_state = GTK_CUPS_HTTP_WRITE;
778 if (httpCheck (request->http))
779 http_status = httpUpdate (request->http);
781 http_status = request->last_status;
783 request->last_status = http_status;
786 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
795 g_io_channel_read_chars (request->data_io,
797 _GTK_CUPS_MAX_CHUNK_SIZE,
801 if (io_status == G_IO_STATUS_ERROR)
803 request->state = GTK_CUPS_POST_DONE;
804 request->poll_state = GTK_CUPS_HTTP_IDLE;
806 gtk_cups_result_set_error (request->result,
810 "Error reading from cache file: %s",
813 g_error_free (error);
816 else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
818 request->state = GTK_CUPS_POST_CHECK;
819 request->poll_state = GTK_CUPS_HTTP_READ;
821 request->attempts = 0;
826 #if HAVE_CUPS_API_1_2
827 if (httpWrite2 (request->http, buffer, bytes) < bytes)
829 if (httpWrite (request->http, buffer, (int) bytes) < bytes)
830 #endif /* HAVE_CUPS_API_1_2 */
834 http_errno = httpError (request->http);
836 request->state = GTK_CUPS_POST_DONE;
837 request->poll_state = GTK_CUPS_HTTP_IDLE;
839 gtk_cups_result_set_error (request->result,
843 "Error writing to socket in Post %s",
844 g_strerror (http_errno));
848 else if (http_status == HTTP_UNAUTHORIZED)
850 request->state = GTK_CUPS_POST_CHECK;
851 request->poll_state = GTK_CUPS_HTTP_READ;
853 request->attempts = 0;
863 _post_auth (GtkCupsRequest *request)
865 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
867 if (request->password == NULL)
869 request->state = GTK_CUPS_POST_DONE;
870 request->poll_state = GTK_CUPS_HTTP_IDLE;
872 gtk_cups_result_set_error (request->result,
879 request->state = GTK_CUPS_POST_CHECK;
884 _get_auth (GtkCupsRequest *request)
886 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
888 if (request->password == NULL)
890 request->state = GTK_CUPS_GET_DONE;
891 request->poll_state = GTK_CUPS_HTTP_IDLE;
893 gtk_cups_result_set_error (request->result,
900 request->state = GTK_CUPS_GET_CHECK;
904 /* Very ugly hack: cups has a stupid synchronous password callback
905 * that doesn't even take the request or user data parameters, so
906 * we have to use a static variable to pass the password to it.
908 * The callback sets cups_password to NULL to signal that the
909 * password has been used.
911 static char *cups_password;
912 static char *cups_username;
915 passwordCB (const char *prompt)
917 char *pwd = cups_password;
918 cups_password = NULL;
920 cupsSetUser (cups_username);
926 _post_check (GtkCupsRequest *request)
928 http_status_t http_status;
930 http_status = request->last_status;
933 g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
935 request->poll_state = GTK_CUPS_HTTP_READ;
937 if (http_status == HTTP_CONTINUE)
941 else if (http_status == HTTP_UNAUTHORIZED)
943 int auth_result = -1;
944 httpFlush (request->http);
946 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
948 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
949 request->state = GTK_CUPS_POST_AUTH;
950 request->need_password = TRUE;
956 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
958 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
960 /* Basic, BasicDigest, Digest and PeerCred */
963 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
965 cups_password = g_strdup ("");
966 cups_username = request->username;
967 cupsSetPasswordCB (passwordCB);
969 /* This call success for PeerCred authentication */
970 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
972 if (auth_result != 0)
974 /* move to AUTH state to let the backend
977 request->state = GTK_CUPS_POST_AUTH;
978 request->need_password = TRUE;
985 cups_password = request->password;
986 cups_username = request->username;
988 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
990 if (cups_password != NULL)
993 if (request->password != NULL)
995 memset (request->password, 0, strlen (request->password));
996 g_free (request->password);
997 request->password = NULL;
1000 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1005 httpReconnect (request->http))
1007 /* if the password has been used, reset password_state
1008 * so that we ask for a new one next time around
1010 if (cups_password == NULL)
1011 request->password_state = GTK_CUPS_PASSWORD_NONE;
1013 request->state = GTK_CUPS_POST_DONE;
1014 request->poll_state = GTK_CUPS_HTTP_IDLE;
1015 gtk_cups_result_set_error (request->result,
1016 GTK_CUPS_ERROR_AUTH,
1023 if (request->data_io != NULL)
1024 g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
1026 request->state = GTK_CUPS_POST_CONNECT;
1027 request->poll_state = GTK_CUPS_HTTP_WRITE;
1029 else if (http_status == HTTP_ERROR)
1031 int error = httpError (request->http);
1033 if (error != WSAENETDOWN && error != WSAENETUNREACH)
1035 if (error != ENETDOWN && error != ENETUNREACH)
1036 #endif /* G_OS_WIN32 */
1038 request->attempts++;
1043 request->state = GTK_CUPS_POST_DONE;
1044 request->poll_state = GTK_CUPS_HTTP_IDLE;
1046 gtk_cups_result_set_error (request->result,
1047 GTK_CUPS_ERROR_HTTP,
1050 "Unknown HTTP error");
1055 else if (http_status == HTTP_UPGRADE_REQUIRED)
1057 /* Flush any error message... */
1058 httpFlush (request->http);
1060 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1061 request->state = GTK_CUPS_POST_CONNECT;
1064 httpReconnect (request->http);
1066 /* Upgrade with encryption... */
1067 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1069 request->attempts++;
1072 else if (http_status != HTTP_OK)
1076 http_errno = httpError (request->http);
1078 if (http_errno == EPIPE)
1079 request->state = GTK_CUPS_POST_CONNECT;
1082 request->state = GTK_CUPS_POST_DONE;
1083 gtk_cups_result_set_error (request->result,
1084 GTK_CUPS_ERROR_HTTP,
1087 "HTTP Error in POST %s",
1088 g_strerror (http_errno));
1089 request->poll_state = GTK_CUPS_HTTP_IDLE;
1091 httpFlush (request->http);
1095 request->poll_state = GTK_CUPS_HTTP_IDLE;
1097 httpFlush (request->http);
1099 request->last_status = HTTP_CONTINUE;
1100 httpClose (request->http);
1101 request->http = NULL;
1106 request->state = GTK_CUPS_POST_READ_RESPONSE;
1111 http_status = HTTP_CONTINUE;
1113 if (httpCheck (request->http))
1114 http_status = httpUpdate (request->http);
1116 request->last_status = http_status;
1120 _post_read_response (GtkCupsRequest *request)
1122 ipp_state_t ipp_status;
1125 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1127 request->poll_state = GTK_CUPS_HTTP_READ;
1129 if (request->result->ipp_response == NULL)
1130 request->result->ipp_response = ippNew();
1132 ipp_status = ippRead (request->http,
1133 request->result->ipp_response);
1135 if (ipp_status == IPP_ERROR)
1137 int ipp_error = cupsLastError ();
1138 gtk_cups_result_set_error (request->result,
1143 ippErrorString (ipp_error));
1145 ippDelete (request->result->ipp_response);
1146 request->result->ipp_response = NULL;
1148 request->state = GTK_CUPS_POST_DONE;
1149 request->poll_state = GTK_CUPS_HTTP_IDLE;
1151 else if (ipp_status == IPP_DATA)
1153 request->state = GTK_CUPS_POST_DONE;
1154 request->poll_state = GTK_CUPS_HTTP_IDLE;
1159 _get_send (GtkCupsRequest *request)
1162 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1164 request->poll_state = GTK_CUPS_HTTP_WRITE;
1166 if (request->data_io == NULL)
1168 gtk_cups_result_set_error (request->result,
1171 G_IO_CHANNEL_ERROR_FAILED,
1172 "Get requires an open io channel");
1174 request->state = GTK_CUPS_GET_DONE;
1175 request->poll_state = GTK_CUPS_HTTP_IDLE;
1180 httpClearFields (request->http);
1181 #ifdef HAVE_HTTPGETAUTHSTRING
1182 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
1184 #ifdef HAVE_HTTP_AUTHSTRING
1185 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
1189 if (httpGet (request->http, request->resource))
1191 if (httpReconnect (request->http))
1193 request->state = GTK_CUPS_GET_DONE;
1194 request->poll_state = GTK_CUPS_HTTP_IDLE;
1196 /* TODO: should add a status or error code for failed GET */
1197 gtk_cups_result_set_error (request->result,
1198 GTK_CUPS_ERROR_GENERAL,
1204 request->attempts++;
1208 if (httpCheck (request->http))
1209 request->last_status = httpUpdate (request->http);
1211 request->attempts = 0;
1213 request->state = GTK_CUPS_GET_CHECK;
1214 request->poll_state = GTK_CUPS_HTTP_READ;
1216 request->ipp_request->state = IPP_IDLE;
1220 _get_check (GtkCupsRequest *request)
1222 http_status_t http_status;
1225 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1227 http_status = request->last_status;
1229 request->poll_state = GTK_CUPS_HTTP_READ;
1231 if (http_status == HTTP_CONTINUE)
1235 else if (http_status == HTTP_UNAUTHORIZED)
1237 int auth_result = -1;
1238 httpFlush (request->http);
1240 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
1242 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
1243 request->state = GTK_CUPS_GET_AUTH;
1244 request->need_password = TRUE;
1250 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
1252 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1254 /* Basic, BasicDigest, Digest and PeerCred */
1257 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
1259 cups_password = g_strdup ("");
1260 cups_username = request->username;
1261 cupsSetPasswordCB (passwordCB);
1263 /* This call success for PeerCred authentication */
1264 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1266 if (auth_result != 0)
1268 /* move to AUTH state to let the backend
1269 * ask for a password
1271 request->state = GTK_CUPS_GET_AUTH;
1272 request->need_password = TRUE;
1279 cups_password = request->password;
1280 cups_username = request->username;
1282 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1284 if (cups_password != NULL)
1287 if (request->password != NULL)
1289 memset (request->password, 0, strlen (request->password));
1290 g_free (request->password);
1291 request->password = NULL;
1294 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1299 httpReconnect (request->http))
1301 /* if the password has been used, reset password_state
1302 * so that we ask for a new one next time around
1304 if (cups_password == NULL)
1305 request->password_state = GTK_CUPS_PASSWORD_NONE;
1307 request->state = GTK_CUPS_GET_DONE;
1308 request->poll_state = GTK_CUPS_HTTP_IDLE;
1309 gtk_cups_result_set_error (request->result,
1310 GTK_CUPS_ERROR_AUTH,
1317 request->state = GTK_CUPS_GET_SEND;
1318 request->last_status = HTTP_CONTINUE;
1322 else if (http_status == HTTP_UPGRADE_REQUIRED)
1324 /* Flush any error message... */
1325 httpFlush (request->http);
1327 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1328 request->state = GTK_CUPS_GET_CONNECT;
1331 httpReconnect (request->http);
1333 /* Upgrade with encryption... */
1334 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1336 request->attempts++;
1339 else if (http_status != HTTP_OK)
1343 http_errno = httpError (request->http);
1345 if (http_errno == EPIPE)
1346 request->state = GTK_CUPS_GET_CONNECT;
1349 request->state = GTK_CUPS_GET_DONE;
1350 gtk_cups_result_set_error (request->result,
1351 GTK_CUPS_ERROR_HTTP,
1354 "HTTP Error in GET %s",
1355 g_strerror (http_errno));
1356 request->poll_state = GTK_CUPS_HTTP_IDLE;
1357 httpFlush (request->http);
1362 request->poll_state = GTK_CUPS_HTTP_IDLE;
1363 httpFlush (request->http);
1364 httpClose (request->http);
1365 request->last_status = HTTP_CONTINUE;
1366 request->http = NULL;
1372 request->state = GTK_CUPS_GET_READ_DATA;
1377 http_status = HTTP_CONTINUE;
1379 if (httpCheck (request->http))
1380 http_status = httpUpdate (request->http);
1382 request->last_status = http_status;
1387 _get_read_data (GtkCupsRequest *request)
1389 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1391 gsize bytes_written;
1392 GIOStatus io_status;
1396 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1400 request->poll_state = GTK_CUPS_HTTP_READ;
1402 #if HAVE_CUPS_API_1_2
1403 bytes = httpRead2 (request->http, buffer, sizeof (buffer));
1405 bytes = httpRead (request->http, buffer, sizeof (buffer));
1406 #endif /* HAVE_CUPS_API_1_2 */
1409 g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes));
1413 request->state = GTK_CUPS_GET_DONE;
1414 request->poll_state = GTK_CUPS_HTTP_IDLE;
1420 g_io_channel_write_chars (request->data_io,
1426 if (io_status == G_IO_STATUS_ERROR)
1428 request->state = GTK_CUPS_GET_DONE;
1429 request->poll_state = GTK_CUPS_HTTP_IDLE;
1431 gtk_cups_result_set_error (request->result,
1436 g_error_free (error);
1441 gtk_cups_request_is_done (GtkCupsRequest *request)
1443 return (request->state == GTK_CUPS_REQUEST_DONE);
1447 gtk_cups_result_is_error (GtkCupsResult *result)
1449 return result->is_error;
1453 gtk_cups_result_get_response (GtkCupsResult *result)
1455 return result->ipp_response;
1459 gtk_cups_result_get_error_type (GtkCupsResult *result)
1461 return result->error_type;
1465 gtk_cups_result_get_error_status (GtkCupsResult *result)
1467 return result->error_status;
1471 gtk_cups_result_get_error_code (GtkCupsResult *result)
1473 return result->error_code;
1477 gtk_cups_result_get_error_string (GtkCupsResult *result)
1479 return result->error_msg;
1482 /* This function allocates new instance of GtkCupsConnectionTest() and creates
1483 * a socket for communication with a CUPS server 'server'.
1485 GtkCupsConnectionTest *
1486 gtk_cups_connection_test_new (const char *server)
1488 GtkCupsConnectionTest *result = NULL;
1489 #ifdef HAVE_CUPS_API_1_2
1490 gchar *port_str = NULL;
1492 result = g_new (GtkCupsConnectionTest, 1);
1494 port_str = g_strdup_printf ("%d", ippPort ());
1497 result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
1499 result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
1503 result->socket = -1;
1504 result->current_addr = NULL;
1505 result->last_wrong_addr = NULL;
1506 result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1508 result->at_init = gtk_cups_connection_test_get_state (result);
1510 result = g_new (GtkCupsConnectionTest, 1);
1517 /* A non-blocking test whether it is possible to connect to a CUPS server specified
1518 * inside of GtkCupsConnectionTest structure.
1519 * - you need to check it more then once.
1520 * The connection is closed after a successful connection.
1522 GtkCupsConnectionState
1523 gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
1525 #ifdef HAVE_CUPS_API_1_2
1526 GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1527 http_addrlist_t *iter;
1533 return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1535 if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
1537 test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1538 return GTK_CUPS_CONNECTION_AVAILABLE;
1542 if (test->socket == -1)
1544 if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
1545 iter = test->last_wrong_addr->next;
1548 test->last_wrong_addr = NULL;
1549 iter = test->addrlist;
1554 test->socket = socket (iter->addr.addr.sa_family,
1558 if (test->socket >= 0)
1560 flags = fcntl (test->socket, F_GETFL);
1563 flags |= O_NONBLOCK;
1565 fcntl (test->socket, F_SETFL, flags);
1567 test->current_addr = iter;
1575 if (test->socket >= 0)
1577 code = connect (test->socket,
1578 &test->current_addr->addr.addr,
1579 httpAddrLength (&test->current_addr->addr));
1583 if (code == 0 || error_code == EISCONN)
1585 close (test->socket);
1587 test->current_addr = NULL;
1588 result = GTK_CUPS_CONNECTION_AVAILABLE;
1592 if (error_code == EALREADY || error_code == EINPROGRESS)
1593 result = GTK_CUPS_CONNECTION_IN_PROGRESS;
1596 close (test->socket);
1598 test->last_wrong_addr = test->current_addr;
1599 result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1607 return GTK_CUPS_CONNECTION_AVAILABLE;
1611 /* This function frees memory used by the GtkCupsConnectionTest structure.
1614 gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
1619 #ifdef HAVE_CUPS_API_1_2
1620 test->current_addr = NULL;
1621 test->last_wrong_addr = NULL;
1622 httpAddrFreeList (test->addrlist);
1623 if (test->socket != -1)
1625 close (test->socket);