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 GtkCupsRequestStateFunc post_states[] = {_connect,
70 GtkCupsRequestStateFunc get_states[] = {_connect,
76 gtk_cups_result_set_error (GtkCupsResult *result,
77 const char *error_msg,
82 result->is_ipp_response = FALSE;
84 result->is_error = TRUE;
86 va_start (args, error_msg);
87 result->error_msg = g_strdup_vprintf (error_msg, args);
92 gtk_cups_request_new (http_t *connection,
93 GtkCupsRequestType req_type,
99 GtkCupsRequest *request;
100 cups_lang_t *language;
102 request = g_new0 (GtkCupsRequest, 1);
103 request->result = g_new0 (GtkCupsResult, 1);
105 request->result->error_msg = NULL;
106 request->result->ipp_response = NULL;
108 request->result->is_error = FALSE;
109 request->result->is_ipp_response = FALSE;
111 request->type = req_type;
112 request->state = GTK_CUPS_REQUEST_START;
115 request->server = g_strdup (server);
117 request->server = g_strdup (cupsServer());
121 request->resource = g_strdup (resource);
123 request->resource = g_strdup ("/");
125 if (connection != NULL)
127 request->http = connection;
128 request->own_http = FALSE;
132 request->http = NULL;
133 request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
136 httpBlocking (request->http, 0);
138 request->own_http = TRUE;
141 request->last_status = HTTP_CONTINUE;
143 request->attempts = 0;
144 request->data_io = data_io;
146 request->ipp_request = ippNew();
147 request->ipp_request->request.op.operation_id = operation_id;
148 request->ipp_request->request.op.request_id = 1;
150 language = cupsLangDefault ();
152 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
153 "attributes-charset",
156 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
157 "attributes-natural-language",
158 NULL, language->language);
160 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
161 "requesting-user-name",
164 cupsLangFree (language);
170 gtk_cups_result_free (GtkCupsResult *result)
172 g_free (result->error_msg);
174 if (result->ipp_response)
175 ippDelete (result->ipp_response);
181 gtk_cups_request_free (GtkCupsRequest *request)
183 if (request->own_http)
185 httpClose (request->http);
187 if (request->ipp_request)
188 ippDelete (request->ipp_request);
190 g_free (request->server);
191 g_free (request->resource);
193 gtk_cups_result_free (request->result);
199 gtk_cups_request_read_write (GtkCupsRequest *request)
201 if (request->type == GTK_CUPS_POST)
202 post_states[request->state](request);
203 else if (request->type == GTK_CUPS_GET)
204 get_states[request->state](request);
206 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
207 request->state != GTK_CUPS_REQUEST_DONE)
209 gtk_cups_result_set_error (request->result, "Too many failed attempts");
210 request->state = GTK_CUPS_REQUEST_DONE;
211 request->poll_state = GTK_CUPS_HTTP_IDLE;
214 if (request->state == GTK_CUPS_REQUEST_DONE)
216 request->poll_state = GTK_CUPS_HTTP_IDLE;
226 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
228 return request->poll_state;
234 gtk_cups_request_get_result (GtkCupsRequest *request)
236 return request->result;
240 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
247 ippAddString (request->ipp_request,
256 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
262 const char * const *values)
264 ippAddStrings (request->ipp_request,
281 static const ipp_option_t ipp_options[] =
283 { "blackplot", IPP_TAG_BOOLEAN },
284 { "brightness", IPP_TAG_INTEGER },
285 { "columns", IPP_TAG_INTEGER },
286 { "copies", IPP_TAG_INTEGER },
287 { "finishings", IPP_TAG_ENUM },
288 { "fitplot", IPP_TAG_BOOLEAN },
289 { "gamma", IPP_TAG_INTEGER },
290 { "hue", IPP_TAG_INTEGER },
291 { "job-k-limit", IPP_TAG_INTEGER },
292 { "job-page-limit", IPP_TAG_INTEGER },
293 { "job-priority", IPP_TAG_INTEGER },
294 { "job-quota-period", IPP_TAG_INTEGER },
295 { "landscape", IPP_TAG_BOOLEAN },
296 { "media", IPP_TAG_KEYWORD },
297 { "mirror", IPP_TAG_BOOLEAN },
298 { "natural-scaling", IPP_TAG_INTEGER },
299 { "number-up", IPP_TAG_INTEGER },
300 { "orientation-requested", IPP_TAG_ENUM },
301 { "page-bottom", IPP_TAG_INTEGER },
302 { "page-left", IPP_TAG_INTEGER },
303 { "page-ranges", IPP_TAG_RANGE },
304 { "page-right", IPP_TAG_INTEGER },
305 { "page-top", IPP_TAG_INTEGER },
306 { "penwidth", IPP_TAG_INTEGER },
307 { "ppi", IPP_TAG_INTEGER },
308 { "prettyprint", IPP_TAG_BOOLEAN },
309 { "printer-resolution", IPP_TAG_RESOLUTION },
310 { "print-quality", IPP_TAG_ENUM },
311 { "saturation", IPP_TAG_INTEGER },
312 { "scaling", IPP_TAG_INTEGER },
313 { "sides", IPP_TAG_KEYWORD },
314 { "wrap", IPP_TAG_BOOLEAN }
319 _find_option_tag (const gchar *option)
321 int lower_bound, upper_bound, num_options;
325 result = IPP_TAG_ZERO;
328 upper_bound = num_options = (int)(sizeof(ipp_options) / sizeof(ipp_options[0])) - 1;
333 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
335 match = strcasecmp(option, ipp_options[current_option].name);
338 result = ipp_options[current_option].value_tag;
343 upper_bound = current_option - 1;
347 lower_bound = current_option + 1;
350 if (upper_bound == lower_bound && upper_bound == current_option)
356 if (lower_bound > num_options)
359 if (upper_bound < lower_bound)
365 gtk_cups_request_encode_option (GtkCupsRequest *request,
369 ipp_tag_t option_tag;
371 g_assert (option != NULL);
372 g_assert (value != NULL);
374 option_tag = _find_option_tag (option);
376 if (option_tag == IPP_TAG_ZERO)
378 option_tag = IPP_TAG_NAME;
379 if (strcasecmp (value, "true") == 0 ||
380 strcasecmp (value, "false") == 0)
382 option_tag = IPP_TAG_BOOLEAN;
388 case IPP_TAG_INTEGER:
390 ippAddInteger (request->ipp_request,
394 strtol (value, NULL, 0));
397 case IPP_TAG_BOOLEAN:
401 if (!strcasecmp(value, "true") ||
402 !strcasecmp(value, "on") ||
403 !strcasecmp(value, "yes"))
406 ippAddBoolean(request->ipp_request,
426 lower = strtol(value, &s, 0);
431 upper = strtol(s + 1, NULL, 0);
438 ippAddRange (request->ipp_request,
447 case IPP_TAG_RESOLUTION:
454 xres = strtol(value, &s, 0);
457 yres = strtol(s + 1, &s, 0);
461 if (strcasecmp(s, "dpc") == 0)
462 units = IPP_RES_PER_CM;
464 units = IPP_RES_PER_INCH;
466 ippAddResolution (request->ipp_request,
477 ippAddString (request->ipp_request,
490 _connect (GtkCupsRequest *request)
492 request->poll_state = GTK_CUPS_HTTP_IDLE;
494 if (request->http == NULL)
496 request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
498 if (request->http == NULL)
502 httpBlocking (request->http, 0);
504 request->own_http = TRUE;
508 request->attempts = 0;
511 /* we always write to the socket after we get
513 request->poll_state = GTK_CUPS_HTTP_WRITE;
518 _post_send (GtkCupsRequest *request)
521 struct stat data_info;
524 g_print ("CUPS Backend: %s\n", G_STRFUNC));
526 request->poll_state = GTK_CUPS_HTTP_WRITE;
528 if (request->data_io != NULL)
530 fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
531 sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request) + data_info.st_size);
535 sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request));
538 httpClearFields(request->http);
539 httpSetField(request->http, HTTP_FIELD_CONTENT_LENGTH, length);
540 httpSetField(request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
541 #ifdef HAVE_HTTP_AUTHSTRING
542 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
545 if (httpPost(request->http, request->resource))
547 if (httpReconnect(request->http))
549 request->state = GTK_CUPS_POST_DONE;
550 request->poll_state = GTK_CUPS_HTTP_IDLE;
552 gtk_cups_result_set_error (request->result, "Failed Post");
559 request->attempts = 0;
561 request->state = GTK_CUPS_POST_WRITE_REQUEST;
562 request->ipp_request->state = IPP_IDLE;
566 _post_write_request (GtkCupsRequest *request)
568 ipp_state_t ipp_status;
571 g_print ("CUPS Backend: %s\n", G_STRFUNC));
573 request->poll_state = GTK_CUPS_HTTP_WRITE;
575 ipp_status = ippWrite(request->http, request->ipp_request);
577 if (ipp_status == IPP_ERROR)
579 request->state = GTK_CUPS_POST_DONE;
580 request->poll_state = GTK_CUPS_HTTP_IDLE;
582 gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ()));
586 if (ipp_status == IPP_DATA)
588 if (request->data_io != NULL)
589 request->state = GTK_CUPS_POST_WRITE_DATA;
592 request->state = GTK_CUPS_POST_CHECK;
593 request->poll_state = GTK_CUPS_HTTP_READ;
599 _post_write_data (GtkCupsRequest *request)
602 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
603 http_status_t http_status;
606 g_print ("CUPS Backend: %s\n", G_STRFUNC));
608 request->poll_state = GTK_CUPS_HTTP_WRITE;
610 if (httpCheck (request->http))
611 http_status = httpUpdate(request->http);
613 http_status = request->last_status;
615 request->last_status = http_status;
618 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
627 g_io_channel_read_chars (request->data_io,
629 _GTK_CUPS_MAX_CHUNK_SIZE,
633 if (io_status == G_IO_STATUS_ERROR)
635 request->state = GTK_CUPS_POST_DONE;
636 request->poll_state = GTK_CUPS_HTTP_IDLE;
638 gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", error->message);
640 g_error_free (error);
643 else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
645 request->state = GTK_CUPS_POST_CHECK;
646 request->poll_state = GTK_CUPS_HTTP_READ;
648 request->attempts = 0;
653 #if HAVE_CUPS_API_1_2
654 if (httpWrite2(request->http, buffer, bytes) < bytes)
656 if (httpWrite(request->http, buffer, (int) bytes) < bytes)
657 #endif /* HAVE_CUPS_API_1_2 */
659 request->state = GTK_CUPS_POST_DONE;
660 request->poll_state = GTK_CUPS_HTTP_IDLE;
662 gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http)));
673 _post_check (GtkCupsRequest *request)
675 http_status_t http_status;
677 http_status = request->last_status;
680 g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
682 request->poll_state = GTK_CUPS_HTTP_READ;
684 if (http_status == HTTP_CONTINUE)
688 else if (http_status == HTTP_UNAUTHORIZED)
690 /* TODO: callout for auth */
691 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
692 request->state = GTK_CUPS_POST_DONE;
693 request->poll_state = GTK_CUPS_HTTP_IDLE;
695 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
698 else if (http_status == HTTP_ERROR)
701 if (request->http->error != WSAENETDOWN &&
702 request->http->error != WSAENETUNREACH)
704 if (request->http->error != ENETDOWN &&
705 request->http->error != ENETUNREACH)
706 #endif /* G_OS_WIN32 */
713 request->state = GTK_CUPS_POST_DONE;
714 request->poll_state = GTK_CUPS_HTTP_IDLE;
716 gtk_cups_result_set_error (request->result, "Unknown HTTP error");
720 /* TODO: detect ssl in configure.ac */
722 else if (http_status == HTTP_UPGRADE_REQUIRED)
724 /* Flush any error message... */
725 httpFlush (request->http);
728 httpReconnect (request->http);
730 /* Upgrade with encryption... */
731 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
737 else if (http_status != HTTP_OK)
741 http_errno = httpError (request->http);
743 if (http_errno == EPIPE)
744 request->state = GTK_CUPS_POST_CONNECT;
747 request->state = GTK_CUPS_POST_DONE;
748 gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno));
749 request->poll_state = GTK_CUPS_HTTP_IDLE;
751 httpFlush(request->http);
755 request->poll_state = GTK_CUPS_HTTP_IDLE;
757 httpFlush(request->http);
759 request->last_status = HTTP_CONTINUE;
760 httpClose (request->http);
761 request->http = NULL;
766 request->state = GTK_CUPS_POST_READ_RESPONSE;
771 http_status = HTTP_CONTINUE;
773 if (httpCheck (request->http))
774 http_status = httpUpdate (request->http);
776 request->last_status = http_status;
780 _post_read_response (GtkCupsRequest *request)
782 ipp_state_t ipp_status;
785 g_print ("CUPS Backend: %s\n", G_STRFUNC));
787 request->poll_state = GTK_CUPS_HTTP_READ;
789 if (request->result->ipp_response == NULL)
790 request->result->ipp_response = ippNew();
792 ipp_status = ippRead (request->http,
793 request->result->ipp_response);
795 if (ipp_status == IPP_ERROR)
797 gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
799 ippDelete (request->result->ipp_response);
800 request->result->ipp_response = NULL;
802 request->state = GTK_CUPS_POST_DONE;
803 request->poll_state = GTK_CUPS_HTTP_IDLE;
805 else if (ipp_status == IPP_DATA)
807 request->state = GTK_CUPS_POST_DONE;
808 request->poll_state = GTK_CUPS_HTTP_IDLE;
813 _get_send (GtkCupsRequest *request)
816 g_print ("CUPS Backend: %s\n", G_STRFUNC));
818 request->poll_state = GTK_CUPS_HTTP_WRITE;
820 if (request->data_io == NULL)
822 gtk_cups_result_set_error (request->result, "Get requires an open io channel");
823 request->state = GTK_CUPS_GET_DONE;
824 request->poll_state = GTK_CUPS_HTTP_IDLE;
829 httpClearFields(request->http);
830 #ifdef HAVE_HTTP_AUTHSTRING
831 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
834 if (httpGet(request->http, request->resource))
836 if (httpReconnect(request->http))
838 request->state = GTK_CUPS_GET_DONE;
839 request->poll_state = GTK_CUPS_HTTP_IDLE;
841 gtk_cups_result_set_error (request->result, "Failed Get");
848 request->attempts = 0;
850 request->state = GTK_CUPS_GET_CHECK;
851 request->poll_state = GTK_CUPS_HTTP_READ;
853 request->ipp_request->state = IPP_IDLE;
857 _get_check (GtkCupsRequest *request)
859 http_status_t http_status;
862 g_print ("CUPS Backend: %s\n", G_STRFUNC));
864 http_status = request->last_status;
866 request->poll_state = GTK_CUPS_HTTP_READ;
868 if (http_status == HTTP_CONTINUE)
872 else if (http_status == HTTP_UNAUTHORIZED)
874 /* TODO: callout for auth */
875 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
876 request->state = GTK_CUPS_GET_DONE;
877 request->poll_state = GTK_CUPS_HTTP_IDLE;
879 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
882 /* TODO: detect ssl in configure.ac */
884 else if (http_status == HTTP_UPGRADE_REQUIRED)
886 /* Flush any error message... */
887 httpFlush (request->http);
890 httpReconnect (request->http);
892 /* Upgrade with encryption... */
893 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
899 else if (http_status != HTTP_OK)
903 http_errno = httpError (request->http);
905 if (http_errno == EPIPE)
906 request->state = GTK_CUPS_GET_CONNECT;
909 request->state = GTK_CUPS_GET_DONE;
910 gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno));
911 request->poll_state = GTK_CUPS_HTTP_IDLE;
912 httpFlush(request->http);
917 request->poll_state = GTK_CUPS_HTTP_IDLE;
918 httpFlush (request->http);
919 httpClose (request->http);
920 request->last_status = HTTP_CONTINUE;
921 request->http = NULL;
927 request->state = GTK_CUPS_GET_READ_DATA;
932 http_status = HTTP_CONTINUE;
934 if (httpCheck (request->http))
935 http_status = httpUpdate (request->http);
937 request->last_status = http_status;
942 _get_read_data (GtkCupsRequest *request)
944 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
951 g_print ("CUPS Backend: %s\n", G_STRFUNC));
955 request->poll_state = GTK_CUPS_HTTP_READ;
957 #if HAVE_CUPS_API_1_2
958 bytes = httpRead2(request->http, buffer, sizeof(buffer));
960 bytes = httpRead(request->http, buffer, sizeof(buffer));
961 #endif /* HAVE_CUPS_API_1_2 */
964 g_print ("CUPS Backend: %i bytes read\n", bytes));
968 request->state = GTK_CUPS_GET_DONE;
969 request->poll_state = GTK_CUPS_HTTP_IDLE;
975 g_io_channel_write_chars (request->data_io,
981 if (io_status == G_IO_STATUS_ERROR)
983 request->state = GTK_CUPS_POST_DONE;
984 request->poll_state = GTK_CUPS_HTTP_IDLE;
986 gtk_cups_result_set_error (request->result, error->message);
987 g_error_free (error);
992 gtk_cups_request_is_done (GtkCupsRequest *request)
994 return (request->state == GTK_CUPS_REQUEST_DONE);
998 gtk_cups_result_is_error (GtkCupsResult *result)
1000 return result->is_error;
1004 gtk_cups_result_get_response (GtkCupsResult *result)
1006 return result->ipp_response;
1010 gtk_cups_result_get_error_string (GtkCupsResult *result)
1012 return result->error_msg;