int ExecuteScript (const SOptions &Options) { int i, j; // Load the script file CDatum dScript; CString sError; if (!CDatum::CreateFromFile(Options.sScriptFile, CDatum::formatAEONScript, &dScript, &sError)) { printf("ERROR: %s\n", (LPSTR)sError); return 1; } // Get the server to connect to CString sServer = dScript.GetElement(FIELD_SERVER); if (sServer.IsEmpty()) sServer = Options.sServer; // Connect CSocket theSocket; if (!ConnectToArcology(STR_ARC_CONSOLE, sServer, Options, &theSocket)) return 1; // Run the script CDatum dCommands = dScript.GetElement(FIELD_COMMANDS); for (i = 0; i < dCommands.GetCount(); i++) { CDatum dCommand = dCommands.GetElement(i); // Generate a command-line from the command CStringBuffer Buffer; for (j = 0; j < dCommand.GetCount(); j++) { if (j != 0) Buffer.Write(" ", 1); dCommand.Serialize(CDatum::formatAEONScript, Buffer); } // Run printf("%s\n", (LPSTR)(const CString &)Buffer); CString sResult = ExecuteArcologyCommand(theSocket, Buffer); PrintUTF8(sResult); printf("\n"); } // Done return 0; }
CComplexArray::CComplexArray (CDatum dSrc) // ComplexArray constructor { int i; if (dSrc.GetBasicType() == CDatum::typeStruct) { InsertEmpty(1); SetElement(0, dSrc); } else { int iCount = dSrc.GetCount(); // Clone from another complex array if (iCount > 0) { InsertEmpty(iCount); for (i = 0; i < iCount; i++) SetElement(i, dSrc.GetElement(i)); } } }
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 IComplexDatum::DeserializeAEONScript (CDatum::ESerializationFormats iFormat, const CString &sTypename, CCharStream *pStream) // DeserializeAEONScript // // Deserialize AEONScript { int i; DWORD dwFlags = OnGetSerializeFlags(); // If we have an open brace then we've stored everything as a structure. if (pStream->GetChar() == '{') { // Object must support this if (!(dwFlags & FLAG_SERIALIZE_AS_STRUCT)) return false; // Parse the structure CAEONScriptParser Parser(pStream); CDatum dData; CAEONScriptParser::ETokens iToken = Parser.ParseToken(&dData); if (iToken != CAEONScriptParser::tkDatum) return false; // Take all the fields in the structure and apply them to our object // (our descendants will do the right thing). for (i = 0; i < dData.GetCount(); i++) SetElement(dData.GetKey(i), dData.GetElement(i)); } // Otherwise we expect base64 encoded data else { // Backup one character because we want the OnDeserialize call to read it. pStream->UnreadChar(); // Deserialize CBase64Decoder Decoder(pStream->GetByteStream()); if (!OnDeserialize(iFormat, sTypename, Decoder)) return false; // Read the next character into the stream pStream->RefreshStream(); pStream->ReadChar(); } return true; }
CComplexStruct::CComplexStruct (CDatum dSrc) // CComplexStruct constructor { // Clone from another complex structure for (int i = 0; i < dSrc.GetCount(); i++) { CString sKey = dSrc.GetKey(i); if (!sKey.IsEmpty()) SetElement(sKey, dSrc.GetElement(i)); } }
void CComplexStruct::AppendStruct (CDatum dDatum) // AppendStruct // // Appends the element of the given structure { int i; if (dDatum.GetBasicType() == CDatum::typeStruct) { for (i = 0; i < dDatum.GetCount(); i++) SetElement(dDatum.GetKey(i), dDatum.GetElement(i)); } }
void CHexeMarkupEvaluator::OutputDatum (CDatum dValue) // OutputDatum // // Outputs a datum to the resulting HTML page. NOTE: We expect the values to be // HTML compatible (i.e., caller is responsible for escaping). { int i; if (dValue.GetBasicType() == CDatum::typeArray) { for (i = 0; i < dValue.GetCount(); i++) OutputDatum(dValue.GetElement(i)); } else { m_Output.Write(dValue.AsString()); } }
CDatum CUserInfoSession::CreateSanitizedUserRecord (CDatum dRecord) // CreateSanitizedUserRecord // // Creates a user record suitable for returning to clients. In partincular, // we remove the authentication information. { int i; // Create a destination CComplexStruct *pDest = new CComplexStruct; // Copy all appropriate fields for (i = 0; i < dRecord.GetCount(); i++) { // If this is an auth field, then skip it if (strEquals(dRecord.GetKey(i), FIELD_AUTH_DESC)) ; else if (strEndsWith(dRecord.GetKey(i), FIELD_AUTH_DESC_SUFFIX)) ; // Otherwise, copy it else pDest->SetElement(dRecord.GetKey(i), dRecord.GetElement(i)); } // Done return CDatum(pDest); }
CString ExecuteUpgrade (CSocket &theSocket, const CString &sCmd) { int i; CString sRoot = fileGetPath(fileGetExecutableFilespec()); // Make a list of all executable files to upgrade TArray<CString> FileList; if (!fileGetFileList(sRoot, NULL_STR, CString("*.exe"), FFL_FLAG_RELATIVE_FILESPEC, &FileList)) return CString("ERROR: Unable to obtain a list of executable files to upgrade."); // Prepare a request upgrade command CStringBuffer Output; Output.Write("requestUpgrade (", 16); for (i = 0; i < FileList.GetCount(); i++) { CString sFilespec = fileAppend(sRoot, FileList[i]); // Version SFileVersionInfo Info; if (!fileGetVersionInfo(sFilespec, &Info)) { printf("ERROR: Unable to get file version: %s\n", (LPSTR)sFilespec); continue; } CIPInteger Version(Info.dwProductVersion); CString sVersion = Version.AsString(); // Checksum DWORD dwChecksum = fileChecksumAdler32(sFilespec); if (dwChecksum == 0) { printf("ERROR: Unable to get file checksum: %s\n", (LPSTR)sFilespec); continue; } CString sOutput = strPattern("{filename:\"%s\" version:%s checksum:%d} ", FileList[i], sVersion, dwChecksum); Output.Write(sOutput); } Output.Write(")", 1); // Send the command CString sSend = CString::CreateFromHandoff(Output); CString sResult; CDatum dResult; ExecuteArcologyCommand(theSocket, sSend, &sResult, &dResult); if (strEquals(sResult, CString("ERROR"))) return dResult.AsString(); // Show all the files to upgrade CDatum dUpgradeDesc = dResult.GetElement(0).GetElement(FIELD_UPGRADE_DESC); for (i = 0; i < dUpgradeDesc.GetCount(); i++) { CDatum dFileDesc = dUpgradeDesc.GetElement(i); printf("Upgrading %s\n", (LPSTR)dFileDesc.GetElement(FIELD_FILENAME).AsString()); } // Confirm CString sConfirm = GetInputLine(CString("\nAre you sure you want to upgrade the arcology? [y/n] : ")); if (*sConfirm.GetParsePointer() != 'y' && *sConfirm.GetParsePointer() != 'Y') return NULL_STR; // Upload the new files. for (i = 0; i < dUpgradeDesc.GetCount(); i++) { CDatum dFileDesc = dUpgradeDesc.GetElement(i); const CString &sFilename = dFileDesc.GetElement(FIELD_FILENAME); CString sFilespec = fileAppend(sRoot, sFilename); CString sResult = UploadFile(theSocket, CMD_UPLOAD_UPGRADE, sFilename, sFilespec); printf("%s\n", (LPSTR)sResult); } // Complete the upgrade return ExecuteArcologyCommand(theSocket, CMD_COMPLETE_UPGRADE); }
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; }
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 CAeonEngine::ParseTableAndView (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx, CDatum dTableAndView, CAeonTable **retpTable, DWORD *retdwViewID, CDatum dKey, CRowKey *retKey) // ParseTableAndView // // Parses a datum as follows: // // If a single string, it specifies a table and the default view. // If an array with two strings, the first is the table name; the second is the view name. { CString sError; // If we're not ready, then error if (!m_bReady) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, ERR_NOT_READY, Msg); return false; } // Parse the table names CString sTable; CString sView; if (dTableAndView.GetCount() < 2) sTable = dTableAndView.AsString(); else { sTable = dTableAndView.GetElement(0).AsString(); sView = dTableAndView.GetElement(1).AsString(); } // Make sure we have access if (pSecurityCtx && !pSecurityCtx->IsNamespaceAccessible(sTable)) { SendMessageReplyError(MSG_ERROR_NOT_ALLOWED, strPattern(ERR_NOT_IN_SANDBOX, sTable, pSecurityCtx->GetSandboxName()), Msg); return false; } // Get the table CAeonTable *pTable; if (!FindTable(sTable, &pTable)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_UNKNOWN_TABLE, sTable), Msg); return false; } // Get the view. If we want a key, take this opportunity to parse it. DWORD dwViewID; if (retKey) { if (!pTable->FindViewAndPath(sView, &dwViewID, dKey, retKey, &sError)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg); return false; } } // Otherwise just get the view. else { if (!pTable->FindView(sView, &dwViewID)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(ERR_UNKNOWN_VIEW, sTable, sView), Msg); return false; } } // Done if (retpTable) *retpTable = pTable; if (retdwViewID) *retdwViewID = dwViewID; return true; }
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; }
int CDatum::DefaultCompare (void *pCtx, const CDatum &dKey1, const CDatum &dKey2) // DefaultCompare // // Default comparison routine used for sorting. Returns: // // -1: If dKey1 < dKey2 // 0: If dKey1 == dKey2 // 1: If dKey1 > dKey2 // // NOTES: // // Nil == "" // Nil == {} // Nil == () // "abc" != "ABC" { int i; // If both are the same datatype, then compare CDatum::Types iType1 = dKey1.GetBasicType(); CDatum::Types iType2 = dKey2.GetBasicType(); // If both types are equal, then compare if (iType1 == iType2) { switch (iType1) { case CDatum::typeNil: case CDatum::typeTrue: return 0; case CDatum::typeInteger32: if ((int)dKey1 > (int)dKey2) return 1; else if ((int)dKey1 < (int)dKey2) return -1; else return 0; case CDatum::typeInteger64: if ((DWORDLONG)dKey1 > (DWORDLONG)dKey2) return 1; else if ((DWORDLONG)dKey1 < (DWORDLONG)dKey2) return -1; else return 0; case CDatum::typeDouble: if ((double)dKey1 > (double)dKey2) return 1; else if ((double)dKey1 < (double)dKey2) return -1; else return 0; case CDatum::typeIntegerIP: return KeyCompare((const CIPInteger &)dKey1, (const CIPInteger &)dKey2); case CDatum::typeString: return KeyCompare((const CString &)dKey1, (const CString &)dKey2); case CDatum::typeDateTime: return ((const CDateTime &)dKey1).Compare((const CDateTime &)dKey2); case CDatum::typeArray: if (dKey1.GetCount() > dKey2.GetCount()) return 1; else if (dKey1.GetCount() < dKey2.GetCount()) return -1; else { for (i = 0; i < dKey1.GetCount(); i++) { CDatum dItem1 = dKey1.GetElement(i); CDatum dItem2 = dKey2.GetElement(i); int iItemCompare = CDatum::DefaultCompare(pCtx, dItem1, dItem2); if (iItemCompare != 0) return iItemCompare; } return 0; } case CDatum::typeStruct: if (dKey1.GetCount() > dKey2.GetCount()) return 1; else if (dKey1.GetCount() < dKey2.GetCount()) return -1; else { for (i = 0; i < dKey1.GetCount(); i++) { CString sItemKey1 = dKey1.GetKey(i); CString sItemKey2 = dKey2.GetKey(i); int iKeyCompare = KeyCompare(sItemKey1, sItemKey2); if (iKeyCompare != 0) return iKeyCompare; CDatum dItem1 = dKey1.GetElement(i); CDatum dItem2 = dKey2.GetElement(i); int iItemCompare = CDatum::DefaultCompare(pCtx, dItem1, dItem2); if (iItemCompare != 0) return iItemCompare; } return 0; } // LATER: Not yet supported default: return 0; } } // If one of the types is nil, then compare else if (iType1 == CDatum::typeNil || iType2 == CDatum::typeNil) { CDatum dNonNil; int iResult; if (iType2 == CDatum::typeNil) { dNonNil = dKey1; Swap(iType1, iType2); iResult = 1; } else { dNonNil = dKey2; iResult = -1; } switch (iType2) { case CDatum::typeString: if (((const CString &)dNonNil).IsEmpty()) return 0; else return iResult; case CDatum::typeArray: case CDatum::typeStruct: if (dNonNil.GetCount() == 0) return 0; else return iResult; default: // nil is always less return iResult; } } // If one of the types is a number, then compare as numbers else if (dKey1.IsNumber() || dKey2.IsNumber()) { CNumberValue Number1(dKey1); CNumberValue Number2(dKey2); if (Number1.IsValidNumber() && Number2.IsValidNumber()) return Number1.Compare(Number2); else if (Number1.IsValidNumber()) return 1; else if (Number2.IsValidNumber()) return -1; else return 0; } // Otherwise, cannot compare else return 0; }
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); }
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 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; }
void CAeonEngine::MsgOnMnemosynthModified (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx) // MsgOnMnemosynthModified // // Mnemosynth.onModified {updates} { int i; // If we haven't yet started we don't care about this message. if (!m_bMachineStarted) return; // Get the updates CDatum dCollections = Msg.dPayload.GetElement(FIELD_COLLECTIONS); CDatum dEntries = Msg.dPayload.GetElement(FIELD_ENTRIES); // See what changed bool bStorageChanged = false; for (i = 0; i < dEntries.GetCount(); i++) { CDatum dEntry = dEntries.GetElement(i); const CString &sCollection = dCollections.GetElement((int)dEntry.GetElement(0)); if (strEquals(sCollection, MNEMO_ARC_STORAGE)) bStorageChanged = true; } // If storage changed then update local storage (which will tell us what // storage was added or deleted). if (bStorageChanged) { CSmartLock Lock(m_cs); CString sError; TArray<CString> VolumesAdded; TArray<CString> VolumesDeleted; if (!m_LocalVolumes.Reinit(GetProcessCtx(), &VolumesAdded, &VolumesDeleted, &sError)) { // LATER: We don't know how to recover from this, so we should go into // safe mode. Log(MSG_LOG_ERROR, sError); return; } // Tell every table that volumes were added and deleted so that they can // change their primary and backup volumes. if (VolumesAdded.GetCount() > 0 || VolumesDeleted.GetCount() > 0) { for (i = 0; i < m_Tables.GetCount(); i++) m_Tables[i]->OnVolumesChanged(VolumesDeleted); // LATER: See if there are any existing tables in the new volume. } } }