Exemple #1
0
 def doom_request_transaction(self, exc: Exception, *args, **kwargs) -> None:
     try:
         # NOTE 2020-09-09 - S.G.
         # we have to search for the request object in all arguments
         # as we cannot be sure of its place in it.
         # For example if a handler is an object method the first argument
         # will be the controller object, not the request object.
         request = next(arg for arg in args if isinstance(arg, Request))
         transaction_status = request.tm.get().status
         # INFO - 2020-10-01 - G.M : In some specific case: we do arrive in situation
         # where transaction is not active anymore, in such situation, it's not possible
         # to doom the request and we do get " ValueError('non-doomable')" Exception.
         # to handle this case in a more clear way, we do Doom only active Transaction.
         if transaction_status == TransactionStatus.ACTIVE:
             request.tm.doom()
         else:
             # INFO - 2020-10-01 - G.M : debug unattended transaction status
             # for troubleshooting future issues
             logger.debug(
                 self,
                 'Transaction not active, status : "{}", cannot be doomed'.format(
                     transaction_status
                 ),
             )
     except StopIteration:
         logger.error(self, "Cannot find request object in arguments")
     except NoTransaction:
         # INFO - 2020-10-01 - G.M - In some very specific case like PageNotFound case, pyramid
         # doesn't provide a true transaction, so doom it will raise a NoTransaction exception.
         logger.debug(self, "Transaction not initialized, cannot be doomed")
Exemple #2
0
    def execute_updated_user_actions(self, user: User) -> None:
        """
        WARNING! This method will be deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method does post-update user actions
        """

        # TODO - G.M - 04-04-2018 - [auth]
        # Check if this is already needed with
        # new auth system
        user.ensure_auth_token(
            validity_seconds=self._config.USER__AUTH_TOKEN__VALIDITY)

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_updated_user should start hook use by agenda  app code.

        app_lib = ApplicationApi(app_list=app_list)
        if app_lib.exist(AGENDA__APP_SLUG):
            agenda_api = AgendaApi(current_user=self._user,
                                   session=self._session,
                                   config=self._config)
            try:
                agenda_api.ensure_user_agenda_exists(user)
            except AgendaServerConnectionError as exc:
                logger.error(self, "Cannot connect to agenda server")
                logger.exception(self, exc)
            except Exception as exc:
                logger.error(
                    self, "Something goes wrong during agenda create/update")
                logger.exception(self, exc)
Exemple #3
0
    def execute_created_user_actions(self, user: User) -> None:
        """
        WARNING! This method will be deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method do post-create user actions
        """

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_created_user should start hook use by agenda  app code.

        app_lib = ApplicationApi(app_list=app_list)
        if app_lib.exist(AGENDA__APP_SLUG):
            agenda_api = AgendaApi(current_user=self._user,
                                   session=self._session,
                                   config=self._config)
            try:
                agenda_already_exist = agenda_api.ensure_user_agenda_exists(
                    user)
                if agenda_already_exist:
                    logger.warning(
                        self,
                        "user {} has just been created but their own agenda already exists"
                        .format(user.user_id),
                    )
            except AgendaServerConnectionError as exc:
                logger.error(self, "Cannot connect to the agenda server")
                logger.exception(self, exc)
            except Exception as exc:
                logger.error(
                    self, "Something went wrong during agenda create/update")
                logger.exception(self, exc)
Exemple #4
0
    def execute_updated_user_actions(self, user: User) -> None:
        """
        WARNING ! This method Will be Deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method do post-update user actions
        """

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mecanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_updated_user should start hook use by agenda  app code.

        if self._config.CALDAV__ENABLED:
            agenda_api = AgendaApi(current_user=self._user,
                                   session=self._session,
                                   config=self._config)
            try:
                agenda_api.ensure_user_agenda_exists(user)
            except AgendaServerConnectionError as exc:
                logger.error(self, "Cannot connect to agenda server")
                logger.exception(self, exc)
            except Exception as exc:
                logger.error(
                    self, "Something goes wrong during agenda create/update")
                logger.exception(self, exc)
