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