Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gtkcupsutils.c

  Sprache: C
 

/* GTK - The GIMP Toolkit
 * gtkcupsutilsprivate.h: Statemachine implementation of POST and GET 
 * cups calls which can be used to create a non-blocking cups API
 * Copyright (C) 2006, 2007 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */


#include "config.h"
#include <gtk/gtk.h>
#include "gtkcupsutilsprivate.h"
#include "gtkprivate.h"

#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <sys/socket.h>

typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);

static void _connect            (GtkCupsRequest *request);
static void _post_send          (GtkCupsRequest *request);
static void _post_write_request (GtkCupsRequest *request);
static void _post_write_data    (GtkCupsRequest *request);
static void _post_check         (GtkCupsRequest *request);
static void _post_auth          (GtkCupsRequest *request);
static void _post_read_response (GtkCupsRequest *request);

static void _get_send           (GtkCupsRequest *request);
static void _get_check          (GtkCupsRequest *request);
static void _get_auth           (GtkCupsRequest *request);
static void _get_read_data      (GtkCupsRequest *request);

struct _GtkCupsResult
{
  char *error_msg;
  ipp_t *ipp_response;
  GtkCupsErrorType error_type;

  /* some error types like HTTP_ERROR have a status and a code */
  int error_status;            
  int error_code;

  guint is_error : 1;
  guint is_ipp_response : 1;
};


#define _GTK_CUPS_MAX_ATTEMPTS 10 
#define _GTK_CUPS_MAX_CHUNK_SIZE 8192

#if CUPS_VERSION_MAJOR < 3
#define cupsGetEncryption() cupsEncryption()
#define cupsGetError() cupsLastError()
#define cupsGetServer() cupsServer()
#define cupsGetUser() cupsUser()
#define cupsLangGetName(lang) (lang)->language

#define httpAddrGetLength(http_address) httpAddrLength(http_address)
#define _httpConnect(host, port, addrlist, family, encryption, blocking, msec, cancel) httpConnect2(host, port, addrlist, family, encryption, blocking, msec, cancel)
#define httpConnectAgain(http_connection, connection_timeout, cancel_variable) (httpReconnect2(http_connection, connection_timeout, cancel_variable) == 0)
#define httpGetError(http_connection) httpError(http_connection)
#define httpGetLength(http_connection) httpGetLength2(http_connection)
#define httpRead(http_connection, data_buffer, maximal_length) httpRead2(http_connection, data_buffer, maximal_length)
#define httpSetBlocking(http_connection, blocking) httpBlocking(http_connection, blocking)
#define httpSetEncryption(http_connection, encryption_preference) httpEncryption(http_connection, encryption_preference)
#define httpWrite(http_connection, data_buffer, maximal_length) httpWrite2(http_connection, data_buffer, maximal_length)

#define ippGetLength(ipp_message) ippLength(ipp_message)
#define ippGetPort() ippPort()

#define HTTP_STATUS_ERROR HTTP_ERROR
#define HTTP_STATUS_CONTINUE HTTP_CONTINUE
#define HTTP_STATUS_OK HTTP_OK
#define HTTP_STATUS_UNAUTHORIZED HTTP_UNAUTHORIZED
#define HTTP_STATUS_UPGRADE_REQUIRED HTTP_UPGRADE_REQUIRED

#define HTTP_ENCRYPTION_REQUIRED HTTP_ENCRYPT_REQUIRED

#define IPP_STATE_ERROR IPP_ERROR
#define IPP_STATE_IDLE IPP_IDLE
#define IPP_STATE_DATA IPP_DATA
#else
#define _httpConnect(host, port, addrlist, family, encryption, blocking, msec, cancel) httpConnect(host, port, addrlist, family, encryption, blocking, msec, cancel)
#endif

static GtkCupsRequestStateFunc post_states[] = {
  _connect,
  _post_send,
  _post_write_request,
  _post_write_data,
  _post_check,
  _post_auth,
  _post_read_response
};

static GtkCupsRequestStateFunc get_states[] = {
  _connect,
  _get_send,
  _get_check,
  _get_auth,
  _get_read_data
};

static void G_GNUC_PRINTF (56)
gtk_cups_result_set_error (GtkCupsResult    *result,
                           GtkCupsErrorType  error_type,
                           int               error_status,
                           int               error_code,
                           const char       *error_msg,
                           ...)
{
  va_list args;

  result->is_ipp_response = FALSE;
  result->is_error = TRUE;
  result->error_type = error_type;
  result->error_status = error_status;
  result->error_code = error_code;

  va_start (args, error_msg);
  result->error_msg = g_strdup_vprintf (error_msg, args);
  va_end (args);
}

