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"
26 #include <sys/types.h>
31 typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
33 static void _connect (GtkCupsRequest *request);
34 static void _post_send (GtkCupsRequest *request);
35 static void _post_write_request (GtkCupsRequest *request);
36 static void _post_write_data (GtkCupsRequest *request);
37 static void _post_check (GtkCupsRequest *request);
38 static void _post_read_response (GtkCupsRequest *request);
40 static void _get_send (GtkCupsRequest *request);
41 static void _get_check (GtkCupsRequest *request);
42 static void _get_read_data (GtkCupsRequest *request);
50 guint is_ipp_response : 1;
54 #define _GTK_CUPS_MAX_ATTEMPTS 10
55 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
57 GtkCupsRequestStateFunc post_states[] = {_connect,
64 GtkCupsRequestStateFunc get_states[] = {_connect,
70 gtk_cups_result_set_error (GtkCupsResult *result,
71 const char *error_msg,
76 result->is_ipp_response = FALSE;
78 result->is_error = TRUE;
80 va_start (args, error_msg);
81 result->error_msg = g_strdup_vprintf (error_msg, args);
86 gtk_cups_request_new (http_t *connection,
87 GtkCupsRequestType req_type,
93 GtkCupsRequest *request;
94 cups_lang_t *language;
96 request = g_new0 (GtkCupsRequest, 1);
97 request->result = g_new0 (GtkCupsResult, 1);
99 request->result->error_msg = NULL;
100 request->result->ipp_response = NULL;
102 request->result->is_error = FALSE;
103 request->result->is_ipp_response = FALSE;
105 request->type = req_type;
106 request->state = GTK_CUPS_REQUEST_START;
109 request->server = g_strdup (server);
111 request->server = g_strdup (cupsServer());
115 request->resource = g_strdup (resource);
117 request->resource = g_strdup ("/");
119 if (connection != NULL)
121 request->http = connection;
122 request->own_http = FALSE;
126 request->http = NULL;
127 request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
130 httpBlocking (request->http, 0);
132 request->own_http = TRUE;
135 request->last_status = HTTP_CONTINUE;
137 request->attempts = 0;
138 request->data_fd = data_fd;
140 request->ipp_request = ippNew();
141 request->ipp_request->request.op.operation_id = operation_id;
142 request->ipp_request->request.op.request_id = 1;
144 language = cupsLangDefault ();
146 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
147 "attributes-charset",
150 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
151 "attributes-natural-language",
152 NULL, language->language);
154 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
155 "requesting-user-name",
158 cupsLangFree (language);
164 gtk_cups_result_free (GtkCupsResult *result)
166 g_free (result->error_msg);
168 if (result->ipp_response)
169 ippDelete (result->ipp_response);
175 gtk_cups_request_free (GtkCupsRequest *request)
177 if (request->own_http)
179 httpClose (request->http);
181 if (request->ipp_request)
182 ippDelete (request->ipp_request);
184 g_free (request->server);
185 g_free (request->resource);
187 gtk_cups_result_free (request->result);
193 gtk_cups_request_read_write (GtkCupsRequest *request)
195 if (request->type == GTK_CUPS_POST)
196 post_states[request->state](request);
197 else if (request->type == GTK_CUPS_GET)
198 get_states[request->state](request);
200 if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
201 request->state != GTK_CUPS_REQUEST_DONE)
203 gtk_cups_result_set_error (request->result, "Too many failed attempts");
204 request->state = GTK_CUPS_REQUEST_DONE;
205 request->poll_state = GTK_CUPS_HTTP_IDLE;
208 if (request->state == GTK_CUPS_REQUEST_DONE)
210 request->poll_state = GTK_CUPS_HTTP_IDLE;
220 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
222 return request->poll_state;
228 gtk_cups_request_get_result (GtkCupsRequest *request)
230 return request->result;
234 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
241 ippAddString (request->ipp_request,
250 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
256 const char * const *values)
258 ippAddStrings (request->ipp_request,
275 static const ipp_option_t ipp_options[] =
277 { "blackplot", IPP_TAG_BOOLEAN },
278 { "brightness", IPP_TAG_INTEGER },
279 { "columns", IPP_TAG_INTEGER },
280 { "copies", IPP_TAG_INTEGER },
281 { "finishings", IPP_TAG_ENUM },
282 { "fitplot", IPP_TAG_BOOLEAN },
283 { "gamma", IPP_TAG_INTEGER },
284 { "hue", IPP_TAG_INTEGER },
285 { "job-k-limit", IPP_TAG_INTEGER },
286 { "job-page-limit", IPP_TAG_INTEGER },
287 { "job-priority", IPP_TAG_INTEGER },
288 { "job-quota-period", IPP_TAG_INTEGER },
289 { "landscape", IPP_TAG_BOOLEAN },
290 { "media", IPP_TAG_KEYWORD },
291 { "mirror", IPP_TAG_BOOLEAN },
292 { "natural-scaling", IPP_TAG_INTEGER },
293 { "number-up", IPP_TAG_INTEGER },
294 { "orientation-requested", IPP_TAG_ENUM },
295 { "page-bottom", IPP_TAG_INTEGER },
296 { "page-left", IPP_TAG_INTEGER },
297 { "page-ranges", IPP_TAG_RANGE },
298 { "page-right", IPP_TAG_INTEGER },
299 { "page-top", IPP_TAG_INTEGER },
300 { "penwidth", IPP_TAG_INTEGER },
301 { "ppi", IPP_TAG_INTEGER },
302 { "prettyprint", IPP_TAG_BOOLEAN },
303 { "printer-resolution", IPP_TAG_RESOLUTION },
304 { "print-quality", IPP_TAG_ENUM },
305 { "saturation", IPP_TAG_INTEGER },
306 { "scaling", IPP_TAG_INTEGER },
307 { "sides", IPP_TAG_KEYWORD },
308 { "wrap", IPP_TAG_BOOLEAN }
313 _find_option_tag (const gchar *option)
315 int lower_bound, upper_bound, num_options;
319 result = IPP_TAG_ZERO;
322 upper_bound = num_options = (int)(sizeof(ipp_options) / sizeof(ipp_options[0])) - 1;
327 current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
329 match = strcasecmp(option, ipp_options[current_option].name);
332 result = ipp_options[current_option].value_tag;
337 upper_bound = current_option - 1;
341 lower_bound = current_option + 1;
344 if (upper_bound == lower_bound && upper_bound == current_option)
350 if (lower_bound > num_options)
353 if (upper_bound < lower_bound)
359 gtk_cups_request_encode_option (GtkCupsRequest *request,
363 ipp_tag_t option_tag;
365 g_assert (option != NULL);
366 g_assert (value != NULL);
368 option_tag = _find_option_tag (option);
370 if (option_tag == IPP_TAG_ZERO)
372 option_tag = IPP_TAG_NAME;
373 if (strcasecmp (value, "true") == 0 ||
374 strcasecmp (value, "false") == 0)
376 option_tag = IPP_TAG_BOOLEAN;
382 case IPP_TAG_INTEGER:
384 ippAddInteger (request->ipp_request,
388 strtol (value, NULL, 0));
391 case IPP_TAG_BOOLEAN:
395 if (!strcasecmp(value, "true") ||
396 !strcasecmp(value, "on") ||
397 !strcasecmp(value, "yes"))
400 ippAddBoolean(request->ipp_request,
420 lower = strtol(value, &s, 0);
425 upper = strtol(s + 1, NULL, 0);
432 ippAddRange (request->ipp_request,
441 case IPP_TAG_RESOLUTION:
448 xres = strtol(value, &s, 0);
451 yres = strtol(s + 1, &s, 0);
455 if (strcasecmp(s, "dpc") == 0)
456 units = IPP_RES_PER_CM;
458 units = IPP_RES_PER_INCH;
460 ippAddResolution (request->ipp_request,
471 ippAddString (request->ipp_request,
484 _connect (GtkCupsRequest *request)
486 request->poll_state = GTK_CUPS_HTTP_IDLE;
488 if (request->http == NULL)
490 request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
492 if (request->http == NULL)
496 httpBlocking (request->http, 0);
498 request->own_http = TRUE;
502 request->attempts = 0;
505 /* we always write to the socket after we get
507 request->poll_state = GTK_CUPS_HTTP_WRITE;
512 _post_send (GtkCupsRequest *request)
515 struct stat data_info;
517 request->poll_state = GTK_CUPS_HTTP_WRITE;
519 if (request->data_fd != 0)
521 fstat (request->data_fd, &data_info);
522 sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request) + data_info.st_size);
526 sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request));
529 httpClearFields(request->http);
530 httpSetField(request->http, HTTP_FIELD_CONTENT_LENGTH, length);
531 httpSetField(request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
532 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
534 if (httpPost(request->http, request->resource))
536 if (httpReconnect(request->http))
538 request->state = GTK_CUPS_POST_DONE;
539 request->poll_state = GTK_CUPS_HTTP_IDLE;
541 gtk_cups_result_set_error (request->result, "Failed Post");
548 request->attempts = 0;
550 request->state = GTK_CUPS_POST_WRITE_REQUEST;
551 request->ipp_request->state = IPP_IDLE;
555 _post_write_request (GtkCupsRequest *request)
557 ipp_state_t ipp_status;
559 request->poll_state = GTK_CUPS_HTTP_WRITE;
561 ipp_status = ippWrite(request->http, request->ipp_request);
563 if (ipp_status == IPP_ERROR)
565 request->state = GTK_CUPS_POST_DONE;
566 request->poll_state = GTK_CUPS_HTTP_IDLE;
568 gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ()));
572 if (ipp_status == IPP_DATA)
574 if (request->data_fd != 0)
575 request->state = GTK_CUPS_POST_WRITE_DATA;
578 request->state = GTK_CUPS_POST_CHECK;
579 request->poll_state = GTK_CUPS_HTTP_READ;
585 _post_write_data (GtkCupsRequest *request)
588 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
589 http_status_t http_status;
591 request->poll_state = GTK_CUPS_HTTP_WRITE;
593 if (httpCheck (request->http))
594 http_status = httpUpdate(request->http);
596 http_status = request->last_status;
598 request->last_status = http_status;
601 if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
604 bytes = read(request->data_fd, buffer, _GTK_CUPS_MAX_CHUNK_SIZE);
608 request->state = GTK_CUPS_POST_CHECK;
609 request->poll_state = GTK_CUPS_HTTP_READ;
611 request->attempts = 0;
614 else if (bytes == -1)
616 request->state = GTK_CUPS_POST_DONE;
617 request->poll_state = GTK_CUPS_HTTP_IDLE;
619 gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", strerror (errno));
623 if (httpWrite(request->http, buffer, (int)bytes) < bytes)
625 request->state = GTK_CUPS_POST_DONE;
626 request->poll_state = GTK_CUPS_HTTP_IDLE;
628 gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http)));
639 _post_check (GtkCupsRequest *request)
641 http_status_t http_status;
643 http_status = request->last_status;
645 request->poll_state = GTK_CUPS_HTTP_READ;
647 if (http_status == HTTP_CONTINUE)
651 else if (http_status == HTTP_UNAUTHORIZED)
653 /* TODO: callout for auth */
654 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
655 request->state = GTK_CUPS_POST_DONE;
656 request->poll_state = GTK_CUPS_HTTP_IDLE;
658 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
661 else if (http_status == HTTP_ERROR)
664 if (request->http->error != WSAENETDOWN &&
665 request->http->error != WSAENETUNREACH)
667 if (request->http->error != ENETDOWN &&
668 request->http->error != ENETUNREACH)
669 #endif /* G_OS_WIN32 */
676 request->state = GTK_CUPS_POST_DONE;
677 request->poll_state = GTK_CUPS_HTTP_IDLE;
679 gtk_cups_result_set_error (request->result, "Unknown HTTP error");
683 /* TODO: detect ssl in configure.ac */
685 else if (http_status == HTTP_UPGRADE_REQUIRED)
687 /* Flush any error message... */
688 httpFlush (request->http);
691 httpReconnect (request->http);
693 /* Upgrade with encryption... */
694 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
700 else if (http_status != HTTP_OK)
704 http_errno = httpError (request->http);
706 if (http_errno == EPIPE)
707 request->state = GTK_CUPS_POST_CONNECT;
710 request->state = GTK_CUPS_POST_DONE;
711 gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno));
712 request->poll_state = GTK_CUPS_HTTP_IDLE;
714 httpFlush(request->http);
718 request->poll_state = GTK_CUPS_HTTP_IDLE;
720 httpFlush(request->http);
722 request->last_status = HTTP_CONTINUE;
723 httpClose (request->http);
724 request->http = NULL;
729 request->state = GTK_CUPS_POST_READ_RESPONSE;
734 http_status = HTTP_CONTINUE;
736 if (httpCheck (request->http))
737 http_status = httpUpdate (request->http);
739 request->last_status = http_status;
743 _post_read_response (GtkCupsRequest *request)
745 ipp_state_t ipp_status;
747 request->poll_state = GTK_CUPS_HTTP_READ;
749 if (request->result->ipp_response == NULL)
750 request->result->ipp_response = ippNew();
752 ipp_status = ippRead (request->http,
753 request->result->ipp_response);
755 if (ipp_status == IPP_ERROR)
757 gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
759 ippDelete (request->result->ipp_response);
760 request->result->ipp_response = NULL;
762 request->state = GTK_CUPS_POST_DONE;
763 request->poll_state = GTK_CUPS_HTTP_IDLE;
765 else if (ipp_status == IPP_DATA)
767 request->state = GTK_CUPS_POST_DONE;
768 request->poll_state = GTK_CUPS_HTTP_IDLE;
773 _get_send (GtkCupsRequest *request)
775 request->poll_state = GTK_CUPS_HTTP_WRITE;
777 if (request->data_fd == 0)
779 gtk_cups_result_set_error (request->result, "Get requires an open file descriptor");
780 request->state = GTK_CUPS_GET_DONE;
781 request->poll_state = GTK_CUPS_HTTP_IDLE;
786 httpClearFields(request->http);
787 httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
789 if (httpGet(request->http, request->resource))
791 if (httpReconnect(request->http))
793 request->state = GTK_CUPS_GET_DONE;
794 request->poll_state = GTK_CUPS_HTTP_IDLE;
796 gtk_cups_result_set_error (request->result, "Failed Get");
803 request->attempts = 0;
805 request->state = GTK_CUPS_GET_CHECK;
806 request->poll_state = GTK_CUPS_HTTP_READ;
808 request->ipp_request->state = IPP_IDLE;
812 _get_check (GtkCupsRequest *request)
814 http_status_t http_status;
816 http_status = request->last_status;
818 request->poll_state = GTK_CUPS_HTTP_READ;
820 if (http_status == HTTP_CONTINUE)
824 else if (http_status == HTTP_UNAUTHORIZED)
826 /* TODO: callout for auth */
827 g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
828 request->state = GTK_CUPS_GET_DONE;
829 request->poll_state = GTK_CUPS_HTTP_IDLE;
831 gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
834 /* TODO: detect ssl in configure.ac */
836 else if (http_status == HTTP_UPGRADE_REQUIRED)
838 /* Flush any error message... */
839 httpFlush (request->http);
842 httpReconnect (request->http);
844 /* Upgrade with encryption... */
845 httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
851 else if (http_status != HTTP_OK)
855 http_errno = httpError (request->http);
857 if (http_errno == EPIPE)
858 request->state = GTK_CUPS_GET_CONNECT;
861 request->state = GTK_CUPS_GET_DONE;
862 gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno));
863 request->poll_state = GTK_CUPS_HTTP_IDLE;
864 httpFlush(request->http);
869 request->poll_state = GTK_CUPS_HTTP_IDLE;
870 httpFlush (request->http);
871 httpClose (request->http);
872 request->last_status = HTTP_CONTINUE;
873 request->http = NULL;
879 request->state = GTK_CUPS_GET_READ_DATA;
884 http_status = HTTP_CONTINUE;
886 if (httpCheck (request->http))
887 http_status = httpUpdate (request->http);
889 request->last_status = http_status;
894 _get_read_data (GtkCupsRequest *request)
896 char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
899 request->poll_state = GTK_CUPS_HTTP_READ;
901 bytes = httpRead(request->http, buffer, sizeof(buffer));
905 request->state = GTK_CUPS_GET_DONE;
906 request->poll_state = GTK_CUPS_HTTP_IDLE;
911 if (write (request->data_fd, buffer, bytes) == -1)
915 request->state = GTK_CUPS_POST_DONE;
916 request->poll_state = GTK_CUPS_HTTP_IDLE;
918 error_msg = strerror (errno);
919 gtk_cups_result_set_error (request->result, error_msg ? error_msg:"");
924 gtk_cups_request_is_done (GtkCupsRequest *request)
926 return (request->state == GTK_CUPS_REQUEST_DONE);
930 gtk_cups_result_is_error (GtkCupsResult *result)
932 return result->is_error;
936 gtk_cups_result_get_response (GtkCupsResult *result)
938 return result->ipp_response;
942 gtk_cups_result_get_error_string (GtkCupsResult *result)
944 return result->error_msg;