Ejabberd - get user from multi user chat message using Smack XMPP client or enforce nickname - xmpp

I am running an ejabberd server with a series of locked down multi user chats (members only, registration required, no subject change or PMs permitted etc)
One requirement is to strictly identify which users (from their user Id/account Jid when registering with the server) are present in each room, and which user has sent a message.
How this is achieved is not important, it can either be:
a) By getting the userId from message.getFrom()
b) By getting the nick/resource part from the message sender, and enforcing what nick a user can choose
In direct messages, the Jid of a sender will look like:
<userId>#<domain>/<resourcepart>
so I can take the userId (LocalPart) and not worry too much what nickname was chosen.
This is not possible in multi user chats however, since the Jid will appear as:
<roomName>#conference.<domain>/<resourcepart>
The userId of the sender is not present, so I have to rely on the nickname, but this can be set to anything by the users (and changed at any point in the chat)
Is there a way to enforce how a nick is set? (i.e. set to the same value as userId) or otherwise extract the userId from a multi user chat message?

As I wrote, you need a non-anonymous room. The real XMPP address (JID) of a room occupant will then be part of the participant's presence (XEP-0045 § 7.2.3). You can obtain the presence of a occupant via MultiUserChat.getOccupantPresence​(EntityFullJid user). From this Presence you want to extra the MUCUser information via MUCUser.from(presence). From which you extra the MUCIitem which should allow to retrieve the real JID via MUCItem.getJid()1.
1: Note that the javadoc if this method seems to be misleading, it should contain the real JID of the user and not the MUC JID.

There is a room option that allows all room occupants to view the real Jabber ID of other occupants. By default only room moderators can view those real Jabber ID.
An alternative would be to customize the source code to only accept a room join if the nick is identical to the username in the JID, and don't accept any nick change afterwards.

The answer given above by Flow works well for users who are still present in the room. However, for historic messages where the user has left the room, the Presence will not be available.
For users without a Presence, the message stanza will contain an address node, e.g.:
<message
xmlns='jabber:client'
xml:lang='en'
to='bob#example.com/12345'
from='dummyroom#conference.example.com/johnny'
id='purple44d872cb' type='groupchat'>
<addresses xmlns='http://jabber.org/protocol/address'>
<address xmlns='http://jabber.org/protocol/address' type='ofrom' jid='john#example.com/12345'/>
</addresses>
<delay xmlns='urn:xmpp:delay' stamp='2023-01-27T10:08:59.594+00:00' from='dummyroom#conference.example.com'/>
<body>me</body>
</message>
To extract this in smack I've called the message toXML() method to get the stanza (required upgrade to smack v4.4.x), then used an XML parser to extract the jid attribute, i.e.:
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.impl.JidCreate;
public static EntityFullJid getUserJIDFromMessage(MultiUserChat muc, Message message) {
EntityFullJid jid = null;
//1. Extract JID from presence
try {
EntityFullJid channelJid = JidCreate.entityFullFrom(message.getFrom());
jid = extractJidFromPresence(muc, channelJid);
if (jid != null) return jid;
} catch (Exception e) {}
//2. If presence unavailable, parse the stanza
Document messageDoc = Jsoup.parse(message.toXML().toString());
for (Element address: messageDoc.select("addresses").select("address")) {
if (address.attr("type").equals("ofrom")) {
try {
jid = JidCreate.entityFullFrom(address.attr("jid"));
return jid;
} catch (Exception e) {}
}
} return null;
}
private static EntityFullJid extractJidFromPresence(MultiUserChat muc, EntityFullJid channelJid) {
EntityFullJid jid = null;
try {
MUCUser mucUser = MUCUser.from(muc.getOccupantPresence(channelJid));
jid = (EntityFullJid) mucUser.getItem().getJid();
return jid;
} catch (Exception e) {}
return jid;
}

Related

Error in processing inbound mail in Apex Email Services

I have cretaed an Emaail service and associated an Apex class with it. The Apex class does a simple job of checking the subject of the mail and insert a record in the contact object, if the suject matches witha particular string. The last name of the contact is extracted from the inbound mail body.
The code is as follows:
global class CreateContactFrmEmail implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
String subToCompare = 'Create Contact';
if(email.subject.equalsIgnoreCase(subToCompare))
{
Contact c = new Contact();
c.LastName = email.plainTextBody;
insert c;
}
result.success = true;
return result;
}
}
However, whenever I send a mail to the generated email address, it is unable to create the contact, the mail bounces and the following exception is reported.
Any help in this regard is appreciated. TIA.