GtkCupsRequest *
gtk_cups_request_new_with_username (http_t             *connection,
                                    GtkCupsRequestType  req_type, 
                                    int                 operation_id,
                                    GIOChannel         *data_io,
                                    const char         *server,
                                    const char         *resource,
                                    const char         *username)
{
  GtkCupsRequest *request;
  cups_lang_t *language;
  
  request = g_new0 (GtkCupsRequest, 1);
  request->result = g_new0 (GtkCupsResult, 1);

  request->result->error_msg = NULL;
  request->result->ipp_response = NULL;

  request->result->is_error = FALSE;
  request->result->is_ipp_response = FALSE;

  request->type = req_type;
  request->state = GTK_CUPS_REQUEST_START;

  request->password_state = GTK_CUPS_PASSWORD_NONE;

   if (server)
    request->server = g_strdup (server);
  else
    request->server = g_strdup (cupsGetServer ());

  if (resource)
    request->resource = g_strdup (resource);
  else
    request->resource = g_strdup ("/");
 
  if (connection != NULL)
    {
      request->http = connection;
      request->own_http = FALSE;
    }
  else
    {
      request->http = _httpConnect (request->server, ippGetPort (),
                                    NULL, AF_UNSPEC,
                                    cupsGetEncryption (),
                                    130000, NULL);

      if (request->http)
        httpSetBlocking (request->http, 0);
        
      request->own_http = TRUE;
    }

  request->last_status = HTTP_STATUS_CONTINUE;

  request->attempts = 0;
  request->data_io = data_io;

  request->ipp_request = ippNew ();
  ippSetOperation (request->ipp_request, operation_id);
  ippSetRequestId (request->ipp_request, 1);

  language = cupsLangDefault ();

  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
                                   "attributes-charset"
                                   NULL, "utf-8");
 
  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
                                   "attributes-natural-language"
                                   NULL, cupsLangGetName (language));

  if (username != NULL)
    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
                                     "requesting-user-name",
                                     NULL, username);
  else
    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
                                     "requesting-user-name",
                                     NULL, cupsGetUser ());

  request->auth_info_required = NULL;
  request->auth_info = NULL;
  request->need_auth_info = FALSE;

#if CUPS_VERSION_MAJOR < 3
  cupsLangFree (language);
#endif

  return request;
}

GtkCupsRequest *
gtk_cups_request_new (http_t             *connection,
                      GtkCupsRequestType  req_type, 
                      int                 operation_id,
                      GIOChannel         *data_io,
                      const char         *server,
                      const char         *resource)
{
  return gtk_cups_request_new_with_username (connection,
                                             req_type,
                                             operation_id,
                                             data_io,
                                             server,
                                             resource,
                                             NULL);
}

static void
gtk_cups_result_free (GtkCupsResult *result)
{
  g_free (result->error_msg);

  if (result->ipp_response)
    ippDelete (result->ipp_response);

  g_free (result);
}

void
gtk_cups_request_free (GtkCupsRequest *request)
{
  if (request->own_http)
    {
      if (request->http)
        httpClose (request->http);
    }
  
  if (request->ipp_request)
    ippDelete (request->ipp_request);

  g_free (request->server);
  g_free (request->resource);
  if (request->password != NULL)
    {
      memset (request->password, 0, strlen (request->password));
      g_free (request->password);
    }

  g_free (request->username);
  g_strfreev (request->auth_info_required);

  gtk_cups_result_free (request->result);

  g_free (request);
}

gboolean 
gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
{
  if (connect_only && request->state != GTK_CUPS_REQUEST_START)
    return FALSE;

  do
    {
      if (request->type == GTK_CUPS_POST)
        post_states[request->state] (request);
      else if (request->type == GTK_CUPS_GET)
        get_states[request->state] (request);

      if (gtk_cups_result_is_error (request->result))
        request->state = GTK_CUPS_REQUEST_DONE;

      if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
          request->state != GTK_CUPS_REQUEST_DONE)
        {
          /* TODO: should add a status or error code for too many failed attempts */
          gtk_cups_result_set_error (request->result,
                                     GTK_CUPS_ERROR_GENERAL,
                                     0,
                                     0,
                                     "Too many failed attempts");

          request->state = GTK_CUPS_REQUEST_DONE;
        }

      if (request->state == GTK_CUPS_REQUEST_DONE)
        {
          request->poll_state = GTK_CUPS_HTTP_IDLE;
          return TRUE;
        }
    }
  /* We need to recheck using httpCheck if the poll_state is read, because
   * Cups has an internal read buffer. And if this buffer is filled, we may
   * never get a poll event again. */

  while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpWait(request->http, 0));

  return FALSE;
}

GtkCupsPollState 
gtk_cups_request_get_poll_state (GtkCupsRequest *request)
{
  return request->poll_state;
}



GtkCupsResult *
gtk_cups_request_get_result (GtkCupsRequest *request)
{
  return request->result;
}

void            
gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
                                 ipp_tag_t       group,
                                 ipp_tag_t       tag,
                                 const char     *name,
                                 const char     *charset,
                                 const char     *value)
{
  ippAddString (request->ipp_request,
                group,
                tag,
                name,
                charset,
                value);
}

void            
gtk_cups_request_ipp_add_strings (GtkCupsRequest    *request,
      ipp_tag_t          group,
      ipp_tag_t          tag,
      const char        *name,
      int                num_values,
      const char        *charset,
      const char *const *values)
{
  ippAddStrings (request->ipp_request,
   group,
   tag,
   name,
   num_values,
   charset,
   values);
}

const char *
gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
                                 ipp_tag_t       tag,
                                 const char     *name)
{
  ipp_attribute_t *attribute = NULL;

  if (request != NULL && request->ipp_request != NULL)
    attribute = ippFindAttribute (request->ipp_request,
                                  name,
                                  tag);

  if (attribute != NULL && ippGetCount (attribute) > 0)
      return ippGetString (attribute, 0, NULL);
  else
    return NULL;
}


typedef struct
{
  const char *name;
  ipp_tag_t value_tag;
} ipp_option_t;

