]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkcupsutils.c
Change FSF Address
[~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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "config.h"
21 #include <gtk/gtk.h>
22 #include "gtkcupsutils.h"
23
24 #include <errno.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdlib.h>
29 #include <time.h>
30 #include <fcntl.h>
31 #include <sys/socket.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_auth          (GtkCupsRequest *request);
41 static void _post_read_response (GtkCupsRequest *request);
42
43 static void _get_send           (GtkCupsRequest *request);
44 static void _get_check          (GtkCupsRequest *request);
45 static void _get_auth           (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_auth,
73   _post_read_response
74 };
75
76 static GtkCupsRequestStateFunc get_states[] = {
77   _connect,
78   _get_send,
79   _get_check,
80   _get_auth,
81   _get_read_data
82 };
83
84 static void
85 gtk_cups_result_set_error (GtkCupsResult    *result,
86                            GtkCupsErrorType  error_type,
87                            int               error_status,
88                            int               error_code, 
89                            const char       *error_msg,
90                            ...)
91 {
92   va_list args;
93
94   result->is_ipp_response = FALSE;
95   result->is_error = TRUE;
96   result->error_type = error_type;
97   result->error_status = error_status;
98   result->error_code = error_code;
99
100   va_start (args, error_msg);
101   result->error_msg = g_strdup_vprintf (error_msg, args);
102   va_end (args);
103 }
104
105 GtkCupsRequest *
106 gtk_cups_request_new_with_username (http_t             *connection,
107                                     GtkCupsRequestType  req_type, 
108                                     gint                operation_id,
109                                     GIOChannel         *data_io,
110                                     const char         *server,
111                                     const char         *resource,
112                                     const char         *username)
113 {
114   GtkCupsRequest *request;
115   cups_lang_t *language;
116   
117   request = g_new0 (GtkCupsRequest, 1);
118   request->result = g_new0 (GtkCupsResult, 1);
119
120   request->result->error_msg = NULL;
121   request->result->ipp_response = NULL;
122
123   request->result->is_error = FALSE;
124   request->result->is_ipp_response = FALSE;
125
126   request->type = req_type;
127   request->state = GTK_CUPS_REQUEST_START;
128
129   request->password_state = GTK_CUPS_PASSWORD_NONE;
130
131    if (server)
132     request->server = g_strdup (server);
133   else
134     request->server = g_strdup (cupsServer ());
135
136
137   if (resource)
138     request->resource = g_strdup (resource);
139   else
140     request->resource = g_strdup ("/");
141  
142   if (connection != NULL)
143     {
144       request->http = connection;
145       request->own_http = FALSE;
146     }
147   else
148     {
149       request->http = NULL;
150       request->http = httpConnectEncrypt (request->server, 
151                                           ippPort (), 
152                                           cupsEncryption ());
153
154       if (request->http)
155         httpBlocking (request->http, 0);
156         
157       request->own_http = TRUE;
158     }
159
160   request->last_status = HTTP_CONTINUE;
161
162   request->attempts = 0;
163   request->data_io = data_io;
164
165   request->ipp_request = ippNew ();
166   request->ipp_request->request.op.operation_id = operation_id;
167   request->ipp_request->request.op.request_id = 1;
168
169   language = cupsLangDefault ();
170
171   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
172                                    "attributes-charset", 
173                                    NULL, "utf-8");
174         
175   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
176                                    "attributes-natural-language", 
177                                    NULL, language->language);
178
179   if (username != NULL)
180     gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
181                                      "requesting-user-name",
182                                      NULL, username);
183   else
184     gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
185                                      "requesting-user-name",
186                                      NULL, cupsUser ());
187
188   request->auth_info_required = NULL;
189   request->auth_info = NULL;
190   request->need_auth_info = FALSE;
191
192   cupsLangFree (language);
193
194   return request;
195 }
196
197 GtkCupsRequest *
198 gtk_cups_request_new (http_t             *connection,
199                       GtkCupsRequestType  req_type, 
200                       gint                operation_id,
201                       GIOChannel         *data_io,
202                       const char         *server,
203                       const char         *resource)
204 {
205   return gtk_cups_request_new_with_username (connection,
206                                              req_type,
207                                              operation_id,
208                                              data_io,
209                                              server,
210                                              resource,
211                                              NULL);
212 }
213
214 static void
215 gtk_cups_result_free (GtkCupsResult *result)
216 {
217   g_free (result->error_msg);
218
219   if (result->ipp_response)
220     ippDelete (result->ipp_response);
221
222   g_free (result);
223 }
224
225 void
226 gtk_cups_request_free (GtkCupsRequest *request)
227 {
228   if (request->own_http)
229     {
230       if (request->http)
231         httpClose (request->http);
232     }
233   
234   if (request->ipp_request)
235     ippDelete (request->ipp_request);
236
237   g_free (request->server);
238   g_free (request->resource);
239   if (request->password != NULL)
240     {
241       memset (request->password, 0, strlen (request->password));
242       g_free (request->password);
243     }
244
245   g_free (request->username);
246   g_strfreev (request->auth_info_required);
247
248   gtk_cups_result_free (request->result);
249
250   g_free (request);
251 }
252
253 gboolean 
254 gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
255 {
256   if (connect_only && request->state != GTK_CUPS_REQUEST_START)
257     return FALSE;
258
259   do
260     {
261       if (request->type == GTK_CUPS_POST)
262         post_states[request->state] (request);
263       else if (request->type == GTK_CUPS_GET)
264         get_states[request->state] (request);
265
266       if (gtk_cups_result_is_error (request->result))
267         request->state = GTK_CUPS_REQUEST_DONE;
268
269       if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
270           request->state != GTK_CUPS_REQUEST_DONE)
271         {
272           /* TODO: should add a status or error code for too many failed attempts */
273           gtk_cups_result_set_error (request->result,
274                                      GTK_CUPS_ERROR_GENERAL,
275                                      0,
276                                      0,
277                                      "Too many failed attempts");
278
279           request->state = GTK_CUPS_REQUEST_DONE;
280         }
281
282       if (request->state == GTK_CUPS_REQUEST_DONE)
283         {
284           request->poll_state = GTK_CUPS_HTTP_IDLE;
285           return TRUE;
286         }
287     }
288   /* We need to recheck using httpCheck if the poll_state is read, because
289    * Cups has an internal read buffer. And if this buffer is filled, we may
290    * never get a poll event again. */
291   while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http));
292
293   return FALSE;
294 }
295
296 GtkCupsPollState 
297 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
298 {
299   return request->poll_state;
300 }
301
302
303
304 GtkCupsResult *
305 gtk_cups_request_get_result (GtkCupsRequest *request)
306 {
307   return request->result;
308 }
309
310 void            
311 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
312                                  ipp_tag_t       group,
313                                  ipp_tag_t       tag,
314                                  const char     *name,
315                                  const char     *charset,
316                                  const char     *value)
317 {
318   ippAddString (request->ipp_request,
319                 group,
320                 tag,
321                 name,
322                 charset,
323                 value);
324 }
325
326 void            
327 gtk_cups_request_ipp_add_strings (GtkCupsRequest    *request,
328                                   ipp_tag_t          group,
329                                   ipp_tag_t          tag,
330                                   const char        *name,
331                                   int                num_values,
332                                   const char        *charset,
333                                   const char *const *values)
334 {
335   ippAddStrings (request->ipp_request,
336                  group,
337                  tag,
338                  name,
339                  num_values,
340                  charset,
341                  values);
342 }
343
344 const char *
345 gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
346                                  ipp_tag_t       tag,
347                                  const char     *name)
348 {
349   ipp_attribute_t *attribute = NULL;
350
351   if (request != NULL && request->ipp_request != NULL)
352     attribute = ippFindAttribute (request->ipp_request,
353                                   name,
354                                   tag);
355
356   if (attribute != NULL && attribute->values != NULL)
357     return attribute->values[0].string.text;
358   else
359     return NULL;
360 }
361
362
363 typedef struct
364 {
365   const char    *name;
366   ipp_tag_t     value_tag;
367 } ipp_option_t;
368
369 static const ipp_option_t ipp_options[] = {
370   { "blackplot",                IPP_TAG_BOOLEAN },
371   { "brightness",               IPP_TAG_INTEGER },
372   { "columns",                  IPP_TAG_INTEGER },
373   { "copies",                   IPP_TAG_INTEGER },
374   { "finishings",               IPP_TAG_ENUM },
375   { "fitplot",                  IPP_TAG_BOOLEAN },
376   { "gamma",                    IPP_TAG_INTEGER },
377   { "hue",                      IPP_TAG_INTEGER },
378   { "job-k-limit",              IPP_TAG_INTEGER },
379   { "job-page-limit",           IPP_TAG_INTEGER },
380   { "job-priority",             IPP_TAG_INTEGER },
381   { "job-quota-period",         IPP_TAG_INTEGER },
382   { "landscape",                IPP_TAG_BOOLEAN },
383   { "media",                    IPP_TAG_KEYWORD },
384   { "mirror",                   IPP_TAG_BOOLEAN },
385   { "natural-scaling",          IPP_TAG_INTEGER },
386   { "number-up",                IPP_TAG_INTEGER },
387   { "orientation-requested",    IPP_TAG_ENUM },
388   { "page-bottom",              IPP_TAG_INTEGER },
389   { "page-left",                IPP_TAG_INTEGER },
390   { "page-ranges",              IPP_TAG_RANGE },
391   { "page-right",               IPP_TAG_INTEGER },
392   { "page-top",                 IPP_TAG_INTEGER },
393   { "penwidth",                 IPP_TAG_INTEGER },
394   { "ppi",                      IPP_TAG_INTEGER },
395   { "prettyprint",              IPP_TAG_BOOLEAN },
396   { "printer-resolution",       IPP_TAG_RESOLUTION },
397   { "print-quality",            IPP_TAG_ENUM },
398   { "saturation",               IPP_TAG_INTEGER },
399   { "scaling",                  IPP_TAG_INTEGER },
400   { "sides",                    IPP_TAG_KEYWORD },
401   { "wrap",                     IPP_TAG_BOOLEAN },
402   { "number-up-layout",         IPP_TAG_INTEGER }
403 };
404
405
406 static ipp_tag_t
407 _find_option_tag (const gchar *option)
408 {
409   int lower_bound, upper_bound, num_options;
410   int current_option;
411   ipp_tag_t result;
412
413   result = IPP_TAG_ZERO;
414
415   lower_bound = 0;
416   upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
417   
418   while (1)
419     {
420       int match;
421       current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
422
423       match = strcasecmp (option, ipp_options[current_option].name);
424       if (match == 0)
425         {
426           result = ipp_options[current_option].value_tag;
427           return result;
428         }
429       else if (match < 0)
430         {
431           upper_bound = current_option - 1;
432         }
433       else
434         {
435           lower_bound = current_option + 1;
436         }
437
438       if (upper_bound == lower_bound && upper_bound == current_option)
439         return result;
440
441       if (upper_bound < 0)
442         return result;
443
444       if (lower_bound > num_options)
445         return result;
446
447       if (upper_bound < lower_bound)
448         return result;
449     }
450 }
451
452 /*
453  * Note that this function uses IPP_TAG_JOB, so it is
454  * only suitable for IPP Group 2 attributes.
455  * See RFC 2911.
456  */
457 void
458 gtk_cups_request_encode_option (GtkCupsRequest *request,
459                                 const gchar    *option,
460                                 const gchar    *value)
461 {
462   ipp_tag_t option_tag;
463
464   g_return_if_fail (option != NULL);
465   g_return_if_fail (value != NULL);
466
467   option_tag = _find_option_tag (option);
468
469   if (option_tag == IPP_TAG_ZERO)
470     {
471       option_tag = IPP_TAG_NAME;
472       if (strcasecmp (value, "true") == 0 ||
473           strcasecmp (value, "false") == 0)
474         {
475           option_tag = IPP_TAG_BOOLEAN;
476         }
477     }
478         
479   switch (option_tag)
480     {
481       case IPP_TAG_INTEGER:
482       case IPP_TAG_ENUM:
483         ippAddInteger (request->ipp_request,
484                        IPP_TAG_JOB,
485                        option_tag,
486                        option,
487                        strtol (value, NULL, 0));
488         break;
489
490       case IPP_TAG_BOOLEAN:
491         {
492           char b;
493           
494           if (strcasecmp (value, "true") == 0 ||
495               strcasecmp (value, "on") == 0 ||
496               strcasecmp (value, "yes") == 0) 
497             b = 1;
498           else
499             b = 0;
500
501           ippAddBoolean (request->ipp_request,
502                          IPP_TAG_JOB,
503                          option,
504                          b);
505         
506           break;
507         }
508         
509       case IPP_TAG_RANGE:
510         {
511           char  *s;
512           int lower;
513           int upper;
514           
515           if (*value == '-')
516             {
517               lower = 1;
518               s = (char *)value;
519             }
520           else
521             lower = strtol (value, &s, 0);
522
523           if (*s == '-')
524             {
525               if (s[1])
526                 upper = strtol (s + 1, NULL, 0);
527               else
528                 upper = 2147483647;
529             }
530           else
531             upper = lower;
532          
533           ippAddRange (request->ipp_request,
534                        IPP_TAG_JOB,
535                        option,
536                        lower,
537                        upper);
538
539           break;
540         }
541
542       case IPP_TAG_RESOLUTION:
543         {
544           char *s;
545           int xres;
546           int yres;
547           ipp_res_t units;
548           
549           xres = strtol (value, &s, 0);
550
551           if (*s == 'x')
552             yres = strtol (s + 1, &s, 0);
553           else
554             yres = xres;
555
556           if (strcasecmp (s, "dpc") == 0)
557             units = IPP_RES_PER_CM;
558           else
559             units = IPP_RES_PER_INCH;
560           
561           ippAddResolution (request->ipp_request,
562                             IPP_TAG_JOB,
563                             option,
564                             units,
565                             xres,
566                             yres);
567
568           break;
569         }
570
571       default:
572         {
573           char *values;
574           char *s;
575           int in_quotes;
576           char *next;
577           GPtrArray *strings;
578           
579           values = g_strdup (value);
580           strings = NULL;
581           in_quotes = 0;
582
583           for (s = values, next = s; *s != '\0'; s++)
584             {
585               if (in_quotes != 2 && *s == '\'')
586                 {
587                   /* skip quoted value */
588                   if (in_quotes == 0)
589                     in_quotes = 1;
590                   else
591                     in_quotes = 0;
592                 }
593               else if (in_quotes != 1 && *s == '\"')
594                 {
595                   /* skip quoted value */
596                   if (in_quotes == 0)
597                     in_quotes = 2;
598                   else
599                     in_quotes = 0;
600                 }
601               else if (in_quotes == 0 && *s == ',')
602                 {
603                   /* found delimiter, add to value array */
604                   *s = '\0';
605                   if (strings == NULL)
606                     strings = g_ptr_array_new ();
607                   g_ptr_array_add (strings, next);
608                   next = s + 1;
609                 }
610               else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
611                 {
612                   /* skip escaped character */
613                   s++;
614                 }
615             }
616           
617           if (strings == NULL)
618             {
619               /* single value */
620               ippAddString (request->ipp_request,
621                             IPP_TAG_JOB,
622                             option_tag,
623                             option,
624                             NULL,
625                             value);
626             }
627           else
628             {
629               /* multiple values */
630               
631               /* add last value */
632               g_ptr_array_add (strings, next);
633               
634               ippAddStrings (request->ipp_request,
635                              IPP_TAG_JOB,
636                              option_tag,
637                              option,
638                              strings->len,
639                              NULL,
640                              (const char **) strings->pdata);
641               g_ptr_array_free (strings, TRUE);
642             }
643
644           g_free (values);
645         }
646
647         break;
648     }
649 }
650                                 
651
652 static void
653 _connect (GtkCupsRequest *request)
654 {
655   request->poll_state = GTK_CUPS_HTTP_IDLE;
656   request->bytes_received = 0;
657
658   if (request->http == NULL)
659     {
660       request->http = httpConnectEncrypt (request->server, 
661                                           ippPort (), 
662                                           cupsEncryption ());
663
664       if (request->http == NULL)
665         request->attempts++;
666
667       if (request->http)
668         httpBlocking (request->http, 0);
669         
670       request->own_http = TRUE;
671     }
672   else
673     {
674       request->attempts = 0;
675       request->state++;
676
677       /* we always write to the socket after we get
678          the connection */
679       request->poll_state = GTK_CUPS_HTTP_WRITE;
680     }
681 }
682
683 static void 
684 _post_send (GtkCupsRequest *request)
685 {
686   gchar length[255];
687   struct stat data_info;
688
689   GTK_NOTE (PRINTING,
690             g_print ("CUPS Backend: %s\n", G_STRFUNC));
691
692   request->poll_state = GTK_CUPS_HTTP_WRITE;
693
694   if (request->data_io != NULL)
695     {
696       fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
697       sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
698     }
699   else
700     sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
701         
702   httpClearFields (request->http);
703   httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
704   httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
705 #ifdef HAVE_HTTPGETAUTHSTRING
706   httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
707 #else
708 #ifdef HAVE_HTTP_AUTHSTRING
709   httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
710 #endif
711 #endif
712
713   if (httpPost (request->http, request->resource))
714     {
715       if (httpReconnect (request->http))
716         {
717           request->state = GTK_CUPS_POST_DONE;
718           request->poll_state = GTK_CUPS_HTTP_IDLE;
719
720           /* TODO: should add a status or error code for failed post */
721           gtk_cups_result_set_error (request->result,
722                                      GTK_CUPS_ERROR_GENERAL,
723                                      0,
724                                      0,
725                                      "Failed Post");
726         }
727
728       request->attempts++;
729       return;    
730     }
731         
732     request->attempts = 0;
733
734     request->state = GTK_CUPS_POST_WRITE_REQUEST;
735     request->ipp_request->state = IPP_IDLE;
736 }
737
738 static void 
739 _post_write_request (GtkCupsRequest *request)
740 {
741   ipp_state_t ipp_status;
742
743   GTK_NOTE (PRINTING,
744             g_print ("CUPS Backend: %s\n", G_STRFUNC));
745
746   request->poll_state = GTK_CUPS_HTTP_WRITE;
747   
748   ipp_status = ippWrite (request->http, request->ipp_request);
749
750   if (ipp_status == IPP_ERROR)
751     {
752       int cups_error = cupsLastError ();
753       request->state = GTK_CUPS_POST_DONE;
754       request->poll_state = GTK_CUPS_HTTP_IDLE;
755  
756       gtk_cups_result_set_error (request->result, 
757                                  GTK_CUPS_ERROR_IPP,
758                                  ipp_status,
759                                  cups_error,
760                                  "%s", 
761                                  ippErrorString (cups_error));
762       return;
763     }
764
765   if (ipp_status == IPP_DATA)
766     {
767       if (request->data_io != NULL)
768         request->state = GTK_CUPS_POST_WRITE_DATA;
769       else
770         {
771           request->state = GTK_CUPS_POST_CHECK;
772           request->poll_state = GTK_CUPS_HTTP_READ;
773         }
774     }
775 }
776
777 static void 
778 _post_write_data (GtkCupsRequest *request)
779 {
780   gsize bytes;
781   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
782   http_status_t http_status;
783
784   GTK_NOTE (PRINTING,
785             g_print ("CUPS Backend: %s\n", G_STRFUNC));
786
787   request->poll_state = GTK_CUPS_HTTP_WRITE;
788   
789   if (httpCheck (request->http))
790     http_status = httpUpdate (request->http);
791   else
792     http_status = request->last_status;
793
794   request->last_status = http_status;
795
796
797   if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
798     {
799       GIOStatus io_status;
800       GError *error;
801
802       error = NULL;
803
804       /* send data */
805       io_status =
806         g_io_channel_read_chars (request->data_io, 
807                                  buffer, 
808                                  _GTK_CUPS_MAX_CHUNK_SIZE,
809                                  &bytes,
810                                  &error);
811
812       if (io_status == G_IO_STATUS_ERROR)
813         {
814           request->state = GTK_CUPS_POST_DONE;
815           request->poll_state = GTK_CUPS_HTTP_IDLE;
816      
817           gtk_cups_result_set_error (request->result,
818                                      GTK_CUPS_ERROR_IO,
819                                      io_status,
820                                      error->code, 
821                                      "Error reading from cache file: %s",
822                                      error->message);
823
824           g_error_free (error);
825           return;
826         }
827       else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
828         {
829           request->state = GTK_CUPS_POST_CHECK;
830           request->poll_state = GTK_CUPS_HTTP_READ;
831
832           request->attempts = 0;
833           return;
834         }
835
836
837 #if HAVE_CUPS_API_1_2
838       if (httpWrite2 (request->http, buffer, bytes) < bytes)
839 #else
840       if (httpWrite (request->http, buffer, (int) bytes) < bytes)
841 #endif /* HAVE_CUPS_API_1_2 */
842         {
843           int http_errno;
844
845           http_errno = httpError (request->http);
846
847           request->state = GTK_CUPS_POST_DONE;
848           request->poll_state = GTK_CUPS_HTTP_IDLE;
849      
850           gtk_cups_result_set_error (request->result,
851                                      GTK_CUPS_ERROR_HTTP,
852                                      http_status,
853                                      http_errno, 
854                                      "Error writing to socket in Post %s", 
855                                      g_strerror (http_errno));
856           return;
857         }
858     }
859   else if (http_status == HTTP_UNAUTHORIZED)
860     {
861       request->state = GTK_CUPS_POST_CHECK;
862       request->poll_state = GTK_CUPS_HTTP_READ;
863
864       request->attempts = 0;
865       return;
866     }
867   else
868     {
869       request->attempts++;
870     }
871 }
872
873 static void
874 _post_auth (GtkCupsRequest *request)
875 {
876   if (request->password_state == GTK_CUPS_PASSWORD_HAS)
877     {
878       if (request->password == NULL)
879         {
880           request->state = GTK_CUPS_POST_DONE;
881           request->poll_state = GTK_CUPS_HTTP_IDLE;
882
883           gtk_cups_result_set_error (request->result, 
884                                      GTK_CUPS_ERROR_AUTH,
885                                      0,
886                                      1,
887                                      "Canceled by user");
888         }
889       else
890         request->state = GTK_CUPS_POST_CHECK;
891     }
892 }
893
894 static void
895 _get_auth (GtkCupsRequest *request)
896 {
897   if (request->password_state == GTK_CUPS_PASSWORD_HAS)
898     {
899       if (request->password == NULL)
900         {
901           request->state = GTK_CUPS_GET_DONE;
902           request->poll_state = GTK_CUPS_HTTP_IDLE;
903
904           gtk_cups_result_set_error (request->result, 
905                                      GTK_CUPS_ERROR_AUTH,
906                                      0,
907                                      1,
908                                      "Canceled by user");
909         }
910       else
911         request->state = GTK_CUPS_GET_CHECK;
912     }
913 }
914
915 /* Very ugly hack: cups has a stupid synchronous password callback 
916  * that doesn't even take the request or user data parameters, so 
917  * we have to use a static variable to pass the password to it.
918  * Not threadsafe !
919  * The callback sets cups_password to NULL to signal that the 
920  * password has been used.
921  */
922 static char *cups_password = NULL;
923 static char *cups_username = NULL;
924
925 static const char *
926 passwordCB (const char *prompt)
927 {
928   char *pwd = cups_password;
929   cups_password = NULL;
930
931   cupsSetUser (cups_username);
932
933   return pwd;
934 }
935
936 static void 
937 _post_check (GtkCupsRequest *request)
938 {
939   http_status_t http_status;
940
941   http_status = request->last_status;
942
943   GTK_NOTE (PRINTING,
944             g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
945
946   request->poll_state = GTK_CUPS_HTTP_READ;
947
948   if (http_status == HTTP_CONTINUE)
949     {
950       goto again; 
951     }
952   else if (http_status == HTTP_UNAUTHORIZED)
953     {
954       int auth_result = -1;
955       httpFlush (request->http);
956
957       if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
958         {
959           request->poll_state = GTK_CUPS_HTTP_IDLE;
960           request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
961           request->state = GTK_CUPS_POST_AUTH;
962           request->need_password = TRUE;
963
964           return;
965         }
966
967       /* Negotiate */
968       if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
969         {
970           auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
971         }
972       /* Basic, BasicDigest, Digest and PeerCred */
973       else
974         {
975           if (request->password_state == GTK_CUPS_PASSWORD_NONE)
976             {
977               cups_username = request->username;
978               cupsSetPasswordCB (passwordCB);
979
980               /* This call success for PeerCred authentication */
981               auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
982
983               if (auth_result != 0)
984                 {
985                   /* move to AUTH state to let the backend 
986                    * ask for a password
987                    */ 
988                   request->poll_state = GTK_CUPS_HTTP_IDLE;
989                   request->state = GTK_CUPS_POST_AUTH;
990                   request->need_password = TRUE;
991
992                   return;
993                 }
994             }
995           else
996             {
997               cups_password = request->password;
998               cups_username = request->username;
999
1000               auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
1001
1002               if (cups_password != NULL)
1003                 return;
1004
1005               if (request->password != NULL)
1006                 {
1007                   memset (request->password, 0, strlen (request->password));
1008                   g_free (request->password);
1009                   request->password = NULL;
1010                 }
1011
1012               request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1013             }
1014         }
1015
1016       if (auth_result ||
1017           httpReconnect (request->http))
1018         {
1019           /* if the password has been used, reset password_state
1020            * so that we ask for a new one next time around
1021            */ 
1022           if (cups_password == NULL)
1023             request->password_state = GTK_CUPS_PASSWORD_NONE;
1024
1025           request->state = GTK_CUPS_POST_DONE;
1026           request->poll_state = GTK_CUPS_HTTP_IDLE;
1027           gtk_cups_result_set_error (request->result, 
1028                                      GTK_CUPS_ERROR_AUTH,
1029                                      0,
1030                                      0,
1031                                      "Not authorized");
1032           return;
1033         }
1034       
1035       if (request->data_io != NULL)
1036         g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
1037
1038       request->state = GTK_CUPS_POST_CONNECT;
1039       request->poll_state = GTK_CUPS_HTTP_WRITE;
1040     }
1041   else if (http_status == HTTP_ERROR)
1042     {
1043       int error = httpError (request->http);
1044 #ifdef G_OS_WIN32
1045       if (error != WSAENETDOWN && error != WSAENETUNREACH)
1046 #else
1047       if (error != ENETDOWN && error != ENETUNREACH)      
1048 #endif /* G_OS_WIN32 */
1049         {
1050           request->attempts++;
1051           goto again;
1052         }
1053       else
1054         {
1055           request->state = GTK_CUPS_POST_DONE;
1056           request->poll_state = GTK_CUPS_HTTP_IDLE;
1057      
1058           gtk_cups_result_set_error (request->result,
1059                                      GTK_CUPS_ERROR_HTTP,
1060                                      http_status,
1061                                      error, 
1062                                      "Unknown HTTP error");
1063
1064           return;
1065         }
1066     }
1067   else if (http_status == HTTP_UPGRADE_REQUIRED)
1068     {
1069       /* Flush any error message... */
1070       httpFlush (request->http);
1071
1072       cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1073       request->state = GTK_CUPS_POST_CONNECT;
1074
1075       /* Reconnect... */
1076       httpReconnect (request->http);
1077
1078       /* Upgrade with encryption... */
1079       httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1080  
1081       request->attempts++;
1082       goto again;
1083     }
1084   else if (http_status != HTTP_OK)
1085     {
1086       int http_errno;
1087
1088       http_errno = httpError (request->http);
1089
1090       if (http_errno == EPIPE)
1091         request->state = GTK_CUPS_POST_CONNECT;
1092       else
1093         {
1094           request->state = GTK_CUPS_POST_DONE;
1095           gtk_cups_result_set_error (request->result,
1096                                      GTK_CUPS_ERROR_HTTP,
1097                                      http_status,
1098                                      http_errno, 
1099                                      "HTTP Error in POST %s", 
1100                                      g_strerror (http_errno));
1101           request->poll_state = GTK_CUPS_HTTP_IDLE;
1102  
1103           httpFlush (request->http); 
1104           return;
1105         }
1106
1107       request->poll_state = GTK_CUPS_HTTP_IDLE;
1108        
1109       httpFlush (request->http); 
1110       
1111       request->last_status = HTTP_CONTINUE;
1112       httpClose (request->http);
1113       request->http = NULL;
1114       return;  
1115     }
1116   else
1117     {
1118       request->state = GTK_CUPS_POST_READ_RESPONSE;
1119       return;
1120     }
1121
1122  again:
1123   http_status = HTTP_CONTINUE;
1124
1125   if (httpCheck (request->http))
1126     http_status = httpUpdate (request->http);
1127
1128   request->last_status = http_status;
1129 }
1130
1131 static void 
1132 _post_read_response (GtkCupsRequest *request)
1133 {
1134   ipp_state_t ipp_status;
1135
1136   GTK_NOTE (PRINTING,
1137             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1138
1139   request->poll_state = GTK_CUPS_HTTP_READ;
1140
1141   if (request->result->ipp_response == NULL)
1142     request->result->ipp_response = ippNew();
1143
1144   ipp_status = ippRead (request->http, 
1145                         request->result->ipp_response);
1146
1147   if (ipp_status == IPP_ERROR)
1148     {
1149       int ipp_error = cupsLastError ();
1150       gtk_cups_result_set_error (request->result,  
1151                                  GTK_CUPS_ERROR_IPP,
1152                                  ipp_status,
1153                                  ipp_error,
1154                                  "%s",
1155                                  ippErrorString (ipp_error));
1156       
1157       ippDelete (request->result->ipp_response);
1158       request->result->ipp_response = NULL;
1159
1160       request->state = GTK_CUPS_POST_DONE;
1161       request->poll_state = GTK_CUPS_HTTP_IDLE;
1162     }
1163   else if (ipp_status == IPP_DATA)
1164     {
1165       request->state = GTK_CUPS_POST_DONE;
1166       request->poll_state = GTK_CUPS_HTTP_IDLE;
1167     }
1168 }
1169
1170 static void 
1171 _get_send (GtkCupsRequest *request)
1172 {
1173   GTK_NOTE (PRINTING,
1174             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1175
1176   request->poll_state = GTK_CUPS_HTTP_WRITE;
1177
1178   if (request->data_io == NULL)
1179     {
1180       gtk_cups_result_set_error (request->result,
1181                                  GTK_CUPS_ERROR_IO,
1182                                  G_IO_STATUS_ERROR,
1183                                  G_IO_CHANNEL_ERROR_FAILED, 
1184                                  "Get requires an open io channel");
1185
1186       request->state = GTK_CUPS_GET_DONE;
1187       request->poll_state = GTK_CUPS_HTTP_IDLE;
1188
1189       return;
1190     }
1191
1192   httpClearFields (request->http);
1193 #ifdef HAVE_HTTPGETAUTHSTRING
1194   httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
1195 #else
1196 #ifdef HAVE_HTTP_AUTHSTRING
1197   httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
1198 #endif
1199 #endif
1200
1201   if (httpGet (request->http, request->resource))
1202     {
1203       if (httpReconnect (request->http))
1204         {
1205           request->state = GTK_CUPS_GET_DONE;
1206           request->poll_state = GTK_CUPS_HTTP_IDLE;
1207          
1208           /* TODO: should add a status or error code for failed GET */ 
1209           gtk_cups_result_set_error (request->result, 
1210                                      GTK_CUPS_ERROR_GENERAL,
1211                                      0,
1212                                      0,
1213                                      "Failed Get");
1214         }
1215
1216       request->attempts++;
1217       return;    
1218     }
1219
1220   if (httpCheck (request->http))
1221     request->last_status = httpUpdate (request->http);
1222         
1223   request->attempts = 0;
1224
1225   request->state = GTK_CUPS_GET_CHECK;
1226   request->poll_state = GTK_CUPS_HTTP_READ;
1227   
1228   request->ipp_request->state = IPP_IDLE;
1229 }
1230
1231 static void 
1232 _get_check (GtkCupsRequest *request)
1233 {
1234   http_status_t http_status;
1235
1236   GTK_NOTE (PRINTING,
1237             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1238
1239   http_status = request->last_status;
1240
1241   request->poll_state = GTK_CUPS_HTTP_READ;
1242
1243   if (http_status == HTTP_CONTINUE)
1244     {
1245       goto again; 
1246     }
1247   else if (http_status == HTTP_UNAUTHORIZED)
1248     {
1249       int auth_result = -1;
1250       httpFlush (request->http);
1251
1252       if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
1253         {
1254           request->poll_state = GTK_CUPS_HTTP_IDLE;
1255           request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
1256           request->state = GTK_CUPS_GET_AUTH;
1257           request->need_password = TRUE;
1258
1259           return;
1260         }
1261
1262       /* Negotiate */
1263       if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
1264         {
1265           auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1266         }
1267       /* Basic, BasicDigest, Digest and PeerCred */
1268       else
1269         {
1270           if (request->password_state == GTK_CUPS_PASSWORD_NONE)
1271             {
1272               cups_username = request->username;
1273               cupsSetPasswordCB (passwordCB);
1274
1275               /* This call success for PeerCred authentication */
1276               auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1277
1278               if (auth_result != 0)
1279                 {
1280                   /* move to AUTH state to let the backend
1281                    * ask for a password
1282                    */
1283                   request->poll_state = GTK_CUPS_HTTP_IDLE;
1284                   request->state = GTK_CUPS_GET_AUTH;
1285                   request->need_password = TRUE;
1286
1287                   return;
1288                 }
1289             }
1290           else
1291             {
1292               cups_password = request->password;
1293               cups_username = request->username;
1294
1295               auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
1296
1297               if (cups_password != NULL)
1298                 return;
1299
1300               if (request->password != NULL)
1301                 {
1302                   memset (request->password, 0, strlen (request->password));
1303                   g_free (request->password);
1304                   request->password = NULL;
1305                 }
1306
1307               request->password_state = GTK_CUPS_PASSWORD_APPLIED;
1308             }
1309         }
1310
1311       if (auth_result ||
1312           httpReconnect (request->http))
1313         {
1314           /* if the password has been used, reset password_state
1315            * so that we ask for a new one next time around
1316            */
1317           if (cups_password == NULL)
1318             request->password_state = GTK_CUPS_PASSWORD_NONE;
1319
1320           request->state = GTK_CUPS_GET_DONE;
1321           request->poll_state = GTK_CUPS_HTTP_IDLE;
1322           gtk_cups_result_set_error (request->result, 
1323                                      GTK_CUPS_ERROR_AUTH,
1324                                      0,
1325                                      0,
1326                                      "Not authorized");
1327           return;
1328         }
1329
1330       request->state = GTK_CUPS_GET_CONNECT;
1331       request->last_status = HTTP_CONTINUE;
1332
1333      return;
1334     }
1335   else if (http_status == HTTP_UPGRADE_REQUIRED)
1336     {
1337       /* Flush any error message... */
1338       httpFlush (request->http);
1339
1340       cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
1341       request->state = GTK_CUPS_GET_CONNECT;
1342
1343       /* Reconnect... */
1344       httpReconnect (request->http);
1345
1346       /* Upgrade with encryption... */
1347       httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
1348  
1349       request->attempts++;
1350       goto again;
1351     }
1352   else if (http_status != HTTP_OK)
1353     {
1354       int http_errno;
1355
1356       http_errno = httpError (request->http);
1357
1358       if (http_errno == EPIPE)
1359         request->state = GTK_CUPS_GET_CONNECT;
1360       else
1361         {
1362           request->state = GTK_CUPS_GET_DONE;
1363           gtk_cups_result_set_error (request->result,
1364                                      GTK_CUPS_ERROR_HTTP,
1365                                      http_status,
1366                                      http_errno, 
1367                                      "HTTP Error in GET %s", 
1368                                      g_strerror (http_errno));
1369           request->poll_state = GTK_CUPS_HTTP_IDLE;
1370           httpFlush (request->http);
1371
1372           return;
1373         }
1374
1375       request->poll_state = GTK_CUPS_HTTP_IDLE;
1376       httpFlush (request->http);
1377       httpClose (request->http);
1378       request->last_status = HTTP_CONTINUE;
1379       request->http = NULL;
1380       return;
1381
1382     }
1383   else
1384     {
1385       request->state = GTK_CUPS_GET_READ_DATA;
1386       return;
1387     }
1388
1389  again:
1390   http_status = HTTP_CONTINUE;
1391
1392   if (httpCheck (request->http))
1393     http_status = httpUpdate (request->http);
1394
1395   request->last_status = http_status;
1396
1397 }
1398
1399 static void 
1400 _get_read_data (GtkCupsRequest *request)
1401 {
1402   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
1403   gsize bytes;
1404   gsize bytes_written;
1405   GIOStatus io_status;
1406   GError *error;
1407
1408   GTK_NOTE (PRINTING,
1409             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1410
1411   error = NULL;
1412
1413   request->poll_state = GTK_CUPS_HTTP_READ;
1414
1415 #if HAVE_CUPS_API_1_2
1416   bytes = httpRead2 (request->http, buffer, sizeof (buffer));
1417 #else
1418   bytes = httpRead (request->http, buffer, sizeof (buffer));
1419 #endif /* HAVE_CUPS_API_1_2 */
1420   request->bytes_received += bytes;
1421
1422   GTK_NOTE (PRINTING,
1423             g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes));
1424
1425   io_status =
1426     g_io_channel_write_chars (request->data_io, 
1427                               buffer, 
1428                               bytes, 
1429                               &bytes_written,
1430                               &error);
1431
1432   if (io_status == G_IO_STATUS_ERROR)
1433     {
1434       request->state = GTK_CUPS_GET_DONE;
1435       request->poll_state = GTK_CUPS_HTTP_IDLE;
1436     
1437       gtk_cups_result_set_error (request->result,
1438                                  GTK_CUPS_ERROR_IO,
1439                                  io_status,
1440                                  error->code, 
1441                                  error->message);
1442       g_error_free (error);
1443     }
1444
1445   /* Stop if we do not expect any more data or EOF was received. */
1446 #if HAVE_CUPS_API_1_2
1447   if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
1448 #else
1449   if (httpGetLength (request->http) <= request->bytes_received || bytes == 0)
1450 #endif /* HAVE_CUPS_API_1_2 */
1451     {
1452       request->state = GTK_CUPS_GET_DONE;
1453       request->poll_state = GTK_CUPS_HTTP_IDLE;
1454
1455       return;
1456     }
1457 }
1458
1459 gboolean
1460 gtk_cups_request_is_done (GtkCupsRequest *request)
1461 {
1462   return (request->state == GTK_CUPS_REQUEST_DONE);
1463 }
1464
1465 gboolean
1466 gtk_cups_result_is_error (GtkCupsResult *result)
1467 {
1468   return result->is_error;
1469 }
1470
1471 ipp_t *
1472 gtk_cups_result_get_response (GtkCupsResult *result)
1473 {
1474   return result->ipp_response;
1475 }
1476
1477 GtkCupsErrorType
1478 gtk_cups_result_get_error_type (GtkCupsResult *result)
1479 {
1480   return result->error_type;
1481 }
1482
1483 int
1484 gtk_cups_result_get_error_status (GtkCupsResult *result)
1485 {
1486   return result->error_status;
1487 }
1488
1489 int
1490 gtk_cups_result_get_error_code (GtkCupsResult *result)
1491 {
1492   return result->error_code;
1493 }
1494
1495 const char *
1496 gtk_cups_result_get_error_string (GtkCupsResult *result)
1497 {
1498   return result->error_msg; 
1499 }
1500
1501 /* This function allocates new instance of GtkCupsConnectionTest() and creates
1502  * a socket for communication with a CUPS server 'server'.
1503  */
1504 GtkCupsConnectionTest *
1505 gtk_cups_connection_test_new (const char *server)
1506 {
1507   GtkCupsConnectionTest *result = NULL;
1508 #ifdef HAVE_CUPS_API_1_2
1509   gchar                 *port_str = NULL;
1510
1511   result = g_new (GtkCupsConnectionTest, 1);
1512
1513   port_str = g_strdup_printf ("%d", ippPort ());
1514
1515   if (server != NULL)
1516     result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
1517   else
1518     result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
1519
1520   g_free (port_str);
1521
1522   result->socket = -1;
1523   result->current_addr = NULL;
1524   result->last_wrong_addr = NULL;
1525   result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1526
1527   result->at_init = gtk_cups_connection_test_get_state (result);
1528 #else
1529   result = g_new (GtkCupsConnectionTest, 1);
1530 #endif
1531
1532   return result;
1533 }
1534
1535
1536 /* A non-blocking test whether it is possible to connect to a CUPS server specified
1537  * inside of GtkCupsConnectionTest structure.
1538  *  - you need to check it more then once.
1539  * The connection is closed after a successful connection.
1540  */
1541 GtkCupsConnectionState 
1542 gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
1543 {
1544 #ifdef HAVE_CUPS_API_1_2
1545   GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1546   http_addrlist_t       *iter;
1547   gint                   error_code;
1548   gint                   flags;
1549   gint                   code;
1550
1551   if (test == NULL)
1552     return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1553
1554   if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
1555     {
1556       test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1557       return GTK_CUPS_CONNECTION_AVAILABLE;
1558     }
1559   else
1560     {
1561       if (test->socket == -1)
1562         {
1563           if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
1564             iter = test->last_wrong_addr->next;
1565           else
1566             {
1567               test->last_wrong_addr = NULL;
1568               iter = test->addrlist;
1569             }
1570
1571           while (iter)
1572             {
1573               test->socket = socket (iter->addr.addr.sa_family,
1574                                      SOCK_STREAM,
1575                                      0);
1576
1577               if (test->socket >= 0)
1578                 {
1579                   flags = fcntl (test->socket, F_GETFL);
1580
1581                   if (flags != -1)
1582                     flags |= O_NONBLOCK;
1583
1584                   fcntl (test->socket, F_SETFL, flags);
1585               
1586                   test->current_addr = iter;
1587               
1588                   break;
1589                 }
1590                iter = iter->next;
1591             }
1592         }
1593
1594       if (test->socket >= 0)
1595         {
1596           code = connect (test->socket,
1597                           &test->current_addr->addr.addr,
1598                           httpAddrLength (&test->current_addr->addr));
1599
1600           error_code = errno;
1601
1602           if (code == 0 || error_code == EISCONN)
1603             {
1604               close (test->socket);
1605               test->socket = -1;
1606               test->current_addr = NULL;
1607               result = GTK_CUPS_CONNECTION_AVAILABLE;
1608             }
1609           else
1610             {
1611               if (error_code == EALREADY || error_code == EINPROGRESS)
1612                 result = GTK_CUPS_CONNECTION_IN_PROGRESS;
1613               else
1614                 {
1615                   close (test->socket);
1616                   test->socket = -1;
1617                   test->last_wrong_addr = test->current_addr;
1618                   result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
1619                 }
1620             }
1621          }
1622
1623       return result;
1624     }
1625 #else
1626   return GTK_CUPS_CONNECTION_AVAILABLE;
1627 #endif
1628 }
1629
1630 /* This function frees memory used by the GtkCupsConnectionTest structure.
1631  */
1632 void 
1633 gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
1634 {
1635   if (test == NULL)
1636     return;
1637
1638 #ifdef HAVE_CUPS_API_1_2
1639   test->current_addr = NULL;
1640   test->last_wrong_addr = NULL;
1641   httpAddrFreeList (test->addrlist);
1642   if (test->socket != -1)
1643     {
1644       close (test->socket);
1645       test->socket = -1;
1646     }
1647 #endif
1648   g_free (test);
1649 }