]> git.pld-linux.org Git - packages/mutt.git/blobdiff - mutt-vvv.nntp.patch
- release 2, vvv patches updated from upstream
[packages/mutt.git] / mutt-vvv.nntp.patch
index 89da2415d56301204c04b03914bc7a3575c63dc0..c99ab70125d847436f95c1ed881b72c81de03430 100644 (file)
@@ -9,10 +9,26 @@ autoconf
 Vsevolod Volkov <vvv@mutt.org.ua>
 
 
-diff -udprP mutt-1.10.0.orig/ChangeLog.nntp mutt-1.10.0/ChangeLog.nntp
---- mutt-1.10.0.orig/ChangeLog.nntp    1970-01-01 03:00:00.000000000 +0300
-+++ mutt-1.10.0/ChangeLog.nntp 2018-06-16 17:38:41.196958026 +0300
-@@ -0,0 +1,448 @@
+diff -udprP mutt-1.12.1.orig/ChangeLog.nntp mutt-1.12.1/ChangeLog.nntp
+--- mutt-1.12.1.orig/ChangeLog.nntp    1970-01-01 03:00:00.000000000 +0300
++++ mutt-1.12.1/ChangeLog.nntp 2019-08-11 20:31:26.130940734 +0300
+@@ -0,0 +1,464 @@
++* Sun Aug 11 2019 Vsevolod Volkov <vvv@mutt.org.ua>
++- update to 1.12.1
++
++* Sun Aug 11 2019 Vsevolod Volkov <vvv@mutt.org.ua>
++- update to 1.12.0
++
++* Sun Dec  9 2018 Vsevolod Volkov <vvv@mutt.org.ua>
++- update to 1.11.1
++
++* Sun Dec  9 2018 Vsevolod Volkov <vvv@mutt.org.ua>
++- update to 1.11.0
++- FREE(&hdata) replaced with mutt_hcache_free(&hdata)
++  (thanks to Steffen Wolf)
++- fixed SIGSEGV when pressing OP_FOLLOWUP in attachment menu
++  (thanks to Moritz Barsnick)
++
 +* Sat Jun 16 2018 Vsevolod Volkov <vvv@mutt.org.ua>
 +- update to 1.10.0
 +
@@ -461,9 +477,82 @@ diff -udprP mutt-1.10.0.orig/ChangeLog.nntp mutt-1.10.0/ChangeLog.nntp
 +
 +* Fri Jan 28 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
 +- update to 1.1.2
-diff -udprP mutt-1.10.0.orig/OPS mutt-1.10.0/OPS
---- mutt-1.10.0.orig/OPS       2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/OPS    2018-06-16 17:22:30.190469794 +0300
+diff -udprP mutt-1.12.1.orig/Makefile.am mutt-1.12.1/Makefile.am
+--- mutt-1.12.1.orig/Makefile.am       2019-06-15 18:57:01.000000000 +0300
++++ mutt-1.12.1/Makefile.am    2019-08-11 20:31:26.131940718 +0300
+@@ -60,6 +60,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c
+       mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \
+       mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \
+       pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \
++      nntp.c newsrc.c \
+       sidebar.c smime.c smtp.c utf8.c wcwidth.c \
+       bcache.h browser.h hcache.h mbyte.h monitor.h mutt_idna.h remailer.h url.h
+@@ -72,6 +73,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP O
+       mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
+       mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
+       rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \
++      nntp.h ChangeLog.nntp \
+       _mutt_regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \
+       mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \
+       README.SSL smime.h group.h \
+diff -udprP mutt-1.12.1.orig/Makefile.in mutt-1.12.1/Makefile.in
+--- mutt-1.12.1.orig/Makefile.in       2019-06-15 19:07:04.000000000 +0300
++++ mutt-1.12.1/Makefile.in    2019-08-11 20:31:38.771749259 +0300
+@@ -256,6 +256,7 @@ am__depfiles_remade = $(DEPDIR)/mkdtemp.
+       ./$(DEPDIR)/rfc3676.Po ./$(DEPDIR)/rfc822.Po \
+       ./$(DEPDIR)/safe_asprintf.Po ./$(DEPDIR)/score.Po \
+       ./$(DEPDIR)/send.Po ./$(DEPDIR)/sendlib.Po ./$(DEPDIR)/sha1.Po \
++      ./$(DEPDIR)/newsrc.Po ./$(DEPDIR)/nntp.Po \
+       ./$(DEPDIR)/sidebar.Po ./$(DEPDIR)/signal.Po \
+       ./$(DEPDIR)/smime.Po ./$(DEPDIR)/smtp.Po ./$(DEPDIR)/sort.Po \
+       ./$(DEPDIR)/status.Po ./$(DEPDIR)/system.Po \
+@@ -570,6 +571,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c
+       mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \
+       mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \
+       pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \
++      nntp.c newsrc.c \
+       sidebar.c smime.c smtp.c utf8.c wcwidth.c \
+       bcache.h browser.h hcache.h mbyte.h monitor.h mutt_idna.h remailer.h url.h
+@@ -582,6 +584,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP O
+       mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
+       mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
+       rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \
++      nntp.h ChangeLog.nntp \
+       _mutt_regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \
+       mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \
+       README.SSL smime.h group.h \
+@@ -853,6 +856,8 @@ distclean-compile:
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mutt_tunnel.Po@am__quote@ # am--include-marker
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muttlib.Po@am__quote@ # am--include-marker
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mx.Po@am__quote@ # am--include-marker
++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newsrc.Po@am__quote@ # am--include-marker
++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nntp.Po@am__quote@ # am--include-marker
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pager.Po@am__quote@ # am--include-marker
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@ # am--include-marker
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/patchlist.Po@am__quote@ # am--include-marker
+@@ -1356,6 +1361,8 @@ distclean: distclean-recursive
+       -rm -f ./$(DEPDIR)/mutt_tunnel.Po
+       -rm -f ./$(DEPDIR)/muttlib.Po
+       -rm -f ./$(DEPDIR)/mx.Po
++      -rm -f ./$(DEPDIR)/newsrc.Po
++      -rm -f ./$(DEPDIR)/nntp.Po
+       -rm -f ./$(DEPDIR)/pager.Po
+       -rm -f ./$(DEPDIR)/parse.Po
+       -rm -f ./$(DEPDIR)/patchlist.Po
+@@ -1525,6 +1532,8 @@ maintainer-clean: maintainer-clean-recur
+       -rm -f ./$(DEPDIR)/mutt_tunnel.Po
+       -rm -f ./$(DEPDIR)/muttlib.Po
+       -rm -f ./$(DEPDIR)/mx.Po
++      -rm -f ./$(DEPDIR)/newsrc.Po
++      -rm -f ./$(DEPDIR)/nntp.Po
+       -rm -f ./$(DEPDIR)/pager.Po
+       -rm -f ./$(DEPDIR)/parse.Po
+       -rm -f ./$(DEPDIR)/patchlist.Po
+diff -udprP mutt-1.12.1.orig/OPS mutt-1.12.1/OPS
+--- mutt-1.12.1.orig/OPS       2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/OPS    2019-08-11 20:31:26.132940703 +0300
 @@ -8,14 +8,16 @@ OP_BOUNCE_MESSAGE "remail a message to a
  OP_BROWSER_NEW_FILE "select a new file in this directory"
  OP_BROWSER_VIEW_FILE "view file"
@@ -494,7 +583,7 @@ diff -udprP mutt-1.10.0.orig/OPS mutt-1.10.0/OPS
  OP_COMPOSE_EDIT_SUBJECT "edit the subject of this message"
  OP_COMPOSE_EDIT_TO "edit the TO list"
  OP_CREATE_MAILBOX "create a new mailbox (IMAP only)"
-@@ -89,8 +94,13 @@ OP_EXIT "exit this menu"
+@@ -90,8 +95,13 @@ OP_EXIT "exit this menu"
  OP_FILTER "filter attachment through a shell command"
  OP_FIRST_ENTRY "move to the first entry"
  OP_FLAG_MESSAGE "toggle a message's 'important' flag"
@@ -505,10 +594,10 @@ diff -udprP mutt-1.10.0.orig/OPS mutt-1.10.0/OPS
 +OP_GET_CHILDREN "get all children of the current message"
 +OP_GET_MESSAGE "get message with Message-Id"
 +OP_GET_PARENT "get parent of the current message"
+ OP_GROUP_CHAT_REPLY "reply to all recipients preserving To/Cc"
  OP_GROUP_REPLY "reply to all recipients"
  OP_HALF_DOWN "scroll down 1/2 page"
- OP_HALF_UP "scroll up 1/2 page"
-@@ -98,11 +108,14 @@ OP_HELP "this screen"
+@@ -100,11 +110,14 @@ OP_HELP "this screen"
  OP_JUMP "jump to an index number"
  OP_LAST_ENTRY "move to the last entry"
  OP_LIST_REPLY "reply to specified mailing list"
@@ -523,7 +612,7 @@ diff -udprP mutt-1.10.0.orig/OPS mutt-1.10.0/OPS
  OP_MAIN_CLEAR_FLAG "clear a status flag from a message"
  OP_MAIN_DELETE_PATTERN "delete messages matching a pattern"
  OP_MAIN_IMAP_FETCH "force retrieval of mail from IMAP server"
-@@ -142,6 +155,7 @@ OP_PAGER_HIDE_QUOTED "toggle display of
+@@ -144,6 +157,7 @@ OP_PAGER_HIDE_QUOTED "toggle display of
  OP_PAGER_SKIP_QUOTED "skip beyond quoted text"
  OP_PAGER_TOP "jump to the top of the message"
  OP_PIPE "pipe message/attachment to a shell command"
@@ -531,7 +620,7 @@ diff -udprP mutt-1.10.0.orig/OPS mutt-1.10.0/OPS
  OP_PREV_ENTRY "move to the previous entry"
  OP_PREV_LINE "scroll up one line"
  OP_PREV_PAGE "move to the previous page"
-@@ -151,6 +165,7 @@ OP_QUERY "query external program for add
+@@ -153,6 +167,7 @@ OP_QUERY "query external program for add
  OP_QUERY_APPEND "append new query results to current results"
  OP_QUIT "save changes to mailbox and quit"
  OP_RECALL_MESSAGE "recall a postponed message"
@@ -539,7 +628,7 @@ diff -udprP mutt-1.10.0.orig/OPS mutt-1.10.0/OPS
  OP_REDRAW "clear and redraw the screen"
  OP_REFORMAT_WINCH "{internal}"
  OP_RENAME_MAILBOX "rename the current mailbox (IMAP only)"
-@@ -165,18 +180,22 @@ OP_SEARCH_TOGGLE "toggle search pattern
+@@ -167,18 +182,22 @@ OP_SEARCH_TOGGLE "toggle search pattern
  OP_SHELL_ESCAPE "invoke a command in a subshell"
  OP_SORT "sort messages"
  OP_SORT_REVERSE "sort messages in reverse order"
@@ -562,18 +651,18 @@ diff -udprP mutt-1.10.0.orig/OPS mutt-1.10.0/OPS
  OP_VERSION "show the Mutt version number and date"
  OP_VIEW_ATTACH "view attachment using mailcap entry if necessary"
  OP_VIEW_ATTACHMENTS "show MIME attachments"
-diff -udprP mutt-1.10.0.orig/PATCHES mutt-1.10.0/PATCHES
---- mutt-1.10.0.orig/PATCHES   2017-12-03 05:10:17.000000000 +0200
-+++ mutt-1.10.0/PATCHES        2018-06-16 17:22:30.190469794 +0300
+diff -udprP mutt-1.12.1.orig/PATCHES mutt-1.12.1/PATCHES
+--- mutt-1.12.1.orig/PATCHES   2017-12-03 05:10:17.000000000 +0200
++++ mutt-1.12.1/PATCHES        2019-08-11 20:31:26.132940703 +0300
 @@ -0,0 +1 @@
 +vvv.nntp
-diff -udprP mutt-1.10.0.orig/account.c mutt-1.10.0/account.c
---- mutt-1.10.0.orig/account.c 2017-12-18 22:27:19.000000000 +0200
-+++ mutt-1.10.0/account.c      2018-06-16 17:22:30.191469778 +0300
+diff -udprP mutt-1.12.1.orig/account.c mutt-1.12.1/account.c
+--- mutt-1.12.1.orig/account.c 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/account.c      2019-08-11 20:31:26.133940688 +0300
 @@ -51,8 +51,17 @@ int mutt_account_match (const ACCOUNT* a
      user = PopUser;
  #endif
-   
 +#ifdef USE_NNTP
 +  if (a1->type == MUTT_ACCT_TYPE_NNTP && NntpUser)
 +    user = NntpUser;
@@ -627,9 +716,9 @@ diff -udprP mutt-1.10.0.orig/account.c mutt-1.10.0/account.c
    else if (option (OPTNOCURSES))
      return -1;
    else
-diff -udprP mutt-1.10.0.orig/account.h mutt-1.10.0/account.h
---- mutt-1.10.0.orig/account.h 2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/account.h      2018-06-16 17:22:30.191469778 +0300
+diff -udprP mutt-1.12.1.orig/account.h mutt-1.12.1/account.h
+--- mutt-1.12.1.orig/account.h 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/account.h      2019-08-11 20:31:26.133940688 +0300
 @@ -29,7 +29,8 @@ enum
    MUTT_ACCT_TYPE_NONE = 0,
    MUTT_ACCT_TYPE_IMAP,
@@ -640,9 +729,9 @@ diff -udprP mutt-1.10.0.orig/account.h mutt-1.10.0/account.h
  };
  
  /* account flags */
-diff -udprP mutt-1.10.0.orig/attach.h mutt-1.10.0/attach.h
---- mutt-1.10.0.orig/attach.h  2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/attach.h       2018-06-16 17:22:30.191469778 +0300
+diff -udprP mutt-1.12.1.orig/attach.h mutt-1.12.1/attach.h
+--- mutt-1.12.1.orig/attach.h  2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/attach.h       2019-08-11 20:31:26.133940688 +0300
 @@ -71,7 +71,7 @@ void mutt_print_attachment_list (ATTACH_
  
  void mutt_attach_bounce (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
@@ -650,11 +739,11 @@ diff -udprP mutt-1.10.0.orig/attach.h mutt-1.10.0/attach.h
 -void mutt_attach_forward (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
 +void mutt_attach_forward (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *, int);
  void mutt_attach_reply (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *, int);
+ void mutt_attach_mail_sender (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
  
- void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach);
-diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
---- mutt-1.10.0.orig/browser.c 2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/browser.c      2018-06-16 17:22:30.192469762 +0300
+diff -udprP mutt-1.12.1.orig/browser.c mutt-1.12.1/browser.c
+--- mutt-1.12.1.orig/browser.c 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/browser.c      2019-08-11 20:31:26.133940688 +0300
 @@ -32,6 +32,9 @@
  #ifdef USE_IMAP
  #include "imap.h"
@@ -685,7 +774,7 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
  typedef struct folder_t
  {
    struct folder_file *ff;
-@@ -134,9 +150,17 @@ static void browser_sort (struct browser
+@@ -140,9 +156,17 @@ static void browser_sort (struct browser
      case SORT_ORDER:
        return;
      case SORT_DATE:
@@ -703,7 +792,7 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
        f = browser_compare_size;
        break;
      case SORT_COUNT:
-@@ -367,8 +391,112 @@ folder_format_str (char *dest, size_t de
+@@ -388,8 +412,112 @@ folder_format_str (char *dest, size_t de
    return (src);
  }
  
@@ -817,7 +906,7 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
  {
    if (state->entrylen == state->entrymax)
    {
-@@ -406,6 +534,10 @@ static void add_folder (MUTTMENU *m, str
+@@ -427,6 +555,10 @@ static void add_folder (MUTTMENU *m, str
  #ifdef USE_IMAP
    (state->entry)[state->entrylen].imap = 0;
  #endif
@@ -828,13 +917,13 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
    (state->entrylen)++;
  }
  
-@@ -421,9 +553,35 @@ static void init_state (struct browser_s
+@@ -442,9 +574,35 @@ static void init_state (struct browser_s
      menu->data = state->entry;
  }
  
 +/* get list of all files/newsgroups with mask */
  static int examine_directory (MUTTMENU *menu, struct browser_state *state,
-                             char *d, const char *prefix)
+                             const char *d, const char *prefix)
  {
 +#ifdef USE_NNTP
 +  if (option (OPTNEWS))
@@ -864,16 +953,20 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
    struct stat s;
    DIR *dp;
    struct dirent *de;
-@@ -492,17 +650,40 @@ static int examine_directory (MUTTMENU *
+@@ -514,18 +672,41 @@ static int examine_directory (MUTTMENU *
        tmp->msg_count = Context->msgcount;
        tmp->msg_unread = Context->unread;
      }
 -    add_folder (menu, state, de->d_name, &s, tmp);
 +    add_folder (menu, state, de->d_name, &s, tmp, NULL);
    }
-   closedir (dp);  
+   closedir (dp);
+-  browser_sort (state);
+-
+   mutt_buffer_pool_release (&buffer);
 +  }
-   browser_sort (state);
++
++  browser_sort (state);
    return 0;
  }
  
@@ -881,7 +974,6 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
  static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
  {
    struct stat s;
-   char buffer[LONG_STRING];
 +
 +#ifdef USE_NNTP
 +  if (option (OPTNEWS))
@@ -904,42 +996,47 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +#endif
 +  {
    BUFFY *tmp = Incoming;
-   if (!Incoming)
-@@ -527,14 +708,21 @@ static int examine_mailboxes (MUTTMENU *
+   BUFFER *mailbox = NULL;
+   BUFFER *md = NULL;
+@@ -554,14 +735,21 @@ static int examine_mailboxes (MUTTMENU *
  #ifdef USE_IMAP
-     if (mx_is_imap (tmp->path))
+     if (mx_is_imap (mutt_b2s (tmp->pathbuf)))
      {
--      add_folder (menu, state, buffer, NULL, tmp);
-+      add_folder (menu, state, buffer, NULL, tmp, NULL);
+-      add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp);
++      add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp, NULL);
        continue;
      }
  #endif
  #ifdef USE_POP
-     if (mx_is_pop (tmp->path))
+     if (mx_is_pop (mutt_b2s (tmp->pathbuf)))
      {
--      add_folder (menu, state, buffer, NULL, tmp);
-+      add_folder (menu, state, buffer, NULL, tmp, NULL);
+-      add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp);
++      add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp, NULL);
 +      continue;
 +    }
 +#endif
 +#ifdef USE_NNTP
-+    if (mx_is_nntp (tmp->path))
++    if (mx_is_nntp (mutt_b2s (tmp->pathbuf)))
 +    {
-+      add_folder (menu, state, buffer, NULL, tmp, NULL);
++      add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp, NULL);
        continue;
      }
  #endif
-@@ -560,15 +748,20 @@ static int examine_mailboxes (MUTTMENU *
+@@ -586,18 +774,23 @@ static int examine_mailboxes (MUTTMENU *
        s.st_mtime = st2.st_mtime;
      }
  
--    add_folder (menu, state, buffer, &s, tmp);
-+    add_folder (menu, state, buffer, &s, tmp, NULL);
+-    add_folder (menu, state, mutt_b2s (mailbox), &s, tmp);
++    add_folder (menu, state, mutt_b2s (mailbox), &s, tmp, NULL);
    }
    while ((tmp = tmp->next));
+-  browser_sort (state);
+-
+   mutt_buffer_pool_release (&mailbox);
+   mutt_buffer_pool_release (&md);
 +  }
-   browser_sort (state);
++
++  browser_sort (state);
    return 0;
  }
  
@@ -952,23 +1049,23 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
    return (regexec (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
  }
  
-@@ -579,6 +772,12 @@ static void folder_entry (char *s, size_
+@@ -608,6 +801,12 @@ static void folder_entry (char *s, size_
    folder.ff = &((struct folder_file *) menu->data)[num];
    folder.num = num;
-   
 +#ifdef USE_NNTP
 +  if (option (OPTNEWS))
 +    mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL(GroupFormat),
 +      newsgroup_format_str, (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
 +  else
 +#endif
-   mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL(FolderFormat), folder_format_str, 
-       (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
+   mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL(FolderFormat), folder_format_str,
+                      (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
  }
-@@ -599,6 +798,17 @@ static void init_menu (struct browser_st
+@@ -630,6 +829,17 @@ static void init_menu (struct browser_st
  
    menu->tagged = 0;
-   
 +#ifdef USE_NNTP
 +  if (option (OPTNEWS))
 +  {
@@ -983,15 +1080,15 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
    if (buffy)
      snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0));
    else
-@@ -654,6 +864,31 @@ void _mutt_select_file (char *f, size_t
+@@ -717,6 +927,31 @@ void _mutt_buffer_select_file (BUFFER *f
    if (!folder)
-     strfcpy (LastDirBackup, LastDir, sizeof (LastDirBackup));
+     mutt_buffer_strcpy (LastDirBackup, mutt_b2s (LastDir));
  
 +#ifdef USE_NNTP
 +  if (option (OPTNEWS))
 +  {
-+    if (*f)
-+      strfcpy (prefix, f, sizeof (prefix));
++    if (*(mutt_b2s (f)))
++      mutt_buffer_strcpy (prefix, mutt_b2s (f));
 +    else
 +    {
 +      NNTP_SERVER *nserv = CurrentNewsSrv;
@@ -1012,22 +1109,22 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +  }
 +  else
 +#endif
-   if (*f)
+   if (*(mutt_b2s (f)))
    {
-     mutt_expand_path (f, flen);
-@@ -750,6 +985,9 @@ void _mutt_select_file (char *f, size_t
+     mutt_buffer_expand_path (f);
+@@ -812,6 +1047,9 @@ void _mutt_buffer_select_file (BUFFER *f
      menu->tag = file_tag;
  
    menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
 +#ifdef USE_NNTP
-+    option (OPTNEWS) ? FolderNewsHelp :
++                                  option (OPTNEWS) ? FolderNewsHelp :
 +#endif
-     FolderHelp);
+                                   FolderHelp);
    mutt_push_current_menu (menu);
  
-@@ -889,7 +1127,11 @@ void _mutt_select_file (char *f, size_t
-         }
-       }
+@@ -959,7 +1197,11 @@ void _mutt_buffer_select_file (BUFFER *f
+           break;
+         }
  
 +#ifdef USE_NNTP
 +      if (buffy || option (OPTNEWS))
@@ -1035,9 +1132,9 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
        if (buffy)
 +#endif
        {
-         strfcpy (f, state.entry[menu->current].name, flen);
-         mutt_expand_path (f, flen);
-@@ -946,14 +1188,6 @@ void _mutt_select_file (char *f, size_t
+         mutt_buffer_strcpy (f, state.entry[menu->current].name);
+         mutt_buffer_expand_path (f);
+@@ -1014,14 +1256,6 @@ void _mutt_buffer_select_file (BUFFER *f
          break;
  
  #ifdef USE_IMAP
@@ -1052,8 +1149,8 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
        case OP_BROWSER_TOGGLE_LSUB:
        if (option (OPTIMAPLSUB))
          unset_option (OPTIMAPLSUB);
-@@ -1055,6 +1289,11 @@ void _mutt_select_file (char *f, size_t
-       
+@@ -1123,6 +1357,11 @@ void _mutt_buffer_select_file (BUFFER *f
        case OP_CHANGE_DIRECTORY:
  
 +#ifdef USE_NNTP
@@ -1061,13 +1158,13 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +        break;
 +#endif
 +
-       strfcpy (buf, LastDir, sizeof (buf));
+       mutt_buffer_strcpy (buf, mutt_b2s (LastDir));
  #ifdef USE_IMAP
        if (!state.imap_browse)
-@@ -1321,6 +1560,209 @@ void _mutt_select_file (char *f, size_t
-         else
-           mutt_error _("Error trying to view file");
-       }
+@@ -1395,6 +1634,210 @@ void _mutt_buffer_select_file (BUFFER *f
+             else
+               mutt_error _("Error trying to view file");
+           }
 +      break;
 +
 +#ifdef USE_NNTP
@@ -1083,7 +1180,7 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +        if (rc < 0)
 +          break;
 +
-+        if (i == OP_CATCHUP)
++        if (op == OP_CATCHUP)
 +          nntp_data = mutt_newsgroup_catchup (CurrentNewsSrv, f->name);
 +        else
 +          nntp_data = mutt_newsgroup_uncatchup (CurrentNewsSrv, f->name);
@@ -1156,16 +1253,17 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +        NNTP_SERVER *nserv = CurrentNewsSrv;
 +        NNTP_DATA *nntp_data;
 +        regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
++        char buf[_POSIX_PATH_MAX];
 +        char *s = buf;
 +        int rc, j = menu->current;
 +
-+        if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN)
++        if (op == OP_SUBSCRIBE_PATTERN || op == OP_UNSUBSCRIBE_PATTERN)
 +        {
 +          char tmp[STRING];
 +          int err;
 +
 +          buf[0] = 0;
-+          if (i == OP_SUBSCRIBE_PATTERN)
++          if (op == OP_SUBSCRIBE_PATTERN)
 +            snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
 +          else
 +            snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
@@ -1201,10 +1299,10 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +        {
 +          struct folder_file *f = &state.entry[j];
 +
-+          if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
++          if (op == OP_BROWSER_SUBSCRIBE || op == OP_BROWSER_UNSUBSCRIBE ||
 +                regexec (rx, f->name, 0, NULL, 0) == 0)
 +          {
-+            if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
++            if (op == OP_BROWSER_SUBSCRIBE || op == OP_SUBSCRIBE_PATTERN)
 +              nntp_data = mutt_newsgroup_subscribe (nserv, f->name);
 +            else
 +              nntp_data = mutt_newsgroup_unsubscribe (nserv, f->name);
@@ -1224,7 +1322,7 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +              f->desc = safe_strdup (buffer);
 +            } */
 +          }
-+          if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE)
++          if (op == OP_BROWSER_SUBSCRIBE || op == OP_BROWSER_UNSUBSCRIBE)
 +          {
 +            if (menu->current + 1 < menu->max)
 +              menu->current++;
@@ -1232,7 +1330,7 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +            break;
 +          }
 +        }
-+        if (i == OP_SUBSCRIBE_PATTERN)
++        if (op == OP_SUBSCRIBE_PATTERN)
 +        {
 +          unsigned int i;
 +
@@ -1255,7 +1353,7 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +        nntp_newsrc_update (nserv);
 +        nntp_clear_cache (nserv);
 +        nntp_newsrc_close (nserv);
-+        if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
++        if (op != OP_BROWSER_SUBSCRIBE && op != OP_BROWSER_UNSUBSCRIBE)
 +          regfree (rx);
 +        FREE (&rx);
 +      }
@@ -1265,7 +1363,7 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +#endif /* USE_NNTP */
 +#ifdef USE_IMAP
 +      {
-+        if (i == OP_BROWSER_SUBSCRIBE)
++        if (op == OP_BROWSER_SUBSCRIBE)
 +          imap_subscribe (state.entry[menu->current].name, 1);
 +        else
 +          imap_subscribe (state.entry[menu->current].name, 0);
@@ -1273,10 +1371,10 @@ diff -udprP mutt-1.10.0.orig/browser.c mutt-1.10.0/browser.c
 +#endif /* USE_IMAP */
      }
    }
-   
-diff -udprP mutt-1.10.0.orig/browser.h mutt-1.10.0/browser.h
---- mutt-1.10.0.orig/browser.h 2017-12-18 22:27:19.000000000 +0200
-+++ mutt-1.10.0/browser.h      2018-06-16 17:22:30.192469762 +0300
+diff -udprP mutt-1.12.1.orig/browser.h mutt-1.12.1/browser.h
+--- mutt-1.12.1.orig/browser.h 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/browser.h      2019-08-11 20:31:26.133940688 +0300
 @@ -19,6 +19,10 @@
  #ifndef _BROWSER_H
  #define _BROWSER_H 1
@@ -1298,10 +1396,10 @@ diff -udprP mutt-1.10.0.orig/browser.h mutt-1.10.0/browser.h
    unsigned tagged : 1;
  };
  
-diff -udprP mutt-1.10.0.orig/buffy.c mutt-1.10.0/buffy.c
---- mutt-1.10.0.orig/buffy.c   2018-04-17 02:31:03.000000000 +0300
-+++ mutt-1.10.0/buffy.c        2018-06-16 17:22:30.192469762 +0300
-@@ -512,6 +512,9 @@ int mutt_buffy_check (int force)
+diff -udprP mutt-1.12.1.orig/buffy.c mutt-1.12.1/buffy.c
+--- mutt-1.12.1.orig/buffy.c   2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/buffy.c        2019-08-11 20:31:26.134940673 +0300
+@@ -570,6 +570,9 @@ int mutt_buffy_check (int force)
  
    /* check device ID and serial number instead of comparing paths */
    if (!Context || Context->magic == MUTT_IMAP || Context->magic == MUTT_POP
@@ -1311,19 +1409,19 @@ diff -udprP mutt-1.10.0.orig/buffy.c mutt-1.10.0/buffy.c
        || stat (Context->path, &contex_sb) != 0)
    {
      contex_sb.st_dev=0;
-@@ -535,6 +538,11 @@ int mutt_buffy_check (int force)
+@@ -593,6 +596,11 @@ int mutt_buffy_check (int force)
        tmp->magic = MUTT_POP;
        else
  #endif
 +#ifdef USE_NNTP
-+      if ((tmp->magic == MUTT_NNTP) || mx_is_nntp (tmp->path))
++      if ((tmp->magic == MUTT_NNTP) || mx_is_nntp (mutt_b2s (tmp->pathbuf)))
 +      tmp->magic = MUTT_NNTP;
 +      else
 +#endif
-       if (stat (tmp->path, &sb) != 0 || (S_ISREG(sb.st_mode) && sb.st_size == 0) ||
-         (!tmp->magic && (tmp->magic = mx_get_magic (tmp->path)) <= 0))
-       {
-@@ -550,7 +558,11 @@ int mutt_buffy_check (int force)
+         if (stat (mutt_b2s (tmp->pathbuf), &sb) != 0 ||
+             (S_ISREG(sb.st_mode) && sb.st_size == 0) ||
+             (!tmp->magic &&
+@@ -610,7 +618,11 @@ int mutt_buffy_check (int force)
      /* check to see if the folder is the currently selected folder
       * before polling */
      if (!Context || !Context->path ||
@@ -1332,12 +1430,12 @@ diff -udprP mutt-1.10.0.orig/buffy.c mutt-1.10.0/buffy.c
 +#else
        (( tmp->magic == MUTT_IMAP || tmp->magic == MUTT_POP )
 +#endif
-           ? mutt_strcmp (tmp->path, Context->path) :
+             ? mutt_strcmp (mutt_b2s (tmp->pathbuf), Context->path) :
              (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino)))
      {
-diff -udprP mutt-1.10.0.orig/complete.c mutt-1.10.0/complete.c
---- mutt-1.10.0.orig/complete.c        2017-12-03 05:10:17.000000000 +0200
-+++ mutt-1.10.0/complete.c     2018-06-16 17:22:30.192469762 +0300
+diff -udprP mutt-1.12.1.orig/complete.c mutt-1.12.1/complete.c
+--- mutt-1.12.1.orig/complete.c        2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/complete.c     2019-08-11 20:31:26.134940673 +0300
 @@ -25,6 +25,9 @@
  #include "mailbox.h"
  #include "imap.h"
@@ -1419,10 +1517,10 @@ diff -udprP mutt-1.10.0.orig/complete.c mutt-1.10.0/complete.c
    /* we can use '/' as a delimiter, imap_complete rewrites it */
    if (*s == '=' || *s == '+' || *s == '!')
    {
-diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
---- mutt-1.10.0.orig/compose.c 2018-04-17 02:31:03.000000000 +0300
-+++ mutt-1.10.0/compose.c      2018-06-16 17:22:30.193469746 +0300
-@@ -32,11 +32,16 @@
+diff -udprP mutt-1.12.1.orig/compose.c mutt-1.12.1/compose.c
+--- mutt-1.12.1.orig/compose.c 2019-06-14 18:57:37.000000000 +0300
++++ mutt-1.12.1/compose.c      2019-08-11 20:31:26.134940673 +0300
+@@ -33,11 +33,16 @@
  #include "mailbox.h"
  #include "sort.h"
  #include "charset.h"
@@ -1439,7 +1537,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
  #include <errno.h>
  #include <string.h>
  #include <sys/stat.h>
-@@ -68,10 +73,20 @@ enum
+@@ -69,10 +74,20 @@ enum
    HDR_CRYPT,
    HDR_CRYPTINFO,
  
@@ -1460,7 +1558,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
  int MaxHeaderWidth = 0;
  
  #define HDR_XOFFSET MaxHeaderWidth
-@@ -109,6 +124,14 @@ static const char * const Prompts[] =
+@@ -110,6 +125,14 @@ static const char * const Prompts[] =
     * than 15-20 character cells.
     */
    N_("Sign as: ")
@@ -1475,7 +1573,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
  };
  
  static const struct mapping_t ComposeHelp[] = {
-@@ -126,6 +149,21 @@ static const struct mapping_t ComposeHel
+@@ -127,6 +150,21 @@ static const struct mapping_t ComposeHel
    { NULL,     0 }
  };
  
@@ -1497,7 +1595,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
  static void calc_header_width_padding (int idx, const char *header, int calc_max)
  {
    int width;
-@@ -161,7 +199,14 @@ static void init_header_padding (void)
+@@ -162,7 +200,14 @@ static void init_header_padding (void)
     * the other fields look funny. */
    calc_header_width_padding (HDR_CRYPTINFO, _(Prompts[HDR_CRYPTINFO]), 0);
  
@@ -1512,7 +1610,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
    {
      HeaderPadding[i] += MaxHeaderWidth;
      if (HeaderPadding[i] < 0)
-@@ -357,9 +402,37 @@ static void draw_envelope_addr (int line
+@@ -358,9 +403,37 @@ static void draw_envelope_addr (int line
  static void draw_envelope (HEADER *msg, char *fcc)
  {
    draw_envelope_addr (HDR_FROM, msg->env->from);
@@ -1550,7 +1648,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
  
    SETCOLOR (MT_COLOR_COMPOSE_HEADER);
    mutt_window_mvprintw (MuttIndexWindow, HDR_SUBJECT, 0,
-@@ -712,6 +785,12 @@ int mutt_compose_menu (HEADER *msg,   /*
+@@ -713,6 +786,12 @@ int mutt_compose_menu (HEADER *msg,   /*
    int oldSort, oldSortAux;
    struct stat st;
    compose_redraw_data_t rd;
@@ -1563,7 +1661,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
  
    init_header_padding ();
  
-@@ -722,6 +801,11 @@ int mutt_compose_menu (HEADER *msg,   /*
+@@ -723,6 +802,11 @@ int mutt_compose_menu (HEADER *msg,   /*
    menu->offset = HDR_ATTACH;
    menu->make_entry = snd_entry;
    menu->tag = mutt_tag_attach;
@@ -1575,7 +1673,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
    menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
    menu->custom_menu_redraw = compose_menu_redraw;
    menu->redraw_data = &rd;
-@@ -733,6 +817,9 @@ int mutt_compose_menu (HEADER *msg,   /*
+@@ -734,6 +818,9 @@ int mutt_compose_menu (HEADER *msg,   /*
  
    while (loop)
    {
@@ -1585,7 +1683,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
      switch (op = mutt_menuLoop (menu))
      {
        case OP_COMPOSE_EDIT_FROM:
-@@ -740,6 +827,10 @@ int mutt_compose_menu (HEADER *msg,   /*
+@@ -741,6 +828,10 @@ int mutt_compose_menu (HEADER *msg,   /*
          mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
        break;
        case OP_COMPOSE_EDIT_TO:
@@ -1596,7 +1694,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
        edit_address_list (HDR_TO, &msg->env->to);
        if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
        {
-@@ -749,6 +840,10 @@ int mutt_compose_menu (HEADER *msg,   /*
+@@ -750,6 +841,10 @@ int mutt_compose_menu (HEADER *msg,   /*
          mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
          break;
        case OP_COMPOSE_EDIT_BCC:
@@ -1607,7 +1705,7 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
        edit_address_list (HDR_BCC, &msg->env->bcc);
        if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
        {
-@@ -758,6 +853,10 @@ int mutt_compose_menu (HEADER *msg,   /*
+@@ -759,6 +854,10 @@ int mutt_compose_menu (HEADER *msg,   /*
          mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
        break;
        case OP_COMPOSE_EDIT_CC:
@@ -1618,9 +1716,9 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
        edit_address_list (HDR_CC, &msg->env->cc);
        if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
        {
-@@ -766,6 +865,62 @@ int mutt_compose_menu (HEADER *msg,   /*
+@@ -767,6 +866,62 @@ int mutt_compose_menu (HEADER *msg,   /*
        }
-         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);        
+         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
          break;
 +#ifdef USE_NNTP
 +      case OP_COMPOSE_EDIT_NEWSGROUPS:
@@ -1681,67 +1779,75 @@ diff -udprP mutt-1.10.0.orig/compose.c mutt-1.10.0/compose.c
        case OP_COMPOSE_EDIT_SUBJECT:
        if (msg->env->subject)
          strfcpy (buf, msg->env->subject, sizeof (buf));
-@@ -910,6 +1065,9 @@ int mutt_compose_menu (HEADER *msg,   /*
-         break;
+@@ -911,6 +1066,9 @@ int mutt_compose_menu (HEADER *msg,   /*
+       break;
  
        case OP_COMPOSE_ATTACH_MESSAGE:
 +#ifdef USE_NNTP
 +      case OP_COMPOSE_ATTACH_NEWS_MESSAGE:
 +#endif
-       {
-         char *prompt;
-         HEADER *h;
-@@ -917,7 +1075,22 @@ int mutt_compose_menu (HEADER *msg,   /*
-         fname[0] = 0;
-         prompt = _("Open mailbox to attach message from");
+       {
+         char *prompt;
+         HEADER *h;
+@@ -918,7 +1076,22 @@ int mutt_compose_menu (HEADER *msg,   /*
+         fname[0] = 0;
+         prompt = _("Open mailbox to attach message from");
  
 +#ifdef USE_NNTP
-+        unset_option (OPTNEWS);
-+        if (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE)
-+        {
-+          if (!(CurrentNewsSrv = nntp_select_server (NewsServer, 0)))
-+            break;
++        unset_option (OPTNEWS);
++        if (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE)
++        {
++          if (!(CurrentNewsSrv = nntp_select_server (NewsServer, 0)))
++            break;
 +
-+          prompt = _("Open newsgroup to attach message from");
-+          set_option (OPTNEWS);
-+        }
++          prompt = _("Open newsgroup to attach message from");
++          set_option (OPTNEWS);
++        }
 +#endif
 +
-         if (Context)
+         if (Context)
 +#ifdef USE_NNTP
-+        if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->magic == MUTT_NNTP))
++        if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->magic == MUTT_NNTP))
 +#endif
-         {
-           strfcpy (fname, NONULL (Context->path), sizeof (fname));
-           mutt_pretty_mailbox (fname, sizeof (fname));
-@@ -926,6 +1099,11 @@ int mutt_compose_menu (HEADER *msg,   /*
-         if (mutt_enter_fname (prompt, fname, sizeof (fname), 1) == -1 || !fname[0])
+         {
+           strfcpy (fname, NONULL (Context->path), sizeof (fname));
+           mutt_pretty_mailbox (fname, sizeof (fname));
+@@ -934,6 +1107,9 @@ int mutt_compose_menu (HEADER *msg,   /*
+ #ifdef USE_POP
+           if (!mx_is_pop (fname))
+ #endif
++#ifdef USE_NNTP
++          if (!mx_is_nntp (fname) && !option (OPTNEWS))
++#endif
+             /* check to make sure the file exists and is readable */
+             if (access (fname, R_OK) == -1)
+             {
+@@ -1219,6 +1395,19 @@ int mutt_compose_menu (HEADER *msg,   /*
            break;
+         }
  
 +#ifdef USE_NNTP
 +        if (option (OPTNEWS))
-+          nntp_expand_path (fname, sizeof (fname), &CurrentNewsSrv->conn->account);
++        {
++          BUFFER *s_buf;
++
++          s_buf = mutt_buffer_pool_get ();
++          mutt_buffer_addstr (s_buf, fname);
++          nntp_buffer_expand_path (s_buf, &CurrentNewsSrv->conn->account);
++          strfcpy (fname, mutt_b2s (s_buf), sizeof (fname));
++          mutt_buffer_pool_release (&s_buf);
++        }
 +        else
 +#endif
          mutt_expand_path (fname, sizeof (fname));
- #ifdef USE_IMAP
-           if (!mx_is_imap (fname))
-@@ -933,6 +1111,9 @@ int mutt_compose_menu (HEADER *msg,   /*
- #ifdef USE_POP
-           if (!mx_is_pop (fname))
- #endif
-+#ifdef USE_NNTP
-+        if (!mx_is_nntp (fname) && !option (OPTNEWS))
-+#endif
-         /* check to make sure the file exists and is readable */
-         if (access (fname, R_OK) == -1)
-         {
-diff -udprP mutt-1.10.0.orig/config.h.in mutt-1.10.0/config.h.in
---- mutt-1.10.0.orig/config.h.in       2018-05-15 04:09:08.000000000 +0300
-+++ mutt-1.10.0/config.h.in    2018-06-16 17:22:30.193469746 +0300
-@@ -640,6 +640,9 @@
- /* Define if you want support for the IMAP protocol. */
- #undef USE_IMAP
+         if (mutt_rename_file (CURATTACH->content->filename, fname))
+           break;
+diff -udprP mutt-1.12.1.orig/config.h.in mutt-1.12.1/config.h.in
+--- mutt-1.12.1.orig/config.h.in       2019-06-15 19:07:25.000000000 +0300
++++ mutt-1.12.1/config.h.in    2019-08-11 20:31:26.134940673 +0300
+@@ -665,6 +665,9 @@
+    only). */
+ #undef USE_INOTIFY
  
 +/* Define if you want support for the NNTP protocol. */
 +#undef USE_NNTP
@@ -1749,10 +1855,59 @@ diff -udprP mutt-1.10.0.orig/config.h.in mutt-1.10.0/config.h.in
  /* Define if you want support for the POP3 protocol. */
  #undef USE_POP
  
-diff -udprP mutt-1.10.0.orig/configure.ac mutt-1.10.0/configure.ac
---- mutt-1.10.0.orig/configure.ac      2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/configure.ac   2018-06-16 17:22:30.193469746 +0300
-@@ -633,6 +633,15 @@ AC_ARG_ENABLE(imap, AS_HELP_STRING([--en
+diff -udprP mutt-1.12.1.orig/configure mutt-1.12.1/configure
+--- mutt-1.12.1.orig/configure 2019-06-15 19:04:19.000000000 +0300
++++ mutt-1.12.1/configure      2019-08-11 20:31:26.136940643 +0300
+@@ -813,6 +813,7 @@ with_docdir
+ with_domain
+ enable_pop
+ enable_imap
++enable_nntp
+ enable_smtp
+ with_gss
+ with_ssl
+@@ -1499,6 +1500,7 @@ Optional Features:
+                           Force use of an external dotlock program
+   --enable-pop            Enable POP3 support
+   --enable-imap           Enable IMAP support
++  --enable-nntp           Enable NNTP support
+   --enable-smtp           include internal SMTP relay support
+   --enable-debug          Enable debugging support
+   --enable-flock          Use flock() to lock files
+@@ -10108,6 +10110,20 @@ else
+ fi
++# Check whether --enable-nntp was given.
++if test "${enable_nntp+set}" = set; then :
++  enableval=$enable_nntp;     if test x$enableval = xyes ; then
++
++$as_echo "#define USE_NNTP 1" >>confdefs.h
++
++              MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS nntp.o newsrc.o"
++              need_nntp="yes"
++              need_socket="yes"
++      fi
++
++fi
++
++
+ # Check whether --enable-smtp was given.
+ if test "${enable_smtp+set}" = set; then :
+   enableval=$enable_smtp; if test $enableval = yes; then
+@@ -10120,7 +10136,7 @@ $as_echo "#define USE_SMTP 1" >>confdefs
+ fi
+-if test x"$need_imap" = xyes -o x"$need_pop" = xyes ; then
++if test x"$need_imap" = xyes -o x"$need_pop" = xyes -o x"$need_nntp" = xyes ; then
+   MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS bcache.o"
+ fi
+diff -udprP mutt-1.12.1.orig/configure.ac mutt-1.12.1/configure.ac
+--- mutt-1.12.1.orig/configure.ac      2019-06-15 18:57:01.000000000 +0300
++++ mutt-1.12.1/configure.ac   2019-08-11 20:31:26.136940643 +0300
+@@ -648,6 +648,15 @@ AC_ARG_ENABLE(imap, AS_HELP_STRING([--en
  ])
  AM_CONDITIONAL(BUILD_IMAP, test x$need_imap = xyes)
  
@@ -1768,7 +1923,7 @@ diff -udprP mutt-1.10.0.orig/configure.ac mutt-1.10.0/configure.ac
  AC_ARG_ENABLE(smtp, AS_HELP_STRING([--enable-smtp],[include internal SMTP relay support]),
        [if test $enableval = yes; then
                AC_DEFINE(USE_SMTP, 1, [Include internal SMTP relay support])
-@@ -640,7 +649,7 @@ AC_ARG_ENABLE(smtp, AS_HELP_STRING([--en
+@@ -655,7 +664,7 @@ AC_ARG_ENABLE(smtp, AS_HELP_STRING([--en
                need_socket="yes"
        fi])
  
@@ -1777,9 +1932,9 @@ diff -udprP mutt-1.10.0.orig/configure.ac mutt-1.10.0/configure.ac
    MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS bcache.o"
  fi
  
-diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
---- mutt-1.10.0.orig/curs_main.c       2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/curs_main.c    2018-06-16 17:37:28.758111855 +0300
+diff -udprP mutt-1.12.1.orig/curs_main.c mutt-1.12.1/curs_main.c
+--- mutt-1.12.1.orig/curs_main.c       2019-06-11 03:53:09.000000000 +0300
++++ mutt-1.12.1/curs_main.c    2019-08-11 20:31:26.136940643 +0300
 @@ -22,6 +22,7 @@
  
  #include "mutt.h"
@@ -1788,7 +1943,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
  #include "mutt_menu.h"
  #include "mailbox.h"
  #include "mapping.h"
-@@ -43,6 +44,10 @@
+@@ -47,6 +48,10 @@
  
  #include "mutt_crypt.h"
  
@@ -1799,7 +1954,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
  
  #include <ctype.h>
  #include <stdlib.h>
-@@ -547,12 +552,27 @@ static void index_menu_redraw (MUTTMENU
+@@ -599,12 +604,27 @@ static void index_menu_redraw (MUTTMENU
    menu->redraw = 0;
  }
  
@@ -1827,7 +1982,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
    int op = OP_NULL;
    int done = 0;                /* controls when to exit the "event" loop */
    int i = 0, j;
-@@ -571,7 +591,11 @@ int mutt_index_menu (void)
+@@ -623,7 +643,11 @@ int mutt_index_menu (void)
    menu->make_entry = index_make_entry;
    menu->color = index_color;
    menu->current = ci_first_message ();
@@ -1840,7 +1995,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
    menu->custom_menu_redraw = index_menu_redraw;
    mutt_push_current_menu (menu);
  
-@@ -786,6 +810,9 @@ int mutt_index_menu (void)
+@@ -845,6 +869,9 @@ int mutt_index_menu (void)
        mutt_curs_set (1);      /* fallback from the pager */
      }
  
@@ -1850,7 +2005,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
      switch (op)
      {
  
-@@ -836,6 +863,161 @@ int mutt_index_menu (void)
+@@ -895,6 +922,161 @@ int mutt_index_menu (void)
        menu_current_bottom (menu);
        break;
  
@@ -2012,7 +2167,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
        case OP_JUMP:
  
        CHECK_MSGCOUNT;
-@@ -946,11 +1128,33 @@ int mutt_index_menu (void)
+@@ -1005,11 +1187,33 @@ int mutt_index_menu (void)
          break;
  
        case OP_MAIN_LIMIT:
@@ -2020,7 +2175,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
  
        CHECK_IN_MAILBOX;
        menu->oldcurrent = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ?
-               CURHDR->index : -1;
+           CURHDR->index : -1;
 -      if (mutt_pattern_func (MUTT_LIMIT, _("Limit to messages matching: ")) == 0)
 +      if (op == OP_TOGGLE_READ)
 +      {
@@ -2047,7 +2202,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
        {
          if (menu->oldcurrent >= 0)
          {
-@@ -1195,15 +1399,22 @@ int mutt_index_menu (void)
+@@ -1258,20 +1462,30 @@ int mutt_index_menu (void)
  #endif
        case OP_MAIN_CHANGE_FOLDER:
        case OP_MAIN_NEXT_UNREAD_MAILBOX:
@@ -2061,25 +2216,33 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
 +#ifdef USE_NNTP
 +      case OP_MAIN_CHANGE_GROUP:
 +      case OP_MAIN_CHANGE_GROUP_READONLY:
-+      unset_option (OPTNEWS);
-+#endif
-+      if (attach_msg || option (OPTREADONLY) ||
-+#ifdef USE_NNTP
-+          op == OP_MAIN_CHANGE_GROUP_READONLY ||
 +#endif
-+          op == OP_MAIN_CHANGE_FOLDER_READONLY)
-+        flags = MUTT_READONLY;
-+      else
-+        flags = 0;
+       {
+         BUFFER *folderbuf;
+         int cont = 0;  /* Set if we want to continue instead of break */
+         folderbuf = mutt_buffer_pool_get ();
  
 -        if ((op == OP_MAIN_CHANGE_FOLDER_READONLY) || option (OPTREADONLY))
-+      if (flags)
++#ifdef USE_NNTP
++        unset_option (OPTNEWS);
++#endif
++        if (attach_msg || option (OPTREADONLY) ||
++#ifdef USE_NNTP
++            op == OP_MAIN_CHANGE_GROUP_READONLY ||
++#endif
++            op == OP_MAIN_CHANGE_FOLDER_READONLY)
++          flags = MUTT_READONLY;
++        else
++          flags = 0;
++
++        if (flags)
            cp = _("Open mailbox in read-only mode");
          else
            cp = _("Open mailbox");
-@@ -1236,6 +1447,22 @@ int mutt_index_menu (void)
-             strfcpy (buf, Context->path, sizeof (buf));
-             mutt_pretty_mailbox (buf, sizeof (buf));
+@@ -1303,6 +1517,22 @@ int mutt_index_menu (void)
+             mutt_buffer_strcpy (folderbuf, Context->path);
+             mutt_buffer_pretty_mailbox (folderbuf);
            }
 +#ifdef USE_NNTP
 +        if (op == OP_MAIN_CHANGE_GROUP ||
@@ -2093,14 +2256,14 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
 +            cp = _("Open newsgroup in read-only mode");
 +          else
 +            cp = _("Open newsgroup");
-+          nntp_buffy (buf, sizeof (buf));
++          nntp_buffer_buffy (folderbuf);
 +        }
 +        else
 +#endif
-         mutt_buffy (buf, sizeof (buf));
+         mutt_buffer_buffy (folderbuf);
  
-           if (mutt_enter_fname (cp, buf, sizeof (buf), 1) == -1)
-@@ -1255,6 +1482,14 @@ int mutt_index_menu (void)
+           if (mutt_buffer_enter_fname (cp, folderbuf, 1) == -1)
+@@ -1321,6 +1551,14 @@ int mutt_index_menu (void)
          }
        }
  
@@ -2108,25 +2271,25 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
 +      if (option (OPTNEWS))
 +      {
 +        unset_option (OPTNEWS);
-+        nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account);
++        nntp_buffer_expand_path (folderbuf, &CurrentNewsSrv->conn->account);
 +      }
 +      else
 +#endif
-       mutt_expand_path (buf, sizeof (buf));
-       if (mx_get_magic (buf) <= 0)
+       mutt_buffer_expand_path (folderbuf);
+         if (mx_get_magic (mutt_b2s (folderbuf)) <= 0)
        {
-@@ -1307,9 +1542,7 @@ int mutt_index_menu (void)
+@@ -1381,9 +1619,7 @@ int mutt_index_menu (void)
           * switch statement would need to be run. */
-       mutt_folder_hook (buf);
+       mutt_folder_hook (mutt_b2s (folderbuf));
  
--      if ((Context = mx_open_mailbox (buf,
+-      if ((Context = mx_open_mailbox (mutt_b2s (folderbuf),
 -                                      (option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
 -                                      MUTT_READONLY : 0, NULL)) != NULL)
-+      if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL)
++      if ((Context = mx_open_mailbox (mutt_b2s (folderbuf), flags, NULL)) != NULL)
        {
          menu->current = ci_first_message ();
-       }
-@@ -1320,6 +1553,12 @@ int mutt_index_menu (void)
+ #ifdef USE_INOTIFY
+@@ -1397,6 +1633,12 @@ int mutt_index_menu (void)
          mutt_sb_set_open_buffy ();
  #endif
  
@@ -2137,9 +2300,9 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
 +#endif
 +
        mutt_clear_error ();
-       mutt_buffy_check(1); /* force the buffy check after we have changed
-                             the folder */
-@@ -1397,6 +1636,7 @@ int mutt_index_menu (void)
+       mutt_buffy_check(MUTT_BUFFY_CHECK_FORCE); /* force the buffy check after
+                                                    we have changed the
+@@ -1482,6 +1724,7 @@ int mutt_index_menu (void)
        CHECK_MSGCOUNT;
          CHECK_VISIBLE;
        CHECK_READONLY;
@@ -2147,7 +2310,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
  
          if ((Sort & SORT_MASK) != SORT_THREADS)
          mutt_error _("Threading is not enabled.");
-@@ -1432,7 +1672,7 @@ int mutt_index_menu (void)
+@@ -1517,7 +1760,7 @@ int mutt_index_menu (void)
          CHECK_VISIBLE;
        CHECK_READONLY;
          /* L10N: CHECK_ACL */
@@ -2156,7 +2319,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
  
          if ((Sort & SORT_MASK) != SORT_THREADS)
          mutt_error _("Threading is not enabled.");
-@@ -2068,6 +2308,20 @@ int mutt_index_menu (void)
+@@ -2170,6 +2413,20 @@ int mutt_index_menu (void)
        }
        break;
  
@@ -2177,7 +2340,7 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
        case OP_DISPLAY_ADDRESS:
  
        CHECK_MSGCOUNT;
-@@ -2326,6 +2580,39 @@ int mutt_index_menu (void)
+@@ -2408,6 +2665,39 @@ int mutt_index_menu (void)
          menu->redraw = REDRAW_FULL;
          break;
  
@@ -2215,6100 +2378,11 @@ diff -udprP mutt-1.10.0.orig/curs_main.c mutt-1.10.0/curs_main.c
 +#endif
 +
        case OP_REPLY:
-       CHECK_ATTACH;
-diff -udprP mutt-1.10.0.orig/doc/manual.xml.head mutt-1.10.0/doc/manual.xml.head
---- mutt-1.10.0.orig/doc/manual.xml.head       2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/doc/manual.xml.head    2018-06-16 17:22:30.195469715 +0300
-@@ -1755,6 +1755,26 @@ See also the <link linkend="postpone">$p
- </sect1>
-+<sect1 id="nntp">
-+<title>Reading news via NNTP</title>
-+
-+<para>
-+If compiled with <emphasis>--enable-nntp</emphasis> option, Mutt can
-+read news from news server via NNTP.  You can open a newsgroup with
-+function ``change-newsgroup'' (default: ``i'').  Default news server
-+can be obtained from <literal>$NNTPSERVER</literal> environment
-+variable or from <literal>/etc/nntpserver</literal> file.  Like other
-+news readers, info about subscribed newsgroups is saved in file by
-+<link linkend="newsrc">$newsrc</link> variable.  The variable <link
-+linkend="news-cache-dir">$news_cache_dir</link> can be used to point
-+to a directory.  Mutt will create a hierarchy of subdirectories named
-+like the account and newsgroup the cache is for.  Also the hierarchy
-+is used to store header cache if Mutt was compiled with <link
-+linkend="header-caching">header cache</link> support.
-+</para>
-+
-+</sect1>
-+
- </chapter>
- <chapter id="configuration">
-diff -udprP mutt-1.10.0.orig/doc/mutt.man mutt-1.10.0/doc/mutt.man
---- mutt-1.10.0.orig/doc/mutt.man      2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/doc/mutt.man   2018-06-16 17:22:30.195469715 +0300
-@@ -23,8 +23,8 @@ mutt \- The Mutt Mail User Agent
- .SH SYNOPSIS
- .PP
- .B mutt
--[\-nRyzZ]
--[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-m \fItype\fP] [\-f \fIfile\fP]
-+[\-GnRyzZ]
-+[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-g \fIserver\fP] [\-m \fItype\fP] [\-f \fIfile\fP]
- .PP
- .B mutt 
- [\-Enx] 
-@@ -104,6 +104,10 @@ files.
- Specify which mailbox to load.
- .IP "-F \fImuttrc\fP"
- Specify an initialization file to read instead of ~/.muttrc
-+.IP "-g \fIserver\fP"
-+Start Mutt with a listing of subscribed newsgroups at specified news server.
-+.IP "-G"
-+Start Mutt with a listing of subscribed newsgroups.
- .IP "-h"
- Display help.
- .IP "-H \fIdraft\fP"
-diff -udprP mutt-1.10.0.orig/functions.h mutt-1.10.0/functions.h
---- mutt-1.10.0.orig/functions.h       2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/functions.h    2018-06-16 17:22:30.195469715 +0300
-@@ -89,6 +89,10 @@ const struct binding_t OpMain[] = { /* m
-   { "break-thread",           OP_MAIN_BREAK_THREAD,           "#" },
-   { "change-folder",          OP_MAIN_CHANGE_FOLDER,          "c" },
-   { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
-+#ifdef USE_NNTP
-+  { "change-newsgroup",               OP_MAIN_CHANGE_GROUP,           "i" },
-+  { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY,        "\033i" },
-+#endif
-   { "next-unread-mailbox",    OP_MAIN_NEXT_UNREAD_MAILBOX,    NULL },
-   { "collapse-thread",                OP_MAIN_COLLAPSE_THREAD,        "\033v" },
-   { "collapse-all",           OP_MAIN_COLLAPSE_ALL,           "\033V" },
-@@ -103,7 +107,15 @@ const struct binding_t OpMain[] = { /* m
-   { "edit-label",             OP_EDIT_LABEL,                  "Y" },
-   { "edit-type",              OP_EDIT_TYPE,                   "\005" },
-   { "forward-message",                OP_FORWARD_MESSAGE,             "f" },
--  { "flag-message",           OP_FLAG_MESSAGE,                "F" },
-+#ifdef USE_NNTP
-+  { "forward-to-group",               OP_FORWARD_TO_GROUP,            "\033F" },
-+  { "followup-message",               OP_FOLLOWUP,                    "F" },
-+  { "get-children",           OP_GET_CHILDREN,                NULL },
-+  { "get-message",            OP_GET_MESSAGE,                 "\007" },
-+  { "get-parent",             OP_GET_PARENT,                  "\033G" },
-+  { "reconstruct-thread",     OP_RECONSTRUCT_THREAD,          NULL },
-+#endif
-+  { "flag-message",           OP_FLAG_MESSAGE,                "\033f" },
-   { "group-reply",            OP_GROUP_REPLY,                 "g" },
- #ifdef USE_POP
-   { "fetch-mail",             OP_MAIN_FETCH_MAIL,             "G" },
-@@ -131,6 +143,9 @@ const struct binding_t OpMain[] = { /* m
-   { "sort-mailbox",           OP_SORT,                        "o" },
-   { "sort-reverse",           OP_SORT_REVERSE,                "O" },
-   { "print-message",          OP_PRINT,                       "p" },
-+#ifdef USE_NNTP
-+  { "post-message",           OP_POST,                        "P" },
-+#endif
-   { "previous-thread",                OP_MAIN_PREV_THREAD,            "\020" },
-   { "previous-subthread",     OP_MAIN_PREV_SUBTHREAD,         "\033p" },
-   { "recall-message",         OP_RECALL_MESSAGE,              "R" },
-@@ -150,6 +165,10 @@ const struct binding_t OpMain[] = { /* m
-   { "show-version",           OP_VERSION,                     "V" },
-   { "set-flag",                       OP_MAIN_SET_FLAG,               "w" },
-   { "clear-flag",             OP_MAIN_CLEAR_FLAG,             "W" },
-+  { "toggle-read",            OP_TOGGLE_READ,                 "X" },
-+#ifdef USE_NNTP
-+  { "catchup",                        OP_CATCHUP,                     "y" },
-+#endif
-   { "display-message",                OP_DISPLAY_MESSAGE,             MUTT_ENTER_S },
-   { "mark-message",           OP_MARK_MSG,                    "~" },
-   { "buffy-list",             OP_BUFFY_LIST,                  "." },
-@@ -162,7 +181,7 @@ const struct binding_t OpMain[] = { /* m
-   { "previous-new-then-unread",       OP_MAIN_PREV_NEW_THEN_UNREAD,   "\033\t" },
-   { "next-unread",            OP_MAIN_NEXT_UNREAD,            NULL },
-   { "previous-unread",                OP_MAIN_PREV_UNREAD,            NULL },
--  { "parent-message",         OP_MAIN_PARENT_MESSAGE,         "P" },
-+  { "parent-message",         OP_MAIN_PARENT_MESSAGE,         NULL },
-   { "root-message",           OP_MAIN_ROOT_MESSAGE,           NULL },
-@@ -193,6 +212,10 @@ const struct binding_t OpPager[] = { /*
-   { "bounce-message", OP_BOUNCE_MESSAGE,              "b" },
-   { "change-folder",  OP_MAIN_CHANGE_FOLDER,          "c" },
-   { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
-+#ifdef USE_NNTP
-+  { "change-newsgroup",               OP_MAIN_CHANGE_GROUP,           "i" },
-+  { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY,        "\033i" },
-+#endif
-   { "next-unread-mailbox",    OP_MAIN_NEXT_UNREAD_MAILBOX, NULL },
-   { "copy-message",   OP_COPY_MESSAGE,                "C" },
-   { "decode-copy",    OP_DECODE_COPY,                 "\033C" },
-@@ -204,8 +227,12 @@ const struct binding_t OpPager[] = { /*
-   { "edit",           OP_EDIT_MESSAGE,                "e" },
-   { "edit-label",     OP_EDIT_LABEL,                  "Y" },
-   { "edit-type",      OP_EDIT_TYPE,                   "\005" },
-+#ifdef USE_NNTP
-+  { "followup-message",       OP_FOLLOWUP,                    "F" },
-+  { "forward-to-group",       OP_FORWARD_TO_GROUP,            "\033F" },
-+#endif
-   { "forward-message",        OP_FORWARD_MESSAGE,             "f" },
--  { "flag-message",   OP_FLAG_MESSAGE,                "F" },
-+  { "flag-message",   OP_FLAG_MESSAGE,                "\033f" },
-   { "group-reply",    OP_GROUP_REPLY,                 "g" },
- #ifdef USE_IMAP
-   { "imap-fetch-mail",  OP_MAIN_IMAP_FETCH,           NULL },
-@@ -227,6 +254,9 @@ const struct binding_t OpPager[] = { /*
-   { "sort-mailbox",   OP_SORT,                        "o" },
-   { "sort-reverse",   OP_SORT_REVERSE,                "O" },
-   { "print-message",  OP_PRINT,                       "p" },
-+#ifdef USE_NNTP
-+  { "post-message",   OP_POST,                        "P" },
-+#endif
-   { "previous-thread",        OP_MAIN_PREV_THREAD,            "\020" },
-   { "previous-subthread",OP_MAIN_PREV_SUBTHREAD,      "\033p" },
-   { "purge-message",  OP_PURGE_MESSAGE,               NULL },
-@@ -276,7 +306,7 @@ const struct binding_t OpPager[] = { /*
-   { "half-down",      OP_HALF_DOWN,                   NULL },
-   { "previous-line",  OP_PREV_LINE,                   NULL },
-   { "bottom",         OP_PAGER_BOTTOM,                NULL },
--  { "parent-message", OP_MAIN_PARENT_MESSAGE,         "P" },
-+  { "parent-message", OP_MAIN_PARENT_MESSAGE,         NULL },
-   { "root-message",   OP_MAIN_ROOT_MESSAGE,           NULL },
-@@ -309,6 +339,10 @@ const struct binding_t OpAttach[] = { /*
-   { "bounce-message", OP_BOUNCE_MESSAGE,              "b" },
-   { "display-toggle-weed",    OP_DISPLAY_HEADERS,     "h" },
-   { "edit-type",      OP_EDIT_TYPE,                   "\005" },
-+#ifdef USE_NNTP
-+  { "followup-message",       OP_FOLLOWUP,                    "F" },
-+  { "forward-to-group",       OP_FORWARD_TO_GROUP,            "\033F" },
-+#endif
-   { "print-entry",    OP_PRINT,                       "p" },
-   { "save-entry",     OP_SAVE,                        "s" },
-   { "pipe-entry",     OP_PIPE,                        "|" },
-@@ -334,6 +368,7 @@ const struct binding_t OpAttach[] = { /*
- const struct binding_t OpCompose[] = { /* map: compose */
-   { "attach-file",    OP_COMPOSE_ATTACH_FILE,         "a" },
-   { "attach-message", OP_COMPOSE_ATTACH_MESSAGE,      "A" },
-+  { "attach-news-message",OP_COMPOSE_ATTACH_NEWS_MESSAGE,"\033a" },
-   { "edit-bcc",               OP_COMPOSE_EDIT_BCC,            "b" },
-   { "edit-cc",                OP_COMPOSE_EDIT_CC,             "c" },
-   { "copy-file",      OP_SAVE,                        "C" },
-@@ -353,6 +388,11 @@ const struct binding_t OpCompose[] = { /
-   { "print-entry",    OP_PRINT,                       "l" },
-   { "edit-mime",      OP_COMPOSE_EDIT_MIME,           "m" },
-   { "new-mime",               OP_COMPOSE_NEW_MIME,            "n" },
-+#ifdef USE_NNTP
-+  { "edit-newsgroups",        OP_COMPOSE_EDIT_NEWSGROUPS,     "N" },
-+  { "edit-followup-to",       OP_COMPOSE_EDIT_FOLLOWUP_TO,    "o" },
-+  { "edit-x-comment-to",OP_COMPOSE_EDIT_X_COMMENT_TO, "x" },
-+#endif
-   { "postpone-message",       OP_COMPOSE_POSTPONE_MESSAGE,    "P" },
-   { "edit-reply-to",  OP_COMPOSE_EDIT_REPLY_TO,       "r" },
-   { "rename-attachment",OP_COMPOSE_RENAME_ATTACHMENT, "\017" },
-@@ -405,14 +445,25 @@ const struct binding_t OpBrowser[] = { /
-   { "select-new",     OP_BROWSER_NEW_FILE,    "N" },
-   { "check-new",      OP_CHECK_NEW,           NULL },
-   { "toggle-mailboxes", OP_TOGGLE_MAILBOXES,  "\t" },
-+#ifdef USE_NNTP
-+  { "reload-active",  OP_LOAD_ACTIVE,         "g" },
-+  { "subscribe-pattern", OP_SUBSCRIBE_PATTERN,        "S" },
-+  { "unsubscribe-pattern", OP_UNSUBSCRIBE_PATTERN, "U" },
-+  { "catchup",                OP_CATCHUP,             "y" },
-+  { "uncatchup",      OP_UNCATCHUP,           "Y" },
-+#endif
-   { "view-file",      OP_BROWSER_VIEW_FILE,   " " },
-   { "buffy-list",     OP_BUFFY_LIST,          "." },
- #ifdef USE_IMAP
-   { "create-mailbox",   OP_CREATE_MAILBOX,      "C" },
-   { "delete-mailbox",   OP_DELETE_MAILBOX,      "d" },
-   { "rename-mailbox",   OP_RENAME_MAILBOX,      "r" },
-+#endif
-+#if defined USE_IMAP || defined USE_NNTP
-   { "subscribe",      OP_BROWSER_SUBSCRIBE,   "s" },
-   { "unsubscribe",    OP_BROWSER_UNSUBSCRIBE, "u" },
-+#endif
-+#ifdef USE_IMAP
-   { "toggle-subscribed", OP_BROWSER_TOGGLE_LSUB, "T" },
- #endif
-   { NULL,             0,                      NULL }
-diff -udprP mutt-1.10.0.orig/globals.h mutt-1.10.0/globals.h
---- mutt-1.10.0.orig/globals.h 2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/globals.h      2018-06-16 17:22:30.196469699 +0300
-@@ -71,7 +71,7 @@ WHERE char *Inbox;
- WHERE char *Ispell;
- WHERE char *MailcapPath;
- WHERE char *Maildir;
--#if defined(USE_IMAP) || defined(USE_POP)
-+#if defined(USE_IMAP) || defined(USE_POP) || defined(USE_NNTP)
- WHERE char *MessageCachedir;
- #endif
- #if USE_HCACHE
-@@ -99,6 +99,17 @@ WHERE char *MixEntryFormat;
- #endif
- WHERE char *Muttrc INITVAL (NULL);
-+#ifdef USE_NNTP
-+WHERE char *GroupFormat;
-+WHERE char *Inews;
-+WHERE char *NewsCacheDir;
-+WHERE char *NewsServer;
-+WHERE char *NewsgroupsCharset;
-+WHERE char *NewsRc;
-+WHERE char *NntpAuthenticators;
-+WHERE char *NntpUser;
-+WHERE char *NntpPass;
-+#endif
- WHERE char *Outbox;
- WHERE char *Pager;
- WHERE char *PagerFmt;
-@@ -207,6 +218,11 @@ extern unsigned char QuadOptions[];
- WHERE unsigned short Counter INITVAL (0);
-+#ifdef USE_NNTP
-+WHERE short NewsPollTimeout;
-+WHERE short NntpContext;
-+#endif
-+
- WHERE short ConnectTimeout;
- WHERE short ErrorHistSize;
- WHERE short HistSize;
-diff -udprP mutt-1.10.0.orig/hcache.c mutt-1.10.0/hcache.c
---- mutt-1.10.0.orig/hcache.c  2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/hcache.c       2018-06-16 17:22:30.196469699 +0300
-@@ -534,6 +534,12 @@ dump_envelope(ENVELOPE * e, unsigned cha
-   d = dump_list(e->in_reply_to, d, off, 0);
-   d = dump_list(e->userhdrs, d, off, convert);
-+#ifdef USE_NNTP
-+  d = dump_char(e->xref, d, off, 0);
-+  d = dump_char(e->followup_to, d, off, 0);
-+  d = dump_char(e->x_comment_to, d, off, convert);
-+#endif
-+
-   return d;
- }
-@@ -570,6 +576,12 @@ restore_envelope(ENVELOPE * e, const uns
-   restore_list(&e->references, d, off, 0);
-   restore_list(&e->in_reply_to, d, off, 0);
-   restore_list(&e->userhdrs, d, off, convert);
-+
-+#ifdef USE_NNTP
-+  restore_char(&e->xref, d, off, 0);
-+  restore_char(&e->followup_to, d, off, 0);
-+  restore_char(&e->x_comment_to, d, off, convert);
-+#endif
- }
- static int
-diff -udprP mutt-1.10.0.orig/hdrline.c mutt-1.10.0/hdrline.c
---- mutt-1.10.0.orig/hdrline.c 2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/hdrline.c      2018-06-16 17:22:30.196469699 +0300
-@@ -227,6 +227,7 @@ static char *apply_subject_mods (ENVELOP
-  * %E = number of messages in current thread
-  * %f = entire from line
-  * %F = like %n, unless from self
-+ * %g = newsgroup name (if compiled with NNTP support)
-  * %i = message-id
-  * %l = number of lines in the message
-  * %L = like %F, except `lists' are displayed first
-@@ -243,6 +244,8 @@ static char *apply_subject_mods (ENVELOP
-  * %T = $to_chars
-  * %u = user (login) name of author
-  * %v = first name of author, unless from self
-+ * %W = where user is (organization)
-+ * %x = `x-comment-to:' field (if present and compiled with NNTP support)
-  * %X = number of MIME attachments
-  * %y = `x-label:' field (if present)
-  * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
-@@ -474,6 +477,12 @@ hdr_format_str (char *dest,
-       break;
-+#ifdef USE_NNTP
-+    case 'g':
-+      mutt_format_s (dest, destlen, prefix, hdr->env->newsgroups ? hdr->env->newsgroups : "");
-+      break;
-+#endif
-+
-     case 'i':
-       mutt_format_s (dest, destlen, prefix, hdr->env->message_id ? hdr->env->message_id : "<no.id>");
-       break;
-@@ -678,6 +687,22 @@ hdr_format_str (char *dest,
-       mutt_format_s (dest, destlen, prefix, buf2);
-       break;
-+    case 'W':
-+      if (!optional)
-+      mutt_format_s (dest, destlen, prefix, hdr->env->organization ? hdr->env->organization : "");
-+      else if (!hdr->env->organization)
-+      optional = 0;
-+      break;
-+
-+#ifdef USE_NNTP
-+    case 'x':
-+      if (!optional)
-+      mutt_format_s (dest, destlen, prefix, hdr->env->x_comment_to ? hdr->env->x_comment_to : "");
-+      else if (!hdr->env->x_comment_to)
-+      optional = 0;
-+      break;
-+#endif
-+
-     case 'Z':
-     
-       ch = ' ';
-diff -udprP mutt-1.10.0.orig/headers.c mutt-1.10.0/headers.c
---- mutt-1.10.0.orig/headers.c 2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/headers.c      2018-06-16 17:22:30.196469699 +0300
-@@ -115,6 +115,9 @@ void mutt_edit_headers (const char *edit
-      $edit_headers set, we remove References: as they're likely invalid;
-      we can simply compare strings as we don't generate References for
-      multiple Message-Ids in IRT anyways */
-+#ifdef USE_NNTP
-+  if (!option (OPTNEWSSEND))
-+#endif
-   if (msg->env->in_reply_to &&
-       (!n->in_reply_to || mutt_strcmp (n->in_reply_to->data,
-                                      msg->env->in_reply_to->data) != 0))
-diff -udprP mutt-1.10.0.orig/init.c mutt-1.10.0/init.c
---- mutt-1.10.0.orig/init.c    2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/init.c 2018-06-16 17:22:30.197469683 +0300
-@@ -3445,6 +3445,28 @@ void mutt_init (int skip_sys_rc, LIST *c
-     Fqdn = safe_strdup(utsname.nodename);
-+#ifdef USE_NNTP
-+  {
-+    FILE *f;
-+    char *i;
-+
-+    if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
-+    {
-+      buffer[0] = '\0';
-+      fgets (buffer, sizeof (buffer), f);
-+      p = buffer;
-+      SKIPWS (p);
-+      i = p;
-+      while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
-+      *i = '\0';
-+      NewsServer = safe_strdup (p);
-+      fclose (f);
-+    }
-+  }
-+  if ((p = getenv ("NNTPSERVER")))
-+    NewsServer = safe_strdup (p);
-+#endif
-+
-   if ((p = getenv ("MAIL")))
-     Spoolfile = safe_strdup (p);
-   else if ((p = getenv ("MAILDIR")))
-diff -udprP mutt-1.10.0.orig/init.h mutt-1.10.0/init.h
---- mutt-1.10.0.orig/init.h    2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/init.h 2018-06-16 17:29:50.406514947 +0300
-@@ -200,6 +200,20 @@ struct option_t MuttVars[] = {
-   ** If \fIset\fP, Mutt will prompt you for carbon-copy (Cc) recipients before
-   ** editing the body of an outgoing message.
-   */
-+#ifdef USE_NNTP
-+  { "ask_follow_up",  DT_BOOL, R_NONE, OPTASKFOLLOWUP, 0 },
-+  /*
-+  ** .pp
-+  ** If set, Mutt will prompt you for follow-up groups before editing
-+  ** the body of an outgoing message.
-+  */
-+  { "ask_x_comment_to",       DT_BOOL, R_NONE, OPTASKXCOMMENTTO, 0 },
-+  /*
-+  ** .pp
-+  ** If set, Mutt will prompt you for x-comment-to field before editing
-+  ** the body of an outgoing message.
-+  */
-+#endif
-   { "assumed_charset", DT_STR, R_NONE, UL &AssumedCharset, UL 0},
-   /*
-   ** .pp
-@@ -372,6 +386,14 @@ struct option_t MuttVars[] = {
-   ** doesn't make intuitive sense.  In those cases, it may be
-   ** desirable to \fIunset\fP this variable.
-   */
-+#ifdef USE_NNTP
-+  { "catchup_newsgroup", DT_QUAD, R_NONE, OPT_CATCHUP, MUTT_ASKYES },
-+  /*
-+  ** .pp
-+  ** If this variable is \fIset\fP, Mutt will mark all articles in newsgroup
-+  ** as read when you quit the newsgroup (catchup newsgroup).
-+  */
-+#endif
- #if defined(USE_SSL)
-   { "certificate_file",       DT_PATH, R_NONE, UL &SslCertFile, UL "~/.mutt_certificates" },
-   /*
-@@ -926,6 +948,16 @@ struct option_t MuttVars[] = {
-   ** sent to both the list and your address, resulting in two copies
-   ** of the same email for you.
-   */
-+#ifdef USE_NNTP
-+  { "followup_to_poster", DT_QUAD, R_NONE, OPT_FOLLOWUPTOPOSTER, MUTT_ASKYES },
-+  /*
-+  ** .pp
-+  ** If this variable is \fIset\fP and the keyword "poster" is present in
-+  ** \fIFollowup-To\fP header, follow-up to newsgroup function is not
-+  ** permitted.  The message will be mailed to the submitter of the
-+  ** message via mail.
-+  */
-+#endif
-   { "force_name",     DT_BOOL, R_NONE, OPTFORCENAME, 0 },
-   /*
-   ** .pp
-@@ -1024,6 +1056,26 @@ struct option_t MuttVars[] = {
-   ** a regular expression that will match the whole name so mutt will expand
-   ** ``Franklin'' to ``Franklin, Steve''.
-   */
-+#ifdef USE_NNTP
-+  { "group_index_format", DT_STR, R_BOTH, UL &GroupFormat, UL "%4C %M%N %5s  %-45.45f %d" },
-+  /*
-+  ** .pp
-+  ** This variable allows you to customize the newsgroup browser display to
-+  ** your personal taste.  This string is similar to ``$index_format'', but
-+  ** has its own set of printf()-like sequences:
-+  ** .dl
-+  ** .dt %C  .dd current newsgroup number
-+  ** .dt %d  .dd description of newsgroup (becomes from server)
-+  ** .dt %f  .dd newsgroup name
-+  ** .dt %M  .dd - if newsgroup not allowed for direct post (moderated for example)
-+  ** .dt %N  .dd N if newsgroup is new, u if unsubscribed, blank otherwise
-+  ** .dt %n  .dd number of new articles in newsgroup
-+  ** .dt %s  .dd number of unread articles in newsgroup
-+  ** .dt %>X .dd right justify the rest of the string and pad with character "X"
-+  ** .dt %|X .dd pad to the end of the line with character "X"
-+  ** .de
-+  */
-+#endif
-   { "hdr_format",     DT_SYN,  R_NONE, UL "index_format", 0 },
-   /*
-   */
-@@ -1445,6 +1497,7 @@ struct option_t MuttVars[] = {
-   ** .dt %E .dd number of messages in current thread
-   ** .dt %f .dd sender (address + real name), either From: or Return-Path:
-   ** .dt %F .dd author name, or recipient name if the message is from you
-+  ** .dt %g .dd newsgroup name (if compiled with NNTP support)
-   ** .dt %H .dd spam attribute(s) of this message
-   ** .dt %i .dd message-id of the current message
-   ** .dt %l .dd number of lines in the message (does not work with maildir,
-@@ -1468,6 +1521,8 @@ struct option_t MuttVars[] = {
-   ** .dt %T .dd the appropriate character from the $$to_chars string
-   ** .dt %u .dd user (login) name of the author
-   ** .dt %v .dd first name of the author, or the recipient if the message is from you
-+  ** .dt %W .dd name of organization of author (``Organization:'' field)
-+  ** .dt %x .dd ``X-Comment-To:'' field (if present and compiled with NNTP support)
-   ** .dt %X .dd number of attachments
-   **            (please see the ``$attachments'' section for possible speed effects)
-   ** .dt %y .dd ``X-Label:'' field, if present
-@@ -1506,6 +1561,25 @@ struct option_t MuttVars[] = {
-   ** Note that these expandos are supported in
-   ** ``$save-hook'', ``$fcc-hook'' and ``$fcc-save-hook'', too.
-   */
-+#ifdef USE_NNTP
-+  { "inews",          DT_PATH, R_NONE, UL &Inews, UL "" },
-+  /*
-+  ** .pp
-+  ** If set, specifies the program and arguments used to deliver news posted
-+  ** by Mutt.  Otherwise, mutt posts article using current connection to
-+  ** news server.  The following printf-style sequence is understood:
-+  ** .dl
-+  ** .dt %a .dd account url
-+  ** .dt %p .dd port
-+  ** .dt %P .dd port if specified
-+  ** .dt %s .dd news server name
-+  ** .dt %S .dd url schema
-+  ** .dt %u .dd username
-+  ** .de
-+  ** .pp
-+  ** Example: set inews="/usr/local/bin/inews -hS"
-+  */
-+#endif
-   { "ispell",         DT_PATH, R_NONE, UL &Ispell, UL ISPELL },
-   /*
-   ** .pp
-@@ -1793,6 +1867,15 @@ struct option_t MuttVars[] = {
-   ** When \fIset\fP, the $$mime_type_query_command will be run before the
-   ** mime.types lookup.
-   */
-+#ifdef USE_NNTP
-+  { "mime_subject",   DT_BOOL, R_NONE, OPTMIMESUBJECT, 1 },
-+  /*
-+  ** .pp
-+  ** If \fIunset\fP, 8-bit ``subject:'' line in article header will not be
-+  ** encoded according to RFC2047 to base64.  This is useful when message
-+  ** is Usenet article, because MIME for news is nonstandard feature.
-+  */
-+#endif
- #ifdef MIXMASTER
-   { "mix_entry_format", DT_STR,  R_NONE, UL &MixEntryFormat, UL "%4n %c %-16s %a" },
-   /*
-@@ -1847,6 +1930,106 @@ struct option_t MuttVars[] = {
-   ** See the $$status_format documentation for the values that can be formatted
-   ** into this command.
-   */
-+#ifdef USE_NNTP
-+  { "news_cache_dir", DT_PATH, R_NONE, UL &NewsCacheDir, UL "~/.mutt" },
-+  /*
-+  ** .pp
-+  ** This variable pointing to directory where Mutt will save cached news
-+  ** articles and headers in. If \fIunset\fP, articles and headers will not be
-+  ** saved at all and will be reloaded from the server each time.
-+  */
-+  { "news_server",    DT_STR, R_NONE, UL &NewsServer, 0 },
-+  /*
-+  ** .pp
-+  ** This variable specifies domain name or address of NNTP server. It
-+  ** defaults to the news server specified in the environment variable
-+  ** $$$NNTPSERVER or contained in the file /etc/nntpserver.  You can also
-+  ** specify username and an alternative port for each news server, ie:
-+  ** .pp
-+  ** [[s]news://][username[:password]@]server[:port]
-+  */
-+  { "newsgroups_charset", DT_STR, R_NONE, UL &NewsgroupsCharset, UL "utf-8" },
-+  /*
-+  ** .pp
-+  ** Character set of newsgroups descriptions.
-+  */
-+  { "newsrc",         DT_PATH, R_NONE, UL &NewsRc, UL "~/.newsrc" },
-+  /*
-+  ** .pp
-+  ** The file, containing info about subscribed newsgroups - names and
-+  ** indexes of read articles.  The following printf-style sequence
-+  ** is understood:
-+  ** .dl
-+  ** .dt %a .dd account url
-+  ** .dt %p .dd port
-+  ** .dt %P .dd port if specified
-+  ** .dt %s .dd news server name
-+  ** .dt %S .dd url schema
-+  ** .dt %u .dd username
-+  ** .de
-+  */
-+  { "nntp_authenticators", DT_STR, R_NONE, UL &NntpAuthenticators, UL 0 },
-+  /*
-+  ** .pp
-+  ** This is a colon-delimited list of authentication methods mutt may
-+  ** attempt to use to log in to a news server, in the order mutt should
-+  ** try them.  Authentication methods are either ``user'' or any
-+  ** SASL mechanism, e.g. ``digest-md5'', ``gssapi'' or ``cram-md5''.
-+  ** This option is case-insensitive.  If it's \fIunset\fP (the default)
-+  ** mutt will try all available methods, in order from most-secure to
-+  ** least-secure.
-+  ** .pp
-+  ** Example:
-+  ** .ts
-+  ** set nntp_authenticators="digest-md5:user"
-+  ** .te
-+  ** .pp
-+  ** \fBNote:\fP Mutt will only fall back to other authentication methods if
-+  ** the previous methods are unavailable. If a method is available but
-+  ** authentication fails, mutt will not connect to the IMAP server.
-+  */
-+  { "nntp_context",   DT_NUM, R_NONE, UL &NntpContext, 1000 },
-+  /*
-+  ** .pp
-+  ** This variable defines number of articles which will be in index when
-+  ** newsgroup entered.  If active newsgroup have more articles than this
-+  ** number, oldest articles will be ignored.  Also controls how many
-+  ** articles headers will be saved in cache when you quit newsgroup.
-+  */
-+  { "nntp_listgroup", DT_BOOL, R_NONE, OPTLISTGROUP, 1 },
-+  /*
-+  ** .pp
-+  ** This variable controls whether or not existence of each article is
-+  ** checked when newsgroup is entered.
-+  */
-+  { "nntp_load_description", DT_BOOL, R_NONE, OPTLOADDESC, 1 },
-+  /*
-+  ** .pp
-+  ** This variable controls whether or not descriptions for each newsgroup
-+  ** must be loaded when newsgroup is added to list (first time list
-+  ** loading or new newsgroup adding).
-+  */
-+  { "nntp_user",      DT_STR, R_NONE, UL &NntpUser, UL "" },
-+  /*
-+  ** .pp
-+  ** Your login name on the NNTP server.  If \fIunset\fP and NNTP server requires
-+  ** authentification, Mutt will prompt you for your account name when you
-+  ** connect to news server.
-+  */
-+  { "nntp_pass",      DT_STR, R_NONE, UL &NntpPass, UL "" },
-+  /*
-+  ** .pp
-+  ** Your password for NNTP account.
-+  */
-+  { "nntp_poll",      DT_NUM, R_NONE, UL &NewsPollTimeout, 60 },
-+  /*
-+  ** .pp
-+  ** The time in seconds until any operations on newsgroup except post new
-+  ** article will cause recheck for new news.  If set to 0, Mutt will
-+  ** recheck newsgroup on each operation in index (stepping, read article,
-+  ** etc.).
-+  */
-+#endif
-   { "pager",          DT_PATH, R_NONE, UL &Pager, UL "builtin" },
-   /*
-   ** .pp
-@@ -2395,6 +2578,16 @@ struct option_t MuttVars[] = {
-   { "post_indent_str",  DT_SYN,  R_NONE, UL "post_indent_string", 0 },
-   /*
-   */
-+#ifdef USE_NNTP
-+  { "post_moderated", DT_QUAD, R_NONE, OPT_TOMODERATED, MUTT_ASKYES },
-+  /*
-+  ** .pp
-+  ** If set to \fIyes\fP, Mutt will post article to newsgroup that have
-+  ** not permissions to posting (e.g. moderated).  \fBNote:\fP if news server
-+  ** does not support posting to that newsgroup or totally read-only, that
-+  ** posting will not have an effect.
-+  */
-+#endif
-   { "postpone",               DT_QUAD, R_NONE, OPT_POSTPONE, MUTT_ASKYES },
-   /*
-   ** .pp
-@@ -2875,6 +3068,28 @@ struct option_t MuttVars[] = {
-   ** Command to use when spawning a subshell.  By default, the user's login
-   ** shell from \fC/etc/passwd\fP is used.
-   */
-+#ifdef USE_NNTP
-+  { "save_unsubscribed", DT_BOOL, R_NONE, OPTSAVEUNSUB, 0 },
-+  /*
-+  ** .pp
-+  ** When \fIset\fP, info about unsubscribed newsgroups will be saved into
-+  ** ``newsrc'' file and into cache.
-+  */
-+  { "show_new_news",  DT_BOOL, R_NONE, OPTSHOWNEWNEWS, 1 },
-+  /*
-+  ** .pp
-+  ** If \fIset\fP, news server will be asked for new newsgroups on entering
-+  ** the browser.  Otherwise, it will be done only once for a news server.
-+  ** Also controls whether or not number of new articles of subscribed
-+  ** newsgroups will be then checked.
-+  */
-+  { "show_only_unread",       DT_BOOL, R_NONE, OPTSHOWONLYUNREAD, 0 },
-+  /*
-+  ** .pp
-+  ** If \fIset\fP, only subscribed newsgroups that contain unread articles
-+  ** will be displayed in browser.
-+  */
-+#endif
- #ifdef USE_SIDEBAR
-   { "sidebar_delim_chars", DT_STR, R_SIDEBAR, UL &SidebarDelimChars, UL "/." },
-   /*
-@@ -4005,6 +4220,14 @@ struct option_t MuttVars[] = {
-   {"xterm_set_titles",        DT_SYN,  R_NONE, UL "ts_enabled", 0 },
-   /*
-   */
-+#ifdef USE_NNTP
-+  { "x_comment_to",   DT_BOOL, R_NONE, OPTXCOMMENTTO, 0 },
-+  /*
-+  ** .pp
-+  ** If \fIset\fP, Mutt will add ``X-Comment-To:'' field (that contains full
-+  ** name of original article author) to article that followuped to newsgroup.
-+  */
-+#endif
-   /*--*/
-   { NULL, 0, 0, 0, 0 }
- };
-diff -udprP mutt-1.10.0.orig/keymap.c mutt-1.10.0/keymap.c
---- mutt-1.10.0.orig/keymap.c  2018-02-03 21:18:49.000000000 +0200
-+++ mutt-1.10.0/keymap.c       2018-06-16 17:22:30.198469667 +0300
-@@ -800,7 +800,6 @@ void km_init (void)
-   km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
-   km_bindkey ("x", MENU_PAGER, OP_EXIT);
--  km_bindkey ("i", MENU_PAGER, OP_EXIT);
-   km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE);
-   km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
-   km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE);
-diff -udprP mutt-1.10.0.orig/mailbox.h mutt-1.10.0/mailbox.h
---- mutt-1.10.0.orig/mailbox.h 2017-12-18 22:27:19.000000000 +0200
-+++ mutt-1.10.0/mailbox.h      2018-06-16 17:22:30.198469667 +0300
-@@ -78,6 +78,9 @@ int mx_is_imap (const char *);
- #ifdef USE_POP
- int mx_is_pop (const char *);
- #endif
-+#ifdef USE_NNTP
-+int mx_is_nntp (const char *);
-+#endif
- int mx_access (const char*, int);
- int mx_check_empty (const char *);
-diff -udprP mutt-1.10.0.orig/main.c mutt-1.10.0/main.c
---- mutt-1.10.0.orig/main.c    2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/main.c 2018-06-16 17:22:30.198469667 +0300
-@@ -65,6 +65,10 @@
- #include <idn/stringprep.h>
- #endif
-+#ifdef USE_NNTP
-+#include "nntp.h"
-+#endif
-+
- static const char *ReachingUs = N_("\
- To contact the developers, please mail to <mutt-dev@mutt.org>.\n\
- To report a bug, please contact the Mutt maintainers via gitlab:\n\
-@@ -145,6 +149,8 @@ options:\n\
-   -e <command>\tspecify a command to be executed after initialization\n\
-   -f <file>\tspecify which mailbox to read\n\
-   -F <file>\tspecify an alternate muttrc file\n\
-+  -g <server>\tspecify a news server (if compiled with NNTP)\n\
-+  -G\t\tselect a newsgroup (if compiled with NNTP)\n\
-   -H <file>\tspecify a draft file to read header and body from\n\
-   -i <file>\tspecify a file which Mutt should include in the body\n\
-   -m <type>\tspecify a default mailbox type\n\
-@@ -298,6 +304,12 @@ static void show_version (void)
-       "-USE_POP  "
- #endif
-+#ifdef USE_NNTP
-+      "+USE_NNTP  "
-+#else
-+      "-USE_NNTP  "
-+#endif
-+
- #ifdef USE_IMAP
-         "+USE_IMAP  "
- #else
-@@ -593,6 +605,7 @@ init_extended_keys();
- #define MUTT_NOSYSRC (1<<2)   /* -n */
- #define MUTT_RO      (1<<3)   /* -R */
- #define MUTT_SELECT  (1<<4)   /* -y */
-+#define MUTT_NEWS    (1<<5)   /* -g and -G */
- int main (int argc, char **argv, char **environ)
- {
-@@ -682,7 +695,11 @@ int main (int argc, char **argv, char **
-         argv[nargc++] = argv[optind];
-     }
-+#ifdef USE_NNTP
-+    if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:Ee:g:GH:s:i:hm:npQ:RvxyzZ")) != EOF)
-+#else
-     if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:Ee:H:s:i:hm:npQ:RvxyzZ")) != EOF)
-+#endif
-       switch (i)
-       {
-       case 'A':
-@@ -783,6 +800,20 @@ int main (int argc, char **argv, char **
-       flags |= MUTT_SELECT;
-       break;
-+#ifdef USE_NNTP
-+      case 'g': /* Specify a news server */
-+      {
-+        char buf[LONG_STRING];
-+
-+        snprintf (buf, sizeof (buf), "set news_server=%s", optarg);
-+        commands = mutt_add_list (commands, buf);
-+      }
-+
-+      case 'G': /* List of newsgroups */
-+      flags |= MUTT_SELECT | MUTT_NEWS;
-+      break;
-+#endif
-+
-       case 'z':
-       flags |= MUTT_IGNORE;
-       break;
-@@ -1229,6 +1260,18 @@ int main (int argc, char **argv, char **
-     }
-     else if (flags & MUTT_SELECT)
-     {
-+#ifdef USE_NNTP
-+      if (flags & MUTT_NEWS)
-+      {
-+      set_option (OPTNEWS);
-+      if(!(CurrentNewsSrv = nntp_select_server (NewsServer, 0)))
-+      {
-+        mutt_endwin (Errorbuf);
-+        exit (1);
-+      }
-+      }
-+      else
-+#endif
-       if (!Incoming)
-       {
-         exit_endwin_msg = _("No incoming mailboxes defined.");
-@@ -1244,6 +1287,15 @@ int main (int argc, char **argv, char **
-     if (!folder[0])
-       strfcpy (folder, NONULL(Spoolfile), sizeof (folder));
-+
-+#ifdef USE_NNTP
-+    if (option (OPTNEWS))
-+    {
-+      unset_option (OPTNEWS);
-+      nntp_expand_path (folder, sizeof (folder), &CurrentNewsSrv->conn->account);
-+    }
-+    else
-+#endif
-     mutt_expand_path (folder, sizeof (folder));
-     mutt_str_replace (&CurrentFolder, folder);
-diff -udprP mutt-1.10.0.orig/mutt.h mutt-1.10.0/mutt.h
---- mutt-1.10.0.orig/mutt.h    2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/mutt.h 2018-06-16 17:23:34.827448604 +0300
-@@ -254,6 +254,9 @@ enum
-   MUTT_XLABEL,
-   MUTT_MIMEATTACH,
-   MUTT_MIMETYPE,
-+#ifdef USE_NNTP
-+  MUTT_NEWSGROUPS,
-+#endif
-   
-   /* Options for Mailcap lookup */
-   MUTT_EDIT,
-@@ -311,6 +314,11 @@ enum
- #endif
-   OPT_SUBJECT,
-   OPT_VERIFYSIG,      /* verify PGP signatures */
-+#ifdef USE_NNTP
-+  OPT_TOMODERATED,
-+  OPT_CATCHUP,
-+  OPT_FOLLOWUPTOPOSTER,
-+#endif
-     
-   /* THIS MUST BE THE LAST VALUE. */
-   OPT_MAX
-@@ -329,6 +337,7 @@ enum
- #define SENDNOFREEHEADER      (1<<10)   /* Used by the -E flag */
- #define SENDDRAFTFILE         (1<<11)   /* Used by the -H flag */
- #define SENDTOSENDER    (1<<12)
-+#define SENDNEWS      (1<<13)
- /* flags for mutt_compose_menu() */
- #define MUTT_COMPOSE_NOFREEHEADER (1<<0)
-@@ -351,6 +360,8 @@ enum
-   OPTASCIICHARS,
-   OPTASKBCC,
-   OPTASKCC,
-+  OPTASKFOLLOWUP,
-+  OPTASKXCOMMENTTO,
-   OPTATTACHSPLIT,
-   OPTAUTOEDIT,
-   OPTAUTOTAG,
-@@ -441,6 +452,9 @@ enum
-   OPTMETOO,
-   OPTMHPURGE,
-   OPTMIMEFORWDECODE,
-+#ifdef USE_NNTP
-+  OPTMIMESUBJECT,     /* encode subject line with RFC2047 */
-+#endif
-   OPTMIMETYPEQUERYFIRST,
-   OPTNARROWTREE,
-   OPTPAGERSTOP,
-@@ -542,6 +556,17 @@ enum
-   OPTPGPAUTOINLINE,
-   OPTPGPREPLYINLINE,
-+  /* news options */
-+
-+#ifdef USE_NNTP
-+  OPTSHOWNEWNEWS,
-+  OPTSHOWONLYUNREAD,
-+  OPTSAVEUNSUB,
-+  OPTLISTGROUP,
-+  OPTLOADDESC,
-+  OPTXCOMMENTTO,
-+#endif
-+
-   /* pseudo options */
-   OPTAUXSORT,         /* (pseudo) using auxiliary sort function */
-@@ -559,6 +584,7 @@ enum
-   OPTSORTSUBTHREADS,  /* (pseudo) used when $sort_aux changes */
-   OPTNEEDRESCORE,     /* (pseudo) set when the `score' command is used */
-   OPTATTACHMSG,               /* (pseudo) used by attach-message */
-+  OPTHIDEREAD,                /* (pseudo) whether or not hide read messages */
-   OPTKEEPQUIET,               /* (pseudo) shut up the message and refresh
-                        *          functions while we are executing an
-                        *          external program.
-@@ -569,6 +595,11 @@ enum
-   OPTDONTHANDLEPGPKEYS,       /* (pseudo) used to extract PGP keys */
-   OPTIGNOREMACROEVENTS, /* (pseudo) don't process macro/push/exec events while set */
-+#ifdef USE_NNTP
-+  OPTNEWS,            /* (pseudo) used to change reader mode */
-+  OPTNEWSSEND,                /* (pseudo) used to change behavior when posting */
-+#endif
-+
-   OPTMAX
- };
-@@ -649,6 +680,13 @@ typedef struct envelope
-   char *supersedes;
-   char *date;
-   char *x_label;
-+  char *organization;
-+#ifdef USE_NNTP
-+  char *newsgroups;
-+  char *xref;
-+  char *followup_to;
-+  char *x_comment_to;
-+#endif
-   BUFFER *spam;
-   LIST *references;           /* message references (in reverse order) */
-   LIST *in_reply_to;          /* in-reply-to header content */
-@@ -836,7 +874,7 @@ typedef struct header
-   int refno;                  /* message number on server */
- #endif
--#if defined USE_POP || defined USE_IMAP
-+#if defined USE_POP || defined USE_IMAP || defined USE_NNTP
-   void *data;                 /* driver-specific data */
- #endif
-   
-diff -udprP mutt-1.10.0.orig/mutt_sasl.c mutt-1.10.0/mutt_sasl.c
---- mutt-1.10.0.orig/mutt_sasl.c       2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/mutt_sasl.c    2018-06-16 17:22:30.199469651 +0300
-@@ -192,6 +192,11 @@ int mutt_sasl_client_new (CONNECTION* co
-     case MUTT_ACCT_TYPE_SMTP:
-       service = "smtp";
-       break;
-+#ifdef USE_NNTP
-+    case MUTT_ACCT_TYPE_NNTP:
-+      service = "nntp";
-+      break;
-+#endif
-     default:
-       mutt_error (_("Unknown SASL profile"));
-       return -1;
-diff -udprP mutt-1.10.0.orig/muttlib.c mutt-1.10.0/muttlib.c
---- mutt-1.10.0.orig/muttlib.c 2018-04-17 02:31:03.000000000 +0300
-+++ mutt-1.10.0/muttlib.c      2018-06-16 17:22:30.199469651 +0300
-@@ -354,7 +354,7 @@ void mutt_free_header (HEADER **h)
- #ifdef MIXMASTER
-   mutt_free_list (&(*h)->chain);
- #endif
--#if defined USE_POP || defined USE_IMAP
-+#if defined USE_POP || defined USE_IMAP || defined USE_NNTP
-   FREE (&(*h)->data);
- #endif
-   FREE (h);           /* __FREE_CHECKED__ */
-@@ -740,6 +740,13 @@ void mutt_free_envelope (ENVELOPE **p)
-   FREE (&(*p)->supersedes);
-   FREE (&(*p)->date);
-   FREE (&(*p)->x_label);
-+  FREE (&(*p)->organization);
-+#ifdef USE_NNTP
-+  FREE (&(*p)->newsgroups);
-+  FREE (&(*p)->xref);
-+  FREE (&(*p)->followup_to);
-+  FREE (&(*p)->x_comment_to);
-+#endif
-   mutt_buffer_free (&(*p)->spam);
-@@ -1663,6 +1670,14 @@ int mutt_save_confirm (const char *s, st
-     }
-   }
-+#ifdef USE_NNTP
-+  if (magic == MUTT_NNTP)
-+  {
-+    mutt_error _("Can't save message to news server.");
-+    return 0;
-+  }
-+#endif
-+
-   if (stat (s, st) != -1)
-   {
-     if (magic == -1)
-diff -udprP mutt-1.10.0.orig/mx.c mutt-1.10.0/mx.c
---- mutt-1.10.0.orig/mx.c      2018-04-17 02:31:03.000000000 +0300
-+++ mutt-1.10.0/mx.c   2018-06-16 17:22:30.199469651 +0300
-@@ -45,6 +45,10 @@
- #include "pop.h"
- #endif
-+#ifdef USE_NNTP
-+#include "nntp.h"
-+#endif
-+
- #include "buffy.h"
- #ifdef USE_DOTLOCK
-@@ -88,6 +92,10 @@ struct mx_ops* mx_get_ops (int magic)
-     case MUTT_COMPRESSED:
-       return &mx_comp_ops;
- #endif
-+#ifdef USE_NNTP
-+    case MUTT_NNTP:
-+      return &mx_nntp_ops;
-+#endif
-     default:
-       return NULL;
-   }
-@@ -378,6 +386,22 @@ int mx_is_pop (const char *p)
- }
- #endif
-+#ifdef USE_NNTP
-+int mx_is_nntp (const char *p)
-+{
-+  url_scheme_t scheme;
-+
-+  if (!p)
-+    return 0;
-+
-+  scheme = url_check_scheme (p);
-+  if (scheme == U_NNTP || scheme == U_NNTPS)
-+    return 1;
-+
-+  return 0;
-+}
-+#endif
-+
- int mx_get_magic (const char *path)
- {
-   struct stat st;
-@@ -395,6 +419,11 @@ int mx_get_magic (const char *path)
-     return MUTT_POP;
- #endif /* USE_POP */
-+#ifdef USE_NNTP
-+  if (mx_is_nntp (path))
-+    return MUTT_NNTP;
-+#endif /* USE_NNTP */
-+
-   if (stat (path, &st) == -1)
-   {
-     dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n",
-@@ -803,6 +832,25 @@ int mx_close_mailbox (CONTEXT *ctx, int
-     return 0;
-   }
-+#ifdef USE_NNTP
-+  if (ctx->unread && ctx->magic == MUTT_NNTP)
-+  {
-+    NNTP_DATA *nntp_data = ctx->data;
-+
-+    if (nntp_data && nntp_data->nserv && nntp_data->group)
-+    {
-+      int rc = query_quadoption (OPT_CATCHUP, _("Mark all articles read?"));
-+      if (rc < 0)
-+      {
-+      ctx->closing = 0;
-+      return -1;
-+      }
-+      else if (rc == MUTT_YES)
-+      mutt_newsgroup_catchup (nntp_data->nserv, nntp_data->group);
-+    }
-+  }
-+#endif
-+
-   for (i = 0; i < ctx->msgcount; i++)
-   {
-     if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read 
-@@ -810,6 +858,12 @@ int mx_close_mailbox (CONTEXT *ctx, int
-       read_msgs++;
-   }
-+#ifdef USE_NNTP
-+  /* don't need to move articles from newsgroup */
-+  if (ctx->magic == MUTT_NNTP)
-+    read_msgs = 0;
-+#endif
-+
-   if (read_msgs && quadoption (OPT_MOVE) != MUTT_NO)
-   {
-     char *p;
-diff -udprP mutt-1.10.0.orig/mx.h mutt-1.10.0/mx.h
---- mutt-1.10.0.orig/mx.h      2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/mx.h   2018-06-16 17:22:30.199469651 +0300
-@@ -35,6 +35,9 @@ enum
-   MUTT_MMDF,
-   MUTT_MH,
-   MUTT_MAILDIR,
-+#ifdef USE_NNTP
-+  MUTT_NNTP,
-+#endif
-   MUTT_IMAP,
-   MUTT_POP
- #ifdef USE_COMPRESSED
-diff -udprP mutt-1.10.0.orig/newsrc.c mutt-1.10.0/newsrc.c
---- mutt-1.10.0.orig/newsrc.c  1970-01-01 03:00:00.000000000 +0300
-+++ mutt-1.10.0/newsrc.c       2018-06-16 17:22:30.200469636 +0300
-@@ -0,0 +1,1259 @@
-+/*
-+ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
-+ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
-+ * Copyright (C) 2000-2017 Vsevolod Volkov <vvv@mutt.org.ua>
-+ *
-+ *     This program is free software; you can redistribute it and/or modify
-+ *     it under the terms of the GNU General Public License as published by
-+ *     the Free Software Foundation; either version 2 of the License, or
-+ *     (at your option) any later version.
-+ *
-+ *     This program is distributed in the hope that it will be useful,
-+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *     GNU General Public License for more details.
-+ *
-+ *     You should have received a copy of the GNU General Public License
-+ *     along with this program; if not, write to the Free Software
-+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#if HAVE_CONFIG_H
-+#include "config.h"
-+#endif
-+
-+#include "mutt.h"
-+#include "mutt_curses.h"
-+#include "sort.h"
-+#include "mx.h"
-+#include "mime.h"
-+#include "mailbox.h"
-+#include "nntp.h"
-+#include "rfc822.h"
-+#include "rfc1524.h"
-+#include "rfc2047.h"
-+#include "bcache.h"
-+
-+#if USE_HCACHE
-+#include "hcache.h"
-+#endif
-+
-+#include <unistd.h>
-+#include <string.h>
-+#include <ctype.h>
-+#include <stdlib.h>
-+#include <sys/stat.h>
-+#include <sys/types.h>
-+#include <dirent.h>
-+#include <errno.h>
-+
-+/* Find NNTP_DATA for given newsgroup or add it */
-+static NNTP_DATA *nntp_data_find (NNTP_SERVER *nserv, const char *group)
-+{
-+  NNTP_DATA *nntp_data = hash_find (nserv->groups_hash, group);
-+
-+  if (!nntp_data)
-+  {
-+    /* create NNTP_DATA structure and add it to hash */
-+    nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1);
-+    nntp_data->group = (char *)nntp_data + sizeof (NNTP_DATA);
-+    strcpy (nntp_data->group, group);
-+    nntp_data->nserv = nserv;
-+    nntp_data->deleted = 1;
-+    hash_insert (nserv->groups_hash, nntp_data->group, nntp_data);
-+
-+    /* add NNTP_DATA to list */
-+    if (nserv->groups_num >= nserv->groups_max)
-+    {
-+      nserv->groups_max *= 2;
-+      safe_realloc (&nserv->groups_list,
-+                  nserv->groups_max * sizeof (nntp_data));
-+    }
-+    nserv->groups_list[nserv->groups_num++] = nntp_data;
-+  }
-+  return nntp_data;
-+}
-+
-+/* Remove all temporarily cache files */
-+void nntp_acache_free (NNTP_DATA *nntp_data)
-+{
-+  int i;
-+
-+  for (i = 0; i < NNTP_ACACHE_LEN; i++)
-+  {
-+    if (nntp_data->acache[i].path)
-+    {
-+      unlink (nntp_data->acache[i].path);
-+      FREE (&nntp_data->acache[i].path);
-+    }
-+  }
-+}
-+
-+/* Free NNTP_DATA, used to destroy hash elements */
-+void nntp_data_free (void *data)
-+{
-+  NNTP_DATA *nntp_data = data;
-+
-+  if (!nntp_data)
-+    return;
-+  nntp_acache_free (nntp_data);
-+  mutt_bcache_close (&nntp_data->bcache);
-+  FREE (&nntp_data->newsrc_ent);
-+  FREE (&nntp_data->desc);
-+  FREE (&data);
-+}
-+
-+/* Unlock and close .newsrc file */
-+void nntp_newsrc_close (NNTP_SERVER *nserv)
-+{
-+  if (!nserv->newsrc_fp)
-+    return;
-+
-+  dprint (1, (debugfile, "Unlocking %s\n", nserv->newsrc_file));
-+  mx_unlock_file (nserv->newsrc_file, fileno (nserv->newsrc_fp), 0);
-+  safe_fclose (&nserv->newsrc_fp);
-+}
-+
-+/* Parse .newsrc file:
-+ *  0 - not changed
-+ *  1 - parsed
-+ * -1 - error */
-+int nntp_newsrc_parse (NNTP_SERVER *nserv)
-+{
-+  unsigned int i;
-+  char *line;
-+  struct stat sb;
-+
-+  /* if file doesn't exist, create it */
-+  nserv->newsrc_fp = safe_fopen (nserv->newsrc_file, "a");
-+  safe_fclose (&nserv->newsrc_fp);
-+
-+  /* open .newsrc */
-+  nserv->newsrc_fp = safe_fopen (nserv->newsrc_file, "r");
-+  if (!nserv->newsrc_fp)
-+  {
-+    mutt_perror (nserv->newsrc_file);
-+    mutt_sleep (2);
-+    return -1;
-+  }
-+
-+  /* lock it */
-+  dprint (1, (debugfile, "Locking %s\n", nserv->newsrc_file));
-+  if (mx_lock_file (nserv->newsrc_file, fileno (nserv->newsrc_fp), 0, 0, 1))
-+  {
-+    safe_fclose (&nserv->newsrc_fp);
-+    return -1;
-+  }
-+
-+  if (stat (nserv->newsrc_file, &sb))
-+  {
-+    mutt_perror (nserv->newsrc_file);
-+    nntp_newsrc_close (nserv);
-+    mutt_sleep (2);
-+    return -1;
-+  }
-+
-+  if (nserv->size == sb.st_size && nserv->mtime == sb.st_mtime)
-+    return 0;
-+
-+  nserv->size = sb.st_size;
-+  nserv->mtime = sb.st_mtime;
-+  nserv->newsrc_modified = 1;
-+  dprint (1, (debugfile, "Parsing %s\n", nserv->newsrc_file));
-+
-+  /* .newsrc has been externally modified or hasn't been loaded yet */
-+  for (i = 0; i < nserv->groups_num; i++)
-+  {
-+    NNTP_DATA *nntp_data = nserv->groups_list[i];
-+
-+    if (!nntp_data)
-+      continue;
-+
-+    nntp_data->subscribed = 0;
-+    nntp_data->newsrc_len = 0;
-+    FREE (&nntp_data->newsrc_ent);
-+  }
-+
-+  line = safe_malloc (sb.st_size + 1);
-+  while (sb.st_size && fgets (line, sb.st_size + 1, nserv->newsrc_fp))
-+  {
-+    char *b, *h, *p;
-+    unsigned int subs = 0, i = 1;
-+    NNTP_DATA *nntp_data;
-+
-+    /* find end of newsgroup name */
-+    p = strpbrk (line, ":!");
-+    if (!p)
-+      continue;
-+
-+    /* ":" - subscribed, "!" - unsubscribed */
-+    if (*p == ':')
-+      subs++;
-+    *p++ = '\0';
-+
-+    /* get newsgroup data */
-+    nntp_data = nntp_data_find (nserv, line);
-+    FREE (&nntp_data->newsrc_ent);
-+
-+    /* count number of entries */
-+    b = p;
-+    while (*b)
-+      if (*b++ == ',')
-+      i++;
-+    nntp_data->newsrc_ent = safe_calloc (i, sizeof (NEWSRC_ENTRY));
-+    nntp_data->subscribed = subs;
-+
-+    /* parse entries */
-+    i = 0;
-+    while (p)
-+    {
-+      b = p;
-+
-+      /* find end of entry */
-+      p = strchr (p, ',');
-+      if (p)
-+      *p++ = '\0';
-+
-+      /* first-last or single number */
-+      h = strchr (b, '-');
-+      if (h)
-+      *h++ = '\0';
-+      else
-+      h = b;
-+
-+      if (sscanf (b, ANUM, &nntp_data->newsrc_ent[i].first) == 1 &&
-+        sscanf (h, ANUM, &nntp_data->newsrc_ent[i].last) == 1)
-+      i++;
-+    }
-+    if (i == 0)
-+    {
-+      nntp_data->newsrc_ent[i].first = 1;
-+      nntp_data->newsrc_ent[i].last = 0;
-+      i++;
-+    }
-+    if (nntp_data->lastMessage == 0)
-+      nntp_data->lastMessage = nntp_data->newsrc_ent[i - 1].last;
-+    nntp_data->newsrc_len = i;
-+    safe_realloc (&nntp_data->newsrc_ent, i * sizeof (NEWSRC_ENTRY));
-+    nntp_group_unread_stat (nntp_data);
-+    dprint (2, (debugfile, "nntp_newsrc_parse: %s\n", nntp_data->group));
-+  }
-+  FREE (&line);
-+  return 1;
-+}
-+
-+/* Generate array of .newsrc entries */
-+void nntp_newsrc_gen_entries (CONTEXT *ctx)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  anum_t last = 0, first = 1;
-+  int series, i;
-+  int save_sort = SORT_ORDER;
-+  unsigned int entries;
-+
-+  if (Sort != SORT_ORDER)
-+  {
-+    save_sort = Sort;
-+    Sort = SORT_ORDER;
-+    mutt_sort_headers (ctx, 0);
-+  }
-+
-+  entries = nntp_data->newsrc_len;
-+  if (!entries)
-+  {
-+    entries = 5;
-+    nntp_data->newsrc_ent = safe_calloc (entries, sizeof (NEWSRC_ENTRY));
-+  }
-+
-+  /* Set up to fake initial sequence from 1 to the article before the
-+   * first article in our list */
-+  nntp_data->newsrc_len = 0;
-+  series = 1;
-+  for (i = 0; i < ctx->msgcount; i++)
-+  {
-+    /* search for first unread */
-+    if (series)
-+    {
-+      /* We don't actually check sequential order, since we mark
-+       * "missing" entries as read/deleted */
-+      last = NHDR (ctx->hdrs[i])->article_num;
-+      if (last >= nntp_data->firstMessage && !ctx->hdrs[i]->deleted &&
-+        !ctx->hdrs[i]->read)
-+      {
-+      if (nntp_data->newsrc_len >= entries)
-+      {
-+        entries *= 2;
-+        safe_realloc (&nntp_data->newsrc_ent, entries * sizeof (NEWSRC_ENTRY));
-+      }
-+      nntp_data->newsrc_ent[nntp_data->newsrc_len].first = first;
-+      nntp_data->newsrc_ent[nntp_data->newsrc_len].last = last - 1;
-+      nntp_data->newsrc_len++;
-+      series = 0;
-+      }
-+    }
-+
-+    /* search for first read */
-+    else
-+    {
-+      if (ctx->hdrs[i]->deleted || ctx->hdrs[i]->read)
-+      {
-+      first = last + 1;
-+      series = 1;
-+      }
-+      last = NHDR (ctx->hdrs[i])->article_num;
-+    }
-+  }
-+
-+  if (series && first <= nntp_data->lastLoaded)
-+  {
-+    if (nntp_data->newsrc_len >= entries)
-+    {
-+      entries++;
-+      safe_realloc (&nntp_data->newsrc_ent, entries * sizeof (NEWSRC_ENTRY));
-+    }
-+    nntp_data->newsrc_ent[nntp_data->newsrc_len].first = first;
-+    nntp_data->newsrc_ent[nntp_data->newsrc_len].last = nntp_data->lastLoaded;
-+    nntp_data->newsrc_len++;
-+  }
-+  safe_realloc (&nntp_data->newsrc_ent,
-+              nntp_data->newsrc_len * sizeof (NEWSRC_ENTRY));
-+
-+  if (save_sort != Sort)
-+  {
-+    Sort = save_sort;
-+    mutt_sort_headers (ctx, 0);
-+  }
-+}
-+
-+/* Update file with new contents */
-+static int update_file (char *filename, char *buf)
-+{
-+  FILE *fp;
-+  char tmpfile[_POSIX_PATH_MAX];
-+  int rc = -1;
-+
-+  while (1)
-+  {
-+    snprintf (tmpfile, sizeof (tmpfile), "%s.tmp", filename);
-+    fp = fopen (tmpfile, "w");
-+    if (!fp)
-+    {
-+      mutt_perror (tmpfile);
-+      *tmpfile = '\0';
-+      break;
-+    }
-+    if (fputs (buf, fp) == EOF)
-+    {
-+      mutt_perror (tmpfile);
-+      break;
-+    }
-+    if (fclose (fp) == EOF)
-+    {
-+      mutt_perror (tmpfile);
-+      fp = NULL;
-+      break;
-+    }
-+    fp = NULL;
-+    if (rename (tmpfile, filename) < 0)
-+    {
-+      mutt_perror (filename);
-+      break;
-+    }
-+    *tmpfile = '\0';
-+    rc = 0;
-+    break;
-+  }
-+  if (fp)
-+    fclose (fp);
-+  if (*tmpfile)
-+    unlink (tmpfile);
-+  if (rc)
-+    mutt_sleep (2);
-+  return rc;
-+}
-+
-+/* Update .newsrc file */
-+int nntp_newsrc_update (NNTP_SERVER *nserv)
-+{
-+  char *buf;
-+  size_t buflen, off;
-+  unsigned int i;
-+  int rc = -1;
-+
-+  if (!nserv)
-+    return -1;
-+
-+  buflen = 10 * LONG_STRING;
-+  buf = safe_calloc (1, buflen);
-+  off = 0;
-+
-+  /* we will generate full newsrc here */
-+  for (i = 0; i < nserv->groups_num; i++)
-+  {
-+    NNTP_DATA *nntp_data = nserv->groups_list[i];
-+    unsigned int n;
-+
-+    if (!nntp_data || !nntp_data->newsrc_ent)
-+      continue;
-+
-+    /* write newsgroup name */
-+    if (off + strlen (nntp_data->group) + 3 > buflen)
-+    {
-+      buflen *= 2;
-+      safe_realloc (&buf, buflen);
-+    }
-+    snprintf (buf + off, buflen - off, "%s%c ", nntp_data->group,
-+            nntp_data->subscribed ? ':' : '!');
-+    off += strlen (buf + off);
-+
-+    /* write entries */
-+    for (n = 0; n < nntp_data->newsrc_len; n++)
-+    {
-+      if (off + LONG_STRING > buflen)
-+      {
-+      buflen *= 2;
-+      safe_realloc (&buf, buflen);
-+      }
-+      if (n)
-+      buf[off++] = ',';
-+      if (nntp_data->newsrc_ent[n].first == nntp_data->newsrc_ent[n].last)
-+      snprintf (buf + off, buflen - off, "%d", nntp_data->newsrc_ent[n].first);
-+      else if (nntp_data->newsrc_ent[n].first < nntp_data->newsrc_ent[n].last)
-+      snprintf (buf + off, buflen - off, "%d-%d",
-+                nntp_data->newsrc_ent[n].first, nntp_data->newsrc_ent[n].last);
-+      off += strlen (buf + off);
-+    }
-+    buf[off++] = '\n';
-+  }
-+  buf[off] = '\0';
-+
-+  /* newrc being fully rewritten */
-+  dprint (1, (debugfile, "Updating %s\n", nserv->newsrc_file));
-+  if (nserv->newsrc_file && update_file (nserv->newsrc_file, buf) == 0)
-+  {
-+    struct stat sb;
-+
-+    rc = stat (nserv->newsrc_file, &sb);
-+    if (rc == 0)
-+    {
-+      nserv->size = sb.st_size;
-+      nserv->mtime = sb.st_mtime;
-+    }
-+    else
-+    {
-+      mutt_perror (nserv->newsrc_file);
-+      mutt_sleep (2);
-+    }
-+  }
-+  FREE (&buf);
-+  return rc;
-+}
-+
-+/* Make fully qualified cache file name */
-+static void cache_expand (char *dst, size_t dstlen, ACCOUNT *acct, char *src)
-+{
-+  char *c;
-+  char file[_POSIX_PATH_MAX];
-+
-+  /* server subdirectory */
-+  if (acct)
-+  {
-+    ciss_url_t url;
-+
-+    mutt_account_tourl (acct, &url);
-+    url.path = src;
-+    url_ciss_tostring (&url, file, sizeof (file), U_PATH);
-+  }
-+  else
-+    strfcpy (file, src ? src : "", sizeof (file));
-+
-+  snprintf (dst, dstlen, "%s/%s", NewsCacheDir, file);
-+
-+  /* remove trailing slash */
-+  c = dst + strlen (dst) - 1;
-+  if (*c == '/')
-+    *c = '\0';
-+  mutt_expand_path (dst, dstlen);
-+}
-+
-+/* Make fully qualified url from newsgroup name */
-+void nntp_expand_path (char *line, size_t len, ACCOUNT *acct)
-+{
-+  ciss_url_t url;
-+
-+  url.path = safe_strdup (line);
-+  mutt_account_tourl (acct, &url);
-+  url_ciss_tostring (&url, line, len, 0);
-+  FREE (&url.path);
-+}
-+
-+/* Parse newsgroup */
-+int nntp_add_group (char *line, void *data)
-+{
-+  NNTP_SERVER *nserv = data;
-+  NNTP_DATA *nntp_data;
-+  char group[LONG_STRING];
-+  char desc[HUGE_STRING] = "";
-+  char mod;
-+  anum_t first, last;
-+
-+  if (!nserv || !line)
-+    return 0;
-+
-+  if (sscanf (line, "%s " ANUM " " ANUM " %c %[^\n]", group,
-+            &last, &first, &mod, desc) < 4)
-+    return 0;
-+
-+  nntp_data = nntp_data_find (nserv, group);
-+  nntp_data->deleted = 0;
-+  nntp_data->firstMessage = first;
-+  nntp_data->lastMessage = last;
-+  nntp_data->allowed = mod == 'y' || mod == 'm' ? 1 : 0;
-+  mutt_str_replace (&nntp_data->desc, desc);
-+  if (nntp_data->newsrc_ent || nntp_data->lastCached)
-+    nntp_group_unread_stat (nntp_data);
-+  else if (nntp_data->lastMessage &&
-+         nntp_data->firstMessage <= nntp_data->lastMessage)
-+    nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
-+  else
-+    nntp_data->unread = 0;
-+  return 0;
-+}
-+
-+/* Load list of all newsgroups from cache */
-+static int active_get_cache (NNTP_SERVER *nserv)
-+{
-+  char buf[HUGE_STRING];
-+  char file[_POSIX_PATH_MAX];
-+  time_t t;
-+  FILE *fp;
-+
-+  cache_expand (file, sizeof (file), &nserv->conn->account, ".active");
-+  dprint (1, (debugfile, "Parsing %s\n", file));
-+  fp = safe_fopen (file, "r");
-+  if (!fp)
-+    return -1;
-+
-+  if (fgets (buf, sizeof (buf), fp) == NULL ||
-+      sscanf (buf, "%ld%s", &t, file) != 1 || t == 0)
-+  {
-+    fclose (fp);
-+    return -1;
-+  }
-+  nserv->newgroups_time = t;
-+
-+  mutt_message _("Loading list of groups from cache...");
-+  while (fgets (buf, sizeof (buf), fp))
-+    nntp_add_group (buf, nserv);
-+  nntp_add_group (NULL, NULL);
-+  fclose (fp);
-+  mutt_clear_error ();
-+  return 0;
-+}
-+
-+/* Save list of all newsgroups to cache */
-+int nntp_active_save_cache (NNTP_SERVER *nserv)
-+{
-+  char file[_POSIX_PATH_MAX];
-+  char *buf;
-+  size_t buflen, off;
-+  unsigned int i;
-+  int rc;
-+
-+  if (!nserv->cacheable)
-+    return 0;
-+
-+  buflen = 10 * LONG_STRING;
-+  buf = safe_calloc (1, buflen);
-+  snprintf (buf, buflen, "%lu\n", (unsigned long)nserv->newgroups_time);
-+  off = strlen (buf);
-+
-+  for (i = 0; i < nserv->groups_num; i++)
-+  {
-+    NNTP_DATA *nntp_data = nserv->groups_list[i];
-+
-+    if (!nntp_data || nntp_data->deleted)
-+      continue;
-+
-+    if (off + strlen (nntp_data->group) +
-+      (nntp_data->desc ? strlen (nntp_data->desc) : 0) + 50 > buflen)
-+    {
-+      buflen *= 2;
-+      safe_realloc (&buf, buflen);
-+    }
-+    snprintf (buf + off, buflen - off, "%s %d %d %c%s%s\n", nntp_data->group,
-+            nntp_data->lastMessage, nntp_data->firstMessage,
-+            nntp_data->allowed ? 'y' : 'n', nntp_data->desc ? " " : "",
-+            nntp_data->desc ? nntp_data->desc : "");
-+    off += strlen (buf + off);
-+  }
-+
-+  cache_expand (file, sizeof (file), &nserv->conn->account, ".active");
-+  dprint (1, (debugfile, "Updating %s\n", file));
-+  rc = update_file (file, buf);
-+  FREE (&buf);
-+  return rc;
-+}
-+
-+#ifdef USE_HCACHE
-+/* Used by mutt_hcache_open() to compose hcache file name */
-+static int nntp_hcache_namer (const char *path, char *dest, size_t destlen)
-+{
-+  return snprintf (dest, destlen, "%s.hcache", path);
-+}
-+
-+/* Open newsgroup hcache */
-+header_cache_t *nntp_hcache_open (NNTP_DATA *nntp_data)
-+{
-+  ciss_url_t url;
-+  char file[_POSIX_PATH_MAX];
-+
-+  if (!nntp_data->nserv || !nntp_data->nserv->cacheable ||
-+      !nntp_data->nserv->conn || !nntp_data->group ||
-+      !(nntp_data->newsrc_ent || nntp_data->subscribed ||
-+      option (OPTSAVEUNSUB)))
-+    return NULL;
-+
-+  mutt_account_tourl (&nntp_data->nserv->conn->account, &url);
-+  url.path = nntp_data->group;
-+  url_ciss_tostring (&url, file, sizeof (file), U_PATH);
-+  return mutt_hcache_open (NewsCacheDir, file, nntp_hcache_namer);
-+}
-+
-+/* Remove stale cached headers */
-+void nntp_hcache_update (NNTP_DATA *nntp_data, header_cache_t *hc)
-+{
-+  char buf[16];
-+  int old = 0;
-+  void *hdata;
-+  anum_t first, last, current;
-+
-+  if (!hc)
-+    return;
-+
-+  /* fetch previous values of first and last */
-+  hdata = mutt_hcache_fetch_raw (hc, "index", strlen);
-+  if (hdata)
-+  {
-+    dprint (2, (debugfile,
-+              "nntp_hcache_update: mutt_hcache_fetch index: %s\n", hdata));
-+    if (sscanf (hdata, ANUM " " ANUM, &first, &last) == 2)
-+    {
-+      old = 1;
-+      nntp_data->lastCached = last;
-+
-+      /* clean removed headers from cache */
-+      for (current = first; current <= last; current++)
-+      {
-+      if (current >= nntp_data->firstMessage &&
-+          current <= nntp_data->lastMessage)
-+        continue;
-+
-+      snprintf (buf, sizeof (buf), "%d", current);
-+      dprint (2, (debugfile,
-+                  "nntp_hcache_update: mutt_hcache_delete %s\n", buf));
-+      mutt_hcache_delete (hc, buf, strlen);
-+      }
-+    }
-+    FREE (&hdata);
-+  }
-+
-+  /* store current values of first and last */
-+  if (!old || nntp_data->firstMessage != first ||
-+            nntp_data->lastMessage != last)
-+  {
-+    snprintf (buf, sizeof (buf), "%u %u", nntp_data->firstMessage,
-+                                        nntp_data->lastMessage);
-+    dprint (2, (debugfile,
-+              "nntp_hcache_update: mutt_hcache_store index: %s\n", buf));
-+    mutt_hcache_store_raw (hc, "index", buf, strlen (buf) + 1, strlen);
-+  }
-+}
-+#endif
-+
-+/* Remove bcache file */
-+static int nntp_bcache_delete (const char *id, body_cache_t *bcache, void *data)
-+{
-+  NNTP_DATA *nntp_data = data;
-+  anum_t anum;
-+  char c;
-+
-+  if (!nntp_data || sscanf (id, ANUM "%c", &anum, &c) != 1 ||
-+      anum < nntp_data->firstMessage || anum > nntp_data->lastMessage)
-+  {
-+    if (nntp_data)
-+      dprint (2, (debugfile, "nntp_bcache_delete: mutt_bcache_del %s\n", id));
-+    mutt_bcache_del (bcache, id);
-+  }
-+  return 0;
-+}
-+
-+/* Remove stale cached messages */
-+void nntp_bcache_update (NNTP_DATA *nntp_data)
-+{
-+  mutt_bcache_list (nntp_data->bcache, nntp_bcache_delete, nntp_data);
-+}
-+
-+/* Remove hcache and bcache of newsgroup */
-+void nntp_delete_group_cache (NNTP_DATA *nntp_data)
-+{
-+  char file[_POSIX_PATH_MAX];
-+
-+  if (!nntp_data || !nntp_data->nserv || !nntp_data->nserv->cacheable)
-+    return;
-+
-+#ifdef USE_HCACHE
-+  nntp_hcache_namer (nntp_data->group, file, sizeof (file));
-+  cache_expand (file, sizeof (file), &nntp_data->nserv->conn->account, file);
-+  unlink (file);
-+  nntp_data->lastCached = 0;
-+  dprint (2, (debugfile, "nntp_delete_group_cache: %s\n", file));
-+#endif
-+
-+  if (!nntp_data->bcache)
-+    nntp_data->bcache = mutt_bcache_open (&nntp_data->nserv->conn->account,
-+                      nntp_data->group);
-+  if (nntp_data->bcache)
-+  {
-+    dprint (2, (debugfile, "nntp_delete_group_cache: %s/*\n", nntp_data->group));
-+    mutt_bcache_list (nntp_data->bcache, nntp_bcache_delete, NULL);
-+    mutt_bcache_close (&nntp_data->bcache);
-+  }
-+}
-+
-+/* Remove hcache and bcache of all unexistent and unsubscribed newsgroups */
-+void nntp_clear_cache (NNTP_SERVER *nserv)
-+{
-+  char file[_POSIX_PATH_MAX];
-+  char *fp;
-+  struct dirent *entry;
-+  DIR *dp;
-+
-+  if (!nserv || !nserv->cacheable)
-+    return;
-+
-+  cache_expand (file, sizeof (file), &nserv->conn->account, NULL);
-+  dp = opendir (file);
-+  if (dp)
-+  {
-+    safe_strncat (file, sizeof (file), "/", 1);
-+    fp = file + strlen (file);
-+    while ((entry = readdir (dp)))
-+    {
-+      char *group = entry->d_name;
-+      struct stat sb;
-+      NNTP_DATA *nntp_data;
-+      NNTP_DATA nntp_tmp;
-+
-+      if (mutt_strcmp (group, ".") == 0 ||
-+        mutt_strcmp (group, "..") == 0)
-+      continue;
-+      *fp = '\0';
-+      safe_strncat (file, sizeof (file), group, strlen (group));
-+      if (stat (file, &sb))
-+      continue;
-+
-+#ifdef USE_HCACHE
-+      if (S_ISREG (sb.st_mode))
-+      {
-+      char *ext = group + strlen (group) - 7;
-+      if (strlen (group) < 8 || mutt_strcmp (ext, ".hcache"))
-+        continue;
-+      *ext = '\0';
-+      }
-+      else
-+#endif
-+      if (!S_ISDIR (sb.st_mode))
-+      continue;
-+
-+      nntp_data = hash_find (nserv->groups_hash, group);
-+      if (!nntp_data)
-+      {
-+      nntp_data = &nntp_tmp;
-+      nntp_data->nserv = nserv;
-+      nntp_data->group = group;
-+      nntp_data->bcache = NULL;
-+      }
-+      else if (nntp_data->newsrc_ent || nntp_data->subscribed ||
-+             option (OPTSAVEUNSUB))
-+      continue;
-+
-+      nntp_delete_group_cache (nntp_data);
-+      if (S_ISDIR (sb.st_mode))
-+      {
-+      rmdir (file);
-+      dprint (2, (debugfile, "nntp_clear_cache: %s\n", file));
-+      }
-+    }
-+    closedir (dp);
-+  }
-+  return;
-+}
-+
-+/* %a = account url
-+ * %p = port
-+ * %P = port if specified
-+ * %s = news server name
-+ * %S = url schema
-+ * %u = username */
-+const char *
-+nntp_format_str (char *dest, size_t destlen, size_t col, int cols, char op,
-+              const char *src, const char *fmt, const char *ifstring,
-+              const char *elsestring, unsigned long data, format_flag flags)
-+{
-+  NNTP_SERVER *nserv = (NNTP_SERVER *)data;
-+  ACCOUNT *acct = &nserv->conn->account;
-+  ciss_url_t url;
-+  char fn[SHORT_STRING], tmp[SHORT_STRING], *p;
-+
-+  switch (op)
-+  {
-+    case 'a':
-+      mutt_account_tourl (acct, &url);
-+      url_ciss_tostring (&url, fn, sizeof (fn), U_PATH);
-+      p = strchr (fn, '/');
-+      if (p)
-+      *p = '\0';
-+      snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
-+      snprintf (dest, destlen, tmp, fn);
-+      break;
-+    case 'p':
-+      snprintf (tmp, sizeof (tmp), "%%%su", fmt);
-+      snprintf (dest, destlen, tmp, acct->port);
-+      break;
-+    case 'P':
-+      *dest = '\0';
-+      if (acct->flags & MUTT_ACCT_PORT)
-+      {
-+      snprintf (tmp, sizeof (tmp), "%%%su", fmt);
-+      snprintf (dest, destlen, tmp, acct->port);
-+      }
-+      break;
-+    case 's':
-+      strncpy (fn, acct->host, sizeof (fn) - 1);
-+      mutt_strlower (fn);
-+      snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
-+      snprintf (dest, destlen, tmp, fn);
-+      break;
-+    case 'S':
-+      mutt_account_tourl (acct, &url);
-+      url_ciss_tostring (&url, fn, sizeof (fn), U_PATH);
-+      p = strchr (fn, ':');
-+      if (p)
-+      *p = '\0';
-+      snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
-+      snprintf (dest, destlen, tmp, fn);
-+      break;
-+    case 'u':
-+      snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
-+      snprintf (dest, destlen, tmp, acct->user);
-+      break;
-+  }
-+  return (src);
-+}
-+
-+/* Automatically loads a newsrc into memory, if necessary.
-+ * Checks the size/mtime of a newsrc file, if it doesn't match, load
-+ * again.  Hmm, if a system has broken mtimes, this might mean the file
-+ * is reloaded every time, which we'd have to fix. */
-+NNTP_SERVER *nntp_select_server (char *server, int leave_lock)
-+{
-+  char file[_POSIX_PATH_MAX];
-+  char *p;
-+  int rc;
-+  struct stat sb;
-+  ACCOUNT acct;
-+  NNTP_SERVER *nserv;
-+  NNTP_DATA *nntp_data;
-+  CONNECTION *conn;
-+  ciss_url_t url;
-+
-+  if (!server || !*server)
-+  {
-+    mutt_error _("No news server defined!");
-+    mutt_sleep (2);
-+    return NULL;
-+  }
-+
-+  /* create account from news server url */
-+  acct.flags = 0;
-+  acct.port = NNTP_PORT;
-+  acct.type = MUTT_ACCT_TYPE_NNTP;
-+  snprintf (file, sizeof (file), "%s%s",
-+          strstr (server, "://") ? "" : "news://", server);
-+  if (url_parse_ciss (&url, file) < 0 ||
-+      (url.path && *url.path) ||
-+      !(url.scheme == U_NNTP || url.scheme == U_NNTPS) ||
-+      mutt_account_fromurl (&acct, &url) < 0)
-+  {
-+    mutt_error (_("%s is an invalid news server specification!"), server);
-+    mutt_sleep (2);
-+    return NULL;
-+  }
-+  if (url.scheme == U_NNTPS)
-+  {
-+    acct.flags |= MUTT_ACCT_SSL;
-+    acct.port = NNTP_SSL_PORT;
-+  }
-+
-+  /* find connection by account */
-+  conn = mutt_conn_find (NULL, &acct);
-+  if (!conn)
-+    return NULL;
-+  if (!(conn->account.flags & MUTT_ACCT_USER) && acct.flags & MUTT_ACCT_USER)
-+  {
-+    conn->account.flags |= MUTT_ACCT_USER;
-+    conn->account.user[0] = '\0';
-+  }
-+
-+  /* news server already exists */
-+  nserv = conn->data;
-+  if (nserv)
-+  {
-+    if (nserv->status == NNTP_BYE)
-+      nserv->status = NNTP_NONE;
-+    if (nntp_open_connection (nserv) < 0)
-+      return NULL;
-+
-+    rc = nntp_newsrc_parse (nserv);
-+    if (rc < 0)
-+      return NULL;
-+
-+    /* check for new newsgroups */
-+    if (!leave_lock && nntp_check_new_groups (nserv) < 0)
-+      rc = -1;
-+
-+    /* .newsrc has been externally modified */
-+    if (rc > 0)
-+      nntp_clear_cache (nserv);
-+    if (rc < 0 || !leave_lock)
-+      nntp_newsrc_close (nserv);
-+    return rc < 0 ? NULL : nserv;
-+  }
-+
-+  /* new news server */
-+  nserv = safe_calloc (1, sizeof (NNTP_SERVER));
-+  nserv->conn = conn;
-+  nserv->groups_hash = hash_create (1009, 0);
-+  nserv->groups_max = 16;
-+  nserv->groups_list = safe_malloc (nserv->groups_max * sizeof (nntp_data));
-+
-+  rc = nntp_open_connection (nserv);
-+
-+  /* try to create cache directory and enable caching */
-+  nserv->cacheable = 0;
-+  if (rc >= 0 && NewsCacheDir && *NewsCacheDir)
-+  {
-+    cache_expand (file, sizeof (file), &conn->account, NULL);
-+    p = *file == '/' ? file + 1 : file;
-+    while (1)
-+    {
-+      p = strchr (p, '/');
-+      if (p)
-+      *p = '\0';
-+      if ((stat (file, &sb) || (sb.st_mode & S_IFDIR) == 0) &&
-+        mkdir (file, 0700))
-+      {
-+      mutt_error (_("Can't create %s: %s."), file, strerror (errno));
-+      mutt_sleep (2);
-+      break;
-+      }
-+      if (!p)
-+      {
-+      nserv->cacheable = 1;
-+      break;
-+      }
-+      *p++ = '/';
-+    }
-+  }
-+
-+  /* load .newsrc */
-+  if (rc >= 0)
-+  {
-+    mutt_FormatString (file, sizeof (file), 0, sizeof (file), NONULL (NewsRc),
-+                     nntp_format_str, (unsigned long)nserv, 0);
-+    mutt_expand_path (file, sizeof (file));
-+    nserv->newsrc_file = safe_strdup (file);
-+    rc = nntp_newsrc_parse (nserv);
-+  }
-+  if (rc >= 0)
-+  {
-+    /* try to load list of newsgroups from cache */
-+    if (nserv->cacheable && active_get_cache (nserv) == 0)
-+      rc = nntp_check_new_groups (nserv);
-+
-+    /* load list of newsgroups from server */
-+    else
-+      rc = nntp_active_fetch (nserv, 0);
-+  }
-+
-+  if (rc >= 0)
-+    nntp_clear_cache (nserv);
-+
-+#ifdef USE_HCACHE
-+  /* check cache files */
-+  if (rc >= 0 && nserv->cacheable)
-+  {
-+    struct dirent *entry;
-+    DIR *dp = opendir (file);
-+
-+    if (dp)
-+    {
-+      while ((entry = readdir (dp)))
-+      {
-+      header_cache_t *hc;
-+      void *hdata;
-+      char *group = entry->d_name;
-+
-+      p = group + strlen (group) - 7;
-+      if (strlen (group) < 8 || strcmp (p, ".hcache"))
-+        continue;
-+      *p = '\0';
-+      nntp_data = hash_find (nserv->groups_hash, group);
-+      if (!nntp_data)
-+        continue;
-+
-+      hc = nntp_hcache_open (nntp_data);
-+      if (!hc)
-+        continue;
-+
-+      /* fetch previous values of first and last */
-+      hdata = mutt_hcache_fetch_raw (hc, "index", strlen);
-+      if (hdata)
-+      {
-+        anum_t first, last;
-+
-+        if (sscanf (hdata, ANUM " " ANUM, &first, &last) == 2)
-+        {
-+          if (nntp_data->deleted)
-+          {
-+            nntp_data->firstMessage = first;
-+            nntp_data->lastMessage = last;
-+          }
-+          if (last >= nntp_data->firstMessage &&
-+              last <= nntp_data->lastMessage)
-+          {
-+            nntp_data->lastCached = last;
-+            dprint (2, (debugfile, "nntp_select_server: %s lastCached=%u\n",
-+                        nntp_data->group, last));
-+          }
-+        }
-+        FREE (&hdata);
-+      }
-+      mutt_hcache_close (hc);
-+      }
-+      closedir (dp);
-+    }
-+  }
-+#endif
-+
-+  if (rc < 0 || !leave_lock)
-+    nntp_newsrc_close (nserv);
-+
-+  if (rc < 0)
-+  {
-+    hash_destroy (&nserv->groups_hash, nntp_data_free);
-+    FREE (&nserv->groups_list);
-+    FREE (&nserv->newsrc_file);
-+    FREE (&nserv->authenticators);
-+    FREE (&nserv);
-+    mutt_socket_close (conn);
-+    mutt_socket_free (conn);
-+    return NULL;
-+  }
-+
-+  conn->data = nserv;
-+  return nserv;
-+}
-+
-+/* Full status flags are not supported by nntp, but we can fake some of them:
-+ * Read = a read message number is in the .newsrc
-+ * New = not read and not cached
-+ * Old = not read but cached */
-+void nntp_article_status (CONTEXT *ctx, HEADER *hdr, char *group, anum_t anum)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  unsigned int i;
-+
-+  if (group)
-+    nntp_data = hash_find (nntp_data->nserv->groups_hash, group);
-+
-+  if (!nntp_data)
-+    return;
-+
-+  for (i = 0; i < nntp_data->newsrc_len; i++)
-+  {
-+    if ((anum >= nntp_data->newsrc_ent[i].first) &&
-+      (anum <= nntp_data->newsrc_ent[i].last))
-+    {
-+      /* can't use mutt_set_flag() because mx_update_context()
-+       didn't called yet */
-+      hdr->read = 1;
-+      return;
-+    }
-+  }
-+
-+  /* article was not cached yet, it's new */
-+  if (anum > nntp_data->lastCached)
-+    return;
-+
-+  /* article isn't read but cached, it's old */
-+  if (option (OPTMARKOLD))
-+    hdr->old = 1;
-+}
-+
-+/* calculate number of unread articles using .newsrc data */
-+void nntp_group_unread_stat (NNTP_DATA *nntp_data)
-+{
-+  unsigned int i;
-+  anum_t first, last;
-+
-+  nntp_data->unread = 0;
-+  if (nntp_data->lastMessage == 0 ||
-+      nntp_data->firstMessage > nntp_data->lastMessage)
-+    return;
-+
-+  nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
-+  for (i = 0; i < nntp_data->newsrc_len; i++)
-+  {
-+    first = nntp_data->newsrc_ent[i].first;
-+    if (first < nntp_data->firstMessage)
-+      first = nntp_data->firstMessage;
-+    last = nntp_data->newsrc_ent[i].last;
-+    if (last > nntp_data->lastMessage)
-+      last = nntp_data->lastMessage;
-+    if (first <= last)
-+      nntp_data->unread -= last - first + 1;
-+  }
-+}
-+
-+/* Subscribe newsgroup */
-+NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *nserv, char *group)
-+{
-+  NNTP_DATA *nntp_data;
-+
-+  if (!nserv || !nserv->groups_hash || !group || !*group)
-+    return NULL;
-+
-+  nntp_data = nntp_data_find (nserv, group);
-+  nntp_data->subscribed = 1;
-+  if (!nntp_data->newsrc_ent)
-+  {
-+    nntp_data->newsrc_ent = safe_calloc (1, sizeof (NEWSRC_ENTRY));
-+    nntp_data->newsrc_len = 1;
-+    nntp_data->newsrc_ent[0].first = 1;
-+    nntp_data->newsrc_ent[0].last = 0;
-+  }
-+  return nntp_data;
-+}
-+
-+/* Unsubscribe newsgroup */
-+NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *nserv, char *group)
-+{
-+  NNTP_DATA *nntp_data;
-+
-+  if (!nserv || !nserv->groups_hash || !group || !*group)
-+    return NULL;
-+
-+  nntp_data = hash_find (nserv->groups_hash, group);
-+  if (!nntp_data)
-+    return NULL;
-+
-+  nntp_data->subscribed = 0;
-+  if (!option (OPTSAVEUNSUB))
-+  {
-+    nntp_data->newsrc_len = 0;
-+    FREE (&nntp_data->newsrc_ent);
-+  }
-+  return nntp_data;
-+}
-+
-+/* Catchup newsgroup */
-+NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *nserv, char *group)
-+{
-+  NNTP_DATA *nntp_data;
-+
-+  if (!nserv || !nserv->groups_hash || !group || !*group)
-+    return NULL;
-+
-+  nntp_data = hash_find (nserv->groups_hash, group);
-+  if (!nntp_data)
-+    return NULL;
-+
-+  if (nntp_data->newsrc_ent)
-+  {
-+    safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
-+    nntp_data->newsrc_len = 1;
-+    nntp_data->newsrc_ent[0].first = 1;
-+    nntp_data->newsrc_ent[0].last = nntp_data->lastMessage;
-+  }
-+  nntp_data->unread = 0;
-+  if (Context && Context->data == nntp_data)
-+  {
-+    unsigned int i;
-+
-+    for (i = 0; i < Context->msgcount; i++)
-+      mutt_set_flag (Context, Context->hdrs[i], MUTT_READ, 1);
-+  }
-+  return nntp_data;
-+}
-+
-+/* Uncatchup newsgroup */
-+NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *nserv, char *group)
-+{
-+  NNTP_DATA *nntp_data;
-+
-+  if (!nserv || !nserv->groups_hash || !group || !*group)
-+    return NULL;
-+
-+  nntp_data = hash_find (nserv->groups_hash, group);
-+  if (!nntp_data)
-+    return NULL;
-+
-+  if (nntp_data->newsrc_ent)
-+  {
-+    safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
-+    nntp_data->newsrc_len = 1;
-+    nntp_data->newsrc_ent[0].first = 1;
-+    nntp_data->newsrc_ent[0].last = nntp_data->firstMessage - 1;
-+  }
-+  if (Context && Context->data == nntp_data)
-+  {
-+    unsigned int i;
-+
-+    nntp_data->unread = Context->msgcount;
-+    for (i = 0; i < Context->msgcount; i++)
-+      mutt_set_flag (Context, Context->hdrs[i], MUTT_READ, 0);
-+  }
-+  else
-+    nntp_data->unread = nntp_data->lastMessage - nntp_data->newsrc_ent[0].last;
-+  return nntp_data;
-+}
-+
-+/* Get first newsgroup with new messages */
-+void nntp_buffy (char *buf, size_t len)
-+{
-+  unsigned int i;
-+
-+  for (i = 0; i < CurrentNewsSrv->groups_num; i++)
-+  {
-+    NNTP_DATA *nntp_data = CurrentNewsSrv->groups_list[i];
-+
-+    if (!nntp_data || !nntp_data->subscribed || !nntp_data->unread)
-+      continue;
-+
-+    if (Context && Context->magic == MUTT_NNTP &&
-+      !mutt_strcmp (nntp_data->group, ((NNTP_DATA *)Context->data)->group))
-+    {
-+      unsigned int i, unread = 0;
-+
-+      for (i = 0; i < Context->msgcount; i++)
-+      if (!Context->hdrs[i]->read && !Context->hdrs[i]->deleted)
-+        unread++;
-+      if (!unread)
-+      continue;
-+    }
-+    strfcpy (buf, nntp_data->group, len);
-+    break;
-+  }
-+}
-diff -udprP mutt-1.10.0.orig/nntp.c mutt-1.10.0/nntp.c
---- mutt-1.10.0.orig/nntp.c    1970-01-01 03:00:00.000000000 +0300
-+++ mutt-1.10.0/nntp.c 2018-06-16 17:22:30.200469636 +0300
-@@ -0,0 +1,2486 @@
-+/*
-+ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
-+ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
-+ * Copyright (C) 2000-2017 Vsevolod Volkov <vvv@mutt.org.ua>
-+ *
-+ *     This program is free software; you can redistribute it and/or modify
-+ *     it under the terms of the GNU General Public License as published by
-+ *     the Free Software Foundation; either version 2 of the License, or
-+ *     (at your option) any later version.
-+ *
-+ *     This program is distributed in the hope that it will be useful,
-+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *     GNU General Public License for more details.
-+ *
-+ *     You should have received a copy of the GNU General Public License
-+ *     along with this program; if not, write to the Free Software
-+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#if HAVE_CONFIG_H
-+#include "config.h"
-+#endif
-+
-+#include "mutt.h"
-+#include "mutt_curses.h"
-+#include "sort.h"
-+#include "mx.h"
-+#include "mime.h"
-+#include "rfc1524.h"
-+#include "rfc2047.h"
-+#include "mailbox.h"
-+#include "mutt_crypt.h"
-+#include "nntp.h"
-+
-+#if defined(USE_SSL)
-+#include "mutt_ssl.h"
-+#endif
-+
-+#ifdef HAVE_PGP
-+#include "pgp.h"
-+#endif
-+
-+#ifdef HAVE_SMIME
-+#include "smime.h"
-+#endif
-+
-+#if USE_HCACHE
-+#include "hcache.h"
-+#endif
-+
-+#include <unistd.h>
-+#include <string.h>
-+#include <ctype.h>
-+#include <stdlib.h>
-+
-+#ifdef USE_SASL
-+#include <sasl/sasl.h>
-+#include <sasl/saslutil.h>
-+
-+#include "mutt_sasl.h"
-+#endif
-+
-+static int nntp_connect_error (NNTP_SERVER *nserv)
-+{
-+  nserv->status = NNTP_NONE;
-+  mutt_error _("Server closed connection!");
-+  mutt_sleep (2);
-+  return -1;
-+}
-+
-+/* Get capabilities:
-+ * -1 - error, connection is closed
-+ *  0 - mode is reader, capabilities setted up
-+ *  1 - need to switch to reader mode */
-+static int nntp_capabilities (NNTP_SERVER *nserv)
-+{
-+  CONNECTION *conn = nserv->conn;
-+  unsigned int mode_reader = 0;
-+  char buf[LONG_STRING];
-+  char authinfo[LONG_STRING] = "";
-+
-+  nserv->hasCAPABILITIES = 0;
-+  nserv->hasSTARTTLS = 0;
-+  nserv->hasDATE = 0;
-+  nserv->hasLIST_NEWSGROUPS = 0;
-+  nserv->hasLISTGROUP = 0;
-+  nserv->hasLISTGROUPrange = 0;
-+  nserv->hasOVER = 0;
-+  FREE (&nserv->authenticators);
-+
-+  if (mutt_socket_write (conn, "CAPABILITIES\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+    return nntp_connect_error (nserv);
-+
-+  /* no capabilities */
-+  if (mutt_strncmp ("101", buf, 3))
-+    return 1;
-+  nserv->hasCAPABILITIES = 1;
-+
-+  /* parse capabilities */
-+  do
-+  {
-+    if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+    if (!mutt_strcmp ("STARTTLS", buf))
-+      nserv->hasSTARTTLS = 1;
-+    else if (!mutt_strcmp ("MODE-READER", buf))
-+      mode_reader = 1;
-+    else if (!mutt_strcmp ("READER", buf))
-+    {
-+      nserv->hasDATE = 1;
-+      nserv->hasLISTGROUP = 1;
-+      nserv->hasLISTGROUPrange = 1;
-+    }
-+    else if (!mutt_strncmp ("AUTHINFO ", buf, 9))
-+    {
-+      safe_strcat (buf, sizeof (buf), " ");
-+      strfcpy (authinfo, buf + 8, sizeof (authinfo));
-+    }
-+#ifdef USE_SASL
-+    else if (!mutt_strncmp ("SASL ", buf, 5))
-+    {
-+      char *p = buf + 5;
-+      while (*p == ' ')
-+      p++;
-+      nserv->authenticators = safe_strdup (p);
-+    }
-+#endif
-+    else if (!mutt_strcmp ("OVER", buf))
-+      nserv->hasOVER = 1;
-+    else if (!mutt_strncmp ("LIST ", buf, 5))
-+    {
-+      char *p = strstr (buf, " NEWSGROUPS");
-+      if (p)
-+      {
-+      p += 11;
-+      if (*p == '\0' || *p == ' ')
-+        nserv->hasLIST_NEWSGROUPS = 1;
-+      }
-+    }
-+  } while (mutt_strcmp (".", buf));
-+  *buf = '\0';
-+#ifdef USE_SASL
-+  if (nserv->authenticators && strcasestr (authinfo, " SASL "))
-+    strfcpy (buf, nserv->authenticators, sizeof (buf));
-+#endif
-+  if (strcasestr (authinfo, " USER "))
-+  {
-+    if (*buf)
-+      safe_strcat (buf, sizeof (buf), " ");
-+    safe_strcat (buf, sizeof (buf), "USER");
-+  }
-+  mutt_str_replace (&nserv->authenticators, buf);
-+
-+  /* current mode is reader */
-+  if (nserv->hasDATE)
-+    return 0;
-+
-+  /* server is mode-switching, need to switch to reader mode */
-+  if (mode_reader)
-+    return 1;
-+
-+  mutt_socket_close (conn);
-+  nserv->status = NNTP_BYE;
-+  mutt_error _("Server doesn't support reader mode.");
-+  mutt_sleep (2);
-+  return -1;
-+}
-+
-+char *OverviewFmt =
-+      "Subject:\0"
-+      "From:\0"
-+      "Date:\0"
-+      "Message-ID:\0"
-+      "References:\0"
-+      "Content-Length:\0"
-+      "Lines:\0"
-+      "\0";
-+
-+/* Detect supported commands */
-+static int nntp_attempt_features (NNTP_SERVER *nserv)
-+{
-+  CONNECTION *conn = nserv->conn;
-+  char buf[LONG_STRING];
-+
-+  /* no CAPABILITIES, trying DATE, LISTGROUP, LIST NEWSGROUPS */
-+  if (!nserv->hasCAPABILITIES)
-+  {
-+    if (mutt_socket_write (conn, "DATE\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+    if (mutt_strncmp ("500", buf, 3))
-+      nserv->hasDATE = 1;
-+
-+    if (mutt_socket_write (conn, "LISTGROUP\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+    if (mutt_strncmp ("500", buf, 3))
-+      nserv->hasLISTGROUP = 1;
-+
-+    if (mutt_socket_write (conn, "LIST NEWSGROUPS +\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+    if (mutt_strncmp ("500", buf, 3))
-+      nserv->hasLIST_NEWSGROUPS = 1;
-+    if (!mutt_strncmp ("215", buf, 3))
-+    {
-+      do
-+      {
-+      if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+        return nntp_connect_error (nserv);
-+      } while (mutt_strcmp (".", buf));
-+    }
-+  }
-+
-+  /* no LIST NEWSGROUPS, trying XGTITLE */
-+  if (!nserv->hasLIST_NEWSGROUPS)
-+  {
-+    if (mutt_socket_write (conn, "XGTITLE\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+    if (mutt_strncmp ("500", buf, 3))
-+      nserv->hasXGTITLE = 1;
-+  }
-+
-+  /* no OVER, trying XOVER */
-+  if (!nserv->hasOVER)
-+  {
-+    if (mutt_socket_write (conn, "XOVER\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+    if (mutt_strncmp ("500", buf, 3))
-+      nserv->hasXOVER = 1;
-+  }
-+
-+  /* trying LIST OVERVIEW.FMT */
-+  if (nserv->hasOVER || nserv->hasXOVER)
-+  {
-+    if (mutt_socket_write (conn, "LIST OVERVIEW.FMT\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+    if (mutt_strncmp ("215", buf, 3))
-+      nserv->overview_fmt = OverviewFmt;
-+    else
-+    {
-+      int chunk, cont = 0;
-+      size_t buflen = 2 * LONG_STRING, off = 0, b = 0;
-+
-+      if (nserv->overview_fmt)
-+      FREE (&nserv->overview_fmt);
-+      nserv->overview_fmt = safe_malloc (buflen);
-+
-+      while (1)
-+      {
-+      if (buflen - off < LONG_STRING)
-+      {
-+        buflen *= 2;
-+        safe_realloc (&nserv->overview_fmt, buflen);
-+      }
-+
-+      chunk = mutt_socket_readln (nserv->overview_fmt + off,
-+                                  buflen - off, conn);
-+      if (chunk < 0)
-+      {
-+        FREE (&nserv->overview_fmt);
-+        return nntp_connect_error (nserv);
-+      }
-+
-+      if (!cont && !mutt_strcmp (".", nserv->overview_fmt + off))
-+        break;
-+
-+      cont = chunk >= buflen - off ? 1 : 0;
-+      off += strlen (nserv->overview_fmt + off);
-+      if (!cont)
-+      {
-+        char *colon;
-+
-+        if (nserv->overview_fmt[b] == ':')
-+        {
-+          memmove (nserv->overview_fmt + b,
-+                   nserv->overview_fmt + b + 1, off - b - 1);
-+          nserv->overview_fmt[off - 1] = ':';
-+        }
-+        colon = strchr (nserv->overview_fmt + b, ':');
-+        if (!colon)
-+          nserv->overview_fmt[off++] = ':';
-+        else if (strcmp (colon + 1, "full"))
-+          off = colon + 1 - nserv->overview_fmt;
-+        if (!strcasecmp (nserv->overview_fmt + b, "Bytes:"))
-+        {
-+          strcpy (nserv->overview_fmt + b, "Content-Length:");
-+          off = b + strlen (nserv->overview_fmt + b);
-+        }
-+        nserv->overview_fmt[off++] = '\0';
-+        b = off;
-+      }
-+      }
-+      nserv->overview_fmt[off++] = '\0';
-+      safe_realloc (&nserv->overview_fmt, off);
-+    }
-+  }
-+  return 0;
-+}
-+
-+/* Get login, password and authenticate */
-+static int nntp_auth (NNTP_SERVER *nserv)
-+{
-+  CONNECTION *conn = nserv->conn;
-+  char buf[LONG_STRING];
-+  char authenticators[LONG_STRING] = "USER";
-+  char *method, *a, *p;
-+  unsigned char flags = conn->account.flags;
-+
-+  while (1)
-+  {
-+    /* get login and password */
-+    if (mutt_account_getuser (&conn->account) || !conn->account.user[0] ||
-+      mutt_account_getpass (&conn->account) || !conn->account.pass[0])
-+      break;
-+
-+    /* get list of authenticators */
-+    if (NntpAuthenticators && *NntpAuthenticators)
-+      strfcpy (authenticators, NntpAuthenticators, sizeof (authenticators));
-+    else if (nserv->hasCAPABILITIES)
-+    {
-+      strfcpy (authenticators, NONULL (nserv->authenticators),
-+             sizeof (authenticators));
-+      p = authenticators;
-+      while (*p)
-+      {
-+      if (*p == ' ')
-+        *p = ':';
-+      p++;
-+      }
-+    }
-+    p = authenticators;
-+    while (*p)
-+    {
-+      *p = ascii_toupper (*p);
-+      p++;
-+    }
-+
-+    dprint (1, (debugfile,
-+              "nntp_auth: available methods: %s\n", nserv->authenticators));
-+    a = authenticators;
-+    while (1)
-+    {
-+      if (!a)
-+      {
-+      mutt_error _("No authenticators available");
-+      mutt_sleep (2);
-+      break;
-+      }
-+
-+      method = a;
-+      a = strchr (a, ':');
-+      if (a)
-+      *a++ = '\0';
-+
-+      /* check authenticator */
-+      if (nserv->hasCAPABILITIES)
-+      {
-+      char *m;
-+
-+      if (!nserv->authenticators)
-+        continue;
-+      m = strcasestr (nserv->authenticators, method);
-+      if (!m)
-+        continue;
-+      if (m > nserv->authenticators && *(m - 1) != ' ')
-+        continue;
-+      m += strlen (method);
-+      if (*m != '\0' && *m != ' ')
-+        continue;
-+      }
-+      dprint (1, (debugfile, "nntp_auth: trying method %s\n", method));
-+
-+      /* AUTHINFO USER authentication */
-+      if (!strcmp (method, "USER"))
-+      {
-+      mutt_message (_("Authenticating (%s)..."), method);
-+      snprintf (buf, sizeof (buf), "AUTHINFO USER %s\r\n", conn->account.user);
-+      if (mutt_socket_write (conn, buf) < 0 ||
-+          mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+        break;
-+
-+      /* authenticated, password is not required */
-+      if (!mutt_strncmp ("281", buf, 3))
-+        return 0;
-+
-+      /* username accepted, sending password */
-+      if (!mutt_strncmp ("381", buf, 3))
-+      {
-+#ifdef DEBUG
-+        if (debuglevel < MUTT_SOCK_LOG_FULL)
-+          dprint (MUTT_SOCK_LOG_CMD, (debugfile,
-+                  "%d> AUTHINFO PASS *\n", conn->fd));
-+#endif
-+        snprintf (buf, sizeof (buf), "AUTHINFO PASS %s\r\n",
-+                  conn->account.pass);
-+        if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) < 0 ||
-+            mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+        break;
-+
-+        /* authenticated */
-+        if (!mutt_strncmp ("281", buf, 3))
-+          return 0;
-+      }
-+
-+      /* server doesn't support AUTHINFO USER, trying next method */
-+      if (*buf == '5')
-+        continue;
-+      }
-+
-+      else
-+      {
-+#ifdef USE_SASL
-+      sasl_conn_t *saslconn;
-+      sasl_interact_t *interaction = NULL;
-+      int rc;
-+      char inbuf[LONG_STRING] = "";
-+      const char *mech;
-+      const char *client_out = NULL;
-+      unsigned int client_len, len;
-+
-+      if (mutt_sasl_client_new (conn, &saslconn) < 0)
-+      {
-+        dprint (1, (debugfile,
-+                "nntp_auth: error allocating SASL connection.\n"));
-+        continue;
-+      }
-+
-+      while (1)
-+      {
-+        rc = sasl_client_start (saslconn, method, &interaction,
-+                                &client_out, &client_len, &mech);
-+        if (rc != SASL_INTERACT)
-+          break;
-+        mutt_sasl_interact (interaction);
-+      }
-+      if (rc != SASL_OK && rc != SASL_CONTINUE)
-+      {
-+        sasl_dispose (&saslconn);
-+        dprint (1, (debugfile,
-+                "nntp_auth: error starting SASL authentication exchange.\n"));
-+        continue;
-+      }
-+
-+      mutt_message (_("Authenticating (%s)..."), method);
-+      snprintf (buf, sizeof (buf), "AUTHINFO SASL %s", method);
-+
-+      /* looping protocol */
-+      while (rc == SASL_CONTINUE || (rc == SASL_OK && client_len))
-+      {
-+        /* send out client response */
-+        if (client_len)
-+        {
-+#ifdef DEBUG
-+          if (debuglevel >= MUTT_SOCK_LOG_FULL)
-+          {
-+            char tmp[LONG_STRING];
-+            memcpy (tmp, client_out, client_len);
-+            for (p = tmp; p < tmp + client_len; p++)
-+            {
-+              if (*p == '\0')
-+                *p = '.';
-+            }
-+            *p = '\0';
-+            dprint (1, (debugfile, "SASL> %s\n", tmp));
-+          }
-+#endif
-+
-+          if (*buf)
-+            safe_strcat (buf, sizeof (buf), " ");
-+          len = strlen (buf);
-+          if (sasl_encode64 (client_out, client_len,
-+              buf + len, sizeof (buf) - len, &len) != SASL_OK)
-+          {
-+            dprint (1, (debugfile,
-+                    "nntp_auth: error base64-encoding client response.\n"));
-+            break;
-+          }
-+        }
-+
-+        safe_strcat (buf, sizeof (buf), "\r\n");
-+#ifdef DEBUG
-+        if (debuglevel < MUTT_SOCK_LOG_FULL)
-+        {
-+          if (strchr (buf, ' '))
-+            dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d> AUTHINFO SASL %s%s\n",
-+                    conn->fd, method, client_len ? " sasl_data" : ""));
-+          else
-+            dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d> sasl_data\n", conn->fd));
-+        }
-+#endif
-+        client_len = 0;
-+        if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) < 0 ||
-+            mutt_socket_readln_d (inbuf, sizeof (inbuf), conn, MUTT_SOCK_LOG_FULL) < 0)
-+          break;
-+        if (mutt_strncmp (inbuf, "283 ", 4) &&
-+            mutt_strncmp (inbuf, "383 ", 4))
-+        {
-+#ifdef DEBUG
-+          if (debuglevel < MUTT_SOCK_LOG_FULL)
-+            dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d< %s\n", conn->fd, inbuf));
-+#endif
-+          break;
-+        }
-+#ifdef DEBUG
-+        if (debuglevel < MUTT_SOCK_LOG_FULL)
-+        {
-+          inbuf[3] = '\0';
-+          dprint (MUTT_SOCK_LOG_CMD, (debugfile,
-+                  "%d< %s sasl_data\n", conn->fd, inbuf));
-+        }
-+#endif
-+
-+        if (!strcmp ("=", inbuf + 4))
-+          len = 0;
-+        else if (sasl_decode64 (inbuf + 4, strlen (inbuf + 4),
-+                 buf, sizeof (buf) - 1, &len) != SASL_OK)
-+        {
-+          dprint (1, (debugfile,
-+                  "nntp_auth: error base64-decoding server response.\n"));
-+          break;
-+        }
-+#ifdef DEBUG
-+        else if (debuglevel >= MUTT_SOCK_LOG_FULL)
-+        {
-+          char tmp[LONG_STRING];
-+          memcpy (tmp, buf, len);
-+          for (p = tmp; p < tmp + len; p++)
-+          {
-+            if (*p == '\0')
-+              *p = '.';
-+          }
-+          *p = '\0';
-+          dprint (1, (debugfile, "SASL< %s\n", tmp));
-+        }
-+#endif
-+
-+        while (1)
-+        {
-+          rc = sasl_client_step (saslconn, buf, len,
-+                                 &interaction, &client_out, &client_len);
-+          if (rc != SASL_INTERACT)
-+            break;
-+          mutt_sasl_interact (interaction);
-+        }
-+        if (*inbuf != '3')
-+          break;
-+
-+        *buf = '\0';
-+      } /* looping protocol */
-+
-+      if (rc == SASL_OK && client_len == 0 && *inbuf == '2')
-+      {
-+        mutt_sasl_setup_conn (conn, saslconn);
-+        return 0;
-+      }
-+
-+      /* terminate SASL sessoin */
-+      sasl_dispose (&saslconn);
-+      if (conn->fd < 0)
-+        break;
-+      if (!mutt_strncmp (inbuf, "383 ", 4))
-+      {
-+        if (mutt_socket_write (conn, "*\r\n") < 0 ||
-+            mutt_socket_readln (inbuf, sizeof (inbuf), conn) < 0)
-+          break;
-+      }
-+
-+      /* server doesn't support AUTHINFO SASL, trying next method */
-+      if (*inbuf == '5')
-+        continue;
-+#else
-+      continue;
-+#endif /* USE_SASL */
-+      }
-+
-+      mutt_error (_("%s authentication failed."), method);
-+      mutt_sleep (2);
-+      break;
-+    }
-+    break;
-+  }
-+
-+  /* error */
-+  nserv->status = NNTP_BYE;
-+  conn->account.flags = flags;
-+  if (conn->fd < 0)
-+  {
-+    mutt_error _("Server closed connection!");
-+    mutt_sleep (2);
-+  }
-+  else
-+    mutt_socket_close (conn);
-+  return -1;
-+}
-+
-+/* Connect to server, authenticate and get capabilities */
-+int nntp_open_connection (NNTP_SERVER *nserv)
-+{
-+  CONNECTION *conn = nserv->conn;
-+  char buf[STRING];
-+  int cap;
-+  unsigned int posting = 0, auth = 1;
-+
-+  if (nserv->status == NNTP_OK)
-+    return 0;
-+  if (nserv->status == NNTP_BYE)
-+    return -1;
-+  nserv->status = NNTP_NONE;
-+
-+  if (mutt_socket_open (conn) < 0)
-+    return -1;
-+
-+  if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+    return nntp_connect_error (nserv);
-+
-+  if (!mutt_strncmp ("200", buf, 3))
-+    posting = 1;
-+  else if (mutt_strncmp ("201", buf, 3))
-+  {
-+    mutt_socket_close (conn);
-+    mutt_remove_trailing_ws (buf);
-+    mutt_error ("%s", buf);
-+    mutt_sleep (2);
-+    return -1;
-+  }
-+
-+  /* get initial capabilities */
-+  cap = nntp_capabilities (nserv);
-+  if (cap < 0)
-+    return -1;
-+
-+  /* tell news server to switch to mode reader if it isn't so */
-+  if (cap > 0)
-+  {
-+    if (mutt_socket_write (conn, "MODE READER\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+
-+    if (!mutt_strncmp ("200", buf, 3))
-+      posting = 1;
-+    else if (!mutt_strncmp ("201", buf, 3))
-+      posting = 0;
-+    /* error if has capabilities, ignore result if no capabilities */
-+    else if (nserv->hasCAPABILITIES)
-+    {
-+      mutt_socket_close (conn);
-+      mutt_error _("Could not switch to reader mode.");
-+      mutt_sleep (2);
-+      return -1;
-+    }
-+
-+    /* recheck capabilities after MODE READER */
-+    if (nserv->hasCAPABILITIES)
-+    {
-+      cap = nntp_capabilities (nserv);
-+      if (cap < 0)
-+      return -1;
-+    }
-+  }
-+
-+  mutt_message (_("Connected to %s. %s"), conn->account.host,
-+              posting ? _("Posting is ok.") : _("Posting is NOT ok."));
-+  mutt_sleep (1);
-+
-+#if defined(USE_SSL)
-+  /* Attempt STARTTLS if available and desired. */
-+  if (nserv->use_tls != 1 && (nserv->hasSTARTTLS || option (OPTSSLFORCETLS)))
-+  {
-+    if (nserv->use_tls == 0)
-+      nserv->use_tls = option (OPTSSLFORCETLS) ||
-+                      query_quadoption (OPT_SSLSTARTTLS,
-+                      _("Secure connection with TLS?")) == MUTT_YES ? 2 : 1;
-+    if (nserv->use_tls == 2)
-+    {
-+      if (mutt_socket_write (conn, "STARTTLS\r\n") < 0 ||
-+        mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+      if (mutt_strncmp ("382", buf, 3))
-+      {
-+      nserv->use_tls = 0;
-+      mutt_error ("STARTTLS: %s", buf);
-+      mutt_sleep (2);
-+      }
-+      else if (mutt_ssl_starttls (conn))
-+      {
-+      nserv->use_tls = 0;
-+      nserv->status = NNTP_NONE;
-+      mutt_socket_close (nserv->conn);
-+      mutt_error _("Could not negotiate TLS connection");
-+      mutt_sleep (2);
-+      return -1;
-+      }
-+      else
-+      {
-+      /* recheck capabilities after STARTTLS */
-+      cap = nntp_capabilities (nserv);
-+      if (cap < 0)
-+        return -1;
-+      }
-+    }
-+  }
-+#endif
-+
-+  /* authentication required? */
-+  if (conn->account.flags & MUTT_ACCT_USER)
-+  {
-+    if (!conn->account.user[0])
-+      auth = 0;
-+  }
-+  else
-+  {
-+    if (mutt_socket_write (conn, "STAT\r\n") < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
-+      return nntp_connect_error (nserv);
-+    if (mutt_strncmp ("480", buf, 3))
-+      auth = 0;
-+  }
-+
-+  /* authenticate */
-+  if (auth && nntp_auth (nserv) < 0)
-+      return -1;
-+
-+  /* get final capabilities after authentication */
-+  if (nserv->hasCAPABILITIES && (auth || cap > 0))
-+  {
-+    cap = nntp_capabilities (nserv);
-+    if (cap < 0)
-+      return -1;
-+    if (cap > 0)
-+    {
-+      mutt_socket_close (conn);
-+      mutt_error _("Could not switch to reader mode.");
-+      mutt_sleep (2);
-+      return -1;
-+    }
-+  }
-+
-+  /* attempt features */
-+  if (nntp_attempt_features (nserv) < 0)
-+    return -1;
-+
-+  nserv->status = NNTP_OK;
-+  return 0;
-+}
-+
-+/* Send data from buffer and receive answer to same buffer */
-+static int nntp_query (NNTP_DATA *nntp_data, char *line, size_t linelen)
-+{
-+  NNTP_SERVER *nserv = nntp_data->nserv;
-+  char buf[LONG_STRING];
-+
-+  if (nserv->status == NNTP_BYE)
-+    return -1;
-+
-+  while (1)
-+  {
-+    if (nserv->status == NNTP_OK)
-+    {
-+      int rc = 0;
-+
-+      if (*line)
-+      rc = mutt_socket_write (nserv->conn, line);
-+      else if (nntp_data->group)
-+      {
-+      snprintf (buf, sizeof (buf), "GROUP %s\r\n", nntp_data->group);
-+      rc = mutt_socket_write (nserv->conn, buf);
-+      }
-+      if (rc >= 0)
-+      rc = mutt_socket_readln (buf, sizeof (buf), nserv->conn);
-+      if (rc >= 0)
-+      break;
-+    }
-+
-+    /* reconnect */
-+    while (1)
-+    {
-+      nserv->status = NNTP_NONE;
-+      if (nntp_open_connection (nserv) == 0)
-+      break;
-+
-+      snprintf (buf, sizeof (buf), _("Connection to %s lost. Reconnect?"),
-+              nserv->conn->account.host);
-+      if (mutt_yesorno (buf, MUTT_YES) != MUTT_YES)
-+      {
-+      nserv->status = NNTP_BYE;
-+      return -1;
-+      }
-+    }
-+
-+    /* select newsgroup after reconnection */
-+    if (nntp_data->group)
-+    {
-+      snprintf (buf, sizeof (buf), "GROUP %s\r\n", nntp_data->group);
-+      if (mutt_socket_write (nserv->conn, buf) < 0 ||
-+        mutt_socket_readln (buf, sizeof (buf), nserv->conn) < 0)
-+      return nntp_connect_error (nserv);
-+    }
-+    if (!*line)
-+      break;
-+  }
-+
-+  strfcpy (line, buf, linelen);
-+  return 0;
-+}
-+
-+/* This function calls funct(*line, *data) for each received line,
-+ * funct(NULL, *data) if rewind(*data) needs, exits when fail or done:
-+ *  0 - success
-+ *  1 - bad response (answer in query buffer)
-+ * -1 - conection lost
-+ * -2 - error in funct(*line, *data) */
-+static int nntp_fetch_lines (NNTP_DATA *nntp_data, char *query, size_t qlen,
-+                      char *msg, int (*funct) (char *, void *), void *data)
-+{
-+  int done = FALSE;
-+  int rc;
-+
-+  while (!done)
-+  {
-+    char buf[LONG_STRING];
-+    char *line;
-+    unsigned int lines = 0;
-+    size_t off = 0;
-+    progress_t progress;
-+
-+    if (msg)
-+      mutt_progress_init (&progress, msg, MUTT_PROGRESS_MSG, ReadInc, -1);
-+
-+    strfcpy (buf, query, sizeof (buf));
-+    if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
-+      return -1;
-+    if (buf[0] != '2')
-+    {
-+      strfcpy (query, buf, qlen);
-+      return 1;
-+    }
-+
-+    line = safe_malloc (sizeof (buf));
-+    rc = 0;
-+
-+    while (1)
-+    {
-+      char *p;
-+      int chunk = mutt_socket_readln_d (buf, sizeof (buf),
-+                nntp_data->nserv->conn, MUTT_SOCK_LOG_HDR);
-+      if (chunk < 0)
-+      {
-+      nntp_data->nserv->status = NNTP_NONE;
-+      break;
-+      }
-+
-+      p = buf;
-+      if (!off && buf[0] == '.')
-+      {
-+      if (buf[1] == '\0')
-+      {
-+        done = TRUE;
-+        break;
-+      }
-+      if (buf[1] == '.')
-+        p++;
-+      }
-+
-+      strfcpy (line + off, p, sizeof (buf));
-+
-+      if (chunk >= sizeof (buf))
-+      off += strlen (p);
-+      else
-+      {
-+      if (msg)
-+        mutt_progress_update (&progress, ++lines, -1);
-+
-+      if (rc == 0 && funct (line, data) < 0)
-+        rc = -2;
-+      off = 0;
-+      }
-+
-+      safe_realloc (&line, off + sizeof (buf));
-+    }
-+    FREE (&line);
-+    funct (NULL, data);
-+  }
-+  return rc;
-+}
-+
-+/* Parse newsgroup description */
-+static int fetch_description (char *line, void *data)
-+{
-+  NNTP_SERVER *nserv = data;
-+  NNTP_DATA *nntp_data;
-+  char *desc;
-+
-+  if (!line)
-+    return 0;
-+
-+  desc = strpbrk (line, " \t");
-+  if (desc)
-+  {
-+    *desc++ = '\0';
-+    desc += strspn (desc, " \t");
-+  }
-+  else
-+    desc = strchr (line, '\0');
-+
-+  nntp_data = hash_find (nserv->groups_hash, line);
-+  if (nntp_data && mutt_strcmp (desc, nntp_data->desc))
-+  {
-+    mutt_str_replace (&nntp_data->desc, desc);
-+    dprint (2, (debugfile, "group: %s, desc: %s\n", line, desc));
-+  }
-+  return 0;
-+}
-+
-+/* Fetch newsgroups descriptions.
-+ * Returns the same code as nntp_fetch_lines() */
-+static int get_description (NNTP_DATA *nntp_data, char *wildmat, char *msg)
-+{
-+  NNTP_SERVER *nserv;
-+  char buf[STRING];
-+  char *cmd;
-+  int rc;
-+
-+  /* get newsgroup description, if possible */
-+  nserv = nntp_data->nserv;
-+  if (!wildmat)
-+    wildmat = nntp_data->group;
-+  if (nserv->hasLIST_NEWSGROUPS)
-+    cmd = "LIST NEWSGROUPS";
-+  else if (nserv->hasXGTITLE)
-+    cmd = "XGTITLE";
-+  else
-+    return 0;
-+
-+  snprintf (buf, sizeof (buf), "%s %s\r\n", cmd, wildmat);
-+  rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), msg,
-+                       fetch_description, nserv);
-+  if (rc > 0)
-+  {
-+    mutt_error ("%s: %s", cmd, buf);
-+    mutt_sleep (2);
-+  }
-+  return rc;
-+}
-+
-+/* Update read flag and set article number if empty */
-+static void nntp_parse_xref (CONTEXT *ctx, HEADER *hdr)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  char *buf, *p;
-+
-+  buf = p = safe_strdup (hdr->env->xref);
-+  while (p)
-+  {
-+    char *grp, *colon;
-+    anum_t anum;
-+
-+    /* skip to next word */
-+    p += strspn (p, " \t");
-+    grp = p;
-+
-+    /* skip to end of word */
-+    p = strpbrk (p, " \t");
-+    if (p)
-+      *p++ = '\0';
-+
-+    /* find colon */
-+    colon = strchr (grp, ':');
-+    if (!colon)
-+      continue;
-+    *colon++ = '\0';
-+    if (sscanf (colon, ANUM, &anum) != 1)
-+      continue;
-+
-+    nntp_article_status (ctx, hdr, grp, anum);
-+    if (hdr && !NHDR (hdr)->article_num && !mutt_strcmp (nntp_data->group, grp))
-+      NHDR (hdr)->article_num = anum;
-+  }
-+  FREE (&buf);
-+}
-+
-+/* Write line to temporarily file */
-+static int fetch_tempfile (char *line, void *data)
-+{
-+  FILE *fp = data;
-+
-+  if (!line)
-+    rewind (fp);
-+  else if (fputs (line, fp) == EOF || fputc ('\n', fp) == EOF)
-+    return -1;
-+  return 0;
-+}
-+
-+typedef struct
-+{
-+  CONTEXT *ctx;
-+  anum_t first;
-+  anum_t last;
-+  int restore;
-+  unsigned char *messages;
-+  progress_t progress;
-+#ifdef USE_HCACHE
-+  header_cache_t *hc;
-+#endif
-+} FETCH_CTX;
-+
-+/* Parse article number */
-+static int fetch_numbers (char *line, void *data)
-+{
-+  FETCH_CTX *fc = data;
-+  anum_t anum;
-+
-+  if (!line)
-+    return 0;
-+  if (sscanf (line, ANUM, &anum) != 1)
-+    return 0;
-+  if (anum < fc->first || anum > fc->last)
-+    return 0;
-+  fc->messages[anum - fc->first] = 1;
-+  return 0;
-+}
-+
-+/* Parse overview line */
-+static int parse_overview_line (char *line, void *data)
-+{
-+  FETCH_CTX *fc = data;
-+  CONTEXT *ctx = fc->ctx;
-+  NNTP_DATA *nntp_data = ctx->data;
-+  HEADER *hdr;
-+  FILE *fp;
-+  char tempfile[_POSIX_PATH_MAX];
-+  char *header, *field;
-+  int save = 1;
-+  anum_t anum;
-+
-+  if (!line)
-+    return 0;
-+
-+  /* parse article number */
-+  field = strchr (line, '\t');
-+  if (field)
-+    *field++ = '\0';
-+  if (sscanf (line, ANUM, &anum) != 1)
-+    return 0;
-+  dprint (2, (debugfile, "parse_overview_line: " ANUM "\n", anum));
-+
-+  /* out of bounds */
-+  if (anum < fc->first || anum > fc->last)
-+    return 0;
-+
-+  /* not in LISTGROUP */
-+  if (!fc->messages[anum - fc->first])
-+  {
-+    /* progress */
-+    if (!ctx->quiet)
-+      mutt_progress_update (&fc->progress, anum - fc->first + 1, -1);
-+    return 0;
-+  }
-+
-+  /* convert overview line to header */
-+  mutt_mktemp (tempfile, sizeof (tempfile));
-+  fp = safe_fopen (tempfile, "w+");
-+  if (!fp)
-+    return -1;
-+
-+  header = nntp_data->nserv->overview_fmt;
-+  while (field)
-+  {
-+    char *b = field;
-+
-+    if (*header)
-+    {
-+      if (strstr (header, ":full") == NULL && fputs (header, fp) == EOF)
-+      {
-+      fclose (fp);
-+      unlink (tempfile);
-+      return -1;
-+      }
-+      header = strchr (header, '\0') + 1;
-+    }
-+
-+    field = strchr (field, '\t');
-+    if (field)
-+      *field++ = '\0';
-+    if (fputs (b, fp) == EOF || fputc ('\n', fp) == EOF)
-+    {
-+      fclose (fp);
-+      unlink (tempfile);
-+      return -1;
-+    }
-+  }
-+  rewind (fp);
-+
-+  /* allocate memory for headers */
-+  if (ctx->msgcount >= ctx->hdrmax)
-+    mx_alloc_memory (ctx);
-+
-+  /* parse header */
-+  hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
-+  hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
-+  hdr->env->newsgroups = safe_strdup (nntp_data->group);
-+  hdr->received = hdr->date_sent;
-+  fclose (fp);
-+  unlink (tempfile);
-+
-+#ifdef USE_HCACHE
-+  if (fc->hc)
-+  {
-+    void *hdata;
-+    char buf[16];
-+
-+    /* try to replace with header from cache */
-+    snprintf (buf, sizeof (buf), "%d", anum);
-+    hdata = mutt_hcache_fetch (fc->hc, buf, strlen);
-+    if (hdata)
-+    {
-+      dprint (2, (debugfile,
-+                "parse_overview_line: mutt_hcache_fetch %s\n", buf));
-+      mutt_free_header (&hdr);
-+      ctx->hdrs[ctx->msgcount] =
-+      hdr = mutt_hcache_restore (hdata, NULL);
-+      FREE (&hdata);
-+      hdr->data = 0;
-+      hdr->read = 0;
-+      hdr->old = 0;
-+
-+      /* skip header marked as deleted in cache */
-+      if (hdr->deleted && !fc->restore)
-+      {
-+      if (nntp_data->bcache)
-+      {
-+        dprint (2, (debugfile,
-+                    "parse_overview_line: mutt_bcache_del %s\n", buf));
-+        mutt_bcache_del (nntp_data->bcache, buf);
-+      }
-+      save = 0;
-+      }
-+    }
-+
-+    /* not chached yet, store header */
-+    else
-+    {
-+      dprint (2, (debugfile,
-+                "parse_overview_line: mutt_hcache_store %s\n", buf));
-+      mutt_hcache_store (fc->hc, buf, hdr, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
-+    }
-+  }
-+#endif
-+
-+  if (save)
-+  {
-+    hdr->index = ctx->msgcount++;
-+    hdr->read = 0;
-+    hdr->old = 0;
-+    hdr->deleted = 0;
-+    hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
-+    NHDR (hdr)->article_num = anum;
-+    if (fc->restore)
-+      hdr->changed = 1;
-+    else
-+    {
-+      nntp_article_status (ctx, hdr, NULL, anum);
-+      if (!hdr->read)
-+      nntp_parse_xref (ctx, hdr);
-+    }
-+    if (anum > nntp_data->lastLoaded)
-+      nntp_data->lastLoaded = anum;
-+  }
-+  else
-+    mutt_free_header (&hdr);
-+
-+  /* progress */
-+  if (!ctx->quiet)
-+    mutt_progress_update (&fc->progress, anum - fc->first + 1, -1);
-+  return 0;
-+}
-+
-+/* Fetch headers */
-+static int nntp_fetch_headers (CONTEXT *ctx, void *hc,
-+                             anum_t first, anum_t last, int restore)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  FETCH_CTX fc;
-+  HEADER *hdr;
-+  char buf[HUGE_STRING];
-+  int rc = 0;
-+  int oldmsgcount = ctx->msgcount;
-+  anum_t current;
-+  anum_t first_over = first;
-+#ifdef USE_HCACHE
-+  void *hdata;
-+#endif
-+
-+  /* if empty group or nothing to do */
-+  if (!last || first > last)
-+    return 0;
-+
-+  /* init fetch context */
-+  fc.ctx = ctx;
-+  fc.first = first;
-+  fc.last = last;
-+  fc.restore = restore;
-+  fc.messages = safe_calloc (last - first + 1, sizeof (unsigned char));
-+#ifdef USE_HCACHE
-+  fc.hc = hc;
-+#endif
-+
-+  /* fetch list of articles */
-+  if (option (OPTLISTGROUP) && nntp_data->nserv->hasLISTGROUP &&
-+      !nntp_data->deleted)
-+  {
-+    if (!ctx->quiet)
-+      mutt_message _("Fetching list of articles...");
-+    if (nntp_data->nserv->hasLISTGROUPrange)
-+      snprintf (buf, sizeof (buf), "LISTGROUP %s %d-%d\r\n", nntp_data->group,
-+              first, last);
-+    else
-+      snprintf (buf, sizeof (buf), "LISTGROUP %s\r\n", nntp_data->group);
-+    rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
-+                         fetch_numbers, &fc);
-+    if (rc > 0)
-+    {
-+      mutt_error ("LISTGROUP: %s", buf);
-+      mutt_sleep (2);
-+    }
-+    if (rc == 0)
-+    {
-+      for (current = first; current <= last && rc == 0; current++)
-+      {
-+      if (fc.messages[current - first])
-+        continue;
-+
-+      snprintf (buf, sizeof (buf), "%d", current);
-+      if (nntp_data->bcache)
-+      {
-+        dprint (2, (debugfile,
-+                    "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
-+        mutt_bcache_del (nntp_data->bcache, buf);
-+      }
-+
-+#ifdef USE_HCACHE
-+      if (fc.hc)
-+      {
-+        dprint (2, (debugfile,
-+                    "nntp_fetch_headers: mutt_hcache_delete %s\n", buf));
-+        mutt_hcache_delete (fc.hc, buf, strlen);
-+      }
-+#endif
-+      }
-+    }
-+  }
-+  else
-+    for (current = first; current <= last; current++)
-+      fc.messages[current - first] = 1;
-+
-+  /* fetching header from cache or server, or fallback to fetch overview */
-+  if (!ctx->quiet)
-+    mutt_progress_init (&fc.progress, _("Fetching message headers..."),
-+                      MUTT_PROGRESS_MSG, ReadInc, last - first + 1);
-+  for (current = first; current <= last && rc == 0; current++)
-+  {
-+    if (!ctx->quiet)
-+      mutt_progress_update (&fc.progress, current - first + 1, -1);
-+
-+#ifdef USE_HCACHE
-+    snprintf (buf, sizeof (buf), "%d", current);
-+#endif
-+
-+    /* delete header from cache that does not exist on server */
-+    if (!fc.messages[current - first])
-+      continue;
-+
-+    /* allocate memory for headers */
-+    if (ctx->msgcount >= ctx->hdrmax)
-+      mx_alloc_memory (ctx);
-+
-+#ifdef USE_HCACHE
-+    /* try to fetch header from cache */
-+    hdata = mutt_hcache_fetch (fc.hc, buf, strlen);
-+    if (hdata)
-+    {
-+      dprint (2, (debugfile,
-+                "nntp_fetch_headers: mutt_hcache_fetch %s\n", buf));
-+      ctx->hdrs[ctx->msgcount] =
-+      hdr = mutt_hcache_restore (hdata, NULL);
-+      FREE (&hdata);
-+      hdr->data = 0;
-+
-+      /* skip header marked as deleted in cache */
-+      if (hdr->deleted && !restore)
-+      {
-+      mutt_free_header (&hdr);
-+      if (nntp_data->bcache)
-+      {
-+        dprint (2, (debugfile,
-+                    "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
-+        mutt_bcache_del (nntp_data->bcache, buf);
-+      }
-+      continue;
-+      }
-+
-+      hdr->read = 0;
-+      hdr->old = 0;
-+    }
-+    else
-+#endif
-+
-+    /* don't try to fetch header from removed newsgroup */
-+    if (nntp_data->deleted)
-+      continue;
-+
-+    /* fallback to fetch overview */
-+    else if (nntp_data->nserv->hasOVER || nntp_data->nserv->hasXOVER)
-+      if (option (OPTLISTGROUP) && nntp_data->nserv->hasLISTGROUP)
-+      break;
-+      else
-+      continue;
-+
-+    /* fetch header from server */
-+    else
-+    {
-+      FILE *fp;
-+      char tempfile[_POSIX_PATH_MAX];
-+
-+      mutt_mktemp (tempfile, sizeof (tempfile));
-+      fp = safe_fopen (tempfile, "w+");
-+      if (!fp)
-+      {
-+      mutt_perror (tempfile);
-+      mutt_sleep (2);
-+      unlink (tempfile);
-+      rc = -1;
-+      break;
-+      }
-+
-+      snprintf (buf, sizeof (buf), "HEAD %d\r\n", current);
-+      rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
-+                           fetch_tempfile, fp);
-+      if (rc)
-+      {
-+      fclose (fp);
-+      unlink (tempfile);
-+      if (rc < 0)
-+        break;
-+
-+      /* invalid response */
-+      if (mutt_strncmp ("423", buf, 3))
-+      {
-+        mutt_error ("HEAD: %s", buf);
-+        mutt_sleep (2);
-+        break;
-+      }
-+
-+      /* no such article */
-+      if (nntp_data->bcache)
-+      {
-+        snprintf (buf, sizeof (buf), "%d", current);
-+        dprint (2, (debugfile,
-+                    "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
-+        mutt_bcache_del (nntp_data->bcache, buf);
-+      }
-+      rc = 0;
-+      continue;
-+      }
-+
-+      /* parse header */
-+      hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
-+      hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
-+      hdr->received = hdr->date_sent;
-+      fclose (fp);
-+      unlink (tempfile);
-+    }
-+
-+    /* save header in context */
-+    hdr->index = ctx->msgcount++;
-+    hdr->read = 0;
-+    hdr->old = 0;
-+    hdr->deleted = 0;
-+    hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
-+    NHDR (hdr)->article_num = current;
-+    if (restore)
-+      hdr->changed = 1;
-+    else
-+    {
-+      nntp_article_status (ctx, hdr, NULL, NHDR (hdr)->article_num);
-+      if (!hdr->read)
-+      nntp_parse_xref (ctx, hdr);
-+    }
-+    if (current > nntp_data->lastLoaded)
-+      nntp_data->lastLoaded = current;
-+    first_over = current + 1;
-+  }
-+
-+  if (!option (OPTLISTGROUP) || !nntp_data->nserv->hasLISTGROUP)
-+    current = first_over;
-+
-+  /* fetch overview information */
-+  if (current <= last && rc == 0 && !nntp_data->deleted) {
-+    char *cmd = nntp_data->nserv->hasOVER ? "OVER" : "XOVER";
-+    snprintf (buf, sizeof (buf), "%s %d-%d\r\n", cmd, current, last);
-+    rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
-+       parse_overview_line, &fc);
-+    if (rc > 0)
-+    {
-+      mutt_error ("%s: %s", cmd, buf);
-+      mutt_sleep (2);
-+    }
-+  }
-+
-+  if (ctx->msgcount > oldmsgcount)
-+    mx_update_context (ctx, ctx->msgcount - oldmsgcount);
-+
-+  FREE (&fc.messages);
-+  if (rc != 0)
-+    return -1;
-+  mutt_clear_error ();
-+  return 0;
-+}
-+
-+/* Open newsgroup */
-+static int nntp_open_mailbox (CONTEXT *ctx)
-+{
-+  NNTP_SERVER *nserv;
-+  NNTP_DATA *nntp_data;
-+  char buf[HUGE_STRING];
-+  char server[LONG_STRING];
-+  char *group;
-+  int rc;
-+  void *hc = NULL;
-+  anum_t first, last, count = 0;
-+  ciss_url_t url;
-+
-+  strfcpy (buf, ctx->path, sizeof (buf));
-+  if (url_parse_ciss (&url, buf) < 0 || !url.path ||
-+     !(url.scheme == U_NNTP || url.scheme == U_NNTPS))
-+  {
-+    mutt_error (_("%s is an invalid newsgroup specification!"), ctx->path);
-+    mutt_sleep (2);
-+    return -1;
-+  }
-+
-+  group = url.path;
-+  url.path = strchr (url.path, '\0');
-+  url_ciss_tostring (&url, server, sizeof (server), 0);
-+  nserv = nntp_select_server (server, 1);
-+  if (!nserv)
-+    return -1;
-+  CurrentNewsSrv = nserv;
-+
-+  /* find news group data structure */
-+  nntp_data = hash_find (nserv->groups_hash, group);
-+  if (!nntp_data)
-+  {
-+    nntp_newsrc_close (nserv);
-+    mutt_error (_("Newsgroup %s not found on the server."), group);
-+    mutt_sleep (2);
-+    return -1;
-+  }
-+
-+  mutt_bit_unset (ctx->rights, MUTT_ACL_INSERT);
-+  if (!nntp_data->newsrc_ent && !nntp_data->subscribed &&
-+      !option (OPTSAVEUNSUB))
-+    ctx->readonly = 1;
-+
-+  /* select newsgroup */
-+  mutt_message (_("Selecting %s..."), group);
-+  buf[0] = '\0';
-+  if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
-+  {
-+    nntp_newsrc_close (nserv);
-+    return -1;
-+  }
-+
-+  /* newsgroup not found, remove it */
-+  if (!mutt_strncmp ("411", buf, 3))
-+  {
-+    mutt_error (_("Newsgroup %s has been removed from the server."),
-+              nntp_data->group);
-+    if (!nntp_data->deleted)
-+    {
-+      nntp_data->deleted = 1;
-+      nntp_active_save_cache (nserv);
-+    }
-+    if (nntp_data->newsrc_ent && !nntp_data->subscribed &&
-+      !option (OPTSAVEUNSUB))
-+    {
-+      FREE (&nntp_data->newsrc_ent);
-+      nntp_data->newsrc_len = 0;
-+      nntp_delete_group_cache (nntp_data);
-+      nntp_newsrc_update (nserv);
-+    }
-+    mutt_sleep (2);
-+  }
-+
-+  /* parse newsgroup info */
-+  else {
-+    if (sscanf (buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
-+    {
-+      nntp_newsrc_close (nserv);
-+      mutt_error ("GROUP: %s", buf);
-+      mutt_sleep (2);
-+      return -1;
-+    }
-+    nntp_data->firstMessage = first;
-+    nntp_data->lastMessage = last;
-+    nntp_data->deleted = 0;
-+
-+    /* get description if empty */
-+    if (option (OPTLOADDESC) && !nntp_data->desc)
-+    {
-+      if (get_description (nntp_data, NULL, NULL) < 0)
-+      {
-+      nntp_newsrc_close (nserv);
-+      return -1;
-+      }
-+      if (nntp_data->desc)
-+      nntp_active_save_cache (nserv);
-+    }
-+  }
-+
-+  time (&nserv->check_time);
-+  ctx->data = nntp_data;
-+  if (!nntp_data->bcache && (nntp_data->newsrc_ent ||
-+      nntp_data->subscribed || option (OPTSAVEUNSUB)))
-+    nntp_data->bcache = mutt_bcache_open (&nserv->conn->account,
-+                      nntp_data->group);
-+
-+  /* strip off extra articles if adding context is greater than $nntp_context */
-+  first = nntp_data->firstMessage;
-+  if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext)
-+    first = nntp_data->lastMessage - NntpContext + 1;
-+  nntp_data->lastLoaded = first ? first - 1 : 0;
-+  count = nntp_data->firstMessage;
-+  nntp_data->firstMessage = first;
-+  nntp_bcache_update (nntp_data);
-+  nntp_data->firstMessage = count;
-+#ifdef USE_HCACHE
-+  hc = nntp_hcache_open (nntp_data);
-+  nntp_hcache_update (nntp_data, hc);
-+#endif
-+  if (!hc)
-+  {
-+    mutt_bit_unset (ctx->rights, MUTT_ACL_WRITE);
-+    mutt_bit_unset (ctx->rights, MUTT_ACL_DELETE);
-+  }
-+  nntp_newsrc_close (nserv);
-+  rc = nntp_fetch_headers (ctx, hc, first, nntp_data->lastMessage, 0);
-+#ifdef USE_HCACHE
-+  mutt_hcache_close (hc);
-+#endif
-+  if (rc < 0)
-+    return -1;
-+  nntp_data->lastLoaded = nntp_data->lastMessage;
-+  nserv->newsrc_modified = 0;
-+  return 0;
-+}
-+
-+/* Fetch message */
-+static int nntp_open_message (CONTEXT *ctx, MESSAGE *msg, int msgno)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  NNTP_ACACHE *acache;
-+  HEADER *hdr = ctx->hdrs[msgno];
-+  char buf[_POSIX_PATH_MAX];
-+  char article[16];
-+  char *fetch_msg = _("Fetching message...");
-+  int rc;
-+
-+  /* try to get article from cache */
-+  acache = &nntp_data->acache[hdr->index % NNTP_ACACHE_LEN];
-+  if (acache->path)
-+  {
-+    if (acache->index == hdr->index)
-+    {
-+      msg->fp = fopen (acache->path, "r");
-+      if (msg->fp)
-+      return 0;
-+    }
-+    /* clear previous entry */
-+    else
-+    {
-+      unlink (acache->path);
-+      FREE (&acache->path);
-+    }
-+  }
-+  snprintf (article, sizeof (article), "%d", NHDR (hdr)->article_num);
-+  msg->fp = mutt_bcache_get (nntp_data->bcache, article);
-+  if (msg->fp)
-+  {
-+    if (NHDR (hdr)->parsed)
-+      return 0;
-+  }
-+  else
-+  {
-+    /* don't try to fetch article from removed newsgroup */
-+    if (nntp_data->deleted)
-+      return -1;
-+
-+    /* create new cache file */
-+    mutt_message (fetch_msg);
-+    msg->fp = mutt_bcache_put (nntp_data->bcache, article, 1);
-+    if (!msg->fp)
-+    {
-+      mutt_mktemp (buf, sizeof (buf));
-+      acache->path = safe_strdup (buf);
-+      acache->index = hdr->index;
-+      msg->fp = safe_fopen (acache->path, "w+");
-+      if (!msg->fp)
-+      {
-+      mutt_perror (acache->path);
-+      unlink (acache->path);
-+      FREE (&acache->path);
-+      return -1;
-+      }
-+    }
-+
-+    /* fetch message to cache file */
-+    snprintf (buf, sizeof (buf), "ARTICLE %s\r\n",
-+            NHDR (hdr)->article_num ? article : hdr->env->message_id);
-+    rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), fetch_msg,
-+                         fetch_tempfile, msg->fp);
-+    if (rc)
-+    {
-+      safe_fclose (&msg->fp);
-+      if (acache->path)
-+      {
-+      unlink (acache->path);
-+      FREE (&acache->path);
-+      }
-+      if (rc > 0)
-+      {
-+      if (!mutt_strncmp (NHDR (hdr)->article_num ? "423" : "430", buf, 3))
-+        mutt_error (_("Article %d not found on the server."),
-+                    NHDR (hdr)->article_num ? article : hdr->env->message_id);
-+      else
-+        mutt_error ("ARTICLE: %s", buf);
-+      }
-+      return -1;
-+    }
-+
-+    if (!acache->path)
-+      mutt_bcache_commit (nntp_data->bcache, article);
-+  }
-+
-+  /* replace envelope with new one
-+   * hash elements must be updated because pointers will be changed */
-+  if (ctx->id_hash && hdr->env->message_id)
-+    hash_delete (ctx->id_hash, hdr->env->message_id, hdr, NULL);
-+  if (ctx->subj_hash && hdr->env->real_subj)
-+    hash_delete (ctx->subj_hash, hdr->env->real_subj, hdr, NULL);
-+
-+  mutt_free_envelope (&hdr->env);
-+  hdr->env = mutt_read_rfc822_header (msg->fp, hdr, 0, 0);
-+
-+  if (ctx->id_hash && hdr->env->message_id)
-+    hash_insert (ctx->id_hash, hdr->env->message_id, hdr);
-+  if (ctx->subj_hash && hdr->env->real_subj)
-+    hash_insert (ctx->subj_hash, hdr->env->real_subj, hdr);
-+
-+  /* fix content length */
-+  fseek (msg->fp, 0, SEEK_END);
-+  hdr->content->length = ftell (msg->fp) - hdr->content->offset;
-+
-+  /* this is called in mutt before the open which fetches the message,
-+   * which is probably wrong, but we just call it again here to handle
-+   * the problem instead of fixing it */
-+  NHDR (hdr)->parsed = 1;
-+  mutt_parse_mime_message (ctx, hdr);
-+
-+  /* these would normally be updated in mx_update_context(), but the
-+   * full headers aren't parsed with overview, so the information wasn't
-+   * available then */
-+  if (WithCrypto)
-+    hdr->security = crypt_query (hdr->content);
-+
-+  rewind (msg->fp);
-+  mutt_clear_error();
-+  return 0;
-+}
-+
-+/* Close message */
-+static int nntp_close_message (CONTEXT *ctx, MESSAGE *msg)
-+{
-+  return safe_fclose (&msg->fp);
-+}
-+
-+/* Post article */
-+int nntp_post (const char *msg) {
-+  NNTP_DATA *nntp_data, nntp_tmp;
-+  FILE *fp;
-+  char buf[LONG_STRING];
-+  size_t len;
-+
-+  if (Context && Context->magic == MUTT_NNTP)
-+    nntp_data = Context->data;
-+  else
-+  {
-+    CurrentNewsSrv = nntp_select_server (NewsServer, 0);
-+    if (!CurrentNewsSrv)
-+      return -1;
-+
-+    nntp_data = &nntp_tmp;
-+    nntp_data->nserv = CurrentNewsSrv;
-+    nntp_data->group = NULL;
-+  }
-+
-+  fp = safe_fopen (msg, "r");
-+  if (!fp)
-+  {
-+    mutt_perror (msg);
-+    return -1;
-+  }
-+
-+  strfcpy (buf, "POST\r\n", sizeof (buf));
-+  if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
-+    return -1;
-+  if (buf[0] != '3')
-+  {
-+    mutt_error (_("Can't post article: %s"), buf);
-+    return -1;
-+  }
-+
-+  buf[0] = '.';
-+  buf[1] = '\0';
-+  while (fgets (buf + 1, sizeof (buf) - 2, fp))
-+  {
-+    len = strlen (buf);
-+    if (buf[len - 1] == '\n')
-+    {
-+      buf[len - 1] = '\r';
-+      buf[len] = '\n';
-+      len++;
-+      buf[len] = '\0';
-+    }
-+    if (mutt_socket_write_d (nntp_data->nserv->conn,
-+      buf[1] == '.' ? buf : buf + 1, -1, MUTT_SOCK_LOG_HDR) < 0)
-+      return nntp_connect_error (nntp_data->nserv);
-+  }
-+  fclose (fp);
-+
-+  if ((buf[strlen (buf) - 1] != '\n' &&
-+      mutt_socket_write_d (nntp_data->nserv->conn, "\r\n", -1, MUTT_SOCK_LOG_HDR) < 0) ||
-+      mutt_socket_write_d (nntp_data->nserv->conn, ".\r\n", -1, MUTT_SOCK_LOG_HDR) < 0 ||
-+      mutt_socket_readln (buf, sizeof (buf), nntp_data->nserv->conn) < 0)
-+    return nntp_connect_error (nntp_data->nserv);
-+  if (buf[0] != '2')
-+  {
-+    mutt_error (_("Can't post article: %s"), buf);
-+    return -1;
-+  }
-+  return 0;
-+}
-+
-+/* Free up memory associated with the newsgroup context */
-+static int nntp_close_mailbox (CONTEXT *ctx)
-+{
-+  NNTP_DATA *nntp_data = ctx->data, *nntp_tmp;
-+
-+  if (!nntp_data)
-+    return 0;
-+
-+  nntp_data->unread = ctx->unread;
-+
-+  nntp_acache_free (nntp_data);
-+  if (!nntp_data->nserv || !nntp_data->nserv->groups_hash || !nntp_data->group)
-+    return 0;
-+
-+  nntp_tmp = hash_find (nntp_data->nserv->groups_hash, nntp_data->group);
-+  if (nntp_tmp == NULL || nntp_tmp != nntp_data)
-+    nntp_data_free (nntp_data);
-+  return 0;
-+}
-+
-+/* Get date and time from server */
-+int nntp_date (NNTP_SERVER *nserv, time_t *now)
-+{
-+  if (nserv->hasDATE)
-+  {
-+    NNTP_DATA nntp_data;
-+    char buf[LONG_STRING];
-+    struct tm tm;
-+
-+    nntp_data.nserv = nserv;
-+    nntp_data.group = NULL;
-+    strfcpy (buf, "DATE\r\n", sizeof (buf));
-+    if (nntp_query (&nntp_data, buf, sizeof (buf)) < 0)
-+      return -1;
-+
-+    if (sscanf (buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
-+              &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
-+    {
-+      tm.tm_year -= 1900;
-+      tm.tm_mon--;
-+      *now = timegm (&tm);
-+      if (*now >= 0)
-+      {
-+      dprint (1, (debugfile, "nntp_date: server time is %d\n", *now));
-+      return 0;
-+      }
-+    }
-+  }
-+  time (now);
-+  return 0;
-+}
-+
-+/* Fetch list of all newsgroups from server */
-+int nntp_active_fetch (NNTP_SERVER *nserv, unsigned int new)
-+{
-+  NNTP_DATA nntp_data;
-+  char msg[SHORT_STRING];
-+  char buf[LONG_STRING];
-+  unsigned int i;
-+  int rc;
-+
-+  snprintf (msg, sizeof (msg), _("Loading list of groups from server %s..."),
-+          nserv->conn->account.host);
-+  mutt_message (msg);
-+  if (nntp_date (nserv, &nserv->newgroups_time) < 0)
-+    return -1;
-+
-+  nntp_data.nserv = nserv;
-+  nntp_data.group = NULL;
-+  i = nserv->groups_num;
-+  strfcpy (buf, "LIST\r\n", sizeof (buf));
-+  rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
-+                       nntp_add_group, nserv);
-+  if (rc)
-+  {
-+    if (rc > 0)
-+    {
-+      mutt_error ("LIST: %s", buf);
-+      mutt_sleep (2);
-+    }
-+    return -1;
-+  }
-+
-+  if (new)
-+  {
-+    for (; i < nserv->groups_num; i++)
-+    {
-+      NNTP_DATA *nntp_data = nserv->groups_list[i];
-+      nntp_data->new = 1;
-+    }
-+  }
-+
-+  for (i = 0; i < nserv->groups_num; i++)
-+  {
-+    NNTP_DATA *nntp_data = nserv->groups_list[i];
-+
-+    if (nntp_data && nntp_data->deleted && !nntp_data->newsrc_ent)
-+    {
-+      nntp_delete_group_cache (nntp_data);
-+      hash_delete (nserv->groups_hash, nntp_data->group, NULL, nntp_data_free);
-+      nserv->groups_list[i] = NULL;
-+    }
-+  }
-+
-+  if (option (OPTLOADDESC))
-+    rc = get_description (&nntp_data, "*", _("Loading descriptions..."));
-+
-+  nntp_active_save_cache (nserv);
-+  if (rc < 0)
-+    return -1;
-+  mutt_clear_error ();
-+  return 0;
-+}
-+
-+/* Check newsgroup for new articles:
-+ *  1 - new articles found
-+ *  0 - no change
-+ * -1 - lost connection */
-+static int nntp_group_poll (NNTP_DATA *nntp_data, int update_stat)
-+{
-+  char buf[LONG_STRING] = "";
-+  anum_t count, first, last;
-+
-+  /* use GROUP command to poll newsgroup */
-+  if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
-+    return -1;
-+  if (sscanf (buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
-+    return 0;
-+  if (first == nntp_data->firstMessage && last == nntp_data->lastMessage)
-+    return 0;
-+
-+  /* articles have been renumbered */
-+  if (last < nntp_data->lastMessage)
-+  {
-+    nntp_data->lastCached = 0;
-+    if (nntp_data->newsrc_len)
-+    {
-+      safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
-+      nntp_data->newsrc_len = 1;
-+      nntp_data->newsrc_ent[0].first = 1;
-+      nntp_data->newsrc_ent[0].last = 0;
-+    }
-+  }
-+  nntp_data->firstMessage = first;
-+  nntp_data->lastMessage = last;
-+  if (!update_stat)
-+    return 1;
-+
-+  /* update counters */
-+  else if (!last || (!nntp_data->newsrc_ent && !nntp_data->lastCached))
-+    nntp_data->unread = count;
-+  else
-+    nntp_group_unread_stat (nntp_data);
-+  return 1;
-+}
-+
-+/* Check current newsgroup for new articles, leave newsrc locked:
-+ *  MUTT_REOPENED - articles have been renumbered or removed from server
-+ *  MUTT_NEW_MAIL - new articles found
-+ *  0           - no change
-+ * -1           - lost connection */
-+static int check_mailbox (CONTEXT *ctx)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  NNTP_SERVER *nserv = nntp_data->nserv;
-+  time_t now = time (NULL);
-+  int i, j;
-+  int rc, ret = 0;
-+  void *hc = NULL;
-+
-+  if (nserv->check_time + NewsPollTimeout > now)
-+    return 0;
-+
-+  mutt_message _("Checking for new messages...");
-+  if (nntp_newsrc_parse (nserv) < 0)
-+    return -1;
-+
-+  nserv->check_time = now;
-+  rc = nntp_group_poll (nntp_data, 0);
-+  if (rc < 0)
-+  {
-+    nntp_newsrc_close (nserv);
-+    return -1;
-+  }
-+  if (rc)
-+    nntp_active_save_cache (nserv);
-+
-+  /* articles have been renumbered, remove all headers */
-+  if (nntp_data->lastMessage < nntp_data->lastLoaded)
-+  {
-+    for (i = 0; i < ctx->msgcount; i++)
-+      mutt_free_header (&ctx->hdrs[i]);
-+    ctx->msgcount = 0;
-+    ctx->tagged = 0;
-+
-+    if (nntp_data->lastMessage < nntp_data->lastLoaded)
-+    {
-+      nntp_data->lastLoaded = nntp_data->firstMessage - 1;
-+      if (NntpContext && nntp_data->lastMessage - nntp_data->lastLoaded >
-+        NntpContext)
-+      nntp_data->lastLoaded = nntp_data->lastMessage - NntpContext;
-+    }
-+    ret = MUTT_REOPENED;
-+  }
-+
-+  /* .newsrc has been externally modified */
-+  if (nserv->newsrc_modified)
-+  {
-+    anum_t anum;
-+#ifdef USE_HCACHE
-+    unsigned char *messages;
-+    char buf[16];
-+    void *hdata;
-+    HEADER *hdr;
-+    anum_t first = nntp_data->firstMessage;
-+
-+    if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext)
-+      first = nntp_data->lastMessage - NntpContext + 1;
-+    messages = safe_calloc (nntp_data->lastLoaded - first + 1,
-+                          sizeof (unsigned char));
-+    hc = nntp_hcache_open (nntp_data);
-+    nntp_hcache_update (nntp_data, hc);
-+#endif
-+
-+    /* update flags according to .newsrc */
-+    for (i = j = 0; i < ctx->msgcount; i++)
-+    {
-+      int flagged = 0;
-+      anum = NHDR (ctx->hdrs[i])->article_num;
-+
-+#ifdef USE_HCACHE
-+      /* check hcache for flagged and deleted flags */
-+      if (hc)
-+      {
-+      if (anum >= first && anum <= nntp_data->lastLoaded)
-+        messages[anum - first] = 1;
-+
-+      snprintf (buf, sizeof (buf), "%d", anum);
-+      hdata = mutt_hcache_fetch (hc, buf, strlen);
-+      if (hdata)
-+      {
-+        int deleted;
-+
-+        dprint (2, (debugfile,
-+                    "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf));
-+        hdr = mutt_hcache_restore (hdata, NULL);
-+        FREE (&hdata);
-+        hdr->data = 0;
-+        deleted = hdr->deleted;
-+        flagged = hdr->flagged;
-+        mutt_free_header (&hdr);
-+
-+        /* header marked as deleted, removing from context */
-+        if (deleted)
-+        {
-+          mutt_set_flag (ctx, ctx->hdrs[i], MUTT_TAG, 0);
-+          mutt_free_header (&ctx->hdrs[i]);
-+          continue;
-+        }
-+      }
-+      }
-+#endif
-+
-+      if (!ctx->hdrs[i]->changed)
-+      {
-+      ctx->hdrs[i]->flagged = flagged;
-+      ctx->hdrs[i]->read = 0;
-+      ctx->hdrs[i]->old = 0;
-+      nntp_article_status (ctx, ctx->hdrs[i], NULL, anum);
-+      if (!ctx->hdrs[i]->read)
-+        nntp_parse_xref (ctx, ctx->hdrs[i]);
-+      }
-+      ctx->hdrs[j++] = ctx->hdrs[i];
-+    }
-+
-+#ifdef USE_HCACHE
-+    ctx->msgcount = j;
-+
-+    /* restore headers without "deleted" flag */
-+    for (anum = first; anum <= nntp_data->lastLoaded; anum++)
-+    {
-+      if (messages[anum - first])
-+      continue;
-+
-+      snprintf (buf, sizeof (buf), "%d", anum);
-+      hdata = mutt_hcache_fetch (hc, buf, strlen);
-+      if (hdata)
-+      {
-+      dprint (2, (debugfile,
-+                  "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf));
-+      if (ctx->msgcount >= ctx->hdrmax)
-+        mx_alloc_memory (ctx);
-+
-+      ctx->hdrs[ctx->msgcount] =
-+      hdr = mutt_hcache_restore (hdata, NULL);
-+      FREE (&hdata);
-+      hdr->data = 0;
-+      if (hdr->deleted)
-+      {
-+        mutt_free_header (&hdr);
-+        if (nntp_data->bcache)
-+        {
-+          dprint (2, (debugfile,
-+                      "nntp_check_mailbox: mutt_bcache_del %s\n", buf));
-+          mutt_bcache_del (nntp_data->bcache, buf);
-+        }
-+        continue;
-+      }
-+
-+      ctx->msgcount++;
-+      hdr->read = 0;
-+      hdr->old = 0;
-+      hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
-+      NHDR (hdr)->article_num = anum;
-+      nntp_article_status (ctx, hdr, NULL, anum);
-+      if (!hdr->read)
-+        nntp_parse_xref (ctx, hdr);
-+      }
-+    }
-+    FREE (&messages);
-+#endif
-+
-+    nserv->newsrc_modified = 0;
-+    ret = MUTT_REOPENED;
-+  }
-+
-+  /* some headers were removed, context must be updated */
-+  if (ret == MUTT_REOPENED)
-+  {
-+    if (ctx->subj_hash)
-+      hash_destroy (&ctx->subj_hash, NULL);
-+    if (ctx->id_hash)
-+      hash_destroy (&ctx->id_hash, NULL);
-+    mutt_clear_threads (ctx);
-+
-+    ctx->vcount = 0;
-+    ctx->deleted = 0;
-+    ctx->new = 0;
-+    ctx->unread = 0;
-+    ctx->flagged = 0;
-+    ctx->changed = 0;
-+    ctx->id_hash = NULL;
-+    ctx->subj_hash = NULL;
-+    mx_update_context (ctx, ctx->msgcount);
-+  }
-+
-+  /* fetch headers of new articles */
-+  if (nntp_data->lastMessage > nntp_data->lastLoaded)
-+  {
-+    int oldmsgcount = ctx->msgcount;
-+    int quiet = ctx->quiet;
-+    ctx->quiet = 1;
-+#ifdef USE_HCACHE
-+    if (!hc)
-+    {
-+      hc = nntp_hcache_open (nntp_data);
-+      nntp_hcache_update (nntp_data, hc);
-+    }
-+#endif
-+    rc = nntp_fetch_headers (ctx, hc, nntp_data->lastLoaded + 1,
-+                           nntp_data->lastMessage, 0);
-+    ctx->quiet = quiet;
-+    if (rc >= 0)
-+      nntp_data->lastLoaded = nntp_data->lastMessage;
-+    if (ret == 0 && ctx->msgcount > oldmsgcount)
-+      ret = MUTT_NEW_MAIL;
-+  }
-+
-+#ifdef USE_HCACHE
-+  mutt_hcache_close (hc);
-+#endif
-+  if (ret)
-+    nntp_newsrc_close (nserv);
-+  mutt_clear_error ();
-+  return ret;
-+}
-+
-+/* Check current newsgroup for new articles:
-+ *  MUTT_REOPENED - articles have been renumbered or removed from server
-+ *  MUTT_NEW_MAIL - new articles found
-+ *  0           - no change
-+ * -1           - lost connection */
-+static int nntp_check_mailbox (CONTEXT *ctx, int *index_hint)
-+{
-+  int ret = check_mailbox (ctx);
-+  if (ret == 0)
-+  {
-+    NNTP_DATA *nntp_data = ctx->data;
-+    NNTP_SERVER *nserv = nntp_data->nserv;
-+    nntp_newsrc_close (nserv);
-+  }
-+  return ret;
-+}
-+
-+/* Save changes to .newsrc and cache */
-+static int nntp_sync_mailbox (CONTEXT *ctx, int *index_hint)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  int rc, i;
-+#ifdef USE_HCACHE
-+  header_cache_t *hc;
-+#endif
-+
-+  /* check for new articles */
-+  nntp_data->nserv->check_time = 0;
-+  rc = check_mailbox (ctx);
-+  if (rc)
-+    return rc;
-+
-+#ifdef USE_HCACHE
-+  nntp_data->lastCached = 0;
-+  hc = nntp_hcache_open (nntp_data);
-+#endif
-+
-+  for (i = 0; i < ctx->msgcount; i++)
-+  {
-+    HEADER *hdr = ctx->hdrs[i];
-+    char buf[16];
-+
-+    snprintf (buf, sizeof (buf), "%d", NHDR (hdr)->article_num);
-+    if (nntp_data->bcache && hdr->deleted)
-+    {
-+      dprint (2, (debugfile, "nntp_sync_mailbox: mutt_bcache_del %s\n", buf));
-+      mutt_bcache_del (nntp_data->bcache, buf);
-+    }
-+
-+#ifdef USE_HCACHE
-+    if (hc && (hdr->changed || hdr->deleted))
-+    {
-+      dprint (2, (debugfile, "nntp_sync_mailbox: mutt_hcache_store %s\n", buf));
-+      mutt_hcache_store (hc, buf, hdr, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
-+    }
-+#endif
-+  }
-+
-+#ifdef USE_HCACHE
-+  if (hc)
-+  {
-+    mutt_hcache_close (hc);
-+    nntp_data->lastCached = nntp_data->lastLoaded;
-+  }
-+#endif
-+
-+  /* save .newsrc entries */
-+  nntp_newsrc_gen_entries (ctx);
-+  nntp_newsrc_update (nntp_data->nserv);
-+  nntp_newsrc_close (nntp_data->nserv);
-+  return 0;
-+}
-+
-+/* Check for new groups and new articles in subscribed groups:
-+ *  1 - new groups found
-+ *  0 - no new groups
-+ * -1 - error */
-+int nntp_check_new_groups (NNTP_SERVER *nserv)
-+{
-+  NNTP_DATA nntp_data;
-+  time_t now;
-+  struct tm *tm;
-+  char buf[LONG_STRING];
-+  char *msg = _("Checking for new newsgroups...");
-+  unsigned int i;
-+  int rc, update_active = FALSE;
-+
-+  if (!nserv || !nserv->newgroups_time)
-+    return -1;
-+
-+  /* check subscribed newsgroups for new articles */
-+  if (option (OPTSHOWNEWNEWS))
-+  {
-+    mutt_message _("Checking for new messages...");
-+    for (i = 0; i < nserv->groups_num; i++)
-+    {
-+      NNTP_DATA *nntp_data = nserv->groups_list[i];
-+
-+      if (nntp_data && nntp_data->subscribed)
-+      {
-+      rc = nntp_group_poll (nntp_data, 1);
-+      if (rc < 0)
-+        return -1;
-+      if (rc > 0)
-+        update_active = TRUE;
-+      }
-+    }
-+    /* select current newsgroup */
-+    if (Context && Context->magic == MUTT_NNTP)
-+    {
-+      buf[0] = '\0';
-+      if (nntp_query ((NNTP_DATA *)Context->data, buf, sizeof (buf)) < 0)
-+      return -1;
-+    }
-+  }
-+  else if (nserv->newgroups_time)
-+    return 0;
-+
-+  /* get list of new groups */
-+  mutt_message (msg);
-+  if (nntp_date (nserv, &now) < 0)
-+    return -1;
-+  nntp_data.nserv = nserv;
-+  if (Context && Context->magic == MUTT_NNTP)
-+    nntp_data.group = ((NNTP_DATA *)Context->data)->group;
-+  else
-+    nntp_data.group = NULL;
-+  i = nserv->groups_num;
-+  tm = gmtime (&nserv->newgroups_time);
-+  snprintf (buf, sizeof (buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n",
-+          tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
-+          tm->tm_hour, tm->tm_min, tm->tm_sec);
-+  rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
-+                       nntp_add_group, nserv);
-+  if (rc)
-+  {
-+    if (rc > 0)
-+    {
-+      mutt_error ("NEWGROUPS: %s", buf);
-+      mutt_sleep (2);
-+    }
-+    return -1;
-+  }
-+
-+  /* new groups found */
-+  rc = 0;
-+  if (nserv->groups_num != i)
-+  {
-+    int groups_num = i;
-+
-+    nserv->newgroups_time = now;
-+    for (; i < nserv->groups_num; i++)
-+    {
-+      NNTP_DATA *nntp_data = nserv->groups_list[i];
-+      nntp_data->new = 1;
-+    }
-+
-+    /* loading descriptions */
-+    if (option (OPTLOADDESC))
-+    {
-+      unsigned int count = 0;
-+      progress_t progress;
-+
-+      mutt_progress_init (&progress, _("Loading descriptions..."),
-+                        MUTT_PROGRESS_MSG, ReadInc, nserv->groups_num - i);
-+      for (i = groups_num; i < nserv->groups_num; i++)
-+      {
-+      NNTP_DATA *nntp_data = nserv->groups_list[i];
-+
-+      if (get_description (nntp_data, NULL, NULL) < 0)
-+        return -1;
-+      mutt_progress_update (&progress, ++count, -1);
-+      }
-+    }
-+    update_active = TRUE;
-+    rc = 1;
-+  }
-+  if (update_active)
-+    nntp_active_save_cache (nserv);
-+  mutt_clear_error ();
-+  return rc;
-+}
-+
-+/* Fetch article by Message-ID:
-+ *  0 - success
-+ *  1 - no such article
-+ * -1 - error */
-+int nntp_check_msgid (CONTEXT *ctx, const char *msgid)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  HEADER *hdr;
-+  FILE *fp;
-+  char tempfile[_POSIX_PATH_MAX];
-+  char buf[LONG_STRING];
-+  int rc;
-+
-+  mutt_mktemp (tempfile, sizeof (tempfile));
-+  fp = safe_fopen (tempfile, "w+");
-+  if (!fp)
-+  {
-+    mutt_perror (tempfile);
-+    unlink (tempfile);
-+    return -1;
-+  }
-+
-+  snprintf (buf, sizeof (buf), "HEAD %s\r\n", msgid);
-+  rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
-+                       fetch_tempfile, fp);
-+  if (rc)
-+  {
-+    fclose (fp);
-+    unlink (tempfile);
-+    if (rc < 0)
-+      return -1;
-+    if (!mutt_strncmp ("430", buf, 3))
-+      return 1;
-+    mutt_error ("HEAD: %s", buf);
-+    return -1;
-+  }
-+
-+  /* parse header */
-+  if (ctx->msgcount == ctx->hdrmax)
-+    mx_alloc_memory (ctx);
-+  hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
-+  hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
-+  hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
-+  fclose (fp);
-+  unlink (tempfile);
-+
-+  /* get article number */
-+  if (hdr->env->xref)
-+    nntp_parse_xref (ctx, hdr);
-+  else
-+  {
-+    snprintf (buf, sizeof (buf), "STAT %s\r\n", msgid);
-+    if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
-+    {
-+      mutt_free_header (&hdr);
-+      return -1;
-+    }
-+    sscanf (buf + 4, ANUM, &NHDR (hdr)->article_num);
-+  }
-+
-+  /* reset flags */
-+  hdr->read = 0;
-+  hdr->old = 0;
-+  hdr->deleted = 0;
-+  hdr->changed = 1;
-+  hdr->received = hdr->date_sent;
-+  hdr->index = ctx->msgcount++;
-+  mx_update_context (ctx, 1);
-+  return 0;
-+}
-+
-+typedef struct
-+{
-+  CONTEXT *ctx;
-+  unsigned int num;
-+  unsigned int max;
-+  anum_t *child;
-+} CHILD_CTX;
-+
-+/* Parse XPAT line */
-+static int fetch_children (char *line, void *data)
-+{
-+  CHILD_CTX *cc = data;
-+  anum_t anum;
-+  unsigned int i;
-+
-+  if (!line || sscanf (line, ANUM, &anum) != 1)
-+    return 0;
-+  for (i = 0; i < cc->ctx->msgcount; i++)
-+    if (NHDR (cc->ctx->hdrs[i])->article_num == anum)
-+      return 0;
-+  if (cc->num >= cc->max)
-+  {
-+    cc->max *= 2;
-+    safe_realloc (&cc->child, sizeof (anum_t) * cc->max);
-+  }
-+  cc->child[cc->num++] = anum;
-+  return 0;
-+}
-+
-+/* Fetch children of article with the Message-ID */
-+int nntp_check_children (CONTEXT *ctx, const char *msgid)
-+{
-+  NNTP_DATA *nntp_data = ctx->data;
-+  CHILD_CTX cc;
-+  char buf[STRING];
-+  int i, rc, quiet;
-+  void *hc = NULL;
-+
-+  if (!nntp_data || !nntp_data->nserv)
-+    return -1;
-+  if (nntp_data->firstMessage > nntp_data->lastLoaded)
-+    return 0;
-+
-+  /* init context */
-+  cc.ctx = ctx;
-+  cc.num = 0;
-+  cc.max = 10;
-+  cc.child = safe_malloc (sizeof (anum_t) * cc.max);
-+
-+  /* fetch numbers of child messages */
-+  snprintf (buf, sizeof (buf), "XPAT References %d-%d *%s*\r\n",
-+          nntp_data->firstMessage, nntp_data->lastLoaded, msgid);
-+  rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
-+                       fetch_children, &cc);
-+  if (rc)
-+  {
-+    FREE (&cc.child);
-+    if (rc > 0) {
-+      if (mutt_strncmp ("500", buf, 3))
-+      mutt_error ("XPAT: %s", buf);
-+      else
-+      mutt_error _("Unable to find child articles because server does not support XPAT command.");
-+    }
-+    return -1;
-+  }
-+
-+  /* fetch all found messages */
-+  quiet = ctx->quiet;
-+  ctx->quiet = 1;
-+#ifdef USE_HCACHE
-+  hc = nntp_hcache_open (nntp_data);
-+#endif
-+  for (i = 0; i < cc.num; i++)
-+  {
-+    rc = nntp_fetch_headers (ctx, hc, cc.child[i], cc.child[i], 1);
-+    if (rc < 0)
-+      break;
-+  }
-+#ifdef USE_HCACHE
-+  mutt_hcache_close (hc);
-+#endif
-+  ctx->quiet = quiet;
-+  FREE (&cc.child);
-+  return rc < 0 ? -1 : 0;
-+}
-+
-+struct mx_ops mx_nntp_ops = {
-+  .open = nntp_open_mailbox,
-+  .open_append = NULL,
-+  .close = nntp_close_mailbox,
-+  .open_msg = nntp_open_message,
-+  .close_msg = nntp_close_message,
-+  .check = nntp_check_mailbox,
-+  .commit_msg = NULL,
-+  .open_new_msg = NULL,
-+  .sync = nntp_sync_mailbox,
-+};
-diff -udprP mutt-1.10.0.orig/nntp.h mutt-1.10.0/nntp.h
---- mutt-1.10.0.orig/nntp.h    1970-01-01 03:00:00.000000000 +0300
-+++ mutt-1.10.0/nntp.h 2018-06-16 17:22:30.200469636 +0300
-@@ -0,0 +1,165 @@
-+/*
-+ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
-+ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
-+ * Copyright (C) 2000-2016 Vsevolod Volkov <vvv@mutt.org.ua>
-+ *
-+ *     This program is free software; you can redistribute it and/or modify
-+ *     it under the terms of the GNU General Public License as published by
-+ *     the Free Software Foundation; either version 2 of the License, or
-+ *     (at your option) any later version.
-+ *
-+ *     This program is distributed in the hope that it will be useful,
-+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *     GNU General Public License for more details.
-+ *
-+ *     You should have received a copy of the GNU General Public License
-+ *     along with this program; if not, write to the Free Software
-+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#ifndef _NNTP_H_
-+#define _NNTP_H_ 1
-+
-+#include "mutt_socket.h"
-+#include "mailbox.h"
-+#include "bcache.h"
-+
-+#if USE_HCACHE
-+#include "hcache.h"
-+#endif
-+
-+#include <time.h>
-+#include <sys/types.h>
-+#include <stdint.h>
-+
-+#define NNTP_PORT 119
-+#define NNTP_SSL_PORT 563
-+
-+/* number of entries in article cache */
-+#define NNTP_ACACHE_LEN 10
-+
-+/* article number type and format */
-+#define anum_t uint32_t
-+#define ANUM "%u"
-+
-+enum
-+{
-+  NNTP_NONE = 0,
-+  NNTP_OK,
-+  NNTP_BYE
-+};
-+
-+typedef struct
-+{
-+  unsigned int hasCAPABILITIES : 1;
-+  unsigned int hasSTARTTLS : 1;
-+  unsigned int hasDATE : 1;
-+  unsigned int hasLIST_NEWSGROUPS : 1;
-+  unsigned int hasXGTITLE : 1;
-+  unsigned int hasLISTGROUP : 1;
-+  unsigned int hasLISTGROUPrange : 1;
-+  unsigned int hasOVER : 1;
-+  unsigned int hasXOVER : 1;
-+  unsigned int use_tls : 3;
-+  unsigned int status : 3;
-+  unsigned int cacheable : 1;
-+  unsigned int newsrc_modified : 1;
-+  FILE *newsrc_fp;
-+  char *newsrc_file;
-+  char *authenticators;
-+  char *overview_fmt;
-+  off_t size;
-+  time_t mtime;
-+  time_t newgroups_time;
-+  time_t check_time;
-+  unsigned int groups_num;
-+  unsigned int groups_max;
-+  void **groups_list;
-+  HASH *groups_hash;
-+  CONNECTION *conn;
-+} NNTP_SERVER;
-+
-+typedef struct
-+{
-+  anum_t first;
-+  anum_t last;
-+} NEWSRC_ENTRY;
-+
-+typedef struct
-+{
-+  unsigned int index;
-+  char *path;
-+} NNTP_ACACHE;
-+
-+typedef struct
-+{
-+  char *group;
-+  char *desc;
-+  anum_t firstMessage;
-+  anum_t lastMessage;
-+  anum_t lastLoaded;
-+  anum_t lastCached;
-+  anum_t unread;
-+  unsigned int subscribed : 1;
-+  unsigned int new : 1;
-+  unsigned int allowed : 1;
-+  unsigned int deleted : 1;
-+  unsigned int newsrc_len;
-+  NEWSRC_ENTRY *newsrc_ent;
-+  NNTP_SERVER *nserv;
-+  NNTP_ACACHE acache[NNTP_ACACHE_LEN];
-+  body_cache_t *bcache;
-+} NNTP_DATA;
-+
-+typedef struct
-+{
-+  anum_t article_num;
-+  unsigned int parsed : 1;
-+} NNTP_HEADER_DATA;
-+
-+#define NHDR(hdr) ((NNTP_HEADER_DATA*)((hdr)->data))
-+
-+/* internal functions */
-+int nntp_add_group (char *, void *);
-+int nntp_active_save_cache (NNTP_SERVER *);
-+int nntp_check_new_groups (NNTP_SERVER *);
-+int nntp_open_connection (NNTP_SERVER *);
-+void nntp_newsrc_gen_entries (CONTEXT *);
-+void nntp_bcache_update (NNTP_DATA *);
-+void nntp_article_status (CONTEXT *, HEADER *, char *, anum_t);
-+void nntp_group_unread_stat (NNTP_DATA *);
-+void nntp_data_free (void *);
-+void nntp_acache_free (NNTP_DATA *);
-+void nntp_delete_group_cache (NNTP_DATA *);
-+
-+/* exposed interface */
-+NNTP_SERVER *nntp_select_server (char *, int);
-+NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *, char *);
-+NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *, char *);
-+NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *, char *);
-+NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *, char *);
-+int nntp_active_fetch (NNTP_SERVER *, unsigned int);
-+int nntp_newsrc_update (NNTP_SERVER *);
-+int nntp_post (const char *);
-+int nntp_check_msgid (CONTEXT *, const char *);
-+int nntp_check_children (CONTEXT *, const char *);
-+int nntp_newsrc_parse (NNTP_SERVER *);
-+void nntp_newsrc_close (NNTP_SERVER *);
-+void nntp_buffy (char *, size_t);
-+void nntp_expand_path (char *, size_t, ACCOUNT *);
-+void nntp_clear_cache (NNTP_SERVER *);
-+const char *nntp_format_str (char *, size_t, size_t, int, char, const char *,
-+                           const char *, const char *, const char *,
-+                           unsigned long, format_flag);
-+
-+NNTP_SERVER *CurrentNewsSrv INITVAL (NULL);
-+
-+#ifdef USE_HCACHE
-+header_cache_t *nntp_hcache_open (NNTP_DATA *);
-+void nntp_hcache_update (NNTP_DATA *, header_cache_t *);
-+#endif
-+
-+extern struct mx_ops mx_nntp_ops;
-+
-+#endif /* _NNTP_H_ */
-diff -udprP mutt-1.10.0.orig/pager.c mutt-1.10.0/pager.c
---- mutt-1.10.0.orig/pager.c   2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/pager.c        2018-06-16 17:22:30.201469620 +0300
-@@ -1114,6 +1114,11 @@ fill_buffer (FILE *f, LOFF_T *last_pos,
-   return b_read;
- }
-+#ifdef USE_NNTP
-+#include "mx.h"
-+#include "nntp.h"
-+#endif
-+
- static int format_line (struct line_t **lineInfo, int n, unsigned char *buf,
-                       int flags, ansi_attr *pa, int cnt,
-@@ -1584,6 +1589,15 @@ static const struct mapping_t PagerHelpE
-   { N_("Next"),       OP_MAIN_NEXT_UNDELETED },
-   { NULL,     0 }
- };
-+#ifdef USE_NNTP
-+static struct mapping_t PagerNewsHelpExtra[] = {
-+  { N_("Post"),     OP_POST },
-+  { N_("Followup"), OP_FOLLOWUP },
-+  { N_("Del"),      OP_DELETE },
-+  { N_("Next"),     OP_MAIN_NEXT_UNDELETED },
-+  { NULL,           0 }
-+};
-+#endif
- void mutt_clear_pager_position (void)
- {
-@@ -1923,6 +1937,10 @@ mutt_pager (const char *banner, const ch
-   pager_redraw_data_t rd;
-+#ifdef USE_NNTP
-+  char *followup_to;
-+#endif
-+
-   if (!(flags & MUTT_SHOWCOLOR))
-     flags |= MUTT_SHOWFLAT;
-@@ -1972,7 +1990,11 @@ mutt_pager (const char *banner, const ch
-   if (IsHeader (extra))
-   {
-     strfcpy (tmphelp, helpstr, sizeof (tmphelp));
--    mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER, PagerHelpExtra);
-+    mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER,
-+#ifdef USE_NNTP
-+      (Context && (Context->magic == MUTT_NNTP)) ? PagerNewsHelpExtra :
-+#endif
-+      PagerHelpExtra);
-     snprintf (helpstr, sizeof (helpstr), "%s %s", tmphelp, buffer);
-   }
-   if (!InHelp)
-@@ -2639,6 +2661,60 @@ search_next:
-       pager_menu->redraw = REDRAW_FULL;
-       break;
-+#ifdef USE_NNTP
-+      case OP_POST:
-+      CHECK_MODE(IsHeader (extra) && !IsAttach (extra));
-+      CHECK_ATTACH;
-+      if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
-+          !((NNTP_DATA *)extra->ctx->data)->allowed &&
-+          query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
-+        break;
-+      ci_send_message (SENDNEWS, NULL, NULL, extra->ctx, NULL);
-+      pager_menu->redraw = REDRAW_FULL;
-+      break;
-+
-+      case OP_FORWARD_TO_GROUP:
-+      CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
-+      CHECK_ATTACH;
-+      if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
-+          !((NNTP_DATA *)extra->ctx->data)->allowed &&
-+          query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
-+        break;
-+      if (IsMsgAttach (extra))
-+        mutt_attach_forward (extra->fp, extra->hdr, extra->actx,
-+                             extra->bdy, SENDNEWS);
-+      else
-+        ci_send_message (SENDNEWS|SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
-+      pager_menu->redraw = REDRAW_FULL;
-+      break;
-+
-+      case OP_FOLLOWUP:
-+      CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
-+      CHECK_ATTACH;
-+
-+      if (IsMsgAttach (extra))
-+        followup_to = extra->bdy->hdr->env->followup_to;
-+      else
-+        followup_to = extra->hdr->env->followup_to;
-+
-+      if (!followup_to || mutt_strcasecmp (followup_to, "poster") ||
-+          query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != MUTT_YES)
-+      {
-+        if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
-+            !((NNTP_DATA *)extra->ctx->data)->allowed &&
-+            query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
-+          break;
-+        if (IsMsgAttach (extra))
-+          mutt_attach_reply (extra->fp, extra->hdr, extra->actx,
-+                             extra->bdy, SENDNEWS|SENDREPLY);
-+        else
-+          ci_send_message (SENDNEWS|SENDREPLY, NULL, NULL,
-+                           extra->ctx, extra->hdr);
-+        pager_menu->redraw = REDRAW_FULL;
-+        break;
-+      }
-+#endif
-+
-       case OP_REPLY:
-       CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
-         CHECK_ATTACH;      
-@@ -2684,7 +2760,7 @@ search_next:
-         CHECK_ATTACH;
-         if (IsMsgAttach (extra))
-         mutt_attach_forward (extra->fp, extra->hdr, extra->actx,
--                             extra->bdy);
-+                             extra->bdy, 0);
-         else
-         ci_send_message (SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
-       pager_menu->redraw = REDRAW_FULL;
-diff -udprP mutt-1.10.0.orig/parse.c mutt-1.10.0/parse.c
---- mutt-1.10.0.orig/parse.c   2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/parse.c        2018-06-16 17:22:30.201469620 +0300
-@@ -94,7 +94,7 @@ char *mutt_read_rfc822_line (FILE *f, ch
-   /* not reached */
- }
--static LIST *mutt_parse_references (char *s, int in_reply_to)
-+LIST *mutt_parse_references (char *s, int in_reply_to)
- {
-   LIST *t, *lst = NULL;
-   char *m;
-@@ -1077,6 +1077,17 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
-       e->from = rfc822_parse_adrlist (e->from, p);
-       matched = 1;
-     }
-+#ifdef USE_NNTP
-+    else if (!mutt_strcasecmp (line+1, "ollowup-to"))
-+    {
-+      if (!e->followup_to)
-+      {
-+      mutt_remove_trailing_ws (p);
-+      e->followup_to = safe_strdup (mutt_skip_whitespace (p));
-+      }
-+      matched = 1;
-+    }
-+#endif
-     break;
-     
-     case 'i':
-@@ -1159,6 +1170,27 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
-     }
-     break;
-     
-+#ifdef USE_NNTP
-+    case 'n':
-+    if (!mutt_strcasecmp (line + 1, "ewsgroups"))
-+    {
-+      FREE (&e->newsgroups);
-+      mutt_remove_trailing_ws (p);
-+      e->newsgroups = safe_strdup (mutt_skip_whitespace (p));
-+      matched = 1;
-+    }
-+    break;
-+#endif
-+
-+    case 'o':
-+    /* field `Organization:' saves only for pager! */
-+    if (!mutt_strcasecmp (line + 1, "rganization"))
-+    {
-+      if (!e->organization && mutt_strcasecmp (p, "unknown"))
-+      e->organization = safe_strdup (p);
-+    }
-+    break;
-+
-     case 'r':
-     if (!ascii_strcasecmp (line + 1, "eferences"))
-     {
-@@ -1271,6 +1303,20 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
-       e->x_label = safe_strdup(p);
-       matched = 1;
-     }
-+#ifdef USE_NNTP
-+    else if (!mutt_strcasecmp (line + 1, "-comment-to"))
-+    {
-+      if (!e->x_comment_to)
-+      e->x_comment_to = safe_strdup (p);
-+      matched = 1;
-+    }
-+    else if (!mutt_strcasecmp (line + 1, "ref"))
-+    {
-+      if (!e->xref)
-+      e->xref = safe_strdup (p);
-+      matched = 1;
-+    }
-+#endif
-     
-     default:
-     break;
-diff -udprP mutt-1.10.0.orig/pattern.c mutt-1.10.0/pattern.c
---- mutt-1.10.0.orig/pattern.c 2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/pattern.c      2018-06-16 17:22:30.201469620 +0300
-@@ -94,6 +94,9 @@ Flags[] =
-   { 'U', MUTT_UNREAD,         0,              NULL },
-   { 'v', MUTT_COLLAPSED,              0,              NULL },
-   { 'V', MUTT_CRYPT_VERIFIED, 0,              NULL },
-+#ifdef USE_NNTP
-+  { 'w', MUTT_NEWSGROUPS,     0,              eat_regexp },
-+#endif
-   { 'x', MUTT_REFERENCE,              0,              eat_regexp },
-   { 'X', MUTT_MIMEATTACH,             0,              eat_range },
-   { 'y', MUTT_XLABEL,         0,              eat_regexp },
-@@ -1368,6 +1371,10 @@ mutt_pattern_exec (struct pattern_t *pat
-       return (pat->not ^ match_mime_content_type (pat, ctx, h));
-     case MUTT_UNREFERENCED:
-       return (pat->not ^ (h->thread && !h->thread->child));
-+#ifdef USE_NNTP
-+    case MUTT_NEWSGROUPS:
-+      return (pat->not ^ (h->env->newsgroups && patmatch (pat, h->env->newsgroups) == 0));
-+#endif
-   }
-   mutt_error (_("error: unknown op %d (report this error)."), pat->op);
-   return (-1);
-@@ -1449,6 +1456,7 @@ int mutt_pattern_func (int op, char *pro
-   progress_t progress;
-   strfcpy (buf, NONULL (Context->pattern), sizeof (buf));
-+  if (prompt || op != MUTT_LIMIT)
-   if (mutt_get_field (prompt, buf, sizeof (buf), MUTT_PATTERN | MUTT_CLEAR) != 0 || !buf[0])
-     return (-1);
-diff -udprP mutt-1.10.0.orig/po/POTFILES.in mutt-1.10.0/po/POTFILES.in
---- mutt-1.10.0.orig/po/POTFILES.in    2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/po/POTFILES.in 2018-06-16 17:22:30.201469620 +0300
-@@ -47,6 +47,8 @@ mutt_ssl_gnutls.c
- mutt_tunnel.c
- muttlib.c
- mx.c
-+newsrc.c
-+nntp.c
- pager.c
- parse.c
- pattern.c
-diff -udprP mutt-1.10.0.orig/postpone.c mutt-1.10.0/postpone.c
---- mutt-1.10.0.orig/postpone.c        2018-04-17 02:31:03.000000000 +0300
-+++ mutt-1.10.0/postpone.c     2018-06-16 17:22:30.201469620 +0300
-@@ -125,15 +125,26 @@ int mutt_num_postponed (int force)
-   if (LastModify < st.st_mtime)
-   {
-+#ifdef USE_NNTP
-+    int optnews = option (OPTNEWS);
-+#endif
-     LastModify = st.st_mtime;
-     if (access (Postponed, R_OK | F_OK) != 0)
-       return (PostCount = 0);
-+#ifdef USE_NNTP
-+    if (optnews)
-+      unset_option (OPTNEWS);
-+#endif
-     if (mx_open_mailbox (Postponed, MUTT_NOSORT | MUTT_QUIET, &ctx) == NULL)
-       PostCount = 0;
-     else
-       PostCount = ctx.msgcount;
-     mx_fastclose_mailbox (&ctx);
-+#ifdef USE_NNTP
-+    if (optnews)
-+      set_option (OPTNEWS);
-+#endif
-   }
-   return (PostCount);
-diff -udprP mutt-1.10.0.orig/protos.h mutt-1.10.0/protos.h
---- mutt-1.10.0.orig/protos.h  2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/protos.h       2018-06-16 17:22:30.202469604 +0300
-@@ -111,6 +111,7 @@ HASH *mutt_make_id_hash (CONTEXT *);
- HASH *mutt_make_subj_hash (CONTEXT *);
- LIST *mutt_make_references(ENVELOPE *e);
-+LIST *mutt_parse_references (char *, int);
- char *mutt_read_rfc822_line (FILE *, char *, size_t *);
- ENVELOPE *mutt_read_rfc822_header (FILE *, HEADER *, short, short);
-diff -udprP mutt-1.10.0.orig/recvattach.c mutt-1.10.0/recvattach.c
---- mutt-1.10.0.orig/recvattach.c      2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/recvattach.c   2018-06-16 17:22:30.202469604 +0300
-@@ -1224,6 +1224,15 @@ void mutt_view_attachments (HEADER *hdr)
-       }
- #endif
-+#ifdef USE_NNTP
-+      if (Context->magic == MUTT_NNTP)
-+      {
-+        mutt_flushinp ();
-+        mutt_error _("Can't delete attachment from news server.");
-+        break;
-+      }
-+#endif
-+
-         if (WithCrypto && (hdr->security & ENCRYPT))
-         {
-           mutt_message _(
-@@ -1318,10 +1327,32 @@ void mutt_view_attachments (HEADER *hdr)
-       case OP_FORWARD_MESSAGE:
-         CHECK_ATTACH;
-         mutt_attach_forward (CURATTACH->fp, hdr, actx,
--                           menu->tagprefix ? NULL : CURATTACH->content);
-+                           menu->tagprefix ? NULL : CURATTACH->content, 0);
-         menu->redraw = REDRAW_FULL;
-         break;
-+#ifdef USE_NNTP
-+      case OP_FORWARD_TO_GROUP:
-+      CHECK_ATTACH;
-+      mutt_attach_forward (CURATTACH->fp, hdr, actx,
-+                           menu->tagprefix ? NULL : CURATTACH->content, SENDNEWS);
-+      menu->redraw = REDRAW_FULL;
-+      break;
-+
-+      case OP_FOLLOWUP:
-+      CHECK_ATTACH;
-+
-+      if (!CURATTACH->content->hdr->env->followup_to ||
-+          mutt_strcasecmp (CURATTACH->content->hdr->env->followup_to, "poster") ||
-+          query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != MUTT_YES)
-+      {
-+        mutt_attach_reply (CURATTACH->fp, hdr, actx,
-+                           menu->tagprefix ? NULL : CURATTACH->content, SENDNEWS|SENDREPLY);
-+        menu->redraw = REDRAW_FULL;
-+        break;
-+      }
-+#endif
-+
-       case OP_COMPOSE_TO_SENDER:
-         CHECK_ATTACH;
-         mutt_attach_mail_sender (CURATTACH->fp, hdr, actx,
-diff -udprP mutt-1.10.0.orig/recvcmd.c mutt-1.10.0/recvcmd.c
---- mutt-1.10.0.orig/recvcmd.c 2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/recvcmd.c      2018-06-16 17:22:30.202469604 +0300
-@@ -398,7 +398,7 @@ static BODY ** copy_problematic_attachme
- static void attach_forward_bodies (FILE * fp, HEADER * hdr,
-                                  ATTACH_CONTEXT *actx,
-                                  BODY * cur,
--                                 short nattach)
-+                                 short nattach, int flags)
- {
-   short i;
-   short mime_fwd_all = 0;
-@@ -554,7 +554,7 @@ _("Can't decode all tagged attachments.
-   tmpfp = NULL;
-   /* now that we have the template, send it. */
--  ci_send_message (0, tmphdr, tmpbody, NULL, parent_hdr);
-+  ci_send_message (flags, tmphdr, tmpbody, NULL, parent_hdr);
-   return;
-   
-   bail:
-@@ -581,7 +581,7 @@ _("Can't decode all tagged attachments.
-  */
- static void attach_forward_msgs (FILE * fp, HEADER * hdr, 
--             ATTACH_CONTEXT *actx, BODY * cur)
-+             ATTACH_CONTEXT *actx, BODY * cur, int flags)
- {
-   HEADER *curhdr = NULL;
-   HEADER *tmphdr;
-@@ -686,23 +686,23 @@ static void attach_forward_msgs (FILE *
-   else
-     mutt_free_header (&tmphdr);
--  ci_send_message (0, tmphdr, *tmpbody ? tmpbody : NULL, 
-+  ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, 
-                  NULL, curhdr);
- }
- void mutt_attach_forward (FILE * fp, HEADER * hdr, 
--                        ATTACH_CONTEXT *actx, BODY * cur)
-+                        ATTACH_CONTEXT *actx, BODY * cur, int flags)
- {
-   short nattach;
-   
-   if (check_all_msg (actx, cur, 0) == 0)
--    attach_forward_msgs (fp, hdr, actx, cur);
-+    attach_forward_msgs (fp, hdr, actx, cur, flags);
-   else
-   {
-     nattach = count_tagged (actx);
--    attach_forward_bodies (fp, hdr, actx, cur, nattach);
-+    attach_forward_bodies (fp, hdr, actx, cur, nattach, flags);
-   }
- }
-@@ -760,6 +760,17 @@ attach_reply_envelope_defaults (ENVELOPE
-     return -1;
-   }
-+#ifdef USE_NNTP
-+  if ((flags & SENDNEWS))
-+  {
-+    /* in case followup set Newsgroups: with Followup-To: if it present */
-+    if (!env->newsgroups && curenv &&
-+      mutt_strcasecmp (curenv->followup_to, "poster"))
-+      env->newsgroups = safe_strdup (curenv->followup_to);
-+  }
-+  else
-+#endif
-+ {
-   if (parent)
-   {
-     if (mutt_fetch_recips (env, curenv, flags) == -1)
-@@ -782,6 +793,7 @@ attach_reply_envelope_defaults (ENVELOPE
-   }
-   
-   mutt_fix_reply_recipients (env);
-+ }
-   mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
-   if (parent)
-@@ -844,6 +856,13 @@ void mutt_attach_reply (FILE * fp, HEADE
-   char prefix[SHORT_STRING];
-   int rc;
-   
-+#ifdef USE_NNTP
-+  if (flags & SENDNEWS)
-+    set_option (OPTNEWSSEND);
-+  else
-+    unset_option (OPTNEWSSEND);
-+#endif
-+
-   if (check_all_msg (actx, cur, 0) == -1)
-   {
-     nattach = count_tagged (actx);
-diff -udprP mutt-1.10.0.orig/send.c mutt-1.10.0/send.c
---- mutt-1.10.0.orig/send.c    2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/send.c 2018-06-16 17:22:30.202469604 +0300
-@@ -45,6 +45,11 @@
- #include <sys/types.h>
- #include <utime.h>
-+#ifdef USE_NNTP
-+#include "nntp.h"
-+#include "mx.h"
-+#endif
-+
- #ifdef MIXMASTER
- #include "remailer.h"
- #endif
-@@ -214,17 +219,51 @@ static int edit_address (ADDRESS **a, /*
-   return 0;
- }
--static int edit_envelope (ENVELOPE *en)
-+static int edit_envelope (ENVELOPE *en, int flags)
- {
-   char buf[HUGE_STRING];
-   LIST *uh = UserHeader;
-+#ifdef USE_NNTP
-+  if (option (OPTNEWSSEND))
-+  {
-+    if (en->newsgroups)
-+      strfcpy (buf, en->newsgroups, sizeof (buf));
-+    else
-+      buf[0] = 0;
-+    if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) != 0)
-+      return (-1);
-+    FREE (&en->newsgroups);
-+    en->newsgroups = safe_strdup (buf);
-+
-+    if (en->followup_to)
-+      strfcpy (buf, en->followup_to, sizeof (buf));
-+    else
-+      buf[0] = 0;
-+    if (option (OPTASKFOLLOWUP) && mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) != 0)
-+      return (-1);
-+    FREE (&en->followup_to);
-+    en->followup_to = safe_strdup (buf);
-+
-+    if (en->x_comment_to)
-+      strfcpy (buf, en->x_comment_to, sizeof (buf));
-+    else
-+      buf[0] = 0;
-+    if (option (OPTXCOMMENTTO) && option (OPTASKXCOMMENTTO) && mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) != 0)
-+      return (-1);
-+    FREE (&en->x_comment_to);
-+    en->x_comment_to = safe_strdup (buf);
-+  }
-+  else
-+#endif
-+ {
-   if (edit_address (&en->to, _("To: ")) == -1 || en->to == NULL)
-     return (-1);
-   if (option (OPTASKCC) && edit_address (&en->cc, _("Cc: ")) == -1)
-     return (-1);
-   if (option (OPTASKBCC) && edit_address (&en->bcc, _("Bcc: ")) == -1)
-     return (-1);
-+ }
-   if (en->subject)
-   {
-@@ -259,6 +298,14 @@ static int edit_envelope (ENVELOPE *en)
-   return 0;
- }
-+#ifdef USE_NNTP
-+char *nntp_get_header (const char *s)
-+{
-+  SKIPWS (s);
-+  return safe_strdup (s);
-+}
-+#endif
-+
- static void process_user_recips (ENVELOPE *env)
- {
-   LIST *uh = UserHeader;
-@@ -271,6 +318,14 @@ static void process_user_recips (ENVELOP
-       env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3);
-     else if (ascii_strncasecmp ("bcc:", uh->data, 4) == 0)
-       env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4);
-+#ifdef USE_NNTP
-+    else if (ascii_strncasecmp ("newsgroups:", uh->data, 11) == 0)
-+      env->newsgroups = nntp_get_header (uh->data + 11);
-+    else if (ascii_strncasecmp ("followup-to:", uh->data, 12) == 0)
-+      env->followup_to = nntp_get_header (uh->data + 12);
-+    else if (ascii_strncasecmp ("x-comment-to:", uh->data, 13) == 0)
-+      env->x_comment_to = nntp_get_header (uh->data + 13);
-+#endif
-   }
- }
-@@ -309,6 +364,12 @@ static void process_user_header (ENVELOP
-     else if (ascii_strncasecmp ("to:", uh->data, 3) != 0 &&
-            ascii_strncasecmp ("cc:", uh->data, 3) != 0 &&
-            ascii_strncasecmp ("bcc:", uh->data, 4) != 0 &&
-+#ifdef USE_NNTP
-+           ascii_strncasecmp ("newsgroups:", uh->data, 11) != 0 &&
-+           ascii_strncasecmp ("followup-to:", uh->data, 12) != 0 &&
-+           ascii_strncasecmp ("x-comment-to:", uh->data, 13) != 0 &&
-+#endif
-+           ascii_strncasecmp ("supersedes:", uh->data, 11) != 0 &&
-            ascii_strncasecmp ("subject:", uh->data, 8) != 0 &&
-            ascii_strncasecmp ("return-path:", uh->data, 12) != 0)
-     {
-@@ -656,6 +717,10 @@ void mutt_add_to_reference_headers (ENVE
-   if (pp) *pp = p;
-   if (qq) *qq = q;
-   
-+#ifdef USE_NNTP
-+  if (option (OPTNEWSSEND) && option (OPTXCOMMENTTO) && curenv->from)
-+    env->x_comment_to = safe_strdup (mutt_get_name (curenv->from));
-+#endif
- }
- static void 
-@@ -718,6 +783,16 @@ envelope_defaults (ENVELOPE *env, CONTEX
-   if (flags & SENDREPLY)
-   {
-+#ifdef USE_NNTP
-+    if ((flags & SENDNEWS))
-+    {
-+      /* in case followup set Newsgroups: with Followup-To: if it present */
-+      if (!env->newsgroups && curenv &&
-+        mutt_strcasecmp (curenv->followup_to, "poster"))
-+      env->newsgroups = safe_strdup (curenv->followup_to);
-+    }
-+    else
-+#endif
-     if (tag)
-     {
-       HEADER *h;
-@@ -864,7 +939,18 @@ void mutt_set_followup_to (ENVELOPE *e)
-    * it hasn't already been set
-    */
--  if (option (OPTFOLLOWUPTO) && !e->mail_followup_to)
-+  if (!option (OPTFOLLOWUPTO))
-+    return;
-+#ifdef USE_NNTP
-+  if (option (OPTNEWSSEND))
-+  {
-+    if (!e->followup_to && e->newsgroups && (strrchr (e->newsgroups, ',')))
-+      e->followup_to = safe_strdup (e->newsgroups);
-+    return;
-+  }
-+#endif
-+
-+  if (!e->mail_followup_to)
-   {
-     if (mutt_is_list_cc (0, e->to, e->cc))
-     {
-@@ -1026,6 +1112,9 @@ static int send_message (HEADER *msg)
- #endif
- #if USE_SMTP
-+#ifdef USE_NNTP
-+  if (!option (OPTNEWSSEND))
-+#endif
-   if (SmtpUrl)
-       return mutt_smtp_send (msg->env->from, msg->env->to, msg->env->cc,
-                              msg->env->bcc, tempfile,
-@@ -1189,6 +1278,13 @@ ci_send_message (int flags,             /* send mod
-   int rv = -1;
-   
-+#ifdef USE_NNTP
-+  if (flags & SENDNEWS)
-+    set_option (OPTNEWSSEND);
-+  else
-+    unset_option (OPTNEWSSEND);
-+#endif
-+
-   if (!flags && !msg && quadoption (OPT_RECALL) != MUTT_NO &&
-       mutt_num_postponed (1))
-   {
-@@ -1224,6 +1320,22 @@ ci_send_message (int flags,             /* send mod
-     {
-       if ((flags = mutt_get_postponed (ctx, msg, &cur, fcc, sizeof (fcc))) < 0)
-       goto cleanup;
-+#ifdef USE_NNTP
-+      /*
-+       * If postponed message is a news article, it have
-+       * a "Newsgroups:" header line, then set appropriate flag.
-+       */
-+      if (msg->env->newsgroups)
-+      {
-+      flags |= SENDNEWS;
-+      set_option (OPTNEWSSEND);
-+      }
-+      else
-+      {
-+      flags &= ~SENDNEWS;
-+      unset_option (OPTNEWSSEND);
-+      }
-+#endif
-     }
-     if (flags & (SENDPOSTPONED|SENDRESEND))
-@@ -1325,11 +1437,16 @@ ci_send_message (int flags,            /* send mod
-     if (flags & SENDREPLY)
-       mutt_fix_reply_recipients (msg->env);
-+#ifdef USE_NNTP
-+    if ((flags & SENDNEWS) && ctx && ctx->magic == MUTT_NNTP && !msg->env->newsgroups)
-+      msg->env->newsgroups = safe_strdup (((NNTP_DATA *)ctx->data)->group);
-+#endif
-+
-     if (! (flags & (SENDMAILX|SENDBATCH)) &&
-       ! (option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
-       ! ((flags & SENDREPLY) && option (OPTFASTREPLY)))
-     {
--      if (edit_envelope (msg->env) == -1)
-+      if (edit_envelope (msg->env, flags) == -1)
-       goto cleanup;
-     }
-@@ -1640,6 +1757,11 @@ main_loop:
-     if (i == -1)
-     {
-       /* abort */
-+#ifdef USE_NNTP
-+      if (flags & SENDNEWS)
-+      mutt_message _("Article not posted.");
-+      else
-+#endif
-       mutt_message _("Mail not sent.");
-       goto cleanup;
-     }
-@@ -1706,6 +1828,9 @@ main_loop:
-     }
-   }
-+#ifdef USE_NNTP
-+  if (!(flags & SENDNEWS))
-+#endif
-   if (!has_recips (msg->env->to) && !has_recips (msg->env->cc) &&
-       !has_recips (msg->env->bcc))
-   {
-@@ -1739,6 +1864,19 @@ main_loop:
-       mutt_error _("No subject specified.");
-     goto main_loop;
-   }
-+#ifdef USE_NNTP
-+  if ((flags & SENDNEWS) && !msg->env->subject)
-+  {
-+    mutt_error _("No subject specified.");
-+    goto main_loop;
-+  }
-+
-+  if ((flags & SENDNEWS) && !msg->env->newsgroups)
-+  {
-+    mutt_error _("No newsgroup specified.");
-+    goto main_loop;
-+  }
-+#endif
-   /* Scan for a mention of an attachment in the message body and
-    * prompt if there is none. */
-@@ -1964,7 +2102,12 @@ full_fcc:
-     }
-   }
-   else if (!option (OPTNOCURSES) && ! (flags & SENDMAILX))
--    mutt_message (i == 0 ? _("Mail sent.") : _("Sending in background."));
-+    mutt_message (i != 0 ? _("Sending in background.") :
-+#ifdef USE_NNTP
-+                (flags & SENDNEWS) ? _("Article posted.") : _("Mail sent."));
-+#else
-+                _("Mail sent."));
-+#endif
-   if (WithCrypto && (msg->security & ENCRYPT))
-     FREE (&pgpkeylist);
-diff -udprP mutt-1.10.0.orig/sendlib.c mutt-1.10.0/sendlib.c
---- mutt-1.10.0.orig/sendlib.c 2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/sendlib.c      2018-06-16 17:22:30.203469588 +0300
-@@ -46,6 +46,10 @@
- #include <sys/wait.h>
- #include <fcntl.h>
-+#ifdef USE_NNTP
-+#include "nntp.h"
-+#endif
-+
- #ifdef HAVE_SYSEXITS_H
- #include <sysexits.h>
- #else /* Make sure EX_OK is defined <philiph@pobox.com> */
-@@ -1576,6 +1580,14 @@ void mutt_write_references (LIST *r, FIL
- {
-   LIST **ref = NULL;
-   int refcnt = 0, refmax = 0;
-+  int multiline = 1;
-+  int space = 0;
-+
-+  if (trim < 0)
-+  {
-+    trim = -trim;
-+    multiline = 0;
-+  }
-   for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
-   {
-@@ -1586,9 +1598,11 @@ void mutt_write_references (LIST *r, FIL
-   while (refcnt-- > 0)
-   {
--    fputc (' ', f);
-+    if (multiline || space)
-+      fputc (' ', f);
-+    space = 1;
-     fputs (ref[refcnt]->data, f);
--    if (refcnt >= 1)
-+    if (multiline && refcnt >= 1)
-       fputc ('\n', f);
-   }
-@@ -2002,6 +2016,9 @@ int mutt_write_rfc822_header (FILE *fp,
-     mutt_write_address_list (env->to, fp, 4, 0);
-   }
-   else if (mode > 0)
-+#ifdef USE_NNTP
-+  if (!option (OPTNEWSSEND))
-+#endif
-     fputs ("To: \n", fp);
-   if (env->cc)
-@@ -2010,6 +2027,9 @@ int mutt_write_rfc822_header (FILE *fp,
-     mutt_write_address_list (env->cc, fp, 4, 0);
-   }
-   else if (mode > 0)
-+#ifdef USE_NNTP
-+  if (!option (OPTNEWSSEND))
-+#endif
-     fputs ("Cc: \n", fp);
-   if (env->bcc)
-@@ -2021,8 +2041,28 @@ int mutt_write_rfc822_header (FILE *fp,
-     }
-   }
-   else if (mode > 0)
-+#ifdef USE_NNTP
-+  if (!option (OPTNEWSSEND))
-+#endif
-     fputs ("Bcc: \n", fp);
-+#ifdef USE_NNTP
-+  if (env->newsgroups)
-+    fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
-+  else if (mode == 1 && option (OPTNEWSSEND))
-+    fputs ("Newsgroups: \n", fp);
-+
-+  if (env->followup_to)
-+    fprintf (fp, "Followup-To: %s\n", env->followup_to);
-+  else if (mode == 1 && option (OPTNEWSSEND))
-+    fputs ("Followup-To: \n", fp);
-+
-+  if (env->x_comment_to)
-+    fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
-+  else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
-+    fputs ("X-Comment-To: \n", fp);
-+#endif
-+
-   if (env->subject)
-     mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
-   else if (mode == 1)
-@@ -2041,6 +2081,9 @@ int mutt_write_rfc822_header (FILE *fp,
-     fputs ("Reply-To: \n", fp);
-   if (env->mail_followup_to)
-+#ifdef USE_NNTP
-+  if (!option (OPTNEWSSEND))
-+#endif
-   {
-     fputs ("Mail-Followup-To: ", fp);
-     mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
-@@ -2388,6 +2431,24 @@ mutt_invoke_sendmail (ADDRESS *from,    /*
-   size_t extra_argslen = 0, extra_argsmax = 0;
-   int i;
-+#ifdef USE_NNTP
-+  if (option (OPTNEWSSEND))
-+  {
-+    char cmd[LONG_STRING];
-+
-+    mutt_FormatString (cmd, sizeof (cmd), 0, MuttIndexWindow->cols,
-+                     NONULL (Inews), nntp_format_str, 0, 0);
-+    if (!*cmd)
-+    {
-+      i = nntp_post (msg);
-+      unlink (msg);
-+      return i;
-+    }
-+
-+    s = safe_strdup (cmd);
-+  }
-+#endif
-+
-   /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
-   if (!s)
-   {
-@@ -2437,6 +2498,10 @@ mutt_invoke_sendmail (ADDRESS *from,    /*
-     }
-   }
-+#ifdef USE_NNTP
-+  if (!option (OPTNEWSSEND))
-+  {
-+#endif
-   if (eightbit && option (OPTUSE8BITMIME))
-     args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
-@@ -2470,6 +2535,9 @@ mutt_invoke_sendmail (ADDRESS *from,     /*
-   args = add_args (args, &argslen, &argsmax, to);
-   args = add_args (args, &argslen, &argsmax, cc);
-   args = add_args (args, &argslen, &argsmax, bcc);
-+#ifdef USE_NNTP
-+  }
-+#endif
-   if (argslen == argsmax)
-     safe_realloc (&args, sizeof (char *) * (++argsmax));
-@@ -2558,6 +2626,9 @@ void mutt_prepare_envelope (ENVELOPE *en
-   rfc2047_encode_string (&env->x_label);
-   if (env->subject)
-+#ifdef USE_NNTP
-+  if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
-+#endif
-   {
-     rfc2047_encode_string (&env->subject);
-   }
-@@ -2678,6 +2749,10 @@ int mutt_bounce_message (FILE *fp, HEADE
-   }
-   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
-+#ifdef USE_NNTP
-+  unset_option (OPTNEWSSEND);
-+#endif
-+
-   /*
-    * prepare recipient list. idna conversion appears to happen before this
-    * function is called, since the user receives confirmation of the address
-diff -udprP mutt-1.10.0.orig/sort.c mutt-1.10.0/sort.c
---- mutt-1.10.0.orig/sort.c    2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/sort.c 2018-06-16 17:22:30.203469588 +0300
-@@ -24,6 +24,11 @@
- #include "sort.h"
- #include "mutt_idna.h"
-+#ifdef USE_NNTP
-+#include "mx.h"
-+#include "nntp.h"
-+#endif
-+
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
-@@ -151,6 +156,17 @@ static int compare_order (const void *a,
-   HEADER **ha = (HEADER **) a;
-   HEADER **hb = (HEADER **) b;
-+#ifdef USE_NNTP
-+  if (Context && Context->magic == MUTT_NNTP)
-+  {
-+    anum_t na = NHDR (*ha)->article_num;
-+    anum_t nb = NHDR (*hb)->article_num;
-+    int result = na == nb ? 0 : na > nb ? 1 : -1;
-+    AUXSORT (result, a, b);
-+    return (SORTCODE (result));
-+  }
-+  else
-+#endif
-   /* no need to auxsort because you will never have equality here */
-   return (SORTCODE ((*ha)->index - (*hb)->index));
- }
-diff -udprP mutt-1.10.0.orig/url.c mutt-1.10.0/url.c
---- mutt-1.10.0.orig/url.c     2017-12-18 22:31:37.000000000 +0200
-+++ mutt-1.10.0/url.c  2018-06-16 17:22:30.203469588 +0300
-@@ -40,6 +40,8 @@ static const struct mapping_t UrlMap[] =
-   { "imaps",  U_IMAPS },
-   { "pop",    U_POP },
-   { "pops",   U_POPS },
-+  { "news",   U_NNTP },
-+  { "snews",  U_NNTPS },
-   { "mailto", U_MAILTO },
-   { "smtp",     U_SMTP },
-   { "smtps",    U_SMTPS },
-@@ -224,7 +226,7 @@ int url_ciss_tostring (ciss_url_t* ciss,
-       safe_strcat (dest, len, "//");
-     len -= (l = strlen (dest)); dest += l;
--    if (ciss->user)
-+    if (ciss->user && (ciss->user[0] || !(flags & U_PATH)))
-     {
-       char u[STRING];
-       url_pct_encode (u, sizeof (u), ciss->user);
-diff -udprP mutt-1.10.0.orig/url.h mutt-1.10.0/url.h
---- mutt-1.10.0.orig/url.h     2017-12-03 05:10:17.000000000 +0200
-+++ mutt-1.10.0/url.h  2018-06-16 17:22:30.203469588 +0300
-@@ -8,6 +8,8 @@ typedef enum url_scheme
-   U_POPS,
-   U_IMAP,
-   U_IMAPS,
-+  U_NNTP,
-+  U_NNTPS,
-   U_SMTP,
-   U_SMTPS,
-   U_MAILTO,
-diff -udprP mutt-1.10.0.orig/Makefile.am mutt-1.10.0/Makefile.am
---- mutt-1.10.0.orig/Makefile.am       2018-05-15 00:51:53.000000000 +0300
-+++ mutt-1.10.0/Makefile.am    2018-06-16 17:22:30.203469588 +0300
-@@ -60,6 +60,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c
-       mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \
-       mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \
-       pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \
-+      nntp.c newsrc.c \
-       sidebar.c smime.c smtp.c utf8.c wcwidth.c \
-       bcache.h browser.h hcache.h mbyte.h mutt_idna.h remailer.h url.h
-@@ -71,6 +72,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP O
-       mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
-       mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
-       rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \
-+      nntp.h ChangeLog.nntp \
-       _mutt_regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \
-       mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \
-       README.SSL smime.h group.h \
-diff -udprP mutt-1.10.0.orig/Makefile.in mutt-1.10.0/Makefile.in
---- mutt-1.10.0.orig/Makefile.in       2018-05-15 04:08:48.000000000 +0300
-+++ mutt-1.10.0/Makefile.in    2018-06-16 17:22:30.203469588 +0300
-@@ -504,6 +504,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c
-       mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \
-       mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \
-       pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \
-+      nntp.c newsrc.c \
-       sidebar.c smime.c smtp.c utf8.c wcwidth.c \
-       bcache.h browser.h hcache.h mbyte.h mutt_idna.h remailer.h url.h
-@@ -515,6 +516,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP O
-       mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
-       mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
-       rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \
-+      nntp.h ChangeLog.nntp \
-       _mutt_regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \
-       mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \
-       README.SSL smime.h group.h \
-@@ -784,6 +786,8 @@ distclean-compile:
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mutt_tunnel.Po@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muttlib.Po@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mx.Po@am__quote@ # am--include-marker
-+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newsrc.Po@am__quote@ # am--include-marker
-+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nntp.Po@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pager.Po@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/patchlist.Po@am__quote@ # am--include-marker
-diff -udprP mutt-1.10.0.orig/configure mutt-1.10.0/configure
---- mutt-1.10.0.orig/configure 2018-05-19 20:34:20.000000000 +0300
-+++ mutt-1.10.0/configure      2018-06-16 17:22:30.205469557 +0300
-@@ -802,6 +802,7 @@ with_docdir
- with_domain
- enable_pop
- enable_imap
-+enable_nntp
- enable_smtp
- with_gss
- with_ssl
-@@ -1487,6 +1488,7 @@ Optional Features:
-                           Force use of an external dotlock program
-   --enable-pop            Enable POP3 support
-   --enable-imap           Enable IMAP support
-+  --enable-nntp           Enable NNTP support
-   --enable-smtp           include internal SMTP relay support
-   --enable-debug          Enable debugging support
-   --enable-flock          Use flock() to lock files
-@@ -9750,6 +9752,20 @@ else
- fi
-+# Check whether --enable-nntp was given.
-+if test "${enable_nntp+set}" = set; then :
-+  enableval=$enable_nntp;     if test x$enableval = xyes ; then
-+
-+$as_echo "#define USE_NNTP 1" >>confdefs.h
-+
-+              MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS nntp.o newsrc.o"
-+              need_nntp="yes"
-+              need_socket="yes"
-+      fi
-+
-+fi
-+
-+
- # Check whether --enable-smtp was given.
- if test "${enable_smtp+set}" = set; then :
-   enableval=$enable_smtp; if test $enableval = yes; then
-@@ -9762,7 +9778,7 @@ $as_echo "#define USE_SMTP 1" >>confdefs
- fi
--if test x"$need_imap" = xyes -o x"$need_pop" = xyes ; then
-+if test x"$need_imap" = xyes -o x"$need_pop" = xyes -o x"$need_nntp" = xyes ; then
-   MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS bcache.o"
- fi
-diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
---- mutt-1.10.0.orig/doc/Muttrc        2018-05-19 20:47:52.000000000 +0300
-+++ mutt-1.10.0/doc/Muttrc     2018-06-16 17:22:30.206469541 +0300
+       case OP_GROUP_REPLY:
+       case OP_GROUP_CHAT_REPLY:
+diff -udprP mutt-1.12.1.orig/doc/Muttrc mutt-1.12.1/doc/Muttrc
+--- mutt-1.12.1.orig/doc/Muttrc        2019-06-15 19:07:32.000000000 +0300
++++ mutt-1.12.1/doc/Muttrc     2019-08-11 20:31:26.137940627 +0300
 @@ -275,6 +275,28 @@ attachments   -I message/external-body
  # editing the body of an outgoing message.
  # 
@@ -8338,7 +2412,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set assumed_charset=""
  #
  # Name: assumed_charset
-@@ -516,6 +538,17 @@ attachments   -I message/external-body
+@@ -530,6 +552,17 @@ attachments   -I message/external-body
  # desirable to unset this variable.
  # 
  # 
@@ -8356,7 +2430,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set certificate_file="~/.mutt_certificates"
  #
  # Name: certificate_file
-@@ -1294,6 +1327,19 @@ attachments   -I message/external-body
+@@ -1410,6 +1443,19 @@ attachments   -I message/external-body
  # of the same email for you.
  # 
  # 
@@ -8376,7 +2450,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set force_name=no
  #
  # Name: force_name
-@@ -1430,6 +1476,27 @@ attachments   -I message/external-body
+@@ -1560,6 +1606,27 @@ attachments   -I message/external-body
  # ``Franklin'' to ``Franklin, Steve''.
  # 
  # 
@@ -8404,7 +2478,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set hdrs=yes
  #
  # Name: hdrs
-@@ -2046,6 +2113,7 @@ attachments   -I message/external-body
+@@ -2252,6 +2319,7 @@ attachments   -I message/external-body
  # %E      number of messages in current thread
  # %f      sender (address + real name), either From: or Return-Path:
  # %F      author name, or recipient name if the message is from you
@@ -8412,7 +2486,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # %H      spam attribute(s) of this message
  # %i      message-id of the current message
  # %l      number of lines in the message (does not work with maildir,
-@@ -2069,6 +2137,8 @@ attachments   -I message/external-body
+@@ -2275,6 +2343,8 @@ attachments   -I message/external-body
  # %T      the appropriate character from the $to_chars string
  # %u      user (login) name of the author
  # %v      first name of the author, or the recipient if the message is from you
@@ -8421,8 +2495,8 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # %X      number of attachments
  #         (please see the ``attachments'' section for possible speed effects)
  # %y      ``X-Label:'' field, if present
-@@ -2108,6 +2178,27 @@ attachments   -I message/external-body
- # ``save-hook'', ``fcc-hook'' and ``fcc-save-hook'', too.
+@@ -2322,6 +2392,27 @@ attachments   -I message/external-body
+ # and $post_indent_string.
  # 
  # 
 +# set inews=""
@@ -8449,7 +2523,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set ispell="/usr/bin/ispell"
  #
  # Name: ispell
-@@ -2555,6 +2646,18 @@ attachments   -I message/external-body
+@@ -2774,6 +2865,18 @@ attachments   -I message/external-body
  # mime.types lookup.
  # 
  # 
@@ -8468,7 +2542,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set mix_entry_format="%4n %c %-16s %a"
  #
  # Name: mix_entry_format
-@@ -2633,6 +2736,144 @@ attachments   -I message/external-body
+@@ -2852,6 +2955,144 @@ attachments   -I message/external-body
  # into this command.
  # 
  # 
@@ -8613,8 +2687,8 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set pager="builtin"
  #
  # Name: pager
-@@ -3411,6 +3652,19 @@ attachments   -I message/external-body
- # string after the inclusion of a message which is being replied to.
+@@ -3686,6 +3927,19 @@ attachments   -I message/external-body
+ # the section on $index_format.
  # 
  # 
 +# set post_moderated=ask-yes
@@ -8633,7 +2707,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set postpone=ask-yes
  #
  # Name: postpone
-@@ -4285,6 +4539,41 @@ attachments   -I message/external-body
+@@ -4567,6 +4821,41 @@ attachments   -I message/external-body
  # Chinese characters.
  # 
  # 
@@ -8675,7 +2749,7 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
  # set sig_dashes=yes
  #
  # Name: sig_dashes
-@@ -5671,3 +5960,14 @@ attachments   -I message/external-body
+@@ -5971,3 +6260,14 @@ attachments   -I message/external-body
  # ``tuning'' section of the manual for performance considerations.
  # 
  # 
@@ -8690,3 +2764,5997 @@ diff -udprP mutt-1.10.0.orig/doc/Muttrc mutt-1.10.0/doc/Muttrc
 +# name of original article author) to article that followuped to newsgroup.
 +# 
 +# 
+diff -udprP mutt-1.12.1.orig/doc/manual.xml.head mutt-1.12.1/doc/manual.xml.head
+--- mutt-1.12.1.orig/doc/manual.xml.head       2019-06-14 04:29:29.000000000 +0300
++++ mutt-1.12.1/doc/manual.xml.head    2019-08-11 20:31:26.138940612 +0300
+@@ -1948,6 +1948,26 @@ to update status flags for your certific
+ </sect1>
++<sect1 id="nntp">
++<title>Reading news via NNTP</title>
++
++<para>
++If compiled with <emphasis>--enable-nntp</emphasis> option, Mutt can
++read news from news server via NNTP.  You can open a newsgroup with
++function ``change-newsgroup'' (default: ``i'').  Default news server
++can be obtained from <literal>$NNTPSERVER</literal> environment
++variable or from <literal>/etc/nntpserver</literal> file.  Like other
++news readers, info about subscribed newsgroups is saved in file by
++<link linkend="newsrc">$newsrc</link> variable.  The variable <link
++linkend="news-cache-dir">$news_cache_dir</link> can be used to point
++to a directory.  Mutt will create a hierarchy of subdirectories named
++like the account and newsgroup the cache is for.  Also the hierarchy
++is used to store header cache if Mutt was compiled with <link
++linkend="header-caching">header cache</link> support.
++</para>
++
++</sect1>
++
+ </chapter>
+ <chapter id="configuration">
+diff -udprP mutt-1.12.1.orig/doc/mutt.man mutt-1.12.1/doc/mutt.man
+--- mutt-1.12.1.orig/doc/mutt.man      2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/doc/mutt.man   2019-08-11 20:31:26.138940612 +0300
+@@ -23,8 +23,8 @@ mutt \- The Mutt Mail User Agent
+ .SH SYNOPSIS
+ .PP
+ .B mutt
+-[\-nRyzZ]
+-[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-m \fItype\fP] [\-f \fIfile\fP]
++[\-GnRyzZ]
++[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-g \fIserver\fP] [\-m \fItype\fP] [\-f \fIfile\fP]
+ .PP
+ .B mutt 
+ [\-Enx] 
+@@ -104,6 +104,10 @@ files.
+ Specify which mailbox to load.
+ .IP "-F \fImuttrc\fP"
+ Specify an initialization file to read instead of ~/.muttrc
++.IP "-g \fIserver\fP"
++Start Mutt with a listing of subscribed newsgroups at specified news server.
++.IP "-G"
++Start Mutt with a listing of subscribed newsgroups.
+ .IP "-h"
+ Display help.
+ .IP "-H \fIdraft\fP"
+diff -udprP mutt-1.12.1.orig/functions.h mutt-1.12.1/functions.h
+--- mutt-1.12.1.orig/functions.h       2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/functions.h    2019-08-11 20:31:26.138940612 +0300
+@@ -90,6 +90,10 @@ const struct binding_t OpMain[] = { /* m
+   { "break-thread",           OP_MAIN_BREAK_THREAD,           "#" },
+   { "change-folder",          OP_MAIN_CHANGE_FOLDER,          "c" },
+   { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
++#ifdef USE_NNTP
++  { "change-newsgroup",               OP_MAIN_CHANGE_GROUP,           "i" },
++  { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY,        "\033i" },
++#endif
+   { "next-unread-mailbox",    OP_MAIN_NEXT_UNREAD_MAILBOX,    NULL },
+   { "collapse-thread",                OP_MAIN_COLLAPSE_THREAD,        "\033v" },
+   { "collapse-all",           OP_MAIN_COLLAPSE_ALL,           "\033V" },
+@@ -105,7 +109,15 @@ const struct binding_t OpMain[] = { /* m
+   { "edit-label",             OP_EDIT_LABEL,                  "Y" },
+   { "edit-type",              OP_EDIT_TYPE,                   "\005" },
+   { "forward-message",                OP_FORWARD_MESSAGE,             "f" },
+-  { "flag-message",           OP_FLAG_MESSAGE,                "F" },
++#ifdef USE_NNTP
++  { "forward-to-group",               OP_FORWARD_TO_GROUP,            "\033F" },
++  { "followup-message",               OP_FOLLOWUP,                    "F" },
++  { "get-children",           OP_GET_CHILDREN,                NULL },
++  { "get-message",            OP_GET_MESSAGE,                 "\007" },
++  { "get-parent",             OP_GET_PARENT,                  "\033G" },
++  { "reconstruct-thread",     OP_RECONSTRUCT_THREAD,          NULL },
++#endif
++  { "flag-message",           OP_FLAG_MESSAGE,                "\033f" },
+   { "group-chat-reply",               OP_GROUP_CHAT_REPLY,            NULL },
+   { "group-reply",            OP_GROUP_REPLY,                 "g" },
+ #ifdef USE_POP
+@@ -134,6 +146,9 @@ const struct binding_t OpMain[] = { /* m
+   { "sort-mailbox",           OP_SORT,                        "o" },
+   { "sort-reverse",           OP_SORT_REVERSE,                "O" },
+   { "print-message",          OP_PRINT,                       "p" },
++#ifdef USE_NNTP
++  { "post-message",           OP_POST,                        "P" },
++#endif
+   { "previous-thread",                OP_MAIN_PREV_THREAD,            "\020" },
+   { "previous-subthread",     OP_MAIN_PREV_SUBTHREAD,         "\033p" },
+   { "recall-message",         OP_RECALL_MESSAGE,              "R" },
+@@ -153,6 +168,10 @@ const struct binding_t OpMain[] = { /* m
+   { "show-version",           OP_VERSION,                     "V" },
+   { "set-flag",                       OP_MAIN_SET_FLAG,               "w" },
+   { "clear-flag",             OP_MAIN_CLEAR_FLAG,             "W" },
++  { "toggle-read",            OP_TOGGLE_READ,                 "X" },
++#ifdef USE_NNTP
++  { "catchup",                        OP_CATCHUP,                     "y" },
++#endif
+   { "display-message",                OP_DISPLAY_MESSAGE,             MUTT_ENTER_S },
+   { "mark-message",           OP_MARK_MSG,                    "~" },
+   { "buffy-list",             OP_BUFFY_LIST,                  "." },
+@@ -165,7 +184,7 @@ const struct binding_t OpMain[] = { /* m
+   { "previous-new-then-unread",       OP_MAIN_PREV_NEW_THEN_UNREAD,   "\033\t" },
+   { "next-unread",            OP_MAIN_NEXT_UNREAD,            NULL },
+   { "previous-unread",                OP_MAIN_PREV_UNREAD,            NULL },
+-  { "parent-message",         OP_MAIN_PARENT_MESSAGE,         "P" },
++  { "parent-message",         OP_MAIN_PARENT_MESSAGE,         NULL },
+   { "root-message",           OP_MAIN_ROOT_MESSAGE,           NULL },
+@@ -196,6 +215,10 @@ const struct binding_t OpPager[] = { /*
+   { "bounce-message", OP_BOUNCE_MESSAGE,              "b" },
+   { "change-folder",  OP_MAIN_CHANGE_FOLDER,          "c" },
+   { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
++#ifdef USE_NNTP
++  { "change-newsgroup",               OP_MAIN_CHANGE_GROUP,           "i" },
++  { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY,        "\033i" },
++#endif
+   { "next-unread-mailbox",    OP_MAIN_NEXT_UNREAD_MAILBOX, NULL },
+   { "compose-to-sender",        OP_COMPOSE_TO_SENDER,           NULL},
+   { "copy-message",   OP_COPY_MESSAGE,                "C" },
+@@ -208,8 +231,12 @@ const struct binding_t OpPager[] = { /*
+   { "edit",           OP_EDIT_MESSAGE,                "e" },
+   { "edit-label",     OP_EDIT_LABEL,                  "Y" },
+   { "edit-type",      OP_EDIT_TYPE,                   "\005" },
++#ifdef USE_NNTP
++  { "followup-message",       OP_FOLLOWUP,                    "F" },
++  { "forward-to-group",       OP_FORWARD_TO_GROUP,            "\033F" },
++#endif
+   { "forward-message",        OP_FORWARD_MESSAGE,             "f" },
+-  { "flag-message",   OP_FLAG_MESSAGE,                "F" },
++  { "flag-message",   OP_FLAG_MESSAGE,                "\033f" },
+   { "group-chat-reply",       OP_GROUP_CHAT_REPLY,            NULL },
+   { "group-reply",    OP_GROUP_REPLY,                 "g" },
+ #ifdef USE_IMAP
+@@ -233,6 +260,9 @@ const struct binding_t OpPager[] = { /*
+   { "sort-mailbox",   OP_SORT,                        "o" },
+   { "sort-reverse",   OP_SORT_REVERSE,                "O" },
+   { "print-message",  OP_PRINT,                       "p" },
++#ifdef USE_NNTP
++  { "post-message",   OP_POST,                        "P" },
++#endif
+   { "previous-thread",        OP_MAIN_PREV_THREAD,            "\020" },
+   { "previous-subthread",OP_MAIN_PREV_SUBTHREAD,      "\033p" },
+   { "purge-message",  OP_PURGE_MESSAGE,               NULL },
+@@ -282,7 +312,7 @@ const struct binding_t OpPager[] = { /*
+   { "half-down",      OP_HALF_DOWN,                   NULL },
+   { "previous-line",  OP_PREV_LINE,                   NULL },
+   { "bottom",         OP_PAGER_BOTTOM,                NULL },
+-  { "parent-message", OP_MAIN_PARENT_MESSAGE,         "P" },
++  { "parent-message", OP_MAIN_PARENT_MESSAGE,         NULL },
+   { "root-message",   OP_MAIN_ROOT_MESSAGE,           NULL },
+@@ -317,6 +347,10 @@ const struct binding_t OpAttach[] = { /*
+   { "display-toggle-weed",    OP_DISPLAY_HEADERS,     "h" },
+   { "compose-to-sender",        OP_COMPOSE_TO_SENDER,           NULL},
+   { "edit-type",      OP_EDIT_TYPE,                   "\005" },
++#ifdef USE_NNTP
++  { "followup-message",       OP_FOLLOWUP,                    "F" },
++  { "forward-to-group",       OP_FORWARD_TO_GROUP,            "\033F" },
++#endif
+   { "print-entry",    OP_PRINT,                       "p" },
+   { "save-entry",     OP_SAVE,                        "s" },
+   { "pipe-entry",     OP_PIPE,                        "|" },
+@@ -343,6 +377,7 @@ const struct binding_t OpAttach[] = { /*
+ const struct binding_t OpCompose[] = { /* map: compose */
+   { "attach-file",    OP_COMPOSE_ATTACH_FILE,         "a" },
+   { "attach-message", OP_COMPOSE_ATTACH_MESSAGE,      "A" },
++  { "attach-news-message",OP_COMPOSE_ATTACH_NEWS_MESSAGE,"\033a" },
+   { "edit-bcc",               OP_COMPOSE_EDIT_BCC,            "b" },
+   { "edit-cc",                OP_COMPOSE_EDIT_CC,             "c" },
+   { "copy-file",      OP_SAVE,                        "C" },
+@@ -362,6 +397,11 @@ const struct binding_t OpCompose[] = { /
+   { "print-entry",    OP_PRINT,                       "l" },
+   { "edit-mime",      OP_COMPOSE_EDIT_MIME,           "m" },
+   { "new-mime",               OP_COMPOSE_NEW_MIME,            "n" },
++#ifdef USE_NNTP
++  { "edit-newsgroups",        OP_COMPOSE_EDIT_NEWSGROUPS,     "N" },
++  { "edit-followup-to",       OP_COMPOSE_EDIT_FOLLOWUP_TO,    "o" },
++  { "edit-x-comment-to",OP_COMPOSE_EDIT_X_COMMENT_TO, "x" },
++#endif
+   { "postpone-message",       OP_COMPOSE_POSTPONE_MESSAGE,    "P" },
+   { "edit-reply-to",  OP_COMPOSE_EDIT_REPLY_TO,       "r" },
+   { "rename-attachment",OP_COMPOSE_RENAME_ATTACHMENT, "\017" },
+@@ -415,14 +455,25 @@ const struct binding_t OpBrowser[] = { /
+   { "select-new",     OP_BROWSER_NEW_FILE,    "N" },
+   { "check-new",      OP_CHECK_NEW,           NULL },
+   { "toggle-mailboxes", OP_TOGGLE_MAILBOXES,  "\t" },
++#ifdef USE_NNTP
++  { "reload-active",  OP_LOAD_ACTIVE,         "g" },
++  { "subscribe-pattern", OP_SUBSCRIBE_PATTERN,        "S" },
++  { "unsubscribe-pattern", OP_UNSUBSCRIBE_PATTERN, "U" },
++  { "catchup",                OP_CATCHUP,             "y" },
++  { "uncatchup",      OP_UNCATCHUP,           "Y" },
++#endif
+   { "view-file",      OP_BROWSER_VIEW_FILE,   " " },
+   { "buffy-list",     OP_BUFFY_LIST,          "." },
+ #ifdef USE_IMAP
+   { "create-mailbox",   OP_CREATE_MAILBOX,      "C" },
+   { "delete-mailbox",   OP_DELETE_MAILBOX,      "d" },
+   { "rename-mailbox",   OP_RENAME_MAILBOX,      "r" },
++#endif
++#if defined USE_IMAP || defined USE_NNTP
+   { "subscribe",      OP_BROWSER_SUBSCRIBE,   "s" },
+   { "unsubscribe",    OP_BROWSER_UNSUBSCRIBE, "u" },
++#endif
++#ifdef USE_IMAP
+   { "toggle-subscribed", OP_BROWSER_TOGGLE_LSUB, "T" },
+ #endif
+   { NULL,             0,                      NULL }
+diff -udprP mutt-1.12.1.orig/globals.h mutt-1.12.1/globals.h
+--- mutt-1.12.1.orig/globals.h 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/globals.h      2019-08-11 20:31:26.139940597 +0300
+@@ -73,7 +73,7 @@ WHERE char *Inbox;
+ WHERE char *Ispell;
+ WHERE char *MailcapPath;
+ WHERE char *Maildir;
+-#if defined(USE_IMAP) || defined(USE_POP)
++#if defined(USE_IMAP) || defined(USE_POP) || defined(USE_NNTP)
+ WHERE char *MessageCachedir;
+ #endif
+ #if USE_HCACHE
+@@ -101,6 +101,17 @@ WHERE char *MixEntryFormat;
+ #endif
+ WHERE char *Muttrc INITVAL (NULL);
++#ifdef USE_NNTP
++WHERE char *GroupFormat;
++WHERE char *Inews;
++WHERE char *NewsCacheDir;
++WHERE char *NewsServer;
++WHERE char *NewsgroupsCharset;
++WHERE char *NewsRc;
++WHERE char *NntpAuthenticators;
++WHERE char *NntpUser;
++WHERE char *NntpPass;
++#endif
+ WHERE char *Outbox;
+ WHERE char *Pager;
+ WHERE char *PagerFmt;
+@@ -213,6 +224,11 @@ extern unsigned char QuadOptions[];
+ WHERE unsigned short Counter INITVAL (0);
++#ifdef USE_NNTP
++WHERE short NewsPollTimeout;
++WHERE short NntpContext;
++#endif
++
+ WHERE short ConnectTimeout;
+ WHERE short ErrorHistSize;
+ WHERE short HistSize;
+diff -udprP mutt-1.12.1.orig/hcache.c mutt-1.12.1/hcache.c
+--- mutt-1.12.1.orig/hcache.c  2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/hcache.c       2019-08-11 20:31:26.139940597 +0300
+@@ -543,6 +543,12 @@ dump_envelope(ENVELOPE * e, unsigned cha
+   d = dump_list(e->in_reply_to, d, off, 0);
+   d = dump_list(e->userhdrs, d, off, convert);
++#ifdef USE_NNTP
++  d = dump_char(e->xref, d, off, 0);
++  d = dump_char(e->followup_to, d, off, 0);
++  d = dump_char(e->x_comment_to, d, off, convert);
++#endif
++
+   return d;
+ }
+@@ -582,6 +588,12 @@ restore_envelope(ENVELOPE * e, const uns
+   restore_list(&e->references, d, off, 0);
+   restore_list(&e->in_reply_to, d, off, 0);
+   restore_list(&e->userhdrs, d, off, convert);
++
++#ifdef USE_NNTP
++  restore_char(&e->xref, d, off, 0);
++  restore_char(&e->followup_to, d, off, 0);
++  restore_char(&e->x_comment_to, d, off, convert);
++#endif
+ }
+ static int
+diff -udprP mutt-1.12.1.orig/hdrline.c mutt-1.12.1/hdrline.c
+--- mutt-1.12.1.orig/hdrline.c 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/hdrline.c      2019-08-11 20:31:26.139940597 +0300
+@@ -227,6 +227,7 @@ static char *apply_subject_mods (ENVELOP
+  * %E = number of messages in current thread
+  * %f = entire from line
+  * %F = like %n, unless from self
++ * %g = newsgroup name (if compiled with NNTP support)
+  * %i = message-id
+  * %l = number of lines in the message
+  * %L = like %F, except `lists' are displayed first
+@@ -243,6 +244,8 @@ static char *apply_subject_mods (ENVELOP
+  * %T = $to_chars
+  * %u = user (login) name of author
+  * %v = first name of author, unless from self
++ * %W = where user is (organization)
++ * %x = `x-comment-to:' field (if present and compiled with NNTP support)
+  * %X = number of MIME attachments
+  * %y = `x-label:' field (if present)
+  * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
+@@ -476,6 +479,12 @@ hdr_format_str (char *dest,
+       break;
++#ifdef USE_NNTP
++    case 'g':
++      mutt_format_s (dest, destlen, prefix, hdr->env->newsgroups ? hdr->env->newsgroups : "");
++      break;
++#endif
++
+     case 'i':
+       mutt_format_s (dest, destlen, prefix, hdr->env->message_id ? hdr->env->message_id : "<no.id>");
+       break;
+@@ -680,6 +689,22 @@ hdr_format_str (char *dest,
+       mutt_format_s (dest, destlen, prefix, buf2);
+       break;
++    case 'W':
++      if (!optional)
++      mutt_format_s (dest, destlen, prefix, hdr->env->organization ? hdr->env->organization : "");
++      else if (!hdr->env->organization)
++      optional = 0;
++      break;
++
++#ifdef USE_NNTP
++    case 'x':
++      if (!optional)
++      mutt_format_s (dest, destlen, prefix, hdr->env->x_comment_to ? hdr->env->x_comment_to : "");
++      else if (!hdr->env->x_comment_to)
++      optional = 0;
++      break;
++#endif
++
+     case 'Z':
+       ch = ' ';
+diff -udprP mutt-1.12.1.orig/headers.c mutt-1.12.1/headers.c
+--- mutt-1.12.1.orig/headers.c 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/headers.c      2019-08-11 20:31:26.139940597 +0300
+@@ -115,6 +115,9 @@ void mutt_edit_headers (const char *edit
+      $edit_headers set, we remove References: as they're likely invalid;
+      we can simply compare strings as we don't generate References for
+      multiple Message-Ids in IRT anyways */
++#ifdef USE_NNTP
++  if (!option (OPTNEWSSEND))
++#endif
+   if (msg->env->in_reply_to &&
+       (!n->in_reply_to || mutt_strcmp (n->in_reply_to->data,
+                                      msg->env->in_reply_to->data) != 0))
+diff -udprP mutt-1.12.1.orig/init.c mutt-1.12.1/init.c
+--- mutt-1.12.1.orig/init.c    2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/init.c 2019-08-11 20:31:26.139940597 +0300
+@@ -3517,6 +3517,28 @@ void mutt_init (int skip_sys_rc, LIST *c
+     Fqdn = safe_strdup(utsname.nodename);
++#ifdef USE_NNTP
++  {
++    FILE *f;
++    char *i;
++
++    if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
++    {
++      buffer[0] = '\0';
++      fgets (buffer, sizeof (buffer), f);
++      p = buffer;
++      SKIPWS (p);
++      i = p;
++      while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
++      *i = '\0';
++      NewsServer = safe_strdup (p);
++      fclose (f);
++    }
++  }
++  if ((p = getenv ("NNTPSERVER")))
++    NewsServer = safe_strdup (p);
++#endif
++
+   if ((p = getenv ("MAIL")))
+     Spoolfile = safe_strdup (p);
+   else if ((p = getenv ("MAILDIR")))
+diff -udprP mutt-1.12.1.orig/init.h mutt-1.12.1/init.h
+--- mutt-1.12.1.orig/init.h    2019-06-15 18:57:01.000000000 +0300
++++ mutt-1.12.1/init.h 2019-08-11 20:31:26.140940582 +0300
+@@ -202,6 +202,20 @@ struct option_t MuttVars[] = {
+   ** If \fIset\fP, Mutt will prompt you for carbon-copy (Cc) recipients before
+   ** editing the body of an outgoing message.
+   */
++#ifdef USE_NNTP
++  { "ask_follow_up",  DT_BOOL, R_NONE, OPTASKFOLLOWUP, 0 },
++  /*
++  ** .pp
++  ** If set, Mutt will prompt you for follow-up groups before editing
++  ** the body of an outgoing message.
++  */
++  { "ask_x_comment_to",       DT_BOOL, R_NONE, OPTASKXCOMMENTTO, 0 },
++  /*
++  ** .pp
++  ** If set, Mutt will prompt you for x-comment-to field before editing
++  ** the body of an outgoing message.
++  */
++#endif
+   { "assumed_charset", DT_STR, R_NONE, UL &AssumedCharset, UL 0},
+   /*
+   ** .pp
+@@ -383,6 +397,14 @@ struct option_t MuttVars[] = {
+   ** doesn't make intuitive sense.  In those cases, it may be
+   ** desirable to \fIunset\fP this variable.
+   */
++#ifdef USE_NNTP
++  { "catchup_newsgroup", DT_QUAD, R_NONE, OPT_CATCHUP, MUTT_ASKYES },
++  /*
++  ** .pp
++  ** If this variable is \fIset\fP, Mutt will mark all articles in newsgroup
++  ** as read when you quit the newsgroup (catchup newsgroup).
++  */
++#endif
+ #if defined(USE_SSL)
+   { "certificate_file",       DT_PATH, R_NONE, UL &SslCertFile, UL "~/.mutt_certificates" },
+   /*
+@@ -1018,6 +1040,16 @@ struct option_t MuttVars[] = {
+   ** sent to both the list and your address, resulting in two copies
+   ** of the same email for you.
+   */
++#ifdef USE_NNTP
++  { "followup_to_poster", DT_QUAD, R_NONE, OPT_FOLLOWUPTOPOSTER, MUTT_ASKYES },
++  /*
++  ** .pp
++  ** If this variable is \fIset\fP and the keyword "poster" is present in
++  ** \fIFollowup-To\fP header, follow-up to newsgroup function is not
++  ** permitted.  The message will be mailed to the submitter of the
++  ** message via mail.
++  */
++#endif
+   { "force_name",     DT_BOOL, R_NONE, OPTFORCENAME, 0 },
+   /*
+   ** .pp
+@@ -1125,6 +1157,26 @@ struct option_t MuttVars[] = {
+   ** a regular expression that will match the whole name so mutt will expand
+   ** ``Franklin'' to ``Franklin, Steve''.
+   */
++#ifdef USE_NNTP
++  { "group_index_format", DT_STR, R_BOTH, UL &GroupFormat, UL "%4C %M%N %5s  %-45.45f %d" },
++  /*
++  ** .pp
++  ** This variable allows you to customize the newsgroup browser display to
++  ** your personal taste.  This string is similar to ``$index_format'', but
++  ** has its own set of printf()-like sequences:
++  ** .dl
++  ** .dt %C  .dd current newsgroup number
++  ** .dt %d  .dd description of newsgroup (becomes from server)
++  ** .dt %f  .dd newsgroup name
++  ** .dt %M  .dd - if newsgroup not allowed for direct post (moderated for example)
++  ** .dt %N  .dd N if newsgroup is new, u if unsubscribed, blank otherwise
++  ** .dt %n  .dd number of new articles in newsgroup
++  ** .dt %s  .dd number of unread articles in newsgroup
++  ** .dt %>X .dd right justify the rest of the string and pad with character "X"
++  ** .dt %|X .dd pad to the end of the line with character "X"
++  ** .de
++  */
++#endif
+   { "hdr_format",     DT_SYN,  R_NONE, UL "index_format", 0 },
+   /*
+   */
+@@ -1597,6 +1649,7 @@ struct option_t MuttVars[] = {
+   ** .dt %E .dd number of messages in current thread
+   ** .dt %f .dd sender (address + real name), either From: or Return-Path:
+   ** .dt %F .dd author name, or recipient name if the message is from you
++  ** .dt %g .dd newsgroup name (if compiled with NNTP support)
+   ** .dt %H .dd spam attribute(s) of this message
+   ** .dt %i .dd message-id of the current message
+   ** .dt %l .dd number of lines in the message (does not work with maildir,
+@@ -1620,6 +1673,8 @@ struct option_t MuttVars[] = {
+   ** .dt %T .dd the appropriate character from the $$to_chars string
+   ** .dt %u .dd user (login) name of the author
+   ** .dt %v .dd first name of the author, or the recipient if the message is from you
++  ** .dt %W .dd name of organization of author (``Organization:'' field)
++  ** .dt %x .dd ``X-Comment-To:'' field (if present and compiled with NNTP support)
+   ** .dt %X .dd number of attachments
+   **            (please see the ``$attachments'' section for possible speed effects)
+   ** .dt %y .dd ``X-Label:'' field, if present
+@@ -1666,6 +1721,25 @@ struct option_t MuttVars[] = {
+   ** $$forward_format, $$indent_string, $$message_format, $$pager_format,
+   ** and $$post_indent_string.
+   */
++#ifdef USE_NNTP
++  { "inews",          DT_PATH, R_NONE, UL &Inews, UL "" },
++  /*
++  ** .pp
++  ** If set, specifies the program and arguments used to deliver news posted
++  ** by Mutt.  Otherwise, mutt posts article using current connection to
++  ** news server.  The following printf-style sequence is understood:
++  ** .dl
++  ** .dt %a .dd account url
++  ** .dt %p .dd port
++  ** .dt %P .dd port if specified
++  ** .dt %s .dd news server name
++  ** .dt %S .dd url schema
++  ** .dt %u .dd username
++  ** .de
++  ** .pp
++  ** Example: set inews="/usr/local/bin/inews -hS"
++  */
++#endif
+   { "ispell",         DT_PATH, R_NONE, UL &Ispell, UL ISPELL },
+   /*
+   ** .pp
+@@ -1958,6 +2032,15 @@ struct option_t MuttVars[] = {
+   ** When \fIset\fP, the $$mime_type_query_command will be run before the
+   ** mime.types lookup.
+   */
++#ifdef USE_NNTP
++  { "mime_subject",   DT_BOOL, R_NONE, OPTMIMESUBJECT, 1 },
++  /*
++  ** .pp
++  ** If \fIunset\fP, 8-bit ``subject:'' line in article header will not be
++  ** encoded according to RFC2047 to base64.  This is useful when message
++  ** is Usenet article, because MIME for news is nonstandard feature.
++  */
++#endif
+ #ifdef MIXMASTER
+   { "mix_entry_format", DT_STR,  R_NONE, UL &MixEntryFormat, UL "%4n %c %-16s %a" },
+   /*
+@@ -2012,6 +2095,106 @@ struct option_t MuttVars[] = {
+   ** See the $$status_format documentation for the values that can be formatted
+   ** into this command.
+   */
++#ifdef USE_NNTP
++  { "news_cache_dir", DT_PATH, R_NONE, UL &NewsCacheDir, UL "~/.mutt" },
++  /*
++  ** .pp
++  ** This variable pointing to directory where Mutt will save cached news
++  ** articles and headers in. If \fIunset\fP, articles and headers will not be
++  ** saved at all and will be reloaded from the server each time.
++  */
++  { "news_server",    DT_STR, R_NONE, UL &NewsServer, 0 },
++  /*
++  ** .pp
++  ** This variable specifies domain name or address of NNTP server. It
++  ** defaults to the news server specified in the environment variable
++  ** $$$NNTPSERVER or contained in the file /etc/nntpserver.  You can also
++  ** specify username and an alternative port for each news server, ie:
++  ** .pp
++  ** [[s]news://][username[:password]@]server[:port]
++  */
++  { "newsgroups_charset", DT_STR, R_NONE, UL &NewsgroupsCharset, UL "utf-8" },
++  /*
++  ** .pp
++  ** Character set of newsgroups descriptions.
++  */
++  { "newsrc",         DT_PATH, R_NONE, UL &NewsRc, UL "~/.newsrc" },
++  /*
++  ** .pp
++  ** The file, containing info about subscribed newsgroups - names and
++  ** indexes of read articles.  The following printf-style sequence
++  ** is understood:
++  ** .dl
++  ** .dt %a .dd account url
++  ** .dt %p .dd port
++  ** .dt %P .dd port if specified
++  ** .dt %s .dd news server name
++  ** .dt %S .dd url schema
++  ** .dt %u .dd username
++  ** .de
++  */
++  { "nntp_authenticators", DT_STR, R_NONE, UL &NntpAuthenticators, UL 0 },
++  /*
++  ** .pp
++  ** This is a colon-delimited list of authentication methods mutt may
++  ** attempt to use to log in to a news server, in the order mutt should
++  ** try them.  Authentication methods are either ``user'' or any
++  ** SASL mechanism, e.g. ``digest-md5'', ``gssapi'' or ``cram-md5''.
++  ** This option is case-insensitive.  If it's \fIunset\fP (the default)
++  ** mutt will try all available methods, in order from most-secure to
++  ** least-secure.
++  ** .pp
++  ** Example:
++  ** .ts
++  ** set nntp_authenticators="digest-md5:user"
++  ** .te
++  ** .pp
++  ** \fBNote:\fP Mutt will only fall back to other authentication methods if
++  ** the previous methods are unavailable. If a method is available but
++  ** authentication fails, mutt will not connect to the IMAP server.
++  */
++  { "nntp_context",   DT_NUM, R_NONE, UL &NntpContext, 1000 },
++  /*
++  ** .pp
++  ** This variable defines number of articles which will be in index when
++  ** newsgroup entered.  If active newsgroup have more articles than this
++  ** number, oldest articles will be ignored.  Also controls how many
++  ** articles headers will be saved in cache when you quit newsgroup.
++  */
++  { "nntp_listgroup", DT_BOOL, R_NONE, OPTLISTGROUP, 1 },
++  /*
++  ** .pp
++  ** This variable controls whether or not existence of each article is
++  ** checked when newsgroup is entered.
++  */
++  { "nntp_load_description", DT_BOOL, R_NONE, OPTLOADDESC, 1 },
++  /*
++  ** .pp
++  ** This variable controls whether or not descriptions for each newsgroup
++  ** must be loaded when newsgroup is added to list (first time list
++  ** loading or new newsgroup adding).
++  */
++  { "nntp_user",      DT_STR, R_NONE, UL &NntpUser, UL "" },
++  /*
++  ** .pp
++  ** Your login name on the NNTP server.  If \fIunset\fP and NNTP server requires
++  ** authentification, Mutt will prompt you for your account name when you
++  ** connect to news server.
++  */
++  { "nntp_pass",      DT_STR, R_NONE, UL &NntpPass, UL "" },
++  /*
++  ** .pp
++  ** Your password for NNTP account.
++  */
++  { "nntp_poll",      DT_NUM, R_NONE, UL &NewsPollTimeout, 60 },
++  /*
++  ** .pp
++  ** The time in seconds until any operations on newsgroup except post new
++  ** article will cause recheck for new news.  If set to 0, Mutt will
++  ** recheck newsgroup on each operation in index (stepping, read article,
++  ** etc.).
++  */
++#endif
+   { "pager",          DT_PATH, R_NONE, UL &Pager, UL "builtin" },
+   /*
+   ** .pp
+@@ -2606,6 +2789,16 @@ struct option_t MuttVars[] = {
+   { "post_indent_str",  DT_SYN,  R_NONE, UL "post_indent_string", 0 },
+   /*
+   */
++#ifdef USE_NNTP
++  { "post_moderated", DT_QUAD, R_NONE, OPT_TOMODERATED, MUTT_ASKYES },
++  /*
++  ** .pp
++  ** If set to \fIyes\fP, Mutt will post article to newsgroup that have
++  ** not permissions to posting (e.g. moderated).  \fBNote:\fP if news server
++  ** does not support posting to that newsgroup or totally read-only, that
++  ** posting will not have an effect.
++  */
++#endif
+   { "postpone",               DT_QUAD, R_NONE, OPT_POSTPONE, MUTT_ASKYES },
+   /*
+   ** .pp
+@@ -3093,6 +3286,28 @@ struct option_t MuttVars[] = {
+   ** Command to use when spawning a subshell.  By default, the user's login
+   ** shell from \fC/etc/passwd\fP is used.
+   */
++#ifdef USE_NNTP
++  { "save_unsubscribed", DT_BOOL, R_NONE, OPTSAVEUNSUB, 0 },
++  /*
++  ** .pp
++  ** When \fIset\fP, info about unsubscribed newsgroups will be saved into
++  ** ``newsrc'' file and into cache.
++  */
++  { "show_new_news",  DT_BOOL, R_NONE, OPTSHOWNEWNEWS, 1 },
++  /*
++  ** .pp
++  ** If \fIset\fP, news server will be asked for new newsgroups on entering
++  ** the browser.  Otherwise, it will be done only once for a news server.
++  ** Also controls whether or not number of new articles of subscribed
++  ** newsgroups will be then checked.
++  */
++  { "show_only_unread",       DT_BOOL, R_NONE, OPTSHOWONLYUNREAD, 0 },
++  /*
++  ** .pp
++  ** If \fIset\fP, only subscribed newsgroups that contain unread articles
++  ** will be displayed in browser.
++  */
++#endif
+ #ifdef USE_SIDEBAR
+   { "sidebar_delim_chars", DT_STR, R_SIDEBAR, UL &SidebarDelimChars, UL "/." },
+   /*
+@@ -4234,6 +4449,14 @@ struct option_t MuttVars[] = {
+   {"xterm_set_titles",        DT_SYN,  R_NONE, UL "ts_enabled", 0 },
+   /*
+   */
++#ifdef USE_NNTP
++  { "x_comment_to",   DT_BOOL, R_NONE, OPTXCOMMENTTO, 0 },
++  /*
++  ** .pp
++  ** If \fIset\fP, Mutt will add ``X-Comment-To:'' field (that contains full
++  ** name of original article author) to article that followuped to newsgroup.
++  */
++#endif
+   /*--*/
+   { NULL, 0, 0, 0, 0 }
+ };
+diff -udprP mutt-1.12.1.orig/keymap.c mutt-1.12.1/keymap.c
+--- mutt-1.12.1.orig/keymap.c  2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/keymap.c       2019-08-11 20:31:26.140940582 +0300
+@@ -808,7 +808,6 @@ void km_init (void)
+   km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
+   km_bindkey ("x", MENU_PAGER, OP_EXIT);
+-  km_bindkey ("i", MENU_PAGER, OP_EXIT);
+   km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE);
+   km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
+   km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE);
+diff -udprP mutt-1.12.1.orig/mailbox.h mutt-1.12.1/mailbox.h
+--- mutt-1.12.1.orig/mailbox.h 2019-06-11 03:53:09.000000000 +0300
++++ mutt-1.12.1/mailbox.h      2019-08-11 20:31:26.140940582 +0300
+@@ -78,6 +78,9 @@ int mx_is_imap (const char *);
+ #ifdef USE_POP
+ int mx_is_pop (const char *);
+ #endif
++#ifdef USE_NNTP
++int mx_is_nntp (const char *);
++#endif
+ int mx_access (const char*, int);
+ int mx_check_empty (const char *);
+diff -udprP mutt-1.12.1.orig/main.c mutt-1.12.1/main.c
+--- mutt-1.12.1.orig/main.c    2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/main.c 2019-08-11 20:31:26.140940582 +0300
+@@ -69,6 +69,10 @@
+ #include <idn/stringprep.h>
+ #endif
++#ifdef USE_NNTP
++#include "nntp.h"
++#endif
++
+ static const char *ReachingUs = N_("\
+ To contact the developers, please mail to <mutt-dev@mutt.org>.\n\
+ To report a bug, please contact the Mutt maintainers via gitlab:\n\
+@@ -149,6 +153,8 @@ options:\n\
+   -e <command>\tspecify a command to be executed after initialization\n\
+   -f <file>\tspecify which mailbox to read\n\
+   -F <file>\tspecify an alternate muttrc file\n\
++  -g <server>\tspecify a news server (if compiled with NNTP)\n\
++  -G\t\tselect a newsgroup (if compiled with NNTP)\n\
+   -H <file>\tspecify a draft file to read header and body from\n\
+   -i <file>\tspecify a file which Mutt should include in the body\n\
+   -m <type>\tspecify a default mailbox type\n\
+@@ -302,6 +308,12 @@ static void show_version (void)
+     "-USE_POP  "
+ #endif
++#ifdef USE_NNTP
++      "+USE_NNTP  "
++#else
++      "-USE_NNTP  "
++#endif
++
+ #ifdef USE_IMAP
+     "+USE_IMAP  "
+ #else
+@@ -605,6 +617,7 @@ static void start_curses (void)
+ #define MUTT_NOSYSRC (1<<2)   /* -n */
+ #define MUTT_RO      (1<<3)   /* -R */
+ #define MUTT_SELECT  (1<<4)   /* -y */
++#define MUTT_NEWS    (1<<5)   /* -g and -G */
+ int main (int argc, char **argv, char **environ)
+ {
+@@ -696,7 +709,11 @@ int main (int argc, char **argv, char **
+         argv[nargc++] = argv[optind];
+     }
++#ifdef USE_NNTP
++    if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:Ee:g:GH:s:i:hm:npQ:RvxyzZ")) != EOF)
++#else
+     if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:Ee:H:s:i:hm:npQ:RvxyzZ")) != EOF)
++#endif
+       switch (i)
+       {
+         case 'A':
+@@ -799,6 +816,20 @@ int main (int argc, char **argv, char **
+           flags |= MUTT_SELECT;
+           break;
++#ifdef USE_NNTP
++        case 'g': /* Specify a news server */
++          {
++            char buf[LONG_STRING];
++
++            snprintf (buf, sizeof (buf), "set news_server=%s", optarg);
++            commands = mutt_add_list (commands, buf);
++          }
++
++        case 'G': /* List of newsgroups */
++          flags |= MUTT_SELECT | MUTT_NEWS;
++          break;
++#endif
++
+         case 'z':
+           flags |= MUTT_IGNORE;
+           break;
+@@ -1284,6 +1315,18 @@ int main (int argc, char **argv, char **
+     }
+     else if (flags & MUTT_SELECT)
+     {
++#ifdef USE_NNTP
++      if (flags & MUTT_NEWS)
++      {
++      set_option (OPTNEWS);
++      if (!(CurrentNewsSrv = nntp_select_server (NewsServer, 0)))
++      {
++        exit_endwin_msg = Errorbuf;
++        goto cleanup_and_exit;
++      }
++      }
++      else
++#endif
+       if (!Incoming)
+       {
+         exit_endwin_msg = _("No incoming mailboxes defined.");
+@@ -1300,6 +1343,14 @@ int main (int argc, char **argv, char **
+     if (!mutt_buffer_len (folder))
+       mutt_buffer_strcpy (folder, NONULL(Spoolfile));
++#ifdef USE_NNTP
++    if (option (OPTNEWS))
++    {
++      unset_option (OPTNEWS);
++      nntp_buffer_expand_path (folder, &CurrentNewsSrv->conn->account);
++    }
++    else
++#endif
+     mutt_buffer_expand_path (folder);
+     mutt_str_replace (&CurrentFolder, mutt_b2s (folder));
+diff -udprP mutt-1.12.1.orig/mutt.h mutt-1.12.1/mutt.h
+--- mutt-1.12.1.orig/mutt.h    2019-06-14 04:29:29.000000000 +0300
++++ mutt-1.12.1/mutt.h 2019-08-11 20:31:26.141940567 +0300
+@@ -271,6 +271,9 @@ enum
+   MUTT_XLABEL,
+   MUTT_MIMEATTACH,
+   MUTT_MIMETYPE,
++#ifdef USE_NNTP
++  MUTT_NEWSGROUPS,
++#endif
+   /* Options for Mailcap lookup */
+   MUTT_EDIT,
+@@ -329,6 +332,11 @@ enum
+ #endif
+   OPT_SUBJECT,
+   OPT_VERIFYSIG,      /* verify PGP signatures */
++#ifdef USE_NNTP
++  OPT_TOMODERATED,
++  OPT_CATCHUP,
++  OPT_FOLLOWUPTOPOSTER,
++#endif
+   /* THIS MUST BE THE LAST VALUE. */
+   OPT_MAX
+@@ -349,6 +357,7 @@ enum
+ #define SENDDRAFTFILE         (1<<11)   /* Used by the -H flag */
+ #define SENDTOSENDER            (1<<12)
+ #define SENDGROUPCHATREPLY      (1<<13)
++#define SENDNEWS      (1<<14)
+ /* flags for mutt_compose_menu() */
+ #define MUTT_COMPOSE_NOFREEHEADER (1<<0)
+@@ -371,6 +380,8 @@ enum
+   OPTASCIICHARS,
+   OPTASKBCC,
+   OPTASKCC,
++  OPTASKFOLLOWUP,
++  OPTASKXCOMMENTTO,
+   OPTATTACHSPLIT,
+   OPTAUTOEDIT,
+   OPTAUTOSUBSCRIBE,
+@@ -466,6 +477,9 @@ enum
+   OPTMETOO,
+   OPTMHPURGE,
+   OPTMIMEFORWDECODE,
++#ifdef USE_NNTP
++  OPTMIMESUBJECT,     /* encode subject line with RFC2047 */
++#endif
+   OPTMIMETYPEQUERYFIRST,
+   OPTNARROWTREE,
+   OPTPAGERSTOP,
+@@ -568,6 +582,17 @@ enum
+   OPTPGPAUTOINLINE,
+   OPTPGPREPLYINLINE,
++  /* news options */
++
++#ifdef USE_NNTP
++  OPTSHOWNEWNEWS,
++  OPTSHOWONLYUNREAD,
++  OPTSAVEUNSUB,
++  OPTLISTGROUP,
++  OPTLOADDESC,
++  OPTXCOMMENTTO,
++#endif
++
+   /* pseudo options */
+   OPTAUXSORT,         /* (pseudo) using auxiliary sort function */
+@@ -585,6 +610,7 @@ enum
+   OPTSORTSUBTHREADS,  /* (pseudo) used when $sort_aux changes */
+   OPTNEEDRESCORE,     /* (pseudo) set when the `score' command is used */
+   OPTATTACHMSG,               /* (pseudo) used by attach-message */
++  OPTHIDEREAD,                /* (pseudo) whether or not hide read messages */
+   OPTKEEPQUIET,               /* (pseudo) shut up the message and refresh
+                        *          functions while we are executing an
+                        *          external program.
+@@ -595,6 +621,11 @@ enum
+   OPTDONTHANDLEPGPKEYS,       /* (pseudo) used to extract PGP keys */
+   OPTIGNOREMACROEVENTS, /* (pseudo) don't process macro/push/exec events while set */
++#ifdef USE_NNTP
++  OPTNEWS,            /* (pseudo) used to change reader mode */
++  OPTNEWSSEND,                /* (pseudo) used to change behavior when posting */
++#endif
++
+   OPTMAX
+ };
+@@ -680,6 +711,13 @@ typedef struct envelope
+   char *supersedes;
+   char *date;
+   char *x_label;
++  char *organization;
++#ifdef USE_NNTP
++  char *newsgroups;
++  char *xref;
++  char *followup_to;
++  char *x_comment_to;
++#endif
+   BUFFER *spam;
+   LIST *references;           /* message references (in reverse order) */
+   LIST *in_reply_to;          /* in-reply-to header content */
+@@ -869,7 +907,7 @@ typedef struct header
+   int refno;                  /* message number on server */
+ #endif
+-#if defined USE_POP || defined USE_IMAP
++#if defined USE_POP || defined USE_IMAP || defined USE_NNTP
+   void *data;                 /* driver-specific data */
+ #endif
+diff -udprP mutt-1.12.1.orig/mutt_sasl.c mutt-1.12.1/mutt_sasl.c
+--- mutt-1.12.1.orig/mutt_sasl.c       2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/mutt_sasl.c    2019-08-11 20:31:26.141940567 +0300
+@@ -192,6 +192,11 @@ int mutt_sasl_client_new (CONNECTION* co
+     case MUTT_ACCT_TYPE_SMTP:
+       service = "smtp";
+       break;
++#ifdef USE_NNTP
++    case MUTT_ACCT_TYPE_NNTP:
++      service = "nntp";
++      break;
++#endif
+     default:
+       mutt_error (_("Unknown SASL profile"));
+       return -1;
+diff -udprP mutt-1.12.1.orig/muttlib.c mutt-1.12.1/muttlib.c
+--- mutt-1.12.1.orig/muttlib.c 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/muttlib.c      2019-08-11 20:31:26.141940567 +0300
+@@ -364,7 +364,7 @@ void mutt_free_header (HEADER **h)
+ #ifdef MIXMASTER
+   mutt_free_list (&(*h)->chain);
+ #endif
+-#if defined USE_POP || defined USE_IMAP
++#if defined USE_POP || defined USE_IMAP || defined USE_NNTP
+   FREE (&(*h)->data);
+ #endif
+   FREE (h);           /* __FREE_CHECKED__ */
+@@ -778,6 +778,13 @@ void mutt_free_envelope (ENVELOPE **p)
+   FREE (&(*p)->supersedes);
+   FREE (&(*p)->date);
+   FREE (&(*p)->x_label);
++  FREE (&(*p)->organization);
++#ifdef USE_NNTP
++  FREE (&(*p)->newsgroups);
++  FREE (&(*p)->xref);
++  FREE (&(*p)->followup_to);
++  FREE (&(*p)->x_comment_to);
++#endif
+   mutt_buffer_free (&(*p)->spam);
+@@ -1803,6 +1810,14 @@ int mutt_save_confirm (const char *s, st
+     }
+   }
++#ifdef USE_NNTP
++  if (magic == MUTT_NNTP)
++  {
++    mutt_error _("Can't save message to news server.");
++    return 0;
++  }
++#endif
++
+   if (stat (s, st) != -1)
+   {
+     if (magic == -1)
+diff -udprP mutt-1.12.1.orig/mx.c mutt-1.12.1/mx.c
+--- mutt-1.12.1.orig/mx.c      2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/mx.c   2019-08-11 20:31:26.141940567 +0300
+@@ -45,6 +45,10 @@
+ #include "pop.h"
+ #endif
++#ifdef USE_NNTP
++#include "nntp.h"
++#endif
++
+ #include "buffy.h"
+ #ifdef USE_DOTLOCK
+@@ -88,6 +92,10 @@ struct mx_ops* mx_get_ops (int magic)
+     case MUTT_COMPRESSED:
+       return &mx_comp_ops;
+ #endif
++#ifdef USE_NNTP
++    case MUTT_NNTP:
++      return &mx_nntp_ops;
++#endif
+     default:
+       return NULL;
+   }
+@@ -387,6 +395,22 @@ int mx_is_pop (const char *p)
+ }
+ #endif
++#ifdef USE_NNTP
++int mx_is_nntp (const char *p)
++{
++  url_scheme_t scheme;
++
++  if (!p)
++    return 0;
++
++  scheme = url_check_scheme (p);
++  if (scheme == U_NNTP || scheme == U_NNTPS)
++    return 1;
++
++  return 0;
++}
++#endif
++
+ int mx_get_magic (const char *path)
+ {
+   struct stat st;
+@@ -404,6 +428,11 @@ int mx_get_magic (const char *path)
+     return MUTT_POP;
+ #endif /* USE_POP */
++#ifdef USE_NNTP
++  if (mx_is_nntp (path))
++    return MUTT_NNTP;
++#endif /* USE_NNTP */
++
+   if (stat (path, &st) == -1)
+   {
+     dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n",
+@@ -849,6 +878,25 @@ int mx_close_mailbox (CONTEXT *ctx, int
+     return 0;
+   }
++#ifdef USE_NNTP
++  if (ctx->unread && ctx->magic == MUTT_NNTP)
++  {
++    NNTP_DATA *nntp_data = ctx->data;
++
++    if (nntp_data && nntp_data->nserv && nntp_data->group)
++    {
++      int rc = query_quadoption (OPT_CATCHUP, _("Mark all articles read?"));
++      if (rc < 0)
++      {
++      ctx->closing = 0;
++      return -1;
++      }
++      else if (rc == MUTT_YES)
++      mutt_newsgroup_catchup (nntp_data->nserv, nntp_data->group);
++    }
++  }
++#endif
++
+   for (i = 0; i < ctx->msgcount; i++)
+   {
+     if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read
+@@ -856,6 +904,12 @@ int mx_close_mailbox (CONTEXT *ctx, int
+       read_msgs++;
+   }
++#ifdef USE_NNTP
++  /* don't need to move articles from newsgroup */
++  if (ctx->magic == MUTT_NNTP)
++    read_msgs = 0;
++#endif
++
+   if (read_msgs && quadoption (OPT_MOVE) != MUTT_NO)
+   {
+     char *p;
+diff -udprP mutt-1.12.1.orig/mx.h mutt-1.12.1/mx.h
+--- mutt-1.12.1.orig/mx.h      2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/mx.h   2019-08-11 20:31:26.141940567 +0300
+@@ -35,6 +35,9 @@ enum
+   MUTT_MMDF,
+   MUTT_MH,
+   MUTT_MAILDIR,
++#ifdef USE_NNTP
++  MUTT_NNTP,
++#endif
+   MUTT_IMAP,
+   MUTT_POP
+ #ifdef USE_COMPRESSED
+diff -udprP mutt-1.12.1.orig/newsrc.c mutt-1.12.1/newsrc.c
+--- mutt-1.12.1.orig/newsrc.c  1970-01-01 03:00:00.000000000 +0300
++++ mutt-1.12.1/newsrc.c       2019-08-11 20:31:26.142940552 +0300
+@@ -0,0 +1,1260 @@
++/*
++ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
++ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
++ * Copyright (C) 2000-2019 Vsevolod Volkov <vvv@mutt.org.ua>
++ *
++ *     This program is free software; you can redistribute it and/or modify
++ *     it under the terms of the GNU General Public License as published by
++ *     the Free Software Foundation; either version 2 of the License, or
++ *     (at your option) any later version.
++ *
++ *     This program is distributed in the hope that it will be useful,
++ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *     GNU General Public License for more details.
++ *
++ *     You should have received a copy of the GNU General Public License
++ *     along with this program; if not, write to the Free Software
++ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#if HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include "mutt.h"
++#include "mutt_curses.h"
++#include "sort.h"
++#include "mx.h"
++#include "mime.h"
++#include "mailbox.h"
++#include "nntp.h"
++#include "rfc822.h"
++#include "rfc1524.h"
++#include "rfc2047.h"
++#include "bcache.h"
++
++#if USE_HCACHE
++#include "hcache.h"
++#endif
++
++#include <unistd.h>
++#include <string.h>
++#include <ctype.h>
++#include <stdlib.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <dirent.h>
++#include <errno.h>
++
++/* Find NNTP_DATA for given newsgroup or add it */
++static NNTP_DATA *nntp_data_find (NNTP_SERVER *nserv, const char *group)
++{
++  NNTP_DATA *nntp_data = hash_find (nserv->groups_hash, group);
++
++  if (!nntp_data)
++  {
++    /* create NNTP_DATA structure and add it to hash */
++    nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1);
++    nntp_data->group = (char *)nntp_data + sizeof (NNTP_DATA);
++    strcpy (nntp_data->group, group);
++    nntp_data->nserv = nserv;
++    nntp_data->deleted = 1;
++    hash_insert (nserv->groups_hash, nntp_data->group, nntp_data);
++
++    /* add NNTP_DATA to list */
++    if (nserv->groups_num >= nserv->groups_max)
++    {
++      nserv->groups_max *= 2;
++      safe_realloc (&nserv->groups_list,
++                  nserv->groups_max * sizeof (nntp_data));
++    }
++    nserv->groups_list[nserv->groups_num++] = nntp_data;
++  }
++  return nntp_data;
++}
++
++/* Remove all temporarily cache files */
++void nntp_acache_free (NNTP_DATA *nntp_data)
++{
++  int i;
++
++  for (i = 0; i < NNTP_ACACHE_LEN; i++)
++  {
++    if (nntp_data->acache[i].path)
++    {
++      unlink (nntp_data->acache[i].path);
++      FREE (&nntp_data->acache[i].path);
++    }
++  }
++}
++
++/* Free NNTP_DATA, used to destroy hash elements */
++void nntp_data_free (void *data)
++{
++  NNTP_DATA *nntp_data = data;
++
++  if (!nntp_data)
++    return;
++  nntp_acache_free (nntp_data);
++  mutt_bcache_close (&nntp_data->bcache);
++  FREE (&nntp_data->newsrc_ent);
++  FREE (&nntp_data->desc);
++  FREE (&data);
++}
++
++/* Unlock and close .newsrc file */
++void nntp_newsrc_close (NNTP_SERVER *nserv)
++{
++  if (!nserv->newsrc_fp)
++    return;
++
++  dprint (1, (debugfile, "Unlocking %s\n", nserv->newsrc_file));
++  mx_unlock_file (nserv->newsrc_file, fileno (nserv->newsrc_fp), 0);
++  safe_fclose (&nserv->newsrc_fp);
++}
++
++/* Parse .newsrc file:
++ *  0 - not changed
++ *  1 - parsed
++ * -1 - error */
++int nntp_newsrc_parse (NNTP_SERVER *nserv)
++{
++  unsigned int i;
++  char *line;
++  struct stat sb;
++
++  /* if file doesn't exist, create it */
++  nserv->newsrc_fp = safe_fopen (nserv->newsrc_file, "a");
++  safe_fclose (&nserv->newsrc_fp);
++
++  /* open .newsrc */
++  nserv->newsrc_fp = safe_fopen (nserv->newsrc_file, "r");
++  if (!nserv->newsrc_fp)
++  {
++    mutt_perror (nserv->newsrc_file);
++    mutt_sleep (2);
++    return -1;
++  }
++
++  /* lock it */
++  dprint (1, (debugfile, "Locking %s\n", nserv->newsrc_file));
++  if (mx_lock_file (nserv->newsrc_file, fileno (nserv->newsrc_fp), 0, 0, 1))
++  {
++    safe_fclose (&nserv->newsrc_fp);
++    return -1;
++  }
++
++  if (stat (nserv->newsrc_file, &sb))
++  {
++    mutt_perror (nserv->newsrc_file);
++    nntp_newsrc_close (nserv);
++    mutt_sleep (2);
++    return -1;
++  }
++
++  if (nserv->size == sb.st_size && nserv->mtime == sb.st_mtime)
++    return 0;
++
++  nserv->size = sb.st_size;
++  nserv->mtime = sb.st_mtime;
++  nserv->newsrc_modified = 1;
++  dprint (1, (debugfile, "Parsing %s\n", nserv->newsrc_file));
++
++  /* .newsrc has been externally modified or hasn't been loaded yet */
++  for (i = 0; i < nserv->groups_num; i++)
++  {
++    NNTP_DATA *nntp_data = nserv->groups_list[i];
++
++    if (!nntp_data)
++      continue;
++
++    nntp_data->subscribed = 0;
++    nntp_data->newsrc_len = 0;
++    FREE (&nntp_data->newsrc_ent);
++  }
++
++  line = safe_malloc (sb.st_size + 1);
++  while (sb.st_size && fgets (line, sb.st_size + 1, nserv->newsrc_fp))
++  {
++    char *b, *h, *p;
++    unsigned int subs = 0, i = 1;
++    NNTP_DATA *nntp_data;
++
++    /* find end of newsgroup name */
++    p = strpbrk (line, ":!");
++    if (!p)
++      continue;
++
++    /* ":" - subscribed, "!" - unsubscribed */
++    if (*p == ':')
++      subs++;
++    *p++ = '\0';
++
++    /* get newsgroup data */
++    nntp_data = nntp_data_find (nserv, line);
++    FREE (&nntp_data->newsrc_ent);
++
++    /* count number of entries */
++    b = p;
++    while (*b)
++      if (*b++ == ',')
++      i++;
++    nntp_data->newsrc_ent = safe_calloc (i, sizeof (NEWSRC_ENTRY));
++    nntp_data->subscribed = subs;
++
++    /* parse entries */
++    i = 0;
++    while (p)
++    {
++      b = p;
++
++      /* find end of entry */
++      p = strchr (p, ',');
++      if (p)
++      *p++ = '\0';
++
++      /* first-last or single number */
++      h = strchr (b, '-');
++      if (h)
++      *h++ = '\0';
++      else
++      h = b;
++
++      if (sscanf (b, ANUM, &nntp_data->newsrc_ent[i].first) == 1 &&
++        sscanf (h, ANUM, &nntp_data->newsrc_ent[i].last) == 1)
++      i++;
++    }
++    if (i == 0)
++    {
++      nntp_data->newsrc_ent[i].first = 1;
++      nntp_data->newsrc_ent[i].last = 0;
++      i++;
++    }
++    if (nntp_data->lastMessage == 0)
++      nntp_data->lastMessage = nntp_data->newsrc_ent[i - 1].last;
++    nntp_data->newsrc_len = i;
++    safe_realloc (&nntp_data->newsrc_ent, i * sizeof (NEWSRC_ENTRY));
++    nntp_group_unread_stat (nntp_data);
++    dprint (2, (debugfile, "nntp_newsrc_parse: %s\n", nntp_data->group));
++  }
++  FREE (&line);
++  return 1;
++}
++
++/* Generate array of .newsrc entries */
++void nntp_newsrc_gen_entries (CONTEXT *ctx)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  anum_t last = 0, first = 1;
++  int series, i;
++  int save_sort = SORT_ORDER;
++  unsigned int entries;
++
++  if (Sort != SORT_ORDER)
++  {
++    save_sort = Sort;
++    Sort = SORT_ORDER;
++    mutt_sort_headers (ctx, 0);
++  }
++
++  entries = nntp_data->newsrc_len;
++  if (!entries)
++  {
++    entries = 5;
++    nntp_data->newsrc_ent = safe_calloc (entries, sizeof (NEWSRC_ENTRY));
++  }
++
++  /* Set up to fake initial sequence from 1 to the article before the
++   * first article in our list */
++  nntp_data->newsrc_len = 0;
++  series = 1;
++  for (i = 0; i < ctx->msgcount; i++)
++  {
++    /* search for first unread */
++    if (series)
++    {
++      /* We don't actually check sequential order, since we mark
++       * "missing" entries as read/deleted */
++      last = NHDR (ctx->hdrs[i])->article_num;
++      if (last >= nntp_data->firstMessage && !ctx->hdrs[i]->deleted &&
++        !ctx->hdrs[i]->read)
++      {
++      if (nntp_data->newsrc_len >= entries)
++      {
++        entries *= 2;
++        safe_realloc (&nntp_data->newsrc_ent, entries * sizeof (NEWSRC_ENTRY));
++      }
++      nntp_data->newsrc_ent[nntp_data->newsrc_len].first = first;
++      nntp_data->newsrc_ent[nntp_data->newsrc_len].last = last - 1;
++      nntp_data->newsrc_len++;
++      series = 0;
++      }
++    }
++
++    /* search for first read */
++    else
++    {
++      if (ctx->hdrs[i]->deleted || ctx->hdrs[i]->read)
++      {
++      first = last + 1;
++      series = 1;
++      }
++      last = NHDR (ctx->hdrs[i])->article_num;
++    }
++  }
++
++  if (series && first <= nntp_data->lastLoaded)
++  {
++    if (nntp_data->newsrc_len >= entries)
++    {
++      entries++;
++      safe_realloc (&nntp_data->newsrc_ent, entries * sizeof (NEWSRC_ENTRY));
++    }
++    nntp_data->newsrc_ent[nntp_data->newsrc_len].first = first;
++    nntp_data->newsrc_ent[nntp_data->newsrc_len].last = nntp_data->lastLoaded;
++    nntp_data->newsrc_len++;
++  }
++  safe_realloc (&nntp_data->newsrc_ent,
++              nntp_data->newsrc_len * sizeof (NEWSRC_ENTRY));
++
++  if (save_sort != Sort)
++  {
++    Sort = save_sort;
++    mutt_sort_headers (ctx, 0);
++  }
++}
++
++/* Update file with new contents */
++static int update_file (char *filename, char *buf)
++{
++  FILE *fp;
++  char tmpfile[_POSIX_PATH_MAX];
++  int rc = -1;
++
++  while (1)
++  {
++    snprintf (tmpfile, sizeof (tmpfile), "%s.tmp", filename);
++    fp = fopen (tmpfile, "w");
++    if (!fp)
++    {
++      mutt_perror (tmpfile);
++      *tmpfile = '\0';
++      break;
++    }
++    if (fputs (buf, fp) == EOF)
++    {
++      mutt_perror (tmpfile);
++      break;
++    }
++    if (fclose (fp) == EOF)
++    {
++      mutt_perror (tmpfile);
++      fp = NULL;
++      break;
++    }
++    fp = NULL;
++    if (rename (tmpfile, filename) < 0)
++    {
++      mutt_perror (filename);
++      break;
++    }
++    *tmpfile = '\0';
++    rc = 0;
++    break;
++  }
++  if (fp)
++    fclose (fp);
++  if (*tmpfile)
++    unlink (tmpfile);
++  if (rc)
++    mutt_sleep (2);
++  return rc;
++}
++
++/* Update .newsrc file */
++int nntp_newsrc_update (NNTP_SERVER *nserv)
++{
++  char *buf;
++  size_t buflen, off;
++  unsigned int i;
++  int rc = -1;
++
++  if (!nserv)
++    return -1;
++
++  buflen = 10 * LONG_STRING;
++  buf = safe_calloc (1, buflen);
++  off = 0;
++
++  /* we will generate full newsrc here */
++  for (i = 0; i < nserv->groups_num; i++)
++  {
++    NNTP_DATA *nntp_data = nserv->groups_list[i];
++    unsigned int n;
++
++    if (!nntp_data || !nntp_data->newsrc_ent)
++      continue;
++
++    /* write newsgroup name */
++    if (off + strlen (nntp_data->group) + 3 > buflen)
++    {
++      buflen *= 2;
++      safe_realloc (&buf, buflen);
++    }
++    snprintf (buf + off, buflen - off, "%s%c ", nntp_data->group,
++            nntp_data->subscribed ? ':' : '!');
++    off += strlen (buf + off);
++
++    /* write entries */
++    for (n = 0; n < nntp_data->newsrc_len; n++)
++    {
++      if (off + LONG_STRING > buflen)
++      {
++      buflen *= 2;
++      safe_realloc (&buf, buflen);
++      }
++      if (n)
++      buf[off++] = ',';
++      if (nntp_data->newsrc_ent[n].first == nntp_data->newsrc_ent[n].last)
++      snprintf (buf + off, buflen - off, "%d", nntp_data->newsrc_ent[n].first);
++      else if (nntp_data->newsrc_ent[n].first < nntp_data->newsrc_ent[n].last)
++      snprintf (buf + off, buflen - off, "%d-%d",
++                nntp_data->newsrc_ent[n].first, nntp_data->newsrc_ent[n].last);
++      off += strlen (buf + off);
++    }
++    buf[off++] = '\n';
++  }
++  buf[off] = '\0';
++
++  /* newrc being fully rewritten */
++  dprint (1, (debugfile, "Updating %s\n", nserv->newsrc_file));
++  if (nserv->newsrc_file && update_file (nserv->newsrc_file, buf) == 0)
++  {
++    struct stat sb;
++
++    rc = stat (nserv->newsrc_file, &sb);
++    if (rc == 0)
++    {
++      nserv->size = sb.st_size;
++      nserv->mtime = sb.st_mtime;
++    }
++    else
++    {
++      mutt_perror (nserv->newsrc_file);
++      mutt_sleep (2);
++    }
++  }
++  FREE (&buf);
++  return rc;
++}
++
++/* Make fully qualified cache file name */
++static void cache_expand (char *dst, size_t dstlen, ACCOUNT *acct, char *src)
++{
++  char *c;
++  char file[_POSIX_PATH_MAX];
++
++  /* server subdirectory */
++  if (acct)
++  {
++    ciss_url_t url;
++
++    mutt_account_tourl (acct, &url);
++    url.path = src;
++    url_ciss_tostring (&url, file, sizeof (file), U_PATH);
++  }
++  else
++    strfcpy (file, src ? src : "", sizeof (file));
++
++  snprintf (dst, dstlen, "%s/%s", NewsCacheDir, file);
++
++  /* remove trailing slash */
++  c = dst + strlen (dst) - 1;
++  if (*c == '/')
++    *c = '\0';
++  mutt_expand_path (dst, dstlen);
++}
++
++/* Make fully qualified url from newsgroup name */
++void nntp_buffer_expand_path (BUFFER *line, ACCOUNT *acct)
++{
++  ciss_url_t url;
++
++  url.path = safe_strdup (mutt_b2s (line));
++  mutt_account_tourl (acct, &url);
++  url_ciss_tobuffer (&url, line, 0);
++  FREE (&url.path);
++}
++
++/* Parse newsgroup */
++int nntp_add_group (char *line, void *data)
++{
++  NNTP_SERVER *nserv = data;
++  NNTP_DATA *nntp_data;
++  char group[LONG_STRING];
++  char desc[HUGE_STRING] = "";
++  char mod;
++  anum_t first, last;
++
++  if (!nserv || !line)
++    return 0;
++
++  if (sscanf (line, "%s " ANUM " " ANUM " %c %[^\n]", group,
++            &last, &first, &mod, desc) < 4)
++    return 0;
++
++  nntp_data = nntp_data_find (nserv, group);
++  nntp_data->deleted = 0;
++  nntp_data->firstMessage = first;
++  nntp_data->lastMessage = last;
++  nntp_data->allowed = mod == 'y' || mod == 'm' ? 1 : 0;
++  mutt_str_replace (&nntp_data->desc, desc);
++  if (nntp_data->newsrc_ent || nntp_data->lastCached)
++    nntp_group_unread_stat (nntp_data);
++  else if (nntp_data->lastMessage &&
++         nntp_data->firstMessage <= nntp_data->lastMessage)
++    nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
++  else
++    nntp_data->unread = 0;
++  return 0;
++}
++
++/* Load list of all newsgroups from cache */
++static int active_get_cache (NNTP_SERVER *nserv)
++{
++  char buf[HUGE_STRING];
++  char file[_POSIX_PATH_MAX];
++  time_t t;
++  FILE *fp;
++
++  cache_expand (file, sizeof (file), &nserv->conn->account, ".active");
++  dprint (1, (debugfile, "Parsing %s\n", file));
++  fp = safe_fopen (file, "r");
++  if (!fp)
++    return -1;
++
++  if (fgets (buf, sizeof (buf), fp) == NULL ||
++      sscanf (buf, "%ld%s", &t, file) != 1 || t == 0)
++  {
++    fclose (fp);
++    return -1;
++  }
++  nserv->newgroups_time = t;
++
++  mutt_message _("Loading list of groups from cache...");
++  while (fgets (buf, sizeof (buf), fp))
++    nntp_add_group (buf, nserv);
++  nntp_add_group (NULL, NULL);
++  fclose (fp);
++  mutt_clear_error ();
++  return 0;
++}
++
++/* Save list of all newsgroups to cache */
++int nntp_active_save_cache (NNTP_SERVER *nserv)
++{
++  char file[_POSIX_PATH_MAX];
++  char *buf;
++  size_t buflen, off;
++  unsigned int i;
++  int rc;
++
++  if (!nserv->cacheable)
++    return 0;
++
++  buflen = 10 * LONG_STRING;
++  buf = safe_calloc (1, buflen);
++  snprintf (buf, buflen, "%lu\n", (unsigned long)nserv->newgroups_time);
++  off = strlen (buf);
++
++  for (i = 0; i < nserv->groups_num; i++)
++  {
++    NNTP_DATA *nntp_data = nserv->groups_list[i];
++
++    if (!nntp_data || nntp_data->deleted)
++      continue;
++
++    if (off + strlen (nntp_data->group) +
++      (nntp_data->desc ? strlen (nntp_data->desc) : 0) + 50 > buflen)
++    {
++      buflen *= 2;
++      safe_realloc (&buf, buflen);
++    }
++    snprintf (buf + off, buflen - off, "%s %d %d %c%s%s\n", nntp_data->group,
++            nntp_data->lastMessage, nntp_data->firstMessage,
++            nntp_data->allowed ? 'y' : 'n', nntp_data->desc ? " " : "",
++            nntp_data->desc ? nntp_data->desc : "");
++    off += strlen (buf + off);
++  }
++
++  cache_expand (file, sizeof (file), &nserv->conn->account, ".active");
++  dprint (1, (debugfile, "Updating %s\n", file));
++  rc = update_file (file, buf);
++  FREE (&buf);
++  return rc;
++}
++
++#ifdef USE_HCACHE
++/* Used by mutt_hcache_open() to compose hcache file name */
++static int nntp_hcache_namer (const char *path, char *dest, size_t destlen)
++{
++  return snprintf (dest, destlen, "%s.hcache", path);
++}
++
++/* Open newsgroup hcache */
++header_cache_t *nntp_hcache_open (NNTP_DATA *nntp_data)
++{
++  ciss_url_t url;
++  char file[_POSIX_PATH_MAX];
++
++  if (!nntp_data->nserv || !nntp_data->nserv->cacheable ||
++      !nntp_data->nserv->conn || !nntp_data->group ||
++      !(nntp_data->newsrc_ent || nntp_data->subscribed ||
++      option (OPTSAVEUNSUB)))
++    return NULL;
++
++  mutt_account_tourl (&nntp_data->nserv->conn->account, &url);
++  url.path = nntp_data->group;
++  url_ciss_tostring (&url, file, sizeof (file), U_PATH);
++  return mutt_hcache_open (NewsCacheDir, file, nntp_hcache_namer);
++}
++
++/* Remove stale cached headers */
++void nntp_hcache_update (NNTP_DATA *nntp_data, header_cache_t *hc)
++{
++  char buf[16];
++  int old = 0;
++  void *hdata;
++  anum_t first, last, current;
++
++  if (!hc)
++    return;
++
++  /* fetch previous values of first and last */
++  hdata = mutt_hcache_fetch_raw (hc, "index", strlen);
++  if (hdata)
++  {
++    dprint (2, (debugfile,
++              "nntp_hcache_update: mutt_hcache_fetch index: %s\n", hdata));
++    if (sscanf (hdata, ANUM " " ANUM, &first, &last) == 2)
++    {
++      old = 1;
++      nntp_data->lastCached = last;
++
++      /* clean removed headers from cache */
++      for (current = first; current <= last; current++)
++      {
++      if (current >= nntp_data->firstMessage &&
++          current <= nntp_data->lastMessage)
++        continue;
++
++      snprintf (buf, sizeof (buf), "%d", current);
++      dprint (2, (debugfile,
++                  "nntp_hcache_update: mutt_hcache_delete %s\n", buf));
++      mutt_hcache_delete (hc, buf, strlen);
++      }
++    }
++    mutt_hcache_free (&hdata);
++  }
++
++  /* store current values of first and last */
++  if (!old || nntp_data->firstMessage != first ||
++            nntp_data->lastMessage != last)
++  {
++    snprintf (buf, sizeof (buf), "%u %u", nntp_data->firstMessage,
++                                        nntp_data->lastMessage);
++    dprint (2, (debugfile,
++              "nntp_hcache_update: mutt_hcache_store index: %s\n", buf));
++    mutt_hcache_store_raw (hc, "index", buf, strlen (buf) + 1, strlen);
++  }
++}
++#endif
++
++/* Remove bcache file */
++static int nntp_bcache_delete (const char *id, body_cache_t *bcache, void *data)
++{
++  NNTP_DATA *nntp_data = data;
++  anum_t anum;
++  char c;
++
++  if (!nntp_data || sscanf (id, ANUM "%c", &anum, &c) != 1 ||
++      anum < nntp_data->firstMessage || anum > nntp_data->lastMessage)
++  {
++    if (nntp_data)
++      dprint (2, (debugfile, "nntp_bcache_delete: mutt_bcache_del %s\n", id));
++    mutt_bcache_del (bcache, id);
++  }
++  return 0;
++}
++
++/* Remove stale cached messages */
++void nntp_bcache_update (NNTP_DATA *nntp_data)
++{
++  mutt_bcache_list (nntp_data->bcache, nntp_bcache_delete, nntp_data);
++}
++
++/* Remove hcache and bcache of newsgroup */
++void nntp_delete_group_cache (NNTP_DATA *nntp_data)
++{
++  char file[_POSIX_PATH_MAX];
++
++  if (!nntp_data || !nntp_data->nserv || !nntp_data->nserv->cacheable)
++    return;
++
++#ifdef USE_HCACHE
++  nntp_hcache_namer (nntp_data->group, file, sizeof (file));
++  cache_expand (file, sizeof (file), &nntp_data->nserv->conn->account, file);
++  unlink (file);
++  nntp_data->lastCached = 0;
++  dprint (2, (debugfile, "nntp_delete_group_cache: %s\n", file));
++#endif
++
++  if (!nntp_data->bcache)
++    nntp_data->bcache = mutt_bcache_open (&nntp_data->nserv->conn->account,
++                      nntp_data->group);
++  if (nntp_data->bcache)
++  {
++    dprint (2, (debugfile, "nntp_delete_group_cache: %s/*\n", nntp_data->group));
++    mutt_bcache_list (nntp_data->bcache, nntp_bcache_delete, NULL);
++    mutt_bcache_close (&nntp_data->bcache);
++  }
++}
++
++/* Remove hcache and bcache of all unexistent and unsubscribed newsgroups */
++void nntp_clear_cache (NNTP_SERVER *nserv)
++{
++  char file[_POSIX_PATH_MAX];
++  char *fp;
++  struct dirent *entry;
++  DIR *dp;
++
++  if (!nserv || !nserv->cacheable)
++    return;
++
++  cache_expand (file, sizeof (file), &nserv->conn->account, NULL);
++  dp = opendir (file);
++  if (dp)
++  {
++    safe_strncat (file, sizeof (file), "/", 1);
++    fp = file + strlen (file);
++    while ((entry = readdir (dp)))
++    {
++      char *group = entry->d_name;
++      struct stat sb;
++      NNTP_DATA *nntp_data;
++      NNTP_DATA nntp_tmp;
++
++      if (mutt_strcmp (group, ".") == 0 ||
++        mutt_strcmp (group, "..") == 0)
++      continue;
++      *fp = '\0';
++      safe_strncat (file, sizeof (file), group, strlen (group));
++      if (stat (file, &sb))
++      continue;
++
++#ifdef USE_HCACHE
++      if (S_ISREG (sb.st_mode))
++      {
++      char *ext = group + strlen (group) - 7;
++      if (strlen (group) < 8 || mutt_strcmp (ext, ".hcache"))
++        continue;
++      *ext = '\0';
++      }
++      else
++#endif
++      if (!S_ISDIR (sb.st_mode))
++      continue;
++
++      nntp_data = hash_find (nserv->groups_hash, group);
++      if (!nntp_data)
++      {
++      nntp_data = &nntp_tmp;
++      nntp_data->nserv = nserv;
++      nntp_data->group = group;
++      nntp_data->bcache = NULL;
++      }
++      else if (nntp_data->newsrc_ent || nntp_data->subscribed ||
++             option (OPTSAVEUNSUB))
++      continue;
++
++      nntp_delete_group_cache (nntp_data);
++      if (S_ISDIR (sb.st_mode))
++      {
++      rmdir (file);
++      dprint (2, (debugfile, "nntp_clear_cache: %s\n", file));
++      }
++    }
++    closedir (dp);
++  }
++  return;
++}
++
++/* %a = account url
++ * %p = port
++ * %P = port if specified
++ * %s = news server name
++ * %S = url schema
++ * %u = username */
++const char *
++nntp_format_str (char *dest, size_t destlen, size_t col, int cols, char op,
++              const char *src, const char *fmt, const char *ifstring,
++              const char *elsestring, unsigned long data, format_flag flags)
++{
++  NNTP_SERVER *nserv = (NNTP_SERVER *)data;
++  ACCOUNT *acct = &nserv->conn->account;
++  ciss_url_t url;
++  char fn[SHORT_STRING], tmp[SHORT_STRING], *p;
++
++  switch (op)
++  {
++    case 'a':
++      mutt_account_tourl (acct, &url);
++      url_ciss_tostring (&url, fn, sizeof (fn), U_PATH);
++      p = strchr (fn, '/');
++      if (p)
++      *p = '\0';
++      snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
++      snprintf (dest, destlen, tmp, fn);
++      break;
++    case 'p':
++      snprintf (tmp, sizeof (tmp), "%%%su", fmt);
++      snprintf (dest, destlen, tmp, acct->port);
++      break;
++    case 'P':
++      *dest = '\0';
++      if (acct->flags & MUTT_ACCT_PORT)
++      {
++      snprintf (tmp, sizeof (tmp), "%%%su", fmt);
++      snprintf (dest, destlen, tmp, acct->port);
++      }
++      break;
++    case 's':
++      strncpy (fn, acct->host, sizeof (fn) - 1);
++      mutt_strlower (fn);
++      snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
++      snprintf (dest, destlen, tmp, fn);
++      break;
++    case 'S':
++      mutt_account_tourl (acct, &url);
++      url_ciss_tostring (&url, fn, sizeof (fn), U_PATH);
++      p = strchr (fn, ':');
++      if (p)
++      *p = '\0';
++      snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
++      snprintf (dest, destlen, tmp, fn);
++      break;
++    case 'u':
++      snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
++      snprintf (dest, destlen, tmp, acct->user);
++      break;
++  }
++  return (src);
++}
++
++/* Automatically loads a newsrc into memory, if necessary.
++ * Checks the size/mtime of a newsrc file, if it doesn't match, load
++ * again.  Hmm, if a system has broken mtimes, this might mean the file
++ * is reloaded every time, which we'd have to fix. */
++NNTP_SERVER *nntp_select_server (char *server, int leave_lock)
++{
++  char file[_POSIX_PATH_MAX];
++  char *p;
++  int rc;
++  struct stat sb;
++  ACCOUNT acct;
++  NNTP_SERVER *nserv;
++  NNTP_DATA *nntp_data;
++  CONNECTION *conn;
++  ciss_url_t url;
++
++  if (!server || !*server)
++  {
++    mutt_error _("No news server defined!");
++    mutt_sleep (2);
++    return NULL;
++  }
++
++  /* create account from news server url */
++  acct.flags = 0;
++  acct.port = NNTP_PORT;
++  acct.type = MUTT_ACCT_TYPE_NNTP;
++  snprintf (file, sizeof (file), "%s%s",
++          strstr (server, "://") ? "" : "news://", server);
++  if (url_parse_ciss (&url, file) < 0 ||
++      (url.path && *url.path) ||
++      !(url.scheme == U_NNTP || url.scheme == U_NNTPS) ||
++      mutt_account_fromurl (&acct, &url) < 0)
++  {
++    mutt_error (_("%s is an invalid news server specification!"), server);
++    mutt_sleep (2);
++    return NULL;
++  }
++  if (url.scheme == U_NNTPS)
++  {
++    acct.flags |= MUTT_ACCT_SSL;
++    acct.port = NNTP_SSL_PORT;
++  }
++
++  /* find connection by account */
++  conn = mutt_conn_find (NULL, &acct);
++  if (!conn)
++    return NULL;
++  if (!(conn->account.flags & MUTT_ACCT_USER) && acct.flags & MUTT_ACCT_USER)
++  {
++    conn->account.flags |= MUTT_ACCT_USER;
++    conn->account.user[0] = '\0';
++  }
++
++  /* news server already exists */
++  nserv = conn->data;
++  if (nserv)
++  {
++    if (nserv->status == NNTP_BYE)
++      nserv->status = NNTP_NONE;
++    if (nntp_open_connection (nserv) < 0)
++      return NULL;
++
++    rc = nntp_newsrc_parse (nserv);
++    if (rc < 0)
++      return NULL;
++
++    /* check for new newsgroups */
++    if (!leave_lock && nntp_check_new_groups (nserv) < 0)
++      rc = -1;
++
++    /* .newsrc has been externally modified */
++    if (rc > 0)
++      nntp_clear_cache (nserv);
++    if (rc < 0 || !leave_lock)
++      nntp_newsrc_close (nserv);
++    return rc < 0 ? NULL : nserv;
++  }
++
++  /* new news server */
++  nserv = safe_calloc (1, sizeof (NNTP_SERVER));
++  nserv->conn = conn;
++  nserv->groups_hash = hash_create (1009, 0);
++  nserv->groups_max = 16;
++  nserv->groups_list = safe_malloc (nserv->groups_max * sizeof (nntp_data));
++
++  rc = nntp_open_connection (nserv);
++
++  /* try to create cache directory and enable caching */
++  nserv->cacheable = 0;
++  if (rc >= 0 && NewsCacheDir && *NewsCacheDir)
++  {
++    cache_expand (file, sizeof (file), &conn->account, NULL);
++    p = *file == '/' ? file + 1 : file;
++    while (1)
++    {
++      p = strchr (p, '/');
++      if (p)
++      *p = '\0';
++      if ((stat (file, &sb) || (sb.st_mode & S_IFDIR) == 0) &&
++        mkdir (file, 0700))
++      {
++      mutt_error (_("Can't create %s: %s."), file, strerror (errno));
++      mutt_sleep (2);
++      break;
++      }
++      if (!p)
++      {
++      nserv->cacheable = 1;
++      break;
++      }
++      *p++ = '/';
++    }
++  }
++
++  /* load .newsrc */
++  if (rc >= 0)
++  {
++    mutt_FormatString (file, sizeof (file), 0, sizeof (file), NONULL (NewsRc),
++                     nntp_format_str, (unsigned long)nserv, 0);
++    mutt_expand_path (file, sizeof (file));
++    nserv->newsrc_file = safe_strdup (file);
++    rc = nntp_newsrc_parse (nserv);
++  }
++  if (rc >= 0)
++  {
++    /* try to load list of newsgroups from cache */
++    if (nserv->cacheable && active_get_cache (nserv) == 0)
++      rc = nntp_check_new_groups (nserv);
++
++    /* load list of newsgroups from server */
++    else
++      rc = nntp_active_fetch (nserv, 0);
++  }
++
++  if (rc >= 0)
++    nntp_clear_cache (nserv);
++
++#ifdef USE_HCACHE
++  /* check cache files */
++  if (rc >= 0 && nserv->cacheable)
++  {
++    struct dirent *entry;
++    DIR *dp = opendir (file);
++
++    if (dp)
++    {
++      while ((entry = readdir (dp)))
++      {
++      header_cache_t *hc;
++      void *hdata;
++      char *group = entry->d_name;
++
++      p = group + strlen (group) - 7;
++      if (strlen (group) < 8 || strcmp (p, ".hcache"))
++        continue;
++      *p = '\0';
++      nntp_data = hash_find (nserv->groups_hash, group);
++      if (!nntp_data)
++        continue;
++
++      hc = nntp_hcache_open (nntp_data);
++      if (!hc)
++        continue;
++
++      /* fetch previous values of first and last */
++      hdata = mutt_hcache_fetch_raw (hc, "index", strlen);
++      if (hdata)
++      {
++        anum_t first, last;
++
++        if (sscanf (hdata, ANUM " " ANUM, &first, &last) == 2)
++        {
++          if (nntp_data->deleted)
++          {
++            nntp_data->firstMessage = first;
++            nntp_data->lastMessage = last;
++          }
++          if (last >= nntp_data->firstMessage &&
++              last <= nntp_data->lastMessage)
++          {
++            nntp_data->lastCached = last;
++            dprint (2, (debugfile, "nntp_select_server: %s lastCached=%u\n",
++                        nntp_data->group, last));
++          }
++        }
++        mutt_hcache_free (&hdata);
++      }
++      mutt_hcache_close (hc);
++      }
++      closedir (dp);
++    }
++  }
++#endif
++
++  if (rc < 0 || !leave_lock)
++    nntp_newsrc_close (nserv);
++
++  if (rc < 0)
++  {
++    hash_destroy (&nserv->groups_hash, nntp_data_free);
++    FREE (&nserv->groups_list);
++    FREE (&nserv->newsrc_file);
++    FREE (&nserv->authenticators);
++    FREE (&nserv);
++    mutt_socket_close (conn);
++    mutt_socket_free (conn);
++    return NULL;
++  }
++
++  conn->data = nserv;
++  return nserv;
++}
++
++/* Full status flags are not supported by nntp, but we can fake some of them:
++ * Read = a read message number is in the .newsrc
++ * New = not read and not cached
++ * Old = not read but cached */
++void nntp_article_status (CONTEXT *ctx, HEADER *hdr, char *group, anum_t anum)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  unsigned int i;
++
++  if (group)
++    nntp_data = hash_find (nntp_data->nserv->groups_hash, group);
++
++  if (!nntp_data)
++    return;
++
++  for (i = 0; i < nntp_data->newsrc_len; i++)
++  {
++    if ((anum >= nntp_data->newsrc_ent[i].first) &&
++      (anum <= nntp_data->newsrc_ent[i].last))
++    {
++      /* can't use mutt_set_flag() because mx_update_context()
++       didn't called yet */
++      hdr->read = 1;
++      return;
++    }
++  }
++
++  /* article was not cached yet, it's new */
++  if (anum > nntp_data->lastCached)
++    return;
++
++  /* article isn't read but cached, it's old */
++  if (option (OPTMARKOLD))
++    hdr->old = 1;
++}
++
++/* calculate number of unread articles using .newsrc data */
++void nntp_group_unread_stat (NNTP_DATA *nntp_data)
++{
++  unsigned int i;
++  anum_t first, last;
++
++  nntp_data->unread = 0;
++  if (nntp_data->lastMessage == 0 ||
++      nntp_data->firstMessage > nntp_data->lastMessage)
++    return;
++
++  nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
++  for (i = 0; i < nntp_data->newsrc_len; i++)
++  {
++    first = nntp_data->newsrc_ent[i].first;
++    if (first < nntp_data->firstMessage)
++      first = nntp_data->firstMessage;
++    last = nntp_data->newsrc_ent[i].last;
++    if (last > nntp_data->lastMessage)
++      last = nntp_data->lastMessage;
++    if (first <= last)
++      nntp_data->unread -= last - first + 1;
++  }
++}
++
++/* Subscribe newsgroup */
++NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *nserv, char *group)
++{
++  NNTP_DATA *nntp_data;
++
++  if (!nserv || !nserv->groups_hash || !group || !*group)
++    return NULL;
++
++  nntp_data = nntp_data_find (nserv, group);
++  nntp_data->subscribed = 1;
++  if (!nntp_data->newsrc_ent)
++  {
++    nntp_data->newsrc_ent = safe_calloc (1, sizeof (NEWSRC_ENTRY));
++    nntp_data->newsrc_len = 1;
++    nntp_data->newsrc_ent[0].first = 1;
++    nntp_data->newsrc_ent[0].last = 0;
++  }
++  return nntp_data;
++}
++
++/* Unsubscribe newsgroup */
++NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *nserv, char *group)
++{
++  NNTP_DATA *nntp_data;
++
++  if (!nserv || !nserv->groups_hash || !group || !*group)
++    return NULL;
++
++  nntp_data = hash_find (nserv->groups_hash, group);
++  if (!nntp_data)
++    return NULL;
++
++  nntp_data->subscribed = 0;
++  if (!option (OPTSAVEUNSUB))
++  {
++    nntp_data->newsrc_len = 0;
++    FREE (&nntp_data->newsrc_ent);
++  }
++  return nntp_data;
++}
++
++/* Catchup newsgroup */
++NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *nserv, char *group)
++{
++  NNTP_DATA *nntp_data;
++
++  if (!nserv || !nserv->groups_hash || !group || !*group)
++    return NULL;
++
++  nntp_data = hash_find (nserv->groups_hash, group);
++  if (!nntp_data)
++    return NULL;
++
++  if (nntp_data->newsrc_ent)
++  {
++    safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
++    nntp_data->newsrc_len = 1;
++    nntp_data->newsrc_ent[0].first = 1;
++    nntp_data->newsrc_ent[0].last = nntp_data->lastMessage;
++  }
++  nntp_data->unread = 0;
++  if (Context && Context->data == nntp_data)
++  {
++    unsigned int i;
++
++    for (i = 0; i < Context->msgcount; i++)
++      mutt_set_flag (Context, Context->hdrs[i], MUTT_READ, 1);
++  }
++  return nntp_data;
++}
++
++/* Uncatchup newsgroup */
++NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *nserv, char *group)
++{
++  NNTP_DATA *nntp_data;
++
++  if (!nserv || !nserv->groups_hash || !group || !*group)
++    return NULL;
++
++  nntp_data = hash_find (nserv->groups_hash, group);
++  if (!nntp_data)
++    return NULL;
++
++  if (nntp_data->newsrc_ent)
++  {
++    safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
++    nntp_data->newsrc_len = 1;
++    nntp_data->newsrc_ent[0].first = 1;
++    nntp_data->newsrc_ent[0].last = nntp_data->firstMessage - 1;
++  }
++  if (Context && Context->data == nntp_data)
++  {
++    unsigned int i;
++
++    nntp_data->unread = Context->msgcount;
++    for (i = 0; i < Context->msgcount; i++)
++      mutt_set_flag (Context, Context->hdrs[i], MUTT_READ, 0);
++  }
++  else
++    nntp_data->unread = nntp_data->lastMessage - nntp_data->newsrc_ent[0].last;
++  return nntp_data;
++}
++
++/* Get first newsgroup with new messages */
++void nntp_buffer_buffy (BUFFER *s)
++{
++  unsigned int i;
++
++  for (i = 0; i < CurrentNewsSrv->groups_num; i++)
++  {
++    NNTP_DATA *nntp_data = CurrentNewsSrv->groups_list[i];
++
++    if (!nntp_data || !nntp_data->subscribed || !nntp_data->unread)
++      continue;
++
++    if (Context && Context->magic == MUTT_NNTP &&
++      !mutt_strcmp (nntp_data->group, ((NNTP_DATA *)Context->data)->group))
++    {
++      unsigned int i, unread = 0;
++
++      for (i = 0; i < Context->msgcount; i++)
++      if (!Context->hdrs[i]->read && !Context->hdrs[i]->deleted)
++        unread++;
++      if (!unread)
++      continue;
++    }
++    mutt_buffer_strcpy (s, nntp_data->group);
++    return;
++  }
++  mutt_buffer_clear (s);
++}
+diff -udprP mutt-1.12.1.orig/nntp.c mutt-1.12.1/nntp.c
+--- mutt-1.12.1.orig/nntp.c    1970-01-01 03:00:00.000000000 +0300
++++ mutt-1.12.1/nntp.c 2019-08-11 20:31:26.142940552 +0300
+@@ -0,0 +1,2486 @@
++/*
++ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
++ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
++ * Copyright (C) 2000-2017 Vsevolod Volkov <vvv@mutt.org.ua>
++ *
++ *     This program is free software; you can redistribute it and/or modify
++ *     it under the terms of the GNU General Public License as published by
++ *     the Free Software Foundation; either version 2 of the License, or
++ *     (at your option) any later version.
++ *
++ *     This program is distributed in the hope that it will be useful,
++ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *     GNU General Public License for more details.
++ *
++ *     You should have received a copy of the GNU General Public License
++ *     along with this program; if not, write to the Free Software
++ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#if HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include "mutt.h"
++#include "mutt_curses.h"
++#include "sort.h"
++#include "mx.h"
++#include "mime.h"
++#include "rfc1524.h"
++#include "rfc2047.h"
++#include "mailbox.h"
++#include "mutt_crypt.h"
++#include "nntp.h"
++
++#if defined(USE_SSL)
++#include "mutt_ssl.h"
++#endif
++
++#ifdef HAVE_PGP
++#include "pgp.h"
++#endif
++
++#ifdef HAVE_SMIME
++#include "smime.h"
++#endif
++
++#if USE_HCACHE
++#include "hcache.h"
++#endif
++
++#include <unistd.h>
++#include <string.h>
++#include <ctype.h>
++#include <stdlib.h>
++
++#ifdef USE_SASL
++#include <sasl/sasl.h>
++#include <sasl/saslutil.h>
++
++#include "mutt_sasl.h"
++#endif
++
++static int nntp_connect_error (NNTP_SERVER *nserv)
++{
++  nserv->status = NNTP_NONE;
++  mutt_error _("Server closed connection!");
++  mutt_sleep (2);
++  return -1;
++}
++
++/* Get capabilities:
++ * -1 - error, connection is closed
++ *  0 - mode is reader, capabilities setted up
++ *  1 - need to switch to reader mode */
++static int nntp_capabilities (NNTP_SERVER *nserv)
++{
++  CONNECTION *conn = nserv->conn;
++  unsigned int mode_reader = 0;
++  char buf[LONG_STRING];
++  char authinfo[LONG_STRING] = "";
++
++  nserv->hasCAPABILITIES = 0;
++  nserv->hasSTARTTLS = 0;
++  nserv->hasDATE = 0;
++  nserv->hasLIST_NEWSGROUPS = 0;
++  nserv->hasLISTGROUP = 0;
++  nserv->hasLISTGROUPrange = 0;
++  nserv->hasOVER = 0;
++  FREE (&nserv->authenticators);
++
++  if (mutt_socket_write (conn, "CAPABILITIES\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++    return nntp_connect_error (nserv);
++
++  /* no capabilities */
++  if (mutt_strncmp ("101", buf, 3))
++    return 1;
++  nserv->hasCAPABILITIES = 1;
++
++  /* parse capabilities */
++  do
++  {
++    if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++    if (!mutt_strcmp ("STARTTLS", buf))
++      nserv->hasSTARTTLS = 1;
++    else if (!mutt_strcmp ("MODE-READER", buf))
++      mode_reader = 1;
++    else if (!mutt_strcmp ("READER", buf))
++    {
++      nserv->hasDATE = 1;
++      nserv->hasLISTGROUP = 1;
++      nserv->hasLISTGROUPrange = 1;
++    }
++    else if (!mutt_strncmp ("AUTHINFO ", buf, 9))
++    {
++      safe_strcat (buf, sizeof (buf), " ");
++      strfcpy (authinfo, buf + 8, sizeof (authinfo));
++    }
++#ifdef USE_SASL
++    else if (!mutt_strncmp ("SASL ", buf, 5))
++    {
++      char *p = buf + 5;
++      while (*p == ' ')
++      p++;
++      nserv->authenticators = safe_strdup (p);
++    }
++#endif
++    else if (!mutt_strcmp ("OVER", buf))
++      nserv->hasOVER = 1;
++    else if (!mutt_strncmp ("LIST ", buf, 5))
++    {
++      char *p = strstr (buf, " NEWSGROUPS");
++      if (p)
++      {
++      p += 11;
++      if (*p == '\0' || *p == ' ')
++        nserv->hasLIST_NEWSGROUPS = 1;
++      }
++    }
++  } while (mutt_strcmp (".", buf));
++  *buf = '\0';
++#ifdef USE_SASL
++  if (nserv->authenticators && strcasestr (authinfo, " SASL "))
++    strfcpy (buf, nserv->authenticators, sizeof (buf));
++#endif
++  if (strcasestr (authinfo, " USER "))
++  {
++    if (*buf)
++      safe_strcat (buf, sizeof (buf), " ");
++    safe_strcat (buf, sizeof (buf), "USER");
++  }
++  mutt_str_replace (&nserv->authenticators, buf);
++
++  /* current mode is reader */
++  if (nserv->hasDATE)
++    return 0;
++
++  /* server is mode-switching, need to switch to reader mode */
++  if (mode_reader)
++    return 1;
++
++  mutt_socket_close (conn);
++  nserv->status = NNTP_BYE;
++  mutt_error _("Server doesn't support reader mode.");
++  mutt_sleep (2);
++  return -1;
++}
++
++char *OverviewFmt =
++      "Subject:\0"
++      "From:\0"
++      "Date:\0"
++      "Message-ID:\0"
++      "References:\0"
++      "Content-Length:\0"
++      "Lines:\0"
++      "\0";
++
++/* Detect supported commands */
++static int nntp_attempt_features (NNTP_SERVER *nserv)
++{
++  CONNECTION *conn = nserv->conn;
++  char buf[LONG_STRING];
++
++  /* no CAPABILITIES, trying DATE, LISTGROUP, LIST NEWSGROUPS */
++  if (!nserv->hasCAPABILITIES)
++  {
++    if (mutt_socket_write (conn, "DATE\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++    if (mutt_strncmp ("500", buf, 3))
++      nserv->hasDATE = 1;
++
++    if (mutt_socket_write (conn, "LISTGROUP\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++    if (mutt_strncmp ("500", buf, 3))
++      nserv->hasLISTGROUP = 1;
++
++    if (mutt_socket_write (conn, "LIST NEWSGROUPS +\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++    if (mutt_strncmp ("500", buf, 3))
++      nserv->hasLIST_NEWSGROUPS = 1;
++    if (!mutt_strncmp ("215", buf, 3))
++    {
++      do
++      {
++      if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++        return nntp_connect_error (nserv);
++      } while (mutt_strcmp (".", buf));
++    }
++  }
++
++  /* no LIST NEWSGROUPS, trying XGTITLE */
++  if (!nserv->hasLIST_NEWSGROUPS)
++  {
++    if (mutt_socket_write (conn, "XGTITLE\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++    if (mutt_strncmp ("500", buf, 3))
++      nserv->hasXGTITLE = 1;
++  }
++
++  /* no OVER, trying XOVER */
++  if (!nserv->hasOVER)
++  {
++    if (mutt_socket_write (conn, "XOVER\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++    if (mutt_strncmp ("500", buf, 3))
++      nserv->hasXOVER = 1;
++  }
++
++  /* trying LIST OVERVIEW.FMT */
++  if (nserv->hasOVER || nserv->hasXOVER)
++  {
++    if (mutt_socket_write (conn, "LIST OVERVIEW.FMT\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++    if (mutt_strncmp ("215", buf, 3))
++      nserv->overview_fmt = OverviewFmt;
++    else
++    {
++      int chunk, cont = 0;
++      size_t buflen = 2 * LONG_STRING, off = 0, b = 0;
++
++      if (nserv->overview_fmt)
++      FREE (&nserv->overview_fmt);
++      nserv->overview_fmt = safe_malloc (buflen);
++
++      while (1)
++      {
++      if (buflen - off < LONG_STRING)
++      {
++        buflen *= 2;
++        safe_realloc (&nserv->overview_fmt, buflen);
++      }
++
++      chunk = mutt_socket_readln (nserv->overview_fmt + off,
++                                  buflen - off, conn);
++      if (chunk < 0)
++      {
++        FREE (&nserv->overview_fmt);
++        return nntp_connect_error (nserv);
++      }
++
++      if (!cont && !mutt_strcmp (".", nserv->overview_fmt + off))
++        break;
++
++      cont = chunk >= buflen - off ? 1 : 0;
++      off += strlen (nserv->overview_fmt + off);
++      if (!cont)
++      {
++        char *colon;
++
++        if (nserv->overview_fmt[b] == ':')
++        {
++          memmove (nserv->overview_fmt + b,
++                   nserv->overview_fmt + b + 1, off - b - 1);
++          nserv->overview_fmt[off - 1] = ':';
++        }
++        colon = strchr (nserv->overview_fmt + b, ':');
++        if (!colon)
++          nserv->overview_fmt[off++] = ':';
++        else if (strcmp (colon + 1, "full"))
++          off = colon + 1 - nserv->overview_fmt;
++        if (!strcasecmp (nserv->overview_fmt + b, "Bytes:"))
++        {
++          strcpy (nserv->overview_fmt + b, "Content-Length:");
++          off = b + strlen (nserv->overview_fmt + b);
++        }
++        nserv->overview_fmt[off++] = '\0';
++        b = off;
++      }
++      }
++      nserv->overview_fmt[off++] = '\0';
++      safe_realloc (&nserv->overview_fmt, off);
++    }
++  }
++  return 0;
++}
++
++/* Get login, password and authenticate */
++static int nntp_auth (NNTP_SERVER *nserv)
++{
++  CONNECTION *conn = nserv->conn;
++  char buf[LONG_STRING];
++  char authenticators[LONG_STRING] = "USER";
++  char *method, *a, *p;
++  unsigned char flags = conn->account.flags;
++
++  while (1)
++  {
++    /* get login and password */
++    if (mutt_account_getuser (&conn->account) || !conn->account.user[0] ||
++      mutt_account_getpass (&conn->account) || !conn->account.pass[0])
++      break;
++
++    /* get list of authenticators */
++    if (NntpAuthenticators && *NntpAuthenticators)
++      strfcpy (authenticators, NntpAuthenticators, sizeof (authenticators));
++    else if (nserv->hasCAPABILITIES)
++    {
++      strfcpy (authenticators, NONULL (nserv->authenticators),
++             sizeof (authenticators));
++      p = authenticators;
++      while (*p)
++      {
++      if (*p == ' ')
++        *p = ':';
++      p++;
++      }
++    }
++    p = authenticators;
++    while (*p)
++    {
++      *p = ascii_toupper (*p);
++      p++;
++    }
++
++    dprint (1, (debugfile,
++              "nntp_auth: available methods: %s\n", nserv->authenticators));
++    a = authenticators;
++    while (1)
++    {
++      if (!a)
++      {
++      mutt_error _("No authenticators available");
++      mutt_sleep (2);
++      break;
++      }
++
++      method = a;
++      a = strchr (a, ':');
++      if (a)
++      *a++ = '\0';
++
++      /* check authenticator */
++      if (nserv->hasCAPABILITIES)
++      {
++      char *m;
++
++      if (!nserv->authenticators)
++        continue;
++      m = strcasestr (nserv->authenticators, method);
++      if (!m)
++        continue;
++      if (m > nserv->authenticators && *(m - 1) != ' ')
++        continue;
++      m += strlen (method);
++      if (*m != '\0' && *m != ' ')
++        continue;
++      }
++      dprint (1, (debugfile, "nntp_auth: trying method %s\n", method));
++
++      /* AUTHINFO USER authentication */
++      if (!strcmp (method, "USER"))
++      {
++      mutt_message (_("Authenticating (%s)..."), method);
++      snprintf (buf, sizeof (buf), "AUTHINFO USER %s\r\n", conn->account.user);
++      if (mutt_socket_write (conn, buf) < 0 ||
++          mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++        break;
++
++      /* authenticated, password is not required */
++      if (!mutt_strncmp ("281", buf, 3))
++        return 0;
++
++      /* username accepted, sending password */
++      if (!mutt_strncmp ("381", buf, 3))
++      {
++#ifdef DEBUG
++        if (debuglevel < MUTT_SOCK_LOG_FULL)
++          dprint (MUTT_SOCK_LOG_CMD, (debugfile,
++                  "%d> AUTHINFO PASS *\n", conn->fd));
++#endif
++        snprintf (buf, sizeof (buf), "AUTHINFO PASS %s\r\n",
++                  conn->account.pass);
++        if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) < 0 ||
++            mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++        break;
++
++        /* authenticated */
++        if (!mutt_strncmp ("281", buf, 3))
++          return 0;
++      }
++
++      /* server doesn't support AUTHINFO USER, trying next method */
++      if (*buf == '5')
++        continue;
++      }
++
++      else
++      {
++#ifdef USE_SASL
++      sasl_conn_t *saslconn;
++      sasl_interact_t *interaction = NULL;
++      int rc;
++      char inbuf[LONG_STRING] = "";
++      const char *mech;
++      const char *client_out = NULL;
++      unsigned int client_len, len;
++
++      if (mutt_sasl_client_new (conn, &saslconn) < 0)
++      {
++        dprint (1, (debugfile,
++                "nntp_auth: error allocating SASL connection.\n"));
++        continue;
++      }
++
++      while (1)
++      {
++        rc = sasl_client_start (saslconn, method, &interaction,
++                                &client_out, &client_len, &mech);
++        if (rc != SASL_INTERACT)
++          break;
++        mutt_sasl_interact (interaction);
++      }
++      if (rc != SASL_OK && rc != SASL_CONTINUE)
++      {
++        sasl_dispose (&saslconn);
++        dprint (1, (debugfile,
++                "nntp_auth: error starting SASL authentication exchange.\n"));
++        continue;
++      }
++
++      mutt_message (_("Authenticating (%s)..."), method);
++      snprintf (buf, sizeof (buf), "AUTHINFO SASL %s", method);
++
++      /* looping protocol */
++      while (rc == SASL_CONTINUE || (rc == SASL_OK && client_len))
++      {
++        /* send out client response */
++        if (client_len)
++        {
++#ifdef DEBUG
++          if (debuglevel >= MUTT_SOCK_LOG_FULL)
++          {
++            char tmp[LONG_STRING];
++            memcpy (tmp, client_out, client_len);
++            for (p = tmp; p < tmp + client_len; p++)
++            {
++              if (*p == '\0')
++                *p = '.';
++            }
++            *p = '\0';
++            dprint (1, (debugfile, "SASL> %s\n", tmp));
++          }
++#endif
++
++          if (*buf)
++            safe_strcat (buf, sizeof (buf), " ");
++          len = strlen (buf);
++          if (sasl_encode64 (client_out, client_len,
++              buf + len, sizeof (buf) - len, &len) != SASL_OK)
++          {
++            dprint (1, (debugfile,
++                    "nntp_auth: error base64-encoding client response.\n"));
++            break;
++          }
++        }
++
++        safe_strcat (buf, sizeof (buf), "\r\n");
++#ifdef DEBUG
++        if (debuglevel < MUTT_SOCK_LOG_FULL)
++        {
++          if (strchr (buf, ' '))
++            dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d> AUTHINFO SASL %s%s\n",
++                    conn->fd, method, client_len ? " sasl_data" : ""));
++          else
++            dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d> sasl_data\n", conn->fd));
++        }
++#endif
++        client_len = 0;
++        if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) < 0 ||
++            mutt_socket_readln_d (inbuf, sizeof (inbuf), conn, MUTT_SOCK_LOG_FULL) < 0)
++          break;
++        if (mutt_strncmp (inbuf, "283 ", 4) &&
++            mutt_strncmp (inbuf, "383 ", 4))
++        {
++#ifdef DEBUG
++          if (debuglevel < MUTT_SOCK_LOG_FULL)
++            dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d< %s\n", conn->fd, inbuf));
++#endif
++          break;
++        }
++#ifdef DEBUG
++        if (debuglevel < MUTT_SOCK_LOG_FULL)
++        {
++          inbuf[3] = '\0';
++          dprint (MUTT_SOCK_LOG_CMD, (debugfile,
++                  "%d< %s sasl_data\n", conn->fd, inbuf));
++        }
++#endif
++
++        if (!strcmp ("=", inbuf + 4))
++          len = 0;
++        else if (sasl_decode64 (inbuf + 4, strlen (inbuf + 4),
++                 buf, sizeof (buf) - 1, &len) != SASL_OK)
++        {
++          dprint (1, (debugfile,
++                  "nntp_auth: error base64-decoding server response.\n"));
++          break;
++        }
++#ifdef DEBUG
++        else if (debuglevel >= MUTT_SOCK_LOG_FULL)
++        {
++          char tmp[LONG_STRING];
++          memcpy (tmp, buf, len);
++          for (p = tmp; p < tmp + len; p++)
++          {
++            if (*p == '\0')
++              *p = '.';
++          }
++          *p = '\0';
++          dprint (1, (debugfile, "SASL< %s\n", tmp));
++        }
++#endif
++
++        while (1)
++        {
++          rc = sasl_client_step (saslconn, buf, len,
++                                 &interaction, &client_out, &client_len);
++          if (rc != SASL_INTERACT)
++            break;
++          mutt_sasl_interact (interaction);
++        }
++        if (*inbuf != '3')
++          break;
++
++        *buf = '\0';
++      } /* looping protocol */
++
++      if (rc == SASL_OK && client_len == 0 && *inbuf == '2')
++      {
++        mutt_sasl_setup_conn (conn, saslconn);
++        return 0;
++      }
++
++      /* terminate SASL sessoin */
++      sasl_dispose (&saslconn);
++      if (conn->fd < 0)
++        break;
++      if (!mutt_strncmp (inbuf, "383 ", 4))
++      {
++        if (mutt_socket_write (conn, "*\r\n") < 0 ||
++            mutt_socket_readln (inbuf, sizeof (inbuf), conn) < 0)
++          break;
++      }
++
++      /* server doesn't support AUTHINFO SASL, trying next method */
++      if (*inbuf == '5')
++        continue;
++#else
++      continue;
++#endif /* USE_SASL */
++      }
++
++      mutt_error (_("%s authentication failed."), method);
++      mutt_sleep (2);
++      break;
++    }
++    break;
++  }
++
++  /* error */
++  nserv->status = NNTP_BYE;
++  conn->account.flags = flags;
++  if (conn->fd < 0)
++  {
++    mutt_error _("Server closed connection!");
++    mutt_sleep (2);
++  }
++  else
++    mutt_socket_close (conn);
++  return -1;
++}
++
++/* Connect to server, authenticate and get capabilities */
++int nntp_open_connection (NNTP_SERVER *nserv)
++{
++  CONNECTION *conn = nserv->conn;
++  char buf[STRING];
++  int cap;
++  unsigned int posting = 0, auth = 1;
++
++  if (nserv->status == NNTP_OK)
++    return 0;
++  if (nserv->status == NNTP_BYE)
++    return -1;
++  nserv->status = NNTP_NONE;
++
++  if (mutt_socket_open (conn) < 0)
++    return -1;
++
++  if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++    return nntp_connect_error (nserv);
++
++  if (!mutt_strncmp ("200", buf, 3))
++    posting = 1;
++  else if (mutt_strncmp ("201", buf, 3))
++  {
++    mutt_socket_close (conn);
++    mutt_remove_trailing_ws (buf);
++    mutt_error ("%s", buf);
++    mutt_sleep (2);
++    return -1;
++  }
++
++  /* get initial capabilities */
++  cap = nntp_capabilities (nserv);
++  if (cap < 0)
++    return -1;
++
++  /* tell news server to switch to mode reader if it isn't so */
++  if (cap > 0)
++  {
++    if (mutt_socket_write (conn, "MODE READER\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++
++    if (!mutt_strncmp ("200", buf, 3))
++      posting = 1;
++    else if (!mutt_strncmp ("201", buf, 3))
++      posting = 0;
++    /* error if has capabilities, ignore result if no capabilities */
++    else if (nserv->hasCAPABILITIES)
++    {
++      mutt_socket_close (conn);
++      mutt_error _("Could not switch to reader mode.");
++      mutt_sleep (2);
++      return -1;
++    }
++
++    /* recheck capabilities after MODE READER */
++    if (nserv->hasCAPABILITIES)
++    {
++      cap = nntp_capabilities (nserv);
++      if (cap < 0)
++      return -1;
++    }
++  }
++
++  mutt_message (_("Connected to %s. %s"), conn->account.host,
++              posting ? _("Posting is ok.") : _("Posting is NOT ok."));
++  mutt_sleep (1);
++
++#if defined(USE_SSL)
++  /* Attempt STARTTLS if available and desired. */
++  if (nserv->use_tls != 1 && (nserv->hasSTARTTLS || option (OPTSSLFORCETLS)))
++  {
++    if (nserv->use_tls == 0)
++      nserv->use_tls = option (OPTSSLFORCETLS) ||
++                      query_quadoption (OPT_SSLSTARTTLS,
++                      _("Secure connection with TLS?")) == MUTT_YES ? 2 : 1;
++    if (nserv->use_tls == 2)
++    {
++      if (mutt_socket_write (conn, "STARTTLS\r\n") < 0 ||
++        mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++      if (mutt_strncmp ("382", buf, 3))
++      {
++      nserv->use_tls = 0;
++      mutt_error ("STARTTLS: %s", buf);
++      mutt_sleep (2);
++      }
++      else if (mutt_ssl_starttls (conn))
++      {
++      nserv->use_tls = 0;
++      nserv->status = NNTP_NONE;
++      mutt_socket_close (nserv->conn);
++      mutt_error _("Could not negotiate TLS connection");
++      mutt_sleep (2);
++      return -1;
++      }
++      else
++      {
++      /* recheck capabilities after STARTTLS */
++      cap = nntp_capabilities (nserv);
++      if (cap < 0)
++        return -1;
++      }
++    }
++  }
++#endif
++
++  /* authentication required? */
++  if (conn->account.flags & MUTT_ACCT_USER)
++  {
++    if (!conn->account.user[0])
++      auth = 0;
++  }
++  else
++  {
++    if (mutt_socket_write (conn, "STAT\r\n") < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++      return nntp_connect_error (nserv);
++    if (mutt_strncmp ("480", buf, 3))
++      auth = 0;
++  }
++
++  /* authenticate */
++  if (auth && nntp_auth (nserv) < 0)
++      return -1;
++
++  /* get final capabilities after authentication */
++  if (nserv->hasCAPABILITIES && (auth || cap > 0))
++  {
++    cap = nntp_capabilities (nserv);
++    if (cap < 0)
++      return -1;
++    if (cap > 0)
++    {
++      mutt_socket_close (conn);
++      mutt_error _("Could not switch to reader mode.");
++      mutt_sleep (2);
++      return -1;
++    }
++  }
++
++  /* attempt features */
++  if (nntp_attempt_features (nserv) < 0)
++    return -1;
++
++  nserv->status = NNTP_OK;
++  return 0;
++}
++
++/* Send data from buffer and receive answer to same buffer */
++static int nntp_query (NNTP_DATA *nntp_data, char *line, size_t linelen)
++{
++  NNTP_SERVER *nserv = nntp_data->nserv;
++  char buf[LONG_STRING];
++
++  if (nserv->status == NNTP_BYE)
++    return -1;
++
++  while (1)
++  {
++    if (nserv->status == NNTP_OK)
++    {
++      int rc = 0;
++
++      if (*line)
++      rc = mutt_socket_write (nserv->conn, line);
++      else if (nntp_data->group)
++      {
++      snprintf (buf, sizeof (buf), "GROUP %s\r\n", nntp_data->group);
++      rc = mutt_socket_write (nserv->conn, buf);
++      }
++      if (rc >= 0)
++      rc = mutt_socket_readln (buf, sizeof (buf), nserv->conn);
++      if (rc >= 0)
++      break;
++    }
++
++    /* reconnect */
++    while (1)
++    {
++      nserv->status = NNTP_NONE;
++      if (nntp_open_connection (nserv) == 0)
++      break;
++
++      snprintf (buf, sizeof (buf), _("Connection to %s lost. Reconnect?"),
++              nserv->conn->account.host);
++      if (mutt_yesorno (buf, MUTT_YES) != MUTT_YES)
++      {
++      nserv->status = NNTP_BYE;
++      return -1;
++      }
++    }
++
++    /* select newsgroup after reconnection */
++    if (nntp_data->group)
++    {
++      snprintf (buf, sizeof (buf), "GROUP %s\r\n", nntp_data->group);
++      if (mutt_socket_write (nserv->conn, buf) < 0 ||
++        mutt_socket_readln (buf, sizeof (buf), nserv->conn) < 0)
++      return nntp_connect_error (nserv);
++    }
++    if (!*line)
++      break;
++  }
++
++  strfcpy (line, buf, linelen);
++  return 0;
++}
++
++/* This function calls funct(*line, *data) for each received line,
++ * funct(NULL, *data) if rewind(*data) needs, exits when fail or done:
++ *  0 - success
++ *  1 - bad response (answer in query buffer)
++ * -1 - conection lost
++ * -2 - error in funct(*line, *data) */
++static int nntp_fetch_lines (NNTP_DATA *nntp_data, char *query, size_t qlen,
++                      char *msg, int (*funct) (char *, void *), void *data)
++{
++  int done = FALSE;
++  int rc;
++
++  while (!done)
++  {
++    char buf[LONG_STRING];
++    char *line;
++    unsigned int lines = 0;
++    size_t off = 0;
++    progress_t progress;
++
++    if (msg)
++      mutt_progress_init (&progress, msg, MUTT_PROGRESS_MSG, ReadInc, -1);
++
++    strfcpy (buf, query, sizeof (buf));
++    if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
++      return -1;
++    if (buf[0] != '2')
++    {
++      strfcpy (query, buf, qlen);
++      return 1;
++    }
++
++    line = safe_malloc (sizeof (buf));
++    rc = 0;
++
++    while (1)
++    {
++      char *p;
++      int chunk = mutt_socket_readln_d (buf, sizeof (buf),
++                nntp_data->nserv->conn, MUTT_SOCK_LOG_HDR);
++      if (chunk < 0)
++      {
++      nntp_data->nserv->status = NNTP_NONE;
++      break;
++      }
++
++      p = buf;
++      if (!off && buf[0] == '.')
++      {
++      if (buf[1] == '\0')
++      {
++        done = TRUE;
++        break;
++      }
++      if (buf[1] == '.')
++        p++;
++      }
++
++      strfcpy (line + off, p, sizeof (buf));
++
++      if (chunk >= sizeof (buf))
++      off += strlen (p);
++      else
++      {
++      if (msg)
++        mutt_progress_update (&progress, ++lines, -1);
++
++      if (rc == 0 && funct (line, data) < 0)
++        rc = -2;
++      off = 0;
++      }
++
++      safe_realloc (&line, off + sizeof (buf));
++    }
++    FREE (&line);
++    funct (NULL, data);
++  }
++  return rc;
++}
++
++/* Parse newsgroup description */
++static int fetch_description (char *line, void *data)
++{
++  NNTP_SERVER *nserv = data;
++  NNTP_DATA *nntp_data;
++  char *desc;
++
++  if (!line)
++    return 0;
++
++  desc = strpbrk (line, " \t");
++  if (desc)
++  {
++    *desc++ = '\0';
++    desc += strspn (desc, " \t");
++  }
++  else
++    desc = strchr (line, '\0');
++
++  nntp_data = hash_find (nserv->groups_hash, line);
++  if (nntp_data && mutt_strcmp (desc, nntp_data->desc))
++  {
++    mutt_str_replace (&nntp_data->desc, desc);
++    dprint (2, (debugfile, "group: %s, desc: %s\n", line, desc));
++  }
++  return 0;
++}
++
++/* Fetch newsgroups descriptions.
++ * Returns the same code as nntp_fetch_lines() */
++static int get_description (NNTP_DATA *nntp_data, char *wildmat, char *msg)
++{
++  NNTP_SERVER *nserv;
++  char buf[STRING];
++  char *cmd;
++  int rc;
++
++  /* get newsgroup description, if possible */
++  nserv = nntp_data->nserv;
++  if (!wildmat)
++    wildmat = nntp_data->group;
++  if (nserv->hasLIST_NEWSGROUPS)
++    cmd = "LIST NEWSGROUPS";
++  else if (nserv->hasXGTITLE)
++    cmd = "XGTITLE";
++  else
++    return 0;
++
++  snprintf (buf, sizeof (buf), "%s %s\r\n", cmd, wildmat);
++  rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), msg,
++                       fetch_description, nserv);
++  if (rc > 0)
++  {
++    mutt_error ("%s: %s", cmd, buf);
++    mutt_sleep (2);
++  }
++  return rc;
++}
++
++/* Update read flag and set article number if empty */
++static void nntp_parse_xref (CONTEXT *ctx, HEADER *hdr)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  char *buf, *p;
++
++  buf = p = safe_strdup (hdr->env->xref);
++  while (p)
++  {
++    char *grp, *colon;
++    anum_t anum;
++
++    /* skip to next word */
++    p += strspn (p, " \t");
++    grp = p;
++
++    /* skip to end of word */
++    p = strpbrk (p, " \t");
++    if (p)
++      *p++ = '\0';
++
++    /* find colon */
++    colon = strchr (grp, ':');
++    if (!colon)
++      continue;
++    *colon++ = '\0';
++    if (sscanf (colon, ANUM, &anum) != 1)
++      continue;
++
++    nntp_article_status (ctx, hdr, grp, anum);
++    if (hdr && !NHDR (hdr)->article_num && !mutt_strcmp (nntp_data->group, grp))
++      NHDR (hdr)->article_num = anum;
++  }
++  FREE (&buf);
++}
++
++/* Write line to temporarily file */
++static int fetch_tempfile (char *line, void *data)
++{
++  FILE *fp = data;
++
++  if (!line)
++    rewind (fp);
++  else if (fputs (line, fp) == EOF || fputc ('\n', fp) == EOF)
++    return -1;
++  return 0;
++}
++
++typedef struct
++{
++  CONTEXT *ctx;
++  anum_t first;
++  anum_t last;
++  int restore;
++  unsigned char *messages;
++  progress_t progress;
++#ifdef USE_HCACHE
++  header_cache_t *hc;
++#endif
++} FETCH_CTX;
++
++/* Parse article number */
++static int fetch_numbers (char *line, void *data)
++{
++  FETCH_CTX *fc = data;
++  anum_t anum;
++
++  if (!line)
++    return 0;
++  if (sscanf (line, ANUM, &anum) != 1)
++    return 0;
++  if (anum < fc->first || anum > fc->last)
++    return 0;
++  fc->messages[anum - fc->first] = 1;
++  return 0;
++}
++
++/* Parse overview line */
++static int parse_overview_line (char *line, void *data)
++{
++  FETCH_CTX *fc = data;
++  CONTEXT *ctx = fc->ctx;
++  NNTP_DATA *nntp_data = ctx->data;
++  HEADER *hdr;
++  FILE *fp;
++  char tempfile[_POSIX_PATH_MAX];
++  char *header, *field;
++  int save = 1;
++  anum_t anum;
++
++  if (!line)
++    return 0;
++
++  /* parse article number */
++  field = strchr (line, '\t');
++  if (field)
++    *field++ = '\0';
++  if (sscanf (line, ANUM, &anum) != 1)
++    return 0;
++  dprint (2, (debugfile, "parse_overview_line: " ANUM "\n", anum));
++
++  /* out of bounds */
++  if (anum < fc->first || anum > fc->last)
++    return 0;
++
++  /* not in LISTGROUP */
++  if (!fc->messages[anum - fc->first])
++  {
++    /* progress */
++    if (!ctx->quiet)
++      mutt_progress_update (&fc->progress, anum - fc->first + 1, -1);
++    return 0;
++  }
++
++  /* convert overview line to header */
++  mutt_mktemp (tempfile, sizeof (tempfile));
++  fp = safe_fopen (tempfile, "w+");
++  if (!fp)
++    return -1;
++
++  header = nntp_data->nserv->overview_fmt;
++  while (field)
++  {
++    char *b = field;
++
++    if (*header)
++    {
++      if (strstr (header, ":full") == NULL && fputs (header, fp) == EOF)
++      {
++      fclose (fp);
++      unlink (tempfile);
++      return -1;
++      }
++      header = strchr (header, '\0') + 1;
++    }
++
++    field = strchr (field, '\t');
++    if (field)
++      *field++ = '\0';
++    if (fputs (b, fp) == EOF || fputc ('\n', fp) == EOF)
++    {
++      fclose (fp);
++      unlink (tempfile);
++      return -1;
++    }
++  }
++  rewind (fp);
++
++  /* allocate memory for headers */
++  if (ctx->msgcount >= ctx->hdrmax)
++    mx_alloc_memory (ctx);
++
++  /* parse header */
++  hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
++  hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
++  hdr->env->newsgroups = safe_strdup (nntp_data->group);
++  hdr->received = hdr->date_sent;
++  fclose (fp);
++  unlink (tempfile);
++
++#ifdef USE_HCACHE
++  if (fc->hc)
++  {
++    void *hdata;
++    char buf[16];
++
++    /* try to replace with header from cache */
++    snprintf (buf, sizeof (buf), "%d", anum);
++    hdata = mutt_hcache_fetch (fc->hc, buf, strlen);
++    if (hdata)
++    {
++      dprint (2, (debugfile,
++                "parse_overview_line: mutt_hcache_fetch %s\n", buf));
++      mutt_free_header (&hdr);
++      ctx->hdrs[ctx->msgcount] =
++      hdr = mutt_hcache_restore (hdata, NULL);
++      mutt_hcache_free (&hdata);
++      hdr->data = 0;
++      hdr->read = 0;
++      hdr->old = 0;
++
++      /* skip header marked as deleted in cache */
++      if (hdr->deleted && !fc->restore)
++      {
++      if (nntp_data->bcache)
++      {
++        dprint (2, (debugfile,
++                    "parse_overview_line: mutt_bcache_del %s\n", buf));
++        mutt_bcache_del (nntp_data->bcache, buf);
++      }
++      save = 0;
++      }
++    }
++
++    /* not chached yet, store header */
++    else
++    {
++      dprint (2, (debugfile,
++                "parse_overview_line: mutt_hcache_store %s\n", buf));
++      mutt_hcache_store (fc->hc, buf, hdr, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
++    }
++  }
++#endif
++
++  if (save)
++  {
++    hdr->index = ctx->msgcount++;
++    hdr->read = 0;
++    hdr->old = 0;
++    hdr->deleted = 0;
++    hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
++    NHDR (hdr)->article_num = anum;
++    if (fc->restore)
++      hdr->changed = 1;
++    else
++    {
++      nntp_article_status (ctx, hdr, NULL, anum);
++      if (!hdr->read)
++      nntp_parse_xref (ctx, hdr);
++    }
++    if (anum > nntp_data->lastLoaded)
++      nntp_data->lastLoaded = anum;
++  }
++  else
++    mutt_free_header (&hdr);
++
++  /* progress */
++  if (!ctx->quiet)
++    mutt_progress_update (&fc->progress, anum - fc->first + 1, -1);
++  return 0;
++}
++
++/* Fetch headers */
++static int nntp_fetch_headers (CONTEXT *ctx, void *hc,
++                             anum_t first, anum_t last, int restore)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  FETCH_CTX fc;
++  HEADER *hdr;
++  char buf[HUGE_STRING];
++  int rc = 0;
++  int oldmsgcount = ctx->msgcount;
++  anum_t current;
++  anum_t first_over = first;
++#ifdef USE_HCACHE
++  void *hdata;
++#endif
++
++  /* if empty group or nothing to do */
++  if (!last || first > last)
++    return 0;
++
++  /* init fetch context */
++  fc.ctx = ctx;
++  fc.first = first;
++  fc.last = last;
++  fc.restore = restore;
++  fc.messages = safe_calloc (last - first + 1, sizeof (unsigned char));
++#ifdef USE_HCACHE
++  fc.hc = hc;
++#endif
++
++  /* fetch list of articles */
++  if (option (OPTLISTGROUP) && nntp_data->nserv->hasLISTGROUP &&
++      !nntp_data->deleted)
++  {
++    if (!ctx->quiet)
++      mutt_message _("Fetching list of articles...");
++    if (nntp_data->nserv->hasLISTGROUPrange)
++      snprintf (buf, sizeof (buf), "LISTGROUP %s %d-%d\r\n", nntp_data->group,
++              first, last);
++    else
++      snprintf (buf, sizeof (buf), "LISTGROUP %s\r\n", nntp_data->group);
++    rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
++                         fetch_numbers, &fc);
++    if (rc > 0)
++    {
++      mutt_error ("LISTGROUP: %s", buf);
++      mutt_sleep (2);
++    }
++    if (rc == 0)
++    {
++      for (current = first; current <= last && rc == 0; current++)
++      {
++      if (fc.messages[current - first])
++        continue;
++
++      snprintf (buf, sizeof (buf), "%d", current);
++      if (nntp_data->bcache)
++      {
++        dprint (2, (debugfile,
++                    "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
++        mutt_bcache_del (nntp_data->bcache, buf);
++      }
++
++#ifdef USE_HCACHE
++      if (fc.hc)
++      {
++        dprint (2, (debugfile,
++                    "nntp_fetch_headers: mutt_hcache_delete %s\n", buf));
++        mutt_hcache_delete (fc.hc, buf, strlen);
++      }
++#endif
++      }
++    }
++  }
++  else
++    for (current = first; current <= last; current++)
++      fc.messages[current - first] = 1;
++
++  /* fetching header from cache or server, or fallback to fetch overview */
++  if (!ctx->quiet)
++    mutt_progress_init (&fc.progress, _("Fetching message headers..."),
++                      MUTT_PROGRESS_MSG, ReadInc, last - first + 1);
++  for (current = first; current <= last && rc == 0; current++)
++  {
++    if (!ctx->quiet)
++      mutt_progress_update (&fc.progress, current - first + 1, -1);
++
++#ifdef USE_HCACHE
++    snprintf (buf, sizeof (buf), "%d", current);
++#endif
++
++    /* delete header from cache that does not exist on server */
++    if (!fc.messages[current - first])
++      continue;
++
++    /* allocate memory for headers */
++    if (ctx->msgcount >= ctx->hdrmax)
++      mx_alloc_memory (ctx);
++
++#ifdef USE_HCACHE
++    /* try to fetch header from cache */
++    hdata = mutt_hcache_fetch (fc.hc, buf, strlen);
++    if (hdata)
++    {
++      dprint (2, (debugfile,
++                "nntp_fetch_headers: mutt_hcache_fetch %s\n", buf));
++      ctx->hdrs[ctx->msgcount] =
++      hdr = mutt_hcache_restore (hdata, NULL);
++      mutt_hcache_free (&hdata);
++      hdr->data = 0;
++
++      /* skip header marked as deleted in cache */
++      if (hdr->deleted && !restore)
++      {
++      mutt_free_header (&hdr);
++      if (nntp_data->bcache)
++      {
++        dprint (2, (debugfile,
++                    "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
++        mutt_bcache_del (nntp_data->bcache, buf);
++      }
++      continue;
++      }
++
++      hdr->read = 0;
++      hdr->old = 0;
++    }
++    else
++#endif
++
++    /* don't try to fetch header from removed newsgroup */
++    if (nntp_data->deleted)
++      continue;
++
++    /* fallback to fetch overview */
++    else if (nntp_data->nserv->hasOVER || nntp_data->nserv->hasXOVER)
++      if (option (OPTLISTGROUP) && nntp_data->nserv->hasLISTGROUP)
++      break;
++      else
++      continue;
++
++    /* fetch header from server */
++    else
++    {
++      FILE *fp;
++      char tempfile[_POSIX_PATH_MAX];
++
++      mutt_mktemp (tempfile, sizeof (tempfile));
++      fp = safe_fopen (tempfile, "w+");
++      if (!fp)
++      {
++      mutt_perror (tempfile);
++      mutt_sleep (2);
++      unlink (tempfile);
++      rc = -1;
++      break;
++      }
++
++      snprintf (buf, sizeof (buf), "HEAD %d\r\n", current);
++      rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
++                           fetch_tempfile, fp);
++      if (rc)
++      {
++      fclose (fp);
++      unlink (tempfile);
++      if (rc < 0)
++        break;
++
++      /* invalid response */
++      if (mutt_strncmp ("423", buf, 3))
++      {
++        mutt_error ("HEAD: %s", buf);
++        mutt_sleep (2);
++        break;
++      }
++
++      /* no such article */
++      if (nntp_data->bcache)
++      {
++        snprintf (buf, sizeof (buf), "%d", current);
++        dprint (2, (debugfile,
++                    "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
++        mutt_bcache_del (nntp_data->bcache, buf);
++      }
++      rc = 0;
++      continue;
++      }
++
++      /* parse header */
++      hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
++      hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
++      hdr->received = hdr->date_sent;
++      fclose (fp);
++      unlink (tempfile);
++    }
++
++    /* save header in context */
++    hdr->index = ctx->msgcount++;
++    hdr->read = 0;
++    hdr->old = 0;
++    hdr->deleted = 0;
++    hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
++    NHDR (hdr)->article_num = current;
++    if (restore)
++      hdr->changed = 1;
++    else
++    {
++      nntp_article_status (ctx, hdr, NULL, NHDR (hdr)->article_num);
++      if (!hdr->read)
++      nntp_parse_xref (ctx, hdr);
++    }
++    if (current > nntp_data->lastLoaded)
++      nntp_data->lastLoaded = current;
++    first_over = current + 1;
++  }
++
++  if (!option (OPTLISTGROUP) || !nntp_data->nserv->hasLISTGROUP)
++    current = first_over;
++
++  /* fetch overview information */
++  if (current <= last && rc == 0 && !nntp_data->deleted) {
++    char *cmd = nntp_data->nserv->hasOVER ? "OVER" : "XOVER";
++    snprintf (buf, sizeof (buf), "%s %d-%d\r\n", cmd, current, last);
++    rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
++       parse_overview_line, &fc);
++    if (rc > 0)
++    {
++      mutt_error ("%s: %s", cmd, buf);
++      mutt_sleep (2);
++    }
++  }
++
++  if (ctx->msgcount > oldmsgcount)
++    mx_update_context (ctx, ctx->msgcount - oldmsgcount);
++
++  FREE (&fc.messages);
++  if (rc != 0)
++    return -1;
++  mutt_clear_error ();
++  return 0;
++}
++
++/* Open newsgroup */
++static int nntp_open_mailbox (CONTEXT *ctx)
++{
++  NNTP_SERVER *nserv;
++  NNTP_DATA *nntp_data;
++  char buf[HUGE_STRING];
++  char server[LONG_STRING];
++  char *group;
++  int rc;
++  void *hc = NULL;
++  anum_t first, last, count = 0;
++  ciss_url_t url;
++
++  strfcpy (buf, ctx->path, sizeof (buf));
++  if (url_parse_ciss (&url, buf) < 0 || !url.path ||
++     !(url.scheme == U_NNTP || url.scheme == U_NNTPS))
++  {
++    mutt_error (_("%s is an invalid newsgroup specification!"), ctx->path);
++    mutt_sleep (2);
++    return -1;
++  }
++
++  group = url.path;
++  url.path = strchr (url.path, '\0');
++  url_ciss_tostring (&url, server, sizeof (server), 0);
++  nserv = nntp_select_server (server, 1);
++  if (!nserv)
++    return -1;
++  CurrentNewsSrv = nserv;
++
++  /* find news group data structure */
++  nntp_data = hash_find (nserv->groups_hash, group);
++  if (!nntp_data)
++  {
++    nntp_newsrc_close (nserv);
++    mutt_error (_("Newsgroup %s not found on the server."), group);
++    mutt_sleep (2);
++    return -1;
++  }
++
++  mutt_bit_unset (ctx->rights, MUTT_ACL_INSERT);
++  if (!nntp_data->newsrc_ent && !nntp_data->subscribed &&
++      !option (OPTSAVEUNSUB))
++    ctx->readonly = 1;
++
++  /* select newsgroup */
++  mutt_message (_("Selecting %s..."), group);
++  buf[0] = '\0';
++  if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
++  {
++    nntp_newsrc_close (nserv);
++    return -1;
++  }
++
++  /* newsgroup not found, remove it */
++  if (!mutt_strncmp ("411", buf, 3))
++  {
++    mutt_error (_("Newsgroup %s has been removed from the server."),
++              nntp_data->group);
++    if (!nntp_data->deleted)
++    {
++      nntp_data->deleted = 1;
++      nntp_active_save_cache (nserv);
++    }
++    if (nntp_data->newsrc_ent && !nntp_data->subscribed &&
++      !option (OPTSAVEUNSUB))
++    {
++      FREE (&nntp_data->newsrc_ent);
++      nntp_data->newsrc_len = 0;
++      nntp_delete_group_cache (nntp_data);
++      nntp_newsrc_update (nserv);
++    }
++    mutt_sleep (2);
++  }
++
++  /* parse newsgroup info */
++  else {
++    if (sscanf (buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
++    {
++      nntp_newsrc_close (nserv);
++      mutt_error ("GROUP: %s", buf);
++      mutt_sleep (2);
++      return -1;
++    }
++    nntp_data->firstMessage = first;
++    nntp_data->lastMessage = last;
++    nntp_data->deleted = 0;
++
++    /* get description if empty */
++    if (option (OPTLOADDESC) && !nntp_data->desc)
++    {
++      if (get_description (nntp_data, NULL, NULL) < 0)
++      {
++      nntp_newsrc_close (nserv);
++      return -1;
++      }
++      if (nntp_data->desc)
++      nntp_active_save_cache (nserv);
++    }
++  }
++
++  time (&nserv->check_time);
++  ctx->data = nntp_data;
++  if (!nntp_data->bcache && (nntp_data->newsrc_ent ||
++      nntp_data->subscribed || option (OPTSAVEUNSUB)))
++    nntp_data->bcache = mutt_bcache_open (&nserv->conn->account,
++                      nntp_data->group);
++
++  /* strip off extra articles if adding context is greater than $nntp_context */
++  first = nntp_data->firstMessage;
++  if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext)
++    first = nntp_data->lastMessage - NntpContext + 1;
++  nntp_data->lastLoaded = first ? first - 1 : 0;
++  count = nntp_data->firstMessage;
++  nntp_data->firstMessage = first;
++  nntp_bcache_update (nntp_data);
++  nntp_data->firstMessage = count;
++#ifdef USE_HCACHE
++  hc = nntp_hcache_open (nntp_data);
++  nntp_hcache_update (nntp_data, hc);
++#endif
++  if (!hc)
++  {
++    mutt_bit_unset (ctx->rights, MUTT_ACL_WRITE);
++    mutt_bit_unset (ctx->rights, MUTT_ACL_DELETE);
++  }
++  nntp_newsrc_close (nserv);
++  rc = nntp_fetch_headers (ctx, hc, first, nntp_data->lastMessage, 0);
++#ifdef USE_HCACHE
++  mutt_hcache_close (hc);
++#endif
++  if (rc < 0)
++    return -1;
++  nntp_data->lastLoaded = nntp_data->lastMessage;
++  nserv->newsrc_modified = 0;
++  return 0;
++}
++
++/* Fetch message */
++static int nntp_open_message (CONTEXT *ctx, MESSAGE *msg, int msgno)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  NNTP_ACACHE *acache;
++  HEADER *hdr = ctx->hdrs[msgno];
++  char buf[_POSIX_PATH_MAX];
++  char article[16];
++  char *fetch_msg = _("Fetching message...");
++  int rc;
++
++  /* try to get article from cache */
++  acache = &nntp_data->acache[hdr->index % NNTP_ACACHE_LEN];
++  if (acache->path)
++  {
++    if (acache->index == hdr->index)
++    {
++      msg->fp = fopen (acache->path, "r");
++      if (msg->fp)
++      return 0;
++    }
++    /* clear previous entry */
++    else
++    {
++      unlink (acache->path);
++      FREE (&acache->path);
++    }
++  }
++  snprintf (article, sizeof (article), "%d", NHDR (hdr)->article_num);
++  msg->fp = mutt_bcache_get (nntp_data->bcache, article);
++  if (msg->fp)
++  {
++    if (NHDR (hdr)->parsed)
++      return 0;
++  }
++  else
++  {
++    /* don't try to fetch article from removed newsgroup */
++    if (nntp_data->deleted)
++      return -1;
++
++    /* create new cache file */
++    mutt_message (fetch_msg);
++    msg->fp = mutt_bcache_put (nntp_data->bcache, article, 1);
++    if (!msg->fp)
++    {
++      mutt_mktemp (buf, sizeof (buf));
++      acache->path = safe_strdup (buf);
++      acache->index = hdr->index;
++      msg->fp = safe_fopen (acache->path, "w+");
++      if (!msg->fp)
++      {
++      mutt_perror (acache->path);
++      unlink (acache->path);
++      FREE (&acache->path);
++      return -1;
++      }
++    }
++
++    /* fetch message to cache file */
++    snprintf (buf, sizeof (buf), "ARTICLE %s\r\n",
++            NHDR (hdr)->article_num ? article : hdr->env->message_id);
++    rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), fetch_msg,
++                         fetch_tempfile, msg->fp);
++    if (rc)
++    {
++      safe_fclose (&msg->fp);
++      if (acache->path)
++      {
++      unlink (acache->path);
++      FREE (&acache->path);
++      }
++      if (rc > 0)
++      {
++      if (!mutt_strncmp (NHDR (hdr)->article_num ? "423" : "430", buf, 3))
++        mutt_error (_("Article %d not found on the server."),
++                    NHDR (hdr)->article_num ? article : hdr->env->message_id);
++      else
++        mutt_error ("ARTICLE: %s", buf);
++      }
++      return -1;
++    }
++
++    if (!acache->path)
++      mutt_bcache_commit (nntp_data->bcache, article);
++  }
++
++  /* replace envelope with new one
++   * hash elements must be updated because pointers will be changed */
++  if (ctx->id_hash && hdr->env->message_id)
++    hash_delete (ctx->id_hash, hdr->env->message_id, hdr, NULL);
++  if (ctx->subj_hash && hdr->env->real_subj)
++    hash_delete (ctx->subj_hash, hdr->env->real_subj, hdr, NULL);
++
++  mutt_free_envelope (&hdr->env);
++  hdr->env = mutt_read_rfc822_header (msg->fp, hdr, 0, 0);
++
++  if (ctx->id_hash && hdr->env->message_id)
++    hash_insert (ctx->id_hash, hdr->env->message_id, hdr);
++  if (ctx->subj_hash && hdr->env->real_subj)
++    hash_insert (ctx->subj_hash, hdr->env->real_subj, hdr);
++
++  /* fix content length */
++  fseek (msg->fp, 0, SEEK_END);
++  hdr->content->length = ftell (msg->fp) - hdr->content->offset;
++
++  /* this is called in mutt before the open which fetches the message,
++   * which is probably wrong, but we just call it again here to handle
++   * the problem instead of fixing it */
++  NHDR (hdr)->parsed = 1;
++  mutt_parse_mime_message (ctx, hdr);
++
++  /* these would normally be updated in mx_update_context(), but the
++   * full headers aren't parsed with overview, so the information wasn't
++   * available then */
++  if (WithCrypto)
++    hdr->security = crypt_query (hdr->content);
++
++  rewind (msg->fp);
++  mutt_clear_error();
++  return 0;
++}
++
++/* Close message */
++static int nntp_close_message (CONTEXT *ctx, MESSAGE *msg)
++{
++  return safe_fclose (&msg->fp);
++}
++
++/* Post article */
++int nntp_post (const char *msg) {
++  NNTP_DATA *nntp_data, nntp_tmp;
++  FILE *fp;
++  char buf[LONG_STRING];
++  size_t len;
++
++  if (Context && Context->magic == MUTT_NNTP)
++    nntp_data = Context->data;
++  else
++  {
++    CurrentNewsSrv = nntp_select_server (NewsServer, 0);
++    if (!CurrentNewsSrv)
++      return -1;
++
++    nntp_data = &nntp_tmp;
++    nntp_data->nserv = CurrentNewsSrv;
++    nntp_data->group = NULL;
++  }
++
++  fp = safe_fopen (msg, "r");
++  if (!fp)
++  {
++    mutt_perror (msg);
++    return -1;
++  }
++
++  strfcpy (buf, "POST\r\n", sizeof (buf));
++  if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
++    return -1;
++  if (buf[0] != '3')
++  {
++    mutt_error (_("Can't post article: %s"), buf);
++    return -1;
++  }
++
++  buf[0] = '.';
++  buf[1] = '\0';
++  while (fgets (buf + 1, sizeof (buf) - 2, fp))
++  {
++    len = strlen (buf);
++    if (buf[len - 1] == '\n')
++    {
++      buf[len - 1] = '\r';
++      buf[len] = '\n';
++      len++;
++      buf[len] = '\0';
++    }
++    if (mutt_socket_write_d (nntp_data->nserv->conn,
++      buf[1] == '.' ? buf : buf + 1, -1, MUTT_SOCK_LOG_HDR) < 0)
++      return nntp_connect_error (nntp_data->nserv);
++  }
++  fclose (fp);
++
++  if ((buf[strlen (buf) - 1] != '\n' &&
++      mutt_socket_write_d (nntp_data->nserv->conn, "\r\n", -1, MUTT_SOCK_LOG_HDR) < 0) ||
++      mutt_socket_write_d (nntp_data->nserv->conn, ".\r\n", -1, MUTT_SOCK_LOG_HDR) < 0 ||
++      mutt_socket_readln (buf, sizeof (buf), nntp_data->nserv->conn) < 0)
++    return nntp_connect_error (nntp_data->nserv);
++  if (buf[0] != '2')
++  {
++    mutt_error (_("Can't post article: %s"), buf);
++    return -1;
++  }
++  return 0;
++}
++
++/* Free up memory associated with the newsgroup context */
++static int nntp_close_mailbox (CONTEXT *ctx)
++{
++  NNTP_DATA *nntp_data = ctx->data, *nntp_tmp;
++
++  if (!nntp_data)
++    return 0;
++
++  nntp_data->unread = ctx->unread;
++
++  nntp_acache_free (nntp_data);
++  if (!nntp_data->nserv || !nntp_data->nserv->groups_hash || !nntp_data->group)
++    return 0;
++
++  nntp_tmp = hash_find (nntp_data->nserv->groups_hash, nntp_data->group);
++  if (nntp_tmp == NULL || nntp_tmp != nntp_data)
++    nntp_data_free (nntp_data);
++  return 0;
++}
++
++/* Get date and time from server */
++int nntp_date (NNTP_SERVER *nserv, time_t *now)
++{
++  if (nserv->hasDATE)
++  {
++    NNTP_DATA nntp_data;
++    char buf[LONG_STRING];
++    struct tm tm;
++
++    nntp_data.nserv = nserv;
++    nntp_data.group = NULL;
++    strfcpy (buf, "DATE\r\n", sizeof (buf));
++    if (nntp_query (&nntp_data, buf, sizeof (buf)) < 0)
++      return -1;
++
++    if (sscanf (buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
++              &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
++    {
++      tm.tm_year -= 1900;
++      tm.tm_mon--;
++      *now = timegm (&tm);
++      if (*now >= 0)
++      {
++      dprint (1, (debugfile, "nntp_date: server time is %d\n", *now));
++      return 0;
++      }
++    }
++  }
++  time (now);
++  return 0;
++}
++
++/* Fetch list of all newsgroups from server */
++int nntp_active_fetch (NNTP_SERVER *nserv, unsigned int new)
++{
++  NNTP_DATA nntp_data;
++  char msg[SHORT_STRING];
++  char buf[LONG_STRING];
++  unsigned int i;
++  int rc;
++
++  snprintf (msg, sizeof (msg), _("Loading list of groups from server %s..."),
++          nserv->conn->account.host);
++  mutt_message (msg);
++  if (nntp_date (nserv, &nserv->newgroups_time) < 0)
++    return -1;
++
++  nntp_data.nserv = nserv;
++  nntp_data.group = NULL;
++  i = nserv->groups_num;
++  strfcpy (buf, "LIST\r\n", sizeof (buf));
++  rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
++                       nntp_add_group, nserv);
++  if (rc)
++  {
++    if (rc > 0)
++    {
++      mutt_error ("LIST: %s", buf);
++      mutt_sleep (2);
++    }
++    return -1;
++  }
++
++  if (new)
++  {
++    for (; i < nserv->groups_num; i++)
++    {
++      NNTP_DATA *nntp_data = nserv->groups_list[i];
++      nntp_data->new = 1;
++    }
++  }
++
++  for (i = 0; i < nserv->groups_num; i++)
++  {
++    NNTP_DATA *nntp_data = nserv->groups_list[i];
++
++    if (nntp_data && nntp_data->deleted && !nntp_data->newsrc_ent)
++    {
++      nntp_delete_group_cache (nntp_data);
++      hash_delete (nserv->groups_hash, nntp_data->group, NULL, nntp_data_free);
++      nserv->groups_list[i] = NULL;
++    }
++  }
++
++  if (option (OPTLOADDESC))
++    rc = get_description (&nntp_data, "*", _("Loading descriptions..."));
++
++  nntp_active_save_cache (nserv);
++  if (rc < 0)
++    return -1;
++  mutt_clear_error ();
++  return 0;
++}
++
++/* Check newsgroup for new articles:
++ *  1 - new articles found
++ *  0 - no change
++ * -1 - lost connection */
++static int nntp_group_poll (NNTP_DATA *nntp_data, int update_stat)
++{
++  char buf[LONG_STRING] = "";
++  anum_t count, first, last;
++
++  /* use GROUP command to poll newsgroup */
++  if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
++    return -1;
++  if (sscanf (buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
++    return 0;
++  if (first == nntp_data->firstMessage && last == nntp_data->lastMessage)
++    return 0;
++
++  /* articles have been renumbered */
++  if (last < nntp_data->lastMessage)
++  {
++    nntp_data->lastCached = 0;
++    if (nntp_data->newsrc_len)
++    {
++      safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
++      nntp_data->newsrc_len = 1;
++      nntp_data->newsrc_ent[0].first = 1;
++      nntp_data->newsrc_ent[0].last = 0;
++    }
++  }
++  nntp_data->firstMessage = first;
++  nntp_data->lastMessage = last;
++  if (!update_stat)
++    return 1;
++
++  /* update counters */
++  else if (!last || (!nntp_data->newsrc_ent && !nntp_data->lastCached))
++    nntp_data->unread = count;
++  else
++    nntp_group_unread_stat (nntp_data);
++  return 1;
++}
++
++/* Check current newsgroup for new articles, leave newsrc locked:
++ *  MUTT_REOPENED - articles have been renumbered or removed from server
++ *  MUTT_NEW_MAIL - new articles found
++ *  0           - no change
++ * -1           - lost connection */
++static int check_mailbox (CONTEXT *ctx)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  NNTP_SERVER *nserv = nntp_data->nserv;
++  time_t now = time (NULL);
++  int i, j;
++  int rc, ret = 0;
++  void *hc = NULL;
++
++  if (nserv->check_time + NewsPollTimeout > now)
++    return 0;
++
++  mutt_message _("Checking for new messages...");
++  if (nntp_newsrc_parse (nserv) < 0)
++    return -1;
++
++  nserv->check_time = now;
++  rc = nntp_group_poll (nntp_data, 0);
++  if (rc < 0)
++  {
++    nntp_newsrc_close (nserv);
++    return -1;
++  }
++  if (rc)
++    nntp_active_save_cache (nserv);
++
++  /* articles have been renumbered, remove all headers */
++  if (nntp_data->lastMessage < nntp_data->lastLoaded)
++  {
++    for (i = 0; i < ctx->msgcount; i++)
++      mutt_free_header (&ctx->hdrs[i]);
++    ctx->msgcount = 0;
++    ctx->tagged = 0;
++
++    if (nntp_data->lastMessage < nntp_data->lastLoaded)
++    {
++      nntp_data->lastLoaded = nntp_data->firstMessage - 1;
++      if (NntpContext && nntp_data->lastMessage - nntp_data->lastLoaded >
++        NntpContext)
++      nntp_data->lastLoaded = nntp_data->lastMessage - NntpContext;
++    }
++    ret = MUTT_REOPENED;
++  }
++
++  /* .newsrc has been externally modified */
++  if (nserv->newsrc_modified)
++  {
++    anum_t anum;
++#ifdef USE_HCACHE
++    unsigned char *messages;
++    char buf[16];
++    void *hdata;
++    HEADER *hdr;
++    anum_t first = nntp_data->firstMessage;
++
++    if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext)
++      first = nntp_data->lastMessage - NntpContext + 1;
++    messages = safe_calloc (nntp_data->lastLoaded - first + 1,
++                          sizeof (unsigned char));
++    hc = nntp_hcache_open (nntp_data);
++    nntp_hcache_update (nntp_data, hc);
++#endif
++
++    /* update flags according to .newsrc */
++    for (i = j = 0; i < ctx->msgcount; i++)
++    {
++      int flagged = 0;
++      anum = NHDR (ctx->hdrs[i])->article_num;
++
++#ifdef USE_HCACHE
++      /* check hcache for flagged and deleted flags */
++      if (hc)
++      {
++      if (anum >= first && anum <= nntp_data->lastLoaded)
++        messages[anum - first] = 1;
++
++      snprintf (buf, sizeof (buf), "%d", anum);
++      hdata = mutt_hcache_fetch (hc, buf, strlen);
++      if (hdata)
++      {
++        int deleted;
++
++        dprint (2, (debugfile,
++                    "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf));
++        hdr = mutt_hcache_restore (hdata, NULL);
++        mutt_hcache_free (&hdata);
++        hdr->data = 0;
++        deleted = hdr->deleted;
++        flagged = hdr->flagged;
++        mutt_free_header (&hdr);
++
++        /* header marked as deleted, removing from context */
++        if (deleted)
++        {
++          mutt_set_flag (ctx, ctx->hdrs[i], MUTT_TAG, 0);
++          mutt_free_header (&ctx->hdrs[i]);
++          continue;
++        }
++      }
++      }
++#endif
++
++      if (!ctx->hdrs[i]->changed)
++      {
++      ctx->hdrs[i]->flagged = flagged;
++      ctx->hdrs[i]->read = 0;
++      ctx->hdrs[i]->old = 0;
++      nntp_article_status (ctx, ctx->hdrs[i], NULL, anum);
++      if (!ctx->hdrs[i]->read)
++        nntp_parse_xref (ctx, ctx->hdrs[i]);
++      }
++      ctx->hdrs[j++] = ctx->hdrs[i];
++    }
++
++#ifdef USE_HCACHE
++    ctx->msgcount = j;
++
++    /* restore headers without "deleted" flag */
++    for (anum = first; anum <= nntp_data->lastLoaded; anum++)
++    {
++      if (messages[anum - first])
++      continue;
++
++      snprintf (buf, sizeof (buf), "%d", anum);
++      hdata = mutt_hcache_fetch (hc, buf, strlen);
++      if (hdata)
++      {
++      dprint (2, (debugfile,
++                  "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf));
++      if (ctx->msgcount >= ctx->hdrmax)
++        mx_alloc_memory (ctx);
++
++      ctx->hdrs[ctx->msgcount] =
++      hdr = mutt_hcache_restore (hdata, NULL);
++      mutt_hcache_free (&hdata);
++      hdr->data = 0;
++      if (hdr->deleted)
++      {
++        mutt_free_header (&hdr);
++        if (nntp_data->bcache)
++        {
++          dprint (2, (debugfile,
++                      "nntp_check_mailbox: mutt_bcache_del %s\n", buf));
++          mutt_bcache_del (nntp_data->bcache, buf);
++        }
++        continue;
++      }
++
++      ctx->msgcount++;
++      hdr->read = 0;
++      hdr->old = 0;
++      hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
++      NHDR (hdr)->article_num = anum;
++      nntp_article_status (ctx, hdr, NULL, anum);
++      if (!hdr->read)
++        nntp_parse_xref (ctx, hdr);
++      }
++    }
++    FREE (&messages);
++#endif
++
++    nserv->newsrc_modified = 0;
++    ret = MUTT_REOPENED;
++  }
++
++  /* some headers were removed, context must be updated */
++  if (ret == MUTT_REOPENED)
++  {
++    if (ctx->subj_hash)
++      hash_destroy (&ctx->subj_hash, NULL);
++    if (ctx->id_hash)
++      hash_destroy (&ctx->id_hash, NULL);
++    mutt_clear_threads (ctx);
++
++    ctx->vcount = 0;
++    ctx->deleted = 0;
++    ctx->new = 0;
++    ctx->unread = 0;
++    ctx->flagged = 0;
++    ctx->changed = 0;
++    ctx->id_hash = NULL;
++    ctx->subj_hash = NULL;
++    mx_update_context (ctx, ctx->msgcount);
++  }
++
++  /* fetch headers of new articles */
++  if (nntp_data->lastMessage > nntp_data->lastLoaded)
++  {
++    int oldmsgcount = ctx->msgcount;
++    int quiet = ctx->quiet;
++    ctx->quiet = 1;
++#ifdef USE_HCACHE
++    if (!hc)
++    {
++      hc = nntp_hcache_open (nntp_data);
++      nntp_hcache_update (nntp_data, hc);
++    }
++#endif
++    rc = nntp_fetch_headers (ctx, hc, nntp_data->lastLoaded + 1,
++                           nntp_data->lastMessage, 0);
++    ctx->quiet = quiet;
++    if (rc >= 0)
++      nntp_data->lastLoaded = nntp_data->lastMessage;
++    if (ret == 0 && ctx->msgcount > oldmsgcount)
++      ret = MUTT_NEW_MAIL;
++  }
++
++#ifdef USE_HCACHE
++  mutt_hcache_close (hc);
++#endif
++  if (ret)
++    nntp_newsrc_close (nserv);
++  mutt_clear_error ();
++  return ret;
++}
++
++/* Check current newsgroup for new articles:
++ *  MUTT_REOPENED - articles have been renumbered or removed from server
++ *  MUTT_NEW_MAIL - new articles found
++ *  0           - no change
++ * -1           - lost connection */
++static int nntp_check_mailbox (CONTEXT *ctx, int *index_hint)
++{
++  int ret = check_mailbox (ctx);
++  if (ret == 0)
++  {
++    NNTP_DATA *nntp_data = ctx->data;
++    NNTP_SERVER *nserv = nntp_data->nserv;
++    nntp_newsrc_close (nserv);
++  }
++  return ret;
++}
++
++/* Save changes to .newsrc and cache */
++static int nntp_sync_mailbox (CONTEXT *ctx, int *index_hint)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  int rc, i;
++#ifdef USE_HCACHE
++  header_cache_t *hc;
++#endif
++
++  /* check for new articles */
++  nntp_data->nserv->check_time = 0;
++  rc = check_mailbox (ctx);
++  if (rc)
++    return rc;
++
++#ifdef USE_HCACHE
++  nntp_data->lastCached = 0;
++  hc = nntp_hcache_open (nntp_data);
++#endif
++
++  for (i = 0; i < ctx->msgcount; i++)
++  {
++    HEADER *hdr = ctx->hdrs[i];
++    char buf[16];
++
++    snprintf (buf, sizeof (buf), "%d", NHDR (hdr)->article_num);
++    if (nntp_data->bcache && hdr->deleted)
++    {
++      dprint (2, (debugfile, "nntp_sync_mailbox: mutt_bcache_del %s\n", buf));
++      mutt_bcache_del (nntp_data->bcache, buf);
++    }
++
++#ifdef USE_HCACHE
++    if (hc && (hdr->changed || hdr->deleted))
++    {
++      dprint (2, (debugfile, "nntp_sync_mailbox: mutt_hcache_store %s\n", buf));
++      mutt_hcache_store (hc, buf, hdr, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
++    }
++#endif
++  }
++
++#ifdef USE_HCACHE
++  if (hc)
++  {
++    mutt_hcache_close (hc);
++    nntp_data->lastCached = nntp_data->lastLoaded;
++  }
++#endif
++
++  /* save .newsrc entries */
++  nntp_newsrc_gen_entries (ctx);
++  nntp_newsrc_update (nntp_data->nserv);
++  nntp_newsrc_close (nntp_data->nserv);
++  return 0;
++}
++
++/* Check for new groups and new articles in subscribed groups:
++ *  1 - new groups found
++ *  0 - no new groups
++ * -1 - error */
++int nntp_check_new_groups (NNTP_SERVER *nserv)
++{
++  NNTP_DATA nntp_data;
++  time_t now;
++  struct tm *tm;
++  char buf[LONG_STRING];
++  char *msg = _("Checking for new newsgroups...");
++  unsigned int i;
++  int rc, update_active = FALSE;
++
++  if (!nserv || !nserv->newgroups_time)
++    return -1;
++
++  /* check subscribed newsgroups for new articles */
++  if (option (OPTSHOWNEWNEWS))
++  {
++    mutt_message _("Checking for new messages...");
++    for (i = 0; i < nserv->groups_num; i++)
++    {
++      NNTP_DATA *nntp_data = nserv->groups_list[i];
++
++      if (nntp_data && nntp_data->subscribed)
++      {
++      rc = nntp_group_poll (nntp_data, 1);
++      if (rc < 0)
++        return -1;
++      if (rc > 0)
++        update_active = TRUE;
++      }
++    }
++    /* select current newsgroup */
++    if (Context && Context->magic == MUTT_NNTP)
++    {
++      buf[0] = '\0';
++      if (nntp_query ((NNTP_DATA *)Context->data, buf, sizeof (buf)) < 0)
++      return -1;
++    }
++  }
++  else if (nserv->newgroups_time)
++    return 0;
++
++  /* get list of new groups */
++  mutt_message (msg);
++  if (nntp_date (nserv, &now) < 0)
++    return -1;
++  nntp_data.nserv = nserv;
++  if (Context && Context->magic == MUTT_NNTP)
++    nntp_data.group = ((NNTP_DATA *)Context->data)->group;
++  else
++    nntp_data.group = NULL;
++  i = nserv->groups_num;
++  tm = gmtime (&nserv->newgroups_time);
++  snprintf (buf, sizeof (buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n",
++          tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
++          tm->tm_hour, tm->tm_min, tm->tm_sec);
++  rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
++                       nntp_add_group, nserv);
++  if (rc)
++  {
++    if (rc > 0)
++    {
++      mutt_error ("NEWGROUPS: %s", buf);
++      mutt_sleep (2);
++    }
++    return -1;
++  }
++
++  /* new groups found */
++  rc = 0;
++  if (nserv->groups_num != i)
++  {
++    int groups_num = i;
++
++    nserv->newgroups_time = now;
++    for (; i < nserv->groups_num; i++)
++    {
++      NNTP_DATA *nntp_data = nserv->groups_list[i];
++      nntp_data->new = 1;
++    }
++
++    /* loading descriptions */
++    if (option (OPTLOADDESC))
++    {
++      unsigned int count = 0;
++      progress_t progress;
++
++      mutt_progress_init (&progress, _("Loading descriptions..."),
++                        MUTT_PROGRESS_MSG, ReadInc, nserv->groups_num - i);
++      for (i = groups_num; i < nserv->groups_num; i++)
++      {
++      NNTP_DATA *nntp_data = nserv->groups_list[i];
++
++      if (get_description (nntp_data, NULL, NULL) < 0)
++        return -1;
++      mutt_progress_update (&progress, ++count, -1);
++      }
++    }
++    update_active = TRUE;
++    rc = 1;
++  }
++  if (update_active)
++    nntp_active_save_cache (nserv);
++  mutt_clear_error ();
++  return rc;
++}
++
++/* Fetch article by Message-ID:
++ *  0 - success
++ *  1 - no such article
++ * -1 - error */
++int nntp_check_msgid (CONTEXT *ctx, const char *msgid)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  HEADER *hdr;
++  FILE *fp;
++  char tempfile[_POSIX_PATH_MAX];
++  char buf[LONG_STRING];
++  int rc;
++
++  mutt_mktemp (tempfile, sizeof (tempfile));
++  fp = safe_fopen (tempfile, "w+");
++  if (!fp)
++  {
++    mutt_perror (tempfile);
++    unlink (tempfile);
++    return -1;
++  }
++
++  snprintf (buf, sizeof (buf), "HEAD %s\r\n", msgid);
++  rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
++                       fetch_tempfile, fp);
++  if (rc)
++  {
++    fclose (fp);
++    unlink (tempfile);
++    if (rc < 0)
++      return -1;
++    if (!mutt_strncmp ("430", buf, 3))
++      return 1;
++    mutt_error ("HEAD: %s", buf);
++    return -1;
++  }
++
++  /* parse header */
++  if (ctx->msgcount == ctx->hdrmax)
++    mx_alloc_memory (ctx);
++  hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
++  hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
++  hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
++  fclose (fp);
++  unlink (tempfile);
++
++  /* get article number */
++  if (hdr->env->xref)
++    nntp_parse_xref (ctx, hdr);
++  else
++  {
++    snprintf (buf, sizeof (buf), "STAT %s\r\n", msgid);
++    if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
++    {
++      mutt_free_header (&hdr);
++      return -1;
++    }
++    sscanf (buf + 4, ANUM, &NHDR (hdr)->article_num);
++  }
++
++  /* reset flags */
++  hdr->read = 0;
++  hdr->old = 0;
++  hdr->deleted = 0;
++  hdr->changed = 1;
++  hdr->received = hdr->date_sent;
++  hdr->index = ctx->msgcount++;
++  mx_update_context (ctx, 1);
++  return 0;
++}
++
++typedef struct
++{
++  CONTEXT *ctx;
++  unsigned int num;
++  unsigned int max;
++  anum_t *child;
++} CHILD_CTX;
++
++/* Parse XPAT line */
++static int fetch_children (char *line, void *data)
++{
++  CHILD_CTX *cc = data;
++  anum_t anum;
++  unsigned int i;
++
++  if (!line || sscanf (line, ANUM, &anum) != 1)
++    return 0;
++  for (i = 0; i < cc->ctx->msgcount; i++)
++    if (NHDR (cc->ctx->hdrs[i])->article_num == anum)
++      return 0;
++  if (cc->num >= cc->max)
++  {
++    cc->max *= 2;
++    safe_realloc (&cc->child, sizeof (anum_t) * cc->max);
++  }
++  cc->child[cc->num++] = anum;
++  return 0;
++}
++
++/* Fetch children of article with the Message-ID */
++int nntp_check_children (CONTEXT *ctx, const char *msgid)
++{
++  NNTP_DATA *nntp_data = ctx->data;
++  CHILD_CTX cc;
++  char buf[STRING];
++  int i, rc, quiet;
++  void *hc = NULL;
++
++  if (!nntp_data || !nntp_data->nserv)
++    return -1;
++  if (nntp_data->firstMessage > nntp_data->lastLoaded)
++    return 0;
++
++  /* init context */
++  cc.ctx = ctx;
++  cc.num = 0;
++  cc.max = 10;
++  cc.child = safe_malloc (sizeof (anum_t) * cc.max);
++
++  /* fetch numbers of child messages */
++  snprintf (buf, sizeof (buf), "XPAT References %d-%d *%s*\r\n",
++          nntp_data->firstMessage, nntp_data->lastLoaded, msgid);
++  rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
++                       fetch_children, &cc);
++  if (rc)
++  {
++    FREE (&cc.child);
++    if (rc > 0) {
++      if (mutt_strncmp ("500", buf, 3))
++      mutt_error ("XPAT: %s", buf);
++      else
++      mutt_error _("Unable to find child articles because server does not support XPAT command.");
++    }
++    return -1;
++  }
++
++  /* fetch all found messages */
++  quiet = ctx->quiet;
++  ctx->quiet = 1;
++#ifdef USE_HCACHE
++  hc = nntp_hcache_open (nntp_data);
++#endif
++  for (i = 0; i < cc.num; i++)
++  {
++    rc = nntp_fetch_headers (ctx, hc, cc.child[i], cc.child[i], 1);
++    if (rc < 0)
++      break;
++  }
++#ifdef USE_HCACHE
++  mutt_hcache_close (hc);
++#endif
++  ctx->quiet = quiet;
++  FREE (&cc.child);
++  return rc < 0 ? -1 : 0;
++}
++
++struct mx_ops mx_nntp_ops = {
++  .open = nntp_open_mailbox,
++  .open_append = NULL,
++  .close = nntp_close_mailbox,
++  .open_msg = nntp_open_message,
++  .close_msg = nntp_close_message,
++  .check = nntp_check_mailbox,
++  .commit_msg = NULL,
++  .open_new_msg = NULL,
++  .sync = nntp_sync_mailbox,
++};
+diff -udprP mutt-1.12.1.orig/nntp.h mutt-1.12.1/nntp.h
+--- mutt-1.12.1.orig/nntp.h    1970-01-01 03:00:00.000000000 +0300
++++ mutt-1.12.1/nntp.h 2019-08-11 20:31:26.142940552 +0300
+@@ -0,0 +1,165 @@
++/*
++ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
++ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
++ * Copyright (C) 2000-2019 Vsevolod Volkov <vvv@mutt.org.ua>
++ *
++ *     This program is free software; you can redistribute it and/or modify
++ *     it under the terms of the GNU General Public License as published by
++ *     the Free Software Foundation; either version 2 of the License, or
++ *     (at your option) any later version.
++ *
++ *     This program is distributed in the hope that it will be useful,
++ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *     GNU General Public License for more details.
++ *
++ *     You should have received a copy of the GNU General Public License
++ *     along with this program; if not, write to the Free Software
++ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef _NNTP_H_
++#define _NNTP_H_ 1
++
++#include "mutt_socket.h"
++#include "mailbox.h"
++#include "bcache.h"
++
++#if USE_HCACHE
++#include "hcache.h"
++#endif
++
++#include <time.h>
++#include <sys/types.h>
++#include <stdint.h>
++
++#define NNTP_PORT 119
++#define NNTP_SSL_PORT 563
++
++/* number of entries in article cache */
++#define NNTP_ACACHE_LEN 10
++
++/* article number type and format */
++#define anum_t uint32_t
++#define ANUM "%u"
++
++enum
++{
++  NNTP_NONE = 0,
++  NNTP_OK,
++  NNTP_BYE
++};
++
++typedef struct
++{
++  unsigned int hasCAPABILITIES : 1;
++  unsigned int hasSTARTTLS : 1;
++  unsigned int hasDATE : 1;
++  unsigned int hasLIST_NEWSGROUPS : 1;
++  unsigned int hasXGTITLE : 1;
++  unsigned int hasLISTGROUP : 1;
++  unsigned int hasLISTGROUPrange : 1;
++  unsigned int hasOVER : 1;
++  unsigned int hasXOVER : 1;
++  unsigned int use_tls : 3;
++  unsigned int status : 3;
++  unsigned int cacheable : 1;
++  unsigned int newsrc_modified : 1;
++  FILE *newsrc_fp;
++  char *newsrc_file;
++  char *authenticators;
++  char *overview_fmt;
++  off_t size;
++  time_t mtime;
++  time_t newgroups_time;
++  time_t check_time;
++  unsigned int groups_num;
++  unsigned int groups_max;
++  void **groups_list;
++  HASH *groups_hash;
++  CONNECTION *conn;
++} NNTP_SERVER;
++
++typedef struct
++{
++  anum_t first;
++  anum_t last;
++} NEWSRC_ENTRY;
++
++typedef struct
++{
++  unsigned int index;
++  char *path;
++} NNTP_ACACHE;
++
++typedef struct
++{
++  char *group;
++  char *desc;
++  anum_t firstMessage;
++  anum_t lastMessage;
++  anum_t lastLoaded;
++  anum_t lastCached;
++  anum_t unread;
++  unsigned int subscribed : 1;
++  unsigned int new : 1;
++  unsigned int allowed : 1;
++  unsigned int deleted : 1;
++  unsigned int newsrc_len;
++  NEWSRC_ENTRY *newsrc_ent;
++  NNTP_SERVER *nserv;
++  NNTP_ACACHE acache[NNTP_ACACHE_LEN];
++  body_cache_t *bcache;
++} NNTP_DATA;
++
++typedef struct
++{
++  anum_t article_num;
++  unsigned int parsed : 1;
++} NNTP_HEADER_DATA;
++
++#define NHDR(hdr) ((NNTP_HEADER_DATA*)((hdr)->data))
++
++/* internal functions */
++int nntp_add_group (char *, void *);
++int nntp_active_save_cache (NNTP_SERVER *);
++int nntp_check_new_groups (NNTP_SERVER *);
++int nntp_open_connection (NNTP_SERVER *);
++void nntp_newsrc_gen_entries (CONTEXT *);
++void nntp_bcache_update (NNTP_DATA *);
++void nntp_article_status (CONTEXT *, HEADER *, char *, anum_t);
++void nntp_group_unread_stat (NNTP_DATA *);
++void nntp_data_free (void *);
++void nntp_acache_free (NNTP_DATA *);
++void nntp_delete_group_cache (NNTP_DATA *);
++
++/* exposed interface */
++NNTP_SERVER *nntp_select_server (char *, int);
++NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *, char *);
++NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *, char *);
++NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *, char *);
++NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *, char *);
++int nntp_active_fetch (NNTP_SERVER *, unsigned int);
++int nntp_newsrc_update (NNTP_SERVER *);
++int nntp_post (const char *);
++int nntp_check_msgid (CONTEXT *, const char *);
++int nntp_check_children (CONTEXT *, const char *);
++int nntp_newsrc_parse (NNTP_SERVER *);
++void nntp_newsrc_close (NNTP_SERVER *);
++void nntp_buffer_buffy (BUFFER *);
++void nntp_buffer_expand_path (BUFFER *, ACCOUNT *);
++void nntp_clear_cache (NNTP_SERVER *);
++const char *nntp_format_str (char *, size_t, size_t, int, char, const char *,
++                           const char *, const char *, const char *,
++                           unsigned long, format_flag);
++
++NNTP_SERVER *CurrentNewsSrv INITVAL (NULL);
++
++#ifdef USE_HCACHE
++header_cache_t *nntp_hcache_open (NNTP_DATA *);
++void nntp_hcache_update (NNTP_DATA *, header_cache_t *);
++#endif
++
++extern struct mx_ops mx_nntp_ops;
++
++#endif /* _NNTP_H_ */
+diff -udprP mutt-1.12.1.orig/pager.c mutt-1.12.1/pager.c
+--- mutt-1.12.1.orig/pager.c   2019-06-15 18:57:01.000000000 +0300
++++ mutt-1.12.1/pager.c        2019-08-11 20:31:26.143940536 +0300
+@@ -1118,6 +1118,11 @@ fill_buffer (FILE *f, LOFF_T *last_pos,
+   return b_read;
+ }
++#ifdef USE_NNTP
++#include "mx.h"
++#include "nntp.h"
++#endif
++
+ static int format_line (struct line_t **lineInfo, int n, unsigned char *buf,
+                       int flags, ansi_attr *pa, int cnt,
+@@ -1583,6 +1588,15 @@ static const struct mapping_t PagerHelpE
+   { N_("Next"),       OP_MAIN_NEXT_UNDELETED },
+   { NULL,     0 }
+ };
++#ifdef USE_NNTP
++static struct mapping_t PagerNewsHelpExtra[] = {
++  { N_("Post"),     OP_POST },
++  { N_("Followup"), OP_FOLLOWUP },
++  { N_("Del"),      OP_DELETE },
++  { N_("Next"),     OP_MAIN_NEXT_UNDELETED },
++  { NULL,           0 }
++};
++#endif
+ void mutt_clear_pager_position (void)
+ {
+@@ -1921,6 +1935,10 @@ mutt_pager (const char *banner, const ch
+   pager_redraw_data_t rd;
++#ifdef USE_NNTP
++  char *followup_to;
++#endif
++
+   if (!(flags & MUTT_SHOWCOLOR))
+     flags |= MUTT_SHOWFLAT;
+@@ -1970,7 +1988,11 @@ mutt_pager (const char *banner, const ch
+   mutt_buffer_strcpy (helpstr, buffer);
+   if (IsHeader (extra))
+   {
+-    mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER, PagerHelpExtra);
++    mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER,
++#ifdef USE_NNTP
++      (Context && (Context->magic == MUTT_NNTP)) ? PagerNewsHelpExtra :
++#endif
++      PagerHelpExtra);
+     mutt_buffer_addch (helpstr, ' ');
+     mutt_buffer_addstr (helpstr, buffer);
+   }
+@@ -2653,6 +2675,60 @@ search_next:
+       pager_menu->redraw = REDRAW_FULL;
+       break;
++#ifdef USE_NNTP
++      case OP_POST:
++      CHECK_MODE(IsHeader (extra) && !IsAttach (extra));
++      CHECK_ATTACH;
++      if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
++          !((NNTP_DATA *)extra->ctx->data)->allowed &&
++          query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
++        break;
++      ci_send_message (SENDNEWS, NULL, NULL, extra->ctx, NULL);
++      pager_menu->redraw = REDRAW_FULL;
++      break;
++
++      case OP_FORWARD_TO_GROUP:
++      CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
++      CHECK_ATTACH;
++      if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
++          !((NNTP_DATA *)extra->ctx->data)->allowed &&
++          query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
++        break;
++      if (IsMsgAttach (extra))
++        mutt_attach_forward (extra->fp, extra->hdr, extra->actx,
++                             extra->bdy, SENDNEWS);
++      else
++        ci_send_message (SENDNEWS|SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
++      pager_menu->redraw = REDRAW_FULL;
++      break;
++
++      case OP_FOLLOWUP:
++      CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
++      CHECK_ATTACH;
++
++      if (IsMsgAttach (extra))
++        followup_to = extra->bdy->hdr->env->followup_to;
++      else
++        followup_to = extra->hdr->env->followup_to;
++
++      if (!followup_to || mutt_strcasecmp (followup_to, "poster") ||
++          query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != MUTT_YES)
++      {
++        if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
++            !((NNTP_DATA *)extra->ctx->data)->allowed &&
++            query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
++          break;
++        if (IsMsgAttach (extra))
++          mutt_attach_reply (extra->fp, extra->hdr, extra->actx,
++                             extra->bdy, SENDNEWS|SENDREPLY);
++        else
++          ci_send_message (SENDNEWS|SENDREPLY, NULL, NULL,
++                           extra->ctx, extra->hdr);
++        pager_menu->redraw = REDRAW_FULL;
++        break;
++      }
++#endif
++
+       case OP_REPLY:
+       case OP_GROUP_REPLY:
+       case OP_GROUP_CHAT_REPLY:
+@@ -2689,7 +2765,7 @@ search_next:
+         CHECK_ATTACH;
+         if (IsMsgAttach (extra))
+         mutt_attach_forward (extra->fp, extra->hdr, extra->actx,
+-                             extra->bdy);
++                             extra->bdy, 0);
+         else
+         ci_send_message (SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
+       pager_menu->redraw = REDRAW_FULL;
+diff -udprP mutt-1.12.1.orig/parse.c mutt-1.12.1/parse.c
+--- mutt-1.12.1.orig/parse.c   2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/parse.c        2019-08-11 20:31:26.143940536 +0300
+@@ -94,7 +94,7 @@ char *mutt_read_rfc822_line (FILE *f, ch
+   /* not reached */
+ }
+-static LIST *mutt_parse_references (char *s, int in_reply_to)
++LIST *mutt_parse_references (char *s, int in_reply_to)
+ {
+   LIST *t, *lst = NULL;
+   char *m;
+@@ -1122,6 +1122,17 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
+         e->from = rfc822_parse_adrlist (e->from, p);
+         matched = 1;
+       }
++#ifdef USE_NNTP
++      else if (!mutt_strcasecmp (line+1, "ollowup-to"))
++      {
++        if (!e->followup_to)
++        {
++          mutt_remove_trailing_ws (p);
++          e->followup_to = safe_strdup (mutt_skip_whitespace (p));
++        }
++        matched = 1;
++      }
++#endif
+       break;
+     case 'i':
+@@ -1206,6 +1217,27 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
+       }
+       break;
++#ifdef USE_NNTP
++    case 'n':
++      if (!mutt_strcasecmp (line + 1, "ewsgroups"))
++      {
++        FREE (&e->newsgroups);
++        mutt_remove_trailing_ws (p);
++        e->newsgroups = safe_strdup (mutt_skip_whitespace (p));
++        matched = 1;
++      }
++      break;
++#endif
++
++    case 'o':
++      /* field `Organization:' saves only for pager! */
++      if (!mutt_strcasecmp (line + 1, "rganization"))
++      {
++        if (!e->organization && mutt_strcasecmp (p, "unknown"))
++          e->organization = safe_strdup (p);
++      }
++      break;
++
+     case 'r':
+       if (!ascii_strcasecmp (line + 1, "eferences"))
+       {
+@@ -1318,6 +1350,20 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
+         e->x_label = safe_strdup(p);
+         matched = 1;
+       }
++#ifdef USE_NNTP
++      else if (!mutt_strcasecmp (line + 1, "-comment-to"))
++      {
++        if (!e->x_comment_to)
++          e->x_comment_to = safe_strdup (p);
++        matched = 1;
++      }
++      else if (!mutt_strcasecmp (line + 1, "ref"))
++      {
++        if (!e->xref)
++          e->xref = safe_strdup (p);
++        matched = 1;
++      }
++#endif
+     default:
+       break;
+diff -udprP mutt-1.12.1.orig/pattern.c mutt-1.12.1/pattern.c
+--- mutt-1.12.1.orig/pattern.c 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/pattern.c      2019-08-11 20:31:26.143940536 +0300
+@@ -94,6 +94,9 @@ Flags[] =
+   { 'U', MUTT_UNREAD,         0,              NULL },
+   { 'v', MUTT_COLLAPSED,              0,              NULL },
+   { 'V', MUTT_CRYPT_VERIFIED, 0,              NULL },
++#ifdef USE_NNTP
++  { 'w', MUTT_NEWSGROUPS,     0,              eat_regexp },
++#endif
+   { 'x', MUTT_REFERENCE,              0,              eat_regexp },
+   { 'X', MUTT_MIMEATTACH,             0,              eat_range },
+   { 'y', MUTT_XLABEL,         0,              eat_regexp },
+@@ -1412,6 +1415,10 @@ mutt_pattern_exec (struct pattern_t *pat
+       return (pat->not ^ match_mime_content_type (pat, ctx, h));
+     case MUTT_UNREFERENCED:
+       return (pat->not ^ (h->thread && !h->thread->child));
++#ifdef USE_NNTP
++    case MUTT_NEWSGROUPS:
++      return (pat->not ^ (h->env->newsgroups && patmatch (pat, h->env->newsgroups) == 0));
++#endif
+   }
+   mutt_error (_("error: unknown op %d (report this error)."), pat->op);
+   return (-1);
+@@ -1497,6 +1504,7 @@ int mutt_pattern_func (int op, char *pro
+   buf = mutt_buffer_pool_get ();
+   mutt_buffer_strcpy (buf, NONULL (Context->pattern));
++  if (prompt || op != MUTT_LIMIT)
+   if (mutt_get_field (prompt, buf->data, buf->dsize, MUTT_PATTERN | MUTT_CLEAR) != 0 ||
+       !(mutt_b2s (buf)[0]))
+   {
+diff -udprP mutt-1.12.1.orig/po/POTFILES.in mutt-1.12.1/po/POTFILES.in
+--- mutt-1.12.1.orig/po/POTFILES.in    2017-12-18 22:31:37.000000000 +0200
++++ mutt-1.12.1/po/POTFILES.in 2019-08-11 20:31:26.143940536 +0300
+@@ -47,6 +47,8 @@ mutt_ssl_gnutls.c
+ mutt_tunnel.c
+ muttlib.c
+ mx.c
++newsrc.c
++nntp.c
+ pager.c
+ parse.c
+ pattern.c
+diff -udprP mutt-1.12.1.orig/postpone.c mutt-1.12.1/postpone.c
+--- mutt-1.12.1.orig/postpone.c        2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/postpone.c     2019-08-11 20:31:26.143940536 +0300
+@@ -125,15 +125,26 @@ int mutt_num_postponed (int force)
+   if (LastModify < st.st_mtime)
+   {
++#ifdef USE_NNTP
++    int optnews = option (OPTNEWS);
++#endif
+     LastModify = st.st_mtime;
+     if (access (Postponed, R_OK | F_OK) != 0)
+       return (PostCount = 0);
++#ifdef USE_NNTP
++    if (optnews)
++      unset_option (OPTNEWS);
++#endif
+     if (mx_open_mailbox (Postponed, MUTT_NOSORT | MUTT_QUIET, &ctx) == NULL)
+       PostCount = 0;
+     else
+       PostCount = ctx.msgcount;
+     mx_fastclose_mailbox (&ctx);
++#ifdef USE_NNTP
++    if (optnews)
++      set_option (OPTNEWS);
++#endif
+   }
+   return (PostCount);
+diff -udprP mutt-1.12.1.orig/protos.h mutt-1.12.1/protos.h
+--- mutt-1.12.1.orig/protos.h  2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/protos.h       2019-08-11 20:31:26.144940521 +0300
+@@ -103,6 +103,7 @@ HASH *mutt_make_id_hash (CONTEXT *);
+ HASH *mutt_make_subj_hash (CONTEXT *);
+ LIST *mutt_make_references(ENVELOPE *e);
++LIST *mutt_parse_references (char *, int);
+ char *mutt_read_rfc822_line (FILE *, char *, size_t *);
+ ENVELOPE *mutt_read_rfc822_header (FILE *, HEADER *, short, short);
+diff -udprP mutt-1.12.1.orig/recvattach.c mutt-1.12.1/recvattach.c
+--- mutt-1.12.1.orig/recvattach.c      2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/recvattach.c   2019-08-11 20:31:26.144940521 +0300
+@@ -1226,6 +1226,15 @@ void mutt_view_attachments (HEADER *hdr)
+       }
+ #endif
++#ifdef USE_NNTP
++      if (Context->magic == MUTT_NNTP)
++      {
++        mutt_flushinp ();
++        mutt_error _("Can't delete attachment from news server.");
++        break;
++      }
++#endif
++
+         if (WithCrypto && (hdr->security & ENCRYPT))
+         {
+           mutt_message _(
+@@ -1320,7 +1329,7 @@ void mutt_view_attachments (HEADER *hdr)
+       case OP_FORWARD_MESSAGE:
+         CHECK_ATTACH;
+         mutt_attach_forward (CURATTACH->fp, hdr, actx,
+-                           menu->tagprefix ? NULL : CURATTACH->content);
++                           menu->tagprefix ? NULL : CURATTACH->content, 0);
+         menu->redraw = REDRAW_FULL;
+         break;
+@@ -1331,6 +1340,28 @@ void mutt_view_attachments (HEADER *hdr)
+         menu->redraw = REDRAW_FULL;
+         break;
++#ifdef USE_NNTP
++      case OP_FORWARD_TO_GROUP:
++      CHECK_ATTACH;
++      mutt_attach_forward (CURATTACH->fp, hdr, actx,
++                           menu->tagprefix ? NULL : CURATTACH->content, SENDNEWS);
++      menu->redraw = REDRAW_FULL;
++      break;
++
++      case OP_FOLLOWUP:
++      CHECK_ATTACH;
++
++      if (!hdr->env->followup_to ||
++          mutt_strcasecmp (hdr->env->followup_to, "poster") ||
++          query_quadoption (OPT_FOLLOWUPTOPOSTER, _("Reply by mail as poster prefers?")) != MUTT_YES)
++      {
++        mutt_attach_reply (CURATTACH->fp, hdr, actx,
++                           menu->tagprefix ? NULL : CURATTACH->content, SENDNEWS|SENDREPLY);
++        menu->redraw = REDRAW_FULL;
++        break;
++      }
++#endif
++
+       case OP_REPLY:
+       case OP_GROUP_REPLY:
+       case OP_GROUP_CHAT_REPLY:
+diff -udprP mutt-1.12.1.orig/recvcmd.c mutt-1.12.1/recvcmd.c
+--- mutt-1.12.1.orig/recvcmd.c 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/recvcmd.c      2019-08-11 20:31:26.144940521 +0300
+@@ -398,7 +398,7 @@ static BODY ** copy_problematic_attachme
+ static void attach_forward_bodies (FILE * fp, HEADER * hdr,
+                                  ATTACH_CONTEXT *actx,
+                                  BODY * cur,
+-                                 short nattach)
++                                 short nattach, int flags)
+ {
+   short i;
+   short mime_fwd_all = 0;
+@@ -554,7 +554,7 @@ static void attach_forward_bodies (FILE
+   tmpfp = NULL;
+   /* now that we have the template, send it. */
+-  ci_send_message (0, tmphdr, tmpbody, NULL, parent_hdr);
++  ci_send_message (flags, tmphdr, tmpbody, NULL, parent_hdr);
+   return;
+ bail:
+@@ -581,7 +581,7 @@ bail:
+  */
+ static void attach_forward_msgs (FILE * fp, HEADER * hdr,
+-                                 ATTACH_CONTEXT *actx, BODY * cur)
++                                 ATTACH_CONTEXT *actx, BODY * cur, int flags)
+ {
+   HEADER *curhdr = NULL;
+   HEADER *tmphdr;
+@@ -686,23 +686,23 @@ static void attach_forward_msgs (FILE *
+   else
+     mutt_free_header (&tmphdr);
+-  ci_send_message (0, tmphdr, *tmpbody ? tmpbody : NULL,
++  ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL,
+                  NULL, curhdr);
+ }
+ void mutt_attach_forward (FILE * fp, HEADER * hdr,
+-                        ATTACH_CONTEXT *actx, BODY * cur)
++                        ATTACH_CONTEXT *actx, BODY * cur, int flags)
+ {
+   short nattach;
+   if (check_all_msg (actx, cur, 0) == 0)
+-    attach_forward_msgs (fp, hdr, actx, cur);
++    attach_forward_msgs (fp, hdr, actx, cur, flags);
+   else
+   {
+     nattach = count_tagged (actx);
+-    attach_forward_bodies (fp, hdr, actx, cur, nattach);
++    attach_forward_bodies (fp, hdr, actx, cur, nattach, flags);
+   }
+ }
+@@ -795,6 +795,17 @@ attach_reply_envelope_defaults (ENVELOPE
+     return -1;
+   }
++#ifdef USE_NNTP
++  if ((flags & SENDNEWS))
++  {
++    /* in case followup set Newsgroups: with Followup-To: if it present */
++    if (!env->newsgroups && curenv &&
++      mutt_strcasecmp (curenv->followup_to, "poster"))
++      env->newsgroups = safe_strdup (curenv->followup_to);
++  }
++  else
++#endif
++ {
+   if (parent)
+   {
+     if (mutt_fetch_recips (env, curenv, flags) == -1)
+@@ -817,6 +828,7 @@ attach_reply_envelope_defaults (ENVELOPE
+   }
+   mutt_fix_reply_recipients (env);
++ }
+   mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
+   if (parent)
+@@ -879,6 +891,13 @@ void mutt_attach_reply (FILE * fp, HEADE
+   char prefix[SHORT_STRING];
+   int rc;
++#ifdef USE_NNTP
++  if (flags & SENDNEWS)
++    set_option (OPTNEWSSEND);
++  else
++    unset_option (OPTNEWSSEND);
++#endif
++
+   if (check_all_msg (actx, cur, 0) == -1)
+   {
+     nattach = count_tagged (actx);
+diff -udprP mutt-1.12.1.orig/rfc2047.c mutt-1.12.1/rfc2047.c
+--- mutt-1.12.1.orig/rfc2047.c 2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/rfc2047.c      2019-08-11 20:31:26.144940521 +0300
+@@ -633,6 +633,9 @@ void rfc2047_encode_envelope (ENVELOPE *
+   rfc2047_encode_adrlist (e->mail_followup_to, "Mail-Followup-To");
+   rfc2047_encode_adrlist (e->sender, "Sender");
+   rfc2047_encode_string (&e->x_label);
++#ifdef USE_NNTP
++  if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
++#endif
+   rfc2047_encode_string (&e->subject);
+ }
+diff -udprP mutt-1.12.1.orig/send.c mutt-1.12.1/send.c
+--- mutt-1.12.1.orig/send.c    2019-06-14 18:57:33.000000000 +0300
++++ mutt-1.12.1/send.c 2019-08-11 20:31:26.144940521 +0300
+@@ -46,6 +46,11 @@
+ #include <sys/types.h>
+ #include <utime.h>
++#ifdef USE_NNTP
++#include "nntp.h"
++#include "mx.h"
++#endif
++
+ #ifdef MIXMASTER
+ #include "remailer.h"
+ #endif
+@@ -217,17 +222,51 @@ static int edit_address (ADDRESS **a, /*
+   return 0;
+ }
+-static int edit_envelope (ENVELOPE *en)
++static int edit_envelope (ENVELOPE *en, int flags)
+ {
+   char buf[HUGE_STRING];
+   LIST *uh = UserHeader;
++#ifdef USE_NNTP
++  if (option (OPTNEWSSEND))
++  {
++    if (en->newsgroups)
++      strfcpy (buf, en->newsgroups, sizeof (buf));
++    else
++      buf[0] = 0;
++    if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) != 0)
++      return (-1);
++    FREE (&en->newsgroups);
++    en->newsgroups = safe_strdup (buf);
++
++    if (en->followup_to)
++      strfcpy (buf, en->followup_to, sizeof (buf));
++    else
++      buf[0] = 0;
++    if (option (OPTASKFOLLOWUP) && mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) != 0)
++      return (-1);
++    FREE (&en->followup_to);
++    en->followup_to = safe_strdup (buf);
++
++    if (en->x_comment_to)
++      strfcpy (buf, en->x_comment_to, sizeof (buf));
++    else
++      buf[0] = 0;
++    if (option (OPTXCOMMENTTO) && option (OPTASKXCOMMENTTO) && mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) != 0)
++      return (-1);
++    FREE (&en->x_comment_to);
++    en->x_comment_to = safe_strdup (buf);
++  }
++  else
++#endif
++ {
+   if (edit_address (&en->to, _("To: ")) == -1 || en->to == NULL)
+     return (-1);
+   if (option (OPTASKCC) && edit_address (&en->cc, _("Cc: ")) == -1)
+     return (-1);
+   if (option (OPTASKBCC) && edit_address (&en->bcc, _("Bcc: ")) == -1)
+     return (-1);
++ }
+   if (en->subject)
+   {
+@@ -262,6 +301,14 @@ static int edit_envelope (ENVELOPE *en)
+   return 0;
+ }
++#ifdef USE_NNTP
++char *nntp_get_header (const char *s)
++{
++  SKIPWS (s);
++  return safe_strdup (s);
++}
++#endif
++
+ static void process_user_recips (ENVELOPE *env)
+ {
+   LIST *uh = UserHeader;
+@@ -274,6 +321,14 @@ static void process_user_recips (ENVELOP
+       env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3);
+     else if (ascii_strncasecmp ("bcc:", uh->data, 4) == 0)
+       env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4);
++#ifdef USE_NNTP
++    else if (ascii_strncasecmp ("newsgroups:", uh->data, 11) == 0)
++      env->newsgroups = nntp_get_header (uh->data + 11);
++    else if (ascii_strncasecmp ("followup-to:", uh->data, 12) == 0)
++      env->followup_to = nntp_get_header (uh->data + 12);
++    else if (ascii_strncasecmp ("x-comment-to:", uh->data, 13) == 0)
++      env->x_comment_to = nntp_get_header (uh->data + 13);
++#endif
+   }
+ }
+@@ -313,6 +368,12 @@ static void process_user_header (ENVELOP
+     else if (ascii_strncasecmp ("to:", uh->data, 3) != 0 &&
+            ascii_strncasecmp ("cc:", uh->data, 3) != 0 &&
+            ascii_strncasecmp ("bcc:", uh->data, 4) != 0 &&
++#ifdef USE_NNTP
++           ascii_strncasecmp ("newsgroups:", uh->data, 11) != 0 &&
++           ascii_strncasecmp ("followup-to:", uh->data, 12) != 0 &&
++           ascii_strncasecmp ("x-comment-to:", uh->data, 13) != 0 &&
++#endif
++           ascii_strncasecmp ("supersedes:", uh->data, 11) != 0 &&
+            ascii_strncasecmp ("subject:", uh->data, 8) != 0 &&
+            ascii_strncasecmp ("return-path:", uh->data, 12) != 0)
+     {
+@@ -775,6 +836,10 @@ void mutt_add_to_reference_headers (ENVE
+   if (pp) *pp = p;
+   if (qq) *qq = q;
++#ifdef USE_NNTP
++  if (option (OPTNEWSSEND) && option (OPTXCOMMENTTO) && curenv->from)
++    env->x_comment_to = safe_strdup (mutt_get_name (curenv->from));
++#endif
+ }
+ static void
+@@ -837,6 +902,16 @@ envelope_defaults (ENVELOPE *env, CONTEX
+   if (flags & (SENDREPLY|SENDTOSENDER))
+   {
++#ifdef USE_NNTP
++    if ((flags & SENDNEWS))
++    {
++      /* in case followup set Newsgroups: with Followup-To: if it present */
++      if (!env->newsgroups && curenv &&
++        mutt_strcasecmp (curenv->followup_to, "poster"))
++      env->newsgroups = safe_strdup (curenv->followup_to);
++    }
++    else
++#endif
+     if (tag)
+     {
+       HEADER *h;
+@@ -982,7 +1057,18 @@ void mutt_set_followup_to (ENVELOPE *e)
+    * it hasn't already been set
+    */
+-  if (option (OPTFOLLOWUPTO) && !e->mail_followup_to)
++  if (!option (OPTFOLLOWUPTO))
++    return;
++#ifdef USE_NNTP
++  if (option (OPTNEWSSEND))
++  {
++    if (!e->followup_to && e->newsgroups && (strrchr (e->newsgroups, ',')))
++      e->followup_to = safe_strdup (e->newsgroups);
++    return;
++  }
++#endif
++
++  if (!e->mail_followup_to)
+   {
+     if (mutt_is_list_cc (0, e->to, e->cc))
+     {
+@@ -1148,6 +1234,9 @@ static int send_message (HEADER *msg)
+ #endif
+ #if USE_SMTP
++#ifdef USE_NNTP
++  if (!option (OPTNEWSSEND))
++#endif
+   if (SmtpUrl)
+     return mutt_smtp_send (msg->env->from, msg->env->to, msg->env->cc,
+                            msg->env->bcc, tempfile,
+@@ -1563,6 +1652,13 @@ ci_send_message (int flags,             /* send mod
+   int rv = -1;
++#ifdef USE_NNTP
++  if (flags & SENDNEWS)
++    set_option (OPTNEWSSEND);
++  else
++    unset_option (OPTNEWSSEND);
++#endif
++
+   if (!flags && !msg && quadoption (OPT_RECALL) != MUTT_NO &&
+       mutt_num_postponed (1))
+   {
+@@ -1702,12 +1798,33 @@ ci_send_message (int flags,            /* send mod
+     if (flags & SENDREPLY)
+       mutt_fix_reply_recipients (msg->env);
++#ifdef USE_NNTP
++    if ((flags & SENDNEWS) && ctx && ctx->magic == MUTT_NNTP && !msg->env->newsgroups)
++      msg->env->newsgroups = safe_strdup (((NNTP_DATA *)ctx->data)->group);
++#endif
++
+     if (! (flags & (SENDMAILX|SENDBATCH)) &&
+       ! (option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
+       ! ((flags & SENDREPLY) && option (OPTFASTREPLY)))
+     {
+-      if (edit_envelope (msg->env) == -1)
++      if (edit_envelope (msg->env, flags) == -1)
+       goto cleanup;
++#ifdef USE_NNTP
++      /*
++       * If postponed message is a news article, it have
++       * a "Newsgroups:" header line, then set appropriate flag.
++       */
++      if (msg->env->newsgroups)
++      {
++      flags |= SENDNEWS;
++      set_option (OPTNEWSSEND);
++      }
++      else
++      {
++      flags &= ~SENDNEWS;
++      unset_option (OPTNEWSSEND);
++      }
++#endif
+     }
+     /* the from address must be set here regardless of whether or not
+@@ -2016,6 +2133,11 @@ main_loop:
+     if (i == -1)
+     {
+       /* abort */
++#ifdef USE_NNTP
++      if (flags & SENDNEWS)
++      mutt_message _("Article not posted.");
++      else
++#endif
+       mutt_message _("Mail not sent.");
+       goto cleanup;
+     }
+@@ -2029,6 +2151,9 @@ main_loop:
+     }
+   }
++#ifdef USE_NNTP
++  if (!(flags & SENDNEWS))
++#endif
+   if (!has_recips (msg->env->to) && !has_recips (msg->env->cc) &&
+       !has_recips (msg->env->bcc))
+   {
+@@ -2062,6 +2187,19 @@ main_loop:
+       mutt_error _("No subject specified.");
+     goto main_loop;
+   }
++#ifdef USE_NNTP
++  if ((flags & SENDNEWS) && !msg->env->subject)
++  {
++    mutt_error _("No subject specified.");
++    goto main_loop;
++  }
++
++  if ((flags & SENDNEWS) && !msg->env->newsgroups)
++  {
++    mutt_error _("No newsgroup specified.");
++    goto main_loop;
++  }
++#endif
+   /* Scan for a mention of an attachment in the message body and
+    * prompt if there is none. */
+@@ -2184,7 +2322,12 @@ main_loop:
+   if (!option (OPTNOCURSES) && ! (flags & SENDMAILX))
+   {
+-    mutt_message (i == 0 ? _("Mail sent.") : _("Sending in background."));
++    mutt_message (i != 0 ? _("Sending in background.") :
++#ifdef USE_NNTP
++                (flags & SENDNEWS) ? _("Article posted.") : _("Mail sent."));
++#else
++                _("Mail sent."));
++#endif
+     mutt_sleep (0);
+   }
+diff -udprP mutt-1.12.1.orig/sendlib.c mutt-1.12.1/sendlib.c
+--- mutt-1.12.1.orig/sendlib.c 2019-06-14 18:57:30.000000000 +0300
++++ mutt-1.12.1/sendlib.c      2019-08-11 20:31:26.145940506 +0300
+@@ -47,6 +47,10 @@
+ #include <sys/wait.h>
+ #include <fcntl.h>
++#ifdef USE_NNTP
++#include "nntp.h"
++#endif
++
+ #ifdef HAVE_SYSEXITS_H
+ #include <sysexits.h>
+ #else /* Make sure EX_OK is defined <philiph@pobox.com> */
+@@ -1590,6 +1594,14 @@ void mutt_write_references (LIST *r, FIL
+ {
+   LIST **ref = NULL;
+   int refcnt = 0, refmax = 0;
++  int multiline = 1;
++  int space = 0;
++
++  if (trim < 0)
++  {
++    trim = -trim;
++    multiline = 0;
++  }
+   for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
+   {
+@@ -1600,9 +1612,11 @@ void mutt_write_references (LIST *r, FIL
+   while (refcnt-- > 0)
+   {
+-    fputc (' ', f);
++    if (multiline || space)
++      fputc (' ', f);
++    space = 1;
+     fputs (ref[refcnt]->data, f);
+-    if (refcnt >= 1)
++    if (multiline && refcnt >= 1)
+       fputc ('\n', f);
+   }
+@@ -2018,6 +2032,9 @@ int mutt_write_rfc822_header (FILE *fp,
+     mutt_write_address_list (env->to, fp, 4, 0);
+   }
+   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
++#ifdef USE_NNTP
++  if (!option (OPTNEWSSEND))
++#endif
+     fputs ("To: \n", fp);
+   if (env->cc)
+@@ -2026,6 +2043,9 @@ int mutt_write_rfc822_header (FILE *fp,
+     mutt_write_address_list (env->cc, fp, 4, 0);
+   }
+   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
++#ifdef USE_NNTP
++  if (!option (OPTNEWSSEND))
++#endif
+     fputs ("Cc: \n", fp);
+   if (env->bcc)
+@@ -2039,8 +2059,28 @@ int mutt_write_rfc822_header (FILE *fp,
+     }
+   }
+   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
++#ifdef USE_NNTP
++  if (!option (OPTNEWSSEND))
++#endif
+     fputs ("Bcc: \n", fp);
++#ifdef USE_NNTP
++  if (env->newsgroups)
++    fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
++  else if (mode == 1 && option (OPTNEWSSEND))
++    fputs ("Newsgroups: \n", fp);
++
++  if (env->followup_to)
++    fprintf (fp, "Followup-To: %s\n", env->followup_to);
++  else if (mode == 1 && option (OPTNEWSSEND))
++    fputs ("Followup-To: \n", fp);
++
++  if (env->x_comment_to)
++    fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
++  else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
++    fputs ("X-Comment-To: \n", fp);
++#endif
++
+   if (env->subject)
+   {
+     if (hide_protected_subject &&
+@@ -2066,6 +2106,9 @@ int mutt_write_rfc822_header (FILE *fp,
+     fputs ("Reply-To: \n", fp);
+   if (env->mail_followup_to)
++#ifdef USE_NNTP
++  if (!option (OPTNEWSSEND))
++#endif
+   {
+     fputs ("Mail-Followup-To: ", fp);
+     mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
+@@ -2415,6 +2458,24 @@ mutt_invoke_sendmail (ADDRESS *from,    /*
+   size_t extra_argslen = 0, extra_argsmax = 0;
+   int i;
++#ifdef USE_NNTP
++  if (option (OPTNEWSSEND))
++  {
++    char cmd[LONG_STRING];
++
++    mutt_FormatString (cmd, sizeof (cmd), 0, MuttIndexWindow->cols,
++                     NONULL (Inews), nntp_format_str, 0, 0);
++    if (!*cmd)
++    {
++      i = nntp_post (msg);
++      unlink (msg);
++      return i;
++    }
++
++    s = safe_strdup (cmd);
++  }
++#endif
++
+   /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
+   if (!s)
+   {
+@@ -2464,6 +2525,10 @@ mutt_invoke_sendmail (ADDRESS *from,    /*
+     }
+   }
++#ifdef USE_NNTP
++  if (!option (OPTNEWSSEND))
++  {
++#endif
+   if (eightbit && option (OPTUSE8BITMIME))
+     args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
+@@ -2497,6 +2562,9 @@ mutt_invoke_sendmail (ADDRESS *from,     /*
+   args = add_args (args, &argslen, &argsmax, to);
+   args = add_args (args, &argslen, &argsmax, cc);
+   args = add_args (args, &argslen, &argsmax, bcc);
++#ifdef USE_NNTP
++  }
++#endif
+   if (argslen == argsmax)
+     safe_realloc (&args, sizeof (char *) * (++argsmax));
+@@ -2689,6 +2757,10 @@ int mutt_bounce_message (FILE *fp, HEADE
+   }
+   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
++#ifdef USE_NNTP
++  unset_option (OPTNEWSSEND);
++#endif
++
+   /*
+    * prepare recipient list. idna conversion appears to happen before this
+    * function is called, since the user receives confirmation of the address
+diff -udprP mutt-1.12.1.orig/sort.c mutt-1.12.1/sort.c
+--- mutt-1.12.1.orig/sort.c    2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/sort.c 2019-08-11 20:31:26.145940506 +0300
+@@ -24,6 +24,11 @@
+ #include "sort.h"
+ #include "mutt_idna.h"
++#ifdef USE_NNTP
++#include "mx.h"
++#include "nntp.h"
++#endif
++
+ #include <stdlib.h>
+ #include <string.h>
+ #include <ctype.h>
+@@ -155,6 +160,17 @@ static int compare_order (const void *a,
+   HEADER **ha = (HEADER **) a;
+   HEADER **hb = (HEADER **) b;
++#ifdef USE_NNTP
++  if (Context && Context->magic == MUTT_NNTP)
++  {
++    anum_t na = NHDR (*ha)->article_num;
++    anum_t nb = NHDR (*hb)->article_num;
++    int result = na == nb ? 0 : na > nb ? 1 : -1;
++    AUXSORT (result, a, b);
++    return (SORTCODE (result));
++  }
++  else
++#endif
+   /* no need to auxsort because you will never have equality here */
+   return (SORTCODE ((*ha)->index - (*hb)->index));
+ }
+diff -udprP mutt-1.12.1.orig/url.c mutt-1.12.1/url.c
+--- mutt-1.12.1.orig/url.c     2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/url.c  2019-08-11 20:31:26.145940506 +0300
+@@ -40,6 +40,8 @@ static const struct mapping_t UrlMap[] =
+   { "imaps",  U_IMAPS },
+   { "pop",    U_POP },
+   { "pops",   U_POPS },
++  { "news",   U_NNTP },
++  { "snews",  U_NNTPS },
+   { "mailto", U_MAILTO },
+   { "smtp",     U_SMTP },
+   { "smtps",    U_SMTPS },
+@@ -243,7 +245,7 @@ int url_ciss_tobuffer (ciss_url_t* ciss,
+     if (!(flags & U_PATH))
+       mutt_buffer_addstr (dest, "//");
+-    if (ciss->user)
++    if (ciss->user && (ciss->user[0] || !(flags & U_PATH)))
+     {
+       char u[STRING];
+       url_pct_encode (u, sizeof (u), ciss->user);
+diff -udprP mutt-1.12.1.orig/url.h mutt-1.12.1/url.h
+--- mutt-1.12.1.orig/url.h     2019-05-25 19:22:39.000000000 +0300
++++ mutt-1.12.1/url.h  2019-08-11 20:31:26.145940506 +0300
+@@ -8,6 +8,8 @@ typedef enum url_scheme
+   U_POPS,
+   U_IMAP,
+   U_IMAPS,
++  U_NNTP,
++  U_NNTPS,
+   U_SMTP,
+   U_SMTPS,
+   U_MAILTO,
This page took 0.565202 seconds and 4 git commands to generate.