]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkcupsutils.c
Complete the fix for bug 357280
[~andy/gtk] / modules / printbackends / cups / gtkcupsutils.c
1 /* GTK - The GIMP Toolkit
2  * gtkcupsutils.h: Statemachine implementation of POST and GET 
3  * cup calls which can be used to create a non-blocking cups API
4  * Copyright (C) 2003, Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "gtkcupsutils.h"
23 #include "config.h"
24 #include "gtkdebug.h"
25
26 #include <errno.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <stdlib.h>
31 #include <time.h>
32
33 #if CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR == 1 && CUPS_VERSION_PATCH >= 20)
34 #define HAVE_HTTP_AUTHSTRING 1
35 #endif
36
37 typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
38
39 static void _connect            (GtkCupsRequest *request);
40 static void _post_send          (GtkCupsRequest *request);
41 static void _post_write_request (GtkCupsRequest *request);
42 static void _post_write_data    (GtkCupsRequest *request);
43 static void _post_check         (GtkCupsRequest *request);
44 static void _post_read_response (GtkCupsRequest *request);
45
46 static void _get_send           (GtkCupsRequest *request);
47 static void _get_check          (GtkCupsRequest *request);
48 static void _get_read_data      (GtkCupsRequest *request);
49
50 struct _GtkCupsResult
51 {
52   gchar *error_msg;
53   ipp_t *ipp_response;
54
55   guint is_error : 1;
56   guint is_ipp_response : 1;
57 };
58
59
60 #define _GTK_CUPS_MAX_ATTEMPTS 10 
61 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
62
63 GtkCupsRequestStateFunc post_states[] = {_connect,
64                                          _post_send,
65                                          _post_write_request,
66                                          _post_write_data,
67                                          _post_check,
68                                          _post_read_response};
69
70 GtkCupsRequestStateFunc get_states[] = {_connect,
71                                         _get_send,
72                                         _get_check,
73                                         _get_read_data};
74
75 static void
76 gtk_cups_result_set_error (GtkCupsResult *result, 
77                            const char *error_msg,
78                            ...)
79 {
80   va_list args;
81
82   result->is_ipp_response = FALSE;
83
84   result->is_error = TRUE;
85
86   va_start (args, error_msg);
87   result->error_msg = g_strdup_vprintf (error_msg, args);
88   va_end (args);
89 }
90
91 GtkCupsRequest *
92 gtk_cups_request_new (http_t *connection,
93                       GtkCupsRequestType req_type, 
94                       gint operation_id,
95                       GIOChannel *data_io,
96                       const char *server,
97                       const char *resource)
98 {
99   GtkCupsRequest *request;
100   cups_lang_t *language;
101   
102   request = g_new0 (GtkCupsRequest, 1);
103   request->result = g_new0 (GtkCupsResult, 1);
104
105   request->result->error_msg = NULL;
106   request->result->ipp_response = NULL;
107
108   request->result->is_error = FALSE;
109   request->result->is_ipp_response = FALSE;
110
111   request->type = req_type;
112   request->state = GTK_CUPS_REQUEST_START;
113
114    if (server)
115     request->server = g_strdup (server);
116   else
117     request->server = g_strdup (cupsServer());
118
119
120   if (resource)
121     request->resource = g_strdup (resource);
122   else
123     request->resource = g_strdup ("/");
124  
125   if (connection != NULL)
126     {
127       request->http = connection;
128       request->own_http = FALSE;
129     }
130   else
131     {
132       request->http = NULL;
133       request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
134
135       if (request->http)
136         httpBlocking (request->http, 0);
137         
138       request->own_http = TRUE;
139     }
140
141   request->last_status = HTTP_CONTINUE;
142
143   request->attempts = 0;
144   request->data_io = data_io;
145
146   request->ipp_request = ippNew();
147   request->ipp_request->request.op.operation_id = operation_id;
148   request->ipp_request->request.op.request_id = 1;
149
150   language = cupsLangDefault ();
151
152   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
153                                    "attributes-charset", 
154                                    NULL, "utf-8");
155         
156   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
157                                    "attributes-natural-language", 
158                                    NULL, language->language);
159
160   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
161                                    "requesting-user-name",
162                                    NULL, cupsUser());
163   
164   cupsLangFree (language);
165
166   return request;
167 }
168
169 static void
170 gtk_cups_result_free (GtkCupsResult *result)
171 {
172   g_free (result->error_msg);
173
174   if (result->ipp_response)
175     ippDelete (result->ipp_response);
176
177   g_free (result);
178 }
179
180 void
181 gtk_cups_request_free (GtkCupsRequest *request)
182 {
183   if (request->own_http)
184     if (request->http)
185       httpClose (request->http);
186   
187   if (request->ipp_request)
188     ippDelete (request->ipp_request);
189
190   g_free (request->server);
191   g_free (request->resource);
192
193   gtk_cups_result_free (request->result);
194
195   g_free (request);
196 }
197
198 gboolean 
199 gtk_cups_request_read_write (GtkCupsRequest *request)
200 {
201   if (request->type == GTK_CUPS_POST)
202     post_states[request->state](request);
203   else if (request->type == GTK_CUPS_GET)
204     get_states[request->state](request);
205
206   if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS && 
207       request->state != GTK_CUPS_REQUEST_DONE)
208     {
209       gtk_cups_result_set_error (request->result, "Too many failed attempts");
210       request->state = GTK_CUPS_REQUEST_DONE;
211       request->poll_state = GTK_CUPS_HTTP_IDLE;
212     }
213     
214   if (request->state == GTK_CUPS_REQUEST_DONE)
215     {
216       request->poll_state = GTK_CUPS_HTTP_IDLE;
217       return TRUE;
218     }
219   else
220     {
221       return FALSE;
222     }
223 }
224
225 GtkCupsPollState 
226 gtk_cups_request_get_poll_state (GtkCupsRequest *request)
227 {
228   return request->poll_state;
229 }
230
231
232
233 GtkCupsResult *
234 gtk_cups_request_get_result (GtkCupsRequest *request)
235 {
236   return request->result;
237 }
238
239 void            
240 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
241                                  ipp_tag_t group,
242                                  ipp_tag_t tag,
243                                  const char *name,
244                                  const char *charset,
245                                  const char *value)
246 {
247   ippAddString (request->ipp_request,
248                 group,
249                 tag,
250                 name,
251                 charset,
252                 value);
253 }
254
255 void            
256 gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
257                                   ipp_tag_t group,
258                                   ipp_tag_t tag,
259                                   const char *name,
260                                   int num_values,
261                                   const char *charset,
262                                   const char * const *values)
263 {
264   ippAddStrings (request->ipp_request,
265                  group,
266                  tag,
267                  name,
268                  num_values,
269                  charset,
270                  values);
271 }
272
273
274
275 typedef struct
276 {
277   const char    *name;
278   ipp_tag_t     value_tag;
279 } ipp_option_t;
280
281 static const ipp_option_t ipp_options[] =
282                         {
283                           { "blackplot",                IPP_TAG_BOOLEAN },
284                           { "brightness",               IPP_TAG_INTEGER },
285                           { "columns",                  IPP_TAG_INTEGER },
286                           { "copies",                   IPP_TAG_INTEGER },
287                           { "finishings",               IPP_TAG_ENUM },
288                           { "fitplot",                  IPP_TAG_BOOLEAN },
289                           { "gamma",                    IPP_TAG_INTEGER },
290                           { "hue",                      IPP_TAG_INTEGER },
291                           { "job-k-limit",              IPP_TAG_INTEGER },
292                           { "job-page-limit",           IPP_TAG_INTEGER },
293                           { "job-priority",             IPP_TAG_INTEGER },
294                           { "job-quota-period",         IPP_TAG_INTEGER },
295                           { "landscape",                IPP_TAG_BOOLEAN },
296                           { "media",                    IPP_TAG_KEYWORD },
297                           { "mirror",                   IPP_TAG_BOOLEAN },
298                           { "natural-scaling",          IPP_TAG_INTEGER },
299                           { "number-up",                IPP_TAG_INTEGER },
300                           { "orientation-requested",    IPP_TAG_ENUM },
301                           { "page-bottom",              IPP_TAG_INTEGER },
302                           { "page-left",                IPP_TAG_INTEGER },
303                           { "page-ranges",              IPP_TAG_RANGE },
304                           { "page-right",               IPP_TAG_INTEGER },
305                           { "page-top",                 IPP_TAG_INTEGER },
306                           { "penwidth",                 IPP_TAG_INTEGER },
307                           { "ppi",                      IPP_TAG_INTEGER },
308                           { "prettyprint",              IPP_TAG_BOOLEAN },
309                           { "printer-resolution",       IPP_TAG_RESOLUTION },
310                           { "print-quality",            IPP_TAG_ENUM },
311                           { "saturation",               IPP_TAG_INTEGER },
312                           { "scaling",                  IPP_TAG_INTEGER },
313                           { "sides",                    IPP_TAG_KEYWORD },
314                           { "wrap",                     IPP_TAG_BOOLEAN }
315                         };
316
317
318 static ipp_tag_t
319 _find_option_tag (const gchar *option)
320 {
321   int lower_bound, upper_bound, num_options;
322   int current_option;
323   ipp_tag_t result;
324
325   result = IPP_TAG_ZERO;
326
327   lower_bound = 0;
328   upper_bound = num_options = (int)(sizeof(ipp_options) / sizeof(ipp_options[0])) - 1;
329   
330   while (1)
331     {
332       int match;
333       current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
334
335       match = strcasecmp(option, ipp_options[current_option].name);
336       if (match == 0)
337         {
338           result = ipp_options[current_option].value_tag;
339           return result;
340         }
341       else if (match < 0)
342         {
343           upper_bound = current_option - 1;
344         }
345       else
346         {
347           lower_bound = current_option + 1;
348         }
349
350       if (upper_bound == lower_bound && upper_bound == current_option)
351         return result;
352
353       if (upper_bound < 0)
354         return result;
355
356       if (lower_bound > num_options)
357         return result;
358
359       if (upper_bound < lower_bound)
360         return result;
361     }
362 }
363
364 void
365 gtk_cups_request_encode_option (GtkCupsRequest *request,
366                                 const gchar *option,
367                                 const gchar *value)
368 {
369   ipp_tag_t option_tag;
370
371   g_assert (option != NULL);
372   g_assert (value != NULL);
373
374   option_tag = _find_option_tag (option);
375
376   if (option_tag == IPP_TAG_ZERO)
377     {
378       option_tag = IPP_TAG_NAME;
379       if (strcasecmp (value, "true") == 0 ||
380           strcasecmp (value, "false") == 0)
381         {
382           option_tag = IPP_TAG_BOOLEAN;
383         }
384     }
385         
386   switch (option_tag)
387     {
388       case IPP_TAG_INTEGER:
389       case IPP_TAG_ENUM:
390         ippAddInteger (request->ipp_request,
391                        IPP_TAG_OPERATION,
392                        option_tag,
393                        option,
394                        strtol (value, NULL, 0));
395         break;
396
397       case IPP_TAG_BOOLEAN:
398         {
399           char b;
400           b = 0;
401           if (!strcasecmp(value, "true") ||
402               !strcasecmp(value, "on") ||
403               !strcasecmp(value, "yes")) 
404             b = 1;
405           
406           ippAddBoolean(request->ipp_request,
407                         IPP_TAG_OPERATION,
408                         option,
409                         b);
410         
411           break;
412         }
413         
414       case IPP_TAG_RANGE:
415         {
416           char  *s;
417           int lower;
418           int upper;
419           
420           if (*value == '-')
421             {
422               lower = 1;
423               s = (char *)value;
424             }
425           else
426             lower = strtol(value, &s, 0);
427
428           if (*s == '-')
429             {
430               if (s[1])
431                 upper = strtol(s + 1, NULL, 0);
432               else
433                 upper = 2147483647;
434             }
435           else
436             upper = lower;
437          
438           ippAddRange (request->ipp_request,
439                        IPP_TAG_OPERATION,
440                        option,
441                        lower,
442                        upper);
443
444           break;
445         }
446
447       case IPP_TAG_RESOLUTION:
448         {
449           char *s;
450           int xres;
451           int yres;
452           ipp_res_t units;
453           
454           xres = strtol(value, &s, 0);
455
456           if (*s == 'x')
457             yres = strtol(s + 1, &s, 0);
458           else
459             yres = xres;
460
461           if (strcasecmp(s, "dpc") == 0)
462             units = IPP_RES_PER_CM;
463           else
464             units = IPP_RES_PER_INCH;
465           
466           ippAddResolution (request->ipp_request,
467                             IPP_TAG_OPERATION,
468                             option,
469                             units,
470                             xres,
471                             yres);
472
473           break;
474         }
475
476       default:
477         ippAddString (request->ipp_request,
478                       IPP_TAG_OPERATION,
479                       option_tag,
480                       option,
481                       NULL,
482                       value);
483
484         break;
485     }
486 }
487                                 
488
489 static void
490 _connect (GtkCupsRequest *request)
491 {
492   request->poll_state = GTK_CUPS_HTTP_IDLE;
493
494   if (request->http == NULL)
495     {
496       request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
497
498       if (request->http == NULL)
499         request->attempts++;
500
501       if (request->http)
502         httpBlocking (request->http, 0);
503         
504       request->own_http = TRUE;
505     }
506   else
507     {
508       request->attempts = 0;
509       request->state++;
510
511       /* we always write to the socket after we get
512          the connection */
513       request->poll_state = GTK_CUPS_HTTP_WRITE;
514     }
515 }
516
517 static void 
518 _post_send (GtkCupsRequest *request)
519 {
520   gchar length[255];
521   struct stat data_info;
522
523   GTK_NOTE (PRINTING,
524             g_print ("CUPS Backend: %s\n", G_STRFUNC));
525
526   request->poll_state = GTK_CUPS_HTTP_WRITE;
527
528   if (request->data_io != NULL)
529     {
530       fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
531       sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request) + data_info.st_size);
532     }
533   else
534     {
535       sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request));
536     }
537         
538   httpClearFields(request->http);
539   httpSetField(request->http, HTTP_FIELD_CONTENT_LENGTH, length);
540   httpSetField(request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
541 #ifdef HAVE_HTTP_AUTHSTRING
542   httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
543 #endif
544
545   if (httpPost(request->http, request->resource))
546     {
547       if (httpReconnect(request->http))
548         {
549           request->state = GTK_CUPS_POST_DONE;
550           request->poll_state = GTK_CUPS_HTTP_IDLE;
551
552           gtk_cups_result_set_error (request->result, "Failed Post");
553         }
554
555       request->attempts++;
556       return;    
557     }
558         
559     request->attempts = 0;
560
561     request->state = GTK_CUPS_POST_WRITE_REQUEST;
562     request->ipp_request->state = IPP_IDLE;
563 }
564
565 static void 
566 _post_write_request (GtkCupsRequest *request)
567 {
568   ipp_state_t ipp_status;
569
570   GTK_NOTE (PRINTING,
571             g_print ("CUPS Backend: %s\n", G_STRFUNC));
572
573   request->poll_state = GTK_CUPS_HTTP_WRITE;
574   
575   ipp_status = ippWrite(request->http, request->ipp_request);
576
577   if (ipp_status == IPP_ERROR)
578     {
579       request->state = GTK_CUPS_POST_DONE;
580       request->poll_state = GTK_CUPS_HTTP_IDLE;
581  
582       gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ()));
583       return;
584     }
585
586   if (ipp_status == IPP_DATA)
587     {
588       if (request->data_io != NULL)
589         request->state = GTK_CUPS_POST_WRITE_DATA;
590       else
591         {
592           request->state = GTK_CUPS_POST_CHECK;
593           request->poll_state = GTK_CUPS_HTTP_READ;
594         }
595     }
596 }
597
598 static void 
599 _post_write_data (GtkCupsRequest *request)
600 {
601   gsize bytes;
602   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
603   http_status_t http_status;
604
605   GTK_NOTE (PRINTING,
606             g_print ("CUPS Backend: %s\n", G_STRFUNC));
607
608   request->poll_state = GTK_CUPS_HTTP_WRITE;
609   
610   if (httpCheck (request->http))
611     http_status = httpUpdate(request->http);
612   else
613     http_status = request->last_status;
614
615   request->last_status = http_status;
616
617
618   if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
619     {
620       GIOStatus io_status;
621       GError *error;
622
623       error = NULL;
624
625       /* send data */
626       io_status =
627         g_io_channel_read_chars (request->data_io, 
628                                  buffer, 
629                                  _GTK_CUPS_MAX_CHUNK_SIZE,
630                                  &bytes,
631                                  &error);
632
633       if (io_status == G_IO_STATUS_ERROR)
634         {
635           request->state = GTK_CUPS_POST_DONE;
636           request->poll_state = GTK_CUPS_HTTP_IDLE;
637      
638           gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", error->message);
639
640           g_error_free (error);
641           return;
642         }
643       else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
644         {
645           request->state = GTK_CUPS_POST_CHECK;
646           request->poll_state = GTK_CUPS_HTTP_READ;
647
648           request->attempts = 0;
649           return;
650         }
651
652
653 #if HAVE_CUPS_API_1_2
654       if (httpWrite2(request->http, buffer, bytes) < bytes)
655 #else
656       if (httpWrite(request->http, buffer, (int) bytes) < bytes)
657 #endif /* HAVE_CUPS_API_1_2 */
658         {
659           request->state = GTK_CUPS_POST_DONE;
660           request->poll_state = GTK_CUPS_HTTP_IDLE;
661      
662           gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http)));
663           return;
664         }
665     }
666    else
667     {
668       request->attempts++;
669     }
670 }
671
672 static void 
673 _post_check (GtkCupsRequest *request)
674 {
675   http_status_t http_status;
676
677   http_status = request->last_status;
678
679   GTK_NOTE (PRINTING,
680             g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
681
682   request->poll_state = GTK_CUPS_HTTP_READ;
683
684   if (http_status == HTTP_CONTINUE)
685     {
686       goto again; 
687     }
688   else if (http_status == HTTP_UNAUTHORIZED)
689     {
690       /* TODO: callout for auth */
691       g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
692       request->state = GTK_CUPS_POST_DONE;
693       request->poll_state = GTK_CUPS_HTTP_IDLE;
694       
695       gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
696       return;
697     }
698   else if (http_status == HTTP_ERROR)
699     {
700 #ifdef G_OS_WIN32
701       if (request->http->error != WSAENETDOWN && 
702           request->http->error != WSAENETUNREACH)
703 #else
704       if (request->http->error != ENETDOWN && 
705           request->http->error != ENETUNREACH)
706 #endif /* G_OS_WIN32 */
707         {
708           request->attempts++;
709           goto again;
710         }
711       else
712         {
713           request->state = GTK_CUPS_POST_DONE;
714           request->poll_state = GTK_CUPS_HTTP_IDLE;
715      
716           gtk_cups_result_set_error (request->result, "Unknown HTTP error");
717           return;
718         }
719     }
720 /* TODO: detect ssl in configure.ac */
721 #if HAVE_SSL
722   else if (http_status == HTTP_UPGRADE_REQUIRED)
723     {
724       /* Flush any error message... */
725       httpFlush (request->http);
726
727       /* Reconnect... */
728       httpReconnect (request->http);
729
730       /* Upgrade with encryption... */
731       httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
732  
733       request->attempts++;
734       goto again;
735     }
736 #endif 
737   else if (http_status != HTTP_OK)
738     {
739       int http_errno;
740
741       http_errno = httpError (request->http);
742
743       if (http_errno == EPIPE)
744         request->state = GTK_CUPS_POST_CONNECT;
745       else
746         {
747           request->state = GTK_CUPS_POST_DONE;
748           gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno));
749          request->poll_state = GTK_CUPS_HTTP_IDLE;
750  
751           httpFlush(request->http); 
752           return;
753         }
754
755       request->poll_state = GTK_CUPS_HTTP_IDLE;
756        
757       httpFlush(request->http); 
758       
759       request->last_status = HTTP_CONTINUE;
760       httpClose (request->http);
761       request->http = NULL;
762       return;  
763     }
764   else
765     {
766       request->state = GTK_CUPS_POST_READ_RESPONSE;
767       return;
768     }
769
770  again:
771   http_status = HTTP_CONTINUE;
772
773   if (httpCheck (request->http))
774     http_status = httpUpdate (request->http);
775
776   request->last_status = http_status;
777 }
778
779 static void 
780 _post_read_response (GtkCupsRequest *request)
781 {
782   ipp_state_t ipp_status;
783
784   GTK_NOTE (PRINTING,
785             g_print ("CUPS Backend: %s\n", G_STRFUNC));
786
787   request->poll_state = GTK_CUPS_HTTP_READ;
788
789   if (request->result->ipp_response == NULL)
790     request->result->ipp_response = ippNew();
791
792   ipp_status = ippRead (request->http, 
793                         request->result->ipp_response);
794
795   if (ipp_status == IPP_ERROR)
796     {
797       gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
798       
799       ippDelete (request->result->ipp_response);
800       request->result->ipp_response = NULL;
801
802       request->state = GTK_CUPS_POST_DONE;
803       request->poll_state = GTK_CUPS_HTTP_IDLE;
804     }
805   else if (ipp_status == IPP_DATA)
806     {
807       request->state = GTK_CUPS_POST_DONE;
808       request->poll_state = GTK_CUPS_HTTP_IDLE;
809     }
810 }
811
812 static void 
813 _get_send (GtkCupsRequest *request)
814 {
815   GTK_NOTE (PRINTING,
816             g_print ("CUPS Backend: %s\n", G_STRFUNC));
817
818   request->poll_state = GTK_CUPS_HTTP_WRITE;
819
820   if (request->data_io == NULL)
821     {
822       gtk_cups_result_set_error (request->result, "Get requires an open io channel");
823       request->state = GTK_CUPS_GET_DONE;
824       request->poll_state = GTK_CUPS_HTTP_IDLE;
825
826       return;
827     }
828
829   httpClearFields(request->http);
830 #ifdef HAVE_HTTP_AUTHSTRING
831   httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
832 #endif
833
834   if (httpGet(request->http, request->resource))
835     {
836       if (httpReconnect(request->http))
837         {
838           request->state = GTK_CUPS_GET_DONE;
839           request->poll_state = GTK_CUPS_HTTP_IDLE;
840           
841           gtk_cups_result_set_error (request->result, "Failed Get");
842         }
843
844       request->attempts++;
845       return;    
846     }
847         
848   request->attempts = 0;
849
850   request->state = GTK_CUPS_GET_CHECK;
851   request->poll_state = GTK_CUPS_HTTP_READ;
852   
853   request->ipp_request->state = IPP_IDLE;
854 }
855
856 static void 
857 _get_check (GtkCupsRequest *request)
858 {
859   http_status_t http_status;
860
861   GTK_NOTE (PRINTING,
862             g_print ("CUPS Backend: %s\n", G_STRFUNC));
863
864   http_status = request->last_status;
865
866   request->poll_state = GTK_CUPS_HTTP_READ;
867
868   if (http_status == HTTP_CONTINUE)
869     {
870       goto again; 
871     }
872   else if (http_status == HTTP_UNAUTHORIZED)
873     {
874       /* TODO: callout for auth */
875       g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
876       request->state = GTK_CUPS_GET_DONE;
877       request->poll_state = GTK_CUPS_HTTP_IDLE;
878  
879       gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
880       return;
881     }
882 /* TODO: detect ssl in configure.ac */
883 #if HAVE_SSL
884   else if (http_status == HTTP_UPGRADE_REQUIRED)
885     {
886       /* Flush any error message... */
887       httpFlush (request->http);
888
889       /* Reconnect... */
890       httpReconnect (request->http);
891
892       /* Upgrade with encryption... */
893       httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
894  
895       request->attempts++;
896       goto again;
897     }
898 #endif 
899   else if (http_status != HTTP_OK)
900     {
901       int http_errno;
902
903       http_errno = httpError (request->http);
904
905       if (http_errno == EPIPE)
906         request->state = GTK_CUPS_GET_CONNECT;
907       else
908         {
909           request->state = GTK_CUPS_GET_DONE;
910           gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno));
911           request->poll_state = GTK_CUPS_HTTP_IDLE;
912           httpFlush(request->http);
913
914           return;
915         }
916
917       request->poll_state = GTK_CUPS_HTTP_IDLE;
918       httpFlush (request->http);
919       httpClose (request->http);
920       request->last_status = HTTP_CONTINUE;
921       request->http = NULL;
922       return;
923
924     }
925   else
926     {
927       request->state = GTK_CUPS_GET_READ_DATA;
928       return;
929     }
930
931  again:
932   http_status = HTTP_CONTINUE;
933
934   if (httpCheck (request->http))
935     http_status = httpUpdate (request->http);
936
937   request->last_status = http_status;
938
939 }
940
941 static void 
942 _get_read_data (GtkCupsRequest *request)
943 {
944   char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
945   gsize bytes;
946   gsize bytes_written;
947   GIOStatus io_status;
948   GError *error;
949
950   GTK_NOTE (PRINTING,
951             g_print ("CUPS Backend: %s\n", G_STRFUNC));
952
953   error = NULL;
954
955   request->poll_state = GTK_CUPS_HTTP_READ;
956
957 #if HAVE_CUPS_API_1_2
958   bytes = httpRead2(request->http, buffer, sizeof(buffer));
959 #else
960   bytes = httpRead(request->http, buffer, sizeof(buffer));
961 #endif /* HAVE_CUPS_API_1_2 */
962
963   GTK_NOTE (PRINTING,
964             g_print ("CUPS Backend: %i bytes read\n", bytes));
965   
966   if (bytes == 0)
967     {
968       request->state = GTK_CUPS_GET_DONE;
969       request->poll_state = GTK_CUPS_HTTP_IDLE;
970
971       return;
972     }
973   
974   io_status =
975     g_io_channel_write_chars (request->data_io, 
976                               buffer, 
977                               bytes, 
978                               &bytes_written,
979                               &error);
980
981   if (io_status == G_IO_STATUS_ERROR)
982     {
983       request->state = GTK_CUPS_POST_DONE;
984       request->poll_state = GTK_CUPS_HTTP_IDLE;
985     
986       gtk_cups_result_set_error (request->result, error->message);
987       g_error_free (error);
988     }
989 }
990
991 gboolean
992 gtk_cups_request_is_done (GtkCupsRequest *request)
993 {
994   return (request->state == GTK_CUPS_REQUEST_DONE);
995 }
996
997 gboolean
998 gtk_cups_result_is_error (GtkCupsResult *result)
999 {
1000   return result->is_error;
1001 }
1002
1003 ipp_t *
1004 gtk_cups_result_get_response (GtkCupsResult *result)
1005 {
1006   return result->ipp_response;
1007 }
1008
1009 const char *
1010 gtk_cups_result_get_error_string (GtkCupsResult *result)
1011 {
1012   return result->error_msg; 
1013 }
1014