From ee38fbb03bdf425ed055c71c30c3158820400e24 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 6 Jul 2021 12:17:13 -0300 Subject: [PATCH 1/5] WIP --- .../server/functions/notifications/email.js | 2 +- .../server/lib/interceptDirectReplyEmails.js | 154 +++++++++--------- app/lib/server/lib/processDirectEmail.js | 131 --------------- app/lib/server/lib/processDirectEmail.ts | 119 ++++++++++++++ .../startup/settingsOnLoadDirectReply.js | 83 +++------- app/models/server/raw/Users.js | 2 +- definition/IMessage/IMessage.ts | 8 +- definition/IRoom.ts | 2 +- server/email/IMAPInterceptor.ts | 23 ++- .../EmailInbox/EmailInbox_Outgoing.ts | 1 + 10 files changed, 239 insertions(+), 286 deletions(-) delete mode 100644 app/lib/server/lib/processDirectEmail.js create mode 100644 app/lib/server/lib/processDirectEmail.ts diff --git a/app/lib/server/functions/notifications/email.js b/app/lib/server/functions/notifications/email.js index 3583899487ea..5bb1ff66e59e 100644 --- a/app/lib/server/functions/notifications/email.js +++ b/app/lib/server/functions/notifications/email.js @@ -170,7 +170,7 @@ export function getEmailData({ const replyto = settings.get('Direct_Reply_ReplyTo') || settings.get('Direct_Reply_Username'); // Reply-To header with format "username+messageId@domain" - email.headers['Reply-To'] = `${ replyto.split('@')[0].split(settings.get('Direct_Reply_Separator'))[0] }${ settings.get('Direct_Reply_Separator') }${ message._id }@${ replyto.split('@')[1] }`; + email.headers['Reply-To'] = `${ replyto.split('@')[0].split(settings.get('Direct_Reply_Separator'))[0] }${ settings.get('Direct_Reply_Separator') }${ message.tmid || message._id }@${ replyto.split('@')[1] }`; } metrics.notificationsSent.inc({ notification_type: 'email' }); diff --git a/app/lib/server/lib/interceptDirectReplyEmails.js b/app/lib/server/lib/interceptDirectReplyEmails.js index ed7f022b5313..65cdc775aa0b 100644 --- a/app/lib/server/lib/interceptDirectReplyEmails.js +++ b/app/lib/server/lib/interceptDirectReplyEmails.js @@ -1,4 +1,3 @@ -import { Meteor } from 'meteor/meteor'; import POP3Lib from 'poplib'; import { simpleParser } from 'mailparser'; @@ -6,14 +5,14 @@ import { settings } from '../../../settings'; import { IMAPInterceptor } from '../../../../server/email/IMAPInterceptor'; import { processDirectEmail } from '.'; -export class IMAPIntercepter extends IMAPInterceptor { +export class DirectReplyIMAPInterceptor extends IMAPInterceptor { constructor(imapConfig, options = {}) { imapConfig = { user: settings.get('Direct_Reply_Username'), password: settings.get('Direct_Reply_Password'), host: settings.get('Direct_Reply_Host'), port: settings.get('Direct_Reply_Port'), - debug: settings.get('Direct_Reply_Debug') ? console.log : false, + // debug: settings.get('Direct_Reply_Debug') ? console.log : false, tls: !settings.get('Direct_Reply_IgnoreTLS'), ...imapConfig, }; @@ -22,7 +21,7 @@ export class IMAPIntercepter extends IMAPInterceptor { super(imapConfig, options); - this.on('email', Meteor.bindEnvironment((email) => processDirectEmail(email))); + this.on('email', (email) => processDirectEmail(email)); } } @@ -30,125 +29,118 @@ export class POP3Intercepter { constructor() { this.pop3 = new POP3Lib(settings.get('Direct_Reply_Port'), settings.get('Direct_Reply_Host'), { enabletls: !settings.get('Direct_Reply_IgnoreTLS'), - debug: settings.get('Direct_Reply_Debug') ? console.log : false, + // debug: settings.get('Direct_Reply_Debug') ? console.log : false, + debug: console.log, }); this.totalMsgCount = 0; this.currentMsgCount = 0; - this.pop3.on('connect', Meteor.bindEnvironment(() => { + this.pop3.on('connect', () => { + console.log('Pop connect'); this.pop3.login(settings.get('Direct_Reply_Username'), settings.get('Direct_Reply_Password')); - })); - - this.pop3.on('login', Meteor.bindEnvironment((status) => { - if (status) { - // run on start - this.pop3.list(); - } else { - console.log('Unable to Log-in ....'); + }); + + this.pop3.on('login', (status) => { + if (!status) { + return console.log('Unable to Log-in ....'); } - })); + console.log('Pop logged'); + // run on start + this.pop3.list(); + }); // on getting list of all emails - this.pop3.on('list', Meteor.bindEnvironment((status, msgcount) => { - if (status) { - if (msgcount > 0) { - this.totalMsgCount = msgcount; - this.currentMsgCount = 1; - // Retrieve email - this.pop3.retr(this.currentMsgCount); - } else { - this.pop3.quit(); - } - } else { + this.pop3.on('list', (status, msgcount) => { + if (!status) { console.log('Cannot Get Emails ....'); } - })); + if (msgcount === 0) { + return this.pop3.quit(); + } + + this.totalMsgCount = msgcount; + this.currentMsgCount = 1; + // Retrieve email + this.pop3.retr(this.currentMsgCount); + }); // on retrieved email - this.pop3.on('retr', Meteor.bindEnvironment((status, msgnumber, data) => { - if (status) { - // parse raw email data to JSON object - simpleParser(data, Meteor.bindEnvironment((err, mail) => { - this.initialProcess(mail); - })); - - this.currentMsgCount += 1; - - // delete email - this.pop3.dele(msgnumber); - } else { - console.log('Cannot Retrieve Message ....'); + this.pop3.on('retr', (status, msgnumber, data) => { + if (!status) { + return console.log('Cannot Retrieve Message ....'); } - })); + + // parse raw email data to JSON object + simpleParser(data, (err, mail) => { + processDirectEmail(mail); + }); + + this.currentMsgCount += 1; + + // delete email + this.pop3.dele(msgnumber); + }); // on email deleted - this.pop3.on('dele', Meteor.bindEnvironment((status) => { - if (status) { - // get next email - if (this.currentMsgCount <= this.totalMsgCount) { - this.pop3.retr(this.currentMsgCount); - } else { - // parsed all messages.. so quitting - this.pop3.quit(); - } - } else { - console.log('Cannot Delete Message....'); + this.pop3.on('dele', (status) => { + if (!status) { + return console.log('Cannot Delete Message....'); } - })); + + // get next email + if (this.currentMsgCount <= this.totalMsgCount) { + return this.pop3.retr(this.currentMsgCount); + } + + // parsed all messages.. so quitting + this.pop3.quit(); + }); // invalid server state this.pop3.on('invalid-state', function(cmd) { console.log(`Invalid state. You tried calling ${ cmd }`); }); + this.pop3.on('error', function(cmd) { + console.log(`error state. You tried calling ${ cmd }`); + }); + // locked => command already running, not finished yet this.pop3.on('locked', function(cmd) { console.log(`Current command has not finished yet. You tried calling ${ cmd }`); }); } - - initialProcess(mail) { - const email = { - headers: { - from: mail.from.text, - to: mail.to.text, - date: mail.date, - 'message-id': mail.messageId, - }, - body: mail.text, - }; - - processDirectEmail(email); - } } -export let POP3; export class POP3Helper { - constructor() { + constructor(frequency) { + this.frequency = frequency; this.running = false; - } - start() { - // run every x-minutes - if (settings.get('Direct_Reply_Frequency')) { - POP3 = new POP3Intercepter(); - - this.running = Meteor.setInterval(() => { - // get new emails and process - POP3 = new POP3Intercepter(); - }, Math.max(settings.get('Direct_Reply_Frequency') * 60 * 1000, 2 * 60 * 1000)); - } + this.POP3 = new POP3Intercepter(); } isActive() { return this.running; } + start() { + this.log('POP3 started'); + this.running = setInterval(() => { + // get new emails and process + this.POP3 = new POP3Intercepter(); + }, Math.max(this.frequency * 60 * 1000, 2 * 60 * 1000)); + } + + log(...args) { console.log(...args); } + stop(callback = new Function()) { + this.log('POP3 stop called'); if (this.isActive()) { - Meteor.clearInterval(this.running); + clearInterval(this.running); } callback(); + this.log('POP3 stopped'); } } diff --git a/app/lib/server/lib/processDirectEmail.js b/app/lib/server/lib/processDirectEmail.js deleted file mode 100644 index deab36914c60..000000000000 --- a/app/lib/server/lib/processDirectEmail.js +++ /dev/null @@ -1,131 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { EmailReplyParser as reply } from 'emailreplyparser'; -import moment from 'moment'; - -import { settings } from '../../../settings'; -import { Rooms, Messages, Users, Subscriptions } from '../../../models'; -import { metrics } from '../../../metrics'; -import { hasPermission } from '../../../authorization'; -import { sendMessage as _sendMessage } from '../functions'; - -export const processDirectEmail = function(email) { - function sendMessage(email) { - const message = { - ts: new Date(email.headers.date), - msg: email.body, - sentByEmail: true, - groupable: false, - }; - - if (message.ts) { - const tsDiff = Math.abs(moment(message.ts).diff()); - if (tsDiff > 10000) { - message.ts = new Date(); - } - } else { - message.ts = new Date(); - } - - if (message.msg && message.msg.length > settings.get('Message_MaxAllowedSize')) { - return false; - } - - // reduce new lines in multiline message - message.msg = message.msg.split('\n\n').join('\n'); - - const user = Users.findOneByEmailAddress(email.headers.from, { - fields: { - username: 1, - name: 1, - }, - }); - if (!user) { - // user not found - return false; - } - - const prevMessage = Messages.findOneById(email.headers.mid, { - rid: 1, - u: 1, - }); - if (!prevMessage) { - // message doesn't exist anymore - return false; - } - message.rid = prevMessage.rid; - - const room = Meteor.call('canAccessRoom', message.rid, user._id); - if (!room) { - return false; - } - - const roomInfo = Rooms.findOneById(message.rid, { - t: 1, - name: 1, - }); - - // check mention - if (message.msg.indexOf(`@${ prevMessage.u.username }`) === -1 && roomInfo.t !== 'd') { - message.msg = `@${ prevMessage.u.username } ${ message.msg }`; - } - - // reply message link - let prevMessageLink = `[ ](${ Meteor.absoluteUrl().replace(/\/$/, '') }`; - if (roomInfo.t === 'c') { - prevMessageLink += `/channel/${ roomInfo.name }?msg=${ email.headers.mid }) `; - } else if (roomInfo.t === 'd') { - prevMessageLink += `/direct/${ prevMessage.u.username }?msg=${ email.headers.mid }) `; - } else if (roomInfo.t === 'p') { - prevMessageLink += `/group/${ roomInfo.name }?msg=${ email.headers.mid }) `; - } - // add reply message link - message.msg = prevMessageLink + message.msg; - - const subscription = Subscriptions.findOneByRoomIdAndUserId(message.rid, user._id); - if (subscription && (subscription.blocked || subscription.blocker)) { - // room is blocked - return false; - } - - if ((room.muted || []).includes(user.username)) { - // user is muted - return false; - } - - // room is readonly - if (room.ro === true) { - if (!hasPermission(Meteor.userId(), 'post-readonly', room._id)) { - // Check if the user was manually unmuted - if (!(room.unmuted || []).includes(user.username)) { - return false; - } - } - } - - metrics.messagesSent.inc(); // TODO This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736 - - return _sendMessage(user, message, room); - } - - // Extract/parse reply from email body - email.body = reply.parse_reply(email.body); - - // if 'To' email format is "Name " - if (email.headers.to.indexOf('<') >= 0 && email.headers.to.indexOf('>') >= 0) { - email.headers.to = email.headers.to.split('<')[1].split('>')[0]; - } - - // if 'From' email format is "Name " - if (email.headers.from.indexOf('<') >= 0 && email.headers.from.indexOf('>') >= 0) { - email.headers.from = email.headers.from.split('<')[1].split('>')[0]; - } - - // 'To' email format "username+messageId@domain" - if (email.headers.to.indexOf('+') >= 0) { - // Valid 'To' format - email.headers.mid = email.headers.to.split('@')[0].split('+')[1]; - sendMessage(email); - } else { - console.log('Invalid Email....If not. Please report it.'); - } -}; diff --git a/app/lib/server/lib/processDirectEmail.ts b/app/lib/server/lib/processDirectEmail.ts new file mode 100644 index 000000000000..3d44adb8532a --- /dev/null +++ b/app/lib/server/lib/processDirectEmail.ts @@ -0,0 +1,119 @@ +import { Meteor } from 'meteor/meteor'; +import moment from 'moment'; +import { ParsedMail } from 'mailparser'; + +import { IMessage } from '../../../../definition/IMessage'; +import { settings } from '../../../settings/server'; +import { Rooms, Messages, Users, Subscriptions } from '../../../models/server'; +import { metrics } from '../../../metrics'; +import { hasPermission } from '../../../authorization/server'; +import { sendMessage } from '../functions/sendMessage'; + +const isParsedEmail = (email: ParsedMail): email is Required => 'date' in email && 'html' in email; + +export const processDirectEmail = Meteor.bindEnvironment(function(email: ParsedMail): void { + if (!isParsedEmail(email)) { + return console.log('invalid', email); + } + + const to = Array.isArray(email.to) ? email.to.find((email) => email.text) : email.to; + const mid = to?.text.split('@')[0].split('+')[1]; + + if (!mid) { + console.log('mid', email); + return; + } + + const ts = new Date(email.date); + + const tsDiff = Math.abs(moment(ts).diff(new Date())); + + let msg = email.text.split('\n\n').join('\n'); + + if (msg && msg.length > (settings.get('Message_MaxAllowedSize') as number)) { + return; + } + + const user = Users.findOneByEmailAddress(email.from.text, { + fields: { + username: 1, + name: 1, + }, + }); + + if (!user) { + // user not found + return; + } + + const prevMessage = Messages.findOneById(mid, { + rid: 1, + u: 1, + }); + + if (!prevMessage) { + // message doesn't exist anymore + return; + } + + const room = Meteor.call('canAccessRoom', prevMessage.rid, user._id); + if (!room) { + return; + } + + const roomInfo = Rooms.findOneById(prevMessage.rid, { + t: 1, + name: 1, + }); + + // check mention + if (msg.indexOf(`@${ prevMessage.u.username }`) === -1 && roomInfo.t !== 'd') { + msg = `@${ prevMessage.u.username } ${ msg }`; + } + + // reply message link WE HA THREADS KNOW DONT NEED TO QUOTE THE previous message + // let prevMessageLink = `[ ](${ Meteor.absoluteUrl().replace(/\/$/, '') }`; + // if (roomInfo.t === 'c') { + // prevMessageLink += `/channel/${ roomInfo.name }?msg=${ mid }) `; + // } else if (roomInfo.t === 'd') { + // prevMessageLink += `/direct/${ prevMessage.u.username }?msg=${ mid }) `; + // } else if (roomInfo.t === 'p') { + // prevMessageLink += `/group/${ roomInfo.name }?msg=${ mid }) `; + // } + // // add reply message link + // msg = prevMessageLink + msg; + + const subscription = Subscriptions.findOneByRoomIdAndUserId(prevMessage.rid, user._id); + if (subscription && (subscription.blocked || subscription.blocker)) { + // room is blocked + return; + } + + if ((room.muted || []).includes(user.username)) { + // user is muted + return; + } + + // room is readonly + if (room.ro === true) { + if (!hasPermission(Meteor.userId(), 'post-readonly', room._id)) { + // Check if the user was manually unmuted + if (!(room.unmuted || []).includes(user.username)) { + return; + } + } + } + + metrics.messagesSent.inc(); // TODO This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736 + + const message: Pick = { + ts: tsDiff < 10000 ? ts : new Date(), + msg, + sentByEmail: true, + groupable: false, + tmid: mid, + rid: prevMessage.rid, + }; + + return sendMessage(user, message, room); +}); diff --git a/app/lib/server/startup/settingsOnLoadDirectReply.js b/app/lib/server/startup/settingsOnLoadDirectReply.js index 73675065cb5b..d6d77ba1b52e 100644 --- a/app/lib/server/startup/settingsOnLoadDirectReply.js +++ b/app/lib/server/startup/settingsOnLoadDirectReply.js @@ -1,73 +1,32 @@ -import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import { settings } from '../../../settings'; -import { IMAPIntercepter, POP3Helper, POP3 } from '../lib/interceptDirectReplyEmails.js'; +import { DirectReplyIMAPInterceptor, POP3Helper } from '../lib/interceptDirectReplyEmails.js'; +let client; -let IMAP; -let _POP3Helper; +const startEmailIntercepter = _.debounce(async function() { + console.log('Email Intercepter...'); + const protocol = settings.get('Direct_Reply_Protocol'); -const startEmailIntercepter = _.debounce(Meteor.bindEnvironment(function() { + const isEnabled = settings.get('Direct_Reply_Enable') && protocol && settings.get('Direct_Reply_Host') && settings.get('Direct_Reply_Port') && settings.get('Direct_Reply_Username') && settings.get('Direct_Reply_Password'); + await new Promise((resolve) => (client ? client.stop(resolve) : resolve())); + + if (!isEnabled) { + console.log('Email Intercepter Stopped...'); + return; + } console.log('Starting Email Intercepter...'); - if (settings.get('Direct_Reply_Enable') && settings.get('Direct_Reply_Protocol') && settings.get('Direct_Reply_Host') && settings.get('Direct_Reply_Port') && settings.get('Direct_Reply_Username') && settings.get('Direct_Reply_Password')) { - if (settings.get('Direct_Reply_Protocol') === 'IMAP') { - // stop already running IMAP instance - if (IMAP && IMAP.isActive()) { - console.log('Disconnecting already running IMAP instance...'); - IMAP.stop(Meteor.bindEnvironment(function() { - console.log('Starting new IMAP instance......'); - IMAP = new IMAPIntercepter(); - IMAP.start(); - return true; - })); - } else if (POP3 && _POP3Helper && _POP3Helper.isActive()) { - console.log('Disconnecting already running POP instance...'); - _POP3Helper.stop(Meteor.bindEnvironment(function() { - console.log('Starting new IMAP instance......'); - IMAP = new IMAPIntercepter(); - IMAP.start(); - return true; - })); - } else { - console.log('Starting new IMAP instance......'); - IMAP = new IMAPIntercepter(); - IMAP.start(); - return true; - } - } else if (settings.get('Direct_Reply_Protocol') === 'POP') { - // stop already running POP instance - if (POP3 && _POP3Helper && _POP3Helper.isActive()) { - console.log('Disconnecting already running POP instance...'); - _POP3Helper.stop(Meteor.bindEnvironment(function() { - console.log('Starting new POP instance......'); - _POP3Helper = new POP3Helper(); - _POP3Helper.start(); - return true; - })); - } else if (IMAP && IMAP.isActive()) { - console.log('Disconnecting already running IMAP instance...'); - IMAP.stop(Meteor.bindEnvironment(function() { - console.log('Starting new POP instance......'); - _POP3Helper = new POP3Helper(); - _POP3Helper.start(); - return true; - })); - } else { - console.log('Starting new POP instance......'); - _POP3Helper = new POP3Helper(); - _POP3Helper.start(); - return true; - } - } - } else if (IMAP && IMAP.isActive()) { - // stop IMAP instance - IMAP.stop(); - } else if (POP3 && _POP3Helper.isActive()) { - // stop POP3 instance - _POP3Helper.stop(); + if (protocol === 'IMAP') { + client = new DirectReplyIMAPInterceptor(); } -}), 1000); + if (protocol === 'POP') { + client = new POP3Helper(settings.get('Direct_Reply_Frequency')); + } + client.start(); +}, 1000); settings.onload(/^Direct_Reply_.+/, startEmailIntercepter); + +startEmailIntercepter(); diff --git a/app/models/server/raw/Users.js b/app/models/server/raw/Users.js index 22eacaf861bb..89b491ab2f9e 100644 --- a/app/models/server/raw/Users.js +++ b/app/models/server/raw/Users.js @@ -144,7 +144,7 @@ export class UsersRaw extends BaseRaw { roles: roleName, }; - return this.findOne(query, { fields: { roles: 1 } }); + return this.findOne(query, { projection: { roles: 1 } }); } getDistinctFederationDomains() { diff --git a/definition/IMessage/IMessage.ts b/definition/IMessage/IMessage.ts index 5cddc0248020..a8a74f652185 100644 --- a/definition/IMessage/IMessage.ts +++ b/definition/IMessage/IMessage.ts @@ -66,7 +66,9 @@ export interface IMessage extends IRocketChatRecord { t?: MessageTypesValues; e2e?: 'pending'; - urls: any; - file: any; - attachments: MessageAttachment[]; + urls?: any; + file?: any; + attachments?: MessageAttachment[]; + + sentByEmail?: true; } diff --git a/definition/IRoom.ts b/definition/IRoom.ts index eae89900bb4c..da6477244302 100644 --- a/definition/IRoom.ts +++ b/definition/IRoom.ts @@ -72,7 +72,7 @@ export interface IDirectMessageRoom extends Omit { t: 'l'; v: { - _id?: string; + _id: string; token?: string; status: 'online' | 'busy' | 'away' | 'offline'; }; diff --git a/server/email/IMAPInterceptor.ts b/server/email/IMAPInterceptor.ts index a8d631ae9160..1713a0a56798 100644 --- a/server/email/IMAPInterceptor.ts +++ b/server/email/IMAPInterceptor.ts @@ -19,22 +19,28 @@ export declare interface IMAPInterceptor { export class IMAPInterceptor extends EventEmitter { private imap: IMAP; + private options: IMAPOptions; + constructor( imapConfig: IMAP.Config, - private options: IMAPOptions = { - deleteAfterRead: false, - filter: ['UNSEEN'], - markSeen: true, - }, + options?: Partial, ) { super(); this.imap = new IMAP({ connTimeout: 30000, keepalive: true, + ...imapConfig.tls && { tlsOptions: { servername: imapConfig.host } }, ...imapConfig, }); + this.options = { + deleteAfterRead: false, + filter: ['UNSEEN'], + markSeen: true, + ...options, + }; + // On successfully connected. this.imap.on('ready', () => { if (this.imap.state !== 'disconnected') { @@ -83,8 +89,13 @@ export class IMAPInterceptor extends EventEmitter { } stop(callback = new Function()): void { + this.log('IMAP stop called'); this.imap.end(); - this.imap.once('end', callback); + this.imap.once('end', () => { + this.log('IMAP stopped'); + callback && callback(); + }); + callback && callback(); } restart(): void { diff --git a/server/features/EmailInbox/EmailInbox_Outgoing.ts b/server/features/EmailInbox/EmailInbox_Outgoing.ts index d0aca00d5880..a9f913a2f3f9 100644 --- a/server/features/EmailInbox/EmailInbox_Outgoing.ts +++ b/server/features/EmailInbox/EmailInbox_Outgoing.ts @@ -130,6 +130,7 @@ callbacks.add('beforeSaveMessage', function(message: IMessage, room: any) { } if (message.file) { + message.attachments = message.attachments || []; message.attachments.push({ actions: [{ type: 'button', From 32ed1e0cb7ec358c15cbdeca3d9aff395a688218 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 3 Sep 2021 15:26:22 -0300 Subject: [PATCH 2/5] Fix somehow --- .../server/lib/interceptDirectReplyEmails.js | 2 +- app/lib/server/lib/processDirectEmail.ts | 23 ++++++++----------- definition/IRoom.ts | 2 ++ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/lib/server/lib/interceptDirectReplyEmails.js b/app/lib/server/lib/interceptDirectReplyEmails.js index 65cdc775aa0b..6a6a7562db31 100644 --- a/app/lib/server/lib/interceptDirectReplyEmails.js +++ b/app/lib/server/lib/interceptDirectReplyEmails.js @@ -12,7 +12,7 @@ export class DirectReplyIMAPInterceptor extends IMAPInterceptor { password: settings.get('Direct_Reply_Password'), host: settings.get('Direct_Reply_Host'), port: settings.get('Direct_Reply_Port'), - // debug: settings.get('Direct_Reply_Debug') ? console.log : false, + debug: settings.get('Direct_Reply_Debug') ? console.log : false, tls: !settings.get('Direct_Reply_IgnoreTLS'), ...imapConfig, }; diff --git a/app/lib/server/lib/processDirectEmail.ts b/app/lib/server/lib/processDirectEmail.ts index 3d44adb8532a..8a213f1e37ab 100644 --- a/app/lib/server/lib/processDirectEmail.ts +++ b/app/lib/server/lib/processDirectEmail.ts @@ -3,24 +3,24 @@ import moment from 'moment'; import { ParsedMail } from 'mailparser'; import { IMessage } from '../../../../definition/IMessage'; +import { IRoom } from '../../../../definition/IRoom'; import { settings } from '../../../settings/server'; import { Rooms, Messages, Users, Subscriptions } from '../../../models/server'; import { metrics } from '../../../metrics'; -import { hasPermission } from '../../../authorization/server'; +import { canAccessRoom, hasPermission } from '../../../authorization/server'; import { sendMessage } from '../functions/sendMessage'; const isParsedEmail = (email: ParsedMail): email is Required => 'date' in email && 'html' in email; export const processDirectEmail = Meteor.bindEnvironment(function(email: ParsedMail): void { if (!isParsedEmail(email)) { - return console.log('invalid', email); + return; } const to = Array.isArray(email.to) ? email.to.find((email) => email.text) : email.to; const mid = to?.text.split('@')[0].split('+')[1]; if (!mid) { - console.log('mid', email); return; } @@ -56,15 +56,12 @@ export const processDirectEmail = Meteor.bindEnvironment(function(email: ParsedM return; } - const room = Meteor.call('canAccessRoom', prevMessage.rid, user._id); + const room = canAccessRoom({ _id: prevMessage.rid }, user); if (!room) { return; } - const roomInfo = Rooms.findOneById(prevMessage.rid, { - t: 1, - name: 1, - }); + const roomInfo: IRoom = Rooms.findOneById(prevMessage.rid); // check mention if (msg.indexOf(`@${ prevMessage.u.username }`) === -1 && roomInfo.t !== 'd') { @@ -89,16 +86,16 @@ export const processDirectEmail = Meteor.bindEnvironment(function(email: ParsedM return; } - if ((room.muted || []).includes(user.username)) { + if ((roomInfo.muted || []).includes(user.username)) { // user is muted return; } // room is readonly - if (room.ro === true) { - if (!hasPermission(Meteor.userId(), 'post-readonly', room._id)) { + if (roomInfo.ro === true) { + if (!hasPermission(Meteor.userId(), 'post-readonly', roomInfo._id)) { // Check if the user was manually unmuted - if (!(room.unmuted || []).includes(user.username)) { + if (!(roomInfo.unmuted || []).includes(user.username)) { return; } } @@ -115,5 +112,5 @@ export const processDirectEmail = Meteor.bindEnvironment(function(email: ParsedM rid: prevMessage.rid, }; - return sendMessage(user, message, room); + return sendMessage(user, message, roomInfo); }); diff --git a/definition/IRoom.ts b/definition/IRoom.ts index 33b9dafc8659..45d931ecddd1 100644 --- a/definition/IRoom.ts +++ b/definition/IRoom.ts @@ -61,6 +61,8 @@ export interface IRoom extends IRocketChatRecord { sysMes?: string[]; muted?: string[]; + ro?: true; + unmuted?: string[]; } export interface IDirectMessageRoom extends Omit { From a5ae90951c6b6151f051233bd525f6f25b413f05 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 3 Sep 2021 16:02:29 -0300 Subject: [PATCH 3/5] Fix review --- app/lib/server/lib/processDirectEmail.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/server/lib/processDirectEmail.ts b/app/lib/server/lib/processDirectEmail.ts index 8a213f1e37ab..8e178e082d0b 100644 --- a/app/lib/server/lib/processDirectEmail.ts +++ b/app/lib/server/lib/processDirectEmail.ts @@ -34,7 +34,7 @@ export const processDirectEmail = Meteor.bindEnvironment(function(email: ParsedM return; } - const user = Users.findOneByEmailAddress(email.from.text, { + const user = Users.findOneByEmailAddress(email.from.value[0].address, { fields: { username: 1, name: 1, From 567112ea9c829c8d3b68872f33abd49d36561eb0 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 27 Jun 2022 22:50:05 -0300 Subject: [PATCH 4/5] fix CI --- apps/meteor/app/lib/server/lib/processDirectEmail.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/meteor/app/lib/server/lib/processDirectEmail.ts b/apps/meteor/app/lib/server/lib/processDirectEmail.ts index 01eb2af524a4..0819f4475626 100644 --- a/apps/meteor/app/lib/server/lib/processDirectEmail.ts +++ b/apps/meteor/app/lib/server/lib/processDirectEmail.ts @@ -55,13 +55,13 @@ export const processDirectEmail = Meteor.bindEnvironment(function (email: Parsed return; } - const room = canAccessRoom({ _id: prevMessage.rid }, user); + const roomInfo: IRoom = Rooms.findOneById(prevMessage.rid); + + const room = canAccessRoom(roomInfo, user); if (!room) { return; } - const roomInfo: IRoom = Rooms.findOneById(prevMessage.rid); - // check mention if (msg.indexOf(`@${prevMessage.u.username}`) === -1 && roomInfo.t !== 'd') { msg = `@${prevMessage.u.username} ${msg}`; @@ -92,7 +92,7 @@ export const processDirectEmail = Meteor.bindEnvironment(function (email: Parsed // room is readonly if (roomInfo.ro === true) { - if (!hasPermission(Meteor.userId(), 'post-readonly', roomInfo._id)) { + if (!hasPermission(user._id, 'post-readonly', roomInfo._id)) { // Check if the user was manually unmuted if (!(roomInfo.unmuted || []).includes(user.username)) { return; From f8e72311e41ad854f92ff8d9b497ea4c8a26d0e8 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 28 Jun 2022 00:56:14 -0300 Subject: [PATCH 5/5] ts fix --- ...rectReply.js => settingsOnLoadDirectReply.ts} | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) rename apps/meteor/app/lib/server/startup/{settingsOnLoadDirectReply.js => settingsOnLoadDirectReply.ts} (76%) diff --git a/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.js b/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts similarity index 76% rename from apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.js rename to apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts index 5e9ef35fa9e8..d970f54bdc6a 100644 --- a/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.js +++ b/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts @@ -1,10 +1,9 @@ import _ from 'underscore'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { DirectReplyIMAPInterceptor, POP3Helper } from '../lib/interceptDirectReplyEmails.js'; -let client; - +let client: DirectReplyIMAPInterceptor | POP3Helper | undefined; const startEmailIntercepter = _.debounce(async function () { console.log('Email Intercepter...'); const protocol = settings.get('Direct_Reply_Protocol'); @@ -16,7 +15,10 @@ const startEmailIntercepter = _.debounce(async function () { settings.get('Direct_Reply_Port') && settings.get('Direct_Reply_Username') && settings.get('Direct_Reply_Password'); - await new Promise((resolve) => (client ? client.stop(resolve) : resolve())); + + if (client) { + await client.stop(); + } if (!isEnabled) { console.log('Email Intercepter Stopped...'); @@ -26,13 +28,15 @@ const startEmailIntercepter = _.debounce(async function () { if (protocol === 'IMAP') { client = new DirectReplyIMAPInterceptor(); + client.start(); } + if (protocol === 'POP') { client = new POP3Helper(settings.get('Direct_Reply_Frequency')); + client.start(); } - client.start(); }, 1000); -settings.onload(/^Direct_Reply_.+/, startEmailIntercepter); +settings.watchByRegex(/^Direct_Reply_.+/, startEmailIntercepter); startEmailIntercepter();