static const ipp_option_t ipp_options[] = {
  { "blackplot",  IPP_TAG_BOOLEAN },
  { "brightness",  IPP_TAG_INTEGER },
  { "columns",   IPP_TAG_INTEGER },
  { "copies",   IPP_TAG_INTEGER },
  { "finishings",  IPP_TAG_ENUM },
  { "fitplot",   IPP_TAG_BOOLEAN },
  { "gamma",   IPP_TAG_INTEGER },
  { "hue",   IPP_TAG_INTEGER },
  { "job-k-limit",  IPP_TAG_INTEGER },
  { "job-page-limit",  IPP_TAG_INTEGER },
  { "job-priority",  IPP_TAG_INTEGER },
  { "job-quota-period",  IPP_TAG_INTEGER },
  { "landscape",  IPP_TAG_BOOLEAN },
  { "media",   IPP_TAG_KEYWORD },
  { "mirror",   IPP_TAG_BOOLEAN },
  { "natural-scaling",  IPP_TAG_INTEGER },
  { "number-up",  IPP_TAG_INTEGER },
  { "orientation-requested", IPP_TAG_ENUM },
  { "page-bottom",  IPP_TAG_INTEGER },
  { "page-left",  IPP_TAG_INTEGER },
  { "page-ranges",  IPP_TAG_RANGE },
  { "page-right",  IPP_TAG_INTEGER },
  { "page-top",   IPP_TAG_INTEGER },
  { "penwidth",   IPP_TAG_INTEGER },
  { "ppi",   IPP_TAG_INTEGER },
  { "prettyprint",  IPP_TAG_BOOLEAN },
  { "printer-resolution", IPP_TAG_RESOLUTION },
  { "print-quality",  IPP_TAG_ENUM },
  { "saturation",  IPP_TAG_INTEGER },
  { "scaling",   IPP_TAG_INTEGER },
  { "sides",   IPP_TAG_KEYWORD },
  { "wrap",   IPP_TAG_BOOLEAN },
  { "number-up-layout",  IPP_TAG_INTEGER }
};


static ipp_tag_t
_find_option_tag (const char *option)
{
  int lower_bound, upper_bound, num_options;
  int current_option;
  ipp_tag_t result;

  result = IPP_TAG_ZERO;

  lower_bound = 0;
  upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
  
  while (1)
    {
      int match;
      current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);

      match = strcasecmp (option, ipp_options[current_option].name);
      if (match == 0)
        {
   result = ipp_options[current_option].value_tag;
   return result;
 }
      else if (match < 0)
        {
          upper_bound = current_option - 1;
 }
      else
        {
          lower_bound = current_option + 1;
 }

      if (upper_bound == lower_bound && upper_bound == current_option)
        return result;

      if (upper_bound < 0)
        return result;

      if (lower_bound > num_options)
        return result;

      if (upper_bound < lower_bound)
        return result;
    }
}

/*
 * Note that this function uses IPP_TAG_JOB, so it is
 * only suitable for IPP Group 2 attributes.
 * See RFC 2911.
 */

