libcgi-cookie-xs-perl/XS.xs
2017-09-15 15:16:18 +02:00

193 lines
4.3 KiB
Plaintext

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "INLINE.h"
#define DDD(x)
#ifndef DDD
#define DDD(x) fprintf(stderr, "%s\n", x);
#endif
#define COOKIE_LEN_LIMIT 1024 * 4
#ifndef NULL
#define NULL (void*)0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef BOOL
#define BOOL short int
#endif
//static char *encode_hex_str(const char*, char **);
extern char** XS_unpack_charPtrPtr(SV* arg);
extern void XS_pack_charPtrPtr( SV* arg, char** array, int count);
char Buffer[COOKIE_LEN_LIMIT];
static int _decode_hex_str(const char*, char **);
SV* _parse_cookie(char* cs) {
int i, value_flag;
char* p; /* moving first for look-ahead */
char* q; /* moving slower for tracking values */
char* decode;
AV *array = NULL;
HV *hash = NULL;
BOOL parsing_value = FALSE;
decode = (char *) malloc (COOKIE_LEN_LIMIT * sizeof(decode));
if (decode == NULL) {
croak("CGI::Cookie::XS::parse - Failed to malloc");
}
strncpy(Buffer, cs, COOKIE_LEN_LIMIT);
Buffer[COOKIE_LEN_LIMIT-1] = '\0';
hash = newHV();
p = Buffer;
DDD("before loop");
while (*p == ' ' || *p == '\t') p++; // remove leading spaces
q = p;
while (*p) {
//DDD("in loop");
if (*p == '=' && !parsing_value ){
array = newAV();
*p = '\0';
// Only move on if not the end of the cookie value
if (*(p+1) != ';' && *(p+1) != ',' && *(p+1) != '\0')
p++;
_decode_hex_str(q, &decode);
q = p;
hv_store(
hash, decode, strlen(decode), newRV_noinc((SV *)array), 0
);
//array = NULL;
parsing_value = TRUE;
} else if (*p == ';' || *p == ',') {
*p = '\0';
p++;
while (*p == ' ')
p++;
_decode_hex_str(q, &decode);
q = p;
if (*decode != '\0' && parsing_value && array != NULL)
av_push(array, newSVpvf("%s", decode));
parsing_value = FALSE;
} else if (*p == '&') { // find a second value
*p = 0; p++;
_decode_hex_str(q, &decode);
q = p;
if (parsing_value && array != NULL)
av_push(array, newSVpvf("%s", decode));
}
p++;
}
DDD("before decode");
if (*q != '\0' && parsing_value) {
_decode_hex_str(q, &decode);
DDD("before push array");
if (array != NULL)
av_push(array, newSVpvf("%s", decode));
DDD("after push array");
}
if (decode) free(decode);
DDD("before return");
return newRV_noinc((SV *) hash);
}
char *encode_hex_str(const char *str, char **out_buf)
{
static const char *verbatim = "-_.*";
static const char *hex = "0123456789ABCDEF";
char *newstr = *out_buf;
char *c;
if (!str && !newstr)
return NULL;
for (c = newstr; *str; str++)
if ((isalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str))
*c++ = *str;
else if (*str == ' ')
*c++ = '+';
else if (*str == '\n') {
*c++ = '%';
*c++ = '0';
*c++ = 'D';
*c++ = '%';
*c++ = '0';
*c++ = 'A';
} else {
*c++ = '%';
*c++ = hex[(*str >> 4) & 15];
*c++ = hex[*str & 15];
}
*c = 0;
return newstr;
}
static int decode_hex_octet(const char *s)
{
int hex_value;
char *tail, hex[3];
if (s && (hex[0] = s[0]) && (hex[1] = s[1])) {
hex[2] = 0;
hex_value = strtol(hex, &tail, 16);
if (tail - hex == 2)
return hex_value;
}
return -1;
}
int _decode_hex_str (const char *str, char **out)
{
char *dest = *out;
int i, val;
memset(dest, 0, COOKIE_LEN_LIMIT);
if (!str && ! dest)
return 0;
// most cases won't have hex octets
if (!strchr(str, '%')){
strcpy(dest, str);
return 1;
}
for (i = 0; str[i]; i++) {
*dest++ = (str[i] == '%' && (val = decode_hex_octet(str+i+1)) >= 0) ?
i+=2, val : str[i];
}
return 1;
}
MODULE = CGI::Cookie::XS PACKAGE = CGI::Cookie::XS
PROTOTYPES: DISABLE
SV *
_parse_cookie (cs)
char * cs
int
_decode_hex_str (str, out)
char * str
char ** out