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