Exemple #5
0
    def execute_update_workspace_actions(self, workspace: Workspace) -> None:
        """
        WARNING ! This method Will be Deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method do post update workspace actions
        """

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_updated_workspace should start hook use by agenda app code.

        app_lib = ApplicationApi(app_list=app_list)
        if app_lib.exist(AGENDA__APP_SLUG):
            # TODO - G.M - 2019-04-11 - Circular Import, will probably be remove
            # with event refactor, see https://github.com/tracim/tracim/issues/1487
            from tracim_backend.applications.agenda.lib import AgendaApi

            if workspace.agenda_enabled:
                agenda_api = AgendaApi(current_user=self._user,
                                       session=self._session,
                                       config=self._config)
                try:
                    agenda_api.ensure_workspace_agenda_exists(workspace)
                except AgendaServerConnectionError as exc:
                    logger.error(self, "Cannot connect to agenda server")
                    logger.exception(self, exc)
                except Exception as exc:
                    logger.error(
                        self,
                        "Something goes wrong during agenda create/update")
                    logger.exception(self, exc)
Exemple #6
0
 def get_content_schema_for_type(cls, content_type: str) -> ContentSchema:
     try:
         return cls.content_schemas[content_type]
     except KeyError:
         logger.error(
             cls,
             ("Cannot dump proper content for content-type '{}' in generated event "
              "as it is unknown, falling back to generic content schema"
              ).format(content_type),
         )
         return ContentSchema()
Exemple #7
0
    def connect(self):
        if not self._smtp_connection:
            log = "Connecting to SMTP server {}"
            logger.info(self, log.format(self._smtp_config.server))
            if self._smtp_config.use_implicit_ssl:
                self._smtp_connection = smtplib.SMTP_SSL(
                    self._smtp_config.server, self._smtp_config.port
                )
            else:
                self._smtp_connection = smtplib.SMTP(
                    self._smtp_config.server, self._smtp_config.port
                )
            self._smtp_connection.ehlo()

            # TODO - G.M - 2020-04-02 - Starttls usage should be explicit in configuration, see
            # https://github.com/tracim/tracim/issues/2815
            if self._smtp_config.login and not self._smtp_config.use_implicit_ssl:
                try:
                    starttls_result = self._smtp_connection.starttls()

                    if starttls_result[0] == 220:
                        logger.info(self, "SMTP Start TLS OK")

                    log = "SMTP Start TLS return code: {} with message: {}"
                    logger.debug(
                        self, log.format(starttls_result[0], starttls_result[1].decode("utf-8"))
                    )
                except smtplib.SMTPResponseException as exc:
                    log = "SMTP start TLS return error code: {} with message: {}"
                    logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8")))
                except Exception:
                    log = "Unexpected exception during SMTP start TLS process"
                    logger.exception(self, log)

            if self._smtp_config.login:
                try:
                    login_res = self._smtp_connection.login(
                        self._smtp_config.login, self._smtp_config.password
                    )

                    if login_res[0] == 235:
                        logger.info(self, "SMTP Authentication Successful")
                    if login_res[0] == 503:
                        logger.info(self, "SMTP Already Authenticated")

                    log = "SMTP login return code: {} with message: {}"
                    logger.debug(self, log.format(login_res[0], login_res[1].decode("utf-8")))
                except smtplib.SMTPAuthenticationError as exc:
                    log = "SMTP auth return error code: {} with message: {}"
                    logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8")))
                    logger.error(
                        self, "check your auth params combinaison " "(login/password) for SMTP"
                    )
                except smtplib.SMTPResponseException as exc:
                    log = "SMTP login return error code: {} with message: {}"
                    logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8")))
                except Exception:
                    log = "Unexpected exception during SMTP login"
                    logger.exception(self, log)
Exemple #8
0
    def _notify_tracim(self, mails: typing.List[DecodedMail],
                       imapc: imapclient.IMAPClient) -> None:
        """
        Send http request to tracim endpoint
        :param mails: list of mails to send
        :return: none
        """
        logger.debug(self,
                     "Notify tracim about {} new responses".format(len(mails)))
        # TODO BS 20171124: Look around mail.get_from_address(), mail.get_key()
        # , mail.get_body() etc ... for raise InvalidEmailError if missing
        #  required informations (actually get_from_address raise IndexError
        #  if no from address for example) and catch it here
        while mails:
            mail = mails.pop()
            try:
                method, endpoint, json_body_dict = self._create_comment_request(
                    mail)
            except NoKeyFound:
                log = "Failed to create comment request due to missing specialkey in mail"
                logger.exception(self, log)
                continue
            except EmptyEmailBody:
                log = "Empty body, skip mail"
                logger.error(self, log)
                continue
            except AutoReplyEmailNotAllowed:
                log = "Autoreply mail, skip mail"
                logger.warning(self, log)
                continue
            except Exception:
                log = "Failed to create comment request in mail fetcher error"
                logger.exception(self, log)
                continue

            try:
                self._send_request(
                    mail=mail,
                    imapc=imapc,
                    method=method,
                    endpoint=endpoint,
                    json_body_dict=json_body_dict,
                )
            except requests.exceptions.Timeout:
                log = "Timeout error to transmit fetched mail to tracim"
                logger.exception(self, log)
            except requests.exceptions.RequestException:
                log = "Fail to transmit fetched mail to tracim"
                logger.exception(self, log)