void
gtk_cups_request_encode_option (GtkCupsRequest *request,
                                const char     *option,
           const char     *value)
{
  ipp_tag_t option_tag;

  g_return_if_fail (option != NULL);
  g_return_if_fail (value != NULL);

  option_tag = _find_option_tag (option);

  if (option_tag == IPP_TAG_ZERO)
    {
      option_tag = IPP_TAG_NAME;
      if (strcasecmp (value, "true") == 0 ||
          strcasecmp (value, "false") == 0)
        {
          option_tag = IPP_TAG_BOOLEAN;
        }
    }
        
  switch ((guint)option_tag)
    {
      case IPP_TAG_INTEGER:
      case IPP_TAG_ENUM:
        ippAddInteger (request->ipp_request,
                       IPP_TAG_JOB,
                       option_tag,
                       option,
                       strtol (value, NULL, 0));
        break;

      case IPP_TAG_BOOLEAN:
        {
          char b;
          
          if (strcasecmp (value, "true") == 0 ||
       strcasecmp (value, "on") == 0 ||
       strcasecmp (value, "yes") == 0
     b = 1;
   else
            b = 0;

          ippAddBoolean (request->ipp_request,
                         IPP_TAG_JOB,
                         option,
                         b);
        
          break;
        }
        
      case IPP_TAG_RANGE:
        {
          char *s;
          int lower;
          int upper;
          
          if (*value == '-')
     {
       lower = 1;
       s = (char *)value;
     }
   else
     lower = strtol (value, &s, 0);

   if (*s == '-')
     {
       if (s[1])
  upper = strtol (s + 1, NULL, 0);
       else
  upper = 2147483647;
            }
   else
     upper = lower;
         
          ippAddRange (request->ipp_request,
                       IPP_TAG_JOB,
                       option,
                       lower,
                       upper);

          break;
        }

      case IPP_TAG_RESOLUTION:
        {
          char *s;
          int xres;
          int yres;
          ipp_res_t units;
          
          xres = strtol (value, &s, 0);

   if (*s == 'x')
     yres = strtol (s + 1, &s, 0);
   else
     yres = xres;

   if (strcasecmp (s, "dpc") == 0)
            units = IPP_RES_PER_CM;
          else
            units = IPP_RES_PER_INCH;
          
          ippAddResolution (request->ipp_request,
                            IPP_TAG_JOB,
                            option,
                            units,
                            xres,
                            yres);

          break;
        }

      default:
        {
          char *values;
          char *s;
          int in_quotes;
          char *next;
          GPtrArray *strings;
          
          values = g_strdup (value);
          strings = NULL;
   in_quotes = 0;

          for (s = values, next = s; *s != '\0'; s++)
            {
              if (in_quotes != 2 && *s == '\'')
                {
                  /* skip quoted value */
                  if (in_quotes == 0)
                    in_quotes = 1;
                  else
                    in_quotes = 0;
                }
              else if (in_quotes != 1 && *s == '\"')
                {
                  /* skip quoted value */
                  if (in_quotes == 0)
                    in_quotes = 2;
                  else
                    in_quotes = 0;
                }
              else if (in_quotes == 0 && *s == ',')
                {
                  /* found delimiter, add to value array */
                  *s = '\0';
                  if (strings == NULL)
                    strings = g_ptr_array_new ();
                  g_ptr_array_add (strings, next);
                  next = s + 1;
                }
              else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
                {
                  /* skip escaped character */
                  s++;
                }
            }
          
          if (strings == NULL)
            {
              /* single value */
              ippAddString (request->ipp_request,
                            IPP_TAG_JOB,
                            option_tag,
                            option,
                            NULL,
                            value);
            }
          else
            {
              /* multiple values */
              
              /* add last value */
              g_ptr_array_add (strings, next);
              
              ippAddStrings (request->ipp_request,
                             IPP_TAG_JOB,
                             option_tag,
                             option,
                             strings->len,
                             NULL,
                             (const char **) strings->pdata);
              g_ptr_array_free (strings, TRUE);
            }

          g_free (values);
        }

        break;
    }
}
    
void
gtk_cups_request_set_ipp_version (GtkCupsRequest     *request,
      int                 major,
      int                 minor)
{
  ippSetVersion (request->ipp_request, major, minor);
}

static void
_connect (GtkCupsRequest *request)
{
  request->poll_state = GTK_CUPS_HTTP_IDLE;
  request->bytes_received = 0;

  if (request->http == NULL)
    {
      request->http = _httpConnect (request->server, ippGetPort (),
                                    NULL, AF_UNSPEC,
                                    cupsGetEncryption (),
                                    130000, NULL);
      if (request->http == NULL)
        request->attempts++;

      if (request->http)
        httpSetBlocking (request->http, 0);
        
      request->own_http = TRUE;
    }
  else
    {
      request->attempts = 0;
      request->state++;

      /* we always write to the socket after we get
         the connection */

      request->poll_state = GTK_CUPS_HTTP_WRITE;
    }
}

static void 
_post_send (GtkCupsRequest *request)
{
  char length[255];
  struct stat data_info;

  GTK_DEBUG (PRINTING, "CUPS Backend: %s", G_STRFUNC);

  request->poll_state = GTK_CUPS_HTTP_WRITE;

  if (request->data_io != NULL)
    {
      fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
      sprintf (length, "%lu", (unsigned long) (ippGetLength (request->ipp_request) + data_info.st_size));
    }
  else
    sprintf (length, "%lu", (unsigned long) ippGetLength (request->ipp_request));
 

  httpClearFields (request->http);
  httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
  httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));

#if CUPS_VERSION_MAJOR < 3
  if (httpPost (request->http, request->resource))
#else
  if (!httpWriteRequest (request->http, "POST", request->resource))
#endif
    {
      if (!httpConnectAgain (request->http, 30000, NULL))
        {
          request->state = GTK_CUPS_POST_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;

          /* TODO: should add a status or error code for failed post */
          gtk_cups_result_set_error (request->result,
                                     GTK_CUPS_ERROR_GENERAL,
                                     0,
                                     0,
                                     "Failed Post");
        }

      request->attempts++;
      return;    
    }
        
    request->attempts = 0;

    request->state = GTK_CUPS_POST_WRITE_REQUEST;
    ippSetState (request->ipp_request, IPP_STATE_IDLE);
}

static void 
_post_write_request (GtkCupsRequest *request)
{
  ipp_state_t ipp_status;

  GTK_DEBUG (PRINTING, "CUPS Backend: %s", G_STRFUNC);

  request->poll_state = GTK_CUPS_HTTP_WRITE;
  
  ipp_status = ippWrite (request->http, request->ipp_request);

  if (ipp_status == IPP_STATE_ERROR)
    {
      int cups_error = cupsGetError ();
      request->state = GTK_CUPS_POST_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;
 
      gtk_cups_result_set_error (request->result, 
                                 GTK_CUPS_ERROR_IPP,
                                 ipp_status,
                                 cups_error,
                                 "%s"
                                 ippErrorString (cups_error));
      return;
    }

  if (ipp_status == IPP_STATE_DATA)
    {
      if (request->data_io != NULL)
        request->state = GTK_CUPS_POST_WRITE_DATA;
      else
        {
          request->state = GTK_CUPS_POST_CHECK;
          request->poll_state = GTK_CUPS_HTTP_READ;
 }
    }
}

