Esempio n. 1
0
    def connect(self):
        if not self._smtp_connection:
            log = 'Connecting from SMTP server {}'
            logger.info(self, log.format(self._smtp_config.server))
            self._smtp_connection = smtplib.SMTP(self._smtp_config.server,
                                                 self._smtp_config.port)
            self._smtp_connection.ehlo()

            if self._smtp_config.login:
                try:
                    starttls_result = self._smtp_connection.starttls()
                    log = 'SMTP start TLS result: {}'
                    logger.debug(self, log.format(starttls_result))
                except Exception as e:
                    log = 'SMTP start TLS error: {}'
                    logger.debug(self, log.format(e.__str__()))

            if self._smtp_config.login:
                try:
                    login_res = self._smtp_connection.login(
                        self._smtp_config.login, self._smtp_config.password)
                    log = 'SMTP login result: {}'
                    logger.debug(self, log.format(login_res))
                except Exception as e:
                    log = 'SMTP login error: {}'
                    logger.debug(self, log.format(e.__str__()))
            logger.info(self, 'Connection OK')
Esempio n. 2
0
 def notify_content_update(self, content: Content):
     type(self).send_count += 1
     logger.info(
         self,
         'Fake notifier, do not send notification for update of content {}'.
         format(content.content_id)  # nopep8
     )
Esempio n. 3
0
 def __init__(self,
              config: CFG,
              session: Session,
              current_user: User = None) -> None:
     INotifier.__init__(
         self,
         config,
         session,
         current_user,
     )
     logger.info(self, 'Instantiating Dummy Notifier')
Esempio n. 4
0
 def send_mail(self, message: MIMEMultipart):
     if not self._is_active:
         log = 'Not sending email to {} (service disabled)'
         logger.info(self, log.format(message['To']))
     else:
         self.connect()  # Actually, this connects to SMTP only if required
         logger.info(self, 'Sending email to {}'.format(message['To']))
         self._smtp_connection.send_message(message)
         from tracim.lib.mail_notifier.notifier import EmailManager
         EmailManager.log_notification(
             action='   SENT',
             recipient=message['To'],
             subject=message['Subject'],
             config=self.config,
         )
Esempio n. 5
0
    def __setattr__(self, key, value):
        """
        Log-ready setter.

        Logs all configuration parameters except password.
        :param key:
        :param value:
        :return:
        """
        if 'PASSWORD' not in key and \
                ('URL' not in key or type(value) == str) and \
                'CONTENT' not in key:
            # We do not show PASSWORD for security reason
            # we do not show URL because At the time of configuration setup,
            # it can't be evaluated
            # We do not show CONTENT in order not to pollute log files
            logger.info(self, 'CONFIG: [ {} | {} ]'.format(key, value))
        else:
            logger.info(self, 'CONFIG: [ {} | <value not shown> ]'.format(key))

        self.__dict__[key] = value
Esempio n. 6
0
    def __init__(
            self,
            config: CFG,
            session: Session,
            current_user: User=None
    ):
        """
        :param current_user: the user that has triggered the notification
        :return:
        """
        INotifier.__init__(self, config, session, current_user)
        logger.info(self, 'Instantiating Email Notifier')

        self._user = current_user
        self.session = session
        self.config = config
        self._smtp_config = SmtpConfiguration(
            self.config.EMAIL_NOTIFICATION_SMTP_SERVER,
            self.config.EMAIL_NOTIFICATION_SMTP_PORT,
            self.config.EMAIL_NOTIFICATION_SMTP_USER,
            self.config.EMAIL_NOTIFICATION_SMTP_PASSWORD
        )
Esempio n. 7
0
 def __init__(self, config, current_user: User = None):
     INotifier.__init__(config, current_user)
     logger.info(self, 'Instantiating Dummy Notifier')
