Example #1
0
void QoreProgram::parseFileAndRun(const char* filename) {
   ExceptionSink xsink;

   parseFile(filename, &xsink);

   if (!xsink.isEvent()) {
      // get class name
      if (priv->exec_class) {
	 if (!priv->exec_class_name.empty())
	    runClass(priv->exec_class_name.c_str(), &xsink);
	 else {
	    char* c, *bn = q_basenameptr(filename);
	    if (!(c = strrchr(bn, '.')))
	       runClass(filename, &xsink);
	    else {
	       QoreString qcn; // for possible class name
	       qcn.concat(bn, c - bn);
	       runClass(qcn.getBuffer(), &xsink);
	    }
	 }
      }
      else
	 run(&xsink);
   }
}
Example #2
0
int QoreValue::getAsString(QoreString& str, int format_offset, ExceptionSink *xsink) const {
   if (isNothing()) {
      str.concat(format_offset == FMT_YAML_SHORT ? &YamlNullString : &NothingTypeString);
      return 0;
   }
   switch (type) {
      case QV_Int: str.sprintf(QLLD, v.i); break;
      case QV_Bool: str.concat(v.b ? &TrueString : &FalseString); break;
      case QV_Float: str.sprintf("%.9g", v.f); break;
      case QV_Node: return v.n->getAsString(str, format_offset, xsink);
      default:
	 assert(false);
	 // no break;
   }
   return 0;
}
Example #3
0
int QoreValueList::getAsString(QoreString &str, int foff, ExceptionSink* xsink) const {
   QoreContainerHelper cch(this);
   if (!cch) {
      str.sprintf("[ERROR: recursive reference to list %p]", this);
      return 0;
   }

   if (foff == FMT_YAML_SHORT) {
      str.concat('[');
      ConstValueListIterator li(this);
      while (li.next()) {
	 QoreValue n = li.getValue();
	 if (n.getAsString(str, foff, xsink))
	    return -1;
	 if (!li.last())
	    str.concat(", ");
      }
      str.concat(']');
      return 0;
   }

   if (!size()) {
      str.concat(&EmptyListString);
      return 0;
   }
   str.concat("list: (");

   if (foff != FMT_NONE)
      str.sprintf("%d element%s)\n", priv->length, priv->length == 1 ? "" : "s");

   for (size_t i = 0; i < priv->length; ++i) {
      if (foff != FMT_NONE) {
	 str.addch(' ', foff + 2);
	 str.sprintf("[%d]=", i);
      }

      QoreValue n = priv->entry[i];
      if (n.getAsString(str, foff != FMT_NONE ? foff + 2 : foff, xsink))
	 return -1;

      if (i != (priv->length - 1)) {
	 if (foff != FMT_NONE)
	    str.concat('\n');
	 else
	    str.concat(", ");
      }
   }
   if (foff == FMT_NONE)
      str.concat(')');

   return 0;
}
Example #4
0
void concatOffset(int utcoffset, QoreString &str) {
   //printd(0, "concatOffset(%d)", utcoffset);

   if (!utcoffset) {
      str.concat('Z');
      return;
   }

   str.concat(utcoffset < 0 ? '-' : '+');
   if (utcoffset < 0)
      utcoffset = -utcoffset;
   int h = utcoffset / SECS_PER_HOUR;
   // the remaining seconds after hours
   int r = utcoffset % SECS_PER_HOUR;
   // minutes
   int m = r / SECS_PER_MINUTE;
   // we have already output the hour sign above
   str.sprintf("%02d:%02d", h < 0 ? -h : h, m);
   // see if there are any seconds
   int s = utcoffset - h * SECS_PER_HOUR - m * SECS_PER_MINUTE;
   if (s)
      str.sprintf(":%02d", s);
}
Example #5
0
// static function
void RegexSubstNode::concat(QoreString *cstr, int *ovector, int olen, const char *ptr, const char *target, int rc) {
   while (*ptr) {
      if (*ptr == '\\' && *(ptr+1) == '$') {
	 ++ptr;
	 cstr->concat(*(ptr++));
      }
      else if (*ptr == '$' && isdigit(ptr[1])) {
	 QoreString n;
	 ++ptr;
	 do
	    n.concat(*(ptr++));
	 while (isdigit(*ptr));
	 int num = atoi(n.getBuffer());
	 int pos = num * 2;
	 //printd(5, "RegexSubstNode::concat() pos: %d olen: %d ovector[%d]: %d ovector[%d]: %d rc: %d\n", pos, olen, pos, ovector[pos], pos + 1, ovector[pos + 1], rc);
	 if (pos > 0 && pos < olen && num < rc && ovector[pos] != -1)
	    cstr->concat(target + ovector[pos], ovector[pos + 1] - ovector[pos]);
      }
      else
	 cstr->concat(*(ptr++));
   }
}
int QoreLogicalNotOperatorNode::getAsString(QoreString& str, int foff, ExceptionSink* xsink) const {
   str.concat(&LogicalNot_str);
   return 0;
}
Example #7
0
// concatenate string representation to a QoreString (no action for complex types = default implementation)
void DateTimeNode::getStringRepresentation(QoreString& str) const {
   str.concat(this);
}
Example #8
0
void qore_date_private::format(QoreString &str, const char *fmt) const {
   qore_time_info i;
   get(i);

   const char *s = fmt;
   while (*s) {
      switch (*s) {
         case 'Y':
            if (s[1] != 'Y') {
               str.concat('Y');
               break;
            }
            s++;
            if ((s[1] == 'Y') && (s[2] == 'Y')) {
               str.sprintf("%04d", i.year);
               s += 2;
            }
            else
               str.sprintf("%02d", i.year - (i.year / 100) * 100);
            break;
         case 'M':
            if (s[1] == 'M') {
               str.sprintf("%02d", i.month);
               s++;
               break;
            }
            if ((s[1] == 'o') && (s[2] == 'n')) {
               s += 2;
               if ((s[1] == 't') && (s[2] == 'h')) {
                  s += 2;
                  if (i.month && (i.month <= 12))
                     str.sprintf("%s", months[(int)i.month - 1].long_name);
                  else
                     str.sprintf("Month%d", i.month - 1);
                  break;
               }
               if (i.month && (i.month <= 12))
                  str.sprintf("%s", months[(int)i.month - 1].abbr);
               else
                  str.sprintf("M%02d", i.month);
               break;
            }
            if ((s[1] == 'O') && (s[2] == 'N')) {
               s += 2;
               if ((s[1] == 'T') && (s[2] == 'H')) {
                  s += 2;
                  if (i.month && (i.month <= 12)) {
                     str.sprintf("%s", months[(int)i.month - 1].upper_long_name);
                  }
                  else
                     str.sprintf("MONTH%d", i.month);
                  break;
               }
               if (i.month && (i.month <= 12)) {
                  str.sprintf("%s", months[(int)i.month - 1].upper_abbr);
               }
               else
                  str.sprintf("M%02d", i.month);
               break;
            }
            str.sprintf("%d", i.month);
            break;
         case 'D':
            if (s[1] == 'D') {
               str.sprintf("%02d", i.day);
               s++;
               break;
            }
            if ((s[1] == 'a') && (s[2] == 'y')) {
               s += 2;
               int wday = qore_date_info::getDayOfWeek(i.year, i.month, i.day);
               str.sprintf("%s", days[wday].long_name);
               break;
            }
            if ((s[1] == 'A') && (s[2] == 'Y')) {
               s += 2;
               int wday = qore_date_info::getDayOfWeek(i.year, i.month, i.day);
               str.sprintf("%s", days[wday].upper_long_name);
               break;
            }
            if ((s[1] == 'y') || (s[1] == 'Y')) {
               s++;
               int wday = qore_date_info::getDayOfWeek(i.year, i.month, i.day);
               str.sprintf("%s", *s == 'Y' ? days[wday].upper_abbr : days[wday].abbr);
               break;
            }
            str.sprintf("%d", i.day);
            break;
         case 'H':
            if (s[1] == 'H') {
               str.sprintf("%02d", i.hour);
               s++;
            }
            else
               str.sprintf("%d", i.hour);
            break;
         case 'h':
            if (s[1] == 'h') {
               str.sprintf("%02d", ampm(i.hour));
               s++;
            }
            else
               str.sprintf("%d", ampm(i.hour));
            break;
         case 'P':
            if (i.hour > 11)
               str.sprintf("PM");
            else
               str.sprintf("AM");
            break;
         case 'p':
            if (i.hour > 11)
               str.sprintf("pm");
            else
               str.sprintf("am");
            break;
         case 'm':
            if (s[1] == 'm') {
               str.sprintf("%02d", i.minute);
               s++;
            }
            else if (s[1] == 's') {
               str.sprintf("%03d", i.us / 1000);
               s++;
            }
            else
               str.sprintf("%d", i.minute);
            break;
         case 'S':
            if (s[1] == 'S') {
               str.sprintf("%02d", i.second);
               s++;
            }
            else
               str.sprintf("%d", i.second);
            break;
         case 'u':
            if (s[1] == 'u') {
               str.sprintf("%03d", i.us / 1000);
               s++;
            }
            else if (s[1] == 's') {
               str.sprintf("%06d", i.us);
               s++;
            }
            else
               str.sprintf("%d", i.us / 1000);
            break;
         case 'x':
            if (s[1] == 'x') {
               str.sprintf("%06d", i.us);
               s++;
            }
            else
               str.sprintf("%d", i.us);
            break;
         case 'y':
            str.sprintf("%06d", i.us);
            str.trim_trailing('0');
            break;
         case 'z':
	    str.sprintf("%s", i.zname);
            break;
	    // add iso8601 UTC offset
	 case 'Z':
	    concatOffset(i.utcoffset, str);
	    break;
         default:
	    str.concat(*s);
            break;
      }
      s++;
   }
}
Example #9
0
void qore_number_private::getAsString(QoreString& str, bool round) const {
   // first check for zero
   if (zero()) {
      str.concat("0");
      return;
   }

   mpfr_exp_t exp;

   char* buf = mpfr_get_str(0, &exp, 10, 0, num, QORE_MPFR_RND);
   if (!buf) {
      numError(str);
      return;
   }
   ON_BLOCK_EXIT(mpfr_free_str, buf);

   //printd(5, "qore_number_private::getAsString(round: %d) this: %p buf: '%s'\n", round, this, buf);

   // if it's a regular number, then format accordingly
   if (number()) {
      int sgn = sign();
      qore_size_t len = str.size() + (sgn < 0 ? 1 : 0);
      //printd(5, "qore_number_private::getAsString() this: %p '%s' exp "QLLD" len: "QLLD"\n", this, buf, exp, len);

      qore_size_t dp = 0;

      str.concat(buf);
      // trim the trailing zeros off the end
      str.trim_trailing('0');
      if (exp <= 0) {
	 exp = -exp;
	 str.insert("0.", len);
	 dp = len + 1;
	 //printd(5, "qore_number_private::getAsString() this: %p str: '%s' exp: "QLLD" dp: "QLLD" len: "QLLD"\n", this, str.getBuffer(), exp, dp, len);
	 if (exp)
	    str.insertch('0', len + 2, exp);
      }
      else {
	 // get remaining length of string (how many characters were added)
	 qore_size_t rlen = str.size() - len;

	 //printd(5, "qore_number_private::getAsString() this: %p str: '%s' exp: "QLLD" rlen: "QLLD"\n", this, str.getBuffer(), exp, rlen);

	 // assert that we have added at least 1 character
	 assert(rlen > 0);
	 if ((qore_size_t)exp > rlen)
	    str.insertch('0', str.size(), exp - rlen);
	 else if ((qore_size_t)exp < rlen) {
	    str.insertch('.', len + exp, 1);
	    dp = len + exp;
	 }
      }
      // try to do some rounding (noise reduction with binary->decimal conversions)
      if (dp && round)
         applyRoundingHeuristic(str, dp, str.size());
   }
   else
      str.concat(buf);

   //printd(5, "qore_number_private::getAsString() this: %p returning '%s'\n", this, str.getBuffer());
}
Example #10
0
int qore_number_private::formatNumberString(QoreString& num, const QoreString& fmt, ExceptionSink* xsink) {
   assert(!num.empty());
   assert(num.getEncoding() == fmt.getEncoding());
   // get the length of the format string in characters (not bytes)
   qore_size_t fl = fmt.length();
   if (fmt.empty() || fl == 2) {
      printd(5, "qore_number_private::formatNumberString() invalid format string: '%s' for number: '%s'\n", fmt.getBuffer(), num.getBuffer());
      return 0;
   }

   // get thousands separator character
   QoreString tsep;
   if (tsep.concat(fmt, 0, 1, xsink))
      return -1;

   // decimal separator
   QoreString dsep;
   // number of digits after the decimal separator
   unsigned prec = 0;
   if (fl > 1) {
      if (dsep.concat(fmt, 1, 1, xsink))
         return -1;
      // get byte offset of start of decimal precision number
      qore_offset_t i = fmt.getByteOffset(2, xsink);
      if (*xsink)
         return -1;
      assert(i >= 2);
      prec = atoi(fmt.getBuffer() + i);
      if (!prec)
         dsep.clear();
   }

   //printd(5, "qore_number_private::formatNumberString() tsep: '%s' dsep: '%s' prec: %d '%s'\n", tsep.getBuffer(), dsep.getBuffer(), prec, num.getBuffer());

   // find decimal point
   qore_offset_t dp = num.find('.');
   if (dp != -1) {
      // how many digits do we have now after the decimal point
      qore_size_t d = num.strlen() - dp - 1;
      assert(d);
      if (d < prec)
         num.addch('0', prec - d);
      else if (d > prec) {
         if ((num[dp + prec + 1] > '4') && (roundUp(num, dp + prec)))
            ++dp;
         num.terminate(dp + prec + 1);
      }
      // now substitute decimal point if necessary
      if (dsep.strlen() != 1 || dsep[0] != '.')
         num.replace(dp, 1, dsep.getBuffer());
   }
   else {
      dp = num.size();
      if (prec) {
         // add decimal point
         num.concat(&dsep, xsink);
         assert(!*xsink);
         // add zeros for significant digits
         num.addch('0', prec);
      }
   }

   // now insert thousands separator
   // start of digits before the decimal point
   qore_offset_t ds = num[0] == '-' ? 1 : 0;

   // work backwards from the decimal point
   qore_offset_t i = dp - 3;
   while (i > ds) {
      num.replace(i, 0, tsep.getBuffer());
      i -= 3;
   }

   //printd(0, "qore_number_private::formatNumberString() ok '%s'\n", num.getBuffer());

   //assert(false); xxx
   return 0;
}
Example #11
0
   DLLLOCAL void appendLocation(QoreString& str) {
      if (loc) {
	 str.concat(" at ");
	 loc->toString(str);
      }
   }