Exemple #9
0
 def _check_agenda_exist(self, agenda_url) -> bool:
     try:
         response = requests.get(agenda_url)
     except requests.exceptions.ConnectionError as exc:
         logger.error(
             self,
             "Cannot check agenda existence, connection error to radicale server"
         )
         logger.exception(self, exc)
         raise AgendaServerConnectionError() from exc
     if response.status_code < 400:
         return True
     else:
         # TODO - G.M - 2019-03-13 - Better deal with other error code
         return False
Exemple #10
0
    def _notify_tracim(self, mails: typing.List[DecodedMail],
                       imapc: imapclient.IMAPClient) -> None:
        """
        Send http request to tracim endpoint
        :param mails: list of mails to send
        :return: none
        """
        logger.debug(
            self, 'Notify tracim about {} new responses'.format(len(mails), ))
        # TODO BS 20171124: Look around mail.get_from_address(), mail.get_key()
        # , mail.get_body() etc ... for raise InvalidEmailError if missing
        #  required informations (actually get_from_address raise IndexError
        #  if no from address for example) and catch it here
        while mails:
            mail = mails.pop()
            try:
                method, endpoint, json_body_dict = self._create_comment_request(
                    mail)  # nopep8
            except NoSpecialKeyFound as exc:
                log = 'Failed to create comment request due to missing specialkey in mail {}'  # nopep8
                logger.error(self, log.format(exc.__str__()))
                continue
            except EmptyEmailBody as exc:
                log = 'Empty body, skip mail'
                logger.error(self, log)
                continue
            except Exception as exc:
                log = 'Failed to create comment request in mail fetcher error {}'  # nopep8
                logger.error(self, log.format(exc.__str__()))
                continue

            try:
                self._send_request(
                    mail=mail,
                    imapc=imapc,
                    method=method,
                    endpoint=endpoint,
                    json_body_dict=json_body_dict,
                )
            except requests.exceptions.Timeout as e:
                log = 'Timeout error to transmit fetched mail to tracim : {}'
                logger.error(self, log.format(str(e)))
            except requests.exceptions.RequestException as e:
                log = 'Fail to transmit fetched mail to tracim : {}'
                logger.error(self, log.format(str(e)))
Exemple #11
0
 def index_all_content(self) -> IndexedContentsResults:
     """
     Index/update all content in current index of ElasticSearch
     """
     content_api = ContentApi(
         session=self._session,
         config=self._config,
         current_user=self._user,
         show_archived=True,
         show_active=True,
         show_deleted=True,
     )
     contents = content_api.get_all()
     content_ids_to_index = []  # type: typing.List[int]
     errored_indexed_contents_ids = []  # type: typing.List[int]
     for content in contents:
         content_in_context = ContentInContext(
             content, config=self._config, dbsession=self._session
         )
         content_ids_to_index.append(content_in_context.content_id)
         try:
             self.index_content(content_in_context)
         except ConnectionError as exc:
             logger.error(
                 self,
                 "connexion error issue with elasticsearch during indexing of content {}".format(
                     content_in_context.content_id
                 ),
             )
             logger.exception(self, exc)
             errored_indexed_contents_ids.append(content_in_context.content_id)
         except Exception as exc:
             logger.error(
                 self,
                 "something goes wrong during indexing of content {}".format(
                     content_in_context.content_id
                 ),
             )
             logger.exception(self, exc)
             errored_indexed_contents_ids.append(content_in_context.content_id)
     return IndexedContentsResults(content_ids_to_index, errored_indexed_contents_ids)
