0

Today I got scared after noticing how many Microsoft ATP Safelinks had made their way into my org-mode calendar file. Therefore, is it possible to create a URL decoder to bypass Microsoft Safelinks for mu4e(gnus)? There are implementations for other mail-clients in Python? and Javascript but elisp (if possible) would be preferable. I am using mu4e 1.4.1 and emacs 26.3. Thanks in advance.

Update:

Thanks wasamasa. To wire it up to mu4e I've developed a url reader (below), based on this answer.

2 Answers 2

1

I've ported the Python code you've linked:

(require 'url-parse)

(defun my-decode-safelink (url)
  (if (string-match-p (regexp-quote "safelinks.protection.outlook.com") url)
      (let* ((query (url-filename (url-generic-parse-url url)))
             (url (cadr (assoc "/?url" (url-parse-query-string query))))
             (path (url-unhex-string url)))
        (replace-regexp-in-string (rx "/" (>= 20 (any "#$%&*^@"))) "" path))
    url))

(my-decode-safelink "https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Foffice.memoriesflower.com%2FPermission%2F%2525%2524%255E%2526%2526*%2523%2523%255E%2524%2525%255E%2526%255E*%2526%2523%255E%2525%2525%2526%2540%255E*%2523%2526%255E%2525%2523%2526%2540%2525*%255E%2540%255E%2523%2525%255E%2540%2526%2525*%255E%2540%2Foffice.php&data=01%7C01%7Cdavid.levin%40mheducation.com%7C0ac9a3770fe64fbb21fb08d50764c401%7Cf919b1efc0c347358fca0928ec39d8d5%7C0&sdata=PEoDOerQnha%2FACafNx8JAep8O9MdllcKCsHET2Ye%2B4%3D&reserved=0")
;; "https://office.memoriesflower.com/Permission/office.php"

(my-decode-safelink "https://office.memoriesflower.com/Permission/office.php")

;; "https://office.memoriesflower.com/Permission/office.php"

That leaves the part of wiring it up to mu4e.

1

This is my answer, based on code from Wasamasa and Joseph Gay.

Post version 1.6.0~54

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;             Microsoft safelinks decoder                            ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'url-parse)
(defun my-decode-safelink (url)
  ;; (print url)
  "Given a url string this function returns the corresponding decoded url"
  (if (string-match-p (regexp-quote "safelinks.protection") url)
      (let* ((query (url-filename (url-generic-parse-url url)))
             (url (cadr (assoc ".*/?url" (url-parse-query-string query) (lambda (pat x) (string-match-p x pat)))))
             (path (replace-regexp-in-string "3Dhttps" "https" (url-unhex-string url))))
        (url-encode-url (replace-regexp-in-string (rx "/" (>= 20 (any "#$%&*^@"))) "" path)))
    url))

;; Main function
(defun unsafelinks ()
  "This function filters MS safelinks from a message buffer"
  (interactive)
  (let (url current-start-pos next-change-pos)
    (save-excursion
      (goto-char (point-min))
      (let ((inhibit-read-only t)
            (simple-url-regexp "https?://") urls)
        (search-forward-regexp "^$" nil t)
        (setq next-change-pos (or (next-single-property-change (point) 'shr-url)
                                  (point-max)))
        (goto-char next-change-pos)
    
        (while (< next-change-pos (point-max))
          (setq url               (get-text-property (point) 'shr-url)
                current-start-pos (point)
                next-change-pos   (or (next-single-property-change (point) 'shr-url)
                                      (point-max)))
          (when url
            (setq text (buffer-substring-no-properties current-start-pos (point)))
            ;; edit widget URLs
            (add-text-properties current-start-pos next-change-pos
                                 (list 'shr-url (my-decode-safelink url)
                                       'help-echo (my-decode-safelink url)))
            ;; edit text URLs
            (when-let ((link (thing-at-point 'url))
                       (bounds (thing-at-point-bounds-of-url-at-point)))
              (delete-region (car bounds) (cdr bounds))
              (insert (my-decode-safelink url)))
            )
          (goto-char next-change-pos))
        (remove-overlays)
        (mu4e~view-activate-urls)
        (set-buffer-modified-p nil)))))

(defun unsafelinks-advice (msg)
  (unsafelinks))
(advice-add #'mu4e~view-render-buffer :after #'unsafelinks-advice)

Previous to version 1.6.0~123

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Microsoft safelinks decoder
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'url-parse)
(defun my-decode-safelink (url)
  "Given a url string this function returns the corresponding decoded url"
  (if (string-match-p (regexp-quote "safelinks.protection") url)      
      (let* ((query (url-filename (url-generic-parse-url url)))
         (url (cadr (assoc "/?url" (url-parse-query-string query))))
         (path (url-unhex-string url)))
    (replace-regexp-in-string (rx "/" (>= 20 (any "#$%&*^@"))) "" path))
    url))

(defun unsafelinks (vmode)
  "This function filters MS safelinks from a message buffer"
  (interactive)
  (beginning-of-buffer)
  (let ((simple-url-regexp "https?://") urls)
    (save-excursion
      ;; edit text URLs
      (while (search-forward-regexp simple-url-regexp nil t)
    (when-let ((url (thing-at-point 'url))
           (bounds (thing-at-point-bounds-of-url-at-point)))
      (delete-region (car bounds) (cdr bounds))
      (insert (my-decode-safelink url))))
      ;; edit widget URLs
      (beginning-of-buffer)
      (while (not (eobp)) 
    (goto-char (next-overlay-change (point)))
    (when-let (link (get-text-property (point) 'shr-url))             
      (and (string-match simple-url-regexp link) 
           ;; change overlay url echo
           (when (overlay-put (car (overlays-at (point))) 'help-echo (my-decode-safelink link)) t)
           ;; change url text-properties 
           (add-text-properties (point) (next-overlay-change (point)) (list 'shr-url (my-decode-safelink link) 'help-echo (my-decode-safelink link))) 
           ))
    (goto-char (next-overlay-change (point))))
      (when vmode (gnus-article-prepare-display)
        (set-buffer-modified-p nil)
        (read-only-mode))))
    (mu4e-compose-goto-top))

;; Append it to mu4e view and compose mode hooks
(add-hook 'mu4e-view-mode-hook (lambda () (unsafelinks t)) t)
(add-hook 'mu4e-compose-mode-hook (lambda () (unsafelinks nil)) t)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.