blob: 6554447a619799d0b03f9f11c86cefa483513ed5 [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"""Issue Tracker code to serve out issue attachments.
6
7Summary of page classes:
8 AttachmentPage: Serve the content of an attachment w/ the appropriate
9 MIME type.
10 IssueAttachmentDeletion: Form handler for deleting attachments.
11"""
12from __future__ import print_function
13from __future__ import division
14from __future__ import absolute_import
15
Copybara854996b2021-09-07 19:36:02 +000016import logging
Copybara854996b2021-09-07 19:36:02 +000017
18from google.appengine.api import app_identity
Copybara854996b2021-09-07 19:36:02 +000019
20from framework import exceptions
21from framework import framework_constants
Copybara854996b2021-09-07 19:36:02 +000022from framework import gcs_helpers
Copybara854996b2021-09-07 19:36:02 +000023from framework import servlet
Copybara854996b2021-09-07 19:36:02 +000024from tracker import attachment_helpers
25from tracker import tracker_helpers
Copybara854996b2021-09-07 19:36:02 +000026
27
28# This will likely appear blank or as a broken image icon in the browser.
29NO_PREVIEW_ICON = ''
30NO_PREVIEW_MIME_TYPE = 'image/png'
31
32
33class AttachmentPage(servlet.Servlet):
34 """AttachmentPage serves issue attachments."""
35
36 def GatherPageData(self, mr):
37 """Parse the attachment ID from the request and serve its content.
38
39 Args:
40 mr: commonly used info parsed from the request.
41
42 Returns: dict of values used by EZT for rendering the page.
43 """
44 if mr.signed_aid != attachment_helpers.SignAttachmentID(mr.aid):
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020045 self.abort(400, 'Please reload the issue page')
Copybara854996b2021-09-07 19:36:02 +000046
47 try:
48 attachment, _issue = tracker_helpers.GetAttachmentIfAllowed(
49 mr, self.services)
50 except exceptions.NoSuchIssueException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020051 self.abort(404, 'issue not found')
Copybara854996b2021-09-07 19:36:02 +000052 except exceptions.NoSuchAttachmentException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020053 self.abort(404, 'attachment not found')
Copybara854996b2021-09-07 19:36:02 +000054 except exceptions.NoSuchCommentException:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020055 self.abort(404, 'comment not found')
Copybara854996b2021-09-07 19:36:02 +000056
57 if not attachment.gcs_object_id:
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020058 self.abort(404, 'attachment data not found')
Copybara854996b2021-09-07 19:36:02 +000059
60 bucket_name = app_identity.get_default_gcs_bucket_name()
61
62 gcs_object_id = attachment.gcs_object_id
63
64 logging.info('attachment id %d is %s', mr.aid, gcs_object_id)
65
66 # By default GCS will return images and attachments displayable inline.
67 if mr.thumb:
68 # Thumbnails are stored in a separate obj always displayed inline.
69 gcs_object_id = gcs_object_id + '-thumbnail'
70 elif not mr.inline:
71 # Downloads are stored in a separate obj with disposiiton set.
72 filename = attachment.filename
73 if not framework_constants.FILENAME_RE.match(filename):
74 logging.info('bad file name: %s' % attachment.attachment_id)
75 filename = 'attachment-%d.dat' % attachment.attachment_id
76 if gcs_helpers.MaybeCreateDownload(
77 bucket_name, gcs_object_id, filename):
78 gcs_object_id = gcs_object_id + '-download'
79
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010080 redirect_url = gcs_helpers.SignUrl(bucket_name, gcs_object_id)
81 raise exceptions.RedirectException(redirect_url)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020082
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010083 def GetAttachmentPage(self, **kwargs):
84 return self.handler(**kwargs)