#include "globals.h"
#include "debug.h"
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#if defined(WIN32) || defined(__EMX__)
# include <io.h>
#else
# include <unistd.h>
#endif
#ifndef O_BINARY
# define O_BINARY 0
#endif
ipp_t *
cupsDoFileRequest(http_t *http,
ipp_t *request,
const char *resource,
const char *filename)
{
ipp_t *response;
int infile;
DEBUG_printf(("cupsDoFileRequest(http=%p, request=%p(%s), resource=\"%s\", "
"filename=\"%s\")", http, request,
request ? ippOpString(request->request.op.operation_id) : "?",
resource, filename));
if (filename)
{
if ((infile = open(filename, O_RDONLY | O_BINARY)) < 0)
{
_cupsSetError(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
NULL, 0);
ippDelete(request);
return (NULL);
}
}
else
infile = -1;
response = cupsDoIORequest(http, request, resource, infile, -1);
if (infile >= 0)
close(infile);
return (response);
}
ipp_t *
cupsDoIORequest(http_t *http,
ipp_t *request,
const char *resource,
int infile,
int outfile)
{
ipp_t *response = NULL;
size_t length = 0;
http_status_t status;
struct stat fileinfo;
int bytes;
char buffer[32768];
DEBUG_printf(("cupsDoIORequest(http=%p, request=%p(%s), resource=\"%s\", "
"infile=%d, outfile=%d)", http, request,
request ? ippOpString(request->request.op.operation_id) : "?",
resource, infile, outfile));
if (!request || !resource)
{
ippDelete(request);
_cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
return (NULL);
}
if (!http)
if ((http = _cupsConnect()) == NULL)
return (NULL);
if (infile >= 0)
{
if (fstat(infile, &fileinfo))
{
_cupsSetError(errno == EBADF ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
NULL, 0);
ippDelete(request);
return (NULL);
}
#ifdef WIN32
if (fileinfo.st_mode & _S_IFDIR)
#else
if (S_ISDIR(fileinfo.st_mode))
#endif
{
ippDelete(request);
_cupsSetError(IPP_NOT_POSSIBLE, strerror(EISDIR), 0);
return (NULL);
}
#ifndef WIN32
if (!S_ISREG(fileinfo.st_mode))
length = 0;
else
#endif
length = ippLength(request) + fileinfo.st_size;
}
else
length = ippLength(request);
DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld",
(long)ippLength(request), (long)length));
if (http->authstring && !strncmp(http->authstring, "Local ", 6))
httpSetAuthString(http, NULL, NULL);
while (response == NULL)
{
DEBUG_puts("2cupsDoIORequest: setup...");
status = cupsSendRequest(http, request, resource, length);
DEBUG_printf(("2cupsDoIORequest: status=%d", status));
if (status == HTTP_CONTINUE && request->state == IPP_DATA && infile >= 0)
{
DEBUG_puts("2cupsDoIORequest: file write...");
#ifndef WIN32
if (S_ISREG(fileinfo.st_mode))
#endif
lseek(infile, 0, SEEK_SET);
while ((bytes = (int)read(infile, buffer, sizeof(buffer))) > 0)
{
if (httpCheck(http))
{
if ((status = httpUpdate(http)) != HTTP_CONTINUE)
break;
}
if (httpWrite2(http, buffer, bytes) < bytes)
break;
}
}
if (status == HTTP_CONTINUE || status == HTTP_OK)
{
response = cupsGetResponse(http, resource);
status = http->status;
}
DEBUG_printf(("2cupsDoIORequest: status=%d", status));
if (status >= HTTP_BAD_REQUEST &&
status != HTTP_UNAUTHORIZED &&
status != HTTP_UPGRADE_REQUIRED)
{
httpFlush(http);
_cupsSetHTTPError(status);
break;
}
if (response)
{
if (outfile >= 0)
{
while ((bytes = (int)httpRead2(http, buffer, sizeof(buffer))) > 0)
if (write(outfile, buffer, bytes) < bytes)
break;
}
else
{
httpFlush(http);
}
}
}
ippDelete(request);
return (response);
}
ipp_t *
cupsDoRequest(http_t *http,
ipp_t *request,
const char *resource)
{
DEBUG_printf(("cupsDoRequest(http=%p, request=%p(%s), resource=\"%s\")",
http, request,
request ? ippOpString(request->request.op.operation_id) : "?",
resource));
return (cupsDoIORequest(http, request, resource, -1, -1));
}
ipp_t *
cupsGetResponse(http_t *http,
const char *resource)
{
http_status_t status;
ipp_state_t state;
ipp_t *response = NULL;
DEBUG_printf(("cupsGetResponse(http=%p, resource=\"%s\")", http, resource));
if (!http)
http = _cupsConnect();
if (!http || (http->state != HTTP_POST_RECV && http->state != HTTP_POST_SEND))
return (NULL);
if (http->data_encoding == HTTP_ENCODE_CHUNKED)
{
DEBUG_puts("2cupsGetResponse: Finishing chunked POST...");
if (httpWrite2(http, "", 0) < 0)
return (NULL);
}
DEBUG_printf(("2cupsGetResponse: Update loop, http->status=%d...",
http->status));
status = http->status;
while (status == HTTP_CONTINUE)
status = httpUpdate(http);
DEBUG_printf(("2cupsGetResponse: status=%d", status));
if (status == HTTP_OK)
{
response = ippNew();
while ((state = ippRead(http, response)) != IPP_DATA)
if (state == IPP_ERROR)
break;
if (state == IPP_ERROR)
{
DEBUG_puts("1cupsGetResponse: IPP read error!");
ippDelete(response);
response = NULL;
_cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
}
}
else if (status != HTTP_ERROR)
{
httpFlush(http);
if (status == HTTP_UNAUTHORIZED)
{
DEBUG_puts("2cupsGetResponse: Need authorization...");
if (!cupsDoAuthentication(http, "POST", resource))
httpReconnect(http);
else
status = HTTP_AUTHORIZATION_CANCELED;
}
#ifdef HAVE_SSL
else if (status == HTTP_UPGRADE_REQUIRED)
{
DEBUG_puts("2cupsGetResponse: Need encryption...");
if (!httpReconnect(http))
httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
}
#endif
}
if (response)
{
ipp_attribute_t *attr;
attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
DEBUG_printf(("1cupsGetResponse: status-code=%s, status-message=\"%s\"",
ippErrorString(response->request.status.status_code),
attr ? attr->values[0].string.text : ""));
_cupsSetError(response->request.status.status_code,
attr ? attr->values[0].string.text :
ippErrorString(response->request.status.status_code), 0);
}
else if (status != HTTP_OK)
_cupsSetHTTPError(status);
return (response);
}
ssize_t
cupsReadResponseData(
http_t *http,
char *buffer,
size_t length)
{
DEBUG_printf(("cupsReadResponseData(http=%p, buffer=%p, "
"length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
if (!http)
{
_cups_globals_t *cg = _cupsGlobals();
if ((http = cg->http) == NULL)
{
_cupsSetError(IPP_INTERNAL_ERROR, _("No active connection"), 1);
return (-1);
}
}
return (httpRead2(http, buffer, length));
}
http_status_t
cupsSendRequest(http_t *http,
ipp_t *request,
const char *resource,
size_t length)
{
http_status_t status;
int got_status;
ipp_state_t state;
http_status_t expect;
DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", "
"length=" CUPS_LLFMT ")", http, request,
request ? ippOpString(request->request.op.operation_id) : "?",
resource, CUPS_LLCAST length));
if (!request || !resource)
{
_cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
return (HTTP_ERROR);
}
if (!http)
if ((http = _cupsConnect()) == NULL)
return (HTTP_SERVICE_UNAVAILABLE);
#ifdef HAVE_SSL
if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
!httpAddrLocalhost(http->hostaddr) && !http->tls &&
httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
{
_cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
return (HTTP_SERVICE_UNAVAILABLE);
}
#endif
if (!strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close"))
if (httpReconnect(http))
{
_cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
return (HTTP_SERVICE_UNAVAILABLE);
}
expect = HTTP_CONTINUE;
for (;;)
{
DEBUG_puts("2cupsSendRequest: Setup...");
httpClearFields(http);
httpSetLength(http, length);
httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
httpSetExpect(http, expect);
DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring));
DEBUG_puts("2cupsSendRequest: Sending HTTP POST...");
if (httpPost(http, resource))
{
if (httpReconnect(http))
{
_cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
return (HTTP_SERVICE_UNAVAILABLE);
}
else
continue;
}
DEBUG_puts("2cupsSendRequest: Writing IPP request...");
request->state = IPP_IDLE;
status = HTTP_CONTINUE;
got_status = 0;
while ((state = ippWrite(http, request)) != IPP_DATA)
if (state == IPP_ERROR)
break;
else if (httpCheck(http))
{
got_status = 1;
if ((status = httpUpdate(http)) != HTTP_CONTINUE)
break;
}
if (!got_status && expect == HTTP_CONTINUE)
{
DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
if (httpWait(http, 1000))
status = httpUpdate(http);
}
else if (httpCheck(http))
status = httpUpdate(http);
DEBUG_printf(("2cupsSendRequest: status=%d", status));
switch (status)
{
case HTTP_ERROR :
case HTTP_CONTINUE :
case HTTP_OK :
return (status);
case HTTP_UNAUTHORIZED :
if (!cupsDoAuthentication(http, "POST", resource))
{
if (httpReconnect(http))
{
_cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
return (HTTP_SERVICE_UNAVAILABLE);
}
}
else
status = HTTP_AUTHORIZATION_CANCELED;
return (status);
#ifdef HAVE_SSL
case HTTP_UPGRADE_REQUIRED :
if (httpReconnect(http))
{
_cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
return (HTTP_SERVICE_UNAVAILABLE);
}
httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
return (status);
#endif
case HTTP_EXPECTATION_FAILED :
expect = (http_status_t)0;
if (httpReconnect(http))
{
_cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
return (HTTP_SERVICE_UNAVAILABLE);
}
break;
default :
return (status);
}
}
}
http_status_t
cupsWriteRequestData(
http_t *http,
const char *buffer,
size_t length)
{
int wused;
DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, "
"length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
if (!http)
{
_cups_globals_t *cg = _cupsGlobals();
if ((http = cg->http) == NULL)
{
_cupsSetError(IPP_INTERNAL_ERROR, _("No active connection"), 1);
return (HTTP_ERROR);
}
}
wused = http->wused;
if (httpWrite2(http, buffer, length) < 0)
return (HTTP_ERROR);
if (length >= HTTP_MAX_BUFFER ||
http->wused < wused ||
(wused > 0 && http->wused == length))
{
if (_httpWait(http, 0, 1))
return (httpUpdate(http));
}
return (HTTP_CONTINUE);
}
void
_cupsSetError(ipp_status_t status,
const char *message,
int localize)
{
_cups_globals_t *cg;
if (!message && errno)
{
message = strerror(errno);
localize = 0;
}
cg = _cupsGlobals();
cg->last_error = status;
if (cg->last_status_message)
{
_cupsStrFree(cg->last_status_message);
cg->last_status_message = NULL;
}
if (message)
{
if (localize)
{
if (!cg->lang_default)
cg->lang_default = cupsLangDefault();
cg->last_status_message = _cupsStrAlloc(_cupsLangString(cg->lang_default,
message));
}
else
cg->last_status_message = _cupsStrAlloc(message);
}
DEBUG_printf(("4_cupsSetError: last_error=%s, last_status_message=\"%s\"",
ippErrorString(cg->last_error), cg->last_status_message));
}
void
_cupsSetHTTPError(http_status_t status)
{
switch (status)
{
case HTTP_NOT_FOUND :
_cupsSetError(IPP_NOT_FOUND, httpStatus(status), 0);
break;
case HTTP_UNAUTHORIZED :
case HTTP_AUTHORIZATION_CANCELED :
_cupsSetError(IPP_NOT_AUTHORIZED, httpStatus(status), 0);
break;
case HTTP_FORBIDDEN :
_cupsSetError(IPP_FORBIDDEN, httpStatus(status), 0);
break;
case HTTP_BAD_REQUEST :
_cupsSetError(IPP_BAD_REQUEST, httpStatus(status), 0);
break;
case HTTP_REQUEST_TOO_LARGE :
_cupsSetError(IPP_REQUEST_VALUE, httpStatus(status), 0);
break;
case HTTP_NOT_IMPLEMENTED :
_cupsSetError(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status), 0);
break;
case HTTP_NOT_SUPPORTED :
_cupsSetError(IPP_VERSION_NOT_SUPPORTED, httpStatus(status), 0);
break;
default :
DEBUG_printf(("4_cupsSetHTTPError: HTTP error %d mapped to "
"IPP_SERVICE_UNAVAILABLE!", status));
_cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status), 0);
break;
}
}