Exemple #12
0
    def execute_created_workspace_actions(self, workspace: Workspace) -> None:
        """
        WARNING ! This method Will be Deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method do post creation workspace actions
        """

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mecanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_created_workspace should start hook use by agenda app code.

        # TODO - G.M - 2019-04-11 - Circular Import, will probably be remove
        # with event refactor, see https://github.com/tracim/tracim/issues/1487
        from tracim_backend.lib.agenda.agenda import AgendaApi

        if self._config.CALDAV__ENABLED:
            if workspace.agenda_enabled:
                agenda_api = AgendaApi(current_user=self._user,
                                       session=self._session,
                                       config=self._config)
                try:
                    agenda_already_exist = agenda_api.ensure_workspace_agenda_exists(
                        workspace)
                    if agenda_already_exist:
                        logger.warning(
                            self,
                            "workspace {} is just created but it own agenda already exist !!"
                            .format(workspace.workspace_id),
                        )
                except AgendaServerConnectionError as exc:
                    logger.error(self, "Cannot connect to agenda server")
                    logger.exception(self, exc)
                except Exception as exc:
                    logger.error(
                        self,
                        "Something goes wrong during agenda create/update")
                    logger.exception(self, exc)
Exemple #13
0
    def execute_created_user_actions(self, user: User) -> None:
        """
        WARNING ! This method Will be Deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method do post-create user actions
        """

        # TODO - G.M - 04-04-2018 - [auth]
        # Check if this is already needed with
        # new auth system
        user.ensure_auth_token(
            validity_seconds=self._config.USER__AUTH_TOKEN__VALIDITY)

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mecanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_created_user should start hook use by agenda  app code.

        if self._config.CALDAV__ENABLED:
            agenda_api = AgendaApi(current_user=self._user,
                                   session=self._session,
                                   config=self._config)
            try:
                agenda_already_exist = agenda_api.ensure_user_agenda_exists(
                    user)
                if agenda_already_exist:
                    logger.warning(
                        self,
                        "user {} is just created but his own agenda already exist !!"
                        .format(user.user_id),
                    )
            except AgendaServerConnectionError as exc:
                logger.error(self, "Cannot connect to agenda server")
                logger.exception(self, exc)
            except Exception as exc:
                logger.error(
                    self, "Something goes wrong during agenda create/update")
                logger.exception(self, exc)
Exemple #14
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"]))
            # TODO - G.M - 2019-01-29 - optimisize this code, we should not send
            # email if connection has failed.
            send_action = "{:8s}".format("SENT")
            failed_action = "{:8s}".format("SENDFAIL")
            action = send_action
            try:
                send_message_result = self._smtp_connection.send_message(
                    message)
                # INFO - G.M - 2019-01-29 - send_message return if not failed,
                # dict of refused recipients.

                if send_message_result == {}:
                    logger.debug(self, "One mail correctly sent using SMTP.")
                else:
                    # INFO - G.M - 2019-01-29 - send_message_result != {}
                    # case should not happened
                    # as we send not mail with multiple recipient at the same
                    # time. send_message will not raise exception
                    # just if some recipient work and some other failed.
                    # TODO - G.M - 2019-01-29 - better support for multirecipient email
                    log = "Mail could not be send to some recipient: {}"
                    logger.debug(self, log.format(send_message_result))
                    action = failed_action

            except smtplib.SMTPException as exc:
                log = "SMTP sending message return error: {}"
                logger.error(self, log.format(str(exc)))
                action = failed_action
            except Exception as exc:
                log = "Unexpected exception during sending email message using SMTP: {}"
                logger.error(self, log.format(exc.__str__()))
                logger.error(self, traceback.format_exc())
                action = failed_action

            from tracim_backend.lib.mail_notifier.notifier import EmailManager

            if action == send_action:
                msg = "an email was sended to {}".format(message["To"])
            else:
                msg = "fail to send email to {}".format(message["To"])

            EmailManager.log_email_notification(
                msg=msg,
                action=action,
                email_recipient=message["To"],
                email_subject=message["Subject"],
                config=self.config,
            )
