4 from qtpy
import QtCore
6 from qtpy
import QtWidgets
7 from qtpy
import QtNetwork
12 from .compat
import parse
13 from .models
import prefs
14 from .widgets
import defs
19 def url_for_email(email
, imgsize
):
20 email_hash
= md5_hexdigest(email
)
21 # Python2.6 requires byte strings for urllib2.quote() so we have
23 default_url
= 'https://git-cola.github.io/images/git-64x64.jpg'
24 encoded_url
= parse
.quote(core
.encode(default_url
), core
.encode(''))
25 query
= '?s=%d&d=%s' % (imgsize
, core
.decode(encoded_url
))
26 url
= 'https://gravatar.com/avatar/' + email_hash
+ query
30 def md5_hexdigest(value
):
31 """Return the md5 hexdigest for a value.
33 Used for implementing the gravatar API. Not used for security purposes.
35 # https://github.com/git-cola/git-cola/issues/1157
36 # ValueError: error:060800A3:
37 # digital envelope routines: EVP_DigestInit_ex: disabled for fips
39 # Newer versions of Python, including Centos8's patched Python3.6 and
40 # mainline Python 3.9+ have a "usedoforsecurity" parameter which allows us
41 # to continue using hashlib.md5().
42 encoded_value
= core
.encode(value
)
45 # This could raise ValueError in theory but we always use encoded bytes
46 # so that does not happen in practice.
47 # pylint: disable=unexpected-keyword-arg
48 result
= hashlib
.md5(encoded_value
, usedforsecurity
=False).hexdigest()
50 # Fallback to trying hashlib.md5 directly.
51 result
= hashlib
.md5(encoded_value
).hexdigest()
52 return core
.decode(result
)
55 class GravatarLabel(QtWidgets
.QLabel
):
56 def __init__(self
, context
, parent
=None):
57 QtWidgets
.QLabel
.__init
__(self
, parent
)
59 self
.context
= context
63 self
.imgsize
= defs
.medium_icon
65 self
._default
_pixmap
_bytes
= None
67 self
.network
= QtNetwork
.QNetworkAccessManager()
68 # pylint: disable=no-member
69 self
.network
.finished
.connect(self
.network_finished
)
71 def set_email(self
, email
):
72 """Update the author icon based on the specified email"""
73 pixmap
= self
.pixmaps
.get(email
, None)
74 if pixmap
is not None:
75 self
.setPixmap(pixmap
)
77 if self
.timeout
> 0 and (int(time
.time()) - self
.timeout
) < (5 * 60):
78 self
.set_pixmap_from_response()
80 if email
== self
.email
and self
.response
is not None:
81 self
.set_pixmap_from_response()
86 def request(self
, email
):
87 if prefs
.enable_gravatar(self
.context
):
88 url
= Gravatar
.url_for_email(email
, self
.imgsize
)
89 self
.network
.get(QtNetwork
.QNetworkRequest(QtCore
.QUrl(url
)))
91 self
.pixmaps
[email
] = self
.set_pixmap_from_response()
93 def default_pixmap_as_bytes(self
):
94 if self
._default
_pixmap
_bytes
is None:
96 pixmap
= icons
.cola().pixmap(xres
)
97 byte_array
= QtCore
.QByteArray()
98 buf
= QtCore
.QBuffer(byte_array
)
99 buf
.open(QtCore
.QIODevice
.WriteOnly
)
100 pixmap
.save(buf
, 'PNG')
102 self
._default
_pixmap
_bytes
= byte_array
104 byte_array
= self
._default
_pixmap
_bytes
107 def network_finished(self
, reply
):
110 header
= QtCore
.QByteArray(b
'Location')
111 location
= core
.decode(bytes(reply
.rawHeader(header
))).strip()
113 request_location
= Gravatar
.url_for_email(self
.email
, self
.imgsize
)
114 relocated
= location
!= request_location
117 no_error
= qtutils
.enum_value(
118 QtNetwork
.QNetworkReply
.NetworkError
.NoError
# pylint: disable=no-member
120 reply_error
= qtutils
.enum_value(reply
.error())
121 if reply_error
== no_error
:
123 # We could do get_url(parse.unquote(location)) to
124 # download the default image.
125 # Save bandwidth by using a pixmap.
126 self
.response
= self
.default_pixmap_as_bytes()
128 self
.response
= reply
.readAll()
131 self
.response
= self
.default_pixmap_as_bytes()
132 self
.timeout
= int(time
.time())
134 pixmap
= self
.set_pixmap_from_response()
136 # If the email has not changed (e.g. no other requests)
137 # then we know that this pixmap corresponds to this specific
138 # email address. We can't blindly trust self.email else
139 # we may add cache entries for thee wrong email address.
140 url
= Gravatar
.url_for_email(email
, self
.imgsize
)
141 if url
== reply
.url().toString():
142 self
.pixmaps
[email
] = pixmap
144 def set_pixmap_from_response(self
):
145 if self
.response
is None:
146 self
.response
= self
.default_pixmap_as_bytes()
147 pixmap
= QtGui
.QPixmap()
148 pixmap
.loadFromData(self
.response
)
149 self
.setPixmap(pixmap
)