/** * Recursively applies context to the given input object. * * @param ctx the context to use. * @param usedCtx the used context values. * @param predicate the related predicate or NULL if none. * @param in the input object. * @param out the contextualized output object. */ static void _applyContext( DynamicObject& ctx, DynamicObject& usedCtx, const char* predicate, DynamicObject& in, DynamicObject& out) { if(in.isNull()) { out.setNull(); } else { // initialize output DynamicObjectType inType = in->getType(); out->setType(inType); if(inType == Map) { // add context to each property in the map char* tmp = NULL; DynamicObjectIterator i = in.getIterator(); while(i->hasNext()) { // compact predicate DynamicObject& next = i->next(); DynamicObject cp(NULL); const char* p; if(strcmp(i->getName(), "@") == 0) { p = "@"; } else { cp = _compactString(ctx, usedCtx, NULL, i->getName(), &tmp); p = cp; } // recurse _applyContext(ctx, usedCtx, p, next, out[p]); } free(tmp); } else if(inType == Array) { // apply context to each object in the array DynamicObjectIterator i = in.getIterator(); while(i->hasNext()) { DynamicObject& next = i->next(); _applyContext(ctx, usedCtx, predicate, next, out->append()); } } // only strings need context applied, numbers & booleans don't else if(inType == String) { // compact string char* tmp = NULL; out = _compactString(ctx, usedCtx, predicate, in->getString(), &tmp); free(tmp); } } }
void DatabaseClient::appendValuesSql(string& sql, DynamicObject& params) { string values; bool first = true; DynamicObjectIterator i = params.getIterator(); while(i->hasNext()) { DynamicObject& name = i->next()["name"]; if(first) { first = false; sql.append(" ("); values.append(" VALUES (?"); } else { sql.append(","); values.append(",?"); } // append unaliased name sql.push_back('`'); sql.append(name->getString()); sql.push_back('`'); } if(!first) { sql.append(")"); values.append(")"); } sql.append(values); }
void DatabaseClient::appendSetSql(string& sql, DynamicObject& params) { bool first = true; DynamicObjectIterator i = params.getIterator(); while(i->hasNext()) { DynamicObject& param = i->next(); if(first) { first = false; sql.append(" "); } else { sql.append(","); } // append name if(param->hasMember("tableAlias")) { sql.append(param["tableAlias"]->getString()); sql.push_back('.'); } sql.append(param["name"]->getString()); sql.append("=?"); } }
/** * Recursively filter input to output. * * @param filter the fitler to use. * @param in the input object. * @param out the filtered output object. */ static void _filter( DynamicObject& filter, DynamicObject& in, DynamicObject& out) { if(!in.isNull()) { DynamicObjectType inType = in->getType(); if(inType == Map) { // check if this object matches filter if(_filterOne(filter, in)) { out["@"]->append(in); } } if(inType == Map || inType == Array) { // filter each object DynamicObjectIterator i = in.getIterator(); while(i->hasNext()) { _filter(filter, i->next(), out); } } } }
void DatabaseClient::appendColumnNames( string& sql, DynamicObject& columnSchemas) { bool first = true; DynamicObjectIterator i = columnSchemas.getIterator(); while(i->hasNext()) { DynamicObject& next = i->next(); if(first) { first = false; sql.append(" "); } else { sql.append(","); } if(next->hasMember("tableAlias")) { sql.append(next["tableAlias"]->getString()); sql.append("."); } sql.append(next["column"]["name"]->getString()); } }
void EventController::unregisterObserver( Observer* observer, DynamicObject& eventTypes) { DynamicObjectIterator i = eventTypes.getIterator(); while(i->hasNext()) { DynamicObject& type = i->next(); unregisterObserver(observer, type->getString()); } }
string X509Certificate::getField(DynamicObject& subjectOrIssuer, const char* sn) { string rval; DynamicObjectIterator i = subjectOrIssuer.getIterator(); while(i->hasNext()) { DynamicObject& entry = i->next(); if(strcmp(entry["type"]->getString(), sn) == 0) { rval = entry["value"]->getString(); break; } } return rval; }
static bool _setDecodedParam( Statement* s, unsigned int index, DynamicObject& param, DynamicObject& value) { bool rval = true; if(param->hasMember("encode")) { // FIXME: could use streams here and handle types other than string, // but the DatabaseClient API might be abandoned before this actually // ever really gets used to that extent // fill byte buffer with initial data ByteBuffer b; b.put(value->getString(), value->length(), true); // apply each decoding // FIXME: optimize this by doing it once and storing it when // defining the schema DynamicObject decode = param["encode"].clone(); decode->reverse(); DynamicObjectIterator i = decode.getIterator(); while(rval && i->hasNext()) { const char* type = i->next()->getString(); // convert hex to binary if(strcmp(type, "hex") == 0) { const char* hex = b.bytes(); unsigned int len = b.length(); unsigned int binLen = (len >> 1) + 1; char bin[binLen]; rval = Convert::hexToBytes(hex, len, bin, binLen); if(rval) { b.clear(); b.put(bin, binLen, true); } } } // only blobs are supported at the moment rval = rval && s->setBlob(index, b.bytes(), b.length()); }
void EventController::registerObserver( Observer* observer, DynamicObject& eventTypes) { DynamicObjectIterator i = eventTypes.getIterator(); while(i->hasNext()) { // register all types DynamicObject& next = i->next(); if(next->getType() == String) { registerObserver(observer, next->getString()); } else if(next->getType() == Map) { registerObserver( observer, next["type"]->getString(), next->hasMember("filter") ? &next["filter"] : NULL); } } }
/** * Recursively removes context from the given input object. * * @param ctx the context to use (changes during recursion as necessary). * @param in the input object. * @param out the normalized output object. * @param bnodeId the last blank node ID used. * * @return true on success, false on failure with exception set. */ static bool _removeContext( DynamicObject& ctx, DynamicObject& in, DynamicObject& out, int& bnodeId) { bool rval = true; if(in.isNull()) { out.setNull(); } else { // initialize output DynamicObjectType inType = in->getType(); out->setType(inType); // update context if(inType == Map && in->hasMember("#")) { ctx = in["#"]; } if(inType == Map) { rval = _normalize(ctx, in, NULL, &out, bnodeId); } else if(inType == Array) { // strip context from each object in the array DynamicObjectIterator i = in.getIterator(); while(i->hasNext()) { DynamicObject& next = i->next(); rval = _removeContext(ctx, next, out->append(), bnodeId); } } } return rval; }
bool Contains::isValid( DynamicObject& obj, ValidatorContext* context) { bool rval = false; // check if objects are equal rval = (obj == mObject); // if not equal, check if target contains the validation object if(!rval && !obj.isNull() && (obj->getType() == Array || obj->getType() == Map)) { DynamicObjectIterator i = obj.getIterator(); while(!rval && i->hasNext()) { DynamicObject& next = i->next(); rval = (next == mObject); } } if(!rval) { DynamicObject detail = context->addError("monarch.validation.NotFound", &obj); detail["validator"] = "monarch.validator.Contains"; detail["expectedValue"] = mObject; detail["message"] = mErrorMessage ? mErrorMessage : "The input object was not equal to or found in the validator."; } if(rval) { context->addSuccess(); } return rval; }
void DatabaseClient::appendOrderSql( string& sql, DynamicObject& params, bool useTableAlias) { bool first = true; DynamicObjectIterator i = params.getIterator(); while(i->hasNext()) { DynamicObject& param = i->next(); if(first) { first = false; sql.append(" ORDER BY "); } else { sql.append(", "); } // append aliased name if(useTableAlias) { sql.append(param["tableAlias"]->getString()); sql.append("."); } sql.append(param["name"]->getString()); // direction if(param["direction"]->getInt32() == ASC) { sql.append(" ASC"); } else { sql.append(" DESC"); } } }
// a helper function that converts message parameters into a dom element static void paramsToElement(SoapMessage& msg, DynamicObject& params, Element& e) { if(params->getType() == Map) { DynamicObjectIterator pi = params.getIterator(); while(pi->hasNext()) { DynamicObject& p = pi->next(); // add param element Element param; param["name"] = pi->getName(); param["namespace"] = msg["namespace"]->getString(); e["children"][pi->getName()]->append(param); paramsToElement(msg, p, param); } } else { // use 0/1 for booleans e["data"] = (params->getType() == Boolean) ? (params->getBoolean() ? "1" : "0") : params->getString(); } }
static bool _filterOne(DynamicObject& filter, DynamicObject& object) { bool rval = true; // loop over all filter properties DynamicObjectIterator i; i = filter.getIterator(); while(rval && i->hasNext()) { DynamicObject& next = i->next(); const char* name = i->getName(); // check if object has property rval = object->hasMember(name); if(rval) { // loop over all filter values DynamicObjectIterator fpi; fpi = next.getIterator(); while(rval && fpi->hasNext()) { DynamicObject& fpnext = fpi->next(); // make sure value appears in object property DynamicObjectIterator opi; opi = object[name].getIterator(); rval = false; while(!rval && opi->hasNext()) { DynamicObject& opnext = opi->next(); rval = (fpnext == opnext); } } } } return rval; }
bool WebServiceContainer::addService( WebServiceRef& service, WebService::SecurityType st, bool initialize, const char* domain) { bool rval = true; if(initialize) { rval = service->initialize(); } if(rval) { // build list of domains to add service to DynamicObject domains(NULL); DynamicObject added; added->setType(Array); if(domain != NULL) { domains = DynamicObject(); domains->append(domain); } else { domains = mDefaultDomains; } HttpRequestServicer* hrs = &(*service); const char* path = hrs->getPath(); // prevent other container access mContainerLock.lockExclusive(); DynamicObjectIterator di = domains.getIterator(); while(rval && di->hasNext()) { const char* dom = di->next()->getString(); rval = internalAddService(service, st, dom); if(rval) { added->append(dom); } } di = rval ? domains.getIterator() : added.getIterator(); while(di->hasNext()) { const char* dom = di->next()->getString(); // success, add domain to http servicer if(rval) { if(st == WebService::Secure || st == WebService::Both) { mHttpConnectionServicer.addRequestServicer(hrs, true, dom); MO_CAT_DEBUG(MO_WS_CAT, "Added secure web service: %s%s", dom, path); } if(st != WebService::Secure) { mHttpConnectionServicer.addRequestServicer(hrs, false, dom); MO_CAT_DEBUG(MO_WS_CAT, "Added non-secure web service: %s%s", dom, path); } } // could not add service to all domains, so remove it else { internalRemoveService(path, st, dom, NULL); } } // permit access again mContainerLock.unlockExclusive(); } // failed to add service if(!rval) { // service was initialized, so clean it up if(initialize) { service->cleanup(); } // set exception ExceptionRef e = new Exception( "Could not add web service.", "monarch.ws.AddWebServiceFailure"); Exception::push(e); } return rval; }
bool SampleService::sendm3u(BtpAction* action, DynamicObject& fileInfos) { bool rval = true; // build m3u file string content; content.append("#EXTM3U\n"); // create play list char temp[22]; FileInfoIterator i = fileInfos.getIterator(); while(i->hasNext()) { FileInfo& fi = i->next(); Media& media = fi["media"]; int sampleLength = media["sampleRange"][1]->getUInt32() - media["sampleRange"][0]->getUInt32(); snprintf(temp, 22, "%u", (sampleLength < 0 ? 0 : sampleLength)); string artist; if(media["contributors"]->hasMember("Performer")) { artist = media["contributors"]["Performer"][0]["name"]->getString(); } else if(media["contributors"]->hasMember("Artist")) { artist = media["contributors"]["Artist"][0]["name"]->getString(); } content.append("#EXTINF:"); content.append(temp); content.push_back(','); content.append(artist); content.append(" - "); content.append(media["title"]->getString()); content.push_back('\n'); // FIXME: get host from configuration or something // needs to be public IP that remote side can access DynamicObject vars; action->getResourceQuery(vars); if(!vars->hasMember("nodeuser")) { BM_ID_SET(vars["nodeuser"], mNode->getDefaultUserId()); } HttpRequestHeader* header = action->getRequest()->getHeader(); string host = header->getFieldValue("X-Forwarded-Host"); if(host.length() == 0) { host = header->getFieldValue("Host"); } content.append("http://"); content.append(host); content.append("/api/3.0/sales/samples/file/"); content.append(fi["mediaId"]->getString()); content.push_back('/'); content.append(fi["id"]->getString()); content.push_back('\n'); } // set up response header HttpResponseHeader* header = action->getResponse()->getHeader(); header->setField("Content-Type", "audio/x-mpegurl"); header->setField( "Content-Disposition", "attachment; filename=bitmunk-sample.m3u"); // create content input stream ByteBuffer b(content.length()); b.put(content.c_str(), content.length(), false); ByteArrayInputStream bais(&b); // send sample action->getResponse()->getHeader()->setStatus(200, "OK"); action->sendResult(&bais); return rval; }
/** * Build a columnSchema Map out of an ORDER specification. * * Input: * [ * { * "name1": order, * ... * }, * ... * ] * * Output: * { * "name1": { * "value * } * } */ static void _buildOrderParams( SchemaObject& schema, DynamicObject& order, DynamicObject& params, const char* tableAlias) { // create shared table alias object DynamicObject taObj(NULL); if(tableAlias != NULL) { taObj = DynamicObject(); taObj = tableAlias; } // build map of names DynamicObject names(Array); { DynamicObjectIterator i = order.getIterator(); while(i->hasNext()) { DynamicObjectIterator ni = i->next().getIterator(); while(ni->hasNext()) { DynamicObject& next = ni->next(); names[ni->getName()]["direction"] = next; } } } // get column details for each name { DynamicObjectIterator i = schema["columns"].getIterator(); while(i->hasNext()) { DynamicObject& next = i->next(); const char* memberName = next["memberName"]->getString(); // get details for column if needed if(names->hasMember(memberName)) { DynamicObject& nameInfo = names[memberName]; nameInfo["name"] = next["name"]; if(tableAlias != NULL) { nameInfo["tableAlias"] = taObj; } } } } // setup params in proper order { DynamicObjectIterator i = order.getIterator(); while(i->hasNext()) { DynamicObjectIterator ni = i->next().getIterator(); while(ni->hasNext()) { ni->next(); params->append(names[ni->getName()]); } } } }
static DynamicObject _compactIri( DynamicObject& ctx, DynamicObject& usedCtx, const char* predicate, const char* encoded, const char* value, char** curie) { DynamicObject rval(NULL); // used to store if brackets should be added bool addBrackets = true; // predicates themselves have no brackets if(predicate == NULL) { addBrackets = false; } // no brackets for "@" or "a" else if(strcmp(predicate, "@") == 0 || strcmp(predicate, "a") == 0) { addBrackets = false; } // check type coercion for IRI else if(ctx->hasMember("#types") && ctx["#types"]->hasMember(predicate)) { DynamicObject& type = ctx["#types"][predicate]; usedCtx["#types"][predicate] = type.clone(); // single type if(type->getType() == String) { string t = _normalizeValue(ctx, type, RDF_TYPE_IRI, NULL, &usedCtx); if(strcmp(t.c_str(), XSD_ANY_URI_NORM) == 0) { addBrackets = false; } } // type coercion info is an ordered list of possible types else { // do not add brackets if URI is a valid type DynamicObjectIterator ti = type.getIterator(); while(ti->hasNext()) { DynamicObject& next = ti->next(); string t = _normalizeValue(ctx, next, RDF_TYPE_IRI, NULL, &usedCtx); if(strcmp(t.c_str(), XSD_ANY_URI_NORM) == 0) { addBrackets = false; } } } } // check the context for a prefix that could shorten the IRI to a CURIE DynamicObjectIterator i = ctx.getIterator(); while(rval.isNull() && i->hasNext()) { DynamicObject& next = i->next(); // skip special context keys (start with '#') const char* name = i->getName(); if(name[0] != '#') { const char* uri = next; const char* ptr = strstr(value, uri); if(ptr != NULL && ptr == value) { size_t ulen = strlen(uri); size_t vlen = strlen(value); if(vlen > ulen) { // add 2 to make room for null-terminator and colon size_t total = strlen(name) + (vlen - ulen) + 2; if(addBrackets) { total += 2; } _realloc(curie, total); const char* format = addBrackets ? "<%s:%s>" : "%s:%s"; snprintf(*curie, total, format, name, ptr + ulen); rval = DynamicObject(); rval = *curie; usedCtx[name] = uri; } else if(vlen == ulen && strcmp(name, "a") == 0) { rval = DynamicObject(); rval = "a"; } } } } // if rval still not set, use encoded or value based on addBrackets if(rval.isNull()) { rval = DynamicObject(); rval = addBrackets ? encoded : value; } return rval; }
bool JsonLd::normalize(DynamicObject& in, DynamicObject& out) { bool rval = true; // initialize output out->setType(Map); out->clear(); // create map to store subjects DynamicObject subjects; subjects->setType(Map); // initialize context DynamicObject ctx(NULL); if(in->hasMember("#")) { ctx = in["#"]; } // put all subjects in an array for single code path DynamicObject input; input->setType(Array); if(in->hasMember("@") && in["@"]->getType() == Array) { input.merge(in["@"], true); } else { input->append(in); } // do normalization int bnodeId = 0; DynamicObjectIterator i = input.getIterator(); while(rval && i->hasNext()) { rval = _normalize(ctx, i->next(), &subjects, NULL, bnodeId); } // build output if(rval) { // single subject if(subjects->length() == 1) { DynamicObject subject = subjects.first(); out.merge(subject, false); // FIXME: will need to check predicates for blank nodes as well... // and fail if they aren't embeds? // strip blank node '@' if(_isBlankNode(out)) { out->removeMember("@"); } } // multiple subjects else { DynamicObject& array = out["@"]; array->setType(Array); i = subjects.getIterator(); while(i->hasNext()) { DynamicObject& next = i->next(); // FIXME: will need to check predicates for blank nodes as well... // and fail if they aren't embeds? // strip blank node '@' if(_isBlankNode(next)) { next->removeMember("@"); } array->append(next); } } } return rval; }
/** * Recursively normalizes the given input object. * * Input: A subject with predicates and possibly embedded other subjects. * Output: Either a map of normalized subjects OR a tree of normalized subjects. * * Normalization Algorithm: * * Replace the existing context if the input has '#'. * * For each key-value: * 1. Split the key on a colon and look for prefix in the context. If found, * expand the key to an IRI, else it is already an IRI, add <>, save the * new predicate to add to the output. * 2. If value is a Map, then it is a subject, set the predicate to the * subject's '@' IRI value and recurse into it. Else goto #3. * 3. Look up the key in the context to find type coercion info. If not found, * goto #4, else goto #5. * 4. Check the value for an integer, double, or boolean. If matched, set * type according to xsd types. If not matched, look for <>, if found, * do CURIE vs. IRI check like #1 and create appropriate value. * 5. If type coercion entry is a string, encode the value using the specific * type. If it is an array, check the type in order of preference. If an * unrecognized type (non-xsd, non-IRI) is provided, throw an exception. * * @param ctx the context to use (changes during recursion as necessary). * @param in the input object. * @param subjects a map of normalized subjects. * @param out a tree normalized objects. * @param bnodeId the last blank node ID used. * * @return true on success, false on failure with exception set. */ static bool _normalize( DynamicObject ctx, DynamicObject& in, DynamicObject* subjects, DynamicObject* out, int& bnodeId) { bool rval = true; // FIXME: validate context (check for non-xsd types in type coercion arrays) if(!in.isNull()) { // update context if(in->hasMember("#")) { ctx = in["#"]; } // vars for normalization state DynamicObject subject(NULL); if(subjects != NULL) { subject = DynamicObject(); subject->setType(Map); // assign blank node ID as needed if(!in->hasMember("@")) { string bnodeKey = _createBlankNodeId(++bnodeId); subject["@"] = bnodeKey.c_str(); } } string nKey; // iterate over key-values DynamicObjectIterator i = in.getIterator(); while(rval && i->hasNext()) { DynamicObject& value = i->next(); const char* key = i->getName(); // skip context keys if(key[0] == '#') { continue; } // get normalized key nKey = _normalizeValue(ctx, key, RDF_TYPE_IRI, NULL, NULL); // put values in an array for single code path DynamicObject values; values->setType(Array); if(value->getType() == Array) { values.merge(value, true); // preserve array structure when not using subjects map if(out != NULL) { (*out)[nKey.c_str()]->setType(Array); } } else { values->append(value); } // normalize all values DynamicObjectIterator vi = values.getIterator(); while(rval && vi->hasNext()) { DynamicObject v = vi->next(); if(v->getType() == Map) { if(subjects != NULL) { // get a normalized subject for the value string vSubject; if(v->hasMember("@")) { // normalize existing subject vSubject = _normalizeValue( ctx, v["@"], RDF_TYPE_IRI, NULL, NULL); } else { // generate the next blank node ID in order to preserve // the blank node embed in the code below vSubject = _createBlankNodeId(bnodeId + 1); } // determine if value is a blank node bool isBNode = _isBlankNode(v); // update non-blank node subject (use value's subject IRI) if(!isBNode) { _setPredicate(subject, nKey.c_str(), vSubject.c_str()); } // recurse rval = _normalize(ctx, v, subjects, out, bnodeId); // preserve embedded blank node if(rval && isBNode) { // remove embed from top-level subjects DynamicObject embed = (*subjects)[vSubject.c_str()]; (*subjects)->removeMember(vSubject.c_str()); embed->removeMember("@"); _setEmbed(subject, nKey.c_str(), embed); } } else { // update out and recurse DynamicObject next = (*out)[nKey.c_str()]; if(value->getType() == Array) { next = next->append(); } else { next->setType(Map); } rval = _normalize(ctx, v, subjects, &next, bnodeId); } } else { _setPredicate( (subjects != NULL) ? subject : *out, nKey.c_str(), _normalizeValue(ctx, v, RDF_TYPE_UNKNOWN, key, NULL).c_str()); } } } // add subject to map if(subjects != NULL) { (*subjects)[subject["@"]->getString()] = subject; } } return rval; }
void DatabaseClient::appendWhereSql( string& sql, DynamicObject& params, bool useTableAlias) { // FIXME: consider allowing for more complex WHERE clauses other // than a bunch of "key=value AND"s concatenated together bool first = true; DynamicObjectIterator i = params.getIterator(); while(i->hasNext()) { DynamicObject& param = i->next(); if(first) { first = false; sql.append(" WHERE "); } else { sql.push_back(' '); sql.append(param["boolOp"]->getString()); sql.push_back(' '); } // append aliased name if(useTableAlias) { sql.append(param["tableAlias"]->getString()); sql.append("."); } sql.append(param["name"]->getString()); // use IN clause if(param["value"]->getType() == Array) { sql.append(" IN ("); bool aFirst = true; DynamicObjectIterator ai = param["value"].getIterator(); while(ai->hasNext()) { ai->next(); if(aFirst) { aFirst = false; sql.append("?"); } else { sql.append(",?"); } } sql.append(")"); } // use parameter operator else { sql.append(param["op"]->getString()); sql.append("?"); } } }
void DownloadStateEventReactor::reprocessRequired(Event& e) { // get user ID UserId userId = BM_USER_ID(e["details"]["userId"]); // ensure operation is done as the user to protect against logout Operation op = mNode->currentOperation(); if(mNode->addUserOperation(userId, op, NULL)) { // lock to prevent double-reprocessing download states // Note: Reprocessing the download state isn't fatal, it just may fail // silently or be annoying by re-assembling already assembled files, etc. mReprocessLock.lock(); { // log activity MO_CAT_DEBUG(BM_EVENTREACTOR_DS_CAT, "Event reactor handling ds 'reprocessRequired' " "event for user %" PRIu64 "...", userId); // get messenger Messenger* messenger = mNode->getMessenger(); // process all queued directives { Url url; url.format("%s/api/3.0/system/directives?nodeuser=%" PRIu64, messenger->getSelfUrl(true).c_str(), userId); DynamicObject directives; if(messenger->get(&url, directives, userId)) { DynamicObjectIterator i = directives.getIterator(); while(i->hasNext()) { DynamicObject& directive = i->next(); // process the directive if it is of type "peerbuy": if(strcmp(directive["type"]->getString(), "peerbuy") == 0) { url.format( "%s/api/3.0/system/directives/process/%s?nodeuser=%" PRIu64, messenger->getSelfUrl(true).c_str(), directive["id"]->getString(), userId); DynamicObject in; if(!messenger->post(&url, NULL, &in, userId)) { // schedule exception event Event e2; e2["type"] = "bitmunk.eventreactor.EventReactor.exception"; e2["details"]["exception"] = Exception::getAsDynamicObject(); mNode->getEventController()->schedule(e2); } } } } } // create a list of download states to reprocess DownloadStateList list; list->setType(Array); if(e["details"]->hasMember("all") && e["details"]["all"]->getBoolean()) { // get all non-processing download states Url url; url.format("%s/api/3.0/purchase/contracts/downloadstates" "?nodeuser=%" PRIu64 "&incomplete=true&processing=false", messenger->getSelfUrl(true).c_str(), userId); DynamicObject in; if(messenger->get(&url, in, userId)) { // append states to list list.merge(in["downloadStates"], true); } else { // schedule exception event Event e2; e2["type"] = "bitmunk.eventreactor.EventReactor.exception"; e2["details"]["exception"] = Exception::getAsDynamicObject(); mNode->getEventController()->schedule(e2); } } else if(e["details"]->hasMember("downloadStateId")) { // reprocess single download state DownloadStateId dsId = e["details"]["downloadStateId"]->getUInt64(); IPurchaseModule* ipm = dynamic_cast<IPurchaseModule*>( mNode->getKernel()->getModuleApi( "bitmunk.purchase.Purchase")); DownloadState ds; ds["id"] = dsId; BM_ID_SET(ds["userId"], userId); if(ipm->populateDownloadState(ds)) { // append state to list list->append(ds); } else { // schedule exception event Event e2; e2["type"] = "bitmunk.eventreactor.EventReactor.exception"; e2["details"]["exception"] = Exception::getAsDynamicObject(); mNode->getEventController()->schedule(e2); } } // reprocess all states in the list DynamicObjectIterator i = list.getIterator(); while(i->hasNext()) { DownloadState& ds = i->next(); DownloadStateId dsId = ds["id"]->getUInt64(); if(ds["downloadStarted"]->getBoolean()) { // do purchase only if no pieces remain if(ds["remainingPieces"]->getUInt32() == 0) { if(!ds["licensePurchased"]->getBoolean() || !ds["dataPurchased"]->getBoolean()) { // do purchase postDownloadStateId(userId, dsId, "purchase"); } else if(!ds["filesAssembled"]->getBoolean()) { // do file assembly postDownloadStateId(userId, dsId, "assemble"); } } else if(!ds["downloadPaused"]->getBoolean()) { // restart download postDownloadStateId(userId, dsId, "download"); } } else if(!ds["initialized"]->getBoolean()) { // initialize download state postDownloadStateId(userId, dsId, "initialize"); } else if(!ds["licenseAcquired"]->getBoolean()) { // acquire license postDownloadStateId(userId, dsId, "license"); } } } mReprocessLock.unlock(); } }
bool JsonWriter::write(DynamicObject& dyno, OutputStream* os, int level) { bool rval = true; if(level < 0) { level = mIndentLevel; } if(dyno.isNull()) { rval = os->write("null", 4); } else { switch(dyno->getType()) { case String: { const char* temp = dyno->getString(); size_t length = strlen(temp); // Note: UTF-8 has a maximum of 6-bytes per character when // encoded in json format ("\u1234") string encoded; encoded.reserve(length); encoded.push_back('"'); char unicode[6]; for(size_t i = 0; i < length; ++i) { unsigned char c = temp[i]; if((c >= 0x5d /* && c <= 0x10FFFF */) || (c >= 0x23 && c <= 0x5B) || (c == 0x21) || (c == 0x20)) { // TODO: check this handles UTF-* properly encoded.push_back(c); } else { encoded.push_back('\\'); switch(c) { case '"': /* 0x22 */ case '\\': /* 0x5C */ // '/' is in the RFC but not required to be escaped //case '/': /* 0x2F */ encoded.push_back(c); break; case '\b': /* 0x08 */ encoded.push_back('b'); break; case '\f': /* 0x0C */ encoded.push_back('f'); break; case '\n': /* 0x0A */ encoded.push_back('n'); break; case '\r': /* 0x0D */ encoded.push_back('r'); break; case '\t': /* 0x09 */ encoded.push_back('t'); break; default: // produces "u01af" (4 digits of 0-filled hex) snprintf(unicode, 6, "u%04x", c); encoded.append(unicode, 5); break; } } } // end string serialization and write encoded string encoded.push_back('"'); rval = os->write(encoded.c_str(), encoded.length()); break; } case Boolean: case Int32: case UInt32: case Int64: case UInt64: case Double: { const char* temp = dyno->getString(); rval = os->write(temp, strlen(temp)); break; } case Map: { // start map serialization rval = (mCompact || dyno->length() == 0) ? os->write("{", 1) : os->write("{\n", 2); // serialize each map member DynamicObjectIterator i = dyno.getIterator(); while(rval && i->hasNext()) { DynamicObject& next = i->next(); // serialize indentation and start serializing member name if((rval = writeIndentation(os, level + 1) && os->write("\"", 1) && os->write(i->getName(), strlen(i->getName())))) { // end serializing member name, serialize member value rval = ((mCompact) ? os->write("\":", 2) : os->write("\": ", 3)) && write(next, os, level + 1); // serialize delimiter if appropriate if(rval && i->hasNext()) { rval = os->write(",", 1); } // add formatting if appropriate if(rval && !mCompact) { rval = os->write("\n", 1); } } } // end map serialization if(dyno->length() > 0) { rval = rval && writeIndentation(os, level); } rval = rval && os->write("}", 1); break; } case Array: { // start array serialization rval = (mCompact || dyno->length() == 0) ? os->write("[", 1) : os->write("[\n", 2); // serialize each array element DynamicObjectIterator i = dyno.getIterator(); while(rval && i->hasNext()) { // serialize indentation and array value rval = writeIndentation(os, level + 1) && write(i->next(), os, level + 1); // serialize delimiter if appropriate if(rval && i->hasNext()) { rval = os->write(",", 1); } // add formatting if appropriate if(rval && !mCompact) { rval = os->write("\n", 1); } } // end array serialization if(dyno->length() > 0) { rval = rval && writeIndentation(os, level); } rval = rval && os->write("]", 1); break; } } } return rval; }