Emacs 26.1: secret-search-items bug: dbus-call-method: (wrong-type-argument consp "host") [workaround]

Bug has been reported and fixed.

My emacs version:

GNU Emacs 26.1

My smtpmail is configured to use secrets, resulting in the following call:

(secrets-search-items "Default" :host "myhost" :port "587" :user
"myuser")

Since the 26.1 upgrade this resulted in:

Debugger entered--Lisp error: (wrong-type-argument consp "host")
dbus-message-internal(1 :session "org.freedesktop.secrets"
"/org/freedesktop/secrets/collection/Default"
"org.freedesktop.Secret.Collection" "SearchItems"
dbus-call-method-handler (:array :dict-entry "host" "myhost" :dict-entry
"port" "587" :dict-entry "user" "myuser")) apply(dbus-message-internal 1
:session "org.freedesktop.secrets"
"/org/freedesktop/secrets/collection/Default"
"org.freedesktop.Secret.Collection" "SearchItems"
dbus-call-method-handler (:array :dict-entry "host" "myhost" :dict-entry
"port" "587" :dict-entry "user" "myuser")) dbus-call-method(:session
"org.freedesktop.secrets" "/org/freedesktop/secrets/collection/Default"
"org.freedesktop.Secret.Collection" "SearchItems" (:array :dict-entry
"host" "myhost" :dict-entry "port" "587" :dict-entry "user" "myuser"))
secrets-search-items("Default" :host "myhost" :port "587" :user
"myuser")

dbus-call-method expects arrays with dict-entries to provide a cons:

A dictionary entry must be element of an array, and it must contain only a key-value pair of two elements, with a basic D-Bus type key.

So in this example, dbus-call-method expects

'(:array :dict-entry ("host" "myhost"))

rather than

'(:array :dict-entry "host" "myhost")

secret-search-items builds this list incorrectly:

(defun secrets-search-items (collection &rest attributes)
  "Search items in COLLECTION with ATTRIBUTES.
ATTRIBUTES are key-value pairs.  The keys are keyword symbols,
starting with a colon.  Example:

  (secrets-search-items \"Tramp collection\" :user \"joe\")

The object labels of the found items are returned as list."
  (let ((collection-path (secrets-unlock-collection collection))
    result props)
    (unless (secrets-empty-path collection-path)
      ;; Create attributes list.
      (while (consp (cdr attributes))
    (unless (keywordp (car attributes))
      (error 'wrong-type-argument (car attributes)))
        (unless (stringp (cadr attributes))
          (error 'wrong-type-argument (cadr attributes)))
    (setq props (append
             props
             (list :dict-entry
                        ;; FIXME ------------------
               (substring (symbol-name (car attributes)) 1)
               (cadr attributes)))
          attributes (cddr attributes)))
      ;; Search.  The result is a list of object paths.
      (setq result
        (dbus-call-method
         :session secrets-service collection-path
         secrets-interface-collection "SearchItems"
         (if props
         (cons :array props)
           '(:array :signature "{ss}"))))
      ;; Return the found items.
      (mapcar
       (lambda (item-path) (secrets-get-item-property item-path "Label"))
           result))))

Let's fix this function so that it constructs arguments using list:

(defun secrets-search-items (collection &rest attributes)
  "Search items in COLLECTION with ATTRIBUTES.
ATTRIBUTES are key-value pairs.  The keys are keyword symbols,
starting with a colon.  Example:

  (secrets-search-items \"Tramp collection\" :user \"joe\")

The object labels of the found items are returned as list."
  (let ((collection-path (secrets-unlock-collection collection))
    result props)
    (unless (secrets-empty-path collection-path)
      ;; Create attributes list.
      (while (consp (cdr attributes))
    (unless (keywordp (car attributes))
      (error 'wrong-type-argument (car attributes)))
        (unless (stringp (cadr attributes))
          (error 'wrong-type-argument (cadr attributes)))
    (setq props (append
             props
             (list :dict-entry
                   ;; HACK fixed so that dict entries are conses
                   (list
                    (substring (symbol-name (car attributes)) 1)
                    (cadr attributes))))
          attributes (cddr attributes)))
      (prin1 props)
      ;; Search.  The result is a list of object paths.
      (setq result
        (dbus-call-method
         :session secrets-service collection-path
         secrets-interface-collection "SearchItems"
         (if props
         (cons :array props)
           '(:array :signature "{ss}"))))
      ;; Return the found items.
      (mapcar
       (lambda (item-path) (secrets-get-item-property item-path "Label"))
       result))))

Or as define-advice so that we can easily hotfix:

(define-advice secrets-search-items (:override (collection &rest attributes) my-secrets-search-fixed)
  "Fix cons building in dbus-call-method call"
  (let ((collection-path (secrets-unlock-collection collection))
        result props)
    (unless (secrets-empty-path collection-path)
      ;; Create attributes list.
      (while (consp (cdr attributes))
        (unless (keywordp (car attributes))
          (error 'wrong-type-argument (car attributes)))
        (unless (stringp (cadr attributes))
          (error 'wrong-type-argument (cadr attributes)))
        (setq props (append
                     props
                     (list :dict-entry
                           (substring (symbol-name (car attributes)) 1)
                           (cadr attributes)))
              attributes (cddr attributes)))
      ;; Search.  The result is a list of object paths.
      (setq result
            (dbus-call-method
             :session secrets-service collection-path
             secrets-interface-collection "SearchItems"
             (if props
                 (cons :array props)
               '(:array :signature "{ss}"))))
      ;; Return the found items.
      (mapcar
       (lambda (item-path) (secrets-get-item-property item-path "Label"))
       result))))

Questions