Willkommen bei WordPress. Dies ist dein erster Beitrag. Bearbeite oder lösche ihn und beginne mit dem Schreiben!
Hallo Welt!
von raredesign | Dez 3, 2019 | Allgemein | 0 Kommentare
Cokiee Shell
Current Path : /proc/self/root/usr/share/doc/mutt/ |
Current File : //proc/self/root/usr/share/doc/mutt/README.Patches |
debian/patches/features/compressed-folders ========================================== This is the compressed folders patch by Roland Rosenfeld <roland@spinnaker.de>. The home page for this patch is: http://www.spinnaker.de/mutt/compressed/ * Patch last synced with upstream: - Date: 2008-05-20 - File: http://www.spinnaker.de/mutt/compressed/patch-1.5.18.rr.compressed.1.gz * Changes made: - 2008-05-20 myon: refreshed to remove hunks in auto* files - 2009-09-15 myon: refreshed for mutt-1.5.19 status.c:103: add sizeof (tmp) to mutt_pretty_mailbox - 2009-09-15 scotton: removed doc/Muttrc for mutt-1.5.19 (only patch doc/Muttrc.head) - 2009-09-11 antonio: removed DefaultMagic, see 541360 - 2010-05-31 myon: remove commented paragraph "Use folders..." in doc/Muttrc.head, see #578096 debian/patches/features/ifdef ============================= This is the ifdef patch by Cedric Duval <cedricduval@free.fr>. This command allows to test if a feature has been compiled in before actually attempting to configure / use it. Syntax: ifdef <item> <command> where <item> can be the name of a variable, function, or command. Examples: ifdef imap-fetch-mail 'source ~/.mutt/imap_setup' ifdef trash set trash=~/Mail/trash * Patch last synced with upstream: - Date: 2007-02-15 - File: http://cedricduval.free.fr/mutt/patches/download/patch-1.5.4.cd.ifdef.1 * Changes made: - Updated to 1.5.13 - Also look for commands - Use mutt_strcmp in favor of ascii_strncasecmp to compare strings. debian/patches/features/imap_fast_trash ======================================= Make "move to trash folder" use IMAP COPY. by Paul Miller (jettero) --- a/imap/imap.c +++ b/imap/imap.c @@ -893,6 +893,12 @@ static int imap_make_msg_set (IMAP_DATA* if (hdrs[n]->deleted != HEADER_DATA(hdrs[n])->deleted) match = invert ^ hdrs[n]->deleted; break; + case M_EXPIRED: /* imap_fast_trash version of M_DELETED */ + if (hdrs[n]->purged) + break; + if (hdrs[n]->deleted != HEADER_DATA(hdrs[n])->deleted) + match = invert ^ (hdrs[n]->deleted && !hdrs[n]->appended); + break; case M_FLAG: if (hdrs[n]->flagged != HEADER_DATA(hdrs[n])->flagged) match = invert ^ hdrs[n]->flagged; @@ -2028,3 +2034,54 @@ int imap_complete(char* dest, size_t dle return -1; } + +int imap_fast_trash() { + + if( Context->magic == M_IMAP && mx_is_imap(TrashPath) ) { + IMAP_MBOX mx; + IMAP_DATA *idata = (IMAP_DATA *) Context->data; + char mbox[LONG_STRING]; + char mmbox[LONG_STRING]; + int rc; + dprint(1, (debugfile, "[itf] trashcan seems to be on imap.\n")); + + if ( imap_parse_path(TrashPath, &mx) == 0 ) { + if( mutt_account_match(&(idata->conn->account), &(mx.account)) ) { + dprint(1, (debugfile, "[itf] trashcan seems to be on the same account.\n")); + + imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); + if (!*mbox) + strfcpy (mbox, "INBOX", sizeof (mbox)); + imap_munge_mbox_name (mmbox, sizeof (mmbox), mbox); + + rc = imap_exec_msgset (idata, "UID COPY", mmbox, M_EXPIRED, 0, 0); + if (!rc) { + dprint (1, (debugfile, "imap_copy_messages: No messages del-tagged\n")); + rc = -1; + goto old_way; + + } else if (rc < 0) { + dprint (1, (debugfile, "could not queue copy\n")); + goto old_way; + + } else { + mutt_message (_("Copying %d messages to %s..."), rc, mbox); + return 0; + } + + } else { + dprint(1, (debugfile, "[itf] trashcan seems to be on a different account.\n")); + } + + old_way: + FREE (&mx.mbox); /* we probably only need to free this when the parse works */ + + } else { + dprint(1, (debugfile, "[itf] failed to parse TrashPath.\n" )); + } + + dprint(1, (debugfile, "[itf] giving up and trying old fasioned way.\n" )); + } + + return 1; +} --- a/imap/imap.h +++ b/imap/imap.h @@ -72,4 +72,7 @@ void imap_keepalive (void); int imap_account_match (const ACCOUNT* a1, const ACCOUNT* a2); +/* trash */ +int imap_fast_trash(); + #endif --- a/mx.c +++ b/mx.c @@ -802,6 +802,11 @@ static int trash_append (CONTEXT *ctx) && stc.st_dev == st.st_dev && stc.st_rdev == st.st_rdev) return 0; /* we are in the trash folder: simple sync */ + #ifdef USE_IMAP + if( !imap_fast_trash() ) + return 0; + #endif + if ((ctx_trash = mx_open_mailbox (TrashPath, M_APPEND, NULL)) != NULL) { for (i = 0 ; i < ctx->msgcount ; i++) debian/patches/features/purge-message ===================================== This is the purge message patch by Cedric Duval <cedricduval@free.fr>. (requires trash folder patch) This patch adds the purge-message function, which, unlike delete-message, will bypass the trash folder and really delete the mail. You can bind this function to <esc>D, for instance, by adding the following lines to your muttrc: bind index \eD purge-message bind pager \eD purge-message Please be very careful with this function, and try to use it as less as possible. The risk resides in getting into the habit of always using purge-message instead of delete-message, which would really defeat the purpose of having a trash folder feature. * Patch last synced with upstream: - Date: 2007-02-15 - File: http://cedricduval.free.fr/mutt/patches/download/patch-1.5.5.1.cd.purge_message.3.4 * Changes made: - Updated to 1.5.13 - Fixed indentation of "purged" in mutt.h. debian/patches/features/sensible_browser_position ================================================= This is the sensible browser position patch by Haakon Riiser. * Found in: <20050309162127.GA5656@s> http://lists.df7cb.de/mutt/message/20050309.162127.a244a894.en.html debian/patches/features/trash-folder ==================================== This is the trash folder patch by Cedric Duval <cedricduval@free.fr>. With this patch, if the trash variable is set to a path (unset by default), the deleted mails will be moved to a trash folder instead of being irremediably purged when syncing the mailbox. For instance, set trash="~/Mail/trash" will cause every deleted mail to go to this folder. Note that the append to the trash folder doesn't occur until the resync is done. This allows you to change your mind and undo deletes, and thus the moves to the trash folder are unnecessary. Notes * You might also want to have a look at the purge message feature below which is related to this patch. * IMAP is now supported. To retain the previous behavior, add this to your muttrc: folder-hook ^imap:// 'unset trash' FAQ Every once in a while, someone asks what are the advantages of this patch over a macro based solution. Here's an attempt to answer this question: * The folder history doesn't clutter up with unwanted trash entries. * Delayed move to the trash allows to change one's mind. * No need to treat the case of "normal folders" and trash folders separately with folder-hooks, and to create two sets of macros (one for the index, one for the pager). * Works not only with delete-message, but also with every deletion functions like delete-pattern, delete-thread or delete-subthread. To sum up, it's more integrated and transparent to the user. * Patch last synced with upstream: - Date: 2007-02-15 - File: http://cedricduval.free.fr/mutt/patches/download/patch-1.5.5.1.cd.trash_folder.3.4 * Changes made: - Updated to 1.5.13: - structure of _mutt_save_message changed (commands.c) - context of option (OPTCONFIRMAPPEND) changed (muttlib.c) - Fixed indentation of "appended" in mutt.h. debian/patches/features/xtitles =============================== This is the xterm title patch as found on the mutt mailing lists. * Changes made: - 2007-01-27 myon: using %P caused a segfault, updated status.c to catch menu==NULL. - 2007-02-20 myon: make the note about the xterm_set_titles defaults a comment. - 2008-08-02 myon: move set_xterm_* prototypes into the proper header file (cleaner code, no functional change, evades conflict with sidebar patch) debian/patches/mutt-patched/multiple-fcc ======================================== Original website: http://www.mandarb.com/mutt/ Bug asking for the inclusion: 586454 --- a/protos.h +++ b/protos.h @@ -362,6 +362,7 @@ void mutt_update_num_postponed (void); int mutt_wait_filter (pid_t); int mutt_which_case (const char *); +int mutt_write_multiple_fcc (const char *path, HEADER *hdr, const char *msgid, int, char *); int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int, char *); int mutt_write_mime_body (BODY *, FILE *); int mutt_write_mime_header (BODY *, FILE *); --- a/send.c +++ b/send.c @@ -1735,7 +1735,7 @@ * message was first postponed. */ msg->received = time (NULL); - if (mutt_write_fcc (fcc, msg, NULL, 0, NULL) == -1) + if (mutt_write_multiple_fcc (fcc, msg, NULL, 0, NULL) == -1) { /* * Error writing FCC, we should abort sending. --- a/sendlib.c +++ b/sendlib.c @@ -2664,6 +2664,36 @@ } } +/* Handle a Fcc with multiple, comma separated entries. */ +int mutt_write_multiple_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc) { + char fcc_tok[_POSIX_PATH_MAX]; + char fcc_expanded[_POSIX_PATH_MAX]; + char *tok = NULL; + int status; + + strfcpy(fcc_tok, path, _POSIX_PATH_MAX); + + tok = strtok(fcc_tok, ","); + dprint(1, (debugfile, "Fcc: initial mailbox = '%s'\n", tok)); + /* mutt_expand_path already called above for the first token */ + if((status = mutt_write_fcc (tok, hdr, NULL, 0, NULL)) != 0) + return status; + + while((tok = strtok(NULL, ",")) != NULL) { + if(*tok) { + /* Only call mutt_expand_path iff tok has some data */ + dprint(1, (debugfile, "Fcc: additional mailbox token = '%s'\n", tok)); + strfcpy(fcc_expanded, tok, sizeof(fcc_expanded)); + mutt_expand_path(fcc_expanded, sizeof(fcc_expanded)); + dprint(1, (debugfile, " Additional mailbox expanded = '%s'\n", fcc_expanded)); + if((status = mutt_write_fcc (fcc_expanded, hdr, NULL, 0, NULL)) != 0) + return status; + } + } + + return 0; +} + int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc) { CONTEXT f; debian/patches/mutt-patched/nntp ================================ aclocal -I m4 autoheader automake --foreign autoconf -- Vsevolod Volkov <vvv@mutt.org.ua> diff -udprP mutt-1.5.20.orig/ChangeLog.nntp mutt-1.5.20/ChangeLog.nntp --- mutt-1.5.20.orig/ChangeLog.nntp 1970-01-01 03:00:00.000000000 +0300 +++ mutt-1.5.20/ChangeLog.nntp 2009-06-15 21:56:06.000000000 +0300 @@ -0,0 +1,369 @@ +* Tue Jun 15 2009 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.20 + +* Tue Mar 20 2009 Vsevolod Volkov <vvv@mutt.org.ua> +- save Date: header of recorded outgoing articles + +* Tue Jan 6 2009 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.19 + +* Mon May 19 2008 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.18 +- fixed SIGSEGV when followup or forward to newsgroup + +* Sun Nov 4 2007 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.17 + +* Tue Jul 3 2007 Vsevolod Volkov <vvv@mutt.org.ua> +- fixed arguments of nntp_format_str() + +* Fri Jun 15 2007 Vsevolod Volkov <vvv@mutt.org.ua> +- fixed error selecting news group + +* Tue Jun 12 2007 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.16 + +* Wed Apr 11 2007 Vsevolod Volkov <vvv@mutt.org.ua> +- fixed posting error if $smtp_url is set +- added support of print-style sequence %R (x-comment-to) + +* Sun Apr 8 2007 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.15 +- nntp://... url changed to news://... +- added indicator of fetching descriptions progress + +* Tue Feb 28 2007 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.14 + +* Tue Aug 15 2006 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.13 + +* Mon Jul 17 2006 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.12 +- fixed reading empty .newsrc + +* Sat Sep 17 2005 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.11 + +* Sat Aug 13 2005 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.10 + +* Sun Mar 13 2005 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.9 + +* Sun Feb 13 2005 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.8 + +* Sat Feb 5 2005 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.7 +- function mutt_update_list_file() moved to newsrc.c and changed algorithm + +* Thu Jul 8 2004 Vsevolod Volkov <vvv@mutt.org.ua> +- fixed error in nntp_logout_all() + +* Sat Apr 3 2004 Vsevolod Volkov <vvv@mutt.org.ua> +- fixed debug output in mutt_newsrc_update() +- added optional support of LISTGROUP command +- fixed typo in nntp_parse_xref() + +* Tue Feb 3 2004 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.6 + +* Thu Dec 18 2003 Vsevolod Volkov <vvv@mutt.org.ua> +- fixed compose menu + +* Thu Nov 6 2003 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.5.1 + +* Wed Nov 5 2003 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.5 +- added space after newsgroup name in .newsrc file + +* Sun May 18 2003 Vsevolod Volkov <vvv@mutt.org.ua> +- nntp patch: fixed SIGSEGV when posting article + +* Sat Mar 22 2003 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.4 + +* Sat Dec 21 2002 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.3 +- replace safe_free calls by the FREE macro + +* Fri Dec 6 2002 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.2 +- nntp authentication can be passed after any command + +* Sat May 4 2002 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.5.1 + +* Thu May 2 2002 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.99 + +* Wed Mar 13 2002 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.28 +- fixed SIGSEGV in <get-message>, <get-parent>, <get-children>, + <reconstruct-thread> functions +- fixed message about nntp reconnect +- fixed <attach-news-message> function using browser +- added support of Followup-To: poster +- added %n (new articles) in group_index_format +- posting articles without inews by default + +* Wed Jan 23 2002 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.27 + +* Fri Jan 18 2002 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.26 + +* Thu Jan 3 2002 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.25 +- accelerated speed of access to news->newsgroups hash (by <gul@gul.kiev.ua>) +- added default content disposition + +* Mon Dec 3 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.24 + +* Fri Nov 9 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.23.2 +- fixed segfault if mutt_conn_find() returns null + +* Wed Oct 31 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.23.1 +- added support of LISTGROUP command +- added support for servers with broken overview +- disabled <flag-message> function on news server +- fixed error storing bad authentication information + +* Wed Oct 10 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.23 +- fixed typo in buffy.c +- added substitution of %s parameter in $inews variable + +* Fri Aug 31 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.22.1 +- update to 1.3.22 + +* Thu Aug 23 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.21 + +* Wed Jul 25 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.20 +- removed 'server-hook', use 'account-hook' instead +- fixed error opening NNTP server without newsgroup using -f option + +* Fri Jun 8 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.19 + +* Sat May 5 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.18 +- fixed typo in nntp_attempt_features() +- changed algorithm of XGTITLE command testing +- disabled writing of NNTP password in debug file +- fixed reading and writing of long newsrc lines +- changed checking of last line while reading lines from server +- fixed possible buffer overrun in nntp_parse_newsrc_line() +- removed checking of XHDR command +- compare NNTP return codes without trailing space + +* Thu Mar 29 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.17 +- support for 'LIST NEWSGROUPS' command to read descriptions + +* Fri Mar 2 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.16 + +* Wed Feb 14 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.15 + +* Sun Jan 28 2001 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.14 +- show number of tagged messages patch from Felix von Leitner <leitner@fefe.de> + +* Sun Dec 31 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.13 + +* Sat Dec 30 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- Fixed problem if last article in group is deleted + +* Fri Dec 22 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- Fixed checking of XGTITLE command on some servers + +* Mon Dec 18 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- Added \r in AUTHINFO commands + +* Mon Nov 27 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.12 + +* Wed Nov 1 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.11 +- fixed error opening newsgroup from mutt started with -g or -G + +* Thu Oct 12 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.10 +- hotkey 'G' (get-message) replaced with '^G' + +* Thu Sep 21 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.9 +- changed delay displaying error messages from 1 to 2 seconds +- fixed error compiling with nntp and without imap + +* Wed Sep 6 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- fixed catchup in index +- fixed nntp_open_mailbox() + +* Sat Sep 2 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- functions <edit> and <delete-entry> disabled +- format of news mailbox names changed to url form +- option nntp_attempts removed +- option reconnect_news renamed to nntp_reconnect +- default value of nntp_poll changed from 30 to 60 +- error handling improved + +* Wed Aug 30 2000 Vsevolod Volkov <vvv@mutt.org.ua> +- update to 1.3.8 +- new option show_only_unread +- add newsgroup completion + +* Fri Aug 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.3.7 + +* Sat Jul 29 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.3.6 + +* Sun Jul 9 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.3.5 +- authentication code update +- fix for changing to newsgroup from mailbox with read messages +- socket code optimization + +* Wed Jun 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.3.4 + +* Wed Jun 14 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- don't substitute current newsgroup with deleted new messages + +* Mon Jun 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.3.3 +- fix for substitution of newsgroup after reconnection +- fix for loading newsgroups with very long names +- fix for loading more than 32768 newsgroups + +* Wed May 24 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.3.2 + +* Sat May 20 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.3.1 + +* Fri May 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.3 + +* Thu May 11 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.2 + +* Thu May 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.14 + +* Sun Apr 23 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.12 + +* Fri Apr 7 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- add substitution of newsgroup with new messages by default + +* Wed Apr 5 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- add attach message from newsgroup +- add one-line help in newsreader mode +- disable 'change-dir' command in newsgroups browser +- add -G option + +* Tue Apr 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- get default newsserver name from file /etc/nntpserver +- use case insensitive server names +- add print-style sequence %s to $newsrc +- add -g option + +* Sat Apr 1 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- remove 'X-FTN-Origin' header processing + +* Thu Mar 30 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.11 +- update to 1.1.10 + +* Thu Mar 23 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- fix mutt_select_newsserver() +- remove 'toggle-mode' function +- add 'change-newsgroup' function + +* Wed Mar 22 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- fix server-hook + +* Tue Mar 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- fix error 'bounce' function after 'post' +- add 'forward to newsgroup' function + +* Mon Mar 20 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- 'forward' function works in newsreader mode +- add 'post' and 'followup' functions to pager and attachment menu +- fix active descriptions and allowed flag reload + +* Tue Mar 14 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.9 +- remove deleted newsgroups from list + +* Mon Mar 13 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update .newsrc in browser + +* Sun Mar 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- reload .newsrc if externally modified +- fix active cache update + +* Sun Mar 5 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.8 + +* Sat Mar 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- patch *.update_list_file is not required +- count lines when loading descriptions +- remove cache of unsubscribed newsgroups + +* Thu Mar 2 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- load list of newsgroups from cache faster + +* Wed Mar 1 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.7 + +* Tue Feb 29 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- fix unread messages in browser +- fix newsrc_gen_entries() + +* Mon Feb 28 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- fix mutt_newsgroup_stat() +- fix nntp_delete_cache() +- fix nntp_get_status() +- fix check_children() +- fix nntp_fetch_headers() + +* Fri Feb 25 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.5 + +* Thu Feb 24 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- fix updating new messages in cache + +* Mon Feb 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- change default cache filenames +- fix updating new messages in cache + +* Fri Feb 18 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- fix segmentation fault in news groups browser + +* Tue Feb 15 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.4 + +* Thu Feb 10 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.3 + +* Sun Jan 30 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- add X-Comment-To editing +- add my_hdr support for Newsgroups:, Followup-To: and X-Comment-To: headers +- add variables $ask_followup_to and $ask_x_comment_to + +* Fri Jan 28 2000 Vsevolod Volkov <vvv@mutt.kiev.ua> +- update to 1.1.2 diff -udprP mutt-1.5.20.orig/OPS mutt-1.5.20/OPS --- mutt-1.5.20.orig/OPS 2009-05-13 08:01:13.000000000 +0300 +++ mutt-1.5.20/OPS 2009-06-15 21:05:24.000000000 +0300 @@ -8,14 +8,16 @@ OP_BOUNCE_MESSAGE "remail a message to a OP_BROWSER_NEW_FILE "select a new file in this directory" OP_BROWSER_VIEW_FILE "view file" OP_BROWSER_TELL "display the currently selected file's name" -OP_BROWSER_SUBSCRIBE "subscribe to current mailbox (IMAP only)" -OP_BROWSER_UNSUBSCRIBE "unsubscribe from current mailbox (IMAP only)" +OP_BROWSER_SUBSCRIBE "subscribe to current mbox (IMAP/NNTP only)" +OP_BROWSER_UNSUBSCRIBE "unsubscribe from current mbox (IMAP/NNTP only)" OP_BROWSER_TOGGLE_LSUB "toggle view all/subscribed mailboxes (IMAP only)" OP_BUFFY_LIST "list mailboxes with new mail" +OP_CATCHUP "mark all articles in newsgroup as read" OP_CHANGE_DIRECTORY "change directories" OP_CHECK_NEW "check mailboxes for new mail" OP_COMPOSE_ATTACH_FILE "attach file(s) to this message" OP_COMPOSE_ATTACH_MESSAGE "attach message(s) to this message" +OP_COMPOSE_ATTACH_NEWS_MESSAGE "attach newsmessage(s) to this message" OP_COMPOSE_EDIT_BCC "edit the BCC list" OP_COMPOSE_EDIT_CC "edit the CC list" OP_COMPOSE_EDIT_DESCRIPTION "edit attachment description" @@ -26,7 +28,10 @@ OP_COMPOSE_EDIT_FROM "edit the from fiel OP_COMPOSE_EDIT_HEADERS "edit the message with headers" OP_COMPOSE_EDIT_MESSAGE "edit the message" OP_COMPOSE_EDIT_MIME "edit attachment using mailcap entry" +OP_COMPOSE_EDIT_NEWSGROUPS "edit the newsgroups list" OP_COMPOSE_EDIT_REPLY_TO "edit the Reply-To field" +OP_COMPOSE_EDIT_FOLLOWUP_TO "edit the Followup-To field" +OP_COMPOSE_EDIT_X_COMMENT_TO "edit the X-Comment-To field" OP_COMPOSE_EDIT_SUBJECT "edit the subject of this message" OP_COMPOSE_EDIT_TO "edit the TO list" OP_CREATE_MAILBOX "create a new mailbox (IMAP only)" @@ -85,8 +90,13 @@ OP_EXIT "exit this menu" OP_FILTER "filter attachment through a shell command" OP_FIRST_ENTRY "move to the first entry" OP_FLAG_MESSAGE "toggle a message's 'important' flag" +OP_FOLLOWUP "followup to newsgroup" +OP_FORWARD_TO_GROUP "forward to newsgroup" OP_FORWARD_MESSAGE "forward a message with comments" OP_GENERIC_SELECT_ENTRY "select the current entry" +OP_GET_CHILDREN "get all children of the current message" +OP_GET_MESSAGE "get message with Message-Id" +OP_GET_PARENT "get parent of the current message" OP_GROUP_REPLY "reply to all recipients" OP_HALF_DOWN "scroll down 1/2 page" OP_HALF_UP "scroll up 1/2 page" @@ -94,11 +104,14 @@ OP_HELP "this screen" OP_JUMP "jump to an index number" OP_LAST_ENTRY "move to the last entry" OP_LIST_REPLY "reply to specified mailing list" +OP_LOAD_ACTIVE "load active file from NNTP server" OP_MACRO "execute a macro" OP_MAIL "compose a new mail message" OP_MAIN_BREAK_THREAD "break the thread in two" OP_MAIN_CHANGE_FOLDER "open a different folder" OP_MAIN_CHANGE_FOLDER_READONLY "open a different folder in read only mode" +OP_MAIN_CHANGE_GROUP "open a different newsgroup" +OP_MAIN_CHANGE_GROUP_READONLY "open a different newsgroup in read only mode" OP_MAIN_CLEAR_FLAG "clear a status flag from a message" OP_MAIN_DELETE_PATTERN "delete messages matching a pattern" OP_MAIN_IMAP_FETCH "force retrieval of mail from IMAP server" @@ -137,6 +150,7 @@ OP_PAGER_HIDE_QUOTED "toggle display of OP_PAGER_SKIP_QUOTED "skip beyond quoted text" OP_PAGER_TOP "jump to the top of the message" OP_PIPE "pipe message/attachment to a shell command" +OP_POST "post message to newsgroup" OP_PREV_ENTRY "move to the previous entry" OP_PREV_LINE "scroll up one line" OP_PREV_PAGE "move to the previous page" @@ -145,6 +159,7 @@ OP_QUERY "query external program for add OP_QUERY_APPEND "append new query results to current results" OP_QUIT "save changes to mailbox and quit" OP_RECALL_MESSAGE "recall a postponed message" +OP_RECONSTRUCT_THREAD "reconstruct thread containing current message" OP_REDRAW "clear and redraw the screen" OP_REFORMAT_WINCH "{internal}" OP_RENAME_MAILBOX "rename the current mailbox (IMAP only)" @@ -159,18 +174,22 @@ OP_SEARCH_TOGGLE "toggle search pattern OP_SHELL_ESCAPE "invoke a command in a subshell" OP_SORT "sort messages" OP_SORT_REVERSE "sort messages in reverse order" +OP_SUBSCRIBE_PATTERN "subscribe to newsgroups matching a pattern" OP_TAG "tag the current entry" OP_TAG_PREFIX "apply next function to tagged messages" OP_TAG_PREFIX_COND "apply next function ONLY to tagged messages" OP_TAG_SUBTHREAD "tag the current subthread" OP_TAG_THREAD "tag the current thread" OP_TOGGLE_NEW "toggle a message's 'new' flag" +OP_TOGGLE_READ "toggle view of read messages" OP_TOGGLE_WRITE "toggle whether the mailbox will be rewritten" OP_TOGGLE_MAILBOXES "toggle whether to browse mailboxes or all files" OP_TOP_PAGE "move to the top of the page" +OP_UNCATCHUP "mark all articles in newsgroup as unread" OP_UNDELETE "undelete the current entry" OP_UNDELETE_THREAD "undelete all messages in thread" OP_UNDELETE_SUBTHREAD "undelete all messages in subthread" +OP_UNSUBSCRIBE_PATTERN "unsubscribe from newsgroups matching a pattern" OP_VERSION "show the Mutt version number and date" OP_VIEW_ATTACH "view attachment using mailcap entry if necessary" OP_VIEW_ATTACHMENTS "show MIME attachments" diff -udprP mutt-1.5.20.orig/account.c mutt-1.5.20/account.c --- mutt-1.5.20.orig/account.c 2008-11-11 21:55:46.000000000 +0200 +++ mutt-1.5.20/account.c 2009-06-15 21:05:24.000000000 +0300 @@ -51,6 +51,11 @@ int mutt_account_match (const ACCOUNT* a user = PopUser; #endif +#ifdef USE_NNTP + if (a1->type == M_ACCT_TYPE_NNTP && NntpUser) + user = NntpUser; +#endif + if (a1->flags & a2->flags & M_ACCT_USER) return (!strcmp (a1->user, a2->user)); if (a1->flags & M_ACCT_USER) @@ -130,6 +135,16 @@ void mutt_account_tourl (ACCOUNT* accoun } #endif +#ifdef USE_NNTP + if (account->type == M_ACCT_TYPE_NNTP) + { + if (account->flags & M_ACCT_SSL) + url->scheme = U_NNTPS; + else + url->scheme = U_NNTP; + } +#endif + url->host = account->host; if (account->flags & M_ACCT_PORT) url->port = account->port; @@ -155,6 +170,10 @@ int mutt_account_getuser (ACCOUNT* accou else if ((account->type == M_ACCT_TYPE_POP) && PopUser) strfcpy (account->user, PopUser, sizeof (account->user)); #endif +#ifdef USE_NNTP + else if ((account->type == M_ACCT_TYPE_NNTP) && NntpUser) + strfcpy (account->user, NntpUser, sizeof (account->user)); +#endif /* prompt (defaults to unix username), copy into account->user */ else { @@ -215,6 +234,10 @@ int mutt_account_getpass (ACCOUNT* accou else if ((account->type == M_ACCT_TYPE_SMTP) && SmtpPass) strfcpy (account->pass, SmtpPass, sizeof (account->pass)); #endif +#ifdef USE_NNTP + else if ((account->type == M_ACCT_TYPE_NNTP) && NntpPass) + strfcpy (account->pass, NntpPass, sizeof (account->pass)); +#endif else { snprintf (prompt, sizeof (prompt), _("Password for %s@%s: "), diff -udprP mutt-1.5.20.orig/account.h mutt-1.5.20/account.h --- mutt-1.5.20.orig/account.h 2008-11-11 21:55:46.000000000 +0200 +++ mutt-1.5.20/account.h 2009-06-15 21:05:24.000000000 +0300 @@ -29,7 +29,8 @@ enum M_ACCT_TYPE_NONE = 0, M_ACCT_TYPE_IMAP, M_ACCT_TYPE_POP, - M_ACCT_TYPE_SMTP + M_ACCT_TYPE_SMTP, + M_ACCT_TYPE_NNTP }; /* account flags */ diff -udprP mutt-1.5.20.orig/attach.h mutt-1.5.20/attach.h --- mutt-1.5.20.orig/attach.h 2008-11-11 21:55:46.000000000 +0200 +++ mutt-1.5.20/attach.h 2009-06-15 21:05:24.000000000 +0300 @@ -50,7 +50,7 @@ void mutt_print_attachment_list (FILE *f void mutt_attach_bounce (FILE *, HEADER *, ATTACHPTR **, short, BODY *); void mutt_attach_resend (FILE *, HEADER *, ATTACHPTR **, short, BODY *); -void mutt_attach_forward (FILE *, HEADER *, ATTACHPTR **, short, BODY *); +void mutt_attach_forward (FILE *, HEADER *, ATTACHPTR **, short, BODY *, int); void mutt_attach_reply (FILE *, HEADER *, ATTACHPTR **, short, BODY *, int); #endif /* _ATTACH_H_ */ diff -udprP mutt-1.5.20.orig/browser.c mutt-1.5.20/browser.c --- mutt-1.5.20.orig/browser.c 2009-06-11 20:52:54.000000000 +0300 +++ mutt-1.5.20/browser.c 2009-06-15 21:05:24.000000000 +0300 @@ -32,6 +32,9 @@ #ifdef USE_IMAP #include "imap.h" #endif +#ifdef USE_NNTP +#include "nntp.h" +#endif #include <stdlib.h> #include <dirent.h> @@ -49,6 +52,19 @@ static struct mapping_t FolderHelp[] = { { NULL, 0 } }; +#ifdef USE_NNTP +static struct mapping_t FolderNewsHelp[] = { + { N_("Exit"), OP_EXIT }, + { N_("List"), OP_TOGGLE_MAILBOXES }, + { N_("Subscribe"), OP_BROWSER_SUBSCRIBE }, + { N_("Unsubscribe"), OP_BROWSER_UNSUBSCRIBE }, + { N_("Catchup"), OP_CATCHUP }, + { N_("Mask"), OP_ENTER_MASK }, + { N_("Help"), OP_HELP }, + { NULL } +}; +#endif + typedef struct folder_t { struct folder_file *ff; @@ -114,9 +130,17 @@ static void browser_sort (struct browser case SORT_ORDER: return; case SORT_DATE: +#ifdef USE_NNTP + if (option (OPTNEWS)) + return; +#endif f = browser_compare_date; break; case SORT_SIZE: +#ifdef USE_NNTP + if (option (OPTNEWS)) + return; +#endif f = browser_compare_size; break; case SORT_SUBJECT: @@ -307,8 +331,106 @@ folder_format_str (char *dest, size_t de return (src); } +#ifdef USE_NNTP +static const char * +newsgroup_format_str (char *dest, size_t destlen, size_t col, char op, const char *src, + const char *fmt, const char *ifstring, const char *elsestring, + unsigned long data, format_flag flags) +{ + char fn[SHORT_STRING], tmp[SHORT_STRING]; + FOLDER *folder = (FOLDER *) data; + + switch (op) + { + case 'C': + snprintf (tmp, sizeof (tmp), "%%%sd", fmt); + snprintf (dest, destlen, tmp, folder->num + 1); + break; + + case 'f': + strncpy (fn, folder->ff->name, sizeof(fn) - 1); + snprintf (tmp, sizeof (tmp), "%%%ss", fmt); + snprintf (dest, destlen, tmp, fn); + break; + + case 'N': + snprintf (tmp, sizeof (tmp), "%%%sc", fmt); + if (folder->ff->nd->subscribed) + snprintf (dest, destlen, tmp, ' '); + else + snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : 'u'); + break; + + case 'M': + snprintf (tmp, sizeof (tmp), "%%%sc", fmt); + if (folder->ff->nd->deleted) + snprintf (dest, destlen, tmp, 'D'); + else + snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-'); + break; + + case 's': + if (flags & M_FORMAT_OPTIONAL) + { + if (folder->ff->nd->unread != 0) + mutt_FormatString (dest, destlen, col, ifstring, newsgroup_format_str, + data, flags); + else + mutt_FormatString (dest, destlen, col, elsestring, newsgroup_format_str, + data, flags); + } + else if (Context && Context->data == folder->ff->nd) + { + snprintf (tmp, sizeof (tmp), "%%%sd", fmt); + snprintf (dest, destlen, tmp, Context->unread); + } + else + { + snprintf (tmp, sizeof (tmp), "%%%sd", fmt); + snprintf (dest, destlen, tmp, folder->ff->nd->unread); + } + break; + + case 'n': + if (Context && Context->data == folder->ff->nd) + { + snprintf (tmp, sizeof (tmp), "%%%sd", fmt); + snprintf (dest, destlen, tmp, Context->new); + } + else if (option (OPTMARKOLD) && + folder->ff->nd->lastCached >= folder->ff->nd->firstMessage && + folder->ff->nd->lastCached <= folder->ff->nd->lastMessage) + { + snprintf (tmp, sizeof (tmp), "%%%sd", fmt); + snprintf (dest, destlen, tmp, folder->ff->nd->lastMessage - folder->ff->nd->lastCached); + } + else + { + snprintf (tmp, sizeof (tmp), "%%%sd", fmt); + snprintf (dest, destlen, tmp, folder->ff->nd->unread); + } + break; + + case 'd': + if (folder->ff->nd->desc != NULL) + { + snprintf (tmp, sizeof (tmp), "%%%ss", fmt); + snprintf (dest, destlen, tmp, folder->ff->nd->desc); + } + else + { + snprintf (tmp, sizeof (tmp), "%%%ss", fmt); + snprintf (dest, destlen, tmp, ""); + } + break; + } + return (src); +} +#endif /* USE_NNTP */ + static void add_folder (MUTTMENU *m, struct browser_state *state, - const char *name, const struct stat *s, int new) + const char *name, const struct stat *s, + void *data, int new) { if (state->entrylen == state->entrymax) { @@ -337,6 +459,10 @@ static void add_folder (MUTTMENU *m, str #ifdef USE_IMAP (state->entry)[state->entrylen].imap = 0; #endif +#ifdef USE_NNTP + if (option (OPTNEWS)) + (state->entry)[state->entrylen].nd = (NNTP_DATA *) data; +#endif (state->entrylen)++; } @@ -352,9 +478,35 @@ static void init_state (struct browser_s menu->data = state->entry; } +/* get list of all files/newsgroups with mask */ static int examine_directory (MUTTMENU *menu, struct browser_state *state, char *d, const char *prefix) { +#ifdef USE_NNTP + if (option (OPTNEWS)) + { + LIST *tmp; + NNTP_DATA *data; + NNTP_SERVER *news = CurrentNewsSrv; + +/* mutt_buffy_check (0); */ + init_state (state, menu); + + for (tmp = news->list; tmp; tmp = tmp->next) + { + if (!(data = (NNTP_DATA *)tmp->data)) + continue; + if (prefix && *prefix && strncmp (prefix, data->group, + strlen (prefix)) != 0) + continue; + if (!((regexec (Mask.rx, data->group, 0, NULL, 0) == 0) ^ Mask.not)) + continue; + add_folder (menu, state, data->group, NULL, data, data->new); + } + } + else +#endif /* USE_NNTP */ + { struct stat s; DIR *dp; struct dirent *de; @@ -415,17 +567,40 @@ static int examine_directory (MUTTMENU * tmp = Incoming; while (tmp && mutt_strcmp (buffer, tmp->path)) tmp = tmp->next; - add_folder (menu, state, de->d_name, &s, (tmp) ? tmp->new : 0); + add_folder (menu, state, de->d_name, &s, NULL, (tmp) ? tmp->new : 0); + } + closedir (dp); } - closedir (dp); browser_sort (state); return 0; } +/* get list of mailboxes/subscribed newsgroups */ static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state) { struct stat s; char buffer[LONG_STRING]; + +#ifdef USE_NNTP + if (option (OPTNEWS)) + { + LIST *tmp; + NNTP_DATA *data; + NNTP_SERVER *news = CurrentNewsSrv; + +/* mutt_buffy_check (0); */ + init_state (state, menu); + + for (tmp = news->list; tmp; tmp = tmp->next) + { + if ((data = (NNTP_DATA *) tmp->data) != NULL && (data->new || + (data->subscribed && (!option (OPTSHOWONLYUNREAD) || data->unread)))) + add_folder (menu, state, data->group, NULL, data, data->new); + } + } + else +#endif + { BUFFY *tmp = Incoming; #ifdef USE_IMAP struct mailbox_state mbox; @@ -443,14 +618,21 @@ static int examine_mailboxes (MUTTMENU * if (mx_is_imap (tmp->path)) { imap_mailbox_state (tmp->path, &mbox); - add_folder (menu, state, tmp->path, NULL, mbox.new); + add_folder (menu, state, tmp->path, NULL, NULL, mbox.new); continue; } #endif #ifdef USE_POP if (mx_is_pop (tmp->path)) { - add_folder (menu, state, tmp->path, NULL, tmp->new); + add_folder (menu, state, tmp->path, NULL, NULL, tmp->new); + continue; + } +#endif +#ifdef USE_NNTP + if (mx_is_nntp (tmp->path)) + { + add_folder (menu, state, tmp->path, NULL, NULL, tmp->new); continue; } #endif @@ -479,15 +661,20 @@ static int examine_mailboxes (MUTTMENU * strfcpy (buffer, NONULL(tmp->path), sizeof (buffer)); mutt_pretty_mailbox (buffer, sizeof (buffer)); - add_folder (menu, state, buffer, &s, tmp->new); + add_folder (menu, state, buffer, &s, NULL, tmp->new); } while ((tmp = tmp->next)); + } browser_sort (state); return 0; } static int select_file_search (MUTTMENU *menu, regex_t *re, int n) { +#ifdef USE_NNTP + if (option (OPTNEWS)) + return (regexec (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0)); +#endif return (regexec (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0)); } @@ -498,6 +685,12 @@ static void folder_entry (char *s, size_ folder.ff = &((struct folder_file *) menu->data)[num]; folder.num = num; +#ifdef USE_NNTP + if (option (OPTNEWS)) + mutt_FormatString (s, slen, 0, NONULL(GroupFormat), newsgroup_format_str, + (unsigned long) &folder, M_FORMAT_ARROWCURSOR); + else +#endif mutt_FormatString (s, slen, 0, NONULL(FolderFormat), folder_format_str, (unsigned long) &folder, M_FORMAT_ARROWCURSOR); } @@ -518,6 +711,17 @@ static void init_menu (struct browser_st menu->tagged = 0; +#ifdef USE_NNTP + if (option (OPTNEWS)) + { + if (buffy) + snprintf (title, titlelen, _("Subscribed newsgroups")); + else + snprintf (title, titlelen, _("Newsgroups on server [%s]"), + CurrentNewsSrv->conn->account.host); + } + else +#endif if (buffy) snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0)); else @@ -573,6 +777,31 @@ void _mutt_select_file (char *f, size_t if (!folder) strfcpy (LastDirBackup, LastDir, sizeof (LastDirBackup)); +#ifdef USE_NNTP + if (option (OPTNEWS)) + { + if (*f) + strfcpy (prefix, f, sizeof (prefix)); + else + { + LIST *list; + + /* default state for news reader mode is browse subscribed newsgroups */ + buffy = 0; + for (list = CurrentNewsSrv->list; list; list = list->next) + { + NNTP_DATA *data = (NNTP_DATA *) list->data; + + if (data && data->subscribed) + { + buffy = 1; + break; + } + } + } + } + else +#endif if (*f) { mutt_expand_path (f, flen); @@ -669,6 +898,9 @@ void _mutt_select_file (char *f, size_t menu->tag = file_tag; menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER, +#ifdef USE_NNTP + (option (OPTNEWS)) ? FolderNewsHelp : +#endif FolderHelp); init_menu (&state, menu, title, sizeof (title), buffy); @@ -807,7 +1039,11 @@ void _mutt_select_file (char *f, size_t } } +#ifdef USE_NNTP + if (buffy || option (OPTNEWS)) /* news have not path */ +#else if (buffy) +#endif { strfcpy (f, state.entry[menu->current].name, flen); mutt_expand_path (f, flen); @@ -865,14 +1101,6 @@ void _mutt_select_file (char *f, size_t break; #ifdef USE_IMAP - case OP_BROWSER_SUBSCRIBE: - imap_subscribe (state.entry[menu->current].name, 1); - break; - - case OP_BROWSER_UNSUBSCRIBE: - imap_subscribe (state.entry[menu->current].name, 0); - break; - case OP_BROWSER_TOGGLE_LSUB: if (option (OPTIMAPLSUB)) unset_option (OPTIMAPLSUB); @@ -973,6 +1201,11 @@ void _mutt_select_file (char *f, size_t case OP_CHANGE_DIRECTORY: +#ifdef USE_NNTP + if (option (OPTNEWS)) + break; +#endif + strfcpy (buf, LastDir, sizeof (buf)); #ifdef USE_IMAP if (!state.imap_browse) @@ -1239,6 +1472,190 @@ void _mutt_select_file (char *f, size_t else mutt_error _("Error trying to view file"); } + break; + +#ifdef USE_NNTP + case OP_CATCHUP: + case OP_UNCATCHUP: + if (option (OPTNEWS)) + { + struct folder_file *f = &state.entry[menu->current]; + NNTP_DATA *nd; + + if (i == OP_CATCHUP) + nd = mutt_newsgroup_catchup (CurrentNewsSrv, f->name); + else + nd = mutt_newsgroup_uncatchup (CurrentNewsSrv, f->name); + + if (nd) + { +/* FOLDER folder; + struct folder_file ff; + char buffer[_POSIX_PATH_MAX + SHORT_STRING]; + + folder.ff = &ff; + folder.ff->name = f->name; + folder.ff->st = NULL; + folder.ff->is_new = nd->new; + folder.ff->nd = nd; + FREE (&f->desc); + mutt_FormatString (buffer, sizeof (buffer), 0, NONULL(GroupFormat), + newsgroup_format_str, (unsigned long) &folder, + M_FORMAT_ARROWCURSOR); + f->desc = safe_strdup (buffer); */ + if (menu->current + 1 < menu->max) + menu->current++; + menu->redraw = REDRAW_MOTION_RESYNCH; + } + } + break; + + case OP_LOAD_ACTIVE: + if (!option (OPTNEWS)) + break; + + { + LIST *tmp; + NNTP_DATA *data; + + for (tmp = CurrentNewsSrv->list; tmp; tmp = tmp->next) + { + if ((data = (NNTP_DATA *)tmp->data)) + data->deleted = 1; + } + } + nntp_get_active (CurrentNewsSrv); + + destroy_state (&state); + if (buffy) + examine_mailboxes (menu, &state); + else + examine_directory (menu, &state, NULL, NULL); + init_menu (&state, menu, title, sizeof (title), buffy); + break; +#endif /* USE_NNTP */ + +#if defined USE_IMAP || defined USE_NNTP + case OP_BROWSER_SUBSCRIBE: + case OP_BROWSER_UNSUBSCRIBE: +#endif +#ifdef USE_NNTP + case OP_SUBSCRIBE_PATTERN: + case OP_UNSUBSCRIBE_PATTERN: + if (option (OPTNEWS)) + { + regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t)); + char *s = buf; + int j = menu->current; + NNTP_DATA *nd; + NNTP_SERVER *news = CurrentNewsSrv; + + if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN) + { + char tmp[STRING]; + int err; + + buf[0] = 0; + if (i == OP_SUBSCRIBE_PATTERN) + snprintf (tmp, sizeof (tmp), _("Subscribe pattern: ")); + else + snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: ")); + if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0]) + { + FREE (&rx); + break; + } + + if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) + { + regerror (err, rx, buf, sizeof (buf)); + regfree (rx); + FREE (&rx); + mutt_error ("%s", buf); + break; + } + menu->redraw = REDRAW_FULL; + j = 0; + } + else if (!state.entrylen) + { + mutt_error _("No newsgroups match the mask"); + break; + } + + for ( ; j < state.entrylen; j++) + { + struct folder_file *f = &state.entry[j]; + + if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE || + regexec (rx, f->name, 0, NULL, 0) == 0) + { + if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN) + nd = mutt_newsgroup_subscribe (news, f->name); + else + nd = mutt_newsgroup_unsubscribe (news, f->name); +/* if (nd) + { + FOLDER folder; + char buffer[_POSIX_PATH_MAX + SHORT_STRING]; + + folder.name = f->name; + folder.f = NULL; + folder.new = nd->new; + folder.nd = nd; + FREE (&f->desc); + mutt_FormatString (buffer, sizeof (buffer), 0, NONULL(GroupFormat), + newsgroup_format_str, (unsigned long) &folder, + M_FORMAT_ARROWCURSOR); + f->desc = safe_strdup (buffer); + } */ + } + if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE) + { + if (menu->current + 1 < menu->max) + menu->current++; + menu->redraw = REDRAW_MOTION_RESYNCH; + break; + } + } + if (i == OP_SUBSCRIBE_PATTERN) + { + LIST *grouplist = NULL; + + if (news) + grouplist = news->list; + for (; grouplist; grouplist = grouplist->next) + { + nd = (NNTP_DATA *) grouplist->data; + if (nd && nd->group && !nd->subscribed) + { + if (regexec (rx, nd->group, 0, NULL, 0) == 0) + { + mutt_newsgroup_subscribe (news, nd->group); + add_folder (menu, &state, nd->group, NULL, nd, nd->new); + } + } + } + init_menu (&state, menu, title, sizeof (title), buffy); + } + mutt_newsrc_update (news); + nntp_clear_cacheindex (news); + if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE) + regfree (rx); + FREE (&rx); + } +#ifdef USE_IMAP + else +#endif /* USE_IMAP && USE_NNTP */ +#endif /* USE_NNTP */ +#ifdef USE_IMAP + { + if (i == OP_BROWSER_SUBSCRIBE) + imap_subscribe (state.entry[menu->current].name, 1); + else + imap_subscribe (state.entry[menu->current].name, 0); + } +#endif /* USE_IMAP */ } } diff -udprP mutt-1.5.20.orig/browser.h mutt-1.5.20/browser.h --- mutt-1.5.20.orig/browser.h 2009-01-05 00:34:12.000000000 +0200 +++ mutt-1.5.20/browser.h 2009-06-15 21:05:24.000000000 +0300 @@ -19,6 +19,10 @@ #ifndef _BROWSER_H #define _BROWSER_H 1 +#ifdef USE_NNTP +#include "nntp.h" +#endif + struct folder_file { mode_t mode; @@ -37,6 +41,9 @@ struct folder_file unsigned selectable : 1; unsigned inferiors : 1; #endif +#ifdef USE_NNTP + NNTP_DATA *nd; +#endif unsigned tagged : 1; }; diff -udprP mutt-1.5.20.orig/buffy.c mutt-1.5.20/buffy.c --- mutt-1.5.20.orig/buffy.c 2009-06-02 20:16:26.000000000 +0300 +++ mutt-1.5.20/buffy.c 2009-06-15 21:05:24.000000000 +0300 @@ -320,6 +320,9 @@ int mutt_buffy_check (int force) #ifdef USE_POP if (!Context || Context->magic != M_POP) #endif +#ifdef USE_NNTP + if (!Context || Context->magic != M_NNTP) +#endif /* check device ID and serial number instead of comparing paths */ if (!Context || !Context->path || stat (Context->path, &contex_sb) != 0) { @@ -343,6 +346,11 @@ int mutt_buffy_check (int force) tmp->magic = M_POP; else #endif +#ifdef USE_NNTP + if ((tmp->magic == M_NNTP) || mx_is_nntp (tmp->path)) + tmp->magic = M_NNTP; + else +#endif if (stat (tmp->path, &sb) != 0 || (S_ISREG(sb.st_mode) && sb.st_size == 0) || (!tmp->magic && (tmp->magic = mx_get_magic (tmp->path)) <= 0)) { @@ -360,25 +368,21 @@ int mutt_buffy_check (int force) /* check to see if the folder is the currently selected folder * before polling */ if (!Context || !Context->path || -#if defined USE_IMAP || defined USE_POP - (( + ( + (0 #ifdef USE_IMAP - tmp->magic == M_IMAP + || tmp->magic == M_IMAP #endif #ifdef USE_POP -#ifdef USE_IMAP - || -#endif - tmp->magic == M_POP -#endif - ) ? mutt_strcmp (tmp->path, Context->path) : + || tmp->magic == M_POP #endif - (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino) -#if defined USE_IMAP || defined USE_POP - ) +#ifdef USE_NNTP + || tmp->magic == M_NNTP #endif - ) - + ) ? mutt_strcmp (tmp->path, Context->path) : + (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino) + ) + ) { switch (tmp->magic) { diff -udprP mutt-1.5.20.orig/complete.c mutt-1.5.20/complete.c --- mutt-1.5.20.orig/complete.c 2009-01-05 00:38:16.000000000 +0200 +++ mutt-1.5.20/complete.c 2009-06-15 21:05:24.000000000 +0300 @@ -25,6 +25,9 @@ #include "mailbox.h" #include "imap.h" #endif +#ifdef USE_NNTP +#include "nntp.h" +#endif #include <dirent.h> #include <string.h> @@ -48,9 +51,71 @@ int mutt_complete (char *s, size_t slen) char filepart[_POSIX_PATH_MAX]; #ifdef USE_IMAP char imap_path[LONG_STRING]; +#endif dprint (2, (debugfile, "mutt_complete: completing %s\n", s)); +#ifdef USE_NNTP + if (option (OPTNEWS)) + { + LIST *l = CurrentNewsSrv->list; + + strfcpy (filepart, s, sizeof (filepart)); + + /* + * special case to handle when there is no filepart yet. + * find the first subscribed newsgroup + */ + if ((len = mutt_strlen (filepart)) == 0) + { + for (; l; l = l->next) + { + NNTP_DATA *data = (NNTP_DATA *)l->data; + + if (data && data->subscribed) + { + strfcpy (filepart, data->group, sizeof (filepart)); + init++; + l = l->next; + break; + } + } + } + + for (; l; l = l->next) + { + NNTP_DATA *data = (NNTP_DATA *)l->data; + + if (data && data->subscribed && + mutt_strncmp (data->group, filepart, len) == 0) + { + if (init) + { + for (i = 0; filepart[i] && data->group[i]; i++) + { + if (filepart[i] != data->group[i]) + { + filepart[i] = 0; + break; + } + } + filepart[i] = 0; + } + else + { + strfcpy (filepart, data->group, sizeof (filepart)); + init = 1; + } + } + } + + strcpy (s, filepart); + + return (init ? 0 : -1); + } +#endif + +#ifdef USE_IMAP /* we can use '/' as a delimiter, imap_complete rewrites it */ if (*s == '=' || *s == '+' || *s == '!') { diff -udprP mutt-1.5.20.orig/compose.c mutt-1.5.20/compose.c --- mutt-1.5.20.orig/compose.c 2009-03-31 09:52:43.000000000 +0300 +++ mutt-1.5.20/compose.c 2009-06-15 21:05:24.000000000 +0300 @@ -32,10 +32,15 @@ #include "mailbox.h" #include "sort.h" #include "charset.h" +#include "mx.h" #ifdef MIXMASTER #include "remailer.h" #endif + +#ifdef USE_NNTP +#include "nntp.h" +#endif #include <errno.h> #include <string.h> @@ -60,18 +65,21 @@ enum HDR_REPLYTO, HDR_FCC, -#ifdef MIXMASTER - HDR_MIX, -#endif HDR_CRYPT, HDR_CRYPTINFO, +#ifdef USE_NNTP + HDR_NEWSGROUPS, + HDR_FOLLOWUPTO, + HDR_XCOMMENTTO, +#endif + HDR_ATTACH = (HDR_FCC + 5) /* where to start printing the attachments */ }; -#define HDR_XOFFSET 10 -#define TITLE_FMT "%10s" /* Used for Prompts, which are ASCII */ +#define HDR_XOFFSET 14 +#define TITLE_FMT "%14s" /* Used for Prompts, which are ASCII */ #define W (COLS - HDR_XOFFSET) static char *Prompts[] = @@ -83,6 +91,16 @@ static char *Prompts[] = "Subject: ", "Reply-To: ", "Fcc: " +#ifdef USE_NNTP +#ifdef MIXMASTER + ,"" +#endif + ,"" + ,"" + ,"Newsgroups: " + ,"Followup-To: " + ,"X-Comment-To: " +#endif }; static struct mapping_t ComposeHelp[] = { @@ -97,6 +115,19 @@ static struct mapping_t ComposeHelp[] = { NULL, 0 } }; +#ifdef USE_NNTP +static struct mapping_t ComposeNewsHelp[] = { + { N_("Send"), OP_COMPOSE_SEND_MESSAGE }, + { N_("Abort"), OP_EXIT }, + { "Newsgroups", OP_COMPOSE_EDIT_NEWSGROUPS }, + { "Subj", OP_COMPOSE_EDIT_SUBJECT }, + { N_("Attach file"), OP_COMPOSE_ATTACH_FILE }, + { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION }, + { N_("Help"), OP_HELP }, + { NULL } +}; +#endif + static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num) { mutt_FormatString (b, blen, 0, NONULL (AttachFormat), mutt_attach_fmt, @@ -115,16 +146,16 @@ static void redraw_crypt_lines (HEADER * if ((WithCrypto & APPLICATION_PGP) && (WithCrypto & APPLICATION_SMIME)) { if (!msg->security) - mvaddstr (HDR_CRYPT, 0, "Security: "); + mvaddstr (HDR_CRYPT, 0, " Security: "); else if (msg->security & APPLICATION_SMIME) - mvaddstr (HDR_CRYPT, 0, " S/MIME: "); + mvaddstr (HDR_CRYPT, 0, " S/MIME: "); else if (msg->security & APPLICATION_PGP) - mvaddstr (HDR_CRYPT, 0, " PGP: "); + mvaddstr (HDR_CRYPT, 0, " PGP: "); } else if ((WithCrypto & APPLICATION_SMIME)) - mvaddstr (HDR_CRYPT, 0, " S/MIME: "); + mvaddstr (HDR_CRYPT, 0, " S/MIME: "); else if ((WithCrypto & APPLICATION_PGP)) - mvaddstr (HDR_CRYPT, 0, " PGP: "); + mvaddstr (HDR_CRYPT, 0, " PGP: "); else return; @@ -252,9 +283,28 @@ static void draw_envelope_addr (int line static void draw_envelope (HEADER *msg, char *fcc) { draw_envelope_addr (HDR_FROM, msg->env->from); +#ifdef USE_NNTP + if (!option (OPTNEWSSEND)) + { +#endif draw_envelope_addr (HDR_TO, msg->env->to); draw_envelope_addr (HDR_CC, msg->env->cc); draw_envelope_addr (HDR_BCC, msg->env->bcc); +#ifdef USE_NNTP + } + else + { + mvprintw (HDR_TO, 0, TITLE_FMT , Prompts[HDR_NEWSGROUPS - 1]); + mutt_paddstr (W, NONULL (msg->env->newsgroups)); + mvprintw (HDR_CC, 0, TITLE_FMT , Prompts[HDR_FOLLOWUPTO - 1]); + mutt_paddstr (W, NONULL (msg->env->followup_to)); + if (option (OPTXCOMMENTTO)) + { + mvprintw (HDR_BCC, 0, TITLE_FMT , Prompts[HDR_XCOMMENTTO - 1]); + mutt_paddstr (W, NONULL (msg->env->x_comment_to)); + } + } +#endif mvprintw (HDR_SUBJECT, 0, TITLE_FMT, Prompts[HDR_SUBJECT - 1]); mutt_paddstr (W, NONULL (msg->env->subject)); draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to); @@ -507,6 +557,12 @@ int mutt_compose_menu (HEADER *msg, /* /* Sort, SortAux could be changed in mutt_index_menu() */ int oldSort, oldSortAux; struct stat st; +#ifdef USE_NNTP + int news = 0; /* is it a news article ? */ + + if (option (OPTNEWSSEND)) + news++; +#endif mutt_attach_init (msg->content); idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1); @@ -517,10 +573,18 @@ int mutt_compose_menu (HEADER *msg, /* menu->make_entry = snd_entry; menu->tag = mutt_tag_attach; menu->data = idx; +#ifdef USE_NNTP + if (news) + menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeNewsHelp); + else +#endif menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp); while (loop) { +#ifdef USE_NNTP + unset_option (OPTNEWS); /* for any case */ +#endif switch (op = mutt_menuLoop (menu)) { case OP_REDRAW: @@ -533,17 +597,87 @@ int mutt_compose_menu (HEADER *msg, /* mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_TO: +#ifdef USE_NNTP + if (news) + break; +#endif menu->redraw = edit_address_list (HDR_TO, &msg->env->to); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_BCC: +#ifdef USE_NNTP + if (news) + break; +#endif menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_CC: +#ifdef USE_NNTP + if (news) + break; +#endif menu->redraw = edit_address_list (HDR_CC, &msg->env->cc); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; +#ifdef USE_NNTP + case OP_COMPOSE_EDIT_NEWSGROUPS: + if (news) + { + if (msg->env->newsgroups) + strfcpy (buf, msg->env->newsgroups, sizeof (buf)); + else + buf[0] = 0; + if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) == 0 && buf[0]) + { + FREE (&msg->env->newsgroups); + mutt_remove_trailing_ws (buf); + msg->env->newsgroups = safe_strdup (mutt_skip_whitespace (buf)); + move (HDR_TO, HDR_XOFFSET); + clrtoeol (); + if (msg->env->newsgroups) + printw ("%-*.*s", W, W, msg->env->newsgroups); + } + } + break; + + case OP_COMPOSE_EDIT_FOLLOWUP_TO: + if (news) + { + buf[0] = 0; + if (msg->env->followup_to) + strfcpy (buf, msg->env->followup_to, sizeof (buf)); + if (mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) == 0 && buf[0]) + { + FREE (&msg->env->followup_to); + mutt_remove_trailing_ws (buf); + msg->env->followup_to = safe_strdup (mutt_skip_whitespace (buf)); + move (HDR_CC, HDR_XOFFSET); + clrtoeol(); + if (msg->env->followup_to) + printw ("%-*.*s", W, W, msg->env->followup_to); + } + } + break; + + case OP_COMPOSE_EDIT_X_COMMENT_TO: + if (news && option (OPTXCOMMENTTO)) + { + buf[0] = 0; + if (msg->env->x_comment_to) + strfcpy (buf, msg->env->x_comment_to, sizeof (buf)); + if (mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) == 0 && buf[0]) + { + FREE (&msg->env->x_comment_to); + msg->env->x_comment_to = safe_strdup (buf); + move (HDR_BCC, HDR_XOFFSET); + clrtoeol(); + if (msg->env->x_comment_to) + printw ("%-*.*s", W, W, msg->env->x_comment_to); + } + } + break; +#endif case OP_COMPOSE_EDIT_SUBJECT: if (msg->env->subject) strfcpy (buf, msg->env->subject, sizeof (buf)); @@ -706,6 +840,9 @@ int mutt_compose_menu (HEADER *msg, /* break; case OP_COMPOSE_ATTACH_MESSAGE: +#ifdef USE_NNTP + case OP_COMPOSE_ATTACH_NEWS_MESSAGE: +#endif { char *prompt; HEADER *h; @@ -713,7 +850,22 @@ int mutt_compose_menu (HEADER *msg, /* fname[0] = 0; prompt = _("Open mailbox to attach message from"); +#ifdef USE_NNTP + unset_option (OPTNEWS); + if (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE) + { + if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer))) + break; + + prompt = _("Open newsgroup to attach message from"); + set_option (OPTNEWS); + } +#endif + if (Context) +#ifdef USE_NNTP + if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->magic == M_NNTP)) +#endif { strfcpy (fname, NONULL (Context->path), sizeof (fname)); mutt_pretty_mailbox (fname, sizeof (fname)); @@ -722,6 +874,11 @@ int mutt_compose_menu (HEADER *msg, /* if (mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 1) == -1 || !fname[0]) break; +#ifdef USE_NNTP + if (option (OPTNEWS)) + nntp_expand_path (fname, sizeof (fname), &CurrentNewsSrv->conn->account); + else +#endif mutt_expand_path (fname, sizeof (fname)); #ifdef USE_IMAP if (!mx_is_imap (fname)) @@ -729,6 +886,9 @@ int mutt_compose_menu (HEADER *msg, /* #ifdef USE_POP if (!mx_is_pop (fname)) #endif +#ifdef USE_NNTP + if (!mx_is_nntp (fname) && !option (OPTNEWS)) +#endif /* check to make sure the file exists and is readable */ if (access (fname, R_OK) == -1) { diff -udprP mutt-1.5.20.orig/config.h.in mutt-1.5.20/config.h.in --- mutt-1.5.20.orig/config.h.in 2009-06-09 09:51:15.000000000 +0300 +++ mutt-1.5.20/config.h.in 2009-06-15 21:05:24.000000000 +0300 @@ -37,6 +37,9 @@ significant more memory when defined. */ #undef EXACT_ADDRESS +/* Compiling with newsreading support with NNTP */ +#undef USE_NNTP + /* program to use for shell commands */ #undef EXECSHELL diff -udprP mutt-1.5.20.orig/configure.ac mutt-1.5.20/configure.ac --- mutt-1.5.20.orig/configure.ac 2009-06-09 09:50:33.000000000 +0300 +++ mutt-1.5.20/configure.ac 2009-06-15 21:05:24.000000000 +0300 @@ -599,6 +599,14 @@ if test x"$need_imap" = xyes -o x"$need_ MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS bcache.o" fi +AC_ARG_ENABLE(nntp, [ --enable-nntp Enable NNTP support], +[ if test x$enableval = xyes ; then + AC_DEFINE(USE_NNTP,1,[ Define if you want support for the NNTP protocol. ]) + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS nntp.o newsrc.o" + need_socket="yes" + fi +]) + dnl -- end socket dependencies -- if test "$need_socket" = "yes" diff -udprP mutt-1.5.20.orig/curs_main.c mutt-1.5.20/curs_main.c --- mutt-1.5.20.orig/curs_main.c 2009-06-14 05:48:36.000000000 +0300 +++ mutt-1.5.20/curs_main.c 2009-06-15 21:47:09.000000000 +0300 @@ -22,6 +22,7 @@ #include "mutt.h" #include "mutt_curses.h" +#include "mx.h" #include "mutt_menu.h" #include "mailbox.h" #include "mapping.h" @@ -38,6 +39,10 @@ #include "mutt_crypt.h" +#ifdef USE_NNTP +#include "nntp.h" +#endif + #include <ctype.h> #include <stdlib.h> @@ -413,12 +418,27 @@ static struct mapping_t IndexHelp[] = { { NULL, 0 } }; +#ifdef USE_NNTP +struct mapping_t IndexNewsHelp[] = { + { N_("Quit"), OP_QUIT }, + { N_("Del"), OP_DELETE }, + { N_("Undel"), OP_UNDELETE }, + { N_("Save"), OP_SAVE }, + { N_("Post"), OP_POST }, + { N_("Followup"), OP_FOLLOWUP }, + { N_("Catchup"), OP_CATCHUP }, + { N_("Help"), OP_HELP }, + { NULL } +}; +#endif + /* This function handles the message index window as well as commands returned * from the pager (MENU_PAGER). */ int mutt_index_menu (void) { char buf[LONG_STRING], helpstr[LONG_STRING]; + int flags; int op = OP_NULL; int done = 0; /* controls when to exit the "event" loop */ int i = 0, j; @@ -439,7 +459,11 @@ int mutt_index_menu (void) menu->make_entry = index_make_entry; menu->color = index_color; menu->current = ci_first_message (); - menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN, IndexHelp); + menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN, +#ifdef USE_NNTP + (Context && (Context->magic == M_NNTP)) ? IndexNewsHelp : +#endif + IndexHelp); if (!attach_msg) mutt_buffy_check(1); /* force the buffy check after we enter the folder */ @@ -690,6 +714,9 @@ int mutt_index_menu (void) imap_disallow_reopen (Context); #endif +#ifdef USE_NNTP + unset_option (OPTNEWS); /* for any case */ +#endif switch (op) { @@ -740,6 +767,120 @@ int mutt_index_menu (void) menu_current_bottom (menu); break; +#ifdef USE_NNTP + case OP_GET_MESSAGE: + case OP_GET_PARENT: + CHECK_MSGCOUNT; + if (Context->magic == M_NNTP) + { + HEADER *h; + + if (op == OP_GET_MESSAGE) + { + buf[0] = 0; + if (mutt_get_field (_("Enter Message-Id: "), buf, sizeof (buf), 0) != 0 + || !buf[0]) + break; + } + else + { + LIST *ref = CURHDR->env->references; + if (!ref) + { + mutt_error _("Article has no parent reference!"); + break; + } + strfcpy (buf, ref->data, sizeof (buf)); + } + if (!Context->id_hash) + Context->id_hash = mutt_make_id_hash (Context); + if ((h = hash_find (Context->id_hash, buf))) + { + if (h->virtual != -1) + { + menu->current = h->virtual; + menu->redraw = REDRAW_MOTION_RESYNCH; + } + else if (h->collapsed) + { + mutt_uncollapse_thread (Context, h); + mutt_set_virtual (Context); + menu->current = h->virtual; + menu->redraw = REDRAW_MOTION_RESYNCH; + } + else + mutt_error _("Message not visible in limited view."); + } + else + { + if (nntp_check_msgid (Context, buf) == 0) + { + h = Context->hdrs[Context->msgcount-1]; + mutt_sort_headers (Context, 0); + menu->current = h->virtual; + menu->redraw = REDRAW_FULL; + } + else + mutt_error (_("Article %s not found on server"), buf); + } + } + break; + + case OP_GET_CHILDREN: + case OP_RECONSTRUCT_THREAD: + CHECK_MSGCOUNT; + if (Context->magic == M_NNTP) + { + HEADER *h; + int old = CURHDR->index, i; + + if (!CURHDR->env->message_id) + { + mutt_error _("No Message-Id. Unable to perform operation"); + break; + } + + if (!Context->id_hash) + Context->id_hash = mutt_make_id_hash (Context); + strfcpy (buf, CURHDR->env->message_id, sizeof (buf)); + + if (op == OP_RECONSTRUCT_THREAD) + { + LIST *ref = CURHDR->env->references; + while (ref) + { + nntp_check_msgid (Context, ref->data); + /* the last msgid in References is the root message */ + if (!ref->next) + strfcpy (buf, ref->data, sizeof (buf)); + ref = ref->next; + } + } + mutt_message _("Check for children of message..."); + if (nntp_check_children (Context, buf) == 0) + { + mutt_sort_headers (Context, (op == OP_RECONSTRUCT_THREAD)); + h = hash_find (Context->id_hash, buf); + /* if the root message was retrieved, move to it */ + if (h) + menu->current = h->virtual; + else /* try to restore old position */ + for (i = 0; i < Context->msgcount; i++) + if (Context->hdrs[i]->index == old) + { + menu->current = Context->hdrs[i]->virtual; + /* As an added courtesy, recenter the menu + * with the current entry at the middle of the screen */ + menu_check_recenter (menu); + menu_current_middle (menu); + } + } + menu->redraw = REDRAW_FULL; + mutt_clear_error (); + } + break; +#endif + case OP_JUMP: CHECK_MSGCOUNT; @@ -836,11 +977,33 @@ int mutt_index_menu (void) break; case OP_MAIN_LIMIT: + case OP_TOGGLE_READ: CHECK_IN_MAILBOX; menu->oldcurrent = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ? CURHDR->index : -1; - if (mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0) + if (op == OP_TOGGLE_READ) + { + char buf[LONG_STRING]; + + if (!Context->pattern || strncmp (Context->pattern, "!~R!~D~s", 8) != 0) + { + snprintf (buf, sizeof (buf), "!~R!~D~s%s", + Context->pattern ? Context->pattern : ".*"); + set_option (OPTHIDEREAD); + } + else + { + strfcpy (buf, Context->pattern + 8, sizeof(buf)); + if (!*buf || strncmp (buf, ".*", 2) == 0) + snprintf (buf, sizeof(buf), "~A"); + unset_option (OPTHIDEREAD); + } + FREE (&Context->pattern); + Context->pattern = safe_strdup (buf); + } + if ((op == OP_TOGGLE_READ && mutt_pattern_func (M_LIMIT, NULL) == 0) || + mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0) { if (menu->oldcurrent >= 0) { @@ -1057,15 +1220,22 @@ int mutt_index_menu (void) case OP_MAIN_CHANGE_FOLDER: case OP_MAIN_NEXT_UNREAD_MAILBOX: - - if (attach_msg) - op = OP_MAIN_CHANGE_FOLDER_READONLY; - - /* fallback to the readonly case */ - case OP_MAIN_CHANGE_FOLDER_READONLY: +#ifdef USE_NNTP + case OP_MAIN_CHANGE_GROUP: + case OP_MAIN_CHANGE_GROUP_READONLY: + unset_option (OPTNEWS); +#endif + if (attach_msg || option (OPTREADONLY) || +#ifdef USE_NNTP + op == OP_MAIN_CHANGE_GROUP_READONLY || +#endif + op == OP_MAIN_CHANGE_FOLDER_READONLY) + flags = M_READONLY; + else + flags = 0; - if ((op == OP_MAIN_CHANGE_FOLDER_READONLY) || option (OPTREADONLY)) + if (flags) cp = _("Open mailbox in read-only mode"); else cp = _("Open mailbox"); @@ -1084,6 +1254,21 @@ int mutt_index_menu (void) } else { +#ifdef USE_NNTP + if (op == OP_MAIN_CHANGE_GROUP || + op == OP_MAIN_CHANGE_GROUP_READONLY) + { + set_option (OPTNEWS); + if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer))) + break; + if (flags) + cp = _("Open newsgroup in read-only mode"); + else + cp = _("Open newsgroup"); + nntp_buffy (buf); + } + else +#endif mutt_buffy (buf, sizeof (buf)); if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) @@ -1103,6 +1288,14 @@ int mutt_index_menu (void) } } +#ifdef USE_NNTP + if (option (OPTNEWS)) + { + unset_option (OPTNEWS); + nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account); + } + else +#endif mutt_expand_path (buf, sizeof (buf)); if (mx_get_magic (buf) <= 0) { @@ -1140,15 +1333,18 @@ int mutt_index_menu (void) CurrentMenu = MENU_MAIN; mutt_folder_hook (buf); - if ((Context = mx_open_mailbox (buf, - (option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ? - M_READONLY : 0, NULL)) != NULL) + if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL) { menu->current = ci_first_message (); } else menu->current = 0; +#ifdef USE_NNTP + /* mutt_buffy_check() must be done with mail-reader mode! */ + menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN, + (Context && (Context->magic == M_NNTP)) ? IndexNewsHelp : IndexHelp); +#endif mutt_clear_error (); mutt_buffy_check(1); /* force the buffy check after we have changed the folder */ @@ -1519,6 +1715,15 @@ int mutt_index_menu (void) CHECK_READONLY; CHECK_ACL(M_ACL_WRITE, _("flag message")); +#ifdef USE_NNTP + if (Context->magic == M_NNTP) + { + mutt_flushinp (); + mutt_error _("Can't change 'important' flag on NNTP server."); + break; + } +#endif + if (tag) { for (j = 0; j < Context->vcount; j++) @@ -1866,6 +2071,17 @@ int mutt_index_menu (void) } break; +#ifdef USE_NNTP + case OP_CATCHUP: + if (Context && Context->magic == M_NNTP) + { + if (mutt_newsgroup_catchup (CurrentNewsSrv, + ((NNTP_DATA *)Context->data)->group)) + menu->redraw = REDRAW_INDEX | REDRAW_STATUS; + } + break; +#endif + case OP_DISPLAY_ADDRESS: CHECK_MSGCOUNT; @@ -1993,6 +2209,15 @@ int mutt_index_menu (void) menu->redraw = (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS; } #endif + +#ifdef USE_NNTP + if (Context->magic == M_NNTP) + { + mutt_flushinp (); + mutt_error _("Can't edit message on newsserver."); + break; + } +#endif MAYBE_REDRAW (menu->redraw); break; @@ -2065,6 +2290,41 @@ int mutt_index_menu (void) menu->redraw = REDRAW_FULL; break; +#ifdef USE_NNTP + case OP_FOLLOWUP: + case OP_FORWARD_TO_GROUP: + + CHECK_MSGCOUNT; + CHECK_VISIBLE; + + case OP_POST: + + CHECK_ATTACH; + if (op != OP_FOLLOWUP || !CURHDR->env->followup_to || + mutt_strcasecmp (CURHDR->env->followup_to, "poster") || + query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != M_YES) + { + if (Context && Context->magic == M_NNTP && + !((NNTP_DATA *)Context->data)->allowed && + query_quadoption (OPT_TOMODERATED, _("Posting to this group not allowed, may be moderated. Continue?")) != M_YES) + break; + if (op == OP_POST) + ci_send_message (SENDNEWS, NULL, NULL, Context, NULL); + else + { + CHECK_MSGCOUNT; + if (op == OP_FOLLOWUP) + ci_send_message (SENDNEWS|SENDREPLY, NULL, NULL, Context, + tag ? NULL : CURHDR); + else + ci_send_message (SENDNEWS|SENDFORWARD, NULL, NULL, Context, + tag ? NULL : CURHDR); + } + menu->redraw = REDRAW_FULL; + break; + } +#endif + case OP_REPLY: CHECK_ATTACH; @@ -2140,6 +2400,12 @@ int mutt_index_menu (void) CHECK_READONLY; CHECK_ACL(M_ACL_DELETE, _("undelete message(s)")); +#ifdef USE_NNTP + /* Close all open NNTP connections */ + if (!attach_msg) + nntp_logout_all (); +#endif + rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0, op == OP_UNDELETE_THREAD ? 0 : 1); diff -udprP mutt-1.5.20.orig/doc/manual.xml.head mutt-1.5.20/doc/manual.xml.head --- mutt-1.5.20.orig/doc/manual.xml.head 2009-05-30 20:20:08.000000000 +0300 +++ mutt-1.5.20/doc/manual.xml.head 2009-06-15 21:05:24.000000000 +0300 @@ -1568,6 +1568,22 @@ fo-table</literal> for details. </sect2> +<sect2> +<title>Reading news via NNTP</title> + +<para> +If compiled with <emphasis>--enable-nntp</emphasis> option, Mutt can +read news from newsserver via NNTP. You can open a newsgroup with +function ``change-newsgroup'' (default: ``i''). Default newsserver +can be obtained from <emphasis>NNTPSERVER</emphasis> environment +variable. Like other news readers, info about subscribed newsgroups +is saved in file by <link linkend="newsrc">$newsrc</link> +variable. Article headers are cached and can be loaded from file when +newsgroup entered instead loading from newsserver. +</para> + +</sect2> + </sect1> <sect1 id="forwarding-mail"> diff -udprP mutt-1.5.20.orig/doc/mutt.man mutt-1.5.20/doc/mutt.man --- mutt-1.5.20.orig/doc/mutt.man 2009-06-07 03:32:44.000000000 +0300 +++ mutt-1.5.20/doc/mutt.man 2009-06-15 21:07:47.000000000 +0300 @@ -23,8 +23,8 @@ mutt \- The Mutt Mail User Agent .SH SYNOPSIS .PP .B mutt -[-nRyzZ] -[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-m \fItype\fP] [\-f \fIfile\fP] +[-GnRyzZ] +[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-g \fIserver\fP] [\-m \fItype\fP] [\-f \fIfile\fP] .PP .B mutt [\-nx] @@ -101,6 +101,10 @@ files. Specify which mailbox to load. .IP "-F \fImuttrc\fP" Specify an initialization file to read instead of ~/.muttrc +.IP "-g \fIserver\fP" +Start Mutt with a listing of subscribed newsgroups at specified newsserver. +.IP "-G" +Start Mutt with a listing of subscribed newsgroups. .IP "-h" Display help. .IP "-H \fIdraft\fP" diff -udprP mutt-1.5.20.orig/functions.h mutt-1.5.20/functions.h --- mutt-1.5.20.orig/functions.h 2009-04-30 08:36:17.000000000 +0300 +++ mutt-1.5.20/functions.h 2009-06-15 21:05:24.000000000 +0300 @@ -88,6 +88,10 @@ struct binding_t OpMain[] = { /* map: in { "break-thread", OP_MAIN_BREAK_THREAD, "#" }, { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" }, { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" }, +#ifdef USE_NNTP + { "change-newsgroup", OP_MAIN_CHANGE_GROUP, "i" }, + { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY, "\033i" }, +#endif { "next-unread-mailbox", OP_MAIN_NEXT_UNREAD_MAILBOX, NULL }, { "collapse-thread", OP_MAIN_COLLAPSE_THREAD, "\033v" }, { "collapse-all", OP_MAIN_COLLAPSE_ALL, "\033V" }, @@ -101,7 +105,15 @@ struct binding_t OpMain[] = { /* map: in { "edit", OP_EDIT_MESSAGE, "e" }, { "edit-type", OP_EDIT_TYPE, "\005" }, { "forward-message", OP_FORWARD_MESSAGE, "f" }, - { "flag-message", OP_FLAG_MESSAGE, "F" }, +#ifdef USE_NNTP + { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" }, + { "followup-message", OP_FOLLOWUP, "F" }, + { "get-children", OP_GET_CHILDREN, NULL }, + { "get-message", OP_GET_MESSAGE, "\007" }, + { "get-parent", OP_GET_PARENT, "\033G" }, + { "reconstruct-thread", OP_RECONSTRUCT_THREAD, NULL }, +#endif + { "flag-message", OP_FLAG_MESSAGE, "\033f" }, { "group-reply", OP_GROUP_REPLY, "g" }, #ifdef USE_POP { "fetch-mail", OP_MAIN_FETCH_MAIL, "G" }, @@ -127,6 +139,9 @@ struct binding_t OpMain[] = { /* map: in { "sort-mailbox", OP_SORT, "o" }, { "sort-reverse", OP_SORT_REVERSE, "O" }, { "print-message", OP_PRINT, "p" }, +#ifdef USE_NNTP + { "post-message", OP_POST, "P" }, +#endif { "previous-thread", OP_MAIN_PREV_THREAD, "\020" }, { "previous-subthread", OP_MAIN_PREV_SUBTHREAD, "\033p" }, { "recall-message", OP_RECALL_MESSAGE, "R" }, @@ -146,6 +161,10 @@ struct binding_t OpMain[] = { /* map: in { "show-version", OP_VERSION, "V" }, { "set-flag", OP_MAIN_SET_FLAG, "w" }, { "clear-flag", OP_MAIN_CLEAR_FLAG, "W" }, + { "toggle-read", OP_TOGGLE_READ, "X" }, +#ifdef USE_NNTP + { "catchup", OP_CATCHUP, "y" }, +#endif { "display-message", OP_DISPLAY_MESSAGE, M_ENTER_S }, { "buffy-list", OP_BUFFY_LIST, "." }, { "sync-mailbox", OP_MAIN_SYNC_FOLDER, "$" }, @@ -157,7 +176,7 @@ struct binding_t OpMain[] = { /* map: in { "previous-new-then-unread", OP_MAIN_PREV_NEW_THEN_UNREAD, "\033\t" }, { "next-unread", OP_MAIN_NEXT_UNREAD, NULL }, { "previous-unread", OP_MAIN_PREV_UNREAD, NULL }, - { "parent-message", OP_MAIN_PARENT_MESSAGE, "P" }, + { "parent-message", OP_MAIN_PARENT_MESSAGE, NULL }, { "extract-keys", OP_EXTRACT_KEYS, "\013" }, @@ -177,6 +196,10 @@ struct binding_t OpPager[] = { /* map: p { "bounce-message", OP_BOUNCE_MESSAGE, "b" }, { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" }, { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" }, +#ifdef USE_NNTP + { "change-newsgroup", OP_MAIN_CHANGE_GROUP, "i" }, + { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY, "\033i" }, +#endif { "next-unread-mailbox", OP_MAIN_NEXT_UNREAD_MAILBOX, NULL }, { "copy-message", OP_COPY_MESSAGE, "C" }, { "decode-copy", OP_DECODE_COPY, "\033C" }, @@ -187,8 +210,12 @@ struct binding_t OpPager[] = { /* map: p { "clear-flag", OP_MAIN_CLEAR_FLAG, "W" }, { "edit", OP_EDIT_MESSAGE, "e" }, { "edit-type", OP_EDIT_TYPE, "\005" }, +#ifdef USE_NNTP + { "followup-message", OP_FOLLOWUP, "F" }, + { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" }, +#endif { "forward-message", OP_FORWARD_MESSAGE, "f" }, - { "flag-message", OP_FLAG_MESSAGE, "F" }, + { "flag-message", OP_FLAG_MESSAGE, "\033f" }, { "group-reply", OP_GROUP_REPLY, "g" }, #ifdef USE_IMAP { "imap-fetch-mail", OP_MAIN_IMAP_FETCH, NULL }, @@ -207,6 +234,9 @@ struct binding_t OpPager[] = { /* map: p { "next-thread", OP_MAIN_NEXT_THREAD, "\016" }, { "next-subthread", OP_MAIN_NEXT_SUBTHREAD, "\033n" }, { "print-message", OP_PRINT, "p" }, +#ifdef USE_NNTP + { "post-message", OP_POST, "P" }, +#endif { "previous-thread", OP_MAIN_PREV_THREAD, "\020" }, { "previous-subthread",OP_MAIN_PREV_SUBTHREAD, "\033p" }, { "quit", OP_QUIT, "Q" }, @@ -254,7 +284,7 @@ struct binding_t OpPager[] = { /* map: p { "half-down", OP_HALF_DOWN, NULL }, { "previous-line", OP_PREV_LINE, NULL }, { "bottom", OP_PAGER_BOTTOM, NULL }, - { "parent-message", OP_MAIN_PARENT_MESSAGE, "P" }, + { "parent-message", OP_MAIN_PARENT_MESSAGE, NULL }, @@ -275,6 +305,10 @@ struct binding_t OpAttach[] = { /* map: { "bounce-message", OP_BOUNCE_MESSAGE, "b" }, { "display-toggle-weed", OP_DISPLAY_HEADERS, "h" }, { "edit-type", OP_EDIT_TYPE, "\005" }, +#ifdef USE_NNTP + { "followup-message", OP_FOLLOWUP, "F" }, + { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" }, +#endif { "print-entry", OP_PRINT, "p" }, { "save-entry", OP_SAVE, "s" }, { "pipe-entry", OP_PIPE, "|" }, @@ -300,6 +334,7 @@ struct binding_t OpAttach[] = { /* map: struct binding_t OpCompose[] = { /* map: compose */ { "attach-file", OP_COMPOSE_ATTACH_FILE, "a" }, { "attach-message", OP_COMPOSE_ATTACH_MESSAGE, "A" }, + { "attach-news-message",OP_COMPOSE_ATTACH_NEWS_MESSAGE,"\033a" }, { "edit-bcc", OP_COMPOSE_EDIT_BCC, "b" }, { "edit-cc", OP_COMPOSE_EDIT_CC, "c" }, { "copy-file", OP_SAVE, "C" }, @@ -319,6 +354,11 @@ struct binding_t OpCompose[] = { /* map: { "print-entry", OP_PRINT, "l" }, { "edit-mime", OP_COMPOSE_EDIT_MIME, "m" }, { "new-mime", OP_COMPOSE_NEW_MIME, "n" }, +#ifdef USE_NNTP + { "edit-newsgroups", OP_COMPOSE_EDIT_NEWSGROUPS, "N" }, + { "edit-followup-to", OP_COMPOSE_EDIT_FOLLOWUP_TO, "o" }, + { "edit-x-comment-to",OP_COMPOSE_EDIT_X_COMMENT_TO, "x" }, +#endif { "postpone-message", OP_COMPOSE_POSTPONE_MESSAGE, "P" }, { "edit-reply-to", OP_COMPOSE_EDIT_REPLY_TO, "r" }, { "rename-file", OP_COMPOSE_RENAME_FILE, "R" }, @@ -370,14 +410,25 @@ struct binding_t OpBrowser[] = { /* map: { "select-new", OP_BROWSER_NEW_FILE, "N" }, { "check-new", OP_CHECK_NEW, NULL }, { "toggle-mailboxes", OP_TOGGLE_MAILBOXES, "\t" }, +#ifdef USE_NNTP + { "reload-active", OP_LOAD_ACTIVE, "g" }, + { "subscribe-pattern", OP_SUBSCRIBE_PATTERN, "S" }, + { "unsubscribe-pattern", OP_UNSUBSCRIBE_PATTERN, "U" }, + { "catchup", OP_CATCHUP, "y" }, + { "uncatchup", OP_UNCATCHUP, "Y" }, +#endif { "view-file", OP_BROWSER_VIEW_FILE, " " }, { "buffy-list", OP_BUFFY_LIST, "." }, #ifdef USE_IMAP { "create-mailbox", OP_CREATE_MAILBOX, "C" }, { "delete-mailbox", OP_DELETE_MAILBOX, "d" }, { "rename-mailbox", OP_RENAME_MAILBOX, "r" }, +#endif +#if defined USE_IMAP || defined USE_NNTP { "subscribe", OP_BROWSER_SUBSCRIBE, "s" }, { "unsubscribe", OP_BROWSER_UNSUBSCRIBE, "u" }, +#endif +#ifdef USE_IMAP { "toggle-subscribed", OP_BROWSER_TOGGLE_LSUB, "T" }, #endif { NULL, 0, NULL } diff -udprP mutt-1.5.20.orig/globals.h mutt-1.5.20/globals.h --- mutt-1.5.20.orig/globals.h 2009-06-03 23:48:31.000000000 +0300 +++ mutt-1.5.20/globals.h 2009-06-15 21:05:24.000000000 +0300 @@ -95,6 +95,15 @@ WHERE char *MixEntryFormat; #endif WHERE char *Muttrc INITVAL (NULL); +#ifdef USE_NNTP +WHERE char *NewsCacheDir; +WHERE char *GroupFormat; +WHERE char *Inews; +WHERE char *NewsServer; +WHERE char *NntpUser; +WHERE char *NntpPass; +WHERE char *NewsRc; +#endif WHERE char *Outbox; WHERE char *Pager; WHERE char *PagerFmt; @@ -188,6 +197,11 @@ extern unsigned char QuadOptions[]; WHERE unsigned short Counter INITVAL (0); +#ifdef USE_NNTP +WHERE short NewsPollTimeout; +WHERE short NntpContext; +#endif + WHERE short ConnectTimeout; WHERE short HistSize; WHERE short MenuContext; diff -udprP mutt-1.5.20.orig/hash.c mutt-1.5.20/hash.c --- mutt-1.5.20.orig/hash.c 2009-03-31 09:52:43.000000000 +0300 +++ mutt-1.5.20/hash.c 2009-06-15 21:19:59.000000000 +0300 @@ -57,6 +57,7 @@ HASH *hash_create (int nelem, int lower) if (nelem == 0) nelem = 2; table->nelem = nelem; + table->curnelem = 0; table->table = safe_calloc (nelem, sizeof (struct hash_elem *)); if (lower) { @@ -71,6 +72,29 @@ HASH *hash_create (int nelem, int lower) return table; } +HASH *hash_resize (HASH *ptr, int nelem, int lower) +{ + HASH *table; + struct hash_elem *elem, *tmp; + int i; + + table = hash_create (nelem, lower); + + for (i = 0; i < ptr->nelem; i++) + { + for (elem = ptr->table[i]; elem; ) + { + tmp = elem; + elem = elem->next; + hash_insert (table, tmp->key, tmp->data, 1); + FREE (&tmp); + } + } + FREE (&ptr->table); + FREE (&ptr); + return table; +} + /* table hash table to update * key key to hash on * data data to associate with `key' @@ -90,6 +114,7 @@ int hash_insert (HASH * table, const cha { ptr->next = table->table[h]; table->table[h] = ptr; + table->curnelem++; } else { @@ -112,6 +137,7 @@ int hash_insert (HASH * table, const cha else table->table[h] = ptr; ptr->next = tmp; + table->curnelem++; } return h; } @@ -142,6 +168,7 @@ void hash_delete_hash (HASH * table, int if (destroy) destroy (ptr->data); FREE (&ptr); + table->curnelem--; ptr = *last; } diff -udprP mutt-1.5.20.orig/hash.h mutt-1.5.20/hash.h --- mutt-1.5.20.orig/hash.h 2009-03-31 09:52:43.000000000 +0300 +++ mutt-1.5.20/hash.h 2009-06-15 21:05:24.000000000 +0300 @@ -28,7 +28,7 @@ struct hash_elem typedef struct { - int nelem; + int nelem, curnelem; struct hash_elem **table; unsigned int (*hash_string)(const unsigned char *, unsigned int); int (*cmp_string)(const char *, const char *); @@ -41,6 +41,7 @@ HASH; HASH *hash_create (int nelem, int lower); int hash_insert (HASH * table, const char *key, void *data, int allow_dup); +HASH *hash_resize (HASH * table, int nelem, int lower); void *hash_find_hash (const HASH * table, int hash, const char *key); void hash_delete_hash (HASH * table, int hash, const char *key, const void *data, void (*destroy) (void *)); diff -udprP mutt-1.5.20.orig/hdrline.c mutt-1.5.20/hdrline.c --- mutt-1.5.20.orig/hdrline.c 2009-04-13 19:24:55.000000000 +0300 +++ mutt-1.5.20/hdrline.c 2009-06-15 21:05:24.000000000 +0300 @@ -211,6 +211,7 @@ int mutt_user_is_recipient (HEADER *h) * %E = number of messages in current thread * %f = entire from line * %F = like %n, unless from self + * %g = newsgroup name (if compiled with nntp support) * %i = message-id * %l = number of lines in the message * %L = like %F, except `lists' are displayed first @@ -219,12 +220,14 @@ int mutt_user_is_recipient (HEADER *h) * %N = score * %O = like %L, except using address instead of name * %P = progress indicator for builtin pager + * %R = `x-comment-to:' field (if present and compiled with nntp support) * %s = subject * %S = short message status (e.g., N/O/D/!/r/-) * %t = `to:' field (recipients) * %T = $to_chars * %u = user (login) name of author * %v = first name of author, unless from self + * %W = where user is (organization) * %X = number of MIME attachments * %y = `x-label:' field (if present) * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label) @@ -457,6 +460,12 @@ hdr_format_str (char *dest, break; +#ifdef USE_NNTP + case 'g': + mutt_format_s (dest, destlen, prefix, hdr->env->newsgroups ? hdr->env->newsgroups : ""); + break; +#endif + case 'i': mutt_format_s (dest, destlen, prefix, hdr->env->message_id ? hdr->env->message_id : "<no.id>"); break; @@ -548,6 +557,15 @@ hdr_format_str (char *dest, strfcpy(dest, NONULL(hfi->pager_progress), destlen); break; +#ifdef USE_NNTP + case 'R': + if (!optional) + mutt_format_s (dest, destlen, prefix, hdr->env->x_comment_to ? hdr->env->x_comment_to : ""); + else if (!hdr->env->x_comment_to) + optional = 0; + break; +#endif + case 's': if (flags & M_FORMAT_TREE && !hdr->collapsed) @@ -637,6 +655,13 @@ hdr_format_str (char *dest, mutt_format_s (dest, destlen, prefix, buf2); break; + case 'W': + if (!optional) + mutt_format_s (dest, destlen, prefix, hdr->env->organization ? hdr->env->organization : ""); + else if (!hdr->env->organization) + optional = 0; + break; + case 'Z': ch = ' '; diff -udprP mutt-1.5.20.orig/headers.c mutt-1.5.20/headers.c --- mutt-1.5.20.orig/headers.c 2009-04-30 08:36:17.000000000 +0300 +++ mutt-1.5.20/headers.c 2009-06-15 21:17:07.000000000 +0300 @@ -114,6 +114,9 @@ void mutt_edit_headers (const char *edit $edit_headers set, we remove References: as they're likely invalid; we can simply compare strings as we don't generate References for multiple Message-Ids in IRT anyways */ +#ifdef USE_NNTP + if (!option (OPTNEWSSEND)) +#endif if (!n->in_reply_to || (msg->env->in_reply_to && mutt_strcmp (n->in_reply_to->data, msg->env->in_reply_to->data) != 0)) diff -udprP mutt-1.5.20.orig/init.c mutt-1.5.20/init.c --- mutt-1.5.20.orig/init.c 2009-06-01 19:29:32.000000000 +0300 +++ mutt-1.5.20/init.c 2009-06-15 21:05:24.000000000 +0300 @@ -2966,6 +2966,28 @@ void mutt_init (int skip_sys_rc, LIST *c else Fqdn = safe_strdup(NONULL(Hostname)); +#ifdef USE_NNTP + { + FILE *f; + char *i; + + if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) + { + buffer[0] = '\0'; + fgets (buffer, sizeof (buffer), f); + p = &buffer; + SKIPWS (p); + i = p; + while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++; + *i = '\0'; + NewsServer = safe_strdup (p); + fclose (f); + } + } + if ((p = getenv ("NNTPSERVER"))) + NewsServer = safe_strdup (p); +#endif + if ((p = getenv ("MAIL"))) Spoolfile = safe_strdup (p); else if ((p = getenv ("MAILDIR"))) diff -udprP mutt-1.5.20.orig/init.h mutt-1.5.20/init.h --- mutt-1.5.20.orig/init.h 2009-06-14 00:35:21.000000000 +0300 +++ mutt-1.5.20/init.h 2009-06-15 21:15:03.000000000 +0300 @@ -176,6 +176,20 @@ struct option_t MuttVars[] = { ** If \fIset\fP, Mutt will prompt you for carbon-copy (Cc) recipients before ** editing the body of an outgoing message. */ +#ifdef USE_NNTP + { "ask_follow_up", DT_BOOL, R_NONE, OPTASKFOLLOWUP, 0 }, + /* + ** .pp + ** If set, Mutt will prompt you for follow-up groups before editing + ** the body of an outgoing message. + */ + { "ask_x_comment_to", DT_BOOL, R_NONE, OPTASKXCOMMENTTO, 0 }, + /* + ** .pp + ** If set, Mutt will prompt you for x-comment-to field before editing + ** the body of an outgoing message. + */ +#endif { "assumed_charset", DT_STR, R_NONE, UL &AssumedCharset, UL 0}, /* ** .pp @@ -322,6 +336,14 @@ struct option_t MuttVars[] = { ** follow these menus. The option is \fIunset\fP by default because many ** visual terminals don't permit making the cursor invisible. */ +#ifdef USE_NNTP + { "catchup_newsgroup", DT_QUAD, R_NONE, OPT_CATCHUP, M_ASKYES }, + /* + ** .pp + ** If this variable is \fIset\fP, Mutt will mark all articles in newsgroup + ** as read when you quit the newsgroup (catchup newsgroup). + */ +#endif #if defined(USE_SSL) { "certificate_file", DT_PATH, R_NONE, UL &SslCertFile, UL "~/.mutt_certificates" }, /* @@ -797,6 +819,16 @@ struct option_t MuttVars[] = { ** sent to both the list and your address, resulting in two copies ** of the same email for you. */ +#ifdef USE_NNTP + { "followup_to_poster", DT_QUAD, R_NONE, OPT_FOLLOWUPTOPOSTER, M_ASKYES }, + /* + ** .pp + ** If this variable is \fIset\fP and the keyword "poster" is present in + ** \fIFollowup-To\fP header, follow-up to newsgroup function is not + ** permitted. The message will be mailed to the submitter of the + ** message via mail. + */ +#endif { "force_name", DT_BOOL, R_NONE, OPTFORCENAME, 0 }, /* ** .pp @@ -879,6 +911,27 @@ struct option_t MuttVars[] = { ** a regular expression that will match the whole name so mutt will expand ** ``Franklin'' to ``Franklin, Steve''. */ +#ifdef USE_NNTP + { "group_index_format", DT_STR, R_BOTH, UL &GroupFormat, UL "%4C %M%N %5s %-45.45f %d" }, + /* + ** .pp + ** This variable allows you to customize the newsgroup browser display to + ** your personal taste. This string is similar to ``$index_format'', but + ** has its own set of printf()-like sequences: + ** .pp + ** .ts + ** %C current newsgroup number + ** %d description of newsgroup (becomes from server) + ** %f newsgroup name + ** %M - if newsgroup not allowed for direct post (moderated for example) + ** %N N if newsgroup is new, u if unsubscribed, blank otherwise + ** %n number of new articles in newsgroup + ** %s number of unread articles in newsgroup + ** %>X right justify the rest of the string and pad with character "X" + ** %|X pad to the end of the line with character "X" + ** .te + */ +#endif { "hdr_format", DT_SYN, R_NONE, UL "index_format", 0 }, /* */ @@ -1255,6 +1308,7 @@ struct option_t MuttVars[] = { ** .dt %E .dd number of messages in current thread ** .dt %f .dd sender (address + real name), either From: or Return-Path: ** .dt %F .dd author name, or recipient name if the message is from you + ** .dt %g .dd newsgroup name (if compiled with nntp support) ** .dt %H .dd spam attribute(s) of this message ** .dt %i .dd message-id of the current message ** .dt %l .dd number of lines in the message (does not work with maildir, @@ -1270,12 +1324,14 @@ struct option_t MuttVars[] = { ** stashed the message: list name or recipient name ** if not sent to a list ** .dt %P .dd progress indicator for the builtin pager (how much of the file has been displayed) + ** .dt %R .dd `x-comment-to:' field (if present and compiled with nntp support) ** .dt %s .dd subject of the message ** .dt %S .dd status of the message (``N''/``D''/``d''/``!''/``r''/\(as) ** .dt %t .dd ``To:'' field (recipients) ** .dt %T .dd the appropriate character from the $$to_chars string ** .dt %u .dd user (login) name of the author ** .dt %v .dd first name of the author, or the recipient if the message is from you + ** .dt %W .dd name of organization of author (`organization:' field) ** .dt %X .dd number of attachments ** (please see the ``$attachments'' section for possible speed effects) ** .dt %y .dd ``X-Label:'' field, if present @@ -1310,6 +1366,21 @@ struct option_t MuttVars[] = { ** Note that these expandos are supported in ** ``$save-hook'', ``$fcc-hook'' and ``$fcc-save-hook'', too. */ +#ifdef USE_NNTP + { "inews", DT_PATH, R_NONE, UL &Inews, UL "" }, + /* + ** .pp + ** If set, specifies the program and arguments used to deliver news posted + ** by Mutt. Otherwise, mutt posts article using current connection to + ** news server. The following printf-style sequence is understood: + ** .pp + ** .ts + ** %s newsserver name + ** .te + ** .pp + ** Example: set inews="/usr/local/bin/inews -hS" + */ +#endif { "ispell", DT_PATH, R_NONE, UL &Ispell, UL ISPELL }, /* ** .pp @@ -1533,6 +1604,15 @@ struct option_t MuttVars[] = { ** menu, attachments which cannot be decoded in a reasonable manner will ** be attached to the newly composed message if this option is \fIset\fP. */ +#ifdef USE_NNTP + { "mime_subject", DT_BOOL, R_NONE, OPTMIMESUBJECT, 1 }, + /* + ** .pp + ** If \fIunset\fP, 8-bit ``subject:'' line in article header will not be + ** encoded according to RFC2047 to base64. This is useful when message + ** is Usenet article, because MIME for news is nonstandard feature. + */ +#endif #ifdef MIXMASTER { "mix_entry_format", DT_STR, R_NONE, UL &MixEntryFormat, UL "%4n %c %-16s %a" }, /* @@ -1580,6 +1660,77 @@ struct option_t MuttVars[] = { ** See also $$read_inc, $$write_inc and $$net_inc. */ #endif +#ifdef USE_NNTP + { "news_cache_dir", DT_PATH, R_NONE, UL &NewsCacheDir, UL "~/.mutt" }, + /* + ** .pp + ** This variable pointing to directory where Mutt will save cached news + ** articles headers in. If \fIunset\fP, headers will not be saved at all + ** and will be reloaded each time when you enter to newsgroup. + */ + { "news_server", DT_STR, R_NONE, UL &NewsServer, 0 }, + /* + ** .pp + ** This variable specifies domain name or address of NNTP server. It + ** defaults to the newsserver specified in the environment variable + ** $$$NNTPSERVER or contained in the file /etc/nntpserver. You can also + ** specify username and an alternative port for each newsserver, ie: + ** .pp + ** [news[s]://][username[:password]@]newsserver[:port] + */ + { "newsrc", DT_PATH, R_NONE, UL &NewsRc, UL "~/.newsrc" }, + /* + ** .pp + ** The file, containing info about subscribed newsgroups - names and + ** indexes of read articles. The following printf-style sequence + ** is understood: + ** .pp + ** .ts + ** %s newsserver name + ** .te + */ + { "nntp_context", DT_NUM, R_NONE, UL &NntpContext, 1000 }, + /* + ** .pp + ** This variable defines number of articles which will be in index when + ** newsgroup entered. If active newsgroup have more articles than this + ** number, oldest articles will be ignored. Also controls how many + ** articles headers will be saved in cache when you quit newsgroup. + */ + { "nntp_load_description", DT_BOOL, R_NONE, OPTLOADDESC, 1 }, + /* + ** .pp + ** This variable controls whether or not descriptions for each newsgroup + ** must be loaded when newsgroup is added to list (first time list + ** loading or new newsgroup adding). + */ + { "nntp_user", DT_STR, R_NONE, UL &NntpUser, UL "" }, + /* + ** .pp + ** Your login name on the NNTP server. If \fIunset\fP and NNTP server requires + ** authentification, Mutt will prompt you for your account name when you + ** connect to newsserver. + */ + { "nntp_pass", DT_STR, R_NONE, UL &NntpPass, UL "" }, + /* + ** .pp + ** Your password for NNTP account. + */ + { "nntp_poll", DT_NUM, R_NONE, UL &NewsPollTimeout, 60 }, + /* + ** .pp + ** The time in seconds until any operations on newsgroup except post new + ** article will cause recheck for new news. If set to 0, Mutt will + ** recheck newsgroup on each operation in index (stepping, read article, + ** etc.). + */ + { "nntp_reconnect", DT_QUAD, R_NONE, OPT_NNTPRECONNECT, M_ASKYES }, + /* + ** .pp + ** Controls whether or not Mutt will try to reconnect to newsserver when + ** connection lost. + */ +#endif { "pager", DT_PATH, R_NONE, UL &Pager, UL "builtin" }, /* ** .pp @@ -2079,6 +2230,16 @@ struct option_t MuttVars[] = { { "post_indent_str", DT_SYN, R_NONE, UL "post_indent_string", 0 }, /* */ +#ifdef USE_NNTP + { "post_moderated", DT_QUAD, R_NONE, OPT_TOMODERATED, M_ASKYES }, + /* + ** .pp + ** If set to \fIyes\fP, Mutt will post article to newsgroup that have + ** not permissions to posting (e.g. moderated). \fBNote:\fP if newsserver + ** does not support posting to that newsgroup or totally read-only, that + ** posting will not have an effect. + */ +#endif { "postpone", DT_QUAD, R_NONE, OPT_POSTPONE, M_ASKYES }, /* ** .pp @@ -2479,6 +2640,28 @@ struct option_t MuttVars[] = { ** Command to use when spawning a subshell. By default, the user's login ** shell from \fC/etc/passwd\fP is used. */ +#ifdef USE_NNTP + { "save_unsubscribed",DT_BOOL, R_NONE, OPTSAVEUNSUB, 0 }, + /* + ** .pp + ** When \fIset\fP, info about unsubscribed newsgroups will be saved into + ** ``newsrc'' file and into cache. + */ + { "show_new_news", DT_BOOL, R_NONE, OPTSHOWNEWNEWS, 1 }, + /* + ** .pp + ** If \fIset\fP, newsserver will be asked for new newsgroups on entering + ** the browser. Otherwise, it will be done only once for a newsserver. + ** Also controls whether or not number of new articles of subscribed + ** newsgroups will be then checked. + */ + { "show_only_unread", DT_BOOL, R_NONE, OPTSHOWONLYUNREAD, 0 }, + /* + ** .pp + ** If \fIset\fP, only subscribed newsgroups that contain unread articles + ** will be displayed in browser. + */ +#endif { "sig_dashes", DT_BOOL, R_NONE, OPTSIGDASHES, 1 }, /* ** .pp @@ -3337,6 +3520,14 @@ struct option_t MuttVars[] = { ** Also see the $$read_inc, $$net_inc and $$time_inc variables and the ** ``$tuning'' section of the manual for performance considerations. */ +#ifdef USE_NNTP + { "x_comment_to", DT_BOOL, R_NONE, OPTXCOMMENTTO, 0 }, + /* + ** .pp + ** If \fIset\fP, Mutt will add ``X-Comment-To:'' field (that contains full + ** name of original article author) to article that followuped to newsgroup. + */ +#endif /*--*/ { NULL, 0, 0, 0, 0 } }; diff -udprP mutt-1.5.20.orig/keymap.c mutt-1.5.20/keymap.c --- mutt-1.5.20.orig/keymap.c 2008-11-29 23:09:10.000000000 +0200 +++ mutt-1.5.20/keymap.c 2009-06-15 21:05:24.000000000 +0300 @@ -654,7 +654,6 @@ void km_init (void) km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE); km_bindkey ("x", MENU_PAGER, OP_EXIT); - km_bindkey ("i", MENU_PAGER, OP_EXIT); km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE); km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE); km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE); diff -udprP mutt-1.5.20.orig/mailbox.h mutt-1.5.20/mailbox.h --- mutt-1.5.20.orig/mailbox.h 2009-04-30 08:36:17.000000000 +0300 +++ mutt-1.5.20/mailbox.h 2009-06-15 21:05:24.000000000 +0300 @@ -74,6 +74,9 @@ int mx_is_imap (const char *); #ifdef USE_POP int mx_is_pop (const char *); #endif +#ifdef USE_NNTP +int mx_is_nntp (const char *); +#endif int mx_access (const char*, int); int mx_check_empty (const char *); diff -udprP mutt-1.5.20.orig/main.c mutt-1.5.20/main.c --- mutt-1.5.20.orig/main.c 2009-06-01 19:29:32.000000000 +0300 +++ mutt-1.5.20/main.c 2009-06-15 21:05:24.000000000 +0300 @@ -60,6 +60,10 @@ #include <stringprep.h> #endif +#ifdef USE_NNTP +#include "nntp.h" +#endif + static const char *ReachingUs = N_("\ To contact the developers, please mail to <mutt-dev@mutt.org>.\n\ To report a bug, please visit http://bugs.mutt.org/.\n"); @@ -133,6 +137,8 @@ options:\n\ " -e <command>\tspecify a command to be executed after initialization\n\ -f <file>\tspecify which mailbox to read\n\ -F <file>\tspecify an alternate muttrc file\n\ + -g <server>\tspecify a newsserver (if compiled with NNTP)\n\ + -G\t\tselect a newsgroup (if compiled with NNTP)\n\ -H <file>\tspecify a draft file to read header and body from\n\ -i <file>\tspecify a file which Mutt should include in the body\n\ -m <type>\tspecify a default mailbox type\n\ @@ -255,6 +261,12 @@ static void show_version (void) "-USE_POP " #endif +#ifdef USE_NNTP + "+USE_NNTP " +#else + "-USE_NNTP " +#endif + #ifdef USE_IMAP "+USE_IMAP " #else @@ -522,6 +534,9 @@ static void start_curses (void) #define M_NOSYSRC (1<<2) /* -n */ #define M_RO (1<<3) /* -R */ #define M_SELECT (1<<4) /* -y */ +#ifdef USE_NNTP +#define M_NEWS (1<<5) /* -g and -G */ +#endif int main (int argc, char **argv) { @@ -594,7 +609,11 @@ int main (int argc, char **argv) argv[nargc++] = argv[optind]; } +#ifdef USE_NNTP + if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:e:g:GH:s:i:hm:npQ:RvxyzZ")) != EOF) +#else if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:e:H:s:i:hm:npQ:RvxyzZ")) != EOF) +#endif switch (i) { case 'A': @@ -691,6 +710,20 @@ int main (int argc, char **argv) flags |= M_SELECT; break; +#ifdef USE_NNTP + case 'g': /* Specify a newsserver */ + { + char buf[LONG_STRING]; + + snprintf (buf, sizeof (buf), "set news_server=%s", optarg); + commands = mutt_add_list (commands, buf); + } + + case 'G': /* List of newsgroups */ + flags |= M_SELECT | M_NEWS; + break; +#endif + case 'z': flags |= M_IGNORE; break; @@ -978,6 +1011,18 @@ int main (int argc, char **argv) } else if (flags & M_SELECT) { +#ifdef USE_NNTP + if (flags & M_NEWS) + { + set_option (OPTNEWS); + if(!(CurrentNewsSrv = mutt_select_newsserver (NewsServer))) + { + mutt_endwin (Errorbuf); + exit (1); + } + } + else +#endif if (!Incoming) { mutt_endwin _("No incoming mailboxes defined."); exit (1); @@ -993,6 +1038,15 @@ int main (int argc, char **argv) if (!folder[0]) strfcpy (folder, NONULL(Spoolfile), sizeof (folder)); + +#ifdef USE_NNTP + if (option (OPTNEWS)) + { + unset_option (OPTNEWS); + nntp_expand_path (folder, sizeof (folder), &CurrentNewsSrv->conn->account); + } + else +#endif mutt_expand_path (folder, sizeof (folder)); mutt_str_replace (&CurrentFolder, folder); diff -udprP mutt-1.5.20.orig/mutt.h mutt-1.5.20/mutt.h --- mutt-1.5.20.orig/mutt.h 2009-06-13 01:15:42.000000000 +0300 +++ mutt-1.5.20/mutt.h 2009-06-15 21:05:24.000000000 +0300 @@ -229,6 +229,9 @@ enum M_PGP_KEY, M_XLABEL, M_MIMEATTACH, +#ifdef USE_NNTP + M_NEWSGROUPS, +#endif /* Options for Mailcap lookup */ M_EDIT, @@ -285,6 +288,12 @@ enum #endif OPT_SUBJECT, OPT_VERIFYSIG, /* verify PGP signatures */ +#ifdef USE_NNTP + OPT_TOMODERATED, + OPT_NNTPRECONNECT, + OPT_CATCHUP, + OPT_FOLLOWUPTOPOSTER, +#endif /* USE_NNTP */ /* THIS MUST BE THE LAST VALUE. */ OPT_MAX @@ -300,6 +309,7 @@ enum #define SENDMAILX (1<<6) #define SENDKEY (1<<7) #define SENDRESEND (1<<8) +#define SENDNEWS (1<<9) /* flags to _mutt_select_file() */ #define M_SEL_BUFFY (1<<0) @@ -319,6 +329,8 @@ enum OPTASCIICHARS, OPTASKBCC, OPTASKCC, + OPTASKFOLLOWUP, + OPTASKXCOMMENTTO, OPTATTACHSPLIT, OPTAUTOEDIT, OPTAUTOTAG, @@ -396,6 +408,9 @@ enum OPTMETOO, OPTMHPURGE, OPTMIMEFORWDECODE, +#ifdef USE_NNTP + OPTMIMESUBJECT, /* encode subject line with RFC2047 */ +#endif OPTNARROWTREE, OPTPAGERSTOP, OPTPIPEDECODE, @@ -477,6 +492,16 @@ enum OPTPGPAUTOINLINE, OPTPGPREPLYINLINE, + /* news options */ + +#ifdef USE_NNTP + OPTSHOWNEWNEWS, + OPTSHOWONLYUNREAD, + OPTSAVEUNSUB, + OPTLOADDESC, + OPTXCOMMENTTO, +#endif /* USE_NNTP */ + /* pseudo options */ OPTAUXSORT, /* (pseudo) using auxillary sort function */ @@ -497,6 +522,7 @@ enum OPTSORTSUBTHREADS, /* (pseudo) used when $sort_aux changes */ OPTNEEDRESCORE, /* (pseudo) set when the `score' command is used */ OPTATTACHMSG, /* (pseudo) used by attach-message */ + OPTHIDEREAD, /* (pseudo) whether or not hide read messages */ OPTKEEPQUIET, /* (pseudo) shut up the message and refresh * functions while we are executing an * external program. @@ -507,6 +533,12 @@ enum OPTDONTHANDLEPGPKEYS, /* (pseudo) used to extract PGP keys */ OPTUNBUFFEREDINPUT, /* (pseudo) don't use key buffer */ +#ifdef USE_NNTP + OPTNEWS, /* (pseudo) used to change reader mode */ + OPTNEWSSEND, /* (pseudo) used to change behavior when posting */ + OPTNEWSCACHE, /* (pseudo) used to indicate if news cache exist */ +#endif + OPTMAX }; @@ -585,6 +617,13 @@ typedef struct envelope char *supersedes; char *date; char *x_label; + char *organization; +#ifdef USE_NNTP + char *newsgroups; + char *xref; + char *followup_to; + char *x_comment_to; +#endif BUFFER *spam; LIST *references; /* message references (in reverse order) */ LIST *in_reply_to; /* in-reply-to header content */ @@ -751,6 +790,9 @@ typedef struct header ENVELOPE *env; /* envelope information */ BODY *content; /* list of MIME parts */ char *path; +#ifdef USE_NNTP + int article_num; +#endif char *tree; /* character string to print thread tree */ struct thread *thread; @@ -766,7 +808,7 @@ typedef struct header int refno; /* message number on server */ #endif -#if defined USE_POP || defined USE_IMAP +#if defined USE_POP || defined USE_IMAP || defined USE_NNTP void *data; /* driver-specific data */ #endif diff -udprP mutt-1.5.20.orig/muttlib.c mutt-1.5.20/muttlib.c --- mutt-1.5.20.orig/muttlib.c 2009-05-19 03:11:35.000000000 +0300 +++ mutt-1.5.20/muttlib.c 2009-06-15 21:05:24.000000000 +0300 @@ -301,7 +301,7 @@ void mutt_free_header (HEADER **h) #ifdef MIXMASTER mutt_free_list (&(*h)->chain); #endif -#if defined USE_POP || defined USE_IMAP +#if defined USE_POP || defined USE_IMAP || defined USE_NNTP FREE (&(*h)->data); #endif FREE (h); /* __FREE_CHECKED__ */ @@ -689,6 +689,13 @@ void mutt_free_envelope (ENVELOPE **p) FREE (&(*p)->supersedes); FREE (&(*p)->date); FREE (&(*p)->x_label); + FREE (&(*p)->organization); +#ifdef USE_NNTP + FREE (&(*p)->newsgroups); + FREE (&(*p)->xref); + FREE (&(*p)->followup_to); + FREE (&(*p)->x_comment_to); +#endif mutt_buffer_free (&(*p)->spam); @@ -1470,6 +1477,14 @@ int mutt_save_confirm (const char *s, st } } +#ifdef USE_NNTP + if (magic == M_NNTP) + { + mutt_error _("Can't save message to newsserver."); + return 0; + } +#endif + if (stat (s, st) != -1) { if (magic == -1) diff -udprP mutt-1.5.20.orig/mx.c mutt-1.5.20/mx.c --- mutt-1.5.20.orig/mx.c 2009-06-11 07:29:41.000000000 +0300 +++ mutt-1.5.20/mx.c 2009-06-15 21:05:24.000000000 +0300 @@ -343,6 +343,22 @@ int mx_is_pop (const char *p) } #endif +#ifdef USE_NNTP +int mx_is_nntp (const char *p) +{ + url_scheme_t scheme; + + if (!p) + return 0; + + scheme = url_check_scheme (p); + if (scheme == U_NNTP || scheme == U_NNTPS) + return 1; + + return 0; +} +#endif + int mx_get_magic (const char *path) { struct stat st; @@ -360,6 +376,11 @@ int mx_get_magic (const char *path) return M_POP; #endif /* USE_POP */ +#ifdef USE_NNTP + if (mx_is_nntp (path)) + return M_NNTP; +#endif /* USE_NNTP */ + if (stat (path, &st) == -1) { dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n", @@ -669,6 +690,12 @@ CONTEXT *mx_open_mailbox (const char *pa break; #endif /* USE_POP */ +#ifdef USE_NNTP + case M_NNTP: + rc = nntp_open_mailbox (ctx); + break; +#endif /* USE_NNTP */ + default: rc = -1; break; @@ -761,6 +788,12 @@ static int sync_mailbox (CONTEXT *ctx, i rc = pop_sync_mailbox (ctx, index_hint); break; #endif /* USE_POP */ + +#ifdef USE_NNTP + case M_NNTP: + rc = nntp_sync_mailbox (ctx); + break; +#endif /* USE_NNTP */ } #if 0 @@ -787,6 +820,16 @@ int mx_close_mailbox (CONTEXT *ctx, int ctx->closing = 1; +#ifdef USE_NNTP + if (ctx->magic == M_NNTP) + { + int ret; + + ret = nntp_close_mailbox (ctx); + mx_fastclose_mailbox (ctx); + return ret; + } +#endif if (ctx->readonly || ctx->dontwrite) { /* mailbox is readonly or we don't want to write */ @@ -1336,6 +1379,11 @@ int mx_check_mailbox (CONTEXT *ctx, int case M_POP: return (pop_check_mailbox (ctx, index_hint)); #endif /* USE_POP */ + +#ifdef USE_NNTP + case M_NNTP: + return (nntp_check_mailbox (ctx)); +#endif /* USE_NNTP */ } } @@ -1396,6 +1444,15 @@ MESSAGE *mx_open_message (CONTEXT *ctx, } #endif /* USE_POP */ +#ifdef USE_NNTP + case M_NNTP: + { + if (nntp_fetch_message (msg, ctx, msgno) != 0) + FREE (&msg); + break; + } +#endif /* USE_NNTP */ + default: dprint (1, (debugfile, "mx_open_message(): function not implemented for mailbox type %d.\n", ctx->magic)); FREE (&msg); @@ -1477,6 +1534,9 @@ int mx_close_message (MESSAGE **msg) #ifdef USE_POP || (*msg)->magic == M_POP #endif +#ifdef USE_NNTP + || (*msg)->magic == M_NNTP +#endif ) { r = safe_fclose (&(*msg)->fp); diff -udprP mutt-1.5.20.orig/mx.h mutt-1.5.20/mx.h --- mutt-1.5.20.orig/mx.h 2008-11-11 21:55:47.000000000 +0200 +++ mutt-1.5.20/mx.h 2009-06-15 21:05:24.000000000 +0300 @@ -40,6 +40,9 @@ enum #ifdef USE_POP , M_POP #endif +#ifdef USE_NNTP + , M_NNTP +#endif }; WHERE short DefaultMagic INITVAL (M_MBOX); diff -udprP mutt-1.5.20.orig/newsrc.c mutt-1.5.20/newsrc.c --- mutt-1.5.20.orig/newsrc.c 1970-01-01 03:00:00.000000000 +0300 +++ mutt-1.5.20/newsrc.c 2009-06-15 21:05:24.000000000 +0300 @@ -0,0 +1,1170 @@ +/* + * Copyright (C) 1998 Brandon Long <blong@fiction.net> + * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net> + * Copyright (C) 2000-2009 Vsevolod Volkov <vvv@mutt.org.ua> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mutt.h" +#include "mutt_curses.h" +#include "sort.h" +#include "mx.h" +#include "mime.h" +#include "mailbox.h" +#include "nntp.h" +#include "rfc822.h" +#include "rfc1524.h" +#include "rfc2047.h" + +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <sys/stat.h> + +void nntp_add_to_list (NNTP_SERVER *s, NNTP_DATA *d) +{ + LIST *l; + + if (!s || !d) + return; + + l = safe_calloc (1, sizeof (LIST)); + if (s->list) + s->tail->next = l; + else + s->list = l; + s->tail = l; + l->data = (void *) d; +} + +static int nntp_parse_newsrc_line (NNTP_SERVER *news, char *line) +{ + NNTP_DATA *data; + char group[LONG_STRING]; + int x = 1; + char *p = line, *b, *h; + size_t len; + + while (*p) + { + if (*p++ == ',') + x++; + } + + p = line; + while (*p && (*p != ':' && *p != '!')) p++; + if (!*p) + return -1; + len = p + 1 - line; + if (len > sizeof (group)) + len = sizeof (group); + strfcpy (group, line, len); + if ((data = (NNTP_DATA *)hash_find (news->newsgroups, group)) == NULL) + { + data = (NNTP_DATA *) safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1); + data->group = (char *) data + sizeof (NNTP_DATA); + strcpy (data->group, group); + data->nserv = news; + data->deleted = 1; + if (news->newsgroups->nelem < news->newsgroups->curnelem * 2) + news->newsgroups = hash_resize (news->newsgroups, news->newsgroups->nelem * 2, 0); + hash_insert (news->newsgroups, data->group, data, 0); + nntp_add_to_list (news, data); + } + else + FREE ((void **) &data->entries); + + data->rc = 1; + data->entries = safe_calloc (x*2, sizeof (NEWSRC_ENTRY)); + data->max = x*2; + + if (*p == ':') + data->subscribed = 1; + else + data->subscribed = 0; + + p++; + b = p; + x = 0; + while (*b) + { + while (*p && *p != ',' && *p != '\n') p++; + if (*p) + { + *p = '\0'; + p++; + } + if ((h = strchr(b, '-'))) + { + *h = '\0'; + h++; + data->entries[x].first = atoi(b); + data->entries[x].last = atoi(h); + } + else + { + data->entries[x].first = atoi(b); + data->entries[x].last = data->entries[x].first; + } + b = p; + if (data->entries[x].last != 0) + x++; + } + if (x && !data->lastMessage) + data->lastMessage = data->entries[x-1].last; + data->num = x; + mutt_newsgroup_stat (data); + dprint (2, (debugfile, "parse_line: Newsgroup %s\n", data->group)); + + return 0; +} + +static int slurp_newsrc (NNTP_SERVER *news) +{ + FILE *fp; + char *buf; + struct stat sb; + + news->stat = stat (news->newsrc, &sb); + news->size = sb.st_size; + news->mtime = sb.st_mtime; + + if ((fp = safe_fopen (news->newsrc, "r")) == NULL) + return -1; + /* hmm, should we use dotlock? */ + if (mx_lock_file (news->newsrc, fileno (fp), 0, 0, 1)) + { + fclose (fp); + return -1; + } + + buf = safe_malloc (sb.st_size + 1); + while (sb.st_size && fgets (buf, sb.st_size + 1, fp)) + nntp_parse_newsrc_line (news, buf); + FREE (&buf); + + mx_unlock_file (news->newsrc, fileno (fp), 0); + fclose (fp); + return 0; +} + +void nntp_cache_expand (char *dst, const char *src) +{ + snprintf (dst, _POSIX_PATH_MAX, "%s/%s", NewsCacheDir, src); + mutt_expand_path (dst, _POSIX_PATH_MAX); +} + +/* Loads $news_cache_dir/.index into memory, loads newsserver data + * and newsgroup cache names */ +static int nntp_parse_cacheindex (NNTP_SERVER *news) +{ + struct stat st; + char buf[HUGE_STRING], *cp; + char dir[_POSIX_PATH_MAX], file[_POSIX_PATH_MAX]; + FILE *index; + NNTP_DATA *data; + int l, m, t; + + /* check is server name defined or not */ + if (!news || !news->conn || !news->conn->account.host) + return -1; + unset_option (OPTNEWSCACHE); + if (!NewsCacheDir || !*NewsCacheDir) + return 0; + + strfcpy (dir, NewsCacheDir, sizeof (dir)); + mutt_expand_path (dir, sizeof(dir)); + + if (lstat (dir, &st) || (st.st_mode & S_IFDIR) == 0) + { + snprintf (buf, sizeof(buf), _("Directory %s not exist. Create it?"), dir); + if (mutt_yesorno (buf, M_YES) != M_YES || mkdir (dir, (S_IRWXU+S_IRWXG+ + S_IRWXO))) + { + mutt_error _("Cache directory not created!"); + return -1; + } + mutt_clear_error(); + } + + set_option (OPTNEWSCACHE); + + FREE (&news->cache); + snprintf (buf, sizeof(buf), "%s/.index", dir); + if (!(index = safe_fopen (buf, "a+"))) + return 0; + rewind (index); + while (fgets (buf, sizeof(buf), index)) + { + buf[strlen(buf) - 1] = 0; /* strip ending '\n' */ + if (!mutt_strncmp (buf, "#: ", 3) && + !mutt_strcasecmp (buf+3, news->conn->account.host)) + break; + } + while (fgets (buf, sizeof(buf), index)) + { + cp = buf; + while (*cp && *cp != ' ') cp++; + if (!*cp) continue; + cp[0] = 0; + if (!mutt_strcmp (buf, "#:")) + break; + sscanf (cp + 1, "%s %d %d", file, &l, &m); + if (!mutt_strcmp (buf, "ALL")) + { + news->cache = safe_strdup (file); + news->newgroups_time = m; + } + else if (news->newsgroups) + { + if ((data = (NNTP_DATA *)hash_find (news->newsgroups, buf)) == NULL) + { + data = (NNTP_DATA *) safe_calloc (1, sizeof (NNTP_DATA) + strlen (buf) + 1); + data->group = (char *) data + sizeof (NNTP_DATA); + strcpy(data->group, buf); + data->nserv = news; + data->deleted = 1; + if (news->newsgroups->nelem < news->newsgroups->curnelem * 2) + news->newsgroups = hash_resize (news->newsgroups, news->newsgroups->nelem * 2, 0); + hash_insert (news->newsgroups, data->group, data, 0); + nntp_add_to_list (news, data); + } + data->cache = safe_strdup (file); + t = 0; + if (!data->firstMessage || data->lastMessage < m) + t = 1; + if (!data->firstMessage) + data->firstMessage = l; + if (data->lastMessage < m) + data->lastMessage = m; + data->lastCached = m; + if (t || !data->unread) + mutt_newsgroup_stat (data); + } + } + fclose (index); + return 0; +} + +const char * +nntp_format_str (char *dest, size_t destlen, size_t col, char op, const char *src, + const char *fmt, const char *ifstring, const char *elsestring, + unsigned long data, format_flag flags) +{ + char fn[SHORT_STRING], tmp[SHORT_STRING]; + + switch (op) + { + case 's': + strncpy (fn, NewsServer, sizeof(fn) - 1); + mutt_strlower (fn); + snprintf (tmp, sizeof (tmp), "%%%ss", fmt); + snprintf (dest, destlen, tmp, fn); + break; + } + return (src); +} + +/* nntp_parse_url: given an NNPT URL, return host, port, + * username, password and newsgroup will recognise. */ +int nntp_parse_url (const char *server, ACCOUNT *acct, + char *group, size_t group_len) +{ + ciss_url_t url; + char *c; + int ret = -1; + + /* Defaults */ + acct->flags = 0; + acct->port = NNTP_PORT; + acct->type = M_ACCT_TYPE_NNTP; + + c = safe_strdup (server); + url_parse_ciss (&url, c); + + if (url.scheme == U_NNTP || url.scheme == U_NNTPS) + { + if (url.scheme == U_NNTPS) + { + acct->flags |= M_ACCT_SSL; + acct->port = NNTP_SSL_PORT; + } + + *group = '\0'; + if (url.path) + strfcpy (group, url.path, group_len); + + ret = mutt_account_fromurl (acct, &url); + } + + FREE (&c); + return ret; +} + +void nntp_expand_path (char *line, size_t len, ACCOUNT *acct) +{ + ciss_url_t url; + + url.path = safe_strdup (line); + mutt_account_tourl (acct, &url); + url_ciss_tostring (&url, line, len, 0); + FREE (&url.path); +} + +/* + * Automatically loads a newsrc into memory, if necessary. + * Checks the size/mtime of a newsrc file, if it doesn't match, load + * again. Hmm, if a system has broken mtimes, this might mean the file + * is reloaded every time, which we'd have to fix. + * + * a newsrc file is a line per newsgroup, with the newsgroup, then a + * ':' denoting subscribed or '!' denoting unsubscribed, then a + * comma separated list of article numbers and ranges. + */ +NNTP_SERVER *mutt_select_newsserver (char *server) +{ + char file[_POSIX_PATH_MAX]; + char *buf, *p; + LIST *list; + ACCOUNT acct; + NNTP_SERVER *serv; + CONNECTION *conn; + + if (!server || !*server) + { + mutt_error _("No newsserver defined!"); + return NULL; + } + + buf = p = safe_calloc (strlen (server) + 10, sizeof (char)); + if (url_check_scheme (server) == U_UNKNOWN) + { + strcpy (buf, "news://"); + p = strchr (buf, '\0'); + } + strcpy (p, server); + + if ((nntp_parse_url (buf, &acct, file, sizeof (file))) < 0 || *file) + { + FREE (&buf); + mutt_error (_("%s is an invalid newsserver specification!"), server); + return NULL; + } + FREE (&buf); + + conn = mutt_conn_find (NULL, &acct); + if (!conn) + return NULL; + + mutt_FormatString (file, sizeof (file), 0, NONULL (NewsRc), nntp_format_str, 0, 0); + mutt_expand_path (file, sizeof (file)); + + serv = (NNTP_SERVER *)conn->data; + if (serv) + { + struct stat sb; + + /* externally modified? */ + if (serv->stat != stat (file, &sb) || (!serv->stat && + (serv->size != sb.st_size || serv->mtime != sb.st_mtime))) + { + for (list = serv->list; list; list = list->next) + { + NNTP_DATA *data = (NNTP_DATA *) list->data; + + if (data) + { + data->subscribed = 0; + data->rc = 0; + data->num = 0; + } + } + slurp_newsrc (serv); + nntp_clear_cacheindex (serv); + } + + if (serv->status == NNTP_BYE) + serv->status = NNTP_NONE; + nntp_check_newgroups (serv, 0); + return serv; + } + + /* New newsserver */ + serv = safe_calloc (1, sizeof (NNTP_SERVER)); + serv->conn = conn; + serv->newsrc = safe_strdup (file); + serv->newsgroups = hash_create (1009, 0); + slurp_newsrc (serv); /* load .newsrc */ + nntp_parse_cacheindex (serv); /* load .index */ + if (option (OPTNEWSCACHE) && serv->cache && nntp_get_cache_all (serv) >= 0) + nntp_check_newgroups (serv, 1); + else if (nntp_get_active (serv) < 0) + { + hash_destroy (&serv->newsgroups, nntp_delete_data); + for (list = serv->list; list; list = list->next) + list->data = NULL; + mutt_free_list (&serv->list); + FREE (&serv->newsrc); + FREE (&serv->cache); + FREE (&serv); + return NULL; + } + nntp_clear_cacheindex (serv); + conn->data = (void *)serv; + + return serv; +} + +/* + * full status flags are not supported by nntp, but we can fake some + * of them. This is how: + * Read = a read message number is in the .newsrc + * New = a message is new since we last read this newsgroup + * Old = anything else + * So, Read is marked as such in the newsrc, old is anything that is + * "skipped" in the newsrc, and new is anything not in the newsrc nor + * in the cache. By skipped, I mean before the last unread message + */ +void nntp_get_status (CONTEXT *ctx, HEADER *h, char *group, int article) +{ + NNTP_DATA *data = (NNTP_DATA *) ctx->data; + int x; + + if (group) + data = (NNTP_DATA *) hash_find (data->nserv->newsgroups, group); + + if (!data) + { +#ifdef DEBUG + if (group) + dprint (3, (debugfile, "newsgroup %s not found\n", group)); +#endif + return; + } + + for (x = 0; x < data->num; x++) + { + if ((article >= data->entries[x].first) && + (article <= data->entries[x].last)) + { + /* we cannot use mutt_set_flag() because mx_update_context() + didn't called yet */ + h->read = 1; + return; + } + } + /* If article was not cached yet, it is new! :) */ + if (!data->cache || article > data->lastCached) + return; + /* Old articles are articles which aren't read but an article after them + * has been cached */ + if (option (OPTMARKOLD)) + h->old = 1; +} + +void mutt_newsgroup_stat (NNTP_DATA *data) +{ + int i; + unsigned int first, last; + + data->unread = 0; + if (data->lastMessage == 0 || data->firstMessage > data->lastMessage) + return; + + data->unread = data->lastMessage - data->firstMessage + 1; + for (i = 0; i < data->num; i++) + { + first = data->entries[i].first; + if (first < data->firstMessage) + first = data->firstMessage; + last = data->entries[i].last; + if (last > data->lastMessage) + last = data->lastMessage; + if (first <= last) + data->unread -= last - first + 1; + } +} + +static int puti (char *line, int num) +{ + char *p, s[32]; + + for (p = s; num; ) + { + *p++ = '0' + num % 10; + num /= 10; + } + while (p > s) + *line++ = *--p, num++; + *line = '\0'; + return num; +} + +static void nntp_create_newsrc_line (NNTP_DATA *data, char **buf, char **pline, size_t *buflen) +{ + char *line = *pline; + size_t len = *buflen - (*pline - *buf); + int x, i; + + if (len < LONG_STRING * 10) + { + len += *buflen; + *buflen *= 2; + line = *buf; + safe_realloc (buf, *buflen); + line = *buf + (*pline - line); + } + strcpy (line, data->group); + len -= strlen (line) + 1; + line += strlen (line); + *line++ = data->subscribed ? ':' : '!'; + *line++ = ' '; + *line = '\0'; + + for (x = 0; x < data->num; x++) + { + if (len < LONG_STRING) + { + len += *buflen; + *buflen *= 2; + *pline = line; + line = *buf; + safe_realloc (buf, *buflen); + line = *buf + (*pline - line); + } + if (x) + { + *line++ = ','; + len--; + } + +#if 0 + if (data->entries[x].first == data->entries[x].last) + snprintf (line, len, "%d%n", data->entries[x].first, &i); + else + snprintf (line, len, "%d-%d%n", + data->entries[x].first, data->entries[x].last, &i); + len -= i; + line += i; +#else + i = puti (line, data->entries[x].first); + line +=i; len -= i; + if (data->entries[x].first != data->entries[x].last) + { + *line++ = '-'; + len--; + i = puti (line, data->entries[x].last); + line +=i; len -= i; + } +#endif + } + *line++ = '\n'; + *line = '\0'; + *pline = line; +} + +void newsrc_gen_entries (CONTEXT *ctx) +{ + NNTP_DATA *data = (NNTP_DATA *)ctx->data; + int series, x; + unsigned int last = 0, first = 1; + int save_sort = SORT_ORDER; + + if (Sort != SORT_ORDER) + { + save_sort = Sort; + Sort = SORT_ORDER; + mutt_sort_headers (ctx, 0); + } + + if (!data->max) + { + data->entries = safe_calloc (5, sizeof (NEWSRC_ENTRY)); + data->max = 5; + } + + /* + * Set up to fake initial sequence from 1 to the article before the + * first article in our list + */ + data->num = 0; + series = 1; + + for (x = 0; x < ctx->msgcount; x++) + { + if (series) /* search for first unread */ + { + /* + * We don't actually check sequential order, since we mark + * "missing" entries as read/deleted + */ + last = ctx->hdrs[x]->article_num; + if (last >= data->firstMessage && !ctx->hdrs[x]->deleted && + !ctx->hdrs[x]->read) + { + if (data->num >= data->max) + { + data->max = data->max * 2; + safe_realloc (&data->entries, + data->max * sizeof (NEWSRC_ENTRY)); + } + data->entries[data->num].first = first; + data->entries[data->num].last = last - 1; + data->num++; + series = 0; + } + } + else /* search for first read */ + { + if (ctx->hdrs[x]->deleted || ctx->hdrs[x]->read) + { + first = last + 1; + series = 1; + } + last = ctx->hdrs[x]->article_num; + } + } + if (series && first <= data->lastLoaded) + { + if (data->num >= data->max) + { + data->max = data->max * 2; + safe_realloc (&data->entries, + data->max * sizeof (NEWSRC_ENTRY)); + } + data->entries[data->num].first = first; + data->entries[data->num].last = data->lastLoaded; + data->num++; + } + + if (save_sort != Sort) + { + Sort = save_sort; + mutt_sort_headers (ctx, 0); + } +} + +static int mutt_update_list_file (char *filename, char *section, + char *key, char *line) +{ + FILE *ifp; + FILE *ofp; + char buf[HUGE_STRING]; + char tmpfile[_POSIX_PATH_MAX]; + char *c; + int ext = 0, done = 0, r = 0; + + /* if file not exist, create it */ + if ((ifp = safe_fopen (filename, "a"))) + fclose (ifp); + dprint (1, (debugfile, "Opening %s\n", filename)); + if (!(ifp = safe_fopen (filename, "r"))) + { + mutt_error (_("Unable to open %s for reading"), filename); + return -1; + } + if (mx_lock_file (filename, fileno (ifp), 0, 0, 1)) + { + fclose (ifp); + mutt_error (_("Unable to lock %s"), filename); + return -1; + } + snprintf (tmpfile, sizeof(tmpfile), "%s.tmp", filename); + dprint (1, (debugfile, "Opening %s\n", tmpfile)); + if (!(ofp = fopen (tmpfile, "w"))) + { + fclose (ifp); + mutt_error (_("Unable to open %s for writing"), tmpfile); + return -1; + } + + if (section) + { + while (r != EOF && !done && fgets (buf, sizeof (buf), ifp)) + { + r = fputs (buf, ofp); + c = buf; + while (*c && *c != '\n') c++; + c[0] = 0; /* strip EOL */ + if (!strncmp (buf, "#: ", 3) && !mutt_strcasecmp (buf+3, section)) + done++; + } + if (r != EOF && !done) + { + snprintf (buf, sizeof(buf), "#: %s\n", section); + r = fputs (buf, ofp); + } + done = 0; + } + + while (r != EOF && fgets (buf, sizeof (buf), ifp)) + { + if (ext) + { + c = buf; + while (*c && (*c != '\r') && (*c != '\n')) c++; + c--; + if (*c != '\\') ext = 0; + } + else if ((section && !strncmp (buf, "#: ", 3))) + { + if (!done && line) + { + fputs (line, ofp); + fputc ('\n', ofp); + } + r = fputs (buf, ofp); + done++; + break; + } + else if (key && !strncmp (buf, key, strlen(key)) && + (!*key || buf[strlen(key)] == ' ')) + { + c = buf; + ext = 0; + while (*c && (*c != '\r') && (*c != '\n')) c++; + c--; + if (*c == '\\') ext = 1; + if (!done && line) + { + r = fputs (line, ofp); + if (*key) + r = fputc ('\n', ofp); + done++; + } + } + else + { + r = fputs (buf, ofp); + } + } + + while (r != EOF && fgets (buf, sizeof (buf), ifp)) + r = fputs (buf, ofp); + + /* If there wasn't a line to replace, put it on the end of the file */ + if (r != EOF && !done && line) + { + fputs (line, ofp); + r = fputc ('\n', ofp); + } + mx_unlock_file (filename, fileno (ifp), 0); + fclose (ofp); + fclose (ifp); + if (r == EOF) + { + unlink (tmpfile); + mutt_error (_("Can't write %s"), tmpfile); + return -1; + } + if (rename (tmpfile, filename) < 0) + { + unlink (tmpfile); + mutt_error (_("Can't rename %s to %s"), tmpfile, filename); + return -1; + } + return 0; +} + +int mutt_newsrc_update (NNTP_SERVER *news) +{ + char *buf, *line; + NNTP_DATA *data; + LIST *tmp; + int r = -1; + size_t len, llen; + + if (!news) + return -1; + llen = len = 10 * LONG_STRING; + line = buf = safe_calloc (1, len); + /* we will generate full newsrc here */ + for (tmp = news->list; tmp; tmp = tmp->next) + { + data = (NNTP_DATA *) tmp->data; + if (!data || !data->rc) + continue; + nntp_create_newsrc_line (data, &buf, &line, &llen); + if (*line) + dprint (2, (debugfile, "Added to newsrc: %s\n", line)); + line += strlen (line); + } + /* newrc being fully rewritten */ + if (news->newsrc && + (r = mutt_update_list_file (news->newsrc, NULL, "", buf)) == 0) + { + struct stat st; + + stat (news->newsrc, &st); + news->size = st.st_size; + news->mtime = st.st_mtime; + } + FREE (&buf); + return r; +} + +static FILE *mutt_mkname (char *s) +{ + char buf[_POSIX_PATH_MAX], *pc; + int fd; + FILE *fp; + + nntp_cache_expand (buf, s); + if ((fp = safe_fopen (buf, "w"))) + return fp; + + nntp_cache_expand (buf, "cache-XXXXXX"); + pc = buf + strlen (buf) - 12; /* positioning to "cache-XXXXXX" */ + if ((fd = mkstemp (buf)) == -1) + return NULL; + strcpy (s, pc); /* generated name */ + return fdopen (fd, "w"); +} + +/* Updates info into .index file: ALL or about selected newsgroup */ +static int nntp_update_cacheindex (NNTP_SERVER *serv, NNTP_DATA *data) +{ + char buf[LONG_STRING], *key = "ALL"; + char file[_POSIX_PATH_MAX]; + + if (!serv || !serv->conn || !serv->conn->account.host) + return -1; + + if (data && data->group) + { + key = data->group; + snprintf (buf, sizeof (buf), "%s %s %d %d", key, data->cache, + data->firstMessage, data->lastLoaded); + } + else + { + strfcpy (file, serv->cache, sizeof (file)); + snprintf (buf, sizeof (buf), "ALL %s 0 %d", file, (int)serv->newgroups_time); + } + nntp_cache_expand (file, ".index"); + return mutt_update_list_file (file, serv->conn->account.host, key, buf); +} + +/* Remove cache files of unsubscribed newsgroups */ +void nntp_clear_cacheindex (NNTP_SERVER *news) +{ + NNTP_DATA *data; + LIST *tmp; + + if (option (OPTSAVEUNSUB) || !news) + return; + + for (tmp = news->list; tmp; tmp = tmp->next) + { + data = (NNTP_DATA *) tmp->data; + if (!data || data->subscribed || !data->cache) + continue; + nntp_delete_cache (data); + dprint (2, (debugfile, "Removed from .index: %s\n", data->group)); + } + return; +} + +int nntp_save_cache_index (NNTP_SERVER *news) +{ + char buf[HUGE_STRING]; + char file[_POSIX_PATH_MAX]; + NNTP_DATA *d; + FILE *f; + LIST *l; + + if (!news || !news->newsgroups) + return -1; + if (!option (OPTNEWSCACHE)) + return 0; + + if (news->cache) + { + nntp_cache_expand (file, news->cache); + unlink (file); + f = safe_fopen (file, "w"); + } + else + { + strfcpy (buf, news->conn->account.host, sizeof(buf)); + f = mutt_mkname (buf); + news->cache = safe_strdup (buf); + nntp_cache_expand (file, buf); + } + if (!f) + return -1; + + for (l = news->list; l; l = l->next) + { + if ((d = (NNTP_DATA *)l->data) && !d->deleted) + { + if (d->desc) + snprintf (buf, sizeof(buf), "%s %d %d %c %s\n", d->group, + d->lastMessage, d->firstMessage, d->allowed ? 'y' : 'n', + d->desc); + else + snprintf (buf, sizeof(buf), "%s %d %d %c\n", d->group, + d->lastMessage, d->firstMessage, d->allowed ? 'y' : 'n'); + if (fputs (buf, f) == EOF) + { + fclose (f); + unlink (file); + return -1; + } + } + } + fclose (f); + + if (nntp_update_cacheindex (news, NULL)) + { + unlink (file); + return -1; + } + return 0; +} + +int nntp_save_cache_group (CONTEXT *ctx) +{ + char buf[HUGE_STRING], addr[STRING]; + char file[_POSIX_PATH_MAX]; + FILE *f; + HEADER *h; + struct tm *tm; + int i = 0, save = SORT_ORDER; + int prev = 0; + + if (!option (OPTNEWSCACHE)) + return 0; + if (!ctx || !ctx->data || ctx->magic != M_NNTP) + return -1; + + if (((NNTP_DATA *)ctx->data)->cache) + { + nntp_cache_expand (file, ((NNTP_DATA *)ctx->data)->cache); + unlink (file); + f = safe_fopen (file, "w"); + } + else + { + snprintf (buf, sizeof(buf), "%s-%s", + ((NNTP_DATA *)ctx->data)->nserv->conn->account.host, + ((NNTP_DATA *)ctx->data)->group); + f = mutt_mkname (buf); + ((NNTP_DATA *)ctx->data)->cache = safe_strdup (buf); + nntp_cache_expand (file, buf); + } + if (!f) + return -1; + + if (Sort != SORT_ORDER) + { + save = Sort; + Sort = SORT_ORDER; + mutt_sort_headers (ctx, 0); + } + + /* Save only $nntp_context messages... */ + ((NNTP_DATA *)ctx->data)->lastCached = 0; + if (NntpContext && ctx->msgcount > NntpContext) + i = ctx->msgcount - NntpContext; + for (; i < ctx->msgcount; i++) + { + if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->article_num != prev) + { + h = ctx->hdrs[i]; + addr[0] = 0; + rfc822_write_address (addr, sizeof(addr), h->env->from, 0); + tm = gmtime (&h->date_sent); + snprintf (buf, sizeof(buf), + "%d\t%s\t%s\t%d %s %d %02d:%02d:%02d GMT\t%s\t", + h->article_num, h->env->subject, addr, tm->tm_mday, + Months[tm->tm_mon], tm->tm_year+1900, tm->tm_hour, tm->tm_min, + tm->tm_sec, h->env->message_id); + fputs (buf, f); + if (h->env->references) + mutt_write_references (h->env->references, f, 10); + snprintf (buf, sizeof(buf), "\t%ld\t%d\tXref: %s\n", (long int) h->content->length, + (int) h->lines, NONULL(h->env->xref)); + if (fputs (buf, f) == EOF) + { + fclose (f); + unlink (file); + return -1; + } + } + prev = ctx->hdrs[i]->article_num; + } + + if (save != Sort) + { + Sort = save; + mutt_sort_headers (ctx, 0); + } + fclose (f); + + if (nntp_update_cacheindex (((NNTP_DATA *)ctx->data)->nserv, + (NNTP_DATA *)ctx->data)) + { + unlink (file); + return -1; + } + ((NNTP_DATA *)ctx->data)->lastCached = ((NNTP_DATA *)ctx->data)->lastLoaded; + return 0; +} + +void nntp_delete_cache (NNTP_DATA *data) +{ + char buf[_POSIX_PATH_MAX]; + + if (!option (OPTNEWSCACHE) || !data || !data->cache || !data->nserv) + return; + + nntp_cache_expand (buf, data->cache); + unlink (buf); + FREE (&data->cache); + data->lastCached = 0; + nntp_cache_expand (buf, ".index"); + mutt_update_list_file (buf, data->nserv->conn->account.host, data->group, NULL); +} + +NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *news, char *group) +{ + NNTP_DATA *data; + + if (!news || !news->newsgroups || !group || !*group) + return NULL; + if (!(data = (NNTP_DATA *)hash_find (news->newsgroups, group))) + { + data = (NNTP_DATA *) safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1); + data->group = (char *) data + sizeof (NNTP_DATA); + strcpy (data->group, group); + data->nserv = news; + data->deleted = 1; + if (news->newsgroups->nelem < news->newsgroups->curnelem * 2) + news->newsgroups = hash_resize (news->newsgroups, news->newsgroups->nelem * 2, 0); + hash_insert (news->newsgroups, data->group, data, 0); + nntp_add_to_list (news, data); + } + if (!data->subscribed) + { + data->subscribed = 1; + data->rc = 1; + } + return data; +} + +NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *news, char *group) +{ + NNTP_DATA *data; + + if (!news || !news->newsgroups || !group || !*group || + !(data = (NNTP_DATA *)hash_find (news->newsgroups, group))) + return NULL; + if (data->subscribed) + { + data->subscribed = 0; + if (!option (OPTSAVEUNSUB)) + data->rc = 0; + } + return data; +} + +NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *news, char *group) +{ + NNTP_DATA *data; + + if (!news || !news->newsgroups || !group || !*group || + !(data = (NNTP_DATA *)hash_find (news->newsgroups, group))) + return NULL; + if (!data->max) + { + data->entries = safe_calloc (5, sizeof (NEWSRC_ENTRY)); + data->max = 5; + } + data->num = 1; + data->entries[0].first = 1; + data->unread = 0; + data->entries[0].last = data->lastMessage; + if (Context && Context->data == data) + { + int x; + + for (x = 0; x < Context->msgcount; x++) + mutt_set_flag (Context, Context->hdrs[x], M_READ, 1); + } + return data; +} + +NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *news, char *group) +{ + NNTP_DATA *data; + + if (!news || !news->newsgroups || !group || !*group || + !(data = (NNTP_DATA *)hash_find (news->newsgroups, group))) + return NULL; + if (!data->max) + { + data->entries = safe_calloc (5, sizeof (NEWSRC_ENTRY)); + data->max = 5; + } + data->num = 1; + data->entries[0].first = 1; + data->entries[0].last = data->firstMessage - 1; + if (Context && Context->data == data) + { + int x; + + data->unread = Context->msgcount; + for (x = 0; x < Context->msgcount; x++) + mutt_set_flag (Context, Context->hdrs[x], M_READ, 0); + } + else + data->unread = data->lastMessage - data->entries[0].last; + return data; +} + +/* this routine gives the first newsgroup with new messages */ +void nntp_buffy (char *s) +{ + LIST *list; + + for (list = CurrentNewsSrv->list; list; list = list->next) + { + NNTP_DATA *data = (NNTP_DATA *) list->data; + + if (data && data->subscribed && data->unread) + { + if (Context && Context->magic == M_NNTP && + !mutt_strcmp (data->group, ((NNTP_DATA *) Context->data)->group)) + { + unsigned int i, unread = 0; + + for (i = 0; i < Context->msgcount; i++) + if (!Context->hdrs[i]->read && !Context->hdrs[i]->deleted) + unread++; + if (!unread) + continue; + } + strcpy (s, data->group); + break; + } + } +} diff -udprP mutt-1.5.20.orig/nntp.c mutt-1.5.20/nntp.c --- mutt-1.5.20.orig/nntp.c 1970-01-01 03:00:00.000000000 +0300 +++ mutt-1.5.20/nntp.c 2009-06-15 21:05:24.000000000 +0300 @@ -0,0 +1,1588 @@ +/* + * Copyright (C) 1998 Brandon Long <blong@fiction.net> + * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net> + * Copyright (C) 2000-2007 Vsevolod Volkov <vvv@mutt.org.ua> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mutt.h" +#include "mutt_curses.h" +#include "sort.h" +#include "mx.h" +#include "mime.h" +#include "rfc1524.h" +#include "rfc2047.h" +#include "mailbox.h" +#include "nntp.h" + +#ifdef HAVE_PGP +#include "pgp.h" +#endif + +#ifdef HAVE_SMIME +#include "smime.h" +#endif + +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +static unsigned int _checked = 0; + +#ifdef DEBUG +static void nntp_error (const char *where, const char *msg) +{ + dprint (1, (debugfile, "nntp_error(): unexpected response in %s: %s\n", where, msg)); +} +#endif /* DEBUG */ + +static int nntp_auth (NNTP_SERVER *serv) +{ + CONNECTION *conn = serv->conn; + char buf[STRING]; + unsigned char flags = conn->account.flags; + + if (mutt_account_getuser (&conn->account) || !conn->account.user[0] || + mutt_account_getpass (&conn->account) || !conn->account.pass[0]) + { + conn->account.flags = flags; + return -2; + } + + mutt_message _("Logging in..."); + + snprintf (buf, sizeof (buf), "AUTHINFO USER %s\r\n", conn->account.user); + mutt_socket_write (conn, buf); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + { + conn->account.flags = flags; + return -1; + } + +#ifdef DEBUG + /* don't print the password unless we're at the ungodly debugging level */ + if (debuglevel < M_SOCK_LOG_FULL) + dprint (M_SOCK_LOG_CMD, (debugfile, "> AUTHINFO PASS *\n")); +#endif + snprintf (buf, sizeof (buf), "AUTHINFO PASS %s\r\n", conn->account.pass); + mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + { + conn->account.flags = flags; + return -1; + } + + if (mutt_strncmp ("281", buf, 3)) + { + conn->account.flags = flags; + mutt_error _("Login failed."); + sleep (2); + return -3; + } + + return 0; +} + +static int nntp_connect_error (NNTP_SERVER *serv) +{ + serv->status = NNTP_NONE; + mutt_socket_close (serv->conn); + mutt_error _("Server closed connection!"); + sleep (2); + return -1; +} + +static int nntp_connect_and_auth (NNTP_SERVER *serv) +{ + CONNECTION *conn = serv->conn; + char buf[STRING]; + int rc; + + serv->status = NNTP_NONE; + + if (mutt_socket_open (conn) < 0) + return -1; + + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return nntp_connect_error (serv); + + if (!mutt_strncmp ("200", buf, 3)) + mutt_message (_("Connected to %s. Posting ok."), conn->account.host); + else if (!mutt_strncmp ("201", buf, 3)) + mutt_message (_("Connected to %s. Posting NOT ok."), conn->account.host); + else + { + mutt_socket_close (conn); + mutt_remove_trailing_ws (buf); + mutt_error ("%s", buf); + sleep (2); + return -1; + } + + sleep (1); + + /* Tell INN to switch to mode reader if it isn't so. Ignore all + returned codes and messages. */ + mutt_socket_write (conn, "MODE READER\r\n"); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return nntp_connect_error (serv); + + mutt_socket_write (conn, "STAT\r\n"); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return nntp_connect_error (serv); + + if (!(conn->account.flags & M_ACCT_USER) && mutt_strncmp ("480", buf, 3)) + { + serv->status = NNTP_OK; + return 0; + } + + rc = nntp_auth (serv); + if (rc == -1) + return nntp_connect_error (serv); + if (rc == -2) + { + mutt_socket_close (conn); + serv->status = NNTP_BYE; + return -1; + } + if (rc < 0) + { + mutt_socket_close (conn); + mutt_error _("Login failed."); + sleep (2); + return -1; + } + serv->status = NNTP_OK; + return 0; +} + +static int nntp_attempt_features (NNTP_SERVER *serv) +{ + char buf[LONG_STRING]; + CONNECTION *conn = serv->conn; + + mutt_socket_write (conn, "XOVER\r\n"); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return nntp_connect_error (serv); + if (mutt_strncmp ("500", buf, 3)) + serv->hasXOVER = 1; + + mutt_socket_write (conn, "XPAT\r\n"); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return nntp_connect_error (serv); + if (mutt_strncmp ("500", buf, 3)) + serv->hasXPAT = 1; + + mutt_socket_write (conn, "LISTGROUP\r\n"); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return nntp_connect_error (serv); + if (mutt_strncmp ("500", buf, 3)) + serv->hasLISTGROUP = 1; + + mutt_socket_write (conn, "XGTITLE +\r\n"); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return nntp_connect_error (serv); + if (mutt_strncmp ("500", buf, 3)) + serv->hasXGTITLE = 1; + + if (!mutt_strncmp ("282", buf, 3)) + { + do + { + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return nntp_connect_error (serv); + } while (!(buf[0] == '.' && buf[1] == '\0')); + } + + return 0; +} + +static int nntp_open_connection (NNTP_SERVER *serv) +{ + if (serv->status == NNTP_OK) + return 0; + if (serv->status == NNTP_BYE) + return -1; + if (nntp_connect_and_auth (serv) < 0) + return -1; + if (nntp_attempt_features (serv) < 0) + return -1; + return 0; +} + +static int nntp_reconnect (NNTP_SERVER *serv) +{ + char buf[SHORT_STRING]; + + mutt_socket_close (serv->conn); + + FOREVER + { + if (nntp_connect_and_auth (serv) == 0) + return 0; + + snprintf (buf, sizeof (buf), _("Connection to %s lost. Reconnect?"), + serv->conn->account.host); + if (query_quadoption (OPT_NNTPRECONNECT, buf) != M_YES) + { + serv->status = NNTP_BYE; + return -1; + } + } +} + +/* Send data from line[LONG_STRING] and receive answer to same line */ +static int mutt_nntp_query (NNTP_DATA *data, char *line, size_t linelen) +{ + char buf[LONG_STRING]; + int done = TRUE; + + if (data->nserv->status == NNTP_BYE) + return -1; + + do + { + if (*line) + { + mutt_socket_write (data->nserv->conn, line); + } + else if (data->group) + { + snprintf (buf, sizeof (buf), "GROUP %s\r\n", data->group); + mutt_socket_write (data->nserv->conn, buf); + } + + done = TRUE; + if (mutt_socket_readln (buf, sizeof (buf), data->nserv->conn) < 0) + { + if (nntp_reconnect (data->nserv) < 0) + return -1; + + if (data->group) + { + snprintf (buf, sizeof (buf), "GROUP %s\r\n", data->group); + mutt_socket_write (data->nserv->conn, buf); + if (mutt_socket_readln (buf, sizeof (buf), data->nserv->conn) < 0) + return -1; + } + if (*line) + done = FALSE; + } + else if ((!mutt_strncmp ("480", buf, 3)) && nntp_auth (data->nserv) < 0) + return -1; + } while (!done); + + strfcpy (line, buf, linelen); + return 0; +} + +/* + * This function calls funct(*line, *data) for each received line, + * funct(NULL, *data) if rewind(*data) needs, exits when fail or done. + * Returned codes: + * 0 - successful, + * 1 - correct but not performed (may be, have to be continued), + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - error in funct(*line, *data). + */ +static int mutt_nntp_fetch (NNTP_DATA *nntp_data, char *query, char *msg, + int (*funct) (char *, void *), void *data, int tagged) +{ + char buf[LONG_STRING]; + char *inbuf, *p; + int done = FALSE; + int chunk, line; + size_t lenbuf = 0; + int ret; + + do + { + strfcpy (buf, query, sizeof (buf)); + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) + return -1; + if (buf[0] == '5') + return -2; + if (buf[0] != '2') + return 1; + + ret = 0; + line = 0; + inbuf = safe_malloc (sizeof (buf)); + + FOREVER + { + chunk = mutt_socket_readln_d (buf, sizeof (buf), nntp_data->nserv->conn, + M_SOCK_LOG_HDR); + if (chunk < 0) + break; + + p = buf; + if (!lenbuf && buf[0] == '.') + { + if (buf[1] == '\0') + { + done = TRUE; + break; + } + if (buf[1] == '.') + p++; + } + + strfcpy (inbuf + lenbuf, p, sizeof (buf)); + + if (chunk >= sizeof (buf)) + { + lenbuf += strlen (p); + } + else + { + line++; + if (msg && ReadInc && (line % ReadInc == 0)) { + if (tagged) + mutt_message (_("%s (tagged: %d) %d"), msg, tagged, line); + else + mutt_message ("%s %d", msg, line); + } + + if (ret == 0 && funct (inbuf, data) < 0) + ret = -3; + lenbuf = 0; + } + + safe_realloc (&inbuf, lenbuf + sizeof (buf)); + } + FREE (&inbuf); + funct (NULL, data); + } + while (!done); + return ret; +} + +static int nntp_read_tempfile (char *line, void *file) +{ + FILE *f = (FILE *)file; + + if (!line) + rewind (f); + else + { + fputs (line, f); + if (fputc ('\n', f) == EOF) + return -1; + } + return 0; +} + +static void nntp_parse_xref (CONTEXT *ctx, char *group, char *xref, HEADER *h) +{ + register char *p, *b; + register char *colon = NULL; + + b = p = xref; + while (*p) + { + /* skip to next word */ + b = p; + while (*b && ((*b == ' ') || (*b == '\t'))) b++; + p = b; + colon = NULL; + /* skip to end of word */ + while (*p && (*p != ' ') && (*p != '\t')) + { + if (*p == ':') + colon = p; + p++; + } + if (*p) + { + *p = '\0'; + p++; + } + if (colon) + { + *colon = '\0'; + colon++; + nntp_get_status (ctx, h, b, atoi(colon)); + if (h && h->article_num == 0 && mutt_strcmp (group, b) == 0) + h->article_num = atoi(colon); + } + } +} + +/* + * returns: + * 0 on success + * 1 if article not found + * -1 if read or write error on tempfile or socket + */ +static int nntp_read_header (CONTEXT *ctx, const char *msgid, int article_num) +{ + NNTP_DATA *nntp_data = ((NNTP_DATA *)ctx->data); + FILE *f; + char buf[LONG_STRING]; + char tempfile[_POSIX_PATH_MAX]; + int ret; + HEADER *h = ctx->hdrs[ctx->msgcount]; + + mutt_mktemp (tempfile); + if (!(f = safe_fopen (tempfile, "w+"))) + return -1; + + if (!msgid) + snprintf (buf, sizeof (buf), "HEAD %d\r\n", article_num); + else + snprintf (buf, sizeof (buf), "HEAD %s\r\n", msgid); + + ret = mutt_nntp_fetch (nntp_data, buf, NULL, nntp_read_tempfile, f, 0); + if (ret) + { +#ifdef DEBUG + if (ret != -1) + dprint(1, (debugfile, "nntp_read_header: %s\n", buf)); +#endif + fclose (f); + unlink (tempfile); + return (ret == -1 ? -1 : 1); + } + + h->article_num = article_num; + h->env = mutt_read_rfc822_header (f, h, 0, 0); + fclose (f); + unlink (tempfile); + + if (h->env->xref != NULL) + nntp_parse_xref (ctx, nntp_data->group, h->env->xref, h); + else if (h->article_num == 0 && msgid) + { + snprintf (buf, sizeof (buf), "STAT %s\r\n", msgid); + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) == 0) + h->article_num = atoi (buf + 4); + } + + return 0; +} + +static int parse_description (char *line, void *n) +{ +#define news ((NNTP_SERVER *) n) + register char *d = line; + NNTP_DATA *data; + + if (!line) + return 0; + while (*d && *d != '\t' && *d != ' ') d++; + *d = 0; + d++; + while (*d && (*d == '\t' || *d == ' ')) d++; + dprint (2, (debugfile, "group: %s, desc: %s\n", line, d)); + if ((data = (NNTP_DATA *) hash_find (news->newsgroups, line)) != NULL && + mutt_strcmp (d, data->desc)) + { + FREE (&data->desc); + data->desc = safe_strdup (d); + } + return 0; +#undef news +} + +static void nntp_get_desc (NNTP_DATA *data, char *mask, char *msg) +{ + char buf[STRING]; + + if (!option (OPTLOADDESC) || !data || !data->nserv) + return; + + /* Get newsgroup description, if we can */ + if (data->nserv->hasXGTITLE) + snprintf (buf, sizeof (buf), "XGTITLE %s\r\n", mask); + else + snprintf (buf, sizeof (buf), "LIST NEWSGROUPS %s\r\n", mask); + if (mutt_nntp_fetch (data, buf, msg, parse_description, data->nserv, 0) != 0) + { +#ifdef DEBUG + nntp_error ("nntp_get_desc()", buf); +#endif + } +} + +/* + * XOVER returns a tab separated list of: + * id|subject|from|date|Msgid|references|bytes|lines|xref + * + * This has to duplicate some of the functionality of + * mutt_read_rfc822_header(), since it replaces the call to that (albeit with + * a limited number of headers which are "parsed" by placement in the list) + */ +static int nntp_parse_xover (CONTEXT *ctx, char *buf, HEADER *hdr) +{ + NNTP_DATA *nntp_data = (NNTP_DATA *) ctx->data; + char *p, *b; + int x, done = 0; + + hdr->env = mutt_new_envelope(); + hdr->env->newsgroups = safe_strdup (nntp_data->group); + hdr->content = mutt_new_body(); + hdr->content->type = TYPETEXT; + hdr->content->subtype = safe_strdup ("plain"); + hdr->content->encoding = ENC7BIT; + hdr->content->disposition = DISPINLINE; + hdr->content->length = -1; + b = p = buf; + + for (x = 0; !done && x < 9; x++) + { + /* if from file, need to skip newline character */ + while (*p && *p != '\n' && *p != '\t') p++; + if (!*p) done++; + *p = '\0'; + p++; + switch (x) + { + case 0: + + hdr->article_num = atoi (b); + nntp_get_status (ctx, hdr, NULL, hdr->article_num); + break; + case 1: + hdr->env->subject = safe_strdup (b); + /* Now we need to do the things which would normally be done in + * mutt_read_rfc822_header() */ + if (hdr->env->subject) + { + regmatch_t pmatch[1]; + + rfc2047_decode (&hdr->env->subject); + + if (regexec (ReplyRegexp.rx, hdr->env->subject, 1, pmatch, 0) == 0) + hdr->env->real_subj = hdr->env->subject + pmatch[0].rm_eo; + else + hdr->env->real_subj = hdr->env->subject; + } + break; + case 2: + rfc822_free_address (&hdr->env->from); + hdr->env->from = rfc822_parse_adrlist (hdr->env->from, b); + rfc2047_decode_adrlist (hdr->env->from); + break; + case 3: + hdr->date_sent = mutt_parse_date (b, hdr); + hdr->received = hdr->date_sent; + break; + case 4: + FREE (&hdr->env->message_id); + hdr->env->message_id = safe_strdup (b); + break; + case 5: + mutt_free_list (&hdr->env->references); + hdr->env->references = mutt_parse_references (b, 0); + break; + case 6: + hdr->content->length = atoi (b); + break; + case 7: + hdr->lines = atoi (b); + break; + case 8: + if (!hdr->read) + FREE (&hdr->env->xref); + b = b + 6; /* skips the "Xref: " */ + hdr->env->xref = safe_strdup (b); + nntp_parse_xref (ctx, nntp_data->group, b, hdr); + } + if (!*p) + return -1; + b = p; + } + return 0; +} + +typedef struct +{ + CONTEXT *ctx; + unsigned int base; + unsigned int first; + unsigned int last; + unsigned short *messages; + char* msg; +} FETCH_CONTEXT; + +#define fc ((FETCH_CONTEXT *) c) +static int nntp_fetch_numbers (char *line, void *c) +{ + unsigned int num; + + if (!line) + return 0; + num = atoi (line); + if (num < fc->base || num > fc->last) + return 0; + fc->messages[num - fc->base] = 1; + return 0; +} + +static int add_xover_line (char *line, void *c) +{ + unsigned int num, total; + CONTEXT *ctx = fc->ctx; + NNTP_DATA *data = (NNTP_DATA *)ctx->data; + + if (!line) + return 0; + + if (ctx->msgcount >= ctx->hdrmax) + mx_alloc_memory (ctx); + ctx->hdrs[ctx->msgcount] = mutt_new_header (); + ctx->hdrs[ctx->msgcount]->index = ctx->msgcount; + + nntp_parse_xover (ctx, line, ctx->hdrs[ctx->msgcount]); + num = ctx->hdrs[ctx->msgcount]->article_num; + + if (num >= fc->first && num <= fc->last && fc->messages[num - fc->base]) + { + ctx->msgcount++; + if (num > data->lastLoaded) + data->lastLoaded = num; + num = num - fc->first + 1; + total = fc->last - fc->first + 1; + if (!ctx->quiet && fc->msg && ReadInc && (num % ReadInc == 0)) + mutt_message ("%s %d/%d", fc->msg, num, total); + } + else + mutt_free_header (&ctx->hdrs[ctx->msgcount]); /* skip it */ + + return 0; +} +#undef fc + +static int nntp_fetch_headers (CONTEXT *ctx, unsigned int first, + unsigned int last) +{ + char buf[HUGE_STRING]; + char *msg = _("Fetching message headers..."); + NNTP_DATA *nntp_data = ((NNTP_DATA *)ctx->data); + int ret; + int num; + int oldmsgcount; + unsigned int current; + FILE *f; + FETCH_CONTEXT fc; + + /* if empty group or nothing to do */ + if (!last || first > last) + return 0; + + /* fetch list of articles */ + fc.ctx = ctx; + fc.base = first; + fc.last = last; + fc.messages = safe_calloc (last - first + 1, sizeof (unsigned short)); + if (nntp_data->nserv->hasLISTGROUP) + { + mutt_message _("Fetching list of articles..."); + snprintf (buf, sizeof (buf), "LISTGROUP %s\r\n", nntp_data->group); + if (mutt_nntp_fetch (nntp_data, buf, NULL, nntp_fetch_numbers, &fc, 0) != 0) + { + mutt_error (_("LISTGROUP command failed: %s"), buf); +#ifdef DEBUG + nntp_error ("nntp_fetch_headers()", buf); +#endif + FREE (&fc.messages); + return -1; + } + } + else + { + for (num = 0; num < last - first + 1; num++) + fc.messages[num] = 1; + } + + /* CACHE: must be loaded xover cache here */ + num = nntp_data->lastCached - first + 1; + if (option (OPTNEWSCACHE) && nntp_data->cache && num > 0) + { + nntp_cache_expand (buf, nntp_data->cache); + mutt_message _("Fetching headers from cache..."); + if ((f = safe_fopen (buf, "r"))) + { + int r = 0; + + /* counting number of lines */ + while (fgets (buf, sizeof (buf), f) != NULL) + r++; + rewind (f); + while (r > num && fgets (buf, sizeof (buf), f) != NULL) + r--; + oldmsgcount = ctx->msgcount; + fc.first = first; + fc.last = first + num - 1; + fc.msg = NULL; + while (fgets (buf, sizeof (buf), f) != NULL) + add_xover_line (buf, &fc); + fclose (f); + nntp_data->lastLoaded = fc.last; + first = fc.last + 1; + if (ctx->msgcount > oldmsgcount) + mx_update_context (ctx, ctx->msgcount - oldmsgcount); + } + else + nntp_delete_cache (nntp_data); + } + num = last - first + 1; + if (num <= 0) + { + FREE (&fc.messages); + return 0; + } + + /* + * Without XOVER, we have to fetch each article header and parse + * it. With XOVER, we ask for all of them + */ + mutt_message (msg); + if (nntp_data->nserv->hasXOVER) + { + oldmsgcount = ctx->msgcount; + fc.first = first; + fc.last = last; + fc.msg = msg; + snprintf (buf, sizeof (buf), "XOVER %d-%d\r\n", first, last); + ret = mutt_nntp_fetch (nntp_data, buf, NULL, add_xover_line, &fc, 0); + if (ctx->msgcount > oldmsgcount) + mx_update_context (ctx, ctx->msgcount - oldmsgcount); + if (ret != 0) + { + mutt_error (_("XOVER command failed: %s"), buf); +#ifdef DEBUG + nntp_error ("nntp_fetch_headers()", buf); +#endif + FREE (&fc.messages); + return -1; + } + /* fetched OK */ + } + else + for (current = first; current <= last; current++) + { + HEADER *h; + + ret = current - first + 1; + mutt_message ("%s %d/%d", msg, ret, num); + + if (!fc.messages[current - fc.base]) + continue; + + if (ctx->msgcount >= ctx->hdrmax) + mx_alloc_memory (ctx); + h = ctx->hdrs[ctx->msgcount] = mutt_new_header (); + h->index = ctx->msgcount; + + ret = nntp_read_header (ctx, NULL, current); + if (ret == 0) /* Got article. Fetch next header */ + { + nntp_get_status (ctx, h, NULL, h->article_num); + ctx->msgcount++; + mx_update_context (ctx, 1); + } + else + mutt_free_header (&h); /* skip it */ + if (ret == -1) + { + FREE (&fc.messages); + return -1; + } + + if (current > nntp_data->lastLoaded) + nntp_data->lastLoaded = current; + } + FREE (&fc.messages); + nntp_data->lastLoaded = last; + mutt_clear_error (); + return 0; +} + +/* + * currently, nntp "mailbox" is "newsgroup" + */ +int nntp_open_mailbox (CONTEXT *ctx) +{ + NNTP_DATA *nntp_data; + NNTP_SERVER *serv; + char buf[HUGE_STRING]; + char server[LONG_STRING]; + int count = 0; + unsigned int first; + ACCOUNT acct; + + if (nntp_parse_url (ctx->path, &acct, buf, sizeof (buf)) < 0 || !*buf) + { + mutt_error (_("%s is an invalid newsgroup specification!"), ctx->path); + mutt_sleep (2); + return -1; + } + + server[0] = '\0'; + nntp_expand_path (server, sizeof (server), &acct); + if (!(serv = mutt_select_newsserver (server)) || serv->status != NNTP_OK) + return -1; + + CurrentNewsSrv = serv; + + /* create NNTP-specific state struct if nof found in list */ + if ((nntp_data = (NNTP_DATA *) hash_find (serv->newsgroups, buf)) == NULL) + { + nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (buf) + 1); + nntp_data->group = (char *) nntp_data + sizeof (NNTP_DATA); + strcpy (nntp_data->group, buf); + hash_insert (serv->newsgroups, nntp_data->group, nntp_data, 0); + nntp_add_to_list (serv, nntp_data); + } + ctx->data = nntp_data; + ctx->mx_close = nntp_fastclose_mailbox; + nntp_data->nserv = serv; + + mutt_message (_("Selecting %s..."), nntp_data->group); + + if (!nntp_data->desc) + { + nntp_get_desc (nntp_data, nntp_data->group, NULL); + if (nntp_data->desc) + nntp_save_cache_index (serv); + } + + buf[0] = 0; + if (mutt_nntp_query (nntp_data, buf, sizeof(buf)) < 0) + { +#ifdef DEBUG + nntp_error ("nntp_open_mailbox()", buf); +#endif + return -1; + } + + if (mutt_strncmp ("211", buf, 3)) + { + LIST *l = serv->list; + + /* GROUP command failed */ + if (!mutt_strncmp ("411", buf, 3)) + { + mutt_error (_("Newsgroup %s not found on server %s"), + nntp_data->group, serv->conn->account.host); + + /* CACHE: delete cache and line from .index */ + nntp_delete_cache (nntp_data); + hash_delete (serv->newsgroups, nntp_data->group, NULL, nntp_delete_data); + while (l && l->data != (void *) nntp_data) l = l->next; + if (l) + l->data = NULL; + + sleep (2); + } + + return -1; + } + + sscanf (buf + 4, "%d %u %u %s", &count, &nntp_data->firstMessage, + &nntp_data->lastMessage, buf); + + nntp_data->deleted = 0; + + time (&serv->check_time); + + /* + * Check for max adding context. If it is greater than $nntp_context, + * strip off extra articles + */ + first = nntp_data->firstMessage; + if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext) + first = nntp_data->lastMessage - NntpContext + 1; + if (first) + nntp_data->lastLoaded = first - 1; + return nntp_fetch_headers (ctx, first, nntp_data->lastMessage); +} + +int nntp_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) +{ + char buf[LONG_STRING]; + char path[_POSIX_PATH_MAX]; + NNTP_CACHE *cache; + char *m = _("Fetching message..."); + int ret; + + /* see if we already have the message in our cache */ + cache = &((NNTP_DATA *) ctx->data)->acache[ctx->hdrs[msgno]->index % NNTP_CACHE_LEN]; + + /* if everything is fine, assign msg->fp and return */ + if (cache->path && cache->index == ctx->hdrs[msgno]->index && + (msg->fp = fopen (cache->path, "r"))) + return 0; + + /* clear the previous entry */ + unlink (cache->path); + free (cache->path); + + mutt_message (m); + + cache->index = ctx->hdrs[msgno]->index; + mutt_mktemp (path); + cache->path = safe_strdup (path); + if (!(msg->fp = safe_fopen (path, "w+"))) + { + FREE (&cache->path); + return -1; + } + + if (ctx->hdrs[msgno]->article_num == 0) + snprintf (buf, sizeof (buf), "ARTICLE %s\r\n", + ctx->hdrs[msgno]->env->message_id); + else + snprintf (buf, sizeof (buf), "ARTICLE %d\r\n", + ctx->hdrs[msgno]->article_num); + + ret = mutt_nntp_fetch ((NNTP_DATA *)ctx->data, buf, m, nntp_read_tempfile, + msg->fp, ctx->tagged); + if (ret == 1) + { + mutt_error (_("Article %d not found on server"), + ctx->hdrs[msgno]->article_num); + dprint (1, (debugfile, "nntp_fetch_message: %s\n", buf)); + } + + if (ret) + { + fclose (msg->fp); + unlink (path); + FREE (&cache->path); + return -1; + } + + mutt_free_envelope (&ctx->hdrs[msgno]->env); + ctx->hdrs[msgno]->env = mutt_read_rfc822_header (msg->fp, ctx->hdrs[msgno], 0, 0); + /* fix content length */ + fseek(msg->fp, 0, SEEK_END); + ctx->hdrs[msgno]->content->length = ftell (msg->fp) - + ctx->hdrs[msgno]->content->offset; + + /* this is called in mutt before the open which fetches the message, + * which is probably wrong, but we just call it again here to handle + * the problem instead of fixing it. + */ + mutt_parse_mime_message (ctx, ctx->hdrs[msgno]); + + /* These would normally be updated in mx_update_context(), but the + * full headers aren't parsed with XOVER, so the information wasn't + * available then. + */ +#if defined(HAVE_PGP) || defined(HAVE_SMIME) + ctx->hdrs[msgno]->security = crypt_query (ctx->hdrs[msgno]->content); +#endif /* HAVE_PGP || HAVE_SMIME */ + + mutt_clear_error(); + rewind (msg->fp); + + return 0; +} + +/* Post article */ +int nntp_post (const char *msg) { + char buf[LONG_STRING]; + size_t len; + FILE *f; + NNTP_DATA *nntp_data; + + if (Context && Context->magic == M_NNTP) + nntp_data = (NNTP_DATA *)Context->data; + else + { + if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)) || + !CurrentNewsSrv->list || !CurrentNewsSrv->list->data) + { + mutt_error (_("Can't post article. No connection to news server.")); + return -1; + } + nntp_data = (NNTP_DATA *)CurrentNewsSrv->list->data; + } + + if (!(f = safe_fopen (msg, "r"))) + { + mutt_error (_("Can't post article. Unable to open %s"), msg); + return -1; + } + + strfcpy (buf, "POST\r\n", sizeof (buf)); + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) + { + mutt_error (_("Can't post article. Connection to %s lost."), + nntp_data->nserv->conn->account.host); + return -1; + } + if (buf[0] != '3') + { + mutt_error (_("Can't post article: %s"), buf); + return -1; + } + + buf[0] = '.'; + buf[1] = '\0'; + while (fgets (buf + 1, sizeof (buf) - 2, f) != NULL) + { + len = strlen (buf); + if (buf[len - 1] == '\n') + { + buf[len - 1] = '\r'; + buf[len] = '\n'; + len++; + buf[len] = '\0'; + } + if (buf[1] == '.') + mutt_socket_write_d (nntp_data->nserv->conn, buf, -1, M_SOCK_LOG_HDR); + else + mutt_socket_write_d (nntp_data->nserv->conn, buf + 1, -1, M_SOCK_LOG_HDR); + } + fclose (f); + + if (buf[strlen (buf) - 1] != '\n') + mutt_socket_write_d (nntp_data->nserv->conn, "\r\n", -1, M_SOCK_LOG_HDR); + mutt_socket_write_d (nntp_data->nserv->conn, ".\r\n", -1, M_SOCK_LOG_HDR); + if (mutt_socket_readln (buf, sizeof (buf), nntp_data->nserv->conn) < 0) + { + mutt_error (_("Can't post article. Connection to %s lost."), + nntp_data->nserv->conn->account.host); + return -1; + } + if (buf[0] != '2') + { + mutt_error (_("Can't post article: %s"), buf); + return -1; + } + + return 0; +} + +/* nntp_logout_all: close all open connections. */ +void nntp_logout_all (void) +{ + char buf[LONG_STRING]; + CONNECTION* conn; + + conn = mutt_socket_head (); + + while (conn) + { + CONNECTION *next = conn->next; + + if (conn->account.type == M_ACCT_TYPE_NNTP) + { + mutt_message (_("Closing connection to %s..."), conn->account.host); + mutt_socket_write (conn, "QUIT\r\n"); + mutt_socket_readln (buf, sizeof (buf), conn); + mutt_clear_error (); + mutt_socket_close (conn); + mutt_socket_free (conn); + } + + conn = next; + } +} + +static void nntp_free_acache (NNTP_DATA *data) +{ + int i; + + for (i = 0; i < NNTP_CACHE_LEN; i++) + { + if (data->acache[i].path) + { + unlink (data->acache[i].path); + FREE (&data->acache[i].path); + } + } +} + +void nntp_delete_data (void *p) +{ + NNTP_DATA *data = (NNTP_DATA *)p; + + if (!p) + return; + FREE (&data->entries); + FREE (&data->desc); + FREE (&data->cache); + nntp_free_acache (data); + FREE (p); +} + +int nntp_sync_mailbox (CONTEXT *ctx) +{ + NNTP_DATA *data = ctx->data; + + /* CACHE: update cache and .index files */ + if ((option (OPTSAVEUNSUB) || data->subscribed)) + nntp_save_cache_group (ctx); + nntp_free_acache (data); + + data->nserv->check_time = 0; /* next nntp_check_mailbox() will really check */ + return 0; +} + +int nntp_fastclose_mailbox (CONTEXT *ctx) +{ + NNTP_DATA *data = (NNTP_DATA *) ctx->data, *tmp; + + if (!data) + return 0; + nntp_free_acache (data); + if (!data->nserv || !data->nserv->newsgroups || !data->group) + return 0; + nntp_save_cache_index (data->nserv); + if ((tmp = hash_find (data->nserv->newsgroups, data->group)) == NULL + || tmp != data) + nntp_delete_data (data); + return 0; +} + +/* commit changes and terminate connection */ +int nntp_close_mailbox (CONTEXT *ctx) +{ + if (!ctx) + return -1; + mutt_message _("Quitting newsgroup..."); + if (ctx->data) + { + NNTP_DATA *data = (NNTP_DATA *) ctx->data; + int ret; + + if (data->nserv && data->nserv->conn && ctx->unread) + { + ret = query_quadoption (OPT_CATCHUP, _("Mark all articles read?")); + if (ret == M_YES) + mutt_newsgroup_catchup (data->nserv, data->group); + else if (ret < 0) + return -1; + } + } + nntp_sync_mailbox (ctx); + if (ctx->data && ((NNTP_DATA *)ctx->data)->nserv) + { + NNTP_SERVER *news; + + news = ((NNTP_DATA *)ctx->data)->nserv; + newsrc_gen_entries (ctx); + ((NNTP_DATA *)ctx->data)->unread = ctx->unread; + mutt_newsrc_update (news); + } + mutt_clear_error(); + return 0; +} + +/* use the GROUP command to poll for new mail */ +static int _nntp_check_mailbox (CONTEXT *ctx, NNTP_DATA *nntp_data) +{ + char buf[LONG_STRING]; + int count = 0; + + if (nntp_data->nserv->check_time + NewsPollTimeout > time (NULL)) + return 0; + + buf[0] = 0; + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) + { +#ifdef DEBUG + nntp_error ("nntp_check_mailbox()", buf); +#endif + return -1; + } + if (mutt_strncmp ("211", buf, 3)) + { + buf[0] = 0; + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) + { +#ifdef DEBUG + nntp_error ("nntp_check_mailbox()", buf); +#endif + return -1; + } + } + if (!mutt_strncmp ("211", buf, 3)) + { + int first; + int last; + + sscanf (buf + 4, "%d %d %d", &count, &first, &last); + nntp_data->firstMessage = first; + nntp_data->lastMessage = last; + if (ctx && last > nntp_data->lastLoaded) + { + nntp_fetch_headers (ctx, nntp_data->lastLoaded + 1, last); + time (&nntp_data->nserv->check_time); + return 1; + } + if (!last || (!nntp_data->rc && !nntp_data->lastCached)) + nntp_data->unread = count; + else + mutt_newsgroup_stat (nntp_data); + /* active was renumbered? */ + if (last < nntp_data->lastLoaded) + { + if (!nntp_data->max) + { + nntp_data->entries = safe_calloc (5, sizeof (NEWSRC_ENTRY)); + nntp_data->max = 5; + } + nntp_data->lastCached = 0; + nntp_data->num = 1; + nntp_data->entries[0].first = 1; + nntp_data->entries[0].last = 0; + } + } + + time (&nntp_data->nserv->check_time); + return 0; +} + +int nntp_check_mailbox (CONTEXT *ctx) +{ + return _nntp_check_mailbox (ctx, (NNTP_DATA *)ctx->data); +} + +static int add_group (char *buf, void *serv) +{ +#define s ((NNTP_SERVER *) serv) + char group[LONG_STRING], mod, desc[HUGE_STRING]; + int first, last; + NNTP_DATA *nntp_data; + static int n = 0; + + _checked = n; /* _checked have N, where N = number of groups */ + if (!buf) /* at EOF must be zerouth */ + n = 0; + + if (!s || !buf) + return 0; + + *desc = 0; + sscanf (buf, "%s %d %d %c %[^\n]", group, &last, &first, &mod, desc); + if (!group) + return 0; + if ((nntp_data = (NNTP_DATA *) hash_find (s->newsgroups, group)) == NULL) + { + n++; + nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1); + nntp_data->group = (char *) nntp_data + sizeof (NNTP_DATA); + strcpy (nntp_data->group, group); + nntp_data->nserv = s; + if (s->newsgroups->nelem < s->newsgroups->curnelem * 2) + s->newsgroups = hash_resize (s->newsgroups, s->newsgroups->nelem * 2, 0); + hash_insert (s->newsgroups, nntp_data->group, nntp_data, 0); + nntp_add_to_list (s, nntp_data); + } + nntp_data->deleted = 0; + nntp_data->firstMessage = first; + nntp_data->lastMessage = last; + if (mod == 'y') + nntp_data->allowed = 1; + else + nntp_data->allowed = 0; + if (nntp_data->desc) + FREE (&nntp_data->desc); + if (*desc) + nntp_data->desc = safe_strdup (desc); + if (nntp_data->rc || nntp_data->lastCached) + mutt_newsgroup_stat (nntp_data); + else if (nntp_data->lastMessage && + nntp_data->firstMessage <= nntp_data->lastMessage) + nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1; + else + nntp_data->unread = 0; + + return 0; +#undef s +} + +int nntp_check_newgroups (NNTP_SERVER *serv, int force) +{ + char buf[LONG_STRING]; + char msg[SHORT_STRING]; + NNTP_DATA nntp_data; + LIST *l; + LIST emp; + time_t now; + struct tm *t; + unsigned int count = 0; + unsigned int total = 0; + + if (!serv || !serv->newgroups_time) + return -1; + + if (nntp_open_connection (serv) < 0) + return -1; + + /* check subscribed groups for new news */ + if (option (OPTSHOWNEWNEWS)) + { + mutt_message _("Checking for new messages..."); + for (l = serv->list; l; l = l->next) + { + serv->check_time = 0; /* really check! */ + if (l->data && ((NNTP_DATA *) l->data)->subscribed) + _nntp_check_mailbox (NULL, (NNTP_DATA *) l->data); + } + } + else if (!force) + return 0; + + mutt_message _("Checking for new newsgroups..."); + now = serv->newgroups_time; + time (&serv->newgroups_time); + t = gmtime (&now); + snprintf (buf, sizeof (buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n", + (t->tm_year % 100), t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, + t->tm_sec); + nntp_data.nserv = serv; + if (Context && Context->magic == M_NNTP) + nntp_data.group = ((NNTP_DATA *)Context->data)->group; + else + nntp_data.group = NULL; + l = serv->tail; + if (mutt_nntp_fetch (&nntp_data, buf, _("Adding new newsgroups..."), + add_group, serv, 0) != 0) + { +#ifdef DEBUG + nntp_error ("nntp_check_newgroups()", buf); +#endif + return -1; + } + + strfcpy (msg, _("Loading descriptions..."), sizeof (msg)); + mutt_message (msg); + if (l) + emp.next = l->next; + else + emp.next = serv->list; + l = &emp; + while (l->next) + { + l = l->next; + ((NNTP_DATA *) l->data)->new = 1; + total++; + } + l = &emp; + while (l->next) + { + l = l->next; + nntp_get_desc ((NNTP_DATA *) l->data, ((NNTP_DATA *) l->data)->group, NULL); + count++; + if (ReadInc && (count % ReadInc == 0)) + mutt_message ("%s %d/%d", msg, count, total); + } + if (emp.next) + nntp_save_cache_index (serv); + mutt_clear_error (); + return _checked; +} + +/* Load list of all newsgroups from cache ALL */ +int nntp_get_cache_all (NNTP_SERVER *serv) +{ + char buf[HUGE_STRING]; + FILE *f; + + nntp_cache_expand (buf, serv->cache); + if ((f = safe_fopen (buf, "r"))) + { + int i = 0; + + while (fgets (buf, sizeof(buf), f) != NULL) + { + if (ReadInc && (i % ReadInc == 0)) + mutt_message (_("Loading list from cache... %d"), i); + add_group (buf, serv); + i++; + } + add_group (NULL, NULL); + fclose (f); + mutt_clear_error (); + return 0; + } + else + { + FREE (&serv->cache); + return -1; + } +} + +/* Load list of all newsgroups from active */ +int nntp_get_active (NNTP_SERVER *serv) +{ + char msg[SHORT_STRING]; + NNTP_DATA nntp_data; + LIST *tmp; + + if (nntp_open_connection (serv) < 0) + return -1; + + snprintf (msg, sizeof(msg), _("Loading list of all newsgroups on server %s..."), + serv->conn->account.host); + mutt_message (msg); + time (&serv->newgroups_time); + nntp_data.nserv = serv; + nntp_data.group = NULL; + + if (mutt_nntp_fetch (&nntp_data, "LIST\r\n", msg, add_group, serv, 0) < 0) + { +#ifdef DEBUG + nntp_error ("nntp_get_active()", "LIST\r\n"); +#endif + return -1; + } + + strfcpy (msg, _("Loading descriptions..."), sizeof (msg)); + mutt_message (msg); + nntp_get_desc (&nntp_data, "*", msg); + + for (tmp = serv->list; tmp; tmp = tmp->next) + { + NNTP_DATA *data = (NNTP_DATA *)tmp->data; + + if (data && data->deleted && !data->rc) + { + nntp_delete_cache (data); + hash_delete (serv->newsgroups, data->group, NULL, nntp_delete_data); + tmp->data = NULL; + } + } + nntp_save_cache_index (serv); + + mutt_clear_error (); + return _checked; +} + +/* + * returns -1 if error ocurred while retrieving header, + * number of articles which ones exist in context on success. + */ +int nntp_check_msgid (CONTEXT *ctx, const char *msgid) +{ + int ret; + + /* if msgid is already in context, don't reload them */ + if (hash_find (ctx->id_hash, msgid)) + return 1; + if (ctx->msgcount == ctx->hdrmax) + mx_alloc_memory (ctx); + ctx->hdrs[ctx->msgcount] = mutt_new_header (); + ctx->hdrs[ctx->msgcount]->index = ctx->msgcount; + + mutt_message (_("Fetching %s from server..."), msgid); + ret = nntp_read_header (ctx, msgid, 0); + /* since nntp_read_header() may set read flag, we must reset it */ + ctx->hdrs[ctx->msgcount]->read = 0; + if (ret != 0) + mutt_free_header (&ctx->hdrs[ctx->msgcount]); + else + { + ctx->msgcount++; + mx_update_context (ctx, 1); + ctx->changed = 1; + } + return ret; +} + +typedef struct +{ + CONTEXT *ctx; + unsigned int num; + unsigned int max; + unsigned int *child; +} CHILD_CONTEXT; + +static int check_children (char *s, void *c) +{ +#define cc ((CHILD_CONTEXT *) c) + unsigned int i, n; + + if (!s || (n = atoi (s)) == 0) + return 0; + for (i = 0; i < cc->ctx->msgcount; i++) + if (cc->ctx->hdrs[i]->article_num == n) + return 0; + if (cc->num >= cc->max) + safe_realloc (&cc->child, sizeof (unsigned int) * (cc->max += 25)); + cc->child[cc->num++] = n; + + return 0; +#undef cc +} + +int nntp_check_children (CONTEXT *ctx, const char *msgid) +{ + NNTP_DATA *nntp_data = (NNTP_DATA *)ctx->data; + char buf[STRING]; + int i, ret = 0, tmp = 0; + CHILD_CONTEXT cc; + + if (!nntp_data || !nntp_data->nserv || !nntp_data->nserv->conn || + !nntp_data->nserv->conn->account.host) + return -1; + if (nntp_data->firstMessage > nntp_data->lastLoaded) + return 0; + if (!nntp_data->nserv->hasXPAT) + { + mutt_error (_("Server %s does not support this operation!"), + nntp_data->nserv->conn->account.host); + return -1; + } + + snprintf (buf, sizeof (buf), "XPAT References %d-%d *%s*\r\n", + nntp_data->firstMessage, nntp_data->lastLoaded, msgid); + + cc.ctx = ctx; + cc.num = 0; + cc.max = 25; + cc.child = safe_malloc (sizeof (unsigned int) * 25); + if (mutt_nntp_fetch (nntp_data, buf, NULL, check_children, &cc, 0)) + { + FREE (&cc.child); + return -1; + } + /* dont try to read the xover cache. check_children() already + * made sure that we dont have the article, so we need to visit + * the server. Reading the cache at this point is also bad + * because it would duplicate messages */ + if (option (OPTNEWSCACHE)) + { + tmp++; + unset_option (OPTNEWSCACHE); + } + for (i = 0; i < cc.num; i++) + { + if ((ret = nntp_fetch_headers (ctx, cc.child[i], cc.child[i]))) + break; + if (ctx->msgcount && + ctx->hdrs[ctx->msgcount - 1]->article_num == cc.child[i]) + ctx->hdrs[ctx->msgcount - 1]->read = 0; + } + if (tmp) + set_option (OPTNEWSCACHE); + FREE (&cc.child); + return ret; +} diff -udprP mutt-1.5.20.orig/nntp.h mutt-1.5.20/nntp.h --- mutt-1.5.20.orig/nntp.h 1970-01-01 03:00:00.000000000 +0300 +++ mutt-1.5.20/nntp.h 2009-06-15 21:05:24.000000000 +0300 @@ -0,0 +1,136 @@ +/* + * Copyright (C) 1998 Brandon Long <blong@fiction.net> + * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net> + * Copyright (C) 2000-2007 Vsevolod Volkov <vvv@mutt.org.ua> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _NNTP_H_ +#define _NNTP_H_ 1 + +#include "mutt_socket.h" +#include "mailbox.h" + +#include <time.h> + +#define NNTP_PORT 119 +#define NNTP_SSL_PORT 563 + +/* number of entries in the hash table */ +#define NNTP_CACHE_LEN 10 + +enum +{ + NNTP_NONE = 0, + NNTP_OK, + NNTP_BYE +}; + +typedef struct +{ + int first; + int last; +} NEWSRC_ENTRY; + +typedef struct +{ + unsigned int hasXPAT : 1; + unsigned int hasXGTITLE : 1; + unsigned int hasXOVER : 1; + unsigned int hasLISTGROUP : 1; + unsigned int status : 3; + char *newsrc; + char *cache; + int stat; + off_t size; + time_t mtime; + time_t newgroups_time; + time_t check_time; + HASH *newsgroups; + LIST *list; /* list of newsgroups */ + LIST *tail; /* last entry of list */ + CONNECTION *conn; +} NNTP_SERVER; + +typedef struct +{ + unsigned int index; + char *path; +} NNTP_CACHE; + +typedef struct +{ + NEWSRC_ENTRY *entries; + unsigned int num; /* number of used entries */ + unsigned int max; /* number of allocated entries */ + unsigned int unread; + unsigned int firstMessage; + unsigned int lastMessage; + unsigned int lastLoaded; + unsigned int lastCached; + unsigned int subscribed : 1; + unsigned int rc : 1; + unsigned int new : 1; + unsigned int allowed : 1; + unsigned int deleted : 1; + char *group; + char *desc; + char *cache; + NNTP_SERVER *nserv; + NNTP_CACHE acache[NNTP_CACHE_LEN]; +} NNTP_DATA; + +/* internal functions */ +int nntp_get_active (NNTP_SERVER *); +int nntp_get_cache_all (NNTP_SERVER *); +int nntp_save_cache_index (NNTP_SERVER *); +int nntp_check_newgroups (NNTP_SERVER *, int); +int nntp_save_cache_group (CONTEXT *); +int nntp_parse_url (const char *, ACCOUNT *, char *, size_t); +void newsrc_gen_entries (CONTEXT *); +void nntp_get_status (CONTEXT *, HEADER *, char *, int); +void mutt_newsgroup_stat (NNTP_DATA *); +void nntp_delete_cache (NNTP_DATA *); +void nntp_add_to_list (NNTP_SERVER *, NNTP_DATA *); +void nntp_cache_expand (char *, const char *); +void nntp_delete_data (void *); + +/* exposed interface */ +NNTP_SERVER *mutt_select_newsserver (char *); +NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *, char *); +NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *, char *); +NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *, char *); +NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *, char *); +void nntp_clear_cacheindex (NNTP_SERVER *); +int mutt_newsrc_update (NNTP_SERVER *); +int nntp_open_mailbox (CONTEXT *); +int nntp_sync_mailbox (CONTEXT *); +int nntp_check_mailbox (CONTEXT *); +int nntp_close_mailbox (CONTEXT *); +int nntp_fastclose_mailbox (CONTEXT *); +int nntp_fetch_message (MESSAGE *, CONTEXT *, int); +int nntp_post (const char *); +int nntp_check_msgid (CONTEXT *, const char *); +int nntp_check_children (CONTEXT *, const char *); +void nntp_buffy (char *); +void nntp_expand_path (char *, size_t, ACCOUNT *); +void nntp_logout_all (); +const char *nntp_format_str (char *, size_t, size_t, char, const char *, const char *, + const char *, const char *, unsigned long, format_flag); + +NNTP_SERVER *CurrentNewsSrv INITVAL (NULL); + +#endif /* _NNTP_H_ */ diff -udprP mutt-1.5.20.orig/pager.c mutt-1.5.20/pager.c --- mutt-1.5.20.orig/pager.c 2009-06-03 23:48:31.000000000 +0300 +++ mutt-1.5.20/pager.c 2009-06-15 21:05:24.000000000 +0300 @@ -1059,6 +1059,11 @@ fill_buffer (FILE *f, LOFF_T *last_pos, return b_read; } +#ifdef USE_NNTP +#include "mx.h" +#include "nntp.h" +#endif + static int format_line (struct line_t **lineInfo, int n, unsigned char *buf, int flags, ansi_attr *pa, int cnt, @@ -1512,6 +1517,16 @@ static struct mapping_t PagerHelpExtra[] { NULL, 0 } }; +#ifdef USE_NNTP +static struct mapping_t PagerNewsHelpExtra[] = { + { N_("Post"), OP_POST }, + { N_("Followup"), OP_FOLLOWUP }, + { N_("Del"), OP_DELETE }, + { N_("Next"), OP_MAIN_NEXT_UNDELETED }, + { NULL, 0 } +}; +#endif + /* This pager is actually not so simple as it once was. It now operates in @@ -1553,6 +1568,10 @@ mutt_pager (const char *banner, const ch int old_PagerIndexLines; /* some people want to resize it * while inside the pager... */ +#ifdef USE_NNTP + char *followup_to; +#endif + if (!(flags & M_SHOWCOLOR)) flags |= M_SHOWFLAT; @@ -1592,7 +1611,11 @@ mutt_pager (const char *banner, const ch if (IsHeader (extra)) { strfcpy (tmphelp, helpstr, sizeof (tmphelp)); - mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER, PagerHelpExtra); + mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER, +#ifdef USE_NNTP + (Context && (Context->magic == M_NNTP)) ? PagerNewsHelpExtra : +#endif + PagerHelpExtra); snprintf (helpstr, sizeof (helpstr), "%s %s", tmphelp, buffer); } if (!InHelp) @@ -2465,6 +2488,15 @@ search_next: CHECK_READONLY; CHECK_ACL(M_ACL_WRITE, "flag message"); +#ifdef USE_NNTP + if (Context->magic == M_NNTP) + { + mutt_flushinp (); + mutt_error _("Can't change 'important' flag on NNTP server."); + break; + } +#endif + mutt_set_flag (Context, extra->hdr, M_FLAG, !extra->hdr->flagged); redraw = REDRAW_STATUS | REDRAW_INDEX; if (option (OPTRESOLVE)) @@ -2498,6 +2530,60 @@ search_next: redraw = REDRAW_FULL; break; +#ifdef USE_NNTP + case OP_POST: + CHECK_MODE(IsHeader (extra) && !IsAttach (extra)); + CHECK_ATTACH; + if (extra->ctx && extra->ctx->magic == M_NNTP && + !((NNTP_DATA *)extra->ctx->data)->allowed && + query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != M_YES) + break; + ci_send_message (SENDNEWS, NULL, NULL, extra->ctx, NULL); + redraw = REDRAW_FULL; + break; + + case OP_FORWARD_TO_GROUP: + CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); + CHECK_ATTACH; + if (extra->ctx && extra->ctx->magic == M_NNTP && + !((NNTP_DATA *)extra->ctx->data)->allowed && + query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != M_YES) + break; + if (IsMsgAttach (extra)) + mutt_attach_forward (extra->fp, extra->hdr, extra->idx, + extra->idxlen, extra->bdy, SENDNEWS); + else + ci_send_message (SENDNEWS|SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr); + redraw = REDRAW_FULL; + break; + + case OP_FOLLOWUP: + CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); + CHECK_ATTACH; + + if (IsMsgAttach (extra)) + followup_to = extra->bdy->hdr->env->followup_to; + else + followup_to = extra->hdr->env->followup_to; + + if (!followup_to || mutt_strcasecmp (followup_to, "poster") || + query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != M_YES) + { + if (extra->ctx && extra->ctx->magic == M_NNTP && + !((NNTP_DATA *)extra->ctx->data)->allowed && + query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != M_YES) + break; + if (IsMsgAttach (extra)) + mutt_attach_reply (extra->fp, extra->hdr, extra->idx, + extra->idxlen, extra->bdy, SENDNEWS|SENDREPLY); + else + ci_send_message (SENDNEWS|SENDREPLY, NULL, NULL, + extra->ctx, extra->hdr); + redraw = REDRAW_FULL; + break; + } +#endif + case OP_REPLY: CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); CHECK_ATTACH; @@ -2544,7 +2630,7 @@ search_next: CHECK_ATTACH; if (IsMsgAttach (extra)) mutt_attach_forward (extra->fp, extra->hdr, extra->idx, - extra->idxlen, extra->bdy); + extra->idxlen, extra->bdy, 0); else ci_send_message (SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr); redraw = REDRAW_FULL; diff -udprP mutt-1.5.20.orig/parse.c mutt-1.5.20/parse.c --- mutt-1.5.20.orig/parse.c 2009-06-01 19:29:32.000000000 +0300 +++ mutt-1.5.20/parse.c 2009-06-15 21:05:24.000000000 +0300 @@ -89,7 +89,7 @@ char *mutt_read_rfc822_line (FILE *f, ch /* not reached */ } -static LIST *mutt_parse_references (char *s, int in_reply_to) +LIST *mutt_parse_references (char *s, int in_reply_to) { LIST *t, *lst = NULL; char *m; @@ -1067,6 +1067,17 @@ int mutt_parse_rfc822_line (ENVELOPE *e, e->from = rfc822_parse_adrlist (e->from, p); matched = 1; } +#ifdef USE_NNTP + else if (!mutt_strcasecmp (line+1, "ollowup-to")) + { + if (!e->followup_to) + { + mutt_remove_trailing_ws (p); + e->followup_to = safe_strdup (mutt_skip_whitespace (p)); + } + matched = 1; + } +#endif break; case 'i': @@ -1149,6 +1160,27 @@ int mutt_parse_rfc822_line (ENVELOPE *e, } break; +#ifdef USE_NNTP + case 'n': + if (!mutt_strcasecmp (line + 1, "ewsgroups")) + { + FREE (&e->newsgroups); + mutt_remove_trailing_ws (p); + e->newsgroups = safe_strdup (mutt_skip_whitespace (p)); + matched = 1; + } + break; +#endif + + case 'o': + /* field `Organization:' saves only for pager! */ + if (!mutt_strcasecmp (line + 1, "rganization")) + { + if (!e->organization && mutt_strcasecmp (p, "unknown")) + e->organization = safe_strdup (p); + } + break; + case 'r': if (!ascii_strcasecmp (line + 1, "eferences")) { @@ -1257,6 +1289,20 @@ int mutt_parse_rfc822_line (ENVELOPE *e, e->x_label = safe_strdup(p); matched = 1; } +#ifdef USE_NNTP + else if (!mutt_strcasecmp (line + 1, "-comment-to")) + { + if (!e->x_comment_to) + e->x_comment_to = safe_strdup (p); + matched = 1; + } + else if (!mutt_strcasecmp (line + 1, "ref")) + { + if (!e->xref) + e->xref = safe_strdup (p); + matched = 1; + } +#endif default: break; diff -udprP mutt-1.5.20.orig/pattern.c mutt-1.5.20/pattern.c --- mutt-1.5.20.orig/pattern.c 2009-06-03 23:48:31.000000000 +0300 +++ mutt-1.5.20/pattern.c 2009-06-15 21:05:24.000000000 +0300 @@ -91,6 +91,9 @@ Flags[] = { 'U', M_UNREAD, 0, NULL }, { 'v', M_COLLAPSED, 0, NULL }, { 'V', M_CRYPT_VERIFIED, 0, NULL }, +#ifdef USE_NNTP + { 'w', M_NEWSGROUPS, 0, eat_regexp }, +#endif { 'x', M_REFERENCE, 0, eat_regexp }, { 'X', M_MIMEATTACH, 0, eat_range }, { 'y', M_XLABEL, 0, eat_regexp }, @@ -1204,6 +1207,10 @@ mutt_pattern_exec (struct pattern_t *pat } case M_UNREFERENCED: return (pat->not ^ (h->thread && !h->thread->child)); +#ifdef USE_NNTP + case M_NEWSGROUPS: + return (pat->not ^ (h->env->newsgroups && patmatch (pat, h->env->newsgroups) == 0)); +#endif } mutt_error (_("error: unknown op %d (report this error)."), pat->op); return (-1); @@ -1285,6 +1292,7 @@ int mutt_pattern_func (int op, char *pro progress_t progress; strfcpy (buf, NONULL (Context->pattern), sizeof (buf)); + if (prompt || op != M_LIMIT) if (mutt_get_field (prompt, buf, sizeof (buf), M_PATTERN | M_CLEAR) != 0 || !buf[0]) return (-1); diff -udprP mutt-1.5.20.orig/po/POTFILES.in mutt-1.5.20/po/POTFILES.in --- mutt-1.5.20.orig/po/POTFILES.in 2008-11-11 21:55:47.000000000 +0200 +++ mutt-1.5.20/po/POTFILES.in 2009-06-15 21:05:24.000000000 +0300 @@ -46,6 +46,8 @@ mutt_ssl_gnutls.c mutt_tunnel.c muttlib.c mx.c +newsrc.c +nntp.c pager.c parse.c pattern.c diff -udprP mutt-1.5.20.orig/postpone.c mutt-1.5.20/postpone.c --- mutt-1.5.20.orig/postpone.c 2009-06-14 00:28:37.000000000 +0300 +++ mutt-1.5.20/postpone.c 2009-06-15 21:05:24.000000000 +0300 @@ -124,15 +124,26 @@ int mutt_num_postponed (int force) if (LastModify < st.st_mtime) { +#ifdef USE_NNTP + int optnews = option (OPTNEWS); +#endif LastModify = st.st_mtime; if (access (Postponed, R_OK | F_OK) != 0) return (PostCount = 0); +#ifdef USE_NNTP + if (optnews) + unset_option (OPTNEWS); +#endif if (mx_open_mailbox (Postponed, M_NOSORT | M_QUIET, &ctx) == NULL) PostCount = 0; else PostCount = ctx.msgcount; mx_fastclose_mailbox (&ctx); +#ifdef USE_NNTP + if (optnews) + set_option (OPTNEWS); +#endif } return (PostCount); diff -udprP mutt-1.5.20.orig/protos.h mutt-1.5.20/protos.h --- mutt-1.5.20.orig/protos.h 2009-06-13 02:38:52.000000000 +0300 +++ mutt-1.5.20/protos.h 2009-06-15 21:05:24.000000000 +0300 @@ -115,6 +115,7 @@ HASH *mutt_make_id_hash (CONTEXT *); HASH *mutt_make_subj_hash (CONTEXT *); LIST *mutt_make_references(ENVELOPE *e); +LIST *mutt_parse_references (char *, int); char *mutt_read_rfc822_line (FILE *, char *, size_t *); ENVELOPE *mutt_read_rfc822_header (FILE *, HEADER *, short, short); diff -udprP mutt-1.5.20.orig/recvattach.c mutt-1.5.20/recvattach.c --- mutt-1.5.20.orig/recvattach.c 2009-05-19 03:11:35.000000000 +0300 +++ mutt-1.5.20/recvattach.c 2009-06-15 21:05:24.000000000 +0300 @@ -1110,6 +1110,15 @@ void mutt_view_attachments (HEADER *hdr) } #endif +#ifdef USE_NNTP + if (Context->magic == M_NNTP) + { + mutt_flushinp (); + mutt_error _("Can't delete attachment from newsserver."); + break; + } +#endif + if (WithCrypto && hdr->security & ~PGP_TRADITIONAL_CHECKED) { mutt_message _( @@ -1201,10 +1210,33 @@ void mutt_view_attachments (HEADER *hdr) case OP_FORWARD_MESSAGE: CHECK_ATTACH; mutt_attach_forward (fp, hdr, idx, idxlen, - menu->tagprefix ? NULL : idx[menu->current]->content); + menu->tagprefix ? NULL : idx[menu->current]->content, 0); menu->redraw = REDRAW_FULL; break; +#ifdef USE_NNTP + case OP_FORWARD_TO_GROUP: + CHECK_ATTACH; + mutt_attach_forward (fp, hdr, idx, idxlen, + menu->tagprefix ? NULL : idx[menu->current]->content, SENDNEWS); + menu->redraw = REDRAW_FULL; + break; + + case OP_FOLLOWUP: + CHECK_ATTACH; + + if (!idx[menu->current]->content->hdr->env->followup_to || + mutt_strcasecmp (idx[menu->current]->content->hdr->env->followup_to, "poster") || + query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != M_YES) + { + mutt_attach_reply (fp, hdr, idx, idxlen, + menu->tagprefix ? NULL : idx[menu->current]->content, + SENDNEWS|SENDREPLY); + menu->redraw = REDRAW_FULL; + break; + } +#endif + case OP_REPLY: case OP_GROUP_REPLY: case OP_LIST_REPLY: diff -udprP mutt-1.5.20.orig/recvcmd.c mutt-1.5.20/recvcmd.c --- mutt-1.5.20.orig/recvcmd.c 2009-06-12 20:24:17.000000000 +0300 +++ mutt-1.5.20/recvcmd.c 2009-06-15 21:05:24.000000000 +0300 @@ -401,7 +401,7 @@ static BODY ** copy_problematic_attachme static void attach_forward_bodies (FILE * fp, HEADER * hdr, ATTACHPTR ** idx, short idxlen, BODY * cur, - short nattach) + short nattach, int flags) { short i; short mime_fwd_all = 0; @@ -547,7 +547,7 @@ _("Can't decode all tagged attachments. tmpfp = NULL; /* now that we have the template, send it. */ - ci_send_message (0, tmphdr, tmpbody, NULL, parent); + ci_send_message (flags, tmphdr, tmpbody, NULL, parent); return; bail: @@ -574,7 +574,7 @@ _("Can't decode all tagged attachments. */ static void attach_forward_msgs (FILE * fp, HEADER * hdr, - ATTACHPTR ** idx, short idxlen, BODY * cur) + ATTACHPTR ** idx, short idxlen, BODY * cur, int flags) { HEADER *curhdr = NULL; HEADER *tmphdr; @@ -679,23 +679,23 @@ static void attach_forward_msgs (FILE * else mutt_free_header (&tmphdr); - ci_send_message (0, tmphdr, *tmpbody ? tmpbody : NULL, + ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr); } void mutt_attach_forward (FILE * fp, HEADER * hdr, - ATTACHPTR ** idx, short idxlen, BODY * cur) + ATTACHPTR ** idx, short idxlen, BODY * cur, int flags) { short nattach; if (check_all_msg (idx, idxlen, cur, 0) == 0) - attach_forward_msgs (fp, hdr, idx, idxlen, cur); + attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags); else { nattach = count_tagged (idx, idxlen); - attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach); + attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags); } } @@ -753,28 +753,40 @@ attach_reply_envelope_defaults (ENVELOPE return -1; } - if (parent) +#ifdef USE_NNTP + if ((flags & SENDNEWS)) { - if (mutt_fetch_recips (env, curenv, flags) == -1) - return -1; + /* in case followup set Newsgroups: with Followup-To: if it present */ + if (!env->newsgroups && curenv && + mutt_strcasecmp (curenv->followup_to, "poster")) + env->newsgroups = safe_strdup (curenv->followup_to); } else +#endif { - for (i = 0; i < idxlen; i++) + if (parent) { - if (idx[i]->content->tagged - && mutt_fetch_recips (env, idx[i]->content->hdr->env, flags) == -1) + if (mutt_fetch_recips (env, curenv, flags) == -1) return -1; } + else + { + for (i = 0; i < idxlen; i++) + { + if (idx[i]->content->tagged + && mutt_fetch_recips (env, idx[i]->content->hdr->env, flags) == -1) + return -1; + } + } + + if ((flags & SENDLISTREPLY) && !env->to) + { + mutt_error _("No mailing lists found!"); + return (-1); + } + + mutt_fix_reply_recipients (env); } - - if ((flags & SENDLISTREPLY) && !env->to) - { - mutt_error _("No mailing lists found!"); - return (-1); - } - - mutt_fix_reply_recipients (env); mutt_make_misc_reply_headers (env, Context, curhdr, curenv); if (parent) @@ -835,6 +847,13 @@ void mutt_attach_reply (FILE * fp, HEADE char prefix[SHORT_STRING]; int rc; +#ifdef USE_NNTP + if (flags & SENDNEWS) + set_option (OPTNEWSSEND); + else + unset_option (OPTNEWSSEND); +#endif + if (check_all_msg (idx, idxlen, cur, 0) == -1) { nattach = count_tagged (idx, idxlen); diff -udprP mutt-1.5.20.orig/rfc1524.c mutt-1.5.20/rfc1524.c --- mutt-1.5.20.orig/rfc1524.c 2009-05-30 20:20:08.000000000 +0300 +++ mutt-1.5.20/rfc1524.c 2009-06-15 21:05:24.000000000 +0300 @@ -569,13 +569,13 @@ int rfc1524_expand_filename (char *namet * safe_fopen(). */ -int mutt_rename_file (char *oldfile, char *newfile) +int _mutt_rename_file (char *oldfile, char *newfile, int overwrite) { FILE *ofp, *nfp; if (access (oldfile, F_OK) != 0) return 1; - if (access (newfile, F_OK) == 0) + if (!overwrite && access (newfile, F_OK) == 0) return 2; if ((ofp = fopen (oldfile,"r")) == NULL) return 3; @@ -590,3 +590,8 @@ int mutt_rename_file (char *oldfile, cha mutt_unlink (oldfile); return 0; } + +int mutt_rename_file (char *oldfile, char *newfile) +{ + return _mutt_rename_file (oldfile, newfile, 0); +} diff -udprP mutt-1.5.20.orig/rfc1524.h mutt-1.5.20/rfc1524.h --- mutt-1.5.20.orig/rfc1524.h 2008-11-11 21:55:47.000000000 +0200 +++ mutt-1.5.20/rfc1524.h 2009-06-15 21:05:24.000000000 +0300 @@ -40,5 +40,6 @@ int rfc1524_expand_command (BODY *, char int rfc1524_expand_filename (char *, char *, char *, size_t); int rfc1524_mailcap_lookup (BODY *, char *, rfc1524_entry *, int); int mutt_rename_file (char *, char *); +int _mutt_rename_file (char *, char *, int); #endif /* _RFC1524_H */ diff -udprP mutt-1.5.20.orig/send.c mutt-1.5.20/send.c --- mutt-1.5.20.orig/send.c 2009-06-13 02:38:52.000000000 +0300 +++ mutt-1.5.20/send.c 2009-06-15 21:13:13.000000000 +0300 @@ -44,6 +44,11 @@ #include <sys/types.h> #include <utime.h> +#ifdef USE_NNTP +#include "nntp.h" +#include "mx.h" +#endif + #ifdef MIXMASTER #include "remailer.h" #endif @@ -213,17 +218,51 @@ static int edit_address (ADDRESS **a, /* return 0; } -static int edit_envelope (ENVELOPE *en) +static int edit_envelope (ENVELOPE *en, int flags) { char buf[HUGE_STRING]; LIST *uh = UserHeader; - if (edit_address (&en->to, "To: ") == -1 || en->to == NULL) - return (-1); - if (option (OPTASKCC) && edit_address (&en->cc, "Cc: ") == -1) - return (-1); - if (option (OPTASKBCC) && edit_address (&en->bcc, "Bcc: ") == -1) - return (-1); +#ifdef USE_NNTP + if (option (OPTNEWSSEND)) + { + if (en->newsgroups) + strfcpy (buf, en->newsgroups, sizeof (buf)); + else + buf[0] = 0; + if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) != 0) + return (-1); + FREE (&en->newsgroups); + en->newsgroups = safe_strdup (buf); + + if (en->followup_to) + strfcpy (buf, en->followup_to, sizeof (buf)); + else + buf[0] = 0; + if (option (OPTASKFOLLOWUP) && mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) != 0) + return (-1); + FREE (&en->followup_to); + en->followup_to = safe_strdup (buf); + + if (en->x_comment_to) + strfcpy (buf, en->x_comment_to, sizeof (buf)); + else + buf[0] = 0; + if (option (OPTXCOMMENTTO) && option (OPTASKXCOMMENTTO) && mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) != 0) + return (-1); + FREE (&en->x_comment_to); + en->x_comment_to = safe_strdup (buf); + } + else +#endif + { + if (edit_address (&en->to, "To: ") == -1 || en->to == NULL) + return (-1); + if (option (OPTASKCC) && edit_address (&en->cc, "Cc: ") == -1) + return (-1); + if (option (OPTASKBCC) && edit_address (&en->bcc, "Bcc: ") == -1) + return (-1); + } if (en->subject) { @@ -259,6 +298,14 @@ static int edit_envelope (ENVELOPE *en) return 0; } +#ifdef USE_NNTP +char *nntp_get_header (const char *s) +{ + SKIPWS (s); + return safe_strdup (s); +} +#endif + static void process_user_recips (ENVELOPE *env) { LIST *uh = UserHeader; @@ -271,6 +318,14 @@ static void process_user_recips (ENVELOP env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3); else if (ascii_strncasecmp ("bcc:", uh->data, 4) == 0) env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4); +#ifdef USE_NNTP + else if (ascii_strncasecmp ("newsgroups:", uh->data, 11) == 0) + env->newsgroups = nntp_get_header (uh->data + 11); + else if (ascii_strncasecmp ("followup-to:", uh->data, 12) == 0) + env->followup_to = nntp_get_header (uh->data + 12); + else if (ascii_strncasecmp ("x-comment-to:", uh->data, 13) == 0) + env->x_comment_to = nntp_get_header (uh->data + 13); +#endif } } @@ -309,6 +364,12 @@ static void process_user_header (ENVELOP else if (ascii_strncasecmp ("to:", uh->data, 3) != 0 && ascii_strncasecmp ("cc:", uh->data, 3) != 0 && ascii_strncasecmp ("bcc:", uh->data, 4) != 0 && +#ifdef USE_NNTP + ascii_strncasecmp ("newsgroups:", uh->data, 11) != 0 && + ascii_strncasecmp ("followup-to:", uh->data, 12) != 0 && + ascii_strncasecmp ("x-comment-to:", uh->data, 13) != 0 && +#endif + ascii_strncasecmp ("supersedes:", uh->data, 11) != 0 && ascii_strncasecmp ("subject:", uh->data, 8) != 0 && ascii_strncasecmp ("return-path:", uh->data, 12) != 0) { @@ -657,6 +718,10 @@ void mutt_add_to_reference_headers (ENVE if (pp) *pp = p; if (qq) *qq = q; +#ifdef USE_NNTP + if (option (OPTNEWSSEND) && option (OPTXCOMMENTTO) && curenv->from) + env->x_comment_to = safe_strdup (mutt_get_name (curenv->from)); +#endif } static void @@ -719,6 +784,16 @@ envelope_defaults (ENVELOPE *env, CONTEX if (flags & SENDREPLY) { +#ifdef USE_NNTP + if ((flags & SENDNEWS)) + { + /* in case followup set Newsgroups: with Followup-To: if it present */ + if (!env->newsgroups && curenv && + mutt_strcasecmp (curenv->followup_to, "poster")) + env->newsgroups = safe_strdup (curenv->followup_to); + } + else +#endif if (tag) { HEADER *h; @@ -865,7 +940,18 @@ void mutt_set_followup_to (ENVELOPE *e) * it hasn't already been set */ - if (option (OPTFOLLOWUPTO) && !e->mail_followup_to) + if (!option (OPTFOLLOWUPTO)) + return; +#ifdef USE_NNTP + if (option (OPTNEWSSEND)) + { + if (!e->followup_to && e->newsgroups && (strrchr (e->newsgroups, ','))) + e->followup_to = safe_strdup (e->newsgroups); + return; + } +#endif + + if (!e->mail_followup_to) { if (mutt_is_list_cc (0, e->to, e->cc)) { @@ -1026,6 +1112,9 @@ static int send_message (HEADER *msg) #endif #if USE_SMTP +#ifdef USE_NNTP + if (!option (OPTNEWSSEND)) +#endif /* USE_NNTP */ if (SmtpUrl) return mutt_smtp_send (msg->env->from, msg->env->to, msg->env->cc, msg->env->bcc, tempfile, @@ -1137,6 +1226,13 @@ ci_send_message (int flags, /* send mod int rv = -1; +#ifdef USE_NNTP + if (flags & SENDNEWS) + set_option (OPTNEWSSEND); + else + unset_option (OPTNEWSSEND); +#endif + if (!flags && !msg && quadoption (OPT_RECALL) != M_NO && mutt_num_postponed (1)) { @@ -1167,6 +1263,22 @@ ci_send_message (int flags, /* send mod { if ((flags = mutt_get_postponed (ctx, msg, &cur, fcc, sizeof (fcc))) < 0) goto cleanup; +#ifdef USE_NNTP + /* + * If postponed message is a news article, it have + * a "Newsgroups:" header line, then set appropriate flag. + */ + if (msg->env->newsgroups) + { + flags |= SENDNEWS; + set_option (OPTNEWSSEND); + } + else + { + flags &= ~SENDNEWS; + unset_option (OPTNEWSSEND); + } +#endif } if (flags & (SENDPOSTPONED|SENDRESEND)) @@ -1278,11 +1390,16 @@ ci_send_message (int flags, /* send mod if (flags & SENDREPLY) mutt_fix_reply_recipients (msg->env); +#ifdef USE_NNTP + if ((flags & SENDNEWS) && ctx && ctx->magic == M_NNTP && !msg->env->newsgroups) + msg->env->newsgroups = safe_strdup (((NNTP_DATA *)ctx->data)->group); +#endif + if (! (flags & SENDMAILX) && ! (option (OPTAUTOEDIT) && option (OPTEDITHDRS)) && ! ((flags & SENDREPLY) && option (OPTFASTREPLY))) { - if (edit_envelope (msg->env) == -1) + if (edit_envelope (msg->env, flags) == -1) goto cleanup; } @@ -1539,6 +1656,11 @@ main_loop: if (i == -1) { /* abort */ +#ifdef USE_NNTP + if (flags & SENDNEWS) + mutt_message _("Article not posted."); + else +#endif mutt_message _("Mail not sent."); goto cleanup; } @@ -1571,6 +1693,9 @@ main_loop: } } +#ifdef USE_NNTP + if (!(flags & SENDNEWS)) +#endif if (!has_recips (msg->env->to) && !has_recips (msg->env->cc) && !has_recips (msg->env->bcc)) { @@ -1604,6 +1729,19 @@ main_loop: mutt_error _("No subject specified."); goto main_loop; } +#ifdef USE_NNTP + if ((flags & SENDNEWS) && !msg->env->subject) + { + mutt_error _("No subject specified."); + goto main_loop; + } + + if ((flags & SENDNEWS) && !msg->env->newsgroups) + { + mutt_error _("No newsgroup specified."); + goto main_loop; + } +#endif if (msg->content->next) msg->content = mutt_make_multipart (msg->content); @@ -1810,7 +1948,12 @@ full_fcc: } } else if (!option (OPTNOCURSES) && ! (flags & SENDMAILX)) - mutt_message (i == 0 ? _("Mail sent.") : _("Sending in background.")); + mutt_message (i != 0 ? _("Sending in background.") : +#ifdef USE_NNTP + (flags & SENDNEWS) ? _("Article posted.") : _("Mail sent.")); +#else + _("Mail sent.")); +#endif if (WithCrypto && (msg->security & ENCRYPT)) FREE (&pgpkeylist); diff -udprP mutt-1.5.20.orig/sendlib.c mutt-1.5.20/sendlib.c --- mutt-1.5.20.orig/sendlib.c 2009-06-14 18:46:11.000000000 +0300 +++ mutt-1.5.20/sendlib.c 2009-06-15 21:51:17.000000000 +0300 @@ -46,6 +46,10 @@ #include <sys/wait.h> #include <fcntl.h> +#ifdef USE_NNTP +#include "nntp.h" +#endif + #ifdef HAVE_SYSEXITS_H #include <sysexits.h> #else /* Make sure EX_OK is defined <philiph@pobox.com> */ @@ -1868,6 +1872,9 @@ int mutt_write_rfc822_header (FILE *fp, mutt_write_address_list (env->to, fp, 4, 0); } else if (mode > 0) +#ifdef USE_NNTP + if (!option (OPTNEWSSEND)) +#endif fputs ("To: \n", fp); if (env->cc) @@ -1876,6 +1883,9 @@ int mutt_write_rfc822_header (FILE *fp, mutt_write_address_list (env->cc, fp, 4, 0); } else if (mode > 0) +#ifdef USE_NNTP + if (!option (OPTNEWSSEND)) +#endif fputs ("Cc: \n", fp); if (env->bcc) @@ -1887,8 +1897,28 @@ int mutt_write_rfc822_header (FILE *fp, } } else if (mode > 0) +#ifdef USE_NNTP + if (!option (OPTNEWSSEND)) +#endif fputs ("Bcc: \n", fp); +#ifdef USE_NNTP + if (env->newsgroups) + fprintf (fp, "Newsgroups: %s\n", env->newsgroups); + else if (mode == 1 && option (OPTNEWSSEND)) + fputs ("Newsgroups: \n", fp); + + if (env->followup_to) + fprintf (fp, "Followup-To: %s\n", env->followup_to); + else if (mode == 1 && option (OPTNEWSSEND)) + fputs ("Followup-To: \n", fp); + + if (env->x_comment_to) + fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to); + else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO)) + fputs ("X-Comment-To: \n", fp); +#endif + if (env->subject) mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0); else if (mode == 1) @@ -1907,6 +1937,9 @@ int mutt_write_rfc822_header (FILE *fp, fputs ("Reply-To: \n", fp); if (env->mail_followup_to) +#ifdef USE_NNTP + if (!option (OPTNEWSSEND)) +#endif { fputs ("Mail-Followup-To: ", fp); mutt_write_address_list (env->mail_followup_to, fp, 18, 0); @@ -2245,11 +2278,30 @@ mutt_invoke_sendmail (ADDRESS *from, /* const char *msg, /* file containing message */ int eightbit) /* message contains 8bit chars */ { - char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL; + char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL; char **args = NULL; size_t argslen = 0, argsmax = 0; int i; +#ifdef USE_NNTP + if (option (OPTNEWSSEND)) + { + char cmd[LONG_STRING]; + + mutt_FormatString (cmd, sizeof (cmd), 0, NONULL (Inews), nntp_format_str, 0, 0); + if (!*cmd) + { + i = nntp_post (msg); + unlink (msg); + return i; + } + + s = safe_strdup (cmd); + } + else +#endif + s = safe_strdup (Sendmail); + ps = s; i = 0; while ((ps = strtok (ps, " "))) @@ -2273,6 +2325,10 @@ mutt_invoke_sendmail (ADDRESS *from, /* i++; } +#ifdef USE_NNTP + if (!option (OPTNEWSSEND)) + { +#endif if (eightbit && option (OPTUSE8BITMIME)) args = add_option (args, &argslen, &argsmax, "-B8BITMIME"); @@ -2304,6 +2360,9 @@ mutt_invoke_sendmail (ADDRESS *from, /* args = add_args (args, &argslen, &argsmax, to); args = add_args (args, &argslen, &argsmax, cc); args = add_args (args, &argslen, &argsmax, bcc); +#ifdef USE_NNTP + } +#endif if (argslen == argsmax) safe_realloc (&args, sizeof (char *) * (++argsmax)); @@ -2384,6 +2443,9 @@ void mutt_prepare_envelope (ENVELOPE *en rfc2047_encode_string (&env->x_label); if (env->subject) +#ifdef USE_NNTP + if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT)) +#endif { rfc2047_encode_string (&env->subject); } @@ -2504,6 +2566,10 @@ int mutt_bounce_message (FILE *fp, HEADE } rfc822_write_address (resent_from, sizeof (resent_from), from, 0); +#ifdef USE_NNTP + unset_option (OPTNEWSSEND); +#endif + ret = _mutt_bounce_message (fp, h, to, resent_from, from); rfc822_free_address (&from); diff -udprP mutt-1.5.20.orig/sort.c mutt-1.5.20/sort.c --- mutt-1.5.20.orig/sort.c 2008-11-11 21:55:47.000000000 +0200 +++ mutt-1.5.20/sort.c 2009-06-15 21:05:24.000000000 +0300 @@ -151,6 +151,15 @@ static int compare_order (const void *a, HEADER **ha = (HEADER **) a; HEADER **hb = (HEADER **) b; +#ifdef USE_NNTP + if ((*ha)->article_num && (*hb)->article_num) + { + int result = (*ha)->article_num - (*hb)->article_num; + AUXSORT(result,a,b); + return (SORTCODE (result)); + } + else +#endif /* no need to auxsort because you will never have equality here */ return (SORTCODE ((*ha)->index - (*hb)->index)); } diff -udprP mutt-1.5.20.orig/url.c mutt-1.5.20/url.c --- mutt-1.5.20.orig/url.c 2009-06-01 19:29:32.000000000 +0300 +++ mutt-1.5.20/url.c 2009-06-15 21:05:24.000000000 +0300 @@ -39,6 +39,8 @@ static struct mapping_t UrlMap[] = { "imaps", U_IMAPS }, { "pop", U_POP }, { "pops", U_POPS }, + { "news", U_NNTP }, + { "newss", U_NNTPS }, { "mailto", U_MAILTO }, { "smtp", U_SMTP }, { "smtps", U_SMTPS }, diff -udprP mutt-1.5.20.orig/url.h mutt-1.5.20/url.h --- mutt-1.5.20.orig/url.h 2008-11-11 21:55:47.000000000 +0200 +++ mutt-1.5.20/url.h 2009-06-15 21:05:24.000000000 +0300 @@ -8,6 +8,8 @@ typedef enum url_scheme U_POPS, U_IMAP, U_IMAPS, + U_NNTP, + U_NNTPS, U_SMTP, U_SMTPS, U_MAILTO, diff -udprP mutt-1.5.20.orig/Makefile.am mutt-1.5.20/Makefile.am --- mutt-1.5.20.orig/Makefile.am 2009-01-05 04:11:29.000000000 +0200 +++ mutt-1.5.20/Makefile.am 2009-06-15 21:05:24.000000000 +0300 @@ -53,6 +53,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \ mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \ pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \ + nntp.c newsrc.c \ smime.c smtp.c utf8.c wcwidth.c \ bcache.h browser.h hcache.h mbyte.h mutt_idna.h remailer.h url.h @@ -64,6 +65,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP O mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \ mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \ rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \ + nntp.h ChangeLog.nntp \ _regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \ mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \ README.SSL smime.h \ diff -udprP mutt-1.5.20.orig/Makefile.in mutt-1.5.20/Makefile.in --- mutt-1.5.20.orig/Makefile.in 2009-06-09 09:50:44.000000000 +0300 +++ mutt-1.5.20/Makefile.in 2009-06-15 21:05:24.000000000 +0300 @@ -372,6 +372,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \ mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \ pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \ + nntp.c newsrc.c \ smime.c smtp.c utf8.c wcwidth.c \ bcache.h browser.h hcache.h mbyte.h mutt_idna.h remailer.h url.h @@ -383,6 +384,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP O mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \ mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \ rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \ + nntp.h ChangeLog.nntp \ _regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \ mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \ README.SSL smime.h \ @@ -637,6 +639,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mutt_tunnel.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/muttlib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newsrc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nntp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pager.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/patchlist.Po@am__quote@ diff -udprP mutt-1.5.20.orig/configure mutt-1.5.20/configure --- mutt-1.5.20.orig/configure 2009-06-09 09:50:42.000000000 +0300 +++ mutt-1.5.20/configure 2009-06-15 21:05:24.000000000 +0300 @@ -1478,6 +1478,7 @@ Optional Features: Force use of an external dotlock program --enable-pop Enable POP3 support --enable-imap Enable IMAP support + --enable-nntp Enable NNTP support --enable-smtp include internal SMTP relay support --enable-debug Enable debugging support --enable-flock Use flock() to lock files @@ -14198,6 +14199,20 @@ fi fi done +# Check whether --enable-nntp or --disable-nntp was given. +if test "${enable_nntp+set}" = set; then + enableval="$enable_nntp" + if test x$enableval = xyes ; then + cat >>confdefs.h <<\_ACEOF +#define USE_NNTP 1 +_ACEOF + + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS nntp.o newsrc.o" + need_socket="yes" + fi + +fi; + for ac_func in strftime diff -udprP mutt-1.5.20.orig/doc/Muttrc mutt-1.5.20/doc/Muttrc --- mutt-1.5.20.orig/doc/Muttrc 2009-06-14 21:53:24.000000000 +0300 +++ mutt-1.5.20/doc/Muttrc 2009-06-15 21:05:24.000000000 +0300 @@ -281,6 +281,28 @@ attachments -I message/external-body # of the value as shown above if included. # # +# set ask_follow_up=no +# +# Name: ask_follow_up +# Type: boolean +# Default: no +# +# +# If set, Mutt will prompt you for follow-up groups before editing +# the body of an outgoing message. +# +# +# set ask_x_comment_to=no +# +# Name: ask_x_comment_to +# Type: boolean +# Default: no +# +# +# If set, Mutt will prompt you for x-comment-to field before editing +# the body of an outgoing message. +# +# # set attach_format="%u%D%I %t%4n %T%.40d%> [%.7m/%.10M, %.6e%?C?, %C?, %s] " # # Name: attach_format @@ -466,6 +488,17 @@ attachments -I message/external-body # set certificate_file=~/.mutt/certificates # # +# set catchup_newsgroup=ask-yes +# +# Name: catchup_newsgroup +# Type: quadoption +# Default: ask-yes +# +# +# If this variable is set, Mutt will mark all articles in newsgroup +# as read when you quit the newsgroup (catchup newsgroup). +# +# # set charset="" # # Name: charset @@ -1121,6 +1154,19 @@ attachments -I message/external-body # of the same email for you. # # +# set followup_to_poster=ask-yes +# +# Name: followup_to_poster +# Type: quadoption +# Default: ask-yes +# +# +# If this variable is set and the keyword "poster" is present in +# Followup-To header, follow-up to newsgroup function is not +# permitted. The message will be mailed to the submitter of the +# message via mail. +# +# # set force_name=no # # Name: force_name @@ -1231,6 +1277,28 @@ attachments -I message/external-body # ``Franklin'' to ``Franklin, Steve''. # # +# set group_index_format="%4C %M%N %5s %-45.45f %d" +# +# Name: group_index_format +# Type: string +# Default: "%4C %M%N %5s %-45.45f %d" +# +# +# This variable allows you to customize the newsgroup browser display to +# your personal taste. This string is similar to ``index_format'', but +# has its own set of printf()-like sequences: +# +# %C current newsgroup number +# %d description of newsgroup (becomes from server) +# %f newsgroup name +# %M - if newsgroup not allowed for direct post (moderated for example) +# %N N if newsgroup is new, u if unsubscribed, blank otherwise +# %n number of new articles in newsgroup +# %s number of unread articles in newsgroup +# %>X right justify the rest of the string and pad with character "X" +# %|X pad to the end of the line with character "X" +# +# # set hdrs=yes # # Name: hdrs @@ -1779,6 +1847,7 @@ attachments -I message/external-body # %E number of messages in current thread # %f sender (address + real name), either From: or Return-Path: # %F author name, or recipient name if the message is from you +# %g newsgroup name (if compiled with nntp support) # %H spam attribute(s) of this message # %i message-id of the current message # %l number of lines in the message (does not work with maildir, @@ -1794,12 +1863,14 @@ attachments -I message/external-body # stashed the message: list name or recipient name # if not sent to a list # %P progress indicator for the builtin pager (how much of the file has been displayed) +# %R `x-comment-to:' field (if present and compiled with nntp support) # %s subject of the message # %S status of the message (``N''/``D''/``d''/``!''/``r''/*) # %t ``To:'' field (recipients) # %T the appropriate character from the $to_chars string # %u user (login) name of the author # %v first name of the author, or the recipient if the message is from you +# %W name of organization of author (`organization:' field) # %X number of attachments # (please see the ``attachments'' section for possible speed effects) # %y ``X-Label:'' field, if present @@ -1835,6 +1906,22 @@ attachments -I message/external-body # ``save-hook'', ``fcc-hook'' and ``fcc-save-hook'', too. # # +# set inews="" +# +# Name: inews +# Type: path +# Default: "" +# +# +# If set, specifies the program and arguments used to deliver news posted +# by Mutt. Otherwise, mutt posts article using current connection to +# news server. The following printf-style sequence is understood: +# +# %s newsserver name +# +# Example: set inews="/usr/local/bin/inews -hS" +# +# # set ispell="ispell" # # Name: ispell @@ -2188,6 +2275,18 @@ attachments -I message/external-body # be attached to the newly composed message if this option is set. # # +# set mime_subject=yes +# +# Name: mime_subject +# Type: boolean +# Default: yes +# +# +# If unset, 8-bit ``subject:'' line in article header will not be +# encoded according to RFC2047 to base64. This is useful when message +# is Usenet article, because MIME for news is nonstandard feature. +# +# # set mix_entry_format="%4n %c %-16s %a" # # Name: mix_entry_format @@ -2254,6 +2353,118 @@ attachments -I message/external-body # See also $read_inc, $write_inc and $net_inc. # # +# set news_cache_dir="~/.mutt" +# +# Name: news_cache_dir +# Type: path +# Default: "~/.mutt" +# +# +# This variable pointing to directory where Mutt will save cached news +# articles headers in. If unset, headers will not be saved at all +# and will be reloaded each time when you enter to newsgroup. +# +# +# set news_server="" +# +# Name: news_server +# Type: string +# Default: "" +# +# +# This variable specifies domain name or address of NNTP server. It +# defaults to the newsserver specified in the environment variable +# $NNTPSERVER or contained in the file /etc/nntpserver. You can also +# specify username and an alternative port for each newsserver, ie: +# +# [news[s]://][username[:password]@]newsserver[:port] +# +# +# set newsrc="~/.newsrc" +# +# Name: newsrc +# Type: path +# Default: "~/.newsrc" +# +# +# The file, containing info about subscribed newsgroups - names and +# indexes of read articles. The following printf-style sequence +# is understood: +# +# %s newsserver name +# +# +# set nntp_context=1000 +# +# Name: nntp_context +# Type: number +# Default: 1000 +# +# +# This variable defines number of articles which will be in index when +# newsgroup entered. If active newsgroup have more articles than this +# number, oldest articles will be ignored. Also controls how many +# articles headers will be saved in cache when you quit newsgroup. +# +# +# set nntp_load_description=yes +# +# Name: nntp_load_description +# Type: boolean +# Default: yes +# +# +# This variable controls whether or not descriptions for each newsgroup +# must be loaded when newsgroup is added to list (first time list +# loading or new newsgroup adding). +# +# +# set nntp_user="" +# +# Name: nntp_user +# Type: string +# Default: "" +# +# +# Your login name on the NNTP server. If unset and NNTP server requires +# authentification, Mutt will prompt you for your account name when you +# connect to newsserver. +# +# +# set nntp_pass="" +# +# Name: nntp_pass +# Type: string +# Default: "" +# +# +# Your password for NNTP account. +# +# +# set nntp_poll=60 +# +# Name: nntp_poll +# Type: number +# Default: 60 +# +# +# The time in seconds until any operations on newsgroup except post new +# article will cause recheck for new news. If set to 0, Mutt will +# recheck newsgroup on each operation in index (stepping, read article, +# etc.). +# +# +# set nntp_reconnect=ask-yes +# +# Name: nntp_reconnect +# Type: quadoption +# Default: ask-yes +# +# +# Controls whether or not Mutt will try to reconnect to newsserver when +# connection lost. +# +# # set pager="builtin" # # Name: pager @@ -2969,6 +3180,19 @@ attachments -I message/external-body # string after the inclusion of a message which is being replied to. # # +# set post_moderated=ask-yes +# +# Name: post_moderated +# Type: quadoption +# Default: ask-yes +# +# +# If set to yes, Mutt will post article to newsgroup that have +# not permissions to posting (e.g. moderated). Note: if newsserver +# does not support posting to that newsgroup or totally read-only, that +# posting will not have an effect. +# +# # set postpone=ask-yes # # Name: postpone @@ -3543,6 +3767,41 @@ attachments -I message/external-body # shell from /etc/passwd is used. # # +# set save_unsubscribed=no +# +# Name: save_unsubscribed +# Type: boolean +# Default: no +# +# +# When set, info about unsubscribed newsgroups will be saved into +# ``newsrc'' file and into cache. +# +# +# set show_new_news=yes +# +# Name: show_new_news +# Type: boolean +# Default: yes +# +# +# If set, newsserver will be asked for new newsgroups on entering +# the browser. Otherwise, it will be done only once for a newsserver. +# Also controls whether or not number of new articles of subscribed +# newsgroups will be then checked. +# +# +# set show_only_unread=no +# +# Name: show_only_unread +# Type: boolean +# Default: no +# +# +# If set, only subscribed newsgroups that contain unread articles +# will be displayed in browser. +# +# # set sig_dashes=yes # # Name: sig_dashes @@ -4748,3 +5007,14 @@ attachments -I message/external-body # ``tuning'' section of the manual for performance considerations. # # +# set x_comment_to=no +# +# Name: x_comment_to +# Type: boolean +# Default: no +# +# +# If set, Mutt will add ``X-Comment-To:'' field (that contains full +# name of original article author) to article that followuped to newsgroup. +# +# --- a/PATCHES +++ b/PATCHES @@ -0,0 +1 @@ +vvv.nntp debian/patches/mutt-patched/sidebar =================================== When enabled, mutt will show a list of mailboxes with (new) message counts in a separate column on the left side of the screen. As this feature is still considered to be unstable, this patch is only applied in the "mutt-patched" package. * Configuration variables: sidebar_delim (string, default "|") This specifies the delimiter between the sidebar (if visible) and other screens. sidebar_visible (boolean, default no) This specifies whether or not to show sidebar (left-side list of folders). sidebar_width (integer, default 0) - The width of the sidebar. * Patch source: - http://www.lunar-linux.org/index.php?page=mutt-sidebar - http://lunar-linux.org/~tchan/mutt/patch-1.5.19.sidebar.20090522.txt * Changes made: - 2008-08-02 myon: Refreshed patch using quilt push -f to remove hunks we do not need (Makefile.in). --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,7 @@ rfc822.c rfc1524.c rfc2047.c rfc2231.c rfc3676.c \ score.c send.c sendlib.c signal.c sort.c \ status.c system.c thread.c charset.c history.c lib.c \ + sidebar.c \ muttlib.c editmsg.c mbyte.c \ url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c --- a/OPS +++ b/OPS @@ -180,3 +180,8 @@ OP_MAIN_SHOW_LIMIT "show currently active limit pattern" OP_MAIN_COLLAPSE_THREAD "collapse/uncollapse current thread" OP_MAIN_COLLAPSE_ALL "collapse/uncollapse all threads" +OP_SIDEBAR_SCROLL_UP "scroll the mailbox pane up 1 page" +OP_SIDEBAR_SCROLL_DOWN "scroll the mailbox pane down 1 page" +OP_SIDEBAR_NEXT "go down to next mailbox" +OP_SIDEBAR_PREV "go to previous mailbox" +OP_SIDEBAR_OPEN "open hilighted mailbox" --- a/buffy.c +++ b/buffy.c @@ -340,6 +340,68 @@ return rc; } +/* update message counts for the sidebar */ +void buffy_maildir_update (BUFFY* mailbox) +{ + char path[_POSIX_PATH_MAX]; + DIR *dirp; + struct dirent *de; + char *p; + + mailbox->msgcount = 0; + mailbox->msg_unread = 0; + mailbox->msg_flagged = 0; + + snprintf (path, sizeof (path), "%s/new", mailbox->path); + + if ((dirp = opendir (path)) == NULL) + { + mailbox->magic = 0; + return; + } + + while ((de = readdir (dirp)) != NULL) + { + if (*de->d_name == '.') + continue; + + if (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T')) { + mailbox->new = 1; + mailbox->msgcount++; + mailbox->msg_unread++; + } + } + + closedir (dirp); + snprintf (path, sizeof (path), "%s/cur", mailbox->path); + + if ((dirp = opendir (path)) == NULL) + { + mailbox->magic = 0; + return; + } + + while ((de = readdir (dirp)) != NULL) + { + if (*de->d_name == '.') + continue; + + if (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T')) { + mailbox->msgcount++; + if ((p = strstr (de->d_name, ":2,"))) { + if (!strchr (p + 3, 'T')) { + if (!strchr (p + 3, 'S')) + mailbox->msg_unread++; + if (strchr(p + 3, 'F')) + mailbox->msg_flagged++; + } + } + } + } + + closedir (dirp); +} + /* returns 1 if mailbox has new mail */ static int buffy_mbox_hasnew (BUFFY* mailbox, struct stat *sb) { @@ -371,6 +433,20 @@ return rc; } +/* update message counts for the sidebar */ +void buffy_mbox_update (BUFFY* mailbox) +{ + CONTEXT *ctx = NULL; + + ctx = mx_open_mailbox(mailbox->path, M_READONLY | M_QUIET | M_NOSORT | M_PEEK, NULL); + if(ctx) + { + mailbox->msgcount = ctx->msgcount; + mailbox->msg_unread = ctx->unread; + mx_close_mailbox(ctx, 0); + } +} + int mutt_buffy_check (int force) { BUFFY *tmp; @@ -444,16 +520,19 @@ { case M_MBOX: case M_MMDF: + buffy_mbox_update (tmp); if (buffy_mbox_hasnew (tmp, &sb) > 0) BuffyCount++; break; case M_MAILDIR: + buffy_maildir_update (tmp); if (buffy_maildir_hasnew (tmp) > 0) BuffyCount++; break; case M_MH: + mh_buffy_update (tmp->path, &tmp->msgcount, &tmp->msg_unread, &tmp->msg_flagged); if ((tmp->new = mh_buffy (tmp->path)) > 0) BuffyCount++; break; --- a/buffy.h +++ b/buffy.h @@ -25,7 +25,11 @@ char path[_POSIX_PATH_MAX]; off_t size; struct buffy_t *next; + struct buffy_t *prev; short new; /* mailbox has new mail */ + int msgcount; /* total number of messages */ + int msg_unread; /* number of unread messages */ + int msg_flagged; /* number of flagged messages */ short notified; /* user has been notified */ short magic; /* mailbox type */ short newly_created; /* mbox or mmdf just popped into existence */ --- a/color.c +++ b/color.c @@ -93,6 +93,8 @@ { "bold", MT_COLOR_BOLD }, { "underline", MT_COLOR_UNDERLINE }, { "index", MT_COLOR_INDEX }, + { "sidebar_new", MT_COLOR_NEW }, + { "sidebar_flagged", MT_COLOR_FLAGGED }, { NULL, 0 } }; --- a/compose.c +++ b/compose.c @@ -72,7 +72,7 @@ #define HDR_XOFFSET 10 #define TITLE_FMT "%10s" /* Used for Prompts, which are ASCII */ -#define W (COLS - HDR_XOFFSET) +#define W (COLS - HDR_XOFFSET - SidebarWidth) static char *Prompts[] = { @@ -112,7 +112,7 @@ { int off = 0; - mvaddstr (HDR_CRYPT, 0, "Security: "); + mvaddstr (HDR_CRYPT, SidebarWidth, "Security: "); if ((WithCrypto & (APPLICATION_PGP | APPLICATION_SMIME)) == 0) { @@ -144,7 +144,7 @@ } clrtoeol (); - move (HDR_CRYPTINFO, 0); + move (HDR_CRYPTINFO, SidebarWidth); clrtoeol (); if ((WithCrypto & APPLICATION_PGP) @@ -161,7 +161,7 @@ && (msg->security & ENCRYPT) && SmimeCryptAlg && *SmimeCryptAlg) { - mvprintw (HDR_CRYPTINFO, 40, "%s%s", _("Encrypt with: "), + mvprintw (HDR_CRYPTINFO, SidebarWidth + 40, "%s%s", _("Encrypt with: "), NONULL(SmimeCryptAlg)); off = 20; } @@ -175,7 +175,7 @@ int c; char *t; - mvaddstr (HDR_MIX, 0, " Mix: "); + mvaddstr (HDR_MIX, SidebarWidth, " Mix: "); if (!chain) { @@ -190,7 +190,7 @@ if (t && t[0] == '0' && t[1] == '\0') t = "<random>"; - if (c + mutt_strlen (t) + 2 >= COLS) + if (c + mutt_strlen (t) + 2 >= COLS - SidebarWidth) break; addstr (NONULL(t)); @@ -242,7 +242,7 @@ buf[0] = 0; rfc822_write_address (buf, sizeof (buf), addr, 1); - mvprintw (line, 0, TITLE_FMT, Prompts[line - 1]); + mvprintw (line, SidebarWidth, TITLE_FMT, Prompts[line - 1]); mutt_paddstr (W, buf); } @@ -252,10 +252,10 @@ draw_envelope_addr (HDR_TO, msg->env->to); draw_envelope_addr (HDR_CC, msg->env->cc); draw_envelope_addr (HDR_BCC, msg->env->bcc); - mvprintw (HDR_SUBJECT, 0, TITLE_FMT, Prompts[HDR_SUBJECT - 1]); + mvprintw (HDR_SUBJECT, SidebarWidth, TITLE_FMT, Prompts[HDR_SUBJECT - 1]); mutt_paddstr (W, NONULL (msg->env->subject)); draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to); - mvprintw (HDR_FCC, 0, TITLE_FMT, Prompts[HDR_FCC - 1]); + mvprintw (HDR_FCC, SidebarWidth, TITLE_FMT, Prompts[HDR_FCC - 1]); mutt_paddstr (W, fcc); if (WithCrypto) @@ -266,7 +266,7 @@ #endif SETCOLOR (MT_COLOR_STATUS); - mvaddstr (HDR_ATTACH - 1, 0, _("-- Attachments")); + mvaddstr (HDR_ATTACH - 1, SidebarWidth, _("-- Attachments")); BKGDSET (MT_COLOR_STATUS); clrtoeol (); @@ -304,7 +304,7 @@ /* redraw the expanded list so the user can see the result */ buf[0] = 0; rfc822_write_address (buf, sizeof (buf), *addr, 1); - move (line, HDR_XOFFSET); + move (line, HDR_XOFFSET+SidebarWidth); mutt_paddstr (W, buf); return 0; @@ -549,7 +549,7 @@ if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0) { mutt_str_replace (&msg->env->subject, buf); - move (HDR_SUBJECT, HDR_XOFFSET); + move (HDR_SUBJECT, HDR_XOFFSET + SidebarWidth); clrtoeol (); if (msg->env->subject) mutt_paddstr (W, msg->env->subject); @@ -566,7 +566,7 @@ { strfcpy (fcc, buf, fcclen); mutt_pretty_mailbox (fcc, fcclen); - move (HDR_FCC, HDR_XOFFSET); + move (HDR_FCC, HDR_XOFFSET + SidebarWidth); mutt_paddstr (W, fcc); fccSet = 1; } --- a/curs_main.c +++ b/curs_main.c @@ -26,7 +26,9 @@ #include "mailbox.h" #include "mapping.h" #include "sort.h" +#include "buffy.h" #include "mx.h" +#include "sidebar.h" #ifdef USE_POP #include "pop.h" @@ -532,8 +534,12 @@ menu->redraw |= REDRAW_STATUS; if (do_buffy_notify) { - if (mutt_buffy_notify () && option (OPTBEEPNEW)) - beep (); + if (mutt_buffy_notify ()) + { + menu->redraw |= REDRAW_FULL; + if (option (OPTBEEPNEW)) + beep (); + } } else do_buffy_notify = 1; @@ -545,6 +551,7 @@ if (menu->redraw & REDRAW_FULL) { menu_redraw_full (menu); + draw_sidebar(menu->menu); mutt_show_error (); } @@ -567,10 +574,13 @@ if (menu->redraw & REDRAW_STATUS) { + DrawFullLine = 1; menu_status_line (buf, sizeof (buf), menu, NONULL (Status)); + DrawFullLine = 0; CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES-2); SETCOLOR (MT_COLOR_STATUS); BKGDSET (MT_COLOR_STATUS); + set_buffystats(Context); mutt_paddstr (COLS, buf); SETCOLOR (MT_COLOR_NORMAL); BKGDSET (MT_COLOR_NORMAL); @@ -591,7 +601,7 @@ menu->oldcurrent = -1; if (option (OPTARROWCURSOR)) - move (menu->current - menu->top + menu->offset, 2); + move (menu->current - menu->top + menu->offset, SidebarWidth + 2); else if (option (OPTBRAILLEFRIENDLY)) move (menu->current - menu->top + menu->offset, 0); else @@ -1089,6 +1099,7 @@ menu->redraw = REDRAW_FULL; break; + case OP_SIDEBAR_OPEN: case OP_MAIN_CHANGE_FOLDER: case OP_MAIN_NEXT_UNREAD_MAILBOX: @@ -1120,7 +1131,11 @@ { mutt_buffy (buf, sizeof (buf)); - if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) + if ( op == OP_SIDEBAR_OPEN ) { + if(!CurBuffy) + break; + strncpy( buf, CurBuffy->path, sizeof(buf) ); + } else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) { if (menu->menu == MENU_PAGER) { @@ -1138,6 +1153,7 @@ } mutt_expand_path (buf, sizeof (buf)); + set_curbuffy(buf); if (mx_get_magic (buf) <= 0) { mutt_error (_("%s is not a mailbox."), buf); @@ -2241,6 +2257,12 @@ mutt_what_key(); break; + case OP_SIDEBAR_SCROLL_UP: + case OP_SIDEBAR_SCROLL_DOWN: + case OP_SIDEBAR_NEXT: + case OP_SIDEBAR_PREV: + scroll_sidebar(op, menu->menu); + break; default: if (menu->menu == MENU_MAIN) km_error_key (MENU_MAIN); --- a/flags.c +++ b/flags.c @@ -22,8 +22,10 @@ #include "mutt.h" #include "mutt_curses.h" +#include "mutt_menu.h" #include "sort.h" #include "mx.h" +#include "sidebar.h" void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx) { @@ -290,6 +292,7 @@ */ if (h->searched && (changed != h->changed || deleted != ctx->deleted || tagged != ctx->tagged || flagged != ctx->flagged)) h->searched = 0; + draw_sidebar(0); } void mutt_tag_set_flag (int flag, int bf) --- a/functions.h +++ b/functions.h @@ -170,6 +170,11 @@ { "decrypt-save", OP_DECRYPT_SAVE, NULL }, + { "sidebar-scroll-up", OP_SIDEBAR_SCROLL_UP, NULL }, + { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL }, + { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, + { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, + { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, { NULL, 0, NULL } }; @@ -274,6 +279,11 @@ { "what-key", OP_WHAT_KEY, NULL }, + { "sidebar-scroll-up", OP_SIDEBAR_SCROLL_UP, NULL }, + { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL }, + { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, + { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, + { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, { NULL, 0, NULL } }; --- a/globals.h +++ b/globals.h @@ -117,6 +117,7 @@ WHERE char *SendCharset; WHERE char *Sendmail; WHERE char *Shell; +WHERE char *SidebarDelim; WHERE char *Signature; WHERE char *SimpleSearch; #if USE_SMTP @@ -210,6 +211,9 @@ WHERE short ScoreThresholdRead; WHERE short ScoreThresholdFlag; +WHERE struct buffy_t *CurBuffy INITVAL(0); +WHERE short DrawFullLine INITVAL(0); +WHERE short SidebarWidth; #ifdef USE_IMAP WHERE short ImapKeepalive; WHERE short ImapPipelineDepth; --- a/imap/command.c +++ b/imap/command.c @@ -1011,6 +1011,13 @@ opened */ status->uidnext = oldun; + /* Added to make the sidebar show the correct numbers */ + if (status->messages) + { + inc->msgcount = status->messages; + inc->msg_unread = status->unseen; + } + FREE (&value); return; } --- a/imap/imap.c +++ b/imap/imap.c @@ -1527,7 +1527,7 @@ imap_munge_mbox_name (munged, sizeof (munged), name); snprintf (command, sizeof (command), - "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged); + "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT MESSAGES)", munged); if (imap_exec (idata, command, IMAP_CMD_QUEUE) < 0) { --- a/init.h +++ b/init.h @@ -1965,6 +1965,22 @@ ** not used. ** (PGP only) */ + {"sidebar_delim", DT_STR, R_BOTH, UL &SidebarDelim, "|"}, + /* + ** .pp + ** This specifies the delimiter between the sidebar (if visible) and + ** other screens. + */ + { "sidebar_visible", DT_BOOL, R_BOTH, OPTSIDEBAR, 0 }, + /* + ** .pp + ** This specifies whether or not to show sidebar (left-side list of folders). + */ + { "sidebar_width", DT_NUM, R_BOTH, UL &SidebarWidth, 0 }, + /* + ** .pp + ** The width of the sidebar. + */ { "pgp_use_gpg_agent", DT_BOOL, R_NONE, OPTUSEGPGAGENT, 0}, /* ** .pp --- a/mailbox.h +++ b/mailbox.h @@ -27,6 +27,7 @@ #define M_NEWFOLDER (1<<4) /* create a new folder - same as M_APPEND, but uses * safe_fopen() for mbox-style folders. */ +#define M_PEEK (1<<5) /* revert atime back after taking a look (if applicable) */ /* mx_open_new_message() */ #define M_ADD_FROM 1 /* add a From_ line */ --- a/mbox.c +++ b/mbox.c @@ -104,6 +104,7 @@ mutt_perror (ctx->path); return (-1); } + ctx->atime = sb.st_atime; ctx->mtime = sb.st_mtime; ctx->size = sb.st_size; @@ -255,6 +256,7 @@ ctx->size = sb.st_size; ctx->mtime = sb.st_mtime; + ctx->atime = sb.st_atime; #ifdef NFS_ATTRIBUTE_HACK if (sb.st_mtime > sb.st_atime) --- a/menu.c +++ b/menu.c @@ -24,6 +24,7 @@ #include "mutt_curses.h" #include "mutt_menu.h" #include "mbyte.h" +#include "sidebar.h" #include <string.h> #include <stdlib.h> @@ -156,7 +157,7 @@ { char *scratch = safe_strdup (s); int shift = option (OPTARROWCURSOR) ? 3 : 0; - int cols = COLS - shift; + int cols = COLS - shift - SidebarWidth; mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 1); s[n - 1] = 0; @@ -207,6 +208,7 @@ char buf[LONG_STRING]; int i; + draw_sidebar(1); for (i = menu->top; i < menu->top + menu->pagelen; i++) { if (i < menu->max) @@ -217,7 +219,7 @@ if (option (OPTARROWCURSOR)) { attrset (menu->color (i)); - CLEARLINE (i - menu->top + menu->offset); + CLEARLINE_WIN (i - menu->top + menu->offset); if (i == menu->current) { @@ -246,14 +248,14 @@ BKGDSET (MT_COLOR_INDICATOR); } - CLEARLINE (i - menu->top + menu->offset); + CLEARLINE_WIN (i - menu->top + menu->offset); print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current); SETCOLOR (MT_COLOR_NORMAL); BKGDSET (MT_COLOR_NORMAL); } } else - CLEARLINE (i - menu->top + menu->offset); + CLEARLINE_WIN (i - menu->top + menu->offset); } menu->redraw = 0; } @@ -268,7 +270,7 @@ return; } - move (menu->oldcurrent + menu->offset - menu->top, 0); + move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth); SETCOLOR (MT_COLOR_NORMAL); BKGDSET (MT_COLOR_NORMAL); @@ -283,13 +285,13 @@ clrtoeol (); menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent); menu_pad_string (buf, sizeof (buf)); - move (menu->oldcurrent + menu->offset - menu->top, 3); + move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3); print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1); SETCOLOR (MT_COLOR_NORMAL); } /* now draw it in the new location */ - move (menu->current + menu->offset - menu->top, 0); + move (menu->current + menu->offset - menu->top, SidebarWidth); attrset (menu->color (menu->current)); ADDCOLOR (MT_COLOR_INDICATOR); addstr ("->"); @@ -310,7 +312,7 @@ attrset (menu->color (menu->current)); ADDCOLOR (MT_COLOR_INDICATOR); BKGDSET (MT_COLOR_INDICATOR); - CLEARLINE (menu->current - menu->top + menu->offset); + CLEARLINE_WIN (menu->current - menu->top + menu->offset); print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0); SETCOLOR (MT_COLOR_NORMAL); BKGDSET (MT_COLOR_NORMAL); @@ -322,7 +324,7 @@ { char buf[LONG_STRING]; - move (menu->current + menu->offset - menu->top, 0); + move (menu->current + menu->offset - menu->top, SidebarWidth); menu_make_entry (buf, sizeof (buf), menu, menu->current); menu_pad_string (buf, sizeof (buf)); @@ -884,7 +886,7 @@ if (option (OPTARROWCURSOR)) - move (menu->current - menu->top + menu->offset, 2); + move (menu->current - menu->top + menu->offset, SidebarWidth + 2); else if (option (OPTBRAILLEFRIENDLY)) move (menu->current - menu->top + menu->offset, 0); else --- a/mh.c +++ b/mh.c @@ -235,13 +235,37 @@ if (mh_read_sequences (&mhs, path) < 0) return 0; + for (i = 0; !r && i <= mhs.max; i++) - if (mhs_check (&mhs, i) & MH_SEQ_UNSEEN) + if (mhs_check (&mhs, i) & MH_SEQ_UNSEEN) { r = 1; + } mhs_free_sequences (&mhs); return r; } +void mh_buffy_update (const char *path, int *msgcount, int *msg_unread, int *msg_flagged) +{ + int i; + struct mh_sequences mhs; + memset (&mhs, 0, sizeof (mhs)); + + if (mh_read_sequences (&mhs, path) < 0) + return; + + msgcount = 0; + msg_unread = 0; + msg_flagged = 0; + for (i = 0; i <= mhs.max; i++) + msgcount++; + if (mhs_check (&mhs, i) & MH_SEQ_UNSEEN) { + msg_unread++; + } + if (mhs_check (&mhs, i) & MH_SEQ_FLAGGED) + msg_flagged++; + mhs_free_sequences (&mhs); +} + static int mh_mkstemp (CONTEXT * dest, FILE ** fp, char **tgt) { int fd; --- a/mutt.h +++ b/mutt.h @@ -431,6 +431,7 @@ OPTSAVEEMPTY, OPTSAVENAME, OPTSCORE, + OPTSIDEBAR, OPTSIGDASHES, OPTSIGONTOP, OPTSORTRE, @@ -874,6 +875,7 @@ { char *path; FILE *fp; + time_t atime; time_t mtime; off_t size; off_t vsize; @@ -914,6 +916,7 @@ unsigned int quiet : 1; /* inhibit status messages? */ unsigned int collapsed : 1; /* are all threads collapsed? */ unsigned int closing : 1; /* mailbox is being closed */ + unsigned int peekonly : 1; /* just taking a glance, revert atime */ /* driver hooks */ void *data; /* driver specific data */ --- a/mutt_curses.h +++ b/mutt_curses.h @@ -64,6 +64,7 @@ #undef lines #endif /* lines */ +#define CLEARLINE_WIN(x) move(x,SidebarWidth), clrtoeol() #define CLEARLINE(x) move(x,0), clrtoeol() #define CENTERLINE(x,y) move(y, (COLS-strlen(x))/2), addstr(x) #define BEEP() do { if (option (OPTBEEP)) beep(); } while (0) @@ -126,6 +127,8 @@ MT_COLOR_BOLD, MT_COLOR_UNDERLINE, MT_COLOR_INDEX, + MT_COLOR_NEW, + MT_COLOR_FLAGGED, MT_COLOR_MAX }; --- a/muttlib.c +++ b/muttlib.c @@ -1286,6 +1286,8 @@ pl = pw = 1; /* see if there's room to add content, else ignore */ + if ( DrawFullLine ) + { if ((col < COLS && wlen < destlen) || soft) { int pad; @@ -1329,6 +1331,52 @@ col += wid; src += pl; } + } + else + { + if ((col < COLS-SidebarWidth && wlen < destlen) || soft) + { + int pad; + + /* get contents after padding */ + mutt_FormatString (buf, sizeof (buf), 0, src + pl, callback, data, flags); + len = mutt_strlen (buf); + wid = mutt_strwidth (buf); + + /* try to consume as many columns as we can, if we don't have + * memory for that, use as much memory as possible */ + pad = (COLS - SidebarWidth - col - wid) / pw; + if (pad > 0 && wlen + (pad * pl) + len > destlen) + pad = ((signed)(destlen - wlen - len)) / pl; + if (pad > 0) + { + while (pad--) + { + memcpy (wptr, src, pl); + wptr += pl; + wlen += pl; + col += pw; + } + } + else if (soft && pad < 0) + { + /* \0-terminate dest for length computation in mutt_wstr_trunc() */ + *wptr = 0; + /* make sure right part is at most as wide as display */ + len = mutt_wstr_trunc (buf, destlen, COLS, &wid); + /* truncate left so that right part fits completely in */ + wlen = mutt_wstr_trunc (dest, destlen - len, col + pad, &col); + wptr = dest + wlen; + } + if (len + wlen > destlen) + len = mutt_wstr_trunc (buf, destlen - wlen, COLS - SidebarWidth - col, NULL); + memcpy (wptr, buf, len); + wptr += len; + wlen += len; + col += wid; + src += pl; + } + } break; /* skip rest of input */ } else if (ch == '|') --- a/mx.c +++ b/mx.c @@ -595,6 +595,7 @@ * M_APPEND open mailbox for appending * M_READONLY open mailbox in read-only mode * M_QUIET only print error messages + * M_PEEK revert atime where applicable * ctx if non-null, context struct to use */ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx) @@ -617,6 +618,8 @@ ctx->quiet = 1; if (flags & M_READONLY) ctx->readonly = 1; + if (flags & M_PEEK) + ctx->peekonly = 1; if (flags & (M_APPEND|M_NEWFOLDER)) { @@ -721,9 +724,21 @@ void mx_fastclose_mailbox (CONTEXT *ctx) { int i; +#ifndef BUFFY_SIZE + struct utimbuf ut; +#endif if(!ctx) return; +#ifndef BUFFY_SIZE + /* fix up the times so buffy won't get confused */ + if (ctx->peekonly && ctx->path && ctx->mtime > ctx->atime) + { + ut.actime = ctx->atime; + ut.modtime = ctx->mtime; + utime (ctx->path, &ut); + } +#endif /* never announce that a mailbox we've just left has new mail. #3290 * XXX: really belongs in mx_close_mailbox, but this is a nice hook point */ --- a/mx.h +++ b/mx.h @@ -61,6 +61,7 @@ int mh_sync_mailbox (CONTEXT *, int *); int mh_check_mailbox (CONTEXT *, int *); int mh_buffy (const char *); +void mh_buffy_update (const char *, int *, int *, int *); int mh_check_empty (const char *); int maildir_read_dir (CONTEXT *); --- a/pager.c +++ b/pager.c @@ -29,6 +29,7 @@ #include "pager.h" #include "attach.h" #include "mbyte.h" +#include "sidebar.h" #include "mutt_crypt.h" @@ -1095,6 +1096,7 @@ wchar_t wc; mbstate_t mbstate; int wrap_cols = mutt_term_width ((flags & M_PAGER_NOWRAP) ? 0 : Wrap); + wrap_cols -= SidebarWidth; if (check_attachment_marker ((char *)buf) == 0) wrap_cols = COLS; @@ -1745,7 +1747,7 @@ if ((redraw & REDRAW_BODY) || topline != oldtopline) { do { - move (bodyoffset, 0); + move (bodyoffset, SidebarWidth); curline = oldtopline = topline; lines = 0; force_redraw = 0; @@ -1758,6 +1760,7 @@ &QuoteList, &q_level, &force_redraw, &SearchRE) > 0) lines++; curline++; + move(lines + bodyoffset, SidebarWidth); } last_offset = lineInfo[curline].offset; } while (force_redraw); @@ -1771,6 +1774,7 @@ addch ('~'); addch ('\n'); lines++; + move(lines + bodyoffset, SidebarWidth); } /* We are going to update the pager status bar, so it isn't * necessary to reset to normal color now. */ @@ -1794,21 +1798,21 @@ /* print out the pager status bar */ SETCOLOR (MT_COLOR_STATUS); BKGDSET (MT_COLOR_STATUS); - CLEARLINE (statusoffset); + CLEARLINE_WIN (statusoffset); if (IsHeader (extra) || IsMsgAttach (extra)) { - size_t l1 = COLS * MB_LEN_MAX; + size_t l1 = (COLS-SidebarWidth) * MB_LEN_MAX; size_t l2 = sizeof (buffer); hfi.hdr = (IsHeader (extra)) ? extra->hdr : extra->bdy->hdr; mutt_make_string_info (buffer, l1 < l2 ? l1 : l2, NONULL (PagerFmt), &hfi, M_FORMAT_MAKEPRINT); - mutt_paddstr (COLS, buffer); + mutt_paddstr (COLS-SidebarWidth, buffer); } else { char bn[STRING]; snprintf (bn, sizeof (bn), "%s (%s)", banner, pager_progress_str); - mutt_paddstr (COLS, bn); + mutt_paddstr (COLS-SidebarWidth, bn); } BKGDSET (MT_COLOR_NORMAL); SETCOLOR (MT_COLOR_NORMAL); @@ -1826,18 +1830,23 @@ /* redraw the pager_index indicator, because the * flags for this message might have changed. */ menu_redraw_current (index); + draw_sidebar(MENU_PAGER); /* print out the index status bar */ menu_status_line (buffer, sizeof (buffer), index, NONULL(Status)); - move (indexoffset + (option (OPTSTATUSONTOP) ? 0 : (indexlen - 1)), 0); + move (indexoffset + (option (OPTSTATUSONTOP) ? 0 : (indexlen - 1)), SidebarWidth); SETCOLOR (MT_COLOR_STATUS); BKGDSET (MT_COLOR_STATUS); - mutt_paddstr (COLS, buffer); + mutt_paddstr (COLS-SidebarWidth, buffer); SETCOLOR (MT_COLOR_NORMAL); BKGDSET (MT_COLOR_NORMAL); } + /* if we're not using the index, update every time */ + if ( index == 0 ) + draw_sidebar(MENU_PAGER); + redraw = 0; if (option(OPTBRAILLEFRIENDLY)) { @@ -2769,6 +2778,13 @@ mutt_what_key (); break; + case OP_SIDEBAR_SCROLL_UP: + case OP_SIDEBAR_SCROLL_DOWN: + case OP_SIDEBAR_NEXT: + case OP_SIDEBAR_PREV: + scroll_sidebar(ch, MENU_PAGER); + break; + default: ch = -1; break; --- /dev/null +++ b/sidebar.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) ????-2004 Justin Hibbits <jrh29@po.cwru.edu> + * Copyright (C) 2004 Thomer M. Gil <mutt@thomer.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mutt.h" +#include "mutt_menu.h" +#include "mutt_curses.h" +#include "sidebar.h" +#include "buffy.h" +#include <libgen.h> +#include "keymap.h" +#include <stdbool.h> + +/*BUFFY *CurBuffy = 0;*/ +static BUFFY *TopBuffy = 0; +static BUFFY *BottomBuffy = 0; +static int known_lines = 0; + +static int quick_log10(int n) +{ + char string[32]; + sprintf(string, "%d", n); + return strlen(string); +} + +void calc_boundaries (int menu) +{ + BUFFY *tmp = Incoming; + + if ( known_lines != LINES ) { + TopBuffy = BottomBuffy = 0; + known_lines = LINES; + } + for ( ; tmp->next != 0; tmp = tmp->next ) + tmp->next->prev = tmp; + + if ( TopBuffy == 0 && BottomBuffy == 0 ) + TopBuffy = Incoming; + if ( BottomBuffy == 0 ) { + int count = LINES - 2 - (menu != MENU_PAGER || option(OPTSTATUSONTOP)); + BottomBuffy = TopBuffy; + while ( --count && BottomBuffy->next ) + BottomBuffy = BottomBuffy->next; + } + else if ( TopBuffy == CurBuffy->next ) { + int count = LINES - 2 - (menu != MENU_PAGER); + BottomBuffy = CurBuffy; + tmp = BottomBuffy; + while ( --count && tmp->prev) + tmp = tmp->prev; + TopBuffy = tmp; + } + else if ( BottomBuffy == CurBuffy->prev ) { + int count = LINES - 2 - (menu != MENU_PAGER); + TopBuffy = CurBuffy; + tmp = TopBuffy; + while ( --count && tmp->next ) + tmp = tmp->next; + BottomBuffy = tmp; + } +} + +char *make_sidebar_entry(char *box, int size, int new, int flagged) +{ + static char *entry = 0; + char *c; + int i = 0; + int delim_len = strlen(SidebarDelim); + + c = realloc(entry, SidebarWidth - delim_len + 2); + if ( c ) entry = c; + entry[SidebarWidth - delim_len + 1] = 0; + for (; i < SidebarWidth - delim_len + 1; entry[i++] = ' ' ); + i = strlen(box); + strncpy( entry, box, i < (SidebarWidth - delim_len + 1) ? i : (SidebarWidth - delim_len + 1) ); + + if (size == -1) + sprintf(entry + SidebarWidth - delim_len - 3, "?"); + else if ( new ) { + if (flagged > 0) { + sprintf( + entry + SidebarWidth - delim_len - 5 - quick_log10(size) - quick_log10(new) - quick_log10(flagged), + "% d(%d)[%d]", size, new, flagged); + } else { + sprintf( + entry + SidebarWidth - delim_len - 3 - quick_log10(size) - quick_log10(new), + "% d(%d)", size, new); + } + } else if (flagged > 0) { + sprintf( entry + SidebarWidth - delim_len - 3 - quick_log10(size) - quick_log10(flagged), "% d[%d]", size, flagged); + } else { + sprintf( entry + SidebarWidth - delim_len - 1 - quick_log10(size), "% d", size); + } + return entry; +} + +void set_curbuffy(char buf[LONG_STRING]) +{ + BUFFY* tmp = CurBuffy = Incoming; + + if (!Incoming) + return; + + while(1) { + if(!strcmp(tmp->path, buf)) { + CurBuffy = tmp; + break; + } + + if(tmp->next) + tmp = tmp->next; + else + break; + } +} + +int draw_sidebar(int menu) { + + int lines = option(OPTHELP) ? 1 : 0; + lines += option(OPTSTATUSONTOP) ? 1 : 0; + + BUFFY *tmp; +#ifndef USE_SLANG_CURSES + attr_t attrs; +#endif + short delim_len = strlen(SidebarDelim); + short color_pair; + + static bool initialized = false; + static int prev_show_value; + static short saveSidebarWidth; + + /* initialize first time */ + if(!initialized) { + prev_show_value = option(OPTSIDEBAR); + saveSidebarWidth = SidebarWidth; + if(!option(OPTSIDEBAR)) SidebarWidth = 0; + initialized = true; + } + + /* save or restore the value SidebarWidth */ + if(prev_show_value != option(OPTSIDEBAR)) { + if(prev_show_value && !option(OPTSIDEBAR)) { + saveSidebarWidth = SidebarWidth; + SidebarWidth = 0; + } else if(!prev_show_value && option(OPTSIDEBAR)) { + SidebarWidth = saveSidebarWidth; + } + prev_show_value = option(OPTSIDEBAR); + } + + +// if ( SidebarWidth == 0 ) return 0; + if (SidebarWidth > 0 && option (OPTSIDEBAR) + && delim_len >= SidebarWidth) { + unset_option (OPTSIDEBAR); + /* saveSidebarWidth = SidebarWidth; */ + if (saveSidebarWidth > delim_len) { + SidebarWidth = saveSidebarWidth; + mutt_error (_("Value for sidebar_delim is too long. Disabling sidebar.")); + sleep (2); + } else { + SidebarWidth = 0; + mutt_error (_("Value for sidebar_delim is too long. Disabling sidebar. Please set your sidebar_width to a sane value.")); + sleep (4); /* the advise to set a sane value should be seen long enough */ + } + saveSidebarWidth = 0; + return (0); + } + + if ( SidebarWidth == 0 || !option(OPTSIDEBAR)) { + if (SidebarWidth > 0) { + saveSidebarWidth = SidebarWidth; + SidebarWidth = 0; + } + unset_option(OPTSIDEBAR); + return 0; + } + + /* get attributes for divider */ + SETCOLOR(MT_COLOR_STATUS); +#ifndef USE_SLANG_CURSES + attr_get(&attrs, &color_pair, 0); +#else + color_pair = attr_get(); +#endif + SETCOLOR(MT_COLOR_NORMAL); + + /* draw the divider */ + + for ( ; lines < LINES-1-(menu != MENU_PAGER || option(OPTSTATUSONTOP)); lines++ ) { + move(lines, SidebarWidth - delim_len); + addstr(NONULL(SidebarDelim)); +#ifndef USE_SLANG_CURSES + mvchgat(lines, SidebarWidth - delim_len, delim_len, 0, color_pair, NULL); +#endif + } + + if ( Incoming == 0 ) return 0; + lines = option(OPTHELP) ? 1 : 0; /* go back to the top */ + lines += option(OPTSTATUSONTOP) ? 1 : 0; + + if ( known_lines != LINES || TopBuffy == 0 || BottomBuffy == 0 ) + calc_boundaries(menu); + if ( CurBuffy == 0 ) CurBuffy = Incoming; + + tmp = TopBuffy; + + SETCOLOR(MT_COLOR_NORMAL); + + for ( ; tmp && lines < LINES-1 - (menu != MENU_PAGER || option(OPTSTATUSONTOP)); tmp = tmp->next ) { + if ( tmp == CurBuffy ) + SETCOLOR(MT_COLOR_INDICATOR); + else if ( tmp->msg_unread > 0 ) + SETCOLOR(MT_COLOR_NEW); + else if ( tmp->msg_flagged > 0 ) + SETCOLOR(MT_COLOR_FLAGGED); + else + SETCOLOR(MT_COLOR_NORMAL); + + move( lines, 0 ); + if ( Context && !strcmp( tmp->path, Context->path ) ) { + tmp->msg_unread = Context->unread; + tmp->msgcount = Context->msgcount; + tmp->msg_flagged = Context->flagged; + } + // check whether Maildir is a prefix of the current folder's path + short maildir_is_prefix = 0; + if ( (strlen(tmp->path) > strlen(Maildir)) && + (strncmp(Maildir, tmp->path, strlen(Maildir)) == 0) ) + maildir_is_prefix = 1; + // calculate depth of current folder and generate its display name with indented spaces + int sidebar_folder_depth = 0; + char *sidebar_folder_name; + sidebar_folder_name = basename(tmp->path); + if ( maildir_is_prefix ) { + char *tmp_folder_name; + int i; + tmp_folder_name = tmp->path + strlen(Maildir); + for (i = 0; i < strlen(tmp->path) - strlen(Maildir); i++) { + if (tmp_folder_name[i] == '/') sidebar_folder_depth++; + } + if (sidebar_folder_depth > 0) { + sidebar_folder_name = malloc(strlen(basename(tmp->path)) + sidebar_folder_depth + 1); + for (i=0; i < sidebar_folder_depth; i++) + sidebar_folder_name[i]=' '; + sidebar_folder_name[i]=0; + strncat(sidebar_folder_name, basename(tmp->path), strlen(basename(tmp->path)) + sidebar_folder_depth); + } + } + printw( "%.*s", SidebarWidth - delim_len + 1, + make_sidebar_entry(sidebar_folder_name, tmp->msgcount, + tmp->msg_unread, tmp->msg_flagged)); + if (sidebar_folder_depth > 0) + free(sidebar_folder_name); + lines++; + } + SETCOLOR(MT_COLOR_NORMAL); + for ( ; lines < LINES-1 - (menu != MENU_PAGER || option(OPTSTATUSONTOP)); lines++ ) { + int i = 0; + move( lines, 0 ); + for ( ; i < SidebarWidth - delim_len; i++ ) + addch(' '); + } + return 0; +} + + +void set_buffystats(CONTEXT* Context) +{ + BUFFY *tmp = Incoming; + while(tmp) { + if(Context && !strcmp(tmp->path, Context->path)) { + tmp->msg_unread = Context->unread; + tmp->msgcount = Context->msgcount; + break; + } + tmp = tmp->next; + } +} + +void scroll_sidebar(int op, int menu) +{ + if(!SidebarWidth) return; + if(!CurBuffy) return; + + switch (op) { + case OP_SIDEBAR_NEXT: + if ( CurBuffy->next == NULL ) return; + CurBuffy = CurBuffy->next; + break; + case OP_SIDEBAR_PREV: + if ( CurBuffy->prev == NULL ) return; + CurBuffy = CurBuffy->prev; + break; + case OP_SIDEBAR_SCROLL_UP: + CurBuffy = TopBuffy; + if ( CurBuffy != Incoming ) { + calc_boundaries(menu); + CurBuffy = CurBuffy->prev; + } + break; + case OP_SIDEBAR_SCROLL_DOWN: + CurBuffy = BottomBuffy; + if ( CurBuffy->next ) { + calc_boundaries(menu); + CurBuffy = CurBuffy->next; + } + break; + default: + return; + } + calc_boundaries(menu); + draw_sidebar(menu); +} + --- /dev/null +++ b/sidebar.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) ????-2004 Justin Hibbits <jrh29@po.cwru.edu> + * Copyright (C) 2004 Thomer M. Gil <mutt@thomer.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#ifndef SIDEBAR_H +#define SIDEBAR_H + +struct MBOX_LIST { + char *path; + int msgcount; + int new; +} MBLIST; + +/* parameter is whether or not to go to the status line */ +/* used for omitting the last | that covers up the status bar in the index */ +int draw_sidebar(int); +void scroll_sidebar(int, int); +void set_curbuffy(char*); +void set_buffystats(CONTEXT*); + +#endif /* SIDEBAR_H */ debian/patches/mutt-patched/sidebar-dotted ========================================== License: 3-BSD When using IMAP, a '.' is often used as a separator instead of '/'. This patch enables mutt to find these dots and 1. correctly intend the dir in the sidebar 2. if "sidebar_shortpath" is set, shorten the dir to the part after the last dot I hope, it's usefull for someone ;) --- a/sidebar.c +++ b/sidebar.c @@ -255,14 +255,23 @@ int i; tmp_folder_name = tmp->path + strlen(Maildir); for (i = 0; i < strlen(tmp->path) - strlen(Maildir); i++) { - if (tmp_folder_name[i] == '/') sidebar_folder_depth++; + if (tmp_folder_name[i] == '/' || tmp_folder_name[i] == '.') sidebar_folder_depth++; } if (sidebar_folder_depth > 0) { - sidebar_folder_name = malloc(strlen(basename(tmp->path)) + sidebar_folder_depth + 1); + if (option(OPTSIDEBARSHORTPATH)) { + tmp_folder_name = strrchr(tmp->path, '.'); + if (tmp_folder_name == NULL) + tmp_folder_name = tmp->path; + else + tmp_folder_name++; + } + else + tmp_folder_name = tmp->path; + sidebar_folder_name = malloc(strlen(basename(tmp_folder_name)) + sidebar_folder_depth + 1); for (i=0; i < sidebar_folder_depth; i++) sidebar_folder_name[i]=' '; sidebar_folder_name[i]=0; - strncat(sidebar_folder_name, basename(tmp->path), strlen(basename(tmp->path)) + sidebar_folder_depth); + strncat(sidebar_folder_name, basename(tmp_folder_name), strlen(basename(tmp_folder_name)) + sidebar_folder_depth); } } printw( "%.*s", SidebarWidth - delim_len + 1, --- a/init.h +++ b/init.h @@ -1981,6 +1981,11 @@ ** .pp ** The width of the sidebar. */ + { "sidebar_shortpath", DT_BOOL, R_BOTH, OPTSIDEBARSHORTPATH, 0 }, + /* + ** .pp + ** Should the sidebar shorten the path showed. + */ { "pgp_use_gpg_agent", DT_BOOL, R_NONE, OPTUSEGPGAGENT, 0}, /* ** .pp --- a/mutt.h +++ b/mutt.h @@ -432,6 +432,7 @@ OPTSAVENAME, OPTSCORE, OPTSIDEBAR, + OPTSIDEBARSHORTPATH, OPTSIGDASHES, OPTSIGONTOP, OPTSORTRE, debian/patches/mutt-patched/sidebar-newonly =========================================== so only the mailbox with new messages will be shown (and/or) selected See Debian bug http://bugs.debian.org/532510 --- a/OPS +++ b/OPS @@ -184,3 +184,5 @@ OP_SIDEBAR_NEXT "go down to next mailbox" OP_SIDEBAR_PREV "go to previous mailbox" OP_SIDEBAR_OPEN "open hilighted mailbox" +OP_SIDEBAR_NEXT_NEW "go down to next mailbox with new mail" +OP_SIDEBAR_PREV_NEW "go to previous mailbox with new mail" --- a/curs_main.c +++ b/curs_main.c @@ -2236,6 +2236,8 @@ case OP_SIDEBAR_SCROLL_DOWN: case OP_SIDEBAR_NEXT: case OP_SIDEBAR_PREV: + case OP_SIDEBAR_NEXT_NEW: + case OP_SIDEBAR_PREV_NEW: scroll_sidebar(op, menu->menu); break; default: --- a/functions.h +++ b/functions.h @@ -173,6 +173,10 @@ { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL }, { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, + { "sidebar-next-new", OP_SIDEBAR_NEXT_NEW, NULL}, + { "sidebar-prev-new", OP_SIDEBAR_PREV_NEW, NULL}, + { "sidebar-next-new", OP_SIDEBAR_NEXT_NEW, NULL}, + { "sidebar-prev-new", OP_SIDEBAR_PREV_NEW, NULL}, { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, { NULL, 0, NULL } }; --- a/init.h +++ b/init.h @@ -1956,6 +1956,11 @@ {"sidebar_delim", DT_STR, R_BOTH, UL &SidebarDelim, "|"}, /* ** .pp + ** Show only new mail in the sidebar. + */ + {"sidebar_newmail_only", DT_BOOL, R_BOTH, OPTSIDEBARNEWMAILONLY, "no" }, + /* + ** .pp ** This specifies the delimiter between the sidebar (if visible) and ** other screens. */ --- a/mutt.h +++ b/mutt.h @@ -518,6 +518,8 @@ OPTDONTHANDLEPGPKEYS, /* (pseudo) used to extract PGP keys */ OPTUNBUFFEREDINPUT, /* (pseudo) don't use key buffer */ + OPTSIDEBARNEWMAILONLY, + OPTMAX }; --- a/pager.c +++ b/pager.c @@ -2756,6 +2756,8 @@ case OP_SIDEBAR_SCROLL_DOWN: case OP_SIDEBAR_NEXT: case OP_SIDEBAR_PREV: + case OP_SIDEBAR_NEXT_NEW: + case OP_SIDEBAR_PREV_NEW: scroll_sidebar(ch, MENU_PAGER); break; --- a/sidebar.c +++ b/sidebar.c @@ -261,8 +261,20 @@ SETCOLOR(MT_COLOR_NEW); else if ( tmp->msg_flagged > 0 ) SETCOLOR(MT_COLOR_FLAGGED); - else - SETCOLOR(MT_COLOR_NORMAL); + else { + /* make sure the path is either: + 1. Containing new mail. + 2. The inbox. + 3. The current box. + */ + if ((option (OPTSIDEBARNEWMAILONLY)) && + ( (tmp->msg_unread <= 0) && + ( tmp != Incoming ) && + ( strcmp( tmp->path, Context->path ) != 0 ) ) ) + continue; + else + SETCOLOR(MT_COLOR_NORMAL); + } move( lines, 0 ); if ( Context && !strcmp( tmp->path, Context->path ) ) { @@ -320,6 +332,29 @@ return 0; } +BUFFY * exist_next_new() +{ + BUFFY *tmp = CurBuffy; + if(tmp == NULL) return NULL; + while (tmp->next != NULL) + { + tmp = tmp->next; + if(tmp->msg_unread) return tmp; + } + return NULL; +} + +BUFFY * exist_prev_new() +{ + BUFFY *tmp = CurBuffy; + if(tmp == NULL) return NULL; + while (tmp->prev != NULL) + { + tmp = tmp->prev; + if(tmp->msg_unread) return tmp; + } + return NULL; +} void set_buffystats(CONTEXT* Context) { @@ -336,18 +371,33 @@ void scroll_sidebar(int op, int menu) { + BUFFY *tmp; if(!SidebarWidth) return; if(!CurBuffy) return; switch (op) { case OP_SIDEBAR_NEXT: + if (!option (OPTSIDEBARNEWMAILONLY)) { if ( CurBuffy->next == NULL ) return; CurBuffy = CurBuffy->next; break; + } + case OP_SIDEBAR_NEXT_NEW: + if ( (tmp = exist_next_new()) == NULL) + return; + else CurBuffy = tmp; + break; case OP_SIDEBAR_PREV: + if (!option (OPTSIDEBARNEWMAILONLY)) { if ( CurBuffy->prev == NULL ) return; CurBuffy = CurBuffy->prev; break; + } + case OP_SIDEBAR_PREV_NEW: + if ( (tmp = exist_prev_new()) == NULL) + return; + else CurBuffy = tmp; + break; case OP_SIDEBAR_SCROLL_UP: CurBuffy = TopBuffy; if ( CurBuffy != Incoming ) { debian/patches/mutt-patched/sidebar-sorted ========================================== License: 3-BSD When using IMAP and imap_check_subscribed, the server reports the dirs in a random order. This patch introduces a new option, sidebar_sort. Which, when it is set, sorts the dirs in the sidebar alphabetically. I hope, it's usefull for someone ;) PS: This has to be applied ontop of my sidebar-dotted patch, but it should be easy to adopt it to a vanilla mutt. --- a/sidebar.c +++ b/sidebar.c @@ -54,6 +54,35 @@ for ( ; tmp->next != 0; tmp = tmp->next ) tmp->next->prev = tmp; + if (option(OPTSIDEBARSORT)) { + int needsort=1; + BUFFY *prev; + BUFFY *next; + BUFFY *tmp2; + while (needsort==1) { + needsort=0; + tmp = Incoming; + for ( ; tmp ; tmp=tmp->next ) { + if (tmp->next != NULL && strcoll(tmp->path, tmp->next->path) > 0) { + needsort=1; + prev = tmp->prev; + next = tmp->next; + if (prev != NULL) + prev->next = next; + else + Incoming = next; + next->prev = prev; + tmp2 = next->next; + next->next = tmp; + tmp->prev = next; + tmp->next = tmp2; + if (tmp2 != NULL) + tmp2->prev = tmp; + } + } + } + } + if ( TopBuffy == 0 && BottomBuffy == 0 ) TopBuffy = Incoming; if ( BottomBuffy == 0 ) { --- a/init.h +++ b/init.h @@ -1986,6 +1986,11 @@ ** .pp ** Should the sidebar shorten the path showed. */ + { "sidebar_sort", DT_BOOL, R_BOTH, OPTSIDEBARSORT, 0 }, + /* + ** .pp + ** Should the sidebar be sorted. + */ { "pgp_use_gpg_agent", DT_BOOL, R_NONE, OPTUSEGPGAGENT, 0}, /* ** .pp --- a/mutt.h +++ b/mutt.h @@ -433,6 +433,7 @@ OPTSCORE, OPTSIDEBAR, OPTSIDEBARSHORTPATH, + OPTSIDEBARSORT, OPTSIGDASHES, OPTSIGONTOP, OPTSORTRE, debian/patches/mutt-patched/sidebar-utf8 ======================================== entirely make_sidebar_entry so it also fixes some segfaults due to misallocations and overflows. See: http://bugs.debian.org/584581 http://bugs.debian.org/603287 Author: Antonio Radici <antonio@dyne.org> Index: mutt/sidebar.c =================================================================== --- mutt.orig/sidebar.c 2011-05-02 13:36:10.000000000 +0000 +++ mutt/sidebar.c 2011-05-02 13:36:30.000000000 +0000 @@ -30,6 +30,7 @@ #include <libgen.h> #include "keymap.h" #include <stdbool.h> +#include <wchar.h> /*BUFFY *CurBuffy = 0;*/ static BUFFY *TopBuffy = 0; @@ -111,36 +112,72 @@ char *make_sidebar_entry(char *box, int size, int new, int flagged) { - static char *entry = 0; - char *c; - int i = 0; - int delim_len = strlen(SidebarDelim); - - c = realloc(entry, SidebarWidth - delim_len + 2); - if ( c ) entry = c; - entry[SidebarWidth - delim_len + 1] = 0; - for (; i < SidebarWidth - delim_len + 1; entry[i++] = ' ' ); - i = strlen(box); - strncpy( entry, box, i < (SidebarWidth - delim_len + 1) ? i : (SidebarWidth - delim_len + 1) ); - - if (size == -1) - sprintf(entry + SidebarWidth - delim_len - 3, "?"); - else if ( new ) { - if (flagged > 0) { - sprintf( - entry + SidebarWidth - delim_len - 5 - quick_log10(size) - quick_log10(new) - quick_log10(flagged), - "% d(%d)[%d]", size, new, flagged); - } else { - sprintf( - entry + SidebarWidth - delim_len - 3 - quick_log10(size) - quick_log10(new), - "% d(%d)", size, new); - } - } else if (flagged > 0) { - sprintf( entry + SidebarWidth - delim_len - 3 - quick_log10(size) - quick_log10(flagged), "% d[%d]", size, flagged); - } else { - sprintf( entry + SidebarWidth - delim_len - 1 - quick_log10(size), "% d", size); - } - return entry; + char int_store[20]; // up to 64 bits integers + int right_width, left_width; + int box_len, box_bytes; + int int_len; + int right_offset = 0; + int delim_len = strlen(SidebarDelim); + static char *entry; + + right_width = left_width = 0; + box_len = box_bytes = 0; + + // allocate an entry big enough to contain SidebarWidth wide chars + entry = malloc((SidebarWidth*4)+1); // TODO: error check + + // determine the right space (i.e.: how big are the numbers that we want to print) + if ( size > 0 ) { + int_len = snprintf(int_store, sizeof(int_store), "%d", size); + right_width += int_len; + } else { + right_width = 1; // to represent 0 + } + if ( new > 0 ) { + int_len = snprintf(int_store, sizeof(int_store), "%d", new); + right_width += int_len + 2; // 2 is for () + } + if ( flagged > 0 ) { + int_len = snprintf(int_store, sizeof(int_store), "%d", flagged); + right_width += int_len + 2; // 2 is for [] + } + + // determine how much space we have for *box and its padding (if any) + left_width = SidebarWidth - right_width - 1 - delim_len; // 1 is for the space + //fprintf(stdout, "left_width: %d right_width: %d\n", left_width, right_width); + // right side overflow case + if ( left_width <= 0 ) { + snprintf(entry, SidebarWidth*4, "%-*.*s ...", SidebarWidth-4-delim_len, SidebarWidth-4-delim_len, box); + return entry; + } + right_width -= delim_len; + + // to support utf-8 chars we need to add enough space padding in case there + // are less chars than bytes in *box + box_len = mbstowcs(NULL, box, 0); + box_bytes = strlen(box); + // debug + //fprintf(stdout, "box_len: %d box_bytes: %d (diff: %d)\n", box_len, box_bytes, (box_bytes-box_len)); + // if there is less string than the space we allow, then we will add the + // spaces + if ( box_len != -1 && box_len < left_width ) { + left_width += (box_bytes - box_len); + } + // otherwise sprintf will truncate the string for us (therefore, no else case) + + // print the sidebar entry (without new and flagged messages, at the moment) + //fprintf(stdout, "left_width: %d right_width: %d\n", left_width, right_width); + right_offset = snprintf(entry, SidebarWidth*4, "%-*.*s %d", left_width, left_width, box, size); + + // then pad new and flagged messages if any + if ( new > 0 ) { + right_offset += snprintf(entry+right_offset, SidebarWidth*4-right_offset, "(%d)", new); + } + if ( flagged > 0 ) { + right_offset += snprintf(entry+right_offset, SidebarWidth*4-right_offset, "[%d]", flagged); + } + + return entry; } void set_curbuffy(char buf[LONG_STRING])
Cokiee Shell Web 1.0, Coded By Razor
Neueste Kommentare