Exemple #15
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']))
            # TODO - G.M - 2019-01-29 - optimisize this code, we should not send
            # email if connection has failed.
            send_action = '{:8s}'.format('SENT')
            failed_action = '{:8s}'.format('SENDFAIL')
            action = send_action
            try:
                send_message_result = self._smtp_connection.send_message(message)
                # INFO - G.M - 2019-01-29 - send_message return if not failed,
                # dict of refused recipients.

                if send_message_result == {}:
                    logger.debug(self, 'One mail correctly sent using SMTP.')
                else:
                    # INFO - G.M - 2019-01-29 - send_message_result != {}
                    # case should not happened
                    # as we send not mail with multiple recipient at the same
                    # time. send_message will not raise exception
                    # just if some recipient work and some other failed.
                    # TODO - G.M - 2019-01-29 - better support for multirecipient email
                    log = 'Mail could not be send to some recipient: {}'
                    logger.debug(self, log.format(send_message_result))
                    action = failed_action

            except smtplib.SMTPException as exc:
                log = 'SMTP sending message return error: {}'
                logger.error(self, log.format(str(exc)))
                action = failed_action
            except Exception as exc:
                log = 'Unexpected exception during sending email message using SMTP: {}'
                logger.error(self, log.format(exc.__str__()))
                logger.error(self, traceback.format_exc())
                action = failed_action

            from tracim_backend.lib.mail_notifier.notifier import EmailManager
            if action == send_action:
                msg = 'an email was sended to {}'.format(message['To'])
            else:
                msg = 'fail to send email to {}'.format(message['To'])

            EmailManager.log_email_notification(
                msg=msg,
                action=action,
                email_recipient=message['To'],
                email_subject=message['Subject'],
                config=self.config,
            )
Exemple #16
0
 def init_database(self, settings: typing.Dict[str, typing.Any]):
     with transaction.manager:
         try:
             fixtures_loader = FixturesLoader(self.session, self.app_config)
             fixtures_loader.loads(self.fixtures)
             transaction.commit()
             logger.info(self, "Database initialized.")
         except IntegrityError:
             logger.error(
                 self,
                 'Warning, there was a problem when adding default data'  # nopep8
                 ', it may have already been added:')
             import traceback
             logger.error(self, traceback.format_exc())
             transaction.abort()
             logger.error(self, 'Database initialization failed')
Exemple #17
0
 def init_database(self) -> None:
     session = get_tm_session(self.session_factory, transaction.manager)
     with transaction.manager:
         try:
             DeclarativeBase.metadata.create_all(self.engine)
             fixtures_loader = FixturesLoader(session, self.app_config)
             fixtures_loader.loads(self.fixtures)
             transaction.commit()
             logger.info(self, "Database initialized.")
         except IntegrityError:
             logger.error(
                 self,
                 'Warning, there was a problem when adding default data'  # nopep8
                 ', it may have already been added:')
             import traceback
             logger.error(self, traceback.format_exc())
             transaction.abort()
             logger.error(self, 'Database initialization failed')