static void 
_post_write_data (GtkCupsRequest *request)
{
  gsize bytes;
  char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
  http_status_t http_status;

  GTK_DEBUG (PRINTING, "CUPS Backend: %s", G_STRFUNC);

  request->poll_state = GTK_CUPS_HTTP_WRITE;
  
  if (httpWait (request->http, 0))
    http_status = httpUpdate (request->http);
  else
    http_status = request->last_status;

  request->last_status = http_status;


  if (http_status == HTTP_STATUS_CONTINUE || http_status == HTTP_STATUS_OK)
    {
      GIOStatus io_status;
      GError *error;

      error = NULL;

      /* send data */
      io_status =
        g_io_channel_read_chars (request->data_io, 
                          buffer, 
     _GTK_CUPS_MAX_CHUNK_SIZE,
     &bytes,
     &error);

      if (io_status == G_IO_STATUS_ERROR)
        {
          request->state = GTK_CUPS_POST_DONE;
   request->poll_state = GTK_CUPS_HTTP_IDLE;
     
          gtk_cups_result_set_error (request->result,
                                     GTK_CUPS_ERROR_IO,
                                     io_status,
                                     error->code, 
                                     "Error reading from cache file: %s",
                                     error->message);

   g_error_free (error);
          return;
 }
      else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
        {
          request->state = GTK_CUPS_POST_CHECK;
   request->poll_state = GTK_CUPS_HTTP_READ;

          request->attempts = 0;
          return;
        }


      if (httpWrite (request->http, buffer, bytes) < bytes)
        {
          int http_errno;

          http_errno = httpGetError (request->http);

          request->state = GTK_CUPS_POST_DONE;
   request->poll_state = GTK_CUPS_HTTP_IDLE;
     
          gtk_cups_result_set_error (request->result,
                                     GTK_CUPS_ERROR_HTTP,
                                     http_status,
                                     http_errno, 
                                     "Error writing to socket in Post %s"
                                     g_strerror (http_errno));
          return;
        }
    }
  else if (http_status == HTTP_STATUS_UNAUTHORIZED)
    {
      request->state = GTK_CUPS_POST_CHECK;
      request->poll_state = GTK_CUPS_HTTP_READ;

      request->attempts = 0;
      return;
    }
  else
    {
      request->attempts++;
    }
}

static void
_post_auth (GtkCupsRequest *request)
{
  if (request->password_state == GTK_CUPS_PASSWORD_HAS)
    {
      if (request->password == NULL)
        {
          request->state = GTK_CUPS_POST_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;

          gtk_cups_result_set_error (request->result, 
                                     GTK_CUPS_ERROR_AUTH,
                                     0,
                                     1,
                                     "Canceled by user");
        }
      else
        request->state = GTK_CUPS_POST_CHECK;
    }
}

static void
_get_auth (GtkCupsRequest *request)
{
  if (request->password_state == GTK_CUPS_PASSWORD_HAS)
    {
      if (request->password == NULL)
        {
          request->state = GTK_CUPS_GET_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;

          gtk_cups_result_set_error (request->result, 
                                     GTK_CUPS_ERROR_AUTH,
                                     0,
                                     1,
                                     "Canceled by user");
        }
      else
        request->state = GTK_CUPS_GET_CHECK;
    }
}

/* Very ugly hack: cups has a stupid synchronous password callback 
 * that doesn't even take the request or user data parameters, so 
 * we have to use a static variable to pass the password to it.
 * Not threadsafe !
 * The callback sets cups_password to NULL to signal that the 
 * password has been used.
 */

static char *cups_password = NULL;
static char *cups_username = NULL;

static const char *
#if CUPS_VERSION_MAJOR < 3
passwordCB (const char *prompt)
#else
passwordCB (const char *prompt,
            http_t     *http,
            const char *method,
            const char *resource,
            void       *user_data)
#endif
{
  char *pwd = cups_password;
  cups_password = NULL;

  cupsSetUser (cups_username);

  return pwd;
}

