Does gnus-summary-move-article need to clear the marks?

2023-01-10 Thread Kevin Boulain
Hey,

I'm trying out Gnus and the IMAP server from which I'm getting my emails
is running a Sieve script that takes some actions, including setting a
bunch of IMAP keywords/flags ('client' ones, not the system ones like
\Seen as per https://www.rfc-editor.org/rfc/rfc3501#section-2.3.2).

When an email with these custom flags is marked as read (via 'd',
gnus-summary-mark-as-read-forward) and moved to another IMAP folder (via
'B m', gnus-summary-move-article) it loses its flags.

In the IMAP log (that I got with nnimap-record-commands) I can see:
  UID STORE 65 FLAGS.SILENT (\Seen)

A quick Edebug breakpoint on nnimap-request-set-mark (where the magic
seems to happen) gives the following backtrace:
  nnimap-request-set-mark("INBOX" (((65) set (read)) ((65) del (unexist seen 
forward unsend download cache save score dormant bookmark killed expire reply 
tick))) "$SERVER")
  gnus-request-set-mark("nnimap+$SERVER:INBOX" (((65) set (read)) ((65) del 
(unexist seen forward unsend download cache save score dormant bookmark killed 
expire reply tick
  gnus-summary-push-marks-to-backend(65)
  gnus-summary-move-article(nil)
  funcall-interactively(gnus-summary-move-article nil)
  command-execute(gnus-summary-move-article)

So the issue appears to be that gnus-summary-push-marks-to-backend calls
gnus-request-set-mark with 'set:
https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/gnus/gnus-sum.el?h=emacs-28.2#n10390

Should it be 'add instead, which should preserve the set of flags?
https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/gnus/nnimap.el?h=emacs-28.2#n1243

Similarly to what marking as read does:
  UID STORE 67 +FLAGS.SILENT (\Seen)
>From around here:
https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/gnus/gnus-sum.el?h=emacs-28.2#n12896

This patch appears to work but I'm not familiar with the code:
diff --git i/lisp/gnus/gnus-sum.el w/lisp/gnus/gnus-sum.el
index 3f350bffb3..a13777cd95 100644
--- i/lisp/gnus/gnus-sum.el
+++ w/lisp/gnus/gnus-sum.el
@@ -10390 +10390 @@ ACTION can be either `move' (the default), `crosspost' or 
`copy'."
-(gnus-request-set-mark gnus-newsgroup-name `(((,article) set ,set)
+(gnus-request-set-mark gnus-newsgroup-name `(((,article) add ,set)

PS: I sent a similar email to d...@gnus.org a few days ago but it
doesn't look like it went through. Apologies if it ends up duplicated.



Re: Keeping IMAP connection alive when using it in mail-sources

2023-01-10 Thread Emanuel Berg
Adam Sjøgren wrote:

> Here's the modified version of mail-source-fetch-imap
> I cobbled together - the handling of deleting the buffer if
> there is no process is not so pretty:
>
> (defun mail-source-fetch-imap (source callback)
>   "Fetcher for imap sources."
>   (mail-source-bind (imap source)
> (mail-source-run-script
>  prescript
>  `((?p . ,password) (?t . ,mail-source-crash-box)
>(?s . ,server) (?P . ,port) (?u . ,user))
>  prescript-delay)
> (let ((from (format "%s:%s:%s" server user port))
> (found 0)
>   (imap-shell-program (or (list program) imap-shell-program)))
>   (let ((buf (or (and (or (get-buffer-process " *imap source*")
>   (and (get-buffer " *imap source*")
>(kill-buffer " *imap source*")))
>   (get-buffer " *imap source*"))
>  (let ((newbuf (generate-new-buffer " *imap source*")))
>  (if (and (imap-open server port stream authentication 
> newbuf)
>   (imap-authenticate
>user (or (cdr (assoc from 
> mail-source-password-cache))
>   password)
>  newbuf))
>newbuf
>  (progn
>(imap-close newbuf)
>  ;; We nix out the password in case the error
>  ;; was because of a wrong password being given.
>  (setq mail-source-password-cache
>(delq (assoc from mail-source-password-cache)
>  mail-source-password-cache))
>  (error "IMAP error: %s" (imap-error-text 
> newbuf
> (let ((mailbox-list (if (listp mailbox) mailbox (list mailbox
>   (dolist (mailbox mailbox-list)
> (when (imap-mailbox-select mailbox nil buf)
> (let ((coding-system-for-write
>  mail-source-imap-file-coding-system)
>   (mail-source-string (format "imap:%s:%s" server mailbox))
>   str remove)
> (message "Fetching from %s..." mailbox)
>   (with-temp-file mail-source-crash-box
> ;; Avoid converting 8-bit chars from inserted strings to
> ;; multibyte.
> (mm-disable-multibyte)
> ;; remember password
> (with-current-buffer buf
>   (when (and imap-password
>  (not (member (cons from imap-password)
> mail-source-password-cache)))
> (push (cons from imap-password) 
> mail-source-password-cache)))
> ;; if predicate is nil, use all uids
> (dolist (uid (imap-search (or predicate "1:*") buf))
>   (when (setq str
>   (if (imap-capability 'IMAP4rev1 buf)
>   (caddar (imap-fetch uid "BODY.PEEK[]"
>   'BODYDETAIL nil buf))
> (imap-fetch uid "RFC822.PEEK" 'RFC822 nil 
> buf)))
> (push uid remove)
> (insert "From imap " (current-time-string) "\n")
> (save-excursion
>   (insert str "\n\n"))
> (while (let ((case-fold-search nil))
>  (re-search-forward "^From " nil t))
>   (replace-match ">From "))
> (goto-char (point-max
> (nnheader-ms-strip-cr))
>   (cl-incf found (mail-source-callback callback server))
>   (mail-source-delete-crash-box)
>   (when (and remove fetchflag)
> (setq remove (nreverse remove))
> (imap-message-flags-add
>  (imap-range-to-message-set (gnus-compress-sequence remove))
>  fetchflag nil buf))
>   (if dontexpunge
>   (imap-mailbox-unselect buf)
>   (imap-mailbox-close nil buf))
> (mail-source-run-script
>  postscript
>  `((?p . ,password) (?t . ,mail-source-crash-box)
>(?s . ,server) (?P . ,port) (?u . ,user)))
> found

Good work but consider writing longer functions?

-- 
underground experts united
https://dataswamp.org/~incal




Re: Keeping IMAP connection alive when using it in mail-sources

2023-01-10 Thread Adam Sjøgren
Adam writes:

> Eric writes:
>
>> Not with the code as it's written! There's a very definite
>> (imap-close buf) at the end of the mail source fetching, then the buffer
>> is deleted. I guess it wouldn't be a bad idea to add a keepalive option
>> to imap.el, but someone would have to do that!
>
> I guess changing mail-source-fetch-imap to check if the buffer already
> exists and if the connected process is still there, and then not closing
> imap and not killing the buffer at the end, would be the bare minimum.

I've tried doing that now.

Fetching news and mail now takes me 1.3 second, down from 2.2 seconds.

Nice!

Here's the modified version of mail-source-fetch-imap I cobbled
together - the handling of deleting the buffer if there is no process is
not so pretty:

(defun mail-source-fetch-imap (source callback)
  "Fetcher for imap sources."
  (mail-source-bind (imap source)
(mail-source-run-script
 prescript
 `((?p . ,password) (?t . ,mail-source-crash-box)
   (?s . ,server) (?P . ,port) (?u . ,user))
 prescript-delay)
(let ((from (format "%s:%s:%s" server user port))
  (found 0)
  (imap-shell-program (or (list program) imap-shell-program)))
  (let ((buf (or (and (or (get-buffer-process " *imap source*")
  (and (get-buffer " *imap source*")
   (kill-buffer " *imap source*")))
  (get-buffer " *imap source*"))
 (let ((newbuf (generate-new-buffer " *imap source*")))
   (if (and (imap-open server port stream authentication 
newbuf)
(imap-authenticate
 user (or (cdr (assoc from 
mail-source-password-cache))
  password)
 newbuf))
   newbuf
 (progn
   (imap-close newbuf)
   ;; We nix out the password in case the error
   ;; was because of a wrong password being given.
   (setq mail-source-password-cache
 (delq (assoc from mail-source-password-cache)
   mail-source-password-cache))
   (error "IMAP error: %s" (imap-error-text 
newbuf
(let ((mailbox-list (if (listp mailbox) mailbox (list mailbox
  (dolist (mailbox mailbox-list)
(when (imap-mailbox-select mailbox nil buf)
  (let ((coding-system-for-write
 mail-source-imap-file-coding-system)
(mail-source-string (format "imap:%s:%s" server mailbox))
str remove)
(message "Fetching from %s..." mailbox)
(with-temp-file mail-source-crash-box
  ;; Avoid converting 8-bit chars from inserted strings to
  ;; multibyte.
  (mm-disable-multibyte)
  ;; remember password
  (with-current-buffer buf
(when (and imap-password
   (not (member (cons from imap-password)
mail-source-password-cache)))
  (push (cons from imap-password) 
mail-source-password-cache)))
  ;; if predicate is nil, use all uids
  (dolist (uid (imap-search (or predicate "1:*") buf))
(when (setq str
(if (imap-capability 'IMAP4rev1 buf)
(caddar (imap-fetch uid "BODY.PEEK[]"
'BODYDETAIL nil buf))
  (imap-fetch uid "RFC822.PEEK" 'RFC822 nil 
buf)))
  (push uid remove)
  (insert "From imap " (current-time-string) "\n")
  (save-excursion
(insert str "\n\n"))
  (while (let ((case-fold-search nil))
   (re-search-forward "^From " nil t))
(replace-match ">From "))
  (goto-char (point-max
  (nnheader-ms-strip-cr))
(cl-incf found (mail-source-callback callback server))
(mail-source-delete-crash-box)
(when (and remove fetchflag)
  (setq remove (nreverse remove))
  (imap-message-flags-add
   (imap-range-to-message-set (gnus-compress-sequence remove))
   fetchflag nil buf))
(if dontexpunge
(imap-mailbox-unselect buf)
  (imap-mailbox-close nil buf))
(mail-source-run-script
 postscript
 `((?p . ,password) (?t . ,mail-source-crash-box)
   (?s . ,server) (?P . ,port) (?u . ,user)

Re: Keeping IMAP connection alive when using it in mail-sources

2023-01-10 Thread Adam Sjøgren
Eric writes:

> Not with the code as it's written! There's a very definite
> (imap-close buf) at the end of the mail source fetching, then the buffer
> is deleted. I guess it wouldn't be a bad idea to add a keepalive option
> to imap.el, but someone would have to do that!

I guess changing mail-source-fetch-imap to check if the buffer already
exists and if the connected process is still there, and then not closing
imap and not killing the buffer at the end, would be the bare minimum.

Interesting.

Thanks for the pointer!


  Best regards,

Adam

-- 
 "Emacs [...] a [...] nuclear-powered Swiss ArmyAdam Sjøgren
  knife favored by top-notch programmers and computer  a...@koldfront.dk
  scientists."