iam using mail-listener2 to download files from an gmail account
it works and I can see the files on the mailListener.on("mail",) event but it looks like
files r not save on the attachments folder
on the file event the file.path is undefined so I am guessing it dit not saved...
why I can't see the files?
why there is a .on("attachment") event id i can see the files on "mail" event?
i only want to process png files, is there any filter i can use?
this is my code...
thanks
import path from "path";
var MailListener = require("mail-listener2");
export class InvoiceFileLisetner {
private readonly hostName: string = 'imap.gmail.com'
private readonly userName: string = 'username';
private readonly password: string = 'password';
private readonly port: number;
public Listen() {
var mailListener = new MailListener({
username: this.userName,
password: this.password,
host: this.hostName,
port: 993, // imap port
tls: true,
connTimeout: 10000, // Default by node-imap
authTimeout: 5000, // Default by node-imap,
debug: console.log, // Or your custom function with only one incoming argument. Default: null
tlsOptions: { rejectUnauthorized: false },
mailbox: "INBOX", // mailbox to monitor
searchFilter: ["UNSEEN"], // the search filter being used after an IDLE notification has been retrieved
markSeen: true, // all fetched email willbe marked as seen and not fetched next time
fetchUnreadOnStart: true, // use it only if you want to get all unread email on lib start. Default is `false`,
mailParserOptions: { streamAttachments: true }, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: { directory: path.join(__dirname, '\\attachments/') }
});
mailListener.start(); // start listening
mailListener.on("server:connected", function () {
console.log("imapConnected");
});
mailListener.on("server:disconnected", function () {
console.log("imapDisconnected");
});
mailListener.on("error", function (err) {
console.log(err);
});
mailListener.on("mail", function (mail, seqno, attributes) {
if (mail.attachments != undefined) {
let accountName = mail.subject;
let invoiceFile = null;
for (let index = 0; index < mail.attachments.length; index++) {
if (mail.attachments[index].contentType == 'application/vnd.ms-excel') {
}
}
}
// mail processing code goes here
});
mailListener.on("attachment", function (attachment) {
console.log(attachment.path);
});
}
}
Related
I'm currently using mail-listener5 package to assert the number of emails in the Inbox of an email address.
My test framework is nightwatchjs.
Currently, my command script to get the number of emails from the email address inbox (titled getInboxEmails) is;
var {MailListener} = require('mail-listener5');
exports.command = function() {
this.perform(function(done) {
var mailListener = new MailListener({
username: '##########outlook.com',
password: '#########',
host: 'imap-mail.outlook.com',
port: 993,
tls: true,
connTimeout: 10000,
authTimeout: 5000,
tlsOptions: { rejectUnauthorized: false },
mailbox: 'Inbox',
searchFilter: ['ALL'],
markSeen: true,
fetchUnreadOnStart: false,
attachments: false,
});
mailListener.start();
mailListener.on('server:connected', function() {});
mailListener.on('mailbox', function(mailbox) {
var totalInboxMessages = (mailbox.messages.total);
console.log('Total number of Inbox emails: ', totalInboxMessages);
});
// mailListener.on('server:disconnected', function() {
// });
// mailListener.stop();
done();
});
};
And my nightwatchjs test script looks like this;
module.exports = {
'getting emails...': function (browser) {
browser.getInboxEmails();
},
'bfs': function (browser) {
browser.url('https://www.motorcyclenews.com/bikes-for-sale/****');
*...assertions, etc*
},
'closing the browser': function (browser) {
browser.browserEnd();
},
};
When I run this nightwatchjs script, the following is outputted;
however, the browser doesn't close.
I kind of expected this, as in my getInboxEmails command script included;
// mailListener.on('server:disconnected', function() {
// });
// mailListener.stop();
However, when I uncommented these two commands, I didn't get the number of emails displayed (but the test/browser correctly 'shut down'.
So the output was now;
So it appears that the number of emails is correctly displayed when the disconnect and stop are omitted, but the test doesn't cease.
And when the disconnect and stop are added, the number of emails is not displayed but the test finishes correctly.
And what I'd like to do is list the number of emails and for the test to end.
Any help would be greatly appreciated.
Yo need to init your mail listener in beforeAll part
and stop that in afterAll
the problem in your example is that your stop is reached in the init part so your mailbox listener will never be raised
mailListener.on('mailbox', function(mailbox) {
var totalInboxMessages = (mailbox.messages.total);
console.log('Total number of Inbox emails: ', totalInboxMessages);
});
Just do some staff as below
let mailListener ;
jest.beforeAll(() => {
mailListener = new MailListener({
username: '##########outlook.com',
password: '#########',
host: 'imap-mail.outlook.com',
port: 993,
tls: true,
connTimeout: 10000,
authTimeout: 5000,
tlsOptions: { rejectUnauthorized: false },
mailbox: 'Inbox',
searchFilter: ['ALL'],
markSeen: true,
fetchUnreadOnStart: false,
attachments: false,
});
mailListener.start();
mailListener.on('server:connected', function() {});
mailListener.on('mailbox', function(mailbox) {
var totalInboxMessages = (mailbox.messages.total);
console.log('Total number of Inbox emails: ', totalInboxMessages);
});
});
and finally in the end part of your tests
jest.afterAll(() => {
mailListener.stop();
});
wish this help
I have a scenario which I need to login to a gmail account and read the content or the message. Then need to get a URL from that message. I can do this using a browser in protractor. But the issue is that gmail account enabled the 2FA. I have achieved this using the core Selenium which has jar files to log in to the gmail account using IMAP protocol.
Can someone please give me a good solution?
You can read emails from Gmail inside protractor tests using mail-listener2 npm package. Check the below example code.
mailListener.ts
const MailListenerClient = require("mail-listener2");
const cheerio = require('cheerio');
const simpleParser = require('mailparser').simpleParser;
export class MailListener {
public mailListener:any;
constructor() {
this.mailListener = new MailListenerClient({
username: "username#gmail.com",
password: "password",
host: "imap.gmail.com",
port: 993,
tls: true,
mailbox: "INBOX",
searchFilter: ["UNSEEN", ["FROM", "fromemail#gmail.com"],["SUBJECT","subject of the email"]],
/*it will search for are "unseen" mail send from "fromemail#gmail.com" with subject "fromemail#gmail.com"*/
connTimeout: 10000,
authTimeout: 5000,
markSeen: true,
mailParserOptions: {streamAttachments: true}, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: {directory: "attachments/"},
debug : console.log
});
}
init() {
this.mailListener.start();
}
close() {
this.mailListener.stop();
}
getLinkFromEmail() {
var self = this;
return new Promise((resolve, reject) => {
self.mailListener.on("mail", function (mail) {
/*simpleParser is used to convert string to HTML format*/
simpleParser(mail.eml).then(function (parsedEmail) {
var html = parsedEmail.html;
/* cheerio is used to write query on parsed HTML content
* refer https://www.npmjs.com/package/cheerio
*/
resolve(cheerio.load(html)("a").attr("href"));
});
});
self.mailListener.on("error", function (err) {
reject(err);
});
});
}
}
test.ts
import {MailListener} from "mailListner";
describe("Read email from gmail using imap", function () {
let mailListener = new MailListener();
beforeAll(function(){
mailListener.init();
});
afterAll(function(){
mailListener.close();
})
it("Test email recieved",function(){
let urlFromEmail = mailListener.getLinkFromEmail();
/*Perform some action on UI that triggers email.(Just for example im doing it)*/
element(by.id("email")).sendKeys("email#gmail.com");
element(by.buttonText("Send Email")).click();
expect(urlFromEmail).toEqual("some link");
})
});
I have written the code in typescript and hope you can rewrite the same in javascript. Let me know if this is clear or do I need to add more details to the code.
One of the best practice is to use Gmail API. Take a look at official documentation
Referencing the information from the question Fetching values from email in protractor test case, I am still unable to reference the emails. In my test case, the 'expect' is not getting executed for some unknown reason to me.
Also if I use the line,
browser.controlFlow().await(getLastEmail()).then(...)
There is a 'browser.controlFlow(...).await is not a function error'
conf.js
var MailListener = require("mail-listener2")
exports.config = {
framework: 'jasmine2',
specs: ['./test.js'],
jasmineNodeOpts: { defaultTimeoutInterval: 360000 },
allScriptsTimeout: 60000,
onPrepare: function () {
var mailListener = new MailListener({
username: "username",
password: "password",
host: "imapPort",
port: 993, // imap port
secure: true,
tls: true,
tlsOptions: { rejectUnauthorized: false },
mailbox: "INBOX", // mailbox to monitor
searchFilter: ["UNSEEN", "FLAGGED"], // the search filter being used after an IDLE notification has been retrieved
markSeen: true, // all fetched email willbe marked as seen and not fetched next time
fetchUnreadOnStart: true, // use it only if you want to get all unread email on lib start. Default is `false`,
mailParserOptions: {streamAttachments: true}, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: { directory: "attachments/" } // specify a download directory for attachments
})
mailListener.start()
mailListener.on("server:connected", function(){
console.log("Mail listener initialized")
})
mailListener.on("error", function(err){
console.log(err)
})
mailListener.on("server:disconnected", function(){
console.log("imapDisconnected")
})
global.mailListener = mailListener
},
onCleanUp: function () {
mailListener.stop()
}
}
The test case:
describe('Email Testing', function () {
it('should login with a registration code sent to an email', function () {
//this line causes a 'browser.controlFlow(...).await is not a function' error
// browser.controlFlow().await(getLastEmail()).then(function (email) {
getLastEmail().then(function (email) {
// The expect does not get executed as it should fail
expect(email.subject).toEqual('My Subject')
})
})
})
function getLastEmail () {
var deferred = protractor.promise.defer()
console.log('Waiting for an email...')
mailListener.on('mail', function (mail) {
console.log('No Console Log Here!')
deferred.fulfill(mail)
})
return deferred.promise
}
I am not certain what I am missing in my test case to be able to read the subject or body of the email?
Ran into the same issue today. Turns out the API for the webdriver and ControlFlow has been updated and await has been changed to wait. Yeah, subtle difference. See the new API reference here: https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/promise_exports_ControlFlow.html
To schedule a task for the wait condition, change your code to look like this:
browser.controlFlow().wait(getLastEmail()).then(...)
you would basically have to wrap that asynchronous code inside of a promise and the pass that promise/function into the flow.execute()
var flow = protractor.promise.controlFlow();
flow.execute( getLastEmail() ).then(function(email){
text = email.text
});
I need to test a protractor test case in which a user signs up, receives an email, goes to the link provided in the email and fills up his/her details in activation signup form.
The problem is how can I get the redeem token from the email. My email has a link to the activation page which has the auth token like following:
http://127.0.0.1:3000/#/signup/redeem/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJlOTRhYzY3MC1kYTNlLTQyYTUtODVkZS02NDU4ZjVmZGMwYjAiLCJzdWIiOiJ0ZXN0QGNvZWYuY28iLCJpYXQiOjE0Mjc0OTM5MDMsImV4cCI6MTQyODA5ODcwM30.
But how do I fetch that token so that I can build the url or how can I click that button in my email so that I can complete the flow ? I am using mailcatcher to simulate email.
This is something I've solved recently. Hope the solution would also apply for your use-case.
Prerequisites:
mail-listener2 package
understanding of the concept of promises
Step by step instructions:
Install mail-listener2:
npm install mail-listener2 --save-dev
In your protractor config initialize Mail Listener and make it available globally:
onPrepare: function () {
var MailListener = require("mail-listener2");
// here goes your email connection configuration
var mailListener = new MailListener({
username: "imap-username",
password: "imap-password",
host: "imap-host",
port: 993, // imap port
tls: true,
tlsOptions: { rejectUnauthorized: false },
mailbox: "INBOX", // mailbox to monitor
searchFilter: ["UNSEEN", "FLAGGED"], // the search filter being used after an IDLE notification has been retrieved
markSeen: true, // all fetched email willbe marked as seen and not fetched next time
fetchUnreadOnStart: true, // use it only if you want to get all unread email on lib start. Default is `false`,
mailParserOptions: {streamAttachments: true}, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: { directory: "attachments/" } // specify a download directory for attachments
});
mailListener.start();
mailListener.on("server:connected", function(){
console.log("Mail listener initialized");
});
global.mailListener = mailListener;
}),
onCleanUp: function () {
mailListener.stop();
},
Create a helper getLastEmail() function which would wait for an email to be retrieved:
function getLastEmail() {
var deferred = protractor.promise.defer();
console.log("Waiting for an email...");
mailListener.on("mail", function(mail){
deferred.fulfill(mail);
});
return deferred.promise;
};
Example test case:
describe("Sample test case", function () {
beforeEach(function () {
browser.get("/#login");
browser.waitForAngular();
});
it("should login with a registration code sent to an email", function () {
element(by.id("username")).sendKeys("MyUserName");
element(by.id("password")).sendKeys("MyPassword");
element(by.id("loginButton")).click();
browser.controlFlow().await(getLastEmail()).then(function (email) {
expect(email.subject).toEqual("New Registration Code");
expect(email.headers.to).toEqual("myemail#email.com");
// extract registration code from the email message
var pattern = /Registration code is: (\w+)/g;
var regCode = pattern.exec(email.text)[1];
console.log(regCode);
});
});
});
The solution I implemented was using mailcatcher API, if you scroll down a bit you'll find the following about the API:
A fairly RESTful URL schema means you can download a list of messages
in JSON from /messages, each message's metadata with
/messages/:id.json, and then the pertinent parts with
/messages/:id.html and /messages/:id.plain for the default HTML and
plain text version, /messages/:id/:cid for individual attachments by
CID, or the whole message with /messages/:id.source.
So we first fetched the whole json response, parse it and fetch the latest email id:
// Returns the last email id
function(emails, user) {
var email, recipient;
for(var i = emails.length - 1; i >= 0; i--) {
email = emails[i];
for(var j = 0; j < email.recipients.length ; j++) {
recipient = email.recipients[j];
if(recipient == "<"+user+">") {
return email.id;
}
}
}
};
using that email id we can get the body of the email by hitting /messages/:id.plain(of course there are more variants like fetching the email source code or email rendered html, we only needed the message) then we can just parse the body to fetch what we want, following is the code:
browser.driver.get(mailcatcherUrl+"/messages");
browser.driver.findElement(by.tagName('body')).getText().then(function(response) {
var emails, lastEmailId, partialTokens ;
emails = JSON.parse(response);
lastEmailId = getLastEmailId(emails, user);
browser.driver.get(mailcatcherUrl+"/messages/"+lastEmailId+".plain");
browser.driver.findElement(by.tagName('body')).getText().then(function(lastEmail) {
// use latestEmail to get what you want.
});
});
And Cheers!
I had to do the same thing but the mail testing server we were using did not have imap support.
So in case anyone runs into the same issue, I achieved a similar solution as alecxe using mailpop3 npm library.
The thing with the pop3 client, however, was that it doesn't act as a listener so we had to define a helper function that would connect, login and fetch the latest email when we needed to test the latest email.
Something like this:
function getLastEmail() {
var deferred = protractor.promise.defer();
var POP3Client = require("mailpop3");
var client = new POP3Client(port, host, {
tlserrs: false,
enabletls: true,
debug: false
});
client.on("connect", function() {
console.log("CONNECT success");
client.login(username, password);
});
client.on("login", function(status, rawdata) {
if (status) {
console.log("LOGIN/PASS success");
client.retr(1);
} else {
console.log("LOGIN/PASS failed");
client.quit();
}
});
client.on("retr", function(status, msgnumber, data, rawdata) {
if (status === true) {
console.log("RETR success for msgnumber " + msgnumber);
deferred.fulfill(data);
} else {
console.log("RETR failed for msgnumber " + msgnumber);
}
client.quit();
});
return deferred.promise;
}
I have a web ftp portal that was created a few years ago by a developer that is no longer around. The code for the website is written in Node.js. Inside of app.js is the following code:
var validUsers = [{
name:'x',
user:'907c78ef73998eafc2680e5fdd4798a8eef0881a',
pass:'95489cf3039eb2f5938e3daa954d04276bbf90e7',
dir:''
},{
name:'y',
user:'b26e5ebda152e81099ec78be2f9c191ee25e1cd6',
pass:'e3725873ae302e3f12eb97b02feb7457de9706c2',
dir:'y'
},{
name:'y2',
user:'3182b54d9f4d08641b5a9a0fb33f74df5d76b222',
pass:'916b2e1941c9e23610f8bd3462cdb19f55b5c631',
dir:'y2'
},{
name:'y3',
user:'38aa53de31c04bcfae9163cc23b7963ed9cf90f7',
pass:'7a98cf84c2c61a30f6c4e3984c0cad2eb29f5d6f',
dir:'y3'
},{
name:'y4',
user:'51e822c50cc62cdbdb850a439ea75b6d45ac487b',
pass:'da6a77293ddcdc7047dd461a94c88c8377753265',
dir:'y4'
},{
name:'y5',
user:'14ad0aca26e00f615990946181ee3405c6ede0f1',
pass:'4eb4e0e1ea0f04422b5bc6031ee37c8dc971236d',
dir:'y5'
},{
name:'y6',
user:'4ec9bdb28c5da0f9813e9eed55a0f1dc6217a305',
pass:'e72bd0bbd37423bb0c9b9edfb9ce94446161c511',
dir:'y6'
},{
name:'y7',
user:'f4603bd4ae9e4aa2a11d903d0b178b37a57b1bac',
pass:'8a6a67f235738c4b2e4f88d4608bdcf0bbc49f51',
dir:'y7'
},{
name:'Guest',
user:'35675e68f4b5af7b995d9205ad0fc43842f16450',
pass:'370bb444ef91a3999b1c36af97e166f18848e7b7',
dir:'Guest'
},{
name:'y8',
user:'d8f51fbf5e13e9f2637a8d5c4bd1ab251bd61c30',
pass:'1a047e6dd554ffdd67524916820a8fa23acd2c6e',
dir:'y8'
}];
The x and y1-8 are substitutions for the actual client names and corresponding directories. Example being the 'Guest' name and directory. My question is, the user and pass are hash values from crypto. Yet they result in specific usernames and passwords. If I wanted to reset a username or password, or add another. How would I figure out the corresponding hash value to add to the code based on the username/password strings I want to add.
Any input would be very helpful.
EDIT:
The rest of the FTP code:
app.get('/ftp/', function(req, res){
var pageName = 'File Transfer Portal';
var rNav = '',
sNav = '',
cNav = '',
imNav = '',
title = 'companyNameOmitted: '+pageName,
bodyClass = 'top ftp',
keywords = 'keywordsOmitted',
description = 'descriptionOmiited',
url = '/ftp/';
res.render('ftp', {
title: title,
bodyClass: bodyClass,
keywords: keywords,
description: description,
url: siteRoot+url,
pageEmail: 'mailto:?subject='+escape(title)+'&body='+escape(description)+'%0A'+siteRoot+url,
eUrl:escape(siteRoot+url),
eTitle:escape(title),
eDescription:escape(description),
rNav:rNav,
sNav:sNav,
cNav:cNav,
imNav:imNav});
//console.log(uniqId()+':'+pageName);
});
app.post('/ftp/upload', function(req, res){
//console.log(req.files);
var SID = req.cookies.SID;
var sessionUser = (users[SID]) ? users[SID] : false;
if (!!sessionUser){
_.each(req.files,function (file) {
console.log(new Date(curTime()).toGMTString()+' | Recieved '+file.name+' ('+file.size+' bytes) from '+sessionUser.name);
var newPath = __dirname + '/complete/'+_.where(validUsers,{user:sessionUser.user})[0].dir+'/'+file.name;
fs.rename(file.path,newPath,function(err) {
if (err) throw err;
else {
res.redirect('back');
if (sessionUser.name != 'adminOmitted') {
var htmlString = '<b>'+sessionUser.name+'</b> has uploaded a file <b>'+file.name+'</b>.<br /><br />View it on the File Transfer Portal.';
var transport = nodemailer.createTransport("SMTP",{
host: "hostname.com", // hostname
secureConnection: true, // use SSL
port: 465, // port for secure SMTP
auth: {
user: "user#host.com",
pass: "pass"
}
});
transport.sendMail({
sender:'sender#host.com',
to:'receiver#host.com',
subject:'File Upload: '+sessionUser.name+' uploaded '+file.name,
html: htmlString
},function(err) {
if (err) console.log(err);
else console.log('Notification Sent: S&A File Upload: '+sessionUser.name+' uploaded '+file.name);
});
}
}
});
And the login code...
app.get('/ftp/d/:hash/:filename', function(req, res){
var SID = req.cookies.SID;
var ip = req.ip;
//console.log(ip);
var sessionUser = (users[SID]) ? ((users[SID].md5==req.params.hash)&&(users[SID].ip==ip)) ? users[SID] : false : false;
if (sessionUser) {
var realpath = __dirname +'/complete/'+_.where(validUsers,{user:sessionUser.user})[0].dir+'/'+req.params.filename.replace('>','/');
console.log(new Date(curTime()).toGMTString()+' | Sending '+realpath.substr(realpath.indexOf('complete')+9)+' to '+sessionUser.name);
res.download(realpath,realpath.substr(realpath.lastIndexOf('/')+1),function(err){
if (err) {
res.redirect(302,'/ftp/');
throw (err);
}
});
} else {
console.log(new Date(curTime()).toGMTString()+' | Download request failed authorization for '+req.params.filename);
console.log(new Date(curTime()).toGMTString()+' | Hash: '+req.params.hash);
console.log(new Date(curTime()).toGMTString()+' | SID: '+req.cookies.SID);
res.redirect(302,'/ftp/');
}
});
sio.sockets.on('connection', function (socket) {
var SID = socket.handshake.SID;
if (!users[SID]) register(SID,socket.handshake.address.address);
//console.log(users);
socket.on('login',function(data) {
var thisUser = _.where(validUsers,{user:data.u,pass:data.p})[0];
if (_.isEmpty(thisUser)) {
if (!!users[SID].ip) {
console.log(new Date(curTime()).toGMTString()+' | '+users[SID].ip+' has failed logging in.');
console.log(new Date(curTime()).toGMTString()+' | '+'U:'+data.u);
console.log(new Date(curTime()).toGMTString()+' | '+'P:'+data.p);
}
socket.emit('login',{complete:false,name:false});
} else {
console.log(new Date(curTime()).toGMTString()+' | '+thisUser.name+' has logged in.');
users[SID].name = thisUser.name;
users[SID].user = thisUser.user;
socket.emit('login',{complete:true,name:thisUser.name});
}
});
And the disconnect function, the only code between the login and the disconnect functions are a move file and a delete file function which I doubt are of any use.
//console.log(users);
socket.on('disconnect',function() {
setTimeout(function() {
if (!!users[SID]) {
if (curTime()-users[SID].lastTap>30000)
unregister(SID);
else console.log('Not removing; connection still active. ('+users[SID].name+')');
} else (unregister(SID));
},30000);
});
});
and finally, the crypto functions:
function getMD5(string) {
return crypto.
createHash('md5').
update(string).
digest("hex");
}
function getSHA1(string) {
return crypto.
createHash('sha1').
update(string).
digest("hex");
}
I know the formatting isn't perfect, I've tried to keep it as neat as possible, I think that's all of the relevant functions. I doubt the .jade file for the FTP Portal would be of any use.
You can't.
The usernames and passwords have been put through an asymmetric encryption (ie MD5). This was likely done to protect the user's personal information if the server is hacked.
You're still missing the part of the code that handles the authentication and sets the session cookie.
If you can find the code that handles the auth and you know the username beforehand you could re-hash it to cross-reference the username to the entries list.
Otherwise, your only option is to crack the usernames/passwords which can be difficult/impossible depending on their complexity.
Good luck...