Esempio n. 8
0
    def notify_content_update(self, content: Content):

        if content.get_last_action().id not \
                in self.config.EMAIL_NOTIFICATION_NOTIFIED_EVENTS:
            logger.info(
                self,
                'Skip email notification for update of content {}'
                'by user {} (the action is {})'.format(
                    content.content_id,
                    # below: 0 means "no user"
                    self._user.user_id if self._user else 0,
                    content.get_last_action().id
                )
            )
            return

        logger.info(self,
                    'About to email-notify update'
                    'of content {} by user {}'.format(
                        content.content_id,
                        # Below: 0 means "no user"
                        self._user.user_id if self._user else 0
                    )
        )

        if content.type not \
                in self.config.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS:
            logger.info(
                self,
                'Skip email notification for update of content {}'
                'by user {} (the content type is {})'.format(
                    content.type,
                    # below: 0 means "no user"
                    self._user.user_id if self._user else 0,
                    content.get_last_action().id
                )
            )
            return

        logger.info(self,
                    'About to email-notify update'
                    'of content {} by user {}'.format(
                        content.content_id,
                        # Below: 0 means "no user"
                        self._user.user_id if self._user else 0
                    )
        )

        ####
        #
        # INFO - D.A. - 2014-11-05 - Emails are sent through asynchronous jobs.
        # For that reason, we do not give SQLAlchemy objects but ids only
        # (SQLA objects are related to a given thread/session)
        #
        try:
            if self.config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower() == self.config.CST.ASYNC.lower():
                logger.info(self, 'Sending email in ASYNC mode')
                # TODO - D.A - 2014-11-06
                # This feature must be implemented in order to be able to scale to large communities
                raise NotImplementedError('Sending emails through ASYNC mode is not working yet')
            else:
                logger.info(self, 'Sending email in SYNC mode')
                EmailManager(
                    self._smtp_config,
                    self.config,
                    self.session,
                ).notify_content_update(self._user.user_id, content.content_id)
        except TypeError as e:
            logger.error(self, 'Exception catched during email notification: {}'.format(e.__str__()))
Esempio n. 9
0
    def notify_created_account(
            self,
            user: User,
            password: str,
    ) -> None:
        """
        Send created account email to given user.

        :param password: choosed password
        :param user: user to notify
        """
        # TODO BS 20160712: Cyclic import
        logger.debug(self, 'user: {}'.format(user.user_id))
        logger.info(self, 'Sending asynchronous email to 1 user ({0})'.format(
            user.email,
        ))

        async_email_sender = EmailSender(
            self.config,
            self._smtp_config,
            self.config.EMAIL_NOTIFICATION_ACTIVATED
        )

        subject = \
            self.config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_SUBJECT \
            .replace(
                EST.WEBSITE_TITLE,
                self.config.WEBSITE_TITLE.__str__()
            )
        message = MIMEMultipart('alternative')
        message['Subject'] = subject
        message['From'] = self._get_sender()
        message['To'] = formataddr((user.get_display_name(), user.email))

        text_template_file_path = self.config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_TEXT  # nopep8
        html_template_file_path = self.config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML  # nopep8

        context = {
            'user': user,
            'password': password,
            # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for logo_url  # nopep8
            'logo_url': '',
            # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for login_url  # nopep8
            'login_url': self.config.WEBSITE_BASE_URL,
        }
        body_text = self._render_template(
            mako_template_filepath=text_template_file_path,
            context=context
        )

        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
        )

        part1 = MIMEText(body_text, 'plain', 'utf-8')
        part2 = MIMEText(body_html, 'html', 'utf-8')

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message,
        # in this case the HTML message, is best and preferred.
        message.attach(part1)
        message.attach(part2)

        send_email_through(
            config=self.config,
            sendmail_callable=async_email_sender.send_mail,
            message=message
        )
