/*
* Copyright (C) 2001, Shilad Sen, Sourcelight Technologies, Inc.
* See xmlrpc.h or the README for more copyright information.
*/
#include <assert.h>
#include <string.h>
#include "xmlrpc.h"
#include "rpcInternal.h"
#define BUFF_START 256
#define EOL "\r\n"
#define COM_BEG "<!-- "
#define COM_END " -->"
#define TAG_LEN(t) (sizeof(t)-1)
typedef struct {
char *beg; /* beginning of the string */
ulong len, /* length of the string */
all; /* length of allocated memory */
} strBuff;
static char *chompStr(char **cp, char *ep, ulong *lines);
static bool findTag(
char *tag,
char **cpp,
char *ep,
ulong *lines,
bool chomp
);
static bool findXmlVersion(char **cpp, char *ep, ulong *lines);
static PyObject *syntaxErr(ulong line);
static PyObject *eosErr(void);
static bool buildInt(char *cp, int slen, int *ip);
static PyObject *unescapeString(char *bp, char *ep);
static PyObject *escapeString(PyObject *oldStr);
static strBuff *newBuff(void);
static strBuff *growBuff(strBuff *sp, ulong moreBytes);
static void freeBuff(strBuff *sp);
static strBuff *buffConcat(strBuff *sp, char *cp);
static strBuff *buffAppend(strBuff *sp, char *cp, ulong len);
static strBuff *buffRepeat(strBuff *sp, char c, uint reps);
static strBuff *encodeValue(strBuff *sp, PyObject *value, uint tabs);
static strBuff *encodeBool(strBuff *sp, PyObject *value);
static strBuff *encodeInt(strBuff *sp, PyObject *value);
static strBuff *encodeDouble(strBuff *sp, PyObject *value);
static strBuff *encodeString(strBuff *sp, PyObject *value);
static strBuff *encodeBase64(strBuff *sp, PyObject *value);
static strBuff *encodeDate(strBuff *sp, PyObject *value);
static strBuff *encodeArray(strBuff *sp, PyObject *value, uint tabs);
static strBuff *encodeStruct(strBuff *sp, PyObject *value, uint tabs);
static PyObject *decodeValue(char **cp, char *ep, ulong *lines);
static PyObject *decodeInt(char **cp, char *ep, ulong *lines);
static PyObject *decodeI4(char **cp, char *ep, ulong *lines);
static PyObject *decodeDate(char **cp, char *eq, ulong *lines);
static PyObject *decodeDouble(char **cp, char *ep, ulong *lines);
static PyObject *decodeString(char **cp, char *ep, ulong *lines);
static PyObject *decodeTaglessString(char **cp, char *ep, ulong *lines);
static PyObject *decodeBool(char **cp, char *ep, ulong *lines);
static PyObject *decodeBase64(char **cp, char *ep, ulong *lines);
static PyObject *decodeArray(char **cp, char *ep, ulong *lines);
static PyObject *decodeStruct(char **cp, char *ep, ulong *lines);
static strBuff *buildHeader(
int reqType,
char *url,
PyObject *addInfo,
long len
);
static PyObject *parseFault(char *cp, char *ep, long lines);
static PyObject *parseHeader(
char **cpp,
char *ep,
ulong *lines,
int reqType
);
static bool parseHeaderLine(
PyObject *addInfo,
char **cp,
char *ep,
ulong *lines
);
bool parseParams (char **cpp, char *ep, ulong *lines, PyObject *params);
static strBuff *
newBuff(void)
{
strBuff *sp;
sp = alloc(sizeof(*sp));
if (sp == NULL)
return NULL;
sp->len = 0;
sp->all = BUFF_START;
sp->beg = alloc(sizeof(*sp->beg) * sp->all);
if (sp->beg == NULL)
return NULL;
memset(sp->beg, 0, sp->all);
return sp;
}
static strBuff *
growBuff(strBuff *sp, ulong moreBytes)
{
ulong nBytes;
if (sp->all > (sp->len + moreBytes + 1))
return sp;
else if (sp->all * 2 > (sp->len + moreBytes + 1))
nBytes = sp->all * 2;
else
nBytes = sp->all + moreBytes + 1;
sp->all = nBytes;
sp->beg = ralloc(sp->beg, sp->all);
if (sp->beg == NULL)
return NULL;
memset(sp->beg + sp->len, 0, sp->all - sp->len);
return sp;
}
static strBuff *
buffConcat(strBuff *sp, char *cp)
{
ulong l;
l = strlen(cp);
sp = growBuff(sp, l);
if (sp == NULL)
return NULL;
strcpy(sp->beg + sp->len, cp);
sp->len += l;
return sp;
}
static strBuff *
buffAppend(strBuff *sp, char *cp, ulong len)
{
sp = growBuff(sp, len);
if (sp == NULL)
return NULL;
memcpy(sp->beg + sp->len, cp, len);
sp->len += len;
return sp;
}
/* handy define to avoid useless calls to buffConcat when working with constant strings */
#define buffConstant(sp, x) buffAppend(sp, x, sizeof(x)-1)
static strBuff *
buffRepeat(strBuff *sp, char c, uint reps)
{
sp = growBuff(sp, reps);
if (sp == NULL)
return NULL;
memset(sp->beg + sp->len, c, reps);
sp->len += reps;
return sp;
}
static void
freeBuff(strBuff *sp)
{
if (sp->beg != NULL)
free(sp->beg);
free(sp);
}
PyObject *
xmlEncode(PyObject *value)
{
strBuff *sp;
PyObject *res;
sp = newBuff();
if (sp == NULL)
return NULL;
sp = encodeValue(sp, value, 0);
if (sp == NULL)
return NULL;
res = PyString_FromStringAndSize(sp->beg, sp->len);
freeBuff(sp);
return res;
}
static strBuff *
encodeValue(strBuff *sp, PyObject *value, uint tabs)
{
if (buffConstant(sp, "<value>") == NULL)
return NULL;
if (PyInt_Check(value) or PyLong_Check(value))
sp = encodeInt(sp, value);
else if (PyFloat_Check(value))
sp = encodeDouble(sp, value);
else if (value->ob_type == &rpcBoolType)
sp = encodeBool(sp, value);
else if (value->ob_type == &rpcDateType)
sp = encodeDate(sp, value);
else if (value->ob_type == &rpcBase64Type)
sp = encodeBase64(sp, value);
else if (PyString_Check(value))
sp = encodeString(sp, value);
else if (PyList_Check(value) or PyTuple_Check(value))
sp = encodeArray(sp, value, tabs);
else if (PyDict_Check(value))
sp = encodeStruct(sp, value, tabs);
else {
PyObject *str1,
*str2;
freeBuff(sp);
str1 = PyString_FromString("invalid object to encode: ");
str2 = PyObject_Repr(value);
if (str1 == NULL or str2 == NULL)
return NULL;
PyString_Concat(&str1, str2);
PyErr_SetString(rpcError, PyString_AS_STRING(str1));
Py_DECREF(str1);
Py_DECREF(str2);
return NULL;
}
if (sp == NULL)
return NULL;
sp = buffConstant(sp, "</value>");
return sp;
}
/*
* encode the int 4 (for example) as: "<int>4</int>"
* this could definitely be optimized, but I doubt it really hurts us
*/
static strBuff *
encodeInt(strBuff *sp, PyObject *value)
{
PyObject *strval;
strval = PyObject_Str(value);
if ((strval == NULL)
or (buffConstant(sp, "<int>") == NULL)
or (buffConcat(sp, PyString_AS_STRING(strval)) == NULL)
or (buffConstant(sp, "</int>") == NULL))
return NULL;
Py_DECREF(strval);
return sp;
}
/*
* encode the double 4.0 (for example) as: "<double>4.0</double>"
* i should fix the precision, but I'm not sure how to.
*/
static strBuff *
encodeDouble(strBuff *sp, PyObject *value)
{
char buff[256];
double d;
d = PyFloat_AS_DOUBLE(value);
snprintf(buff, 255, "%f", d);
if ((buffConstant(sp, "<double>") == NULL)
or (buffConcat(sp, buff) == NULL)
or (buffConstant(sp, "</double>") == NULL))
return NULL;
return sp;
}
/*
* encode the boolean true (for example) as: "<boolean>1</boolean>"
*/
static strBuff *
encodeBool(strBuff *sp, PyObject *value)
{
if (((rpcBool *)value)->value)
return buffConstant(sp, "<boolean>1</boolean>");
else
return buffConstant(sp, "<boolean>0</boolean>");
}
/*
* encode "Hello, World!" (for example) as: "<string>Hello, World!</string>"
*/
static strBuff *
encodeString(strBuff *sp, PyObject *value)
{
PyObject *escStr;
uint len;
char *cp;
escStr = escapeString(value);
if (escStr == NULL)
return NULL;
cp = PyString_AS_STRING(escStr);
len = PyObject_Length(escStr);
if ((buffConstant(sp, "<string>") == NULL)
or (buffAppend(sp, cp, len) == NULL)
or (buffConstant(sp, "</string>") == NULL))
return NULL;
Py_DECREF(escStr);
return sp;
}
/*
* encode the Base64 type
*/
static strBuff *
encodeBase64(strBuff *sp, PyObject *value)
{
char *str;
str = rpcBase64Encode(((rpcBase64 *)value)->value);
if (str == NULL)
return NULL;
if ((buffConstant(sp, "<base64>") == NULL)
or (buffAppend(sp, str, strlen(str)) == NULL)
or (buffConstant(sp, "</base64>") == NULL)) {
return NULL;
}
free(str);
return sp;
}
/*
* encode the Date type
*/
static strBuff *
encodeDate(strBuff *sp, PyObject *value)
{
int year,
month,
day,
hour,
min,
sec;
char add[6];
PyArg_ParseTuple(((rpcDate *)value)->value, "iiiiii",
&year, &month, &day, &hour, &min, &sec);
if (buffConstant(sp, "<dateTime.iso8601>") == NULL)
return (NULL);
if (year < 1000)
snprintf(add, 5, "0%d", year);
else
snprintf(add, 5, "%d", year);
if (buffConcat(sp, add) == NULL)
return (NULL);
if (month < 10)
snprintf(add, 5, "0%d", month);
else
snprintf(add, 5, "%d", month);
if (buffConcat(sp, add) == NULL)
return (NULL);
if (day < 10)
snprintf(add, 5, "0%d", day);
else
snprintf(add, 5, "%d", day);
if (buffConcat(sp, add) == NULL)
return (NULL);
if (buffConstant(sp, "T") == NULL)
return (NULL);
if (hour < 10)
snprintf(add, 5, "0%d:", hour);
else
snprintf(add, 5, "%d:", hour);
if (buffConcat(sp, add) == NULL)
return (NULL);
if (min < 10)
snprintf(add, 5, "0%d:", min);
else
snprintf(add, 5, "%d:", min);
if (buffConcat(sp, add) == NULL)
return (NULL);
if (sec < 10)
snprintf(add, 5, "0%d", sec);
else
snprintf(add, 5, "%d", sec);
if (buffConcat(sp, add) == NULL
or buffConstant(sp, "</dateTime.iso8601>") == NULL)
return (NULL);
return (sp);
}
/*
* encode an array in xml
* this could REALLY be optimized
*/
static strBuff *
encodeArray(strBuff *sp, PyObject *value, uint tabs)
{
PyObject *elem;
int i;
if ((buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs + 1) == NULL)
or (buffConstant(sp, "<array>") == NULL)
or (buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs + 2) == NULL)
or (buffConstant(sp, "<data>") == NULL)
or (buffConstant(sp, EOL) == NULL))
return NULL;
for (i = 0; i < PyObject_Length(value); ++i) {
elem = PySequence_GetItem(value, i);
if ((elem == NULL)
or (buffRepeat(sp, '\t', tabs + 3) == NULL))
return NULL;
if (encodeValue(sp, elem, tabs + 3) == NULL)
return NULL;
if ((buffConstant(sp, EOL) == NULL))
return NULL;
Py_DECREF(elem);
}
if ((buffRepeat(sp, '\t', tabs + 2) == NULL)
or (buffConstant(sp, "</data>") == NULL)
or (buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs + 1) == NULL)
or (buffConstant(sp, "</array>") == NULL)
or (buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs) == NULL))
return NULL;
return sp;
}
/*
* encode an array in xml
* this could REALLY be optimized
*/
static strBuff *
encodeStruct(strBuff *sp, PyObject *value, uint tabs)
{
PyObject *items,
*tup,
*name,
*val;
int i;
items = PyMapping_Items(value);
if ((items == NULL)
or (buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs + 1) == NULL)
or (buffConstant(sp, "<struct>") == NULL)
or (buffConstant(sp, EOL) == NULL))
return NULL;
for (i = 0; i < PyObject_Length(items); ++i) {
tup = PySequence_GetItem(items, i);
name = PySequence_GetItem(tup, 0);
val = PySequence_GetItem(tup, 1);
unless (PyString_Check(name)) {
Py_DECREF(tup);
Py_DECREF(name);
Py_DECREF(val);
return setPyErr("dictionary keys must be strings");
}
if ((tup == NULL || name == NULL || val == NULL)
or (buffRepeat(sp, '\t', tabs + 2) == NULL)
or (buffConstant(sp, "<member>") == NULL)
or (buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs + 3) == NULL)
or (buffConstant(sp, "<name>") == NULL)
or (buffConcat(sp, PyString_AS_STRING(name)) == NULL)
or (buffConstant(sp, "</name>") == NULL)
or (buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs + 3) == NULL)
or (encodeValue(sp, val, tabs + 3) == NULL)
or (buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs + 2) == NULL)
or (buffConstant(sp, "</member>") == NULL)
or (buffConstant(sp, EOL) == NULL))
return NULL;
Py_DECREF(tup);
Py_DECREF(name);
Py_DECREF(val);
}
Py_DECREF(items);
if ((buffRepeat(sp, '\t', tabs + 1) == NULL)
or (buffConstant(sp, "</struct>") == NULL)
or (buffConstant(sp, EOL) == NULL)
or (buffRepeat(sp, '\t', tabs) == NULL))
return NULL;
return sp;
}
PyObject *
xmlDecode(PyObject *sp)
{
PyObject *res,
*tup;
char *cp,
*ep;
ulong lines;
lines = 0;
cp = PyString_AS_STRING(sp);
ep = cp + PyObject_Length(sp);
res = decodeValue(&cp, ep, &lines);
if (res == NULL)
return NULL;
tup = Py_BuildValue("(O, s#)", res, cp, ep - cp);
Py_DECREF(res);
return tup;
}
static PyObject *
decodeValue(char **cp, char *ep, ulong *lines)
{
PyObject *res;
char *tp;
if (chompStr(cp, ep, lines) >= ep)
return eosErr();
tp = *cp + strlen("<value>"); /* HACK!! */
unless (findTag("<value>", cp, ep, lines, true))
return NULL;
if (chompStr(cp, ep, lines) >= ep)
return eosErr();
if (strncmp(*cp, "<int>", 5) == 0)
res = decodeInt(cp, ep, lines);
else if (strncmp(*cp, "<i4>", 4) == 0)
res = decodeI4(cp, ep, lines);
else if (strncmp(*cp, "<boolean>", 9) == 0)
res = decodeBool(cp, ep, lines);
else if (strncmp(*cp, "<double>", 8) == 0)
res = decodeDouble(cp, ep, lines);
else if (strncmp(*cp, "<string>", 8) == 0)
res = decodeString(cp, ep, lines);
else if (strncmp(*cp, "<string/>", 9) == 0)
res = decodeString(cp, ep, lines);
else if (strncmp(*cp, "<array>", 7) == 0)
res = decodeArray(cp, ep, lines);
else if (strncmp(*cp, "<struct>", 8) == 0)
res = decodeStruct(cp, ep, lines);
else if (strncmp(*cp, "<dateTime.iso8601>", 18) == 0)
res = decodeDate(cp, ep, lines);
else if (strncmp(*cp, "<base64>", 8) == 0)
res = decodeBase64(cp, ep, lines);
else { /* it must be a string */
*cp = tp;
res = decodeTaglessString(cp, ep, lines);
}
if (res == NULL)
return NULL;
unless (findTag("</value>", cp, ep, lines, false)) {
Py_DECREF(res);
return NULL;
}
chompStr(cp, ep, lines);
return res;
}
static PyObject *
decodeInt(char **cp, char *ep, ulong *lines)
{
long i;
*cp += strlen("<int>");
unless (decodeActLong(cp, ep, &i))
return NULL;
if (*cp >= ep)
return eosErr();
unless (findTag("</int>", cp, ep, lines, true))
return NULL;
return PyInt_FromLong(i);
}
static PyObject *
decodeI4(char **cp, char *ep, ulong *lines)
{
long i;
*cp += strlen("<i4>");
unless (decodeActLong(cp, ep, &i))
return NULL;
if (*cp >= ep)
return eosErr();
unless (findTag("</i4>", cp, ep, lines, true))
return NULL;
return PyInt_FromLong(i);
}
static PyObject *
decodeBool(char **cp, char *ep, ulong *lines)
{
PyObject *res;
bool value;
value = true; /* to appease the compiler */
res = NULL; /* to appease the compiler */
if (*cp + 20 >= ep)
return eosErr();
if (strncmp(*cp, "<boolean>1</boolean>", 20) == 0)
value = true;
else if (strncmp(*cp, "<boolean>0</boolean>", 20) == 0)
value = false;
else
syntaxErr(*lines);
*cp += 20;
if (chompStr(cp, ep, lines) >= ep)
return eosErr();
return rpcBoolNew(value);
}
static PyObject *
decodeDouble(char **cp, char *ep, ulong *lines)
{
double d;
d = 0.0;
*cp += strlen("<double>");
unless (decodeActDouble(cp, ep, &d))
return syntaxErr(*lines);
unless (findTag("</double>", cp, ep, lines, true))
return NULL;
return PyFloat_FromDouble(d);
}
static PyObject *
decodeBase64(char **cp, char *ep, ulong *lines)
{
PyObject *encStr,
*decStr,
*ret;
char *tp;
*cp += strlen("<base64>");
tp = *cp;
while (strncmp(*cp, "</base64>", 9)) {
if (**cp == '\n')
(*lines)++;
if (*cp >= ep)
return eosErr();
(*cp)++;
}
encStr = PyString_FromStringAndSize(tp, *cp - tp);
if (encStr == NULL)
return (NULL);
unless (findTag("</base64>", cp, ep, lines, true)) {
Py_DECREF(encStr);
return (NULL);
}
decStr = rpcBase64Decode(encStr);
Py_DECREF(encStr);
if (decStr == NULL)
return NULL;
ret = rpcBase64New(decStr);
Py_DECREF(decStr);
return (ret);
}
/*
* build an integer from a few digits
*/
static bool
buildInt(char *cp, int slen, int *ip)
{
char *ep;
int i = 0;
ep = cp + slen;
for (; cp < ep; ++cp)
if ('0' <= *cp && *cp <= '9')
i = i * 10 + (*cp - '0');
else {
PyErr_SetString(rpcError,
"<dateTime> expects numbers for date values");
return (0);
}
*ip = i;
return (1);
}
static PyObject *
decodeDate(char **cp, char *eq, ulong *lines)
{
rpcDate *ret;
PyObject *arg;
char *tp;
int year,
month,
day,
hour,
min,
sec;
*cp += strlen("<dateTime.iso8601>");
tp = *cp;
/* while (strncmp(*cp, "</dateTime.iso8601>", 19)) {
if (**cp == '\n')
(*lines)++;
if (*cp >= eq)
return eosErr();
(*cp)++;
}
unless (findTag("</dateTime.iso8601>", cp, eq, lines, false))
return (NULL);
tp[(*cp-tp)-strlen("</dateTime.iso8601>")] = '\0';
printf("TP IS %s\n", tp);
unless (strlen(tp) == 17) {
PyErr_SetString(rpcError, "Invalid format of <dateTime>");
return (NULL);
}*/
unless ((buildInt(tp, 4, &year))
and (buildInt(tp + 4, 2, &month))
and (buildInt(tp + 6, 2, &day))
and (buildInt(tp + 9, 2, &hour))
and (buildInt(tp + 12, 2, &min))
and (buildInt(tp + 15, 2, &sec)))
return NULL;
arg = Py_BuildValue("(i, i, i, i, i, i)",
year, month, day, hour, min , sec);
unless (arg)
return (NULL);
ret = (rpcDate *)rpcDateNew(arg);
Py_DECREF(arg);
unless (ret)
return (NULL);
while (strncmp(*cp, "</dateTime.iso8601>", 19)) {
if (**cp == '\n')
(*lines)++;
if (*cp >= eq)
return eosErr();
(*cp)++;
}
unless (findTag("</dateTime.iso8601>", cp, eq, lines, true))
return (NULL);
return (PyObject *)(ret);
}
static PyObject *
decodeString(char **cp, char *ep, ulong *lines)
{
PyObject *res;
char *tp;
if ((*cp)[7] == '/') {
*cp += strlen("<string/>");
chompStr(cp, ep, lines);
return PyString_FromString("");
} else
*cp += strlen("<string>");
tp = *cp;
while (strncmp(*cp, "</string>", 8)) {
if (**cp == '\n')
(*lines)++;
if (*cp >= ep)
return eosErr();
(*cp)++;
}
res = unescapeString(tp, *cp);
if (res == NULL)
return NULL;
unless (findTag("</string>", cp, ep, lines, true)) {
Py_DECREF(res);
return NULL;
}
return res;
}
static PyObject *
decodeTaglessString(char **cp, char *ep, ulong *lines)
{
PyObject *res;
char *tp;
tp = *cp;
while (strncmp(*cp, "</value>", 8)) {
if (**cp == '\n')
(*lines)++;
if (*cp >= ep)
return eosErr();
(*cp)++;
}
res = unescapeString(tp, *cp);
if (res == NULL)
return NULL;
return res;
}
static PyObject *
decodeArray(char **cp, char *ep, ulong *lines)
{
PyObject *res,
*elem;
unless (findTag("<array>", cp, ep, lines, true))
return NULL;
res = PyList_New(0);
if (res == NULL)
return NULL;
if (strncmp("<data>", *cp, 6) == 0) {
unless (findTag("<data>", cp, ep, lines, true))
return NULL;
while (strncmp(*cp, "<value>", 7) == 0) {
elem = decodeValue(cp, ep, lines);
if (elem == NULL) {
Py_DECREF(res);
return NULL;
}
if (PyList_Append(res, elem)) {
Py_DECREF(res);
Py_DECREF(elem);
return NULL;
}
Py_DECREF(elem);
}
unless (findTag("</data>", cp, ep, lines, true)) {
Py_DECREF(res);
return NULL;
}
}
unless (findTag("</array>", cp, ep, lines, true)) {
Py_DECREF(res);
return NULL;
}
return res;
}
/*
* fix: doesn't handle null byte keys properly
*/
static PyObject *
decodeStruct(char **cp, char *ep, ulong *lines)
{
PyObject *res,
*val;
char *tp,
*key;
uint klen;
res = PyDict_New();
if (res == NULL)
return NULL;
unless (findTag("<struct>", cp, ep, lines, true)) {
Py_DECREF(res);
return NULL;
}
while (strncmp(*cp, "<member>", 8) == 0) {
unless ((findTag("<member>", cp, ep, lines, true))
and (findTag("<name>", cp, ep, lines, false))) {
Py_DECREF(res);
return NULL;
}
tp = *cp;
while (strncmp(*cp, "</name>", 7)) { /* find name */
if (**cp == '\n')
(*lines)++;
else if (*cp > ep) {
Py_DECREF(res);
eosErr();
}
(*cp)++;
}
klen = *cp - tp;
key = alloc(klen + 1); /* add null byte... */
if (key == NULL) {
Py_DECREF(res);
return NULL;
}
strncpy(key, tp, klen);
key[klen] = EOS;
unless (findTag("</name>", cp, ep, lines, true)) {
Py_DECREF(res);
free(key);
return NULL;
}
val = decodeValue(cp, ep, lines); /* get value */
if (val == NULL) {
Py_DECREF(res);
free(key);
return NULL;
}
unless ((PyDict_SetItemString(res, key, val) == 0)
and (findTag("</member>", cp, ep, lines, true))) {
Py_DECREF(res);
free(key);
Py_DECREF(val);
return NULL;
}
free(key);
Py_DECREF(val);
}
unless (findTag("</struct>", cp, ep, lines, true)) {
Py_DECREF(res);
return NULL;
}
return res;
}
/* build the methodcall xmlrpc string that is used by several functions */
static strBuff *
xmlMethod(char *method, PyObject *params)
{
strBuff *body;
int i;
assert(PySequence_Check(params));
assert((method != NULL));
body = newBuff();
if ((body == NULL)
or (buffConstant(body, "<?xml version=\"1.0\"?>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "<methodCall>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "\t<methodName>") == NULL)
or (buffConcat(body, method) == NULL)
or (buffConstant(body, "</methodName>") == NULL)
or (buffConstant(body, EOL) == NULL))
return NULL;
if ((buffConstant(body, "\t<params>") == NULL)
or (buffConstant(body, EOL) == NULL))
return NULL;
for (i = 0; i < PyObject_Length(params); ++i) {
PyObject *elem;
elem = PySequence_GetItem(params, i);
if (elem == NULL)
return NULL;
if ((buffConstant(body, "\t\t<param>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffRepeat(body, '\t', 3) == NULL)
or (encodeValue(body, elem, 3) == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "\t\t</param>") == NULL)
or (buffConstant(body, EOL) == NULL))
return NULL;
Py_DECREF(elem);
}
if ((buffConstant(body, "\t</params>") == NULL)
or (buffConstant(body, EOL) == NULL))
return NULL;
if (buffConstant(body, "</methodCall>") == NULL)
return NULL;
return body;
}
/*
* build the xmlrpc method call for the remote procedure call
*/
PyObject *
buildCall(char *method, PyObject *params)
{
strBuff *body;
PyObject *res;
body = xmlMethod(method, params);
if (body == NULL)
return NULL;
res = PyString_FromStringAndSize(body->beg, body->len);
freeBuff(body);
return res;
}
/*
* build a request for a remote procedure call
*/
PyObject *
buildRequest(char *url, char *method, PyObject *params, PyObject *addInfo)
{
PyObject *res;
strBuff *header,
*body;
body = xmlMethod(method, params);
if (body == NULL)
return NULL;
header = buildHeader(TYPE_REQ, url, addInfo, body->len);
if ((header == NULL)
or (buffAppend(header, body->beg, body->len) == NULL))
return NULL;
res = PyString_FromStringAndSize(header->beg, header->len);
freeBuff(header);
freeBuff(body);
return res;
}
/*
* build a response to a remote procedure call
*/
PyObject *
buildResponse(PyObject *result, PyObject *addInfo)
{
PyObject *res;
strBuff *header,
*body;
body = newBuff();
if ((body == NULL)
or (buffConstant(body, "<?xml version=\"1.0\"?>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "<methodResponse>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "\t<params>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "\t\t<param>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffRepeat(body, '\t', 3) == NULL)
or (encodeValue(body, result, 3) == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "\t\t</param>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "\t</params>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "</methodResponse>") == NULL)
or (buffConstant(body, EOL) == NULL))
return NULL;
header = buildHeader(TYPE_RESP, NULL, addInfo, body->len);
if ((header == NULL)
or (buffAppend(header, body->beg, body->len) == NULL))
return NULL;
res = PyString_FromStringAndSize(header->beg, header->len);
freeBuff(header);
freeBuff(body);
return res;
}
/*
* build a fault response
*/
PyObject *
buildFault(int errCode, char *errStr, PyObject *addInfo)
{
PyObject *error,
*res;
strBuff *header,
*body;
error = Py_BuildValue("{s: i, s: s}",
"faultCode", errCode,
"faultString", errStr);
if (error == NULL)
return NULL;
body = newBuff();
if ((body == NULL)
or (buffConstant(body, "<?xml version=\"1.0\"?>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "<methodResponse>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "\t<fault>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffRepeat(body, '\t', 2) == NULL)
or (encodeValue(body, error, 2) == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "\t</fault>") == NULL)
or (buffConstant(body, EOL) == NULL)
or (buffConstant(body, "</methodResponse>") == NULL))
return NULL;
Py_DECREF(error);
header = buildHeader(TYPE_RESP, NULL, addInfo, body->len);
if ((header == NULL)
or (buffAppend(header, body->beg, body->len) == NULL))
return NULL;
res = PyString_FromStringAndSize(header->beg, header->len);
freeBuff(header);
freeBuff(body);
return res;
}
static strBuff *
buildHeader(int reqType, char *url, PyObject *addInfo, long bodyLen)
{
PyObject *items,
*tup,
*key,
*val;
strBuff *header;
char buffLen[110];
int i;
assert(PyDict_Check(addInfo));
header = newBuff();
if (header == NULL)
return NULL;
switch (reqType) {
case TYPE_REQ:
if ((buffConstant(header, "POST ") == NULL)
or (buffConcat(header, url) == NULL)
or (buffConstant(header, " HTTP/1.1") == NULL)
or (buffConstant(header, EOL) == NULL)
or (buffConstant(header, "User-Agent: ") == NULL)
or (buffConcat(header, XMLRPC_LIB_STR) == NULL)
or (buffConstant(header, EOL) == NULL))
return NULL;
break;
case TYPE_RESP:
if ((buffConstant(header, "HTTP/1.1 200 OK") == NULL)
or (buffConstant(header, EOL) == NULL)
or (buffConstant(header, "Server: ") == NULL)
or (buffConcat(header, XMLRPC_LIB_STR) == NULL)
or (buffConstant(header, EOL) == NULL))
return NULL;
break;
}
items = PyDict_Items(addInfo);
if (items == NULL)
return NULL;
for (i = 0; i < PyObject_Length(items); ++i) {
tup = PySequence_GetItem(items, i);
assert(PyObject_Length(tup) == 2);
key = PySequence_GetItem(tup, 0);
val = PySequence_GetItem(tup, 1);
if (not PyString_Check(key) || not PyString_Check(val))
return setPyErr("header info keys and values must be strings");
if ((buffConcat(header, PyString_AS_STRING(key)) == NULL)
or (buffConstant(header, ": ") == NULL)
or (buffConcat(header, PyString_AS_STRING(val)) == NULL)
or (buffConstant(header, EOL) == NULL))
return NULL;
Py_DECREF(tup);
Py_DECREF(key);
Py_DECREF(val);
}
Py_DECREF(items);
sprintf(buffLen, "Content-length: %ld%s", bodyLen, EOL);
if ((buffConstant(header, "Content-Type: text/xml") == NULL)
or (buffConstant(header, EOL) == NULL)
or (buffConcat(header, buffLen) == NULL)
or (buffConstant(header, EOL) == NULL))
return NULL;
return header;
}
/* parse the xmlrpc call without the header section */
PyObject *
parseCall(PyObject *request)
{
PyObject *method,
*params,
*tuple;
ulong lines;
char *cp,
*ep,
*tp;
unless (PyString_Check(request))
return NULL;
lines = 1;
cp = PyString_AS_STRING(request);
ep = cp + PyObject_Length(request);
unless ((findXmlVersion(&cp, ep, &lines))
and (findTag("<methodCall>", &cp, ep, &lines, true))
and (findTag("<methodName>", &cp, ep, &lines, false)))
return NULL;
tp = cp;
for (; cp < ep; ++cp)
if (*cp == '\n')
lines++;
else if (strncmp("</methodName>", cp, 13) == 0)
break;
if (cp >= ep)
return eosErr();
method = PyString_FromStringAndSize(tp, cp - tp);
if (method == NULL)
return NULL;
unless (findTag("</methodName>", &cp, ep, &lines, true)) {
Py_DECREF(method);
return NULL;
}
params = PyList_New(0);
if (params == NULL) {
Py_DECREF(method);
return NULL;
}
if ((strncmp("<params>", cp, 8) == 0)
and (not parseParams(&cp, ep, &lines, params))) {
Py_DECREF(method);
Py_DECREF(params);
return NULL;
} else if (strncmp("<params/>", cp, 9) == 0) {
cp += 9;
chompStr(&cp, ep, &lines);
}
unless (findTag("</methodCall>", &cp, ep, &lines, false)) {
Py_DECREF(method);
Py_DECREF(params);
return NULL;
}
chompStr(&cp, ep, &lines);
if (cp != ep) {
Py_DECREF(method);
Py_DECREF(params);
return setPyErr("unused data when parsing request");
}
tuple = Py_BuildValue("(O, O)", method, params);
Py_DECREF(method);
Py_DECREF(params);
return tuple;
}
/* Parse an incoming request with the header. The heavy lifting is done
* by parseCall()
*/
PyObject *
parseRequest(PyObject *request)
{
PyObject *method,
*addInfo,
*params,
*tuple;
ulong lines;
char *cp, *ep;
lines = 1;
cp = PyString_AS_STRING(request);
ep = cp + PyObject_Length(request);
addInfo = parseHeader(&cp, ep, &lines, TYPE_REQ);
if (addInfo == NULL)
return NULL;
/* prepare a string object to call parseCall() */
method = PyString_FromStringAndSize(cp, ep - cp);
if (method == NULL) {
Py_DECREF(addInfo);
return NULL;
}
tuple = parseCall(method);
if ( (tuple == NULL) or
!PySequence_Check(tuple) or
(PyObject_Length(tuple) != 2) ) {
Py_DECREF(method);
Py_DECREF(addInfo);
return NULL;
}
/* go the tuple back, piece it together */
Py_DECREF(method); /* no longer needed */
/* decode the tuple */
method = PySequence_GetItem(tuple, 0);
params = PySequence_GetItem(tuple, 1);
if ((method == NULL) or (params == NULL)) {
Py_DECREF(addInfo);
Py_DECREF(tuple);
Py_XDECREF(method);
Py_XDECREF(params);
return NULL;
}
Py_DECREF(tuple); /* this older tuple is no longer required */
tuple = Py_BuildValue("(O, O, O)", method, params, addInfo);
Py_DECREF(method);
Py_DECREF(params);
Py_DECREF(addInfo);
return tuple;
}
bool
parseParams(char **cpp, char *ep, ulong *linep, PyObject *params)
{
ulong lines;
char *cp;
PyObject *elem;
int r;
cp = *cpp;
lines = *linep;
unless (findTag("<params>", &cp, ep, &lines, true))
return false;
while (strncmp(cp, "<param>", 7) == 0) {
unless (findTag("<param>", &cp, ep, &lines, true))
return false;
elem = decodeValue(&cp, ep, &lines);
if (elem == NULL)
return false;
r = PyList_Append(params, elem);
Py_DECREF(elem);
unless ((r == 0)
and (findTag("</param>", &cp, ep, &lines, true)))
return false;
}
unless (findTag("</params>", &cp, ep, &lines, true))
return false;
*cpp = cp;
*linep = lines;
return true;
}
bool
doKeepAliveFromDict(PyObject *addInfo)
{
PyObject *pyVer,
*pyCon;
char *con;
double version;
bool keepAlive;
pyVer = PyDict_GetItemString(addInfo, "HTTP Version");
if (pyVer == NULL) {
Py_DECREF(addInfo);
return false;
}
assert(PyFloat_Check(pyVer));
version = PyFloat_AS_DOUBLE(pyVer);
pyCon = PyDict_GetItemString(addInfo, "Connection");
if (pyCon != NULL) {
assert(PyString_Check(pyCon));
con = PyString_AS_STRING(pyCon);
} else
con = NULL;
keepAlive = false;
if ((version == 1.0)
and (con && strcasecmp(con, "keep-alive") == 0))
keepAlive = true;
if ((version == 1.1)
and (con == NULL || strcasecmp(con, "close")))
keepAlive = true;
return (keepAlive);
}
bool
doKeepAlive(PyObject *header, int reqType)
{
char *cp;
ulong lines;
PyObject *addInfo;
bool keepAlive;
cp = PyString_AsString(header);
lines = 0;
addInfo = parseHeader(&cp, cp+PyString_GET_SIZE(header),
&lines, reqType);
if (addInfo == NULL)
return false;
keepAlive = doKeepAliveFromDict(addInfo);
Py_DECREF(addInfo);
return keepAlive;
}
/*
* storing the URI in the header information is ugly, but oh well...
*/
static PyObject *
parseHeader(char **cpp, char *ep, ulong *lines, int reqType)
{
PyObject *addInfo,
*pyUri,
*pyVersion;
char *cp,
*tp,
method[256],
error[256];
double version;
cp = *cpp;
pyUri = NULL;
version = 0.0;
switch (reqType) {
case TYPE_REQ:
tp = cp;
for (tp = cp; *tp != ' '; ++tp) {
if (*tp == '\n')
return setPyErr("illegal Request-Line");
if (tp + 1 == ep)
return setPyErr("EOS reached early");
}
if (tp - cp > 255)
return setPyErr("HTTP Method too long");
strncpy(method, cp, tp-cp);
method[tp-cp] = EOS; /* get rid of the space */
if (strcasecmp(method, "POST") != 0) {
snprintf(error, 255, "unsupported HTTP Method: '%s'",
method);
return setPyErr(error);
}
cp = tp + 1;
for (tp = cp; *tp != ' '; ++tp) {
if (*tp == '\n')
return setPyErr("illegal Request-Line");
if (tp + 1 == ep)
return setPyErr("EOS reached early");
}
pyUri = PyString_FromStringAndSize(cp, tp-cp);
if (pyUri == NULL)
return NULL;
cp = tp + 1;
if (strncmp(cp, "HTTP/1.0", 8) == 0)
version = 1.0;
else if (strncmp(cp, "HTTP/1.1", 8) == 0)
version = 1.1;
else {
Py_XDECREF(pyUri);
return setPyErr("illegal HTTP Version");
}
cp += 11;
break;
case TYPE_RESP:
if (strncmp(cp, "HTTP/1.0 ", 9) == 0)
version = 1.0;
else if (strncmp(cp, "HTTP/1.1 ", 9) == 0)
version = 1.1;
else
return setPyErr("illegal HTTP version");
cp += 9;
break;
}
while (cp <= ep and *cp != '\n')
cp++;
cp++;
(*lines)++;
if (cp > ep) {
Py_XDECREF(pyUri);
return eosErr();
}
addInfo = PyDict_New();
if (addInfo == NULL) {
Py_XDECREF(pyUri);
return NULL;
}
pyVersion = PyFloat_FromDouble(version);
if (pyVersion == NULL) {
Py_XDECREF(addInfo);
Py_XDECREF(pyUri);
return NULL;
}
if (PyDict_SetItemString(addInfo, "HTTP Version", pyVersion)) {
Py_XDECREF(addInfo);
Py_XDECREF(pyUri);
return NULL;
}
Py_DECREF(pyVersion);
if (pyUri != NULL) {
if (PyDict_SetItemString(addInfo, "URI", pyUri))
return NULL;
Py_DECREF(pyUri);
}
while (cp <= ep)
if (*cp == '\n') {
cp++;
(*lines)++;
break;
} else if (cp[0] == '\r' && cp[1] == '\n') {
cp += 2;
(*lines)++;
break;
} else unless (parseHeaderLine(addInfo, &cp, ep, lines))
return NULL;
if (chompStr(&cp, ep, lines) > ep)
return eosErr();
*cpp = cp;
return addInfo;
}
static bool
parseHeaderLine(PyObject *addInfo, char **cpp, char *ep, ulong *lines)
{
PyObject *name,
*value;
char *cp,
*sp,
*tp;
value = NULL; /* to appease the compiler */
cp = *cpp;
tp = cp;
while (*cp != ':' and cp <= ep)
cp++;
if (cp > ep)
return (bool)eosErr();
name = PyString_FromStringAndSize(tp, cp - tp);
sp = PyString_AS_STRING(name);
tp = sp + PyString_GET_SIZE(name);
for (; sp < tp; ++sp) { /* capitalize */
if (sp == PyString_AS_STRING(name)) {
if ('a' <= *sp && *sp <= 'z')
*sp -= 'a' - 'A';
} else
if ('A' <= *sp && *sp <= 'Z')
*sp += 'a' - 'A';
}
cp++;
while ((cp <= ep)
and (*cp == '\t' || *cp == ' '))
cp++;
if (cp > ep)
return (bool)eosErr();
tp = cp;
for (;cp <= ep; ++cp)
if (*cp == '\n') {
value = PyString_FromStringAndSize(tp, cp - tp);
cp += 1;
break;
} else if (cp[0] == '\r' && cp[1] == '\n') {
value = PyString_FromStringAndSize(tp, cp - tp);
cp += 2;
break;
}
if (cp > ep)
return (bool)eosErr();
if (value == NULL)
return false;
if (PyDict_SetItem(addInfo, name, value))
return false;
Py_DECREF(name);
Py_DECREF(value);
*cpp = cp;
return true;
}
PyObject *
parseResponse(PyObject *request)
{
PyObject *tuple,
*addInfo,
*result;
ulong lines;
char *cp,
*ep;
lines = 1;
cp = PyString_AS_STRING(request);
ep = cp + PyObject_Length(request);
addInfo = parseHeader(&cp, ep, &lines, TYPE_RESP);
if (addInfo == NULL)
return NULL;
unless ((findXmlVersion(&cp, ep, &lines))
and (findTag("<methodResponse>", &cp, ep, &lines, true))) {
Py_DECREF(addInfo);
return NULL;
}
if (strncmp("<fault>", cp, 7) == 0) {
Py_DECREF(addInfo);
return parseFault(cp, ep, lines);
}
unless ((findTag("<params>", &cp, ep, &lines, true))
and (findTag("<param>", &cp, ep, &lines, true))) {
Py_DECREF(addInfo);
return NULL;
}
result = decodeValue(&cp, ep, &lines);
if (result == NULL) {
Py_DECREF(addInfo);
return NULL;
}
unless ((findTag("</param>", &cp, ep, &lines, true))
and (findTag("</params>", &cp, ep, &lines, true))
and (findTag("</methodResponse>", &cp, ep, &lines, false))) {
Py_DECREF(addInfo);
Py_DECREF(result);
return NULL;
}
chompStr(&cp, ep, &lines);
if (cp != ep) {
Py_DECREF(addInfo);
Py_DECREF(result);
return setPyErr("unused data when parsing response");
}
tuple = Py_BuildValue("(O, O)", result, addInfo);
Py_DECREF(result);
Py_DECREF(addInfo);
return tuple;
}
static PyObject *
parseFault(char *cp, char *ep, long lines)
{
PyObject *errDict,
*errStr,
*errCode;
unless (findTag("<fault>", &cp, ep, &lines, true))
return NULL;
errDict = decodeValue(&cp, ep, &lines);
if (errDict == NULL)
return NULL;
unless ((PyDict_Check(errDict))
and (PyMapping_HasKeyString(errDict, "faultCode"))
and (PyMapping_HasKeyString(errDict, "faultString"))) {
Py_DECREF(errDict);
return setPyErr("illegal fault value");
}
errCode = PyDict_GetItemString(errDict, "faultCode");
errStr = PyDict_GetItemString(errDict, "faultString");
if (errCode == NULL || errStr == NULL)
return NULL;
unless (PyInt_Check(errCode) && PyString_Check(errStr)) {
Py_DECREF(errDict);
return setPyErr("illegal fault value");
}
rpcFaultRaise(errCode, errStr);
Py_DECREF(errDict);
unless ((findTag("</fault>", &cp, ep, &lines, true))
and (findTag("</methodResponse>", &cp, ep, &lines, false))) {
return NULL;
}
chompStr(&cp, ep, &lines);
if (cp != ep) {
return setPyErr("unused data when parsing response");
}
return NULL;
}
static PyObject *
eosErr(void)
{
return setPyErr("EOS error while decoding xml");
}
static PyObject *
syntaxErr(ulong line)
{
char buff[100];
snprintf(buff, 100, "syntax error in line %ld\n", line);
return setPyErr(buff);
}
static char *
chompStr(char **cp, char *ep, ulong *lines)
{
for (; *cp < ep; ++(*cp)) {
if (**cp == '\t' or **cp == ' ' or **cp == '\r')
continue;
else if (**cp == '\n')
(*lines)++;
else if ((ep - *cp >= TAG_LEN(COM_BEG))
and (strncmp(*cp, COM_BEG, TAG_LEN(COM_BEG)) == 0)) {
*cp += TAG_LEN(COM_BEG);
while (true) {
if (ep - *cp < TAG_LEN(COM_END)) {
*cp = ep;
return ep; /* no matches */
}
if (strncmp(*cp, COM_END, TAG_LEN(COM_END)) == 0) {
*cp += TAG_LEN(COM_END);
break;
}
(*cp)++;
}
} else
return *cp;
}
return *cp;
}
bool
findXmlVersion(char **cpp, char *ep, ulong *lines)
{
double ver;
char *cp;
cp = *cpp;
unless (strncmp("<?xml version=", cp, 14) == 0) {
setPyErr("bad xml version tag");
return false;
}
cp += 14;
unless (*cp == '\'' || *cp == '\"') {
setPyErr("bad xml version tag");
return false;
}
cp++;
unless (decodeActDouble(&cp, ep, &ver)) {
setPyErr("bad xml version tag");
return false;
}
while (strncmp(cp, "?>", 2)) {
if ((cp >= ep)
or (*cp == '\n')) {
setPyErr("bad xml version tag");
return false;
}
++cp;
}
cp += 2;
if (chompStr(&cp, ep, lines) > ep)
return false;
*cpp = cp;
return true;
}
bool
findTag(char *tag, char **cpp, char *ep, ulong *lines, bool chomp)
{
char err[256];
uint l;
l = strlen(tag);
if (strncmp(*cpp, tag, l)) {
snprintf(err, 255, "couldn't find %s tag in line %ld: %.30s",
tag, *lines, *cpp);
setPyErr(err);
return false;
}
(*cpp) += l;
if (chomp and chompStr(cpp, ep, lines) > ep) {
(void)eosErr();
return false;
}
return true;
}
static PyObject *
unescapeString(char *bp, char *ep)
{
PyObject *res;
char *newStr,
*tp;
int remLen;
ulong tmp;
if (ep == bp)
return PyString_FromString("");
assert(ep > bp);
newStr = alloc(sizeof(*bp) * (ep - bp + 1));
tp = newStr;
while (bp < ep) {
if (*bp == '&') {
remLen = ep - bp;
if ((remLen >= 4)
and (strncmp(bp, "<", 4) == 0)) {
*(tp++) = '<';
bp += 4;
continue;
}
if ((remLen >= 4)
and (strncmp(bp, ">", 4) == 0)) {
*(tp++) = '>';
bp += 4;
continue;
}
if ((remLen >= 3)
and (strncmp(bp, "&&;", 3) == 0)) {
*(tp++) = '&';
bp += 3;
continue;
}
if ((remLen >= 5)
and (strncmp(bp, "&", 5) == 0)) {
*(tp++) = '&';
bp += 5;
continue;
}
if ((remLen >= 6)
and (strncmp(bp, "'", 6) == 0)) {
*(tp++) = '\'';
bp += 6;
continue;
}
if ((remLen >= 6)
and (strncmp(bp, """, 6) == 0)) {
*(tp++) = '"';
bp += 6;
continue;
}
if ((remLen > 4)
and (strncasecmp(bp, "&#x", 3) == 0)) {
bp += 3;
if (!decodeActLongHex(&bp, ep, &tmp))
return setPyErr("Illegal quoted sequence");
if (*bp++ != ';')
return setPyErr("Illegal quoted sequence");
*(tp++) = tmp;
continue;
}
if ((remLen > 3)
and (strncmp(bp, "&#", 2) == 0)) {
bp += 2;
if (!decodeActLong(&bp, ep, &tmp))
return setPyErr("Illegal quoted sequence");
if (*bp++ != ';')
return setPyErr("Illegal quoted sequence");
*(tp++) = tmp;
continue;
}
return setPyErr("Illegal quoted sequence");
} else
*(tp++) = *(bp++);
}
*tp = EOS;
res = PyString_FromStringAndSize(newStr, tp-newStr);
free(newStr);
return res;
}
static PyObject *
escapeString(PyObject *oldStr)
{
PyObject *newStr;
int newLen, oldLen;
char *np,
*op,
*ep;
assert(PyString_Check(oldStr));
newLen = 0;
oldLen = PyString_GET_SIZE(oldStr);
ep = PyString_AS_STRING(oldStr) + oldLen;
op = PyString_AS_STRING(oldStr);
for (; op < ep; ++op) {
switch (*op) {
case '<' :
newLen += 4;
break;
case '&' :
newLen += 5;
break;
default :
newLen++;
break;
}
}
/* check if we really need to work on this string */
if (newLen <= oldLen) {
Py_INCREF(oldStr); /* the calling function expects this to be a new object
* and calls Py_DECREF on it */
return oldStr; /* avoid extra work because it is not necessary */
}
newStr = PyString_FromStringAndSize(NULL, newLen);
if (newStr == NULL)
return NULL;
op = PyString_AS_STRING(oldStr);
np = PyString_AS_STRING(newStr);
for (; op < ep; ++op) {
switch (*op) {
case '<' :
strncpy(np, "<", 4);
np += 4;
break;
case '&' :
strncpy(np, "&", 5);
np += 5;
break;
default :
*(np++) = *op;
break;
}
}
assert(np == (PyString_AS_STRING(newStr) + newLen));
*np = EOS; /* probably unnecessary (python already does it) */
return newStr;
}
syntax highlighted by Code2HTML, v. 0.9.1