1 /* GTK - The GIMP Toolkit
2 * gtkcupsutils.h: Statemachine implementation of POST and GET
3 * cup calls which can be used to create a non-blocking cups API
4 * Copyright (C) 2003, 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.
22 #include "gtkcupsutils.h"
28 #include <sys/types.h>
33 #if CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR == 1 && CUPS_VERSION_PATCH >= 20)
34 #define HAVE_HTTP_AUTHSTRING 1
37 typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
39 static void _connect (GtkCupsRequest *request);
40 static void _post_send (GtkCupsRequest *request);
41 static void _post_write_request (GtkCupsRequest *request);
42 static void _post_write_data (GtkCupsRequest *request);
43 static void _post_check (GtkCupsRequest *request);
44 static void _post_read_response (GtkCupsRequest *request);
46 static void _get_send (GtkCupsRequest *request);
47 static void _get_check (GtkCupsRequest *request);
48 static void _get_read_data (GtkCupsRequest *request);
56 guint is_ipp_response : 1;
60 #define _GTK_CUPS_MAX_ATTEMPTS 10
61 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
63 static GtkCupsRequestStateFunc post_states[] = {
72 static GtkCupsRequestStateFunc get_states[] = {
80 gtk_cups_result_set_error (GtkCupsResult *result,
81 const char *error_msg,
86 result->is_ipp_response = FALSE;
88 result->is_error = TRUE;
90 va_start (args, error_msg);
91 result->error_msg = g_strdup_vprintf (error_msg, args);
96 gtk_cups_request_new (http_t *connection,
97 GtkCupsRequestType req_type,
101 const char *resource)
103 GtkCupsRequest *request;
104 cups_lang_t *language;
106 request = g_new0 (GtkCupsRequest, 1);
107 request->result = g_new0 (GtkCupsResult, 1);
109 request->result->error_msg = NULL;
110 request->result->ipp_response = NULL;
112 request->result->is_error = FALSE;
113 request->result->is_ipp_response = FALSE;
115 request->type = req_type;
116 request->state = GTK_CUPS_REQUEST_START;
119 request->server = g_strdup (server);
121 request->server = g_strdup (cupsServer());
125 request->resource = g_strdup (resource);
127 request->resource = g_strdup ("/");
129 if (connection != NULL)
131 request->http = connection;
132 request->own_http = FALSE;
136 request->http = NULL;
137 request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
140 httpBlocking (request->http, 0);
142 request->own_http = TRUE;
145 request->last_status = HTTP_CONTINUE;
147 request->attempts = 0;
148 request->data_io = data_io;
150 request->ipp_request = ippNew();
151 request->ipp_request->request.op.operation_id = operation_id;
152 request->ipp_request->request.op.request_id = 1;
154 language = cupsLangDefault ();
156 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
157 "attributes-charset",
160 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
161 "attributes-natural-language",
162 NULL, language->language);
164 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
165 "requesting-user-name",
168 cupsLangFree (language);
174 gtk_cups_result_free (GtkCupsResult *result)
176 g_free (result->error_msg);
178 if (result->ipp_response)
179 ippDelete (result->ipp_response);
185 gtk_cups_request_free (GtkCupsRequest *request)
187 if (request->own_http)
189 httpClose (request->http);
191 if (request->ipp_request)
192 ippDelete (request->ipp_request);
194 g_free (request->server);
195 g_free (request->resource);
197 gtk_cups_result_free (request->result);
203 gtk_cups_request_read_write (GtkCupsRequest *request)
205 if (request->type == GTK_CUPS_POST)
206 post_states[request->state](request);
207 else if (request->type == GTK_CUPS_GET)
208 get_states[request->state](request);
210 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
211 request->state != GTK_CUPS_REQUEST_DONE)
213 gtk_cups_result_set_error (request->result, "Too many failed attempts");
214 request->state = GTK_CUPS_REQUEST_DONE;
215 request->poll_state = GTK_CUPS_HTTP_IDLE;
218 if (request->state == GTK_CUPS_REQUEST_DONE)
220 request->poll_state = GTK_CUPS_HTTP_IDLE;
230 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
232 return request->poll_state;
238 gtk_cups_request_get_result (GtkCupsRequest *request)
240 return request->result;
244 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
251 ippAddString (request->ipp_request,
260 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
266 const char *const *values)
268 ippAddStrings (request->ipp_request,
285 static const ipp_option_t ipp_options[] = {
286 { "blackplot", IPP_TAG_BOOLEAN },
287 { "brightness", IPP_TAG_INTEGER },
288 { "columns", IPP_TAG_INTEGER },
289 { "copies", IPP_TAG_INTEGER },
290 { "finishings", IPP_TAG_ENUM },
291 { "fitplot", IPP_TAG_BOOLEAN },
292 { "gamma", IPP_TAG_INTEGER },
293 { "hue", IPP_TAG_INTEGER },
294 { "job-k-limit", IPP_TAG_INTEGER },
295 { "job-page-limit", IPP_TAG_INTEGER },
296 { "job-priority", IPP_TAG_INTEGER },
297 { "job-quota-period", IPP_TAG_INTEGER },
298 { "landscape", IPP_TAG_BOOLEAN },
299 { "media", IPP_TAG_KEYWORD },
300 { "mirror", IPP_TAG_BOOLEAN },
301 { "natural-scaling", IPP_TAG_INTEGER },
302 { "number-up", IPP_TAG_INTEGER },
303 { "orientation-requested", IPP_TAG_ENUM },
304 { "page-bottom", IPP_TAG_INTEGER },
305 { "page-left", IPP_TAG_INTEGER },
306 { "page-ranges", IPP_TAG_RANGE },
307 { "page-right", IPP_TAG_INTEGER },
308 { "page-top", IPP_TAG_INTEGER },
309 { "penwidth", IPP_TAG_INTEGER },
310 { "ppi", IPP_TAG_INTEGER },
311 { "prettyprint", IPP_TAG_BOOLEAN },
312 { "printer-resolution", IPP_TAG_RESOLUTION },
313 { "print-quality", IPP_TAG_ENUM },
314 { "saturation", IPP_TAG_INTEGER },
315 { "scaling", IPP_TAG_INTEGER },
316 { "sides", IPP_TAG_KEYWORD },
317 { "wrap", IPP_TAG_BOOLEAN }
322 _find_option_tag (const gchar *option)
324 int lower_bound, upper_bound, num_options;
328 result = IPP_TAG_ZERO;
331 upper_bound = num_options = (int)(sizeof(ipp_options) / sizeof(ipp_options[0])) - 1;
336 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
338 match = strcasecmp(option, ipp_options[current_option].name);
341 result = ipp_options[current_option].value_tag;
346 upper_bound = current_option - 1;
350 lower_bound = current_option + 1;
353 if (upper_bound == lower_bound && upper_bound == current_option)
359 if (lower_bound > num_options)
362 if (upper_bound < lower_bound)
368 gtk_cups_request_encode_option (GtkCupsRequest *request,
372 ipp_tag_t option_tag;
374 g_assert (option != NULL);
375 g_assert (value != NULL);
377 option_tag = _find_option_tag (option);
379 if (option_tag == IPP_TAG_ZERO)
381 option_tag = IPP_TAG_NAME;
382 if (strcasecmp (value, "true") == 0 ||
383 strcasecmp (value, "false") == 0)
385 option_tag = IPP_TAG_BOOLEAN;
391 case IPP_TAG_INTEGER:
393 ippAddInteger (request->ipp_request,
397 strtol (value, NULL, 0));
400 case IPP_TAG_BOOLEAN:
404 if (!strcasecmp(value, "true") ||
405 !strcasecmp(value, "on") ||
406 !strcasecmp(value, "yes"))
409 ippAddBoolean(request->ipp_request,
429 lower = strtol(value, &s, 0);
434 upper = strtol(s + 1, NULL, 0);
441 ippAddRange (request->ipp_request,
450 case IPP_TAG_RESOLUTION:
457 xres = strtol(value, &s, 0);
460 yres = strtol(s + 1, &s, 0);
464 if (strcasecmp(s, "dpc") == 0)
465 units = IPP_RES_PER_CM;
467 units = IPP_RES_PER_INCH;
469 ippAddResolution (request->ipp_request,
487 values = g_strdup (value);
491 for (s = values, next = s; *s != '\0'; s++)
493 if (in_quotes != 2 && *s == '\'')
495 /* skip quoted value */
501 else if (in_quotes != 1 && *s == '\"')
503 /* skip quoted value */
509 else if (in_quotes == 0 && *s == ',')
511 /* found delimiter, add to value array */
514 strings = g_ptr_array_new ();
515 g_ptr_array_add (strings, next);
518 else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
520 /* skip escaped character */
528 ippAddString (request->ipp_request,
537 /* multiple values */
540 g_ptr_array_add (strings, next);
542 ippAddStrings (request->ipp_request,
548 (const char **) strings->pdata);
549 g_ptr_array_free (strings, TRUE);
561 _connect (GtkCupsRequest *request)
563 request->poll_state = GTK_CUPS_HTTP_IDLE;
565 if (request->http == NULL)
567 request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
569 if (request->http == NULL)
573 httpBlocking (request->http, 0);
575 request->own_http = TRUE;
579 request->attempts = 0;
582 /* we always write to the socket after we get
584 request->poll_state = GTK_CUPS_HTTP_WRITE;
589 _post_send (GtkCupsRequest *request)
592 struct stat data_info;
595 g_print ("CUPS Backend: %s\n", G_STRFUNC));
597 request->poll_state = GTK_CUPS_HTTP_WRITE;
599 if (request->data_io != NULL)
601 fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
602 sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request) + data_info.st_size);
606 sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request));
609 httpClearFields(request->http);
610 httpSetField(request->http, HTTP_FIELD_CONTENT_LENGTH, length);
611 httpSetField(request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
612 #ifdef HAVE_HTTP_AUTHSTRING
613 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
616 if (httpPost(request->http, request->resource))
618 if (httpReconnect(request->http))
620 request->state = GTK_CUPS_POST_DONE;
621 request->poll_state = GTK_CUPS_HTTP_IDLE;
623 gtk_cups_result_set_error (request->result, "Failed Post");
630 request->attempts = 0;
632 request->state = GTK_CUPS_POST_WRITE_REQUEST;
633 request->ipp_request->state = IPP_IDLE;
637 _post_write_request (GtkCupsRequest *request)
639 ipp_state_t ipp_status;
642 g_print ("CUPS Backend: %s\n", G_STRFUNC));
644 request->poll_state = GTK_CUPS_HTTP_WRITE;
646 ipp_status = ippWrite(request->http, request->ipp_request);
648 if (ipp_status == IPP_ERROR)
650 request->state = GTK_CUPS_POST_DONE;
651 request->poll_state = GTK_CUPS_HTTP_IDLE;
653 gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ()));
657 if (ipp_status == IPP_DATA)
659 if (request->data_io != NULL)
660 request->state = GTK_CUPS_POST_WRITE_DATA;
663 request->state = GTK_CUPS_POST_CHECK;
664 request->poll_state = GTK_CUPS_HTTP_READ;
670 _post_write_data (GtkCupsRequest *request)
673 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
674 http_status_t http_status;
677 g_print ("CUPS Backend: %s\n", G_STRFUNC));
679 request->poll_state = GTK_CUPS_HTTP_WRITE;
681 if (httpCheck (request->http))
682 http_status = httpUpdate(request->http);
684 http_status = request->last_status;
686 request->last_status = http_status;
689 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
698 g_io_channel_read_chars (request->data_io,
700 _GTK_CUPS_MAX_CHUNK_SIZE,
704 if (io_status == G_IO_STATUS_ERROR)
706 request->state = GTK_CUPS_POST_DONE;
707 request->poll_state = GTK_CUPS_HTTP_IDLE;
709 gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", error->message);
711 g_error_free (error);
714 else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
716 request->state = GTK_CUPS_POST_CHECK;
717 request->poll_state = GTK_CUPS_HTTP_READ;
719 request->attempts = 0;
724 #if HAVE_CUPS_API_1_2
725 if (httpWrite2(request->http, buffer, bytes) < bytes)
727 if (httpWrite(request->http, buffer, (int) bytes) < bytes)
728 #endif /* HAVE_CUPS_API_1_2 */
730 request->state = GTK_CUPS_POST_DONE;
731 request->poll_state = GTK_CUPS_HTTP_IDLE;
733 gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http)));
744 _post_check (GtkCupsRequest *request)
746 http_status_t http_status;
748 http_status = request->last_status;
751 g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
753 request->poll_state = GTK_CUPS_HTTP_READ;
755 if (http_status == HTTP_CONTINUE)
759 else if (http_status == HTTP_UNAUTHORIZED)
761 /* TODO: callout for auth */
762 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
763 request->state = GTK_CUPS_POST_DONE;
764 request->poll_state = GTK_CUPS_HTTP_IDLE;
766 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
769 else if (http_status == HTTP_ERROR)
772 if (request->http->error != WSAENETDOWN &&
773 request->http->error != WSAENETUNREACH)
775 if (request->http->error != ENETDOWN &&
776 request->http->error != ENETUNREACH)
777 #endif /* G_OS_WIN32 */
784 request->state = GTK_CUPS_POST_DONE;
785 request->poll_state = GTK_CUPS_HTTP_IDLE;
787 gtk_cups_result_set_error (request->result, "Unknown HTTP error");
791 /* TODO: detect ssl in configure.ac */
793 else if (http_status == HTTP_UPGRADE_REQUIRED)
795 /* Flush any error message... */
796 httpFlush (request->http);
799 httpReconnect (request->http);
801 /* Upgrade with encryption... */
802 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
808 else if (http_status != HTTP_OK)
812 http_errno = httpError (request->http);
814 if (http_errno == EPIPE)
815 request->state = GTK_CUPS_POST_CONNECT;
818 request->state = GTK_CUPS_POST_DONE;
819 gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno));
820 request->poll_state = GTK_CUPS_HTTP_IDLE;
822 httpFlush(request->http);
826 request->poll_state = GTK_CUPS_HTTP_IDLE;
828 httpFlush(request->http);
830 request->last_status = HTTP_CONTINUE;
831 httpClose (request->http);
832 request->http = NULL;
837 request->state = GTK_CUPS_POST_READ_RESPONSE;
842 http_status = HTTP_CONTINUE;
844 if (httpCheck (request->http))
845 http_status = httpUpdate (request->http);
847 request->last_status = http_status;
851 _post_read_response (GtkCupsRequest *request)
853 ipp_state_t ipp_status;
856 g_print ("CUPS Backend: %s\n", G_STRFUNC));
858 request->poll_state = GTK_CUPS_HTTP_READ;
860 if (request->result->ipp_response == NULL)
861 request->result->ipp_response = ippNew();
863 ipp_status = ippRead (request->http,
864 request->result->ipp_response);
866 if (ipp_status == IPP_ERROR)
868 gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
870 ippDelete (request->result->ipp_response);
871 request->result->ipp_response = NULL;
873 request->state = GTK_CUPS_POST_DONE;
874 request->poll_state = GTK_CUPS_HTTP_IDLE;
876 else if (ipp_status == IPP_DATA)
878 request->state = GTK_CUPS_POST_DONE;
879 request->poll_state = GTK_CUPS_HTTP_IDLE;
884 _get_send (GtkCupsRequest *request)
887 g_print ("CUPS Backend: %s\n", G_STRFUNC));
889 request->poll_state = GTK_CUPS_HTTP_WRITE;
891 if (request->data_io == NULL)
893 gtk_cups_result_set_error (request->result, "Get requires an open io channel");
894 request->state = GTK_CUPS_GET_DONE;
895 request->poll_state = GTK_CUPS_HTTP_IDLE;
900 httpClearFields(request->http);
901 #ifdef HAVE_HTTP_AUTHSTRING
902 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
905 if (httpGet(request->http, request->resource))
907 if (httpReconnect(request->http))
909 request->state = GTK_CUPS_GET_DONE;
910 request->poll_state = GTK_CUPS_HTTP_IDLE;
912 gtk_cups_result_set_error (request->result, "Failed Get");
919 request->attempts = 0;
921 request->state = GTK_CUPS_GET_CHECK;
922 request->poll_state = GTK_CUPS_HTTP_READ;
924 request->ipp_request->state = IPP_IDLE;
928 _get_check (GtkCupsRequest *request)
930 http_status_t http_status;
933 g_print ("CUPS Backend: %s\n", G_STRFUNC));
935 http_status = request->last_status;
937 request->poll_state = GTK_CUPS_HTTP_READ;
939 if (http_status == HTTP_CONTINUE)
943 else if (http_status == HTTP_UNAUTHORIZED)
945 /* TODO: callout for auth */
946 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
947 request->state = GTK_CUPS_GET_DONE;
948 request->poll_state = GTK_CUPS_HTTP_IDLE;
950 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
953 /* TODO: detect ssl in configure.ac */
955 else if (http_status == HTTP_UPGRADE_REQUIRED)
957 /* Flush any error message... */
958 httpFlush (request->http);
961 httpReconnect (request->http);
963 /* Upgrade with encryption... */
964 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
970 else if (http_status != HTTP_OK)
974 http_errno = httpError (request->http);
976 if (http_errno == EPIPE)
977 request->state = GTK_CUPS_GET_CONNECT;
980 request->state = GTK_CUPS_GET_DONE;
981 gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno));
982 request->poll_state = GTK_CUPS_HTTP_IDLE;
983 httpFlush(request->http);
988 request->poll_state = GTK_CUPS_HTTP_IDLE;
989 httpFlush (request->http);
990 httpClose (request->http);
991 request->last_status = HTTP_CONTINUE;
992 request->http = NULL;
998 request->state = GTK_CUPS_GET_READ_DATA;
1003 http_status = HTTP_CONTINUE;
1005 if (httpCheck (request->http))
1006 http_status = httpUpdate (request->http);
1008 request->last_status = http_status;
1013 _get_read_data (GtkCupsRequest *request)
1015 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1017 gsize bytes_written;
1018 GIOStatus io_status;
1022 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1026 request->poll_state = GTK_CUPS_HTTP_READ;
1028 #if HAVE_CUPS_API_1_2
1029 bytes = httpRead2(request->http, buffer, sizeof(buffer));
1031 bytes = httpRead(request->http, buffer, sizeof(buffer));
1032 #endif /* HAVE_CUPS_API_1_2 */
1035 g_print ("CUPS Backend: %i bytes read\n", bytes));
1039 request->state = GTK_CUPS_GET_DONE;
1040 request->poll_state = GTK_CUPS_HTTP_IDLE;
1046 g_io_channel_write_chars (request->data_io,
1052 if (io_status == G_IO_STATUS_ERROR)
1054 request->state = GTK_CUPS_POST_DONE;
1055 request->poll_state = GTK_CUPS_HTTP_IDLE;
1057 gtk_cups_result_set_error (request->result, error->message);
1058 g_error_free (error);
1063 gtk_cups_request_is_done (GtkCupsRequest *request)
1065 return (request->state == GTK_CUPS_REQUEST_DONE);
1069 gtk_cups_result_is_error (GtkCupsResult *result)
1071 return result->is_error;
1075 gtk_cups_result_get_response (GtkCupsResult *result)
1077 return result->ipp_response;
1081 gtk_cups_result_get_error_string (GtkCupsResult *result)
1083 return result->error_msg;