Ejemplo n.º 1
0
    def render_preference_panel(self, req, panel):
        if req.method == 'POST':
            if Locale and \
                    req.args.get('language') != req.session.get('language'):
                # reactivate translations with new language setting
                # when changed
                del req.locale  # for re-negotiating locale
                deactivate()
                make_activable(lambda: req.locale, self.env.path)
            _do_save(req, panel, self._form_fields)

        data = {
            'timezones': all_timezones,
            'timezone': get_timezone,
            'localtz': localtz,
            'has_babel': False,
        }
        if Locale:
            locale_ids = get_available_locales()
            locales = [Locale.parse(locale) for locale in locale_ids]
            # use locale identifiers from get_available_locales() instead
            # of str(locale) to prevent storing expanded locale identifier
            # to session, e.g. zh_Hans_CN and zh_Hant_TW, since Babel 1.0.
            # see #11258.
            languages = sorted((id_, locale.display_name)
                               for id_, locale in zip(locale_ids, locales))
            data['locales'] = locales
            data['languages'] = languages
            data['has_babel'] = True
        return 'prefs_localization.html', data
Ejemplo n.º 2
0
 def notify(self, tickets, new_values, comment, action, author):
     """Send batch ticket change notification e-mail (untranslated)"""
     t = deactivate()
     try:
         self._notify(tickets, new_values, comment, action, author)
     finally:
         reactivate(t)
Ejemplo n.º 3
0
 def _format_body(self):
     stream = self.template.generate(**self.data)
     # don't translate the e-mail stream
     t = deactivate()
     try:
         return stream.render('text', encoding='utf-8')
     finally:
         reactivate(t)
Ejemplo n.º 4
0
 def format(self, tickets, new_values, comment, action, author):
     """Format batch ticket change notification e-mail (untranslated)"""
     t = deactivate()
     try:
         self._prepare_body(tickets, new_values, comment, action, author)
         return self._format_body()
     finally:
         reactivate(t)
Ejemplo n.º 5
0
 def notify(self, ticket, newticket=True, modtime=None):
     """Send ticket change notification e-mail (untranslated)"""
     t = deactivate()
     translated_fields = ticket.fields
     try:
         ticket.fields = TicketSystem(self.env).get_ticket_fields()
         self._notify(ticket, newticket, modtime)
     finally:
         ticket.fields = translated_fields
         reactivate(t)
Ejemplo n.º 6
0
def _translation_deactivated(ticket=None):
    t = deactivate()
    if ticket is not None:
        ts = TicketSystem(ticket.env)
        translated_fields = ticket.fields
        ticket.fields = ts.get_ticket_fields()
    try:
        yield
    finally:
        if ticket is not None:
            ticket.fields = translated_fields
        reactivate(t)
Ejemplo n.º 7
0
Archivo: api.py Proyecto: miihael/trac
def translation_deactivated(ticket=None):
    t = deactivate()
    if ticket is not None:
        ts = TicketSystem(ticket.env)
        translated_fields = ticket.fields
        ticket.fields = ts.get_ticket_fields()
    try:
        yield
    finally:
        if ticket is not None:
            ticket.fields = translated_fields
        reactivate(t)
Ejemplo n.º 8
0
 def test_error_with_lazy_translation(self):
     self._create_env()
     os.remove(self.db_path)
     env = Environment(self.env_path)
     chrome = Chrome(env)
     dispatcher = RequestDispatcher(env)
     req = self._create_req(cookie='trac_auth=1234567890')
     req.callbacks.update({'authname': dispatcher.authenticate,
                           'chrome': chrome.prepare_request,
                           'session': dispatcher._get_session,
                           'locale': dispatcher._get_locale})
     translation.make_activable(lambda: req.locale, env.path)
     try:
         self._db_query(env)
         self.fail('ConfigurationError not raised')
     except ConfigurationError as e:
         message = unicode(e)
         self.assertIn('Database "', message)
         self.assertIn('" not found.', message)
     finally:
         translation.deactivate()
Ejemplo n.º 9
0
    def notify_attachment(self, ticket, author, filename, modtime, body):
        """Send ticket attachment notification (untranslated)"""
        t = deactivate()
        translated_fields = ticket.fields
        try:
            ticket.fields = TicketSystem(self.env).get_ticket_fields()

            self.ticket = ticket
            self.modtime = modtime
            self.newticket = False
            self.reporter = ''
            self.owner = ''

            link = self.env.abs_href.ticket(ticket.id)
            summary = self.ticket['summary']
            ticket_values = ticket.values.copy()
            ticket_values['id'] = ticket.id

            wrap_kargs = {
                'initial_indent': ' ',
                'subsequent_indent': ' ',
                'linesep': CRLF
            }
            if 'ambiwidth' in getargspec(wrap)[0]:
                wrap_kargs['ambiwidth'] = self.ambiwidth

            ticket_values['description'] = wrap(
                ticket_values.get('description', ''),
                self.COLS,
                **wrap_kargs
            )

            ticket_values['new'] = self.newticket
            ticket_values['link'] = link
            subject = 'Re: ' + self.format_subj(summary)
            author = obfuscate_email_address(author)
            change = { 'author': author }

            self.data.update({
                'ticket_props': self.format_props(),
                'ticket_body_hdr': self.format_hdr(),
                'subject': subject,
                'ticket': ticket_values,
                'change': change,
                'changes_body': body,
                })

            NotifyEmail.notify(self, ticket.id, subject)
        finally:
            ticket.fields = translated_fields
            reactivate(t)
Ejemplo n.º 10
0
 def _do_save(self, req):
     language = req.session.get('language')
     for field in self._form_fields:
         val = req.args.get(field, '').strip()
         if val:
             if field == 'tz' and 'tz' in req.session and \
                     val not in all_timezones:
                 del req.session['tz']
             elif field == 'newsid':
                 req.session.change_sid(val)
             elif field == 'accesskeys':
                 req.session[field] = '1'
             else:
                 req.session[field] = val
         elif field in req.session and (field in req.args or
                                        field + '_cb' in req.args):
             del req.session[field]
     if Locale and req.session.get('language') != language:
         # reactivate translations with new language setting when changed
         del req.locale  # for re-negotiating locale
         deactivate()
         make_activable(lambda: req.locale, self.env.path)
     add_notice(req, _("Your preferences have been saved."))
Ejemplo n.º 11
0
 def _do_save(self, req):
     language = req.session.get('language')
     for field in self._form_fields:
         val = req.args.get(field, '').strip()
         if val:
             if field == 'tz' and 'tz' in req.session and \
                     val not in all_timezones:
                 del req.session['tz']
             elif field == 'newsid':
                 req.session.change_sid(val)
             elif field == 'accesskeys':
                 req.session[field] = '1'
             else:
                 req.session[field] = val
         elif field in req.session and (field in req.args
                                        or field + '_cb' in req.args):
             del req.session[field]
     if Locale and req.session.get('language') != language:
         # reactivate translations with new language setting when changed
         del req.locale  # for re-negotiating locale
         deactivate()
         make_activable(lambda: req.locale, self.env.path)
     add_notice(req, _("Your preferences have been saved."))
 def substitute_message(self, message, ignore_exc=True):
     try:
         chrome = Chrome(self.env)
         req = self._create_request()
         t = deactivate()
         try:
             make_activable(lambda: req.locale, self.env.path)
             return self._substitute_message(chrome, req, message)
         finally:
             reactivate(t)
     except:
         self.log.warn('Caught exception while substituting message',
                       exc_info=True)
         if ignore_exc:
             return message
         raise
Ejemplo n.º 13
0
    def _internal_render(self, req, name, content, render_args=None):
        if not name == 'latex':
            return self._show_err('Unknown macro %s' % (name))

        label = None
        for line in content.split("\n"):
            m = reLABEL.search(content)
            if m:
                label = m.group(1)

        # We have to remove unnecessary ending lines because it seems otherwise preview LaTeX package
        # improperly crops the PDF -- if there is an empty line bettween the end of the content
        # and \end{preview}
        content = content.strip()

        key = sha1(
            content.encode('utf-8') + self.template_digest +
            str(self.png_resolution)).hexdigest()

        imgname = key + '.png'
        imgpath = os.path.join(self.cache_dir, imgname)

        if not os.path.exists(imgpath):
            errmsg = self._validate(content)
            if errmsg:
                return self._show_err(errmsg)

            texname = key + '.tex'
            texpath = os.path.join(self.cache_dir, texname)

            # Don't translate tex file
            t = deactivate()
            try:
                f = open(texpath, mode='w')
                self.template.generate(content=content).render(
                    encoding='utf-8', out=f)
                f.close()
            except Exception, e:
                reactivate(t)
                return self._show_err("Problem creating tex file: %s" % (e))
            finally:
Ejemplo n.º 14
0
    def _internal_render(self, req, name, content, render_args=None):
        if not name == 'latex':
            return self._show_err('Unknown macro %s' % (name))

        label = None
        for line in content.split("\n"):
            m = reLABEL.search(content)
            if m:
                label = m.group(1)

        # We have to remove unnecessary ending lines because it seems otherwise preview LaTeX package
        # improperly crops the PDF -- if there is an empty line bettween the end of the content
        # and \end{preview}
        content = content.strip()

        key = sha1(content.encode('utf-8') + self.template_digest + str(self.png_resolution)).hexdigest()

        imgname = key + '.png'
        imgpath = os.path.join(self.cache_dir, imgname)

        if not os.path.exists(imgpath):
            errmsg = self._validate(content)
            if errmsg:
                return self._show_err(errmsg)

            texname = key + '.tex'
            texpath = os.path.join(self.cache_dir, texname)

            # Don't translate tex file
            t = deactivate()
            try:
                f = open(texpath, mode='w')
                self.template.generate(content=content).render(encoding='utf-8', out=f)
                f.close()
            except Exception, e:
                reactivate(t)
                return self._show_err("Problem creating tex file: %s" % (e))
            finally:
Ejemplo n.º 15
0
 def tearDown(self):
     translation.deactivate()
     self.env.reset_db()
     shutil.rmtree(self.env.path)
Ejemplo n.º 16
0
    def send(self, torcpts, ccrcpts, mime_headers={}):
        from email.MIMEText import MIMEText
        from email.Utils import formatdate
        stream = self.template.generate(**self.data)
        # don't translate the e-mail stream
        t = deactivate()
        try:
            body = stream.render('text', encoding='utf-8')
        finally:
            reactivate(t)
        public_cc = self.config.getbool('notification', 'use_public_cc')
        headers = {
            'X-Mailer': 'Trac %s, by Edgewall Software' % __version__,
            'X-Trac-Version': __version__,
            'X-Trac-Project': self.env.project_name,
            'X-URL': self.env.project_url,
            'Precedence': 'bulk',
            'Auto-Submitted': 'auto-generated',
            'Subject': self.subject,
            'From': (self.from_name, self.from_email) if self.from_name
                                                      else self.from_email,
            'Reply-To': self.replyto_email
        }

        def build_addresses(rcpts):
            """Format and remove invalid addresses"""
            return filter(lambda x: x,
                          [self.get_smtp_address(addr) for addr in rcpts])

        def remove_dup(rcpts, all):
            """Remove duplicates"""
            tmp = []
            for rcpt in rcpts:
                if not rcpt in all:
                    tmp.append(rcpt)
                    all.append(rcpt)
            return tmp, all

        notify_sys = NotificationSystem(self.env)
        toaddrs = build_addresses(torcpts)
        ccaddrs = build_addresses(ccrcpts)
        accaddrs = notify_sys.smtp_always_cc_list
        bccaddrs = notify_sys.smtp_always_bcc_list

        recipients = []
        toaddrs, recipients = remove_dup(toaddrs, recipients)
        ccaddrs, recipients = remove_dup(ccaddrs, recipients)
        accaddrs, recipients = remove_dup(accaddrs, recipients)
        bccaddrs, recipients = remove_dup(bccaddrs, recipients)

        # if there is not valid recipient, leave immediately
        if len(recipients) < 1:
            self.env.log.info("no recipient for a ticket notification")
            return

        pcc = accaddrs
        if public_cc:
            pcc += ccaddrs
            if toaddrs:
                headers['To'] = ', '.join(toaddrs)
        if pcc:
            headers['Cc'] = ', '.join(pcc)
        headers['Date'] = formatdate()
        msg = MIMEText(body, 'plain')
        # Message class computes the wrong type from MIMEText constructor,
        # which does not take a Charset object as initializer. Reset the
        # encoding type to force a new, valid evaluation
        del msg['Content-Transfer-Encoding']
        msg.set_charset(self._charset)
        self.add_headers(msg, headers)
        self.add_headers(msg, mime_headers)
        NotificationSystem(self.env).send_email(self.from_email, recipients,
                                                msg.as_string())
Ejemplo n.º 17
0
 def tearDown(self):
     translation.deactivate()
     self.env.reset_db()
     shutil.rmtree(self.env.path)
    def send(self, torcpts, ccrcpts, mime_headers={}):
        from email.MIMEText import MIMEText
        from email.Utils import formatdate

        attach_diff = self.config.getbool('wiki-notification', 'attach_diff')
        if attach_diff:
            from email.MIMEMultipart import MIMEMultipart
            self.data["wikidiff"] = None

        charset = str(self._charset)

        stream = self.template.generate(**self.data)
        # don't translate the e-mail stream
        t = deactivate()
        try:
            body = stream.render('text', encoding=charset)
        finally:
            reactivate(t)
#        self.env.log.debug('Email Contents: %s', body)
        public_cc = self.config.getbool('wiki-notification', 'use_public_cc')
        headers = {}
        headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__
        headers['X-Trac-Version'] = __version__
        headers['X-Trac-Project'] = self.env.project_name
        headers['X-URL'] = self.env.project_url
        headers['Precedence'] = 'bulk'
        headers['Auto-Submitted'] = 'auto-generated'
        headers['Subject'] = self.subject
        headers['From'] = (
            self.from_name, self.from_email) if self.from_name else self.from_email
        headers['Reply-To'] = self.replyto_email

        def build_addresses(rcpts):
            """Format and remove invalid addresses"""
            return filter(lambda x: x,
                          [self.get_smtp_address(addr) for addr in rcpts])

        blocked_addresses = []

        def remove_dup(rcpts, all):
            """Remove duplicates"""
            tmp = []
            for rcpt in rcpts:
                if rcpt in self.banned_addresses:
                    self.env.log.debug("Banned Address: %s", rcpt)
                    blocked_addresses.append(rcpt)
                elif not rcpt in all:
                    tmp.append(rcpt)
                    all.append(rcpt)
            return (tmp, all)

        toaddrs = build_addresses(torcpts)
        ccaddrs = build_addresses(ccrcpts)
        accaddrs = build_addresses(self.config.getlist(
            'wiki-notification', 'smtp_always_cc', []))
        bccaddrs = build_addresses(self.config.getlist(
            'wiki-notification', 'smtp_always_bcc', []))

        recipients = []
        (toaddrs, recipients) = remove_dup(toaddrs, recipients)
        (ccaddrs, recipients) = remove_dup(ccaddrs, recipients)
        (accaddrs, recipients) = remove_dup(accaddrs, recipients)
        (bccaddrs, recipients) = remove_dup(bccaddrs, recipients)

        self.env.log.debug("Not notifying the following addresses: %s",
                           ', '.join(blocked_addresses))

        # if there is not valid recipient, leave immediately
        if len(recipients) < 1:
            self.env.log.info('no recipient for a wiki notification')
            return

        dest = self.change_author or 'anonymous'
        headers['X-Trac-Wiki-URL'] = self.data['link']

        pcc = accaddrs
        if public_cc:
            pcc += ccaddrs
            if toaddrs:
                headers['To'] = ', '.join(toaddrs)
        if pcc:
            headers['Cc'] = ', '.join(pcc)
        headers['Date'] = formatdate()
        if attach_diff:
            # With MIMEMultipart the charset has to be set before any parts
            # are added.
            msg = MIMEMultipart('mixed', None, [], charset=charset)
            msg.preamble = 'This is a multi-part message in MIME format.'

            # The text Message
            mail = MIMEText(body, 'plain', charset)
            mail.add_header('Content-Disposition', 'inline',
                            filename="message.txt")
            msg.attach(mail)
            try:
                # The Diff Attachment
                attach = MIMEText(self.wikidiff.encode(
                    'utf-8'), 'x-patch', charset)
                attach.add_header('Content-Disposition', 'inline',
                                  filename=self.page.name + '.diff')
                msg.attach(attach)
            except AttributeError:
                # We don't have a wikidiff to attach
                pass
        else:
            msg = MIMEText(body, 'plain', charset)

        self.add_headers(msg, headers)
        self.add_headers(msg, mime_headers)
        self.env.log.info("Sending notification to %s" % (recipients,))
        try:
            NotificationSystem(self.env).send_email(
                self.from_email, recipients, msg.as_string())
        except Exception, err:
            self.env.log.debug('Notification could not be sent: %r', err)
Ejemplo n.º 19
0
    def send(self, torcpts, ccrcpts, mime_headers={}):
        from email.MIMEText import MIMEText
        from email.Utils import formatdate
        stream = self.template.generate(**self.data)
        # don't translate the e-mail stream
        t = deactivate()
        try:
            body = stream.render('text', encoding='utf-8')
        finally:
            reactivate(t)
        public_cc = self.config.getbool('notification', 'use_public_cc')
        headers = {}
        headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__
        headers['X-Trac-Version'] =  __version__
        headers['X-Trac-Project'] =  self.env.project_name
        headers['X-URL'] = self.env.project_url
        headers['Precedence'] = 'bulk'
        headers['Auto-Submitted'] = 'auto-generated'
        headers['Subject'] = self.subject
        headers['From'] = (self.from_name, self.from_email) if self.from_name \
                          else self.from_email
        headers['Reply-To'] = self.replyto_email

        def build_addresses(rcpts):
            """Format and remove invalid addresses"""
            return filter(lambda x: x, \
                          [self.get_smtp_address(addr) for addr in rcpts])

        def remove_dup(rcpts, all):
            """Remove duplicates"""
            tmp = []
            for rcpt in rcpts:
                if not rcpt in all:
                    tmp.append(rcpt)
                    all.append(rcpt)
            return (tmp, all)

        toaddrs = build_addresses(torcpts)
        ccaddrs = build_addresses(ccrcpts)
        accparam = self.config.get('notification', 'smtp_always_cc')
        accaddrs = accparam and \
                   build_addresses(accparam.replace(',', ' ').split()) or []
        bccparam = self.config.get('notification', 'smtp_always_bcc')
        bccaddrs = bccparam and \
                   build_addresses(bccparam.replace(',', ' ').split()) or []

        recipients = []
        (toaddrs, recipients) = remove_dup(toaddrs, recipients)
        (ccaddrs, recipients) = remove_dup(ccaddrs, recipients)
        (accaddrs, recipients) = remove_dup(accaddrs, recipients)
        (bccaddrs, recipients) = remove_dup(bccaddrs, recipients)
        
        # if there is not valid recipient, leave immediately
        if len(recipients) < 1:
            self.env.log.info('no recipient for a ticket notification')
            return

        pcc = accaddrs
        if public_cc:
            pcc += ccaddrs
            if toaddrs:
                headers['To'] = ', '.join(toaddrs)
        if pcc:
            headers['Cc'] = ', '.join(pcc)
        headers['Date'] = formatdate()
        msg = MIMEText(body, 'plain')
        # Message class computes the wrong type from MIMEText constructor,
        # which does not take a Charset object as initializer. Reset the
        # encoding type to force a new, valid evaluation
        del msg['Content-Transfer-Encoding']
        msg.set_charset(self._charset)
        self.add_headers(msg, headers)
        self.add_headers(msg, mime_headers)
        NotificationSystem(self.env).send_email(self.from_email, recipients,
                                                msg.as_string())
Ejemplo n.º 20
0
                environ.update({
                    'trac.web.frontend': 'mod_wsgi',
                    'trac.web.version': mod_wsgi_version})
            env.webfrontend = environ.get('trac.web.frontend')
            if env.webfrontend:
                env.systeminfo.append((env.webfrontend,
                                       environ['trac.web.version']))
    except Exception, e:
        env_error = e

    req = RequestWithSession(environ, start_response)
    translation.make_activable(lambda: req.locale, env.path if env else None)
    try:
        return _dispatch_request(req, env, env_error)
    finally:
        translation.deactivate()
        if env and not run_once:
            env.shutdown(threading._get_ident())
            # Now it's a good time to do some clean-ups
            #
            # Note: enable the '##' lines as soon as there's a suspicion
            #       of memory leak due to uncollectable objects (typically
            #       objects with a __del__ method caught in a cycle)
            #
            ##gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
            unreachable = gc.collect()
            ##env.log.debug("%d unreachable objects found.", unreachable)
            ##uncollectable = len(gc.garbage)
            ##if uncollectable:
            ##    del gc.garbage[:]
            ##    env.log.warn("%d uncollectable objects found.", uncollectable)
Ejemplo n.º 21
0
    def test_error_with_lazy_translation(self):
        self._create_env()
        os.chmod(self.db_path, 0444)
        env = Environment(self.env_path)
        chrome = Chrome(env)
        dispatcher = RequestDispatcher(env)
        req = self._create_req(cookie='trac_auth=1234567890')
        req.callbacks.update({'authname': dispatcher.authenticate,
                              'chrome': chrome.prepare_request,
                              'session': dispatcher._get_session,
                              'locale': dispatcher._get_locale})
        translation.make_activable(lambda: req.locale, env.path)
        try:
            self._db_query(env)
            self.fail('ConfigurationError not raised')
        except ConfigurationError, e:
            self.assertIn('requires read _and_ write permissions', unicode(e))
        finally:
            translation.deactivate()


def suite():
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(DatabaseFileTestCase))
    return suite


if __name__ == '__main__':
    unittest.main(defaultTest='suite')
Ejemplo n.º 22
0
 def tearDown(self):
     translation.deactivate()
     self.env.reset_db_and_disk()
Ejemplo n.º 23
0
def dispatch_request(environ, start_response):
    """Main entry point for the Trac web interface.

    :param environ: the WSGI environment dict
    :param start_response: the WSGI callback for starting the response
    """

    global _warn_setuptools
    if _warn_setuptools is False:
        _warn_setuptools = True
        warn_setuptools_issue(out=environ.get('wsgi.errors'))

    # SCRIPT_URL is an Apache var containing the URL before URL rewriting
    # has been applied, so we can use it to reconstruct logical SCRIPT_NAME
    script_url = environ.get('SCRIPT_URL')
    if script_url is not None:
        path_info = environ.get('PATH_INFO')
        if not path_info:
            environ['SCRIPT_NAME'] = script_url
        else:
            # mod_wsgi squashes slashes in PATH_INFO (!)
            script_url = _slashes_re.sub('/', script_url)
            path_info = _slashes_re.sub('/', path_info)
            if script_url.endswith(path_info):
                environ['SCRIPT_NAME'] = script_url[:-len(path_info)]

    # If the expected configuration keys aren't found in the WSGI environment,
    # try looking them up in the process environment variables
    environ.setdefault('trac.env_path', os.getenv('TRAC_ENV'))
    environ.setdefault('trac.env_parent_dir',
                       os.getenv('TRAC_ENV_PARENT_DIR'))
    environ.setdefault('trac.env_index_template',
                       os.getenv('TRAC_ENV_INDEX_TEMPLATE'))
    environ.setdefault('trac.template_vars',
                       os.getenv('TRAC_TEMPLATE_VARS'))
    environ.setdefault('trac.locale', '')
    environ.setdefault('trac.base_url',
                       os.getenv('TRAC_BASE_URL'))


    locale.setlocale(locale.LC_ALL, environ['trac.locale'])

    # Determine the environment
    env_path = environ.get('trac.env_path')
    if not env_path:
        env_parent_dir = environ.get('trac.env_parent_dir')
        env_paths = environ.get('trac.env_paths')
        if env_parent_dir or env_paths:
            # The first component of the path is the base name of the
            # environment
            path_info = environ.get('PATH_INFO', '').lstrip('/').split('/')
            env_name = path_info.pop(0)

            if not env_name:
                # No specific environment requested, so render an environment
                # index page
                send_project_index(environ, start_response, env_parent_dir,
                                   env_paths)
                return []

            errmsg = None

            # To make the matching patterns of request handlers work, we append
            # the environment name to the `SCRIPT_NAME` variable, and keep only
            # the remaining path in the `PATH_INFO` variable.
            script_name = environ.get('SCRIPT_NAME', '')
            try:
                script_name = unicode(script_name, 'utf-8')
                # (as Href expects unicode parameters)
                environ['SCRIPT_NAME'] = Href(script_name)(env_name)
                environ['PATH_INFO'] = '/' + '/'.join(path_info)

                if env_parent_dir:
                    env_path = os.path.join(env_parent_dir, env_name)
                else:
                    env_path = get_environments(environ).get(env_name)

                if not env_path or not os.path.isdir(env_path):
                    errmsg = 'Environment not found'
            except UnicodeDecodeError:
                errmsg = 'Invalid URL encoding (was %r)' % script_name

            if errmsg:
                start_response('404 Not Found',
                               [('Content-Type', 'text/plain'),
                                ('Content-Length', str(len(errmsg)))])
                return [errmsg]

    if not env_path:
        raise EnvironmentError('The environment options "TRAC_ENV" or '
                               '"TRAC_ENV_PARENT_DIR" or the mod_python '
                               'options "TracEnv" or "TracEnvParentDir" are '
                               'missing. Trac requires one of these options '
                               'to locate the Trac environment(s).')
    run_once = environ['wsgi.run_once']

    env = env_error = None
    try:
        env = open_environment(env_path, use_cache=not run_once)
        if env.base_url_for_redirect:
            environ['trac.base_url'] = env.base_url

        # Web front-end type and version information
        if not hasattr(env, 'webfrontend'):
            mod_wsgi_version = environ.get('mod_wsgi.version')
            if mod_wsgi_version:
                mod_wsgi_version = (
                        "%s (WSGIProcessGroup %s WSGIApplicationGroup %s)" %
                        ('.'.join([str(x) for x in mod_wsgi_version]),
                         environ.get('mod_wsgi.process_group'),
                         environ.get('mod_wsgi.application_group') or
                         '%{GLOBAL}'))
                environ.update({
                    'trac.web.frontend': 'mod_wsgi',
                    'trac.web.version': mod_wsgi_version})
            env.webfrontend = environ.get('trac.web.frontend')
            if env.webfrontend:
                env.webfrontend_version = environ['trac.web.version']
    except Exception as e:
        env_error = e

    req = RequestWithSession(environ, start_response)
    translation.make_activable(lambda: req.locale, env.path if env else None)
    try:
        return _dispatch_request(req, env, env_error)
    finally:
        translation.deactivate()
        if env and not run_once:
            env.shutdown(threading._get_ident())
            # Now it's a good time to do some clean-ups
            #
            # Note: enable the '##' lines as soon as there's a suspicion
            #       of memory leak due to uncollectable objects (typically
            #       objects with a __del__ method caught in a cycle)
            #
            ##gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
            unreachable = gc.collect()
Ejemplo n.º 24
0
    def send(self, torcpts, ccrcpts, mime_headers={}):
        from email.MIMEText import MIMEText
        from email.Utils import formatdate

        attach_diff = self.config.getbool('wiki-notification', 'attach_diff')
        if attach_diff:
            from email.MIMEMultipart import MIMEMultipart
            self.data["wikidiff"] = None

        charset = str(self._charset)

        stream = self.template.generate(**self.data)
        # don't translate the e-mail stream
        t = deactivate()
        try:
            body = stream.render('text', encoding=charset)
        finally:
            reactivate(t)
#        self.env.log.debug('Email Contents: %s', body)
        public_cc = self.config.getbool('wiki-notification', 'use_public_cc')
        headers = {}
        headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__
        headers['X-Trac-Version'] = __version__
        headers['X-Trac-Project'] = self.env.project_name
        headers['X-URL'] = self.env.project_url
        headers['Precedence'] = 'bulk'
        headers['Auto-Submitted'] = 'auto-generated'
        headers['Subject'] = self.subject
        headers['From'] = (
            self.from_name,
            self.from_email) if self.from_name else self.from_email
        headers['Reply-To'] = self.replyto_email

        def build_addresses(rcpts):
            """Format and remove invalid addresses"""
            return filter(lambda x: x,
                          [self.get_smtp_address(addr) for addr in rcpts])

        blocked_addresses = []

        def remove_dup(rcpts, all):
            """Remove duplicates"""
            tmp = []
            for rcpt in rcpts:
                if rcpt in self.banned_addresses:
                    self.env.log.debug("Banned Address: %s", rcpt)
                    blocked_addresses.append(rcpt)
                elif not rcpt in all:
                    tmp.append(rcpt)
                    all.append(rcpt)
            return (tmp, all)

        toaddrs = build_addresses(torcpts)
        ccaddrs = build_addresses(ccrcpts)
        accaddrs = build_addresses(
            self.config.getlist('wiki-notification', 'smtp_always_cc', []))
        bccaddrs = build_addresses(
            self.config.getlist('wiki-notification', 'smtp_always_bcc', []))

        recipients = []
        (toaddrs, recipients) = remove_dup(toaddrs, recipients)
        (ccaddrs, recipients) = remove_dup(ccaddrs, recipients)
        (accaddrs, recipients) = remove_dup(accaddrs, recipients)
        (bccaddrs, recipients) = remove_dup(bccaddrs, recipients)

        self.env.log.debug("Not notifying the following addresses: %s",
                           ', '.join(blocked_addresses))

        # if there is not valid recipient, leave immediately
        if len(recipients) < 1:
            self.env.log.info('no recipient for a wiki notification')
            return

        dest = self.change_author or 'anonymous'
        headers['X-Trac-Wiki-URL'] = self.data['link']

        pcc = accaddrs
        if public_cc:
            pcc += ccaddrs
            if toaddrs:
                headers['To'] = ', '.join(toaddrs)
        if pcc:
            headers['Cc'] = ', '.join(pcc)
        headers['Date'] = formatdate()
        if attach_diff:
            # With MIMEMultipart the charset has to be set before any parts
            # are added.
            msg = MIMEMultipart('mixed', None, [], charset=charset)
            msg.preamble = 'This is a multi-part message in MIME format.'

            # The text Message
            mail = MIMEText(body, 'plain', charset)
            mail.add_header('Content-Disposition',
                            'inline',
                            filename="message.txt")
            msg.attach(mail)
            try:
                # The Diff Attachment
                attach = MIMEText(self.wikidiff.encode('utf-8'), 'x-patch',
                                  charset)
                attach.add_header('Content-Disposition',
                                  'inline',
                                  filename=self.page.name + '.diff')
                msg.attach(attach)
            except AttributeError:
                # We don't have a wikidiff to attach
                pass
        else:
            msg = MIMEText(body, 'plain', charset)

        self.add_headers(msg, headers)
        self.add_headers(msg, mime_headers)
        self.env.log.info("Sending notification to %s" % (recipients, ))
        try:
            NotificationSystem(self.env).send_email(self.from_email,
                                                    recipients,
                                                    msg.as_string())
        except Exception, err:
            self.env.log.debug('Notification could not be sent: %r', err)
Ejemplo n.º 25
0
def dispatch_request(environ, start_response):
    """Main entry point for the Trac web interface.

    :param environ: the WSGI environment dict
    :param start_response: the WSGI callback for starting the response
    """

    global _warn_setuptools
    if _warn_setuptools is False:
        _warn_setuptools = True
        warn_setuptools_issue(out=environ.get('wsgi.errors'))

    # SCRIPT_URL is an Apache var containing the URL before URL rewriting
    # has been applied, so we can use it to reconstruct logical SCRIPT_NAME
    script_url = environ.get('SCRIPT_URL')
    if script_url is not None:
        path_info = environ.get('PATH_INFO')
        if not path_info:
            environ['SCRIPT_NAME'] = script_url
        else:
            # mod_wsgi squashes slashes in PATH_INFO (!)
            script_url = _slashes_re.sub('/', script_url)
            path_info = _slashes_re.sub('/', path_info)
            if script_url.endswith(path_info):
                environ['SCRIPT_NAME'] = script_url[:-len(path_info)]

    # If the expected configuration keys aren't found in the WSGI environment,
    # try looking them up in the process environment variables
    environ.setdefault('trac.env_path', os.getenv('TRAC_ENV'))
    environ.setdefault('trac.env_parent_dir', os.getenv('TRAC_ENV_PARENT_DIR'))
    environ.setdefault('trac.env_index_template',
                       os.getenv('TRAC_ENV_INDEX_TEMPLATE'))
    environ.setdefault('trac.template_vars', os.getenv('TRAC_TEMPLATE_VARS'))
    environ.setdefault('trac.locale', '')
    environ.setdefault('trac.base_url', os.getenv('TRAC_BASE_URL'))

    locale.setlocale(locale.LC_ALL, environ['trac.locale'])

    # Determine the environment
    env_path = environ.get('trac.env_path')
    if not env_path:
        env_parent_dir = environ.get('trac.env_parent_dir')
        env_paths = environ.get('trac.env_paths')
        if env_parent_dir or env_paths:
            # The first component of the path is the base name of the
            # environment
            path_info = environ.get('PATH_INFO', '').lstrip('/').split('/')
            env_name = path_info.pop(0)

            if not env_name:
                # No specific environment requested, so render an environment
                # index page
                send_project_index(environ, start_response, env_parent_dir,
                                   env_paths)
                return []

            errmsg = None

            # To make the matching patterns of request handlers work, we append
            # the environment name to the `SCRIPT_NAME` variable, and keep only
            # the remaining path in the `PATH_INFO` variable.
            script_name = environ.get('SCRIPT_NAME', '')
            try:
                script_name = unicode(script_name, 'utf-8')
                # (as Href expects unicode parameters)
                environ['SCRIPT_NAME'] = Href(script_name)(env_name)
                environ['PATH_INFO'] = '/' + '/'.join(path_info)

                if env_parent_dir:
                    env_path = os.path.join(env_parent_dir, env_name)
                else:
                    env_path = get_environments(environ).get(env_name)

                if not env_path or not os.path.isdir(env_path):
                    errmsg = 'Environment not found'
            except UnicodeDecodeError:
                errmsg = 'Invalid URL encoding (was %r)' % script_name

            if errmsg:
                start_response('404 Not Found',
                               [('Content-Type', 'text/plain'),
                                ('Content-Length', str(len(errmsg)))])
                return [errmsg]

    if not env_path:
        raise EnvironmentError('The environment options "TRAC_ENV" or '
                               '"TRAC_ENV_PARENT_DIR" or the mod_python '
                               'options "TracEnv" or "TracEnvParentDir" are '
                               'missing. Trac requires one of these options '
                               'to locate the Trac environment(s).')
    run_once = environ['wsgi.run_once']

    env = env_error = None
    try:
        env = open_environment(env_path, use_cache=not run_once)
        if env.base_url_for_redirect:
            environ['trac.base_url'] = env.base_url

        # Web front-end type and version information
        if not hasattr(env, 'webfrontend'):
            mod_wsgi_version = environ.get('mod_wsgi.version')
            if mod_wsgi_version:
                mod_wsgi_version = (
                    "%s (WSGIProcessGroup %s WSGIApplicationGroup %s)" %
                    ('.'.join([str(x) for x in mod_wsgi_version
                               ]), environ.get('mod_wsgi.process_group'),
                     environ.get('mod_wsgi.application_group') or '%{GLOBAL}'))
                environ.update({
                    'trac.web.frontend': 'mod_wsgi',
                    'trac.web.version': mod_wsgi_version
                })
            env.webfrontend = environ.get('trac.web.frontend')
            if env.webfrontend:
                env.webfrontend_version = environ['trac.web.version']
    except Exception as e:
        env_error = e

    req = RequestWithSession(environ, start_response)
    translation.make_activable(lambda: req.locale, env.path if env else None)
    try:
        return _dispatch_request(req, env, env_error)
    finally:
        translation.deactivate()
        if env and not run_once:
            env.shutdown(threading._get_ident())
            # Now it's a good time to do some clean-ups
            #
            # Note: enable the '##' lines as soon as there's a suspicion
            #       of memory leak due to uncollectable objects (typically
            #       objects with a __del__ method caught in a cycle)
            #
            ##gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
            unreachable = gc.collect()
Ejemplo n.º 26
0
    def send(self, torcpts, ccrcpts, mime_headers={}):
        """
        this method is based NotifyEmail in trac/notification.py

        As the default trac NotifyEmail class assumes alot, and will overwrite headers
        we do not call our ancestor class method here, but send the mail direct
        """
        from email.MIMEText import MIMEText
        from email.Utils import formatdate
        stream = self.template.generate(**self.data)
        # don't translate the e-mail stream
        t = deactivate()
        try:
            body = stream.render('text')
        finally:
            reactivate(t)
        projname = self.env.project_name
        public_cc = self.config.getbool('notification', 'use_public_cc')
        headers = {}
        headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__
        headers['X-Trac-Version'] = __version__
        headers['X-Trac-Project'] = projname
        headers['X-URL'] = self.env.project_url
        headers['Precedence'] = 'bulk'
        headers['Auto-Submitted'] = 'auto-generated'
        headers['Subject'] = self.subject
        headers['From'] = (self.from_name or projname, self.from_email)
        headers['Reply-To'] = self.reply_to_email

        # add Message-ID and In-Reply-To for threaded mail clients
        if self.action == 'post_created':
            headers['Message-ID'] = self.get_message_id(
                projname, self.blog.name)
        else:
            headers['Message-ID'] = self.get_message_id(
                projname, self.blog.name, self.time)
            headers['In-Reply-To'] = headers[
                'References'] = self.get_message_id(projname, self.blog.name)

        def build_addresses(rcpts):
            """Format and remove invalid addresses"""
            return filter(lambda x: x,
                          [self.get_smtp_address(addr) for addr in rcpts])

        def remove_dup(rcpts, all):
            """Remove duplicates"""
            tmp = []
            for rcpt in rcpts:
                if not rcpt in all:
                    tmp.append(rcpt)
                    all.append(rcpt)
            return (tmp, all)

        toaddrs = build_addresses(torcpts)
        ccaddrs = build_addresses(ccrcpts)
        accparam = self.config.getlist('fullblog-notification',
                                       'smtp_always_cc')
        accaddrs = accparam and build_addresses(accparam) or []

        recipients = []
        (toaddrs, recipients) = remove_dup(toaddrs, recipients)
        (ccaddrs, recipients) = remove_dup(ccaddrs, recipients)
        (accaddrs, recipients) = remove_dup(accaddrs, recipients)

        # if there is not valid recipient, leave immediately
        if len(recipients) < 1:
            self.env.log.info('no recipient for a fullblog notification')
            return

        cc = accaddrs + ccaddrs
        if cc:
            headers['Cc'] = ', '.join(cc)
        if toaddrs:
            headers['To'] = ', '.join(toaddrs)
        headers['Date'] = formatdate()
        msg = MIMEText(body, 'plain')
        # Message class computes the wrong type from MIMEText constructor,
        # which does not take a Charset object as initializer. Reset the
        # encoding type to force a new, valid evaluation
        del msg['Content-Transfer-Encoding']
        msg.set_charset(self._charset)
        self.add_headers(msg, headers)
        self.add_headers(msg, mime_headers)
        NotificationSystem(self.env).send_email(self.from_email, recipients,
                                                msg.as_string())