How to intercept incoming email and retrieve message body in thunderbird - email

In my Thunderbird add-on I want to listen to new incoming emails and process the message body.
So I have written a mailListener and added it to an instance of nsIMsgFolderNotificationService.
The listener works fine and notifies when a mail comes. I get the nsIMsgDBHdr object which was fetched, but I cannot stream the message for the particular folder in the msgAdded function of my mailListener. it hangs, and I cannot even see the message body in the Thunderbird's message pane.
I think the nsISyncStreamListener used to stream the message from the folder waits for OnDataAvailable event which is not yet triggered inside the mailListener's msgAdded function.
Any inputs on how to fetch message body when a new email comes? Below is the code for my mailListener
var newMailListener = {
msgAdded: function(aMsgHdr) {
if( !aMsgHdr.isRead ){
let folder = aMsgHdr.folder;
if(aMsgHdr.recipients == "myemail+special#gmail.com"){
let messenger = Components.classes["#mozilla.org/messenger;1"]
.createInstance(Components.interfaces.nsIMessenger);
let listener = Components.classes["#mozilla.org/network/sync-stream-listener;1"]
.createInstance(Components.interfaces.nsISyncStreamListener);
let uri = aMsgHdr.folder.getUriForMsg(aMsgHdr);
messenger.messageServiceFromURI(uri).streamMessage(uri, listener, null, null, false, "");
let messageBody = aMsgHdr.folder.getMsgTextFromStream(listener.inputStream,
aMsgHdr.Charset,
65536,
32768,
false,
true,
{ });
alert("the message body : " + messageBody);
}
}
}
};

I had a similar problem. The solution I found (not easily) is to use MsgHdrToMimeMessage from mimemsg.js as Gloda is not available yet. This uses the callback function:
var newMailListener = {
msgAdded: function(aMsgHdr) {
if( !aMsgHdr.isRead ){
MsgHdrToMimeMessage(aMsgHdr, null, function (aMsgHdr, aMimeMessage) {
// do something with aMimeMessage:
alert("the message body : " + aMimeMessage.coerceBodyToPlaintext());
//alert(aMimeMessage.allUserAttachments.length);
//alert(aMimeMessage.size);
}, true);
}
}
};
And do not forget to include the necessary module:
Components.utils.import("resource:///modules/gloda/mimemsg.js");
More folow up reading can be found e. g. here.

Related

How to implement Microsoft Graph deferred sending

I am trying to implement a deferred sending function to my site which currently sends email via Microsoft Graph. I have found some articles about SingleValueLegacyExtendedProperty being used to defer sending, but so far has been unsuccessful with it.
My current code just ignores the deferred sending time and sends the email immediately.
var message = new Message
{
Subject = Subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = bodyText
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = recipient
}
}
},
};
message.SingleValueExtendedProperties = new MessageSingleValueExtendedPropertiesCollectionPage
{
new SingleValueLegacyExtendedProperty()
{
Id = "SystemTime 0x3FEF",
Value = DateTimeToSend.ToString("o")
}
};
var saveToSentItems = true;
await graphServiceClient.Me
.SendMail(message, saveToSentItems)
.Request()
.PostAsync();
In this article they suggest that the ID should be String {8ECCC264-6880-4EBE-992F-8888D2EEAA1D} Name pidTagDeferredSendTime when passing as JSON but it looks like that was not successful for other. I checked and can confirm that it did not work for me either.
Its important that the DateTime that you want the message to be sent is in UTC eg
"value": "2022-08-01T23:39:00Z"
Using local time won't work as Exchange does everything in UTC

Javascript injection goes wrong

