/* * Copyright (C) 2001, Shilad Sen, Sourcelight Technologies, Inc. * See xmlrpc.h or the README for more copyright information. */ #include #include #include "xmlrpc.h" #include "rpcInternal.h" #define BUFF_START 256 #define EOL "\r\n" #define COM_BEG "" #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, "") == 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, ""); return sp; } /* * encode the int 4 (for example) as: "4" * 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, "") == NULL) or (buffConcat(sp, PyString_AS_STRING(strval)) == NULL) or (buffConstant(sp, "") == NULL)) return NULL; Py_DECREF(strval); return sp; } /* * encode the double 4.0 (for example) as: "4.0" * 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, "") == NULL) or (buffConcat(sp, buff) == NULL) or (buffConstant(sp, "") == NULL)) return NULL; return sp; } /* * encode the boolean true (for example) as: "1" */ static strBuff * encodeBool(strBuff *sp, PyObject *value) { if (((rpcBool *)value)->value) return buffConstant(sp, "1"); else return buffConstant(sp, "0"); } /* * encode "Hello, World!" (for example) as: "Hello, World!" */ 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, "") == NULL) or (buffAppend(sp, cp, len) == NULL) or (buffConstant(sp, "") == 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, "") == NULL) or (buffAppend(sp, str, strlen(str)) == NULL) or (buffConstant(sp, "") == 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, "") == 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, "") == 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, "") == NULL) or (buffConstant(sp, EOL) == NULL) or (buffRepeat(sp, '\t', tabs + 2) == NULL) or (buffConstant(sp, "") == 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, "") == NULL) or (buffConstant(sp, EOL) == NULL) or (buffRepeat(sp, '\t', tabs + 1) == NULL) or (buffConstant(sp, "") == 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, "") == 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, "") == NULL) or (buffConstant(sp, EOL) == NULL) or (buffRepeat(sp, '\t', tabs + 3) == NULL) or (buffConstant(sp, "") == NULL) or (buffConcat(sp, PyString_AS_STRING(name)) == NULL) or (buffConstant(sp, "") == 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, "") == 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, "") == 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(""); /* HACK!! */ unless (findTag("", cp, ep, lines, true)) return NULL; if (chompStr(cp, ep, lines) >= ep) return eosErr(); if (strncmp(*cp, "", 5) == 0) res = decodeInt(cp, ep, lines); else if (strncmp(*cp, "", 4) == 0) res = decodeI4(cp, ep, lines); else if (strncmp(*cp, "", 9) == 0) res = decodeBool(cp, ep, lines); else if (strncmp(*cp, "", 8) == 0) res = decodeDouble(cp, ep, lines); else if (strncmp(*cp, "", 8) == 0) res = decodeString(cp, ep, lines); else if (strncmp(*cp, "", 9) == 0) res = decodeString(cp, ep, lines); else if (strncmp(*cp, "", 7) == 0) res = decodeArray(cp, ep, lines); else if (strncmp(*cp, "", 8) == 0) res = decodeStruct(cp, ep, lines); else if (strncmp(*cp, "", 18) == 0) res = decodeDate(cp, ep, lines); else if (strncmp(*cp, "", 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("", 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(""); unless (decodeActLong(cp, ep, &i)) return NULL; if (*cp >= ep) return eosErr(); unless (findTag("", cp, ep, lines, true)) return NULL; return PyInt_FromLong(i); } static PyObject * decodeI4(char **cp, char *ep, ulong *lines) { long i; *cp += strlen(""); unless (decodeActLong(cp, ep, &i)) return NULL; if (*cp >= ep) return eosErr(); unless (findTag("", 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, "1", 20) == 0) value = true; else if (strncmp(*cp, "0", 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(""); unless (decodeActDouble(cp, ep, &d)) return syntaxErr(*lines); unless (findTag("", 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(""); tp = *cp; while (strncmp(*cp, "", 9)) { if (**cp == '\n') (*lines)++; if (*cp >= ep) return eosErr(); (*cp)++; } encStr = PyString_FromStringAndSize(tp, *cp - tp); if (encStr == NULL) return (NULL); unless (findTag("", 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, " 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(""); tp = *cp; /* while (strncmp(*cp, "", 19)) { if (**cp == '\n') (*lines)++; if (*cp >= eq) return eosErr(); (*cp)++; } unless (findTag("", cp, eq, lines, false)) return (NULL); tp[(*cp-tp)-strlen("")] = '\0'; printf("TP IS %s\n", tp); unless (strlen(tp) == 17) { PyErr_SetString(rpcError, "Invalid format of "); 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, "", 19)) { if (**cp == '\n') (*lines)++; if (*cp >= eq) return eosErr(); (*cp)++; } unless (findTag("", 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(""); chompStr(cp, ep, lines); return PyString_FromString(""); } else *cp += strlen(""); tp = *cp; while (strncmp(*cp, "", 8)) { if (**cp == '\n') (*lines)++; if (*cp >= ep) return eosErr(); (*cp)++; } res = unescapeString(tp, *cp); if (res == NULL) return NULL; unless (findTag("", 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, "", 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("", cp, ep, lines, true)) return NULL; res = PyList_New(0); if (res == NULL) return NULL; if (strncmp("", *cp, 6) == 0) { unless (findTag("", cp, ep, lines, true)) return NULL; while (strncmp(*cp, "", 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("", cp, ep, lines, true)) { Py_DECREF(res); return NULL; } } unless (findTag("", 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("", cp, ep, lines, true)) { Py_DECREF(res); return NULL; } while (strncmp(*cp, "", 8) == 0) { unless ((findTag("", cp, ep, lines, true)) and (findTag("", cp, ep, lines, false))) { Py_DECREF(res); return NULL; } tp = *cp; while (strncmp(*cp, "", 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("", 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("", cp, ep, lines, true))) { Py_DECREF(res); free(key); Py_DECREF(val); return NULL; } free(key); Py_DECREF(val); } unless (findTag("", 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, "") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "\t") == NULL) or (buffConcat(body, method) == NULL) or (buffConstant(body, "") == NULL) or (buffConstant(body, EOL) == NULL)) return NULL; if ((buffConstant(body, "\t") == 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") == 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") == NULL) or (buffConstant(body, EOL) == NULL)) return NULL; Py_DECREF(elem); } if ((buffConstant(body, "\t") == NULL) or (buffConstant(body, EOL) == NULL)) return NULL; if (buffConstant(body, "") == 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, "") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "\t") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "\t\t") == 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") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "\t") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "") == 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, "") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "\t") == 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") == NULL) or (buffConstant(body, EOL) == NULL) or (buffConstant(body, "") == 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("", &cp, ep, &lines, true)) and (findTag("", &cp, ep, &lines, false))) return NULL; tp = cp; for (; cp < ep; ++cp) if (*cp == '\n') lines++; else if (strncmp("", cp, 13) == 0) break; if (cp >= ep) return eosErr(); method = PyString_FromStringAndSize(tp, cp - tp); if (method == NULL) return NULL; unless (findTag("", &cp, ep, &lines, true)) { Py_DECREF(method); return NULL; } params = PyList_New(0); if (params == NULL) { Py_DECREF(method); return NULL; } if ((strncmp("", cp, 8) == 0) and (not parseParams(&cp, ep, &lines, params))) { Py_DECREF(method); Py_DECREF(params); return NULL; } else if (strncmp("", cp, 9) == 0) { cp += 9; chompStr(&cp, ep, &lines); } unless (findTag("", &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("", &cp, ep, &lines, true)) return false; while (strncmp(cp, "", 7) == 0) { unless (findTag("", &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("", &cp, ep, &lines, true))) return false; } unless (findTag("", &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("", &cp, ep, &lines, true))) { Py_DECREF(addInfo); return NULL; } if (strncmp("", cp, 7) == 0) { Py_DECREF(addInfo); return parseFault(cp, ep, lines); } unless ((findTag("", &cp, ep, &lines, true)) and (findTag("", &cp, ep, &lines, true))) { Py_DECREF(addInfo); return NULL; } result = decodeValue(&cp, ep, &lines); if (result == NULL) { Py_DECREF(addInfo); return NULL; } unless ((findTag("", &cp, ep, &lines, true)) and (findTag("", &cp, ep, &lines, true)) and (findTag("", &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("", &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("", &cp, ep, &lines, true)) and (findTag("", &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("", 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; }