]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkcupsutils.c
Properly pass multi-value options to cups. (#355350, Jürg Billeter)
[~andy/gtk] / modules / printbackends / cups / gtkcupsutils.c
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.
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 #include "gtkcupsutils.h"
23 #include "config.h"
24 #include "gtkdebug.h"
25
26 #include <errno.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <stdlib.h>
31 #include <time.h>
32
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
35 #endif
36
37 typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
38
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);
45
46 static void _get_send           (GtkCupsRequest *request);
47 static void _get_check          (GtkCupsRequest *request);
48 static void _get_read_data      (GtkCupsRequest *request);
49
50 struct _GtkCupsResult
51 {
52   gchar *error_msg;
53   ipp_t *ipp_response;
54
55   guint is_error : 1;
56   guint is_ipp_response : 1;
57 };
58
59
60 #define _GTK_CUPS_MAX_ATTEMPTS 10 
61 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
62
63 static GtkCupsRequestStateFunc post_states[] = {
64   _connect,
65   _post_send,
66   _post_write_request,
67   _post_write_data,
68   _post_check,
69   _post_read_response
70 };
71
72 static GtkCupsRequestStateFunc get_states[] = {
73   _connect,
74   _get_send,
75   _get_check,
76   _get_read_data
77 };
78
79 static void
80 gtk_cups_result_set_error (GtkCupsResult *result, 
81                            const char    *error_msg,
82                            ...)
83 {
84   va_list args;
85
86   result->is_ipp_response = FALSE;
87
88   result->is_error = TRUE;
89
90   va_start (args, error_msg);
91   result->error_msg = g_strdup_vprintf (error_msg, args);
92   va_end (args);
93 }
94
95 GtkCupsRequest *
96 gtk_cups_request_new (http_t             *connection,
97                       GtkCupsRequestType  req_type, 
98                       gint                operation_id,
99                       GIOChannel         *data_io,
100                       const char         *server,
101                       const char         *resource)
102 {
103   GtkCupsRequest *request;
104   cups_lang_t *language;
105   
106   request = g_new0 (GtkCupsRequest, 1);
107   request->result = g_new0 (GtkCupsResult, 1);
108
109   request->result->error_msg = NULL;
110   request->result->ipp_response = NULL;
111
112   request->result->is_error = FALSE;
113   request->result->is_ipp_response = FALSE;
114
115   request->type = req_type;
116   request->state = GTK_CUPS_REQUEST_START;
117
118    if (server)
119     request->server = g_strdup (server);
120   else
121     request->server = g_strdup (cupsServer());
122
123
124   if (resource)
125     request->resource = g_strdup (resource);
126   else
127     request->resource = g_strdup ("/");
128  
129   if (connection != NULL)
130     {
131       request->http = connection;
132       request->own_http = FALSE;
133     }
134   else
135     {
136       request->http = NULL;
137       request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
138
139       if (request->http)
140         httpBlocking (request->http, 0);
141         
142       request->own_http = TRUE;
143     }
144
145   request->last_status = HTTP_CONTINUE;
146
147   request->attempts = 0;
148   request->data_io = data_io;
149
150   request->ipp_request = ippNew();
151   request->ipp_request->request.op.operation_id = operation_id;
152   request->ipp_request->request.op.request_id = 1;
153
154   language = cupsLangDefault ();
155
156   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
157                                    "attributes-charset", 
158                                    NULL, "utf-8");
159         
160   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
161                                    "attributes-natural-language", 
162                                    NULL, language->language);
163
164   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
165                                    "requesting-user-name",
166                                    NULL, cupsUser());
167   
168   cupsLangFree (language);
169
170   return request;
171 }
172
173 static void
174 gtk_cups_result_free (GtkCupsResult *result)
175 {
176   g_free (result->error_msg);
177
178   if (result->ipp_response)
179     ippDelete (result->ipp_response);
180
181   g_free (result);
182 }
183
184 void
185 gtk_cups_request_free (GtkCupsRequest *request)
186 {
187   if (request->own_http)
188     if (request->http)
189       httpClose (request->http);
190   
191   if (request->ipp_request)
192     ippDelete (request->ipp_request);
193
194   g_free (request->server);
195   g_free (request->resource);
196
197   gtk_cups_result_free (request->result);
198
199   g_free (request);
200 }
201
202 gboolean 
203 gtk_cups_request_read_write (GtkCupsRequest *request)
204 {
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);
209
210   if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS && 
211       request->state != GTK_CUPS_REQUEST_DONE)
212     {
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;
216     }
217     
218   if (request->state == GTK_CUPS_REQUEST_DONE)
219     {
220       request->poll_state = GTK_CUPS_HTTP_IDLE;
221       return TRUE;
222     }
223   else
224     {
225       return FALSE;
226     }
227 }
228
229 GtkCupsPollState 
230 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
231 {
232   return request->poll_state;
233 }
234
235
236
237 GtkCupsResult *
238 gtk_cups_request_get_result (GtkCupsRequest *request)
239 {
240   return request->result;
241 }
242
243 void            
244 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
245                                  ipp_tag_t       group,
246                                  ipp_tag_t       tag,
247                                  const char     *name,
248                                  const char     *charset,
249                                  const char     *value)
250 {
251   ippAddString (request->ipp_request,
252                 group,
253                 tag,
254                 name,
255                 charset,
256                 value);
257 }
258
259 void            
260 gtk_cups_request_ipp_add_strings (GtkCupsRequest    *request,
261                                   ipp_tag_t          group,
262                                   ipp_tag_t          tag,
263                                   const char        *name,
264                                   int                num_values,
265                                   const char        *charset,
266                                   const char *const *values)
267 {
268   ippAddStrings (request->ipp_request,
269                  group,
270                  tag,
271                  name,
272                  num_values,
273                  charset,
274                  values);
275 }
276
277
278
279 typedef struct
280 {
281   const char    *name;
282   ipp_tag_t     value_tag;
283 } ipp_option_t;
284
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 }
318 };
319
320
321 static ipp_tag_t
322 _find_option_tag (const gchar *option)
323 {
324   int lower_bound, upper_bound, num_options;
325   int current_option;
326   ipp_tag_t result;
327
328   result = IPP_TAG_ZERO;
329
330   lower_bound = 0;
331   upper_bound = num_options = (int)(sizeof(ipp_options) / sizeof(ipp_options[0])) - 1;
332   
333   while (1)
334     {
335       int match;
336       current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
337
338       match = strcasecmp(option, ipp_options[current_option].name);
339       if (match == 0)
340         {
341           result = ipp_options[current_option].value_tag;
342           return result;
343         }
344       else if (match < 0)
345         {
346           upper_bound = current_option - 1;
347         }
348       else
349         {
350           lower_bound = current_option + 1;
351         }
352
353       if (upper_bound == lower_bound && upper_bound == current_option)
354         return result;
355
356       if (upper_bound < 0)
357         return result;
358
359       if (lower_bound > num_options)
360         return result;
361
362       if (upper_bound < lower_bound)
363         return result;
364     }
365 }
366
367 void
368 gtk_cups_request_encode_option (GtkCupsRequest *request,
369                                 const gchar    *option,
370                                 const gchar    *value)
371 {
372   ipp_tag_t option_tag;
373
374   g_assert (option != NULL);
375   g_assert (value != NULL);
376
377   option_tag = _find_option_tag (option);
378
379   if (option_tag == IPP_TAG_ZERO)
380     {
381       option_tag = IPP_TAG_NAME;
382       if (strcasecmp (value, "true") == 0 ||
383           strcasecmp (value, "false") == 0)
384         {
385           option_tag = IPP_TAG_BOOLEAN;
386         }
387     }
388         
389   switch (option_tag)
390     {
391       case IPP_TAG_INTEGER:
392       case IPP_TAG_ENUM:
393         ippAddInteger (request->ipp_request,
394                        IPP_TAG_OPERATION,
395                        option_tag,
396                        option,
397                        strtol (value, NULL, 0));
398         break;
399
400       case IPP_TAG_BOOLEAN:
401         {
402           char b;
403           b = 0;
404           if (!strcasecmp(value, "true") ||
405               !strcasecmp(value, "on") ||
406               !strcasecmp(value, "yes")) 
407             b = 1;
408           
409           ippAddBoolean(request->ipp_request,
410                         IPP_TAG_OPERATION,
411                         option,
412                         b);
413         
414           break;
415         }
416         
417       case IPP_TAG_RANGE:
418         {
419           char  *s;
420           int lower;
421           int upper;
422           
423           if (*value == '-')
424             {
425               lower = 1;
426               s = (char *)value;
427             }
428           else
429             lower = strtol(value, &s, 0);
430
431           if (*s == '-')
432             {
433               if (s[1])
434                 upper = strtol(s + 1, NULL, 0);
435               else
436                 upper = 2147483647;
437             }
438           else
439             upper = lower;
440          
441           ippAddRange (request->ipp_request,
442                        IPP_TAG_OPERATION,
443                        option,
444                        lower,
445                        upper);
446
447           break;
448         }
449
450       case IPP_TAG_RESOLUTION:
451         {
452           char *s;
453           int xres;
454           int yres;
455           ipp_res_t units;
456           
457           xres = strtol(value, &s, 0);
458
459           if (*s == 'x')
460             yres = strtol(s + 1, &s, 0);
461           else
462             yres = xres;
463
464           if (strcasecmp(s, "dpc") == 0)
465             units = IPP_RES_PER_CM;
466           else
467             units = IPP_RES_PER_INCH;
468           
469           ippAddResolution (request->ipp_request,
470                             IPP_TAG_OPERATION,
471                             option,
472                             units,
473                             xres,
474                             yres);
475
476           break;
477         }
478
479       default:
480         {
481           char *values;
482           char *s;
483           int in_quotes;
484           char *next;
485           GPtrArray *strings;
486           
487           values = g_strdup (value);
488           strings = NULL;
489           in_quotes = 0;
490
491           for (s = values, next = s; *s != '\0'; s++)
492             {
493               if (in_quotes != 2 && *s == '\'')
494                 {
495                   /* skip quoted value */
496                   if (in_quotes == 0)
497                     in_quotes = 1;
498                   else
499                     in_quotes = 0;
500                 }
501               else if (in_quotes != 1 && *s == '\"')
502                 {
503                   /* skip quoted value */
504                   if (in_quotes == 0)
505                     in_quotes = 2;
506                   else
507                     in_quotes = 0;
508                 }
509               else if (in_quotes == 0 && *s == ',')
510                 {
511                   /* found delimiter, add to value array */
512                   *s = '\0';
513                   if (strings == NULL)
514                     strings = g_ptr_array_new ();
515                   g_ptr_array_add (strings, next);
516                   next = s + 1;
517                 }
518               else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
519                 {
520                   /* skip escaped character */
521                   s++;
522                 }
523             }
524           
525           if (strings == NULL)
526             {
527               /* single value */
528               ippAddString (request->ipp_request,
529                             IPP_TAG_OPERATION,
530                             option_tag,
531                             option,
532                             NULL,
533                             value);
534             }
535           else
536             {
537               /* multiple values */
538               
539               /* add last value */
540               g_ptr_array_add (strings, next);
541               
542               ippAddStrings (request->ipp_request,
543                              IPP_TAG_OPERATION,
544                              option_tag,
545                              option,
546                              strings->len,
547                              NULL,
548                              (const char **) strings->pdata);
549               g_ptr_array_free (strings, TRUE);
550             }
551
552           g_free (values);
553         }
554
555         break;
556     }
557 }
558                                 
559
560 static void
561 _connect (GtkCupsRequest *request)
562 {
563   request->poll_state = GTK_CUPS_HTTP_IDLE;
564
565   if (request->http == NULL)
566     {
567       request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
568
569       if (request->http == NULL)
570         request->attempts++;
571
572       if (request->http)
573         httpBlocking (request->http, 0);
574         
575       request->own_http = TRUE;
576     }
577   else
578     {
579       request->attempts = 0;
580       request->state++;
581
582       /* we always write to the socket after we get
583          the connection */
584       request->poll_state = GTK_CUPS_HTTP_WRITE;
585     }
586 }
587
588 static void 
589 _post_send (GtkCupsRequest *request)
590 {
591   gchar length[255];
592   struct stat data_info;
593
594   GTK_NOTE (PRINTING,
595             g_print ("CUPS Backend: %s\n", G_STRFUNC));
596
597   request->poll_state = GTK_CUPS_HTTP_WRITE;
598
599   if (request->data_io != NULL)
600     {
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);
603     }
604   else
605     {
606       sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request));
607     }
608         
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);
614 #endif
615
616   if (httpPost(request->http, request->resource))
617     {
618       if (httpReconnect(request->http))
619         {
620           request->state = GTK_CUPS_POST_DONE;
621           request->poll_state = GTK_CUPS_HTTP_IDLE;
622
623           gtk_cups_result_set_error (request->result, "Failed Post");
624         }
625
626       request->attempts++;
627       return;    
628     }
629         
630     request->attempts = 0;
631
632     request->state = GTK_CUPS_POST_WRITE_REQUEST;
633     request->ipp_request->state = IPP_IDLE;
634 }
635
636 static void 
637 _post_write_request (GtkCupsRequest *request)
638 {
639   ipp_state_t ipp_status;
640
641   GTK_NOTE (PRINTING,
642             g_print ("CUPS Backend: %s\n", G_STRFUNC));
643
644   request->poll_state = GTK_CUPS_HTTP_WRITE;
645   
646   ipp_status = ippWrite(request->http, request->ipp_request);
647
648   if (ipp_status == IPP_ERROR)
649     {
650       request->state = GTK_CUPS_POST_DONE;
651       request->poll_state = GTK_CUPS_HTTP_IDLE;
652  
653       gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ()));
654       return;
655     }
656
657   if (ipp_status == IPP_DATA)
658     {
659       if (request->data_io != NULL)
660         request->state = GTK_CUPS_POST_WRITE_DATA;
661       else
662         {
663           request->state = GTK_CUPS_POST_CHECK;
664           request->poll_state = GTK_CUPS_HTTP_READ;
665         }
666     }
667 }
668
669 static void 
670 _post_write_data (GtkCupsRequest *request)
671 {
672   gsize bytes;
673   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
674   http_status_t http_status;
675
676   GTK_NOTE (PRINTING,
677             g_print ("CUPS Backend: %s\n", G_STRFUNC));
678
679   request->poll_state = GTK_CUPS_HTTP_WRITE;
680   
681   if (httpCheck (request->http))
682     http_status = httpUpdate(request->http);
683   else
684     http_status = request->last_status;
685
686   request->last_status = http_status;
687
688
689   if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
690     {
691       GIOStatus io_status;
692       GError *error;
693
694       error = NULL;
695
696       /* send data */
697       io_status =
698         g_io_channel_read_chars (request->data_io, 
699                                  buffer, 
700                                  _GTK_CUPS_MAX_CHUNK_SIZE,
701                                  &bytes,
702                                  &error);
703
704       if (io_status == G_IO_STATUS_ERROR)
705         {
706           request->state = GTK_CUPS_POST_DONE;
707           request->poll_state = GTK_CUPS_HTTP_IDLE;
708      
709           gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", error->message);
710
711           g_error_free (error);
712           return;
713         }
714       else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
715         {
716           request->state = GTK_CUPS_POST_CHECK;
717           request->poll_state = GTK_CUPS_HTTP_READ;
718
719           request->attempts = 0;
720           return;
721         }
722
723
724 #if HAVE_CUPS_API_1_2
725       if (httpWrite2(request->http, buffer, bytes) < bytes)
726 #else
727       if (httpWrite(request->http, buffer, (int) bytes) < bytes)
728 #endif /* HAVE_CUPS_API_1_2 */
729         {
730           request->state = GTK_CUPS_POST_DONE;
731           request->poll_state = GTK_CUPS_HTTP_IDLE;
732      
733           gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http)));
734           return;
735         }
736     }
737    else
738     {
739       request->attempts++;
740     }
741 }
742
743 static void 
744 _post_check (GtkCupsRequest *request)
745 {
746   http_status_t http_status;
747
748   http_status = request->last_status;
749
750   GTK_NOTE (PRINTING,
751             g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
752
753   request->poll_state = GTK_CUPS_HTTP_READ;
754
755   if (http_status == HTTP_CONTINUE)
756     {
757       goto again; 
758     }
759   else if (http_status == HTTP_UNAUTHORIZED)
760     {
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;
765       
766       gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
767       return;
768     }
769   else if (http_status == HTTP_ERROR)
770     {
771 #ifdef G_OS_WIN32
772       if (request->http->error != WSAENETDOWN && 
773           request->http->error != WSAENETUNREACH)
774 #else
775       if (request->http->error != ENETDOWN && 
776           request->http->error != ENETUNREACH)
777 #endif /* G_OS_WIN32 */
778         {
779           request->attempts++;
780           goto again;
781         }
782       else
783         {
784           request->state = GTK_CUPS_POST_DONE;
785           request->poll_state = GTK_CUPS_HTTP_IDLE;
786      
787           gtk_cups_result_set_error (request->result, "Unknown HTTP error");
788           return;
789         }
790     }
791 /* TODO: detect ssl in configure.ac */
792 #if HAVE_SSL
793   else if (http_status == HTTP_UPGRADE_REQUIRED)
794     {
795       /* Flush any error message... */
796       httpFlush (request->http);
797
798       /* Reconnect... */
799       httpReconnect (request->http);
800
801       /* Upgrade with encryption... */
802       httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
803  
804       request->attempts++;
805       goto again;
806     }
807 #endif 
808   else if (http_status != HTTP_OK)
809     {
810       int http_errno;
811
812       http_errno = httpError (request->http);
813
814       if (http_errno == EPIPE)
815         request->state = GTK_CUPS_POST_CONNECT;
816       else
817         {
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;
821  
822           httpFlush(request->http); 
823           return;
824         }
825
826       request->poll_state = GTK_CUPS_HTTP_IDLE;
827        
828       httpFlush(request->http); 
829       
830       request->last_status = HTTP_CONTINUE;
831       httpClose (request->http);
832       request->http = NULL;
833       return;  
834     }
835   else
836     {
837       request->state = GTK_CUPS_POST_READ_RESPONSE;
838       return;
839     }
840
841  again:
842   http_status = HTTP_CONTINUE;
843
844   if (httpCheck (request->http))
845     http_status = httpUpdate (request->http);
846
847   request->last_status = http_status;
848 }
849
850 static void 
851 _post_read_response (GtkCupsRequest *request)
852 {
853   ipp_state_t ipp_status;
854
855   GTK_NOTE (PRINTING,
856             g_print ("CUPS Backend: %s\n", G_STRFUNC));
857
858   request->poll_state = GTK_CUPS_HTTP_READ;
859
860   if (request->result->ipp_response == NULL)
861     request->result->ipp_response = ippNew();
862
863   ipp_status = ippRead (request->http, 
864                         request->result->ipp_response);
865
866   if (ipp_status == IPP_ERROR)
867     {
868       gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
869       
870       ippDelete (request->result->ipp_response);
871       request->result->ipp_response = NULL;
872
873       request->state = GTK_CUPS_POST_DONE;
874       request->poll_state = GTK_CUPS_HTTP_IDLE;
875     }
876   else if (ipp_status == IPP_DATA)
877     {
878       request->state = GTK_CUPS_POST_DONE;
879       request->poll_state = GTK_CUPS_HTTP_IDLE;
880     }
881 }
882
883 static void 
884 _get_send (GtkCupsRequest *request)
885 {
886   GTK_NOTE (PRINTING,
887             g_print ("CUPS Backend: %s\n", G_STRFUNC));
888
889   request->poll_state = GTK_CUPS_HTTP_WRITE;
890
891   if (request->data_io == NULL)
892     {
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;
896
897       return;
898     }
899
900   httpClearFields(request->http);
901 #ifdef HAVE_HTTP_AUTHSTRING
902   httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
903 #endif
904
905   if (httpGet(request->http, request->resource))
906     {
907       if (httpReconnect(request->http))
908         {
909           request->state = GTK_CUPS_GET_DONE;
910           request->poll_state = GTK_CUPS_HTTP_IDLE;
911           
912           gtk_cups_result_set_error (request->result, "Failed Get");
913         }
914
915       request->attempts++;
916       return;    
917     }
918         
919   request->attempts = 0;
920
921   request->state = GTK_CUPS_GET_CHECK;
922   request->poll_state = GTK_CUPS_HTTP_READ;
923   
924   request->ipp_request->state = IPP_IDLE;
925 }
926
927 static void 
928 _get_check (GtkCupsRequest *request)
929 {
930   http_status_t http_status;
931
932   GTK_NOTE (PRINTING,
933             g_print ("CUPS Backend: %s\n", G_STRFUNC));
934
935   http_status = request->last_status;
936
937   request->poll_state = GTK_CUPS_HTTP_READ;
938
939   if (http_status == HTTP_CONTINUE)
940     {
941       goto again; 
942     }
943   else if (http_status == HTTP_UNAUTHORIZED)
944     {
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;
949  
950       gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
951       return;
952     }
953 /* TODO: detect ssl in configure.ac */
954 #if HAVE_SSL
955   else if (http_status == HTTP_UPGRADE_REQUIRED)
956     {
957       /* Flush any error message... */
958       httpFlush (request->http);
959
960       /* Reconnect... */
961       httpReconnect (request->http);
962
963       /* Upgrade with encryption... */
964       httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
965  
966       request->attempts++;
967       goto again;
968     }
969 #endif 
970   else if (http_status != HTTP_OK)
971     {
972       int http_errno;
973
974       http_errno = httpError (request->http);
975
976       if (http_errno == EPIPE)
977         request->state = GTK_CUPS_GET_CONNECT;
978       else
979         {
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);
984
985           return;
986         }
987
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;
993       return;
994
995     }
996   else
997     {
998       request->state = GTK_CUPS_GET_READ_DATA;
999       return;
1000     }
1001
1002  again:
1003   http_status = HTTP_CONTINUE;
1004
1005   if (httpCheck (request->http))
1006     http_status = httpUpdate (request->http);
1007
1008   request->last_status = http_status;
1009
1010 }
1011
1012 static void 
1013 _get_read_data (GtkCupsRequest *request)
1014 {
1015   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1016   gsize bytes;
1017   gsize bytes_written;
1018   GIOStatus io_status;
1019   GError *error;
1020
1021   GTK_NOTE (PRINTING,
1022             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1023
1024   error = NULL;
1025
1026   request->poll_state = GTK_CUPS_HTTP_READ;
1027
1028 #if HAVE_CUPS_API_1_2
1029   bytes = httpRead2(request->http, buffer, sizeof(buffer));
1030 #else
1031   bytes = httpRead(request->http, buffer, sizeof(buffer));
1032 #endif /* HAVE_CUPS_API_1_2 */
1033
1034   GTK_NOTE (PRINTING,
1035             g_print ("CUPS Backend: %i bytes read\n", bytes));
1036   
1037   if (bytes == 0)
1038     {
1039       request->state = GTK_CUPS_GET_DONE;
1040       request->poll_state = GTK_CUPS_HTTP_IDLE;
1041
1042       return;
1043     }
1044   
1045   io_status =
1046     g_io_channel_write_chars (request->data_io, 
1047                               buffer, 
1048                               bytes, 
1049                               &bytes_written,
1050                               &error);
1051
1052   if (io_status == G_IO_STATUS_ERROR)
1053     {
1054       request->state = GTK_CUPS_POST_DONE;
1055       request->poll_state = GTK_CUPS_HTTP_IDLE;
1056     
1057       gtk_cups_result_set_error (request->result, error->message);
1058       g_error_free (error);
1059     }
1060 }
1061
1062 gboolean
1063 gtk_cups_request_is_done (GtkCupsRequest *request)
1064 {
1065   return (request->state == GTK_CUPS_REQUEST_DONE);
1066 }
1067
1068 gboolean
1069 gtk_cups_result_is_error (GtkCupsResult *result)
1070 {
1071   return result->is_error;
1072 }
1073
1074 ipp_t *
1075 gtk_cups_result_get_response (GtkCupsResult *result)
1076 {
1077   return result->ipp_response;
1078 }
1079
1080 const char *
1081 gtk_cups_result_get_error_string (GtkCupsResult *result)
1082 {
1083   return result->error_msg; 
1084 }
1085