#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;
if (filename)
{
if ((infile = open(filename, O_RDONLY | O_BINARY)) < 0)
{
_cupsSetError(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
strerror(errno));
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;
size_t length;
http_status_t status;
int got_status;
ipp_state_t state;
struct stat fileinfo;
int bytes;
char buffer[32768];
http_status_t expect;
DEBUG_printf(("cupsDoFileRequest(%p, %p, \'%s\', \'%s\')\n",
http, request, resource ? resource : "(null)",
filename ? filename : "(null)"));
if (http == NULL || request == NULL || resource == NULL)
{
if (request != NULL)
ippDelete(request);
_cupsSetError(IPP_INTERNAL_ERROR, NULL);
return (NULL);
}
if (infile >= 0)
{
if (fstat(infile, &fileinfo))
{
_cupsSetError(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
strerror(errno));
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));
return (NULL);
}
}
#ifdef HAVE_SSL
if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
!httpAddrLocalhost(http->hostaddr) && !http->tls &&
httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
return (NULL);
#endif
response = NULL;
status = HTTP_ERROR;
expect = HTTP_CONTINUE;
while (response == NULL)
{
DEBUG_puts("cupsDoFileRequest: setup...");
length = ippLength(request);
if (infile >= 0)
{
#ifndef WIN32
if (!S_ISREG(fileinfo.st_mode))
length = 0;
else
#endif
length += fileinfo.st_size;
}
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(("cupsDoFileRequest: authstring=\"%s\"\n", http->authstring));
DEBUG_puts("cupsDoFileRequest: post...");
if (httpPost(http, resource))
{
if (httpReconnect(http))
{
status = HTTP_ERROR;
break;
}
else
continue;
}
DEBUG_puts("cupsDoFileRequest: ipp write...");
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)
{
if (httpWait(http, 1000))
status = httpUpdate(http);
}
else if (httpCheck(http))
status = httpUpdate(http);
if (status == HTTP_CONTINUE && state == IPP_DATA && infile >= 0)
{
DEBUG_puts("cupsDoFileRequest: 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;
}
}
DEBUG_puts("cupsDoFileRequest: update...");
while (status == HTTP_CONTINUE)
status = httpUpdate(http);
DEBUG_printf(("cupsDoFileRequest: status = %d\n", status));
if (status == HTTP_UNAUTHORIZED)
{
DEBUG_puts("cupsDoFileRequest: unauthorized...");
httpFlush(http);
if (cupsDoAuthentication(http, "POST", resource))
break;
if (httpReconnect(http))
{
status = HTTP_ERROR;
break;
}
continue;
}
else if (status == HTTP_ERROR)
{
DEBUG_printf(("cupsDoFileRequest: http->error=%d (%s)\n", http->error,
strerror(http->error)));
#ifdef WIN32
if (http->error != WSAENETDOWN && http->error != WSAENETUNREACH &&
http->error != WSAETIMEDOUT)
#else
if (http->error != ENETDOWN && http->error != ENETUNREACH &&
http->error != ETIMEDOUT)
#endif
continue;
else
break;
}
#ifdef HAVE_SSL
else if (status == HTTP_UPGRADE_REQUIRED)
{
httpFlush(http);
if (httpReconnect(http))
{
status = HTTP_ERROR;
break;
}
httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
continue;
}
#endif
else if (status == HTTP_EXPECTATION_FAILED)
{
expect = (http_status_t)0;
}
else if (status != HTTP_OK)
{
DEBUG_printf(("cupsDoFileRequest: error %d...\n", status));
httpFlush(http);
break;
}
else
{
DEBUG_puts("cupsDoFileRequest: response...");
response = ippNew();
while ((state = ippRead(http, response)) != IPP_DATA)
if (state == IPP_ERROR)
break;
if (state == IPP_ERROR)
{
DEBUG_puts("IPP read error!");
ippDelete(response);
response = NULL;
_cupsSetError(IPP_SERVICE_UNAVAILABLE, strerror(errno));
break;
}
else if (outfile >= 0)
{
while ((bytes = (int)httpRead2(http, buffer, sizeof(buffer))) > 0)
if (write(outfile, buffer, bytes) < bytes)
break;
}
else
{
httpFlush(http);
}
}
}
ippDelete(request);
if (response)
{
ipp_attribute_t *attr;
attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
_cupsSetError(response->request.status.status_code,
attr ? attr->values[0].string.text :
ippErrorString(response->request.status.status_code));
}
else if (status != HTTP_OK)
_cupsSetHTTPError(status);
return (response);
}
ipp_t *
cupsDoRequest(http_t *http,
ipp_t *request,
const char *resource)
{
return (cupsDoFileRequest(http, request, resource, NULL));
}
void
_cupsSetError(ipp_status_t status,
const char *message)
{
_cups_globals_t *cg;
cg = _cupsGlobals();
cg->last_error = status;
if (cg->last_status_message)
{
free(cg->last_status_message);
cg->last_status_message = NULL;
}
if (message)
cg->last_status_message = strdup(message);
}
void
_cupsSetHTTPError(http_status_t status)
{
switch (status)
{
case HTTP_NOT_FOUND :
_cupsSetError(IPP_NOT_FOUND, httpStatus(status));
break;
case HTTP_UNAUTHORIZED :
_cupsSetError(IPP_NOT_AUTHORIZED, httpStatus(status));
break;
case HTTP_FORBIDDEN :
_cupsSetError(IPP_FORBIDDEN, httpStatus(status));
break;
case HTTP_BAD_REQUEST :
_cupsSetError(IPP_BAD_REQUEST, httpStatus(status));
break;
case HTTP_REQUEST_TOO_LARGE :
_cupsSetError(IPP_REQUEST_VALUE, httpStatus(status));
break;
case HTTP_NOT_IMPLEMENTED :
_cupsSetError(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status));
break;
case HTTP_NOT_SUPPORTED :
_cupsSetError(IPP_VERSION_NOT_SUPPORTED, httpStatus(status));
break;
default :
DEBUG_printf(("HTTP error %d mapped to IPP_SERVICE_UNAVAILABLE!\n",
status));
_cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status));
break;
}
}