Exemple #18
0
    def run(self) -> None:
        logger.info(self, "Starting MailFetcher")
        while self._is_active:
            imapc = None
            sleep_after_connection = True
            try:
                imapc = imapclient.IMAPClient(
                    self.host, self.port, ssl=self.use_ssl, timeout=MAIL_FETCHER_CONNECTION_TIMEOUT
                )
                imapc.login(self.user, self.password)

                logger.debug(self, "Select folder {}".format(self.folder))
                imapc.select_folder(self.folder)

                # force renew connection when deadline is reached
                deadline = time.time() + self.connection_max_lifetime
                while True:
                    if not self._is_active:
                        logger.warning(self, "Mail Fetcher process aborted")
                        sleep_after_connection = False
                        break

                    if time.time() > deadline:
                        logger.debug(
                            self,
                            "MailFetcher Connection Lifetime limit excess"
                            ", Try Re-new connection",
                        )
                        sleep_after_connection = False
                        break

                    # check for new mails
                    self._check_mail(imapc)

                    if self.use_idle and imapc.has_capability("IDLE"):
                        # IDLE_mode wait until event from server
                        logger.debug(self, "wail for event(IDLE)")
                        imapc.idle()
                        imapc.idle_check(timeout=MAIL_FETCHER_IDLE_RESPONSE_TIMEOUT)
                        imapc.idle_done()
                    else:
                        if self.use_idle and not imapc.has_capability("IDLE"):
                            log = (
                                "IDLE mode activated but server do not"
                                "support it, use polling instead."
                            )
                            logger.warning(self, log)

                        if self.burst:
                            self.stop()
                            break
                        # normal polling mode : sleep a define duration
                        logger.debug(self, "sleep for {}".format(self.heartbeat))
                        time.sleep(self.heartbeat)

                    if self.burst:
                        self.stop()
                        break
            # Socket
            except (socket.error, socket.gaierror, socket.herror) as e:
                log = "Socket fail with IMAP connection {}"
                logger.error(self, log.format(e.__str__()))

            except socket.timeout as e:
                log = "Socket timeout on IMAP connection {}"
                logger.error(self, log.format(e.__str__()))

            # SSL
            except ssl.SSLError as e:
                log = "SSL error on IMAP connection"
                logger.error(self, log.format(e.__str__()))

            except ssl.CertificateError as e:
                log = "SSL Certificate verification failed on IMAP connection"
                logger.error(self, log.format(e.__str__()))

            # Filelock
            except filelock.Timeout as e:
                log = "Mail Fetcher Lock Timeout {}"
                logger.warning(self, log.format(e.__str__()))

            # IMAP
            # TODO - G.M - 10-01-2017 - Support imapclient exceptions
            # when Imapclient stable will be 2.0+

            except BadIMAPFetchResponse as e:
                log = (
                    "Imap Fetch command return bad response."
                    "Is someone else connected to the mailbox ?: "
                    "{}"
                )
                logger.error(self, log.format(e.__str__()))
            # Others
            except Exception as e:
                log = "Mail Fetcher error {}"
                logger.error(self, log.format(e.__str__()))

            finally:
                # INFO - G.M - 2018-01-09 - Connection closing
                # Properly close connection according to
                # https://github.com/mjs/imapclient/pull/279/commits/043e4bd0c5c775c5a08cb5f1baa93876a46732ee
                # TODO : Use __exit__ method instead when imapclient stable will
                # be 2.0+ .
                if imapc:
                    logger.debug(self, "Try logout")
                    try:
                        imapc.logout()
                    except Exception:
                        try:
                            imapc.shutdown()
                        except Exception as e:
                            log = "Can't logout, connection broken ? {}"
                            logger.error(self, log.format(e.__str__()))

            if self.burst:
                self.stop()
                break

            if sleep_after_connection:
                logger.debug(self, "sleep for {}".format(self.heartbeat))
                time.sleep(self.heartbeat)

        log = "Mail Fetcher stopped"
        logger.debug(self, log)
Exemple #19
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 Exception as e:
            # TODO - G.M - 2018-08-27 - Do Better catching for exception here
            logger.error(self, 'Exception catched during email notification: {}'.format(e.__str__()))
            logger.exception(self, e)
Exemple #20
0
    def connect(self):
        if not self._smtp_connection:
            log = 'Connecting to SMTP server {}'
            logger.info(self, log.format(self._smtp_config.server))
            # TODO - G.M - 2019-01-29 - Support for SMTP SSL-only port connection
            # using smtplib.SMTP_SSL
            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()

                    if starttls_result[0] == 220:
                        logger.info(self, 'SMTP Start TLS OK')

                    log = 'SMTP Start TLS return code: {} with message: {}'
                    logger.debug(
                        self,
                        log.format(
                            starttls_result[0],
                            starttls_result[1].decode('utf-8')
                        )
                    )
                except smtplib.SMTPResponseException as exc:
                    log = 'SMTP start TLS return error code: {} with message: {}'
                    logger.error(
                        self,
                        log.format(
                            exc.smtp_code,
                            exc.smtp_error.decode('utf-8')
                        )
                    )
                except Exception as exc:
                    log = 'Unexpected exception during SMTP start TLS process: {}'
                    logger.error(self, log.format(exc.__str__()))
                    logger.error(self, traceback.format_exc())


            if self._smtp_config.login:
                try:
                    login_res = self._smtp_connection.login(
                        self._smtp_config.login,
                        self._smtp_config.password
                    )

                    if login_res[0] == 235:
                        logger.info(self, 'SMTP Authentication Successful')
                    if login_res[0] == 503:
                        logger.info(self, 'SMTP Already Authenticated')

                    log = 'SMTP login return code: {} with message: {}'
                    logger.debug(
                        self,
                        log.format(
                            login_res[0],
                            login_res[1].decode('utf-8')
                        )
                    )
                except smtplib.SMTPAuthenticationError as exc:

                    log = 'SMTP auth return error code: {} with message: {}'
                    logger.error(
                        self,
                        log.format(
                            exc.smtp_code,
                            exc.smtp_error.decode('utf-8')
                        )
                    )
                    logger.error(self,
                                 'check your auth params combinaison '
                                 '(login/password) for SMTP'
                    )
                except smtplib.SMTPResponseException as exc:
                    log = 'SMTP login return error code: {} with message: {}'
                    logger.error(
                        self,
                        log.format(
                            exc.smtp_code,
                            exc.smtp_error.decode('utf-8')
                        )
                    )
                except Exception as exc:
                    log = 'Unexpected exception during SMTP login {}'
                    logger.error(self, log.format(exc.__str__()))
                    logger.error(self, traceback.format_exc())