static void 
_post_check (GtkCupsRequest *request)
{
  http_status_t http_status;

  http_status = request->last_status;

  GTK_DEBUG (PRINTING, "CUPS Backend: %s - status %i", G_STRFUNC, http_status);

  request->poll_state = GTK_CUPS_HTTP_READ;

  if (http_status == HTTP_STATUS_CONTINUE)
    {
      goto again; 
    }
  else if (http_status == HTTP_STATUS_UNAUTHORIZED)
    {
      int auth_result = -1;
      httpFlush (request->http);

      if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
        {
          request->poll_state = GTK_CUPS_HTTP_IDLE;
          request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
          request->state = GTK_CUPS_POST_AUTH;
          request->need_password = TRUE;

          return;
        }

      /* Negotiate */
      if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate"9== 0)
        {
          auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
        }
      /* Basic, BasicDigest, Digest and PeerCred */
      else
        {
          if (request->password_state == GTK_CUPS_PASSWORD_NONE)
            {
              cups_username = request->username;
#if CUPS_VERSION_MAJOR < 3
              cupsSetPasswordCB (passwordCB);
#else
              cupsSetPasswordCB (passwordCB, NULL);
#endif

              /* This call success for PeerCred authentication */
              auth_result = cupsDoAuthentication (request->http, "POST", request->resource);

              if (auth_result != 0)
                {
                  /* move to AUTH state to let the backend 
                   * ask for a password
                   */
 
                  request->poll_state = GTK_CUPS_HTTP_IDLE;
                  request->state = GTK_CUPS_POST_AUTH;
                  request->need_password = TRUE;

                  return;
                }
            }
          else
            {
              cups_password = request->password;
              cups_username = request->username;

              auth_result = cupsDoAuthentication (request->http, "POST", request->resource);

              if (cups_password != NULL)
                return;

              if (request->password != NULL)
                {
                  memset (request->password, 0, strlen (request->password));
                  g_clear_pointer (&request->password, g_free);
                }

              request->password_state = GTK_CUPS_PASSWORD_APPLIED;
            }
        }

      if (auth_result ||
          !httpConnectAgain (request->http, 30000, NULL))
        {
          /* if the password has been used, reset password_state
           * so that we ask for a new one next time around
           */
 
          if (cups_password == NULL)
            request->password_state = GTK_CUPS_PASSWORD_NONE;

          request->state = GTK_CUPS_POST_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;
          gtk_cups_result_set_error (request->result, 
                                     GTK_CUPS_ERROR_AUTH,
                                     0,
                                     0,
                                     "Not authorized");
          return;
        }
      
      if (request->data_io != NULL)
        g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);

      request->state = GTK_CUPS_POST_CONNECT;
      request->poll_state = GTK_CUPS_HTTP_WRITE;
    }
  else if (http_status == HTTP_STATUS_ERROR)
    {
      int error = httpGetError (request->http);
#ifdef G_OS_WIN32
      if (error != WSAENETDOWN && error != WSAENETUNREACH)
#else
      if (error != ENETDOWN && error != ENETUNREACH)   
#endif /* G_OS_WIN32 */
        {
          request->attempts++;
          goto again;
        }
      else
        {
          request->state = GTK_CUPS_POST_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;
     
          gtk_cups_result_set_error (request->result,
                                     GTK_CUPS_ERROR_HTTP,
                                     http_status,
                                     error, 
                                     "Unknown HTTP error");

          return;
        }
    }
  else if (http_status == HTTP_STATUS_UPGRADE_REQUIRED)
    {
      /* Flush any error message... */
      httpFlush (request->http);

      cupsSetEncryption (HTTP_ENCRYPTION_REQUIRED);
      request->state = GTK_CUPS_POST_CONNECT;

      /* Reconnect... */
      if (httpConnectAgain (request->http, 30000, NULL))
        {
          /* Upgrade with encryption... */
          httpSetEncryption (request->http, HTTP_ENCRYPTION_REQUIRED);
        }

      request->attempts++;
      goto again;
    }
  else if (http_status != HTTP_STATUS_OK)
    {
      int http_errno;

      http_errno = httpGetError (request->http);

      if (http_errno == EPIPE)
        request->state = GTK_CUPS_POST_CONNECT;
      else
        {
          request->state = GTK_CUPS_POST_DONE;
          gtk_cups_result_set_error (request->result,
                                     GTK_CUPS_ERROR_HTTP,
                                     http_status,
                                     http_errno, 
                                     "HTTP Error in POST %s"
                                     g_strerror (http_errno));
          request->poll_state = GTK_CUPS_HTTP_IDLE;
 
          httpFlush (request->http); 
          return;
        }

      request->poll_state = GTK_CUPS_HTTP_IDLE;
      request->last_status = HTTP_STATUS_CONTINUE;

      httpFlush (request->http);
      if (request->own_http)
        httpClose (request->http);
      request->http = NULL;

      return;
    }
  else
    {
      request->state = GTK_CUPS_POST_READ_RESPONSE;
      return;
    }

 again:
  http_status = HTTP_STATUS_CONTINUE;

  if (httpWait (request->http, 0))
    http_status = httpUpdate (request->http);

  request->last_status = http_status;
}

static void 
_post_read_response (GtkCupsRequest *request)
{
  ipp_state_t ipp_status;

  GTK_DEBUG (PRINTING, "CUPS Backend: %s", G_STRFUNC);

  request->poll_state = GTK_CUPS_HTTP_READ;

  if (request->result->ipp_response == NULL)
    request->result->ipp_response = ippNew();

  ipp_status = ippRead (request->http, 
                        request->result->ipp_response);

  if (ipp_status == IPP_STATE_ERROR)
    {
      int ipp_error = cupsGetError ();
      gtk_cups_result_set_error (request->result,  
                                 GTK_CUPS_ERROR_IPP,
                                 ipp_status,
                                 ipp_error,
                                 "%s",
                                 ippErrorString (ipp_error));
      
      ippDelete (request->result->ipp_response);
      request->result->ipp_response = NULL;

      request->state = GTK_CUPS_POST_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;
    }
  else if (ipp_status == IPP_STATE_DATA)
    {
      request->state = GTK_CUPS_POST_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;
    }
}

static void 
_get_send (GtkCupsRequest *request)
{
  GTK_DEBUG (PRINTING, "CUPS Backend: %s", G_STRFUNC);

  request->poll_state = GTK_CUPS_HTTP_WRITE;

  if (request->data_io == NULL)
    {
      gtk_cups_result_set_error (request->result,
                                 GTK_CUPS_ERROR_IO,
                                 G_IO_STATUS_ERROR,
                                 G_IO_CHANNEL_ERROR_FAILED, 
                                 "Get requires an open io channel");

      request->state = GTK_CUPS_GET_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;

      return;
    }

  httpClearFields (request->http);
  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));

#if CUPS_VERSION_MAJOR < 3
  if (httpGet (request->http, request->resource))
