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