1# Copyright 2014 The Distro Tracker Developers
2# See the COPYRIGHT file at the top-level directory of this distribution and
3# at https://deb.li/DTAuthors
4#
5# This file is part of Distro Tracker. It is subject to the license terms
6# in the LICENSE file found in the top-level directory of this
7# distribution and at https://deb.li/DTLicense. No part of Distro Tracker,
8# including this file, may be copied, modified, propagated, or distributed
9# except according to the terms contained in the LICENSE file.
11"""
12Module including some utility functions to inject links in plain text.
13"""
14import re
16from django.conf import settings
18from distro_tracker.core.utils.plugins import PluginRegistry
21class Linkify(metaclass=PluginRegistry):
22 """
23 A base class representing ways to inject useful links in plain text data
25 If you want to recognize a new syntax where links could provide value to
26 a view of the content, just create a subclass and implement the linkify
27 method.
28 """
30 @staticmethod
31 def linkify(text):
32 """
33 :param text: the text where we should inject HTML links
34 :type param: str
35 :returns: the text formatted with HTML links
36 :rtype: str
37 """
38 return text
41class LinkifyHttpLinks(Linkify):
42 """
43 Detect http:// and https:// URLs and transform them in true HTML
44 links.
45 """
47 @staticmethod
48 def linkify(text):
49 return re.sub(r'(?:^|(?<=\s))(https?://[^\s]*)',
50 r'<a href="\1">\1</a>',
51 text)
54class LinkifyDebianBugLinks(Linkify):
55 """
56 Detect "Closes: #123, 234" syntax used in Debian changelogs to close
57 bugs and inject HTML links to the corresponding bug tracker entry.
58 Also handles the "Closes: 123 456" fields of .changes files.
59 """
61 close_prefix = 'Closes:'
62 close_field = 'Closes:'
63 bug_url = 'https://bugs.debian.org/'
65 @classmethod
66 def _linkify_field(cls, text):
67 if not cls.close_field: 67 ↛ 68line 67 didn't jump to line 68, because the condition on line 67 was never true
68 return text
69 split_text = re.split(
70 '(^' + cls.close_field + r'(?: \d+)+\s*$)',
71 text, flags=re.IGNORECASE | re.MULTILINE)
72 generated_link = ''
73 for i, txt in enumerate(split_text):
74 if i % 2:
75 new_txt = re.sub(
76 r'(\d+)', r'<a href="{}\1">\1</a>'.format(cls.bug_url),
77 txt, flags=re.IGNORECASE)
78 generated_link += new_txt
79 else:
80 generated_link += txt
81 return generated_link
83 @classmethod
84 def _linkify_changelog_entry(cls, text):
85 split_text = re.split(
86 '(' + cls.close_prefix +
87 r'\s*(?:bug)?(?:#)?\d+(?:\s*,\s*(?:bug)?(?:#)?\d+)*)',
88 text, flags=re.IGNORECASE)
89 generated_link = ''
90 for i, txt in enumerate(split_text):
91 if i % 2:
92 new_txt = re.sub(
93 r'((?:#)?(\d+))',
94 r'<a href="{}\2">\1</a>'.format(cls.bug_url),
95 txt, flags=re.IGNORECASE)
96 generated_link += new_txt
97 else:
98 generated_link += txt
99 return generated_link
101 @classmethod
102 def linkify(cls, text):
103 return cls._linkify_changelog_entry(cls._linkify_field(text))
106class LinkifyUbuntuBugLinks(LinkifyDebianBugLinks):
107 """
108 Detect "LP: #123, 234" syntax used in Ubuntu changelogs to close
109 bugs and inject HTML links to the corresponding bug tracker entry.
110 """
112 close_prefix = 'LP:'
113 close_field = 'Launchpad-Bugs-Fixed:'
114 bug_url = 'https://bugs.launchpad.net/bugs/'
117class LinkifyCVELinks(Linkify):
118 """
119 Detect "CVE-2014-1234" words and transform them into links to the
120 CVE tracker at cve.mitre.org. The exact URL can be overridden with a
121 ``DISTRO_TRACKER_CVE_URL`` configuration setting to redirect
122 the URL to a custom tracker.
123 """
125 @staticmethod
126 def linkify(text):
127 address = getattr(settings, 'DISTRO_TRACKER_CVE_URL',
128 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=')
129 return re.sub(r'(?:(?<=\s|\()|\A)((CVE)-(\d{4})-(\d{4,}))',
130 r'<a href="{}\1">\1</a>'.format(address),
131 text, flags=re.IGNORECASE)
134def linkify(message):
135 """
136 :param message: the message where we should inject HTML links
137 :type param: str
138 :returns: the message formatted with HTML links
139 :rtype: str
140 """
141 for plugin in Linkify.plugins:
142 message = plugin.linkify(message)
143 return message