#else
  if (!httpWriteRequest (request->http, "GET", request->resource))
#endif
    {
      if (!httpConnectAgain (request->http, 30000, NULL))
        {
          request->state = GTK_CUPS_GET_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;
  
          /* TODO: should add a status or error code for failed GET */ 
          gtk_cups_result_set_error (request->result, 
                                     GTK_CUPS_ERROR_GENERAL,
                                     0,
                                     0,
                                     "Failed Get");
        }

      request->attempts++;
      return;    
    }

  if (httpWait (request->http, 0))
    request->last_status = httpUpdate (request->http);
        
  request->attempts = 0;

  request->state = GTK_CUPS_GET_CHECK;
  request->poll_state = GTK_CUPS_HTTP_READ;
  
  ippSetState (request->ipp_request, IPP_STATE_IDLE);
}

static void 
_get_check (GtkCupsRequest *request)
{
  http_status_t http_status;

  GTK_DEBUG (PRINTING, "CUPS Backend: %s", G_STRFUNC);

  http_status = request->last_status;

  request->poll_state = GTK_CUPS_HTTP_READ;

  if (http_status == HTTP_STATUS_CONTINUE)
    {
      goto again; 
    }
  else if (http_status == HTTP_STATUS_UNAUTHORIZED)
    {
      int auth_result = -1;
      httpFlush (request->http);

      if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
        {
          request->poll_state = GTK_CUPS_HTTP_IDLE;
          request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
          request->state = GTK_CUPS_GET_AUTH;
          request->need_password = TRUE;

          return;
        }

      /* Negotiate */
      if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate"9== 0)
        {
          auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
        }
      /* Basic, BasicDigest, Digest and PeerCred */
      else
        {
          if (request->password_state == GTK_CUPS_PASSWORD_NONE)
            {
              cups_username = request->username;
#if CUPS_VERSION_MAJOR < 3
              cupsSetPasswordCB (passwordCB);
#else
              cupsSetPasswordCB (passwordCB, NULL);
#endif

              /* This call success for PeerCred authentication */
              auth_result = cupsDoAuthentication (request->http, "GET", request->resource);

              if (auth_result != 0)
                {
                  /* move to AUTH state to let the backend
                   * ask for a password
                   */

                  request->poll_state = GTK_CUPS_HTTP_IDLE;
                  request->state = GTK_CUPS_GET_AUTH;
                  request->need_password = TRUE;

                  return;
                }
            }
          else
            {
              cups_password = request->password;
              cups_username = request->username;

              auth_result = cupsDoAuthentication (request->http, "GET", request->resource);

              if (cups_password != NULL)
                return;

              if (request->password != NULL)
                {
                  memset (request->password, 0, strlen (request->password));
                  g_clear_pointer (&request->password, g_free);
                }

              request->password_state = GTK_CUPS_PASSWORD_APPLIED;
            }
        }

      if (auth_result ||
          !httpConnectAgain (request->http, 30000, NULL))
        {
          /* if the password has been used, reset password_state
           * so that we ask for a new one next time around
           */

          if (cups_password == NULL)
            request->password_state = GTK_CUPS_PASSWORD_NONE;

          request->state = GTK_CUPS_GET_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;
          gtk_cups_result_set_error (request->result, 
                                     GTK_CUPS_ERROR_AUTH,
                                     0,
                                     0,
                                     "Not authorized");
          return;
        }

      request->state = GTK_CUPS_GET_CONNECT;
      request->last_status = HTTP_STATUS_CONTINUE;

     return;
    }
  else if (http_status == HTTP_STATUS_UPGRADE_REQUIRED)
    {
      /* Flush any error message... */
      httpFlush (request->http);

      cupsSetEncryption (HTTP_ENCRYPTION_REQUIRED);
      request->state = GTK_CUPS_GET_CONNECT;

      /* Reconnect... */
      if (httpConnectAgain (request->http, 30000, NULL))
        {
          /* Upgrade with encryption... */
          httpSetEncryption (request->http, HTTP_ENCRYPTION_REQUIRED);
        }

      request->attempts++;
      goto again;
    }
  else if (http_status != HTTP_STATUS_OK)
    {
      int http_errno;

      http_errno = httpGetError (request->http);

      if (http_errno == EPIPE)
        request->state = GTK_CUPS_GET_CONNECT;
      else
        {
          request->state = GTK_CUPS_GET_DONE;
          gtk_cups_result_set_error (request->result,
                                     GTK_CUPS_ERROR_HTTP,
                                     http_status,
                                     http_errno, 
                                     "HTTP Error in GET %s"
                                     g_strerror (http_errno));
          request->poll_state = GTK_CUPS_HTTP_IDLE;
          httpFlush (request->http);

          return;
        }

      request->poll_state = GTK_CUPS_HTTP_IDLE;
      request->last_status = HTTP_STATUS_CONTINUE;

      httpFlush (request->http);
      if (request->own_http)
        httpClose (request->http);
      request->http = NULL;

      return;
    }
  else
    {
      request->state = GTK_CUPS_GET_READ_DATA;
      return;
    }

 again:
  http_status = HTTP_STATUS_CONTINUE;

  if (httpWait (request->http, 0))
    http_status = httpUpdate (request->http);

  request->last_status = http_status;

}

