0

I want to use url-file-exists-p (or more precisely, the underlying url-https-file-exists-p) to detect possible broken links. However, some https link makes url-file-exists-p require interaction (This even happens when url-file-exists-p is called programmatically):

  1. when the TLS connection is insecure: a buffer named *Network Security Manager* pop up to show certificate information and a prompt is waiting for input as below; enter image description here (below is the result of the help). enter image description here
  2. ask for user-name and password.

I'm looking for a way to

  1. prevent the buffer *Network Security Manager* pop up
  2. send input to the prompt whenever is needed
  3. return different values/symbols depending on the context
    • t: secure connection, no prompt showed up
    • nil: connection failure
    • TLS: 'always, 'session and 'no: depending on the input (2.) sent whenever a prompt requires one
    • username/password: 'ignored if username/password are not sent to the prompt causing failure.
Firmin Martin
  • 1,265
  • 7
  • 23

1 Answers1

0

I list below several attempts I tried.

  1. Associate display-buffer-no-window to the buffer *Network Security Manager*. This prevent the buffer to pop up, and since url-file-exists-p fails to receive input, an error is throw. Finally, intepret the "pop up" as 'no.
(let ((display-buffer-alist
     (cons display-buffer-alist
           (cons "\\*Network Security Manager\\*"
             (cons #'display-buffer-no-window nil))))) 
    (condition-case nil
    (url-file-exists-p url)
      (error 'no)))
  1. Bound (minibuffer-message-timeout 0) in a let-in form.

For whatever reason, the two first attempts fail to deal with the prompt asking for authentification. By inspecting the source code of url-* packages, I found a workaround:

Send a GET request instead of a HEAD request as url-http-file-exists-p do (url-http-head).

  1. Strangely, the website requiring authentification doesn't require one when is a GET request in contrast to HEAD request. Obviously, this is just a workaround as a GET request may take much more resource than a HEAD one.
  (defun my/url-http-file-exists-p (url)
    (let ((buffer (url-retrieve-synchronously url t t)))
      (when buffer
    (let ((status (url-http-symbol-value-in-buffer 'url-http-response-status
                               buffer
                               500)))
      (prog1
          (and (integerp status)
         (>= status 200) (< status 300))
        (kill-buffer buffer))))))
  1. Use the auto-answer package where it is illustrated in this answer. To do this, we need to enhance it so that it supports read-multiple-choice (the function called when there is an insecure TLS connection).
(defun rmc-auto-answer (oldfun &rest args)
  (let ((prompt (car args)))
    (let*
        ((matcher-answer (and (stringp prompt)
                              (--first (string-match (car it) prompt) auto-answer)))
         (dontask (and matcher-answer (cdr matcher-answer))))
      (if dontask
          (--first (eq (car it) dontask) (cadr args))
        (apply oldfun args)))))

(advice-add #'read-multiple-choice :around #'rmc-auto-answer)

Finally, by binding the variable auto-answer, we can feed the input automatically whenever there is an insecure TLS connection asking for input

(let ((auto-answer (list (cons "Continue connecting\\?" ?s))))
             (url-http-file-exists-p url))

However, the authentification prompt occurs in an endless loop which means that this attempt is not sufficient (even if we can auto-answer username/password).

  1. TODO Use Request.el instead of url.el.
Firmin Martin
  • 1,265
  • 7
  • 23