In our Android project (download manager) we need to show built-in web browser so we able to catch downloads there with the all data (headers, cookies, post data) so we can handle them properly.
Unfortunately, WebView control we use does not provide any way to access POST data of the requests it makes.
So we use a hacky way to get this data. We inject this javascript code in the each html code the browser loads:
<script language="JavaScript">
HTMLFormElement.prototype._submit = HTMLFormElement.prototype.submit;
HTMLFormElement.prototype.submit = formSubmitMonitor;
window.addEventListener('submit', function(e) {
formSubmitMonitor(e);
}, true);
function formSubmitMonitor(e) {
var frm = e ? e.target : this;
formSubmitMonitor_onsubmit(frm);
frm._submit();
}
function formSubmitMonitor_onsubmit(f) {
var data = "";
for (i = 0; i < f.elements.length; i++) {
var name = f.elements[i].name;
var value = f.elements[i].value;
//var type = f.elements[i].type;
if (name)
{
if (data !== "")
data += '&';
data += encodeURIComponent(name) + '=' + encodeURIComponent(value);
}
}
postDataMonitor.onBeforeSendPostData(
f.attributes['method'] === undefined ? null : f.attributes['method'].nodeValue,
new URL(f.action, document.baseURI).href,
data,
f.attributes['enctype'] === undefined ? null : f.attributes['enctype'].nodeValue);
}
XMLHttpRequest.prototype.origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
// these will be the key to retrieve the payload
this.recordedMethod = method;
this.recordedUrl = url;
this.origOpen(method, url, async, user, password);
};
XMLHttpRequest.prototype.origSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(body) {
if (body)
{
postDataMonitor.onBeforeSendPostData(
this.recordedMethod,
this.recordedUrl,
body,
null);
}
this.origSend(body);
};
const origFetch = window.fetch;
window.fetch = function()
{
postDataMonitor.onBeforeSendPostData(
"POST",
"test",
"TEST",
null);
return origFetch.apply(this, arguments);
}
</script>
Generally, it works fine.
But in Google Mail web interface, it's not working for some unknown reason. E.g. when the user enters his login name and presses Next. I thought it's using Fetch API, so I've added interception for it too. But this did not help. Please note, that we do not need to intercept the user credentials, but we need to be able to intercept all, or nothing. Unfortunately, this is the way the whole system works there...
Addition #1.
I've found another way: don't override shouldInterceptRequest, but override onPageStarted instead and call evaluateJavascript there. That way it works even on Google Mail web site! But why the first method is not working then? We break HTML code somehow?

Microsoft Bot Framework channel integration: more endpoints?

I'm using Microsoft Bot Framework using the channel registration product and the REST API. I have setup the "messaging endpoint" and everything works fine for sending and receiving messages.
But I don't just want to send/receive messages. Something as simple as setting up a welcome message seems impossible because my endpoint receives nothing other than messaging events (when the bot is in the channel / conversation.)
Is there something I have missed?
I would like to setup several endpoints, or use the same, whatever, to listen to other types of events.
You need to implement in the MessageController something like these:
Pay attention in the else if. The funcition in the controller is HandleSystemMessage.
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
IConversationUpdateActivity update = message;
var cliente = new ConnectorClient(new System.Uri(message.ServiceUrl), new MicrosoftAppCredentials());
if (update.MembersAdded != null && update.MembersAdded.Count > 0)
{
foreach(var member in update.MembersAdded)
{
if(member.Id != message.Recipient.Id)
{
//var username = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
var username = message.From.Name;
var reply = message.CreateReply();
//string dir = System.AppDomain.CurrentDomain.BaseDirectory + "Images" + Path.DirectorySeparatorChar + "cajamar.png";
string dir = HttpRuntime.AppDomainAppPath + "Images" + Path.DirectorySeparatorChar + "cajamar.png";
reply.Attachments.Add(new Attachment(
contentUrl: dir,
contentType: "image/png",
name: "cajamar.png"
));
reply.Text = $"Bienvenido {username} al ChatBot de convenios:";
cliente.Conversations.ReplyToActivity(reply);
//var reply = message.CreateReply();
//reply.Text = $"El directorio base es: {HttpRuntime.AppDomainAppPath}";
//cliente.Conversations.ReplyToActivityAsync(reply);
}
}
}
}

AEM - How to tweak activation error message

