]> git.pld-linux.org Git - packages/mutt.git/blame - mutt-vvv.nntp.patch
- updated to 2.1.4
[packages/mutt.git] / mutt-vvv.nntp.patch
CommitLineData
95768aa9 1WARNING: Run the following script before configure:
2
3aclocal -I m4
4autoheader
5automake --foreign
6autoconf
7
8--
9Vsevolod Volkov <vvv@mutt.org.ua>
10
11
32845784
AG
12diff -udprP mutt-1.12.1.orig/ChangeLog.nntp mutt-1.12.1/ChangeLog.nntp
13--- mutt-1.12.1.orig/ChangeLog.nntp 1970-01-01 03:00:00.000000000 +0300
14+++ mutt-1.12.1/ChangeLog.nntp 2019-08-11 20:31:26.130940734 +0300
15@@ -0,0 +1,464 @@
16+* Sun Aug 11 2019 Vsevolod Volkov <vvv@mutt.org.ua>
17+- update to 1.12.1
18+
19+* Sun Aug 11 2019 Vsevolod Volkov <vvv@mutt.org.ua>
20+- update to 1.12.0
21+
22+* Sun Dec 9 2018 Vsevolod Volkov <vvv@mutt.org.ua>
23+- update to 1.11.1
24+
25+* Sun Dec 9 2018 Vsevolod Volkov <vvv@mutt.org.ua>
26+- update to 1.11.0
27+- FREE(&hdata) replaced with mutt_hcache_free(&hdata)
28+ (thanks to Steffen Wolf)
29+- fixed SIGSEGV when pressing OP_FOLLOWUP in attachment menu
30+ (thanks to Moritz Barsnick)
31+
c02f3a2c
AG
32+* Sat Jun 16 2018 Vsevolod Volkov <vvv@mutt.org.ua>
33+- update to 1.10.0
34+
35+* Thu Apr 19 2018 Vsevolod Volkov <vvv@mutt.org.ua>
36+- update to 1.9.5
37+
38+* Sat Sep 9 2017 Vsevolod Volkov <vvv@mutt.org.ua>
39+- update to 1.9.0
40+
41+* Sun Feb 26 2017 Vsevolod Volkov <vvv@mutt.org.ua>
42+- update to 1.8.0
43+
44+* Sun Dec 18 2016 Vsevolod Volkov <vvv@mutt.org.ua>
45+- update to 1.7.2
46+
47+* Sun Oct 9 2016 Vsevolod Volkov <vvv@mutt.org.ua>
48+- update to 1.7.1
49+
50+* Wed Aug 31 2016 Vsevolod Volkov <vvv@mutt.org.ua>
51+- newsgroups aren't marked as new on first load
52+
53+* Wed Aug 24 2016 Vsevolod Volkov <vvv@mutt.org.ua>
54+- update to 1.7.0
55+- fixed new groups marking
56+- fixed unread messages counter update
57+
58+* Sat Aug 6 2016 Vsevolod Volkov <vvv@mutt.org.ua>
59+- update to 1.6.2
60+
61+* Sat Aug 6 2016 Vsevolod Volkov <vvv@mutt.org.ua>
62+- update to 1.6.1
63+
64+* Wed Apr 6 2016 Vsevolod Volkov <vvv@mutt.org.ua>
65+- update to 1.6.0
66+- %R changed to %x in format strings
67+
68+* Wed Nov 25 2015 Vsevolod Volkov <vvv@mutt.org.ua>
69+- fixed memory leaks
70+- fixed SIGSEGV when reading hcache in some cases
71+
72+* Tue Nov 10 2015 Vsevolod Volkov <vvv@mutt.org.ua>
73+- fixed error compiling with nntp and without imap or pop3
74+- fixed error loading articles after <change-newsgroup> and <quit>
75+
76+* Wed Sep 2 2015 Vsevolod Volkov <vvv@mutt.org.ua>
77+- update to 1.5.24
78+- new option nntp_listgroup
79+- use range in LISTGROUP command if possible
80+
81+* Thu Mar 13 2014 Vsevolod Volkov <vvv@mutt.org.ua>
82+- update to 1.5.23
83+
84+* Tue Oct 29 2013 Vsevolod Volkov <vvv@mutt.org.ua>
85+- minor bug fixed while removing new articles
86+
87+* Fri Oct 18 2013 Vsevolod Volkov <vvv@mutt.org.ua>
88+- update to 1.5.22
89+
90+* Tue Nov 27 2012 Vsevolod Volkov <vvv@mutt.org.ua>
91+- SASL authentication
92+- new option nntp_authenticators
93+
94+* Fri Nov 16 2012 Vsevolod Volkov <vvv@mutt.org.ua>
95+- support of NNTP commands: CAPABILITIES, STARTTLS, LIST NEWSGROUPS,
96+ LIST OVERVIEW.FMT, OVER, DATE
97+- added bcache support
98+- newss URI scheme renamed to snews
99+- removed option nntp_reconnect
100+
101+* Sun Sep 16 2012 Vsevolod Volkov <vvv@mutt.org.ua>
102+- internal header caching replaced with hcache
103+- new option newsgroups_charset
104+
4adf126f
JB
105+* Wed Sep 16 2010 Vsevolod Volkov <vvv@mutt.org.ua>
106+- update to 1.5.21
107+
108+* Thu Aug 13 2009 Vsevolod Volkov <vvv@mutt.org.ua>
109+- fixed writting references in nntp_save_cache_group()
110+
111+* Tue Jun 15 2009 Vsevolod Volkov <vvv@mutt.org.ua>
112+- update to 1.5.20
113+
114+* Tue Mar 20 2009 Vsevolod Volkov <vvv@mutt.org.ua>
115+- save Date: header of recorded outgoing articles
116+
bebcbf73
AG
117+* Tue Jan 6 2009 Vsevolod Volkov <vvv@mutt.org.ua>
118+- update to 1.5.19
119+
120+* Mon May 19 2008 Vsevolod Volkov <vvv@mutt.org.ua>
121+- update to 1.5.18
122+- fixed SIGSEGV when followup or forward to newsgroup
123+
124+* Sun Nov 4 2007 Vsevolod Volkov <vvv@mutt.org.ua>
125+- update to 1.5.17
126+
127+* Tue Jul 3 2007 Vsevolod Volkov <vvv@mutt.org.ua>
128+- fixed arguments of nntp_format_str()
129+
ce1255c1
JB
130+* Fri Jun 15 2007 Vsevolod Volkov <vvv@mutt.org.ua>
131+- fixed error selecting news group
132+
133+* Tue Jun 12 2007 Vsevolod Volkov <vvv@mutt.org.ua>
134+- update to 1.5.16
135+
e1bffe0b
JB
136+* Wed Apr 11 2007 Vsevolod Volkov <vvv@mutt.org.ua>
137+- fixed posting error if $smtp_url is set
138+- added support of print-style sequence %R (x-comment-to)
139+
140+* Sun Apr 8 2007 Vsevolod Volkov <vvv@mutt.org.ua>
141+- update to 1.5.15
142+- nntp://... url changed to news://...
143+- added indicator of fetching descriptions progress
144+
145+* Tue Feb 28 2007 Vsevolod Volkov <vvv@mutt.org.ua>
146+- update to 1.5.14
147+
95768aa9 148+* Tue Aug 15 2006 Vsevolod Volkov <vvv@mutt.org.ua>
149+- update to 1.5.13
150+
151+* Mon Jul 17 2006 Vsevolod Volkov <vvv@mutt.org.ua>
152+- update to 1.5.12
153+- fixed reading empty .newsrc
154+
155+* Sat Sep 17 2005 Vsevolod Volkov <vvv@mutt.org.ua>
156+- update to 1.5.11
157+
158+* Sat Aug 13 2005 Vsevolod Volkov <vvv@mutt.org.ua>
159+- update to 1.5.10
160+
161+* Sun Mar 13 2005 Vsevolod Volkov <vvv@mutt.org.ua>
162+- update to 1.5.9
163+
164+* Sun Feb 13 2005 Vsevolod Volkov <vvv@mutt.org.ua>
165+- update to 1.5.8
166+
167+* Sat Feb 5 2005 Vsevolod Volkov <vvv@mutt.org.ua>
168+- update to 1.5.7
169+- function mutt_update_list_file() moved to newsrc.c and changed algorithm
170+
171+* Thu Jul 8 2004 Vsevolod Volkov <vvv@mutt.org.ua>
172+- fixed error in nntp_logout_all()
173+
174+* Sat Apr 3 2004 Vsevolod Volkov <vvv@mutt.org.ua>
175+- fixed debug output in mutt_newsrc_update()
176+- added optional support of LISTGROUP command
177+- fixed typo in nntp_parse_xref()
178+
179+* Tue Feb 3 2004 Vsevolod Volkov <vvv@mutt.org.ua>
180+- update to 1.5.6
181+
182+* Thu Dec 18 2003 Vsevolod Volkov <vvv@mutt.org.ua>
183+- fixed compose menu
184+
185+* Thu Nov 6 2003 Vsevolod Volkov <vvv@mutt.org.ua>
186+- update to 1.5.5.1
187+
188+* Wed Nov 5 2003 Vsevolod Volkov <vvv@mutt.org.ua>
189+- update to 1.5.5
190+- added space after newsgroup name in .newsrc file
191+
192+* Sun May 18 2003 Vsevolod Volkov <vvv@mutt.org.ua>
c02f3a2c 193+- fixed SIGSEGV when posting article
95768aa9 194+
195+* Sat Mar 22 2003 Vsevolod Volkov <vvv@mutt.org.ua>
196+- update to 1.5.4
197+
198+* Sat Dec 21 2002 Vsevolod Volkov <vvv@mutt.org.ua>
199+- update to 1.5.3
200+- replace safe_free calls by the FREE macro
201+
202+* Fri Dec 6 2002 Vsevolod Volkov <vvv@mutt.org.ua>
203+- update to 1.5.2
204+- nntp authentication can be passed after any command
205+
206+* Sat May 4 2002 Vsevolod Volkov <vvv@mutt.org.ua>
207+- update to 1.5.1
208+
209+* Thu May 2 2002 Vsevolod Volkov <vvv@mutt.org.ua>
210+- update to 1.3.99
211+
212+* Wed Mar 13 2002 Vsevolod Volkov <vvv@mutt.org.ua>
213+- update to 1.3.28
214+- fixed SIGSEGV in <get-message>, <get-parent>, <get-children>,
215+ <reconstruct-thread> functions
216+- fixed message about nntp reconnect
217+- fixed <attach-news-message> function using browser
218+- added support of Followup-To: poster
219+- added %n (new articles) in group_index_format
220+- posting articles without inews by default
221+
222+* Wed Jan 23 2002 Vsevolod Volkov <vvv@mutt.org.ua>
223+- update to 1.3.27
224+
225+* Fri Jan 18 2002 Vsevolod Volkov <vvv@mutt.org.ua>
226+- update to 1.3.26
227+
228+* Thu Jan 3 2002 Vsevolod Volkov <vvv@mutt.org.ua>
229+- update to 1.3.25
230+- accelerated speed of access to news->newsgroups hash (by <gul@gul.kiev.ua>)
231+- added default content disposition
232+
233+* Mon Dec 3 2001 Vsevolod Volkov <vvv@mutt.org.ua>
234+- update to 1.3.24
235+
236+* Fri Nov 9 2001 Vsevolod Volkov <vvv@mutt.org.ua>
237+- update to 1.3.23.2
238+- fixed segfault if mutt_conn_find() returns null
239+
240+* Wed Oct 31 2001 Vsevolod Volkov <vvv@mutt.org.ua>
241+- update to 1.3.23.1
242+- added support of LISTGROUP command
243+- added support for servers with broken overview
244+- disabled <flag-message> function on news server
245+- fixed error storing bad authentication information
246+
247+* Wed Oct 10 2001 Vsevolod Volkov <vvv@mutt.org.ua>
248+- update to 1.3.23
249+- fixed typo in buffy.c
250+- added substitution of %s parameter in $inews variable
251+
252+* Fri Aug 31 2001 Vsevolod Volkov <vvv@mutt.org.ua>
253+- update to 1.3.22.1
254+- update to 1.3.22
255+
256+* Thu Aug 23 2001 Vsevolod Volkov <vvv@mutt.org.ua>
257+- update to 1.3.21
258+
259+* Wed Jul 25 2001 Vsevolod Volkov <vvv@mutt.org.ua>
260+- update to 1.3.20
261+- removed 'server-hook', use 'account-hook' instead
262+- fixed error opening NNTP server without newsgroup using -f option
263+
264+* Fri Jun 8 2001 Vsevolod Volkov <vvv@mutt.org.ua>
265+- update to 1.3.19
266+
267+* Sat May 5 2001 Vsevolod Volkov <vvv@mutt.org.ua>
268+- update to 1.3.18
269+- fixed typo in nntp_attempt_features()
270+- changed algorithm of XGTITLE command testing
271+- disabled writing of NNTP password in debug file
272+- fixed reading and writing of long newsrc lines
273+- changed checking of last line while reading lines from server
274+- fixed possible buffer overrun in nntp_parse_newsrc_line()
275+- removed checking of XHDR command
276+- compare NNTP return codes without trailing space
277+
278+* Thu Mar 29 2001 Vsevolod Volkov <vvv@mutt.org.ua>
279+- update to 1.3.17
280+- support for 'LIST NEWSGROUPS' command to read descriptions
281+
282+* Fri Mar 2 2001 Vsevolod Volkov <vvv@mutt.org.ua>
283+- update to 1.3.16
284+
285+* Wed Feb 14 2001 Vsevolod Volkov <vvv@mutt.org.ua>
286+- update to 1.3.15
287+
288+* Sun Jan 28 2001 Vsevolod Volkov <vvv@mutt.org.ua>
289+- update to 1.3.14
290+- show number of tagged messages patch from Felix von Leitner <leitner@fefe.de>
291+
292+* Sun Dec 31 2000 Vsevolod Volkov <vvv@mutt.org.ua>
293+- update to 1.3.13
294+
295+* Sat Dec 30 2000 Vsevolod Volkov <vvv@mutt.org.ua>
296+- Fixed problem if last article in group is deleted
297+
298+* Fri Dec 22 2000 Vsevolod Volkov <vvv@mutt.org.ua>
299+- Fixed checking of XGTITLE command on some servers
300+
301+* Mon Dec 18 2000 Vsevolod Volkov <vvv@mutt.org.ua>
302+- Added \r in AUTHINFO commands
303+
304+* Mon Nov 27 2000 Vsevolod Volkov <vvv@mutt.org.ua>
305+- update to 1.3.12
306+
307+* Wed Nov 1 2000 Vsevolod Volkov <vvv@mutt.org.ua>
308+- update to 1.3.11
309+- fixed error opening newsgroup from mutt started with -g or -G
310+
311+* Thu Oct 12 2000 Vsevolod Volkov <vvv@mutt.org.ua>
312+- update to 1.3.10
313+- hotkey 'G' (get-message) replaced with '^G'
314+
315+* Thu Sep 21 2000 Vsevolod Volkov <vvv@mutt.org.ua>
316+- update to 1.3.9
317+- changed delay displaying error messages from 1 to 2 seconds
318+- fixed error compiling with nntp and without imap
319+
320+* Wed Sep 6 2000 Vsevolod Volkov <vvv@mutt.org.ua>
321+- fixed catchup in index
322+- fixed nntp_open_mailbox()
323+
324+* Sat Sep 2 2000 Vsevolod Volkov <vvv@mutt.org.ua>
325+- functions <edit> and <delete-entry> disabled
326+- format of news mailbox names changed to url form
327+- option nntp_attempts removed
328+- option reconnect_news renamed to nntp_reconnect
329+- default value of nntp_poll changed from 30 to 60
330+- error handling improved
331+
332+* Wed Aug 30 2000 Vsevolod Volkov <vvv@mutt.org.ua>
333+- update to 1.3.8
334+- new option show_only_unread
335+- add newsgroup completion
336+
337+* Fri Aug 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
338+- update to 1.3.7
339+
340+* Sat Jul 29 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
341+- update to 1.3.6
342+
343+* Sun Jul 9 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
344+- update to 1.3.5
345+- authentication code update
346+- fix for changing to newsgroup from mailbox with read messages
347+- socket code optimization
348+
349+* Wed Jun 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
350+- update to 1.3.4
351+
352+* Wed Jun 14 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
353+- don't substitute current newsgroup with deleted new messages
354+
355+* Mon Jun 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
356+- update to 1.3.3
357+- fix for substitution of newsgroup after reconnection
358+- fix for loading newsgroups with very long names
359+- fix for loading more than 32768 newsgroups
360+
361+* Wed May 24 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
362+- update to 1.3.2
363+
364+* Sat May 20 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
365+- update to 1.3.1
366+
367+* Fri May 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
368+- update to 1.3
369+
370+* Thu May 11 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
371+- update to 1.2
372+
373+* Thu May 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
374+- update to 1.1.14
375+
376+* Sun Apr 23 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
377+- update to 1.1.12
378+
379+* Fri Apr 7 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
380+- add substitution of newsgroup with new messages by default
381+
382+* Wed Apr 5 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
383+- add attach message from newsgroup
384+- add one-line help in newsreader mode
385+- disable 'change-dir' command in newsgroups browser
386+- add -G option
387+
388+* Tue Apr 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
c02f3a2c 389+- get default news server name from file /etc/nntpserver
95768aa9 390+- use case insensitive server names
391+- add print-style sequence %s to $newsrc
392+- add -g option
393+
394+* Sat Apr 1 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
395+- remove 'X-FTN-Origin' header processing
396+
397+* Thu Mar 30 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
398+- update to 1.1.11
399+- update to 1.1.10
400+
401+* Thu Mar 23 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
402+- fix mutt_select_newsserver()
403+- remove 'toggle-mode' function
404+- add 'change-newsgroup' function
405+
406+* Wed Mar 22 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
407+- fix server-hook
408+
409+* Tue Mar 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
410+- fix error 'bounce' function after 'post'
411+- add 'forward to newsgroup' function
412+
413+* Mon Mar 20 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
414+- 'forward' function works in newsreader mode
415+- add 'post' and 'followup' functions to pager and attachment menu
416+- fix active descriptions and allowed flag reload
417+
418+* Tue Mar 14 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
419+- update to 1.1.9
420+- remove deleted newsgroups from list
421+
422+* Mon Mar 13 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
423+- update .newsrc in browser
424+
425+* Sun Mar 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
426+- reload .newsrc if externally modified
427+- fix active cache update
428+
429+* Sun Mar 5 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
430+- update to 1.1.8
431+
432+* Sat Mar 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
433+- patch *.update_list_file is not required
434+- count lines when loading descriptions
435+- remove cache of unsubscribed newsgroups
436+
437+* Thu Mar 2 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
438+- load list of newsgroups from cache faster
439+
440+* Wed Mar 1 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
441+- update to 1.1.7
442+
443+* Tue Feb 29 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
444+- fix unread messages in browser
445+- fix newsrc_gen_entries()
446+
447+* Mon Feb 28 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
448+- fix mutt_newsgroup_stat()
449+- fix nntp_delete_cache()
450+- fix nntp_get_status()
451+- fix check_children()
452+- fix nntp_fetch_headers()
453+
454+* Fri Feb 25 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
455+- update to 1.1.5
456+
457+* Thu Feb 24 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
458+- fix updating new messages in cache
459+
460+* Mon Feb 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
461+- change default cache filenames
462+- fix updating new messages in cache
463+
464+* Fri Feb 18 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
465+- fix segmentation fault in news groups browser
466+
467+* Tue Feb 15 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
468+- update to 1.1.4
469+
470+* Thu Feb 10 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
471+- update to 1.1.3
472+
473+* Sun Jan 30 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
474+- add X-Comment-To editing
475+- add my_hdr support for Newsgroups:, Followup-To: and X-Comment-To: headers
476+- add variables $ask_followup_to and $ask_x_comment_to
477+
478+* Fri Jan 28 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
479+- update to 1.1.2
32845784
AG
480diff -udprP mutt-1.12.1.orig/Makefile.am mutt-1.12.1/Makefile.am
481--- mutt-1.12.1.orig/Makefile.am 2019-06-15 18:57:01.000000000 +0300
482+++ mutt-1.12.1/Makefile.am 2019-08-11 20:31:26.131940718 +0300
483@@ -60,6 +60,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c
484 mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \
485 mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \
486 pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \
487+ nntp.c newsrc.c \
488 sidebar.c smime.c smtp.c utf8.c wcwidth.c \
489 bcache.h browser.h hcache.h mbyte.h monitor.h mutt_idna.h remailer.h url.h
490
491@@ -72,6 +73,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP O
492 mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
493 mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
494 rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \
495+ nntp.h ChangeLog.nntp \
496 _mutt_regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \
497 mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \
498 README.SSL smime.h group.h \
499diff -udprP mutt-1.12.1.orig/Makefile.in mutt-1.12.1/Makefile.in
500--- mutt-1.12.1.orig/Makefile.in 2019-06-15 19:07:04.000000000 +0300
501+++ mutt-1.12.1/Makefile.in 2019-08-11 20:31:38.771749259 +0300
502@@ -256,6 +256,7 @@ am__depfiles_remade = $(DEPDIR)/mkdtemp.
503 ./$(DEPDIR)/rfc3676.Po ./$(DEPDIR)/rfc822.Po \
504 ./$(DEPDIR)/safe_asprintf.Po ./$(DEPDIR)/score.Po \
505 ./$(DEPDIR)/send.Po ./$(DEPDIR)/sendlib.Po ./$(DEPDIR)/sha1.Po \
506+ ./$(DEPDIR)/newsrc.Po ./$(DEPDIR)/nntp.Po \
507 ./$(DEPDIR)/sidebar.Po ./$(DEPDIR)/signal.Po \
508 ./$(DEPDIR)/smime.Po ./$(DEPDIR)/smtp.Po ./$(DEPDIR)/sort.Po \
509 ./$(DEPDIR)/status.Po ./$(DEPDIR)/system.Po \
510@@ -570,6 +571,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c
511 mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \
512 mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \
513 pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \
514+ nntp.c newsrc.c \
515 sidebar.c smime.c smtp.c utf8.c wcwidth.c \
516 bcache.h browser.h hcache.h mbyte.h monitor.h mutt_idna.h remailer.h url.h
517
518@@ -582,6 +584,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP O
519 mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
520 mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
521 rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \
522+ nntp.h ChangeLog.nntp \
523 _mutt_regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \
524 mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \
525 README.SSL smime.h group.h \
526@@ -853,6 +856,8 @@ distclean-compile:
527 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mutt_tunnel.Po@am__quote@ # am--include-marker
528 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muttlib.Po@am__quote@ # am--include-marker
529 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mx.Po@am__quote@ # am--include-marker
530+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newsrc.Po@am__quote@ # am--include-marker
531+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nntp.Po@am__quote@ # am--include-marker
532 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pager.Po@am__quote@ # am--include-marker
533 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@ # am--include-marker
534 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/patchlist.Po@am__quote@ # am--include-marker
535@@ -1356,6 +1361,8 @@ distclean: distclean-recursive
536 -rm -f ./$(DEPDIR)/mutt_tunnel.Po
537 -rm -f ./$(DEPDIR)/muttlib.Po
538 -rm -f ./$(DEPDIR)/mx.Po
539+ -rm -f ./$(DEPDIR)/newsrc.Po
540+ -rm -f ./$(DEPDIR)/nntp.Po
541 -rm -f ./$(DEPDIR)/pager.Po
542 -rm -f ./$(DEPDIR)/parse.Po
543 -rm -f ./$(DEPDIR)/patchlist.Po
544@@ -1525,6 +1532,8 @@ maintainer-clean: maintainer-clean-recur
545 -rm -f ./$(DEPDIR)/mutt_tunnel.Po
546 -rm -f ./$(DEPDIR)/muttlib.Po
547 -rm -f ./$(DEPDIR)/mx.Po
548+ -rm -f ./$(DEPDIR)/newsrc.Po
549+ -rm -f ./$(DEPDIR)/nntp.Po
550 -rm -f ./$(DEPDIR)/pager.Po
551 -rm -f ./$(DEPDIR)/parse.Po
552 -rm -f ./$(DEPDIR)/patchlist.Po
553diff -udprP mutt-1.12.1.orig/OPS mutt-1.12.1/OPS
554--- mutt-1.12.1.orig/OPS 2019-05-25 19:22:39.000000000 +0300
555+++ mutt-1.12.1/OPS 2019-08-11 20:31:26.132940703 +0300
bebcbf73
AG
556@@ -8,14 +8,16 @@ OP_BOUNCE_MESSAGE "remail a message to a
557 OP_BROWSER_NEW_FILE "select a new file in this directory"
558 OP_BROWSER_VIEW_FILE "view file"
559 OP_BROWSER_TELL "display the currently selected file's name"
560-OP_BROWSER_SUBSCRIBE "subscribe to current mailbox (IMAP only)"
561-OP_BROWSER_UNSUBSCRIBE "unsubscribe from current mailbox (IMAP only)"
562+OP_BROWSER_SUBSCRIBE "subscribe to current mbox (IMAP/NNTP only)"
563+OP_BROWSER_UNSUBSCRIBE "unsubscribe from current mbox (IMAP/NNTP only)"
564 OP_BROWSER_TOGGLE_LSUB "toggle view all/subscribed mailboxes (IMAP only)"
565 OP_BUFFY_LIST "list mailboxes with new mail"
566+OP_CATCHUP "mark all articles in newsgroup as read"
567 OP_CHANGE_DIRECTORY "change directories"
568 OP_CHECK_NEW "check mailboxes for new mail"
569 OP_COMPOSE_ATTACH_FILE "attach file(s) to this message"
570 OP_COMPOSE_ATTACH_MESSAGE "attach message(s) to this message"
c02f3a2c 571+OP_COMPOSE_ATTACH_NEWS_MESSAGE "attach news article(s) to this message"
bebcbf73
AG
572 OP_COMPOSE_EDIT_BCC "edit the BCC list"
573 OP_COMPOSE_EDIT_CC "edit the CC list"
574 OP_COMPOSE_EDIT_DESCRIPTION "edit attachment description"
575@@ -26,7 +28,10 @@ OP_COMPOSE_EDIT_FROM "edit the from fiel
576 OP_COMPOSE_EDIT_HEADERS "edit the message with headers"
577 OP_COMPOSE_EDIT_MESSAGE "edit the message"
578 OP_COMPOSE_EDIT_MIME "edit attachment using mailcap entry"
579+OP_COMPOSE_EDIT_NEWSGROUPS "edit the newsgroups list"
580 OP_COMPOSE_EDIT_REPLY_TO "edit the Reply-To field"
581+OP_COMPOSE_EDIT_FOLLOWUP_TO "edit the Followup-To field"
582+OP_COMPOSE_EDIT_X_COMMENT_TO "edit the X-Comment-To field"
583 OP_COMPOSE_EDIT_SUBJECT "edit the subject of this message"
584 OP_COMPOSE_EDIT_TO "edit the TO list"
585 OP_CREATE_MAILBOX "create a new mailbox (IMAP only)"
32845784 586@@ -90,8 +95,13 @@ OP_EXIT "exit this menu"
bebcbf73
AG
587 OP_FILTER "filter attachment through a shell command"
588 OP_FIRST_ENTRY "move to the first entry"
589 OP_FLAG_MESSAGE "toggle a message's 'important' flag"
590+OP_FOLLOWUP "followup to newsgroup"
591+OP_FORWARD_TO_GROUP "forward to newsgroup"
592 OP_FORWARD_MESSAGE "forward a message with comments"
593 OP_GENERIC_SELECT_ENTRY "select the current entry"
594+OP_GET_CHILDREN "get all children of the current message"
595+OP_GET_MESSAGE "get message with Message-Id"
596+OP_GET_PARENT "get parent of the current message"
32845784 597 OP_GROUP_CHAT_REPLY "reply to all recipients preserving To/Cc"
bebcbf73
AG
598 OP_GROUP_REPLY "reply to all recipients"
599 OP_HALF_DOWN "scroll down 1/2 page"
32845784 600@@ -100,11 +110,14 @@ OP_HELP "this screen"
bebcbf73
AG
601 OP_JUMP "jump to an index number"
602 OP_LAST_ENTRY "move to the last entry"
603 OP_LIST_REPLY "reply to specified mailing list"
c02f3a2c 604+OP_LOAD_ACTIVE "load list of all newsgroups from NNTP server"
bebcbf73
AG
605 OP_MACRO "execute a macro"
606 OP_MAIL "compose a new mail message"
607 OP_MAIN_BREAK_THREAD "break the thread in two"
608 OP_MAIN_CHANGE_FOLDER "open a different folder"
609 OP_MAIN_CHANGE_FOLDER_READONLY "open a different folder in read only mode"
610+OP_MAIN_CHANGE_GROUP "open a different newsgroup"
611+OP_MAIN_CHANGE_GROUP_READONLY "open a different newsgroup in read only mode"
612 OP_MAIN_CLEAR_FLAG "clear a status flag from a message"
613 OP_MAIN_DELETE_PATTERN "delete messages matching a pattern"
614 OP_MAIN_IMAP_FETCH "force retrieval of mail from IMAP server"
32845784 615@@ -144,6 +157,7 @@ OP_PAGER_HIDE_QUOTED "toggle display of
bebcbf73
AG
616 OP_PAGER_SKIP_QUOTED "skip beyond quoted text"
617 OP_PAGER_TOP "jump to the top of the message"
618 OP_PIPE "pipe message/attachment to a shell command"
619+OP_POST "post message to newsgroup"
620 OP_PREV_ENTRY "move to the previous entry"
621 OP_PREV_LINE "scroll up one line"
622 OP_PREV_PAGE "move to the previous page"
32845784 623@@ -153,6 +167,7 @@ OP_QUERY "query external program for add
bebcbf73
AG
624 OP_QUERY_APPEND "append new query results to current results"
625 OP_QUIT "save changes to mailbox and quit"
626 OP_RECALL_MESSAGE "recall a postponed message"
627+OP_RECONSTRUCT_THREAD "reconstruct thread containing current message"
628 OP_REDRAW "clear and redraw the screen"
629 OP_REFORMAT_WINCH "{internal}"
630 OP_RENAME_MAILBOX "rename the current mailbox (IMAP only)"
32845784 631@@ -167,18 +182,22 @@ OP_SEARCH_TOGGLE "toggle search pattern
bebcbf73
AG
632 OP_SHELL_ESCAPE "invoke a command in a subshell"
633 OP_SORT "sort messages"
634 OP_SORT_REVERSE "sort messages in reverse order"
635+OP_SUBSCRIBE_PATTERN "subscribe to newsgroups matching a pattern"
636 OP_TAG "tag the current entry"
637 OP_TAG_PREFIX "apply next function to tagged messages"
638 OP_TAG_PREFIX_COND "apply next function ONLY to tagged messages"
639 OP_TAG_SUBTHREAD "tag the current subthread"
640 OP_TAG_THREAD "tag the current thread"
641 OP_TOGGLE_NEW "toggle a message's 'new' flag"
642+OP_TOGGLE_READ "toggle view of read messages"
643 OP_TOGGLE_WRITE "toggle whether the mailbox will be rewritten"
644 OP_TOGGLE_MAILBOXES "toggle whether to browse mailboxes or all files"
645 OP_TOP_PAGE "move to the top of the page"
646+OP_UNCATCHUP "mark all articles in newsgroup as unread"
647 OP_UNDELETE "undelete the current entry"
648 OP_UNDELETE_THREAD "undelete all messages in thread"
649 OP_UNDELETE_SUBTHREAD "undelete all messages in subthread"
650+OP_UNSUBSCRIBE_PATTERN "unsubscribe from newsgroups matching a pattern"
651 OP_VERSION "show the Mutt version number and date"
652 OP_VIEW_ATTACH "view attachment using mailcap entry if necessary"
653 OP_VIEW_ATTACHMENTS "show MIME attachments"
32845784
AG
654diff -udprP mutt-1.12.1.orig/PATCHES mutt-1.12.1/PATCHES
655--- mutt-1.12.1.orig/PATCHES 2017-12-03 05:10:17.000000000 +0200
656+++ mutt-1.12.1/PATCHES 2019-08-11 20:31:26.132940703 +0300
bebcbf73
AG
657@@ -0,0 +1 @@
658+vvv.nntp
32845784
AG
659diff -udprP mutt-1.12.1.orig/account.c mutt-1.12.1/account.c
660--- mutt-1.12.1.orig/account.c 2019-05-25 19:22:39.000000000 +0300
661+++ mutt-1.12.1/account.c 2019-08-11 20:31:26.133940688 +0300
c02f3a2c 662@@ -51,8 +51,17 @@ int mutt_account_match (const ACCOUNT* a
bebcbf73
AG
663 user = PopUser;
664 #endif
32845784 665
bebcbf73 666+#ifdef USE_NNTP
c02f3a2c 667+ if (a1->type == MUTT_ACCT_TYPE_NNTP && NntpUser)
bebcbf73
AG
668+ user = NntpUser;
669+#endif
670+
c02f3a2c 671 if (a1->flags & a2->flags & MUTT_ACCT_USER)
bebcbf73 672 return (!strcmp (a1->user, a2->user));
c02f3a2c
AG
673+#ifdef USE_NNTP
674+ if (a1->type == MUTT_ACCT_TYPE_NNTP)
675+ return a1->flags & MUTT_ACCT_USER && a1->user[0] ? 0 : 1;
676+#endif
677 if (a1->flags & MUTT_ACCT_USER)
678 return (!strcmp (a1->user, user));
679 if (a2->flags & MUTT_ACCT_USER)
680@@ -130,6 +139,16 @@ void mutt_account_tourl (ACCOUNT* accoun
bebcbf73
AG
681 }
682 #endif
95768aa9 683
bebcbf73 684+#ifdef USE_NNTP
c02f3a2c 685+ if (account->type == MUTT_ACCT_TYPE_NNTP)
bebcbf73 686+ {
c02f3a2c 687+ if (account->flags & MUTT_ACCT_SSL)
bebcbf73
AG
688+ url->scheme = U_NNTPS;
689+ else
690+ url->scheme = U_NNTP;
691+ }
692+#endif
693+
694 url->host = account->host;
c02f3a2c 695 if (account->flags & MUTT_ACCT_PORT)
bebcbf73 696 url->port = account->port;
c02f3a2c
AG
697@@ -155,6 +174,10 @@ int mutt_account_getuser (ACCOUNT* accou
698 else if ((account->type == MUTT_ACCT_TYPE_POP) && PopUser)
bebcbf73
AG
699 strfcpy (account->user, PopUser, sizeof (account->user));
700 #endif
701+#ifdef USE_NNTP
c02f3a2c 702+ else if ((account->type == MUTT_ACCT_TYPE_NNTP) && NntpUser)
bebcbf73
AG
703+ strfcpy (account->user, NntpUser, sizeof (account->user));
704+#endif
4adf126f
JB
705 else if (option (OPTNOCURSES))
706 return -1;
bebcbf73 707 /* prompt (defaults to unix username), copy into account->user */
c02f3a2c
AG
708@@ -217,6 +240,10 @@ int mutt_account_getpass (ACCOUNT* accou
709 else if ((account->type == MUTT_ACCT_TYPE_SMTP) && SmtpPass)
bebcbf73
AG
710 strfcpy (account->pass, SmtpPass, sizeof (account->pass));
711 #endif
712+#ifdef USE_NNTP
c02f3a2c 713+ else if ((account->type == MUTT_ACCT_TYPE_NNTP) && NntpPass)
bebcbf73
AG
714+ strfcpy (account->pass, NntpPass, sizeof (account->pass));
715+#endif
4adf126f
JB
716 else if (option (OPTNOCURSES))
717 return -1;
bebcbf73 718 else
32845784
AG
719diff -udprP mutt-1.12.1.orig/account.h mutt-1.12.1/account.h
720--- mutt-1.12.1.orig/account.h 2019-05-25 19:22:39.000000000 +0300
721+++ mutt-1.12.1/account.h 2019-08-11 20:31:26.133940688 +0300
bebcbf73 722@@ -29,7 +29,8 @@ enum
c02f3a2c
AG
723 MUTT_ACCT_TYPE_NONE = 0,
724 MUTT_ACCT_TYPE_IMAP,
725 MUTT_ACCT_TYPE_POP,
726- MUTT_ACCT_TYPE_SMTP
727+ MUTT_ACCT_TYPE_SMTP,
728+ MUTT_ACCT_TYPE_NNTP
bebcbf73 729 };
ce1255c1
JB
730
731 /* account flags */
32845784
AG
732diff -udprP mutt-1.12.1.orig/attach.h mutt-1.12.1/attach.h
733--- mutt-1.12.1.orig/attach.h 2019-05-25 19:22:39.000000000 +0300
734+++ mutt-1.12.1/attach.h 2019-08-11 20:31:26.133940688 +0300
c02f3a2c
AG
735@@ -71,7 +71,7 @@ void mutt_print_attachment_list (ATTACH_
736
737 void mutt_attach_bounce (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
738 void mutt_attach_resend (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
739-void mutt_attach_forward (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
740+void mutt_attach_forward (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *, int);
741 void mutt_attach_reply (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *, int);
32845784 742 void mutt_attach_mail_sender (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
c02f3a2c 743
32845784
AG
744diff -udprP mutt-1.12.1.orig/browser.c mutt-1.12.1/browser.c
745--- mutt-1.12.1.orig/browser.c 2019-05-25 19:22:39.000000000 +0300
746+++ mutt-1.12.1/browser.c 2019-08-11 20:31:26.133940688 +0300
ce1255c1
JB
747@@ -32,6 +32,9 @@
748 #ifdef USE_IMAP
749 #include "imap.h"
750 #endif
751+#ifdef USE_NNTP
752+#include "nntp.h"
753+#endif
754
755 #include <stdlib.h>
756 #include <dirent.h>
c02f3a2c 757@@ -50,6 +53,19 @@ static const struct mapping_t FolderHelp
4adf126f 758 { NULL, 0 }
ce1255c1
JB
759 };
760
761+#ifdef USE_NNTP
762+static struct mapping_t FolderNewsHelp[] = {
763+ { N_("Exit"), OP_EXIT },
764+ { N_("List"), OP_TOGGLE_MAILBOXES },
765+ { N_("Subscribe"), OP_BROWSER_SUBSCRIBE },
766+ { N_("Unsubscribe"), OP_BROWSER_UNSUBSCRIBE },
767+ { N_("Catchup"), OP_CATCHUP },
768+ { N_("Mask"), OP_ENTER_MASK },
769+ { N_("Help"), OP_HELP },
c02f3a2c 770+ { NULL, 0 }
ce1255c1
JB
771+};
772+#endif
773+
774 typedef struct folder_t
775 {
776 struct folder_file *ff;
32845784 777@@ -140,9 +156,17 @@ static void browser_sort (struct browser
ce1255c1
JB
778 case SORT_ORDER:
779 return;
780 case SORT_DATE:
781+#ifdef USE_NNTP
782+ if (option (OPTNEWS))
783+ return;
784+#endif
785 f = browser_compare_date;
786 break;
787 case SORT_SIZE:
788+#ifdef USE_NNTP
789+ if (option (OPTNEWS))
790+ return;
791+#endif
792 f = browser_compare_size;
793 break;
c02f3a2c 794 case SORT_COUNT:
32845784 795@@ -388,8 +412,112 @@ folder_format_str (char *dest, size_t de
ce1255c1
JB
796 return (src);
797 }
798
799+#ifdef USE_NNTP
800+static const char *
c02f3a2c
AG
801+newsgroup_format_str (char *dest, size_t destlen, size_t col, int cols, char op,
802+ const char *src, const char *fmt, const char *ifstring,
803+ const char *elsestring, unsigned long data, format_flag flags)
ce1255c1
JB
804+{
805+ char fn[SHORT_STRING], tmp[SHORT_STRING];
806+ FOLDER *folder = (FOLDER *) data;
807+
808+ switch (op)
809+ {
810+ case 'C':
811+ snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
812+ snprintf (dest, destlen, tmp, folder->num + 1);
813+ break;
c02f3a2c 814+
ce1255c1
JB
815+ case 'f':
816+ strncpy (fn, folder->ff->name, sizeof(fn) - 1);
817+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
818+ snprintf (dest, destlen, tmp, fn);
819+ break;
820+
821+ case 'N':
822+ snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
823+ if (folder->ff->nd->subscribed)
824+ snprintf (dest, destlen, tmp, ' ');
825+ else
c02f3a2c 826+ snprintf (dest, destlen, tmp, folder->ff->nd->new ? 'N' : 'u');
ce1255c1
JB
827+ break;
828+
829+ case 'M':
830+ snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
831+ if (folder->ff->nd->deleted)
832+ snprintf (dest, destlen, tmp, 'D');
833+ else
834+ snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-');
835+ break;
836+
837+ case 's':
c02f3a2c 838+ if (flags & MUTT_FORMAT_OPTIONAL)
ce1255c1
JB
839+ {
840+ if (folder->ff->nd->unread != 0)
c02f3a2c
AG
841+ mutt_FormatString (dest, destlen, col, cols, ifstring,
842+ newsgroup_format_str, data, flags);
ce1255c1 843+ else
c02f3a2c
AG
844+ mutt_FormatString (dest, destlen, col, cols, elsestring,
845+ newsgroup_format_str, data, flags);
ce1255c1
JB
846+ }
847+ else if (Context && Context->data == folder->ff->nd)
848+ {
849+ snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
850+ snprintf (dest, destlen, tmp, Context->unread);
851+ }
852+ else
853+ {
854+ snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
855+ snprintf (dest, destlen, tmp, folder->ff->nd->unread);
856+ }
857+ break;
858+
859+ case 'n':
860+ if (Context && Context->data == folder->ff->nd)
861+ {
862+ snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
863+ snprintf (dest, destlen, tmp, Context->new);
864+ }
865+ else if (option (OPTMARKOLD) &&
866+ folder->ff->nd->lastCached >= folder->ff->nd->firstMessage &&
867+ folder->ff->nd->lastCached <= folder->ff->nd->lastMessage)
868+ {
869+ snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
870+ snprintf (dest, destlen, tmp, folder->ff->nd->lastMessage - folder->ff->nd->lastCached);
871+ }
872+ else
873+ {
874+ snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
875+ snprintf (dest, destlen, tmp, folder->ff->nd->unread);
876+ }
877+ break;
878+
879+ case 'd':
880+ if (folder->ff->nd->desc != NULL)
881+ {
c02f3a2c
AG
882+ char *buf = safe_strdup (folder->ff->nd->desc);
883+ if (NewsgroupsCharset && *NewsgroupsCharset)
884+ mutt_convert_string (&buf, NewsgroupsCharset, Charset, MUTT_ICONV_HOOK_FROM);
885+ mutt_filter_unprintable (&buf);
886+
ce1255c1 887+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
c02f3a2c
AG
888+ snprintf (dest, destlen, tmp, buf);
889+ FREE (&buf);
ce1255c1
JB
890+ }
891+ else
892+ {
893+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
894+ snprintf (dest, destlen, tmp, "");
895+ }
896+ break;
897+ }
898+ return (src);
899+}
900+#endif /* USE_NNTP */
901+
902 static void add_folder (MUTTMENU *m, struct browser_state *state,
c02f3a2c
AG
903- const char *name, const struct stat *s, BUFFY *b)
904+ const char *name, const struct stat *s, BUFFY *b,
905+ void *data)
ce1255c1
JB
906 {
907 if (state->entrylen == state->entrymax)
908 {
32845784 909@@ -427,6 +555,10 @@ static void add_folder (MUTTMENU *m, str
ce1255c1
JB
910 #ifdef USE_IMAP
911 (state->entry)[state->entrylen].imap = 0;
912 #endif
913+#ifdef USE_NNTP
914+ if (option (OPTNEWS))
c02f3a2c 915+ (state->entry)[state->entrylen].nd = (NNTP_DATA *)data;
ce1255c1
JB
916+#endif
917 (state->entrylen)++;
918 }
919
32845784 920@@ -442,9 +574,35 @@ static void init_state (struct browser_s
ce1255c1
JB
921 menu->data = state->entry;
922 }
923
924+/* get list of all files/newsgroups with mask */
925 static int examine_directory (MUTTMENU *menu, struct browser_state *state,
32845784 926 const char *d, const char *prefix)
ce1255c1
JB
927 {
928+#ifdef USE_NNTP
929+ if (option (OPTNEWS))
930+ {
c02f3a2c
AG
931+ NNTP_SERVER *nserv = CurrentNewsSrv;
932+ unsigned int i;
ce1255c1
JB
933+
934+/* mutt_buffy_check (0); */
935+ init_state (state, menu);
936+
c02f3a2c 937+ for (i = 0; i < nserv->groups_num; i++)
ce1255c1 938+ {
c02f3a2c
AG
939+ NNTP_DATA *nntp_data = nserv->groups_list[i];
940+ if (!nntp_data)
ce1255c1 941+ continue;
c02f3a2c
AG
942+ if (prefix && *prefix &&
943+ strncmp (prefix, nntp_data->group, strlen (prefix)))
ce1255c1 944+ continue;
c02f3a2c 945+ if (!((regexec (Mask.rx, nntp_data->group, 0, NULL, 0) == 0) ^ Mask.not))
ce1255c1 946+ continue;
c02f3a2c 947+ add_folder (menu, state, nntp_data->group, NULL, NULL, nntp_data);
ce1255c1
JB
948+ }
949+ }
950+ else
951+#endif /* USE_NNTP */
952+ {
953 struct stat s;
954 DIR *dp;
955 struct dirent *de;
32845784 956@@ -514,18 +672,41 @@ static int examine_directory (MUTTMENU *
c02f3a2c
AG
957 tmp->msg_count = Context->msgcount;
958 tmp->msg_unread = Context->unread;
959 }
960- add_folder (menu, state, de->d_name, &s, tmp);
961+ add_folder (menu, state, de->d_name, &s, tmp, NULL);
ce1255c1 962 }
32845784
AG
963 closedir (dp);
964- browser_sort (state);
965-
966 mutt_buffer_pool_release (&buffer);
c02f3a2c 967+ }
32845784
AG
968+
969+ browser_sort (state);
ce1255c1
JB
970 return 0;
971 }
972
973+/* get list of mailboxes/subscribed newsgroups */
974 static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
975 {
976 struct stat s;
ce1255c1
JB
977+
978+#ifdef USE_NNTP
979+ if (option (OPTNEWS))
980+ {
c02f3a2c
AG
981+ NNTP_SERVER *nserv = CurrentNewsSrv;
982+ unsigned int i;
ce1255c1
JB
983+
984+/* mutt_buffy_check (0); */
985+ init_state (state, menu);
986+
c02f3a2c 987+ for (i = 0; i < nserv->groups_num; i++)
ce1255c1 988+ {
c02f3a2c
AG
989+ NNTP_DATA *nntp_data = nserv->groups_list[i];
990+ if (nntp_data && (nntp_data->new || (nntp_data->subscribed &&
991+ (nntp_data->unread || !option (OPTSHOWONLYUNREAD)))))
992+ add_folder (menu, state, nntp_data->group, NULL, NULL, nntp_data);
ce1255c1
JB
993+ }
994+ }
995+ else
996+#endif
997+ {
998 BUFFY *tmp = Incoming;
32845784
AG
999 BUFFER *mailbox = NULL;
1000 BUFFER *md = NULL;
1001@@ -554,14 +735,21 @@ static int examine_mailboxes (MUTTMENU *
ce1255c1 1002 #ifdef USE_IMAP
32845784 1003 if (mx_is_imap (mutt_b2s (tmp->pathbuf)))
ce1255c1 1004 {
32845784
AG
1005- add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp);
1006+ add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp, NULL);
ce1255c1
JB
1007 continue;
1008 }
1009 #endif
1010 #ifdef USE_POP
32845784 1011 if (mx_is_pop (mutt_b2s (tmp->pathbuf)))
ce1255c1 1012 {
32845784
AG
1013- add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp);
1014+ add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp, NULL);
ce1255c1
JB
1015+ continue;
1016+ }
1017+#endif
1018+#ifdef USE_NNTP
32845784 1019+ if (mx_is_nntp (mutt_b2s (tmp->pathbuf)))
ce1255c1 1020+ {
32845784 1021+ add_folder (menu, state, mutt_b2s (mailbox), NULL, tmp, NULL);
ce1255c1
JB
1022 continue;
1023 }
1024 #endif
32845784 1025@@ -586,18 +774,23 @@ static int examine_mailboxes (MUTTMENU *
c02f3a2c
AG
1026 s.st_mtime = st2.st_mtime;
1027 }
ce1255c1 1028
32845784
AG
1029- add_folder (menu, state, mutt_b2s (mailbox), &s, tmp);
1030+ add_folder (menu, state, mutt_b2s (mailbox), &s, tmp, NULL);
ce1255c1
JB
1031 }
1032 while ((tmp = tmp->next));
32845784
AG
1033- browser_sort (state);
1034-
1035 mutt_buffer_pool_release (&mailbox);
1036 mutt_buffer_pool_release (&md);
ce1255c1 1037+ }
32845784
AG
1038+
1039+ browser_sort (state);
ce1255c1
JB
1040 return 0;
1041 }
1042
1043 static int select_file_search (MUTTMENU *menu, regex_t *re, int n)
1044 {
1045+#ifdef USE_NNTP
1046+ if (option (OPTNEWS))
1047+ return (regexec (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
1048+#endif
1049 return (regexec (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
1050 }
1051
32845784 1052@@ -608,6 +801,12 @@ static void folder_entry (char *s, size_
ce1255c1
JB
1053 folder.ff = &((struct folder_file *) menu->data)[num];
1054 folder.num = num;
32845784 1055
ce1255c1
JB
1056+#ifdef USE_NNTP
1057+ if (option (OPTNEWS))
c02f3a2c
AG
1058+ mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL(GroupFormat),
1059+ newsgroup_format_str, (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
ce1255c1
JB
1060+ else
1061+#endif
32845784
AG
1062 mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL(FolderFormat), folder_format_str,
1063 (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
ce1255c1 1064 }
32845784 1065@@ -630,6 +829,17 @@ static void init_menu (struct browser_st
ce1255c1
JB
1066
1067 menu->tagged = 0;
32845784 1068
ce1255c1
JB
1069+#ifdef USE_NNTP
1070+ if (option (OPTNEWS))
1071+ {
1072+ if (buffy)
1073+ snprintf (title, titlelen, _("Subscribed newsgroups"));
1074+ else
1075+ snprintf (title, titlelen, _("Newsgroups on server [%s]"),
1076+ CurrentNewsSrv->conn->account.host);
1077+ }
1078+ else
1079+#endif
1080 if (buffy)
1081 snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0));
1082 else
32845784 1083@@ -717,6 +927,31 @@ void _mutt_buffer_select_file (BUFFER *f
ce1255c1 1084 if (!folder)
32845784 1085 mutt_buffer_strcpy (LastDirBackup, mutt_b2s (LastDir));
ce1255c1
JB
1086
1087+#ifdef USE_NNTP
1088+ if (option (OPTNEWS))
1089+ {
32845784
AG
1090+ if (*(mutt_b2s (f)))
1091+ mutt_buffer_strcpy (prefix, mutt_b2s (f));
ce1255c1
JB
1092+ else
1093+ {
c02f3a2c
AG
1094+ NNTP_SERVER *nserv = CurrentNewsSrv;
1095+ unsigned int i;
ce1255c1
JB
1096+
1097+ /* default state for news reader mode is browse subscribed newsgroups */
1098+ buffy = 0;
c02f3a2c 1099+ for (i = 0; i < nserv->groups_num; i++)
ce1255c1 1100+ {
c02f3a2c
AG
1101+ NNTP_DATA *nntp_data = nserv->groups_list[i];
1102+ if (nntp_data && nntp_data->subscribed)
ce1255c1
JB
1103+ {
1104+ buffy = 1;
1105+ break;
1106+ }
1107+ }
1108+ }
1109+ }
1110+ else
1111+#endif
32845784 1112 if (*(mutt_b2s (f)))
ce1255c1 1113 {
32845784
AG
1114 mutt_buffer_expand_path (f);
1115@@ -812,6 +1047,9 @@ void _mutt_buffer_select_file (BUFFER *f
ce1255c1
JB
1116 menu->tag = file_tag;
1117
1118 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
1119+#ifdef USE_NNTP
32845784 1120+ option (OPTNEWS) ? FolderNewsHelp :
ce1255c1 1121+#endif
32845784 1122 FolderHelp);
c02f3a2c 1123 mutt_push_current_menu (menu);
ce1255c1 1124
32845784
AG
1125@@ -959,7 +1197,11 @@ void _mutt_buffer_select_file (BUFFER *f
1126 break;
1127 }
ce1255c1
JB
1128
1129+#ifdef USE_NNTP
c02f3a2c 1130+ if (buffy || option (OPTNEWS))
ce1255c1
JB
1131+#else
1132 if (buffy)
1133+#endif
1134 {
32845784
AG
1135 mutt_buffer_strcpy (f, state.entry[menu->current].name);
1136 mutt_buffer_expand_path (f);
1137@@ -1014,14 +1256,6 @@ void _mutt_buffer_select_file (BUFFER *f
ce1255c1
JB
1138 break;
1139
1140 #ifdef USE_IMAP
1141- case OP_BROWSER_SUBSCRIBE:
1142- imap_subscribe (state.entry[menu->current].name, 1);
1143- break;
1144-
1145- case OP_BROWSER_UNSUBSCRIBE:
1146- imap_subscribe (state.entry[menu->current].name, 0);
1147- break;
1148-
1149 case OP_BROWSER_TOGGLE_LSUB:
1150 if (option (OPTIMAPLSUB))
bebcbf73 1151 unset_option (OPTIMAPLSUB);
32845784
AG
1152@@ -1123,6 +1357,11 @@ void _mutt_buffer_select_file (BUFFER *f
1153
ce1255c1
JB
1154 case OP_CHANGE_DIRECTORY:
1155
1156+#ifdef USE_NNTP
1157+ if (option (OPTNEWS))
1158+ break;
1159+#endif
1160+
32845784 1161 mutt_buffer_strcpy (buf, mutt_b2s (LastDir));
ce1255c1
JB
1162 #ifdef USE_IMAP
1163 if (!state.imap_browse)
32845784
AG
1164@@ -1395,6 +1634,210 @@ void _mutt_buffer_select_file (BUFFER *f
1165 else
1166 mutt_error _("Error trying to view file");
1167 }
ce1255c1
JB
1168+ break;
1169+
1170+#ifdef USE_NNTP
1171+ case OP_CATCHUP:
1172+ case OP_UNCATCHUP:
1173+ if (option (OPTNEWS))
1174+ {
1175+ struct folder_file *f = &state.entry[menu->current];
c02f3a2c
AG
1176+ int rc;
1177+ NNTP_DATA *nntp_data;
1178+
1179+ rc = nntp_newsrc_parse (CurrentNewsSrv);
1180+ if (rc < 0)
1181+ break;
ce1255c1 1182+
32845784 1183+ if (op == OP_CATCHUP)
c02f3a2c 1184+ nntp_data = mutt_newsgroup_catchup (CurrentNewsSrv, f->name);
ce1255c1 1185+ else
c02f3a2c 1186+ nntp_data = mutt_newsgroup_uncatchup (CurrentNewsSrv, f->name);
ce1255c1 1187+
c02f3a2c 1188+ if (nntp_data)
ce1255c1
JB
1189+ {
1190+/* FOLDER folder;
1191+ struct folder_file ff;
1192+ char buffer[_POSIX_PATH_MAX + SHORT_STRING];
1193+
1194+ folder.ff = &ff;
1195+ folder.ff->name = f->name;
1196+ folder.ff->st = NULL;
c02f3a2c
AG
1197+ folder.ff->is_new = nntp_data->new;
1198+ folder.ff->nntp_data = nntp_data;
ce1255c1 1199+ FREE (&f->desc);
c02f3a2c
AG
1200+ mutt_FormatString (buffer, sizeof (buffer), 0, MuttIndexWindow->cols,
1201+ NONULL(GroupFormat), newsgroup_format_str,
1202+ (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
ce1255c1 1203+ f->desc = safe_strdup (buffer); */
c02f3a2c 1204+ nntp_newsrc_update (CurrentNewsSrv);
ce1255c1
JB
1205+ if (menu->current + 1 < menu->max)
1206+ menu->current++;
1207+ menu->redraw = REDRAW_MOTION_RESYNCH;
1208+ }
c02f3a2c
AG
1209+ if (rc)
1210+ menu->redraw = REDRAW_INDEX;
1211+ nntp_newsrc_close (CurrentNewsSrv);
ce1255c1
JB
1212+ }
1213+ break;
1214+
1215+ case OP_LOAD_ACTIVE:
c02f3a2c 1216+ if (option (OPTNEWS))
ce1255c1 1217+ {
c02f3a2c
AG
1218+ NNTP_SERVER *nserv = CurrentNewsSrv;
1219+ unsigned int i;
1220+
1221+ if (nntp_newsrc_parse (nserv) < 0)
1222+ break;
ce1255c1 1223+
c02f3a2c 1224+ for (i = 0; i < nserv->groups_num; i++)
ce1255c1 1225+ {
c02f3a2c
AG
1226+ NNTP_DATA *nntp_data = nserv->groups_list[i];
1227+ if (nntp_data)
1228+ nntp_data->deleted = 1;
ce1255c1 1229+ }
c02f3a2c
AG
1230+ nntp_active_fetch (nserv, 1);
1231+ nntp_newsrc_update (nserv);
1232+ nntp_newsrc_close (nserv);
ce1255c1 1233+
c02f3a2c
AG
1234+ destroy_state (&state);
1235+ if (buffy)
1236+ examine_mailboxes (menu, &state);
1237+ else
1238+ examine_directory (menu, &state, NULL, NULL);
1239+ init_menu (&state, menu, title, sizeof (title), buffy);
1240+ }
ce1255c1
JB
1241+ break;
1242+#endif /* USE_NNTP */
1243+
1244+#if defined USE_IMAP || defined USE_NNTP
1245+ case OP_BROWSER_SUBSCRIBE:
1246+ case OP_BROWSER_UNSUBSCRIBE:
1247+#endif
1248+#ifdef USE_NNTP
1249+ case OP_SUBSCRIBE_PATTERN:
1250+ case OP_UNSUBSCRIBE_PATTERN:
c02f3a2c 1251+ if (option (OPTNEWS))
ce1255c1 1252+ {
c02f3a2c
AG
1253+ NNTP_SERVER *nserv = CurrentNewsSrv;
1254+ NNTP_DATA *nntp_data;
ce1255c1 1255+ regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
32845784 1256+ char buf[_POSIX_PATH_MAX];
ce1255c1 1257+ char *s = buf;
c02f3a2c 1258+ int rc, j = menu->current;
ce1255c1 1259+
32845784 1260+ if (op == OP_SUBSCRIBE_PATTERN || op == OP_UNSUBSCRIBE_PATTERN)
ce1255c1
JB
1261+ {
1262+ char tmp[STRING];
1263+ int err;
1264+
1265+ buf[0] = 0;
32845784 1266+ if (op == OP_SUBSCRIBE_PATTERN)
ce1255c1
JB
1267+ snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
1268+ else
1269+ snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
1270+ if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0])
1271+ {
1272+ FREE (&rx);
1273+ break;
1274+ }
1275+
c02f3a2c
AG
1276+ err = REGCOMP (rx, s, REG_NOSUB);
1277+ if (err)
ce1255c1
JB
1278+ {
1279+ regerror (err, rx, buf, sizeof (buf));
1280+ regfree (rx);
1281+ FREE (&rx);
1282+ mutt_error ("%s", buf);
1283+ break;
1284+ }
1285+ menu->redraw = REDRAW_FULL;
1286+ j = 0;
1287+ }
1288+ else if (!state.entrylen)
1289+ {
1290+ mutt_error _("No newsgroups match the mask");
1291+ break;
1292+ }
1293+
c02f3a2c
AG
1294+ rc = nntp_newsrc_parse (nserv);
1295+ if (rc < 0)
1296+ break;
1297+
ce1255c1
JB
1298+ for ( ; j < state.entrylen; j++)
1299+ {
1300+ struct folder_file *f = &state.entry[j];
1301+
32845784 1302+ if (op == OP_BROWSER_SUBSCRIBE || op == OP_BROWSER_UNSUBSCRIBE ||
ce1255c1
JB
1303+ regexec (rx, f->name, 0, NULL, 0) == 0)
1304+ {
32845784 1305+ if (op == OP_BROWSER_SUBSCRIBE || op == OP_SUBSCRIBE_PATTERN)
c02f3a2c 1306+ nntp_data = mutt_newsgroup_subscribe (nserv, f->name);
ce1255c1 1307+ else
c02f3a2c
AG
1308+ nntp_data = mutt_newsgroup_unsubscribe (nserv, f->name);
1309+/* if (nntp_data)
ce1255c1
JB
1310+ {
1311+ FOLDER folder;
1312+ char buffer[_POSIX_PATH_MAX + SHORT_STRING];
1313+
1314+ folder.name = f->name;
1315+ folder.f = NULL;
c02f3a2c
AG
1316+ folder.new = nntp_data->new;
1317+ folder.nd = nntp_data;
ce1255c1 1318+ FREE (&f->desc);
c02f3a2c
AG
1319+ mutt_FormatString (buffer, sizeof (buffer), 0, MuttIndexWindow->cols,
1320+ NONULL(GroupFormat), newsgroup_format_str,
1321+ (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
ce1255c1
JB
1322+ f->desc = safe_strdup (buffer);
1323+ } */
1324+ }
32845784 1325+ if (op == OP_BROWSER_SUBSCRIBE || op == OP_BROWSER_UNSUBSCRIBE)
ce1255c1
JB
1326+ {
1327+ if (menu->current + 1 < menu->max)
1328+ menu->current++;
1329+ menu->redraw = REDRAW_MOTION_RESYNCH;
1330+ break;
1331+ }
1332+ }
32845784 1333+ if (op == OP_SUBSCRIBE_PATTERN)
ce1255c1 1334+ {
c02f3a2c 1335+ unsigned int i;
ce1255c1 1336+
c02f3a2c 1337+ for (i = 0; nserv && i < nserv->groups_num; i++)
ce1255c1 1338+ {
c02f3a2c
AG
1339+ nntp_data = nserv->groups_list[i];
1340+ if (nntp_data && nntp_data->group && !nntp_data->subscribed)
ce1255c1 1341+ {
c02f3a2c 1342+ if (regexec (rx, nntp_data->group, 0, NULL, 0) == 0)
ce1255c1 1343+ {
c02f3a2c
AG
1344+ mutt_newsgroup_subscribe (nserv, nntp_data->group);
1345+ add_folder (menu, &state, nntp_data->group, NULL, NULL, nntp_data);
ce1255c1
JB
1346+ }
1347+ }
1348+ }
1349+ init_menu (&state, menu, title, sizeof (title), buffy);
1350+ }
c02f3a2c
AG
1351+ if (rc > 0)
1352+ menu->redraw = REDRAW_FULL;
1353+ nntp_newsrc_update (nserv);
1354+ nntp_clear_cache (nserv);
1355+ nntp_newsrc_close (nserv);
32845784 1356+ if (op != OP_BROWSER_SUBSCRIBE && op != OP_BROWSER_UNSUBSCRIBE)
ce1255c1
JB
1357+ regfree (rx);
1358+ FREE (&rx);
1359+ }
1360+#ifdef USE_IMAP
1361+ else
1362+#endif /* USE_IMAP && USE_NNTP */
1363+#endif /* USE_NNTP */
1364+#ifdef USE_IMAP
1365+ {
32845784 1366+ if (op == OP_BROWSER_SUBSCRIBE)
ce1255c1
JB
1367+ imap_subscribe (state.entry[menu->current].name, 1);
1368+ else
1369+ imap_subscribe (state.entry[menu->current].name, 0);
1370+ }
1371+#endif /* USE_IMAP */
1372 }
1373 }
32845784
AG
1374
1375diff -udprP mutt-1.12.1.orig/browser.h mutt-1.12.1/browser.h
1376--- mutt-1.12.1.orig/browser.h 2019-05-25 19:22:39.000000000 +0300
1377+++ mutt-1.12.1/browser.h 2019-08-11 20:31:26.133940688 +0300
bebcbf73 1378@@ -19,6 +19,10 @@
ce1255c1
JB
1379 #ifndef _BROWSER_H
1380 #define _BROWSER_H 1
1381
1382+#ifdef USE_NNTP
1383+#include "nntp.h"
1384+#endif
1385+
1386 struct folder_file
1387 {
1388 mode_t mode;
c02f3a2c 1389@@ -44,6 +48,9 @@ struct folder_file
ce1255c1 1390 #endif
c02f3a2c
AG
1391 unsigned has_buffy : 1;
1392 unsigned local : 1; /* folder is on local filesystem */
ce1255c1
JB
1393+#ifdef USE_NNTP
1394+ NNTP_DATA *nd;
1395+#endif
1396 unsigned tagged : 1;
1397 };
1398
32845784
AG
1399diff -udprP mutt-1.12.1.orig/buffy.c mutt-1.12.1/buffy.c
1400--- mutt-1.12.1.orig/buffy.c 2019-05-25 19:22:39.000000000 +0300
1401+++ mutt-1.12.1/buffy.c 2019-08-11 20:31:26.134940673 +0300
1402@@ -570,6 +570,9 @@ int mutt_buffy_check (int force)
4adf126f
JB
1403
1404 /* check device ID and serial number instead of comparing paths */
c02f3a2c 1405 if (!Context || Context->magic == MUTT_IMAP || Context->magic == MUTT_POP
ce1255c1 1406+#ifdef USE_NNTP
c02f3a2c 1407+ || Context->magic == MUTT_NNTP
ce1255c1 1408+#endif
4adf126f 1409 || stat (Context->path, &contex_sb) != 0)
ce1255c1 1410 {
4adf126f 1411 contex_sb.st_dev=0;
32845784 1412@@ -593,6 +596,11 @@ int mutt_buffy_check (int force)
c02f3a2c 1413 tmp->magic = MUTT_POP;
4adf126f 1414 else
ce1255c1
JB
1415 #endif
1416+#ifdef USE_NNTP
32845784 1417+ if ((tmp->magic == MUTT_NNTP) || mx_is_nntp (mutt_b2s (tmp->pathbuf)))
c02f3a2c 1418+ tmp->magic = MUTT_NNTP;
4adf126f 1419+ else
ce1255c1 1420+#endif
32845784
AG
1421 if (stat (mutt_b2s (tmp->pathbuf), &sb) != 0 ||
1422 (S_ISREG(sb.st_mode) && sb.st_size == 0) ||
1423 (!tmp->magic &&
1424@@ -610,7 +618,11 @@ int mutt_buffy_check (int force)
ce1255c1
JB
1425 /* check to see if the folder is the currently selected folder
1426 * before polling */
1427 if (!Context || !Context->path ||
ce1255c1 1428+#ifdef USE_NNTP
c02f3a2c
AG
1429+ (( tmp->magic == MUTT_IMAP || tmp->magic == MUTT_POP || tmp->magic == MUTT_NNTP )
1430+#else
1431 (( tmp->magic == MUTT_IMAP || tmp->magic == MUTT_POP )
4adf126f 1432+#endif
32845784 1433 ? mutt_strcmp (mutt_b2s (tmp->pathbuf), Context->path) :
4adf126f 1434 (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino)))
ce1255c1 1435 {
32845784
AG
1436diff -udprP mutt-1.12.1.orig/complete.c mutt-1.12.1/complete.c
1437--- mutt-1.12.1.orig/complete.c 2019-05-25 19:22:39.000000000 +0300
1438+++ mutt-1.12.1/complete.c 2019-08-11 20:31:26.134940673 +0300
ce1255c1
JB
1439@@ -25,6 +25,9 @@
1440 #include "mailbox.h"
1441 #include "imap.h"
1442 #endif
1443+#ifdef USE_NNTP
1444+#include "nntp.h"
1445+#endif
1446
1447 #include <dirent.h>
1448 #include <string.h>
c02f3a2c 1449@@ -48,9 +51,70 @@ int mutt_complete (char *s, size_t slen)
ce1255c1
JB
1450 char filepart[_POSIX_PATH_MAX];
1451 #ifdef USE_IMAP
1452 char imap_path[LONG_STRING];
1453+#endif
1454
1455 dprint (2, (debugfile, "mutt_complete: completing %s\n", s));
1456
1457+#ifdef USE_NNTP
1458+ if (option (OPTNEWS))
1459+ {
c02f3a2c
AG
1460+ NNTP_SERVER *nserv = CurrentNewsSrv;
1461+ unsigned int n = 0;
ce1255c1
JB
1462+
1463+ strfcpy (filepart, s, sizeof (filepart));
1464+
c02f3a2c
AG
1465+ /* special case to handle when there is no filepart yet
1466+ * find the first subscribed newsgroup */
1467+ len = mutt_strlen (filepart);
1468+ if (len == 0)
ce1255c1 1469+ {
c02f3a2c 1470+ for (; n < nserv->groups_num; n++)
ce1255c1 1471+ {
c02f3a2c 1472+ NNTP_DATA *nntp_data = nserv->groups_list[n];
ce1255c1 1473+
c02f3a2c 1474+ if (nntp_data && nntp_data->subscribed)
ce1255c1 1475+ {
c02f3a2c
AG
1476+ strfcpy (filepart, nntp_data->group, sizeof (filepart));
1477+ init = 1;
1478+ n++;
ce1255c1
JB
1479+ break;
1480+ }
1481+ }
1482+ }
1483+
c02f3a2c 1484+ for (; n < nserv->groups_num; n++)
ce1255c1 1485+ {
c02f3a2c 1486+ NNTP_DATA *nntp_data = nserv->groups_list[n];
ce1255c1 1487+
c02f3a2c
AG
1488+ if (nntp_data && nntp_data->subscribed &&
1489+ mutt_strncmp (nntp_data->group, filepart, len) == 0)
ce1255c1
JB
1490+ {
1491+ if (init)
1492+ {
c02f3a2c 1493+ for (i = 0; filepart[i] && nntp_data->group[i]; i++)
ce1255c1 1494+ {
c02f3a2c 1495+ if (filepart[i] != nntp_data->group[i])
ce1255c1
JB
1496+ {
1497+ filepart[i] = 0;
1498+ break;
1499+ }
1500+ }
1501+ filepart[i] = 0;
1502+ }
1503+ else
1504+ {
c02f3a2c 1505+ strfcpy (filepart, nntp_data->group, sizeof (filepart));
ce1255c1
JB
1506+ init = 1;
1507+ }
1508+ }
1509+ }
1510+
1511+ strcpy (s, filepart);
ce1255c1
JB
1512+ return (init ? 0 : -1);
1513+ }
1514+#endif
1515+
1516+#ifdef USE_IMAP
1517 /* we can use '/' as a delimiter, imap_complete rewrites it */
1518 if (*s == '=' || *s == '+' || *s == '!')
1519 {
32845784
AG
1520diff -udprP mutt-1.12.1.orig/compose.c mutt-1.12.1/compose.c
1521--- mutt-1.12.1.orig/compose.c 2019-06-14 18:57:37.000000000 +0300
1522+++ mutt-1.12.1/compose.c 2019-08-11 20:31:26.134940673 +0300
1523@@ -33,11 +33,16 @@
ce1255c1
JB
1524 #include "mailbox.h"
1525 #include "sort.h"
1526 #include "charset.h"
1527+#include "mx.h"
1528
1529 #ifdef MIXMASTER
1530 #include "remailer.h"
1531 #endif
c02f3a2c 1532
ce1255c1
JB
1533+#ifdef USE_NNTP
1534+#include "nntp.h"
1535+#endif
c02f3a2c 1536+
ce1255c1
JB
1537 #include <errno.h>
1538 #include <string.h>
c02f3a2c 1539 #include <sys/stat.h>
32845784 1540@@ -69,10 +74,20 @@ enum
ce1255c1
JB
1541 HDR_CRYPT,
1542 HDR_CRYPTINFO,
1543
1544+#ifdef USE_NNTP
1545+ HDR_NEWSGROUPS,
1546+ HDR_FOLLOWUPTO,
1547+ HDR_XCOMMENTTO,
1548+#endif
1549+
1550 HDR_ATTACH = (HDR_FCC + 5) /* where to start printing the attachments */
1551 };
1552
ce1255c1 1553+#ifdef USE_NNTP
c02f3a2c
AG
1554+int HeaderPadding[HDR_XCOMMENTTO + 1] = {0};
1555+#else
1556 int HeaderPadding[HDR_CRYPTINFO + 1] = {0};
ce1255c1 1557+#endif
c02f3a2c
AG
1558 int MaxHeaderWidth = 0;
1559
1560 #define HDR_XOFFSET MaxHeaderWidth
32845784 1561@@ -110,6 +125,14 @@ static const char * const Prompts[] =
c02f3a2c
AG
1562 * than 15-20 character cells.
1563 */
1564 N_("Sign as: ")
1565+#ifdef USE_NNTP
1566+ /* L10N: Compose menu field. May not want to translate. */
1567+ ,N_("Newsgroups: ")
1568+ /* L10N: Compose menu field. May not want to translate. */
1569+ ,N_("Followup-To: ")
1570+ /* L10N: Compose menu field. May not want to translate. */
1571+ ,N_("X-Comment-To: ")
ce1255c1
JB
1572+#endif
1573 };
1574
c02f3a2c 1575 static const struct mapping_t ComposeHelp[] = {
32845784 1576@@ -127,6 +150,21 @@ static const struct mapping_t ComposeHel
4adf126f 1577 { NULL, 0 }
ce1255c1
JB
1578 };
1579
1580+#ifdef USE_NNTP
1581+static struct mapping_t ComposeNewsHelp[] = {
c02f3a2c
AG
1582+ { N_("Send"), OP_COMPOSE_SEND_MESSAGE },
1583+ { N_("Abort"), OP_EXIT },
1584+ /* L10N: compose menu help line entry */
1585+ { N_("Newsgroups"), OP_COMPOSE_EDIT_NEWSGROUPS },
1586+ /* L10N: compose menu help line entry */
1587+ { N_("Subj"), OP_COMPOSE_EDIT_SUBJECT },
1588+ { N_("Attach file"), OP_COMPOSE_ATTACH_FILE },
1589+ { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
1590+ { N_("Help"), OP_HELP },
1591+ { NULL, 0 }
ce1255c1
JB
1592+};
1593+#endif
1594+
c02f3a2c 1595 static void calc_header_width_padding (int idx, const char *header, int calc_max)
ce1255c1 1596 {
c02f3a2c 1597 int width;
32845784 1598@@ -162,7 +200,14 @@ static void init_header_padding (void)
c02f3a2c
AG
1599 * the other fields look funny. */
1600 calc_header_width_padding (HDR_CRYPTINFO, _(Prompts[HDR_CRYPTINFO]), 0);
4adf126f 1601
c02f3a2c
AG
1602+#ifdef USE_NNTP
1603+ for (i = HDR_NEWSGROUPS; i <= HDR_XCOMMENTTO; i++)
1604+ calc_header_width_padding (i, _(Prompts[i]), 1);
1605+
1606+ for (i = 0; i <= HDR_XCOMMENTTO; i++)
1607+#else
1608 for (i = 0; i <= HDR_CRYPTINFO; i++)
1609+#endif
4adf126f 1610 {
c02f3a2c
AG
1611 HeaderPadding[i] += MaxHeaderWidth;
1612 if (HeaderPadding[i] < 0)
32845784 1613@@ -358,9 +403,37 @@ static void draw_envelope_addr (int line
ce1255c1
JB
1614 static void draw_envelope (HEADER *msg, char *fcc)
1615 {
1616 draw_envelope_addr (HDR_FROM, msg->env->from);
1617+#ifdef USE_NNTP
c02f3a2c 1618+ if (option (OPTNEWSSEND))
ce1255c1 1619+ {
c02f3a2c
AG
1620+ SETCOLOR (MT_COLOR_COMPOSE_HEADER);
1621+ mutt_window_mvprintw (MuttIndexWindow, HDR_TO, 0,
1622+ "%*s", HeaderPadding[HDR_NEWSGROUPS], _(Prompts[HDR_NEWSGROUPS]));
1623+ NORMAL_COLOR;
ce1255c1 1624+ mutt_paddstr (W, NONULL (msg->env->newsgroups));
c02f3a2c
AG
1625+
1626+ SETCOLOR (MT_COLOR_COMPOSE_HEADER);
1627+ mutt_window_mvprintw (MuttIndexWindow, HDR_CC, 0,
1628+ "%*s", HeaderPadding[HDR_FOLLOWUPTO], _(Prompts[HDR_FOLLOWUPTO]));
1629+ NORMAL_COLOR;
ce1255c1 1630+ mutt_paddstr (W, NONULL (msg->env->followup_to));
c02f3a2c 1631+
ce1255c1
JB
1632+ if (option (OPTXCOMMENTTO))
1633+ {
c02f3a2c
AG
1634+ SETCOLOR (MT_COLOR_COMPOSE_HEADER);
1635+ mutt_window_mvprintw (MuttIndexWindow, HDR_BCC, 0,
1636+ "%*s", HeaderPadding[HDR_XCOMMENTTO], _(Prompts[HDR_XCOMMENTTO]));
1637+ NORMAL_COLOR;
ce1255c1
JB
1638+ mutt_paddstr (W, NONULL (msg->env->x_comment_to));
1639+ }
1640+ }
c02f3a2c 1641+ else
ce1255c1 1642+#endif
c02f3a2c
AG
1643+ {
1644 draw_envelope_addr (HDR_TO, msg->env->to);
1645 draw_envelope_addr (HDR_CC, msg->env->cc);
1646 draw_envelope_addr (HDR_BCC, msg->env->bcc);
1647+ }
1648
1649 SETCOLOR (MT_COLOR_COMPOSE_HEADER);
1650 mutt_window_mvprintw (MuttIndexWindow, HDR_SUBJECT, 0,
32845784 1651@@ -713,6 +786,12 @@ int mutt_compose_menu (HEADER *msg, /*
ce1255c1
JB
1652 int oldSort, oldSortAux;
1653 struct stat st;
c02f3a2c 1654 compose_redraw_data_t rd;
ce1255c1 1655+#ifdef USE_NNTP
c02f3a2c 1656+ int news = 0; /* is it a news article ? */
ce1255c1
JB
1657+
1658+ if (option (OPTNEWSSEND))
1659+ news++;
1660+#endif
1661
c02f3a2c
AG
1662 init_header_padding ();
1663
32845784 1664@@ -723,6 +802,11 @@ int mutt_compose_menu (HEADER *msg, /*
c02f3a2c 1665 menu->offset = HDR_ATTACH;
ce1255c1
JB
1666 menu->make_entry = snd_entry;
1667 menu->tag = mutt_tag_attach;
ce1255c1
JB
1668+#ifdef USE_NNTP
1669+ if (news)
c02f3a2c 1670+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeNewsHelp);
ce1255c1
JB
1671+ else
1672+#endif
1673 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
c02f3a2c
AG
1674 menu->custom_menu_redraw = compose_menu_redraw;
1675 menu->redraw_data = &rd;
32845784 1676@@ -734,6 +818,9 @@ int mutt_compose_menu (HEADER *msg, /*
c02f3a2c 1677
ce1255c1
JB
1678 while (loop)
1679 {
1680+#ifdef USE_NNTP
1681+ unset_option (OPTNEWS); /* for any case */
1682+#endif
1683 switch (op = mutt_menuLoop (menu))
1684 {
c02f3a2c 1685 case OP_COMPOSE_EDIT_FROM:
32845784 1686@@ -741,6 +828,10 @@ int mutt_compose_menu (HEADER *msg, /*
c02f3a2c 1687 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
ce1255c1
JB
1688 break;
1689 case OP_COMPOSE_EDIT_TO:
1690+#ifdef USE_NNTP
1691+ if (news)
1692+ break;
1693+#endif
c02f3a2c
AG
1694 edit_address_list (HDR_TO, &msg->env->to);
1695 if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
1696 {
32845784 1697@@ -750,6 +841,10 @@ int mutt_compose_menu (HEADER *msg, /*
c02f3a2c 1698 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
ce1255c1
JB
1699 break;
1700 case OP_COMPOSE_EDIT_BCC:
1701+#ifdef USE_NNTP
1702+ if (news)
1703+ break;
1704+#endif
c02f3a2c
AG
1705 edit_address_list (HDR_BCC, &msg->env->bcc);
1706 if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
1707 {
32845784 1708@@ -759,6 +854,10 @@ int mutt_compose_menu (HEADER *msg, /*
c02f3a2c 1709 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
ce1255c1
JB
1710 break;
1711 case OP_COMPOSE_EDIT_CC:
1712+#ifdef USE_NNTP
1713+ if (news)
1714+ break;
1715+#endif
c02f3a2c
AG
1716 edit_address_list (HDR_CC, &msg->env->cc);
1717 if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
1718 {
32845784 1719@@ -767,6 +866,62 @@ int mutt_compose_menu (HEADER *msg, /*
c02f3a2c 1720 }
32845784 1721 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
ce1255c1
JB
1722 break;
1723+#ifdef USE_NNTP
1724+ case OP_COMPOSE_EDIT_NEWSGROUPS:
1725+ if (news)
1726+ {
1727+ if (msg->env->newsgroups)
1728+ strfcpy (buf, msg->env->newsgroups, sizeof (buf));
1729+ else
1730+ buf[0] = 0;
c02f3a2c 1731+ if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) == 0)
ce1255c1 1732+ {
c02f3a2c 1733+ mutt_str_replace (&msg->env->newsgroups, buf);
ce1255c1 1734+ move (HDR_TO, HDR_XOFFSET);
ce1255c1 1735+ if (msg->env->newsgroups)
c02f3a2c
AG
1736+ mutt_paddstr (W, msg->env->newsgroups);
1737+ else
1738+ clrtoeol ();
ce1255c1
JB
1739+ }
1740+ }
1741+ break;
ce1255c1
JB
1742+ case OP_COMPOSE_EDIT_FOLLOWUP_TO:
1743+ if (news)
1744+ {
ce1255c1
JB
1745+ if (msg->env->followup_to)
1746+ strfcpy (buf, msg->env->followup_to, sizeof (buf));
c02f3a2c
AG
1747+ else
1748+ buf[0] = 0;
1749+ if (mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) == 0)
ce1255c1 1750+ {
c02f3a2c 1751+ mutt_str_replace (&msg->env->followup_to, buf);
ce1255c1 1752+ move (HDR_CC, HDR_XOFFSET);
ce1255c1 1753+ if (msg->env->followup_to)
c02f3a2c
AG
1754+ mutt_paddstr (W, msg->env->followup_to);
1755+ else
1756+ clrtoeol ();
ce1255c1
JB
1757+ }
1758+ }
c02f3a2c 1759+ break;
ce1255c1
JB
1760+ case OP_COMPOSE_EDIT_X_COMMENT_TO:
1761+ if (news && option (OPTXCOMMENTTO))
1762+ {
ce1255c1
JB
1763+ if (msg->env->x_comment_to)
1764+ strfcpy (buf, msg->env->x_comment_to, sizeof (buf));
c02f3a2c
AG
1765+ else
1766+ buf[0] = 0;
1767+ if (mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) == 0)
ce1255c1 1768+ {
c02f3a2c 1769+ mutt_str_replace (&msg->env->x_comment_to, buf);
ce1255c1 1770+ move (HDR_BCC, HDR_XOFFSET);
ce1255c1 1771+ if (msg->env->x_comment_to)
c02f3a2c
AG
1772+ mutt_paddstr (W, msg->env->x_comment_to);
1773+ else
1774+ clrtoeol ();
ce1255c1
JB
1775+ }
1776+ }
1777+ break;
1778+#endif
1779 case OP_COMPOSE_EDIT_SUBJECT:
1780 if (msg->env->subject)
1781 strfcpy (buf, msg->env->subject, sizeof (buf));
32845784
AG
1782@@ -911,6 +1066,9 @@ int mutt_compose_menu (HEADER *msg, /*
1783 break;
ce1255c1
JB
1784
1785 case OP_COMPOSE_ATTACH_MESSAGE:
1786+#ifdef USE_NNTP
1787+ case OP_COMPOSE_ATTACH_NEWS_MESSAGE:
1788+#endif
32845784
AG
1789 {
1790 char *prompt;
1791 HEADER *h;
1792@@ -918,7 +1076,22 @@ int mutt_compose_menu (HEADER *msg, /*
1793 fname[0] = 0;
1794 prompt = _("Open mailbox to attach message from");
ce1255c1
JB
1795
1796+#ifdef USE_NNTP
32845784
AG
1797+ unset_option (OPTNEWS);
1798+ if (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE)
1799+ {
1800+ if (!(CurrentNewsSrv = nntp_select_server (NewsServer, 0)))
1801+ break;
ce1255c1 1802+
32845784
AG
1803+ prompt = _("Open newsgroup to attach message from");
1804+ set_option (OPTNEWS);
1805+ }
ce1255c1
JB
1806+#endif
1807+
32845784 1808 if (Context)
ce1255c1 1809+#ifdef USE_NNTP
32845784 1810+ if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->magic == MUTT_NNTP))
ce1255c1 1811+#endif
32845784
AG
1812 {
1813 strfcpy (fname, NONULL (Context->path), sizeof (fname));
1814 mutt_pretty_mailbox (fname, sizeof (fname));
1815@@ -934,6 +1107,9 @@ int mutt_compose_menu (HEADER *msg, /*
1816 #ifdef USE_POP
1817 if (!mx_is_pop (fname))
1818 #endif
1819+#ifdef USE_NNTP
1820+ if (!mx_is_nntp (fname) && !option (OPTNEWS))
1821+#endif
1822 /* check to make sure the file exists and is readable */
1823 if (access (fname, R_OK) == -1)
1824 {
1825@@ -1219,6 +1395,19 @@ int mutt_compose_menu (HEADER *msg, /*
ce1255c1 1826 break;
32845784 1827 }
ce1255c1
JB
1828
1829+#ifdef USE_NNTP
1830+ if (option (OPTNEWS))
32845784
AG
1831+ {
1832+ BUFFER *s_buf;
1833+
1834+ s_buf = mutt_buffer_pool_get ();
1835+ mutt_buffer_addstr (s_buf, fname);
1836+ nntp_buffer_expand_path (s_buf, &CurrentNewsSrv->conn->account);
1837+ strfcpy (fname, mutt_b2s (s_buf), sizeof (fname));
1838+ mutt_buffer_pool_release (&s_buf);
1839+ }
ce1255c1
JB
1840+ else
1841+#endif
1842 mutt_expand_path (fname, sizeof (fname));
32845784
AG
1843 if (mutt_rename_file (CURATTACH->content->filename, fname))
1844 break;
1845diff -udprP mutt-1.12.1.orig/config.h.in mutt-1.12.1/config.h.in
1846--- mutt-1.12.1.orig/config.h.in 2019-06-15 19:07:25.000000000 +0300
1847+++ mutt-1.12.1/config.h.in 2019-08-11 20:31:26.134940673 +0300
1848@@ -665,6 +665,9 @@
1849 only). */
1850 #undef USE_INOTIFY
c02f3a2c
AG
1851
1852+/* Define if you want support for the NNTP protocol. */
ce1255c1
JB
1853+#undef USE_NNTP
1854+
c02f3a2c
AG
1855 /* Define if you want support for the POP3 protocol. */
1856 #undef USE_POP
ce1255c1 1857
32845784
AG
1858diff -udprP mutt-1.12.1.orig/configure mutt-1.12.1/configure
1859--- mutt-1.12.1.orig/configure 2019-06-15 19:04:19.000000000 +0300
1860+++ mutt-1.12.1/configure 2019-08-11 20:31:26.136940643 +0300
1861@@ -813,6 +813,7 @@ with_docdir
1862 with_domain
1863 enable_pop
1864 enable_imap
1865+enable_nntp
1866 enable_smtp
1867 with_gss
1868 with_ssl
1869@@ -1499,6 +1500,7 @@ Optional Features:
1870 Force use of an external dotlock program
1871 --enable-pop Enable POP3 support
1872 --enable-imap Enable IMAP support
1873+ --enable-nntp Enable NNTP support
1874 --enable-smtp include internal SMTP relay support
1875 --enable-debug Enable debugging support
1876 --enable-flock Use flock() to lock files
1877@@ -10108,6 +10110,20 @@ else
1878 fi
1879
1880
1881+# Check whether --enable-nntp was given.
1882+if test "${enable_nntp+set}" = set; then :
1883+ enableval=$enable_nntp; if test x$enableval = xyes ; then
1884+
1885+$as_echo "#define USE_NNTP 1" >>confdefs.h
1886+
1887+ MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS nntp.o newsrc.o"
1888+ need_nntp="yes"
1889+ need_socket="yes"
1890+ fi
1891+
1892+fi
1893+
1894+
1895 # Check whether --enable-smtp was given.
1896 if test "${enable_smtp+set}" = set; then :
1897 enableval=$enable_smtp; if test $enableval = yes; then
1898@@ -10120,7 +10136,7 @@ $as_echo "#define USE_SMTP 1" >>confdefs
1899 fi
1900
1901
1902-if test x"$need_imap" = xyes -o x"$need_pop" = xyes ; then
1903+if test x"$need_imap" = xyes -o x"$need_pop" = xyes -o x"$need_nntp" = xyes ; then
1904 MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS bcache.o"
1905 fi
1906
1907diff -udprP mutt-1.12.1.orig/configure.ac mutt-1.12.1/configure.ac
1908--- mutt-1.12.1.orig/configure.ac 2019-06-15 18:57:01.000000000 +0300
1909+++ mutt-1.12.1/configure.ac 2019-08-11 20:31:26.136940643 +0300
1910@@ -648,6 +648,15 @@ AC_ARG_ENABLE(imap, AS_HELP_STRING([--en
c02f3a2c
AG
1911 ])
1912 AM_CONDITIONAL(BUILD_IMAP, test x$need_imap = xyes)
ce1255c1 1913
c02f3a2c 1914+AC_ARG_ENABLE(nntp, AC_HELP_STRING([--enable-nntp],[Enable NNTP support]),
ce1255c1
JB
1915+[ if test x$enableval = xyes ; then
1916+ AC_DEFINE(USE_NNTP,1,[ Define if you want support for the NNTP protocol. ])
1917+ MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS nntp.o newsrc.o"
c02f3a2c 1918+ need_nntp="yes"
ce1255c1
JB
1919+ need_socket="yes"
1920+ fi
1921+])
1922+
c02f3a2c
AG
1923 AC_ARG_ENABLE(smtp, AS_HELP_STRING([--enable-smtp],[include internal SMTP relay support]),
1924 [if test $enableval = yes; then
1925 AC_DEFINE(USE_SMTP, 1, [Include internal SMTP relay support])
32845784 1926@@ -655,7 +664,7 @@ AC_ARG_ENABLE(smtp, AS_HELP_STRING([--en
c02f3a2c
AG
1927 need_socket="yes"
1928 fi])
1929
1930-if test x"$need_imap" = xyes -o x"$need_pop" = xyes ; then
1931+if test x"$need_imap" = xyes -o x"$need_pop" = xyes -o x"$need_nntp" = xyes ; then
1932 MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS bcache.o"
1933 fi
ce1255c1 1934
32845784
AG
1935diff -udprP mutt-1.12.1.orig/curs_main.c mutt-1.12.1/curs_main.c
1936--- mutt-1.12.1.orig/curs_main.c 2019-06-11 03:53:09.000000000 +0300
1937+++ mutt-1.12.1/curs_main.c 2019-08-11 20:31:26.136940643 +0300
ce1255c1
JB
1938@@ -22,6 +22,7 @@
1939
1940 #include "mutt.h"
1941 #include "mutt_curses.h"
1942+#include "mx.h"
1943 #include "mutt_menu.h"
ce1255c1 1944 #include "mailbox.h"
bebcbf73 1945 #include "mapping.h"
32845784 1946@@ -47,6 +48,10 @@
ce1255c1
JB
1947
1948 #include "mutt_crypt.h"
1949
1950+#ifdef USE_NNTP
1951+#include "nntp.h"
1952+#endif
1953+
1954
1955 #include <ctype.h>
1956 #include <stdlib.h>
32845784 1957@@ -599,12 +604,27 @@ static void index_menu_redraw (MUTTMENU
c02f3a2c
AG
1958 menu->redraw = 0;
1959 }
ce1255c1
JB
1960
1961+#ifdef USE_NNTP
1962+struct mapping_t IndexNewsHelp[] = {
1963+ { N_("Quit"), OP_QUIT },
1964+ { N_("Del"), OP_DELETE },
1965+ { N_("Undel"), OP_UNDELETE },
1966+ { N_("Save"), OP_SAVE },
1967+ { N_("Post"), OP_POST },
1968+ { N_("Followup"), OP_FOLLOWUP },
1969+ { N_("Catchup"), OP_CATCHUP },
1970+ { N_("Help"), OP_HELP },
c02f3a2c 1971+ { NULL, 0 }
ce1255c1
JB
1972+};
1973+#endif
1974+
1975 /* This function handles the message index window as well as commands returned
1976 * from the pager (MENU_PAGER).
1977 */
1978 int mutt_index_menu (void)
1979 {
1980 char buf[LONG_STRING], helpstr[LONG_STRING];
1981+ int flags;
1982 int op = OP_NULL;
1983 int done = 0; /* controls when to exit the "event" loop */
1984 int i = 0, j;
32845784 1985@@ -623,7 +643,11 @@ int mutt_index_menu (void)
ce1255c1
JB
1986 menu->make_entry = index_make_entry;
1987 menu->color = index_color;
1988 menu->current = ci_first_message ();
1989- menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN, IndexHelp);
1990+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
1991+#ifdef USE_NNTP
c02f3a2c 1992+ (Context && (Context->magic == MUTT_NNTP)) ? IndexNewsHelp :
ce1255c1
JB
1993+#endif
1994+ IndexHelp);
c02f3a2c
AG
1995 menu->custom_menu_redraw = index_menu_redraw;
1996 mutt_push_current_menu (menu);
4adf126f 1997
32845784 1998@@ -845,6 +869,9 @@ int mutt_index_menu (void)
4adf126f
JB
1999 mutt_curs_set (1); /* fallback from the pager */
2000 }
ce1255c1
JB
2001
2002+#ifdef USE_NNTP
2003+ unset_option (OPTNEWS); /* for any case */
2004+#endif
2005 switch (op)
2006 {
2007
32845784 2008@@ -895,6 +922,161 @@ int mutt_index_menu (void)
ce1255c1
JB
2009 menu_current_bottom (menu);
2010 break;
2011
2012+#ifdef USE_NNTP
ce1255c1
JB
2013+ case OP_GET_PARENT:
2014+ CHECK_MSGCOUNT;
c02f3a2c
AG
2015+ CHECK_VISIBLE;
2016+
2017+ case OP_GET_MESSAGE:
2018+ CHECK_IN_MAILBOX;
2019+ CHECK_READONLY;
2020+ CHECK_ATTACH;
2021+ if (Context->magic == MUTT_NNTP)
ce1255c1 2022+ {
c02f3a2c 2023+ HEADER *hdr;
ce1255c1
JB
2024+
2025+ if (op == OP_GET_MESSAGE)
2026+ {
2027+ buf[0] = 0;
c02f3a2c
AG
2028+ if (mutt_get_field (_("Enter Message-Id: "),
2029+ buf, sizeof (buf), 0) != 0 || !buf[0])
ce1255c1
JB
2030+ break;
2031+ }
2032+ else
2033+ {
2034+ LIST *ref = CURHDR->env->references;
2035+ if (!ref)
2036+ {
c02f3a2c 2037+ mutt_error _("Article has no parent reference.");
ce1255c1
JB
2038+ break;
2039+ }
2040+ strfcpy (buf, ref->data, sizeof (buf));
2041+ }
2042+ if (!Context->id_hash)
2043+ Context->id_hash = mutt_make_id_hash (Context);
c02f3a2c
AG
2044+ hdr = hash_find (Context->id_hash, buf);
2045+ if (hdr)
ce1255c1 2046+ {
c02f3a2c 2047+ if (hdr->virtual != -1)
ce1255c1 2048+ {
c02f3a2c 2049+ menu->current = hdr->virtual;
ce1255c1
JB
2050+ menu->redraw = REDRAW_MOTION_RESYNCH;
2051+ }
c02f3a2c 2052+ else if (hdr->collapsed)
ce1255c1 2053+ {
c02f3a2c 2054+ mutt_uncollapse_thread (Context, hdr);
ce1255c1 2055+ mutt_set_virtual (Context);
c02f3a2c 2056+ menu->current = hdr->virtual;
ce1255c1
JB
2057+ menu->redraw = REDRAW_MOTION_RESYNCH;
2058+ }
2059+ else
c02f3a2c 2060+ mutt_error _("Message is not visible in limited view.");
ce1255c1
JB
2061+ }
2062+ else
2063+ {
c02f3a2c
AG
2064+ int rc;
2065+
2066+ mutt_message (_("Fetching %s from server..."), buf);
2067+ rc = nntp_check_msgid (Context, buf);
2068+ if (rc == 0)
ce1255c1 2069+ {
c02f3a2c 2070+ hdr = Context->hdrs[Context->msgcount - 1];
ce1255c1 2071+ mutt_sort_headers (Context, 0);
c02f3a2c 2072+ menu->current = hdr->virtual;
ce1255c1
JB
2073+ menu->redraw = REDRAW_FULL;
2074+ }
c02f3a2c
AG
2075+ else if (rc > 0)
2076+ mutt_error (_("Article %s not found on the server."), buf);
ce1255c1
JB
2077+ }
2078+ }
2079+ break;
2080+
2081+ case OP_GET_CHILDREN:
2082+ case OP_RECONSTRUCT_THREAD:
2083+ CHECK_MSGCOUNT;
c02f3a2c
AG
2084+ CHECK_VISIBLE;
2085+ CHECK_READONLY;
2086+ CHECK_ATTACH;
2087+ if (Context->magic == MUTT_NNTP)
ce1255c1 2088+ {
c02f3a2c
AG
2089+ int oldmsgcount = Context->msgcount;
2090+ int oldindex = CURHDR->index;
2091+ int rc = 0;
ce1255c1
JB
2092+
2093+ if (!CURHDR->env->message_id)
2094+ {
c02f3a2c 2095+ mutt_error _("No Message-Id. Unable to perform operation.");
ce1255c1
JB
2096+ break;
2097+ }
2098+
c02f3a2c 2099+ mutt_message _("Fetching message headers...");
ce1255c1
JB
2100+ if (!Context->id_hash)
2101+ Context->id_hash = mutt_make_id_hash (Context);
2102+ strfcpy (buf, CURHDR->env->message_id, sizeof (buf));
2103+
c02f3a2c 2104+ /* trying to find msgid of the root message */
ce1255c1
JB
2105+ if (op == OP_RECONSTRUCT_THREAD)
2106+ {
2107+ LIST *ref = CURHDR->env->references;
2108+ while (ref)
2109+ {
c02f3a2c
AG
2110+ if (hash_find (Context->id_hash, ref->data) == NULL)
2111+ {
2112+ rc = nntp_check_msgid (Context, ref->data);
2113+ if (rc < 0)
2114+ break;
2115+ }
2116+
ce1255c1
JB
2117+ /* the last msgid in References is the root message */
2118+ if (!ref->next)
2119+ strfcpy (buf, ref->data, sizeof (buf));
2120+ ref = ref->next;
2121+ }
2122+ }
c02f3a2c
AG
2123+
2124+ /* fetching all child messages */
2125+ if (rc >= 0)
2126+ rc = nntp_check_children (Context, buf);
2127+
2128+ /* at least one message has been loaded */
2129+ if (Context->msgcount > oldmsgcount)
ce1255c1 2130+ {
c02f3a2c
AG
2131+ HEADER *hdr;
2132+ int i, quiet = Context->quiet;
2133+
2134+ if (rc < 0)
2135+ Context->quiet = 1;
ce1255c1 2136+ mutt_sort_headers (Context, (op == OP_RECONSTRUCT_THREAD));
c02f3a2c
AG
2137+ Context->quiet = quiet;
2138+
ce1255c1 2139+ /* if the root message was retrieved, move to it */
c02f3a2c
AG
2140+ hdr = hash_find (Context->id_hash, buf);
2141+ if (hdr)
2142+ menu->current = hdr->virtual;
2143+
2144+ /* try to restore old position */
2145+ else
2146+ {
ce1255c1 2147+ for (i = 0; i < Context->msgcount; i++)
c02f3a2c
AG
2148+ {
2149+ if (Context->hdrs[i]->index == oldindex)
ce1255c1
JB
2150+ {
2151+ menu->current = Context->hdrs[i]->virtual;
c02f3a2c 2152+ /* as an added courtesy, recenter the menu
ce1255c1
JB
2153+ * with the current entry at the middle of the screen */
2154+ menu_check_recenter (menu);
2155+ menu_current_middle (menu);
2156+ }
c02f3a2c
AG
2157+ }
2158+ }
2159+ menu->redraw = REDRAW_FULL;
ce1255c1 2160+ }
c02f3a2c
AG
2161+ else if (rc >= 0)
2162+ mutt_error _("No deleted messages found in the thread.");
ce1255c1
JB
2163+ }
2164+ break;
2165+#endif
2166+
2167 case OP_JUMP:
2168
2169 CHECK_MSGCOUNT;
32845784 2170@@ -1005,11 +1187,33 @@ int mutt_index_menu (void)
ce1255c1
JB
2171 break;
2172
2173 case OP_MAIN_LIMIT:
2174+ case OP_TOGGLE_READ:
2175
2176 CHECK_IN_MAILBOX;
2177 menu->oldcurrent = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ?
32845784 2178 CURHDR->index : -1;
c02f3a2c 2179- if (mutt_pattern_func (MUTT_LIMIT, _("Limit to messages matching: ")) == 0)
ce1255c1
JB
2180+ if (op == OP_TOGGLE_READ)
2181+ {
2182+ char buf[LONG_STRING];
2183+
2184+ if (!Context->pattern || strncmp (Context->pattern, "!~R!~D~s", 8) != 0)
2185+ {
2186+ snprintf (buf, sizeof (buf), "!~R!~D~s%s",
2187+ Context->pattern ? Context->pattern : ".*");
2188+ set_option (OPTHIDEREAD);
2189+ }
2190+ else
2191+ {
2192+ strfcpy (buf, Context->pattern + 8, sizeof(buf));
2193+ if (!*buf || strncmp (buf, ".*", 2) == 0)
2194+ snprintf (buf, sizeof(buf), "~A");
2195+ unset_option (OPTHIDEREAD);
2196+ }
2197+ FREE (&Context->pattern);
2198+ Context->pattern = safe_strdup (buf);
2199+ }
c02f3a2c
AG
2200+ if ((op == OP_TOGGLE_READ && mutt_pattern_func (MUTT_LIMIT, NULL) == 0) ||
2201+ mutt_pattern_func (MUTT_LIMIT, _("Limit to messages matching: ")) == 0)
ce1255c1
JB
2202 {
2203 if (menu->oldcurrent >= 0)
2204 {
32845784 2205@@ -1258,20 +1462,30 @@ int mutt_index_menu (void)
c02f3a2c 2206 #endif
ce1255c1
JB
2207 case OP_MAIN_CHANGE_FOLDER:
2208 case OP_MAIN_NEXT_UNREAD_MAILBOX:
4adf126f 2209-
ce1255c1
JB
2210- if (attach_msg)
2211- op = OP_MAIN_CHANGE_FOLDER_READONLY;
2212-
2213- /* fallback to the readonly case */
2214-
2215 case OP_MAIN_CHANGE_FOLDER_READONLY:
2216+#ifdef USE_NNTP
2217+ case OP_MAIN_CHANGE_GROUP:
2218+ case OP_MAIN_CHANGE_GROUP_READONLY:
ce1255c1 2219+#endif
32845784
AG
2220 {
2221 BUFFER *folderbuf;
2222 int cont = 0; /* Set if we want to continue instead of break */
2223
2224 folderbuf = mutt_buffer_pool_get ();
ce1255c1
JB
2225
2226- if ((op == OP_MAIN_CHANGE_FOLDER_READONLY) || option (OPTREADONLY))
32845784
AG
2227+#ifdef USE_NNTP
2228+ unset_option (OPTNEWS);
2229+#endif
2230+ if (attach_msg || option (OPTREADONLY) ||
2231+#ifdef USE_NNTP
2232+ op == OP_MAIN_CHANGE_GROUP_READONLY ||
2233+#endif
2234+ op == OP_MAIN_CHANGE_FOLDER_READONLY)
2235+ flags = MUTT_READONLY;
2236+ else
2237+ flags = 0;
2238+
2239+ if (flags)
ce1255c1
JB
2240 cp = _("Open mailbox in read-only mode");
2241 else
2242 cp = _("Open mailbox");
32845784
AG
2243@@ -1303,6 +1517,22 @@ int mutt_index_menu (void)
2244 mutt_buffer_strcpy (folderbuf, Context->path);
2245 mutt_buffer_pretty_mailbox (folderbuf);
c02f3a2c 2246 }
ce1255c1
JB
2247+#ifdef USE_NNTP
2248+ if (op == OP_MAIN_CHANGE_GROUP ||
2249+ op == OP_MAIN_CHANGE_GROUP_READONLY)
2250+ {
2251+ set_option (OPTNEWS);
c02f3a2c
AG
2252+ CurrentNewsSrv = nntp_select_server (NewsServer, 0);
2253+ if (!CurrentNewsSrv)
ce1255c1
JB
2254+ break;
2255+ if (flags)
2256+ cp = _("Open newsgroup in read-only mode");
2257+ else
2258+ cp = _("Open newsgroup");
32845784 2259+ nntp_buffer_buffy (folderbuf);
ce1255c1
JB
2260+ }
2261+ else
2262+#endif
32845784 2263 mutt_buffer_buffy (folderbuf);
ce1255c1 2264
32845784
AG
2265 if (mutt_buffer_enter_fname (cp, folderbuf, 1) == -1)
2266@@ -1321,6 +1551,14 @@ int mutt_index_menu (void)
ce1255c1
JB
2267 }
2268 }
2269
2270+#ifdef USE_NNTP
2271+ if (option (OPTNEWS))
2272+ {
2273+ unset_option (OPTNEWS);
32845784 2274+ nntp_buffer_expand_path (folderbuf, &CurrentNewsSrv->conn->account);
ce1255c1
JB
2275+ }
2276+ else
2277+#endif
32845784
AG
2278 mutt_buffer_expand_path (folderbuf);
2279 if (mx_get_magic (mutt_b2s (folderbuf)) <= 0)
ce1255c1 2280 {
32845784 2281@@ -1381,9 +1619,7 @@ int mutt_index_menu (void)
c02f3a2c 2282 * switch statement would need to be run. */
32845784 2283 mutt_folder_hook (mutt_b2s (folderbuf));
ce1255c1 2284
32845784 2285- if ((Context = mx_open_mailbox (mutt_b2s (folderbuf),
ce1255c1 2286- (option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
c02f3a2c 2287- MUTT_READONLY : 0, NULL)) != NULL)
32845784 2288+ if ((Context = mx_open_mailbox (mutt_b2s (folderbuf), flags, NULL)) != NULL)
ce1255c1
JB
2289 {
2290 menu->current = ci_first_message ();
32845784
AG
2291 #ifdef USE_INOTIFY
2292@@ -1397,6 +1633,12 @@ int mutt_index_menu (void)
c02f3a2c
AG
2293 mutt_sb_set_open_buffy ();
2294 #endif
ce1255c1
JB
2295
2296+#ifdef USE_NNTP
2297+ /* mutt_buffy_check() must be done with mail-reader mode! */
2298+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
c02f3a2c 2299+ (Context && (Context->magic == MUTT_NNTP)) ? IndexNewsHelp : IndexHelp);
ce1255c1 2300+#endif
c02f3a2c 2301+
ce1255c1 2302 mutt_clear_error ();
32845784
AG
2303 mutt_buffy_check(MUTT_BUFFY_CHECK_FORCE); /* force the buffy check after
2304 we have changed the
2305@@ -1482,6 +1724,7 @@ int mutt_index_menu (void)
c02f3a2c
AG
2306 CHECK_MSGCOUNT;
2307 CHECK_VISIBLE;
ce1255c1 2308 CHECK_READONLY;
c02f3a2c 2309+ CHECK_ACL(MUTT_ACL_WRITE, _("Cannot break thread"));
ce1255c1 2310
c02f3a2c
AG
2311 if ((Sort & SORT_MASK) != SORT_THREADS)
2312 mutt_error _("Threading is not enabled.");
32845784 2313@@ -1517,7 +1760,7 @@ int mutt_index_menu (void)
c02f3a2c
AG
2314 CHECK_VISIBLE;
2315 CHECK_READONLY;
2316 /* L10N: CHECK_ACL */
2317- CHECK_ACL(MUTT_ACL_DELETE, _("Cannot link threads"));
2318+ CHECK_ACL(MUTT_ACL_WRITE, _("Cannot link threads"));
2319
2320 if ((Sort & SORT_MASK) != SORT_THREADS)
2321 mutt_error _("Threading is not enabled.");
32845784 2322@@ -2170,6 +2413,20 @@ int mutt_index_menu (void)
ce1255c1
JB
2323 }
2324 break;
2325
2326+#ifdef USE_NNTP
2327+ case OP_CATCHUP:
c02f3a2c
AG
2328+ CHECK_MSGCOUNT;
2329+ CHECK_READONLY;
2330+ CHECK_ATTACH
2331+ if (Context && Context->magic == MUTT_NNTP)
ce1255c1 2332+ {
c02f3a2c
AG
2333+ NNTP_DATA *nntp_data = Context->data;
2334+ if (mutt_newsgroup_catchup (nntp_data->nserv, nntp_data->group))
ce1255c1
JB
2335+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2336+ }
2337+ break;
2338+#endif
2339+
2340 case OP_DISPLAY_ADDRESS:
2341
2342 CHECK_MSGCOUNT;
32845784 2343@@ -2408,6 +2665,39 @@ int mutt_index_menu (void)
ce1255c1
JB
2344 menu->redraw = REDRAW_FULL;
2345 break;
4adf126f 2346
ce1255c1 2347+#ifdef USE_NNTP
ce1255c1
JB
2348+ case OP_FOLLOWUP:
2349+ case OP_FORWARD_TO_GROUP:
2350+
bebcbf73
AG
2351+ CHECK_MSGCOUNT;
2352+ CHECK_VISIBLE;
2353+
2354+ case OP_POST:
2355+
ce1255c1
JB
2356+ CHECK_ATTACH;
2357+ if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
2358+ mutt_strcasecmp (CURHDR->env->followup_to, "poster") ||
c02f3a2c
AG
2359+ query_quadoption (OPT_FOLLOWUPTOPOSTER,
2360+ _("Reply by mail as poster prefers?")) != MUTT_YES)
ce1255c1 2361+ {
c02f3a2c 2362+ if (Context && Context->magic == MUTT_NNTP &&
ce1255c1 2363+ !((NNTP_DATA *)Context->data)->allowed &&
c02f3a2c
AG
2364+ query_quadoption (OPT_TOMODERATED,
2365+ _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
ce1255c1
JB
2366+ break;
2367+ if (op == OP_POST)
2368+ ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
2369+ else
2370+ {
2371+ CHECK_MSGCOUNT;
c02f3a2c
AG
2372+ ci_send_message ((op == OP_FOLLOWUP ? SENDREPLY : SENDFORWARD) |
2373+ SENDNEWS, NULL, NULL, Context, tag ? NULL : CURHDR);
ce1255c1
JB
2374+ }
2375+ menu->redraw = REDRAW_FULL;
2376+ break;
2377+ }
2378+#endif
4adf126f 2379+
ce1255c1 2380 case OP_REPLY:
32845784
AG
2381 case OP_GROUP_REPLY:
2382 case OP_GROUP_CHAT_REPLY:
2383diff -udprP mutt-1.12.1.orig/doc/Muttrc mutt-1.12.1/doc/Muttrc
2384--- mutt-1.12.1.orig/doc/Muttrc 2019-06-15 19:07:32.000000000 +0300
2385+++ mutt-1.12.1/doc/Muttrc 2019-08-11 20:31:26.137940627 +0300
c02f3a2c
AG
2386@@ -275,6 +275,28 @@ attachments -I message/external-body
2387 # editing the body of an outgoing message.
bebcbf73
AG
2388 #
2389 #
2390+# set ask_follow_up=no
2391+#
2392+# Name: ask_follow_up
2393+# Type: boolean
2394+# Default: no
2395+#
2396+#
2397+# If set, Mutt will prompt you for follow-up groups before editing
2398+# the body of an outgoing message.
2399+#
2400+#
2401+# set ask_x_comment_to=no
2402+#
2403+# Name: ask_x_comment_to
2404+# Type: boolean
2405+# Default: no
2406+#
2407+#
2408+# If set, Mutt will prompt you for x-comment-to field before editing
2409+# the body of an outgoing message.
2410+#
2411+#
c02f3a2c 2412 # set assumed_charset=""
bebcbf73 2413 #
c02f3a2c 2414 # Name: assumed_charset
32845784 2415@@ -530,6 +552,17 @@ attachments -I message/external-body
c02f3a2c 2416 # desirable to unset this variable.
bebcbf73
AG
2417 #
2418 #
2419+# set catchup_newsgroup=ask-yes
2420+#
2421+# Name: catchup_newsgroup
2422+# Type: quadoption
2423+# Default: ask-yes
2424+#
2425+#
2426+# If this variable is set, Mutt will mark all articles in newsgroup
2427+# as read when you quit the newsgroup (catchup newsgroup).
2428+#
2429+#
c02f3a2c 2430 # set certificate_file="~/.mutt_certificates"
bebcbf73 2431 #
c02f3a2c 2432 # Name: certificate_file
32845784 2433@@ -1410,6 +1443,19 @@ attachments -I message/external-body
bebcbf73
AG
2434 # of the same email for you.
2435 #
2436 #
2437+# set followup_to_poster=ask-yes
2438+#
2439+# Name: followup_to_poster
2440+# Type: quadoption
2441+# Default: ask-yes
2442+#
2443+#
2444+# If this variable is set and the keyword "poster" is present in
2445+# Followup-To header, follow-up to newsgroup function is not
2446+# permitted. The message will be mailed to the submitter of the
2447+# message via mail.
2448+#
2449+#
2450 # set force_name=no
2451 #
2452 # Name: force_name
32845784 2453@@ -1560,6 +1606,27 @@ attachments -I message/external-body
bebcbf73
AG
2454 # ``Franklin'' to ``Franklin, Steve''.
2455 #
2456 #
2457+# set group_index_format="%4C %M%N %5s %-45.45f %d"
2458+#
2459+# Name: group_index_format
2460+# Type: string
2461+# Default: "%4C %M%N %5s %-45.45f %d"
2462+#
2463+#
2464+# This variable allows you to customize the newsgroup browser display to
2465+# your personal taste. This string is similar to ``index_format'', but
2466+# has its own set of printf()-like sequences:
bebcbf73
AG
2467+# %C current newsgroup number
2468+# %d description of newsgroup (becomes from server)
2469+# %f newsgroup name
2470+# %M - if newsgroup not allowed for direct post (moderated for example)
2471+# %N N if newsgroup is new, u if unsubscribed, blank otherwise
2472+# %n number of new articles in newsgroup
2473+# %s number of unread articles in newsgroup
2474+# %>X right justify the rest of the string and pad with character "X"
2475+# %|X pad to the end of the line with character "X"
2476+#
2477+#
2478 # set hdrs=yes
2479 #
2480 # Name: hdrs
32845784 2481@@ -2252,6 +2319,7 @@ attachments -I message/external-body
bebcbf73
AG
2482 # %E number of messages in current thread
2483 # %f sender (address + real name), either From: or Return-Path:
2484 # %F author name, or recipient name if the message is from you
c02f3a2c 2485+# %g newsgroup name (if compiled with NNTP support)
bebcbf73
AG
2486 # %H spam attribute(s) of this message
2487 # %i message-id of the current message
2488 # %l number of lines in the message (does not work with maildir,
32845784 2489@@ -2275,6 +2343,8 @@ attachments -I message/external-body
bebcbf73
AG
2490 # %T the appropriate character from the $to_chars string
2491 # %u user (login) name of the author
2492 # %v first name of the author, or the recipient if the message is from you
c02f3a2c
AG
2493+# %W name of organization of author (``Organization:'' field)
2494+# %x ``X-Comment-To:'' field (if present and compiled with NNTP support)
bebcbf73
AG
2495 # %X number of attachments
2496 # (please see the ``attachments'' section for possible speed effects)
2497 # %y ``X-Label:'' field, if present
32845784
AG
2498@@ -2322,6 +2392,27 @@ attachments -I message/external-body
2499 # and $post_indent_string.
bebcbf73
AG
2500 #
2501 #
2502+# set inews=""
2503+#
2504+# Name: inews
2505+# Type: path
2506+# Default: ""
2507+#
2508+#
2509+# If set, specifies the program and arguments used to deliver news posted
2510+# by Mutt. Otherwise, mutt posts article using current connection to
2511+# news server. The following printf-style sequence is understood:
c02f3a2c
AG
2512+# %a account url
2513+# %p port
2514+# %P port if specified
2515+# %s news server name
2516+# %S url schema
2517+# %u username
bebcbf73 2518+#
bebcbf73
AG
2519+#
2520+# Example: set inews="/usr/local/bin/inews -hS"
2521+#
2522+#
c02f3a2c 2523 # set ispell="/usr/bin/ispell"
bebcbf73
AG
2524 #
2525 # Name: ispell
32845784 2526@@ -2774,6 +2865,18 @@ attachments -I message/external-body
c02f3a2c 2527 # mime.types lookup.
bebcbf73
AG
2528 #
2529 #
2530+# set mime_subject=yes
2531+#
2532+# Name: mime_subject
2533+# Type: boolean
2534+# Default: yes
2535+#
2536+#
2537+# If unset, 8-bit ``subject:'' line in article header will not be
2538+# encoded according to RFC2047 to base64. This is useful when message
2539+# is Usenet article, because MIME for news is nonstandard feature.
2540+#
2541+#
2542 # set mix_entry_format="%4n %c %-16s %a"
2543 #
2544 # Name: mix_entry_format
32845784 2545@@ -2852,6 +2955,144 @@ attachments -I message/external-body
c02f3a2c 2546 # into this command.
bebcbf73
AG
2547 #
2548 #
2549+# set news_cache_dir="~/.mutt"
2550+#
2551+# Name: news_cache_dir
2552+# Type: path
2553+# Default: "~/.mutt"
2554+#
2555+#
2556+# This variable pointing to directory where Mutt will save cached news
c02f3a2c
AG
2557+# articles and headers in. If unset, articles and headers will not be
2558+# saved at all and will be reloaded from the server each time.
bebcbf73
AG
2559+#
2560+#
2561+# set news_server=""
2562+#
2563+# Name: news_server
2564+# Type: string
2565+# Default: ""
2566+#
2567+#
2568+# This variable specifies domain name or address of NNTP server. It
c02f3a2c 2569+# defaults to the news server specified in the environment variable
bebcbf73 2570+# $NNTPSERVER or contained in the file /etc/nntpserver. You can also
c02f3a2c
AG
2571+# specify username and an alternative port for each news server, ie:
2572+#
2573+# [[s]news://][username[:password]@]server[:port]
2574+#
2575+#
2576+# set newsgroups_charset="utf-8"
2577+#
2578+# Name: newsgroups_charset
2579+# Type: string
2580+# Default: "utf-8"
2581+#
bebcbf73 2582+#
c02f3a2c 2583+# Character set of newsgroups descriptions.
bebcbf73
AG
2584+#
2585+#
2586+# set newsrc="~/.newsrc"
2587+#
2588+# Name: newsrc
2589+# Type: path
2590+# Default: "~/.newsrc"
2591+#
2592+#
2593+# The file, containing info about subscribed newsgroups - names and
2594+# indexes of read articles. The following printf-style sequence
2595+# is understood:
c02f3a2c
AG
2596+# %a account url
2597+# %p port
2598+# %P port if specified
2599+# %s news server name
2600+# %S url schema
2601+# %u username
bebcbf73 2602+#
c02f3a2c
AG
2603+#
2604+# set nntp_authenticators=""
2605+#
2606+# Name: nntp_authenticators
2607+# Type: string
2608+# Default: ""
2609+#
2610+#
2611+# This is a colon-delimited list of authentication methods mutt may
2612+# attempt to use to log in to a news server, in the order mutt should
2613+# try them. Authentication methods are either ``user'' or any
2614+# SASL mechanism, e.g. ``digest-md5'', ``gssapi'' or ``cram-md5''.
2615+# This option is case-insensitive. If it's unset (the default)
2616+# mutt will try all available methods, in order from most-secure to
2617+# least-secure.
2618+#
2619+# Example:
2620+# set nntp_authenticators="digest-md5:user"
2621+#
2622+# Note: Mutt will only fall back to other authentication methods if
2623+# the previous methods are unavailable. If a method is available but
2624+# authentication fails, mutt will not connect to the IMAP server.
bebcbf73
AG
2625+#
2626+#
2627+# set nntp_context=1000
2628+#
2629+# Name: nntp_context
2630+# Type: number
2631+# Default: 1000
2632+#
2633+#
2634+# This variable defines number of articles which will be in index when
2635+# newsgroup entered. If active newsgroup have more articles than this
2636+# number, oldest articles will be ignored. Also controls how many
2637+# articles headers will be saved in cache when you quit newsgroup.
2638+#
2639+#
2640+# set nntp_load_description=yes
2641+#
2642+# Name: nntp_load_description
2643+# Type: boolean
2644+# Default: yes
2645+#
2646+#
2647+# This variable controls whether or not descriptions for each newsgroup
2648+# must be loaded when newsgroup is added to list (first time list
2649+# loading or new newsgroup adding).
2650+#
2651+#
2652+# set nntp_user=""
2653+#
2654+# Name: nntp_user
2655+# Type: string
2656+# Default: ""
2657+#
2658+#
2659+# Your login name on the NNTP server. If unset and NNTP server requires
2660+# authentification, Mutt will prompt you for your account name when you
c02f3a2c 2661+# connect to news server.
bebcbf73
AG
2662+#
2663+#
2664+# set nntp_pass=""
2665+#
2666+# Name: nntp_pass
2667+# Type: string
2668+# Default: ""
2669+#
2670+#
2671+# Your password for NNTP account.
2672+#
2673+#
2674+# set nntp_poll=60
2675+#
2676+# Name: nntp_poll
2677+# Type: number
2678+# Default: 60
2679+#
2680+#
2681+# The time in seconds until any operations on newsgroup except post new
2682+# article will cause recheck for new news. If set to 0, Mutt will
2683+# recheck newsgroup on each operation in index (stepping, read article,
2684+# etc.).
2685+#
bebcbf73
AG
2686+#
2687 # set pager="builtin"
2688 #
2689 # Name: pager
32845784
AG
2690@@ -3686,6 +3927,19 @@ attachments -I message/external-body
2691 # the section on $index_format.
bebcbf73
AG
2692 #
2693 #
2694+# set post_moderated=ask-yes
2695+#
2696+# Name: post_moderated
2697+# Type: quadoption
2698+# Default: ask-yes
2699+#
2700+#
2701+# If set to yes, Mutt will post article to newsgroup that have
c02f3a2c 2702+# not permissions to posting (e.g. moderated). Note: if news server
bebcbf73
AG
2703+# does not support posting to that newsgroup or totally read-only, that
2704+# posting will not have an effect.
2705+#
2706+#
2707 # set postpone=ask-yes
2708 #
2709 # Name: postpone
32845784 2710@@ -4567,6 +4821,41 @@ attachments -I message/external-body
c02f3a2c 2711 # Chinese characters.
bebcbf73
AG
2712 #
2713 #
2714+# set save_unsubscribed=no
2715+#
2716+# Name: save_unsubscribed
2717+# Type: boolean
2718+# Default: no
2719+#
2720+#
2721+# When set, info about unsubscribed newsgroups will be saved into
2722+# ``newsrc'' file and into cache.
2723+#
2724+#
2725+# set show_new_news=yes
2726+#
2727+# Name: show_new_news
2728+# Type: boolean
2729+# Default: yes
2730+#
2731+#
c02f3a2c
AG
2732+# If set, news server will be asked for new newsgroups on entering
2733+# the browser. Otherwise, it will be done only once for a news server.
bebcbf73
AG
2734+# Also controls whether or not number of new articles of subscribed
2735+# newsgroups will be then checked.
2736+#
2737+#
2738+# set show_only_unread=no
2739+#
2740+# Name: show_only_unread
2741+# Type: boolean
2742+# Default: no
2743+#
2744+#
2745+# If set, only subscribed newsgroups that contain unread articles
2746+# will be displayed in browser.
2747+#
2748+#
2749 # set sig_dashes=yes
2750 #
2751 # Name: sig_dashes
32845784 2752@@ -5971,3 +6260,14 @@ attachments -I message/external-body
c02f3a2c 2753 # ``tuning'' section of the manual for performance considerations.
bebcbf73
AG
2754 #
2755 #
2756+# set x_comment_to=no
2757+#
2758+# Name: x_comment_to
2759+# Type: boolean
2760+# Default: no
2761+#
2762+#
2763+# If set, Mutt will add ``X-Comment-To:'' field (that contains full
2764+# name of original article author) to article that followuped to newsgroup.
2765+#
2766+#
32845784
AG
2767diff -udprP mutt-1.12.1.orig/doc/manual.xml.head mutt-1.12.1/doc/manual.xml.head
2768--- mutt-1.12.1.orig/doc/manual.xml.head 2019-06-14 04:29:29.000000000 +0300
2769+++ mutt-1.12.1/doc/manual.xml.head 2019-08-11 20:31:26.138940612 +0300
2770@@ -1948,6 +1948,26 @@ to update status flags for your certific
2771
2772 </sect1>
2773
2774+<sect1 id="nntp">
2775+<title>Reading news via NNTP</title>
2776+
2777+<para>
2778+If compiled with <emphasis>--enable-nntp</emphasis> option, Mutt can
2779+read news from news server via NNTP. You can open a newsgroup with
2780+function ``change-newsgroup'' (default: ``i''). Default news server
2781+can be obtained from <literal>$NNTPSERVER</literal> environment
2782+variable or from <literal>/etc/nntpserver</literal> file. Like other
2783+news readers, info about subscribed newsgroups is saved in file by
2784+<link linkend="newsrc">$newsrc</link> variable. The variable <link
2785+linkend="news-cache-dir">$news_cache_dir</link> can be used to point
2786+to a directory. Mutt will create a hierarchy of subdirectories named
2787+like the account and newsgroup the cache is for. Also the hierarchy
2788+is used to store header cache if Mutt was compiled with <link
2789+linkend="header-caching">header cache</link> support.
2790+</para>
2791+
2792+</sect1>
2793+
2794 </chapter>
2795
2796 <chapter id="configuration">
2797diff -udprP mutt-1.12.1.orig/doc/mutt.man mutt-1.12.1/doc/mutt.man
2798--- mutt-1.12.1.orig/doc/mutt.man 2019-05-25 19:22:39.000000000 +0300
2799+++ mutt-1.12.1/doc/mutt.man 2019-08-11 20:31:26.138940612 +0300
2800@@ -23,8 +23,8 @@ mutt \- The Mutt Mail User Agent
2801 .SH SYNOPSIS
2802 .PP
2803 .B mutt
2804-[\-nRyzZ]
2805-[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-m \fItype\fP] [\-f \fIfile\fP]
2806+[\-GnRyzZ]
2807+[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-g \fIserver\fP] [\-m \fItype\fP] [\-f \fIfile\fP]
2808 .PP
2809 .B mutt
2810 [\-Enx]
2811@@ -104,6 +104,10 @@ files.
2812 Specify which mailbox to load.
2813 .IP "-F \fImuttrc\fP"
2814 Specify an initialization file to read instead of ~/.muttrc
2815+.IP "-g \fIserver\fP"
2816+Start Mutt with a listing of subscribed newsgroups at specified news server.
2817+.IP "-G"
2818+Start Mutt with a listing of subscribed newsgroups.
2819 .IP "-h"
2820 Display help.
2821 .IP "-H \fIdraft\fP"
2822diff -udprP mutt-1.12.1.orig/functions.h mutt-1.12.1/functions.h
2823--- mutt-1.12.1.orig/functions.h 2019-05-25 19:22:39.000000000 +0300
2824+++ mutt-1.12.1/functions.h 2019-08-11 20:31:26.138940612 +0300
2825@@ -90,6 +90,10 @@ const struct binding_t OpMain[] = { /* m
2826 { "break-thread", OP_MAIN_BREAK_THREAD, "#" },
2827 { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" },
2828 { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
2829+#ifdef USE_NNTP
2830+ { "change-newsgroup", OP_MAIN_CHANGE_GROUP, "i" },
2831+ { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY, "\033i" },
2832+#endif
2833 { "next-unread-mailbox", OP_MAIN_NEXT_UNREAD_MAILBOX, NULL },
2834 { "collapse-thread", OP_MAIN_COLLAPSE_THREAD, "\033v" },
2835 { "collapse-all", OP_MAIN_COLLAPSE_ALL, "\033V" },
2836@@ -105,7 +109,15 @@ const struct binding_t OpMain[] = { /* m
2837 { "edit-label", OP_EDIT_LABEL, "Y" },
2838 { "edit-type", OP_EDIT_TYPE, "\005" },
2839 { "forward-message", OP_FORWARD_MESSAGE, "f" },
2840- { "flag-message", OP_FLAG_MESSAGE, "F" },
2841+#ifdef USE_NNTP
2842+ { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" },
2843+ { "followup-message", OP_FOLLOWUP, "F" },
2844+ { "get-children", OP_GET_CHILDREN, NULL },
2845+ { "get-message", OP_GET_MESSAGE, "\007" },
2846+ { "get-parent", OP_GET_PARENT, "\033G" },
2847+ { "reconstruct-thread", OP_RECONSTRUCT_THREAD, NULL },
2848+#endif
2849+ { "flag-message", OP_FLAG_MESSAGE, "\033f" },
2850 { "group-chat-reply", OP_GROUP_CHAT_REPLY, NULL },
2851 { "group-reply", OP_GROUP_REPLY, "g" },
2852 #ifdef USE_POP
2853@@ -134,6 +146,9 @@ const struct binding_t OpMain[] = { /* m
2854 { "sort-mailbox", OP_SORT, "o" },
2855 { "sort-reverse", OP_SORT_REVERSE, "O" },
2856 { "print-message", OP_PRINT, "p" },
2857+#ifdef USE_NNTP
2858+ { "post-message", OP_POST, "P" },
2859+#endif
2860 { "previous-thread", OP_MAIN_PREV_THREAD, "\020" },
2861 { "previous-subthread", OP_MAIN_PREV_SUBTHREAD, "\033p" },
2862 { "recall-message", OP_RECALL_MESSAGE, "R" },
2863@@ -153,6 +168,10 @@ const struct binding_t OpMain[] = { /* m
2864 { "show-version", OP_VERSION, "V" },
2865 { "set-flag", OP_MAIN_SET_FLAG, "w" },
2866 { "clear-flag", OP_MAIN_CLEAR_FLAG, "W" },
2867+ { "toggle-read", OP_TOGGLE_READ, "X" },
2868+#ifdef USE_NNTP
2869+ { "catchup", OP_CATCHUP, "y" },
2870+#endif
2871 { "display-message", OP_DISPLAY_MESSAGE, MUTT_ENTER_S },
2872 { "mark-message", OP_MARK_MSG, "~" },
2873 { "buffy-list", OP_BUFFY_LIST, "." },
2874@@ -165,7 +184,7 @@ const struct binding_t OpMain[] = { /* m
2875 { "previous-new-then-unread", OP_MAIN_PREV_NEW_THEN_UNREAD, "\033\t" },
2876 { "next-unread", OP_MAIN_NEXT_UNREAD, NULL },
2877 { "previous-unread", OP_MAIN_PREV_UNREAD, NULL },
2878- { "parent-message", OP_MAIN_PARENT_MESSAGE, "P" },
2879+ { "parent-message", OP_MAIN_PARENT_MESSAGE, NULL },
2880 { "root-message", OP_MAIN_ROOT_MESSAGE, NULL },
2881
2882
2883@@ -196,6 +215,10 @@ const struct binding_t OpPager[] = { /*
2884 { "bounce-message", OP_BOUNCE_MESSAGE, "b" },
2885 { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" },
2886 { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
2887+#ifdef USE_NNTP
2888+ { "change-newsgroup", OP_MAIN_CHANGE_GROUP, "i" },
2889+ { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY, "\033i" },
2890+#endif
2891 { "next-unread-mailbox", OP_MAIN_NEXT_UNREAD_MAILBOX, NULL },
2892 { "compose-to-sender", OP_COMPOSE_TO_SENDER, NULL},
2893 { "copy-message", OP_COPY_MESSAGE, "C" },
2894@@ -208,8 +231,12 @@ const struct binding_t OpPager[] = { /*
2895 { "edit", OP_EDIT_MESSAGE, "e" },
2896 { "edit-label", OP_EDIT_LABEL, "Y" },
2897 { "edit-type", OP_EDIT_TYPE, "\005" },
2898+#ifdef USE_NNTP
2899+ { "followup-message", OP_FOLLOWUP, "F" },
2900+ { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" },
2901+#endif
2902 { "forward-message", OP_FORWARD_MESSAGE, "f" },
2903- { "flag-message", OP_FLAG_MESSAGE, "F" },
2904+ { "flag-message", OP_FLAG_MESSAGE, "\033f" },
2905 { "group-chat-reply", OP_GROUP_CHAT_REPLY, NULL },
2906 { "group-reply", OP_GROUP_REPLY, "g" },
2907 #ifdef USE_IMAP
2908@@ -233,6 +260,9 @@ const struct binding_t OpPager[] = { /*
2909 { "sort-mailbox", OP_SORT, "o" },
2910 { "sort-reverse", OP_SORT_REVERSE, "O" },
2911 { "print-message", OP_PRINT, "p" },
2912+#ifdef USE_NNTP
2913+ { "post-message", OP_POST, "P" },
2914+#endif
2915 { "previous-thread", OP_MAIN_PREV_THREAD, "\020" },
2916 { "previous-subthread",OP_MAIN_PREV_SUBTHREAD, "\033p" },
2917 { "purge-message", OP_PURGE_MESSAGE, NULL },
2918@@ -282,7 +312,7 @@ const struct binding_t OpPager[] = { /*
2919 { "half-down", OP_HALF_DOWN, NULL },
2920 { "previous-line", OP_PREV_LINE, NULL },
2921 { "bottom", OP_PAGER_BOTTOM, NULL },
2922- { "parent-message", OP_MAIN_PARENT_MESSAGE, "P" },
2923+ { "parent-message", OP_MAIN_PARENT_MESSAGE, NULL },
2924 { "root-message", OP_MAIN_ROOT_MESSAGE, NULL },
2925
2926
2927@@ -317,6 +347,10 @@ const struct binding_t OpAttach[] = { /*
2928 { "display-toggle-weed", OP_DISPLAY_HEADERS, "h" },
2929 { "compose-to-sender", OP_COMPOSE_TO_SENDER, NULL},
2930 { "edit-type", OP_EDIT_TYPE, "\005" },
2931+#ifdef USE_NNTP
2932+ { "followup-message", OP_FOLLOWUP, "F" },
2933+ { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" },
2934+#endif
2935 { "print-entry", OP_PRINT, "p" },
2936 { "save-entry", OP_SAVE, "s" },
2937 { "pipe-entry", OP_PIPE, "|" },
2938@@ -343,6 +377,7 @@ const struct binding_t OpAttach[] = { /*
2939 const struct binding_t OpCompose[] = { /* map: compose */
2940 { "attach-file", OP_COMPOSE_ATTACH_FILE, "a" },
2941 { "attach-message", OP_COMPOSE_ATTACH_MESSAGE, "A" },
2942+ { "attach-news-message",OP_COMPOSE_ATTACH_NEWS_MESSAGE,"\033a" },
2943 { "edit-bcc", OP_COMPOSE_EDIT_BCC, "b" },
2944 { "edit-cc", OP_COMPOSE_EDIT_CC, "c" },
2945 { "copy-file", OP_SAVE, "C" },
2946@@ -362,6 +397,11 @@ const struct binding_t OpCompose[] = { /
2947 { "print-entry", OP_PRINT, "l" },
2948 { "edit-mime", OP_COMPOSE_EDIT_MIME, "m" },
2949 { "new-mime", OP_COMPOSE_NEW_MIME, "n" },
2950+#ifdef USE_NNTP
2951+ { "edit-newsgroups", OP_COMPOSE_EDIT_NEWSGROUPS, "N" },
2952+ { "edit-followup-to", OP_COMPOSE_EDIT_FOLLOWUP_TO, "o" },
2953+ { "edit-x-comment-to",OP_COMPOSE_EDIT_X_COMMENT_TO, "x" },
2954+#endif
2955 { "postpone-message", OP_COMPOSE_POSTPONE_MESSAGE, "P" },
2956 { "edit-reply-to", OP_COMPOSE_EDIT_REPLY_TO, "r" },
2957 { "rename-attachment",OP_COMPOSE_RENAME_ATTACHMENT, "\017" },
2958@@ -415,14 +455,25 @@ const struct binding_t OpBrowser[] = { /
2959 { "select-new", OP_BROWSER_NEW_FILE, "N" },
2960 { "check-new", OP_CHECK_NEW, NULL },
2961 { "toggle-mailboxes", OP_TOGGLE_MAILBOXES, "\t" },
2962+#ifdef USE_NNTP
2963+ { "reload-active", OP_LOAD_ACTIVE, "g" },
2964+ { "subscribe-pattern", OP_SUBSCRIBE_PATTERN, "S" },
2965+ { "unsubscribe-pattern", OP_UNSUBSCRIBE_PATTERN, "U" },
2966+ { "catchup", OP_CATCHUP, "y" },
2967+ { "uncatchup", OP_UNCATCHUP, "Y" },
2968+#endif
2969 { "view-file", OP_BROWSER_VIEW_FILE, " " },
2970 { "buffy-list", OP_BUFFY_LIST, "." },
2971 #ifdef USE_IMAP
2972 { "create-mailbox", OP_CREATE_MAILBOX, "C" },
2973 { "delete-mailbox", OP_DELETE_MAILBOX, "d" },
2974 { "rename-mailbox", OP_RENAME_MAILBOX, "r" },
2975+#endif
2976+#if defined USE_IMAP || defined USE_NNTP
2977 { "subscribe", OP_BROWSER_SUBSCRIBE, "s" },
2978 { "unsubscribe", OP_BROWSER_UNSUBSCRIBE, "u" },
2979+#endif
2980+#ifdef USE_IMAP
2981 { "toggle-subscribed", OP_BROWSER_TOGGLE_LSUB, "T" },
2982 #endif
2983 { NULL, 0, NULL }
2984diff -udprP mutt-1.12.1.orig/globals.h mutt-1.12.1/globals.h
2985--- mutt-1.12.1.orig/globals.h 2019-05-25 19:22:39.000000000 +0300
2986+++ mutt-1.12.1/globals.h 2019-08-11 20:31:26.139940597 +0300
2987@@ -73,7 +73,7 @@ WHERE char *Inbox;
2988 WHERE char *Ispell;
2989 WHERE char *MailcapPath;
2990 WHERE char *Maildir;
2991-#if defined(USE_IMAP) || defined(USE_POP)
2992+#if defined(USE_IMAP) || defined(USE_POP) || defined(USE_NNTP)
2993 WHERE char *MessageCachedir;
2994 #endif
2995 #if USE_HCACHE
2996@@ -101,6 +101,17 @@ WHERE char *MixEntryFormat;
2997 #endif
2998
2999 WHERE char *Muttrc INITVAL (NULL);
3000+#ifdef USE_NNTP
3001+WHERE char *GroupFormat;
3002+WHERE char *Inews;
3003+WHERE char *NewsCacheDir;
3004+WHERE char *NewsServer;
3005+WHERE char *NewsgroupsCharset;
3006+WHERE char *NewsRc;
3007+WHERE char *NntpAuthenticators;
3008+WHERE char *NntpUser;
3009+WHERE char *NntpPass;
3010+#endif
3011 WHERE char *Outbox;
3012 WHERE char *Pager;
3013 WHERE char *PagerFmt;
3014@@ -213,6 +224,11 @@ extern unsigned char QuadOptions[];
3015
3016 WHERE unsigned short Counter INITVAL (0);
3017
3018+#ifdef USE_NNTP
3019+WHERE short NewsPollTimeout;
3020+WHERE short NntpContext;
3021+#endif
3022+
3023 WHERE short ConnectTimeout;
3024 WHERE short ErrorHistSize;
3025 WHERE short HistSize;
3026diff -udprP mutt-1.12.1.orig/hcache.c mutt-1.12.1/hcache.c
3027--- mutt-1.12.1.orig/hcache.c 2019-05-25 19:22:39.000000000 +0300
3028+++ mutt-1.12.1/hcache.c 2019-08-11 20:31:26.139940597 +0300
3029@@ -543,6 +543,12 @@ dump_envelope(ENVELOPE * e, unsigned cha
3030 d = dump_list(e->in_reply_to, d, off, 0);
3031 d = dump_list(e->userhdrs, d, off, convert);
3032
3033+#ifdef USE_NNTP
3034+ d = dump_char(e->xref, d, off, 0);
3035+ d = dump_char(e->followup_to, d, off, 0);
3036+ d = dump_char(e->x_comment_to, d, off, convert);
3037+#endif
3038+
3039 return d;
3040 }
3041
3042@@ -582,6 +588,12 @@ restore_envelope(ENVELOPE * e, const uns
3043 restore_list(&e->references, d, off, 0);
3044 restore_list(&e->in_reply_to, d, off, 0);
3045 restore_list(&e->userhdrs, d, off, convert);
3046+
3047+#ifdef USE_NNTP
3048+ restore_char(&e->xref, d, off, 0);
3049+ restore_char(&e->followup_to, d, off, 0);
3050+ restore_char(&e->x_comment_to, d, off, convert);
3051+#endif
3052 }
3053
3054 static int
3055diff -udprP mutt-1.12.1.orig/hdrline.c mutt-1.12.1/hdrline.c
3056--- mutt-1.12.1.orig/hdrline.c 2019-05-25 19:22:39.000000000 +0300
3057+++ mutt-1.12.1/hdrline.c 2019-08-11 20:31:26.139940597 +0300
3058@@ -227,6 +227,7 @@ static char *apply_subject_mods (ENVELOP
3059 * %E = number of messages in current thread
3060 * %f = entire from line
3061 * %F = like %n, unless from self
3062+ * %g = newsgroup name (if compiled with NNTP support)
3063 * %i = message-id
3064 * %l = number of lines in the message
3065 * %L = like %F, except `lists' are displayed first
3066@@ -243,6 +244,8 @@ static char *apply_subject_mods (ENVELOP
3067 * %T = $to_chars
3068 * %u = user (login) name of author
3069 * %v = first name of author, unless from self
3070+ * %W = where user is (organization)
3071+ * %x = `x-comment-to:' field (if present and compiled with NNTP support)
3072 * %X = number of MIME attachments
3073 * %y = `x-label:' field (if present)
3074 * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
3075@@ -476,6 +479,12 @@ hdr_format_str (char *dest,
3076
3077 break;
3078
3079+#ifdef USE_NNTP
3080+ case 'g':
3081+ mutt_format_s (dest, destlen, prefix, hdr->env->newsgroups ? hdr->env->newsgroups : "");
3082+ break;
3083+#endif
3084+
3085 case 'i':
3086 mutt_format_s (dest, destlen, prefix, hdr->env->message_id ? hdr->env->message_id : "<no.id>");
3087 break;
3088@@ -680,6 +689,22 @@ hdr_format_str (char *dest,
3089 mutt_format_s (dest, destlen, prefix, buf2);
3090 break;
3091
3092+ case 'W':
3093+ if (!optional)
3094+ mutt_format_s (dest, destlen, prefix, hdr->env->organization ? hdr->env->organization : "");
3095+ else if (!hdr->env->organization)
3096+ optional = 0;
3097+ break;
3098+
3099+#ifdef USE_NNTP
3100+ case 'x':
3101+ if (!optional)
3102+ mutt_format_s (dest, destlen, prefix, hdr->env->x_comment_to ? hdr->env->x_comment_to : "");
3103+ else if (!hdr->env->x_comment_to)
3104+ optional = 0;
3105+ break;
3106+#endif
3107+
3108 case 'Z':
3109
3110 ch = ' ';
3111diff -udprP mutt-1.12.1.orig/headers.c mutt-1.12.1/headers.c
3112--- mutt-1.12.1.orig/headers.c 2019-05-25 19:22:39.000000000 +0300
3113+++ mutt-1.12.1/headers.c 2019-08-11 20:31:26.139940597 +0300
3114@@ -115,6 +115,9 @@ void mutt_edit_headers (const char *edit
3115 $edit_headers set, we remove References: as they're likely invalid;
3116 we can simply compare strings as we don't generate References for
3117 multiple Message-Ids in IRT anyways */
3118+#ifdef USE_NNTP
3119+ if (!option (OPTNEWSSEND))
3120+#endif
3121 if (msg->env->in_reply_to &&
3122 (!n->in_reply_to || mutt_strcmp (n->in_reply_to->data,
3123 msg->env->in_reply_to->data) != 0))
3124diff -udprP mutt-1.12.1.orig/init.c mutt-1.12.1/init.c
3125--- mutt-1.12.1.orig/init.c 2019-05-25 19:22:39.000000000 +0300
3126+++ mutt-1.12.1/init.c 2019-08-11 20:31:26.139940597 +0300
3127@@ -3517,6 +3517,28 @@ void mutt_init (int skip_sys_rc, LIST *c
3128 Fqdn = safe_strdup(utsname.nodename);
3129
3130
3131+#ifdef USE_NNTP
3132+ {
3133+ FILE *f;
3134+ char *i;
3135+
3136+ if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
3137+ {
3138+ buffer[0] = '\0';
3139+ fgets (buffer, sizeof (buffer), f);
3140+ p = buffer;
3141+ SKIPWS (p);
3142+ i = p;
3143+ while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
3144+ *i = '\0';
3145+ NewsServer = safe_strdup (p);
3146+ fclose (f);
3147+ }
3148+ }
3149+ if ((p = getenv ("NNTPSERVER")))
3150+ NewsServer = safe_strdup (p);
3151+#endif
3152+
3153 if ((p = getenv ("MAIL")))
3154 Spoolfile = safe_strdup (p);
3155 else if ((p = getenv ("MAILDIR")))
3156diff -udprP mutt-1.12.1.orig/init.h mutt-1.12.1/init.h
3157--- mutt-1.12.1.orig/init.h 2019-06-15 18:57:01.000000000 +0300
3158+++ mutt-1.12.1/init.h 2019-08-11 20:31:26.140940582 +0300
3159@@ -202,6 +202,20 @@ struct option_t MuttVars[] = {
3160 ** If \fIset\fP, Mutt will prompt you for carbon-copy (Cc) recipients before
3161 ** editing the body of an outgoing message.
3162 */
3163+#ifdef USE_NNTP
3164+ { "ask_follow_up", DT_BOOL, R_NONE, OPTASKFOLLOWUP, 0 },
3165+ /*
3166+ ** .pp
3167+ ** If set, Mutt will prompt you for follow-up groups before editing
3168+ ** the body of an outgoing message.
3169+ */
3170+ { "ask_x_comment_to", DT_BOOL, R_NONE, OPTASKXCOMMENTTO, 0 },
3171+ /*
3172+ ** .pp
3173+ ** If set, Mutt will prompt you for x-comment-to field before editing
3174+ ** the body of an outgoing message.
3175+ */
3176+#endif
3177 { "assumed_charset", DT_STR, R_NONE, UL &AssumedCharset, UL 0},
3178 /*
3179 ** .pp
3180@@ -383,6 +397,14 @@ struct option_t MuttVars[] = {
3181 ** doesn't make intuitive sense. In those cases, it may be
3182 ** desirable to \fIunset\fP this variable.
3183 */
3184+#ifdef USE_NNTP
3185+ { "catchup_newsgroup", DT_QUAD, R_NONE, OPT_CATCHUP, MUTT_ASKYES },
3186+ /*
3187+ ** .pp
3188+ ** If this variable is \fIset\fP, Mutt will mark all articles in newsgroup
3189+ ** as read when you quit the newsgroup (catchup newsgroup).
3190+ */
3191+#endif
3192 #if defined(USE_SSL)
3193 { "certificate_file", DT_PATH, R_NONE, UL &SslCertFile, UL "~/.mutt_certificates" },
3194 /*
3195@@ -1018,6 +1040,16 @@ struct option_t MuttVars[] = {
3196 ** sent to both the list and your address, resulting in two copies
3197 ** of the same email for you.
3198 */
3199+#ifdef USE_NNTP
3200+ { "followup_to_poster", DT_QUAD, R_NONE, OPT_FOLLOWUPTOPOSTER, MUTT_ASKYES },
3201+ /*
3202+ ** .pp
3203+ ** If this variable is \fIset\fP and the keyword "poster" is present in
3204+ ** \fIFollowup-To\fP header, follow-up to newsgroup function is not
3205+ ** permitted. The message will be mailed to the submitter of the
3206+ ** message via mail.
3207+ */
3208+#endif
3209 { "force_name", DT_BOOL, R_NONE, OPTFORCENAME, 0 },
3210 /*
3211 ** .pp
3212@@ -1125,6 +1157,26 @@ struct option_t MuttVars[] = {
3213 ** a regular expression that will match the whole name so mutt will expand
3214 ** ``Franklin'' to ``Franklin, Steve''.
3215 */
3216+#ifdef USE_NNTP
8d013520 3217+ { "group_index_format", DT_STR, R_BOTH, {.p=&GroupFormat}, {.p="%4C %M%N %5s %-45.45f %d"} },
32845784
AG
3218+ /*
3219+ ** .pp
3220+ ** This variable allows you to customize the newsgroup browser display to
3221+ ** your personal taste. This string is similar to ``$index_format'', but
3222+ ** has its own set of printf()-like sequences:
3223+ ** .dl
3224+ ** .dt %C .dd current newsgroup number
3225+ ** .dt %d .dd description of newsgroup (becomes from server)
3226+ ** .dt %f .dd newsgroup name
3227+ ** .dt %M .dd - if newsgroup not allowed for direct post (moderated for example)
3228+ ** .dt %N .dd N if newsgroup is new, u if unsubscribed, blank otherwise
3229+ ** .dt %n .dd number of new articles in newsgroup
3230+ ** .dt %s .dd number of unread articles in newsgroup
3231+ ** .dt %>X .dd right justify the rest of the string and pad with character "X"
3232+ ** .dt %|X .dd pad to the end of the line with character "X"
3233+ ** .de
3234+ */
3235+#endif
3236 { "hdr_format", DT_SYN, R_NONE, UL "index_format", 0 },
3237 /*
3238 */
3239@@ -1597,6 +1649,7 @@ struct option_t MuttVars[] = {
3240 ** .dt %E .dd number of messages in current thread
3241 ** .dt %f .dd sender (address + real name), either From: or Return-Path:
3242 ** .dt %F .dd author name, or recipient name if the message is from you
3243+ ** .dt %g .dd newsgroup name (if compiled with NNTP support)
3244 ** .dt %H .dd spam attribute(s) of this message
3245 ** .dt %i .dd message-id of the current message
3246 ** .dt %l .dd number of lines in the message (does not work with maildir,
3247@@ -1620,6 +1673,8 @@ struct option_t MuttVars[] = {
3248 ** .dt %T .dd the appropriate character from the $$to_chars string
3249 ** .dt %u .dd user (login) name of the author
3250 ** .dt %v .dd first name of the author, or the recipient if the message is from you
3251+ ** .dt %W .dd name of organization of author (``Organization:'' field)
3252+ ** .dt %x .dd ``X-Comment-To:'' field (if present and compiled with NNTP support)
3253 ** .dt %X .dd number of attachments
3254 ** (please see the ``$attachments'' section for possible speed effects)
3255 ** .dt %y .dd ``X-Label:'' field, if present
3256@@ -1666,6 +1721,25 @@ struct option_t MuttVars[] = {
3257 ** $$forward_format, $$indent_string, $$message_format, $$pager_format,
3258 ** and $$post_indent_string.
3259 */
3260+#ifdef USE_NNTP
8d013520 3261+ { "inews", DT_PATH, R_NONE, {.p={&Inews}, {.p=""} },
32845784
AG
3262+ /*
3263+ ** .pp
3264+ ** If set, specifies the program and arguments used to deliver news posted
3265+ ** by Mutt. Otherwise, mutt posts article using current connection to
3266+ ** news server. The following printf-style sequence is understood:
3267+ ** .dl
3268+ ** .dt %a .dd account url
3269+ ** .dt %p .dd port
3270+ ** .dt %P .dd port if specified
3271+ ** .dt %s .dd news server name
3272+ ** .dt %S .dd url schema
3273+ ** .dt %u .dd username
3274+ ** .de
3275+ ** .pp
3276+ ** Example: set inews="/usr/local/bin/inews -hS"
3277+ */
3278+#endif
3279 { "ispell", DT_PATH, R_NONE, UL &Ispell, UL ISPELL },
3280 /*
3281 ** .pp
3282@@ -1958,6 +2032,15 @@ struct option_t MuttVars[] = {
3283 ** When \fIset\fP, the $$mime_type_query_command will be run before the
3284 ** mime.types lookup.
3285 */
3286+#ifdef USE_NNTP
3287+ { "mime_subject", DT_BOOL, R_NONE, OPTMIMESUBJECT, 1 },
3288+ /*
3289+ ** .pp
3290+ ** If \fIunset\fP, 8-bit ``subject:'' line in article header will not be
3291+ ** encoded according to RFC2047 to base64. This is useful when message
3292+ ** is Usenet article, because MIME for news is nonstandard feature.
3293+ */
3294+#endif
3295 #ifdef MIXMASTER
3296 { "mix_entry_format", DT_STR, R_NONE, UL &MixEntryFormat, UL "%4n %c %-16s %a" },
3297 /*
3298@@ -2012,6 +2095,106 @@ struct option_t MuttVars[] = {
3299 ** See the $$status_format documentation for the values that can be formatted
3300 ** into this command.
3301 */
3302+#ifdef USE_NNTP
8d013520 3303+ { "news_cache_dir", DT_PATH, R_NONE, {.p=&NewsCacheDir}, {.p="~/.mutt"} },
32845784
AG
3304+ /*
3305+ ** .pp
3306+ ** This variable pointing to directory where Mutt will save cached news
3307+ ** articles and headers in. If \fIunset\fP, articles and headers will not be
3308+ ** saved at all and will be reloaded from the server each time.
3309+ */
8d013520 3310+ { "news_server", DT_STR, R_NONE, {.p=&NewsServer}, {.l=0} },
32845784
AG
3311+ /*
3312+ ** .pp
3313+ ** This variable specifies domain name or address of NNTP server. It
3314+ ** defaults to the news server specified in the environment variable
3315+ ** $$$NNTPSERVER or contained in the file /etc/nntpserver. You can also
3316+ ** specify username and an alternative port for each news server, ie:
3317+ ** .pp
3318+ ** [[s]news://][username[:password]@]server[:port]
3319+ */
8d013520 3320+ { "newsgroups_charset", DT_STR, R_NONE, &{.p=NewsgroupsCharset}, "{.p=utf-8"} },
32845784
AG
3321+ /*
3322+ ** .pp
3323+ ** Character set of newsgroups descriptions.
3324+ */
8d013520 3325+ { "newsrc", DT_PATH, R_NONE, &{.p=NewsRc}, "{.p=~/.newsrc"} },
32845784
AG
3326+ /*
3327+ ** .pp
3328+ ** The file, containing info about subscribed newsgroups - names and
3329+ ** indexes of read articles. The following printf-style sequence
3330+ ** is understood:
3331+ ** .dl
3332+ ** .dt %a .dd account url
3333+ ** .dt %p .dd port
3334+ ** .dt %P .dd port if specified
3335+ ** .dt %s .dd news server name
3336+ ** .dt %S .dd url schema
3337+ ** .dt %u .dd username
3338+ ** .de
3339+ */
8d013520 3340+ { "nntp_authenticators", DT_STR, R_NONE, &{.p=NntpAuthenticators}, {.l=0} },
32845784
AG
3341+ /*
3342+ ** .pp
3343+ ** This is a colon-delimited list of authentication methods mutt may
3344+ ** attempt to use to log in to a news server, in the order mutt should
3345+ ** try them. Authentication methods are either ``user'' or any
3346+ ** SASL mechanism, e.g. ``digest-md5'', ``gssapi'' or ``cram-md5''.
3347+ ** This option is case-insensitive. If it's \fIunset\fP (the default)
3348+ ** mutt will try all available methods, in order from most-secure to
3349+ ** least-secure.
3350+ ** .pp
3351+ ** Example:
3352+ ** .ts
3353+ ** set nntp_authenticators="digest-md5:user"
3354+ ** .te
3355+ ** .pp
3356+ ** \fBNote:\fP Mutt will only fall back to other authentication methods if
3357+ ** the previous methods are unavailable. If a method is available but
3358+ ** authentication fails, mutt will not connect to the IMAP server.
3359+ */
8d013520 3360+ { "nntp_context", DT_NUM, R_NONE, &{.p=NntpContext}, {.l=1000} },
32845784
AG
3361+ /*
3362+ ** .pp
3363+ ** This variable defines number of articles which will be in index when
3364+ ** newsgroup entered. If active newsgroup have more articles than this
3365+ ** number, oldest articles will be ignored. Also controls how many
3366+ ** articles headers will be saved in cache when you quit newsgroup.
3367+ */
3368+ { "nntp_listgroup", DT_BOOL, R_NONE, OPTLISTGROUP, 1 },
3369+ /*
3370+ ** .pp
3371+ ** This variable controls whether or not existence of each article is
3372+ ** checked when newsgroup is entered.
3373+ */
3374+ { "nntp_load_description", DT_BOOL, R_NONE, OPTLOADDESC, 1 },
3375+ /*
3376+ ** .pp
3377+ ** This variable controls whether or not descriptions for each newsgroup
3378+ ** must be loaded when newsgroup is added to list (first time list
3379+ ** loading or new newsgroup adding).
3380+ */
8d013520 3381+ { "nntp_user", DT_STR, R_NONE, {.p=&NntpUser}, {.p=""} },
32845784
AG
3382+ /*
3383+ ** .pp
3384+ ** Your login name on the NNTP server. If \fIunset\fP and NNTP server requires
3385+ ** authentification, Mutt will prompt you for your account name when you
3386+ ** connect to news server.
3387+ */
8d013520 3388+ { "nntp_pass", DT_STR, R_NONE, &{.p=NntpPass}, {.p=""} },
32845784
AG
3389+ /*
3390+ ** .pp
3391+ ** Your password for NNTP account.
3392+ */
8d013520 3393+ { "nntp_poll", DT_NUM, R_NONE, &{.p=NewsPollTimeout}, {.l=60} },
32845784
AG
3394+ /*
3395+ ** .pp
3396+ ** The time in seconds until any operations on newsgroup except post new
3397+ ** article will cause recheck for new news. If set to 0, Mutt will
3398+ ** recheck newsgroup on each operation in index (stepping, read article,
3399+ ** etc.).
3400+ */
3401+#endif
3402 { "pager", DT_PATH, R_NONE, UL &Pager, UL "builtin" },
3403 /*
3404 ** .pp
3405@@ -2606,6 +2789,16 @@ struct option_t MuttVars[] = {
3406 { "post_indent_str", DT_SYN, R_NONE, UL "post_indent_string", 0 },
3407 /*
3408 */
3409+#ifdef USE_NNTP
3410+ { "post_moderated", DT_QUAD, R_NONE, OPT_TOMODERATED, MUTT_ASKYES },
3411+ /*
3412+ ** .pp
3413+ ** If set to \fIyes\fP, Mutt will post article to newsgroup that have
3414+ ** not permissions to posting (e.g. moderated). \fBNote:\fP if news server
3415+ ** does not support posting to that newsgroup or totally read-only, that
3416+ ** posting will not have an effect.
3417+ */
3418+#endif
3419 { "postpone", DT_QUAD, R_NONE, OPT_POSTPONE, MUTT_ASKYES },
3420 /*
3421 ** .pp
3422@@ -3093,6 +3286,28 @@ struct option_t MuttVars[] = {
3423 ** Command to use when spawning a subshell. By default, the user's login
3424 ** shell from \fC/etc/passwd\fP is used.
3425 */
3426+#ifdef USE_NNTP
3427+ { "save_unsubscribed", DT_BOOL, R_NONE, OPTSAVEUNSUB, 0 },
3428+ /*
3429+ ** .pp
3430+ ** When \fIset\fP, info about unsubscribed newsgroups will be saved into
3431+ ** ``newsrc'' file and into cache.
3432+ */
3433+ { "show_new_news", DT_BOOL, R_NONE, OPTSHOWNEWNEWS, 1 },
3434+ /*
3435+ ** .pp
3436+ ** If \fIset\fP, news server will be asked for new newsgroups on entering
3437+ ** the browser. Otherwise, it will be done only once for a news server.
3438+ ** Also controls whether or not number of new articles of subscribed
3439+ ** newsgroups will be then checked.
3440+ */
3441+ { "show_only_unread", DT_BOOL, R_NONE, OPTSHOWONLYUNREAD, 0 },
3442+ /*
3443+ ** .pp
3444+ ** If \fIset\fP, only subscribed newsgroups that contain unread articles
3445+ ** will be displayed in browser.
3446+ */
3447+#endif
3448 #ifdef USE_SIDEBAR
3449 { "sidebar_delim_chars", DT_STR, R_SIDEBAR, UL &SidebarDelimChars, UL "/." },
3450 /*
3451@@ -4234,6 +4449,14 @@ struct option_t MuttVars[] = {
3452 {"xterm_set_titles", DT_SYN, R_NONE, UL "ts_enabled", 0 },
3453 /*
3454 */
3455+#ifdef USE_NNTP
3456+ { "x_comment_to", DT_BOOL, R_NONE, OPTXCOMMENTTO, 0 },
3457+ /*
3458+ ** .pp
3459+ ** If \fIset\fP, Mutt will add ``X-Comment-To:'' field (that contains full
3460+ ** name of original article author) to article that followuped to newsgroup.
3461+ */
3462+#endif
3463 /*--*/
3464 { NULL, 0, 0, 0, 0 }
3465 };
3466diff -udprP mutt-1.12.1.orig/keymap.c mutt-1.12.1/keymap.c
3467--- mutt-1.12.1.orig/keymap.c 2019-05-25 19:22:39.000000000 +0300
3468+++ mutt-1.12.1/keymap.c 2019-08-11 20:31:26.140940582 +0300
3469@@ -808,7 +808,6 @@ void km_init (void)
3470 km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
3471
3472 km_bindkey ("x", MENU_PAGER, OP_EXIT);
3473- km_bindkey ("i", MENU_PAGER, OP_EXIT);
3474 km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE);
3475 km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
3476 km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE);
3477diff -udprP mutt-1.12.1.orig/mailbox.h mutt-1.12.1/mailbox.h
3478--- mutt-1.12.1.orig/mailbox.h 2019-06-11 03:53:09.000000000 +0300
3479+++ mutt-1.12.1/mailbox.h 2019-08-11 20:31:26.140940582 +0300
3480@@ -78,6 +78,9 @@ int mx_is_imap (const char *);
3481 #ifdef USE_POP
3482 int mx_is_pop (const char *);
3483 #endif
3484+#ifdef USE_NNTP
3485+int mx_is_nntp (const char *);
3486+#endif
3487
3488 int mx_access (const char*, int);
3489 int mx_check_empty (const char *);
3490diff -udprP mutt-1.12.1.orig/main.c mutt-1.12.1/main.c
3491--- mutt-1.12.1.orig/main.c 2019-05-25 19:22:39.000000000 +0300
3492+++ mutt-1.12.1/main.c 2019-08-11 20:31:26.140940582 +0300
3493@@ -69,6 +69,10 @@
3494 #include <idn/stringprep.h>
3495 #endif
3496
3497+#ifdef USE_NNTP
3498+#include "nntp.h"
3499+#endif
3500+
3501 static const char *ReachingUs = N_("\
3502 To contact the developers, please mail to <mutt-dev@mutt.org>.\n\
3503 To report a bug, please contact the Mutt maintainers via gitlab:\n\
3504@@ -149,6 +153,8 @@ options:\n\
3505 -e <command>\tspecify a command to be executed after initialization\n\
3506 -f <file>\tspecify which mailbox to read\n\
3507 -F <file>\tspecify an alternate muttrc file\n\
3508+ -g <server>\tspecify a news server (if compiled with NNTP)\n\
3509+ -G\t\tselect a newsgroup (if compiled with NNTP)\n\
3510 -H <file>\tspecify a draft file to read header and body from\n\
3511 -i <file>\tspecify a file which Mutt should include in the body\n\
3512 -m <type>\tspecify a default mailbox type\n\
3513@@ -302,6 +308,12 @@ static void show_version (void)
3514 "-USE_POP "
3515 #endif
3516
3517+#ifdef USE_NNTP
3518+ "+USE_NNTP "
3519+#else
3520+ "-USE_NNTP "
3521+#endif
3522+
3523 #ifdef USE_IMAP
3524 "+USE_IMAP "
3525 #else
3526@@ -605,6 +617,7 @@ static void start_curses (void)
3527 #define MUTT_NOSYSRC (1<<2) /* -n */
3528 #define MUTT_RO (1<<3) /* -R */
3529 #define MUTT_SELECT (1<<4) /* -y */
3530+#define MUTT_NEWS (1<<5) /* -g and -G */
3531
3532 int main (int argc, char **argv, char **environ)
3533 {
3534@@ -696,7 +709,11 @@ int main (int argc, char **argv, char **
3535 argv[nargc++] = argv[optind];
3536 }
3537
3538+#ifdef USE_NNTP
3539+ if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:Ee:g:GH:s:i:hm:npQ:RvxyzZ")) != EOF)
3540+#else
3541 if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:Ee:H:s:i:hm:npQ:RvxyzZ")) != EOF)
3542+#endif
3543 switch (i)
3544 {
3545 case 'A':
3546@@ -799,6 +816,20 @@ int main (int argc, char **argv, char **
3547 flags |= MUTT_SELECT;
3548 break;
3549
3550+#ifdef USE_NNTP
3551+ case 'g': /* Specify a news server */
3552+ {
3553+ char buf[LONG_STRING];
3554+
3555+ snprintf (buf, sizeof (buf), "set news_server=%s", optarg);
3556+ commands = mutt_add_list (commands, buf);
3557+ }
3558+
3559+ case 'G': /* List of newsgroups */
3560+ flags |= MUTT_SELECT | MUTT_NEWS;
3561+ break;
3562+#endif
3563+
3564 case 'z':
3565 flags |= MUTT_IGNORE;
3566 break;
3567@@ -1284,6 +1315,18 @@ int main (int argc, char **argv, char **
3568 }
3569 else if (flags & MUTT_SELECT)
3570 {
3571+#ifdef USE_NNTP
3572+ if (flags & MUTT_NEWS)
3573+ {
3574+ set_option (OPTNEWS);
3575+ if (!(CurrentNewsSrv = nntp_select_server (NewsServer, 0)))
3576+ {
3577+ exit_endwin_msg = Errorbuf;
3578+ goto cleanup_and_exit;
3579+ }
3580+ }
3581+ else
3582+#endif
3583 if (!Incoming)
3584 {
3585 exit_endwin_msg = _("No incoming mailboxes defined.");
3586@@ -1300,6 +1343,14 @@ int main (int argc, char **argv, char **
3587
3588 if (!mutt_buffer_len (folder))
3589 mutt_buffer_strcpy (folder, NONULL(Spoolfile));
3590+#ifdef USE_NNTP
3591+ if (option (OPTNEWS))
3592+ {
3593+ unset_option (OPTNEWS);
3594+ nntp_buffer_expand_path (folder, &CurrentNewsSrv->conn->account);
3595+ }
3596+ else
3597+#endif
3598 mutt_buffer_expand_path (folder);
3599
3600 mutt_str_replace (&CurrentFolder, mutt_b2s (folder));
3601diff -udprP mutt-1.12.1.orig/mutt.h mutt-1.12.1/mutt.h
3602--- mutt-1.12.1.orig/mutt.h 2019-06-14 04:29:29.000000000 +0300
3603+++ mutt-1.12.1/mutt.h 2019-08-11 20:31:26.141940567 +0300
3604@@ -271,6 +271,9 @@ enum
3605 MUTT_XLABEL,
3606 MUTT_MIMEATTACH,
3607 MUTT_MIMETYPE,
3608+#ifdef USE_NNTP
3609+ MUTT_NEWSGROUPS,
3610+#endif
3611
3612 /* Options for Mailcap lookup */
3613 MUTT_EDIT,
3614@@ -329,6 +332,11 @@ enum
3615 #endif
3616 OPT_SUBJECT,
3617 OPT_VERIFYSIG, /* verify PGP signatures */
3618+#ifdef USE_NNTP
3619+ OPT_TOMODERATED,
3620+ OPT_CATCHUP,
3621+ OPT_FOLLOWUPTOPOSTER,
3622+#endif
3623
3624 /* THIS MUST BE THE LAST VALUE. */
3625 OPT_MAX
3626@@ -349,6 +357,7 @@ enum
3627 #define SENDDRAFTFILE (1<<11) /* Used by the -H flag */
3628 #define SENDTOSENDER (1<<12)
3629 #define SENDGROUPCHATREPLY (1<<13)
3630+#define SENDNEWS (1<<14)
3631
3632 /* flags for mutt_compose_menu() */
3633 #define MUTT_COMPOSE_NOFREEHEADER (1<<0)
3634@@ -371,6 +380,8 @@ enum
3635 OPTASCIICHARS,
3636 OPTASKBCC,
3637 OPTASKCC,
3638+ OPTASKFOLLOWUP,
3639+ OPTASKXCOMMENTTO,
3640 OPTATTACHSPLIT,
3641 OPTAUTOEDIT,
3642 OPTAUTOSUBSCRIBE,
3643@@ -466,6 +477,9 @@ enum
3644 OPTMETOO,
3645 OPTMHPURGE,
3646 OPTMIMEFORWDECODE,
3647+#ifdef USE_NNTP
3648+ OPTMIMESUBJECT, /* encode subject line with RFC2047 */
3649+#endif
3650 OPTMIMETYPEQUERYFIRST,
3651 OPTNARROWTREE,
3652 OPTPAGERSTOP,
3653@@ -568,6 +582,17 @@ enum
3654 OPTPGPAUTOINLINE,
3655 OPTPGPREPLYINLINE,
3656
3657+ /* news options */
3658+
3659+#ifdef USE_NNTP
3660+ OPTSHOWNEWNEWS,
3661+ OPTSHOWONLYUNREAD,
3662+ OPTSAVEUNSUB,
3663+ OPTLISTGROUP,
3664+ OPTLOADDESC,
3665+ OPTXCOMMENTTO,
3666+#endif
3667+
3668 /* pseudo options */
3669
3670 OPTAUXSORT, /* (pseudo) using auxiliary sort function */
3671@@ -585,6 +610,7 @@ enum
3672 OPTSORTSUBTHREADS, /* (pseudo) used when $sort_aux changes */
3673 OPTNEEDRESCORE, /* (pseudo) set when the `score' command is used */
3674 OPTATTACHMSG, /* (pseudo) used by attach-message */
3675+ OPTHIDEREAD, /* (pseudo) whether or not hide read messages */
3676 OPTKEEPQUIET, /* (pseudo) shut up the message and refresh
3677 * functions while we are executing an
3678 * external program.
3679@@ -595,6 +621,11 @@ enum
3680 OPTDONTHANDLEPGPKEYS, /* (pseudo) used to extract PGP keys */
3681 OPTIGNOREMACROEVENTS, /* (pseudo) don't process macro/push/exec events while set */
3682
3683+#ifdef USE_NNTP
3684+ OPTNEWS, /* (pseudo) used to change reader mode */
3685+ OPTNEWSSEND, /* (pseudo) used to change behavior when posting */
3686+#endif
3687+
3688 OPTMAX
3689 };
3690
3691@@ -680,6 +711,13 @@ typedef struct envelope
3692 char *supersedes;
3693 char *date;
3694 char *x_label;
3695+ char *organization;
3696+#ifdef USE_NNTP
3697+ char *newsgroups;
3698+ char *xref;
3699+ char *followup_to;
3700+ char *x_comment_to;
3701+#endif
3702 BUFFER *spam;
3703 LIST *references; /* message references (in reverse order) */
3704 LIST *in_reply_to; /* in-reply-to header content */
3705@@ -869,7 +907,7 @@ typedef struct header
3706 int refno; /* message number on server */
3707 #endif
3708
3709-#if defined USE_POP || defined USE_IMAP
3710+#if defined USE_POP || defined USE_IMAP || defined USE_NNTP
3711 void *data; /* driver-specific data */
3712 #endif
3713
3714diff -udprP mutt-1.12.1.orig/mutt_sasl.c mutt-1.12.1/mutt_sasl.c
3715--- mutt-1.12.1.orig/mutt_sasl.c 2019-05-25 19:22:39.000000000 +0300
3716+++ mutt-1.12.1/mutt_sasl.c 2019-08-11 20:31:26.141940567 +0300
3717@@ -192,6 +192,11 @@ int mutt_sasl_client_new (CONNECTION* co
3718 case MUTT_ACCT_TYPE_SMTP:
3719 service = "smtp";
3720 break;
3721+#ifdef USE_NNTP
3722+ case MUTT_ACCT_TYPE_NNTP:
3723+ service = "nntp";
3724+ break;
3725+#endif
3726 default:
3727 mutt_error (_("Unknown SASL profile"));
3728 return -1;
3729diff -udprP mutt-1.12.1.orig/muttlib.c mutt-1.12.1/muttlib.c
3730--- mutt-1.12.1.orig/muttlib.c 2019-05-25 19:22:39.000000000 +0300
3731+++ mutt-1.12.1/muttlib.c 2019-08-11 20:31:26.141940567 +0300
3732@@ -364,7 +364,7 @@ void mutt_free_header (HEADER **h)
3733 #ifdef MIXMASTER
3734 mutt_free_list (&(*h)->chain);
3735 #endif
3736-#if defined USE_POP || defined USE_IMAP
3737+#if defined USE_POP || defined USE_IMAP || defined USE_NNTP
3738 FREE (&(*h)->data);
3739 #endif
3740 FREE (h); /* __FREE_CHECKED__ */
3741@@ -778,6 +778,13 @@ void mutt_free_envelope (ENVELOPE **p)
3742 FREE (&(*p)->supersedes);
3743 FREE (&(*p)->date);
3744 FREE (&(*p)->x_label);
3745+ FREE (&(*p)->organization);
3746+#ifdef USE_NNTP
3747+ FREE (&(*p)->newsgroups);
3748+ FREE (&(*p)->xref);
3749+ FREE (&(*p)->followup_to);
3750+ FREE (&(*p)->x_comment_to);
3751+#endif
3752
3753 mutt_buffer_free (&(*p)->spam);
3754
3755@@ -1803,6 +1810,14 @@ int mutt_save_confirm (const char *s, st
3756 }
3757 }
3758
3759+#ifdef USE_NNTP
3760+ if (magic == MUTT_NNTP)
3761+ {
3762+ mutt_error _("Can't save message to news server.");
3763+ return 0;
3764+ }
3765+#endif
3766+
3767 if (stat (s, st) != -1)
3768 {
3769 if (magic == -1)
3770diff -udprP mutt-1.12.1.orig/mx.c mutt-1.12.1/mx.c
3771--- mutt-1.12.1.orig/mx.c 2019-05-25 19:22:39.000000000 +0300
3772+++ mutt-1.12.1/mx.c 2019-08-11 20:31:26.141940567 +0300
3773@@ -45,6 +45,10 @@
3774 #include "pop.h"
3775 #endif
3776
3777+#ifdef USE_NNTP
3778+#include "nntp.h"
3779+#endif
3780+
3781 #include "buffy.h"
3782
3783 #ifdef USE_DOTLOCK
3784@@ -88,6 +92,10 @@ struct mx_ops* mx_get_ops (int magic)
3785 case MUTT_COMPRESSED:
3786 return &mx_comp_ops;
3787 #endif
3788+#ifdef USE_NNTP
3789+ case MUTT_NNTP:
3790+ return &mx_nntp_ops;
3791+#endif
3792 default:
3793 return NULL;
3794 }
3795@@ -387,6 +395,22 @@ int mx_is_pop (const char *p)
3796 }
3797 #endif
3798
3799+#ifdef USE_NNTP
3800+int mx_is_nntp (const char *p)
3801+{
3802+ url_scheme_t scheme;
3803+
3804+ if (!p)
3805+ return 0;
3806+
3807+ scheme = url_check_scheme (p);
3808+ if (scheme == U_NNTP || scheme == U_NNTPS)
3809+ return 1;
3810+
3811+ return 0;
3812+}
3813+#endif
3814+
3815 int mx_get_magic (const char *path)
3816 {
3817 struct stat st;
3818@@ -404,6 +428,11 @@ int mx_get_magic (const char *path)
3819 return MUTT_POP;
3820 #endif /* USE_POP */
3821
3822+#ifdef USE_NNTP
3823+ if (mx_is_nntp (path))
3824+ return MUTT_NNTP;
3825+#endif /* USE_NNTP */
3826+
3827 if (stat (path, &st) == -1)
3828 {
3829 dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n",
3830@@ -849,6 +878,25 @@ int mx_close_mailbox (CONTEXT *ctx, int
3831 return 0;
3832 }
3833
3834+#ifdef USE_NNTP
3835+ if (ctx->unread && ctx->magic == MUTT_NNTP)
3836+ {
3837+ NNTP_DATA *nntp_data = ctx->data;
3838+
3839+ if (nntp_data && nntp_data->nserv && nntp_data->group)
3840+ {
3841+ int rc = query_quadoption (OPT_CATCHUP, _("Mark all articles read?"));
3842+ if (rc < 0)
3843+ {
3844+ ctx->closing = 0;
3845+ return -1;
3846+ }
3847+ else if (rc == MUTT_YES)
3848+ mutt_newsgroup_catchup (nntp_data->nserv, nntp_data->group);
3849+ }
3850+ }
3851+#endif
3852+
3853 for (i = 0; i < ctx->msgcount; i++)
3854 {
3855 if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read
3856@@ -856,6 +904,12 @@ int mx_close_mailbox (CONTEXT *ctx, int
3857 read_msgs++;
3858 }
3859
3860+#ifdef USE_NNTP
3861+ /* don't need to move articles from newsgroup */
3862+ if (ctx->magic == MUTT_NNTP)
3863+ read_msgs = 0;
3864+#endif
3865+
3866 if (read_msgs && quadoption (OPT_MOVE) != MUTT_NO)
3867 {
3868 char *p;
3869diff -udprP mutt-1.12.1.orig/mx.h mutt-1.12.1/mx.h
3870--- mutt-1.12.1.orig/mx.h 2019-05-25 19:22:39.000000000 +0300
3871+++ mutt-1.12.1/mx.h 2019-08-11 20:31:26.141940567 +0300
3872@@ -35,6 +35,9 @@ enum
3873 MUTT_MMDF,
3874 MUTT_MH,
3875 MUTT_MAILDIR,
3876+#ifdef USE_NNTP
3877+ MUTT_NNTP,
3878+#endif
3879 MUTT_IMAP,
3880 MUTT_POP
3881 #ifdef USE_COMPRESSED
3882diff -udprP mutt-1.12.1.orig/newsrc.c mutt-1.12.1/newsrc.c
3883--- mutt-1.12.1.orig/newsrc.c 1970-01-01 03:00:00.000000000 +0300
3884+++ mutt-1.12.1/newsrc.c 2019-08-11 20:31:26.142940552 +0300
3885@@ -0,0 +1,1260 @@
3886+/*
3887+ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
3888+ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
3889+ * Copyright (C) 2000-2019 Vsevolod Volkov <vvv@mutt.org.ua>
3890+ *
3891+ * This program is free software; you can redistribute it and/or modify
3892+ * it under the terms of the GNU General Public License as published by
3893+ * the Free Software Foundation; either version 2 of the License, or
3894+ * (at your option) any later version.
3895+ *
3896+ * This program is distributed in the hope that it will be useful,
3897+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3898+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3899+ * GNU General Public License for more details.
3900+ *
3901+ * You should have received a copy of the GNU General Public License
3902+ * along with this program; if not, write to the Free Software
3903+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
3904+ */
3905+
3906+#if HAVE_CONFIG_H
3907+#include "config.h"
3908+#endif
3909+
3910+#include "mutt.h"
3911+#include "mutt_curses.h"
3912+#include "sort.h"
3913+#include "mx.h"
3914+#include "mime.h"
3915+#include "mailbox.h"
3916+#include "nntp.h"
3917+#include "rfc822.h"
3918+#include "rfc1524.h"
3919+#include "rfc2047.h"
3920+#include "bcache.h"
3921+
3922+#if USE_HCACHE
3923+#include "hcache.h"
3924+#endif
3925+
3926+#include <unistd.h>
3927+#include <string.h>
3928+#include <ctype.h>
3929+#include <stdlib.h>
3930+#include <sys/stat.h>
3931+#include <sys/types.h>
3932+#include <dirent.h>
3933+#include <errno.h>
3934+
3935+/* Find NNTP_DATA for given newsgroup or add it */
3936+static NNTP_DATA *nntp_data_find (NNTP_SERVER *nserv, const char *group)
3937+{
3938+ NNTP_DATA *nntp_data = hash_find (nserv->groups_hash, group);
3939+
3940+ if (!nntp_data)
3941+ {
3942+ /* create NNTP_DATA structure and add it to hash */
3943+ nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1);
3944+ nntp_data->group = (char *)nntp_data + sizeof (NNTP_DATA);
3945+ strcpy (nntp_data->group, group);
3946+ nntp_data->nserv = nserv;
3947+ nntp_data->deleted = 1;
3948+ hash_insert (nserv->groups_hash, nntp_data->group, nntp_data);
3949+
3950+ /* add NNTP_DATA to list */
3951+ if (nserv->groups_num >= nserv->groups_max)
3952+ {
3953+ nserv->groups_max *= 2;
3954+ safe_realloc (&nserv->groups_list,
3955+ nserv->groups_max * sizeof (nntp_data));
3956+ }
3957+ nserv->groups_list[nserv->groups_num++] = nntp_data;
3958+ }
3959+ return nntp_data;
3960+}
3961+
3962+/* Remove all temporarily cache files */
3963+void nntp_acache_free (NNTP_DATA *nntp_data)
3964+{
3965+ int i;
3966+
3967+ for (i = 0; i < NNTP_ACACHE_LEN; i++)
3968+ {
3969+ if (nntp_data->acache[i].path)
3970+ {
3971+ unlink (nntp_data->acache[i].path);
3972+ FREE (&nntp_data->acache[i].path);
3973+ }
3974+ }
3975+}
3976+
3977+/* Free NNTP_DATA, used to destroy hash elements */
3978+void nntp_data_free (void *data)
3979+{
3980+ NNTP_DATA *nntp_data = data;
3981+
3982+ if (!nntp_data)
3983+ return;
3984+ nntp_acache_free (nntp_data);
3985+ mutt_bcache_close (&nntp_data->bcache);
3986+ FREE (&nntp_data->newsrc_ent);
3987+ FREE (&nntp_data->desc);
3988+ FREE (&data);
3989+}
3990+
3991+/* Unlock and close .newsrc file */
3992+void nntp_newsrc_close (NNTP_SERVER *nserv)
3993+{
3994+ if (!nserv->newsrc_fp)
3995+ return;
3996+
3997+ dprint (1, (debugfile, "Unlocking %s\n", nserv->newsrc_file));
3998+ mx_unlock_file (nserv->newsrc_file, fileno (nserv->newsrc_fp), 0);
3999+ safe_fclose (&nserv->newsrc_fp);
4000+}
4001+
4002+/* Parse .newsrc file:
4003+ * 0 - not changed
4004+ * 1 - parsed
4005+ * -1 - error */
4006+int nntp_newsrc_parse (NNTP_SERVER *nserv)
4007+{
4008+ unsigned int i;
4009+ char *line;
4010+ struct stat sb;
4011+
4012+ /* if file doesn't exist, create it */
4013+ nserv->newsrc_fp = safe_fopen (nserv->newsrc_file, "a");
4014+ safe_fclose (&nserv->newsrc_fp);
4015+
4016+ /* open .newsrc */
4017+ nserv->newsrc_fp = safe_fopen (nserv->newsrc_file, "r");
4018+ if (!nserv->newsrc_fp)
4019+ {
4020+ mutt_perror (nserv->newsrc_file);
4021+ mutt_sleep (2);
4022+ return -1;
4023+ }
4024+
4025+ /* lock it */
4026+ dprint (1, (debugfile, "Locking %s\n", nserv->newsrc_file));
4027+ if (mx_lock_file (nserv->newsrc_file, fileno (nserv->newsrc_fp), 0, 0, 1))
4028+ {
4029+ safe_fclose (&nserv->newsrc_fp);
4030+ return -1;
4031+ }
4032+
4033+ if (stat (nserv->newsrc_file, &sb))
4034+ {
4035+ mutt_perror (nserv->newsrc_file);
4036+ nntp_newsrc_close (nserv);
4037+ mutt_sleep (2);
4038+ return -1;
4039+ }
4040+
4041+ if (nserv->size == sb.st_size && nserv->mtime == sb.st_mtime)
4042+ return 0;
4043+
4044+ nserv->size = sb.st_size;
4045+ nserv->mtime = sb.st_mtime;
4046+ nserv->newsrc_modified = 1;
4047+ dprint (1, (debugfile, "Parsing %s\n", nserv->newsrc_file));
4048+
4049+ /* .newsrc has been externally modified or hasn't been loaded yet */
4050+ for (i = 0; i < nserv->groups_num; i++)
4051+ {
4052+ NNTP_DATA *nntp_data = nserv->groups_list[i];
4053+
4054+ if (!nntp_data)
4055+ continue;
4056+
4057+ nntp_data->subscribed = 0;
4058+ nntp_data->newsrc_len = 0;
4059+ FREE (&nntp_data->newsrc_ent);
4060+ }
4061+
4062+ line = safe_malloc (sb.st_size + 1);
4063+ while (sb.st_size && fgets (line, sb.st_size + 1, nserv->newsrc_fp))
4064+ {
4065+ char *b, *h, *p;
4066+ unsigned int subs = 0, i = 1;
4067+ NNTP_DATA *nntp_data;
4068+
4069+ /* find end of newsgroup name */
4070+ p = strpbrk (line, ":!");
4071+ if (!p)
4072+ continue;
4073+
4074+ /* ":" - subscribed, "!" - unsubscribed */
4075+ if (*p == ':')
4076+ subs++;
4077+ *p++ = '\0';
4078+
4079+ /* get newsgroup data */
4080+ nntp_data = nntp_data_find (nserv, line);
4081+ FREE (&nntp_data->newsrc_ent);
4082+
4083+ /* count number of entries */
4084+ b = p;
4085+ while (*b)
4086+ if (*b++ == ',')
4087+ i++;
4088+ nntp_data->newsrc_ent = safe_calloc (i, sizeof (NEWSRC_ENTRY));
4089+ nntp_data->subscribed = subs;
4090+
4091+ /* parse entries */
4092+ i = 0;
4093+ while (p)
4094+ {
4095+ b = p;
4096+
4097+ /* find end of entry */
4098+ p = strchr (p, ',');
4099+ if (p)
4100+ *p++ = '\0';
4101+
4102+ /* first-last or single number */
4103+ h = strchr (b, '-');
4104+ if (h)
4105+ *h++ = '\0';
4106+ else
4107+ h = b;
4108+
4109+ if (sscanf (b, ANUM, &nntp_data->newsrc_ent[i].first) == 1 &&
4110+ sscanf (h, ANUM, &nntp_data->newsrc_ent[i].last) == 1)
4111+ i++;
4112+ }
4113+ if (i == 0)
4114+ {
4115+ nntp_data->newsrc_ent[i].first = 1;
4116+ nntp_data->newsrc_ent[i].last = 0;
4117+ i++;
4118+ }
4119+ if (nntp_data->lastMessage == 0)
4120+ nntp_data->lastMessage = nntp_data->newsrc_ent[i - 1].last;
4121+ nntp_data->newsrc_len = i;
4122+ safe_realloc (&nntp_data->newsrc_ent, i * sizeof (NEWSRC_ENTRY));
4123+ nntp_group_unread_stat (nntp_data);
4124+ dprint (2, (debugfile, "nntp_newsrc_parse: %s\n", nntp_data->group));
4125+ }
4126+ FREE (&line);
4127+ return 1;
4128+}
4129+
4130+/* Generate array of .newsrc entries */
4131+void nntp_newsrc_gen_entries (CONTEXT *ctx)
4132+{
4133+ NNTP_DATA *nntp_data = ctx->data;
4134+ anum_t last = 0, first = 1;
4135+ int series, i;
4136+ int save_sort = SORT_ORDER;
4137+ unsigned int entries;
4138+
4139+ if (Sort != SORT_ORDER)
4140+ {
4141+ save_sort = Sort;
4142+ Sort = SORT_ORDER;
4143+ mutt_sort_headers (ctx, 0);
4144+ }
4145+
4146+ entries = nntp_data->newsrc_len;
4147+ if (!entries)
4148+ {
4149+ entries = 5;
4150+ nntp_data->newsrc_ent = safe_calloc (entries, sizeof (NEWSRC_ENTRY));
4151+ }
4152+
4153+ /* Set up to fake initial sequence from 1 to the article before the
4154+ * first article in our list */
4155+ nntp_data->newsrc_len = 0;
4156+ series = 1;
4157+ for (i = 0; i < ctx->msgcount; i++)
4158+ {
4159+ /* search for first unread */
4160+ if (series)
4161+ {
4162+ /* We don't actually check sequential order, since we mark
4163+ * "missing" entries as read/deleted */
4164+ last = NHDR (ctx->hdrs[i])->article_num;
4165+ if (last >= nntp_data->firstMessage && !ctx->hdrs[i]->deleted &&
4166+ !ctx->hdrs[i]->read)
4167+ {
4168+ if (nntp_data->newsrc_len >= entries)
4169+ {
4170+ entries *= 2;
4171+ safe_realloc (&nntp_data->newsrc_ent, entries * sizeof (NEWSRC_ENTRY));
4172+ }
4173+ nntp_data->newsrc_ent[nntp_data->newsrc_len].first = first;
4174+ nntp_data->newsrc_ent[nntp_data->newsrc_len].last = last - 1;
4175+ nntp_data->newsrc_len++;
4176+ series = 0;
4177+ }
4178+ }
4179+
4180+ /* search for first read */
4181+ else
4182+ {
4183+ if (ctx->hdrs[i]->deleted || ctx->hdrs[i]->read)
4184+ {
4185+ first = last + 1;
4186+ series = 1;
4187+ }
4188+ last = NHDR (ctx->hdrs[i])->article_num;
4189+ }
4190+ }
4191+
4192+ if (series && first <= nntp_data->lastLoaded)
4193+ {
4194+ if (nntp_data->newsrc_len >= entries)
4195+ {
4196+ entries++;
4197+ safe_realloc (&nntp_data->newsrc_ent, entries * sizeof (NEWSRC_ENTRY));
4198+ }
4199+ nntp_data->newsrc_ent[nntp_data->newsrc_len].first = first;
4200+ nntp_data->newsrc_ent[nntp_data->newsrc_len].last = nntp_data->lastLoaded;
4201+ nntp_data->newsrc_len++;
4202+ }
4203+ safe_realloc (&nntp_data->newsrc_ent,
4204+ nntp_data->newsrc_len * sizeof (NEWSRC_ENTRY));
4205+
4206+ if (save_sort != Sort)
4207+ {
4208+ Sort = save_sort;
4209+ mutt_sort_headers (ctx, 0);
4210+ }
4211+}
4212+
4213+/* Update file with new contents */
4214+static int update_file (char *filename, char *buf)
4215+{
4216+ FILE *fp;
4217+ char tmpfile[_POSIX_PATH_MAX];
4218+ int rc = -1;
4219+
4220+ while (1)
4221+ {
4222+ snprintf (tmpfile, sizeof (tmpfile), "%s.tmp", filename);
4223+ fp = fopen (tmpfile, "w");
4224+ if (!fp)
4225+ {
4226+ mutt_perror (tmpfile);
4227+ *tmpfile = '\0';
4228+ break;
4229+ }
4230+ if (fputs (buf, fp) == EOF)
4231+ {
4232+ mutt_perror (tmpfile);
4233+ break;
4234+ }
4235+ if (fclose (fp) == EOF)
4236+ {
4237+ mutt_perror (tmpfile);
4238+ fp = NULL;
4239+ break;
4240+ }
4241+ fp = NULL;
4242+ if (rename (tmpfile, filename) < 0)
4243+ {
4244+ mutt_perror (filename);
4245+ break;
4246+ }
4247+ *tmpfile = '\0';
4248+ rc = 0;
4249+ break;
4250+ }
4251+ if (fp)
4252+ fclose (fp);
4253+ if (*tmpfile)
4254+ unlink (tmpfile);
4255+ if (rc)
4256+ mutt_sleep (2);
4257+ return rc;
4258+}
4259+
4260+/* Update .newsrc file */
4261+int nntp_newsrc_update (NNTP_SERVER *nserv)
4262+{
4263+ char *buf;
4264+ size_t buflen, off;
4265+ unsigned int i;
4266+ int rc = -1;
4267+
4268+ if (!nserv)
4269+ return -1;
4270+
4271+ buflen = 10 * LONG_STRING;
4272+ buf = safe_calloc (1, buflen);
4273+ off = 0;
4274+
4275+ /* we will generate full newsrc here */
4276+ for (i = 0; i < nserv->groups_num; i++)
4277+ {
4278+ NNTP_DATA *nntp_data = nserv->groups_list[i];
4279+ unsigned int n;
4280+
4281+ if (!nntp_data || !nntp_data->newsrc_ent)
4282+ continue;
4283+
4284+ /* write newsgroup name */
4285+ if (off + strlen (nntp_data->group) + 3 > buflen)
4286+ {
4287+ buflen *= 2;
4288+ safe_realloc (&buf, buflen);
4289+ }
4290+ snprintf (buf + off, buflen - off, "%s%c ", nntp_data->group,
4291+ nntp_data->subscribed ? ':' : '!');
4292+ off += strlen (buf + off);
4293+
4294+ /* write entries */
4295+ for (n = 0; n < nntp_data->newsrc_len; n++)
4296+ {
4297+ if (off + LONG_STRING > buflen)
4298+ {
4299+ buflen *= 2;
4300+ safe_realloc (&buf, buflen);
4301+ }
4302+ if (n)
4303+ buf[off++] = ',';
4304+ if (nntp_data->newsrc_ent[n].first == nntp_data->newsrc_ent[n].last)
4305+ snprintf (buf + off, buflen - off, "%d", nntp_data->newsrc_ent[n].first);
4306+ else if (nntp_data->newsrc_ent[n].first < nntp_data->newsrc_ent[n].last)
4307+ snprintf (buf + off, buflen - off, "%d-%d",
4308+ nntp_data->newsrc_ent[n].first, nntp_data->newsrc_ent[n].last);
4309+ off += strlen (buf + off);
4310+ }
4311+ buf[off++] = '\n';
4312+ }
4313+ buf[off] = '\0';
4314+
4315+ /* newrc being fully rewritten */
4316+ dprint (1, (debugfile, "Updating %s\n", nserv->newsrc_file));
4317+ if (nserv->newsrc_file && update_file (nserv->newsrc_file, buf) == 0)
4318+ {
4319+ struct stat sb;
4320+
4321+ rc = stat (nserv->newsrc_file, &sb);
4322+ if (rc == 0)
4323+ {
4324+ nserv->size = sb.st_size;
4325+ nserv->mtime = sb.st_mtime;
4326+ }
4327+ else
4328+ {
4329+ mutt_perror (nserv->newsrc_file);
4330+ mutt_sleep (2);
4331+ }
4332+ }
4333+ FREE (&buf);
4334+ return rc;
4335+}
4336+
4337+/* Make fully qualified cache file name */
4338+static void cache_expand (char *dst, size_t dstlen, ACCOUNT *acct, char *src)
4339+{
4340+ char *c;
4341+ char file[_POSIX_PATH_MAX];
4342+
4343+ /* server subdirectory */
4344+ if (acct)
4345+ {
4346+ ciss_url_t url;
4347+
4348+ mutt_account_tourl (acct, &url);
4349+ url.path = src;
4350+ url_ciss_tostring (&url, file, sizeof (file), U_PATH);
4351+ }
4352+ else
4353+ strfcpy (file, src ? src : "", sizeof (file));
4354+
4355+ snprintf (dst, dstlen, "%s/%s", NewsCacheDir, file);
4356+
4357+ /* remove trailing slash */
4358+ c = dst + strlen (dst) - 1;
4359+ if (*c == '/')
4360+ *c = '\0';
4361+ mutt_expand_path (dst, dstlen);
4362+}
4363+
4364+/* Make fully qualified url from newsgroup name */
4365+void nntp_buffer_expand_path (BUFFER *line, ACCOUNT *acct)
4366+{
4367+ ciss_url_t url;
4368+
4369+ url.path = safe_strdup (mutt_b2s (line));
4370+ mutt_account_tourl (acct, &url);
4371+ url_ciss_tobuffer (&url, line, 0);
4372+ FREE (&url.path);
4373+}
4374+
4375+/* Parse newsgroup */
4376+int nntp_add_group (char *line, void *data)
4377+{
4378+ NNTP_SERVER *nserv = data;
4379+ NNTP_DATA *nntp_data;
4380+ char group[LONG_STRING];
4381+ char desc[HUGE_STRING] = "";
4382+ char mod;
4383+ anum_t first, last;
4384+
4385+ if (!nserv || !line)
4386+ return 0;
4387+
4388+ if (sscanf (line, "%s " ANUM " " ANUM " %c %[^\n]", group,
4389+ &last, &first, &mod, desc) < 4)
4390+ return 0;
4391+
4392+ nntp_data = nntp_data_find (nserv, group);
4393+ nntp_data->deleted = 0;
4394+ nntp_data->firstMessage = first;
4395+ nntp_data->lastMessage = last;
4396+ nntp_data->allowed = mod == 'y' || mod == 'm' ? 1 : 0;
4397+ mutt_str_replace (&nntp_data->desc, desc);
4398+ if (nntp_data->newsrc_ent || nntp_data->lastCached)
4399+ nntp_group_unread_stat (nntp_data);
4400+ else if (nntp_data->lastMessage &&
4401+ nntp_data->firstMessage <= nntp_data->lastMessage)
4402+ nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
4403+ else
4404+ nntp_data->unread = 0;
4405+ return 0;
4406+}
4407+
4408+/* Load list of all newsgroups from cache */
4409+static int active_get_cache (NNTP_SERVER *nserv)
4410+{
4411+ char buf[HUGE_STRING];
4412+ char file[_POSIX_PATH_MAX];
4413+ time_t t;
4414+ FILE *fp;
4415+
4416+ cache_expand (file, sizeof (file), &nserv->conn->account, ".active");
4417+ dprint (1, (debugfile, "Parsing %s\n", file));
4418+ fp = safe_fopen (file, "r");
4419+ if (!fp)
4420+ return -1;
4421+
4422+ if (fgets (buf, sizeof (buf), fp) == NULL ||
4423+ sscanf (buf, "%ld%s", &t, file) != 1 || t == 0)
4424+ {
4425+ fclose (fp);
4426+ return -1;
4427+ }
4428+ nserv->newgroups_time = t;
4429+
4430+ mutt_message _("Loading list of groups from cache...");
4431+ while (fgets (buf, sizeof (buf), fp))
4432+ nntp_add_group (buf, nserv);
4433+ nntp_add_group (NULL, NULL);
4434+ fclose (fp);
4435+ mutt_clear_error ();
4436+ return 0;
4437+}
4438+
4439+/* Save list of all newsgroups to cache */
4440+int nntp_active_save_cache (NNTP_SERVER *nserv)
4441+{
4442+ char file[_POSIX_PATH_MAX];
4443+ char *buf;
4444+ size_t buflen, off;
4445+ unsigned int i;
4446+ int rc;
4447+
4448+ if (!nserv->cacheable)
4449+ return 0;
4450+
4451+ buflen = 10 * LONG_STRING;
4452+ buf = safe_calloc (1, buflen);
4453+ snprintf (buf, buflen, "%lu\n", (unsigned long)nserv->newgroups_time);
4454+ off = strlen (buf);
4455+
4456+ for (i = 0; i < nserv->groups_num; i++)
4457+ {
4458+ NNTP_DATA *nntp_data = nserv->groups_list[i];
4459+
4460+ if (!nntp_data || nntp_data->deleted)
4461+ continue;
4462+
4463+ if (off + strlen (nntp_data->group) +
4464+ (nntp_data->desc ? strlen (nntp_data->desc) : 0) + 50 > buflen)
4465+ {
4466+ buflen *= 2;
4467+ safe_realloc (&buf, buflen);
4468+ }
4469+ snprintf (buf + off, buflen - off, "%s %d %d %c%s%s\n", nntp_data->group,
4470+ nntp_data->lastMessage, nntp_data->firstMessage,
4471+ nntp_data->allowed ? 'y' : 'n', nntp_data->desc ? " " : "",
4472+ nntp_data->desc ? nntp_data->desc : "");
4473+ off += strlen (buf + off);
4474+ }
4475+
4476+ cache_expand (file, sizeof (file), &nserv->conn->account, ".active");
4477+ dprint (1, (debugfile, "Updating %s\n", file));
4478+ rc = update_file (file, buf);
4479+ FREE (&buf);
4480+ return rc;
4481+}
4482+
4483+#ifdef USE_HCACHE
4484+/* Used by mutt_hcache_open() to compose hcache file name */
4485+static int nntp_hcache_namer (const char *path, char *dest, size_t destlen)
4486+{
4487+ return snprintf (dest, destlen, "%s.hcache", path);
4488+}
4489+
4490+/* Open newsgroup hcache */
4491+header_cache_t *nntp_hcache_open (NNTP_DATA *nntp_data)
4492+{
4493+ ciss_url_t url;
4494+ char file[_POSIX_PATH_MAX];
4495+
4496+ if (!nntp_data->nserv || !nntp_data->nserv->cacheable ||
4497+ !nntp_data->nserv->conn || !nntp_data->group ||
4498+ !(nntp_data->newsrc_ent || nntp_data->subscribed ||
4499+ option (OPTSAVEUNSUB)))
4500+ return NULL;
4501+
4502+ mutt_account_tourl (&nntp_data->nserv->conn->account, &url);
4503+ url.path = nntp_data->group;
4504+ url_ciss_tostring (&url, file, sizeof (file), U_PATH);
4505+ return mutt_hcache_open (NewsCacheDir, file, nntp_hcache_namer);
4506+}
4507+
4508+/* Remove stale cached headers */
4509+void nntp_hcache_update (NNTP_DATA *nntp_data, header_cache_t *hc)
4510+{
4511+ char buf[16];
4512+ int old = 0;
4513+ void *hdata;
4514+ anum_t first, last, current;
4515+
4516+ if (!hc)
4517+ return;
4518+
4519+ /* fetch previous values of first and last */
4520+ hdata = mutt_hcache_fetch_raw (hc, "index", strlen);
4521+ if (hdata)
4522+ {
4523+ dprint (2, (debugfile,
4524+ "nntp_hcache_update: mutt_hcache_fetch index: %s\n", hdata));
4525+ if (sscanf (hdata, ANUM " " ANUM, &first, &last) == 2)
4526+ {
4527+ old = 1;
4528+ nntp_data->lastCached = last;
4529+
4530+ /* clean removed headers from cache */
4531+ for (current = first; current <= last; current++)
4532+ {
4533+ if (current >= nntp_data->firstMessage &&
4534+ current <= nntp_data->lastMessage)
4535+ continue;
4536+
4537+ snprintf (buf, sizeof (buf), "%d", current);
4538+ dprint (2, (debugfile,
4539+ "nntp_hcache_update: mutt_hcache_delete %s\n", buf));
4540+ mutt_hcache_delete (hc, buf, strlen);
4541+ }
4542+ }
4543+ mutt_hcache_free (&hdata);
4544+ }
4545+
4546+ /* store current values of first and last */
4547+ if (!old || nntp_data->firstMessage != first ||
4548+ nntp_data->lastMessage != last)
4549+ {
4550+ snprintf (buf, sizeof (buf), "%u %u", nntp_data->firstMessage,
4551+ nntp_data->lastMessage);
4552+ dprint (2, (debugfile,
4553+ "nntp_hcache_update: mutt_hcache_store index: %s\n", buf));
4554+ mutt_hcache_store_raw (hc, "index", buf, strlen (buf) + 1, strlen);
4555+ }
4556+}
4557+#endif
4558+
4559+/* Remove bcache file */
4560+static int nntp_bcache_delete (const char *id, body_cache_t *bcache, void *data)
4561+{
4562+ NNTP_DATA *nntp_data = data;
4563+ anum_t anum;
4564+ char c;
4565+
4566+ if (!nntp_data || sscanf (id, ANUM "%c", &anum, &c) != 1 ||
4567+ anum < nntp_data->firstMessage || anum > nntp_data->lastMessage)
4568+ {
4569+ if (nntp_data)
4570+ dprint (2, (debugfile, "nntp_bcache_delete: mutt_bcache_del %s\n", id));
4571+ mutt_bcache_del (bcache, id);
4572+ }
4573+ return 0;
4574+}
4575+
4576+/* Remove stale cached messages */
4577+void nntp_bcache_update (NNTP_DATA *nntp_data)
4578+{
4579+ mutt_bcache_list (nntp_data->bcache, nntp_bcache_delete, nntp_data);
4580+}
4581+
4582+/* Remove hcache and bcache of newsgroup */
4583+void nntp_delete_group_cache (NNTP_DATA *nntp_data)
4584+{
4585+ char file[_POSIX_PATH_MAX];
4586+
4587+ if (!nntp_data || !nntp_data->nserv || !nntp_data->nserv->cacheable)
4588+ return;
4589+
4590+#ifdef USE_HCACHE
4591+ nntp_hcache_namer (nntp_data->group, file, sizeof (file));
4592+ cache_expand (file, sizeof (file), &nntp_data->nserv->conn->account, file);
4593+ unlink (file);
4594+ nntp_data->lastCached = 0;
4595+ dprint (2, (debugfile, "nntp_delete_group_cache: %s\n", file));
4596+#endif
4597+
4598+ if (!nntp_data->bcache)
4599+ nntp_data->bcache = mutt_bcache_open (&nntp_data->nserv->conn->account,
4600+ nntp_data->group);
4601+ if (nntp_data->bcache)
4602+ {
4603+ dprint (2, (debugfile, "nntp_delete_group_cache: %s/*\n", nntp_data->group));
4604+ mutt_bcache_list (nntp_data->bcache, nntp_bcache_delete, NULL);
4605+ mutt_bcache_close (&nntp_data->bcache);
4606+ }
4607+}
4608+
4609+/* Remove hcache and bcache of all unexistent and unsubscribed newsgroups */
4610+void nntp_clear_cache (NNTP_SERVER *nserv)
4611+{
4612+ char file[_POSIX_PATH_MAX];
4613+ char *fp;
4614+ struct dirent *entry;
4615+ DIR *dp;
4616+
4617+ if (!nserv || !nserv->cacheable)
4618+ return;
4619+
4620+ cache_expand (file, sizeof (file), &nserv->conn->account, NULL);
4621+ dp = opendir (file);
4622+ if (dp)
4623+ {
4624+ safe_strncat (file, sizeof (file), "/", 1);
4625+ fp = file + strlen (file);
4626+ while ((entry = readdir (dp)))
4627+ {
4628+ char *group = entry->d_name;
4629+ struct stat sb;
4630+ NNTP_DATA *nntp_data;
4631+ NNTP_DATA nntp_tmp;
4632+
4633+ if (mutt_strcmp (group, ".") == 0 ||
4634+ mutt_strcmp (group, "..") == 0)
4635+ continue;
4636+ *fp = '\0';
4637+ safe_strncat (file, sizeof (file), group, strlen (group));
4638+ if (stat (file, &sb))
4639+ continue;
4640+
4641+#ifdef USE_HCACHE
4642+ if (S_ISREG (sb.st_mode))
4643+ {
4644+ char *ext = group + strlen (group) - 7;
4645+ if (strlen (group) < 8 || mutt_strcmp (ext, ".hcache"))
4646+ continue;
4647+ *ext = '\0';
4648+ }
4649+ else
4650+#endif
4651+ if (!S_ISDIR (sb.st_mode))
4652+ continue;
4653+
4654+ nntp_data = hash_find (nserv->groups_hash, group);
4655+ if (!nntp_data)
4656+ {
4657+ nntp_data = &nntp_tmp;
4658+ nntp_data->nserv = nserv;
4659+ nntp_data->group = group;
4660+ nntp_data->bcache = NULL;
4661+ }
4662+ else if (nntp_data->newsrc_ent || nntp_data->subscribed ||
4663+ option (OPTSAVEUNSUB))
4664+ continue;
4665+
4666+ nntp_delete_group_cache (nntp_data);
4667+ if (S_ISDIR (sb.st_mode))
4668+ {
4669+ rmdir (file);
4670+ dprint (2, (debugfile, "nntp_clear_cache: %s\n", file));
4671+ }
4672+ }
4673+ closedir (dp);
4674+ }
4675+ return;
4676+}
4677+
4678+/* %a = account url
4679+ * %p = port
4680+ * %P = port if specified
4681+ * %s = news server name
4682+ * %S = url schema
4683+ * %u = username */
4684+const char *
4685+nntp_format_str (char *dest, size_t destlen, size_t col, int cols, char op,
4686+ const char *src, const char *fmt, const char *ifstring,
4687+ const char *elsestring, unsigned long data, format_flag flags)
4688+{
4689+ NNTP_SERVER *nserv = (NNTP_SERVER *)data;
4690+ ACCOUNT *acct = &nserv->conn->account;
4691+ ciss_url_t url;
4692+ char fn[SHORT_STRING], tmp[SHORT_STRING], *p;
4693+
4694+ switch (op)
4695+ {
4696+ case 'a':
4697+ mutt_account_tourl (acct, &url);
4698+ url_ciss_tostring (&url, fn, sizeof (fn), U_PATH);
4699+ p = strchr (fn, '/');
4700+ if (p)
4701+ *p = '\0';
4702+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
4703+ snprintf (dest, destlen, tmp, fn);
4704+ break;
4705+ case 'p':
4706+ snprintf (tmp, sizeof (tmp), "%%%su", fmt);
4707+ snprintf (dest, destlen, tmp, acct->port);
4708+ break;
4709+ case 'P':
4710+ *dest = '\0';
4711+ if (acct->flags & MUTT_ACCT_PORT)
4712+ {
4713+ snprintf (tmp, sizeof (tmp), "%%%su", fmt);
4714+ snprintf (dest, destlen, tmp, acct->port);
4715+ }
4716+ break;
4717+ case 's':
4718+ strncpy (fn, acct->host, sizeof (fn) - 1);
4719+ mutt_strlower (fn);
4720+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
4721+ snprintf (dest, destlen, tmp, fn);
4722+ break;
4723+ case 'S':
4724+ mutt_account_tourl (acct, &url);
4725+ url_ciss_tostring (&url, fn, sizeof (fn), U_PATH);
4726+ p = strchr (fn, ':');
4727+ if (p)
4728+ *p = '\0';
4729+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
4730+ snprintf (dest, destlen, tmp, fn);
4731+ break;
4732+ case 'u':
4733+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
4734+ snprintf (dest, destlen, tmp, acct->user);
4735+ break;
4736+ }
4737+ return (src);
4738+}
4739+
4740+/* Automatically loads a newsrc into memory, if necessary.
4741+ * Checks the size/mtime of a newsrc file, if it doesn't match, load
4742+ * again. Hmm, if a system has broken mtimes, this might mean the file
4743+ * is reloaded every time, which we'd have to fix. */
4744+NNTP_SERVER *nntp_select_server (char *server, int leave_lock)
4745+{
4746+ char file[_POSIX_PATH_MAX];
4747+ char *p;
4748+ int rc;
4749+ struct stat sb;
4750+ ACCOUNT acct;
4751+ NNTP_SERVER *nserv;
4752+ NNTP_DATA *nntp_data;
4753+ CONNECTION *conn;
4754+ ciss_url_t url;
4755+
4756+ if (!server || !*server)
4757+ {
4758+ mutt_error _("No news server defined!");
4759+ mutt_sleep (2);
4760+ return NULL;
4761+ }
4762+
4763+ /* create account from news server url */
4764+ acct.flags = 0;
4765+ acct.port = NNTP_PORT;
4766+ acct.type = MUTT_ACCT_TYPE_NNTP;
4767+ snprintf (file, sizeof (file), "%s%s",
4768+ strstr (server, "://") ? "" : "news://", server);
4769+ if (url_parse_ciss (&url, file) < 0 ||
4770+ (url.path && *url.path) ||
4771+ !(url.scheme == U_NNTP || url.scheme == U_NNTPS) ||
4772+ mutt_account_fromurl (&acct, &url) < 0)
4773+ {
4774+ mutt_error (_("%s is an invalid news server specification!"), server);
4775+ mutt_sleep (2);
4776+ return NULL;
4777+ }
4778+ if (url.scheme == U_NNTPS)
4779+ {
4780+ acct.flags |= MUTT_ACCT_SSL;
4781+ acct.port = NNTP_SSL_PORT;
4782+ }
4783+
4784+ /* find connection by account */
4785+ conn = mutt_conn_find (NULL, &acct);
4786+ if (!conn)
4787+ return NULL;
4788+ if (!(conn->account.flags & MUTT_ACCT_USER) && acct.flags & MUTT_ACCT_USER)
4789+ {
4790+ conn->account.flags |= MUTT_ACCT_USER;
4791+ conn->account.user[0] = '\0';
4792+ }
4793+
4794+ /* news server already exists */
4795+ nserv = conn->data;
4796+ if (nserv)
4797+ {
4798+ if (nserv->status == NNTP_BYE)
4799+ nserv->status = NNTP_NONE;
4800+ if (nntp_open_connection (nserv) < 0)
4801+ return NULL;
4802+
4803+ rc = nntp_newsrc_parse (nserv);
4804+ if (rc < 0)
4805+ return NULL;
4806+
4807+ /* check for new newsgroups */
4808+ if (!leave_lock && nntp_check_new_groups (nserv) < 0)
4809+ rc = -1;
4810+
4811+ /* .newsrc has been externally modified */
4812+ if (rc > 0)
4813+ nntp_clear_cache (nserv);
4814+ if (rc < 0 || !leave_lock)
4815+ nntp_newsrc_close (nserv);
4816+ return rc < 0 ? NULL : nserv;
4817+ }
4818+
4819+ /* new news server */
4820+ nserv = safe_calloc (1, sizeof (NNTP_SERVER));
4821+ nserv->conn = conn;
4822+ nserv->groups_hash = hash_create (1009, 0);
4823+ nserv->groups_max = 16;
4824+ nserv->groups_list = safe_malloc (nserv->groups_max * sizeof (nntp_data));
4825+
4826+ rc = nntp_open_connection (nserv);
4827+
4828+ /* try to create cache directory and enable caching */
4829+ nserv->cacheable = 0;
4830+ if (rc >= 0 && NewsCacheDir && *NewsCacheDir)
4831+ {
4832+ cache_expand (file, sizeof (file), &conn->account, NULL);
4833+ p = *file == '/' ? file + 1 : file;
4834+ while (1)
4835+ {
4836+ p = strchr (p, '/');
4837+ if (p)
4838+ *p = '\0';
4839+ if ((stat (file, &sb) || (sb.st_mode & S_IFDIR) == 0) &&
4840+ mkdir (file, 0700))
4841+ {
4842+ mutt_error (_("Can't create %s: %s."), file, strerror (errno));
4843+ mutt_sleep (2);
4844+ break;
4845+ }
4846+ if (!p)
4847+ {
4848+ nserv->cacheable = 1;
4849+ break;
4850+ }
4851+ *p++ = '/';
4852+ }
4853+ }
4854+
4855+ /* load .newsrc */
4856+ if (rc >= 0)
4857+ {
4858+ mutt_FormatString (file, sizeof (file), 0, sizeof (file), NONULL (NewsRc),
4859+ nntp_format_str, (unsigned long)nserv, 0);
4860+ mutt_expand_path (file, sizeof (file));
4861+ nserv->newsrc_file = safe_strdup (file);
4862+ rc = nntp_newsrc_parse (nserv);
4863+ }
4864+ if (rc >= 0)
4865+ {
4866+ /* try to load list of newsgroups from cache */
4867+ if (nserv->cacheable && active_get_cache (nserv) == 0)
4868+ rc = nntp_check_new_groups (nserv);
4869+
4870+ /* load list of newsgroups from server */
4871+ else
4872+ rc = nntp_active_fetch (nserv, 0);
4873+ }
4874+
4875+ if (rc >= 0)
4876+ nntp_clear_cache (nserv);
4877+
4878+#ifdef USE_HCACHE
4879+ /* check cache files */
4880+ if (rc >= 0 && nserv->cacheable)
4881+ {
4882+ struct dirent *entry;
4883+ DIR *dp = opendir (file);
4884+
4885+ if (dp)
4886+ {
4887+ while ((entry = readdir (dp)))
4888+ {
4889+ header_cache_t *hc;
4890+ void *hdata;
4891+ char *group = entry->d_name;
4892+
4893+ p = group + strlen (group) - 7;
4894+ if (strlen (group) < 8 || strcmp (p, ".hcache"))
4895+ continue;
4896+ *p = '\0';
4897+ nntp_data = hash_find (nserv->groups_hash, group);
4898+ if (!nntp_data)
4899+ continue;
4900+
4901+ hc = nntp_hcache_open (nntp_data);
4902+ if (!hc)
4903+ continue;
4904+
4905+ /* fetch previous values of first and last */
4906+ hdata = mutt_hcache_fetch_raw (hc, "index", strlen);
4907+ if (hdata)
4908+ {
4909+ anum_t first, last;
4910+
4911+ if (sscanf (hdata, ANUM " " ANUM, &first, &last) == 2)
4912+ {
4913+ if (nntp_data->deleted)
4914+ {
4915+ nntp_data->firstMessage = first;
4916+ nntp_data->lastMessage = last;
4917+ }
4918+ if (last >= nntp_data->firstMessage &&
4919+ last <= nntp_data->lastMessage)
4920+ {
4921+ nntp_data->lastCached = last;
4922+ dprint (2, (debugfile, "nntp_select_server: %s lastCached=%u\n",
4923+ nntp_data->group, last));
4924+ }
4925+ }
4926+ mutt_hcache_free (&hdata);
4927+ }
4928+ mutt_hcache_close (hc);
4929+ }
4930+ closedir (dp);
4931+ }
4932+ }
4933+#endif
4934+
4935+ if (rc < 0 || !leave_lock)
4936+ nntp_newsrc_close (nserv);
4937+
4938+ if (rc < 0)
4939+ {
4940+ hash_destroy (&nserv->groups_hash, nntp_data_free);
4941+ FREE (&nserv->groups_list);
4942+ FREE (&nserv->newsrc_file);
4943+ FREE (&nserv->authenticators);
4944+ FREE (&nserv);
4945+ mutt_socket_close (conn);
4946+ mutt_socket_free (conn);
4947+ return NULL;
4948+ }
4949+
4950+ conn->data = nserv;
4951+ return nserv;
4952+}
4953+
4954+/* Full status flags are not supported by nntp, but we can fake some of them:
4955+ * Read = a read message number is in the .newsrc
4956+ * New = not read and not cached
4957+ * Old = not read but cached */
4958+void nntp_article_status (CONTEXT *ctx, HEADER *hdr, char *group, anum_t anum)
4959+{
4960+ NNTP_DATA *nntp_data = ctx->data;
4961+ unsigned int i;
4962+
4963+ if (group)
4964+ nntp_data = hash_find (nntp_data->nserv->groups_hash, group);
4965+
4966+ if (!nntp_data)
4967+ return;
4968+
4969+ for (i = 0; i < nntp_data->newsrc_len; i++)
4970+ {
4971+ if ((anum >= nntp_data->newsrc_ent[i].first) &&
4972+ (anum <= nntp_data->newsrc_ent[i].last))
4973+ {
4974+ /* can't use mutt_set_flag() because mx_update_context()
4975+ didn't called yet */
4976+ hdr->read = 1;
4977+ return;
4978+ }
4979+ }
4980+
4981+ /* article was not cached yet, it's new */
4982+ if (anum > nntp_data->lastCached)
4983+ return;
4984+
4985+ /* article isn't read but cached, it's old */
4986+ if (option (OPTMARKOLD))
4987+ hdr->old = 1;
4988+}
4989+
4990+/* calculate number of unread articles using .newsrc data */
4991+void nntp_group_unread_stat (NNTP_DATA *nntp_data)
4992+{
4993+ unsigned int i;
4994+ anum_t first, last;
4995+
4996+ nntp_data->unread = 0;
4997+ if (nntp_data->lastMessage == 0 ||
4998+ nntp_data->firstMessage > nntp_data->lastMessage)
4999+ return;
5000+
5001+ nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
5002+ for (i = 0; i < nntp_data->newsrc_len; i++)
5003+ {
5004+ first = nntp_data->newsrc_ent[i].first;
5005+ if (first < nntp_data->firstMessage)
5006+ first = nntp_data->firstMessage;
5007+ last = nntp_data->newsrc_ent[i].last;
5008+ if (last > nntp_data->lastMessage)
5009+ last = nntp_data->lastMessage;
5010+ if (first <= last)
5011+ nntp_data->unread -= last - first + 1;
5012+ }
5013+}
5014+
5015+/* Subscribe newsgroup */
5016+NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *nserv, char *group)
5017+{
5018+ NNTP_DATA *nntp_data;
5019+
5020+ if (!nserv || !nserv->groups_hash || !group || !*group)
5021+ return NULL;
5022+
5023+ nntp_data = nntp_data_find (nserv, group);
5024+ nntp_data->subscribed = 1;
5025+ if (!nntp_data->newsrc_ent)
5026+ {
5027+ nntp_data->newsrc_ent = safe_calloc (1, sizeof (NEWSRC_ENTRY));
5028+ nntp_data->newsrc_len = 1;
5029+ nntp_data->newsrc_ent[0].first = 1;
5030+ nntp_data->newsrc_ent[0].last = 0;
5031+ }
5032+ return nntp_data;
5033+}
5034+
5035+/* Unsubscribe newsgroup */
5036+NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *nserv, char *group)
5037+{
5038+ NNTP_DATA *nntp_data;
5039+
5040+ if (!nserv || !nserv->groups_hash || !group || !*group)
5041+ return NULL;
5042+
5043+ nntp_data = hash_find (nserv->groups_hash, group);
5044+ if (!nntp_data)
5045+ return NULL;
5046+
5047+ nntp_data->subscribed = 0;
5048+ if (!option (OPTSAVEUNSUB))
5049+ {
5050+ nntp_data->newsrc_len = 0;
5051+ FREE (&nntp_data->newsrc_ent);
5052+ }
5053+ return nntp_data;
5054+}
5055+
5056+/* Catchup newsgroup */
5057+NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *nserv, char *group)
5058+{
5059+ NNTP_DATA *nntp_data;
5060+
5061+ if (!nserv || !nserv->groups_hash || !group || !*group)
5062+ return NULL;
5063+
5064+ nntp_data = hash_find (nserv->groups_hash, group);
5065+ if (!nntp_data)
5066+ return NULL;
5067+
5068+ if (nntp_data->newsrc_ent)
5069+ {
5070+ safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
5071+ nntp_data->newsrc_len = 1;
5072+ nntp_data->newsrc_ent[0].first = 1;
5073+ nntp_data->newsrc_ent[0].last = nntp_data->lastMessage;
5074+ }
5075+ nntp_data->unread = 0;
5076+ if (Context && Context->data == nntp_data)
5077+ {
5078+ unsigned int i;
5079+
5080+ for (i = 0; i < Context->msgcount; i++)
5081+ mutt_set_flag (Context, Context->hdrs[i], MUTT_READ, 1);
5082+ }
5083+ return nntp_data;
5084+}
5085+
5086+/* Uncatchup newsgroup */
5087+NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *nserv, char *group)
5088+{
5089+ NNTP_DATA *nntp_data;
5090+
5091+ if (!nserv || !nserv->groups_hash || !group || !*group)
5092+ return NULL;
5093+
5094+ nntp_data = hash_find (nserv->groups_hash, group);
5095+ if (!nntp_data)
5096+ return NULL;
5097+
5098+ if (nntp_data->newsrc_ent)
5099+ {
5100+ safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
5101+ nntp_data->newsrc_len = 1;
5102+ nntp_data->newsrc_ent[0].first = 1;
5103+ nntp_data->newsrc_ent[0].last = nntp_data->firstMessage - 1;
5104+ }
5105+ if (Context && Context->data == nntp_data)
5106+ {
5107+ unsigned int i;
5108+
5109+ nntp_data->unread = Context->msgcount;
5110+ for (i = 0; i < Context->msgcount; i++)
5111+ mutt_set_flag (Context, Context->hdrs[i], MUTT_READ, 0);
5112+ }
5113+ else
5114+ nntp_data->unread = nntp_data->lastMessage - nntp_data->newsrc_ent[0].last;
5115+ return nntp_data;
5116+}
5117+
5118+/* Get first newsgroup with new messages */
5119+void nntp_buffer_buffy (BUFFER *s)
5120+{
5121+ unsigned int i;
5122+
5123+ for (i = 0; i < CurrentNewsSrv->groups_num; i++)
5124+ {
5125+ NNTP_DATA *nntp_data = CurrentNewsSrv->groups_list[i];
5126+
5127+ if (!nntp_data || !nntp_data->subscribed || !nntp_data->unread)
5128+ continue;
5129+
5130+ if (Context && Context->magic == MUTT_NNTP &&
5131+ !mutt_strcmp (nntp_data->group, ((NNTP_DATA *)Context->data)->group))
5132+ {
5133+ unsigned int i, unread = 0;
5134+
5135+ for (i = 0; i < Context->msgcount; i++)
5136+ if (!Context->hdrs[i]->read && !Context->hdrs[i]->deleted)
5137+ unread++;
5138+ if (!unread)
5139+ continue;
5140+ }
5141+ mutt_buffer_strcpy (s, nntp_data->group);
5142+ return;
5143+ }
5144+ mutt_buffer_clear (s);
5145+}
5146diff -udprP mutt-1.12.1.orig/nntp.c mutt-1.12.1/nntp.c
5147--- mutt-1.12.1.orig/nntp.c 1970-01-01 03:00:00.000000000 +0300
5148+++ mutt-1.12.1/nntp.c 2019-08-11 20:31:26.142940552 +0300
5149@@ -0,0 +1,2486 @@
5150+/*
5151+ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
5152+ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
5153+ * Copyright (C) 2000-2017 Vsevolod Volkov <vvv@mutt.org.ua>
5154+ *
5155+ * This program is free software; you can redistribute it and/or modify
5156+ * it under the terms of the GNU General Public License as published by
5157+ * the Free Software Foundation; either version 2 of the License, or
5158+ * (at your option) any later version.
5159+ *
5160+ * This program is distributed in the hope that it will be useful,
5161+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
5162+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5163+ * GNU General Public License for more details.
5164+ *
5165+ * You should have received a copy of the GNU General Public License
5166+ * along with this program; if not, write to the Free Software
5167+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
5168+ */
5169+
5170+#if HAVE_CONFIG_H
5171+#include "config.h"
5172+#endif
5173+
5174+#include "mutt.h"
5175+#include "mutt_curses.h"
5176+#include "sort.h"
5177+#include "mx.h"
5178+#include "mime.h"
5179+#include "rfc1524.h"
5180+#include "rfc2047.h"
5181+#include "mailbox.h"
5182+#include "mutt_crypt.h"
5183+#include "nntp.h"
5184+
5185+#if defined(USE_SSL)
5186+#include "mutt_ssl.h"
5187+#endif
5188+
5189+#ifdef HAVE_PGP
5190+#include "pgp.h"
5191+#endif
5192+
5193+#ifdef HAVE_SMIME
5194+#include "smime.h"
5195+#endif
5196+
5197+#if USE_HCACHE
5198+#include "hcache.h"
5199+#endif
5200+
5201+#include <unistd.h>
5202+#include <string.h>
5203+#include <ctype.h>
5204+#include <stdlib.h>
5205+
5206+#ifdef USE_SASL
5207+#include <sasl/sasl.h>
5208+#include <sasl/saslutil.h>
5209+
5210+#include "mutt_sasl.h"
5211+#endif
5212+
5213+static int nntp_connect_error (NNTP_SERVER *nserv)
5214+{
5215+ nserv->status = NNTP_NONE;
5216+ mutt_error _("Server closed connection!");
5217+ mutt_sleep (2);
5218+ return -1;
5219+}
5220+
5221+/* Get capabilities:
5222+ * -1 - error, connection is closed
5223+ * 0 - mode is reader, capabilities setted up
5224+ * 1 - need to switch to reader mode */
5225+static int nntp_capabilities (NNTP_SERVER *nserv)
5226+{
5227+ CONNECTION *conn = nserv->conn;
5228+ unsigned int mode_reader = 0;
5229+ char buf[LONG_STRING];
5230+ char authinfo[LONG_STRING] = "";
5231+
5232+ nserv->hasCAPABILITIES = 0;
5233+ nserv->hasSTARTTLS = 0;
5234+ nserv->hasDATE = 0;
5235+ nserv->hasLIST_NEWSGROUPS = 0;
5236+ nserv->hasLISTGROUP = 0;
5237+ nserv->hasLISTGROUPrange = 0;
5238+ nserv->hasOVER = 0;
5239+ FREE (&nserv->authenticators);
5240+
5241+ if (mutt_socket_write (conn, "CAPABILITIES\r\n") < 0 ||
5242+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5243+ return nntp_connect_error (nserv);
5244+
5245+ /* no capabilities */
5246+ if (mutt_strncmp ("101", buf, 3))
5247+ return 1;
5248+ nserv->hasCAPABILITIES = 1;
5249+
5250+ /* parse capabilities */
5251+ do
5252+ {
5253+ if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5254+ return nntp_connect_error (nserv);
5255+ if (!mutt_strcmp ("STARTTLS", buf))
5256+ nserv->hasSTARTTLS = 1;
5257+ else if (!mutt_strcmp ("MODE-READER", buf))
5258+ mode_reader = 1;
5259+ else if (!mutt_strcmp ("READER", buf))
5260+ {
5261+ nserv->hasDATE = 1;
5262+ nserv->hasLISTGROUP = 1;
5263+ nserv->hasLISTGROUPrange = 1;
5264+ }
5265+ else if (!mutt_strncmp ("AUTHINFO ", buf, 9))
5266+ {
5267+ safe_strcat (buf, sizeof (buf), " ");
5268+ strfcpy (authinfo, buf + 8, sizeof (authinfo));
5269+ }
5270+#ifdef USE_SASL
5271+ else if (!mutt_strncmp ("SASL ", buf, 5))
5272+ {
5273+ char *p = buf + 5;
5274+ while (*p == ' ')
5275+ p++;
5276+ nserv->authenticators = safe_strdup (p);
5277+ }
5278+#endif
5279+ else if (!mutt_strcmp ("OVER", buf))
5280+ nserv->hasOVER = 1;
5281+ else if (!mutt_strncmp ("LIST ", buf, 5))
5282+ {
5283+ char *p = strstr (buf, " NEWSGROUPS");
5284+ if (p)
5285+ {
5286+ p += 11;
5287+ if (*p == '\0' || *p == ' ')
5288+ nserv->hasLIST_NEWSGROUPS = 1;
5289+ }
5290+ }
5291+ } while (mutt_strcmp (".", buf));
5292+ *buf = '\0';
5293+#ifdef USE_SASL
5294+ if (nserv->authenticators && strcasestr (authinfo, " SASL "))
5295+ strfcpy (buf, nserv->authenticators, sizeof (buf));
5296+#endif
5297+ if (strcasestr (authinfo, " USER "))
5298+ {
5299+ if (*buf)
5300+ safe_strcat (buf, sizeof (buf), " ");
5301+ safe_strcat (buf, sizeof (buf), "USER");
5302+ }
5303+ mutt_str_replace (&nserv->authenticators, buf);
5304+
5305+ /* current mode is reader */
5306+ if (nserv->hasDATE)
5307+ return 0;
5308+
5309+ /* server is mode-switching, need to switch to reader mode */
5310+ if (mode_reader)
5311+ return 1;
5312+
5313+ mutt_socket_close (conn);
5314+ nserv->status = NNTP_BYE;
5315+ mutt_error _("Server doesn't support reader mode.");
5316+ mutt_sleep (2);
5317+ return -1;
5318+}
5319+
5320+char *OverviewFmt =
5321+ "Subject:\0"
5322+ "From:\0"
5323+ "Date:\0"
5324+ "Message-ID:\0"
5325+ "References:\0"
5326+ "Content-Length:\0"
5327+ "Lines:\0"
5328+ "\0";
5329+
5330+/* Detect supported commands */
5331+static int nntp_attempt_features (NNTP_SERVER *nserv)
5332+{
5333+ CONNECTION *conn = nserv->conn;
5334+ char buf[LONG_STRING];
5335+
5336+ /* no CAPABILITIES, trying DATE, LISTGROUP, LIST NEWSGROUPS */
5337+ if (!nserv->hasCAPABILITIES)
5338+ {
5339+ if (mutt_socket_write (conn, "DATE\r\n") < 0 ||
5340+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5341+ return nntp_connect_error (nserv);
5342+ if (mutt_strncmp ("500", buf, 3))
5343+ nserv->hasDATE = 1;
5344+
5345+ if (mutt_socket_write (conn, "LISTGROUP\r\n") < 0 ||
5346+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5347+ return nntp_connect_error (nserv);
5348+ if (mutt_strncmp ("500", buf, 3))
5349+ nserv->hasLISTGROUP = 1;
5350+
5351+ if (mutt_socket_write (conn, "LIST NEWSGROUPS +\r\n") < 0 ||
5352+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5353+ return nntp_connect_error (nserv);
5354+ if (mutt_strncmp ("500", buf, 3))
5355+ nserv->hasLIST_NEWSGROUPS = 1;
5356+ if (!mutt_strncmp ("215", buf, 3))
5357+ {
5358+ do
5359+ {
5360+ if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5361+ return nntp_connect_error (nserv);
5362+ } while (mutt_strcmp (".", buf));
5363+ }
5364+ }
5365+
5366+ /* no LIST NEWSGROUPS, trying XGTITLE */
5367+ if (!nserv->hasLIST_NEWSGROUPS)
5368+ {
5369+ if (mutt_socket_write (conn, "XGTITLE\r\n") < 0 ||
5370+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5371+ return nntp_connect_error (nserv);
5372+ if (mutt_strncmp ("500", buf, 3))
5373+ nserv->hasXGTITLE = 1;
5374+ }
5375+
5376+ /* no OVER, trying XOVER */
5377+ if (!nserv->hasOVER)
5378+ {
5379+ if (mutt_socket_write (conn, "XOVER\r\n") < 0 ||
5380+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5381+ return nntp_connect_error (nserv);
5382+ if (mutt_strncmp ("500", buf, 3))
5383+ nserv->hasXOVER = 1;
5384+ }
5385+
5386+ /* trying LIST OVERVIEW.FMT */
5387+ if (nserv->hasOVER || nserv->hasXOVER)
5388+ {
5389+ if (mutt_socket_write (conn, "LIST OVERVIEW.FMT\r\n") < 0 ||
5390+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5391+ return nntp_connect_error (nserv);
5392+ if (mutt_strncmp ("215", buf, 3))
5393+ nserv->overview_fmt = OverviewFmt;
5394+ else
5395+ {
5396+ int chunk, cont = 0;
5397+ size_t buflen = 2 * LONG_STRING, off = 0, b = 0;
5398+
5399+ if (nserv->overview_fmt)
5400+ FREE (&nserv->overview_fmt);
5401+ nserv->overview_fmt = safe_malloc (buflen);
5402+
5403+ while (1)
5404+ {
5405+ if (buflen - off < LONG_STRING)
5406+ {
5407+ buflen *= 2;
5408+ safe_realloc (&nserv->overview_fmt, buflen);
5409+ }
5410+
5411+ chunk = mutt_socket_readln (nserv->overview_fmt + off,
5412+ buflen - off, conn);
5413+ if (chunk < 0)
5414+ {
5415+ FREE (&nserv->overview_fmt);
5416+ return nntp_connect_error (nserv);
5417+ }
5418+
5419+ if (!cont && !mutt_strcmp (".", nserv->overview_fmt + off))
5420+ break;
5421+
5422+ cont = chunk >= buflen - off ? 1 : 0;
5423+ off += strlen (nserv->overview_fmt + off);
5424+ if (!cont)
5425+ {
5426+ char *colon;
5427+
5428+ if (nserv->overview_fmt[b] == ':')
5429+ {
5430+ memmove (nserv->overview_fmt + b,
5431+ nserv->overview_fmt + b + 1, off - b - 1);
5432+ nserv->overview_fmt[off - 1] = ':';
5433+ }
5434+ colon = strchr (nserv->overview_fmt + b, ':');
5435+ if (!colon)
5436+ nserv->overview_fmt[off++] = ':';
5437+ else if (strcmp (colon + 1, "full"))
5438+ off = colon + 1 - nserv->overview_fmt;
5439+ if (!strcasecmp (nserv->overview_fmt + b, "Bytes:"))
5440+ {
5441+ strcpy (nserv->overview_fmt + b, "Content-Length:");
5442+ off = b + strlen (nserv->overview_fmt + b);
5443+ }
5444+ nserv->overview_fmt[off++] = '\0';
5445+ b = off;
5446+ }
5447+ }
5448+ nserv->overview_fmt[off++] = '\0';
5449+ safe_realloc (&nserv->overview_fmt, off);
5450+ }
5451+ }
5452+ return 0;
5453+}
5454+
5455+/* Get login, password and authenticate */
5456+static int nntp_auth (NNTP_SERVER *nserv)
5457+{
5458+ CONNECTION *conn = nserv->conn;
5459+ char buf[LONG_STRING];
5460+ char authenticators[LONG_STRING] = "USER";
5461+ char *method, *a, *p;
5462+ unsigned char flags = conn->account.flags;
5463+
5464+ while (1)
5465+ {
5466+ /* get login and password */
5467+ if (mutt_account_getuser (&conn->account) || !conn->account.user[0] ||
5468+ mutt_account_getpass (&conn->account) || !conn->account.pass[0])
5469+ break;
5470+
5471+ /* get list of authenticators */
5472+ if (NntpAuthenticators && *NntpAuthenticators)
5473+ strfcpy (authenticators, NntpAuthenticators, sizeof (authenticators));
5474+ else if (nserv->hasCAPABILITIES)
5475+ {
5476+ strfcpy (authenticators, NONULL (nserv->authenticators),
5477+ sizeof (authenticators));
5478+ p = authenticators;
5479+ while (*p)
5480+ {
5481+ if (*p == ' ')
5482+ *p = ':';
5483+ p++;
5484+ }
5485+ }
5486+ p = authenticators;
5487+ while (*p)
5488+ {
5489+ *p = ascii_toupper (*p);
5490+ p++;
5491+ }
5492+
5493+ dprint (1, (debugfile,
5494+ "nntp_auth: available methods: %s\n", nserv->authenticators));
5495+ a = authenticators;
5496+ while (1)
5497+ {
5498+ if (!a)
5499+ {
5500+ mutt_error _("No authenticators available");
5501+ mutt_sleep (2);
5502+ break;
5503+ }
5504+
5505+ method = a;
5506+ a = strchr (a, ':');
5507+ if (a)
5508+ *a++ = '\0';
5509+
5510+ /* check authenticator */
5511+ if (nserv->hasCAPABILITIES)
5512+ {
5513+ char *m;
5514+
5515+ if (!nserv->authenticators)
5516+ continue;
5517+ m = strcasestr (nserv->authenticators, method);
5518+ if (!m)
5519+ continue;
5520+ if (m > nserv->authenticators && *(m - 1) != ' ')
5521+ continue;
5522+ m += strlen (method);
5523+ if (*m != '\0' && *m != ' ')
5524+ continue;
5525+ }
5526+ dprint (1, (debugfile, "nntp_auth: trying method %s\n", method));
5527+
5528+ /* AUTHINFO USER authentication */
5529+ if (!strcmp (method, "USER"))
5530+ {
5531+ mutt_message (_("Authenticating (%s)..."), method);
5532+ snprintf (buf, sizeof (buf), "AUTHINFO USER %s\r\n", conn->account.user);
5533+ if (mutt_socket_write (conn, buf) < 0 ||
5534+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5535+ break;
5536+
5537+ /* authenticated, password is not required */
5538+ if (!mutt_strncmp ("281", buf, 3))
5539+ return 0;
5540+
5541+ /* username accepted, sending password */
5542+ if (!mutt_strncmp ("381", buf, 3))
5543+ {
5544+#ifdef DEBUG
5545+ if (debuglevel < MUTT_SOCK_LOG_FULL)
5546+ dprint (MUTT_SOCK_LOG_CMD, (debugfile,
5547+ "%d> AUTHINFO PASS *\n", conn->fd));
5548+#endif
5549+ snprintf (buf, sizeof (buf), "AUTHINFO PASS %s\r\n",
5550+ conn->account.pass);
5551+ if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) < 0 ||
5552+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5553+ break;
5554+
5555+ /* authenticated */
5556+ if (!mutt_strncmp ("281", buf, 3))
5557+ return 0;
5558+ }
5559+
5560+ /* server doesn't support AUTHINFO USER, trying next method */
5561+ if (*buf == '5')
5562+ continue;
5563+ }
5564+
5565+ else
5566+ {
5567+#ifdef USE_SASL
5568+ sasl_conn_t *saslconn;
5569+ sasl_interact_t *interaction = NULL;
5570+ int rc;
5571+ char inbuf[LONG_STRING] = "";
5572+ const char *mech;
5573+ const char *client_out = NULL;
5574+ unsigned int client_len, len;
5575+
5576+ if (mutt_sasl_client_new (conn, &saslconn) < 0)
5577+ {
5578+ dprint (1, (debugfile,
5579+ "nntp_auth: error allocating SASL connection.\n"));
5580+ continue;
5581+ }
5582+
5583+ while (1)
5584+ {
5585+ rc = sasl_client_start (saslconn, method, &interaction,
5586+ &client_out, &client_len, &mech);
5587+ if (rc != SASL_INTERACT)
5588+ break;
5589+ mutt_sasl_interact (interaction);
5590+ }
5591+ if (rc != SASL_OK && rc != SASL_CONTINUE)
5592+ {
5593+ sasl_dispose (&saslconn);
5594+ dprint (1, (debugfile,
5595+ "nntp_auth: error starting SASL authentication exchange.\n"));
5596+ continue;
5597+ }
5598+
5599+ mutt_message (_("Authenticating (%s)..."), method);
5600+ snprintf (buf, sizeof (buf), "AUTHINFO SASL %s", method);
5601+
5602+ /* looping protocol */
5603+ while (rc == SASL_CONTINUE || (rc == SASL_OK && client_len))
5604+ {
5605+ /* send out client response */
5606+ if (client_len)
5607+ {
5608+#ifdef DEBUG
5609+ if (debuglevel >= MUTT_SOCK_LOG_FULL)
5610+ {
5611+ char tmp[LONG_STRING];
5612+ memcpy (tmp, client_out, client_len);
5613+ for (p = tmp; p < tmp + client_len; p++)
5614+ {
5615+ if (*p == '\0')
5616+ *p = '.';
5617+ }
5618+ *p = '\0';
5619+ dprint (1, (debugfile, "SASL> %s\n", tmp));
5620+ }
5621+#endif
5622+
5623+ if (*buf)
5624+ safe_strcat (buf, sizeof (buf), " ");
5625+ len = strlen (buf);
5626+ if (sasl_encode64 (client_out, client_len,
5627+ buf + len, sizeof (buf) - len, &len) != SASL_OK)
5628+ {
5629+ dprint (1, (debugfile,
5630+ "nntp_auth: error base64-encoding client response.\n"));
5631+ break;
5632+ }
5633+ }
5634+
5635+ safe_strcat (buf, sizeof (buf), "\r\n");
5636+#ifdef DEBUG
5637+ if (debuglevel < MUTT_SOCK_LOG_FULL)
5638+ {
5639+ if (strchr (buf, ' '))
5640+ dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d> AUTHINFO SASL %s%s\n",
5641+ conn->fd, method, client_len ? " sasl_data" : ""));
5642+ else
5643+ dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d> sasl_data\n", conn->fd));
5644+ }
5645+#endif
5646+ client_len = 0;
5647+ if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) < 0 ||
5648+ mutt_socket_readln_d (inbuf, sizeof (inbuf), conn, MUTT_SOCK_LOG_FULL) < 0)
5649+ break;
5650+ if (mutt_strncmp (inbuf, "283 ", 4) &&
5651+ mutt_strncmp (inbuf, "383 ", 4))
5652+ {
5653+#ifdef DEBUG
5654+ if (debuglevel < MUTT_SOCK_LOG_FULL)
5655+ dprint (MUTT_SOCK_LOG_CMD, (debugfile, "%d< %s\n", conn->fd, inbuf));
5656+#endif
5657+ break;
5658+ }
5659+#ifdef DEBUG
5660+ if (debuglevel < MUTT_SOCK_LOG_FULL)
5661+ {
5662+ inbuf[3] = '\0';
5663+ dprint (MUTT_SOCK_LOG_CMD, (debugfile,
5664+ "%d< %s sasl_data\n", conn->fd, inbuf));
5665+ }
5666+#endif
5667+
5668+ if (!strcmp ("=", inbuf + 4))
5669+ len = 0;
5670+ else if (sasl_decode64 (inbuf + 4, strlen (inbuf + 4),
5671+ buf, sizeof (buf) - 1, &len) != SASL_OK)
5672+ {
5673+ dprint (1, (debugfile,
5674+ "nntp_auth: error base64-decoding server response.\n"));
5675+ break;
5676+ }
5677+#ifdef DEBUG
5678+ else if (debuglevel >= MUTT_SOCK_LOG_FULL)
5679+ {
5680+ char tmp[LONG_STRING];
5681+ memcpy (tmp, buf, len);
5682+ for (p = tmp; p < tmp + len; p++)
5683+ {
5684+ if (*p == '\0')
5685+ *p = '.';
5686+ }
5687+ *p = '\0';
5688+ dprint (1, (debugfile, "SASL< %s\n", tmp));
5689+ }
5690+#endif
5691+
5692+ while (1)
5693+ {
5694+ rc = sasl_client_step (saslconn, buf, len,
5695+ &interaction, &client_out, &client_len);
5696+ if (rc != SASL_INTERACT)
5697+ break;
5698+ mutt_sasl_interact (interaction);
5699+ }
5700+ if (*inbuf != '3')
5701+ break;
5702+
5703+ *buf = '\0';
5704+ } /* looping protocol */
5705+
5706+ if (rc == SASL_OK && client_len == 0 && *inbuf == '2')
5707+ {
5708+ mutt_sasl_setup_conn (conn, saslconn);
5709+ return 0;
5710+ }
5711+
5712+ /* terminate SASL sessoin */
5713+ sasl_dispose (&saslconn);
5714+ if (conn->fd < 0)
5715+ break;
5716+ if (!mutt_strncmp (inbuf, "383 ", 4))
5717+ {
5718+ if (mutt_socket_write (conn, "*\r\n") < 0 ||
5719+ mutt_socket_readln (inbuf, sizeof (inbuf), conn) < 0)
5720+ break;
5721+ }
5722+
5723+ /* server doesn't support AUTHINFO SASL, trying next method */
5724+ if (*inbuf == '5')
5725+ continue;
5726+#else
5727+ continue;
5728+#endif /* USE_SASL */
5729+ }
5730+
5731+ mutt_error (_("%s authentication failed."), method);
5732+ mutt_sleep (2);
5733+ break;
5734+ }
5735+ break;
5736+ }
5737+
5738+ /* error */
5739+ nserv->status = NNTP_BYE;
5740+ conn->account.flags = flags;
5741+ if (conn->fd < 0)
5742+ {
5743+ mutt_error _("Server closed connection!");
5744+ mutt_sleep (2);
5745+ }
5746+ else
5747+ mutt_socket_close (conn);
5748+ return -1;
5749+}
5750+
5751+/* Connect to server, authenticate and get capabilities */
5752+int nntp_open_connection (NNTP_SERVER *nserv)
5753+{
5754+ CONNECTION *conn = nserv->conn;
5755+ char buf[STRING];
5756+ int cap;
5757+ unsigned int posting = 0, auth = 1;
5758+
5759+ if (nserv->status == NNTP_OK)
5760+ return 0;
5761+ if (nserv->status == NNTP_BYE)
5762+ return -1;
5763+ nserv->status = NNTP_NONE;
5764+
5765+ if (mutt_socket_open (conn) < 0)
5766+ return -1;
5767+
5768+ if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5769+ return nntp_connect_error (nserv);
5770+
5771+ if (!mutt_strncmp ("200", buf, 3))
5772+ posting = 1;
5773+ else if (mutt_strncmp ("201", buf, 3))
5774+ {
5775+ mutt_socket_close (conn);
5776+ mutt_remove_trailing_ws (buf);
5777+ mutt_error ("%s", buf);
5778+ mutt_sleep (2);
5779+ return -1;
5780+ }
5781+
5782+ /* get initial capabilities */
5783+ cap = nntp_capabilities (nserv);
5784+ if (cap < 0)
5785+ return -1;
5786+
5787+ /* tell news server to switch to mode reader if it isn't so */
5788+ if (cap > 0)
5789+ {
5790+ if (mutt_socket_write (conn, "MODE READER\r\n") < 0 ||
5791+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5792+ return nntp_connect_error (nserv);
5793+
5794+ if (!mutt_strncmp ("200", buf, 3))
5795+ posting = 1;
5796+ else if (!mutt_strncmp ("201", buf, 3))
5797+ posting = 0;
5798+ /* error if has capabilities, ignore result if no capabilities */
5799+ else if (nserv->hasCAPABILITIES)
5800+ {
5801+ mutt_socket_close (conn);
5802+ mutt_error _("Could not switch to reader mode.");
5803+ mutt_sleep (2);
5804+ return -1;
5805+ }
5806+
5807+ /* recheck capabilities after MODE READER */
5808+ if (nserv->hasCAPABILITIES)
5809+ {
5810+ cap = nntp_capabilities (nserv);
5811+ if (cap < 0)
5812+ return -1;
5813+ }
5814+ }
5815+
5816+ mutt_message (_("Connected to %s. %s"), conn->account.host,
5817+ posting ? _("Posting is ok.") : _("Posting is NOT ok."));
5818+ mutt_sleep (1);
5819+
5820+#if defined(USE_SSL)
5821+ /* Attempt STARTTLS if available and desired. */
5822+ if (nserv->use_tls != 1 && (nserv->hasSTARTTLS || option (OPTSSLFORCETLS)))
5823+ {
5824+ if (nserv->use_tls == 0)
5825+ nserv->use_tls = option (OPTSSLFORCETLS) ||
5826+ query_quadoption (OPT_SSLSTARTTLS,
5827+ _("Secure connection with TLS?")) == MUTT_YES ? 2 : 1;
5828+ if (nserv->use_tls == 2)
5829+ {
5830+ if (mutt_socket_write (conn, "STARTTLS\r\n") < 0 ||
5831+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5832+ return nntp_connect_error (nserv);
5833+ if (mutt_strncmp ("382", buf, 3))
5834+ {
5835+ nserv->use_tls = 0;
5836+ mutt_error ("STARTTLS: %s", buf);
5837+ mutt_sleep (2);
5838+ }
5839+ else if (mutt_ssl_starttls (conn))
5840+ {
5841+ nserv->use_tls = 0;
5842+ nserv->status = NNTP_NONE;
5843+ mutt_socket_close (nserv->conn);
5844+ mutt_error _("Could not negotiate TLS connection");
5845+ mutt_sleep (2);
5846+ return -1;
5847+ }
5848+ else
5849+ {
5850+ /* recheck capabilities after STARTTLS */
5851+ cap = nntp_capabilities (nserv);
5852+ if (cap < 0)
5853+ return -1;
5854+ }
5855+ }
5856+ }
5857+#endif
5858+
5859+ /* authentication required? */
5860+ if (conn->account.flags & MUTT_ACCT_USER)
5861+ {
5862+ if (!conn->account.user[0])
5863+ auth = 0;
5864+ }
5865+ else
5866+ {
5867+ if (mutt_socket_write (conn, "STAT\r\n") < 0 ||
5868+ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
5869+ return nntp_connect_error (nserv);
5870+ if (mutt_strncmp ("480", buf, 3))
5871+ auth = 0;
5872+ }
5873+
5874+ /* authenticate */
5875+ if (auth && nntp_auth (nserv) < 0)
5876+ return -1;
5877+
5878+ /* get final capabilities after authentication */
5879+ if (nserv->hasCAPABILITIES && (auth || cap > 0))
5880+ {
5881+ cap = nntp_capabilities (nserv);
5882+ if (cap < 0)
5883+ return -1;
5884+ if (cap > 0)
5885+ {
5886+ mutt_socket_close (conn);
5887+ mutt_error _("Could not switch to reader mode.");
5888+ mutt_sleep (2);
5889+ return -1;
5890+ }
5891+ }
5892+
5893+ /* attempt features */
5894+ if (nntp_attempt_features (nserv) < 0)
5895+ return -1;
5896+
5897+ nserv->status = NNTP_OK;
5898+ return 0;
5899+}
5900+
5901+/* Send data from buffer and receive answer to same buffer */
5902+static int nntp_query (NNTP_DATA *nntp_data, char *line, size_t linelen)
5903+{
5904+ NNTP_SERVER *nserv = nntp_data->nserv;
5905+ char buf[LONG_STRING];
5906+
5907+ if (nserv->status == NNTP_BYE)
5908+ return -1;
5909+
5910+ while (1)
5911+ {
5912+ if (nserv->status == NNTP_OK)
5913+ {
5914+ int rc = 0;
5915+
5916+ if (*line)
5917+ rc = mutt_socket_write (nserv->conn, line);
5918+ else if (nntp_data->group)
5919+ {
5920+ snprintf (buf, sizeof (buf), "GROUP %s\r\n", nntp_data->group);
5921+ rc = mutt_socket_write (nserv->conn, buf);
5922+ }
5923+ if (rc >= 0)
5924+ rc = mutt_socket_readln (buf, sizeof (buf), nserv->conn);
5925+ if (rc >= 0)
5926+ break;
5927+ }
5928+
5929+ /* reconnect */
5930+ while (1)
5931+ {
5932+ nserv->status = NNTP_NONE;
5933+ if (nntp_open_connection (nserv) == 0)
5934+ break;
5935+
5936+ snprintf (buf, sizeof (buf), _("Connection to %s lost. Reconnect?"),
5937+ nserv->conn->account.host);
5938+ if (mutt_yesorno (buf, MUTT_YES) != MUTT_YES)
5939+ {
5940+ nserv->status = NNTP_BYE;
5941+ return -1;
5942+ }
5943+ }
5944+
5945+ /* select newsgroup after reconnection */
5946+ if (nntp_data->group)
5947+ {
5948+ snprintf (buf, sizeof (buf), "GROUP %s\r\n", nntp_data->group);
5949+ if (mutt_socket_write (nserv->conn, buf) < 0 ||
5950+ mutt_socket_readln (buf, sizeof (buf), nserv->conn) < 0)
5951+ return nntp_connect_error (nserv);
5952+ }
5953+ if (!*line)
5954+ break;
5955+ }
5956+
5957+ strfcpy (line, buf, linelen);
5958+ return 0;
5959+}
5960+
5961+/* This function calls funct(*line, *data) for each received line,
5962+ * funct(NULL, *data) if rewind(*data) needs, exits when fail or done:
5963+ * 0 - success
5964+ * 1 - bad response (answer in query buffer)
5965+ * -1 - conection lost
5966+ * -2 - error in funct(*line, *data) */
5967+static int nntp_fetch_lines (NNTP_DATA *nntp_data, char *query, size_t qlen,
5968+ char *msg, int (*funct) (char *, void *), void *data)
5969+{
5970+ int done = FALSE;
5971+ int rc;
5972+
5973+ while (!done)
5974+ {
5975+ char buf[LONG_STRING];
5976+ char *line;
5977+ unsigned int lines = 0;
5978+ size_t off = 0;
5979+ progress_t progress;
5980+
5981+ if (msg)
5982+ mutt_progress_init (&progress, msg, MUTT_PROGRESS_MSG, ReadInc, -1);
5983+
5984+ strfcpy (buf, query, sizeof (buf));
5985+ if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
5986+ return -1;
5987+ if (buf[0] != '2')
5988+ {
5989+ strfcpy (query, buf, qlen);
5990+ return 1;
5991+ }
5992+
5993+ line = safe_malloc (sizeof (buf));
5994+ rc = 0;
5995+
5996+ while (1)
5997+ {
5998+ char *p;
5999+ int chunk = mutt_socket_readln_d (buf, sizeof (buf),
6000+ nntp_data->nserv->conn, MUTT_SOCK_LOG_HDR);
6001+ if (chunk < 0)
6002+ {
6003+ nntp_data->nserv->status = NNTP_NONE;
6004+ break;
6005+ }
6006+
6007+ p = buf;
6008+ if (!off && buf[0] == '.')
6009+ {
6010+ if (buf[1] == '\0')
6011+ {
6012+ done = TRUE;
6013+ break;
6014+ }
6015+ if (buf[1] == '.')
6016+ p++;
6017+ }
6018+
6019+ strfcpy (line + off, p, sizeof (buf));
6020+
6021+ if (chunk >= sizeof (buf))
6022+ off += strlen (p);
6023+ else
6024+ {
6025+ if (msg)
6026+ mutt_progress_update (&progress, ++lines, -1);
6027+
6028+ if (rc == 0 && funct (line, data) < 0)
6029+ rc = -2;
6030+ off = 0;
6031+ }
6032+
6033+ safe_realloc (&line, off + sizeof (buf));
6034+ }
6035+ FREE (&line);
6036+ funct (NULL, data);
6037+ }
6038+ return rc;
6039+}
6040+
6041+/* Parse newsgroup description */
6042+static int fetch_description (char *line, void *data)
6043+{
6044+ NNTP_SERVER *nserv = data;
6045+ NNTP_DATA *nntp_data;
6046+ char *desc;
6047+
6048+ if (!line)
6049+ return 0;
6050+
6051+ desc = strpbrk (line, " \t");
6052+ if (desc)
6053+ {
6054+ *desc++ = '\0';
6055+ desc += strspn (desc, " \t");
6056+ }
6057+ else
6058+ desc = strchr (line, '\0');
6059+
6060+ nntp_data = hash_find (nserv->groups_hash, line);
6061+ if (nntp_data && mutt_strcmp (desc, nntp_data->desc))
6062+ {
6063+ mutt_str_replace (&nntp_data->desc, desc);
6064+ dprint (2, (debugfile, "group: %s, desc: %s\n", line, desc));
6065+ }
6066+ return 0;
6067+}
6068+
6069+/* Fetch newsgroups descriptions.
6070+ * Returns the same code as nntp_fetch_lines() */
6071+static int get_description (NNTP_DATA *nntp_data, char *wildmat, char *msg)
6072+{
6073+ NNTP_SERVER *nserv;
6074+ char buf[STRING];
6075+ char *cmd;
6076+ int rc;
6077+
6078+ /* get newsgroup description, if possible */
6079+ nserv = nntp_data->nserv;
6080+ if (!wildmat)
6081+ wildmat = nntp_data->group;
6082+ if (nserv->hasLIST_NEWSGROUPS)
6083+ cmd = "LIST NEWSGROUPS";
6084+ else if (nserv->hasXGTITLE)
6085+ cmd = "XGTITLE";
6086+ else
6087+ return 0;
6088+
6089+ snprintf (buf, sizeof (buf), "%s %s\r\n", cmd, wildmat);
6090+ rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), msg,
6091+ fetch_description, nserv);
6092+ if (rc > 0)
6093+ {
6094+ mutt_error ("%s: %s", cmd, buf);
6095+ mutt_sleep (2);
6096+ }
6097+ return rc;
6098+}
6099+
6100+/* Update read flag and set article number if empty */
6101+static void nntp_parse_xref (CONTEXT *ctx, HEADER *hdr)
6102+{
6103+ NNTP_DATA *nntp_data = ctx->data;
6104+ char *buf, *p;
6105+
6106+ buf = p = safe_strdup (hdr->env->xref);
6107+ while (p)
6108+ {
6109+ char *grp, *colon;
6110+ anum_t anum;
6111+
6112+ /* skip to next word */
6113+ p += strspn (p, " \t");
6114+ grp = p;
6115+
6116+ /* skip to end of word */
6117+ p = strpbrk (p, " \t");
6118+ if (p)
6119+ *p++ = '\0';
6120+
6121+ /* find colon */
6122+ colon = strchr (grp, ':');
6123+ if (!colon)
6124+ continue;
6125+ *colon++ = '\0';
6126+ if (sscanf (colon, ANUM, &anum) != 1)
6127+ continue;
6128+
6129+ nntp_article_status (ctx, hdr, grp, anum);
6130+ if (hdr && !NHDR (hdr)->article_num && !mutt_strcmp (nntp_data->group, grp))
6131+ NHDR (hdr)->article_num = anum;
6132+ }
6133+ FREE (&buf);
6134+}
6135+
6136+/* Write line to temporarily file */
6137+static int fetch_tempfile (char *line, void *data)
6138+{
6139+ FILE *fp = data;
6140+
6141+ if (!line)
6142+ rewind (fp);
6143+ else if (fputs (line, fp) == EOF || fputc ('\n', fp) == EOF)
6144+ return -1;
6145+ return 0;
6146+}
6147+
6148+typedef struct
6149+{
6150+ CONTEXT *ctx;
6151+ anum_t first;
6152+ anum_t last;
6153+ int restore;
6154+ unsigned char *messages;
6155+ progress_t progress;
6156+#ifdef USE_HCACHE
6157+ header_cache_t *hc;
6158+#endif
6159+} FETCH_CTX;
6160+
6161+/* Parse article number */
6162+static int fetch_numbers (char *line, void *data)
6163+{
6164+ FETCH_CTX *fc = data;
6165+ anum_t anum;
6166+
6167+ if (!line)
6168+ return 0;
6169+ if (sscanf (line, ANUM, &anum) != 1)
6170+ return 0;
6171+ if (anum < fc->first || anum > fc->last)
6172+ return 0;
6173+ fc->messages[anum - fc->first] = 1;
6174+ return 0;
6175+}
6176+
6177+/* Parse overview line */
6178+static int parse_overview_line (char *line, void *data)
6179+{
6180+ FETCH_CTX *fc = data;
6181+ CONTEXT *ctx = fc->ctx;
6182+ NNTP_DATA *nntp_data = ctx->data;
6183+ HEADER *hdr;
6184+ FILE *fp;
6185+ char tempfile[_POSIX_PATH_MAX];
6186+ char *header, *field;
6187+ int save = 1;
6188+ anum_t anum;
6189+
6190+ if (!line)
6191+ return 0;
6192+
6193+ /* parse article number */
6194+ field = strchr (line, '\t');
6195+ if (field)
6196+ *field++ = '\0';
6197+ if (sscanf (line, ANUM, &anum) != 1)
6198+ return 0;
6199+ dprint (2, (debugfile, "parse_overview_line: " ANUM "\n", anum));
6200+
6201+ /* out of bounds */
6202+ if (anum < fc->first || anum > fc->last)
6203+ return 0;
6204+
6205+ /* not in LISTGROUP */
6206+ if (!fc->messages[anum - fc->first])
6207+ {
6208+ /* progress */
6209+ if (!ctx->quiet)
6210+ mutt_progress_update (&fc->progress, anum - fc->first + 1, -1);
6211+ return 0;
6212+ }
6213+
6214+ /* convert overview line to header */
6215+ mutt_mktemp (tempfile, sizeof (tempfile));
6216+ fp = safe_fopen (tempfile, "w+");
6217+ if (!fp)
6218+ return -1;
6219+
6220+ header = nntp_data->nserv->overview_fmt;
6221+ while (field)
6222+ {
6223+ char *b = field;
6224+
6225+ if (*header)
6226+ {
6227+ if (strstr (header, ":full") == NULL && fputs (header, fp) == EOF)
6228+ {
6229+ fclose (fp);
6230+ unlink (tempfile);
6231+ return -1;
6232+ }
6233+ header = strchr (header, '\0') + 1;
6234+ }
6235+
6236+ field = strchr (field, '\t');
6237+ if (field)
6238+ *field++ = '\0';
6239+ if (fputs (b, fp) == EOF || fputc ('\n', fp) == EOF)
6240+ {
6241+ fclose (fp);
6242+ unlink (tempfile);
6243+ return -1;
6244+ }
6245+ }
6246+ rewind (fp);
6247+
6248+ /* allocate memory for headers */
6249+ if (ctx->msgcount >= ctx->hdrmax)
6250+ mx_alloc_memory (ctx);
6251+
6252+ /* parse header */
6253+ hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
6254+ hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
6255+ hdr->env->newsgroups = safe_strdup (nntp_data->group);
6256+ hdr->received = hdr->date_sent;
6257+ fclose (fp);
6258+ unlink (tempfile);
6259+
6260+#ifdef USE_HCACHE
6261+ if (fc->hc)
6262+ {
6263+ void *hdata;
6264+ char buf[16];
6265+
6266+ /* try to replace with header from cache */
6267+ snprintf (buf, sizeof (buf), "%d", anum);
6268+ hdata = mutt_hcache_fetch (fc->hc, buf, strlen);
6269+ if (hdata)
6270+ {
6271+ dprint (2, (debugfile,
6272+ "parse_overview_line: mutt_hcache_fetch %s\n", buf));
6273+ mutt_free_header (&hdr);
6274+ ctx->hdrs[ctx->msgcount] =
6275+ hdr = mutt_hcache_restore (hdata, NULL);
6276+ mutt_hcache_free (&hdata);
6277+ hdr->data = 0;
6278+ hdr->read = 0;
6279+ hdr->old = 0;
6280+
6281+ /* skip header marked as deleted in cache */
6282+ if (hdr->deleted && !fc->restore)
6283+ {
6284+ if (nntp_data->bcache)
6285+ {
6286+ dprint (2, (debugfile,
6287+ "parse_overview_line: mutt_bcache_del %s\n", buf));
6288+ mutt_bcache_del (nntp_data->bcache, buf);
6289+ }
6290+ save = 0;
6291+ }
6292+ }
6293+
6294+ /* not chached yet, store header */
6295+ else
6296+ {
6297+ dprint (2, (debugfile,
6298+ "parse_overview_line: mutt_hcache_store %s\n", buf));
6299+ mutt_hcache_store (fc->hc, buf, hdr, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
6300+ }
6301+ }
6302+#endif
6303+
6304+ if (save)
6305+ {
6306+ hdr->index = ctx->msgcount++;
6307+ hdr->read = 0;
6308+ hdr->old = 0;
6309+ hdr->deleted = 0;
6310+ hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
6311+ NHDR (hdr)->article_num = anum;
6312+ if (fc->restore)
6313+ hdr->changed = 1;
6314+ else
6315+ {
6316+ nntp_article_status (ctx, hdr, NULL, anum);
6317+ if (!hdr->read)
6318+ nntp_parse_xref (ctx, hdr);
6319+ }
6320+ if (anum > nntp_data->lastLoaded)
6321+ nntp_data->lastLoaded = anum;
6322+ }
6323+ else
6324+ mutt_free_header (&hdr);
6325+
6326+ /* progress */
6327+ if (!ctx->quiet)
6328+ mutt_progress_update (&fc->progress, anum - fc->first + 1, -1);
6329+ return 0;
6330+}
6331+
6332+/* Fetch headers */
6333+static int nntp_fetch_headers (CONTEXT *ctx, void *hc,
6334+ anum_t first, anum_t last, int restore)
6335+{
6336+ NNTP_DATA *nntp_data = ctx->data;
6337+ FETCH_CTX fc;
6338+ HEADER *hdr;
6339+ char buf[HUGE_STRING];
6340+ int rc = 0;
6341+ int oldmsgcount = ctx->msgcount;
6342+ anum_t current;
6343+ anum_t first_over = first;
6344+#ifdef USE_HCACHE
6345+ void *hdata;
6346+#endif
6347+
6348+ /* if empty group or nothing to do */
6349+ if (!last || first > last)
6350+ return 0;
6351+
6352+ /* init fetch context */
6353+ fc.ctx = ctx;
6354+ fc.first = first;
6355+ fc.last = last;
6356+ fc.restore = restore;
6357+ fc.messages = safe_calloc (last - first + 1, sizeof (unsigned char));
6358+#ifdef USE_HCACHE
6359+ fc.hc = hc;
6360+#endif
6361+
6362+ /* fetch list of articles */
6363+ if (option (OPTLISTGROUP) && nntp_data->nserv->hasLISTGROUP &&
6364+ !nntp_data->deleted)
6365+ {
6366+ if (!ctx->quiet)
6367+ mutt_message _("Fetching list of articles...");
6368+ if (nntp_data->nserv->hasLISTGROUPrange)
6369+ snprintf (buf, sizeof (buf), "LISTGROUP %s %d-%d\r\n", nntp_data->group,
6370+ first, last);
6371+ else
6372+ snprintf (buf, sizeof (buf), "LISTGROUP %s\r\n", nntp_data->group);
6373+ rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
6374+ fetch_numbers, &fc);
6375+ if (rc > 0)
6376+ {
6377+ mutt_error ("LISTGROUP: %s", buf);
6378+ mutt_sleep (2);
6379+ }
6380+ if (rc == 0)
6381+ {
6382+ for (current = first; current <= last && rc == 0; current++)
6383+ {
6384+ if (fc.messages[current - first])
6385+ continue;
6386+
6387+ snprintf (buf, sizeof (buf), "%d", current);
6388+ if (nntp_data->bcache)
6389+ {
6390+ dprint (2, (debugfile,
6391+ "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
6392+ mutt_bcache_del (nntp_data->bcache, buf);
6393+ }
6394+
6395+#ifdef USE_HCACHE
6396+ if (fc.hc)
6397+ {
6398+ dprint (2, (debugfile,
6399+ "nntp_fetch_headers: mutt_hcache_delete %s\n", buf));
6400+ mutt_hcache_delete (fc.hc, buf, strlen);
6401+ }
6402+#endif
6403+ }
6404+ }
6405+ }
6406+ else
6407+ for (current = first; current <= last; current++)
6408+ fc.messages[current - first] = 1;
6409+
6410+ /* fetching header from cache or server, or fallback to fetch overview */
6411+ if (!ctx->quiet)
6412+ mutt_progress_init (&fc.progress, _("Fetching message headers..."),
6413+ MUTT_PROGRESS_MSG, ReadInc, last - first + 1);
6414+ for (current = first; current <= last && rc == 0; current++)
6415+ {
6416+ if (!ctx->quiet)
6417+ mutt_progress_update (&fc.progress, current - first + 1, -1);
6418+
6419+#ifdef USE_HCACHE
6420+ snprintf (buf, sizeof (buf), "%d", current);
6421+#endif
6422+
6423+ /* delete header from cache that does not exist on server */
6424+ if (!fc.messages[current - first])
6425+ continue;
6426+
6427+ /* allocate memory for headers */
6428+ if (ctx->msgcount >= ctx->hdrmax)
6429+ mx_alloc_memory (ctx);
6430+
6431+#ifdef USE_HCACHE
6432+ /* try to fetch header from cache */
6433+ hdata = mutt_hcache_fetch (fc.hc, buf, strlen);
6434+ if (hdata)
6435+ {
6436+ dprint (2, (debugfile,
6437+ "nntp_fetch_headers: mutt_hcache_fetch %s\n", buf));
6438+ ctx->hdrs[ctx->msgcount] =
6439+ hdr = mutt_hcache_restore (hdata, NULL);
6440+ mutt_hcache_free (&hdata);
6441+ hdr->data = 0;
6442+
6443+ /* skip header marked as deleted in cache */
6444+ if (hdr->deleted && !restore)
6445+ {
6446+ mutt_free_header (&hdr);
6447+ if (nntp_data->bcache)
6448+ {
6449+ dprint (2, (debugfile,
6450+ "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
6451+ mutt_bcache_del (nntp_data->bcache, buf);
6452+ }
6453+ continue;
6454+ }
6455+
6456+ hdr->read = 0;
6457+ hdr->old = 0;
6458+ }
6459+ else
6460+#endif
6461+
6462+ /* don't try to fetch header from removed newsgroup */
6463+ if (nntp_data->deleted)
6464+ continue;
6465+
6466+ /* fallback to fetch overview */
6467+ else if (nntp_data->nserv->hasOVER || nntp_data->nserv->hasXOVER)
6468+ if (option (OPTLISTGROUP) && nntp_data->nserv->hasLISTGROUP)
6469+ break;
6470+ else
6471+ continue;
6472+
6473+ /* fetch header from server */
6474+ else
6475+ {
6476+ FILE *fp;
6477+ char tempfile[_POSIX_PATH_MAX];
6478+
6479+ mutt_mktemp (tempfile, sizeof (tempfile));
6480+ fp = safe_fopen (tempfile, "w+");
6481+ if (!fp)
6482+ {
6483+ mutt_perror (tempfile);
6484+ mutt_sleep (2);
6485+ unlink (tempfile);
6486+ rc = -1;
6487+ break;
6488+ }
6489+
6490+ snprintf (buf, sizeof (buf), "HEAD %d\r\n", current);
6491+ rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
6492+ fetch_tempfile, fp);
6493+ if (rc)
6494+ {
6495+ fclose (fp);
6496+ unlink (tempfile);
6497+ if (rc < 0)
6498+ break;
6499+
6500+ /* invalid response */
6501+ if (mutt_strncmp ("423", buf, 3))
6502+ {
6503+ mutt_error ("HEAD: %s", buf);
6504+ mutt_sleep (2);
6505+ break;
6506+ }
6507+
6508+ /* no such article */
6509+ if (nntp_data->bcache)
6510+ {
6511+ snprintf (buf, sizeof (buf), "%d", current);
6512+ dprint (2, (debugfile,
6513+ "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
6514+ mutt_bcache_del (nntp_data->bcache, buf);
6515+ }
6516+ rc = 0;
6517+ continue;
6518+ }
6519+
6520+ /* parse header */
6521+ hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
6522+ hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
6523+ hdr->received = hdr->date_sent;
6524+ fclose (fp);
6525+ unlink (tempfile);
6526+ }
6527+
6528+ /* save header in context */
6529+ hdr->index = ctx->msgcount++;
6530+ hdr->read = 0;
6531+ hdr->old = 0;
6532+ hdr->deleted = 0;
6533+ hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
6534+ NHDR (hdr)->article_num = current;
6535+ if (restore)
6536+ hdr->changed = 1;
6537+ else
6538+ {
6539+ nntp_article_status (ctx, hdr, NULL, NHDR (hdr)->article_num);
6540+ if (!hdr->read)
6541+ nntp_parse_xref (ctx, hdr);
6542+ }
6543+ if (current > nntp_data->lastLoaded)
6544+ nntp_data->lastLoaded = current;
6545+ first_over = current + 1;
6546+ }
6547+
6548+ if (!option (OPTLISTGROUP) || !nntp_data->nserv->hasLISTGROUP)
6549+ current = first_over;
6550+
6551+ /* fetch overview information */
6552+ if (current <= last && rc == 0 && !nntp_data->deleted) {
6553+ char *cmd = nntp_data->nserv->hasOVER ? "OVER" : "XOVER";
6554+ snprintf (buf, sizeof (buf), "%s %d-%d\r\n", cmd, current, last);
6555+ rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
6556+ parse_overview_line, &fc);
6557+ if (rc > 0)
6558+ {
6559+ mutt_error ("%s: %s", cmd, buf);
6560+ mutt_sleep (2);
6561+ }
6562+ }
6563+
6564+ if (ctx->msgcount > oldmsgcount)
6565+ mx_update_context (ctx, ctx->msgcount - oldmsgcount);
6566+
6567+ FREE (&fc.messages);
6568+ if (rc != 0)
6569+ return -1;
6570+ mutt_clear_error ();
6571+ return 0;
6572+}
6573+
6574+/* Open newsgroup */
6575+static int nntp_open_mailbox (CONTEXT *ctx)
6576+{
6577+ NNTP_SERVER *nserv;
6578+ NNTP_DATA *nntp_data;
6579+ char buf[HUGE_STRING];
6580+ char server[LONG_STRING];
6581+ char *group;
6582+ int rc;
6583+ void *hc = NULL;
6584+ anum_t first, last, count = 0;
6585+ ciss_url_t url;
6586+
6587+ strfcpy (buf, ctx->path, sizeof (buf));
6588+ if (url_parse_ciss (&url, buf) < 0 || !url.path ||
6589+ !(url.scheme == U_NNTP || url.scheme == U_NNTPS))
6590+ {
6591+ mutt_error (_("%s is an invalid newsgroup specification!"), ctx->path);
6592+ mutt_sleep (2);
6593+ return -1;
6594+ }
6595+
6596+ group = url.path;
6597+ url.path = strchr (url.path, '\0');
6598+ url_ciss_tostring (&url, server, sizeof (server), 0);
6599+ nserv = nntp_select_server (server, 1);
6600+ if (!nserv)
6601+ return -1;
6602+ CurrentNewsSrv = nserv;
6603+
6604+ /* find news group data structure */
6605+ nntp_data = hash_find (nserv->groups_hash, group);
6606+ if (!nntp_data)
6607+ {
6608+ nntp_newsrc_close (nserv);
6609+ mutt_error (_("Newsgroup %s not found on the server."), group);
6610+ mutt_sleep (2);
6611+ return -1;
6612+ }
6613+
6614+ mutt_bit_unset (ctx->rights, MUTT_ACL_INSERT);
6615+ if (!nntp_data->newsrc_ent && !nntp_data->subscribed &&
6616+ !option (OPTSAVEUNSUB))
6617+ ctx->readonly = 1;
6618+
6619+ /* select newsgroup */
6620+ mutt_message (_("Selecting %s..."), group);
6621+ buf[0] = '\0';
6622+ if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
6623+ {
6624+ nntp_newsrc_close (nserv);
6625+ return -1;
6626+ }
6627+
6628+ /* newsgroup not found, remove it */
6629+ if (!mutt_strncmp ("411", buf, 3))
6630+ {
6631+ mutt_error (_("Newsgroup %s has been removed from the server."),
6632+ nntp_data->group);
6633+ if (!nntp_data->deleted)
6634+ {
6635+ nntp_data->deleted = 1;
6636+ nntp_active_save_cache (nserv);
6637+ }
6638+ if (nntp_data->newsrc_ent && !nntp_data->subscribed &&
6639+ !option (OPTSAVEUNSUB))
6640+ {
6641+ FREE (&nntp_data->newsrc_ent);
6642+ nntp_data->newsrc_len = 0;
6643+ nntp_delete_group_cache (nntp_data);
6644+ nntp_newsrc_update (nserv);
6645+ }
6646+ mutt_sleep (2);
6647+ }
6648+
6649+ /* parse newsgroup info */
6650+ else {
6651+ if (sscanf (buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
6652+ {
6653+ nntp_newsrc_close (nserv);
6654+ mutt_error ("GROUP: %s", buf);
6655+ mutt_sleep (2);
6656+ return -1;
6657+ }
6658+ nntp_data->firstMessage = first;
6659+ nntp_data->lastMessage = last;
6660+ nntp_data->deleted = 0;
6661+
6662+ /* get description if empty */
6663+ if (option (OPTLOADDESC) && !nntp_data->desc)
6664+ {
6665+ if (get_description (nntp_data, NULL, NULL) < 0)
6666+ {
6667+ nntp_newsrc_close (nserv);
6668+ return -1;
6669+ }
6670+ if (nntp_data->desc)
6671+ nntp_active_save_cache (nserv);
6672+ }
6673+ }
6674+
6675+ time (&nserv->check_time);
6676+ ctx->data = nntp_data;
6677+ if (!nntp_data->bcache && (nntp_data->newsrc_ent ||
6678+ nntp_data->subscribed || option (OPTSAVEUNSUB)))
6679+ nntp_data->bcache = mutt_bcache_open (&nserv->conn->account,
6680+ nntp_data->group);
6681+
6682+ /* strip off extra articles if adding context is greater than $nntp_context */
6683+ first = nntp_data->firstMessage;
6684+ if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext)
6685+ first = nntp_data->lastMessage - NntpContext + 1;
6686+ nntp_data->lastLoaded = first ? first - 1 : 0;
6687+ count = nntp_data->firstMessage;
6688+ nntp_data->firstMessage = first;
6689+ nntp_bcache_update (nntp_data);
6690+ nntp_data->firstMessage = count;
6691+#ifdef USE_HCACHE
6692+ hc = nntp_hcache_open (nntp_data);
6693+ nntp_hcache_update (nntp_data, hc);
6694+#endif
6695+ if (!hc)
6696+ {
6697+ mutt_bit_unset (ctx->rights, MUTT_ACL_WRITE);
6698+ mutt_bit_unset (ctx->rights, MUTT_ACL_DELETE);
6699+ }
6700+ nntp_newsrc_close (nserv);
6701+ rc = nntp_fetch_headers (ctx, hc, first, nntp_data->lastMessage, 0);
6702+#ifdef USE_HCACHE
6703+ mutt_hcache_close (hc);
6704+#endif
6705+ if (rc < 0)
6706+ return -1;
6707+ nntp_data->lastLoaded = nntp_data->lastMessage;
6708+ nserv->newsrc_modified = 0;
6709+ return 0;
6710+}
6711+
6712+/* Fetch message */
6713+static int nntp_open_message (CONTEXT *ctx, MESSAGE *msg, int msgno)
6714+{
6715+ NNTP_DATA *nntp_data = ctx->data;
6716+ NNTP_ACACHE *acache;
6717+ HEADER *hdr = ctx->hdrs[msgno];
6718+ char buf[_POSIX_PATH_MAX];
6719+ char article[16];
6720+ char *fetch_msg = _("Fetching message...");
6721+ int rc;
6722+
6723+ /* try to get article from cache */
6724+ acache = &nntp_data->acache[hdr->index % NNTP_ACACHE_LEN];
6725+ if (acache->path)
6726+ {
6727+ if (acache->index == hdr->index)
6728+ {
6729+ msg->fp = fopen (acache->path, "r");
6730+ if (msg->fp)
6731+ return 0;
6732+ }
6733+ /* clear previous entry */
6734+ else
6735+ {
6736+ unlink (acache->path);
6737+ FREE (&acache->path);
6738+ }
6739+ }
6740+ snprintf (article, sizeof (article), "%d", NHDR (hdr)->article_num);
6741+ msg->fp = mutt_bcache_get (nntp_data->bcache, article);
6742+ if (msg->fp)
6743+ {
6744+ if (NHDR (hdr)->parsed)
6745+ return 0;
6746+ }
6747+ else
6748+ {
6749+ /* don't try to fetch article from removed newsgroup */
6750+ if (nntp_data->deleted)
6751+ return -1;
6752+
6753+ /* create new cache file */
6754+ mutt_message (fetch_msg);
6755+ msg->fp = mutt_bcache_put (nntp_data->bcache, article, 1);
6756+ if (!msg->fp)
6757+ {
6758+ mutt_mktemp (buf, sizeof (buf));
6759+ acache->path = safe_strdup (buf);
6760+ acache->index = hdr->index;
6761+ msg->fp = safe_fopen (acache->path, "w+");
6762+ if (!msg->fp)
6763+ {
6764+ mutt_perror (acache->path);
6765+ unlink (acache->path);
6766+ FREE (&acache->path);
6767+ return -1;
6768+ }
6769+ }
6770+
6771+ /* fetch message to cache file */
6772+ snprintf (buf, sizeof (buf), "ARTICLE %s\r\n",
6773+ NHDR (hdr)->article_num ? article : hdr->env->message_id);
6774+ rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), fetch_msg,
6775+ fetch_tempfile, msg->fp);
6776+ if (rc)
6777+ {
6778+ safe_fclose (&msg->fp);
6779+ if (acache->path)
6780+ {
6781+ unlink (acache->path);
6782+ FREE (&acache->path);
6783+ }
6784+ if (rc > 0)
6785+ {
6786+ if (!mutt_strncmp (NHDR (hdr)->article_num ? "423" : "430", buf, 3))
6787+ mutt_error (_("Article %d not found on the server."),
6788+ NHDR (hdr)->article_num ? article : hdr->env->message_id);
6789+ else
6790+ mutt_error ("ARTICLE: %s", buf);
6791+ }
6792+ return -1;
6793+ }
6794+
6795+ if (!acache->path)
6796+ mutt_bcache_commit (nntp_data->bcache, article);
6797+ }
6798+
6799+ /* replace envelope with new one
6800+ * hash elements must be updated because pointers will be changed */
6801+ if (ctx->id_hash && hdr->env->message_id)
6802+ hash_delete (ctx->id_hash, hdr->env->message_id, hdr, NULL);
6803+ if (ctx->subj_hash && hdr->env->real_subj)
6804+ hash_delete (ctx->subj_hash, hdr->env->real_subj, hdr, NULL);
6805+
6806+ mutt_free_envelope (&hdr->env);
6807+ hdr->env = mutt_read_rfc822_header (msg->fp, hdr, 0, 0);
6808+
6809+ if (ctx->id_hash && hdr->env->message_id)
6810+ hash_insert (ctx->id_hash, hdr->env->message_id, hdr);
6811+ if (ctx->subj_hash && hdr->env->real_subj)
6812+ hash_insert (ctx->subj_hash, hdr->env->real_subj, hdr);
6813+
6814+ /* fix content length */
6815+ fseek (msg->fp, 0, SEEK_END);
6816+ hdr->content->length = ftell (msg->fp) - hdr->content->offset;
6817+
6818+ /* this is called in mutt before the open which fetches the message,
6819+ * which is probably wrong, but we just call it again here to handle
6820+ * the problem instead of fixing it */
6821+ NHDR (hdr)->parsed = 1;
6822+ mutt_parse_mime_message (ctx, hdr);
6823+
6824+ /* these would normally be updated in mx_update_context(), but the
6825+ * full headers aren't parsed with overview, so the information wasn't
6826+ * available then */
6827+ if (WithCrypto)
6828+ hdr->security = crypt_query (hdr->content);
6829+
6830+ rewind (msg->fp);
6831+ mutt_clear_error();
6832+ return 0;
6833+}
6834+
6835+/* Close message */
6836+static int nntp_close_message (CONTEXT *ctx, MESSAGE *msg)
6837+{
6838+ return safe_fclose (&msg->fp);
6839+}
6840+
6841+/* Post article */
6842+int nntp_post (const char *msg) {
6843+ NNTP_DATA *nntp_data, nntp_tmp;
6844+ FILE *fp;
6845+ char buf[LONG_STRING];
6846+ size_t len;
6847+
6848+ if (Context && Context->magic == MUTT_NNTP)
6849+ nntp_data = Context->data;
6850+ else
6851+ {
6852+ CurrentNewsSrv = nntp_select_server (NewsServer, 0);
6853+ if (!CurrentNewsSrv)
6854+ return -1;
6855+
6856+ nntp_data = &nntp_tmp;
6857+ nntp_data->nserv = CurrentNewsSrv;
6858+ nntp_data->group = NULL;
6859+ }
6860+
6861+ fp = safe_fopen (msg, "r");
6862+ if (!fp)
6863+ {
6864+ mutt_perror (msg);
6865+ return -1;
6866+ }
6867+
6868+ strfcpy (buf, "POST\r\n", sizeof (buf));
6869+ if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
6870+ return -1;
6871+ if (buf[0] != '3')
6872+ {
6873+ mutt_error (_("Can't post article: %s"), buf);
6874+ return -1;
6875+ }
6876+
6877+ buf[0] = '.';
6878+ buf[1] = '\0';
6879+ while (fgets (buf + 1, sizeof (buf) - 2, fp))
6880+ {
6881+ len = strlen (buf);
6882+ if (buf[len - 1] == '\n')
6883+ {
6884+ buf[len - 1] = '\r';
6885+ buf[len] = '\n';
6886+ len++;
6887+ buf[len] = '\0';
6888+ }
6889+ if (mutt_socket_write_d (nntp_data->nserv->conn,
6890+ buf[1] == '.' ? buf : buf + 1, -1, MUTT_SOCK_LOG_HDR) < 0)
6891+ return nntp_connect_error (nntp_data->nserv);
6892+ }
6893+ fclose (fp);
6894+
6895+ if ((buf[strlen (buf) - 1] != '\n' &&
6896+ mutt_socket_write_d (nntp_data->nserv->conn, "\r\n", -1, MUTT_SOCK_LOG_HDR) < 0) ||
6897+ mutt_socket_write_d (nntp_data->nserv->conn, ".\r\n", -1, MUTT_SOCK_LOG_HDR) < 0 ||
6898+ mutt_socket_readln (buf, sizeof (buf), nntp_data->nserv->conn) < 0)
6899+ return nntp_connect_error (nntp_data->nserv);
6900+ if (buf[0] != '2')
6901+ {
6902+ mutt_error (_("Can't post article: %s"), buf);
6903+ return -1;
6904+ }
6905+ return 0;
6906+}
6907+
6908+/* Free up memory associated with the newsgroup context */
6909+static int nntp_close_mailbox (CONTEXT *ctx)
6910+{
6911+ NNTP_DATA *nntp_data = ctx->data, *nntp_tmp;
6912+
6913+ if (!nntp_data)
6914+ return 0;
6915+
6916+ nntp_data->unread = ctx->unread;
6917+
6918+ nntp_acache_free (nntp_data);
6919+ if (!nntp_data->nserv || !nntp_data->nserv->groups_hash || !nntp_data->group)
6920+ return 0;
6921+
6922+ nntp_tmp = hash_find (nntp_data->nserv->groups_hash, nntp_data->group);
6923+ if (nntp_tmp == NULL || nntp_tmp != nntp_data)
6924+ nntp_data_free (nntp_data);
6925+ return 0;
6926+}
6927+
6928+/* Get date and time from server */
6929+int nntp_date (NNTP_SERVER *nserv, time_t *now)
6930+{
6931+ if (nserv->hasDATE)
6932+ {
6933+ NNTP_DATA nntp_data;
6934+ char buf[LONG_STRING];
6935+ struct tm tm;
6936+
6937+ nntp_data.nserv = nserv;
6938+ nntp_data.group = NULL;
6939+ strfcpy (buf, "DATE\r\n", sizeof (buf));
6940+ if (nntp_query (&nntp_data, buf, sizeof (buf)) < 0)
6941+ return -1;
6942+
6943+ if (sscanf (buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
6944+ &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
6945+ {
6946+ tm.tm_year -= 1900;
6947+ tm.tm_mon--;
6948+ *now = timegm (&tm);
6949+ if (*now >= 0)
6950+ {
6951+ dprint (1, (debugfile, "nntp_date: server time is %d\n", *now));
6952+ return 0;
6953+ }
6954+ }
6955+ }
6956+ time (now);
6957+ return 0;
6958+}
6959+
6960+/* Fetch list of all newsgroups from server */
6961+int nntp_active_fetch (NNTP_SERVER *nserv, unsigned int new)
6962+{
6963+ NNTP_DATA nntp_data;
6964+ char msg[SHORT_STRING];
6965+ char buf[LONG_STRING];
6966+ unsigned int i;
6967+ int rc;
6968+
6969+ snprintf (msg, sizeof (msg), _("Loading list of groups from server %s..."),
6970+ nserv->conn->account.host);
6971+ mutt_message (msg);
6972+ if (nntp_date (nserv, &nserv->newgroups_time) < 0)
6973+ return -1;
6974+
6975+ nntp_data.nserv = nserv;
6976+ nntp_data.group = NULL;
6977+ i = nserv->groups_num;
6978+ strfcpy (buf, "LIST\r\n", sizeof (buf));
6979+ rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
6980+ nntp_add_group, nserv);
6981+ if (rc)
6982+ {
6983+ if (rc > 0)
6984+ {
6985+ mutt_error ("LIST: %s", buf);
6986+ mutt_sleep (2);
6987+ }
6988+ return -1;
6989+ }
6990+
6991+ if (new)
6992+ {
6993+ for (; i < nserv->groups_num; i++)
6994+ {
6995+ NNTP_DATA *nntp_data = nserv->groups_list[i];
6996+ nntp_data->new = 1;
6997+ }
6998+ }
6999+
7000+ for (i = 0; i < nserv->groups_num; i++)
7001+ {
7002+ NNTP_DATA *nntp_data = nserv->groups_list[i];
7003+
7004+ if (nntp_data && nntp_data->deleted && !nntp_data->newsrc_ent)
7005+ {
7006+ nntp_delete_group_cache (nntp_data);
7007+ hash_delete (nserv->groups_hash, nntp_data->group, NULL, nntp_data_free);
7008+ nserv->groups_list[i] = NULL;
7009+ }
7010+ }
7011+
7012+ if (option (OPTLOADDESC))
7013+ rc = get_description (&nntp_data, "*", _("Loading descriptions..."));
7014+
7015+ nntp_active_save_cache (nserv);
7016+ if (rc < 0)
7017+ return -1;
7018+ mutt_clear_error ();
7019+ return 0;
7020+}
7021+
7022+/* Check newsgroup for new articles:
7023+ * 1 - new articles found
7024+ * 0 - no change
7025+ * -1 - lost connection */
7026+static int nntp_group_poll (NNTP_DATA *nntp_data, int update_stat)
7027+{
7028+ char buf[LONG_STRING] = "";
7029+ anum_t count, first, last;
7030+
7031+ /* use GROUP command to poll newsgroup */
7032+ if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
7033+ return -1;
7034+ if (sscanf (buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
7035+ return 0;
7036+ if (first == nntp_data->firstMessage && last == nntp_data->lastMessage)
7037+ return 0;
7038+
7039+ /* articles have been renumbered */
7040+ if (last < nntp_data->lastMessage)
7041+ {
7042+ nntp_data->lastCached = 0;
7043+ if (nntp_data->newsrc_len)
7044+ {
7045+ safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
7046+ nntp_data->newsrc_len = 1;
7047+ nntp_data->newsrc_ent[0].first = 1;
7048+ nntp_data->newsrc_ent[0].last = 0;
7049+ }
7050+ }
7051+ nntp_data->firstMessage = first;
7052+ nntp_data->lastMessage = last;
7053+ if (!update_stat)
7054+ return 1;
7055+
7056+ /* update counters */
7057+ else if (!last || (!nntp_data->newsrc_ent && !nntp_data->lastCached))
7058+ nntp_data->unread = count;
7059+ else
7060+ nntp_group_unread_stat (nntp_data);
7061+ return 1;
7062+}
7063+
7064+/* Check current newsgroup for new articles, leave newsrc locked:
7065+ * MUTT_REOPENED - articles have been renumbered or removed from server
7066+ * MUTT_NEW_MAIL - new articles found
7067+ * 0 - no change
7068+ * -1 - lost connection */
7069+static int check_mailbox (CONTEXT *ctx)
7070+{
7071+ NNTP_DATA *nntp_data = ctx->data;
7072+ NNTP_SERVER *nserv = nntp_data->nserv;
7073+ time_t now = time (NULL);
7074+ int i, j;
7075+ int rc, ret = 0;
7076+ void *hc = NULL;
7077+
7078+ if (nserv->check_time + NewsPollTimeout > now)
7079+ return 0;
7080+
7081+ mutt_message _("Checking for new messages...");
7082+ if (nntp_newsrc_parse (nserv) < 0)
7083+ return -1;
7084+
7085+ nserv->check_time = now;
7086+ rc = nntp_group_poll (nntp_data, 0);
7087+ if (rc < 0)
7088+ {
7089+ nntp_newsrc_close (nserv);
7090+ return -1;
7091+ }
7092+ if (rc)
7093+ nntp_active_save_cache (nserv);
7094+
7095+ /* articles have been renumbered, remove all headers */
7096+ if (nntp_data->lastMessage < nntp_data->lastLoaded)
7097+ {
7098+ for (i = 0; i < ctx->msgcount; i++)
7099+ mutt_free_header (&ctx->hdrs[i]);
7100+ ctx->msgcount = 0;
7101+ ctx->tagged = 0;
7102+
7103+ if (nntp_data->lastMessage < nntp_data->lastLoaded)
7104+ {
7105+ nntp_data->lastLoaded = nntp_data->firstMessage - 1;
7106+ if (NntpContext && nntp_data->lastMessage - nntp_data->lastLoaded >
7107+ NntpContext)
7108+ nntp_data->lastLoaded = nntp_data->lastMessage - NntpContext;
7109+ }
7110+ ret = MUTT_REOPENED;
7111+ }
7112+
7113+ /* .newsrc has been externally modified */
7114+ if (nserv->newsrc_modified)
7115+ {
7116+ anum_t anum;
7117+#ifdef USE_HCACHE
7118+ unsigned char *messages;
7119+ char buf[16];
7120+ void *hdata;
7121+ HEADER *hdr;
7122+ anum_t first = nntp_data->firstMessage;
7123+
7124+ if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext)
7125+ first = nntp_data->lastMessage - NntpContext + 1;
7126+ messages = safe_calloc (nntp_data->lastLoaded - first + 1,
7127+ sizeof (unsigned char));
7128+ hc = nntp_hcache_open (nntp_data);
7129+ nntp_hcache_update (nntp_data, hc);
7130+#endif
7131+
7132+ /* update flags according to .newsrc */
7133+ for (i = j = 0; i < ctx->msgcount; i++)
7134+ {
7135+ int flagged = 0;
7136+ anum = NHDR (ctx->hdrs[i])->article_num;
7137+
7138+#ifdef USE_HCACHE
7139+ /* check hcache for flagged and deleted flags */
7140+ if (hc)
7141+ {
7142+ if (anum >= first && anum <= nntp_data->lastLoaded)
7143+ messages[anum - first] = 1;
7144+
7145+ snprintf (buf, sizeof (buf), "%d", anum);
7146+ hdata = mutt_hcache_fetch (hc, buf, strlen);
7147+ if (hdata)
7148+ {
7149+ int deleted;
7150+
7151+ dprint (2, (debugfile,
7152+ "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf));
7153+ hdr = mutt_hcache_restore (hdata, NULL);
7154+ mutt_hcache_free (&hdata);
7155+ hdr->data = 0;
7156+ deleted = hdr->deleted;
7157+ flagged = hdr->flagged;
7158+ mutt_free_header (&hdr);
7159+
7160+ /* header marked as deleted, removing from context */
7161+ if (deleted)
7162+ {
7163+ mutt_set_flag (ctx, ctx->hdrs[i], MUTT_TAG, 0);
7164+ mutt_free_header (&ctx->hdrs[i]);
7165+ continue;
7166+ }
7167+ }
7168+ }
7169+#endif
7170+
7171+ if (!ctx->hdrs[i]->changed)
7172+ {
7173+ ctx->hdrs[i]->flagged = flagged;
7174+ ctx->hdrs[i]->read = 0;
7175+ ctx->hdrs[i]->old = 0;
7176+ nntp_article_status (ctx, ctx->hdrs[i], NULL, anum);
7177+ if (!ctx->hdrs[i]->read)
7178+ nntp_parse_xref (ctx, ctx->hdrs[i]);
7179+ }
7180+ ctx->hdrs[j++] = ctx->hdrs[i];
7181+ }
7182+
7183+#ifdef USE_HCACHE
7184+ ctx->msgcount = j;
7185+
7186+ /* restore headers without "deleted" flag */
7187+ for (anum = first; anum <= nntp_data->lastLoaded; anum++)
7188+ {
7189+ if (messages[anum - first])
7190+ continue;
7191+
7192+ snprintf (buf, sizeof (buf), "%d", anum);
7193+ hdata = mutt_hcache_fetch (hc, buf, strlen);
7194+ if (hdata)
7195+ {
7196+ dprint (2, (debugfile,
7197+ "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf));
7198+ if (ctx->msgcount >= ctx->hdrmax)
7199+ mx_alloc_memory (ctx);
7200+
7201+ ctx->hdrs[ctx->msgcount] =
7202+ hdr = mutt_hcache_restore (hdata, NULL);
7203+ mutt_hcache_free (&hdata);
7204+ hdr->data = 0;
7205+ if (hdr->deleted)
7206+ {
7207+ mutt_free_header (&hdr);
7208+ if (nntp_data->bcache)
7209+ {
7210+ dprint (2, (debugfile,
7211+ "nntp_check_mailbox: mutt_bcache_del %s\n", buf));
7212+ mutt_bcache_del (nntp_data->bcache, buf);
7213+ }
7214+ continue;
7215+ }
7216+
7217+ ctx->msgcount++;
7218+ hdr->read = 0;
7219+ hdr->old = 0;
7220+ hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
7221+ NHDR (hdr)->article_num = anum;
7222+ nntp_article_status (ctx, hdr, NULL, anum);
7223+ if (!hdr->read)
7224+ nntp_parse_xref (ctx, hdr);
7225+ }
7226+ }
7227+ FREE (&messages);
7228+#endif
7229+
7230+ nserv->newsrc_modified = 0;
7231+ ret = MUTT_REOPENED;
7232+ }
7233+
7234+ /* some headers were removed, context must be updated */
7235+ if (ret == MUTT_REOPENED)
7236+ {
7237+ if (ctx->subj_hash)
7238+ hash_destroy (&ctx->subj_hash, NULL);
7239+ if (ctx->id_hash)
7240+ hash_destroy (&ctx->id_hash, NULL);
7241+ mutt_clear_threads (ctx);
7242+
7243+ ctx->vcount = 0;
7244+ ctx->deleted = 0;
7245+ ctx->new = 0;
7246+ ctx->unread = 0;
7247+ ctx->flagged = 0;
7248+ ctx->changed = 0;
7249+ ctx->id_hash = NULL;
7250+ ctx->subj_hash = NULL;
7251+ mx_update_context (ctx, ctx->msgcount);
7252+ }
7253+
7254+ /* fetch headers of new articles */
7255+ if (nntp_data->lastMessage > nntp_data->lastLoaded)
7256+ {
7257+ int oldmsgcount = ctx->msgcount;
7258+ int quiet = ctx->quiet;
7259+ ctx->quiet = 1;
7260+#ifdef USE_HCACHE
7261+ if (!hc)
7262+ {
7263+ hc = nntp_hcache_open (nntp_data);
7264+ nntp_hcache_update (nntp_data, hc);
7265+ }
7266+#endif
7267+ rc = nntp_fetch_headers (ctx, hc, nntp_data->lastLoaded + 1,
7268+ nntp_data->lastMessage, 0);
7269+ ctx->quiet = quiet;
7270+ if (rc >= 0)
7271+ nntp_data->lastLoaded = nntp_data->lastMessage;
7272+ if (ret == 0 && ctx->msgcount > oldmsgcount)
7273+ ret = MUTT_NEW_MAIL;
7274+ }
7275+
7276+#ifdef USE_HCACHE
7277+ mutt_hcache_close (hc);
7278+#endif
7279+ if (ret)
7280+ nntp_newsrc_close (nserv);
7281+ mutt_clear_error ();
7282+ return ret;
7283+}
7284+
7285+/* Check current newsgroup for new articles:
7286+ * MUTT_REOPENED - articles have been renumbered or removed from server
7287+ * MUTT_NEW_MAIL - new articles found
7288+ * 0 - no change
7289+ * -1 - lost connection */
7290+static int nntp_check_mailbox (CONTEXT *ctx, int *index_hint)
7291+{
7292+ int ret = check_mailbox (ctx);
7293+ if (ret == 0)
7294+ {
7295+ NNTP_DATA *nntp_data = ctx->data;
7296+ NNTP_SERVER *nserv = nntp_data->nserv;
7297+ nntp_newsrc_close (nserv);
7298+ }
7299+ return ret;
7300+}
7301+
7302+/* Save changes to .newsrc and cache */
7303+static int nntp_sync_mailbox (CONTEXT *ctx, int *index_hint)
7304+{
7305+ NNTP_DATA *nntp_data = ctx->data;
7306+ int rc, i;
7307+#ifdef USE_HCACHE
7308+ header_cache_t *hc;
7309+#endif
7310+
7311+ /* check for new articles */
7312+ nntp_data->nserv->check_time = 0;
7313+ rc = check_mailbox (ctx);
7314+ if (rc)
7315+ return rc;
7316+
7317+#ifdef USE_HCACHE
7318+ nntp_data->lastCached = 0;
7319+ hc = nntp_hcache_open (nntp_data);
7320+#endif
7321+
7322+ for (i = 0; i < ctx->msgcount; i++)
7323+ {
7324+ HEADER *hdr = ctx->hdrs[i];
7325+ char buf[16];
7326+
7327+ snprintf (buf, sizeof (buf), "%d", NHDR (hdr)->article_num);
7328+ if (nntp_data->bcache && hdr->deleted)
7329+ {
7330+ dprint (2, (debugfile, "nntp_sync_mailbox: mutt_bcache_del %s\n", buf));
7331+ mutt_bcache_del (nntp_data->bcache, buf);
7332+ }
7333+
7334+#ifdef USE_HCACHE
7335+ if (hc && (hdr->changed || hdr->deleted))
7336+ {
7337+ dprint (2, (debugfile, "nntp_sync_mailbox: mutt_hcache_store %s\n", buf));
7338+ mutt_hcache_store (hc, buf, hdr, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
7339+ }
7340+#endif
7341+ }
7342+
7343+#ifdef USE_HCACHE
7344+ if (hc)
7345+ {
7346+ mutt_hcache_close (hc);
7347+ nntp_data->lastCached = nntp_data->lastLoaded;
7348+ }
7349+#endif
7350+
7351+ /* save .newsrc entries */
7352+ nntp_newsrc_gen_entries (ctx);
7353+ nntp_newsrc_update (nntp_data->nserv);
7354+ nntp_newsrc_close (nntp_data->nserv);
7355+ return 0;
7356+}
7357+
7358+/* Check for new groups and new articles in subscribed groups:
7359+ * 1 - new groups found
7360+ * 0 - no new groups
7361+ * -1 - error */
7362+int nntp_check_new_groups (NNTP_SERVER *nserv)
7363+{
7364+ NNTP_DATA nntp_data;
7365+ time_t now;
7366+ struct tm *tm;
7367+ char buf[LONG_STRING];
7368+ char *msg = _("Checking for new newsgroups...");
7369+ unsigned int i;
7370+ int rc, update_active = FALSE;
7371+
7372+ if (!nserv || !nserv->newgroups_time)
7373+ return -1;
7374+
7375+ /* check subscribed newsgroups for new articles */
7376+ if (option (OPTSHOWNEWNEWS))
7377+ {
7378+ mutt_message _("Checking for new messages...");
7379+ for (i = 0; i < nserv->groups_num; i++)
7380+ {
7381+ NNTP_DATA *nntp_data = nserv->groups_list[i];
7382+
7383+ if (nntp_data && nntp_data->subscribed)
7384+ {
7385+ rc = nntp_group_poll (nntp_data, 1);
7386+ if (rc < 0)
7387+ return -1;
7388+ if (rc > 0)
7389+ update_active = TRUE;
7390+ }
7391+ }
7392+ /* select current newsgroup */
7393+ if (Context && Context->magic == MUTT_NNTP)
7394+ {
7395+ buf[0] = '\0';
7396+ if (nntp_query ((NNTP_DATA *)Context->data, buf, sizeof (buf)) < 0)
7397+ return -1;
7398+ }
7399+ }
7400+ else if (nserv->newgroups_time)
7401+ return 0;
7402+
7403+ /* get list of new groups */
7404+ mutt_message (msg);
7405+ if (nntp_date (nserv, &now) < 0)
7406+ return -1;
7407+ nntp_data.nserv = nserv;
7408+ if (Context && Context->magic == MUTT_NNTP)
7409+ nntp_data.group = ((NNTP_DATA *)Context->data)->group;
7410+ else
7411+ nntp_data.group = NULL;
7412+ i = nserv->groups_num;
7413+ tm = gmtime (&nserv->newgroups_time);
7414+ snprintf (buf, sizeof (buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n",
7415+ tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
7416+ tm->tm_hour, tm->tm_min, tm->tm_sec);
7417+ rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
7418+ nntp_add_group, nserv);
7419+ if (rc)
7420+ {
7421+ if (rc > 0)
7422+ {
7423+ mutt_error ("NEWGROUPS: %s", buf);
7424+ mutt_sleep (2);
7425+ }
7426+ return -1;
7427+ }
7428+
7429+ /* new groups found */
7430+ rc = 0;
7431+ if (nserv->groups_num != i)
7432+ {
7433+ int groups_num = i;
7434+
7435+ nserv->newgroups_time = now;
7436+ for (; i < nserv->groups_num; i++)
7437+ {
7438+ NNTP_DATA *nntp_data = nserv->groups_list[i];
7439+ nntp_data->new = 1;
7440+ }
7441+
7442+ /* loading descriptions */
7443+ if (option (OPTLOADDESC))
7444+ {
7445+ unsigned int count = 0;
7446+ progress_t progress;
7447+
7448+ mutt_progress_init (&progress, _("Loading descriptions..."),
7449+ MUTT_PROGRESS_MSG, ReadInc, nserv->groups_num - i);
7450+ for (i = groups_num; i < nserv->groups_num; i++)
7451+ {
7452+ NNTP_DATA *nntp_data = nserv->groups_list[i];
7453+
7454+ if (get_description (nntp_data, NULL, NULL) < 0)
7455+ return -1;
7456+ mutt_progress_update (&progress, ++count, -1);
7457+ }
7458+ }
7459+ update_active = TRUE;
7460+ rc = 1;
7461+ }
7462+ if (update_active)
7463+ nntp_active_save_cache (nserv);
7464+ mutt_clear_error ();
7465+ return rc;
7466+}
7467+
7468+/* Fetch article by Message-ID:
7469+ * 0 - success
7470+ * 1 - no such article
7471+ * -1 - error */
7472+int nntp_check_msgid (CONTEXT *ctx, const char *msgid)
7473+{
7474+ NNTP_DATA *nntp_data = ctx->data;
7475+ HEADER *hdr;
7476+ FILE *fp;
7477+ char tempfile[_POSIX_PATH_MAX];
7478+ char buf[LONG_STRING];
7479+ int rc;
7480+
7481+ mutt_mktemp (tempfile, sizeof (tempfile));
7482+ fp = safe_fopen (tempfile, "w+");
7483+ if (!fp)
7484+ {
7485+ mutt_perror (tempfile);
7486+ unlink (tempfile);
7487+ return -1;
7488+ }
7489+
7490+ snprintf (buf, sizeof (buf), "HEAD %s\r\n", msgid);
7491+ rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
7492+ fetch_tempfile, fp);
7493+ if (rc)
7494+ {
7495+ fclose (fp);
7496+ unlink (tempfile);
7497+ if (rc < 0)
7498+ return -1;
7499+ if (!mutt_strncmp ("430", buf, 3))
7500+ return 1;
7501+ mutt_error ("HEAD: %s", buf);
7502+ return -1;
7503+ }
7504+
7505+ /* parse header */
7506+ if (ctx->msgcount == ctx->hdrmax)
7507+ mx_alloc_memory (ctx);
7508+ hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
7509+ hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
7510+ hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
7511+ fclose (fp);
7512+ unlink (tempfile);
7513+
7514+ /* get article number */
7515+ if (hdr->env->xref)
7516+ nntp_parse_xref (ctx, hdr);
7517+ else
7518+ {
7519+ snprintf (buf, sizeof (buf), "STAT %s\r\n", msgid);
7520+ if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
7521+ {
7522+ mutt_free_header (&hdr);
7523+ return -1;
7524+ }
7525+ sscanf (buf + 4, ANUM, &NHDR (hdr)->article_num);
7526+ }
7527+
7528+ /* reset flags */
7529+ hdr->read = 0;
7530+ hdr->old = 0;
7531+ hdr->deleted = 0;
7532+ hdr->changed = 1;
7533+ hdr->received = hdr->date_sent;
7534+ hdr->index = ctx->msgcount++;
7535+ mx_update_context (ctx, 1);
7536+ return 0;
7537+}
7538+
7539+typedef struct
7540+{
7541+ CONTEXT *ctx;
7542+ unsigned int num;
7543+ unsigned int max;
7544+ anum_t *child;
7545+} CHILD_CTX;
7546+
7547+/* Parse XPAT line */
7548+static int fetch_children (char *line, void *data)
7549+{
7550+ CHILD_CTX *cc = data;
7551+ anum_t anum;
7552+ unsigned int i;
7553+
7554+ if (!line || sscanf (line, ANUM, &anum) != 1)
7555+ return 0;
7556+ for (i = 0; i < cc->ctx->msgcount; i++)
7557+ if (NHDR (cc->ctx->hdrs[i])->article_num == anum)
7558+ return 0;
7559+ if (cc->num >= cc->max)
7560+ {
7561+ cc->max *= 2;
7562+ safe_realloc (&cc->child, sizeof (anum_t) * cc->max);
7563+ }
7564+ cc->child[cc->num++] = anum;
7565+ return 0;
7566+}
7567+
7568+/* Fetch children of article with the Message-ID */
7569+int nntp_check_children (CONTEXT *ctx, const char *msgid)
7570+{
7571+ NNTP_DATA *nntp_data = ctx->data;
7572+ CHILD_CTX cc;
7573+ char buf[STRING];
7574+ int i, rc, quiet;
7575+ void *hc = NULL;
7576+
7577+ if (!nntp_data || !nntp_data->nserv)
7578+ return -1;
7579+ if (nntp_data->firstMessage > nntp_data->lastLoaded)
7580+ return 0;
7581+
7582+ /* init context */
7583+ cc.ctx = ctx;
7584+ cc.num = 0;
7585+ cc.max = 10;
7586+ cc.child = safe_malloc (sizeof (anum_t) * cc.max);
7587+
7588+ /* fetch numbers of child messages */
7589+ snprintf (buf, sizeof (buf), "XPAT References %d-%d *%s*\r\n",
7590+ nntp_data->firstMessage, nntp_data->lastLoaded, msgid);
7591+ rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
7592+ fetch_children, &cc);
7593+ if (rc)
7594+ {
7595+ FREE (&cc.child);
7596+ if (rc > 0) {
7597+ if (mutt_strncmp ("500", buf, 3))
7598+ mutt_error ("XPAT: %s", buf);
7599+ else
7600+ mutt_error _("Unable to find child articles because server does not support XPAT command.");
7601+ }
7602+ return -1;
7603+ }
7604+
7605+ /* fetch all found messages */
7606+ quiet = ctx->quiet;
7607+ ctx->quiet = 1;
7608+#ifdef USE_HCACHE
7609+ hc = nntp_hcache_open (nntp_data);
7610+#endif
7611+ for (i = 0; i < cc.num; i++)
7612+ {
7613+ rc = nntp_fetch_headers (ctx, hc, cc.child[i], cc.child[i], 1);
7614+ if (rc < 0)
7615+ break;
7616+ }
7617+#ifdef USE_HCACHE
7618+ mutt_hcache_close (hc);
7619+#endif
7620+ ctx->quiet = quiet;
7621+ FREE (&cc.child);
7622+ return rc < 0 ? -1 : 0;
7623+}
7624+
7625+struct mx_ops mx_nntp_ops = {
7626+ .open = nntp_open_mailbox,
7627+ .open_append = NULL,
7628+ .close = nntp_close_mailbox,
7629+ .open_msg = nntp_open_message,
7630+ .close_msg = nntp_close_message,
7631+ .check = nntp_check_mailbox,
7632+ .commit_msg = NULL,
7633+ .open_new_msg = NULL,
7634+ .sync = nntp_sync_mailbox,
7635+};
7636diff -udprP mutt-1.12.1.orig/nntp.h mutt-1.12.1/nntp.h
7637--- mutt-1.12.1.orig/nntp.h 1970-01-01 03:00:00.000000000 +0300
7638+++ mutt-1.12.1/nntp.h 2019-08-11 20:31:26.142940552 +0300
7639@@ -0,0 +1,165 @@
7640+/*
7641+ * Copyright (C) 1998 Brandon Long <blong@fiction.net>
7642+ * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
7643+ * Copyright (C) 2000-2019 Vsevolod Volkov <vvv@mutt.org.ua>
7644+ *
7645+ * This program is free software; you can redistribute it and/or modify
7646+ * it under the terms of the GNU General Public License as published by
7647+ * the Free Software Foundation; either version 2 of the License, or
7648+ * (at your option) any later version.
7649+ *
7650+ * This program is distributed in the hope that it will be useful,
7651+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
7652+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7653+ * GNU General Public License for more details.
7654+ *
7655+ * You should have received a copy of the GNU General Public License
7656+ * along with this program; if not, write to the Free Software
7657+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
7658+ */
7659+
7660+#ifndef _NNTP_H_
7661+#define _NNTP_H_ 1
7662+
7663+#include "mutt_socket.h"
7664+#include "mailbox.h"
7665+#include "bcache.h"
7666+
7667+#if USE_HCACHE
7668+#include "hcache.h"
7669+#endif
7670+
7671+#include <time.h>
7672+#include <sys/types.h>
7673+#include <stdint.h>
7674+
7675+#define NNTP_PORT 119
7676+#define NNTP_SSL_PORT 563
7677+
7678+/* number of entries in article cache */
7679+#define NNTP_ACACHE_LEN 10
7680+
7681+/* article number type and format */
7682+#define anum_t uint32_t
7683+#define ANUM "%u"
7684+
7685+enum
7686+{
7687+ NNTP_NONE = 0,
7688+ NNTP_OK,
7689+ NNTP_BYE
7690+};
7691+
7692+typedef struct
7693+{
7694+ unsigned int hasCAPABILITIES : 1;
7695+ unsigned int hasSTARTTLS : 1;
7696+ unsigned int hasDATE : 1;
7697+ unsigned int hasLIST_NEWSGROUPS : 1;
7698+ unsigned int hasXGTITLE : 1;
7699+ unsigned int hasLISTGROUP : 1;
7700+ unsigned int hasLISTGROUPrange : 1;
7701+ unsigned int hasOVER : 1;
7702+ unsigned int hasXOVER : 1;
7703+ unsigned int use_tls : 3;
7704+ unsigned int status : 3;
7705+ unsigned int cacheable : 1;
7706+ unsigned int newsrc_modified : 1;
7707+ FILE *newsrc_fp;
7708+ char *newsrc_file;
7709+ char *authenticators;
7710+ char *overview_fmt;
7711+ off_t size;
7712+ time_t mtime;
7713+ time_t newgroups_time;
7714+ time_t check_time;
7715+ unsigned int groups_num;
7716+ unsigned int groups_max;
7717+ void **groups_list;
7718+ HASH *groups_hash;
7719+ CONNECTION *conn;
7720+} NNTP_SERVER;
7721+
7722+typedef struct
7723+{
7724+ anum_t first;
7725+ anum_t last;
7726+} NEWSRC_ENTRY;
7727+
7728+typedef struct
7729+{
7730+ unsigned int index;
7731+ char *path;
7732+} NNTP_ACACHE;
7733+
7734+typedef struct
7735+{
7736+ char *group;
7737+ char *desc;
7738+ anum_t firstMessage;
7739+ anum_t lastMessage;
7740+ anum_t lastLoaded;
7741+ anum_t lastCached;
7742+ anum_t unread;
7743+ unsigned int subscribed : 1;
7744+ unsigned int new : 1;
7745+ unsigned int allowed : 1;
7746+ unsigned int deleted : 1;
7747+ unsigned int newsrc_len;
7748+ NEWSRC_ENTRY *newsrc_ent;
7749+ NNTP_SERVER *nserv;
7750+ NNTP_ACACHE acache[NNTP_ACACHE_LEN];
7751+ body_cache_t *bcache;
7752+} NNTP_DATA;
7753+
7754+typedef struct
7755+{
7756+ anum_t article_num;
7757+ unsigned int parsed : 1;
7758+} NNTP_HEADER_DATA;
7759+
7760+#define NHDR(hdr) ((NNTP_HEADER_DATA*)((hdr)->data))
7761+
7762+/* internal functions */
7763+int nntp_add_group (char *, void *);
7764+int nntp_active_save_cache (NNTP_SERVER *);
7765+int nntp_check_new_groups (NNTP_SERVER *);
7766+int nntp_open_connection (NNTP_SERVER *);
7767+void nntp_newsrc_gen_entries (CONTEXT *);
7768+void nntp_bcache_update (NNTP_DATA *);
7769+void nntp_article_status (CONTEXT *, HEADER *, char *, anum_t);
7770+void nntp_group_unread_stat (NNTP_DATA *);
7771+void nntp_data_free (void *);
7772+void nntp_acache_free (NNTP_DATA *);
7773+void nntp_delete_group_cache (NNTP_DATA *);
7774+
7775+/* exposed interface */
7776+NNTP_SERVER *nntp_select_server (char *, int);
7777+NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *, char *);
7778+NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *, char *);
7779+NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *, char *);
7780+NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *, char *);
7781+int nntp_active_fetch (NNTP_SERVER *, unsigned int);
7782+int nntp_newsrc_update (NNTP_SERVER *);
7783+int nntp_post (const char *);
7784+int nntp_check_msgid (CONTEXT *, const char *);
7785+int nntp_check_children (CONTEXT *, const char *);
7786+int nntp_newsrc_parse (NNTP_SERVER *);
7787+void nntp_newsrc_close (NNTP_SERVER *);
7788+void nntp_buffer_buffy (BUFFER *);
7789+void nntp_buffer_expand_path (BUFFER *, ACCOUNT *);
7790+void nntp_clear_cache (NNTP_SERVER *);
7791+const char *nntp_format_str (char *, size_t, size_t, int, char, const char *,
7792+ const char *, const char *, const char *,
7793+ unsigned long, format_flag);
7794+
7795+NNTP_SERVER *CurrentNewsSrv INITVAL (NULL);
7796+
7797+#ifdef USE_HCACHE
7798+header_cache_t *nntp_hcache_open (NNTP_DATA *);
7799+void nntp_hcache_update (NNTP_DATA *, header_cache_t *);
7800+#endif
7801+
7802+extern struct mx_ops mx_nntp_ops;
7803+
7804+#endif /* _NNTP_H_ */
7805diff -udprP mutt-1.12.1.orig/pager.c mutt-1.12.1/pager.c
7806--- mutt-1.12.1.orig/pager.c 2019-06-15 18:57:01.000000000 +0300
7807+++ mutt-1.12.1/pager.c 2019-08-11 20:31:26.143940536 +0300
7808@@ -1118,6 +1118,11 @@ fill_buffer (FILE *f, LOFF_T *last_pos,
7809 return b_read;
7810 }
7811
7812+#ifdef USE_NNTP
7813+#include "mx.h"
7814+#include "nntp.h"
7815+#endif
7816+
7817
7818 static int format_line (struct line_t **lineInfo, int n, unsigned char *buf,
7819 int flags, ansi_attr *pa, int cnt,
7820@@ -1583,6 +1588,15 @@ static const struct mapping_t PagerHelpE
7821 { N_("Next"), OP_MAIN_NEXT_UNDELETED },
7822 { NULL, 0 }
7823 };
7824+#ifdef USE_NNTP
7825+static struct mapping_t PagerNewsHelpExtra[] = {
7826+ { N_("Post"), OP_POST },
7827+ { N_("Followup"), OP_FOLLOWUP },
7828+ { N_("Del"), OP_DELETE },
7829+ { N_("Next"), OP_MAIN_NEXT_UNDELETED },
7830+ { NULL, 0 }
7831+};
7832+#endif
7833
7834 void mutt_clear_pager_position (void)
7835 {
7836@@ -1921,6 +1935,10 @@ mutt_pager (const char *banner, const ch
7837
7838 pager_redraw_data_t rd;
7839
7840+#ifdef USE_NNTP
7841+ char *followup_to;
7842+#endif
7843+
7844 if (!(flags & MUTT_SHOWCOLOR))
7845 flags |= MUTT_SHOWFLAT;
7846
7847@@ -1970,7 +1988,11 @@ mutt_pager (const char *banner, const ch
7848 mutt_buffer_strcpy (helpstr, buffer);
7849 if (IsHeader (extra))
7850 {
7851- mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER, PagerHelpExtra);
7852+ mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER,
7853+#ifdef USE_NNTP
7854+ (Context && (Context->magic == MUTT_NNTP)) ? PagerNewsHelpExtra :
7855+#endif
7856+ PagerHelpExtra);
7857 mutt_buffer_addch (helpstr, ' ');
7858 mutt_buffer_addstr (helpstr, buffer);
7859 }
7860@@ -2653,6 +2675,60 @@ search_next:
7861 pager_menu->redraw = REDRAW_FULL;
7862 break;
7863
7864+#ifdef USE_NNTP
7865+ case OP_POST:
7866+ CHECK_MODE(IsHeader (extra) && !IsAttach (extra));
7867+ CHECK_ATTACH;
7868+ if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
7869+ !((NNTP_DATA *)extra->ctx->data)->allowed &&
7870+ query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
7871+ break;
7872+ ci_send_message (SENDNEWS, NULL, NULL, extra->ctx, NULL);
7873+ pager_menu->redraw = REDRAW_FULL;
7874+ break;
7875+
7876+ case OP_FORWARD_TO_GROUP:
7877+ CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
7878+ CHECK_ATTACH;
7879+ if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
7880+ !((NNTP_DATA *)extra->ctx->data)->allowed &&
7881+ query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
7882+ break;
7883+ if (IsMsgAttach (extra))
7884+ mutt_attach_forward (extra->fp, extra->hdr, extra->actx,
7885+ extra->bdy, SENDNEWS);
7886+ else
7887+ ci_send_message (SENDNEWS|SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
7888+ pager_menu->redraw = REDRAW_FULL;
7889+ break;
7890+
7891+ case OP_FOLLOWUP:
7892+ CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
7893+ CHECK_ATTACH;
7894+
7895+ if (IsMsgAttach (extra))
7896+ followup_to = extra->bdy->hdr->env->followup_to;
7897+ else
7898+ followup_to = extra->hdr->env->followup_to;
7899+
7900+ if (!followup_to || mutt_strcasecmp (followup_to, "poster") ||
7901+ query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != MUTT_YES)
7902+ {
7903+ if (extra->ctx && extra->ctx->magic == MUTT_NNTP &&
7904+ !((NNTP_DATA *)extra->ctx->data)->allowed &&
7905+ query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
7906+ break;
7907+ if (IsMsgAttach (extra))
7908+ mutt_attach_reply (extra->fp, extra->hdr, extra->actx,
7909+ extra->bdy, SENDNEWS|SENDREPLY);
7910+ else
7911+ ci_send_message (SENDNEWS|SENDREPLY, NULL, NULL,
7912+ extra->ctx, extra->hdr);
7913+ pager_menu->redraw = REDRAW_FULL;
7914+ break;
7915+ }
7916+#endif
7917+
7918 case OP_REPLY:
7919 case OP_GROUP_REPLY:
7920 case OP_GROUP_CHAT_REPLY:
7921@@ -2689,7 +2765,7 @@ search_next:
7922 CHECK_ATTACH;
7923 if (IsMsgAttach (extra))
7924 mutt_attach_forward (extra->fp, extra->hdr, extra->actx,
7925- extra->bdy);
7926+ extra->bdy, 0);
7927 else
7928 ci_send_message (SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
7929 pager_menu->redraw = REDRAW_FULL;
7930diff -udprP mutt-1.12.1.orig/parse.c mutt-1.12.1/parse.c
7931--- mutt-1.12.1.orig/parse.c 2019-05-25 19:22:39.000000000 +0300
7932+++ mutt-1.12.1/parse.c 2019-08-11 20:31:26.143940536 +0300
7933@@ -94,7 +94,7 @@ char *mutt_read_rfc822_line (FILE *f, ch
7934 /* not reached */
7935 }
7936
7937-static LIST *mutt_parse_references (char *s, int in_reply_to)
7938+LIST *mutt_parse_references (char *s, int in_reply_to)
7939 {
7940 LIST *t, *lst = NULL;
7941 char *m;
7942@@ -1122,6 +1122,17 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
7943 e->from = rfc822_parse_adrlist (e->from, p);
7944 matched = 1;
7945 }
7946+#ifdef USE_NNTP
7947+ else if (!mutt_strcasecmp (line+1, "ollowup-to"))
7948+ {
7949+ if (!e->followup_to)
7950+ {
7951+ mutt_remove_trailing_ws (p);
7952+ e->followup_to = safe_strdup (mutt_skip_whitespace (p));
7953+ }
7954+ matched = 1;
7955+ }
7956+#endif
7957 break;
7958
7959 case 'i':
7960@@ -1206,6 +1217,27 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
7961 }
7962 break;
7963
7964+#ifdef USE_NNTP
7965+ case 'n':
7966+ if (!mutt_strcasecmp (line + 1, "ewsgroups"))
7967+ {
7968+ FREE (&e->newsgroups);
7969+ mutt_remove_trailing_ws (p);
7970+ e->newsgroups = safe_strdup (mutt_skip_whitespace (p));
7971+ matched = 1;
7972+ }
7973+ break;
7974+#endif
7975+
7976+ case 'o':
7977+ /* field `Organization:' saves only for pager! */
7978+ if (!mutt_strcasecmp (line + 1, "rganization"))
7979+ {
7980+ if (!e->organization && mutt_strcasecmp (p, "unknown"))
7981+ e->organization = safe_strdup (p);
7982+ }
7983+ break;
7984+
7985 case 'r':
7986 if (!ascii_strcasecmp (line + 1, "eferences"))
7987 {
7988@@ -1318,6 +1350,20 @@ int mutt_parse_rfc822_line (ENVELOPE *e,
7989 e->x_label = safe_strdup(p);
7990 matched = 1;
7991 }
7992+#ifdef USE_NNTP
7993+ else if (!mutt_strcasecmp (line + 1, "-comment-to"))
7994+ {
7995+ if (!e->x_comment_to)
7996+ e->x_comment_to = safe_strdup (p);
7997+ matched = 1;
7998+ }
7999+ else if (!mutt_strcasecmp (line + 1, "ref"))
8000+ {
8001+ if (!e->xref)
8002+ e->xref = safe_strdup (p);
8003+ matched = 1;
8004+ }
8005+#endif
8006
8007 default:
8008 break;
8009diff -udprP mutt-1.12.1.orig/pattern.c mutt-1.12.1/pattern.c
8010--- mutt-1.12.1.orig/pattern.c 2019-05-25 19:22:39.000000000 +0300
8011+++ mutt-1.12.1/pattern.c 2019-08-11 20:31:26.143940536 +0300
8012@@ -94,6 +94,9 @@ Flags[] =
8013 { 'U', MUTT_UNREAD, 0, NULL },
8014 { 'v', MUTT_COLLAPSED, 0, NULL },
8015 { 'V', MUTT_CRYPT_VERIFIED, 0, NULL },
8016+#ifdef USE_NNTP
8017+ { 'w', MUTT_NEWSGROUPS, 0, eat_regexp },
8018+#endif
8019 { 'x', MUTT_REFERENCE, 0, eat_regexp },
8020 { 'X', MUTT_MIMEATTACH, 0, eat_range },
8021 { 'y', MUTT_XLABEL, 0, eat_regexp },
8022@@ -1412,6 +1415,10 @@ mutt_pattern_exec (struct pattern_t *pat
8023 return (pat->not ^ match_mime_content_type (pat, ctx, h));
8024 case MUTT_UNREFERENCED:
8025 return (pat->not ^ (h->thread && !h->thread->child));
8026+#ifdef USE_NNTP
8027+ case MUTT_NEWSGROUPS:
8028+ return (pat->not ^ (h->env->newsgroups && patmatch (pat, h->env->newsgroups) == 0));
8029+#endif
8030 }
8031 mutt_error (_("error: unknown op %d (report this error)."), pat->op);
8032 return (-1);
8033@@ -1497,6 +1504,7 @@ int mutt_pattern_func (int op, char *pro
8034 buf = mutt_buffer_pool_get ();
8035
8036 mutt_buffer_strcpy (buf, NONULL (Context->pattern));
8037+ if (prompt || op != MUTT_LIMIT)
8038 if (mutt_get_field (prompt, buf->data, buf->dsize, MUTT_PATTERN | MUTT_CLEAR) != 0 ||
8039 !(mutt_b2s (buf)[0]))
8040 {
8041diff -udprP mutt-1.12.1.orig/po/POTFILES.in mutt-1.12.1/po/POTFILES.in
8042--- mutt-1.12.1.orig/po/POTFILES.in 2017-12-18 22:31:37.000000000 +0200
8043+++ mutt-1.12.1/po/POTFILES.in 2019-08-11 20:31:26.143940536 +0300
8044@@ -47,6 +47,8 @@ mutt_ssl_gnutls.c
8045 mutt_tunnel.c
8046 muttlib.c
8047 mx.c
8048+newsrc.c
8049+nntp.c
8050 pager.c
8051 parse.c
8052 pattern.c
8053diff -udprP mutt-1.12.1.orig/postpone.c mutt-1.12.1/postpone.c
8054--- mutt-1.12.1.orig/postpone.c 2019-05-25 19:22:39.000000000 +0300
8055+++ mutt-1.12.1/postpone.c 2019-08-11 20:31:26.143940536 +0300
8056@@ -125,15 +125,26 @@ int mutt_num_postponed (int force)
8057
8058 if (LastModify < st.st_mtime)
8059 {
8060+#ifdef USE_NNTP
8061+ int optnews = option (OPTNEWS);
8062+#endif
8063 LastModify = st.st_mtime;
8064
8065 if (access (Postponed, R_OK | F_OK) != 0)
8066 return (PostCount = 0);
8067+#ifdef USE_NNTP
8068+ if (optnews)
8069+ unset_option (OPTNEWS);
8070+#endif
8071 if (mx_open_mailbox (Postponed, MUTT_NOSORT | MUTT_QUIET, &ctx) == NULL)
8072 PostCount = 0;
8073 else
8074 PostCount = ctx.msgcount;
8075 mx_fastclose_mailbox (&ctx);
8076+#ifdef USE_NNTP
8077+ if (optnews)
8078+ set_option (OPTNEWS);
8079+#endif
8080 }
8081
8082 return (PostCount);
8083diff -udprP mutt-1.12.1.orig/protos.h mutt-1.12.1/protos.h
8084--- mutt-1.12.1.orig/protos.h 2019-05-25 19:22:39.000000000 +0300
8085+++ mutt-1.12.1/protos.h 2019-08-11 20:31:26.144940521 +0300
8086@@ -103,6 +103,7 @@ HASH *mutt_make_id_hash (CONTEXT *);
8087 HASH *mutt_make_subj_hash (CONTEXT *);
8088
8089 LIST *mutt_make_references(ENVELOPE *e);
8090+LIST *mutt_parse_references (char *, int);
8091
8092 char *mutt_read_rfc822_line (FILE *, char *, size_t *);
8093 ENVELOPE *mutt_read_rfc822_header (FILE *, HEADER *, short, short);
8094diff -udprP mutt-1.12.1.orig/recvattach.c mutt-1.12.1/recvattach.c
8095--- mutt-1.12.1.orig/recvattach.c 2019-05-25 19:22:39.000000000 +0300
8096+++ mutt-1.12.1/recvattach.c 2019-08-11 20:31:26.144940521 +0300
8097@@ -1226,6 +1226,15 @@ void mutt_view_attachments (HEADER *hdr)
8098 }
8099 #endif
8100
8101+#ifdef USE_NNTP
8102+ if (Context->magic == MUTT_NNTP)
8103+ {
8104+ mutt_flushinp ();
8105+ mutt_error _("Can't delete attachment from news server.");
8106+ break;
8107+ }
8108+#endif
8109+
8110 if (WithCrypto && (hdr->security & ENCRYPT))
8111 {
8112 mutt_message _(
8113@@ -1320,7 +1329,7 @@ void mutt_view_attachments (HEADER *hdr)
8114 case OP_FORWARD_MESSAGE:
8115 CHECK_ATTACH;
8116 mutt_attach_forward (CURATTACH->fp, hdr, actx,
8117- menu->tagprefix ? NULL : CURATTACH->content);
8118+ menu->tagprefix ? NULL : CURATTACH->content, 0);
8119 menu->redraw = REDRAW_FULL;
8120 break;
8121
8122@@ -1331,6 +1340,28 @@ void mutt_view_attachments (HEADER *hdr)
8123 menu->redraw = REDRAW_FULL;
8124 break;
8125
8126+#ifdef USE_NNTP
8127+ case OP_FORWARD_TO_GROUP:
8128+ CHECK_ATTACH;
8129+ mutt_attach_forward (CURATTACH->fp, hdr, actx,
8130+ menu->tagprefix ? NULL : CURATTACH->content, SENDNEWS);
8131+ menu->redraw = REDRAW_FULL;
8132+ break;
8133+
8134+ case OP_FOLLOWUP:
8135+ CHECK_ATTACH;
8136+
8137+ if (!hdr->env->followup_to ||
8138+ mutt_strcasecmp (hdr->env->followup_to, "poster") ||
8139+ query_quadoption (OPT_FOLLOWUPTOPOSTER, _("Reply by mail as poster prefers?")) != MUTT_YES)
8140+ {
8141+ mutt_attach_reply (CURATTACH->fp, hdr, actx,
8142+ menu->tagprefix ? NULL : CURATTACH->content, SENDNEWS|SENDREPLY);
8143+ menu->redraw = REDRAW_FULL;
8144+ break;
8145+ }
8146+#endif
8147+
8148 case OP_REPLY:
8149 case OP_GROUP_REPLY:
8150 case OP_GROUP_CHAT_REPLY:
8151diff -udprP mutt-1.12.1.orig/recvcmd.c mutt-1.12.1/recvcmd.c
8152--- mutt-1.12.1.orig/recvcmd.c 2019-05-25 19:22:39.000000000 +0300
8153+++ mutt-1.12.1/recvcmd.c 2019-08-11 20:31:26.144940521 +0300
8154@@ -398,7 +398,7 @@ static BODY ** copy_problematic_attachme
8155 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
8156 ATTACH_CONTEXT *actx,
8157 BODY * cur,
8158- short nattach)
8159+ short nattach, int flags)
8160 {
8161 short i;
8162 short mime_fwd_all = 0;
8163@@ -554,7 +554,7 @@ static void attach_forward_bodies (FILE
8164 tmpfp = NULL;
8165
8166 /* now that we have the template, send it. */
8167- ci_send_message (0, tmphdr, tmpbody, NULL, parent_hdr);
8168+ ci_send_message (flags, tmphdr, tmpbody, NULL, parent_hdr);
8169 return;
8170
8171 bail:
8172@@ -581,7 +581,7 @@ bail:
8173 */
8174
8175 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
8176- ATTACH_CONTEXT *actx, BODY * cur)
8177+ ATTACH_CONTEXT *actx, BODY * cur, int flags)
8178 {
8179 HEADER *curhdr = NULL;
8180 HEADER *tmphdr;
8181@@ -686,23 +686,23 @@ static void attach_forward_msgs (FILE *
8182 else
8183 mutt_free_header (&tmphdr);
8184
8185- ci_send_message (0, tmphdr, *tmpbody ? tmpbody : NULL,
8186+ ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL,
8187 NULL, curhdr);
8188
8189 }
8190
8191 void mutt_attach_forward (FILE * fp, HEADER * hdr,
8192- ATTACH_CONTEXT *actx, BODY * cur)
8193+ ATTACH_CONTEXT *actx, BODY * cur, int flags)
8194 {
8195 short nattach;
8196
8197
8198 if (check_all_msg (actx, cur, 0) == 0)
8199- attach_forward_msgs (fp, hdr, actx, cur);
8200+ attach_forward_msgs (fp, hdr, actx, cur, flags);
8201 else
8202 {
8203 nattach = count_tagged (actx);
8204- attach_forward_bodies (fp, hdr, actx, cur, nattach);
8205+ attach_forward_bodies (fp, hdr, actx, cur, nattach, flags);
8206 }
8207 }
8208
8209@@ -795,6 +795,17 @@ attach_reply_envelope_defaults (ENVELOPE
8210 return -1;
8211 }
8212
8213+#ifdef USE_NNTP
8214+ if ((flags & SENDNEWS))
8215+ {
8216+ /* in case followup set Newsgroups: with Followup-To: if it present */
8217+ if (!env->newsgroups && curenv &&
8218+ mutt_strcasecmp (curenv->followup_to, "poster"))
8219+ env->newsgroups = safe_strdup (curenv->followup_to);
8220+ }
8221+ else
8222+#endif
8223+ {
8224 if (parent)
8225 {
8226 if (mutt_fetch_recips (env, curenv, flags) == -1)
8227@@ -817,6 +828,7 @@ attach_reply_envelope_defaults (ENVELOPE
8228 }
8229
8230 mutt_fix_reply_recipients (env);
8231+ }
8232 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
8233
8234 if (parent)
8235@@ -879,6 +891,13 @@ void mutt_attach_reply (FILE * fp, HEADE
8236 char prefix[SHORT_STRING];
8237 int rc;
8238
8239+#ifdef USE_NNTP
8240+ if (flags & SENDNEWS)
8241+ set_option (OPTNEWSSEND);
8242+ else
8243+ unset_option (OPTNEWSSEND);
8244+#endif
8245+
8246 if (check_all_msg (actx, cur, 0) == -1)
8247 {
8248 nattach = count_tagged (actx);
8249diff -udprP mutt-1.12.1.orig/rfc2047.c mutt-1.12.1/rfc2047.c
8250--- mutt-1.12.1.orig/rfc2047.c 2019-05-25 19:22:39.000000000 +0300
8251+++ mutt-1.12.1/rfc2047.c 2019-08-11 20:31:26.144940521 +0300
8252@@ -633,6 +633,9 @@ void rfc2047_encode_envelope (ENVELOPE *
8253 rfc2047_encode_adrlist (e->mail_followup_to, "Mail-Followup-To");
8254 rfc2047_encode_adrlist (e->sender, "Sender");
8255 rfc2047_encode_string (&e->x_label);
8256+#ifdef USE_NNTP
8257+ if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
8258+#endif
8259 rfc2047_encode_string (&e->subject);
8260 }
8261
8262diff -udprP mutt-1.12.1.orig/send.c mutt-1.12.1/send.c
8263--- mutt-1.12.1.orig/send.c 2019-06-14 18:57:33.000000000 +0300
8264+++ mutt-1.12.1/send.c 2019-08-11 20:31:26.144940521 +0300
8265@@ -46,6 +46,11 @@
8266 #include <sys/types.h>
8267 #include <utime.h>
8268
8269+#ifdef USE_NNTP
8270+#include "nntp.h"
8271+#include "mx.h"
8272+#endif
8273+
8274 #ifdef MIXMASTER
8275 #include "remailer.h"
8276 #endif
8277@@ -217,17 +222,51 @@ static int edit_address (ADDRESS **a, /*
8278 return 0;
8279 }
8280
8281-static int edit_envelope (ENVELOPE *en)
8282+static int edit_envelope (ENVELOPE *en, int flags)
8283 {
8284 char buf[HUGE_STRING];
8285 LIST *uh = UserHeader;
8286
8287+#ifdef USE_NNTP
8288+ if (option (OPTNEWSSEND))
8289+ {
8290+ if (en->newsgroups)
8291+ strfcpy (buf, en->newsgroups, sizeof (buf));
8292+ else
8293+ buf[0] = 0;
8294+ if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) != 0)
8295+ return (-1);
8296+ FREE (&en->newsgroups);
8297+ en->newsgroups = safe_strdup (buf);
8298+
8299+ if (en->followup_to)
8300+ strfcpy (buf, en->followup_to, sizeof (buf));
8301+ else
8302+ buf[0] = 0;
8303+ if (option (OPTASKFOLLOWUP) && mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) != 0)
8304+ return (-1);
8305+ FREE (&en->followup_to);
8306+ en->followup_to = safe_strdup (buf);
8307+
8308+ if (en->x_comment_to)
8309+ strfcpy (buf, en->x_comment_to, sizeof (buf));
8310+ else
8311+ buf[0] = 0;
8312+ if (option (OPTXCOMMENTTO) && option (OPTASKXCOMMENTTO) && mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) != 0)
8313+ return (-1);
8314+ FREE (&en->x_comment_to);
8315+ en->x_comment_to = safe_strdup (buf);
8316+ }
8317+ else
8318+#endif
8319+ {
8320 if (edit_address (&en->to, _("To: ")) == -1 || en->to == NULL)
8321 return (-1);
8322 if (option (OPTASKCC) && edit_address (&en->cc, _("Cc: ")) == -1)
8323 return (-1);
8324 if (option (OPTASKBCC) && edit_address (&en->bcc, _("Bcc: ")) == -1)
8325 return (-1);
8326+ }
8327
8328 if (en->subject)
8329 {
8330@@ -262,6 +301,14 @@ static int edit_envelope (ENVELOPE *en)
8331 return 0;
8332 }
8333
8334+#ifdef USE_NNTP
8335+char *nntp_get_header (const char *s)
8336+{
8337+ SKIPWS (s);
8338+ return safe_strdup (s);
8339+}
8340+#endif
8341+
8342 static void process_user_recips (ENVELOPE *env)
8343 {
8344 LIST *uh = UserHeader;
8345@@ -274,6 +321,14 @@ static void process_user_recips (ENVELOP
8346 env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3);
8347 else if (ascii_strncasecmp ("bcc:", uh->data, 4) == 0)
8348 env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4);
8349+#ifdef USE_NNTP
8350+ else if (ascii_strncasecmp ("newsgroups:", uh->data, 11) == 0)
8351+ env->newsgroups = nntp_get_header (uh->data + 11);
8352+ else if (ascii_strncasecmp ("followup-to:", uh->data, 12) == 0)
8353+ env->followup_to = nntp_get_header (uh->data + 12);
8354+ else if (ascii_strncasecmp ("x-comment-to:", uh->data, 13) == 0)
8355+ env->x_comment_to = nntp_get_header (uh->data + 13);
8356+#endif
8357 }
8358 }
8359
8360@@ -313,6 +368,12 @@ static void process_user_header (ENVELOP
8361 else if (ascii_strncasecmp ("to:", uh->data, 3) != 0 &&
8362 ascii_strncasecmp ("cc:", uh->data, 3) != 0 &&
8363 ascii_strncasecmp ("bcc:", uh->data, 4) != 0 &&
8364+#ifdef USE_NNTP
8365+ ascii_strncasecmp ("newsgroups:", uh->data, 11) != 0 &&
8366+ ascii_strncasecmp ("followup-to:", uh->data, 12) != 0 &&
8367+ ascii_strncasecmp ("x-comment-to:", uh->data, 13) != 0 &&
8368+#endif
8369+ ascii_strncasecmp ("supersedes:", uh->data, 11) != 0 &&
8370 ascii_strncasecmp ("subject:", uh->data, 8) != 0 &&
8371 ascii_strncasecmp ("return-path:", uh->data, 12) != 0)
8372 {
8373@@ -775,6 +836,10 @@ void mutt_add_to_reference_headers (ENVE
8374 if (pp) *pp = p;
8375 if (qq) *qq = q;
8376
8377+#ifdef USE_NNTP
8378+ if (option (OPTNEWSSEND) && option (OPTXCOMMENTTO) && curenv->from)
8379+ env->x_comment_to = safe_strdup (mutt_get_name (curenv->from));
8380+#endif
8381 }
8382
8383 static void
8384@@ -837,6 +902,16 @@ envelope_defaults (ENVELOPE *env, CONTEX
8385
8386 if (flags & (SENDREPLY|SENDTOSENDER))
8387 {
8388+#ifdef USE_NNTP
8389+ if ((flags & SENDNEWS))
8390+ {
8391+ /* in case followup set Newsgroups: with Followup-To: if it present */
8392+ if (!env->newsgroups && curenv &&
8393+ mutt_strcasecmp (curenv->followup_to, "poster"))
8394+ env->newsgroups = safe_strdup (curenv->followup_to);
8395+ }
8396+ else
8397+#endif
8398 if (tag)
8399 {
8400 HEADER *h;
8401@@ -982,7 +1057,18 @@ void mutt_set_followup_to (ENVELOPE *e)
8402 * it hasn't already been set
8403 */
8404
8405- if (option (OPTFOLLOWUPTO) && !e->mail_followup_to)
8406+ if (!option (OPTFOLLOWUPTO))
8407+ return;
8408+#ifdef USE_NNTP
8409+ if (option (OPTNEWSSEND))
8410+ {
8411+ if (!e->followup_to && e->newsgroups && (strrchr (e->newsgroups, ',')))
8412+ e->followup_to = safe_strdup (e->newsgroups);
8413+ return;
8414+ }
8415+#endif
8416+
8417+ if (!e->mail_followup_to)
8418 {
8419 if (mutt_is_list_cc (0, e->to, e->cc))
8420 {
8421@@ -1148,6 +1234,9 @@ static int send_message (HEADER *msg)
8422 #endif
8423
8424 #if USE_SMTP
8425+#ifdef USE_NNTP
8426+ if (!option (OPTNEWSSEND))
8427+#endif
8428 if (SmtpUrl)
8429 return mutt_smtp_send (msg->env->from, msg->env->to, msg->env->cc,
8430 msg->env->bcc, tempfile,
8431@@ -1563,6 +1652,13 @@ ci_send_message (int flags, /* send mod
8432
8433 int rv = -1;
8434
8435+#ifdef USE_NNTP
8436+ if (flags & SENDNEWS)
8437+ set_option (OPTNEWSSEND);
8438+ else
8439+ unset_option (OPTNEWSSEND);
8440+#endif
8441+
8442 if (!flags && !msg && quadoption (OPT_RECALL) != MUTT_NO &&
8443 mutt_num_postponed (1))
8444 {
8445@@ -1702,12 +1798,33 @@ ci_send_message (int flags, /* send mod
8446 if (flags & SENDREPLY)
8447 mutt_fix_reply_recipients (msg->env);
8448
8449+#ifdef USE_NNTP
8450+ if ((flags & SENDNEWS) && ctx && ctx->magic == MUTT_NNTP && !msg->env->newsgroups)
8451+ msg->env->newsgroups = safe_strdup (((NNTP_DATA *)ctx->data)->group);
8452+#endif
8453+
8454 if (! (flags & (SENDMAILX|SENDBATCH)) &&
8455 ! (option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
8456 ! ((flags & SENDREPLY) && option (OPTFASTREPLY)))
8457 {
8458- if (edit_envelope (msg->env) == -1)
8459+ if (edit_envelope (msg->env, flags) == -1)
8460 goto cleanup;
8461+#ifdef USE_NNTP
8462+ /*
8463+ * If postponed message is a news article, it have
8464+ * a "Newsgroups:" header line, then set appropriate flag.
8465+ */
8466+ if (msg->env->newsgroups)
8467+ {
8468+ flags |= SENDNEWS;
8469+ set_option (OPTNEWSSEND);
8470+ }
8471+ else
8472+ {
8473+ flags &= ~SENDNEWS;
8474+ unset_option (OPTNEWSSEND);
8475+ }
8476+#endif
8477 }
8478
8479 /* the from address must be set here regardless of whether or not
8480@@ -2016,6 +2133,11 @@ main_loop:
8481 if (i == -1)
8482 {
8483 /* abort */
8484+#ifdef USE_NNTP
8485+ if (flags & SENDNEWS)
8486+ mutt_message _("Article not posted.");
8487+ else
8488+#endif
8489 mutt_message _("Mail not sent.");
8490 goto cleanup;
8491 }
8492@@ -2029,6 +2151,9 @@ main_loop:
8493 }
8494 }
8495
8496+#ifdef USE_NNTP
8497+ if (!(flags & SENDNEWS))
8498+#endif
8499 if (!has_recips (msg->env->to) && !has_recips (msg->env->cc) &&
8500 !has_recips (msg->env->bcc))
8501 {
8502@@ -2062,6 +2187,19 @@ main_loop:
8503 mutt_error _("No subject specified.");
8504 goto main_loop;
8505 }
8506+#ifdef USE_NNTP
8507+ if ((flags & SENDNEWS) && !msg->env->subject)
8508+ {
8509+ mutt_error _("No subject specified.");
8510+ goto main_loop;
8511+ }
8512+
8513+ if ((flags & SENDNEWS) && !msg->env->newsgroups)
8514+ {
8515+ mutt_error _("No newsgroup specified.");
8516+ goto main_loop;
8517+ }
8518+#endif
8519
8520 /* Scan for a mention of an attachment in the message body and
8521 * prompt if there is none. */
8522@@ -2184,7 +2322,12 @@ main_loop:
8523
8524 if (!option (OPTNOCURSES) && ! (flags & SENDMAILX))
8525 {
8526- mutt_message (i == 0 ? _("Mail sent.") : _("Sending in background."));
8527+ mutt_message (i != 0 ? _("Sending in background.") :
8528+#ifdef USE_NNTP
8529+ (flags & SENDNEWS) ? _("Article posted.") : _("Mail sent."));
8530+#else
8531+ _("Mail sent."));
8532+#endif
8533 mutt_sleep (0);
8534 }
8535
8536diff -udprP mutt-1.12.1.orig/sendlib.c mutt-1.12.1/sendlib.c
8537--- mutt-1.12.1.orig/sendlib.c 2019-06-14 18:57:30.000000000 +0300
8538+++ mutt-1.12.1/sendlib.c 2019-08-11 20:31:26.145940506 +0300
8539@@ -47,6 +47,10 @@
8540 #include <sys/wait.h>
8541 #include <fcntl.h>
8542
8543+#ifdef USE_NNTP
8544+#include "nntp.h"
8545+#endif
8546+
8547 #ifdef HAVE_SYSEXITS_H
8548 #include <sysexits.h>
8549 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
8550@@ -1590,6 +1594,14 @@ void mutt_write_references (LIST *r, FIL
8551 {
8552 LIST **ref = NULL;
8553 int refcnt = 0, refmax = 0;
8554+ int multiline = 1;
8555+ int space = 0;
8556+
8557+ if (trim < 0)
8558+ {
8559+ trim = -trim;
8560+ multiline = 0;
8561+ }
8562
8563 for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
8564 {
8565@@ -1600,9 +1612,11 @@ void mutt_write_references (LIST *r, FIL
8566
8567 while (refcnt-- > 0)
8568 {
8569- fputc (' ', f);
8570+ if (multiline || space)
8571+ fputc (' ', f);
8572+ space = 1;
8573 fputs (ref[refcnt]->data, f);
8574- if (refcnt >= 1)
8575+ if (multiline && refcnt >= 1)
8576 fputc ('\n', f);
8577 }
8578
8579@@ -2018,6 +2032,9 @@ int mutt_write_rfc822_header (FILE *fp,
8580 mutt_write_address_list (env->to, fp, 4, 0);
8581 }
8582 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
8583+#ifdef USE_NNTP
8584+ if (!option (OPTNEWSSEND))
8585+#endif
8586 fputs ("To: \n", fp);
8587
8588 if (env->cc)
8589@@ -2026,6 +2043,9 @@ int mutt_write_rfc822_header (FILE *fp,
8590 mutt_write_address_list (env->cc, fp, 4, 0);
8591 }
8592 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
8593+#ifdef USE_NNTP
8594+ if (!option (OPTNEWSSEND))
8595+#endif
8596 fputs ("Cc: \n", fp);
8597
8598 if (env->bcc)
8599@@ -2039,8 +2059,28 @@ int mutt_write_rfc822_header (FILE *fp,
8600 }
8601 }
8602 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
8603+#ifdef USE_NNTP
8604+ if (!option (OPTNEWSSEND))
8605+#endif
8606 fputs ("Bcc: \n", fp);
8607
8608+#ifdef USE_NNTP
8609+ if (env->newsgroups)
8610+ fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
8611+ else if (mode == 1 && option (OPTNEWSSEND))
8612+ fputs ("Newsgroups: \n", fp);
8613+
8614+ if (env->followup_to)
8615+ fprintf (fp, "Followup-To: %s\n", env->followup_to);
8616+ else if (mode == 1 && option (OPTNEWSSEND))
8617+ fputs ("Followup-To: \n", fp);
8618+
8619+ if (env->x_comment_to)
8620+ fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
8621+ else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
8622+ fputs ("X-Comment-To: \n", fp);
8623+#endif
8624+
8625 if (env->subject)
8626 {
8627 if (hide_protected_subject &&
8628@@ -2066,6 +2106,9 @@ int mutt_write_rfc822_header (FILE *fp,
8629 fputs ("Reply-To: \n", fp);
8630
8631 if (env->mail_followup_to)
8632+#ifdef USE_NNTP
8633+ if (!option (OPTNEWSSEND))
8634+#endif
8635 {
8636 fputs ("Mail-Followup-To: ", fp);
8637 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
8638@@ -2415,6 +2458,24 @@ mutt_invoke_sendmail (ADDRESS *from, /*
8639 size_t extra_argslen = 0, extra_argsmax = 0;
8640 int i;
8641
8642+#ifdef USE_NNTP
8643+ if (option (OPTNEWSSEND))
8644+ {
8645+ char cmd[LONG_STRING];
8646+
8647+ mutt_FormatString (cmd, sizeof (cmd), 0, MuttIndexWindow->cols,
8648+ NONULL (Inews), nntp_format_str, 0, 0);
8649+ if (!*cmd)
8650+ {
8651+ i = nntp_post (msg);
8652+ unlink (msg);
8653+ return i;
8654+ }
8655+
8656+ s = safe_strdup (cmd);
8657+ }
8658+#endif
8659+
8660 /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
8661 if (!s)
8662 {
8663@@ -2464,6 +2525,10 @@ mutt_invoke_sendmail (ADDRESS *from, /*
8664 }
8665 }
8666
8667+#ifdef USE_NNTP
8668+ if (!option (OPTNEWSSEND))
8669+ {
8670+#endif
8671 if (eightbit && option (OPTUSE8BITMIME))
8672 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
8673
8674@@ -2497,6 +2562,9 @@ mutt_invoke_sendmail (ADDRESS *from, /*
8675 args = add_args (args, &argslen, &argsmax, to);
8676 args = add_args (args, &argslen, &argsmax, cc);
8677 args = add_args (args, &argslen, &argsmax, bcc);
8678+#ifdef USE_NNTP
8679+ }
8680+#endif
8681
8682 if (argslen == argsmax)
8683 safe_realloc (&args, sizeof (char *) * (++argsmax));
8684@@ -2689,6 +2757,10 @@ int mutt_bounce_message (FILE *fp, HEADE
8685 }
8686 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
8687
8688+#ifdef USE_NNTP
8689+ unset_option (OPTNEWSSEND);
8690+#endif
8691+
8692 /*
8693 * prepare recipient list. idna conversion appears to happen before this
8694 * function is called, since the user receives confirmation of the address
8695diff -udprP mutt-1.12.1.orig/sort.c mutt-1.12.1/sort.c
8696--- mutt-1.12.1.orig/sort.c 2019-05-25 19:22:39.000000000 +0300
8697+++ mutt-1.12.1/sort.c 2019-08-11 20:31:26.145940506 +0300
8698@@ -24,6 +24,11 @@
8699 #include "sort.h"
8700 #include "mutt_idna.h"
8701
8702+#ifdef USE_NNTP
8703+#include "mx.h"
8704+#include "nntp.h"
8705+#endif
8706+
8707 #include <stdlib.h>
8708 #include <string.h>
8709 #include <ctype.h>
8710@@ -155,6 +160,17 @@ static int compare_order (const void *a,
8711 HEADER **ha = (HEADER **) a;
8712 HEADER **hb = (HEADER **) b;
8713
8714+#ifdef USE_NNTP
8715+ if (Context && Context->magic == MUTT_NNTP)
8716+ {
8717+ anum_t na = NHDR (*ha)->article_num;
8718+ anum_t nb = NHDR (*hb)->article_num;
8719+ int result = na == nb ? 0 : na > nb ? 1 : -1;
8720+ AUXSORT (result, a, b);
8721+ return (SORTCODE (result));
8722+ }
8723+ else
8724+#endif
8725 /* no need to auxsort because you will never have equality here */
8726 return (SORTCODE ((*ha)->index - (*hb)->index));
8727 }
8728diff -udprP mutt-1.12.1.orig/url.c mutt-1.12.1/url.c
8729--- mutt-1.12.1.orig/url.c 2019-05-25 19:22:39.000000000 +0300
8730+++ mutt-1.12.1/url.c 2019-08-11 20:31:26.145940506 +0300
8731@@ -40,6 +40,8 @@ static const struct mapping_t UrlMap[] =
8732 { "imaps", U_IMAPS },
8733 { "pop", U_POP },
8734 { "pops", U_POPS },
8735+ { "news", U_NNTP },
8736+ { "snews", U_NNTPS },
8737 { "mailto", U_MAILTO },
8738 { "smtp", U_SMTP },
8739 { "smtps", U_SMTPS },
8740@@ -243,7 +245,7 @@ int url_ciss_tobuffer (ciss_url_t* ciss,
8741 if (!(flags & U_PATH))
8742 mutt_buffer_addstr (dest, "//");
8743
8744- if (ciss->user)
8745+ if (ciss->user && (ciss->user[0] || !(flags & U_PATH)))
8746 {
8747 char u[STRING];
8748 url_pct_encode (u, sizeof (u), ciss->user);
8749diff -udprP mutt-1.12.1.orig/url.h mutt-1.12.1/url.h
8750--- mutt-1.12.1.orig/url.h 2019-05-25 19:22:39.000000000 +0300
8751+++ mutt-1.12.1/url.h 2019-08-11 20:31:26.145940506 +0300
8752@@ -8,6 +8,8 @@ typedef enum url_scheme
8753 U_POPS,
8754 U_IMAP,
8755 U_IMAPS,
8756+ U_NNTP,
8757+ U_NNTPS,
8758 U_SMTP,
8759 U_SMTPS,
8760 U_MAILTO,
This page took 1.238037 seconds and 4 git commands to generate.