Esempio n. 10
0
    def notify_content_update(
            self,
            event_actor_id: int,
            event_content_id: int
    ) -> None:
        """
        Look for all users to be notified about the new content and send them an
        individual email
        :param event_actor_id: id of the user that has triggered the event
        :param event_content_id: related content_id
        :return:
        """
        # FIXME - D.A. - 2014-11-05
        # Dirty import. It's here in order to avoid circular import
        from tracim.lib.core.content import ContentApi
        from tracim.lib.core.user import UserApi
        user = UserApi(
            None,
            config=self.config,
            session=self.session,
            ).get_one(event_actor_id)
        logger.debug(self, 'Content: {}'.format(event_content_id))
        content_api = ContentApi(
            current_user=user,
            session=self.session,
            config=self.config,
            )
        content = ContentApi(
            session=self.session,
            current_user=user, # TODO - use a system user instead of the user that has triggered the event
            config=self.config,
            show_archived=True,
            show_deleted=True,
        ).get_one(event_content_id, ContentType.Any)
        main_content = content.parent if content.type == ContentType.Comment else content
        notifiable_roles = WorkspaceApi(
            current_user=user,
            session=self.session,
            config=self.config,
        ).get_notifiable_roles(content.workspace)

        if len(notifiable_roles) <= 0:
            logger.info(self, 'Skipping notification as nobody subscribed to in workspace {}'.format(content.workspace.label))
            return


        logger.info(self, 'Sending asynchronous emails to {} user(s)'.format(len(notifiable_roles)))
        # INFO - D.A. - 2014-11-06
        # The following email sender will send emails in the async task queue
        # This allow to build all mails through current thread but really send them (including SMTP connection)
        # In the other thread.
        #
        # This way, the webserver will return sooner (actually before notification emails are sent
        async_email_sender = EmailSender(
            self.config,
            self._smtp_config,
            self.config.EMAIL_NOTIFICATION_ACTIVATED
        )
        for role in notifiable_roles:
            logger.info(self, 'Sending email to {}'.format(role.user.email))
            to_addr = formataddr((role.user.display_name, role.user.email))
            #
            # INFO - G.M - 2017-11-15 - set content_id in header to permit reply
            # references can have multiple values, but only one in this case.
            replyto_addr = self.config.EMAIL_NOTIFICATION_REPLY_TO_EMAIL.replace( # nopep8
                '{content_id}',str(content.content_id)
            )

            reference_addr = self.config.EMAIL_NOTIFICATION_REFERENCES_EMAIL.replace( #nopep8
                '{content_id}',str(content.content_id)
             )
            #
            #  INFO - D.A. - 2014-11-06
            # We do not use .format() here because the subject defined in the .ini file
            # may not include all required labels. In order to avoid partial format() (which result in an exception)
            # we do use replace and force the use of .__str__() in order to process LazyString objects
            #
            subject = self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT
            subject = subject.replace(EST.WEBSITE_TITLE, self.config.WEBSITE_TITLE.__str__())
            subject = subject.replace(EST.WORKSPACE_LABEL, main_content.workspace.label.__str__())
            subject = subject.replace(EST.CONTENT_LABEL, main_content.label.__str__())
            subject = subject.replace(EST.CONTENT_STATUS_LABEL, main_content.get_status().label.__str__())
            reply_to_label = l_('{username} & all members of {workspace}').format(
                username=user.display_name,
                workspace=main_content.workspace.label)

            message = MIMEMultipart('alternative')
            message['Subject'] = subject
            message['From'] = self._get_sender(user)
            message['To'] = to_addr
            message['Reply-to'] = formataddr((reply_to_label, replyto_addr))
            # INFO - G.M - 2017-11-15
            # References can theorically have label, but in pratice, references
            # contains only message_id from parents post in thread.
            # To link this email to a content we create a virtual parent
            # in reference who contain the content_id.
            message['References'] = formataddr(('',reference_addr))
            body_text = self._build_email_body_for_content(self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_TEXT, role, content, user)
            body_html = self._build_email_body_for_content(self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML, role, content, user)

            part1 = MIMEText(body_text, 'plain', 'utf-8')
            part2 = MIMEText(body_html, 'html', 'utf-8')
            # Attach parts into message container.
            # According to RFC 2046, the last part of a multipart message, in this case
            # the HTML message, is best and preferred.
            message.attach(part1)
            message.attach(part2)

            self.log_notification(
                action='CREATED',
                recipient=message['To'],
                subject=message['Subject'],
                config=self.config,
            )

            send_email_through(
                self.config,
                async_email_sender.send_mail,
                message
            )
Esempio n. 11
0
 def disconnect(self):
     if self._smtp_connection:
         log = 'Disconnecting from SMTP server {}'
         logger.info(self, log.format(self._smtp_config.server))
         self._smtp_connection.quit()
         logger.info(self, 'Connection closed.')