]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkcupsutils.c
forgotten file
[~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       int error = httpError (request->http);
772 #ifdef G_OS_WIN32
773       if (error != WSAENETDOWN && error != WSAENETUNREACH)
774 #else
775       if (error != ENETDOWN && error != ENETUNREACH)      
776 #endif /* G_OS_WIN32 */
777         {
778           request->attempts++;
779           goto again;
780         }
781       else
782         {
783           request->state = GTK_CUPS_POST_DONE;
784           request->poll_state = GTK_CUPS_HTTP_IDLE;
785      
786           gtk_cups_result_set_error (request->result, "Unknown HTTP error");
787           return;
788         }
789     }
790 /* TODO: detect ssl in configure.ac */
791 #if HAVE_SSL
792   else if (http_status == HTTP_UPGRADE_REQUIRED)
793     {
794       /* Flush any error message... */
795       httpFlush (request->http);
796
797       /* Reconnect... */
798       httpReconnect (request->http);
799
800       /* Upgrade with encryption... */
801       httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
802  
803       request->attempts++;
804       goto again;
805     }
806 #endif 
807   else if (http_status != HTTP_OK)
808     {
809       int http_errno;
810
811       http_errno = httpError (request->http);
812
813       if (http_errno == EPIPE)
814         request->state = GTK_CUPS_POST_CONNECT;
815       else
816         {
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;
820  
821           httpFlush(request->http); 
822           return;
823         }
824
825       request->poll_state = GTK_CUPS_HTTP_IDLE;
826        
827       httpFlush(request->http); 
828       
829       request->last_status = HTTP_CONTINUE;
830       httpClose (request->http);
831       request->http = NULL;
832       return;  
833     }
834   else
835     {
836       request->state = GTK_CUPS_POST_READ_RESPONSE;
837       return;
838     }
839
840  again:
841   http_status = HTTP_CONTINUE;
842
843   if (httpCheck (request->http))
844     http_status = httpUpdate (request->http);
845
846   request->last_status = http_status;
847 }
848
849 static void 
850 _post_read_response (GtkCupsRequest *request)
851 {
852   ipp_state_t ipp_status;
853
854   GTK_NOTE (PRINTING,
855             g_print ("CUPS Backend: %s\n", G_STRFUNC));
856
857   request->poll_state = GTK_CUPS_HTTP_READ;
858
859   if (request->result->ipp_response == NULL)
860     request->result->ipp_response = ippNew();
861
862   ipp_status = ippRead (request->http, 
863                         request->result->ipp_response);
864
865   if (ipp_status == IPP_ERROR)
866     {
867       gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
868       
869       ippDelete (request->result->ipp_response);
870       request->result->ipp_response = NULL;
871
872       request->state = GTK_CUPS_POST_DONE;
873       request->poll_state = GTK_CUPS_HTTP_IDLE;
874     }
875   else if (ipp_status == IPP_DATA)
876     {
877       request->state = GTK_CUPS_POST_DONE;
878       request->poll_state = GTK_CUPS_HTTP_IDLE;
879     }
880 }
881
882 static void 
883 _get_send (GtkCupsRequest *request)
884 {
885   GTK_NOTE (PRINTING,
886             g_print ("CUPS Backend: %s\n", G_STRFUNC));
887
888   request->poll_state = GTK_CUPS_HTTP_WRITE;
889
890   if (request->data_io == NULL)
891     {
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;
895
896       return;
897     }
898
899   httpClearFields(request->http);
900 #ifdef HAVE_HTTP_AUTHSTRING
901   httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
902 #endif
903
904   if (httpGet(request->http, request->resource))
905     {
906       if (httpReconnect(request->http))
907         {
908           request->state = GTK_CUPS_GET_DONE;
909           request->poll_state = GTK_CUPS_HTTP_IDLE;
910           
911           gtk_cups_result_set_error (request->result, "Failed Get");
912         }
913
914       request->attempts++;
915       return;    
916     }
917         
918   request->attempts = 0;
919
920   request->state = GTK_CUPS_GET_CHECK;
921   request->poll_state = GTK_CUPS_HTTP_READ;
922   
923   request->ipp_request->state = IPP_IDLE;
924 }
925
926 static void 
927 _get_check (GtkCupsRequest *request)
928 {
929   http_status_t http_status;
930
931   GTK_NOTE (PRINTING,
932             g_print ("CUPS Backend: %s\n", G_STRFUNC));
933
934   http_status = request->last_status;
935
936   request->poll_state = GTK_CUPS_HTTP_READ;
937
938   if (http_status == HTTP_CONTINUE)
939     {
940       goto again; 
941     }
942   else if (http_status == HTTP_UNAUTHORIZED)
943     {
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;
948  
949       gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
950       return;
951     }
952 /* TODO: detect ssl in configure.ac */
953 #if HAVE_SSL
954   else if (http_status == HTTP_UPGRADE_REQUIRED)
955     {
956       /* Flush any error message... */
957       httpFlush (request->http);
958
959       /* Reconnect... */
960       httpReconnect (request->http);
961
962       /* Upgrade with encryption... */
963       httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
964  
965       request->attempts++;
966       goto again;
967     }
968 #endif 
969   else if (http_status != HTTP_OK)
970     {
971       int http_errno;
972
973       http_errno = httpError (request->http);
974
975       if (http_errno == EPIPE)
976         request->state = GTK_CUPS_GET_CONNECT;
977       else
978         {
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);
983
984           return;
985         }
986
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;
992       return;
993
994     }
995   else
996     {
997       request->state = GTK_CUPS_GET_READ_DATA;
998       return;
999     }
1000
1001  again:
1002   http_status = HTTP_CONTINUE;
1003
1004   if (httpCheck (request->http))
1005     http_status = httpUpdate (request->http);
1006
1007   request->last_status = http_status;
1008
1009 }
1010
1011 static void 
1012 _get_read_data (GtkCupsRequest *request)
1013 {
1014   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1015   gsize bytes;
1016   gsize bytes_written;
1017   GIOStatus io_status;
1018   GError *error;
1019
1020   GTK_NOTE (PRINTING,
1021             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1022
1023   error = NULL;
1024
1025   request->poll_state = GTK_CUPS_HTTP_READ;
1026
1027 #if HAVE_CUPS_API_1_2
1028   bytes = httpRead2(request->http, buffer, sizeof(buffer));
1029 #else
1030   bytes = httpRead(request->http, buffer, sizeof(buffer));
1031 #endif /* HAVE_CUPS_API_1_2 */
1032
1033   GTK_NOTE (PRINTING,
1034             g_print ("CUPS Backend: %i bytes read\n", bytes));
1035   
1036   if (bytes == 0)
1037     {
1038       request->state = GTK_CUPS_GET_DONE;
1039       request->poll_state = GTK_CUPS_HTTP_IDLE;
1040
1041       return;
1042     }
1043   
1044   io_status =
1045     g_io_channel_write_chars (request->data_io, 
1046                               buffer, 
1047                               bytes, 
1048                               &bytes_written,
1049                               &error);
1050
1051   if (io_status == G_IO_STATUS_ERROR)
1052     {
1053       request->state = GTK_CUPS_POST_DONE;
1054       request->poll_state = GTK_CUPS_HTTP_IDLE;
1055     
1056       gtk_cups_result_set_error (request->result, error->message);
1057       g_error_free (error);
1058     }
1059 }
1060
1061 gboolean
1062 gtk_cups_request_is_done (GtkCupsRequest *request)
1063 {
1064   return (request->state == GTK_CUPS_REQUEST_DONE);
1065 }
1066
1067 gboolean
1068 gtk_cups_result_is_error (GtkCupsResult *result)
1069 {
1070   return result->is_error;
1071 }
1072
1073 ipp_t *
1074 gtk_cups_result_get_response (GtkCupsResult *result)
1075 {
1076   return result->ipp_response;
1077 }
1078
1079 const char *
1080 gtk_cups_result_get_error_string (GtkCupsResult *result)
1081 {
1082   return result->error_msg; 
1083 }
1084