/*
     * When 'node' has been created under 'parent' (by the sessionContext user) this will send a
     * notification to foreign servers. This call returns immediately and delegates the actuall
     * proccessing to a daemon thread.
     * 
     * For concurrency reasons, note that we pass in the nodeId to this method rather than the node even
     * if we do have the node, because we want to make sure there's no concurrent access.
     * 
     * IMPORTANT: This method ONLY sends notifications to users who ARE in the 'acl' which means these
     * can only be users ALREADY imported into the system, however this is ok, because we will have
     * called saveMentionsToNodeACL() right before calling this method so the 'acl' should completely
     * contain all the mentions that exist in the text of the message.
     * 
     * todo-0: we should probably extract OUT of this method the actual construction of the 'message'
     * itself and then pass the message in as a parameter, and we'd be passing in without setting the
     * 'cc + to' properties on the message and then the message would have those added inside here
     * insead of building the entire message from scratch.
     */
public void sendObjOutbound(MongoSession ms, SubNode parent, SubNode node, boolean forceSendToPublic) {
        exec.run(() -> {
            try {
                boolean isAccnt = NodeType.ACCOUNT.s().equals(node.getType());
                // Get the inReplyTo from the parent property (foreign node) or if not found generate one based on
                // what the local server version of it is.
                String inReplyTo = !isAccnt ? apUtil.buildUrlForReplyTo(ms, parent) : null;
                APList attachments = !isAccnt ? apub.createAttachmentsList(node) : null;
                String replyToType = parent.getStr(NodeProp.ACT_PUB_OBJ_TYPE);
                String boostTarget = parent.getStr(NodeProp.BOOST);
                // toUserNames will hold ALL usernames in the ACL list (both local and foreign user names)
                HashSet<String> toUserNames = new HashSet<>();
                boolean privateMessage = true;
                if (forceSendToPublic) {
                    privateMessage = false;
                } else {
                    if (ok(node.getAc())) {
                        /*
                         * Lookup all userNames from the ACL info, to add them all to 'toUserNames'
                         */
                        for (String accntId : node.getAc().keySet()) {
                            if (PrincipalName.PUBLIC.s().equals(accntId)) {
                                privateMessage = false;
                            } else {
                                SubNode accntNode = cachedGetAccntNodeById(ms, accntId);
                                // get username off this node and add to 'toUserNames'
                                if (ok(accntNode)) {
                                    toUserNames.add(accntNode.getStr(NodeProp.USER));
                                }
                            }
                        }
                    }
                }
                // String apId = parent.getStringProp(NodeProp.ACT_PUB_ID.s());
                String fromUser = ThreadLocals.getSC().getUserName();
                String fromActor = apUtil.makeActorUrlForUserName(fromUser);
                String privateKey = apCrypto.getPrivateKey(ms, fromUser);
                String objUrl = snUtil.getIdBasedUrl(node);
                APObj message = null;
                if (node.getType().equals(NodeType.ACCOUNT.s())) {
                    // construct the Update-type wrapper around teh Person object, and send
                    message = apFactory.newUpdateForPerson(fromUser, toUserNames, fromActor, privateMessage, node);
                    log.debug("Sending updated Person outbound: " + XString.prettyPrint(message));
                } else {
                    // if this node has a boostTarget, we know it's an Announce so we send out the announce
                    // todo-0: we should probably rely on if there's an ActPub TYPE itself that's "Announce" (we save
                    // that right?)
                    if (!StringUtils.isEmpty(boostTarget)) {
                        ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
                        message =
                                apFactory.newAnnounce(fromUser, fromActor, objUrl, toUserNames, boostTarget, now, privateMessage);
                    }
                    // else send out as a note.
                    else {
                        message = apFactory.newCreateForNote(fromUser, toUserNames, fromActor, inReplyTo, replyToType,
                                node.getContent(), objUrl, privateMessage, attachments);
                    }
                }
                // for users that don't have a sharedInbox we collect their inboxes here to send to them
                // individually
                HashSet<String> userInboxes = new HashSet<>();
                // When posting a public message we send out to all unique sharedInboxes here
                if (!privateMessage) {
                    HashSet<String> sharedInboxes = new HashSet<>();
                    // loads ONLY foreign user's inboxes into the two sets.
                    getSharedInboxesOfFollowers(fromUser, sharedInboxes, userInboxes);
                    // merge both sets of inboxes into allInboxes and send to them
                    HashSet<String> allInboxes = new HashSet<>(userInboxes);
                    allInboxes.addAll(sharedInboxes);
                    apUtil.securePostEx(allInboxes, fromActor, privateKey, fromActor, message, APConst.MTYPE_LD_JSON_PROF);
                }
                // Post message to all foreign usernames found in 'toUserNames', but skip all in userInboxes becasue
                // we just sent to those above.
                if (toUserNames.size() > 0) {
                    sendMessageToUsers(ms, toUserNames, fromUser, message, privateMessage, userInboxes);
                }
            } //
            catch (Exception e) {
                log.error("sendNote failed", e);
                throw new RuntimeException(e);
            }
        });
    }