Exemple #21
0
    def _build_context_for_content_update(
            self,
            role: UserRoleInWorkspace,
            content_in_context: ContentInContext,
            parent_in_context: typing.Optional[ContentInContext],
            workspace_in_context: WorkspaceInContext,
            actor: User,
            translator: Translator
    ):

        _ = translator.get_translation
        content = content_in_context.content
        action = content.get_last_action().id

        # default values
        user = role.user
        workspace = role.workspace
        workspace_url = workspace_in_context.frontend_url
        main_title = content.label
        status_label = content.get_status().label
        # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for status_icon_url  # nopep8
        status_icon_url = ''
        role_label = role.role_as_label()
        content_intro = '<span id="content-intro-username">{}</span> did something.'.format(actor.display_name)  # nopep8
        content_text = content.description
        call_to_action_text = 'See more'
        call_to_action_url = content_in_context.frontend_url
        logo_url = get_email_logo_frontend_url(self.config)

        if ActionDescription.CREATION == action:
            call_to_action_text = _('View online')
            content_intro = _('<span id="content-intro-username">{}</span> create a content:').format(actor.display_name)  # nopep8

            if content_type_list.Thread.slug == content.type:
                if content.get_last_comment_from(actor):
                    content_text = content.get_last_comment_from(actor).description  # nopep8

                call_to_action_text = _('Answer')
                content_intro = _('<span id="content-intro-username">{}</span> started a thread entitled:').format(actor.display_name)
                content_text = '<p id="content-body-intro">{}</p>'.format(content.label) + content_text  # nopep8

            elif content_type_list.File.slug == content.type:
                content_intro = _('<span id="content-intro-username">{}</span> added a file entitled:').format(actor.display_name)
                if content.description:
                    content_text = content.description
                else:
                    content_text = '<span id="content-body-only-title">{}</span>'.format(content.label)

            elif content_type_list.Page.slug == content.type:
                content_intro = _('<span id="content-intro-username">{}</span> added a page entitled:').format(actor.display_name)
                content_text = '<span id="content-body-only-title">{}</span>'.format(content.label)

        elif ActionDescription.REVISION == action:
            content_text = content.description
            call_to_action_text = _('View online')

            if content_type_list.File.slug == content.type:
                content_intro = _('<span id="content-intro-username">{}</span> uploaded a new revision.').format(actor.display_name)
                content_text = content.description

        elif ActionDescription.EDITION == action:
            call_to_action_text = _('View online')

            if content_type_list.File.slug == content.type:
                content_intro = _('<span id="content-intro-username">{}</span> updated the file description.').format(actor.display_name)
                content_text = '<p id="content-body-intro">{}</p>'.format(content.get_label()) + content.description  # nopep8

            elif content_type_list.Thread.slug == content.type:
                content_intro = _('<span id="content-intro-username">{}</span> updated the thread description.').format(actor.display_name)
                previous_revision = content.get_previous_revision()
                title_diff = ''
                if previous_revision.label != content.label:
                    title_diff = htmldiff(previous_revision.label, content.label)
                content_text = str('<p id="content-body-intro">{}</p> {text} {title_diff} {content_diff}').format(
                    text=_('Here is an overview of the changes:'),
                    title_diff=title_diff,
                    content_diff=htmldiff(previous_revision.description, content.description)
                )
            elif content_type_list.Page.slug == content.type:
                content_intro = _('<span id="content-intro-username">{}</span> updated this page.').format(actor.display_name)
                previous_revision = content.get_previous_revision()
                title_diff = ''
                if previous_revision.label != content.label:
                    title_diff = htmldiff(previous_revision.label, content.label)  # nopep8
                content_text = str('<p id="content-body-intro">{}</p> {text}</p> {title_diff} {content_diff}').format(  # nopep8
                    actor.display_name,
                    text=_('Here is an overview of the changes:'),
                    title_diff=title_diff,
                    content_diff=htmldiff(previous_revision.description, content.description)
                )

        elif ActionDescription.STATUS_UPDATE == action:
            intro_user_msg = _(
                '<span id="content-intro-username">{}</span> '
                'updated the following status:'
            )
            intro_body_msg = '<p id="content-body-intro">{}: {}</p>'

            call_to_action_text = _('View online')
            content_intro = intro_user_msg.format(actor.display_name)
            content_text = intro_body_msg.format(
                content.get_label(),
                content.get_status().label,
            )

        elif ActionDescription.COMMENT == action:
            call_to_action_text = _('Answer')
            main_title = parent_in_context.label
            content_intro = _('<span id="content-intro-username">{}</span> added a comment:').format(actor.display_name)  # nopep8
            call_to_action_url = parent_in_context.frontend_url

        if not content_intro and not content_text:
            # Skip notification, but it's not normal
            logger.error(
                self,
                'A notification is being sent but no content. '
                'Here are some debug informations: [content_id: {cid}]'
                '[action: {act}][author: {actor}]'.format(
                    cid=content.content_id,
                    act=action,
                    actor=actor
                )
            )
            raise EmptyNotificationError('Unexpected empty notification')

        # FIXME: remove/readapt assert to debug easily broken case
        assert user
        assert workspace
        assert main_title
        assert status_label
        # assert status_icon_url
        assert role_label
        assert content_intro
        assert content_text or content_text == content.description
        assert call_to_action_text
        assert call_to_action_url
        assert logo_url

        return {
            'user': role.user,
            'workspace': role.workspace,
            'workspace_url': workspace_url,
            'main_title': main_title,
            'status_label': status_label,
            'status_icon_url': status_icon_url,
            'role_label': role_label,
            'content_intro': content_intro,
            'content_text': content_text,
            'call_to_action_text': call_to_action_text,
            'call_to_action_url': call_to_action_url,
            'logo_url': logo_url,
        }
