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[] = {
85 gtk_cups_result_set_error (GtkCupsResult *result,
86 GtkCupsErrorType error_type,
89 const char *error_msg,
94 result->is_ipp_response = FALSE;
95 result->is_error = TRUE;
96 result->error_type = error_type;
97 result->error_status = error_status;
98 result->error_code = error_code;
100 va_start (args, error_msg);
101 result->error_msg = g_strdup_vprintf (error_msg, args);
106 gtk_cups_request_new_with_username (http_t *connection,
107 GtkCupsRequestType req_type,
111 const char *resource,
112 const char *username)
114 GtkCupsRequest *request;
115 cups_lang_t *language;
117 request = g_new0 (GtkCupsRequest, 1);
118 request->result = g_new0 (GtkCupsResult, 1);
120 request->result->error_msg = NULL;
121 request->result->ipp_response = NULL;
123 request->result->is_error = FALSE;
124 request->result->is_ipp_response = FALSE;
126 request->type = req_type;
127 request->state = GTK_CUPS_REQUEST_START;
129 request->password_state = GTK_CUPS_PASSWORD_NONE;
132 request->server = g_strdup (server);
134 request->server = g_strdup (cupsServer ());
138 request->resource = g_strdup (resource);
140 request->resource = g_strdup ("/");
142 if (connection != NULL)
144 request->http = connection;
145 request->own_http = FALSE;
149 request->http = NULL;
150 request->http = httpConnectEncrypt (request->server,
155 httpBlocking (request->http, 0);
157 request->own_http = TRUE;
160 request->last_status = HTTP_CONTINUE;
162 request->attempts = 0;
163 request->data_io = data_io;
165 request->ipp_request = ippNew ();
166 request->ipp_request->request.op.operation_id = operation_id;
167 request->ipp_request->request.op.request_id = 1;
169 language = cupsLangDefault ();
171 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
172 "attributes-charset",
175 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
176 "attributes-natural-language",
177 NULL, language->language);
179 if (username != NULL)
180 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
181 "requesting-user-name",
184 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
185 "requesting-user-name",
188 request->auth_info_required = NULL;
189 request->auth_info = NULL;
190 request->need_auth_info = FALSE;
192 cupsLangFree (language);
198 gtk_cups_request_new (http_t *connection,
199 GtkCupsRequestType req_type,
203 const char *resource)
205 return gtk_cups_request_new_with_username (connection,
215 gtk_cups_result_free (GtkCupsResult *result)
217 g_free (result->error_msg);
219 if (result->ipp_response)
220 ippDelete (result->ipp_response);
226 gtk_cups_request_free (GtkCupsRequest *request)
228 if (request->own_http)
231 httpClose (request->http);
234 if (request->ipp_request)
235 ippDelete (request->ipp_request);
237 g_free (request->server);
238 g_free (request->resource);
239 if (request->password != NULL)
241 memset (request->password, 0, strlen (request->password));
242 g_free (request->password);
245 g_free (request->username);
246 g_strfreev (request->auth_info_required);
248 gtk_cups_result_free (request->result);
254 gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
256 if (connect_only && request->state != GTK_CUPS_REQUEST_START)
261 if (request->type == GTK_CUPS_POST)
262 post_states[request->state] (request);
263 else if (request->type == GTK_CUPS_GET)
264 get_states[request->state] (request);
266 if (gtk_cups_result_is_error (request->result))
267 request->state = GTK_CUPS_REQUEST_DONE;
269 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
270 request->state != GTK_CUPS_REQUEST_DONE)
272 /* TODO: should add a status or error code for too many failed attempts */
273 gtk_cups_result_set_error (request->result,
274 GTK_CUPS_ERROR_GENERAL,
277 "Too many failed attempts");
279 request->state = GTK_CUPS_REQUEST_DONE;
282 if (request->state == GTK_CUPS_REQUEST_DONE)
284 request->poll_state = GTK_CUPS_HTTP_IDLE;
288 /* We need to recheck using httpCheck if the poll_state is read, because
289 * Cups has an internal read buffer. And if this buffer is filled, we may
290 * never get a poll event again. */
291 while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http));
297 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
299 return request->poll_state;
305 gtk_cups_request_get_result (GtkCupsRequest *request)
307 return request->result;
311 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
318 ippAddString (request->ipp_request,
327 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
333 const char *const *values)
335 ippAddStrings (request->ipp_request,
345 gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
349 ipp_attribute_t *attribute = NULL;
351 if (request != NULL && request->ipp_request != NULL)
352 attribute = ippFindAttribute (request->ipp_request,
356 if (attribute != NULL && attribute->values != NULL)
357 return attribute->values[0].string.text;
369 static const ipp_option_t ipp_options[] = {
370 { "blackplot", IPP_TAG_BOOLEAN },
371 { "brightness", IPP_TAG_INTEGER },
372 { "columns", IPP_TAG_INTEGER },
373 { "copies", IPP_TAG_INTEGER },
374 { "finishings", IPP_TAG_ENUM },
375 { "fitplot", IPP_TAG_BOOLEAN },
376 { "gamma", IPP_TAG_INTEGER },
377 { "hue", IPP_TAG_INTEGER },
378 { "job-k-limit", IPP_TAG_INTEGER },
379 { "job-page-limit", IPP_TAG_INTEGER },
380 { "job-priority", IPP_TAG_INTEGER },
381 { "job-quota-period", IPP_TAG_INTEGER },
382 { "landscape", IPP_TAG_BOOLEAN },
383 { "media", IPP_TAG_KEYWORD },
384 { "mirror", IPP_TAG_BOOLEAN },
385 { "natural-scaling", IPP_TAG_INTEGER },
386 { "number-up", IPP_TAG_INTEGER },
387 { "orientation-requested", IPP_TAG_ENUM },
388 { "page-bottom", IPP_TAG_INTEGER },
389 { "page-left", IPP_TAG_INTEGER },
390 { "page-ranges", IPP_TAG_RANGE },
391 { "page-right", IPP_TAG_INTEGER },
392 { "page-top", IPP_TAG_INTEGER },
393 { "penwidth", IPP_TAG_INTEGER },
394 { "ppi", IPP_TAG_INTEGER },
395 { "prettyprint", IPP_TAG_BOOLEAN },
396 { "printer-resolution", IPP_TAG_RESOLUTION },
397 { "print-quality", IPP_TAG_ENUM },
398 { "saturation", IPP_TAG_INTEGER },
399 { "scaling", IPP_TAG_INTEGER },
400 { "sides", IPP_TAG_KEYWORD },
401 { "wrap", IPP_TAG_BOOLEAN },
402 { "number-up-layout", IPP_TAG_INTEGER }
407 _find_option_tag (const gchar *option)
409 int lower_bound, upper_bound, num_options;
413 result = IPP_TAG_ZERO;
416 upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
421 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
423 match = strcasecmp (option, ipp_options[current_option].name);
426 result = ipp_options[current_option].value_tag;
431 upper_bound = current_option - 1;
435 lower_bound = current_option + 1;
438 if (upper_bound == lower_bound && upper_bound == current_option)
444 if (lower_bound > num_options)
447 if (upper_bound < lower_bound)
453 * Note that this function uses IPP_TAG_JOB, so it is
454 * only suitable for IPP Group 2 attributes.
458 gtk_cups_request_encode_option (GtkCupsRequest *request,
462 ipp_tag_t option_tag;
464 g_return_if_fail (option != NULL);
465 g_return_if_fail (value != NULL);
467 option_tag = _find_option_tag (option);
469 if (option_tag == IPP_TAG_ZERO)
471 option_tag = IPP_TAG_NAME;
472 if (strcasecmp (value, "true") == 0 ||
473 strcasecmp (value, "false") == 0)
475 option_tag = IPP_TAG_BOOLEAN;
481 case IPP_TAG_INTEGER:
483 ippAddInteger (request->ipp_request,
487 strtol (value, NULL, 0));
490 case IPP_TAG_BOOLEAN:
494 if (strcasecmp (value, "true") == 0 ||
495 strcasecmp (value, "on") == 0 ||
496 strcasecmp (value, "yes") == 0)
501 ippAddBoolean (request->ipp_request,
521 lower = strtol (value, &s, 0);
526 upper = strtol (s + 1, NULL, 0);
533 ippAddRange (request->ipp_request,
542 case IPP_TAG_RESOLUTION:
549 xres = strtol (value, &s, 0);
552 yres = strtol (s + 1, &s, 0);
556 if (strcasecmp (s, "dpc") == 0)
557 units = IPP_RES_PER_CM;
559 units = IPP_RES_PER_INCH;
561 ippAddResolution (request->ipp_request,
579 values = g_strdup (value);
583 for (s = values, next = s; *s != '\0'; s++)
585 if (in_quotes != 2 && *s == '\'')
587 /* skip quoted value */
593 else if (in_quotes != 1 && *s == '\"')
595 /* skip quoted value */
601 else if (in_quotes == 0 && *s == ',')
603 /* found delimiter, add to value array */
606 strings = g_ptr_array_new ();
607 g_ptr_array_add (strings, next);
610 else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
612 /* skip escaped character */
620 ippAddString (request->ipp_request,
629 /* multiple values */
632 g_ptr_array_add (strings, next);
634 ippAddStrings (request->ipp_request,
640 (const char **) strings->pdata);
641 g_ptr_array_free (strings, TRUE);
653 _connect (GtkCupsRequest *request)
655 request->poll_state = GTK_CUPS_HTTP_IDLE;
656 request->bytes_received = 0;
658 if (request->http == NULL)
660 request->http = httpConnectEncrypt (request->server,
664 if (request->http == NULL)
668 httpBlocking (request->http, 0);
670 request->own_http = TRUE;
674 request->attempts = 0;
677 /* we always write to the socket after we get
679 request->poll_state = GTK_CUPS_HTTP_WRITE;
684 _post_send (GtkCupsRequest *request)
687 struct stat data_info;
690 g_print ("CUPS Backend: %s\n", G_STRFUNC));
692 request->poll_state = GTK_CUPS_HTTP_WRITE;
694 if (request->data_io != NULL)
696 fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
697 sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
700 sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
702 httpClearFields (request->http);
703 httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
704 httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
705 #ifdef HAVE_HTTPGETAUTHSTRING
706 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
708 #ifdef HAVE_HTTP_AUTHSTRING
709 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
713 if (httpPost (request->http, request->resource))
715 if (httpReconnect (request->http))
717 request->state = GTK_CUPS_POST_DONE;
718 request->poll_state = GTK_CUPS_HTTP_IDLE;
720 /* TODO: should add a status or error code for failed post */
721 gtk_cups_result_set_error (request->result,
722 GTK_CUPS_ERROR_GENERAL,
732 request->attempts = 0;
734 request->state = GTK_CUPS_POST_WRITE_REQUEST;
735 request->ipp_request->state = IPP_IDLE;
739 _post_write_request (GtkCupsRequest *request)
741 ipp_state_t ipp_status;
744 g_print ("CUPS Backend: %s\n", G_STRFUNC));
746 request->poll_state = GTK_CUPS_HTTP_WRITE;
748 ipp_status = ippWrite (request->http, request->ipp_request);
750 if (ipp_status == IPP_ERROR)
752 int cups_error = cupsLastError ();
753 request->state = GTK_CUPS_POST_DONE;
754 request->poll_state = GTK_CUPS_HTTP_IDLE;
756 gtk_cups_result_set_error (request->result,
761 ippErrorString (cups_error));
765 if (ipp_status == IPP_DATA)
767 if (request->data_io != NULL)
768 request->state = GTK_CUPS_POST_WRITE_DATA;
771 request->state = GTK_CUPS_POST_CHECK;
772 request->poll_state = GTK_CUPS_HTTP_READ;
778 _post_write_data (GtkCupsRequest *request)
781 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
782 http_status_t http_status;
785 g_print ("CUPS Backend: %s\n", G_STRFUNC));
787 request->poll_state = GTK_CUPS_HTTP_WRITE;
789 if (httpCheck (request->http))
790 http_status = httpUpdate (request->http);
792 http_status = request->last_status;
794 request->last_status = http_status;
797 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
806 g_io_channel_read_chars (request->data_io,
808 _GTK_CUPS_MAX_CHUNK_SIZE,
812 if (io_status == G_IO_STATUS_ERROR)
814 request->state = GTK_CUPS_POST_DONE;
815 request->poll_state = GTK_CUPS_HTTP_IDLE;
817 gtk_cups_result_set_error (request->result,
821 "Error reading from cache file: %s",
824 g_error_free (error);
827 else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
829 request->state = GTK_CUPS_POST_CHECK;
830 request->poll_state = GTK_CUPS_HTTP_READ;
832 request->attempts = 0;
837 #if HAVE_CUPS_API_1_2
838 if (httpWrite2 (request->http, buffer, bytes) < bytes)
840 if (httpWrite (request->http, buffer, (int) bytes) < bytes)
841 #endif /* HAVE_CUPS_API_1_2 */
845 http_errno = httpError (request->http);
847 request->state = GTK_CUPS_POST_DONE;
848 request->poll_state = GTK_CUPS_HTTP_IDLE;
850 gtk_cups_result_set_error (request->result,
854 "Error writing to socket in Post %s",
855 g_strerror (http_errno));
859 else if (http_status == HTTP_UNAUTHORIZED)
861 request->state = GTK_CUPS_POST_CHECK;
862 request->poll_state = GTK_CUPS_HTTP_READ;
864 request->attempts = 0;
874 _post_auth (GtkCupsRequest *request)
876 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
878 if (request->password == NULL)
880 request->state = GTK_CUPS_POST_DONE;
881 request->poll_state = GTK_CUPS_HTTP_IDLE;
883 gtk_cups_result_set_error (request->result,
890 request->state = GTK_CUPS_POST_CHECK;
895 _get_auth (GtkCupsRequest *request)
897 if (request->password_state == GTK_CUPS_PASSWORD_HAS)
899 if (request->password == NULL)
901 request->state = GTK_CUPS_GET_DONE;
902 request->poll_state = GTK_CUPS_HTTP_IDLE;
904 gtk_cups_result_set_error (request->result,
911 request->state = GTK_CUPS_GET_CHECK;
915 /* Very ugly hack: cups has a stupid synchronous password callback
916 * that doesn't even take the request or user data parameters, so
917 * we have to use a static variable to pass the password to it.
919 * The callback sets cups_password to NULL to signal that the
920 * password has been used.
922 static char *cups_password = NULL;
923 static char *cups_username = NULL;
926 passwordCB (const char *prompt)
928 char *pwd = cups_password;
929 cups_password = NULL;
931 cupsSetUser (cups_username);
937 _post_check (GtkCupsRequest *request)
939 http_status_t http_status;
941 http_status = request->last_status;
944 g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
946 request->poll_state = GTK_CUPS_HTTP_READ;
948 if (http_status == HTTP_CONTINUE)
952 else if (http_status == HTTP_UNAUTHORIZED)
954 int auth_result = -1;
955 httpFlush (request->http);
957 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
959 request->poll_state = GTK_CUPS_HTTP_IDLE;
960 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
961 request->state = GTK_CUPS_POST_AUTH;
962 request->need_password = TRUE;
968 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
970 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
972 /* Basic, BasicDigest, Digest and PeerCred */
975 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
977 cups_username = request->username;
978 cupsSetPasswordCB (passwordCB);
980 /* This call success for PeerCred authentication */
981 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
983 if (auth_result != 0)
985 /* move to AUTH state to let the backend
988 request->poll_state = GTK_CUPS_HTTP_IDLE;
989 request->state = GTK_CUPS_POST_AUTH;
990 request->need_password = TRUE;
997 cups_password = request->password;
998 cups_username = request->username;
1000 auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
1002 if (cups_password != NULL)
1005 if (request->password != NULL)
1007 memset (request->password, 0, strlen (request->password));
1008 g_free (request->password);
1009 request->password = NULL;
1012 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1017 httpReconnect (request->http))
1019 /* if the password has been used, reset password_state
1020 * so that we ask for a new one next time around
1022 if (cups_password == NULL)
1023 request->password_state = GTK_CUPS_PASSWORD_NONE;
1025 request->state = GTK_CUPS_POST_DONE;
1026 request->poll_state = GTK_CUPS_HTTP_IDLE;
1027 gtk_cups_result_set_error (request->result,
1028 GTK_CUPS_ERROR_AUTH,
1035 if (request->data_io != NULL)
1036 g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
1038 request->state = GTK_CUPS_POST_CONNECT;
1039 request->poll_state = GTK_CUPS_HTTP_WRITE;
1041 else if (http_status == HTTP_ERROR)
1043 int error = httpError (request->http);
1045 if (error != WSAENETDOWN && error != WSAENETUNREACH)
1047 if (error != ENETDOWN && error != ENETUNREACH)
1048 #endif /* G_OS_WIN32 */
1050 request->attempts++;
1055 request->state = GTK_CUPS_POST_DONE;
1056 request->poll_state = GTK_CUPS_HTTP_IDLE;
1058 gtk_cups_result_set_error (request->result,
1059 GTK_CUPS_ERROR_HTTP,
1062 "Unknown HTTP error");
1067 else if (http_status == HTTP_UPGRADE_REQUIRED)
1069 /* Flush any error message... */
1070 httpFlush (request->http);
1072 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1073 request->state = GTK_CUPS_POST_CONNECT;
1076 httpReconnect (request->http);
1078 /* Upgrade with encryption... */
1079 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1081 request->attempts++;
1084 else if (http_status != HTTP_OK)
1088 http_errno = httpError (request->http);
1090 if (http_errno == EPIPE)
1091 request->state = GTK_CUPS_POST_CONNECT;
1094 request->state = GTK_CUPS_POST_DONE;
1095 gtk_cups_result_set_error (request->result,
1096 GTK_CUPS_ERROR_HTTP,
1099 "HTTP Error in POST %s",
1100 g_strerror (http_errno));
1101 request->poll_state = GTK_CUPS_HTTP_IDLE;
1103 httpFlush (request->http);
1107 request->poll_state = GTK_CUPS_HTTP_IDLE;
1109 httpFlush (request->http);
1111 request->last_status = HTTP_CONTINUE;
1112 httpClose (request->http);
1113 request->http = NULL;
1118 request->state = GTK_CUPS_POST_READ_RESPONSE;
1123 http_status = HTTP_CONTINUE;
1125 if (httpCheck (request->http))
1126 http_status = httpUpdate (request->http);
1128 request->last_status = http_status;
1132 _post_read_response (GtkCupsRequest *request)
1134 ipp_state_t ipp_status;
1137 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1139 request->poll_state = GTK_CUPS_HTTP_READ;
1141 if (request->result->ipp_response == NULL)
1142 request->result->ipp_response = ippNew();
1144 ipp_status = ippRead (request->http,
1145 request->result->ipp_response);
1147 if (ipp_status == IPP_ERROR)
1149 int ipp_error = cupsLastError ();
1150 gtk_cups_result_set_error (request->result,
1155 ippErrorString (ipp_error));
1157 ippDelete (request->result->ipp_response);
1158 request->result->ipp_response = NULL;
1160 request->state = GTK_CUPS_POST_DONE;
1161 request->poll_state = GTK_CUPS_HTTP_IDLE;
1163 else if (ipp_status == IPP_DATA)
1165 request->state = GTK_CUPS_POST_DONE;
1166 request->poll_state = GTK_CUPS_HTTP_IDLE;
1171 _get_send (GtkCupsRequest *request)
1174 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1176 request->poll_state = GTK_CUPS_HTTP_WRITE;
1178 if (request->data_io == NULL)
1180 gtk_cups_result_set_error (request->result,
1183 G_IO_CHANNEL_ERROR_FAILED,
1184 "Get requires an open io channel");
1186 request->state = GTK_CUPS_GET_DONE;
1187 request->poll_state = GTK_CUPS_HTTP_IDLE;
1192 httpClearFields (request->http);
1193 #ifdef HAVE_HTTPGETAUTHSTRING
1194 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
1196 #ifdef HAVE_HTTP_AUTHSTRING
1197 httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
1201 if (httpGet (request->http, request->resource))
1203 if (httpReconnect (request->http))
1205 request->state = GTK_CUPS_GET_DONE;
1206 request->poll_state = GTK_CUPS_HTTP_IDLE;
1208 /* TODO: should add a status or error code for failed GET */
1209 gtk_cups_result_set_error (request->result,
1210 GTK_CUPS_ERROR_GENERAL,
1216 request->attempts++;
1220 if (httpCheck (request->http))
1221 request->last_status = httpUpdate (request->http);
1223 request->attempts = 0;
1225 request->state = GTK_CUPS_GET_CHECK;
1226 request->poll_state = GTK_CUPS_HTTP_READ;
1228 request->ipp_request->state = IPP_IDLE;
1232 _get_check (GtkCupsRequest *request)
1234 http_status_t http_status;
1237 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1239 http_status = request->last_status;
1241 request->poll_state = GTK_CUPS_HTTP_READ;
1243 if (http_status == HTTP_CONTINUE)
1247 else if (http_status == HTTP_UNAUTHORIZED)
1249 int auth_result = -1;
1250 httpFlush (request->http);
1252 if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
1254 request->poll_state = GTK_CUPS_HTTP_IDLE;
1255 request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
1256 request->state = GTK_CUPS_GET_AUTH;
1257 request->need_password = TRUE;
1263 if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
1265 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1267 /* Basic, BasicDigest, Digest and PeerCred */
1270 if (request->password_state == GTK_CUPS_PASSWORD_NONE)
1272 cups_username = request->username;
1273 cupsSetPasswordCB (passwordCB);
1275 /* This call success for PeerCred authentication */
1276 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1278 if (auth_result != 0)
1280 /* move to AUTH state to let the backend
1281 * ask for a password
1283 request->poll_state = GTK_CUPS_HTTP_IDLE;
1284 request->state = GTK_CUPS_GET_AUTH;
1285 request->need_password = TRUE;
1292 cups_password = request->password;
1293 cups_username = request->username;
1295 auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1297 if (cups_password != NULL)
1300 if (request->password != NULL)
1302 memset (request->password, 0, strlen (request->password));
1303 g_free (request->password);
1304 request->password = NULL;
1307 request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1312 httpReconnect (request->http))
1314 /* if the password has been used, reset password_state
1315 * so that we ask for a new one next time around
1317 if (cups_password == NULL)
1318 request->password_state = GTK_CUPS_PASSWORD_NONE;
1320 request->state = GTK_CUPS_GET_DONE;
1321 request->poll_state = GTK_CUPS_HTTP_IDLE;
1322 gtk_cups_result_set_error (request->result,
1323 GTK_CUPS_ERROR_AUTH,
1330 request->state = GTK_CUPS_GET_CONNECT;
1331 request->last_status = HTTP_CONTINUE;
1335 else if (http_status == HTTP_UPGRADE_REQUIRED)
1337 /* Flush any error message... */
1338 httpFlush (request->http);
1340 cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1341 request->state = GTK_CUPS_GET_CONNECT;
1344 httpReconnect (request->http);
1346 /* Upgrade with encryption... */
1347 httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1349 request->attempts++;
1352 else if (http_status != HTTP_OK)
1356 http_errno = httpError (request->http);
1358 if (http_errno == EPIPE)
1359 request->state = GTK_CUPS_GET_CONNECT;
1362 request->state = GTK_CUPS_GET_DONE;
1363 gtk_cups_result_set_error (request->result,
1364 GTK_CUPS_ERROR_HTTP,
1367 "HTTP Error in GET %s",
1368 g_strerror (http_errno));
1369 request->poll_state = GTK_CUPS_HTTP_IDLE;
1370 httpFlush (request->http);
1375 request->poll_state = GTK_CUPS_HTTP_IDLE;
1376 httpFlush (request->http);
1377 httpClose (request->http);
1378 request->last_status = HTTP_CONTINUE;
1379 request->http = NULL;
1385 request->state = GTK_CUPS_GET_READ_DATA;
1390 http_status = HTTP_CONTINUE;
1392 if (httpCheck (request->http))
1393 http_status = httpUpdate (request->http);
1395 request->last_status = http_status;
1400 _get_read_data (GtkCupsRequest *request)
1402 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1404 gsize bytes_written;
1405 GIOStatus io_status;
1409 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1413 request->poll_state = GTK_CUPS_HTTP_READ;
1415 #if HAVE_CUPS_API_1_2
1416 bytes = httpRead2 (request->http, buffer, sizeof (buffer));
1418 bytes = httpRead (request->http, buffer, sizeof (buffer));
1419 #endif /* HAVE_CUPS_API_1_2 */
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 HAVE_CUPS_API_1_2
1447 if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
1449 if (httpGetLength (request->http) <= request->bytes_received || bytes == 0)
1450 #endif /* HAVE_CUPS_API_1_2 */
1452 request->state = GTK_CUPS_GET_DONE;
1453 request->poll_state = GTK_CUPS_HTTP_IDLE;
1460 gtk_cups_request_is_done (GtkCupsRequest *request)
1462 return (request->state == GTK_CUPS_REQUEST_DONE);
1466 gtk_cups_result_is_error (GtkCupsResult *result)
1468 return result->is_error;
1472 gtk_cups_result_get_response (GtkCupsResult *result)
1474 return result->ipp_response;
1478 gtk_cups_result_get_error_type (GtkCupsResult *result)
1480 return result->error_type;
1484 gtk_cups_result_get_error_status (GtkCupsResult *result)
1486 return result->error_status;
1490 gtk_cups_result_get_error_code (GtkCupsResult *result)
1492 return result->error_code;
1496 gtk_cups_result_get_error_string (GtkCupsResult *result)
1498 return result->error_msg;
1501 /* This function allocates new instance of GtkCupsConnectionTest() and creates
1502 * a socket for communication with a CUPS server 'server'.
1504 GtkCupsConnectionTest *
1505 gtk_cups_connection_test_new (const char *server)
1507 GtkCupsConnectionTest *result = NULL;
1508 #ifdef HAVE_CUPS_API_1_2
1509 gchar *port_str = NULL;
1511 result = g_new (GtkCupsConnectionTest, 1);
1513 port_str = g_strdup_printf ("%d", ippPort ());
1516 result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
1518 result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
1522 result->socket = -1;
1523 result->current_addr = NULL;
1524 result->last_wrong_addr = NULL;
1525 result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1527 result->at_init = gtk_cups_connection_test_get_state (result);
1529 result = g_new (GtkCupsConnectionTest, 1);
1536 /* A non-blocking test whether it is possible to connect to a CUPS server specified
1537 * inside of GtkCupsConnectionTest structure.
1538 * - you need to check it more then once.
1539 * The connection is closed after a successful connection.
1541 GtkCupsConnectionState
1542 gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
1544 #ifdef HAVE_CUPS_API_1_2
1545 GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1546 http_addrlist_t *iter;
1552 return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1554 if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
1556 test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1557 return GTK_CUPS_CONNECTION_AVAILABLE;
1561 if (test->socket == -1)
1563 if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
1564 iter = test->last_wrong_addr->next;
1567 test->last_wrong_addr = NULL;
1568 iter = test->addrlist;
1573 test->socket = socket (iter->addr.addr.sa_family,
1577 if (test->socket >= 0)
1579 flags = fcntl (test->socket, F_GETFL);
1582 flags |= O_NONBLOCK;
1584 fcntl (test->socket, F_SETFL, flags);
1586 test->current_addr = iter;
1594 if (test->socket >= 0)
1596 code = connect (test->socket,
1597 &test->current_addr->addr.addr,
1598 httpAddrLength (&test->current_addr->addr));
1602 if (code == 0 || error_code == EISCONN)
1604 close (test->socket);
1606 test->current_addr = NULL;
1607 result = GTK_CUPS_CONNECTION_AVAILABLE;
1611 if (error_code == EALREADY || error_code == EINPROGRESS)
1612 result = GTK_CUPS_CONNECTION_IN_PROGRESS;
1615 close (test->socket);
1617 test->last_wrong_addr = test->current_addr;
1618 result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1626 return GTK_CUPS_CONNECTION_AVAILABLE;
1630 /* This function frees memory used by the GtkCupsConnectionTest structure.
1633 gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
1638 #ifdef HAVE_CUPS_API_1_2
1639 test->current_addr = NULL;
1640 test->last_wrong_addr = NULL;
1641 httpAddrFreeList (test->addrlist);
1642 if (test->socket != -1)
1644 close (test->socket);