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