Outlook draft message changes immutable ID when sent

Context I am using: office-js (retrieve rest ID of message item), java backend (using GraphClient to get the immutable ID, subscription webhook endpoint)
When I get the rest itemId of the draft item via office-js like this:
Office.context.mailbox.item.saveAsync((asyncResult) => {
if (asyncResult.error) {
//hadle
} else {
resolve(
Office.context.mailbox.convertToRestId
(
asyncResult.value,
Office.MailboxEnums.RestVersion.v1_0
)
);
}
});
I send it to the backend where I translate it to Immutable ID, via GraphClient, that I save.
Once I get a notification on my subscription endpoint (I change and save the subject of the message draft
in outlook), it is successfully paired.
Problem is when I send the draft from outlook. I get notification to the subscription enpoint, but it has a different immutable ID. I create subscriptions with Prefer header like this:
Subscription subscription = new Subscription();
subscription.changeType = "updated";
subscription.notificationUrl = notificationUrl;
subscription.resource = resource;
subscription.expirationDateTime = OffsetDateTime.now().plusDays(2);
subscription.clientState = secret;
subscription.latestSupportedTlsVersion = "v1_2";
SubscriptionCollectionRequest request = graphServiceClient.subscriptions().buildRequest();
if(request != null) {
request.addHeader("Prefer", "IdType=\"ImmutableId\"");
request.post(subscription);
} else {
Is there anything I am doing wrong? Draft is move to the "Sent items" folder, which should not change immutable ID (https://learn.microsoft.com/en-us/graph/outlook-immutable-id).
Ids looks like this AAkALgAAA.........yACqAC-EWg0AC.......7B4s_RdwAA....TwAA I suppose they are correct. Just last section after underscore changes on draft sent.
Not surprising at all - it is a physically different message. Just the way Exchange works - sent/unsent flag cannot be flipped after the message is saved, so a new message is created in the Sent Items folder.

SignalR sending notification to a specific client it's not working

When sending a notification with SignalR to a specific user it's not working.
I'm storing connection IDs in a table and when the notification should be sent I get the connection ID for the receiver from the DB but he doesn't get anything. What is wrong with my code?
// get the connectionId of the receiver
if (_db.UserConnectionid != null)
{
var userConn = await _db.UserConnectionid.Where(x => x.UserId == receiver).Select(x => x.ConnectionId).FirstOrDefaultAsync();
//if the receiver is online
if (userConn != null)
{
await Clients.Client(userConn).SendAsync("RecieveMessage", message);
}
}
I'm storing connection IDs in a table and when the notification should be sent I get the connection ID for the receiver from the DB but he doesn't get anything. What is wrong with my code?
Firstly, please note that a user could have more than one connection id, to troubleshoot the issue, you can try to debug the code and make sure the connection id you retrieved from db is same as the one of current connecting user.
Besides, to send message to a specific user, you can try to get all stored connection id(s) of a specific user/receiver, then send message by specify connectionIds, like below.
var ConnectionIds = _db.UserConnectionid.Where(x => x.UserId == receiver).Select(x => x.ConnectionId).ToList();
if (ConnectionIds.Count > 0)
{
await Clients.Clients(ConnectionIds).SendAsync("RecieveMessage", message);
}
If you are using default user claims mechanism for authorization, you probably can take a look into this mechanism:
https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0#use-claims-to-customize-identity-handling
If you will provide IUserIdProvider service or map userId to ClaimTypes.NameIdentifier claim, you will be able to filter your SignalR clients by stringified user id using this method:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.signalr.ihubclients-1.user?view=aspnetcore-5.0#Microsoft_AspNetCore_SignalR_IHubClients_1_User_System_String_
Like this:
await Clients.User(receiver.ToString()).SendAsync("RecieveMessage", message);
As recommended in the article, don’t store Id’s.
https://consultwithgriff.com/signalr-connection-ids/

Send mail for multiple user and the each recipents have only his email address not the others

I am new bie for send grid. I have checked this url for two emails "https://sendgrid.com/api/mail.send.js..." and got the mail successfully in both emails.
The mail received by the users from the above URL have both email address in "TO" field like
For ex. User Test To: test#example.com;test2#example.com. and For User test2 To: test#example.com;test2#example.com.
As per my requirement i want to send mail for multiple user and the each recipents have only his email address not the others.
For ex. User Test To: test#example.com and For User test2 To: test2#example.com.
Can this scenario is possible with send grid.
Thanks
You can send the same email to multiple recipients by using the SendGrid SMTP API's to parameter.
To do this you'll set an X-SMTPAPI: header in your message, this header will include the JSON to communicate with SendGrid's SMTP API. The header will look as such:
X-SMTPAPI: { "to": [ "test#example.com", "test2#example.com" ] }
Each recipient will receive a unique email, addressed only to them.
Be aware: you'll still need to set a To: header for the message to send, however you can set this to yourself or one of the recipients (and then exclude them from the JSON list).
Send grid being a standard SMTP server will respond as if you are sending email from gmail, yahoo or outlook.
The scenario is not possible that I am aware of. I have only incorporated it into 2 applications, so I am certain there are better experts.
As an alternative you could test using the blind copy, The problem with that is would need a main address in the To field, which may not fill your requirement.
Send email on Azure Mobile Service /NodeJS
var sendgrid = new SendGrid('KEY');
sendgrid.send({
to: toEMail, // ["email1#mail.com", "email2#mail.com",...]
from: fromEMail,
subject: subject,
text: body
}, function (success, message) {
console.log("send mail: " + subject);
// If the email failed to send, log it as an error so we can investigate
if (!success) {
console.error(message);
}
});
possible on Sendgrid. You can use the normal bcc on sendgrid via personalization, but we dont like it because the To: is always required. So my solution is sendgrid transactional email. This will send 1 email to multiple users (using sendgrid / php / laravel ):
$email = new \SendGrid\Mail\Mail();
$email->setFrom("sender#mail.com", "Sender Name");
$email->setSubject("Your subject");
$email->addTo(
"email.1#mail.com",
"User One",
[],
0
);
$email->addTo(
"email.2#mail.com",
"User Two",
[],
1
);
$email->addTo(
"email.3#mail.com",
"User Three",
[],
2
);
$email->addContent("text/plain", "your body");
$email->addContent("text/html", "<strong>your body</body>");
$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));
try {
$response = $sendgrid->send($email);
return response()->json($response, 200);
} catch (Exception $e) {
return response()->json($e->getMessage(), 400);
}

