+ {
+ if (httpFlushWrite(http) < 0)
+ return (-1);
+ }
+
+ if (http->data_encoding == HTTP_ENCODING_CHUNKED)
+ {
+ /*
+ * Send a 0-length chunk at the end of the request...
+ */
+
+ http_write(http, "0\r\n\r\n", 5);
+
+ /*
+ * Reset the data state...
+ */
+
+ http->data_encoding = HTTP_ENCODING_FIELDS;
+ http->data_remaining = 0;
+ }
+
+ if (http->state == HTTP_STATE_POST_RECV)
+ http->state ++;
+ else if (http->state == HTTP_STATE_POST_SEND ||
+ http->state == HTTP_STATE_GET_SEND)
+ http->state = HTTP_STATE_WAITING;
+ else
+ http->state = HTTP_STATE_STATUS;
+
+ DEBUG_printf(("2httpWrite2: Changed state to %s.",
+ httpStateString(http->state)));
+ }
+
+ DEBUG_printf(("1httpWrite2: Returning " CUPS_LLFMT ".", CUPS_LLCAST bytes));
+
+ return (bytes);
+}
+
+
+/*
+ * 'httpWriteResponse()' - Write a HTTP response to a client connection.
+ *
+ * @since CUPS 1.7/macOS 10.9@
+ */
+
+int /* O - 0 on success, -1 on error */
+httpWriteResponse(http_t *http, /* I - HTTP connection */
+ http_status_t status) /* I - Status code */
+{
+ http_encoding_t old_encoding; /* Old data_encoding value */
+ off_t old_remaining; /* Old data_remaining value */
+
+
+ /*
+ * Range check input...
+ */
+
+ DEBUG_printf(("httpWriteResponse(http=%p, status=%d)", (void *)http, status));
+
+ if (!http || status < HTTP_STATUS_CONTINUE)
+ {
+ DEBUG_puts("1httpWriteResponse: Bad input.");
+ return (-1);
+ }
+
+ /*
+ * Set the various standard fields if they aren't already...
+ */
+
+ if (!http->fields[HTTP_FIELD_DATE][0])
+ httpSetField(http, HTTP_FIELD_DATE, httpGetDateString(time(NULL)));
+
+ if (status >= HTTP_STATUS_BAD_REQUEST && http->keep_alive)
+ {
+ http->keep_alive = HTTP_KEEPALIVE_OFF;
+ httpSetField(http, HTTP_FIELD_KEEP_ALIVE, "");
+ }
+
+ if (http->version == HTTP_VERSION_1_1)
+ {
+ if (!http->fields[HTTP_FIELD_CONNECTION][0])
+ {
+ if (http->keep_alive)
+ httpSetField(http, HTTP_FIELD_CONNECTION, "Keep-Alive");
+ else
+ httpSetField(http, HTTP_FIELD_CONNECTION, "close");
+ }
+
+ if (http->keep_alive && !http->fields[HTTP_FIELD_KEEP_ALIVE][0])
+ httpSetField(http, HTTP_FIELD_KEEP_ALIVE, "timeout=10");
+ }
+
+#ifdef HAVE_SSL
+ if (status == HTTP_STATUS_UPGRADE_REQUIRED ||
+ status == HTTP_STATUS_SWITCHING_PROTOCOLS)
+ {
+ if (!http->fields[HTTP_FIELD_CONNECTION][0])
+ httpSetField(http, HTTP_FIELD_CONNECTION, "Upgrade");
+
+ if (!http->fields[HTTP_FIELD_UPGRADE][0])
+ httpSetField(http, HTTP_FIELD_UPGRADE, "TLS/1.2,TLS/1.1,TLS/1.0");
+
+ if (!http->fields[HTTP_FIELD_CONTENT_LENGTH][0])
+ httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, "0");
+ }
+#endif /* HAVE_SSL */
+
+ if (!http->server)
+ httpSetField(http, HTTP_FIELD_SERVER,
+ http->default_server ? http->default_server : CUPS_MINIMAL);
+
+ /*
+ * Set the Accept-Encoding field if it isn't already...
+ */
+
+ if (!http->accept_encoding)
+ httpSetField(http, HTTP_FIELD_ACCEPT_ENCODING,
+ http->default_accept_encoding ? http->default_accept_encoding :
+#ifdef HAVE_LIBZ
+ "gzip, deflate, identity");
+#else
+ "identity");
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Send the response header...
+ */
+
+ old_encoding = http->data_encoding;
+ old_remaining = http->data_remaining;
+ http->data_encoding = HTTP_ENCODING_FIELDS;
+
+ if (httpPrintf(http, "HTTP/%d.%d %d %s\r\n", http->version / 100,
+ http->version % 100, (int)status, httpStatus(status)) < 0)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ if (status != HTTP_STATUS_CONTINUE)
+ {
+ /*
+ * 100 Continue doesn't have the rest of the response headers...
+ */
+
+ int i; /* Looping var */
+ const char *value; /* Field value */
+
+ for (i = 0; i < HTTP_FIELD_MAX; i ++)
+ {
+ if ((value = httpGetField(http, i)) != NULL && *value)
+ {
+ if (httpPrintf(http, "%s: %s\r\n", http_fields[i], value) < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+ }
+ }
+
+ if (http->cookie)
+ {
+ if (strchr(http->cookie, ';'))
+ {
+ if (httpPrintf(http, "Set-Cookie: %s\r\n", http->cookie) < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+ }
+ else if (httpPrintf(http, "Set-Cookie: %s; path=/; httponly;%s\r\n", http->cookie, http->tls ? " secure;" : "") < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+ }
+
+ /*
+ * "Click-jacking" defense (STR #4492)...
+ */
+
+ if (httpPrintf(http, "X-Frame-Options: DENY\r\n"
+ "Content-Security-Policy: frame-ancestors 'none'\r\n") < 1)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+ }
+
+ if (httpWrite2(http, "\r\n", 2) < 2)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ if (httpFlushWrite(http) < 0)
+ {
+ http->status = HTTP_STATUS_ERROR;
+ return (-1);
+ }
+
+ if (status == HTTP_STATUS_CONTINUE ||
+ status == HTTP_STATUS_SWITCHING_PROTOCOLS)
+ {
+ /*
+ * Restore the old data_encoding and data_length values...
+ */
+
+ http->data_encoding = old_encoding;
+ http->data_remaining = old_remaining;
+
+ if (old_remaining <= INT_MAX)
+ http->_data_remaining = (int)old_remaining;
+ else
+ http->_data_remaining = INT_MAX;
+ }
+ else if (http->state == HTTP_STATE_OPTIONS ||
+ http->state == HTTP_STATE_HEAD ||
+ http->state == HTTP_STATE_PUT ||
+ http->state == HTTP_STATE_TRACE ||
+ http->state == HTTP_STATE_CONNECT ||
+ http->state == HTTP_STATE_STATUS)
+ {
+ DEBUG_printf(("1httpWriteResponse: Resetting state to HTTP_STATE_WAITING, "
+ "was %s.", httpStateString(http->state)));
+ http->state = HTTP_STATE_WAITING;
+ }
+ else
+ {
+ /*
+ * Force data_encoding and data_length to be set according to the response
+ * headers...
+ */
+
+ http_set_length(http);
+
+ if (http->data_encoding == HTTP_ENCODING_LENGTH && http->data_remaining == 0)
+ {
+ DEBUG_printf(("1httpWriteResponse: Resetting state to HTTP_STATE_WAITING, "
+ "was %s.", httpStateString(http->state)));
+ http->state = HTTP_STATE_WAITING;
+ return (0);
+ }
+
+#ifdef HAVE_LIBZ
+ /*
+ * Then start any content encoding...
+ */
+
+ DEBUG_puts("1httpWriteResponse: Calling http_content_coding_start.");
+ http_content_coding_start(http,
+ httpGetField(http, HTTP_FIELD_CONTENT_ENCODING));
+#endif /* HAVE_LIBZ */
+
+ }
+
+ return (0);
+}
+
+
+#ifdef HAVE_LIBZ
+/*
+ * 'http_content_coding_finish()' - Finish doing any content encoding.
+ */
+
+static void
+http_content_coding_finish(
+ http_t *http) /* I - HTTP connection */
+{
+ int zerr; /* Compression status */
+ Byte dummy[1]; /* Dummy read buffer */
+ size_t bytes; /* Number of bytes to write */
+
+
+ DEBUG_printf(("http_content_coding_finish(http=%p)", (void *)http));
+ DEBUG_printf(("1http_content_coding_finishing: http->coding=%d", http->coding));
+
+ switch (http->coding)
+ {
+ case _HTTP_CODING_DEFLATE :
+ case _HTTP_CODING_GZIP :
+ http->stream.next_in = dummy;
+ http->stream.avail_in = 0;
+
+ do
+ {
+ zerr = deflate(&(http->stream), Z_FINISH);
+ bytes = _HTTP_MAX_SBUFFER - http->stream.avail_out;
+
+ if (bytes > 0)
+ {
+ DEBUG_printf(("1http_content_coding_finish: Writing trailing chunk, len=%d", (int)bytes));
+
+ if (http->data_encoding == HTTP_ENCODING_CHUNKED)
+ http_write_chunk(http, (char *)http->sbuffer, bytes);
+ else
+ http_write(http, (char *)http->sbuffer, bytes);
+ }
+
+ http->stream.next_out = (Bytef *)http->sbuffer;
+ http->stream.avail_out = (uInt)_HTTP_MAX_SBUFFER;
+ }
+ while (zerr == Z_OK);