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