Java send mail - how to use "Send via"

We are sending mails from our local system.
We got our IPs white listed.
We have a scenario where we have to send email on behalf of somebody.
for ex: our email id is: support#mycompany.com
but we need to send email with a from address: john#abc.com
When we send with different from address, the receiving mail client displays "phishing" error.
One of the solution is to use "via" as dispayed in google link
https://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=185812
We also want the message to be displayed like this in receivers inbox.
Any pointers in this will help us a lot.
thanks in advance.
Note: We are using localhost as the smtp.
Read about email headers. you can add email headers while creating the mail message at runtime.
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
public void postMail( String recipients[ ], String subject, String message , String from) throws MessagingException
{
boolean debug = false;
//Set the host smtp address
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.jcom.net");
// create some properties and get the default Session
Session session = Session.getDefaultInstance(props, null);
session.setDebug(debug);
// create a message
Message msg = new MimeMessage(session);
// set the from and to address
InternetAddress addressFrom = new InternetAddress(from);
msg.setFrom(addressFrom);
InternetAddress[] addressTo = new InternetAddress[recipients.length];
for (int i = 0; i < recipients.length; i++)
{
addressTo[i] = new InternetAddress(recipients[i]);
}
msg.setRecipients(Message.RecipientType.TO, addressTo);
// Optional : You can also set your custom headers in the Email if you Want
msg.addHeader("MyHeaderName", "myHeaderValue");
// Setting the Subject and Content Type
msg.setSubject(subject);
msg.setContent(message, "text/plain");
Transport.send(msg);
}
for further reading check this :
http://www.javacommerce.com/displaypage.jsp?name=javamail.sql&id=18274
http://javamail.kenai.com/nonav/javadocs/javax/mail/internet/package-summary.html
#http://javamail.kenai.com/nonav/javadocs/javax/mail/internet/MimeMessage.html
You can create aliases for the smtp server too.