blob: ea3a1ac2edfe1a762464bd2dc37fdba35f68e9b4 [file] [log] [blame]
#include "apreq_xs_tables.h"
MP_STATIC XS(apreq_xs_jar)
{
dXSARGS;
apreq_handle_t *req;
SV *obj;
IV iv;
if (items == 0 || items > 2 || !SvROK(ST(0))
|| !sv_derived_from(ST(0), "APR::Request"))
Perl_croak(aTHX_ "Usage: APR::Request::jar($req [,$name])");
obj = apreq_xs_sv2object(aTHX_ ST(0), HANDLE_CLASS, 'r');
iv = SvIVX(obj);
req = INT2PTR(apreq_handle_t *, iv);
if (items == 2 && GIMME_V == G_SCALAR) {
apreq_cookie_t *c = apreq_jar_get(req, SvPV_nolen(ST(1)));
if (c != NULL) {
ST(0) = apreq_xs_cookie2sv(aTHX_ c, NULL, obj);
sv_2mortal(ST(0));
XSRETURN(1);
}
else {
const apr_table_t *t;
apr_status_t s;
s = apreq_jar(req, &t);
if (apreq_module_status_is_error(s)
&& !sv_derived_from(ST(0), ERROR_CLASS))
apreq_xs_croak(aTHX_ newHV(), obj, s,
"APR::Request::jar", ERROR_CLASS);
XSRETURN_UNDEF;
}
}
else {
struct apreq_xs_do_arg d = {NULL, NULL, NULL, aTHX};
const apr_table_t *t;
apr_status_t s;
s = apreq_jar(req, &t);
if (apreq_module_status_is_error(s)
&& !sv_derived_from(ST(0), ERROR_CLASS))
apreq_xs_croak(aTHX_ newHV(), obj, s,
"APR::Request::jar", ERROR_CLASS);
if (t == NULL)
XSRETURN_EMPTY;
d.pkg = NULL;
d.parent = obj;
switch (GIMME_V) {
case G_ARRAY:
XSprePUSH;
PUTBACK;
if (items == 1)
apr_table_do(apreq_xs_cookie_table_keys, &d, t, NULL);
else
apr_table_do(apreq_xs_cookie_table_values, &d, t,
SvPV_nolen(ST(1)), NULL);
return;
case G_SCALAR:
ST(0) = apreq_xs_cookie_table2sv(aTHX_ t,
COOKIE_TABLE_CLASS,
obj, NULL, 0);
sv_2mortal(ST(0));
XSRETURN(1);
default:
XSRETURN(0);
}
}
}
MP_STATIC XS(apreq_xs_args)
{
dXSARGS;
apreq_handle_t *req;
SV *obj;
IV iv;
if (items == 0 || items > 2 || !SvROK(ST(0))
|| !sv_derived_from(ST(0), HANDLE_CLASS))
Perl_croak(aTHX_ "Usage: APR::Request::args($req [,$name])");
obj = apreq_xs_sv2object(aTHX_ ST(0), HANDLE_CLASS, 'r');
iv = SvIVX(obj);
req = INT2PTR(apreq_handle_t *, iv);
if (items == 2 && GIMME_V == G_SCALAR) {
apreq_param_t *p = apreq_args_get(req, SvPV_nolen(ST(1)));
if (p != NULL) {
ST(0) = apreq_xs_param2sv(aTHX_ p, NULL, obj);
sv_2mortal(ST(0));
XSRETURN(1);
}
else {
const apr_table_t *t;
apr_status_t s;
s = apreq_args(req, &t);
if (apreq_module_status_is_error(s) &&
!sv_derived_from(ST(0), ERROR_CLASS))
apreq_xs_croak(aTHX_ newHV(), obj, s,
"APR::Request::args", ERROR_CLASS);
XSRETURN_UNDEF;
}
}
else {
struct apreq_xs_do_arg d = {NULL, NULL, NULL, aTHX};
const apr_table_t *t;
apr_status_t s;
s = apreq_args(req, &t);
if (apreq_module_status_is_error(s) &&
!sv_derived_from(ST(0), ERROR_CLASS))
apreq_xs_croak(aTHX_ newHV(), obj, s,
"APR::Request::args", ERROR_CLASS);
if (t == NULL)
XSRETURN_EMPTY;
d.pkg = NULL;
d.parent = obj;
switch (GIMME_V) {
case G_ARRAY:
XSprePUSH;
PUTBACK;
if (items == 1)
apr_table_do(apreq_xs_param_table_keys, &d, t, NULL);
else
apr_table_do(apreq_xs_param_table_values, &d, t,
SvPV_nolen(ST(1)), NULL);
return;
case G_SCALAR:
ST(0) = apreq_xs_param_table2sv(aTHX_ t,
PARAM_TABLE_CLASS,
obj, NULL, 0);
sv_2mortal(ST(0));
XSRETURN(1);
default:
XSRETURN(0);
}
}
}
MP_STATIC XS(apreq_xs_body)
{
dXSARGS;
apreq_handle_t *req;
SV *obj;
IV iv;
if (items == 0 || items > 2 || !SvROK(ST(0))
|| !sv_derived_from(ST(0),HANDLE_CLASS))
Perl_croak(aTHX_ "Usage: APR::Request::body($req [,$name])");
obj = apreq_xs_sv2object(aTHX_ ST(0), HANDLE_CLASS, 'r');
iv = SvIVX(obj);
req = INT2PTR(apreq_handle_t *, iv);
if (items == 2 && GIMME_V == G_SCALAR) {
apreq_param_t *p = apreq_body_get(req, SvPV_nolen(ST(1)));
if (p != NULL) {
ST(0) = apreq_xs_param2sv(aTHX_ p, NULL, obj);
sv_2mortal(ST(0));
XSRETURN(1);
}
else {
const apr_table_t *t;
apr_status_t s;
s = apreq_body(req, &t);
if (apreq_module_status_is_error(s) &&
!sv_derived_from(ST(0), ERROR_CLASS))
apreq_xs_croak(aTHX_ newHV(), obj, s,
"APR::Request::body", ERROR_CLASS);
XSRETURN_UNDEF;
}
}
else {
struct apreq_xs_do_arg d = {NULL, NULL, NULL, aTHX};
const apr_table_t *t;
apr_status_t s;
s = apreq_body(req, &t);
if (apreq_module_status_is_error(s) &&
!sv_derived_from(ST(0), ERROR_CLASS))
apreq_xs_croak(aTHX_ newHV(), obj, s,
"APR::Request::body", ERROR_CLASS);
if (t == NULL)
XSRETURN_EMPTY;
d.pkg = NULL;
d.parent = obj;
switch (GIMME_V) {
case G_ARRAY:
XSprePUSH;
PUTBACK;
if (items == 1)
apr_table_do(apreq_xs_param_table_keys, &d, t, NULL);
else
apr_table_do(apreq_xs_param_table_values, &d, t,
SvPV_nolen(ST(1)), NULL);
return;
case G_SCALAR:
ST(0) = apreq_xs_param_table2sv(aTHX_ t,
PARAM_TABLE_CLASS,
obj, NULL, 0);
sv_2mortal(ST(0));
XSRETURN(1);
default:
XSRETURN(0);
}
}
}
MP_STATIC XS(apreq_xs_param)
{
dXSARGS;
apreq_handle_t *req;
SV *obj;
IV iv;
if (items == 0 || items > 2 || !SvROK(ST(0))
|| !sv_derived_from(ST(0), "APR::Request"))
Perl_croak(aTHX_ "Usage: APR::Request::param($req [,$name])");
obj = apreq_xs_sv2object(aTHX_ ST(0), HANDLE_CLASS, 'r');
iv = SvIVX(obj);
req = INT2PTR(apreq_handle_t *, iv);
if (items == 2 && GIMME_V == G_SCALAR) {
apreq_param_t *p = apreq_param(req, SvPV_nolen(ST(1)));
if (p != NULL) {
ST(0) = apreq_xs_param2sv(aTHX_ p, NULL, obj);
sv_2mortal(ST(0));
XSRETURN(1);
}
else {
XSRETURN_UNDEF;
}
}
else {
struct apreq_xs_do_arg d = {NULL, NULL, NULL, aTHX};
const apr_table_t *t;
d.pkg = NULL;
d.parent = obj;
switch (GIMME_V) {
case G_ARRAY:
XSprePUSH;
PUTBACK;
if (items == 1) {
apreq_args(req, &t);
if (t != NULL)
apr_table_do(apreq_xs_param_table_keys, &d, t, NULL);
apreq_body(req, &t);
if (t != NULL)
apr_table_do(apreq_xs_param_table_keys, &d, t, NULL);
}
else {
char *val = SvPV_nolen(ST(1));
apreq_args(req, &t);
if (t != NULL)
apr_table_do(apreq_xs_param_table_values, &d, t, val, NULL);
apreq_body(req, &t);
if (t != NULL)
apr_table_do(apreq_xs_param_table_values, &d, t, val, NULL);
}
return;
case G_SCALAR:
t = apreq_params(req, req->pool);
if (t == NULL)
XSRETURN_UNDEF;
ST(0) = apreq_xs_param_table2sv(aTHX_ t,
PARAM_TABLE_CLASS,
obj, NULL, 0);
sv_2mortal(ST(0));
XSRETURN(1);
default:
XSRETURN(0);
}
}
}
MP_STATIC XS(apreq_xs_parse)
{
dXSARGS;
apreq_handle_t *req;
apr_status_t s;
const apr_table_t *t;
if (items != 1 || !SvROK(ST(0)))
Perl_croak(aTHX_ "Usage: APR::Request::parse($req)");
req = apreq_xs_sv2handle(aTHX_ ST(0));
XSprePUSH;
EXTEND(SP, 3);
s = apreq_jar(req, &t);
PUSHs(sv_2mortal(apreq_xs_error2sv(aTHX_ s)));
s = apreq_args(req, &t);
PUSHs(sv_2mortal(apreq_xs_error2sv(aTHX_ s)));
s = apreq_body(req, &t);
PUSHs(sv_2mortal(apreq_xs_error2sv(aTHX_ s)));
PUTBACK;
}
struct hook_ctx {
SV *hook;
SV *bucket_data;
SV *parent;
PerlInterpreter *perl;
};
#define DEREF(slot) if (ctx->slot) SvREFCNT_dec(ctx->slot)
static apr_status_t upload_hook_cleanup(void *ctx_)
{
struct hook_ctx *ctx = ctx_;
#ifdef USE_ITHREADS
dTHXa(ctx->perl);
#endif
DEREF(hook);
DEREF(bucket_data);
DEREF(parent);
return APR_SUCCESS;
}
APR_INLINE
static apr_status_t eval_upload_hook(pTHX_ apreq_param_t *upload,
struct hook_ctx *ctx)
{
dSP;
SV *sv = ctx->bucket_data;
PUSHMARK(SP);
EXTEND(SP, 2);
ENTER;
SAVETMPS;
sv = apreq_xs_param2sv(aTHX_ upload, PARAM_CLASS, ctx->parent);
PUSHs(sv_2mortal(sv));
PUSHs(ctx->bucket_data);
PUTBACK;
perl_call_sv(ctx->hook, G_EVAL|G_DISCARD);
FREETMPS;
LEAVE;
if (SvTRUE(ERRSV)) {
Perl_warn(aTHX_ "Upload hook failed: %s", SvPV_nolen(ERRSV));
return APREQ_ERROR_GENERAL;
}
return APR_SUCCESS;
}
static apr_status_t apreq_xs_upload_hook(APREQ_HOOK_ARGS)
{
struct hook_ctx *ctx = hook->ctx; /* ctx set during $req->config */
apr_bucket *e;
apr_status_t s = APR_SUCCESS;
#ifdef USE_ITHREADS
dTHXa(ctx->perl);
#endif
if (bb == NULL) {
if (hook->next)
return apreq_hook_run(hook->next, param, bb);
return APR_SUCCESS;
}
for (e = APR_BRIGADE_FIRST(bb); e!= APR_BRIGADE_SENTINEL(bb);
e = APR_BUCKET_NEXT(e))
{
apr_size_t len;
const char *data;
if (APR_BUCKET_IS_EOS(e)) { /*last call on this upload */
SV *sv = ctx->bucket_data;
ctx->bucket_data = &PL_sv_undef;
s = eval_upload_hook(aTHX_ param, ctx);
ctx->bucket_data = sv;
if (s != APR_SUCCESS)
return s;
break;
}
s = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
if (s != APR_SUCCESS) {
s = APR_SUCCESS;
continue;
}
sv_setpvn(ctx->bucket_data, data, (STRLEN)len);
s = eval_upload_hook(aTHX_ param, ctx);
if (s != APR_SUCCESS)
return s;
}
if (hook->next)
s = apreq_hook_run(hook->next, param, bb);
return s;
}
static int apreq_xs_cookie_table_do_sub(void *data, const char *key,
const char *val)
{
struct apreq_xs_do_arg *d = data;
apreq_cookie_t *c = apreq_value_to_cookie(val);
dTHXa(d->perl);
dSP;
SV *sv = apreq_xs_cookie2sv(aTHX_ c, d->pkg, d->parent);
int rv;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP,2);
PUSHs(sv_2mortal(newSVpvn(c->v.name, c->v.nlen)));
PUSHs(sv_2mortal(sv));
PUTBACK;
rv = call_sv(d->sub, G_SCALAR);
SPAGAIN;
rv = (1 == rv) ? POPi : 1;
PUTBACK;
FREETMPS;
LEAVE;
return rv;
}
MP_STATIC XS(apreq_xs_cookie_table_do)
{
dXSARGS;
struct apreq_xs_do_arg d = { NULL, NULL, NULL, aTHX };
const apr_table_t *t;
int i, rv = 1;
SV *sv, *t_obj;
IV iv;
MAGIC *mg;
if (items < 2 || !SvROK(ST(0)) || !SvROK(ST(1)))
Perl_croak(aTHX_ "Usage: $object->do(\\&callback, @keys)");
sv = ST(0);
t_obj = apreq_xs_sv2object(aTHX_ sv, COOKIE_TABLE_CLASS, 't');
iv = SvIVX(t_obj);
t = INT2PTR(const apr_table_t *, iv);
mg = mg_find(t_obj, PERL_MAGIC_ext);
d.parent = mg->mg_obj;
d.pkg = mg->mg_ptr;
d.sub = ST(1);
if (items == 2) {
rv = apr_table_do(apreq_xs_cookie_table_do_sub, &d, t, NULL);
XSRETURN_IV(rv);
}
for (i = 2; i < items; ++i) {
const char *key = SvPV_nolen(ST(i));
rv = apr_table_do(apreq_xs_cookie_table_do_sub, &d, t, key, NULL);
if (rv == 0)
break;
}
XSRETURN_IV(rv);
}
MP_STATIC XS(apreq_xs_cookie_table_FETCH)
{
dXSARGS;
const apr_table_t *t;
const char *cookie_class;
SV *sv, *obj, *parent;
IV iv;
MAGIC *mg;
if (items != 2 || !SvROK(ST(0))
|| !sv_derived_from(ST(0), COOKIE_TABLE_CLASS))
Perl_croak(aTHX_ "Usage: " COOKIE_TABLE_CLASS "::FETCH($table, $key)");
sv = ST(0);
obj = apreq_xs_sv2object(aTHX_ sv, COOKIE_TABLE_CLASS, 't');
iv = SvIVX(obj);
t = INT2PTR(const apr_table_t *, iv);
mg = mg_find(obj, PERL_MAGIC_ext);
cookie_class = mg->mg_ptr;
parent = mg->mg_obj;
if (GIMME_V == G_SCALAR) {
IV idx;
const char *key, *val;
const apr_array_header_t *arr;
apr_table_entry_t *te;
key = SvPV_nolen(ST(1));
idx = SvCUR(obj);
arr = apr_table_elts(t);
te = (apr_table_entry_t *)arr->elts;
if (idx > 0 && idx <= arr->nelts
&& !strcasecmp(key, te[idx-1].key))
val = te[idx-1].val;
else
val = apr_table_get(t, key);
if (val != NULL) {
apreq_cookie_t *c = apreq_value_to_cookie(val);
ST(0) = apreq_xs_cookie2sv(aTHX_ c, cookie_class, parent);
sv_2mortal(ST(0));
XSRETURN(1);
}
else {
XSRETURN_UNDEF;
}
}
else if (GIMME_V == G_ARRAY) {
struct apreq_xs_do_arg d = {NULL, NULL, NULL, aTHX};
d.pkg = cookie_class;
d.parent = parent;
XSprePUSH;
PUTBACK;
apr_table_do(apreq_xs_cookie_table_values, &d, t, SvPV_nolen(ST(1)), NULL);
}
else
XSRETURN(0);
}
MP_STATIC XS(apreq_xs_cookie_table_NEXTKEY)
{
dXSARGS;
SV *sv, *obj;
IV iv, idx;
const apr_table_t *t;
const apr_array_header_t *arr;
apr_table_entry_t *te;
if (!SvROK(ST(0)))
Perl_croak(aTHX_ "Usage: $table->NEXTKEY($prev)");
sv = ST(0);
obj = apreq_xs_sv2object(aTHX_ sv, COOKIE_TABLE_CLASS, 't');
iv = SvIVX(obj);
t = INT2PTR(const apr_table_t *, iv);
arr = apr_table_elts(t);
te = (apr_table_entry_t *)arr->elts;
if (items == 1)
SvCUR(obj) = 0;
if (SvCUR(obj) >= arr->nelts) {
SvCUR(obj) = 0;
XSRETURN_UNDEF;
}
idx = SvCUR(obj)++;
sv = newSVpv(te[idx].key, 0);
ST(0) = sv_2mortal(sv);
XSRETURN(1);
}
static int apreq_xs_param_table_do_sub(void *data, const char *key,
const char *val)
{
struct apreq_xs_do_arg *d = data;
apreq_param_t *p = apreq_value_to_param(val);
dTHXa(d->perl);
dSP;
SV *sv = apreq_xs_param2sv(aTHX_ p, d->pkg, d->parent);
int rv;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP,2);
PUSHs(sv_2mortal(newSVpvn(p->v.name, p->v.nlen)));
PUSHs(sv_2mortal(sv));
PUTBACK;
rv = call_sv(d->sub, G_SCALAR);
SPAGAIN;
rv = (1 == rv) ? POPi : 1;
PUTBACK;
FREETMPS;
LEAVE;
return rv;
}
MP_STATIC XS(apreq_xs_param_table_do)
{
dXSARGS;
struct apreq_xs_do_arg d = { NULL, NULL, NULL, aTHX };
const apr_table_t *t;
int i, rv = 1;
SV *sv, *t_obj;
IV iv;
MAGIC *mg;
if (items < 2 || !SvROK(ST(0)) || !SvROK(ST(1)))
Perl_croak(aTHX_ "Usage: $object->do(\\&callback, @keys)");
sv = ST(0);
t_obj = apreq_xs_sv2object(aTHX_ sv, PARAM_TABLE_CLASS, 't');
iv = SvIVX(t_obj);
t = INT2PTR(const apr_table_t *, iv);
mg = mg_find(t_obj, PERL_MAGIC_ext);
d.parent = mg->mg_obj;
d.pkg = mg->mg_ptr;
d.sub = ST(1);
if (items == 2) {
rv = apr_table_do(apreq_xs_param_table_do_sub, &d, t, NULL);
XSRETURN_IV(rv);
}
for (i = 2; i < items; ++i) {
const char *key = SvPV_nolen(ST(i));
rv = apr_table_do(apreq_xs_param_table_do_sub, &d, t, key, NULL);
if (rv == 0)
break;
}
XSRETURN_IV(rv);
}
MP_STATIC XS(apreq_xs_param_table_FETCH)
{
dXSARGS;
const apr_table_t *t;
const char *param_class;
SV *sv, *t_obj, *parent;
IV iv;
MAGIC *mg;
if (items != 2 || !SvROK(ST(0))
|| !sv_derived_from(ST(0), PARAM_TABLE_CLASS))
Perl_croak(aTHX_ "Usage: " PARAM_TABLE_CLASS "::FETCH($table, $key)");
sv = ST(0);
t_obj = apreq_xs_sv2object(aTHX_ sv, PARAM_TABLE_CLASS, 't');
iv = SvIVX(t_obj);
t = INT2PTR(const apr_table_t *, iv);
mg = mg_find(t_obj, PERL_MAGIC_ext);
param_class = mg->mg_ptr;
parent = mg->mg_obj;
if (GIMME_V == G_SCALAR) {
IV idx;
const char *key, *val;
const apr_array_header_t *arr;
apr_table_entry_t *te;
key = SvPV_nolen(ST(1));
idx = SvCUR(t_obj);
arr = apr_table_elts(t);
te = (apr_table_entry_t *)arr->elts;
if (idx > 0 && idx <= arr->nelts
&& !strcasecmp(key, te[idx-1].key))
val = te[idx-1].val;
else
val = apr_table_get(t, key);
if (val != NULL) {
apreq_param_t *p = apreq_value_to_param(val);
ST(0) = apreq_xs_param2sv(aTHX_ p, param_class, parent);
sv_2mortal(ST(0));
XSRETURN(1);
}
else {
XSRETURN_UNDEF;
}
}
else if (GIMME_V == G_ARRAY) {
struct apreq_xs_do_arg d = {NULL, NULL, NULL, aTHX};
d.pkg = param_class;
d.parent = parent;
XSprePUSH;
PUTBACK;
apr_table_do(apreq_xs_param_table_values, &d, t, SvPV_nolen(ST(1)), NULL);
}
else
XSRETURN(0);
}
MP_STATIC XS(apreq_xs_param_table_NEXTKEY)
{
dXSARGS;
SV *sv, *obj;
IV iv, idx;
const apr_table_t *t;
const apr_array_header_t *arr;
apr_table_entry_t *te;
if (!SvROK(ST(0)) || !sv_derived_from(ST(0), PARAM_TABLE_CLASS))
Perl_croak(aTHX_ "Usage: " PARAM_TABLE_CLASS "::NEXTKEY($table, $key)");
sv = ST(0);
obj = apreq_xs_sv2object(aTHX_ sv, PARAM_TABLE_CLASS,'t');
iv = SvIVX(obj);
t = INT2PTR(const apr_table_t *, iv);
arr = apr_table_elts(t);
te = (apr_table_entry_t *)arr->elts;
if (items == 1)
SvCUR(obj) = 0;
if (SvCUR(obj) >= arr->nelts) {
SvCUR(obj) = 0;
XSRETURN_UNDEF;
}
idx = SvCUR(obj)++;
sv = newSVpv(te[idx].key, 0);
ST(0) = sv_2mortal(sv);
XSRETURN(1);
}