Exemple #22
0
    def connect(self):
        if not self._smtp_connection:
            log = "Connecting to SMTP server {}"
            logger.info(self, log.format(self._smtp_config.server))
            # TODO - G.M - 2019-01-29 - Support for SMTP SSL-only port connection
            # using smtplib.SMTP_SSL
            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()

                    if starttls_result[0] == 220:
                        logger.info(self, "SMTP Start TLS OK")

                    log = "SMTP Start TLS return code: {} with message: {}"
                    logger.debug(
                        self,
                        log.format(starttls_result[0],
                                   starttls_result[1].decode("utf-8")))
                except smtplib.SMTPResponseException as exc:
                    log = "SMTP start TLS return error code: {} with message: {}"
                    logger.error(
                        self,
                        log.format(exc.smtp_code,
                                   exc.smtp_error.decode("utf-8")))
                except Exception as exc:
                    log = "Unexpected exception during SMTP start TLS process: {}"
                    logger.error(self, log.format(exc.__str__()))
                    logger.error(self, traceback.format_exc())

            if self._smtp_config.login:
                try:
                    login_res = self._smtp_connection.login(
                        self._smtp_config.login, self._smtp_config.password)

                    if login_res[0] == 235:
                        logger.info(self, "SMTP Authentication Successful")
                    if login_res[0] == 503:
                        logger.info(self, "SMTP Already Authenticated")

                    log = "SMTP login return code: {} with message: {}"
                    logger.debug(
                        self,
                        log.format(login_res[0], login_res[1].decode("utf-8")))
                except smtplib.SMTPAuthenticationError as exc:

                    log = "SMTP auth return error code: {} with message: {}"
                    logger.error(
                        self,
                        log.format(exc.smtp_code,
                                   exc.smtp_error.decode("utf-8")))
                    logger.error(
                        self, "check your auth params combinaison "
                        "(login/password) for SMTP")
                except smtplib.SMTPResponseException as exc:
                    log = "SMTP login return error code: {} with message: {}"
                    logger.error(
                        self,
                        log.format(exc.smtp_code,
                                   exc.smtp_error.decode("utf-8")))
                except Exception as exc:
                    log = "Unexpected exception during SMTP login {}"
                    logger.error(self, log.format(exc.__str__()))
                    logger.error(self, traceback.format_exc())