bool CCryptosaurEngine::ValidateAuthDescActual (CDatum dAuthDesc, const CString &sScope, CDatum dUserData) // ValidateAuthDescActual // // Validates that dAuthDesc is valid authorization from the actual user. { CDatum dAuthToken = dAuthDesc.GetElement(FIELD_AUTH_TOKEN); CDatum dCredentials = dAuthDesc.GetElement(FIELD_CREDENTIALS); // Do we have an authToken? if (!dAuthToken.IsNil()) { // Get the key to sign with (the key is guaranteed to exist because we // checked at boot time). CIPInteger *pAuthTokenKey = m_Keys.GetAt(KEY_CRYPTOSAUR_AUTH_TOKEN); // Validate CDatum dData; if (!CCryptosaurInterface::ValidateAuthToken(dAuthToken, *pAuthTokenKey, &dData)) return false; // The proper user? if (!strEquals(strToLower(dData.GetElement(FIELD_USERNAME)), strToLower(dUserData.GetElement(FIELD_USERNAME)))) return false; // AuthToken for actual? if (!dData.GetElement(FIELD_SCOPE).IsNil()) return false; // OK return true; } // Otherwise, we better have credentials else if (!dCredentials.IsNil()) { // Compare credentials against user record CDatum dTrueAuthDesc = dUserData.GetElement(FIELD_AUTH_DESC); if (!((const CIPInteger &)dCredentials == (const CIPInteger &)dTrueAuthDesc.GetElement(FIELD_CREDENTIALS))) return false; // OK return true; } // Otherwise we fail. else return false; }
void CAeonView::CreateSecondaryData (const CTableDimensions &PrimaryDims, const CRowKey &PrimaryKey, CDatum dFullData, SEQUENCENUMBER RowID, CDatum *retdData) // CreateSecondaryData // // Creates the data for a secondary view row. { int i, j; CComplexStruct *pData = new CComplexStruct; // If the list of columns is empty then we just add the primary key if (m_Columns.GetCount() == 0) pData->SetElement(FIELD_PRIMARY_KEY, PrimaryKey.AsDatum(PrimaryDims)); // Otherwise we add all the fields listed in the columns array else { for (i = 0; i < m_Columns.GetCount(); i++) { // The special string "primaryKey" means that we insert the // primary key as a special field. if (strEquals(m_Columns[i], FIELD_PRIMARY_KEY)) pData->SetElement(FIELD_PRIMARY_KEY, PrimaryKey.AsDatum(PrimaryDims)); // The special string "*" means that we insert all existing // fields. else if (strEquals(m_Columns[i], STR_ALL_COLUMNS)) { for (j = 0; j < dFullData.GetCount(); j++) { CDatum dKey = dFullData.GetKey(j); CDatum dValue = dFullData.GetElement(j); if (!dValue.IsNil()) pData->SetElement(dKey, dValue); } } // Add the field by name. else { CDatum dColData = dFullData.GetElement(m_Columns[i]); if (!dColData.IsNil()) pData->SetElement(m_Columns[i], dColData); } } } // Done *retdData = CDatum(pData); }
bool CDatum::IsEqual (CDatum dValue) const // IsEqual // // Returns TRUE if the values are equal { switch (GetBasicType()) { case typeNil: return dValue.IsNil(); case typeTrue: return !dValue.IsNil(); case typeInteger32: case typeInteger64: case typeIntegerIP: case typeDouble: return (dValue.IsNumber() && CNumberValue(*this).Compare(dValue) == 0); case typeString: return (dValue.GetBasicType() == typeString && strEquals(*this, dValue)); case typeDateTime: return (dValue.GetBasicType() == typeDateTime && ((const CDateTime &)*this == (const CDateTime &)dValue)); // LATER case typeArray: case typeBinary: case typeStruct: case typeSymbol: return false; default: ASSERT(false); return false; } }
void CMnemosynthDb::IncorporateDelta (CDatum dPayload) // IncorporateDelta // // Incorporates the delta data { CSmartLock Lock(m_cs); int i; // Get various elements CDatum dCollections = dPayload.GetElement(STR_COLLECTIONS); CDatum dEndpoint = dPayload.GetElement(STR_ENDPOINT); CDatum dEntries = dPayload.GetElement(STR_ENTRIES); DWORD dwProcessID = dPayload.GetElement(FIELD_PROCESS_ID); // Make sure the endpoint exists SEndpoint *pEndpoint = GetOrAddEndpoint(dEndpoint, dwProcessID); DWORD dwOriginalSeq = pEndpoint->dwSeqRecv; DWORD dwMaxSeq = dwOriginalSeq; // Loop over all entries for (i = 0; i < dEntries.GetCount(); i++) { CDatum dEntry = dEntries.GetElement(i); DWORD dwSeq = (DWORD)(int)dEntry.GetElement(3); if (dwSeq > dwOriginalSeq) { // LATER: Detect and resolve conflicts if (dwSeq > dwMaxSeq) dwMaxSeq = dwSeq; const CString &sCollection = dCollections.GetElement((int)dEntry.GetElement(0)); const CString &sKey = dEntry.GetElement(1); CDatum dValue = dEntry.GetElement(2); // If we're not CentralModule and we get a deletion, then delete // right away. If we're CentralModule then we incorporate because // we will send it out on the next update. if (dValue.IsNil() && !m_pProcess->IsCentralModule()) { DeleteEntry(sCollection, sKey); #ifdef DEBUG_MNEMOSYNTH printf("Delete entry: %s/%s\n", (LPSTR)sCollection, (LPSTR)sKey); #endif } // Incorporate else { SEntry *pEntry = GetWriteEntry(sCollection, sKey); pEntry->dValue = dValue; pEntry->dwOwnerID = pEndpoint->dwID; pEntry->dwSequence = dwSeq; #ifdef DEBUG_MNEMOSYNTH printf("Modify entry: %s/%s [owner = %s seq = %d]\n", (LPSTR)sCollection, (LPSTR)sKey, (LPSTR)pEndpoint->sName, dwSeq); #endif } } #ifdef DEBUG_MNEMOSYNTH else { printf("%s: Skipping %s because %d <= %d\n", (LPSTR)m_pProcess->GetModuleName(), (LPSTR)dCollections.GetElement((int)dEntry.GetElement(0)).AsString(), dwSeq, dwOriginalSeq); } #endif } // Done pEndpoint->dwSeqRecv = dwMaxSeq; }
bool CUserInfoSession::OnProcessMessage (const SArchonMessage &Msg) // OnProcessMessage // // We received a reply from Aeon { int i; // If this is an error, then we return the error back to the client if (IsError(Msg)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, Msg.dPayload); return false; } // If we're waiting for the user record, then see if we can process it now. if (m_iState == stateWaitingForUserRecord) { // Cryptosaur.getUser if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_GET_USER)) { CDatum dUserData = Msg.dPayload; // If the user does not exist, then we return Nil if (dUserData.IsNil()) { SendMessageReply(MSG_REPLY_DATA, CDatum()); return false; } // Generate a sanitized user record CComplexStruct *pReply = new CComplexStruct; pReply->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME)); // Sanitize rights CDatum dRights = dUserData.GetElement(FIELD_RIGHTS); if (!m_sScope.IsEmpty()) { CComplexArray *pRights = new CComplexArray; for (i = 0; i < dRights.GetCount(); i++) if (strStartsWith(dRights.GetElement(i), m_sScope)) pRights->Insert(dRights.GetElement(i)); pReply->SetElement(FIELD_RIGHTS, CDatum(pRights)); } else pReply->SetElement(FIELD_RIGHTS, dRights); // Done SendMessageReply(MSG_REPLY_DATA, CDatum(pReply)); return false; } // If we get back nil then the user does not exist. else if (Msg.dPayload.IsNil()) { SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, strPattern(ERR_UNKNOWN_USERNAME, m_sUsername)); return false; } // Otherwise, we handle the result based on the original message else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_CHECK_PASSWORD_SHA1)) { // Get the parameters from the original message CDatum dChallenge = GetOriginalMsg().dPayload.GetElement(1); CDatum dResponse = GetOriginalMsg().dPayload.GetElement(2); // Get the password has from the response CDatum dAuthDesc = Msg.dPayload.GetElement(FIELD_AUTH_DESC); CDatum dPasswordHash = dAuthDesc.GetElement(FIELD_CREDENTIALS); // Create a response to the challenge based on the password hash that // we have stored. CDatum dCorrect = CAI1Protocol::CreateSHAPasswordChallengeResponse(dPasswordHash, dChallenge); // Compare the correct response to the actual if ((const CIPInteger &)dResponse == (const CIPInteger &)dCorrect) return UpdateLoginSuccess(stateWaitingForSuccessUpdate); else return UpdateLoginFailure(); } // Cryptosaur.hasRights else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_HAS_RIGHTS)) { CDatum dRights = Msg.dPayload.GetElement(FIELD_RIGHTS); CDatum dRightsRequired = m_dPayload.GetElement(1); // Get the rights from the user CAttributeList Rights; dRights.AsAttributeList(&Rights); // Check for (i = 0; i < dRightsRequired.GetCount(); i++) { if (!Rights.HasAttribute(dRightsRequired.GetElement(i))) { SendMessageReply(MSG_REPLY_DATA, CDatum()); return false; } } // We have all rights SendMessageReply(MSG_REPLY_DATA, CDatum(CDatum::constTrue)); return false; } // Cryptosaur.loginUser else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_LOGIN_USER)) { // Get the parameters from the original message CDatum dRequestAuthDesc = GetOriginalMsg().dPayload.GetElement(1); CDatum dCredentials = dRequestAuthDesc.GetElement(FIELD_CREDENTIALS); CDatum dChallengeCredentials = dRequestAuthDesc.GetElement(FIELD_CHALLENGE_CREDENTIALS); CDatum dPassword = dRequestAuthDesc.GetElement(FIELD_PASSWORD); m_bActual = !dRequestAuthDesc.GetElement(FIELD_ACTUAL).IsNil(); if (!dRequestAuthDesc.GetElement(FIELD_AUTH_TOKEN_INFINITE).IsNil()) m_dwAuthTokenLifetime = 0; else { m_dwAuthTokenLifetime = (DWORD)(int)dRequestAuthDesc.GetElement(FIELD_AUTH_TOKEN_LIFETIME); if (m_dwAuthTokenLifetime == 0) m_dwAuthTokenLifetime = DEFAULT_AUTH_TOKEN_TIMEOUT; } // If we're not actual and have no scope, then we can't continue if (!m_bActual && m_sScope.IsEmpty()) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, ERR_SCOPE_REQUIRED); return false; } // User data CDatum dUserData = Msg.dPayload; CDatum dAuthDesc; if (m_bActual) dAuthDesc = dUserData.GetElement(FIELD_AUTH_DESC); else dAuthDesc = dUserData.GetElement(strPattern("%s%s", m_sScope, FIELD_AUTH_DESC)); // If we have no authdesc, then we can't continue. This is likely // because the client is in a sandbox that the user has not registered // with. We treat it the same as a username/password failure. if (dAuthDesc.IsNil()) { SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD); return false; } // If we've failed more than 5 consecutive times, we may need to delay // the next login attempt. if ((int)dUserData.GetElement(FIELD_LOGIN_FAILURE_COUNT) > MAX_LOGIN_ATTEMPTS) { CDateTime LastLoginFailure = dUserData.GetElement(FIELD_LAST_LOGIN_FAILURE_ON); CTimeSpan TimeSinceLastFailure = timeSpan(LastLoginFailure, CDateTime(CDateTime::Now)); // If it has not been at least 1 hour, we return an error. if (TimeSinceLastFailure.Days() == 0 && TimeSinceLastFailure.Seconds() < LOGIN_TIMEOUT) { // Timeout SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_FAILURE_TIMEOUT); return false; } } // If we have straight credentials, then just compare bool bSuccess; if (!dCredentials.IsNil()) bSuccess = ((const CIPInteger &)dCredentials == (const CIPInteger &)dAuthDesc.GetElement(FIELD_CREDENTIALS)); // Otherwise, we compare against the challenge else if (!dChallengeCredentials.IsNil()) { // Get the challenge. If not provided then we get it from the user // record. CDatum dChallenge = GetOriginalMsg().dPayload.GetElement(2); if (dChallenge.IsNil()) { // Get the expiration time of the challenge const CDateTime &Expires = dAuthDesc.GetElement(FIELD_CHALLENGE_EXPIRATION); if (Expires < CDateTime(CDateTime::Now)) { SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD); return false; } dChallenge = dAuthDesc.GetElement(FIELD_CHALLENGE); } // Create a response to the challenge based on the password hash that // we have stored. CDatum dCorrectChallenge = CAI1Protocol::CreateSHAPasswordChallengeResponse( dAuthDesc.GetElement(FIELD_CREDENTIALS), dChallenge ); bSuccess = ((const CIPInteger &)dChallengeCredentials == (const CIPInteger &)dCorrectChallenge); } // Otherwise we expect a clear text password else if (!dPassword.IsNil()) { // We have to hash the password to compare with credentials. CIPInteger Credentials; CCryptosaurInterface::CreateCredentials(dUserData.GetElement(FIELD_USERNAME), dPassword, &Credentials); // Compare bSuccess = (Credentials == (const CIPInteger &)dAuthDesc.GetElement(FIELD_CREDENTIALS)); } else bSuccess = false; // Success or failure if (bSuccess) return UpdateLoginSuccess(stateWaitingForCredentials); else return UpdateLoginFailure(); } // Can never get here. else { ASSERT(false); return false; } } // Otherwise, if we're waiting for the user record update, then continue else if (m_iState == stateWaitingForSuccessUpdate) { // Since we succeeded, we send the user sanitized user record back. SendMessageReply(MSG_REPLY_DATA, CreateSanitizedUserRecord(Msg.dPayload)); return false; } // If we're waiting for credentials, compose them else if (m_iState == stateWaitingForCredentials) { // The mutation returns the full record CDatum dUserData = Msg.dPayload; // Compute the result CComplexStruct *pAuthToken = new CComplexStruct; pAuthToken->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME)); pAuthToken->SetElement(FIELD_RIGHTS, dUserData.GetElement(FIELD_RIGHTS)); if (!m_bActual) pAuthToken->SetElement(FIELD_SCOPE, m_sScope); CDatum dAuthToken = m_pEngine->GenerateAuthToken(CDatum(pAuthToken), m_dwAuthTokenLifetime); // Compose a basic user record CComplexStruct *pReply = new CComplexStruct; pReply->SetElement(FIELD_AUTH_TOKEN, dAuthToken); pReply->SetElement(FIELD_RIGHTS, dUserData.GetElement(FIELD_RIGHTS)); pReply->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME)); // Send the reply SendMessageReply(MSG_REPLY_DATA, CDatum(pReply)); // Done return false; } // Otherwise, failure else if (m_iState == stateWaitingForFailureUpdate) { CDatum dUserData = Msg.dPayload; // If we've exceeded our limit, log it int iAttempts = (int)dUserData.GetElement(FIELD_LOGIN_FAILURE_COUNT); if (iAttempts > MAX_LOGIN_ATTEMPTS) GetProcessCtx()->Log(MSG_LOG_INFO, strPattern(ERR_USERNAME_TIMEOUT, m_sUsername, iAttempts)); // Send a failure SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD); return false; } // Can never get here else { ASSERT(false); return false; } }
void CAeonView::Insert (const CTableDimensions &PrimaryDims, CHexeProcess &Process, const CRowKey &PrimaryKey, CDatum dData, CDatum dOldData, SEQUENCENUMBER RowID, bool *retbRecoveryFailed, CString *retsError) // Insert // // Insert a row // // NOTE: We cannot fail here because callers should have called CanInsert // (above). { int i; CString sError; *retbRecoveryFailed = false; // If this is a primary view then all we have to do is insert the row if (!IsSecondaryView()) { m_pRows->Insert(PrimaryKey, dData, RowID); if (!m_Recovery.Insert(PrimaryKey, dData, RowID, retsError)) *retbRecoveryFailed = true; } // Otherwise, it's more complicated. else { // If this view is not yet up to date then we skip insertion (we will // insert all rows later). if (!IsUpToDate()) return; // If we are updating an existing row, then we need to remove the old // value. Note that it is OK if OldKey and NewKey end up being the // same; we just end up overwriting it. if (!dOldData.IsNil()) { // Generate a key for the old value. If all key values are non-nil // then we update the old value. TArray<CRowKey> OldKeys; if (CreateSecondaryKeys(Process, ComputeColumns(Process, dOldData), RowID, &OldKeys)) { // Now delete the old value (by writing out Nil) for (i = 0; i < OldKeys.GetCount(); i++) { m_pRows->Insert(OldKeys[i], CDatum(), RowID); if (!m_Recovery.Insert(OldKeys[i], CDatum(), RowID, retsError)) *retbRecoveryFailed = true; } } } // Save the new value (only if not Nil) if (!dData.IsNil()) { // Compute columns dData = ComputeColumns(Process, dData); // Generate a key for the new value. If all key values are non-nil // then we insert the row into the secondary view. TArray<CRowKey> NewKeys; if (CreateSecondaryKeys(Process, dData, RowID, &NewKeys)) { // Generate data for secondary key CDatum dViewData; CreateSecondaryData(PrimaryDims, PrimaryKey, dData, RowID, &dViewData); // Insert it for (i = 0; i < NewKeys.GetCount(); i++) { m_pRows->Insert(NewKeys[i], dViewData, RowID); if (!m_Recovery.Insert(NewKeys[i], dViewData, RowID, retsError)) *retbRecoveryFailed = true; } } } } }
bool CAeonView::InitAsSecondaryView (CDatum dDesc, CHexeProcess &Process, const CString &sRecoveryFilespec, bool bForceUpdate, CString *retsError) // InitAsSecondaryView // // Initializes a secondary view. { int i; ASSERT(m_Dims.GetCount() == 0); // Get the name m_sName = dDesc.GetElement(FIELD_NAME); m_bUsesListKeys = false; // Parse the x dimension CDatum dimDesc = dDesc.GetElement(FIELD_X); if (!dimDesc.IsNil()) { SDimensionDesc *pDimDesc = m_Dims.Insert(); CDatum *pKey = m_Keys.Insert(); if (!CAeonTable::ParseDimensionDescForSecondaryView(dimDesc, Process, pDimDesc, pKey, retsError)) { m_bInvalid = true; return false; } if (pDimDesc->iKeyType == keyListUTF8) m_bUsesListKeys = true; // Parse the y dimension dimDesc = dDesc.GetElement(FIELD_Y); if (!dimDesc.IsNil()) { pDimDesc = m_Dims.Insert(); pKey = m_Keys.Insert(); if (!CAeonTable::ParseDimensionDescForSecondaryView(dimDesc, Process, pDimDesc, pKey, retsError)) { m_bInvalid = true; return false; } if (pDimDesc->iKeyType == keyListUTF8) m_bUsesListKeys = true; // Parse z dimension dimDesc = dDesc.GetElement(FIELD_Z); if (!dimDesc.IsNil()) { pDimDesc = m_Dims.Insert(); pKey = m_Keys.Insert(); if (!CAeonTable::ParseDimensionDescForSecondaryView(dimDesc, Process, pDimDesc, pKey, retsError)) { m_bInvalid = true; return false; } if (pDimDesc->iKeyType == keyListUTF8) m_bUsesListKeys = true; } } } // If we don't have at least one dimension then this is an error else { *retsError = ERR_DIMENSIONS_REQUIRED; m_bInvalid = true; return false; } // Secondary views always have an extra dimension. We use the rowID as a // way to break ties in the other parts of the key (since secondary keys // need not be unique). SDimensionDesc *pDimDesc = m_Dims.Insert(); pDimDesc->iKeyType = keyInt64; pDimDesc->iSort = AscendingSort; // Parse columns CDatum dColumns = dDesc.GetElement(FIELD_COLUMNS); for (i = 0; i < dColumns.GetCount(); i++) { const CString &sCol = dColumns.GetElement(i); if (!sCol.IsEmpty()) m_Columns.Insert(sCol); } // Computed columns m_ComputedColumns = dDesc.GetElement(FIELD_COMPUTED_COLUMNS); // We need to set the global environment because it got loaded under a // different process. if (strEquals(m_ComputedColumns.GetTypename(), TYPENAME_HEXE_FUNCTION)) m_ComputedColumns.SetElement(FIELD_GLOBAL_ENV, Process.GetGlobalEnv()); // Exclude nil? m_bExcludeNil = !dDesc.GetElement(FIELD_EXCLUDE_NIL_KEYS).IsNil(); // Initialize rows if (!InitRows(sRecoveryFilespec, NULL, retsError)) { m_bInvalid = true; return false; } // Set up update. If we have stored an update sequence number then it means // that we are loading an old view that has not yet been fully updated. // // Otherwise we take the updating number passed in. m_bUpdateNeeded = (bForceUpdate ? true : !dDesc.GetElement(FIELD_UPDATE_NEEDED).IsNil()); // Done return true; }
bool CAeonView::InitAsPrimaryView (CDatum dDesc, const CString &sRecoveryFilespec, int *retiRowsRecovered, CString *retsError) // InitAsPrimaryView // // Initialize from a descriptor { int i; ASSERT(m_Dims.GetCount() == 0); // Parse the x dimension CDatum dimDesc = dDesc.GetElement(FIELD_X); if (!dimDesc.IsNil()) { SDimensionDesc *pDimDesc = m_Dims.Insert(); if (!CAeonTable::ParseDimensionDesc(dimDesc, pDimDesc, retsError)) return false; // Parse the y dimension dimDesc = dDesc.GetElement(FIELD_Y); if (!dimDesc.IsNil()) { pDimDesc = m_Dims.Insert(); if (!CAeonTable::ParseDimensionDesc(dimDesc, pDimDesc, retsError)) return false; // Parse z dimension dimDesc = dDesc.GetElement(FIELD_Z); if (!dimDesc.IsNil()) { pDimDesc = m_Dims.Insert(); if (!CAeonTable::ParseDimensionDesc(dimDesc, pDimDesc, retsError)) return false; } } } // If we don't have at least one dimension then this is an error else { *retsError = ERR_DIMENSIONS_REQUIRED; return false; } // We do not support list types for primary views (only secondary views) for (i = 0; i < m_Dims.GetCount(); i++) if (m_Dims[i].iKeyType == keyListUTF8) { *retsError = ERR_PRIMARY_KEY_CANT_BE_LIST; return false; } // Initialize rows if (!InitRows(sRecoveryFilespec, retiRowsRecovered, retsError)) return false; // Done return true; }
bool CAeonView::CreateSecondaryKeys (CHexeProcess &Process, CDatum dData, SEQUENCENUMBER RowID, TArray<CRowKey> *retKeys) // CreateSecondaryKeys // // Creates a secondary key from the data and rowID. We return TRUE if all of // the key values are non-nil. FALSE if one or more values are nil. { int i; bool bAllValid = true; // Pull the dimensions from the data TArray<CDatum> KeyData; for (i = 0; i < m_Keys.GetCount(); i++) { CDatum dValue; CDatum dKeyDesc = m_Keys[i]; // If this is a function then we need to evaluate it. if (dKeyDesc.CanInvoke()) { TArray<CDatum> Args; Args.Insert(dData); CHexeProcess::ERunCodes iRun = Process.Run(dKeyDesc, Args, &dValue); switch (iRun) { case CHexeProcess::runOK: // dValue is a valid value for a key break; case CHexeProcess::runError: dValue = CDatum(strPattern("(%s)", dValue.AsString())); break; default: dValue = CDatum(STR_ERROR_KEY); } } // Otherwise this specifies a field in the data to use as a key else dValue = dData.GetElement((const CString &)dKeyDesc); // We don't support Nil keys, so we have to replace these with a // a special value. if (dValue.IsNil()) { dValue = CDatum(STR_EMPTY_KEY); // If we're not valid if we're excluding nil keys if (m_bExcludeNil) bAllValid = false; } // Add to list KeyData.Insert(dValue); } // Generate the keys. // If we use list keys then we need to create permuted keys retKeys->DeleteAll(); if (m_bUsesListKeys) CreatePermutedKeys(KeyData, 0, TArray<CDatum>(), RowID, retKeys); // Otherwise we just create the key normally else { CRowKey *pNewKey = retKeys->Insert(); CRowKey::CreateFromDatumAndRowID(m_Dims, KeyData, RowID, pNewKey); } // Done return bAllValid; }
void CAeonView::CreatePermutedKeys (const TArray<CDatum> &KeyData, int iDim, const TArray<CDatum> &PrevKey, SEQUENCENUMBER RowID, TArray<CRowKey> *retKeys) // CreatePermutedKeys // // Adds keys to retKeys by permuting any list values. { int i; // If we're done, then add the key if (iDim == KeyData.GetCount()) { CRowKey *pNewKey = retKeys->Insert(); CRowKey::CreateFromDatumAndRowID(m_Dims, PrevKey, RowID, pNewKey); } // Otherwise, we generate the keys just for the current dimension and // recurse. else { switch (m_Dims[iDim].iKeyType) { // For list keys, add all the values of the list as separate keys case keyListUTF8: { CDatum dList = KeyData[iDim]; // If nil, just add as a single nil key if (dList.IsNil()) { TArray<CDatum> NewKey(PrevKey); NewKey.Insert(dList); CreatePermutedKeys(KeyData, iDim + 1, NewKey, RowID, retKeys); } // Otherwise, add all values else { TArray<CDatum> NewKey(PrevKey); NewKey.Insert(CDatum()); for (i = 0; i < dList.GetCount(); i++) { NewKey[iDim] = dList.GetElement(i); CreatePermutedKeys(KeyData, iDim + 1, NewKey, RowID, retKeys); } } break; } // For non-list keys we just continue adding them default: { TArray<CDatum> NewKey(PrevKey); NewKey.Insert(KeyData[iDim]); CreatePermutedKeys(KeyData, iDim + 1, NewKey, RowID, retKeys); } } } }
bool CHexeMarkupEvaluator::ProcessResult (SHTTPRequestCtx &Ctx, CHexeProcess::ERunCodes iRun, CDatum dResult) // ProcessResult // // Process the result of an evaluation. Returns TRUE if processing should // continue; FALSE if we need RPC or are done processing. { // If we have more async calls then return if (iRun == CHexeProcess::runAsyncRequest) { Ctx.iStatus = pstatRPCReady; Ctx.sRPCAddr = dResult.GetElement(0); Ctx.RPCMsg.sMsg = dResult.GetElement(1); Ctx.RPCMsg.dPayload = dResult.GetElement(2); Ctx.RPCMsg.dwTicket = 0; Ctx.RPCMsg.sReplyAddr = NULL_STR; return false; } // Otherwise, process the result based on the directive that we're // evaluating. bool bResult = true; switch (m_iProcessing) { case tagEval: OutputDatum(dResult); break; case tagFile: // If this is an error, then we return with 404 if (iRun == CHexeProcess::runError) { Ctx.iStatus = pstatResponseReady; Ctx.Response.InitResponse(http_NOT_FOUND, dResult.AsString()); bResult = false; } // If the result is a list then we expect a fileDesc and fileData. else if (dResult.GetCount() >= 2) { Ctx.iStatus = pstatFileDataReady; Ctx.dFileDesc = dResult.GetElement(0); Ctx.dFileData = dResult.GetElement(1); Ctx.AdditionalHeaders = m_Headers; bResult = false; } // Otherwise we expect a filePath else { Ctx.iStatus = pstatFilePathReady; Ctx.sFilePath = dResult; Ctx.AdditionalHeaders = m_Headers; bResult = false; } break; case tagHeader: bResult = ProcessHeader(Ctx, dResult); break; case tagIf: m_iIfLevel++; if (dResult.IsNil()) m_iIfLevelEnd = m_iIfLevel; break; case tagRedirect: // If this is an error, then we return with 404 if (iRun == CHexeProcess::runError) { Ctx.iStatus = pstatResponseReady; Ctx.Response.InitResponse(http_NOT_FOUND, dResult.AsString()); bResult = false; } // Otherwise, we expect a string containing the new address. else if (!dResult.IsNil()) { m_dwResponseCode = http_MOVED_PERMANENTLY; m_sResponseMsg = STR_MOVED_PERMANENTLY; AddHeader(HEADER_LOCATION, dResult); } break; default: ASSERT(false); } m_iProcessing = tagNone; return bResult; }
void CAeonEngine::MsgGetRows (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx) // MsgGetRows // // Aeon.getRows {tableAndView} {key} {count} // Aeon.getMoreRows {tableAndView} {lastKey} {count} { int i; CAeonTable *pTable; DWORD dwViewID; if (!ParseTableAndView(Msg, pSecurityCtx, Msg.dPayload.GetElement(0), &pTable, &dwViewID)) return; // Get the row limits int iRowCount; TArray<int> Limits; CDatum dLimits = Msg.dPayload.GetElement(2); if (dLimits.IsNil()) iRowCount = -1; else if (dLimits.GetCount() <= 1) { iRowCount = (int)dLimits.GetElement(0); if (iRowCount <= 0) iRowCount = -1; } else { iRowCount = (int)dLimits.GetElement(0); if (iRowCount <= 0) iRowCount = -1; Limits.InsertEmpty(dLimits.GetCount() - 1); for (i = 1; i < dLimits.GetCount(); i++) Limits[i - 1] = (int)dLimits.GetElement(i); } // Set up flags and options DWORD dwFlags = 0; dwFlags |= (strEquals(Msg.sMsg, MSG_AEON_GET_ROWS) ? 0 : CAeonTable::FLAG_MORE_ROWS); CDatum dOptions = Msg.dPayload.GetElement(3); for (i = 0; i < dOptions.GetCount(); i++) { if (strEquals(dOptions.GetElement(i), OPTION_INCLUDE_KEY)) dwFlags |= CAeonTable::FLAG_INCLUDE_KEY; else if (strEquals(dOptions.GetElement(i), OPTION_NO_KEY)) dwFlags |= CAeonTable::FLAG_NO_KEY; else { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(ERR_INVALID_GET_ROWS_OPTION, Msg.sMsg, dOptions.GetElement(i).AsString()), Msg); return; } } // Ask the table CDatum dResult; CString sError; if (!pTable->GetRows(dwViewID, Msg.dPayload.GetElement(1), iRowCount, Limits, dwFlags, &dResult, &sError)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg); return; } // Done SendMessageReply(MSG_REPLY_DATA, dResult, Msg); }
void CAeonEngine::MsgFileDownload (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx) // MsgFileDownload // // Aeon.fileDownload {filePath} [{fileDownloadDesc}] { CString sError; // Get the filePath CString sTable; CString sFilePath; if (!CAeonTable::ParseFilePath(Msg.dPayload.GetElement(0), &sTable, &sFilePath, &sError)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_PARSING_FILE_PATH, sError), Msg); return; } // Make sure we are allowed access to this table if (!ValidateTableAccess(Msg, pSecurityCtx, sTable)) return; // See if we have a fileDownloadDesc CDatum dFileDownloadDesc = Msg.dPayload.GetElement(1); int iPartialMaxSize = (dFileDownloadDesc.IsNil() ? -1 : (int)dFileDownloadDesc.GetElement(FIELD_PARTIAL_MAX_SIZE)); int iPartialPos = (dFileDownloadDesc.IsNil() ? 0 : (int)dFileDownloadDesc.GetElement(FIELD_PARTIAL_POS)); if (iPartialPos < 0) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, ERR_INVALID_PARAMETERS, Msg); return; } CDateTime IfModifiedAfter = dFileDownloadDesc.GetElement(FIELD_IF_MODIFIED_AFTER); // Get the table CAeonTable *pTable; if (!FindTable(sTable, &pTable)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_UNKNOWN_TABLE, sTable), Msg); return; } #ifdef DEBUG_BLOB_PERF DWORD dwStart = ::sysGetTickCount(); #endif // Get the data if (!pTable->GetFileData(sFilePath, iPartialMaxSize, iPartialPos, IfModifiedAfter, &dFileDownloadDesc, false, &sError)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg); return; } #ifdef DEBUG_BLOB_PERF DWORD dwTime = ::sysGetTicksElapsed(dwStart); if (dwTime > 100) Log(MSG_LOG_INFO, strPattern("GetFileData %s %D bytes took %D ms.", sFilePath, dFileDownloadDesc.GetElement(FIELD_DATA).GetBinarySize(), dwTime)); #endif // Done SendMessageReply(MSG_AEON_FILE_DOWNLOAD_DESC, dFileDownloadDesc, Msg); }
void CAeonEngine::MsgTranspaceDownload (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx) // MsgTranspaceDownload // // Transpace.download {address} {originalAddress} [{fileDownloadDesc}] { CString sError; CString sAddress = Msg.dPayload.GetElement(0); CString sOriginalAddress = Msg.dPayload.GetElement(1); CDatum dFileDownloadDesc = Msg.dPayload.GetElement(2); // Parse the address to get the path CString sFullPath; if (!CTranspaceInterface::ParseAddress(sAddress, NULL, &sFullPath)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_PARSING_TRANSPACE_ADDR, sAddress), Msg); return; } // Get the filePath CString sTable; CString sFilePath; if (!CAeonTable::ParseFilePath(sFullPath, &sTable, &sFilePath, &sError)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_PARSING_FILE_PATH, sError), Msg); return; } // Make sure we are allowed access to this table if (!ValidateTableAccess(Msg, pSecurityCtx, sTable)) return; // See if we have a fileDownloadDesc int iPartialMaxSize = (dFileDownloadDesc.IsNil() ? -1 : (int)dFileDownloadDesc.GetElement(FIELD_PARTIAL_MAX_SIZE)); int iPartialPos = (dFileDownloadDesc.IsNil() ? 0 : (int)dFileDownloadDesc.GetElement(FIELD_PARTIAL_POS)); if (iPartialPos < 0) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, ERR_INVALID_PARAMETERS, Msg); return; } CDateTime IfModifiedAfter = dFileDownloadDesc.GetElement(FIELD_IF_MODIFIED_AFTER); // Get the table CAeonTable *pTable; if (!FindTable(sTable, &pTable)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_UNKNOWN_TABLE, sTable), Msg); return; } // Get the data if (!pTable->GetFileData(sFilePath, iPartialMaxSize, iPartialPos, IfModifiedAfter, &dFileDownloadDesc, true, &sError)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg); return; } // Add the original address to the download descriptor // // NOTE: It is OK to modify this field because we know that GetFileData // created a new structure. dFileDownloadDesc.GetElement(FIELD_FILE_DESC).SetElement(FIELD_ADDRESS, sOriginalAddress); #ifdef DEBUG_FILES Log(MSG_LOG_DEBUG, strPattern("[%x]: Transpace.download address=%s original=%s filePath=%s", Msg.dwTicket, sAddress, sOriginalAddress, sFilePath)); #endif // Done SendMessageReply(MSG_AEON_FILE_DOWNLOAD_DESC, dFileDownloadDesc, Msg); }