aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/image
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/image')
-rw-r--r--lisp/image/gravatar.el111
1 files changed, 89 insertions, 22 deletions
diff --git a/lisp/image/gravatar.el b/lisp/image/gravatar.el
index d1091e57cb5..3543be6de91 100644
--- a/lisp/image/gravatar.el
+++ b/lisp/image/gravatar.el
@@ -39,6 +39,7 @@
39 "Whether to cache retrieved gravatars." 39 "Whether to cache retrieved gravatars."
40 :type 'boolean 40 :type 'boolean
41 :group 'gravatar) 41 :group 'gravatar)
42(make-obsolete-variable 'gravatar-automatic-caching nil "28.1")
42 43
43(defcustom gravatar-cache-ttl 2592000 44(defcustom gravatar-cache-ttl 2592000
44 "Time to live in seconds for gravatar cache entries. 45 "Time to live in seconds for gravatar cache entries.
@@ -48,6 +49,7 @@ is retrieved anew. The default value is 30 days."
48 ;; Restricted :type to number of seconds. 49 ;; Restricted :type to number of seconds.
49 :version "27.1" 50 :version "27.1"
50 :group 'gravatar) 51 :group 'gravatar)
52(make-obsolete-variable 'gravatar-cache-ttl nil "28.1")
51 53
52(defcustom gravatar-rating "g" 54(defcustom gravatar-rating "g"
53 "Most explicit Gravatar rating level to allow. 55 "Most explicit Gravatar rating level to allow.
@@ -156,18 +158,58 @@ to track whether you're reading a specific mail."
156 (setq func 158 (setq func
157 (lambda (result) 159 (lambda (result)
158 (cond 160 (cond
159 (result 161 ((and
160 (funcall callback (format "%s://%s/avatar" 162 result ;there is a result
161 (cdar records) result))) 163 (let* ((data (mapcar (lambda (record)
162 ((> (length records) 1) 164 (dns-get 'data (cdr record)))
163 (pop records) 165 (dns-get 'answers result)))
166 (priorities (mapcar (lambda (r)
167 (dns-get 'priority r))
168 data))
169 (max-priority (if priorities
170 (apply #'max priorities)
171 0))
172 (sum 0) top)
173 ;; Attempt to find all records with the same maximal
174 ;; priority, and calculate the sum of their weights.
175 (dolist (ent data)
176 (when (= max-priority (dns-get 'priority ent))
177 (setq sum (+ sum (dns-get 'weight ent)))
178 (push ent top)))
179 ;; In case there is more than one maximal priority
180 ;; record, choose one at random, while taking the
181 ;; individual record weights into consideration.
182 (catch 'done
183 (dolist (ent top)
184 (when (and (or (= 0 sum)
185 (<= 0 (random sum)
186 (dns-get 'weight ent)))
187 ;; Ensure that port and domain data are
188 ;; valid. In case non of the results
189 ;; were valid, `catch' will evaluate to
190 ;; nil, and the next cond clause will be
191 ;; tested.
192 (<= 1 (dns-get 'port ent) 65535)
193 (string-match-p "\\`[-.0-9A-Za-z]+\\'"
194 (dns-get 'target ent)))
195 (funcall callback
196 (url-normalize-url
197 (format "%s://%s:%s/avatar"
198 (cdar records)
199 (dns-get 'target ent)
200 (dns-get 'port ent))))
201 (throw 'done t))
202 (setq sum (- sum (dns-get 'weight ent))))))))
203 ((setq records (cdr records))
204 ;; In case there are at least two methods.
164 (dns-query-asynchronous 205 (dns-query-asynchronous
165 (concat (caar records) "._tcp." domain) 206 (concat (caar records) "._tcp." domain)
166 func 'SRV)) 207 func 'SRV))
167 (t 208 (t ;fallback
168 (funcall callback "https://seccdn.libravatar.org/avatar"))))) 209 (funcall callback "https://seccdn.libravatar.org/avatar")))))
169 (dns-query-asynchronous 210 (dns-query-asynchronous
170 (concat (caar records) "._tcp." domain) func 'SRV))))) 211 (concat (caar records) "._tcp." domain)
212 func 'SRV t)))))
171 213
172(defun gravatar-hash (mail-address) 214(defun gravatar-hash (mail-address)
173 "Return the Gravatar hash for MAIL-ADDRESS." 215 "Return the Gravatar hash for MAIL-ADDRESS."
@@ -206,19 +248,50 @@ to track whether you're reading a specific mail."
206 (search-forward "\n\n" nil t) 248 (search-forward "\n\n" nil t)
207 (buffer-substring (point) (point-max))))) 249 (buffer-substring (point) (point-max)))))
208 250
251(defvar gravatar--cache (make-hash-table :test 'equal)
252 "Cache for gravatars.")
253
209;;;###autoload 254;;;###autoload
210(defun gravatar-retrieve (mail-address callback &optional cbargs) 255(defun gravatar-retrieve (mail-address callback &optional cbargs)
211 "Asynchronously retrieve a gravatar for MAIL-ADDRESS. 256 "Asynchronously retrieve a gravatar for MAIL-ADDRESS.
212When finished, call CALLBACK as (apply CALLBACK GRAVATAR CBARGS), 257When finished, call CALLBACK as (apply CALLBACK GRAVATAR CBARGS),
213where GRAVATAR is either an image descriptor, or the symbol 258where GRAVATAR is either an image descriptor, or the symbol
214`error' if the retrieval failed." 259`error' if the retrieval failed."
215 (gravatar-build-url 260 (let ((cached (gethash mail-address gravatar--cache)))
216 mail-address 261 (gravatar--prune-cache)
217 (lambda (url) 262 (if cached
218 (if (url-cache-expired url gravatar-cache-ttl) 263 (apply callback (cdr cached) cbargs)
219 (url-retrieve url #'gravatar-retrieved (list callback cbargs) t) 264 ;; Nothing in the cache, fetch it.
220 (with-current-buffer (url-fetch-from-cache url) 265 (gravatar-build-url
221 (gravatar-retrieved () callback cbargs)))))) 266 mail-address
267 (lambda (url)
268 (url-retrieve
269 url
270 (lambda (status)
271 (let* ((data (and (not (plist-get status :error))
272 (gravatar-get-data)))
273 (image (and data (create-image data nil t))))
274 ;; Store the image in the cache.
275 (when image
276 (setf (gethash mail-address gravatar--cache)
277 (cons (time-convert (current-time) 'integer)
278 image)))
279 (prog1
280 (apply callback (if data image 'error) cbargs)
281 (kill-buffer))))
282 nil t))))))
283
284(defun gravatar--prune-cache ()
285 (let ((expired nil)
286 (time (- (time-convert (current-time) 'integer)
287 ;; Twelve hours.
288 (* 12 60 60))))
289 (maphash (lambda (key val)
290 (when (< (car val) time)
291 (push key expired)))
292 gravatar--cache)
293 (dolist (key expired)
294 (remhash key gravatar--cache))))
222 295
223;;;###autoload 296;;;###autoload
224(defun gravatar-retrieve-synchronously (mail-address) 297(defun gravatar-retrieve-synchronously (mail-address)
@@ -229,10 +302,8 @@ retrieval failed."
229 (gravatar-build-url mail-address (lambda (u) (setq url u))) 302 (gravatar-build-url mail-address (lambda (u) (setq url u)))
230 (while (not url) 303 (while (not url)
231 (sleep-for 0.01)) 304 (sleep-for 0.01))
232 (with-current-buffer (if (url-cache-expired url gravatar-cache-ttl) 305 (with-current-buffer (url-retrieve-synchronously url t)
233 (url-retrieve-synchronously url t) 306 (gravatar-retrieved nil #'identity))))
234 (url-fetch-from-cache url))
235 (gravatar-retrieved () #'identity))))
236 307
237(defun gravatar-retrieved (status cb &optional cbargs) 308(defun gravatar-retrieved (status cb &optional cbargs)
238 "Handle Gravatar response data in current buffer. 309 "Handle Gravatar response data in current buffer.
@@ -241,10 +312,6 @@ an image descriptor, or the symbol `error' on failure.
241This function is intended as a callback for `url-retrieve'." 312This function is intended as a callback for `url-retrieve'."
242 (let ((data (unless (plist-get status :error) 313 (let ((data (unless (plist-get status :error)
243 (gravatar-get-data)))) 314 (gravatar-get-data))))
244 (and data ; Only cache on success.
245 url-current-object ; Only cache if not already cached.
246 gravatar-automatic-caching
247 (url-store-in-cache))
248 (prog1 (apply cb (if data (create-image data nil t) 'error) cbargs) 315 (prog1 (apply cb (if data (create-image data nil t) 'error) cbargs)
249 (kill-buffer)))) 316 (kill-buffer))))
250 317