int QoreHashMapOperatorNode::getAsString(QoreString &str, int foff, ExceptionSink *xsink) const {
   str.concat(&map_str);
   return 0;
}
QoreHashNode* qore_httpclient_priv::send_internal(ExceptionSink* xsink, const char* mname, const char* meth, const char* mpath, const QoreHashNode* headers, const void* data, unsigned size, const ResolvedCallReferenceNode* send_callback, bool getbody, QoreHashNode* info, int timeout_ms, const ResolvedCallReferenceNode* recv_callback, QoreObject* obj) {
   assert(!(data && send_callback));

   // check if method is valid
   method_map_t::const_iterator i = method_map.find(meth);
   if (i == method_map.end()) {
       i = additional_methods_map.find(meth);
       if (i == additional_methods_map.end()) {
          xsink->raiseException("HTTP-CLIENT-METHOD-ERROR", "HTTP method (%s) not recognized.", meth);
          return 0;
       }
   }

   // make sure the capitalized version is used
   meth = i->first.c_str();
   bool bodyp = i->second;

   // use the default timeout value if a zero value is given in the call
   if (!timeout_ms)
      timeout_ms = timeout;

   SafeLocker sl(msock->m);
   Queue* cb_queue = msock->socket->getQueue();

   ReferenceHolder<QoreHashNode> nh(new QoreHashNode, xsink);
   bool keep_alive = true;

   bool transfer_encoding = false;

   if (headers) {
      ConstHashIterator hi(headers);
      while (hi.next()) {
	 // if one of the mandatory headers is found, then ignore it
	 strcase_set_t::iterator si = header_ignore.find(hi.getKey());
	 if (si != header_ignore.end())
	    continue;

	 // otherwise set the value in the hash
	 const AbstractQoreNode* n = hi.getValue();
	 if (!is_nothing(n)) {
	    if (!strcasecmp(hi.getKey(), "transfer-encoding"))
	       transfer_encoding = true;

	    nh->setKeyValue(hi.getKey(), n->refSelf(), xsink);

	    if (!strcasecmp(hi.getKey(), "connection") || (proxy_connection.has_url() && !strcasecmp(hi.getKey(), "proxy-connection"))) {
	       const char* conn = get_string_header(xsink, **nh, hi.getKey(), true);
	       if (*xsink) {
		  disconnect_unlocked();
		  return 0;
	       }
	       if (conn && !strcasecmp(conn, "close"))
		  keep_alive = false;
	    }
	 }
      }
   }

   // add default headers if they weren't overridden
   for (header_map_t::const_iterator hdri = default_headers.begin(), e = default_headers.end(); hdri != e; ++hdri) {
      // look in original headers to see if the key was already given
      if (headers) {
	 bool skip = false;
	 ConstHashIterator hi(headers);
	 while (hi.next()) {
	    if (!strcasecmp(hi.getKey(), hdri->first.c_str())) {
	       skip = true;
	       break;
	    }
	 }
	 if (skip)
	    continue;
      }
      // if there is no message body then do not send the "content-type" header
      if (!data && !send_callback && !strcmp(hdri->first.c_str(), "Content-Type"))
	 continue;
      nh->setKeyValue(hdri->first.c_str(), new QoreStringNode(hdri->second.c_str()), xsink);
   }

   // set Transfer-Encoding: chunked if used with a send callback
   if (send_callback && !transfer_encoding)
      nh->setKeyValue("Transfer-Encoding", new QoreStringNode("chunked"), xsink);

   if (!connection.username.empty()) {
      // check for "Authorization" header
      bool auth_found = false;
      if (headers) {
	 ConstHashIterator hi(headers);
	 while (hi.next()) {
	    if (!strcasecmp(hi.getKey(), "Authorization")) {
	       auth_found = true;
	       break;
	    }
	 }
      }

      if (!auth_found) {
	 QoreString tmp;
	 tmp.sprintf("%s:%s", connection.username.c_str(), connection.password.c_str());
	 QoreStringNode* auth_str = new QoreStringNode("Basic ");
	 auth_str->concatBase64(&tmp);
	 nh->setKeyValue("Authorization", auth_str, xsink);
      }
   }

   // save original HTTP method in case we have to issue a CONNECT request to a proxy for an HTTPS connection
   const char* meth_orig = meth;

   bool use_proxy_connect = false;
   const char* proxy_path = 0;
   ReferenceHolder<QoreHashNode> proxy_headers(xsink);
   QoreString hostport;
   if (!proxy_connected && proxy_connection.has_url()) {
      // use CONNECT if we need to make an HTTPS connection from the proxy
      if (!proxy_connection.ssl && connection.ssl) {
	 meth = "CONNECT";
	 use_proxy_connect = true;
	 hostport.concat(connection.host);
	 if (connection.port)
	    hostport.sprintf(":%d", connection.port);
	 proxy_path = hostport.getBuffer();
	 proxy_headers = new QoreHashNode;
	 proxy_headers->setKeyValue("Host", new QoreStringNode(hostport), xsink);

	 addProxyAuthorization(headers, **proxy_headers, xsink);
      }
      else
	 addProxyAuthorization(headers, **nh, xsink);
   }

   bool host_override = headers ? (bool)headers->getKeyValue("Host") : false;

   int code;
   ReferenceHolder<QoreHashNode> ans(xsink);
   int redirect_count = 0;
   const char* location = 0;

   // flag for aborted chunked sends
   bool send_aborted = false;

   while (true) {
      // set host field automatically if not overridden
      if (!host_override)
	 nh->setKeyValue("Host", getHostHeaderValue(), xsink);

      if (info) {
	 info->setKeyValue("headers", nh->copy(), xsink);
	 if (*xsink)
	    return 0;
      }

      //printd(5, "qore_httpclient_priv::send_internal() meth=%s proxy_path=%s mpath=%s upc=%d\n", meth, proxy_path ? proxy_path : "n/a", mpath, use_proxy_connect);
      // send HTTP message and get response header
      if (use_proxy_connect)
	 ans = sendMessageAndGetResponse(meth, proxy_path, *(*proxy_headers), 0, 0, 0, info, true, timeout_ms, code, send_aborted, xsink);
      else
	 ans = sendMessageAndGetResponse(meth, mpath, *(*nh), data, size, send_callback, info, false, timeout_ms, code, send_aborted, xsink);
      if (!ans)
	 return 0;

      if (info) {
	 info->setKeyValue("response-headers", ans->refSelf(), xsink);
	 if (*xsink)
	    return 0;
      }

      if (code >= 300 && code < 400) {
	 disconnect_unlocked();

	 host_override = false;
	 const QoreStringNode* mess = reinterpret_cast<QoreStringNode*>(ans->getKeyValue("status_message"));

	 const QoreStringNode* loc = get_string_header_node(xsink, **ans, "location");
	 if (*xsink)
	    return 0;
	 const char* location = loc && !loc->empty() ? loc->getBuffer() : 0;
	 if (!location) {
	    sl.unlock();
	    const char* msg = mess ? mess->getBuffer() : "<no message>";
	    xsink->raiseException("HTTP-CLIENT-REDIRECT-ERROR", "no redirect location given for status code %d: message: '%s'", code, msg);
	    return 0;
	 }

	 if (cb_queue)
	    do_redirect_event(cb_queue, msock->socket->getObjectIDForEvents(), loc, mess);

	 if (++redirect_count > max_redirects)
	    break;

	 if (set_url_unlocked(location, xsink)) {
	    sl.unlock();
	    const char* msg = mess ? mess->getBuffer() : "<no message>";
	    xsink->raiseException("HTTP-CLIENT-REDIRECT-ERROR", "exception occurred while setting URL for new location '%s' (code %d: message: '%s')", location, code, msg);
	    return 0;
	 }

	 // set redirect info in info hash if present
	 if (info) {
	    QoreString tmp;
	    tmp.sprintf("redirect-%d", redirect_count);
	    info->setKeyValue(tmp.getBuffer(), loc->refSelf(), xsink);
	    if (*xsink)
	       return 0;

	    tmp.clear();
	    tmp.sprintf("redirect-message-%d", redirect_count);
	    info->setKeyValue(tmp.getBuffer(), mess ? mess->refSelf() : 0, xsink);
	 }

	 // FIXME: reset send callback and send_aborted here

	 // set mpath to NULL so that the new path will be taken
	 mpath = 0;
	 continue;
      }
      else if (use_proxy_connect) {
	 meth = meth_orig;
	 use_proxy_connect = false;
	 proxy_path = 0;
	 if (msock->socket->upgradeClientToSSL(0, 0, xsink)) {
	    disconnect_unlocked();
	    return 0;
	 }
	 proxy_connected = true;

	 // remove "Proxy-Authorization" header
	 nh->removeKey("Proxy-Authorization", xsink);
	 if (*xsink)
	    return 0;

	 // try again as if we are talking directly to the client
	 continue;
      }

      break;
   }

   if (code >= 300 && code < 400) {
      sl.unlock();
      const char* mess = get_string_header(xsink, **ans, "status_message");
      if (!mess)
	 mess = "<no message>";
      if (!location)
	 location = "<no location>";
      xsink->raiseException("HTTP-CLIENT-MAXIMUM-REDIRECTS-EXCEEDED", "maximum redirections (%d) exceeded; redirect code %d to '%s' ignored (message: '%s')", max_redirects, code, location, mess);
      return 0;
   }

   // process content-type
   const QoreStringNode* v = get_string_header_node(xsink, **ans, "content-type");
   if (*xsink) {
      disconnect_unlocked();
      return 0;
   }
   //ans->getKeyValue("content-type");   

   // see if there is a character set specification in the content-type header
   if (v) {
      // save original content-type header before processing
      ans->setKeyValue("_qore_orig_content_type", v->refSelf(), xsink);

      const char* str = v->getBuffer();
      const char* p = strstr(str, "charset=");
      if (p && (p == str || *(p - 1) == ';' || *(p - 1) == ' ')) {
	 // move p to start of encoding
	 const char* c = p + 8;
	 char quote = '\0';
	 if (*c == '\'' || *c == '"') {
	    quote = *c;
	    ++c;
	 }
	 QoreString enc;
	 while (*c && *c != ';' && *c != ' ' && *c != quote)
	    enc.concat(*(c++));
	 
	 if (quote && *c == quote)
	    ++c;

	 printd(5, "QoreHttpClientObject::send_intern() setting encoding to '%s' from content-type header: '%s' (cs=%p c=%p %d)\n", enc.getBuffer(), str, p + 8, c);

	 // set new encoding
	 msock->socket->setEncoding(QEM.findCreate(&enc));
	 // strip from content-type
	 QoreStringNode* nc = new QoreStringNode();
	 // skip any spaces before the charset=
	 while (p != str && (*(p - 1) == ' ' || *(p - 1) == ';'))
	    p--;
	 if (p != str)
	    nc->concat(str, p - str);
	 if (*c)
	    nc->concat(c);
	 ans->setKeyValue("content-type", nc, xsink);
	 str = nc->getBuffer();
      }
      // split into a list if ";" characters are present
      p = strchr(str, ';');
      if (p) {
	 bool multipart = false;
	 QoreListNode* l = new QoreListNode();
	 do {
	    // skip whitespace
	    while (*str == ' ') str++;
	    if (str != p) {
	       int len = p - str;
	       check_headers(str, len, multipart, *(*ans), msock->socket->getEncoding(), xsink);
	       l->push(new QoreStringNode(str, len, msock->socket->getEncoding()));
	    }
	    str = p + 1;
	 } while ((p = strchr(str, ';')));
	 // skip whitespace
	 while (*str == ' ') str++;
	 // add last field
	 if (*str) {
	    check_headers(str, strlen(str), multipart, *(*ans), msock->socket->getEncoding(), xsink);
	    l->push(new QoreStringNode(str, msock->socket->getEncoding()));
	 }
	 ans->setKeyValue("content-type", l, xsink);
      }
   }

   // send headers to recv_callback
   if (recv_callback && msock->socket->priv->runHeaderCallback(xsink, mname, *recv_callback, &msock->m, *ans, send_aborted, obj))
      return 0;

   AbstractQoreNode* body = 0;
   const char* content_encoding = 0;

   // do not read any message body for messages that cannot have one
   // rfc 2616 4.4 p1 (http://tools.ietf.org/html/rfc2616#section-4.4)
   /*
     1.Any response message which "MUST NOT" include a message-body (such
     as the 1xx, 204, and 304 responses and any response to a HEAD
     request) is always terminated by the first empty line after the
     header fields, regardless of the entity-header fields present in
     the message.
    */
   //printd(5, "qore_httpclient_priv::send_internal() this: %p bodyp: %d code: %d\n", this, bodyp, code);

   qore_uncompress_to_string_t dec = 0;

   // code >= 300 && < 400 is already handled above
   if (bodyp && (code < 100 || code >= 200) && code != 204) {
      // see if we should do a binary or string read
      content_encoding = get_string_header(xsink, **ans, "content-encoding");
      if (*xsink) {
	 disconnect_unlocked();
	 return 0;
      }

      if (content_encoding) {
	 // check for misuse (? not sure: check RFCs again) of this field by including a character encoding value
	 if (!strncasecmp(content_encoding, "iso", 3) || !strncasecmp(content_encoding, "utf-", 4)) {
	    msock->socket->setEncoding(QEM.findCreate(content_encoding));
	    content_encoding = 0;
	 }
	 else if (!recv_callback) {
	    // only decode message bodies automatically if there is no receive callback
	    if (!strcasecmp(content_encoding, "deflate") || !strcasecmp(content_encoding, "x-deflate"))
	       dec = qore_inflate_to_string;
	    else if (!strcasecmp(content_encoding, "gzip") || !strcasecmp(content_encoding, "x-gzip"))
	       dec = qore_gunzip_to_string;
	    else if (!strcasecmp(content_encoding, "bzip2") || !strcasecmp(content_encoding, "x-bzip2"))
	       dec = qore_bunzip2_to_string;
	 }
      }

      const char* te = get_string_header(xsink, **ans, "transfer-encoding");
      if (*xsink) {
	 disconnect_unlocked();
	 return 0;
      }
   
      // get response body, if any
      const char* cl = get_string_header(xsink, **ans, "content-length");
      if (*xsink) {
	 disconnect_unlocked();
	 return 0;
      }
      int len = cl ? atoi(cl) : 0;
      
      if (cl && cb_queue)
	 do_content_length_event(cb_queue, msock->socket->getObjectIDForEvents(), len);

      if (te && !strcasecmp(te, "chunked")) { // check for chunked response body
	 if (cb_queue)
	    do_event(cb_queue, msock->socket->getObjectIDForEvents(), QORE_EVENT_HTTP_CHUNKED_START);
	 ReferenceHolder<QoreHashNode> nah(xsink);
	 if (recv_callback) {
	    if (content_encoding)
	       msock->socket->priv->readHttpChunkedBodyBinary(timeout_ms, xsink, QORE_SOURCE_HTTPCLIENT, recv_callback, &msock->m, obj);
	    else
	       msock->socket->priv->readHttpChunkedBody(timeout_ms, xsink, QORE_SOURCE_HTTPCLIENT, recv_callback, &msock->m, obj);
	 }
	 else {
	    if (content_encoding)
	       nah = msock->socket->priv->readHttpChunkedBodyBinary(timeout_ms, xsink, QORE_SOURCE_HTTPCLIENT);
	    else
	       nah = msock->socket->priv->readHttpChunkedBody(timeout_ms, xsink, QORE_SOURCE_HTTPCLIENT);
	 }
	 if (cb_queue)
	    do_event(cb_queue, msock->socket->getObjectIDForEvents(), QORE_EVENT_HTTP_CHUNKED_END);
	 
	 if (!nah && !recv_callback) {
	    if (!msock->socket->isOpen())
	       disconnect_unlocked();
	    return 0;
	 }

	 if (info) {
	    info->setKeyValue("chunked", &True, xsink);
	    if (*xsink)
	       return 0;
	 }
	 
	 if (!recv_callback) {
	    body = nah->takeKeyValue("body");
	    ans->merge(*nah, xsink);
	 }
      }
      else if (getbody || len) {
	 if (content_encoding) {
	    SimpleRefHolder<BinaryNode> bobj(msock->socket->recvBinary(len, timeout_ms, xsink));
	    if (!(*xsink) && bobj)
	       body = bobj.release();
	 }
	 else {
	    QoreStringNodeHolder bstr(msock->socket->recv(len, timeout_ms, xsink));
	    if (!(*xsink) && bstr)
	       body = bstr.release();
	 }
	 
	 if (*xsink && !msock->socket->isOpen())
	    disconnect_unlocked();
	 //printf("body=%p\n", body);
      }  
   }

   // check for connection: close header
   if (!keep_alive)
      disconnect_unlocked();
   else {
      const char* conn = get_string_header(xsink, **ans, "connection", true);
      if (*xsink) {
	 disconnect_unlocked();
	 return 0;
      }
      if (conn && !strcasecmp(conn, "close"))
	 disconnect_unlocked();
   }

   sl.unlock();

   // for content-encoding processing we can run unlocked

   // add body to result hash and process content encoding if necessary
   if (body) {
      if (content_encoding) {
	 if (!dec) {
	    if (!recv_callback) {
	       xsink->raiseException("HTTP-CLIENT-RECEIVE-ERROR", "don't know how to handle content-encoding '%s'", content_encoding);
	       ans = 0;
	    }
	 }
	 else {
	    BinaryNode* bobj = reinterpret_cast<BinaryNode*>(body);
	    QoreStringNode* str = dec(bobj, msock->socket->getEncoding(), xsink);
	    bobj->deref();
	    body = str;
	 }
      }

      if (body) {
	 // send data to recv_callback (already unlocked)
	 if (recv_callback) {
	    ReferenceHolder<> bh(body, xsink);
	    if (msock->socket->priv->runDataCallback(xsink, mname, *recv_callback, 0, body, false)
		|| msock->socket->priv->runHeaderCallback(xsink, mname, *recv_callback, 0, 0, send_aborted, obj))
	       return 0;
	 }
	 else
	    ans->setKeyValue("body", body, xsink);
      }
   }

   // do not throw an exception if a receive callback is used
   if (!recv_callback && !*xsink && (code < 100 || code >= 300)) {
      const char* mess = get_string_header(xsink, **ans, "status_message");
      if (!mess)
	 mess = "<no message>";
      assert(!*xsink);

      xsink->raiseExceptionArg("HTTP-CLIENT-RECEIVE-ERROR", ans.release(), "HTTP status code %d received: message: %s", code, mess);
      return 0;
   }

   return *xsink || recv_callback ? 0 : ans.release();
}