public void sendObjOutbound(MongoSession ms, SubNode parent, SubNode node, boolean forceSendToPublic) {
exec.run(() -> {
try {
boolean isAccnt = NodeType.ACCOUNT.s().equals(node.getType());
// Get the inReplyTo from the parent property (foreign node) or if not found generate one based on
// what the local server version of it is.
String inReplyTo = !isAccnt ? apUtil.buildUrlForReplyTo(ms, parent) : null;
APList attachments = !isAccnt ? apub.createAttachmentsList(node) : null;
String replyToType = parent.getStr(NodeProp.ACT_PUB_OBJ_TYPE);
String boostTarget = parent.getStr(NodeProp.BOOST);
// toUserNames will hold ALL usernames in the ACL list (both local and foreign user names)
HashSet<String> toUserNames = new HashSet<>();
boolean privateMessage = true;
if (forceSendToPublic) {
privateMessage = false;
} else {
if (ok(node.getAc())) {
/*
* Lookup all userNames from the ACL info, to add them all to 'toUserNames'
*/
for (String accntId : node.getAc().keySet()) {
if (PrincipalName.PUBLIC.s().equals(accntId)) {
privateMessage = false;
} else {
SubNode accntNode = cachedGetAccntNodeById(ms, accntId);
// get username off this node and add to 'toUserNames'
if (ok(accntNode)) {
toUserNames.add(accntNode.getStr(NodeProp.USER));
}
}
}
}
}
// String apId = parent.getStringProp(NodeProp.ACT_PUB_ID.s());
String fromUser = ThreadLocals.getSC().getUserName();
String fromActor = apUtil.makeActorUrlForUserName(fromUser);
String privateKey = apCrypto.getPrivateKey(ms, fromUser);
String objUrl = snUtil.getIdBasedUrl(node);
APObj message = null;
if (node.getType().equals(NodeType.ACCOUNT.s())) {
// construct the Update-type wrapper around teh Person object, and send
message = apFactory.newUpdateForPerson(fromUser, toUserNames, fromActor, privateMessage, node);
log.debug("Sending updated Person outbound: " + XString.prettyPrint(message));
} else {
// if this node has a boostTarget, we know it's an Announce so we send out the announce
// todo-0: we should probably rely on if there's an ActPub TYPE itself that's "Announce" (we save
// that right?)
if (!StringUtils.isEmpty(boostTarget)) {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
message =
apFactory.newAnnounce(fromUser, fromActor, objUrl, toUserNames, boostTarget, now, privateMessage);
}
// else send out as a note.
else {
message = apFactory.newCreateForNote(fromUser, toUserNames, fromActor, inReplyTo, replyToType,
node.getContent(), objUrl, privateMessage, attachments);
}
}
// for users that don't have a sharedInbox we collect their inboxes here to send to them
// individually
HashSet<String> userInboxes = new HashSet<>();
// When posting a public message we send out to all unique sharedInboxes here
if (!privateMessage) {
HashSet<String> sharedInboxes = new HashSet<>();
// loads ONLY foreign user's inboxes into the two sets.
getSharedInboxesOfFollowers(fromUser, sharedInboxes, userInboxes);
// merge both sets of inboxes into allInboxes and send to them
HashSet<String> allInboxes = new HashSet<>(userInboxes);
allInboxes.addAll(sharedInboxes);
apUtil.securePostEx(allInboxes, fromActor, privateKey, fromActor, message, APConst.MTYPE_LD_JSON_PROF);
}
// Post message to all foreign usernames found in 'toUserNames', but skip all in userInboxes becasue
// we just sent to those above.
if (toUserNames.size() > 0) {
sendMessageToUsers(ms, toUserNames, fromUser, message, privateMessage, userInboxes);
}
} //
catch (Exception e) {
log.error("sendNote failed", e);
throw new RuntimeException(e);
}
});
}
public void sendObjOutbound(MongoSession ms, SubNode parent, SubNode node, boolean forceSendToPublic) {
exec.run(() -> {
try {
boolean isAccnt = NodeType.ACCOUNT.s().equals(node.getType());
// Get the inReplyTo from the parent property (foreign node) or if not found generate one based on
// what the local server version of it is.
String inReplyTo = !isAccnt ? apUtil.buildUrlForReplyTo(ms, parent) : null;
APList attachments = !isAccnt ? apub.createAttachmentsList(node) : null;
String replyToType = parent.getStr(NodeProp.ACT_PUB_OBJ_TYPE);
String boostTarget = parent.getStr(NodeProp.BOOST);
// toUserNames will hold ALL usernames in the ACL list (both local and foreign user names)
HashSet<String> toUserNames = new HashSet<>();
boolean privateMessage = true;
if (forceSendToPublic) {
privateMessage = false;
} else {
if (ok(node.getAc())) {
/*
* Lookup all userNames from the ACL info, to add them all to 'toUserNames'
*/
for (String accntId : node.getAc().keySet()) {
if (PrincipalName.PUBLIC.s().equals(accntId)) {
privateMessage = false;
} else {
SubNode accntNode = cachedGetAccntNodeById(ms, accntId);
// get username off this node and add to 'toUserNames'
if (ok(accntNode)) {
toUserNames.add(accntNode.getStr(NodeProp.USER));
}
}
}
}
}
// String apId = parent.getStringProp(NodeProp.ACT_PUB_ID.s());
String fromUser = ThreadLocals.getSC().getUserName();
String fromActor = apUtil.makeActorUrlForUserName(fromUser);
String privateKey = apCrypto.getPrivateKey(ms, fromUser);
String objUrl = snUtil.getIdBasedUrl(node);
APObj message = null;
if (node.getType().equals(NodeType.ACCOUNT.s())) {
// construct the Update-type wrapper around teh Person object, and send
message = apFactory.newUpdateForPerson(fromUser, toUserNames, fromActor, privateMessage, node);
log.debug("Sending updated Person outbound: " + XString.prettyPrint(message));
} else {
// if this node has a boostTarget, we know it's an Announce so we send out the announce
// todo-0: we should probably rely on if there's an ActPub TYPE itself that's "Announce" (we save
// that right?)
if (!StringUtils.isEmpty(boostTarget)) {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
message =
apFactory.newAnnounce(fromUser, fromActor, objUrl, toUserNames, boostTarget, now, privateMessage);
}
// else send out as a note.
else {
message = apFactory.newCreateForNote(fromUser, toUserNames, fromActor, inReplyTo, replyToType,
node.getContent(), objUrl, privateMessage, attachments);
}
}
// for users that don't have a sharedInbox we collect their inboxes here to send to them
// individually
HashSet<String> userInboxes = new HashSet<>();
// When posting a public message we send out to all unique sharedInboxes here
if (!privateMessage) {
HashSet<String> sharedInboxes = new HashSet<>();
// loads ONLY foreign user's inboxes into the two sets.
getSharedInboxesOfFollowers(fromUser, sharedInboxes, userInboxes);
// merge both sets of inboxes into allInboxes and send to them
HashSet<String> allInboxes = new HashSet<>(userInboxes);
allInboxes.addAll(sharedInboxes);
apUtil.securePostEx(allInboxes, fromActor, privateKey, fromActor, message, APConst.MTYPE_LD_JSON_PROF);
}
// Post message to all foreign usernames found in 'toUserNames', but skip all in userInboxes becasue
// we just sent to those above.
if (toUserNames.size() > 0) {
sendMessageToUsers(ms, toUserNames, fromUser, message, privateMessage, userInboxes);
}
} //
catch (Exception e) {
log.error("sendNote failed", e);
throw new RuntimeException(e);
}
});
}
public void sendObjOutbound(MongoSession ms, SubNode parent, SubNode node, boolean forceSendToPublic) {
exec.run(() -> {
try {
boolean isAccnt = NodeType.ACCOUNT.s().equals(node.getType());
// Get the inReplyTo from the parent property (foreign node) or if not found generate one based on
// what the local server version of it is.
String inReplyTo = !isAccnt ? apUtil.buildUrlForReplyTo(ms, parent) : null;
APList attachments = !isAccnt ? apub.createAttachmentsList(node) : null;
String replyToType = parent.getStr(NodeProp.ACT_PUB_OBJ_TYPE);
String boostTarget = parent.getStr(NodeProp.BOOST);
// toUserNames will hold ALL usernames in the ACL list (both local and foreign user names)
HashSet<String> toUserNames = new HashSet<>();
boolean privateMessage = true;
if (forceSendToPublic) {
privateMessage = false;
} else {
if (ok(node.getAc())) {
/*
* Lookup all userNames from the ACL info, to add them all to 'toUserNames'
*/
for (String accntId : node.getAc().keySet()) {
if (PrincipalName.PUBLIC.s().equals(accntId)) {
privateMessage = false;
} else {
SubNode accntNode = cachedGetAccntNodeById(ms, accntId);
// get username off this node and add to 'toUserNames'
if (ok(accntNode)) {
toUserNames.add(accntNode.getStr(NodeProp.USER));
}
}
}
}
}
// String apId = parent.getStringProp(NodeProp.ACT_PUB_ID.s());
String fromUser = ThreadLocals.getSC().getUserName();
String fromActor = apUtil.makeActorUrlForUserName(fromUser);
String privateKey = apCrypto.getPrivateKey(ms, fromUser);
String objUrl = snUtil.getIdBasedUrl(node);
APObj message = null;
if (node.getType().equals(NodeType.ACCOUNT.s())) {
// construct the Update-type wrapper around teh Person object, and send
message = apFactory.newUpdateForPerson(fromUser, toUserNames, fromActor, privateMessage, node);
log.debug("Sending updated Person outbound: " + XString.prettyPrint(message));
} else {
// if this node has a boostTarget, we know it's an Announce so we send out the announce
// todo-0: we should probably rely on if there's an ActPub TYPE itself that's "Announce" (we save
// that right?)
if (!StringUtils.isEmpty(boostTarget)) {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
message =
apFactory.newAnnounce(fromUser, fromActor, objUrl, toUserNames, boostTarget, now, privateMessage);
}
// else send out as a note.
else {
message = apFactory.newCreateForNote(fromUser, toUserNames, fromActor, inReplyTo, replyToType,
node.getContent(), objUrl, privateMessage, attachments);
}
}
// for users that don't have a sharedInbox we collect their inboxes here to send to them
// individually
HashSet<String> userInboxes = new HashSet<>();
// When posting a public message we send out to all unique sharedInboxes here
if (!privateMessage) {
HashSet<String> sharedInboxes = new HashSet<>();
// loads ONLY foreign user's inboxes into the two sets.
getSharedInboxesOfFollowers(fromUser, sharedInboxes, userInboxes);
// merge both sets of inboxes into allInboxes and send to them
HashSet<String> allInboxes = new HashSet<>(userInboxes);
allInboxes.addAll(sharedInboxes);
apUtil.securePostEx(allInboxes, fromActor, privateKey, fromActor, message, APConst.MTYPE_LD_JSON_PROF);
}
// Post message to all foreign usernames found in 'toUserNames', but skip all in userInboxes becasue
// we just sent to those above.
if (toUserNames.size() > 0) {
sendMessageToUsers(ms, toUserNames, fromUser, message, privateMessage, userInboxes);
}
} //
catch (Exception e) {
log.error("sendNote failed", e);
throw new RuntimeException(e);
}
});
}