static void 
_get_read_data (GtkCupsRequest *request)
{
  char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
  gsize bytes;
  gsize bytes_written;
  GIOStatus io_status;
  GError *error;

  GTK_DEBUG (PRINTING, "CUPS Backend: %s", G_STRFUNC);

  error = NULL;

  request->poll_state = GTK_CUPS_HTTP_READ;

  bytes = httpRead (request->http, buffer, sizeof (buffer));
  request->bytes_received += bytes;

  GTK_DEBUG (PRINTING, "CUPS Backend: %"G_GSIZE_FORMAT" bytes read", bytes);

  io_status =
    g_io_channel_write_chars (request->data_io, 
                              buffer, 
         bytes, 
         &bytes_written,
         &error);

  if (io_status == G_IO_STATUS_ERROR)
    {
      request->state = GTK_CUPS_GET_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;

      gtk_cups_result_set_error (request->result,
                                 GTK_CUPS_ERROR_IO,
                                 io_status,
                                 error->code,
                                 "%s",
                                 error->message);
      g_error_free (error);
    }

  /* Stop if we do not expect any more data or EOF was received. */
  if (httpGetLength (request->http) <= request->bytes_received || bytes == 0)
    {
      request->state = GTK_CUPS_GET_DONE;
      request->poll_state = GTK_CUPS_HTTP_IDLE;

      return;
    }
}

gboolean
gtk_cups_request_is_done (GtkCupsRequest *request)
{
  return (request->state == GTK_CUPS_REQUEST_DONE);
}

gboolean
gtk_cups_result_is_error (GtkCupsResult *result)
{
  return result->is_error;
}

ipp_t *
gtk_cups_result_get_response (GtkCupsResult *result)
{
  return result->ipp_response;
}

GtkCupsErrorType
gtk_cups_result_get_error_type (GtkCupsResult *result)
{
  return result->error_type;
}

int
gtk_cups_result_get_error_status (GtkCupsResult *result)
{
  return result->error_status;
}

int
gtk_cups_result_get_error_code (GtkCupsResult *result)
{
  return result->error_code;
}

const char *
gtk_cups_result_get_error_string (GtkCupsResult *result)
{
  return result->error_msg; 
}

/* This function allocates new instance of GtkCupsConnectionTest() and creates
 * a socket for communication with a CUPS server 'server'.
 */

GtkCupsConnectionTest *
gtk_cups_connection_test_new (const char *server,
                              const int   port)
{
  GtkCupsConnectionTest *result = NULL;
  char                  *port_str = NULL;

  result = g_new (GtkCupsConnectionTest, 1);

  if (port >= 0)
    port_str = g_strdup_printf ("%d", port);
  else
    port_str = g_strdup_printf ("%d", ippGetPort ());

  if (server != NULL)
    result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
  else
    result->addrlist = httpAddrGetList (cupsGetServer (), AF_UNSPEC, port_str);

  g_free (port_str);

  result->socket = -1;
  result->current_addr = NULL;
  result->last_wrong_addr = NULL;
  result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;

  result->at_init = gtk_cups_connection_test_get_state (result);

  return result;
}


/* A non-blocking test whether it is possible to connect to a CUPS server specified
 * inside of GtkCupsConnectionTest structure.
 *  - you need to check it more then once.
 * The connection is closed after a successful connection.
 */

GtkCupsConnectionState 
gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
{
  GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
  http_addrlist_t       *iter;
  int                    error_code;
  int                    flags;
  int                    code;

  if (test == NULL)
    return GTK_CUPS_CONNECTION_NOT_AVAILABLE;

  if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
    {
      test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
      return GTK_CUPS_CONNECTION_AVAILABLE;
    }
  else
    {
      if (test->socket == -1)
        {
          if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
            iter = test->last_wrong_addr->next;
          else
            {
              test->last_wrong_addr = NULL;
              iter = test->addrlist;
            }

          while (iter)
            {
              test->socket = socket (iter->addr.addr.sa_family,
                                     SOCK_STREAM,
                                     0);

              if (test->socket >= 0)
                {
                  flags = fcntl (test->socket, F_GETFL);

                  if (flags != -1)
                    flags |= O_NONBLOCK;

                  fcntl (test->socket, F_SETFL, flags);
              
                  test->current_addr = iter;
              
                  break;
                }
               iter = iter->next;
            }
        }

      if (test->socket >= 0)
        {
          code = connect (test->socket,
                          &test->current_addr->addr.addr,
                          httpAddrGetLength (&test->current_addr->addr));

          error_code = errno;

          if (code == 0 || error_code == EISCONN)
            {
              close (test->socket);
              test->socket = -1;
              test->current_addr = NULL;
              result = GTK_CUPS_CONNECTION_AVAILABLE;
            }
          else
            {
              if (error_code == EALREADY || error_code == EINPROGRESS)
                result = GTK_CUPS_CONNECTION_IN_PROGRESS;
              else
                {
                  close (test->socket);
                  test->socket = -1;
                  test->last_wrong_addr = test->current_addr;
                  result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
                }
            }
         }

      return result;
    }
}

/* This function frees memory used by the GtkCupsConnectionTest structure.
 */

void 
gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
{
  if (test == NULL)
    return;

  test->current_addr = NULL;
  test->last_wrong_addr = NULL;
  httpAddrFreeList (test->addrlist);
  if (test->socket != -1)
    {
      close (test->socket);
      test->socket = -1;
    }
  g_free (test);
}

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet am  2026-07-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik