def _find_first_component_from_list(self, possible_components):
     ret = None
     for compname in possible_components:
         try:
             component = TicketComponent(self.env, compname)
             ret = component.name
             break
         except ResourceNotFound:
             # No such component exists
             pass
     return ret
Exemple #2
0
    def _sub_owner_keyword(self, owner, ticket):
        """Substitute keywords from the default_owner field.

        < default > -> component owner
        """
        if owner in ('< default >', '<default>'):
            default_owner = ''
            if ticket['component']:
                try:
                    component = TicketComponent(self.env, ticket['component'])
                except ResourceNotFound:
                    pass  # No such component exists
                else:
                    default_owner = component.owner  # May be empty
            return default_owner
        return owner
Exemple #3
0
    def __call__(self, context, hack_name):
        hack_type = context.data.get('type', 'plugin')
        page_name = get_page_name(hack_name, hack_type)
        if WikiPage(self.env, page_name).exists:
            raise ValidationError('Page already exists.')

        repos = self.env.get_repository()
        path = '/%s' % page_name.lower()
        if repos.has_node(path):
            raise ValidationError(
                'Resulting path "%s" already exists in repository.' % path
            )

        try:
            TicketComponent(self.env, page_name)
        except ResourceNotFound, e:
            pass
Exemple #4
0
def copy_component(source_env, dest_env, name, dest_db=None):
    # In case a string gets passed in
    if not isinstance(source_env, Environment):
        source_env = _open_environment(source_env)
    if not isinstance(dest_env, Environment):
        dest_env = _open_environment(dest_env)

    # Log message
    source_env.log.info(
        'DatamoverPlugin: Moving component %s to the environment at %s', name,
        dest_env.path)
    dest_env.log.info(
        'DatamoverPlugin: Moving component %s from the environment at %s',
        name, source_env.path)

    # Open databases
    source_db = source_env.get_db_cnx()
    source_cursor = source_db.cursor()
    handle_commit = True
    if not dest_db:
        dest_db, handle_commit = dest_env.get_db_cnx(), False
    dest_cursor = dest_db.cursor()

    # Remove the component from the destination
    try:
        dest_comp = TicketComponent(dest_env, name, db=dest_db)
        dest_comp.delete(db=dest_db)
    except TracError:
        pass

    # Copy each entry in the component table
    source_cursor.execute('SELECT * FROM component WHERE name=%s', (name, ))
    for row in source_cursor:
        comp_data = dict(zip([d[0] for d in source_cursor.description], row))
        q = make_query(comp_data, 'component')
        dest_cursor.execute(*q)

    if handle_commit:
        dest_db.commit()
Exemple #5
0
    def get_ticket_changes(self, req, ticket, action):
        this_action = self.actions[action]

        # Enforce permissions
        if not self._is_action_allowed(req, this_action, ticket.resource):
            # The user does not have any of the listed permissions, so we won't
            # do anything.
            return {}

        updated = {}
        # Status changes
        status = this_action['newstate']
        if status != '*':
            updated['status'] = status

        for operation in this_action['operations']:
            if operation == 'del_owner':
                updated['owner'] = ''
            elif operation in ('set_owner', 'may_set_owner'):
                set_owner = this_action.get('set_owner')
                newowner = req.args.get('action_%s_reassign_owner' % action,
                                        set_owner[0] if set_owner else '')
                # If there was already an owner, we get a list, [new, old],
                # but if there wasn't we just get new.
                if type(newowner) == list:
                    newowner = newowner[0]
                updated['owner'] = self._sub_owner_keyword(newowner, ticket)
            elif operation == 'set_owner_to_self':
                updated['owner'] = get_reporter_id(req, 'author')
            elif operation == 'del_resolution':
                updated['resolution'] = ''
            elif operation == 'set_resolution':
                set_resolution = this_action.get('set_resolution')
                newresolution = req.args.get('action_%s_resolve_resolution'
                                             % action,
                                             set_resolution[0]
                                             if set_resolution else '')
                updated['resolution'] = newresolution

            # reset_workflow is just a no-op here, so we don't look for it.
            # leave_status is just a no-op here, so we don't look for it.

        # Set owner to component owner for 'new' ticket if:
        #  - ticket doesn't exist and owner is < default >
        #  - component is changed
        #  - owner isn't explicitly changed
        #  - ticket owner is equal to owner of previous component
        #  - new component has an owner
        if not ticket.exists and 'owner' not in updated:
            updated['owner'] = self._sub_owner_keyword(ticket['owner'], ticket)
        elif ticket['status'] == 'new' and \
                'component' in ticket.values and \
                'component' in ticket._old and \
                'owner' not in updated:
            try:
                old_comp = TicketComponent(self.env, ticket._old['component'])
            except ResourceNotFound:
                # If the old component has been removed from the database
                # we just leave the owner as is.
                pass
            else:
                old_owner = old_comp.owner or ''
                current_owner = ticket['owner'] or ''
                if old_owner == current_owner:
                    new_comp = TicketComponent(self.env, ticket['component'])
                    if new_comp.owner:
                        updated['owner'] = new_comp.owner

        return updated
