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