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)
771 int error = httpError (request->http);
773 if (error != WSAENETDOWN && error != WSAENETUNREACH)
775 if (error != ENETDOWN && error != ENETUNREACH)
776 #endif /* G_OS_WIN32 */
783 request->state = GTK_CUPS_POST_DONE;
784 request->poll_state = GTK_CUPS_HTTP_IDLE;
786 gtk_cups_result_set_error (request->result, "Unknown HTTP error");
790 /* TODO: detect ssl in configure.ac */
792 else if (http_status == HTTP_UPGRADE_REQUIRED)
794 /* Flush any error message... */
795 httpFlush (request->http);
798 httpReconnect (request->http);
800 /* Upgrade with encryption... */
801 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
807 else if (http_status != HTTP_OK)
811 http_errno = httpError (request->http);
813 if (http_errno == EPIPE)
814 request->state = GTK_CUPS_POST_CONNECT;
817 request->state = GTK_CUPS_POST_DONE;
818 gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno));
819 request->poll_state = GTK_CUPS_HTTP_IDLE;
821 httpFlush(request->http);
825 request->poll_state = GTK_CUPS_HTTP_IDLE;
827 httpFlush(request->http);
829 request->last_status = HTTP_CONTINUE;
830 httpClose (request->http);
831 request->http = NULL;
836 request->state = GTK_CUPS_POST_READ_RESPONSE;
841 http_status = HTTP_CONTINUE;
843 if (httpCheck (request->http))
844 http_status = httpUpdate (request->http);
846 request->last_status = http_status;
850 _post_read_response (GtkCupsRequest *request)
852 ipp_state_t ipp_status;
855 g_print ("CUPS Backend: %s\n", G_STRFUNC));
857 request->poll_state = GTK_CUPS_HTTP_READ;
859 if (request->result->ipp_response == NULL)
860 request->result->ipp_response = ippNew();
862 ipp_status = ippRead (request->http,
863 request->result->ipp_response);
865 if (ipp_status == IPP_ERROR)
867 gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
869 ippDelete (request->result->ipp_response);
870 request->result->ipp_response = NULL;
872 request->state = GTK_CUPS_POST_DONE;
873 request->poll_state = GTK_CUPS_HTTP_IDLE;
875 else if (ipp_status == IPP_DATA)
877 request->state = GTK_CUPS_POST_DONE;
878 request->poll_state = GTK_CUPS_HTTP_IDLE;
883 _get_send (GtkCupsRequest *request)
886 g_print ("CUPS Backend: %s\n", G_STRFUNC));
888 request->poll_state = GTK_CUPS_HTTP_WRITE;
890 if (request->data_io == NULL)
892 gtk_cups_result_set_error (request->result, "Get requires an open io channel");
893 request->state = GTK_CUPS_GET_DONE;
894 request->poll_state = GTK_CUPS_HTTP_IDLE;
899 httpClearFields(request->http);
900 #ifdef HAVE_HTTP_AUTHSTRING
901 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
904 if (httpGet(request->http, request->resource))
906 if (httpReconnect(request->http))
908 request->state = GTK_CUPS_GET_DONE;
909 request->poll_state = GTK_CUPS_HTTP_IDLE;
911 gtk_cups_result_set_error (request->result, "Failed Get");
918 request->attempts = 0;
920 request->state = GTK_CUPS_GET_CHECK;
921 request->poll_state = GTK_CUPS_HTTP_READ;
923 request->ipp_request->state = IPP_IDLE;
927 _get_check (GtkCupsRequest *request)
929 http_status_t http_status;
932 g_print ("CUPS Backend: %s\n", G_STRFUNC));
934 http_status = request->last_status;
936 request->poll_state = GTK_CUPS_HTTP_READ;
938 if (http_status == HTTP_CONTINUE)
942 else if (http_status == HTTP_UNAUTHORIZED)
944 /* TODO: callout for auth */
945 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
946 request->state = GTK_CUPS_GET_DONE;
947 request->poll_state = GTK_CUPS_HTTP_IDLE;
949 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
952 /* TODO: detect ssl in configure.ac */
954 else if (http_status == HTTP_UPGRADE_REQUIRED)
956 /* Flush any error message... */
957 httpFlush (request->http);
960 httpReconnect (request->http);
962 /* Upgrade with encryption... */
963 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
969 else if (http_status != HTTP_OK)
973 http_errno = httpError (request->http);
975 if (http_errno == EPIPE)
976 request->state = GTK_CUPS_GET_CONNECT;
979 request->state = GTK_CUPS_GET_DONE;
980 gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno));
981 request->poll_state = GTK_CUPS_HTTP_IDLE;
982 httpFlush(request->http);
987 request->poll_state = GTK_CUPS_HTTP_IDLE;
988 httpFlush (request->http);
989 httpClose (request->http);
990 request->last_status = HTTP_CONTINUE;
991 request->http = NULL;
997 request->state = GTK_CUPS_GET_READ_DATA;
1002 http_status = HTTP_CONTINUE;
1004 if (httpCheck (request->http))
1005 http_status = httpUpdate (request->http);
1007 request->last_status = http_status;
1012 _get_read_data (GtkCupsRequest *request)
1014 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1016 gsize bytes_written;
1017 GIOStatus io_status;
1021 g_print ("CUPS Backend: %s\n", G_STRFUNC));
1025 request->poll_state = GTK_CUPS_HTTP_READ;
1027 #if HAVE_CUPS_API_1_2
1028 bytes = httpRead2(request->http, buffer, sizeof(buffer));
1030 bytes = httpRead(request->http, buffer, sizeof(buffer));
1031 #endif /* HAVE_CUPS_API_1_2 */
1034 g_print ("CUPS Backend: %i bytes read\n", bytes));
1038 request->state = GTK_CUPS_GET_DONE;
1039 request->poll_state = GTK_CUPS_HTTP_IDLE;
1045 g_io_channel_write_chars (request->data_io,
1051 if (io_status == G_IO_STATUS_ERROR)
1053 request->state = GTK_CUPS_POST_DONE;
1054 request->poll_state = GTK_CUPS_HTTP_IDLE;
1056 gtk_cups_result_set_error (request->result, error->message);
1057 g_error_free (error);
1062 gtk_cups_request_is_done (GtkCupsRequest *request)
1064 return (request->state == GTK_CUPS_REQUEST_DONE);
1068 gtk_cups_result_is_error (GtkCupsResult *result)
1070 return result->is_error;
1074 gtk_cups_result_get_response (GtkCupsResult *result)
1076 return result->ipp_response;
1080 gtk_cups_result_get_error_string (GtkCupsResult *result)
1082 return result->error_msg;