mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-16 23:24:16 +00:00
2c5ae21cdc
Adds authentication support of CUPS backend against CUPS server. Print dialog is now capable to ask user for password and pass it to the CUPS server. It is also possible to authenticate user through Kerberos (GSS-API) (#384940).
1611 lines
42 KiB
C
1611 lines
42 KiB
C
/* GTK - The GIMP Toolkit
|
|
* gtkcupsutils.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, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <gtk/gtk.h>
|
|
#include "gtkcupsutils.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
|
|
{
|
|
gchar *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
|
|
|
|
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
|
|
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,
|
|
gint 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 (cupsServer ());
|
|
|
|
|
|
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 = NULL;
|
|
request->http = httpConnectEncrypt (request->server,
|
|
ippPort (),
|
|
cupsEncryption ());
|
|
|
|
if (request->http)
|
|
httpBlocking (request->http, 0);
|
|
|
|
request->own_http = TRUE;
|
|
}
|
|
|
|
request->last_status = HTTP_CONTINUE;
|
|
|
|
request->attempts = 0;
|
|
request->data_io = data_io;
|
|
|
|
request->ipp_request = ippNew ();
|
|
request->ipp_request->request.op.operation_id = operation_id;
|
|
request->ipp_request->request.op.request_id = 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, language->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, cupsUser ());
|
|
|
|
cupsLangFree (language);
|
|
|
|
return request;
|
|
}
|
|
|
|
GtkCupsRequest *
|
|
gtk_cups_request_new (http_t *connection,
|
|
GtkCupsRequestType req_type,
|
|
gint 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);
|
|
|
|
gtk_cups_result_free (request->result);
|
|
|
|
g_free (request);
|
|
}
|
|
|
|
gboolean
|
|
gtk_cups_request_read_write (GtkCupsRequest *request)
|
|
{
|
|
if (request->type == GTK_CUPS_POST)
|
|
post_states[request->state] (request);
|
|
else if (request->type == GTK_CUPS_GET)
|
|
get_states[request->state] (request);
|
|
|
|
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;
|
|
request->poll_state = GTK_CUPS_HTTP_IDLE;
|
|
}
|
|
|
|
if (request->state == GTK_CUPS_REQUEST_DONE)
|
|
{
|
|
request->poll_state = GTK_CUPS_HTTP_IDLE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
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 && attribute->values != NULL)
|
|
return attribute->values[0].string.text;
|
|
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 }
|
|
};
|
|
|
|
|
|
static ipp_tag_t
|
|
_find_option_tag (const gchar *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 gchar *option,
|
|
const gchar *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 (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;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
_connect (GtkCupsRequest *request)
|
|
{
|
|
request->poll_state = GTK_CUPS_HTTP_IDLE;
|
|
|
|
if (request->http == NULL)
|
|
{
|
|
request->http = httpConnectEncrypt (request->server,
|
|
ippPort (),
|
|
cupsEncryption ());
|
|
|
|
if (request->http == NULL)
|
|
request->attempts++;
|
|
|
|
if (request->http)
|
|
httpBlocking (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)
|
|
{
|
|
gchar length[255];
|
|
struct stat data_info;
|
|
|
|
GTK_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %s\n", 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) (ippLength (request->ipp_request) + data_info.st_size));
|
|
}
|
|
else
|
|
sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
|
|
|
|
httpClearFields (request->http);
|
|
httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
|
|
httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
|
|
#ifdef HAVE_HTTPGETAUTHSTRING
|
|
httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
|
|
#else
|
|
#ifdef HAVE_HTTP_AUTHSTRING
|
|
httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
|
|
#endif
|
|
#endif
|
|
|
|
if (httpPost (request->http, request->resource))
|
|
{
|
|
if (httpReconnect (request->http))
|
|
{
|
|
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;
|
|
request->ipp_request->state = IPP_IDLE;
|
|
}
|
|
|
|
static void
|
|
_post_write_request (GtkCupsRequest *request)
|
|
{
|
|
ipp_state_t ipp_status;
|
|
|
|
GTK_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
|
|
|
request->poll_state = GTK_CUPS_HTTP_WRITE;
|
|
|
|
ipp_status = ippWrite (request->http, request->ipp_request);
|
|
|
|
if (ipp_status == IPP_ERROR)
|
|
{
|
|
int cups_error = cupsLastError ();
|
|
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_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_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
|
|
|
request->poll_state = GTK_CUPS_HTTP_WRITE;
|
|
|
|
if (httpCheck (request->http))
|
|
http_status = httpUpdate (request->http);
|
|
else
|
|
http_status = request->last_status;
|
|
|
|
request->last_status = http_status;
|
|
|
|
|
|
if (http_status == HTTP_CONTINUE || http_status == HTTP_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 HAVE_CUPS_API_1_2
|
|
if (httpWrite2 (request->http, buffer, bytes) < bytes)
|
|
#else
|
|
if (httpWrite (request->http, buffer, (int) bytes) < bytes)
|
|
#endif /* HAVE_CUPS_API_1_2 */
|
|
{
|
|
int http_errno;
|
|
|
|
http_errno = httpError (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_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;
|
|
static char *cups_username;
|
|
|
|
static const char *
|
|
passwordCB (const char *prompt)
|
|
{
|
|
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_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
|
|
|
|
request->poll_state = GTK_CUPS_HTTP_READ;
|
|
|
|
if (http_status == HTTP_CONTINUE)
|
|
{
|
|
goto again;
|
|
}
|
|
else if (http_status == HTTP_UNAUTHORIZED)
|
|
{
|
|
int auth_result = -1;
|
|
httpFlush (request->http);
|
|
|
|
if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
|
|
{
|
|
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_password = g_strdup ("");
|
|
cups_username = request->username;
|
|
cupsSetPasswordCB (passwordCB);
|
|
|
|
/* 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->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_free (request->password);
|
|
request->password = NULL;
|
|
}
|
|
|
|
request->password_state = GTK_CUPS_PASSWORD_APPLIED;
|
|
}
|
|
}
|
|
|
|
if (auth_result ||
|
|
httpReconnect (request->http))
|
|
{
|
|
/* 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_ERROR)
|
|
{
|
|
int error = httpError (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_UPGRADE_REQUIRED)
|
|
{
|
|
/* Flush any error message... */
|
|
httpFlush (request->http);
|
|
|
|
cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
|
|
request->state = GTK_CUPS_POST_CONNECT;
|
|
|
|
/* Reconnect... */
|
|
httpReconnect (request->http);
|
|
|
|
/* Upgrade with encryption... */
|
|
httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
|
|
|
|
request->attempts++;
|
|
goto again;
|
|
}
|
|
else if (http_status != HTTP_OK)
|
|
{
|
|
int http_errno;
|
|
|
|
http_errno = httpError (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;
|
|
|
|
httpFlush (request->http);
|
|
|
|
request->last_status = HTTP_CONTINUE;
|
|
httpClose (request->http);
|
|
request->http = NULL;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
request->state = GTK_CUPS_POST_READ_RESPONSE;
|
|
return;
|
|
}
|
|
|
|
again:
|
|
http_status = HTTP_CONTINUE;
|
|
|
|
if (httpCheck (request->http))
|
|
http_status = httpUpdate (request->http);
|
|
|
|
request->last_status = http_status;
|
|
}
|
|
|
|
static void
|
|
_post_read_response (GtkCupsRequest *request)
|
|
{
|
|
ipp_state_t ipp_status;
|
|
|
|
GTK_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %s\n", 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_ERROR)
|
|
{
|
|
int ipp_error = cupsLastError ();
|
|
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_DATA)
|
|
{
|
|
request->state = GTK_CUPS_POST_DONE;
|
|
request->poll_state = GTK_CUPS_HTTP_IDLE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_get_send (GtkCupsRequest *request)
|
|
{
|
|
GTK_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %s\n", 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);
|
|
#ifdef HAVE_HTTPGETAUTHSTRING
|
|
httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
|
|
#else
|
|
#ifdef HAVE_HTTP_AUTHSTRING
|
|
httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
|
|
#endif
|
|
#endif
|
|
|
|
if (httpGet (request->http, request->resource))
|
|
{
|
|
if (httpReconnect (request->http))
|
|
{
|
|
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 (httpCheck (request->http))
|
|
request->last_status = httpUpdate (request->http);
|
|
|
|
request->attempts = 0;
|
|
|
|
request->state = GTK_CUPS_GET_CHECK;
|
|
request->poll_state = GTK_CUPS_HTTP_READ;
|
|
|
|
request->ipp_request->state = IPP_IDLE;
|
|
}
|
|
|
|
static void
|
|
_get_check (GtkCupsRequest *request)
|
|
{
|
|
http_status_t http_status;
|
|
|
|
GTK_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
|
|
|
http_status = request->last_status;
|
|
|
|
request->poll_state = GTK_CUPS_HTTP_READ;
|
|
|
|
if (http_status == HTTP_CONTINUE)
|
|
{
|
|
goto again;
|
|
}
|
|
else if (http_status == HTTP_UNAUTHORIZED)
|
|
{
|
|
int auth_result = -1;
|
|
httpFlush (request->http);
|
|
|
|
if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
|
|
{
|
|
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_password = g_strdup ("");
|
|
cups_username = request->username;
|
|
cupsSetPasswordCB (passwordCB);
|
|
|
|
/* 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->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_free (request->password);
|
|
request->password = NULL;
|
|
}
|
|
|
|
request->password_state = GTK_CUPS_PASSWORD_APPLIED;
|
|
}
|
|
}
|
|
|
|
if (auth_result ||
|
|
httpReconnect (request->http))
|
|
{
|
|
/* 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_SEND;
|
|
request->last_status = HTTP_CONTINUE;
|
|
|
|
return;
|
|
}
|
|
else if (http_status == HTTP_UPGRADE_REQUIRED)
|
|
{
|
|
/* Flush any error message... */
|
|
httpFlush (request->http);
|
|
|
|
cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
|
|
request->state = GTK_CUPS_GET_CONNECT;
|
|
|
|
/* Reconnect... */
|
|
httpReconnect (request->http);
|
|
|
|
/* Upgrade with encryption... */
|
|
httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
|
|
|
|
request->attempts++;
|
|
goto again;
|
|
}
|
|
else if (http_status != HTTP_OK)
|
|
{
|
|
int http_errno;
|
|
|
|
http_errno = httpError (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;
|
|
httpFlush (request->http);
|
|
httpClose (request->http);
|
|
request->last_status = HTTP_CONTINUE;
|
|
request->http = NULL;
|
|
return;
|
|
|
|
}
|
|
else
|
|
{
|
|
request->state = GTK_CUPS_GET_READ_DATA;
|
|
return;
|
|
}
|
|
|
|
again:
|
|
http_status = HTTP_CONTINUE;
|
|
|
|
if (httpCheck (request->http))
|
|
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_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %s\n", G_STRFUNC));
|
|
|
|
error = NULL;
|
|
|
|
request->poll_state = GTK_CUPS_HTTP_READ;
|
|
|
|
#if HAVE_CUPS_API_1_2
|
|
bytes = httpRead2 (request->http, buffer, sizeof (buffer));
|
|
#else
|
|
bytes = httpRead (request->http, buffer, sizeof (buffer));
|
|
#endif /* HAVE_CUPS_API_1_2 */
|
|
|
|
GTK_NOTE (PRINTING,
|
|
g_print ("CUPS Backend: %i bytes read\n", bytes));
|
|
|
|
if (bytes == 0)
|
|
{
|
|
request->state = GTK_CUPS_GET_DONE;
|
|
request->poll_state = GTK_CUPS_HTTP_IDLE;
|
|
|
|
return;
|
|
}
|
|
|
|
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,
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
GtkCupsConnectionTest *result = NULL;
|
|
#ifdef HAVE_CUPS_API_1_2
|
|
gchar *port_str = NULL;
|
|
|
|
result = g_new (GtkCupsConnectionTest, 1);
|
|
|
|
port_str = g_strdup_printf ("%d", ippPort ());
|
|
|
|
if (server != NULL)
|
|
result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
|
|
else
|
|
result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
|
|
|
|
g_free (port_str);
|
|
|
|
result->socket = -1;
|
|
result->current_addr = NULL;
|
|
result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
|
|
|
|
result->at_init = gtk_cups_connection_test_get_state (result);
|
|
#else
|
|
result = g_new (GtkCupsConnectionTest, 1);
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
#ifdef HAVE_CUPS_API_1_2
|
|
GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
|
|
http_addrlist_t *iter;
|
|
gint error_code;
|
|
gint flags;
|
|
gint 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)
|
|
{
|
|
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,
|
|
httpAddrLength (&test->current_addr->addr));
|
|
|
|
error_code = errno;
|
|
|
|
if (code == 0)
|
|
{
|
|
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
|
|
result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
return GTK_CUPS_CONNECTION_AVAILABLE;
|
|
#endif
|
|
}
|
|
|
|
/* This function frees memory used by the GtkCupsConnectionTest structure.
|
|
*/
|
|
void
|
|
gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
|
|
{
|
|
if (test == NULL)
|
|
return;
|
|
|
|
#ifdef HAVE_CUPS_API_1_2
|
|
test->current_addr = NULL;
|
|
httpAddrFreeList (test->addrlist);
|
|
if (test->socket != -1)
|
|
{
|
|
close (test->socket);
|
|
test->socket = -1;
|
|
}
|
|
#endif
|
|
g_free (test);
|
|
}
|