blob: c12d135270c95bde0557895c14cb0394c0b80df6 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2016 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""Servlet to safely display textual issue attachments.
6
7Unlike most attachments, this is not a download, it is a full HTML page
8with safely escaped user content.
9"""
10from __future__ import print_function
11from __future__ import division
12from __future__ import absolute_import
13
14import logging
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010015import six
Copybara854996b2021-09-07 19:36:02 +000016
Copybara854996b2021-09-07 19:36:02 +000017import ezt
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020018from google.appengine.api import app_identity
19from google.cloud import storage
Copybara854996b2021-09-07 19:36:02 +000020
21from features import prettify
22from framework import exceptions
23from framework import filecontent
24from framework import permissions
25from framework import servlet
26from framework import template_helpers
27from tracker import attachment_helpers
28from tracker import tracker_bizobj
29from tracker import tracker_helpers
30
31
32class AttachmentText(servlet.Servlet):
33 """AttachmentText displays textual attachments much like source browsing."""
34
35 _PAGE_TEMPLATE = 'tracker/issue-attachment-text.ezt'
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010036 _MAIN_TAB_MODE = servlet.Servlet.MAIN_TAB_ISSUES
Copybara854996b2021-09-07 19:36:02 +000037
38 def GatherPageData(self, mr):
39 """Parse the attachment ID from the request and serve its content.
40
41 Args:
42 mr: commonly used info parsed from the request.
43
44 Returns:
45 Dict of values used by EZT for rendering almost the page.
46 """
47 with mr.profiler.Phase('get issue, comment, and attachment'):
48 try:
49 attachment, issue = tracker_helpers.GetAttachmentIfAllowed(
50 mr, self.services)
51 except exceptions.NoSuchIssueException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020052 self.abort(404, 'issue not found')
Copybara854996b2021-09-07 19:36:02 +000053 except exceptions.NoSuchAttachmentException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020054 self.abort(404, 'attachment not found')
Copybara854996b2021-09-07 19:36:02 +000055 except exceptions.NoSuchCommentException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020056 self.abort(404, 'comment not found')
Copybara854996b2021-09-07 19:36:02 +000057
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020058 content = b''
Copybara854996b2021-09-07 19:36:02 +000059 if attachment.gcs_object_id:
60 bucket_name = app_identity.get_default_gcs_bucket_name()
61 full_path = '/' + bucket_name + attachment.gcs_object_id
62 logging.info("reading gcs: %s" % full_path)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020063
64 # Strip leading slash from object ID for backwards compatibility.
65 blob_name = attachment.gcs_object_id
66 if blob_name.startswith('/'):
67 blob_name = blob_name[1:]
68
69 client = storage.Client()
70 bucket = client.get_bucket(bucket_name)
71 blob = bucket.get_blob(blob_name)
72 content = blob.download_as_bytes()
Copybara854996b2021-09-07 19:36:02 +000073
74 filesize = len(content)
75
76 # This servlet only displays safe textual attachments. The user should
77 # not have been given a link to this servlet for any other kind.
78 if not attachment_helpers.IsViewableText(attachment.mimetype, filesize):
79 self.abort(400, 'not a text file')
80
81 u_text, is_binary, too_large = filecontent.DecodeFileContents(content)
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010082 lines = prettify.PrepareSourceLinesForHighlighting(six.ensure_str(u_text))
Copybara854996b2021-09-07 19:36:02 +000083
84 config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
85 granted_perms = tracker_bizobj.GetGrantedPerms(
86 issue, mr.auth.effective_ids, config)
87 page_perms = self.MakePagePerms(
88 mr, issue, permissions.DELETE_ISSUE, permissions.CREATE_ISSUE,
89 granted_perms=granted_perms)
90
91 page_data = {
92 'issue_tab_mode': 'issueDetail',
93 'local_id': issue.local_id,
94 'filename': attachment.filename,
95 'filesize': template_helpers.BytesKbOrMb(filesize),
96 'file_lines': lines,
97 'is_binary': ezt.boolean(is_binary),
98 'too_large': ezt.boolean(too_large),
99 'code_reviews': None,
100 'page_perms': page_perms,
101 }
102 if is_binary or too_large:
103 page_data['should_prettify'] = ezt.boolean(False)
104 else:
105 page_data.update(prettify.BuildPrettifyData(
106 len(lines), attachment.filename))
107
108 return page_data
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200109
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100110 def GetAttachmentText(self, **kwargs):
111 return self.handler(**kwargs)