blob: 40db170d72ebb482ab676ec8073fc38f5d2a2f39 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file or at
4# https://developers.google.com/open-source/licenses/bsd
5
6"""Servlet to safely display textual issue attachments.
7
8Unlike most attachments, this is not a download, it is a full HTML page
9with safely escaped user content.
10"""
11from __future__ import print_function
12from __future__ import division
13from __future__ import absolute_import
14
15import logging
16
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
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020023from framework import flaskservlet
Copybara854996b2021-09-07 19:36:02 +000024from framework import filecontent
25from framework import permissions
26from framework import servlet
27from framework import template_helpers
28from tracker import attachment_helpers
29from tracker import tracker_bizobj
30from tracker import tracker_helpers
31
32
33class AttachmentText(servlet.Servlet):
34 """AttachmentText displays textual attachments much like source browsing."""
35
36 _PAGE_TEMPLATE = 'tracker/issue-attachment-text.ezt'
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020037 _MAIN_TAB_MODE = flaskservlet.FlaskServlet.MAIN_TAB_ISSUES
Copybara854996b2021-09-07 19:36:02 +000038
39 def GatherPageData(self, mr):
40 """Parse the attachment ID from the request and serve its content.
41
42 Args:
43 mr: commonly used info parsed from the request.
44
45 Returns:
46 Dict of values used by EZT for rendering almost the page.
47 """
48 with mr.profiler.Phase('get issue, comment, and attachment'):
49 try:
50 attachment, issue = tracker_helpers.GetAttachmentIfAllowed(
51 mr, self.services)
52 except exceptions.NoSuchIssueException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020053 self.abort(404, 'issue not found')
Copybara854996b2021-09-07 19:36:02 +000054 except exceptions.NoSuchAttachmentException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020055 self.abort(404, 'attachment not found')
Copybara854996b2021-09-07 19:36:02 +000056 except exceptions.NoSuchCommentException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020057 self.abort(404, 'comment not found')
Copybara854996b2021-09-07 19:36:02 +000058
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020059 content = b''
Copybara854996b2021-09-07 19:36:02 +000060 if attachment.gcs_object_id:
61 bucket_name = app_identity.get_default_gcs_bucket_name()
62 full_path = '/' + bucket_name + attachment.gcs_object_id
63 logging.info("reading gcs: %s" % full_path)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020064
65 # Strip leading slash from object ID for backwards compatibility.
66 blob_name = attachment.gcs_object_id
67 if blob_name.startswith('/'):
68 blob_name = blob_name[1:]
69
70 client = storage.Client()
71 bucket = client.get_bucket(bucket_name)
72 blob = bucket.get_blob(blob_name)
73 content = blob.download_as_bytes()
Copybara854996b2021-09-07 19:36:02 +000074
75 filesize = len(content)
76
77 # This servlet only displays safe textual attachments. The user should
78 # not have been given a link to this servlet for any other kind.
79 if not attachment_helpers.IsViewableText(attachment.mimetype, filesize):
80 self.abort(400, 'not a text file')
81
82 u_text, is_binary, too_large = filecontent.DecodeFileContents(content)
83 lines = prettify.PrepareSourceLinesForHighlighting(u_text.encode('utf8'))
84
85 config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
86 granted_perms = tracker_bizobj.GetGrantedPerms(
87 issue, mr.auth.effective_ids, config)
88 page_perms = self.MakePagePerms(
89 mr, issue, permissions.DELETE_ISSUE, permissions.CREATE_ISSUE,
90 granted_perms=granted_perms)
91
92 page_data = {
93 'issue_tab_mode': 'issueDetail',
94 'local_id': issue.local_id,
95 'filename': attachment.filename,
96 'filesize': template_helpers.BytesKbOrMb(filesize),
97 'file_lines': lines,
98 'is_binary': ezt.boolean(is_binary),
99 'too_large': ezt.boolean(too_large),
100 'code_reviews': None,
101 'page_perms': page_perms,
102 }
103 if is_binary or too_large:
104 page_data['should_prettify'] = ezt.boolean(False)
105 else:
106 page_data.update(prettify.BuildPrettifyData(
107 len(lines), attachment.filename))
108
109 return page_data
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200110
111 # def GetAttachmentText(self, **kwargs):
112 # return self.handler(**kwargs)