/* Process extra 'len' bytes of 'buf' which were received after the * DEFLATE data. */ static int process_footer(ne_decompress *ctx, const unsigned char *buf, size_t len) { if (len + ctx->footcount > 8) { ne_set_error(ctx->session, "Too many bytes (%" NE_FMT_SIZE_T ") in gzip footer", len); return -1; } else { memcpy(ctx->footer + ctx->footcount, buf, len); ctx->footcount += len; if (ctx->footcount == 8) { uLong crc = BUF2UINT(ctx->footer) & 0xFFFFFFFF; if (crc == ctx->checksum) { ctx->state = NE_Z_FINISHED; NE_DEBUG(NE_DBG_HTTP, "compress: End of response; checksum match.\n"); } else { NE_DEBUG(NE_DBG_HTTP, "compress: End of response; checksum mismatch: " "given %lu vs computed %lu\n", crc, ctx->checksum); ne_set_error(ctx->session, "Checksum invalid for compressed stream"); return -1; } } } return 0; }
static int get_range_common(ne_session *sess, const char *req_uri, const char *brange, char *buf, ssize_t *bytes_read) { ne_request *req = ne_request_create(sess, "GET", req_uri); const ne_status *status; int ret; ne_add_request_header(req, "Range", brange); ne_add_request_header(req, "Accept-Ranges", "bytes"); ret = dispatch_to_buffer(sess, req, buf, brange, bytes_read); status = ne_get_status(req); if (ret == NE_OK && status->code == 416) { /* connection is terminated too early with Apache/1.3, so we check * this even if ret == NE_ERROR... */ ne_set_error(sess, "Range is not satisfiable"); ret = NE_ERROR; } else if (ret == NE_OK) { if (status->klass == 2 && status->code != 206) { ne_set_error(sess, "Resource does not support ranged GET requests"); ret = NE_ERROR; } else if (status->klass != 2) { ret = NE_ERROR; } } ne_request_destroy(req); return ret; }
/* parse_header parses the gzip header, sets the next state and returns * HDR_DONE: all done, bytes following are raw DEFLATE data. * HDR_EXTENDED: all done, expect a NUL-termianted string * before the DEFLATE data * HDR_ERROR: invalid header, give up (session error is set). */ static int parse_header(ne_decompress *ctx) { NE_DEBUG(NE_DBG_HTTP, "ID1: %d ID2: %d, cmeth %d, flags %d\n", HDR_ID1(ctx), HDR_ID2(ctx), HDR_CMETH(ctx), HDR_FLAGS(ctx)); if (HDR_ID1(ctx) != ID1 || HDR_ID2(ctx) != ID2 || HDR_CMETH(ctx) != 8) { ne_set_error(ctx->session, "Compressed stream invalid"); return HDR_ERROR; } NE_DEBUG(NE_DBG_HTTP, "mtime: %d, xflags: %d, os: %d\n", HDR_MTIME(ctx), HDR_XFLAGS(ctx), HDR_OS(ctx)); /* TODO: we can only handle one NUL-terminated extensions field * currently. Really, we should count the number of bits set, and * skip as many fields as bits set (bailing if any reserved bits * are set. */ if (HDR_FLAGS(ctx) == 8) { ctx->state = NE_Z_POST_HEADER; return HDR_EXTENDED; } else if (HDR_FLAGS(ctx) != 0) { ne_set_error(ctx->session, "Compressed stream not supported"); return HDR_ERROR; } NE_DEBUG(NE_DBG_HTTP, "compress: Good stream.\n"); ctx->state = NE_Z_INFLATING; return HDR_DONE; }
/* Verifies an SSL server certificate. */ static int check_certificate(ne_session *sess, gnutls_session sock, ne_ssl_certificate *chain) { int ret, failures = 0; ne_uri server; unsigned int status; memset(&server, 0, sizeof server); ne_fill_server_uri(sess, &server); ret = check_identity(&server, chain->subject, NULL); ne_uri_free(&server); if (ret < 0) { ne_set_error(sess, _("Server certificate was missing commonName " "attribute in subject name")); return NE_ERROR; } else if (ret > 0) { failures |= NE_SSL_IDMISMATCH; } failures |= check_chain_expiry(chain); ret = gnutls_certificate_verify_peers2(sock, &status); NE_DEBUG(NE_DBG_SSL, "ssl: Verify peers returned %d, status=%u\n", ret, status); if (ret != GNUTLS_E_SUCCESS) { ne_set_error(sess, _("Could not verify server certificate: %s"), gnutls_strerror(ret)); return NE_ERROR; } failures |= map_verify_failures(&status); NE_DEBUG(NE_DBG_SSL, "ssl: Verification failures = %d (status = %u).\n", failures, status); if (status && status != GNUTLS_CERT_INVALID) { char *errstr = verify_error_string(status); ne_set_error(sess, _("Certificate verification error: %s"), errstr); ne_free(errstr); return NE_ERROR; } if (failures == 0) { ret = NE_OK; } else { ne__ssl_set_verify_err(sess, failures); ret = NE_ERROR; if (sess->ssl_verify_fn && sess->ssl_verify_fn(sess->ssl_verify_ud, failures, chain) == 0) ret = NE_OK; } return ret; }
/* Negotiate an SSL connection. */ int ne__negotiate_ssl(ne_session *sess) { ne_ssl_context *const ctx = sess->ssl_context; ne_ssl_certificate *chain; gnutls_session sock; NE_DEBUG(NE_DBG_SSL, "Negotiating SSL connection.\n"); /* Pass through the hostname if SNI is enabled. */ ctx->hostname = sess->flags[NE_SESSFLAG_TLS_SNI] ? sess->server.hostname : NULL; if (ne_sock_connect_ssl(sess->socket, ctx, sess)) { if (sess->ssl_cc_requested) { ne_set_error(sess, _("SSL handshake failed, " "client certificate was requested: %s"), ne_sock_error(sess->socket)); } else { ne_set_error(sess, _("SSL handshake failed: %s"), ne_sock_error(sess->socket)); } return NE_ERROR; } sock = ne__sock_sslsock(sess->socket); chain = make_peers_chain(sock, ctx->cred); if (chain == NULL) { ne_set_error(sess, _("Server did not send certificate chain")); return NE_ERROR; } if (sess->server_cert && ne_ssl_cert_cmp(sess->server_cert, chain) == 0) { /* Same cert as last time; presume OK. This is not optimal as * make_peers_chain() has already gone through and done the * expensive DER parsing stuff for the whole chain by now. */ ne_ssl_cert_free(chain); return NE_OK; } if (check_certificate(sess, sock, chain)) { ne_ssl_cert_free(chain); return NE_ERROR; } sess->server_cert = chain; return NE_OK; }
/* Handle an XML response parse error, setting session error string * and closing the connection. */ static int parse_error(ne_session *sess, ne_xml_parser *parser) { ne_set_error(sess, _("Could not parse response: %s"), ne_xml_get_error(parser)); ne_close_connection(sess); return NE_ERROR; }
/* Dispatch a GET request REQ, writing the response body to FD fd. If * RANGE is non-NULL, then it is the value of the Range request * header, e.g. "bytes=1-5". Returns an NE_* error code. */ static int dispatch_to_fd(ne_request *req, int fd, const char *range) { ne_session *const sess = ne_get_session(req); const ne_status *const st = ne_get_status(req); int ret; do { const char *value; ret = ne_begin_request(req); if (ret != NE_OK) break; value = ne_get_response_header(req, "Content-Range"); /* For a 206 response, check that a Content-Range header is * given which matches the Range request header. */ if (range && st->code == 206 && (value == NULL || strncmp(value, "bytes ", 6) != 0 || strcmp(range + 6, value + 6))) { ne_set_error(sess, _("Response did not include requested range")); return NE_ERROR; } if ((range && st->code == 206) || (!range && st->klass == 2)) { ret = ne_read_response_to_fd(req, fd); } else { ret = ne_discard_response(req); } if (ret == NE_OK) ret = ne_end_request(req); } while (ret == NE_RETRY); return ret; }
/* PUT's from fd to URI */ int ne_put(ne_session *sess, const char *uri, int fd) { ne_request *req; struct stat st; int ret; if (fstat(fd, &st)) { int errnum = errno; char buf[200]; ne_set_error(sess, _("Could not determine file size: %s"), ne_strerror(errnum, buf, sizeof buf)); return NE_ERROR; } req = ne_request_create(sess, "PUT", uri); #ifdef NE_HAVE_DAV ne_lock_using_resource(req, uri, 0); ne_lock_using_parent(req, uri); #endif ne_set_request_body_fd(req, fd, 0, st.st_size); ret = ne_request_dispatch(req); if (ret == NE_OK && ne_get_status(req)->klass != 2) ret = NE_ERROR; ne_request_destroy(req); return ret; }
/* A zlib function failed with 'code'; set the session error string * appropriately. */ static void set_zlib_error(ne_decompress *ctx, const char *msg, int code) { if (ctx->zstr.msg) ne_set_error(ctx->session, _("%s: %s"), msg, ctx->zstr.msg); else { const char *err; switch (code) { case Z_STREAM_ERROR: err = "stream error"; break; case Z_DATA_ERROR: err = "data corrupt"; break; case Z_MEM_ERROR: err = "out of memory"; break; case Z_BUF_ERROR: err = "buffer error"; break; case Z_VERSION_ERROR: err = "library version mismatch"; break; default: err = "unknown error"; break; } ne_set_error(ctx->session, _("%s: %s (code %d)"), msg, err, code); } }
int ne_get_range(ne_session *sess, const char *uri, ne_content_range *range, int fd) { ne_request *req = ne_request_create(sess, "GET", uri); const ne_status *status; int ret; char brange[64]; if (range->end == -1) { ne_snprintf(brange, sizeof brange, "bytes=%" NE_FMT_OFF_T "-", range->start); } else { ne_snprintf(brange, sizeof brange, "bytes=%" NE_FMT_OFF_T "-%" NE_FMT_OFF_T, range->start, range->end); } ne_add_request_header(req, "Range", brange); ne_add_request_header(req, "Accept-Ranges", "bytes"); ret = dispatch_to_fd(req, fd, brange); status = ne_get_status(req); if (ret == NE_OK && status->code == 416) { /* connection is terminated too early with Apache/1.3, so we check * this even if ret == NE_ERROR... */ ne_set_error(sess, _("Range is not satisfiable")); ret = NE_ERROR; } else if (ret == NE_OK) { if (status->klass == 2 && status->code != 206) { ne_set_error(sess, _("Resource does not support ranged GETs.")); ret = NE_ERROR; } else if (status->klass != 2) { ret = NE_ERROR; } } ne_request_destroy(req); return ret; }
int ne_lock_refresh(ne_session *sess, struct ne_lock *lock) { ne_request *req = ne_request_create(sess, "LOCK", lock->uri.path); ne_xml_parser *parser = ne_xml_create(); int ret; struct lock_ctx ctx; memset(&ctx, 0, sizeof ctx); ctx.cdata = ne_buffer_create(); ctx.req = req; ctx.token = lock->token; /* Handle the response and update *lock appropriately. */ ne_xml_push_handler(parser, lk_startelm, lk_cdata, lk_endelm, &ctx); /* For a lock refresh, submitting only this lock token must be * sufficient. */ ne_print_request_header(req, "If", "(<%s>)", lock->token); add_timeout_header(req, lock->timeout); ret = ne_xml_dispatch_request(req, parser); if (ret == NE_OK) { if (ne_get_status(req)->klass != 2) { ret = NE_ERROR; /* and use default session error */ } else if (ne_xml_failed(parser)) { ret = NE_ERROR; ne_set_error(sess, "%s", ne_xml_get_error(parser)); } else if (!ctx.found) { ne_set_error(sess, _("No activelock for <%s> returned in " "LOCK refresh response"), lock->token); ret = NE_ERROR; } else /* success! */ { /* update timeout for passed-in lock structure. */ lock->timeout = ctx.active.timeout; } } ne_lock_free(&ctx.active); ne_buffer_destroy(ctx.cdata); ne_request_destroy(req); ne_xml_destroy(parser); return ret; }
/* Negotiate an SSL connection. */ int ne__negotiate_ssl(ne_session *sess) { ne_ssl_context *const ctx = sess->ssl_context; ne_ssl_certificate *chain; gnutls_session sock; NE_DEBUG(NE_DBG_SSL, "Negotiating SSL connection.\n"); if (ne_sock_connect_ssl(sess->socket, ctx, sess)) { ne_set_error(sess, _("SSL negotiation failed: %s"), ne_sock_error(sess->socket)); return NE_ERROR; } sock = ne__sock_sslsock(sess->socket); chain = make_peers_chain(sock); if (chain == NULL) { ne_set_error(sess, _("Server did not send certificate chain")); return NE_ERROR; } if (sess->server_cert && ne_ssl_cert_cmp(sess->server_cert, chain) == 0) { /* Same cert as last time; presume OK. This is not optimal as * make_peers_chain() has already gone through and done the * expensive DER parsing stuff for the whole chain by now. */ ne_ssl_cert_free(chain); return NE_OK; } if (check_certificate(sess, sock, chain)) { ne_ssl_cert_free(chain); return NE_ERROR; } sess->server_cert = chain; return NE_OK; }
/* Dispatch a GET request REQ, writing the response body to FD fd. If * RANGE is non-NULL, then it is the value of the Range request * header, e.g. "bytes=1-5". Returns an NE_* error code. */ static int dispatch_to_fd(ne_request *req, int fd, const char *range) { ne_session *const sess = ne_get_session(req); const ne_status *const st = ne_get_status(req); int ret; do { const char *value; ret = ne_begin_request(req); if (ret != NE_OK) break; value = ne_get_response_header(req, "Content-Range"); /* For a 206 response, check that a Content-Range header is * given which matches the Range request header. */ if (range && st->code == 206) { int err = 0; if (value == NULL || strncmp(value, "bytes ", 6) != 0) { err++; } else { /* If the response gives a range begin-end/total, limit * the comparison to the range itself. */ int len = strlen(value); char *cp = strchr(value, '/'); if (cp != NULL) len = (int)(cp - value); len -= 6; if (strncmp(range + 6, value + 6, len)) err++; } if (err) { ne_set_error(sess, _("Response did not include requested range")); return NE_ERROR; } } if ((range && st->code == 206) || (!range && st->klass == 2)) { ret = ne_read_response_to_fd(req, fd); } else { ret = ne_discard_response(req); } if (ret == NE_OK) ret = ne_end_request(req); } while (ret == NE_RETRY); return ret; }
static int lk_startelm(void *userdata, int parent, const char *nspace, const char *name, const char **atts) { struct lock_ctx *ctx = userdata; int id; id = ne_xml_mapid(element_map, NE_XML_MAPLEN(element_map), nspace, name); NE_DEBUG(NE_DBG_LOCKS, "lk_startelm: %s => %d\n", name, id); if (id == 0) return NE_XML_DECLINE; if (parent == 0 && ctx->token == NULL) { const char *token = ne_get_response_header(ctx->req, "Lock-Token"); /* at the root element; retrieve the Lock-Token header, * and bail if it wasn't given. */ if (token == NULL) { ne_set_error(ne_get_session(ctx->req), "%s", _("LOCK response missing Lock-Token header")); return NE_XML_ABORT; } if (token[0] == '<') token++; ctx->token = ne_strdup(token); ne_shave(ctx->token, ">"); NE_DEBUG(NE_DBG_LOCKS, "lk_startelm: Finding token %s\n", ctx->token); } /* TODO: only accept 'prop' as root for LOCK response */ if (!can_accept(parent, id)) return NE_XML_DECLINE; if (id == ELM_activelock && !ctx->found) { /* a new activelock */ ne_lock_free(&ctx->active); memset(&ctx->active, 0, sizeof ctx->active); ctx->active.timeout = NE_TIMEOUT_INVALID; } ne_buffer_clear(ctx->cdata); return id; }
/* Verifies an SSL server certificate. */ static int check_certificate(ne_session *sess, gnutls_session sock, ne_ssl_certificate *chain) { time_t before, after, now = time(NULL); int ret, failures = 0; before = gnutls_x509_crt_get_activation_time(chain->subject); after = gnutls_x509_crt_get_expiration_time(chain->subject); if (now < before) failures |= NE_SSL_NOTYETVALID; else if (now > after) failures |= NE_SSL_EXPIRED; ret = check_identity(sess->server.hostname, chain->subject, NULL); if (ret < 0) { ne_set_error(sess, _("Server certificate was missing commonName " "attribute in subject name")); return NE_ERROR; } else if (ret > 0) { failures |= NE_SSL_IDMISMATCH; } if (gnutls_certificate_verify_peers(sock)) { failures |= NE_SSL_UNTRUSTED; } NE_DEBUG(NE_DBG_SSL, "Failures = %d\n", failures); if (failures == 0) { ret = NE_OK; } else { ne__ssl_set_verify_err(sess, failures); ret = NE_ERROR; if (sess->ssl_verify_fn && sess->ssl_verify_fn(sess->ssl_verify_ud, failures, chain) == 0) ret = NE_OK; } return ret; }
static int dispatch_to_buffer(ne_session *sess, ne_request *req, char *buf, const char *range, ssize_t *bytes_read) { const ne_status *const st = ne_get_status(req); int ret; size_t rlen; /* length of bytespec after "bytes=" */ rlen = range ? strlen(range + 6) : 0; do { const char *value; ret = ne_begin_request(req); if (ret != NE_OK) break; value = ne_get_response_header(req, "Content-Range"); /* For a 206 response, check that a Content-Range header is * given which matches the Range request header. */ if (range && st->code == 206 && (value == NULL || strncmp(value, "bytes ", 6) != 0 || strncmp(range + 6, value + 6, rlen) || (range[5 + rlen] != '-' && value[6 + rlen] != '/'))) { ne_set_error(sess, "Response did not include requested range"); return NE_ERROR; } if ((range && st->code == 206) || (!range && st->klass == 2)) { ret = ne_read_response_to_buf(req, buf, bytes_read); } else { ret = ne_discard_response(req); } if (ret == NE_OK) ret = ne_end_request(req); } while (ret == NE_RETRY); return ret; }
/* Continue a GSS-API Negotiate exchange, using input TOKEN if * non-NULL. Returns non-zero on error. */ static int continue_negotiate(auth_session *sess, const char *token) { unsigned int major, minor; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; unsigned char *bintoken = NULL; int ret; if (token) { input.length = ne_unbase64(token, &bintoken); if (input.length == 0) { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Invalid input [%s].\n", token); return -1; } input.value = bintoken; NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Continuation token [%s]\n", token); } else if (sess->gssctx != GSS_C_NO_CONTEXT) { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Reset incomplete context.\n"); gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER); } major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &sess->gssctx, sess->gssname, sess->gssmech, GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &input, &sess->gssmech, &output, NULL, NULL); /* done with the input token. */ if (bintoken) ne_free(bintoken); if (GSS_ERROR(major)) { ne_buffer *err = ne_buffer_create(); int flag = 0; make_gss_error(err, &flag, major, GSS_C_GSS_CODE); make_gss_error(err, &flag, minor, GSS_C_MECH_CODE); NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Error: %s\n", err->data); ne_set_error(sess->sess, _("GSSAPI authentication error (%s)"), err->data); ne_buffer_destroy(err); return -1; } if (major == GSS_S_CONTINUE_NEEDED || major == GSS_S_COMPLETE) { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: init_sec_context OK. (major=%d)\n", major); ret = 0; } else { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Init failure %d.\n", major); ret = -1; } if (major != GSS_S_CONTINUE_NEEDED) { /* context no longer needed: destroy it */ gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER); } if (output.length) { sess->gssapi_token = ne_base64(output.value, output.length); NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Output token: [%s]\n", sess->gssapi_token); gss_release_buffer(&minor, &output); } else { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: No output token.\n"); } return ret; }
static int ah_post_send(ne_request *req, void *cookie, const ne_status *status) { auth_session *sess = cookie; struct auth_request *areq = ne_get_request_private(req, sess->spec->id); const char *auth_hdr, *auth_info_hdr; int ret = NE_OK; if (!areq) return NE_OK; auth_hdr = ne_get_response_header(req, sess->spec->resp_hdr); auth_info_hdr = ne_get_response_header(req, sess->spec->resp_info_hdr); if (sess->context == AUTH_CONNECT && status->code == 401 && !auth_hdr) { /* Some broken proxies issue a 401 as a proxy auth challenge * to a CONNECT request; handle this here. */ auth_hdr = ne_get_response_header(req, "WWW-Authenticate"); auth_info_hdr = NULL; } #ifdef HAVE_GSSAPI /* whatever happens: forget the GSSAPI token cached thus far */ if (sess->gssapi_token) { ne_free(sess->gssapi_token); sess->gssapi_token = NULL; } #endif NE_DEBUG(NE_DBG_HTTPAUTH, "ah_post_send (#%d), code is %d (want %d), %s is %s\n", sess->attempt, status->code, sess->spec->status_code, sess->spec->resp_hdr, auth_hdr ? auth_hdr : "(none)"); if (auth_info_hdr && sess->scheme == auth_scheme_digest) { if (verify_digest_response(areq, sess, auth_info_hdr)) { NE_DEBUG(NE_DBG_HTTPAUTH, "Response authentication invalid.\n"); ne_set_error(sess->sess, "%s", _(sess->spec->fail_msg)); ret = NE_ERROR; } } #ifdef HAVE_GSSAPI /* one must wonder... has Mr Brezak actually read RFC2617? */ else if (sess->scheme == auth_scheme_gssapi && (status->klass == 2 || status->klass == 3) && auth_hdr) { char *hdr = ne_strdup(auth_hdr); if (verify_negotiate_response(sess, hdr)) { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Mutual auth failed.\n"); ret = NE_ERROR; } ne_free(hdr); } #endif /* HAVE_GSSAPI */ else if ((status->code == sess->spec->status_code || (status->code == 401 && sess->context == AUTH_CONNECT)) && auth_hdr) { /* note above: allow a 401 in response to a CONNECT request * from a proxy since some buggy proxies send that. */ NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge (code %d).\n", status->code); if (!auth_challenge(sess, auth_hdr)) { ret = NE_RETRY; } else { clean_session(sess); ret = sess->spec->fail_code; } } #ifdef HAVE_SSPI else if (sess->sspi_context) { ne_sspi_clear_context(sess->sspi_context); } #endif return ret; }
/* Callback which is passed blocks of the response body. */ static int gz_reader(void *ud, const char *buf, size_t len) { ne_decompress *ctx = ud; const char *zbuf; size_t count; const char *hdr; if (len == 0) { /* End of response: */ switch (ctx->state) { case NE_Z_BEFORE_DATA: hdr = ne_get_response_header(ctx->request, "Content-Encoding"); if (hdr && strcasecmp(hdr, "gzip") == 0) { /* response was truncated: return error. */ break; } /* else, fall through */ case NE_Z_FINISHED: /* complete gzip response */ case NE_Z_PASSTHROUGH: /* complete uncompressed response */ return ctx->reader(ctx->userdata, buf, 0); default: /* invalid state: truncated response. */ break; } /* else: truncated response, fail. */ ne_set_error(ctx->session, "Compressed response was truncated"); return NE_ERROR; } switch (ctx->state) { case NE_Z_PASSTHROUGH: /* move along there. */ return ctx->reader(ctx->userdata, buf, len); case NE_Z_FINISHED: /* Could argue for tolerance, and ignoring trailing content; * but it could mean something more serious. */ if (len > 0) { ne_set_error(ctx->session, "Unexpected content received after compressed stream"); return NE_ERROR; } break; case NE_Z_BEFORE_DATA: /* work out whether this is a compressed response or not. */ hdr = ne_get_response_header(ctx->request, "Content-Encoding"); if (hdr && strcasecmp(hdr, "gzip") == 0) { int ret; NE_DEBUG(NE_DBG_HTTP, "compress: got gzipped stream.\n"); /* inflateInit2() works here where inflateInit() doesn't. */ ret = inflateInit2(&ctx->zstr, -MAX_WBITS); if (ret != Z_OK) { set_zlib_error(ctx, _("Could not initialize zlib"), ret); return -1; } ctx->zstrinit = 1; } else { /* No Content-Encoding header: pass it on. TODO: we could * hack it and register the real callback now. But that * would require add_resp_body_rdr to have defined * ordering semantics etc etc */ ctx->state = NE_Z_PASSTHROUGH; return ctx->reader(ctx->userdata, buf, len); } ctx->state = NE_Z_IN_HEADER; /* FALLTHROUGH */ case NE_Z_IN_HEADER: /* copy as many bytes as possible into the buffer. */ if (len + ctx->hdrcount > 10) { count = 10 - ctx->hdrcount; } else { count = len; } memcpy(ctx->header + ctx->hdrcount, buf, count); ctx->hdrcount += count; /* have we got the full header yet? */ if (ctx->hdrcount != 10) { return 0; } buf += count; len -= count; switch (parse_header(ctx)) { case HDR_EXTENDED: if (len == 0) return 0; break; case HDR_ERROR: return NE_ERROR; case HDR_DONE: if (len > 0) { return do_inflate(ctx, buf, len); } break; } /* FALLTHROUGH */ case NE_Z_POST_HEADER: /* eating the filename string. */ zbuf = memchr(buf, '\0', len); if (zbuf == NULL) { /* not found it yet. */ return 0; } NE_DEBUG(NE_DBG_HTTP, "compresss: skipped %" NE_FMT_SIZE_T " header bytes.\n", zbuf - buf); /* found end of string. */ len -= (1 + zbuf - buf); buf = zbuf + 1; ctx->state = NE_Z_INFLATING; if (len == 0) { /* end of string was at end of buffer. */ return 0; } /* FALLTHROUGH */ case NE_Z_INFLATING: return do_inflate(ctx, buf, len); case NE_Z_AFTER_DATA: return process_footer(ctx, (unsigned char *)buf, len); } return 0; }
int ne_lock(ne_session *sess, struct ne_lock *lock) { ne_request *req = ne_request_create(sess, "LOCK", lock->uri.path); ne_buffer *body = ne_buffer_create(); ne_xml_parser *parser = ne_xml_create(); int ret, parse_failed; struct lock_ctx ctx; memset(&ctx, 0, sizeof ctx); ctx.cdata = ne_buffer_create(); ctx.req = req; ne_xml_push_handler(parser, lk_startelm, lk_cdata, lk_endelm, &ctx); /* Create the body */ ne_buffer_concat(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL "<lockinfo xmlns='DAV:'>" EOL " <lockscope>", lock->scope==ne_lockscope_exclusive? "<exclusive/>":"<shared/>", "</lockscope>" EOL "<locktype><write/></locktype>", NULL); if (lock->owner) { ne_buffer_concat(body, "<owner>", lock->owner, "</owner>" EOL, NULL); } ne_buffer_zappend(body, "</lockinfo>" EOL); ne_set_request_body_buffer(req, body->data, ne_buffer_size(body)); /* ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE); */ /* Just to test whether sever accepts both text/xml and application/xml */ ne_add_request_header(req, "Content-Type", "text/xml"); ne_add_depth_header(req, lock->depth); add_timeout_header(req, lock->timeout); /* TODO: * By 2518, we need this only if we are creating a lock-null resource. * Since we don't KNOW whether the lock we're given is a lock-null * or not, we cover our bases. */ ne_lock_using_parent(req, lock->uri.path); /* This one is clearer from 2518 sec 8.10.4. */ ne_lock_using_resource(req, lock->uri.path, lock->depth); ret = ne_xml_dispatch_request(req, parser); ne_buffer_destroy(body); ne_buffer_destroy(ctx.cdata); parse_failed = ne_xml_failed(parser); if (ret == NE_OK && ne_get_status(req)->klass == 2) { if (ctx.token == NULL) { ret = NE_ERROR; ne_set_error(sess, _("No Lock-Token header given")); } else if (parse_failed) { ret = NE_ERROR; ne_set_error(sess, "%s", ne_xml_get_error(parser)); } else if (ne_get_status(req)->code == 207) { ret = NE_ERROR; /* TODO: set the error string appropriately */ } else if (ctx.found) { /* it worked: copy over real lock details if given. */ if (lock->token) ne_free(lock->token); lock->token = ctx.token; ctx.token = NULL; if (ctx.active.timeout != NE_TIMEOUT_INVALID) lock->timeout = ctx.active.timeout; lock->scope = ctx.active.scope; lock->type = ctx.active.type; if (ctx.active.depth >= 0) lock->depth = ctx.active.depth; if (ctx.active.owner) { if (lock->owner) ne_free(lock->owner); lock->owner = ctx.active.owner; ctx.active.owner = NULL; } } else { ret = NE_ERROR; ne_set_error(sess, _("Response missing activelock for %s"), ctx.token); } } else if (ret == NE_OK /* && status != 2xx */) { ret = NE_ERROR; } ne_lock_free(&ctx.active); if (ctx.token) ne_free(ctx.token); ne_request_destroy(req); ne_xml_destroy(parser); return ret; }