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