We are working in an AEM 6.1 environment and have created an activation preprocessor that will stop pages from being activated if certain attributes are not set. That works great but we'd also like to change the error message that's displayed by the activation process when the preprocessor throws a ReplicationExcdeption. Can anyone point me to the code that actually displays the error message?
We overrided several functions in SiteAdmin.Actions.js. Copy it from libs folder /apps/cq/ui/widgets/source/widgets/wcm/SiteAdmin.Actions.js or use CQ.Ext.override
We need to override CQ.wcm.SiteAdmin.scheduleForActivation and CQ.wcm.SiteAdmin.internalActivatePage methods.
We do it with using the following code
CQ.wcm.SiteAdmin.internalActivatePage = function(paths, callback) {
if (callback == undefined) {
// assume scope is admin and reload grid
var admin = this;
callback = function(options, success, response) {
if (success) admin.reloadPages();
else admin.unmask();
};
}
preActionCallback = function(options, success, response) {
if (success) {
var responseObj = CQ.Util.eval(response);
if (responseObj.activation) {
CQ.HTTP.post(
CQ.shared.HTTP.externalize("/bin/replicate.json"),
callback,
{ "_charset_":"utf-8", "path":paths, "cmd":"Activate" }
);
} else {
CQ.wcm.SiteAdmin.preactivateMessage(responseObj);
}
}else{
CQ.Ext.Msg.alert(
CQ.I18n.getMessage("Error"), CQ.I18n.getMessage("Could not activate page."));
}
admin.unmask();
};
CQ.HTTP.get(
"/apps/sling/servlet/content/preActivateValidator.html?path=" + paths,
preActionCallback
);
};
This path /apps/sling/servlet/content/preActivateValidator.html (You can use any other link and extension) returns json with some info about messages, which are parsed in custom method and generates custom error messages CQ.wcm.SiteAdmin.preactivateMessage:
CQ.wcm.SiteAdmin.preactivateMessage = function(responseObj) {
var message = "";
var incorrectItems = responseObj.incorrectItems;
if (responseObj.countOfIncorrectItems > 1) message = message + "s";
if (responseObj.missingMetadata) {
message = message + "Please, set \"Programming Type\" for next videos:<br/>";
var missingMetadataPaths = responseObj.missingMetadata;
for(var i = 0; i < missingMetadataPaths.length; i++){
message = message + ""+missingMetadataPaths[i].path+"<br/>";
}
message += "<br/>";
}
if(message == ""){
message = "Unknown error.";
}
CQ.Ext.Msg.alert(
CQ.I18n.getMessage("Error"), CQ.I18n.getMessage(message));
}
So you can implement component or servlet which will verify your attributes and will generate JSON.

Zend Dojo. Ajax submit dojo form

How to submit dojo form using AJAX and if there are errors, print errors near incorrectly filled fields?
Now I am doing something like that:
dojo.ready(function() {
var form = dojo.byId("user_profile_form");
dojo.connect(form, "onsubmit", function(event){
dojo.stopEvent(event);
var xhrArgs = {
form: form,
handleAs: "json",
load: function(responseText){
var result_data = zen.json.getResult(responseText);
dojo.byId("response").innerHTML = "Form posted.";
},
error: function(error){
// We'll 404 in the demo, but that's okay. We don't have a 'postIt' service on the
// docs server.
dojo.byId("response").innerHTML = "Form posted.";
}
}
// Call the asynchronous xhrPost
dojo.byId("response").innerHTML = "Form being sent..."
var deferred = dojo.xhrPost(xhrArgs);
});
But I don't know how to print errors
There are a few ways that you can do this. The one that I prefer is to subscribe to the IO Pipeline Topics
For errors, subscribe to the /dojo/io/error topic. Here's an example that will Growl the errors.
dojo.subscribe("/dojo/io/error", function(/*dojo.Deferred*/ dfd, /*Object*/ error){
// Triggered whenever an IO request has errored.
// It passes the error and the dojo.Deferred
// for the request with the topic.
var responseTextObject = dojo.fromJson(error.responseText)
var growlMessage = '';
if (responseTextObject && responseTextObject.message) {
growlMessage += responseTextObject.message
} else {
// Don't Growl the xhr cancelled messages.
if (error.message == 'xhr cancelled') {
return;
}
growlMessage = error.message
}
new ext.Growl({
message: growlMessage
});
});
The server should provide all the error details in the response. In this example, a JSON formatted response is expected but if it's not provided, the error is still shown.
If you want to see the nice invalid field styling, put the widgets in a dijit.form.Form