MojErr MojDbPurgeTest::run() { MojDb db; MojErr err = db.open(MojDbTestDir); MojTestErrCheck(err); // put type MojObject obj; err = obj.fromJson(MojKindStr); MojTestErrCheck(err); err = db.putKind(obj); MojTestErrCheck(err); MojObject revNums[10]; MojObject ids[10]; //put 10 objects in the db for(int i = 0; i < 10; i++) { err = obj.fromJson(MojTestObjStr1); MojTestErrCheck(err); err = db.put(obj); MojTestErrCheck(err); // get _rev and id MojObject rev; err = obj.getRequired(MojDb::RevKey, rev); MojTestErrCheck(err); revNums[i] = rev; MojObject id; err = obj.getRequired(MojDb::IdKey, id); MojTestErrCheck(err); ids[i] = id; } //purge now, there are no RevTimestamp entries MojUInt32 count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 0, 10, 1, -1); MojTestErrCheck(err); //create a RevTimestamp entry - that's not more than PurgeNumDays days ago MojTime time; err = MojGetCurrentTime(time); MojTestErrCheck(err); err = createRevTimestamp(db, revNums[0], time.microsecs()); MojTestErrCheck(err); //purge now, there are no RevTimestamp entries that are more than //PurgeNumDays ago, so nothing should be purged count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 0, 10, 3, -1); MojTestErrCheck(err); //create a RevTimestamp entry for more than PurgeNumDays days ago err = MojGetCurrentTime(time); MojTestErrCheck(err); err = createRevTimestamp(db, revNums[9], time.microsecs() - (((MojInt64)40) * MojTime::UnitsPerDay)); MojTestErrCheck(err); //purge now, since nothing has been deleted, nothing should be purged, //but the RevTimestamp object should be deleted count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 0, 10, 4, -1); MojTestErrCheck(err); //delete something - this will set its revision number higher bool found; err = db.del(ids[0], found); MojTestErrCheck(err); MojTestAssert(found == true); //purge now, since nothing has been deleted prior to the revision //number, nothing should be purged count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 0, 9, 5, -1); MojTestErrCheck(err); //delete another object err = db.del(ids[1], found); MojTestErrCheck(err); MojTestAssert(found == true); //create a RevTimestamp entry for more than PurgeNumDays days ago, //with the rev number of the 1st obj we deleted MojDbQuery query; err = query.from(_T("PurgeTest:1")); MojTestErrCheck(err); err = query.where(MojDb::IdKey, MojDbQuery::OpEq, ids[0]); MojTestErrCheck(err); err = query.includeDeleted(); MojTestErrCheck(err); MojDbCursor cursor; err = db.find(query, cursor); MojTestErrCheck(err); MojObject objFromDb; err = cursor.get(objFromDb, found); MojTestErrCheck(err); err = cursor.close(); MojTestErrCheck(err); MojTestAssert(found == true); MojObject revFromDb; err = objFromDb.getRequired(MojDb::RevKey, revFromDb); MojTestErrCheck(err); err = MojGetCurrentTime(time); MojTestErrCheck(err); err = createRevTimestamp(db, revFromDb, time.microsecs() - (((MojInt64)35) * MojTime::UnitsPerDay)); MojTestErrCheck(err); //now purge, only id[0] should be purged count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 1, 8, 6, revFromDb); MojTestErrCheck(err); //TODO 2.12.10 - this test does not pass yet, we need to fix calling delKind after a purge //err = delKindTest(db); //MojTestErrCheck(err); err = db.close(); MojTestErrCheck(err); return MojErrNone; }
MojErr TrimFolderEmailsCommand::GetLocalEmailsResponse(MojObject& response, MojErr err) { try { // Check database response ErrorToException(err); MojObject results; err = response.getRequired("results", results); ErrorToException(err); MojObject::ArrayIterator it; err = results.arrayBegin(it); ErrorToException(err); for (; it != results.arrayEnd(); ++it) { MojObject& emailObj = *it; MojErr err; MojObject id; err = emailObj.getRequired(PopEmailAdapter::ID, id); ErrorToException(err); MojString uid; err = emailObj.getRequired(PopEmailAdapter::SERVER_UID, uid); ErrorToException(err); std::string uidStr(uid); MojInt64 timestamp; err = emailObj.getRequired(EmailSchema::TIMESTAMP, timestamp); // add email ID to the list of IDs to be deleted m_idsToTrim.push(id); // move trimmed emails to old emails' cache so that they won't be // added in the next sync m_uidCache.GetOldEmailsCache().AddToCache(uid.data(), timestamp); m_uidCache.GetOldEmailsCache().SetChanged(true); // decrement the number of emails to be trimmed if (--m_numberToTrim == 0) { break; } } bool hasMore = DatabaseAdapter::GetNextPage(response, m_localEmailsPage); if(m_numberToTrim > 0 && hasMore) { // Get next batch of emails to be deleted MojLogInfo(m_log, "getting another batch of local emails"); GetLocalEmails(); } else { MojLogInfo(m_log, "Delete local emails cache"); DeleteLocalEmails(); } } catch(const std::exception& e) { MojLogError(m_log, "Failed to get local emails: %s", e.what()); Failure(e); } catch(...) { MojLogError(m_log, "Uncaught exception %s", __PRETTY_FUNCTION__); MailException exc("Unable to get local emails", __FILE__, __LINE__); Failure(exc); } return MojErrNone; }
void EmailAdapter::ParseDatabaseObject(const MojObject& obj, Email& email) { MojErr err; MojObject folderId; err = obj.getRequired(FOLDER_ID, folderId); ErrorToException(err); email.SetFolderId(folderId); MojString subject; err = obj.getRequired(SUBJECT, subject); ErrorToException(err); email.SetSubject( std::string(subject) ); MojObject fromAddress; err = obj.getRequired(FROM, fromAddress); ErrorToException(err); email.SetFrom( ParseAddress(fromAddress) ); // Optional replyTo address MojObject replyTo; if(obj.get(REPLY_TO, replyTo) && !replyTo.null()) { email.SetReplyTo( ParseAddress(replyTo) ); } MojInt64 timestamp; err = obj.getRequired(TIMESTAMP, timestamp); ErrorToException(err); email.SetDateReceived(timestamp); // Parse recipients MojObject recipients; err = obj.getRequired(RECIPIENTS, recipients); ErrorToException(err); ParseRecipients(recipients, email); // Parse flags MojObject flags; if (obj.get(FLAGS, flags)) { ParseFlags(flags, email); } // Parse parts MojObject parts; err = obj.getRequired(PARTS, parts); ErrorToException(err); EmailPartList partList; ParseParts(parts, partList); email.SetPartList(partList); MojObject originalMsgId; if (obj.get(ORIGINAL_MSG_ID, originalMsgId)) { email.SetOriginalMsgId(originalMsgId); } MojString draftType; bool hasDraftType = false; err = obj.get(DRAFT_TYPE, draftType, hasDraftType); ErrorToException(err); if (hasDraftType) { email.SetDraftType( ParseDraftType(draftType) ); } MojString priority; bool hasPriority = false; err = obj.get(PRIORITY, priority, hasPriority); ErrorToException(err); // If no priority exists, this will default to normal email.SetPriority(ParsePriority(priority)); email.SetMessageId( DatabaseAdapter::GetOptionalString(obj, MESSAGE_ID) ); email.SetInReplyTo( DatabaseAdapter::GetOptionalString(obj, IN_REPLY_TO) ); // SendStatus // NOTE: This is not being serialized back to the database in SerializeToDatabaseObject MojObject sendStatus; if (obj.get(SEND_STATUS, sendStatus)) { bool isSent = false; if (sendStatus.get(SendStatus::SENT, isSent)) { email.SetSent(isSent); } bool hasFatalError = false; if (sendStatus.get(SendStatus::FATAL_ERROR, hasFatalError)) { email.SetHasFatalError(hasFatalError); } MojInt64 retryCount; if (sendStatus.get(SendStatus::RETRY_COUNT, retryCount)) { email.SetRetryCount(retryCount); } MojObject sendError; if (sendStatus.get(SendStatus::ERROR, sendError)) { MojObject errorCode; if (sendError.get(SendStatusError::ERROR_CODE, errorCode) && !errorCode.null() && !errorCode.undefined()) { email.SetSendError(static_cast<MailError::ErrorCode>(errorCode.intValue())); } } } }
/* * Send one IMCommand that has handler set to transport * * errors returned up to OutgoingIMCommandHandler * * * @param imCmd - imCommand that was read out of the DB with handler set to transport { "id" : "ImCommand", "type" : "object", "properties" : { "command" : {"type" : "string", "enum" : ["blockBuddy","deleteBuddy","sendBuddyInvite","receivedBuddyInvite","createChatGroup","inviteToGroup","leaveGroup"], "description" : "The command to be processed"}, "params" : {"type" : "any", "description" : "Parameters associated with the command are stored here."} "handler" : {"type" : "string", "enum" : ["transport","application"], "description" : "Who is responsible for handling the command"}, "targetUsername" : {"type" : "string", "description" : "The buddyname the command will act on"}, "fromUsername" : {"type" : "string", "description" : "The username that originated the command"}, "serviceName" : {"type" : "string", "description" : "Name of originating service (see IMAddress of contacts load library*)."} } * } */ MojErr SendOneCommandHandler::doSend(const MojObject imCmd) { IMServiceHandler::logMojObjectJsonString(_T("send command: %s"),imCmd); MojString command; LibpurpleAdapter::SendResult retVal = LibpurpleAdapter::SENT; bool found = false; MojErr err = imCmd.get(MOJDB_COMMAND, command, found); MojErrCheck(err); // serviceName err = imCmd.get(MOJDB_SERVICE_NAME, m_serviceName, found); MojErrCheck(err); // get the id so we can update the status in the DB after sending err = imCmd.getRequired(MOJDB_ID, m_currentCmdDbId); MojErrCheck(err); // for receiving invite, the user account and remote user are reversed: targetUsername is us and the fromUsername is the remote buddy if (0 == command.compare(_T("receivedBuddyInvite"))) { // username of current account err = imCmd.get(MOJDB_FROM_USER, m_buddyName, found); MojErrCheck(err); // buddy / remote user err = imCmd.get(MOJODB_TARGET_USER, m_username, found); MojErrCheck(err); } else { // username of current account err = imCmd.get(MOJDB_FROM_USER, m_username, found); MojErrCheck(err); // buddy / remote user err = imCmd.get(MOJODB_TARGET_USER, m_buddyName, found); MojErrCheck(err); } // which command? if (0 == command.compare(_T("blockBuddy"))) { retVal = blockBuddy(imCmd); } else if (0 == command.compare(_T("deleteBuddy"))) { retVal = removeBuddy(imCmd); } else if (0 == command.compare(_T("sendBuddyInvite"))) { retVal = inviteBuddy(imCmd); } else if (0 == command.compare(_T("receivedBuddyInvite"))) { retVal = receivedBuddyInvite(imCmd); } else { MojLogError(IMServiceApp::s_log, _T("doSend: unknown command %s"), command.data()); retVal = LibpurpleAdapter::SEND_FAILED; } // we can't just delete the command if the user is not logged on... // need to save command in "waiting" state waiting for user to login if (LibpurpleAdapter::USER_NOT_LOGGED_IN == retVal) { // user not logged in - put in queued state MojLogError(IMServiceApp::s_log, _T("doSend - can't process command - user not logged in. Waiting for connection")); MojObject propObject; propObject.putString(MOJDB_STATUS, IMMessage::statusStrings[WaitingForConnection]); // id to update propObject.putString(MOJDB_ID, m_currentCmdDbId); // save the new fields - call merge err = m_dbClient.merge(this->m_imSaveCommandSlot, propObject); if (err) { MojLogError(IMServiceApp::s_log, _T("doSend - DB merge command failed. err %d, DB id %s: "), err, m_currentCmdDbId.data() ); } } else { // delete command so we don't keep processing it // put id in an array MojObject idsToDelete; // array err = idsToDelete.push(m_currentCmdDbId); // luna://com.palm.db/del '{"ids":[2]}' IMServiceHandler::logMojObjectJsonString(_T("deleting imcommand: %s"), idsToDelete); err = m_dbClient.del(this->m_imDeleteCommandSlot, idsToDelete.arrayBegin(), idsToDelete.arrayEnd()); if (err) { MojLogError(IMServiceApp::s_log, _T("doSend - DB del command failed. err %d, DB id %s: "), err, m_currentCmdDbId.data() ); } } if (LibpurpleAdapter::SENT != retVal) { // something went wrong - nothing more we can do MojLogError(IMServiceApp::s_log, _T("doSend: command failed")); m_outgoingIMHandler->messageFinished(); } return MojErrNone; }
/** * Parse two database objects into an ImapAccount * * @param in com.palm.mail.account database object * @param transportIn com.palm.imap.account database object * @param out ImapAccount */ void ImapAccountAdapter::GetImapAccount(const MojObject& in, const MojObject& transportIn, ImapAccount& out) { MojObject id; MojErr err = in.getRequired(ID, id); ErrorToException(err); out.SetId(id); // Template from account object MojString templateId; bool hasTemplateId = false; err = in.get(TEMPLATEID, templateId, hasTemplateId); // make required later ErrorToException(err); if(hasTemplateId) { out.SetTemplateId(templateId.data()); } boost::shared_ptr<ImapLoginSettings> loginSettings(new ImapLoginSettings()); // Username from account object (this is always the email address) MojString username; in.getRequired(USERNAME, username); ErrorToException(err); loginSettings->SetUsername(username.data()); // Read server settings ParseLoginConfig(transportIn, *loginSettings); // Store updated server settings on account out.SetLoginSettings(loginSettings); int syncFrequencyMin; err = transportIn.getRequired(EmailAccountAdapter::SYNC_FREQUENCY_MINS, syncFrequencyMin); ErrorToException(err); out.SetSyncFrequencyMins(syncFrequencyMin); int syncWindowDays; err = transportIn.getRequired(EmailAccountAdapter::SYNC_WINDOW_DAYS, syncWindowDays); ErrorToException(err); out.SetSyncWindowDays(syncWindowDays); MojInt64 rev; err = transportIn.getRequired(REV, rev); ErrorToException(err); out.SetRevision(rev); // Retrieve the optional error status MojObject errorObj; if(transportIn.get(EmailAccountAdapter::ERROR, errorObj)) { EmailAccount::AccountError accountError; EmailAccountAdapter::ParseErrorInfo(errorObj, accountError); out.SetError(accountError); } // Compatibility/workaround options bool enableCompress = DatabaseAdapter::GetOptionalBool(transportIn, ENABLE_COMPRESS, ImapConfig::GetConfig().GetEnableCompress()); bool enableExpunge = DatabaseAdapter::GetOptionalBool(transportIn, ENABLE_EXPUNGE, true); out.SetEnableCompression(enableCompress); out.SetEnableExpunge(enableExpunge); // Read special folders EmailAccountAdapter::ParseSpecialFolders(transportIn, out); // Read retry status EmailAccountAdapter::ParseAccountRetryStatus(transportIn, out); // Root folder prefix bool hasRootFolder = false; MojString rootFolder; err = transportIn.get(ROOT_FOLDER, rootFolder, hasRootFolder); ErrorToException(err); if(hasRootFolder) { out.SetNamespaceRoot( string(rootFolder.data()) ); } }
MojErr MojDbRevTest::run() { MojDb db; MojErr err = db.open(MojDbTestDir); MojTestErrCheck(err); // put type MojObject obj; err = obj.fromJson(MojKindStr); MojTestErrCheck(err); // put obj err = db.putKind(obj); MojTestErrCheck(err); err = obj.fromJson(MojTestObjStr1); MojTestErrCheck(err); err = db.put(obj); MojTestErrCheck(err); MojObject id; err = obj.getRequired(MojDb::IdKey, id); MojTestErrCheck(err); // get obj and verify rev eq MojObject rev; err = obj.getRequired(MojDb::RevKey, rev); MojTestErrCheck(err); err = checkRevEq(db, id, rev); MojTestErrCheck(err); // put identical obj and verify rev not changed err = db.put(obj); MojTestErrCheck(err); err = checkRevEq(db, id, rev); MojTestErrCheck(err); // put with changed prop and verify rev gt err = obj.fromJson(MojTestObjStr2); MojTestErrCheck(err); err = obj.put(MojDb::IdKey, id); MojTestErrCheck(err); err = db.put(obj); // this put will fail because we haven't included a rev MojTestErrExpected(err, MojErrDbRevNotSpecified); bool found = false; err = db.del(id, found); // verify that we can put without a rev if the object is deleted MojTestErrCheck(err); MojTestAssert(found); err = db.put(obj); MojTestErrCheck(err); err = obj.getRequired(MojDb::RevKey, rev); MojTestErrCheck(err); err = obj.put(MojDb::RevKey, 1); MojTestErrCheck(err); err = db.put(obj); // this put will fail because the revision num is lower MojTestErrExpected(err, MojErrDbRevisionMismatch); err = obj.put(MojDb::RevKey, rev); MojTestErrCheck(err); err = db.put(obj); // now this should succeed MojTestErrCheck(err); // merge with unchanged prop and verify rev not changed err = obj.fromJson(MojTestObjStr3); MojTestErrCheck(err); err = obj.put(MojDb::IdKey, id); MojTestErrCheck(err); err = db.merge(obj); MojTestErrCheck(err); err = checkRevEq(db, id, rev); MojTestErrCheck(err); // merge with changed prop and verify rev gt err = obj.fromJson(MojTestObjStr4); MojTestErrCheck(err); err = obj.put(MojDb::IdKey, id); MojTestErrCheck(err); err = db.merge(obj); MojTestErrCheck(err); err = checkRevGt(db, id, rev); MojTestErrCheck(err); // query merge with unchanged prop and verify rev not changed MojDbQuery query; err = query.from("RevTest:1"); MojTestErrCheck(err); err = obj.fromJson(MojTestObjStr4); MojTestErrCheck(err); MojUInt32 count = 0; err = db.merge(query, obj, count); MojTestErrCheck(err); MojTestAssert(count == 1); err = checkRevEq(db, id, rev); MojTestErrCheck(err); // query merge with changed prop and verify rev gt err = obj.fromJson(MojTestObjStr3); MojTestErrCheck(err); err = db.merge(query, obj, count); MojTestErrCheck(err); MojTestAssert(count == 1); err = checkRevGt(db, id, rev); MojTestErrCheck(err); // del verify rev gt err = db.del(id, found); MojTestErrCheck(err); MojTestAssert(found); err = checkRevGt(db, id, rev); MojTestErrCheck(err); // del again and verify rev not changed err = db.del(id, found); MojTestErrCheck(err); MojTestAssert(found); err = checkRevEq(db, id, rev); MojTestErrCheck(err); // undel and verify rev gt err = obj.fromJson(MojTestObjStr5); MojTestErrCheck(err); err = obj.put(MojDb::IdKey, id); MojTestErrCheck(err); err = db.merge(obj); MojTestErrCheck(err); err = checkRevGt(db, id, rev); MojTestErrCheck(err); // query del and verify rev gt err = db.del(query, count); MojTestErrCheck(err); MojTestAssert(count == 1); err = checkRevGt(db, id, rev); MojTestErrCheck(err); err = db.close(); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbQuery::fromObject(const MojObject& obj) { // TODO: validate against query schema bool found; MojErr err; MojObject array; MojString str; // distinct found = false; err = obj.get(DistinctKey, str, found); MojErrCheck(err); if (found) { err = distinct(str); MojErrCheck(err); // if "distinct" is set, force "distinct" column into "select". err = select(str); MojErrCheck(err); // order err = order(str); MojErrCheck(err); } else { // select if (obj.get(SelectKey, array)) { if(array.empty()) { MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: select clause but no selected properties")); } MojObject prop; MojSize i = 0; while (array.at(i++, prop)) { MojErr err = prop.stringValue(str); MojErrCheck(err); err = select(str); MojErrCheck(err); } } // order found = false; err = obj.get(OrderByKey, str, found); MojErrCheck(err); if (found) { err = order(str); MojErrCheck(err); } } // from err = obj.getRequired(FromKey, str); MojErrCheck(err); err = from(str); MojErrCheck(err); // where if (obj.get(WhereKey, array)) { err = addClauses(m_whereClauses, array); MojErrCheck(err); } // filter if (obj.get(FilterKey, array)) { err = addClauses(m_filterClauses, array); MojErrCheck(err); } // desc bool descVal; if (obj.get(DescKey, descVal)) { desc(descVal); } // limit MojInt64 lim; if (obj.get(LimitKey, lim)) { if (lim < 0) MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: negative query limit")); } else { lim = LimitDefault; } limit((MojUInt32) lim); // page MojObject pageObj; if (obj.get(PageKey, pageObj)) { Page pagec; err = pagec.fromObject(pageObj); MojErrCheck(err); page(pagec); } bool incDel = false; if (obj.get(IncludeDeletedKey, incDel) && incDel) { err = includeDeleted(); MojErrCheck(err); } return MojErrNone; }
MojErr OnEnabledHandler::findImLoginStateResult(MojObject& payload, MojErr err) { MojLogTrace(IMServiceApp::s_log); if (err != MojErrNone) { MojString error; MojErrToString(err, error); MojLogCritical(IMServiceApp::s_log, _T("findImLoginStateResult failed: error %d - %s"), err, error.data()); } else { // "results" in result MojObject results; payload.get(_T("results"), results); // check to see if array is empty - normally it will be if this is a newly created account. There should never be more than 1 item here if (!results.empty()){ IMServiceHandler::logMojObjectJsonString(_T("findImLoginStateResult found existing imLoginState record: %s"), payload); // if there is a record already, make sure the account id matches. MojObject loginState; MojObject::ConstArrayIterator itr = results.arrayBegin(); bool foundOne = false; while (itr != results.arrayEnd()) { if (foundOne) { MojLogError(IMServiceApp::s_log, _T("findImLoginStateResult: found more than one ImLoginState with same username/serviceName - using the first one")); break; } loginState = *itr; foundOne = true; itr++; } MojString accountId; MojErr err = loginState.getRequired("accountId", accountId); if (err) { MojLogError(IMServiceApp::s_log, _T("findImLoginStateResult: missing accountId in loginState entry")); } if (0 != accountId.compare(m_accountId)) { MojLogError(IMServiceApp::s_log, _T("findImLoginStateResult: existing loginState record does not have matching account id. accountId = %s"), accountId.data()); // delete this record MojObject idsToDelete; MojString dbId; err = loginState.getRequired("_id", dbId); if (err) { MojLogError(IMServiceApp::s_log, _T("findImLoginStateResult: missing dbId in loginState entry")); } else { idsToDelete.push(dbId); // luna://com.palm.db/del '{"ids":[2]}' MojLogInfo(IMServiceApp::s_log, _T("findImLoginStateResult: deleting loginState entry id: %s"), dbId.data()); err = m_dbClient.del(this->m_deleteImLoginStateSlot, idsToDelete.arrayBegin(), idsToDelete.arrayEnd()); if (err != MojErrNone) { MojString error; MojErrToString(err, error); MojLogError(IMServiceApp::s_log, _T("findImLoginStateResult: db.del() failed: error %d - %s"), err, error.data()); } } } // if the account id matches, leave the old record and we are done else return MojErrNone; } // no existing record found or the old one was deleted - create a new one MojLogInfo(IMServiceApp::s_log, _T("findImLoginStateResult: no matching loginState record found for %s, %s. creating a new one"), m_username.data(), m_serviceName.data()); MojObject imLoginState; imLoginState.putString(_T("_kind"), IM_LOGINSTATE_KIND); imLoginState.put(_T("accountId"), m_accountId); imLoginState.put(_T("serviceName"), m_serviceName); imLoginState.put(_T("username"), m_username); imLoginState.put(_T("capabilityId"), m_capabilityProviderId); imLoginState.putString(_T("state"), LOGIN_STATE_OFFLINE); imLoginState.putInt(_T("availability"), PalmAvailability::ONLINE); //default to online so we automatically login at first imLoginState.put(_T("config"), m_config); MojErr err = m_dbClient.put(m_addImLoginStateSlot, imLoginState); if (err != MojErrNone) { MojString error; MojErrToString(err, error); MojLogError(IMServiceApp::s_log, _T("findImLoginStateResult: db.put() failed: error %d - %s"), err, error.data()); } } return err; }
MojErr MojDb::purgeImpl(MojObject& obj, MojUInt32& countOut, MojDbReq& req) { MojLogTrace(s_log); MojObject val; MojErr err = obj.getRequired(RevNumKey, val); MojErrCheck(err); MojObject timestamp; err = obj.getRequired(TimestampKey, timestamp); MojErrCheck(err); // purge all objects that were deleted on or prior to this rev num // query for objects in two passes - once where backup is true and once where backup is false MojDbQuery objQuery; err = objQuery.from(MojDbKindEngine::RootKindId); MojErrCheck(err); err = objQuery.where(DelKey, MojDbQuery::OpEq, true); MojErrCheck(err); err = objQuery.where(SyncKey, MojDbQuery::OpEq, true); MojErrCheck(err); err = objQuery.where(RevKey, MojDbQuery::OpLessThanEq, val); MojErrCheck(err); MojUInt32 backupCount = 0; req.autobatch(true); req.fixmode(true); objQuery.limit(AutoBatchSize); err = delImpl(objQuery, backupCount, req, FlagPurge); MojErrCheck(err); MojDbQuery objQuery2; err = objQuery2.from(MojDbKindEngine::RootKindId); MojErrCheck(err); err = objQuery2.where(DelKey, MojDbQuery::OpEq, true); MojErrCheck(err); err = objQuery2.where(SyncKey, MojDbQuery::OpEq, false); MojErrCheck(err); err = objQuery2.where(RevKey, MojDbQuery::OpLessThanEq, val); MojErrCheck(err); MojUInt32 count = 0; MojUInt32 batchRemain = 0; if (backupCount <= AutoBatchSize) batchRemain = AutoBatchSize - backupCount; req.autobatch(true); // enable auto batch req.fixmode(true); // force deletion of bad entries if (batchRemain > 0) { objQuery2.limit(batchRemain); err = delImpl(objQuery2, count, req, FlagPurge); MojErrCheck(err); } countOut = count + backupCount; req.autobatch(false); // if we actually deleted objects, store this rev num as the last purge rev if (countOut > 0) { err = updateState(LastPurgedRevKey, val, req); MojErrCheck(err); } // delete all the RevTimestamp entries prior to this one MojDbQuery revTimestampQuery; err = revTimestampQuery.from(MojDbKindEngine::RevTimestampId); MojErrCheck(err); err = revTimestampQuery.where(TimestampKey, MojDbQuery::OpLessThanEq, timestamp); MojErrCheck(err); count = 0; err = delImpl(revTimestampQuery, count, req, FlagPurge); MojErrCheck(err); return MojErrNone; }
MojErr MojDbKind::configure(const MojObject& obj, const KindMap& map, const MojString& locale, MojDbReq& req) { MojLogTrace(s_log); // get owner before checking permissions MojString owner; MojErr err = obj.getRequired(OwnerKey, owner); MojErrCheck(err); // only admin can change owner if (!m_owner.empty() && m_owner != owner && !req.admin()) { err = deny(req); MojErrCheck(err); } m_owner = owner; err = checkPermission(OpKindUpdate, req); MojErrCheck(err); // schema MojObject schema; if (obj.get(SchemaKey,schema)) { err = m_schema.fromObject(schema); MojErrCheck(err); } // supers StringVec superIds; MojObject supers; if (obj.get(ExtendsKey, supers)) { MojObject::ConstArrayIterator end = supers.arrayEnd(); for (MojObject::ConstArrayIterator i = supers.arrayBegin(); i != end; ++i) { MojString str; err = i->stringValue(str); MojErrCheck(err); err = superIds.push(str); MojErrCheck(err); } } // backup bool backup = false; if (obj.get(SyncKey, backup)) m_backup = backup; bool updating = !m_obj.undefined(); // load state m_state.reset(new MojDbKindState(m_id, m_kindEngine)); MojAllocCheck(m_state.get()); err = m_state->init(m_schema.strings(), req); MojErrCheck(err); // indexes err = configureIndexes(obj, locale, req); MojErrCheck(err); // supers err = updateSupers(map, superIds, updating, req); MojErrCheck(err); // revision sets err = configureRevSets(obj); MojErrCheck(err); // keep a copy of obj m_obj = obj; return MojErrNone; }
MojErr MojDb::dumpImpl(MojFile& file, bool backup, bool incDel, const MojObject& revParam, const MojObject& delRevParam, bool skipKinds, MojUInt32& countOut, MojDbReq& req, MojObject* response, const MojChar* keyName, MojSize& bytesWritten, MojSize& warns, MojUInt32 maxBytes) { // query for objects, adding the backup key and rev key if necessary MojDbQuery query; MojErr err = query.from(MojDbKindEngine::RootKindId); MojErrCheck(err); err = query.where(MojDb::DelKey, MojDbQuery::OpEq, incDel); MojErrCheck(err); if (backup) { err = query.where(MojDb::SyncKey, MojDbQuery::OpEq, true); MojErrCheck(err); if (incDel) { err = query.where(MojDb::RevKey, MojDbQuery::OpGreaterThan, delRevParam); MojErrCheck(err); } else { err = query.where(MojDb::RevKey, MojDbQuery::OpGreaterThan, revParam); MojErrCheck(err); } } MojDbCursor cursor; err = findImpl(query, cursor, NULL, req, OpRead); MojErrCheck(err); warns = 0; MojObject curRev; for(;;) { bool found = false; MojObject obj; err = cursor.get(obj, found); // So that we can get as much data as possible from a corrupt database // We simply skip ghost keys and continue if (err == MojErrInternalIndexOnFind) { warns++; continue; } MojErrCheck(err); if (!found) break; if (skipKinds) { MojString kind; err = obj.getRequired(KindKey, kind); MojErrCheck(err); if (kind == MojDbKindEngine::KindKindId) { continue; } } // write out each object, if the backup is full, insert the appropriate incremental key err = dumpObj(file, obj, bytesWritten, maxBytes); MojErrCatch(err, MojErrDbBackupFull) { if (response) { MojErr errBackup = MojErrNone; if (!curRev.undefined()) { errBackup = insertIncrementalKey(*response, keyName, curRev); MojErrCheck(errBackup); } else { errBackup = insertIncrementalKey(*response, keyName, incDel ? delRevParam: revParam); MojErrCheck(errBackup); } errBackup = handleBackupFull(revParam, delRevParam, *response, keyName); MojErrCheck(errBackup); } return MojErrNone; } MojErrCheck(err); err = obj.getRequired(MojDb::RevKey, curRev); MojErrCheck(err); countOut++; } err = cursor.close(); MojErrCheck(err); if (warns > 0) { MojLogWarning(s_log, _T("Finished Backup with %d warnings \n"), (int)warns); } else { MojLogDebug(s_log, _T("Finished Backup with no warnings \n")); } // construct the next incremental key if (response && !curRev.undefined()) { err = insertIncrementalKey(*response, keyName, curRev); MojErrCheck(err); } return MojErrNone; }
MojErr ReconcileEmailsCommand::GetLocalEmailsResponse(MojObject& response, MojErr err) { try { MojLogDebug(m_log, "Got local emails response: %s", AsJsonString(response).c_str()); // Check database response ErrorToException(err); MojObject results; err = response.getRequired("results", results); ErrorToException(err); MojObject::ArrayIterator it; err = results.arrayBegin(it); ErrorToException(err); for (; it != results.arrayEnd(); ++it) { MojObject& emailObj = *it; MojErr err; MojObject id; err = emailObj.getRequired(PopEmailAdapter::ID, id); ErrorToException(err); MojString uid; err = emailObj.getRequired(PopEmailAdapter::SERVER_UID, uid); ErrorToException(err); std::string uidStr(uid); MojInt64 timestamp; err = emailObj.getRequired(EmailSchema::TIMESTAMP, timestamp); if (!m_uidMap->HasUid(uidStr)) { // email is not found in server if (!m_account->IsDeleteOnDevice()) { // if the user chooses not to delete emails on device that // have been deleted from the server, that means these emails // won't been deleted. So we need to include these emails as wells. m_messageCount++; } // TODO: need to handle the case when an email is moved from other // folders into inbox. May add a virtual flag in POP email to indicate // that the email has been locally move into inbox. m_serverDeletedEmailIds.push(id); } else { boost::shared_ptr<ReconcileInfo> info = m_reconcileUidMap[uidStr]; if (timestamp < m_cutOffTime) { // email is beyond sync window // add id to the array of IDs that will be deleted soon m_oldEmailIds.push(id); // add this old email into old emails' cache m_uidCache.GetOldEmailsCache().AddToCache(std::string(uid), timestamp); m_uidCache.GetOldEmailsCache().SetChanged(true); // mark reconcile info as old email info->SetStatus(Status_Old_Email); info->SetTimestamp(timestamp); } else { // email is still within sync window // mark reconcile info as downloaded info->SetStatus(Status_Downloaded); info->SetTimestamp(timestamp); if (timestamp > m_latestEmailTimestamp) { m_latestEmailTimestamp = timestamp; } // keep track of how many emails will be remained in the inbox m_messageCount++; } } } bool hasMore = DatabaseAdapter::GetNextPage(response, m_localEmailsPage); if(hasMore) { // Get next batch of emails MojLogDebug(m_log, "getting another batch of local emails"); GetLocalEmails(); } else { MojLogDebug(m_log, "Got local deleted emails cache"); GetLocalDeletedEmails(); } } catch(const std::exception& e) { MojLogError(m_log, "Failed to get local emails: %s", e.what()); Failure(e); } catch(...) { MojLogError(m_log, "Uncaught exception %s", __PRETTY_FUNCTION__); MailException exc("Unable to get local emails", __FILE__, __LINE__); Failure(exc); } return MojErrNone; }
MojErr ReconcileEmailsCommand::GetDeletedEmailsResponse(MojObject& response, MojErr err) { try { MojLogDebug(m_log, "Got local deleted emails response: %s", AsJsonString(response).c_str()); // Check database response ErrorToException(err); MojObject results; err = response.getRequired("results", results); ErrorToException(err); if(results.size() > 0) { MojLogInfo(m_log, "Got %d local deleted emails", results.size()); } MojObject::ArrayIterator it; err = results.arrayBegin(it); ErrorToException(err); // Read email properties: _id, UID, flags // If you need additional properties, modify the database query for (; it != results.arrayEnd(); ++it) { MojObject& emailObj = *it; MojObject id; err = emailObj.getRequired(PopEmailAdapter::ID, id); ErrorToException(err); MojString uid; err = emailObj.getRequired(PopEmailAdapter::SERVER_UID, uid); ErrorToException(err); std::string uidStr(uid); m_localDeletedEmailUids.push_back(LocalDeletedEmailInfo(id, uidStr)); ReconcileInfoPtr infoPtr = m_reconcileUidMap[uidStr]; if (infoPtr.get()) { m_reconcileUidMap.erase(std::string(uid)); UidMap::MessageInfo info = infoPtr->GetMessageInfo(); int msgNum = info.GetMessageNumber(); m_reconcileMsgNumMap.erase(msgNum); } } bool hasMore = DatabaseAdapter::GetNextPage(response, m_localEmailsPage); if(hasMore) { // Get next batch of emails MojLogDebug(m_log, "getting another batch of local deleted emails"); GetLocalDeletedEmails(); } else { MojLogDebug(m_log, "Got old emails cache"); GetUidCache(); } } catch(const std::exception& e) { MojLogError(m_log, "Failed to sync folder: %s", e.what()); Failure(e); } catch(...) { MojLogError(m_log, "Uncaught exception %s", __PRETTY_FUNCTION__); MailException exc("Unable to get deleted emails", __FILE__, __LINE__); Failure(exc); } return MojErrNone; }
void PopAccountAdapter::GetPopAccountFromPayload(MojLogger& log, const MojObject& payload, MojObject& popAccount) { MojErr err = popAccount.putString(KIND, POP_ACCOUNT_KIND); ErrorToException(err); MojObject accountId; err = payload.getRequired(ACCOUNT_ID, accountId); ErrorToException(err); err = popAccount.put(ACCOUNT_ID, accountId); ErrorToException(err); MojObject config; err = payload.getRequired(CONFIG, config); ErrorToException(err); MojObject hostname; err = config.getRequired(HOST_NAME, hostname); ErrorToException(err); err = popAccount.put(HOST_NAME, hostname); ErrorToException(err); MojObject port; err = config.getRequired(PORT, port); ErrorToException(err); err = popAccount.put(PORT, port); ErrorToException(err); MojObject encryption; err = config.getRequired(ENCRYPTION, encryption); ErrorToException(err); err = popAccount.put(ENCRYPTION, encryption); ErrorToException(err); MojObject username; err = config.getRequired(USERNAME, username); ErrorToException(err); err = popAccount.put(USERNAME, username); ErrorToException(err); // Set 'deleteFromServer' flag bool deleteFromServer = true; if (!config.get(DELETE_FROM_SERVER, deleteFromServer)) { // default value is true if this field doesn't exist deleteFromServer = true; } err = popAccount.putBool(DELETE_FROM_SERVER, deleteFromServer); ErrorToException(err); // Set 'deleteOnDevice' flag bool deleteOnDevice = false; if (!config.get(DELETE_ON_DEVICE, deleteOnDevice)) { // defaul value is false if this field doesn't exist deleteOnDevice = false; } err = popAccount.putBool(DELETE_ON_DEVICE, deleteOnDevice); ErrorToException(err); // Set sync window MojInt64 syncWindow; if (!config.get(EmailAccountAdapter::SYNC_WINDOW_DAYS, syncWindow)) { // set default value if this field doesn't exist syncWindow = PopAccount::DEFAULT_SYNC_WINDOW; } err = popAccount.putInt(EmailAccountAdapter::SYNC_WINDOW_DAYS, syncWindow); ErrorToException(err); // Set sync frequency MojInt64 syncFreq; if (!config.get(SYNC_FREQUENCY_MINS, syncFreq)) { syncFreq = PopAccount::DEFAULT_SYNC_FREQUENCY_MINS; } err = popAccount.putInt(SYNC_FREQUENCY_MINS, syncFreq); ErrorToException(err); }
MojErr MovePopEmailsCommand::GetEmailsToMoveResponse(MojObject& response, MojErr err) { try { ErrorToException(err); MojLogInfo(m_log, "response to get emails to move: %s", AsJsonString(response).c_str()); MojObject results; err = response.getRequired(_T("results"), results); ErrorToException(err); MojObject::ObjectVec movedEmails; PopClient::AccountPtr account = m_client.GetAccount(); for (MojObject::ConstArrayIterator it = results.arrayBegin(); it != results.arrayEnd(); it++) { MojObject singleMovedEmail; MojObject id; err = it->getRequired(PopEmailAdapter::ID, id); ErrorToException(err); MojObject srcFolderId; err = it->getRequired(EmailSchema::FOLDER_ID, srcFolderId); ErrorToException(err); MojObject destFolderId; err = it->getRequired("destFolderId", destFolderId); ErrorToException(err); MojObject flags; err = it->getRequired("flags", flags); ErrorToException(err); // set to be visible err = flags.put("visible", true); ErrorToException(err); // setup moved email, push onto moved emails vector err = singleMovedEmail.put(PopEmailAdapter::ID, id); ErrorToException(err); err = singleMovedEmail.put("folderId", destFolderId); ErrorToException(err); err = singleMovedEmail.put("destFolderId", MojObject()); ErrorToException(err); err = singleMovedEmail.put("flags", flags); ErrorToException(err); // add emails to be moved into 'movedEmails' list movedEmails.push(singleMovedEmail); if (account->GetInboxFolderId() == srcFolderId) { // add moved inbox emails inot 'm_inboxEmailsMoved' set MojString uid; err = it->getRequired(PopEmailAdapter::SERVER_UID, uid); ErrorToException(err); m_inboxEmailsMoved.insert(std::string(uid)); } } m_client.GetDatabaseInterface().UpdateItems(m_emailsMovedSlot, movedEmails); } catch (const std::exception& ex) { m_msg->replyError(MojErrInternal, ex.what()); Failure(ex); } catch (...) { m_msg->replyError(MojErrInternal); Failure(MailException("unknown exception", __FILE__, __LINE__)); } return MojErrNone; }
void PopAccountAdapter::GetPopAccount(const MojObject& in, const MojObject& transportIn, PopAccount& out) { MojObject id; MojErr err = transportIn.getRequired(ID, id); ErrorToException(err); out.SetId(id); MojObject accntId; err = transportIn.getRequired(ACCOUNT_ID, accntId); ErrorToException(err); out.SetAccountId(accntId); MojString username; err = transportIn.getRequired(USERNAME, username); ErrorToException(err); out.SetUsername(username.data()); MojString hostname; err = transportIn.getRequired(HOST_NAME, hostname); ErrorToException(err); out.SetHostName(hostname.data()); int port; err = transportIn.getRequired(PORT, port); ErrorToException(err); out.SetPort(port); MojString encryptionStr; err = transportIn.getRequired(ENCRYPTION, encryptionStr); ErrorToException(err); out.SetEncryption(encryptionStr.data()); bool initialSync; if (!transportIn.get(INITIAL_SYNC, initialSync)) { // if 'initialSynced' flag doesn't exist, it may come from an account // that has been created without this flag. So set this flag to be // true to indicate that the account has passed the initial sync stage. initialSync = false; } out.SetInitialSync(initialSync); bool deleteFromServer; err = transportIn.getRequired(DELETE_FROM_SERVER, deleteFromServer); ErrorToException(err); out.SetDeleteFromServer(deleteFromServer); bool deleteOnDevice; err = transportIn.getRequired(DELETE_ON_DEVICE, deleteOnDevice); ErrorToException(err); out.SetDeleteOnDevice(deleteOnDevice); MojInt64 syncWindow; if (!transportIn.get(EmailAccountAdapter::SYNC_WINDOW_DAYS, syncWindow)) { // set default value if this field doesn't exist syncWindow = PopAccount::DEFAULT_SYNC_WINDOW; } out.SetSyncWindowDays(syncWindow); ErrorToException(err); // Set sync frequency MojInt64 syncFreq; if (!transportIn.get(SYNC_FREQUENCY_MINS, syncFreq)) { syncFreq = PopAccount::DEFAULT_SYNC_FREQUENCY_MINS; } out.SetSyncFrequencyMins(syncFreq); ErrorToException(err); MojObject error; if(transportIn.get(ERROR, error)) { EmailAccount::AccountError accErr; MojInt64 errorCode; if(error.get(ERROR_CODE, errorCode)) { accErr.errorCode = GetErrorCode(errorCode); } bool found = false; MojString errorText; err = error.get(ERROR_TEXT, errorText, found); ErrorToException(err); if(found) { accErr.errorText = errorText.data(); } out.SetError(accErr); } MojString inboxId; bool exists; err = transportIn.get(INBOX_FOLDERID, inboxId, exists); ErrorToException(err); if (exists) out.SetInboxFolderId(inboxId); MojString outboxId; err = transportIn.get(OUTBOX_FOLDERID, outboxId, exists); ErrorToException(err); if (exists) out.SetOutboxFolderId(outboxId); MojString draftId; err = transportIn.get(DRAFTS_FOLDERID, draftId, exists); ErrorToException(err); if (exists) out.SetDraftsFolderId(draftId); MojString sentId; err = transportIn.get(SENT_FOLDERID, sentId, exists); ErrorToException(err); if (exists) out.SetSentFolderId(sentId); MojString trashId; err = transportIn.get(TRASH_FOLDERID, trashId, exists); ErrorToException(err); if (exists) out.SetTrashFolderId(trashId); }
MojErr MojDbServiceHandler::findImpl(MojServiceMessage* msg, MojObject& payload, MojDbReq& req, MojDbCursor& cursor, bool doCount) { MojAssert(msg); MojLogTrace(s_log); MojObject queryObj; MojErr err = payload.getRequired(MojDbServiceDefs::QueryKey, queryObj); MojErrCheck(err); bool doWatch = false; payload.get(MojDbServiceDefs::WatchKey, doWatch); MojDbQuery query; err = query.fromObject(queryObj); MojErrCheck(err); MojUInt32 limit = query.limit(); if (limit == MojDbQuery::LimitDefault){ query.limit(MaxQueryLimit); } else if (limit > MaxQueryLimit) { MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: limit greater than %d not allowed"), MaxQueryLimit); } if (doWatch) { MojRefCountedPtr<Watcher> watcher(new Watcher(msg)); MojAllocCheck(watcher.get()); err = m_db.find(query, cursor, watcher->m_watchSlot, req); MojErrCheck(err); } else { err = m_db.find(query, cursor, req); MojErrCheck(err); } // append results MojObjectVisitor& writer = msg->writer(); err = writer.beginObject(); MojErrCheck(err); err = writer.boolProp(MojServiceMessage::ReturnValueKey, true); MojErrCheck(err); err = writer.propName(MojDbServiceDefs::ResultsKey); MojErrCheck(err); err = writer.beginArray(); MojErrCheck(err); err = cursor.visit(writer); MojErrCheck(err); err = writer.endArray(); MojErrCheck(err); // append next page MojDbQuery::Page page; err = cursor.nextPage(page); MojErrCheck(err); if (!page.empty()) { MojObject pageObj; err = page.toObject(pageObj); MojErrCheck(err); err = writer.objectProp(MojDbServiceDefs::NextKey, pageObj); MojErrCheck(err); } // append count if (doCount) { MojUInt32 count = 0; err = cursor.count(count); MojErrCheck(err); err = writer.intProp(MojDbServiceDefs::CountKey, (MojInt64) count); MojErrCheck(err); } err = writer.endObject(); MojErrCheck(err); if (doWatch) { // if this is a watched query, it cannot be part of a batch so it's safe to reply here err = msg->reply(); MojErrCheck(err); } // notifications can fire any time after the cursor is closed, // so don't close it until after sending the reply. err = cursor.close(); MojErrCheck(err); return MojErrNone; }
// Copy fields from createAccount payload into com.palm.imap.account object void ImapAccountAdapter::GetImapAccountFromPayload(const MojObject& accountObj, const MojObject& payload, MojObject& imapAccount) { MojErr err = imapAccount.putString(ImapAccountAdapter::KIND, ImapAccountAdapter::SCHEMA); ErrorToException(err); MojObject accountId; err = payload.getRequired(ImapAccountAdapter::ACCOUNT_ID, accountId); ErrorToException(err); err = imapAccount.put(ImapAccountAdapter::ACCOUNT_ID, accountId); ErrorToException(err); MojObject config; err = payload.getRequired(ImapAccountAdapter::CONFIG, config); ErrorToException(err); MojString username; bool hasUsername = false; err = config.get(ImapAccountAdapter::USERNAME, username, hasUsername); ErrorToException(err); if(!hasUsername) { err = accountObj.getRequired(ImapAccountAdapter::USERNAME, username); ErrorToException(err); } err = imapAccount.put(ImapAccountAdapter::USERNAME, username); ErrorToException(err); MojString hostname; err = config.getRequired(ImapAccountAdapter::HOSTNAME, hostname); ErrorToException(err); err = imapAccount.put(ImapAccountAdapter::HOSTNAME, hostname); ErrorToException(err); int port; err = config.getRequired(ImapAccountAdapter::PORT, port); ErrorToException(err); err = imapAccount.put(ImapAccountAdapter::PORT, port); ErrorToException(err); MojString encryption; err = config.getRequired(ImapAccountAdapter::ENCRYPTION, encryption); ErrorToException(err); err = imapAccount.put(ImapAccountAdapter::ENCRYPTION, encryption); ErrorToException(err); /* Optional config parameters; fill in with defaults if not provided */ bool hasSyncFrequency = false; int syncFrequencyMins = 0; config.get(EmailAccountAdapter::SYNC_FREQUENCY_MINS, syncFrequencyMins, hasSyncFrequency); ErrorToException(err); err = imapAccount.putInt(EmailAccountAdapter::SYNC_FREQUENCY_MINS, hasSyncFrequency ? syncFrequencyMins : DEFAULT_SYNC_FREQUENCY_MINS); ErrorToException(err); bool hasSyncWindow = false; int syncWindow = 0; err = config.get(EmailAccountAdapter::SYNC_WINDOW_DAYS, syncWindow, hasSyncWindow); ErrorToException(err); err = imapAccount.putInt(EmailAccountAdapter::SYNC_WINDOW_DAYS, hasSyncWindow ? syncWindow : DEFAULT_SYNC_WINDOW_DAYS); ErrorToException(err); }
MojErr MojDb::putImpl(MojObject& obj, MojUInt32 flags, MojDbReq& req, bool checkSchema) { MojLogTrace(s_log); MojRefCountedPtr<MojDbStorageItem> prevItem; MojObject id; if (obj.get(IdKey, id)) { // get previous revision if we have an id MojErr err = m_objDb->get(id, req.txn(), true, prevItem); MojErrCheck(err); } if (MojFlagGet(flags, FlagIgnoreMissing) && MojFlagGet(flags, FlagMerge)) { if (!prevItem.get()) { MojErr err = obj.putBool(MojDb::IgnoreIdKey, true); // so that we can drop it in output MojErrCheck(err); return MojErrNone; } } MojDbOp op = OpCreate; MojObject* prevPtr = NULL; MojObject prev; if (prevItem.get()) { // deal with prev, if it exists op = OpUpdate; prevPtr = &prev; MojErr err = prevItem->toObject(prev, m_kindEngine); MojErrCheck(err); if (MojFlagGet(flags, FlagMerge)) { // do merge MojObject merged; err = mergeInto(merged, obj, prev); MojErrCheck(err); obj = merged; } else if (!MojFlagGet(flags, FlagForce)) { // if the force flag is not set, throw error if we are updating // an existing, non-deleted object and no rev was specified MojInt64 rev; if (obj.get(RevKey, rev) == false) { bool deleted = false; if (!prev.get(DelKey, deleted) || !deleted) MojErrThrow(MojErrDbRevNotSpecified); } } } // if the new object has a rev and it doesn't match the old rev, don't do the update MojInt64 newRev; if (prevPtr != NULL && obj.get(RevKey, newRev)) { MojInt64 oldRev; MojErr err = prevPtr->getRequired(RevKey, oldRev); MojErrCheck(err); if (!MojFlagGet(flags, FlagForce) && newRev != oldRev) MojErrThrowMsg(MojErrDbRevisionMismatch, _T("db: revision mismatch - expected %lld, got %lld"), oldRev, newRev); } // save it MojErr err = putObj(id, obj, prevPtr, prevItem.get(), req, op, checkSchema); MojErrCheck(err); if (prevItem.get()) { err = prevItem->close(); MojErrCheck(err); } return MojErrNone; }
MojErr ActivityConfigurator::ProcessConfig(const string& filePath, MojObject& params) { MojLogTrace(m_log); const std::string& creator = ParentId(filePath); if (creator.empty()) { MojLogError(m_log, "Service id for activity '%s' is empty - needs to be in an appropriate subdirectory or given as part of the service call", filePath.c_str()); return MojErrInvalidArg; } if (m_firstUseOnly) { bool firstUseSafe; if (!params.get(FIRST_USE_SAFE, firstUseSafe) || !firstUseSafe) { MojLogDebug(m_log, "Running before first use but activity %s not marked as safe for configuration at this time", filePath.c_str()); return MojErrInProgress; } } MojLogDebug(m_log, "ActivityConfigurator creator for %s is %s\n", filePath.c_str(), creator.c_str()); MojObject activity; MojErr err; err = params.getRequired(ACTIVITY, activity); MojErrCheck(err); ConfigType confType; if (m_confType == ConfigUnknown) { if (StartsWith(filePath, APP_DIR)) { confType = ConfigApplication; } else { if (!StartsWith(filePath, SERVICE_DIR)) { //Fix for DFISH-14510 turn down logging here //MojLogWarning(m_log, "Deprecated usage: %s not in application or service subdirectory as required - assumed to be service", filePath.c_str()); } confType = ConfigService; } } else { confType = m_confType; } MojObject creatorObj; switch (confType) { case ConfigApplication: err = creatorObj.putString(APPLICATION_ID, creator.c_str()); break; case ConfigService: err = creatorObj.putString(SERVICE_ID, creator.c_str()); break; case ConfigUnknown: // impossible case assert(false); break; } MojErrCheck(err); err = activity.put(CREATOR, creatorObj); MojErrCheck(err); err = params.put(ACTIVITY, activity); MojErrCheck(err); // strip configurator-specific keys // so that the schema on activity manager isn't violated RemoveKey(params, FIRST_USE_SAFE); return m_busClient.CreateRequest()->send(CreateCallback(filePath)->m_slot, ServiceName(), ACTIVITYMGR_CREATE_METHOD, params); }
//TODO: when the connection is lost (no internet), complete/cancel the activity and exit the service MojErr ConnectionState::ConnectionStateHandler::connectionManagerResult(MojObject& result, MojErr err) { // log the parameters IMServiceHandler::logMojObjectJsonString(_T("ConnectionStateHandler::connectionManagerResult: %s"), result); if (err == MojErrNone && result.contains("$activity")) { MojObject activity, requirements, internetRequirements; // The connectionmanager status is under $activity.requirements.internet err = result.getRequired("$activity", activity); if (err == MojErrNone) { err = activity.getRequired("requirements", requirements); if (err == MojErrNone) { err = requirements.getRequired("internet", internetRequirements); } } if (err == MojErrNone) { m_receivedResponse = true; bool prevInetConnected = m_connState->m_inetConnected; internetRequirements.get("isInternetConnectionAvailable", m_connState->m_inetConnected); bool prevWifiConnected = m_connState->m_wifiConnected; bool found = false; MojObject wifiObj; err = internetRequirements.getRequired("wifi", wifiObj); MojString wifiState; err = wifiObj.getRequired("state", wifiState); m_connState->m_wifiConnected = (err == MojErrNone && wifiState.compare("connected") == 0); // If the connection was lost, keep the old ipAddress so it can be used to // notify accounts that were using it if (m_connState->m_wifiConnected == true) { err = wifiObj.get("ipAddress", m_connState->m_wifiIpAddress, found); if (err != MojErrNone || found == false) { // It may claim to be connected, but there's no interface IP address MojLogError(IMServiceApp::s_log, _T("Marking WiFi as disconnected because ipAddress is missing")); m_connState->m_wifiConnected = false; } MojLogInfo(IMServiceApp::s_log, _T("ConnectionStateHandler::connectionManagerResult found=%i, wifi ipAddress=%s"), found, m_connState->m_wifiIpAddress.data()); } bool prevWanConnected = m_connState->m_wanConnected; MojObject wanObj; err = internetRequirements.getRequired("wan", wanObj); MojErrCheck(err); MojString wanState; err = wanObj.getRequired("state", wanState); MojErrCheck(err); m_connState->m_wanConnected = (err == MojErrNone && wanState.compare("connected") == 0); // If the connection was lost, keep the old ipAddress so it can be used to // notify accounts that were using it if (m_connState->m_wanConnected == true) { err = wanObj.get("ipAddress", m_connState->m_wanIpAddress, found); if (err != MojErrNone || found == false) { // It may claim to be connected, but there's no interface IP address MojLogError(IMServiceApp::s_log, _T("Marking WAN as disconnected because ipAddress is missing")); m_connState->m_wanConnected = false; } MojLogInfo(IMServiceApp::s_log, _T("ConnectionStateHandler::connectionManagerResult found=%i, wan ipAddress=%s"), found, m_connState->m_wanIpAddress.data()); } // If the loginState machine setup a listener, then post the change if (m_loginState != NULL) { if (prevInetConnected != m_connState->m_inetConnected || prevWifiConnected != m_connState->m_wifiConnected || prevWanConnected != m_connState->m_wanConnected) { MojLogInfo(IMServiceApp::s_log, _T("ConnectionStateHandler::connectionManagerResult - connection changed - scheduling activity.")); ConnectionState::ConnectionChangedScheduler *changeScheduler = new ConnectionState::ConnectionChangedScheduler(m_service); changeScheduler->scheduleActivity(); } else { MojLogInfo(IMServiceApp::s_log, _T("ConnectionStateHandler::connectionManagerResult - no change from previous state.")); } } } } else { MojLogInfo(IMServiceApp::s_log, _T("ConnectionStateHandler::connectionManagerResult no activity object - ignoring. err = %d"), err); } return MojErrNone; }