]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkcupsutils.c
ae03a0a9398c2032c993dfaf127570e35df1fc76
[~andy/gtk] / modules / printbackends / cups / gtkcupsutils.c
1 /* GTK - The GTK+ Toolkit
2  * gtkcupsutils.h: Statemachine implementation of POST and GET 
3  * cups calls which can be used to create a non-blocking cups API
4  * Copyright (C) 2006, 2007 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 "config.h"
23 #include <gtk/gtk.h>
24 #include "gtkcupsutils.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 typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
34
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);
41
42 static void _get_send           (GtkCupsRequest *request);
43 static void _get_check          (GtkCupsRequest *request);
44 static void _get_read_data      (GtkCupsRequest *request);
45
46 struct _GtkCupsResult
47 {
48   gchar *error_msg;
49   ipp_t *ipp_response;
50   GtkCupsErrorType error_type;
51
52   /* some error types like HTTP_ERROR have a status and a code */
53   int error_status;            
54   int error_code;
55
56   guint is_error : 1;
57   guint is_ipp_response : 1;
58 };
59
60
61 #define _GTK_CUPS_MAX_ATTEMPTS 10 
62 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
63
64 static GtkCupsRequestStateFunc post_states[] = {
65   _connect,
66   _post_send,
67   _post_write_request,
68   _post_write_data,
69   _post_check,
70   _post_read_response
71 };
72
73 static GtkCupsRequestStateFunc get_states[] = {
74   _connect,
75   _get_send,
76   _get_check,
77   _get_read_data
78 };
79
80 static void
81 gtk_cups_result_set_error (GtkCupsResult    *result,
82                            GtkCupsErrorType  error_type,
83                            int               error_status,
84                            int               error_code, 
85                            const char       *error_msg,
86                            ...)
87 {
88   va_list args;
89
90   result->is_ipp_response = FALSE;
91   result->is_error = TRUE;
92   result->error_type = error_type;
93   result->error_status = error_status;
94   result->error_code = error_code;
95
96   va_start (args, error_msg);
97   result->error_msg = g_strdup_vprintf (error_msg, args);
98   va_end (args);
99 }
100
101 GtkCupsRequest *
102 gtk_cups_request_new (http_t             *connection,
103                       GtkCupsRequestType  req_type, 
104                       gint                operation_id,
105                       GIOChannel         *data_io,
106                       const char         *server,
107                       const char         *resource)
108 {
109   GtkCupsRequest *request;
110   cups_lang_t *language;
111   
112   request = g_new0 (GtkCupsRequest, 1);
113   request->result = g_new0 (GtkCupsResult, 1);
114
115   request->result->error_msg = NULL;
116   request->result->ipp_response = NULL;
117
118   request->result->is_error = FALSE;
119   request->result->is_ipp_response = FALSE;
120
121   request->type = req_type;
122   request->state = GTK_CUPS_REQUEST_START;
123
124    if (server)
125     request->server = g_strdup (server);
126   else
127     request->server = g_strdup (cupsServer ());
128
129
130   if (resource)
131     request->resource = g_strdup (resource);
132   else
133     request->resource = g_strdup ("/");
134  
135   if (connection != NULL)
136     {
137       request->http = connection;
138       request->own_http = FALSE;
139     }
140   else
141     {
142       request->http = NULL;
143       request->http = httpConnectEncrypt (request->server, 
144                                           ippPort (), 
145                                           cupsEncryption ());
146
147       if (request->http)
148         httpBlocking (request->http, 0);
149         
150       request->own_http = TRUE;
151     }
152
153   request->last_status = HTTP_CONTINUE;
154
155   request->attempts = 0;
156   request->data_io = data_io;
157
158   request->ipp_request = ippNew ();
159   request->ipp_request->request.op.operation_id = operation_id;
160   request->ipp_request->request.op.request_id = 1;
161
162   language = cupsLangDefault ();
163
164   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
165                                    "attributes-charset", 
166                                    NULL, "utf-8");
167         
168   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
169                                    "attributes-natural-language", 
170                                    NULL, language->language);
171
172   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
173                                    "requesting-user-name",
174                                    NULL, cupsUser ());
175   
176   cupsLangFree (language);
177
178   return request;
179 }
180
181 static void
182 gtk_cups_result_free (GtkCupsResult *result)
183 {
184   g_free (result->error_msg);
185
186   if (result->ipp_response)
187     ippDelete (result->ipp_response);
188
189   g_free (result);
190 }
191
192 void
193 gtk_cups_request_free (GtkCupsRequest *request)
194 {
195   if (request->own_http)
196     {
197       if (request->http)
198         httpClose (request->http);
199     }
200   
201   if (request->ipp_request)
202     ippDelete (request->ipp_request);
203
204   g_free (request->server);
205   g_free (request->resource);
206
207   gtk_cups_result_free (request->result);
208
209   g_free (request);
210 }
211
212 gboolean 
213 gtk_cups_request_read_write (GtkCupsRequest *request)
214 {
215   if (request->type == GTK_CUPS_POST)
216     post_states[request->state] (request);
217   else if (request->type == GTK_CUPS_GET)
218     get_states[request->state] (request);
219
220   if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS && 
221       request->state != GTK_CUPS_REQUEST_DONE)
222     {
223       /* TODO: should add a status or error code for too many failed attempts */
224       gtk_cups_result_set_error (request->result, 
225                                  GTK_CUPS_ERROR_GENERAL,
226                                  0,
227                                  0, 
228                                  "Too many failed attempts");
229
230       request->state = GTK_CUPS_REQUEST_DONE;
231       request->poll_state = GTK_CUPS_HTTP_IDLE;
232     }
233     
234   if (request->state == GTK_CUPS_REQUEST_DONE)
235     {
236       request->poll_state = GTK_CUPS_HTTP_IDLE;
237       return TRUE;
238     }
239   else
240     return FALSE;
241 }
242
243 GtkCupsPollState 
244 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
245 {
246   return request->poll_state;
247 }
248
249
250
251 GtkCupsResult *
252 gtk_cups_request_get_result (GtkCupsRequest *request)
253 {
254   return request->result;
255 }
256
257 void            
258 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
259                                  ipp_tag_t       group,
260                                  ipp_tag_t       tag,
261                                  const char     *name,
262                                  const char     *charset,
263                                  const char     *value)
264 {
265   ippAddString (request->ipp_request,
266                 group,
267                 tag,
268                 name,
269                 charset,
270                 value);
271 }
272
273 void            
274 gtk_cups_request_ipp_add_strings (GtkCupsRequest    *request,
275                                   ipp_tag_t          group,
276                                   ipp_tag_t          tag,
277                                   const char        *name,
278                                   int                num_values,
279                                   const char        *charset,
280                                   const char *const *values)
281 {
282   ippAddStrings (request->ipp_request,
283                  group,
284                  tag,
285                  name,
286                  num_values,
287                  charset,
288                  values);
289 }
290
291
292
293 typedef struct
294 {
295   const char    *name;
296   ipp_tag_t     value_tag;
297 } ipp_option_t;
298
299 static const ipp_option_t ipp_options[] = {
300   { "blackplot",                IPP_TAG_BOOLEAN },
301   { "brightness",               IPP_TAG_INTEGER },
302   { "columns",                  IPP_TAG_INTEGER },
303   { "copies",                   IPP_TAG_INTEGER },
304   { "finishings",               IPP_TAG_ENUM },
305   { "fitplot",                  IPP_TAG_BOOLEAN },
306   { "gamma",                    IPP_TAG_INTEGER },
307   { "hue",                      IPP_TAG_INTEGER },
308   { "job-k-limit",              IPP_TAG_INTEGER },
309   { "job-page-limit",           IPP_TAG_INTEGER },
310   { "job-priority",             IPP_TAG_INTEGER },
311   { "job-quota-period",         IPP_TAG_INTEGER },
312   { "landscape",                IPP_TAG_BOOLEAN },
313   { "media",                    IPP_TAG_KEYWORD },
314   { "mirror",                   IPP_TAG_BOOLEAN },
315   { "natural-scaling",          IPP_TAG_INTEGER },
316   { "number-up",                IPP_TAG_INTEGER },
317   { "orientation-requested",    IPP_TAG_ENUM },
318   { "page-bottom",              IPP_TAG_INTEGER },
319   { "page-left",                IPP_TAG_INTEGER },
320   { "page-ranges",              IPP_TAG_RANGE },
321   { "page-right",               IPP_TAG_INTEGER },
322   { "page-top",                 IPP_TAG_INTEGER },
323   { "penwidth",                 IPP_TAG_INTEGER },
324   { "ppi",                      IPP_TAG_INTEGER },
325   { "prettyprint",              IPP_TAG_BOOLEAN },
326   { "printer-resolution",       IPP_TAG_RESOLUTION },
327   { "print-quality",            IPP_TAG_ENUM },
328   { "saturation",               IPP_TAG_INTEGER },
329   { "scaling",                  IPP_TAG_INTEGER },
330   { "sides",                    IPP_TAG_KEYWORD },
331   { "wrap",                     IPP_TAG_BOOLEAN }
332 };
333
334
335 static ipp_tag_t
336 _find_option_tag (const gchar *option)
337 {
338   int lower_bound, upper_bound, num_options;
339   int current_option;
340   ipp_tag_t result;
341
342   result = IPP_TAG_ZERO;
343
344   lower_bound = 0;
345   upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
346   
347   while (1)
348     {
349       int match;
350       current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
351
352       match = strcasecmp (option, ipp_options[current_option].name);
353       if (match == 0)
354         {
355           result = ipp_options[current_option].value_tag;
356           return result;
357         }
358       else if (match < 0)
359         {
360           upper_bound = current_option - 1;
361         }
362       else
363         {
364           lower_bound = current_option + 1;
365         }
366
367       if (upper_bound == lower_bound && upper_bound == current_option)
368         return result;
369
370       if (upper_bound < 0)
371         return result;
372
373       if (lower_bound > num_options)
374         return result;
375
376       if (upper_bound < lower_bound)
377         return result;
378     }
379 }
380
381 /*
382  * Note that this function uses IPP_TAG_JOB, so it is
383  * only suitable for IPP Group 2 attributes.
384  * See RFC 2911.
385  */
386 void
387 gtk_cups_request_encode_option (GtkCupsRequest *request,
388                                 const gchar    *option,
389                                 const gchar    *value)
390 {
391   ipp_tag_t option_tag;
392
393   g_return_if_fail (option != NULL);
394   g_return_if_fail (value != NULL);
395
396   option_tag = _find_option_tag (option);
397
398   if (option_tag == IPP_TAG_ZERO)
399     {
400       option_tag = IPP_TAG_NAME;
401       if (strcasecmp (value, "true") == 0 ||
402           strcasecmp (value, "false") == 0)
403         {
404           option_tag = IPP_TAG_BOOLEAN;
405         }
406     }
407         
408   switch (option_tag)
409     {
410       case IPP_TAG_INTEGER:
411       case IPP_TAG_ENUM:
412         ippAddInteger (request->ipp_request,
413                        IPP_TAG_JOB,
414                        option_tag,
415                        option,
416                        strtol (value, NULL, 0));
417         break;
418
419       case IPP_TAG_BOOLEAN:
420         {
421           char b;
422           
423           if (strcasecmp (value, "true") == 0 ||
424               strcasecmp (value, "on") == 0 ||
425               strcasecmp (value, "yes") == 0) 
426             b = 1;
427           else
428             b = 0;
429
430           ippAddBoolean (request->ipp_request,
431                          IPP_TAG_JOB,
432                          option,
433                          b);
434         
435           break;
436         }
437         
438       case IPP_TAG_RANGE:
439         {
440           char  *s;
441           int lower;
442           int upper;
443           
444           if (*value == '-')
445             {
446               lower = 1;
447               s = (char *)value;
448             }
449           else
450             lower = strtol (value, &s, 0);
451
452           if (*s == '-')
453             {
454               if (s[1])
455                 upper = strtol (s + 1, NULL, 0);
456               else
457                 upper = 2147483647;
458             }
459           else
460             upper = lower;
461          
462           ippAddRange (request->ipp_request,
463                        IPP_TAG_JOB,
464                        option,
465                        lower,
466                        upper);
467
468           break;
469         }
470
471       case IPP_TAG_RESOLUTION:
472         {
473           char *s;
474           int xres;
475           int yres;
476           ipp_res_t units;
477           
478           xres = strtol (value, &s, 0);
479
480           if (*s == 'x')
481             yres = strtol (s + 1, &s, 0);
482           else
483             yres = xres;
484
485           if (strcasecmp (s, "dpc") == 0)
486             units = IPP_RES_PER_CM;
487           else
488             units = IPP_RES_PER_INCH;
489           
490           ippAddResolution (request->ipp_request,
491                             IPP_TAG_JOB,
492                             option,
493                             units,
494                             xres,
495                             yres);
496
497           break;
498         }
499
500       default:
501         {
502           char *values;
503           char *s;
504           int in_quotes;
505           char *next;
506           GPtrArray *strings;
507           
508           values = g_strdup (value);
509           strings = NULL;
510           in_quotes = 0;
511
512           for (s = values, next = s; *s != '\0'; s++)
513             {
514               if (in_quotes != 2 && *s == '\'')
515                 {
516                   /* skip quoted value */
517                   if (in_quotes == 0)
518                     in_quotes = 1;
519                   else
520                     in_quotes = 0;
521                 }
522               else if (in_quotes != 1 && *s == '\"')
523                 {
524                   /* skip quoted value */
525                   if (in_quotes == 0)
526                     in_quotes = 2;
527                   else
528                     in_quotes = 0;
529                 }
530               else if (in_quotes == 0 && *s == ',')
531                 {
532                   /* found delimiter, add to value array */
533                   *s = '\0';
534                   if (strings == NULL)
535                     strings = g_ptr_array_new ();
536                   g_ptr_array_add (strings, next);
537                   next = s + 1;
538                 }
539               else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
540                 {
541                   /* skip escaped character */
542                   s++;
543                 }
544             }
545           
546           if (strings == NULL)
547             {
548               /* single value */
549               ippAddString (request->ipp_request,
550                             IPP_TAG_JOB,
551                             option_tag,
552                             option,
553                             NULL,
554                             value);
555             }
556           else
557             {
558               /* multiple values */
559               
560               /* add last value */
561               g_ptr_array_add (strings, next);
562               
563               ippAddStrings (request->ipp_request,
564                              IPP_TAG_JOB,
565                              option_tag,
566                              option,
567                              strings->len,
568                              NULL,
569                              (const char **) strings->pdata);
570               g_ptr_array_free (strings, TRUE);
571             }
572
573           g_free (values);
574         }
575
576         break;
577     }
578 }
579                                 
580
581 static void
582 _connect (GtkCupsRequest *request)
583 {
584   request->poll_state = GTK_CUPS_HTTP_IDLE;
585
586   if (request->http == NULL)
587     {
588       request->http = httpConnectEncrypt (request->server, 
589                                           ippPort (), 
590                                           cupsEncryption ());
591
592       if (request->http == NULL)
593         request->attempts++;
594
595       if (request->http)
596         httpBlocking (request->http, 0);
597         
598       request->own_http = TRUE;
599     }
600   else
601     {
602       request->attempts = 0;
603       request->state++;
604
605       /* we always write to the socket after we get
606          the connection */
607       request->poll_state = GTK_CUPS_HTTP_WRITE;
608     }
609 }
610
611 static void 
612 _post_send (GtkCupsRequest *request)
613 {
614   gchar length[255];
615   struct stat data_info;
616
617   GTK_NOTE (PRINTING,
618             g_print ("CUPS Backend: %s\n", G_STRFUNC));
619
620   request->poll_state = GTK_CUPS_HTTP_WRITE;
621
622   if (request->data_io != NULL)
623     {
624       fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
625       sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
626     }
627   else
628     sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
629         
630   httpClearFields (request->http);
631   httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
632   httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
633 #ifdef HAVE_HTTPGETAUTHSTRING
634   httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
635 #else
636 #ifdef HAVE_HTTP_AUTHSTRING
637   httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
638 #endif
639 #endif
640
641   if (httpPost (request->http, request->resource))
642     {
643       if (httpReconnect (request->http))
644         {
645           request->state = GTK_CUPS_POST_DONE;
646           request->poll_state = GTK_CUPS_HTTP_IDLE;
647
648           /* TODO: should add a status or error code for failed post */
649           gtk_cups_result_set_error (request->result,
650                                      GTK_CUPS_ERROR_GENERAL,
651                                      0,
652                                      0,
653                                      "Failed Post");
654         }
655
656       request->attempts++;
657       return;    
658     }
659         
660     request->attempts = 0;
661
662     request->state = GTK_CUPS_POST_WRITE_REQUEST;
663     request->ipp_request->state = IPP_IDLE;
664 }
665
666 static void 
667 _post_write_request (GtkCupsRequest *request)
668 {
669   ipp_state_t ipp_status;
670
671   GTK_NOTE (PRINTING,
672             g_print ("CUPS Backend: %s\n", G_STRFUNC));
673
674   request->poll_state = GTK_CUPS_HTTP_WRITE;
675   
676   ipp_status = ippWrite (request->http, request->ipp_request);
677
678   if (ipp_status == IPP_ERROR)
679     {
680       int cups_error = cupsLastError ();
681       request->state = GTK_CUPS_POST_DONE;
682       request->poll_state = GTK_CUPS_HTTP_IDLE;
683  
684       gtk_cups_result_set_error (request->result, 
685                                  GTK_CUPS_ERROR_IPP,
686                                  ipp_status,
687                                  cups_error,
688                                  "%s", 
689                                  ippErrorString (cups_error));
690       return;
691     }
692
693   if (ipp_status == IPP_DATA)
694     {
695       if (request->data_io != NULL)
696         request->state = GTK_CUPS_POST_WRITE_DATA;
697       else
698         {
699           request->state = GTK_CUPS_POST_CHECK;
700           request->poll_state = GTK_CUPS_HTTP_READ;
701         }
702     }
703 }
704
705 static void 
706 _post_write_data (GtkCupsRequest *request)
707 {
708   gsize bytes;
709   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
710   http_status_t http_status;
711
712   GTK_NOTE (PRINTING,
713             g_print ("CUPS Backend: %s\n", G_STRFUNC));
714
715   request->poll_state = GTK_CUPS_HTTP_WRITE;
716   
717   if (httpCheck (request->http))
718     http_status = httpUpdate (request->http);
719   else
720     http_status = request->last_status;
721
722   request->last_status = http_status;
723
724
725   if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
726     {
727       GIOStatus io_status;
728       GError *error;
729
730       error = NULL;
731
732       /* send data */
733       io_status =
734         g_io_channel_read_chars (request->data_io, 
735                                  buffer, 
736                                  _GTK_CUPS_MAX_CHUNK_SIZE,
737                                  &bytes,
738                                  &error);
739
740       if (io_status == G_IO_STATUS_ERROR)
741         {
742           request->state = GTK_CUPS_POST_DONE;
743           request->poll_state = GTK_CUPS_HTTP_IDLE;
744      
745           gtk_cups_result_set_error (request->result,
746                                      GTK_CUPS_ERROR_IO,
747                                      io_status,
748                                      error->code, 
749                                      "Error reading from cache file: %s",
750                                      error->message);
751
752           g_error_free (error);
753           return;
754         }
755       else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
756         {
757           request->state = GTK_CUPS_POST_CHECK;
758           request->poll_state = GTK_CUPS_HTTP_READ;
759
760           request->attempts = 0;
761           return;
762         }
763
764
765 #if HAVE_CUPS_API_1_2
766       if (httpWrite2 (request->http, buffer, bytes) < bytes)
767 #else
768       if (httpWrite (request->http, buffer, (int) bytes) < bytes)
769 #endif /* HAVE_CUPS_API_1_2 */
770         {
771           int http_errno;
772
773           http_errno = httpError (request->http);
774
775           request->state = GTK_CUPS_POST_DONE;
776           request->poll_state = GTK_CUPS_HTTP_IDLE;
777      
778           gtk_cups_result_set_error (request->result,
779                                      GTK_CUPS_ERROR_HTTP,
780                                      http_status,
781                                      http_errno, 
782                                      "Error writing to socket in Post %s", 
783                                      g_strerror (http_errno));
784           return;
785         }
786     }
787    else
788     {
789       request->attempts++;
790     }
791 }
792
793 static void 
794 _post_check (GtkCupsRequest *request)
795 {
796   http_status_t http_status;
797
798   http_status = request->last_status;
799
800   GTK_NOTE (PRINTING,
801             g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
802
803   request->poll_state = GTK_CUPS_HTTP_READ;
804
805   if (http_status == HTTP_CONTINUE)
806     {
807       goto again; 
808     }
809   else if (http_status == HTTP_UNAUTHORIZED)
810     {
811       /* TODO: callout for auth */
812       g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
813       request->state = GTK_CUPS_POST_DONE;
814       request->poll_state = GTK_CUPS_HTTP_IDLE;
815       
816       /* TODO: create a not implemented error code */
817       gtk_cups_result_set_error (request->result, 
818                                  GTK_CUPS_ERROR_GENERAL,
819                                  0,
820                                  0,
821                                  "Can't prompt for authorization");
822       return;
823     }
824   else if (http_status == HTTP_ERROR)
825     {
826       int error = httpError (request->http);
827 #ifdef G_OS_WIN32
828       if (error != WSAENETDOWN && error != WSAENETUNREACH)
829 #else
830       if (error != ENETDOWN && error != ENETUNREACH)      
831 #endif /* G_OS_WIN32 */
832         {
833           request->attempts++;
834           goto again;
835         }
836       else
837         {
838           request->state = GTK_CUPS_POST_DONE;
839           request->poll_state = GTK_CUPS_HTTP_IDLE;
840      
841           gtk_cups_result_set_error (request->result,
842                                      GTK_CUPS_ERROR_HTTP,
843                                      http_status,
844                                      error, 
845                                      "Unknown HTTP error");
846
847           return;
848         }
849     }
850   else if (http_status == HTTP_UPGRADE_REQUIRED)
851     {
852       /* Flush any error message... */
853       httpFlush (request->http);
854
855       cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
856       request->state = GTK_CUPS_POST_CONNECT;
857
858       /* Reconnect... */
859       httpReconnect (request->http);
860
861       /* Upgrade with encryption... */
862       httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
863  
864       request->attempts++;
865       goto again;
866     }
867   else if (http_status != HTTP_OK)
868     {
869       int http_errno;
870
871       http_errno = httpError (request->http);
872
873       if (http_errno == EPIPE)
874         request->state = GTK_CUPS_POST_CONNECT;
875       else
876         {
877           request->state = GTK_CUPS_POST_DONE;
878           gtk_cups_result_set_error (request->result,
879                                      GTK_CUPS_ERROR_HTTP,
880                                      http_status,
881                                      http_errno, 
882                                      "HTTP Error in POST %s", 
883                                      g_strerror (http_errno));
884          request->poll_state = GTK_CUPS_HTTP_IDLE;
885  
886           httpFlush (request->http); 
887           return;
888         }
889
890       request->poll_state = GTK_CUPS_HTTP_IDLE;
891        
892       httpFlush (request->http); 
893       
894       request->last_status = HTTP_CONTINUE;
895       httpClose (request->http);
896       request->http = NULL;
897       return;  
898     }
899   else
900     {
901       request->state = GTK_CUPS_POST_READ_RESPONSE;
902       return;
903     }
904
905  again:
906   http_status = HTTP_CONTINUE;
907
908   if (httpCheck (request->http))
909     http_status = httpUpdate (request->http);
910
911   request->last_status = http_status;
912 }
913
914 static void 
915 _post_read_response (GtkCupsRequest *request)
916 {
917   ipp_state_t ipp_status;
918
919   GTK_NOTE (PRINTING,
920             g_print ("CUPS Backend: %s\n", G_STRFUNC));
921
922   request->poll_state = GTK_CUPS_HTTP_READ;
923
924   if (request->result->ipp_response == NULL)
925     request->result->ipp_response = ippNew();
926
927   ipp_status = ippRead (request->http, 
928                         request->result->ipp_response);
929
930   if (ipp_status == IPP_ERROR)
931     {
932       int ipp_error = cupsLastError ();
933       gtk_cups_result_set_error (request->result,  
934                                  GTK_CUPS_ERROR_IPP,
935                                  ipp_status,
936                                  ipp_error,
937                                  "%s",
938                                  ippErrorString (ipp_error));
939       
940       ippDelete (request->result->ipp_response);
941       request->result->ipp_response = NULL;
942
943       request->state = GTK_CUPS_POST_DONE;
944       request->poll_state = GTK_CUPS_HTTP_IDLE;
945     }
946   else if (ipp_status == IPP_DATA)
947     {
948       request->state = GTK_CUPS_POST_DONE;
949       request->poll_state = GTK_CUPS_HTTP_IDLE;
950     }
951 }
952
953 static void 
954 _get_send (GtkCupsRequest *request)
955 {
956   GTK_NOTE (PRINTING,
957             g_print ("CUPS Backend: %s\n", G_STRFUNC));
958
959   request->poll_state = GTK_CUPS_HTTP_WRITE;
960
961   if (request->data_io == NULL)
962     {
963       gtk_cups_result_set_error (request->result,
964                                  GTK_CUPS_ERROR_IO,
965                                  G_IO_STATUS_ERROR,
966                                  G_IO_CHANNEL_ERROR_FAILED, 
967                                  "Get requires an open io channel");
968
969       request->state = GTK_CUPS_GET_DONE;
970       request->poll_state = GTK_CUPS_HTTP_IDLE;
971
972       return;
973     }
974
975   httpClearFields (request->http);
976 #ifdef HAVE_HTTP_AUTHSTRING
977   httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
978 #endif
979
980   if (httpGet (request->http, request->resource))
981     {
982       if (httpReconnect (request->http))
983         {
984           request->state = GTK_CUPS_GET_DONE;
985           request->poll_state = GTK_CUPS_HTTP_IDLE;
986          
987           /* TODO: should add a status or error code for failed GET */ 
988           gtk_cups_result_set_error (request->result, 
989                                      GTK_CUPS_ERROR_GENERAL,
990                                      0,
991                                      0,
992                                      "Failed Get");
993         }
994
995       request->attempts++;
996       return;    
997     }
998         
999   request->attempts = 0;
1000
1001   request->state = GTK_CUPS_GET_CHECK;
1002   request->poll_state = GTK_CUPS_HTTP_READ;
1003   
1004   request->ipp_request->state = IPP_IDLE;
1005 }
1006
1007 static void 
1008 _get_check (GtkCupsRequest *request)
1009 {
1010   http_status_t http_status;
1011
1012   GTK_NOTE (PRINTING,
1013             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1014
1015   http_status = request->last_status;
1016
1017   request->poll_state = GTK_CUPS_HTTP_READ;
1018
1019   if (http_status == HTTP_CONTINUE)
1020     {
1021       goto again; 
1022     }
1023   else if (http_status == HTTP_UNAUTHORIZED)
1024     {
1025       /* TODO: callout for auth */
1026       g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
1027       request->state = GTK_CUPS_GET_DONE;
1028       request->poll_state = GTK_CUPS_HTTP_IDLE;
1029
1030       /* TODO: should add a status or error code for not implemented */ 
1031       gtk_cups_result_set_error (request->result, 
1032                                  GTK_CUPS_ERROR_GENERAL,
1033                                  0,
1034                                  0,
1035                                  "Can't prompt for authorization");
1036       return;
1037     }
1038   else if (http_status == HTTP_UPGRADE_REQUIRED)
1039     {
1040       /* Flush any error message... */
1041       httpFlush (request->http);
1042
1043       cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1044       request->state = GTK_CUPS_POST_CONNECT;
1045
1046       /* Reconnect... */
1047       httpReconnect (request->http);
1048
1049       /* Upgrade with encryption... */
1050       httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1051  
1052       request->attempts++;
1053       goto again;
1054     }
1055   else if (http_status != HTTP_OK)
1056     {
1057       int http_errno;
1058
1059       http_errno = httpError (request->http);
1060
1061       if (http_errno == EPIPE)
1062         request->state = GTK_CUPS_GET_CONNECT;
1063       else
1064         {
1065           request->state = GTK_CUPS_GET_DONE;
1066           gtk_cups_result_set_error (request->result,
1067                                      GTK_CUPS_ERROR_HTTP,
1068                                      http_status,
1069                                      http_errno, 
1070                                      "HTTP Error in GET %s", 
1071                                      g_strerror (http_errno));
1072           request->poll_state = GTK_CUPS_HTTP_IDLE;
1073           httpFlush (request->http);
1074
1075           return;
1076         }
1077
1078       request->poll_state = GTK_CUPS_HTTP_IDLE;
1079       httpFlush (request->http);
1080       httpClose (request->http);
1081       request->last_status = HTTP_CONTINUE;
1082       request->http = NULL;
1083       return;
1084
1085     }
1086   else
1087     {
1088       request->state = GTK_CUPS_GET_READ_DATA;
1089       return;
1090     }
1091
1092  again:
1093   http_status = HTTP_CONTINUE;
1094
1095   if (httpCheck (request->http))
1096     http_status = httpUpdate (request->http);
1097
1098   request->last_status = http_status;
1099
1100 }
1101
1102 static void 
1103 _get_read_data (GtkCupsRequest *request)
1104 {
1105   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1106   gsize bytes;
1107   gsize bytes_written;
1108   GIOStatus io_status;
1109   GError *error;
1110
1111   GTK_NOTE (PRINTING,
1112             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1113
1114   error = NULL;
1115
1116   request->poll_state = GTK_CUPS_HTTP_READ;
1117
1118 #if HAVE_CUPS_API_1_2
1119   bytes = httpRead2 (request->http, buffer, sizeof (buffer));
1120 #else
1121   bytes = httpRead (request->http, buffer, sizeof (buffer));
1122 #endif /* HAVE_CUPS_API_1_2 */
1123
1124   GTK_NOTE (PRINTING,
1125             g_print ("CUPS Backend: %i bytes read\n", bytes));
1126   
1127   if (bytes == 0)
1128     {
1129       request->state = GTK_CUPS_GET_DONE;
1130       request->poll_state = GTK_CUPS_HTTP_IDLE;
1131
1132       return;
1133     }
1134   
1135   io_status =
1136     g_io_channel_write_chars (request->data_io, 
1137                               buffer, 
1138                               bytes, 
1139                               &bytes_written,
1140                               &error);
1141
1142   if (io_status == G_IO_STATUS_ERROR)
1143     {
1144       request->state = GTK_CUPS_POST_DONE;
1145       request->poll_state = GTK_CUPS_HTTP_IDLE;
1146     
1147       gtk_cups_result_set_error (request->result,
1148                                  GTK_CUPS_ERROR_IO,
1149                                  io_status,
1150                                  error->code, 
1151                                  error->message);
1152       g_error_free (error);
1153     }
1154 }
1155
1156 gboolean
1157 gtk_cups_request_is_done (GtkCupsRequest *request)
1158 {
1159   return (request->state == GTK_CUPS_REQUEST_DONE);
1160 }
1161
1162 gboolean
1163 gtk_cups_result_is_error (GtkCupsResult *result)
1164 {
1165   return result->is_error;
1166 }
1167
1168 ipp_t *
1169 gtk_cups_result_get_response (GtkCupsResult *result)
1170 {
1171   return result->ipp_response;
1172 }
1173
1174 GtkCupsErrorType
1175 gtk_cups_result_get_error_type (GtkCupsResult *result)
1176 {
1177   return result->error_type;
1178 }
1179
1180 int
1181 gtk_cups_result_get_error_status (GtkCupsResult *result)
1182 {
1183   return result->error_status;
1184 }
1185
1186 int
1187 gtk_cups_result_get_error_code (GtkCupsResult *result)
1188 {
1189   return result->error_code;
1190 }
1191
1192 const char *
1193 gtk_cups_result_get_error_string (GtkCupsResult *result)
1194 {
1195   return result->error_msg; 
1196 }
1197