Exemple #6
0
    def create_hack(self, req, data, vars):
        import fcntl

        messages = []
        created = False
        have_lock = False
        lockfile = open(self.lockfile, "w")
        try:
            rv = fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
            if rv:
                raise TracError('Failed to acquire lock, error: %i' % rv)
            have_lock = True
        except IOError:
            messages.append(
                'A hack is currently being created by another user. '
                'Please wait a few seconds, then click the "Create hack" '
                'button again.'
            )

        if have_lock:
            steps_done = []
            try:
                # Step 1: create repository paths
                from os import popen

                svn_path = 'file://%s' % \
                    self.env.config.get('trac', 'repository_dir')
                svn_path = svn_path.rstrip('/')
                page_name = vars['WIKINAME']
                hack_path = vars['LCNAME']
                paths = [ '%s/%s' % (svn_path, hack_path) ]
                selected_releases = data['selected_releases']
                if isinstance(selected_releases, (basestring, unicode)):
                    selected_releases = [ selected_releases, ]
                for release in selected_releases:
                    if release == 'anyrelease': continue
                    paths.append("%s/%s/%s" % \
                        (svn_path, hack_path, release))

                cmd  = '/usr/bin/op create-hack %s ' % req.authname
                cmd += '"New hack %s, created by %s" ' % \
                        (page_name, req.authname)
                cmd += '%s 2>&1' % ' '.join(paths)
                output = popen(cmd).readlines()
                if output:
                    raise Exception(
                        "Failed to create Subversion paths:\n%s" % \
                            '\n'.join(output)
                        )
                steps_done.append('repository')

                # Step 2: Add permissions
                from svnauthz.model import User, Path, PathAcl

                authz_file = self.env.config.get('trac', 'authz_file')
                authz = AuthzFileReader().read(authz_file)

                svn_path_acl = PathAcl(User(req.authname), r=True, w=True)
                authz.add_path(Path("/%s" % hack_path, acls = [svn_path_acl,]))
                AuthzFileWriter().write(authz_file, authz)
                steps_done.append('permissions')

                # Step 3: Add component
                component = TicketComponent(self.env)
                component.name = page_name
                component.owner = req.authname
                component.insert()
                steps_done.append('component')

                # Step 4: Create wiki page
                template_page = WikiPage(self.env, self.template)
                page = WikiPage(self.env, page_name)
                page.text = Template(template_page.text).substitute(vars)
                page.save(req.authname, 'New hack %s, created by %s' % \
                          (page_name, req.authname), '0.0.0.0')
                steps_done.append('wiki')

                # Step 5: Tag the new wiki page
                res = Resource('wiki', page_name)
                tags = data['tags'].split() + selected_releases + [data['type']]
                TagSystem(self.env).set_tags(req, res, tags)
                steps_done.append('tags')

                rv = fcntl.flock(lockfile, fcntl.LOCK_UN)
                created = True
            except Exception, e:
                try:
                    if 'tags' in steps_done:
                        res = Resource('wiki', page_name)
                        tags = data['tags'].split() + selected_releases
                        TagSystem(self.env).delete_tags(req, res, tags)
                    if 'wiki' in steps_done:
                        WikiPage(self.env, page_name).delete()
                    if 'component' in steps_done:
                        TicketComponent(self.env, page_name).delete()
                    if 'permissions' in steps_done:
                        authz_file = self.env.config.get('trac', 'authz_file')
                        authz = AuthzFileReader().read(authz_file)
                        authz.del_path(Path("/%s" % hack_path))
                        AuthzFileWriter().write(authz_file, authz)
                    # TODO: rollback subversion path creation
                    rv = fcntl.flock(lockfile, fcntl.LOCK_UN)
                except:
                    self.env.log.error("Rollback failed")
                    rv = fcntl.flock(lockfile, fcntl.LOCK_UN)
                self.env.log.error(e, exc_info=True)
                raise TracError(str(e))
    def process_request_submit(self, req):
        if req.method != "POST":
            return self._error_response(req, status=HTTPMethodNotAllowed.code, body='Method %s not allowed' % req.method)

        manual_upload = req.args.as_int('manual_upload', 0)
        if manual_upload == 0:
            user_agent_full = req.get_header('User-Agent')
            if user_agent_full is None:
                return self._error_response(req, status=HTTPForbidden.code, body='No user-agent specified.')
            if '/' in user_agent_full:
                user_agent, agent_ver = user_agent_full.split('/', 1)
            else:
                user_agent = user_agent_full
            if user_agent != 'terra3d-crashuploader':
                return self._error_response(req, status=HTTPForbidden.code, body='User-agent %s not allowed' % user_agent_full)

        headers = {}
        headers['Max-Upload-Size'] = self.max_upload_size
        headers['Upload-Disabled'] = '1' if self.upload_disabled else '0'

        if self.upload_disabled:
            return self._error_response(req, status=HTTPInternalServerError.code, body='Crashdump upload has been disabled by the administrator.', headers=headers)

        id_str = req.args.get('id')
        if not manual_upload:
            if not id_str or not CrashDump.uuid_is_valid(id_str):
                return self._error_response(req, status=HTTPInternalServerError.code, body='Invalid crash identifier %s specified.' % id_str)

        total_upload_size = self._get_total_upload_size(req)
        if self.max_upload_size > 0 and total_upload_size > self.max_upload_size:
            self.log.debug('total_upload_size %i > max_upload_size %i' % (total_upload_size, self.max_upload_size) )
            return self._error_response(req, status=HTTPInternalServerError.code, body='Upload size %i bytes exceed the upload limit of %i bytes' % (total_upload_size, self.max_upload_size), headers=headers)
        else:
            self.log.debug('total_upload_size %i <= max_upload_size %i' % (total_upload_size, self.max_upload_size) )

        if manual_upload:
            self.log.debug('manual_upload')

            files = req.args.getlist('files')
            if len(files) == 0:
                return self._error_response(req, status=HTTPInternalServerError.code, body='No files uploaded.')

            import re

            id_str = None
            minidump = None
            minidumpreportxml = None

            for file in files:
                if isinstance(file, cgi.FieldStorage):
                    filename = os.path.basename(file.filename)
                    self.log.debug('got file %s' % filename)
                    match = re.match(r'^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\.([0-9a-zA-Z\.]+)$', filename)
                    if match:
                        new_id_str = match.groups()[0]
                        ext = match.groups()[1]
                        self.log.debug('got file match %s' % new_id_str)
                        if id_str is None:
                            id_str = new_id_str
                        elif id_str == new_id_str:
                            pass
                        else:
                            return self._error_response(req, status=HTTPInternalServerError.code, body='At the moment uploading multiples crashes is not supported.')
                        if ext == 'dmp':
                            minidump = file
                        elif ext == 'dmp.xml':
                            minidumpreportxml = file
                        self.log.debug('got id %s, ext %s' % (id_str, ext))
                else:
                    self.log.debug('skip file field %s-%s' % (type(file), file) )
            if not id_str:
                return self._manual_upload_result(req, error='Cannot determine crash identifier from file upload. The files uploaded must have a UUID in its name and the extentsion must either be .dmp or .dmp.xml.')
            elif minidump is None and minidumpreportxml is None:
                return self._manual_upload_result(req, error='Uploaded files do not contain a valid crash dump information.')

            self.log.debug('got crashid %s' % id_str)
            if minidump is not None:
                req.args['minidump'] = minidump
            if minidumpreportxml is not None:
                req.args['minidumpreportxml'] = minidumpreportxml

        uuid = UUID(id_str)
        crashid = None
        crashobj = CrashDump.find_by_uuid(self.env, uuid)
        if not crashobj:
            crashobj = CrashDump(uuid=uuid, env=self.env, must_exist=False)
        else:
            crashid = crashobj.id

        force_str = req.args.get('force') or 'false'
        force = True if force_str.lower() == 'true' else False
        if crashid is not None and not force and not manual_upload:
            headers = {}
            headers['Crash-URL'] = req.abs_href('crash', str(uuid))
            headers['CrashId'] = str(crashid)
            self.log.debug('crash %s already uploaded %s' % (uuid, headers['Crash-URL']) )
            return self._error_response(req, status=HTTPInternalServerError.code, body='Crash identifier %s already uploaded.' % id_str, headers=headers)

        ticket_str = req.args.get('ticket') or 'no'

        linked_tickets = set()
        ticketobjs = []
        new_ticket = None
        if ticket_str == 'no':
            pass
        elif '#' in ticket_str:
            ticket_ids = []
            for t in ticket_str.split(','):
                if t[0] == '#':
                    ticket_ids.append(int(t[1:]))
            ticketobjs = []
            for tkt_id in ticket_ids:
                try:
                    ticketobjs.append(Ticket(env=self.env, tkt_id=tkt_id))
                except ResourceNotFound:
                    return self._error_response(req, status=HTTPNotFound.code, body='Ticket %i not found. Cannot link crash %s to the requested ticket.' % (tkt_id, str(uuid)))

        elif ticket_str == 'auto':
            if crashid is None:
                new_ticket = Ticket(env=self.env)
                ticketobjs = [ new_ticket ]
            else:
                for tkt_id in crashobj.linked_tickets:
                    try:
                        ticketobjs.append( Ticket(env=self.env, tkt_id=tkt_id) )
                        break
                    except ResourceNotFound:
                        pass
                if len(ticketobjs) == 0:
                    new_ticket = Ticket(env=self.env)
                    ticketobjs = [ new_ticket ]
        elif ticket_str == 'new':
            new_ticket = Ticket(env=self.env)
            ticketobjs = [ new_ticket ]
        else:
            return self._error_response(req, status=HTTPInternalServerError.code, body='Unrecognized ticket string %s for crash %s.' % (ticket_str, str(uuid)))
        
        #print('ticket_str=%s' % ticket_str)
        #print('ticketobjs=%s' % str(ticketobjs))

        # we require at least one crash dump file (either minidump or coredump)
        # and any number of report files
        failure_message = None
        result = False
        ok, new_minidumpfile, errmsg = self._store_dump_file(uuid, req, 'minidump', force)
        if ok:
            result = True
        elif failure_message is None:
            failure_message = errmsg
        ok, new_minidumpreporttextfile, errmsg = self._store_dump_file(uuid, req, 'minidumpreport', force)
        ok, new_minidumpreportxmlfile, errmsg = self._store_dump_file(uuid, req, 'minidumpreportxml', force)
        # accept XML crash upload only for manual uploads
        if manual_upload and ok:
            result = True
        elif failure_message is None:
            failure_message = errmsg
        ok, new_minidumpreporthtmlfile, errmsg = self._store_dump_file(uuid, req, 'minidumpreporthtml', force)
        ok, new_coredumpfile, errmsg = self._store_dump_file(uuid, req, 'coredump', force)
        if ok:
            result = True
        elif failure_message is None:
            failure_message = errmsg
        ok, new_coredumpreporttextfile, errmsg = self._store_dump_file(uuid, req, 'coredumpreport', force)
        ok, new_coredumpreportxmlfile, errmsg = self._store_dump_file(uuid, req, 'coredumpreportxml', force)
        ok, new_coredumpreporthtmlfile, errmsg = self._store_dump_file(uuid, req, 'coredumpreporthtml', force)

        self.log.debug('new_minidumpfile \'%s\'' % new_minidumpfile)
        self.log.debug('new_minidumpreportxmlfile \'%s\'' % new_minidumpreportxmlfile)
        self.log.debug('before crashobj[minidumpfile] \'%s\'' % crashobj['minidumpfile'])
        self.log.debug('before crashobj[minidumpreportxmlfile] \'%s\'' % crashobj['minidumpreportxmlfile'])

        if manual_upload:
            if not crashobj['minidumpfile'] or force:
                crashobj['minidumpfile'] = new_minidumpfile
            if not crashobj['minidumpreporttextfile'] or force:
                crashobj['minidumpreporttextfile'] = new_minidumpreporttextfile
            if not crashobj['minidumpreportxmlfile'] or force:
                crashobj['minidumpreportxmlfile'] = new_minidumpreportxmlfile
            if not crashobj['minidumpreporthtmlfile'] or force:
                crashobj['minidumpreporthtmlfile'] = new_minidumpreporthtmlfile
            if not crashobj['coredumpfile'] or force:
                crashobj['coredumpfile'] = new_coredumpfile
            if not crashobj['coredumpreporttextfile'] or force:
                crashobj['coredumpreporttextfile'] = new_coredumpreporttextfile
            if not crashobj['coredumpreportxmlfile'] or force:
                crashobj['coredumpreportxmlfile'] = new_coredumpreportxmlfile
            if not crashobj['coredumpreporthtmlfile'] or force:
                crashobj['coredumpreporthtmlfile'] = new_coredumpreporthtmlfile
        else:
            crashobj['minidumpfile'] = new_minidumpfile
            crashobj['minidumpreporttextfile'] = new_minidumpreporttextfile
            crashobj['minidumpreportxmlfile'] = new_minidumpreportxmlfile
            crashobj['minidumpreporthtmlfile'] = new_minidumpreporthtmlfile
            crashobj['coredumpfile'] = new_coredumpfile
            crashobj['coredumpreporttextfile'] = new_coredumpreporttextfile
            crashobj['coredumpreportxmlfile'] = new_coredumpreportxmlfile
            crashobj['coredumpreporthtmlfile'] = new_coredumpreporthtmlfile

        self.log.debug('after crashobj[minidumpfile] \'%s\'' % crashobj['minidumpfile'])
        self.log.debug('after crashobj[minidumpreportxmlfile] \'%s\'' % crashobj['minidumpreportxmlfile'])

        new_applicationfile = req.args.get('applicationfile')
        if not crashobj['applicationfile']:
            crashobj['applicationfile'] = new_applicationfile

        self.log.debug('crashtimestamp from http form \'%s\'' % req.args.get('crashtimestamp'))
        self.log.debug('reporttimestamp from http form \'%s\'' % req.args.get('reporttimestamp'))

        try:
            crashtimestamp = parse_date(req.args.get('crashtimestamp', ''), hint='iso8601' )
        except TracError:
            crashtimestamp = None
            self.log.warn('invalid crash timestamp \'%s\'' % (req.args.get('crashtimestamp')))
        try:
            reporttimestamp = parse_date(req.args.get('reporttimestamp', ''), hint='iso8601' )
        except TracError:
            reporttimestamp = None
            self.log.warn('invalid crash report timestamp \'%s\'' % (req.args.get('reporttimestamp')))

        crashobj['crashtime'] = crashtimestamp if crashtimestamp else None
        crashobj['reporttime'] = reporttimestamp if reporttimestamp else None
        crashobj['uploadtime'] = datetime.datetime.now(utc)

        self.log.debug('crashtimestamp %s' % (crashobj['crashtime']))
        self.log.debug('reporttimestamp %s' % (crashobj['reporttime']))
        self.log.debug('uploadtime %s' % (crashobj['uploadtime']))

        if not manual_upload:
            crashobj['productname'] = req.args.get('productname')
            crashobj['productcodename'] = req.args.get('productcodename')
            crashobj['productversion'] = req.args.get('productversion')
            crashobj['producttargetversion'] = req.args.get('producttargetversion')
            crashobj['uploadhostname'] = req.args.get('fqdn')
            crashobj['uploadusername'] = req.args.get('username')
            crashobj['crashhostname'] = req.args.get('crashfqdn')
            crashobj['crashusername'] = req.args.get('crashusername')
            crashobj['buildtype'] = req.args.get('buildtype')
            crashobj['buildpostfix'] = req.args.get('buildpostfix')
            crashobj['machinetype'] = req.args.get('machinetype')
            crashobj['systemname'] = req.args.get('systemname')
            crashobj['osversion'] = req.args.get('osversion')
            crashobj['osrelease'] = req.args.get('osrelease')
            crashobj['osmachine'] = req.args.get('osmachine')

        if result:

            xmlreport = None
            try:
                if crashobj['minidumpreportxmlfile']:
                    xmlfile = self._get_dump_filename(crashobj, 'minidumpreportxmlfile')
                    xmlreport = XMLReport(xmlfile)
                elif crashobj['coredumpreportxmlfile']:
                    xmlfile = self._get_dump_filename(crashobj, 'coredumpreportxmlfile')
                    xmlreport = XMLReport(xmlfile)
            except XMLReport.XMLReportException as e:
                return self._error_response(req, status=HTTPInternalServerError.code, body='Failed to process crash dump %s: %s' % (uuid, str(e)))

            if xmlreport and manual_upload:

                if xmlreport.crash_info:
                    crashobj['crashtime'] = xmlreport.crash_info.crash_timestamp
                    crashobj['reporttime'] = xmlreport.crash_info.report_time
                    crashobj['uploadhostname'] = req.remote_addr
                    crashobj['uploadusername'] = req.remote_user
                    crashobj['applicationfile'] = xmlreport.crash_info.application
                if xmlreport.fast_protect_version_info:
                    crashobj['productname'] = xmlreport.fast_protect_version_info.product_name
                    crashobj['productcodename'] = xmlreport.fast_protect_version_info.product_code_name
                    crashobj['productversion'] = xmlreport.fast_protect_version_info.product_version
                    crashobj['producttargetversion'] = xmlreport.fast_protect_version_info.product_target_version
                    crashobj['buildtype'] = xmlreport.fast_protect_version_info.product_build_type
                    crashobj['buildpostfix'] = xmlreport.fast_protect_version_info.product_build_postfix

                if xmlreport.fast_protect_system_info:
                    crashobj['crashhostname'] = xmlreport.fast_protect_system_info.fqdn
                    crashobj['crashusername'] = xmlreport.fast_protect_system_info.username
                    crashobj['machinetype'] = xmlreport.fast_protect_system_info.machine_type

                if xmlreport.system_info:
                    crashobj['systemname'] = xmlreport.system_info.platform_type
                    crashobj['osversion'] = xmlreport.system_info.os_version
                    crashobj['osrelease'] = xmlreport.system_info.os_build_number
                    crashobj['osmachine'] = xmlreport.system_info.cpu_type

            # get the application name from the application file
            if crashobj['applicationfile']:
                appfile = crashobj['applicationfile']
                if '/' in appfile:
                    appbase = appfile.split('/')[-1]
                elif '\\' in appfile:
                    appbase = appfile.split('\\')[-1]
                else:
                    appbase = os.path.basename(appfile)
                (appbase, ext) = os.path.splitext(appbase)
                if crashobj['buildpostfix'] and appbase.endswith(crashobj['buildpostfix']):
                    appbase = appbase[:-len(crashobj['buildpostfix'])]
                crashobj['applicationname'] = appbase

            new_crash = True if crashid is None else False
            if new_crash:
                crashobj['status'] = 'new'
                crashobj['type'] = 'crash'
                crashobj['priority'] = self.default_priority
                if self.default_milestone == '< default >':
                    crashobj['milestone'] = self._find_milestone(crashobj['productversion'], crashobj['producttargetversion'])
                else:
                    crashobj['milestone'] = self.default_milestone
                if self.default_version == '< default >':
                    crashobj['version'] = self._find_version(crashobj['productversion'], crashobj['producttargetversion'])
                else:
                    crashobj['version'] = self.default_version
                if self.default_component == '< default >':
                    if xmlreport is not None and xmlreport.exception is not None and xmlreport.exception.involved_modules:
                        crashobj['component'] = self._find_component_from_involved_modules(xmlreport.exception.involved_modules, crashobj['buildpostfix'])
                    if not crashobj['component']:
                        crashobj['component'] = self._find_component_for_application(crashobj['applicationname'])
                else:
                    crashobj['component'] = self.default_component
                crashobj['severity'] = self.default_severity
                crashobj['summary'] = self.default_summary
                crashobj['description'] = self.default_description
                crashobj['keywords'] = self.default_keywords
                if self.default_owner == '< default >':
                    default_to_owner = ''
                    if crashobj['component']:
                        try:
                            component = TicketComponent(self.env, crashobj['component'])
                            default_to_owner = component.owner # even if it's empty
                        except ResourceNotFound:
                            # No such component exists
                            pass
                    if default_to_owner:
                        crashobj['owner'] = default_to_owner
                    else:
                        # If the current owner is "< default >", we need to set it to
                        # _something_ else, even if that something else is blank.
                        crashobj['owner'] = crashobj['crashusername']
                else:
                    crashobj['owner'] = self.default_owner
                if self.default_reporter == '< default >':
                    crashobj['reporter'] = crashobj['crashusername']
                else:
                    crashobj['reporter'] = self.default_reporter

                # apply replacements on usernames in owner and reporter field
                crashobj['owner'] = self._apply_username_replacements(crashobj['owner'])
                crashobj['reporter'] = self._apply_username_replacements(crashobj['reporter'])

                crashid = crashobj.insert()
                result = True if crashid else False
                if result:
                    if xmlreport is not None and xmlreport.exception is not None:
                        ex_thread = xmlreport.exception.thread
                    else:
                        ex_thread = None
                    if ex_thread is not None:
                        threadid = ex_thread.id
                        stackdump = ex_thread.simplified_stackdump if ex_thread.simplified_stackdump is not None else ex_thread.stackdump
                        if stackdump:
                            for frameno, frm in enumerate(stackdump.callstack):
                                frameobj = CrashDumpStackFrame(crashid, threadid,frameno, env=self.env)
                                frameobj['module'] = frm.module
                                frameobj['function'] = frm.function
                                frameobj['funcoff'] = frm.funcoff
                                frameobj['source'] = frm.source
                                frameobj['line'] = frm.line
                                frameobj['lineoff'] = frm.lineoff
                                frameobj.insert()


            else:
                #print('update crash %s' % crashobj)
                result = crashobj.save_changes(author=crashobj['crashusername'])

            if result:
                values = crashobj.values
                values['crashtimestamp'] = crashtimestamp
                values['reporttimestamp'] = reporttimestamp
                values['crashid'] = crashid
                values['uuid'] = crashobj.uuid
                values['app'] = crashobj['applicationname'] if crashobj['applicationname'] else crashobj['applicationfile']
                # Update all already linked tickets
                for tkt_id in crashobj.linked_tickets:
                    try:
                        new_linked_ticketobj = Ticket(env=self.env, tkt_id=tkt_id)
                        comment = """The crash [[/crash/%(uuid)s|CrashId#%(crashid)s - %(uuid)s]] has been updated by **%(uploadusername)s**
from **%(uploadhostname)s** is already linked to this ticket.
""" % values

                        new_linked_ticketobj.save_changes(author=crashobj['reporter'], comment=comment)
                        # Only add valid tickets to the linked_tickets set
                        linked_tickets.add(tkt_id)
                    except ResourceNotFound:
                        pass
                    
                if new_ticket is not None:
                    new_ticket['type'] = self.default_ticket_type
                    new_ticket['summary'] = "Crash %(uuid)s in %(app)s" % values
                    comment = """The crash [[/crash/%(uuid)s|CrashId#%(crashid)s - %(uuid)s]] has been uploaded by **%(uploadusername)s**
from **%(uploadhostname)s** and this ticket has been automatically created to track the progress in finding and resolving the cause of the crash.
""" % values
                    new_ticket['description'] = comment
                    # copy over some fields from the crash itself
                    for field in ['status', 'priority', 'milestone', 'component',
                                'severity', 'keywords']:
                        new_ticket[field] = crashobj[field]

                    # apply replacements on usernames in owner and reporter field
                    new_ticket['owner'] = self._apply_username_replacements(crashobj['owner'])
                    new_ticket['reporter'] = self._apply_username_replacements(crashobj['reporter'])

                    new_ticket['linked_crash'] = str(crashid)
                    new_ticket.insert()

                # Now add the newly linked tickets as well
                for tkt_obj in ticketobjs:
                    if tkt_obj.id not in crashobj.linked_tickets:
                        ticket_values = self.escape_ticket_values(values)
                        #self.log.debug('ticket_values=%s' % str(ticket_values))
                        comment = """The crash [[/crash/%(uuid)s|CrashId#%(crashid)s - %(uuid)s]] has been uploaded by **%(uploadusername)s**
from **%(uploadhostname)s** and linked to this ticket.

The crash occured at //%(crashtimestamp)s UTC// on **%(crashhostname)s** with user **%(crashusername)s** while running `%(applicationfile)s`. The
application was running as part of %(productname)s (%(productcodename)s) version %(productversion)s (%(producttargetversion)s, %(buildtype)s) on a
%(systemname)s/%(machinetype)s with %(osversion)s (%(osrelease)s/%(osmachine)s).
""" % ticket_values
                        linked_crashes = tkt_obj['linked_crash'] if tkt_obj['linked_crash'] else ''
                        linked_crashes = set([int(x.strip()) for x in linked_crashes.split(',') if x.strip()])
                        #print('crashid=%s' % crashid)
                        #print('linked_crashes=%s' % linked_crashes)
                        linked_crashes.add(crashid)
                        #print('linked_crashes=%s' % linked_crashes)
                        tkt_obj['linked_crash'] = ', '.join(str(x) for x in sorted(linked_crashes))
                        tkt_obj.save_changes(author=crashobj['reporter'], comment=comment)
                    
                        linked_tickets.add(tkt_obj.id)
                        with self.env.db_transaction as db:
                            links = CrashDumpTicketLinks(self.env, tkt=tkt_obj, db=db)
                            links.crashes.add(crashid)
                            links.save(author=crashobj['reporter'], db=db)

            if result:
                if manual_upload:
                    req.redirect(req.abs_href('crash', str(uuid)))
                else:
                    headers = {}
                    linked_ticket_header = []
                    for tkt_id in linked_tickets:
                        linked_ticket_header.append('#%i:%s' % (tkt_id, req.abs_href.ticket(tkt_id)))
                    if linked_ticket_header:
                        headers['Linked-Tickets'] = ';'.join(linked_ticket_header)
                    headers['Crash-URL'] = req.abs_href('crash', str(uuid))
                    headers['CrashId'] = str(crashid)

                    return self._success_response(req, body='Crash dump %s uploaded successfully.' % uuid, headers=headers)
            elif new_crash:
                return self._error_response(req, status=HTTPInternalServerError.code, body='Failed to add crash dump %s to database' % uuid)
            else:
                headers = {}
                headers['Crash-URL'] = req.abs_href('crash', str(uuid))
                headers['CrashId'] = str(crashid)
                return self._error_response(req, status=HTTPInternalServerError.code, body='Failed to update crash dump %s to database' % uuid, headers=headers)
        else:
            if failure_message is None:
                body = 'Failed to process crash dump %s' % uuid
            else:
                body = 'The following occured while processing the crash dump %s: %s' % (uuid, failure_message)
            return self._error_response(req, status=HTTPInternalServerError.code, body=body)