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 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_read_response (GtkCupsRequest *request);
42 static void _get_send (GtkCupsRequest *request);
43 static void _get_check (GtkCupsRequest *request);
44 static void _get_read_data (GtkCupsRequest *request);
52 guint is_ipp_response : 1;
56 #define _GTK_CUPS_MAX_ATTEMPTS 10
57 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
59 GtkCupsRequestStateFunc post_states[] = {_connect,
66 GtkCupsRequestStateFunc get_states[] = {_connect,
72 gtk_cups_result_set_error (GtkCupsResult *result,
73 const char *error_msg,
78 result->is_ipp_response = FALSE;
80 result->is_error = TRUE;
82 va_start (args, error_msg);
83 result->error_msg = g_strdup_vprintf (error_msg, args);
88 gtk_cups_request_new (http_t *connection,
89 GtkCupsRequestType req_type,
95 GtkCupsRequest *request;
96 cups_lang_t *language;
98 request = g_new0 (GtkCupsRequest, 1);
99 request->result = g_new0 (GtkCupsResult, 1);
101 request->result->error_msg = NULL;
102 request->result->ipp_response = NULL;
104 request->result->is_error = FALSE;
105 request->result->is_ipp_response = FALSE;
107 request->type = req_type;
108 request->state = GTK_CUPS_REQUEST_START;
111 request->server = g_strdup (server);
113 request->server = g_strdup (cupsServer());
117 request->resource = g_strdup (resource);
119 request->resource = g_strdup ("/");
121 if (connection != NULL)
123 request->http = connection;
124 request->own_http = FALSE;
128 request->http = NULL;
129 request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
132 httpBlocking (request->http, 0);
134 request->own_http = TRUE;
137 request->last_status = HTTP_CONTINUE;
139 request->attempts = 0;
140 request->data_io = data_io;
142 request->ipp_request = ippNew();
143 request->ipp_request->request.op.operation_id = operation_id;
144 request->ipp_request->request.op.request_id = 1;
146 language = cupsLangDefault ();
148 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
149 "attributes-charset",
152 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
153 "attributes-natural-language",
154 NULL, language->language);
156 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
157 "requesting-user-name",
160 cupsLangFree (language);
166 gtk_cups_result_free (GtkCupsResult *result)
168 g_free (result->error_msg);
170 if (result->ipp_response)
171 ippDelete (result->ipp_response);
177 gtk_cups_request_free (GtkCupsRequest *request)
179 if (request->own_http)
181 httpClose (request->http);
183 if (request->ipp_request)
184 ippDelete (request->ipp_request);
186 g_free (request->server);
187 g_free (request->resource);
189 gtk_cups_result_free (request->result);
195 gtk_cups_request_read_write (GtkCupsRequest *request)
197 if (request->type == GTK_CUPS_POST)
198 post_states[request->state](request);
199 else if (request->type == GTK_CUPS_GET)
200 get_states[request->state](request);
202 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
203 request->state != GTK_CUPS_REQUEST_DONE)
205 gtk_cups_result_set_error (request->result, "Too many failed attempts");
206 request->state = GTK_CUPS_REQUEST_DONE;
207 request->poll_state = GTK_CUPS_HTTP_IDLE;
210 if (request->state == GTK_CUPS_REQUEST_DONE)
212 request->poll_state = GTK_CUPS_HTTP_IDLE;
222 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
224 return request->poll_state;
230 gtk_cups_request_get_result (GtkCupsRequest *request)
232 return request->result;
236 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
243 ippAddString (request->ipp_request,
252 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
258 const char * const *values)
260 ippAddStrings (request->ipp_request,
277 static const ipp_option_t ipp_options[] =
279 { "blackplot", IPP_TAG_BOOLEAN },
280 { "brightness", IPP_TAG_INTEGER },
281 { "columns", IPP_TAG_INTEGER },
282 { "copies", IPP_TAG_INTEGER },
283 { "finishings", IPP_TAG_ENUM },
284 { "fitplot", IPP_TAG_BOOLEAN },
285 { "gamma", IPP_TAG_INTEGER },
286 { "hue", IPP_TAG_INTEGER },
287 { "job-k-limit", IPP_TAG_INTEGER },
288 { "job-page-limit", IPP_TAG_INTEGER },
289 { "job-priority", IPP_TAG_INTEGER },
290 { "job-quota-period", IPP_TAG_INTEGER },
291 { "landscape", IPP_TAG_BOOLEAN },
292 { "media", IPP_TAG_KEYWORD },
293 { "mirror", IPP_TAG_BOOLEAN },
294 { "natural-scaling", IPP_TAG_INTEGER },
295 { "number-up", IPP_TAG_INTEGER },
296 { "orientation-requested", IPP_TAG_ENUM },
297 { "page-bottom", IPP_TAG_INTEGER },
298 { "page-left", IPP_TAG_INTEGER },
299 { "page-ranges", IPP_TAG_RANGE },
300 { "page-right", IPP_TAG_INTEGER },
301 { "page-top", IPP_TAG_INTEGER },
302 { "penwidth", IPP_TAG_INTEGER },
303 { "ppi", IPP_TAG_INTEGER },
304 { "prettyprint", IPP_TAG_BOOLEAN },
305 { "printer-resolution", IPP_TAG_RESOLUTION },
306 { "print-quality", IPP_TAG_ENUM },
307 { "saturation", IPP_TAG_INTEGER },
308 { "scaling", IPP_TAG_INTEGER },
309 { "sides", IPP_TAG_KEYWORD },
310 { "wrap", IPP_TAG_BOOLEAN }
315 _find_option_tag (const gchar *option)
317 int lower_bound, upper_bound, num_options;
321 result = IPP_TAG_ZERO;
324 upper_bound = num_options = (int)(sizeof(ipp_options) / sizeof(ipp_options[0])) - 1;
329 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
331 match = strcasecmp(option, ipp_options[current_option].name);
334 result = ipp_options[current_option].value_tag;
339 upper_bound = current_option - 1;
343 lower_bound = current_option + 1;
346 if (upper_bound == lower_bound && upper_bound == current_option)
352 if (lower_bound > num_options)
355 if (upper_bound < lower_bound)
361 gtk_cups_request_encode_option (GtkCupsRequest *request,
365 ipp_tag_t option_tag;
367 g_assert (option != NULL);
368 g_assert (value != NULL);
370 option_tag = _find_option_tag (option);
372 if (option_tag == IPP_TAG_ZERO)
374 option_tag = IPP_TAG_NAME;
375 if (strcasecmp (value, "true") == 0 ||
376 strcasecmp (value, "false") == 0)
378 option_tag = IPP_TAG_BOOLEAN;
384 case IPP_TAG_INTEGER:
386 ippAddInteger (request->ipp_request,
390 strtol (value, NULL, 0));
393 case IPP_TAG_BOOLEAN:
397 if (!strcasecmp(value, "true") ||
398 !strcasecmp(value, "on") ||
399 !strcasecmp(value, "yes"))
402 ippAddBoolean(request->ipp_request,
422 lower = strtol(value, &s, 0);
427 upper = strtol(s + 1, NULL, 0);
434 ippAddRange (request->ipp_request,
443 case IPP_TAG_RESOLUTION:
450 xres = strtol(value, &s, 0);
453 yres = strtol(s + 1, &s, 0);
457 if (strcasecmp(s, "dpc") == 0)
458 units = IPP_RES_PER_CM;
460 units = IPP_RES_PER_INCH;
462 ippAddResolution (request->ipp_request,
473 ippAddString (request->ipp_request,
486 _connect (GtkCupsRequest *request)
488 request->poll_state = GTK_CUPS_HTTP_IDLE;
490 if (request->http == NULL)
492 request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
494 if (request->http == NULL)
498 httpBlocking (request->http, 0);
500 request->own_http = TRUE;
504 request->attempts = 0;
507 /* we always write to the socket after we get
509 request->poll_state = GTK_CUPS_HTTP_WRITE;
514 _post_send (GtkCupsRequest *request)
517 struct stat data_info;
520 g_print ("CUPS Backend: %s\n", G_STRFUNC));
522 request->poll_state = GTK_CUPS_HTTP_WRITE;
524 if (request->data_io != NULL)
526 fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
527 sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request) + data_info.st_size);
531 sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request));
534 httpClearFields(request->http);
535 httpSetField(request->http, HTTP_FIELD_CONTENT_LENGTH, length);
536 httpSetField(request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
537 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
539 if (httpPost(request->http, request->resource))
541 if (httpReconnect(request->http))
543 request->state = GTK_CUPS_POST_DONE;
544 request->poll_state = GTK_CUPS_HTTP_IDLE;
546 gtk_cups_result_set_error (request->result, "Failed Post");
553 request->attempts = 0;
555 request->state = GTK_CUPS_POST_WRITE_REQUEST;
556 request->ipp_request->state = IPP_IDLE;
560 _post_write_request (GtkCupsRequest *request)
562 ipp_state_t ipp_status;
565 g_print ("CUPS Backend: %s\n", G_STRFUNC));
567 request->poll_state = GTK_CUPS_HTTP_WRITE;
569 ipp_status = ippWrite(request->http, request->ipp_request);
571 if (ipp_status == IPP_ERROR)
573 request->state = GTK_CUPS_POST_DONE;
574 request->poll_state = GTK_CUPS_HTTP_IDLE;
576 gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ()));
580 if (ipp_status == IPP_DATA)
582 if (request->data_io != NULL)
583 request->state = GTK_CUPS_POST_WRITE_DATA;
586 request->state = GTK_CUPS_POST_CHECK;
587 request->poll_state = GTK_CUPS_HTTP_READ;
593 _post_write_data (GtkCupsRequest *request)
596 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
597 http_status_t http_status;
600 g_print ("CUPS Backend: %s\n", G_STRFUNC));
602 request->poll_state = GTK_CUPS_HTTP_WRITE;
604 if (httpCheck (request->http))
605 http_status = httpUpdate(request->http);
607 http_status = request->last_status;
609 request->last_status = http_status;
612 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
621 g_io_channel_read_chars (request->data_io,
623 _GTK_CUPS_MAX_CHUNK_SIZE,
627 if (io_status == G_IO_STATUS_ERROR)
629 request->state = GTK_CUPS_POST_DONE;
630 request->poll_state = GTK_CUPS_HTTP_IDLE;
632 gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", error->message);
634 g_error_free (error);
637 else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
639 request->state = GTK_CUPS_POST_CHECK;
640 request->poll_state = GTK_CUPS_HTTP_READ;
642 request->attempts = 0;
647 #if HAVE_CUPS_API_1_2
648 if (httpWrite2(request->http, buffer, bytes) < bytes)
650 if (httpWrite(request->http, buffer, (int) bytes) < bytes)
651 #endif /* HAVE_CUPS_API_1_2 */
653 request->state = GTK_CUPS_POST_DONE;
654 request->poll_state = GTK_CUPS_HTTP_IDLE;
656 gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http)));
667 _post_check (GtkCupsRequest *request)
669 http_status_t http_status;
671 http_status = request->last_status;
674 g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
676 request->poll_state = GTK_CUPS_HTTP_READ;
678 if (http_status == HTTP_CONTINUE)
682 else if (http_status == HTTP_UNAUTHORIZED)
684 /* TODO: callout for auth */
685 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
686 request->state = GTK_CUPS_POST_DONE;
687 request->poll_state = GTK_CUPS_HTTP_IDLE;
689 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
692 else if (http_status == HTTP_ERROR)
695 if (request->http->error != WSAENETDOWN &&
696 request->http->error != WSAENETUNREACH)
698 if (request->http->error != ENETDOWN &&
699 request->http->error != ENETUNREACH)
700 #endif /* G_OS_WIN32 */
707 request->state = GTK_CUPS_POST_DONE;
708 request->poll_state = GTK_CUPS_HTTP_IDLE;
710 gtk_cups_result_set_error (request->result, "Unknown HTTP error");
714 /* TODO: detect ssl in configure.ac */
716 else if (http_status == HTTP_UPGRADE_REQUIRED)
718 /* Flush any error message... */
719 httpFlush (request->http);
722 httpReconnect (request->http);
724 /* Upgrade with encryption... */
725 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
731 else if (http_status != HTTP_OK)
735 http_errno = httpError (request->http);
737 if (http_errno == EPIPE)
738 request->state = GTK_CUPS_POST_CONNECT;
741 request->state = GTK_CUPS_POST_DONE;
742 gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno));
743 request->poll_state = GTK_CUPS_HTTP_IDLE;
745 httpFlush(request->http);
749 request->poll_state = GTK_CUPS_HTTP_IDLE;
751 httpFlush(request->http);
753 request->last_status = HTTP_CONTINUE;
754 httpClose (request->http);
755 request->http = NULL;
760 request->state = GTK_CUPS_POST_READ_RESPONSE;
765 http_status = HTTP_CONTINUE;
767 if (httpCheck (request->http))
768 http_status = httpUpdate (request->http);
770 request->last_status = http_status;
774 _post_read_response (GtkCupsRequest *request)
776 ipp_state_t ipp_status;
779 g_print ("CUPS Backend: %s\n", G_STRFUNC));
781 request->poll_state = GTK_CUPS_HTTP_READ;
783 if (request->result->ipp_response == NULL)
784 request->result->ipp_response = ippNew();
786 ipp_status = ippRead (request->http,
787 request->result->ipp_response);
789 if (ipp_status == IPP_ERROR)
791 gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
793 ippDelete (request->result->ipp_response);
794 request->result->ipp_response = NULL;
796 request->state = GTK_CUPS_POST_DONE;
797 request->poll_state = GTK_CUPS_HTTP_IDLE;
799 else if (ipp_status == IPP_DATA)
801 request->state = GTK_CUPS_POST_DONE;
802 request->poll_state = GTK_CUPS_HTTP_IDLE;
807 _get_send (GtkCupsRequest *request)
810 g_print ("CUPS Backend: %s\n", G_STRFUNC));
812 request->poll_state = GTK_CUPS_HTTP_WRITE;
814 if (request->data_io == NULL)
816 gtk_cups_result_set_error (request->result, "Get requires an open io channel");
817 request->state = GTK_CUPS_GET_DONE;
818 request->poll_state = GTK_CUPS_HTTP_IDLE;
823 httpClearFields(request->http);
824 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
826 if (httpGet(request->http, request->resource))
828 if (httpReconnect(request->http))
830 request->state = GTK_CUPS_GET_DONE;
831 request->poll_state = GTK_CUPS_HTTP_IDLE;
833 gtk_cups_result_set_error (request->result, "Failed Get");
840 request->attempts = 0;
842 request->state = GTK_CUPS_GET_CHECK;
843 request->poll_state = GTK_CUPS_HTTP_READ;
845 request->ipp_request->state = IPP_IDLE;
849 _get_check (GtkCupsRequest *request)
851 http_status_t http_status;
854 g_print ("CUPS Backend: %s\n", G_STRFUNC));
856 http_status = request->last_status;
858 request->poll_state = GTK_CUPS_HTTP_READ;
860 if (http_status == HTTP_CONTINUE)
864 else if (http_status == HTTP_UNAUTHORIZED)
866 /* TODO: callout for auth */
867 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
868 request->state = GTK_CUPS_GET_DONE;
869 request->poll_state = GTK_CUPS_HTTP_IDLE;
871 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
874 /* TODO: detect ssl in configure.ac */
876 else if (http_status == HTTP_UPGRADE_REQUIRED)
878 /* Flush any error message... */
879 httpFlush (request->http);
882 httpReconnect (request->http);
884 /* Upgrade with encryption... */
885 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
891 else if (http_status != HTTP_OK)
895 http_errno = httpError (request->http);
897 if (http_errno == EPIPE)
898 request->state = GTK_CUPS_GET_CONNECT;
901 request->state = GTK_CUPS_GET_DONE;
902 gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno));
903 request->poll_state = GTK_CUPS_HTTP_IDLE;
904 httpFlush(request->http);
909 request->poll_state = GTK_CUPS_HTTP_IDLE;
910 httpFlush (request->http);
911 httpClose (request->http);
912 request->last_status = HTTP_CONTINUE;
913 request->http = NULL;
919 request->state = GTK_CUPS_GET_READ_DATA;
924 http_status = HTTP_CONTINUE;
926 if (httpCheck (request->http))
927 http_status = httpUpdate (request->http);
929 request->last_status = http_status;
934 _get_read_data (GtkCupsRequest *request)
936 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
943 g_print ("CUPS Backend: %s\n", G_STRFUNC));
947 request->poll_state = GTK_CUPS_HTTP_READ;
949 #if HAVE_CUPS_API_1_2
950 bytes = httpRead2(request->http, buffer, sizeof(buffer));
952 bytes = httpRead(request->http, buffer, sizeof(buffer));
953 #endif /* HAVE_CUPS_API_1_2 */
956 g_print ("CUPS Backend: %i bytes read\n", bytes));
960 request->state = GTK_CUPS_GET_DONE;
961 request->poll_state = GTK_CUPS_HTTP_IDLE;
967 g_io_channel_write_chars (request->data_io,
973 if (io_status == G_IO_STATUS_ERROR)
975 request->state = GTK_CUPS_POST_DONE;
976 request->poll_state = GTK_CUPS_HTTP_IDLE;
978 gtk_cups_result_set_error (request->result, error->message);
979 g_error_free (error);
984 gtk_cups_request_is_done (GtkCupsRequest *request)
986 return (request->state == GTK_CUPS_REQUEST_DONE);
990 gtk_cups_result_is_error (GtkCupsResult *result)
992 return result->is_error;
996 gtk_cups_result_get_response (GtkCupsResult *result)
998 return result->ipp_response;
1002 gtk_cups_result_get_error_string (GtkCupsResult *result)
1004 return result->error_msg;