neomutt-neomutt-20171215/000077500000000000000000000000001321473123000151365ustar00rootroot00000000000000neomutt-neomutt-20171215/.clang-format000066400000000000000000000037611321473123000175200ustar00rootroot00000000000000Language: Cpp # BasedOnStyle TabWidth: 8 UseTab: Never IndentWidth: 2 ColumnLimit: 80 BreakBeforeBraces: Allman IncludeCategories: - Regex: '"config\.h"' Priority: -10 - Regex: '' Priority: -8 - Regex: '<.*>' Priority: -7 - Regex: '".*_private\.h"' Priority: -6 - Regex: '"mutt/.*\.h"' Priority: -4 - Regex: '"conn/.*\.h"' Priority: -3 - Regex: '"mutt\.h"' Priority: -2 # Main Header 0 - Regex: '".*"' Priority: 5 AlignAfterOpenBracket: true AlignEscapedNewlinesLeft: false AlignOperands: true AlwaysBreakAfterReturnType: None BinPackArguments: true BinPackParameters: true BreakBeforeBinaryOperators: false BreakBeforeTernaryOperators: false Cpp11BracedListStyle: false DerivePointerAlignment: false IndentCaseLabels: true IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false MaxEmptyLinesToKeep: 1 PointerAlignment: Right ReflowComments: false SortIncludes: true SpaceAfterCStyleCast: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false # Allow some slightly over-long lines PenaltyExcessCharacter: 1 # NEVER AllowShortFunctionsOnASingleLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false # OPTIONAL AlignTrailingComments: true # AlignConsecutiveAssignments: true # AlignConsecutiveDeclarations: true # AlwaysBreakBeforeMultilineStrings: true # SpacesBeforeTrailingComments: 2 neomutt-neomutt-20171215/.editorconfig000066400000000000000000000004361321473123000176160ustar00rootroot00000000000000root = true [*] end_of_line = lf insert_final_newline = true charset = utf8 trim_trailing_whitespace = false # those two settings may be overwritten below. indent_size = 2 indent_style = space [ChangeLog.md] indent_size = 2 [{[mM]akefile*,*.am}] indent_size = 4 indent_style = tab neomutt-neomutt-20171215/.gitattributes000066400000000000000000000000301321473123000200220ustar00rootroot00000000000000*.h linguist-language=C neomutt-neomutt-20171215/.github/000077500000000000000000000000001321473123000164765ustar00rootroot00000000000000neomutt-neomutt-20171215/.github/ISSUE_TEMPLATE.md000066400000000000000000000003551321473123000212060ustar00rootroot00000000000000## bug reports * Expected behavior * Actual behavior * Steps to reproduce * Used program versions * Operating System and its version ## feature requests * Why do you not like the current state? * What would you like to see to change? neomutt-neomutt-20171215/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000013431321473123000223000ustar00rootroot00000000000000* **What does this PR do?** * **Are there points in the code the reviewer needs to double check?** * **Screenshots (if relevant)** * **Does this PR meet the acceptance criteria?** (This is just a reminder for you, this section can be removed if you fulfill it.) - Documentation created/updated (you have to edit [doc/manual.xml.head](https://www.github.com/neomutt/neomutt/blob/master/doc/manual.xml.head) for that) - All builds are passing - Added [doxygen code documentation](https://www.neomutt.org/dev/doxygen) [syntax](http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html) - Code follows the [style guide](https://www.neomutt.org/dev/coding-style) * **What are the relevant issue numbers?** neomutt-neomutt-20171215/.gitignore000066400000000000000000000023411321473123000171260ustar00rootroot00000000000000# Wildcard ignores *.o *.a *.in tags # Autosetup's jimsh autosetup/jimsh0 # Ignore these files in all subdirectories **/Makefile # Ignore directories **/.deps/ **/*.Po **/*.Tpo autom4te.cache/ .build-aux/ # Built conststrings.c git_ver.h hcache/hcversion.h neomutt pgpewrap pgpring txt2c # ./configure config.h config.log config.status stamp-h1 # Ignored within subdirectories doc/neomutt.1 doc/*.html doc/makedoc doc/manual.txt doc/manual.xml doc/neomuttrc doc/neomuttrc.man po/*.gmo po/*.mo po/POTFILES po/stamp-po # make dist distcheck neomutt-*.tar.gz neomutt-*/ # Generated by `autoreconf --install` ABOUT-NLS aclocal.m4 configure m4/codeset.m4 m4/gettext.m4 m4/glibc2.m4 m4/glibc21.m4 m4/iconv.m4 m4/intdiv0.m4 m4/intl.m4 m4/intldir.m4 m4/intlmacosx.m4 m4/intmax.m4 m4/inttypes-pri.m4 m4/inttypes_h.m4 m4/lcmessage.m4 m4/lib-ld.m4 m4/lib-link.m4 m4/lib-prefix.m4 m4/lock.m4 m4/longlong.m4 m4/nls.m4 m4/po.m4 m4/printf-posix.m4 m4/progtest.m4 m4/size_max.m4 m4/stdint_h.m4 m4/uintmax_t.m4 m4/visibility.m4 m4/wchar_t.m4 m4/wint_t.m4 m4/xsize.m4 po/boldquot.sed po/en@boldquot.header po/en@quot.header po/insert-header.sin po/Makevars.template po/quot.sed po/remove-potcdate.sed po/remove-potcdate.sin po/Rules-quot po/neomutt.pot html neomutt-neomutt-20171215/.mailmap000066400000000000000000001144471321473123000165720ustar00rootroot00000000000000## NeoMutt Contributors Adam Borowski Adam Borowski # @kilobyte Andreas Rammhold Andreas Rammhold # @andir André Berger André Berger # @hvkls André Berger André Berger # @hvkls Anton Rieger Anton Rieger # seishinryohosha Anton Rieger seishinryohosha # seishinryohosha Antonio Radici Antonio Radici # @aradici Antonio Radici Unknown # @aradici Austin Ray Austin Ray # @austin-ray Bernard Pratz Bernard 'Guyzmo' Pratz # @guyzmo Bernard Pratz Bernard Pratz # @guyzmo Bernard Pratz Guyzmo # @guyzmo Bernard Pratz guyzmo # @guyzmo Bletchley Park <18015852+libBletchley@users.noreply.github.com> Bletchley Park <18015852+libBletchley@users.noreply.github.com> # @libbletchley Bo Yu Bo Yu # @yuzibo Bryan Bennett Bryan Bennett # @bbenne10 Chris Czettel chrissl # @christopher-john-czettel Chris Czettel Christopher John CZETTEL # @christopher-john-czettel Christian Dröge Christian Dröge # @cdroege Christoph Berg Christoph Berg # @myon Clemens Lang Clemens Lang # @neverpanic Damien Riegel Damien R # @d-k-c Damien Riegel Damien Riegel # @d-k-c Damien Riegel Damien Riegel # @d-k-c Damien Riegel Damien Riegel # @d-k-c Darshit Shah Darshit Shah # @darnir Darshit Shah Darshit Shah # @darnir David Sterba David Sterba # @kdave Doug Stone-Weaver Doug Stone-Weaver # @doweaver Edward Betts Edward Betts # @edwardbetts Elimar Riesebieter Elimar Riesebieter # @riesebie Fabian Groffen Fabian Groffen # @grobian Fabian Groffen Fabian Groffen # @grobian Fabrice Bellet Fabrice Bellet # @fbellet Florian Klink Florian Klink # @flokli František Hájik František Hájik # Guillaume Brogi Guillaume Brogi # @guiniol Guillaume Brogi guiniol # @guiniol Ian Zimmerman Ian Zimmerman # @nobrowser Ian Zimmerman Ian Zimmerman # @nobrowser Ian Zimmerman Ian Zimmerman # @nobrowser Ismaël Bouya Ismaël Bouya # @immae Ivan Tham Ivan Tham # @pickfire Jack Stratton Jack Stratton # @phroa Jakub Wilk Jakub Wilk # jwilk Jelle van der Waa Jelle van der Waa # jelly Jenya Sovetkin Jenya Sovetkin # @esovetkin Johannes Weißl Johannes Weißl # @weisslj Jonathan Perkin Jonathan Perkin # @jperkin Joshua Jordi Jakkin # @jakkinstewart Joshua Jordi Joshua Jordi # @jakkinstewart Julian Andres Klode Julian Andres Klode # @julian-klode Karel Zak Karel Zak # @karelzak Kevin Decherf Kevin Decherf # @kdecherf Kevin Velghe Kevin Velghe # @paretje Larry Rosenman Larry Rosenman # @lrosenman Manos Pitsidianakis Manos Pitsidianakis # @epilys Marcin Rajner Marcin Rajner lenovo # @mrajner Marco Hinz Marco Hinz # @mhinz Mehdi Abaakouk Mehdi ABAAKOUK # @sileht Mehdi Abaakouk Mehdi Abaakouk # @sileht ng0 ng0 # @ng-0 Nicolas Bock Nicolas Bock # @nicolasbock Peter Hogg Pig Monkey # @pigmonkey Peter Lewis Peter Lewis # @petelewis Phil Pennock Phil Pennock # @philpennock Pierre-Elliott Bécue Pierre-Elliott Bécue # @p-eb Pierre-Elliott Bécue Pierre-Elliott Bécue # @p-eb Pietro Cerutti Pietro Ceruti # @gahr Pietro Cerutti Pietro Cerutti # @gahr Regid Ichira Regid Ichira # Reis Radomil Reis Radomil # @reisradomil Riad Wahby Riad S. Wahby # @kwantam Richard Russon Richard Russon # @flatcap Rubén Llorente Rubén Llorente # Santiago Torres Santiago Torres # @santiagotorres Serge Gebhardt Serge Gebhardt # @sgeb Somini somini # @somini Somini somini # @somini Stefan Assmann sassmann # @sassmann Stefan Assmann Stefan Assmann # @sassmann Stefan Assmann Stefan Assmann # @sassmann Stefan Bühler Stefan Bühler # @stbuehler Stephen Gilles S. Gilles # Steven Ragnarök Steven! Ragnarök # @nuclearsandwich Thomas Adam Thomas Adam # @thomasadam Thomas Klausner Thomas Klausner # @0-wiz-0 Tobias Angele Tobias Angele # @toogley Tobias Angele toogley # @toogley Udo Schweigert Udo Schweigert # Werner Fink Werner Fink # @bitstreamout Wieland Hoffmann Wieland Hoffmann # @mineo Wieland Hoffmann Wieland Hoffmann # @mineo William Pettersson William Pettersson # @wpettersson William Pettersson William Pettersson # @wpettersson Yoshiki Vázquez Baeza Yoshiki Vázquez Baeza # @eldeveloper Zero King Zero King # @l2dy ## Mutt Contributors Aaron Lehmann Aaron Lehmann Aaron Schrab Aaron Schrab Aaron Schrab Aaron Schrab Adeodato Simó Adeodato Simó Adeodato Simó Adeodato Simó Alain Bench Alain Bench Alain Bench Alain Bench Alain Penders Alain Penders Alexey Froloff Alexey I. Froloff Alexey Tourbin Alexey Tourbin Ambrose Li Ambrose Li Anatoly Vorobey Anatoly Vorobey Anders Helmersson Anders Helmersson Anders Helmersson Anders Helmersson Andreas Amann Unknown Andreas Jaggi Andreas Jaggi Andreas Jobs Andreas Jobs Andrew Gaul Andrew Gaul Andrew Nosenko Andrew W. Nosenko Antoine Reilles Antoine Reilles Anton Lindqvist Anton Lindqvist Armin Wolfermann Armin Wolfermann Aron Griffis Aron Griffis Athanasios Douitsis Athanasios Douitsis Benno Schulenberg Benno Schulenberg Bernd Ahlers Bernd Ahlers Bertram Felgenhauer Bertram Felgenhauer Bertrand Janin Bertrand Janin Bill Nottingham Bill Nottingham Bjoern Jacke Bjoern Jacke Bjoern Jacke Bjoern Jacke Brendan Cully Brendan Cully Brent Nordquist Brent J. Nordquist Bruno Postle Bruno Postle Byrial Jensen Byrial Jensen Cameron Patrick Cameron Patrick Cedric Duval Cedric Duval Cedric Duval Cedric Duval Christian Aichinger Christian Aichinger Christian Ebert Christian Ebert Christian Vogel Christian Vogel Christoph Ludwig Christoph Ludwig Consus Consus convert-repo convert-repo Dale Woolridge Dale Woolridge Dale Woolridge Dale Woolridge Dan Born Dan Born Dan Fandrich Dan Fandrich Dan Hopper Dan Hopper Dan Loewenherz Dan Loewenherz Dan Nelson Dan Nelson Dan Ohnesorg Dan Ohnesorg Daniel Burrows Daniel Burrows Daniel Jacobowitz Daniel Jacobowitz David Champion David Champion David Champion David Champion David Jardine David Jardine David Shaw David Shaw David Wilson David Wilson David Yitzchak Cohen David Yitzchak Cohen David Yitzchak Cohen David Yitzchak Cohen Deng Xiyue Deng Xiyue Derek Martin Derek Martin Derek Martin Derek Martin Derek Schrock Derek Schrock Dmitri Vereshchagin Dmitri Vereshchagin Edmund Grimley Evans Edmund GRIMLEY EVANS Edmund Grimley Evans Edmund GRIMLEY EVANS Eike Rathke Eike Rathke Elmar Hoffmann Elmar Hoffmann Emanuele Giaquinta Emanuele Giaquinta Emanuele Giaquinta Emanuele Giaquinta Eric Raymond Unknown Erik Hovland Erik Hovland Fahri Cihan Demirci Fahri Cihan Demirci Florian Weimer Florian Weimer Gary Johnson Gary Johnson Gary Johnson Gary Johnson Gero Treuner Gero Treuner Gregory Shapiro Gregory Shapiro Guilhem Moulin Guilhem Moulin guns guns H.N.Caldwell hncaldwell Holger Weiss Holger Weiss Holger Weiss Holger Weiss Honza Horak Honza Horak Honza Horak Unknown Horst Schirmeier Horst Schirmeier Horvath Szabolcs Horvath Szabolcs Hugo Haas Hugo Haas Im Eunjea Im Eunjea Ivan Vilata i Balaguer Ivan Vilata i Balaguer James McCoy James McCoy Jeff Ito Jeff Ito Jesus Climent Jesus Climent Jesus Climent Unknown Jim Mock Jim Mock Joel Dahl Joel Dahl Johan D Johan D Johan Svedberg Johan Svedberg Johan Svedberg Johan Svedberg John Beck Unknown John Hawkinson John Hawkinson John Swinbank John Swinbank Jon Miles Jon Miles Jonathan Wakely Jonathan Wakely Joël Riou Joël Riou Juan Altmayer Pizzorno Juan Altmayer Pizzorno Jukka Salmi Jukka Salmi Jukka Salmi Jukka Salmi Julien Cristau Julien Cristau Julius Plenz Julius Plenz Jörgen Tegnér Jörgen Tegnér Kees Cook Kees Cook Kees Cook Unknown Kevin McCarthy Kevin McCarthy Kevin Scannell Kevin Scannell Kevin Scannell Kevin Scannell Kimihiro Nonaka NONAKA Kimihiro Kyle Wheeler Kyle Wheeler Kyle Wheeler Kyle Wheeler Kyle Wheeler Kyle Wheeler Lars Hecking Lars Hecking Lars Hecking Lars Hecking Lars Hecking Lars Hecking Luke Mewburn Luke Mewburn Mads Martin Joergensen Mads Martin Joergensen Malcolm Parsons Malcolm Parsons Mantas Mikulėnas Mantas Mikulėnas Marcel Telka Marcel Telka Marcel Telka Unknown Marco d'Itri Marco d'Itri Marco Paolone Marco Paolone Martin Michlmayr Martin Michlmayr Martin Sandsmark Martin Sandsmark Martin Siegert Martin Siegert Masayuki Moriyama Masayuki Moriyama Matt Kraai Matt Kraai Matthias Andree Matthias Andree Michael Elkins Michael Elkins Michael Elkins Michael Elkins Michael Elkins Unknown Michael Tatge Michael Tatge Michael Vrable Michael Vrable Michał Kępień Michał Kępień Mike Frysinger Mike Frysinger Mike Hallock Mike Hallock Mike Schiraldi <1074468571@schiraldi.org> Mike Schiraldi <1074468571@schiraldi.org> Miroslav Lichvar Miroslav Lichvar Moritz Schulte Moritz Schulte Moritz Schulte Moritz Schulte Moritz Schulte Unknown Morten Bo Johansen Morten Bo Johansen Morten Bo Johansen Morten Bo Johansen Morten Bo Johansen Morten Bo Johansen Morten Bo Johansen Morten Bo Johansen N.J. Mann N.J. Mann Nathan Dushman Nathan Dushman Neil Brown Neil Brown Nicolas Rachinsky Nicolas Rachinsky Nicolas Rachinsky Nicolas Rachinsky Nik Melchior Nik A. Melchior Olaf Hering Olaf Hering Ondřej Bílka Ondřej Bílka Oota Toshiya oota toshiya Oswald Buddenhagen Oswald Buddenhagen Patrick Welche Patrick Welche Patrick Welche Patrick Welche Paul Eggert Paul Eggert Paul Walker Paul WALKER Paul Walker Paul Walker Pawel Dziekonski Pawel Dziekonski Pawel Dziekonski Pawel Dziekonski Peter Collingbourne Peter Collingbourne Peter Rosin Peter Rosin Peter Wu Peter Wu Petr Písař Petr Pisar Petr Písař Petr Písař Petr Písař Petr P√≠sa≈ô Phil Pennock Phil Pennock Piarres Beobide Egaña pi Piarres Beobide Egaña Piarres Beobide Egaña Rado Smiljanic Rado S Rado Smiljanic Rado Smiljanic Ralf Wildenhues Ralf Wildenhues Recai Oktas Recai Oktas René Clerc René Clerc René Clerc Ren√© Clerc Rich Burridge Unknown Robert Schiele Robert Schiele Rocco Rutte Rocco Rutte Rocco Rutte Rocco Rutte Roger Cornelius Roger Cornelius Roger Pau Monne Roger Pau Monne Roland Rosenfeld Roland Rosenfeld Roman Kagan Roman Kagan Roman Kraevskiy Roman Kraevskiy Ronny Haryanto Ronny Haryanto Ronny Haryanto Ronny Haryanto Rudi Chiarito Rudi Chiarito Rudy Taraschi Rudy Taraschi Rupert Levene Rupert Levene Sahil Tandon Sahil Tandon Sami Farin Sami Farin Sami Farin Sami Farin Samuel Tardieu Samuel Tardieu Scott Koranda Scott Koranda Sertaç Ö. Yıldız Sertaç Ö. Yıldız Seth Arnold Seth Arnold Seth Forshee Seth Forshee Simon Ruderich Simon Ruderich Stefan Kuhn Stefan Kuhn Steve Kemp Steve Kemp Steve Kennedy Steve Kennedy Sébastien Hinderer Sébastien Hinderer Sébastien Hinderer Sébastien Hinderer Takashi Takizawa Takashi TAKIZAWA Takashi Takizawa TAKIZAWA Takashi Tamotsu Takahashi TAKAHASHI Tamotsu Tamotsu Takahashi TAKAHASHI Tamotsu Tamotsu Takahashi TAKAHASHI Tamotsu Tamotsu Takahashi Takahashi Tamotsu Tamotsu Takahashi Tamotsu TAKAHASHI ta_panta_rei <7vvtch802@sneakemail.com> ta_panta_rei <7vvtch802@sneakemail.com> Thomas Glanzmann Thomas Glanzmann Thomas Roessler Thomas Roessler Thomas Wiegner Thomas Wiegner Tim Stoakes Tim Stoakes Todd Zullinger Todd Zullinger Tomas Hoger Tomas Hoger Tommi Komulainen Tommi Komulainen Tomokazu Yonetani YONETANI Tomokazu Tony Leneis Tony Leneis Toomas Soome Toomas Soome Toomas Soome Toomas Soome Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Urs Janßen Urs Janßen Velko Hristov Velko Hristov Vincent Lefevre Vincent Lefevre Vincent Lefevre Vincent Lefevre Vladimir Marek Vladimir Marek Vsevolod Volkov Vsevolod Volkov Werner Koch Werner Koch Wilfried Goesgens Wilfried Goesgens Will Fiveash Will Fiveash Will Fiveash Will Fiveash Will Fiveash Will Fiveash Wouter Verheijen Wouter Verheijen Yasuhiro Matsumoto Yasuhiro Matsumoto neomutt-neomutt-20171215/.travis.yml000066400000000000000000000015451321473123000172540ustar00rootroot00000000000000notifications: email: recipients: - rich@flatcap.org on_success: change on_failure: always sudo: false dist: trusty language: c compiler: gcc cache: ccache addons: apt: packages: - libgpgme11-dev - libnotmuch-dev - libtokyocabinet-dev - libkyotocabinet-dev - libgdbm-dev - libdb-dev - liblmdb-dev - libqdbm-dev - libslang2-dev - libgnutls-dev - libssl-dev - libsasl2-dev - libgss-dev - xsltproc - lynx - docbook-simple - docbook-xsl - libxml2-utils - gettext - lua5.2 - liblua5.2-dev - autopoint git: depth: 3 install: - git clone --depth 1 https://github.com/neomutt/travis-build.git ~/config before_script: - ccache --zero-stats script: - ~/config/build after_script: - ccache --show-stats neomutt-neomutt-20171215/AUTOSETUP.md000066400000000000000000000143371321473123000170610ustar00rootroot00000000000000# Autosetup build system for NeoMutt This document explains the changes introduced to NeoMutt's build system by switching to an Autosetup-based configuration and the rationale behind some of the choices that have been made. ## Introduction "[Autosetup](https://msteveb.github.io/autosetup/) is a tool, similar to the Autotools family, to configure a build system for the appropriate environment, according to the system capabilities and the user-selected options." The website explains in great details the major goals of the project and the similarities and differences with Autotools. For the sake of this short introduction, I'll summarize the major points, as relevant to Neomutt. ### Zero-dependency In general, Autotools-based systems read a script named `configure.ac`, which is written in a mix of m4 and shell called M4sh. This script is translated into a portable shell script named `configure` by means of `autoconf` and other support tools. Autosetup, on the other hand, reads and directly runs a configuration script, usually named `auto.def`. The script and the support modules in the `autosetup/` directory are written in [Tcl](https://tcl.tk). The major result of this choice is that there is no need for an initial translation to a portable environment. Autosetup ships with a minimal implementation of Tcl called [Jim](http://jim.tcl.tk), which is compiled and used on-demand, if no full Tcl shell is found in the path. Projects ship a `configure` script that can be directly run. So, this ```sh autoreconf --install && ./configure && make ``` becomes ```sh ./configure && make ``` **Bottom line**: no build-time dependencies, faster configure stage, higher level of debuggability of the build scripts, no more "autoconf before ship". ### Simple and consistent options system Autosetup allows users to personalize the build at configure time. Unlike Autotools, the object model for the options system is simple and consistent. There are two types of options: booleans and strings. Both can be specified to have default values. The options are defined in a self-explanatory `options` section (it's actually a proc under the hood): ``` options { smime=1 => "Disable S/Mime" flock=0 => "Enable flock(1)" gpgme=0 => "Enable GPGME" with-gpgme:path => "Location of GPGME" with-mailpath:=/var/mail => "Location of the spool mailboxes" } ``` A user can configure the build to suit his needs by modifying the default values, e.g., `./configure --disable-smime --enable-flock --gpgme --with-gpgme=/usr/local`. Within `auto.def`, option can be easily queried with `[opt-bool smime]` and `[opt-val with-gpgme $prefix]`, with the latter using `$prefix` if not value was given. In the above example, `[opt-val with-mailpath]` will return the default value `/var/mail` if not overridden by the user. **Bottom line**: no more confusion due to the differences and similarities between `--with-opt`, `--enable-opt`, `with_val`, `without_val`. Simple and self-documenting API for managing configure options. ### Focus on features Autotools comes with high level primitives, which allow to focus on the features to be tested. In the ~850 lines of our `auto.def` file - compare to the current 970 lines in configure.ac - there is almost no boilerplate code. The code is self-explanatory and easily readable - yes, it is Tcl and it might take a little getting used to, but it's nothing compared to M4sh. **Bottom line**: readable and debuggable configure script, no M4sh quoting intricacies, easily extensible. ## Autosetup for Neomutt In this section, I'll explain a few design decisions I took when porting NeoMutt's build system to Autosetup. ### Non-recursive Makefiles The build system is driven by the top-level Makefile, which includes additional Makefiles from the subdirectories `doc`, `contrib`, and `po`. I'll stress that these Makefiles are included by the main Makefile and *not* invoked recursively (google for "recursive make considered harmful"). The build system relies on the fact that each of the sub-makefiles defines its own targets, conventionally named all-*subdir*, clean-*subdir*, install-*subdir*, and uninstall-*subdir*. For example, `po/Makefile` defines the targets `all-po`, `clean-po`, `install-po`, and `uninstall-po`. To add a new subdir named `mydir` to the build system, follow these steps: 1. create `mydir/Makefile.autosetup` 2. define the target `all-mydir`, `clean-mydir`, `install-mydir`, and `uninstall-mydir` 3. update the `subdirs` variable definition in `auto.def` The top-level Makefile will invoke your targets as dependencies for the main `all`, `clean`, `install`, and `uninstall` targets. ### Configuration options For a list of the currently supported options and a brief help text, please run `./configure.autosetup --help`. ### Installation / uninstallation Two parameters play an important role when deciding where to install NeoMutt. The first is the `--prefix` parameter to configure. The second is the `DESTDIR` variable used by Makefiles. The parameter `--prefix` is used to specify both the default search path for headers and libraries and the final directory structure of the installed files. These are often the same: if you have your dependencies installed in `/usr/include` and `/usr/lib`, you also probably want the NeoMutt executable to end up in `/usr/bin` and its documentation in `/usr/share/doc`. This behavior can be tweaked by specifying where 3rd party dependencies are to be found. This is done on a per-dependency basis using the `--with-=path` family of options. As an example, a GPGMe installation in `/opt` can be looked up using the arguments `--gpgme --with-gpgme=/opt`. The second parameter, the `DESTDIR` make variable, is used for staged builds and is prepended to the final path. This allows to stage the whole installation into `./tmp` by simply using a make invokation like `make DESTDIR=./tmp install`. Staged builds are used by downstream packagers and allow to track the list of files installed by a package: it is easier to `find ./tmp -type f` than to snapshot the whole `/` filesystem and figure out the modifications introduced by installing a package. This information is usually used to list the contents of an installed package or to uninstall it. neomutt-neomutt-20171215/CODE_OF_CONDUCT.md000066400000000000000000000070401321473123000177360ustar00rootroot00000000000000# Code of Conduct NeoMutt's community is made up from developers and enthusiasts around the globe. We come from different countries and cultures, we speak different languages. We encourage everyone to follow these guidelines, to help keep NeoMutt a positive community in which to work. If you have experienced some unwelcome behaviour, please contact the project leader in confidence: Richard Russon (flatcap) <[rich@flatcap.org](mailto:rich@flatcap.org)> ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behaviour that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behaviour by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behaviour and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviours that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported by contacting the project leader, Richard Russon (flatcap) <[rich@flatcap.org](mailto:rich@flatcap.org)> All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the Contributor Covenant homepage, version 1.4, available at http://contributor-covenant.org/version/1/4. neomutt-neomutt-20171215/CONTRIBUTING.md000066400000000000000000000050511321473123000173700ustar00rootroot00000000000000Hello and thank you for your interest in our project :) Tips for a good bugreport ------------------------------- * Help us help you! The more details you give, the better we\'ll be able to help you out. * Please use markdown syntax, especially for backtraces and config files! * Please provide a minimal working example. That means, you should try to replicate your issue with the minimal configuration from your muttrc and/or any other relevant config files. These are the details you should be posting. * Sometimes even small and inconspicuous details matter, so please be very careful when writing down the steps to reproduce your problem. * Please give us a list of program versions of **every** program relevant to your report. Tips for writing a good feature request --------------------------------------- * Describe your problem and especially the context of it very detailed. It has happened many times, that the problem could be solved without implementing another feature. * Please describe also your desired solution carefully. Nothing is more frustrating - for both you and us, as developers - if we understand your problem the wrong way and implement something which doesn't help you. Tips for writing good pull requests --------------------------------------- * The first line should be a short (50 characters or less) summary of your commit message. If you can't find a short enough one-line summary, split the commit into multiple ones. * Keep one line between the one-line summary and the body. * Use bullet points in the body of the commit message to separate multiple things you did. * your commits should be clear and concise. That means also you shouldn't sum up two features or two bug fixes into one commit. If you do, it makes both bug fixes or features harder to understand. Also, don't hesitate to [rewrite](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) the Github history of your development branch. * Wrap the body of the commit message at around 80 characters. * if your commit addresses a particular PR, commit or Issue, please say so in your commit. The Github documentation ([1](https://help.github.com/articles/autolinked-references-and-urls/) and [2](https://help.github.com/articles/closing-issues-via-commit-messages/)) can help you with that. * Please eliminate any warnings gcc or any other tool produces during the compilation stage. * If your commit addresses only a specific method or a specific file, please say so in your commit. Thank you for your help! The Neomutt Team. neomutt-neomutt-20171215/COPYRIGHT000066400000000000000000000033531321473123000164350ustar00rootroot00000000000000The following copyright notices apply to most of the program. Some modules are under different licenses, or in the public domain. Please note that this is by no means an exhaustive list of all the persons who have been contributing to this program. Please see the manual for a (probably still non complete) list of the persons who have been helpful with the development of this program. Please also see our source code repository at http://dev.mutt.org/hg/mutt/ for the full history of commits. Copyright (C) 1996-2016 Michael R. Elkins Copyright (C) 1996-2002 Brandon Long Copyright (C) 1997-2009 Thomas Roessler Copyright (C) 1998-2005 Werner Koch Copyright (C) 1999-2014 Brendan Cully Copyright (C) 1999-2002 Tommi Komulainen Copyright (C) 2000-2004 Edmund Grimley Evans Copyright (C) 2006-2009 Rocco Rutte Copyright (C) 2014-2016 Kevin J. McCarthy 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. neomutt-neomutt-20171215/ChangeLog.md000066400000000000000000001434751321473123000173250ustar00rootroot000000000000002017-12-15 Richard Russon * Bug Fixes - Fix some regressions in the previous release 2017-12-08 Richard Russon * Features - Enhance ifdef feature to support my_ vars - Add - Remove vim syntax file from the main repo - Support reading FQDN from mailname files * Bug Fixes - Do not turn CRLF into LF when dealing with transfer-encoding=base64 - Cleanup "SSL is unavailable" error in mutt_conn_find - Don't clear the macro buffer during startup - Fixup smart modify-labels-then-hide for !tag case - Add sleep after SMTP error - Restore folder settings after folder-hook - Fix segfault when pipe'ing a deleted message * Docs - Display_filter escape sequence - Correct spelling mistakes - Add a sentence to quasi-delete docs - Modify gpg.rc to accommodate GPG 2.1 changes * Build - Fix build for RHEL6 - Define NCURSES_WIDECHAR to require wide-char support from ncurses - Autosetup: fix check for missing sendmail - Respect --with-ssl path - Check that OpenSSL md5 supports -r before using it - Autosetup: expand --everything in `neomutt -v` - Make sure objects are not compiled before git_ver.h is generated - Build: fix update-po target - Fix out-of-tree builds - Fix stdout + stderr redirection in hcachever.sh - Build: moved the check for idn before the check for notmuch - Define prefix in Makefile.autosetup - Install stuff to $(PACKAGE) in $(libexecdir), not $(libdir) - Update autosetup to latest master * Code - Rename files - Rename functions - Rename variables - Rename constants - Remove unused parameters - Document functions - Rearrange functions - Move functions to libraries - Add new library functions - Rearrange switch statements - Boolification - Drop #ifdef DEBUG - Fix Coverity defects - Insert braces - Split ifs - Fallthrough - Fix shadow variable - Replace mutt_debug with a macro - Return early where possible * Upstream - Note which ssl config vars are GnuTLS or OpenSSL only - Add message count to $move quadoption prompt - Add %R (number of read messages) for $status_format - Add $change_folder_next option to control mailbox suggestion order - Fix $smart_wrap to not be disabled by whitespace-prefixed lines - Remove useless else branch in the $smart_wrap code - Fix ansi escape sequences with both reset and color parameters 2017-10-27 Richard Russon * Bug Fixes - variable type when using fread - prevent timezone overflow - tags: Show fake header for all backends - notmuch: virtual-mailboxes should accept a limit - Issue 888: Fix imap mailbox flag logging - fix actions on tagged messages - call the folder-hook before saving to $record - Fix smart wrap in pager without breaking header - Add polling for the IDLE command * Docs - imap/notmuch tags: Add some documentation - English and other cleanups - compressed and nntp features are now always built * Website - Update Arch instructions * Build - Fix update-po - Fix neomutt.pot location, remove from git - Allow to specify --docdir at configure time - Generate neomuttrc even if configured with --disable-doc - Let autosetup define PWD, do not unnecessarily try to create hcache dir - Use bundled wcscasecmp if an implementation is not found in libc - Use host compiler to build the documentation - Update autosetup to latest master branch - autosetup: delete makedoc on 'make clean' - Fixes for endianness detection - Update autosetup to latest master branch - Do not use CPPFLAGS / CFLAGS together with CC_FOR_BUILD - --enable-everything includes lua - autosetup: check for sys_siglist[] * Code - move functions to library - lib: move MIN/MAX macros - simplify null checks - kill preproc expansion laziness - reduce scope of variables - merge: minor code cleanups - split up 'if' statements that assign and test - Refactor: Remove unused return type - Bool: change functions in mx.h - bool: convert function parameters in nntp.h - add extra checks to mutt_pattern_exec() - Use safe_calloc to initialize memory, simplify size_t overflow check - Move mutt_rename_file to lib/file.[hc] - doxygen: fix a few warnings - minor code fixes - use mutt_array_size() - refactor out O_NOFOLLOW - initialise variables - lib: move List and Queue into library - url: make notmuch query string parser generic - Wrap dirname(3) inside a mutt_dirname() function 2017-10-13 Richard Russon * Bug Fixes - crash using uncolor - Sort the folders list when browsing an IMAP server - Prefer a helpful error message over a BEEP * Build - Do not fail if deflate is not in libz - Support EXTRA_CFLAGS and EXTRA_LDFLAGS, kill unused variable 2017-10-06 Richard Russon * Features - Add IMAP keywords support * Bug Fixes - set mbox_type - %{fmt} date format - Fix off-by-one buffer overflow in add_index_color - crash in mbox_to_udomain - crash in mutt_substrdup - crash looking up mime body type - digest_collapse was broken - crash using notmuch expando with imap - imap: Fix mx.mbox leak in imap_get_parent_path - overflow in mutt_mktime() - add more range-checking on dates/times - Remove spurious error message - Unsubscribe after deleting an imap folder - Do not pop from MuttrcStack what wasn't pushed * Docs - replace mutt refs with neomutt - drop old vim syntax file * Code - convert functions to use 'bool' - convert structs to use STAILQ * Build - Autosetup-based configuration - drop upstream mutt references - rename everything 'mutt' to 'neomutt' - move helper programs to lib dir - rename regexp to regex - expand buffers to avoid gcc7 warnings * Upstream - Remove \Seen flag setting for imap trash - Change imap copy/save and trash to sync flags, excluding deleted - Improve imap fetch handler to accept an initial UID - Display an error message when delete mailbox fails - Updated French translation - Fix imap sync segfault due to inactive headers during an expunge - Close the imap socket for the selected mailbox on error - Add missing IMAP_CMD_POLL flag in imap buffy check - Change maildir and mh check_mailbox to use dynamic sized hash - Fix uses of context->changed as a counter - Make cmd_parse_fetch() more precise about setting reopen/check flags - Enable $reply_self for group-reply, even with $metoo unset 2017-09-12 Richard Russon * Bug Fixes - broken check on resend message - crash in vfolder-from-query * Build - Be more formal about quoting in m4 macros - fix warnings raised by gcc7 - notmuch: add support for the v5 API 2017-09-07 Richard Russon * Contrib - Add guix build support * Bug Fixes - Only match real mailboxes when looking for new mail - Fix the printing of ncurses version in -v output - Bind editor \ to delete-char - Fix overflowing colours - Fix empty In-Reply-To generation - Trim trailing slash from completed dirs - Add guix-neomutt.scm - Fix setting custom query_type in notmuch query * Website - New technical documentation LINK - Improve Gentoo distro page * Build - Better curses identification - Use the system's wchar_t support - Use the system's md5 tool (or equivalent) - Clean up configure.ac - Teach gen-map-doc about the new opcode header * Source - Rename functions (snake_case) - Rename constants/defines (UPPER_CASE) - Create library of shared functions - Much tidying - Rename globals to match user config - Drop unnecessary functions/macros - Use a standard list implementation - Coverity fixes - Use explicit NUL for string terminators - Drop OPS\* in favour of opcodes.h * Upstream - Fix menu color calls to occur before positioning the cursor - When guessing an attachment type, don't allow text/plain if there is a null character - Add $imap_poll_timeout to allow mailbox polling to time out - Handle error if REGCOMP in pager fails when resizing - Change recvattach to allow nested encryption - Fix attachment check_traditional and extract_keys operations - Add edit-content-type helper and warning for decrypted attachments - Add option to run command to query attachment mime type - Add warning about using inline pgp with format=flowed 2017-07-14 Richard Russon * Translations - Update German translation * Docs - compile-time output: use two lists - doxygen: add config file - doxygen: tidy existing comments * Build - fix hcachever.sh script * Upstream - Fix crash when $postponed is on another server. 2017-07-07 Richard Russon * Features - Support Gmail's X-GM-RAW server-side search - Include pattern for broken threads - Allow sourcing of multiple files * Contrib - vombatidae colorscheme - zenburn colorscheme - black 256 solarized colorscheme - neonwolf colorscheme - Mutt logos * Bug Fixes - flags: update the hdr message last - gpgme S/MIME non-detached signature handling - menu: the thread tree color - Uses CurrentFolder to populate LastDir with IMAP - stabilise sidebar sort order - colour emails with a '+' in them - the padding expando '%>' - Do not set old flag if mark_old is false - maildir creation - Decode CRLF line endings to LF when copying headers - score address pattern do not match personal name - open attachments in read-only mode - Add Cc, In-Reply-To, and References to default mailto_allow - Improve search for mime.types * Translations - Update Chinese (Simplified) translation * Coverity defects - dodgy buffers - leaks in lua get/set options - some resource leaks * Docs - update credits - limitations of new-mail %f expando - escape <>'s in nested conditions - add code of conduct - fix ifdef examples - update mailmap - Update modify-labels-then-hide - fix mailmap - drop UPDATING files * Website - Changes pages (diff) - Update Arch distro page - Update NixOS distro page - Add new Exherbo distro page - Update translation hi-score table - Update code of conduct - Update Newbies page - Add page about Rebuilding the Documentation - Add page of hard problems * Build - remove unnecessary steps - drop instdoc script - move smime_keys into contrib - fixes for Solaris - don't delete non-existent files - remove another reference to devel-notes.txt - Handle native Solaris GSSAPI. - drop configure options --enable-exact-address - drop configure option --with-exec-shell - drop configure option --enable-nfs-fix - drop configure option --disable-warnings - Completely remove dotlock - More sophisticated check for BDB version + support for DB6 (non default) * Tidy - drop VirtIncoming - split mutt_parse_mailboxes into mutt_parse_unmailboxes - tidy some buffy code - tidy the version strings * Upstream - Add ~<() and ~>() immediate parent/children patterns - Add L10N comments to the GNUTLS certificate prompt - Add more description for the %S and %Z $index_format characters - Add config vars for forwarded message attribution intro/trailer - Block SIGWINCH during connect() - Improve the L10N comment about Sign as - Auto-pad translation for the GPGME key selection "verify key" headers - Enable all header fields in the compose menu to be translated - Force hard redraw after $sendmail instead of calling mutt_endwin - Make GPGME key selection behavior the same as classic-PGP - Rename 'sign as' to 'Sign as'; makes compose menu more consistent - Change the compose menu fields to be dynamically padded 2017-06-09 Richard Russon * Contrib - unbind mappings before overwriting in vim-keys * Bug Fixes - latest coverity issues (#624) - don't pass colour-codes to filters - Don't set a colour unless it's been defined. - crash if no from is set or founds - ifdef command * Translations - fix translations - fix some remaining translation problems * Docs - explain binding warnings - don't document unsupported arches * Build - fix make git_ver.h - allow xsltproc and w3m calls to fail - fix make dist * Upstream - Add a mutt_endwin() before invoking $sendmail - Restore setenv function - Fix tag-prefix to not abort on $timeout - Change km_dokey() to return -2 on a timeout/sigwinch - Enable TEXTDOMAINDIR override to make translation testing easier - Fix "format string is not a string literal" warnings 2017-06-02 Richard Russon * Features - Warn on bindkey aliasing - Drop PATCHES, tidy 'mutt -v' output - Add %z format strings to index_format - Add debug_level/debug_file options * Bug Fixes - Fix nntp group selection - Fix status color - Tidy up S/MIME contrib - Do not try to create Maildir if it is an NNTP URI - Fix missing NONULL for mutt.set() in Lua * Translations - Fix German PGP shortkeys * Docs - Remove feature muttrc files - Merge README.notmuch into manual - Remove unneded scripts - Remove README.SECURITY - Remove BEWARE and devel-notes.txt - Update Makefiles - Delete TODO files - Remove legacy files - Don't generate vim-neomutt syntax file - Remove LaTeX/pdf manual generation - Add missing docs for expandos - Fix sidebar howto examples - Remove some upstream references - Drop refs to patches - Improve PR template and CONTRIBUTING.md * Website - Fix list items in newbie-tutorial's Mailing List Guidelines - Remove configure options that no longer exist - fix newbie tutorial - document signing tags / releases - config: drop unused paginate command - script: split tests up into several - convert credits page to markdown - simpify 404 page - improve newbie tutorial - remove help.html and integrate its content elsewhere - make: "graphviz" program is needed for generating diagram - improve getting started guide // include legacy files - dev: add list of architectures/operating systems - numerous small fixes * Build - Remove typedefs and rename ~130 structs - Add separate hcache dir - Move crypto files to ncrypt dir - Split up mutt.h, protos.h - Always build: sidebar, imap, pop, smtp, compressed, nntp - Remove --enable-mailtool configure option - Make dotlock optional - Change gpgme requirement back to 1.1.0 - Remove check_sec.sh - Fix safe_calloc args - Remove unused macros - Remove unused option: SmimeSignOpaqueCommand - Move configure-generated files - Update distcheck build flags - Drop obsolete iconv check - Unused prototypes - unsupported systems - Drop many configure tests for things defined in POSIX:2001 - Kill useless crypthash.h file - Run clang-format on the code - Fail early if ncursesw cannot be found - Add names prototype arguments - Abbreviate pointer tests against NULL - Initialise pointers to NULL - Reduce the scope of for loop variables - Coverity: fix defects * Upstream - Convert all exec calls to use mutt_envlist(), remove setenv function - Note that mbox-hooks are dependent on $move - Refresh header color when updating label - Remove glibc-specific execvpe() call in sendlib.c - Add color commands for the compose menu headers and security status - Fix sidebar count updates when closing mailbox - Don't modify LastFolder/CurrentFolder upon aborting a change folder operation - Change message modifying operations to additively set redraw flags - Improve maildir and mh to report flag changes in mx_check_mailbox() - Add $header_color_partial to allow partial coloring of headers - Rename REDRAW_SIGWINCH to REDRAW_FLOW - Create R_PAGER_FLOW config variable flag - Turn IMAP_EXPUNGE_EXPECTED back off when syncing - Add $history_remove_dups option to remove dups from history ring - Also remove duplicates from the history file - Don't filter new entries when compacting history save file - Move the IMAP msn field to IMAP_HEADER_DATA - Fix imap expunge to match msn and fix index - Fix cmd_parse_fetch() to match against MSN - Start fixing imap_read_headers() to account for MSN gaps - Add msn_index and max_msn to find and check boundaries by MSN - Properly adjust fetch ranges when handling new mail - Small imap fetch fixes - Don't abort header cache evaluation when there is a hole - Fix mfc overflow check and uninitialized variable - Fix potential segv if mx_open_mailbox is passed an empty string - Don't clean up idata when closing an open-append mailbox - Don't clean up msn idata when closing an open-append mailbox - Fix memory leak when closing mailbox and using the sidebar - Change imap body cache cleanup to use the uid_hash - Convert classic s/mime to space delimit findKeys output - Add self-encrypt options for PGP and S/MIME - Change $postpone_encrypt to use self-encrypt variables first - Automatic post-release commit for mutt-1.8.3 - Add note about message scoring and thread patterns 2017-04-28 Richard Russon * Bug Fixes - Fix and simplify handling of GPGME in configure.ac (@gahr) * Docs - Fix typo in README.neomutt (@l2dy) * Upstream - Fix km_error_key() infinite loop and unget buffer pollution - Fix error message when opening a mailbox with no read permission 2017-04-21 Richard Russon * Features - add lua scripting - add command-line batch mode - index_format: add support of %K * Bug Fixes - attachment/pager: Use mailcap for test/* except plain - Fix uncollapse_new in pager - fix garbage in chdir prompt due to unescaped string - Fix inbox-first functionality when using mutt_pretty_mailbox - add full neomutt version to log startup - fix bug in uncolor for notmuch tag - fix broken from_chars behaviour * Coverity defects - strfcpy - add variable - function arg could be NULL/invalid - add variable - failed function leads to invalid variable - add variable - Context could become NULL - add variable - alloc/strdup could return NULL - add variable - route through code leads to invalid variable - remove variable test - test functions - tidy switches - unused variables - refactor only - check for buffer underruns - fix leaks - minor fixes - bug: add missing break - bug: don't pass large object by value - fix: use correct buffer size - shadow variables - 0 -> NULL * Docs - many minor updates - sync translations - delete trailing whitespace - indent the docbook manual - use w3m as default for generating UTF8 manual.txt * Website - many minor updates - fix broken links - add to list of useful programs - test automatic html checker - remove trailing whitespace - add irc description - update issue labels (dev) - new page: closed discussions - new page: making neomutt (dev) * Build - drop obsolete m4 scripts - don't look for lua libs unless asked for - workaround slang warnings - lower the gettext requirement 0.18 -> 0.17 - add keymap_alldefs.h to BUILT_SOURCES - fix make dist distcheck - Remove -Iimap from CFLAGS and include imap/imap.h explicitly - mx: fix conditional builds - Make iconv mandatory (no more --disable-iconv) - refactor: Split out BUFFER-handling functions * Tidy - drop control characters from the source - drop vim modelines - delete trailing whitespace - mark all local functions as static - delete unused functions - replace FOREVER with while (true) - drop #if HAVE_CONFIG_H - use #ifdef for potentially missing symbols - remove #if 0 code blocks - drop commented out source - IMAP auth functions are stored by pointer cannot be static - force OPS to be rebuilt after a reconfigure - be specific about void functions - expand a few more alloc macros - add argument names to function prototypes - drop local copy of regex code - rearrange code to avoid forward declarations - limit the scope of some functions - give the compress functions a unique name - use snake_case for function names - add missing newlines to mutt_debug - remove generated files from repo - look for translations in all files - fix arguments to printf-style functions - license text - unify include-guards - tidy makefiles - initialise pointers - make strcmp-like functions clearer - unify sizeof usage - remove forward declarations - remove ()s from return - rename files hyphen to underscore - remove unused macros - use SEEK_SET, SEEK_CUR, SEEK_END - remove constant code - fix typos and grammar in the comments - Switch to using an external gettext runtime - apply clang-format to the source code - boolify returns of 84 functions - boolify lots of struct members - boolify some function parameters * Upstream - Add $ssl_verify_partial_chains option for OpenSSL - Move the OpenSSL partial chain support check inside configure.ac - Don't allow storing duplicate certs for OpenSSL interactive prompt - Prevent skipped certs from showing a second time - OpenSSL: Don't offer (a)ccept always choice for hostname mismatches - Add SNI support for OpenSSL - Add SNI support for GnuTLS - Add shortcuts for IMAP and POP mailboxes in the file browser - Change OpenSSL to use SHA-256 for cert comparison - Fix conststrings type mismatches - Pass envlist to filter children too - Fix mutt_envlist_set() for the case that envlist is null - Fix setenv overwriting to not truncate the envlist - Fix (un)sidebar_whitelist to expand paths - Fix mutt_refresh() pausing during macro events - Add a menu stack to track current and past menus - Change CurrentMenu to be controlled by the menu stack - Set refresh when popping the menu stack - Remove redraw parameter from crypt send_menus - Don't full redraw the index when handling a command from the pager - Filter other directional markers that corrupt the screen - Remove the OPTFORCEREDRAW options - Remove SidebarNeedsRedraw - Change reflow_windows() to set full redraw - Create R_MENU redraw option - Remove refresh parameter from mutt_enter_fname() - Remove redraw flag setting after mutt_endwin() - Change km_dokey() to pass SigWinch on for the MENU_EDITOR - Separate out the compose menu redrawing - Separate out the index menu redrawing - Prepare for pager redraw separation - Separate out the pager menu redrawing - Don't create query menu until after initial prompt - Silence imap progress messages for pipe-message - Ensure mutt stays in endwin during calls to pipe_msg() - Fix memleak when attaching files - Add $ssl_verify_partial_chains option for OpenSSL - Move the OpenSSL partial chain support check inside configureac - Don't allow storing duplicate certs for OpenSSL interactive prompt - Prevent skipped certs from showing a second time - OpenSSL: Don't offer (a)ccept always choice for hostname mismatches - Add SNI support for OpenSSL - Add SNI support for GnuTLS - Add shortcuts for IMAP and POP mailboxes in the file browser - Updated French translation - Change OpenSSL to use SHA-256 for cert comparison - Fix conststrings type mismatches - Pass envlist to filter children too - Fix mutt_envlist_set() for the case that envlist is null - Fix setenv overwriting to not truncate the envlist - Fix mutt_refresh() pausing during macro events - Add a menu stack to track current and past menus - Change CurrentMenu to be controlled by the menu stack - Set refresh when popping the menu stack - Remove redraw parameter from crypt send_menus - Don't full redraw the index when handling a command from the pager - Fix (un)sidebar_whitelist to expand paths - Filter other directional markers that corrupt the screen - Remove the OPTFORCEREDRAW options - Remove SidebarNeedsRedraw - Change reflow_windows() to set full redraw - Create R_MENU redraw option - Remove refresh parameter from mutt_enter_fname() - Remove redraw flag setting after mutt_endwin() - Change km_dokey() to pass SigWinch on for the MENU_EDITOR - Separate out the compose menu redrawing - Separate out the index menu redrawing - Prepare for pager redraw separation - Separate out the pager menu redrawing - Don't create query menu until after initial prompt - Silence imap progress messages for pipe-message - Ensure mutt stays in endwin during calls to pipe_msg() - Fix memleak when attaching files - automatic post-release commit for mutt-181 - Added tag mutt-1-8-1-rel for changeset f44974c10990 - mutt-181 signed - Add ifdefs around new mutt_resize_screen calls - Add multiline and sigwinch handling to mutt_multi_choice - Set pager's REDRAW_SIGWINCH when reflowing windows - Add multiline and sigwinch handling to mutt_yesorno - Change the sort prompt to use (s)ort style prompts - Handle the pager sort prompt inside the pager - Fix GPG_TTY to be added to envlist - automatic post-release commit for mutt-182 2017-03-06 Richard Russon * Bug Fixes - Get the correct buffer size under fmemopen/torify (#441) - Use static inlines to make gcc 4.2.1 happy - getdnsdomainname: cancel getaddrinfo_a if needed - imap: remove useless code (#434) (origin/master) - Fixes missing semi-colon compilation issue (#433) * Docs - github: added template for Pull Requests, issues and a CONTRIBUTION.md (#339) - editorconfig: support for new files, fix whitespace (#439) - add blocking fmemopen bug on debian to manual (#422) * Upstream - Increase ACCOUNT.pass field size. (closes #3921) - SSL: Fix memory leak in subject alternative name code. (closes #3920) - Prevent segv if open-appending to an mbox fails. (closes #3918) - Clear out extraneous errors before SSL_connect() (see #3916) 2017-02-25 Richard Russon * Features - Add option $show_multipart_alternative - notmuch: Allow to use untransformed tag for color - Use getaddrinfo_a if possible (#420) * Bug Fixes - handle sigint within socket operations (#411) - Avoid browsing the remote spoolfile by setting MUTT_SELECT_MULTI attach - notmuch: fix crash when completing tags (#395) - Fixes missing failure return of notmuch msg open (#401) - Fix latest Coverity issues (#387) - Advance by the correct number of position even for unknown characters (#368) - Release KyotoCabinet data with kcfree() (#384) - 22 resource leaks * Translations - Update translations - Update the german translation (#397) * Docs - fix typo in notmuch example - remove duplicate "default" in the sidebar intro - fix confusing description of notmuch operators (#371) - correct spelling mistakes (#412) * Website - link to clang-format config in main repo (#28) - updated list of useful programs - update/improve list of useful programs - sidebar_format has a single default value - fix name of GNU Guix - added guix distro - added link to new afew maintainers - add code of conduct - add mutt-addressbook to useful - remove unnecessary unicode non-breaking spaces - github merging * Build - Enable and run unit-tests on the feature/unit-test branch - add notmuch to default, feature - new dbs for mutt - master is now the main branch - streamline builds - fix doc generator - add a few includes (prelude to clang-format) - slcurses.h defines its own bool type - travis: use container build - add clang-format file - Remove ugly macros and casts from crypt_gpgme.c - fix minor reflow issues in some comments - editorconfig: use spaces to indent in *.[ch] files - added comment-blocks for clang-format to ignore - fix 80 column limit, align statements - Remove snprintf.c from EXTRA_DIST (#406) - Kill homebrew (v)snprintf implementations, as they are C99 (#402) - Display charset + small refactoring - Do not cast or check returns from safe_calloc (#396) - refactor: create a generic base64 encode/decode - debug: remove dprint in favor of mutt_debug (#375) - Fix dubious use macro for _() / gettext() (#376) - Use mutt_buffer_init instead of memset - Make the heap method and datatype a plain list - Reverts making AliasFile into a list_t (#379) - Turn mutt_new_* macros into inline functions - Do not cast return values from malloc (et similia) * Upstream - Simplify mutt_label_complete(). - Permit tab completion of pattern expressions with ~y (labels). - Fix the mutt_label_complete() pos parameter. - Fix the x-label update code check location. - Improve the label completion hash table usage. - Adds label completion. - Add hash_find_elem to get the hash element. - Minor fixes to the x-label patch from David. - Adds capability to edit x-labels inside mutt, and to sort by label. - Allow "unsubjectrc *" to remove all patterns. - Add subjectrx command to replace matching subjects with something else. - Abstract the SPAM_LIST as a generic REPLACE_LIST - Improve Reply-to vs From comparison when replying. (closes #3909) - Fix sidebar references to the "new count" to be "unread". (closes #3908) - Fix several alias hashtable issues. - Add casecmp and strdup_key flags to hash_create() - Improve error handling in mbox magic detection. - Allow initial blank lines in local mailboxes. - Fix minor documentation issues. - Convert cmd_parse_search to use the uid hash. (closes #3905) - Create a uid hash for imap. (see #3905) - Convert HASH to be indexable by unsigned int. (see #3905) - Fix imap server-side search to call uid2msgno() only once. (see #3905) - Add a pattern_cache_t to speed up a few repeated matches. - Canonicalize line endings for GPGME S/MIME encryption. (closes #3904) - Fix build for bdb. - Create function to free header cache data. - Add Kyoto Cabinet support to the header cache. - Prevent null pointer exception for h->ai_canonname - Show SHA1 fp in interactive cert check menu. - Fix potential cert memory leak in check_certificate_by_digest(). - Plug memory leak in weed-expired-certs code. - Filter expired local certs for OpenSSL verification. - Change "allow_dups" into a flag at hash creation. 2017-02-06 Richard Russon * Bug Fixes - Unicode 0x202F is a non-break space too (#358) (@gahr) - improve readability of find_subject() (@toogley) - Import hcache-lmdb fixes from upstream (#363) (@gahr) - Rework the "inbox-first" implementation to make code self-explanatory (#356) (@gahr) - If possible, only redraw after gpgme has invoked pinentry (#352) (@gahr) - Remove two use-after free in global hooks (#353) (@guiniol) - Handle BAD as IMAP_AUTH_UNAVAIL (#351) (@gahr) - Do not crash when closing a non-opened mailbox (origin/requests/github/343) (@gahr) - Import hcache benchmark (@gahr) - fix: bug introduced by mkdir changes (#350) - change pager to allow timehook-hook to fire * Docs - Update documentation about modify-labels-then-hide (@bbenne10) 2017-01-28 Richard Russon * Features - Add option for missing subject replacement - notmuch: Allow to toggle labels - Support for aborting mailbox loading - Do a buffy check after shell escape - Support of relative paths sourcing and cyclic source detection - Support of multiple config files as CLI arguments - Extend the ~m pattern to allow relative ranges - Implement SASL's PLAIN mechanism as a standalone authenticator - Add support for sensitive config options - Searching with a window over notmuch vfolders * Contrib - fix vim syntax file for index-color commands - add .editorconfig * Bug Fixes - fix global hooks to not take a pattern - Avoid breaking relative paths when avoiding cyclic checks on - Fix sorting when using '/' as a namespace separator * Docs - Added waffle badges to readme - Describe the new message ranges - add documentation for -DS command line switch - fix typos in section on config locations - remove reference to missing keybinding - fix docbook validation * Build - Start migrating to stdbool logic - add recursive mkdir() - reformat the source to mutt standards - appease check_sec.sh 2017-01-13 Richard Russon * Features - Allow custom status flags in index_format - $from_chars highlights differences in authorship - notmuch: make 'Folder' and 'Tags' respect (un)ignore - notmuch: add "virtual-unmailboxes" command * Bug Fixes - pick smarter default for $sidebar_divider_char - status color breaks "mutt -D" - Enable reconstruct-thread in the pager - manually touch 'atime' when reading a mbox file - allow $to_chars to contain Unicode characters - increase the max lmdb database size - restore limit current thread - don't reset the alarm unless we set it - some more places that may get NULL pointers - rework initials to allow unicode characters * Translations - Spanish translation - German translation * Docs - Improve whitespace and grammar on the NNTP feature page - make $to_chars docs more legible - de-tab the DocBook - fix 301 redirects * Build - New configure option --enable-everything - add a constant for an aborted question - enhance mutt_to_base64() (and callers) - Fix configure.ac to require md5 if hcache is enabled - Bail if a selected hcache backend cannot be found - refactor mutt_matches_ignore - fix hcache + make dist - add unicode string helper function - Re-indent configure.ac - generate devel version suffix - fix check_sec.sh warnings - remove unnecessary #ifdef's - add missing #ifdef for nntp - ignore some configure temp files - fix "make dist" target - fix function prototypes - fix coverity warnings - notmuch: drop strndup, replace with mutt_substrdup * Upstream - Fix failure with GPGME 1.8: do not steal the gpgme_ prefix. - search muttrc file according to XDG Base Specification (closes #3207) - Improve openssl interactive_check_cert. (closes #3899) - Add mutt_array_size macro, change interactive_check_cert() to use it. (see #3899) - Return to pager upon aborting a jump operation. (closes #3901) - Change sidebar_spoolfile coloring to be lower precedence. - Move '@' pattern modifier documentation to the right section. - Add setenv/unsetenv commands. - Rework OpenSSL certificate verification to support alternative chains. (closes #3903) - Add option to control whether threads uncollapse when new mail arrives. - In the manual, replaced 2 para by example (similar to the first example). - Create MbTable type for multibyte character arrays. (see #3024) - Make to_chars and status_chars accept mulitibyte characters. (closes #3024) 2016-11-26 Richard Russon * Features - Upstream adoption of compress - Multiple hcache backends and run-time selection - $forward_references includes References: header on forwards - Hooks: define hooks for startup and shutdown - Add $collapse_all to close threads automatically * Bug Fixes - Index in pager crash - Tag with multiple labels - Make sure gdbm's symbols are not resolved in QDBM's compatibility layer - Fix crash when doing collapse_all on an empty folder - Fix: crash when browsing empty dir - Initialize imap_authenticate's return value to something meaningful * Translations - Update German translation - Update Slovak translation - Update French translation - Add English (British) translation - Convert files to utf-8 - Mass tidy up of the translation messages * Docs - new-mail bug is fixed - add since date for features - expand example command options for compress - fix entries for beep and new-mail-command - add a version number to the generated vimrc - fix links in README - don't use smart quotes in manual examples - and \e means refers to both alt and escape key * Build - Travis: test messages - Add option to disable translation messages - Split hcache code into per-backend files - Doc/Makefile clean neomutt-syntax.vim - Improve discovery for the Berkeley Database - Fix nntp/notmuch conditionals - Implement mutt_strchrnul() - Rename vim-keybindings to vim-keys * Upstream - attach_format: add new %F placeholder - Compose: add operation to rename an attachment - Chain %d->%F->%f in the attachment menu - Move mbox close-append logic inside mbox_close_mailbox() - When $flag_safe is set, flagged messages cannot be deleted - Adds the '@' pattern modifier to limit matches to known aliases - Adds binding to create "hotkeys" for messages - Updated requirement on the C compiler - Fix mark-message translation and keybind menu - More openssl1.1 fixes: remove uses of X509->name in debugging. (closes #3870) - Don't close stderr when opening a tunnel. (closes #3726) - Minor resource and error logic cleanup in tunnel_socket_open() - Make sure that the output of X509_NAME_oneline is null-terminated 2016-11-04 Richard Russon * Bug Fixes - don't crash when the imap connection dies * Upstream - Add root-message function to jump to root message in thread. - Updated French translation. - Prevent an integer overflow in mutt_mktime() (closes #3880) - Fix pager segfault when lineInfo.chunks overflows. (closes #3888) - Perform charset conversion on text attachments when piping. (closes #3773) (see #3886) - Add a --disable-doc configuration option. - Make ncurses and ncursesw header checking the same. - Attempt to silence a clang range warning. (closes #3891) - Fixed issue from changeset 4da647a80c55. (closes #3892) - Define PATH_MAX, it's missing on the GNU Hurd. (closes #3815) 2016-10-28 Richard Russon * Features - nntp: use safe_{fopen,fclose} - nntp: fix resource leak - forgotten-attachment: Ignore lines matching quote_regexp. - forgotten-attachment: Fix checking logic. - forgotten-attachment: Update docs regarding $quote_regexp. - notmuch: Add a fake "Folder" header to viewed emails - sidebar: consider description when using whitelist - skip-quoted: skip to body * Bug Fixes - sensible-browser/notmuch changing mailbox - "inbox" sorting function - overhaul the index/pager updates - crash in hdrline - remove stray line introduced by pager fix - Possible fix for random pager crashes. * Docs - use a more expressive coverity scan badge - light tidying * Build - replace the ugly strfcpy() macro with a function - build: Look for tgetent in ncurses, fallback to tinfo only if not found - build: fix a couple of build warnings - travis: install doc dependencies - build: fix install/dist/distcheck targets * Upstream - Fix POP3 SASL authentication mechanism DIGEST-MD5. (closes #3862) - Add a few explanatory comments to pop_auth_sasl(). (see #3862) - Fix GPGME signature zero timestamp and locale awareness issues. (closes #3882) - Handle presence of '--' delimiter in $sendmail. (closes #3168) - Allow IPv6 literal addresses in URLs. (closes #3681) - Fix gpgme segfault in create_recipient_set(). - Use mutt_strlen and mutt_strncmp in sidebar.c. - Change sidebar to only match $folder prefix on a $sidebar_divider_char. (closes #3887) - Actually fix gpgme segfault in create_recipient_set(). 2016-10-14 Richard Russon * Features - sidebar: Make sure INBOX appears first in the list. - notmuch: Synchronise tags to flags * Bug Fixes - updates when pager is open - crash when neither $spoolfile, $folder are set - forgotten-attachment: fix empty regex expression - status-color when pager_index_lines > 0 - buffer underrun when no menu item is selected - crash handling keywords/labels * Docs - update notmuch references * Build - update references to 1.7.1 - strfcpy() improvement * Upstream - automatic post-release commit for mutt-1.7.1 - Mark IMAP fast-trash'ed messages as read before copying. (see #3860) - Updated Czech translation. - Preserve forwarded attachment names in d_filename. 2016-10-03 Richard Russon * Build - Fix install and dist targets 2016-10-02 Richard Russon * Features - Kyoto Cabinet header cache - Compose to Sender - Forgotten Attachment uses a regex - Optimize LMDB's hcache backend - Sensible-browser behaviour fixes * Bug Fixes - Fixes repaint problem with $pager_index_lines #159 - Quasi-Delete: check there's a selection - Bulletproof the pager - Typo in the version string * Docs - Add badges to README.neomutt - Document the Kyoto cabinet hcache backend - Fix the layout of the syntax file - Make the license clear to github - Fix the alignment in a 'nested-if' example - Fix notmuch vim syntax file - Added Mailinglist mailto links to "Where is NeoMutt" section - Fix build of neomutt-syntax.vim - Fixed typo of devel mailinglist name * Build - Travis: install the kyoto-cabinet dev files - Build source before docs - Build fix for strndup / malloc - Change gcc build options to prevent crashes * Upstream - Ensure signatures exist when verifying multipart/signed emails. (closes #3881). - RFC2047-decode mailto url headers after RFC2822 parsing. (closes #3879) - RFC2047-decode mailto header values. (closes #3879) - Reset invalid parsed received dates to 0. (closes #3878) - Clear pager position when toggling headers. - Don't abort the menu editor on sigwinch. (closes #3875) - Mark some gpgme pgp menu keybinding translations as fuzzy. (closes #3874) - Check for NULL mx_ops in mx.c - Use body color for gpgme output. (closes #3872) - Fix gpgme segfault when querying candidates with a '+' in the address. (closes #3873) 2016-09-16 Richard Russon * Bug Fixes - Avoid segfault when listing mailboxes on startup John Swinbank - Fix buffer overrun in search for attach keyword James McCoy - Fix off-by-one in error message Antonio Radici - fix AC_INIT tarname parameter - fix crash when exiting the pager - fix another crash in the pager - nntp: close message handles - fix: make the pager more robust - fix sidebar sort order - fix notmuch tag completion * Docs - doc: Removes bug entry in new-mail docs Santiago Torres - fix some translations in crypt_gpgme.c Antonio Radici - docs: mass tidy up * Upstream - Fix sidebar documentation a bit - Add unsidebar_whitelist command - Remove the $locale configuration variable - Add $attribution_locale configuration variable - Add missing include to send.c and edit.c - Filter out zero width no-break space (U+FEFF) - Update a confusing and obsolete comment - Moves mutt_copy_list to muttlib.c, where it belongs - Redraw screen after an SSL cert prompt - Preserve message-id and mft headers for recalled messages - Fix openssl 1.1 compilation issues 2016-09-10 Richard Russon * New Features - Colouring Attachments with Regex Guillaume Brogi - PGP Encrypt to Self Guillaume Brogi - Sensible Browser Pierre-Elliott Bécue - Reply using X-Original-To: header Pierre-Elliott Bécue - Purge Thread Darshit Shah - Forgotten attachment Darshit Shah - Add sidebar_ordinary color * Bug Fixes - align the nntp code with mutt Fabian Groffen - check for new mail while in pager when idle Stefan Assmann - Allow the user to interrupt slow IO operations Antonio Radici - keywords: check there are emails to tag - fix duplicate saved messages - flatten contrib/keybase dir to fix install - restore the pager keymapping 'i' to exit - proposed fix for clearing labels - notmuch: sync vfolder_format to folder_format * Docs - Update List of Features and Authors * Build - fix configure check for fmemopen - use fixed version strings * Upstream - Increase date buffer size for $folder_format. - Disable ~X when message scoring. - Fix pgpring reporting of DSA and Elgamal key lengths. - Stub out getdnsdomainname() unless HAVE_GETADDRINFO. - Autoconf: always check for getaddrinfo(). - Add missing sidebar contrib sample files to dist tarball. 2016-08-27 Richard Russon * NeoMutt for Mutt 1.7.0 * Build - Disable fmemopen until bug is fixed * Contrib - Keybase portability improvements Joshua Jordi (JakkinStewart) 2016-08-21 Richard Russon * Contrib - Updates to Keybase Support Joshua Jordi (JakkinStewart) * Bug Fixes - Fix data-loss when appending a compressed file - Don't paint invisible progress bars - Revert to Mutt keybindings - Don't de-tag emails after labelling them - Don't whine if getrandom() fails Adam Borowski (kilobyte) - Fix display when 'from' field is invalid * Config - Support for $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS Marco Hinz (mhinz) * Docs - Fix DocBook validation - Document Notmuch queries * Build - More Autoconf improvements Darshit Shah (darnir) - Create Distribution Tarballs with autogen sources Darshit Shah (darnir) 2016-08-08 Richard Russon * New Features - Timeout Hook - Run a command periodically - Multiple fcc - Save multiple copies of outgoing mail * Contrib - Keybase Integration Joshua Jordi (JakkinStewart) * Devel - Attached - Prevent missing attachments Darshit Shah (darnir) - Virtual Unmailboxes - Remove unwanted virtual mailboxes Richard Russon (flatcap) * Bug Fixes - Sidebar's inbox occasionally shows zero/wrong value - Fix crash opening a second compressed mailbox * Config - Look for /etc/neomuttrc and ~/.neomuttrc * Docs - Fix broken links, typos - Update project link - Fix version string in the manual * Build - Add option to disable fmemopen - Install all the READMEs and contribs - Big overhaul of the build Darshit Shah (darnir) 2016-07-23 Richard Russon * New Motto: "Teaching an Old Dog New Tricks" - Thanks to Alok Singh * New Features - New Mail Command - Execute a command on receipt of new mail - vim-keys - Mutt config for vim users - LMDB: In-memory header caching database - SMIME Encrypt to Self - Secure storage of sensitive email * Bug Fixes - rework mutt_draw_statusline() - fix cursor position after sidebar redraw - Add sidebar_format flag '%n' to display 'N' on new mail. - fix index_format truncation problem - Fix compiler warnings due to always true condition - Change sidebar next/prev-new to look at buffy->new too. - Change the default for sidebar_format to use %n. - sidebar "unsorted" order to match Buffy list order. - Include ncurses tinfo library if found. - Sidebar width problem - sidebar crash for non-existent mailbox - Temporary compatibility workaround - Reset buffy->new for the current mailbox in IMAP. - version.sh regression - crash when notmuch tries to read a message - status line wrapping * Docs - Mass tidy up of the docs - Fix xml validation - Add missing docs for new features * Travis - New build system: https://github.com/neomutt/travis-build Now we have central control over what gets built 2016-07-09 Richard Russon * Bug-fixes - This release was a temporary measure 2016-06-11 Richard Russon * Change in behaviour - Temporarily disable $sidebar_refresh_time Unfortunately, this was causing too many problems. It will be fixed and re-enabled as soon as possible. * Bug Fixes - Fix several crashes, on startup, in Keywords - Reflow text now works as it should - Lots of typos fixed - Compress config bug prevented it working - Some minor bug-fixes from mutt/default - Single quote at line beginning misinterpreted by groff - Setting $sidebar_width to more than 128 would cause bad things to happen. - Fix alignment in the compose menu. - Fix sidebar buffy stats updating on mailbox close. * Build Changes - Sync whitespace to mutt/default - Alter ChangeLog date format to simplify Makefiles - Use the new notmuch functions that return a status - Rename sidebar functions sb_* -> mutt_sb_* 2016-05-23 Richard Russon * New Features: - Keywords: Email Label/Keywords/Tagging - Compress: Compressed mailboxes support - NNTP: Talk to a usenet news server - Separate mappings for and - New configure option: --enable-quick-build - Various build fixes 2016-05-02 Richard Russon * Update for Mutt-1.6.0 * Bug Fixes: - Build for Notmuch works if Sidebar is disabled - Sidebar functions work even if the Sidebar is hidden - sidebar-next-new, etc, only find *new* mail, as documented - Notmuch supports *very* long queries 2016-04-16 Richard Russon * Big Bugfix Release * Bug Fixes: - Fix crash caused by sidebar_folder_indent - Allow the user to change mailboxes again - Correct sidebar's messages counts - Only sort the sidebar if we're asked to - Fix refresh of pager when toggling the sidebar - Compose mode: make messages respect the TITLE_FMT - Conditional include if sys/syscall.h - Build fix for old compilers - Try harder to keep track of the open mailbox * Changes to Features - Allow sidebar_divider_char to be longer (it was limited to one character) - Ignore case when sorting the sidebar alphabetically * Other Changes - Numerous small tweaks to the docs - Lots of minor code tidy-ups - Enabling Notmuch now forcibly enables Sidebar (it is dependent on it, for now) - A couple of bug fixes from mutt/stable 2016-04-04 Richard Russon * Update for Mutt-1.6.0 * No other changes in this release 2016-03-28 Richard Russon * New Features - skip-quoted - skip quoted text - limit-current-thread - limit index view to current thread * Sidebar Intro - A Gentle Introduction to the Sidebar (with pictures). 2016-03-20 Richard Russon * Numerous small bugfixes * TravisCI integration 2016-03-17 Richard Russon * New Features - notmuch - email search support - ifdef - improvements 2016-03-07 Richard Russon * First NeoMutt release * List of Features: - bug-fixes - various bug fixes - cond-date - use rules to choose date format - fmemopen - use memory buffers instead of files - ifdef - conditional config options - index-color - theme the email index - initials - expando for author's initials - nested-if - allow deeply nested conditions - progress - show a visual progress bar - quasi-delete - mark emails to be hidden - sidebar - overview of mailboxes - status-color - theming the status bar - tls-sni - negotiate for a certificate - trash - move 'deleted' emails to a trash bin neomutt-neomutt-20171215/INSTALL000066400000000000000000000144101321473123000161670ustar00rootroot00000000000000Notes ===== - A C99 compiler (such as GCC) is required. - You must also have a SysV compatible curses library, or you must install either GNU ncurses, ftp://prep.ai.mit.edu/pub/gnu/ or S-Lang, ftp://space.mit.edu/pub/davis/slang/ - Mutt needs an implementation of the iconv API for character set conversions. A free one can be found under the following URL: http://www.gnu.org/software/libiconv/ - For building the manual, mutt needs the DocBook XSL stylesheets as well as the DocBook DTD as of version 4.2 installed locally. Installation ============ Installing Mutt is rather painless through the use of the GNU autoconf package. Simply untar the Mutt distribution, and run the ``configure'' script. If you have obtained the distribution from the Mercurial repository, run the ``prepare'' script with the same command line parameters you would pass to configure. It will set up mutt's build environment and add the files which are present in the tar balls, but not in the Mercurial repository. In most cases, configure will automatically determine everything it needs to know in order to compile. However, there are a few options to ``configure'' to help it out, or change the default behavior: --prefix=DIR install Mutt in DIR instead of /usr/local --with-curses=DIR use the curses lib in DIR/lib. If you have ncurses, ``configure'' will automatically look in /usr/include/ncurses for the include files. --with-slang[=DIR] use the S-Lang library instead of ncurses. This library seems to work better for some people because it is less picky about proper termcap entries than ncurses. It is recommended that you use at *least* version 0.99-38 with Mutt. --with-mailpath=DIR specify where the spool mailboxes are located on your system --with-homespool[=FILE] treat file in the user's home directory as the spool mailbox. Note that this is *not* the full pathname, but relative to the user's home directory. Defaults to "mailbox" if FILE is not specified. --with-gss[=PFX] Enable GSSAPI authentication to IMAP servers. This should work with both MIT and Heimdal GSSAPI implementations - others haven't been tested. Note that the Cyrus SASL library also supports GSSAPI, and may be able to encrypt your session with it - you should use SASL instead if you can. --with-ssl[=PFX] enable SSL support with IMAP and POP. SSL support requires you to have OpenSSL headers and libraries properly installed before compiling. If the OpenSSL headers and libraries are not in the default system pats you can use the optional PFX argument to define the root directory of your installation. The libraries are then expected to be found in PFX/lib and headers in PFX/include/openssl. --with-sasl[=PFX] Use the Cyrus SASL library for IMAP or POP authentication. This library provides generic support for several authentication methods, and more may be added by the system administrator without recompiling mutt. SASL may also be able to encrypt your mail session even if SSL is not available. --disable-nls This switch disables mutt's native language support. --enable-flock use flock() to lock files. --disable-fcntl by default, Mutt uses fcntl() to lock files. Over NFS this can result in poor performance on read/write. --enable-locales-fix on some systems, the result of isprint() can't be used reliably to decide which characters are printable, even if you set the LANG environment variable. If you set this option, Mutt will assume all characters in the ISO-8859-* range are printable. If you leave it unset, Mutt will attempt to use isprint() if either of the environment variables LANG, LC_ALL or LC_CTYPE is set, and will revert to the ISO-8859-* range if they aren't. If you need --enable-locales-fix then you will probably need --without-wc-funcs too. However, on a correctly configured modern system you shouldn't need either (try setting LANG, LC_ALL or LC_CTYPE instead). --without-wc-funcs by default Mutt uses the functions mbrtowc(), wctomb() and wcwidth() provided by the system, when they are available. With this option Mutt will use its own version of those functions, which should work with 8-bit display charsets, UTF-8, euc-jp or shift_jis, even if the system doesn't normally support those multibyte charsets. If you find Mutt is displaying non-ascii characters as octal escape sequences (e.g. \243), even though you have set LANG and LC_CTYPE correctly, then you might find you can solve the problem with either or both of --enable-locales-fix and --without-wc-funcs. Once ``configure'' has completed, simply type ``make install.'' Mutt should compile cleanly (without errors) and you should end up with a binary called ``mutt.'' If you get errors about undefined symbols like A_NORMAL or KEY_MIN, then you probably don't have a SysV compliant curses library. You should install either ncurses or S-Lang (see above), and then run the ``configure'' script again. Please note that "VPATH" builds currently only work with GNU make (gmake). Character set support ===================== Mutt no longer contains functions for doing character set conversion. Instead, it expects the iconv functions (iconv_open, iconv, iconv_close) to be provided. Most up-to-date systems provide these functions, often as part of the C library. If you are installing Mutt on a system which does not have them, it is recommended that you install Bruno Haible's portable libiconv library, which you can obtain from: ftp://ftp.ilog.fr/pub/Users/haible/gnu/ Even if your system does provide the iconv functions, you might want to install libiconv, as some systems provide only a very limited version of iconv. If you decide to use your system's iconv implementation, you may need to tell mutt about implementation-defined names for some character sets. Sample configuration files for various systems can be found in the directory contrib/iconv/ in this source distribution, and will be installed in the samples/iconv directory as part of mutt's documentation. In order to use these sample configuration files, just put a line like source /usr/local/doc/mutt/samples/iconv/iconv.osf1-4.0d.rc into your system's global Muttrc, which normally resides in /etc or /usr/local/etc. If you really want to, you can configure Mutt --disable-iconv, but there will then be no character set conversion. neomutt-neomutt-20171215/LICENSE.md000066400000000000000000000430011321473123000165400ustar00rootroot00000000000000GNU General Public License ========================== _Version 2, June 1991_ _Copyright © 1989, 1991 Free Software Foundation, Inc.,_ _51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA_ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ### Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: **(1)** copyright the software, and **(2)** offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION **0.** This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The “Program”, below, refers to any such program or work, and a “work based on the Program” means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term “modification”.) Each licensee is addressed as “you”. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. **1.** You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. **2.** You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: * **a)** You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. * **b)** You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. * **c)** If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. **3.** You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: * **a)** Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, * **b)** Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, * **c)** Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. **4.** You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. **5.** You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. **6.** Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. **7.** If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. **8.** If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. **9.** The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and “any later version”, you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. **10.** If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. ### NO WARRANTY **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ### How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w` and `show c` should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w` and `show c`; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a “copyright disclaimer” for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. neomutt-neomutt-20171215/Makefile.autosetup000066400000000000000000000217111321473123000206300ustar00rootroot00000000000000############################################################################### # substitutions by autosetup PACKAGE= @PACKAGE@ PACKAGE_VERSION=@PACKAGE_VERSION@ PWD= @PWD@ AR= @AR@ CC= @CC@ CC_FOR_BUILD= @CC_FOR_BUILD@ @CFLAGS_FOR_BUILD@ CPP= @CPP@ CFLAGS= @CPPFLAGS@ @CFLAGS@ -I. -I@top_srcdir@ -Wall $(EXTRA_CFLAGS) LDFLAGS= @LDFLAGS@ $(EXTRA_LDFLAGS) EXEEXT= @EXEEXT@ LIBS= @LIBS@ @if ENABLE_NLS INTLLIBS= @INTLLIBS@ @endif RANLIB= @RANLIB@ SRCDIR= @srcdir@ INSTALL= @INSTALL@ -c INSTALL_DATA= @INSTALL@ -m 644 MKDIR_P= mkdir -p RM= rm -fr DEPFILES= $(ALLOBJS:.o=.Po) BINFILES= $(NEOMUTT) @if HAVE_PGP LIBBINFILES+= $(PGPEWRAP) $(PGPRING) @endif @if HAVE_SMIME LIBBINFILES+= $(SRCDIR)/contrib/smime_keys @endif # paths prefix= @prefix@ bindir= @BINDIR@ datadir= @PKGDATADIR@ docdir= @PKGDOCDIR@ mandir= @mandir@ libdir= @libdir@ libexecdir= @libexecdir@ sysconfdir= @SYSCONFDIR@ # targets for specific subdirectories ALL_TARGETS= @ALL_TARGETS@ CLEAN_TARGETS= @CLEAN_TARGETS@ INSTALL_TARGETS= @INSTALL_TARGETS@ UNINSTALL_TARGETS= @UNINSTALL_TARGETS@ VPATH= $(SRCDIR) ALL_FILES!= (cd $(SRCDIR) && git ls-tree -r --name-only HEAD 2>/dev/null) \ | grep -v git_ver.h || true ############################################################################### # neomutt NEOMUTT= neomutt$(EXEEXT) NEOMUTTOBJS= mutt_account.o addrbook.o alias.o attach.o bcache.o body.o \ browser.o buffy.o mutt_charset.o color.o commands.o complete.o \ compose.o compress.o conststrings.o copy.o curs_lib.o \ curs_main.o edit.o editmsg.o enter.o envelope.o filter.o \ flags.o from.o group.o handler.o hdrline.o \ header.o help.o history.o hook.o init.o keymap.o main.o \ mbox.o mbyte.o menu.o mh.o muttlib.o mutt_address.o mutt_idna.o \ mutt_socket.o tags.o mx.o \ newsrc.o nntp.o pager.o parameter.o parse.o pattern.o pop.o \ pop_auth.o pop_lib.o postpone.o query.o recvattach.o recvcmd.o \ rfc1524.o rfc2047.o rfc2231.o rfc3676.o address.o \ safe_asprintf.o score.o send.o sendlib.o sidebar.o mutt_signal.o \ smtp.o sort.o state.o status.o system.o thread.o url.o \ version.o @if HAVE_WCSCASECMP @else NEOMUTTOBJS+= wcscasecmp.o @endif @if HAVE_RESIZETERM NEOMUTTOBJS+= resize.o @endif @if USE_NOTMUCH NEOMUTTOBJS+= mutt_notmuch.o @endif @if MIXMASTER NEOMUTTOBJS+= remailer.o @endif @if USE_LUA NEOMUTTOBJS+= mutt_lua.o @endif CLEANFILES+= $(NEOMUTT) $(NEOMUTTOBJS) ALLOBJS+= $(NEOMUTTOBJS) ############################################################################### # libmutt LIBMUTT= libmutt.a LIBMUTTOBJS= mutt/base64.o mutt/buffer.o mutt/charset.o mutt/date.o mutt/debug.o mutt/exit.o \ mutt/file.o mutt/hash.o mutt/list.o mutt/mapping.o mutt/mbyte.o mutt/md5.o \ mutt/memory.o mutt/message.o mutt/sha1.o mutt/signal.o mutt/string.o CLEANFILES+= $(LIBMUTT) $(LIBMUTTOBJS) MUTTLIBS+= $(LIBMUTT) ALLOBJS+= $(LIBMUTTOBJS) ############################################################################### # libncrypt LIBNCRYPT= libncrypt.a LIBNCRYPTOBJS= ncrypt/crypt.o ncrypt/crypt_mod.o ncrypt/cryptglue.o @if HAVE_GPGME LIBNCRYPTOBJS+= ncrypt/crypt_gpgme.o ncrypt/crypt_mod_pgp_gpgme.o \ ncrypt/crypt_mod_smime_gpgme.o @endif @if HAVE_PGP LIBNCRYPTOBJS+= ncrypt/crypt_mod_pgp_classic.o ncrypt/gnupgparse.o \ ncrypt/pgp.o ncrypt/pgpinvoke.o ncrypt/pgpkey.o \ ncrypt/pgplib.o ncrypt/pgpmicalg.o ncrypt/pgppacket.o @endif @if HAVE_SMIME LIBNCRYPTOBJS+= ncrypt/crypt_mod_smime_classic.o ncrypt/smime.o @endif CLEANFILES+= $(LIBNCRYPT) $(LIBNCRYPTOBJS) MUTTLIBS+= $(LIBNCRYPT) ALLOBJS+= $(LIBNCRYPTOBJS) ############################################################################### # libimap LIBIMAP= libimap.a LIBIMAPOBJS= imap/auth.o imap/auth_anon.o imap/auth_cram.o \ imap/auth_login.o imap/auth_plain.o imap/browse.o \ imap/command.o imap/imap.o imap/message.o imap/utf7.o \ imap/util.o @if USE_GSS LIBIMAPOBJS+= imap/auth_gss.o @endif @if HAVE_SASL LIBIMAPOBJS+= imap/auth_sasl.o @endif CLEANFILES+= $(LIBIMAP) $(LIBIMAPOBJS) MUTTLIBS+= $(LIBIMAP) ALLOBJS+= $(LIBIMAPOBJS) ############################################################################### # libconn LIBCONN= libconn.a LIBCONNOBJS= conn/conn_globals.o conn/getdomain.o conn/sasl_plain.o \ conn/socket.o conn/tunnel.o @if HAVE_SASL LIBCONNOBJS+= conn/sasl.o @endif @if USE_SSL_OPENSSL LIBCONNOBJS+= conn/ssl.o @endif @if USE_SSL_GNUTLS LIBCONNOBJS+= conn/ssl_gnutls.o @endif CLEANFILES+= $(LIBCONN) $(LIBCONNOBJS) MUTTLIBS+= $(LIBCONN) ALLOBJS+= $(LIBCONNOBJS) ############################################################################### # libhcache @if USE_HCACHE LIBHCACHE= libhcache.a LIBHCACHEOBJS= hcache/hcache.o CLEANFILES+= $(LIBHCACHE) $(LIBHCACHEOBJS) MUTTLIBS+= $(LIBHCACHE) ALLOBJS+= $(LIBHCACHEOBJS) @endif @if HAVE_BDB LIBHCACHEOBJS+= hcache/bdb.o @endif @if HAVE_GDBM LIBHCACHEOBJS+= hcache/gdbm.o @endif @if HAVE_KC LIBHCACHEOBJS+= hcache/kc.o @endif @if HAVE_LMDB LIBHCACHEOBJS+= hcache/lmdb.o @endif @if HAVE_QDBM LIBHCACHEOBJS+= hcache/qdbm.o @endif @if HAVE_TC LIBHCACHEOBJS+= hcache/tc.o @endif ############################################################################### # pgpewrap PGPEWRAP= pgpewrap$(EXEEXT) PGPEWRAPOBJS= pgpewrap.o CLEANFILES+= $(PGPEWRAP) $(PGPEWRAPOBJS) ALLOBJS+= $(PGPEWRAPOBJS) ############################################################################### # pgpring PGPRING= pgpring$(EXEEXT) PGPRINGOBJS= pgppubring.o CLEANFILES+= $(PGPRING) $(PGPRINGOBJS) ALLOBJS+= $(PGPRINGOBJS) ############################################################################### # generated GENERATED= git_ver.h hcache/hcversion.h CLEANFILES+= $(GENERATED) ############################################################################## # targets all: $(BINFILES) $(LIBBINFILES) $(ALL_TARGETS) # compile + dependencies .c.o: $(CC) $(CFLAGS) -MT $@ -MD -MP -MF $*.Tpo -c -o $@ $< @mv $*.Tpo $*.Po # make sure git_ver.h is built before any .o files $(ALLOBJS): git_ver.h # mutt $(NEOMUTT): $(GENERATED) $(NEOMUTTOBJS) $(MUTTLIBS) $(CC) -o $@ $(NEOMUTTOBJS) $(MUTTLIBS) $(LDFLAGS) $(LIBS) # libmutt $(LIBMUTT): $(PWD)/mutt $(LIBMUTTOBJS) $(AR) cr $@ $(LIBMUTTOBJS) $(RANLIB) $@ $(PWD)/mutt: $(MKDIR_P) $(PWD)/mutt # libncrypt $(LIBNCRYPT): $(PWD)/ncrypt $(LIBNCRYPTOBJS) $(AR) cr $@ $(LIBNCRYPTOBJS) $(RANLIB) $@ $(PWD)/ncrypt: $(MKDIR_P) $(PWD)/ncrypt # libimap $(LIBIMAP): $(PWD)/imap $(LIBIMAPOBJS) $(AR) cr $@ $(LIBIMAPOBJS) $(RANLIB) $@ $(PWD)/imap: $(MKDIR_P) $(PWD)/imap # libconn $(LIBCONN): $(PWD)/conn $(LIBCONNOBJS) $(AR) cr $@ $(LIBCONNOBJS) $(RANLIB) $@ $(PWD)/conn: $(MKDIR_P) $(PWD)/conn # libhcache hcache/hcache.o: hcache/hcversion.h $(LIBHCACHE): $(PWD)/hcache $(LIBHCACHEOBJS) $(AR) cr $@ $(LIBHCACHEOBJS) $(RANLIB) $@ $(PWD)/hcache: $(MKDIR_P) $(PWD)/hcache # pgpewrap $(PGPEWRAP): $(PGPEWRAPOBJS) $(CC) $(LDFLAGS) -o $@ $(PGPEWRAPOBJS) # pgpring $(PGPRING): $(PGPRINGOBJS) $(LIBMUTT) $(LIBNCRYPT) $(CC) $(LDFLAGS) -o $@ $(PGPRINGOBJS) $(LIBMUTT) $(LIBNCRYPT) $(INTLLIBS) # generated git_ver.h: $(ALL_FILES) version=`git describe --dirty --abbrev=6 --match "neomutt-*" 2> /dev/null | sed -e 's/^neomutt-[0-9]\{8\}//' -e 's/g//'`; \ echo 'const char *GitVer = "'$$version'";' > git_ver.h.tmp; \ cmp -s git_ver.h.tmp git_ver.h || mv git_ver.h.tmp git_ver.h; \ rm -f git_ver.h.tmp hcache/hcversion.h: $(SRCDIR)/mutt.h $(SRCDIR)/address.h \ $(SRCDIR)/mutt/list.h $(SRCDIR)/mutt/buffer.h \ $(SRCDIR)/parameter.h $(SRCDIR)/body.h \ $(SRCDIR)/envelope.h $(SRCDIR)/header.h \ $(SRCDIR)/hcache/hcachever.sh \ $(PWD)/hcache ( echo '#include "config.h"'; echo '#include "mutt.h"'; \ echo '#include "address.h"'; echo '#include "mutt/list.h"'; \ echo '#include "mutt/buffer.h"'; echo '#include "parameter.h"'; \ echo '#include "body.h"'; echo '#include "envelope.h"'; \ echo '#include "header.h"';) | $(CPP) $(CFLAGS) - | \ sh $(SRCDIR)/hcache/hcachever.sh hcache/hcversion.h # clean clean: $(CLEAN_TARGETS) rm -f $(CLEANFILES) # install install: all $(INSTALL_TARGETS) # Install binaries $(MKDIR_P) $(DESTDIR)$(bindir) for f in $(BINFILES); do \ $(INSTALL) $$f $(DESTDIR)$(bindir)/`basename $$f`; \ done $(MKDIR_P) $(DESTDIR)$(libexecdir)/$(PACKAGE) for f in $(LIBBINFILES); do \ $(INSTALL) $$f $(DESTDIR)$(libexecdir)/$(PACKAGE)/`basename $$f`; \ done # uninstall uninstall: $(UNINSTALL_TARGETS) # Uninstall binaries for f in $(BINFILES); do \ $(RM) $(DESTDIR)$(bindir)/`basename $$f`; \ done for f in $(LIBBINFILES); do \ $(RM) $(DESTDIR)$(libexecdir)/$(PACKAGE)/`basename $$f`; \ done # distclean distclean: clean $(RM) $(DEPFILES) conststrings.c config.h config.log doc/neomutt.1 \ Makefile po/Makefile contrib/Makefile doc/Makefile \ autosetup/jimsh0 ############################################################################## # include generated dependency files -include $(DEPFILES) ############################################################################## # include special-purpose makefiles, each one of which MUST define the # dedicated all-, clean-, install-, and uninstall- targets. include po/Makefile include contrib/Makefile include doc/Makefile # vim: set ts=8 noexpandtab: neomutt-neomutt-20171215/README.SSL000066400000000000000000000121031321473123000164530ustar00rootroot00000000000000IMAP/SSL in mutt ================ Compilation ----------- If you want to have SSL support in mutt, you need to install OpenSSL (http://www.openssl.org) libraries and headers before compiling. OpenSSL versions 0.9.3 through 1.0.1c have been tested. For SSL support to be enabled, you need to run the ``configure'' script with ``--with-ssl[=PFX]'' parameters. If the OpenSSL headers and libraries are not in the default system search paths (usually /usr/include and /usr/lib) you can use the optional PFX argument to define the root directory of your installation. The libraries are then expected to be found in PFX/lib and headers in PFX/include/openssl. Usage ----- IMAP/SSL folders can be accessed just like normal IMAP folders, but you will also have to add '/ssl' before the closing curly brace. Or you can use IMAP url notation, where the methods is called imaps. For example: mailboxes {localhost/ssl}inbox mailboxes {localhost:994/ssl}inbox or mailboxes imaps://localhost/inbox mailboxes imaps://localhost:994/inbox If you get errors about lack of entropy, it means that Mutt was unable to find a source of random data to initialize SSL library with. Should this happen, you need to generate the data yourself and save it in a file pointed by $entropy_file or $RANDFILE (environment) variables or in ~/.rnd. One way to generate random data would be to run a command which generates unpredictable output, for example 'ps aluxww' in Linux, and calculating the MD5-sum from the output and saving it in a file. ** Note: The contents of the file pointed by $RANDFILE environment ** variable (or ~/.rnd if unset) will be overwritten every time Mutt ** is run so don't put anything you can't afford to lose in that file. The files Mutt will try to use to initialize SSL library with are files pointed by $entropy_file and $RANDFILE (or ~/.rnd if unset.) If your OpenSSL is version 0.9.5 or later, the previous files can also be EGD sockets (see http://www.lothar.com/tech/crypto/ for more information about Entropy Gathering Daemon) and in addition sockets in the following places are tried: socket pointed by $EGDSOCKET environment variable, ~/.entropy and /tmp/entropy. All the files and sockets mentioned above must be owned by the user and have permissions of 600. Certificates ------------ Each time a server is contacted, its certificate is checked against known valid certificates. When an unknown certificate is encountered, you are asked to verify it. If you reject the certificate, the connection will be terminated immediately. If you accept the certificate, the connection will be established. Accepted certificates can also be saved so that further connections to the server are automatically accepted. If OpenSSL was built with support for ServerNameIndication (SNI) and TLS is used in the negotiation, mutt will send its idea of the server-name as part of the TLS negotiation. This allows the server to select an appropriate certificate, in the event that one server handles multiple hostnames with different certificates. If your organization has several equivalent IMAP-servers, each of them should have a unique certificate which is signed with a common certificate. If you want to use all of those servers, you don't need to save each server certificate on the first connect. Instead, you can get the signer certificate and save it instead. That way, mutt will automatically accept all certificates signed with the saved certificate. System-wide certificates are by default considered trusted when checking certificates by signer. This allows system administrators to setup trusted certificates for all users. How to install certificates system-wide, depends on the OpenSSL installation. Use of system-wide certificates can be disabled by unsetting $ssl_usesystemcerts variable. Certificates will be saved in the file specified by $certificate_file variable. It is empty as default, so if you don't want to verify certificates each time you connect to a server, you have set this variable to some reasonable value. For example: set certificate_file=~/.mutt/certificates Troubleshooting --------------- If after doing the above, you are unable to successfully connect, it is likely that your IMAP server does not support one of the SSL protocols. There exist three different protocols, TLSv1, SSLv2, and SSLv3. To check each of these, you use the following: openssl s_client -host -port -verify -debug -no_tls1 openssl s_client -host -port -verify -debug -no_ssl2 openssl s_client -host -port -verify -debug -no_ssl3 You can also combine the options until you get a successful connect. Once you know which options do not work, you can set the variables for non-working protocols to know. The variables for the protocols are ssl_use_tlsv1, ssl_use_sslv2, and ssl_use_sslv3. To verify TLS SNI support by a server, you can use: openssl s_client -host -port \ -tls1 -servername -- Tommi Komulainen Tommi.Komulainen@iki.fi Updated by: Jeremy Katz Phil Pennock neomutt-neomutt-20171215/README.md000066400000000000000000000155601321473123000164240ustar00rootroot00000000000000# This is the NeoMutt Project [![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](https://github.com/neomutt/neomutt/blob/neomutt/COPYRIGHT) [![Travis branch](https://api.travis-ci.org/neomutt/neomutt.svg?branch=neomutt)](https://travis-ci.org/neomutt/neomutt) [![Coverity Scan](https://img.shields.io/coverity/scan/8495.svg)](https://scan.coverity.com/projects/neomutt-neomutt) [![Backlog](https://badge.waffle.io/neomutt/neomutt.svg?label=status:backlog&title=Backlog)](http://waffle.io/neomutt/neomutt) [![In Progress](https://badge.waffle.io/neomutt/neomutt.svg?label=status:in-progress&title=In%20Progress)](http://waffle.io/neomutt/neomutt) [![Ready](https://badge.waffle.io/neomutt/neomutt.svg?label=status:ready&title=Ready)](http://waffle.io/neomutt/neomutt) ## What is NeoMutt? * NeoMutt is a project of projects. * A place to gather all the patches against Mutt. * A place for all the developers to gather. Hopefully this will build the community and reduce duplicated effort. NeoMutt was created when Richard Russon (FlatCap) took all the old Mutt patches, sorted through them, fixed them up and documented them. ## What Features does NeoMutt have? | Name | Description | -------------------- | ------------------------------------------------------ | Attach Headers Color | Color attachment headers using regex, just like mail bodies | Compose to Sender | Send new mail to the sender of the current mail | Compressed Folders | Read from/write to compressed mailboxes | Conditional Dates | Use rules to choose date format | Encrypt-to-Self | Save a self-encrypted copy of emails | Fmemopen | Replace some temporary files with memory buffers | Forgotten Attachment | Alert user when (s)he forgets to attach a file to an outgoing email. | Global Hooks | Define actions to run globally within Mutt | Ifdef | Conditional config options | Index Color | Custom rules for theming the email index | Initials Expando | Expando for author's initials | Kyoto Cabinet | Kyoto Cabinet backend for the header cache | Limit Current Thread | Focus on one Email Thread | LMDB | LMDB backend for the header cache | Multiple FCC | Save multiple copies of outgoing mail | Nested If | Allow complex nested conditions in format strings | New Mail | Execute a command upon the receipt of new mail. | NNTP | Talk to a Usenet news server | Notmuch | Email search engine | Progress Bar | Show a visual progress bar on slow operations | Quasi-Delete | Mark emails that should be hidden, but not deleted | Reply With X-Orig-To | Direct reply to email using X-Original-To header | Sensible Browser | Make the file browser behave | Sidebar | Panel containing list of Mailboxes | Skip Quoted | Leave some context visible | Status Color | Custom rules for theming the status bar | TLS-SNI | Negotiate with a server for a TLS/SSL certificate | Trash Folder | Automatically move deleted emails to a trash bin ## Contributed Scripts and Config | Name | Description | ---------------------- | --------------------------------------------- | Header Cache Benchmark | Script to test the speed of the header cache | Keybase | Keybase Integration | Useful programs | List of useful programs interacting with mutt | Vi Keys | Easy and clean Vi-keys for Mutt | Vim Syntax | Vim Syntax File ## Where is NeoMutt? - Source Code: https://github.com/neomutt/neomutt - Releases: https://github.com/neomutt/neomutt/releases/latest - Questions/Bugs: https://github.com/neomutt/neomutt/issues - Website: http://www.neomutt.org/ - IRC: irc://irc.freenode.net/neomutt - please be patient. We're a small group, so our answer might take some time. - Mailinglists: [neomutt-users](mailto:neomutt-users-request@neomutt.org?subject=subscribe) and [neomutt-devel](mailto:neomutt-devel-request@neomutt.org?subject=subscribe) - Development: http://www.neomutt.org/dev.html ## NeoMutt Developers Here's a list of everyone who's helped NeoMutt: Adam Borowski, Alad Wenter, Alex Pearce, Alok Singh, Ander Punnar, Andreas Rammhold, André Berger, Anton Rieger, Antonio Radici, Austin Ray, Baptiste Daroussin, Bernard Pratz, Bletchley Park, Bo Yu, Bryan Bennett, Chris Czettel, Chris Salzberg, Christian Dröge, Christoph Berg, cinder88, Clemens Lang, Damien Riegel, Darshit Shah, David Sterba, Dimitrios Semitsoglou-Tsiapos, Doug Stone-Weaver, Edward Betts, Elimar Riesebieter, Evgeni Golov, Fabian Groffen, Fabio Alessandro Locati, Fabrice Bellet, Faidon Liambotis, Florian Klink, Floyd Anderson, František Hájik, Guillaume Brogi, Hugo Barrera, Ian Zimmerman, Ismaël Bouya, Ivan Tham, Jack Stratton, Jakub Wilk, Jasper Adriaanse, Jelle van der Waa, Jenya Sovetkin, Johannes Frankenau, Johannes Weißl, Jonathan Perkin, Joshua Jordi, Julian Andres Klode, Karel Zak, Kevin Decherf, Kevin Velghe, Kurt Jaeger, Leonardo Schenkel, Leonidas Spyropoulos, Manos Pitsidianakis, Marcin Rajner, Marco Hinz, Matteo Vescovi, Mehdi Abaakouk, ng0, Nicolas Bock, Olaf Lessenich, Peter Hogg, Peter Lewis, Phil Pennock, Philipp Marek, Pierre-Elliott Bécue, Pietro Cerutti, r3lgar, Regid Ichira, Reis Radomil, Riad Wahby, Richard Hartmann, Richard Russon, Rubén Llorente, Santiago Torres, Serge Gebhardt, somini, Stefan Assmann, Stefan Bühler, Stephen Gilles, Steven Ragnarök, Sven Guckes, Thomas Adam, Thomas Klausner, Thomas Schneider, Tobias Angele, Udo Schweigert, Vsevolod Volkov, Werner Fink, Wieland Hoffmann, William Pettersson, Yoshiki Vázquez Baeza, Zero King. ## Original Patch Authors Without the original patch authors, there would be nothing. So, a Big Thank You to: Aaron Schrab, Alain Penders, Benjamin Kuperman, Cedric Duval, Chris Mason, Christian Aichinger, Christoph Berg, Christoph Rissner, David Champion, David Riebenbauer, David Sterba, David Wilson, Don Zickus, Elimar Riesebieter, Eric Davis, Evgeni Golov, Fabian Groffen, Felix von Leitner, Jan Synacek, Jason DeTiberus, Jeremiah Foster, Jeremy Katz, Josh Poimboeuf, Julius Plenz, Justin Hibbits, Karel Zak, Kirill Shutemov, Luke Macken, Mantas Mikulenas, Matteo Vescovi, Patrick Brisbin, Paul Miller, Phil Pennock, Philippe Le Brouster, Richard Russon, Rocco Rutte, Roland Rosenfeld, Sami Farin, Stefan Assmann, Stefan Kuhn, Steve Kemp, Terry Chan, Thomas Glanzmann, Thomer Gil, Tim Stoakes, Tyler Earnest, Victor Manuel Jaquez Leal, Vincent Lefevre, Vladimir Marek, Vsevolod Volkov. ## Original Mutt Authors And of course, we should thank the original Mutt authors, including the original author Michael Elkins and all the people that have contributed to Mutt during its long history, see the Acknowledgements section of the user manual for a detailed list. http://www.neomutt.org/guide/miscellany.html#acknowledgements neomutt-neomutt-20171215/addrbook.c000066400000000000000000000167711321473123000171030ustar00rootroot00000000000000/** * @file * Address book handling aliases * * @authors * Copyright (C) 1996-2000,2007 Michael R. Elkins * * @copyright * 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, see . */ #include "config.h" #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "address.h" #include "alias.h" #include "format_flags.h" #include "globals.h" #include "keymap.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "opcodes.h" #include "options.h" #include "protos.h" #include "sort.h" #define RSORT(x) (SortAlias & SORT_REVERSE) ? -x : x static const struct Mapping AliasHelp[] = { { N_("Exit"), OP_EXIT }, { N_("Del"), OP_DELETE }, { N_("Undel"), OP_UNDELETE }, { N_("Select"), OP_GENERIC_SELECT_ENTRY }, { N_("Help"), OP_HELP }, { NULL, 0 }, }; /** * alias_format_str - Format a string for the alias list * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] col Starting column * @param[in] cols Number of screen columns * @param[in] op printf-like operator, e.g. 't' * @param[in] src printf-like format string * @param[in] prec Field precision, e.g. "-3.4" * @param[in] if_str If condition is met, display this string * @param[in] else_str Otherwise, display this string * @param[in] data Pointer to the mailbox Context * @param[in] flags Format flags * @retval src (unchanged) * * alias_format_str() is a callback function for mutt_expando_format(). * * | Expando | Description * |:--------|:-------------------------------------------------------- * | \%a | Alias name * | \%f | Flags - currently, a 'd' for an alias marked for deletion * | \%n | Index number * | \%r | Address which alias expands to * | \%t | Character which indicates if the alias is tagged for inclusion */ static const char *alias_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, enum FormatFlag flags) { char fmt[SHORT_STRING], adr[SHORT_STRING]; struct Alias *alias = (struct Alias *) data; switch (op) { case 'a': mutt_format_s(buf, buflen, prec, alias->name); break; case 'f': snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, alias->del ? "D" : " "); break; case 'n': snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, alias->num + 1); break; case 'r': adr[0] = '\0'; rfc822_write_address(adr, sizeof(adr), alias->addr, 1); snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, adr); break; case 't': buf[0] = alias->tagged ? '*' : ' '; buf[1] = '\0'; break; } return src; } /** * alias_entry - Format a menu item for the alias list * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] menu Menu containing aliases * @param[in] num Index into the menu */ static void alias_entry(char *buf, size_t buflen, struct Menu *menu, int num) { mutt_expando_format(buf, buflen, 0, MuttIndexWindow->cols, NONULL(AliasFormat), alias_format_str, (unsigned long) ((struct Alias **) menu->data)[num], MUTT_FORMAT_ARROWCURSOR); } static int alias_tag(struct Menu *menu, int n, int m) { struct Alias *cur = ((struct Alias **) menu->data)[n]; bool ot = cur->tagged; cur->tagged = (m >= 0 ? m : !cur->tagged); return cur->tagged - ot; } static int alias_sort_alias(const void *a, const void *b) { struct Alias *pa = *(struct Alias **) a; struct Alias *pb = *(struct Alias **) b; int r = mutt_str_strcasecmp(pa->name, pb->name); return (RSORT(r)); } static int alias_sort_address(const void *a, const void *b) { struct Address *pa = (*(struct Alias **) a)->addr; struct Address *pb = (*(struct Alias **) b)->addr; int r; if (pa == pb) r = 0; else if (!pa) r = -1; else if (!pb) r = 1; else if (pa->personal) { if (pb->personal) r = mutt_str_strcasecmp(pa->personal, pb->personal); else r = 1; } else if (pb->personal) r = -1; else r = mutt_str_strcasecmp(pa->mailbox, pb->mailbox); return (RSORT(r)); } void mutt_alias_menu(char *buf, size_t buflen, struct Alias *aliases) { struct Alias *aliasp = NULL; struct Menu *menu = NULL; struct Alias **AliasTable = NULL; int t = -1; int i; bool done = false; int op; char helpstr[LONG_STRING]; int omax; if (!aliases) { mutt_error(_("You have no aliases!")); return; } menu = mutt_new_menu(MENU_ALIAS); menu->make_entry = alias_entry; menu->tag = alias_tag; menu->title = _("Aliases"); menu->help = mutt_compile_help(helpstr, sizeof(helpstr), MENU_ALIAS, AliasHelp); mutt_push_current_menu(menu); new_aliases: omax = menu->max; /* count the number of aliases */ for (aliasp = aliases; aliasp; aliasp = aliasp->next) { aliasp->del = false; aliasp->tagged = false; menu->max++; } mutt_mem_realloc(&AliasTable, menu->max * sizeof(struct Alias *)); menu->data = AliasTable; if (!AliasTable) return; for (i = omax, aliasp = aliases; aliasp; aliasp = aliasp->next, i++) { AliasTable[i] = aliasp; aliases = aliasp; } if ((SortAlias & SORT_MASK) != SORT_ORDER) { qsort(AliasTable, i, sizeof(struct Alias *), (SortAlias & SORT_MASK) == SORT_ADDRESS ? alias_sort_address : alias_sort_alias); } for (i = 0; i < menu->max; i++) AliasTable[i]->num = i; while (!done) { if (aliases->next) { menu->redraw |= REDRAW_FULL; aliases = aliases->next; goto new_aliases; } switch ((op = mutt_menu_loop(menu))) { case OP_DELETE: case OP_UNDELETE: if (menu->tagprefix) { for (i = 0; i < menu->max; i++) if (AliasTable[i]->tagged) AliasTable[i]->del = (op == OP_DELETE); menu->redraw |= REDRAW_INDEX; } else { AliasTable[menu->current]->del = (op == OP_DELETE); menu->redraw |= REDRAW_CURRENT; if (option(OPT_RESOLVE) && menu->current < menu->max - 1) { menu->current++; menu->redraw |= REDRAW_INDEX; } } break; case OP_GENERIC_SELECT_ENTRY: t = menu->current; done = true; break; case OP_EXIT: done = true; break; } } for (i = 0; i < menu->max; i++) { if (AliasTable[i]->tagged) { rfc822_write_address(buf, buflen, AliasTable[i]->addr, 1); t = -1; } } if (t != -1) { rfc822_write_address(buf, buflen, AliasTable[t]->addr, 1); } mutt_pop_current_menu(menu); mutt_menu_destroy(&menu); FREE(&AliasTable); } neomutt-neomutt-20171215/address.c000066400000000000000000000600251321473123000167320ustar00rootroot00000000000000/** * @file * Representation of an email address * * @authors * Copyright (C) 1996-2000,2011-2013 Michael R. Elkins * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ /** * @page address Representation of an email address * * Representation of an email address * * | Data | Description * | :--------------- | :-------------------------------------------------- * | #AddressError | An out-of-band error code * | #AddressErrors | Messages for the error codes in #AddressError * | #AddressSpecials | Characters with special meaning for email addresses * * | Function | Description * | :--------------------------- | :--------------------------------------------------------- * | mutt_addr_append() | Append one list of addresses onto another * | mutt_addr_cat() | Copy a string and escape the specified characters * | mutt_addr_cmp() | Compare two e-mail addresses * | mutt_addr_cmp_strict() | Strictly compare two Address lists * | mutt_addr_copy() | Copy the real address * | mutt_addr_copy_list() | Copy a list of addresses * | mutt_addr_free() | Free a list of Addresses * | mutt_addr_has_recips() | Count the number of Addresses with valid recipients * | mutt_addr_new() | Create a new Address * | mutt_addr_parse_list() | Parse a list of email addresses * | mutt_addr_parse_list2() | Parse a list of email addresses * | mutt_addr_qualify() | Expand local names in an Address list using a hostname * | mutt_addr_remove_from_list() | Remove an Address from a list * | mutt_addr_search() | Search for an e-mail address in a list * | mutt_addr_valid_msgid() | Is this a valid Message ID? */ #include "config.h" #include #include #include "mutt/mutt.h" #include "address.h" #include "mutt_idna.h" /** * AddressSpecials - Characters with special meaning for email addresses */ const char AddressSpecials[] = "@.,:;<>[]\\\"()"; /** * is_special - Is this character special to an email address? */ #define is_special(x) strchr(AddressSpecials, x) /** * free_address - Free a single Address * @param a Address to free * * @note This doesn't alter the links if the Address is in a list. */ static void free_address(struct Address **a) { if (!a || !*a) return; FREE(&(*a)->personal); FREE(&(*a)->mailbox); FREE(&(*a)); } /** * parse_comment - Extract a comment (parenthesised string) * @param[in] s String, just after the opening parenthesis * @param[out] comment Buffer to store parenthesised string * @param[out] commentlen Length of parenthesised string * @param[in] commentmax Length of buffer * @retval ptr First character after parenthesised string * @retval NULL Error */ static const char *parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax) { int level = 1; while (*s && level) { if (*s == '(') level++; else if (*s == ')') { if (--level == 0) { s++; break; } } else if (*s == '\\') { if (!*++s) break; } if (*commentlen < commentmax) comment[(*commentlen)++] = *s; s++; } if (level) { AddressError = ERR_MISMATCH_PAREN; return NULL; } return s; } /** * parse_quote - Extract a quoted string * @param[in] s String, just after the opening quote mark * @param[out] token Buffer to store quoted string * @param[out] tokenlen Length of quoted string * @param[in] tokenmax Length of buffer * @retval ptr First character after quoted string * @retval NULL Error */ static const char *parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax) { while (*s) { if (*tokenlen < tokenmax) token[*tokenlen] = *s; if (*s == '\\') { if (!*++s) break; if (*tokenlen < tokenmax) token[*tokenlen] = *s; } else if (*s == '"') return (s + 1); (*tokenlen)++; s++; } AddressError = ERR_MISMATCH_QUOTE; return NULL; } /** * next_token - Find the next word, skipping quoted and parenthesised text * @param[in] s String to search * @param[out] token Buffer for the token * @param[out] tokenlen Length of the next token * @param[in] tokenmax Length of the buffer * @retval ptr First character after the next token */ static const char *next_token(const char *s, char *token, size_t *tokenlen, size_t tokenmax) { if (*s == '(') return (parse_comment(s + 1, token, tokenlen, tokenmax)); if (*s == '"') return (parse_quote(s + 1, token, tokenlen, tokenmax)); if (*s && is_special(*s)) { if (*tokenlen < tokenmax) token[(*tokenlen)++] = *s; return (s + 1); } while (*s) { if (mutt_str_is_email_wsp(*s) || is_special(*s)) break; if (*tokenlen < tokenmax) token[(*tokenlen)++] = *s; s++; } return s; } /** * parse_mailboxdomain - Extract part of an email address (and a comment) * @param[in] s String to parse * @param[in] nonspecial Specific characters that are valid * @param[out] mailbox Buffer for email address * @param[out] mailboxlen Length of saved email address * @param[in] mailboxmax Length of mailbox buffer * @param[out] comment Buffer for comment * @param[out] commentlen Length of saved comment * @param[in] commentmax Length of comment buffer * @retval ptr First character after the email address part * * This will be called twice to parse an email address, first for the mailbox * name, then for the domain name. Each part can also have a comment in `()`. * The comment can be at the start or end of the mailbox or domain. * * Examples: * - "john.doe@example.com" * - "john.doe(comment)@example.com" * - "john.doe@example.com(comment)" * * The first call will return "john.doe" with optional comment, "comment". * The second call will return "example.com" with optional comment, "comment". */ static const char *parse_mailboxdomain(const char *s, const char *nonspecial, char *mailbox, size_t *mailboxlen, size_t mailboxmax, char *comment, size_t *commentlen, size_t commentmax) { const char *ps = NULL; while (*s) { s = mutt_str_skip_email_wsp(s); if (!*s) return s; if (strchr(nonspecial, *s) == NULL && is_special(*s)) return s; if (*s == '(') { if (*commentlen && *commentlen < commentmax) comment[(*commentlen)++] = ' '; ps = next_token(s, comment, commentlen, commentmax); } else ps = next_token(s, mailbox, mailboxlen, mailboxmax); if (!ps) return NULL; s = ps; } return s; } /** * parse_address - Extract an email address * @param[in] s String, just after the opening `<` * @param[out] token Buffer for the email address * @param[out] tokenlen Length of the email address * @param[in] tokenmax Length of the email address buffer * @param[out] comment Buffer for any comments * @param[out] commentlen Length of any comments * @param[in] commentmax Length of the comment buffer * @param[in] addr Address to store the results * @retval ptr The closing `>` of the email address * @retval NULL Error */ static const char *parse_address(const char *s, char *token, size_t *tokenlen, size_t tokenmax, char *comment, size_t *commentlen, size_t commentmax, struct Address *addr) { s = parse_mailboxdomain(s, ".\"(\\", token, tokenlen, tokenmax, comment, commentlen, commentmax); if (!s) return NULL; if (*s == '@') { if (*tokenlen < tokenmax) token[(*tokenlen)++] = '@'; s = parse_mailboxdomain(s + 1, ".([]\\", token, tokenlen, tokenmax, comment, commentlen, commentmax); if (!s) return NULL; } terminate_string(token, *tokenlen, tokenmax); addr->mailbox = mutt_str_strdup(token); if (*commentlen && !addr->personal) { terminate_string(comment, *commentlen, commentmax); addr->personal = mutt_str_strdup(comment); } return s; } /** * parse_route_addr - Parse an email addresses * @param[in] s String, just after the opening `<` * @param[out] comment Buffer for any comments * @param[out] commentlen Length of any comments * @param[in] commentmax Length of the comments buffer * @param[in] addr Address to store the details * @retval ptr First character after the email address */ static const char *parse_route_addr(const char *s, char *comment, size_t *commentlen, size_t commentmax, struct Address *addr) { char token[LONG_STRING]; size_t tokenlen = 0; s = mutt_str_skip_email_wsp(s); /* find the end of the route */ if (*s == '@') { while (s && *s == '@') { if (tokenlen < sizeof(token) - 1) token[tokenlen++] = '@'; s = parse_mailboxdomain(s + 1, ",.\\[](", token, &tokenlen, sizeof(token) - 1, comment, commentlen, commentmax); } if (!s || *s != ':') { AddressError = ERR_BAD_ROUTE; return NULL; /* invalid route */ } if (tokenlen < sizeof(token) - 1) token[tokenlen++] = ':'; s++; } if ((s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen, commentmax, addr)) == NULL) return NULL; if (*s != '>') { AddressError = ERR_BAD_ROUTE_ADDR; return NULL; } if (!addr->mailbox) addr->mailbox = mutt_str_strdup("@"); s++; return s; } /** * parse_addr_spec - Parse an email address * @param[in] s String to parse * @param[out] comment Buffer for any comments * @param[out] commentlen Length of any comments * @param[in] commentmax Length of the comments buffer * @param[in] addr Address to fill in * @retval ptr First character after the email address */ static const char *parse_addr_spec(const char *s, char *comment, size_t *commentlen, size_t commentmax, struct Address *addr) { char token[LONG_STRING]; size_t tokenlen = 0; s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen, commentmax, addr); if (s && *s && *s != ',' && *s != ';') { AddressError = ERR_BAD_ADDR_SPEC; return NULL; } return s; } /** * add_addrspec - Parse an email address and add an Address to a list * @param[in] top Top of Address list * @param[in] last End of Address list * @param[in] phrase String to parse * @param[out] comment Buffer for any comments * @param[out] commentlen Length of any comments * @param[in] commentmax Length of the comments buffer */ static void add_addrspec(struct Address **top, struct Address **last, const char *phrase, char *comment, size_t *commentlen, size_t commentmax) { struct Address *cur = mutt_addr_new(); if (parse_addr_spec(phrase, comment, commentlen, commentmax, cur) == NULL) { mutt_addr_free(&cur); return; } if (*last) (*last)->next = cur; else *top = cur; *last = cur; } /** * AddressError - An out-of-band error code * * Many of the Address functions set this variable on error. * Its values are defined in #AddressError. * Text for the errors can be looked up using #AddressErrors. */ int AddressError = 0; /** * AddressErrors - Messages for the error codes in #AddressError * * These must defined in the same order as enum AddressError. */ const char *const AddressErrors[] = { "out of memory", "mismatched parenthesis", "mismatched quotes", "bad route in <>", "bad address in <>", "bad address spec", }; /** * mutt_addr_new - Create a new Address * @retval ptr Newly allocated Address * * Free the result with free_address() or mutt_addr_free() */ struct Address *mutt_addr_new(void) { return mutt_mem_calloc(1, sizeof(struct Address)); } /** * mutt_addr_remove_from_list - Remove an Address from a list * @param a Address list * @param mailbox Email address to match * @retval 0 Success * @retval -1 Error, or email not found */ int mutt_addr_remove_from_list(struct Address **a, const char *mailbox) { struct Address *p = NULL, *last = NULL, *t = NULL; int rc = -1; p = *a; last = NULL; while (p) { if (mutt_str_strcasecmp(mailbox, p->mailbox) == 0) { if (last) last->next = p->next; else (*a) = p->next; t = p; p = p->next; free_address(&t); rc = 0; } else { last = p; p = p->next; } } return rc; } /** * mutt_addr_free - Free a list of Addresses * @param p Top of the list */ void mutt_addr_free(struct Address **p) { struct Address *t = NULL; while (*p) { t = *p; *p = (*p)->next; free_address(&t); } } /** * mutt_addr_parse_list - Parse a list of email addresses * @param top List to append addresses * @param s String to parse * @retval ptr Top of the address list * @retval NULL Error */ struct Address *mutt_addr_parse_list(struct Address *top, const char *s) { int ws_pending, nl; const char *ps = NULL; char comment[LONG_STRING], phrase[LONG_STRING]; size_t phraselen = 0, commentlen = 0; struct Address *cur = NULL, *last = NULL; AddressError = 0; last = top; while (last && last->next) last = last->next; ws_pending = mutt_str_is_email_wsp(*s); if ((nl = mutt_str_strlen(s))) nl = s[nl - 1] == '\n'; s = mutt_str_skip_email_wsp(s); while (*s) { if (*s == ',') { if (phraselen) { terminate_buffer(phrase, phraselen); add_addrspec(&top, &last, phrase, comment, &commentlen, sizeof(comment) - 1); } else if (commentlen && last && !last->personal) { terminate_buffer(comment, commentlen); last->personal = mutt_str_strdup(comment); } commentlen = 0; phraselen = 0; s++; } else if (*s == '(') { if (commentlen && commentlen < sizeof(comment) - 1) comment[commentlen++] = ' '; ps = next_token(s, comment, &commentlen, sizeof(comment) - 1); if (!ps) { mutt_addr_free(&top); return NULL; } s = ps; } else if (*s == '"') { if (phraselen && phraselen < sizeof(phrase) - 1) phrase[phraselen++] = ' '; ps = parse_quote(s + 1, phrase, &phraselen, sizeof(phrase) - 1); if (!ps) { mutt_addr_free(&top); return NULL; } s = ps; } else if (*s == ':') { cur = mutt_addr_new(); terminate_buffer(phrase, phraselen); cur->mailbox = mutt_str_strdup(phrase); cur->group = 1; if (last) last->next = cur; else top = cur; last = cur; phraselen = 0; commentlen = 0; s++; } else if (*s == ';') { if (phraselen) { terminate_buffer(phrase, phraselen); add_addrspec(&top, &last, phrase, comment, &commentlen, sizeof(comment) - 1); } else if (commentlen && last && !last->personal) { terminate_buffer(comment, commentlen); last->personal = mutt_str_strdup(comment); } /* add group terminator */ cur = mutt_addr_new(); if (last) { last->next = cur; last = cur; } phraselen = 0; commentlen = 0; s++; } else if (*s == '<') { terminate_buffer(phrase, phraselen); cur = mutt_addr_new(); if (phraselen) cur->personal = mutt_str_strdup(phrase); ps = parse_route_addr(s + 1, comment, &commentlen, sizeof(comment) - 1, cur); if (!ps) { mutt_addr_free(&top); mutt_addr_free(&cur); return NULL; } if (last) last->next = cur; else top = cur; last = cur; phraselen = 0; commentlen = 0; s = ps; } else { if (phraselen && phraselen < sizeof(phrase) - 1 && ws_pending) phrase[phraselen++] = ' '; ps = next_token(s, phrase, &phraselen, sizeof(phrase) - 1); if (!ps) { mutt_addr_free(&top); return NULL; } s = ps; } ws_pending = mutt_str_is_email_wsp(*s); s = mutt_str_skip_email_wsp(s); } if (phraselen) { terminate_buffer(phrase, phraselen); terminate_buffer(comment, commentlen); add_addrspec(&top, &last, phrase, comment, &commentlen, sizeof(comment) - 1); } else if (commentlen && last && !last->personal) { terminate_buffer(comment, commentlen); last->personal = mutt_str_strdup(comment); } return top; } /** * mutt_addr_parse_list2 - Parse a list of email addresses * @param p Add to this List of Addresses * @param s String to parse * @retval ptr Head of the list of addresses * * The email addresses can be separated by whitespace or commas. */ struct Address *mutt_addr_parse_list2(struct Address *p, const char *s) { const char *q = NULL; /* check for a simple whitespace separated list of addresses */ q = strpbrk(s, "\"<>():;,\\"); if (!q) { char tmp[HUGE_STRING]; char *r = NULL; mutt_str_strfcpy(tmp, s, sizeof(tmp)); r = tmp; while ((r = strtok(r, " \t")) != NULL) { p = mutt_addr_parse_list(p, r); r = NULL; } } else p = mutt_addr_parse_list(p, s); return p; } /** * mutt_addr_qualify - Expand local names in an Address list using a hostname * @param addr Address list * @param host Hostname * * Any addresses containing a bare name will be expanded using the hostname. * e.g. "john", "example.com" -> 'john@example.com'. */ void mutt_addr_qualify(struct Address *addr, const char *host) { char *p = NULL; for (; addr; addr = addr->next) { if (!addr->group && addr->mailbox && strchr(addr->mailbox, '@') == NULL) { p = mutt_mem_malloc(mutt_str_strlen(addr->mailbox) + mutt_str_strlen(host) + 2); sprintf(p, "%s@%s", addr->mailbox, host); FREE(&addr->mailbox); addr->mailbox = p; } } } /** * mutt_addr_cat - Copy a string and escape the specified characters * @param buf Buffer for the result * @param buflen Length of the result buffer * @param value String to copy * @param specials Characters to be escaped */ void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials) { if (strpbrk(value, specials)) { char tmp[256], *pc = tmp; size_t tmplen = sizeof(tmp) - 3; *pc++ = '"'; for (; *value && tmplen > 1; value++) { if (*value == '\\' || *value == '"') { *pc++ = '\\'; tmplen--; } *pc++ = *value; tmplen--; } *pc++ = '"'; *pc = 0; mutt_str_strfcpy(buf, tmp, buflen); } else mutt_str_strfcpy(buf, value, buflen); } /** * mutt_addr_copy - Copy the real address * @param addr Address to copy * @retval ptr New Address */ struct Address *mutt_addr_copy(struct Address *addr) { struct Address *p = mutt_addr_new(); p->personal = mutt_str_strdup(addr->personal); p->mailbox = mutt_str_strdup(addr->mailbox); p->group = addr->group; p->is_intl = addr->is_intl; p->intl_checked = addr->intl_checked; return p; } /** * mutt_addr_copy_list - Copy a list of addresses * @param addr Address list * @param prune Skip groups if there are more addresses * @retval ptr New Address list */ struct Address *mutt_addr_copy_list(struct Address *addr, bool prune) { struct Address *top = NULL, *last = NULL; for (; addr; addr = addr->next) { if (prune && addr->group && (!addr->next || !addr->next->mailbox)) { /* ignore this element of the list */ } else if (last) { last->next = mutt_addr_copy(addr); last = last->next; } else top = last = mutt_addr_copy(addr); } return top; } /** * mutt_addr_append - Append one list of addresses onto another * @param a Destination Address list * @param b Source Address list * @param prune Skip groups if there are more addresses * @retval ptr Last Address in the combined list * * Append the Source onto the end of the Destination Address list. */ struct Address *mutt_addr_append(struct Address **a, struct Address *b, bool prune) { struct Address *tmp = *a; while (tmp && tmp->next) tmp = tmp->next; if (!b) return tmp; if (tmp) tmp->next = mutt_addr_copy_list(b, prune); else tmp = *a = mutt_addr_copy_list(b, prune); while (tmp && tmp->next) tmp = tmp->next; return tmp; } /** * mutt_addr_valid_msgid - Is this a valid Message ID? * @param msgid Message ID * @retval bool True if it is valid * * Incomplete. Only used to thwart the APOP MD5 attack (#2846). */ bool mutt_addr_valid_msgid(const char *msgid) { /* msg-id = "<" addr-spec ">" * addr-spec = local-part "@" domain * local-part = word *("." word) * word = atom / quoted-string * atom = 1* * CHAR = ( 0.-127. ) * specials = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "." / "[" / "]" * SPACE = ( 32. ) * CTLS = ( 0.-31., 127.) * quoted-string = <"> *(qtext/quoted-pair) <"> * qtext = , "\" and CR> * CR = ( 13. ) * quoted-pair = "\" CHAR * domain = sub-domain *("." sub-domain) * sub-domain = domain-ref / domain-literal * domain-ref = atom * domain-literal = "[" *(dtext / quoted-pair) "]" */ size_t l; if (!msgid || !*msgid) return false; l = mutt_str_strlen(msgid); if (l < 5) /* */ return false; if (msgid[0] != '<' || msgid[l - 1] != '>') return false; if (!(strrchr(msgid, '@'))) return false; /* TODO: complete parser */ for (size_t i = 0; i < l; i++) if ((unsigned char) msgid[i] > 127) return false; return true; } /** * mutt_addr_cmp_strict - Strictly compare two Address lists * @param a First Address * @param b Second Address * @retval true Address lists are strictly identical */ bool mutt_addr_cmp_strict(const struct Address *a, const struct Address *b) { while (a && b) { if ((mutt_str_strcmp(a->mailbox, b->mailbox) != 0) || (mutt_str_strcmp(a->personal, b->personal) != 0)) { return false; } a = a->next; b = b->next; } if (a || b) return false; return true; } /** * mutt_addr_has_recips - Count the number of Addresses with valid recipients * @param a Address list * @retval num Number of valid Addresses * * An Address has a recipient if the mailbox or group is set. */ int mutt_addr_has_recips(struct Address *a) { int c = 0; for (; a; a = a->next) { if (!a->mailbox || a->group) continue; c++; } return c; } /** * mutt_addr_cmp - Compare two e-mail addresses * @param a Address 1 * @param b Address 2 * @retval true if they are equivalent */ bool mutt_addr_cmp(struct Address *a, struct Address *b) { if (!a->mailbox || !b->mailbox) return false; if (mutt_str_strcasecmp(a->mailbox, b->mailbox) != 0) return false; return true; } /** * mutt_addr_search - Search for an e-mail address in a list * @param a Address containing the search email * @param lst Address List * @retval true If the Address is in the list */ bool mutt_addr_search(struct Address *a, struct Address *lst) { for (; lst; lst = lst->next) { if (mutt_addr_cmp(a, lst)) return true; } return false; } neomutt-neomutt-20171215/address.h000066400000000000000000000050141321473123000167340ustar00rootroot00000000000000/** * @file * Representation of an email address * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ #ifndef _MUTT_ADDRESS_H #define _MUTT_ADDRESS_H #include /** * struct Address - An email address */ struct Address { char *personal; /**< real name of address */ char *mailbox; /**< mailbox and host address */ int group; /**< group mailbox? */ struct Address *next; bool is_intl : 1; bool intl_checked : 1; }; /** * enum AddressError - possible values for AddressError */ enum AddressError { ERR_MEMORY = 1, ERR_MISMATCH_PAREN, ERR_MISMATCH_QUOTE, ERR_BAD_ROUTE, ERR_BAD_ROUTE_ADDR, ERR_BAD_ADDR_SPEC }; extern int AddressError; extern const char *const AddressErrors[]; extern const char AddressSpecials[]; #define address_error(x) AddressErrors[x] struct Address *mutt_addr_append(struct Address **a, struct Address *b, bool prune); void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials); bool mutt_addr_cmp_strict(const struct Address *a, const struct Address *b); bool mutt_addr_cmp(struct Address *a, struct Address *b); struct Address *mutt_addr_copy_list(struct Address *addr, bool prune); struct Address *mutt_addr_copy(struct Address *addr); void mutt_addr_free(struct Address **p); int mutt_addr_has_recips(struct Address *a); struct Address *mutt_addr_new(void); struct Address *mutt_addr_parse_list2(struct Address *p, const char *s); struct Address *mutt_addr_parse_list(struct Address *top, const char *s); void mutt_addr_qualify(struct Address *addr, const char *host); int mutt_addr_remove_from_list(struct Address **a, const char *mailbox); bool mutt_addr_search(struct Address *a, struct Address *lst); bool mutt_addr_valid_msgid(const char *msgid); #endif /* _MUTT_ADDRESS_H */ neomutt-neomutt-20171215/alias.c000066400000000000000000000403771321473123000164060ustar00rootroot00000000000000/** * @file * Representation of a single alias to an email address * * @authors * Copyright (C) 1996-2002 Michael R. Elkins * * @copyright * 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, see . */ #include "config.h" #include #include #ifdef ENABLE_NLS #include #endif #include #include #include #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "address.h" #include "alias.h" #include "envelope.h" #include "globals.h" #include "mutt_charset.h" #include "mutt_curses.h" #include "mutt_idna.h" #include "options.h" #include "protos.h" struct Address *mutt_lookup_alias(const char *s) { struct Alias *t = Aliases; for (; t; t = t->next) if (mutt_str_strcasecmp(s, t->name) == 0) return t->addr; return NULL; /* no such alias */ } static struct Address *expand_aliases_r(struct Address *a, struct ListHead *expn) { struct Address *head = NULL, *last = NULL, *t = NULL, *w = NULL; bool i; const char *fqdn = NULL; while (a) { if (!a->group && !a->personal && a->mailbox && strchr(a->mailbox, '@') == NULL) { t = mutt_lookup_alias(a->mailbox); if (t) { i = false; struct ListNode *np; STAILQ_FOREACH(np, expn, entries) { if (mutt_str_strcmp(a->mailbox, np->data) == 0) /* alias already found */ { mutt_debug(1, "loop in alias found for '%s'\n", a->mailbox); i = true; break; } } if (!i) { mutt_list_insert_head(expn, mutt_str_strdup(a->mailbox)); w = mutt_addr_copy_list(t, false); w = expand_aliases_r(w, expn); if (head) last->next = w; else head = last = w; while (last && last->next) last = last->next; } t = a; a = a->next; t->next = NULL; mutt_addr_free(&t); continue; } else { struct passwd *pw = getpwnam(a->mailbox); if (pw) { char namebuf[STRING]; mutt_gecos_name(namebuf, sizeof(namebuf), pw); mutt_str_replace(&a->personal, namebuf); } } } if (head) { last->next = a; last = last->next; } else head = last = a; a = a->next; last->next = NULL; } if (option(OPT_USE_DOMAIN) && (fqdn = mutt_fqdn(1))) { /* now qualify all local addresses */ mutt_addr_qualify(head, fqdn); } return head; } struct Address *mutt_expand_aliases(struct Address *a) { struct Address *t = NULL; struct ListHead expn; /* previously expanded aliases to avoid loops */ STAILQ_INIT(&expn); t = expand_aliases_r(a, &expn); mutt_list_free(&expn); return (mutt_remove_duplicates(t)); } void mutt_expand_aliases_env(struct Envelope *env) { env->from = mutt_expand_aliases(env->from); env->to = mutt_expand_aliases(env->to); env->cc = mutt_expand_aliases(env->cc); env->bcc = mutt_expand_aliases(env->bcc); env->reply_to = mutt_expand_aliases(env->reply_to); env->mail_followup_to = mutt_expand_aliases(env->mail_followup_to); } /** * write_safe_address - Defang malicious email addresses * * if someone has an address like * From: Michael `/bin/rm -f ~` Elkins * and the user creates an alias for this, NeoMutt could wind up executing * the backticks because it writes aliases like * alias me Michael `/bin/rm -f ~` Elkins * To avoid this problem, use a backslash (\) to quote any backticks. We also * need to quote backslashes as well, since you could defeat the above by * doing * From: Michael \`/bin/rm -f ~\` Elkins * since that would get aliased as * alias me Michael \\`/bin/rm -f ~\\` Elkins * which still gets evaluated because the double backslash is not a quote. * * Additionally, we need to quote ' and " characters - otherwise, neomutt will * interpret them on the wrong parsing step. * * $ wants to be quoted since it may indicate the start of an environment * variable. */ static void write_safe_address(FILE *fp, char *s) { while (*s) { if (*s == '\\' || *s == '`' || *s == '\'' || *s == '"' || *s == '$') fputc('\\', fp); fputc(*s, fp); s++; } } struct Address *mutt_get_address(struct Envelope *env, char **pfxp) { struct Address *adr = NULL; char *pfx = NULL; if (mutt_addr_is_user(env->from)) { if (env->to && !mutt_is_mail_list(env->to)) { pfx = "To"; adr = env->to; } else { pfx = "Cc"; adr = env->cc; } } else if (env->reply_to && !mutt_is_mail_list(env->reply_to)) { pfx = "Reply-To"; adr = env->reply_to; } else { adr = env->from; pfx = "From"; } if (pfxp) *pfxp = pfx; return adr; } static void recode_buf(char *buf, size_t buflen) { char *s = NULL; if (!ConfigCharset || !*ConfigCharset || !Charset) return; s = mutt_str_strdup(buf); if (!s) return; if (mutt_convert_string(&s, Charset, ConfigCharset, 0) == 0) mutt_str_strfcpy(buf, s, buflen); FREE(&s); } /** * check_alias_name - Sanity-check an alias name * * Only characters which are non-special to both the RFC822 and the neomutt * configuration parser are permitted. */ int check_alias_name(const char *s, char *dest, size_t destlen) { wchar_t wc; mbstate_t mb; size_t l; int rc = 0, bad = 0, dry = !dest || !destlen; memset(&mb, 0, sizeof(mbstate_t)); if (!dry) destlen--; for (; s && *s && (dry || destlen) && (l = mbrtowc(&wc, s, MB_CUR_MAX, &mb)) != 0; s += l, destlen -= l) { bad = l == (size_t)(-1) || l == (size_t)(-2); /* conversion error */ bad = bad || (!dry && l > destlen); /* too few room for mb char */ if (l == 1) bad = bad || (strchr("-_+=.", *s) == NULL && !iswalnum(wc)); else bad = bad || !iswalnum(wc); if (bad) { if (dry) return -1; if (l == (size_t)(-1)) memset(&mb, 0, sizeof(mbstate_t)); *dest++ = '_'; rc = -1; } else if (!dry) { memcpy(dest, s, l); dest += l; } } if (!dry) *dest = '\0'; return rc; } void mutt_create_alias(struct Envelope *cur, struct Address *iadr) { struct Alias *new = NULL, *t = NULL; char buf[LONG_STRING], tmp[LONG_STRING], prompt[SHORT_STRING], *pc = NULL; char *err = NULL; char fixed[LONG_STRING]; FILE *rc = NULL; struct Address *adr = NULL; if (cur) { adr = mutt_get_address(cur, NULL); } else if (iadr) { adr = iadr; } if (adr && adr->mailbox) { mutt_str_strfcpy(tmp, adr->mailbox, sizeof(tmp)); if ((pc = strchr(tmp, '@'))) *pc = '\0'; } else tmp[0] = '\0'; /* Don't suggest a bad alias name in the event of a strange local part. */ check_alias_name(tmp, buf, sizeof(buf)); retry_name: /* L10N: prompt to add a new alias */ if (mutt_get_field(_("Alias as: "), buf, sizeof(buf), 0) != 0 || !buf[0]) return; /* check to see if the user already has an alias defined */ if (mutt_lookup_alias(buf)) { mutt_error(_("You already have an alias defined with that name!")); return; } if (check_alias_name(buf, fixed, sizeof(fixed))) { switch (mutt_yesorno(_("Warning: This alias name may not work. Fix it?"), MUTT_YES)) { case MUTT_YES: mutt_str_strfcpy(buf, fixed, sizeof(buf)); goto retry_name; case MUTT_ABORT: return; } } new = mutt_mem_calloc(1, sizeof(struct Alias)); new->name = mutt_str_strdup(buf); mutt_addrlist_to_local(adr); if (adr && adr->mailbox) mutt_str_strfcpy(buf, adr->mailbox, sizeof(buf)); else buf[0] = '\0'; mutt_addrlist_to_intl(adr, NULL); do { if (mutt_get_field(_("Address: "), buf, sizeof(buf), 0) != 0 || !buf[0]) { mutt_free_alias(&new); return; } new->addr = mutt_addr_parse_list(new->addr, buf); if (!new->addr) BEEP(); if (mutt_addrlist_to_intl(new->addr, &err)) { mutt_error(_("Error: '%s' is a bad IDN."), err); mutt_sleep(2); continue; } } while (!new->addr); if (adr && adr->personal && !mutt_is_mail_list(adr)) mutt_str_strfcpy(buf, adr->personal, sizeof(buf)); else buf[0] = '\0'; if (mutt_get_field(_("Personal name: "), buf, sizeof(buf), 0) != 0) { mutt_free_alias(&new); return; } new->addr->personal = mutt_str_strdup(buf); buf[0] = '\0'; rfc822_write_address(buf, sizeof(buf), new->addr, 1); snprintf(prompt, sizeof(prompt), _("[%s = %s] Accept?"), new->name, buf); if (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES) { mutt_free_alias(&new); return; } mutt_alias_add_reverse(new); if ((t = Aliases)) { while (t->next) t = t->next; t->next = new; } else Aliases = new; mutt_str_strfcpy(buf, NONULL(AliasFile), sizeof(buf)); if (mutt_get_field(_("Save to file: "), buf, sizeof(buf), MUTT_FILE) != 0) return; mutt_expand_path(buf, sizeof(buf)); if ((rc = fopen(buf, "a+"))) { /* terminate existing file with \n if necessary */ if (fseek(rc, 0, SEEK_END)) goto fseek_err; if (ftell(rc) > 0) { if (fseek(rc, -1, SEEK_CUR) < 0) goto fseek_err; if (fread(buf, 1, 1, rc) != 1) { mutt_perror(_("Error reading alias file")); mutt_file_fclose(&rc); return; } if (fseek(rc, 0, SEEK_END) < 0) goto fseek_err; if (buf[0] != '\n') fputc('\n', rc); } if (check_alias_name(new->name, NULL, 0)) mutt_file_quote_filename(buf, sizeof(buf), new->name); else mutt_str_strfcpy(buf, new->name, sizeof(buf)); recode_buf(buf, sizeof(buf)); fprintf(rc, "alias %s ", buf); buf[0] = '\0'; rfc822_write_address(buf, sizeof(buf), new->addr, 0); recode_buf(buf, sizeof(buf)); write_safe_address(rc, buf); fputc('\n', rc); if (mutt_file_fsync_close(&rc) != 0) mutt_message("Trouble adding alias: %s.", strerror(errno)); else mutt_message(_("Alias added.")); } else mutt_perror(buf); return; fseek_err: mutt_perror(_("Error seeking in alias file")); mutt_file_fclose(&rc); return; } /** * alias_reverse_lookup - Does the user have an alias for the given address */ struct Address *alias_reverse_lookup(struct Address *a) { if (!a || !a->mailbox) return NULL; return mutt_hash_find(ReverseAliases, a->mailbox); } void mutt_alias_add_reverse(struct Alias *t) { struct Address *ap = NULL; if (!t) return; /* Note that the address mailbox should be converted to intl form * before using as a key in the hash. This is currently done * by all callers, but added here mostly as documentation.. */ mutt_addrlist_to_intl(t->addr, NULL); for (ap = t->addr; ap; ap = ap->next) { if (!ap->group && ap->mailbox) mutt_hash_insert(ReverseAliases, ap->mailbox, ap); } } void mutt_alias_delete_reverse(struct Alias *t) { struct Address *ap = NULL; if (!t) return; /* If the alias addresses were converted to local form, they won't * match the hash entries. */ mutt_addrlist_to_intl(t->addr, NULL); for (ap = t->addr; ap; ap = ap->next) { if (!ap->group && ap->mailbox) mutt_hash_delete(ReverseAliases, ap->mailbox, ap, NULL); } } /** * mutt_alias_complete - alias completion routine * * given a partial alias, this routine attempts to fill in the alias * from the alias list as much as possible. if given empty search string * or found nothing, present all aliases */ int mutt_alias_complete(char *s, size_t buflen) { struct Alias *a = Aliases; struct Alias *a_list = NULL, *a_cur = NULL; char bestname[HUGE_STRING]; int i; if (s[0] != 0) /* avoid empty string as strstr argument */ { memset(bestname, 0, sizeof(bestname)); while (a) { if (a->name && strstr(a->name, s) == a->name) { if (!bestname[0]) /* init */ mutt_str_strfcpy(bestname, a->name, MIN(mutt_str_strlen(a->name) + 1, sizeof(bestname))); else { for (i = 0; a->name[i] && a->name[i] == bestname[i]; i++) ; bestname[i] = '\0'; } } a = a->next; } if (bestname[0] != 0) { if (mutt_str_strcmp(bestname, s) != 0) { /* we are adding something to the completion */ mutt_str_strfcpy(s, bestname, mutt_str_strlen(bestname) + 1); return 1; } /* build alias list and show it */ a = Aliases; while (a) { if (a->name && (strstr(a->name, s) == a->name)) { if (!a_list) /* init */ a_cur = a_list = mutt_mem_malloc(sizeof(struct Alias)); else { a_cur->next = mutt_mem_malloc(sizeof(struct Alias)); a_cur = a_cur->next; } memcpy(a_cur, a, sizeof(struct Alias)); a_cur->next = NULL; } a = a->next; } } } bestname[0] = '\0'; mutt_alias_menu(bestname, sizeof(bestname), a_list ? a_list : Aliases); if (bestname[0] != 0) mutt_str_strfcpy(s, bestname, buflen); /* free the alias list */ while (a_list) { a_cur = a_list; a_list = a_list->next; FREE(&a_cur); } /* remove any aliases marked for deletion */ a_list = NULL; for (a_cur = Aliases; a_cur;) { if (a_cur->del) { if (a_list) a_list->next = a_cur->next; else Aliases = a_cur->next; a_cur->next = NULL; mutt_free_alias(&a_cur); if (a_list) a_cur = a_list; else a_cur = Aliases; } else { a_list = a_cur; a_cur = a_cur->next; } } return 0; } static bool string_is_address(const char *str, const char *u, const char *d) { char buf[LONG_STRING]; snprintf(buf, sizeof(buf), "%s@%s", NONULL(u), NONULL(d)); if (mutt_str_strcasecmp(str, buf) == 0) return true; return false; } /** * mutt_addr_is_user - Does the address belong to the user * @retval true if the given address belongs to the user */ bool mutt_addr_is_user(struct Address *addr) { const char *fqdn = NULL; /* NULL address is assumed to be the user. */ if (!addr) { mutt_debug(5, "yes, NULL address\n"); return true; } if (!addr->mailbox) { mutt_debug(5, "no, no mailbox\n"); return false; } if (mutt_str_strcasecmp(addr->mailbox, Username) == 0) { mutt_debug(5, "#1 yes, %s = %s\n", addr->mailbox, Username); return true; } if (string_is_address(addr->mailbox, Username, ShortHostname)) { mutt_debug(5, "#2 yes, %s = %s @ %s\n", addr->mailbox, Username, ShortHostname); return true; } fqdn = mutt_fqdn(0); if (string_is_address(addr->mailbox, Username, fqdn)) { mutt_debug(5, "#3 yes, %s = %s @ %s\n", addr->mailbox, Username, NONULL(fqdn)); return true; } fqdn = mutt_fqdn(1); if (string_is_address(addr->mailbox, Username, fqdn)) { mutt_debug(5, "#4 yes, %s = %s @ %s\n", addr->mailbox, Username, NONULL(fqdn)); return true; } if (From && (mutt_str_strcasecmp(From->mailbox, addr->mailbox) == 0)) { mutt_debug(5, "#5 yes, %s = %s\n", addr->mailbox, From->mailbox); return true; } if (mutt_match_regex_list(addr->mailbox, Alternates)) { mutt_debug(5, "yes, %s matched by alternates.\n", addr->mailbox); if (mutt_match_regex_list(addr->mailbox, UnAlternates)) mutt_debug(5, "but, %s matched by unalternates.\n", addr->mailbox); else return true; } mutt_debug(5, "no, all failed.\n"); return false; } void mutt_free_alias(struct Alias **p) { struct Alias *t = NULL; while (*p) { t = *p; *p = (*p)->next; mutt_alias_delete_reverse(t); FREE(&t->name); mutt_addr_free(&t->addr); FREE(&t); } } neomutt-neomutt-20171215/alias.h000066400000000000000000000026511321473123000164040ustar00rootroot00000000000000/** * @file * Representation of a single alias to an email address * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ #ifndef _MUTT_ALIAS_H #define _MUTT_ALIAS_H #include struct Envelope; struct Address; /** * struct Alias - A shortcut for an email address */ struct Alias { char *name; struct Address *addr; struct Alias *next; bool tagged; bool del; short num; }; struct Address *mutt_lookup_alias(const char *s); struct Address *mutt_expand_aliases(struct Address *a); void mutt_expand_aliases_env(struct Envelope *env); struct Address *mutt_get_address(struct Envelope *env, char **pfxp); void mutt_create_alias(struct Envelope *cur, struct Address *iadr); void mutt_free_alias(struct Alias **p); #endif /* _MUTT_ALIAS_H */ neomutt-neomutt-20171215/attach.c000066400000000000000000000751231321473123000165560ustar00rootroot00000000000000/** * @file * Handling of email attachments * * @authors * Copyright (C) 1996-2000,2002,2013 Michael R. Elkins * Copyright (C) 1999-2004,2006 Thomas Roessler * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "attach.h" #include "body.h" #include "context.h" #include "copy.h" #include "filter.h" #include "globals.h" #include "header.h" #include "mailbox.h" #include "mime.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mx.h" #include "ncrypt/ncrypt.h" #include "options.h" #include "pager.h" #include "parameter.h" #include "protos.h" #include "rfc1524.h" #include "state.h" int mutt_get_tmp_attachment(struct Body *a) { char type[STRING]; char tempfile[_POSIX_PATH_MAX]; FILE *fpin = NULL, *fpout = NULL; struct stat st; if (a->unlink) return 0; struct Rfc1524MailcapEntry *entry = rfc1524_new_entry(); snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype); rfc1524_mailcap_lookup(a, type, entry, 0); rfc1524_expand_filename(entry->nametemplate, a->filename, tempfile, sizeof(tempfile)); rfc1524_free_entry(&entry); if (stat(a->filename, &st) == -1) return -1; if ((fpin = fopen(a->filename, "r")) && (fpout = mutt_file_fopen(tempfile, "w"))) { mutt_file_copy_stream(fpin, fpout); mutt_str_replace(&a->filename, tempfile); a->unlink = true; if (a->stamp >= st.st_mtime) mutt_stamp_attachment(a); } else mutt_perror(fpin ? tempfile : a->filename); if (fpin) mutt_file_fclose(&fpin); if (fpout) mutt_file_fclose(&fpout); return a->unlink ? 0 : -1; } /** * mutt_compose_attachment - Create an attachment * @retval 1 if require full screen redraw * @retval 0 otherwise */ int mutt_compose_attachment(struct Body *a) { char type[STRING]; char command[STRING]; char newfile[_POSIX_PATH_MAX] = ""; struct Rfc1524MailcapEntry *entry = rfc1524_new_entry(); bool unlink_newfile = false; int rc = 0; snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype); if (rfc1524_mailcap_lookup(a, type, entry, MUTT_COMPOSE)) { if (entry->composecommand || entry->composetypecommand) { if (entry->composetypecommand) mutt_str_strfcpy(command, entry->composetypecommand, sizeof(command)); else mutt_str_strfcpy(command, entry->composecommand, sizeof(command)); if (rfc1524_expand_filename(entry->nametemplate, a->filename, newfile, sizeof(newfile))) { mutt_debug(1, "oldfile: %s\t newfile: %s\n", a->filename, newfile); if (mutt_file_symlink(a->filename, newfile) == -1) { if (mutt_yesorno(_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES) goto bailout; } else unlink_newfile = true; } else mutt_str_strfcpy(newfile, a->filename, sizeof(newfile)); if (rfc1524_expand_command(a, newfile, type, command, sizeof(command))) { /* For now, editing requires a file, no piping */ mutt_error(_("Mailcap compose entry requires %%s")); } else { int r; mutt_endwin(NULL); r = mutt_system(command); if (r == -1) mutt_error(_("Error running \"%s\"!"), command); if (r != -1 && entry->composetypecommand) { struct Body *b = NULL; FILE *fp = NULL, *tfp = NULL; char tempfile[_POSIX_PATH_MAX]; fp = mutt_file_fopen(a->filename, "r"); if (!fp) { mutt_perror(_("Failure to open file to parse headers.")); goto bailout; } b = mutt_read_mime_header(fp, 0); if (b) { if (b->parameter) { mutt_param_free(&a->parameter); a->parameter = b->parameter; b->parameter = NULL; } if (b->description) { FREE(&a->description); a->description = b->description; b->description = NULL; } if (b->form_name) { FREE(&a->form_name); a->form_name = b->form_name; b->form_name = NULL; } /* Remove headers by copying out data to another file, then * copying the file back */ fseeko(fp, b->offset, SEEK_SET); mutt_free_body(&b); mutt_mktemp(tempfile, sizeof(tempfile)); tfp = mutt_file_fopen(tempfile, "w"); if (!tfp) { mutt_perror(_("Failure to open file to strip headers.")); goto bailout; } mutt_file_copy_stream(fp, tfp); mutt_file_fclose(&fp); mutt_file_fclose(&tfp); mutt_file_unlink(a->filename); if (mutt_file_rename(tempfile, a->filename) != 0) { mutt_perror(_("Failure to rename file.")); goto bailout; } } } } } } else { rfc1524_free_entry(&entry); mutt_message(_("No mailcap compose entry for %s, creating empty file."), type); return 1; } rc = 1; bailout: if (unlink_newfile) unlink(newfile); rfc1524_free_entry(&entry); return rc; } /** * mutt_edit_attachment - Edit an attachment * @param a Email containing attachment * @retval 1 if editor found * @retval 0 if not * * Currently, this only works for send mode, as it assumes that the * Body->filename actually contains the information. I'm not sure * we want to deal with editing attachments we've already received, * so this should be ok. * * Returning 0 is useful to tell the calling menu to redraw */ int mutt_edit_attachment(struct Body *a) { char type[STRING]; char command[STRING]; char newfile[_POSIX_PATH_MAX] = ""; struct Rfc1524MailcapEntry *entry = rfc1524_new_entry(); bool unlink_newfile = false; int rc = 0; snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype); if (rfc1524_mailcap_lookup(a, type, entry, MUTT_EDIT)) { if (entry->editcommand) { mutt_str_strfcpy(command, entry->editcommand, sizeof(command)); if (rfc1524_expand_filename(entry->nametemplate, a->filename, newfile, sizeof(newfile))) { mutt_debug(1, "oldfile: %s\t newfile: %s\n", a->filename, newfile); if (mutt_file_symlink(a->filename, newfile) == -1) { if (mutt_yesorno(_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES) goto bailout; } else unlink_newfile = true; } else mutt_str_strfcpy(newfile, a->filename, sizeof(newfile)); if (rfc1524_expand_command(a, newfile, type, command, sizeof(command))) { /* For now, editing requires a file, no piping */ mutt_error(_("Mailcap Edit entry requires %%s")); goto bailout; } else { mutt_endwin(NULL); if (mutt_system(command) == -1) { mutt_error(_("Error running \"%s\"!"), command); goto bailout; } } } } else if (a->type == TYPETEXT) { /* On text, default to editor */ mutt_edit_file(NONULL(Editor), a->filename); } else { rfc1524_free_entry(&entry); mutt_error(_("No mailcap edit entry for %s"), type); return 0; } rc = 1; bailout: if (unlink_newfile) unlink(newfile); rfc1524_free_entry(&entry); return rc; } /** * mutt_check_lookup_list - Update the mime type * @param b Message attachment body * @param type Buffer with mime type of attachment in "type/subtype" format * @param len Buffer length */ void mutt_check_lookup_list(struct Body *b, char *type, size_t len) { int i; struct ListNode *np; STAILQ_FOREACH(np, &MimeLookupList, entries) { i = mutt_str_strlen(np->data) - 1; if ((i > 0 && np->data[i - 1] == '/' && np->data[i] == '*' && (mutt_str_strncasecmp(type, np->data, i) == 0)) || (mutt_str_strcasecmp(type, np->data) == 0)) { struct Body tmp = { 0 }; int n; n = mutt_lookup_mime_type(&tmp, b->filename); if (n != TYPEOTHER) { snprintf(type, len, "%s/%s", n == TYPEAUDIO ? "audio" : n == TYPEAPPLICATION ? "application" : n == TYPEIMAGE ? "image" : n == TYPEMESSAGE ? "message" : n == TYPEMODEL ? "model" : n == TYPEMULTIPART ? "multipart" : n == TYPETEXT ? "text" : n == TYPEVIDEO ? "video" : "other", tmp.subtype); mutt_debug(1, "\"%s\" -> %s\n", b->filename, type); } if (tmp.subtype) FREE(&tmp.subtype); if (tmp.xtype) FREE(&tmp.xtype); } } } /** * mutt_view_attachment - View an attachment * @param fp Source file stream. Can be NULL * @param a The message body containing the attachment * @param flag Option flag for how the attachment should be viewed * @param hdr Message header for the current message. Can be NULL * @param actx Attachment context * @retval 0 If the viewer is run and exited successfully * @retval -1 Error * @retval n Return value of mutt_do_pager() when it is used * * flag can be one of: #MUTT_MAILCAP, #MUTT_REGULAR, #MUTT_AS_TEXT * * Display a message attachment using the viewer program configured in mailcap. * If there is no mailcap entry for a file type, view the image as text. * Viewer processes are opened and waited on synchronously so viewing an * attachment this way will block the main neomutt process until the viewer process * exits. * */ int mutt_view_attachment(FILE *fp, struct Body *a, int flag, struct Header *hdr, struct AttachCtx *actx) { char tempfile[_POSIX_PATH_MAX] = ""; char pagerfile[_POSIX_PATH_MAX] = ""; bool is_message = false; bool use_mailcap = false; bool use_pipe = false; bool use_pager = true; char type[STRING]; char command[HUGE_STRING]; char descrip[STRING]; char *fname = NULL; struct Rfc1524MailcapEntry *entry = NULL; int rc = -1; bool unlink_tempfile = false; is_message = mutt_is_message_type(a->type, a->subtype); if (WithCrypto && is_message && a->hdr && (a->hdr->security & ENCRYPT) && !crypt_valid_passphrase(a->hdr->security)) { return rc; } use_mailcap = (flag == MUTT_MAILCAP || (flag == MUTT_REGULAR && mutt_needs_mailcap(a))); snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype); if (use_mailcap) { entry = rfc1524_new_entry(); if (!rfc1524_mailcap_lookup(a, type, entry, 0)) { if (flag == MUTT_REGULAR) { /* fallback to view as text */ rfc1524_free_entry(&entry); mutt_error(_("No matching mailcap entry found. Viewing as text.")); flag = MUTT_AS_TEXT; use_mailcap = false; } else goto return_error; } } if (use_mailcap) { if (!entry->command) { mutt_error(_("MIME type not defined. Cannot view attachment.")); goto return_error; } mutt_str_strfcpy(command, entry->command, sizeof(command)); if (fp) { fname = mutt_str_strdup(a->filename); mutt_file_sanitize_filename(fname, 1); } else fname = a->filename; if (rfc1524_expand_filename(entry->nametemplate, fname, tempfile, sizeof(tempfile))) { if (fp == NULL && (mutt_str_strcmp(tempfile, a->filename) != 0)) { /* send case: the file is already there */ if (mutt_file_symlink(a->filename, tempfile) == -1) { if (mutt_yesorno(_("Can't match nametemplate, continue?"), MUTT_YES) == MUTT_YES) mutt_str_strfcpy(tempfile, a->filename, sizeof(tempfile)); else goto return_error; } else unlink_tempfile = true; } } else if (!fp) /* send case */ mutt_str_strfcpy(tempfile, a->filename, sizeof(tempfile)); if (fp) { /* recv case: we need to save the attachment to a file */ FREE(&fname); if (mutt_save_attachment(fp, a, tempfile, 0, NULL) == -1) goto return_error; mutt_file_chmod(tempfile, S_IRUSR); } use_pipe = rfc1524_expand_command(a, tempfile, type, command, sizeof(command)); use_pager = entry->copiousoutput; } if (use_pager) { if (fp && !use_mailcap && a->filename) { /* recv case */ mutt_str_strfcpy(pagerfile, a->filename, sizeof(pagerfile)); mutt_adv_mktemp(pagerfile, sizeof(pagerfile)); } else mutt_mktemp(pagerfile, sizeof(pagerfile)); } if (use_mailcap) { pid_t thepid = 0; int tempfd = -1, pagerfd = -1; if (!use_pager) mutt_endwin(NULL); if (use_pager || use_pipe) { if (use_pager && ((pagerfd = mutt_file_open(pagerfile, O_CREAT | O_EXCL | O_WRONLY)) == -1)) { mutt_perror("open"); goto return_error; } if (use_pipe && ((tempfd = open(tempfile, 0)) == -1)) { if (pagerfd != -1) close(pagerfd); mutt_perror("open"); goto return_error; } if ((thepid = mutt_create_filter_fd(command, NULL, NULL, NULL, use_pipe ? tempfd : -1, use_pager ? pagerfd : -1, -1)) == -1) { if (pagerfd != -1) close(pagerfd); if (tempfd != -1) close(tempfd); mutt_error(_("Cannot create filter")); goto return_error; } if (use_pager) { if (a->description) snprintf(descrip, sizeof(descrip), _("---Command: %-20.20s Description: %s"), command, a->description); else snprintf(descrip, sizeof(descrip), _("---Command: %-30.30s Attachment: %s"), command, type); } if ((mutt_wait_filter(thepid) || (entry->needsterminal && option(OPT_WAIT_KEY))) && !use_pager) mutt_any_key_to_continue(NULL); if (tempfd != -1) close(tempfd); if (pagerfd != -1) close(pagerfd); } else { /* interactive command */ int rv = mutt_system(command); if (rv == -1) mutt_debug(1, "Error running \"%s\"!", command); if ((rv != 0) || (entry->needsterminal && option(OPT_WAIT_KEY))) mutt_any_key_to_continue(NULL); } } else { /* Don't use mailcap; the attachment is viewed in the pager */ if (flag == MUTT_AS_TEXT) { /* just let me see the raw data */ if (fp) { /* Viewing from a received message. * * Don't use mutt_save_attachment() because we want to perform charset * conversion since this will be displayed by the internal pager. */ struct State decode_state; memset(&decode_state, 0, sizeof(decode_state)); decode_state.fpout = mutt_file_fopen(pagerfile, "w"); if (!decode_state.fpout) { mutt_debug(1, "mutt_file_fopen(%s) errno=%d %s\n", pagerfile, errno, strerror(errno)); mutt_perror(pagerfile); mutt_sleep(1); goto return_error; } decode_state.fpin = fp; decode_state.flags = MUTT_CHARCONV; mutt_decode_attachment(a, &decode_state); if (fclose(decode_state.fpout) == EOF) mutt_debug(1, "fclose(%s) errno=%d %s\n", pagerfile, errno, strerror(errno)); } else { /* in compose mode, just copy the file. we can't use * mutt_decode_attachment() since it assumes the content-encoding has * already been applied */ if (mutt_save_attachment(fp, a, pagerfile, 0, NULL)) goto return_error; } } else { /* Use built-in handler */ set_option(OPT_VIEW_ATTACH); /* disable the "use 'v' to view this part" * message in case of error */ if (mutt_decode_save_attachment(fp, a, pagerfile, MUTT_DISPLAY, 0)) { unset_option(OPT_VIEW_ATTACH); goto return_error; } unset_option(OPT_VIEW_ATTACH); } if (a->description) mutt_str_strfcpy(descrip, a->description, sizeof(descrip)); else if (a->filename) snprintf(descrip, sizeof(descrip), _("---Attachment: %s: %s"), a->filename, type); else snprintf(descrip, sizeof(descrip), _("---Attachment: %s"), type); } /* We only reach this point if there have been no errors */ if (use_pager) { struct Pager info; memset(&info, 0, sizeof(info)); info.fp = fp; info.bdy = a; info.ctx = Context; info.actx = actx; info.hdr = hdr; rc = mutt_do_pager(descrip, pagerfile, MUTT_PAGER_ATTACHMENT | (is_message ? MUTT_PAGER_MESSAGE : 0), &info); *pagerfile = '\0'; } else rc = 0; return_error: if (entry) rfc1524_free_entry(&entry); if (fp && tempfile[0]) { /* Restore write permission so mutt_file_unlink can open the file for writing */ mutt_file_chmod_add(tempfile, S_IWUSR); mutt_file_unlink(tempfile); } else if (unlink_tempfile) unlink(tempfile); if (pagerfile[0]) mutt_file_unlink(pagerfile); return rc; } /** * mutt_pipe_attachment - Pipe an attachment to a command * @retval 1 on success * @retval 0 on error */ int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile) { pid_t thepid; int out = -1; int rc = 0; if (outfile && *outfile) { out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY); if (out < 0) { mutt_perror("open"); return 0; } } mutt_endwin(NULL); if (fp) { /* recv case */ struct State s; memset(&s, 0, sizeof(struct State)); /* perform charset conversion on text attachments when piping */ s.flags = MUTT_CHARCONV; if (outfile && *outfile) thepid = mutt_create_filter_fd(path, &s.fpout, NULL, NULL, -1, out, -1); else thepid = mutt_create_filter(path, &s.fpout, NULL, NULL); if (thepid < 0) { mutt_perror(_("Can't create filter")); goto bail; } s.fpin = fp; mutt_decode_attachment(b, &s); mutt_file_fclose(&s.fpout); } else { /* send case */ FILE *ifp = NULL, *ofp = NULL; ifp = fopen(b->filename, "r"); if (!ifp) { mutt_perror("fopen"); if (outfile && *outfile) { close(out); unlink(outfile); } return 0; } if (outfile && *outfile) thepid = mutt_create_filter_fd(path, &ofp, NULL, NULL, -1, out, -1); else thepid = mutt_create_filter(path, &ofp, NULL, NULL); if (thepid < 0) { mutt_perror(_("Can't create filter")); mutt_file_fclose(&ifp); goto bail; } mutt_file_copy_stream(ifp, ofp); mutt_file_fclose(&ofp); mutt_file_fclose(&ifp); } rc = 1; bail: if (outfile && *outfile) close(out); /* * check for error exit from child process */ if (mutt_wait_filter(thepid) != 0) rc = 0; if (rc == 0 || option(OPT_WAIT_KEY)) mutt_any_key_to_continue(NULL); return rc; } static FILE *save_attachment_open(char *path, int flags) { if (flags == MUTT_SAVE_APPEND) return fopen(path, "a"); if (flags == MUTT_SAVE_OVERWRITE) return fopen(path, "w"); return mutt_file_fopen(path, "w"); } /** * mutt_save_attachment - Save an attachment * @param fp Source file stream. Can be NULL * @param m Email Body * @param path Where to save the attachment * @param flags Flags, e.g. #MUTT_SAVE_APPEND * @param hdr Message header for the current message. Can be NULL * @retval 0 Success * @retval -1 Error */ int mutt_save_attachment(FILE *fp, struct Body *m, char *path, int flags, struct Header *hdr) { if (!m) return -1; if (fp) { /* recv mode */ if (hdr && m->hdr && m->encoding != ENCBASE64 && m->encoding != ENCQUOTEDPRINTABLE && mutt_is_message_type(m->type, m->subtype)) { /* message type attachments are written to mail folders. */ char buf[HUGE_STRING]; struct Header *hn = NULL; struct Context ctx; struct Message *msg = NULL; int chflags = 0; int r = -1; hn = m->hdr; hn->msgno = hdr->msgno; /* required for MH/maildir */ hn->read = true; if (fseeko(fp, m->offset, SEEK_SET) < 0) return -1; if (fgets(buf, sizeof(buf), fp) == NULL) return -1; if (mx_open_mailbox(path, MUTT_APPEND | MUTT_QUIET, &ctx) == NULL) return -1; msg = mx_open_new_message(&ctx, hn, is_from(buf, NULL, 0, NULL) ? 0 : MUTT_ADD_FROM); if (!msg) { mx_close_mailbox(&ctx, NULL); return -1; } if (ctx.magic == MUTT_MBOX || ctx.magic == MUTT_MMDF) chflags = CH_FROM | CH_UPDATE_LEN; chflags |= (ctx.magic == MUTT_MAILDIR ? CH_NOSTATUS : CH_UPDATE); if (mutt_copy_message_fp(msg->fp, fp, hn, 0, chflags) == 0 && mx_commit_message(msg, &ctx) == 0) { r = 0; } else { r = -1; } mx_close_message(&ctx, &msg); mx_close_mailbox(&ctx, NULL); return r; } else { /* In recv mode, extract from folder and decode */ struct State s; memset(&s, 0, sizeof(s)); s.fpout = save_attachment_open(path, flags); if (!s.fpout) { mutt_perror("fopen"); mutt_sleep(2); return -1; } fseeko((s.fpin = fp), m->offset, SEEK_SET); mutt_decode_attachment(m, &s); if (mutt_file_fsync_close(&s.fpout) != 0) { mutt_perror("fclose"); mutt_sleep(2); return -1; } } } else { if (!m->filename) return -1; /* In send mode, just copy file */ FILE *ofp = NULL, *nfp = NULL; ofp = fopen(m->filename, "r"); if (!ofp) { mutt_perror("fopen"); return -1; } nfp = save_attachment_open(path, flags); if (!nfp) { mutt_perror("fopen"); mutt_file_fclose(&ofp); return -1; } if (mutt_file_copy_stream(ofp, nfp) == -1) { mutt_error(_("Write fault!")); mutt_file_fclose(&ofp); mutt_file_fclose(&nfp); return -1; } mutt_file_fclose(&ofp); if (mutt_file_fsync_close(&nfp) != 0) { mutt_error(_("Write fault!")); return -1; } } return 0; } /** * mutt_decode_save_attachment - Decode, then save an attachment * @retval 0 on success * @retval -1 on error */ int mutt_decode_save_attachment(FILE *fp, struct Body *m, char *path, int displaying, int flags) { struct State s; unsigned int saved_encoding = 0; struct Body *saved_parts = NULL; struct Header *saved_hdr = NULL; int rc = 0; memset(&s, 0, sizeof(s)); s.flags = displaying; if (flags == MUTT_SAVE_APPEND) s.fpout = fopen(path, "a"); else if (flags == MUTT_SAVE_OVERWRITE) s.fpout = fopen(path, "w"); else s.fpout = mutt_file_fopen(path, "w"); if (!s.fpout) { mutt_perror("fopen"); return -1; } if (!fp) { /* When called from the compose menu, the attachment isn't parsed, * so we need to do it here. */ struct stat st; if (stat(m->filename, &st) == -1) { mutt_perror("stat"); mutt_file_fclose(&s.fpout); return -1; } s.fpin = fopen(m->filename, "r"); if (!s.fpin) { mutt_perror("fopen"); return -1; } saved_encoding = m->encoding; if (!is_multipart(m)) m->encoding = ENC8BIT; m->length = st.st_size; m->offset = 0; saved_parts = m->parts; saved_hdr = m->hdr; mutt_parse_part(s.fpin, m); if (m->noconv || is_multipart(m)) s.flags |= MUTT_CHARCONV; } else { s.fpin = fp; s.flags |= MUTT_CHARCONV; } mutt_body_handler(m, &s); if (mutt_file_fsync_close(&s.fpout) != 0) { mutt_perror("fclose"); rc = -1; } if (!fp) { m->length = 0; m->encoding = saved_encoding; if (saved_parts) { mutt_free_header(&m->hdr); m->parts = saved_parts; m->hdr = saved_hdr; } mutt_file_fclose(&s.fpin); } return rc; } /** * mutt_print_attachment - Print out an attachment * * Ok, the difference between send and receive: * recv: Body->filename is a suggested name, and Context|Header points * to the attachment in mailbox which is encoded * send: Body->filename points to the un-encoded file which contains the * attachment */ int mutt_print_attachment(FILE *fp, struct Body *a) { char newfile[_POSIX_PATH_MAX] = ""; char type[STRING]; pid_t thepid; FILE *ifp = NULL, *fpout = NULL; bool unlink_newfile = false; snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype); if (rfc1524_mailcap_lookup(a, type, NULL, MUTT_PRINT)) { char command[_POSIX_PATH_MAX + STRING]; struct Rfc1524MailcapEntry *entry = NULL; int piped = false; mutt_debug(2, "Using mailcap...\n"); entry = rfc1524_new_entry(); rfc1524_mailcap_lookup(a, type, entry, MUTT_PRINT); if (rfc1524_expand_filename(entry->nametemplate, a->filename, newfile, sizeof(newfile))) { if (!fp) { if (mutt_file_symlink(a->filename, newfile) == -1) { if (mutt_yesorno(_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES) { rfc1524_free_entry(&entry); return 0; } mutt_str_strfcpy(newfile, a->filename, sizeof(newfile)); } else unlink_newfile = true; } } /* in recv mode, save file to newfile first */ if (fp && (mutt_save_attachment(fp, a, newfile, 0, NULL) != 0)) return 0; mutt_str_strfcpy(command, entry->printcommand, sizeof(command)); piped = rfc1524_expand_command(a, newfile, type, command, sizeof(command)); mutt_endwin(NULL); /* interactive program */ if (piped) { ifp = fopen(newfile, "r"); if (!ifp) { mutt_perror("fopen"); rfc1524_free_entry(&entry); return 0; } thepid = mutt_create_filter(command, &fpout, NULL, NULL); if (thepid < 0) { mutt_perror(_("Can't create filter")); rfc1524_free_entry(&entry); mutt_file_fclose(&ifp); return 0; } mutt_file_copy_stream(ifp, fpout); mutt_file_fclose(&fpout); mutt_file_fclose(&ifp); if (mutt_wait_filter(thepid) || option(OPT_WAIT_KEY)) mutt_any_key_to_continue(NULL); } else { int rc = mutt_system(command); if (rc == -1) mutt_debug(1, "Error running \"%s\"!", command); if ((rc != 0) || option(OPT_WAIT_KEY)) mutt_any_key_to_continue(NULL); } if (fp) mutt_file_unlink(newfile); else if (unlink_newfile) unlink(newfile); rfc1524_free_entry(&entry); return 1; } if ((mutt_str_strcasecmp("text/plain", type) == 0) || (mutt_str_strcasecmp("application/postscript", type) == 0)) { return (mutt_pipe_attachment(fp, a, NONULL(PrintCommand), NULL)); } else if (mutt_can_decode(a)) { /* decode and print */ int rc = 0; ifp = NULL; fpout = NULL; mutt_mktemp(newfile, sizeof(newfile)); if (mutt_decode_save_attachment(fp, a, newfile, MUTT_PRINTING, 0) == 0) { mutt_debug(2, "successfully decoded %s type attachment to %s\n", type, newfile); ifp = fopen(newfile, "r"); if (!ifp) { mutt_perror("fopen"); goto bail0; } mutt_debug(2, "successfully opened %s read-only\n", newfile); mutt_endwin(NULL); thepid = mutt_create_filter(NONULL(PrintCommand), &fpout, NULL, NULL); if (thepid < 0) { mutt_perror(_("Can't create filter")); goto bail0; } mutt_debug(2, "Filter created.\n"); mutt_file_copy_stream(ifp, fpout); mutt_file_fclose(&fpout); mutt_file_fclose(&ifp); if (mutt_wait_filter(thepid) != 0 || option(OPT_WAIT_KEY)) mutt_any_key_to_continue(NULL); rc = 1; } bail0: mutt_file_fclose(&ifp); mutt_file_fclose(&fpout); mutt_file_unlink(newfile); return rc; } else { mutt_error(_("I don't know how to print that!")); return 0; } } void mutt_actx_add_attach(struct AttachCtx *actx, struct AttachPtr *attach) { int i; if (actx->idxlen == actx->idxmax) { actx->idxmax += 5; mutt_mem_realloc(&actx->idx, sizeof(struct AttachPtr *) * actx->idxmax); mutt_mem_realloc(&actx->v2r, sizeof(short) * actx->idxmax); for (i = actx->idxlen; i < actx->idxmax; i++) actx->idx[i] = NULL; } actx->idx[actx->idxlen++] = attach; } void mutt_actx_add_fp(struct AttachCtx *actx, FILE *new_fp) { int i; if (actx->fp_len == actx->fp_max) { actx->fp_max += 5; mutt_mem_realloc(&actx->fp_idx, sizeof(FILE *) * actx->fp_max); for (i = actx->fp_len; i < actx->fp_max; i++) actx->fp_idx[i] = NULL; } actx->fp_idx[actx->fp_len++] = new_fp; } void mutt_actx_add_body(struct AttachCtx *actx, struct Body *new_body) { int i; if (actx->body_len == actx->body_max) { actx->body_max += 5; mutt_mem_realloc(&actx->body_idx, sizeof(struct Body *) * actx->body_max); for (i = actx->body_len; i < actx->body_max; i++) actx->body_idx[i] = NULL; } actx->body_idx[actx->body_len++] = new_body; } void mutt_actx_free_entries(struct AttachCtx *actx) { int i; for (i = 0; i < actx->idxlen; i++) { if (actx->idx[i]->content) actx->idx[i]->content->aptr = NULL; FREE(&actx->idx[i]->tree); FREE(&actx->idx[i]); } actx->idxlen = 0; actx->vcount = 0; for (i = 0; i < actx->fp_len; i++) mutt_file_fclose(&actx->fp_idx[i]); actx->fp_len = 0; for (i = 0; i < actx->body_len; i++) mutt_free_body(&actx->body_idx[i]); actx->body_len = 0; } void mutt_free_attach_context(struct AttachCtx **pactx) { struct AttachCtx *actx = NULL; if (!pactx || !*pactx) return; actx = *pactx; mutt_actx_free_entries(actx); FREE(&actx->idx); FREE(&actx->v2r); FREE(&actx->fp_idx); FREE(&actx->body_idx); FREE(pactx); } neomutt-neomutt-20171215/attach.h000066400000000000000000000067261321473123000165660ustar00rootroot00000000000000/** * @file * Handling of email attachments * * @authors * Copyright (C) 1996-2000 Michael R. Elkins * * @copyright * 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, see . */ /* common protos for compose / attach menus */ #ifndef _MUTT_ATTACH_H #define _MUTT_ATTACH_H #include #include struct Menu; struct Header; struct Body; /** * struct AttachPtr - An email to which things will be attached */ struct AttachPtr { struct Body *content; FILE *fp; /**< used in the recvattach menu. */ int parent_type; char *tree; int level; int num; bool unowned : 1; /**< don't unlink on detach */ unsigned int decrypted : 1; /**< not part of message as stored in the hdr->content. */ }; /** * struct AttachCtx - A set of attachments */ struct AttachCtx { struct Header *hdr; /**< used by recvattach for updating */ FILE *root_fp; /**< used by recvattach for updating */ struct AttachPtr **idx; short idxlen; short idxmax; short *v2r; /**< mapping from virtual to real attachment */ short vcount; /**< the number of virtual attachments */ FILE **fp_idx; /**< Extra FILE* used for decryption */ short fp_len; short fp_max; struct Body **body_idx; /**< Extra struct Body* used for decryption */ short body_len; short body_max; }; void mutt_attach_init(struct AttachCtx *actx); void mutt_update_tree(struct AttachCtx *actx); int mutt_view_attachment(FILE *fp, struct Body *a, int flag, struct Header *hdr, struct AttachCtx *actx); int mutt_tag_attach(struct Menu *menu, int n, int m); int mutt_attach_display_loop(struct Menu *menu, int op, struct Header *hdr, struct AttachCtx *acvtx, bool recv); void mutt_save_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, struct Header *hdr, struct Menu *menu); void mutt_pipe_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, bool filter); void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top); void mutt_attach_bounce(FILE *fp, struct AttachCtx *actx, struct Body *cur); void mutt_attach_resend(FILE *fp, struct AttachCtx *actx, struct Body *cur); void mutt_attach_forward(FILE *fp, struct Header *hdr, struct AttachCtx *actx, struct Body *cur, int flags); void mutt_attach_reply(FILE *fp, struct Header *hdr, struct AttachCtx *actx, struct Body *cur, int flags); void mutt_actx_add_attach(struct AttachCtx *actx, struct AttachPtr *attach); void mutt_actx_add_fp(struct AttachCtx *actx, FILE *new_fp); void mutt_actx_add_body(struct AttachCtx *actx, struct Body *new_body); void mutt_actx_free_entries(struct AttachCtx *actx); void mutt_free_attach_context(struct AttachCtx **pactx); #endif /* _MUTT_ATTACH_H */ neomutt-neomutt-20171215/auto.def000066400000000000000000000726271321473123000166040ustar00rootroot00000000000000# vim: set ft=tcl syntax=tcl ts=2 sw=2 expandtab: # Make sure we use /usr as a default prefix on Linux; we can't use $host_ # variables because this needs to go before the inclusion of the system module. if {![catch {exec uname} out] && $out eq {Linux}} { define defaultprefix /usr } # Standard autosetup modules and our own modules use system cc cc-lib mutt-gettext mutt-iconv ############################################################################### # Names and versions define PACKAGE "neomutt" define PACKAGE_VERSION "20171215" define BUGS_ADDRESS "neomutt-devel@neomutt.org" # Subdirectories that contain additional Makefile.autosetup files set subdirs {po doc contrib} ############################################################################### ############################################################################### # Add any user options here options { # Curses / S-Lang with-ui:=ncurses => "Select ncurses or slang for the UI" with-ncurses:path => "Location of ncurses" with-slang:path => "Location of S-Lang" # Features w/o 3rd party dependencies doc=1 => "Disable building the documentation" full-doc=0 => "Build the full documentation set" docdir:path => "Documentation root" flock=0 => "Use flock() to lock files" fcntl=1 => "Do NOT use fcntl() to lock files" fmemopen=0 => "Use fmemopen() for temporary in-memory files" locales-fix=0 => "Enable locales fix" pgp=1 => "Disable PGP support" smime=1 => "Disable SMIME support" mixmaster=0 => "Enable Mixmaster support" with-mixmaster:=mixmaster => "Location of the mixmaster executable" homespool=0 => "Enable new mail spooling in the user's HOME" with-homespool:mailbox => "File in the user's HOME where new mail is spooled" with-mailpath:/var/mail => "Directory where spool mailboxes are located" with-domain:domain => "Specify your DNS domain name" # Crypto # OpenSSL or GnuTLS ssl=0 => "Enable TLS support using OpenSSL" with-ssl:path => "Location of OpenSSL" gnutls=0 => "Enable TLS support using GnuTLS" with-gnutls:path => "Location of GnuTLS" # GPGME gpgme=0 => "Enable GPGME support" with-gpgme:path => "Location of GPGME" # GSS (IMAP auth) gss=0 => "Use GSSAPI authentication for IMAP" with-gss:path => "Location of GSSAPI library" # SASL (IMAP and POP auth) sasl=0 => "Use the SASL network security library" with-sasl:path => "Location of the SASL network security library" # Lua lua=0 => "Enable Lua scripting support" with-lua:path => "Location of Lua" # Notmuch notmuch=0 => "Enable Notmuch support" with-notmuch:path => "Location of Notmuch" # NLS nls=1 => "Disable Native Language Support" with-nls:path => "Location of libintl" # IDN idn=1 => "Disable GNU libidn for internationalized domain names" with-idn:path => "Location of GNU libidn" # Header cache bdb=0 => "Use BerkeleyDB for the header cache" with-bdb:path => "Location of BerkeleyDB" gdbm=0 => "Use GNU dbm for the header cache" with-gdbm:path => "Location of GNU dbm" kyotocabinet=0 => "Use KyotoCabinet for the header cache" with-kyotocabinet:path => "Location of KyotoCabinet" lmdb=0 => "Use LMDB for the header cache" with-lmdb:path => "Location of LMDB" qdbm=0 => "Use QDBM for the header cache" with-qdbm:path => "Location of QDBM" tokyocabinet=0 => "Use TokyoCabinet for the header cache" with-tokyocabinet:path => "Location of TokyoCabinet" # Enable all options everything=0 => "Enable all options" } ############################################################################### ############################################################################### # All boolean options are converted to want-* definitions here. Further down, # their values is checked with [get-define opt]. This facilitates the handling # of dependencies among options (see "everything"). if {1} { # Keep sorted, please. foreach opt { bdb doc everything fcntl flock fmemopen full-doc gdbm gnutls gpgme gss homespool idn kyotocabinet lmdb locales-fix lua mixmaster nls notmuch pgp qdbm sasl smime ssl tokyocabinet } { define want-$opt [opt-bool $opt] } # These options support a --with-opt parameter. If that is set, force the # relative --enable-opt to true. This allows "--with-opt=/usr" to be used as # a shortcut for "--opt --with-opt=/usr". foreach opt { bdb gdbm gnutls gpgme gss homespool idn kyotocabinet lmdb lua mixmaster ncurses nls notmuch qdbm sasl slang ssl tokyocabinet } { if {[opt-val with-$opt] ne {}} { define want-$opt 1 } } # No more usage of [opt-bool] below this point. proc opt-bool {args} { user-error "opt-bool should not be called here" } } ############################################################################### ############################################################################### # Paths define BINDIR [get-define bindir] define MUTTLOCALEDIR [get-define datadir]/locale define PKGDATADIR [get-define datadir]/neomutt define PKGDOCDIR [opt-val docdir [get-define datadir]/doc/neomutt] define SYSCONFDIR [get-define sysconfdir] ############################################################################### ############################################################################### # Helper functions if {1} { # Check for a header file and a function in a library proc check-inc-and-lib {name prefix header fun lib} { cc-with [list -cflags -I$prefix/include -libs -L$prefix/lib] { if {[cc-check-includes $header] && [cc-check-function-in-lib $fun $lib]} { define-append CFLAGS -I$prefix/include define-append LDFLAGS -L$prefix/lib define-feature $name } } have-feature $name } # Get the value of a macro by preprocessing a header file that defines it proc check-define-value {incfile macro} { set code "#include \"$incfile\"\n$macro" if {[catch {exec [get-define CC] -xc - -E | tail -1 << $code} out]} { set out "" } set out } # Convert a string into a representation suitable for a C char[] proc text2c {s} { set result "\n " set i 0 foreach c [split $s {}] { append result "0x[format %02x [scan $c %c]], " if {[expr {[incr i] % 12 == 0}]} { append result "\n " set i 0 } } append result "0x00\n"; } # Guess what.. proc yesno val { expr {$val ? "yes" : "no"} } } ############################################################################### ############################################################################### # C compiler definitions and related tools if {1} { # First off, require c99 if {[cc-check-standards c99] eq {}} { user-error "C99 is required" } define-append CFLAGS_FOR_BUILD -std=c99 # Check for tools and programs cc-check-tools ar ranlib strip cc-check-progs install if {![cc-path-progs sendmail]} { define SENDMAIL /usr/sbin/sendmail } # Version of the C compiler set CC [get-define CC] if {[catch {exec $CC -v} cc_version]} { if {[catch {exec $CC --version} cc_version]} { if {[catch {exec $CC -V} cc_version]} { set cc_version "unknown compiler" } } } # GCC-specifc CFLAGS if {![catch {exec [get-define CC] --version} res]} { if {[regexp -nocase gcc $res]} { define-append CFLAGS "-fno-delete-null-pointer-checks" } } # Enable extensions (reverse-engineered from AC_SYSTEM_EXTENSIONS) if {1} { if {[cc-check-includes minix/config.h]} { lappend extensions -D_POSIX_SOURCE=1 lappend extensions -D_POSIX_1_SOURCE=2 lappend extensions -D_MINIX=1 } lappend extensions -D_ALL_SOURCE=1 lappend extensions -D_GNU_SOURCE=1 lappend extensions -D__EXTENSIONS__ define-append CFLAGS_FOR_BUILD {*}$extensions define-append CFLAGS {*}$extensions } cc-with [list -cflags [get-define CFLAGS]] # Endianness cc-check-endian if {[have-feature BIG_ENDIAN]} { define WORDS_BIGENDIAN } # Large file support if {[cc-check-lfs]} { define OFF_T_FMT {"%" PRId64} } else { define OFF_T_FMT {"%" PRId32} } define LOFF_T off_t } ############################################################################### ############################################################################### # signal-related checks # Let's always use volatile for sig_atomic_t cc-check-includes signal.h define SIG_ATOMIC_VOLATILE_T "volatile sig_atomic_t" cc-with {-includes "signal.h unistd.h"} { cc-check-decls sys_siglist } ############################################################################### ############################################################################### # Check for includes and functions that in the code are surrounded by # '#ifdef HAVE_FOO_H' and '#ifdef HAVE_FUNCTION' and for functions that might # be in different libraries if {1} { cc-check-includes \ ioctl.h \ sys/ioctl.h \ sys/syscall.h \ sysexits.h cc-check-functions \ fgetc_unlocked \ futimens \ getaddrinfo \ getsid \ iswblank \ mkdtemp \ strsep \ vasprintf \ wcscasecmp cc-check-function-in-lib gethostent nsl cc-check-function-in-lib setsockopt socket cc-check-function-in-lib getaddrinfo_a anl } ############################################################################### ############################################################################### # Various unconditional defines define USE_COMPRESSED define USE_IMAP define USE_NNTP define USE_POP define USE_SIDEBAR define USE_SMTP define USE_SOCKET define SUN_ATTACHMENT ############################################################################### set prefix [get-define prefix] ############################################################################### # Everything if {[get-define want-everything]} { foreach opt {gpgme pgp smime notmuch lua tokyocabinet kyotocabinet bdb gdbm qdbm lmdb} { define want-$opt append conf_options "--$opt " } } else { set conf_options "$::argv" } ############################################################################### # flock(1) if {[get-define want-flock]} {define USE_FLOCK} ############################################################################### # fcntl(1) if {[get-define want-fcntl]} {define USE_FCNTL} ############################################################################### # Locales fix if {[get-define want-locales-fix]} {define LOCALES_HACK} ############################################################################### # Documentation if {[get-define want-doc]} { if {![cc-check-progs xsltproc]} { user-error "Unable to find xsltproc" } define BUILD_DOC } if {[get-define want-full-doc]} {define MAKEDOC_FULL} ############################################################################### # GPGME if {[get-define want-gpgme]} { set gpgme_cflags {} if {[is-defined _FILE_OFFSET_BITS]} { set gpgme_cflags -D_FILE_OFFSET_BITS=[get-define _FILE_OFFSET_BITS] } cc-with [list -cflags $gpgme_cflags] { if {![check-inc-and-lib gpgme [opt-val with-gpgme $prefix] \ gpgme.h gpgme_new gpgme]} { user-error "Unable to find GPGME" } cc-check-functions gpgme_op_export_keys } define CRYPT_BACKEND_GPGME } ############################################################################### # PGP if {[get-define want-pgp]} { define-feature PGP define CRYPT_BACKEND_CLASSIC_PGP } ############################################################################### # SMIME if {[get-define want-smime]} { define-feature SMIME define CRYPT_BACKEND_CLASSIC_SMIME } ############################################################################### # SASL if {[get-define want-sasl]} { foreach sasl_lib {sasl2 sasl} { if {[check-inc-and-lib sasl [opt-val with-sasl $prefix] \ sasl/sasl.h sasl_encode64 $sasl_lib]} { # RHEL6 doesn't have this function yet cc-check-functions sasl_client_done define USE_SASL break } } if {![get-define USE_SASL]} { user-error "Unable to find SASL" } } ############################################################################### # Lua if {[get-define want-lua]} { set lua_versions { 5.3 5.2 } ;# Will be checked in order apply {{lua_prefix lua_versions} { foreach ver $lua_versions { lassign [split $ver .] maj min foreach lua_suffix [list /lua${maj}${min} /lua${maj}.${min} /lua {}] { msg-checking "Checking for include$lua_suffix/lua.h..." if {[file exists $lua_prefix/include$lua_suffix/lua.h]} { msg-result "yes" set libs [list lua-${maj}.${min} lua${maj}.${min} lua] cc-with [list -libs "-L$lua_prefix/lib"] { if {![cc-check-function-in-lib luaL_openlibs $libs]} { continue } } define-append CFLAGS -I$lua_prefix/include$lua_suffix define-append LDFLAGS -L$lua_prefix/lib define USE_LUA return } msg-result "no" } } user-error "Unable to find Lua" }} [opt-val with-lua $prefix] $lua_versions } ############################################################################### # Notmuch if {[get-define want-notmuch]} { if {![check-inc-and-lib notmuch [opt-val with-notmuch $prefix] \ notmuch.h notmuch_database_open notmuch]} { user-error "Unable to find Notmuch" } define USE_NOTMUCH msg-checking "Checking for Notmuch API version 3..." if {[cctest -includes {notmuch.h} \ -code { notmuch_database_open("/path", NOTMUCH_DATABASE_MODE_READ_ONLY, (notmuch_database_t**)NULL); }]} { define NOTMUCH_API_3 msg-result "yes" } else { msg-result "no" } } ############################################################################### # Native Language Support (NLS) if {[get-define want-nls]} { if {![check-gettext [opt-val with-nls $prefix]]} { user-error "Unable to find gettext. Consider --disable-nls" } if {![cc-check-progs msgfmt msgmerge xgettext]} { user-error "Unable to find gettext tools (msgfmt, msgmerge, xgettext).\ Consider --disable-nls" } } ############################################################################### # fmemopen(3) if {[get-define want-fmemopen]} { if {![cc-check-functions fmemopen]} { user-error "Unable to find fmemopen" } if {![cc-check-functions open_memstream]} { user-error "Unable to find open_memstream" } define USE_FMEMOPEN 1 } else { define USE_FMEMOPEN 0 } ############################################################################### # Ncurses / S-Lang switch [opt-val with-ui ncurses] { ncurses { define-append CFLAGS -DNCURSES_WIDECHAR # Locate the library defining waddnwstr() set ncurses_prefix [opt-val with-ncurses $prefix] cc-with [list -libs -L$ncurses_prefix/lib] { foreach ncurses_lib {ncursesw ncurses curses} { if {[cc-check-function-in-lib waddnwstr $ncurses_lib]} { break } } } if {![have-feature waddnwstr]} { user-error "Unable to find ncursesw library" } # Locate the directory containing ncurses.h # See https://github.com/neomutt/neomutt/pull/679 set found 0 cc-with [list -cflags -I$ncurses_prefix/include] { foreach ncurses_inc {ncursesw/ ncurses/ curses/ {}} { if {[cc-check-includes ${ncurses_inc}ncurses.h] || [cc-check-includes ${ncurses_inc}curses.h]} { set found 1 break } } } if {!$found} { user-error "Unable to find ncurses headers" } cc-with [list -libs -L$ncurses_prefix/lib] { if {![cc-check-function-in-lib tgetent $ncurses_lib]} { cc-check-function-in-lib tgetent tinfo } foreach f {start_color typeahead bkgdset curs_set meta use_default_colors resizeterm} { cc-check-function-in-lib $f $ncurses_lib } cc-check-functions use_extended_names } if {[have-feature start_color]} { define-feature COLOR } } slang { cc-with {-includes sys/param.h} { if {[cc-check-defines BSD]} { cc-check-function-in-lib initscr termlib } } if {![check-inc-and-lib slang [opt-val with-slang $prefix] \ slcurses.h SLtt_get_terminfo slang]} { user-error "Unable to find S-Lang" } define USE_SLANG_CURSES define-feature COLOR define-feature RESIZETERM } default { user-error "Invalid value for --with-ui=[opt-val with-ui], select ncurses\ or slang" } } ############################################################################### # Iconv - try to mimic AM_ICONV by preferring an installed libiconv if {![check-iconv $prefix]} { user-error "Unable to find iconv()" } ############################################################################### # Mailpath and homespool if {[get-define want-homespool]} { define MAILPATH [opt-val with-homespool mailbox] define HOMESPOOL 1 } else { define MAILPATH [opt-val with-mailpath /var/mail] } ############################################################################### # Mixmaster if {[get-define want-mixmaster]} { define MIXMASTER [opt-val with-mixmaster mixmaster] } ############################################################################### # Domain if {[opt-val with-domain] ne {}} { define DOMAIN [opt-val with-domain] } ############################################################################### # TLS support if {[get-define want-ssl] && ![get-define want-gnutls]} { # OpenSSL set ssl_prefix [opt-val with-ssl $prefix] set ssl_cflags -I$ssl_prefix/include set ssl_ldflags -L$ssl_prefix/lib cc-with [list -libs $ssl_ldflags -cflags $ssl_cflags] { if {![cc-check-includes openssl/bio.h openssl/err.h openssl/ssl.h] || ![cc-check-function-in-lib X509_STORE_CTX_new crypto] || ![cc-check-function-in-lib SSL_new ssl] || ![cc-with {-includes openssl/ssl.h} {cc-check-decls SSL_set_mode}]} { user-error "Unable to find OpenSSL" } define-append CFLAGS $ssl_cflags define-append LDFLAGS $ssl_ldflags cc-check-functions RAND_status RAND_egd cc-check-function-in-lib deflate z } define USE_SSL define USE_SSL_OPENSSL if {[cc-with {-includes openssl/ssl.h} { cc-check-decls X509_V_FLAG_PARTIAL_CHAIN}]} { define-feature SSL_PARTIAL_CHAIN } } elseif {[get-define want-gnutls]} { # GnuTLS set gnutls_prefix [opt-val with-gnutls $prefix] cc-with [list -cflags -I$gnutls_prefix/include -libs -L$gnutls_prefix/lib] { if {![cc-check-function-in-lib gnutls_check_version gnutls]} { user-error "Unable to find GnuTLS" } define-append CFLAGS -I$gnutls_prefix/include define-append LDFLAGS -L$gnutls_prefix/lib cc-check-function-in-lib gnutls_priority_set_direct gnutls cc-with {-includes {gnutls/x509.h gnutls/gnutls.h}} { cc-check-decls GNUTLS_VERIFY_DISABLE_TIME_CHECKS cc-check-types gnutls_certificate_credentials_t \ gnutls_certificate_status_t \ gnutls_datum_t \ gnutls_digest_algorithm_t \ gnutls_session_t \ gnutls_transport_ptr_t \ gnutls_x509_crt_t } } define USE_SSL define USE_SSL_GNUTLS } ############################################################################### # GNU libidn if {[get-define want-idn]} { set idn_prefix [opt-val with-idn $prefix] set sprep 0 set idna 0 cc-with [list -cflags -I$idn_prefix/include -libs -L$idn_prefix/lib] { incr sprep [cc-check-includes stringprep.h] incr sprep [cc-check-includes idn/stringprep.h] incr idna [cc-check-includes idna.h] incr idna [cc-check-includes idn/idna.h] set sprcv [cc-check-function-in-lib stringprep_check_version idn] if {$sprep == 0 || $idna == 0 || $sprcv == 0} { user-error "Unable to find GNU libidn" } define-feature libidn define-append CFLAGS -I$idn_prefix/include define-append LDFLAGS -L$idn_prefix/lib cc-check-functions idna_to_unicode_utf8_from_utf8 idna_to_unicode_8z8z cc-check-functions idna_to_ascii_from_utf8 idna_to_ascii_8z cc-check-functions idna_to_ascii_lz idna_to_ascii_from_locale } } ############################################################################### # Header cache - bdb if {[get-define want-bdb]} { set bdb_versions { 5.3 6.2 4.8 } ;# Will be checked in order set bdb_prefix [opt-val with-bdb $prefix] foreach ver $bdb_versions { lassign [split $ver .] maj min # This is ugly, but it allows us to not have an inner loop lappend bdb_majors $maj $maj $maj $maj $maj $maj lappend bdb_minors $min $min $min $min $min $min lappend bdb_exploded "" db-$maj-$min db${maj}${min} db$maj.$min db-$maj db$maj } foreach maj $bdb_majors min $bdb_minors ver $bdb_exploded { set ver_inc_dir $bdb_prefix/include/$ver set ver_lib_dir $bdb_prefix/lib/$ver set ver_inc_file $ver_inc_dir/db.h set ver_lib_file db-$maj.$min # File exists? msg-checking "Checking for BerkeleyDB in $ver_inc_dir..." if {![file exists $ver_inc_file]} { msg-result "no" continue } # Version is coherent? set inc_maj [check-define-value $ver_inc_file DB_VERSION_MAJOR] set inc_min [check-define-value $ver_inc_file DB_VERSION_MINOR] if {$inc_maj eq {} || $inc_min eq {} || $inc_maj != $maj || $inc_min != $min} { msg-result "no (expecting $maj.$min, got $inc_maj.$inc_min)" continue } msg-result "yes" # Can link? cc-with [list -libs -L$ver_lib_dir -cflags -I$ver_inc_dir] { if {![check-inc-and-lib bdb {} db.h db_env_create db-$maj.$min]} { msg-result "no" continue } } define-append CFLAGS -I$ver_inc_dir define-append LDFLAGS -L$ver_lib_dir define-append LIBS -ldb-$maj.$min define-append HCACHE_BACKENDS "bdb" define-append HCACHE_LIBS -ldb-$maj.$min define USE_HCACHE break } if {![have-feature bdb]} { user-error "Unable to find BerkeleyDB" } } ############################################################################### # Header Cache - GNU dbm if {[get-define want-gdbm]} { if {![check-inc-and-lib gdbm [opt-val with-gdbm $prefix] \ gdbm.h gdbm_open gdbm]} { user-error "Unable to find GNU dbm" } define-append HCACHE_BACKENDS "gdbm" define-append HCACHE_LIBS [get-define lib_gdbm_open] define USE_HCACHE } ############################################################################### # Header cache - LMDB if {[get-define want-lmdb]} { if {![check-inc-and-lib lmdb [opt-val with-lmdb $prefix] \ lmdb.h mdb_env_create lmdb]} { user-error "Unable to find LMDB" } define-append HCACHE_BACKENDS "lmdb" define-append HCACHE_LIBS [get-define lib_mdb_env_create] define USE_HCACHE } ############################################################################### # Header cache - KyotoCabinet if {[get-define want-kyotocabinet]} { if {![check-inc-and-lib kc [opt-val with-kyotocabinet $prefix] \ kclangc.h kcdbopen kyotocabinet]} { user-error "Unable to find KyotoCabinet" } define-append HCACHE_BACKENDS "kyotocabinet" define-append HCACHE_LIBS [get-define lib_kcdbopen] define USE_HCACHE } ############################################################################### # Header cache - QDBM if {[get-define want-qdbm]} { # On Linux, headers are in a dedicated subdirectory set qdbm_prefix [opt-val with-qdbm $prefix] if {[file isdirectory $qdbm_prefix/include/qdbm]} { set qdbm_inc_subdir qdbm/ } else { set qdbm_inc_subdir "" } if {[check-inc-and-lib qdbm [opt-val with-qdbm $qdbm_prefix] \ ${qdbm_inc_subdir}villa.h vlopen qdbm]} { define-append CFLAGS -I$qdbm_prefix/include/$qdbm_inc_subdir } else { user-error "Unable to find QDBM" } define-append HCACHE_BACKENDS "qdbm" define-append HCACHE_LIBS [get-define lib_vlopen] define USE_HCACHE } ############################################################################### # Header Cache - TokyoCabinet if {[get-define want-tokyocabinet]} { if {![check-inc-and-lib tc [opt-val with-tokyocabinet $prefix] \ tcbdb.h tcbdbopen tokyocabinet]} { user-error "Unable to find TokyoCabinet" } define-append HCACHE_BACKENDS "tokyocabinet" define-append HCACHE_LIBS [get-define lib_tcbdbopen] define USE_HCACHE } ############################################################################### # GSS if {[get-define want-gss]} { # TODO - Use krb5-config only, which should be enough in any moderately # modern OS. If people report breakage, I'll implement the manual logic # later. set gss_prefix [opt-val with-gss $prefix] set krb5_config_guess [file join $gss_prefix bin krb5-config] if {[file-isexec $krb5_config_guess]} { define KRB5-CONFIG $krb5_config_guess } else { if {![cc-check-progs krb5-config]} { user-error "Unable to find krb5-config" } } msg-checking "Checking for a GSSAPI implementation..." # Cflags set krb5_config [get-define KRB5-CONFIG] if {[catch {exec-with-stderr $krb5_config --cflags gssapi} res err]} { user-error "Could not derive --cflags from $krb5_config" } define-append CFLAGS $res # Libs if {[catch {exec-with-stderr $krb5_config --libs gssapi} res err]} { user-error "Could not derive --libs from $krb5_config" } define-append LDFLAGS $res # Implementation if {[catch {exec-with-stderr $krb5_config --version} res err]} { user-error "Could not derive --version from $krb5_config" } switch -glob $res { "Kerberos 5 *" { set GSSAPI_IMPL "MIT" } "*eimdal*" { set GSSAPI_IMPL "Heimdal" } "Solaris*" { set GSSAPI_IMPL "Solaris" } default { set GSSAPI_IMPL "Unknown" } } msg-result $GSSAPI_IMPL if {$GSSAPI_IMPL in {Heimdal Solaris}} { define HAVE_HEIMDAL } define USE_GSS } ############################################################################### # Generate conststrings.c set conststrings "\ unsigned char cc_version\[\] = {[text2c $cc_version]};\n\ unsigned char cc_cflags\[\] = {[text2c [get-define CFLAGS]]};\n\ unsigned char configure_options\[\] = {[text2c $conf_options]};\n" if {[catch {set fd [open conststrings.c w] puts $fd $conststrings close $fd} msg]} { user-error "Cannot write conststrings.c: $msg" } ############################################################################### # Definitions that are going to be substituted in Makefiles and config.h set auto_rep { _* *_TARGETS BINDIR BUILD_DOC CRYPT_* DOMAIN ENABLE_* HAVE_* HOMESPOOL LOCALES_HACK MAILPATH MAKEDOC_FULL MIXMASTER MUTTLOCALEDIR NOTMUCH_API_3 PACKAGE PKGDATADIR PKGDOCDIR SENDMAIL SUN_ATTACHMENT SYSCONFDIR USE_* WORDS_BIGENDIAN } set bare_rep { ICONV_CONST LOFF_T OFF_T_FMT SIG_ATOMIC_VOLATILE_T } set str_rep { PACKAGE_VERSION } ############################################################################### # Use ccache - don't do it earlier than here if {[get-define CCACHE] ne {none}} { define CC "[get-define CCACHE] [get-define CC]" define CC_FOR_BUILD "[get-define CCACHE] [get-define CC_FOR_BUILD]" } ############################################################################### # Generate targets and Makefiles for subdirectories foreach dir $subdirs { define-append ALL_TARGETS all-$dir define-append CLEAN_TARGETS clean-$dir define-append INSTALL_TARGETS install-$dir define-append UNINSTALL_TARGETS uninstall-$dir make-template $dir/Makefile.autosetup $dir/Makefile } ############################################################################### # Generate Makefile and config.h define PWD [pwd] make-template Makefile.autosetup Makefile make-config-header config.h -auto $auto_rep -bare $bare_rep -str $str_rep ############################################################################### # Generate doc/neomutt.1 define bindir [get-define BINDIR] define docdir [get-define PKGDOCDIR] make-template doc/neomutt.man doc/neomutt.1 ############################################################################### # Print a summary user-notice "Summary of build options: Version: [get-define PACKAGE_VERSION] Host OS: [get-define host_os] Install prefix: [get-define prefix] Compiler: [get-define CC] CFlags: [get-define CFLAGS] LDFlags: [get-define LDFLAGS] Libs: [get-define LIBS] Header cache libs: [get-define HCACHE_LIBS {}] GPGME: [yesno [get-define CRYPT_BACKEND_GPGME]] PGP: [yesno [get-define CRYPT_BACKEND_CLASSIC_PGP]] SMIME: [yesno [get-define CRYPT_BACKEND_CLASSIC_SMIME]] Notmuch: [yesno [get-define USE_NOTMUCH]] Header Cache(s): [get-define HCACHE_BACKENDS {}] Lua: [yesno [get-define USE_LUA]] " neomutt-neomutt-20171215/autosetup/000077500000000000000000000000001321473123000171675ustar00rootroot00000000000000neomutt-neomutt-20171215/autosetup/LICENSE000066400000000000000000000033111321473123000201720ustar00rootroot00000000000000Unless explicitly stated, all files which form part of autosetup are released under the following license: --------------------------------------------------------------------- autosetup - A build environment "autoconfigurator" Copyright (c) 2010-2011, WorkWare Systems Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE WORKWARE SYSTEMS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WORKWARE SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of WorkWare Systems. neomutt-neomutt-20171215/autosetup/README.autosetup000066400000000000000000000005761321473123000221070ustar00rootroot00000000000000README.autosetup created by autosetup v0.6.8 This is the autosetup directory for a local install of autosetup. It contains autosetup, support files and loadable modules. *.tcl files in this directory are optional modules which can be loaded with the 'use' directive. *.auto files in this directory are auto-loaded. For more information, see http://msteveb.github.com/autosetup/ neomutt-neomutt-20171215/autosetup/autosetup000077500000000000000000001651351321473123000211610ustar00rootroot00000000000000#!/bin/sh # Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # vim:se syntax=tcl: # \ dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@" set autosetup(version) 0.6.8 # Can be set to 1 to debug early-init problems set autosetup(debug) [expr {"--debug" in $argv}] ################################################################## # # Main flow of control, option handling # proc main {argv} { global autosetup define # There are 3 potential directories involved: # 1. The directory containing autosetup (this script) # 2. The directory containing auto.def # 3. The current directory # From this we need to determine: # a. The path to this script (and related support files) # b. The path to auto.def # c. The build directory, where output files are created # This is also complicated by the fact that autosetup may # have been run via the configure wrapper ([getenv WRAPPER] is set) # Here are the rules. # a. This script is $::argv0 # => dir, prog, exe, libdir # b. auto.def is in the directory containing the configure wrapper, # otherwise it is in the current directory. # => srcdir, autodef # c. The build directory is the current directory # => builddir, [pwd] # 'misc' is needed before we can do anything, so set a temporary libdir # in case this is the development version set autosetup(libdir) [file dirname $::argv0]/lib use misc # (a) set autosetup(dir) [realdir [file dirname [realpath $::argv0]]] set autosetup(prog) [file join $autosetup(dir) [file tail $::argv0]] set autosetup(exe) [getenv WRAPPER $autosetup(prog)] if {$autosetup(installed)} { set autosetup(libdir) $autosetup(dir) } else { set autosetup(libdir) [file join $autosetup(dir) lib] } autosetup_add_dep $autosetup(prog) # (b) if {[getenv WRAPPER ""] eq ""} { # Invoked directly set autosetup(srcdir) [pwd] } else { # Invoked via the configure wrapper set autosetup(srcdir) [file-normalize [file dirname $autosetup(exe)]] } set autosetup(autodef) [relative-path $autosetup(srcdir)/auto.def] # (c) set autosetup(builddir) [pwd] set autosetup(argv) $argv set autosetup(cmdline) {} # options is a list of known options set autosetup(options) {} # optset is a dictionary of option values set by the user based on getopt set autosetup(optset) {} # optdefault is a dictionary of default values set autosetup(optdefault) {} # options-defaults is a dictionary of overrides for default values for options set autosetup(options-defaults) {} set autosetup(optionhelp) {} set autosetup(showhelp) 0 # Parse options use getopt # At the is point we don't know what is a valid option # We simply parse anything that looks like an option set autosetup(getopt) [getopt argv] #"=Core Options:" options-add { help:=local => "display help and options. Optionally specify a module name, such as --help=system" licence license => "display the autosetup license" version => "display the version of autosetup" ref:=text manual:=text reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'" debug => "display debugging output as autosetup runs" install:=. => "install autosetup to the current or given directory" } if {$autosetup(installed)} { # hidden options so we can produce a nice error options-add { sysinstall:path } } else { options-add { sysinstall:path => "install standalone autosetup to the given directory (e.g.: /usr/local)" } } options-add { force init:=help => "create initial auto.def, etc. Use --init=help for known types" # Undocumented options option-checking=1 nopager quiet timing conf: } if {[opt-bool version]} { puts $autosetup(version) exit 0 } # autosetup --conf=alternate-auto.def if {[opt-str conf o]} { set autosetup(autodef) $o } # Debugging output (set this early) incr autosetup(debug) [opt-bool debug] incr autosetup(force) [opt-bool force] incr autosetup(msg-quiet) [opt-bool quiet] incr autosetup(msg-timing) [opt-bool timing] # If the local module exists, source it now to allow for # project-local customisations if {[file exists $autosetup(libdir)/local.tcl]} { use local } # Now any auto-load modules autosetup_load_auto_modules if {[opt-str help o]} { incr autosetup(showhelp) use help autosetup_help $o } if {[opt-bool licence license]} { use help autosetup_show_license exit 0 } if {[opt-str {manual ref reference} o]} { use help autosetup_reference $o } # Allow combining --install and --init set earlyexit 0 if {[opt-str install o]} { use install autosetup_install $o incr earlyexit } if {[opt-str init o]} { use init autosetup_init $o incr earlyexit } if {$earlyexit} { exit 0 } if {[opt-str sysinstall o]} { use install autosetup_install $o 1 exit 0 } if {![file exists $autosetup(autodef)]} { # Check for invalid option first options {} user-error "No auto.def found in \"$autosetup(srcdir)\" (use [file tail $::autosetup(exe)] --init to create one)" } # Parse extra arguments into autosetup(cmdline) foreach arg $argv { if {[regexp {([^=]*)=(.*)} $arg -> n v]} { dict set autosetup(cmdline) $n $v define $n $v } else { user-error "Unexpected parameter: $arg" } } autosetup_add_dep $autosetup(autodef) define CONFIGURE_OPTS "" foreach arg $autosetup(argv) { define-append CONFIGURE_OPTS [quote-if-needed $arg] } define AUTOREMAKE [file-normalize $autosetup(exe)] define-append AUTOREMAKE [get-define CONFIGURE_OPTS] # Log how we were invoked configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]" # Note that auto.def is *not* loaded in the global scope source $autosetup(autodef) # Could warn here if options {} was not specified show-notices if {$autosetup(debug)} { msg-result "Writing all defines to config.log" configlog "================ defines ======================" foreach n [lsort [array names define]] { configlog "define $n $define($n)" } } exit 0 } # @opt-bool ?-nodefault? option ... # # Check each of the named, boolean options and if any have been explicitly enabled # or disabled by the user, return 1 or 0 accordingly. # # If the option was specified more than once, the last value wins. # e.g. With '--enable-foo --disable-foo', '[opt-bool foo]' will return 0 # # If no value was specified by the user, returns the default value for the # first option. If '-nodefault' is given, this behaviour changes and # -1 is returned instead. # proc opt-bool {args} { set nodefault 0 if {[lindex $args 0] eq "-nodefault"} { set nodefault 1 set args [lrange $args 1 end] } option-check-names {*}$args foreach opt $args { if {[dict exists $::autosetup(optset) $opt]} { return [dict get $::autosetup(optset) $opt] } } if {$nodefault} { return -1 } # Default value is the default for the first option return [dict get $::autosetup(optdefault) [lindex $args 0]] } # @opt-val optionlist ?default=""? # # Returns a list containing all the values given for the non-boolean options in '$optionlist'. # There will be one entry in the list for each option given by the user, including if the # same option was used multiple times. # # If no options were set, '$default' is returned (exactly, not as a list). # # Note: For most use cases, 'opt-str' should be preferred. # proc opt-val {names {default ""}} { option-check-names {*}$names foreach opt $names { if {[dict exists $::autosetup(optset) $opt]} { lappend result {*}[dict get $::autosetup(optset) $opt] } } if {[info exists result]} { return $result } return $default } # @opt-str optionlist varname ?default? # # Sets '$varname' in the callers scope to the value for one of the given options. # # For the list of options given in '$optionlist', if any value is set for any option, # the option value is taken to be the *last* value of the last option (in the order given). # # If no option was given, and a default was specified with 'options-defaults', # that value is used. # # If no 'options-defaults' value was given and '$default' was given, it is used. # # If none of the above provided a value, no value is set. # # The return value depends on whether '$default' was specified. # If it was, the option value is returned. # If it was not, 1 is returns if a value was set, or 0 if not. # # Typical usage is as follows: # ## if {[opt-str {myopt altname} o]} { ## do something with $o ## } # # Or: ## define myname [opt-str {myopt altname} o "/usr/local"] # proc opt-str {names varname args} { global autosetup option-check-names {*}$names upvar $varname value if {[llength $args]} { # A default was given, so always return the string value of the option set default [lindex $args 0] set retopt 1 } else { # No default, so return 0 or 1 to indicate if a value was found set retopt 0 } foreach opt $names { if {[dict exists $::autosetup(optset) $opt]} { set result [lindex [dict get $::autosetup(optset) $opt] end] } } if {![info exists result]} { # No user-specified value. Has options-defaults been set? foreach opt $names { if {[dict exists $::autosetup(options-defaults) $opt]} { set result [dict get $autosetup(options-defaults) $opt] } } } if {[info exists result]} { set value $result if {$retopt} { return $value } return 1 } if {$retopt} { set value $default return $value } return 0 } proc option-check-names {args} { foreach o $args { if {$o ni $::autosetup(options)} { autosetup-error "Request for undeclared option --$o" } } } # Parse the option definition in $opts and update # ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately # proc options-add {opts {header ""}} { global autosetup # First weed out comment lines set realopts {} foreach line [split $opts \n] { if {![string match "#*" [string trimleft $line]]} { append realopts $line \n } } set opts $realopts for {set i 0} {$i < [llength $opts]} {incr i} { set opt [lindex $opts $i] if {[string match =* $opt]} { # This is a special heading lappend autosetup(optionhelp) $opt "" set header {} continue } unset -nocomplain defaultvalue equal value #puts "i=$i, opt=$opt" regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value if {$name in $autosetup(options)} { autosetup-error "Option $name already specified" } #puts "$opt => $name $colon $equal $value" # Find the corresponding value in the user options # and set the default if necessary if {[string match "-*" $opt]} { # This is a documentation-only option, like "-C " set opthelp $opt } elseif {$colon eq ""} { # Boolean option lappend autosetup(options) $name # Check for override if {[dict exists $autosetup(options-defaults) $name]} { # A default was specified with options-defaults, so use it set value [dict get $autosetup(options-defaults) $name] } if {$value eq "1"} { set opthelp "--disable-$name" } else { set opthelp "--$name" } # Set the default if {$value eq ""} { set value 0 } set defaultvalue $value dict set autosetup(optdefault) $name $defaultvalue if {[dict exists $autosetup(getopt) $name]} { # The option was specified by the user. Look at the last value. lassign [lindex [dict get $autosetup(getopt) $name] end] type setvalue if {$type eq "str"} { # Can we convert the value to a boolean? if {$setvalue in {1 enabled yes}} { set setvalue 1 } elseif {$setvalue in {0 disabled no}} { set setvalue 0 } else { user-error "Boolean option $name given as --$name=$setvalue" } } dict set autosetup(optset) $name $setvalue #puts "Found boolean option --$name=$setvalue" } } else { # String option. lappend autosetup(options) $name if {$colon eq ":"} { # Was ":name=default" given? # If so, set $value to the display name and $defaultvalue to the default # (This is the preferred way to set a default value for a string option) if {[regexp {^([^=]+)=(.*)$} $value -> value defaultvalue]} { dict set autosetup(optdefault) $name $defaultvalue } } # Maybe override the default value if {[dict exists $autosetup(options-defaults) $name]} { # A default was specified with options-defaults, so use it set defaultvalue [dict get $autosetup(options-defaults) $name] dict set autosetup(optdefault) $name $defaultvalue } elseif {![info exists defaultvalue]} { # For backward compatiblity, if ":name" was given, use name as both # the display text and the default value, but only if the user # specified the option without the value set defaultvalue $value } if {$equal eq "="} { # String option with optional value set opthelp "--$name?=$value?" } else { # String option with required value set opthelp "--$name=$value" } # Get the values specified by the user if {[dict exists $autosetup(getopt) $name]} { set listvalue {} foreach pair [dict get $autosetup(getopt) $name] { lassign $pair type setvalue if {$type eq "bool" && $setvalue} { if {$equal ne "="} { user-error "Option --$name requires a value" } # If given as a boolean, use the default value set setvalue $defaultvalue } lappend listvalue $setvalue } #puts "Found string option --$name=$listvalue" dict set autosetup(optset) $name $listvalue } } # Now create the help for this option if appropriate if {[lindex $opts $i+1] eq "=>"} { set desc [lindex $opts $i+2] if {[info exists defaultvalue]} { set desc [string map [list @default@ $defaultvalue] $desc] } #string match \n* $desc if {$header ne ""} { lappend autosetup(optionhelp) $header "" set header "" } # A multi-line description lappend autosetup(optionhelp) $opthelp $desc incr i 2 } } } # @module-options optionlist # # Like 'options', but used within a module. proc module-options {opts} { set header "" if {$::autosetup(showhelp) > 1 && [llength $opts]} { set header "Module Options:" } options-add $opts $header if {$::autosetup(showhelp)} { # Ensure that the module isn't executed on --help # We are running under eval or source, so use break # to prevent further execution #return -code break -level 2 return -code break } } proc max {a b} { expr {$a > $b ? $a : $b} } proc options-wrap-desc {text length firstprefix nextprefix initial} { set len $initial set space $firstprefix foreach word [split $text] { set word [string trim $word] if {$word == ""} { continue } if {$len && [string length $space$word] + $len >= $length} { puts "" set len 0 set space $nextprefix } incr len [string length $space$word] puts -nonewline $space$word set space " " } if {$len} { puts "" } } proc options-show {} { # Determine the max option width set max 0 foreach {opt desc} $::autosetup(optionhelp) { if {[string match =* $opt] || [string match \n* $desc]} { continue } set max [max $max [string length $opt]] } set indent [string repeat " " [expr $max+4]] set cols [getenv COLUMNS 80] catch { lassign [exec stty size] rows cols } incr cols -1 # Now output foreach {opt desc} $::autosetup(optionhelp) { if {[string match =* $opt]} { puts [string range $opt 1 end] continue } puts -nonewline " [format %-${max}s $opt]" if {[string match \n* $desc]} { puts $desc } else { options-wrap-desc [string trim $desc] $cols " " $indent [expr $max + 2] } } } # @options optionspec # # Specifies configuration-time options which may be selected by the user # and checked with 'opt-str' and 'opt-bool'. '$optionspec' contains a series # of options specifications separated by newlines, as follows: # # A boolean option is of the form: # ## name[=0|1] => "Description of this boolean option" # # The default is 'name=0', meaning that the option is disabled by default. # If 'name=1' is used to make the option enabled by default, the description should reflect # that with text like "Disable support for ...". # # An argument option (one which takes a parameter) is of the form: # ## name:[=]value => "Description of this option" # # If the 'name:value' form is used, the value must be provided with the option (as '--name=myvalue'). # If the 'name:=value' form is used, the value is optional and the given value is used as the default # if it is not provided. # # The description may contain '@default@', in which case it will be replaced with the default # value for the option (taking into account defaults specified with 'options-defaults'. # # Undocumented options are also supported by omitting the '=> description'. # These options are not displayed with '--help' and can be useful for internal options or as aliases. # # For example, '--disable-lfs' is an alias for '--disable=largefile': # ## lfs=1 largefile=1 => "Disable large file support" # proc options {optlist} { # Allow options as a list or args options-add $optlist "Local Options:" if {$::autosetup(showhelp)} { options-show exit 0 } # Check for invalid options if {[opt-bool option-checking]} { foreach o [dict keys $::autosetup(getopt)] { if {$o ni $::autosetup(options)} { user-error "Unknown option --$o" } } } } # @options-defaults dictionary # # Specifies a dictionary of options and a new default value for each of those options. # Use before any 'use' statements in 'auto.def' to change the defaults for # subsequently included modules. proc options-defaults {dict} { foreach {n v} $dict { dict set ::autosetup(options-defaults) $n $v } } proc config_guess {} { if {[file-isexec $::autosetup(dir)/autosetup-config.guess]} { if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.guess} alias]} { user-error $alias } return $alias } else { configlog "No autosetup-config.guess, so using uname" string tolower [exec uname -p]-unknown-[exec uname -s][exec uname -r] } } proc config_sub {alias} { if {[file-isexec $::autosetup(dir)/autosetup-config.sub]} { if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.sub $alias} alias]} { user-error $alias } } return $alias } # @define name ?value=1? # # Defines the named variable to the given value. # These (name, value) pairs represent the results of the configuration check # and are available to be subsequently checked, modified and substituted. # proc define {name {value 1}} { set ::define($name) $value #dputs "$name <= $value" } # @undefine name # # Undefine the named variable. # proc undefine {name} { unset -nocomplain ::define($name) #dputs "$name <= " } # @define-append name value ... # # Appends the given value(s) to the given "defined" variable. # If the variable is not defined or empty, it is set to '$value'. # Otherwise the value is appended, separated by a space. # Any extra values are similarly appended. # If any value is already contained in the variable (as a substring) it is omitted. # proc define-append {name args} { if {[get-define $name ""] ne ""} { # Avoid duplicates foreach arg $args { set found 0 foreach str [split $::define($name) " "] { if {$str eq $arg} { incr found } } if {!$found} { append ::define($name) " " $arg } } } else { set ::define($name) [join $args] } #dputs "$name += [join $args] => $::define($name)" } # @get-define name ?default=0? # # Returns the current value of the "defined" variable, or '$default' # if not set. # proc get-define {name {default 0}} { if {[info exists ::define($name)]} { #dputs "$name => $::define($name)" return $::define($name) } #dputs "$name => $default" return $default } # @is-defined name # # Returns 1 if the given variable is defined. # proc is-defined {name} { info exists ::define($name) } # @all-defines # # Returns a dictionary (name, value list) of all defined variables. # # This is suitable for use with 'dict', 'array set' or 'foreach' # and allows for arbitrary processing of the defined variables. # proc all-defines {} { array get ::define } # @get-env name default # # If '$name' was specified on the command line, return it. # Otherwise if '$name' was set in the environment, return it. # Otherwise return '$default'. # proc get-env {name default} { if {[dict exists $::autosetup(cmdline) $name]} { return [dict get $::autosetup(cmdline) $name] } getenv $name $default } # @env-is-set name # # Returns 1 if '$name' was specified on the command line or in the environment. # Note that an empty environment variable is not considered to be set. # proc env-is-set {name} { if {[dict exists $::autosetup(cmdline) $name]} { return 1 } if {[getenv $name ""] ne ""} { return 1 } return 0 } # @readfile filename ?default=""? # # Return the contents of the file, without the trailing newline. # If the file doesn't exist or can't be read, returns '$default'. # proc readfile {filename {default_value ""}} { set result $default_value catch { set f [open $filename] set result [read -nonewline $f] close $f } return $result } # @writefile filename value # # Creates the given file containing '$value'. # Does not add an extra newline. # proc writefile {filename value} { set f [open $filename w] puts -nonewline $f $value close $f } proc quote-if-needed {str} { if {[string match {*[\" ]*} $str]} { return \"[string map [list \" \\" \\ \\\\] $str]\" } return $str } proc quote-argv {argv} { set args {} foreach arg $argv { lappend args [quote-if-needed $arg] } join $args } # @list-non-empty list # # Returns a copy of the given list with empty elements removed proc list-non-empty {list} { set result {} foreach p $list { if {$p ne ""} { lappend result $p } } return $result } # @find-executable-path name # # Searches the path for an executable with the given name. # Note that the name may include some parameters, e.g. 'cc -mbig-endian', # in which case the parameters are ignored. # The full path to the executable if found, or "" if not found. # Returns 1 if found, or 0 if not. # proc find-executable-path {name} { # Ignore any parameters set name [lindex $name 0] # The empty string is never a valid executable if {$name ne ""} { foreach p [split-path] { dputs "Looking for $name in $p" set exec [file join $p $name] if {[file-isexec $exec]} { dputs "Found $name -> $exec" return $exec } } } return {} } # @find-executable name # # Searches the path for an executable with the given name. # Note that the name may include some parameters, e.g. 'cc -mbig-endian', # in which case the parameters are ignored. # Returns 1 if found, or 0 if not. # proc find-executable {name} { if {[find-executable-path $name] eq {}} { return 0 } return 1 } # @find-an-executable ?-required? name ... # # Given a list of possible executable names, # searches for one of these on the path. # # Returns the name found, or "" if none found. # If the first parameter is '-required', an error is generated # if no executable is found. # proc find-an-executable {args} { set required 0 if {[lindex $args 0] eq "-required"} { set args [lrange $args 1 end] incr required } foreach name $args { if {[find-executable $name]} { return $name } } if {$required} { if {[llength $args] == 1} { user-error "failed to find: [join $args]" } else { user-error "failed to find one of: [join $args]" } } return "" } # @configlog msg # # Writes the given message to the configuration log, 'config.log'. # proc configlog {msg} { if {![info exists ::autosetup(logfh)]} { set ::autosetup(logfh) [open config.log w] } puts $::autosetup(logfh) $msg } # @msg-checking msg # # Writes the message with no newline to stdout. # proc msg-checking {msg} { if {$::autosetup(msg-quiet) == 0} { maybe-show-timestamp puts -nonewline $msg set ::autosetup(msg-checking) 1 } } # @msg-result msg # # Writes the message to stdout. # proc msg-result {msg} { if {$::autosetup(msg-quiet) == 0} { maybe-show-timestamp puts $msg set ::autosetup(msg-checking) 0 show-notices } } # @msg-quiet command ... # # 'msg-quiet' evaluates it's arguments as a command with output # from 'msg-checking' and 'msg-result' suppressed. # # This is useful if a check needs to run a subcheck which isn't # of interest to the user. proc msg-quiet {args} { incr ::autosetup(msg-quiet) set rc [uplevel 1 $args] incr ::autosetup(msg-quiet) -1 return $rc } # Will be overridden by 'use misc' proc error-stacktrace {msg} { return $msg } proc error-location {msg} { return $msg } ################################################################## # # Debugging output # proc dputs {msg} { if {$::autosetup(debug)} { puts $msg } } ################################################################## # # User and system warnings and errors # # Usage errors such as wrong command line options # @user-error msg # # Indicate incorrect usage to the user, including if required components # or features are not found. # 'autosetup' exits with a non-zero return code. # proc user-error {msg} { show-notices puts stderr "Error: $msg" puts stderr "Try: '[file tail $::autosetup(exe)] --help' for options" exit 1 } # @user-notice msg # # Output the given message to stderr. # proc user-notice {msg} { lappend ::autosetup(notices) $msg } # Incorrect usage in the auto.def file. Identify the location. proc autosetup-error {msg} { autosetup-full-error [error-location $msg] } # Like autosetup-error, except $msg is the full error message. proc autosetup-full-error {msg} { show-notices puts stderr $msg exit 1 } proc show-notices {} { if {$::autosetup(msg-checking)} { puts "" set ::autosetup(msg-checking) 0 } flush stdout if {[info exists ::autosetup(notices)]} { puts stderr [join $::autosetup(notices) \n] unset ::autosetup(notices) } } proc maybe-show-timestamp {} { if {$::autosetup(msg-timing) && $::autosetup(msg-checking) == 0} { puts -nonewline [format {[%6.2f] } [expr {([clock millis] - $::autosetup(start)) % 10000 / 1000.0}]] } } # @autosetup-require-version required # # Checks the current version of 'autosetup' against '$required'. # A fatal error is generated if the current version is less than that required. # proc autosetup-require-version {required} { use util if {[compare-versions $::autosetup(version) $required] < 0} { user-error "autosetup version $required is required, but this is $::autosetup(version)" } } proc autosetup_version {} { return "autosetup v$::autosetup(version)" } ################################################################## # # Directory/path handling # proc realdir {dir} { set oldpwd [pwd] cd $dir set pwd [pwd] cd $oldpwd return $pwd } # Follow symlinks until we get to something which is not a symlink proc realpath {path} { while {1} { if {[catch { set path [file readlink $path] }]} { # Not a link break } } return $path } # Convert absolute path, $path into a path relative # to the given directory (or the current dir, if not given). # proc relative-path {path {pwd {}}} { set diff 0 set same 0 set newf {} set prefix {} set path [file-normalize $path] if {$pwd eq ""} { set pwd [pwd] } else { set pwd [file-normalize $pwd] } if {$path eq $pwd} { return . } # Try to make the filename relative to the current dir foreach p [split $pwd /] f [split $path /] { if {$p ne $f} { incr diff } elseif {!$diff} { incr same } if {$diff} { if {$p ne ""} { # Add .. for sibling or parent dir lappend prefix .. } if {$f ne ""} { lappend newf $f } } } if {$same == 1 || [llength $prefix] > 3} { return $path } file join [join $prefix /] [join $newf /] } # Add filename as a dependency to rerun autosetup # The name will be normalised (converted to a full path) # proc autosetup_add_dep {filename} { lappend ::autosetup(deps) [file-normalize $filename] } ################################################################## # # Library module support # # @use module ... # # Load the given library modules. # e.g. 'use cc cc-shared' # # Note that module 'X' is implemented in either 'autosetup/X.tcl' # or 'autosetup/X/init.tcl' # # The latter form is useful for a complex module which requires additional # support file. In this form, '$::usedir' is set to the module directory # when it is loaded. # proc use {args} { global autosetup libmodule modsource set dirs [list $autosetup(libdir)] if {[info exists autosetup(srcdir)]} { lappend dirs $autosetup(srcdir)/autosetup } foreach m $args { if {[info exists libmodule($m)]} { continue } set libmodule($m) 1 if {[info exists modsource(${m}.tcl)]} { automf_load eval $modsource(${m}.tcl) } else { set locs [list ${m}.tcl ${m}/init.tcl] set found 0 foreach dir $dirs { foreach loc $locs { set source $dir/$loc if {[file exists $source]} { incr found break } } if {$found} { break } } if {$found} { # For the convenience of the "use" source, point to the directory # it is being loaded from set ::usedir [file dirname $source] automf_load source $source autosetup_add_dep $source } else { autosetup-error "use: No such module: $m" } } } } proc autosetup_load_auto_modules {} { global autosetup modsource # First load any embedded auto modules foreach mod [array names modsource *.auto] { automf_load eval $modsource($mod) } # Now any external auto modules foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] { automf_load source $file } } # Load module source in the global scope by executing the given command proc automf_load {args} { if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} { autosetup-full-error [error-dump $msg $opts $::autosetup(debug)] } } # Initial settings set autosetup(exe) $::argv0 set autosetup(istcl) 1 set autosetup(start) [clock millis] set autosetup(installed) 0 set autosetup(sysinstall) 0 set autosetup(msg-checking) 0 set autosetup(msg-quiet) 0 set autosetup(inittypes) {} # Embedded modules are inserted below here set autosetup(installed) 1 set autosetup(sysinstall) 0 # ----- @module asciidoc-formatting.tcl ----- set modsource(asciidoc-formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # asciidoc format use formatting proc para {text} { regsub -all "\[ \t\n\]+" [string trim $text] " " } proc title {text} { underline [para $text] = nl } proc p {text} { puts [para $text] nl } proc code {text} { foreach line [parse_code_block $text] { puts " $line" } nl } proc codelines {lines} { foreach line $lines { puts " $line" } nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[para $text]" - nl } proc subsection {text} { underline "$text" ~ nl } proc bullet {text} { puts "* [para $text]" } proc indent {text} { puts " :: " puts [para $text] } proc defn {first args} { set sep "" if {$first ne ""} { puts "${first}::" } else { puts " :: " } set defn [string trim [join $args \n]] regsub -all "\n\n" $defn "\n ::\n" defn puts $defn } } # ----- @module formatting.tcl ----- set modsource(formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides common text formatting # This is designed for documenation which looks like: # code {...} # or # code { # ... # ... # } # In the second case, we need to work out the indenting # and strip it from all lines but preserve the remaining indenting. # Note that all lines need to be indented with the same initial # spaces/tabs. # # Returns a list of lines with the indenting removed. # proc parse_code_block {text} { # If the text begins with newline, take the following text, # otherwise just return the original if {![regexp "^\n(.*)" $text -> text]} { return [list [string trim $text]] } # And trip spaces off the end set text [string trimright $text] set min 100 # Examine each line to determine the minimum indent foreach line [split $text \n] { if {$line eq ""} { # Ignore empty lines for the indent calculation continue } regexp "^(\[ \t\]*)" $line -> indent set len [string length $indent] if {$len < $min} { set min $len } } # Now make a list of lines with this indent removed set lines {} foreach line [split $text \n] { lappend lines [string range $line $min end] } # Return the result return $lines } } # ----- @module getopt.tcl ----- set modsource(getopt.tcl) { # Copyright (c) 2006 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Simple getopt module # Parse everything out of the argv list which looks like an option # Everything which doesn't look like an option, or is after --, is left unchanged # Understands --enable-xxx as a synonym for --xxx to enable the boolean option xxx. # Understands --disable-xxx to disable the boolean option xxx. # # The returned value is a dictionary keyed by option name # Each value is a list of {type value} ... where type is "bool" or "str". # The value for a boolean option is 0 or 1. The value of a string option is the value given. proc getopt {argvname} { upvar $argvname argv set nargv {} set opts {} for {set i 0} {$i < [llength $argv]} {incr i} { set arg [lindex $argv $i] #dputs arg=$arg if {$arg eq "--"} { # End of options incr i lappend nargv {*}[lrange $argv $i end] break } if {[regexp {^--([^=][^=]+)=(.*)$} $arg -> name value]} { # --name=value dict lappend opts $name [list str $value] } elseif {[regexp {^--(enable-|disable-)?([^=]*)$} $arg -> prefix name]} { if {$prefix in {enable- ""}} { set value 1 } else { set value 0 } dict lappend opts $name [list bool $value] } else { lappend nargv $arg } } #puts "getopt: argv=[join $argv] => [join $nargv]" #array set getopt $opts #parray getopt set argv $nargv return $opts } } # ----- @module help.tcl ----- set modsource(help.tcl) { # Copyright (c) 2010 WorkWare Systems http://workware.net.au/ # All rights reserved # Module which provides usage, help and the command reference proc autosetup_help {what} { use_pager puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n" puts "This is [autosetup_version], a build environment \"autoconfigurator\"" puts "See the documentation online at http://msteveb.github.com/autosetup/\n" if {$what eq "local"} { if {[file exists $::autosetup(autodef)]} { # This relies on auto.def having a call to 'options' # which will display options and quit source $::autosetup(autodef) } else { options-show } } else { incr ::autosetup(showhelp) if {[catch {use $what}]} { user-error "Unknown module: $what" } else { options-show } } exit 0 } proc autosetup_show_license {} { global modsource autosetup use_pager if {[info exists modsource(LICENSE)]} { puts $modsource(LICENSE) return } foreach dir [list $autosetup(libdir) $autosetup(srcdir)] { set path [file join $dir LICENSE] if {[file exists $path]} { puts [readfile $path] return } } puts "LICENSE not found" } # If not already paged and stdout is a tty, pipe the output through the pager # This is done by reinvoking autosetup with --nopager added proc use_pager {} { if {![opt-bool nopager] && [getenv PAGER ""] ne "" && [isatty? stdin] && [isatty? stdout]} { if {[catch { exec [info nameofexecutable] $::argv0 --nopager {*}$::argv |& {*}[getenv PAGER] >@stdout <@stdin 2>@stderr } msg opts] == 1} { if {[dict get $opts -errorcode] eq "NONE"} { # an internal/exec error puts stderr $msg exit 1 } } exit 0 } } # Outputs the autosetup references in one of several formats proc autosetup_reference {{type text}} { use_pager switch -glob -- $type { wiki {use wiki-formatting} ascii* {use asciidoc-formatting} md - markdown {use markdown-formatting} default {use text-formatting} } title "[autosetup_version] -- Command Reference" section {Introduction} p { See http://msteveb.github.com/autosetup/ for the online documentation for 'autosetup' } p { 'autosetup' provides a number of built-in commands which are documented below. These may be used from 'auto.def' to test for features, define variables, create files from templates and other similar actions. } automf_command_reference exit 0 } proc autosetup_output_block {type lines} { if {[llength $lines]} { switch $type { section { section $lines } subsection { subsection $lines } code { codelines $lines } p { p [join $lines] } list { foreach line $lines { bullet $line } nl } } } } # Generate a command reference from inline documentation proc automf_command_reference {} { lappend files $::autosetup(prog) lappend files {*}[lsort [glob -nocomplain $::autosetup(libdir)/*.tcl]] # We want to process all non-module files before module files # and then modules in alphabetical order. # So examine all files and extract docs into doc($modulename) and doc(_core_) # # Each entry is a list of {type data} where $type is one of: section, subsection, code, list, p # and $data is a string for section, subsection or a list of text lines for other types. # XXX: Should commands be in alphabetical order too? Currently they are in file order. set doc(_core_) {} lappend doc(_core_) [list section "Core Commands"] foreach file $files { set modulename [file rootname [file tail $file]] set current _core_ set f [open $file] while {![eof $f]} { set line [gets $f] # Find embedded module names if {[regexp {^#.*@module ([^ ]*)} $line -> modulename]} { continue } # Find lines starting with "# @*" and continuing through the remaining comment lines if {![regexp {^# @(.*)} $line -> cmd]} { continue } # Synopsis or command? if {$cmd eq "synopsis:"} { set current $modulename lappend doc($current) [list section "Module: $modulename"] } else { lappend doc($current) [list subsection $cmd] } set lines {} set type p # Now the description while {![eof $f]} { set line [gets $f] if {![regexp {^#(#)? ?(.*)} $line -> hash cmd]} { break } if {$hash eq "#"} { set t code } elseif {[regexp {^- (.*)} $cmd -> cmd]} { set t list } else { set t p } #puts "hash=$hash, oldhash=$oldhash, lines=[llength $lines], cmd=$cmd" if {$t ne $type || $cmd eq ""} { # Finish the current block lappend doc($current) [list $type $lines] set lines {} set type $t } if {$cmd ne ""} { lappend lines $cmd } } lappend doc($current) [list $type $lines] } close $f } # Now format and output the results # _core_ will sort first foreach module [lsort [array names doc]] { foreach item $doc($module) { autosetup_output_block {*}$item } } } } # ----- @module init.tcl ----- set modsource(init.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module to help create auto.def and configure proc autosetup_init {type} { set help 0 if {$type in {? help}} { incr help } elseif {![dict exists $::autosetup(inittypes) $type]} { puts "Unknown type, --init=$type" incr help } if {$help} { puts "Use one of the following types (e.g. --init=make)\n" foreach type [lsort [dict keys $::autosetup(inittypes)]] { lassign [dict get $::autosetup(inittypes) $type] desc # XXX: Use the options-show code to wrap the description puts [format "%-10s %s" $type $desc] } return } lassign [dict get $::autosetup(inittypes) $type] desc script puts "Initialising $type: $desc\n" # All initialisations happens in the top level srcdir cd $::autosetup(srcdir) uplevel #0 $script } proc autosetup_add_init_type {type desc script} { dict set ::autosetup(inittypes) $type [list $desc $script] } # This is for in creating build-system init scripts # # If the file doesn't exist, create it containing $contents # If the file does exist, only overwrite if --force is specified. # proc autosetup_check_create {filename contents} { if {[file exists $filename]} { if {!$::autosetup(force)} { puts "I see $filename already exists." return } else { puts "I will overwrite the existing $filename because you used --force." } } else { puts "I don't see $filename, so I will create it." } writefile $filename $contents } } # ----- @module install.tcl ----- set modsource(install.tcl) { # Copyright (c) 2006-2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which can install autosetup # autosetup(installed)=1 means that autosetup is not running from source # autosetup(sysinstall)=1 means that autosetup is running from a sysinstall verion # shared=1 means that we are trying to do a sysinstall. This is only possible from the development source. proc autosetup_install {dir {shared 0}} { global autosetup if {$shared} { if {$autosetup(installed) || $autosetup(sysinstall)} { user-error "Can only --sysinstall from development sources" } } elseif {$autosetup(installed) && !$autosetup(sysinstall)} { user-error "Can't --install from project install" } if {$autosetup(sysinstall)} { # This is the sysinstall version, so install just uses references cd $dir puts "[autosetup_version] creating configure to use system-installed autosetup" autosetup_create_configure 1 puts "Creating autosetup/README.autosetup" file mkdir autosetup autosetup_install_readme autosetup/README.autosetup 1 return } if {[catch { if {$shared} { set target $dir/bin/autosetup set installedas $target } else { if {$dir eq "."} { set installedas autosetup } else { set installedas $dir/autosetup } cd $dir file mkdir autosetup set target autosetup/autosetup } set targetdir [file dirname $target] file mkdir $targetdir set f [open $target w] set publicmodules {} # First the main script, but only up until "CUT HERE" set in [open $autosetup(dir)/autosetup] while {[gets $in buf] >= 0} { if {$buf ne "##-- CUT HERE --##"} { puts $f $buf continue } # Insert the static modules here # i.e. those which don't contain @synopsis: # All modules are inserted if $shared is set puts $f "set autosetup(installed) 1" puts $f "set autosetup(sysinstall) $shared" foreach file [lsort [glob $autosetup(libdir)/*.{tcl,auto}]] { set modname [file tail $file] set ext [file ext $modname] set buf [readfile $file] if {!$shared} { if {$ext eq ".auto" || [string match "*\n# @synopsis:*" $buf]} { lappend publicmodules $file continue } } dputs "install: importing lib/[file tail $file]" puts $f "# ----- @module $modname -----" puts $f "\nset modsource($modname) \{" puts $f $buf puts $f "\}\n" } if {$shared} { foreach {srcname destname} [list $autosetup(libdir)/README.autosetup-lib README.autosetup \ $autosetup(srcdir)/LICENSE LICENSE] { dputs "install: importing $srcname as $destname" puts $f "\nset modsource($destname) \\\n[list [readfile $srcname]\n]\n" } } } close $in close $f catch {exec chmod 755 $target} set installfiles {autosetup-config.guess autosetup-config.sub autosetup-test-tclsh} set removefiles {} if {!$shared} { autosetup_install_readme $targetdir/README.autosetup 0 # Install public modules foreach file $publicmodules { set tail [file tail $file] autosetup_install_file $file $targetdir/$tail } lappend installfiles jimsh0.c autosetup-find-tclsh LICENSE lappend removefiles config.guess config.sub test-tclsh find-tclsh } else { lappend installfiles {sys-find-tclsh autosetup-find-tclsh} } # Install support files foreach fileinfo $installfiles { if {[llength $fileinfo] == 2} { lassign $fileinfo source dest } else { lassign $fileinfo source set dest $source } autosetup_install_file $autosetup(dir)/$source $targetdir/$dest } # Remove obsolete files foreach file $removefiles { if {[file exists $targetdir/$file]} { file delete $targetdir/$file } } } error]} { user-error "Failed to install autosetup: $error" } if {$shared} { set type "system" } else { set type "local" } puts "Installed $type [autosetup_version] to $installedas" if {!$shared} { # Now create 'configure' if necessary autosetup_create_configure 0 } } proc autosetup_create_configure {shared} { if {[file exists configure]} { if {!$::autosetup(force)} { # Could this be an autosetup configure? if {![string match "*\nWRAPPER=*" [readfile configure]]} { puts "I see configure, but not created by autosetup, so I won't overwrite it." puts "Remove it or use --force to overwrite." return } } else { puts "I will overwrite the existing configure because you used --force." } } else { puts "I don't see configure, so I will create it." } if {$shared} { writefile configure \ {#!/bin/sh # Note that WRAPPER is set here purely to detect an autosetup-created script WRAPPER="-"; "autosetup" "$@" } } else { writefile configure \ {#!/bin/sh dir="`dirname "$0"`/autosetup" WRAPPER="$0"; export WRAPPER; exec "`$dir/autosetup-find-tclsh`" "$dir/autosetup" "$@" } } catch {exec chmod 755 configure} } # Append the contents of $file to filehandle $f proc autosetup_install_append {f file} { dputs "install: include $file" set in [open $file] puts $f [read $in] close $in } proc autosetup_install_file {source target} { dputs "install: $source => $target" if {![file exists $source]} { error "Missing installation file '$source'" } writefile $target [readfile $source]\n # If possible, copy the file mode file stat $source stat set mode [format %o [expr {$stat(mode) & 0x1ff}]] catch {exec chmod $mode $target} } proc autosetup_install_readme {target sysinstall} { set readme "README.autosetup created by [autosetup_version]\n\n" if {$sysinstall} { append readme \ {This is the autosetup directory for a system install of autosetup. Loadable modules can be added here. } } else { append readme \ {This is the autosetup directory for a local install of autosetup. It contains autosetup, support files and loadable modules. } } append readme { *.tcl files in this directory are optional modules which can be loaded with the 'use' directive. *.auto files in this directory are auto-loaded. For more information, see http://msteveb.github.com/autosetup/ } dputs "install: autosetup/README.autosetup" writefile $target $readme } } # ----- @module markdown-formatting.tcl ----- set modsource(markdown-formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # markdown format (kramdown syntax) use formatting proc para {text} { regsub -all "\[ \t\n\]+" [string trim $text] " " text regsub -all {([^a-zA-Z])'([^']*)'} $text {\1**`\2`**} text regsub -all {^'([^']*)'} $text {**`\1`**} text regsub -all {(http[^ \t\n]*)} $text {[\1](\1)} text return $text } proc title {text} { underline [para $text] = nl } proc p {text} { puts [para $text] nl } proc codelines {lines} { puts "~~~~~~~~~~~~" foreach line $lines { puts $line } puts "~~~~~~~~~~~~" nl } proc code {text} { puts "~~~~~~~~~~~~" foreach line [parse_code_block $text] { puts $line } puts "~~~~~~~~~~~~" nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[para $text]" - nl } proc subsection {text} { puts "### `$text`" nl } proc bullet {text} { puts "* [para $text]" } proc defn {first args} { puts "^" set defn [string trim [join $args \n]] if {$first ne ""} { puts "**${first}**" puts -nonewline ": " regsub -all "\n\n" $defn "\n: " defn } puts "$defn" } } # ----- @module misc.tcl ----- set modsource(misc.tcl) { # Copyright (c) 2007-2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module containing misc procs useful to modules # Largely for platform compatibility set autosetup(istcl) [info exists ::tcl_library] set autosetup(iswin) [string equal windows $tcl_platform(platform)] if {$autosetup(iswin)} { # mingw/windows separates $PATH with semicolons # and doesn't have an executable bit proc split-path {} { split [getenv PATH .] {;} } proc file-isexec {exec} { # Basic test for windows. We ignore .bat if {[file isfile $exec] || [file isfile $exec.exe]} { return 1 } return 0 } } else { # unix separates $PATH with colons and has and executable bit proc split-path {} { split [getenv PATH .] : } proc file-isexec {exec} { file executable $exec } } # Assume that exec can return stdout and stderr proc exec-with-stderr {args} { exec {*}$args 2>@1 } if {$autosetup(istcl)} { # Tcl doesn't have the env command proc getenv {name args} { if {[info exists ::env($name)]} { return $::env($name) } if {[llength $args]} { return [lindex $args 0] } return -code error "environment variable \"$name\" does not exist" } proc isatty? {channel} { dict exists [fconfigure $channel] -xchar } } else { if {$autosetup(iswin)} { # On Windows, backslash convert all environment variables # (Assume that Tcl does this for us) proc getenv {name args} { string map {\\ /} [env $name {*}$args] } } else { # Jim on unix is simple alias getenv env } proc isatty? {channel} { set tty 0 catch { # isatty is a recent addition to Jim Tcl set tty [$channel isatty] } return $tty } } # In case 'file normalize' doesn't exist # proc file-normalize {path} { if {[catch {file normalize $path} result]} { if {$path eq ""} { return "" } set oldpwd [pwd] if {[file isdir $path]} { cd $path set result [pwd] } else { cd [file dirname $path] set result [file join [pwd] [file tail $path]] } cd $oldpwd } return $result } # If everything is working properly, the only errors which occur # should be generated in user code (e.g. auto.def). # By default, we only want to show the error location in user code. # We use [info frame] to achieve this, but it works differently on Tcl and Jim. # # This is designed to be called for incorrect usage in auto.def, via autosetup-error # proc error-location {msg} { if {$::autosetup(debug)} { return -code error $msg } # Search back through the stack trace for the first error in a .def file for {set i 1} {$i < [info level]} {incr i} { if {$::autosetup(istcl)} { array set info [info frame -$i] } else { lassign [info frame -$i] info(caller) info(file) info(line) } if {[string match *.def $info(file)]} { return "[relative-path $info(file)]:$info(line): Error: $msg" } #puts "Skipping $info(file):$info(line)" } return $msg } # If everything is working properly, the only errors which occur # should be generated in user code (e.g. auto.def). # By default, we only want to show the error location in user code. # We use [info frame] to achieve this, but it works differently on Tcl and Jim. # # This is designed to be called for incorrect usage in auto.def, via autosetup-error # proc error-stacktrace {msg} { if {$::autosetup(debug)} { return -code error $msg } # Search back through the stack trace for the first error in a .def file for {set i 1} {$i < [info level]} {incr i} { if {$::autosetup(istcl)} { array set info [info frame -$i] } else { lassign [info frame -$i] info(caller) info(file) info(line) } if {[string match *.def $info(file)]} { return "[relative-path $info(file)]:$info(line): Error: $msg" } #puts "Skipping $info(file):$info(line)" } return $msg } # Given the return from [catch {...} msg opts], returns an appropriate # error message. A nice one for Jim and a less-nice one for Tcl. # If 'fulltrace' is set, a full stack trace is provided. # Otherwise a simple message is provided. # # This is designed for developer errors, e.g. in module code or auto.def code # # proc error-dump {msg opts fulltrace} { if {$::autosetup(istcl)} { if {$fulltrace} { return "Error: [dict get $opts -errorinfo]" } else { return "Error: $msg" } } else { lassign $opts(-errorinfo) p f l if {$f ne ""} { set result "$f:$l: Error: " } append result "$msg\n" if {$fulltrace} { append result [stackdump $opts(-errorinfo)] } # Remove the trailing newline string trim $result } } } # ----- @module text-formatting.tcl ----- set modsource(text-formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting use formatting proc wordwrap {text length {firstprefix ""} {nextprefix ""}} { set len 0 set space $firstprefix foreach word [split $text] { set word [string trim $word] if {$word eq ""} { continue } if {[info exists partial]} { append partial " " $word if {[string first $quote $word] < 0} { # Haven't found end of quoted word continue } # Finished quoted word set word $partial unset partial unset quote } else { set quote [string index $word 0] if {$quote in {' *}} { if {[string first $quote $word 1] < 0} { # Haven't found end of quoted word # Not a whole word. set first [string index $word 0] # Start of quoted word set partial $word continue } } } if {$len && [string length $space$word] + $len >= $length} { puts "" set len 0 set space $nextprefix } incr len [string length $space$word] # Use man-page conventions for highlighting 'quoted' and *quoted* # single words. # Use x^Hx for *bold* and _^Hx for 'underline'. # # less and more will both understand this. # Pipe through 'col -b' to remove them. if {[regexp {^'(.*)'(.*)} $word -> quoted after]} { set quoted [string map {~ " "} $quoted] regsub -all . $quoted "&\b&" quoted set word $quoted$after } elseif {[regexp {^[*](.*)[*](.*)} $word -> quoted after]} { set quoted [string map {~ " "} $quoted] regsub -all . $quoted "_\b&" quoted set word $quoted$after } puts -nonewline $space$word set space " " } if {[info exists partial]} { # Missing end of quote puts -nonewline $space$partial } if {$len} { puts "" } } proc title {text} { underline [string trim $text] = nl } proc p {text} { wordwrap $text 80 nl } proc codelines {lines} { foreach line $lines { puts " $line" } nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[string trim $text]" - nl } proc subsection {text} { underline "$text" ~ nl } proc bullet {text} { wordwrap $text 76 " * " " " } proc indent {text} { wordwrap $text 76 " " " " } proc defn {first args} { if {$first ne ""} { underline " $first" ~ } foreach p $args { if {$p ne ""} { indent $p } } } } # ----- @module util.tcl ----- set modsource(util.tcl) { # Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which contains miscellaneous utility functions # @compare-versions version1 version2 # # Versions are of the form 'a.b.c' (may be any number of numeric components) # # Compares the two versions and returns: ## -1 if v1 < v2 ## 0 if v1 == v2 ## 1 if v1 > v2 # # If one version has fewer components than the other, 0 is substituted to the right. e.g. ## 0.2 < 0.3 ## 0.2.5 > 0.2 ## 1.1 == 1.1.0 # proc compare-versions {v1 v2} { foreach c1 [split $v1 .] c2 [split $v2 .] { if {$c1 eq ""} { set c1 0 } if {$c2 eq ""} { set c2 0 } if {$c1 < $c2} { return -1 } if {$c1 > $c2} { return 1 } } return 0 } # @suffix suf list # # Takes a list and returns a new list with '$suf' appended # to each element # ## suffix .c {a b c} => {a.c b.c c.c} # proc suffix {suf list} { set result {} foreach p $list { lappend result $p$suf } return $result } # @prefix pre list # # Takes a list and returns a new list with '$pre' prepended # to each element # ## prefix jim- {a.c b.c} => {jim-a.c jim-b.c} # proc prefix {pre list} { set result {} foreach p $list { lappend result $pre$p } return $result } } # ----- @module wiki-formatting.tcl ----- set modsource(wiki-formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # wiki.tcl.tk format output use formatting proc joinlines {text} { set lines {} foreach l [split [string trim $text] \n] { lappend lines [string trim $l] } join $lines } proc p {text} { puts [joinlines $text] puts "" } proc title {text} { puts "*** [joinlines $text] ***" puts "" } proc codelines {lines} { puts "======" foreach line $lines { puts " $line" } puts "======" } proc code {text} { puts "======" foreach line [parse_code_block $text] { puts " $line" } puts "======" } proc nl {} { } proc section {text} { puts "'''$text'''" puts "" } proc subsection {text} { puts "''$text''" puts "" } proc bullet {text} { puts " * [joinlines $text]" } proc indent {text} { puts " : [joinlines $text]" } proc defn {first args} { if {$first ne ""} { indent '''$first''' } foreach p $args { p $p } } } ################################################################## # # Entry/Exit # if {$autosetup(debug)} { main $argv } if {[catch {main $argv} msg opts] == 1} { show-notices autosetup-full-error [error-dump $msg $opts $autosetup(debug)] if {!$autosetup(debug)} { puts stderr "Try: '[file tail $autosetup(exe)] --debug' for a full stack trace" } exit 1 } neomutt-neomutt-20171215/autosetup/autosetup-config.guess000077500000000000000000001236721321473123000235510ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2014 Free Software Foundation, Inc. timestamp='2014-11-04' # This file 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 3 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, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: neomutt-neomutt-20171215/autosetup/autosetup-config.sub000077500000000000000000001062231321473123000232050ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2014 Free Software Foundation, Inc. timestamp='2014-12-03' # This file 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 3 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, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: neomutt-neomutt-20171215/autosetup/autosetup-find-tclsh000077500000000000000000000013241321473123000231770ustar00rootroot00000000000000#!/bin/sh # Looks for a suitable tclsh or jimsh in the PATH # If not found, builds a bootstrap jimsh from source # Prefer $autosetup_tclsh if is set in the environment d=`dirname "$0"` { "$d/jimsh0" "$d/autosetup-test-tclsh"; } 2>/dev/null && exit 0 PATH="$PATH:$d"; export PATH for tclsh in $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6; do { $tclsh "$d/autosetup-test-tclsh"; } 2>/dev/null && exit 0 done echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" for cc in ${CC_FOR_BUILD:-cc} gcc; do { $cc -o "$d/jimsh0" "$d/jimsh0.c"; } 2>/dev/null || continue "$d/jimsh0" "$d/autosetup-test-tclsh" && exit 0 done echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." echo false neomutt-neomutt-20171215/autosetup/autosetup-test-tclsh000066400000000000000000000010231321473123000232270ustar00rootroot00000000000000# A small Tcl script to verify that the chosen # interpreter works. Sometimes we might e.g. pick up # an interpreter for a different arch. # Outputs the full path to the interpreter if {[catch {info version} version] == 0} { # This is Jim Tcl if {$version >= 0.72} { # Ensure that regexp works regexp (a.*?) a puts [info nameofexecutable] exit 0 } } elseif {[catch {info tclversion} version] == 0} { if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} { puts [info nameofexecutable] exit 0 } } exit 1 neomutt-neomutt-20171215/autosetup/cc-db.tcl000066400000000000000000000006151321473123000206450ustar00rootroot00000000000000# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc-db' module provides a knowledge-base of system idiosyncrasies. # In general, this module can always be included. use cc module-options {} # openbsd needs sys/types.h to detect some system headers cc-include-needs sys/socket.h sys/types.h cc-include-needs netinet/in.h sys/types.h neomutt-neomutt-20171215/autosetup/cc-lib.tcl000066400000000000000000000106321321473123000210260ustar00rootroot00000000000000# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # Provides a library of common tests on top of the 'cc' module. use cc module-options {} # @cc-check-lfs # # The equivalent of the 'AC_SYS_LARGEFILE' macro. # # defines 'HAVE_LFS' if LFS is available, # and defines '_FILE_OFFSET_BITS=64' if necessary # # Returns 1 if 'LFS' is available or 0 otherwise # proc cc-check-lfs {} { cc-check-includes sys/types.h msg-checking "Checking if -D_FILE_OFFSET_BITS=64 is needed..." set lfs 1 if {[msg-quiet cc-with {-includes sys/types.h} {cc-check-sizeof off_t}] == 8} { msg-result no } elseif {[msg-quiet cc-with {-includes sys/types.h -cflags -D_FILE_OFFSET_BITS=64} {cc-check-sizeof off_t}] == 8} { define _FILE_OFFSET_BITS 64 msg-result yes } else { set lfs 0 msg-result none } define-feature lfs $lfs return $lfs } # @cc-check-endian # # The equivalent of the 'AC_C_BIGENDIAN' macro. # # defines 'HAVE_BIG_ENDIAN' if endian is known to be big, # or 'HAVE_LITTLE_ENDIAN' if endian is known to be little. # # Returns 1 if determined, or 0 if not. # proc cc-check-endian {} { cc-check-includes sys/types.h sys/param.h set rc 0 msg-checking "Checking endian..." cc-with {-includes {sys/types.h sys/param.h}} { if {[cctest -code { #if !defined(BIG_ENDIAN) || !defined(BYTE_ORDER) #error unknown #elif BYTE_ORDER != BIG_ENDIAN #error little #endif }]} { define-feature big-endian msg-result "big" set rc 1 } elseif {[cctest -code { #if !defined(LITTLE_ENDIAN) || !defined(BYTE_ORDER) #error unknown #elif BYTE_ORDER != LITTLE_ENDIAN #error big #endif }]} { define-feature little-endian msg-result "little" set rc 1 } else { msg-result "unknown" } } return $rc } # @cc-check-flags flag ?...? # # Checks whether the given C/C++ compiler flags can be used. Defines feature # names prefixed with 'HAVE_CFLAG' and 'HAVE_CXXFLAG' respectively, and # appends working flags to '-cflags' and 'CFLAGS' or 'CXXFLAGS'. proc cc-check-flags {args} { set result 1 array set opts [cc-get-settings] switch -exact -- $opts(-lang) { c++ { set lang C++ set prefix CXXFLAG } c { set lang C set prefix CFLAG } default { autosetup-error "cc-check-flags failed with unknown language: $opts(-lang)" } } foreach flag $args { msg-checking "Checking whether the $lang compiler accepts $flag..." if {[cctest -cflags $flag]} { msg-result yes define-feature $prefix$flag cc-with [list -cflags [list $flag]] define-append ${prefix}S $flag } else { msg-result no set result 0 } } return $result } # @cc-check-standards ver ?...? # # Checks whether the C/C++ compiler accepts one of the specified '-std=$ver' # options, and appends the first working one to '-cflags' and 'CFLAGS' or # 'CXXFLAGS'. proc cc-check-standards {args} { array set opts [cc-get-settings] foreach std $args { if {[cc-check-flags -std=$std]} { return $std } } return "" } # Checks whether $keyword is usable as alignof proc cctest_alignof {keyword} { msg-checking "Checking for $keyword..." if {[cctest -code "int x = ${keyword}(char), y = ${keyword}('x');"]} then { msg-result ok define-feature $keyword } else { msg-result "not found" } } # @cc-check-c11 # # Checks for several C11/C++11 extensions and their alternatives. Currently # checks for '_Static_assert', '_Alignof', '__alignof__', '__alignof'. proc cc-check-c11 {} { msg-checking "Checking for _Static_assert..." if {[cctest -code { _Static_assert(1, "static assertions are available"); }]} then { msg-result ok define-feature _Static_assert } else { msg-result "not found" } cctest_alignof _Alignof cctest_alignof __alignof__ cctest_alignof __alignof } # @cc-check-alloca # # The equivalent of the 'AC_FUNC_ALLOCA' macro. # # Checks for the existence of 'alloca' # defines 'HAVE_ALLOCA' and returns 1 if it exists. proc cc-check-alloca {} { cc-check-some-feature alloca { cctest -includes alloca.h -code { alloca (2 * sizeof (int)); } } } # @cc-signal-return-type # # The equivalent of the 'AC_TYPE_SIGNAL' macro. # # defines 'RETSIGTYPE' to 'int' or 'void'. proc cc-signal-return-type {} { msg-checking "Checking return type of signal handlers..." cc-with {-includes {sys/types.h signal.h}} { if {[cctest -code {return *(signal (0, 0)) (0) == 1;}]} { set type int } else { set type void } define RETSIGTYPE $type msg-result $type } } neomutt-neomutt-20171215/autosetup/cc-shared.tcl000066400000000000000000000070761321473123000215360ustar00rootroot00000000000000# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc-shared' module provides support for shared libraries and shared objects. # It defines the following variables: # ## SH_CFLAGS Flags to use compiling sources destined for a shared library ## SH_LDFLAGS Flags to use linking (creating) a shared library ## SH_SOPREFIX Prefix to use to set the soname when creating a shared library ## SH_SOEXT Extension for shared libs ## SH_SOEXTVER Format for versioned shared libs - %s = version ## SHOBJ_CFLAGS Flags to use compiling sources destined for a shared object ## SHOBJ_LDFLAGS Flags to use linking a shared object, undefined symbols allowed ## SHOBJ_LDFLAGS_R - as above, but all symbols must be resolved ## SH_LINKFLAGS Flags to use linking an executable which will load shared objects ## LD_LIBRARY_PATH Environment variable which specifies path to shared libraries ## STRIPLIBFLAGS Arguments to strip a dynamic library module-options {} # Defaults: gcc on unix define SHOBJ_CFLAGS -fpic define SHOBJ_LDFLAGS -shared define SH_CFLAGS -fpic define SH_LDFLAGS -shared define SH_LINKFLAGS -rdynamic define SH_SOEXT .so define SH_SOEXTVER .so.%s define SH_SOPREFIX -Wl,-soname, define LD_LIBRARY_PATH LD_LIBRARY_PATH define STRIPLIBFLAGS --strip-unneeded # Note: This is a helpful reference for identifying the toolchain # http://sourceforge.net/apps/mediawiki/predef/index.php?title=Compilers switch -glob -- [get-define host] { *-*-darwin* { define SHOBJ_CFLAGS "-dynamic -fno-common" define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup" define SHOBJ_LDFLAGS_R -bundle define SH_CFLAGS -dynamic define SH_LDFLAGS -dynamiclib define SH_LINKFLAGS "" define SH_SOEXT .dylib define SH_SOEXTVER .%s.dylib define SH_SOPREFIX -Wl,-install_name, define LD_LIBRARY_PATH DYLD_LIBRARY_PATH define STRIPLIBFLAGS -x } *-*-ming* - *-*-cygwin - *-*-msys { define SHOBJ_CFLAGS "" define SHOBJ_LDFLAGS -shared define SH_CFLAGS "" define SH_LDFLAGS -shared define SH_LINKFLAGS "" define SH_SOEXT .dll define SH_SOEXTVER .dll define SH_SOPREFIX "" define LD_LIBRARY_PATH PATH } sparc* { if {[msg-quiet cc-check-decls __SUNPRO_C]} { msg-result "Found sun stdio compiler" # sun stdio compiler # XXX: These haven't been fully tested. define SHOBJ_CFLAGS -KPIC define SHOBJ_LDFLAGS "-G" define SH_CFLAGS -KPIC define SH_LINKFLAGS -Wl,-export-dynamic define SH_SOPREFIX -Wl,-h, } else { # sparc has a very small GOT table limit, so use -fPIC define SH_CFLAGS -fPIC define SHOBJ_CFLAGS -fPIC } } *-*-solaris* { if {[msg-quiet cc-check-decls __SUNPRO_C]} { msg-result "Found sun stdio compiler" # sun stdio compiler # XXX: These haven't been fully tested. define SHOBJ_CFLAGS -KPIC define SHOBJ_LDFLAGS "-G" define SH_CFLAGS -KPIC define SH_LINKFLAGS -Wl,-export-dynamic define SH_SOPREFIX -Wl,-h, } } *-*-hpux { # XXX: These haven't been tested define SHOBJ_CFLAGS "+O3 +z" define SHOBJ_LDFLAGS -b define SH_CFLAGS +z define SH_LINKFLAGS -Wl,+s define LD_LIBRARY_PATH SHLIB_PATH } *-*-haiku { define SHOBJ_CFLAGS "" define SHOBJ_LDFLAGS -shared define SH_CFLAGS "" define SH_LDFLAGS -shared define SH_LINKFLAGS "" define SH_SOPREFIX "" define LD_LIBRARY_PATH LIBRARY_PATH } microblaze* { # Microblaze generally needs -fPIC rather than -fpic define SHOBJ_CFLAGS -fPIC define SH_CFLAGS -fPIC } } if {![is-defined SHOBJ_LDFLAGS_R]} { define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS] } neomutt-neomutt-20171215/autosetup/cc.tcl000066400000000000000000000465311321473123000202710ustar00rootroot00000000000000# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc' module supports checking various 'features' of the C or C++ # compiler/linker environment. Common commands are 'cc-check-includes', # 'cc-check-types', 'cc-check-functions', 'cc-with', 'make-config-header' and 'make-template'. # # The following environment variables are used if set: # ## CC - C compiler ## CXX - C++ compiler ## CCACHE - Set to "none" to disable automatic use of ccache ## CFLAGS - Additional C compiler flags ## CXXFLAGS - Additional C++ compiler flags ## LDFLAGS - Additional compiler flags during linking ## LIBS - Additional libraries to use (for all tests) ## CROSS - Tool prefix for cross compilation # # The following variables are defined from the corresponding # environment variables if set. # ## CPPFLAGS ## LINKFLAGS ## CC_FOR_BUILD ## LD use system module-options {} # Checks for the existence of the given function by linking # proc cctest_function {function} { cctest -link 1 -declare "extern void $function\(void);" -code "$function\();" } # Checks for the existence of the given type by compiling proc cctest_type {type} { cctest -code "$type _x;" } # Checks for the existence of the given type/structure member. # e.g. "struct stat.st_mtime" proc cctest_member {struct_member} { # split at the first dot regexp {^([^.]+)[.](.*)$} $struct_member -> struct member cctest -code "static $struct _s; return sizeof(_s.$member);" } # Checks for the existence of the given define by compiling # proc cctest_define {name} { cctest -code "#ifndef $name\n#error not defined\n#endif" } # Checks for the existence of the given name either as # a macro (#define) or an rvalue (such as an enum) # proc cctest_decl {name} { cctest -code "#ifndef $name\n(void)$name;\n#endif" } # @cc-check-sizeof type ... # # Checks the size of the given types (between 1 and 32, inclusive). # Defines a variable with the size determined, or 'unknown' otherwise. # e.g. for type 'long long', defines 'SIZEOF_LONG_LONG'. # Returns the size of the last type. # proc cc-check-sizeof {args} { foreach type $args { msg-checking "Checking for sizeof $type..." set size unknown # Try the most common sizes first foreach i {4 8 1 2 16 32} { if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} { set size $i break } } msg-result $size set define [feature-define-name $type SIZEOF_] define $define $size } # Return the last result get-define $define } # Checks for each feature in $list by using the given script. # # When the script is evaluated, $each is set to the feature # being checked, and $extra is set to any additional cctest args. # # Returns 1 if all features were found, or 0 otherwise. proc cc-check-some-feature {list script} { set ret 1 foreach each $list { if {![check-feature $each $script]} { set ret 0 } } return $ret } # @cc-check-includes includes ... # # Checks that the given include files can be used. proc cc-check-includes {args} { cc-check-some-feature $args { set with {} if {[dict exists $::autosetup(cc-include-deps) $each]} { set deps [dict keys [dict get $::autosetup(cc-include-deps) $each]] msg-quiet cc-check-includes {*}$deps foreach i $deps { if {[have-feature $i]} { lappend with $i } } } if {[llength $with]} { cc-with [list -includes $with] { cctest -includes $each } } else { cctest -includes $each } } } # @cc-include-needs include required ... # # Ensures that when checking for '$include', a check is first # made for each '$required' file, and if found, it is included with '#include'. proc cc-include-needs {file args} { foreach depfile $args { dict set ::autosetup(cc-include-deps) $file $depfile 1 } } # @cc-check-types type ... # # Checks that the types exist. proc cc-check-types {args} { cc-check-some-feature $args { cctest_type $each } } # @cc-check-defines define ... # # Checks that the given preprocessor symbols are defined. proc cc-check-defines {args} { cc-check-some-feature $args { cctest_define $each } } # @cc-check-decls name ... # # Checks that each given name is either a preprocessor symbol or rvalue # such as an enum. Note that the define used is 'HAVE_DECL_xxx' # rather than 'HAVE_xxx'. proc cc-check-decls {args} { set ret 1 foreach name $args { msg-checking "Checking for $name..." set r [cctest_decl $name] define-feature "decl $name" $r if {$r} { msg-result "ok" } else { msg-result "not found" set ret 0 } } return $ret } # @cc-check-functions function ... # # Checks that the given functions exist (can be linked). proc cc-check-functions {args} { cc-check-some-feature $args { cctest_function $each } } # @cc-check-members type.member ... # # Checks that the given type/structure members exist. # A structure member is of the form 'struct stat.st_mtime'. proc cc-check-members {args} { cc-check-some-feature $args { cctest_member $each } } # @cc-check-function-in-lib function libs ?otherlibs? # # Checks that the given function can be found in one of the libs. # # First checks for no library required, then checks each of the libraries # in turn. # # If the function is found, the feature is defined and 'lib_$function' is defined # to '-l$lib' where the function was found, or "" if no library required. # In addition, '-l$lib' is prepended to the 'LIBS' define. # # If additional libraries may be needed for linking, they should be specified # with '$extralibs' as '-lotherlib1 -lotherlib2'. # These libraries are not automatically added to 'LIBS'. # # Returns 1 if found or 0 if not. # proc cc-check-function-in-lib {function libs {otherlibs {}}} { msg-checking "Checking libs for $function..." set found 0 cc-with [list -libs $otherlibs] { if {[cctest_function $function]} { msg-result "none needed" define lib_$function "" incr found } else { foreach lib $libs { cc-with [list -libs -l$lib] { if {[cctest_function $function]} { msg-result -l$lib define lib_$function -l$lib # prepend to LIBS define LIBS "-l$lib [get-define LIBS]" incr found break } } } } } define-feature $function $found if {!$found} { msg-result "no" } return $found } # @cc-check-tools tool ... # # Checks for existence of the given compiler tools, taking # into account any cross compilation prefix. # # For example, when checking for 'ar', first 'AR' is checked on the command # line and then in the environment. If not found, '${host}-ar' or # simply 'ar' is assumed depending upon whether cross compiling. # The path is searched for this executable, and if found 'AR' is defined # to the executable name. # Note that even when cross compiling, the simple 'ar' is used as a fallback, # but a warning is generated. This is necessary for some toolchains. # # It is an error if the executable is not found. # proc cc-check-tools {args} { foreach tool $args { set TOOL [string toupper $tool] set exe [get-env $TOOL [get-define cross]$tool] if {[find-executable {*}$exe]} { define $TOOL $exe continue } if {[find-executable {*}$tool]} { msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect" define $TOOL $tool continue } user-error "Failed to find $exe" } } # @cc-check-progs prog ... # # Checks for existence of the given executables on the path. # # For example, when checking for 'grep', the path is searched for # the executable, 'grep', and if found 'GREP' is defined as 'grep'. # # If the executable is not found, the variable is defined as 'false'. # Returns 1 if all programs were found, or 0 otherwise. # proc cc-check-progs {args} { set failed 0 foreach prog $args { set PROG [string toupper $prog] msg-checking "Checking for $prog..." if {![find-executable $prog]} { msg-result no define $PROG false incr failed } else { msg-result ok define $PROG $prog } } expr {!$failed} } # @cc-path-progs prog ... # # Like cc-check-progs, but sets the define to the full path rather # than just the program name. # proc cc-path-progs {args} { set failed 0 foreach prog $args { set PROG [string toupper $prog] msg-checking "Checking for $prog..." set path [find-executable-path $prog] if {$path eq ""} { msg-result no define $PROG false incr failed } else { msg-result $path define $PROG $path } } expr {!$failed} } # Adds the given settings to $::autosetup(ccsettings) and # returns the old settings. # proc cc-add-settings {settings} { if {[llength $settings] % 2} { autosetup-error "settings list is missing a value: $settings" } set prev [cc-get-settings] # workaround a bug in some versions of jimsh by forcing # conversion of $prev to a list llength $prev array set new $prev foreach {name value} $settings { switch -exact -- $name { -cflags - -includes { # These are given as lists lappend new($name) {*}[list-non-empty $value] } -declare { lappend new($name) $value } -libs { # Note that new libraries are added before previous libraries set new($name) [list {*}[list-non-empty $value] {*}$new($name)] } -link - -lang - -nooutput { set new($name) $value } -source - -sourcefile - -code { # XXX: These probably are only valid directly from cctest set new($name) $value } default { autosetup-error "unknown cctest setting: $name" } } } cc-store-settings [array get new] return $prev } proc cc-store-settings {new} { set ::autosetup(ccsettings) $new } proc cc-get-settings {} { return $::autosetup(ccsettings) } # Similar to cc-add-settings, but each given setting # simply replaces the existing value. # # Returns the previous settings proc cc-update-settings {args} { set prev [cc-get-settings] cc-store-settings [dict merge $prev $args] return $prev } # @cc-with settings ?{ script }? # # Sets the given 'cctest' settings and then runs the tests in '$script'. # Note that settings such as '-lang' replace the current setting, while # those such as '-includes' are appended to the existing setting. # # If no script is given, the settings become the default for the remainder # of the 'auto.def' file. # ## cc-with {-lang c++} { ## # This will check with the C++ compiler ## cc-check-types bool ## cc-with {-includes signal.h} { ## # This will check with the C++ compiler, signal.h and any existing includes. ## ... ## } ## # back to just the C++ compiler ## } # # The '-libs' setting is special in that newer values are added *before* earlier ones. # ## cc-with {-libs {-lc -lm}} { ## cc-with {-libs -ldl} { ## cctest -libs -lsocket ... ## # libs will be in this order: -lsocket -ldl -lc -lm ## } ## } proc cc-with {settings args} { if {[llength $args] == 0} { cc-add-settings $settings } elseif {[llength $args] > 1} { autosetup-error "usage: cc-with settings ?script?" } else { set save [cc-add-settings $settings] set rc [catch {uplevel 1 [lindex $args 0]} result info] cc-store-settings $save if {$rc != 0} { return -code [dict get $info -code] $result } return $result } } # @cctest ?settings? # # Low level C/C++ compiler checker. Compiles and or links a small C program # according to the arguments and returns 1 if OK, or 0 if not. # # Supported settings are: # ## -cflags cflags A list of flags to pass to the compiler ## -includes list A list of includes, e.g. {stdlib.h stdio.h} ## -declare code Code to declare before main() ## -link 1 Don't just compile, link too ## -lang c|c++ Use the C (default) or C++ compiler ## -libs liblist List of libraries to link, e.g. {-ldl -lm} ## -code code Code to compile in the body of main() ## -source code Compile a complete program. Ignore -includes, -declare and -code ## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file] ## -nooutput 1 Treat any compiler output (e.g. a warning) as an error # # Unless '-source' or '-sourcefile' is specified, the C program looks like: # ## #include /* same for remaining includes in the list */ ## ## declare-code /* any code in -declare, verbatim */ ## ## int main(void) { ## code /* any code in -code, verbatim */ ## return 0; ## } # # Any failures are recorded in 'config.log' # proc cctest {args} { set src conftest__.c set tmp conftest__ # Easiest way to merge in the settings cc-with $args { array set opts [cc-get-settings] } if {[info exists opts(-sourcefile)]} { set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"] } if {[info exists opts(-source)]} { set lines $opts(-source) } else { foreach i $opts(-includes) { if {$opts(-code) ne "" && ![feature-checked $i]} { # Compiling real code with an unchecked header file # Quickly (and silently) check for it now # Remove all -includes from settings before checking set saveopts [cc-update-settings -includes {}] msg-quiet cc-check-includes $i cc-store-settings $saveopts } if {$opts(-code) eq "" || [have-feature $i]} { lappend source "#include <$i>" } } lappend source {*}$opts(-declare) lappend source "int main(void) {" lappend source $opts(-code) lappend source "return 0;" lappend source "}" set lines [join $source \n] } # Build the command line set cmdline {} lappend cmdline {*}[get-define CCACHE] switch -exact -- $opts(-lang) { c++ { lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS] } c { lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS] } default { autosetup-error "cctest called with unknown language: $opts(-lang)" } } if {$opts(-link)} { lappend cmdline {*}[get-define LDFLAGS] } else { set tmp conftest__.o lappend cmdline -c } lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""] lappend cmdline $src -o $tmp {*}$opts(-libs) if {$opts(-link)} { lappend cmdline {*}[get-define LIBS] } # At this point we have the complete command line and the # complete source to be compiled. Get the result from cache if # we can if {[info exists ::cc_cache($cmdline,$lines)]} { msg-checking "(cached) " set ok $::cc_cache($cmdline,$lines) if {$::autosetup(debug)} { configlog "From cache (ok=$ok): [join $cmdline]" configlog "============" configlog $lines configlog "============" } return $ok } writefile $src $lines\n set ok 1 set err [catch {exec-with-stderr {*}$cmdline} result errinfo] if {$err || ($opts(-nooutput) && [string length $result])} { configlog "Failed: [join $cmdline]" configlog $result configlog "============" configlog "The failed code was:" configlog $lines configlog "============" set ok 0 } elseif {$::autosetup(debug)} { configlog "Compiled OK: [join $cmdline]" configlog "============" configlog $lines configlog "============" } file delete $src file delete $tmp # cache it set ::cc_cache($cmdline,$lines) $ok return $ok } # @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*? # # Deprecated - see 'make-config-header' proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} { user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead" make-config-header $file -auto $autopatterns -bare $barepatterns } # @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ... # # Examines all defined variables which match the given patterns # and writes an include file, '$file', which defines each of these. # Variables which match '-auto' are output as follows: # - defines which have the value '0' are ignored. # - defines which have integer values are defined as the integer value. # - any other value is defined as a string, e.g. '"value"' # Variables which match '-bare' are defined as-is. # Variables which match '-str' are defined as a string, e.g. '"value"' # Variables which match '-none' are omitted. # # Note that order is important. The first pattern that matches is selected. # Default behaviour is: # ## -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none * # # If the file would be unchanged, it is not written. proc make-config-header {file args} { set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]] file mkdir [file dirname $file] set lines {} lappend lines "#ifndef $guard" lappend lines "#define $guard" # Add some defaults lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* foreach n [lsort [dict keys [all-defines]]] { set value [get-define $n] set type [calc-define-output-type $n $args] switch -exact -- $type { -bare { # Just output the value unchanged } -none { continue } -str { set value \"[string map [list \\ \\\\ \" \\\"] $value]\" } -auto { # Automatically determine the type if {$value eq "0"} { lappend lines "/* #undef $n */" continue } if {![string is integer -strict $value]} { set value \"[string map [list \\ \\\\ \" \\\"] $value]\" } } "" { continue } default { autosetup-error "Unknown type in make-config-header: $type" } } lappend lines "#define $n $value" } lappend lines "#endif" set buf [join $lines \n] write-if-changed $file $buf { msg-result "Created $file" } } proc calc-define-output-type {name spec} { foreach {type patterns} $spec { foreach pattern $patterns { if {[string match $pattern $name]} { return $type } } } return "" } # Initialise some values from the environment or commandline or default settings foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} { lassign $i var default define $var [get-env $var $default] } if {[env-is-set CC]} { # Set by the user, so don't try anything else set try [list [get-env CC ""]] } else { # Try some reasonable options set try [list [get-define cross]cc [get-define cross]gcc] } define CC [find-an-executable {*}$try] if {[get-define CC] eq ""} { user-error "Could not find a C compiler. Tried: [join $try ", "]" } define CPP [get-env CPP "[get-define CC] -E"] # XXX: Could avoid looking for a C++ compiler until requested # Note that if CXX isn't found, we just set it to "false". It might not be needed. if {[env-is-set CXX]} { define CXX [find-an-executable -required [get-env CXX ""]] } else { define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false] } # CXXFLAGS default to CFLAGS if not specified define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]] # May need a CC_FOR_BUILD, so look for one define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false] if {[get-define CC] eq ""} { user-error "Could not find a C compiler. Tried: [join $try ", "]" } define CCACHE [find-an-executable [get-env CCACHE ccache]] # Initial cctest settings cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0} set autosetup(cc-include-deps) {} msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]" if {[get-define CXX] ne "false"} { msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]" } msg-result "Build C compiler...[get-define CC_FOR_BUILD]" # On Darwin, we prefer to use -g0 to avoid creating .dSYM directories # but some compilers may not support it, so test here. switch -glob -- [get-define host] { *-*-darwin* { if {[cctest -cflags {-g0}]} { define cc-default-debug -g0 } } } if {![cc-check-includes stdlib.h]} { user-error "Compiler does not work. See config.log" } neomutt-neomutt-20171215/autosetup/default.auto000066400000000000000000000010221321473123000215000ustar00rootroot00000000000000# Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Auto-load module for 'make' build system integration use init autosetup_add_init_type make {Simple "make" build system} { autosetup_check_create auto.def \ {# Initial auto.def created by 'autosetup --init=make' use cc # Add any user options here options { } make-config-header config.h make-template Makefile.in } if {![file exists Makefile.in]} { puts "Note: I don't see Makefile.in. You will probably need to create one." } } neomutt-neomutt-20171215/autosetup/mutt-gettext.tcl000066400000000000000000000055741321473123000223610ustar00rootroot00000000000000# Copyright (c) 2017 Pietro Cerutti . All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # @synopsis: # # The 'gettext' module implements a subset of the functionality provided by the # AM_GNU_GETTEXT macro. Namely, it supports the 'external' configuration where # a libintl library is supposed to be present in the system, in contrast to it # being built as part of the package. use system cc # @check-gettext # # Try to locate a usable gettext installation in the system. # # If found, returns 1 and sets 'ENABLE_NLS' to 1. Otherwise, returns 0. proc check-gettext {prefix} { set src { #include extern int _nl_msg_cat_cntr; extern int *_nl_domain_bindings; int main(void) { bindtextdomain("", ""); return * gettext(""); } } set g_msg_l [list $prefix libintl libc] set g_cflags_l [list -I$prefix/include {} {} ] set g_ldflags_l [list -L$prefix/lib {} {} ] set g_libs_l [list -lintl -lintl {} ] foreach g_msg $g_msg_l g_cflags $g_cflags_l g_ldflags $g_ldflags_l g_libs $g_libs_l { msg-checking "Checking for GNU gettext in $g_msg..." if {[cctest -cflags $g_cflags -libs "$g_ldflags $g_libs" -link 1 \ -source $src]} { define-append CFLAGS $g_cflags define-append LDFLAGS $g_ldflags define-append LIBS $g_libs define INTLLIBS "$g_ldflags $g_libs" msg-result yes cc-check-functions bind_textdomain_codeset define ENABLE_NLS return 1 } msg-result no } return 0 } neomutt-neomutt-20171215/autosetup/mutt-iconv.tcl000066400000000000000000000070761321473123000220120ustar00rootroot00000000000000# Copyright (c) 2017 Pietro Cerutti . All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # @synopsis: # # The 'iconv' module tries to mimic the logic provided by the AM_ICONV macro, # by checking for a working iconv() implementation first in libiconv under # prefix, then in libiconv alone, and last in libc. # use system cc # @check-iconv # # Try to locate a usable iconv() implementation, and check whether the second # parameter to iconv() needs a const qualifier. # # If found, returns 1 and sets 'HAVE_ICONV' to 1 and ICONV_CONST to either the # emppty string or 'const', depending on the signature of iconv() # If not found, returns 0. proc check-iconv {prefix} { set iconv_code { iconv_t cd = iconv_open("", ""); iconv(cd, NULL, NULL, NULL, NULL); iconv_close(cd); } set iconv_const_code { size_t iconv (iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); } set iconv_inc {stdlib.h iconv.h} # Check to compile and link a simple iconv() call as follows: # 1. check with $prefix using -liconv # 2. check without prefix using -liconv # 3. check without prefix using only libc # prefix noprefix libc set i_msg_l [list $prefix libiconv libc] set i_cflags_l [list -I$prefix/include {} {} ] set i_ldflags_l [list -L$prefix/lib {} {} ] set i_libs_l [list -liconv -liconv {} ] foreach i_msg $i_msg_l i_cflags $i_cflags_l i_ldflags $i_ldflags_l i_libs $i_libs_l { msg-checking "Checking for iconv() in $i_msg..." if {[cctest -cflags $i_cflags -libs "$i_ldflags $i_libs" -link 1 \ -includes $iconv_inc -code $iconv_code]} { define-append CFLAGS $i_cflags define-append LDFLAGS $i_ldflags define-append LIBS $i_libs define-feature ICONV msg-result "yes" # ICONV_CONST msg-checking "Checking whether iconv() needs const..." if {[cctest -cflags $i_cflags -libs "$i_ldflags $i_libs" -link 1 \ -includes $iconv_inc -code $iconv_const_code]} { msg-result "no" define ICONV_CONST "" } else { msg-result "yes" define ICONV_CONST const } # we found it return 1 } msg-result "no" } return 0 } neomutt-neomutt-20171215/autosetup/pkg-config.tcl000066400000000000000000000077301321473123000217260ustar00rootroot00000000000000# Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'pkg-config' module allows package information to be found via 'pkg-config'. # # If not cross-compiling, the package path should be determined automatically # by 'pkg-config'. # If cross-compiling, the default package path is the compiler sysroot. # If the C compiler doesn't support '-print-sysroot', the path can be supplied # by the '--sysroot' option or by defining 'SYSROOT'. # # 'PKG_CONFIG' may be set to use an alternative to 'pkg-config'. use cc module-options { sysroot:dir => "Override compiler sysroot for pkg-config search path" } # @pkg-config-init ?required? # # Initialises the 'pkg-config' system. Unless '$required' is set to 0, # it is a fatal error if a usable 'pkg-config' is not found . # # This command will normally be called automatically as required, # but it may be invoked explicitly if lack of 'pkg-config' is acceptable. # # Returns 1 if ok, or 0 if 'pkg-config' not found/usable (only if '$required' is 0). # proc pkg-config-init {{required 1}} { if {[is-defined HAVE_PKG_CONFIG]} { return [get-define HAVE_PKG_CONFIG] } set found 0 define PKG_CONFIG [get-env PKG_CONFIG pkg-config] msg-checking "Checking for pkg-config..." if {[catch {exec [get-define PKG_CONFIG] --version} version]} { msg-result "[get-define PKG_CONFIG] (not found)" if {$required} { user-error "No usable pkg-config" } } else { msg-result $version define PKG_CONFIG_VERSION $version set found 1 if {[opt-str sysroot o]} { define SYSROOT [file-normalize $o] msg-result "Using specified sysroot [get-define SYSROOT]" } elseif {[get-define build] ne [get-define host]} { if {[catch {exec-with-stderr [get-define CC] -print-sysroot} result errinfo] == 0} { # Use the compiler sysroot, if there is one define SYSROOT $result msg-result "Found compiler sysroot $result" } else { set msg "pkg-config: Cross compiling, but no compiler sysroot and no --sysroot supplied" if {$required} { user-error $msg } else { msg-result $msg } set found 0 } } if {[is-defined SYSROOT]} { set sysroot [get-define SYSROOT] # XXX: It's possible that these should be set only when invoking pkg-config global env set env(PKG_CONFIG_DIR) "" # Do we need to try /usr/local as well or instead? set env(PKG_CONFIG_LIBDIR) $sysroot/usr/lib/pkgconfig:$sysroot/usr/share/pkgconfig set env(PKG_CONFIG_SYSROOT_DIR) $sysroot } } define HAVE_PKG_CONFIG $found return $found } # @pkg-config module ?requirements? # # Use 'pkg-config' to find the given module meeting the given requirements. # e.g. # ## pkg-config pango >= 1.37.0 # # If found, returns 1 and sets 'HAVE_PKG_PANGO' to 1 along with: # ## PKG_PANGO_VERSION to the found version ## PKG_PANGO_LIBS to the required libs (--libs-only-l) ## PKG_PANGO_LDFLAGS to the required linker flags (--libs-only-L) ## PKG_PANGO_CFLAGS to the required compiler flags (--cflags) # # If not found, returns 0. # proc pkg-config {module args} { set ok [pkg-config-init] msg-checking "Checking for $module $args..." if {!$ok} { msg-result "no pkg-config" return 0 } if {[catch {exec [get-define PKG_CONFIG] --modversion "$module $args"} version]} { msg-result "not found" configlog "pkg-config --modversion $module $args: $version" return 0 } msg-result $version set prefix [feature-define-name $module PKG_] define HAVE_${prefix} define ${prefix}_VERSION $version define ${prefix}_LIBS [exec pkg-config --libs-only-l $module] define ${prefix}_LDFLAGS [exec pkg-config --libs-only-L $module] define ${prefix}_CFLAGS [exec pkg-config --cflags $module] return 1 } # @pkg-config-get module setting # # Convenience access to the results of 'pkg-config'. # # For example, '[pkg-config-get pango CFLAGS]' returns # the value of 'PKG_PANGO_CFLAGS', or '""' if not defined. proc pkg-config-get {module name} { set prefix [feature-define-name $module PKG_] get-define ${prefix}_${name} "" } neomutt-neomutt-20171215/autosetup/system.tcl000066400000000000000000000207121321473123000212210ustar00rootroot00000000000000# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # This module supports common system interrogation and options # such as '--host', '--build', '--prefix', and setting 'srcdir', 'builddir', and 'EXEEXT'. # # It also support the "feature" naming convention, where searching # for a feature such as 'sys/type.h' defines 'HAVE_SYS_TYPES_H'. # # It defines the following variables, based on '--prefix' unless overridden by the user: # ## datadir ## sysconfdir ## sharedstatedir ## localstatedir ## infodir ## mandir ## includedir # # If '--prefix' is not supplied, it defaults to '/usr/local' unless 'defaultprefix' is defined *before* # including the 'system' module. if {[is-defined defaultprefix]} { user-notice "Note: defaultprefix is deprecated. Use options-defaults to set default options" options-defaults [list prefix [get-define defaultprefix]] } module-options [subst -noc -nob { host:host-alias => {a complete or partial cpu-vendor-opsys for the system where the application will run (defaults to the same value as --build)} build:build-alias => {a complete or partial cpu-vendor-opsys for the system where the application will be built (defaults to the result of running config.guess)} prefix:dir=/usr/local => {the target directory for the build (default: '@default@')} # These (hidden) options are supported for autoconf/automake compatibility exec-prefix: bindir: sbindir: includedir: mandir: infodir: libexecdir: datadir: libdir: sysconfdir: sharedstatedir: localstatedir: maintainer-mode=0 dependency-tracking=0 silent-rules=0 }] # @check-feature name { script } # # defines feature '$name' to the return value of '$script', # which should be 1 if found or 0 if not found. # # e.g. the following will define 'HAVE_CONST' to 0 or 1. # ## check-feature const { ## cctest -code {const int _x = 0;} ## } proc check-feature {name code} { msg-checking "Checking for $name..." set r [uplevel 1 $code] define-feature $name $r if {$r} { msg-result "ok" } else { msg-result "not found" } return $r } # @have-feature name ?default=0? # # Returns the value of feature '$name' if defined, or '$default' if not. # # See 'feature-define-name' for how the "feature" name # is translated into the "define" name. # proc have-feature {name {default 0}} { get-define [feature-define-name $name] $default } # @define-feature name ?value=1? # # Sets the feature 'define' to '$value'. # # See 'feature-define-name' for how the "feature" name # is translated into the "define" name. # proc define-feature {name {value 1}} { define [feature-define-name $name] $value } # @feature-checked name # # Returns 1 if feature '$name' has been checked, whether true or not. # proc feature-checked {name} { is-defined [feature-define-name $name] } # @feature-define-name name ?prefix=HAVE_? # # Converts a "feature" name to the corresponding "define", # e.g. 'sys/stat.h' becomes 'HAVE_SYS_STAT_H'. # # Converts '*' to 'P' and all non-alphanumeric to underscore. # proc feature-define-name {name {prefix HAVE_}} { string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _] } # @write-if-changed filename contents ?script? # # If '$filename' doesn't exist, or it's contents are different to '$contents', # the file is written and '$script' is evaluated. # # Otherwise a "file is unchanged" message is displayed. proc write-if-changed {file buf {script {}}} { set old [readfile $file ""] if {$old eq $buf && [file exists $file]} { msg-result "$file is unchanged" } else { writefile $file $buf\n uplevel 1 $script } } # @make-template template ?outfile? # # Reads the input file '/$template' and writes the output file '$outfile' # (unless unchanged). # If '$outfile' is blank/omitted, '$template' should end with '.in' which # is removed to create the output file name. # # Each pattern of the form '@define@' is replaced with the corresponding # "define", if it exists, or left unchanged if not. # # The special value '@srcdir@' is substituted with the relative # path to the source directory from the directory where the output # file is created, while the special value '@top_srcdir@' is substituted # with the relative path to the top level source directory. # # Conditional sections may be specified as follows: ## @if name == value ## lines ## @else ## lines ## @endif # # Where 'name' is a defined variable name and '@else' is optional. # If the expression does not match, all lines through '@endif' are ignored. # # The alternative forms may also be used: ## @if name ## @if name != value # # Where the first form is true if the variable is defined, but not empty nor 0. # # Currently these expressions can't be nested. # proc make-template {template {out {}}} { set infile [file join $::autosetup(srcdir) $template] if {![file exists $infile]} { user-error "Template $template is missing" } # Define this as late as possible define AUTODEPS $::autosetup(deps) if {$out eq ""} { if {[file ext $template] ne ".in"} { autosetup-error "make_template $template has no target file and can't guess" } set out [file rootname $template] } set outdir [file dirname $out] # Make sure the directory exists file mkdir $outdir # Set up srcdir and top_srcdir to be relative to the target dir define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir] define top_srcdir [relative-path $::autosetup(srcdir) $outdir] set mapping {} foreach {n v} [array get ::define] { lappend mapping @$n@ $v } set result {} foreach line [split [readfile $infile] \n] { if {[info exists cond]} { set l [string trimright $line] if {$l eq "@endif"} { unset cond continue } if {$l eq "@else"} { set cond [expr {!$cond}] continue } if {$cond} { lappend result $line } continue } if {[regexp {^@if\s+(\w+)(.*)} $line -> name expression]} { lassign $expression equal value set varval [get-define $name ""] if {$equal eq ""} { set cond [expr {$varval ni {"" 0}}] } else { set cond [expr {$varval eq $value}] if {$equal ne "=="} { set cond [expr {!$cond}] } } continue } lappend result $line } write-if-changed $out [string map $mapping [join $result \n]] { msg-result "Created [relative-path $out] from [relative-path $template]" } } # build/host tuples and cross-compilation prefix opt-str build build "" define build_alias $build if {$build eq ""} { define build [config_guess] } else { define build [config_sub $build] } opt-str host host "" define host_alias $host if {$host eq ""} { define host [get-define build] set cross "" } else { define host [config_sub $host] set cross $host- } define cross [get-env CROSS $cross] # build/host _cpu, _vendor and _os foreach type {build host} { set v [get-define $type] if {![regexp {^([^-]+)-([^-]+)-(.*)$} $v -> cpu vendor os]} { user-error "Invalid canonical $type: $v" } define ${type}_cpu $cpu define ${type}_vendor $vendor define ${type}_os $os } opt-str prefix prefix /usr/local # These are for compatibility with autoconf define target [get-define host] define prefix $prefix define builddir $autosetup(builddir) define srcdir $autosetup(srcdir) define top_srcdir $autosetup(srcdir) define abs_top_srcdir [file-normalize $autosetup(srcdir)] define abs_top_builddir [file-normalize $autosetup(builddir)] # autoconf supports all of these define exec_prefix [opt-str exec-prefix exec_prefix $prefix] foreach {name defpath} { bindir /bin sbindir /sbin libexecdir /libexec libdir /lib } { define $name [opt-str $name o $exec_prefix$defpath] } foreach {name defpath} { datadir /share sharedstatedir /com infodir /share/info mandir /share/man includedir /include } { define $name [opt-str $name o $prefix$defpath] } if {$prefix ne {/usr}} { opt-str sysconfdir sysconfdir $prefix/etc } else { opt-str sysconfdir sysconfdir /etc } define sysconfdir $sysconfdir define localstatedir [opt-str localstatedir o /var] define SHELL [get-env SHELL [find-an-executable sh bash ksh]] # These could be used to generate Makefiles following some automake conventions define AM_SILENT_RULES [opt-bool silent-rules] define AM_MAINTAINER_MODE [opt-bool maintainer-mode] define AM_DEPENDENCY_TRACKING [opt-bool dependency-tracking] # Windows vs. non-Windows switch -glob -- [get-define host] { *-*-ming* - *-*-cygwin - *-*-msys { define-feature windows define EXEEXT .exe } default { define EXEEXT "" } } # Display msg-result "Host System...[get-define host]" msg-result "Build System...[get-define build]" neomutt-neomutt-20171215/autosetup/tmake.auto000066400000000000000000000024011321473123000211570ustar00rootroot00000000000000# Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Auto-load module for 'tmake' build system integration use init autosetup_add_init_type tmake "Tcl-based tmake build system" { autosetup_check_create auto.def \ {# Initial auto.def created by 'autosetup --init=tmake' # vim:set syntax=tcl: use cc cc-lib cc-db cc-shared use tmake # Add any user options here # Really want a --configure that takes over the rest of the command line options { } cc-check-tools ar ranlib set objdir [get-env BUILDDIR objdir] make-config-header $objdir/include/autoconf.h make-tmake-settings $objdir/settings.conf {[A-Z]*} *dir lib_* } autosetup_check_create project.spec \ {# Initial project.spec created by 'autosetup --init=tmake' tmake-require-version 0.7.3 # vim:set syntax=tcl: define? DESTDIR _install # XXX If configure creates additional/different files than include/autoconf.h # that should be reflected here Autosetup include/autoconf.h # e.g. for autoconf.h IncludePaths include ifconfig !CONFIGURED { # Not configured, so don't process subdirs AutoSubDirs off # And don't process this file any further ifconfig false } } if {![file exists build.spec]} { puts "Note: I don't see build.spec. Try running: tmake --genie" } } neomutt-neomutt-20171215/autosetup/tmake.tcl000066400000000000000000000023001321473123000207670ustar00rootroot00000000000000# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'tmake' module makes it easy to support the tmake build system. # # The following variables are set: # ## CONFIGURED - to indicate that the project is configured use system module-options {} define CONFIGURED # @make-tmake-settings outfile patterns ... # # Examines all defined variables which match the given patterns (defaults to '*') # and writes a tmake-compatible .conf file defining those variables. # For example, if 'ABC' is '"3 monkeys"' and 'ABC' matches a pattern, then the file will include: # ## define ABC {3 monkeys} # # If the file would be unchanged, it is not written. # # Typical usage is: # ## make-tmake-settings [get-env BUILDDIR objdir]/settings.conf {[A-Z]*} proc make-tmake-settings {file args} { file mkdir [file dirname $file] set lines {} if {[llength $args] == 0} { set args * } foreach n [lsort [dict keys [all-defines]]] { foreach p $args { if {[string match $p $n]} { set value [get-define $n] lappend lines "define $n [list $value]" break } } } set buf [join $lines \n] write-if-changed $file $buf { msg-result "Created $file" } } neomutt-neomutt-20171215/bcache.c000066400000000000000000000146301321473123000165130ustar00rootroot00000000000000/** * @file * Body Caching - local copies of email bodies * * @authors * Copyright (C) 2006-2007,2009 Brendan Cully * Copyright (C) 2006,2009 Rocco Rutte * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #include #include #include #include "mutt/mutt.h" #include "bcache.h" #include "globals.h" #include "mutt_account.h" #include "protos.h" #include "url.h" /** * struct BodyCache - Local cache of email bodies */ struct BodyCache { char path[_POSIX_PATH_MAX]; size_t pathlen; }; static int bcache_path(struct Account *account, const char *mailbox, char *dst, size_t dstlen) { char host[STRING]; char path[_POSIX_PATH_MAX]; struct Url url; int len; if (!account || !MessageCachedir || !*MessageCachedir || !dst || !dstlen) return -1; /* make up a Url we can turn into a string */ memset(&url, 0, sizeof(struct Url)); mutt_account_tourl(account, &url); /* * mutt_account_tourl() just sets up some pointers; * if this ever changes, we have a memleak here */ url.path = NULL; if (url_tostring(&url, host, sizeof(host), U_PATH) < 0) { mutt_debug(1, "URL to string failed\n"); return -1; } mutt_encode_path(path, sizeof(path), NONULL(mailbox)); int plen = mutt_str_strlen(path); if (plen == 0) return -1; len = snprintf(dst, dstlen - 1, "%s/%s%s%s", MessageCachedir, host, path, (*path && path[plen - 1] == '/') ? "" : "/"); mutt_debug(3, "rc: %d, path: '%s'\n", len, dst); if (len < 0 || (size_t) len >= dstlen - 1) return -1; mutt_debug(3, "directory: '%s'\n", dst); return 0; } static int mutt_bcache_move(struct BodyCache *bcache, const char *id, const char *newid) { char path[_POSIX_PATH_MAX]; char newpath[_POSIX_PATH_MAX]; if (!bcache || !id || !*id || !newid || !*newid) return -1; snprintf(path, sizeof(path), "%s%s", bcache->path, id); snprintf(newpath, sizeof(newpath), "%s%s", bcache->path, newid); mutt_debug(3, "bcache: mv: '%s' '%s'\n", path, newpath); return rename(path, newpath); } struct BodyCache *mutt_bcache_open(struct Account *account, const char *mailbox) { struct BodyCache *bcache = NULL; if (!account) goto bail; bcache = mutt_mem_calloc(1, sizeof(struct BodyCache)); if (bcache_path(account, mailbox, bcache->path, sizeof(bcache->path)) < 0) goto bail; bcache->pathlen = mutt_str_strlen(bcache->path); return bcache; bail: if (bcache) FREE(&bcache); return NULL; } void mutt_bcache_close(struct BodyCache **bcache) { if (!bcache || !*bcache) return; FREE(bcache); } FILE *mutt_bcache_get(struct BodyCache *bcache, const char *id) { char path[_POSIX_PATH_MAX]; FILE *fp = NULL; if (!id || !*id || !bcache) return NULL; path[0] = '\0'; mutt_str_strncat(path, sizeof(path), bcache->path, bcache->pathlen); mutt_str_strncat(path, sizeof(path), id, mutt_str_strlen(id)); fp = mutt_file_fopen(path, "r"); mutt_debug(3, "bcache: get: '%s': %s\n", path, fp == NULL ? "no" : "yes"); return fp; } FILE *mutt_bcache_put(struct BodyCache *bcache, const char *id) { char path[LONG_STRING]; struct stat sb; if (!id || !*id || !bcache) return NULL; if (snprintf(path, sizeof(path), "%s%s%s", bcache->path, id, ".tmp") >= sizeof(path)) { mutt_error(_("Path too long: %s%s%s"), bcache->path, id, ".tmp"); return NULL; } if (stat(bcache->path, &sb) == 0) { if (!S_ISDIR(sb.st_mode)) { mutt_error(_("Message cache isn't a directory: %s."), bcache->path); return NULL; } } else { if (mutt_file_mkdir(bcache->path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { mutt_error(_("Can't create %s %s"), bcache->path, strerror(errno)); return NULL; } } mutt_debug(3, "bcache: put: '%s'\n", path); return mutt_file_fopen(path, "w+"); } int mutt_bcache_commit(struct BodyCache *bcache, const char *id) { char tmpid[_POSIX_PATH_MAX]; snprintf(tmpid, sizeof(tmpid), "%s.tmp", id); return mutt_bcache_move(bcache, tmpid, id); } int mutt_bcache_del(struct BodyCache *bcache, const char *id) { char path[_POSIX_PATH_MAX]; if (!id || !*id || !bcache) return -1; path[0] = '\0'; mutt_str_strncat(path, sizeof(path), bcache->path, bcache->pathlen); mutt_str_strncat(path, sizeof(path), id, mutt_str_strlen(id)); mutt_debug(3, "bcache: del: '%s'\n", path); return unlink(path); } int mutt_bcache_exists(struct BodyCache *bcache, const char *id) { char path[_POSIX_PATH_MAX]; struct stat st; int rc = 0; if (!id || !*id || !bcache) return -1; path[0] = '\0'; mutt_str_strncat(path, sizeof(path), bcache->path, bcache->pathlen); mutt_str_strncat(path, sizeof(path), id, mutt_str_strlen(id)); if (stat(path, &st) < 0) rc = -1; else rc = S_ISREG(st.st_mode) && st.st_size != 0 ? 0 : -1; mutt_debug(3, "bcache: exists: '%s': %s\n", path, rc == 0 ? "yes" : "no"); return rc; } int mutt_bcache_list(struct BodyCache *bcache, int (*want_id)(const char *id, struct BodyCache *bcache, void *data), void *data) { DIR *d = NULL; struct dirent *de = NULL; int rc = -1; if (!bcache || !(d = opendir(bcache->path))) goto out; rc = 0; mutt_debug(3, "bcache: list: dir: '%s'\n", bcache->path); while ((de = readdir(d))) { if ((mutt_str_strncmp(de->d_name, ".", 1) == 0) || (mutt_str_strncmp(de->d_name, "..", 2) == 0)) { continue; } mutt_debug(3, "bcache: list: dir: '%s', id :'%s'\n", bcache->path, de->d_name); if (want_id && want_id(de->d_name, bcache, data) != 0) goto out; rc++; } out: if (d) { if (closedir(d) < 0) rc = -1; } mutt_debug(3, "bcache: list: did %d entries\n", rc); return rc; } neomutt-neomutt-20171215/bcache.h000066400000000000000000000102731321473123000165170ustar00rootroot00000000000000/** * @file * Body Caching - local copies of email bodies * * @authors * Copyright (C) 2006-2007 Brendan Cully * Copyright (C) 2006 Rocco Rutte * * @copyright * 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, see . */ #ifndef _MUTT_BCACHE_H #define _MUTT_BCACHE_H #include #include struct Account; struct BodyCache; /** * mutt_bcache_open - Open an Email-Body Cache * @param account current mailbox' account (required) * @param mailbox path to the mailbox of the account (optional) * @retval NULL on failure * * The driver using it is responsible for ensuring that hierarchies are * separated by '/' (if it knows of such a concepts like mailboxes or * hierarchies) */ struct BodyCache *mutt_bcache_open(struct Account *account, const char *mailbox); /** * mutt_bcache_close - Close an Email-Body Cache * @param bcache Body cache * * Free all memory of bcache and finally FREE() it, too. */ void mutt_bcache_close(struct BodyCache **bcache); /** * mutt_bcache_get - Open a file in the Body Cache * @param bcache Body Cache from mutt_bcache_open() * @param id Per-mailbox unique identifier for the message * @retval FILE* on success * @retval NULL on failure */ FILE *mutt_bcache_get(struct BodyCache *bcache, const char *id); /** * mutt_bcache_put - Create a file in the Body Cache * @param bcache Body Cache from mutt_bcache_open() * @param id Per-mailbox unique identifier for the message * @retval FILE* on success * @retval NULL on failure * * The returned FILE* is in a temporary location. * Use mutt_bcache_commit to put it into place */ FILE *mutt_bcache_put(struct BodyCache *bcache, const char *id); /** * mutt_bcache_commit - Move a temporary file into the Body Cache * @param bcache Body Cache from mutt_bcache_open() * @param id Per-mailbox unique identifier for the message * @retval 0 on success * @retval -1 on failure */ int mutt_bcache_commit(struct BodyCache *bcache, const char *id); /** * mutt_bcache_del - Delete a file from the Body Cache * @param bcache Body Cache from mutt_bcache_open() * @param id Per-mailbox unique identifier for the message * @retval 0 on success * @retval -1 on failure */ int mutt_bcache_del(struct BodyCache *bcache, const char *id); /** * mutt_bcache_exists - Check if a file exists in the Body Cache * @param bcache Body Cache from mutt_bcache_open() * @param id Per-mailbox unique identifier for the message * @retval 0 on success * @retval -1 on failure */ int mutt_bcache_exists(struct BodyCache *bcache, const char *id); /** * mutt_bcache_list - Find matching entries in the Body Cache * @param bcache Body Cache from mutt_bcache_open() * @param want_id Callback function called for each match * @param data Data to pass to the callback function * @retval -1 on failure * @retval >=0 count of matching items * * This more or less "examines" the cache and calls a function with * each id it finds if given. * * The optional callback function gets the id of a message, the very same * body cache handle mutt_bcache_list() is called with (to, perhaps, * perform further operations on the bcache), and a data cookie which is * just passed as-is. If the return value of the callback is non-zero, the * listing is aborted and continued otherwise. The callback is optional * so that this function can be used to count the items in the cache * (see below for return value). */ int mutt_bcache_list(struct BodyCache *bcache, int (*want_id)(const char *id, struct BodyCache *bcache, void *data), void *data); #endif /* _MUTT_BCACHE_H */ neomutt-neomutt-20171215/body.c000066400000000000000000000071421321473123000162430ustar00rootroot00000000000000/** * @file * Representation of the body of an email * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ #include "config.h" #include #include #include #include "mutt/debug.h" #include "mutt/memory.h" #include "mutt/string2.h" #include "body.h" #include "header.h" #include "mime.h" #include "parameter.h" #include "protos.h" struct Body *mutt_new_body(void) { struct Body *p = mutt_mem_calloc(1, sizeof(struct Body)); p->disposition = DISPATTACH; p->use_disp = true; return p; } /** * mutt_copy_body - create a send-mode duplicate from a receive-mode body */ int mutt_copy_body(FILE *fp, struct Body **tgt, struct Body *src) { if (!tgt || !src) return -1; char tmp[_POSIX_PATH_MAX]; struct Body *b = NULL; struct Parameter *par = NULL, **ppar = NULL; bool use_disp; if (src->filename) { use_disp = true; mutt_str_strfcpy(tmp, src->filename, sizeof(tmp)); } else { use_disp = false; tmp[0] = '\0'; } mutt_adv_mktemp(tmp, sizeof(tmp)); if (mutt_save_attachment(fp, src, tmp, 0, NULL) == -1) return -1; *tgt = mutt_new_body(); b = *tgt; memcpy(b, src, sizeof(struct Body)); b->parts = NULL; b->next = NULL; b->filename = mutt_str_strdup(tmp); b->use_disp = use_disp; b->unlink = true; if (mutt_is_text_part(b)) b->noconv = true; b->xtype = mutt_str_strdup(b->xtype); b->subtype = mutt_str_strdup(b->subtype); b->form_name = mutt_str_strdup(b->form_name); b->d_filename = mutt_str_strdup(b->d_filename); /* mutt_adv_mktemp() will mangle the filename in tmp, * so preserve it in d_filename */ if (!b->d_filename && use_disp) b->d_filename = mutt_str_strdup(src->filename); b->description = mutt_str_strdup(b->description); /* * we don't seem to need the Header structure currently. * XXX - this may change in the future */ if (b->hdr) b->hdr = NULL; /* copy parameters */ for (par = b->parameter, ppar = &b->parameter; par; ppar = &(*ppar)->next, par = par->next) { *ppar = mutt_param_new(); (*ppar)->attribute = mutt_str_strdup(par->attribute); (*ppar)->value = mutt_str_strdup(par->value); } mutt_stamp_attachment(b); return 0; } void mutt_free_body(struct Body **p) { struct Body *a = *p, *b = NULL; while (a) { b = a; a = a->next; if (b->parameter) mutt_param_free(&b->parameter); if (b->filename) { if (b->unlink) unlink(b->filename); mutt_debug(1, "%sunlinking %s.\n", b->unlink ? "" : "not ", b->filename); } FREE(&b->filename); FREE(&b->d_filename); FREE(&b->charset); FREE(&b->content); FREE(&b->xtype); FREE(&b->subtype); FREE(&b->description); FREE(&b->form_name); if (b->hdr) { /* Don't free twice (b->hdr->content = b->parts) */ b->hdr->content = NULL; mutt_free_header(&b->hdr); } if (b->parts) mutt_free_body(&b->parts); FREE(&b); } *p = 0; } neomutt-neomutt-20171215/body.h000066400000000000000000000113231321473123000162440ustar00rootroot00000000000000/** * @file * Representation of the body of an email * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ #ifndef _MUTT_BODY_H #define _MUTT_BODY_H #include #include #include /** * struct Body - The body of an email */ struct Body { char *xtype; /**< content-type if x-unknown */ char *subtype; /**< content-type subtype */ struct Parameter *parameter; /**< parameters of the content-type */ char *description; /**< content-description */ char *form_name; /**< Content-Disposition form-data name param */ long hdr_offset; /**< offset in stream where the headers begin. * this info is used when invoking metamail, * where we need to send the headers of the * attachment */ LOFF_T offset; /**< offset where the actual data begins */ LOFF_T length; /**< length (in bytes) of attachment */ char *filename; /**< when sending a message, this is the file * to which this structure refers */ char *d_filename; /**< filename to be used for the * content-disposition header. * If NULL, filename is used * instead. */ char *charset; /**< charset of attached file */ struct Content *content; /**< structure used to store detailed info about * the content of the attachment. this is used * to determine what content-transfer-encoding * is required when sending mail. */ struct Body *next; /**< next attachment in the list */ struct Body *parts; /**< parts of a multipart or message/rfc822 */ struct Header *hdr; /**< header information for message/rfc822 */ struct AttachPtr *aptr; /**< Menu information, used in recvattach.c */ signed short attach_count; time_t stamp; /**< time stamp of last encoding update. */ unsigned int type : 4; /**< content-type primary type */ unsigned int encoding : 3; /**< content-transfer-encoding */ unsigned int disposition : 2; /**< content-disposition */ bool use_disp : 1; /**< Content-Disposition uses filename= ? */ bool unlink : 1; /**< flag to indicate the file named by * "filename" should be unlink()ed before * free()ing this structure */ bool tagged : 1; bool deleted : 1; /**< attachment marked for deletion */ bool noconv : 1; /**< don't do character set conversion */ bool force_charset : 1; /**< send mode: don't adjust the character * set when in send-mode. */ bool is_signed_data : 1; /**< A lot of MUAs don't indicate S/MIME * signed-data correctly, e.g. they use foo.p7m * even for the name of signed data. This flag * is used to keep track of the actual message * type. It gets set during the verification * (which is done if the encryption try failed) * and check by the function to figure the type * of the message. */ bool goodsig : 1; /**< good cryptographic signature */ bool warnsig : 1; /**< maybe good signature */ bool badsig : 1; /**< bad cryptographic signature (needed to * check encrypted s/mime-signatures) */ bool collapsed : 1; /**< used by recvattach */ bool attach_qualifies : 1; }; struct Body *mutt_new_body(void); int mutt_copy_body(FILE *fp, struct Body **tgt, struct Body *src); void mutt_free_body(struct Body **p); #endif /* _MUTT_BODY_H */ neomutt-neomutt-20171215/browser.c000066400000000000000000001720311321473123000167710ustar00rootroot00000000000000/** * @file * GUI component for displaying/selecting items from a list * * @authors * Copyright (C) 1996-2000,2007,2010,2013 Michael R. Elkins * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #ifdef ENABLE_NLS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "mutt/mutt.h" #include "conn/conn.h" #include "mutt.h" #include "attach.h" #include "body.h" #include "browser.h" #include "buffy.h" #include "context.h" #include "format_flags.h" #include "globals.h" #include "keymap.h" #include "mailbox.h" #include "mbyte.h" #include "mutt_account.h" #include "mutt_charset.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mutt_regex.h" #include "mx.h" #include "opcodes.h" #include "options.h" #include "protos.h" #include "sort.h" #include "url.h" #ifdef USE_IMAP #include "imap/imap.h" #endif #ifdef USE_NNTP #include "nntp.h" #endif #ifdef USE_NOTMUCH #include "mutt_notmuch.h" #endif static const struct Mapping FolderHelp[] = { { N_("Exit"), OP_EXIT }, { N_("Chdir"), OP_CHANGE_DIRECTORY }, { N_("Goto"), OP_BROWSER_GOTO_FOLDER }, { N_("Mask"), OP_ENTER_MASK }, { N_("Help"), OP_HELP }, { NULL, 0 }, }; #ifdef USE_NNTP static struct Mapping 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, 0 }, }; #endif /** * struct Folder - A folder/dir in the browser */ struct Folder { struct FolderFile *ff; int num; }; static char OldLastDir[_POSIX_PATH_MAX] = ""; static char LastDir[_POSIX_PATH_MAX] = ""; /** * destroy_state - Free the BrowserState * * Frees up the memory allocated for the local-global variables. */ static void destroy_state(struct BrowserState *state) { for (size_t c = 0; c < state->entrylen; c++) { FREE(&((state->entry)[c].name)); FREE(&((state->entry)[c].desc)); } #ifdef USE_IMAP FREE(&state->folder); #endif FREE(&state->entry); } static int browser_compare_subject(const void *a, const void *b) { struct FolderFile *pa = (struct FolderFile *) a; struct FolderFile *pb = (struct FolderFile *) b; /* inbox should be sorted ahead of its siblings */ int r = mutt_inbox_cmp(pa->name, pb->name); if (r == 0) r = mutt_str_strcoll(pa->name, pb->name); return ((SortBrowser & SORT_REVERSE) ? -r : r); } static int browser_compare_desc(const void *a, const void *b) { struct FolderFile *pa = (struct FolderFile *) a; struct FolderFile *pb = (struct FolderFile *) b; int r = mutt_str_strcoll(pa->desc, pb->desc); return ((SortBrowser & SORT_REVERSE) ? -r : r); } static int browser_compare_date(const void *a, const void *b) { struct FolderFile *pa = (struct FolderFile *) a; struct FolderFile *pb = (struct FolderFile *) b; int r = pa->mtime - pb->mtime; return ((SortBrowser & SORT_REVERSE) ? -r : r); } static int browser_compare_size(const void *a, const void *b) { struct FolderFile *pa = (struct FolderFile *) a; struct FolderFile *pb = (struct FolderFile *) b; int r = pa->size - pb->size; return ((SortBrowser & SORT_REVERSE) ? -r : r); } static int browser_compare_count(const void *a, const void *b) { struct FolderFile *pa = (struct FolderFile *) a; struct FolderFile *pb = (struct FolderFile *) b; int r = 0; if (pa->has_buffy && pb->has_buffy) r = pa->msg_count - pb->msg_count; else if (pa->has_buffy) r = -1; else r = 1; return ((SortBrowser & SORT_REVERSE) ? -r : r); } static int browser_compare_count_new(const void *a, const void *b) { struct FolderFile *pa = (struct FolderFile *) a; struct FolderFile *pb = (struct FolderFile *) b; int r = 0; if (pa->has_buffy && pb->has_buffy) r = pa->msg_unread - pb->msg_unread; else if (pa->has_buffy) r = -1; else r = 1; return ((SortBrowser & SORT_REVERSE) ? -r : r); } /** * browser_compare - Sort the items in the browser * * Wild compare function that calls the others. It's useful because it provides * a way to tell "../" is always on the top of the list, independently of the * sort method. */ static int browser_compare(const void *a, const void *b) { struct FolderFile *pa = (struct FolderFile *) a; struct FolderFile *pb = (struct FolderFile *) b; if ((mutt_str_strcoll(pa->desc, "../") == 0) || (mutt_str_strcoll(pa->desc, "..") == 0)) return -1; if ((mutt_str_strcoll(pb->desc, "../") == 0) || (mutt_str_strcoll(pb->desc, "..") == 0)) return 1; switch (SortBrowser & SORT_MASK) { case SORT_DATE: return browser_compare_date(a, b); case SORT_SIZE: return browser_compare_size(a, b); case SORT_DESC: return browser_compare_desc(a, b); case SORT_COUNT: return browser_compare_count(a, b); case SORT_UNREAD: return browser_compare_count_new(a, b); case SORT_SUBJECT: default: return browser_compare_subject(a, b); } } /** * browser_sort - Sort the entries in the browser * * Call to qsort using browser_compare function. * Some specific sort methods are not used via NNTP. */ static void browser_sort(struct BrowserState *state) { switch (SortBrowser & SORT_MASK) { /* Also called "I don't care"-sort-method. */ case SORT_ORDER: return; #ifdef USE_NNTP case SORT_SIZE: case SORT_DATE: if (option(OPT_NEWS)) return; #endif default: break; } qsort(state->entry, state->entrylen, sizeof(struct FolderFile), browser_compare); } static int link_is_dir(const char *folder, const char *path) { struct stat st; char fullpath[_POSIX_PATH_MAX]; mutt_file_concat_path(fullpath, folder, path, sizeof(fullpath)); if (stat(fullpath, &st) == 0) return (S_ISDIR(st.st_mode)); else return 0; } /** * folder_format_str - Format a string for the folder browser * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] col Starting column * @param[in] cols Number of screen columns * @param[in] op printf-like operator, e.g. 't' * @param[in] src printf-like format string * @param[in] prec Field precision, e.g. "-3.4" * @param[in] if_str If condition is met, display this string * @param[in] else_str Otherwise, display this string * @param[in] data Pointer to the mailbox Context * @param[in] flags Format flags * @retval src (unchanged) * * folder_format_str() is a callback function for mutt_expando_format(). * * | Expando | Description * |:--------|:-------------------------------------------------------- * | \%C | Current file number * | \%d | Date/time folder was last modified * | \%D | Date/time folder was last modified using $$date_format. * | \%F | File permissions * | \%f | Filename (with suffix '/', '@' or '*') * | \%g | Group name (or numeric gid, if missing) * | \%l | Number of hard links * | \%m | Number of messages in the mailbox * * | \%N | N if mailbox has new mail, blank otherwise * | \%n | Number of unread messages in the mailbox * * | \%s | Size in bytes * | \%t | '*' if the file is tagged, blank otherwise * | \%u | Owner name (or numeric uid, if missing) */ static const char *folder_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, enum FormatFlag flags) { char fn[SHORT_STRING], fmt[SHORT_STRING], permission[11]; char date[SHORT_STRING], *t_fmt = NULL; time_t tnow; struct Folder *folder = (struct Folder *) data; struct passwd *pw = NULL; struct group *gr = NULL; int optional = (flags & MUTT_FORMAT_OPTIONAL); switch (op) { case 'C': snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->num + 1); break; case 'd': case 'D': if (folder->ff->local) { int do_locales = true; if (op == 'D') { t_fmt = NONULL(DateFormat); if (*t_fmt == '!') { t_fmt++; do_locales = false; } } else { tnow = time(NULL); t_fmt = tnow - folder->ff->mtime < 31536000 ? "%b %d %H:%M" : "%b %d %Y"; } if (!do_locales) setlocale(LC_TIME, "C"); strftime(date, sizeof(date), t_fmt, localtime(&folder->ff->mtime)); if (!do_locales) setlocale(LC_TIME, ""); mutt_format_s(buf, buflen, prec, date); } else mutt_format_s(buf, buflen, prec, ""); break; case 'f': { char *s = NULL; #ifdef USE_NOTMUCH if (mx_is_notmuch(folder->ff->name)) s = NONULL(folder->ff->desc); else #endif #ifdef USE_IMAP if (folder->ff->imap) s = NONULL(folder->ff->desc); else #endif s = NONULL(folder->ff->name); snprintf(fn, sizeof(fn), "%s%s", s, folder->ff->local ? (S_ISLNK(folder->ff->mode) ? "@" : (S_ISDIR(folder->ff->mode) ? "/" : ((folder->ff->mode & S_IXUSR) != 0 ? "*" : ""))) : ""); mutt_format_s(buf, buflen, prec, fn); break; } case 'F': if (folder->ff->local) { snprintf(permission, sizeof(permission), "%c%c%c%c%c%c%c%c%c%c", S_ISDIR(folder->ff->mode) ? 'd' : (S_ISLNK(folder->ff->mode) ? 'l' : '-'), (folder->ff->mode & S_IRUSR) != 0 ? 'r' : '-', (folder->ff->mode & S_IWUSR) != 0 ? 'w' : '-', (folder->ff->mode & S_ISUID) != 0 ? 's' : (folder->ff->mode & S_IXUSR) != 0 ? 'x' : '-', (folder->ff->mode & S_IRGRP) != 0 ? 'r' : '-', (folder->ff->mode & S_IWGRP) != 0 ? 'w' : '-', (folder->ff->mode & S_ISGID) != 0 ? 's' : (folder->ff->mode & S_IXGRP) != 0 ? 'x' : '-', (folder->ff->mode & S_IROTH) != 0 ? 'r' : '-', (folder->ff->mode & S_IWOTH) != 0 ? 'w' : '-', (folder->ff->mode & S_ISVTX) != 0 ? 't' : (folder->ff->mode & S_IXOTH) != 0 ? 'x' : '-'); mutt_format_s(buf, buflen, prec, permission); } #ifdef USE_IMAP else if (folder->ff->imap) { /* mark folders with subfolders AND mail */ snprintf(permission, sizeof(permission), "IMAP %c", (folder->ff->inferiors && folder->ff->selectable) ? '+' : ' '); mutt_format_s(buf, buflen, prec, permission); } #endif else mutt_format_s(buf, buflen, prec, ""); break; case 'g': if (folder->ff->local) { if ((gr = getgrgid(folder->ff->gid))) mutt_format_s(buf, buflen, prec, gr->gr_name); else { snprintf(fmt, sizeof(fmt), "%%%sld", prec); snprintf(buf, buflen, fmt, folder->ff->gid); } } else mutt_format_s(buf, buflen, prec, ""); break; case 'l': if (folder->ff->local) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->nlink); } else mutt_format_s(buf, buflen, prec, ""); break; case 'm': if (!optional) { if (folder->ff->has_buffy) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->msg_count); } else mutt_format_s(buf, buflen, prec, ""); } else if (!folder->ff->msg_count) optional = 0; break; case 'N': snprintf(fmt, sizeof(fmt), "%%%sc", prec); snprintf(buf, buflen, fmt, folder->ff->new ? 'N' : ' '); break; case 'n': if (!optional) { if (folder->ff->has_buffy) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->msg_unread); } else mutt_format_s(buf, buflen, prec, ""); } else if (!folder->ff->msg_unread) optional = 0; break; case 's': if (folder->ff->local) { mutt_pretty_size(fn, sizeof(fn), folder->ff->size); snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, fn); } else mutt_format_s(buf, buflen, prec, ""); break; case 't': snprintf(fmt, sizeof(fmt), "%%%sc", prec); snprintf(buf, buflen, fmt, folder->ff->tagged ? '*' : ' '); break; case 'u': if (folder->ff->local) { if ((pw = getpwuid(folder->ff->uid))) mutt_format_s(buf, buflen, prec, pw->pw_name); else { snprintf(fmt, sizeof(fmt), "%%%sld", prec); snprintf(buf, buflen, fmt, folder->ff->uid); } } else mutt_format_s(buf, buflen, prec, ""); break; default: snprintf(fmt, sizeof(fmt), "%%%sc", prec); snprintf(buf, buflen, fmt, op); break; } if (optional) mutt_expando_format(buf, buflen, col, cols, if_str, folder_format_str, data, 0); else if (flags & MUTT_FORMAT_OPTIONAL) mutt_expando_format(buf, buflen, col, cols, else_str, folder_format_str, data, 0); return src; } #ifdef USE_NNTP /** * group_index_format_str - Format a string for the newsgroup menu * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] col Starting column * @param[in] cols Number of screen columns * @param[in] op printf-like operator, e.g. 't' * @param[in] src printf-like format string * @param[in] prec Field precision, e.g. "-3.4" * @param[in] if_str If condition is met, display this string * @param[in] else_str Otherwise, display this string * @param[in] data Pointer to the mailbox Context * @param[in] flags Format flags * @retval src (unchanged) * * group_index_format_str() is a callback function for mutt_expando_format(). * * | Expando | Description * |:--------|:-------------------------------------------------------- * | \%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 */ static const char *group_index_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, enum FormatFlag flags) { char fn[SHORT_STRING], fmt[SHORT_STRING]; struct Folder *folder = (struct Folder *) data; switch (op) { case 'C': snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->num + 1); break; case 'd': if (folder->ff->nd->desc) { char *desc = mutt_str_strdup(folder->ff->nd->desc); if (NewsgroupsCharset && *NewsgroupsCharset) mutt_convert_string(&desc, NewsgroupsCharset, Charset, MUTT_ICONV_HOOK_FROM); mutt_filter_unprintable(&desc); snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, desc); FREE(&desc); } else { snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, ""); } break; case 'f': strncpy(fn, folder->ff->name, sizeof(fn) - 1); snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, fn); break; case 'M': snprintf(fmt, sizeof(fmt), "%%%sc", prec); if (folder->ff->nd->deleted) snprintf(buf, buflen, fmt, 'D'); else snprintf(buf, buflen, fmt, folder->ff->nd->allowed ? ' ' : '-'); break; case 'N': snprintf(fmt, sizeof(fmt), "%%%sc", prec); if (folder->ff->nd->subscribed) snprintf(buf, buflen, fmt, ' '); else snprintf(buf, buflen, fmt, folder->ff->new ? 'N' : 'u'); break; case 's': if (flags & MUTT_FORMAT_OPTIONAL) { if (folder->ff->nd->unread != 0) mutt_expando_format(buf, buflen, col, cols, if_str, group_index_format_str, data, flags); else mutt_expando_format(buf, buflen, col, cols, else_str, group_index_format_str, data, flags); } else if (Context && Context->data == folder->ff->nd) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, Context->unread); } else { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->nd->unread); } break; case 'n': if (Context && Context->data == folder->ff->nd) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, Context->new); } else if (option(OPT_MARK_OLD) && folder->ff->nd->last_cached >= folder->ff->nd->first_message && folder->ff->nd->last_cached <= folder->ff->nd->last_message) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->nd->last_message - folder->ff->nd->last_cached); } else { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->nd->unread); } break; } return src; } #endif /* USE_NNTP */ static void add_folder(struct Menu *m, struct BrowserState *state, const char *name, const char *desc, const struct stat *s, struct Buffy *b, void *data) { if (state->entrylen == state->entrymax) { /* need to allocate more space */ mutt_mem_realloc(&state->entry, sizeof(struct FolderFile) * (state->entrymax += 256)); memset(&state->entry[state->entrylen], 0, sizeof(struct FolderFile) * 256); if (m) m->data = state->entry; } if (s) { (state->entry)[state->entrylen].mode = s->st_mode; (state->entry)[state->entrylen].mtime = s->st_mtime; (state->entry)[state->entrylen].size = s->st_size; (state->entry)[state->entrylen].gid = s->st_gid; (state->entry)[state->entrylen].uid = s->st_uid; (state->entry)[state->entrylen].nlink = s->st_nlink; (state->entry)[state->entrylen].local = true; } else (state->entry)[state->entrylen].local = false; if (b) { (state->entry)[state->entrylen].has_buffy = true; (state->entry)[state->entrylen].new = b->new; (state->entry)[state->entrylen].msg_count = b->msg_count; (state->entry)[state->entrylen].msg_unread = b->msg_unread; } (state->entry)[state->entrylen].name = mutt_str_strdup(name); (state->entry)[state->entrylen].desc = mutt_str_strdup(desc ? desc : name); #ifdef USE_IMAP (state->entry)[state->entrylen].imap = false; #endif #ifdef USE_NNTP if (option(OPT_NEWS)) (state->entry)[state->entrylen].nd = (struct NntpData *) data; #endif (state->entrylen)++; } static void init_state(struct BrowserState *state, struct Menu *menu) { state->entrylen = 0; state->entrymax = 256; state->entry = mutt_mem_calloc(state->entrymax, sizeof(struct FolderFile)); #ifdef USE_IMAP state->imap_browse = false; #endif if (menu) menu->data = state->entry; } /** * examine_directory - get list of all files/newsgroups with mask */ static int examine_directory(struct Menu *menu, struct BrowserState *state, char *d, const char *prefix) { #ifdef USE_NNTP if (option(OPT_NEWS)) { struct NntpServer *nserv = CurrentNewsSrv; init_state(state, menu); for (unsigned int i = 0; i < nserv->groups_num; i++) { struct NntpData *nntp_data = nserv->groups_list[i]; if (!nntp_data) continue; if (prefix && *prefix && (strncmp(prefix, nntp_data->group, strlen(prefix)) != 0)) continue; if (!((regexec(Mask.regex, nntp_data->group, 0, NULL, 0) == 0) ^ Mask.not)) continue; add_folder(menu, state, nntp_data->group, NULL, NULL, NULL, nntp_data); } } else #endif /* USE_NNTP */ { struct stat s; DIR *dp = NULL; struct dirent *de = NULL; char buffer[_POSIX_PATH_MAX + SHORT_STRING]; struct Buffy *tmp = NULL; while (stat(d, &s) == -1) { if (errno == ENOENT) { /* The last used directory is deleted, try to use the parent dir. */ char *c = strrchr(d, '/'); if (c && (c > d)) { *c = 0; continue; } } mutt_perror(d); return -1; } if (!S_ISDIR(s.st_mode)) { mutt_error(_("%s is not a directory."), d); return -1; } mutt_buffy_check(false); dp = opendir(d); if (!dp) { mutt_perror(d); return -1; } init_state(state, menu); while ((de = readdir(dp)) != NULL) { if (mutt_str_strcmp(de->d_name, ".") == 0) continue; /* we don't need . */ if (prefix && *prefix && (mutt_str_strncmp(prefix, de->d_name, mutt_str_strlen(prefix)) != 0)) { continue; } if (!((regexec(Mask.regex, de->d_name, 0, NULL, 0) == 0) ^ Mask.not)) continue; mutt_file_concat_path(buffer, d, de->d_name, sizeof(buffer)); if (lstat(buffer, &s) == -1) continue; if ((!S_ISREG(s.st_mode)) && (!S_ISDIR(s.st_mode)) && (!S_ISLNK(s.st_mode))) continue; tmp = Incoming; while (tmp && (mutt_str_strcmp(buffer, tmp->path) != 0)) tmp = tmp->next; if (tmp && Context && (mutt_str_strcmp(tmp->realpath, Context->realpath) == 0)) { tmp->msg_count = Context->msgcount; tmp->msg_unread = Context->unread; } add_folder(menu, state, de->d_name, NULL, &s, tmp, NULL); } closedir(dp); } browser_sort(state); return 0; } #ifdef USE_NOTMUCH static int examine_vfolders(struct Menu *menu, struct BrowserState *state) { struct Buffy *tmp = Incoming; if (!tmp) return -1; mutt_buffy_check(false); init_state(state, menu); do { if (mx_is_notmuch(tmp->path)) { nm_nonctx_get_count(tmp->path, &tmp->msg_count, &tmp->msg_unread); add_folder(menu, state, tmp->path, tmp->desc, NULL, tmp, NULL); continue; } } while ((tmp = tmp->next)); browser_sort(state); return 0; } #endif /** * examine_mailboxes - Get list of mailboxes/subscribed newsgroups */ static int examine_mailboxes(struct Menu *menu, struct BrowserState *state) { struct stat s; char buffer[LONG_STRING]; #ifdef USE_NNTP if (option(OPT_NEWS)) { struct NntpServer *nserv = CurrentNewsSrv; init_state(state, menu); for (unsigned int i = 0; i < nserv->groups_num; i++) { struct NntpData *nntp_data = nserv->groups_list[i]; if (nntp_data && (nntp_data->new || (nntp_data->subscribed && (nntp_data->unread || !option(OPT_SHOW_ONLY_UNREAD))))) add_folder(menu, state, nntp_data->group, NULL, NULL, NULL, nntp_data); } } else #endif { struct Buffy *tmp = Incoming; init_state(state, menu); if (!Incoming) return -1; mutt_buffy_check(false); do { if (Context && (mutt_str_strcmp(tmp->realpath, Context->realpath) == 0)) { tmp->msg_count = Context->msgcount; tmp->msg_unread = Context->unread; } mutt_str_strfcpy(buffer, NONULL(tmp->path), sizeof(buffer)); mutt_pretty_mailbox(buffer, sizeof(buffer)); #ifdef USE_IMAP if (mx_is_imap(tmp->path)) { add_folder(menu, state, buffer, NULL, NULL, tmp, NULL); continue; } #endif #ifdef USE_POP if (mx_is_pop(tmp->path)) { add_folder(menu, state, buffer, NULL, NULL, tmp, NULL); continue; } #endif #ifdef USE_NNTP if (mx_is_nntp(tmp->path)) { add_folder(menu, state, tmp->path, NULL, NULL, tmp, NULL); continue; } #endif if (lstat(tmp->path, &s) == -1) continue; if ((!S_ISREG(s.st_mode)) && (!S_ISDIR(s.st_mode)) && (!S_ISLNK(s.st_mode))) continue; if (mx_is_maildir(tmp->path)) { struct stat st2; char md[LONG_STRING]; snprintf(md, sizeof(md), "%s/new", tmp->path); if (stat(md, &s) < 0) s.st_mtime = 0; snprintf(md, sizeof(md), "%s/cur", tmp->path); if (stat(md, &st2) < 0) st2.st_mtime = 0; if (st2.st_mtime > s.st_mtime) s.st_mtime = st2.st_mtime; } add_folder(menu, state, buffer, NULL, &s, tmp, NULL); } while ((tmp = tmp->next)); } browser_sort(state); return 0; } static int select_file_search(struct Menu *menu, regex_t *re, int n) { #ifdef USE_NNTP if (option(OPT_NEWS)) return (regexec(re, ((struct FolderFile *) menu->data)[n].desc, 0, NULL, 0)); #endif return (regexec(re, ((struct FolderFile *) menu->data)[n].name, 0, NULL, 0)); } #ifdef USE_NOTMUCH static int select_vfolder_search(struct Menu *menu, regex_t *re, int n) { return (regexec(re, ((struct FolderFile *) menu->data)[n].desc, 0, NULL, 0)); } #endif /** * folder_entry - Format a menu item for the folder browser * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] menu Menu containing aliases * @param[in] num Index into the menu */ static void folder_entry(char *buf, size_t buflen, struct Menu *menu, int num) { struct Folder folder; folder.ff = &((struct FolderFile *) menu->data)[num]; folder.num = num; #ifdef USE_NNTP if (option(OPT_NEWS)) mutt_expando_format(buf, buflen, 0, MuttIndexWindow->cols, NONULL(GroupIndexFormat), group_index_format_str, (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR); else #endif mutt_expando_format(buf, buflen, 0, MuttIndexWindow->cols, NONULL(FolderFormat), folder_format_str, (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR); } #ifdef USE_NOTMUCH /** * vfolder_entry - Format a menu item for the virtual folder list * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] menu Menu containing aliases * @param[in] num Index into the menu */ static void vfolder_entry(char *buf, size_t buflen, struct Menu *menu, int num) { struct Folder folder; folder.ff = &((struct FolderFile *) menu->data)[num]; folder.num = num; mutt_expando_format(buf, buflen, 0, MuttIndexWindow->cols, NONULL(VfolderFormat), folder_format_str, (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR); } #endif /** * browser_highlight_default - Decide which browser item should be highlighted * * This function takes a menu and a state and defines the current entry that * should be highlighted. */ static void browser_highlight_default(struct BrowserState *state, struct Menu *menu) { menu->top = 0; /* Reset menu position to 1. * We do not risk overflow as the init_menu function changes * current if it is bigger than state->entrylen. */ if ((mutt_str_strcmp(state->entry[0].desc, "..") == 0) || (mutt_str_strcmp(state->entry[0].desc, "../") == 0)) { /* Skip the first entry, unless there's only one entry. */ menu->current = (menu->max > 1); } else { menu->current = 0; } } static void init_menu(struct BrowserState *state, struct Menu *menu, char *title, size_t titlelen, bool buffy) { char path[_POSIX_PATH_MAX]; menu->max = state->entrylen; if (menu->current >= menu->max) menu->current = menu->max - 1; if (menu->current < 0) menu->current = 0; if (menu->top > menu->current) menu->top = 0; menu->tagged = 0; #ifdef USE_NNTP if (option(OPT_NEWS)) { if (buffy) snprintf(title, titlelen, _("Subscribed newsgroups")); else snprintf(title, titlelen, _("Newsgroups on server [%s]"), CurrentNewsSrv->conn->account.host); } else #endif { if (buffy) { menu->is_mailbox_list = 1; snprintf(title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check(false)); } else { menu->is_mailbox_list = 0; mutt_str_strfcpy(path, LastDir, sizeof(path)); mutt_pretty_mailbox(path, sizeof(path)); snprintf(title, titlelen, _("Directory [%s], File mask: %s"), path, NONULL(Mask.pattern)); } } /* Browser tracking feature. * The goal is to highlight the good directory if LastDir is the parent dir * of OldLastDir (this occurs mostly when one hit "../"). It should also work * properly when the user is in examine_mailboxes-mode. */ int ldlen = mutt_str_strlen(LastDir); if ((ldlen > 0) && (mutt_str_strncmp(LastDir, OldLastDir, ldlen) == 0)) { char TargetDir[_POSIX_PATH_MAX] = ""; #ifdef USE_IMAP /* Use mx_is_imap to check what kind of dir is OldLastDir. */ if (mx_is_imap(OldLastDir)) { mutt_str_strfcpy(TargetDir, OldLastDir, sizeof(TargetDir)); imap_clean_path(TargetDir, sizeof(TargetDir)); } else #endif mutt_str_strfcpy(TargetDir, strrchr(OldLastDir, '/') + 1, sizeof(TargetDir)); /* If we get here, it means that LastDir is the parent directory of * OldLastDir. I.e., we're returning from a subdirectory, and we want * to position the cursor on the directory we're returning from. */ bool matched = false; for (unsigned int i = 0; i < state->entrylen; i++) { if (mutt_str_strcmp(state->entry[i].name, TargetDir) == 0) { menu->current = i; matched = true; break; } } if (!matched) browser_highlight_default(state, menu); } else browser_highlight_default(state, menu); menu->redraw = REDRAW_FULL; } static int file_tag(struct Menu *menu, int n, int m) { struct FolderFile *ff = &(((struct FolderFile *) menu->data)[n]); if (S_ISDIR(ff->mode) || (S_ISLNK(ff->mode) && link_is_dir(LastDir, ff->name))) { mutt_error(_("Can't attach a directory!")); return 0; } bool ot = ff->tagged; ff->tagged = (m >= 0 ? m : !ff->tagged); return ff->tagged - ot; } /** * mutt_browser_select_dir - Remember the last directory selected * * This function helps the browser to know which directory has been selected. * It should be called anywhere a confirm hit is done to open a new * directory/file which is a maildir/mbox. * * We could check if the sort method is appropriate with this feature. */ void mutt_browser_select_dir(char *f) { mutt_str_strfcpy(OldLastDir, f, sizeof(OldLastDir)); /* Method that will fetch the parent path depending on the type of the path. */ mutt_get_parent_path(LastDir, OldLastDir, sizeof(LastDir)); } void mutt_select_file(char *f, size_t flen, int flags, char ***files, int *numfiles) { char buf[_POSIX_PATH_MAX]; char prefix[_POSIX_PATH_MAX] = ""; char helpstr[LONG_STRING]; char title[STRING]; struct BrowserState state; struct Menu *menu = NULL; struct stat st; int i, kill_prefix = 0; int multiple = (flags & MUTT_SEL_MULTI) ? 1 : 0; int folder = (flags & MUTT_SEL_FOLDER) ? 1 : 0; int buffy = (flags & MUTT_SEL_BUFFY) ? 1 : 0; /* Keeps in memory the directory we were in when hitting '=' * to go directly to $folder (Folder) */ char GotoSwapper[_POSIX_PATH_MAX] = ""; buffy = buffy && folder; memset(&state, 0, sizeof(struct BrowserState)); #ifdef USE_NNTP if (option(OPT_NEWS)) { if (*f) mutt_str_strfcpy(prefix, f, sizeof(prefix)); else { struct NntpServer *nserv = CurrentNewsSrv; /* default state for news reader mode is browse subscribed newsgroups */ buffy = 0; for (unsigned int j = 0; j < nserv->groups_num; j++) { struct NntpData *nntp_data = nserv->groups_list[j]; if (nntp_data && nntp_data->subscribed) { buffy = 1; break; } } } } else #endif if (*f) { mutt_expand_path(f, flen); #ifdef USE_IMAP if (mx_is_imap(f)) { init_state(&state, NULL); state.imap_browse = true; if (!imap_browse(f, &state)) { mutt_str_strfcpy(LastDir, state.folder, sizeof(LastDir)); browser_sort(&state); } } else { #endif for (i = mutt_str_strlen(f) - 1; i > 0 && f[i] != '/'; i--) ; if (i > 0) { if (f[0] == '/') { if (i > sizeof(LastDir) - 1) i = sizeof(LastDir) - 1; strncpy(LastDir, f, i); LastDir[i] = 0; } else { getcwd(LastDir, sizeof(LastDir)); mutt_str_strcat(LastDir, sizeof(LastDir), "/"); mutt_str_strncat(LastDir, sizeof(LastDir), f, i); } } else { if (f[0] == '/') strcpy(LastDir, "/"); else getcwd(LastDir, sizeof(LastDir)); } if (i <= 0 && f[0] != '/') mutt_str_strfcpy(prefix, f, sizeof(prefix)); else mutt_str_strfcpy(prefix, f + i + 1, sizeof(prefix)); kill_prefix = 1; #ifdef USE_IMAP } #endif } #ifdef USE_NOTMUCH else if (!(flags & MUTT_SEL_VFOLDER)) #else else #endif { if (!folder) getcwd(LastDir, sizeof(LastDir)); else { /* Whether we use the tracking feature of the browser depends * on which sort method we chose to use. This variable is defined * only to help readability of the code. */ short browser_track; switch (SortBrowser & SORT_MASK) { case SORT_DESC: case SORT_SUBJECT: case SORT_ORDER: browser_track = 1; break; default: browser_track = 0; break; } /* We use mutt_browser_select_dir to initialize the two * variables (LastDir, OldLastDir) at the appropriate * values. * * We do it only when LastDir is not set (first pass there) * or when CurrentFolder and OldLastDir are not the same. * This code is executed only when we list files, not when * we press up/down keys to navigate in a displayed list. * * We only do this when CurrentFolder has been set (ie, not * when listing folders on startup with "neomutt -y"). * * This tracker is only used when browser_track is true, * meaning only with sort methods SUBJECT/DESC for now. */ if (CurrentFolder) { if (!LastDir[0]) { /* If browsing in "local"-mode, than we chose to define LastDir to * MailDir */ switch (mx_get_magic(CurrentFolder)) { case MUTT_MBOX: case MUTT_MMDF: case MUTT_MH: case MUTT_MAILDIR: case MUTT_IMAP: if (Folder) mutt_str_strfcpy(LastDir, NONULL(Folder), sizeof(LastDir)); else if (SpoolFile) mutt_browser_select_dir(SpoolFile); break; default: mutt_browser_select_dir(CurrentFolder); break; } } else if (mutt_str_strcmp(CurrentFolder, OldLastDir) != 0) { mutt_browser_select_dir(CurrentFolder); } } /* When browser tracking feature is disabled, shoot a 0 * on first char of OldLastDir to make it useless. */ if (!browser_track) OldLastDir[0] = '\0'; } #ifdef USE_IMAP if (!buffy && mx_is_imap(LastDir)) { init_state(&state, NULL); state.imap_browse = true; imap_browse(LastDir, &state); browser_sort(&state); } else #endif { i = mutt_str_strlen(LastDir); while (i && LastDir[--i] == '/') LastDir[i] = '\0'; if (!LastDir[0]) getcwd(LastDir, sizeof(LastDir)); } } *f = 0; #ifdef USE_NOTMUCH if (flags & MUTT_SEL_VFOLDER) { if (examine_vfolders(NULL, &state) == -1) goto bail; } else #endif if (buffy) { examine_mailboxes(NULL, &state); } else #ifdef USE_IMAP if (!state.imap_browse) #endif { if (examine_directory(NULL, &state, LastDir, prefix) == -1) goto bail; } menu = mutt_new_menu(MENU_FOLDER); menu->make_entry = folder_entry; menu->search = select_file_search; menu->title = title; menu->data = state.entry; if (multiple) menu->tag = file_tag; #ifdef USE_NOTMUCH if (flags & MUTT_SEL_VFOLDER) { menu->make_entry = vfolder_entry; menu->search = select_vfolder_search; } else #endif menu->make_entry = folder_entry; menu->help = mutt_compile_help(helpstr, sizeof(helpstr), MENU_FOLDER, #ifdef USE_NNTP option(OPT_NEWS) ? FolderNewsHelp : #endif FolderHelp); mutt_push_current_menu(menu); init_menu(&state, menu, title, sizeof(title), buffy); while (true) { switch (i = mutt_menu_loop(menu)) { case OP_GENERIC_SELECT_ENTRY: if (!state.entrylen) { mutt_error(_("No files match the file mask")); break; } if (S_ISDIR(state.entry[menu->current].mode) || (S_ISLNK(state.entry[menu->current].mode) && link_is_dir(LastDir, state.entry[menu->current].name)) #ifdef USE_IMAP || state.entry[menu->current].inferiors #endif ) { /* make sure this isn't a MH or maildir mailbox */ if (buffy) { mutt_str_strfcpy(buf, state.entry[menu->current].name, sizeof(buf)); mutt_expand_path(buf, sizeof(buf)); } #ifdef USE_IMAP else if (state.imap_browse) { mutt_str_strfcpy(buf, state.entry[menu->current].name, sizeof(buf)); } #endif else mutt_file_concat_path(buf, LastDir, state.entry[menu->current].name, sizeof(buf)); if ((mx_get_magic(buf) <= 0) #ifdef USE_IMAP || state.entry[menu->current].inferiors #endif ) { /* save the old directory */ mutt_str_strfcpy(OldLastDir, LastDir, sizeof(OldLastDir)); if (mutt_str_strcmp(state.entry[menu->current].name, "..") == 0) { if (mutt_str_strcmp("..", LastDir + mutt_str_strlen(LastDir) - 2) == 0) strcat(LastDir, "/.."); else { char *p = strrchr(LastDir + 1, '/'); if (p) *p = 0; else { if (LastDir[0] == '/') LastDir[1] = 0; else strcat(LastDir, "/.."); } } } else if (buffy) { mutt_str_strfcpy(LastDir, state.entry[menu->current].name, sizeof(LastDir)); mutt_expand_path(LastDir, sizeof(LastDir)); } #ifdef USE_IMAP else if (state.imap_browse) { int n; struct Url url; mutt_str_strfcpy(LastDir, state.entry[menu->current].name, sizeof(LastDir)); /* tack on delimiter here */ n = strlen(LastDir) + 1; /* special case "" needs no delimiter */ url_parse(&url, state.entry[menu->current].name); if (url.path && (state.entry[menu->current].delim != '\0') && (n < sizeof(LastDir))) { LastDir[n] = '\0'; LastDir[n - 1] = state.entry[menu->current].delim; } url_free(&url); } #endif else { char tmp[_POSIX_PATH_MAX]; mutt_file_concat_path(tmp, LastDir, state.entry[menu->current].name, sizeof(tmp)); mutt_str_strfcpy(LastDir, tmp, sizeof(LastDir)); } destroy_state(&state); if (kill_prefix) { prefix[0] = 0; kill_prefix = 0; } buffy = 0; #ifdef USE_IMAP if (state.imap_browse) { init_state(&state, NULL); state.imap_browse = true; imap_browse(LastDir, &state); browser_sort(&state); menu->data = state.entry; } else #endif if (examine_directory(menu, &state, LastDir, prefix) == -1) { /* try to restore the old values */ mutt_str_strfcpy(LastDir, OldLastDir, sizeof(LastDir)); if (examine_directory(menu, &state, LastDir, prefix) == -1) { mutt_str_strfcpy(LastDir, NONULL(HomeDir), sizeof(LastDir)); goto bail; } } browser_highlight_default(&state, menu); init_menu(&state, menu, title, sizeof(title), buffy); if (GotoSwapper[0]) GotoSwapper[0] = '\0'; break; } } if (buffy || option(OPT_NEWS)) /* USE_NNTP */ { mutt_str_strfcpy(f, state.entry[menu->current].name, flen); mutt_expand_path(f, flen); } #ifdef USE_IMAP else if (state.imap_browse) mutt_str_strfcpy(f, state.entry[menu->current].name, flen); #endif #ifdef USE_NOTMUCH else if (mx_is_notmuch(state.entry[menu->current].name)) mutt_str_strfcpy(f, state.entry[menu->current].name, flen); #endif else mutt_file_concat_path(f, LastDir, state.entry[menu->current].name, flen); /* fallthrough */ case OP_EXIT: if (multiple) { char **tfiles = NULL; if (menu->tagged) { *numfiles = menu->tagged; tfiles = mutt_mem_calloc(*numfiles, sizeof(char *)); for (int j = 0, k = 0; j < state.entrylen; j++) { struct FolderFile ff = state.entry[j]; char full[_POSIX_PATH_MAX]; if (ff.tagged) { mutt_file_concat_path(full, LastDir, ff.name, sizeof(full)); mutt_expand_path(full, sizeof(full)); tfiles[k++] = mutt_str_strdup(full); } } *files = tfiles; } else if (f[0]) /* no tagged entries. return selected entry */ { *numfiles = 1; tfiles = mutt_mem_calloc(*numfiles, sizeof(char *)); mutt_expand_path(f, flen); tfiles[0] = mutt_str_strdup(f); *files = tfiles; } } destroy_state(&state); goto bail; case OP_BROWSER_TELL: if (state.entrylen) mutt_message("%s", state.entry[menu->current].name); break; #ifdef USE_IMAP case OP_BROWSER_TOGGLE_LSUB: if (option(OPT_IMAP_LIST_SUBSCRIBED)) unset_option(OPT_IMAP_LIST_SUBSCRIBED); else set_option(OPT_IMAP_LIST_SUBSCRIBED); mutt_unget_event(0, OP_CHECK_NEW); break; case OP_CREATE_MAILBOX: if (!state.imap_browse) { mutt_error(_("Create is only supported for IMAP mailboxes")); break; } if (!imap_mailbox_create(LastDir)) { /* TODO: find a way to detect if the new folder would appear in * this window, and insert it without starting over. */ destroy_state(&state); init_state(&state, NULL); state.imap_browse = true; imap_browse(LastDir, &state); browser_sort(&state); menu->data = state.entry; browser_highlight_default(&state, menu); init_menu(&state, menu, title, sizeof(title), buffy); } /* else leave error on screen */ break; case OP_RENAME_MAILBOX: if (!state.entry[menu->current].imap) mutt_error(_("Rename is only supported for IMAP mailboxes")); else { int nentry = menu->current; if (imap_mailbox_rename(state.entry[nentry].name) >= 0) { destroy_state(&state); init_state(&state, NULL); state.imap_browse = true; imap_browse(LastDir, &state); browser_sort(&state); menu->data = state.entry; browser_highlight_default(&state, menu); init_menu(&state, menu, title, sizeof(title), buffy); } } break; case OP_DELETE_MAILBOX: if (!state.entry[menu->current].imap) mutt_error(_("Delete is only supported for IMAP mailboxes")); else { char msg[SHORT_STRING]; struct ImapMbox mx; int nentry = menu->current; imap_parse_path(state.entry[nentry].name, &mx); if (!mx.mbox) { mutt_error(_("Cannot delete root folder")); break; } snprintf(msg, sizeof(msg), _("Really delete mailbox \"%s\"?"), mx.mbox); if (mutt_yesorno(msg, MUTT_NO) == MUTT_YES) { if (!imap_delete_mailbox(Context, &mx)) { /* free the mailbox from the browser */ FREE(&((state.entry)[nentry].name)); FREE(&((state.entry)[nentry].desc)); /* and move all other entries up */ if (nentry + 1 < state.entrylen) memmove(state.entry + nentry, state.entry + nentry + 1, sizeof(struct FolderFile) * (state.entrylen - (nentry + 1))); memset(&state.entry[state.entrylen - 1], 0, sizeof(struct FolderFile)); state.entrylen--; mutt_message(_("Mailbox deleted.")); init_menu(&state, menu, title, sizeof(title), buffy); } else mutt_error(_("Mailbox deletion failed.")); } else mutt_message(_("Mailbox not deleted.")); FREE(&mx.mbox); } break; #endif case OP_CHANGE_DIRECTORY: #ifdef USE_NNTP if (option(OPT_NEWS)) break; #endif mutt_str_strfcpy(buf, LastDir, sizeof(buf)); #ifdef USE_IMAP if (!state.imap_browse) #endif { /* add '/' at the end of the directory name if not already there */ size_t len = mutt_str_strlen(buf); if ((len > 0) && (buf[len - 1] != '/') && (sizeof(buf) > (len + 1))) { buf[len] = '/'; buf[len + 1] = '\0'; } } if (mutt_get_field(_("Chdir to: "), buf, sizeof(buf), MUTT_FILE) == 0 && buf[0]) { buffy = 0; mutt_expand_path(buf, sizeof(buf)); #ifdef USE_IMAP if (mx_is_imap(buf)) { mutt_str_strfcpy(LastDir, buf, sizeof(LastDir)); destroy_state(&state); init_state(&state, NULL); state.imap_browse = true; imap_browse(LastDir, &state); browser_sort(&state); menu->data = state.entry; browser_highlight_default(&state, menu); init_menu(&state, menu, title, sizeof(title), buffy); } else #endif { if (*buf != '/') { /* in case dir is relative, make it relative to LastDir, * not current working dir */ char tmp[_POSIX_PATH_MAX]; mutt_file_concat_path(tmp, LastDir, buf, sizeof(tmp)); mutt_str_strfcpy(buf, tmp, sizeof(buf)); } if (stat(buf, &st) == 0) { if (S_ISDIR(st.st_mode)) { destroy_state(&state); if (examine_directory(menu, &state, buf, prefix) == 0) mutt_str_strfcpy(LastDir, buf, sizeof(LastDir)); else { mutt_error(_("Error scanning directory.")); if (examine_directory(menu, &state, LastDir, prefix) == -1) { goto bail; } } browser_highlight_default(&state, menu); init_menu(&state, menu, title, sizeof(title), buffy); } else mutt_error(_("%s is not a directory."), buf); } else mutt_perror(buf); } } break; case OP_ENTER_MASK: mutt_str_strfcpy(buf, NONULL(Mask.pattern), sizeof(buf)); if (mutt_get_field(_("File Mask: "), buf, sizeof(buf), 0) == 0) { regex_t *rx = mutt_mem_malloc(sizeof(regex_t)); char *s = buf; int not = 0, err; buffy = 0; /* assume that the user wants to see everything */ if (!buf[0]) mutt_str_strfcpy(buf, ".", sizeof(buf)); SKIPWS(s); if (*s == '!') { s++; SKIPWS(s); not = 1; } err = REGCOMP(rx, s, REG_NOSUB); if (err != 0) { regerror(err, rx, buf, sizeof(buf)); FREE(&rx); mutt_error("%s", buf); } else { mutt_str_replace(&Mask.pattern, buf); regfree(Mask.regex); FREE(&Mask.regex); Mask.regex = rx; Mask.not = not; destroy_state(&state); #ifdef USE_IMAP if (state.imap_browse) { init_state(&state, NULL); state.imap_browse = true; imap_browse(LastDir, &state); browser_sort(&state); menu->data = state.entry; init_menu(&state, menu, title, sizeof(title), buffy); } else #endif if (examine_directory(menu, &state, LastDir, NULL) == 0) init_menu(&state, menu, title, sizeof(title), buffy); else { mutt_error(_("Error scanning directory.")); goto bail; } kill_prefix = 0; if (!state.entrylen) { mutt_error(_("No files match the file mask")); break; } } } break; case OP_SORT: case OP_SORT_REVERSE: { int resort = 1; int reverse = (i == OP_SORT_REVERSE); switch (mutt_multi_choice( (reverse) ? /* L10N: The highlighted letters must match the "Sort" options */ _("Reverse sort by (d)ate, (a)lpha, si(z)e, d(e)scription, " "(c)ount, ne(w) count, or do(n)'t sort? ") : /* L10N: The highlighted letters must match the "Reverse Sort" options */ _("Sort by (d)ate, (a)lpha, si(z)e, d(e)scription, (c)ount, " "ne(w) count, or do(n)'t sort? "), /* L10N: These must match the highlighted letters from "Sort" and "Reverse Sort" */ _("dazecwn"))) { case -1: /* abort */ resort = 0; break; case 1: /* (d)ate */ SortBrowser = SORT_DATE; break; case 2: /* (a)lpha */ SortBrowser = SORT_SUBJECT; break; case 3: /* si(z)e */ SortBrowser = SORT_SIZE; break; case 4: /* d(e)scription */ SortBrowser = SORT_DESC; break; case 5: /* (c)ount */ SortBrowser = SORT_COUNT; break; case 6: /* ne(w) count */ SortBrowser = SORT_UNREAD; break; case 7: /* do(n)'t sort */ SortBrowser = SORT_ORDER; resort = 0; break; } if (resort) { SortBrowser |= reverse ? SORT_REVERSE : 0; browser_sort(&state); browser_highlight_default(&state, menu); menu->redraw = REDRAW_FULL; } break; } case OP_TOGGLE_MAILBOXES: case OP_BROWSER_GOTO_FOLDER: case OP_CHECK_NEW: if (i == OP_TOGGLE_MAILBOXES) buffy = 1 - buffy; if (i == OP_BROWSER_GOTO_FOLDER) { /* When in mailboxes mode, disables this feature */ if (Folder) { mutt_debug(5, "= hit! Folder: %s, LastDir: %s\n", Folder, LastDir); if (!GotoSwapper[0]) { if (mutt_str_strcmp(LastDir, Folder) != 0) { /* Stores into GotoSwapper LastDir, and swaps to Folder */ mutt_str_strfcpy(GotoSwapper, LastDir, sizeof(GotoSwapper)); mutt_str_strfcpy(OldLastDir, LastDir, sizeof(OldLastDir)); mutt_str_strfcpy(LastDir, Folder, sizeof(LastDir)); } } else { mutt_str_strfcpy(OldLastDir, LastDir, sizeof(OldLastDir)); mutt_str_strfcpy(LastDir, GotoSwapper, sizeof(LastDir)); GotoSwapper[0] = '\0'; } } } destroy_state(&state); prefix[0] = 0; kill_prefix = 0; if (buffy) { examine_mailboxes(menu, &state); } #ifdef USE_IMAP else if (mx_is_imap(LastDir)) { init_state(&state, NULL); state.imap_browse = true; imap_browse(LastDir, &state); browser_sort(&state); menu->data = state.entry; } #endif else if (examine_directory(menu, &state, LastDir, prefix) == -1) goto bail; init_menu(&state, menu, title, sizeof(title), buffy); break; case OP_BUFFY_LIST: mutt_buffy_list(); break; case OP_BROWSER_NEW_FILE: snprintf(buf, sizeof(buf), "%s/", LastDir); if (mutt_get_field(_("New file name: "), buf, sizeof(buf), MUTT_FILE) == 0) { mutt_str_strfcpy(f, buf, flen); destroy_state(&state); goto bail; } break; case OP_BROWSER_VIEW_FILE: if (!state.entrylen) { mutt_error(_("No files match the file mask")); break; } #ifdef USE_IMAP if (state.entry[menu->current].selectable) { mutt_str_strfcpy(f, state.entry[menu->current].name, flen); destroy_state(&state); goto bail; } else #endif if (S_ISDIR(state.entry[menu->current].mode) || (S_ISLNK(state.entry[menu->current].mode) && link_is_dir(LastDir, state.entry[menu->current].name))) { mutt_error(_("Can't view a directory")); break; } else { struct Body *b = NULL; char buf2[_POSIX_PATH_MAX]; mutt_file_concat_path(buf2, LastDir, state.entry[menu->current].name, sizeof(buf2)); b = mutt_make_file_attach(buf2); if (b) { mutt_view_attachment(NULL, b, MUTT_REGULAR, NULL, NULL); mutt_free_body(&b); menu->redraw = REDRAW_FULL; } else mutt_error(_("Error trying to view file")); } break; #ifdef USE_NNTP case OP_CATCHUP: case OP_UNCATCHUP: if (option(OPT_NEWS)) { struct FolderFile *ff = &state.entry[menu->current]; int rc; struct NntpData *nntp_data = NULL; rc = nntp_newsrc_parse(CurrentNewsSrv); if (rc < 0) break; if (i == OP_CATCHUP) nntp_data = mutt_newsgroup_catchup(CurrentNewsSrv, ff->name); else nntp_data = mutt_newsgroup_uncatchup(CurrentNewsSrv, ff->name); if (nntp_data) { nntp_newsrc_update(CurrentNewsSrv); if (menu->current + 1 < menu->max) menu->current++; menu->redraw = REDRAW_MOTION_RESYNCH; } if (rc) menu->redraw = REDRAW_INDEX; nntp_newsrc_close(CurrentNewsSrv); } break; case OP_LOAD_ACTIVE: if (option(OPT_NEWS)) { struct NntpServer *nserv = CurrentNewsSrv; if (nntp_newsrc_parse(nserv) < 0) break; for (unsigned int j = 0; j < nserv->groups_num; j++) { struct NntpData *nntp_data = nserv->groups_list[j]; if (nntp_data) nntp_data->deleted = true; } nntp_active_fetch(nserv, true); nntp_newsrc_update(nserv); nntp_newsrc_close(nserv); 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(OPT_NEWS)) { struct NntpServer *nserv = CurrentNewsSrv; regex_t rx; memset(&rx, 0, sizeof(rx)); char *s = buf; int rc, j = menu->current; 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]) { break; } err = REGCOMP(&rx, s, REG_NOSUB); if (err != 0) { regerror(err, &rx, buf, sizeof(buf)); regfree(&rx); mutt_error("%s", buf); break; } menu->redraw = REDRAW_FULL; j = 0; } else if (!state.entrylen) { mutt_error(_("No newsgroups match the mask")); break; } rc = nntp_newsrc_parse(nserv); if (rc < 0) break; for (; j < state.entrylen; j++) { struct FolderFile *ff = &state.entry[j]; if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE || regexec(&rx, ff->name, 0, NULL, 0) == 0) { if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN) mutt_newsgroup_subscribe(nserv, ff->name); else mutt_newsgroup_unsubscribe(nserv, ff->name); } 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) { for (unsigned int k = 0; nserv && (k < nserv->groups_num); k++) { struct NntpData *nntp_data = nserv->groups_list[k]; if (nntp_data && nntp_data->group && !nntp_data->subscribed) { if (regexec(&rx, nntp_data->group, 0, NULL, 0) == 0) { mutt_newsgroup_subscribe(nserv, nntp_data->group); add_folder(menu, &state, nntp_data->group, NULL, NULL, NULL, nntp_data); } } } init_menu(&state, menu, title, sizeof(title), buffy); } if (rc > 0) menu->redraw = REDRAW_FULL; nntp_newsrc_update(nserv); nntp_clear_cache(nserv); nntp_newsrc_close(nserv); if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE) regfree(&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 */ } } bail: if (menu) { mutt_pop_current_menu(menu); mutt_menu_destroy(&menu); } if (GotoSwapper[0]) GotoSwapper[0] = '\0'; } neomutt-neomutt-20171215/browser.h000066400000000000000000000035631321473123000170010ustar00rootroot00000000000000/** * @file * GUI component for displaying/selecting items from a list * * @authors * Copyright (C) 1996-2000 Michael R. Elkins * * @copyright * 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, see . */ #ifndef _MUTT_BROWSER_H #define _MUTT_BROWSER_H #include #include #include /** * struct FolderFile - Browser entry representing a folder/dir */ struct FolderFile { mode_t mode; off_t size; time_t mtime; uid_t uid; gid_t gid; nlink_t nlink; char *name; char *desc; bool new; /**< true if mailbox has "new mail" */ int msg_count; /**< total number of messages */ int msg_unread; /**< number of unread messages */ #ifdef USE_IMAP char delim; bool imap : 1; bool selectable : 1; bool inferiors : 1; #endif bool has_buffy : 1; #ifdef USE_NNTP struct NntpData *nd; #endif bool local : 1; /**< folder is on local filesystem */ bool tagged : 1; }; /** * struct BrowserState - State of the file/mailbox browser */ struct BrowserState { struct FolderFile *entry; size_t entrylen; /**< number of real entries */ unsigned int entrymax; /**< max entry */ #ifdef USE_IMAP bool imap_browse; char *folder; bool noselect : 1; bool marked : 1; bool unmarked : 1; #endif }; #endif /* _MUTT_BROWSER_H */ neomutt-neomutt-20171215/buffy.c000066400000000000000000000521501321473123000164200ustar00rootroot00000000000000/** * @file * Representation of a mailbox * * @authors * Copyright (C) 1996-2000,2010,2013 Michael R. Elkins * Copyright (C) 2016 Kevin J. McCarthy * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "buffy.h" #include "context.h" #include "envelope.h" #include "globals.h" #include "header.h" #include "mailbox.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mx.h" #include "options.h" #include "protos.h" #ifdef USE_SIDEBAR #include "sidebar.h" #endif #ifdef USE_IMAP #include "imap/imap.h" #endif #ifdef USE_NOTMUCH #include "mutt_notmuch.h" #endif static time_t BuffyTime = 0; /**< last time we started checking for mail */ static time_t BuffyStatsTime = 0; /**< last time we check performed mail_check_stats */ time_t BuffyDoneTime = 0; /**< last time we knew for sure how much mail there was. */ static short BuffyCount = 0; /**< how many boxes with new mail */ static short BuffyNotify = 0; /**< # of unnotified new boxes */ /** * fseek_last_message - Find the last message in the file * @retval 0 on success * @retval -1 if no message found */ static int fseek_last_message(FILE *f) { LOFF_T pos; char buffer[BUFSIZ + 9]; /* 7 for "\n\nFrom " */ size_t bytes_read; memset(buffer, 0, sizeof(buffer)); fseek(f, 0, SEEK_END); pos = ftello(f); /* Set `bytes_read' to the size of the last, probably partial, buffer; * 0 < `bytes_read' <= `BUFSIZ'. */ bytes_read = pos % BUFSIZ; if (bytes_read == 0) bytes_read = BUFSIZ; /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all * reads will be on block boundaries, which might increase efficiency. */ while ((pos -= bytes_read) >= 0) { /* we save in the buffer at the end the first 7 chars from the last read */ strncpy(buffer + BUFSIZ, buffer, 5 + 2); /* 2 == 2 * mutt_str_strlen(CRLF) */ fseeko(f, pos, SEEK_SET); bytes_read = fread(buffer, sizeof(char), bytes_read, f); if (bytes_read == 0) return -1; /* 'i' is Index into `buffer' for scanning. */ for (int i = bytes_read; i >= 0; i--) { if (mutt_str_strncmp(buffer + i, "\n\nFrom ", mutt_str_strlen("\n\nFrom ")) == 0) { /* found it - go to the beginning of the From */ fseeko(f, pos + i + 2, SEEK_SET); return 0; } } bytes_read = BUFSIZ; } /* here we are at the beginning of the file */ if (mutt_str_strncmp("From ", buffer, 5) == 0) { fseek(f, 0, SEEK_SET); return 0; } return -1; } /** * test_last_status_new - Is the last message new * @retval 1 if the last message is new */ static int test_last_status_new(FILE *f) { struct Header *hdr = NULL; struct Envelope *tmp_envelope = NULL; int result = 0; if (fseek_last_message(f) == -1) return 0; hdr = mutt_new_header(); tmp_envelope = mutt_read_rfc822_header(f, hdr, 0, 0); if (!(hdr->read || hdr->old)) result = 1; mutt_env_free(&tmp_envelope); mutt_free_header(&hdr); return result; } static int test_new_folder(const char *path) { FILE *f = NULL; int rc = 0; int typ; typ = mx_get_magic(path); if (typ != MUTT_MBOX && typ != MUTT_MMDF) return 0; if ((f = fopen(path, "rb"))) { rc = test_last_status_new(f); mutt_file_fclose(&f); } return rc; } static struct Buffy *buffy_new(const char *path) { struct Buffy *buffy = NULL; char rp[PATH_MAX] = ""; char *r = NULL; buffy = mutt_mem_calloc(1, sizeof(struct Buffy)); mutt_str_strfcpy(buffy->path, path, sizeof(buffy->path)); r = realpath(path, rp); mutt_str_strfcpy(buffy->realpath, r ? rp : path, sizeof(buffy->realpath)); buffy->next = NULL; buffy->magic = 0; return buffy; } static void buffy_free(struct Buffy **mailbox) { if (mailbox && *mailbox) FREE(&(*mailbox)->desc); FREE(mailbox); } /** * buffy_maildir_check_dir - Check for new mail / mail counts * @param mailbox Mailbox to check * @param dir_name Path to mailbox * @param check_new if true, check for new mail * @param check_stats if true, count total, new, and flagged messages * @retval 1 if the dir has new mail * * Checks the specified maildir subdir (cur or new) for new mail or mail counts. */ static int buffy_maildir_check_dir(struct Buffy *mailbox, const char *dir_name, bool check_new, bool check_stats) { char path[LONG_STRING]; char msgpath[LONG_STRING]; DIR *dirp = NULL; struct dirent *de = NULL; char *p = NULL; int rc = 0; struct stat sb; snprintf(path, sizeof(path), "%s/%s", mailbox->path, dir_name); /* when $mail_check_recent is set, if the new/ directory hasn't been modified since * the user last exited the mailbox, then we know there is no recent mail. */ if (check_new && option(OPT_MAIL_CHECK_RECENT)) { if (stat(path, &sb) == 0 && sb.st_mtime < mailbox->last_visited) { rc = 0; check_new = false; } } if (!(check_new || check_stats)) return rc; dirp = opendir(path); if (!dirp) { mailbox->magic = 0; return 0; } while ((de = readdir(dirp)) != NULL) { if (*de->d_name == '.') continue; p = strstr(de->d_name, ":2,"); if (p && strchr(p + 3, 'T')) continue; if (check_stats) { mailbox->msg_count++; if (p && strchr(p + 3, 'F')) mailbox->msg_flagged++; } if (!p || !strchr(p + 3, 'S')) { if (check_stats) mailbox->msg_unread++; if (check_new) { if (option(OPT_MAIL_CHECK_RECENT)) { snprintf(msgpath, sizeof(msgpath), "%s/%s", path, de->d_name); /* ensure this message was received since leaving this mailbox */ if (stat(msgpath, &sb) == 0 && (sb.st_ctime <= mailbox->last_visited)) continue; } mailbox->new = true; rc = 1; check_new = false; if (!check_stats) break; } } } closedir(dirp); return rc; } /** * buffy_maildir_check - Check for new mail in a maildir mailbox * @param mailbox Mailbox to check * @param check_stats if true, also count total, new, and flagged messages * @retval 1 if the mailbox has new mail */ static int buffy_maildir_check(struct Buffy *mailbox, bool check_stats) { int rc = 1; bool check_new = true; if (check_stats) { mailbox->msg_count = 0; mailbox->msg_unread = 0; mailbox->msg_flagged = 0; } rc = buffy_maildir_check_dir(mailbox, "new", check_new, check_stats); check_new = !rc && option(OPT_MAILDIR_CHECK_CUR); if (check_new || check_stats) if (buffy_maildir_check_dir(mailbox, "cur", check_new, check_stats)) rc = 1; return rc; } /** * buffy_mbox_check - Check for new mail for an mbox mailbox * @param mailbox Mailbox to check * @param sb stat(2) information about the mailbox * @param check_stats if true, also count total, new, and flagged messages * @retval 1 if the mailbox has new mail */ static int buffy_mbox_check(struct Buffy *mailbox, struct stat *sb, bool check_stats) { int rc = 0; int new_or_changed; struct Context ctx; if (option(OPT_CHECK_MBOX_SIZE)) new_or_changed = sb->st_size > mailbox->size; else new_or_changed = sb->st_mtime > sb->st_atime || (mailbox->newly_created && sb->st_ctime == sb->st_mtime && sb->st_ctime == sb->st_atime); if (new_or_changed) { if (!option(OPT_MAIL_CHECK_RECENT) || sb->st_mtime > mailbox->last_visited) { rc = 1; mailbox->new = true; } } else if (option(OPT_CHECK_MBOX_SIZE)) { /* some other program has deleted mail from the folder */ mailbox->size = (off_t) sb->st_size; } if (mailbox->newly_created && (sb->st_ctime != sb->st_mtime || sb->st_ctime != sb->st_atime)) mailbox->newly_created = false; if (check_stats && (mailbox->stats_last_checked < sb->st_mtime)) { if (mx_open_mailbox(mailbox->path, MUTT_READONLY | MUTT_QUIET | MUTT_NOSORT | MUTT_PEEK, &ctx) != NULL) { mailbox->msg_count = ctx.msgcount; mailbox->msg_unread = ctx.unread; mailbox->msg_flagged = ctx.flagged; mailbox->stats_last_checked = ctx.mtime; mx_close_mailbox(&ctx, 0); } } return rc; } static void buffy_check(struct Buffy *tmp, struct stat *contex_sb, bool check_stats) { struct stat sb; #ifdef USE_SIDEBAR short orig_new; int orig_count, orig_unread, orig_flagged; #endif sb.st_size = 0; #ifdef USE_SIDEBAR orig_new = tmp->new; orig_count = tmp->msg_count; orig_unread = tmp->msg_unread; orig_flagged = tmp->msg_flagged; #endif if (tmp->magic != MUTT_IMAP) { tmp->new = false; #ifdef USE_POP if (mx_is_pop(tmp->path)) tmp->magic = MUTT_POP; else #endif #ifdef USE_NNTP if ((tmp->magic == MUTT_NNTP) || mx_is_nntp(tmp->path)) tmp->magic = MUTT_NNTP; #endif #ifdef USE_NOTMUCH if (mx_is_notmuch(tmp->path)) tmp->magic = MUTT_NOTMUCH; 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)) { /* if the mailbox still doesn't exist, set the newly created flag to * be ready for when it does. */ tmp->newly_created = true; tmp->magic = 0; tmp->size = 0; return; } } /* check to see if the folder is the currently selected folder * before polling */ if (!Context || !Context->path || ((tmp->magic == MUTT_IMAP || #ifdef USE_NNTP tmp->magic == MUTT_NNTP || #endif #ifdef USE_NOTMUCH tmp->magic == MUTT_NOTMUCH || #endif tmp->magic == MUTT_POP) ? (mutt_str_strcmp(tmp->path, Context->path) != 0) : (sb.st_dev != contex_sb->st_dev || sb.st_ino != contex_sb->st_ino))) { switch (tmp->magic) { case MUTT_MBOX: case MUTT_MMDF: if (buffy_mbox_check(tmp, &sb, check_stats) > 0) BuffyCount++; break; case MUTT_MAILDIR: if (buffy_maildir_check(tmp, check_stats) > 0) BuffyCount++; break; case MUTT_MH: if (mh_buffy(tmp, check_stats)) BuffyCount++; break; #ifdef USE_NOTMUCH case MUTT_NOTMUCH: tmp->msg_count = 0; tmp->msg_unread = 0; tmp->msg_flagged = 0; nm_nonctx_get_count(tmp->path, &tmp->msg_count, &tmp->msg_unread); if (tmp->msg_unread > 0) { BuffyCount++; tmp->new = true; } break; #endif } } else if (option(OPT_CHECK_MBOX_SIZE) && Context && Context->path) tmp->size = (off_t) sb.st_size; /* update the size of current folder */ #ifdef USE_SIDEBAR if ((orig_new != tmp->new) || (orig_count != tmp->msg_count) || (orig_unread != tmp->msg_unread) || (orig_flagged != tmp->msg_flagged)) { mutt_set_current_menu_redraw(REDRAW_SIDEBAR); } #endif if (!tmp->new) tmp->notified = false; else if (!tmp->notified) BuffyNotify++; } /** * buffy_get - fetch buffy object for given path, if present */ static struct Buffy *buffy_get(const char *path) { struct Buffy *cur = NULL; char *epath = NULL; if (!path) return NULL; epath = mutt_str_strdup(path); mutt_expand_path(epath, mutt_str_strlen(epath)); for (cur = Incoming; cur; cur = cur->next) { /* must be done late because e.g. IMAP delimiter may change */ mutt_expand_path(cur->path, sizeof(cur->path)); if (mutt_str_strcmp(cur->path, path) == 0) { FREE(&epath); return cur; } } FREE(&epath); return NULL; } void mutt_buffy_cleanup(const char *buf, struct stat *st) { struct utimbuf ut; if (option(OPT_CHECK_MBOX_SIZE)) { struct Buffy *b = mutt_find_mailbox(buf); if (b && !b->new) mutt_update_mailbox(b); } else { /* fix up the times so buffy won't get confused */ if (st->st_mtime > st->st_atime) { ut.actime = st->st_atime; ut.modtime = time(NULL); utime(buf, &ut); } else utime(buf, NULL); } } struct Buffy *mutt_find_mailbox(const char *path) { struct stat sb; struct stat tmp_sb; if (stat(path, &sb) != 0) return NULL; for (struct Buffy *b = Incoming; b; b = b->next) { if (stat(b->path, &tmp_sb) == 0 && sb.st_dev == tmp_sb.st_dev && sb.st_ino == tmp_sb.st_ino) { return b; } } return NULL; } void mutt_update_mailbox(struct Buffy *b) { struct stat sb; if (!b) return; if (stat(b->path, &sb) == 0) b->size = (off_t) sb.st_size; else b->size = 0; return; } int mutt_parse_mailboxes(struct Buffer *path, struct Buffer *s, unsigned long data, struct Buffer *err) { struct Buffy **b = NULL; char buf[_POSIX_PATH_MAX]; struct stat sb; char f1[PATH_MAX]; char *p = NULL; while (MoreArgs(s)) { char *desc = NULL; if (data & MUTT_NAMED) { mutt_extract_token(path, s, 0); if (path->data && *path->data) desc = mutt_str_strdup(path->data); else continue; } mutt_extract_token(path, s, 0); #ifdef USE_NOTMUCH if (mx_is_notmuch(path->data)) nm_normalize_uri(buf, path->data, sizeof(buf)); else #endif mutt_str_strfcpy(buf, path->data, sizeof(buf)); mutt_expand_path(buf, sizeof(buf)); /* Skip empty tokens. */ if (!*buf) { FREE(&desc); continue; } /* avoid duplicates */ p = realpath(buf, f1); for (b = &Incoming; *b; b = &((*b)->next)) { if (mutt_str_strcmp(p ? p : buf, (*b)->realpath) == 0) { mutt_debug(3, "mailbox '%s' already registered as '%s'\n", buf, (*b)->path); break; } } if (*b) { FREE(&desc); continue; } *b = buffy_new(buf); (*b)->new = false; (*b)->notified = true; (*b)->newly_created = false; (*b)->desc = desc; #ifdef USE_NOTMUCH if (mx_is_notmuch((*b)->path)) { (*b)->magic = MUTT_NOTMUCH; (*b)->size = 0; } else #endif { /* for check_mbox_size, it is important that if the folder is new (tested by * reading it), the size is set to 0 so that later when we check we see * that it increased. without check_mbox_size we probably don't care. */ if (option(OPT_CHECK_MBOX_SIZE) && stat((*b)->path, &sb) == 0 && !test_new_folder((*b)->path)) { /* some systems out there don't have an off_t type */ (*b)->size = (off_t) sb.st_size; } else (*b)->size = 0; } #ifdef USE_SIDEBAR mutt_sb_notify_mailbox(*b, 1); #endif } return 0; } int mutt_parse_unmailboxes(struct Buffer *path, struct Buffer *s, unsigned long data, struct Buffer *err) { char buf[_POSIX_PATH_MAX]; bool clear_all = false; while (!clear_all && MoreArgs(s)) { mutt_extract_token(path, s, 0); if (mutt_str_strcmp(path->data, "*") == 0) { clear_all = true; } else { #ifdef USE_NOTMUCH if (mx_is_notmuch(path->data)) { nm_normalize_uri(buf, path->data, sizeof(buf)); } else #endif { mutt_str_strfcpy(buf, path->data, sizeof(buf)); mutt_expand_path(buf, sizeof(buf)); } } for (struct Buffy **b = &Incoming; *b;) { /* Decide whether to delete all normal mailboxes or all virtual */ bool virt = (((*b)->magic == MUTT_NOTMUCH) && (data & MUTT_VIRTUAL)); bool norm = (((*b)->magic != MUTT_NOTMUCH) && !(data & MUTT_VIRTUAL)); bool clear_this = clear_all && (virt | norm); if (clear_this || (mutt_str_strcasecmp(buf, (*b)->path) == 0) || (mutt_str_strcasecmp(buf, (*b)->desc) == 0)) { struct Buffy *next = (*b)->next; #ifdef USE_SIDEBAR mutt_sb_notify_mailbox(*b, 0); #endif buffy_free(b); *b = next; continue; } b = &((*b)->next); } } return 0; } /** * mutt_buffy_check - Check all Incoming for new mail * * Check all Incoming for new mail and total/new/flagged messages * force: if true, ignore MailCheck and check for new mail anyway */ int mutt_buffy_check(bool force) { struct stat contex_sb; time_t t; bool check_stats = false; contex_sb.st_dev = 0; contex_sb.st_ino = 0; #ifdef USE_IMAP /* update postponed count as well, on force */ if (force) mutt_update_num_postponed(); #endif /* fastest return if there are no mailboxes */ if (!Incoming) return 0; t = time(NULL); if (!force && (t - BuffyTime < MailCheck)) return BuffyCount; if (option(OPT_MAIL_CHECK_STATS) && (t - BuffyStatsTime >= MailCheckStatsInterval)) { check_stats = true; BuffyStatsTime = t; } BuffyTime = t; BuffyCount = 0; BuffyNotify = 0; #ifdef USE_IMAP BuffyCount += imap_buffy_check(check_stats); #endif /* check device ID and serial number instead of comparing paths */ if (!Context || Context->magic == MUTT_IMAP || Context->magic == MUTT_POP #ifdef USE_NNTP || Context->magic == MUTT_NNTP #endif || stat(Context->path, &contex_sb) != 0) { contex_sb.st_dev = 0; contex_sb.st_ino = 0; } for (struct Buffy *b = Incoming; b; b = b->next) buffy_check(b, &contex_sb, check_stats); BuffyDoneTime = BuffyTime; return BuffyCount; } int mutt_buffy_list(void) { struct Buffy *b = NULL; char path[_POSIX_PATH_MAX]; char buffylist[2 * STRING]; size_t pos = 0; int first = 1; int have_unnotified = BuffyNotify; buffylist[0] = '\0'; pos += strlen(strncat(buffylist, _("New mail in "), sizeof(buffylist) - 1 - pos)); for (b = Incoming; b; b = b->next) { /* Is there new mail in this mailbox? */ if (!b->new || (have_unnotified && b->notified)) continue; mutt_str_strfcpy(path, b->path, sizeof(path)); mutt_pretty_mailbox(path, sizeof(path)); if (!first && (MuttMessageWindow->cols >= 7) && (pos + strlen(path) >= (size_t) MuttMessageWindow->cols - 7)) { break; } if (!first) pos += strlen(strncat(buffylist + pos, ", ", sizeof(buffylist) - 1 - pos)); /* Prepend an asterisk to mailboxes not already notified */ if (!b->notified) { /* pos += strlen (strncat(buffylist + pos, "*", sizeof(buffylist)-1-pos)); */ b->notified = true; BuffyNotify--; } pos += strlen(strncat(buffylist + pos, path, sizeof(buffylist) - 1 - pos)); first = 0; } if (!first && b) { strncat(buffylist + pos, ", ...", sizeof(buffylist) - 1 - pos); } if (!first) { mutt_message("%s", buffylist); return 1; } /* there were no mailboxes needing to be notified, so clean up since * BuffyNotify has somehow gotten out of sync */ BuffyNotify = 0; return 0; } void mutt_buffy_setnotified(const char *path) { struct Buffy *buffy = NULL; buffy = buffy_get(path); if (!buffy) return; buffy->notified = true; time(&buffy->last_visited); } int mutt_buffy_notify(void) { if (mutt_buffy_check(false) && BuffyNotify) { return (mutt_buffy_list()); } return 0; } /** * mutt_buffy - incoming folders completion routine * @param s Buffer containing name of current mailbox * @param slen Buffer length * * Given a folder name, find the next incoming folder with new mail. */ void mutt_buffy(char *s, size_t slen) { int pass, found = 0; mutt_expand_path(s, slen); if (mutt_buffy_check(false)) { for (pass = 0; pass < 2; pass++) { for (struct Buffy *b = Incoming; b; b = b->next) { if (b->magic == MUTT_NOTMUCH) /* only match real mailboxes */ continue; mutt_expand_path(b->path, sizeof(b->path)); if ((found || pass) && b->new) { mutt_str_strfcpy(s, b->path, slen); mutt_pretty_mailbox(s, slen); return; } if (mutt_str_strcmp(s, b->path) == 0) found = 1; } } mutt_buffy_check(true); /* buffy was wrong - resync things */ } /* no folders with new mail */ *s = '\0'; } #ifdef USE_NOTMUCH void mutt_buffy_vfolder(char *s, size_t slen) { bool found = false; if (mutt_buffy_check(false)) { for (int pass = 0; pass < 2; pass++) { for (struct Buffy *b = Incoming; b; b = b->next) { if (b->magic != MUTT_NOTMUCH) continue; if ((found || pass) && b->new) { mutt_str_strfcpy(s, b->desc, slen); return; } if (mutt_str_strcmp(s, b->path) == 0) found = true; } } mutt_buffy_check(true); /* buffy was wrong - resync things */ } /* no folders with new mail */ *s = '\0'; } #endif neomutt-neomutt-20171215/buffy.h000066400000000000000000000052011321473123000164200ustar00rootroot00000000000000/** * @file * Representation of a mailbox * * @authors * Copyright (C) 1996-2000,2010,2013 Michael R. Elkins * * @copyright * 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, see . */ #ifndef _MUTT_BUFFY_H #define _MUTT_BUFFY_H #include #include #include #include #include "where.h" struct stat; /* parameter to mutt_parse_mailboxes */ #define MUTT_NAMED 1 #define MUTT_VIRTUAL 2 /** * struct Buffy - A mailbox */ struct Buffy { char path[_POSIX_PATH_MAX]; char realpath[_POSIX_PATH_MAX]; /**< used for duplicate detection, context * comparison, and the sidebar */ char *desc; off_t size; struct Buffy *next; bool new; /**< mailbox has new mail */ /* These next three are only set when OPT_MAIL_CHECK_STATS is set */ int msg_count; /**< total number of messages */ int msg_unread; /**< number of unread messages */ int msg_flagged; /**< number of flagged messages */ bool notified; /**< user has been notified */ short magic; /**< mailbox type */ bool newly_created; /**< mbox or mmdf just popped into existence */ time_t last_visited; /**< time of last exit from this mailbox */ time_t stats_last_checked; /**< mtime of mailbox the last time stats where checked. */ }; WHERE struct Buffy *Incoming; WHERE short MailCheck; WHERE short MailCheckStatsInterval; #ifdef USE_NOTMUCH void mutt_buffy_vfolder(char *s, size_t slen); #endif extern time_t BuffyDoneTime; /**< last time we knew for sure how much mail there was */ struct Buffy *mutt_find_mailbox(const char *path); void mutt_update_mailbox(struct Buffy *b); /** fixes up atime + mtime after mbox/mmdf mailbox was modified * according to stat() info taken before a modification */ void mutt_buffy_cleanup(const char *buf, struct stat *st); /** mark mailbox just left as already notified */ void mutt_buffy_setnotified(const char *path); bool mh_buffy(struct Buffy *mailbox, bool check_stats); #endif /* _MUTT_BUFFY_H */ neomutt-neomutt-20171215/color.c000066400000000000000000000617661321473123000164400ustar00rootroot00000000000000/** * @file * Color and attribute parsing * * @authors * Copyright (C) 1996-2002,2012 Michael R. Elkins * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "context.h" #include "globals.h" #include "header.h" #include "keymap.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mutt_regex.h" #include "options.h" #include "pattern.h" #include "protos.h" /* globals */ int *ColorQuote = NULL; int ColorQuoteUsed; int ColorDefs[MT_COLOR_MAX]; struct ColorLineHead ColorHdrList = STAILQ_HEAD_INITIALIZER(ColorHdrList); struct ColorLineHead ColorBodyList = STAILQ_HEAD_INITIALIZER(ColorBodyList); struct ColorLineHead ColorAttachList = STAILQ_HEAD_INITIALIZER(ColorAttachList); struct ColorLineHead ColorStatusList = STAILQ_HEAD_INITIALIZER(ColorStatusList); struct ColorLineHead ColorIndexList = STAILQ_HEAD_INITIALIZER(ColorIndexList); struct ColorLineHead ColorIndexAuthorList = STAILQ_HEAD_INITIALIZER(ColorIndexAuthorList); struct ColorLineHead ColorIndexFlagsList = STAILQ_HEAD_INITIALIZER(ColorIndexFlagsList); struct ColorLineHead ColorIndexSubjectList = STAILQ_HEAD_INITIALIZER(ColorIndexSubjectList); struct ColorLineHead ColorIndexTagList = STAILQ_HEAD_INITIALIZER(ColorIndexTagList); /* local to this file */ static int ColorQuoteSize; #ifdef HAVE_COLOR #define COLOR_DEFAULT (-2) /** * struct ColorList - A set of colors */ struct ColorList { short fg; short bg; short index; short count; struct ColorList *next; }; static struct ColorList *ColorList = NULL; static int UserColors = 0; static const struct Mapping Colors[] = { { "black", COLOR_BLACK }, { "blue", COLOR_BLUE }, { "cyan", COLOR_CYAN }, { "green", COLOR_GREEN }, { "magenta", COLOR_MAGENTA }, { "red", COLOR_RED }, { "white", COLOR_WHITE }, { "yellow", COLOR_YELLOW }, #if defined(USE_SLANG_CURSES) || defined(HAVE_USE_DEFAULT_COLORS) { "default", COLOR_DEFAULT }, #endif { 0, 0 }, }; #endif /* HAVE_COLOR */ static const struct Mapping Fields[] = { { "hdrdefault", MT_COLOR_HDEFAULT }, { "quoted", MT_COLOR_QUOTED }, { "signature", MT_COLOR_SIGNATURE }, { "indicator", MT_COLOR_INDICATOR }, { "status", MT_COLOR_STATUS }, { "tree", MT_COLOR_TREE }, { "error", MT_COLOR_ERROR }, { "normal", MT_COLOR_NORMAL }, { "tilde", MT_COLOR_TILDE }, { "markers", MT_COLOR_MARKERS }, { "header", MT_COLOR_HEADER }, { "body", MT_COLOR_BODY }, { "message", MT_COLOR_MESSAGE }, { "attachment", MT_COLOR_ATTACHMENT }, { "attach_headers", MT_COLOR_ATTACH_HEADERS }, { "search", MT_COLOR_SEARCH }, { "bold", MT_COLOR_BOLD }, { "underline", MT_COLOR_UNDERLINE }, { "index", MT_COLOR_INDEX }, { "progress", MT_COLOR_PROGRESS }, { "index_author", MT_COLOR_INDEX_AUTHOR }, { "index_collapsed", MT_COLOR_INDEX_COLLAPSED }, { "index_date", MT_COLOR_INDEX_DATE }, { "index_flags", MT_COLOR_INDEX_FLAGS }, { "index_label", MT_COLOR_INDEX_LABEL }, { "index_number", MT_COLOR_INDEX_NUMBER }, { "index_size", MT_COLOR_INDEX_SIZE }, { "index_subject", MT_COLOR_INDEX_SUBJECT }, { "index_tag", MT_COLOR_INDEX_TAG }, { "index_tags", MT_COLOR_INDEX_TAGS }, { "prompt", MT_COLOR_PROMPT }, #ifdef USE_SIDEBAR { "sidebar_divider", MT_COLOR_DIVIDER }, { "sidebar_flagged", MT_COLOR_FLAGGED }, { "sidebar_highlight", MT_COLOR_HIGHLIGHT }, { "sidebar_indicator", MT_COLOR_SB_INDICATOR }, { "sidebar_new", MT_COLOR_NEW }, { "sidebar_ordinary", MT_COLOR_ORDINARY }, { "sidebar_spoolfile", MT_COLOR_SB_SPOOLFILE }, #endif { NULL, 0 }, }; static const struct Mapping ComposeFields[] = { { "header", MT_COLOR_COMPOSE_HEADER }, { "security_encrypt", MT_COLOR_COMPOSE_SECURITY_ENCRYPT }, { "security_sign", MT_COLOR_COMPOSE_SECURITY_SIGN }, { "security_both", MT_COLOR_COMPOSE_SECURITY_BOTH }, { "security_none", MT_COLOR_COMPOSE_SECURITY_NONE }, { NULL, 0 } }; #define COLOR_QUOTE_INIT 8 static struct ColorLine *new_color_line(void) { struct ColorLine *p = mutt_mem_calloc(1, sizeof(struct ColorLine)); p->fg = p->bg = -1; return p; } static void free_color_line(struct ColorLine *tmp, int free_colors) { if (!tmp) return; #ifdef HAVE_COLOR if (free_colors && tmp->fg != -1 && tmp->bg != -1) mutt_free_color(tmp->fg, tmp->bg); #endif /* we should really introduce a container * type for regular expressions. */ regfree(&tmp->regex); mutt_pattern_free(&tmp->color_pattern); FREE(&tmp->pattern); FREE(&tmp); } void ci_start_color(void) { memset(ColorDefs, A_NORMAL, sizeof(int) * MT_COLOR_MAX); ColorQuote = mutt_mem_malloc(COLOR_QUOTE_INIT * sizeof(int)); memset(ColorQuote, A_NORMAL, sizeof(int) * COLOR_QUOTE_INIT); ColorQuoteSize = COLOR_QUOTE_INIT; ColorQuoteUsed = 0; /* set some defaults */ ColorDefs[MT_COLOR_STATUS] = A_REVERSE; ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE; ColorDefs[MT_COLOR_SEARCH] = A_REVERSE; ColorDefs[MT_COLOR_MARKERS] = A_REVERSE; #ifdef USE_SIDEBAR ColorDefs[MT_COLOR_HIGHLIGHT] = A_UNDERLINE; #endif /* special meaning: toggle the relevant attribute */ ColorDefs[MT_COLOR_BOLD] = 0; ColorDefs[MT_COLOR_UNDERLINE] = 0; #ifdef HAVE_COLOR start_color(); #endif } #ifdef HAVE_COLOR #ifdef USE_SLANG_CURSES static char *get_color_name(char *dest, size_t destlen, int val) { static const char *const missing[3] = { "brown", "lightgray", "default" }; switch (val) { case COLOR_YELLOW: mutt_str_strfcpy(dest, missing[0], destlen); return dest; case COLOR_WHITE: mutt_str_strfcpy(dest, missing[1], destlen); return dest; case COLOR_DEFAULT: mutt_str_strfcpy(dest, missing[2], destlen); return dest; } for (int i = 0; Colors[i].name; i++) { if (Colors[i].value == val) { mutt_str_strfcpy(dest, Colors[i].name, destlen); return dest; } } /* Sigh. If we got this far, the color is of the form 'colorN' * Slang can handle this itself, so just return 'colorN' */ snprintf(dest, destlen, "color%d", val); return dest; } #endif int mutt_alloc_color(int fg, int bg) { struct ColorList *p = ColorList; int i; #ifdef USE_SLANG_CURSES char fgc[SHORT_STRING], bgc[SHORT_STRING]; #endif /* check to see if this color is already allocated to save space */ while (p) { if (p->fg == fg && p->bg == bg) { (p->count)++; return (COLOR_PAIR(p->index)); } p = p->next; } /* check to see if there are colors left */ if (++UserColors > COLOR_PAIRS) return A_NORMAL; /* find the smallest available index (object) */ i = 1; while (true) { p = ColorList; while (p) { if (p->index == i) break; p = p->next; } if (!p) break; i++; } p = mutt_mem_malloc(sizeof(struct ColorList)); p->next = ColorList; ColorList = p; p->index = i; p->count = 1; p->bg = bg; p->fg = fg; #ifdef USE_SLANG_CURSES if (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT) SLtt_set_color(i, NULL, get_color_name(fgc, sizeof(fgc), fg), get_color_name(bgc, sizeof(bgc), bg)); else #elif defined(HAVE_USE_DEFAULT_COLORS) if (fg == COLOR_DEFAULT) fg = -1; if (bg == COLOR_DEFAULT) bg = -1; #endif init_pair(i, fg, bg); mutt_debug(3, "Color pairs used so far: %d\n", UserColors); return (COLOR_PAIR(p->index)); } static int mutt_lookup_color(short pair, short *fg, short *bg) { struct ColorList *p = ColorList; while (p) { if (COLOR_PAIR(p->index) == pair) { if (fg) *fg = p->fg; if (bg) *bg = p->bg; return 0; } p = p->next; } return -1; } int mutt_combine_color(int fg_attr, int bg_attr) { short fg, bg; fg = bg = COLOR_DEFAULT; mutt_lookup_color(fg_attr, &fg, NULL); mutt_lookup_color(bg_attr, NULL, &bg); if ((fg == COLOR_DEFAULT) && (bg == COLOR_DEFAULT)) return A_NORMAL; return mutt_alloc_color(fg, bg); } void mutt_free_color(int fg, int bg) { struct ColorList *p = NULL, *q = NULL; p = ColorList; while (p) { if (p->fg == fg && p->bg == bg) { (p->count)--; if (p->count > 0) return; UserColors--; mutt_debug(1, "Color pairs used so far: %d\n", UserColors); if (p == ColorList) { ColorList = ColorList->next; FREE(&p); return; } q = ColorList; while (q) { if (q->next == p) { q->next = p->next; FREE(&p); return; } q = q->next; } /* can't get here */ } p = p->next; } } #endif /* HAVE_COLOR */ #ifdef HAVE_COLOR static int parse_color_name(const char *s, int *col, int *attr, int is_fg, struct Buffer *err) { char *eptr = NULL; int is_bright = 0; if (mutt_str_strncasecmp(s, "bright", 6) == 0) { is_bright = 1; s += 6; } /* allow aliases for xterm color resources */ if (mutt_str_strncasecmp(s, "color", 5) == 0) { s += 5; *col = strtol(s, &eptr, 10); if (!*s || *eptr || *col < 0 || (*col >= COLORS && !option(OPT_NO_CURSES) && has_colors())) { snprintf(err->data, err->dsize, _("%s: color not supported by term"), s); return -1; } } else if ((*col = mutt_map_get_value(s, Colors)) == -1) { snprintf(err->data, err->dsize, _("%s: no such color"), s); return -1; } if (is_bright) { if (is_fg) { *attr |= A_BOLD; } else if (COLORS < 16) { /* A_BLINK turns the background color brite on some terms */ *attr |= A_BLINK; } else { /* Advance the color by 8 to get the bright version */ *col += 8; } } return 0; } #endif static void do_uncolor(struct Buffer *buf, struct Buffer *s, struct ColorLineHead *cl, int *do_cache, bool parse_uncolor) { struct ColorLine *np = NULL, *tmp = NULL; do { mutt_extract_token(buf, s, 0); if (mutt_str_strcmp("*", buf->data) == 0) { np = STAILQ_FIRST(cl); while (np) { tmp = STAILQ_NEXT(np, entries); if (!*do_cache) { *do_cache = 1; } free_color_line(np, parse_uncolor); np = tmp; } STAILQ_INIT(cl); return; } else { tmp = NULL; STAILQ_FOREACH(np, cl, entries) { if (mutt_str_strcmp(buf->data, np->pattern) == 0) { if (!*do_cache) { *do_cache = 1; } mutt_debug(1, "Freeing pattern \"%s\" from ColorList\n", buf->data); if (tmp) STAILQ_REMOVE_AFTER(cl, tmp, entries); else STAILQ_REMOVE_HEAD(cl, entries); free_color_line(np, parse_uncolor); break; } tmp = np; } } } while (MoreArgs(s)); } /** * parse_uncolor - Parse an 'uncolor' command * * usage: * * uncolor index pattern [pattern...] * * unmono index pattern [pattern...] */ static int parse_uncolor(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err, short parse_uncolor) { int object = 0, do_cache = 0; mutt_extract_token(buf, s, 0); object = mutt_map_get_value(buf->data, Fields); if (object == -1) { snprintf(err->data, err->dsize, _("%s: no such object"), buf->data); return -1; } if (object > MT_COLOR_INDEX_SUBJECT) { /* uncolor index column */ ColorDefs[object] = 0; mutt_set_menu_redraw_full(MENU_MAIN); return 0; } if ((mutt_str_strncmp(buf->data, "body", 4) != 0) && (mutt_str_strncmp(buf->data, "header", 6) != 0) && (mutt_str_strncmp(buf->data, "index", 5) != 0)) { snprintf(err->data, err->dsize, _("%s: command valid only for index, body, header objects"), parse_uncolor ? "uncolor" : "unmono"); return -1; } if (!MoreArgs(s)) { snprintf(err->data, err->dsize, _("%s: too few arguments"), parse_uncolor ? "uncolor" : "unmono"); return -1; } if ( #ifdef HAVE_COLOR /* we're running without curses */ option(OPT_NO_CURSES) || /* we're parsing an uncolor command, and have no colors */ (parse_uncolor && !has_colors()) /* we're parsing an unmono command, and have colors */ || (!parse_uncolor && has_colors()) #else /* We don't even have colors compiled in */ parse_uncolor #endif ) { /* just eat the command, but don't do anything real about it */ do mutt_extract_token(buf, s, 0); while (MoreArgs(s)); return 0; } if (object == MT_COLOR_BODY) do_uncolor(buf, s, &ColorBodyList, &do_cache, parse_uncolor); else if (object == MT_COLOR_HEADER) do_uncolor(buf, s, &ColorHdrList, &do_cache, parse_uncolor); else if (object == MT_COLOR_ATTACH_HEADERS) do_uncolor(buf, s, &ColorAttachList, &do_cache, parse_uncolor); else if (object == MT_COLOR_INDEX) do_uncolor(buf, s, &ColorIndexList, &do_cache, parse_uncolor); else if (object == MT_COLOR_INDEX_AUTHOR) do_uncolor(buf, s, &ColorIndexAuthorList, &do_cache, parse_uncolor); else if (object == MT_COLOR_INDEX_FLAGS) do_uncolor(buf, s, &ColorIndexFlagsList, &do_cache, parse_uncolor); else if (object == MT_COLOR_INDEX_SUBJECT) do_uncolor(buf, s, &ColorIndexSubjectList, &do_cache, parse_uncolor); else if (object == MT_COLOR_INDEX_TAG) do_uncolor(buf, s, &ColorIndexTagList, &do_cache, parse_uncolor); if (do_cache && !option(OPT_NO_CURSES)) { mutt_set_menu_redraw_full(MENU_MAIN); /* force re-caching of index colors */ for (int i = 0; Context && i < Context->msgcount; i++) Context->hdrs[i]->pair = 0; } return 0; } #ifdef HAVE_COLOR int mutt_parse_uncolor(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err) { return parse_uncolor(buf, s, data, err, 1); } #endif int mutt_parse_unmono(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err) { return parse_uncolor(buf, s, data, err, 0); } static int add_pattern(struct ColorLineHead *top, const char *s, int sensitive, int fg, int bg, int attr, struct Buffer *err, int is_index, int match) { /* is_index used to store compiled pattern * only for `index' color object * when called from mutt_parse_color() */ struct ColorLine *tmp = NULL; STAILQ_FOREACH(tmp, top, entries) { if (sensitive) { if (mutt_str_strcmp(s, tmp->pattern) == 0) break; } else { if (mutt_str_strcasecmp(s, tmp->pattern) == 0) break; } } if (tmp) { #ifdef HAVE_COLOR if (fg != -1 && bg != -1) { if (tmp->fg != fg || tmp->bg != bg) { mutt_free_color(tmp->fg, tmp->bg); tmp->fg = fg; tmp->bg = bg; attr |= mutt_alloc_color(fg, bg); } else attr |= (tmp->pair & ~A_BOLD); } #endif /* HAVE_COLOR */ tmp->pair = attr; } else { int r; char buf[LONG_STRING]; tmp = new_color_line(); if (is_index) { mutt_str_strfcpy(buf, NONULL(s), sizeof(buf)); mutt_check_simple(buf, sizeof(buf), NONULL(SimpleSearch)); tmp->color_pattern = mutt_pattern_comp(buf, MUTT_FULL_MSG, err); if (!tmp->color_pattern) { free_color_line(tmp, 1); return -1; } /* force re-caching of index colors */ for (int i = 0; Context && i < Context->msgcount; i++) Context->hdrs[i]->pair = 0; } else if ((r = REGCOMP(&tmp->regex, s, (sensitive ? mutt_which_case(s) : REG_ICASE))) != 0) { regerror(r, &tmp->regex, err->data, err->dsize); free_color_line(tmp, 1); return -1; } tmp->pattern = mutt_str_strdup(s); tmp->match = match; #ifdef HAVE_COLOR if (fg != -1 && bg != -1) { tmp->fg = fg; tmp->bg = bg; attr |= mutt_alloc_color(fg, bg); } #endif tmp->pair = attr; STAILQ_INSERT_HEAD(top, tmp, entries); } return 0; } static int parse_object(struct Buffer *buf, struct Buffer *s, int *o, int *ql, struct Buffer *err) { int q_level = 0; char *eptr = NULL; if (!MoreArgs(s)) { mutt_str_strfcpy(err->data, _("Missing arguments."), err->dsize); return -1; } mutt_extract_token(buf, s, 0); if (mutt_str_strncmp(buf->data, "quoted", 6) == 0) { if (buf->data[6]) { *ql = strtol(buf->data + 6, &eptr, 10); if (*eptr || q_level < 0) { snprintf(err->data, err->dsize, _("%s: no such object"), buf->data); return -1; } } else *ql = 0; *o = MT_COLOR_QUOTED; } else if (!mutt_str_strcasecmp(buf->data, "compose")) { if (!MoreArgs(s)) { mutt_str_strfcpy(err->data, _("Missing arguments."), err->dsize); return -1; } mutt_extract_token(buf, s, 0); *o = mutt_map_get_value(buf->data, ComposeFields); if (*o == -1) { snprintf(err->data, err->dsize, _("%s: no such object"), buf->data); return (-1); } } else if ((*o = mutt_map_get_value(buf->data, Fields)) == -1) { snprintf(err->data, err->dsize, _("%s: no such object"), buf->data); return -1; } return 0; } typedef int (*parser_callback_t)(struct Buffer *buf, struct Buffer *s, int *fg, int *bg, int *attr, struct Buffer *err); #ifdef HAVE_COLOR static int parse_color_pair(struct Buffer *buf, struct Buffer *s, int *fg, int *bg, int *attr, struct Buffer *err) { if (!MoreArgs(s)) { mutt_str_strfcpy(err->data, _("color: too few arguments"), err->dsize); return -1; } mutt_extract_token(buf, s, 0); if (parse_color_name(buf->data, fg, attr, 1, err) != 0) return -1; if (!MoreArgs(s)) { mutt_str_strfcpy(err->data, _("color: too few arguments"), err->dsize); return -1; } mutt_extract_token(buf, s, 0); if (parse_color_name(buf->data, bg, attr, 0, err) != 0) return -1; return 0; } #endif static int parse_attr_spec(struct Buffer *buf, struct Buffer *s, int *fg, int *bg, int *attr, struct Buffer *err) { if (fg) *fg = -1; if (bg) *bg = -1; if (!MoreArgs(s)) { mutt_str_strfcpy(err->data, _("mono: too few arguments"), err->dsize); return -1; } mutt_extract_token(buf, s, 0); if (mutt_str_strcasecmp("bold", buf->data) == 0) *attr |= A_BOLD; else if (mutt_str_strcasecmp("underline", buf->data) == 0) *attr |= A_UNDERLINE; else if (mutt_str_strcasecmp("none", buf->data) == 0) *attr = A_NORMAL; else if (mutt_str_strcasecmp("reverse", buf->data) == 0) *attr |= A_REVERSE; else if (mutt_str_strcasecmp("standout", buf->data) == 0) *attr |= A_STANDOUT; else if (mutt_str_strcasecmp("normal", buf->data) == 0) *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */ else { snprintf(err->data, err->dsize, _("%s: no such attribute"), buf->data); return -1; } return 0; } static int fgbgattr_to_color(int fg, int bg, int attr) { #ifdef HAVE_COLOR if (fg != -1 && bg != -1) return attr | mutt_alloc_color(fg, bg); else #endif return attr; } /** * parse_color - Parse a "color" command * * usage: color OBJECT FG BG [ REGEX ] * mono OBJECT ATTR [ REGEX ] */ static int parse_color(struct Buffer *buf, struct Buffer *s, struct Buffer *err, parser_callback_t callback, bool dry_run) { int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0; int r = 0, match = 0; if (parse_object(buf, s, &object, &q_level, err) == -1) return -1; if (callback(buf, s, &fg, &bg, &attr, err) == -1) return -1; /* extract a regular expression if needed */ if ((object == MT_COLOR_BODY) || (object == MT_COLOR_HEADER) || (object == MT_COLOR_ATTACH_HEADERS) || (object == MT_COLOR_INDEX) || (object == MT_COLOR_INDEX_AUTHOR) || (object == MT_COLOR_INDEX_FLAGS) || (object == MT_COLOR_INDEX_TAG) || (object == MT_COLOR_INDEX_SUBJECT)) { if (!MoreArgs(s)) { mutt_str_strfcpy(err->data, _("too few arguments"), err->dsize); return -1; } mutt_extract_token(buf, s, 0); } if (MoreArgs(s) && (object != MT_COLOR_STATUS)) { mutt_str_strfcpy(err->data, _("too many arguments"), err->dsize); return -1; } /* dry run? */ if (dry_run) { *s->dptr = '\0'; /* fake that we're done parsing */ return 0; } #ifdef HAVE_COLOR #ifdef HAVE_USE_DEFAULT_COLORS if (!option(OPT_NO_CURSES) && has_colors() /* delay use_default_colors() until needed, since it initializes things */ && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT || object == MT_COLOR_TREE) && use_default_colors() != OK) /* the case of the tree object is special, because a non-default * fg color of the tree element may be combined dynamically with * the default bg color of an index line, not necessarily defined in * a rc file. */ { mutt_str_strfcpy(err->data, _("default colors not supported"), err->dsize); return -1; } #endif /* HAVE_USE_DEFAULT_COLORS */ #endif if (object == MT_COLOR_HEADER) r = add_pattern(&ColorHdrList, buf->data, 0, fg, bg, attr, err, 0, match); else if (object == MT_COLOR_BODY) r = add_pattern(&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0, match); else if (object == MT_COLOR_ATTACH_HEADERS) r = add_pattern(&ColorAttachList, buf->data, 1, fg, bg, attr, err, 0, match); else if ((object == MT_COLOR_STATUS) && MoreArgs(s)) { /* 'color status fg bg' can have up to 2 arguments: * 0 arguments: sets the default status color (handled below by else part) * 1 argument : colorize pattern on match * 2 arguments: colorize nth submatch of pattern */ mutt_extract_token(buf, s, 0); if (MoreArgs(s)) { struct Buffer temporary; memset(&temporary, 0, sizeof(struct Buffer)); mutt_extract_token(&temporary, s, 0); match = atoi(temporary.data); FREE(&temporary.data); } if (MoreArgs(s)) { mutt_str_strfcpy(err->data, _("too many arguments"), err->dsize); return -1; } r = add_pattern(&ColorStatusList, buf->data, 1, fg, bg, attr, err, 0, match); } else if (object == MT_COLOR_INDEX) { r = add_pattern(&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1, match); mutt_set_menu_redraw_full(MENU_MAIN); } else if (object == MT_COLOR_INDEX_AUTHOR) { r = add_pattern(&ColorIndexAuthorList, buf->data, 1, fg, bg, attr, err, 1, match); mutt_set_menu_redraw_full(MENU_MAIN); } else if (object == MT_COLOR_INDEX_FLAGS) { r = add_pattern(&ColorIndexFlagsList, buf->data, 1, fg, bg, attr, err, 1, match); mutt_set_menu_redraw_full(MENU_MAIN); } else if (object == MT_COLOR_INDEX_SUBJECT) { r = add_pattern(&ColorIndexSubjectList, buf->data, 1, fg, bg, attr, err, 1, match); mutt_set_menu_redraw_full(MENU_MAIN); } else if (object == MT_COLOR_INDEX_TAG) { r = add_pattern(&ColorIndexTagList, buf->data, 1, fg, bg, attr, err, 1, match); mutt_set_menu_redraw_full(MENU_MAIN); } else if (object == MT_COLOR_QUOTED) { if (q_level >= ColorQuoteSize) { mutt_mem_realloc(&ColorQuote, (ColorQuoteSize += 2) * sizeof(int)); ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED]; ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED]; } if (q_level >= ColorQuoteUsed) ColorQuoteUsed = q_level + 1; if (q_level == 0) { ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color(fg, bg, attr); ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED]; for (q_level = 1; q_level < ColorQuoteUsed; q_level++) { if (ColorQuote[q_level] == A_NORMAL) ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED]; } } else ColorQuote[q_level] = fgbgattr_to_color(fg, bg, attr); } else { ColorDefs[object] = fgbgattr_to_color(fg, bg, attr); if (object > MT_COLOR_INDEX_AUTHOR) mutt_set_menu_redraw_full(MENU_MAIN); } return r; } #ifdef HAVE_COLOR int mutt_parse_color(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err) { bool dry_run = false; if (option(OPT_NO_CURSES) || !has_colors()) dry_run = true; return parse_color(buf, s, err, parse_color_pair, dry_run); } #endif int mutt_parse_mono(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err) { bool dry_run = false; #ifdef HAVE_COLOR if (option(OPT_NO_CURSES) || has_colors()) dry_run = true; #else if (option(OPT_NO_CURSES)) dry_run = true; #endif return parse_color(buf, s, err, parse_attr_spec, dry_run); } neomutt-neomutt-20171215/commands.c000066400000000000000000000672501321473123000171150ustar00rootroot00000000000000/** * @file * Manage where the email is piped to external commands * * @authors * Copyright (C) 1996-2000 Michael R. Elkins * Copyright (C) 2000-2004,2006 Thomas Roessler * * @copyright * 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, see . */ #include "config.h" #include #include #ifdef ENABLE_NLS #include #endif #include #include #include #include #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "address.h" #include "alias.h" #include "body.h" #include "buffy.h" #include "context.h" #include "copy.h" #include "envelope.h" #include "filter.h" #include "format_flags.h" #include "globals.h" #include "header.h" #include "keymap.h" #include "mailbox.h" #include "mime.h" #include "mutt_curses.h" #include "mutt_idna.h" #include "mutt_menu.h" #include "mx.h" #include "ncrypt/ncrypt.h" #include "options.h" #include "pager.h" #include "parameter.h" #include "protos.h" #include "sort.h" #ifdef USE_IMAP #include "imap/imap.h" #endif #ifdef USE_NOTMUCH #include "mutt_notmuch.h" #endif static const char *ExtPagerProgress = "all"; /** The folder the user last saved to. Used by ci_save_message() */ static char LastSaveFolder[_POSIX_PATH_MAX] = ""; int mutt_display_message(struct Header *cur) { char tempfile[_POSIX_PATH_MAX], buf[LONG_STRING]; int rc = 0; bool builtin = false; int cmflags = MUTT_CM_DECODE | MUTT_CM_DISPLAY | MUTT_CM_CHARCONV; int chflags; FILE *fpout = NULL; FILE *fpfilterout = NULL; pid_t filterpid = -1; int res; snprintf(buf, sizeof(buf), "%s/%s", TYPE(cur->content), cur->content->subtype); mutt_parse_mime_message(Context, cur); mutt_message_hook(Context, cur, MUTT_MESSAGEHOOK); /* see if crypto is needed for this message. if so, we should exit curses */ if (WithCrypto && cur->security) { if (cur->security & ENCRYPT) { if (cur->security & APPLICATION_SMIME) crypt_smime_getkeys(cur->env); if (!crypt_valid_passphrase(cur->security)) return 0; cmflags |= MUTT_CM_VERIFY; } else if (cur->security & SIGN) { /* find out whether or not the verify signature */ if (query_quadoption(OPT_CRYPT_VERIFY_SIG, _("Verify PGP signature?")) == MUTT_YES) { cmflags |= MUTT_CM_VERIFY; } } } if (cmflags & MUTT_CM_VERIFY || cur->security & ENCRYPT) { if (cur->security & APPLICATION_PGP) { if (cur->env->from) crypt_pgp_invoke_getkeys(cur->env->from); crypt_invoke_message(APPLICATION_PGP); } if (cur->security & APPLICATION_SMIME) crypt_invoke_message(APPLICATION_SMIME); } mutt_mktemp(tempfile, sizeof(tempfile)); fpout = mutt_file_fopen(tempfile, "w"); if (!fpout) { mutt_error(_("Could not create temporary file!")); return 0; } if (DisplayFilter && *DisplayFilter) { fpfilterout = fpout; fpout = NULL; filterpid = mutt_create_filter_fd(DisplayFilter, &fpout, NULL, NULL, -1, fileno(fpfilterout), -1); if (filterpid < 0) { mutt_error(_("Cannot create display filter")); mutt_file_fclose(&fpfilterout); unlink(tempfile); return 0; } } if (!Pager || (mutt_str_strcmp(Pager, "builtin") == 0)) builtin = true; else { struct HdrFormatInfo hfi; hfi.ctx = Context; hfi.pager_progress = ExtPagerProgress; hfi.hdr = cur; mutt_make_string_info(buf, sizeof(buf), MuttIndexWindow->cols, NONULL(PagerFormat), &hfi, MUTT_FORMAT_MAKEPRINT); fputs(buf, fpout); fputs("\n\n", fpout); } chflags = (option(OPT_WEED) ? (CH_WEED | CH_REORDER) : 0) | CH_DECODE | CH_FROM | CH_DISPLAY; #ifdef USE_NOTMUCH if (Context->magic == MUTT_NOTMUCH) chflags |= CH_VIRTUAL; #endif res = mutt_copy_message_ctx(fpout, Context, cur, cmflags, chflags); if ((mutt_file_fclose(&fpout) != 0 && errno != EPIPE) || res < 0) { mutt_error(_("Could not copy message")); if (fpfilterout) { mutt_wait_filter(filterpid); mutt_file_fclose(&fpfilterout); } mutt_file_unlink(tempfile); return 0; } if (fpfilterout != NULL && mutt_wait_filter(filterpid) != 0) mutt_any_key_to_continue(NULL); mutt_file_fclose(&fpfilterout); /* XXX - check result? */ if (WithCrypto) { /* update crypto information for this message */ cur->security &= ~(GOODSIGN | BADSIGN); cur->security |= crypt_query(cur->content); /* Remove color cache for this message, in case there are color patterns for both ~g and ~V */ cur->pair = 0; } if (builtin) { struct Pager info; if (WithCrypto && (cur->security & APPLICATION_SMIME) && (cmflags & MUTT_CM_VERIFY)) { if (cur->security & GOODSIGN) { if (!crypt_smime_verify_sender(cur)) mutt_message(_("S/MIME signature successfully verified.")); else mutt_error(_("S/MIME certificate owner does not match sender.")); } else if (cur->security & PARTSIGN) mutt_message(_("Warning: Part of this message has not been signed.")); else if (cur->security & SIGN || cur->security & BADSIGN) mutt_error(_("S/MIME signature could NOT be verified.")); } if (WithCrypto && (cur->security & APPLICATION_PGP) && (cmflags & MUTT_CM_VERIFY)) { if (cur->security & GOODSIGN) mutt_message(_("PGP signature successfully verified.")); else if (cur->security & PARTSIGN) mutt_message(_("Warning: Part of this message has not been signed.")); else if (cur->security & SIGN) mutt_message(_("PGP signature could NOT be verified.")); } /* Invoke the builtin pager */ memset(&info, 0, sizeof(struct Pager)); info.hdr = cur; info.ctx = Context; rc = mutt_pager(NULL, tempfile, MUTT_PAGER_MESSAGE, &info); } else { int r; mutt_endwin(NULL); snprintf(buf, sizeof(buf), "%s %s", NONULL(Pager), tempfile); r = mutt_system(buf); if (r == -1) mutt_error(_("Error running \"%s\"!"), buf); unlink(tempfile); if (!option(OPT_NO_CURSES)) keypad(stdscr, true); if (r != -1) mutt_set_flag(Context, cur, MUTT_READ, 1); if (r != -1 && option(OPT_PROMPT_AFTER)) { mutt_unget_event(mutt_any_key_to_continue(_("Command: ")), 0); rc = km_dokey(MENU_PAGER); } else rc = 0; } return rc; } void ci_bounce_message(struct Header *h) { char prompt[SHORT_STRING]; char scratch[SHORT_STRING]; char buf[HUGE_STRING] = { 0 }; struct Address *adr = NULL; char *err = NULL; int rc; /* RFC5322 mandates a From: header, so warn before bouncing * messages without one */ if (h) { if (!h->env->from) { mutt_error(_("Warning: message contains no From: header")); mutt_sleep(2); } } else if (Context) { for (rc = 0; rc < Context->msgcount; rc++) { if (message_is_tagged(Context, rc) && !Context->hdrs[rc]->env->from) { mutt_error(_("Warning: message contains no From: header")); mutt_sleep(2); break; } } } if (h) mutt_str_strfcpy(prompt, _("Bounce message to: "), sizeof(prompt)); else mutt_str_strfcpy(prompt, _("Bounce tagged messages to: "), sizeof(prompt)); rc = mutt_get_field(prompt, buf, sizeof(buf), MUTT_ALIAS); if (rc || !buf[0]) return; adr = mutt_addr_parse_list2(adr, buf); if (!adr) { mutt_error(_("Error parsing address!")); return; } adr = mutt_expand_aliases(adr); if (mutt_addrlist_to_intl(adr, &err) < 0) { mutt_error(_("Bad IDN: '%s'"), err); FREE(&err); mutt_addr_free(&adr); return; } buf[0] = '\0'; rfc822_write_address(buf, sizeof(buf), adr, 1); #define EXTRA_SPACE (15 + 7 + 2) snprintf(scratch, sizeof(scratch), (h ? _("Bounce message to %s") : _("Bounce messages to %s")), buf); if (mutt_strwidth(prompt) > MuttMessageWindow->cols - EXTRA_SPACE) { mutt_simple_format(prompt, sizeof(prompt), 0, MuttMessageWindow->cols - EXTRA_SPACE, FMT_LEFT, 0, scratch, sizeof(scratch), 0); mutt_str_strcat(prompt, sizeof(prompt), "...?"); } else snprintf(prompt, sizeof(prompt), "%s?", scratch); if (query_quadoption(OPT_BOUNCE, prompt) != MUTT_YES) { mutt_addr_free(&adr); mutt_window_clearline(MuttMessageWindow, 0); mutt_message(h ? _("Message not bounced.") : _("Messages not bounced.")); return; } mutt_window_clearline(MuttMessageWindow, 0); rc = mutt_bounce_message(NULL, h, adr); mutt_addr_free(&adr); /* If no error, or background, display message. */ if ((rc == 0) || (rc == S_BKG)) mutt_message(h ? _("Message bounced.") : _("Messages bounced.")); } static void pipe_set_flags(int decode, int print, int *cmflags, int *chflags) { if (decode) { *cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV; *chflags |= CH_DECODE | CH_REORDER; if (option(OPT_WEED)) { *chflags |= CH_WEED; *cmflags |= MUTT_CM_WEED; } } if (print) *cmflags |= MUTT_CM_PRINTING; } static void pipe_msg(struct Header *h, FILE *fp, int decode, int print) { int cmflags = 0; int chflags = CH_FROM; pipe_set_flags(decode, print, &cmflags, &chflags); if (WithCrypto && decode && h->security & ENCRYPT) { if (!crypt_valid_passphrase(h->security)) return; endwin(); } if (decode) mutt_parse_mime_message(Context, h); mutt_copy_message_ctx(fp, Context, h, cmflags, chflags); } /** * pipe_message - Pipe message to a command * * The following code is shared between printing and piping. */ static int pipe_message(struct Header *h, char *cmd, int decode, int print, int split, char *sep) { int rc = 0; pid_t thepid; FILE *fpout = NULL; if (h) { mutt_message_hook(Context, h, MUTT_MESSAGEHOOK); if (WithCrypto && decode) { mutt_parse_mime_message(Context, h); if (h->security & ENCRYPT && !crypt_valid_passphrase(h->security)) return 1; } mutt_endwin(NULL); thepid = mutt_create_filter(cmd, &fpout, NULL, NULL); if (thepid < 0) { mutt_perror(_("Can't create filter process")); return 1; } set_option(OPT_KEEP_QUIET); pipe_msg(h, fpout, decode, print); mutt_file_fclose(&fpout); rc = mutt_wait_filter(thepid); unset_option(OPT_KEEP_QUIET); } else { /* handle tagged messages */ if (WithCrypto && decode) { for (int i = 0; i < Context->msgcount; i++) { if (!message_is_tagged(Context, i)) continue; mutt_message_hook(Context, Context->hdrs[i], MUTT_MESSAGEHOOK); mutt_parse_mime_message(Context, Context->hdrs[i]); if (Context->hdrs[i]->security & ENCRYPT && !crypt_valid_passphrase(Context->hdrs[i]->security)) { return 1; } } } if (split) { for (int i = 0; i < Context->msgcount; i++) { if (!message_is_tagged(Context, i)) continue; mutt_message_hook(Context, Context->hdrs[i], MUTT_MESSAGEHOOK); mutt_endwin(NULL); thepid = mutt_create_filter(cmd, &fpout, NULL, NULL); if (thepid < 0) { mutt_perror(_("Can't create filter process")); return 1; } set_option(OPT_KEEP_QUIET); pipe_msg(Context->hdrs[i], fpout, decode, print); /* add the message separator */ if (sep) fputs(sep, fpout); mutt_file_fclose(&fpout); if (mutt_wait_filter(thepid) != 0) rc = 1; unset_option(OPT_KEEP_QUIET); } } else { mutt_endwin(NULL); thepid = mutt_create_filter(cmd, &fpout, NULL, NULL); if (thepid < 0) { mutt_perror(_("Can't create filter process")); return 1; } set_option(OPT_KEEP_QUIET); for (int i = 0; i < Context->msgcount; i++) { if (!message_is_tagged(Context, i)) continue; mutt_message_hook(Context, Context->hdrs[i], MUTT_MESSAGEHOOK); pipe_msg(Context->hdrs[i], fpout, decode, print); /* add the message separator */ if (sep) fputs(sep, fpout); } mutt_file_fclose(&fpout); if (mutt_wait_filter(thepid) != 0) rc = 1; unset_option(OPT_KEEP_QUIET); } } if (rc || option(OPT_WAIT_KEY)) mutt_any_key_to_continue(NULL); return rc; } void mutt_pipe_message(struct Header *h) { char buffer[LONG_STRING]; buffer[0] = '\0'; if (mutt_get_field(_("Pipe to command: "), buffer, sizeof(buffer), MUTT_CMD) != 0 || !buffer[0]) { return; } mutt_expand_path(buffer, sizeof(buffer)); pipe_message(h, buffer, option(OPT_PIPE_DECODE), 0, option(OPT_PIPE_SPLIT), PipeSep); } void mutt_print_message(struct Header *h) { if (quadoption(OPT_PRINT) && (!PrintCommand || !*PrintCommand)) { mutt_message(_("No printing command has been defined.")); return; } if (query_quadoption(OPT_PRINT, h ? _("Print message?") : _("Print tagged messages?")) != MUTT_YES) return; if (pipe_message(h, PrintCommand, option(OPT_PRINT_DECODE), 1, option(OPT_PRINT_SPLIT), "\f") == 0) mutt_message(h ? _("Message printed") : _("Messages printed")); else mutt_message(h ? _("Message could not be printed") : _("Messages could not be printed")); } int mutt_select_sort(int reverse) { int method = Sort; /* save the current method in case of abort */ switch (mutt_multi_choice(reverse ? /* L10N: The highlighted letters must match the "Sort" options */ _("Rev-Sort " "(d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/" "(u)nsort/si(z)e/s(c)ore/s(p)am/(l)abel?: ") : /* L10N: The highlighted letters must match the "Rev-Sort" options */ _("Sort " "(d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/" "(u)nsort/si(z)e/s(c)ore/s(p)am/(l)abel?: "), /* L10N: These must match the highlighted letters from "Sort" and "Rev-Sort" */ _("dfrsotuzcpl"))) { case -1: /* abort - don't resort */ return -1; case 1: /* (d)ate */ Sort = SORT_DATE; break; case 2: /* (f)rm */ Sort = SORT_FROM; break; case 3: /* (r)ecv */ Sort = SORT_RECEIVED; break; case 4: /* (s)ubj */ Sort = SORT_SUBJECT; break; case 5: /* t(o) */ Sort = SORT_TO; break; case 6: /* (t)hread */ Sort = SORT_THREADS; break; case 7: /* (u)nsort */ Sort = SORT_ORDER; break; case 8: /* si(z)e */ Sort = SORT_SIZE; break; case 9: /* s(c)ore */ Sort = SORT_SCORE; break; case 10: /* s(p)am */ Sort = SORT_SPAM; break; case 11: /* (l)abel */ Sort = SORT_LABEL; break; } if (reverse) Sort |= SORT_REVERSE; return (Sort != method ? 0 : -1); /* no need to resort if it's the same */ } /** * mutt_shell_escape - invoke a command in a subshell */ void mutt_shell_escape(void) { char buf[LONG_STRING]; buf[0] = '\0'; if (mutt_get_field(_("Shell command: "), buf, sizeof(buf), MUTT_CMD) == 0) { if (!buf[0] && Shell) mutt_str_strfcpy(buf, Shell, sizeof(buf)); if (buf[0]) { mutt_window_clearline(MuttMessageWindow, 0); mutt_endwin(NULL); fflush(stdout); int rc = mutt_system(buf); if (rc == -1) mutt_debug(1, "Error running \"%s\"!", buf); if ((rc != 0) || option(OPT_WAIT_KEY)) mutt_any_key_to_continue(NULL); mutt_buffy_check(true); } } } /** * mutt_enter_command - enter a neomutt command */ void mutt_enter_command(void) { struct Buffer err, token; char buffer[LONG_STRING]; int r; buffer[0] = '\0'; if (mutt_get_field(":", buffer, sizeof(buffer), MUTT_COMMAND) != 0 || !buffer[0]) return; mutt_buffer_init(&err); err.dsize = STRING; err.data = mutt_mem_malloc(err.dsize); mutt_buffer_init(&token); r = mutt_parse_rc_line(buffer, &token, &err); FREE(&token.data); if (err.data[0]) { /* since errbuf could potentially contain printf() sequences in it, we must call mutt_error() in this fashion so that vsprintf() doesn't expect more arguments that we passed */ if (r == 0) mutt_message("%s", err.data); else mutt_error("%s", err.data); } FREE(&err.data); } void mutt_display_address(struct Envelope *env) { char *pfx = NULL; char buf[SHORT_STRING]; struct Address *adr = NULL; adr = mutt_get_address(env, &pfx); if (!adr) return; /* * Note: We don't convert IDNA to local representation this time. * That is intentional, so the user has an opportunity to copy & * paste the on-the-wire form of the address to other, IDN-unable * software. */ buf[0] = '\0'; rfc822_write_address(buf, sizeof(buf), adr, 0); mutt_message("%s: %s", pfx, buf); } static void set_copy_flags(struct Header *hdr, int decode, int decrypt, int *cmflags, int *chflags) { *cmflags = 0; *chflags = CH_UPDATE_LEN; if (WithCrypto && !decode && decrypt && (hdr->security & ENCRYPT)) { if ((WithCrypto & APPLICATION_PGP) && mutt_is_multipart_encrypted(hdr->content)) { *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME; *cmflags = MUTT_CM_DECODE_PGP; } else if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(hdr->content) & ENCRYPT) decode = 1; else if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(hdr->content) & ENCRYPT) { *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME; *cmflags = MUTT_CM_DECODE_SMIME; } } if (decode) { *chflags = CH_XMIT | CH_MIME | CH_TXTPLAIN; *cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV; if (!decrypt) /* If decode doesn't kick in for decrypt, */ { *chflags |= CH_DECODE; /* then decode RFC2047 headers, */ if (option(OPT_WEED)) { *chflags |= CH_WEED; /* and respect $weed. */ *cmflags |= MUTT_CM_WEED; } } } } int mutt_save_message_ctx(struct Header *h, int delete, int decode, int decrypt, struct Context *ctx) { int cmflags, chflags; int rc; set_copy_flags(h, decode, decrypt, &cmflags, &chflags); if (decode || decrypt) mutt_parse_mime_message(Context, h); rc = mutt_append_message(ctx, Context, h, cmflags, chflags); if (rc != 0) return rc; if (delete) { mutt_set_flag(Context, h, MUTT_DELETE, 1); mutt_set_flag(Context, h, MUTT_PURGE, 1); if (option(OPT_DELETE_UNTAG)) mutt_set_flag(Context, h, MUTT_TAG, 0); } return 0; } /** * mutt_save_message - Save an email * @retval 0 if the copy/save was successful * @retval -1 on error/abort */ int mutt_save_message(struct Header *h, int delete, int decode, int decrypt) { int need_buffy_cleanup; int need_passphrase = 0, app = 0; char prompt[SHORT_STRING], buf[_POSIX_PATH_MAX]; struct Context ctx; struct stat st; snprintf(prompt, sizeof(prompt), decode ? (delete ? _("Decode-save%s to mailbox") : _("Decode-copy%s to mailbox")) : (decrypt ? (delete ? _("Decrypt-save%s to mailbox") : _("Decrypt-copy%s to mailbox")) : (delete ? _("Save%s to mailbox") : _("Copy%s to mailbox"))), h ? "" : _(" tagged")); if (h) { if (WithCrypto) { need_passphrase = h->security & ENCRYPT; app = h->security; } mutt_message_hook(Context, h, MUTT_MESSAGEHOOK); mutt_default_save(buf, sizeof(buf), h); } else { /* look for the first tagged message */ for (int i = 0; i < Context->msgcount; i++) { if (message_is_tagged(Context, i)) { h = Context->hdrs[i]; break; } } if (h) { mutt_message_hook(Context, h, MUTT_MESSAGEHOOK); mutt_default_save(buf, sizeof(buf), h); if (WithCrypto) { need_passphrase = h->security & ENCRYPT; app = h->security; } h = NULL; } } mutt_pretty_mailbox(buf, sizeof(buf)); if (mutt_enter_fname(prompt, buf, sizeof(buf), 0) == -1) return -1; size_t pathlen = strlen(buf); if (pathlen == 0) return -1; /* Trim any trailing '/' */ if (buf[pathlen - 1] == '/') buf[pathlen - 1] = '\0'; /* This is an undocumented feature of ELM pointed out to me by Felix von * Leitner */ if (mutt_str_strcmp(buf, ".") == 0) mutt_str_strfcpy(buf, LastSaveFolder, sizeof(buf)); else mutt_str_strfcpy(LastSaveFolder, buf, sizeof(LastSaveFolder)); mutt_expand_path(buf, sizeof(buf)); /* check to make sure that this file is really the one the user wants */ if (mutt_save_confirm(buf, &st) != 0) return -1; if (WithCrypto && need_passphrase && (decode || decrypt) && !crypt_valid_passphrase(app)) return -1; mutt_message(_("Copying to %s..."), buf); #ifdef USE_IMAP if (Context->magic == MUTT_IMAP && !(decode || decrypt) && mx_is_imap(buf)) { switch (imap_copy_messages(Context, h, buf, delete)) { /* success */ case 0: mutt_clear_error(); return 0; /* non-fatal error: fall through to fetch/append */ case 1: break; /* fatal error, abort */ case -1: return -1; } } #endif if (mx_open_mailbox(buf, MUTT_APPEND, &ctx) != NULL) { #ifdef USE_COMPRESSED /* If we're saving to a compressed mailbox, the stats won't be updated * until the next open. Until then, improvise. */ struct Buffy *cm = NULL; if (ctx.compress_info) cm = mutt_find_mailbox(ctx.realpath); /* We probably haven't been opened yet */ if (cm && (cm->msg_count == 0)) cm = NULL; #endif if (h) { if (mutt_save_message_ctx(h, delete, decode, decrypt, &ctx) != 0) { mx_close_mailbox(&ctx, NULL); return -1; } #ifdef USE_COMPRESSED if (cm) { cm->msg_count++; if (!h->read) cm->msg_unread++; if (h->flagged) cm->msg_flagged++; } #endif } else { int rc = 0; #ifdef USE_NOTMUCH if (Context->magic == MUTT_NOTMUCH) nm_longrun_init(Context, true); #endif for (int i = 0; i < Context->msgcount; i++) { if (!message_is_tagged(Context, i)) continue; mutt_message_hook(Context, Context->hdrs[i], MUTT_MESSAGEHOOK); rc = mutt_save_message_ctx(Context->hdrs[i], delete, decode, decrypt, &ctx); if (rc != 0) break; #ifdef USE_COMPRESSED if (cm) { struct Header *h2 = Context->hdrs[i]; cm->msg_count++; if (!h2->read) cm->msg_unread++; if (h2->flagged) cm->msg_flagged++; } #endif } #ifdef USE_NOTMUCH if (Context->magic == MUTT_NOTMUCH) nm_longrun_done(Context); #endif if (rc != 0) { mx_close_mailbox(&ctx, NULL); return -1; } } need_buffy_cleanup = (ctx.magic == MUTT_MBOX || ctx.magic == MUTT_MMDF); mx_close_mailbox(&ctx, NULL); if (need_buffy_cleanup) mutt_buffy_cleanup(buf, &st); mutt_clear_error(); return 0; } return -1; } void mutt_version(void) { mutt_message("NeoMutt %s%s", PACKAGE_VERSION, GitVer); } /* * Returns: * 1 when a structural change is made. * recvattach requires this to know when to regenerate the actx. * 0 otherwise. */ int mutt_edit_content_type(struct Header *h, struct Body *b, FILE *fp) { char buf[LONG_STRING]; char obuf[LONG_STRING]; char tmp[STRING]; struct Parameter *p = NULL; char charset[STRING]; char *cp = NULL; short charset_changed = 0; short type_changed = 0; short structure_changed = 0; cp = mutt_param_get("charset", b->parameter); mutt_str_strfcpy(charset, NONULL(cp), sizeof(charset)); snprintf(buf, sizeof(buf), "%s/%s", TYPE(b), b->subtype); mutt_str_strfcpy(obuf, buf, sizeof(obuf)); if (b->parameter) { size_t l; for (p = b->parameter; p; p = p->next) { l = strlen(buf); mutt_addr_cat(tmp, sizeof(tmp), p->value, MimeSpecials); snprintf(buf + l, sizeof(buf) - l, "; %s=%s", p->attribute, tmp); } } if (mutt_get_field("Content-Type: ", buf, sizeof(buf), 0) != 0 || buf[0] == 0) return 0; /* clean up previous junk */ mutt_param_free(&b->parameter); FREE(&b->subtype); mutt_parse_content_type(buf, b); snprintf(tmp, sizeof(tmp), "%s/%s", TYPE(b), NONULL(b->subtype)); type_changed = mutt_str_strcasecmp(tmp, obuf); charset_changed = mutt_str_strcasecmp(charset, mutt_param_get("charset", b->parameter)); /* if in send mode, check for conversion - current setting is default. */ if (!h && b->type == TYPETEXT && charset_changed) { int r; snprintf(tmp, sizeof(tmp), _("Convert to %s upon sending?"), mutt_param_get("charset", b->parameter)); r = mutt_yesorno(tmp, !b->noconv); if (r != MUTT_ABORT) b->noconv = (r == MUTT_NO); } /* inform the user */ snprintf(tmp, sizeof(tmp), "%s/%s", TYPE(b), NONULL(b->subtype)); if (type_changed) mutt_message(_("Content-Type changed to %s."), tmp); if (b->type == TYPETEXT && charset_changed) { if (type_changed) mutt_sleep(1); mutt_message(_("Character set changed to %s; %s."), mutt_param_get("charset", b->parameter), b->noconv ? _("not converting") : _("converting")); } b->force_charset |= charset_changed ? 1 : 0; if (!is_multipart(b) && b->parts) { structure_changed = 1; mutt_free_body(&b->parts); } if (!mutt_is_message_type(b->type, b->subtype) && b->hdr) { structure_changed = 1; b->hdr->content = NULL; mutt_free_header(&b->hdr); } if (fp && !b->parts && (is_multipart(b) || mutt_is_message_type(b->type, b->subtype))) { structure_changed = 1; mutt_parse_part(fp, b); } if (WithCrypto && h) { if (h->content == b) h->security = 0; h->security |= crypt_query(b); } return structure_changed; } static int check_traditional_pgp(struct Header *h, int *redraw) { struct Message *msg = NULL; int rc = 0; h->security |= PGP_TRADITIONAL_CHECKED; mutt_parse_mime_message(Context, h); msg = mx_open_message(Context, h->msgno); if (!msg) return 0; if (crypt_pgp_check_traditional(msg->fp, h->content, 0)) { h->security = crypt_query(h->content); *redraw |= REDRAW_FULL; rc = 1; } h->security |= PGP_TRADITIONAL_CHECKED; mx_close_message(Context, &msg); return rc; } int mutt_check_traditional_pgp(struct Header *h, int *redraw) { int rc = 0; if (h && !(h->security & PGP_TRADITIONAL_CHECKED)) rc = check_traditional_pgp(h, redraw); else { for (int i = 0; i < Context->msgcount; i++) { if (message_is_tagged(Context, i) && !(Context->hdrs[i]->security & PGP_TRADITIONAL_CHECKED)) { rc = check_traditional_pgp(Context->hdrs[i], redraw) || rc; } } } return rc; } neomutt-neomutt-20171215/complete.c000066400000000000000000000160611321473123000171160ustar00rootroot00000000000000/** * @file * String auto-completion routines * * @authors * Copyright (C) 1996-2000,2007 Michael R. Elkins * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #include #include "mutt/mutt.h" #include "globals.h" #include "options.h" #include "protos.h" #ifdef USE_IMAP #include "imap/imap.h" #include "mailbox.h" #endif #ifdef USE_NNTP #include "nntp.h" #endif /** * mutt_complete - Attempt to complete a partial pathname * @param s Buffer containing pathname * @param slen Buffer length * @retval 0 if ok * @retval -1 if no matches * * Given a partial pathname, fill in as much of the rest of the path as is * unique. */ int mutt_complete(char *s, size_t slen) { char *p = NULL; DIR *dirp = NULL; struct dirent *de = NULL; int i, init = 0; size_t len; char dirpart[_POSIX_PATH_MAX], exp_dirpart[_POSIX_PATH_MAX]; char filepart[_POSIX_PATH_MAX]; #ifdef USE_IMAP char imap_path[LONG_STRING]; #endif mutt_debug(2, "completing %s\n", s); #ifdef USE_NNTP if (option(OPT_NEWS)) { struct NntpServer *nserv = CurrentNewsSrv; unsigned int n = 0; mutt_str_strfcpy(filepart, s, sizeof(filepart)); /* special case to handle when there is no filepart yet * find the first subscribed newsgroup */ len = mutt_str_strlen(filepart); if (len == 0) { for (; n < nserv->groups_num; n++) { struct NntpData *nntp_data = nserv->groups_list[n]; if (nntp_data && nntp_data->subscribed) { mutt_str_strfcpy(filepart, nntp_data->group, sizeof(filepart)); init = 1; n++; break; } } } for (; n < nserv->groups_num; n++) { struct NntpData *nntp_data = nserv->groups_list[n]; if (nntp_data && nntp_data->subscribed && (mutt_str_strncmp(nntp_data->group, filepart, len) == 0)) { if (init) { for (i = 0; filepart[i] && nntp_data->group[i]; i++) { if (filepart[i] != nntp_data->group[i]) { filepart[i] = '\0'; break; } } filepart[i] = '\0'; } else { mutt_str_strfcpy(filepart, nntp_data->group, sizeof(filepart)); init = 1; } } } mutt_str_strfcpy(s, filepart, slen); return (init ? 0 : -1); } #endif #ifdef USE_IMAP /* we can use '/' as a delimiter, imap_complete rewrites it */ if (*s == '=' || *s == '+' || *s == '!') { if (*s == '!') p = NONULL(SpoolFile); else p = NONULL(Folder); mutt_file_concat_path(imap_path, p, s + 1, sizeof(imap_path)); } else mutt_str_strfcpy(imap_path, s, sizeof(imap_path)); if (mx_is_imap(imap_path)) return imap_complete(s, slen, imap_path); #endif if (*s == '=' || *s == '+' || *s == '!') { dirpart[0] = *s; dirpart[1] = '\0'; if (*s == '!') mutt_str_strfcpy(exp_dirpart, NONULL(SpoolFile), sizeof(exp_dirpart)); else mutt_str_strfcpy(exp_dirpart, NONULL(Folder), sizeof(exp_dirpart)); if ((p = strrchr(s, '/'))) { char buf[_POSIX_PATH_MAX]; if (mutt_file_concatn_path(buf, sizeof(buf), exp_dirpart, strlen(exp_dirpart), s + 1, (size_t)(p - s - 1)) == NULL) { return -1; } mutt_str_strfcpy(exp_dirpart, buf, sizeof(exp_dirpart)); mutt_str_substr_cpy(dirpart, s, p + 1, sizeof(dirpart)); mutt_str_strfcpy(filepart, p + 1, sizeof(filepart)); } else mutt_str_strfcpy(filepart, s + 1, sizeof(filepart)); dirp = opendir(exp_dirpart); } else { if ((p = strrchr(s, '/'))) { if (p == s) /* absolute path */ { p = s + 1; mutt_str_strfcpy(dirpart, "/", sizeof(dirpart)); exp_dirpart[0] = '\0'; mutt_str_strfcpy(filepart, p, sizeof(filepart)); dirp = opendir(dirpart); } else { mutt_str_substr_cpy(dirpart, s, p, sizeof(dirpart)); mutt_str_strfcpy(filepart, p + 1, sizeof(filepart)); mutt_str_strfcpy(exp_dirpart, dirpart, sizeof(exp_dirpart)); mutt_expand_path(exp_dirpart, sizeof(exp_dirpart)); dirp = opendir(exp_dirpart); } } else { /* no directory name, so assume current directory. */ dirpart[0] = '\0'; mutt_str_strfcpy(filepart, s, sizeof(filepart)); dirp = opendir("."); } } if (!dirp) { mutt_debug(1, "%s: %s (errno %d).\n", exp_dirpart, strerror(errno), errno); return -1; } /* * special case to handle when there is no filepart yet. find the first * file/directory which is not ``.'' or ``..'' */ len = mutt_str_strlen(filepart); if (len == 0) { while ((de = readdir(dirp)) != NULL) { if ((mutt_str_strcmp(".", de->d_name) != 0) && (mutt_str_strcmp("..", de->d_name) != 0)) { mutt_str_strfcpy(filepart, de->d_name, sizeof(filepart)); init++; break; } } } while ((de = readdir(dirp)) != NULL) { if (mutt_str_strncmp(de->d_name, filepart, len) == 0) { if (init) { for (i = 0; filepart[i] && de->d_name[i]; i++) { if (filepart[i] != de->d_name[i]) { filepart[i] = '\0'; break; } } filepart[i] = '\0'; } else { char buf[_POSIX_PATH_MAX]; struct stat st; mutt_str_strfcpy(filepart, de->d_name, sizeof(filepart)); /* check to see if it is a directory */ if (dirpart[0]) { mutt_str_strfcpy(buf, exp_dirpart, sizeof(buf)); mutt_str_strfcpy(buf + strlen(buf), "/", sizeof(buf) - strlen(buf)); } else buf[0] = 0; mutt_str_strfcpy(buf + strlen(buf), filepart, sizeof(buf) - strlen(buf)); if (stat(buf, &st) != -1 && (st.st_mode & S_IFDIR)) mutt_str_strfcpy(filepart + strlen(filepart), "/", sizeof(filepart) - strlen(filepart)); init = 1; } } } closedir(dirp); if (dirpart[0]) { mutt_str_strfcpy(s, dirpart, slen); if ((mutt_str_strcmp("/", dirpart) != 0) && dirpart[0] != '=' && dirpart[0] != '+') mutt_str_strfcpy(s + strlen(s), "/", slen - strlen(s)); mutt_str_strfcpy(s + strlen(s), filepart, slen - strlen(s)); } else mutt_str_strfcpy(s, filepart, slen); return (init ? 0 : -1); } neomutt-neomutt-20171215/compose.c000066400000000000000000001410501321473123000167500ustar00rootroot00000000000000/** * @file * GUI editor for an email's headers * * @authors * Copyright (C) 1996-2000,2002,2007,2010,2012 Michael R. Elkins * Copyright (C) 2004 g10 Code GmbH * * @copyright * 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, see . */ #include "config.h" #include #ifdef ENABLE_NLS #include #endif #include #include #include #include #include #include #include "mutt/mutt.h" #include "conn/conn.h" #include "mutt.h" #include "address.h" #include "alias.h" #include "attach.h" #include "body.h" #include "content.h" #include "context.h" #include "envelope.h" #include "format_flags.h" #include "globals.h" #include "header.h" #include "keymap.h" #include "mailbox.h" #include "mime.h" #include "mutt_curses.h" #include "mutt_idna.h" #include "mutt_menu.h" #include "mx.h" #include "ncrypt/ncrypt.h" #include "opcodes.h" #include "options.h" #include "protos.h" #include "sort.h" #ifdef MIXMASTER #include "remailer.h" #endif #ifdef USE_NNTP #include "nntp.h" #endif struct Address; static const char *There_are_no_attachments = N_("There are no attachments."); #define CHECK_COUNT \ if (actx->idxlen == 0) \ { \ mutt_error(_(There_are_no_attachments)); \ break; \ } #define CURATTACH actx->idx[actx->v2r[menu->current]] /** * enum HeaderField - Ordered list of headers for the compose screen */ enum HeaderField { HDR_FROM = 0, HDR_TO, HDR_CC, HDR_BCC, HDR_SUBJECT, 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 */ }; int HeaderPadding[HDR_XCOMMENTTO + 1] = { 0 }; int MaxHeaderWidth = 0; #define HDR_XOFFSET MaxHeaderWidth #define W (MuttIndexWindow->cols - MaxHeaderWidth) static const char *const Prompts[] = { /* L10N: Compose menu field. May not want to translate. */ N_("From: "), /* L10N: Compose menu field. May not want to translate. */ N_("To: "), /* L10N: Compose menu field. May not want to translate. */ N_("Cc: "), /* L10N: Compose menu field. May not want to translate. */ N_("Bcc: "), /* L10N: Compose menu field. May not want to translate. */ N_("Subject: "), /* L10N: Compose menu field. May not want to translate. */ N_("Reply-To: "), /* L10N: Compose menu field. May not want to translate. */ N_("Fcc: "), #ifdef MIXMASTER /* L10N: "Mix" refers to the MixMaster chain for anonymous email */ N_("Mix: "), #endif /* L10N: Compose menu field. Holds "Encrypt", "Sign" related information */ N_("Security: "), /* L10N: * This string is used by the compose menu. * Since it is hidden by default, it does not increase the * indentation of other compose menu fields. However, if possible, * it should not be longer than the other compose menu fields. * * Since it shares the row with "Encrypt with:", it should not be longer * than 15-20 character cells. */ N_("Sign as: "), #ifdef USE_NNTP /* L10N: Compose menu field. May not want to translate. */ N_("Newsgroups: "), /* L10N: Compose menu field. May not want to translate. */ N_("Followup-To: "), /* L10N: Compose menu field. May not want to translate. */ N_("X-Comment-To: "), #endif }; static const struct Mapping ComposeHelp[] = { { N_("Send"), OP_COMPOSE_SEND_MESSAGE }, { N_("Abort"), OP_EXIT }, /* L10N: compose menu help line entry */ { N_("To"), OP_COMPOSE_EDIT_TO }, /* L10N: compose menu help line entry */ { N_("CC"), OP_COMPOSE_EDIT_CC }, /* L10N: compose menu help line entry */ { N_("Subj"), OP_COMPOSE_EDIT_SUBJECT }, { N_("Attach file"), OP_COMPOSE_ATTACH_FILE }, { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION }, { N_("Help"), OP_HELP }, { NULL, 0 }, }; #ifdef USE_NNTP static struct Mapping ComposeNewsHelp[] = { { N_("Send"), OP_COMPOSE_SEND_MESSAGE }, { N_("Abort"), OP_EXIT }, { N_("Newsgroups"), OP_COMPOSE_EDIT_NEWSGROUPS }, { N_("Subj"), OP_COMPOSE_EDIT_SUBJECT }, { N_("Attach file"), OP_COMPOSE_ATTACH_FILE }, { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION }, { N_("Help"), OP_HELP }, { NULL, 0 }, }; #endif static void calc_header_width_padding(int idx, const char *header, int calc_max) { int width; HeaderPadding[idx] = mutt_str_strlen(header); width = mutt_strwidth(header); if (calc_max && MaxHeaderWidth < width) MaxHeaderWidth = width; HeaderPadding[idx] -= width; } /** * init_header_padding - Calculate how much padding the compose table will need * * The padding needed for each header is strlen() + max_width - strwidth(). * * calc_header_width_padding sets each entry in HeaderPadding to strlen - * width. Then, afterwards, we go through and add max_width to each entry. */ static void init_header_padding(void) { static short done = 0; if (done) return; done = 1; for (int i = 0; i <= HDR_XCOMMENTTO; i++) calc_header_width_padding(i, _(Prompts[i]), 1); /* Don't include "Sign as: " in the MaxHeaderWidth calculation. It * doesn't show up by default, and so can make the indentation of * the other fields look funny. */ calc_header_width_padding(HDR_CRYPTINFO, _(Prompts[HDR_CRYPTINFO]), 0); for (int i = 0; i <= HDR_XCOMMENTTO; i++) { HeaderPadding[i] += MaxHeaderWidth; if (HeaderPadding[i] < 0) HeaderPadding[i] = 0; } } /** * snd_entry - Format a menu item for the attachment list * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] menu Menu containing aliases * @param[in] num Index into the menu */ static void snd_entry(char *buf, size_t buflen, struct Menu *menu, int num) { struct AttachCtx *actx = (struct AttachCtx *) menu->data; mutt_expando_format(buf, buflen, 0, MuttIndexWindow->cols, NONULL(AttachFormat), attach_format_str, (unsigned long) (actx->idx[actx->v2r[num]]), MUTT_FORMAT_STAT_FILE | MUTT_FORMAT_ARROWCURSOR); } static void redraw_crypt_lines(struct Header *msg) { SETCOLOR(MT_COLOR_COMPOSE_HEADER); mutt_window_mvprintw(MuttIndexWindow, HDR_CRYPT, 0, "%*s", HeaderPadding[HDR_CRYPT], _(Prompts[HDR_CRYPT])); NORMAL_COLOR; if ((WithCrypto & (APPLICATION_PGP | APPLICATION_SMIME)) == 0) { addstr(_("Not supported")); return; } if ((msg->security & (ENCRYPT | SIGN)) == (ENCRYPT | SIGN)) { SETCOLOR(MT_COLOR_COMPOSE_SECURITY_BOTH); addstr(_("Sign, Encrypt")); } else if (msg->security & ENCRYPT) { SETCOLOR(MT_COLOR_COMPOSE_SECURITY_ENCRYPT); addstr(_("Encrypt")); } else if (msg->security & SIGN) { SETCOLOR(MT_COLOR_COMPOSE_SECURITY_SIGN); addstr(_("Sign")); } else { /* L10N: This refers to the encryption of the email, e.g. "Security: None" */ SETCOLOR(MT_COLOR_COMPOSE_SECURITY_NONE); addstr(_("None")); } NORMAL_COLOR; if ((msg->security & (ENCRYPT | SIGN))) { if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP)) { if ((msg->security & INLINE)) addstr(_(" (inline PGP)")); else addstr(_(" (PGP/MIME)")); } else if ((WithCrypto & APPLICATION_SMIME) && (msg->security & APPLICATION_SMIME)) addstr(_(" (S/MIME)")); } if (option(OPT_CRYPT_OPPORTUNISTIC_ENCRYPT) && (msg->security & OPPENCRYPT)) addstr(_(" (OppEnc mode)")); mutt_window_clrtoeol(MuttIndexWindow); mutt_window_move(MuttIndexWindow, HDR_CRYPTINFO, 0); mutt_window_clrtoeol(MuttIndexWindow); if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP) && (msg->security & SIGN)) { SETCOLOR(MT_COLOR_COMPOSE_HEADER); printw("%*s", HeaderPadding[HDR_CRYPTINFO], _(Prompts[HDR_CRYPTINFO])); NORMAL_COLOR; printw("%s", PgpSignAs ? PgpSignAs : _("")); } if ((WithCrypto & APPLICATION_SMIME) && (msg->security & APPLICATION_SMIME) && (msg->security & SIGN)) { SETCOLOR(MT_COLOR_COMPOSE_HEADER); printw("%*s", HeaderPadding[HDR_CRYPTINFO], _(Prompts[HDR_CRYPTINFO])); NORMAL_COLOR; printw("%s", SmimeDefaultKey ? SmimeDefaultKey : _("")); } if ((WithCrypto & APPLICATION_SMIME) && (msg->security & APPLICATION_SMIME) && (msg->security & ENCRYPT) && SmimeEncryptWith && *SmimeEncryptWith) { SETCOLOR(MT_COLOR_COMPOSE_HEADER); mutt_window_mvprintw(MuttIndexWindow, HDR_CRYPTINFO, 40, "%s", _("Encrypt with: ")); NORMAL_COLOR; printw("%s", NONULL(SmimeEncryptWith)); } } #ifdef MIXMASTER static void redraw_mix_line(struct ListHead *chain) { char *t = NULL; SETCOLOR(MT_COLOR_COMPOSE_HEADER); mutt_window_mvprintw(MuttIndexWindow, HDR_MIX, 0, "%*s", HeaderPadding[HDR_MIX], _(Prompts[HDR_MIX])); NORMAL_COLOR; if (STAILQ_EMPTY(chain)) { addstr(_("")); mutt_window_clrtoeol(MuttIndexWindow); return; } int c = 12; struct ListNode *np; STAILQ_FOREACH(np, chain, entries) { t = np->data; if (t && t[0] == '0' && t[1] == '\0') t = ""; if (c + mutt_str_strlen(t) + 2 >= MuttIndexWindow->cols) break; addstr(NONULL(t)); if (STAILQ_NEXT(np, entries)) addstr(", "); c += mutt_str_strlen(t) + 2; } } #endif /* MIXMASTER */ static int check_attachments(struct AttachCtx *actx) { int r; struct stat st; char pretty[_POSIX_PATH_MAX], msg[_POSIX_PATH_MAX + SHORT_STRING]; for (int i = 0; i < actx->idxlen; i++) { mutt_str_strfcpy(pretty, actx->idx[i]->content->filename, sizeof(pretty)); if (stat(actx->idx[i]->content->filename, &st) != 0) { mutt_pretty_mailbox(pretty, sizeof(pretty)); mutt_error(_("%s [#%d] no longer exists!"), pretty, i + 1); return -1; } if (actx->idx[i]->content->stamp < st.st_mtime) { mutt_pretty_mailbox(pretty, sizeof(pretty)); snprintf(msg, sizeof(msg), _("%s [#%d] modified. Update encoding?"), pretty, i + 1); r = mutt_yesorno(msg, MUTT_YES); if (r == MUTT_YES) mutt_update_encoding(actx->idx[i]->content); else if (r == MUTT_ABORT) return -1; } } return 0; } static void draw_envelope_addr(int line, struct Address *addr) { char buf[LONG_STRING]; buf[0] = 0; rfc822_write_address(buf, sizeof(buf), addr, 1); SETCOLOR(MT_COLOR_COMPOSE_HEADER); mutt_window_mvprintw(MuttIndexWindow, line, 0, "%*s", HeaderPadding[line], _(Prompts[line])); NORMAL_COLOR; mutt_paddstr(W, buf); } static void draw_envelope(struct Header *msg, char *fcc) { draw_envelope_addr(HDR_FROM, msg->env->from); #ifdef USE_NNTP if (!option(OPT_NEWS_SEND)) { #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 { mutt_window_mvprintw(MuttIndexWindow, HDR_TO, 0, "%*s", HeaderPadding[HDR_NEWSGROUPS], Prompts[HDR_NEWSGROUPS]); mutt_paddstr(W, NONULL(msg->env->newsgroups)); mutt_window_mvprintw(MuttIndexWindow, HDR_CC, 0, "%*s", HeaderPadding[HDR_FOLLOWUPTO], Prompts[HDR_FOLLOWUPTO]); mutt_paddstr(W, NONULL(msg->env->followup_to)); if (option(OPT_X_COMMENT_TO)) { mutt_window_mvprintw(MuttIndexWindow, HDR_BCC, 0, "%*s", HeaderPadding[HDR_XCOMMENTTO], Prompts[HDR_XCOMMENTTO]); mutt_paddstr(W, NONULL(msg->env->x_comment_to)); } } #endif SETCOLOR(MT_COLOR_COMPOSE_HEADER); mutt_window_mvprintw(MuttIndexWindow, HDR_SUBJECT, 0, "%*s", HeaderPadding[HDR_SUBJECT], _(Prompts[HDR_SUBJECT])); NORMAL_COLOR; mutt_paddstr(W, NONULL(msg->env->subject)); draw_envelope_addr(HDR_REPLYTO, msg->env->reply_to); SETCOLOR(MT_COLOR_COMPOSE_HEADER); mutt_window_mvprintw(MuttIndexWindow, HDR_FCC, 0, "%*s", HeaderPadding[HDR_FCC], _(Prompts[HDR_FCC])); NORMAL_COLOR; mutt_paddstr(W, fcc); if (WithCrypto) redraw_crypt_lines(msg); #ifdef MIXMASTER redraw_mix_line(&msg->chain); #endif SETCOLOR(MT_COLOR_STATUS); mutt_window_mvaddstr(MuttIndexWindow, HDR_ATTACH - 1, 0, _("-- Attachments")); mutt_window_clrtoeol(MuttIndexWindow); NORMAL_COLOR; } static void edit_address_list(int line, struct Address **addr) { char buf[HUGE_STRING] = ""; /* needs to be large for alias expansion */ char *err = NULL; mutt_addrlist_to_local(*addr); rfc822_write_address(buf, sizeof(buf), *addr, 0); if (mutt_get_field(_(Prompts[line]), buf, sizeof(buf), MUTT_ALIAS) == 0) { mutt_addr_free(addr); *addr = mutt_addr_parse_list2(*addr, buf); *addr = mutt_expand_aliases(*addr); } if (mutt_addrlist_to_intl(*addr, &err) != 0) { mutt_error(_("Warning: '%s' is a bad IDN."), err); mutt_refresh(); FREE(&err); } /* redraw the expanded list so the user can see the result */ buf[0] = 0; rfc822_write_address(buf, sizeof(buf), *addr, 1); mutt_window_move(MuttIndexWindow, line, HDR_XOFFSET); mutt_paddstr(W, buf); } static int delete_attachment(struct AttachCtx *actx, int x) { struct AttachPtr **idx = actx->idx; int rindex = actx->v2r[x]; if (rindex == 0 && actx->idxlen == 1) { mutt_error(_("You may not delete the only attachment.")); idx[rindex]->content->tagged = false; return -1; } for (int y = 0; y < actx->idxlen; y++) { if (idx[y]->content->next == idx[rindex]->content) { idx[y]->content->next = idx[rindex]->content->next; break; } } idx[rindex]->content->next = NULL; idx[rindex]->content->parts = NULL; mutt_free_body(&(idx[rindex]->content)); FREE(&idx[rindex]->tree); FREE(&idx[rindex]); for (; rindex < actx->idxlen - 1; rindex++) idx[rindex] = idx[rindex + 1]; idx[actx->idxlen - 1] = NULL; actx->idxlen--; return 0; } static void mutt_gen_compose_attach_list(struct AttachCtx *actx, struct Body *m, int parent_type, int level) { struct AttachPtr *new = NULL; for (; m; m = m->next) { if (m->type == TYPEMULTIPART && m->parts && (!(WithCrypto & APPLICATION_PGP) || !mutt_is_multipart_encrypted(m))) { mutt_gen_compose_attach_list(actx, m->parts, m->type, level); } else { new = (struct AttachPtr *) mutt_mem_calloc(1, sizeof(struct AttachPtr)); mutt_actx_add_attach(actx, new); new->content = m; m->aptr = new; new->parent_type = parent_type; new->level = level; /* We don't support multipart messages in the compose menu yet */ } } } static void mutt_update_compose_menu(struct AttachCtx *actx, struct Menu *menu, int init) { if (init) { mutt_gen_compose_attach_list(actx, actx->hdr->content, -1, 0); mutt_attach_init(actx); menu->data = actx; } mutt_update_tree(actx); menu->max = actx->vcount; if (menu->max) { if (menu->current >= menu->max) menu->current = menu->max - 1; } else menu->current = 0; menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *new) { new->level = (actx->idxlen > 0) ? actx->idx[actx->idxlen - 1]->level : 0; if (actx->idxlen) actx->idx[actx->idxlen - 1]->content->next = new->content; new->content->aptr = new; mutt_actx_add_attach(actx, new); mutt_update_compose_menu(actx, menu, 0); menu->current = actx->vcount - 1; } /** * struct ComposeRedrawData - Keep track when the compose screen needs redrawing */ struct ComposeRedrawData { struct Header *msg; char *fcc; }; /* prototype for use below */ static void compose_status_line(char *buf, size_t buflen, size_t col, int cols, struct Menu *menu, const char *p); static void compose_menu_redraw(struct Menu *menu) { char buf[LONG_STRING]; struct ComposeRedrawData *rd = menu->redraw_data; if (!rd) return; if (menu->redraw & REDRAW_FULL) { menu_redraw_full(menu); draw_envelope(rd->msg, rd->fcc); menu->offset = HDR_ATTACH; menu->pagelen = MuttIndexWindow->rows - HDR_ATTACH; } menu_check_recenter(menu); if (menu->redraw & REDRAW_STATUS) { compose_status_line(buf, sizeof(buf), 0, MuttStatusWindow->cols, menu, NONULL(ComposeFormat)); mutt_window_move(MuttStatusWindow, 0, 0); SETCOLOR(MT_COLOR_STATUS); mutt_paddstr(MuttStatusWindow->cols, buf); NORMAL_COLOR; menu->redraw &= ~REDRAW_STATUS; } #ifdef USE_SIDEBAR if (menu->redraw & REDRAW_SIDEBAR) menu_redraw_sidebar(menu); #endif if (menu->redraw & REDRAW_INDEX) menu_redraw_index(menu); else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH)) menu_redraw_motion(menu); else if (menu->redraw == REDRAW_CURRENT) menu_redraw_current(menu); } /** * cum_attachs_size - Cumulative Attachments Size * @param menu Menu listing attachments * @retval n Number of bytes in attachments * * Returns the total number of bytes used by the attachments in the attachment * list _after_ content-transfer-encodings have been applied. */ static unsigned long cum_attachs_size(struct Menu *menu) { size_t s = 0; struct AttachCtx *actx = menu->data; struct AttachPtr **idx = actx->idx; struct Content *info = NULL; struct Body *b = NULL; for (unsigned short i = 0; i < actx->idxlen; i++) { b = idx[i]->content; if (!b->content) b->content = mutt_get_content_info(b->filename, b); if ((info = b->content)) { switch (b->encoding) { case ENCQUOTEDPRINTABLE: s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf; break; case ENCBASE64: s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3; break; default: s += info->lobin + info->hibin + info->ascii + info->crlf; break; } } } return s; } /** * compose_format_str - Create the status bar string for compose mode * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] col Starting column * @param[in] cols Number of screen columns * @param[in] op printf-like operator, e.g. 't' * @param[in] src printf-like format string * @param[in] prec Field precision, e.g. "-3.4" * @param[in] if_str If condition is met, display this string * @param[in] else_str Otherwise, display this string * @param[in] data Pointer to the mailbox Context * @param[in] flags Format flags * @retval src (unchanged) * * compose_format_str() is a callback function for mutt_expando_format(). * * | Expando | Description * |:--------|:-------------------------------------------------------- * | \%a | Total number of attachments * | \%h | Local hostname * | \%l | Approximate size (in bytes) of the current message * | \%v | NeoMutt version string */ static const char *compose_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, enum FormatFlag flags) { char fmt[SHORT_STRING], tmp[SHORT_STRING]; int optional = (flags & MUTT_FORMAT_OPTIONAL); struct Menu *menu = (struct Menu *) data; *buf = 0; switch (op) { case 'a': /* total number of attachments */ snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, menu->max); break; case 'h': /* hostname */ snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, NONULL(ShortHostname)); break; case 'l': /* approx length of current message in bytes */ snprintf(fmt, sizeof(fmt), "%%%ss", prec); mutt_pretty_size(tmp, sizeof(tmp), menu ? cum_attachs_size(menu) : 0); snprintf(buf, buflen, fmt, tmp); break; case 'v': snprintf(buf, buflen, "NeoMutt %s%s", PACKAGE_VERSION, GitVer); break; case 0: *buf = 0; return src; default: snprintf(buf, buflen, "%%%s%c", prec, op); break; } if (optional) compose_status_line(buf, buflen, col, cols, menu, if_str); else if (flags & MUTT_FORMAT_OPTIONAL) compose_status_line(buf, buflen, col, cols, menu, else_str); return src; } static void compose_status_line(char *buf, size_t buflen, size_t col, int cols, struct Menu *menu, const char *p) { mutt_expando_format(buf, buflen, col, cols, p, compose_format_str, (unsigned long) menu, 0); } /** * mutt_compose_menu - Allow the user to edit the message envelope * @retval 1 Message should be postponed * @retval 0 Normal exit * @retval -1 Abort message */ int mutt_compose_menu(struct Header *msg, /* structure for new message */ char *fcc, /* where to save a copy of the message */ size_t fcclen, struct Header *cur, /* current message */ int flags) { char helpstr[LONG_STRING]; char buf[LONG_STRING]; char fname[_POSIX_PATH_MAX]; struct Menu *menu = NULL; struct AttachCtx *actx = NULL; struct AttachPtr *new = NULL; int i, close = 0; int r = -1; /* return value */ int op = 0; int loop = 1; int fcc_set = 0; /* has the user edited the Fcc: field ? */ struct Context *ctx = NULL, *this = NULL; /* Sort, SortAux could be changed in mutt_index_menu() */ int old_sort, old_sort_aux; struct stat st; struct ComposeRedrawData rd; #ifdef USE_NNTP int news = 0; /* is it a news article ? */ if (option(OPT_NEWS_SEND)) news++; #endif init_header_padding(); rd.msg = msg; rd.fcc = fcc; menu = mutt_new_menu(MENU_COMPOSE); menu->offset = HDR_ATTACH; menu->make_entry = snd_entry; menu->tag = mutt_tag_attach; #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); menu->custom_menu_redraw = compose_menu_redraw; menu->redraw_data = &rd; mutt_push_current_menu(menu); actx = mutt_mem_calloc(sizeof(struct AttachCtx), 1); actx->hdr = msg; mutt_update_compose_menu(actx, menu, 1); while (loop) { #ifdef USE_NNTP unset_option(OPT_NEWS); /* for any case */ #endif switch (op = mutt_menu_loop(menu)) { case OP_COMPOSE_EDIT_FROM: edit_address_list(HDR_FROM, &msg->env->from); mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_EDIT_TO: #ifdef USE_NNTP if (news) break; #endif edit_address_list(HDR_TO, &msg->env->to); if (option(OPT_CRYPT_OPPORTUNISTIC_ENCRYPT)) { crypt_opportunistic_encrypt(msg); redraw_crypt_lines(msg); } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_EDIT_BCC: #ifdef USE_NNTP if (news) break; #endif edit_address_list(HDR_BCC, &msg->env->bcc); if (option(OPT_CRYPT_OPPORTUNISTIC_ENCRYPT)) { crypt_opportunistic_encrypt(msg); redraw_crypt_lines(msg); } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_EDIT_CC: #ifdef USE_NNTP if (news) break; #endif edit_address_list(HDR_CC, &msg->env->cc); if (option(OPT_CRYPT_OPPORTUNISTIC_ENCRYPT)) { crypt_opportunistic_encrypt(msg); redraw_crypt_lines(msg); } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; #ifdef USE_NNTP case OP_COMPOSE_EDIT_NEWSGROUPS: if (news) { if (msg->env->newsgroups) mutt_str_strfcpy(buf, msg->env->newsgroups, sizeof(buf)); else buf[0] = 0; if (mutt_get_field("Newsgroups: ", buf, sizeof(buf), 0) == 0) { mutt_str_replace(&msg->env->newsgroups, buf); mutt_window_move(MuttIndexWindow, HDR_TO, HDR_XOFFSET); if (msg->env->newsgroups) mutt_paddstr(W, msg->env->newsgroups); else clrtoeol(); } } break; case OP_COMPOSE_EDIT_FOLLOWUP_TO: if (news) { if (msg->env->followup_to) mutt_str_strfcpy(buf, msg->env->followup_to, sizeof(buf)); else buf[0] = 0; if (mutt_get_field("Followup-To: ", buf, sizeof(buf), 0) == 0) { mutt_str_replace(&msg->env->followup_to, buf); mutt_window_move(MuttIndexWindow, HDR_CC, HDR_XOFFSET); if (msg->env->followup_to) mutt_paddstr(W, msg->env->followup_to); else clrtoeol(); } } break; case OP_COMPOSE_EDIT_X_COMMENT_TO: if (news && option(OPT_X_COMMENT_TO)) { if (msg->env->x_comment_to) mutt_str_strfcpy(buf, msg->env->x_comment_to, sizeof(buf)); else buf[0] = 0; if (mutt_get_field("X-Comment-To: ", buf, sizeof(buf), 0) == 0) { mutt_str_replace(&msg->env->x_comment_to, buf); mutt_window_move(MuttIndexWindow, HDR_BCC, HDR_XOFFSET); if (msg->env->x_comment_to) mutt_paddstr(W, msg->env->x_comment_to); else clrtoeol(); } } break; #endif case OP_COMPOSE_EDIT_SUBJECT: if (msg->env->subject) mutt_str_strfcpy(buf, msg->env->subject, sizeof(buf)); else buf[0] = 0; if (mutt_get_field(_("Subject: "), buf, sizeof(buf), 0) == 0) { mutt_str_replace(&msg->env->subject, buf); mutt_window_move(MuttIndexWindow, HDR_SUBJECT, HDR_XOFFSET); if (msg->env->subject) mutt_paddstr(W, msg->env->subject); else mutt_window_clrtoeol(MuttIndexWindow); } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_EDIT_REPLY_TO: edit_address_list(HDR_REPLYTO, &msg->env->reply_to); mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_EDIT_FCC: mutt_str_strfcpy(buf, fcc, sizeof(buf)); if (mutt_get_field(_("Fcc: "), buf, sizeof(buf), MUTT_FILE | MUTT_CLEAR) == 0) { mutt_str_strfcpy(fcc, buf, fcclen); mutt_pretty_mailbox(fcc, fcclen); mutt_window_move(MuttIndexWindow, HDR_FCC, HDR_XOFFSET); mutt_paddstr(W, fcc); fcc_set = 1; } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_EDIT_MESSAGE: if (Editor && (mutt_str_strcmp("builtin", Editor) != 0) && !option(OPT_EDIT_HEADERS)) { mutt_edit_file(Editor, msg->content->filename); mutt_update_encoding(msg->content); menu->redraw = REDRAW_FULL; mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; } /* fall through */ case OP_COMPOSE_EDIT_HEADERS: if ((mutt_str_strcmp("builtin", Editor) != 0) && (op == OP_COMPOSE_EDIT_HEADERS || (op == OP_COMPOSE_EDIT_MESSAGE && option(OPT_EDIT_HEADERS)))) { char *tag = NULL, *err = NULL; mutt_env_to_local(msg->env); mutt_edit_headers(NONULL(Editor), msg->content->filename, msg, fcc, fcclen); if (mutt_env_to_intl(msg->env, &tag, &err)) { mutt_error(_("Bad IDN in \"%s\": '%s'"), tag, err); FREE(&err); } if (option(OPT_CRYPT_OPPORTUNISTIC_ENCRYPT)) crypt_opportunistic_encrypt(msg); } else { /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the attachment list could change if the user invokes ~v to edit the message with headers, in which we need to execute the code below to regenerate the index array */ mutt_builtin_editor(msg->content->filename, msg, cur); } mutt_update_encoding(msg->content); /* attachments may have been added */ if (actx->idxlen && actx->idx[actx->idxlen - 1]->content->next) { mutt_actx_free_entries(actx); mutt_update_compose_menu(actx, menu, 1); } menu->redraw = REDRAW_FULL; mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_ATTACH_KEY: if (!(WithCrypto & APPLICATION_PGP)) break; new = mutt_mem_calloc(1, sizeof(struct AttachPtr)); new->content = crypt_pgp_make_key_attachment(NULL); if (new->content) { update_idx(menu, actx, new); menu->redraw |= REDRAW_INDEX; } else FREE(&new); menu->redraw |= REDRAW_STATUS; mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_ATTACH_FILE: { char *prompt = NULL, **files = NULL; int error, numfiles; fname[0] = 0; prompt = _("Attach file"); numfiles = 0; files = NULL; if (mutt_enter_fname_full(prompt, fname, sizeof(fname), 0, 1, &files, &numfiles, MUTT_SEL_MULTI) == -1 || *fname == '\0') break; error = 0; if (numfiles > 1) mutt_message(_("Attaching selected files...")); for (i = 0; i < numfiles; i++) { char *att = files[i]; new = (struct AttachPtr *) mutt_mem_calloc(1, sizeof(struct AttachPtr)); new->unowned = 1; new->content = mutt_make_file_attach(att); if (new->content) update_idx(menu, actx, new); else { error = 1; mutt_error(_("Unable to attach %s!"), att); FREE(&new); } FREE(&files[i]); } FREE(&files); if (!error) mutt_clear_error(); menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_ATTACH_MESSAGE: #ifdef USE_NNTP case OP_COMPOSE_ATTACH_NEWS_MESSAGE: #endif { char *prompt = NULL; fname[0] = 0; prompt = _("Open mailbox to attach message from"); #ifdef USE_NNTP unset_option(OPT_NEWS); if (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE) { CurrentNewsSrv = nntp_select_server(NewsServer, false); if (!CurrentNewsSrv) break; prompt = _("Open newsgroup to attach message from"); set_option(OPT_NEWS); } #endif if (Context) #ifdef USE_NNTP if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->magic == MUTT_NNTP)) #endif { mutt_str_strfcpy(fname, NONULL(Context->path), sizeof(fname)); mutt_pretty_mailbox(fname, sizeof(fname)); } if (mutt_enter_fname(prompt, fname, sizeof(fname), 1) == -1 || !fname[0]) break; #ifdef USE_NNTP if (option(OPT_NEWS)) 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)) #endif #ifdef USE_POP if (!mx_is_pop(fname)) #endif #ifdef USE_NNTP if (!mx_is_nntp(fname) && !option(OPT_NEWS)) #endif /* check to make sure the file exists and is readable */ if (access(fname, R_OK) == -1) { mutt_perror(fname); break; } menu->redraw = REDRAW_FULL; ctx = mx_open_mailbox(fname, MUTT_READONLY, NULL); if (!ctx) { mutt_error(_("Unable to open mailbox %s"), fname); break; } if (!ctx->msgcount) { mx_close_mailbox(ctx, NULL); FREE(&ctx); mutt_error(_("No messages in that folder.")); break; } this = Context; /* remember current folder and sort methods */ old_sort = Sort; old_sort_aux = SortAux; Context = ctx; set_option(OPT_ATTACH_MSG); mutt_message(_("Tag the messages you want to attach!")); close = mutt_index_menu(); unset_option(OPT_ATTACH_MSG); if (!Context) { /* go back to the folder we started from */ Context = this; /* Restore old $sort and $sort_aux */ Sort = old_sort; SortAux = old_sort_aux; menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; break; } for (i = 0; i < Context->msgcount; i++) { if (!message_is_tagged(Context, i)) continue; new = (struct AttachPtr *) mutt_mem_calloc(1, sizeof(struct AttachPtr)); new->content = mutt_make_message_attach(Context, Context->hdrs[i], 1); if (new->content != NULL) update_idx(menu, actx, new); else { mutt_error(_("Unable to attach!")); FREE(&new); } } menu->redraw |= REDRAW_FULL; if (close == OP_QUIT) mx_close_mailbox(Context, NULL); else mx_fastclose_mailbox(Context); FREE(&Context); /* go back to the folder we started from */ Context = this; /* Restore old $sort and $sort_aux */ Sort = old_sort; SortAux = old_sort_aux; } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_DELETE: CHECK_COUNT; if (CURATTACH->unowned) CURATTACH->content->unlink = 0; if (delete_attachment(actx, menu->current) == -1) break; mutt_update_compose_menu(actx, menu, 0); if (menu->current == 0) msg->content = actx->idx[0]->content; mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_TOGGLE_RECODE: { CHECK_COUNT; if (!mutt_is_text_part(CURATTACH->content)) { mutt_error(_("Recoding only affects text attachments.")); break; } CURATTACH->content->noconv = !CURATTACH->content->noconv; if (CURATTACH->content->noconv) mutt_message(_("The current attachment won't be converted.")); else mutt_message(_("The current attachment will be converted.")); menu->redraw = REDRAW_CURRENT; mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; } case OP_COMPOSE_EDIT_DESCRIPTION: CHECK_COUNT; mutt_str_strfcpy(buf, CURATTACH->content->description ? CURATTACH->content->description : "", sizeof(buf)); /* header names should not be translated */ if (mutt_get_field("Description: ", buf, sizeof(buf), 0) == 0) { mutt_str_replace(&CURATTACH->content->description, buf); menu->redraw = REDRAW_CURRENT; } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_UPDATE_ENCODING: CHECK_COUNT; if (menu->tagprefix) { struct Body *top = NULL; for (top = msg->content; top; top = top->next) { if (top->tagged) mutt_update_encoding(top); } menu->redraw = REDRAW_FULL; } else { mutt_update_encoding(CURATTACH->content); menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_TOGGLE_DISPOSITION: /* toggle the content-disposition between inline/attachment */ CURATTACH->content->disposition = (CURATTACH->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE; menu->redraw = REDRAW_CURRENT; break; case OP_EDIT_TYPE: CHECK_COUNT; { mutt_edit_content_type(NULL, CURATTACH->content, NULL); /* this may have been a change to text/something */ mutt_update_encoding(CURATTACH->content); menu->redraw = REDRAW_CURRENT; } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_EDIT_ENCODING: CHECK_COUNT; mutt_str_strfcpy(buf, ENCODING(CURATTACH->content->encoding), sizeof(buf)); if (mutt_get_field("Content-Transfer-Encoding: ", buf, sizeof(buf), 0) == 0 && buf[0]) { i = mutt_check_encoding(buf); if ((i != ENCOTHER) && (i != ENCUUENCODED)) { CURATTACH->content->encoding = i; menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; mutt_clear_error(); } else mutt_error(_("Invalid encoding.")); } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_SEND_MESSAGE: /* Note: We don't invoke send2-hook here, since we want to leave * users an opportunity to change settings from the ":" prompt. */ if (check_attachments(actx) != 0) { menu->redraw = REDRAW_FULL; break; } #ifdef MIXMASTER if (!STAILQ_EMPTY(&msg->chain) && mix_check_message(msg) != 0) break; #endif if (!fcc_set && *fcc) { if ((i = query_quadoption(OPT_COPY, _("Save a copy of this message?"))) == MUTT_ABORT) break; else if (i == MUTT_NO) *fcc = 0; } loop = 0; r = 0; break; case OP_COMPOSE_EDIT_FILE: CHECK_COUNT; mutt_edit_file(NONULL(Editor), CURATTACH->content->filename); mutt_update_encoding(CURATTACH->content); menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_TOGGLE_UNLINK: CHECK_COUNT; CURATTACH->content->unlink = !CURATTACH->content->unlink; menu->redraw = REDRAW_INDEX; /* No send2hook since this doesn't change the message. */ break; case OP_COMPOSE_GET_ATTACHMENT: CHECK_COUNT; if (menu->tagprefix) { struct Body *top = NULL; for (top = msg->content; top; top = top->next) { if (top->tagged) mutt_get_tmp_attachment(top); } menu->redraw = REDRAW_FULL; } else if (mutt_get_tmp_attachment(CURATTACH->content) == 0) menu->redraw = REDRAW_CURRENT; /* No send2hook since this doesn't change the message. */ break; case OP_COMPOSE_RENAME_ATTACHMENT: { char *src = NULL; int ret; CHECK_COUNT; if (CURATTACH->content->d_filename) src = CURATTACH->content->d_filename; else src = CURATTACH->content->filename; mutt_str_strfcpy(fname, mutt_file_basename(NONULL(src)), sizeof(fname)); ret = mutt_get_field(_("Send attachment with name: "), fname, sizeof(fname), MUTT_FILE); if (ret == 0) { /* * As opposed to RENAME_FILE, we don't check fname[0] because it's * valid to set an empty string here, to erase what was set */ mutt_str_replace(&CURATTACH->content->d_filename, fname); menu->redraw = REDRAW_CURRENT; } } break; case OP_COMPOSE_RENAME_FILE: CHECK_COUNT; mutt_str_strfcpy(fname, CURATTACH->content->filename, sizeof(fname)); mutt_pretty_mailbox(fname, sizeof(fname)); if (mutt_get_field(_("Rename to: "), fname, sizeof(fname), MUTT_FILE) == 0 && fname[0]) { if (stat(CURATTACH->content->filename, &st) == -1) { /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */ mutt_error(_("Can't stat %s: %s"), fname, strerror(errno)); break; } mutt_expand_path(fname, sizeof(fname)); if (mutt_file_rename(CURATTACH->content->filename, fname)) break; mutt_str_replace(&CURATTACH->content->filename, fname); menu->redraw = REDRAW_CURRENT; if (CURATTACH->content->stamp >= st.st_mtime) mutt_stamp_attachment(CURATTACH->content); } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_NEW_MIME: { char type[STRING]; char *p = NULL; int itype; FILE *fp = NULL; mutt_window_clearline(MuttMessageWindow, 0); fname[0] = 0; if (mutt_get_field(_("New file: "), fname, sizeof(fname), MUTT_FILE) != 0 || !fname[0]) { continue; } mutt_expand_path(fname, sizeof(fname)); /* Call to lookup_mime_type () ? maybe later */ type[0] = 0; if (mutt_get_field("Content-Type: ", type, sizeof(type), 0) != 0 || !type[0]) continue; p = strchr(type, '/'); if (!p) { mutt_error(_("Content-Type is of the form base/sub")); continue; } *p++ = 0; itype = mutt_check_mime_type(type); if (itype == TYPEOTHER) { mutt_error(_("Unknown Content-Type %s"), type); continue; } new = (struct AttachPtr *) mutt_mem_calloc(1, sizeof(struct AttachPtr)); /* Touch the file */ fp = mutt_file_fopen(fname, "w"); if (!fp) { mutt_error(_("Can't create file %s"), fname); FREE(&new); continue; } mutt_file_fclose(&fp); new->content = mutt_make_file_attach(fname); if (!new->content) { mutt_error(_("What we have here is a failure to make an attachment")); FREE(&new); continue; } update_idx(menu, actx, new); CURATTACH->content->type = itype; mutt_str_replace(&CURATTACH->content->subtype, p); CURATTACH->content->unlink = true; menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; if (mutt_compose_attachment(CURATTACH->content)) { mutt_update_encoding(CURATTACH->content); menu->redraw = REDRAW_FULL; } } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_COMPOSE_EDIT_MIME: CHECK_COUNT; if (mutt_edit_attachment(CURATTACH->content)) { mutt_update_encoding(CURATTACH->content); menu->redraw = REDRAW_FULL; } mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_VIEW_ATTACH: case OP_DISPLAY_HEADERS: CHECK_COUNT; mutt_attach_display_loop(menu, op, NULL, actx, false); menu->redraw = REDRAW_FULL; /* no send2hook, since this doesn't modify the message */ break; case OP_SAVE: CHECK_COUNT; mutt_save_attachment_list(actx, NULL, menu->tagprefix, CURATTACH->content, NULL, menu); /* no send2hook, since this doesn't modify the message */ break; case OP_PRINT: CHECK_COUNT; mutt_print_attachment_list(actx, NULL, menu->tagprefix, CURATTACH->content); /* no send2hook, since this doesn't modify the message */ break; case OP_PIPE: case OP_FILTER: CHECK_COUNT; mutt_pipe_attachment_list(actx, NULL, menu->tagprefix, CURATTACH->content, op == OP_FILTER); if (op == OP_FILTER) /* cte might have changed */ menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT; menu->redraw |= REDRAW_STATUS; mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_EXIT: i = query_quadoption(OPT_POSTPONE, _("Postpone this message?")); if (i == MUTT_NO) { for (i = 0; i < actx->idxlen; i++) if (actx->idx[i]->unowned) actx->idx[i]->content->unlink = false; if (!(flags & MUTT_COMPOSE_NOFREEHEADER)) { for (i = 0; i < actx->idxlen; i++) { /* avoid freeing other attachments */ actx->idx[i]->content->next = NULL; actx->idx[i]->content->parts = NULL; mutt_free_body(&actx->idx[i]->content); } } r = -1; loop = 0; break; } else if (i == MUTT_ABORT) break; /* abort */ /* fallthrough */ case OP_COMPOSE_POSTPONE_MESSAGE: if (check_attachments(actx) != 0) { menu->redraw = REDRAW_FULL; break; } loop = 0; r = 1; break; case OP_COMPOSE_ISPELL: endwin(); snprintf(buf, sizeof(buf), "%s -x %s", NONULL(Ispell), msg->content->filename); if (mutt_system(buf) == -1) mutt_error(_("Error running \"%s\"!"), buf); else { mutt_update_encoding(msg->content); menu->redraw |= REDRAW_STATUS; } break; case OP_COMPOSE_WRITE_MESSAGE: fname[0] = '\0'; if (Context) { mutt_str_strfcpy(fname, NONULL(Context->path), sizeof(fname)); mutt_pretty_mailbox(fname, sizeof(fname)); } if (actx->idxlen) msg->content = actx->idx[0]->content; if (mutt_enter_fname(_("Write message to mailbox"), fname, sizeof(fname), 1) != -1 && fname[0]) { mutt_message(_("Writing message to %s ..."), fname); mutt_expand_path(fname, sizeof(fname)); if (msg->content->next) msg->content = mutt_make_multipart(msg->content); if (mutt_write_fcc(fname, msg, NULL, 0, NULL, NULL) < 0) msg->content = mutt_remove_multipart(msg->content); else mutt_message(_("Message written.")); } break; case OP_COMPOSE_PGP_MENU: if (!(WithCrypto & APPLICATION_PGP)) break; if ((WithCrypto & APPLICATION_SMIME) && (msg->security & APPLICATION_SMIME)) { if (msg->security & (ENCRYPT | SIGN)) { if (mutt_yesorno( _("S/MIME already selected. Clear and continue ? "), MUTT_YES) != MUTT_YES) { mutt_clear_error(); break; } msg->security &= ~(ENCRYPT | SIGN); } msg->security &= ~APPLICATION_SMIME; msg->security |= APPLICATION_PGP; crypt_opportunistic_encrypt(msg); redraw_crypt_lines(msg); } msg->security = crypt_pgp_send_menu(msg); redraw_crypt_lines(msg); mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; case OP_FORGET_PASSPHRASE: crypt_forget_passphrase(); break; case OP_COMPOSE_SMIME_MENU: if (!(WithCrypto & APPLICATION_SMIME)) break; if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP)) { if (msg->security & (ENCRYPT | SIGN)) { if (mutt_yesorno(_("PGP already selected. Clear and continue ? "), MUTT_YES) != MUTT_YES) { mutt_clear_error(); break; } msg->security &= ~(ENCRYPT | SIGN); } msg->security &= ~APPLICATION_PGP; msg->security |= APPLICATION_SMIME; crypt_opportunistic_encrypt(msg); redraw_crypt_lines(msg); } msg->security = crypt_smime_send_menu(msg); redraw_crypt_lines(msg); mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; #ifdef MIXMASTER case OP_COMPOSE_MIX: mix_make_chain(&msg->chain); mutt_message_hook(NULL, msg, MUTT_SEND2HOOK); break; #endif } } mutt_pop_current_menu(menu); mutt_menu_destroy(&menu); if (actx->idxlen) msg->content = actx->idx[0]->content; else msg->content = NULL; mutt_free_attach_context(&actx); return r; } neomutt-neomutt-20171215/compress.c000066400000000000000000000521231321473123000171400ustar00rootroot00000000000000/** * @file * Compressed mbox local mailbox type * * @authors * Copyright (C) 1997 Alain Penders * Copyright (C) 2016 Richard Russon * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "compress.h" #include "context.h" #include "format_flags.h" #include "globals.h" #include "mailbox.h" #include "mutt_curses.h" #include "mx.h" #include "options.h" #include "protos.h" struct Header; /* Notes: * Any references to compressed files also apply to encrypted files. * ctx->path == plaintext file * ctx->realpath == compressed file */ /** * struct CompressInfo - Private data for compress * * This object gets attached to the mailbox's Context. */ struct CompressInfo { const char *append; /**< append-hook command */ const char *close; /**< close-hook command */ const char *open; /**< open-hook command */ off_t size; /**< size of the compressed file */ struct MxOps *child_ops; /**< callbacks of de-compressed file */ int locked; /**< if realpath is locked */ FILE *lockfp; /**< fp used for locking */ }; /** * lock_realpath - Try to lock the ctx->realpath * @param ctx Mailbox to lock * @param excl Lock exclusively? * @retval 1 Success (locked or readonly) * @retval 0 Error (can't lock the file) * * Try to (exclusively) lock the mailbox. If we succeed, then we mark the * mailbox as locked. If we fail, but we didn't want exclusive rights, then * the mailbox will be marked readonly. */ static int lock_realpath(struct Context *ctx, int excl) { if (!ctx) return 0; struct CompressInfo *ci = ctx->compress_info; if (!ci) return 0; if (ci->locked) return 1; if (excl) ci->lockfp = fopen(ctx->realpath, "a"); else ci->lockfp = fopen(ctx->realpath, "r"); if (!ci->lockfp) { mutt_perror(ctx->realpath); return 0; } int r = mutt_file_lock(fileno(ci->lockfp), excl, 1); if (r == 0) ci->locked = 1; else if (excl == 0) { mutt_file_fclose(&ci->lockfp); ctx->readonly = true; return 1; } return (r == 0); } /** * unlock_realpath - Unlock the ctx->realpath * @param ctx Mailbox to unlock * * Unlock a mailbox previously locked by lock_mailbox(). */ static void unlock_realpath(struct Context *ctx) { if (!ctx) return; struct CompressInfo *ci = ctx->compress_info; if (!ci) return; if (!ci->locked) return; mutt_file_unlock(fileno(ci->lockfp)); ci->locked = 0; mutt_file_fclose(&ci->lockfp); } /** * setup_paths - Set the mailbox paths * @param ctx Mailbox to modify * @retval 0 Success * @retval -1 Error * * Save the compressed filename in ctx->realpath. * Create a temporary filename and put its name in ctx->path. * The temporary file is created to prevent symlink attacks. */ static int setup_paths(struct Context *ctx) { if (!ctx) return -1; char tmppath[_POSIX_PATH_MAX]; FILE *tmpfp = NULL; /* Setup the right paths */ FREE(&ctx->realpath); ctx->realpath = ctx->path; /* We will uncompress to /tmp */ mutt_mktemp(tmppath, sizeof(tmppath)); ctx->path = mutt_str_strdup(tmppath); tmpfp = mutt_file_fopen(ctx->path, "w"); if (!tmpfp) return -1; mutt_file_fclose(&tmpfp); return 0; } /** * get_size - Get the size of a file * @param path File to measure * @retval n Size in bytes * @retval 0 On error */ static int get_size(const char *path) { if (!path) return 0; struct stat sb; if (stat(path, &sb) != 0) return 0; return sb.st_size; } /** * store_size - Save the size of the compressed file * @param ctx Mailbox * * Save the compressed file size in the compress_info struct. */ static void store_size(const struct Context *ctx) { if (!ctx) return; struct CompressInfo *ci = ctx->compress_info; if (!ci) return; ci->size = get_size(ctx->realpath); } /** * find_hook - Find a hook to match a path * @param type Type of hook, e.g. #MUTT_CLOSEHOOK * @param path Filename to test * @retval string Matching hook command * @retval NULL No matches * * Each hook has a type and a pattern. * Find a command that matches the type and path supplied. e.g. * * User config: * open-hook '\.gz$' "gzip -cd '%f' > '%t'" * * Call: * find_hook (#MUTT_OPENHOOK, "myfile.gz"); */ static const char *find_hook(int type, const char *path) { if (!path) return NULL; const char *c = mutt_find_hook(type, path); if (!c || !*c) return NULL; return c; } /** * set_compress_info - Find the compress hooks for a mailbox * @param ctx Mailbox to examine * @retval ptr CompressInfo Hook info for the mailbox's path * @retval NULL On error * * When a mailbox is opened, we check if there are any matching hooks. */ static struct CompressInfo *set_compress_info(struct Context *ctx) { if (!ctx || !ctx->path) return NULL; if (ctx->compress_info) return ctx->compress_info; /* Open is compulsory */ const char *o = find_hook(MUTT_OPENHOOK, ctx->path); if (!o) return NULL; const char *c = find_hook(MUTT_CLOSEHOOK, ctx->path); const char *a = find_hook(MUTT_APPENDHOOK, ctx->path); struct CompressInfo *ci = mutt_mem_calloc(1, sizeof(struct CompressInfo)); ctx->compress_info = ci; ci->open = mutt_str_strdup(o); ci->close = mutt_str_strdup(c); ci->append = mutt_str_strdup(a); return ci; } /** * free_compress_info - Frees the compress info members and structure * @param ctx Mailbox to free compress_info for */ static void free_compress_info(struct Context *ctx) { struct CompressInfo *ci = NULL; if (!ctx || !ctx->compress_info) return; ci = ctx->compress_info; FREE(&ci->open); FREE(&ci->close); FREE(&ci->append); unlock_realpath(ctx); FREE(&ctx->compress_info); } /** * escape_path - Escapes single quotes in a path for a command string * @param src the path to escape * @retval ptr The escaped string */ static char *escape_path(char *src) { static char dest[HUGE_STRING]; char *destp = dest; int destsize = 0; if (!src) return NULL; while (*src && (destsize < sizeof(dest) - 1)) { if (*src != '\'') { *destp++ = *src++; destsize++; } else { /* convert ' into '\'' */ if (destsize + 4 < sizeof(dest)) { *destp++ = *src++; *destp++ = '\\'; *destp++ = '\''; *destp++ = '\''; destsize += 4; } else break; } } *destp = '\0'; return dest; } /** * compress_format_str - Expand the filenames in a command string * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] col Starting column * @param[in] cols Number of screen columns * @param[in] op printf-like operator, e.g. 't' * @param[in] src printf-like format string * @param[in] prec Field precision, e.g. "-3.4" * @param[in] if_str If condition is met, display this string * @param[in] else_str Otherwise, display this string * @param[in] data Pointer to the mailbox Context * @param[in] flags Format flags * @retval src (unchanged) * * compress_format_str() is a callback function for mutt_expando_format(). * * | Expando | Description * |:--------|:-------------------------------------------------------- * | \%f | Compressed file * | \%t | Plaintext, temporary file */ static const char *compress_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, enum FormatFlag flags) { if (!buf || (data == 0)) return src; struct Context *ctx = (struct Context *) data; switch (op) { case 'f': /* Compressed file */ snprintf(buf, buflen, "%s", NONULL(escape_path(ctx->realpath))); break; case 't': /* Plaintext, temporary file */ snprintf(buf, buflen, "%s", NONULL(escape_path(ctx->path))); break; } return src; } /** * expand_command_str - Expand placeholders in command string * @param ctx Mailbox for paths * @param cmd Template command to be expanded * @param buf Buffer to store the command * @param buflen Size of the buffer * * This function takes a hook command and expands the filename placeholders * within it. The function calls mutt_expando_format() to do the replacement * which calls our callback function compress_format_str(). e.g. * * Template command: * gzip -cd '%f' > '%t' * * Result: * gzip -dc '~/mail/abc.gz' > '/tmp/xyz' */ static void expand_command_str(const struct Context *ctx, const char *cmd, char *buf, int buflen) { if (!ctx || !cmd || !buf) return; mutt_expando_format(buf, buflen, 0, buflen, cmd, compress_format_str, (unsigned long) ctx, 0); } /** * execute_command - Run a system command * @param ctx Mailbox to work with * @param command Command string to execute * @param progress Message to show the user * @retval 1 Success * @retval 0 Failure * * Run the supplied command, taking care of all the NeoMutt requirements, * such as locking files and blocking signals. */ static int execute_command(struct Context *ctx, const char *command, const char *progress) { int rc = 1; char sys_cmd[HUGE_STRING]; if (!ctx || !command || !progress) return 0; if (!ctx->quiet) mutt_message(progress, ctx->realpath); mutt_sig_block(); endwin(); fflush(stdout); expand_command_str(ctx, command, sys_cmd, sizeof(sys_cmd)); if (mutt_system(sys_cmd) != 0) { rc = 0; mutt_any_key_to_continue(NULL); mutt_error(_("Error running \"%s\"!"), sys_cmd); } mutt_sig_unblock(); return rc; } /** * comp_open_mailbox - Open a compressed mailbox * @param ctx Mailbox to open * * Set up a compressed mailbox to be read. * Decompress the mailbox and set up the paths and hooks needed. * Then determine the type of the mailbox so we can delegate the handling of * messages. */ static int comp_open_mailbox(struct Context *ctx) { if (!ctx || (ctx->magic != MUTT_COMPRESSED)) return -1; struct CompressInfo *ci = set_compress_info(ctx); if (!ci) return -1; /* If there's no close-hook, or the file isn't writable */ if (!ci->close || (access(ctx->path, W_OK) != 0)) ctx->readonly = true; if (setup_paths(ctx) != 0) goto or_fail; store_size(ctx); if (!lock_realpath(ctx, 0)) { mutt_error(_("Unable to lock mailbox!")); goto or_fail; } int rc = execute_command(ctx, ci->open, _("Decompressing %s")); if (rc == 0) goto or_fail; unlock_realpath(ctx); ctx->magic = mx_get_magic(ctx->path); if (ctx->magic == 0) { mutt_error(_("Can't identify the contents of the compressed file")); goto or_fail; } ci->child_ops = mx_get_ops(ctx->magic); if (!ci->child_ops) { mutt_error(_("Can't find mailbox ops for mailbox type %d"), ctx->magic); goto or_fail; } return ci->child_ops->open(ctx); or_fail: /* remove the partial uncompressed file */ remove(ctx->path); free_compress_info(ctx); return -1; } /** * comp_open_append_mailbox - Open a compressed mailbox for appending * @param ctx Mailbox to open * @param flags e.g. Does the file already exist? * @retval 0 Success * @retval -1 Failure * * To append to a compressed mailbox we need an append-hook (or both open- and * close-hooks). */ static int comp_open_append_mailbox(struct Context *ctx, int flags) { if (!ctx) return -1; /* If this succeeds, we know there's an open-hook */ struct CompressInfo *ci = set_compress_info(ctx); if (!ci) return -1; /* To append we need an append-hook or a close-hook */ if (!ci->append && !ci->close) { mutt_error(_("Cannot append without an append-hook or close-hook : %s"), ctx->path); goto oa_fail1; } if (setup_paths(ctx) != 0) goto oa_fail2; /* Lock the realpath for the duration of the append. * It will be unlocked in the close */ if (!lock_realpath(ctx, 1)) { mutt_error(_("Unable to lock mailbox!")); goto oa_fail2; } /* Open the existing mailbox, unless we are appending */ if (!ci->append && (get_size(ctx->realpath) > 0)) { int rc = execute_command(ctx, ci->open, _("Decompressing %s")); if (rc == 0) { mutt_error(_("Compress command failed: %s"), ci->open); goto oa_fail2; } ctx->magic = mx_get_magic(ctx->path); } else ctx->magic = MboxType; /* We can only deal with mbox and mmdf mailboxes */ if ((ctx->magic != MUTT_MBOX) && (ctx->magic != MUTT_MMDF)) { mutt_error(_("Unsupported mailbox type for appending.")); goto oa_fail2; } ci->child_ops = mx_get_ops(ctx->magic); if (!ci->child_ops) { mutt_error(_("Can't find mailbox ops for mailbox type %d"), ctx->magic); goto oa_fail2; } if (ci->child_ops->open_append(ctx, flags) != 0) goto oa_fail2; return 0; oa_fail2: /* remove the partial uncompressed file */ remove(ctx->path); oa_fail1: /* Free the compress_info to prevent close from trying to recompress */ free_compress_info(ctx); return -1; } /** * comp_close_mailbox - Close a compressed mailbox * @param ctx Mailbox to close * @retval 0 Success * @retval -1 Failure * * If the mailbox has been changed then re-compress the tmp file. * Then delete the tmp file. */ static int comp_close_mailbox(struct Context *ctx) { if (!ctx) return -1; struct CompressInfo *ci = ctx->compress_info; if (!ci) return -1; struct MxOps *ops = ci->child_ops; if (!ops) { free_compress_info(ctx); return -1; } ops->close(ctx); /* sync has already been called, so we only need to delete some files */ if (!ctx->append) { /* If the file was removed, remove the compressed folder too */ if ((access(ctx->path, F_OK) != 0) && !option(OPT_SAVE_EMPTY)) { remove(ctx->realpath); } else { remove(ctx->path); } } else { const char *append = NULL; const char *msg = NULL; /* The file exists and we can append */ if ((access(ctx->realpath, F_OK) == 0) && ci->append) { append = ci->append; msg = _("Compressed-appending to %s..."); } else { append = ci->close; msg = _("Compressing %s..."); } int rc = execute_command(ctx, append, msg); if (rc == 0) { mutt_any_key_to_continue(NULL); mutt_error(_("Error. Preserving temporary file: %s"), ctx->path); } else remove(ctx->path); unlock_realpath(ctx); } free_compress_info(ctx); return 0; } /** * comp_check_mailbox - Check for changes in the compressed file * @param ctx Mailbox * @param index_hint Currently selected mailbox * @retval 0 Mailbox OK * @retval #MUTT_REOPENED The mailbox was closed and reopened * @retval -1 Mailbox bad * * If the compressed file changes in size but the mailbox hasn't been changed * in NeoMutt, then we can close and reopen the mailbox. * * If the mailbox has been changed in NeoMutt, warn the user. * * The return codes are picked to match mx_check_mailbox(). */ static int comp_check_mailbox(struct Context *ctx, int *index_hint) { if (!ctx) return -1; struct CompressInfo *ci = ctx->compress_info; if (!ci) return -1; struct MxOps *ops = ci->child_ops; if (!ops) return -1; int size = get_size(ctx->realpath); if (size == ci->size) return 0; if (!lock_realpath(ctx, 0)) { mutt_error(_("Unable to lock mailbox!")); return -1; } int rc = execute_command(ctx, ci->open, _("Decompressing %s")); store_size(ctx); unlock_realpath(ctx); if (rc == 0) return -1; return ops->check(ctx, index_hint); } /** * comp_open_message - Delegated to mbox handler */ static int comp_open_message(struct Context *ctx, struct Message *msg, int msgno) { if (!ctx) return -1; struct CompressInfo *ci = ctx->compress_info; if (!ci) return -1; struct MxOps *ops = ci->child_ops; if (!ops) return -1; /* Delegate */ return ops->open_msg(ctx, msg, msgno); } /** * comp_close_message - Delegated to mbox handler */ static int comp_close_message(struct Context *ctx, struct Message *msg) { if (!ctx) return -1; struct CompressInfo *ci = ctx->compress_info; if (!ci) return -1; struct MxOps *ops = ci->child_ops; if (!ops) return -1; /* Delegate */ return ops->close_msg(ctx, msg); } /** * comp_commit_message - Delegated to mbox handler */ static int comp_commit_message(struct Context *ctx, struct Message *msg) { if (!ctx) return -1; struct CompressInfo *ci = ctx->compress_info; if (!ci) return -1; struct MxOps *ops = ci->child_ops; if (!ops) return -1; /* Delegate */ return ops->commit_msg(ctx, msg); } /** * comp_open_new_message - Delegated to mbox handler */ static int comp_open_new_message(struct Message *msg, struct Context *ctx, struct Header *hdr) { if (!ctx) return -1; struct CompressInfo *ci = ctx->compress_info; if (!ci) return -1; struct MxOps *ops = ci->child_ops; if (!ops) return -1; /* Delegate */ return ops->open_new_msg(msg, ctx, hdr); } /** * mutt_comp_can_append - Can we append to this path? * @param ctx Mailbox * @retval true Yes, we can append to the file * @retval false No, appending isn't possible * * To append to a file we can either use an 'append-hook' or a combination of * 'open-hook' and 'close-hook'. * * A match means it's our responsibility to append to the file. */ bool mutt_comp_can_append(struct Context *ctx) { if (!ctx) return false; /* If this succeeds, we know there's an open-hook */ struct CompressInfo *ci = set_compress_info(ctx); if (!ci) return false; /* We have an open-hook, so to append we need an append-hook, * or a close-hook. */ if (ci->append || ci->close) return true; mutt_error(_("Cannot append without an append-hook or close-hook : %s"), ctx->path); return false; } /** * mutt_comp_can_read - Can we read from this file? * @param path Pathname of file to be tested * @retval true Yes, we can read the file * @retval false No, we cannot read the file * * Search for an 'open-hook' with a regex that matches the path. * * A match means it's our responsibility to open the file. */ bool mutt_comp_can_read(const char *path) { if (!path) return false; if (find_hook(MUTT_OPENHOOK, path)) return true; else return false; } /** * comp_sync_mailbox - Save changes to the compressed mailbox file * @param ctx Mailbox to sync * @param index_hint Currently selected mailbox * @retval 0 Success * @retval -1 Failure * * Changes in NeoMutt only affect the tmp file. Calling comp_sync_mailbox() * will commit them to the compressed file. */ static int comp_sync_mailbox(struct Context *ctx, int *index_hint) { if (!ctx) return -1; struct CompressInfo *ci = ctx->compress_info; if (!ci) return -1; if (!ci->close) { mutt_error(_("Can't sync a compressed file without a close-hook")); return -1; } struct MxOps *ops = ci->child_ops; if (!ops) return -1; if (!lock_realpath(ctx, 1)) { mutt_error(_("Unable to lock mailbox!")); return -1; } int rc = comp_check_mailbox(ctx, index_hint); if (rc != 0) goto sync_cleanup; rc = ops->sync(ctx, index_hint); if (rc != 0) goto sync_cleanup; rc = execute_command(ctx, ci->close, _("Compressing %s")); if (rc == 0) { rc = -1; goto sync_cleanup; } rc = 0; sync_cleanup: store_size(ctx); unlock_realpath(ctx); return rc; } /** * mutt_comp_valid_command - Is this command string allowed? * @param cmd Command string * @retval 1 Valid command * @retval 0 "%f" and/or "%t" is missing * * A valid command string must have both "%f" (from file) and "%t" (to file). * We don't check if we can actually run the command. */ int mutt_comp_valid_command(const char *cmd) { if (!cmd) return 0; return (strstr(cmd, "%f") && strstr(cmd, "%t")); } /** * mx_comp_ops - Mailbox callback functions * * Compress only uses open, close and check. * The message functions are delegated to mbox. */ struct MxOps mx_comp_ops = { .open = comp_open_mailbox, .open_append = comp_open_append_mailbox, .close = comp_close_mailbox, .check = comp_check_mailbox, .sync = comp_sync_mailbox, .open_msg = comp_open_message, .close_msg = comp_close_message, .commit_msg = comp_commit_message, .open_new_msg = comp_open_new_message, }; neomutt-neomutt-20171215/compress.h000066400000000000000000000022051321473123000171410ustar00rootroot00000000000000/** * @file * Compressed mbox local mailbox type * * @authors * Copyright (C) 1997 Alain Penders * Copyright (C) 2016 Richard Russon * * @copyright * 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, see . */ #ifndef _MUTT_COMPRESS_H #define _MUTT_COMPRESS_H #include #include "mx.h" struct Context; bool mutt_comp_can_append(struct Context *ctx); bool mutt_comp_can_read(const char *path); int mutt_comp_valid_command(const char *cmd); extern struct MxOps mx_comp_ops; #endif /* _MUTT_COMPRESS_H */ neomutt-neomutt-20171215/configure000077500000000000000000000002001321473123000170350ustar00rootroot00000000000000#!/bin/sh dir="`dirname "$0"`/autosetup" WRAPPER="$0"; export WRAPPER; exec "`$dir/autosetup-find-tclsh`" "$dir/autosetup" "$@" neomutt-neomutt-20171215/conn/000077500000000000000000000000001321473123000160735ustar00rootroot00000000000000neomutt-neomutt-20171215/conn/account.h000066400000000000000000000020711321473123000177000ustar00rootroot00000000000000/** * @file * Account object * * @authors * Copyright (C) 2000-2005,2008 Brendan Cully * * @copyright * 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, see . */ #ifndef _CONN_ACCOUNT_H #define _CONN_ACCOUNT_H /** * struct Account - Login details for a remote server */ struct Account { char user[64]; char login[64]; char pass[256]; char host[128]; unsigned short port; unsigned char type; unsigned char flags; }; #endif /* _CONN_ACCOUNT_H */ neomutt-neomutt-20171215/conn/conn.h000066400000000000000000000025221321473123000172020ustar00rootroot00000000000000/** * @file * Connection Library * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ /** * @page conn Connection Library * * Manage external connections. * * -# @subpage conn_globals * -# @subpage conn_getdomain * -# @subpage conn_sasl * -# @subpage conn_sasl_plain * -# @subpage conn_socket * -# @subpage conn_ssl * -# @subpage conn_ssl_gnutls * -# @subpage conn_tunnel */ #ifndef _CONN_CONN_H #define _CONN_CONN_H #include "account.h" #include "conn_globals.h" #include "connection.h" #ifdef USE_SASL #include "sasl.h" #endif #include "sasl_plain.h" #include "socket.h" #ifdef USE_SSL #include "ssl.h" #endif #include "tunnel.h" #endif /* _CONN_CONN_H */ neomutt-neomutt-20171215/conn/conn_globals.c000066400000000000000000000041561321473123000207050ustar00rootroot00000000000000/** * @file * Connection Global Variables * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ /** * @page conn_globals Connection Global Variables * * These global variables are private to the connection library. * * | Global Variable | NeoMutt Config * | :--------------------- | :------------------------ * | #CertificateFile | $certificate_file * | #ConnectTimeout | $connect_timeout * | #EntropyFile | $entropy_file * | #Preconnect | $preconnect * | #SslCaCertificatesFile | $ssl_ca_certificates_file * | #SslCiphers | $ssl_ciphers * | #SslClientCert | $ssl_client_cert * | #SslMinDhPrimeBits | $ssl_min_dh_prime_bits * | #Tunnel | $tunnel */ #include "config.h" #include short ConnectTimeout = 0; /**< Config: $connect_timeout */ #ifdef USE_SSL const char *CertificateFile = NULL; /**< Config: $certificate_file */ const char *EntropyFile = NULL; /**< Config: $entropy_file */ const char *SslCiphers = NULL; /**< Config: $ssl_ciphers */ const char *SslClientCert = NULL; /**< Config: $ssl_client_cert */ #ifdef USE_SSL_GNUTLS const char *SslCaCertificatesFile = NULL; /**< Config: $ssl_ca_certificates_file */ short SslMinDhPrimeBits = 0; /**< Config: $ssl_min_dh_prime_bits */ #endif #endif #ifdef USE_SOCKET const char *Preconnect = NULL; /**< Config: $preconnect */ const char *Tunnel = NULL; /**< Config: $tunnel */ #endif neomutt-neomutt-20171215/conn/conn_globals.h000066400000000000000000000023041321473123000207030ustar00rootroot00000000000000/** * @file * Connection Global Variables * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ #ifndef _CONN_GLOBALS_H #define _CONN_GLOBALS_H short ConnectTimeout; #ifdef USE_SSL extern const char *CertificateFile; extern const char *EntropyFile; extern const char *SslCiphers; extern const char *SslClientCert; #ifdef USE_SSL_GNUTLS extern const char *SslCaCertificatesFile; short SslMinDhPrimeBits; #endif #endif #ifdef USE_SOCKET extern const char *Preconnect; extern const char *Tunnel; #endif #endif /* _CONN_GLOBALS_H */ neomutt-neomutt-20171215/conn/connection.h000066400000000000000000000032011321473123000203770ustar00rootroot00000000000000/** * @file * An open network connection (socket) * * @authors * Copyright (C) 1998 Brandon Long * Copyright (C) 1999-2005 Brendan Cully * * @copyright * 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, see . */ #ifndef _CONN_CONNECTION_H #define _CONN_CONNECTION_H #include #include #include "mutt/queue.h" #include "account.h" #define LONG_STRING 1024 /** * struct Connection - An open network connection (socket) */ struct Connection { struct Account account; unsigned int ssf; /**< security strength factor, in bits */ void *data; char inbuf[LONG_STRING]; int bufpos; int fd; int available; TAILQ_ENTRY(Connection) entries; void *sockdata; int (*conn_read)(struct Connection *conn, char *buf, size_t len); int (*conn_write)(struct Connection *conn, const char *buf, size_t count); int (*conn_open)(struct Connection *conn); int (*conn_close)(struct Connection *conn); int (*conn_poll)(struct Connection *conn, time_t wait_secs); }; #endif /* _CONN_CONNECTION_H */ neomutt-neomutt-20171215/conn/getdomain.c000066400000000000000000000062161321473123000202130ustar00rootroot00000000000000/** * @file * DNS lookups * * @authors * Copyright (C) 2009,2013,2016 Derek Martin * * @copyright * 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, see . */ /** * @page conn_getdomain DNS lookups * * DNS lookups * * | Function | Description * | :----------------- | :----------------------------------- * | getdnsdomainname() | Lookup the host's name using DNS */ #include "config.h" #include #include #include #include #include #include "mutt/debug.h" #include "mutt/memory.h" #include "mutt/string2.h" /** * getdnsdomainname - Lookup the host's name using DNS * @param d Buffer for the result * @param len Length of the buffer * @retval 0 Success * @retval -1 Error */ int getdnsdomainname(char *d, size_t len) { int rc = -1; #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETADDRINFO_A) char node[STRING]; if (gethostname(node, sizeof(node))) return rc; struct addrinfo hints; struct addrinfo *h = NULL; *d = '\0'; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_CANONNAME; hints.ai_family = AF_UNSPEC; #ifdef HAVE_GETADDRINFO_A /* Allow 0.1 seconds to get the FQDN (fully-qualified domain name). * If it takes longer, the system is mis-configured and the network is not * working properly, so... */ int status; struct timespec timeout = { 0, 100000000 }; struct gaicb *reqs[1]; reqs[0] = mutt_mem_calloc(1, sizeof(*reqs[0])); reqs[0]->ar_name = node; reqs[0]->ar_request = &hints; if (getaddrinfo_a(GAI_NOWAIT, reqs, 1, NULL) == 0) { gai_suspend((const struct gaicb *const *) reqs, 1, &timeout); status = gai_error(reqs[0]); if (status == 0) h = reqs[0]->ar_result; else if (status == EAI_INPROGRESS) { mutt_debug(1, "timeout\n"); /* request is not finish, cancel it to free it safely */ if (gai_cancel(reqs[0]) == EAI_NOTCANCELED) { while (gai_suspend((const struct gaicb *const *) reqs, 1, NULL) != 0) continue; } } else mutt_debug(1, "fail: (%d) %s\n", status, gai_strerror(status)); } FREE(&reqs[0]); #else /* !HAVE_GETADDRINFO_A */ mutt_debug(3, "before getaddrinfo\n"); getaddrinfo(node, NULL, &hints, &h); mutt_debug(3, "after getaddrinfo\n"); #endif char *p = NULL; if (h != NULL && h->ai_canonname && (p = strchr(h->ai_canonname, '.'))) { mutt_str_strfcpy(d, ++p, len); rc = 0; mutt_debug(1, "%s\n", d); freeaddrinfo(h); } #endif /* HAVE_GETADDRINFO || defined HAVE_GETADDRINFO_A */ return rc; } neomutt-neomutt-20171215/conn/sasl.c000066400000000000000000000501511321473123000172030ustar00rootroot00000000000000/** * @file * SASL authentication support * * @authors * Copyright (C) 2000-2008,2012,2014 Brendan Cully * * @copyright * 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, see . */ /** * @page conn_sasl SASL authentication support * * SASL can stack a protection layer on top of an existing connection. To * handle this, we store a saslconn_t in conn->sockdata, and write wrappers * which en/decode the read/write stream, then replace sockdata with an * embedded copy of the old sockdata and call the underlying functions (which * we've also preserved). I thought about trying to make a general stackable * connection system, but it seemed like overkill - something is wrong if we * have 15 filters on top of a socket. Anyway, anything else which wishes to * stack can use the same method. The only disadvantage is we have to write * wrappers for all the socket methods, even if we only stack over read and * write. Thinking about it, the abstraction problem is that there is more in * Connection than there needs to be. Ideally it would have only (void*)data * and methods. * * | Function | Description * | :--------------------- | :----------------------------------- * | mutt_sasl_client_new() | wrapper for sasl_client_new * | mutt_sasl_done() | Invoke when processing is complete. * | mutt_sasl_interact() | Perform an SASL interaction with the user * | mutt_sasl_setup_conn() | Set up an SASL connection */ #include "config.h" #include #include #include #include #include #include #include #include #include "mutt/debug.h" #include "mutt/memory.h" #include "mutt/message.h" #include "mutt/string2.h" #include "sasl.h" #include "account.h" #include "connection.h" #include "mutt_account.h" #include "options.h" #include "protos.h" /* arbitrary. SASL will probably use a smaller buffer anyway. OTOH it's * been a while since I've had access to an SASL server which negotiated * a protection buffer. */ #define MUTT_SASL_MAXBUF 65536 #define IP_PORT_BUFLEN 1024 static sasl_callback_t MuttSaslCallbacks[5]; static sasl_secret_t *secret_ptr = NULL; /** * getnameinfo_err - Convert a getaddrinfo() error code into an SASL error code * @param ret getaddrinfo() error code, e.g. EAI_AGAIN * @retval int SASL error code, e.g. SASL_FAIL */ static int getnameinfo_err(int ret) { int err; mutt_debug(1, "getnameinfo: "); switch (ret) { case EAI_AGAIN: mutt_debug(1, "The name could not be resolved at this time. Future " "attempts may succeed.\n"); err = SASL_TRYAGAIN; break; case EAI_BADFLAGS: mutt_debug(1, "The flags had an invalid value.\n"); err = SASL_BADPARAM; break; case EAI_FAIL: mutt_debug(1, "A non-recoverable error occurred.\n"); err = SASL_FAIL; break; case EAI_FAMILY: mutt_debug(1, "The address family was not recognized or the address " "length was invalid for the specified family.\n"); err = SASL_BADPROT; break; case EAI_MEMORY: mutt_debug(1, "There was a memory allocation failure.\n"); err = SASL_NOMEM; break; case EAI_NONAME: mutt_debug(1, "The name does not resolve for the supplied parameters. " "NI_NAMEREQD is set and the host's name cannot be located, " "or both nodename and servname were null.\n"); err = SASL_FAIL; /* no real equivalent */ break; case EAI_SYSTEM: mutt_debug(1, "A system error occurred. The error code can be found in " "errno(%d,%s)).\n", errno, strerror(errno)); err = SASL_FAIL; /* no real equivalent */ break; default: mutt_debug(1, "Unknown error %d\n", ret); err = SASL_FAIL; /* no real equivalent */ break; } return err; } /** * iptostring - Convert IP Address to string * @param addr IP address * @param addrlen Size of addr struct * @param out Buffer for result * @param outlen Length of buffer * @retval int SASL error code, e.g. SASL_BADPARAM * * utility function, copied from sasl2 sample code */ static int iptostring(const struct sockaddr *addr, socklen_t addrlen, char *out, unsigned int outlen) { char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; int ret; if (!addr || !out) return SASL_BADPARAM; ret = getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), NI_NUMERICHOST | #ifdef NI_WITHSCOPEID NI_WITHSCOPEID | #endif NI_NUMERICSERV); if (ret) return getnameinfo_err(ret); if (outlen < strlen(hbuf) + strlen(pbuf) + 2) return SASL_BUFOVER; snprintf(out, outlen, "%s;%s", hbuf, pbuf); return SASL_OK; } /** * mutt_sasl_cb_log - callback to log SASL messages * @param context Supplied context, always NULL * @param priority Debug level * @param message Message * @retval int SASL_OK, always */ static int mutt_sasl_cb_log(void *context, int priority, const char *message) { mutt_debug(priority, "SASL: %s\n", message); return SASL_OK; } /** * mutt_sasl_start - Initialise SASL library * @retval int SASL error code, e.g. SASL_OK * * Call before doing an SASL exchange - initialises library (if necessary). */ static int mutt_sasl_start(void) { static bool sasl_init = false; static sasl_callback_t callbacks[2]; int rc; if (sasl_init) return SASL_OK; /* set up default logging callback */ callbacks[0].id = SASL_CB_LOG; callbacks[0].proc = (int (*)(void)) mutt_sasl_cb_log; callbacks[0].context = NULL; callbacks[1].id = SASL_CB_LIST_END; callbacks[1].proc = NULL; callbacks[1].context = NULL; rc = sasl_client_init(callbacks); if (rc != SASL_OK) { mutt_debug(1, "libsasl initialisation failed.\n"); return SASL_FAIL; } sasl_init = true; return SASL_OK; } /** * mutt_sasl_cb_authname - callback to retrieve authname or user from Account * @param[in] context Account * @param[in] id Field to get. SASL_CB_USER or SASL_CB_AUTHNAME * @param[out] result Resulting string * @param[out] len Length of result * @retval int SASL error code, e.g. SASL_FAIL */ static int mutt_sasl_cb_authname(void *context, int id, const char **result, unsigned int *len) { struct Account *account = (struct Account *) context; if (!result) return SASL_FAIL; *result = NULL; if (len) *len = 0; if (!account) return SASL_BADPARAM; mutt_debug(2, "getting %s for %s:%u\n", id == SASL_CB_AUTHNAME ? "authname" : "user", account->host, account->port); if (id == SASL_CB_AUTHNAME) { if (mutt_account_getlogin(account) < 0) return SASL_FAIL; *result = account->login; } else { if (mutt_account_getuser(account) < 0) return SASL_FAIL; *result = account->user; } if (len) *len = strlen(*result); return SASL_OK; } /** * mutt_sasl_cb_pass - SASL callback function to get password * @param[in] conn Connection to a server * @param[in] context Account * @param[in] id SASL_CB_PASS * @param[out] psecret SASL secret * @retval int SASL error code, e.g SASL_FAIL */ static int mutt_sasl_cb_pass(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret) { struct Account *account = (struct Account *) context; int len; if (!account || !psecret) return SASL_BADPARAM; mutt_debug(2, "getting password for %s@%s:%u\n", account->login, account->host, account->port); if (mutt_account_getpass(account) < 0) return SASL_FAIL; len = strlen(account->pass); mutt_mem_realloc(&secret_ptr, sizeof(sasl_secret_t) + len); memcpy((char *) secret_ptr->data, account->pass, (size_t) len); secret_ptr->len = len; *psecret = secret_ptr; return SASL_OK; } /** * mutt_sasl_get_callbacks - Get the SASL callback functions * @param account Account to associate with callbacks * @retval ptr Array of callback functions */ static sasl_callback_t *mutt_sasl_get_callbacks(struct Account *account) { sasl_callback_t *callback = NULL; callback = MuttSaslCallbacks; callback->id = SASL_CB_USER; callback->proc = (int (*)(void)) mutt_sasl_cb_authname; callback->context = account; callback++; callback->id = SASL_CB_AUTHNAME; callback->proc = (int (*)(void)) mutt_sasl_cb_authname; callback->context = account; callback++; callback->id = SASL_CB_PASS; callback->proc = (int (*)(void)) mutt_sasl_cb_pass; callback->context = account; callback++; callback->id = SASL_CB_GETREALM; callback->proc = NULL; callback->context = NULL; callback++; callback->id = SASL_CB_LIST_END; callback->proc = NULL; callback->context = NULL; return MuttSaslCallbacks; } /** * mutt_sasl_conn_open - empty wrapper for underlying open function * @param conn Connection to the server * @retval 0 Success * @retval -1 Error * * We don't know in advance that a connection will use SASL, so we replace * conn's methods with sasl methods when authentication is successful, using * mutt_sasl_setup_conn */ static int mutt_sasl_conn_open(struct Connection *conn) { struct SaslData *sasldata = NULL; int rc; sasldata = (struct SaslData *) conn->sockdata; conn->sockdata = sasldata->sockdata; rc = (sasldata->msasl_open)(conn); conn->sockdata = sasldata; return rc; } /** * mutt_sasl_conn_close - close SASL connection * @param conn Connection to a server * @retval 0 Success * @retval -1 Error * * Calls underlying close function and disposes of the sasl_conn_t object, then * restores connection to pre-sasl state */ static int mutt_sasl_conn_close(struct Connection *conn) { struct SaslData *sasldata = NULL; int rc; sasldata = (struct SaslData *) conn->sockdata; /* restore connection's underlying methods */ conn->sockdata = sasldata->sockdata; conn->conn_open = sasldata->msasl_open; conn->conn_close = sasldata->msasl_close; conn->conn_read = sasldata->msasl_read; conn->conn_write = sasldata->msasl_write; conn->conn_poll = sasldata->msasl_poll; /* release sasl resources */ sasl_dispose(&sasldata->saslconn); FREE(&sasldata); /* call underlying close */ rc = (conn->conn_close)(conn); return rc; } /** * mutt_sasl_conn_read - Read data from an SASL connection * @param conn Connection to a server * @param buf Buffer to store the data * @param len Number of bytes to read * @retval >0 Success, number of bytes read * @retval -1 Error, see errno */ static int mutt_sasl_conn_read(struct Connection *conn, char *buf, size_t len) { struct SaslData *sasldata = NULL; int rc; unsigned int olen; sasldata = (struct SaslData *) conn->sockdata; /* if we still have data in our read buffer, copy it into buf */ if (sasldata->blen > sasldata->bpos) { olen = (sasldata->blen - sasldata->bpos > len) ? len : sasldata->blen - sasldata->bpos; memcpy(buf, sasldata->buf + sasldata->bpos, olen); sasldata->bpos += olen; return olen; } conn->sockdata = sasldata->sockdata; sasldata->bpos = 0; sasldata->blen = 0; /* and decode the result, if necessary */ if (*sasldata->ssf) { do { /* call the underlying read function to fill the buffer */ rc = (sasldata->msasl_read)(conn, buf, len); if (rc <= 0) goto out; rc = sasl_decode(sasldata->saslconn, buf, rc, &sasldata->buf, &sasldata->blen); if (rc != SASL_OK) { mutt_debug(1, "SASL decode failed: %s\n", sasl_errstring(rc, NULL, NULL)); goto out; } } while (!sasldata->blen); olen = (sasldata->blen - sasldata->bpos > len) ? len : sasldata->blen - sasldata->bpos; memcpy(buf, sasldata->buf, olen); sasldata->bpos += olen; rc = olen; } else rc = (sasldata->msasl_read)(conn, buf, len); out: conn->sockdata = sasldata; return rc; } /** * mutt_sasl_conn_write - Write to an SASL connection * @param conn Connection to a server * @param buf Buffer to store the data * @param len Number of bytes to read * @retval >0 Success, number of bytes read * @retval -1 Error, see errno */ static int mutt_sasl_conn_write(struct Connection *conn, const char *buf, size_t len) { struct SaslData *sasldata = NULL; int rc; const char *pbuf = NULL; unsigned int olen, plen; sasldata = (struct SaslData *) conn->sockdata; conn->sockdata = sasldata->sockdata; /* encode data, if necessary */ if (*sasldata->ssf) { /* handle data larger than MAXOUTBUF */ do { olen = (len > *sasldata->pbufsize) ? *sasldata->pbufsize : len; rc = sasl_encode(sasldata->saslconn, buf, olen, &pbuf, &plen); if (rc != SASL_OK) { mutt_debug(1, "SASL encoding failed: %s\n", sasl_errstring(rc, NULL, NULL)); goto fail; } rc = (sasldata->msasl_write)(conn, pbuf, plen); if (rc != plen) goto fail; len -= olen; buf += olen; } while (len > *sasldata->pbufsize); } else /* just write using the underlying socket function */ rc = (sasldata->msasl_write)(conn, buf, len); conn->sockdata = sasldata; return rc; fail: conn->sockdata = sasldata; return -1; } /** * mutt_sasl_conn_poll - Check an SASL connection for data * @param conn Connection to a server * @param wait_secs How long to wait for a response * @retval >0 There is data to read * @retval 0 Read would block * @retval -1 Connection doesn't support polling */ static int mutt_sasl_conn_poll(struct Connection *conn, time_t wait_secs) { struct SaslData *sasldata = conn->sockdata; int rc; conn->sockdata = sasldata->sockdata; rc = sasldata->msasl_poll(conn, wait_secs); conn->sockdata = sasldata; return rc; } /** * mutt_sasl_client_new - wrapper for sasl_client_new * @param conn Connection to a server * @param saslconn SASL connection * @retval 0 Success * @retval -1 Error * * which also sets various security properties. If this turns out to be fine * for POP too we can probably stop exporting mutt_sasl_get_callbacks(). */ int mutt_sasl_client_new(struct Connection *conn, sasl_conn_t **saslconn) { sasl_security_properties_t secprops; struct sockaddr_storage local, remote; socklen_t size; char iplocalport[IP_PORT_BUFLEN], ipremoteport[IP_PORT_BUFLEN]; char *plp = NULL; char *prp = NULL; const char *service = NULL; int rc; if (mutt_sasl_start() != SASL_OK) return -1; switch (conn->account.type) { case MUTT_ACCT_TYPE_IMAP: service = "imap"; break; case MUTT_ACCT_TYPE_POP: service = "pop"; break; case MUTT_ACCT_TYPE_SMTP: service = "smtp"; break; #ifdef USE_NNTP case MUTT_ACCT_TYPE_NNTP: service = "nntp"; break; #endif default: mutt_error(_("Unknown SASL profile")); return -1; } size = sizeof(local); if (!getsockname(conn->fd, (struct sockaddr *) &local, &size)) { if (iptostring((struct sockaddr *) &local, size, iplocalport, IP_PORT_BUFLEN) == SASL_OK) plp = iplocalport; else mutt_debug(2, "SASL failed to parse local IP address\n"); } else mutt_debug(2, "SASL failed to get local IP address\n"); size = sizeof(remote); if (!getpeername(conn->fd, (struct sockaddr *) &remote, &size)) { if (iptostring((struct sockaddr *) &remote, size, ipremoteport, IP_PORT_BUFLEN) == SASL_OK) prp = ipremoteport; else mutt_debug(2, "SASL failed to parse remote IP address\n"); } else mutt_debug(2, "SASL failed to get remote IP address\n"); mutt_debug(2, "SASL local ip: %s, remote ip:%s\n", NONULL(plp), NONULL(prp)); rc = sasl_client_new(service, conn->account.host, plp, prp, mutt_sasl_get_callbacks(&conn->account), 0, saslconn); if (rc != SASL_OK) { mutt_error(_("Error allocating SASL connection")); mutt_sleep(2); return -1; } memset(&secprops, 0, sizeof(secprops)); /* Work around a casting bug in the SASL krb4 module */ secprops.max_ssf = 0x7fff; secprops.maxbufsize = MUTT_SASL_MAXBUF; if (sasl_setprop(*saslconn, SASL_SEC_PROPS, &secprops) != SASL_OK) { mutt_error(_("Error setting SASL security properties")); return -1; } if (conn->ssf) { /* I'm not sure this actually has an effect, at least with SASLv2 */ mutt_debug(2, "External SSF: %d\n", conn->ssf); if (sasl_setprop(*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK) { mutt_error(_("Error setting SASL external security strength")); return -1; } } if (conn->account.user[0]) { mutt_debug(2, "External authentication name: %s\n", conn->account.user); if (sasl_setprop(*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) != SASL_OK) { mutt_error(_("Error setting SASL external user name")); return -1; } } return 0; } /** * mutt_sasl_interact - Perform an SASL interaction with the user * @param interaction Details of interaction * @retval int SASL error code: SASL_OK or SASL_FAIL * * An example interaction might be asking the user for a password. */ int mutt_sasl_interact(sasl_interact_t *interaction) { char prompt[SHORT_STRING]; char resp[SHORT_STRING]; while (interaction->id != SASL_CB_LIST_END) { mutt_debug(2, "filling in SASL interaction %ld.\n", interaction->id); snprintf(prompt, sizeof(prompt), "%s: ", interaction->prompt); resp[0] = '\0'; if (option(OPT_NO_CURSES) || mutt_get_field(prompt, resp, sizeof(resp), 0)) return SASL_FAIL; interaction->len = mutt_str_strlen(resp) + 1; interaction->result = mutt_mem_malloc(interaction->len); memcpy((char *) interaction->result, resp, interaction->len); interaction++; } return SASL_OK; } /** * mutt_sasl_setup_conn - Set up an SASL connection * @param conn Connection to a server * @param saslconn SASL connection * * Replace connection methods, sockdata with SASL wrappers, for protection * layers. Also get ssf, as a fastpath for the read/write methods. */ void mutt_sasl_setup_conn(struct Connection *conn, sasl_conn_t *saslconn) { struct SaslData *sasldata = mutt_mem_malloc(sizeof(struct SaslData)); /* work around sasl_getprop aliasing issues */ const void *tmp = NULL; sasldata->saslconn = saslconn; /* get ssf so we know whether we have to (en|de)code read/write */ sasl_getprop(saslconn, SASL_SSF, &tmp); sasldata->ssf = tmp; mutt_debug(3, "SASL protection strength: %u\n", *sasldata->ssf); /* Add SASL SSF to transport SSF */ conn->ssf += *sasldata->ssf; sasl_getprop(saslconn, SASL_MAXOUTBUF, &tmp); sasldata->pbufsize = tmp; mutt_debug(3, "SASL protection buffer size: %u\n", *sasldata->pbufsize); /* clear input buffer */ sasldata->buf = NULL; sasldata->bpos = 0; sasldata->blen = 0; /* preserve old functions */ sasldata->sockdata = conn->sockdata; sasldata->msasl_open = conn->conn_open; sasldata->msasl_close = conn->conn_close; sasldata->msasl_read = conn->conn_read; sasldata->msasl_write = conn->conn_write; sasldata->msasl_poll = conn->conn_poll; /* and set up new functions */ conn->sockdata = sasldata; conn->conn_open = mutt_sasl_conn_open; conn->conn_close = mutt_sasl_conn_close; conn->conn_read = mutt_sasl_conn_read; conn->conn_write = mutt_sasl_conn_write; conn->conn_poll = mutt_sasl_conn_poll; } /** * mutt_sasl_done - Invoke when processing is complete. * * This is a cleanup function, used to free all memory used by the library. * Invoke when processing is complete. */ void mutt_sasl_done(void) { #ifdef HAVE_SASL_CLIENT_DONE /* As we never use the server-side, the silently ignore the return value */ sasl_client_done(); #else sasl_done(); #endif } neomutt-neomutt-20171215/conn/sasl.h000066400000000000000000000034151321473123000172110ustar00rootroot00000000000000/** * @file * SASL authentication support * * @authors * Copyright (C) 2000-2005,2008 Brendan Cully * * @copyright * 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, see . */ /* common SASL helper routines */ #ifndef _CONN_SASL_H #define _CONN_SASL_H #include #include #include struct Connection; int mutt_sasl_client_new(struct Connection *conn, sasl_conn_t **saslconn); int mutt_sasl_interact(sasl_interact_t *interaction); void mutt_sasl_setup_conn(struct Connection *conn, sasl_conn_t *saslconn); void mutt_sasl_done(void); /** * struct SaslData - SASL authentication API */ struct SaslData { sasl_conn_t *saslconn; const sasl_ssf_t *ssf; const unsigned int *pbufsize; /* read buffer */ const char *buf; unsigned int blen; unsigned int bpos; /* underlying socket data */ void *sockdata; int (*msasl_open)(struct Connection *conn); int (*msasl_close)(struct Connection *conn); int (*msasl_read)(struct Connection *conn, char *buf, size_t len); int (*msasl_write)(struct Connection *conn, const char *buf, size_t count); int (*msasl_poll)(struct Connection *conn, time_t wait_secs); }; #endif /* _CONN_SASL_H */ neomutt-neomutt-20171215/conn/sasl_plain.c000066400000000000000000000041121321473123000203620ustar00rootroot00000000000000/** * @file * SASL plain authentication support * * @authors * Copyright (C) 2016 Pietro Cerutti * * @copyright * 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, see . */ /** * @page conn_sasl_plain SASL plain authentication support * * SASL plain authentication support * * | Function | Description * | :-------------------- | :----------------------------------- * | mutt_sasl_plain_msg() | Create an SASL command */ #include "config.h" #include #include "mutt/base64.h" #include "mutt/string2.h" /** * mutt_sasl_plain_msg - Create an SASL command * @param buf Buffer to store the command * @param buflen Length of the buffer * @param cmd SASL command * @param authz Authorisation * @param user Username * @param pass Password * @retval >0 Success, number of chars in the command string * @retval 0 Error * * authz, user, and pass can each be up to 255 bytes, making up for a 765 bytes * string. Add the two NULL bytes in between plus one at the end and we get * 768. */ size_t mutt_sasl_plain_msg(char *buf, size_t buflen, const char *cmd, const char *authz, const char *user, const char *pass) { char tmp[768]; size_t len; size_t tmplen; if (!user || !*user || !pass || !*pass) return 0; tmplen = snprintf(tmp, sizeof(tmp), "%s%c%s%c%s", NONULL(authz), '\0', user, '\0', pass); len = snprintf(buf, buflen, "%s ", cmd); len += mutt_b64_encode(buf + len, tmp, tmplen, buflen - len); return len; } neomutt-neomutt-20171215/conn/sasl_plain.h000066400000000000000000000034341321473123000203750ustar00rootroot00000000000000/** * @file * SASL plain authentication support * * @authors * Copyright (C) 2016 Pietro Cerutti * * @copyright * 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, see . */ #ifndef _CONN_SASL_PLAIN_H #define _CONN_SASL_PLAIN_H #include /** * mutt_sasl_plain_msg - construct a base64 encoded SASL PLAIN message * @param buf Destination buffer * @param buflen Available space in the destination buffer * @param cmd Protocol-specific string the prepend to the PLAIN message * @param authz Authorization identity * @param user Authentication identity (username) * @param pass Password * @retval Number Bytes written to buf * * This function can be used to build a protocol-specific SASL Response message * using the PLAIN mechanism. The protocol specific command is given in the cmd * parameter. The function appends a space, encodes the string derived from * authz\0user\0pass using base64 encoding, and stores the result in buf. * * Example usages for IMAP and SMTP, respectively: */ size_t mutt_sasl_plain_msg(char *buf, size_t buflen, const char *cmd, const char *authz, const char *user, const char *pass); #endif /* _CONN_SASL_PLAIN_H */ neomutt-neomutt-20171215/conn/socket.c000066400000000000000000000335111321473123000175320ustar00rootroot00000000000000/** * @file * Low-level socket handling * * @authors * Copyright (C) 1998,2000 Michael R. Elkins * Copyright (C) 1999-2006,2008 Brendan Cully * Copyright (C) 1999-2000 Tommi Komulainen * * @copyright * 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, see . */ /** * @page conn_socket Low-level socket handling * * Low-level socket handling * * | Function | Description * | :--------------------- | :----------------------------------- * | mutt_socket_close() | Close a socket * | mutt_socket_open() | Simple wrapper * | mutt_socket_poll() | Checks whether reads would block * | mutt_socket_readchar() | simple read buffering to speed things up * | mutt_socket_readln_d() | Read a line from a socket * | mutt_socket_write_d() | Write data to a socket * | raw_socket_close() | Close a socket * | raw_socket_open() | Open a socket * | raw_socket_poll() | Checks whether reads would block * | raw_socket_read() | Read data from a socket * | raw_socket_write() | Write data to a socket * | socket_new_conn() | allocate and initialise a new connection */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mutt/debug.h" #include "mutt/memory.h" #include "mutt/message.h" #include "mutt/string2.h" #include "account.h" #include "conn_globals.h" #include "connection.h" #include "globals.h" #include "mutt_idna.h" #include "options.h" #include "protos.h" #ifdef USE_SSL #include "ssl.h" #endif /** * socket_preconnect - Execute a command before opening a socket * @retval 0 Success * @retval >0 An errno, e.g. EPERM */ static int socket_preconnect(void) { int rc; int save_errno; if (mutt_str_strlen(Preconnect)) { mutt_debug(2, "Executing preconnect: %s\n", Preconnect); rc = mutt_system(Preconnect); mutt_debug(2, "Preconnect result: %d\n", rc); if (rc != 0) { save_errno = errno; mutt_perror(_("Preconnect command failed.")); mutt_sleep(1); return save_errno; } } return 0; } /** * socket_connect - set up to connect to a socket fd * @param fd File descriptor to connect with * @param sa Address info * @retval 0 Success * @retval >0 An errno, e.g. EPERM * @retval -1 Error */ static int socket_connect(int fd, struct sockaddr *sa) { int sa_size; int save_errno; sigset_t set; if (sa->sa_family == AF_INET) sa_size = sizeof(struct sockaddr_in); #ifdef HAVE_GETADDRINFO else if (sa->sa_family == AF_INET6) sa_size = sizeof(struct sockaddr_in6); #endif else { mutt_debug(1, "Unknown address family!\n"); return -1; } if (ConnectTimeout > 0) alarm(ConnectTimeout); mutt_sig_allow_interrupt(1); /* FreeBSD's connect() does not respect SA_RESTART, meaning * a SIGWINCH will cause the connect to fail. */ sigemptyset(&set); sigaddset(&set, SIGWINCH); sigprocmask(SIG_BLOCK, &set, NULL); save_errno = 0; if (connect(fd, sa, sa_size) < 0) { save_errno = errno; mutt_debug(2, "Connection failed. errno: %d...\n", errno); SigInt = 0; /* reset in case we caught SIGINTR while in connect() */ } if (ConnectTimeout > 0) alarm(0); mutt_sig_allow_interrupt(0); sigprocmask(SIG_UNBLOCK, &set, NULL); return save_errno; } /** * mutt_socket_open - Simple wrapper * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ int mutt_socket_open(struct Connection *conn) { int rc; if (socket_preconnect()) return -1; rc = conn->conn_open(conn); mutt_debug(2, "Connected to %s:%d on fd=%d\n", NONULL(conn->account.host), conn->account.port, conn->fd); return rc; } /** * mutt_socket_close - Close a socket * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ int mutt_socket_close(struct Connection *conn) { int rc = -1; if (conn->fd < 0) mutt_debug(1, "Attempt to close closed connection.\n"); else rc = conn->conn_close(conn); conn->fd = -1; conn->ssf = 0; return rc; } /** * mutt_socket_write_d - Write data to a socket * @param conn Connection to a server * @param buf Buffer with data to write * @param len Length of data to write * @param dbg Debug level for logging * @retval >0 Number of bytes written * @retval -1 Error */ int mutt_socket_write_d(struct Connection *conn, const char *buf, int len, int dbg) { int rc; int sent = 0; mutt_debug(dbg, "%d> %s", conn->fd, buf); if (conn->fd < 0) { mutt_debug(1, "attempt to write to closed connection\n"); return -1; } if (len < 0) len = mutt_str_strlen(buf); while (sent < len) { rc = conn->conn_write(conn, buf + sent, len - sent); if (rc < 0) { mutt_debug(1, "error writing (%s), closing socket\n", strerror(errno)); mutt_socket_close(conn); return -1; } if (rc < len - sent) mutt_debug(3, "short write (%d of %d bytes)\n", rc, len - sent); sent += rc; } return sent; } /** * mutt_socket_poll - Checks whether reads would block * @param conn Connection to a server * @param wait_secs How long to wait for a response * @retval >0 There is data to read * @retval 0 Read would block * @retval -1 Connection doesn't support polling */ int mutt_socket_poll(struct Connection *conn, time_t wait_secs) { if (conn->bufpos < conn->available) return conn->available - conn->bufpos; if (conn->conn_poll) return conn->conn_poll(conn, wait_secs); return -1; } /** * mutt_socket_readchar - simple read buffering to speed things up * @param[in] conn Connection to a server * @param[out] c Character that was read * @retval 1 Success * @retval -1 Error */ int mutt_socket_readchar(struct Connection *conn, char *c) { if (conn->bufpos >= conn->available) { if (conn->fd >= 0) conn->available = conn->conn_read(conn, conn->inbuf, sizeof(conn->inbuf)); else { mutt_debug(1, "attempt to read from closed connection.\n"); return -1; } conn->bufpos = 0; if (conn->available == 0) { mutt_error(_("Connection to %s closed"), conn->account.host); mutt_sleep(2); } if (conn->available <= 0) { mutt_socket_close(conn); return -1; } } *c = conn->inbuf[conn->bufpos]; conn->bufpos++; return 1; } /** * mutt_socket_readln_d - Read a line from a socket * @param buf Buffer to store the line * @param buflen Length of data to write * @param conn Connection to a server * @param dbg Debug level for logging * @retval >0 Success, number of bytes read * @retval -1 Error */ int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg) { char ch; int i; for (i = 0; i < buflen - 1; i++) { if (mutt_socket_readchar(conn, &ch) != 1) { buf[i] = '\0'; return -1; } if (ch == '\n') break; buf[i] = ch; } /* strip \r from \r\n termination */ if (i && buf[i - 1] == '\r') i--; buf[i] = '\0'; mutt_debug(dbg, "%d< %s\n", conn->fd, buf); /* number of bytes read, not strlen */ return i + 1; } /** * socket_new_conn - allocate and initialise a new connection * @retval ptr New Connection */ struct Connection *socket_new_conn(void) { struct Connection *conn = NULL; conn = mutt_mem_calloc(1, sizeof(struct Connection)); conn->fd = -1; return conn; } /** * raw_socket_close - Close a socket * @param conn Connection to a server * @retval 0 Success * @retval -1 Error, see errno */ int raw_socket_close(struct Connection *conn) { return close(conn->fd); } /** * raw_socket_read - Read data from a socket * @param conn Connection to a server * @param buf Buffer to store the data * @param len Number of bytes to read * @retval >0 Success, number of bytes read * @retval -1 Error, see errno */ int raw_socket_read(struct Connection *conn, char *buf, size_t len) { int rc; mutt_sig_allow_interrupt(1); rc = read(conn->fd, buf, len); if (rc == -1) { mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno)); mutt_sleep(2); SigInt = 0; } mutt_sig_allow_interrupt(0); if (SigInt) { mutt_error(_("Connection to %s has been aborted"), conn->account.host); mutt_sleep(2); SigInt = 0; rc = -1; } return rc; } /** * raw_socket_write - Write data to a socket * @param conn Connection to a server * @param buf Buffer to read into * @param count Number of bytes to read * @retval >0 Success, number of bytes written * @retval -1 Error, see errno */ int raw_socket_write(struct Connection *conn, const char *buf, size_t count) { int rc; mutt_sig_allow_interrupt(1); rc = write(conn->fd, buf, count); if (rc == -1) { mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno)); mutt_sleep(2); SigInt = 0; } mutt_sig_allow_interrupt(0); if (SigInt) { mutt_error(_("Connection to %s has been aborted"), conn->account.host); mutt_sleep(2); SigInt = 0; rc = -1; } return rc; } /** * raw_socket_poll - Checks whether reads would block * @param conn Connection to a server * @param wait_secs How long to wait for a response * @retval >0 There is data to read * @retval 0 Read would block * @retval -1 Connection doesn't support polling */ int raw_socket_poll(struct Connection *conn, time_t wait_secs) { fd_set rfds; unsigned long wait_millis, post_t_millis; struct timeval tv, pre_t, post_t; int rc; if (conn->fd < 0) return -1; wait_millis = wait_secs * 1000UL; while (true) { tv.tv_sec = wait_millis / 1000; tv.tv_usec = (wait_millis % 1000) * 1000; FD_ZERO(&rfds); FD_SET(conn->fd, &rfds); gettimeofday(&pre_t, NULL); rc = select(conn->fd + 1, &rfds, NULL, NULL, &tv); gettimeofday(&post_t, NULL); if (rc > 0 || (rc < 0 && errno != EINTR)) return rc; if (SigInt) mutt_query_exit(); wait_millis += (pre_t.tv_sec * 1000UL) + (pre_t.tv_usec / 1000); post_t_millis = (post_t.tv_sec * 1000UL) + (post_t.tv_usec / 1000); if (wait_millis <= post_t_millis) return 0; wait_millis -= post_t_millis; } } /** * raw_socket_open - Open a socket * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ int raw_socket_open(struct Connection *conn) { int rc; int fd; char *host_idna = NULL; #ifdef HAVE_GETADDRINFO /* --- IPv4/6 --- */ /* "65536\0" */ char port[6]; struct addrinfo hints; struct addrinfo *res = NULL; struct addrinfo *cur = NULL; /* we accept v4 or v6 STREAM sockets */ memset(&hints, 0, sizeof(hints)); if (option(OPT_USE_IPV6)) hints.ai_family = AF_UNSPEC; else hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; snprintf(port, sizeof(port), "%d", conn->account.port); #ifdef HAVE_LIBIDN if (idna_to_ascii_lz(conn->account.host, &host_idna, 1) != IDNA_SUCCESS) { mutt_error(_("Bad IDN \"%s\"."), conn->account.host); return -1; } #else host_idna = conn->account.host; #endif if (!option(OPT_NO_CURSES)) mutt_message(_("Looking up %s..."), conn->account.host); rc = getaddrinfo(host_idna, port, &hints, &res); #ifdef HAVE_LIBIDN FREE(&host_idna); #endif if (rc) { mutt_error(_("Could not find the host \"%s\""), conn->account.host); mutt_sleep(2); return -1; } if (!option(OPT_NO_CURSES)) mutt_message(_("Connecting to %s..."), conn->account.host); rc = -1; for (cur = res; cur != NULL; cur = cur->ai_next) { fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol); if (fd >= 0) { rc = socket_connect(fd, cur->ai_addr); if (rc == 0) { fcntl(fd, F_SETFD, FD_CLOEXEC); conn->fd = fd; break; } else close(fd); } } freeaddrinfo(res); #else /* --- IPv4 only --- */ struct sockaddr_in sin; struct hostent *he = NULL; memset(&sin, 0, sizeof(sin)); sin.sin_port = htons(conn->account.port); sin.sin_family = AF_INET; #ifdef HAVE_LIBIDN if (idna_to_ascii_lz(conn->account.host, &host_idna, 1) != IDNA_SUCCESS) { mutt_error(_("Bad IDN \"%s\"."), conn->account.host); return -1; } #else host_idna = conn->account.host; #endif if (!option(OPT_NO_CURSES)) mutt_message(_("Looking up %s..."), conn->account.host); he = gethostbyname(host_idna); #ifdef HAVE_LIBIDN FREE(&host_idna); #endif if (!he) { mutt_error(_("Could not find the host \"%s\""), conn->account.host); return -1; } if (!option(OPT_NO_CURSES)) mutt_message(_("Connecting to %s..."), conn->account.host); rc = -1; for (int i = 0; he->h_addr_list[i] != NULL; i++) { memcpy(&sin.sin_addr, he->h_addr_list[i], he->h_length); fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP); if (fd >= 0) { rc = socket_connect(fd, (struct sockaddr *) &sin); if (rc == 0) { fcntl(fd, F_SETFD, FD_CLOEXEC); conn->fd = fd; break; } else close(fd); } } #endif if (rc) { mutt_error(_("Could not connect to %s (%s)."), conn->account.host, (rc > 0) ? strerror(rc) : _("unknown error")); mutt_sleep(2); return -1; } return 0; } neomutt-neomutt-20171215/conn/socket.h000066400000000000000000000032641321473123000175410ustar00rootroot00000000000000/** * @file * Low-level socket handling * * @authors * Copyright (C) 1998 Brandon Long * Copyright (C) 1999-2005 Brendan Cully * * @copyright * 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, see . */ #ifndef _CONN_SOCKET_H #define _CONN_SOCKET_H #include #include struct Connection; struct Connection *socket_new_conn(void); int mutt_socket_open(struct Connection *conn); int mutt_socket_close(struct Connection *conn); int mutt_socket_poll(struct Connection *conn, time_t wait_secs); int mutt_socket_readchar(struct Connection *conn, char *c); int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg); int mutt_socket_write_d(struct Connection *conn, const char *buf, int len, int dbg); int raw_socket_read(struct Connection *conn, char *buf, size_t len); int raw_socket_write(struct Connection *conn, const char *buf, size_t count); int raw_socket_open(struct Connection *conn); int raw_socket_close(struct Connection *conn); int raw_socket_poll(struct Connection *conn, time_t wait_secs); #endif /* _CONN_SOCKET_H */ neomutt-neomutt-20171215/conn/ssl.c000066400000000000000000001200121321473123000170340ustar00rootroot00000000000000/** * @file * Handling of OpenSSL encryption * * @authors * Copyright (C) 1999-2001 Tommi Komulainen * * @copyright * 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, see . */ /** * @page conn_ssl Handling of OpenSSL encryption * * Handling of OpenSSL encryption * * | Function | Description * | :---------------------- | :----------------------------------- * | mutt_ssl_socket_setup() | Set up the socket multiplexor * | mutt_ssl_starttls() | Negotiate TLS over an already opened connection */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mutt/debug.h" #include "mutt/file.h" #include "mutt/memory.h" #include "mutt/message.h" #include "mutt/string2.h" #include "mutt.h" #include "ssl.h" #include "account.h" #include "conn_globals.h" #include "connection.h" #include "globals.h" #include "keymap.h" #include "mutt_account.h" #include "mutt_idna.h" #include "mutt_menu.h" #include "opcodes.h" #include "options.h" #include "protos.h" #include "socket.h" /* Just in case OpenSSL doesn't define DEVRANDOM */ #ifndef DEVRANDOM #define DEVRANDOM "/dev/urandom" #endif /* This is ugly, but as RAND_status came in on OpenSSL version 0.9.5 * and the code has to support older versions too, this is seemed to * be cleaner way compared to having even uglier #ifdefs all around. */ #ifdef HAVE_RAND_STATUS #define HAVE_ENTROPY() (RAND_status() == 1) #else static int entropy_byte_count = 0; /* OpenSSL fills the entropy pool from /dev/urandom if it exists */ #define HAVE_ENTROPY() (!access(DEVRANDOM, R_OK) || entropy_byte_count >= 16) #endif /* index for storing hostname as application specific data in SSL structure */ static int HostExDataIndex = -1; /* Index for storing the "skip mode" state in SSL structure. When the * user skips a certificate in the chain, the stored value will be * non-null. */ static int SkipModeExDataIndex = -1; /* keep a handle on accepted certificates in case we want to * open up another connection to the same server in this session */ static STACK_OF(X509) *SslSessionCerts = NULL; /** * struct SslSockData - SSL socket data */ struct SslSockData { SSL_CTX *ctx; SSL *ssl; X509 *cert; unsigned char isopen; }; /** * ssl_load_certificates - Load certificates and filter out the expired ones * @param ctx SSL context * @retval 1 Success * @retval 0 Error * * ssl certificate verification can behave strangely if there are expired certs * loaded into the trusted store. This function filters out expired certs. * * Previously the code used this form: * SSL_CTX_load_verify_locations (ssldata->ctx, CertificateFile, NULL); */ static int ssl_load_certificates(SSL_CTX *ctx) { FILE *fp = NULL; X509 *cert = NULL; X509_STORE *store = NULL; int rc = 1; char buf[STRING]; mutt_debug(2, "loading trusted certificates\n"); store = SSL_CTX_get_cert_store(ctx); if (!store) { store = X509_STORE_new(); SSL_CTX_set_cert_store(ctx, store); } fp = fopen(CertificateFile, "rt"); if (!fp) return 0; while (NULL != PEM_read_X509(fp, &cert, NULL, NULL)) { if ((X509_cmp_current_time(X509_get_notBefore(cert)) >= 0) || (X509_cmp_current_time(X509_get_notAfter(cert)) <= 0)) { mutt_debug(2, "filtering expired cert: %s\n", X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf))); } else { X509_STORE_add_cert(store, cert); } } /* PEM_read_X509 sets the error NO_START_LINE on eof */ if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE) rc = 0; ERR_clear_error(); X509_free(cert); mutt_file_fclose(&fp); return rc; } /** * ssl_set_verify_partial - Allow verification using partial chains (with no root) * @param ctx SSL context * @retval 0 Success * @retval -1 Error */ static int ssl_set_verify_partial(SSL_CTX *ctx) { int rc = 0; #ifdef HAVE_SSL_PARTIAL_CHAIN X509_VERIFY_PARAM *param = NULL; if (option(OPT_SSL_VERIFY_PARTIAL_CHAINS)) { param = X509_VERIFY_PARAM_new(); if (param) { X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN); if (0 == SSL_CTX_set1_param(ctx, param)) { mutt_debug(2, "SSL_CTX_set1_param() failed.\n"); rc = -1; } X509_VERIFY_PARAM_free(param); } else { mutt_debug(2, "X509_VERIFY_PARAM_new() failed.\n"); rc = -1; } } #endif return rc; } /** * add_entropy - Add a source of random numbers * @param file Random device * @retval >0 Success, number of bytes read from the source * @retval -1 Error */ static int add_entropy(const char *file) { struct stat st; int n = -1; if (!file) return 0; if (stat(file, &st) == -1) return errno == ENOENT ? 0 : -1; mutt_message(_("Filling entropy pool: %s...\n"), file); /* check that the file permissions are secure */ if (st.st_uid != getuid() || ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) || ((st.st_mode & (S_IWOTH | S_IROTH)) != 0)) { mutt_error(_("%s has insecure permissions!"), file); mutt_sleep(2); return -1; } #ifdef HAVE_RAND_EGD n = RAND_egd(file); #endif if (n <= 0) n = RAND_load_file(file, -1); #ifndef HAVE_RAND_STATUS if (n > 0) entropy_byte_count += n; #endif return n; } /** * ssl_err - Display an SSL error message * @param data SSL socket data * @param err SSL error code */ static void ssl_err(struct SslSockData *data, int err) { int e = SSL_get_error(data->ssl, err); switch (e) { case SSL_ERROR_NONE: return; case SSL_ERROR_ZERO_RETURN: data->isopen = 0; break; case SSL_ERROR_SYSCALL: data->isopen = 0; break; } const char *errmsg = NULL; unsigned long sslerr; switch (e) { case SSL_ERROR_ZERO_RETURN: errmsg = "SSL connection closed"; break; case SSL_ERROR_WANT_READ: errmsg = "retry read"; break; case SSL_ERROR_WANT_WRITE: errmsg = "retry write"; break; case SSL_ERROR_WANT_CONNECT: errmsg = "retry connect"; break; case SSL_ERROR_WANT_ACCEPT: errmsg = "retry accept"; break; case SSL_ERROR_WANT_X509_LOOKUP: errmsg = "retry x509 lookup"; break; case SSL_ERROR_SYSCALL: errmsg = "I/O error"; break; case SSL_ERROR_SSL: sslerr = ERR_get_error(); switch (sslerr) { case 0: switch (err) { case 0: errmsg = "EOF"; break; default: errmsg = strerror(errno); } break; default: errmsg = ERR_error_string(sslerr, NULL); } break; default: errmsg = "unknown error"; } mutt_debug(1, "SSL error: %s\n", errmsg); } /** * ssl_dprint_err_stack - Dump the SSL error stack */ static void ssl_dprint_err_stack(void) { BIO *bio = NULL; char *buf = NULL; long buflen; char *output = NULL; bio = BIO_new(BIO_s_mem()); if (!bio) return; ERR_print_errors(bio); buflen = BIO_get_mem_data(bio, &buf); if (buflen > 0) { output = mutt_mem_malloc(buflen + 1); memcpy(output, buf, buflen); output[buflen] = '\0'; mutt_debug(1, "SSL error stack: %s\n", output); FREE(&output); } BIO_free(bio); } /** * ssl_passwd_cb - Callback to get a password * @param buf Buffer for the password * @param size Length of the buffer * @param rwflag 0 if writing, 1 if reading (UNUSED) * @param userdata Account whose password is requested * @retval >0 Success, number of chars written to buf * @retval 0 Error */ static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata) { struct Account *account = (struct Account *) userdata; if (mutt_account_getuser(account) < 0) return 0; mutt_debug(2, "getting password for %s@%s:%u\n", account->user, account->host, account->port); if (mutt_account_getpass(account) < 0) return 0; return snprintf(buf, size, "%s", account->pass); } /** * ssl_socket_open_err - Error callback for opening an SSL connection * @param conn Connection to a server * @retval -1 Always */ static int ssl_socket_open_err(struct Connection *conn) { mutt_error(_("SSL disabled due to the lack of entropy")); mutt_sleep(2); return -1; } /** * ssl_socket_close - Close an SSL connection * @param conn Connection to a server * @retval 0 Success * @retval -1 Error, see errno */ static int ssl_socket_close(struct Connection *conn) { struct SslSockData *data = conn->sockdata; if (data) { if (data->isopen) SSL_shutdown(data->ssl); /* hold onto this for the life of neomutt, in case we want to reconnect. * The purist in me wants a mutt_exit hook. */ SSL_free(data->ssl); SSL_CTX_free(data->ctx); FREE(&conn->sockdata); } return raw_socket_close(conn); } /** * x509_get_part - Retrieve from X509 data * @param name Name of data to retrieve * @param nid ID of the item to retrieve * @retval ptr Retrieved data * * The returned pointer is to a static buffer, so it must not be free()'d. */ static char *x509_get_part(X509_NAME *name, int nid) { static char data[SHORT_STRING]; if (!name || X509_NAME_get_text_by_NID(name, nid, data, sizeof(data)) < 0) mutt_str_strfcpy(data, _("Unknown"), sizeof(data)); return data; } /** * x509_fingerprint - Generate a fingerprint for an X509 certificate * @param s Buffer for fingerprint * @param l Length of buffer * @param cert Certificate * @param hashfunc Hashing function */ static void x509_fingerprint(char *s, int l, X509 *cert, const EVP_MD *(*hashfunc)(void) ) { unsigned char md[EVP_MAX_MD_SIZE]; unsigned int n; if (!X509_digest(cert, hashfunc(), md, &n)) { snprintf(s, l, "%s", _("[unable to calculate]")); } else { for (unsigned int i = 0; i < n; i++) { char ch[8]; snprintf(ch, 8, "%02X%s", md[i], (i % 2 ? " " : "")); mutt_str_strcat(s, l, ch); } } } /** * asn1time_to_string - Convert a time to a string * @param tm Time to convert * @retval ptr Time string * * The returned pointer is to a static buffer, so it must not be free()'d. */ static char *asn1time_to_string(ASN1_UTCTIME *tm) { static char buf[64]; BIO *bio = NULL; mutt_str_strfcpy(buf, _("[invalid date]"), sizeof(buf)); bio = BIO_new(BIO_s_mem()); if (bio) { if (ASN1_TIME_print(bio, tm)) (void) BIO_read(bio, buf, sizeof(buf)); BIO_free(bio); } return buf; } /** * compare_certificates - Compare two X509 certificated * @param cert Certificate * @param peercert Peer certificate * @param peermd Peer certificate message digest * @param peermdlen Length of peer certificate message digest * @retval true Certificates match * @retval false Certificates differ */ static bool compare_certificates(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen) { unsigned char md[EVP_MAX_MD_SIZE]; unsigned int mdlen; /* Avoid CPU-intensive digest calculation if the certificates are * not even remotely equal. */ if (X509_subject_name_cmp(cert, peercert) != 0 || X509_issuer_name_cmp(cert, peercert) != 0) return false; if (!X509_digest(cert, EVP_sha256(), md, &mdlen) || peermdlen != mdlen) return false; if (memcmp(peermd, md, mdlen) != 0) return false; return true; } /** * check_certificate_expiration - Check if a certificate has expired * @param peercert Certificate to check * @param silent If true, don't notify the user if the certificate has expired * @retval true Certificate is valid * @retval false Certificate has expired (or hasn't yet become valid) */ static bool check_certificate_expiration(X509 *peercert, bool silent) { if (option(OPT_SSL_VERIFY_DATES) != MUTT_NO) { if (X509_cmp_current_time(X509_get_notBefore(peercert)) >= 0) { if (!silent) { mutt_debug(2, "Server certificate is not yet valid\n"); mutt_error(_("Server certificate is not yet valid")); mutt_sleep(2); } return false; } if (X509_cmp_current_time(X509_get_notAfter(peercert)) <= 0) { if (!silent) { mutt_debug(2, "Server certificate has expired\n"); mutt_error(_("Server certificate has expired")); mutt_sleep(2); } return false; } } return true; } /** * hostname_match - Does the hostname match the certificate * @param hostname Hostname * @param certname Certificate * @retval true Hostname matches the certificate */ static bool hostname_match(const char *hostname, const char *certname) { const char *cmp1 = NULL, *cmp2 = NULL; if (strncmp(certname, "*.", 2) == 0) { cmp1 = certname + 2; cmp2 = strchr(hostname, '.'); if (!cmp2) { return false; } else { cmp2++; } } else { cmp1 = certname; cmp2 = hostname; } if (*cmp1 == '\0' || *cmp2 == '\0') { return false; } if (strcasecmp(cmp1, cmp2) != 0) { return false; } return true; } /** * ssl_init - Initialise the SSL library * @retval 0 Success * @retval -1 Error * * OpenSSL library needs to be fed with sufficient entropy. On systems with * /dev/urandom, this is done transparently by the library itself, on other * systems we need to fill the entropy pool ourselves. * * Even though only OpenSSL 0.9.5 and later will complain about the lack of * entropy, we try to our best and fill the pool with older versions also. * (That's the reason for the ugly ifdefs and macros, otherwise I could have * simply ifdef'd the whole ssl_init funcion) */ static int ssl_init(void) { char path[_POSIX_PATH_MAX]; static bool init_complete = false; if (init_complete) return 0; if (!HAVE_ENTROPY()) { /* load entropy from files */ add_entropy(EntropyFile); add_entropy(RAND_file_name(path, sizeof(path))); /* load entropy from egd sockets */ #ifdef HAVE_RAND_EGD add_entropy(getenv("EGDSOCKET")); snprintf(path, sizeof(path), "%s/.entropy", NONULL(HomeDir)); add_entropy(path); add_entropy("/tmp/entropy"); #endif /* shuffle $RANDFILE (or ~/.rnd if unset) */ RAND_write_file(RAND_file_name(path, sizeof(path))); mutt_clear_error(); if (!HAVE_ENTROPY()) { mutt_error(_("Failed to find enough entropy on your system")); mutt_sleep(2); return -1; } } /* I don't think you can do this just before reading the error. The call * itself might clobber the last SSL error. */ SSL_load_error_strings(); SSL_library_init(); init_complete = true; return 0; } /** * ssl_socket_read - Read data from an SSL socket * @param conn Connection to a server * @param buf Buffer to store the data * @param len Number of bytes to read * @retval >0 Success, number of bytes read * @retval -1 Error, see errno */ static int ssl_socket_read(struct Connection *conn, char *buf, size_t len) { struct SslSockData *data = conn->sockdata; int rc; rc = SSL_read(data->ssl, buf, len); if (rc <= 0 || errno == EINTR) { if (errno == EINTR) { rc = -1; } data->isopen = 0; ssl_err(data, rc); } return rc; } /** * ssl_socket_write - Write data to an SSL socket * @param conn Connection to a server * @param buf Buffer to read into * @param len Number of bytes to read * @retval >0 Success, number of bytes written * @retval -1 Error, see errno */ static int ssl_socket_write(struct Connection *conn, const char *buf, size_t len) { struct SslSockData *data = conn->sockdata; int rc; rc = SSL_write(data->ssl, buf, len); if (rc <= 0 || errno == EINTR) { if (errno == EINTR) { rc = -1; } ssl_err(data, rc); } return rc; } /** * ssl_get_client_cert - Get the client certificate for an SSL connection * @param ssldata SSL socket data * @param conn Connection to a server */ static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn) { if (SslClientCert) { mutt_debug(2, "Using client certificate %s\n", SslClientCert); SSL_CTX_set_default_passwd_cb_userdata(ssldata->ctx, &conn->account); SSL_CTX_set_default_passwd_cb(ssldata->ctx, ssl_passwd_cb); SSL_CTX_use_certificate_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM); SSL_CTX_use_PrivateKey_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM); /* if we are using a client cert, SASL may expect an external auth name */ if (mutt_account_getuser(&conn->account) < 0) mutt_debug(1, "Couldn't get user info\n"); } } /** * tls_close - Close a TLS Connection * @param conn Connection to a server * @retval 0 Success * @retval -1 Error, see errno */ static int tls_close(struct Connection *conn) { int rc; rc = ssl_socket_close(conn); conn->conn_read = raw_socket_read; conn->conn_write = raw_socket_write; conn->conn_close = raw_socket_close; return rc; } /** * check_certificate_cache - Is the X509 Certificate in the cache? * @param peercert Certificate * @retval true Certificate is in the cache */ static bool check_certificate_cache(X509 *peercert) { unsigned char peermd[EVP_MAX_MD_SIZE]; unsigned int peermdlen; X509 *cert = NULL; if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen) || !SslSessionCerts) { return false; } for (int i = sk_X509_num(SslSessionCerts); i-- > 0;) { cert = sk_X509_value(SslSessionCerts, i); if (compare_certificates(cert, peercert, peermd, peermdlen)) { return true; } } return false; } /** * check_certificate_file - Read and check a certificate file * @param peercert Certificate * @retval 1 Certificate is valid * @retval 0 Error, or certificate is invalid */ static int check_certificate_file(X509 *peercert) { unsigned char peermd[EVP_MAX_MD_SIZE]; unsigned int peermdlen; X509 *cert = NULL; int pass = 0; FILE *fp = NULL; if (!CertificateFile) return 0; fp = fopen(CertificateFile, "rt"); if (!fp) return 0; if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen)) { mutt_file_fclose(&fp); return 0; } while (PEM_read_X509(fp, &cert, NULL, NULL) != NULL) { if (compare_certificates(cert, peercert, peermd, peermdlen) && check_certificate_expiration(cert, true)) { pass = 1; break; } } /* PEM_read_X509 sets an error on eof */ if (!pass) ERR_clear_error(); X509_free(cert); mutt_file_fclose(&fp); return pass; } /** * check_host - Check the host on the certificate * @param x509cert Certificate * @param hostname Hostname * @param err Buffer for error message * @param errlen Length of buffer * @retval 1 Hostname matches the certificate * @retval 0 Error */ static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen) { int rc = 0; /* hostname in ASCII format: */ char *hostname_ascii = NULL; /* needed to get the common name: */ X509_NAME *x509_subject = NULL; char *buf = NULL; int bufsize; /* needed to get the DNS subjectAltNames: */ STACK_OF(GENERAL_NAME) * subj_alt_names; int subj_alt_names_count; GENERAL_NAME *subj_alt_name = NULL; /* did we find a name matching hostname? */ bool match_found; /* Check if 'hostname' matches the one of the subjectAltName extensions of * type DNS or the Common Name (CN). */ #ifdef HAVE_LIBIDN if (idna_to_ascii_lz(hostname, &hostname_ascii, 0) != IDNA_SUCCESS) { hostname_ascii = mutt_str_strdup(hostname); } #else hostname_ascii = mutt_str_strdup(hostname); #endif /* Try the DNS subjectAltNames. */ match_found = false; if ((subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL))) { subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names); for (int i = 0; i < subj_alt_names_count; i++) { subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i); if (subj_alt_name->type == GEN_DNS) { if (subj_alt_name->d.ia5->length >= 0 && mutt_str_strlen((char *) subj_alt_name->d.ia5->data) == (size_t) subj_alt_name->d.ia5->length && (match_found = hostname_match(hostname_ascii, (char *) (subj_alt_name->d.ia5->data)))) { break; } } } GENERAL_NAMES_free(subj_alt_names); } if (!match_found) { /* Try the common name */ x509_subject = X509_get_subject_name(x509cert); if (!x509_subject) { if (err && errlen) mutt_str_strfcpy(err, _("cannot get certificate subject"), errlen); goto out; } /* first get the space requirements */ bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0); if (bufsize == -1) { if (err && errlen) mutt_str_strfcpy(err, _("cannot get certificate common name"), errlen); goto out; } bufsize++; /* space for the terminal nul char */ buf = mutt_mem_malloc((size_t) bufsize); if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1) { if (err && errlen) mutt_str_strfcpy(err, _("cannot get certificate common name"), errlen); goto out; } /* cast is safe since bufsize is incremented above, so bufsize-1 is always * zero or greater. */ if (mutt_str_strlen(buf) == (size_t) bufsize - 1) { match_found = hostname_match(hostname_ascii, buf); } } if (!match_found) { if (err && errlen) snprintf(err, errlen, _("certificate owner does not match hostname %s"), hostname); goto out; } rc = 1; out: FREE(&buf); FREE(&hostname_ascii); return rc; } /** * check_certificate_by_digest - Validate a certificate by its digest * @param peercert Certificate * @retval 1 Certificate is valid * @retval 0 Error */ static int check_certificate_by_digest(X509 *peercert) { return check_certificate_expiration(peercert, false) && check_certificate_file(peercert); } /** * ssl_cache_trusted_cert - Cache a trusted certificate * @param c Certificate * @retval >0 Number of elements in the cache * @retval 0 Error */ static int ssl_cache_trusted_cert(X509 *c) { mutt_debug(1, "trusted\n"); if (!SslSessionCerts) SslSessionCerts = sk_X509_new_null(); return (sk_X509_push(SslSessionCerts, X509_dup(c))); } /** * interactive_check_cert - Ask the user if a certificate is valid * @param cert Certificate * @param idx Place of certificate in the chain * @param len Length of the certificate chain * @param ssl SSL state * @param allow_always If certificate may be always allowed * @retval true User selected 'skip' * @retval false Otherwise */ static int interactive_check_cert(X509 *cert, int idx, size_t len, SSL *ssl, int allow_always) { static const int part[] = { NID_commonName, /* CN */ NID_pkcs9_emailAddress, /* Email */ NID_organizationName, /* O */ NID_organizationalUnitName, /* OU */ NID_localityName, /* L */ NID_stateOrProvinceName, /* ST */ NID_countryName, /* C */ }; X509_NAME *x509_subject = NULL; X509_NAME *x509_issuer = NULL; char helpstr[LONG_STRING]; char buf[STRING]; char title[STRING]; struct Menu *menu = mutt_new_menu(MENU_GENERIC); int done, row; FILE *fp = NULL; int ALLOW_SKIP = 0; /**< All caps tells Coverity that this is effectively a preproc condition */ mutt_push_current_menu(menu); menu->max = mutt_array_size(part) * 2 + 10; menu->dialog = mutt_mem_calloc(1, menu->max * sizeof(char *)); for (int i = 0; i < menu->max; i++) menu->dialog[i] = mutt_mem_calloc(1, SHORT_STRING * sizeof(char)); row = 0; mutt_str_strfcpy(menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING); row++; x509_subject = X509_get_subject_name(cert); for (unsigned int u = 0; u < mutt_array_size(part); u++) snprintf(menu->dialog[row++], SHORT_STRING, " %s", x509_get_part(x509_subject, part[u])); row++; mutt_str_strfcpy(menu->dialog[row], _("This certificate was issued by:"), SHORT_STRING); row++; x509_issuer = X509_get_issuer_name(cert); for (unsigned int u = 0; u < mutt_array_size(part); u++) snprintf(menu->dialog[row++], SHORT_STRING, " %s", x509_get_part(x509_issuer, part[u])); row++; snprintf(menu->dialog[row++], SHORT_STRING, "%s", _("This certificate is valid")); snprintf(menu->dialog[row++], SHORT_STRING, _(" from %s"), asn1time_to_string(X509_get_notBefore(cert))); snprintf(menu->dialog[row++], SHORT_STRING, _(" to %s"), asn1time_to_string(X509_get_notAfter(cert))); row++; buf[0] = '\0'; x509_fingerprint(buf, sizeof(buf), cert, EVP_sha1); snprintf(menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), buf); buf[0] = '\0'; x509_fingerprint(buf, sizeof(buf), cert, EVP_md5); snprintf(menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), buf); snprintf(title, sizeof(title), _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len); menu->title = title; /* The leaf/host certificate can't be skipped. */ #ifdef HAVE_SSL_PARTIAL_CHAIN if ((idx != 0) && (option(OPT_SSL_VERIFY_PARTIAL_CHAINS))) ALLOW_SKIP = 1; #endif /* Inside ssl_verify_callback(), this function is guarded by a call to * check_certificate_by_digest(). This means if check_certificate_expiration() is * true, then check_certificate_file() must be false. Therefore we don't need * to also scan the certificate file here. */ allow_always = allow_always && CertificateFile && check_certificate_expiration(cert, true); /* L10N: * These four letters correspond to the choices in the next four strings: * (r)eject, accept (o)nce, (a)ccept always, (s)kip. * These prompts are the interactive certificate confirmation prompts for * an OpenSSL connection. */ menu->keys = _("roas"); if (allow_always) { if (ALLOW_SKIP) menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always, (s)kip"); else menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always"); } else { if (ALLOW_SKIP) menu->prompt = _("(r)eject, accept (o)nce, (s)kip"); else menu->prompt = _("(r)eject, accept (o)nce"); } helpstr[0] = '\0'; mutt_make_help(buf, sizeof(buf), _("Exit "), MENU_GENERIC, OP_EXIT); mutt_str_strcat(helpstr, sizeof(helpstr), buf); mutt_make_help(buf, sizeof(buf), _("Help"), MENU_GENERIC, OP_HELP); mutt_str_strcat(helpstr, sizeof(helpstr), buf); menu->help = helpstr; done = 0; set_option(OPT_IGNORE_MACRO_EVENTS); while (!done) { switch (mutt_menu_loop(menu)) { case -1: /* abort */ case OP_MAX + 1: /* reject */ case OP_EXIT: done = 1; break; case OP_MAX + 3: /* accept always */ if (!allow_always) break; done = 0; if ((fp = fopen(CertificateFile, "a"))) { if (PEM_write_X509(fp, cert)) done = 1; mutt_file_fclose(&fp); } if (!done) { mutt_error(_("Warning: Couldn't save certificate")); mutt_sleep(2); } else { mutt_message(_("Certificate saved")); mutt_sleep(0); } /* fall through */ case OP_MAX + 2: /* accept once */ done = 2; SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL); ssl_cache_trusted_cert(cert); break; case OP_MAX + 4: /* skip */ if (!ALLOW_SKIP) break; done = 2; SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex); break; } } unset_option(OPT_IGNORE_MACRO_EVENTS); mutt_pop_current_menu(menu); mutt_menu_destroy(&menu); mutt_debug(2, "done=%d\n", done); return (done == 2); } /** * ssl_verify_callback - Certificate verification callback * @param preverify_ok If true, don't question the user if they skipped verification * @param ctx X509 store context * @retval true Certificate is valid * @retval false Error, or Certificate is invalid * * called for each certificate in the chain sent by the peer, starting from the * root; returning 1 means that the given certificate is trusted, returning 0 * immediately aborts the SSL connection */ static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { char buf[STRING]; const char *host = NULL; size_t len; int pos; X509 *cert = NULL; SSL *ssl = NULL; int skip_mode; #ifdef HAVE_SSL_PARTIAL_CHAIN static int last_pos = 0; static X509 *last_cert = NULL; unsigned char last_cert_md[EVP_MAX_MD_SIZE]; unsigned int last_cert_mdlen; #endif ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); if (!ssl) { mutt_debug(1, "failed to retrieve SSL structure from X509_STORE_CTX\n"); return 0; } host = SSL_get_ex_data(ssl, HostExDataIndex); if (!host) { mutt_debug(1, "failed to retrieve hostname from SSL structure\n"); return 0; } /* This is true when a previous entry in the certificate chain did * not verify and the user manually chose to skip it via the * $ssl_verify_partial_chains option. * In this case, all following certificates need to be treated as non-verified * until one is actually verified. */ skip_mode = (SSL_get_ex_data(ssl, SkipModeExDataIndex) != NULL); cert = X509_STORE_CTX_get_current_cert(ctx); pos = X509_STORE_CTX_get_error_depth(ctx); len = sk_X509_num(X509_STORE_CTX_get_chain(ctx)); mutt_debug(1, "checking cert chain entry %s (preverify: %d skipmode: %d)\n", X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)), preverify_ok, skip_mode); #ifdef HAVE_SSL_PARTIAL_CHAIN /* Sometimes, when a certificate is (s)kipped, OpenSSL will pass it * a second time with preverify_ok = 1. Don't show it or the user * will think their "s" key is broken. */ if (option(OPT_SSL_VERIFY_PARTIAL_CHAINS)) { if (skip_mode && preverify_ok && (pos == last_pos) && last_cert) { if (X509_digest(last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) && compare_certificates(cert, last_cert, last_cert_md, last_cert_mdlen)) { mutt_debug(2, "ignoring duplicate skipped certificate.\n"); return 1; } } last_pos = pos; if (last_cert) X509_free(last_cert); last_cert = X509_dup(cert); } #endif /* check session cache first */ if (check_certificate_cache(cert)) { mutt_debug(2, "using cached certificate\n"); SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL); return 1; } /* check hostname only for the leaf certificate */ buf[0] = 0; if (pos == 0 && option(OPT_SSL_VERIFY_HOST) != MUTT_NO) { if (!check_host(cert, host, buf, sizeof(buf))) { mutt_error(_("Certificate host check failed: %s"), buf); mutt_sleep(2); /* we disallow (a)ccept always in the prompt, because it will have no effect * for hostname mismatches. */ return interactive_check_cert(cert, pos, len, ssl, 0); } mutt_debug(2, "hostname check passed\n"); } if (!preverify_ok || skip_mode) { /* automatic check from user's database */ if (CertificateFile && check_certificate_by_digest(cert)) { mutt_debug(2, "digest check passed\n"); SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL); return 1; } /* log verification error */ int err = X509_STORE_CTX_get_error(ctx); snprintf(buf, sizeof(buf), "%s (%d)", X509_verify_cert_error_string(err), err); mutt_debug(2, "X509_verify_cert: %s\n", buf); /* prompt user */ return interactive_check_cert(cert, pos, len, ssl, 1); } return 1; } /** * ssl_negotiate - Attempt to negotiate SSL over the wire * @param conn Connection to a server * @param ssldata SSL socket data * @retval 0 Success * @retval -1 Error * * After SSL state has been initialized, attempt to negotiate SSL over the * wire, including certificate checks. */ static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata) { int err; const char *errmsg = NULL; HostExDataIndex = SSL_get_ex_new_index(0, "host", NULL, NULL, NULL); if (HostExDataIndex == -1) { mutt_debug(1, "#1 failed to get index for application specific data\n"); return -1; } if (!SSL_set_ex_data(ssldata->ssl, HostExDataIndex, conn->account.host)) { mutt_debug(1, "#2 failed to save hostname in SSL structure\n"); return -1; } SkipModeExDataIndex = SSL_get_ex_new_index(0, "skip", NULL, NULL, NULL); if (SkipModeExDataIndex == -1) { mutt_debug(1, "#3 failed to get index for application specific data\n"); return -1; } if (!SSL_set_ex_data(ssldata->ssl, SkipModeExDataIndex, NULL)) { mutt_debug(1, "#4 failed to save skip mode in SSL structure\n"); return -1; } SSL_set_verify(ssldata->ssl, SSL_VERIFY_PEER, ssl_verify_callback); SSL_set_mode(ssldata->ssl, SSL_MODE_AUTO_RETRY); if (!SSL_set_tlsext_host_name(ssldata->ssl, conn->account.host)) { /* L10N: This is a warning when trying to set the host name for * TLS Server Name Indication (SNI). This allows the server to present * the correct certificate if it supports multiple hosts. */ mutt_error(_("Warning: unable to set TLS SNI host name")); mutt_sleep(1); } ERR_clear_error(); err = SSL_connect(ssldata->ssl); if (err != 1) { switch (SSL_get_error(ssldata->ssl, err)) { case SSL_ERROR_SYSCALL: errmsg = _("I/O error"); break; case SSL_ERROR_SSL: errmsg = ERR_error_string(ERR_get_error(), NULL); break; default: errmsg = _("unknown error"); } mutt_error(_("SSL failed: %s"), errmsg); mutt_sleep(1); return -1; } return 0; } /** * ssl_socket_open - Open an SSL socket * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ static int ssl_socket_open(struct Connection *conn) { struct SslSockData *data = NULL; int maxbits; if (raw_socket_open(conn) < 0) return -1; data = mutt_mem_calloc(1, sizeof(struct SslSockData)); conn->sockdata = data; data->ctx = SSL_CTX_new(SSLv23_client_method()); if (!data->ctx) { /* L10N: an SSL context is a data structure returned by the OpenSSL function SSL_CTX_new(). In this case it returned NULL: an error condition. */ mutt_error(_("Unable to create SSL context")); ssl_dprint_err_stack(); mutt_socket_close(conn); return -1; } /* disable SSL protocols as needed */ if (!option(OPT_SSL_USE_TLSV1)) { SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1); } /* TLSv1.1/1.2 support was added in OpenSSL 1.0.1, but some OS distros such * as Fedora 17 are on OpenSSL 1.0.0. */ #ifdef SSL_OP_NO_TLSv1_1 if (!option(OPT_SSL_USE_TLSV1_1)) { SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1_1); } #endif #ifdef SSL_OP_NO_TLSv1_2 if (!option(OPT_SSL_USE_TLSV1_2)) { SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1_2); } #endif if (!option(OPT_SSL_USE_SSLV2)) { SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2); } if (!option(OPT_SSL_USE_SSLV3)) { SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3); } if (option(OPT_SSL_USESYSTEMCERTS)) { if (!SSL_CTX_set_default_verify_paths(data->ctx)) { mutt_debug(1, "Error setting default verify paths\n"); mutt_socket_close(conn); return -1; } } if (CertificateFile && !ssl_load_certificates(data->ctx)) mutt_debug(1, "Error loading trusted certificates\n"); ssl_get_client_cert(data, conn); if (SslCiphers) { SSL_CTX_set_cipher_list(data->ctx, SslCiphers); } if (ssl_set_verify_partial(data->ctx)) { mutt_error(_("Warning: error enabling ssl_verify_partial_chains")); mutt_sleep(2); } data->ssl = SSL_new(data->ctx); SSL_set_fd(data->ssl, conn->fd); if (ssl_negotiate(conn, data)) { mutt_socket_close(conn); return -1; } data->isopen = 1; conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(data->ssl), &maxbits); return 0; } /** * mutt_ssl_starttls - Negotiate TLS over an already opened connection * @param conn Connection to a server * @retval 0 Success * @retval -1 Error * * TODO: Merge this code better with ssl_socket_open. */ int mutt_ssl_starttls(struct Connection *conn) { struct SslSockData *ssldata = NULL; int maxbits; long ssl_options = 0; if (ssl_init()) goto bail; ssldata = mutt_mem_calloc(1, sizeof(struct SslSockData)); /* the ssl_use_xxx protocol options don't apply. We must use TLS in TLS. * * However, we need to be able to negotiate amongst various TLS versions, * which at present can only be done with the SSLv23_client_method; * TLSv1_client_method gives us explicitly TLSv1.0, not 1.1 or 1.2 (True as * of OpenSSL 1.0.1c) */ ssldata->ctx = SSL_CTX_new(SSLv23_client_method()); if (!ssldata->ctx) { mutt_debug(1, "Error allocating SSL_CTX\n"); goto bail_ssldata; } #ifdef SSL_OP_NO_TLSv1_2 if (!option(OPT_SSL_USE_TLSV1_2)) ssl_options |= SSL_OP_NO_TLSv1_2; #endif #ifdef SSL_OP_NO_TLSv1_1 if (!option(OPT_SSL_USE_TLSV1_1)) ssl_options |= SSL_OP_NO_TLSv1_1; #endif #ifdef SSL_OP_NO_TLSv1 if (!option(OPT_SSL_USE_TLSV1)) ssl_options |= SSL_OP_NO_TLSv1; #endif /* these are always set */ #ifdef SSL_OP_NO_SSLv3 ssl_options |= SSL_OP_NO_SSLv3; #endif #ifdef SSL_OP_NO_SSLv2 ssl_options |= SSL_OP_NO_SSLv2; #endif if (!SSL_CTX_set_options(ssldata->ctx, ssl_options)) { mutt_debug(1, "Error setting options to %ld\n", ssl_options); goto bail_ctx; } if (option(OPT_SSL_USESYSTEMCERTS)) { if (!SSL_CTX_set_default_verify_paths(ssldata->ctx)) { mutt_debug(1, "Error setting default verify paths\n"); goto bail_ctx; } } if (CertificateFile && !ssl_load_certificates(ssldata->ctx)) mutt_debug(1, "Error loading trusted certificates\n"); ssl_get_client_cert(ssldata, conn); if (SslCiphers) { if (!SSL_CTX_set_cipher_list(ssldata->ctx, SslCiphers)) { mutt_debug(1, "Could not select preferred ciphers\n"); goto bail_ctx; } } if (ssl_set_verify_partial(ssldata->ctx)) { mutt_error(_("Warning: error enabling ssl_verify_partial_chains")); mutt_sleep(2); } ssldata->ssl = SSL_new(ssldata->ctx); if (!ssldata->ssl) { mutt_debug(1, "Error allocating SSL\n"); goto bail_ctx; } if (SSL_set_fd(ssldata->ssl, conn->fd) != 1) { mutt_debug(1, "Error setting fd\n"); goto bail_ssl; } if (ssl_negotiate(conn, ssldata)) goto bail_ssl; ssldata->isopen = 1; /* hmm. watch out if we're starting TLS over any method other than raw. */ conn->sockdata = ssldata; conn->conn_read = ssl_socket_read; conn->conn_write = ssl_socket_write; conn->conn_close = tls_close; conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(ssldata->ssl), &maxbits); return 0; bail_ssl: FREE(&ssldata->ssl); bail_ctx: FREE(&ssldata->ctx); bail_ssldata: FREE(&ssldata); bail: return -1; } /** * mutt_ssl_socket_setup - Set up the socket multiplexor * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ int mutt_ssl_socket_setup(struct Connection *conn) { if (ssl_init() < 0) { conn->conn_open = ssl_socket_open_err; return -1; } conn->conn_open = ssl_socket_open; conn->conn_read = ssl_socket_read; conn->conn_write = ssl_socket_write; conn->conn_close = ssl_socket_close; conn->conn_poll = raw_socket_poll; return 0; } neomutt-neomutt-20171215/conn/ssl.h000066400000000000000000000017611321473123000170520ustar00rootroot00000000000000/** * @file * Handling of OpenSSL encryption * * @authors * Copyright (C) 1999-2000 Tommi Komulainen * * @copyright * 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, see . */ #ifndef _CONN_SSL_H #define _CONN_SSL_H #ifdef USE_SSL struct Connection; int mutt_ssl_starttls(struct Connection *conn); int mutt_ssl_socket_setup(struct Connection *conn); #endif #endif /* _CONN_SSL_H */ neomutt-neomutt-20171215/conn/ssl_gnutls.c000066400000000000000000001074201321473123000204400ustar00rootroot00000000000000/** * @file * Handling of GnuTLS encryption * * @authors * Copyright (C) 2001 Marco d'Itri * Copyright (C) 2001-2004 Andrew McDonald * * @copyright * 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, see . */ /** * @page conn_ssl_gnutls Handling of GnuTLS encryption * * Handling of GnuTLS encryption * * | Function | Description * | :---------------------- | :----------------------------------- * | mutt_ssl_socket_setup() | Set up SSL socket mulitplexor * | mutt_ssl_starttls() | Set up TLS multiplexor */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "mutt/date.h" #include "mutt/debug.h" #include "mutt/file.h" #include "mutt/memory.h" #include "mutt/message.h" #include "mutt/string2.h" #include "mutt.h" #include "account.h" #include "conn_globals.h" #include "connection.h" #include "keymap.h" #include "mutt_account.h" #include "mutt_menu.h" #include "mutt_regex.h" #include "opcodes.h" #include "options.h" #include "protos.h" #include "socket.h" /* certificate error bitmap values */ #define CERTERR_VALID 0 #define CERTERR_EXPIRED 1 #define CERTERR_NOTYETVALID 2 #define CERTERR_REVOKED 4 #define CERTERR_NOTTRUSTED 8 #define CERTERR_HOSTNAME 16 #define CERTERR_SIGNERNOTCA 32 #define CERTERR_INSECUREALG 64 #define CERT_SEP "-----BEGIN" /** * struct TlsSockData - TLS socket data */ struct TlsSockData { gnutls_session_t state; gnutls_certificate_credentials_t xcred; }; /** * tls_init - Set up Gnu TLS * @retval 0 Success * @retval -1 Error */ static int tls_init(void) { static bool init_complete = false; int err; if (init_complete) return 0; err = gnutls_global_init(); if (err < 0) { mutt_error("gnutls_global_init: %s", gnutls_strerror(err)); mutt_sleep(2); return -1; } init_complete = true; return 0; } /** * tls_socket_read - Read data from a TLS socket * @param conn Connection to a server * @param buf Buffer to store the data * @param len Number of bytes to read * @retval >0 Success, number of bytes read * @retval -1 Error, see errno */ static int tls_socket_read(struct Connection *conn, char *buf, size_t len) { struct TlsSockData *data = conn->sockdata; int rc; if (!data) { mutt_error(_("Error: no TLS socket open")); mutt_sleep(2); return -1; } do { rc = gnutls_record_recv(data->state, buf, len); if ((rc < 0 && gnutls_error_is_fatal(rc) == 1) || rc == GNUTLS_E_INTERRUPTED) { mutt_error("tls_socket_read (%s)", gnutls_strerror(rc)); mutt_sleep(2); return -1; } } while (rc == GNUTLS_E_AGAIN); return rc; } /** * tls_socket_write - Write data to a TLS socket * @param conn Connection to a server * @param buf Buffer to read into * @param len Number of bytes to read * @retval >0 Success, number of bytes written * @retval -1 Error, see errno */ static int tls_socket_write(struct Connection *conn, const char *buf, size_t len) { struct TlsSockData *data = conn->sockdata; int ret; size_t sent = 0; if (!data) { mutt_error(_("Error: no TLS socket open")); mutt_sleep(2); return -1; } do { ret = gnutls_record_send(data->state, buf + sent, len - sent); if (ret < 0) { if (gnutls_error_is_fatal(ret) == 1 || ret == GNUTLS_E_INTERRUPTED) { mutt_error("tls_socket_write (%s)", gnutls_strerror(ret)); mutt_sleep(4); return -1; } return ret; } sent += ret; } while (sent < len); return sent; } /** * tls_socket_close - Close a TLS socket * @param conn Connection to a server * @retval 0 Success * @retval -1 Error, see errno */ static int tls_socket_close(struct Connection *conn) { struct TlsSockData *data = conn->sockdata; if (data) { /* shut down only the write half to avoid hanging waiting for the remote to respond. * * RFC5246 7.2.1. "Closure Alerts" * * It is not required for the initiator of the close to wait for the * responding close_notify alert before closing the read side of the * connection. */ gnutls_bye(data->state, GNUTLS_SHUT_WR); gnutls_certificate_free_credentials(data->xcred); gnutls_deinit(data->state); FREE(&conn->sockdata); } return raw_socket_close(conn); } /** * tls_starttls_close - Close a TLS connection * @param conn Connection to a server * @retval 0 Success * @retval -1 Error, see errno */ static int tls_starttls_close(struct Connection *conn) { int rc; rc = tls_socket_close(conn); conn->conn_read = raw_socket_read; conn->conn_write = raw_socket_write; conn->conn_close = raw_socket_close; return rc; } /** * tls_verify_peers - wrapper for gnutls_certificate_verify_peers * @param tlsstate TLS state * @retval 0 Success * @retval >0 Error, e.g. GNUTLS_CERT_INVALID * * wrapper with sanity-checking */ static gnutls_certificate_status_t tls_verify_peers(gnutls_session_t tlsstate) { int verify_ret; unsigned int status; verify_ret = gnutls_certificate_verify_peers2(tlsstate, &status); if (!verify_ret) return status; if (status == GNUTLS_E_NO_CERTIFICATE_FOUND) { mutt_error(_("Unable to get certificate from peer")); mutt_sleep(2); return 0; } if (verify_ret < 0) { mutt_error(_("Certificate verification error (%s)"), gnutls_strerror(status)); mutt_sleep(2); return 0; } /* We only support X.509 certificates (not OpenPGP) at the moment */ if (gnutls_certificate_type_get(tlsstate) != GNUTLS_CRT_X509) { mutt_error(_("Certificate is not X.509")); mutt_sleep(2); return 0; } return status; } /** * tls_fingerprint - Create a fingerprint of a TLS Certificate * @param algo Fingerprint algorithm, e.g. GNUTLS_MAC_SHA256 * @param s Buffer for the fingerprint * @param l Length of the buffer * @param data Certificate */ static void tls_fingerprint(gnutls_digest_algorithm_t algo, char *s, int l, const gnutls_datum_t *data) { unsigned char md[36]; size_t n; n = 36; if (gnutls_fingerprint(algo, data, (char *) md, &n) < 0) { snprintf(s, l, _("[unable to calculate]")); } else { for (int i = 0; i < (int) n; i++) { char ch[8]; snprintf(ch, 8, "%02X%s", md[i], (i % 2 ? " " : "")); mutt_str_strcat(s, l, ch); } s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */ } } /** * tls_check_stored_hostname - Does the hostname match a stored certificate? * @param cert Certificate * @param hostname Hostname * @retval 1 Hostname match found * @retval 0 Error, or no match */ static int tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname) { char buf[80]; FILE *fp = NULL; char *linestr = NULL; size_t linestrsize; int linenum = 0; regex_t preg; regmatch_t pmatch[3]; /* try checking against names stored in stored certs file */ if ((fp = fopen(CertificateFile, "r"))) { if (REGCOMP(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$", REG_ICASE) != 0) { mutt_file_fclose(&fp); return 0; } buf[0] = '\0'; tls_fingerprint(GNUTLS_DIG_MD5, buf, sizeof(buf), cert); while ((linestr = mutt_file_read_line(linestr, &linestrsize, fp, &linenum, 0)) != NULL) { if (linestr[0] == '#' && linestr[1] == 'H') { if (regexec(&preg, linestr, 3, pmatch, 0) == 0) { linestr[pmatch[1].rm_eo] = '\0'; linestr[pmatch[2].rm_eo] = '\0'; if ((strcmp(linestr + pmatch[1].rm_so, hostname) == 0) && (strcmp(linestr + pmatch[2].rm_so, buf) == 0)) { regfree(&preg); FREE(&linestr); mutt_file_fclose(&fp); return 1; } } } } regfree(&preg); mutt_file_fclose(&fp); } /* not found a matching name */ return 0; } /** * tls_compare_certificates - Compare certificates against CertificateFile * @param peercert Certificate * @retval 1 Certificate matches file * @retval 0 Error, or no match */ static int tls_compare_certificates(const gnutls_datum_t *peercert) { gnutls_datum_t cert; unsigned char *ptr = NULL; FILE *fd1 = NULL; int ret; gnutls_datum_t b64_data; unsigned char *b64_data_data = NULL; struct stat filestat; if (stat(CertificateFile, &filestat) == -1) return 0; b64_data.size = filestat.st_size + 1; b64_data_data = mutt_mem_calloc(1, b64_data.size); b64_data_data[b64_data.size - 1] = '\0'; b64_data.data = b64_data_data; fd1 = fopen(CertificateFile, "r"); if (!fd1) { return 0; } b64_data.size = fread(b64_data.data, 1, b64_data.size, fd1); mutt_file_fclose(&fd1); do { ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert); if (ret != 0) { FREE(&b64_data_data); return 0; } /* find start of cert, skipping junk */ ptr = (unsigned char *) strstr((char *) b64_data.data, CERT_SEP); if (!ptr) { gnutls_free(cert.data); FREE(&b64_data_data); return 0; } /* find start of next cert */ ptr = (unsigned char *) strstr((char *) ptr + 1, CERT_SEP); b64_data.size = b64_data.size - (ptr - b64_data.data); b64_data.data = ptr; if (cert.size == peercert->size) { if (memcmp(cert.data, peercert->data, cert.size) == 0) { /* match found */ gnutls_free(cert.data); FREE(&b64_data_data); return 1; } } gnutls_free(cert.data); } while (ptr); /* no match found */ FREE(&b64_data_data); return 0; } /** * tls_check_preauth - Prepare a certificate for authentication * @param[in] certdata List of GnuTLS certificates * @param[in] certstat GnuTLS certificate status * @param[in] hostname Hostname * @param[in] chainidx Index in the certificate chain * @param[out] certerr Result, e.g. #CERTERR_VALID * @param[out] savedcert 1 if certificate has been saved * @retval 0 Success * @retval -1 Error */ static int tls_check_preauth(const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int chainidx, int *certerr, int *savedcert) { gnutls_x509_crt_t cert; *certerr = CERTERR_VALID; *savedcert = 0; if (gnutls_x509_crt_init(&cert) < 0) { mutt_error(_("Error initialising gnutls certificate data")); mutt_sleep(2); return -1; } if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0) { mutt_error(_("Error processing certificate data")); mutt_sleep(2); gnutls_x509_crt_deinit(cert); return -1; } if (option(OPT_SSL_VERIFY_DATES) != MUTT_NO) { if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) *certerr |= CERTERR_EXPIRED; if (gnutls_x509_crt_get_activation_time(cert) > time(NULL)) *certerr |= CERTERR_NOTYETVALID; } if (chainidx == 0 && option(OPT_SSL_VERIFY_HOST) != MUTT_NO && !gnutls_x509_crt_check_hostname(cert, hostname) && !tls_check_stored_hostname(certdata, hostname)) *certerr |= CERTERR_HOSTNAME; /* see whether certificate is in our cache (certificates file) */ if (tls_compare_certificates(certdata)) { *savedcert = 1; if (chainidx == 0 && (certstat & GNUTLS_CERT_INVALID)) { /* doesn't matter - have decided is valid because server certificate is in our trusted cache */ certstat ^= GNUTLS_CERT_INVALID; } if (chainidx == 0 && (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)) { /* doesn't matter that we haven't found the signer, since certificate is in our trusted cache */ certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND; } if (chainidx <= 1 && (certstat & GNUTLS_CERT_SIGNER_NOT_CA)) { /* Hmm. Not really sure how to handle this, but let's say that we don't care if the CA certificate hasn't got the correct X.509 basic constraints if server or first signer certificate is in our cache. */ certstat ^= GNUTLS_CERT_SIGNER_NOT_CA; } if (chainidx == 0 && (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)) { /* doesn't matter that it was signed using an insecure algorithm, since certificate is in our trusted cache */ certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM; } } if (certstat & GNUTLS_CERT_REVOKED) { *certerr |= CERTERR_REVOKED; certstat ^= GNUTLS_CERT_REVOKED; } if (certstat & GNUTLS_CERT_INVALID) { *certerr |= CERTERR_NOTTRUSTED; certstat ^= GNUTLS_CERT_INVALID; } if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) { /* NB: already cleared if cert in cache */ *certerr |= CERTERR_NOTTRUSTED; certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND; } if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) { /* NB: already cleared if cert in cache */ *certerr |= CERTERR_SIGNERNOTCA; certstat ^= GNUTLS_CERT_SIGNER_NOT_CA; } if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM) { /* NB: already cleared if cert in cache */ *certerr |= CERTERR_INSECUREALG; certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM; } gnutls_x509_crt_deinit(cert); /* we've been zeroing the interesting bits in certstat - don't return OK if there are any unhandled bits we don't understand */ if (*certerr == CERTERR_VALID && certstat == 0) return 0; return -1; } /** * tls_make_date - Create a TLS date string * @param t Time to convert * @param s Buffer for the string * @param len Length of the buffer * @retval ptr Pointer to s */ static char *tls_make_date(time_t t, char *s, size_t len) { struct tm *l = gmtime(&t); if (l) snprintf(s, len, "%s, %d %s %d %02d:%02d:%02d UTC", Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon], l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec); else mutt_str_strfcpy(s, _("[invalid date]"), len); return s; } /** * tls_check_one_certificate - Check a GnuTLS certificate * @param certdata List of GnuTLS certificates * @param certstat GnuTLS certificate status * @param hostname Hostname * @param idx Index into certificate list * @param len Length of certificate list * @retval 0 on failure * @retval >0 on success */ static int tls_check_one_certificate(const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int idx, int len) { int certerr, savedcert; gnutls_x509_crt_t cert; char buf[SHORT_STRING]; char fpbuf[SHORT_STRING]; size_t buflen; char dn_common_name[SHORT_STRING]; char dn_email[SHORT_STRING]; char dn_organization[SHORT_STRING]; char dn_organizational_unit[SHORT_STRING]; char dn_locality[SHORT_STRING]; char dn_province[SHORT_STRING]; char dn_country[SHORT_STRING]; time_t t; char datestr[30]; struct Menu *menu = NULL; char helpstr[LONG_STRING]; char title[STRING]; FILE *fp = NULL; gnutls_datum_t pemdata; int row, done, ret; if (!tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert)) return 1; /* skip signers if insecure algorithm was used */ if (idx && (certerr & CERTERR_INSECUREALG)) { if (idx == 1) { mutt_error(_("Warning: Server certificate was signed using an insecure " "algorithm")); mutt_sleep(2); } return 0; } /* interactive check from user */ if (gnutls_x509_crt_init(&cert) < 0) { mutt_error(_("Error initialising gnutls certificate data")); mutt_sleep(2); return 0; } if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0) { mutt_error(_("Error processing certificate data")); mutt_sleep(2); gnutls_x509_crt_deinit(cert); return 0; } menu = mutt_new_menu(MENU_GENERIC); menu->max = 25; menu->dialog = mutt_mem_calloc(1, menu->max * sizeof(char *)); for (int i = 0; i < menu->max; i++) menu->dialog[i] = mutt_mem_calloc(1, SHORT_STRING * sizeof(char)); mutt_push_current_menu(menu); row = 0; mutt_str_strfcpy(menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING); row++; buflen = sizeof(dn_common_name); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0) dn_common_name[0] = '\0'; buflen = sizeof(dn_email); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0) dn_email[0] = '\0'; buflen = sizeof(dn_organization); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization, &buflen) != 0) dn_organization[0] = '\0'; buflen = sizeof(dn_organizational_unit); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, dn_organizational_unit, &buflen) != 0) dn_organizational_unit[0] = '\0'; buflen = sizeof(dn_locality); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0) dn_locality[0] = '\0'; buflen = sizeof(dn_province); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province, &buflen) != 0) dn_province[0] = '\0'; buflen = sizeof(dn_country); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0) dn_country[0] = '\0'; snprintf(menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name, dn_email); snprintf(menu->dialog[row++], SHORT_STRING, " %s", dn_organization); snprintf(menu->dialog[row++], SHORT_STRING, " %s", dn_organizational_unit); snprintf(menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality, dn_province, dn_country); row++; mutt_str_strfcpy(menu->dialog[row], _("This certificate was issued by:"), SHORT_STRING); row++; buflen = sizeof(dn_common_name); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0) dn_common_name[0] = '\0'; buflen = sizeof(dn_email); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0) dn_email[0] = '\0'; buflen = sizeof(dn_organization); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization, &buflen) != 0) dn_organization[0] = '\0'; buflen = sizeof(dn_organizational_unit); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, dn_organizational_unit, &buflen) != 0) dn_organizational_unit[0] = '\0'; buflen = sizeof(dn_locality); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0) dn_locality[0] = '\0'; buflen = sizeof(dn_province); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province, &buflen) != 0) dn_province[0] = '\0'; buflen = sizeof(dn_country); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0) dn_country[0] = '\0'; snprintf(menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name, dn_email); snprintf(menu->dialog[row++], SHORT_STRING, " %s", dn_organization); snprintf(menu->dialog[row++], SHORT_STRING, " %s", dn_organizational_unit); snprintf(menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality, dn_province, dn_country); row++; snprintf(menu->dialog[row++], SHORT_STRING, _("This certificate is valid")); t = gnutls_x509_crt_get_activation_time(cert); snprintf(menu->dialog[row++], SHORT_STRING, _(" from %s"), tls_make_date(t, datestr, 30)); t = gnutls_x509_crt_get_expiration_time(cert); snprintf(menu->dialog[row++], SHORT_STRING, _(" to %s"), tls_make_date(t, datestr, 30)); fpbuf[0] = '\0'; tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata); snprintf(menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), fpbuf); fpbuf[0] = '\0'; tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata); snprintf(menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), fpbuf); if (certerr & CERTERR_NOTYETVALID) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate is not yet valid"), SHORT_STRING); } if (certerr & CERTERR_EXPIRED) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate has expired"), SHORT_STRING); } if (certerr & CERTERR_REVOKED) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate has been revoked"), SHORT_STRING); } if (certerr & CERTERR_HOSTNAME) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server hostname does not match certificate"), SHORT_STRING); } if (certerr & CERTERR_SIGNERNOTCA) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), SHORT_STRING); } snprintf(title, sizeof(title), _("SSL Certificate check (certificate %d of %d in chain)"), len - idx, len); menu->title = title; /* certificates with bad dates, or that are revoked, must be accepted manually each and every time */ if (CertificateFile && !savedcert && !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID | CERTERR_REVOKED))) { menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always"); /* L10N: * These three letters correspond to the choices in the string: * (r)eject, accept (o)nce, (a)ccept always. * This is an interactive certificate confirmation prompt for * a GNUTLS connection. */ menu->keys = _("roa"); } else { menu->prompt = _("(r)eject, accept (o)nce"); /* L10N: * These two letters correspond to the choices in the string: * (r)eject, accept (o)nce. * These is an interactive certificate confirmation prompt for * a GNUTLS connection. */ menu->keys = _("ro"); } helpstr[0] = '\0'; mutt_make_help(buf, sizeof(buf), _("Exit "), MENU_GENERIC, OP_EXIT); mutt_str_strcat(helpstr, sizeof(helpstr), buf); mutt_make_help(buf, sizeof(buf), _("Help"), MENU_GENERIC, OP_HELP); mutt_str_strcat(helpstr, sizeof(helpstr), buf); menu->help = helpstr; done = 0; set_option(OPT_IGNORE_MACRO_EVENTS); while (!done) { switch (mutt_menu_loop(menu)) { case -1: /* abort */ case OP_MAX + 1: /* reject */ case OP_EXIT: done = 1; break; case OP_MAX + 3: /* accept always */ done = 0; if ((fp = fopen(CertificateFile, "a"))) { /* save hostname if necessary */ if (certerr & CERTERR_HOSTNAME) { fprintf(fp, "#H %s %s\n", hostname, fpbuf); done = 1; } if (certerr & CERTERR_NOTTRUSTED) { done = 0; ret = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata); if (ret == 0) { if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1) { done = 1; } gnutls_free(pemdata.data); } } mutt_file_fclose(&fp); } if (!done) { mutt_error(_("Warning: Couldn't save certificate")); mutt_sleep(2); } else { mutt_message(_("Certificate saved")); mutt_sleep(0); } /* fall through */ case OP_MAX + 2: /* accept once */ done = 2; break; } } unset_option(OPT_IGNORE_MACRO_EVENTS); mutt_pop_current_menu(menu); mutt_menu_destroy(&menu); gnutls_x509_crt_deinit(cert); return (done == 2); } /** * tls_check_certificate - Check a connection's certificate * @param conn Connection to a server * @retval >0 Certificate is valid * @retval 0 Error, or certificate is invalid */ static int tls_check_certificate(struct Connection *conn) { struct TlsSockData *data = conn->sockdata; gnutls_session_t state = data->state; const gnutls_datum_t *cert_list = NULL; unsigned int cert_list_size = 0; gnutls_certificate_status_t certstat; int certerr, preauthrc, savedcert, rc = 0; int rcpeer = -1; /* the result of tls_check_preauth() on the peer's EE cert */ if (gnutls_auth_get_type(state) != GNUTLS_CRD_CERTIFICATE) { mutt_error(_("Unable to get certificate from peer")); mutt_sleep(2); return 0; } certstat = tls_verify_peers(state); cert_list = gnutls_certificate_get_peers(state, &cert_list_size); if (!cert_list) { mutt_error(_("Unable to get certificate from peer")); mutt_sleep(2); return 0; } /* tls_verify_peers doesn't check hostname or expiration, so walk * from most specific to least checking these. If we see a saved certificate, * its status short-circuits the remaining checks. */ preauthrc = 0; for (int i = 0; i < cert_list_size; i++) { rc = tls_check_preauth(&cert_list[i], certstat, conn->account.host, i, &certerr, &savedcert); preauthrc += rc; if (i == 0) { /* This is the peer's end-entity X.509 certificate. Stash the result * to check later in this function. */ rcpeer = rc; } if (savedcert) { if (!preauthrc) return 1; else break; } } /* then check interactively, starting from chain root */ for (int i = cert_list_size - 1; i >= 0; i--) { rc = tls_check_one_certificate(&cert_list[i], certstat, conn->account.host, i, cert_list_size); /* add signers to trust set, then reverify */ if (i && rc) { rc = gnutls_certificate_set_x509_trust_mem(data->xcred, &cert_list[i], GNUTLS_X509_FMT_DER); if (rc != 1) mutt_debug(1, "error trusting certificate %d: %d\n", i, rc); certstat = tls_verify_peers(state); /* If the cert chain now verifies, and the peer's cert was otherwise * valid (rcpeer==0), we are done. */ if (!certstat && !rcpeer) return 1; } } return rc; } /** * tls_get_client_cert - Get the client certificate for a TLS connection * @param conn Connection to a server */ static void tls_get_client_cert(struct Connection *conn) { struct TlsSockData *data = conn->sockdata; const gnutls_datum_t *crtdata = NULL; gnutls_x509_crt_t clientcrt; char *dn = NULL; char *cn = NULL; char *cnend = NULL; size_t dnlen; /* get our cert CN if we have one */ crtdata = gnutls_certificate_get_ours(data->state); if (!crtdata) return; if (gnutls_x509_crt_init(&clientcrt) < 0) { mutt_debug(1, "Failed to init gnutls crt\n"); return; } if (gnutls_x509_crt_import(clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0) { mutt_debug(1, "Failed to import gnutls client crt\n"); goto err_crt; } /* get length of DN */ dnlen = 0; gnutls_x509_crt_get_dn(clientcrt, NULL, &dnlen); dn = mutt_mem_calloc(1, dnlen); gnutls_x509_crt_get_dn(clientcrt, dn, &dnlen); mutt_debug(2, "client certificate DN: %s\n", dn); /* extract CN to use as external user name */ cn = strstr(dn, "CN="); if (!cn) { mutt_debug(1, "no CN found in DN\n"); goto err_dn; } cn += 3; if ((cnend = strstr(dn, ",EMAIL="))) *cnend = '\0'; /* if we are using a client cert, SASL may expect an external auth name */ if (mutt_account_getuser(&conn->account) < 0) mutt_debug(1, "Couldn't get user info\n"); err_dn: FREE(&dn); err_crt: gnutls_x509_crt_deinit(clientcrt); } #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT /** * tls_set_priority - Set TLS algorithm priorities * @param data TLS socket data * @retval 0 Success * @retval -1 Error */ static int tls_set_priority(struct TlsSockData *data) { size_t nproto = 4; char *priority = NULL; size_t priority_size; int err; priority_size = SHORT_STRING + mutt_str_strlen(SslCiphers); priority = mutt_mem_malloc(priority_size); priority[0] = 0; if (SslCiphers) mutt_str_strcat(priority, priority_size, SslCiphers); else mutt_str_strcat(priority, priority_size, "NORMAL"); if (!option(OPT_SSL_USE_TLSV1_2)) { nproto--; mutt_str_strcat(priority, priority_size, ":-VERS-TLS1.2"); } if (!option(OPT_SSL_USE_TLSV1_1)) { nproto--; mutt_str_strcat(priority, priority_size, ":-VERS-TLS1.1"); } if (!option(OPT_SSL_USE_TLSV1)) { nproto--; mutt_str_strcat(priority, priority_size, ":-VERS-TLS1.0"); } if (!option(OPT_SSL_USE_SSLV3)) { nproto--; mutt_str_strcat(priority, priority_size, ":-VERS-SSL3.0"); } if (nproto == 0) { mutt_error(_("All available protocols for TLS/SSL connection disabled")); FREE(&priority); return -1; } err = gnutls_priority_set_direct(data->state, priority, NULL); if (err < 0) { mutt_error("gnutls_priority_set_direct(%s): %s", priority, gnutls_strerror(err)); mutt_sleep(2); FREE(&priority); return -1; } FREE(&priority); return 0; } #else /* This array needs to be large enough to hold all the possible values support * by NeoMutt. The initialized values are just placeholders--the array gets * overwritten in tls_negotiate() depending on the $ssl_use_* options. */ static int protocol_priority[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1, GNUTLS_SSL3, 0 }; static int tls_set_priority(struct TlsSockData *data) { size_t nproto = 0; /* number of tls/ssl protocols */ if (option(OPT_SSL_USE_TLSV1_2)) protocol_priority[nproto++] = GNUTLS_TLS1_2; if (option(OPT_SSL_USE_TLSV1_1)) protocol_priority[nproto++] = GNUTLS_TLS1_1; if (option(OPT_SSL_USE_TLSV1)) protocol_priority[nproto++] = GNUTLS_TLS1; if (option(OPT_SSL_USE_SSLV3)) protocol_priority[nproto++] = GNUTLS_SSL3; protocol_priority[nproto] = 0; if (nproto == 0) { mutt_error(_("All available protocols for TLS/SSL connection disabled")); return -1; } if (SslCiphers) { mutt_error( _("Explicit ciphersuite selection via $ssl_ciphers not supported")); mutt_sleep(2); } /* We use default priorities (see gnutls documentation), except for protocol version */ gnutls_set_default_priority(data->state); gnutls_protocol_set_priority(data->state, protocol_priority); return 0; } #endif /** * tls_negotiate - Negotiate TLS connection * @param conn Connection to a server * @retval 0 Success * @retval -1 Error * * After TLS state has been initialized, attempt to negotiate TLS over the * wire, including certificate checks. */ static int tls_negotiate(struct Connection *conn) { struct TlsSockData *data = NULL; int err; data = mutt_mem_calloc(1, sizeof(struct TlsSockData)); conn->sockdata = data; err = gnutls_certificate_allocate_credentials(&data->xcred); if (err < 0) { FREE(&conn->sockdata); mutt_error("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err)); mutt_sleep(2); return -1; } gnutls_certificate_set_x509_trust_file(data->xcred, CertificateFile, GNUTLS_X509_FMT_PEM); /* ignore errors, maybe file doesn't exist yet */ if (SslCaCertificatesFile) { gnutls_certificate_set_x509_trust_file(data->xcred, SslCaCertificatesFile, GNUTLS_X509_FMT_PEM); } if (SslClientCert) { mutt_debug(2, "Using client certificate %s\n", SslClientCert); gnutls_certificate_set_x509_key_file(data->xcred, SslClientCert, SslClientCert, GNUTLS_X509_FMT_PEM); } #ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS /* disable checking certificate activation/expiration times in gnutls, we do the checks ourselves */ gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); #endif if ((err = gnutls_init(&data->state, GNUTLS_CLIENT))) { mutt_error("gnutls_handshake: %s", gnutls_strerror(err)); mutt_sleep(2); goto fail; } /* set socket */ gnutls_transport_set_ptr(data->state, (gnutls_transport_ptr_t)(long) conn->fd); if (gnutls_server_name_set(data->state, GNUTLS_NAME_DNS, conn->account.host, mutt_str_strlen(conn->account.host))) { mutt_error(_("Warning: unable to set TLS SNI host name")); mutt_sleep(1); } if (tls_set_priority(data) < 0) { goto fail; } if (SslMinDhPrimeBits > 0) { gnutls_dh_set_prime_bits(data->state, SslMinDhPrimeBits); } /* gnutls_set_cred (data->state, GNUTLS_ANON, NULL); */ gnutls_credentials_set(data->state, GNUTLS_CRD_CERTIFICATE, data->xcred); err = gnutls_handshake(data->state); while (err == GNUTLS_E_AGAIN) { err = gnutls_handshake(data->state); } if (err < 0) { if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) { mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err), gnutls_alert_get_name(gnutls_alert_get(data->state))); } else { mutt_error("gnutls_handshake: %s", gnutls_strerror(err)); } mutt_sleep(2); goto fail; } if (!tls_check_certificate(conn)) goto fail; /* set Security Strength Factor (SSF) for SASL */ /* NB: gnutls_cipher_get_key_size() returns key length in bytes */ conn->ssf = gnutls_cipher_get_key_size(gnutls_cipher_get(data->state)) * 8; tls_get_client_cert(conn); if (!option(OPT_NO_CURSES)) { mutt_message(_("SSL/TLS connection using %s (%s/%s/%s)"), gnutls_protocol_get_name(gnutls_protocol_get_version(data->state)), gnutls_kx_get_name(gnutls_kx_get(data->state)), gnutls_cipher_get_name(gnutls_cipher_get(data->state)), gnutls_mac_get_name(gnutls_mac_get(data->state))); mutt_sleep(0); } return 0; fail: gnutls_certificate_free_credentials(data->xcred); gnutls_deinit(data->state); FREE(&conn->sockdata); return -1; } /** * tls_socket_open - Open a TLS socket * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ static int tls_socket_open(struct Connection *conn) { if (raw_socket_open(conn) < 0) return -1; if (tls_negotiate(conn) < 0) { tls_socket_close(conn); return -1; } return 0; } /** * mutt_ssl_socket_setup - Set up SSL socket mulitplexor * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ int mutt_ssl_socket_setup(struct Connection *conn) { if (tls_init() < 0) return -1; conn->conn_open = tls_socket_open; conn->conn_read = tls_socket_read; conn->conn_write = tls_socket_write; conn->conn_close = tls_socket_close; conn->conn_poll = raw_socket_poll; return 0; } /** * mutt_ssl_starttls - Set up TLS multiplexor * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ int mutt_ssl_starttls(struct Connection *conn) { if (tls_init() < 0) return -1; if (tls_negotiate(conn) < 0) return -1; conn->conn_read = tls_socket_read; conn->conn_write = tls_socket_write; conn->conn_close = tls_starttls_close; return 0; } neomutt-neomutt-20171215/conn/tunnel.c000066400000000000000000000143101321473123000175430ustar00rootroot00000000000000/** * @file * Support for network tunnelling * * @authors * Copyright (C) 2000 Manoj Kasichainula * Copyright (C) 2001,2005 Brendan Cully * * @copyright * 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, see . */ /** * @page conn_tunnel Support for network tunnelling * * Support for network tunnelling * * | Function | Description * | :------------------------- | :----------------------------------- * | mutt_tunnel_socket_setup() | setups tunnel connection functions. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "mutt/memory.h" #include "mutt/message.h" #include "mutt/signal2.h" #include "mutt/string2.h" #include "mutt.h" #include "tunnel.h" #include "account.h" #include "conn_globals.h" #include "connection.h" #include "protos.h" #include "socket.h" /** * struct TunnelData - A network tunnel (pair of sockets) */ struct TunnelData { pid_t pid; int readfd; int writefd; }; /** * tunnel_socket_open - Open a tunnel socket * @param conn Connection to a server * @retval 0 Success * @retval -1 Error */ static int tunnel_socket_open(struct Connection *conn) { struct TunnelData *tunnel = NULL; int pid; int rc; int pin[2], pout[2]; int devnull; tunnel = mutt_mem_malloc(sizeof(struct TunnelData)); conn->sockdata = tunnel; mutt_message(_("Connecting with \"%s\"..."), Tunnel); rc = pipe(pin); if (rc == -1) { mutt_perror("pipe"); FREE(&conn->sockdata); return -1; } rc = pipe(pout); if (rc == -1) { mutt_perror("pipe"); close(pin[0]); close(pin[1]); FREE(&conn->sockdata); return -1; } mutt_sig_block_system(); pid = fork(); if (pid == 0) { mutt_sig_unblock_system(0); devnull = open("/dev/null", O_RDWR); if (devnull < 0 || dup2(pout[0], STDIN_FILENO) < 0 || dup2(pin[1], STDOUT_FILENO) < 0 || dup2(devnull, STDERR_FILENO) < 0) { _exit(127); } close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); close(devnull); /* Don't let the subprocess think it can use the controlling tty */ setsid(); execle(EXECSHELL, "sh", "-c", Tunnel, NULL, mutt_envlist()); _exit(127); } mutt_sig_unblock_system(1); if (pid == -1) { mutt_perror("fork"); close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); FREE(&conn->sockdata); return -1; } if (close(pin[1]) < 0 || close(pout[0]) < 0) mutt_perror("close"); fcntl(pin[0], F_SETFD, FD_CLOEXEC); fcntl(pout[1], F_SETFD, FD_CLOEXEC); tunnel->readfd = pin[0]; tunnel->writefd = pout[1]; tunnel->pid = pid; conn->fd = 42; /* stupid hack */ return 0; } /** * tunnel_socket_close - Close a tunnel socket * @param conn Connection to a server * @retval 0 Success * @retval -1 Error, see errno */ static int tunnel_socket_close(struct Connection *conn) { struct TunnelData *tunnel = (struct TunnelData *) conn->sockdata; int status; close(tunnel->readfd); close(tunnel->writefd); waitpid(tunnel->pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status)) { mutt_error(_("Tunnel to %s returned error %d (%s)"), conn->account.host, WEXITSTATUS(status), NONULL(mutt_str_sysexit(WEXITSTATUS(status)))); mutt_sleep(2); } FREE(&conn->sockdata); return 0; } /** * tunnel_socket_read - Read data from a tunnel socket * @param conn Connection to a server * @param buf Buffer to store the data * @param len Number of bytes to read * @retval >0 Success, number of bytes read * @retval -1 Error, see errno */ static int tunnel_socket_read(struct Connection *conn, char *buf, size_t len) { struct TunnelData *tunnel = (struct TunnelData *) conn->sockdata; int rc; rc = read(tunnel->readfd, buf, len); if (rc == -1) { mutt_error(_("Tunnel error talking to %s: %s"), conn->account.host, strerror(errno)); mutt_sleep(1); } return rc; } /** * tunnel_socket_write - Write data to a tunnel socket * @param conn Connection to a server * @param buf Buffer to read into * @param len Number of bytes to read * @retval >0 Success, number of bytes written * @retval -1 Error, see errno */ static int tunnel_socket_write(struct Connection *conn, const char *buf, size_t len) { struct TunnelData *tunnel = (struct TunnelData *) conn->sockdata; int rc; rc = write(tunnel->writefd, buf, len); if (rc == -1) { mutt_error(_("Tunnel error talking to %s: %s"), conn->account.host, strerror(errno)); mutt_sleep(1); } return rc; } /** * tunnel_socket_poll - Checks whether tunnel reads would block * @param conn Connection to a server * @param wait_secs How long to wait for a response * @retval >0 There is data to read * @retval 0 Read would block * @retval -1 Connection doesn't support polling */ static int tunnel_socket_poll(struct Connection *conn, time_t wait_secs) { struct TunnelData *tunnel = (struct TunnelData *) conn->sockdata; int ofd; int rc; ofd = conn->fd; conn->fd = tunnel->readfd; rc = raw_socket_poll(conn, wait_secs); conn->fd = ofd; return rc; } /** * mutt_tunnel_socket_setup - setups tunnel connection functions. * @param conn Connection to assign functions to * * Assign tunnel socket functions to the Connection conn. */ void mutt_tunnel_socket_setup(struct Connection *conn) { conn->conn_open = tunnel_socket_open; conn->conn_close = tunnel_socket_close; conn->conn_read = tunnel_socket_read; conn->conn_write = tunnel_socket_write; conn->conn_poll = tunnel_socket_poll; } neomutt-neomutt-20171215/conn/tunnel.h000066400000000000000000000015571321473123000175610ustar00rootroot00000000000000/** * @file * Support for network tunnelling * * @authors * @copyright * 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, see . */ #ifndef _CONN_TUNNEL_H #define _CONN_TUNNEL_H struct Connection; void mutt_tunnel_socket_setup(struct Connection *conn); #endif /* _CONN_TUNNEL_H */ neomutt-neomutt-20171215/content.h000066400000000000000000000032341321473123000167630ustar00rootroot00000000000000/** * @file * Information about the content of an attachment * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ #ifndef _MUTT_CONTENT_H #define _MUTT_CONTENT_H #include /** * struct Content - Info about an attachment * * Information that helps in determining the Content-* of an attachment */ struct Content { long hibin; /**< 8-bit characters */ long lobin; /**< unprintable 7-bit chars (eg., control chars) */ long nulbin; /**< null characters (0x0) */ long crlf; /**< `\r` and `\n` characters */ long ascii; /**< number of ascii chars */ long linemax; /**< length of the longest line in the file */ bool space : 1; /**< whitespace at the end of lines? */ bool binary : 1; /**< long lines, or CR not in CRLF pair */ bool from : 1; /**< has a line beginning with "From "? */ bool dot : 1; /**< has a line consisting of a single dot? */ bool cr : 1; /**< has CR, even when in a CRLF pair */ }; #endif /* _MUTT_CONTENT_H */ neomutt-neomutt-20171215/context.h000066400000000000000000000067201321473123000170000ustar00rootroot00000000000000/** * @file * The "currently-open" mailbox * * @authors * Copyright (C) 2017 Richard Russon * * @copyright * 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, see . */ #ifndef _MUTT_CONTEXT_H #define _MUTT_CONTEXT_H #include #include #include #include /** * enum AclRights - ACL Rights */ enum AclRights { MUTT_ACL_LOOKUP = 0, MUTT_ACL_READ, MUTT_ACL_SEEN, MUTT_ACL_WRITE, MUTT_ACL_INSERT, MUTT_ACL_POST, MUTT_ACL_CREATE, MUTT_ACL_DELMX, MUTT_ACL_DELETE, MUTT_ACL_EXPUNGE, MUTT_ACL_ADMIN, RIGHTSMAX }; /** * struct Context - The "current" mailbox */ struct Context { char *path; char *realpath; /**< used for buffy comparison and the sidebar */ FILE *fp; time_t atime; time_t mtime; off_t size; off_t vsize; char *pattern; /**< limit pattern string */ struct Pattern *limit_pattern; /**< compiled limit pattern */ struct Header **hdrs; struct Header *last_tag; /**< last tagged msg. used to link threads */ struct MuttThread *tree; /**< top of thread tree */ struct Hash *id_hash; /**< hash table by msg id */ struct Hash *subj_hash; /**< hash table by subject */ struct Hash *thread_hash; /**< hash table for threading */ struct Hash *label_hash; /**< hash table for x-labels */ int *v2r; /**< mapping from virtual to real msgno */ int hdrmax; /**< number of pointers in hdrs */ int msgcount; /**< number of messages in the mailbox */ int vcount; /**< the number of virtual messages */ int tagged; /**< how many messages are tagged? */ int new; /**< how many new messages? */ int unread; /**< how many unread messages? */ int deleted; /**< how many deleted messages */ int flagged; /**< how many flagged messages */ int msgnotreadyet; /**< which msg "new" in pager, -1 if none */ struct Menu *menu; /**< needed for pattern compilation */ short magic; /**< mailbox type */ unsigned char rights[(RIGHTSMAX + 7) / 8]; /**< ACL bits */ bool locked : 1; /**< is the mailbox locked? */ bool changed : 1; /**< mailbox has been modified */ bool readonly : 1; /**< don't allow changes to the mailbox */ bool dontwrite : 1; /**< don't write the mailbox on close */ bool append : 1; /**< mailbox is opened in append mode */ bool quiet : 1; /**< inhibit status messages? */ bool collapsed : 1; /**< are all threads collapsed? */ bool closing : 1; /**< mailbox is being closed */ bool peekonly : 1; /**< just taking a glance, revert atime */ #ifdef USE_COMPRESSED void *compress_info; /**< compressed mbox module private data */ #endif /**< USE_COMPRESSED */ /* driver hooks */ void *data; /**< driver specific data */ struct MxOps *mx_ops; }; #endif /* _MUTT_CONTEXT_H */ neomutt-neomutt-20171215/contrib/000077500000000000000000000000001321473123000165765ustar00rootroot00000000000000neomutt-neomutt-20171215/contrib/Makefile.autosetup000066400000000000000000000020051321473123000222630ustar00rootroot00000000000000SAMPLES= colors.default colors.linux gpg.rc Mush.rc pgp2.rc pgp5.rc \ pgp6.rc Pine.rc sample.mailcap sample.neomuttrc \ sample.neomuttrc-tlr smime.rc smime_keys_test.pl Tin.rc CONTRIB_DIRS= colorschemes hcache-bench keybase logo lua vim-keys all-contrib: clean-contrib: install-contrib: $(INSTALL) -d -m 755 $(DESTDIR)$(docdir)/samples for f in $(SAMPLES); do \ $(INSTALL) -m 644 $(SRCDIR)/contrib/$$f $(DESTDIR)$(docdir)/samples; \ done for d in $(CONTRIB_DIRS); do \ echo "Creating directory $(DESTDIR)$(docdir)/$$d"; \ $(INSTALL) -d -m 755 $(DESTDIR)$(docdir)/$$d; \ for f in $(SRCDIR)/contrib/$$d/*; do \ echo "Installing $$f"; \ $(INSTALL) -m 644 $$f $(DESTDIR)$(docdir)/$$d; \ done \ done chmod +x $(DESTDIR)$(docdir)/keybase/*.sh uninstall-contrib: for f in $(SAMPLES); do \ $(RM) $(DESTDIR)$(docdir)/samples/$$f; \ done for d in $(CONTRIB_DIRS); do \ $(RM) $(DESTDIR)$(docdir)/$$d; \ done -rmdir $(DESTDIR)$(docdir)/samples -rmdir $(DESTDIR)$(docdir) # vim: set ts=8 noexpandtab: neomutt-neomutt-20171215/contrib/Mush.rc000066400000000000000000000006611321473123000200430ustar00rootroot00000000000000# # Key bindings similar to those of MUSH # bind index . display-message bind index t display-message macro index n "" bind index + next-entry bind index j next-entry bind index J next-entry bind index - previous-entry bind index k previous-entry bind index K previous-entry bind index { top-page bind index } bottom-page bind index f change-folder bind index \cu sync-mailbox bind index * flag-message neomutt-neomutt-20171215/contrib/Pine.rc000066400000000000000000000017671321473123000200320ustar00rootroot00000000000000# # This file contains commands to change the keybindings in NeoMutt to be # similar to those of PINE 3.95. # bind index v display-message bind index p previous-undeleted bind index n next-undeleted bind index ' ' next-page bind index c mail bind index g change-folder bind index w search bind index y print-message bind index x sync-mailbox bind index $ sort-mailbox bind index a tag-prefix bind index \; tag-entry # Not possible to simulate zoom-out... macro index z "~T" bind pager p previous-undeleted bind pager n next-undeleted bind pager ' ' next-page bind pager g change-folder bind pager c mail bind pager w search bind pager y print-message bind pager \n noop # PINE prints "No default action for this menu." bind pager previous-line bind pager next-line bind compose \cx send-message # PINE has different defaults for this variables set folder=~/mail set record=+sent-mail set nosave_name set postponed=~/postponed-msgs set index_format="%Z %3C %{%b %d} %-19.19L (%5c) %s" neomutt-neomutt-20171215/contrib/Tin.rc000066400000000000000000000011361321473123000176570ustar00rootroot00000000000000# From: Tom Gilbert # To: mutt-users@mutt.org # Subject: Re: Lynx-like movements # Date: Sat, 4 Sep 1999 21:09:00 +0000 # # These key bindings may be nice for notorious lynx or tin users. # bind pager previous-line bind pager next-line bind pager exit bind pager view-attachments bind attach exit bind attach view-attach bind index display-message macro index "?" bind browser select-entry macro browser "!" neomutt-neomutt-20171215/contrib/colors.default000066400000000000000000000011431321473123000214440ustar00rootroot00000000000000# -*-muttrc-*- # Colors for use with xterm and the like, white background. color hdrdefault blue white color quoted blue white color signature red white color attachment red white color prompt brightmagenta white color message brightred white color error brightred white color indicator brightyellow red color status brightgreen blue color tree black white color normal black white color markers red white color search white black color tilde brightmagenta white color index blue white ~F color index red white "~N|~O" # color body brightblack white '\*+[^*]+\*+' # color body brightblack white '_+[^_]+_+' neomutt-neomutt-20171215/contrib/colors.linux000066400000000000000000000011351321473123000211600ustar00rootroot00000000000000# -*-muttrc-*- # Palette for use with the Linux console. Black background. color hdrdefault blue black color quoted blue black color signature blue black color attachment red black color prompt brightmagenta black color message brightred black color error brightred black color indicator black red color status brightgreen blue color tree white black color normal white black color markers red black color search white black color tilde brightmagenta black color index blue black ~F color index red black "~N|~O" # color body brightwhite black '\*+[^*]+\*+' # color body brightwhite black '_+[^_]+_+' neomutt-neomutt-20171215/contrib/colorschemes/000077500000000000000000000000001321473123000212645ustar00rootroot00000000000000neomutt-neomutt-20171215/contrib/colorschemes/neonwolf-256.neomuttrc000066400000000000000000000200011321473123000253600ustar00rootroot00000000000000# Neonwolf Color Scheme for NeoMutt # Source: https://github.com/h3xx/mutt-colors-neonwolf/blob/master/mutt-colors-neonwolf-256.muttrc # Based mostly on the colors from the badwolf airline theme # custom body highlights ----------------------------------------------- # custom index highlights ---------------------------------------------- # for background in 16 color terminal, valid background colors include: # base03, bg, black, any of the non brights # style notes: # when bg=235, that's a highlighted message # normal bg=233 # basic colors --------------------------------------------------------- color error color196 color235 # message line error text color tilde color81 color233 # vi-like tildes marking blank lines color message color82 color235 color markers brightcolor232 color222 # wrapped-line /^\+/ markers color attachment brightcolor165 color235 # attachment headers color search color232 color154 # search patterns in pager color status brightcolor232 color39 color indicator brightcolor232 color154 # selected email in index color tree brightcolor165 color233 # arrow in threads (`-->') # basic monochrome screen mono bold bold mono underline underline mono indicator reverse mono error bold mono header bold "^(From|Subject|Date|To|Cc|Bcc):" mono quoted bold # index ---------------------------------------------------------------- color index color160 color233 "~A" # all messages color index color166 color233 "~E" # expired messages color index brightcolor154 color233 "~N" # new messages color index color154 color233 "~O" # old messages color index color244 color233 "~R" # read messages color index brightcolor39 color233 "~Q" # messages that have been replied to color index brightcolor154 color233 "~U" # unread messages color index brightcolor154 color233 "~U~$" # unread, unreferenced messages color index color222 color233 "~v" # messages part of a collapsed thread color index color222 color233 "~P" # messages from me #color index color39 color233 "~p!~F" # messages to me #color index color39 color233 "~N~p!~F" # new messages to me #color index color39 color233 "~U~p!~F" # unread messages to me #color index color244 color233 "~R~p!~F" # messages to me color index brightcolor165 color233 "~F" # flagged messages color index brightcolor165 color233 "~F~p" # flagged messages to me color index brightcolor165 color233 "~N~F" # new flagged messages color index brightcolor165 color233 "~N~F~p" # new flagged messages to me color index brightcolor165 color233 "~U~F~p" # new flagged messages to me color index color232 color196 "!~N ~D" # deleted messages color index color232 color196 "~N ~D" # deleted new messages color index color244 color233 "~v~(!~N)" # collapsed thread with no unread color index color81 color233 "~v~(~N)" # collapsed thread with some unread color index color81 color233 "~N~v~(~N)" # collapsed thread with unread parent # statusbg used to indicated flagged when foreground color shows other status # for collapsed thread color index color160 color233 "~v~(~F)!~N" # collapsed thread with flagged, no unread color index color81 color233 "~v~(~F~N)" # collapsed thread with some unread & flagged color index color81 color233 "~N~v~(~F~N)" # collapsed thread with unread parent & flagged color index color81 color233 "~N~v~(~F)" # collapsed thread with unread parent, no unread inside, but some flagged color index color39 color233 "~v~(~p)" # collapsed thread with unread parent, no unread inside, some to me directly color index color81 color160 "~v~(~D)" # thread with deleted (doesn't differentiate between all or partial) color index color222 color233 "~T" # tagged messages color index brightcolor222 color233 "~T~F" # tagged, flagged messages color index brightcolor222 color233 "~T~N" # tagged, new messages color index brightcolor222 color233 "~T~U" # tagged, unread messages # message headers ------------------------------------------------------ color hdrdefault brightcolor222 color235 color header brightcolor39 color235 "^(From|To|Cc|Bcc)" color header brightcolor165 color235 "^(Subject|Date)" # body ----------------------------------------------------------------- color quoted color39 color235 color quoted1 color165 color235 color quoted2 color39 color235 color quoted3 color222 color235 color quoted4 color166 color235 color signature color81 color235 # everything below /^--\s*$/ color bold color255 color233 color underline color233 color244 color normal color244 color233 ## pgp color body color160 color233 "(BAD signature)" color body color39 color233 "(Good signature)" color body color235 color233 "^gpg: Good signature .*" color body color241 color233 "^gpg: " color body color241 color160 "^gpg: BAD signature from.*" mono body bold "^gpg: Good signature" mono body bold "^gpg: BAD signature from.*" # yes, an insane URL regex color body brightcolor39 color233 "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]" # and a heavy handed email regex color body brightcolor39 color233 "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])" # simplified regex for URL & email #color body magenta default "(ftp|https?|gopher|news|telnet|finger)://[^ \"\t\r\n]+" #color body magenta default "[-a-z_0-9.]+@[-a-z_0-9.]+" # vi: ft=muttrc ts=4 sw=4 sts=4 et neomutt-neomutt-20171215/contrib/colorschemes/solarized-dark-256.neomuttrc000066400000000000000000000265451321473123000264670ustar00rootroot00000000000000# vim: filetype=muttrc # # # make sure that you are using neomutt linked against slang, not ncurses, or # suffer the consequences of weird color issues. use "neomutt -v" to check this. # custom body highlights ----------------------------------------------- # highlight my name and other personally relevant strings #color body color136 color234 "(ethan|schoonover)" # custom index highlights ---------------------------------------------- # messages which mention my name in the body #color index color136 color234 "~b \"phil(_g|\!| gregory| gold)|pgregory\" !~N !~T !~F !~p !~P" #color index J_cream color230 "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~N !~T !~F !~p !~P" #color index color136 color37 "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~T !~F !~p !~P" #color index color136 J_magent "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~F !~p !~P" ## messages which are in reference to my mails #color index J_magent color234 "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" !~N !~T !~F !~p !~P" #color index J_magent color230 "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~N !~T !~F !~p !~P" #color index J_magent color37 "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~T !~F !~p !~P" #color index J_magent color160 "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~F !~p !~P" # for background in 16 color terminal, valid background colors include: # base03, bg, black, any of the non brights # basic colors --------------------------------------------------------- color normal color241 color234 color error color160 color234 color tilde color235 color234 color message color37 color234 color markers color160 color254 color attachment color254 color234 color search color61 color234 #color status J_black J_status color status color241 color235 color indicator color234 color136 color tree color136 color234 # arrow in threads # basic monocolor screen mono bold bold mono underline underline mono indicator reverse mono error bold # index ---------------------------------------------------------------- #color index color160 color234 "~D(!~p|~p)" # deleted #color index color235 color234 ~F # flagged #color index color166 color234 ~= # duplicate messages #color index color240 color234 "~A!~N!~T!~p!~Q!~F!~D!~P" # the rest #color index J_base color234 "~A~N!~T!~p!~Q!~F!~D" # the rest, new color index color160 color234 "~A" # all messages color index color166 color234 "~E" # expired messages color index color33 color234 "~N" # new messages color index color33 color234 "~O" # old messages color index color61 color234 "~Q" # messages that have been replied to color index color240 color234 "~R" # read messages color index color33 color234 "~U" # unread messages color index color33 color234 "~U~$" # unread, unreferenced messages color index color241 color234 "~v" # messages part of a collapsed thread color index color241 color234 "~P" # messages from me color index color37 color234 "~p!~F" # messages to me color index color37 color234 "~N~p!~F" # new messages to me color index color37 color234 "~U~p!~F" # unread messages to me color index color240 color234 "~R~p!~F" # messages to me color index color160 color234 "~F" # flagged messages color index color160 color234 "~F~p" # flagged messages to me color index color160 color234 "~N~F" # new flagged messages color index color160 color234 "~N~F~p" # new flagged messages to me color index color160 color234 "~U~F~p" # new flagged messages to me color index color235 color160 "~D" # deleted messages color index color245 color234 "~v~(!~N)" # collapsed thread with no unread color index color136 color234 "~v~(~N)" # collapsed thread with some unread color index color64 color234 "~N~v~(~N)" # collapsed thread with unread parent # statusbg used to indicated flagged when foreground color shows other status # for collapsed thread color index color160 color235 "~v~(~F)!~N" # collapsed thread with flagged, no unread color index color136 color235 "~v~(~F~N)" # collapsed thread with some unread & flagged color index color64 color235 "~N~v~(~F~N)" # collapsed thread with unread parent & flagged color index color64 color235 "~N~v~(~F)" # collapsed thread with unread parent, no unread inside, but some flagged color index color37 color235 "~v~(~p)" # collapsed thread with unread parent, no unread inside, some to me directly color index color136 color160 "~v~(~D)" # thread with deleted (doesn't differentiate between all or partial) #color index color136 color234 "~(~N)" # messages in threads with some unread #color index color64 color234 "~S" # superseded messages #color index color160 color234 "~T" # tagged messages #color index color166 color160 "~=" # duplicated messages # message headers ------------------------------------------------------ #color header color240 color234 "^" color hdrdefault color240 color234 color header color241 color234 "^(From)" color header color33 color234 "^(Subject)" # body ----------------------------------------------------------------- color quoted color33 color234 color quoted1 color37 color234 color quoted2 color136 color234 color quoted3 color160 color234 color quoted4 color166 color234 color signature color240 color234 color bold color235 color234 color underline color235 color234 color normal color244 color234 # color body color245 color234 "[;:][-o][)/(|]" # emoticons color body color245 color234 "[;:][)(|]" # emoticons color body color245 color234 "[*]?((N)?ACK|CU|LOL|SCNR|BRB|BTW|CWYL|\ |FWIW|vbg|GD&R|HTH|HTHBE|IMHO|IMNSHO|\ |IRL|RTFM|ROTFL|ROFL|YMMV)[*]?" color body color245 color234 "[ ][*][^*]*[*][ ]?" # more emoticon? color body color245 color234 "[ ]?[*][^*]*[*][ ]" # more emoticon? ## pgp color body color160 color234 "(BAD signature)" color body color37 color234 "(Good signature)" color body color234 color234 "^gpg: Good signature .*" color body color241 color234 "^gpg: " color body color241 color160 "^gpg: BAD signature from.*" mono body bold "^gpg: Good signature" mono body bold "^gpg: BAD signature from.*" # yes, an insance URL regex color body color160 color234 "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]" # and a heavy handed email regex #color body J_magent color234 "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])" # Various smilies and the like #color body color230 color234 "<[Gg]>" # #color body color230 color234 "<[Bb][Gg]>" # #color body color136 color234 " [;:]-*[})>{(<|]" # :-) etc... # *bold* #color body color33 color234 "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)" #mono body bold "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)" # _underline_ #color body color33 color234 "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)" #mono body underline "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)" # /italic/ (Sometimes gets directory names) #color body color33 color234 "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)" #mono body underline "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)" # Border lines. #color body color33 color234 "( *[-+=#*~_]){6,}" #folder-hook . "color status J_black J_status " #folder-hook gmail/inbox "color status J_black color136 " #folder-hook gmail/important "color status J_black color136 " neomutt-neomutt-20171215/contrib/colorschemes/vombatidae.neomuttrc000066400000000000000000000077061321473123000253530ustar00rootroot00000000000000# NeoMutt color file # Maintainer: Jon Häggblad # URL: http://www.haeggblad.com # Last Change: 2013 May 17 # Version: 0.1 # # NeoMutt colorscheme loosely inspired by vim colorscheme wombat.vim. # # Changelog: # 0.1 - Initial version # --- vombatidae text colors --- # color normal color230 color234 # color message color230 color234 # --- slightly less yellow text colors --- color normal color253 color234 # mod # color normal color253 color233 # mod # color normal color253 default # mod color indicator color230 color238 color status color101 color16 # color tree color113 color234 # color tree color173 color234 color tree color208 color234 color signature color102 color234 color message color253 color234 color attachment color117 color234 color error color30 color234 color tilde color130 color235 color search color100 default color markers color138 default # mono bold reverse # color bold color173 color191 # mono underline reverse # color underline color48 color191 color quoted color107 color234 # quoted text color quoted1 color66 color234 color quoted2 color32 color234 color quoted3 color30 color234 color quoted4 color99 color234 color quoted5 color36 color234 color quoted6 color114 color234 color quoted7 color109 color234 color quoted8 color41 color234 color quoted9 color138 color234 # color body cyan default "((ftp|http|https)://|news:)[^ >)\"\t]+" # color body cyan default "[-a-z_0-9.+]+@[-a-z_0-9.]+" # color body red default "(^| )\\*[-a-z0-9*]+\\*[,.?]?[ \n]" # color body green default "(^| )_[-a-z0-9_]+_[,.?]?[\n]" # color body red default "(^| )\\*[-a-z0-9*]+\\*[,.?]?[ \n]" # color body green default "(^| )_[-a-z0-9_]+_[,.?]?[ \n]" color index color202 color234 ~F # Flagged color index color39 color234 ~N # New color index color39 color234 ~O color index color229 color22 ~T # Tagged color index color240 color234 ~D # Deleted # --- #mono body reverse '^(subject):.*' #color body brightwhite magenta '^(subject):.*' #mono body reverse '[[:alpha:]][[:alnum:]-]+:' #color body black cyan '[[:alpha:]][[:alnum:]-]+:' # --- header --- color hdrdefault color30 color233 color header color132 color233 '^date:' color header color153 color233 '^(to|cc|bcc):' color header color120 color233 '^from:' color header color178 color233 '^subject:' color header color31 color233 '^user-agent:' color header color29 color233 '^reply-to:' #color header magenta default '^(status|lines|date|received|sender|references):' #color header magenta default '^(pr|mime|x-|user|return|content-)[^:]*:' #color header brightyellow default '^content-type:' #color header magenta default '^content-type: *text/plain' # color header brightgreen default '^list-[^:]*:' #mono header bold '^(subject):.*$' #color header brightcyan default '^(disposition)' #color header green default '^(mail-)?followup' #color header white default '^reply' #color header brightwhite default '^(resent)' # color header brightwhite default '^from:' #mono index bold '~h "^content-type: *(multipart/(mixed|signed|encrypted)|application/)"' #color index green black '~h "^content-type: *multipart/(signed|encrypted)"' #color sidebar_new color39 color234 neomutt-neomutt-20171215/contrib/colorschemes/zenburn.neomuttrc000066400000000000000000000052341321473123000247150ustar00rootroot00000000000000# Screenshot http://trovao.droplinegnome.org/stuff/mutt-zenburnt.png # # This is a zenburn-based neomutt color scheme that is not (even by far) # complete. There's no copyright involved. Do whatever you want with it. # Just be aware that I won't be held responsible if the current color-scheme # explodes your mail client. ;) # # Do notice that this color scheme requires a terminal emulator that supports # 256 color. Any modern X terminal emulator should have support for that and # you can enable it by calling neomutt as "TERM=xterm-256color neomutt" or, if you # use screen, by adding "term screen-256color" to your .screenrc. # # This file is in the public domain. # # general-doesn't-fit stuff color normal color188 color237 color error color115 color236 color markers color142 color238 color tilde color108 color237 color status color144 color234 # index stuff color indicator color108 color236 color tree color109 color237 color index color188 color237 ~A color index color188 color237 ~N color index color188 color237 ~O color index color174 color237 ~F color index color174 color237 ~D # header stuff color hdrdefault color223 color237 color header color223 color237 "^Subject" # gpg stuff color body color188 color237 "^gpg: Good signature.*" color body color115 color236 "^gpg: BAD signature.*" color body color174 color237 "^gpg: Can't check signature.*" color body color174 color237 "^-----BEGIN PGP SIGNED MESSAGE-----" color body color174 color237 "^-----BEGIN PGP SIGNATURE-----" color body color174 color237 "^-----END PGP SIGNED MESSAGE-----" color body color174 color237 "^-----END PGP SIGNATURE-----" color body color174 color237 "^Version: GnuPG.*" color body color174 color237 "^Comment: .*" # url, email and web stuff color body color174 color237 "(finger|ftp|http|https|news|telnet)://[^ >]*" color body color174 color237 "" color body color174 color237 "www\\.[-.a-z0-9]+\\.[a-z][a-z][a-z]?([-_./~a-z0-9]+)?" color body color174 color237 "mailto: *[^ ]+\(\\i?subject=[^ ]+\)?" color body color174 color237 "[-a-z_0-9.%$]+@[-a-z_0-9.]+\\.[-a-z][-a-z]+" # misc body stuff color attachment color174 color237 #Add-ons to the message color signature color223 color237 # quote levels color quoted color108 color237 color quoted1 color116 color237 color quoted2 color247 color237 color quoted3 color108 color237 color quoted4 color116 color237 color quoted5 color247 color237 color quoted6 color108 color237 color quoted7 color116 color237 color quoted8 color247 color237 color quoted9 color108 color237 neomutt-neomutt-20171215/contrib/gpg.rc000066400000000000000000000107721321473123000177100ustar00rootroot00000000000000# -*-muttrc-*- # # Command formats for gpg. # # Version notes: # # GPG 2.1 introduces the option "--pinentry-mode", which requires # the "loopback" argument in instances where "--passphrase-fd" is # used. # # The gpg-2comp found in some comments comes from # http://70t.de/download/gpg-2comp.tar.gz # # %p The empty string when no passphrase is needed, # the string "PGPPASSFD=0" if one is needed. # # This is mostly used in conditional % sequences. # # %f Most PGP commands operate on a single file or a file # containing a message. %f expands to this file's name. # # %s When verifying signatures, there is another temporary file # containing the detached signature. %s expands to this # file's name. # # %a In "signing" contexts, this expands to the value of the # configuration variable $pgp_sign_as. You probably need to # use this within a conditional % sequence. # # %r In many contexts, neomutt passes key IDs to pgp. %r expands to # a list of key IDs. # Note that we explicitly set the comment armor header since GnuPG, when used # in some localiaztion environments, generates 8bit data in that header, thereby # breaking PGP/MIME. # decode application/pgp # set pgp_decode_command="gpg --status-fd=2 %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --quiet --batch --output - %f" # Verify a signature # set pgp_verify_command="gpg --status-fd=2 --no-verbose --quiet --batch --output - --verify %s %f" # Decrypt an attachment # set pgp_decrypt_command="gpg --status-fd=2 %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --quiet --batch --output - --decrypt %f" # Create a PGP/MIME signed attachment # # set pgp_sign_command="gpg-2comp --comment '' --no-verbose --batch --output - %?p?--passphrase-fd 0? --armor --detach-sign --textmode %?a?-u %a? %f" # set pgp_sign_command="gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --batch --quiet --output - --armor --textmode %?a?--local-user %a? --detach-sign %f" # Create a application/pgp inline signed message. This style is obsolete but still needed for Hushmail recipients and some MUAs. # # set pgp_clearsign_command="gpg-2comp --comment '' --no-verbose --batch --output - %?p?--passphrase-fd 0? --armor --textmode --clearsign %?a?-u %a? %f" # set pgp_clearsign_command="gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --batch --quiet --output - --armor --textmode %?a?--local-user %a? --clearsign %f" # Create an encrypted attachment (note that some users include the --always-trust option here) # # set pgp_encrypt_only_command="/usr/lib/neomutt/pgpewrap gpg-2comp -v --batch --output - --encrypt --textmode --armor --always-trust -- -r %r -- %f" # set pgp_encrypt_only_command="/usr/lib/neomutt/pgpewrap gpg --batch --quiet --no-verbose --output - --textmode --armor -- --recipient %r -- --encrypt %f" # Create an encrypted and signed attachment (note that some users include the --always-trust option here) # # set pgp_encrypt_sign_command="/usr/lib/neomutt/pgpewrap gpg-2comp %?p?--passphrase-fd 0? -v --batch --output - --encrypt --sign %?a?-u %a? --armor --always-trust -- -r %r -- %f" # set pgp_encrypt_sign_command="/usr/lib/neomutt/pgpewrap gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --batch --quiet --no-verbose --textmode --output - %?a?--local-user %a? --armor -- --recipient %r -- --sign --encrypt %f" # Import a key into the public key ring # set pgp_import_command="gpg --no-verbose --import %f" # Export a key from the public key ring # set pgp_export_command="gpg --no-verbose --armor --export %r" # Verify a key # set pgp_verify_key_command="gpg --verbose --batch --fingerprint --check-sigs %r" # Read in the public key ring # set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-keys %r" # Read in the secret key ring # set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-secret-keys %r" # Fetch keys # set pgp_getkeys_command="pkspxycwrap %r" # pattern for good signature - may need to be adapted to locale! # OK, here's a version which uses gnupg's message catalog: # set pgp_good_sign="^gpgv?: Good signature from" # set pgp_good_sign="`gettext -d gnupg -s 'Good signature from "' | tr -d '"'`" # # Output pattern to indicate a valid signature using --status-fd messages set pgp_good_sign="^\\[GNUPG:\\] GOODSIG" # Output pattern to verify a decryption occurred set pgp_decryption_okay="^\\[GNUPG:\\] DECRYPTION_OKAY" neomutt-neomutt-20171215/contrib/guix-neomutt.scm000066400000000000000000000030111321473123000217420ustar00rootroot00000000000000;;; Copyright (C) 2017 ng0 ;;; ;;; 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, see . ;;; ;;; How to use this file. ;;; Building neomutt from within the git checkout, skipping the hash check: ;;; cd contrib ;;; guix build -f guix-neomutt.scm (use-modules (ice-9 popen) (ice-9 match) (ice-9 rdelim) (guix packages) (guix build-system gnu) (guix gexp) ((guix build utils) #:select (with-directory-excursion)) (gnu packages) (gnu packages base) (gnu packages autotools) (gnu packages mail) (gnu packages gettext)) (define %source-dir (canonicalize-path "..")) (define-public neomutt-git (package (inherit neomutt) (name "neomutt-git") (version (string-append (package-version neomutt) "-git")) (source (local-file %source-dir #:recursive? #t)) (native-inputs `(("gettext-minimal" ,gettext-minimal) ,@(package-native-inputs neomutt))))) neomutt-git neomutt-neomutt-20171215/contrib/hcache-bench/000077500000000000000000000000001321473123000210665ustar00rootroot00000000000000neomutt-neomutt-20171215/contrib/hcache-bench/README.md000066400000000000000000000056021321473123000223500ustar00rootroot00000000000000# NeoMutt's hcache benchmark ## Introduction The shell script and the configuration file in this directory can be used to benchmark the NeoMutt hcache backends. ## Preparation In order to run the benchmark, you must have a directory in maildir format at hand. NeoMutt will load messages from there and populate the header cache with them. Please note that you'll need a reasonable large number of messages - >50k - to see anything interesting. ## Running the benchmark The script accepts the following arguments ``` -e Path to the neomutt executable -m Path to the maildir directory -t Number of times to repeat the test -b List of backends to test ``` Example: `./neomutt-hcache-bench.sh -e /usr/local/bin/neomutt -m ../maildir -t 10 -b "lmdb qdbm bdb kyotocabinet"` ## Operation The benchmark works by instructing NeoMutt to use the backends specified with `-b` one by one and to load the messages from the maildir specified with `-m`. NeoMutt is launched twice with the same configuration. The first time, no header cache storage exists, so NeoMutt populates it. The second time, the previously populated header cache storage is used to reload the headers. The times taken to execute these two operations are kept track of independently. At the end, a summary with the average times is provided. ## Sample output ```sh $ sh neomutt-hcache-bench.sh -m ~/maildir -e ../../neomutt -t 10 -b "bdb gdbm qdbm lmdb kyotocabinet tokyocabinet" Running in /tmp/tmp.TFHQ8iPy 1 - populating - bdb 1 - reloading - bdb 1 - populating - gdbm 1 - reloading - gdbm 1 - populating - qdbm 1 - reloading - qdbm 1 - populating - lmdb 1 - reloading - lmdb 1 - populating - kyotocabinet 1 - reloading - kyotocabinet 1 - populating - tokyocabinet 1 - reloading - tokyocabinet 2 - populating - bdb 2 - reloading - bdb 2 - populating - gdbm .... 10 - populating - kyotocabinet 10 - reloading - kyotocabinet 10 - populating - tokyocabinet 10 - reloading - tokyocabinet *** populate bdb 8.814 real 4.227 user 2.667 sys gdbm 24.938 real 4.082 user 3.852 sys qdbm 9.806 real 5.201 user 2.480 sys lmdb 7.637 real 3.589 user 2.385 sys kyotocabinet 11.493 real 6.456 user 2.537 sys tokyocabinet 9.521 real 5.104 user 2.356 sys *** reload bdb 2.101 real 1.027 user .614 sys gdbm 3.474 real .885 user .584 sys qdbm 2.170 real 1.142 user .552 sys lmdb 1.694 real .845 user .478 sys kyotocabinet 2.729 real 1.541 user .591 sys tokyocabinet 2.526 real 1.395 user .581 sys ``` ## Notes The benchmark uses a temporary directory for the log files and the header cache storage files. These are left available for inspection. This also means that *you* must take care of removing the temporary directory once you are done. The path to the temporary directory is printed on standard output when the benchmark starts, e.g., `Running in /tmp/tmp.WjSFtdPf`. neomutt-neomutt-20171215/contrib/hcache-bench/neomutt-hcache-bench.sh000077500000000000000000000064371321473123000254200ustar00rootroot00000000000000#!/bin/sh # # Copyright 2017 Pietro Cerutti # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # usage() { echo "Usage: $(basename "$0") -e -m -t -b " echo "" echo " -e Path to the neomutt executable" echo " -m Path to a maildir directory" echo " -t Number of times to repeat the test" echo " -b List of backends to test" echo "" } while getopts e:m:t:b: OPT; do case "$OPT" in e) NEOMUTT="$OPTARG" ;; m) MAILDIR="$OPTARG" ;; t) TIMES="$OPTARG" ;; b) BACKENDS="$OPTARG" ;; *) usage exit 1 esac done if [ -z "$MAILDIR" ] || [ -z "$NEOMUTT" ] || [ -z "$TIMES" ] || [ -z "$BACKENDS" ]; then usage exit 1 fi CWD=$(dirname $(realpath $0)) TMPDIR=$(mktemp -d) echo "Running in $TMPDIR" exe() { export my_backend=$1 export my_maildir=$MAILDIR export my_tmpdir=$TMPDIR t=$(time -p $NEOMUTT -F "$CWD"/neomuttrc 2>&1 > /dev/null) echo "$t" | xargs } extract() { grep "$2" "$TMPDIR/result-$1.txt" | awk "{print \$$3}" | xargs } avg() { echo "3 k 0 $* $(printf "%*s" "$TIMES" "" | tr ' ' '+') $TIMES / p" | dc } width=${#TIMES} # generate for i in $(seq "$TIMES"); do for b in $BACKENDS; do rm -f "$TMPDIR"/hcache* # do it twice - the first will populate the cache, the second will reload it printf "%${width}d - populating - $b\n" "$i" t1=$(exe "$b") printf "%${width}d - reloading - $b\n" "$i" t2=$(exe "$b") s=$(du -k "$TMPDIR/hcache-$b" | awk '{print $1}') echo "$b $s $t1" >> "$TMPDIR"/result-populate.txt echo "$b $s $t2" >> "$TMPDIR"/result-reload.txt done done for f in populate reload; do echo "" echo "*** $f" for b in $BACKENDS; do real=$(avg "$(extract "$f" "$b" 4)") user=$(avg "$(extract "$f" "$b" 6)") sys=$(avg "$(extract "$f" "$b" 8)") printf "%-15s" "$b" echo "$real real $user user $sys sys" done done neomutt-neomutt-20171215/contrib/hcache-bench/neomuttrc000066400000000000000000000002741321473123000230340ustar00rootroot00000000000000set read_inc=0 set write_inc=0 set folder=$my_maildir set spoolfile=$my_maildir set header_cache_backend=$my_backend set header_cache=$my_tmpdir/hcache-$my_backend folder-hook . exec exit neomutt-neomutt-20171215/contrib/keybase/000077500000000000000000000000001321473123000202215ustar00rootroot00000000000000neomutt-neomutt-20171215/contrib/keybase/LICENSE000066400000000000000000000022721321473123000212310ustar00rootroot00000000000000This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to neomutt-neomutt-20171215/contrib/keybase/README.md000066400000000000000000000032271321473123000215040ustar00rootroot00000000000000# KeybaseMutt A work in progress. KeybaseMutt exists to let its users utilize Keybase within NeoMutt. (Unfortunately, not natively (yet).) ## Quick start Run the install script. It will set up the directory's and paths for you. Do what it says or suffer the consequences! Then just throw these into your .muttrc `set editor = 'echo %s > ~/.neomutt/keybaseMutt/.tmp; vim %s'` `macro compose K "unset wait_keypython ~/.neomutt/keybaseMutt/keybase.pyset wait_key` Done! ## How to use KeybaseMutt Write an email. In the screen right before you send your email (called the pager mode), press "K". ![](pagerMode.png) (Press 'K' when you're here) You can now use four Keybase commands in this "shell". (I thought it best to limit the user to four commands for security reasons. Running unusual commands would overwrite the email, forcing the user to rewrite the email again.) The commands are: - `keybase encrypt [user]` - `keybase pgp encrypt [user]` - `keybase sign` - `keybase pgp sign` ## Decrypting and verifying messages Unfortunately, there isn't an easy way to decrypt or verify messages through a macro. Instead, you'll need to use the pipe feature of neomutt. Opening the email, you'll need to navigate to the actual attachment. (Press "V" when you're reading the email.) ![](attachmentView.png) Then pipe ("|") it through the one of the scripts. (like "pgpverify.sh" or "decrypt.sh") Please note, using an attachment is very important when decrypting emails that include html. The scripts are unable to separate html and text. Make sure that the attachment is "text/plain" rather than "text/html". neomutt-neomutt-20171215/contrib/keybase/attachmentView.png000066400000000000000000001602361321473123000237220ustar00rootroot00000000000000PNG  IHDR8CbKGD pHYs  tIMEE* IDATx^w\U9Ζ^6RHt6PĮk~`AD B-B 酔̽ǝm&z> ;3[ιmgι}眓dd䜓hd$)B'Z+͓gdMv.?hE_DhN˯P8 p=L?% _-m͗˕sA\\Q4޾9:)RcEѵ>w}8:sT|+cmBJ2Ɠ1&?Fe%巩}Zi5-2ps(W}dl:&V)[eg_6/[y[兩$埗U/Rpfsʜ;_To&[z>at^jv /|ygHԢm̾3 ]6ERG/5D7WC{h+c<_|eQ6/ܫ̼ry/ɮl&׷ 2g>lc\seSNcP~gՍr$c̵=]蜌|W @+ C'c<keJ5HEogd9F_ J&i Id},cNPM. 3ɖc.aZ?U%]̾_x퉎)F2Ss%'ޖ.") iIN2F{~$ݷD G.Yn n 6ߔ?>Kʾ՗E);W| I&+ضٗϯ`!J[F;G5(Gf]\Ӛpb3L*^첊ԣ),.i Z#'7.Hߍ99 ( EazvnUcKENU6[|NsvDM=*hD[ Y_A0{w,9Gy3[{ly'k=IѲFI2E[#E:Y7IA=a ODsNY/e?D۔=/e_RYL8){mssd~mzJIB*H'N7d/jt*^)Q ipatgJ._d5k0L èQ.PiC3=d{}fmt?RZ{)J(ڟ=?w(LJHdJF.s[F'睊AZTJ~"!NmSP2V - %UJjh!'ѽSt.޽**JRanw+ɯH(WnP!%TJg?[el9e#/!c} dv))QQ)K(L)J<)ө]RAЂgC}tҷNWj;uSjޥwx3\_m^0T&nϐ-t`a{]4Dm*^`ʴΙX'L\oJhixa%<%ID߮ۿYJֲK\?8n}sZ|`SmxFU7;"Kf&ISv[\.>7$I;'-Ӛ-ݺD\Sә6ks@_}+S>q {ն%3_ӎ[ڷT\[W PU"иͥ~竗;nΚ^UYu-ZWWrxdYޖ$}C#U]ɓޑ~tT=p5~~5f&بW ȥjF[)8:S6~}>^G_/|e:nM_x{^x{UCvFy=s6h޺}᙭ά9A` Y[zVYusctw_z^uI_ՉZN:\z_|0~=\4߹ٴ`t1р^^kѪ}bcR%I /T 7/))Jw 7of֠m`]}ڲBƬ5|BT;5+m=BO/*I:gb7-*~m/] OusF>LVk`^Y6!I|^Q3EgN{[o+d_28F/XuʯrZ3^}֪4z}ԗZˋԳ?]x^ ?Pa[y IEGꊴ>y뒤5[I6mX}{]"'}l^_/٫+eʾH|M=Rzn {VC5IR{H~q:}RU%&YƭQ}eetҤ%IAwh~%):A zS_;u;{{eJ~x3Mٞ? SSj(S!婮Խ[g'ʧ)mU}~Ж2ؑ UV3ƮG\(BI|])O7?ٴo2VVe"dZ8P1Q#]-If]S"{ˮ<%]yz><]SsO8hNW_ի+g=aY4D,c=d+6e7=u7$}G.{F4j$W獒$ Stضg^$gWև^OzE3^ϸ\C/ o_:rjY ^X2HG_ɣkh%}%P{G?XhK^0L߻cΝX_=u& }tEO[zgmq씳Z M:WWٯ%I[ oZz?kŇ4iV?kfz0v+7E+Noo%rsZ+|nL. ln?nWWFÕ$w֒v=bzW'uP)Öc'N#IQnIIM۫Z)i-NG8}z}e?M^o뭥1ݓn3^nՌku!Kt!KfK7}/GjYgn뺇'+iSG,ԟ_2NW_dz6 T6Л  HzW'3fՕMm/GSS6lܷ//Yw$rk+$ժO2qmii} |Z;Je*vm^vco$]<4q#לuW|w;q`d:I[*$I}7'o%;5uڔeGrӑ{FXCӥǼnjRs*@ %}06e+ӭAz傿?k]0\8=zEYZKҘ[5izoVlṵ>uGNl~tT=`Dd% #z@{uJQ˧i:pq[3t[0zQٜr;@)V.ʍ=:O0q*@gO_bsk1煪zU'SK~$INzEz= JRohI6wuRTG7M$ dT> uX /*ǿ+)ѹB=+S$7RuI_ߤd{_vkE%i~OyUY,}ާ[>uYҞg)YJ?qV_z9g|}=V>-O,rhc0swNӺVOޕ2l˾mG5z&7'Na[L#]>9]uM,m_ Sw75 ~f]yK>.K'~=¡cu:wSzO*G&2]c_;#Iڶ#5[iH:GrCORݦ^q|;:Z6V悲,$mk3z6Q%DrhњںB/}6$=h>~‚_}b?]zktɱ)fzsf?oH҈Uws=wuOOk듵G[ʰ:t^rC87gWʰ-QoKh^r?~ 7df_)xk觳oZr; t'H{dWVI&ب#7hAQ ?đ4q SW4sZG=Қ8rkECҷVǬU2eu(5'~p4tdz.]S@]`ԣxێpn?7q>4mhxem)O(3EDeӽ2%k{O3|n{h˾&iJ^x{~|ܴ=Q>O{jK`{j˾fhJa\MvR3ol2aa< 3 45ۦzF7U eŁ=˼?}BRA`@F#7unK=Tvz8=xƩ UZCS*9嗕J/%?{;#pF^dv6ߪp;J|Jag/٧e'Oi6I01q6;#5˿3;<`t;;[n[Jwe*כ)IwiNm~]fRo>;%I/Tsh+/+||rA ;|%3jT׻$-)*^{ɿ27Ru}Z+gIV_x/_v]mۦ.~KweƎU⊯)+w/}T=+5.Xה2- 8m˱mʤjM'j$4Ig>["G *˥0lW68blW[ ͈QαҔApo[Ff`y+;~Rي9_(| mdjj}$/Lg;gK/*}?٣TUmejj=&;mԉnWA!;}ȑrk(uSO<9u.f>/L+YpRUUӼ6MM76;='ӭSOYf |B\|o+?;\W*_mOiV|T*I#2G36&S3Lnb̸q])&6l=hⷚ&.ڙe2ۃR_W}f4Wm۪_wCm6kӖ)Yf%~3IQYNȁݮ=q,RmUsmqڊmfe\ޭZ%miZQBٮ./N9I Dw(ly_P*{N裲Ow$;~R;&J7&;[9wR_Rc |B{V~l [^!wdm4ύv~&]roUp߽$oѼnNK%,K…o*.+eu9Sމ'Ekk eqkV+ʿ⫹P _n:jȿ"[VTp}ro/0@q'qD>o9Z>#5?qGW0IwQB>۸Qo?y'0TG<'}rkV+oK~;;nw.XP*Wl$)ӷƍgheN_[R|bO'+N춟ȭZ`cN}*x|N+S8VY{g)|y[e w ?_x^?ܠDZ<5NBnٽ+x}tcYR(U἗cWolJYiiMd{TLqbqڬJGNssez,z7GjþQ=5z˯GvԻsY IDAT%`Ks4.|3VTD$WZϚu=FᢅMS[o~-3hO^}2G-[lprQs݆ zR9F;ˌ';%*|ӷ?wշΰ".)}/d'N=(ocݻ oȭXu--~K_RIN>EnZKäe'LT8٢p8wL~cEYZ?Aceο@ڶMr 2ݺ^gzL>')|ҷ"WOىv5#ˎL;gvJ 4:iH~rމWXeخtZdg%GN˿2&ɭ]p|x2F$IvVu{$3t3Tֿ)1l/-zFhء,&wTY)f-ջsR]G/|Qn; f-)fg'=hvO=o2Uu1cӕ8'9-;`y.I2#G*os>,yWtS.xE/ϯx3=$3lX׸,ϓyp/̞pǼYVhNJw.UJgo=h#1mYlRڣbf%d,۸1z>oc%b%ⴋ,+VlrPjh<_뮕eRUNKTgpkv+VDAGz޽wKWd$9ѣVR))}_.|S wߕ)l ,j 6J۶6]@޽J/Nx3v5QH(u$InQDFGPnTpaT;#RpJ}{J~J~ ͓O9++|h(#ju} 3}֯Ie?kՎ/gw2C*??oyX]Q2ƕJ!9 ݻ7Rgv_Q)w?y'$;yf璵|ZZ{NJ~hDOPOO'>4焸{[Jf|L>2C<9 EwL:xB ?Oj̠RuuȑrW2[T/H??ٓ>ףf%=T,,Q1cqi*๹`scbQvQI>YWQ)NYV[`;hl1agP8w_jkw{ ٧vPp]lWeVwQQcʕrʌGnBt:Uݨ+R*ݢqdX),^ Cz&rNɛv¹;8='3j!!K<ҿZVQ*/( y矓92#FJ`=r۷',ve>"4{OVFA2}J;v4MӭLFțvҋG ۴QǔPD;}CCݰrq+VDC)zAZwk G|0{osʧU0GzCN.lyR焸{kv쐂@L71χۣQ _p[Pڥ4VVUEeS[+n㮊[관K&Gnӊf%%ceh㈣ Qe _|A^f&J8:XgUr >,ǩXjcgmݱCvxUג`钨M 1ēKI&#<78ݱN"oZ=0#o С?\oLM̠A@㪪$kv줦AzُFcɧ4IoN:MW nM>vx- Nމ'Iֆ .[RO7IWvuk$cN-)}2)weuSnÆhii/}Aml;{Jك';~*w3``9cշ|Z[8AaƍSTg̙>pGF#%3ު"h ^ۃ6KûwۺXhUhkkyʏuS$§P+2;h_jﵡͪ\ڄ%ojm,1=$|U៥1ڦEvQIfD%ve>E[%6 (~}gn(Җm/m,5 M70;O?-7+omY8IFu1F+\Pgl4op粫c}D>a2Æ)}Mrp*y3g*|a5:;'%Ž.YܶgܶaG0QfVӕ[^n [^ԙavHo.Ta7_nQfh_B_]?eݹ'3ICʭX.ˊHͯe [LVرr sKF魍_>%'|qTTyG-Z`cJ_;޽_|ر )*cdj)\B0eC5226sZ8q*w󊟍ruϙ FDeuP moAͲ/_l][K6mh^TԆݣ̠L16r˖)3[vAvm>2G4;TMbv)mݒܪw[nwo%ezzj>{iv)x(]Q=*כ#ի,3zmH?fwҿFϢ.ZJ\'wy-Ϡn۶Гmƍfh—^0QWo{6? ~uҞﱸ|?va y섉M;|BҥQ/=^}UnӦ&UZYgcd'e 9඿ˡ l[N W[[ܚ5rkD-R}!2ÆY;$k>$7(>(իw3ͪ- yFO_i'xz/o%bmٮ8].~ ;ǁFm[㴋,+Nlr&kc<A#> ;&iÅo*ڍ >:s?v s)2{vg.fRʎicm ާo%z௛ozy3wˌ'_Vp,o>Ͱa8! Ot>Se d4 b>曕2!ۭܺMӚ8<`s¹s<`>}J㏗w-ek1Rv/1N8Q#s턉/=O>Ņ[^;w懢aY(}?d8PfHGn2)ThH\5I *sWpJ_{=~l+'Y|]S,ӧ3,H=.{6nP7H씩oNOF{Ξ-mN8QmgFst=[YV9N MP*Jk6ڬjQ6a)~{vwQۥ:y8F/ͪ-M$3kM8^Ծ该bmٮ8].N9U;#:8&ib.Tlr&ky(DAs6jK=H8A9|w qK/k5m|XTN/=[U?/Y^m~LvR_o6nE^#UwJ%s:a'/ϟjG)'W>ߙ#K&:%ٶ[lQk'f%RI;]MW.睍[>iI& mXuy3_I]MT …J~N>EɧJDs)|Ϣ,vm]t6a.~K/,str.Oupp _zQv6s;Lyǟ I(oãa$e˾ٿLaNJVUE/} )KrB{Ѕ)nenW{쨮D:@'AD$-J$޽z衪R[U3F ޘ#J 6zgRT$-JR`䢸u0ݯT,c$ ]`*@A:9K#e.J%." ]`"hιRI@A@0e$Nڨ/= p" tIcJ%m***|6d1eNK=RI$pM>3ƞ@Cnܞ ~E@ #h'0ЁXK;0ATTT( w/=޿O6Drε0\K9NK%0E:hYC`*@g$.I" ]Q6 蔌1 = _VoӈÛr ]`eh_ ]! ]# `704NN# ]`eGo_=/E0t 1 .ȈeXc@dιTJ@e'14t~N^5f1O#F /;eXɔJmb =:Jmb- '眜chZ (;k-@lh0# GPv+ bdR Ю ;#ο<`2FPNFdT"C@(3dй8 ]C@@'2#?:̓19s'JTZ@fdd$YC@;VoӈK%NK%tl.`N9'9ɖJ\ f:04tj&=SF}VS:>mt΁0tv&cy0t .šR)d34tvQ_.0tvgk ðZ/[" ] т7iĈᥒ`'`NeZɵб9IF @yVah ,#@@@`@D T@Ggd$2:%c/t~ 1F>?@2!Щ9䜓1::DBdLI2Fa5Z<1TRg:7'I%R:80= ](14t~ 7$;}`u5z4Lyp!hh_RkՎ*`vPX]R, *ٻNb p{ RlXZH09Te$5 4.S6 pp}4)׼v{E dB[hs4I(wy8 zhֹFͲ\'U$F`38`mUIRfAV V pj\.|LӗJ8 NR|knY4Կ$,uw*ɨYYĴ IDAT{, `U''AP h3 8m Xߞ*O@O@D>pP㲥`y*Oq}5, z4 `P$˻V,SQB'ҳ}-׏X pjkǏ'NB p `S(0a=%IIO= |5oﯹ^fQ~0*i `u˻VnTOX\?W>`Ug8Ja8 { h3h0iT}ࠞ$X^{>n/(?;UX]'IUYc$Q`}n `UUɘX0*) `$ `P,j0)t2] hshO@@%8NR, A]-IY< |5oﯹ^fQ~BE p `u:4'X^<ނ`m'X\=?X0I `Um*Iy#{0I(Π*5}gQj۞* `=K\y{2:I3X^'Y  N `38HB_eE8 `V,'qPf?'X]=>U?pXmK,VW k_s̢$0*tY4(We pU,c< JҞX]$&9S'B _0 t˫TUYc1RTUXӗ_RUX_%5>_e_}V,_0꺓n0:i0 T(WUI880_0c_%e p$ `Ө,A=}$&9'q}5, z4$8 0)XH"Y=~-VW X p `P`qݝ$I%XĈ 0ںtFR(GV'Nj}8͂p k_s̢,WJT1 p|m Nb p 8$)0/@+Vw_+Ng8 0I(Nb8$i `s}%8/$q4>_e')NB p `P`q*W$'1:0 t8JB\'X]ӭX^w'IIlONF͒^U2z'>_e'YĘX$G ?OptU$'X\0:'PI,W$X]0IT* `}_}8/_MITJ |5oﯹ^fQ~Jg,Y$n,rY ~*NSnRI0򪒸 p+ `3V,`0:I%fA*`ytZ N˫1F>pP[*ɘ8z|>_eGU%U `PY%XZ p m pU `5K `=K `l] Q$W}e8/3fAo?+Oq}5, N`yNX]Qcd81FRѳ$VUI< >0yTUFR,a8_0twFϒ[ߛ_7VWIUeKVV+n,[ o'Ij\?M0H5oﯹ^fQ~PUX]'IwFgYC x`uxyLr,,V7HȖ qyJ< J:gPUY Cψ`iN= $mغtG $IǷIlOI&AIF4'>_e))[, ĝ\w* `U*R,*I2A86ppt: `yu 0:JK0~X t%8N*q1pP_k*c* `\y{2*UQQ `Ub p d8J%8NZ ;V=Iĝ_{`}{* `uw:n;{FR(Iw,uwRIǷYھ|MU%=p`Urk_s̢{MrFVX޾$czӭ8RtgYC7[O\'ݝVs}I{n/(?oAJ0*J2e8N[өJ G4]0K{; /9ln7 `=7w}G< ,΢.S|}࠾|-Ig5`6f5oﯹ^fQ~7ӳ,VJY c `X\J,'1 `=z_ `(NRIY c_0ꪒTF, XJ2f!VPn?uپǷԯtXTwow+5oﯹ^fQ~P{ҝY$>XU{Nƨe8NֿdT,Vv?4*I:c<2Ql WU>ҳVdIY_#d{\`Q^%>fYҩNFYCFҎ,kgQ3}TK0$#[%F+Kdt5oﯹ^fQ~wØ8dK\XZw:6plz Z p8v`yU0:I˫QFg0e8PUU޽y>Jdˇ+kmqCBf!ȄCI S igZJ>@Z @hhC),Ɩ؉-;-J}~xWJvy]o_0f$IkqU7ZP)7sciRVdyyPVԓIE 0n-i ~( kI`YPn5`?u3 0f$I}(Sm} SmM]زi@KV0$,S$InZ\[H0JI)%u2`ʵ$uccc(nm0-IZj߯eb$II-e=KYݿCQn(tk%Ij0&*`Y0)&F$X 0j҆23ښ0,hIu`6Tf@`f(fAKqP)5-f`VԴg)Ww( Y5e tkc90,hV@̄]I)I0RRJI 0ȕ$u(k 0vIߛ `P0X 0+3(fAIR溅~|( L̂7sdiRVdyyPgH 0#3N6A0v&fDMP0 0#ג(ƯMN*n*f`0mnV_I`f(nst0-I3 0Ε=KYݿCQ &m(T+V@̎R&_;ƭX 0 & `RO-` `+)I`J)I``ZKZK-CAF0(sBX?>`J/,f`V K{%{$#P EneA 0v:`69֒R0N}o`RSbL7fF7Y  `Q0蕘 umcc}( RJIךZ K{%{<- `3C 0#jJ0JRPQ`& `-'GRL̄ 5 e7Ʈ$Fd[m ֹC9T-$I@hIj΁=KYݿCQ˿&F$IS_)IQ̄ 0r%-޷fAW3 0c-v@\KZK-Q_+nss0%% #ג 3 IDAT`+/LJLn~K7fDQҞ_ޡ(ϐ`Fַ S$- `1+)IIj-@Z5e( t+iI0 0 jRL^))I`q;Y֢ u(8Ը 0z-IU_IRn֎0lKIRZn~jiRVdyyPTJI)%0$0cNɱ)f6#womnZ)0Lh5`ƫ%5E 0fԴ4+Fos`Nim V@̀cg;zP)m9qWRJjSҞ_ޡ(gNN0^}j `1k-)n:n(tu.Z뇲L0 ӭ%0ȵ0JI)%u(tkMV@f4ؕ+PVV@Ɖ9eq{;vɡ<Sj~˶Zg)Ww( Ys]R[kCYX7?:7g`.}`J)i)fA)%5n^`Zki}KM)CY]M:/ؕZRR'5`Q+%n]k-%&F}ZJ:/R$]`Nu kiI)) [kGL/4I rRPY[ڳ+Y^;ଵַ$u(kK)C9X55 `k}D 0z(FW7Ʈ$0ص֒X 0~ hPZBL`J)IJ43,l֎J0v$oN\k[j4p.,Y,/ RƮn(tkOKhQ>I5/ȵ%0&+KP)V\JRcV撔tCA[U\ 0niRLHҬdx(t+E 0Jc}֧,n?}<Sj}o4ؕRRkMMPY[ڳ+Y^;,6\KP)VJ+Ʈ$iƭl>X 0r'% jԤhF`Ok-R^)%- `+(o2 n̙$eێCCy;Z. eb >5 e=KYݿCQRo} mc}=`&-u(tk-)(Ʈ֒k ebΥ0m `Qk}R0ɗ/2iD 0f}')醂L/.QYs)/LC[)%) h+l۱9|h(ڶcW0N8ZJjkm( -Y,/p~#m#],SO77g`Ji)CA[IFRP)ַ%PuIJ7Ƭ֚֒N 0nFf4FZKjPiWJ 0RRRRL0,$ `k#WJI)IY0R'$em8jq$IL:79g)Ww( Y*& 0ZZKRLO 0&ƭJcV纤T/՚RjPַZ mccc;Ƭ$% `}ZRP֒T/Z3 0vmr75n_))vlG0Hk} ӭ:x.,Y,/pJ)ֆLf4ZRRK)CYXKf`ZK 0 J:`Uޤ'x}?`ZRZJ5c}JZk0JIZֹ,SRJjo ej%$5)CI\kIٺ}r( ں$`ZKRkyŹKc]x}]nܿtjuw_~sեW3L|ǿ3<׮Hwn2yrݾk$9s&$G-/]:k ϼ ?хSݽc-?ռdqa=<3gWW.?=~,U=O}Q~7^Ǐ._ugO7<}gK|ݞxA~oF?Y7ܑ3Y|rkKN'M^}CWw]|g%yo7_wW.ڹk]n_ys|{w߰?$^8Ӭrvw|E 8~33QJ5W\{#|3$^y }ɃY;8$o?Y\X/G-ߕ FK^uOzn|>}eD^{O;sxm>o\s$?o[ɥ]-'ݯL=}:k'l:ӟ{^{{[ct%9ضEy{WZҥksc.=+tݹ ;/N{#?7k?$ɏ}9r;s銋[هvKˋ/=?}3$'>wq>ɷ/H\p~{^RkKߗWܕ/~"cz\}-xpWސz7ޚY~/Oߗȯ~y䉭yS.}8޷;|V)A.ؾ>·vz?^ݓ[wpGVNי^1߿&^"IrG??|Nf~!;8#g9S¼{8|zx|q(e]'̷ܷ;W]XN?wwrͽ=m=2^6kX>5o^T'w?)w{%7s;泱1)X]ȓk~ۖll۲5?OzN3li~?=Sk}NJ_꽏Oo4q Q 7RJ9e{g3ɜ?\ݛ͋.y|(>Cu$oօ$u{YO}IzX}`h}:kS'hΕ]Ǔ$]z)-M,_˳C᪇Wݙw<_~]~x`lp99$K)pyɊꅹgUٸvsxqa= _,=⑔s~ ;U_?ZݛRZܡ)S^9SZMCGց'<{]?%/~0u7ޚ7 =fRk-ݗV7<m~ᮣI/{o̝ۏ}gIyMnù/\~pZ<6oKߒRZ>Sy%NA>tLVpV+gyzk|s WߟoO^ ;?2kO~wNe|otemWߙWϵW<=3_}|_<-ΏV+MIMJ,x|υ~y7ޒ׼\9]=qt>>-/u$G(=r^t .d~-w_GuՏ,[޷"[Qsŕp7_wWz=]y-꽓9{.|,A#[U@5 H@A/ xOpa$$$chB%6~={͠1"Fdo}B}sfFFF̜[0.ܿVO[oᅯ~7̟?;/BwzŞW2ÿk _7/woS=?gx+տ+~~_}/7?ٿ_p?o9忶spppppppppppppppppppppppppp8n8nwW>?88888888888888888888888888880_ W~ju/O&,_|,}pppppppppppppppppppppppppppp+/.w@Z:>888888888888888888888888888#^88888888888888888888888888888i" 胃:,/^uppppppppppppppppppppppppppppޮ~/;OaLW~/ođ}|,upppppppppppppppppppppppppppp𫏸|}"uppppppppppppppppppppppppppppS|pppppppppppppppppppppppppppks 胃<,/(xpppppppppppppppppppppppppppp ^߃_|LWpo Ng1 Tpo~=5GII:mR/ZMGB;mբi-tﶴ@P>R?` W}QN^v=Ǖ|QV|ZwG3\/_ e|OT[!?2=:⽱䗳e̤هNm0"/Ei]>lc2.{K#?=2X=ոssۇ˯ Өuyr}J[mhZ|6;}RT>ۨuն{ÆȬJқ ;xnM&G6$9gɇ|VS`OMX uAʛcj,c65s3/N5kN!V73%gYtΑﴡeE f @Dd^;z C6}>_Zckm4ۑOo8SECy" }ҵUc`rBl68ndP}43ȃG,߄j5;A4ǭd-bhrkWp㷗fK_CjP/n=,O(CuWy F};Ӳ䥚\cSϳ!e5t-]ݕH|-fƼs17k G9N׳ a!T- 3z+B#1Noӗ]~ wy3{*-9%c.o~uSoq.pyVH2'38 IDAT'icݕtjIcSتe4k6'dIRyW_ V`1*N`25}11:CQŒ.mprǜGԮ]<ۡ} Z8xD /ӵ/f17޿XQzldie+ˊ5h́^sK<3OW5%#dkXhC?{_됱Gn}odd-t/Q՚Z2O6zmoK>n2VI]xQḵ ߄p 8&G1?YMbȀX✜] ȹOq~0x1r8| s`ƥ <(Z~MC//Sg;ΫOT,{ُx\ LH J /jsǰCyY)/L*mw W3zC+»? nƗ6K}Gc-ZA~~)Iw*lߔ|zJSfrTu?qyj3c,"ISs*$BͧWCx"=Fq[-m$I6cB!?H'07??m+o9OOހj?Ŀp|c`1}}\| 8 jQ`\H>A-J:INH潤rۓzV`&˭Cs)=Z'ɑy[d0zgv15*'XGS Tcq4rmp ]B9Pr.n:Sn^'T{MC_o\vٔ+o +d`WuS~g9+9p 阫b|9^NKላ/8DBv< s_ݧۖ/C̷.Y_Ɠ& i|n=q]s,s6MLj~qv!nhr5`PN;6Kv27f[~ =ϸm(lOWbNa=Ubv4_i={ 1v0⾹A"Oٴ se1gYܸmG5h1c6 mkf܀}|(!/9/*5v@n-үtά7e$F_2*P%S`61<&AKi[63VoWs Î%_맅2/SK%\^J/utG%ɔ4㛕Lkz ;J75׷vVCȪK&Ds\4j Y#0bLfٝy}F^*XP7`=~o`7y}yeb6 m^5k\vl dշg=<5-֑3KB'[ s-ͲѓΩ._PJgM䕲5y ݜcW,)-X#:2iX'ڿBJI ^ q1>,U,(=kVXœZ(yA0!$ <`pՖ~ XZ&>2d˱ogwl9a2Hi_'3Om#13 ¿{iCCڮ|\z&`Qfu`}U][PZESָ"vk g`M:ҿ :R\>#bb#XL*3@/04tUl9 czj/UK}vVzL!,w+ʥfB%sԢSZu!yb:feE8*+5.M<6Y{ޘjOc?[l@ 8X\1Hr Ovnt|)ը#ۄo.4r ]JV{F7cq@O?"y+91 OOt<' PE\Ehژ7 p|S^o9AÛ$6H}|b*}I>E's/\Utf.O[9g?!B?-_{g<1B:ex<6R=o #Hp 1φi,Qj=lfQG@ QOe{/jm|띹 5LBM3RG)V<%}1m qJGxWQO;o$[y *+*9l"nU}<ǁnp6 i y# 3bϾCllc{]Le.Y/p_^20MN?uPL>Uo_ԃ^{r {x6YbgԶ;}1c䁍жDKۜvkO)r2k;P׈quW 1|ۂ̅]t.T[n`񷫂#@0D^~s և#&wb8E{n!ϵdkw5&y#Y@K#Pi0Qģ"ɼugl;Q&]}ͭudԾgSTXc7cr6EV˘cl"yR^;&}ֶ]BTɡ1fju^.|Ua?clX}V'Y=z7;#vYu-,[kT:z@ bJ4sُub;aP~%d>KnU^%Sʤ(~5`/beB&5tIު\cAM=GerrUbrsJo@³0h[yCÞKvJLB{G+rN-)ϚnP SQlQj%fM6BuDklCd}Mu@.6:LIY+dڥz_B౶&zr=Ky[x c)V'be= /u.[Ť~, R }sb|*>ꄵ`lkZ8~;#}g\MDQ-N]zFL]q6%k?祇UQ嫣AC><^\48-Yw>0Yfn9gsRfpMU)@XIRgeU:AgVGAmǻZ2tM% -R0*TXo-ykKJWYˬfro\Y7]b<1Ots[-o1 W7[Bj8I<}M nOX\ޑxc"䱐1H&h!2?h06PZ4 t U<2]"VY.^r^_rk"ZT);8ɶK_ .ro ^LZCr.V@@]#!UIU1&-up\fpB37֠9ϴ* K;Xw3ۑ׼9Ze8,x*4f0utJwEb roҸ ߚ7KJBmVQXzg$*b<(Ϣ$=:ZGµ7mЦq5/j R [ne7EOrQ_̜,ѧ~z1UWʵGjf+:tCk(L@cy7Ik M8%1Z^:xR\rⲟ"] }RzhA^oc¿k'\7^z ZO ujue6LHO!o8)çjYWMk!f"Q`vĄ +lbSgJﮥv['Է9 3F@qexJdINdd}{aet{ inԅGXl2.g:{`R7lF5la]>Q P啕-廻vA:NvCGO`E JЫ!^琏3xw I~ g~xZ`B`4@Ƌ-ZwUuBK|Hؔy8MsaHM;"5oYm2Ynt:Z7iFbwj7K}-T?E}}~^'u%ˈ?{Gpc>X>eҾGcߎWdxs}5qVt ٝiƀ sEͬާ9qfKfCگ-}1eTcQv"XnyҖo>ͽ-c <)|pئbaSG`6<VDŽӴ]{ęZGNt;y(ɵU>Z=< Jpy6qE>:hOʷW7Lw8\\[nH14Fqp'3Jο3s0AG'{e$KtoW~E߻mK/ŷ{Ayǚ%WJ\v wy'CO64y=&tܚ9S' wxc(eI{ҙe }%G}M97xv|Fx#C<9G﩮W-rk%w|qFıv1O6IxvƁf>Hĺ9)[sn"_i<K:NA+"'.a1~7oMUy>hĥI谵k?eKVڨH۶tk5ҩEhϐ(h8pw!{8A0zxo1V!cJA}kë~_~o#6u&_aX:R oW_~a&~mnTRΟgS:c^(uPma|˷c |$K1lD&gnvk$OgS>ާW[,k[ r`$>C*E ͅ*3D/a4p=ؔhZ\@ؚg9x'J̋˦ގVΝ I$=S gCi]7jqOuEEsTf,>-7?Yzs_zqyfTܓ}XH;fMo.;7 ij|Lt Q@,tIw1$RsbS9bSǿy8 t/F` ~{ok 'me)\k>ϙ7jd~;lW?YqGs~݌OΓSx`fy2"ovik̓auY\:Q"?XlŲ AY P<[mEނ< @QyVsV7c]ejqa|[Q|wp]S60Є)mb3e5uMY5ɛz}[䚛Sl,d7r ƌy [?~SMۅPFrSZV=͜|*# -[)Bv~I56ϓlǚ_,v-{Ѽʳ46f31BgqᭇȾ+,4d}-jGls,o.f$ Iqn{cc vf_Eׁ-N8p)@8㛸o'<.:$.F/g^K_YwNLK 77%iA=c˷tPC5n'ə;ѧ N*^y,WlP3IcCbC.n]kP:`ѧl>f=GTJXwl[N=ϛd|ظXM+ndVH/ȫ5xeO'/W:ȇ!h)WY4|b`.a66ہ?K}e*n IDATNIfۑ=!sӼ8Ϸei䃗FpfRQ_;65OQh_ ۼּ9}}=ϔX󛶴M;c5MQ Ү{@ʥ}Lhi|Xq0po3&(v| ƚ,ykCsiNcջP@E[ʜsx>T>HG%F o;{- d'w\ k\lrst0 _;&JDwĦ|N#.lߝno+|Ol ߂QǮyh[ML܍2W!-t3|cU SDjvZ󚯅}CwbTy}̗g:3dȴdLc V<2+4F1fY+*PdE4{1eư^\sx _10D Qž(׏OLjHՁڧY'`r?9,ύ̋a-Vϗ{ <tm6^ ͆>:is ")߾x8,,ޤaw/yQv(ak뱼96 r]͸,xtĩ۶>Fy#{5עs(f괽a>!vr]A{%aT~[ƇT7A1ZO(?35tLO戸[jͶyag5Uǖ<QZ9FYV"+GZ 4CVU8 r;Q|){IfCq[koCz0G`B- |)_WR k3c.\fQcbWt^|Z@BzIq~MX4߁лԣ۲5sɲGDjݶ!9C8KD85f<%Uu `!>9ߏcQ̋sT2V:<Ȍ[Mo3+\7vմQd#㥊 ;G.9Cw]l*ڠF)ߛgSL+C^s{y`ِgauJ>EŴÀ #hQ6}$/ .KCI'U HvG? '5iww(VvJvI*2xvݠekJvMaVY3h$V{7ԅSzƒP5d~x):c?si=,?ݦ}oK.:(rC5sARҽ0G%=sO[߼#m1s+:q=sBۙDj޶V}?`*8WUqe0K'=UW{v߳|3>Pck 0ZٶۯIvT5ԟT u?6Yq~y}4ݖC8FwАfO9'Ӄٞ*6~Aޏ^[ݱY Jc[W_r8 ֩&3:q-ΏZ6"g:E/󠧀bo7˞xXbDm8Q_2眓N\BOT.豩^>JVtv&2Z(SPjG/9g#UT-fɃyOR.XtPtVhl!9ϓpmJwP7|]lɜc!0cL|t^۲]V:!ht! >3_\{ ]};gH5kk촛z\u }2xcīT!9Ic 6MT3gG8oot:D?-4-ߕO5ܧ=p>=*^S*lϬ9`h߹kis+YE!{Cq7_ŗ^1 _mZL+|AպYw-60܃:8^cIrVg)>QXT;ʵ,Q)K>W۪lF C&dlvm.+M5A!i"RІj8Y-ö>}Hr7y03T? +n1s/!kKtPL`HBs8W\Wtj>#Gf2-jNuz^0rK~3s:~chXI,v\~7A?GI}q-3CJaRZ>Ton2T[{ƂoSBW>礏NteOpN5!y-m,_DlHio8W,d?;WDQacwoH*G[y&fD_[7K5nHlM#@ułUXESG}e{3W_(:؄|T7,/Q7OX('oYݝ8'…#9 Of_ٜ'c=m#s5y'L(yq0`Wˬ7Ɵ[L^a!}sfr1![ہz&m\Tb]39RF@_ +Fڛw: z B?Ӎr>'YU":Nþ[e+?t}{Ur~Rcq e@&x}<{1Pch6nxV*t^(Nh_0N4svp %T0 {B e؃Pׅ̘eaPu}j+ʮӗE@v>ʱ/o`rwhBˈ,#(Hǹe.eV2cigU?PyM+oWk:^ܲ>6Ӷ7ʇ-r̆1"d='Dl F 5'xŐkQZ8T~q*+'EwͥG5h?c徖 @Zy)C }"=˶3X۳!sfܪt}#۔c:'|AK; u0M8oQ9NZo>tFnu-]9]C}Uڢ>1׷ulNCfMkt6ڊ"lVy/Ivq f>en{P=lK[|,")K2E!!/D\hT.ǧL-؉1yv,Ƣ>6ĺ}4<-23gg5_t<%2&J~44+s3p.=KɑKY?BN܏@CO÷BL\d3v`T. 鉑? U,园4_./}b9'V,5DE1ϢY򌐵SO10/GruG,,]񶩏];օڏoߔ h1tfAѼA̙2~c&U*K=kTTmS_]1PcVᨼ 'ju` 'lq4XR<*_ ;ǂ]UU۞ p྿yABn箈]˧ܢfl$L[O>"(+]<8 "UFEPI*@u( OHS:1~8*d7&i=_O74zU[?o9eHҶp\h[ [JNY0R+ΊjP_pMF -#pLx!vP{DCT$F([7g.uxW|734ОuFǡ^Vjyԉ|]:>h6TaW-{[1E9iz @o}EV]XcyԿAjx27̇MTV,<9K4J~\kuHU.J_5L| <.(u:lzнB mqLt^=`bC*U22{emk[mGKk>Gm/>x-egГ'rz>1'.3QzG\u%GwiÔ^D>}ϧePW G-!V`/)k{#>mK#Er meOKVmvpޕ ɝyݛwV/SũR*? S8NJyT7E8Xv p>/8o=BZ ڑx;ʋ.}=WS WNΝO<פs}IJ%3p&,'TrJXۺY`]kި?L@t>ׅh{AxQSE8{1W:Ο2|(z1CBzvjz>Xw$DNCkuUJW䰎v&ౄ'\ޮ[ k'mS[ _[k<[|(&w}79vץ27Vˮӆy 9~Lov5mVz'7OuݟC /iGvqBec h+'X21y:ڞuܷ m0e&Noܑ@U>ۗ E|j+Ƨş'0߿~hqT'p6t߼p7'5IDATp|5d.= ʺ2)]|m{Ł1cʗvf]nxRڽs"S |{VN\~Y%&7߉]@O.ɑׅ+ҮDb\F 2?@=t YV1X}zzƸK/cʹRm\k{!u͵r*>!Bq=6(gpV.ݍF>f-%Ou*II}?CSyYqjKr`Z"XSE#VTӐ(^[pj3N-9[p=594Q,҇LpGvo; liE*v0}#9l˗( ^CהoE^:zNρcmAαP?9KrF4̬c_=}|iU9x5Sv%Ŗ23 {s]Ҏ}Þλ.H{m#̞NGr;拹څ<$l͟w[K>4ԣ!hx3v*FU!YeX]hJ;)>a{N%&vSո7tG+ܗ]⫚?/v#_cBFGmx(C'ߌIhf'SR>41W^J9튆o#[syU68l_<6q_=@llIx jqIza9S?Zž I0%mHۊ| oRHztD_o;SgOX8yd㛱h.7/. 3a< 9a[|Nn4^|Ƨ͸+7gu~&.˔REC]/c3@xb͕n}'K}K_}1_cڥJ02ƩDc@ʣίM1&J(Ci_c3'P@!sψ 鼜lDڳ;9aFKgUڹW0_ά23E蓊lyBu>Z%œﶞ9ӮgK5&J7f[uBKnX)I]?o_!:my}8k,6%?Д)^q_K"ՈZs:{Hel֘(%R}m:<бc·#G"P/Qp]- `iOQ#@\6]PR55kwm8ߪ#f6糙[p`uYJ_^--LiKlT|<~d890^M פ? bv3_mw%~Ĝ2#7sķ7+^7䘸x"gO(U8 e0M"YekFY0ss*uX׹_[yG凔9z@O\ Љ1:1gh8(J":Z껮;˄]\,TZfgt*Cܡ6z@+ybE9ͷ. A(?IӾԇW-LMոNcט975^ Q/:۪de)GϹR_b- v脕ڀjX8}{ u0k\{5Kb UմP;c1f3 ,+)ٯ@{sDNVMFs[99K_[?O?0ȆI v_E^[:Ɛp{0?\ ߅;Zc=m) O^W>'دhR~Ci'M.O'i'c0ŹiS8R4gqة3yli,#n{rpwDa_AY?E77ϣ{X8ɹKM#&72ەyr OvK}t(KkG1oVX`Q5lA>yq)XόϠEPsKnpcQo6cƩ@Gy:Ƶ Bb,\K`];Y>E K#88ACAze*E#bRdۅuLEʣۥ*b m ;tM;'|f]rmdnYm+5+?K:W6P7HW5F|3d/N= O\db]4Ǹ2 ,f}!kC{ކFpIvUhG/#j\d w,2608/TI=%^YSΆQe΃t5`_6 VlR0?a͏{mr%x}zjG#_ʁilbp9{}G ~ Q -{b^h+,Qk>UG̲8$d많Ղ -Fuhq[X]D>I99a}mU1R/T;;<%0GcK(¦Gzߕ>A5Y!v-sԠnr狝)ZF3acb l|Jrk-c:WԚ{RqdZF> ,+;Sb{HW#b6Ŭ/1M3rU@ӷF3"-`0m]}is)xTS%c1v]W9ιwYԘϲ+59~ _kzjy}̫>%-va!9IQ$i$Sn'.P d=@/T`iw;>z0ȕ6+}kBKH>mLkާ(j-0nxR3&Ar}{gXIss ZMg?O a\eGg V=L2#m9i>PՌqn(_ ,ر9'vS/_PZ~vrEuf29n zg%[9z(Rt`Lh9A"` o#Hֽܴw:ޞţkUAcd]' y~}rO*ec}.*%?:!}Bl3 [c=[c(O!V?1}ƪ^ڿ^xxʣrssjCCbL,d;q*- Q?!́1O?)' H?҈:"cfֿ!g1x%F^Zy~ed190՘Iu#_df}^D͉=wqrWs}YeGmG!=OnX2Oh,Ѷ~+f&5Ğ5츮 }=dMƑC;⏋m3fzϋub3thKuLȘ]r `p>݌ڈSNڶȰEbO{wm1_.R;@v|X^%kA9I}o%|6Z81i-ⳮu8[x{;voY}NKވ@z>ϟ6Tu\"xF_3q#:86a{5q+~Qm8|ᓵCr*Yˮ'WCkkw9s͆gumQ8p d}~[.\_b27*,evݷ9 ެG9r]E֧$w_V~PQ+u^͘ua`Ys{_ȼ]]u}]][JZv-qy8fpsj컪h=myVO޵)/6"9_soϟQvGס΃oޘytsF__oypЊY_{o1,;v?{D-s2n3YsgZ/"X7Pw+~1LpBWǣ{NzΛ]G}|<}swjSu=s\O59ǬVmxxXy{9bx<*sGd%ߟI-Z>Y=vL|e v^Tws>~lvsr!{V۞q6VȻg=HNJ7Dg>,}z_vXƣ9}Dd}؎cfDS}sw7w <ܧ{[WZ{N.gCNj;;Fw פf-[q1srcJ^o-Ix6:dl.K{^|ǻ lC+ mᲭv8C0cwˌNYaֱr.cENwk_g6y׆^Lj;?԰"f;Y2#buƿ.c̩};:y_[cXD1{.@ϯ^1O3iy{DOE-?]V9?V[ȏǫz{^cz M8sZc=9mC׾=b97gʕYN>׳|DD~UYMh(Ok$kruX-I;GJ1΅ܸ"bՏK?ƛ$rn(<ՙw[ΰ,2fٚM9g~Qe] etcKs]i Yno_֛/gUus 9FUyvvi|]GY6"r-/>=qU;%#z]n-^J}!u|[+zЊE[=_\ωlg)rkns,e{[ok?k榯Տ}T="rWcG/{v̵|k9?θΘֵX/j]yvgTƺ14bOde 9ڽTez}__991Q~>{Q;ϭ=jf>~_q-U`Fy\}]w[Q0x7 -b1r~@Q}͜c_h=+>?U,G٧>惻ԙ;gkCW_ͯ]_{c5ݣdkV\Ezgvfst2]G7Xfiq}}Xq/fucS=]#/m޷/y7E98xe}1^l9_Dizóy#H_QlgD<6/<>V~0wYq/ec4M^{s{G o" QiǶVbo/w]Ѯڵ-\?~7N?W~kz5h%aCX?WEoc~߿* 0 !,| 0 !,| 0 !,| 0 !,| 0 !,| 0 !,| 0 !,| 0 !llu IENDB`neomutt-neomutt-20171215/contrib/keybase/decrypt.sh000077500000000000000000000001101321473123000222220ustar00rootroot00000000000000#!/bin/sh sed -n 's/^.*BEGIN KEYBASE/BEGIN KEYBASE/p' | keybase decrypt neomutt-neomutt-20171215/contrib/keybase/install.sh000066400000000000000000000042401321473123000222230ustar00rootroot00000000000000#!/bin/sh # If no directory exists, make it exist. if [ -d "$HOME/.neomutt/keybaseMutt" ]; then # If someone already has a backup, complain. if [ -d "$HOME/.neomutt/keybaseMuttBACKUP" ]; then #echo "$HOME/.neomutt/keybaseMuttBACKUP exists" echo "You are going to overwrite your backup. Are you sure you want to do this? [y|n]" read -r overwrite # If they want to delete their backup. if [ "$overwrite" = 'y' ]; then cp -R "$HOME/.neomutt/keybaseMutt" "$HOME/.neomutt/keybaseMuttBACKUP" rm -r "$HOME/.neomutt/keybaseMutt" mkdir -p "$HOME/.neomutt/keybaseMutt/scripts" # Otherwise, abort mission. else echo "ABORT! ABORT! ABORT!" exit 1 fi elif [ ! -d "$HOME/.neomutt/keybaseMuttBACKUP" ]; then echo "Backing up previous install." cp -R "$HOME/.neomutt/keybaseMutt" "$HOME/.neomutt/keybaseMuttBACKUP" rm -r "$HOME/.neomutt/keybaseMutt" mkdir -p "$HOME/.neomutt/keybaseMutt/scripts" fi # Otherwise, make a backup elif [ ! -d "$HOME/.neomutt/keybaseMutt" ]; then echo "Installing your program..." mkdir -p "$HOME/.neomutt/keybaseMutt/scripts" fi # Copy my directory to your directory. cp ./keybase.py "$HOME/.neomutt/keybaseMutt" cp ./pgpdecrypt.sh "$HOME/.neomutt/keybaseMutt/scripts" cp ./decrypt.sh "$HOME/.neomutt/keybaseMutt/scripts" cp ./verify.sh "$HOME/.neomutt/keybaseMutt/scripts" cp ./pgpverify.sh "$HOME/.neomutt/keybaseMutt/scripts" # Yay! Stuff's installed! echo "You'll need to include a path to '~/.neomutt/keybaseMutt/scripts' in your shell's rc file. If you've done this previously on your computer, press 'n'." echo "Do you use [b]ash, [k]sh, or [z]sh? [n]" echo "(You use $SHELL)" read -r shellInput if [ "$shellInput" = 'b' ]; then echo 'export PATH="$PATH:~/.neomutt/keybaseMutt/scripts"' >> "$HOME/.bashrc" elif [ "$shellInput" = 'k' ]; then echo 'export PATH="$PATH:~/.neomutt/keybaseMutt/scripts"' >> "$HOME/.kshrc" elif [ "$shellInput" = 'z' ]; then echo 'export PATH="$PATH:~/.neomutt/keybaseMutt/scripts"' >> "$HOME/.zshrc" else echo "If you use something another shell, you'll need to add the path manually." fi echo "Please restart your shell to be able to use the scripts (closing and reopening the terminal is easiest)." neomutt-neomutt-20171215/contrib/keybase/keybase.py000066400000000000000000000041251321473123000222200ustar00rootroot00000000000000#! /usr/bin/env python # Written by Joshua Jordi import os def helpfunc(): print("Run keybase commands here as if you were using keybase. WARNING: this program is not capable of MIME formatting. Only inline.") print("To encrypt, use keybase syntax. (ie. 'keybase encrypt jakkinstewart' or 'keybase pgp encrypt jakkinstewart') Do not include a '-i' or '-o'. This script uses them in the background. Including either flag will mess with the script. (Unless that's what you want to do.)") print("Don't worry about finding or attaching the file, the macro will take care of that.") print("To sign, give it the style ('sign' or 'pgp sign'. It will automatically include the signature in the file.") print("This program will not be able to decrypt or verify messages. I've created separate scripts for that.") print('Type "quit" to quit.') def encryptSign(parameters): os.system('echo $HOME > .file') directory = open('.file', 'r') pwd = directory.read().strip('\n') directory.close() tmp = open("%s/.neomutt/keybaseMutt/.tmp" % pwd, "r") tmp = tmp.read().strip("\n") print("Working....") os.system('%s -i %s -o %s' % (parameters, tmp, tmp)) print("Done!") #def sign(parameters): # os.system('pwd > .file') # directory = open('.file', 'r') # pwd = directory.read().strip('\n') # directory.close() # tmp = open('%s/.neomutt/keybaseMutt/.tmp' % pwd, 'r') # tmp = tmp.read().strip('\n') # print("Working...") # os.system('%s -i %s -o %s' % (parameters, tmp, tmp)) # print("Done!") exitVar = '' print("Type help to learn how to use me.") while exitVar.lower() != 'quit': inputStuffs = input('mutt#: ') if (inputStuffs.lower() == 'help'): helpfunc() elif ('encrypt' in inputStuffs): encryptSign(inputStuffs) #elif ('decrypt' in inputStuffs): # decrypt(inputStuffs) elif ('sign' in inputStuffs): encryptSign(inputStuffs) elif ('quit' in inputStuffs or 'exit' in inputStuffs): exitVar = 'quit' else: print("You didn't use a known keybase command.") os.system('rm .file') neomutt-neomutt-20171215/contrib/keybase/neomuttrc000066400000000000000000000003221321473123000221610ustar00rootroot00000000000000set editor = 'echo %s > ~/.neomutt/keybaseMutt/.tmp; vim %s' macro compose K "unset wait_keypython ~/.neomutt/keybaseMutt/keybase.pyset wait_key neomutt-neomutt-20171215/contrib/keybase/pagerMode.png000066400000000000000000001521731321473123000226430ustar00rootroot00000000000000PNG  IHDR8CbKGD pHYs  tIME"~g IDATx^w|]zHJ@:HHG~6+@|bW.*6*&I v\ry^gvwvvgϛqbVQut]GQtMZ^:uE):cKNRRJv? ufw)ǢXGѶEJqvKst{PlDuUz.lh0if4M+t+<^mʫ_}םe.-Ke-ۢUŲ7+8x1yQy>DQ wXx6۝#f _rފ]yRm]-˺Ίᬢj=-,lV2m((ME `00pQۅǮJ*~*F[fY~Ke]GE1訊^JZw*~åoR,csًV:?233/~Z`K}=,=iBuWe\eXZzQiݯRt<ya?* ^^^N~~3U-iԋms) QQdySjpEkyJGq)O(꒯kA*_酿?ޜ?ܜCN !?o % *B`04 ]׭eE[Rz+#r!8W^²gʫXIX?..zLUUQdFtF!)n+O79GB!B!B!״m3E5TGBq-14]"'QoIB!B!BT\fP5xR=8 /vuQJ tD׸-##ǃ9 (#+H7iEO#wc*l=|=M)VGor }o T7ae #:ocT&5ӓ{j.\MU'T}Q ܽL;|jKf6(ܡL^ x{]K p+CIQ.ٴqo5 /v·ktz4;A\b^+OaYq/ V ~mi`LƵ*B!B!Yv{M3EIJ/ M#'|9v}'Cr|T ȦUZlw-V Sc s-tw_uDawֆ~9K1W |1oXjL`h|jl=RziRR^q0vV"3NdʫT\zmj@F#:d]tLµ*B!B!wYQ>˷m9nݨeD_Z0kc_a̷拉wRv?67?Hxp&2Y&/iJNUpѯW5&]d籪LٞTa-8ˡ@fۏTᕡiY(!=ZF`y`5MjxS[KI3}vԫB!Wꋒ=wϦl/ 15Sm{ٙr/8SU8s4bW?ڒ[phRwrTΨ#B!B!+v$'@*4 @XPQ4k$zZ6NOXm<߬p]ˀG0Sҽ]|eql ~^ pIwlr5 Qġi0 OȰ .v i4Q-Xl^,0{kfMEst\}S4i +;pxZ35U9g2z>{5.pT&M%?<#ǫ7Rs\WyiWѷ1r1U"/0v5mkzkgby|}iS Ճb/NpEyEY%xbW)GS?#7xUnmq1^N̽L 8w̮\#:q| #Hv",l[wlfHoFCޝ' a3u>S{;?svЬ\l68İ4޿g2Uϡsiҁ{ylz9n4r9\ o aE] mC3~Ѐ\moMR߼\ +n_s9>FdxԷ]؛B_]O;Jj#Ҁr>͙wiQr/}9 v,? 1i NpUY?Ҳ==4yuGx׭!":7:hN'Gu3311r=J o`.?7׷_ i0-m3ܹW٤!99/geWJkrOM&֖|r naAY|pZ<cgxcK]lY"ϏB!B!bmXI_.'dWmDҼIL/u(>VFt:@V ޻21cEck\DIў锂Nl$ݝh vPV?S9-ۓE;nZUQUY^mvze6w~Z `8=!4 HgoO=5sw>y X2{k}FڍW> . yƠ|Jb?WΡ#BS]>?UAʂu=0/dz3w[]lyR bL^#IZ)Ʀá=k/NpUY?GIjwB4OTP0LO.2U'\.gٕyh¾jsrlrS~g̽J{)+@NQ#$UӯHN7@X1.E1Ζ+*!B!B!/͇C9C*?C:5X]Icw&՗䋥Mw'[eD|GQ+vXy {Z8 o3}-|!˺^坟Vu$ eD2g\^2sx1)dx_jGWzq_ ]c幔s9 N_pY^.fzIJ9.߭jDD47<Ð0Iő3ԪoY;[#B!B!(5MaIlmFt:PX?\׆43[mL_фi"7Ҳ=́m\2=ʷ.憽BY}(:\r\~>&|<nYuۅB\Biٖ h0UTU'`Ԃ.6].\UtYIA4BS6=cUh_gb%5ۓwZ`6$q{C4 %rT'dzY]Q4ZY+H _69 j(:'qҞ+upu=W4QΛ;θϦ5R0,7@jU(_ˮ;)AU,/f~.t>ݛg+ggrwWG!B!BqA/ bxZ%!^fA$OTFs \r 1ak \춗J>E_'\%4d~N9Hui[-Ni03ݡ2:?uS?\n0/Äe{Gji6hu‰8|&lOfn9G'3׃pEa@ߕ}uU5,bOZ-/_Kc-[dFI*Hf].-#'To%]NVom7BYʺ/*R=]WXYyFnU1Np)/cvz4`*nY]glr ɥEs4x+`&\r^7}pxl@ZՈ7!ᓝ)8[PZ\E!B!B!A)KԦ+̑3ğ &2iٞl/}\‚8bQ-ݹMS30o@r&{àŇ@:(%90:Ea0x yEC3TiY0kG]WؓB$ZE#^T)Hm<]:{gٕ"|t=JU?7[3,=zN(\Q‚2B߇)~R!*q an:^Q!B!B!ĥ:n`h_OݳLfqt4 !9ݿOup/.ȐB!B!B!0 :-#΢ NomBN4g2d0oegX!B!B!Wbh!B!B!B!מm3AuKYey>(D!B!B!B`lP !B!B!B!麎tGV_~F;~ /: M#oԣڪu!U+QQ#J]5`4b|1̳ļo87c2nyYL3D?u 8 PGg~1_jt_C\7޴Ygos@m˦B[ tVm F[- A'Q6f^h{vٌR&{G @g֡'%Ԫq  mm^mJ(]oУMvf'%b: QcJ e k'N$حezS>R]s5||tu{4hPWKۄB!B!]#()SzmJԘ Srg75ڡԩy\ V/gK~D7嗺J+wix:J`ea6 a^iKжlP6mPj0hѶo6k+WOOԨ(KV0vm\8 &$H jHHH,u]y4MC]3os;{Ϻ癿]ZDٮkjՐβ+?n9כi3>_A8Q^M> @vNF!B!B!5 ::(yRk{Ye6?~y>AӬ{럶}ss3Io}ou5Oy2ǎ)u_ځOLޓ[Q|LiM=Rm[5v۷:uj3/ٺc#+.^Evԟa{f,ǐY51qv¸ظuO=_˦m0n2DԫkΑD7/qv0pePN6З%+ϦՌy,j{:F8qvX׺nD]{|G IDAT_ƍ0f% ]̜=[wnd%5m?\F[oϹuFV[ԏߣRJ\Qn0d^:۷`}ݻ~XcG>0k֯d<|1S&M~pq&_ݼ<,Ym61{oֽ=o'e;UB!B!Y{뺆~xډu#;yZ C'&b^a$jkf) a%1<c@%f͋.7_%׬FCm|Bq@?qM0{Әͅ` ݺb]04k;ӏcEm T=@om݂&mv;7Ʋik׀jhqq+Vf& ;;,SY実>ڬ/@oo :axm#C^zVښ/V;v4c:Qhc|M=ڸMNx{Tc6L&~cS&C:u ~.L:+WsN+trkt~9yʫ/Wߒ|&׾C{ƿ<&Mc%\{GGGGthΙOhPU&C )f,{)gЫg_ +;;^=-n_}%VgQ44:@ddC<̈(oV[Xb>A݈:U+W[.7RU=qmá3{.qq4iҨ4G;nxmԫw'3L~mBBB޳~;}UvӋ``sOӧ_o$z YٿO<5ӾfX{hܤMg&5_~>ߠ>td˾QLć ky/XOOBi^m ㈻1Mf-ZX42WڮCGw!kk+'F͛;_~.bwIYHF{M#/?חy~ݼԩm|9 ˓FQ,[FQJʕmzW٧_ %%ѥk'ڷokm2͜:y CøΩM&N~mIJJbΝ6z9?g~]Y xsgGSJeΝV݅FL4?ʌo$55uk᧙gp,\P4Ħ&˦!ywFPpC f fsܱ ooozE&mz67i {L6/{q8sU;%4?zƋc^jL|el{b|5K)QL;jݒ7uaSϳl]v;Qnټ Y >7'qm B!B!DR'JDӐoIb=*U-ޓVrnT^EC*E24Rը4D?dRXmrm ~bJx8hC;pҨu)1/X@믒Sw4|ҶnAP0'(x Uji-FFM6iǏF/6j~¶g(aaO~B*cu+Wy_uDF: pZ.̫Wa>s^2˽q [Q[F[&TT `8j0}5z zmL~Ч/gЯ?_~FVJFO/5%=W'=lOK[!7iM[lmܰ& N222*xdggk_hڨ%?W6V FdT$(VN8hӸpUʞr$&$>u_lՂ/iݶu;Y05JヨFf͛RvM W۷豬Z;oݧ6<̞;cۮM9tGcZL}f^7 zEkmhadqa 'Mm|||UuAM6?M7׼s.۸r~RQl*JL6|3x\wֽqLxUuӾ׭7lt? !B!B뇇'uMC[t=1=JdY_vfT"J~`.WNeys-)u- h6bmfuQBmVm=y2L~ڶ7 GyfCԮF#ys3mݑ0tJ4̸)[ϲ?=#%8c| /S`i!ԘK\=eBmC~hg1 jiFmϩY=l^{ve˒\z/]RSSmm[MukFMڛ̙3Ѭy3 [K-15N}61Osr0_Yh .,-16_ȹ/DGFŹ]\dfʴ4.klS/b;%2y-[,v90muyO?Q+Mxӧl5,GT %ua;R}~ jvaaahOOUϞѓZ+\222 YHNNN#:'O.ۻ{.>uf>3V,e}&9޻hР>z—ϰwUvv6/\̋/EU2˹}n09e>%+y/-ѵλ訑]k֡iYYY9rMѶ] Ly_Ⴧ9~]C̵hb-\oqϳ}N2t=;+ωg x+ܑ\4T\EQܖ^UUS>7uر|շ̛W !B!B.k˜R7-de4hhct>v>ސkiF#JOTnWR6J~=3C퍡{w חu̺yy(ErsoX.-[ڴJjl=g/ .%֩Vgo't1C gQ7n2XUMY^7lh铏PBc^GrTJÆS|nQK|ac|^d~̓WR?u븠׸m7 萟o/jt4w>ڶwhǞ={HQ1dmԯ_Qhةw[ztٽo$!h_wȴYܹ s13+haќ n^z={ή|)7=|}}iҵwGrrr?2vGOgnc3>nW1mP)۷Ã۳;hgwLf3O'QZR~ 6<>( .+Г1 ku_hb]G۱2=#vՖ@Q*wCni:=seHy`in6mق~ƨ;a^7*UP4lg& Rӭk0͘کچ ڽ?2Y|]nLV-x%-UmXޚ4Ç8 p".];w={ 쬐}1ԂS'mpaz҃իp9R/!'~ ??+Vиq#5QΝ36f^bC其a=e(ApK}UjׯZU-ǭe,.Wyx <ӧx7XdWDBB`[DxUt-^~euF-mb?K^ǟMϏ)Y[lcC䣼?ȟ ͻzZ,[Xʖ1kn<='O{wEB뵉,X&j5|R=lٱ1sƕd0۷{,3wӶM6ٳزcW-潩iw\\+?!B!B!4(Qt]Gu\^rKHHzRׅVF%:&#=̘F glټ̘!~'lUUGn4kތmb|ktaaytHڵߟq{xgʻ9|Q+r`+KI)B!B!ĵMnc6ח4GAl͚7`lܰdWL‰BhZRוG4n?Q5F0y~ ޮ!kjՐβ+?n9כi3>_A8Q^M> @vNF'GB!B!Zq팡+&+Y\_=v3 cJW ^Rח'!!jժo@_`p¶߀,Y6fcmn3u^}}}FGZߏ}k\w7W/)u_ڷeٺs#W/၇Ϻ.((?Mz#۷åzq)免i+*:>zk=v3̶+Swr4hP _w^,_;GQjۇfJe[slݱU1T1WT ;W'm;طw>{_>ؑ=̚+ٰy-O=_LI_ ۷=ƭx'<& IDATX6mqXLyyy2Ybvmb߬YUgL|}Q B!B!,p+MdkXכ0i\];%mbwh֤)=r x(n9 JBBеjd3="lݺ%/Lzp|}eggӫod5L232mb##raF FQL}}jݒ/g| Fɧ̝=SYj eׇӧغe:f]lټG~zIJr7is| V]n]*{ti.C1g\bhҤQi7fwnڨW?)Nf3ۄнg7||IOOw+;w턗_M瞦O$I&.n7Գxj3}ͮиI#z%=-gG`yK|շ$Iv*_ޛmbZ9 өKGαzL8/:WA@@D?-E?/U.\p [@jԬvXm}j]ނU-ZCeUZU=h5)[FF Ąō*U՜xqϟ=ojyBkYrwFRe_-ZTYժ"cRE Zt#&ڴ!)tc6jyA߫U떶Zpr*^EܳM͞mf;,gɆ T7_cǍV@z*RZb=7e4:蓱P\\\t;SIK:vj)_LӶ۵bJ-m$`0ڣUuxwT)2LJKK?R/_Q ׯ窭gjַߩsj}5iX5+VLmrvvh,FO> =c2cd\megSlJj؜l)t"u1H*݊h4߀ms`0`0L2:qrjkZ6m[i_~;zLu%׬Urqq73R}zUfmӤu`eddo>,i+g%##CruuU˗/[dEZ0ZUUԮݪWN%hѢ޻ߜFLA <\=^ogl\JGwuwusUJխ>TVG5UZ5Uxq9::Z}\JL&kEyEm^n)QΝ?>WsqvVqrusӕ+ȰE@6âCVGN?&M+AIҁ1jҴN̴lqpשSIʏx߫d.UJs窃|^x4mcМkْP rrڸ,~~ܥ|"11Q j}ܿ6zR;Zlڽ{T#Ǭ_sPqqxGU;4{YVٳ笊7{. gg]Uk?a<-_չz_VFFL\kCVɿnIRh ŵkgՠa~|o*h:e_?#U$I+[PIϒZuzECqwgKR/LA=.%'ˣeqF۱=RNNjWsf{V[iӿdR7{cZg `.+paü5)5z6qF] ;`U6 0@#FӸ$n-17ic$I|FK'OWs)Kg2mteffjÆPIRUƣ{Ev2] Vy5}T=rTaJMMmR͜1*{2Lںu^xyEG`0h`gϫ;c>TV/y޽4gnߺ]/gJa|oRժUS[tE5jY!+{Ħ-:||1U^Jyj_u9mڴY9p ӧvGkTzӶN;S玊?s[enY3r"7kѪPbh𠡒z3%JkhO^5 `NĽ#F^otdF4FֆoǏ4AolICV2mNAEOh 쭞k5zV/SZZ~e.]$JVJ~=Μ>S2_Gh-$I[l+^־͋ވ=f͟ߗ#A]1L۽ӾѸ4ny/[ +kW aF._<ږ3(D=Z>@/v*IK5vhUX^qU[u4xP4b0;AGr%(#AJMMe 5ɤ~>֋/wNEXz[W3{Mf$ܩr^6dڴlD[q䃗$A` g (UN1Qh+hcZLyKE5*`'E7ӿhTj;oYSjk(N\pAaᒤ !SI-{0;!g''6'4xnX{iաc|g$:tjʾ>V}`0 \]]UHzEs\"3iDխ_T\l t|e$)AFqc'c`O5zRvn6__O|]ǿ4mAhIRĦ-2`ISTHUEѸ1JՕ+ Yb\vvdYP Z{0؉K.i(Im;r͝3_'P%tyLΑ 9 @A0 "(`e0TGҥJKܿ(-I:w$iH99;Yg,rן5~~ܥ|`'<==դicFU^U(.$ig.EoUbyV%oAC& 0@#FӸw`W}=EJJ:Ѝa>kegg3 V~խ+Tbq͙=bd d+wʫׯT핐h+|`7( v3vp9}JF$pxRtL+ڊ;/iG ֤UѪY4/h14g,[;JU/X{p79;9ɉ0܎;wԘqmv?tf͜#w=cޑ3gbwkZ^pzMK~[N2-Z(gg[s_MVE,'~;o)bk"!`PED):&[@ PТyܽU!ak89ՠvjܳMI\]]w2_{=*CcgK~j\ 'EFmUhzM:Iŋ(X !.6N]^|^hHrS5jT?X~~ۿ ͙:, ҥK[Qǿf̚B׳S keܤUf|=Sqq⧧7%˕HfϙЍ?/Hju1rƍGYJ^ W g?-W7W]|9P0~M>YPrr"7Ǡڻ'P+E5x0%%%icH||*[त$I_iuWѡzw{2L  ukI֯ $El"(I2L:y欬,]||]zWLA <\=^ogl\JGUVV- iiiZ*X&(;+[,rVc=-=e2tElnRSRi_N׆V׊*] C($i`^bԡS{ >Dv_X=xU(p[ &^彬|ssh/\ew``#TWb=kutȱ<{T9fumrrvRXvRRRj~=??_uI~~r{n+h:eo(X eddڊzgL#GT֋/u͙vб6o]_Tҥb̌LMݮ[+/XDMn]:ᮉ~M#' z^~ڴlD[qėӾPfV|V<@ʗ//-WѻJ, Ɇ 4ɶ`ڴkT^F! \0r 6rB3i Er9EGѷVXhx/RO1$T1LS=תM{0bmz-[\Κ:}xڱ=|Q~4V>HkK*UրAo'I:s̭B0"11ѼwCHo\./v.3g%Ii*b4H߾{ђ%IK[va `!55UGS^h"woUHZ,G?|;"e(QCnWm,}*+:&JdбV^]{ke2u)\e_u^}}rw`ȃRK)!!|]ǿf̚3h|K:WrqvV:h{#nzs?LVXhPnӶNJR䎝7y{4m? IDATюol]H϶hf lF+A`.3{- EUnnbg9;9i~X;=L&Jz״t2s.33S>L)SeZx~]b}ӛXd&J.sKZ~N+d1:4zXIRx&UVUzvպ!6F{ж!|xE}鯣GIU ^΢3r*hѢ涄?r9|!u)9Yڴt ߊl /nrttm[QoaѶmYUvABժ^[ Z{#x\Jv];\iOtVMFQ=_Ӿ"2e˘III75%UFQʫ,\Zڷ$iJcjpW;2ۭ+VLmrvvh(p` hEڵ3JtI-^ԔTeggkڗӵ!dIߗ,Y¢D egR%v[[9/[\{vU*~j٪f|=*1\v|h%%%EmJOOWvvMF`ahmRzhA"effj=ROgιXJzᇕ$IW+''"w)9Y~M!I98}<#yxxh5VλcPE[`ڿUWO~\(>U?`*WalB4mt=׺>;JMi4Qok2dh zYSz-=ת~ܝ;SzZ*eYd1mآ?/i_~j=5z5~J|jyBA~1) 0 `0lecEkafoyxI4ӱ6Xާo׸cԽg7͞9GjU+VkтE2tkʄn$XʪkQW 5lF4Fֆ `u$)^ ^RmZWmM[3Jޕ*_=ߴ͓n=^Q}ԴQ3]Ib+ ˗$1ʲ+*Voe -_/" mrWfpww7]tVȃ4rm h?lJJJܯF(0pȰ;UK1Qh+ФicEDeo3jIbWzumk222"...Bb/tIg$]TSo؊v{IWkH[Q '''IR9@!u6OX vբ𙕕4Uf|=Sqq⧧7%󕑤1rƍG/(99YZUք+h~3͟._,I_[3fM Y\It2\U>xu{^M~]ZfԲyI931\IRSR0ؑ?|gq1$Ttڤic ?DׅH"6mh4)H2L2L9::/y?*;;ۢFI'+''GƲHo@:w'ɤP]Ed2i4%%%i 5d2t󕳲te5 `G>%$$/^}~E!7X5 jAB(]>=ٰrrr~ӯ:tZ4Z.\ԪV[X17ӯVsht'cGi޻bճjyB_Nʢм3r- Icϟ=RK ‚0ؑC(QBΟjۼd BuE7x+m`$h@Auגt%bbjjw};dkU=jVMFQV~o  )[F'$[̝cQ"6< UT) x)׮+Te#GrT#<ܦ$^իU:tjÇh׮Z*;;[Ӿ !ƞN:ˌNc{Ԭ3ן5~~ܥ|lr%ê왳:|8ޢ+I=_뮰M[[LJJ/ЩSI*[$)33SwQ*~:zWVVYV K5vhUX^qU[u4xz؊8  `']qIժ^V w`'(+{):&JmE ;/iG ֤UѪsFQ1Qzef^Խiݲ{NRJ^cd4o1 `;j̸ڶm:RfΑ{1[C9ɤÇr9妙^/ Ղum@q0ܠǫݴe$sۂrvvŨCNN:b+vHHHT4w|[Q<\N}N^a'I*QCnWm,UVtLժʾ>Щ*vYJ>TFf:tlhZLt;jwma1r,c0 .6N]^|^hHXe֌YӵqCz|*WR}ty-]̜4y׌g*.6N~UTZd՜yIjK4aDUTN:htЪ`Բ931\IRSR%IpmF˗4mӶNJR䎝s6Ԉ4n=rL)@U5v'_qC5yJH.]Һ!\ժ(>.^^GW䩟+2r֮Y')`4ud͚3CɊ߬jhs߀>:t0VxO&Ia^5sAm5iXuWh4r׼Yt.I{ܞ'Ntnɤ'j˗[d&J.sKZ~N+dK?gܪ{Ϯ޻OG$mRjUխgWdWiiiJJJݻ`.4 &ڴ!)tc6jyA߫U떒$GGGծ׬(ܥʾ>*Z$^@]]I, YVDo֥djJTѻ-]a]PւV}9bԤic5km[ߦ5kXO<4Ut)Iy\\I5kf\\\̯`_^,bn27{<`0`0L2:qJ(s3g؊ܑ,\Zڷ?MV_ǎ5hŲб~}E_bŔlі,gggFs1=sL9*Rꟸ^ `CZZ֬ #A)ִ/kCI%]-T.UJEldhsvv5+) ڲ%սgWU⧖Zh׳l c˖,ϋСXyxxXyxx(===+z`nU˪ͷΞ=effj=ROGʒ$)'g'5k\ן{.S|m0#UOJIIMcb*.6NHZjM9??_uI~~:t0V۶h??FY6(z`X 7>c9߿^|fΘmL2]3Fڰ!TTFuU(5vhUX^qU[u4xPIm;`_uN7/TR?bs݊]e4:}]\>yE6php;wN^҅ ,2K,א*,4\^S  Ј4n|YdFfü5)5z6qF] ;`Mf]^^/`oS;hruq'Ť)?7ȜܱSg&(33Sq%s 0X}VS5g\s{o5A2l~Wm c̸ոI#ȏFUEvW3I5udy?jM2]XJw˪ްwhn V7:u2I?ukCn1^voooSBB^ ^RmZWBB8n=^Q}ԴQ3]Ib+ *Hb0:*Voe -_/ `ـA}l}6}9+[qp 0{">F]qIժ^V wVP8PpWxRtL+ڊ6Q;A`NP;A`NP;A`NP;A`El JI/ؓ%5iJ<_\ `}d2hqdee8J`ڟQD{(=c%?JyHR=3zj3hed0H=w3.EjSJ]QVz]TǺGtnF r)Gԭ!GYD=㕒&5NJ%I=ԐQ*W/;!7L Nc_^Rzfe8X+z*朩̟{Z^ŧ%tgBv#Tr{ӿg>RҵrrIR9W>.󒑤]G(xsJ<_LT3EZI99?Je.+%Qo=U-]=NP9M99Օc4?]vgkMbd2gtDItKעz)=6Dǿ 5m^y}'|n=eZ#I*W/e9ys4_JflN$?G:>{+X ¾DOx-'}Z6CO˻9zeyͳg=ێU7ou`M|AJ5ckGӶ+|@NǗ[&EQ撿 3gw}x6ȫ:͑vfϸWV[Ue;tlv^S6lz鎙9cU\@OOˊ̜*8|}$;l2<|le~s嶻g枇f=mO0yk;n)"i9ey><>emݷ,=yv^fѲ2sƪr]; tm}cgϧ=Ͽ<@:?}I [ʗ>(o;yϩ7'2N#ɾ}0ZyE-e[$I~d6 lFO/^pKvN< o[!NoнA{{pG{rl.}Z^pyѿg.{?.#?_SvqyZ7_>{cLbhh\dI:28k0\zQNyY䎑Gc@˾$ 0@Mjկ`2 /9( IDAT6Xg=jc `p00%g K/)/<5Kѯ|B:t.x͓NJGI碌K VIcMZ|+H18nb'Ǧ3YJͦt.~::7?'nϪ4O=-Mo`=U{S.[b|իӘsROG?LG?LlY=;^bGjk~]YHNgzJ3YmUW\$e4_{ョ8:̤;I'IʇLs/N'Ş{7m$\=VW=^qEOiRR͕ڳMa}ut4K/ޙƑGomh Ux0}L7x59_U`үbp01_ٓʹxxolj0I&[/8i$M]=1@mR Jxq:Iŷ J?}֨s٥)fm1Wmi홈bpV3S޲(̑-N^c=Z޽d` 56-̪7+.O󵯛Ԛ*]8zMҜ$VWLy}ivxŷ~BT1Ym[OO.#k~_87Krx8#y#˪sU)y8[x1jJ{k { )Jeȁ_{ip]wKX\fLa,_>b]7oq\}UmI#-6-g0Iơ晙O Si>t.n<'W:|9_b^Rzk]w1wOxNi;CUa_cү*55'˗'l\ri>goE^{o?bŊdLbj*+dժ=W|47.?:j4 JL~mSwi3eץ#}U5ơOiw\|㷩65F{=Wcgҽi~](mf5SQ?/ivx/>%岻ӹ3gqsF#?sV? t4?JJZXsh㙋۔;ƫ_l]%կ5s4=,Iz=vZ4i})#O}2''K2ytݴg)+ OvܱwI*T*ݕ~6NL%l.%y#iի{'Qp>oŴii7JOi_xAN_ Q&W_Q\}};b־~fc<7e^FӾ^$qObp0vx1i1oq͋zP>|ި#Sdt:?t4_~kLf7$8/quuOǏչ4yθXcnYgoex­OO}jdmGmO}M*?9sz:I](Ŝ9y*S@i̙i$I[{z45kTjsbR$i=4?siźg|t"zI^ L^רԯ1Tn8wѻKNM7,:ΝN74c~VL>kto^9d{;c󍯧qaiނJy|뛽x 5`˷bEsE,S>`/LHȣ^wU8w\X::7K땯J1Wjs)}ߪz╓E#BN~[? Ʊ|;abPZo}[:^?4iutZUkr+k)fS$k44; Ok~M"~ϺOZIѽwޙg+|>:T:)i4z!V=y&IyՕ)|0y j*lb]R{e=yZULEbhvʅ Ǯ[Oy}ga{iyV^Uj GӘ4O9_/5wk#MgdOUxQk8ŋƾs_H`iC|siջ7 {GUC252Y*W \=IP~{:W^ơ{n)wܲ(n1`Id59$>=NH݃sk"}jp`:^MG w[L;U뮔ޚb}7߷1Vymh:W^=lsM'`T/MlYez-JGS{Y+t#'v6I*'_,K{ {)b6یpV眓^rtz} k=cixP:矗צ8t2o*R׌); Ş+fg ǽq?4;.kMVLG63Y5Uz{<);t>pZo~˺~1#~3Q˫JM%)_SsO=!ݟ4jv#ƭ4IbE?iОg~gIcRv[7\InO1Ʊǥssy#S֯xEkuiYlcՒ%),Iw$IW]E4iMYO6[Xw-Kצ?y{<22v[8$n۔EHT}5+w\|KZo]^eԬ<ͿNk_Bsٞ1wnssyii<^i_Uŭ84۴fci>4_$I oH_[mƱǥ){d?#_ȟ}꜌WyIb;7Obh4>8/|>)4O~a=wʜg1kdɊ7%žO}U{}x{:嗧|2jN8WYk)?luH_Omofrɒ%6\rE9內fɒ;FOC|yVi87[K_Ly=BQ=VK_Lw;-Y9sq['?eۯJ]8?d㾋ܮ$ð6:[Z{ҽ|)i~OϡeoIOدK]8wt?/t8~$`4O{Y'$)ݶO5OȴP?uז|α}'ɌcA ۷bd~e7AlMmW՗gs=$>uSbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&5! 0@MjB P`Ԅ&5! 0@MjB P~axpΙׯ `Sbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&5! 0@MjB P`Ԅ&5! 0@MjB P~axpΙׯ `Sbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&5! 0@MjB P`Ԅ&5! 0@MjB P~axpΙׯ `Sbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&5! 0@MjB P`Ԅ&5! 0@MAz IDATjB P~axpΙׯ `Sbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&5! 0@MjB P`Ԅ&5! 0@MjB P~axpΙׯ `Sbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&5! 0@MjB P`Ԅ&5! 0@MjB P~axpΙׯ `Sbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&5! 0@MjB P`Ԅ&5! 0@MjB P~axpΙׯ `Sbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&5! 0@MjB P`Ԅ&5! 0@MjB P~axpΙׯ `Sbp`,J 0@MjB P`Ԅ&5! 0@MjB P`Ԅ&5W0gy  00%g fݯ Ԅ&޽]$. t= A&51 Ԏ㘴:ݴLq]i؃ؽiIf8$u߁8XE^,H{B]CxoZ9yfFw\D w4`(B&E=MSH(B(B(B(B(B(B(B(B(B(B(B(B(B(B(i0:Ý7'Ineph vO1jLp'}M3 `"``R[H4``"`"`"`"`"`"`"`"`"`"`"`"`"`"`"3I_o qpVn4M#D w4`(B&E=MSH(B(B(B(B(B(B(B(B(B(B(B(B(B(B(i0:Ý7'Ineph vO1jLp'}M3 `"``R[H4``"`"`"`"`"`"`"`"`"`"`"`"`"`"`"3I_o qpVn4M#D w4`(B&E=MSH(B(B(B(B(B(B(B(B(B(B(B(B(B(B(i0:Ý7'Ineph vO1jLp'}M3 `"``R[H4``"`"`"`"`"`"`"`"`"`"`"`"`"`"`"3I_o qpVn4M#D w4`(B&E=MSH(B(B(B(B(B(B(B(B(B(B(B(B(B(B(i0:Ý7'Ineph vO1jLp'}M3 `"``R[H4``"`"`"`"`"`"`"`"`"`"`"`"`"`"`"3I_o qpVn4M#D w4`(B&E=MSH(B(B(B(B(B(B(B(B(B(B(B(B(B(B(i0:Ý7'Ineph vO1jLp'}M3 `"``R[H4``"`"`"`"`"`"`"`"`"`"`"`"`"`"`"3I_o qpVn4M#D w4`(B&E=MSH(B(B(B(B(B(B(B(B(B(B(B(B(B(B(i0:Ý7'Ineph vO1jLp'}M3 `"``R[H4``"`"`"`"`"`"`"`"`"`"`"`"`"`"`"3I_o qpVn4M#)v{}iOg$N?A_Llsz$?o΅|W$mW-{8Ȝ{r͛$I3{﫛h6kھ~+I_ʖm3'糷hWu_ɂ;$>3/.=S-ޕ;O^}M3>,ږM֗;ZM{Sy#YyڳYp<\9[s >75ޕ/׳!tgn{p4{G,MŏNޔ$ٺczrկr=9K'I_X̞'xu{Lơ򱷯.y(E[3cޜpKO5]!wzs=_fJ̙'g, l63w Mӣwn-~!3^ȧuOhk咾M׽,>~{{|ɏރ;ds竖o8ZZޱ{H> ٱ{Z?V.]KfmK,/.ʃO-jC3sK_+O=yI[әxA>s9Mӣ]I״.%:^ݝiS i k7 <+ue4Ir'reޘ}82*G\?w8Z&#9 Y5/ٜ{" ɵ_i'n9ZpQ/y>_ͯt-IuLK^k:G> s3m=w]&'۞gM.ϟs>ry䮡V75ɪG7u#?}\gS /[f꾒d/AqYt܎lѕ5"k7;/HZeY_;j$[,ҿJIDATg|}4h6IrˇoϒӷEgˊS7do_9'w(?X`Z0>(?+}wohԒ$w=|} w#Ug3-= CW tU+Ȏ]Oo|,[wLυ?=OxW|?;wO]9}ϯ{/^vgգ'fsri㷏l~Wdsry= S٧>fᄅYqʆL}zl.gvo;gϾ?6=^Wg'I3{ƞ $MX}h]P._TfMߛoo]}u6mZ;<̼I#m_RdƹIȅswd=v<>{r}KsϚGL{p4{GIF<PQ;ή<~^.?87[h k7n( }%Irޜ~Gf"͛s n<ĩ_^| }/VK.{4:f߾)Uw /߱*'/ؚ8ad'?t^-ޒӟIgYsϴ8ٹ}$ɒgϾ%gyυCٿJ~ㆋ?fGț,[%={hw$z /ٜ5WFl|lu >tisi_; 塵 s?~'Ryѵ7o]D֟[wz5ͅ׼t_IrϚrϚrgwZ7?Nr‹$wL}Ι'S?;N^5_78v#}^oiXYM|& =3w_onF롧[zOM^uc=P?[r]ٹgZeI[[}C9f,ql1={/ȶ,$ccҽ`['yl.̺MN/~{6o^}lȩ37g=ٽgjFw3[',9/_nA/y>眺UP+h6IUFSnܜ<9I>u˹kȊS7d-+omz4=2w3-ZOo)73sO~]13w{OWNIrd)ٱ+{ K3s7Ol8xg~^th6Lڗo $mLk,:vk~ٻ{< +51do(h@QZc@4v&ib16Dc4I&V{$[h%FKLDT",,ݳcT-g<{=a}~RUg2Tmw sOȷ6#nLwoY{ఌwu<Ϭ)МR밾A;%oT?x״zz򄥙6%Cz6]~=~@k6o45]^1ǶfYO]e)_[y.7@]r;rM]Ot}AJUUw^n+w{-{jgO[3k|o_<ّ5*R5jԨQF5jԨQFMk.;G<`d_xȪR=݋-#r|e}KhؐښR:S]Qu-=#N&uu 넌ޑZceU9{ꎺɞ`Rm|TUu{ۛUR/TZ$ӏXc{k9腹cR)[ߘJUilx6Sn&!iۜݝ{uSWu~e>Y֐эIWۇIfƜqL:5cW䩅?kpGUMMM#?ru;ɋrYw~1!>%$O19=qPd\yפ's 3w=~{o\$Yߚe1+IKg=汭Ҿ)oYӟc\$͏ϭ˾{|R[S]ޙa6/?۬0(3zzfN^/|wGSU8|cj|/η>$w]xg%kN -g?Y>X=^ˇS-U<7Cw ȐA]}k$#nȬgM?3n0m̾krdA+H_#7>%/;?-&In۟eԈuu'dM7tdXtilX+͘u>;Ϛ2Sn:sE=~9MtR5}+}ʍ^֐Ӈ-UUS=1mNe{uG9I.l^)#O#ާǼ|uu}f>ή=\pwKgT>D>4<`d>{ٝ*} &5-[l./ߚWGݧ '3g><5N.~%ͽ'hMǠ>4 _},]wV^ɰJjaCFX۶'}ό#i/i/&r :wx.8mv>Sgw}5aPaw}ܞښdy .+kFzksڒPܥ}aLwwU[Ҙidm f{*]g<ƆyQsԘ֜4|ٹ+Qn;6g; ex>{ꜾSUn$sʐA]i蝻:s$A9gR]ݝUrΉOzsϵksWwto||pK9e}fW:7<uE}wXG$?. VMuSZc\cVk 5X{oJf;￝{cK= G_/5ե ߔ!ۭWFaHg&YCGo?Mw~1!K^mȦ괮ϽnYjC:!IA?ϯ?餼ʻ&O.*Fe}߻fA˳qSunyl\wk~yx3g)~uHmؽ!hm̒mßg`Ǯ@jSn[͋RNG&IGT#CD%N/jR*U{ztvUaJ:L|]mW&YcVf޶赽mG ݐcǷ:fܖ5ߝ{u^\xqYbx6uVgኽe*wu<8IO4L5`%T-v{ hv'xow^ JU2?Լ.kj$Ǧ|*39̽ش`OǷ$3exSʕ\0󩴵IwwU_+f)sO(k>s_&n+Wo 0{>8u]Ie5}I}]W^YW=& G_$Թ^Iw(-{eS=:Iw%@A B P` !(0@A B P` !(0@A&ISSS:Lmmm2($?yJRr !(0@A B P`-WJr%lGu3Yz`*?)g| !(0@A rE(0@A B P` !(0@A B P` !(0@A B P` !(0@A B P` !(0@A  N`x+HIENDB`neomutt-neomutt-20171215/contrib/keybase/pgpdecrypt.sh000077500000000000000000000000641321473123000227410ustar00rootroot00000000000000#!/bin/sh sed -n '/BEGIN/,$p' | keybase pgp decrypt neomutt-neomutt-20171215/contrib/keybase/pgpverify.sh000077500000000000000000000000641321473123000225730ustar00rootroot00000000000000#!/bin/sh gawk '/BEGIN/{y=1}y' | keybase pgp verify neomutt-neomutt-20171215/contrib/keybase/verify.sh000077500000000000000000000001071321473123000220620ustar00rootroot00000000000000#!/bin/sh sed -n 's/^.*BEGIN KEYBASE/BEGIN KEYBASE/p' | keybase verify neomutt-neomutt-20171215/contrib/language.txt000066400000000000000000004412051321473123000211300ustar00rootroot00000000000000# Translation file for PGP 2.6.3(i)n. # ------------------------------------------------------------------ # Character set: ISO-Latin/1 (ISO 8859/1) # Date revised: 7 October 1997 # ------------------------------------------------------------------ # Language: German/Deutsch (de) # Translator: Frank Pruefer # (based on the German translation for PGP 2.3a by # Marc Aurel <4-tea-2@bong.saar.de>) # ------------------------------------------------------------------ # Language: Spanish/Espaol (es) # Translator: Armando Ramos # ------------------------------------------------------------------ # Language: French/Francais (fr) # Translator: Yanik Crpeau # (based on the French translation for PGP 2.3a by # Jean-loup Gailly ) # ------------------------------------------------------------------ # # Additional language files may be obtained from: # # http://www.ifi.uio.no/pgp/modules.shtml # ftp://ftp.ifi.uio.no/pub/pgp/lang/ # # ------------------------------------------------------------------ "\nClear signature file: %s\n" de: "\nDateiname der Klartext-Unterschrift: %s\n" es: "\nFichero normal con firma: %s\n" fr: "\nFichier de signature en clair: %s\n" muttde: "\nDateiname der Klartext-Unterschrift: %s\n" "\nTransport armor file: %s\n" de: "\nDateiname der Versandhlle: %s\n" es: "\nFichero con armadura: %s\n" fr: "\nFichier de transport armure: %s\n" muttde: "\nDateiname der Versandhlle: %s\n" "\nTransport armor files: " de: "\nDateinamen der Versandhllen: " es: "\nFicheros con armadura: " fr: "\nFichiers de transport armure: " muttde: "\nDateinamen der Versandhllen: " "Invalid ASCII armor header line: \"%.40s\"\n\ ASCII armor corrupted.\n" de: "\nUnzulssige Kopfzeile \"%.40s\"\n\ in der ASCII-Versandhlle. Die Versandhlle ist deshalb ungltig.\n" es: "Lnea incorrecta en la cabecera de la armadura ASCII:\n\ \"%.40s\"\n\ Armadura daada\n" fr: "Entte enveloppe ASCII invalide: \"%.40s\"\n\ l'enveloppe ASCII est corrompue" muttde: "\nUnzulssige Kopfzeile \"%.40s\"\n\ in der ASCII-Versandhlle. Die Versandhlle ist deshalb ungltig.\n" "Warning: Unrecognized ASCII armor header label \"%.*s:\" ignored.\n" de: "\nWARNUNG: Der unbekannte Bezeichner \"%.*s:\"\n\ in der ASCII-Versandhlle wurde berlesen.\n" es: "Advertencia: Se ignora la etiqueta \"%.*s:\"\n\ No se reconoce como cabecera de armadura ASCII\n" fr: "Avertissement: Type d'entte de l'envloppe ASCII \"%.*s:\" ignor\n" muttde: "\nWARNUNG: Der unbekannte Bezeichner \"%.*s:\"\n\ in der ASCII-Versandhlle wurde berlesen.\n" "ERROR: Bad ASCII armor checksum in section %d.\n" de: "\nFEHLER: Falsche Prfsumme im Abschnitt %d der Versandhlle.\n" es: "ERROR: Suma incorrecta de comprobacin en armadura ASCII,\n\ seccin %d.\n" fr: "ERREUR: mauvaise vrification de l'armure ASCII dans la section %d.\n" muttde: "\nFEHLER: Falsche Prfsumme im Abschnitt %d der Versandhlle.\n" "Can't find section %d.\n" de: "\nAbschnitt %d nicht gefunden.\n" es: "No se encuentra la seccin %d.\n" fr: "Section %d introuvable.\n" muttde: "\nAbschnitt %d nicht gefunden.\n" "Badly formed section delimiter, part %d.\n" de: "\nFehlerhafter Abschnitts-Begrenzer im Teil %d.\n" es: "Delimitador de seccin mal formado, parte %d.\n" fr: "Sparateurs de section mal forms" muttde: "\nFehlerhafter Abschnitts-Begrenzer im Teil %d.\n" "Sections out of order, expected part %d" de: "\nAbschnitte in falscher Reihenfolge.\nErwartet wurde Teil %d" es: "Las secciones estn desordenadas: se esperaba la parte %d" fr: "Sections en dsordre, partie %d attendue" muttde: "\nAbschnitte in falscher Reihenfolge.\nErwartet wurde Teil %d" ", got part %d\n" de: ", angekommen ist Teil %d.\n" es: ", se encuentra la parte %d\n" fr: ", partie %d obtenue\n" muttde: ", angekommen ist Teil %d.\n" "ERROR: Hit EOF in header of section %d.\n" de: "\nFEHLER: EOF (Dateiende) im Header von Abschnitt %d.\n" es: "ERROR: Hay un EOF (fin de fichero) en la cabecera de\ la seccin %d.\n" fr: "ERREUR: find de fichier dans l'en-tte de la section %d.\n" muttde: "\nFEHLER: EOF (Dateiende) im Header von Abschnitt %d.\n" "ERROR: Badly formed ASCII armor checksum, line %d.\n" de: "\nFEHLER: Falsche Prfsumme in Zeile %d der Versandhlle.\n" es: "ERROR: Suma de comprobacin mal construida en la armadura ASCII,\n\ lnea %d.\n" fr: "ERREUR: Verification de l'armure ASCII mal forme, ligne %d.\n" muttde: "\nFEHLER: Falsche Prfsumme in Zeile %d der Versandhlle.\n" "WARNING: No ASCII armor `END' line.\n" de: "\nWARNUNG: Keine 'END'-Zeile in der Versandhlle.\n" es: "ADVERTENCIA: No hay lnea `END' en la armadura ASCII.\n" fr: "ATTENTION: Pas de ligne `END' dans l'armure ASCII.\n" muttde: "\nWARNUNG: Keine 'END'-Zeile in der Versandhlle.\n" "ERROR: Bad ASCII armor character, line %d.\n" de: "\nFEHLER: Unerlaubtes Zeichen in Zeile %d der Versandhlle.\n" es: "ERROR: Carcter incorrecto en la armadura ASCII, lnea %d.\n" fr: "ERREUR: Mauvais charactre dans l'armure ASCII, ligne %d.\n" muttde: "\nFEHLER: Unerlaubtes Zeichen in Zeile %d der Versandhlle.\n" "ERROR: Bad ASCII armor line length %d on line %d.\n" de: "\nFEHLER: Falsche Zeilenlnge (%d) in Zeile %d der Versandhlle.\n" es: "ERROR: Longitud incorrecta (%d) de lnea en la armadura ASCII,\n\ lnea %d.\n" fr: "ERREUR: Mauvais longueur de ligne %d dans l'armure ASCII, ligne %d.\n" muttde: "\nFEHLER: Falsche Zeilenlnge (%d) in Zeile %d der Versandhlle.\n" "ERROR: Bad ASCII armor checksum" de: "\nFEHLER: Falsche Prfsumme der Versandhlle" es: "ERROR: Suma incorrecta de comprobacin en la armadura ASCII" fr: "ERREUR de vrification dans l'armure ASCII" muttde: "\nFEHLER: Falsche Prfsumme der Versandhlle" " in section %d" de: " im Abschnitt %d.\n" es: " en la seccin %d" fr: " dans la section %d" muttde: " im Abschnitt %d.\n" "Warning: Transport armor lacks a checksum.\n" de: "\nWARNUNG: Die Prfsumme der Versandhlle fehlt.\n" es: "Advertencia: La armadura de transporte no lleva suma de\ comprobacin.\n" fr: "Attention: l'armure de transport n'a pas de vrification.\n" muttde: "\nWARNUNG: Die Prfsumme der Versandhlle fehlt.\n" "ERROR: Can't find file %s\n" de: "\nFEHLER: Datei '%s' nicht gefunden.\n" es: "ERROR: No se encuentra el fichero %s\n" fr: "ERREUR: Fichier %s introuvable\n" muttde: "\nFEHLER: Datei '%s' nicht gefunden.\n" "ERROR: No ASCII armor `BEGIN' line!\n" de: "\nFEHLER: Keine 'BEGIN'-Zeile in der Versandhlle!\n" es: "ERROR: No hay lnea 'BEGIN' en la armadura ASCII\n" fr: "ERREUR: Pas de ligne `BEGIN' dans l'armure ASCII!\n" muttde: "\nFEHLER: Keine 'BEGIN'-Zeile in der Versandhlle!\n" "ERROR: ASCII armor decode input ended unexpectedly!\n" de: "\nFEHLER: Vorzeitiges Ende der Versandhlle!\n" es: "ERROR: La entrada con armadura ASCII termina antes de tiempo\n" fr: "ERREUR: fin prmature du fichier armure ASCII!\n" muttde: "\nFEHLER: Vorzeitiges Ende der Versandhlle!\n" "ERROR: Header line added to ASCII armor: \"%s\"\n\ ASCII armor corrupted.\n" de: "\nFEHLER: Eine Kopfzeile \"%s\" ist\n\ in der ASCII-Versandhlle enthalten. Die Versandhlle ist deshalb ungltig.\n" es: "ERROR: Lnea de cabecera aadida a la armadura ASCII:\n\ \"%s\" Armadura daada\n" fr: "Ligne d'entte ajoute l'enveloppe ASCII: \"%s\"\n\ enveloppe ASCII corrompue" muttde: "\nFEHLER: Eine Kopfzeile \"%s\" ist\n\ in der ASCII-Versandhlle enthalten. Die Versandhlle ist deshalb ungltig.\n" "\n\007Unable to write ciphertext output file '%s'.\n" de: "\n\007FEHLER beim Schreiben der verschlsselten\nAusgabedatei '%s'.\n" es: "\n\007No puede escribirse el fichero de salida cifrado '%s'.\n" fr: "\n\007Ecriture impossible dans le fichier de sortie chiffr '%s'.\n" mutt: "\nUnable to write ciphertext output file '%s'.\n" muttde: "\nFEHLER beim Schreiben der verschlsselten\nAusgabedatei '%s'.\n" "ERROR: Hit EOF in header.\n" de: "\nFEHLER: EOF (Dateiende) im Header.\n" es: "ERROR: Hay un EOF (fin de fichero) en la cabecera.\n" fr: "ERREUR: fin de fichier dans l'en-tte.\n" muttde: "\nFEHLER: EOF (Dateiende) im Header.\n" "Unsupported character set: '%s'\n" de: "\nKeine Untersttzung fr Zeichensatz '%s'.\n" es: "Conjunto de caracteres no admitido: '%s'\n" fr: "Table de caractres non supporte: '%s'\n" muttde: "\nKeine Untersttzung fr Zeichensatz '%s'.\n" "The legal_kludge cannot be disabled in US version.\n" de: "LEGAL_KLUDGE kann in der USA-Version nicht abgeschaltet werden!\n" es: "'legal_kludge' no puede desactivarse en la versin para los EE.UU.\n" fr: "Les embarras lgaux ne peuvent pas tre dsactivs aux Etats-Unis.\n" muttde: "LEGAL_KLUDGE kann in der USA-Version nicht abgeschaltet werden!\n" "The multiple_recipients flag is unnecessary in this \ version of MacPGP.\ \nPlease remove this entry from your configuration file.\n" de: "Die Kennung \"multiple_recipients\" ist in dieser Version von MacPGP nicht\ \nntig. Bitte entferne diesen Eintrag aus Deiner Konfigurationsdatei.\n" es: "No se necesita la bandera 'multiple_recipients' en esta versin\n\ de MacPGP.\ \nElimina esa entrada del fichero de configuracin.\n" fr: "L'indicateur de destinataires multiples n'est pas ncessaire dans \ version de MacPGP. \ \nS.V.P. supprimez cette entre de votre fichier de configuration. \n" muttde: "Die Kennung \"multiple_recipients\" ist in dieser Version von MacPGP nicht\ \nntig. Bitte entferne diesen Eintrag aus Deiner Konfigurationsdatei.\n" "\007\nWARNING: This key has been revoked by its owner,\n\ possibly because the secret key was compromised.\n" de: "\007\nWARNUNG: Dieser Schlssel wurde von seinem Besitzer zurckgezogen,\n\ mglicherweise, weil sein privater Schlssel nicht mehr sicher ist.\n" es: "\007\nADVERTENCIA: Esta clave ha sido revocada por su propietario;\n\ es posible que la clave secreta se haya visto comprometida.\n" fr: "\007\nATTENTION: cette cl a t rvoque par son propritaire,\n\ probablement parce que la cl secrte a t compromise.\n" mutt: "\nWARNING: This key has been revoked by its owner,\n\ possibly because the secret key was compromised.\n" muttde: "\nWARNUNG: Dieser Schlssel wurde von seinem Besitzer zurckgezogen,\n\ mglicherweise, weil sein privater Schlssel nicht mehr sicher ist.\n" "This could mean that this signature is a forgery.\n" de: "Dies knnte bedeuten, da diese Unterschrift eine Flschung ist.\n" es: "Puede significar que la firma est falsificada.\n" fr: "Ceci peut signifier que cette signature est un faux.\n" muttde: "Dies knnte bedeuten, da diese Unterschrift eine Flschung ist.\n" "You cannot use this revoked key.\n" de: "Du kannst diesen Schlssel nicht benutzen, weil er zurckgezogen wurde.\n" es: "No puedes utilizar esta clave revocada.\n" fr: "Vous ne pouvez pas utiliser cette cl rvoque.\n" muttde: "Du kannst diesen Schlssel nicht benutzen, weil er zurckgezogen wurde.\n" "\007\nWARNING: Because this public key is not certified with \ a trusted\nsignature, it is not known with high confidence that this \ public key\nactually belongs to: \"%s\".\n" de: "\007\nWARNUNG: Da dieser ffentliche Schlssel nicht mit einer vertrauenswrdigen\n\ Unterschrift beglaubigt ist, ist nicht sicher, da er wirklich zu\n\"%s\" gehrt.\n" es: "\nAVISO: Esta clave pblica no est certificada con una firma\ de confianza,\n\ por lo que no se sabe con seguridad si realmente pertenece a:\n\ \"%s\".\n" fr: "\007\nATTENTION: Cette cl publique n'est pas certifie avec une\n\ signature fiable. Il n'est donc pas reconnu avec un haut degr de confiance\n\ que cette signature appartient effectivement : \"%s\".\n" mutt: "\nWARNING: Because this public key is not certified with \ a trusted\nsignature, it is not known with high confidence that this \ public key\nactually belongs to: \"%s\".\n" muttde: "\nWARNUNG: Da dieser ffentliche Schlssel nicht mit einer vertrauenswrdigen\n\ Unterschrift beglaubigt ist, ist nicht sicher, da er wirklich zu\n\"%s\" gehrt.\n" "\007\nWARNING: This public key is not trusted to actually belong \ to:\n\"%s\".\n" de: "\007\nWARNUNG: Es ist nicht sicher, da dieser ffentliche Schlssel wirklich\n\ zu \"%s\" gehrt.\n" es: "\nADVERTENCIA: No se sabe con seguridad si esta clave pblica\n\ pertenece realmente a: \"%s\".\n" fr: "\007\nATTENTION: Cette cl publique n'est pas reconnue comme\n\ appartenant : \"%s\".\n" mutt: "\nWARNING: This public key is not trusted to actually belong \ to:\n\"%s\".\n" muttde: "\nWARNUNG: Es ist nicht sicher, da dieser ffentliche Schlssel wirklich\n\ zu \"%s\" gehrt.\n" "\007\nWARNING: Because this public key is not certified with enough \ trusted\nsignatures, it is not known with high confidence that this \ public key\nactually belongs to: \"%s\".\n" de: "\007\nWARNUNG: Da dieser ffentliche Schlssel nicht mit einer ausreichenden\n\ Anzahl vertrauenswrdiger Unterschriften beglaubigt ist, ist nicht sicher,\n\ da er wirklich zu \"%s\" gehrt.\n" es: "\nADVERTENCIA: Como esta clave no est certificada con suficientes\n\ firmas fiables, no se sabe con seguridad si realmente pertenece a:\n\ \"%s\".\n" fr: "\007\nATTENTION: puisque cette cl publique n'est pas certifie avec\n\ suffisament de signatures, il n'est pas connu avec un haut niveau de confiance\ \nque cette cl appartient effectivement : \"%s\".\n" mutt: "\nWARNING: Because this public key is not certified with enough \ trusted\nsignatures, it is not known with high confidence that this \ public key\nactually belongs to: \"%s\".\n" muttde: "\nWARNUNG: Da dieser ffentliche Schlssel nicht mit einer ausreichenden\n\ Anzahl vertrauenswrdiger Unterschriften beglaubigt ist, ist nicht sicher,\n\ da er wirklich zu \"%s\" gehrt.\n" "But you previously approved using this public key anyway.\n" de: "Aber Du hast diesen Schlssel trotzdem bereits benutzt...\n" es: "Ya has permitido antes que se utilice esta clave p\372blica.\n" fr: "Mais vous avez dj accept l'usage de cette cl publique.\n" muttde: "Aber Du hast diesen Schlssel trotzdem bereits benutzt...\n" "\nAre you sure you want to use this public key (y/N)? " de: "\nBist Du sicher, da Du diesen Schlssel benutzen willst? (j/N) " es: "\nEsts seguro de querer utilizar esta clave pblica (s/N)? " fr: "\nEtes vous sr(e) de vouloir utiliser cette cl publique (o/N)? " muttde: "\nBist Du sicher, da Du diesen Schlssel benutzen willst? (j/N) " "\n\007Unsupported packet format - you need a newer version of PGP \ for this file.\n" de: "\n\007WARNUNG: nicht untersttztes Datenformat!\n\ Du brauchst eine neuere PGP-Version fr diese Datei.\n" es: "\n\007Formato desconocido -\ se necesita una versin ms reciente de PGP" fr: "\n\007Format non-support - vous devez utiliser un version nouvelle de PGP pour ce fichier.\n" mutt: "\nUnsupported packet format - you need a newer version of PGP \ for this file.\n" muttde: "\nWARNUNG: nicht untersttztes Datenformat!\n\ Du brauchst eine neuere PGP-Version fr diese Datei.\n" "Preparing random session key..." de: "\nVorbereitung des zuflligen IDEA-Schlssels..." es: "Preparando la clave aleatoria de la sesin..." fr: "Prparation de la cl alatoire..." muttde: "\nVorbereitung des zuflligen IDEA-Schlssels..." "\n\007Error: System clock/calendar is set wrong.\n" de: "\n\007FEHLER: Die System-Zeit und/oder das System-Datum sind falsch.\n" es: "\n\007Error: El reloj/calendario del sistema est equivocado.\n" fr: "\n\007Erreur: L'horloge du systme est incorrecte.\n" mutt: "\nError: System clock/calendar is set wrong.\n" muttde: "\nFEHLER: Die System-Zeit und/oder das System-Datum sind falsch.\n" "Just a moment..." de: "\nEinen Augenblick, bitte..." es: "Un momento..." fr: "Un moment..." muttde: "\nEinen Augenblick, bitte..." "\n\007Can't open input plaintext file '%s'\n" de: "\n\007FEHLER beim ffnen der Eingabedatei '%s'.\n" es: "\n\007No puede abrirse el fichero normal de entrada '%s'\n" fr: "\n\007Ouverture du fichier en clair '%s' impossible.\n" mutt: "\nCan't open input plaintext file '%s'\n" muttde: "\nFEHLER beim ffnen der Eingabedatei '%s'.\n" "\n\007Can't open plaintext file '%s'\n" de: "\n\007FEHLER beim ffnen der Klartextdatei '%s'.\n" es: "\n\007No puede abrirse el fichero normal '%s'\n" fr: "\n\007Ouverture du fichier en clair '%s' impossible\n" mutt: "\nCan't open plaintext file '%s'\n" muttde: "\nFEHLER beim ffnen der Klartextdatei '%s'.\n" "\n\007Can't create signature file '%s'\n" de: "\n\007FEHLER beim Erzeugen der Unterschriftsdatei '%s'.\n" es: "\n\007No puede crearse el fichero de firma '%s'\n" fr: "\n\007Cration du fichier de signature '%s' impossible\n" mutt: "\nCan't create signature file '%s'\n" muttde: "\nFEHLER beim Erzeugen der Unterschriftsdatei '%s'.\n" "\n\007Can't open key ring file '%s'\n" de: "\n\007FEHLER beim ffnen des Schlsselbunds '%s'.\n" es: "\n\007No puede abrirse el anillo de claves '%s'\n" fr: "\n\007Ouverture du fichier de cl '%s' impossible\n" mutt: "\nCan't open key ring file '%s'\n" muttde: "\nFEHLER beim ffnen des Schlsselbunds '%s'.\n" "This key has already been revoked.\n" de: "Dieser Schlssel wurde bereits zurckgezogen.\n" es: "Esta clave ya se haba revocado.\n" fr: "Cette cl a dj t rvoque.\n" muttde: "Dieser Schlssel wurde bereits zurckgezogen.\n" "\n\007Can't create output file to update key ring.\n" de: "\n\007Dateifehler bei der Aktualisierung des Schlsselbunds.\n" es: "\n\007No puede crearse el fichero de salida para actualizar\ el anillo.\n" fr: "\n\007Impossible de crer le fichier de sortie pour modifier le\ \nfichier de cls\n" mutt: "\nCan't create output file to update key ring.\n" muttde: "\nDateifehler bei der Aktualisierung des Schlsselbunds.\n" "\nKey compromise certificate created.\n" de: "\nDie Urkunde zum Zurckziehen des Schlssels wurde erzeugt.\n" es: "\nCreado el certificado de compromiso de clave.\n" fr: "\nCertificat de compromission de cl cr.\n" muttde: "\nDie Urkunde zum Zurckziehen des Schlssels wurde erzeugt.\n" "\n\007Key is already signed by user '%s'.\n" de: "\n\007Der Schlssel wurde von \"%s\"\nbereits unterschrieben.\n" es: "\n\007La clave ya ha sido firmada por '%s'.\n" fr: "\n\007La cl est dj signe par l'utilisateur '%s'.\n" mutt: "\nKey is already signed by user '%s'.\n" muttde: "\nDer Schlssel wurde von \"%s\"\nbereits unterschrieben.\n" "\n\nREAD CAREFULLY: Based on your own direct first-hand knowledge, \ are\nyou absolutely certain that you are prepared to solemnly certify \ that\nthe above public key actually belongs to the user specified by \ the\nabove user ID (y/N)? " de: "\nSORGFLTIG LESEN: Bist Du, gesttzt auf eigenes, direktes Wissen aus\n\ erster Hand, absolut sicher, da Du zuverlssig beglaubigen kannst, da der\n\ oben angezeigte ffentliche Schlssel wirklich zu der oben genannten Person\n\ gehrt? (j/N) " es: "\n\nLEE ATENTAMENTE: Segn tu conocimiento directo,\n\ ests absolutamente seguro de poder certificar solemnemente que la\n\ clave pblica pertenece realmente al usuario especificado por\n\ este identificador (s/N)? " fr: "\n\nLIRE ATTENTIVEMENT: Selon votre propre connaissance directe,\n\ tes vous absoluement certain(e) d'tre prt(e) certifier\n\ solennellement que la cl publique ci-dessus appartient effectivement \n\ la personne spcifie par le nom d'utilisateur ci-dessus (o/N)? " muttde: "\nSORGFLTIG LESEN: Bist Du, gesttzt auf eigenes, direktes Wissen aus\n\ erster Hand, absolut sicher, da Du zuverlssig beglaubigen kannst, da der\n\ oben angezeigte ffentliche Schlssel wirklich zu der oben genannten Person\n\ gehrt? (j/N) " "\nKey signature certificate added.\n" de: "\n\nDer Schlssel wurde mit Deiner Unterschrift beglaubigt.\n" es: "\nSe ha aadido el certificado de firma.\n" fr: "\nCertificat de signature de cl ajout.\n" muttde: "\n\nDer Schlssel wurde mit Deiner Unterschrift beglaubigt.\n" "\nError: Key for signing userid '%s'\n\ does not appear in public keyring '%s'.\n\ Thus, a signature made with this key cannot be checked on this keyring.\n" de: "\nFEHLER: Der Schlssel fr eine Unterschrift unter die Benutzer-ID\n\ \"%s\" ist nicht im ffentlichen\n\ Schlsselbund '%s' enthalten. Deshalb ist eine mit diesem\n\ Schlssel erzeugte Unterschrift mit diesem Schlsselbund nicht berprfbar.\n" es: "\nError: La clave del firmante '%s'\n\ no se encuentra en el anillo '%s'.\n\ No puede comprobarse la firma realizada con esa clave.\n" fr: "\nErreur: La clef du signataire id '%s'\n\ est absente du fichier de clefs publiques '%s'\n\ en consequence, une signature faite avec cette clef ne peut etre verifiee.\n" muttde: "\nFEHLER: Der Schlssel fr eine Unterschrift unter die Benutzer-ID\n\ \"%s\" ist nicht im ffentlichen\n\ Schlsselbund '%s' enthalten. Deshalb ist eine mit diesem\n\ Schlssel erzeugte Unterschrift mit diesem Schlsselbund nicht berprfbar.\n" "\nLooking for key for user '%s':\n" de: "\nSuche den Schlssel fr \"%s\":\n" es: "\nBuscando la clave del usuario '%s':\n" fr: "\nRecherche de la cl pour l'utilisateur '%s':\n" muttde: "\nSuche den Schlssel fr \"%s\":\n" "\n\007Can't open ciphertext file '%s'\n" de: "\n\007FEHLER beim ffnen der verschlsselten Datei '%s'.\n" es: "\n\007No puede abrirse el fichero cifrado '%s'\n" fr: "\n\007Ouverture du fichier chiffr '%s' impossible\n" mutt: "\nCan't open ciphertext file '%s'\n" muttde: "\nFEHLER beim ffnen der verschlsselten Datei '%s'.\n" "\nFile '%s' has signature, but with no text." de: "\nDie Datei '%s' enthlt eine Unterschrift, aber keinen Text." es: "\nEl fichero '%s' tiene firma, pero no texto." fr: "\nLe fichier '%s' une signature, mais pas de texte." mutt: " " muttde: " " "\nText is assumed to be in file '%s'.\n" de: "\nDer Text knnte sich in der Datei '%s' befinden.\n" es: "\nSe asume que el texto se encuentra en el fichero '%s'.\n" fr: "\nLe texte est suppos tre dans le fichier '%s'.\n" mutt: " " muttde: " " "\nPlease enter filename of material that signature applies to: " de: "\nName der Datei, zu der die Unterschrift gehrt: " es: "\nIntroduzca el nombre del fichero al que se aplica la firma: " fr: "SVP indiques le nom du fichier que vous voulez signer: " muttde: "\nName der Datei, zu der die Unterschrift gehrt: " "File signature applies to?" de: "Die Unterschrift gehrt zu welcher Datei?" es: "Dnde se aplica la firma?" fr: "Ce fichier signataire s'applique quoi? " muttde: "Die Unterschrift gehrt zu welcher Datei?" "\n\007Can't open file '%s'\n" de: "\n\007FEHLER beim ffnen der Datei '%s'.\n" es: "\n\007No puede abrirse el fichero '%s'\n" fr: "\n\007Ouverture du fichier '%s' impossible\n" mutt: "\nCan't open file '%s'\n" muttde: "\nFEHLER beim ffnen der Datei '%s'.\n" "File type: '%c'\n" de: "\nDateityp: '%c'\n" es: "\nTipo de fichero: '%c'\n" fr: "Type de fichier: '%c'\n" muttde: "\nDateityp: '%c'\n" "Original plaintext file name was: '%s'\n" de: "Der ursprngliche Name der Klartextdatei war: '%s'.\n" es: "El nombre del fichero original era: '%s'\n" fr: "Le nom originel du fichier en clair tait: '%s'\n" muttde: "Der ursprngliche Name der Klartextdatei war: '%s'.\n" "\nWARNING: Can't find the right public key-- can't check signature \ integrity.\n" de: "\nWARNUNG: Der passende ffentliche Schlssel wurde nicht gefunden.\n\ Eine berprfung der Unterschrift ist nicht mglich.\n" es: "\nAVISO: No se encuentra la clave pblica necesaria para comprobar\n\ la integridad de la firma.\n" fr: "\nATTENTION: impossible de trouver la cl publique adquate et de\n\ vrifier l'integrit de la signature.\n" muttde: "\nWARNUNG: Der passende ffentliche Schlssel wurde nicht gefunden.\n\ Eine berprfung der Unterschrift ist nicht mglich.\n" "\007\nUnrecognized message digest algorithm.\n\ This may require a newer version of PGP.\n\ Can't check signature integrity.\n" de: "\007\nDer Algorithmus fr die Textprfsumme ist unbekannt.\n\ Du brauchst wahrscheinlich eine neuere Version von PGP.\n\ Eine berprfung der Unterschrift ist nicht mglich.\n" es: "\007\nAlgoritmo desconocido de resumen de mensaje.\n\ Puede necesitarse una nueva versin de PGP.\n\ No puede comprobarse la integridad de la firma.\n" fr: "\007Algorithme de digest inconnu.\n\ Ceci peut vouloir dire que vous avez besoin d'une version plus rcente\n\ de PGP. Incapable de vrifier la signature.\n" mutt: "\nUnrecognized message digest algorithm.\n\ This may require a newer version of PGP.\n\ Can't check signature integrity.\n" muttde: "\nDer Algorithmus fr die Textprfsumme ist unbekannt.\n\ Du brauchst wahrscheinlich eine neuere Version von PGP.\n\ Eine berprfung der Unterschrift ist nicht mglich.\n" "\a\nMalformed or obsolete signature. Can't check signature \ integrity.\n" de: "\a\nFehlerhafte oder veraltete Unterschrift! berprfung nicht mglich.\n" es: "\a\nFirma incorrecta u obsoleta.\n\ No puede comprobarse su integridad.\n" fr: "\a\nSignature dforme ou obsolte. Vrification impossible. \n" muttde: "\a\nFehlerhafte oder veraltete Unterschrift! berprfung nicht mglich.\n" "\a\nSigning key is too large. Can't check signature integrity.\n" de: "\a\nDer unterschreibende Schlssel ist zu lang! Eine berprfung der\n\ Unterschrift ist deshalb nicht mglich.\n" es: "\a\nLa clave para firmar es demasiado grande.\n\ No puede comprobarse la integridad de la firma." fr: "La clef signataire est trop grande. Incapable d'en vrifier l'intgrit. \n" muttde: "\a\nDer unterschreibende Schlssel ist zu lang! Eine berprfung der\n\ Unterschrift ist deshalb nicht mglich.\n" "\n\007Error: RSA-decrypted block is corrupted.\n\ This may be caused either by corrupted data or by using the wrong RSA key.\n\ " de: "\n\007FEHLER: Die mit RSA entschlsselten Daten sind fehlerhaft.\n\ Ursache: beschdigte Daten oder ein falscher RSA-Schlssel.\n" es: "\n\007Error: El bloque desencriptado RSA est daado.\n\ Puede deberse a un problema en los datos o a una clave RSA equivocada.\n" fr: "\n\007Erreur: le block dechiffr par RSA est endommag.\n\ Ceci est peut tre caus par des donnes endommages our par\n\ l'utilisation d'une mauvaise cl RSA.\n" mutt: "\nError: RSA-decrypted block is corrupted.\n\ This may be caused either by corrupted data or by using the wrong RSA key.\n\ " muttde: "\nFEHLER: Die mit RSA entschlsselten Daten sind fehlerhaft.\n\ Ursache: beschdigte Daten oder ein falscher RSA-Schlssel.\n" "WARNING: Bad signature, doesn't match file contents!" de: "WARNUNG: Die Unterschrift stimmt nicht mit dem Datei-Inhalt berein!" es: "ADVERTENCIA: Firma incorrecta, no coincide con el contenido\ del fichero\n" fr: "ATTENTION: Mauvaise signature, ne correspond pas au contenu!" muttde: "WARNUNG: Die Unterschrift stimmt nicht mit dem Datei-Inhalt berein!" "\nBad signature from user \"%s\".\n" de: "\nFEHLERHAFTE Unterschrift von \"%s\",\n" es: "\nFirma incorrecta de \"%s\".\n" fr: "\nMauvaise signature de l'utilisateur \"%s\".\n" muttde: "\nFEHLERHAFTE Unterschrift von \"%s\",\n" "Signature made %s using %d-bit key, key ID %s\n" de: "Unterschrift erzeugt am %s mit %d-Bit-Schlssel 0x%s.\n" es: "Firma realizada el %s con una clave de %d bits, identificador %s\n" fr: "Signature faite %s en utilisant un clef de %d bits. Id de la clef:%s\n" muttde: "Unterschrift erzeugt am %s mit %d-Bit-Schlssel 0x%s.\n" "\nPress ENTER to continue..." de: "\nWeiter mit Return..." es: "\nPulse 'Enter' para continuar..." fr: "\nAppuyez sur la touche Retour ou Entre pour continuer..." muttde: "\nWeiter mit Return..." "\nGood signature from user \"%s\".\n" de: "\nBESTTIGTE Unterschrift von \"%s\",\n" es: "\nFirma correcta de \"%s\".\n" fr: "\nBonne signature de l'utilisateur \"%s\".\n" muttde: "\nBESTTIGTE Unterschrift von \"%s\",\n" "\nSignature and text are separate. No output file produced. " de: "\nUnterschrift und Text sind getrennt. Es wurde keine Ausgabedatei erzeugt." es: "\nLa firma y el texto estn separados.\n\ No se produce fichero de salida. " fr: "\nLa signature et le texte sont spars. Fichier de sortie non produit." muttde: "\nUnterschrift und Text sind getrennt. Es wurde keine Ausgabedatei erzeugt." "\n\007Can't create plaintext file '%s'\n" de: "\n\007FEHLER beim Erzeugen der Klartextdatei '%s'.\n" es: "\n\007No puede crearse el fichero normal '%s'\n" fr: "\n\007Creation du fichier en clair '%s' impossible.\n" mutt: "\nCan't create plaintext file '%s'\n" muttde: "\nFEHLER beim Erzeugen der Klartextdatei '%s'.\n" "\n\007Signature file '%s' already exists. Overwrite (y/N)? " de: "\n\007Die Unterschriftsdatei '%s'\nexistiert bereits. berschreiben? (j/N) " es: "\n\007El fichero de firma '%s' ya existe.\n\ Se sobreescribe (s/N)? " fr: "\n\007Le fichier de signature '%s' existe dj. A craser (o/N)? " mutt: "\nSignature file '%s' already exists. Overwrite (y/N)? " muttde: "\nDie Unterschriftsdatei '%s'\nexistiert bereits. berschreiben? (j/N) " "\nWriting signature certificate to '%s'\n" de: "\nDie Unterschrift wird in die Datei '%s' geschrieben.\n" es: "\nEscribiendo el certificado de firma para '%s'\n" fr: "\nEcriture du certificat de signature dans '%s'\n" muttde: "\nDie Unterschrift wird in die Datei '%s' geschrieben.\n" "\n\007Error: Badly-formed or corrupted signature certificate.\n" de: "\n\007FEHLER: Format- oder Datenfehler in der Unterschrift.\n" es: "\n\007Error: Certificado de firma incorrecto o daado.\n" fr: "\n\007Erreur: certificat de signature mal form ou endommag\n" mutt: "\nError: Badly-formed or corrupted signature certificate.\n" muttde: "\nFEHLER: Format- oder Datenfehler in der Unterschrift.\n" "File \"%s\" does not have a properly-formed signature.\n" de: "Die Datei '%s' hat keine formal korrekte Unterschrift.\n" es: "El fichero \"%s\" no tiene una firma construida correctamente.\n" fr: "Le fichier \"%s\" n'a pas une signature correctement forme.\n" muttde: "Die Datei '%s' hat keine formal korrekte Unterschrift.\n" "compressed. " de: "gepackt. " es: "comprimido. " fr: "comprim. " muttde: "gepackt. " "\n\007Can't create compressed file '%s'\n" de: "\n\007FEHLER beim Erzeugen der gepackten Datei '%s'.\n" es: "\n\007No puede crearse el fichero comprimido '%s'\n" fr: "\n\007Cration du fichier compress '%s' impossible\n" mutt: "\nCan't create compressed file '%s'\n" muttde: "\nFEHLER beim Erzeugen der gepackten Datei '%s'.\n" "Compressing file..." de: "Packen der Datei..." es: "Comprimiendo el fichero..." fr: "Compression du fichier..." muttde: "Packen der Datei..." "\n\007Can't create ciphertext file '%s'\n" de: "\n\007FEHLER beim Erzeugen der verschlsselten\nAusgabedatei '%s'.\n" es: "\n\007No puede crearse el fichero cifrado '%s'\n" fr: "\n\007Cration du fichier chiffr '%s' impossible.\n" mutt: "\nCan't create ciphertext file '%s'\n" muttde: "\nFEHLER beim Erzeugen der verschlsselten\nAusgabedatei '%s'.\n" "\nYou need a pass phrase to encrypt the file. " de: "\nDu brauchst ein Mantra zum Verschlsseln der Datei." es: "\nSe necesita una contrasea para encriptar el fichero. " fr: "\nUn mot de passe est ncessaire pour chiffrer ce fichier. " muttde: "\nDu brauchst ein Mantra zum Verschlsseln der Datei." "\n\007Cannot find the public key matching userid '%s'\n\ This user will not be able to decrypt this message.\n" de: "\n\007Der ffentliche Schlssel zu Benutzer-ID \"%s\"\n\ ist nicht aufzufinden. Dieser Empfnger wird diese Nachricht nicht\n\ entschlsseln knnen.\n" es: "\n\007No puede encontrarse la clave pblica de '%s'\n\ Ese usuario no podr descifrar el mensaje.\n" fr: "\n\007Impossible de trouver la cl publique pour l'utilisateur '%s'\n\ Cet utilisateur ne pourra pas dchiffrer ce message.\n" mutt: "\nCannot find the public key matching userid '%s'\n\ This user will not be able to decrypt this message.\n" muttde: "\nDer ffentliche Schlssel zu Benutzer-ID \"%s\"\n\ ist nicht aufzufinden. Dieser Empfnger wird diese Nachricht nicht\n\ entschlsseln knnen.\n" "Skipping userid %s\n" de: "Die Benutzer-ID \"%s\" wird bersprungen.\n" es: "Saltando el identificador %s\n" fr: "je passe l'utiliateur %s\n" muttde: "Die Benutzer-ID \"%s\" wird bersprungen.\n" "\n\007'%s' is not a cipher file.\n" de: "\n\007'%s' ist keine verschlsselte Datei.\n" es: "\n\007'%s' no es un fichero cifrado.\n" fr: "\n\007'%s' n'est pas un fichier chiffr.\n" mutt: "\n'%s' is not a cipher file.\n" muttde: "\n'%s' ist keine verschlsselte Datei.\n" "\n\007Error: RSA block is possibly malformed. Old format, maybe?\n" de: "\n\007FEHLER: RSA-Block mglicherweise fehlerhaft. Vielleicht altes Format?\n" es: "\n\007Error: El bloque RSA est mal formado.\n\ Quiz se trate de un formato antiguo.\n" fr: "\n\007Erreur: Block RSA malform, vieux format ???" mutt: "\nError: RSA block is possibly malformed. Old format, maybe?\n" muttde: "\nFEHLER: RSA-Block mglicherweise fehlerhaft. Vielleicht altes Format?\n" "\nThis message can only be read by:\n" de: "\nDiese Nachricht kann nur gelesen werden von:\n" es: "\nEste mensaje slo puede leerlo:\n" fr: "\nCe message ne peut tre lu que par:\n" muttde: "\nDiese Nachricht kann nur gelesen werden von:\n" " keyID: %s\n" de: " Schlssel-ID: %s\n" es: " identificador: %s\n" fr: " Id de la clef: %s \n" muttde: " Schlssel-ID: %s\n" "\n\007You do not have the secret key needed to decrypt this file.\n" de: "\n\007Dir fehlt der private Schlssel zum Entschlsseln dieser Datei.\n" es: "\n\007No tienes la clave secreta necesaria para descifrar\ este fichero.\n" fr: "\n\007Vous n'avez pas la cl secrte requise pour dchiffrer\ \nce fichier.\n" mutt: "\nYou do not have the secret key needed to decrypt this file.\n" muttde: "\nDir fehlt der private Schlssel zum Entschlsseln dieser Datei.\n" "\n\007Error: Decrypted plaintext is corrupted.\n" de: "\n\007FEHLER: Der entschlsselte Klartext ist fehlerhaft.\n" es: "\n\007Error: El texto en claro desencriptado est daado.\n" fr: "\n\007Erreur: le fichier dchiffr est endommag.\n" mutt: "\nError: Decrypted plaintext is corrupted.\n" muttde: "\nFEHLER: Der entschlsselte Klartext ist fehlerhaft.\n" "\nYou need a pass phrase to decrypt this file. " de: "\nDu brauchst ein Mantra zum Entschlsseln dieser Datei." es: "\nSe necesita la contrasea para desencriptar este fichero. " fr: "\nUn mot de passe est ncessaire pour dchiffrer ce fichier. " muttde: "\nDu brauchst ein Mantra zum Entschlsseln dieser Datei." "\n\007Error: Bad pass phrase.\n" de: "\n\007FEHLER: Falsches Mantra!\n" es: "\n\007Error: Contrasea incorrecta.\n" fr: "\n\007Erreur: Mauvais mot de passe.\n" mutt: "\nError: Bad pass phrase.\n" muttde: "\nFEHLER: Falsches Mantra!\n" "Pass phrase appears good. " de: "\nDas Mantra scheint zu stimmen.\n" es: "Parece correcta. " fr: "Le mot de passe semble correct. " muttde: "\nDas Mantra scheint zu stimmen.\n" "Decompressing plaintext..." de: "Entpacken des Klartextes..." es: "Descomprimiendo el texto normal..." fr: "Decompression du texte en clair..." muttde: "Entpacken des Klartextes..." "\n\007Can't open compressed file '%s'\n" de: "\n\007FEHLER beim ffnen der gepackten Datei '%s'.\n" es: "\n\007No puede abrirse el fichero comprimido '%s'\n" fr: "\n\007Ouverture du fichier compress '%s' impossible.\n" mutt: "\nCan't open compressed file '%s'\n" muttde: "\nFEHLER beim ffnen der gepackten Datei '%s'.\n" "\007\nUnrecognized compression algorithm.\n\ This may require a newer version of PGP.\n" de: "\007\nUnbekanntes Pack-Verfahren. Eine neuere Version von PGP knnte notwendig sein.\n" es: "\007\nAlgoritmo de compresin no reconocido.\n\ Puede necesitarse una nueva versin de PGP.\n" fr: "\007\nAlgorithme de compression non reconnu.\n\ Ceci peut ncessiter une nouvelle version de PGP.\n" mutt: "\nUnrecognized compression algorithm.\n\ This may require a newer version of PGP.\n" muttde: "\nUnbekanntes Pack-Verfahren. Eine neuere Version von PGP knnte notwendig sein.\n" "\n\007Can't create decompressed file '%s'\n" de: "\n\007FEHLER beim Erzeugen der entpackten Datei '%s'.\n" es: "\n\007No puede crearse el fichero descomprimido '%s'\n" fr: "\n\007Cration du fichier dcompress '%s' impossible.\n" mutt: "\nCan't create decompressed file '%s'\n" muttde: "\nFEHLER beim Erzeugen der entpackten Datei '%s'.\n" "\n\007Decompression error. Probable corrupted input.\n" de: "\n\007FEHLER beim Entpacken! Wahrscheinlich beschdigte Eingangsdaten.\n" es: "\n007Error en descompresin. Probable entrada daada.\n" fr: "\n\007Erreur de Decompression, entre probablement corrompue" mutt: "\nDecompression error. Probable corrupted input.\n" muttde: "\nFEHLER beim Entpacken! Wahrscheinlich beschdigte Eingangsdaten.\n" "done. " de: "fertig. " es: "finalizado. " fr: "termin. " muttde: "fertig. " "Truncating filename '%s' " de: "Krzung des Dateinamens '%s' " es: "Truncando el nombre de fichero '%s' " fr: "troncation du fichier '%s'" muttde: "Krzung des Dateinamens '%s' " "y" de: "j" es: "s" fr: "o" muttde: "j" "n" de: "n" es: "n" fr: "n" muttde: "n" "\nShould '%s' be renamed to '%s' (Y/n)? " de: "\nSoll '%s' in '%s' umbenannt werden? (J/n) " es: "\nRenombrar '%s' como '%s' (S/n)? " fr: "\nEst-ce que '%s' doit tre renomm '%s' (O/n)? " muttde: "\nSoll '%s' in '%s' umbenannt werden? (J/n) " "\nDisk full.\n" de: "\nDie Platte ist voll!\n" es: "\nDisco lleno.\n" fr: "\nDisque plein.\n" muttde: "\nDie Platte ist voll!\n" "\nFile write error.\n" de: "\nFEHLER beim Schreiben einer Datei.\n" es: "\nError de escritura del fichero.\n" fr: "\nErreur d'criture sur fichier.\n" muttde: "\nFEHLER beim Schreiben einer Datei.\n" "\007Write error on stdout.\n" de: "\n\007FEHLER beim Schreiben auf stdout (Standard-Ausgabe).\n" es: "\007Error de escritura en la salida estndar (\"stdout\").\n" fr: "\007Erreur d'criture sur la sortie standard.\n" mutt: "Write error on stdout.\n" muttde: "\nFEHLER beim Schreiben auf stdout (Standard-Ausgabe).\n" "\n\007Cannot create temporary file '%s'\n" de: "\n\007FEHLER beim Erzeugen der Temporrdatei '%s'.\n" es: "\n\007No puede crearse el fichero temporal '%s'\n" fr: "\n\007Cration du fichier temporaire '%s' impossible\n" mutt: "\nCannot create temporary file '%s'\n" muttde: "\nFEHLER beim Erzeugen der Temporrdatei '%s'.\n" "Can't create output file '%s'\n" de: "\nFEHLER beim Erzeugen der Ausgabedatei '%s'.\n" es: "No puede crearse el fichero '%s'\n" fr: "Cration du fichier '%s' impossible.\n" muttde: "\nFEHLER beim Erzeugen der Ausgabedatei '%s'.\n" "\n\007Output file '%s' already exists.\n" de: "\n\007Die Ausgabedatei '%s' existiert bereits.\n" es: "\n\007El fichero de salida '%s' ya existe.\n" fr: "\n\007Le ficher de sortie '%s' existe dj.\n" mutt: "\nOutput file '%s' already exists.\n" muttde: "\nDie Ausgabedatei '%s' existiert bereits.\n" "\n\007Output file '%s' already exists. Overwrite (y/N)? " de: "\n\007Die Ausgabedatei '%s' existiert bereits. berschreiben? (j/N) " es: "\n\007El fichero de salida '%s' ya existe. Sobrescribir (s/N)? " fr: "\n\007Le fichier de sortie '%s' existe dj. A craser (o/N)? " mutt: "\nOutput file '%s' already exists. Overwrite (y/N)? " muttde: "\nDie Ausgabedatei '%s' existiert bereits. berschreiben? (j/N) " "Enter new file name:" de: "Gib den neuen Dateinamen ein:" es: "Introduzca el nuevo nombre de fichero: " fr: "Donnez un nouveau nom de fichier:" muttde: "Gib den neuen Dateinamen ein:" "Replacing signature from keyID %s on userid \"%s\"\n" de: "Die Unterschrift von der Schlssel-ID %s unter der\n\ Benutzer-ID \"%s\" wird ersetzt.\n" es: "Sustituyendo la firma de la clave %s para el usuario\n\ \"%s\"\n" fr: "Remplacement la signature de keyID %s de l'utilisateur '%s'\n" muttde: "Die Unterschrift von der Schlssel-ID %s unter der\n\ Benutzer-ID \"%s\" wird ersetzt.\n" "Verifying signature from %s\n" de: "berprfung der Unterschrift von \"%s\"\n" es: "Verificando la firma de %s\n" fr: "Vrification de la signature de %s\n" muttde: "berprfung der Unterschrift von \"%s\"\n" "on userid \"%s\"\n" de: "unter \"%s\".\n" es: "en el identificador \"%s\"\n" fr: "pour le nom d'utilisateur \"%s\"\n" muttde: "unter \"%s\".\n" "Replacing signature from %s\n" de: "Ersetzung der Unterschrift von \"%s\"\n" es: "Sustituyendo la firma de %s\n" fr: "Remplacement de la signature de %s" muttde: "Ersetzung der Unterschrift von \"%s\"\n" "Verification Failed\n" de: "\nDie berprfung ist fehlgeschlagen!\n" es: "Verificacin fallida\n" fr: "Echec de la vrification" muttde: "\nDie berprfung ist fehlgeschlagen!\n" "New signature from keyID %s on userid \"%s\"\n" de: "Neue Unterschrift von %s unter \"%s\".\n" es: "Nueva firma de la clave %s para el usuario \"%s\"\n" fr: "Nouvelle signature de la cl %s sur l'utilisateur \"%s\"\n" muttde: "Neue Unterschrift von %s unter \"%s\".\n" "New signature from %s\n" de: "Neue Unterschrift von \"%s\"\n" es: "Nueva firma de %s\n" fr: "\nNouvelle signature de %s\n" muttde: "Neue Unterschrift von \"%s\"\n" "Key revocation certificate from \"%s\".\n" de: "Urkunde zum Zurckziehen eines Schlssels\nvon \"%s\".\n" es: "Certificado de revocacin de clave de \"%s\".\n" fr: "Certificat de rvocation de cl de \"%s\".\n" muttde: "Urkunde zum Zurckziehen eines Schlssels\nvon \"%s\".\n" "\n\007WARNING: File '%s' contains bad revocation certificate.\n" de: "\n\007WARNUNG: Die Datei '%s' enthlt eine\n\ fehlerhafte Urkunde zum Zurckziehen eines Schlssels.\n" es: "\n\007AVISO: El fichero '%s' tiene un certificado de revocacin\ incorrecto.\n" fr: "\n\007ATTENTION: the fichier '%s' contient de mauvais certificats\n\ de rvocation.\n" mutt: "\nWARNING: File '%s' contains bad revocation certificate.\n" muttde: "\nWARNUNG: Die Datei '%s' enthlt eine\n\ fehlerhafte Urkunde zum Zurckziehen eines Schlssels.\n" "New userid: \"%s\".\n" de: "Neue Benutzer-ID: \"%s\".\n" es: "Nuevo identificador: \"%s\".\n" fr: "Nouveau nom d'utilisateur: \"%s\".\n" muttde: "Neue Benutzer-ID: \"%s\".\n" "\nWill be added to the following key:\n" de: "Sie wird zu dem folgenden Schlssel hinzugefgt:\n" es: "\nSe aadir a la clave siguiente:\n" fr: "\nSera ajout(e) la cl suivante:\n" muttde: "Sie wird zu dem folgenden Schlssel hinzugefgt:\n" "\nAdd this userid (y/N)? " de: "\nSoll diese Benutzer-ID hinzugefgt werden? (j/N) " es: "\nAadir este identificador (s/N)? " fr: "\nAjouter ce nom d'utilisateur (o/N)? " muttde: "\nSoll diese Benutzer-ID hinzugefgt werden? (j/N) " "\n\007Can't open key file '%s'\n" de: "\n\007FEHLER beim ffnen der Schlsseldatei '%s'.\n" es: "\n\007No puede abrirse el fichero de claves '%s'\n" fr: "\n\007Ouverture du fichier de cl '%s' impossible\n" mutt: "\nCan't open key file '%s'\n" muttde: "\nFEHLER beim ffnen der Schlsseldatei '%s'.\n" "\nKey ring file '%s' cannot be created.\n" de: "\nFEHLER beim Anlegen des Schlsselbunds '%s'.\n" es: "\nNo se puede crear el anillo de claves '%s'.\n" fr: "\nCration du fichier de cls '%s' impossible.\n" muttde: "\nFEHLER beim Anlegen des Schlsselbunds '%s'.\n" "\nLooking for new keys...\n" de: "\nSuche nach neuen Schlsseln...\n" es: "\nBuscando nuevas claves...\n" fr: "\nRecherche des nouvelles cls...\n" muttde: "\nSuche nach neuen Schlsseln...\n" "\n\007Could not read key from file '%s'.\n" de: "\n\007FEHLER beim Lesen des Schlssels aus Datei '%s'.\n" es: "\n\007No ha podido leerse la clave en el fichero '%s'.\n" fr: "\n\007Lecture impossible de la cl dans le fichier '%s'.\n" mutt: "\nCould not read key from file '%s'.\n" muttde: "\nFEHLER beim Lesen des Schlssels aus Datei '%s'.\n" "\n\007Warning: Key ID %s matches key ID of key already on \n\ key ring '%s', but the keys themselves differ.\n\ This is highly suspicious. This key will not be added to ring.\n\ Acknowledge by pressing return: " de: "\n\007WARNUNG: Die Schlssel-ID %s stimmt mit einem schon\n\ im Schlsselbund '%s' vorhandenen Schlssel\n\ berein, aber die Schlssel selbst sind nicht identisch.\n\ Das ist hchst verdchtig! Dieser Schlssel wird nicht zum\n\ Schlsselbund hinzugefgt. Mit Return besttigen: " es: "\n\007Advertencia: El identificador de clave %s coincide con otro\n\ en el anillo '%s', pero las claves son distintas.\n\ Es muy sospechoso. Esta clave no se incluir en el anillo.\n\ Confirmar pulsando 'retorno': " fr: "\n\007Attention: l'identificateur de cl %s correspond une cl\ \ndj dans le fichier de cls '%s', mais les cls sont diffrentes.\ \nCeci est trs suspect. Cette cl ne sera pas ajoute au fichier de cls.\ \nAppuyez sur la touche Retour ou Entre: " mutt: "\nWarning: Key ID %s matches key ID of key already on \n\ key ring '%s', but the keys themselves differ.\n\ This is highly suspicious. This key will not be added to ring.\n\ Acknowledge by pressing return: " muttde: "\nWARNUNG: Die Schlssel-ID %s stimmt mit einem schon\n\ im Schlsselbund '%s' vorhandenen Schlssel\n\ berein, aber die Schlssel selbst sind nicht identisch.\n\ Das ist hchst verdchtig! Dieser Schlssel wird nicht zum\n\ Schlsselbund hinzugefgt. Mit Return besttigen: " "\nDo you want to add this key to keyring '%s' (y/N)? " de: "\nMchtest Du diesen Schlssel zum Schlsselbund\n'%s' hinzufgen? (j/N) " es: "\nQuieres aadir esta clave al anillo '%s' (s/N)? " fr: "\nVoulez vous ajouter cette cl au fichier de cls '%s' (o/N)? " muttde: "\nMchtest Du diesen Schlssel zum Schlsselbund\n'%s' hinzufgen? (j/N) " "Key has been revoked.\n" de: "Der Schlssel wurde zurckgezogen.\n" es: "La clave se ha revocado.\n" fr: "La cl a t rvoque.\n" muttde: "Der Schlssel wurde zurckgezogen.\n" "\n\007Key file contains duplicate keys: cannot be added to keyring\n" de: "\n\007Die Datei enthlt doppelte Schlssel, die nicht zum Schlsselbund\n\ hinzugefgt werden.\n" es: "\n\007El fichero contiene claves duplicadas: \ no puede aadirse al anillo\n" fr: "\n\007Le fichier contient des cls dupliques: impossible\n\ de l'ajouter au fichier de cls\n" mutt: "\nKey file contains duplicate keys: cannot be added to keyring\n" muttde: "\nDie Datei enthlt doppelte Schlssel, die nicht zum Schlsselbund\n\ hinzugefgt werden.\n" "No new keys or signatures in keyfile.\n" de: "Keine neuen Schlssel oder Unterschriften in der Datei.\n" es: "No hay nuevas claves ni nuevas firmas en el fichero.\n" fr: "Pas de nouvelles cls ou signatures dans le fichier de cls.\n" muttde: "Keine neuen Schlssel oder Unterschriften in der Datei.\n" "\nKeyfile contains:\n" de: "\nDie Datei enthlt folgende Schlssel:\n" es: "\nEl fichero de claves contiene:\n" fr: "\nLe fichier de cls contient:\n" muttde: "\nDie Datei enthlt folgende Schlssel:\n" "%4d new key(s)\n" de: "%4d neue(n) Schlssel\n" es: "%4d nueva(s) clave(s)\n" fr: "%4d nouvelle(s) cl(s)\n" muttde: "%4d neue(n) Schlssel\n" "%4d new signatures(s)\n" de: "%4d neue Unterschrift(en)\n" es: "%4d nueva(s) firma(s)\n" fr: "%4d nouvelle(s) signatures(s)\n" muttde: "%4d neue Unterschrift(en)\n" "%4d new user ID(s)\n" de: "%4d neue Benutzer-ID(s)\n" es: "%4d nuevo(s) identificador(es) de usuario\n" fr: "%4d nouveau(x) nom(s) d'utilisateur\n" muttde: "%4d neue Benutzer-ID(s)\n" "%4d new revocation(s)\n" de: "%4d neue Urkunde(n) zum Zurckziehen von Schlsseln\n" es: "%4d nueva(s) revocacion(es)\n" fr: "%4d nouvelle(s) rvocation(s)\n" muttde: "%4d neue Urkunde(n) zum Zurckziehen von Schlsseln\n" "\nNo keys found in '%s'.\n" de: "\nKeine Schlssel in '%s' gefunden.\n" es: "\nNo se encuentran claves en '%s'.\n" fr: "\nPas de cls trouves dans '%s'.\n" muttde: "\nKeine Schlssel in '%s' gefunden.\n" "\nOne or more of the new keys are not fully certified.\n\ Do you want to certify any of these keys yourself (y/N)? " de: "\nEin oder mehrere neue Schlssel sind nicht ausreichend beglaubigt.\n\ Willst Du sie selbst beglaubigen? (j/N) " es: "\nUna o ms de las nuevas claves no estn completamente certificadas.\n\ Quieres certificar alguna de ellas t mismo (s/N)? " fr: "\nUne ou plusieurs des nouvelles cls ne sont pas compltement\ \ncertifies. Voulez vous certifier ces cls vous mme (o/N)? " muttde: "\nEin oder mehrere neue Schlssel sind nicht ausreichend beglaubigt.\n\ Willst Du sie selbst beglaubigen? (j/N) " "\nDo you want to certify this key yourself (y/N)? " de: "\nWillst Du diesen Schlssel selbst beglaubigen? (j/N) " es: "\nQuieres certificar esta clave t mismo (s/N)? " fr: "\nVoulez vous certifier cette cl vous mme (o/N)? " muttde: "\nWillst Du diesen Schlssel selbst beglaubigen? (j/N) " "undefined" de: "undefin." es: "sin definir" fr: "indfinie" muttde: "undefin." "unknown" de: "unbekannt" es: "desconocida" fr: "inconnu" muttde: "unbekannt" "untrusted" de: "kein" es: "no fiable" fr: "non sr" muttde: "kein" "marginal" de: "teilweise" es: "relativa" fr: "marginal" muttde: "teilweise" "complete" de: "voll" es: "completa" fr: "complte" muttde: "voll" "ultimate" de: "absolut" es: "fundamental" fr: "ultime" muttde: "absolut" "\nCan't open backup key ring file '%s'\n" de: "\nFEHLER beim ffnen der Schlsselbund-Kopie '%s'.\n" es: "\nNo se puede abrir la copia de seguridad del anillo '%s'\n" fr: "\nImpossible d'ouvrir le fichier de cl de sauvegarde '%s'\n" muttde: "\nFEHLER beim ffnen der Schlsselbund-Kopie '%s'.\n" "\n%d \"trust parameter(s)\" need to be changed.\n" de: "\n%d 'Vertrauens-Einstellung(en)' mu/mssen gendert werden.\n" es: "\n%d \"parmetro(s) de confianza\" debe(n) cambiarse.\n" fr: "\n%d \"paramtre(s) de confiance\" doi(ven)t tre chang(s).\n" muttde: "\n%d 'Vertrauens-Einstellung(en)' mu/mssen gendert werden.\n" "Continue with '%s' (Y/n)? " de: "Weiter mit '%s'? (J/n) " es: "Seguir con '%s' (S/n)? " fr: "Continuer avec '%s' (O/n)? " muttde: "Weiter mit '%s'? (J/n) " "\n%d \"trust parameter(s)\" changed.\n" de: "\n%d 'Vertrauens-Einstellung(en)' gendert.\n" es: "\nCambiados %d \"parmetro(s) de confianza.\n" fr: "\n%d \"paramtre(s) de confiance\" chang(s).\n" muttde: "\n%d 'Vertrauens-Einstellung(en)' gendert.\n" "Update public keyring '%s' (Y/n)? " de: "ffentlichen Schlsselbund '%s' aktualisieren? (J/n) " es: "Actualizar el anillo de claves pblicas '%s' (S/n)? " fr: "Modifier le fichier de cls publiques '%s' (O/n)? " muttde: "ffentlichen Schlsselbund '%s' aktualisieren? (J/n) " "\nCan't open secret key ring file '%s'\n" de: "\nFEHLER beim ffnen des privaten Schlsselbunds '%s'.\n" es: "\nNo puede abrirse el anillo de claves secretas '%s'\n" fr: "\nOuverture du fichier de cls secrtes '%s' impossible.\n" muttde: "\nFEHLER beim ffnen des privaten Schlsselbunds '%s'.\n" "\nPass 1: Looking for the \"ultimately-trusted\" keys...\n" de: "\nDurchlauf 1: Suche nach 'absolut vertrauenswrdigen' Schlsseln...\n" es: "\nProceso 1: Bsqueda de las claves \"fundamentalmente fiables\" ...\n" fr: "\nPasse 1: Recherche des cls \"de confiance ultime\"...\n" muttde: "\nDurchlauf 1: Suche nach 'absolut vertrauenswrdigen' Schlsseln...\n" "\nPass 2: Tracing signature chains...\n" de: "\nDurchlauf 2: berprfung von verketteten Unterschriften...\n" es: "\nProceso 2: Seguimiento de las cadenas de firmas...\n" fr: "\nPasse 2: Vrification des chaines de signatures...\n" muttde: "\nDurchlauf 2: berprfung von verketteten Unterschriften...\n" "Keyring contains duplicate key: %s\n" de: "\nDer Schlsselbund enthlt den Schlssel %s doppelt.\n" es: "El anillo contiene una clave duplicada: %s\n" fr: "Le fichier de cls contient des cls dupliques: %s\n" muttde: "\nDer Schlsselbund enthlt den Schlssel %s doppelt.\n" "No ultimately-trusted keys.\n" de: "Keine absolut vertrauenswrdigen Schlssel gefunden.\n" es: "No hay ninguna clave fundamentalmente fiable.\n" fr: "Pas de cls de confiance ultime.\n" muttde: "Keine absolut vertrauenswrdigen Schlssel gefunden.\n" " KeyID Trust Validity User ID\n" de: "\n ID Vertrauen Gltigk. Benutzer\n" es: " Clave Confianza Validez Identificador\n" fr: " IDcle Conf. Validit Utilisateur\n" muttde: "\n ID Vertrauen Gltigk. Benutzer\n" "(KeyID: %s)\n" de: "(Schlssel-ID: %s)\n" es: "(Identificador: %s)\n" fr: "(IDclef: %s)\n" muttde: "(Schlssel-ID: %s)\n" "\nAn \"axiomatic\" key is one which does not need certifying by\n\ anyone else. Usually this special status is reserved only for your\n\ own keys, which should also appear on your secret keyring. The owner\n\ of an axiomatic key (who is typically yourself) is \"ultimately trusted\"\n\ by you to certify any or all other keys.\n" de: "\nEin 'definitionsgem vertrauenswrdiger' Schlssel braucht nicht von einer\n\ anderen Person beglaubigt zu werden. Normalerweise ist dieser spezielle Status\n\ nur fr Deine eigenen Schlssel reserviert, die sich auch in Deinem privaten\n\ Schlsselbund befinden sollten. Der Besitzer eines 'definitionsgem vertrau-\n\ enswrdigen' Schlssels (das bist in der Regel Du selbst) wird von Dir als\n\ 'absolut vertrauenswrdig' betrachtet, beliebige oder sogar alle anderen\n\ Schlssel Dir gegenber zu beglaubigen.\n" es: "\nUna clave \"axiomatica\" es aquella que no necesita certificacin.\n\ Normalmente este estado se reserva para tus propias claves, que deben\n\ aparecer tambin en tu anillo de claves secretas. El propietario de una\n\ clave axiomtica (normalmente t mismo) es \"fundamentalmente fiable\"\n\ para certificar cualquier clave.\n" fr: "\n Une clef 'axiomatique' est un clef qui n'a pas besoin d'tre\n\ sign par personne. Habituellement ce statut est reserv vos propres \n\ clefs, celles qui apparaissent dans votre tousseau de clefs secrtes. \n\ Le propritaire de ces clefs (vous-mme) possde votre 'confiance ultime'\n\ pour accorder un certificat l'une au l'autre des autres clefs.\n" muttde: "\nEin 'definitionsgem vertrauenswrdiger' Schlssel braucht nicht von einer\n\ anderen Person beglaubigt zu werden. Normalerweise ist dieser spezielle Status\n\ nur fr Deine eigenen Schlssel reserviert, die sich auch in Deinem privaten\n\ Schlsselbund befinden sollten. Der Besitzer eines 'definitionsgem vertrau-\n\ enswrdigen' Schlssels (das bist in der Regel Du selbst) wird von Dir als\n\ 'absolut vertrauenswrdig' betrachtet, beliebige oder sogar alle anderen\n\ Schlssel Dir gegenber zu beglaubigen.\n" "\nKey for user ID \"%s\"\n\ is designated as an \"ultimately-trusted\" introducer, but the key\n\ does not appear in the secret keyring.\n\ Use this key as an ultimately-trusted introducer (y/N)? " de: "\nDer Schlssel von \"%s\"\n\ soll ein 'absolut vertrauenswrdiger Einfhrer' werden, aber er befindet\n\ sich nicht im privaten Schlsselbund. Soll ich diesen Schlssel trotzdem\n\ als 'absolut vertrauenswrdigen Einfhrer' behandeln? (j/N) " es: "\nLa clave del usuario \"%s\"\n\ est designada como referencia \"fundamentalmente fiable\", pero la clave no aparece en el anillo de claves secretas.\n\ Se utiliza como referencia fundamentalmente fiable (s/N)? " fr: "\nLa clef de l'utilisateur '%s' \n\ a t dsign comme ayant la 'confiance ulime', mais cette clef \n\ n'apparrat pas dans le trousseau des clefs secrtes. \n\ Voulez-vous utiliser cette clef comme ayant la 'confiance ultime'? (o/N) " muttde: "\nDer Schlssel von \"%s\"\n\ soll ein 'absolut vertrauenswrdiger Einfhrer' werden, aber er befindet\n\ sich nicht im privaten Schlsselbund. Soll ich diesen Schlssel trotzdem\n\ als 'absolut vertrauenswrdigen Einfhrer' behandeln? (j/N) " "\n\007Cannot read from secret keyring.\n" de: "\n\007FEHLER beim Lesen des privaten Schlsselbunds.\n" es: "\n\007No puede leerse el anillo de claves secretas.\n" fr: "\n\007Lecture du fichier de cls secrtes impossible.\n" mutt: "\nCannot read from secret keyring.\n" muttde: "\nFEHLER beim Lesen des privaten Schlsselbunds.\n" "\n\007WARNING: Public key for user ID: \"%s\"\n\ does not match the corresponding key in the secret keyring.\n" de: "\n\007WARNUNG: Der ffentliche Schlssel\nvon \"%s\" stimmt nicht\n\ mit dem Gegenstck im privaten Schlsselbund berein.\n" es: "\n\007AVISO: La clave pblica de \"%s\"\n\ no coincide con la clave correspondiente en el anillo de claves secretas.\n" fr: "\n\007ATTENTION: la cl publique pour l'utilisateur: \"%s\"\n\ ne correspond pas avec la cl respective dans le fichier de cls\ secrtes.\n" mutt: "\nWARNING: Public key for user ID: \"%s\"\n\ does not match the corresponding key in the secret keyring.\n" muttde: "\nWARNUNG: Der ffentliche Schlssel\nvon \"%s\" stimmt nicht\n\ mit dem Gegenstck im privaten Schlsselbund berein.\n" "This is a serious condition, indicating possible keyring tampering.\n" de: "Dies knnte bedeuten, da die Schlsselbunde manipuliert wurden!\n" es: "Es una situacin grave: posible manipulacin de anillos.\n" fr: "Ceci est une condition srieuse, indiquant une possible manipulation\n\ du fichier de cls.\n" muttde: "Dies knnte bedeuten, da die Schlsselbunde manipuliert wurden!\n" "\nKey for user ID \"%s\"\n\ also appears in the secret key ring." de: "\nDer Schlssel fr die Benutzer-ID \"%s\"\n\ ist auch im privaten Schlsselbund vorhanden." es: "\nLa clave del identificador \"%s\"\n\ tambin aparece en el anillo de claves secretas." fr: "\nLa clef pour l'utilisateur avec id: \"%s\"\n\ apparait egalement dans le repertoire des clefs secretes." muttde: "\nDer Schlssel fr die Benutzer-ID \"%s\"\n\ ist auch im privaten Schlsselbund vorhanden." "\nUse this key as an ultimately-trusted introducer (y/N)? " de: "\nSoll dieser Schlssel als 'absolut vertrauenswrdigen Einfhrer'\n\ behandelt werden? (j/N) " es: "\nSe utiliza esta clave como referencia fundamentalmente fiable (s/N)? " fr: "\nUtiliser cette cl comme introducteur de confiance ultime (o/N)? " muttde: "\nSoll dieser Schlssel als 'absolut vertrauenswrdigen Einfhrer'\n\ behandelt werden? (j/N) " "Public key for: \"%s\"\n\ is not present in the backup keyring '%s'.\n" de: "Der ffentliche Schlssel von \"%s\"\n\ ist nicht in der Schlsselbund-Kopie '%s' enthalten.\n" es: "La clave pblica de: \"%s\"\n\ no se encuentra en la copia de seguridad del anillo '%s'.\n" fr: "La cl publique pour: \"%s\"\n\ n'est pas prsente dans le fichier de cls de sauvegarde '%s'.\n" muttde: "Der ffentliche Schlssel von \"%s\"\n\ ist nicht in der Schlsselbund-Kopie '%s' enthalten.\n" "\n\007WARNING: Secret key for: \"%s\"\n\ does not match the key in the backup keyring '%s'.\n" de: "\n\007WARNUNG: Der private Schlssel von \"%s\"\n\ entspricht nicht der Kopie im Schlsselbund '%s'.\n" es: "\n\007AVISO: La clave secreta de: \"%s\"\n\ no coincide con la clave en la copia de seguridad del anillo '%s'.\n" fr: "\n\007ATTENTION: la cl secrte pour: \"%s\"\n\ ne correspond pas avec la cl dans le fichier de cls de sauvegarde.\n" mutt: "\nWARNING: Secret key for: \"%s\"\n\ does not match the key in the backup keyring '%s'.\n" muttde: "\nWARNUNG: Der private Schlssel von \"%s\"\n\ entspricht nicht der Kopie im Schlsselbund '%s'.\n" "\nMake a determination in your own mind whether this key actually\n\ belongs to the person whom you think it belongs to, based on available\n\ evidence. If you think it does, then based on your estimate of\n\ that person's integrity and competence in key management, answer\n\ the following question:\n" de: "\nEntscheide fr Dich, ob dieser Schlssel tatschlich zu dieser Person gehrt.\n\ Triff diese Entscheidung unter Bercksichtigung der zur Verfgung stehenden\n\ Informationen. Wenn Du glaubst, da der Schlssel echt ist, dann beantworte\n\ folgende Frage aufgrund Deiner Einschtzung der Vertrauenswrdigkeit der\n\ Person und ihrer Kompetenz beim Umgang mit PGP-Schlsseln:\n" es: "\nDecide t mismo si esta clave realmente pertenece, segn la\n\ evidencia a tu alcance, a la persona que crees. Si es as,\n\ contesta a la siguiente pregunta, basndote en tu estimacin de la\n\ integridad de esa persona y de su conocimiento sobre gestin de claves:\n" fr: "\nDeterminez vous mme si cette cl appartient vraiment la personne\ \n qui vous croyez qu'elle appartient, selon les informations disponibles.\ \nSi vous le croyez, alors selon votre estimation de l'intgrit de cette\ \npersonne et de sa comptence dans la gestion de cls, rpondez la\ \nquestion suivante:\n" muttde: "\nEntscheide fr Dich, ob dieser Schlssel tatschlich zu dieser Person gehrt.\n\ Triff diese Entscheidung unter Bercksichtigung der zur Verfgung stehenden\n\ Informationen. Wenn Du glaubst, da der Schlssel echt ist, dann beantworte\n\ folgende Frage aufgrund Deiner Einschtzung der Vertrauenswrdigkeit der\n\ Person und ihrer Kompetenz beim Umgang mit PGP-Schlsseln:\n" "\nWould you trust \"%s\"\n\ to act as an introducer and certify other people's public keys to you?\n\ (1=I don't know. 2=No. 3=Usually. 4=Yes, always.) ? " de: "\nWrdest Du \"%s\" als 'Einfhrer'\n\ und 'Beglaubiger' fr die ffentlichen Schlssel Dritter vertrauen?\n\ (1=Ich wei nicht; 2=Nein; 3=In der Regel; 4=Ja, immer) : " es: "\nConfiaras en \"%s\"\n\ como referencia y para certificar ante ti otras claves pblicas?\n\ (1=No s. 2=No. 3=Normalmente. 4=S, siempre.) ? " fr: "\nAuriez vous confiance en \"%s\"\n\ pour servir d'introducteur et certifier pour vous les cls publiques d'autres\ \npersonnes? (1=Ne sais pas. 2=Non. 3=Gnralement. 4=Oui, toujours.) ? " muttde: "\nWrdest Du \"%s\" als 'Einfhrer'\n\ und 'Beglaubiger' fr die ffentlichen Schlssel Dritter vertrauen?\n\ (1=Ich wei nicht; 2=Nein; 3=In der Regel; 4=Ja, immer) : " "This user is untrusted to certify other keys.\n" de: "Dieser Benutzer ist nicht vertrauenswrdig genug,\n\ um andere Schlssel zu beglaubigen.\n" es: "Este usuario no es fiable para certificar otras claves.\n" fr: "Cet utilisateur n'est pas de confiance pour certifier d'autres cls.\n" muttde: "Dieser Benutzer ist nicht vertrauenswrdig genug,\n\ um andere Schlssel zu beglaubigen.\n" "This user is generally trusted to certify other keys.\n" de: "Dieser Benutzer ist in der Regel vertrauenswrdig genug,\n\ um andere Schlssel zu beglaubigen.\n" es: "Este usuario es de relativa confianza para certificar otras claves.\n" fr: "Cet utilisateur est gnralement de confiance pour certifier d'autres\ cls.\n" muttde: "Dieser Benutzer ist in der Regel vertrauenswrdig genug,\n\ um andere Schlssel zu beglaubigen.\n" "This user is completely trusted to certify other keys.\n" de: "Dieser Benutzer ist immer vertrauenswrdig genug,\n\ um andere Schlssel zu beglaubigen.\n" es: "Este usuario es de completa confianza para certificar otras claves.\n" fr: "Cet utilisateur est de confiance totale pour certifier d'autres cls.\n" muttde: "Dieser Benutzer ist immer vertrauenswrdig genug,\n\ um andere Schlssel zu beglaubigen.\n" "This axiomatic key is ultimately trusted to certify other keys.\n" de: "Dieser Schlssel ist definitionsgem vertrauenswrdig genug,\n\ um andere Schlssel zu beglaubigen.\n" es: "Esta clave axiomtica es absolutamente fiable para certificar otras.\n" fr: "Cette cl axiomatique est de confiance ultime pour certifier\n\ d'autres cls\n" muttde: "Dieser Schlssel ist definitionsgem vertrauenswrdig genug,\n\ um andere Schlssel zu beglaubigen.\n" "This key/userID association is not certified.\n" de: "Diese Schlssel-/Benutzer-Zuordnung ist nicht besttigt.\n" es: "Esta asociacin clave/usuario no est certificada.\n" fr: "Cette association cl/utilisateur n'est pas certifie.\n" muttde: "Diese Schlssel-/Benutzer-Zuordnung ist nicht besttigt.\n" "This key/userID association is marginally certified.\n" de: "Diese Schlssel-/Benutzer-Zuordnung ist teilweise besttigt.\n" es: "Esta asociacin clave/usuario est relativamente certificada.\n" fr: "Cette association cl/utilisateur est marginalement certifie.\n" muttde: "Diese Schlssel-/Benutzer-Zuordnung ist teilweise besttigt.\n" "This key/userID association is fully certified.\n" de: "Diese Schlssel-/Benutzer-Zuordnung ist voll besttigt.\n" es: "Esta asociacin clave/usuario est completamente certificada.\n" fr: "Cette association cl/utilisateur est compltement certifie.\n" muttde: "Diese Schlssel-/Benutzer-Zuordnung ist voll besttigt.\n" " Questionable certification from:\n " de: " Fragwrdige Beglaubigung von:\n " es: " Certificacin cuestionable de:\n " fr: " Certificat de confiance douteux de:\n " muttde: " Fragwrdige Beglaubigung von:\n " " Untrusted certification from:\n " de: " Unglaubwrdige Beglaubigung von:\n " es: " Certificacin no fiable de:\n " fr: " Certificat non sr de:\n " muttde: " Unglaubwrdige Beglaubigung von:\n " " Generally trusted certification from:\n " de: " Glaubwrdige Beglaubigung von:\n " es: " Certificacin relativamente fiable de:\n " fr: " Certificat de confiance relatif de:\n " muttde: " Glaubwrdige Beglaubigung von:\n " " Completely trusted certification from:\n " de: " Voll glaubwrdige Beglaubigung von:\n " es: " Certificacin completamente fiable de:\n " fr: " Certificat de confiance complet de:\n " muttde: " Voll glaubwrdige Beglaubigung von:\n " " Axiomatically trusted certification from:\n " de: " Definitionsgem glaubwrdige Beglaubigung von:\n " es: " Certificacin axiomticamente fiable de:\n " fr: " Certificat de confiance par axiome de:\n " muttde: " Definitionsgem glaubwrdige Beglaubigung von:\n " "\nKey for user ID: %s\n" de: "\nSchlssel fr Benutzer-ID \"%s\",\n" es: "\nClave del usuario: %s\n" fr: "\nCl pour le nom d'utilisateur: %s\n" muttde: "\nSchlssel fr Benutzer-ID \"%s\",\n" "%d-bit key, key ID %s, created %s\n" de: "%d-Bit-Schlssel, Schlssel-ID: %s, erzeugt am: %s.\n" es: "Clave de %d bits, con identificador %s, creada el %\n" fr: "Clef de %d bits. Id clef %s cr %s\n" muttde: "%d-Bit-Schlssel, Schlssel-ID: %s, erzeugt am: %s.\n" "Bad key format.\n" de: "Falsches Schlssel-Format.\n" es: "Formato incorrecto de clave.\n" fr: "Mauvais format de cl\n" muttde: "Falsches Schlssel-Format.\n" "Unrecognized version.\n" de: "Unbekannte Version.\n" es: "Versin no reconocida.\n" fr: "Version non reconnue.\n" muttde: "Unbekannte Version.\n" "Key is disabled.\n" de: "Der Schlssel ist gesperrt.\n" es: "La clave est desactivada.\n" fr: "La cl est inactive.\n" muttde: "Der Schlssel ist gesperrt.\n" "Also known as: %s\n" de: "Alternative Benutzer-ID: %s\n" es: "Tambin conocido como: %s\n" fr: "Egalement connu(e) en tant que: %s\n" muttde: "Alternative Benutzer-ID: %s\n" " Certified by: " de: " Beglaubigt von: " es: " Certificado por: " fr: " Certifie par: " muttde: " Beglaubigt von: " "\nWarning: keyid %4d/%s %s has no user id!\n" de: "\nWARNUNG: Der Schlssel '%4d/%s %s' hat keine Benutzer-ID!\n" es: "\nAdvertencia: la clave %4d/%s %s no tiene identificador\n" fr: "\nAttention: IDclef %4d/%s %s n'est pas associ un utilisateur!\n" muttde: "\nWARNUNG: Der Schlssel '%4d/%s %s' hat keine Benutzer-ID!\n" "Updated keyID: 0x%s\n" de: "Die Schlssel-ID 0x%s wurde aktualisiert.\n" es: "Identificador actualizado: 0x%s\n" fr: "Mise jour de la clef ID: 0x%s\n" muttde: "Die Schlssel-ID 0x%s wurde aktualisiert.\n" "\n\007Unable to create key file '%s'.\n" de: "\n\007FEHLER beim Erzeugen der Schlsseldatei '%s'.\n" es: "\n\007No puede crearse el fichero de claves '%s'.\n" fr: "\n\007Impossible de crer le fichier de cls '%s'.\n" mutt: "\nUnable to create key file '%s'.\n" muttde: "\nFEHLER beim Erzeugen der Schlsseldatei '%s'.\n" "\n\007Keyring file '%s' does not exist. " de: "\n\007Der Schlsselbund '%s' existiert nicht.\n" es: "\n\007El anillo '%s' no existe. " fr: "\n\007Le fichier de cls '%s' n'existe pas. " mutt: "\nKeyring file '%s' does not exist. " muttde: "\nDer Schlsselbund '%s' existiert nicht.\n" "\n\007Sorry, this key has been revoked by its owner.\n" de: "\n\007Dieser Schlssel wurde von seinem Besitzer zurckgezogen.\n" es: "\n\007Esa clave ha sido revocada por su propietario.\n" fr: "\n\007Dsol, cette cl a t rvoque par son propritaire.\n" mutt: "\nSorry, this key has been revoked by its owner.\n" muttde: "\nDieser Schlssel wurde von seinem Besitzer zurckgezogen.\n" "\nKey for user ID \"%s\"\n\ has been revoked. You cannot use this key.\n" de: "\nDer Schlssel von \"%s\"\n\ wurde zurckgezogen und kann nicht verwendet werden.\n" es: "\nLa clave del usuario \"%s\"\n\ ha sido revocada. No puede utilizarse.\n" fr: "\nLa cl pour l'utilisateur \"%s\"\n\ a t rvoque. Vous ne pouvez utiliser cette cl.\n" muttde: "\nDer Schlssel von \"%s\"\n\ wurde zurckgezogen und kann nicht verwendet werden.\n" "\n\007Key matching expected Key ID %s not found in file '%s'.\n" de: "\n\007Der zur erwarteten Schlssel-ID %s passende Schlssel\n\ ist nicht in der Datei '%s' enthalten.\n" es: "\n\007Se esperaba una clave %s que no se encuentra en '%s'.\n" fr: "\n\007Cl correspondant l'identificateur %s non trouve\ \ndans le fichier '%s'.\n" mutt: "\nKey matching expected Key ID %s not found in file \n\ '%s'.\n" muttde: "\nDer zur erwarteten Schlssel-ID %s passende Schlssel\n\ ist nicht in der Datei '%s' enthalten.\n" "\n\007Key matching userid '%s' not found in file '%s'.\n" de: "\n\007Der zur Benutzer-ID \"%s\" passende Schlssel\n\ ist nicht in der Datei '%s' enthalten.\n" es: "\n\007La clave del usuario '%s' no se encuentra en el fichero '%s'.\n" fr: "\n\007Cl correspondant l'utilisateur '%s' introuvable\n\ dans le fichier '%s'.\n" mutt: "\nKey matching userid '%s' not found in file '%s'.\n" muttde: "\nDer zur Benutzer-ID \"%s\" passende Schlssel\n\ ist nicht in der Datei '%s' enthalten.\n" "Enter secret key filename: " de: "Dateiname des privaten Schlssels: " es: "Introduzca el nombre del anillo de claves secretas: " fr: "Entrez le nom du fichier de cls secrtes: " muttde: "Dateiname des privaten Schlssels: " "Enter public key filename: " de: "Dateiname des ffentlichen Schlssels: " es: "Introduzca el nombre del anillo de claves pblicas: " fr: "Entrez le nom du fichier de cls publiques: " muttde: "Dateiname des ffentlichen Schlssels: " "\nYou need a pass phrase to unlock your RSA secret key. " de: "\nDu brauchst ein Mantra, um Deinen privaten RSA-Schlssel zu benutzen." es: "\nSe necesita la contrasea para abrir la clave secreta RSA. " fr: "\nVous devez avoir un mot de passe pour utiliser votre cl secrte RSA." muttde: "\nDu brauchst ein Mantra, um Deinen privaten RSA-Schlssel zu benutzen." "No passphrase; secret key unavailable.\n" de: "\nKein Mantra; der private Schlssel kann nicht benutzt werden.\n" es: "\nSin contrasea; la clave secreta no est disponible.\n" fr: "\nSans la phrase secrete, pas de clef secrete disponible.\n" muttde: "\nKein Mantra; der private Schlssel kann nicht benutzt werden.\n" "\nAdvisory warning: This RSA secret key is not protected by a \ passphrase.\n" de: "\nHinweis: Dieser private Schlssel ist nicht durch ein Mantra geschtzt.\n" es: "\nAdvertencia: Esta clave secreta RSA no tiene contrasea.\n" fr: "\nAttention: cette cl secrte RSA n'est pas protge par un mot \ de passe" muttde: "\nHinweis: Dieser private Schlssel ist nicht durch ein Mantra geschtzt.\n" "Pass phrase is good. " de: "\nDas Mantra ist richtig.\n" es: "La contrasea es correcta. " fr: "Le mot de passe est correct. " muttde: "\nDas Mantra ist richtig.\n" "\n\007Key file '%s' is not a secret key file.\n" de: "\n\007Die Datei '%s' ist keine private Schlsseldatei.\n" es: "\n\007El fichero '%s' no tiene claves secretas.\n" fr: "\n\007Le fichier de cl '%s' n'est pas un fichier de cls secrtes.\n" mutt: "\nKey file '%s' is not a secret key file.\n" muttde: "\nDie Datei '%s' ist keine private Schlsseldatei.\n" "Key fingerprint =" de: "Fingerabdruck des Schlssels:" es: "Huella dactilar =" fr: "Empreinte de la cl =" muttde: "Fingerabdruck des Schlssels:" "\nKey ring: '%s'" de: "\nSchlsselbund '%s':\n" es: "\nAnillo de claves: '%s',\n" fr: "\nFichier de cl: '%s'" muttde: "\nSchlsselbund '%s':\n" ", looking for user ID \"%s\"." de: "Suche nach Benutzer-ID \"%s\":\n" es: "buscando el usuario \"%s\"\n" fr: ", recherche du nom d'utilisateur \"%s\"." muttde: "Suche nach Benutzer-ID \"%s\":\n" "1 matching key found.\n" de: "Es wurde ein passender Schlssel gefunden.\n" es: "Se ha encontrado una clave.\n" fr: "1 clef trouve.\n" muttde: "Es wurde ein passender Schlssel gefunden.\n" "%d matching keys found.\n" de: "Es wurden %d passende Schlssel gefunden.\n" es: "Se han encontrado %d claves.\n" fr: "%d clefs trouves. \n" muttde: "Es wurden %d passende Schlssel gefunden.\n" "\nChecking signatures...\n" de: "\nberprfung der Unterschriften...\n" es: "\nComprobando las firmas...\n" fr: "\nVrification des signatures...\n" muttde: "\nberprfung der Unterschriften...\n" "*** KEY REVOKED ***\n" de: "*** ZURCKGEZOGEN ***\n" es: "*** CLAVE REVOCADA ***\n" fr: "*** CLEF REVOQUE ***\n" muttde: "*** ZURCKGEZOGEN ***\n" "(Unknown signator, can't be checked)" de: "(Unterschreibender unbekannt, keine Prfung)" es: "(Firmante desconocido, no puede comprobarse)" fr: "(Signataire inconnu, ne peut tre vrifi)" muttde: "(Unterschreibender unbekannt, keine Prfung)" "(Key too long, can't be checked)" de: "(Schlssel zu lang, keine Prfung)" es: "(Clave demasiado larga, no puede comprobarse)" fr: "(Clef trop longue, ne peut etre verifie)" muttde: "(Schlssel zu lang, keine Prfung)" "(Malformed or obsolete signature format)" de: "(Unterschriftsformat fehlerhaft oder veraltet)" es: "(Formato de firma obsoleto o incorrecto)" fr: "(Signature malformee ou obsolete)" muttde: "(Unterschriftsformat fehlerhaft oder veraltet)" "(Unknown public-key algorithm)" de: "(unbek. Algorithmus des ffentl. Schlssels)" es: "(Algoritmo desconocido de clave pblica)" fr: "(Algorithme de clef publique inconnu)" muttde: "(unbek. Algorithmus des ffentl. Schlssels)" "(Unknown hash algorithm)" de: "(unbekannter Prfsummen-Algorithmus)" es: "(Algoritmo desconocido de distribucin [hash])" fr: "(Algorithme de hash inconnu)" muttde: "(unbekannter Prfsummen-Algorithmus)" "(Unknown signature packet version)" de: "(unbekannte Version des Unterschriftsblocks)" es: "(Versin desconocida de firma)" fr: "Version de paquet de signature inconnue" muttde: "(unbekannte Version des Unterschriftsblocks)" "(Malformed signature)" de: "(fehlerhafte Unterschrift)" es: "(Firma mal formada)" fr: "(Signature deforme)" muttde: "(fehlerhafte Unterschrift)" "(Corrupted signature packet)" de: "(beschdigter Unterschriftsblock)" es: "(Firma daada)" fr: "(Signature corrompue)" muttde: "(beschdigter Unterschriftsblock)" "\007**** BAD SIGNATURE! ****" de: "\007**** FALSCHE UNTERSCHRIFT! ****" es: "\007**** FIRMA INCORRECTA ****" fr: "\007**** MAUVAISE SIGNATURE! ****" mutt: "**** BAD SIGNATURE! ****" muttde: "**** FALSCHE UNTERSCHRIFT! ****" "Remove bad signatures (Y/n)? " de: "Falsche Unterschriften lschen? (J/n) " es: "Suprimir las firmas incorrectas (S/n)? " fr: "Supprimer les mauvaises signatures (O/n)? " muttde: "Falsche Unterschriften lschen? (J/n) " "\nRemoving signatures from userid '%s' in key ring '%s'\n" de: "\nLschen der Unterschriften unter ID \"%s\"\naus dem Schlsselbund '%s'.\n" es: "\nSuprimiendo las firmas del usuario '%s' del\n\ anillo de claves '%s'\n" fr: "\nSuppression des signatures de l'utilisateur '%s'\n\ dans le fichier de cls '%s'\n" muttde: "\nLschen der Unterschriften unter ID \"%s\"\naus dem Schlsselbund '%s'.\n" "\n\007Key not found in key ring '%s'.\n" de: "\n\007Der Schlssel ist nicht im Schlsselbund\n'%s' enthalten.\n" es: "\n\007No se ha encontrado la clave en el anillo '%s'.\n" fr: "\n\007Cl introuvable dans le fichier de cls '%s'.\n" mutt: "\nKey not found in key ring '%s'.\n" muttde: "\nDer Schlssel ist nicht im Schlsselbund\n'%s' enthalten.\n" "\nKey has no signatures to remove.\n" de: "\nDer Schlssel trgt keine Unterschriften, die gelscht werden knnten.\n" es: "\nLa clave no tiene ninguna firma por borrar.\n" fr: "\nLa cl n'a pas de signatures supprimer.\n" muttde: "\nDer Schlssel trgt keine Unterschriften, die gelscht werden knnten.\n" "\nKey has %d signature(s):\n" de: "\nDer Schlssel trgt %d Unterschrift(en):\n" es: "\nLa clave tiene %d firma(s):\n" fr: "\nLa cl a %d signature(s):\n" muttde: "\nDer Schlssel trgt %d Unterschrift(en):\n" "(Unknown signator, can't be checked)\n" de: "(Unterschreibender unbekannt, keine Prfung)\n" es: "(Firmante desconocido, no puede comprobarse)\n" fr: "(Signataire inconnu, ne peut tre vrifi)\n" muttde: "(Unterschreibender unbekannt, keine Prfung)\n" "Remove this signature (y/N)? " de: "Diese Unterschrift lschen? (j/N) " es: "\277Suprimir esta firma (s/N)? " fr: "Suppression de cette signature (o/N)? " muttde: "Diese Unterschrift lschen? (j/N) " "\nNo key signatures removed.\n" de: "\nKeine Unterschriften gelscht.\n" es: "\nNo se ha suprimido ninguna firma.\n" fr: "\nPas de supression de signature de cl.\n" muttde: "\nKeine Unterschriften gelscht.\n" "\n%d key signature(s) removed.\n" de: "\n%d Unterschrift(en) gelscht.\n" es: "\nSuprimidas %d firma(s) de clave.\n" fr: "\n%d signature(s) de cl supprime(s).\n" muttde: "\n%d Unterschrift(en) gelscht.\n" "\nRemoving from key ring: '%s'" de: "\nLschen aus Schlsselbund '%s'\n" es: "\nSuprimiendo del anillo: '%s'" fr: "\nSuppression du ficher de cls: '%s'" muttde: "\nLschen aus Schlsselbund '%s'\n" ", userid \"%s\".\n" de: "Benutzer-ID \"%s\".\n" es: ", identificador \"%s\".\n" fr: ", utilisateur \"%s\".\n" muttde: "Benutzer-ID \"%s\".\n" "\nKey has more than one user ID.\n\ Do you want to remove the whole key (y/N)? " de: "\nDer Schlssel hat mehr als eine Benutzer-ID.\n\ Soll der ganze Schlssel vollstndig gelscht werden? (j/N) " es: "\nLa clave tiene ms de un identificador de usuario.\n\ Quieres suprimirla por completo (s/N)? " fr: "\nLa cl a plus d'un nom d'utilisateur.\n\ Voulez vous supprimier toute la cl (o/N)? " muttde: "\nDer Schlssel hat mehr als eine Benutzer-ID.\n\ Soll der ganze Schlssel vollstndig gelscht werden? (j/N) " "\nNo more user ID's\n" de: "\nKeine weiteren Benutzer-IDs.\n" es: "\nNo hay ms identificadores de usuario\n" fr: "\nPlus de noms d'utilisateur\n" muttde: "\nKeine weiteren Benutzer-IDs.\n" "Remove \"%s\" (y/N)? " de: "\"%s\" lschen? (j/N) " es: "Suprimir \"%s\" (s/N)? " fr: "Supprimer \"%s\" (o/N)? " muttde: "\"%s\" lschen? (j/N) " "\nAre you sure you want this key removed (y/N)? " de: "\nBist Du sicher, da Du diesen Schlssel lschen willst? (j/N) " es: "\nEsts seguro de querer suprimir esta clave (s/N)? " fr: "\nEtes vous sr(e) de vouloir supprimer cette cl (o/N)? " muttde: "\nBist Du sicher, da Du diesen Schlssel lschen willst? (j/N) " "\nUser ID removed from key ring.\n" de: "\nDie Benutzer-ID wurde aus dem Schlsselbund gelscht.\n" es: "\nIdentificador suprimido del anillo.\n" fr: "\nNom d'utilisateur supprim du fichier de cls.\n" muttde: "\nDie Benutzer-ID wurde aus dem Schlsselbund gelscht.\n" "\nKey removed from key ring.\n" de: "\nDer Schlssel wurde aus dem Schlsselbund gelscht.\n" es: "\nClave suprimida del anillo.\n" fr: "\nCl supprime du fichier de cls.\n" muttde: "\nDer Schlssel wurde aus dem Schlsselbund gelscht.\n" "\nKey or user ID is also present in secret keyring.\n\ Do you also want to remove it from the secret keyring (y/N)? " de: "\nDer Schlssel oder die Benutzer-ID sind auch im privaten Schlsselbund\n\ enthalten. Sollen sie dort ebenfalls gelscht werden? (j/N) " es: "\nEl identificador se encuentra adems en el anillo de\n\ claves secretas. Quieres borrarlo tambin de ah (s/N)? " fr: "\nLa cl ou le nom d'utilisateur est aussi dans le fichier de cls\n\ secrtes. Voulez vous l'enlever du ficher de cls secrtes (o/N)? " muttde: "\nDer Schlssel oder die Benutzer-ID sind auch im privaten Schlsselbund\n\ enthalten. Sollen sie dort ebenfalls gelscht werden? (j/N) " "\nExtracting from key ring: '%s'" de: "\nExtrahieren aus dem Schlsselbund: '%s'\n" es: "\nExtrayendo del anillo de claves: '%s'" fr: "\nExtraction du fichier de cls: '%s'" muttde: "\nExtrahieren aus dem Schlsselbund: '%s'\n" "Extract the above key into which file?" de: "Dateiname dieses extrahierten Schlssels?" es: "En qu fichero se extrae la clave anterior?" fr: "Extraire la clef suivant de quel fichier?" muttde: "Dateiname dieses extrahierten Schlssels?" "Key ID %s is already included in key ring '%s'.\n" de: "Die Schlssel-ID %s ist bereits im Schlsselbund\n'%s' enthalten.\n" es: "La clave %s ya est en el anillo '%s'.\n" fr: "L'identificateur de cl %s est dj prsent\ \ndans le fichier de cls '%s'.\n" muttde: "Die Schlssel-ID %s ist bereits im Schlsselbund\n'%s' enthalten.\n" "\nKey extracted to file '%s'.\n" de: "\nSchlssel extrahiert in Datei '%s'.\n" es: "\nClave extrada en el fichero '%s'.\n" fr: "\nCl mise dans le fichier '%s'.\n" muttde: "\nSchlssel extrahiert in Datei '%s'.\n" "\nThis operation may not be performed on a secret keyring.\n\ Defaulting to public keyring." de: "\nDiese Operation kann mit einem privaten Schlsselbund nicht ausgefhrt\n\ werden. Der ffentliche Schlsselbund wird versucht." es: "\nEsta operacion no puede realizarse sobre el anillo de \ claves secretas.\nSe pasa al anillo de claves pblicas." fr: "\nCette operation ne peut pas tre effectue sur un fichier de cls\n\ secrtes. Le fichier de cls publiques sera utilis la place." muttde: "\nDiese Operation kann mit einem privaten Schlsselbund nicht ausgefhrt\n\ werden. Der ffentliche Schlsselbund wird versucht." "\nEditing userid \"%s\" in key ring: '%s'.\n" de: "\nBearbeitung der Benutzer-ID \"%s\"\nim Schlsselbund '%s'.\n" es: "\nModificacin del identificador \"%s\"\n\ en el anillo: '%s'.\n" fr: "\nModification du nom d'utilsateur \"%s\"\n\ dans le fichier de cl: '%s'.\n" muttde: "\nBearbeitung der Benutzer-ID \"%s\"\nim Schlsselbund '%s'.\n" "\nCan't open public key ring file '%s'\n" de: "\nFEHLER beim ffnen des ffentlichen Schlsselbunds '%s'.\n" es: "\nNo puede abrirse el anillo de claves pblicas '%s'\n" fr: "\nOuverture du fichier de cls publiques '%s' impossible.\n" muttde: "\nFEHLER beim ffnen des ffentlichen Schlsselbunds '%s'.\n" "\n\007File '%s' is not a public keyring.\n" de: "\n\007Die Datei '%s' ist kein ffentlicher Schlsselbund.\n" es: "\n\007El fichero '%s' no es un anillo de claves pblicas.\n" fr: "\n\007Le fichier '%s' n'est pas un fichier de cls publiques.\n" mutt: "\nFile '%s' is not a public keyring.\n" muttde: "\nDie Datei '%s' ist kein ffentlicher Schlsselbund.\n" "\n\007This key has been revoked by its owner.\n" de: "\n\007Dieser Schlssel wurde von seinem Besitzer zurckgezogen.\n" es: "\n\007Esta clave ha sido revocada por su propietario.\n" fr: "\n\007Cette cl a t rvoque par son propritaire.\n" mutt: "\nThis key has been revoked by its owner.\n" muttde: "\nDieser Schlssel wurde von seinem Besitzer zurckgezogen.\n" "\nNo secret key available. Editing public key trust parameter.\n" de: "\nKein privater Schlssel vorhanden. Die 'Vertrauens-Einstellungen' des\n\ ffentlichen Schlssels werden bearbeitet.\n" es: "\nNo hay clave secreta disponible. Modificando el parmetro \ de confianza\n\ de la clave pblica.\n" fr: "\nPas de cl secrte disponible. Modification du paramtre de\n\ confiance de la cl publique.\n" muttde: "\nKein privater Schlssel vorhanden. Die 'Vertrauens-Einstellungen' des\n\ ffentlichen Schlssels werden bearbeitet.\n" "Current trust for this key's owner is: %s\n" de: "\nAktuelles 'Vertrauen' zum Besitzer dieses Schlssels: %s\n" es: "La confianza actual en el propietario de esta clave es: %s\n" fr: "Le niveau de confiance courant pour le propritaire de\n\ cette cl est: %s\n" muttde: "\nAktuelles 'Vertrauen' zum Besitzer dieses Schlssels: %s\n" "Public key ring updated.\n" de: "\nDer ffentliche Schlsselbund wurde aktualisiert.\n" es: "Actualizado el anillo de claves pblicas.\n" fr: "Fichier de cls publiques modifi...\n" muttde: "\nDer ffentliche Schlsselbund wurde aktualisiert.\n" "\nCurrent user ID: %s" de: "\nAktuelle Benutzer-ID: %s" es: "\nIdentificador actual de usuario: %s" fr: "\nNom d'utilisateur courant: %s" muttde: "\nAktuelle Benutzer-ID: %s" "\nDo you want to add a new user ID (y/N)? " de: "\nWillst Du eine neue Benutzer-ID hinzufgen? (j/N) " es: "\nQuieres aadir un nuevo identificador de usuario (s/N)? " fr: "\nVoulez-vous ajouter ce nouvel ID (o/N)?" muttde: "\nWillst Du eine neue Benutzer-ID hinzufgen? (j/N) " "\nEnter the new user ID: " de: "\nGib die neue Benutzer-ID ein: " es: "\nIntroduzca el nuevo identificador: " fr: "\nEntrez le nouveau nom d'utilisateur: " muttde: "\nGib die neue Benutzer-ID ein: " "\nMake this user ID the primary user ID for this key (y/N)? " de: "\nSoll dies die vorrangige Benutzer-ID fr diesen Schlssel werden? (j/N) " es: "\nSe establece este identificador como primario para esta clave (s/N)? " fr: "\nEtablir ce nom d'utilisateur comme nom principal pour cette cl \ (o/N)? " muttde: "\nSoll dies die vorrangige Benutzer-ID fr diesen Schlssel werden? (j/N) " "\nDo you want to change your pass phrase (y/N)? " de: "\nWillst Du Dein Mantra ndern? (j/N) " es: "\nQuieres cambiar la contrasea (s/N)? " fr: "\nVoulez vous changer votre mot de passe (o/N)? " muttde: "\nWillst Du Dein Mantra ndern? (j/N) " "(No changes will be made.)\n" de: "\n(Es werden keine nderungen vorgenommen.)\n" es: "(No se efectuar ningn cambio.)\n" fr: "(Aucun changement ne sera effectu.)\n" muttde: "\n(Es werden keine nderungen vorgenommen.)\n" "\n\007Unable to update secret key ring.\n" de: "\n\007FEHLER beim Aktualisieren des privaten Schlsselbunds.\n" es: "\n\007No puede actualizarse el anillo de claves secretas.\n" fr: "\n\007Impossible de modifier le fichier de cls secrtes.\n" mutt: "\nUnable to update secret key ring.\n" muttde: "\nFEHLER beim Aktualisieren des privaten Schlsselbunds.\n" "\nSecret key ring updated...\n" de: "\n\nDer private Schlsselbund wurde aktualisiert.\n" es: "\nActualizado el anillo de claves secretas ...\n" fr: "\nFichier de cls secrtes modifi...\n" muttde: "\n\nDer private Schlsselbund wurde aktualisiert.\n" "\n\007Unable to update public key ring.\n" de: "\n\007FEHLER beim Aktualisieren des ffentlichen Schlsselbunds.\n" es: "\n\007No puede actualizarse el anillo de claves pblicas.\n" fr: "\n\007Impossible de modifier le fichier de cls publiques.\n" mutt: "\nUnable to update public key ring.\n" muttde: "\nFEHLER beim Aktualisieren des ffentlichen Schlsselbunds.\n" "(No need to update public key ring)\n" de: "\n(Keine nderung am ffentlichen Schlsselbund notwendig.)\n" es: "(No es necesario actualizar el anillo de claves pblicas)\n" fr: "(Pas besoin de modifier le fichier de cls publiques)\n" muttde: "\n(Keine nderung am ffentlichen Schlsselbund notwendig.)\n" "\nDo you want to permanently revoke your public key\n\ by issuing a secret key compromise certificate\n\ for \"%s\" (y/N)? " de: "\nWillst Du Deinen ffentlichen Schlssel wirklich durch das Versenden\n\ einer Widerrufs-Urkunde fr \"%s\"\nzurckziehen, d.h. fr ungltig erklren? (j/N) " es: "\nQuieres revocar permanentemente tu clave pblica\n\ emitiendo un certificado de compromiso de clave secreta\n\ para \"%s\" (s/N)? " fr: "\nVoulez vous rvoquer de faon permanente votre cl publique\n\ en mettant un certificat de compromission de cl secrte\n\ pour \"%s\" (o/N)? " muttde: "\nWillst Du Deinen ffentlichen Schlssel wirklich durch das Versenden\n\ einer Widerrufs-Urkunde fr \"%s\"\nzurckziehen, d.h. fr ungltig erklren? (j/N) " "You can only disable keys on your public keyring.\n" de: "Du kannst nur Schlssel aus Deinem ffentlichen Schlsselbund sperren.\n" es: "Slo puedes desactivar claves en el anillo de claves pblicas.\n" fr: "Vous ne pouvez inactiver des cls que sur votre fichier de cls\ \npubliques.\n" muttde: "Du kannst nur Schlssel aus Deinem ffentlichen Schlsselbund sperren.\n" "\nKey is already disabled.\n\ Do you want to enable this key again (y/N)? " de: "\nDieser Schlssel ist schon gesperrt.\nMchtest Du ihn wieder freigeben? (j/N) " es: "\nLa clave ya est desactivada.\n\ Quieres activarla otra vez (s/N)? " fr: "\nLa cl est dj inactive.\n\ Voulez vous ractiver cette cl (o/N)? " muttde: "\nDieser Schlssel ist schon gesperrt.\nMchtest Du ihn wieder freigeben? (j/N) " "\nDisable this key (y/N)? " de: "\nSoll dieser Schlssel gesperrt werden? (j/N) " es: "\nDesactivar esta clave (s/N)? " fr: "Dsactiver cette cl (o/N)? " muttde: "\nSoll dieser Schlssel gesperrt werden? (j/N) " "Pick your RSA key size:\n\ 1) 1024 bits- User grade, fast but less secure\n\ 2) 1535 bits- Regional CA grade, medium speed, good security\n\ 3) 2048 bits- Root CA grade, slow, high security\n\ Choose 1, 2, or 3, or enter desired number of bits (384...8192): " de: "Whle die Lnge Deines RSA-Schlssels aus:\ \n 1) 1024 Bits: fr Nutzer: schnell, aber nicht ganz so sicher\ \n 2) 1535 Bits: fr regionale CAs: mittelmig schnell, recht sicher\ \n 3) 2048 Bits: fr Root CAs: langsam, jedoch sehr sicher\ \nAuswahl (1, 2, 3 oder die Lnge des Schlssels in Bits (384...8192)): " es: "Elija un tamao de clave RSA:\n\ 1) 1024 bits- Nivel comercial bajo, r\0341pido pero menos seguro\n\ 2) 1535 bits- Nivel comercial alto, velocidad media con buena seguridad\n\ 3) 2048 bits- Nivel \"militar\", lento con alta seguridad\n\ Escoge 1, 2, 3, o el nmero requerido de bits (384...8192): " fr: "Choisissez la taille de votre clef RSA:\n\ 1) 1024 bits- Niveau de base, rapide mais moins securitaire\n\ 2) 1535 bits- Niveau de securit eleve - vitesse moyenne \n\ 3) 2048 bits- Pour les militaires, les diplomates... les paranoiaques \n\ Choisissez 1, 2, ou 3, ou entrez le nombre de bits desires (384...8192): " muttde: "Whle die Lnge Deines RSA-Schlssels aus:\ \n 1) 1024 Bits: fr Nutzer: schnell, aber nicht ganz so sicher\ \n 2) 1535 Bits: fr regionale CAs: mittelmig schnell, recht sicher\ \n 3) 2048 Bits: fr Root CAs: langsam, jedoch sehr sicher\ \nAuswahl (1, 2, 3 oder die Lnge des Schlssels in Bits (384...8192)): " "Generating an RSA key with a %d-bit modulus.\n" de: "Erzeugung eines RSA-Schlssels mit einem %d-Bit-Modulus.\n" es: "Generando una clave RSA con mdulo de %d bits.\n" fr: "Generation d'une cl RSA avec un module de %d bits.\n" muttde: "Erzeugung eines RSA-Schlssels mit einem %d-Bit-Modulus.\n" "\nYou need a user ID for your public key. The desired form for this\n\ user ID is your name, followed by your E-mail address enclosed in\n\ , if you have an E-mail address.\n\ Form: Real Name (comment) (options)\n\ Optional options: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\ Enter a user ID for your public key: \n" de: "\nDu brauchst eine Benutzer-ID fr Deinen ffentlichen Schlssel. Das bliche\n\ Format fr diese Benutzer-ID ist Dein Realname, gefolgt von Deinem Usernamen\n\ in , falls Du per E-Mail erreichbar bist.\n\ Format: Brgerlicher Name (Kommentar) (Optionen)\n\ Freiwillige Optionen: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\ Gib die Benutzer-ID fr Deinen ffentlichen Schlssel ein:\n" es: "\nNecesitas un identificador para tu clave pblica. El formato preferido\n\ consiste en tu nombre, seguido de tu direccin de correo electrnico,\n\ si tienes, entre .\n\ Form: Real Name (comment) (options)\n\ Optional options: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\ Introduce un identificador de usuario para tu clave pblica: \n" fr: "\nIl vous faut un nom d'utilisateur pour votre cl publique. La forme\n\ dsire pour ce nom d'utilisateur est votre nom, suivi de votre addresse\n\ de courrier lectronique entre , si vous en avez une.\n\ Form: Real Name (comment) (options)\n\ Optional options: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\ Entrez un nom d'utilisateur pour votre cl publique\n\ (votre nom): " muttde: "\nDu brauchst eine Benutzer-ID fr Deinen ffentlichen Schlssel. Das bliche\n\ Format fr diese Benutzer-ID ist Dein Realname, gefolgt von Deinem Usernamen\n\ in , falls Du per E-Mail erreichbar bist.\n\ Format: Brgerlicher Name (Kommentar) (Optionen)\n\ Freiwillige Optionen: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\ Gib die Benutzer-ID fr Deinen ffentlichen Schlssel ein:\n" "Generating RSA key-pair with UserID \"%s\".\n" de: "Erzeugung eines RSA-Schlsselpaares mit der\nBenutzer-ID \"%s\".\n" es: "Generando el par de claves RSA con identificador \"%s\".\n" fr: "Gnration d'une paire de clefs RSA de l'utilisateur '%s'.\n" muttde: "Erzeugung eines RSA-Schlsselpaares mit der\nBenutzer-ID \"%s\".\n" "\nYou need a pass phrase to protect your RSA secret key.\n\ Your pass phrase can be any sentence or phrase and may have many\n\ words, spaces, punctuation, or any other printable characters.\n" de: "\nDu brauchst ein Mantra, um Deinen privaten RSA-Schlssel zu schtzen.\n\ Dein Mantra kann jeder beliebige Satz oder Zeichenfolge sein und darf aus\n\ vielen Worten, Leerzeichen oder anderen druckbaren Zeichen bestehen.\n" es: "\nNecesitas una contrasea para proteger tu clave secreta RSA.\n\ Puede ser cualquier expresin formada por varias palabras, espacios,\n\ signos de puntuacin o cualquier otro carcter imprimible.\n" fr: "\nVous devez avoir un mot de passe pour protger votre cl RSA \n\ secrte. Votre mot de passe peut tre n'importe quelle phrase ou portion\n\ de phrase et peut avoir plusieurs mots, espaces, caractres de ponctuation\n\ ou tout autre caractre imprimable.\n" muttde: "\nDu brauchst ein Mantra, um Deinen privaten RSA-Schlssel zu schtzen.\n\ Dein Mantra kann jeder beliebige Satz oder Zeichenfolge sein und darf aus\n\ vielen Worten, Leerzeichen oder anderen druckbaren Zeichen bestehen.\n" "\nNote that key generation is a lengthy process.\n" de: "\n\nBeachte, da die Schlsselerzeugung eine zeitaufwendige Sache ist.\n" es: "\nTen en cuenta que la generacin de claves es un proceso lento.\n" fr: "\nNotez que la gnration de cl est une procdure lente.\n" muttde: "\n\nBeachte, da die Schlsselerzeugung eine zeitaufwendige Sache ist.\n" "Key generation stopped at user request.\n" de: "Die Schlsselerzeugung wurde durch den Benutzer abgebrochen!\n" es: "Generacin de claves interrumpida a peticin del usuario.\n" fr: "Le gnration de la clef a t stoppe la demande de l'utilisateur.\n" muttde: "Die Schlsselerzeugung wurde durch den Benutzer abgebrochen!\n" "\n\007Keygen failed!\n" de: "\n\007FEHLER bei der Schlssel-Erzeugung!\n" es: "\n\007Error en la generacin de claves\n" fr: "\n\007Generation de cl non russie!\n" mutt: "\nKeygen failed!\n" muttde: "\nFEHLER bei der Schlssel-Erzeugung!\n" "Key ID %s\n" de: "Schlssel-ID: %s\n" es: "Identificador de clave %s\n" fr: "Identificateur de cl %s\n" muttde: "Schlssel-ID: %s\n" "Display secret components (y/N)?" de: "Geheime Bestandteile anzeigen? (j/N) " es: "Mostrar los componentes secretos (s/N)?" fr: "Affichier les composantes secretes (o/N)?" muttde: "Geheime Bestandteile anzeigen? (j/N) " "\007Key generation completed.\n" de: "\n\007Die Erzeugung des Schlssels ist beendet.\n" es: "\007Finalizada la generacin de claves.\n" fr: "\007Gnration de cl termine.\n" mutt: "Key generation completed.\n" muttde: "\nDie Erzeugung des Schlssels ist beendet.\n" "Type Bits/KeyID Date User ID\n" de: "Typ Bits/KeyID Datum NutzerID\n" es: "Tipo Bits/Clave Fecha Identificador\n" fr: "Type Bits/Clef Date ID utilisateur\n" muttde: "Typ Bits/KeyID Datum NutzerID\n" "\n\007File '%s' is not a text file; cannot display.\n" de: "\n\007Die Datei '%s' ist keine Textdatei\n\ und kann deshalb nicht angezeigt werden!\n" es: "\n\007El fichero '%s' no es de texto; no puede mostrarse.\n" fr: "\n\007Le fichier '%s' n'est pas un fichier de texte et ne peut tre \ visualis\n" mutt: "\nFile '%s' is not a text file; cannot display.\n" muttde: "\nDie Datei '%s' ist keine Textdatei\n\ und kann deshalb nicht angezeigt werden!\n" "\nDone...hit any key\r" de: "\nFertig... Bitte drcke eine Taste.\r" es: "\nFinalizado...pulse cualquier tecla\r" fr: "\nTermin... appuyez sur n'importe quelle touche \r" muttde: "\nFertig... Bitte drcke eine Taste.\r" "-- More -- Space: next screen, Enter: next line\ , 'B': back, 'Q': quit --\r" de: "-- Weiter -- Vorwrts: Leertaste oder Return; Rckwrts: 'B'; Ende: 'Q' --\r" es: "-- ms -- espacio: otra pantalla, enter: otra lnea,\ B: atrs, Q: salir\r" fr: "- Plus - Espace: procahin cran , Chariot: prochaine ligne\ , 'B': retour, 'Q': quitter - \r" muttde: "-- Weiter -- Vorwrts: Leertaste oder Return; Rckwrts: 'B'; Ende: 'Q' --\r" "More -- %d%% -- Space: next screen, Enter: next line\ , 'B': back, 'Q': quit --\r" de: "Weiter -- %d%% -- Vorwrts: Leertaste oder Return; Rckwrts: 'B'; Ende: 'Q' --\r" es: "Ms -- %d%% -- espacio: otra pantalla, enter: otra lnea,\ B: atrs, Q: salir\r" fr: "- Plus - %D%% - Espace: procahin cran , Chariot: prochaine ligne\ , 'B': retour, 'Q': quitter - \r" muttde: "Weiter -- %d%% -- Vorwrts: Leertaste oder Return; Rckwrts: 'B'; Ende: 'Q' --\r" "\nEnter pass phrase: " de: "\nGib das Mantra ein: " es: "\nIntroduce la contrasea: " fr: "\nEntrez votre mot de passe: " muttde: "\nGib das Mantra ein: " "\nEnter same pass phrase again: " de: "\nWiederhole das Mantra: " es: "\nEscrbela otra vez: " fr: "\nEntrez le mme mot de passe de nouveau: " muttde: "\nWiederhole das Mantra: " "\n\007Error: Pass phrases were different. Try again." de: "\n\007FEHLER: Die beiden Eingaben waren unterschiedlich! Bitte noch einmal." es: "\n\007Error: Las contraseas son diferentes. Prueba otra vez." fr: "\n\007Erreur: Les mots de passe taient diffrents. Essayez encore." mutt: "\nError: Pass phrases were different. Try again." muttde: "\nFEHLER: Die beiden Eingaben waren unterschiedlich! Bitte noch einmal." "\nStopped at user request\n" de: "\nAbbruch durch Benutzer\n" es: "\nInterrupcin por peticin del usuario\n" fr: "\nArrt par demande de l'utilisateur\n" muttde: "\nAbbruch durch Benutzer\n" "Pretty Good Privacy(tm) %s - Public-key encryption for the masses.\n" de: "Pretty Good Privacy(tm) %s - Public-key-Verschlsselung fr die Massen.\n" es: "Pretty Good Privacy(tm) %s - Criptografa de clave pblica para todos.\n" fr: "Pretty Good Privacy(tm) %s - Cryptographie cl publique pour tous.\n" muttde: "Pretty Good Privacy(tm) %s - Public-key-Verschlsselung fr die Massen.\n" "(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software." de: "(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software." es: "(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software." fr: "( c ) 1990-96 Philip Zimmermann, Phil's Pretty Good Software. " muttde: "(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software." "Export of this software may be restricted by the U.S. government.\n" de: "Der Export dieser Software aus den USA kann Beschrnkungen unterliegen.\n" es: "La exportacin de este programa puede estar restringida por\n\ el gobierno de los EE.UU." fr: "L'exportation de ce logiciel peut tre restreint par le \ gouvernement des tats-Unis" muttde: "Der Export dieser Software aus den USA kann Beschrnkungen unterliegen.\n" "International version - not for use in the USA. Does not use RSAREF.\n" de: "Internationale Version - nicht in den USA verwenden! Benutzt nicht RSAREF.\n" es: "Versin internacional - no apta para los EE.UU. No utiliza RSAREF.\n" fr: "Version internationale - ne pas utiliser aux Etats-Unis. N'utilise pas le RSAREF.\n" muttde: "Internationale Version - nicht in den USA verwenden! Benutzt nicht RSAREF.\n" "Current time: %s\n" de: "Aktuelles Datum und Uhrzeit: %s\n" es: "Hora actual: %s\n" fr: "Heure actuelle: %s\n" muttde: "Aktuelles Datum und Uhrzeit: %s\n" "\007No configuration file found.\n" de: "\007Keine Konfigurationsdatei gefunden!\n" es: "\007No se encuentra el fichero de configuracin.\n" fr: "\007Fichier de configuration introuvable.\n" mutt: "No configuration file found.\n" muttde: "Keine Konfigurationsdatei gefunden!\n" "\007WARNING: Environmental variable TZ is not \ defined, so GMT timestamps\n\ may be wrong. See the PGP User's Guide to properly define TZ\n\ in AUTOEXEC.BAT file.\n" de: "\007WARNUNG: Die Umgebungsvariable TZ ist nicht definiert, daher knnten\n\ die GMT-Zeitangaben falsch sein. Beachte den Abschnitt in der PGP-Anleitung\n\ ber das richtige Setzen von TZ in AUTOEXEC.BAT.\n\n" es: "\007ADVERTENCIA: La variable TZ no est definida, por lo que\n\ los sellos de fecha GMT pueden estar equivocados. Consulta la Gua del\n\ usuario de PGP para definir adecuadamente TZ en AUTOEXEC.BAT.\n" fr: "\007ATTENTION: La variable d'environnement TZ n'est pas dfinie, les\n\ temps GMT peuvent donc tres fausss. Voir le guide de l'utilisateur PGP pour\n\ dfinir correctement TZ dans le fichier AUTOEXEC.BAT.\n" mutt: "WARNING: Environmental variable TZ is not \ defined, so GMT timestamps\n\ may be wrong. See the PGP User's Guide to properly define TZ\n\ in AUTOEXEC.BAT file.\n" muttde: "WARNUNG: Die Umgebungsvariable TZ ist nicht definiert, daher knnten\n\ die GMT-Zeitangaben falsch sein. Beachte den Abschnitt in der PGP-Anleitung\n\ ber das richtige Setzen von TZ in AUTOEXEC.BAT.\n\n" "\nFile %s wiped and deleted. " de: "\nDie Datei '%s' wurde berschrieben und gelscht." es: "\nEl fichero %s ha sido borrado y destruido. " fr: "\nFichier %s effac et dtruit. " muttde: "\nDie Datei '%s' wurde berschrieben und gelscht." "\n\007Error: Can't wipe out file '%s' - read only, maybe?\n" de: "\n\007FEHLER: Die Datei '%s' kann nicht berschrieben werden.\n\ Ist sie vielleicht schreibgeschtzt?\n" es: "\n\007Error: No puede eliminarse el fichero '%s' - \ quiz sea slo de lectura\n" fr: "\n\007Erreur: Incapable de dtruire le fichier '%s' - lectu4re seulement peut tre?\n" mutt: "\nError: Can't wipe out file '%s' - read only, maybe?\n" muttde: "\nFEHLER: Die Datei '%s' kann nicht berschrieben werden.\n\ Ist sie vielleicht schreibgeschtzt?\n" "\n\007File '%s' does not exist.\n" de: "\n\007Die Datei '%s' existiert nicht!\n" es: "\n\007El fichero '%s' no existe.\n" fr: "Le fichier '%s' n'existe pas. \n" mutt: "\nFile '%s' does not exist.\n" muttde: "\nDie Datei '%s' existiert nicht!\n" "\nFor details on licensing and distribution, see the PGP User's Guide.\ \nFor other cryptography products and custom development services, contact:\ \nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, \ phone +1 303 541-0140\n" de: "\nInformationen ber Lizenzen und Verteilung finden sich in der PGP-Anleitung.\ \nInformationen ber Verschlsselungs-Produkte und Auftrags-Entwicklungen:\ \nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, Tel. +1-(303)-541-0140\n" es: "\nInformacin sobre licencia y distribucin en la Gua del usuario de PGP.\ \nMs informacin sobre otros productos y servicios criptogrficos a medida:\ \nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, tel +1 303 541-0140\n" fr: "\nConsulter le guide de l'utilisateur de PGP pour les dtails de\n\ license et de distribution. Pour d'autres produits de cryptographie\n\ et services de dveloppement personaliss, contacter: Philip Zimmermann,\n\ 3021 11th St, Boulder CO 80304 USA, tlphone +1 303 541-0140\n" muttde: "\nInformationen ber Lizenzen und Verteilung finden sich in der PGP-Anleitung.\ \nInformationen ber Verschlsselungs-Produkte und Auftrags-Entwicklungen:\ \nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, Tel. +1-(303)-541-0140\n" "@translator@" de: "\nbersetzer:\ \n Frank Pruefer ; Stand: 07.10.1997\ \n (basierend auf der deutschen bersetzung der LANGUAGE.TXT von\ \n Marc Aurel <4-tea-2@bong.saar.de> vom 19.01.1994)\n" es: "\nTraducido al castellano por Armando Ramos .\n" fr: "\nTraduction franaise de Jean-loup Gailly et Yanik \ Crpeau \n" muttde: "\nbersetzer:\ \n Frank Pruefer ; Stand: 07.10.1997\ \n (basierend auf der deutschen bersetzung der LANGUAGE.TXT von\ \n Marc Aurel <4-tea-2@bong.saar.de> vom 19.01.1994)\n" "\nFor a usage summary, type: pgp -h\n" de: "\nEine bersicht der PGP-Befehle erhltst Du mit: pgp -h\n" es: "\nPara ver un resumen de las instrucciones, escribe: pgp -h\n" fr: "\nPour un sommaire d'utilisation, tapez: pgp -h\n" mutt: " " muttde: " " "File %s created containing %d random bytes.\n" de: "\nDie Datei '%s', die %d Bytes Zufallszahlen enthlt,\n\ wurde erzeugt.\n" es: "Generado el fichero %s con %d bytes aleatorios.\n" fr: "Le fichier %s est cr et contient %d octets alatoires" muttde: "\nDie Datei '%s', die %d Bytes Zufallszahlen enthlt,\n\ wurde erzeugt.\n" "\007Invalid filename: '%s' too long\n" de: "\007Ungltiger Dateiname: '%s' ist zu lang.\n" es: "\007Nombre incorrecto: '%s' es demasiado largo\n" fr: "\007Nom incalide: '%s' trop long\n" mutt: "Invalid filename: '%s' too long\n" muttde: "Ungltiger Dateiname: '%s' ist zu lang.\n" "\n\007Input file '%s' looks like it may have been created by PGP. " de: "\n\007Die Eingabedatei '%s' knnte von PGP erzeugt worden sein." es: "\n\007El fichero de entrada '%s' parece haber sido creado por PGP. " fr: "\n\007Le fichier d'entre '%s' semble avoir t cr par PGP. " mutt: "\nInput file '%s' looks like it may have been created by PGP. " muttde: "\nDie Eingabedatei '%s' knnte von PGP erzeugt worden sein." "\nIs it safe to assume that it was created by PGP (y/N)? " de: "\nWurde diese von PGP erzeugt? (j/N) " es: "\nPuede asumirse con seguridad que ha sido as (s/N)? " fr: "\nEtes vous sr qu'il a t cr par PGP (o/N)? " muttde: "\nWurde diese von PGP erzeugt? (j/N) " "\nNote: '%s' is not a pure text file.\n\ File will be treated as binary data.\n" de: "\nHinweis: '%s' ist keine reine Textdatei.\n\ Die Datei wird als Binrdatei behandelt.\n" es: "\nNota: '%s' no es un fichero de texto puro.\n\ Se tratar como datos binarios.\n" fr: "Note: '%s' n'est pas un fichier texte. \n\ Il sera trait comme donnes binaires" muttde: "\nHinweis: '%s' ist keine reine Textdatei.\n\ Die Datei wird als Binrdatei behandelt.\n" "\n\007Error: Only text files may be sent as display-only.\n" de: "\n\007FEHLER: Nur Textdateien knnen \"nur zur Ansicht\" verschickt werden.\n" es: "\n\007Error: Slo los ficheros de texto pueden enviarse para mostrar.\n" fr: "\n\007Erreur: seuls les fichiers de texte peuvent tre envoys\n\ pour affichage exclusivement.\n" mutt: "\nError: Only text files may be sent as display-only.\n" muttde: "\nFEHLER: Nur Textdateien knnen \"nur zur Ansicht\" verschickt werden.\n" "\n\007Error: MacBinary failed!\n" de: "\n\007FEHLER: MacBinary fehlgeschlagen!\n" es: "\n\007Error: ha fallado MacBinary\n" fr:"\n\007Erreur:MacBinary a chou\n" mutt: "\nError: MacBinary failed!\n" muttde: "\nFEHLER: MacBinary fehlgeschlagen!\n" "\nA secret key is required to make a signature. " de: "\nFr eine Unterschrift wird ein privater Schlssel bentigt." es: "\nSe necesita una clave secreta para generar la firma. " fr: "\nUne cl secrte est ncessaire pour faire une signature. " muttde: "\nFr eine Unterschrift wird ein privater Schlssel bentigt." "\nYou specified no user ID to select your secret key,\n\ so the default user ID and key will be the most recently\n\ added key on your secret keyring.\n" de: "\nDa Du keine Benutzer-ID fr Deinen privaten Schlssel angegeben hast,\n\ wird der letzte zum privaten Schlsselbund hinzugefgte Schlssel benutzt.\n" es: "\nNo has indicado ningn identificador para escoger la clave secreta,\n\ por lo que el identificador y la clave por omisin sern los ltimos\n\ aadidos al anillo.\n" fr: "\nVous n'avez pas spcifi de nom d'utilisateur pour slectionner\n\ votre cl secrte, donc le nom et la cl par dfaut seront ceux les\n\ plus rcemment ajouts votre fichier de cls secrtes.\n" muttde: "\nDa Du keine Benutzer-ID fr Deinen privaten Schlssel angegeben hast,\n\ wird der letzte zum privaten Schlsselbund hinzugefgte Schlssel benutzt.\n" "\007Signature error\n" de: "\n\007FEHLER beim Unterschreiben!\n" es: "\007Error de firma\n" fr: "\007Erreur de signature\n" mutt: "Signature error\n" muttde: "\nFEHLER beim Unterschreiben!\n" "\n\nRecipients' public key(s) will be used to encrypt. " de: "\n\nVerschlsselung mit Empfnger-Schlssel(n).\n" es: "\n\nSe utilizan las claves pblicas de los destinatarios para encriptar. " fr: "\n\nLa ou les cl(s) publique(s) du destinataire seront utilises\ pour chiffrer. " muttde: "\n\nVerschlsselung mit Empfnger-Schlssel(n).\n" "\nA user ID is required to select the recipient's public key. " de: "\nZur Auswahl des Empfnger-Schlssels wird eine Benutzer-ID bentigt." es: "\nSe necesita un identificador para encontrar la clave pblica\n\ del destinatario. " fr: "\nUn nom d'utilisateur est ncessaire pour slectionner la cl\n\ publique du destinataire. " muttde: "\nZur Auswahl des Empfnger-Schlssels wird eine Benutzer-ID bentigt." "\nEnter the recipient's user ID: " de: "\nBenutzer-ID des Empfngers: " es: "\nIntroduzca el identificador del destinatario: " fr: "\nEntrez le nom d'utilisateur du destinataire: " muttde: "\nBenutzer-ID des Empfngers: " "\007Encryption error\n" de: "\n\007FEHLER beim Verschlsseln!\n" es: "\007Error en la encriptacin\n" fr: "\007Erreur de chiffrage\n" mutt: "Encryption error\n" muttde: "\nFEHLER beim Verschlsseln!\n" "\nCiphertext file: %s\n" de: "\nVerschlsselte Datei: %s\n" es: "\nFichero cifrado: %s\n" fr: "\nFichier chiffr: %s\n" muttde: "\nVerschlsselte Datei: %s\n" "\nSignature file: %s\n" de: "\nUnterschriftsdatei: %s\n" es: "\nFichero de firma: %s\n" fr: "\nFichier de signature: %s\n" muttde: "\nUnterschriftsdatei: %s\n" "\n\007Error: Transport armor stripping failed for file %s\n" de: "\n\007FEHLER beim Entfernen der Versandhlle von Datei '%s'.\n" es: "\n\007Error: No se ha podido quitar la armadura de %s\n" fr: "\n\007Erreur dans la suppression de la protection de transport pour\n\ le fichier %s\n" mutt: "\nError: Transport armor stripping failed for file %s\n" muttde: "\nFEHLER beim Entfernen der Versandhlle von Datei '%s'.\n" "Stripped transport armor from '%s', producing '%s'.\n" de: "\nDie Versandhlle von Datei '%s' wurde entfernt.\nAusgabedatei: %s\n" es: "Quitada la armadura de '%s', produciendo '%s'.\n" fr: "Protection de transport supprime pour '%s', produisant '%s'.\n" muttde: "\nDie Versandhlle von Datei '%s' wurde entfernt.\nAusgabedatei: %s\n" "\nLooking for next packet in '%s'...\n" de: "\nSuche nach dem nchsten Paket in '%s'...\n" es: "\nBuscando el siguiente paquete en '%s'...\n" fr: "\nRecherche du prochain paquet dans '%s'...\n" muttde: "\nSuche nach dem nchsten Paket in '%s'...\n" "\nFile is encrypted. Secret key is required to read it. " de: "\nDie Datei ist verschlsselt. Zum Lesen wird der private Schlssel bentigt.\n" es: "\nEl fichero est encriptado. Para leerlo se necesita la clave secreta. " fr: "\nLe fichier est chiffr. La cl secrte est ncessaire pour le lire." muttde: "\nDie Datei ist verschlsselt. Zum Lesen wird der private Schlssel bentigt.\n" "\nThis file has a signature, which will be left in place.\n" de: "\nDiese Datei trgt eine Unterschrift, die nicht entfernt wird.\n" es: "\nEste fichero tiene firma, que se deja en su sitio.\n" fr: "\nCe fichier a une signature, qui sera garde.\n" muttde: "\nDiese Datei trgt eine Unterschrift, die nicht entfernt wird.\n" "\nFile has signature. Public key is required to check signature.\n" de: "\nDiese Datei trgt eine Unterschrift.\n\ Zur berprfung wird der ffentliche Schlssel bentigt.\n" es: "\nEl fichero tiene firma. Se necesita la clave pblica para comprobarla.\n" fr: "Ce fichier est sign. Une clef publique est ncessaire pour sa vrification.\n" muttde: "\nDiese Datei trgt eine Unterschrift.\n\ Zur berprfung wird der ffentliche Schlssel bentigt.\n" "\nFile is conventionally encrypted. " de: "\nDiese Datei ist konventionell verschlsselt.\n" es: "\nEl fichero ha sido encriptado convencionalmente. " fr: "\nLe fichier est chiffr de manire conventionnelle. " muttde: "\nDiese Datei ist konventionell verschlsselt.\n" "\nFile contains key(s). Contents follow..." de: "\nDiese Datei enthlt einen oder mehrere Schlssel. Hier kommt die Liste:\n" es: "\nEl fichero contiene claves. Se muestran a continuacin ..." fr: "\nLe fichier contient une ou plusieurs cls. Le contenu suit..." muttde: "\nDiese Datei enthlt einen oder mehrere Schlssel. Hier kommt die Liste:\n" "\nDo you want to add this keyfile to keyring '%s' (y/N)? " de: "\nWillst Du die Schlssel dieser Datei zum Schlsselbund\n'%s' hinzufgen? (j/N) " es: "\nQuieres aadir este fichero de claves al anillo '%s' (s/N)? " fr: "\nVoulez vous ajouter ce fichier de cl au fichier '%s' (o/N)? " muttde: "\nWillst Du die Schlssel dieser Datei zum Schlsselbund\n'%s' hinzufgen? (j/N) " "\007Keyring add error. " de: "\n\007FEHLER beim Hinzufgen zum Schlsselbund.\n" es: "\007Error al aadir en el anillo. " fr: "\007Erreur dans l'addition au fichier de cls. " mutt: "Keyring add error. " muttde: "\nFEHLER beim Hinzufgen zum Schlsselbund.\n" "\007\nError: '%s' is not a ciphertext, signature, or key file.\n" de: "\007\nFEHLER: Die Datei '%s' ist nicht verschlsselt\n\ und enthlt weder eine Unterschrift noch einen oder mehrere Schlssel.\n" es: "\007\nError: '%s' no es un texto cifrado, una firma ni una clave.\n" fr: "\007\nErreur: '%s' n'est pas un fichier chiffr, de signatures\ \nou de cls.\n" mutt: "\nError: '%s' is not a ciphertext, signature, or key file.\n" muttde: "\nFEHLER: Die Datei '%s' ist nicht verschlsselt\n\ und enthlt weder eine Unterschrift noch einen oder mehrere Schlssel.\n" "\n\nThis message is marked \"For your eyes only\". Display now \ (Y/n)? " de: "\n\nDiese Nachricht ist als VERTRAULICH markiert! Jetzt anzeigen? (J/n) " es: "\n\nEste mensaje est marcado como \"Slo para tus ojos\".\ Mostrar ahora (S/n)? " fr: "\n\nCe message est marqu \"Pour vos yeux seulement\".\n\ Afficher maintenant (O/n)? " muttde: "\n\nDiese Nachricht ist als VERTRAULICH markiert! Jetzt anzeigen? (J/n) " "\n\nPlaintext message follows...\n" de: "\n\nHier kommt die Nachricht im Klartext:\n" es: "\n\nMensaje en claro a continuacin...\n" fr: "\n\nLe message en clair suit...\n" muttde: "\n\nHier kommt die Nachricht im Klartext:\n" "Save this file permanently (y/N)? " de: "Klartext sichern? (j/N) " es: "Grabar este fichero de forma permanente (s/N)? " fr: "Sauvegarde de ce fichier de manire permanente (o/N)? " muttde: "Klartext sichern? (j/N) " "Enter filename to save file as: " de: "Klartext sichern als: " es: "Introduzca el nombre para el fichero: " fr: "Entrez le nom du fichier de sauvegarde: " muttde: "Klartext sichern als: " "Enter filename to save file as:" de: "Gib den Namen ein, unter dem die Datei zu sichern ist:" es: "Introduzca el nombre para el fichero:" fr: "Donner le nom de fichier pour sauvegarder sous:" muttde: "Gib den Namen ein, unter dem die Datei zu sichern ist:" "\nPlaintext filename: %s" de: "\nDateiname des Klartextes: %s" es: "\nNombre del fichero normal: %s" fr: "\nNom du fichier en clair: %s" muttde: "\nDateiname des Klartextes: %s" "\nPlaintext file '%s' looks like it contains a public key." de: "\nDie Klartextdatei '%s' scheint einen\nffentlichen Schlssel zu enthalten." es: "\nEl fichero normal '%s' parece contener una clave pblica." fr: "\nLe ficher en clair '%s' semble contenir une cl publique." muttde: "\nDie Klartextdatei '%s' scheint einen\nffentlichen Schlssel zu enthalten." "\nPlaintext file '%s' looks like a %s file." de: "\nDie Klartextdatei '%s' scheint eine %s-Datei zu sein." es: "\nEl fichero normal '%s' parece un fichero %s." fr: "\nLe fichier en clair '%s' semble tre un fichier %s." muttde: "\nDie Klartextdatei '%s' scheint eine %s-Datei zu sein." "\n\007Output file '%s' may contain more ciphertext or signature." de: "\n\007Die Ausgabedatei '%s' knnte weiteren\n\ verschlsselten Text oder eine Unterschrift enthalten." es: "\n\007El fichero de salida '%s' puede contener ms texto \ cifrado o una firma." fr: "\n\007Le fichier de sortie '%s' peut contenir d'autre texte chiffr\n\ ou signature." mutt: "\nOutput file '%s' may contain more ciphertext or signature." muttde: "\nDie Ausgabedatei '%s' knnte weiteren\n\ verschlsselten Text oder eine Unterschrift enthalten." "\a\nError: PGP User's Guide not found.\n\ PGP looked for it in the following directories:\n" de: "\a\nFEHLER: PGP-Benutzerhandbuch nicht gefunden!\n\ PGP hat danach in den folgenden Verzeichnissen gesucht:\n" es: "\a\nError: No se encuentra la Gua del usuario.\n\ Se ha buscado en estos directorios:\n" fr: "\n\aErreur: Le manuel d'utilisation de PGP est introuvable.\n\ PGP a examin les repertoires suivants:\n" muttde: "\a\nFEHLER: PGP-Benutzerhandbuch nicht gefunden!\n\ PGP hat danach in den folgenden Verzeichnissen gesucht:\n" "and the doc subdirectory of each of the above. Please put a copy of\n\ both volumes of the User's Guide in one of these directories.\n\ \n\ Under NO CIRCUMSTANCES should PGP ever be distributed without the PGP\n\ User's Guide, which is included in the standard distribution package.\n\ If you got a copy of PGP without the manual, please inform whomever you\n\ got it from that this is an incomplete package that should not be\n\ distributed further.\n\ \n\ PGP will not generate a key without finding the User's Guide.\n\ There is a simple way to override this restriction. See the\n\ PGP User's Guide for details on how to do it.\n\ \n" de: "sowie jeweils im Unterverzeichnis 'DOC' der oben genannten Verzeichnisse.\n\ Bitte lege eine Kopie beider Teile des Benutzerhandbuches (Dateien PGPDOC1.TXT\n\ und PGPDOC2.TXT) in eines der genannten Verzeichnisse.\n\ Unter GAR KEINEN UMSTNDEN sollte PGP jemals ohne das PGP-Benutzerhandbuch\n\ ausgeliefert werden, das sich im Standard-Auslieferungspaket befindet. Wenn\n\ Du eine Kopie von PGP ohne das Handbuch erhalten hast, dann informiere bitte\n\ denjenigen, von dem Du sie bekommen hast, da es sich um ein unvollstndiges\n\ Paket handelt, das knftig nicht mehr ausgeliefert werden sollte.\n\ PGP generiert keine Schlssel, ohne das Handbuch gefunden zu haben!\n\ Im PGP-Handbuch steht brigens auch, wie diese Einschrnkung zu umgehen ist...\n" es: "y el subdirectorio doc de cada uno de ellos. Pon una copia de\n\ ambos volmenes en uno de esos directorios.\n\ \n\ Bajo *ninguna circunstancia* debe distribuirse PGP sin la Gua del usuario,\n\ incluida con la distribucin habitual.\n\ Si tienes una copia de PGP sin manual, informa a quien te la suministr de\n\ que es un paquete incompleto que no debe seguir distribuyndose.\n\ \n\ PGP no genera ninguna clave si no encuentra la Gua del usuario.\n\ Hay una forma sencilla de saltarse esta restriccin. Consulta la\n\ Gua para ver cmo hacerlo.\n\ \n" fr: "et leur(s) sous-repertoire(s). Veuillez placer une copie des\n\ deux (2) documents constituant le manuel d'utilisation dans l'un de\n\ ces repertoires.\n\ En aucune cinconstances PGP ne devrait etre distribu sans que les\n\ documents constituant le manuel ne soit inclus avec le reste.\n\ Si vous avez obtenu PGP sans les documents constituant le manuel\n\ veuillez SVP en aviser votre fournisseur que PGP est incomplet et\n\ que cette maniere de distribuer PGP doit cesser.\n\ \n\ PGP ne fonctionnera pas sans la presence des deux documents constituant le manuel \n\ La facon de circonvenir a cette restriction est de consulter le manuel\n\ \n" muttde: "sowie jeweils im Unterverzeichnis 'DOC' der oben genannten Verzeichnisse.\n\ Bitte lege eine Kopie beider Teile des Benutzerhandbuches (Dateien PGPDOC1.TXT\n\ und PGPDOC2.TXT) in eines der genannten Verzeichnisse.\n\ Unter GAR KEINEN UMSTNDEN sollte PGP jemals ohne das PGP-Benutzerhandbuch\n\ ausgeliefert werden, das sich im Standard-Auslieferungspaket befindet. Wenn\n\ Du eine Kopie von PGP ohne das Handbuch erhalten hast, dann informiere bitte\n\ denjenigen, von dem Du sie bekommen hast, da es sich um ein unvollstndiges\n\ Paket handelt, das knftig nicht mehr ausgeliefert werden sollte.\n\ PGP generiert keine Schlssel, ohne das Handbuch gefunden zu haben!\n\ Im PGP-Handbuch steht brigens auch, wie diese Einschrnkung zu umgehen ist...\n" "\007Keygen error. " de: "\n\007FEHLER bei der Erzeugung des Schlssels.\n" es: "\007Error en la generacin de claves. " fr: "\007Erreur dans la gnration de cl. " mutt: "Keygen error. " muttde: "\nFEHLER bei der Erzeugung des Schlssels.\n" "\007Keyring check error.\n" de: "\n\007FEHLER bei der berprfung des Schlsselbunds.\n" es: "\007Error en la comprobacin del anillo.\n" fr: "Erreur dans la vrification du trousseau de clef." mutt: "Keyring check error.\n" muttde: "\nFEHLER bei der berprfung des Schlsselbunds.\n" "\007Maintenance pass error. " de: "\n\007FEHLER beim Verwaltungsdurchgang.\n" es: "\007Error en el proceso de mantenimiento. " fr: "\007Erreur dans la passe de maintenance. " mutt: "Maintenance pass error. " muttde: "\nFEHLER beim Verwaltungsdurchgang.\n" "File '%s' is not a public keyring\n" de: "Die Datei '%s' ist kein ffentlicher Schlsselbund.\n" es: "El fichero '%s' no es un anillo de claves pblicas\n" fr: "Le fichier '%s' n'est pas un fichier de cls publiques\n" muttde: "Die Datei '%s' ist kein ffentlicher Schlsselbund.\n" "\nA user ID is required to select the public key you want to sign. " de: "\nZur Auswahl des zu unterschreibenden Schlssels wird\n\ eine Benutzer-ID bentigt." es: "\nSe necesita un identificador para elegir\n\ la clave pblica por firmar. " fr: "\nUn nom d'utilisateur est ncessaire pour slectionner la cl\n\ publique que vous voulez signer. " muttde: "\nZur Auswahl des zu unterschreibenden Schlssels wird\n\ eine Benutzer-ID bentigt." "\nEnter the public key's user ID: " de: "\nBenutzer-ID des ffentlichen Schlssels: " es: "\nIntroduzca el identificador de la clave pblica: " fr: "\nEntrez le nom d'utilisateur pour la cl publique: " muttde: "\nBenutzer-ID des ffentlichen Schlssels: " "\007Key signature error. " de: "\n\007FEHLER beim Unterschreiben des Schlssels.\n" es: "\007Error en firma de clave. " fr: "\007Erreur dans la signature de cl. " mutt: "Key signature error. " muttde: "\nFEHLER beim Unterschreiben des Schlssels.\n" "\nA user ID is required to select the key you want to revoke or \ disable. " de: "\nZur Auswahl des zurckzuziehenden oder zu sperrenden Schlssels wird\n\ eine Benutzer-ID bentigt." es: "\nSe necesita un identificador de usuario para elegir la clave \ que quieras\n\revocar o desactivar. " fr: "\nUn nom d'utilisateur est requis pour slectionner la cl que vous\ voulez rvoquer ou inactiver. " muttde: "\nZur Auswahl des zurckzuziehenden oder zu sperrenden Schlssels wird\n\ eine Benutzer-ID bentigt." "\nEnter user ID: " de: "\nBenutzer-ID: " es: "\nIntroduce el identificador: " fr: "\nEntrez le nom d'utilisateur: " muttde: "\nBenutzer-ID: " "\nA user ID is required to select the key you want to edit. " de: "\nZur Auswahl des zu bearbeitenden Schlssels wird eine Benutzer-ID bentigt." es: "\nSe necesita el identificador de usuario para elegir la clave que \ quieras\nmodificar. " fr: "\nUn nom d'utilisateur est ncessaire pour slectionner la cl que\n\ vous voulez modifier. " muttde: "\nZur Auswahl des zu bearbeitenden Schlssels wird eine Benutzer-ID bentigt." "\nEnter the key's user ID: " de: "\nBenutzer-ID des Schlssels: " es: "\nIntroduce el identificador: " fr: "\nEntrez le nom d'utilisateur pour la cl: " muttde: "\nBenutzer-ID des Schlssels: " "\007Keyring edit error. " de: "\n\007FEHLER beim Bearbeiten des Schlsselbunds.\n" es: "\007Error en la modificacin del anillo. " fr: "\007Erreur dans la modification du fichier de cls. " mutt: "Keyring edit error. " muttde: "\nFEHLER beim Bearbeiten des Schlsselbunds.\n" "\n\007Key file '%s' does not exist.\n" de: "\n\007Die Datei '%s' existiert nicht.\n" es: "\n\007No existe el anillo de claves '%s.\n" fr: "\n\007Le fichier de cls '%s' n'existe pas.\n" mutt: "\nKey file '%s' does not exist.\n" muttde: "\nDie Datei '%s' existiert nicht.\n" "\nA user ID is required to select the key you want to extract. " de: "\nZur Auswahl des zu extrahierenden Schlssels wird eine Benutzer-ID bentigt." es: "\nSe necesita el identificador de usuario para elegir la clave que \ quieras\n\extraer. " fr: "\nUn nom d'utilisateur est ncessaire pour slectionner la cl que\n\ vous voulez extraire. " muttde: "\nZur Auswahl des zu extrahierenden Schlssels wird eine Benutzer-ID bentigt." "\007Keyring extract error. " de: "\n\007FEHLER beim Extrahieren aus dem Schlsselbund.\n" es: "\007Error al extraer del anillo. " fr: "\007Erreur dans l'extraction du fichier de cls. " mutt: "Keyring extract error. " muttde: "\nFEHLER beim Extrahieren aus dem Schlsselbund.\n" "\nA user ID is required to select the public key you want to\n\ remove certifying signatures from. " de: "\nZur Auswahl des Schlssels, von dem Beglaubigungen entfernt werden sollen,\n\ wird eine Benutzer-ID bentigt." es: "\nSe necesita el identificador de usuario para elegir la clave pblica\n\ de la que suprimir firmas. " fr: "\nUn nom d'utilisateur est ncessaire pour slectionner la cl\ publique\n\ pour laquelle vous voulez supprimer des signatures de certification. " muttde: "\nZur Auswahl des Schlssels, von dem Beglaubigungen entfernt werden sollen,\n\ wird eine Benutzer-ID bentigt." "\nA user ID is required to select the key you want to remove. " de: "\nZur Auswahl des zu lschenden Schlssels wird eine Benutzer-ID bentigt." es: "\nSe necesita el identificador de usuario para elegir la clave que \ quieras\nsuprimir . " fr: "\nUn nom d'utilisateur est ncessaire pour slectionner la cl que\n\ vous voulez supprimer. " muttde: "\nZur Auswahl des zu lschenden Schlssels wird eine Benutzer-ID bentigt." "\007Key signature remove error. " de: "\n\007FEHLER beim Entfernen der Beglaubigung.\n" es: "\007Error en la supresin de la firma de una clave. " fr: "\007Erreur dans la suppression de signature d'une cl. " mutt: "Key signature remove error. " muttde: "\nFEHLER beim Entfernen der Beglaubigung.\n" "\007Keyring remove error. " de: "\n\007FEHLER beim Lschen aus dem Schlsselbund.\n" es: "\007Error al suprimir del anillo. " fr: "\007Erreur dans la suppression du fichier de cls. " mutt: "Keyring remove error. " muttde: "\nFEHLER beim Lschen aus dem Schlsselbund.\n" "\007Keyring view error. " de: "\n\007FEHLER beim Anzeigen des Schlsselbunds.\n" es: "\007Error al visualizar el anillo. " fr: "\007Erreur dans la visualisation du fichier de cls. " mutt: "Keyring view error. " muttde: "\nFEHLER beim Anzeigen des Schlsselbunds.\n" "For more detailed help, consult the PGP User's Guide.\n" de: "Ausfhrlichere Hilfe findet sich in der PGP-Anleitung.\n" es: "Para obtener ms ayuda, consulta la Gua del usuario de PGP.\n" fr: "Pour une aide plus dtaille, consultez le guide de l'utilisateur de PGP.\n" mutt: "\n" muttde: "\n" "\nInvalid arguments.\n" de: "\nUngltige Argumente!\n" es: "\nArgumentos incorrectos.\n" fr: "\nArguments invalides.\n" muttde: "\nUngltige Argumente!\n" "\nUsage summary:\ \nTo encrypt a plaintext file with recipent's public key, type:\ \n pgp -e textfile her_userid [other userids] (produces textfile.pgp)\ \nTo sign a plaintext file with your secret key:\ \n pgp -s textfile [-u your_userid] (produces textfile.pgp)\ \nTo sign a plaintext file with your secret key, and then encrypt it\ \n with recipent's public key, producing a .pgp file:\ \n pgp -es textfile her_userid [other userids] [-u your_userid]\ \nTo encrypt with conventional encryption only:\ \n pgp -c textfile\ \nTo decrypt or check a signature for a ciphertext (.pgp) file:\ \n pgp ciphertextfile [-o plaintextfile]\ \nTo produce output in ASCII for email, add the -a option to other options.\ \nTo generate your own unique public/secret key pair: pgp -kg\ \nFor help on other key management functions, type: pgp -k\n" de: "\nbersicht der PGP-Befehle:\ \nVerschlsseln eines Textes mit dem ffentlichen Schlssel des Empfngers:\ \n pgp -e {Text} {Benutzer-ID des Empfngers} (Ergebnis: {Text}.pgp)\ \nUnterschreiben eines Textes mit Deinem privaten Schlssel:\ \n pgp -s {Text} [-u {Deine Benutzer-ID}] (Ergebnis: {Text}.pgp)\ \nUnterschreiben eines Textes mit Deinem privaten Schlssel und anschlieend\ \nVerschlsseln mit dem ffentlichen Schlssel des Empfngers:\ \n pgp -es {Text} {Benutzer-ID des Empfngers} [weitere Benutzer-IDs]\ \n [-u {Deine Benutzer-ID}] (Ergebnis: {Text}.pgp)\ \nVerschlsseln mit konventioneller Verschlsselung:\ \n pgp -c {Text}\ \nEntschlsseln oder berprfen der Unterschrift:\ \n pgp {verschlsselter Text} [-o {Klartext}]\ \nVerpacken der Ausgabe in ASCII (fr E-Mail): pgp {...} -a {...}\ \nErzeugen eines eigenen Schlssel-Paares: pgp -kg\ \nHilfe zur Schlsselverwaltung: pgp -k\n" es: "\nResumen de las instrucciones:\ \nEncriptar fichero normal con la clave pblica del destinatario:\ \n pgp -e ftexto su_identificador (produce ftexto.pgp)\ \nFirmar un fichero de texto normal con tu clave secreta:\ \n pgp -s ftexto [-u tu_identificador] (produce ftexto.pgp)\ \nFirmar un fichero normal con tu clave secreta y despus encriptarlo\ \n con la clave pblica del destinatario, produciendo un fichero .pgp:\ \n pgp -es ftexto su_identificador [otros] [-u tu_identificador]\ \nEncriptar slo con cifrado convencional:\ \n pgp -c ftexto\ \nDesencriptar o comprobar la firma en un fichero cifrado (.pgp):\ \n pgp fcifrado [-o fnormal]\ \nProducir resultado en ASCII para correo electrnico: aadir la opcin -a.\ \nGenerar tu propio par nico de claves pblica/secreta: pgp -kg\ \nAyuda sobre otras funciones de gestin de claves: pgp -k\n" fr: "\nSommaire:\ \nPour chiffrer un fichier en clair avec la cl publique du destinataire, \ tapez:\ \n pgp -e fichier son_nom [autres noms] (produit fichier.pgp)\ \nPour signer un texte en clair avec votre cl secrte:\ \n pgp -s fichier [-u votre_nom] (produit fichier.pgp)\ \nPour signer un texte en clair avec votre cl secrte, puis le chiffrer\ \n avec la cl publique du destinataire, produisant un fichier .pgp:\ \n pgp -es fichier son_nom [autres noms] [-u votre_nom]\ \nPour chiffrer de manire conventionelle seulement:\ \n pgp -c fichier\ \nPour dchiffrer ou vrifier une signature pour un fichier chiffr (.pgp):\ \n pgp fichier_chiffr [-o fichier_en_clair]\ \nPour produire une sortie en ASCII pour courrier lectronique, ajouter\ \nl'option -a aux autres options.\ \nPour gnrer votre propre paire de cls publique/secrte:\ \n pgp -kg\ \nPour de l'aide sur les autres fonctions de gestion de cl, tapez: pgp -k\n" muttde: "\nbersicht der PGP-Befehle:\ \nVerschlsseln eines Textes mit dem ffentlichen Schlssel des Empfngers:\ \n pgp -e {Text} {Benutzer-ID des Empfngers} (Ergebnis: {Text}.pgp)\ \nUnterschreiben eines Textes mit Deinem privaten Schlssel:\ \n pgp -s {Text} [-u {Deine Benutzer-ID}] (Ergebnis: {Text}.pgp)\ \nUnterschreiben eines Textes mit Deinem privaten Schlssel und anschlieend\ \nVerschlsseln mit dem ffentlichen Schlssel des Empfngers:\ \n pgp -es {Text} {Benutzer-ID des Empfngers} [weitere Benutzer-IDs]\ \n [-u {Deine Benutzer-ID}] (Ergebnis: {Text}.pgp)\ \nVerschlsseln mit konventioneller Verschlsselung:\ \n pgp -c {Text}\ \nEntschlsseln oder berprfen der Unterschrift:\ \n pgp {verschlsselter Text} [-o {Klartext}]\ \nVerpacken der Ausgabe in ASCII (fr E-Mail): pgp {...} -a {...}\ \nErzeugen eines eigenen Schlssel-Paares: pgp -kg\ \nHilfe zur Schlsselverwaltung: pgp -k\n" "\nKey management functions:\ \nTo generate your own unique public/secret key pair:\ \n pgp -kg\ \nTo add a key file's contents to your public or secret key ring:\ \n pgp -ka keyfile [keyring]\ \nTo remove a key or a user ID from your public or secret key ring:\ \n pgp -kr userid [keyring]\ \nTo edit your user ID or pass phrase:\ \n pgp -ke your_userid [keyring]\ \nTo extract (copy) a key from your public or secret key ring:\ \n pgp -kx userid keyfile [keyring]\ \nTo view the contents of your public key ring:\ \n pgp -kv[v] [userid] [keyring]\ \nTo check signatures on your public key ring:\ \n pgp -kc [userid] [keyring]\ \nTo sign someone else's public key on your public key ring:\ \n pgp -ks her_userid [-u your_userid] [keyring]\ \nTo remove selected signatures from a userid on a keyring:\ \n pgp -krs userid [keyring]\ \n" de: "\nSchlssel-Verwaltung:\ \nErzeugen eines eigenen, eindeutigen Schlssel-Paares (privat/ffentlich):\ \n pgp -kg\ \nHinzufgen von Schlsseln zum privaten oder ffentlichen Schlsselbund:\ \n pgp -ka {Datei mit Schlsseln} [{Schlsselbund}]\ \nLschen eines Schlssels oder einer Benutzer-ID aus einem Schlsselbund:\ \n pgp -kr {Benutzer-ID} [{Schlsselbund}]\ \nndern Deiner Benutzer-ID oder Deines Mantras:\ \n pgp -ke {Deine Benutzer-ID} [{Schlsselbund}]\ \nHerauskopieren eines Schlssels aus einem Schlsselbund:\ \n pgp -kx {Benutzer-ID} {Ausgabe-Datei} [{Schlsselbund}]\ \nAnzeigen des Inhaltes des ffentlichen Schlsselbunds:\ \n pgp -kv[v] [{Benutzer-ID}] [{Schlsselbund}]\ \nberprfen der Beglaubigungen im ffentlichen Schlsselbund:\ \n pgp -kc [{Benutzer-ID}] [{Schlsselbund}]\ \nBeglaubigen eines ffentlichen Schlssels eines anderen Benutzers:\ \n pgp -ks {seine Benutzer-ID} [-u {Deine Benutzer-ID}] [{Schlsselbund}]\ \nEntfernen ausgewhlter Beglaubigungen von einem Schlssel:\ \n pgp -krs {Benutzer-ID} [{Schlsselbund}]\n" es: "\nFunciones para la gestin de claves:\ \nGenerar tu propio par nico de claves pblica/secreta:\ \n pgp -kg\ \nAadir contenido de fichero de clave al anillo de claves pblicas o secretas:\ \n pgp -ka fdclaves [anillo]\ \nSuprimir una clave o identificador de usuario de un anillo de claves:\ \n pgp -kr identificador [anillo]\ \nModificar tu identificador de usuario o tu contrasea:\ \n pgp -ke tu_identificador [anillo]\ \nExtraer (copiar) una clave del anillo de claves pblicas o secretas:\ \n pgp -kx identificador fdclaves [anillo]\ \nVisualizar el contenido del anillo de claves pblicas:\ \n pgp -kv[v] [identificador] [anillo]\ \nComprobar las firmas del anillo de claves pblicas:\ \n pgp -kc [identificador] [anillo]\ \nFirmar la clave pblica de alguien en el anillo de claves correspondiente:\ \n pgp -ks otro_identificador [-u tu_identificador] [anillo]\ \nSuprimir ciertas firmas de un idusuario en un anillo:\ \n pgp -krs identificador [anillo]\ \n" fr: "Fonctions de gestion des cls:\ \nPour gnrer votre propre paire de cls publique/secrte:\ \n pgp -kg\ \nPour ajouter le contenu d'un fichier de cls votre fichier de cls\ \n public ou secret:\ \n pgp -ka fichier_de_cls [votre_fichier_de_cls]\ \nPour retirer une cl de votre fichier de cls public ou secret:\ \n pgp -kr nom_d_utilisateur [fichier_de_cls]\ \nPour extraire (copier) une cl de votre fichier de cls public ou secret:\ \n pgp -kx nom_d_utilisateur fichier_de_la_cl [fichier_de_cls]\ \nPour visualiser le contenu de votre fichier de cls:\ \n pgp -kv[v] [nom_d_utilisateur] [ficher_de_cls]\ \nPour vrifier les signatures sur votre fichier de cls publiques:\ \n pgp -kc [nom_d_utilisateur] [ficher_de_cls]\ \nPour signer la cl publique de quelqu'un d'autre sur votre fichier de\ \n cls publiques:\ \n pgp -ks son_nom votre_nom [fichier_de_cls]\ \nPour enlever certaines signatures d'une personne sur un fichier de cls:\ \n pgp -krs son_nom [fichier_de_cls]\n" muttde: "\nSchlssel-Verwaltung:\ \nErzeugen eines eigenen, eindeutigen Schlssel-Paares (privat/ffentlich):\ \n pgp -kg\ \nHinzufgen von Schlsseln zum privaten oder ffentlichen Schlsselbund:\ \n pgp -ka {Datei mit Schlsseln} [{Schlsselbund}]\ \nLschen eines Schlssels oder einer Benutzer-ID aus einem Schlsselbund:\ \n pgp -kr {Benutzer-ID} [{Schlsselbund}]\ \nndern Deiner Benutzer-ID oder Deines Mantras:\ \n pgp -ke {Deine Benutzer-ID} [{Schlsselbund}]\ \nHerauskopieren eines Schlssels aus einem Schlsselbund:\ \n pgp -kx {Benutzer-ID} {Ausgabe-Datei} [{Schlsselbund}]\ \nAnzeigen des Inhaltes des ffentlichen Schlsselbunds:\ \n pgp -kv[v] [{Benutzer-ID}] [{Schlsselbund}]\ \nberprfen der Beglaubigungen im ffentlichen Schlsselbund:\ \n pgp -kc [{Benutzer-ID}] [{Schlsselbund}]\ \nBeglaubigen eines ffentlichen Schlssels eines anderen Benutzers:\ \n pgp -ks {seine Benutzer-ID} [-u {Deine Benutzer-ID}] [{Schlsselbund}]\ \nEntfernen ausgewhlter Beglaubigungen von einem Schlssel:\ \n pgp -krs {Benutzer-ID} [{Schlsselbund}]\n" "\nIncluding \"%s\"...\n" de: "\nEmpfngerliste aus der Datei '%s' wird eingelesen...\n" es: "\nIncluyendo \"%s\"...\n" fr: "\nIncluant '%s'...\n" muttde: "\nEmpfngerliste aus der Datei '%s' wird eingelesen...\n" "\nWe need to generate %u random bits. This is done by measuring the\ \ntime intervals between your keystrokes. Please enter some random text\ \non your keyboard until you hear the beep:\n" de: "\nWir mssen %u zufllige Bits erzeugen. Dies wird durch Messung\ \nder Abstnde zwischen Deinen Anschlgen bewerkstelligt. Bitte gib\ \nirgendwelchen beliebigen Text auf der Tastatur ein, bis es piepst:\n" es: "\nNecesitamos generar %d bits aleatorios. Se hace midiendo los\ \nintervalos de tiempo entre pulsaciones de tecla. Escribe\ \ntexto al azar en el teclado hasta que oigas un pitido:\n" fr: "\nNous devons gnrer %d bits alatoires. Ceci est fait en mesurant\ \nl'intervalle de temps entre les frappes de touches. Veuillez tapper du\ \ntexte alatoire sur votre clavier jusqu' ce que vous entendiez le\ \nsignal sonore:\n" muttde: "\nWir mssen %u zufllige Bits erzeugen. Dies wird durch Messung\ \nder Abstnde zwischen Deinen Anschlgen bewerkstelligt. Bitte gib\ \nirgendwelchen beliebigen Text auf der Tastatur ein, bis es piepst:\n" "\007 -Enough, thank you.\n" de: "\007 -Danke, das gengt!\n" es: "\007 -Es suficiente.\n" fr: "\007 -Assez, merci.\n" mutt: " -Enough, thank you.\n" muttde: " -Danke, das gengt!\n" "\ Uses the RSAREF(tm) Toolkit, which is copyright RSA Data Security, Inc.\n\ Distributed by the Massachusetts Institute of Technology.\n" de: "Benutzt das RSAREF(tm) Toolkit, (c) RSA Data Security, Inc.\n\ Ausgeliefert vom Massachusetts Institute of Technology.\n" es: "\ Utiliza RSAREF(tm), copyright de RSA Data Security, Inc.\n\ Distribuido por el Massachusetts Institute of Technology.\n" fr: "Ce logiciel utilise RSAREF(tm) Toolkit, Copyright RSA Data Security, Inc\n\ Distribu par le Massachusetts Institute of Technology.\n" muttde: "Benutzt das RSAREF(tm) Toolkit, (c) RSA Data Security, Inc.\n\ Ausgeliefert vom Massachusetts Institute of Technology.\n" "Out of memory" de: "Zu wenig Speicher!" es: "No queda memoria" fr: "Mmoire insufisante" muttde: "Zu wenig Speicher!" "\nOut of memory\n" de: "\nZu wenig Speicher!\n" es: "\nNo queda memoria\n" fr: "\nMmoire insuffisante\n" muttde: "\nZu wenig Speicher!\n" "\n\007Out of memory.\n" de: "\n\007Zu wenig Speicher!\n" es: "\n\007No queda memoria.\n" fr: "\n\007Mmoire insuffisante.\n" mutt: "\nOut of memory.\n" muttde: "\nZu wenig Speicher!\n" "\nCompression/decompression error\n" de: "\nFEHLER beim Packen/Entpacken!\n" es: "\nError en compresin/descompresin\n" fr: "\nErreur de compression/decompression\n" muttde: "\nFEHLER beim Packen/Entpacken!\n" "\nERROR: unexpected end of compressed data input.\n" de: "\nFEHLER: vorzeitiges Ende der ZIP-gepackten Eingangsdaten.\n" es: "\nERROR: los datos comprimidos terminan antes de tiempo.\n" fr: "\nERREUR: fin innopine des donnes d'entre compresses.\n" muttde: "\nFEHLER: vorzeitiges Ende der ZIP-gepackten Eingangsdaten.\n" # The following 4 translations MUST be exactly 3 characters long! "pub" de: "ff" muttde: "ff" "sec" de: "prv" muttde: "prv" "sig" de: "Unt" es: "fir" muttde: "Unt" "com" de: "Wid" muttde: "Wid" # IN-CH extentions. # translation must not change the string length "rev" de: "zur" muttde: "zur" # translation must not change the string length " Expire: %s%s" de: " Verfall: %s%s" muttde: " Verfall: %s%s" # translation must not change the string length " no expire " de: " kein Verfall " muttde: " kein Verfall " "ENCRyption only\n" de: "*** nur Verschlsselung! ***\n" muttde: "*** nur Verschlsselung! ***\n" "Key ID %s is SIGN only. Decryption avoided.\n" de: "Schlssel ID %s ist nur zum Unterschreiben. Entschlsselung verhindert.\n" muttde: "Schlssel ID %s ist nur zum Unterschreiben. Entschlsselung verhindert.\n" "Key ID %s is not valid.\n" de: "Schlssel ID %s ist ungltig.\n" muttde: "Schlssel ID %s ist ungltig.\n" "Key is an ENCRyption only key.\n" de: "Der Schlssel ist nur zur Verschlsselung.\n" muttde: "Der Schlssel ist nur zur Verschlsselung.\n" "Key is a SIGNature only key.\n" de: "Der Schlssel ist nur fr Unterschriften.\n" muttde: "Der Schlssel ist nur fr Unterschriften.\n" "Key is out of use.\n" de: "Der Schlssel ist nicht mehr in Benutzung.\n" muttde: "Der Schlssel ist nicht mehr in Benutzung.\n" "SIGNature only\n" de: "*** nur Unterschriften! ***\n" muttde: "*** nur Unterschriften! ***\n" " Revoked by: " de: " Zurckgezogen von: " muttde: " Zurckgezogen von: " " Low Cert by: " de: "Niedrige Beglaubigung von: " muttde: "Niedrige Beglaubigung von: " "Medium Cert by: " de: "Mittlere Beglaubigung von: " muttde: "Mittlere Beglaubigung von: " " High Cert by: " de: " Hohe Beglaubigung von: " muttde: " Hohe Beglaubigung von: " " Unknown type: " de: " Unbekannter Typ: " muttde: " Unbekannter Typ: " "Try to obtain the corresponding SIGN key.\n" de: "Es wird versucht, den zugehrigen Unterschriftsschlssel zu verwenden.\n" muttde: "Es wird versucht, den zugehrigen Unterschriftsschlssel zu verwenden.\n" "\n\007Key cert is already revoked by user '%s'.\n" de: "\n\007Die Unterschrift unter dem Schlssel wurde bereits\n\ von '%s' zurckgezogen.\n" mutt: "\nKey cert is already revoked by user '%s'.\n" muttde: "\nDie Unterschrift unter dem Schlssel wurde bereits\n\ von '%s' zurckgezogen.\n" "\n\007Key is not signed by user '%s'.\n" de: "\n\007Der Schlssel ist nicht von '%s' unterschrieben.\n" mutt: "\nKey is not signed by user '%s'.\n" muttde: "\nDer Schlssel ist nicht von '%s' unterschrieben.\n" "\nKey signature certificate revoked.\n" de: "\nDie Unterschrift unter dem Schlssel wurde zurckgezogen.\n" muttde: "\nDie Unterschrift unter dem Schlssel wurde zurckgezogen.\n" "This key is already out of use.\n" de: "Dieser Schlssel ist bereits nicht mehr in Benutzung.\n" muttde: "Dieser Schlssel ist bereits nicht mehr in Benutzung.\n" "\007 Key ID %s is SIGN only. (protest against decryption)\n" de: "\007Die Schlssel-ID %s ist nur fr Unterschriften.\n\ Keine Entschlsselung mglich!\n" mutt: " Key ID %s is SIGN only. (protest against decryption)\n" muttde: "Die Schlssel-ID %s ist nur fr Unterschriften.\n\ Keine Entschlsselung mglich!\n" "Signature was made after the key ID %s was expired.\n" de: "Unterschrift wurde mit dem verfallen Schlssel ID %s erstellt.\n" muttde: "Unterschrift wurde mit dem verfallen Schlssel ID %s erstellt.\n" "Signature was made before the key ID %s was valid.\n" de: "Unterschrift wurde mit dem noch ungltigen Schlssel ID %s erstellt.\n" muttde: "Unterschrift wurde mit dem noch ungltigen Schlssel ID %s erstellt.\n" "Signature was made using the ENCR key ID %s.\n" de: "Unterschrift wurde mit dem Verschlsselungsschlssel ID %s erstellt.\n" muttde: "Unterschrift wurde mit dem Verschlsselungsschlssel ID %s erstellt.\n" "Something goes wrong, may be an unlucky User ID?\n" de: "Irgendwas ist schiefgegangen. Liegt's an der NutzerID?\n" muttde: "Irgendwas ist schiefgegangen. Liegt's an der NutzerID?\n" "Something goes wrong, may be this userid '%s' is unlucky?\n" de: "Irgendwas ist schiefgegangen. Liegt's an der NutzerID '%s'?\n" muttde: "Irgendwas ist schiefgegangen. Liegt's an der NutzerID '%s'?\n" "The compromise certificate might not be accepted on newer versions.\n\ Revoke anyway? (y/N)" de: "Das Widerrufszertifikat kann von neueren Versionen zurckgewiesen\ werden.\nTrotzdem widerrufen? (j/N)" muttde: "Das Widerrufszertifikat kann von neueren Versionen zurckgewiesen\ werden.\nTrotzdem widerrufen? (j/N)" "The key for the userid '%s' is SIGN only.\nI'll try the corresponding ENCR key.\n" de: "Schlssel ID '%s' ist nur zum Unterschreiben.\n\ Der zugehrige Verschlsselungsschlssel wird versucht.\n" muttde: "Schlssel ID '%s' ist nur zum Unterschreiben.\n\ Der zugehrige Verschlsselungsschlssel wird versucht.\n" "The key for the userid '%s' is not valid, skipped.\n" de: "Schlssel ID '%s' ist ungltig.\nbersprungen.\n" muttde: "Schlssel ID '%s' ist ungltig.\nbersprungen.\n" "The user ID typed in has garbled options!\n" de: "Die NutzerID enthlt fehlerhafte Optionseintrge!\n" muttde: "Die NutzerID enthlt fehlerhafte Optionseintrge!\n" "This key is an ENCRyption only key.\n" de: "Das ist ein Verschlsselungsschlssel.\n" muttde: "Das ist ein Verschlsselungsschlssel.\n" "This key is too old to be revoked.\n" de: "Der Schlssel ist zu alt, um zurckgezogen zu werden.\n" muttde: "Der Schlssel ist zu alt, um zurckgezogen zu werden.\n" "This key is too old to be signed.\n" de: "Der Schlssel ist zu alt, um unterschrieben zu werden.\n" muttde: "Der Schlssel ist zu alt, um unterschrieben zu werden.\n" "This key will be generated in future. *oops*\n" de: "Der Schlssel wird in der Zukunft erstellt. *huch*\n" muttde: "Der Schlssel wird in der Zukunft erstellt. *huch*\n" "Your key is too old to sign with.\n" de: "Der Schlssel ist zu alt zum unterschreiben.\n" muttde: "Der Schlssel ist zu alt zum unterschreiben.\n" "Your key will be generated in future. *oops*\n" de: "Dein Schlssel wird in der Zukunft erstellt. *huch*\n" muttde: "Dein Schlssel wird in der Zukunft erstellt. *huch*\n" "\007**** Signed with an ENCRyption only key ****" de: "\007**** Unterschr. mit einem Verschl.-Schlssel ****" mutt: "**** Signed with an ENCRyption only key ****" muttde: "**** Unterschr. mit einem Verschl.-Schlssel ****" "\007**** Signed with an invalid key ****" de: "\007**** Unterschr. mit einem ungltigen Schlssel ****" mutt: "**** Signed with an invalid key ****" muttde: "**** Unterschr. mit einem ungltigen Schlssel ****" "\007**** Signed with a revoked key ****" de: "\007**** Unterschr. mit einem zurckgez. Schlssel ****" mutt: "**** Signed with a revoked key ****" muttde: "**** Unterschr. mit einem zurckgez. Schlssel ****" "\n\nREAD CAREFULLY: How did you prove the users real identity ?\n\ 0) What? I do not understand this question.\n\ 1) No attempt made at all to identify the user with a real name.\n\ 2) Some casual attempt made to identify user with his name.\n\ 3) Heavy-duty identification efforts, photo ID, direct contact...\n" de: "\n\nSORGFLTIG LESEN: Wie hast Du die Identitt des Nutzers geprft ?\n\ 0) Bitte? Ich verstehe die Frage nicht.\n\ 1) Keine berprfung des wirklichen Namens des Nutzers.\n\ 2) Einige Bemhungen, die Person mit diesem Namen zu identifizieren.\n\ 3) Groe Identifizierungsaufwendungen, Ausweis, direkter Kontakt...\n" muttde: "\n\nSORGFLTIG LESEN: Wie hast Du die Identitt des Nutzers geprft ?\n\ 0) Bitte? Ich verstehe die Frage nicht.\n\ 1) Keine berprfung des wirklichen Namens des Nutzers.\n\ 2) Einige Bemhungen, die Person mit diesem Namen zu identifizieren.\n\ 3) Groe Identifizierungsaufwendungen, Ausweis, direkter Kontakt...\n" $Id$ neomutt-neomutt-20171215/contrib/language50.txt000066400000000000000000000712611321473123000212760ustar00rootroot00000000000000#This file contains the strings used by PGP. [DIFFERENT_EXES] us=\ PGP is now invoked from different executables for different operations:\n\n\ pgpe Encrypt (including Encrypt/Sign)\n\ pgps Sign\n\ pgpv Verify/Decrypt\n\ pgpk Key management\n\ pgpo PGP 2.6.2 command-line simulator (not yet implemented)\n\n\ See each application's respective man page or the general PGP documentation\n\ for more information.\n [CREATING_OUTPUT_FILE] us=Creating output file %s\n #Untested [COPYING_KEYFILE_AND_RUNNING_PGPK] us=Copying key file to \"%s\", running pgpk to process it...\n\n mutt= #Untested [NEED_PASSPHRASE] us=You need a passphrase to encrypt the file\n [MUST_SPECIFY_A_RECIPIENT] us=You must specify at least one recipient for encryption!\n #Untested [NEED_PASSPHRASE_AGAIN] us=Enter same passphrase again\n #Untested [PASSPHRASES_DIFFERENT] us=Error: Passphrases were different. Try again.\n #Untested [ZERO_LEN_PASSPHRASE] us=Encryption error\n #Untested [TREAT_AS_PGP] us=This is a PGP File. Treat it as such? [y/N]\n #Untested [PRIVATE_KEY_MISSING] us=Cannot find a private key for signing: %s\n #Untested [CANNOT_CONVERT_TO_PRIVATE_KEY] us=Cannot convert to private key\n #Untested [PRIVATE_KEY_CANNOT_SIGN] us=Private Key cannot sign\n #Untested [CANNOT_UNLOCK_PRIVATE_KEY] us=Cannot unlock private key\n #Untested [NO_KEYRINGS] us=No keyrings to use #Untested [NO_ENCRYPTION_KEYS_FOUND_FOR] us=No encryption keys found for: %s\n #Untested [CANNOT_FIND_KEY] us=Cannot find key: %s\n #Untested [CANNOT_ADD_MY_KEY] us=Cannot add my key to set\n #Untested [NO_VALID_RECIPIENTS] us=No valid keys found for any recipients, exiting...\n #Untested [USING_STDIN] us=No files specified. Using stdin.\n\n mutt= #Untested [CANNOT_OPEN_INPUT_FILE] us=Cannot open input file %s\n #Untested [CANNOT_SETUP_PROCESSING_PIPELINE] us=Cannot Setup Processing Pipeline\n #Untested [UNRECOGNIZED_OPTION_STRING] us=Unrecognized option %s\n #Untested [UNRECOGNIZED_OPTION_STRING_DASH] us=Unrecognized option -%s\n #Untested [UNRECOGNIZED_OPTION_CHAR] us=Unrecognized option -%c\n #Untested [ARGS_INCOMPATABLE] us="Cannot use -%c and -%c together\n" #Untested [ONLY_ONE_OUTPUT_FILE] us="Only one -o option allowed\n" #Untested [ONLY_ONE_USERNAME] us="Only one -u option allowed\n" #Untested [NO_OUTPUT_FILENAME] us=-o option requires an output file name argument\n [NO_RECIPIENT_SPECIFIED] us=-r option requires a recipient name argument\n #Untested [NO_USERID_SPECIFIED] us=-u option requires a userid argument\n #Untested, and probably going away [NO_PASSPHRASE_SPECIFIED_IN_BATCHMODE] us=-z option requires a passphrase argument\n #Untested [CANNOT_COMBINE_CONVENTIONAL_AND_PK] us=Cannot combine -c and -r arguments\n #Untested [PGPK_IS_SEPERATE] us=pgpk is a seperate program, not a symlink to pgp!\n #Untested [UNKNOWN_SYMLINK] us=Invoked with unknown symlink\n #Untested [PRIVATE_KEY_NEEDED_FOR_SIGNATURE] us=A private key is required to make a signature.\n [ENTER_Y_OR_N] us="Invalid response. Please enter Y or N [default %c]: \n" #Untested [GENERIC_KEYRING_ERROR] us="Error on keyring \"%s\": " #Untested [UNABLE_TO_OPEN_DEFAULT_KEYRINGS] us="Unable to open default keyrings: " #Untested [UNABLE_TO_OPEN_KEYRING] us="Unable to open keyring: " #Untested [KEY_CORRUPTED] us="Key Corrupted (%s): " #Untested [NEED_SIG_FILE] us="File to check signature against [%s]: " #untested [GOOD_SIGNATURE] us="Good signature made %s by key:\n" #untested [BAD_SIGNATURE] us="BAD signature made %s by key:\n" #untested [ERROR_SIGNATURE] us="Error %s checking signature: %s\n" #Untested [UNKNOWN_SIGNATURE] us="Signature by unknown keyid: " #untested [ENTER_PASSPHRASE] us="Enter pass phrase: " #Untested [RANDOM_BITS_FROM_DEVICE] us="\n\ We need to generate %u random bits. This is done by reading\n\ %s. Depending on your system, you may be able\n\ to speed this process by typing on your keyboard and/or moving your mouse.\n" #Untested [RANDOM_BITS_FROM_DEVICE_OLD_KERNEL] us="\n\ /dev/random detected; however, on Linux kernel versions < 1.3.33, it is not\n\ cryptographically usable. If you wish to use /dev/random as an entropy\n\ source, it is recommended that you upgrade your kernel version. If you feel\n\ that you received this message in error, add ForceRandomDevice=1 to your\n\ pgp.cfg file, but be warned that by doing so without know what you are\n\ doing, you could compromise the security of your key.\n" #Untested [RANDOM_BITS_FROM_KEYBOARD] us="\n\ We need to generate %u random bits. This is done by measuring the\n\ time intervals between your keystrokes. Please enter some random text\n\ on your keyboard until you hear the beep:\n" #Untested [NO_INPUT_FILE_IN_BATCHMODE] us="Cannot request input file in batchmode\n" #Untested [UNABLE_TO_OPEN_FILE] us="Unable to open file \"%s\"\n" #Untested [UNABLE_TO_CREATE_READ_MODULE] us="Unable to create file read module.\n" #Untested [UNKNOWN_FILE_TYPE] us="Unknown file type (clearsigned?). Assuming text\n" #Untested [OPENING_FILE_WITH_TYPE] us="Opening file \"%s\" type %s.\n" mutt= #Untested [ERROR_CLOSING_OLD_FILE] us="Error closing old file: %d\n" #Untested [NEED_PASSPHRASE_TO_DECRYPT_KEY] us="Need a pass phrase to decrypt private key:\n" #Untested [GOOD_PASSPHRASE] us="Pass phrase is good.\n" #Untested [BAD_PASSPHRASE] us="Error: Bad pass phrase.\n\n" #Untested [PASSPHRASE_INCORRECT] us="Password Incorrect." #Untested [TRY_AGAIN] us=" Try Again." #Untested [UNKNOWN_ESK] us="Unknown ESK type: %d\n" #Untested [CANNOT_DECRYPT] us="Cannot decrypt message. It can only be decrypted by:\n" #Untested [A_PASSPHRASE] us=" A Pass Phrase\n" #Untested [KEY_ID] us=" Key ID " #Untested [FORCE_OVERWRITE] us="File \"%s\" already exists. Overwrite? [y/N] " #Untested [UNABLE_TO_OVERWRITE_FILE] us="Unable to overwrite file \"%s\"\n" #Untested [RANDOM_DEVICE_NOT_DEFAULT] us="Warning! Random device is something other than %s!\n\ This MAY be a security hole.\n" #Untested [RANDOM_DEVICE_WRITABLE] us="Warning! %s is writable by users other than root!\n\ This is probably OK, but you should have your sysadmin fix it.\n\ Proceeding.\n" #Untested [RANDOM_DEVICE_UNREADABLE] us="\ Warning! Random device %s found, but you can't read it!\n" #Untested [BITS_AND_KEYID] us="%6u bits, Key ID " #Untested [KEY_NOT_FOUND] us=Key not found: \"%s\"\n #Untested [PGPERR_TROUBLE_BADTRUST_LONG] us="Trust packet too long: %lu bytes long" #Untested [PGPERR_TROUBLE_UNKPKTBYTE_LONG] us="Unknown packet byte: %02X" #Untested [PGPERR_TROUBLE_KEY2BIG_LONG] us="Key grossly oversized: %lu bytes long" #Untested [PGPERR_TROUBLE_NAME2BIG_LONG] us="User ID too long: %lu bytes long" #Untested [PGPERR_TROUBLE_SIG2BIG_LONG] us="Signature grossly oversized: %lu bytes long" #Untested [PGPERR_TROUBLE_DUPKEYID_LONG] us="Duplicate keyID found. Two keys have the same keyID,\n\ but they are different. This is highly suspicious. The first key is:" #Untested [PGPERR_TROUBLE_DUPKEY_LONG]: us="A key was found twice in one keyring file. It is a duplicate of:\n" #Untested [PGPERR_TROUBLE_DUPNAME_LONG] us="A name was found twice in one keyring file. It is a duplicate of:\n" #Untested [PGPERR_TROUBLE_BAREKEY_LONG] us="A key was found twice in one keyring file. It is a duplicate of: " #Untested [PGPERR_TROUBLE_VERSION_BUG_CUR_LONG] us="This private key's version number appears to be incorrect.\n\ PGP version 2.6 had a bug wherein it would improperly change the\n\ version number of a private key generated by older versions of PGP\n\ when it was edited. The private key in this key file has a version\n\ byte that is different from a copy in another key file, and appears\n\ to be improper. PGP will fix this by changing the version byte in\n\ the private key to the previous value. The key with the problem is:\n" #Untested [PGPERR_TROUBLE_VERSION_BUG_PREV_LONG] us="A previously seen private key's version number appears to be\n\ incorrect. PGP version 2.6 had a bug wherein it would improperly\n\ change the version byte of a private key generated by older versions\n\ of PGP when it was edited. The public key in this key file has\n\ a version byte that is different from a private key elsewhere,\n\ which appears to be improper. PGP will fix this by changing the\n\ version byte in the private key to the previous value. The key\n\ with the problem is:\n" #Untested [PGPERR_KEYIO_READING_LONG] us="I/O error reading file: %s" #Untested [PGPERR_KEYIO_FTELL_LONG] us="I/O error during call to ftell(): %s" #Untested [PGPERR_PRECEDING_ASSOCIATED_WITH] us="The preceeding error was associated with: " #Untested [NOT_PGP_KEYFILE] us="File is not a PGP key file. Aborting.\n" #Untested [FOLLOWING_KEYRING_PROBLEMS] us="The following problems were encountered while reading the keyring:\n" #Untested [OFFSET_DESCRIPTION] us="Offset Description\n" #Untested [UNKNOWN_SIGNATOR] us=" (Unknown signator, can't be checked)\n" #Untested [OPEN_PAREN_KEYID] us=" (KeyID:" #Untested [REVOKED] us="*REVOKED*" #Untested [ABOVE_KEY_REVOKED] us="\ WARNING: The above key has been revoked by its owner,\n\ possibly because the private key was compromised.\n\ You cannot use a revoked key for encryption.\n" #Untested [ABOVE_KEY_DISABLED] us="\ WARNING: The above key has been disabled on your keyring. If you\n\ wish to use it, use \"pgpk -d\" to reenable it.\n" [ABOVE_KEY_EXPIRED] us="\ WARNING: The above key is not valid for use after %s.\n" #Untested [STILL_USE_EXPIRED_KEY] us="\ WARNING: This key is not valid for use after %s.\n\ Do you still want to use it? [y/N] " #Untested [PGP_NAMETRUST_UNKNOWN] us="\ WARNING: Because the following name has not been certified\n\ by a trusted signature, it is not known with a high\n\ degree of confidence that the above key belongs to:\n" #Untested [PGP_NAMETRUST_UNTRUSTED] us="WARNING: The above key is not trusted to belong to:\n" #Untested [PGP_NAMETRUST_MARGINAL] us="\ WARNING: Because the following name is not certified with sufficient\n\ trusted signatures, it is not known with high confidence that the\n\ above key actually belongs to:\n" #Untested [PGP_NEWTRUST_NOT_TRUSTED] us="\n\ WARNING: The above key is not trusted to belong to:\n" #Untested [PGP_NEWTRUST_PARTIAL_TRUST] us="\n\ WARNING: Because the following name is not certified with sufficient\n\ trusted signatures, there is an estimated 1/%-ld probability that\n\ the above key may not belong to:\n" #Untested [PGP_NEWTRUST_NOT_TRUSTED_SIGNING_KET] us="\n\ WARNING: The signing key is not trusted to belong to:\n" #Untested [PREVIOUSLY_APPROVED_KEY] us="\nBut you previously approved using the key with this name.\n" #Untested [DO_YOU_WISH_TO_USE_UNTRUSTED_KEY] us="\nDo you want to use the key with this name? [y/N] " #Untested [DONT_TRUST_SIGS_FROM_REVOKED_KEYS] us="\ WARNING: The signing key has been revoked by its owner,\n\ possibly because the private key was compromised.\n\ A signature made by this key should not be trusted.\n" #Untested [YOU_HAVE_DISABLED_SIGNING_KEY] us="WARNING: You have disabled the signing key\n" #Untested [KEY_HAS_EXPIRED] us="WARNING: This key is not valid for use after %s.\n" #Untested [PGP_NAMETRUST_UNTRUSTED_SIGNING_KEY] us="\nWARNING: The signing key is not trusted to belong to:\n" [MESSAGE_IS_ENCRYPTED] us="Message is encrypted.\n" [GETTING_KEY_FOR] us="Getting key for %s.\n" [LOOKING_UP_HOST] us="Looking up host %s\n" [ESTABLISHING_CONNECTION] us="Establishing connection\n" [SENDING_REQUEST] us="Sending request\n" [RECEIVING_DATA] us="Receiving data\n" [CLEANING_UP] us="Cleaning up\n" [COMPLETE] us="Complete.\n" [ONE_KEY_RECEIVED] us="One key received. Adding it to your keyring...\n" [MANY_KEYS_RECEIVED] us="%li keys received. Adding them to your keyring...\n" [UNKNOWN_PROTOCOL] us="Unknown protocol %s.\n" [SENDING_KEY] us="Sending key \r" [RECEIVING_RESPONSE] us="Receiving response \r" #Untested [NO_KEYFILE_SPECIFIED] us="-a argument requires a key file or URL to add to your keyring." #Untested [UNABLE_TO_IMPORT_KEYFILE] us="Unable to import keyfile \"%s\".\n" #Untested [ADDING_KEYS] us="Adding keys:\n\n" #Untested [UNABLE_TO_CREATE_KEYLIST] us="Unable to create keylist\n" #Untested [NO_KEYS_TO_ADD] us="No keys to add \n" #Untested [KEYS_ADDED_SUCCESSFULLY] us="Keys added successfully.\n" #Untested [INVALID_SELECTION] us="Invalid Selection. Please try again.\n" [TOO_MANY_MATCHES] us="Too many matches; aborting!\n" [CHOOSE_ONE_ABOVE] us="Choose one of the above: " [PLEASE_SELECT_A_USER_ID] us="Please select a user ID %s:\n" [PLEASE_SELECT_A_USER_ID_WITH_SIG] us="Please select a user ID with a signature %s:\n" [PLEASE_SELECT_A_KEY_WITH_USERID] us="Please select a key with a userid %s:" [PLEASE_SELECT_A_KEY_WITH_SIG] us="Please select a key with a signature %s:" [NO_USER_IDS_SELECTED] us="No user IDs selected %s.\n" [PLEASE_SELECT_A_SIGNATURE] us="Please select a signature %s:" [NO_SIGNATURES_SELECTED] us="No signatures selected %s.\n" [NO_KEYS_SELECTED] us="No keys selected %s.\n" [A_USERID_IS_REQUIRED] us="A user ID is required to select the key you want %s.\n\ Enter the key's user ID: " [UNABLE_TO_ORDER_KEYSET] us="Unable to order keyset\n" [PLEASE_SELECT_A_KEY] us="Please select a key %s:" [UNABLE_TO_CREATE_ITER] us="Unable to create key iterator\n" [NO_HTTP_SEND] us="HTTP cannot be used as a sending protocol at this time.\n" [UNKNOWN_PROTOCOL] us="Unknown protocol %s.\n" [NO_KEYS_SELECTED_FOR_EXTRACTION] us="No keys were selected for extraction.\n" [ENABLE_THIS_KEY] us="\nEnable this key? [y/N] " [DISABLE_THIS_KEY] us="\nDisable this key? [y/N] " [KEY_ENABLED] us="\nKey enabled.\n" [KEY_DISABLED] us="\nKey disabled.\n" [CANNOT_TRUST_INVALID_KEYS] us="This key is not valid, and cannot be assigned trust\n" [DO_YOU_WISH_TO_CHANGE_INTRODUCER_RELIABITY] us="Do you want to change your estimate of this key owner's reliability\n\ as an introducer of other keys [y/N]? " [NO_CHANGES_MADE] us="No changes made.\n" [DETERMINE_IN_YOUR_MIND] us="\n"\ "Make a determination in your own mind whether this key actually\n"\ "belongs to the person whom you think it belongs to, based on available\n"\ "evidence. If you think it does, then based on your estimate of\n"\ "that person's integrity and competence in key management, answer\n"\ "the following question:\n" [WOULD_YOU_TRUST_THIS_KEY_AND_OWNER] us="\nWould you trust this key owner to act as an introducer and\n\ certify other people's public keys to you?\n\ (1=I don't know. 2=No. 3=Usually. 4=Yes, always? " [UNRECOGNIZED_RESPONSE] us="Unrecognized response.\n" [UNABLE_TO_SET_TRUST] us="Unable to set trust\n" [DESCRIBE_CONFIDENCE_AS_INTRODUCER] us="\nDescribe the confidence you have in this person as an introducer.\n\ What are the odds that this key owner is going to be wrong about\n\ a key which she has signed as an introducer?\n" [CURRENTLY_INFINITE_TRUST] us="(Currently she is listed as having essentially zero chance\ of being wrong.)\n" [CURRENTLY_ZERO_TRUST] us="(Currently he is listed as not having any confidence as an\ introducer.)\n" [CURRENTLY_HAS_PERCENT_TRUST_START] us="(Currently she is listed as having a one in " [CURRENTLY_HAS_PERCENT_TRUST_END] us=" chance of being wrong.)\n" [ENTER_A_TRUST_RANGE] us="Enter a number from 1 to 2 million" [OR_HIT_RETURN_TO_LEAVE_UNCHANGED] us=", or hit return to leave unchanged." [WILL_BE_WRONG_TIME_TIME_IN] us="\nShe will be wrong one time in: " [DO_YOU_WANT_THIS_KEY_AXIOMATIC] us="\nDo you want to set this key as axiomatic [y/N]? " [PUBLIC_KEYRING_UPDATED] us="Public keyring updated.\n" [NEED_OLD_PASSPHRASE] us="Need old passphrase. " [NEED_NEW_PASSPHRASE] us="Need new passphrase. " [ENTER_IT_A_SECOND_TIME] us="Enter it a second time. " [PASSPHRASES_ARE_DIFFERENT] us="Passphrases are different\n" [CHANGING_MASTER_KEY_PASSPHRASE] us="Changing master key passphrase...\n" [PASSPHRASE_CHANGE_FAILED_MASTER] us="Passphrase change failed for master key.\n" [CHANGING_SUBKEY_PASSPHRASE] us="Changing subkey passphrase...\n" [PASSPHRASE_CHANGE_FAILED_SUBKEY] us="Passphrase change failed for subkey.\n" [CONFIRM_NON_AXIOMATIC] us="\nDo you want to unset this key as axiomatic [y/N]? " [CONFIRM_ADD_NEW_USERID] us="\nDo you want to add a new user ID [y/N]? " [ENTER_NEW_USERID] us="Enter the new user ID: " [NO_NAME_ENTERED] us="No name entered.\n" [UNABLE_TO_ADD_NEW_USERID] us="Unable to add new User ID (%d)\n" [CONFIRM_CHANGE_PASSPHRASE] us="\nDo you want to change your pass phrase (y/N)? " [CHANGE_PASSPHRASE_FAILED] us="Change passphrase failed (%d)\n" [CONFIRM_SET_DEFAULT_KEY] us="\nDo want to set this as your default key [y/N]? " [KEYRINGS_UPDATED] us="Keyrings updated.\n" [TO_BE_REMOVED_FRAGMENT] us="to be removed" [SIGNATURE_FRAGMENT] us="signature" [USERID_FRAGMENT] us="user ID" [KEY_FRAGMENT] us="key" [SELECTED_KEY_HAS_ONLY_ONE_USERID"] us="Selected key has only one user ID; can't be selected %s\n" [FOLLOWING_OBJECT_HAS_BEEN_SELECTED] us="\nThe following %s has been selected %s:\n" [UNABLE_TO_REMOVE_OBJECT] us="Unable to remove object\n" [TO_BE_SIGNED_FRAGMENT] us="to be signed" [VALIDITY_CERTIFICATION_WARNING] us="\n\n\ READ CAREFULLY: Based on your own direct first-hand knowledge, are\n\ you absolutely certain that you are prepared to solemnly certify that\n\ the above public key actually belongs to the user specified by the\n\ above user ID [y/N]? " [KEY_SIGNING_CANCELED] us="Key sign operation cancelled.\n" [KEY_SELECTED_FOR_SIGNING_IS] us="Key selected for signing is:\n" [KEY_SIGN_OPERATION_FAILED] us="Key sign operation failed\n" [KEY_SIG_CERT_ADDED] us="Key signature certificate added.\n" [TO_BE_REVOKED_FRAGMENT] us="to be revoked" [YOU_DONT_HAVE_THE_PRIVATE_KEY] us="You don't have the private key corresponding to that key\n" [SIG_ALREADY_REVOKED] us="That signature has already been revoked.\n\ Are you sure you want to add another revocation certificate [y/N]? " [SIG_REVOCATION_CANCELLED] us="Signature revocation cancelled.\n" [CONFIRM_REVOKE_KEY] us="Do you want to permanently revoke your public key\n\ by issuing a secret key compromise certificate on this key [y/N]? " [CONFIRM_REVOKE_SIG] us="Do you want to revoke this signature [y/N]? " [REVOKE_CANCELLED] us="Revoke cancelled.\n" [UNABLE_TO_GENERATE_REVOCATION_SIGNATURE] us="Unable to generate revocation signature\n" [KEY_REVOCATION_CERT_ADDED] us="Key revocation certificate added.\n" [SELECT_SIGNING_KEY] us="Please select a key to sign with:" [UNABLE_TO_OPEN_KEYRING] us="Unable to open keyring\n" [PGPINITAPP_FAILED] us="pgpInitApp failed\n" [KEY_IS_ALREADY_REVOKED] us="That key has already been revoked\n" [USE_FORCE_TO_ALLOW_OVERWRITING] us="In batchmode, use +force to allow overwriting of output files\n" [INCONSISTENT_RECIPIENT_SET] us="No algorithm available that all keys support.\n" [UNKNOWN_ERROR] us="Unknown error code %i!\n" [VERIFY_REMOVE_KEY_PUBLIC_PRIVATE] us="\nDo you wish to remove this key from your public and private \ keyrings?\n[y/N]? " [UNABLE_TO_ITERATE_KEY] us="Unable to iterate key!\n"; [CANCELED] us="Canceled.\n" [REMOVED] us="Removed.\n" [NEED_FILE_TO_SAVE] us="Save file as [%s] " [PGP_NEWTRUST_NOT_TRUSTED_SIGNING_KEY] us="WARNING: The signing key is not trusted to belong to:\n" [TO_DISABLE_OR_ENABLE] us="to disable or enable" [TO_EDIT] us="to edit" [SELECTED_KEY_HAS_ONLY_ONE_USERID] us="Selected key has only one user ID, can't be selected %s\n" [NO_DEFAULT_PRIVATE_KEY] us="No default private key\n" [MULTIPLE_RECIPIENTS_MATCHED] us="WARNING: %i matches were found for recipient %s.\n\ This may not be what you intended.\n" [ENOUGH_THANK_YOU] us="\a -Enough, thank you.\n" [SEEDING_RANDPOOL_FROM_DEVICE] us="Seeding entropy pool with up to %u bits from %s...\n" [COMPLETE_READ_NUM_BITS] us="Complete. Read %u bits.\n" [RSA_AND_DH_RECIPS] us="WARNING: You are encrypting to both RSA and Diffie-Hellman keys.\n\ If the RSA user is still using PGP version 2.6.3 or earlier; 4.0; or 4.5,\n\ she will not be able to decrypt this message.\n" [ONLY_ONE_USER_ALLOWED] us=Specified operation may only be performed on one argument per execution.\n [CANNOT_DISABLE_AXIOMATIC_KEYS] us=You cannot disable an axiomatic key. Use pgpk -e to change your\n\ trust of this key, first.\n [RETRIEVING_URL] us="Retreiving %s:/%s:%i%s\n" [ADD_THESE_KEYS] us="\nAdd these keys to your keyring? [Y/n] " [ABORTED] us="\nAborted.\n" [WARNING_NO_MRK] us="A requested Message Recovery Key (MRK) for this key was not\ found.\n" [MRK_FOUND] us="Message Recovery Key (MRK) found. Will also encrypt this message\n\ to Key ID %s.\n" #Everything from here down is automatically generated. [PGPERR_OK] us="No errors\n" [PGPERR_GENERIC] us="Generic error (should be changed)\n" [PGPERR_NOMEM] us="Out of Memory\n" [PGPERR_BADPARAM] us="Invalid Parameter\n" [PGPERR_NO_FILE] us="Cannot open file\n" [PGPERR_NO_KEYBITS] us="Internal keyring bits exhausted\n" [PGPERR_BAD_HASHNUM] us="Unknown hash number\n" [PGPERR_BAD_CIPHERNUM] us="Unknown cipher number\n" [PGPERR_BAD_KEYLEN] us="Illegal key length for cipher\n" [PGPERR_SIZEADVISE] us="SizeAdvise promise not kept\n" [PGPERR_CONFIG] us="Error parsing configuration\n" [PGPERR_CONFIG_BADFUNC] us="Invalid configuration function\n" [PGPERR_CONFIG_BADOPT] us="Unknown configuration option\n" [PGPERR_STRING_NOT_FOUND] us="Requested string not found\n" [PGPERR_STRING_NOT_IN_LANGUAGE] us="Requested string not in language\n" [PGPERR_KEY_ISLOCKED] us="Key requires passphrase to unlock\n" [PGPERR_KEY_UNUNLOCKABLE] us="Key requires passphrase each time\n" [PGPERR_SIG_ERROR] us="Error while processing signature\n" [PGPERR_ADDSIG_ERROR] us="Cannot add signature\n" [PGPERR_CANNOT_DECRYPT] us="Cannot decrypt message\n" [PGPERR_ADDESK_ERROR] us="Cannot add encrypted session key\n" [PGPERR_UNK_STRING2KEY] us="Don't know how to convert pass\n" [PGPERR_BAD_STRING2KEY] us="Invalid conversion from pass\n" [PGPERR_ESK_BADTYPE] us="Unknown encrypted session key type\n" [PGPERR_ESK_TOOSHORT] us="Encrypted session key too short\n" [PGPERR_ESK_TOOLONG] us="Encrypted session key too long\n" [PGPERR_ESK_BADVERSION] us="Encrypted session key version\n" [PGPERR_ESK_BADALGORITHM] us="Encrypted session key algorithm\n" [PGPERR_ESK_BITSWRONG] us="Wrong number of bits in ESK\n" [PGPERR_ESK_NOKEY] us="Can't find key to decrypt session key\n" [PGPERR_ESK_NODECRYPT] us="Can't decrypt this session key\n" [PGPERR_ESK_BADPASS] us="Passphrase incorrect\n" [PGPERR_SIG_BADTYPE] us="Unknown signature type\n" [PGPERR_SIG_TOOSHORT] us="Signature too short\n" [PGPERR_SIG_TOOLONG] us="Signature too long\n" [PGPERR_SIG_BADVERSION] us="Signature version unknown\n" [PGPERR_SIG_BADALGORITHM] us="Signature algorithm unknown\n" [PGPERR_SIG_BITSWRONG] us="Wrong number of bits in signature\n" [PGPERR_SIG_NOKEY] us="Can't find necessary key to check sig\n" [PGPERR_SIG_BADEXTRA] us="Invalid Extra Data for Signature\n" [PGPERR_NO_PUBKEY] us="No public key found\n" [PGPERR_NO_SECKEY] us="No secret key found\n" [PGPERR_UNKNOWN_KEYID] us="No matching keyid found\n" [PGPERR_NO_RECOVERYKEY] us="Requested message recovery key\n" [PGPERR_COMMIT_INVALID] us="Invalid commit response\n" [PGPERR_CANNOT_HASH] us="Cannot hash message\n" [PGPERR_UNBALANCED_SCOPE] us="Unbalanced scope\n" [PGPERR_WRONG_SCOPE] us="Data sent in wrong scope\n" [PGPERR_UI_INVALID] us="Invalid UI Callback Object\n" [PGPERR_CB_INVALID] us="Invalid Parser Callback\n" [PGPERR_INTERRUPTED] us="Interrupted encrypt/decrypt\n" [PGPERR_PUBKEY_TOOSMALL] us="Public Key too small for data\n" [PGPERR_PUBKEY_TOOBIG] us="Public key is too big for this version\n" [PGPERR_PUBKEY_UNIMP] us="Unimplemented public key operation\n" [PGPERR_RSA_CORRUPT] us="Corrupt data decrypting RSA block\n" [PGPERR_PK_CORRUPT] us="Corrupt data decrypting public\n" [PGPERR_CMD_TOOBIG] us="Command to Buffer too big\n" [PGPERR_FIFO_READ] us="Incomplete read from Fifo\n" [PGPERR_VRFYSIG_WRITE] us="Data illegally written into\n" [PGPERR_VRFYSIG_BADANN] us="Invalid annotation to signature\n" [PGPERR_ADDHDR_FLUSH] us="Cannot flush buffer until size\n" [PGPERR_JOIN_BADANN] us="Invalid annotation to join module\n" [PGPERR_RANDSEED_TOOSMALL] us="Not enough data in randseed file\n" [PGPERR_ENV_LOWPRI] us="Env Var not set: priority too low\n" [PGPERR_ENV_BADVAR] us="Invalid environment variable\n" [PGPERR_CHARMAP_UNKNOWN] us="Unknown Charset\n" [PGPERR_FILE_PERMISSIONS] us="Unsufficient file permissions\n" [PGPERR_FILE_WRITELOCKED] us="File already open for writing\n" [PGPERR_FILE_BADOP] us="Invalid PgpFile Operation\n" [PGPERR_FILE_OPFAIL] us="PgpFile Operation Failed\n" [PGPERR_IMMUTABLE] us="Attempt to change an\n" [PGPERR_PARSEASC_INCOMPLETE] us="Ascii Armor Input Incomplete\n" [PGPERR_PARSEASC_BADINPUT] us="PGP text input is corrupted\n" [PGPERR_FILEFIFO_SEEK] us="Temp-File Seek Error\n" [PGPERR_FILEFIFO_WRITE] us="Temp-File Write Error\n" [PGPERR_FILEFIFO_READ] us="Temp-File Read Error\n" [PGPERR_FILEIO_BADPKT] us="Corrupted or bad packet in\n" [PGPERR_SYSTEM_PGPK] us="Error Executing PGPK Program\n" [PGPERR_KEYIO_READING] us="I/O error reading keyring\n" [PGPERR_KEYIO_WRITING] us="I/O error writing keyring\n" [PGPERR_KEYIO_FTELL] us="I/O error finding keyring position\n" [PGPERR_KEYIO_SEEKING] us="I/O error seeking keyring\n" [PGPERR_KEYIO_OPENING] us="I/O error opening keyring\n" [PGPERR_KEYIO_CLOSING] us="I/O error closing keyring\n" [PGPERR_KEYIO_FLUSHING] us="I/O error flushing keyring\n" [PGPERR_KEYIO_EOF] us="Unexpected EOF fetching key packet\n" [PGPERR_KEYIO_BADPKT] us="Bad data found where key\n" [PGPERR_KEYIO_BADFILE] us="Not a keyring file\n" [PGPERR_TROUBLE_KEYSUBKEY] us="Key matches subkey\n" [PGPERR_TROUBLE_SIGSUBKEY] us="Signature by subkey\n" [PGPERR_TROUBLE_BADTRUST] us="Trust packet malformed\n" [PGPERR_TROUBLE_UNKPKTBYTE] us="Unknown packet byte in keyring\n" [PGPERR_TROUBLE_UNXSUBKEY] us="Unexpected subkey (before key)\n" [PGPERR_TROUBLE_UNXNAME] us="Unexpected name (before key)\n" [PGPERR_TROUBLE_UNXSIG] us="Unexpected sig (before key)\n" [PGPERR_TROUBLE_UNXUNK] us="Packet of unknown type in unexpected\n" [PGPERR_TROUBLE_UNXTRUST] us="Unexpected trust packet\n" [PGPERR_TROUBLE_KEY2BIG] us="Key grossly oversized\n" [PGPERR_TROUBLE_SEC2BIG] us="Secret key grossly oversized\n" [PGPERR_TROUBLE_NAME2BIG] us="Name grossly oversized\n" [PGPERR_TROUBLE_SIG2BIG] us="Sig grossly oversized\n" [PGPERR_TROUBLE_UNK2BIG] us="Packet of unknown type too large\n" [PGPERR_TROUBLE_DUPKEYID] us="Duplicate KeyID, different keys\n" [PGPERR_TROUBLE_DUPKEY] us="Duplicate key (in same keyring)\n" [PGPERR_TROUBLE_DUPSEC] us="Duplicate secret (in same keyring)\n" [PGPERR_TROUBLE_DUPNAME] us="Duplicate name (in same keyring)\n" [PGPERR_TROUBLE_DUPSIG] us="Duplicate signature (in same keyring)\n" [PGPERR_TROUBLE_DUPUNK] us="Duplicate unknown \"thing\" in keyring\n" [PGPERR_TROUBLE_BAREKEY] us="Key found with no names\n" [PGPERR_TROUBLE_VERSION_BUG_PREV] us="Bug introduced by legal_kludge\n" [PGPERR_TROUBLE_VERSION_BUG_CUR] us="Bug introduced by legal_kludge\n" [PGPERR_TROUBLE_OLDSEC] us="Passphrase is out of date\n" [PGPERR_TROUBLE_NEWSEC] us="Passphrase is newer than another\n" [PGPERR_KEY_NO_RSA_ENCRYPT] us="No RSA Encryption/Signature support\n" [PGPERR_KEY_NO_RSA_DECRYPT] us="No RSA Decryption/Verification support\n" [PGPERR_KEY_NO_RSA] us="No RSA key support\n" [PGPERR_KEY_LONG] us="Key packet has trailing junk\n" [PGPERR_KEY_SHORT] us="Key packet truncated\n" [PGPERR_KEY_VERSION] us="Key version unknown\n" [PGPERR_KEY_PKALG] us="Key algorithm unknown\n" [PGPERR_KEY_MODMPI] us="Key modulus mis-formatted\n" [PGPERR_KEY_EXPMPI] us="Key exponent mis-formatted\n" [PGPERR_KEY_MODEVEN] us="RSA public modulus is even\n" [PGPERR_KEY_EXPEVEN] us="RSA public exponent is even\n" [PGPERR_KEY_MPI] us="Key component mis-formatted\n" [PGPERR_KEY_UNSUPP] us="Key is not supported by this version of PGP\n" [PGPERR_SIG_LONG] us="Signature packet has trailing junk\n" [PGPERR_SIG_SHORT] us="Signature truncated\n" [PGPERR_SIG_MPI] us="Signature integer mis-formatted\n" [PGPERR_SIG_PKALG] us="Signature algorithm unknown\n" [PGPERR_SIG_EXTRALEN] us="Bad signature extra material (not 5)\n" [PGPERR_SIG_VERSION] us="Signature version unknown\n" [PGPERR_KEYDB_BADPASSPHRASE] us="Bad passphrase\n" [PGPERR_KEYDB_KEYDBREADONLY] us="Key database is read-only\n" [PGPERR_KEYDB_NEEDMOREBITS] us="Insufficient random bits\n" [PGPERR_KEYDB_OBJECTREADONLY] us="Object is read-only\n" [PGPERR_KEYDB_INVALIDPROPERTY] us="Invalid property name\n" [PGPERR_KEYDB_BUFFERTOOSHORT] us="Buffer too short\n" [PGPERR_KEYDB_CORRUPT] us="Key database is corrupt\n" [PGPERR_KEYDB_VERSIONTOONEW] us="Data is too new to be read\n" [PGPERR_KEYDB_IOERROR] us="Input/output error\n" [PGPERR_KEYDB_VALUETOOLONG] us="Value too long\n" [PGPERR_KEYDB_DUPLICATE_CERT] us="Duplicate certification\n" [PGPERR_KEYDB_DUPLICATE_USERID] us="Duplicate UserID\n" [PGPERR_KEYDB_CERTIFYINGKEY_DEAD] us="Certifying key no longer\n" [PGPERR_KEYDB_OBJECT_DELETED] us="Object has been deleted\n" $Id$ neomutt-neomutt-20171215/contrib/logo/000077500000000000000000000000001321473123000175365ustar00rootroot00000000000000neomutt-neomutt-20171215/contrib/logo/neomutt-128.png000066400000000000000000000233111321473123000222470ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxy|UOg%+ M]6 .3*sQ_w_棎 0⮣x+(J@BJUwu;|SuN:uyQJqsi@Z"""ȍ"*q8Q D$0}22Z%^'pt W)UЂ3" \ \>@%@J+18I^.v KNN999ZF?†/׿ދc7=z_|aJ[*N8A!"< ěuЁǓKrr_^^|`Z ':4{$''3{lFa+1{=( 3ۗs璑{Zx8 '" ~ӦMRTUU̺u밴nUJUhDW{tC . && ǣ%ihh@DXne[- 8Atwvꪫ 뇈uV>Lzz:eee/Wjh‰"n2; RRR?տm6JKKIII!..[v 5N DZ]taРA~*_Dؿ?EEExXvU[.p 1N=ٳTUU}v{>QJUH5_l"!!&-[jرk~;n?(""2GD>|"FD~D iܟk"@D2TyK;TFtAHHH0rPTTDmmX9vu?^:Z@MwL5 "ҩ LƤ f} fddcG#>$"Ӏ -Ny3~[wp<1{ˣfhÇ7Ծpȑ&NKKX~l;| J=C޽"55Gc:d x,ߋ2D`vp~ѣG u ..Cff]bϗ9f4f̘IJJzٻw]r0Q)Մn5,@RH@Duar^ECǎ l5 o rss5k466z =TU)h/ ?#N:nw̎SN9/766yz`NM8FZ6Ɯ9s9r$^ט; "۷EY#7'#Z g,|j"H7wn߾=={uLL "BN6Rp1Zhȑ#y=z46yꩧ@$$a*7mpx Rߌ".yӦMS"b,|駟n~gY`űcr뭷ҥ ' =>DX(0x`~GsExWw'&&#";vlwNv8z(笠"0a˗5ttMqF뉏gذa8p*u$TY7Ru#!!YfsNk7#h%#99O{<:wLffAl:vȮ]())!++={2qDV\#F[oe׮]޽qqq 6}QS")|@ "suwLL f͢W^lٲtRjSsM5X}/"?կMH!"ok%%#;ٴiwkxSN9:{1#-|h&D?&u pyѫW/<~~EsmHo`ܯkP?{ڵc\#0l0,X={(,,4O>dygKߣ ?`Obhn'Ofդz0,ӧOg~u$]Xs9+Wxx(**2GDHJJgϞlݺ]hjh=lYTݝC^^q?6~3#e8Uwtڕ3g pJ)<wuiiiPSSÐ!C(,,}RUUŊ~J4‡0 1 ~ӧOr^ߪ_ Dd4To@ʽ눍:t(PJѩS'KO>x^u_MFF+V0F}XZ՟'| 0m+{M`߉*|}3gΤs}ک80p"L=>CsOEu[ <4iqE* z뎁2yd_:ܒzg;w̖-[D)d81W4#W5Ф-Zfvh bTp5&ڮsCkX+Xx1Wf|3EEEZ[ZNݻVn))1u "Ih>@{^ϟ9Cu9ŋ3vXDbx ^/IIIz+  `D!>>ޖb p#%^yyykzٶmVD(--ANZZ FZhN·!I"R5['HpNLLdT/Ik^co;v,111B":tٶmB2E$C$"2mF͌osq;`d'ZV?z544hMHH㏛ I>yцY֪Z6|Ks, `#o>ʡ hEHpNIIa„ MSN&3PJŞ=v"-"FljDdpo86:QIq$@vva4a|+2e ! |z(0:bԨQ u|oӦ \{wytUVmjCc*GGSD]k6Dk]7nqInoǥ^J6Wp͚5XRL2ňG\\\rm!zYlKXOORI1OiRS@RD# N\ Swϟ?޽{}ux饗̏_j^.S38s=һln{A32x`c֯غu+:t ))ロ;wc>W_ uhk+іX^Ϸ~SJ}&mwׯ51u`tcƌ I[NIoߞs2rHCJJ |;wwz՚!2eG쯻tٟ9z?E`;X5`O=SSS:oQP aCD`ZۻwoڵJH *@~v9p)Ǜo1y/\[HSò HKm靫b!@)Uw"bWӾ-YSk'77p׬CUHvZճAs_OՓG>En(Eaa!t0`u,_LT!"mSEI2`W5{;?'#RʘA=)$T'8 >V87\= ډH+VKk׮u?_)v]u4x*Hmb20tP#NݸޣT76uLX7٘p C֬f.DqY7H{3 COCvo~>r1p{r+u192XZE2}vX $$$0}t+ҰX܌D$5f+ **XPLru\\v]{ꍞ&V*hκefw}28m4բM.(R?@{NW\Fz ]؄cVk/a2h_@PJmdfÆ i9>>3g_f'b8}xfTx8q#_$@ͽ@Y={j?#Dlg;rG_?ƍO6?l6pCDuulG*?!~MQ07A[s|_E\,C3a` {:iٳX*aCD_@+ÔHo NZ V 9mRZUʕo]8כM?3jZym۶QVVP 0cǚ_HRymlܨ&$$︃ڴi5jO`Qm-\C9 >X8pS $%%1gf⓽'rV>S\B6MF@īR*͛Gz>sEį "2L=fr3i$nN~Ӓ55gt-@B 2k 80*qƛd%*i-o&LJA$ ~csP?RSSQ.,mK@Y7W^<쳌=Z;\e8M=W).8vW{zWwC^x1~}.bmZ*8yLfRL1@1pR@P(_"`Cu]۷xx'o̯jDkY_(ActGfj̙3.?!6663d&Fz~}[\}2{<Ώui% k+CM֮]fOSD.Z YHX"EAp=ә;w~\֕!ar-tرIB ZYY srwI g)eKZ`n|<Ǝwgo'V:57Z|ܹCa#2J7 bGOΝ;yfXEk2EkE.gs)ъYsƏo"@uʦRj>cB)R2y9oȐ!⋬[ 6a|#..ƌgaoB)篻twGj=S{ddf2`@^M=.vI.\"RF " bHujj*yyyL0C]]=zCvvA&ki1;1c}_WMwW^aֵҳgOC:J@uŀg|"Fb1 殮qꩧx0Hvv6_~5/2x=xe/}vp;h>WJc" 0f|5&4Ӎ*n>JOgu>XН*(++3LMn0^0a[!tߍ4@(HJJ?۴ {pKu5/>4eee:" + tիEڇQ57Gif:}kkY57D*<7V)VUSOQ^^C9Ĩ7&P@M5W`vL4ٹ|&l 5h}:ڴ<^έ`ETWWVYsݳ7n3L <Am]6Ye :s:ԥ_6^2FĀ kld|Y}cZYe~0Xۯ^`!9L阧"` ,PG]X{_{ǽ @b^^\H4{fla腊eԨQaPLN$D; [7 lz8 O?Mee&^ 7nZZ/d0Rm a0`III 6TBcW3!<$GmV%|O>dU8?ԦS0éLrjB!m[Vq~c#g9O=EII+OyzUp `H\DHrjN)))q BI^/c;|,\EӯN:$[v:~~u ##,Me%$hH%Kduf?{uVva$JY >mj h?wv:]uNE]mܮ\VJ/Cq}u5cBvO0S9餓w466RYYi nS^^n,Nq@%pR깐!@?LVB.Oݒ rp;C11{hD[+49ˢX[Ͱ~v"xW9tguׯ端jiLRp׆f/UJ._|ጳ]q]]w=7*?lHooEٍ{lOƬƇDjDjup/?G^LxЊ!kDXTʆ .vf7﨩9nnРAxހM@'ֲm6~0cLaa! MޡF8Y8q[E$-p5"}7cƌ^{fff&i݆R>Lii)lݺݻw79CGbb"[}m7Ԉ-n L \O>d͛v^ucxzHm۶5ՏFZ1pa[Ik<Bm4kt5W4tDɋObb"7pqAT:aÆ ~PJ3\Di|$˕0E"N>l233#GXtiX@!0[!hCԢeDM EDRJ >pMYYYya}e:IDATY Bǎ)**bƍőh+0&Ј>g>RKj:pZK J){u-P4\r%;{g…~=++ Ƕm۬+c?C)hAJ6F|p*˵h$=F_r\Kt JرcXM͛7xb-F:dkԇ@f䇢BȽJŦ{F$ rI{ 8~s׮]\\1E#eh *VRꗠ\@DzUjmP7K/.l$ZEk$)3Mm۶ff ))M 2tP.\{ŠiLxq giriw}zUD222IȠ"sITJݏe` Ns9޽{}ׯ٪ggrkO 0l:6+_:ҥ #F`ذadgg(--e͚5())aԨQƾ_\oX "QX p)0c &WAg&@4"u+W"a#G!} ??_v8q"F]]#33Sƒ%ژR ڶmЩS'X؃DHutDÇ3c uoС+g$""P@޽{<͛ǂ hhhcǎ}ٌ3ƕPtz,]0~Q2}g%`HLL$''u`c-);!lFYH 5gp嗓ÃCLL ;wf޽Kdee 0ӞZfe6m4pbٿQi+Zn`x<w#:, 0&F Qq"9`Utbnvۢiyˆݘo!ӆAD#d7fbZO2?EP^^Niiᶴf>& В0 (pC_huۻ+,K+Ԃ jxCC zch ФU__7-,WNY:lRJmoظqcT#dv֚0On!pn&OщMp]jji޽oʸMGu+v /IEƮ%%%N!}@3;f5`Ϟ=ֱ 4aOZlmWh [oqQ_lpo߾62V6{ wX5V+(*Dغu+jV\' < u`C5y1i {y|fkk t:XDNvڵ+f͊bgaRS]OJJJp S)5>fEHE?Ǣf誔jhG[[[ δs[  ymu ( ȚH! 5wCEHRu>-vㄧR 8zγˁv~ * ‡0-H6)b-Y'? k0IENDB`neomutt-neomutt-20171215/contrib/logo/neomutt-256.png000066400000000000000000000460521321473123000222600ustar00rootroot00000000000000PNG  IHDR\rfbKGD pHYsnnWNtIME)7<KIDATx]U5֍ 4ED삂PbAAҋD({+(w]ϟu3//)ro}3=wƼy+nkı3`Wq877oUjrlq`D8x#Vd޼U>ېcGs<8묳.˖-m\_7oX̉mӦM }[ύ5vp4ÛY3fyW-ZR^ ,M7vuxV8 kժ 2$$LK.رcm*k޼̪sBv-2K/B@fl߿o ggwF.M2;p/@GQ*]бcG ěX}mG_~ .,O?F96ś9C-E|W\qEΝ̘1Կ&C+0!{o-U_y%Ϟ=;3fL(!"ճ_7o)oᒝ2'xb,] X\Jy+7NawI b ;/C뮶xV#_K|*>s\sMx5kRp%޼Pc~.SO=3g^{m ,(Zy+ͧxaiɯ_wugGk-P~.V\?l(xԿo cR޿o߾%䧈Rcip„ ZuQ篿sH&"~DZy+]H %K䗉/zMB̛76ſo DۗD|xt`F7Rg+=V(C1N1ceCQ42ʐ$oUʺSxȑNGH_ `ᦛn aI]_n1?,㭲FlA?*EH~|!n;dYQL/s8u̟H gQD|ojժG}a83>b;1;ec{s\) Ib(ON8$?-zk P> Ӹ 72b3c{ߵwk4Vy2Tg^`ˣ[B~Dm +9ȨbJ=+Q͔]ÈLQg"=XIB`B4cjKYn#QPĢ?:;*w254nH1^`ĉ!o]JL[\EmS[ǓaI&%.ƾٽ>\rl&vV/?sH `;( I_l>.t=O$poX!OAN*Ў^mp> _4A-;{[n1엗{u]=q<f*9@JdD5 0狗. f}ej8ϸ7h y(ҏ?gqFpwYxFg\@@=z5|^JH +oxQ[UcB^F|{)KH.Ù}ݷd;wi'>CK|MBαoS7l0 e L9sW{ &Ol{M +KРA)jD@K.qTCߥ 2&P>죝]/@&?a8v;XXL&(~[mUw,]"t6Mͣij/+bŊ{-LZEd^B>EY@t&O>=pg[lkDNeǭ,}& ߔ&>F%c_LR9e^{QW]ȏ> @0! 9 MKb-hٲkĉɵ}wғ #~ T0-®Uw}eX.g3[QFpdž.V/  \Q7d8K}(Ɛ}&zo =M}]/|R,߶:uqxI1Wo/lٲd!q(VwGr]om)^Pcz&M wp<@r!2 QjL2p/sLDrYr1!@~o-c>GkXݥ۵kL6ͶFߤ61B~@jI=Eh/<fЖklSNq&?H`Lo#? Ȏv,=!d!)1J~D HD=@l,aeBI# pܶ]lX֐j }fjlwޱ3_% OڵÐ<ב_%>Z sУ GH%}cBрN0y Haa }ho#?J*aO&?MaUQ;BجBT$nj`ƌ ~.!"B"'|Q>a# s6 }WɏYldžGD~A5kh=`RMDɏ<gڵK?wq 7 =J~5-/oԶm܏#뼿*iȯB'Q1kXE:c_ԩS]:vl\Ӓ{7͛pOƱ|2dHuO*`Io0/g}M$!װcm3ґ@ 5i<#% -w%2|BI 1iu|!X";wnx!KawmCyի7&;Z8d#?fS~\IJ~O=TBD=^pO79 M"36 1_;6 P4?>wxKUL[*dP#Jda2"s{=u5p7sb#@x&YL\//Ok! A(cx{dzXv?w]S {t_@s̉l sȜd,qm?E~! 3|xX&qLϖ@[`f6fP˭Zr"?AXfd?M˰y[YY,4h U ?s!(0 L9v?Ѷ:Y3*Qz頃^۾c1{VZX I noׯ!DęwwAu4lЍe ojgqYX8o0M@%*t] 3!2gM0dQ~2qGaKfP@7W?BY&$&2u>< M6.~|Y 3blNYvۚ20kbG"2L:&@؏N%tߍܬ5ˌZԤL~l@x2Mn;6D%G6\Eu`༾o6ANJoPE?{Ug6Ά>jO_lIۇY}$ 1+%exUT{w軒:I?8 >ɍ^`ʾ86  h@%p̷xNS]vMacQT6'/}?7gqоI D 4`5@8#{V? ZhQ| YpsVj+]{仓`p8.-@x2`or@5]Y0 @L$60[o"1)J8?^.fQSΕBp;^ёoM(c9\ @q"$&^G:G#b?&ŧ.fg2O[诒P+*o2ї(9j!$*p*H01`Հ;S7]nƝf[_i Aآ7{)#cqQ*zY/ m@L e+wP fDrrXkj:8[^RޟGrB @@:UnN@+87 ;ը\r[mBEK9*T]xqjG%O{}Lu:L(@ td)0I@x`gxgH(bgH nM|z,wvF7ψmq`cǎT=y_b먎6zh LЕW+#ԅ` ueu89NI|]zuUxᐗ'pph?rܧ0a ԉ@PU HEA̙DaÆ)c2&oc {]`_ᘦ4#(@'R 5Z$I=X6@Sk&#¾_v}s]=\?f̘O?9~_p|>[q98 9X9~eа^Coɱiev`LB~tVUo YH=r:`K E&P@w|h7.8n^_')<'Gh1߄x6<#*%1Cf+!#Go9<ϱ 9y}e?Sn.# 2f:Hhf).h_J5' 0,:1&>ka\Gۦ__&Gla_|_q|xW9^x/\GĉK hoD<@@P@'bB]of o_^AWsı^K/87e 9N߂Z{ȝ!#NFoב_tޖU1it2@ LUt?k#:j{xp|x8qƊUm)HR\ lQ%6!#>E~8p`kr =x}$sܣx}T.<8ㄿ~0c z"w1yğJ~;Y'VQC>3l~\ĝH` ,6ߑɯT/ ZdI&ϟKT"iq ?x饽~009薣uCEm)M?: `cjUA,\c7+$iQ@t^"  9/ /zQhrE?c|i ?G }w="w&ğ)$cX{c.ǫVk!lj(`5[P ߜjHWWr\qO8EqYr#~Ǡv]^;)AbS/{Ue O|Zc$q±R_0 NDT!Љ*u䷅wYydN4^42^ =+,cǐ^?؃cW8 k)tE](0MCvҥXuӝEx R\|M` DHo"=ռZ5kGiKi>z(^8i? ~  ZL}O+z}e“ztF`֬Y6׬]ɏ-x?O-Š_!CÀ$Q@R@Ϛȯ:  4kҌܼz}5Wx}$x9F+^?G#8<&ם*z]JxtB#;[ɏJG9 !_q\ @jJMڢ` Pe{޽{Ӻyk2i닄{qtW~{-8Zrl/U8oV;p7{?%E wt,&_r̊vZqh4 #jʰ,P#_"LXM7 /z&+^0!ߍcGo1FA0 mi6#6cPtB HWB`ypp܍7͛ 5D@DH Lw!ܻ_RZ;~v޼S#g8Ts~~e'PqSN9&șoSBp| C Č}7 \; l"U \ WG|Q5Y;|/)yGBpDZѶjqT`#zUܔgFTP#J$'P o,ٱC,6H#A:c~I'T\4"5Wx}$qx-8 >"؀)NrnePl~R/:C}+I{m͢" FR5|DIE@'A%Lz7_Ϩ,:p;j"G;ptxFK{}պ|xe=)bՉ?SSq|Pq(} qԔO*mRP]" N (눯[3_8u>ֆ/}kN ={*^#G;ׯñai a}tlR&[{ 1Wǝ*p5J32>H(8G,VL4)CS$E@ o"~\FY,0oъcSo'k쵠+6=YHEӽ@VB >[O}wD>8.N:u8` {m33(m|uW99p4`6JkȈ 6(˝ pnb-J${r @1HZDܤQ@R0 d!Љ wk/GG~ѫW2m?a 5Wx(ׯQgGv .t&MQ`W'|'$,֭=؆IAJwF ڼxMQf.杞e7^?J->gX+]XYS[:ySyipΰY|xx& xAZD}Ea$8_msfUaJW7^_$4tO`/逐J˵:*TL~6bZNr]y }D$cJ˃؆65T@8_%/Lx^䨨E{y#~d?]_.4q.\݅6er2 g14ztǖ:FlmBx,qI˃IqE P`*}30O;wq#=bݼSk}`=L}bі˳Ȗ2 |> !z5kP= ؄GĴ6ɓ˜נiH OU l^&L$gDUߠzpr󓃟:dܼw;M٦>q B zZvmឈ'Y[hMW⳨d@= O8 )-,v6BI,U\\EE(A0ޕq/{LY[2-7ڰT9Ol"EwT 7n Npipe,W4v)ᦞG G9^J!Bime㈀b`eۈJ~<+ԕm ךZ2i: RviГ XxuM1%n.*c>~+8^KzH׬Y_. =C.t"`ؠ~_%>m} 'M6y8w ;&Xʖت`[eslhЅumQ~LaUn`ĩ(iEUS};rի(&F˷4>9D#H/%" @ER<(jd!B`ۈ_ܷBh# 8l@;MPcǎY , `YyjS9xqKQ3lO>d L"@E6)%Ư!NIAtр,&A:&gA~ "$XSNҾ%aȌ,fOW}u ;w?S- 8u,"tR| (˃IE脁|-W'!@&%'c0aB9oߞ0dZ~q clKo֝" jp u#3;v~ \D@a$!D,*"q 'Ǵ˃cdpQ0}^}/{A~!X殰KOvi&NdZ\gĈ[n77k,\Cƽ>3e*T QaMs$S XF:)hW!P& :Sy}u߅`3ep>cu8$dRN9t&c /O3{X3Fu7 N(<asnJ@Zj."F(AޅTȟcA׮]KB'(ɼ{Sk8:Fc[txt" *&A~_Y_ !X&d.jN~,sN1MS/{q @.1$dq)[L" G0U,sGN*q܏\ $E@1DL`qo#:p?J|'9u&I// W  /@VG~&!E<: Ql="}Rxdc?|~lCb`בC9_- \F3gS1q,Jg} t GiυvhDJXqYTE[l"Gtb\_]/{ӧMv(v#OS!_MII7z2{D&]"J R 3Q6b$@R!0 B\דK%>'%?`GY&`JM32W˞_&Ia@gE]i62?\sرP" M (a>+_ӅTo":Gߊb'?N@XN>*ׯ__ Q@>kqR QpF`ƌ%"DT1p?),^6(vD5РAwI_ˡL>,q ,x?0ZO#KuԮn҉Nlb@ &ķ_s%?,nӏ_1/&M4:Wgu2 >hN:JEO#r#eXUT1D_kq;wgZq`. /`*@\@=7oa)hanF 8p&:10 5tw%~\@oE 5y&r .H D{b꜔h($* 5j(7o^)0 *&ApzmC~J[oʈ`ۊ 4t@bU!ܹskl=+<$6m,X`:!pP'+u}JYb +vAa3puJxn x|$bUT1 B\׳%WT;=+Bjۦ X)dhHd jI0`ylK_HHS P':P _qչ3"ZJƁ I ^%}#ys }_>^ppҞ$P樌,Rd DÁ.Q_xhC%nHj(4iĕ8}qʈ?Հ[@Mtg9 T )2 p * -@n"6ܠAJ\&%=žlM3!J[/P^=!62`0 tQ:0I._w1lذݡuцL\&'%=0u԰"D뿙|iLjF\$] - E@*]ȟMhw!x'b6\@o`H%0@-Z$9 g[t/k05Ne% I\W!p%~Rӯ_\0UuќQIiHM'iO8g?Gcq%QZf@n^avۑjDJ\(!p!qC|yQ f$s 8*Jnè r~ JҨ?%,WΛ45(5qppԡNT!0 3:I)]0i %86+c]"~;o[djXOJ@a. ID?5=x(D`\F:ZnlZq~/gjΝ;< HECYtBӐ/ 'p%$a @hz. wfi]vM$YTJ(A}NG|u?W^7v?!r 7 1]y;M/c=,IW\DqD$&Uo#nSYxӧ@1 7[<`v2j{:n`_qu m"6!p| '#qN YTh*GWƯW>$h @@|QPB"S^?)]Bu$GwD-p)_knNV[,a\ sH9Sjٽݰað$B::ۼ~gl.UϦR/pts%h@'I^SG|?nO-Ux,G.Oɭ9#`FC k#~\&ҌM_SzG(:La@( PB@ k?+ ?CD ^B|Tfu] p "Jϩ_6rO'tRPV-_s4l?QG'YEIE&@ޅ^? M_ST'8Ca@(  %P'VqC[ߴiS}==:BDlKtB`y,g7GM~@<5:!2HEiD 6~\fu*?~|PvmvC\:0EPb]~>7ǘg aފ̮:r̙k.L#.B* \Oy$an/͸…V[mZ{*}c˗;EI@!pW«%k uԉCml8;5j~aEY*Ezq~`Jxy v{̎IOMBG_$nҏGV /8P+8 B"qD|׏K~rJQF͛7֭t!\GV_۶mw}ov!N:sY*$zmS^?pGis?óg)ȧFC΃AzBP"`Wpw`rֱ6rR+#:H/K}c-A|k\iP֊3@tB9sv2KI^%J|5Ǹ}wӤ8 .Wѿ?r@|kP|V3Z?څ&?F} 4HK4PIOy|9ѣvi >c_7z-<%˖QYfXF<lxoHm^7ɇa))I/0qDJ>L _)Q<֋At"ŋ"q^[t>vZfϞM OF0v.ޖ*lyՑVEpS BGu>uZpb3L /{S8Vی?ƭE~T &"+mdH =I2eJ81]y,]w5`&5̼aCOmNFg}vzj;p~!$)ǁLvDn;C$['k߽{!&6@SZhhsL3 'mB~.!QQ{Iiڴi!f̘Nwqo(5lذC S1h׮]|.;, 6;Eido޽˕@̷ dNf![~OzOm;,?Cgc -ZZIqU{W&'w%ljк WM<D)G6cGOx=N#L.Oc%L" @$aO2 fO~ՁcV ܱdCm<ɏk6t*E~gϞ@~y-|D\^'RƗYvLC8pq{7gD[VFGU"?1(9NMͩd!8b1"rWĶdkXtbG-@;o%Ύę9N8=;XBrAǘS_+riZԲ[lQ]u[g@ /@W^.kްlχH}!k8ΌƳGṟO>Y?-R PgyT#p B~ 9pʐL~t٠A8!96hDZ3Ǿ.[N`W!eGG>cL+o)f_25pΝ˅ZJ@HHnEp~ B8hM]p9PDyl]4BWt*؃RZ4m=_V94MN؞MQqH6]Y~o=63xOx(*e Y923t&N^ 얈ebכfW/_w/Y$4*"1E&~1'Qjώ3&3aI1s6k,1bDX\%{Qe25$&qM~.`%iŰH&ML:5숇~x!r=,)(Ѷm۰5ֲr֍\ìgty6ޣLYJ.̧~ ]fjD'G-,v 4R`1jD͛Gt" xZ$%AQ{7}"L (pb1N-JKSz≠[nE~5M5Ҧ'=&L"`i24Xbj%+D)g9B_ࠃqvیZg?:{(_ɺ;v$pyq"I eQ@i5mK [mL ;}B|8>P00@%g.x"Rƽ`r#~,n'Rk҅ r3ö>܀T1dT'uQe)+aH)o `e{fuL=vԀH))@pv $H,ڭgԤ+6Y.3q%e/65 *7=lA(X5fĆDجl2[!d!HKTҔ]t/gZWd,Pu7N)渳X: @vo5ԟCjԵ;S@v<uX(@ 6_v&4<* gzf \b2֧7 bgz:'G`U5#iѢE;78j#=`aZjp:YvoYصE)']qDzn::,gMG*\* oPQq`4W0 <*5OXǿnoscJk]tIENDB`neomutt-neomutt-20171215/contrib/logo/neomutt-32.png000066400000000000000000000035331321473123000221650ustar00rootroot00000000000000PNG  IHDR szzgAMA a cHRMz&u0`:pQ<bKGD pHYs B(xtIME 2Tx%IDATXýWiLUg lbQ TAjh%;( V TA6"( "`jڽ5Mڦ{kH}~sf|L !633E ^^^HLLܹs!ϛ_ Á [H75z Hz_>(-> ,lȼ5 R4twwÇvZ_2N󞞞}yn3QF(wY#իW[lAff& \|48;;6655 4vtt{466ܹshiiQEHRqqqpqqy ղgs:W)))hhh3gىtttɓXj*[n… RǏW=B?JJJP]] }4}5Yo~޳gBCCҞa;)((P 0gϞŅ ۋ3f ""SL^+!T8t-}T?@e˰~TTTMMMhmmU@Xr%\Tӄ+WfΚ7ڴ6::Ř4iI|ڢi2O?tjjjp 477N:(5-0zvP|}}Đ@(׌ ^ıcT4L2*+V}7x!/xXnX=7FN>$ɓq]77G=חZ`$jkkU=zwFg<l^^X+;ۢ7J m}[t2Emmm[7HP $S35M`W)D5U8􄕑vU,5p4H,/#Atwwj١OH& Cvvz:J+~HϾ⃂\R@ ]x/_ƥKZg=}4HUH$`׮]R12fT5ܹS%`ˉ9Qz;I.+5E: XIGHp0`رZ2l`sai`i޳Q+\O3r^Q^מgi޾}!x}}% S$-s;AQZ>aL u!gÑ~ֽh"靡 d ߂ew81 bݺujJT7377ec0NmmmYj[vuu[R)I B&{/~l J@.\%EBYfA?O^J@`9s&6l؀m۶aԩj$4Ŵc5˄C}DX>D4$ =7|'NTI#@;>`x{{cΜ9;9 Eꮬ R+,YhO=]lο\YIIIǃ^W0[+s3oTsA@y~ p?~lx`T%tEXtdate:create2017-05-29T00:15:42+01:00%tEXtdate:modify2016-01-13T20:02:50+00:00"XtEXtSoftwarewww.inkscape.org<IENDB`neomutt-neomutt-20171215/contrib/logo/neomutt-64.png000066400000000000000000000105671321473123000221770ustar00rootroot00000000000000PNG  IHDR@@iqgAMA a cHRMz&u0`:pQ<bKGD pHYs B(xtIME 3~AIDATx{pTu?NBHwHy  ąC6cOX]-gݚuZYDy븰cY,  @bx%A 1Mc~ܾtnw4KU~;p0JIfD{u=E~ C7zp e pLL?]-RRCbX;Cy 6( ӍFB⁃@z~~>+W$..{pϕM@E())AEz{{1L9rTR$^$hr pzz:%%%L&\.Otreπ?WV,]@mm-cƌJ! :l6 $/G||<}=g~|۷oH0 tZH@ҬY0L(Bss3 w}wELP%)))iiilNCpFA`0hFbݎ$I'ơOoQz+?6cǎe̵`^֝{HfE10 `22e ͸8&//~N8-[e(,>=vX\=(Λ7X$Ivgڽ޴ZK/D||<ϟM6!˲Uʇz4VO8srYP˾fyիIHHd21aRSS:u*,ŋxb\\\vZZZZe[op^<ʕ+o xMÒ/_Njj* "QQQH$I$''#IO=ɼ+tttvIIIwA>`pCx ˖-cر$`58''bA4QHl޼͸qr {졷yT0l G@!pkvv65S"~X{1DQ@@0 2X,er#@%Y!Yb_"AZ8I^/ "F|,@zZ~PXXdȔNΝ;Ǟ={bوg[V (jp7b({(h|(‘#G|||>RShB X,(2a-xȚ3gɆ!2 czzz(..t裏jEf`?R3py<E 0c ? [I(..6nS^^N^^l6bbbhkkKe.!w/)S9^3?pGQ O (++#55Ip8[TW3w#`61LM&&)`M]]x^]]]Ư`hڠ~qUn; V477Sbb"3f($mok"2(,|>ٹs'?)e}-1oyq+C555 ," .Ԯ)7`MR`F$hDhQ[[%+s9 /s555L0#ϜN^ ^e ˔5h i#!w^A5h{/si(++cA{<ɓ7P)%K_-&$fn?Α#Ghhhn8dg333W_}uM&5ɡLkpXGB[9S[; B,SUUE?!e(V֢"fF$] [Y w{x{ AP0)ÇM$("Cpqg(Ea gEǦM4 Eȁhnnn8s?~=m4aR >}#''ng d57(e{<#%Ĥ$۷`)qɈX ,/**";;;$(L?s6L~eoNrR7?(?'O4~ 0 h)hDQUӎt===aWFCͼ}:uJ`}Cx7(gn E3#rٺu+^ #2n0Urm6>,iiiÎBً`GeܸqڵKއ-CQ nV+0pvEQ?kҎpqy6nܨvUZ溑؀ߊȓO>IRR! \. wOQܨ{+0HCDzJ%%%ESSiii^z׳aÆ@6b`ٴ:O'xKϟϒ%KBGhoog@>IGG466( cƌ("** euD o4xuYDdi"`rrr2>`ȇ;g}@}}3Xܨ%I hhmm&WQI>7ĕ~RUUƍX,_t:Aؼ,jw+("jjĠ~.Z_ދZ%*yŽ8"I&a ڿ?۶m G// x{M֋XP´iӰ|DEEq >PCYWF k.v88q"999$%%rʕ+GCCtG5p&#Ueҥdee Bee%uuuɨ_TC -P6|BQQ .$%%e5kr!%%{"r)!4 //y摑rSLNYK|uɎA`ĉ`2j""f}i [йݨhăZF։j"J٘`E1٨z s5<#1{s Z%tEXtdate:create2016-01-13T20:01:51+00:00?P%tEXtdate:modify2016-01-13T20:01:51+00:00bYtEXtSoftwarewww.inkscape.org<IENDB`neomutt-neomutt-20171215/contrib/logo/neomutt.svg000066400000000000000000000273251321473123000217630ustar00rootroot00000000000000 image/svg+xml 18 March 2009 Malcolm Locke <malc@wholemeal.co.nz> Based on the XPM format icon provided with the NeoMutt Debian package neomutt-neomutt-20171215/contrib/lua/000077500000000000000000000000001321473123000173575ustar00rootroot00000000000000neomutt-neomutt-20171215/contrib/lua/test_lua-api_runner.neomuttrc000066400000000000000000000003301321473123000252750ustar00rootroot00000000000000set visual=vim set connect_timeout=69 set arrow_cursor set mask="!^\\.[^.]" set to_chars="+TCFL" set sort="from" set from="fubar@example.org" set my_fubar="Ford Prefect" lua-source contrib/lua/test_lua-api_spec.lua neomutt-neomutt-20171215/contrib/lua/test_lua-api_spec.lua000066400000000000000000000053431321473123000234700ustar00rootroot00000000000000if mutt and mutt.message then print = mutt.message end found, runner = pcall(require, 'busted.runner') if not found then print("Please install busted, e.g. with luarocks:") print(" %% luarocks install busted") os.exit(1) end runner() local eq = function(exp, act) return assert.are.same(exp, act) end local neq = function(exp, act) return assert.are_not.same(exp, act) end local ok = function(res) return assert.is_true(res) end local test_config_type = function(setting, a, b) eq(a, mutt.get(setting)) mutt.set(setting, b) eq(b, mutt.get(setting)) end describe('lua API', function() describe('test get/set', function() it('works with DT_STRING', function() test_config_type("visual", "vim", "fubar") end) it('works with DT_NUMBER and positive ints', function() test_config_type("connect_timeout", 69, 42) test_config_type("connect_timeout", 42, 69) end) it('works with DT_NUMBER and negative ints', function() test_config_type("connect_timeout", 69, -42) test_config_type("connect_timeout", -42, 69) end) it('works with DT_NUMBER and does not accept positive int overflow', function() assert.has_error(function() test_config_type("connect_timeout", 69, 33000) end) end) it('works with DT_NUMBER and does not accept negative int overflow', function() assert.has_error(function() test_config_type("connect_timeout", 69, -33000) end) end) it('works with DT_BOOL', function() test_config_type("arrow_cursor", true, false) end) it('works with DT_QUAD', function() test_config_type("abort_noattach", mutt.QUAD_NO, mutt.QUAD_ASKNO) end) it('works with DT_PATH', function() test_config_type("alias_file", "contrib/lua/test_lua-api_runner.muttrc", "/dev/null") end) it('works with DT_MAGIC', function() test_config_type("mbox_type", "mbox", "Maildir") end) it('works with DT_SORT', function() test_config_type("sort", "from", "date") end) it('works with DT_REGEX', function() test_config_type("mask", "!^\\\\.[^.]", ".*") end) it('works with DT_MBTABLE', function() test_config_type("to_chars", "+TCFL", "+T") end) it('works with DT_ADDRESS', function() test_config_type("from", "fubar@example.org", "barfu@example.com") end) it('works with custom my_ variable DT_STRING', function() test_config_type("my_fubar", "Ford Prefect", "Zaphod Beeblebrox") end) it('detects a non-existent my_ variable DT_STRING', function() assert.has_error(function() mutt.get("my_doesnotexists") end) end) it('detects a non-existent other variable', function() assert.has_error(function() mutt.get("doesnotexists") end) end) end) end) neomutt-neomutt-20171215/contrib/patch.slang-1.2.2.keypad.1000066400000000000000000000045161321473123000230020ustar00rootroot00000000000000diff -ur slang.old/src/slcurses.c slang/src/slcurses.c --- slang.old/src/slcurses.c Fri Apr 24 09:16:46 1998 +++ slang/src/slcurses.c Sat Jul 4 07:30:31 1998 @@ -134,7 +134,10 @@ } else if (ch == 0xFFFF) return ERR; SLang_ungetkey (ch); - return SLkp_getkey (); + if ((ch = SLkp_getkey ()) != SL_KEY_ERR) + return ch; + else + return SLang_getkey (); } return SLang_getkey (); } diff -ur slang.old/src/slkeymap.c slang/src/slkeymap.c --- slang.old/src/slkeymap.c Fri Apr 24 09:16:47 1998 +++ slang/src/slkeymap.c Sat Jul 4 07:41:42 1998 @@ -343,6 +343,8 @@ SLang_Key_Type *SLang_do_key(SLKeyMap_List_Type *kml, int (*getkey)(void)) { + unsigned char SLang_Undo_Buffer [SL_MAX_INPUT_BUFFER_LEN]; + int SLang_Undo_Len = 0; register SLang_Key_Type *key, *next, *kmax; unsigned int len; unsigned char input_ch; @@ -356,6 +358,7 @@ return NULL; input_ch = (unsigned char) SLang_Last_Key_Char; + SLang_Undo_Buffer [SLang_Undo_Len++] = input_ch; key = (SLang_Key_Type *) &((kml->keymap)[input_ch]); @@ -372,7 +375,11 @@ key = kml->keymap + input_ch; if (key->type == 0) + { + if (getkey == (int (*)(void)) SLang_getkey) + SLang_ungetkey_string (SLang_Undo_Buffer, SLang_Undo_Len); return NULL; + } } /* It appears to be a prefix character in a key sequence. */ @@ -385,6 +392,7 @@ { SLang_Key_TimeOut_Flag = 1; SLang_Last_Key_Char = (*getkey)(); + SLang_Undo_Buffer [SLang_Undo_Len++] = (unsigned char) SLang_Last_Key_Char; SLang_Key_TimeOut_Flag = 0; len++; @@ -458,6 +466,8 @@ kmax = next; } + if (getkey == (int (*)(void)) SLang_getkey) + SLang_ungetkey_string (SLang_Undo_Buffer, SLang_Undo_Len); return NULL; } diff -ur slang.old/src/slkeypad.c slang/src/slkeypad.c --- slang.old/src/slkeypad.c Fri Apr 24 09:16:47 1998 +++ slang/src/slkeypad.c Sat Jul 4 07:30:31 1998 @@ -110,7 +110,7 @@ key = SLang_do_key (Keymap_List, (int (*)(void)) SLang_getkey); if ((key == NULL) || (key->type != SLKEY_F_KEYSYM)) { - SLang_flush_input (); + /* SLang_flush_input (); */ return SL_KEY_ERR; } neomutt-neomutt-20171215/contrib/pgp2.rc000066400000000000000000000030761321473123000200020ustar00rootroot00000000000000# -*-muttrc-*- # # PGP command formats for PGP 2. # # # Note: In order to be able to read your own messages, you'll have # the +encrypttoself command line parameter to the pgp_encrypt_only_command # and pgp_encrypt_sign_command variables. # # decode application/pgp set pgp_decode_command="%?p?PGPPASSFD=0; export PGPPASSFD;? cat %?p?-? %f | pgp +language=mutt +verbose=0 +batchmode -f" # verify a pgp/mime signature set pgp_verify_command="pgp +language=mutt +verbose=0 +batchmode -t %s %f" # decrypt a pgp/mime attachment set pgp_decrypt_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp +language=mutt +verbose=0 +batchmode -f" # create a pgp/mime signed attachment set pgp_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp +language=mutt +verbose=0 +batchmode -abfst %?a? -u %a?" # create a pgp/mime encrypted attachment set pgp_encrypt_only_command="pgp +language=mutt +verbose=0 +batchmode -aeft %r < %f" # create a pgp/mime encrypted and signed attachment set pgp_encrypt_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp +language=mutt +verbose=0 +batchmode -aefts %?a?-u %a? %r" # import a key into the public key ring set pgp_import_command="pgp -ka %f +language=mutt" # export a key from the public key ring set pgp_export_command="pgp -kxaf +language=mutt %r" # verify a key set pgp_verify_key_command="pgp -kcc +language=mutt %r" # read in the public key ring set pgp_list_pubring_command="pgpring -2 %r" # read in the secret key ring set pgp_list_secring_command="pgpring -s -2 %r" # pattern for good signature set pgp_good_sign="Good signature" neomutt-neomutt-20171215/contrib/pgp5.rc000066400000000000000000000032631321473123000200030ustar00rootroot00000000000000# -*-muttrc-*- # # PGP command formats for PGP 5. # # decode application/pgp set pgp_decode_command="%?p?PGPPASSFD=0; export PGPPASSFD;? cat %?p?-? %f | pgpv +language=mutt +verbose=0 +batchmode -f --OutputInformationFD=0" # verify a pgp/mime signature set pgp_verify_command="pgpv +language=mutt +verbose=0 +batchmode --OutputInformationFD=1 %f %s" # string that the verify command outputs if the signature is good set pgp_good_sign = "Good signature" # decrypt a pgp/mime attachment set pgp_decrypt_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgpv +language=mutt +verbose=0 +batchmode --OutputInformationFD=2 -f" # create a pgp/mime signed attachment set pgp_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgps +language=mutt +verbose=0 +batchmode -abft %?a? -u %a?" # create a pgp/mime encrypted attachment set pgp_encrypt_only_command="/usr/lib/neomutt/pgpewrap pgpe +language=mutt +verbose=0 +batchmode +nobatchinvalidkeys=off -aft -- -r %r < %f" # create a pgp/mime encrypted and signed attachment set pgp_encrypt_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | /usr/lib/neomutt/pgpewrap pgpe +language=mutt +verbose=0 +batchmode +nobatchinvalidkeys=off -afts %?a? -u %a? -- -r %r" # import a key into the public key ring set pgp_import_command="pgpk -a +language=mutt --OutputInformationFD=1 %f" # export a key from the public key ring set pgp_export_command="pgpk -xa +language=mutt --OutputInformationFD=1 %r" # verify a key set pgp_verify_key_command="pgpk -c +batchmode +language=mutt --OutputInformationFD=1 %r" # read in the public key ring set pgp_list_pubring_command="pgpring -5 %r" # read in the secret key ring set pgp_list_secring_command="pgpring -5 -s %r" neomutt-neomutt-20171215/contrib/pgp6.rc000066400000000000000000000031161321473123000200010ustar00rootroot00000000000000# -*-muttrc-*- # # PGP command formats for PGP 6. # # decode application/pgp set pgp_decode_command="%?p?PGPPASSFD=0; export PGPPASSFD;? cat %?p?-? %f | pgp6 +compatible +verbose=0 +batchmode -f" # verify a pgp/mime signature set pgp_verify_command="pgp6 +compatible +verbose=0 +batchmode -t %s %f" # decrypt a pgp/mime attachment set pgp_decrypt_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp6 +compatible +verbose=0 +batchmode -f" # create a pgp/mime signed attachment set pgp_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp6 +compatible +verbose=0 +batchmode -abfst %?a? -u %a?" # create a pgp/mime encrypted attachment set pgp_encrypt_only_command="pgp6 +compatible +verbose=0 +encrypttoself +batchmode -aeft %r < %f" # create a pgp/mime encrypted and signed attachment set pgp_encrypt_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp6 +compatible +verbose=0 +encrypttoself +batchmode +clearsig=off -aefts %?a? -u %a? %r" # import a key into the public key ring set pgp_import_command="pgp6 +compatible -ka %f " # export a key from the public key ring set pgp_export_command="pgp6 +compatible -kxaf %r" # verify a key set pgp_verify_key_command="pgp6 +compatible -kcc %r" # read in the public key ring set pgp_list_pubring_command="pgpring -5 %r" # read in the secret key ring set pgp_list_secring_command="pgpring -s -5 %r" # create a clearsigned message set pgp_clearsign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp6 +compatible +verbose=0 +batchmode +clearsig -afst %?a? -u %a?" # fetch keys set pgp_getkeys_command="pkspxycwrap %r" neomutt-neomutt-20171215/contrib/sample.mailcap000066400000000000000000000001761321473123000214130ustar00rootroot00000000000000text/html; netscape -remote openURL\(%s\) image/gif; xv %s image/jpg; xv %s application/pgp-keys; pgp -f < %s ; copiousoutput neomutt-neomutt-20171215/contrib/sample.neomuttrc000066400000000000000000000312451321473123000220260ustar00rootroot00000000000000# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # ME's personal .muttrc (Mutt 0.92.5) # # The format of this file is one command per line. Everything after a pound # sign (#) is a comment, unless a backward slash (\) precedes it. Note: In # folder-hook and send-hook you need to account for two levels of dequoting # (see manual). # # Note: $folder should be set _before_ any other path vars where `+' or `=' # is used because paths are expanded when parsed # #set folder=~/Mail # where i keep my mailboxes #set abort_unmodified=yes # automatically abort replies if I don't # change the message set alias_file=~/.mail_aliases # where I keep my aliases #set allow_8bit # never do Q-P encoding on legal 8-bit chars set arrow_cursor # use -> instead of hiliting the whole line #set ascii_chars # use ASCII instead of ACS chars for threads #set askbcc #set askcc #set attribution="On %d, %n wrote:" # how to attribute replies set autoedit # go to the editor right away when composing #set auto_tag # always operate on tagged messages #set charset="iso-8859-1" # character set for your terminal set noconfirmappend # don't ask me if i want to append to mailboxes #set confirmcreate # prompt when creating new files set copy=yes # always save a copy of outgoing messages set delete=yes # purge deleted messages without asking set edit_headers # let me edit the message header when composing #set editor="emacs -nw" # editor to use when composing messages #set bounce=yes # don't ask about bouncing messages, just do it #set fast_reply # skip initial prompts when replying #set fcc_attach # keep attachments in copies of sent messages? #set force_name # fcc by recipient, create if mailbox doesn't exist #set forward_decode # weed and MIME decode forwaded messages #set forward_format="[%a: %s]" # subject to use when forwarding messages #set forward_quote # quote the header and body of forward msgs #set index_format="%4C %Z %{%m/%d} [%2N] %-15.15F (%4c) %s" set index_format="%4C %Z %{%m/%d} %-15.15F (%4c) %s" # format of the index #set hdrs # include `my_hdr' lines in outgoing messages #set header # include message header when replying set help # show the help lines #set history=20 # number of lines of history to remember #set hostname="mutt.org" # my DNS domain set include # always include messages when replying #set indent_string="> " # how to quote replied text #set locale="C" # locale to use for printing time #set mailcap_path="~/.mailcap:/usr/local/share/mailcap" set nomark_old # i don't care about whether a message is old set mail_check=10 # how often to poll for new mail set mbox=+mbox # where to store read messages #set menu_scroll # no implicit next-page/prev-page #set metoo # remove my address when replying set mime_forward # use message/rfc822 type to forward messages set move=yes # don't ask about moving messages, just do it #set pager=less # some people prefer an external pager #set pager_context=3 # no. of lines of context to give when scrolling #set pager_format="-%S- %-20.20f %s" # format of the pager status bar set pager_index_lines=6 # how many index lines to show in the pager #set pager_stop # don't move to the next message on next-page #set pgp_strict_enc # use Q-P encoding when needed for PGP set postponed=+postponed # mailbox to store postponed messages in #set post_indent_string='---end quoted text---' #set print=ask-yes # ask me if I really want to print messages set print_command=/bin/false # how to print things (I like to save trees) set noprompt_after # ask me for a command after the external pager exits #set quote_regexp="^ *[a-zA-Z]*[>:#}]" # how to catch quoted text set read_inc=25 # show progress when reading a mailbox #set recall # prompt to recall postponed messages set record=+outbox # default location to save outgoing mail set reply_to # always use reply-to if present #set reply_regexp="^(re:[ \t]*)+"# how to identify replies in the subject: #set resolve # move to the next message when an action is performed #set reverse_alias # attempt to look up my names for people set reverse_name # use my address as it appears in the message # i am replying to set nosave_empty # remove files when no messages are left #set save_name # save outgoing messages by recipient, if the #set sendmail="/usr/lib/sendmail -oi -oem" # how to deliver mail #set shell="/bin/zsh" # program to use for shell escapes #set signature="~/.signature" # file which contains my signature # I subscribe to a lot of mailing lists, so this is _very_ useful. This # groups messages on the same subject to make it easier to follow a # discussion. NeoMutt will draw a nice tree showing how the discussion flows. set sort=threads # primary sorting method #set sort_aux=reverse-date-received # how to sort subthreads #set sort_aux=last-date # date of the last message in thread set sort_browser=reverse-date # how to sort files in the dir browser set spoolfile='~/mailbox' # where my new mail is located #set status_format="-%r-NeoMutt: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b? %l]---(%s)-%>-(%P)---" #set status_on_top # some people prefer the status bar on top #set strict_threads # don't thread by subject set tilde # virtual lines to pad blank lines in the pager #set timeout=0 # timeout for prompt in the index menu #set tmpdir=~/tmp # where to store temp files #set to_chars=" +TCF" #set use_8bitmime # enable the -B8BITMIME sendmail flag set nouse_domain # don't qualify local addresses with $domain #set use_from # always generate the `From:' header field set implicit_autoview=yes # pager shows parts having a mailcap viewer set crypt_verify_sig=no # don't automatically verify message signatures #set visual=vim # editor invoked by ~v in the builtin editor #set nowait_key # prompt when a pipe returns normal status set write_inc=25 # show progress while writing mailboxes # only enable the following IFF you have sendmail 8.8.x or you will not # be able to send mail!!! #set dsn_notify='failure,delay' # when to return an error message #set dsn_return=hdrs # what to return in the error message # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Header fields I don't normally want to see # ignore * # this means "ignore all lines by default" # I do want to see these fields, though! unignore from: subject to cc mail-followup-to \ date x-mailer x-url # this shows how nicely wrap long lines # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Color definitions # #color normal white default color hdrdefault red default color quoted brightblue default color signature red default color indicator brightyellow red color error brightred default color status yellow blue color tree magenta default # the thread tree in the index menu color tilde magenta default color message brightcyan default color markers brightcyan default color attachment brightmagenta default color search default green # how to hilite search patterns in the pager color header brightred default ^(From|Subject): color body magenta default "(ftp|http|https)://[^ ]+" # point out URLs color body magenta default [-a-z_0-9.\+]+@[-a-z_0-9.]+ # e-mail addresses color underline brightgreen default # attributes when using a mono terminal #mono header underline ^(From|Subject): mono quoted bold # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Key bindings # # maps: # alias alias menu # attach attachment menu # browser directory browser # compose compose menu # index message index # pgp pgp menu # postpone postponed message recall menu # generic generic keymap for all of the above # editor line editor # pager text viewer # bind generic "\e<" first-entry # emacs-like bindings for moving to top/bottom bind generic \e> last-entry bind generic { top-page bind generic } bottom-page bind generic \177 last-entry macro index \cb " urlview" # simulate the old browse-url function macro index S "+spam" macro pager S "+spam" #macro index \# "bug" # search for bugs #macro index "\"" " set realname=\"real hairy macro\" ?realname" # and a comment to boot! #macro index f1 "woohoo!" bind pager G bottom # just like vi and less #macro pager \Ck " pgp -kaf" # a comment is valid here #macro pager X " morepgp" # pipe PGP message to a script #bind editor \cy eol # make ^Y jump to the end of the line # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # User Defined Headers # #my_hdr X-Useless-Header: Look ma, it's a \# sign! # real comment #my_hdr X-Operating-System: `uname -a` # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Specify default filename when saving messages # # save-hook [!] # # is provided as default when saving messages from #save-hook mutt- =mutt-mail #save-hook aol\\.com$ +spam save-hook ^judge +diplomacy # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Multiple spool mailboxes # # mbox-hook [!] # # Read mail in is moved to when is # closed. #mbox-hook =mutt-users.in =mutt-users #mbox-hook +TEST +inbox # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Change settings based upon message recipient # # send-hook [!] # # is executed when sending mail to an address matching #send-hook mutt- 'set signature=~/.sigmutt; my_hdr From: Mutt User ' # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Specify where to save composed messages # # fcc-hook [!] # # is recipient(s), is where to save a copy #fcc-hook joe +joe #fcc-hook bob +bob # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Change settings based on mailbox # # folder-hook [!] # # is executed when opening a mailbox matching #folder-hook . 'set sort=date-sent' #folder-hook mutt 'set index_format="%4C %Z %02m/%02N %-20.20F (%4l) %s"' #folder-hook =mutt my_hdr Revolution: \#9 # real comment #folder-hook . 'set reply_regexp="^re:[ \t]*"' # this mailing list prepends "[WM]" to all non reply subjects, so set # $reply_regexp to ignore it # Warning: May break threads for other people. #folder-hook +wmaker 'set reply_regexp="^(re:[ \t]*)?\[WM\][ \t]*"' # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Aliases # # alias
[ ,
... ] #alias exam "\# to annoy michael" #alias me Michael Elkins # me! alias mutt-dev Mutt Development List # power users alias mutt-users Mutt User List alias mutt-announce Mutt Announcement List alias wmaker WindowMaker Mailing List # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Mailboxes to watch for new mail # # mailboxes [ ... ] # mailboxes ! +mutt-dev +mutt-users +open-pgp +wmaker +hurricane +vim +ietf \ +drums #mailboxes `echo $HOME/Mail/*` # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Specify the order of the headers to appear when displaying a message # # hdr_order [ ... ] # unhdr_order * # forget the previous settings hdr_order date from subject to cc # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Identify mailing lists I subscribe to # # lists [ ... ] lists ^mutt-dev@mutt\\.org$ ^mutt-users@mutt\\.org$ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Automatically use entries from ~/.mailcap to view these MIME types # # auto_view [ ... ] auto_view application/x-gunzip auto_view application/x-gzip # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Scoring # # score # # 9999 and -9999 are special values which cause processing of hooks to stop # at that entry. If you prefix the score with an equal sign (=), the score # is assigned to the message and processing stops. #score '~f ^me@cs\.hmc\.edu$' 1000 #score '~t mutt | ~c mutt' =500 #score '~f aol\.com$' -9999 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # I use Mutt on several different machines, so I put local config commands # in a separate file so I can have the rest of the settings the same on all # machines. # source ~/.muttrc-local # config commands local to this site # EOF neomutt-neomutt-20171215/contrib/sample.neomuttrc-tlr000066400000000000000000000323411321473123000226230ustar00rootroot00000000000000# -*-muttrc-*- # # Mutt configuration file of Thomas Roessler # # Use and distribute freely. # # Note: This file doesn't contain any personal customization, i.e., # using it won't make you send messages with my name in the header. # # Things to change: You probably want to change the "priv.rc" source # command in the end of this file. Also, it's likely you want to have # a look at the the $editor and $tmpdir variables. # # # MIME settings # # auto_view application/ms-tnef text/x-vcard # auto_view application/x-chess application/x-lotus-notes # auto_view text/html application/x-gzip application/x-gunzip # auto_view application/rtf application/x-rath # auto_view application/msword auto_view text/html mime_lookup application/octet-stream # alternative_order application/pgp text/html text/enriched text/plain alternative_order text/plain text/html # # Key bindings # # # A few of these may resemble Pine. ups. # bind alias " " tag-entry bind alias \n select-entry bind alias \r select-entry bind attach i exit bind attach n next-entry bind attach p previous-entry bind attach " " select-entry bind attach y print-entry bind browser last-entry bind browser first-entry bind editor "\e" kill-word bind editor "\e" kill-word bind editor "" complete-query bind editor "\eq" complete-query bind editor "\Ct" transpose-chars bind generic "\CV" next-page bind generic "\Ca" first-entry bind generic "\Ce" last-entry bind generic "\eV" previous-page bind generic "\ev" previous-page bind generic + tag-entry bind generic ^ first-entry bind generic a tag-prefix bind generic $ last-entry bind generic q exit bind index ";" limit bind index "\Ce" last-entry # override edit-type bind index "\eV" previous-page # override collapse-something bind index "\e<" collapse-thread bind index "\eq" query bind index $ last-entry bind index * flag-message bind index delete-message bind index last-entry bind index first-entry bind index J next-entry bind index K previous-entry bind index Q quit bind index R group-reply bind index \em recall-message bind index a tag-prefix bind index m mail bind index p previous-entry bind index t create-alias bind index x sync-mailbox bind index y print-message bind index n next-entry bind index "\ev" previous-page bind pager "\Cn" next-line bind pager "\Cp" previous-line bind pager + tag-message bind pager * flag-message bind pager delete-message bind pager next-line bind pager bottom bind pager top bind pager previous-line bind pager G group-reply bind pager R group-reply bind pager \em recall-message bind pager t display-toggle-weed # like slrn bind pager y print-message bind query i exit # make it feel like emacs macro generic "\ex" ":exec " macro pager "\ex" ":exec " macro generic "\eX" "\ex" macro pager "\eX" "\ex" macro index "~" ";~" # macro index "%" ";%" # Thread tagging bind index "\et" tag-subthread bind index "\eT" tag-thread # for majordomo list owner and moderator jobs macro index "\ea" ":set nopipe_decode wait_key\n|approve\n:set nowait_key\n" macro pager "\ea" ":set nopipe_decode wait_key\n|approve\n:set nowait_key\n" # emulate the old URL-browser key bindings. macro pager "\Cb" "| urlview -\n" macro index "\Cb" "| urlview -\n" # permit limiting from the pager. macro pager "~" "~" macro pager ";" "" # emulate the old POP-feature bindings macro index G "!fetchmail\n" macro pager G "!fetchmail\n" # razor-report: Report spam. # macro index S ":set nopipe_decode nowait_key\n|razor-report > /dev/null 2> /dev/null\ns+junk\n" # macro pager S ":set nopipe_decode nowait_key\n|razor-report > /dev/null 2> /dev/null\ns+junk\n" macro index S "s+junk\n" macro pager S "s+junk\n" # # Colors # # This is a tiny hack, so I can get different # color schemes on the console and under X11. source ~/.mutt/colors.`if [ "$TERM" = "linux" ] ; then echo linux ; else echo default ; fi` mono index bold ~F # mono body bold '\*[^*]+\*' # mono body underline '_[^_]+_' # # The header weed list # ignore delivered-to ignore content- errors-to in-reply-to mime-version ignore lines precedence status ignore nntp-posting-host path old-return-path received references ignore priority >received >>received ignore resent- return-path xref path ignore x400 importance sensitivity autoforward original-encoded-information ignore x- thread- ignore DomainKey-Signature mail-followup-to ignore list- comments posted-to approved-by unignore x-spam-level x-url x-mailer list-id x-no-spam x-archived-at unignore x-diagnostic hdr_order from to cc date subject reply-to mail-followup-to list-id # # Various settings # set abort_nosubject=no # Let me send messages with an empty subject set abort_unmodified=no # Let me send empty messages set alias_file=~/.mutt/aliases # Where to store aliases unset allow_8bit # Produce correct MIME unset arrow_cursor # Use the bar cursor set askcc # Ask me about CCs unset bounce_delivered # Don't include Delivered-to with bounces # set charset=iso-8859-1 # The local character set set send_charset="us-ascii:iso-8859-1:iso-8859-15:iso-8859-2:utf-8" set confirmcreate # Ask me about creating new files unset confirmappend # Don't ask me about appending to files set delete=yes # Don't ask me whether or not I meant to delete messages # set display_filter="tr '\240\204\223\226' ' \"\"-'" # fix some funny characters set edit_headers # I want to edit the headers. set editor="/usr/bin/jed %s -f 'mail_mode();'" # Invoke jed with mail_mode. This may # or may not work for you. set noenvelope_from # set messages' envelope-from header. set fcc_clear # Store local copies of messages in the clear. set folder=~/Mail # Where my mail folders go set followup_to # Create Mail-Followup-To headers. unset force_name # Don't create save folders which don't exist. set forward_decode # Decode messages when forwarding. set forward_decrypt # Decrypt messages when forwarding. set nohelp # No help line. set include=yes # Always include a copy when replying. set mark_old # Distinguish between seen (but unread) and new messages set mbox=+mbox # The (unused) mbox file. unset metoo # Remove me from CC headers. set mime_forward_rest=ask-no # Ask me whether or not to create a MIME-encapsulated forward set move=no # Don't use mbox set pager_stop # Don't fall through to the next message in the pager set pager_index_lines=0 # The pager index is ugly. set crypt_replyencrypt # Encrypt when replying to encrypted messages. set crypt_replysignencrypted # Sign when replying to encrypted messages. set pgp_show_unusable="no" # Don't display unusable keys. set pgp_sort_keys="keyid" # Sort keys by key ID set crypt_replysign # Sign when replying to signed messages. set pgp_timeout=3600 # Forget the PGP passphrase after an hour. set pipe_decode # Decode messages I pipe to commands, typically to patch(1). set postponed=~/.mutt/postponed # Where to put postponed messages set print=ask-no # Don't waste paper set print_command="enscript -2Gr -Email" # Two columns, landscape, fancy header. set print_split=yes # Invoke enscript once per message set quit=yes # Don't ask me whether or not I want to quit. set quote_regexp="^ *[a-zA-Z]*[>|][>:|]*" # Recognize quotes in the pager. set read_inc=50 # Progress indicator when reading folders. set recall=ask-no # When I say "compose", ask me whether I want to continue # composing a postponed message. set record="+archive/now" # Put copies of most outgoing messages to ~/Mail/archive/now set reply_to=ask-yes # Ask me whether I want to honor users' reply-to headers. set reverse_alias # Use aliases to display real names on the index. set save_name # Save copies by name. Together with $record and $save_name, # this means that when a folder exists, copies of outgoing # messages are written to ~/Mail/, otherwise they go to # ~/Mail/archive/now set signature=~/.signature # Silly signature set sig_dashes # Add dashes above my signature set smart_wrap # Try to be smart when wrapping around lines in the pager set sort=threads # sort by threads, set sort_aux=date # then by date unset strict_threads # don't be strict about threads # set suspend=no # Don't suspend - I usually run mutt like this: "xterm -e mutt" set tilde # Indicate empty lines in the pager. set tmpdir=~/.tmp # Temporary files aren't stored in public places. set to_chars=" +TCF " # Don't tag list mail in the index unset use_domain # Don't append a domain to addresses. set write_inc=50 # Progress indicator when writing folders. set query_command="lbdb2q.pl %s" # Use the Little Brother's Database with the external # query feature. set sendmail_wait=-1 # Don't put sendmail into the background. set encode_from # "From " in the beginning of a line triggers quoted-printable set nowait_key # Return immediately from external programs set forward_format="[fwd] %s (from: %a)" # A different subject for forwarded messages set nobeep # Shut up. ;-) set reply_regexp="^((re([\\[0-9\\]+])*|aw):[ \t]*)+[ \t]*" # A regular expression to detect replies set header # Include the message header when replying. set ignore_list_reply_to # Ignore Reply-To headers pointing to mailing lists. set norfc2047_parameters # Sometimes, I get mails which use a bogus encoding for # MIME parameters. Setting this shouldn't harm. # (OK, she doesn't use Notes any more, so I can unset this. ;-) # set text_flowed # Generate text/plain; format=flowed # unset use_ipv6 # Don't try to use IPv6 - it doesn't work here. set keep_flagged # don't move flagged messages to mbox set hide_missing=yes # Don't show how many messages are missing in a thread structure set status_format="-%r-+(%v) %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b?%?l? %l?]----%>-(%P)---" set compose_format="--+(%v) Compose [Approx. msg size: %l Atts: %a]%>-" set pager_format="-%Z- %C/%m: %.20n %> %s" set smileys="^$" set ispell=iaspell set markers=no # Don't mark wrapped lines set wrapmargin=4 # Leave a margin in the pager # PGP command configuration # source ~/.mutt/pgp2.rc source ~/.mutt/gpg.rc set pgp_getkeys_command="" # source ~/.mutt/smime.rc # source non-public stuff, (hooks, alternates, ...) source ~/.mutt/priv.rc # source aliases # source ~/.mutt/aliases-coruscant source ~/.mutt/aliases neomutt-neomutt-20171215/contrib/smime.rc000066400000000000000000000071241321473123000202420ustar00rootroot00000000000000# -*-muttrc-*- ## The following options are only available if you have ## compiled in S/MIME support # If you compiled neomutt with support for both PGP and S/MIME, PGP # will be the default method unless the following option is set set smime_is_default # Uncomment this if you don't want to set labels for certificates you add. # unset smime_ask_cert_label # Passphrase expiration set smime_timeout=300 # Global crypto options -- these affect PGP operations as well. set crypt_autosign = yes set crypt_replyencrypt = yes set crypt_replysign = yes set crypt_replysignencrypted = yes set crypt_verify_sig = yes # Section A: Key Management. # The (default) keyfile for signing/decrypting. Uncomment the following # line and replace the keyid with your own. set smime_default_key="12345678.0" # Uncomment to make neomutt ask what key to use when trying to decrypt a message. # It will use the default key above (if that was set) else. # unset smime_decrypt_use_default_key # Path to a file or directory with trusted certificates set smime_ca_location="~/.smime/ca-bundle.crt" # Path to where all known certificates go. (must exist!) set smime_certificates="~/.smime/certificates" # Path to where all private keys go. (must exist!) set smime_keys="~/.smime/keys" # These are used to extract a certificate from a message. # First generate a PKCS#7 structure from the message. set smime_pk7out_command="openssl smime -verify -in %f -noverify -pk7out" # Extract the included certificate(s) from a PKCS#7 structure. set smime_get_cert_command="openssl pkcs7 -print_certs -in %f" # Extract the signer's certificate only from a S/MIME signature (sender verification) set smime_get_signer_cert_command="openssl smime -verify -in %f -noverify -signer %c -out /dev/null" # This is used to get the email address the certificate was issued to. set smime_get_cert_email_command="openssl x509 -in %f -noout -email" # Add a certificate to the database using smime_keys. set smime_import_cert_command="/usr/lib/neomutt/smime_keys add_cert %f" # Sction B: Outgoing messages # Algorithm to use for encryption. # valid choices are aes128, aes192, aes256, rc2-40, rc2-64, rc2-128, des, des3 set smime_encrypt_with="aes256" # Encrypt a message. Input file is a MIME entity. set smime_encrypt_command="openssl smime -encrypt -%a -outform DER -in %f %c" # Algorithm for the signature message digest. # Valid choices are md5, sha1, sha224, sha256, sha384, sha512. set smime_sign_digest_alg="sha256" # Sign. set smime_sign_command="openssl smime -sign -md %d -signer %c -inkey %k -passin stdin -in %f -certfile %i -outform DER" #Section C: Incoming messages # Decrypt a message. Output is a MIME entity. set smime_decrypt_command="openssl smime -decrypt -passin stdin -inform DER -in %f -inkey %k -recip %c" # Verify a signature of type multipart/signed set smime_verify_command="openssl smime -verify -inform DER -in %s %C -content %f" # Verify a signature of type application/x-pkcs7-mime set smime_verify_opaque_command="\ openssl smime -verify -inform DER -in %s %C || \ openssl smime -verify -inform DER -in %s -noverify 2>/dev/null" # Section D: Alternatives # Sign. If you wish to NOT include the certificate your CA used in signing # your public key, use this command instead. # set smime_sign_command="openssl smime -sign -md %d -signer %c -inkey %k -passin stdin -in %f -outform DER" # # In order to verify the signature only and skip checking the certificate chain: # # set smime_verify_command="openssl smime -verify -inform DER -in %s -content %f -noverify" # set smime_verify_opaque_command="openssl smime -verify -inform DER -in %s -noverify" # neomutt-neomutt-20171215/contrib/smime_keys000077500000000000000000000742041321473123000207000ustar00rootroot00000000000000#! /usr/bin/perl -w # Copyright (C) 2001-2002 Oliver Ehli # Copyright (C) 2001 Mike Schiraldi # Copyright (C) 2003 Bjoern Jacke # Copyright (C) 2015 Kevin J. McCarthy # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. use strict; use File::Copy; use File::Glob ':glob'; use File::Temp qw(tempfile tempdir); umask 077; use Time::Local; # helper routines sub usage (); sub mutt_Q ($); sub mycopy ($$); sub query_label (); sub mkdir_recursive ($); sub verify_files_exist (@); sub create_tempfile (;$); sub new_cert_structure (); sub create_cert_chains (@); # openssl helpers sub openssl_exec (@); sub openssl_format ($); sub openssl_x509_query ($@); sub openssl_hash ($); sub openssl_fingerprint ($); sub openssl_emails ($); sub openssl_p12_to_pem ($$); sub openssl_verify ($$); sub openssl_crl_text($); sub openssl_trust_flag ($$;$); sub openssl_parse_pem ($$); sub openssl_dump_cert ($); sub openssl_purpose_flag ($); # key/certificate management methods sub cm_list_certs (); sub cm_add_entry ($$$$$$;$); sub cm_add_cert ($); sub cm_add_indexed_cert ($$$); sub cm_add_key ($$$$$$); sub cm_modify_entry ($$$;$); sub cm_find_entry ($$); sub cm_refresh_index (); # op handlers sub handle_init_paths (); sub handle_change_label ($); sub handle_add_cert ($); sub handle_add_pem ($); sub handle_add_p12 ($); sub handle_add_chain ($$$); sub handle_verify_cert($$); sub handle_remove_pair ($); sub handle_add_root_cert ($); my $neomutt = $ENV{MUTT_CMDLINE} || 'neomutt'; my $opensslbin = "/usr/bin/openssl"; my $tmpdir; # Get the directories neomutt uses for certificate/key storage. my $private_keys_path = mutt_Q 'smime_keys'; die "smime_keys is not set in neomutt's configuration file" if length $private_keys_path == 0; my $certificates_path = mutt_Q 'smime_certificates'; die "smime_certificates is not set in neomutt's configuration file" if length $certificates_path == 0; my $root_certs_path = mutt_Q 'smime_ca_location'; die "smime_ca_location is not set in neomutt's configuration file" if length $root_certs_path == 0; my $root_certs_switch; if ( -d $root_certs_path) { $root_certs_switch = -CApath; } else { $root_certs_switch = -CAfile; } ###### # OPS ###### if (@ARGV == 1 and $ARGV[0] eq "init") { handle_init_paths(); } elsif (@ARGV == 1 and $ARGV[0] eq "refresh") { cm_refresh_index(); } elsif (@ARGV == 1 and $ARGV[0] eq "list") { cm_list_certs(); } elsif (@ARGV == 2 and $ARGV[0] eq "label") { handle_change_label($ARGV[1]); } elsif (@ARGV == 2 and $ARGV[0] eq "add_cert") { verify_files_exist($ARGV[1]); handle_add_cert($ARGV[1]); } elsif (@ARGV == 2 and $ARGV[0] eq "add_pem") { verify_files_exist($ARGV[1]); handle_add_pem($ARGV[1]); } elsif ( @ARGV == 2 and $ARGV[0] eq "add_p12") { verify_files_exist($ARGV[1]); handle_add_p12($ARGV[1]); } elsif (@ARGV == 4 and $ARGV[0] eq "add_chain") { verify_files_exist($ARGV[1], $ARGV[2], $ARGV[3]); handle_add_chain($ARGV[1], $ARGV[2], $ARGV[3]); } elsif ((@ARGV == 2 or @ARGV == 3) and $ARGV[0] eq "verify") { verify_files_exist($ARGV[2]) if (@ARGV == 3); handle_verify_cert($ARGV[1], $ARGV[2]); } elsif (@ARGV == 2 and $ARGV[0] eq "remove") { handle_remove_pair($ARGV[1]); } elsif (@ARGV == 2 and $ARGV[0] eq "add_root") { verify_files_exist($ARGV[1]); handle_add_root_cert($ARGV[1]); } else { usage(); exit(1); } exit(0); ############## sub-routines ######################## ################### # helper routines ################### sub usage () { print < [file(s) | keyID [file(s)]] with operation being one of: init : no files needed, inits directory structure. refresh : refreshes certificate and key index files. Updates trust flag (expiration). Adds purpose flag if missing. list : lists the certificates stored in database. label : keyID required. changes/removes/adds label. remove : keyID required. verify : 1=keyID and optionally 2=CRL Verifies the certificate chain, and optionally wether this certificate is included in supplied CRL (PEM format). Note: to verify all certificates at the same time, replace keyID with "all" add_cert : certificate required. add_chain : three files reqd: 1=Key, 2=certificate plus 3=intermediate certificate(s). add_p12 : one file reqd. Adds keypair to database. file is PKCS12 (e.g. export from netscape). add_pem : one file reqd. Adds keypair to database. (file was converted from e.g. PKCS12). add_root : one file reqd. Adds PEM root certificate to the location specified within muttrc (smime_verify_* command) EOF } sub mutt_Q ($) { my ($var) = @_; my $cmd = "$neomutt -v >/dev/null 2>/dev/null"; system ($cmd) == 0 or die<; if (defined($input) && ($input !~ /^\s*$/)) { chomp($input); $input =~ s/^\s+//; ($label, $junk) = split(/\s/, $input, 2); if (defined($junk)) { print "\nUsing '$label' as label; ignoring '$junk'\n"; } } if ((! defined($label)) || ($label =~ /^\s*$/)) { $label = "-"; } return $label; } sub mkdir_recursive ($) { my ($path) = @_; my $tmp_path; for my $dir (split /\//, $path) { $tmp_path .= "$dir/"; -d $tmp_path or mkdir $tmp_path, 0700 or die "Can't mkdir $tmp_path: $!"; } } sub verify_files_exist (@) { my (@files) = @_; foreach my $file (@files) { if ((! -e $file) || (! -s $file)) { die("$file is nonexistent or empty."); } } } # Returns a list ($fh, $filename) sub create_tempfile (;$) { my ($directory) = @_; if (! defined($directory)) { if (! defined($tmpdir)) { $tmpdir = tempdir(CLEANUP => 1); } $directory = $tmpdir; } return tempfile(DIR => $directory); } # Creates a cert data structure used by openssl_parse_pem sub new_cert_structure () { my $cert_data = {}; $cert_data->{datafile} = ""; $cert_data->{type} = ""; $cert_data->{localKeyID} = ""; $cert_data->{subject} = ""; $cert_data->{issuer} = ""; return $cert_data; } sub create_cert_chains (@) { my (@certs) = @_; my (%subject_hash, @leaves, @chains); foreach my $cert (@certs) { $cert->{children} = 0; if ($cert->{subject}) { $subject_hash{$cert->{subject}} = $cert; } } foreach my $cert (@certs) { my $parent = $subject_hash{$cert->{issuer}}; if (defined($parent)) { $parent->{children} += 1; } } @leaves = grep { $_->{children} == 0 } @certs; foreach my $leaf (@leaves) { my $chain = []; my $cert = $leaf; while (defined($cert)) { push @$chain, $cert; $cert = $subject_hash{$cert->{issuer}}; if (defined($cert) && (scalar(grep {$_ == $cert} @$chain) != 0)) { $cert = undef; } } push @chains, $chain; } return @chains; } ################## # openssl helpers ################## sub openssl_exec (@) { my (@args) = @_; my $fh; open($fh, "-|", $opensslbin, @args) or die "Failed to run '$opensslbin @args': $!"; my @output = <$fh>; if (! close($fh)) { # NOTE: Callers should check the value of $? for the exit status. if ($!) { die "Syserr closing '$opensslbin @args' pipe: $!"; } } return @output; } sub openssl_format ($) { my ($filename) = @_; return -B $filename ? 'DER' : 'PEM'; } sub openssl_x509_query ($@) { my ($filename, @query) = @_; my $format = openssl_format($filename); my @args = ("x509", "-in", $filename, "-inform", $format, "-noout", @query); return openssl_exec(@args); } sub openssl_hash ($) { my ($filename) = @_; my $cert_hash = join("", openssl_x509_query($filename, "-hash")); $? and die "openssl -hash '$filename' returned $?"; chomp($cert_hash); return $cert_hash; } sub openssl_fingerprint ($) { my ($filename) = @_; my $fingerprint = join("", openssl_x509_query($filename, "-fingerprint")); $? and die "openssl -fingerprint '$filename' returned $?"; chomp($fingerprint); return $fingerprint; } sub openssl_emails ($) { my ($filename) = @_; my @mailboxes = openssl_x509_query($filename, "-email"); $? and die "openssl -email '$filename' returned $?"; chomp(@mailboxes); return @mailboxes; } sub openssl_p12_to_pem ($$) { my ($p12_file, $pem_file) = @_; my @args = ("pkcs12", "-in", $p12_file, "-out", $pem_file); openssl_exec(@args); $? and die "openssl pkcs12 conversion returned $?"; } sub openssl_verify ($$) { my ($issuer_path, $cert_path) = @_; my @args = ("verify", $root_certs_switch, $root_certs_path, "-purpose", "smimesign", "-purpose", "smimeencrypt", "-untrusted", $issuer_path, $cert_path); my $output = join("", openssl_exec(@args)); chomp($output); return $output; } sub openssl_crl_text($) { my ($crl) = @_; my @args = ("crl", "-text", "-noout", "-in", $crl); my @output = openssl_exec(@args); $? and die "openssl crl -text '$crl' returned $?"; return @output; } sub openssl_trust_flag ($$;$) { my ($cert, $issuerid, $crl) = @_; print "==> about to verify certificate of $cert\n"; my $result = 't'; my $issuer_path; my $cert_path = "$certificates_path/$cert"; if ($issuerid eq '?') { $issuer_path = "$certificates_path/$cert"; } else { $issuer_path = "$certificates_path/$issuerid"; } my $output = openssl_verify($issuer_path, $cert_path); if ($?) { print "openssl verify returned exit code " . ($? >> 8) . " with output:\n"; print "$output\n\n"; print "Marking certificate as invalid\n"; return 'i'; } print "\n$output\n"; if ($output !~ /OK/) { return 'i'; } my ($not_before, $not_after, $serial_in) = openssl_x509_query($cert_path, "-dates", "-serial"); $? and die "openssl -dates -serial '$cert_path' returned $?"; if ( defined $not_before and defined $not_after ) { my %months = ('Jan', '00', 'Feb', '01', 'Mar', '02', 'Apr', '03', 'May', '04', 'Jun', '05', 'Jul', '06', 'Aug', '07', 'Sep', '08', 'Oct', '09', 'Nov', '10', 'Dec', '11'); my @tmp = split (/\=/, $not_before); my $not_before_date = $tmp[1]; my @fields = $not_before_date =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/; if ($#fields == 5) { if (timegm($fields[4], $fields[3], $fields[2], $fields[1], $months{$fields[0]}, $fields[5]) > time) { print "Certificate is not yet valid.\n"; return 'e'; } } else { print "Expiration Date: Parse Error : $not_before_date\n\n"; } @tmp = split (/\=/, $not_after); my $not_after_date = $tmp[1]; @fields = $not_after_date =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/; if ($#fields == 5) { if (timegm($fields[4], $fields[3], $fields[2], $fields[1], $months{$fields[0]}, $fields[5]) < time) { print "Certificate has expired.\n"; return 'e'; } } else { print "Expiration Date: Parse Error : $not_after_date\n\n"; } } if ( defined $crl ) { chomp($serial_in); my @serial = split (/\=/, $serial_in); my $match_line = undef; my @crl_lines = openssl_crl_text($crl); for (my $index = 0; $index <= $#crl_lines; $index++) { if ($crl_lines[$index] =~ /Serial Number:\s*\Q$serial[1]\E\b/) { $match_line = $crl_lines[$index + 1]; last; } } if ( defined $match_line ) { my @revoke_date = split (/:\s/, $match_line); print "FAILURE: Certificate $cert has been revoked on $revoke_date[1]\n"; $result = 'r'; } } print "\n"; return $result; } sub openssl_parse_pem ($$) { my ($filename, $attrs_required) = @_; my $state = 0; my $cert_data; my @certs; my $cert_count = 0; my $bag_count = 0; my $cert_tmp_fh; my $cert_tmp_filename; $cert_data = new_cert_structure(); ($cert_tmp_fh, $cert_data->{datafile}) = create_tempfile(); open(PEM_FILE, "<$filename") or die("Can't open $filename: $!"); while () { if (/^Bag Attributes/) { $bag_count++; $state == 0 or die("PEM-parse error at: $."); $state = 1; } # Allow attributes without the "Bag Attributes" header if ($state != 2) { if (/localKeyID:\s*(.*)/) { $cert_data->{localKeyID} = $1; } if (/subject=\s*(.*)/) { $cert_data->{subject} = $1; } if (/issuer=\s*(.*)/) { $cert_data->{issuer} = $1; } } if (/^-----/) { if (/BEGIN/) { print $cert_tmp_fh $_; $state = 2; if (/PRIVATE/) { $cert_data->{type} = "K"; next; } if (/CERTIFICATE/) { $cert_data->{type} = "C"; next; } die("What's this: $_"); } if (/END/) { $state = 0; print $cert_tmp_fh $_; close($cert_tmp_fh); $cert_count++; push (@certs, $cert_data); $cert_data = new_cert_structure(); ($cert_tmp_fh, $cert_data->{datafile}) = create_tempfile(); next; } } print $cert_tmp_fh $_; } close($cert_tmp_fh); close(PEM_FILE); if ($attrs_required && ($bag_count != $cert_count)) { die("Not all contents were bagged. can't continue."); } return @certs; } sub openssl_dump_cert ($) { my ($filename) = @_; my $format = openssl_format($filename); my @args = ("x509", "-in", $filename, "-inform", $format); my $output = join("", openssl_exec(@args)); $? and die "openssl x509 certicate dump returned $?"; return $output; } sub openssl_purpose_flag ($) { my ($filename) = @_; my $purpose = ""; my @output = openssl_x509_query($filename, "-purpose"); $? and die "openssl -purpose '$filename' returned $?"; foreach my $line (@output) { if ($line =~ /^S\/MIME signing\s*:\s*Yes/) { $purpose .= "s"; } elsif ($line =~ /^S\/MIME encryption\s*:\s*Yes/) { $purpose .= "e"; } } if (! $purpose) { $purpose = "-"; } return $purpose; } ################################# # certificate management methods ################################# sub cm_list_certs () { my %keyflags = ( 'i', '(Invalid)', 'r', '(Revoked)', 'e', '(Expired)', 'u', '(Unverified)', 'v', '(Valid)', 't', '(Trusted)'); open(INDEX, "<$certificates_path/.index") or die "Couldn't open $certificates_path/.index: $!"; print "\n"; while () { my $tmp; my @tmp; my $tab = " "; my @fields = split; if ($fields[2] eq '-') { print "$fields[1]: Issued for: $fields[0] $keyflags{$fields[4]}\n"; } else { print "$fields[1]: Issued for: $fields[0] \"$fields[2]\" $keyflags{$fields[4]}\n"; } my $certfile = "$certificates_path/$fields[1]"; my $cert; { open F, $certfile or die "Couldn't open $certfile: $!"; local $/; $cert = ; close F; } my ($subject_in, $issuer_in, $date1_in, $date2_in) = openssl_x509_query($certfile, "-subject", "-issuer", "-dates"); $? and print "ERROR: openssl -subject -issuer -dates '$certfile' returned $?\n\n" and next; my @subject = split(/\//, $subject_in); while (@subject) { $tmp = shift @subject; ($tmp =~ /^CN\=/) and last; undef $tmp; } defined $tmp and @tmp = split (/\=/, $tmp) and print $tab."Subject: $tmp[1]\n"; my @issuer = split(/\//, $issuer_in); while (@issuer) { $tmp = shift @issuer; ($tmp =~ /^CN\=/) and last; undef $tmp; } defined $tmp and @tmp = split (/\=/, $tmp) and print $tab."Issued by: $tmp[1]"; if ( defined $date1_in and defined $date2_in ) { @tmp = split (/\=/, $date1_in); $tmp = $tmp[1]; @tmp = split (/\=/, $date2_in); print $tab."Certificate is not valid before $tmp". $tab." or after ".$tmp[1]; } -e "$private_keys_path/$fields[1]" and print "$tab - Matching private key installed -\n"; my @purpose = openssl_x509_query($certfile, "-purpose"); $? and die "openssl -purpose '$certfile' returned $?"; chomp(@purpose); print "$tab$purpose[0] (displays S/MIME options only)\n"; while (@purpose) { $tmp = shift @purpose; ($tmp =~ /^S\/MIME/ and $tmp =~ /Yes/) or next; my @tmptmp = split (/:/, $tmp); print "$tab $tmptmp[0]\n"; } print "\n"; } close(INDEX); } sub cm_add_entry ($$$$$$;$) { my ($mailbox, $hashvalue, $use_cert, $label, $trust, $purpose, $issuer_hash) = @_; if (! defined($issuer_hash) ) { $issuer_hash = "?"; } if ($use_cert) { open(INDEX, "+<$certificates_path/.index") or die "Couldn't open $certificates_path/.index: $!"; } else { open(INDEX, "+<$private_keys_path/.index") or die "Couldn't open $private_keys_path/.index: $!"; } while () { my @fields = split; if (($fields[0] eq $mailbox) && ($fields[1] eq $hashvalue)) { close(INDEX); return; } } print INDEX "$mailbox $hashvalue $label $issuer_hash $trust $purpose\n"; close(INDEX); } # Returns the hashvalue.index of the stored cert sub cm_add_cert ($) { my ($filename) = @_; my $iter = 0; my $hashvalue = openssl_hash($filename); my $fp1 = openssl_fingerprint($filename); while (-e "$certificates_path/$hashvalue.$iter") { my $fp2 = openssl_fingerprint("$certificates_path/$hashvalue.$iter"); last if $fp1 eq $fp2; $iter++; } $hashvalue .= ".$iter"; if (-e "$certificates_path/$hashvalue") { print "\nCertificate: $certificates_path/$hashvalue already installed.\n"; } else { mycopy $filename, "$certificates_path/$hashvalue"; } return $hashvalue; } # Returns a reference containing the hashvalue, mailboxes, trust flag, and purpose # flag of the stored cert. sub cm_add_indexed_cert ($$$) { my ($filename, $label, $issuer_hash) = @_; my $cert_data = {}; $cert_data->{hashvalue} = cm_add_cert($filename); $cert_data->{mailboxes} = [ openssl_emails($filename) ]; $cert_data->{trust} = openssl_trust_flag($cert_data->{hashvalue}, $issuer_hash); $cert_data->{purpose} = openssl_purpose_flag($filename); foreach my $mailbox (@{$cert_data->{mailboxes}}) { cm_add_entry($mailbox, $cert_data->{hashvalue}, 1, $label, $cert_data->{trust}, $cert_data->{purpose}, $issuer_hash); print "\ncertificate ", $cert_data->{hashvalue}, " ($label) for $mailbox added.\n"; } return $cert_data; } sub cm_add_key ($$$$$$) { my ($file, $hashvalue, $mailbox, $label, $trust, $purpose) = @_; unless (-e "$private_keys_path/$hashvalue") { mycopy $file, "$private_keys_path/$hashvalue"; } cm_add_entry($mailbox, $hashvalue, 0, $label, $trust, $purpose); print "added private key: " . "$private_keys_path/$hashvalue for $mailbox\n"; } sub cm_modify_entry ($$$;$) { my ($op, $hashvalue, $use_cert, $opt_param) = @_; my $label; my $trust; my $purpose; my $path; my @fields; $op eq 'L' and ($label = $opt_param); $op eq 'T' and ($trust = $opt_param); $op eq 'P' and ($purpose = $opt_param); if ($use_cert) { $path = $certificates_path; } else { $path = $private_keys_path; } open(INDEX, "<$path/.index") or die "Couldn't open $path/.index: $!"; my ($newindex_fh, $newindex) = create_tempfile(); while () { chomp; # fields: mailbox hash label issuer_hash trust purpose @fields = split; if ($fields[1] eq $hashvalue or $hashvalue eq 'all') { $op eq 'R' and next; if ($op eq 'L') { $fields[2] = $label; } if ($op eq 'T') { $fields[3] = "?" if ($#fields < 3); $fields[4] = $trust; } if ($op eq 'P') { $fields[3] = "?" if ($#fields < 3); $fields[4] = "u" if ($#fields < 4); $fields[5] = $purpose; } print $newindex_fh join(" ", @fields), "\n"; } else { print $newindex_fh $_, "\n"; } } close(INDEX); close($newindex_fh); move $newindex, "$path/.index" or die "Couldn't move $newindex to $path/.index: $!\n"; } # This returns the first matching entry. sub cm_find_entry ($$) { my ($hashvalue, $use_cert) = @_; my ($path, $index_fh); if ($use_cert) { $path = $certificates_path; } else { $path = $private_keys_path; } open($index_fh, "<$path/.index") or die "Couldn't open $path/.index: $!"; while (<$index_fh>) { chomp; my @fields = split; if ($fields[1] eq $hashvalue) { close($index_fh); return @fields; } } close($index_fh); return; } # Refreshes trust flags, and adds purpose if missing # (e.g. from an older index format) sub cm_refresh_index () { my $index_fh; my ($last_hash, $last_trust, $last_purpose) = ("", "", ""); open($index_fh, "<$certificates_path/.index") or die "Couldn't open $certificates_path/.index: $!"; my ($newindex_fh, $newindex) = create_tempfile(); while (<$index_fh>) { chomp; # fields: mailbox hash label issuer_hash trust purpose my @fields = split; if ($fields[1] eq $last_hash) { $fields[4] = $last_trust; $fields[5] = $last_purpose; } else { # Don't overwrite a revoked flag, because we don't have the CRL if ($fields[4] ne "r") { $fields[4] = openssl_trust_flag($fields[1], $fields[3]); } if ($#fields < 5) { $fields[5] = openssl_purpose_flag("$certificates_path/$fields[1]"); } # To update an old private keys index format, always push the trust # and purpose out. if (-e "$private_keys_path/$fields[1]") { cm_modify_entry ("T", $fields[1], 0, $fields[4]); cm_modify_entry ("P", $fields[1], 0, $fields[5]); } $last_hash = $fields[1]; $last_trust = $fields[4]; $last_purpose = $fields[5]; } print $newindex_fh join(" ", @fields), "\n"; } close($index_fh); close($newindex_fh); move $newindex, "$certificates_path/.index" or die "Couldn't move $newindex to $certificates_path/.index: $!\n"; } ############## # Op handlers ############## sub handle_init_paths () { mkdir_recursive($certificates_path); mkdir_recursive($private_keys_path); my $file; $file = $certificates_path . "/.index"; -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE) or die "Can't touch $file: $!"; $file = $private_keys_path . "/.index"; -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE) or die "Can't touch $file: $!"; } sub handle_change_label ($) { my ($keyid) = @_; my $label = query_label(); if (-e "$certificates_path/$keyid") { cm_modify_entry('L', $keyid, 1, $label); print "Changed label for certificate $keyid.\n"; } else { die "No such certificate: $keyid"; } if (-e "$private_keys_path/$keyid") { cm_modify_entry('L', $keyid, 0, $label); print "Changed label for private key $keyid.\n"; } } sub handle_add_cert($) { my ($filename) = @_; my $label = query_label(); my @cert_contents = openssl_parse_pem($filename, 0); @cert_contents = grep { $_->{type} eq "C" } @cert_contents; my @cert_chains = create_cert_chains(@cert_contents); print "Found " . scalar(@cert_chains) . " certificate chains\n"; foreach my $chain (@cert_chains) { my $leaf = shift(@$chain); my $issuer_chain_hash = "?"; print "Processing chain:\n"; if ($leaf->{subject}) { print "subject=" . $leaf->{subject} . "\n"; } if (scalar(@$chain) > 0) { my ($issuer_chain_fh, $issuer_chain_file) = create_tempfile(); foreach my $issuer (@$chain) { my $issuer_datafile = $issuer->{datafile}; open(my $issuer_fh, "< $issuer_datafile") or die "can't open $issuer_datafile: $?"; print $issuer_chain_fh $_ while (<$issuer_fh>); close($issuer_fh); } close($issuer_chain_fh); $issuer_chain_hash = cm_add_cert($issuer_chain_file); } cm_add_indexed_cert($leaf->{datafile}, $label, $issuer_chain_hash); } } sub handle_add_pem ($) { my ($filename) = @_; my @pem_contents; my $iter; my $key; my $certificate; my $root_cert; my $issuer_cert_file; @pem_contents = openssl_parse_pem($filename, 1); # look for key $iter = 0; while ($iter <= $#pem_contents) { if ($pem_contents[$iter]->{type} eq "K") { $key = $pem_contents[$iter]; splice(@pem_contents, $iter, 1); last; } $iter++; } defined($key) or die("Couldn't find private key!"); $key->{localKeyID} or die("Attribute 'localKeyID' wasn't set."); # private key and certificate use the same 'localKeyID' $iter = 0; while ($iter <= $#pem_contents) { if (($pem_contents[$iter]->{type} eq "C") && ($pem_contents[$iter]->{localKeyID} eq $key->{localKeyID})) { $certificate = $pem_contents[$iter]; splice(@pem_contents, $iter, 1); last; } $iter++; } defined($certificate) or die("Couldn't find matching certificate!"); if ($#pem_contents < 0) { die("No root and no intermediate certificates. Can't continue."); } # Look for a self signed root certificate $iter = 0; while ($iter <= $#pem_contents) { if ($pem_contents[$iter]->{subject} eq $pem_contents[$iter]->{issuer}) { $root_cert = $pem_contents[$iter]; splice(@pem_contents, $iter, 1); last; } $iter++; } if (defined($root_cert)) { $issuer_cert_file = $root_cert->{datafile}; } else { print "Couldn't identify root certificate!\n"; } # what's left are intermediate certificates. if ($#pem_contents >= 0) { my ($tmp_issuer_cert_fh, $tmp_issuer_cert) = create_tempfile(); $issuer_cert_file = $tmp_issuer_cert; $iter = 0; while ($iter <= $#pem_contents) { my $cert_datafile = $pem_contents[$iter]->{datafile}; open (CERT, "< $cert_datafile") or die "can't open $cert_datafile: $?"; print $tmp_issuer_cert_fh $_ while (); close CERT; $iter++; } close $tmp_issuer_cert_fh; } handle_add_chain($key->{datafile}, $certificate->{datafile}, $issuer_cert_file); } sub handle_add_p12 ($) { my ($filename) = @_; print "\nNOTE: This will ask you for two passphrases:\n"; print " 1. The passphrase you used for exporting\n"; print " 2. The passphrase you wish to secure your private key with.\n\n"; my ($pem_fh, $pem_file) = create_tempfile(); close($pem_fh); openssl_p12_to_pem($filename, $pem_file); -e $pem_file and -s $pem_file or die("Conversion of $filename failed."); handle_add_pem($pem_file); } sub handle_add_chain ($$$) { my ($key_file, $cert_file, $issuer_file) = @_; my $label = query_label(); my $issuer_hash = cm_add_cert($issuer_file); my $cert_data = cm_add_indexed_cert($cert_file, $label, $issuer_hash); foreach my $mailbox (@{$cert_data->{mailboxes}}) { cm_add_key($key_file, $cert_data->{hashvalue}, $mailbox, $label, $cert_data->{trust}, $cert_data->{purpose}); } } sub handle_verify_cert ($$) { my ($keyid, $crl) = @_; -e "$certificates_path/$keyid" or $keyid eq 'all' or die "No such certificate: $keyid"; my @fields = cm_find_entry($keyid, 1); if (scalar(@fields)) { my $issuer_hash = $fields[3]; my $trust = openssl_trust_flag($keyid, $issuer_hash, $crl); cm_modify_entry('T', $keyid, 0, $trust); cm_modify_entry('T', $keyid, 1, $trust); } } sub handle_remove_pair ($) { my ($keyid) = @_; if (-e "$certificates_path/$keyid") { unlink "$certificates_path/$keyid"; cm_modify_entry('R', $keyid, 1); print "Removed certificate $keyid.\n"; } else { die "No such certificate: $keyid"; } if (-e "$private_keys_path/$keyid") { unlink "$private_keys_path/$keyid"; cm_modify_entry('R', $keyid, 0); print "Removed private key $keyid.\n"; } } sub handle_add_root_cert ($) { my ($root_cert) = @_; my $root_hash = openssl_hash($root_cert); if (-d $root_certs_path) { -e "$root_certs_path/$root_hash" or mycopy $root_cert, "$root_certs_path/$root_hash"; } else { open(ROOT_CERTS, ">>$root_certs_path") or die ("Couldn't open $root_certs_path for writing"); my $md5fp = openssl_fingerprint($root_cert); my @cert_text = openssl_x509_query($root_cert, "-text"); $? and die "openssl -text '$root_cert' returned $?"; print "Enter a label, name or description for this certificate: "; my $input = ; my $line = "=======================================\n"; print ROOT_CERTS "\n$input$line$md5fp\nPEM-Data:\n"; my $cert = openssl_dump_cert($root_cert); print ROOT_CERTS $cert; print ROOT_CERTS @cert_text; close (ROOT_CERTS); } } neomutt-neomutt-20171215/contrib/smime_keys_test.pl000066400000000000000000000066501321473123000223460ustar00rootroot00000000000000#! /usr/bin/perl -W # by Mike Schiraldi use strict; use Expect; sub run ($;$ ); umask 077; # probably not necc. but can't hurt my $tmpdir = "/tmp/smime_keys_test-$$-" . time; mkdir $tmpdir or die; chdir $tmpdir or die; open TMP, '>muttrc' or die; print TMP <demoCA/serial' or die; print OUT "01\n"; close OUT; open OUT, '>demoCA/index.txt' or die; close OUT; # make the CA run 'openssl req -new -x509 -keyout demoCA/private/cakey.pem -out demoCA/cacert.pem -days 7300 -nodes', "\n\nx\n\nx\nx\n\n"; # trust it run 'smime_keys add_root demoCA/cacert.pem', "root_CA\n"; # have the CA process the request run 'openssl ca -batch -startdate 000101000000Z -enddate 200101000000Z -days 7300 ' . '-policy policy_anything -out newcert.pem -infiles newreq.pem'; unlink 'newreq.pem' or die; # put it all in a .p12 bundle run 'openssl pkcs12 -export -inkey user.key -in newcert.pem -out cert.p12 -CAfile demoCA/cacert.pem -chain', "pass1\n" x 2; unlink 'newcert.pem' or die; unlink 'demoCA/cacert.pem' or die; unlink 'demoCA/index.txt' or die; unlink 'demoCA/index.txt.attr' or die; unlink 'demoCA/index.txt.old' or die; unlink 'demoCA/serial' or die; unlink 'demoCA/serial.old' or die; unlink 'demoCA/newcerts/01.pem' or die; unlink 'demoCA/private/cakey.pem' or die; rmdir 'demoCA/certs' or die; rmdir 'demoCA/crl' or die; rmdir 'demoCA/private' or die; rmdir 'demoCA/newcerts' or die; rmdir 'demoCA' or die; # have smime_keys process it run 'smime_keys add_p12 cert.p12', "pass1\n" . "pass2\n" x 2 . "old_label\n"; unlink 'cert.p12' or die; # make sure it showed up run 'smime_keys list > list'; open IN, 'list' or die; eq "\n" or die; =~ /^(.*)\: Issued for\: user\@smime\.mutt \"old_label\" \(Trusted\)\n/ or die; close IN; my $keyid = $1; # see if we can rename it run "smime_keys label $keyid", "new_label\n"; # make sure it worked run 'smime_keys list > list'; open IN, 'list' or die; eq "\n" or die; =~ /^$keyid\: Issued for\: user\@smime\.neomutt \"new_label\" \(Trusted\)\n/ or die; close IN; unlink 'list' or die; # try signing something run "openssl smime -sign -signer certificates/$keyid -inkey user.key -in /etc/passwd -certfile ca-bundle.crt > signed"; unlink 'user.key' or die; # verify it run 'openssl smime -verify -out /dev/null -in signed -CAfile ca-bundle.crt'; unlink 'signed' or die; # clean up unlink 'ca-bundle.crt' or die; unlink 'muttrc' or die; unlink 'keys/.index' or die; unlink 'certificates/.index' or die; unlink or die; unlink or die; rmdir 'keys' or die; rmdir 'certificates' or die; chdir '/' or die; rmdir $tmpdir or die; sub run ($;$) { my $cmd = shift or die; my $input = shift; print "\n\nRunning [$cmd]\n"; my $exp = Expect->spawn ($cmd); if (defined $input) { print $exp $input; } $exp->soft_close; $? and die "$cmd returned $?"; } neomutt-neomutt-20171215/contrib/vim-keys/000077500000000000000000000000001321473123000203425ustar00rootroot00000000000000neomutt-neomutt-20171215/contrib/vim-keys/README.md000066400000000000000000000004111321473123000216150ustar00rootroot00000000000000# Vim Keys This NeoMutt config file sets up some keyboard mappings that make NeoMutt more friendly for Vim users. For example: - gg Move to top of Index - G Move to bottom of Index - dd Delete email from Index ## Credits - Ivan Tham neomutt-neomutt-20171215/contrib/vim-keys/vim-keys.rc000066400000000000000000000027171321473123000224430ustar00rootroot00000000000000#------------------------------------------------------------ # Vi Key Bindings #------------------------------------------------------------ # Moving around bind attach,browser,index g noop bind attach,browser,index gg first-entry bind attach,browser,index G last-entry bind pager g noop bind pager gg top bind pager G bottom bind pager k previous-line bind pager j next-line # Scrolling bind attach,browser,pager,index \CF next-page bind attach,browser,pager,index \CB previous-page bind attach,browser,pager,index \Cu half-up bind attach,browser,pager,index \Cd half-down bind browser,pager \Ce next-line bind browser,pager \Cy previous-line bind index \Ce next-line bind index \Cy previous-line bind pager,index d noop bind pager,index dd delete-message # Mail & Reply bind index \Cm list-reply # Doesn't work currently # Threads bind browser,pager,index N search-opposite bind pager,index dT delete-thread bind pager,index dt delete-subthread bind pager,index gt next-thread bind pager,index gT previous-thread bind index za collapse-thread bind index zA collapse-all # Missing :folddisable/foldenable neomutt-neomutt-20171215/copy.c000066400000000000000000000667041321473123000162710ustar00rootroot00000000000000/** * @file * Duplicate the structure of an entire email * * @authors * Copyright (C) 1996-2000,2002,2014 Michael R. Elkins * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "copy.h" #include "address.h" #include "body.h" #include "context.h" #include "envelope.h" #include "globals.h" #include "header.h" #include "mailbox.h" #include "mime.h" #include "mutt_curses.h" #include "mutt_idna.h" #include "mx.h" #include "ncrypt/ncrypt.h" #include "options.h" #include "protos.h" #include "rfc2047.h" #include "state.h" #include "tags.h" #ifdef USE_NOTMUCH #include "mutt_notmuch.h" #endif static int address_header_decode(char **str); static int copy_delete_attach(struct Body *b, FILE *fpin, FILE *fpout, char *date); /** * mutt_copy_hdr - Copy header from one file to another * * Ok, the only reason for not merging this with mutt_copy_header() below is to * avoid creating a Header structure in message_handler(). Also, this one will * wrap headers much more aggressively than the other one. */ int mutt_copy_hdr(FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags, const char *prefix) { bool from = false; bool this_is_from = false; bool ignore = false; char buf[LONG_STRING]; /* should be long enough to get most fields in one pass */ char *nl = NULL; char **headers = NULL; int hdr_count; int x; char *this_one = NULL; size_t this_one_len = 0; int error; if (off_start < 0) return -1; if (ftello(in) != off_start) if (fseeko(in, off_start, SEEK_SET) < 0) return -1; buf[0] = '\n'; buf[1] = '\0'; if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | CH_WEED_DELIVERED)) == 0) { /* Without these flags to complicate things * we can do a more efficient line to line copying */ while (ftello(in) < off_end) { nl = strchr(buf, '\n'); if ((fgets(buf, sizeof(buf), in)) == NULL) break; /* Is it the beginning of a header? */ if (nl && buf[0] != ' ' && buf[0] != '\t') { ignore = true; if (!from && (mutt_str_strncmp("From ", buf, 5) == 0)) { if ((flags & CH_FROM) == 0) continue; from = true; } else if (flags & (CH_NOQFROM) && (mutt_str_strncasecmp(">From ", buf, 6) == 0)) continue; else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) break; /* end of header */ if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) && ((mutt_str_strncasecmp("Status:", buf, 7) == 0) || (mutt_str_strncasecmp("X-Status:", buf, 9) == 0))) continue; if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) && ((mutt_str_strncasecmp("Content-Length:", buf, 15) == 0) || (mutt_str_strncasecmp("Lines:", buf, 6) == 0))) continue; if ((flags & CH_UPDATE_REFS) && (mutt_str_strncasecmp("References:", buf, 11) == 0)) continue; if ((flags & CH_UPDATE_IRT) && (mutt_str_strncasecmp("In-Reply-To:", buf, 12) == 0)) continue; if (flags & CH_UPDATE_LABEL && (mutt_str_strncasecmp("X-Label:", buf, 8) == 0)) continue; ignore = false; } if (!ignore && fputs(buf, out) == EOF) return -1; } return 0; } hdr_count = 1; x = 0; error = false; /* We are going to read and collect the headers in an array * so we are able to do re-ordering. * First count the number of entries in the array */ if (flags & CH_REORDER) { struct ListNode *np; STAILQ_FOREACH(np, &HeaderOrderList, entries) { mutt_debug(3, "Reorder list: %s\n", np->data); hdr_count++; } } mutt_debug(1, "WEED is %s\n", (flags & CH_WEED) ? "Set" : "Not"); headers = mutt_mem_calloc(hdr_count, sizeof(char *)); /* Read all the headers into the array */ while (ftello(in) < off_end) { nl = strchr(buf, '\n'); /* Read a line */ if ((fgets(buf, sizeof(buf), in)) == NULL) break; /* Is it the beginning of a header? */ if (nl && buf[0] != ' ' && buf[0] != '\t') { /* Do we have anything pending? */ if (this_one) { if (flags & CH_DECODE) { if (!address_header_decode(&this_one)) rfc2047_decode(&this_one); this_one_len = mutt_str_strlen(this_one); /* Convert CRLF line endings to LF */ if ((this_one_len > 2) && (this_one[this_one_len - 2] == '\r') && (this_one[this_one_len - 1] == '\n')) { this_one[this_one_len - 2] = '\n'; this_one[this_one_len - 1] = '\0'; } } if (!headers[x]) headers[x] = this_one; else { int hlen = mutt_str_strlen(headers[x]); mutt_mem_realloc(&headers[x], hlen + this_one_len + sizeof(char)); strcat(headers[x] + hlen, this_one); FREE(&this_one); } this_one = NULL; } ignore = true; this_is_from = false; if (!from && (mutt_str_strncmp("From ", buf, 5) == 0)) { if ((flags & CH_FROM) == 0) continue; this_is_from = from = true; } else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) break; /* end of header */ /* note: CH_FROM takes precedence over header weeding. */ if (!((flags & CH_FROM) && (flags & CH_FORCE_FROM) && this_is_from) && (flags & CH_WEED) && mutt_matches_ignore(buf)) { continue; } if ((flags & CH_WEED_DELIVERED) && (mutt_str_strncasecmp("Delivered-To:", buf, 13) == 0)) { continue; } if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) && ((mutt_str_strncasecmp("Status:", buf, 7) == 0) || (mutt_str_strncasecmp("X-Status:", buf, 9) == 0))) { continue; } if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) && ((mutt_str_strncasecmp("Content-Length:", buf, 15) == 0) || (mutt_str_strncasecmp("Lines:", buf, 6) == 0))) { continue; } if ((flags & CH_MIME) && (((mutt_str_strncasecmp("content-", buf, 8) == 0) && ((mutt_str_strncasecmp("transfer-encoding:", buf + 8, 18) == 0) || (mutt_str_strncasecmp("type:", buf + 8, 5) == 0))) || (mutt_str_strncasecmp("mime-version:", buf, 13) == 0))) { continue; } if ((flags & CH_UPDATE_REFS) && (mutt_str_strncasecmp("References:", buf, 11) == 0)) continue; if ((flags & CH_UPDATE_IRT) && (mutt_str_strncasecmp("In-Reply-To:", buf, 12) == 0)) continue; /* Find x -- the array entry where this header is to be saved */ if (flags & CH_REORDER) { struct ListNode *np; x = 0; STAILQ_FOREACH(np, &HeaderOrderList, entries) { ++x; if (mutt_str_strncasecmp(buf, np->data, mutt_str_strlen(np->data)) == 0) { mutt_debug(2, "Reorder: %s matches %s\n", np->data, buf); break; } } } ignore = false; } /* If beginning of header */ if (!ignore) { mutt_debug(2, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count); if (!this_one) { this_one = mutt_str_strdup(buf); this_one_len = mutt_str_strlen(this_one); } else { size_t blen = mutt_str_strlen(buf); mutt_mem_realloc(&this_one, this_one_len + blen + sizeof(char)); strcat(this_one + this_one_len, buf); this_one_len += blen; } } } /* while (ftello (in) < off_end) */ /* Do we have anything pending? -- XXX, same code as in above in the loop. */ if (this_one) { if (flags & CH_DECODE) { if (!address_header_decode(&this_one)) rfc2047_decode(&this_one); this_one_len = mutt_str_strlen(this_one); } if (!headers[x]) headers[x] = this_one; else { int hlen = mutt_str_strlen(headers[x]); mutt_mem_realloc(&headers[x], hlen + this_one_len + sizeof(char)); strcat(headers[x] + hlen, this_one); FREE(&this_one); } this_one = NULL; } /* Now output the headers in order */ for (x = 0; x < hdr_count; x++) { if (headers[x]) { /* We couldn't do the prefixing when reading because RFC2047 * decoding may have concatenated lines. */ if (flags & (CH_DECODE | CH_PREFIX)) { if (mutt_write_one_header(out, 0, headers[x], flags & CH_PREFIX ? prefix : 0, mutt_window_wrap_cols(MuttIndexWindow, Wrap), flags) == -1) { error = true; break; } } else { if (fputs(headers[x], out) == EOF) { error = true; break; } } } } /* Free in a separate loop to be sure that all headers are freed * in case of error. */ for (x = 0; x < hdr_count; x++) FREE(&headers[x]); FREE(&headers); if (error) return -1; return 0; } /** * mutt_copy_header - Copy email header * * flags: * * #CH_DECODE RFC2047 header decoding * * #CH_FROM retain the "From " message separator * * #CH_FORCE_FROM give CH_FROM precedence over CH_WEED * * #CH_MIME ignore MIME fields * * #CH_NOLEN don't write Content-Length: and Lines: * * #CH_NONEWLINE don't output a newline after the header * * #CH_NOSTATUS ignore the Status: and X-Status: * * #CH_PREFIX quote header with $indent_string * * #CH_REORDER output header in order specified by `hdr_order' * * #CH_TXTPLAIN generate text/plain MIME headers [hack alert.] * * #CH_UPDATE write new Status: and X-Status: * * #CH_UPDATE_LEN write new Content-Length: and Lines: * * #CH_XMIT ignore Lines: and Content-Length: * * #CH_WEED do header weeding * * #CH_NOQFROM ignore ">From " line * * #CH_UPDATE_IRT update the In-Reply-To: header * * #CH_UPDATE_REFS update the References: header * * #CH_VIRTUAL write virtual header lines too * * prefix * * string to use if CH_PREFIX is set */ int mutt_copy_header(FILE *in, struct Header *h, FILE *out, int flags, const char *prefix) { char buffer[SHORT_STRING]; if (h->env) flags |= (h->env->irt_changed ? CH_UPDATE_IRT : 0) | (h->env->refs_changed ? CH_UPDATE_REFS : 0); if (mutt_copy_hdr(in, out, h->offset, h->content->offset, flags, prefix) == -1) return -1; if (flags & CH_TXTPLAIN) { char chsbuf[SHORT_STRING]; fputs("MIME-Version: 1.0\n", out); fputs("Content-Transfer-Encoding: 8bit\n", out); fputs("Content-Type: text/plain; charset=", out); mutt_cs_canonical_charset(chsbuf, sizeof(chsbuf), Charset ? Charset : "us-ascii"); mutt_addr_cat(buffer, sizeof(buffer), chsbuf, MimeSpecials); fputs(buffer, out); fputc('\n', out); } if ((flags & CH_UPDATE_IRT) && !STAILQ_EMPTY(&h->env->in_reply_to)) { fputs("In-Reply-To:", out); struct ListNode *np; STAILQ_FOREACH(np, &h->env->in_reply_to, entries) { fputc(' ', out); fputs(np->data, out); } fputc('\n', out); } if ((flags & CH_UPDATE_REFS) && !STAILQ_EMPTY(&h->env->references)) { fputs("References:", out); mutt_write_references(&h->env->references, out, 0); fputc('\n', out); } if ((flags & CH_UPDATE) && (flags & CH_NOSTATUS) == 0) { if (h->old || h->read) { fputs("Status: ", out); if (h->read) fputs("RO", out); else if (h->old) fputc('O', out); fputc('\n', out); } if (h->flagged || h->replied) { fputs("X-Status: ", out); if (h->replied) fputc('A', out); if (h->flagged) fputc('F', out); fputc('\n', out); } } if (flags & CH_UPDATE_LEN && (flags & CH_NOLEN) == 0) { fprintf(out, "Content-Length: " OFF_T_FMT "\n", h->content->length); if (h->lines != 0 || h->content->length == 0) fprintf(out, "Lines: %d\n", h->lines); } #ifdef USE_NOTMUCH if (flags & CH_VIRTUAL) { /* Add some fake headers based on notmuch data */ char *folder = nm_header_get_folder(h); if (folder && !(option(OPT_WEED) && mutt_matches_ignore("folder"))) { char buf[LONG_STRING]; mutt_str_strfcpy(buf, folder, sizeof(buf)); mutt_pretty_mailbox(buf, sizeof(buf)); fputs("Folder: ", out); fputs(buf, out); fputc('\n', out); } } #endif char *tags = driver_tags_get(&h->tags); if (tags && !(option(OPT_WEED) && mutt_matches_ignore("tags"))) { fputs("Tags: ", out); fputs(tags, out); fputc('\n', out); } FREE(&tags); if (flags & CH_UPDATE_LABEL) { h->xlabel_changed = false; if (h->env->x_label) if (fprintf(out, "X-Label: %s\n", h->env->x_label) != 10 + strlen(h->env->x_label)) return -1; } if ((flags & CH_NONEWLINE) == 0) { if (flags & CH_PREFIX) fputs(prefix, out); fputc('\n', out); /* add header terminator */ } if (ferror(out) || feof(out)) return -1; return 0; } /** * count_delete_lines - Count lines to be deleted in this email body * * Count the number of lines and bytes to be deleted in this body */ static int count_delete_lines(FILE *fp, struct Body *b, LOFF_T *length, size_t datelen) { int dellines = 0; int ch; if (b->deleted) { fseeko(fp, b->offset, SEEK_SET); for (long l = b->length; l; l--) { ch = getc(fp); if (ch == EOF) break; if (ch == '\n') dellines++; } dellines -= 3; *length -= b->length - (84 + datelen); /* Count the number of digits exceeding the first one to write the size */ for (long l = 10; b->length >= l; l *= 10) (*length)++; } else { for (b = b->parts; b; b = b->next) dellines += count_delete_lines(fp, b, length, datelen); } return dellines; } /** * mutt_copy_message_fp - make a copy of a message from a FILE pointer * @param fpout Where to write output * @param fpin Where to get input * @param hdr Header of message being copied * @param flags See below * @param chflags Flags to mutt_copy_header() * * * #MUTT_CM_NOHEADER don't copy header * * #MUTT_CM_PREFIX quote header and body * * #MUTT_CM_DECODE decode message body to text/plain * * #MUTT_CM_DISPLAY displaying output to the user * * #MUTT_CM_PRINTING printing the message * * #MUTT_CM_UPDATE update structures in memory after syncing * * #MUTT_CM_DECODE_PGP used for decoding PGP messages * * #MUTT_CM_CHARCONV perform character set conversion */ int mutt_copy_message_fp(FILE *fpout, FILE *fpin, struct Header *hdr, int flags, int chflags) { struct Body *body = hdr->content; char prefix[SHORT_STRING]; struct State s; LOFF_T new_offset = -1; int rc = 0; if (flags & MUTT_CM_PREFIX) { if (option(OPT_TEXT_FLOWED)) mutt_str_strfcpy(prefix, ">", sizeof(prefix)); else mutt_make_string_flags(prefix, sizeof(prefix), NONULL(IndentString), Context, hdr, 0); } if (hdr->xlabel_changed) chflags |= CH_UPDATE_LABEL; if ((flags & MUTT_CM_NOHEADER) == 0) { if (flags & MUTT_CM_PREFIX) chflags |= CH_PREFIX; else if (hdr->attach_del && (chflags & CH_UPDATE_LEN)) { int new_lines; LOFF_T new_length = body->length; char date[SHORT_STRING]; mutt_date_make_date(date, sizeof(date)); int dlen = mutt_str_strlen(date); if (dlen == 0) return -1; date[5] = '\"'; date[dlen - 1] = '\"'; /* Count the number of lines and bytes to be deleted */ fseeko(fpin, body->offset, SEEK_SET); new_lines = hdr->lines - count_delete_lines(fpin, body, &new_length, dlen); /* Copy the headers */ if (mutt_copy_header(fpin, hdr, fpout, chflags | CH_NOLEN | CH_NONEWLINE, NULL)) return -1; fprintf(fpout, "Content-Length: " OFF_T_FMT "\n", new_length); if (new_lines <= 0) new_lines = 0; else fprintf(fpout, "Lines: %d\n", new_lines); putc('\n', fpout); if (ferror(fpout) || feof(fpout)) return -1; new_offset = ftello(fpout); /* Copy the body */ if (fseeko(fpin, body->offset, SEEK_SET) < 0) return -1; if (copy_delete_attach(body, fpin, fpout, date)) return -1; LOFF_T fail = ((ftello(fpout) - new_offset) - new_length); if (fail) { mutt_error("The length calculation was wrong by %ld bytes", fail); new_length += fail; mutt_sleep(1); } /* Update original message if we are sync'ing a mailfolder */ if (flags & MUTT_CM_UPDATE) { hdr->attach_del = false; hdr->lines = new_lines; body->offset = new_offset; /* update the total size of the mailbox to reflect this deletion */ Context->size -= body->length - new_length; /* * if the message is visible, update the visible size of the mailbox * as well. */ if (Context->v2r[hdr->msgno] != -1) Context->vsize -= body->length - new_length; body->length = new_length; mutt_free_body(&body->parts); } return 0; } if (mutt_copy_header(fpin, hdr, fpout, chflags, (chflags & CH_PREFIX) ? prefix : NULL) == -1) return -1; new_offset = ftello(fpout); } if (flags & MUTT_CM_DECODE) { /* now make a text/plain version of the message */ memset(&s, 0, sizeof(struct State)); s.fpin = fpin; s.fpout = fpout; if (flags & MUTT_CM_PREFIX) s.prefix = prefix; if (flags & MUTT_CM_DISPLAY) s.flags |= MUTT_DISPLAY; if (flags & MUTT_CM_PRINTING) s.flags |= MUTT_PRINTING; if (flags & MUTT_CM_WEED) s.flags |= MUTT_WEED; if (flags & MUTT_CM_CHARCONV) s.flags |= MUTT_CHARCONV; if (flags & MUTT_CM_REPLYING) s.flags |= MUTT_REPLYING; if (WithCrypto && flags & MUTT_CM_VERIFY) s.flags |= MUTT_VERIFY; rc = mutt_body_handler(body, &s); } else if (WithCrypto && (flags & MUTT_CM_DECODE_CRYPT) && (hdr->security & ENCRYPT)) { struct Body *cur = NULL; FILE *fp = NULL; if ((WithCrypto & APPLICATION_PGP) && (flags & MUTT_CM_DECODE_PGP) && (hdr->security & APPLICATION_PGP) && hdr->content->type == TYPEMULTIPART) { if (crypt_pgp_decrypt_mime(fpin, &fp, hdr->content, &cur)) return -1; fputs("MIME-Version: 1.0\n", fpout); } if ((WithCrypto & APPLICATION_SMIME) && (flags & MUTT_CM_DECODE_SMIME) && (hdr->security & APPLICATION_SMIME) && hdr->content->type == TYPEAPPLICATION) { if (crypt_smime_decrypt_mime(fpin, &fp, hdr->content, &cur)) return -1; } if (!cur) { mutt_error(_("No decryption engine available for message")); return -1; } mutt_write_mime_header(cur, fpout); fputc('\n', fpout); if (fseeko(fp, cur->offset, SEEK_SET) < 0) return -1; if (mutt_file_copy_bytes(fp, fpout, cur->length) == -1) { mutt_file_fclose(&fp); mutt_free_body(&cur); return -1; } mutt_free_body(&cur); mutt_file_fclose(&fp); } else { if (fseeko(fpin, body->offset, SEEK_SET) < 0) return -1; if (flags & MUTT_CM_PREFIX) { int c; size_t bytes = body->length; fputs(prefix, fpout); while ((c = fgetc(fpin)) != EOF && bytes--) { fputc(c, fpout); if (c == '\n') { fputs(prefix, fpout); } } } else if (mutt_file_copy_bytes(fpin, fpout, body->length) == -1) return -1; } if ((flags & MUTT_CM_UPDATE) && (flags & MUTT_CM_NOHEADER) == 0 && new_offset != -1) { body->offset = new_offset; mutt_free_body(&body->parts); } return rc; } /** * mutt_copy_message_ctx - Copy a message from a Context * * should be made to return -1 on fatal errors, and 1 on non-fatal errors * like partial decode, where it is worth displaying as much as possible */ int mutt_copy_message_ctx(FILE *fpout, struct Context *src, struct Header *hdr, int flags, int chflags) { struct Message *msg = NULL; int r; msg = mx_open_message(src, hdr->msgno); if (!msg) return -1; if (!hdr->content) return -1; r = mutt_copy_message_fp(fpout, msg->fp, hdr, flags, chflags); if ((r == 0) && (ferror(fpout) || feof(fpout))) { mutt_debug(1, "failed to detect EOF!\n"); r = -1; } mx_close_message(src, &msg); return r; } /** * append_message - appends a copy of the given message to a mailbox * @param dest destination mailbox * @param fpin where to get input * @param src source mailbox * @param hdr message being copied * @param flags mutt_open_copy_message() flags * @param chflags mutt_copy_header() flags * @retval 0 on success * @retval -1 on error */ static int append_message(struct Context *dest, FILE *fpin, struct Context *src, struct Header *hdr, int flags, int chflags) { char buf[STRING]; struct Message *msg = NULL; int r; if (fseeko(fpin, hdr->offset, SEEK_SET) < 0) return -1; if (fgets(buf, sizeof(buf), fpin) == NULL) return -1; msg = mx_open_new_message(dest, hdr, is_from(buf, NULL, 0, NULL) ? 0 : MUTT_ADD_FROM); if (!msg) return -1; if (dest->magic == MUTT_MBOX || dest->magic == MUTT_MMDF) chflags |= CH_FROM | CH_FORCE_FROM; chflags |= (dest->magic == MUTT_MAILDIR ? CH_NOSTATUS : CH_UPDATE); r = mutt_copy_message_fp(msg->fp, fpin, hdr, flags, chflags); if (mx_commit_message(msg, dest) != 0) r = -1; #ifdef USE_NOTMUCH if (msg->commited_path && dest->magic == MUTT_MAILDIR && src->magic == MUTT_NOTMUCH) nm_update_filename(src, NULL, msg->commited_path, hdr); #endif mx_close_message(dest, &msg); return r; } int mutt_append_message(struct Context *dest, struct Context *src, struct Header *hdr, int cmflags, int chflags) { struct Message *msg = NULL; int r; msg = mx_open_message(src, hdr->msgno); if (!msg) return -1; r = append_message(dest, msg->fp, src, hdr, cmflags, chflags); mx_close_message(src, &msg); return r; } /** * copy_delete_attach - Copy a message, deleting marked attachments * @retval 0 on success * @retval -1 on failure * * This function copies a message body, while deleting _in_the_copy_ * any attachments which are marked for deletion. * Nothing is changed in the original message -- this is left to the caller. */ static int copy_delete_attach(struct Body *b, FILE *fpin, FILE *fpout, char *date) { struct Body *part = NULL; for (part = b->parts; part; part = part->next) { if (part->deleted || part->parts) { /* Copy till start of this part */ if (mutt_file_copy_bytes(fpin, fpout, part->hdr_offset - ftello(fpin))) return -1; if (part->deleted) { fprintf( fpout, "Content-Type: message/external-body; access-type=x-mutt-deleted;\n" "\texpiration=%s; length=" OFF_T_FMT "\n" "\n", date + 5, part->length); if (ferror(fpout)) return -1; /* Copy the original mime headers */ if (mutt_file_copy_bytes(fpin, fpout, part->offset - ftello(fpin))) return -1; /* Skip the deleted body */ fseeko(fpin, part->offset + part->length, SEEK_SET); } else { if (copy_delete_attach(part, fpin, fpout, date)) return -1; } } } /* Copy the last parts */ if (mutt_file_copy_bytes(fpin, fpout, b->offset + b->length - ftello(fpin))) return -1; return 0; } /** * format_address_header - Write address headers to a buffer * * This function is the equivalent of mutt_write_address_list(), but writes to * a buffer instead of writing to a stream. mutt_write_address_list could be * re-used if we wouldn't store all the decoded headers in a huge array, first. * * XXX - fix that. */ static void format_address_header(char **h, struct Address *a) { char buf[HUGE_STRING]; char cbuf[STRING]; char c2buf[STRING]; char *p = NULL; size_t l, linelen, buflen, cbuflen, c2buflen, plen; linelen = mutt_str_strlen(*h); plen = linelen; buflen = linelen + 3; mutt_mem_realloc(h, buflen); for (int count = 0; a; a = a->next, count++) { struct Address *tmp = a->next; a->next = NULL; *buf = *cbuf = *c2buf = '\0'; l = rfc822_write_address(buf, sizeof(buf), a, 0); a->next = tmp; if (count && linelen + l > 74) { strcpy(cbuf, "\n\t"); linelen = l + 8; } else { if (a->mailbox) { strcpy(cbuf, " "); linelen++; } linelen += l; } if (!a->group && a->next && a->next->mailbox) { linelen++; buflen++; strcpy(c2buf, ","); } cbuflen = mutt_str_strlen(cbuf); c2buflen = mutt_str_strlen(c2buf); buflen += l + cbuflen + c2buflen; mutt_mem_realloc(h, buflen); p = *h; strcat(p + plen, cbuf); plen += cbuflen; strcat(p + plen, buf); plen += l; strcat(p + plen, c2buf); plen += c2buflen; } /* Space for this was allocated in the beginning of this function. */ strcat(p + plen, "\n"); } static int address_header_decode(char **h) { char *s = *h; size_t l; bool rp = false; struct Address *a = NULL; struct Address *cur = NULL; switch (tolower((unsigned char) *s)) { case 'r': { if (mutt_str_strncasecmp(s, "return-path:", 12) == 0) { l = 12; rp = true; break; } else if (mutt_str_strncasecmp(s, "reply-to:", 9) == 0) { l = 9; break; } return 0; } case 'f': { if (mutt_str_strncasecmp(s, "from:", 5) != 0) return 0; l = 5; break; } case 'c': { if (mutt_str_strncasecmp(s, "cc:", 3) != 0) return 0; l = 3; break; } case 'b': { if (mutt_str_strncasecmp(s, "bcc:", 4) != 0) return 0; l = 4; break; } case 's': { if (mutt_str_strncasecmp(s, "sender:", 7) != 0) return 0; l = 7; break; } case 't': { if (mutt_str_strncasecmp(s, "to:", 3) != 0) return 0; l = 3; break; } case 'm': { if (mutt_str_strncasecmp(s, "mail-followup-to:", 17) != 0) return 0; l = 17; break; } default: return 0; } a = mutt_addr_parse_list(a, s + l); if (!a) return 0; mutt_addrlist_to_local(a); rfc2047_decode_adrlist(a); for (cur = a; cur; cur = cur->next) if (cur->personal) mutt_str_dequote_comment(cur->personal); /* angle brackets for return path are mandated by RFC5322, * so leave Return-Path as-is */ if (rp) *h = mutt_str_strdup(s); else { *h = mutt_mem_calloc(1, l + 2); mutt_str_strfcpy(*h, s, l + 1); format_address_header(h, a); } mutt_addr_free(&a); FREE(&s); return 1; } neomutt-neomutt-20171215/copy.h000066400000000000000000000077671321473123000163020ustar00rootroot00000000000000/** * @file * Duplicate the structure of an entire email * * @authors * Copyright (C) 1996-2000 Michael R. Elkins * * @copyright * 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, see . */ #ifndef _MUTT_COPY_H #define _MUTT_COPY_H #include struct Body; struct Header; struct Context; /**< flags to mutt_copy_message */ #define MUTT_CM_NOHEADER (1 << 0) /**< don't copy the message header */ #define MUTT_CM_PREFIX (1 << 1) /**< quote the message */ #define MUTT_CM_DECODE (1 << 2) /**< decode the message body into text/plain */ #define MUTT_CM_DISPLAY (1 << 3) /**< output is displayed to the user */ #define MUTT_CM_UPDATE (1 << 4) /**< update structs on sync */ #define MUTT_CM_WEED (1 << 5) /**< weed message/rfc822 attachment headers */ #define MUTT_CM_CHARCONV (1 << 6) /**< perform character set conversions */ #define MUTT_CM_PRINTING (1 << 7) /**< printing the message - display light */ #define MUTT_CM_REPLYING (1 << 8) /**< replying the message */ #define MUTT_CM_DECODE_PGP (1 << 9) /**< used for decoding PGP messages */ #define MUTT_CM_DECODE_SMIME (1 << 10) /**< used for decoding S/MIME messages */ #define MUTT_CM_DECODE_CRYPT (MUTT_CM_DECODE_PGP | MUTT_CM_DECODE_SMIME) #define MUTT_CM_VERIFY (1 << 11) /**< do signature verification */ /* flags for mutt_copy_header() */ #define CH_UPDATE (1 << 0) /**< update the status and x-status fields? */ #define CH_WEED (1 << 1) /**< weed the headers? */ #define CH_DECODE (1 << 2) /**< do RFC1522 decoding? */ #define CH_XMIT (1 << 3) /**< transmitting this message? */ #define CH_FROM (1 << 4) /**< retain the "From " message separator? */ #define CH_PREFIX (1 << 5) /**< use IndentString string? */ #define CH_NOSTATUS (1 << 6) /**< suppress the status and x-status fields */ #define CH_REORDER (1 << 7) /**< Re-order output of headers */ #define CH_NONEWLINE (1 << 8) /**< don't output terminating newline */ #define CH_MIME (1 << 9) /**< ignore MIME fields */ #define CH_UPDATE_LEN (1 << 10) /**< update Lines: and Content-Length: */ #define CH_TXTPLAIN (1 << 11) /**< generate text/plain MIME headers */ #define CH_NOLEN (1 << 12) /**< don't write Content-Length: and Lines: */ #define CH_WEED_DELIVERED (1 << 13) /**< weed eventual Delivered-To headers */ #define CH_FORCE_FROM (1 << 14) /**< give CH_FROM precedence over CH_WEED? */ #define CH_NOQFROM (1 << 15) /**< ignore ">From " line */ #define CH_UPDATE_IRT (1 << 16) /**< update In-Reply-To: */ #define CH_UPDATE_REFS (1 << 17) /**< update References: */ #define CH_DISPLAY (1 << 18) /**< display result to user */ #define CH_UPDATE_LABEL (1 << 19) /**< update X-Label: from hdr->env->x_label? */ #define CH_VIRTUAL (1 << 20) /**< write virtual header lines too */ int mutt_copy_hdr(FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags, const char *prefix); int mutt_copy_header(FILE *in, struct Header *h, FILE *out, int flags, const char *prefix); int mutt_copy_message_fp (FILE *fpout, FILE *fpin, struct Header *hdr, int flags, int chflags); int mutt_copy_message_ctx(FILE *fpout, struct Context *src, struct Header *hdr, int flags, int chflags); int mutt_append_message(struct Context *dest, struct Context *src, struct Header *hdr, int cmflags, int chflags); #endif /* _MUTT_COPY_H */ neomutt-neomutt-20171215/curs_lib.c000066400000000000000000001066601321473123000171150ustar00rootroot00000000000000/** * @file * GUI miscellaneous curses (window drawing) routines * * @authors * Copyright (C) 1996-2002,2010,2012-2013 Michael R. Elkins * Copyright (C) 2004 g10 Code GmbH * * @copyright * 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, see . */ #include "config.h" #include #include #include #include #include #ifdef ENABLE_NLS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "mutt/mutt.h" #include "mutt.h" #include "context.h" #include "enter_state.h" #include "globals.h" #include "header.h" #include "mbyte.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mutt_regex.h" #include "opcodes.h" #include "options.h" #include "pager.h" #include "protos.h" #ifdef HAVE_ISWBLANK #include #endif #ifdef USE_NOTMUCH #include "mutt_notmuch.h" #endif /* not possible to unget more than one char under some curses libs, and it * is impossible to unget function keys in SLang, so roll our own input * buffering routines. */ /* These are used for macros and exec/push commands. * They can be temporarily ignored by setting OPT_IGNORE_MACRO_EVENTS */ static size_t MacroBufferCount = 0; static size_t MacroBufferLen = 0; static struct Event *MacroEvents; /* These are used in all other "normal" situations, and are not * ignored when setting OPT_IGNORE_MACRO_EVENTS */ static size_t UngetCount = 0; static size_t UngetLen = 0; static struct Event *UngetKeyEvents; struct MuttWindow *MuttHelpWindow = NULL; struct MuttWindow *MuttIndexWindow = NULL; struct MuttWindow *MuttStatusWindow = NULL; struct MuttWindow *MuttMessageWindow = NULL; #ifdef USE_SIDEBAR struct MuttWindow *MuttSidebarWindow = NULL; #endif static void reflow_message_window_rows(int mw_rows); void mutt_refresh(void) { /* don't refresh when we are waiting for a child. */ if (option(OPT_KEEP_QUIET)) return; /* don't refresh in the middle of macros unless necessary */ if (MacroBufferCount && !option(OPT_FORCE_REFRESH) && !option(OPT_IGNORE_MACRO_EVENTS)) return; /* else */ refresh(); } /** * mutt_need_hard_redraw - Force a hard refresh * * Make sure that the next refresh does a full refresh. This could be * optimized by not doing it at all if DISPLAY is set as this might indicate * that a GUI based pinentry was used. Having an option to customize this is * of course the NeoMutt way. */ void mutt_need_hard_redraw(void) { keypad(stdscr, true); clearok(stdscr, true); mutt_set_current_menu_redraw_full(); } struct Event mutt_getch(void) { int ch; struct Event err = { -1, OP_NULL }, ret; struct Event timeout = { -2, OP_NULL }; if (UngetCount) return UngetKeyEvents[--UngetCount]; if (!option(OPT_IGNORE_MACRO_EVENTS) && MacroBufferCount) return MacroEvents[--MacroBufferCount]; SigInt = 0; mutt_sig_allow_interrupt(1); #ifdef KEY_RESIZE /* ncurses 4.2 sends this when the screen is resized */ ch = KEY_RESIZE; while (ch == KEY_RESIZE) #endif /* KEY_RESIZE */ ch = getch(); mutt_sig_allow_interrupt(0); if (SigInt) { mutt_query_exit(); return err; } /* either timeout, a sigwinch (if timeout is set), or the terminal * has been lost */ if (ch == ERR) { if (!isatty(0)) { endwin(); exit(1); } return timeout; } if ((ch & 0x80) && option(OPT_META_KEY)) { /* send ALT-x as ESC-x */ ch &= ~0x80; mutt_unget_event(ch, 0); ret.ch = '\033'; ret.op = 0; return ret; } ret.ch = ch; ret.op = 0; return (ch == ctrl('G') ? err : ret); } int mutt_get_field_full(const char *field, char *buf, size_t buflen, int complete, int multiple, char ***files, int *numfiles) { int ret; int x; struct EnterState *es = mutt_new_enter_state(); do { #if defined(USE_SLANG_CURSES) || defined(HAVE_RESIZETERM) if (SigWinch) { SigWinch = 0; mutt_resize_screen(); clearok(stdscr, TRUE); mutt_current_menu_redraw(); } #endif mutt_window_clearline(MuttMessageWindow, 0); SETCOLOR(MT_COLOR_PROMPT); addstr((char *) field); /* cast to get around bad prototypes */ NORMAL_COLOR; mutt_refresh(); mutt_window_getyx(MuttMessageWindow, NULL, &x); ret = mutt_enter_string_full(buf, buflen, x, complete, multiple, files, numfiles, es); } while (ret == 1); mutt_window_clearline(MuttMessageWindow, 0); mutt_free_enter_state(&es); return ret; } int mutt_get_field_unbuffered(char *msg, char *buf, size_t buflen, int flags) { int rc; set_option(OPT_IGNORE_MACRO_EVENTS); rc = mutt_get_field(msg, buf, buflen, flags); unset_option(OPT_IGNORE_MACRO_EVENTS); return rc; } void mutt_clear_error(void) { ErrorBuf[0] = 0; if (!option(OPT_NO_CURSES)) mutt_window_clearline(MuttMessageWindow, 0); } void mutt_edit_file(const char *editor, const char *data) { char cmd[LONG_STRING]; mutt_endwin(NULL); mutt_expand_file_fmt(cmd, sizeof(cmd), editor, data); if (mutt_system(cmd) != 0) { mutt_error(_("Error running \"%s\"!"), cmd); mutt_sleep(2); } #if defined(USE_SLANG_CURSES) || defined(HAVE_RESIZETERM) /* the terminal may have been resized while the editor owned it */ mutt_resize_screen(); #endif keypad(stdscr, true); clearok(stdscr, true); } int mutt_yesorno(const char *msg, int def) { struct Event ch; char *yes = _("yes"); char *no = _("no"); char *answer_string = NULL; int answer_string_wid, msg_wid; size_t trunc_msg_len; bool redraw = true; int prompt_lines = 1; char *expr = NULL; regex_t reyes; regex_t reno; int reyes_ok; int reno_ok; char answer[2]; answer[1] = '\0'; reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') && (REGCOMP(&reyes, expr, REG_NOSUB) == 0); reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') && (REGCOMP(&reno, expr, REG_NOSUB) == 0); /* * In order to prevent the default answer to the question to wrapped * around the screen in the even the question is wider than the screen, * ensure there is enough room for the answer and truncate the question * to fit. */ safe_asprintf(&answer_string, " ([%s]/%s): ", def == MUTT_YES ? yes : no, def == MUTT_YES ? no : yes); answer_string_wid = mutt_strwidth(answer_string); msg_wid = mutt_strwidth(msg); while (true) { if (redraw || SigWinch) { redraw = false; #if defined(USE_SLANG_CURSES) || defined(HAVE_RESIZETERM) if (SigWinch) { SigWinch = 0; mutt_resize_screen(); clearok(stdscr, TRUE); mutt_current_menu_redraw(); } #endif if (MuttMessageWindow->cols) { prompt_lines = (msg_wid + answer_string_wid + MuttMessageWindow->cols - 1) / MuttMessageWindow->cols; prompt_lines = MAX(1, MIN(3, prompt_lines)); } if (prompt_lines != MuttMessageWindow->rows) { reflow_message_window_rows(prompt_lines); mutt_current_menu_redraw(); } /* maxlen here is sort of arbitrary, so pick a reasonable upper bound */ trunc_msg_len = mutt_wstr_trunc( msg, 4 * prompt_lines * MuttMessageWindow->cols, prompt_lines * MuttMessageWindow->cols - answer_string_wid, NULL); mutt_window_move(MuttMessageWindow, 0, 0); SETCOLOR(MT_COLOR_PROMPT); addnstr(msg, trunc_msg_len); addstr(answer_string); NORMAL_COLOR; mutt_window_clrtoeol(MuttMessageWindow); } mutt_refresh(); /* SigWinch is not processed unless timeout is set */ timeout(30 * 1000); ch = mutt_getch(); timeout(-1); if (ch.ch == -2) continue; if (CI_is_return(ch.ch)) break; if (ch.ch < 0) { def = MUTT_ABORT; break; } answer[0] = ch.ch; if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (tolower(ch.ch) == 'y')) { def = MUTT_YES; break; } else if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (tolower(ch.ch) == 'n')) { def = MUTT_NO; break; } else { BEEP(); } } FREE(&answer_string); if (reyes_ok) regfree(&reyes); if (reno_ok) regfree(&reno); if (MuttMessageWindow->rows != 1) { reflow_message_window_rows(1); mutt_current_menu_redraw(); } else mutt_window_clearline(MuttMessageWindow, 0); if (def != MUTT_ABORT) { addstr((char *) (def == MUTT_YES ? yes : no)); mutt_refresh(); } else { /* when the users cancels with ^G, clear the message stored with * mutt_message() so it isn't displayed when the screen is refreshed. */ mutt_clear_error(); } return def; } /** * mutt_query_exit - Ask the user if they want to leave NeoMutt * * This function is called when the user presses the abort key. */ void mutt_query_exit(void) { mutt_flushinp(); curs_set(1); if (Timeout) timeout(-1); /* restore blocking operation */ if (mutt_yesorno(_("Exit NeoMutt?"), MUTT_YES) == MUTT_YES) { endwin(); exit(1); } mutt_clear_error(); mutt_curs_set(-1); SigInt = 0; } static void curses_message(int error, const char *fmt, va_list ap) { char scratch[LONG_STRING]; vsnprintf(scratch, sizeof(scratch), fmt, ap); mutt_debug(1, "%s\n", scratch); mutt_simple_format(ErrorBuf, sizeof(ErrorBuf), 0, MuttMessageWindow->cols, FMT_LEFT, 0, scratch, sizeof(scratch), 0); if (!option(OPT_KEEP_QUIET)) { if (error) BEEP(); SETCOLOR(error ? MT_COLOR_ERROR : MT_COLOR_MESSAGE); mutt_window_mvaddstr(MuttMessageWindow, 0, 0, ErrorBuf); NORMAL_COLOR; mutt_window_clrtoeol(MuttMessageWindow); mutt_refresh(); } if (error) set_option(OPT_MSG_ERR); else unset_option(OPT_MSG_ERR); } void mutt_curses_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); curses_message(1, fmt, ap); va_end(ap); } void mutt_curses_message(const char *fmt, ...) { va_list ap; va_start(ap, fmt); curses_message(0, fmt, ap); va_end(ap); } void mutt_progress_init(struct Progress *progress, const char *msg, unsigned short flags, unsigned short inc, size_t size) { struct timeval tv = { 0, 0 }; if (!progress) return; if (option(OPT_NO_CURSES)) return; memset(progress, 0, sizeof(struct Progress)); progress->inc = inc; progress->flags = flags; progress->msg = msg; progress->size = size; if (progress->size) { if (progress->flags & MUTT_PROGRESS_SIZE) mutt_pretty_size(progress->sizestr, sizeof(progress->sizestr), progress->size); else snprintf(progress->sizestr, sizeof(progress->sizestr), "%zu", progress->size); } if (!inc) { if (size) mutt_message("%s (%s)", msg, progress->sizestr); else mutt_message(msg); return; } if (gettimeofday(&tv, NULL) < 0) mutt_debug(1, "gettimeofday failed: %d\n", errno); /* if timestamp is 0 no time-based suppression is done */ if (TimeInc) progress->timestamp = ((unsigned int) tv.tv_sec * 1000) + (unsigned int) (tv.tv_usec / 1000); mutt_progress_update(progress, 0, 0); } /** * message_bar - Draw a colourful progress bar * @param percent %age complete * @param fmt printf(1)-like formatting string * @param ... Arguments to formatting string */ static void message_bar(int percent, const char *fmt, ...) { va_list ap; char buf[STRING], buf2[STRING]; int w = percent * COLS / 100; size_t l; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); l = mutt_strwidth(buf); va_end(ap); mutt_simple_format(buf2, sizeof(buf2), 0, COLS - 2, FMT_LEFT, 0, buf, sizeof(buf), 0); move(LINES - 1, 0); if (ColorDefs[MT_COLOR_PROGRESS] == 0) { addstr(buf2); } else { if (l < w) { /* The string fits within the colour bar */ SETCOLOR(MT_COLOR_PROGRESS); addstr(buf2); w -= l; while (w--) { addch(' '); } SETCOLOR(MT_COLOR_NORMAL); } else { /* The string is too long for the colour bar */ char ch; int off = mutt_wstr_trunc(buf2, sizeof(buf2), w, NULL); ch = buf2[off]; buf2[off] = '\0'; SETCOLOR(MT_COLOR_PROGRESS); addstr(buf2); buf2[off] = ch; SETCOLOR(MT_COLOR_NORMAL); addstr(&buf2[off]); } } clrtoeol(); mutt_refresh(); } void mutt_progress_update(struct Progress *progress, long pos, int percent) { char posstr[SHORT_STRING]; bool update = false; struct timeval tv = { 0, 0 }; unsigned int now = 0; if (option(OPT_NO_CURSES)) return; if (!progress->inc) goto out; /* refresh if size > inc */ if (progress->flags & MUTT_PROGRESS_SIZE && (pos >= progress->pos + (progress->inc << 10))) update = true; else if (pos >= progress->pos + progress->inc) update = true; /* skip refresh if not enough time has passed */ if (update && progress->timestamp && !gettimeofday(&tv, NULL)) { now = ((unsigned int) tv.tv_sec * 1000) + (unsigned int) (tv.tv_usec / 1000); if (now && now - progress->timestamp < TimeInc) update = false; } /* always show the first update */ if (!pos) update = true; if (update) { if (progress->flags & MUTT_PROGRESS_SIZE) { pos = pos / (progress->inc << 10) * (progress->inc << 10); mutt_pretty_size(posstr, sizeof(posstr), pos); } else snprintf(posstr, sizeof(posstr), "%ld", pos); mutt_debug(5, "updating progress: %s\n", posstr); progress->pos = pos; if (now) progress->timestamp = now; if (progress->size > 0) { message_bar((percent > 0) ? percent : (int) (100.0 * (double) progress->pos / progress->size), "%s %s/%s (%d%%)", progress->msg, posstr, progress->sizestr, (percent > 0) ? percent : (int) (100.0 * (double) progress->pos / progress->size)); } else { if (percent > 0) message_bar(percent, "%s %s (%d%%)", progress->msg, posstr, percent); else mutt_message("%s %s", progress->msg, posstr); } } out: if (pos >= progress->size) mutt_clear_error(); } void mutt_init_windows(void) { MuttHelpWindow = mutt_mem_calloc(1, sizeof(struct MuttWindow)); MuttIndexWindow = mutt_mem_calloc(1, sizeof(struct MuttWindow)); MuttStatusWindow = mutt_mem_calloc(1, sizeof(struct MuttWindow)); MuttMessageWindow = mutt_mem_calloc(1, sizeof(struct MuttWindow)); #ifdef USE_SIDEBAR MuttSidebarWindow = mutt_mem_calloc(1, sizeof(struct MuttWindow)); #endif } void mutt_free_windows(void) { FREE(&MuttHelpWindow); FREE(&MuttIndexWindow); FREE(&MuttStatusWindow); FREE(&MuttMessageWindow); #ifdef USE_SIDEBAR FREE(&MuttSidebarWindow); #endif } void mutt_reflow_windows(void) { if (option(OPT_NO_CURSES)) return; mutt_debug(2, "entering\n"); MuttStatusWindow->rows = 1; MuttStatusWindow->cols = COLS; MuttStatusWindow->row_offset = option(OPT_STATUS_ON_TOP) ? 0 : LINES - 2; MuttStatusWindow->col_offset = 0; memcpy(MuttHelpWindow, MuttStatusWindow, sizeof(struct MuttWindow)); if (!option(OPT_HELP)) MuttHelpWindow->rows = 0; else MuttHelpWindow->row_offset = option(OPT_STATUS_ON_TOP) ? LINES - 2 : 0; memcpy(MuttMessageWindow, MuttStatusWindow, sizeof(struct MuttWindow)); MuttMessageWindow->row_offset = LINES - 1; memcpy(MuttIndexWindow, MuttStatusWindow, sizeof(struct MuttWindow)); MuttIndexWindow->rows = MAX( LINES - MuttStatusWindow->rows - MuttHelpWindow->rows - MuttMessageWindow->rows, 0); MuttIndexWindow->row_offset = option(OPT_STATUS_ON_TOP) ? MuttStatusWindow->rows : MuttHelpWindow->rows; #ifdef USE_SIDEBAR if (option(OPT_SIDEBAR_VISIBLE)) { memcpy(MuttSidebarWindow, MuttIndexWindow, sizeof(struct MuttWindow)); MuttSidebarWindow->cols = SidebarWidth; MuttIndexWindow->cols -= SidebarWidth; if (option(OPT_SIDEBAR_ON_RIGHT)) { MuttSidebarWindow->col_offset = COLS - SidebarWidth; } else { MuttIndexWindow->col_offset += SidebarWidth; } } #endif mutt_set_current_menu_redraw_full(); /* the pager menu needs this flag set to recalc line_info */ mutt_set_current_menu_redraw(REDRAW_FLOW); } static void reflow_message_window_rows(int mw_rows) { MuttMessageWindow->rows = mw_rows; MuttMessageWindow->row_offset = LINES - mw_rows; MuttStatusWindow->row_offset = option(OPT_STATUS_ON_TOP) ? 0 : LINES - mw_rows - 1; if (option(OPT_HELP)) MuttHelpWindow->row_offset = option(OPT_STATUS_ON_TOP) ? LINES - mw_rows - 1 : 0; MuttIndexWindow->rows = MAX( LINES - MuttStatusWindow->rows - MuttHelpWindow->rows - MuttMessageWindow->rows, 0); #ifdef USE_SIDEBAR if (option(OPT_SIDEBAR_VISIBLE)) MuttSidebarWindow->rows = MuttIndexWindow->rows; #endif /* We don't also set REDRAW_FLOW because this function only * changes rows and is a temporary adjustment. */ mutt_set_current_menu_redraw_full(); } int mutt_window_move(struct MuttWindow *win, int row, int col) { return move(win->row_offset + row, win->col_offset + col); } int mutt_window_mvaddch(struct MuttWindow *win, int row, int col, const chtype ch) { return mvaddch(win->row_offset + row, win->col_offset + col, ch); } int mutt_window_mvaddstr(struct MuttWindow *win, int row, int col, const char *str) { return mvaddstr(win->row_offset + row, win->col_offset + col, str); } #ifdef USE_SLANG_CURSES static int vw_printw(SLcurses_Window_Type *win, const char *fmt, va_list ap) { char buf[LONG_STRING]; (void) SLvsnprintf(buf, sizeof(buf), (char *) fmt, ap); SLcurses_waddnstr(win, buf, -1); return 0; } #endif int mutt_window_mvprintw(struct MuttWindow *win, int row, int col, const char *fmt, ...) { va_list ap; int rc; rc = mutt_window_move(win, row, col); if (rc != ERR) { va_start(ap, fmt); rc = vw_printw(stdscr, fmt, ap); va_end(ap); } return rc; } /** * mutt_window_clrtoeol - Clear to the end of the line * * Assumes the cursor has already been positioned within the window. */ void mutt_window_clrtoeol(struct MuttWindow *win) { int row, col, curcol; if (win->col_offset + win->cols == COLS) clrtoeol(); else { getyx(stdscr, row, col); curcol = col; while (curcol < win->col_offset + win->cols) { addch(' '); curcol++; } move(row, col); } } void mutt_window_clearline(struct MuttWindow *win, int row) { mutt_window_move(win, row, 0); mutt_window_clrtoeol(win); } /** * mutt_window_getyx - Get the cursor position in the window * * Assumes the current position is inside the window. Otherwise it will * happily return negative or values outside the window boundaries */ void mutt_window_getyx(struct MuttWindow *win, int *y, int *x) { int row, col; getyx(stdscr, row, col); if (y) *y = row - win->row_offset; if (x) *x = col - win->col_offset; } void mutt_show_error(void) { if (option(OPT_KEEP_QUIET)) return; SETCOLOR(option(OPT_MSG_ERR) ? MT_COLOR_ERROR : MT_COLOR_MESSAGE); mutt_window_mvaddstr(MuttMessageWindow, 0, 0, ErrorBuf); NORMAL_COLOR; mutt_window_clrtoeol(MuttMessageWindow); } void mutt_endwin(const char *msg) { int e = errno; if (!option(OPT_NO_CURSES)) { /* at least in some situations (screen + xterm under SuSE11/12) endwin() * doesn't properly flush the screen without an explicit call. */ mutt_refresh(); endwin(); } if (msg && *msg) { puts(msg); fflush(stdout); } errno = e; } void mutt_perror_debug(const char *s) { char *p = strerror(errno); mutt_debug(1, "%s: %s (errno = %d)\n", s, p ? p : "unknown error", errno); mutt_error("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno); } int mutt_any_key_to_continue(const char *s) { struct termios t; struct termios old; int f, ch; f = open("/dev/tty", O_RDONLY); if (f < 0) return EOF; tcgetattr(f, &t); memcpy((void *) &old, (void *) &t, sizeof(struct termios)); /* save original state */ t.c_lflag &= ~(ICANON | ECHO); t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; tcsetattr(f, TCSADRAIN, &t); fflush(stdout); if (s) fputs(s, stdout); else fputs(_("Press any key to continue..."), stdout); fflush(stdout); ch = fgetc(stdin); fflush(stdin); tcsetattr(f, TCSADRAIN, &old); close(f); fputs("\r\n", stdout); mutt_clear_error(); return (ch >= 0) ? ch : EOF; } int mutt_do_pager(const char *banner, const char *tempfile, int do_color, struct Pager *info) { int rc; if (!Pager || (mutt_str_strcmp(Pager, "builtin") == 0)) rc = mutt_pager(banner, tempfile, do_color, info); else { char cmd[STRING]; mutt_endwin(NULL); mutt_expand_file_fmt(cmd, sizeof(cmd), Pager, tempfile); if (mutt_system(cmd) == -1) { mutt_error(_("Error running \"%s\"!"), cmd); rc = -1; } else rc = 0; mutt_file_unlink(tempfile); } return rc; } int mutt_enter_fname_full(const char *prompt, char *buf, size_t blen, int buffy, int multiple, char ***files, int *numfiles, int flags) { struct Event ch; SETCOLOR(MT_COLOR_PROMPT); mutt_window_mvaddstr(MuttMessageWindow, 0, 0, (char *) prompt); addstr(_(" ('?' for list): ")); NORMAL_COLOR; if (buf[0]) addstr(buf); mutt_window_clrtoeol(MuttMessageWindow); mutt_refresh(); ch = mutt_getch(); if (ch.ch < 0) { mutt_window_clearline(MuttMessageWindow, 0); return -1; } else if (ch.ch == '?') { mutt_refresh(); buf[0] = '\0'; if (!flags) flags = MUTT_SEL_FOLDER; if (multiple) flags |= MUTT_SEL_MULTI; if (buffy) flags |= MUTT_SEL_BUFFY; mutt_select_file(buf, blen, flags, files, numfiles); } else { char *pc = mutt_mem_malloc(mutt_str_strlen(prompt) + 3); sprintf(pc, "%s: ", prompt); mutt_unget_event(ch.op ? 0 : ch.ch, ch.op ? ch.op : 0); if (mutt_get_field_full(pc, buf, blen, (buffy ? MUTT_EFILE : MUTT_FILE) | MUTT_CLEAR, multiple, files, numfiles) != 0) buf[0] = '\0'; FREE(&pc); #ifdef USE_NOTMUCH if ((flags & MUTT_SEL_VFOLDER) && buf[0] && (strncmp(buf, "notmuch://", 10) != 0)) nm_description_to_path(buf, buf, blen); #endif } return 0; } void mutt_unget_event(int ch, int op) { struct Event tmp; tmp.ch = ch; tmp.op = op; if (UngetCount >= UngetLen) mutt_mem_realloc(&UngetKeyEvents, (UngetLen += 16) * sizeof(struct Event)); UngetKeyEvents[UngetCount++] = tmp; } void mutt_unget_string(char *s) { char *p = s + mutt_str_strlen(s) - 1; while (p >= s) { mutt_unget_event((unsigned char) *p--, 0); } } /** * mutt_push_macro_event - Add the character/operation to the macro buffer * * Adds the ch/op to the macro buffer. * This should be used for macros, push, and exec commands only. */ void mutt_push_macro_event(int ch, int op) { struct Event tmp; tmp.ch = ch; tmp.op = op; if (MacroBufferCount >= MacroBufferLen) mutt_mem_realloc(&MacroEvents, (MacroBufferLen += 128) * sizeof(struct Event)); MacroEvents[MacroBufferCount++] = tmp; } void mutt_flush_macro_to_endcond(void) { UngetCount = 0; while (MacroBufferCount > 0) { if (MacroEvents[--MacroBufferCount].op == OP_END_COND) return; } } /** * mutt_flush_unget_to_endcond - Clear entries from UngetKeyEvents * * Normally, OP_END_COND should only be in the MacroEvent buffer. * km_error_key() (ab)uses OP_END_COND as a barrier in the unget buffer, and * calls this function to flush. */ void mutt_flush_unget_to_endcond(void) { while (UngetCount > 0) { if (UngetKeyEvents[--UngetCount].op == OP_END_COND) return; } } void mutt_flushinp(void) { UngetCount = 0; MacroBufferCount = 0; flushinp(); } #if (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET)) /** * mutt_curs_set - Set the cursor position * @param cursor * * -1: restore the value of the last call * * 0: make the cursor invisible * * 1: make the cursor visible */ void mutt_curs_set(int cursor) { static int SavedCursor = 1; if (cursor < 0) cursor = SavedCursor; else SavedCursor = cursor; if (curs_set(cursor) == ERR) { if (cursor == 1) /* cnorm */ curs_set(2); /* cvvis */ } } #endif int mutt_multi_choice(char *prompt, char *letters) { struct Event ch; int choice; bool redraw = true; int prompt_lines = 1; char *p = NULL; while (true) { if (redraw || SigWinch) { redraw = false; #if defined(USE_SLANG_CURSES) || defined(HAVE_RESIZETERM) if (SigWinch) { SigWinch = 0; mutt_resize_screen(); clearok(stdscr, TRUE); mutt_current_menu_redraw(); } #endif if (MuttMessageWindow->cols) { prompt_lines = (mutt_strwidth(prompt) + MuttMessageWindow->cols - 1) / MuttMessageWindow->cols; prompt_lines = MAX(1, MIN(3, prompt_lines)); } if (prompt_lines != MuttMessageWindow->rows) { reflow_message_window_rows(prompt_lines); mutt_current_menu_redraw(); } SETCOLOR(MT_COLOR_PROMPT); mutt_window_mvaddstr(MuttMessageWindow, 0, 0, prompt); NORMAL_COLOR; mutt_window_clrtoeol(MuttMessageWindow); } mutt_refresh(); /* SigWinch is not processed unless timeout is set */ timeout(30 * 1000); ch = mutt_getch(); timeout(-1); if (ch.ch == -2) continue; /* (ch.ch == 0) is technically possible. Treat the same as < 0 (abort) */ if (ch.ch <= 0 || CI_is_return(ch.ch)) { choice = -1; break; } else { p = strchr(letters, ch.ch); if (p) { choice = p - letters + 1; break; } else if (ch.ch <= '9' && ch.ch > '0') { choice = ch.ch - '0'; if (choice <= mutt_str_strlen(letters)) break; } } BEEP(); } if (MuttMessageWindow->rows != 1) { reflow_message_window_rows(1); mutt_current_menu_redraw(); } else mutt_window_clearline(MuttMessageWindow, 0); mutt_refresh(); return choice; } /** * mutt_addwch - addwch would be provided by an up-to-date curses library */ int mutt_addwch(wchar_t wc) { char buf[MB_LEN_MAX * 2]; mbstate_t mbstate; size_t n1, n2; memset(&mbstate, 0, sizeof(mbstate)); if ((n1 = wcrtomb(buf, wc, &mbstate)) == (size_t)(-1) || (n2 = wcrtomb(buf + n1, 0, &mbstate)) == (size_t)(-1)) { return -1; /* ERR */ } else { return addstr(buf); } } /** * mutt_simple_format - Format a string, like snprintf() * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] min_width Minimum width * @param[in] max_width Maximum width * @param[in] justify Justification, e.g. #FMT_RIGHT * @param[in] pad_char Padding character * @param[in] s String to format * @param[in] n Number of bytes of string to format * @param[in] arboreal If true, string contains graphical tree characters * * This formats a string, a bit like snprintf(buf, buflen, "%-*.*s", * min_width, max_width, s), except that the widths refer to the number of * character cells when printed. */ void mutt_simple_format(char *buf, size_t buflen, int min_width, int max_width, int justify, char pad_char, const char *s, size_t n, int arboreal) { char *p = NULL; wchar_t wc; int w; size_t k, k2; char scratch[MB_LEN_MAX]; mbstate_t mbstate1, mbstate2; int escaped = 0; memset(&mbstate1, 0, sizeof(mbstate1)); memset(&mbstate2, 0, sizeof(mbstate2)); buflen--; p = buf; for (; n && (k = mbrtowc(&wc, s, n, &mbstate1)); s += k, n -= k) { if (k == (size_t)(-1) || k == (size_t)(-2)) { if (k == (size_t)(-1) && errno == EILSEQ) memset(&mbstate1, 0, sizeof(mbstate1)); k = (k == (size_t)(-1)) ? 1 : n; wc = ReplacementChar; } if (escaped) { escaped = 0; w = 0; } else if (arboreal && wc == MUTT_SPECIAL_INDEX) { escaped = 1; w = 0; } else if (arboreal && wc < MUTT_TREE_MAX) { w = 1; /* hack */ } else { #ifdef HAVE_ISWBLANK if (iswblank(wc)) wc = ' '; else #endif if (!IsWPrint(wc)) wc = '?'; w = wcwidth(wc); } if (w >= 0) { if (w > max_width || (k2 = wcrtomb(scratch, wc, &mbstate2)) > buflen) continue; min_width -= w; max_width -= w; strncpy(p, scratch, k2); p += k2; buflen -= k2; } } w = (int) buflen < min_width ? buflen : min_width; if (w <= 0) *p = '\0'; else if (justify == FMT_RIGHT) /* right justify */ { p[w] = '\0'; while (--p >= buf) p[w] = *p; while (--w >= 0) buf[w] = pad_char; } else if (justify == FMT_CENTER) /* center */ { char *savedp = p; int half = (w + 1) / 2; /* half of cushion space */ p[w] = '\0'; /* move str to center of buffer */ while (--p >= buf) p[half] = *p; /* fill rhs */ p = savedp + half; while (--w >= half) *p++ = pad_char; /* fill lhs */ while (half--) buf[half] = pad_char; } else /* left justify */ { while (--w >= 0) *p++ = pad_char; *p = '\0'; } } /** * format_s_x - Format a string like snprintf() * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] prec Field precision, e.g. "-3.4" * @param[in] s String to format * @param[in] arboreal If true, string contains graphical tree characters * * This formats a string rather like * snprintf (fmt, sizeof (fmt), "%%%ss", prec); * snprintf (buf, buflen, fmt, s); * except that the numbers in the conversion specification refer to * the number of character cells when printed. */ static void format_s_x(char *buf, size_t buflen, const char *prec, const char *s, int arboreal) { int justify = FMT_RIGHT; char *p = NULL; int min_width; int max_width = INT_MAX; if (*prec == '-') { prec++; justify = FMT_LEFT; } else if (*prec == '=') { prec++; justify = FMT_CENTER; } min_width = strtol(prec, &p, 10); if (*p == '.') { prec = p + 1; max_width = strtol(prec, &p, 10); if (p <= prec) max_width = INT_MAX; } mutt_simple_format(buf, buflen, min_width, max_width, justify, ' ', s, mutt_str_strlen(s), arboreal); } /** * mutt_format_s - Format a simple string * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] prec Field precision, e.g. "-3.4" * @param[in] s String to format */ void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s) { format_s_x(buf, buflen, prec, s, 0); } /** * mutt_format_s_tree - Format a simple string with tree characters * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] prec Field precision, e.g. "-3.4" * @param[in] s String to format */ void mutt_format_s_tree(char *buf, size_t buflen, const char *prec, const char *s) { format_s_x(buf, buflen, prec, s, 1); } /** * mutt_paddstr - Display a string on screen, padded if necessary * @param n Final width of field * @param s String to display */ void mutt_paddstr(int n, const char *s) { wchar_t wc; int w; size_t k; size_t len = mutt_str_strlen(s); mbstate_t mbstate; memset(&mbstate, 0, sizeof(mbstate)); for (; len && (k = mbrtowc(&wc, s, len, &mbstate)); s += k, len -= k) { if (k == (size_t)(-1) || k == (size_t)(-2)) { if (k == (size_t)(-1)) memset(&mbstate, 0, sizeof(mbstate)); k = (k == (size_t)(-1)) ? 1 : len; wc = ReplacementChar; } if (!IsWPrint(wc)) wc = '?'; w = wcwidth(wc); if (w >= 0) { if (w > n) break; addnstr((char *) s, k); n -= w; } } while (n-- > 0) addch(' '); } /** * mutt_wstr_trunc - Work out how to truncate a widechar string * @param[in] src String to measure * @param[in] maxlen Maximum length of string in bytes * @param[in] maxwid Maximum width in screen columns * @param[out] width Save the truncated screen column width * @retval n Number of bytes to use * * See how many bytes to copy from string so it's at most maxlen bytes long and * maxwid columns wide */ size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width) { wchar_t wc; size_t n, w = 0, l = 0, cl; int cw; mbstate_t mbstate; if (!src) goto out; n = mutt_str_strlen(src); memset(&mbstate, 0, sizeof(mbstate)); for (w = 0; n && (cl = mbrtowc(&wc, src, n, &mbstate)); src += cl, n -= cl) { if (cl == (size_t)(-1) || cl == (size_t)(-2)) { if (cl == (size_t)(-1)) memset(&mbstate, 0, sizeof(mbstate)); cl = (cl == (size_t)(-1)) ? 1 : n; wc = ReplacementChar; } cw = wcwidth(wc); /* hack because MUTT_TREE symbols aren't turned into characters * until rendered by print_enriched_string (#3364) */ if ((cw < 0) && (src[0] == MUTT_SPECIAL_INDEX)) { cl = 2; /* skip the index coloring sequence */ cw = 0; } else if (cw < 0 && cl == 1 && src[0] && src[0] < MUTT_TREE_MAX) cw = 1; else if (cw < 0) cw = 0; /* unprintable wchar */ if (cl + l > maxlen || cw + w > maxwid) break; l += cl; w += cw; } out: if (width) *width = w; return l; } /** * mutt_strwidth - Measure a string's width in screen cells * @param s String to be measured * @retval n Number of screen cells string would use */ int mutt_strwidth(const char *s) { wchar_t wc; int w; size_t k, n; mbstate_t mbstate; if (!s) return 0; n = mutt_str_strlen(s); memset(&mbstate, 0, sizeof(mbstate)); for (w = 0; n && (k = mbrtowc(&wc, s, n, &mbstate)); s += k, n -= k) { if (*s == MUTT_SPECIAL_INDEX) { s += 2; /* skip the index coloring sequence */ k = 0; continue; } if (k == (size_t)(-1) || k == (size_t)(-2)) { if (k == (size_t)(-1)) memset(&mbstate, 0, sizeof(mbstate)); k = (k == (size_t)(-1)) ? 1 : n; wc = ReplacementChar; } if (!IsWPrint(wc)) wc = '?'; w += wcwidth(wc); } return w; } /** * message_is_visible - Is a message in the index within limit * @param ctx Open mailbox * @param index Message ID (index into `ctx->hdrs[]` * @retval bool True if the message is within limit * * If no limit is in effect, all the messages are visible. */ bool message_is_visible(struct Context *ctx, int index) { if (!ctx || !ctx->hdrs || (index >= ctx->msgcount)) return false; return !ctx->pattern || ctx->hdrs[index]->limited; } /** * message_is_tagged - Is a message in the index tagged (and within limit) * @param ctx Open mailbox * @param index Message ID (index into `ctx->hdrs[]` * @retval bool True if the message is both tagged and within limit * * If a limit is in effect, the message must be visible within it. */ bool message_is_tagged(struct Context *ctx, int index) { return message_is_visible(ctx, index) && ctx->hdrs[index]->tagged; } neomutt-neomutt-20171215/curs_main.c000066400000000000000000002754431321473123000173010ustar00rootroot00000000000000/** * @file * GUI manage the main index (list of emails) * * @authors * Copyright (C) 1996-2000,2002,2010,2012-2013 Michael R. Elkins * * @copyright * 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, see . */ #include "config.h" #include #include #ifdef ENABLE_NLS #include #endif #include #include #include #include #include #include "mutt/mutt.h" #include "conn/conn.h" #include "mutt.h" #include "alias.h" #include "body.h" #include "buffy.h" #include "context.h" #include "envelope.h" #include "format_flags.h" #include "globals.h" #include "header.h" #include "keymap.h" #include "mailbox.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mx.h" #include "ncrypt/ncrypt.h" #include "opcodes.h" #include "options.h" #include "pattern.h" #include "protos.h" #include "sort.h" #include "tags.h" #include "thread.h" #ifdef HAVE_NCURSESW_NCURSES_H #include #elif defined(HAVE_NCURSES_NCURSES_H) #include #elif !defined(USE_SLANG_CURSES) #include #endif #ifdef USE_SIDEBAR #include "sidebar.h" #endif #ifdef USE_POP #include "pop.h" #endif #ifdef USE_IMAP #include "imap/imap.h" #endif #ifdef USE_NOTMUCH #include "mutt_notmuch.h" #endif #ifdef USE_NNTP #include "nntp.h" #endif static const char *No_mailbox_is_open = N_("No mailbox is open."); static const char *There_are_no_messages = N_("There are no messages."); static const char *Mailbox_is_read_only = N_("Mailbox is read-only."); static const char *Function_not_permitted_in_attach_message_mode = N_("Function not permitted in attach-message mode."); static const char *NoVisible = N_("No visible messages."); #define CHECK_IN_MAILBOX \ if (!Context) \ { \ mutt_flushinp(); \ mutt_error(_(No_mailbox_is_open)); \ break; \ } #define CHECK_MSGCOUNT \ if (!Context) \ { \ mutt_flushinp(); \ mutt_error(_(No_mailbox_is_open)); \ break; \ } \ else if (!Context->msgcount) \ { \ mutt_flushinp(); \ mutt_error(_(There_are_no_messages)); \ break; \ } #define CHECK_VISIBLE \ if (Context && menu->current >= Context->vcount) \ { \ mutt_flushinp(); \ mutt_error(_(NoVisible)); \ break; \ } #define CHECK_READONLY \ if (Context->readonly) \ { \ mutt_flushinp(); \ mutt_error(_(Mailbox_is_read_only)); \ break; \ } #define CHECK_ACL(aclbit, action) \ if (!mutt_bit_isset(Context->rights, aclbit)) \ { \ mutt_flushinp(); \ /* L10N: %s is one of the CHECK_ACL entries below. */ \ mutt_error(_("%s: Operation not permitted by ACL"), action); \ break; \ } #define CHECK_ATTACH \ if (option(OPT_ATTACH_MSG)) \ { \ mutt_flushinp(); \ mutt_error(_(Function_not_permitted_in_attach_message_mode)); \ break; \ } #define CURHDR Context->hdrs[Context->v2r[menu->current]] #define UNREAD(h) mutt_thread_contains_unread(Context, h) #define FLAGGED(h) mutt_thread_contains_flagged(Context, h) #define CAN_COLLAPSE(header) \ ((option(OPT_COLLAPSE_UNREAD) || !UNREAD(header)) && \ (option(OPT_COLLAPSE_FLAGGED) || !FLAGGED(header))) /* de facto standard escapes for tsl/fsl */ static char *tsl = "\033]0;"; static char *fsl = "\007"; /** * collapse/uncollapse all threads * @param menu current menu * @param toggle toggle collapsed state * * This function is called by the OP_MAIN_COLLAPSE_ALL command and on folder * enter if the OPT_COLLAPSE_ALL option is set. In the first case, the @a toggle * parameter is 1 to actually toggle collapsed/uncollapsed state on all * threads. In the second case, the @a toggle parameter is 0, actually turning * this function into a one-way collapse. */ static void collapse_all(struct Menu *menu, int toggle) { struct Header *h = NULL, *base = NULL; struct MuttThread *thread = NULL, *top = NULL; int final; if (!Context || (Context->msgcount == 0)) return; /* Figure out what the current message would be after folding / unfolding, * so that we can restore the cursor in a sane way afterwards. */ if (CURHDR->collapsed && toggle) final = mutt_uncollapse_thread(Context, CURHDR); else if (CAN_COLLAPSE(CURHDR)) final = mutt_collapse_thread(Context, CURHDR); else final = CURHDR->virtual; base = Context->hdrs[Context->v2r[final]]; /* Iterate all threads, perform collapse/uncollapse as needed */ top = Context->tree; Context->collapsed = toggle ? !Context->collapsed : true; while ((thread = top) != NULL) { while (!thread->message) thread = thread->child; h = thread->message; if (h->collapsed != Context->collapsed) { if (h->collapsed) mutt_uncollapse_thread(Context, h); else if (CAN_COLLAPSE(h)) mutt_collapse_thread(Context, h); } top = top->next; } /* Restore the cursor */ mutt_set_virtual(Context); for (int j = 0; j < Context->vcount; j++) { if (Context->hdrs[Context->v2r[j]]->index == base->index) { menu->current = j; break; } } menu->redraw = REDRAW_INDEX | REDRAW_STATUS; } static int ci_next_undeleted(int msgno) { for (int i = msgno + 1; i < Context->vcount; i++) if (!Context->hdrs[Context->v2r[i]]->deleted) return i; return -1; } static int ci_previous_undeleted(int msgno) { for (int i = msgno - 1; i >= 0; i--) if (!Context->hdrs[Context->v2r[i]]->deleted) return i; return -1; } /** * ci_first_message - Get index of first new message * * Return the index of the first new message, or failing that, the first * unread message. */ static int ci_first_message(void) { int old = -1; if (Context && Context->msgcount) { for (int i = 0; i < Context->vcount; i++) { if (!Context->hdrs[Context->v2r[i]]->read && !Context->hdrs[Context->v2r[i]]->deleted) { if (!Context->hdrs[Context->v2r[i]]->old) return i; else if (old == -1) old = i; } } if (old != -1) return old; /* If Sort is reverse and not threaded, the latest message is first. * If Sort is threaded, the latest message is first iff exactly one * of Sort and SortAux are reverse. */ if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) || ((Sort & SORT_MASK) == SORT_THREADS && ((Sort ^ SortAux) & SORT_REVERSE))) { return 0; } else { return (Context->vcount ? Context->vcount - 1 : 0); } } return 0; } /** * mx_toggle_write - Toggle the mailbox's readonly flag * * This should be in mx.c, but it only gets used here. */ static int mx_toggle_write(struct Context *ctx) { if (!ctx) return -1; if (ctx->readonly) { mutt_error(_("Cannot toggle write on a readonly mailbox!")); return -1; } if (ctx->dontwrite) { ctx->dontwrite = false; mutt_message(_("Changes to folder will be written on folder exit.")); } else { ctx->dontwrite = true; mutt_message(_("Changes to folder will not be written.")); } return 0; } static void resort_index(struct Menu *menu) { struct Header *current = CURHDR; menu->current = -1; mutt_sort_headers(Context, 0); /* Restore the current message */ for (int i = 0; i < Context->vcount; i++) { if (Context->hdrs[Context->v2r[i]] == current) { menu->current = i; break; } } if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0) menu->current = mutt_parent_message(Context, current, 0); if (menu->current < 0) menu->current = ci_first_message(); menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } void update_index(struct Menu *menu, struct Context *ctx, int check, int oldcount, int index_hint) { /* store pointers to the newly added messages */ struct Header **save_new = NULL; if (!menu || !ctx) return; /* take note of the current message */ if (oldcount) { if (menu->current < ctx->vcount) menu->oldcurrent = index_hint; else oldcount = 0; /* invalid message number! */ } /* We are in a limited view. Check if the new message(s) satisfy * the limit criteria. If they do, set their virtual msgno so that * they will be visible in the limited view */ if (ctx->pattern) { for (int i = (check == MUTT_REOPENED) ? 0 : oldcount; i < ctx->msgcount; i++) { if (!i) ctx->vcount = 0; if (mutt_pattern_exec(ctx->limit_pattern, MUTT_MATCH_FULL_ADDRESS, ctx, ctx->hdrs[i], NULL)) { assert(ctx->vcount < ctx->msgcount); ctx->hdrs[i]->virtual = ctx->vcount; ctx->v2r[ctx->vcount] = i; ctx->hdrs[i]->limited = true; ctx->vcount++; struct Body *b = ctx->hdrs[i]->content; ctx->vsize += b->length + b->offset - b->hdr_offset; } } } /* save the list of new messages */ if (option(OPT_UNCOLLAPSE_NEW) && oldcount && check != MUTT_REOPENED && ((Sort & SORT_MASK) == SORT_THREADS)) { save_new = mutt_mem_malloc(sizeof(struct Header *) * (ctx->msgcount - oldcount)); for (int i = oldcount; i < ctx->msgcount; i++) save_new[i - oldcount] = ctx->hdrs[i]; } /* if the mailbox was reopened, need to rethread from scratch */ mutt_sort_headers(ctx, (check == MUTT_REOPENED)); /* uncollapse threads with new mail */ if (option(OPT_UNCOLLAPSE_NEW) && ((Sort & SORT_MASK) == SORT_THREADS)) { if (check == MUTT_REOPENED) { struct MuttThread *h = NULL, *k = NULL; ctx->collapsed = false; for (h = ctx->tree; h; h = h->next) { for (k = h; !k->message; k = k->child) ; mutt_uncollapse_thread(ctx, k->message); } mutt_set_virtual(ctx); } else if (oldcount) { for (int i = 0; i < ctx->msgcount - oldcount; i++) { for (int k = 0; k < ctx->msgcount; k++) { struct Header *h = ctx->hdrs[k]; if (h == save_new[i] && (!ctx->pattern || h->limited)) mutt_uncollapse_thread(ctx, h); } } FREE(&save_new); mutt_set_virtual(ctx); } } menu->current = -1; if (oldcount) { /* restore the current message to the message it was pointing to */ for (int i = 0; i < ctx->vcount; i++) { if (ctx->hdrs[ctx->v2r[i]]->index == menu->oldcurrent) { menu->current = i; break; } } } if (menu->current < 0) menu->current = ci_first_message(); } static int main_change_folder(struct Menu *menu, int op, char *buf, size_t bufsz, int *oldcount, int *index_hint) { #ifdef USE_NNTP if (option(OPT_NEWS)) { unset_option(OPT_NEWS); nntp_expand_path(buf, bufsz, &CurrentNewsSrv->conn->account); } else #endif mutt_expand_path(buf, bufsz); if (mx_get_magic(buf) <= 0) { mutt_error(_("%s is not a mailbox."), buf); return -1; } /* keepalive failure in mutt_enter_fname may kill connection. #3028 */ if (Context && !Context->path) FREE(&Context); if (Context) { int check; char *new_last_folder = NULL; #ifdef USE_COMPRESSED if (Context->compress_info && Context->realpath) new_last_folder = mutt_str_strdup(Context->realpath); else #endif new_last_folder = mutt_str_strdup(Context->path); *oldcount = Context ? Context->msgcount : 0; check = mx_close_mailbox(Context, index_hint); if (check != 0) { if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED) update_index(menu, Context, check, *oldcount, *index_hint); FREE(&new_last_folder); set_option(OPT_SEARCH_INVALID); menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; return 0; } FREE(&Context); FREE(&LastFolder); LastFolder = new_last_folder; } mutt_str_replace(&CurrentFolder, buf); mutt_sleep(0); /* Note that menu->menu may be MENU_PAGER if the change folder * operation originated from the pager. * * However, exec commands currently use CurrentMenu to determine what * functions are available, which is automatically set by the * mutt_push/pop_current_menu() functions. If that changes, the menu * would need to be reset here, and the pager cleanup code after the * switch statement would need to be run. */ mutt_folder_hook(buf); if ((Context = mx_open_mailbox( buf, (option(OPT_READ_ONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ? MUTT_READONLY : 0, NULL)) != NULL) { menu->current = ci_first_message(); } else menu->current = 0; if (((Sort & SORT_MASK) == SORT_THREADS) && option(OPT_COLLAPSE_ALL)) collapse_all(menu, 0); #ifdef USE_SIDEBAR mutt_sb_set_open_buffy(); #endif mutt_clear_error(); mutt_buffy_check(true); /* force the buffy check after we have changed the folder */ menu->redraw = REDRAW_FULL; set_option(OPT_SEARCH_INVALID); return 0; } /** * mutt_ts_capability - Check terminal capabilities * * terminal status capability check. terminfo must have been initialized. */ bool mutt_ts_capability(void) { char *term = getenv("TERM"); char *tcaps = NULL; #ifdef HAVE_USE_EXTENDED_NAMES int tcapi; #endif char **termp = NULL; char *known[] = { "color-xterm", "cygwin", "eterm", "kterm", "nxterm", "putty", "rxvt", "screen", "xterm", NULL, }; /* If tsl is set, then terminfo says that status lines work. */ tcaps = tigetstr("tsl"); if (tcaps && tcaps != (char *) -1 && *tcaps) { /* update the static defns of tsl/fsl from terminfo */ tsl = tcaps; tcaps = tigetstr("fsl"); if (tcaps && tcaps != (char *) -1 && *tcaps) fsl = tcaps; return true; } /* If XT (boolean) is set, then this terminal supports the standard escape. */ /* Beware: tigetflag returns -1 if XT is invalid or not a boolean. */ #ifdef HAVE_USE_EXTENDED_NAMES use_extended_names(true); tcapi = tigetflag("XT"); if (tcapi == 1) return true; #endif /* HAVE_USE_EXTENDED_NAMES */ /* Check term types that are known to support the standard escape without * necessarily asserting it in terminfo. */ for (termp = known; termp; termp++) { if (term && *termp && (mutt_str_strncasecmp(term, *termp, strlen(*termp)) != 0)) return true; } /* not supported */ return false; } void mutt_ts_status(char *str) { /* If empty, do not set. To clear, use a single space. */ if (str == NULL || *str == '\0') return; fprintf(stderr, "%s%s%s", tsl, str, fsl); } void mutt_ts_icon(char *str) { /* If empty, do not set. To clear, use a single space. */ if (str == NULL || *str == '\0') return; /* icon setting is not supported in terminfo, so hardcode the escape - yuck */ fprintf(stderr, "\033]1;%s\007", str); } /** * index_make_entry - Format a menu item for the index list * @param[out] buf Buffer in which to save string * @param[in] buflen Buffer length * @param[in] menu Menu containing aliases * @param[in] num Index into the menu */ void index_make_entry(char *buf, size_t buflen, struct Menu *menu, int num) { if (!Context || !menu || (num < 0) || (num >= Context->hdrmax)) return; struct Header *h = Context->hdrs[Context->v2r[num]]; if (!h) return; enum FormatFlag flag = MUTT_FORMAT_MAKEPRINT | MUTT_FORMAT_ARROWCURSOR | MUTT_FORMAT_INDEX; int edgemsgno, reverse = Sort & SORT_REVERSE; struct MuttThread *tmp = NULL; if ((Sort & SORT_MASK) == SORT_THREADS && h->tree) { flag |= MUTT_FORMAT_TREE; /* display the thread tree */ if (h->display_subject) flag |= MUTT_FORMAT_FORCESUBJ; else { if (reverse) { if (menu->top + menu->pagelen > menu->max) edgemsgno = Context->v2r[menu->max - 1]; else edgemsgno = Context->v2r[menu->top + menu->pagelen - 1]; } else edgemsgno = Context->v2r[menu->top]; for (tmp = h->thread->parent; tmp; tmp = tmp->parent) { if (!tmp->message) continue; /* if no ancestor is visible on current screen, provisionally force * subject... */ if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno) { flag |= MUTT_FORMAT_FORCESUBJ; break; } else if (tmp->message->virtual >= 0) break; } if (flag & MUTT_FORMAT_FORCESUBJ) { for (tmp = h->thread->prev; tmp; tmp = tmp->prev) { if (!tmp->message) continue; /* ...but if a previous sibling is available, don't force it */ if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno) break; else if (tmp->message->virtual >= 0) { flag &= ~MUTT_FORMAT_FORCESUBJ; break; } } } } } mutt_make_string_flags(buf, buflen, NONULL(IndexFormat), Context, h, flag); } int index_color(int index_no) { if (!Context || (index_no < 0)) return 0; struct Header *h = Context->hdrs[Context->v2r[index_no]]; if (h && h->pair) return h->pair; mutt_set_header_color(Context, h); if (h) return h->pair; return 0; } /** * mutt_draw_statusline - Draw a highlighted status bar * @param cols Maximum number of screen columns * @param buf Message to be displayed * @param buflen Length of the buffer * * Users configure the highlighting of the status bar, e.g. * color status red default "[0-9][0-9]:[0-9][0-9]" * * Where regexes overlap, the one nearest the start will be used. * If two regexes start at the same place, the longer match will be used. */ void mutt_draw_statusline(int cols, const char *buf, int buflen) { size_t i = 0; size_t offset = 0; bool found = false; size_t chunks = 0; size_t len = 0; struct Syntax { int color; int first; int last; } *syntax = NULL; if (!buf) return; do { struct ColorLine *cl = NULL; found = false; if (!buf[offset]) break; /* loop through each "color status regex" */ STAILQ_FOREACH(cl, &ColorStatusList, entries) { regmatch_t pmatch[cl->match + 1]; if (regexec(&cl->regex, buf + offset, cl->match + 1, pmatch, 0) != 0) continue; /* regex doesn't match the status bar */ int first = pmatch[cl->match].rm_so + offset; int last = pmatch[cl->match].rm_eo + offset; if (first == last) continue; /* ignore an empty regex */ if (!found) { chunks++; mutt_mem_realloc(&syntax, chunks * sizeof(struct Syntax)); } i = chunks - 1; if (!found || (first < syntax[i].first) || ((first == syntax[i].first) && (last > syntax[i].last))) { syntax[i].color = cl->pair; syntax[i].first = first; syntax[i].last = last; } found = true; } if (syntax) { offset = syntax[i].last; } } while (found); /* Only 'len' bytes will fit into 'cols' screen columns */ len = mutt_wstr_trunc(buf, buflen, cols, NULL); offset = 0; if ((chunks > 0) && (syntax[0].first > 0)) { /* Text before the first highlight */ addnstr(buf, MIN(len, syntax[0].first)); attrset(ColorDefs[MT_COLOR_STATUS]); if (len <= syntax[0].first) goto dsl_finish; /* no more room */ offset = syntax[0].first; } for (i = 0; i < chunks; i++) { /* Highlighted text */ attrset(syntax[i].color); addnstr(buf + offset, MIN(len, syntax[i].last) - offset); if (len <= syntax[i].last) goto dsl_finish; /* no more room */ size_t next; if ((i + 1) == chunks) { next = len; } else { next = MIN(len, syntax[i + 1].first); } attrset(ColorDefs[MT_COLOR_STATUS]); offset = syntax[i].last; addnstr(buf + offset, next - offset); offset = next; if (offset >= len) goto dsl_finish; /* no more room */ } attrset(ColorDefs[MT_COLOR_STATUS]); if (offset < len) { /* Text after the last highlight */ addnstr(buf + offset, len - offset); } int width = mutt_strwidth(buf); if (width < cols) { /* Pad the rest of the line with whitespace */ mutt_paddstr(cols - width, ""); } dsl_finish: FREE(&syntax); } static const struct Mapping IndexHelp[] = { { N_("Quit"), OP_QUIT }, { N_("Del"), OP_DELETE }, { N_("Undel"), OP_UNDELETE }, { N_("Save"), OP_SAVE }, { N_("Mail"), OP_MAIL }, { N_("Reply"), OP_REPLY }, { N_("Group"), OP_GROUP_REPLY }, { N_("Help"), OP_HELP }, { NULL, 0 }, }; #ifdef USE_NNTP struct Mapping 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, 0 }, }; #endif static void index_menu_redraw(struct Menu *menu) { char buf[LONG_STRING]; if (menu->redraw & REDRAW_FULL) { menu_redraw_full(menu); mutt_show_error(); } #ifdef USE_SIDEBAR if (menu->redraw & REDRAW_SIDEBAR) { mutt_sb_set_buffystats(Context); menu_redraw_sidebar(menu); } #endif if (Context && Context->hdrs && !(menu->current >= Context->vcount)) { menu_check_recenter(menu); if (menu->redraw & REDRAW_INDEX) { menu_redraw_index(menu); menu->redraw |= REDRAW_STATUS; } else if (menu->redraw & (REDRAW_MOTION_RESYNCH | REDRAW_MOTION)) menu_redraw_motion(menu); else if (menu->redraw & REDRAW_CURRENT) menu_redraw_current(menu); } if (menu->redraw & REDRAW_STATUS) { menu_status_line(buf, sizeof(buf), menu, NONULL(StatusFormat)); mutt_window_move(MuttStatusWindow, 0, 0); SETCOLOR(MT_COLOR_STATUS); mutt_draw_statusline(MuttStatusWindow->cols, buf, sizeof(buf)); NORMAL_COLOR; menu->redraw &= ~REDRAW_STATUS; if (option(OPT_TS_ENABLED) && TSSupported) { menu_status_line(buf, sizeof(buf), menu, NONULL(TSStatusFormat)); mutt_ts_status(buf); menu_status_line(buf, sizeof(buf), menu, NONULL(TSIconFormat)); mutt_ts_icon(buf); } } menu->redraw = 0; } /** * mutt_index_menu - Display a list of emails * * 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; bool done = false; /* controls when to exit the "event" loop */ int i = 0, j; bool tag = false; /* has the tag-prefix command been pressed? */ int newcount = -1; int oldcount = -1; int rc = -1; struct Menu *menu = NULL; char *cp = NULL; /* temporary variable. */ int index_hint; /* used to restore cursor position */ bool do_buffy_notify = true; int close = 0; /* did we OP_QUIT or OP_EXIT out of this menu? */ int attach_msg = option(OPT_ATTACH_MSG); menu = mutt_new_menu(MENU_MAIN); 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, #ifdef USE_NNTP (Context && (Context->magic == MUTT_NNTP)) ? IndexNewsHelp : #endif IndexHelp); menu->custom_menu_redraw = index_menu_redraw; mutt_push_current_menu(menu); if (!attach_msg) mutt_buffy_check(true); /* force the buffy check after we enter the folder */ if (((Sort & SORT_MASK) == SORT_THREADS) && option(OPT_COLLAPSE_ALL)) { collapse_all(menu, 0); menu->redraw = REDRAW_FULL; } while (true) { /* Clear the tag prefix unless we just started it. Don't clear * the prefix on a timeout (op==-2), but do clear on an abort (op==-1) */ if (tag && op != OP_TAG_PREFIX && op != OP_TAG_PREFIX_COND && op != -2) tag = false; /* check if we need to resort the index because just about * any 'op' below could do mutt_enter_command(), either here or * from any new menu launched, and change $sort/$sort_aux */ if (option(OPT_NEED_RESORT) && Context && Context->msgcount && menu->current >= 0) resort_index(menu); menu->max = Context ? Context->vcount : 0; oldcount = Context ? Context->msgcount : 0; if (option(OPT_REDRAW_TREE) && Context && Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS) { mutt_draw_tree(Context); menu->redraw |= REDRAW_STATUS; unset_option(OPT_REDRAW_TREE); } if (Context) Context->menu = menu; if (Context && !attach_msg) { int check; /* check for new mail in the mailbox. If nonzero, then something has * changed about the file (either we got new mail or the file was * modified underneath us.) */ index_hint = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ? CURHDR->index : 0; check = mx_check_mailbox(Context, &index_hint); if (check < 0) { if (!Context->path) { /* fatal error occurred */ FREE(&Context); menu->redraw = REDRAW_FULL; } set_option(OPT_SEARCH_INVALID); } else if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED || check == MUTT_FLAGS) { /* notify the user of new mail */ if (check == MUTT_REOPENED) mutt_error( _("Mailbox was externally modified. Flags may be wrong.")); else if (check == MUTT_NEW_MAIL) { for (i = oldcount; i < Context->msgcount; i++) { if (!Context->hdrs[i]->read) { mutt_message(_("New mail in this mailbox.")); if (option(OPT_BEEP_NEW)) beep(); if (NewMailCommand) { char cmd[LONG_STRING]; menu_status_line(cmd, sizeof(cmd), menu, NONULL(NewMailCommand)); if (mutt_system(cmd) != 0) mutt_error(_("Error running \"%s\"!"), cmd); } break; } } } else if (check == MUTT_FLAGS) mutt_message(_("Mailbox was externally modified.")); /* avoid the message being overwritten by buffy */ do_buffy_notify = false; bool q = Context->quiet; Context->quiet = true; update_index(menu, Context, check, oldcount, index_hint); Context->quiet = q; menu->redraw = REDRAW_FULL; menu->max = Context->vcount; set_option(OPT_SEARCH_INVALID); } } if (!attach_msg) { /* check for new mail in the incoming folders */ oldcount = newcount; newcount = mutt_buffy_check(false); if (newcount != oldcount) menu->redraw |= REDRAW_STATUS; if (do_buffy_notify) { if (mutt_buffy_notify()) { menu->redraw |= REDRAW_STATUS; if (option(OPT_BEEP_NEW)) beep(); if (NewMailCommand) { char cmd[LONG_STRING]; menu_status_line(cmd, sizeof(cmd), menu, NONULL(NewMailCommand)); if (mutt_system(cmd) != 0) mutt_error(_("Error running \"%s\"!"), cmd); } } } else do_buffy_notify = true; } if (op >= 0) mutt_curs_set(0); if (menu->menu == MENU_MAIN) { index_menu_redraw(menu); /* give visual indication that the next command is a tag- command */ if (tag) { mutt_window_mvaddstr(MuttMessageWindow, 0, 0, "tag-"); mutt_window_clrtoeol(MuttMessageWindow); } if (menu->current < menu->max) menu->oldcurrent = menu->current; else menu->oldcurrent = -1; if (option(OPT_ARROW_CURSOR)) mutt_window_move(MuttIndexWindow, menu->current - menu->top + menu->offset, 2); else if (option(OPT_BRAILLE_FRIENDLY)) mutt_window_move(MuttIndexWindow, menu->current - menu->top + menu->offset, 0); else mutt_window_move(MuttIndexWindow, menu->current - menu->top + menu->offset, MuttIndexWindow->cols - 1); mutt_refresh(); #if defined(USE_SLANG_CURSES) || defined(HAVE_RESIZETERM) if (SigWinch) { mutt_flushinp(); mutt_resize_screen(); SigWinch = 0; menu->top = 0; /* so we scroll the right amount */ /* * force a real complete redraw. clrtobot() doesn't seem to be able * to handle every case without this. */ clearok(stdscr, true); continue; } #endif op = km_dokey(MENU_MAIN); mutt_debug(4, "[%d]: Got op %d\n", __LINE__, op); /* either user abort or timeout */ if (op < 0) { mutt_timeout_hook(); if (tag) mutt_window_clearline(MuttMessageWindow, 0); continue; } mutt_curs_set(1); /* special handling for the tag-prefix function */ if (op == OP_TAG_PREFIX || op == OP_TAG_PREFIX_COND) { /* A second tag-prefix command aborts */ if (tag) { tag = false; mutt_window_clearline(MuttMessageWindow, 0); continue; } if (!Context) { mutt_error(_("No mailbox is open.")); continue; } if (!Context->tagged) { if (op == OP_TAG_PREFIX) mutt_error(_("No tagged messages.")); else if (op == OP_TAG_PREFIX_COND) { mutt_flush_macro_to_endcond(); mutt_message(_("Nothing to do.")); } continue; } /* get the real command */ tag = true; continue; } else if (option(OPT_AUTO_TAG) && Context && Context->tagged) tag = true; mutt_clear_error(); } else { if (menu->current < menu->max) menu->oldcurrent = menu->current; else menu->oldcurrent = -1; mutt_curs_set(1); /* fallback from the pager */ } #ifdef USE_NNTP unset_option(OPT_NEWS); /* for any case */ #endif #ifdef USE_NOTMUCH if (Context) nm_debug_check(Context); #endif switch (op) { /* ---------------------------------------------------------------------- * movement commands */ case OP_BOTTOM_PAGE: menu_bottom_page(menu); break; case OP_FIRST_ENTRY: menu_first_entry(menu); break; case OP_MIDDLE_PAGE: menu_middle_page(menu); break; case OP_HALF_UP: menu_half_up(menu); break; case OP_HALF_DOWN: menu_half_down(menu); break; case OP_NEXT_LINE: menu_next_line(menu); break; case OP_PREV_LINE: menu_prev_line(menu); break; case OP_NEXT_PAGE: menu_next_page(menu); break; case OP_PREV_PAGE: menu_prev_page(menu); break; case OP_LAST_ENTRY: menu_last_entry(menu); break; case OP_TOP_PAGE: menu_top_page(menu); break; case OP_CURRENT_TOP: menu_current_top(menu); break; case OP_CURRENT_MIDDLE: menu_current_middle(menu); break; case OP_CURRENT_BOTTOM: menu_current_bottom(menu); break; #ifdef USE_NNTP case OP_GET_PARENT: CHECK_MSGCOUNT; CHECK_VISIBLE; /* fallthrough */ case OP_GET_MESSAGE: CHECK_IN_MAILBOX; CHECK_READONLY; CHECK_ATTACH; if (Context->magic == MUTT_NNTP) { struct Header *hdr = NULL; if (op == OP_GET_MESSAGE) { buf[0] = 0; if (mutt_get_field(_("Enter Message-Id: "), buf, sizeof(buf), 0) != 0 || !buf[0]) { break; } } else { if (STAILQ_EMPTY(&CURHDR->env->references)) { mutt_error(_("Article has no parent reference.")); break; } mutt_str_strfcpy(buf, STAILQ_FIRST(&CURHDR->env->references)->data, sizeof(buf)); } if (!Context->id_hash) Context->id_hash = mutt_make_id_hash(Context); hdr = mutt_hash_find(Context->id_hash, buf); if (hdr) { if (hdr->virtual != -1) { menu->current = hdr->virtual; menu->redraw = REDRAW_MOTION_RESYNCH; } else if (hdr->collapsed) { mutt_uncollapse_thread(Context, hdr); mutt_set_virtual(Context); menu->current = hdr->virtual; menu->redraw = REDRAW_MOTION_RESYNCH; } else mutt_error(_("Message is not visible in limited view.")); } else { int rc2; mutt_message(_("Fetching %s from server..."), buf); rc2 = nntp_check_msgid(Context, buf); if (rc2 == 0) { hdr = Context->hdrs[Context->msgcount - 1]; mutt_sort_headers(Context, 0); menu->current = hdr->virtual; menu->redraw = REDRAW_FULL; } else if (rc2 > 0) mutt_error(_("Article %s not found on the server."), buf); } } break; case OP_GET_CHILDREN: case OP_RECONSTRUCT_THREAD: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; CHECK_ATTACH; if (Context->magic == MUTT_NNTP) { int oldmsgcount = Context->msgcount; int oldindex = CURHDR->index; int rc2 = 0; if (!CURHDR->env->message_id) { mutt_error(_("No Message-Id. Unable to perform operation.")); break; } mutt_message(_("Fetching message headers...")); if (!Context->id_hash) Context->id_hash = mutt_make_id_hash(Context); mutt_str_strfcpy(buf, CURHDR->env->message_id, sizeof(buf)); /* trying to find msgid of the root message */ if (op == OP_RECONSTRUCT_THREAD) { struct ListNode *ref; STAILQ_FOREACH(ref, &CURHDR->env->references, entries) { if (mutt_hash_find(Context->id_hash, ref->data) == NULL) { rc2 = nntp_check_msgid(Context, ref->data); if (rc2 < 0) break; } /* the last msgid in References is the root message */ if (!STAILQ_NEXT(ref, entries)) mutt_str_strfcpy(buf, ref->data, sizeof(buf)); } } /* fetching all child messages */ if (rc2 >= 0) rc2 = nntp_check_children(Context, buf); /* at least one message has been loaded */ if (Context->msgcount > oldmsgcount) { struct Header *oldcur = CURHDR; struct Header *hdr = NULL; bool quiet = Context->quiet; if (rc2 < 0) Context->quiet = true; mutt_sort_headers(Context, (op == OP_RECONSTRUCT_THREAD)); Context->quiet = quiet; /* Similar to OP_MAIN_ENTIRE_THREAD, keep displaying the old message, but update the index */ if (menu->menu == MENU_PAGER) { menu->current = oldcur->virtual; menu->redraw = REDRAW_STATUS | REDRAW_INDEX; op = OP_DISPLAY_MESSAGE; continue; } /* if the root message was retrieved, move to it */ hdr = mutt_hash_find(Context->id_hash, buf); if (hdr) menu->current = hdr->virtual; /* try to restore old position */ else { for (int k = 0; k < Context->msgcount; k++) { if (Context->hdrs[k]->index == oldindex) { menu->current = Context->hdrs[k]->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; } else if (rc2 >= 0) { mutt_error(_("No deleted messages found in the thread.")); /* Similar to OP_MAIN_ENTIRE_THREAD, keep displaying the old message, but update the index */ if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } } } break; #endif case OP_JUMP: CHECK_MSGCOUNT; CHECK_VISIBLE; if (isdigit(LastKey)) mutt_unget_event(LastKey, 0); buf[0] = 0; if (mutt_get_field(_("Jump to message: "), buf, sizeof(buf), 0) != 0 || !buf[0]) { if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } break; } if (mutt_str_atoi(buf, &i) < 0) { mutt_error(_("Argument must be a message number.")); break; } if (i > 0 && i <= Context->msgcount) { for (j = i - 1; j < Context->msgcount; j++) { if (Context->hdrs[j]->virtual != -1) break; } if (j >= Context->msgcount) { for (j = i - 2; j >= 0; j--) { if (Context->hdrs[j]->virtual != -1) break; } } if (j >= 0) { menu->current = Context->hdrs[j]->virtual; if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_MOTION; } else mutt_error(_("That message is not visible.")); } else mutt_error(_("Invalid message number.")); break; /* -------------------------------------------------------------------- * `index' specific commands */ case OP_MAIN_DELETE_PATTERN: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message(s)")); CHECK_ATTACH; mutt_pattern_func(MUTT_DELETE, _("Delete messages matching: ")); menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; break; #ifdef USE_POP case OP_MAIN_FETCH_MAIL: CHECK_ATTACH; pop_fetch_mail(); menu->redraw = REDRAW_FULL; break; #endif /* USE_POP */ case OP_HELP: mutt_help(MENU_MAIN); menu->redraw = REDRAW_FULL; break; case OP_MAIN_SHOW_LIMIT: CHECK_IN_MAILBOX; if (!Context->pattern) mutt_message(_("No limit pattern is in effect.")); else { char buf2[STRING]; /* L10N: ask for a limit to apply */ snprintf(buf2, sizeof(buf2), _("Limit: %s"), Context->pattern); mutt_message("%s", buf2); } break; case OP_LIMIT_CURRENT_THREAD: 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 (op == OP_TOGGLE_READ) { char buf2[LONG_STRING]; if (!Context->pattern || (strncmp(Context->pattern, "!~R!~D~s", 8) != 0)) { snprintf(buf2, sizeof(buf2), "!~R!~D~s%s", Context->pattern ? Context->pattern : ".*"); set_option(OPT_HIDE_READ); } else { mutt_str_strfcpy(buf2, Context->pattern + 8, sizeof(buf2)); if (!*buf2 || (strncmp(buf2, ".*", 2) == 0)) snprintf(buf2, sizeof(buf2), "~A"); unset_option(OPT_HIDE_READ); } FREE(&Context->pattern); Context->pattern = mutt_str_strdup(buf2); } if (((op == OP_LIMIT_CURRENT_THREAD) && mutt_limit_current_thread(CURHDR)) || ((op == OP_MAIN_LIMIT) && (mutt_pattern_func(MUTT_LIMIT, _("Limit to messages matching: ")) == 0))) { if (menu->oldcurrent >= 0) { /* try to find what used to be the current message */ menu->current = -1; for (i = 0; i < Context->vcount; i++) { if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent) { menu->current = i; break; } } if (menu->current < 0) menu->current = 0; } else menu->current = 0; if (Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS) mutt_draw_tree(Context); menu->redraw = REDRAW_FULL; } if (Context->pattern) mutt_message(_("To view all messages, limit to \"all\".")); break; case OP_QUIT: close = op; if (attach_msg) { done = true; break; } if (query_quadoption(OPT_QUIT, _("Quit NeoMutt?")) == MUTT_YES) { int check; oldcount = Context ? Context->msgcount : 0; mutt_startup_shutdown_hook(MUTT_SHUTDOWNHOOK); if (!Context || (check = mx_close_mailbox(Context, &index_hint)) == 0) done = true; else { if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED) update_index(menu, Context, check, oldcount, index_hint); menu->redraw = REDRAW_FULL; /* new mail arrived? */ set_option(OPT_SEARCH_INVALID); } } break; case OP_REDRAW: clearok(stdscr, true); menu->redraw = REDRAW_FULL; break; case OP_SEARCH: case OP_SEARCH_REVERSE: case OP_SEARCH_NEXT: case OP_SEARCH_OPPOSITE: CHECK_MSGCOUNT; CHECK_VISIBLE; menu->current = mutt_search_command(menu->current, op); if (menu->current == -1) menu->current = menu->oldcurrent; else menu->redraw = REDRAW_MOTION; break; case OP_SORT: case OP_SORT_REVERSE: if (mutt_select_sort((op == OP_SORT_REVERSE)) == 0) { if (Context && Context->msgcount) { resort_index(menu); set_option(OPT_SEARCH_INVALID); } if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } menu->redraw |= REDRAW_STATUS; } break; case OP_TAG: CHECK_MSGCOUNT; CHECK_VISIBLE; if (tag && !option(OPT_AUTO_TAG)) { for (j = 0; j < Context->msgcount; j++) if (message_is_visible(Context, j)) mutt_set_flag(Context, Context->hdrs[j], MUTT_TAG, 0); menu->redraw |= REDRAW_STATUS | REDRAW_INDEX; } else { mutt_set_flag(Context, CURHDR, MUTT_TAG, !CURHDR->tagged); Context->last_tag = CURHDR->tagged ? CURHDR : ((Context->last_tag == CURHDR && !CURHDR->tagged) ? NULL : Context->last_tag); menu->redraw |= REDRAW_STATUS; if (option(OPT_RESOLVE) && menu->current < Context->vcount - 1) { menu->current++; menu->redraw |= REDRAW_MOTION_RESYNCH; } else menu->redraw |= REDRAW_CURRENT; } break; case OP_MAIN_TAG_PATTERN: CHECK_MSGCOUNT; CHECK_VISIBLE; mutt_pattern_func(MUTT_TAG, _("Tag messages matching: ")); menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; break; case OP_MAIN_UNDELETE_PATTERN: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message(s)")); if (mutt_pattern_func(MUTT_UNDELETE, _("Undelete messages matching: ")) == 0) menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; break; case OP_MAIN_UNTAG_PATTERN: CHECK_MSGCOUNT; CHECK_VISIBLE; if (mutt_pattern_func(MUTT_UNTAG, _("Untag messages matching: ")) == 0) menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; break; case OP_COMPOSE_TO_SENDER: if (Context) { mutt_compose_to_sender(tag ? NULL : CURHDR); menu->redraw = REDRAW_FULL; } break; /* -------------------------------------------------------------------- * The following operations can be performed inside of the pager. */ #ifdef USE_IMAP case OP_MAIN_IMAP_FETCH: if (Context && Context->magic == MUTT_IMAP) imap_check_mailbox(Context, 1); break; case OP_MAIN_IMAP_LOGOUT_ALL: if (Context && Context->magic == MUTT_IMAP) { if (mx_close_mailbox(Context, &index_hint) != 0) { set_option(OPT_SEARCH_INVALID); menu->redraw = REDRAW_FULL; break; } FREE(&Context); } imap_logout_all(); mutt_message(_("Logged out of IMAP servers.")); set_option(OPT_SEARCH_INVALID); menu->redraw = REDRAW_FULL; break; #endif case OP_MAIN_SYNC_FOLDER: if (Context && !Context->msgcount) break; CHECK_MSGCOUNT; CHECK_READONLY; { int ovc = Context->vcount; int oc = Context->msgcount; int check, newidx; struct Header *newhdr = NULL; /* don't attempt to move the cursor if there are no visible messages in the current limit */ if (menu->current < Context->vcount) { /* threads may be reordered, so figure out what header the cursor * should be on. #3092 */ newidx = menu->current; if (CURHDR->deleted) newidx = ci_next_undeleted(menu->current); if (newidx < 0) newidx = ci_previous_undeleted(menu->current); if (newidx >= 0) newhdr = Context->hdrs[Context->v2r[newidx]]; } check = mx_sync_mailbox(Context, &index_hint); if (check == 0) { if (newhdr && Context->vcount != ovc) for (j = 0; j < Context->vcount; j++) { if (Context->hdrs[Context->v2r[j]] == newhdr) { menu->current = j; break; } } set_option(OPT_SEARCH_INVALID); } else if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED) update_index(menu, Context, check, oc, index_hint); /* * do a sanity check even if mx_sync_mailbox failed. */ if (menu->current < 0 || menu->current >= Context->vcount) menu->current = ci_first_message(); } /* check for a fatal error, or all messages deleted */ if (!Context->path) FREE(&Context); /* if we were in the pager, redisplay the message */ if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_FULL; break; case OP_MAIN_QUASI_DELETE: CHECK_MSGCOUNT; CHECK_VISIBLE; if (tag) { for (j = 0; j < Context->msgcount; j++) { if (message_is_tagged(Context, j)) { Context->hdrs[j]->quasi_deleted = true; Context->changed = true; } } } else { CURHDR->quasi_deleted = true; Context->changed = true; } break; #ifdef USE_NOTMUCH case OP_MAIN_ENTIRE_THREAD: { if (!Context || (Context->magic != MUTT_NOTMUCH)) { mutt_message(_("No virtual folder, aborting.")); break; } CHECK_MSGCOUNT; CHECK_VISIBLE; int oc = Context->msgcount; if (nm_read_entire_thread(Context, CURHDR) < 0) { mutt_message(_("Failed to read thread, aborting.")); break; } if (oc < Context->msgcount) { struct Header *oldcur = CURHDR; if ((Sort & SORT_MASK) == SORT_THREADS) mutt_sort_headers(Context, 0); menu->current = oldcur->virtual; menu->redraw = REDRAW_STATUS | REDRAW_INDEX; if (oldcur->collapsed || Context->collapsed) { menu->current = mutt_uncollapse_thread(Context, CURHDR); mutt_set_virtual(Context); } } if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } break; } #endif case OP_MAIN_MODIFY_TAGS: case OP_MAIN_MODIFY_TAGS_THEN_HIDE: { if (!Context || !mx_tags_is_supported(Context)) { mutt_message(_("Folder doesn't support tagging, aborting.")); break; } CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; char *tags = NULL; if (!tag) tags = driver_tags_get_with_hidden(&CURHDR->tags); rc = mx_tags_editor(Context, tags, buf, sizeof(buf)); FREE(&tags); if (rc < 0) break; else if (rc == 0) { mutt_message(_("No tag specified, aborting.")); break; } if (tag) { char msgbuf[STRING]; struct Progress progress; int px; if (!Context->quiet) { snprintf(msgbuf, sizeof(msgbuf), _("Update tags...")); mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, 1, Context->tagged); } #ifdef USE_NOTMUCH if (Context->magic == MUTT_NOTMUCH) nm_longrun_init(Context, true); #endif for (px = 0, j = 0; j < Context->msgcount; j++) { if (!message_is_tagged(Context, j)) continue; if (!Context->quiet) mutt_progress_update(&progress, ++px, -1); mx_tags_commit(Context, Context->hdrs[j], buf); if (op == OP_MAIN_MODIFY_TAGS_THEN_HIDE) { bool still_queried = false; #ifdef USE_NOTMUCH if (Context->magic == MUTT_NOTMUCH) still_queried = nm_message_is_still_queried(Context, Context->hdrs[j]); #endif Context->hdrs[j]->quasi_deleted = !still_queried; Context->changed = true; } } #ifdef USE_NOTMUCH if (Context->magic == MUTT_NOTMUCH) nm_longrun_done(Context); #endif menu->redraw = REDRAW_STATUS | REDRAW_INDEX; } else { if (mx_tags_commit(Context, CURHDR, buf)) { mutt_message(_("Failed to modify tags, aborting.")); break; } if (op == OP_MAIN_MODIFY_TAGS_THEN_HIDE) { bool still_queried = false; #ifdef USE_NOTMUCH if (Context->magic == MUTT_NOTMUCH) still_queried = nm_message_is_still_queried(Context, CURHDR); #endif CURHDR->quasi_deleted = !still_queried; Context->changed = true; } if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } if (option(OPT_RESOLVE)) { menu->current = ci_next_undeleted(menu->current); if (menu->current == -1) { menu->current = menu->oldcurrent; menu->redraw = REDRAW_CURRENT; } else menu->redraw = REDRAW_MOTION_RESYNCH; } else menu->redraw = REDRAW_CURRENT; } menu->redraw |= REDRAW_STATUS; break; } #ifdef USE_NOTMUCH case OP_MAIN_VFOLDER_FROM_QUERY: buf[0] = '\0'; if (mutt_get_field("Query: ", buf, sizeof(buf), MUTT_NM_QUERY) != 0 || !buf[0]) { mutt_message(_("No query, aborting.")); break; } if (!nm_uri_from_query(Context, buf, sizeof(buf))) mutt_message(_("Failed to create query, aborting.")); else main_change_folder(menu, op, buf, sizeof(buf), &oldcount, &index_hint); break; case OP_MAIN_WINDOWED_VFOLDER_BACKWARD: mutt_debug(2, "OP_MAIN_WINDOWED_VFOLDER_BACKWARD\n"); if (NmQueryWindowDuration <= 0) { mutt_message(_("Windowed queries disabled.")); break; } if (!NmQueryWindowCurrentSearch) { mutt_message(_("No notmuch vfolder currently loaded.")); break; } nm_query_window_backward(); strncpy(buf, NmQueryWindowCurrentSearch, sizeof(buf)); if (!nm_uri_from_query(Context, buf, sizeof(buf))) mutt_message(_("Failed to create query, aborting.")); else main_change_folder(menu, op, buf, sizeof(buf), &oldcount, &index_hint); break; case OP_MAIN_WINDOWED_VFOLDER_FORWARD: if (NmQueryWindowDuration <= 0) { mutt_message(_("Windowed queries disabled.")); break; } if (!NmQueryWindowCurrentSearch) { mutt_message(_("No notmuch vfolder currently loaded.")); break; } nm_query_window_forward(); strncpy(buf, NmQueryWindowCurrentSearch, sizeof(buf)); if (!nm_uri_from_query(Context, buf, sizeof(buf))) mutt_message(_("Failed to create query, aborting.")); else { mutt_debug(2, "nm: + windowed query (%s)\n", buf); main_change_folder(menu, op, buf, sizeof(buf), &oldcount, &index_hint); } break; case OP_MAIN_CHANGE_VFOLDER: #endif #ifdef USE_SIDEBAR case OP_SIDEBAR_OPEN: #endif case OP_MAIN_CHANGE_FOLDER: case OP_MAIN_NEXT_UNREAD_MAILBOX: case OP_MAIN_CHANGE_FOLDER_READONLY: #ifdef USE_NNTP case OP_MAIN_CHANGE_GROUP: case OP_MAIN_CHANGE_GROUP_READONLY: unset_option(OPT_NEWS); #endif if (attach_msg || option(OPT_READ_ONLY) || #ifdef USE_NNTP op == OP_MAIN_CHANGE_GROUP_READONLY || #endif op == OP_MAIN_CHANGE_FOLDER_READONLY) flags = MUTT_READONLY; else flags = 0; if (flags) cp = _("Open mailbox in read-only mode"); #ifdef USE_NOTMUCH else if (op == OP_MAIN_CHANGE_VFOLDER) cp = _("Open virtual folder"); #endif else cp = _("Open mailbox"); buf[0] = '\0'; if ((op == OP_MAIN_NEXT_UNREAD_MAILBOX) && Context && Context->path) { mutt_str_strfcpy(buf, Context->path, sizeof(buf)); mutt_pretty_mailbox(buf, sizeof(buf)); mutt_buffy(buf, sizeof(buf)); if (!buf[0]) { mutt_error(_("No mailboxes have new mail")); break; } } #ifdef USE_SIDEBAR else if (op == OP_SIDEBAR_OPEN) { const char *path = mutt_sb_get_highlight(); if (!path || !*path) break; strncpy(buf, path, sizeof(buf)); /* Mark the selected dir for the neomutt browser */ mutt_browser_select_dir(buf); } #endif #ifdef USE_NOTMUCH else if (op == OP_MAIN_CHANGE_VFOLDER) { if (Context && (Context->magic == MUTT_NOTMUCH)) { mutt_str_strfcpy(buf, Context->path, sizeof(buf)); mutt_buffy_vfolder(buf, sizeof(buf)); } mutt_enter_vfolder(cp, buf, sizeof(buf), 1); if (!buf[0]) { mutt_window_clearline(MuttMessageWindow, 0); break; } } #endif else { if (option(OPT_CHANGE_FOLDER_NEXT) && Context && Context->path) { mutt_str_strfcpy(buf, Context->path, sizeof(buf)); mutt_pretty_mailbox(buf, sizeof(buf)); } #ifdef USE_NNTP if (op == OP_MAIN_CHANGE_GROUP || op == OP_MAIN_CHANGE_GROUP_READONLY) { set_option(OPT_NEWS); CurrentNewsSrv = nntp_select_server(NewsServer, false); if (!CurrentNewsSrv) break; if (flags) cp = _("Open newsgroup in read-only mode"); else cp = _("Open newsgroup"); nntp_buffy(buf, sizeof(buf)); } else #endif /* By default, fill buf with the next mailbox that contains unread * mail */ mutt_buffy(buf, sizeof(buf)); if (mutt_enter_fname(cp, buf, sizeof(buf), 1) == -1) { if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else break; } /* Selected directory is okay, let's save it. */ mutt_browser_select_dir(buf); if (!buf[0]) { mutt_window_clearline(MuttMessageWindow, 0); break; } } main_change_folder(menu, op, buf, sizeof(buf), &oldcount, &index_hint); #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 == MUTT_NNTP)) ? IndexNewsHelp : IndexHelp); #endif mutt_expand_path(buf, sizeof(buf)); #ifdef USE_SIDEBAR mutt_sb_set_open_buffy(); #endif break; case OP_DISPLAY_MESSAGE: case OP_DISPLAY_HEADERS: /* don't weed the headers */ CHECK_MSGCOUNT; CHECK_VISIBLE; /* * toggle the weeding of headers so that a user can press the key * again while reading the message. */ if (op == OP_DISPLAY_HEADERS) toggle_option(OPT_WEED); unset_option(OPT_NEED_RESORT); if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed) { mutt_uncollapse_thread(Context, CURHDR); mutt_set_virtual(Context); if (option(OPT_UNCOLLAPSE_JUMP)) menu->current = mutt_thread_next_unread(Context, CURHDR); } if (option(OPT_PGP_AUTO_DECODE) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) { mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw); } int hint = Context->hdrs[Context->v2r[menu->current]]->index; /* If we are returning to the pager via an index menu redirection, we * need to reset the menu->menu. Otherwise mutt_pop_current_menu() will * set CurrentMenu incorrectly when we return back to the index menu. */ menu->menu = MENU_MAIN; op = mutt_display_message(CURHDR); if (op < 0) { unset_option(OPT_NEED_RESORT); break; } /* This is used to redirect a single operation back here afterwards. If * mutt_display_message() returns 0, then the menu and pager state will * be cleaned up after this switch statement. */ menu->menu = MENU_PAGER; menu->oldcurrent = menu->current; if (Context) update_index(menu, Context, MUTT_NEW_MAIL, Context->msgcount, hint); continue; case OP_EXIT: close = op; if (menu->menu == MENU_MAIN && attach_msg) { done = true; break; } if ((menu->menu == MENU_MAIN) && (query_quadoption(OPT_QUIT, _("Exit NeoMutt without saving?")) == MUTT_YES)) { if (Context) { mx_fastclose_mailbox(Context); FREE(&Context); } done = true; } break; case OP_MAIN_BREAK_THREAD: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; CHECK_ACL(MUTT_ACL_WRITE, _("Cannot break thread")); if ((Sort & SORT_MASK) != SORT_THREADS) mutt_error(_("Threading is not enabled.")); else if (!STAILQ_EMPTY(&CURHDR->env->in_reply_to) || !STAILQ_EMPTY(&CURHDR->env->references)) { { struct Header *oldcur = CURHDR; mutt_break_thread(CURHDR); mutt_sort_headers(Context, 1); menu->current = oldcur->virtual; } Context->changed = true; mutt_message(_("Thread broken")); if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw |= REDRAW_INDEX; } else mutt_error( _("Thread cannot be broken, message is not part of a thread")); break; case OP_MAIN_LINK_THREADS: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_WRITE, _("Cannot link threads")); if ((Sort & SORT_MASK) != SORT_THREADS) mutt_error(_("Threading is not enabled.")); else if (!CURHDR->env->message_id) mutt_error(_("No Message-ID: header available to link thread")); else if (!tag && (!Context->last_tag || !Context->last_tag->tagged)) mutt_error(_("First, please tag a message to be linked here")); else { struct Header *oldcur = CURHDR; if (mutt_link_threads(CURHDR, tag ? NULL : Context->last_tag, Context)) { mutt_sort_headers(Context, 1); menu->current = oldcur->virtual; Context->changed = true; mutt_message(_("Threads linked")); } else mutt_error(_("No thread linked")); } if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw |= REDRAW_STATUS | REDRAW_INDEX; break; case OP_EDIT_TYPE: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_ATTACH; mutt_edit_content_type(CURHDR, CURHDR->content, NULL); /* if we were in the pager, redisplay the message */ if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_CURRENT; break; case OP_MAIN_NEXT_UNDELETED: CHECK_MSGCOUNT; CHECK_VISIBLE; if (menu->current >= Context->vcount - 1) { if (menu->menu == MENU_MAIN) mutt_error(_("You are on the last message.")); break; } menu->current = ci_next_undeleted(menu->current); if (menu->current == -1) { menu->current = menu->oldcurrent; if (menu->menu == MENU_MAIN) mutt_error(_("No undeleted messages.")); } else if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_MOTION; break; case OP_NEXT_ENTRY: CHECK_MSGCOUNT; CHECK_VISIBLE; if (menu->current >= Context->vcount - 1) { if (menu->menu == MENU_MAIN) mutt_error(_("You are on the last message.")); break; } menu->current++; if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_MOTION; break; case OP_MAIN_PREV_UNDELETED: CHECK_MSGCOUNT; CHECK_VISIBLE; if (menu->current < 1) { mutt_error(_("You are on the first message.")); break; } menu->current = ci_previous_undeleted(menu->current); if (menu->current == -1) { menu->current = menu->oldcurrent; if (menu->menu == MENU_MAIN) mutt_error(_("No undeleted messages.")); } else if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_MOTION; break; case OP_PREV_ENTRY: CHECK_MSGCOUNT; CHECK_VISIBLE; if (menu->current < 1) { if (menu->menu == MENU_MAIN) mutt_error(_("You are on the first message.")); break; } menu->current--; if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_MOTION; break; case OP_DECRYPT_COPY: case OP_DECRYPT_SAVE: if (!WithCrypto) break; /* fall thru */ case OP_COPY_MESSAGE: case OP_SAVE: case OP_DECODE_COPY: case OP_DECODE_SAVE: CHECK_MSGCOUNT; CHECK_VISIBLE; if (mutt_save_message(tag ? NULL : CURHDR, (op == OP_DECRYPT_SAVE) || (op == OP_SAVE) || (op == OP_DECODE_SAVE), (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY), (op == OP_DECRYPT_SAVE) || (op == OP_DECRYPT_COPY) || 0) == 0 && (op == OP_SAVE || op == OP_DECODE_SAVE || op == OP_DECRYPT_SAVE)) { menu->redraw |= REDRAW_STATUS; if (tag) menu->redraw |= REDRAW_INDEX; else if (option(OPT_RESOLVE)) { menu->current = ci_next_undeleted(menu->current); if (menu->current == -1) { menu->current = menu->oldcurrent; menu->redraw |= REDRAW_CURRENT; } else menu->redraw |= REDRAW_MOTION_RESYNCH; } else menu->redraw |= REDRAW_CURRENT; } break; case OP_MAIN_NEXT_NEW: case OP_MAIN_NEXT_UNREAD: case OP_MAIN_PREV_NEW: case OP_MAIN_PREV_UNREAD: case OP_MAIN_NEXT_NEW_THEN_UNREAD: case OP_MAIN_PREV_NEW_THEN_UNREAD: { int first_unread = -1; int first_new = -1; CHECK_MSGCOUNT; CHECK_VISIBLE; i = menu->current; menu->current = -1; for (j = 0; j != Context->vcount; j++) { if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_NEXT_NEW_THEN_UNREAD) { i++; if (i > Context->vcount - 1) { mutt_message(_("Search wrapped to top.")); i = 0; } } else { i--; if (i < 0) { mutt_message(_("Search wrapped to bottom.")); i = Context->vcount - 1; } } struct Header *h = Context->hdrs[Context->v2r[i]]; if (h->collapsed && (Sort & SORT_MASK) == SORT_THREADS) { if (UNREAD(h) && first_unread == -1) first_unread = i; if (UNREAD(h) == 1 && first_new == -1) first_new = i; } else if ((!h->deleted && !h->read)) { if (first_unread == -1) first_unread = i; if ((!h->old) && first_new == -1) first_new = i; } if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) && first_unread != -1) break; if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW || op == OP_MAIN_NEXT_NEW_THEN_UNREAD || op == OP_MAIN_PREV_NEW_THEN_UNREAD) && first_new != -1) break; } if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW || op == OP_MAIN_NEXT_NEW_THEN_UNREAD || op == OP_MAIN_PREV_NEW_THEN_UNREAD) && first_new != -1) menu->current = first_new; else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD || op == OP_MAIN_NEXT_NEW_THEN_UNREAD || op == OP_MAIN_PREV_NEW_THEN_UNREAD) && first_unread != -1) menu->current = first_unread; if (menu->current == -1) { menu->current = menu->oldcurrent; if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW) { if (Context->pattern) mutt_error(_("No new messages in this limited view.")); else mutt_error(_("No new messages.")); } else { if (Context->pattern) mutt_error(_("No unread messages in this limited view.")); else mutt_error(_("No unread messages.")); } } else if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_MOTION; break; } case OP_FLAG_MESSAGE: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_WRITE, _("Cannot flag message")); if (tag) { for (j = 0; j < Context->msgcount; j++) { if (message_is_tagged(Context, j)) mutt_set_flag(Context, Context->hdrs[j], MUTT_FLAG, !Context->hdrs[j]->flagged); } menu->redraw |= REDRAW_INDEX; } else { mutt_set_flag(Context, CURHDR, MUTT_FLAG, !CURHDR->flagged); if (option(OPT_RESOLVE)) { menu->current = ci_next_undeleted(menu->current); if (menu->current == -1) { menu->current = menu->oldcurrent; menu->redraw |= REDRAW_CURRENT; } else menu->redraw |= REDRAW_MOTION_RESYNCH; } else menu->redraw |= REDRAW_CURRENT; } menu->redraw |= REDRAW_STATUS; break; case OP_TOGGLE_NEW: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_SEEN, _("Cannot toggle new")); if (tag) { for (j = 0; j < Context->msgcount; j++) { if (!message_is_tagged(Context, j)) continue; if (Context->hdrs[j]->read || Context->hdrs[j]->old) mutt_set_flag(Context, Context->hdrs[j], MUTT_NEW, 1); else mutt_set_flag(Context, Context->hdrs[j], MUTT_READ, 1); } menu->redraw |= REDRAW_STATUS | REDRAW_INDEX; } else { if (CURHDR->read || CURHDR->old) mutt_set_flag(Context, CURHDR, MUTT_NEW, 1); else mutt_set_flag(Context, CURHDR, MUTT_READ, 1); if (option(OPT_RESOLVE)) { menu->current = ci_next_undeleted(menu->current); if (menu->current == -1) { menu->current = menu->oldcurrent; menu->redraw |= REDRAW_CURRENT; } else menu->redraw |= REDRAW_MOTION_RESYNCH; } else menu->redraw |= REDRAW_CURRENT; menu->redraw |= REDRAW_STATUS; } break; case OP_TOGGLE_WRITE: CHECK_IN_MAILBOX; if (mx_toggle_write(Context) == 0) menu->redraw |= REDRAW_STATUS; break; case OP_MAIN_NEXT_THREAD: case OP_MAIN_NEXT_SUBTHREAD: case OP_MAIN_PREV_THREAD: case OP_MAIN_PREV_SUBTHREAD: CHECK_MSGCOUNT; CHECK_VISIBLE; switch (op) { case OP_MAIN_NEXT_THREAD: menu->current = mutt_next_thread(CURHDR); break; case OP_MAIN_NEXT_SUBTHREAD: menu->current = mutt_next_subthread(CURHDR); break; case OP_MAIN_PREV_THREAD: menu->current = mutt_previous_thread(CURHDR); break; case OP_MAIN_PREV_SUBTHREAD: menu->current = mutt_previous_subthread(CURHDR); break; } if (menu->current < 0) { menu->current = menu->oldcurrent; if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD) mutt_error(_("No more threads.")); else mutt_error(_("You are on the first thread.")); } else if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_MOTION; break; case OP_MAIN_ROOT_MESSAGE: case OP_MAIN_PARENT_MESSAGE: CHECK_MSGCOUNT; CHECK_VISIBLE; menu->current = mutt_parent_message(Context, CURHDR, op == OP_MAIN_ROOT_MESSAGE); if (menu->current < 0) { menu->current = menu->oldcurrent; } else if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw = REDRAW_MOTION; break; case OP_MAIN_SET_FLAG: case OP_MAIN_CLEAR_FLAG: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* CHECK_ACL(MUTT_ACL_WRITE); */ if (mutt_change_flag(tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) == 0) { menu->redraw |= REDRAW_STATUS; if (tag) menu->redraw |= REDRAW_INDEX; else if (option(OPT_RESOLVE)) { menu->current = ci_next_undeleted(menu->current); if (menu->current == -1) { menu->current = menu->oldcurrent; menu->redraw |= REDRAW_CURRENT; } else menu->redraw |= REDRAW_MOTION_RESYNCH; } else menu->redraw |= REDRAW_CURRENT; } break; case OP_MAIN_COLLAPSE_THREAD: CHECK_MSGCOUNT; CHECK_VISIBLE; if ((Sort & SORT_MASK) != SORT_THREADS) { mutt_error(_("Threading is not enabled.")); break; } if (CURHDR->collapsed) { menu->current = mutt_uncollapse_thread(Context, CURHDR); mutt_set_virtual(Context); if (option(OPT_UNCOLLAPSE_JUMP)) menu->current = mutt_thread_next_unread(Context, CURHDR); } else if (CAN_COLLAPSE(CURHDR)) { menu->current = mutt_collapse_thread(Context, CURHDR); mutt_set_virtual(Context); } else { mutt_error(_("Thread contains unread or flagged messages.")); break; } menu->redraw = REDRAW_INDEX | REDRAW_STATUS; break; case OP_MAIN_COLLAPSE_ALL: CHECK_MSGCOUNT; CHECK_VISIBLE; if ((Sort & SORT_MASK) != SORT_THREADS) { mutt_error(_("Threading is not enabled.")); break; } collapse_all(menu, 1); break; /* -------------------------------------------------------------------- * These functions are invoked directly from the internal-pager */ case OP_BOUNCE_MESSAGE: CHECK_ATTACH; CHECK_MSGCOUNT; CHECK_VISIBLE; ci_bounce_message(tag ? NULL : CURHDR); break; case OP_CREATE_ALIAS: mutt_create_alias(Context && Context->vcount ? CURHDR->env : NULL, NULL); menu->redraw |= REDRAW_CURRENT; break; case OP_QUERY: CHECK_ATTACH; mutt_query_menu(NULL, 0); break; case OP_PURGE_MESSAGE: case OP_DELETE: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message")); if (tag) { mutt_tag_set_flag(MUTT_DELETE, 1); mutt_tag_set_flag(MUTT_PURGE, (op == OP_PURGE_MESSAGE)); if (option(OPT_DELETE_UNTAG)) mutt_tag_set_flag(MUTT_TAG, 0); menu->redraw |= REDRAW_INDEX; } else { mutt_set_flag(Context, CURHDR, MUTT_DELETE, 1); mutt_set_flag(Context, CURHDR, MUTT_PURGE, (op == OP_PURGE_MESSAGE)); if (option(OPT_DELETE_UNTAG)) mutt_set_flag(Context, CURHDR, MUTT_TAG, 0); if (option(OPT_RESOLVE)) { menu->current = ci_next_undeleted(menu->current); if (menu->current == -1) { menu->current = menu->oldcurrent; menu->redraw |= REDRAW_CURRENT; } else if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } else menu->redraw |= REDRAW_MOTION_RESYNCH; } else menu->redraw |= REDRAW_CURRENT; } menu->redraw |= REDRAW_STATUS; break; case OP_DELETE_THREAD: case OP_DELETE_SUBTHREAD: case OP_PURGE_THREAD: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message(s)")); { int subthread = (op == OP_DELETE_SUBTHREAD); rc = mutt_thread_set_flag(CURHDR, MUTT_DELETE, 1, subthread); if (rc == -1) break; if (op == OP_PURGE_THREAD) { rc = mutt_thread_set_flag(CURHDR, MUTT_PURGE, 1, subthread); if (rc == -1) break; } if (option(OPT_DELETE_UNTAG)) mutt_thread_set_flag(CURHDR, MUTT_TAG, 0, subthread); if (option(OPT_RESOLVE)) { menu->current = ci_next_undeleted(menu->current); if (menu->current == -1) menu->current = menu->oldcurrent; } menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } break; #ifdef USE_NNTP case OP_CATCHUP: CHECK_MSGCOUNT; CHECK_READONLY; CHECK_ATTACH if (Context && Context->magic == MUTT_NNTP) { struct NntpData *nntp_data = Context->data; if (mutt_newsgroup_catchup(nntp_data->nserv, nntp_data->group)) menu->redraw = REDRAW_INDEX | REDRAW_STATUS; } break; #endif case OP_DISPLAY_ADDRESS: CHECK_MSGCOUNT; CHECK_VISIBLE; mutt_display_address(CURHDR->env); break; case OP_ENTER_COMMAND: mutt_enter_command(); mutt_check_rescore(Context); break; case OP_EDIT_OR_VIEW_RAW_MESSAGE: /* fall through */ case OP_EDIT_RAW_MESSAGE: /* fall through */ case OP_VIEW_RAW_MESSAGE: /* TODO split this into 3 cases? */ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_ATTACH; bool edit; if (op == OP_EDIT_RAW_MESSAGE) { CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_INSERT, _("Cannot edit message")); edit = true; } else if (op == OP_EDIT_OR_VIEW_RAW_MESSAGE) edit = !Context->readonly && mutt_bit_isset(Context->rights, MUTT_ACL_INSERT); else edit = false; if (option(OPT_PGP_AUTO_DECODE) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) { mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw); } if (edit) mutt_edit_message(Context, tag ? NULL : CURHDR); else mutt_view_message(Context, tag ? NULL : CURHDR); menu->redraw = REDRAW_FULL; break; case OP_FORWARD_MESSAGE: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_ATTACH; if (option(OPT_PGP_AUTO_DECODE) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) { mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw); } ci_send_message(SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR); menu->redraw = REDRAW_FULL; break; case OP_FORGET_PASSPHRASE: crypt_forget_passphrase(); break; case OP_GROUP_REPLY: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_ATTACH; if (option(OPT_PGP_AUTO_DECODE) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) { mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw); } ci_send_message(SENDREPLY | SENDGROUPREPLY, NULL, NULL, Context, tag ? NULL : CURHDR); menu->redraw = REDRAW_FULL; break; case OP_EDIT_LABEL: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; rc = mutt_label_message(tag ? NULL : CURHDR); if (rc > 0) { Context->changed = true; menu->redraw = REDRAW_FULL; /* L10N: This is displayed when the x-label on one or more * messages is edited. */ mutt_message(_("%d labels changed."), rc); } else { /* L10N: This is displayed when editing an x-label, but no messages * were updated. Possibly due to canceling at the prompt or if the new * label is the same as the old label. */ mutt_message(_("No labels changed.")); } break; case OP_LIST_REPLY: CHECK_ATTACH; CHECK_MSGCOUNT; CHECK_VISIBLE; if (option(OPT_PGP_AUTO_DECODE) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) { mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw); } ci_send_message(SENDREPLY | SENDLISTREPLY, NULL, NULL, Context, tag ? NULL : CURHDR); menu->redraw = REDRAW_FULL; break; case OP_MAIL: CHECK_ATTACH; ci_send_message(0, NULL, NULL, Context, NULL); menu->redraw = REDRAW_FULL; break; case OP_MAIL_KEY: if (!(WithCrypto & APPLICATION_PGP)) break; CHECK_ATTACH; ci_send_message(SENDKEY, NULL, NULL, NULL, NULL); menu->redraw = REDRAW_FULL; break; case OP_EXTRACT_KEYS: if (!WithCrypto) break; CHECK_MSGCOUNT; CHECK_VISIBLE; crypt_extract_keys_from_messages(tag ? NULL : CURHDR); menu->redraw = REDRAW_FULL; break; case OP_CHECK_TRADITIONAL: if (!(WithCrypto & APPLICATION_PGP)) break; CHECK_MSGCOUNT; CHECK_VISIBLE; if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)) mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw); if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } break; case OP_PIPE: CHECK_MSGCOUNT; CHECK_VISIBLE; mutt_pipe_message(tag ? NULL : CURHDR); #ifdef USE_IMAP /* in an IMAP folder index with imap_peek=no, piping could change * new or old messages status to read. Redraw what's needed. */ if (Context->magic == MUTT_IMAP && !option(OPT_IMAP_PEEK)) { menu->redraw |= (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS; } #endif break; case OP_PRINT: CHECK_MSGCOUNT; CHECK_VISIBLE; mutt_print_message(tag ? NULL : CURHDR); #ifdef USE_IMAP /* in an IMAP folder index with imap_peek=no, printing could change * new or old messages status to read. Redraw what's needed. */ if (Context->magic == MUTT_IMAP && !option(OPT_IMAP_PEEK)) { menu->redraw |= (tag ? REDRAW_INDEX : REDRAW_CURRENT) | REDRAW_STATUS; } #endif break; case OP_MAIN_READ_THREAD: case OP_MAIN_READ_SUBTHREAD: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_SEEN, _("Cannot mark message(s) as read")); rc = mutt_thread_set_flag(CURHDR, MUTT_READ, 1, op == OP_MAIN_READ_THREAD ? 0 : 1); if (rc != -1) { if (option(OPT_RESOLVE)) { menu->current = (op == OP_MAIN_READ_THREAD ? mutt_next_thread(CURHDR) : mutt_next_subthread(CURHDR)); if (menu->current == -1) { menu->current = menu->oldcurrent; } else if (menu->menu == MENU_PAGER) { op = OP_DISPLAY_MESSAGE; continue; } } menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } break; case OP_MARK_MSG: CHECK_MSGCOUNT; CHECK_VISIBLE; if (CURHDR->env->message_id) { char str[STRING], macro[STRING]; char buf2[128]; buf2[0] = '\0'; /* L10N: This is the prompt for . Whatever they enter will be prefixed by $mark_macro_prefix and will become a macro hotkey to jump to the currently selected message. */ if (!mutt_get_field(_("Enter macro stroke: "), buf2, sizeof(buf2), MUTT_CLEAR) && buf2[0]) { snprintf(str, sizeof(str), "%s%s", MarkMacroPrefix, buf2); snprintf(macro, sizeof(macro), "~i \"%s\"\n", CURHDR->env->message_id); /* L10N: "message hotkey" is the key bindings menu description of a macro created by . */ km_bind(str, MENU_MAIN, OP_MACRO, macro, _("message hotkey")); /* L10N: This is echoed after creates a new hotkey macro. %s is the hotkey string ($mark_macro_prefix followed by whatever they typed at the prompt.) */ snprintf(buf2, sizeof(buf2), _("Message bound to %s."), str); mutt_message(buf2); mutt_debug(1, "Mark: %s => %s\n", str, macro); } } else /* L10N: This error is printed if cannot find a Message-ID for the currently selected message in the index. */ mutt_error(_("No message ID to macro.")); break; case OP_RECALL_MESSAGE: CHECK_ATTACH; ci_send_message(SENDPOSTPONED, NULL, NULL, Context, NULL); menu->redraw = REDRAW_FULL; break; case OP_RESEND: CHECK_ATTACH; CHECK_MSGCOUNT; CHECK_VISIBLE; if (tag) { for (j = 0; j < Context->msgcount; j++) { if (message_is_tagged(Context, j)) mutt_resend_message(NULL, Context, Context->hdrs[j]); } } else mutt_resend_message(NULL, Context, CURHDR); menu->redraw = REDRAW_FULL; break; #ifdef USE_NNTP case OP_FOLLOWUP: case OP_FORWARD_TO_GROUP: CHECK_MSGCOUNT; CHECK_VISIBLE; /* fallthrough */ case OP_POST: CHECK_ATTACH; if (op != OP_FOLLOWUP || !CURHDR->env->followup_to || (mutt_str_strcasecmp(CURHDR->env->followup_to, "poster") != 0) || query_quadoption(OPT_FOLLOWUP_TO_POSTER, _("Reply by mail as poster prefers?")) != MUTT_YES) { if (Context && Context->magic == MUTT_NNTP && !((struct NntpData *) Context->data)->allowed && query_quadoption(OPT_POST_MODERATED, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES) { break; } if (op == OP_POST) ci_send_message(SENDNEWS, NULL, NULL, Context, NULL); else { CHECK_MSGCOUNT; ci_send_message((op == OP_FOLLOWUP ? SENDREPLY : SENDFORWARD) | SENDNEWS, NULL, NULL, Context, tag ? NULL : CURHDR); } menu->redraw = REDRAW_FULL; break; } #endif /* fallthrough */ case OP_REPLY: CHECK_ATTACH; CHECK_MSGCOUNT; CHECK_VISIBLE; if (option(OPT_PGP_AUTO_DECODE) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) { mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw); } ci_send_message(SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR); menu->redraw = REDRAW_FULL; break; case OP_SHELL_ESCAPE: mutt_shell_escape(); break; case OP_TAG_THREAD: case OP_TAG_SUBTHREAD: CHECK_MSGCOUNT; CHECK_VISIBLE; rc = mutt_thread_set_flag(CURHDR, MUTT_TAG, !CURHDR->tagged, op == OP_TAG_THREAD ? 0 : 1); if (rc != -1) { if (option(OPT_RESOLVE)) { if (op == OP_TAG_THREAD) menu->current = mutt_next_thread(CURHDR); else menu->current = mutt_next_subthread(CURHDR); if (menu->current == -1) menu->current = menu->oldcurrent; } menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } break; case OP_UNDELETE: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message")); if (tag) { mutt_tag_set_flag(MUTT_DELETE, 0); mutt_tag_set_flag(MUTT_PURGE, 0); menu->redraw |= REDRAW_INDEX; } else { mutt_set_flag(Context, CURHDR, MUTT_DELETE, 0); mutt_set_flag(Context, CURHDR, MUTT_PURGE, 0); if (option(OPT_RESOLVE) && menu->current < Context->vcount - 1) { menu->current++; menu->redraw |= REDRAW_MOTION_RESYNCH; } else menu->redraw |= REDRAW_CURRENT; } menu->redraw |= REDRAW_STATUS; break; case OP_UNDELETE_THREAD: case OP_UNDELETE_SUBTHREAD: CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; /* L10N: CHECK_ACL */ CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message(s)")); rc = mutt_thread_set_flag(CURHDR, MUTT_DELETE, 0, op == OP_UNDELETE_THREAD ? 0 : 1); if (rc != -1) rc = mutt_thread_set_flag(CURHDR, MUTT_PURGE, 0, op == OP_UNDELETE_THREAD ? 0 : 1); if (rc != -1) { if (option(OPT_RESOLVE)) { if (op == OP_UNDELETE_THREAD) menu->current = mutt_next_thread(CURHDR); else menu->current = mutt_next_subthread(CURHDR); if (menu->current == -1) menu->current = menu->oldcurrent; } menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } break; case OP_VERSION: mutt_version(); break; case OP_BUFFY_LIST: mutt_buffy_list(); break; case OP_VIEW_ATTACHMENTS: CHECK_MSGCOUNT; CHECK_VISIBLE; mutt_view_attachments(CURHDR); if (Context && CURHDR->attach_del) Context->changed = true; menu->redraw = REDRAW_FULL; break; case OP_END_COND: break; case OP_WHAT_KEY: mutt_what_key(); break; #ifdef USE_SIDEBAR case OP_SIDEBAR_NEXT: case OP_SIDEBAR_NEXT_NEW: case OP_SIDEBAR_PAGE_DOWN: case OP_SIDEBAR_PAGE_UP: case OP_SIDEBAR_PREV: case OP_SIDEBAR_PREV_NEW: mutt_sb_change_mailbox(op); break; case OP_SIDEBAR_TOGGLE_VISIBLE: toggle_option(OPT_SIDEBAR_VISIBLE); mutt_reflow_windows(); break; case OP_SIDEBAR_TOGGLE_VIRTUAL: mutt_sb_toggle_virtual(); break; #endif default: if (menu->menu == MENU_MAIN) km_error_key(MENU_MAIN); } #ifdef USE_NOTMUCH if (Context) nm_debug_check(Context); #endif if (menu->menu == MENU_PAGER) { mutt_clear_pager_position(); menu->menu = MENU_MAIN; menu->redraw = REDRAW_FULL; } if (done) break; } mutt_pop_current_menu(menu); mutt_menu_destroy(&menu); return close; } void mutt_set_header_color(struct Context *ctx, struct Header *curhdr) { struct ColorLine *color = NULL; struct PatternCache cache; if (!curhdr) return; memset(&cache, 0, sizeof(cache)); STAILQ_FOREACH(color, &ColorIndexList, entries) { if (mutt_pattern_exec(color->color_pattern, MUTT_MATCH_FULL_ADDRESS, ctx, curhdr, &cache)) { curhdr->pair = color->pair; return; } } curhdr->pair = ColorDefs[MT_COLOR_NORMAL]; } neomutt-neomutt-20171215/doc/000077500000000000000000000000001321473123000157035ustar00rootroot00000000000000neomutt-neomutt-20171215/doc/Makefile.autosetup000066400000000000000000000131001321473123000213660ustar00rootroot00000000000000MAKEDOC_CPP = $(CC_FOR_BUILD) -D_MAKEDOC -E -C -I. doc/makedoc$(EXEEXT): $(SRCDIR)/doc/makedoc.c $(CC_FOR_BUILD) -I. -o $@ $(SRCDIR)/doc/makedoc.c doc/neomuttrc: $(SRCDIR)/init.h doc/makedoc$(EXEEXT) $(SRCDIR)/doc/neomuttrc.head sed -e 's,@docdir@,$(docdir),' $(SRCDIR)/doc/neomuttrc.head \ > doc/neomuttrc $(MAKEDOC_CPP) $(SRCDIR)/init.h | doc/makedoc$(EXEEXT) -c \ >> doc/neomuttrc @if BUILD_DOC CHUNKED_DOCFILES = doc/advancedusage.html \ doc/configuration.html \ doc/gettingstarted.html \ doc/intro.html \ doc/mimesupport.html \ doc/miscellany.html \ doc/optionalfeatures.html \ doc/reference.html \ doc/security.html \ doc/tuning.html HTML_DOCFILES = doc/manual.html doc/index.html $(CHUNKED_DOCFILES) srcdir_DOCFILES = $(SRCDIR)/doc/PGP-Notes.txt \ $(SRCDIR)/doc/smime-notes.txt \ $(SRCDIR)/ChangeLog.md \ $(SRCDIR)/CODE_OF_CONDUCT.md \ $(SRCDIR)/COPYRIGHT \ $(SRCDIR)/INSTALL \ $(SRCDIR)/LICENSE.md \ $(SRCDIR)/README.md \ $(SRCDIR)/README.SSL all-doc: $(CHUNKED_DOCFILES) \ doc/index.html \ doc/manual.html \ doc/manual.txt \ doc/neomuttrc \ doc/neomuttrc.man doc/manual.html: doc/manual.xml \ $(SRCDIR)/doc/html.xsl \ $(SRCDIR)/doc/neomutt.xsl \ $(SRCDIR)/doc/neomutt.css xsltproc --nonet -o $@ $(SRCDIR)/doc/html.xsl doc/manual.xml doc/manual.txt: doc/manual.html -LC_ALL=C w3m -dump -O UTF8 doc/manual.html > $@ || \ LC_ALL=C lynx -dump -nolist -with_backspaces \ -display_charset=us-ascii doc/manual.html > $@ || \ LC_ALL=C elinks -dump -no-numbering -no-references \ doc/manual.html | sed -e 's,\\001, ,g' > $@ $(CHUNKED_DOCFILES): doc/index.html doc/index.html: $(SRCDIR)/doc/chunk.xsl \ $(SRCDIR)/doc/neomutt.xsl \ $(SRCDIR)/doc/neomutt.css \ doc/manual.xml xsltproc --nonet -o doc/ $(SRCDIR)/doc/chunk.xsl doc/manual.xml > /dev/null 2>&1 doc/neomuttrc.man: doc/makedoc$(EXEEXT) \ $(SRCDIR)/init.h \ $(SRCDIR)/doc/neomuttrc.man.head \ $(SRCDIR)/doc/neomuttrc.man.tail $(MAKEDOC_CPP) $(SRCDIR)/init.h | doc/makedoc$(EXEEXT) -m | \ cat $(SRCDIR)/doc/neomuttrc.man.head - \ $(SRCDIR)/doc/neomuttrc.man.tail > $@ doc/manual.xml: doc/makedoc$(EXEEXT) $(SRCDIR)/init.h $(SRCDIR)/opcodes.h \ $(SRCDIR)/doc/manual.xml.head $(SRCDIR)/functions.h \ $(SRCDIR)/doc/manual.xml.tail $(SRCDIR)/doc/gen-map-doc ( sed -e "s/@VERSION@/$(PACKAGE_VERSION)/" \ $(SRCDIR)/doc/manual.xml.head && \ $(MAKEDOC_CPP) $(SRCDIR)/init.h | doc/makedoc$(EXEEXT) -s && \ $(MAKEDOC_CPP) $(SRCDIR)/functions.h | \ perl $(SRCDIR)/doc/gen-map-doc $(SRCDIR)/doc/manual.xml.tail \ $(SRCDIR)/opcodes.h \ ) > $@ install-doc: all-doc $(MKDIR_P) $(DESTDIR)$(mandir)/man1 $(MKDIR_P) $(DESTDIR)$(mandir)/man5 $(MKDIR_P) $(DESTDIR)$(sysconfdir) $(INSTALL) -m 644 doc/neomutt.1 $(DESTDIR)$(mandir)/man1/neomutt.1 $(INSTALL) -m 644 doc/neomuttrc.man $(DESTDIR)$(mandir)/man5/neomuttrc.5 $(INSTALL) -m 644 $(SRCDIR)/doc/smime_keys.1 $(DESTDIR)$(mandir)/man1/smime_keys_$(PACKAGE).1 $(INSTALL) -m 644 $(SRCDIR)/doc/pgpewrap.1 $(DESTDIR)$(mandir)/man1/pgpewrap_$(PACKAGE).1 $(INSTALL) -m 644 $(SRCDIR)/doc/pgpring.1 $(DESTDIR)$(mandir)/man1/pgpring_$(PACKAGE).1 $(INSTALL) -m 644 $(SRCDIR)/doc/mbox.5 $(DESTDIR)$(mandir)/man5/mbox_$(PACKAGE).5 $(INSTALL) -m 644 $(SRCDIR)/doc/mmdf.5 $(DESTDIR)$(mandir)/man5/mmdf_$(PACKAGE).5 $(MKDIR_P) $(DESTDIR)$(docdir) for f in $(srcdir_DOCFILES); do \ $(INSTALL) -m 644 $$f $(DESTDIR)$(docdir); \ done -$(INSTALL) -m 644 doc/manual.txt $(DESTDIR)$(docdir) -for f in $(HTML_DOCFILES); do \ $(INSTALL) -m 644 $$f $(DESTDIR)$(docdir); \ done $(INSTALL) -m 644 doc/neomuttrc $(DESTDIR)$(sysconfdir)/neomuttrc # Install mime.types $(INSTALL_DATA) $(SRCDIR)/doc/mime.types $(DESTDIR)$(docdir)/mime.types uninstall-doc: for f in neomutt.1 smime_keys_$(PACKAGE).1 pgpewrap_$(PACKAGE).1 pgpring_$(PACKAGE).1; do \ $(RM) $(DESTDIR)$(mandir)/man1/$$f; \ done for f in neomuttrc.5 mbox_$(PACKAGE).5 mmdf_$(PACKAGE).5; do \ $(RM) $(DESTDIR)$(mandir)/man5/$$f; \ done for f in $(srcdir_DOCFILES) $(HTML_DOCFILES); do \ $(RM) $(DESTDIR)$(docdir)/`basename $$f`; \ done $(RM) $(DESTDIR)$(docdir)/manual.txt $(RM) $(DESTDIR)$(sysconfdir)/neomuttrc # Uninstall mime.types $(RM) $(DESTDIR)$(docdir)/mime.types clean-doc: $(RM) doc/*.html doc/neomuttrc.man \ doc/makedoc$(EXEEXT) doc/makedoc.o \ doc/makedoc.Po doc/manual.txt doc/manual.xml \ doc/neomuttrc validate-doc: doc/manual.xml xmllint --noout --noblanks --postvalid doc/manual.xml spellcheck-doc: -aspell -d american --mode=sgml --encoding=utf-8 -p \ doc/neomutt.pwl check doc/manual.xml.head -aspell -d american --mode=nroff --encoding=utf-8 -p \ doc/neomutt.pwl check doc/neomuttrc.man.head -aspell -d american --mode=ccpp --encoding=utf-8 -p \ doc/neomutt.pwl check init.h sortcheck-doc: doc/manual.xml sed -n -e '1,/^/d' \ -e '1,/^/s//\1/p' \ < doc/manual.xml > doc/vars.tmp.1 sort < doc/vars.tmp.1 > doc/vars.tmp.2 cmp -s doc/vars.tmp.1 doc/vars.tmp.2 || \ diff -u doc/vars.tmp.1 doc/vars.tmp.2 | less $(RM) doc/vars.tmp.1 doc/vars.tmp.2 @else # Let's generate neomuttrc in all cases: it doesn't require any additional 3rd # party dependencies and distributions tend to rely on having it. all-doc: doc/neomuttrc clean-doc: $(RM) doc/neomuttrc doc/makedoc$(EXEEXT) install-doc: all-doc $(MKDIR_P) $(DESTDIR)$(sysconfdir) $(INSTALL) -m 644 doc/neomuttrc $(DESTDIR)$(sysconfdir)/neomuttrc uninstall-doc: $(RM) $(DESTDIR)$(sysconfdir)/neomuttrc @endif # vim: set ts=8 noexpandtab: neomutt-neomutt-20171215/doc/PGP-Notes.txt000066400000000000000000000177661321473123000202010ustar00rootroot00000000000000 USING PGP FROM WITHIN MUTT WARNING: The configuration interface has completely changed as of 0.96.3! USERS' GUIDE How do I use mutt with PGP, PGP5, or GnuPG? ------------------------------------------- Go to the contrib subdirectory of the source tree. You'll find three files there, pgp2.rc, pgp5.rc, and gpg.rc. These files contain ready-to-use configurations for using mutt with pgp2, pgp5, and gpg. Include one of these files with your ~/.muttrc, and things should work out fine. You may wish to verify that all paths and the language parameters given to the PGP binaries match your needs. Frequently Asked Questions and Tips ----------------------------------- Q: "People are sending PGP messages which mutt doesn't recognize. What can I do?" The new way is to leave headers alone and use mutt's check-traditional-pgp function, which can detect PGP messages at run-time, and adjust content-types. The old way is to configure your mail filter so it fixes headers: Add the following lines to your ~/.procmailrc (you are using procmail, aren't you?): ------------------------------ ## ## PGP ## :0 * !^Content-Type: message/ * !^Content-Type: multipart/ * !^Content-Type: application/pgp { :0 fBw * ^-----BEGIN PGP MESSAGE----- * ^-----END PGP MESSAGE----- | formail \ -i "Content-Type: application/pgp; format=text; x-action=encrypt" :0 fBw * ^-----BEGIN PGP SIGNED MESSAGE----- * ^-----BEGIN PGP SIGNATURE----- * ^-----END PGP SIGNATURE----- | formail \ -i "Content-Type: application/pgp; format=text; x-action=sign" } ------------------------------ For users of maildrop, "Mark Weinem" suggests the following recipe: ------------------------------ BPGPM="-----BEGIN PGP MESSAGE-----" EPGPM="-----END PGP MESSAGE-----" BPGPS="-----BEGIN PGP SIGNATURE-----" EPGPS="-----END PGP SIGNATURE-----" if (!/^Content-Type: message/ && !/^Content-Type: multipart/ \ && !/^Content-Type: application\/pgp/) { if (/^$BPGPM/:b && /^$EPGPM/:b) xfilter "reformail -A 'Content-Type: application/pgp; format=text; \ x-action=encrypt'" if (/^$BPGPS/:b && /^$EPGPS/:b) xfilter "reformail -A 'Content-Type: application/pgp; format=text; \ x-action=sign'" } ------------------------------ Q: "I don't like that PGP/MIME stuff, but want to use the old way of PGP-signing my mails. Can't you include that with mutt?" The old answer to this question used to be this: No. Application/pgp is not really suited to a world with MIME, non-textual body parts and similar things. Anyway, if you really want to generate these old-style attachments, include the following macro in your ~/.muttrc (line breaks for readability, this is actually one line): macro compose S "Fpgp +verbose=0 -fast +clearsig=on\ny^T^Uapplication/pgp; format=text; x-action=sign\n" There's a new answer, though: Set the $pgp_autoinline configuration variable (it's a quad-option) to something different from "no" (that's the default). Mutt will then try to use application/pgp wherever it makes sense. In particular, it does not make any sense with multiparts, or non-ASCII or non-text bodies. In all other cases, PGP/MIME is used unconditionally. Note that application/pgp is still strongly deprecated. Q: "I don't like all the ^Gs and various other verbosity PGP is presenting me with." Roland Rosenfeld has found a quite elegant solution to this problem: PGP has some pretty good foreign language support. So we just introduce a language called "mutt" which contains empty strings for the messages we don't want to see. To use this, copy either language.txt or language50.txt (depending on what PGP version you are using) to your $PGPPATH. Make sure the PGP command formats pass "+language=pgp" to all the PGP binaries (but not to pgpring!). For PGP 2.6, a German version called "muttde" is available as well. Q: "My PGP signatures are being invalidated. BTW, I'm using Courier MTA." The author of the Courier MTA believes that the standard specifying multipart/signed is broken. For that reason, he has chosen to implement his MTA in a way which does not assure that multipart/signed body parts are left untouched. We suggest that you abandon courier and change to sendmail, postfix, or exim. BACKGROUND Auxiliary Programs ------------------ Mutt needs two auxiliary programs for its PGP support: pgpewrap and pgpring. 1. pgpring pgpring is a key ring dumper. It extracts information from PGP's binary key ring and emits it in an (almost) readable output format understood by mutt's key selection routines. This output format mimics the one used by the GNU Privacy Guard (GPG). You'll need this program with PGP 2 and PGP 5. Command line options: -k Dump the contents of the key ring specified as an argument to -k. -2, -5 Use the default key ring for PGP 2 or 5, respectively. -s Dump the secret key ring. -S Dump signatures. -f Dump fingerprints. 2. pgpewrap This is a little C program which does some command line munging: The first argument is a command to be executed. When pgpewrap encounters a "--" (dash-dash) argument, it will interpret the next argument as a prefix which is put in front of all following arguments. Example: pgpewrap pgpe file -- -r a b c will execute: pgpe file -r a -r b -r c This script is needed with PGP 5 and with GPG, since their command line interfaces can't be properly served by mutt's format mechanism. The Configuration Interface --------------------------- As usual within mutt, the configuration interface for the PGP commands relies on printf-like formats. For all PGP commands, the following %-sequences are defined. %p The empty string when no passphrase is needed, the string "PGPPASSFD=0" if one is needed. This is mostly used in conditional % sequences. %f Most PGP commands operate on a single file or a file containing a message. %f expands to this file's name. %s When verifying signatures, there is another temporary file containing the detached signature. %s expands to this file's name. %a In "signing" contexts, this expands to the value of the configuration variable $pgp_sign_as. You probably need to use this within a conditional % sequence. %r In many contexts, mutt passes key IDs to pgp. %r expands to a list of key IDs. The following command formats are defined: $pgp_decode_command Decode application/pgp messages. This command operates with and without pass phrases. $pgp_verify_command Verify a PGP/MIME signature. $pgp_decrypt_command Decrypt a PGP/MIME encrypted MIME body. This command always gets a pass phrase. $pgp_sign_command Sign a PGP/MIME body. This command always gets a pass phrase. $pgp_encrypt_sign_command Encrypt and sign a MIME body. This command always gets a pass phrase. $pgp_encrypt_only_command Encrypt a MIME body, but don't sign it. $pgp_import_command Import PGP keys from a file. $pgp_export_command Export PGP keys to a file. The output must be ASCII armored. $pgp_verify_key_command Check a public key. This is used from the key selection menu. $pgp_list_secring_command List the secret keys matching some hints given in %r. $pgp_list_pubring_command List the public keys matching some hints given in %r. The passphrase is always passed on stdin; all commands must send their output to stdout and stderr. neomutt-neomutt-20171215/doc/chunk.xsl000066400000000000000000000005361321473123000175470ustar00rootroot00000000000000 neomutt-neomutt-20171215/doc/gen-map-doc000066400000000000000000000042531321473123000177210ustar00rootroot00000000000000#!/usr/bin/perl -w use strict; my (%OPS, %MAP, %DOC, $map); my $xml = shift @ARGV; open F, "cat @ARGV |" or die "OPS*: $!"; while () { if ( /^\s+_fmt\((\w+),\s+N_\("(.+)"\)\)/ ) { $OPS{$1} = $2; } } close F; my $num_ops = scalar(keys(%OPS)); if ( $num_ops == 0 ) { die "No OPS found. Has the syntax changed?"; } while () { if (/^const struct Binding Op.*{ \/\* map: (.*) \*\//) { $map = $1; $DOC{$map} = ""; } if ($map and /^\s*\*\*\s*(.*)/) { $DOC{$map} .= "$1\n"; } if ($map and /{\s*"(.+)"\s*,\s*(\w+)\s*,\s*(?:"([^"]+)"|(\w+))\s*}/) { my ($function, $op, $binding) = ($1, $2, $3 || $4); $binding =~ s/&/&/; # for , try CamelCasing into $binding =~ s/<(.)(.+)>/<\U$1\E$2>/; $binding =~ s//>/; $binding =~ s/ /<Space>/; $binding =~ s/^\\033/Esc /; $binding =~ s/^\\010/<Backspace>/; $binding =~ s/^\\(0\d+)$/'^'.chr(64+oct($1))/e; $binding =~ s/^\\(0\d+)(.)/'^'.chr(64+oct($1)) ." $2"/e; $binding =~ s/\\t/<Tab>/; $binding =~ s/\\r/<Return>/; $binding =~ s/\\n/<Enter>/; $binding =~ s/NULL//; die "unknown key $binding" if $binding =~ /\\[^\\]|<|>/; die "unknown OP $op" unless $OPS{$op}; $MAP{$map} .= "<$function>$binding$OPS{$op}\n"; } if ($map and /^}/) { undef $map; } } open XML, $xml or die "$xml: $!"; while () { if (/__print_map\((.*)\)/) { my $map = $1; my $maptitle = $1; $maptitle =~ s/^(.)/\U$1\E/; unless ($MAP{$map}) { warn "map $map undefined"; next; } my $title = $map; $title =~ s/(.)(.+)/\U$1\E$2/; print < $maptitle Menu $DOC{$map} Default $title Menu Bindings FunctionDefault keyDescription $MAP{$map}
EOT delete $MAP{$map}; } else { print; } } close XML; warn "unprinted maps: ". join(" ", keys %MAP) if %MAP; neomutt-neomutt-20171215/doc/html.xsl000066400000000000000000000003671321473123000174050ustar00rootroot00000000000000 neomutt-neomutt-20171215/doc/makedoc.c000066400000000000000000000746531321473123000174710ustar00rootroot00000000000000/** * @file * Read nroff-like comments in the code and convert them into documentation * * @authors * Copyright (C) 1999-2000 Thomas Roessler * * @copyright * 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, see . */ /** ** This program parses neomutt's init.h and generates documentation in ** three different formats: ** ** -> a commented neomuttrc configuration file ** -> nroff, suitable for inclusion in a manual page ** -> docbook-xml, suitable for inclusion in the ** SGML-based manual ** **/ #include "config.h" #include #include #include #include #include #include #include #include "makedoc_defs.h" extern int optind; #define BUFSIZE 2048 /** * enum OutputFormats - Documentation output formats */ enum OutputFormats { F_CONF, F_MAN, F_SGML, F_NONE }; #define D_NL (1 << 0) #define D_EM (1 << 1) #define D_BF (1 << 2) #define D_TAB (1 << 3) #define D_NP (1 << 4) #define D_INIT (1 << 5) #define D_DL (1 << 6) #define D_DT (1 << 7) #define D_DD (1 << 8) #define D_PA (1 << 9) #define D_IL (1 << 10) #define D_TT (1 << 11) /** * enum SpecialChars - All specially-treated characters */ enum SpecialChars { SP_START_EM, SP_START_BF, SP_START_TT, SP_END_FT, SP_NEWLINE, SP_NEWPAR, SP_END_PAR, SP_STR, SP_START_TAB, SP_END_TAB, SP_START_DL, SP_DT, SP_DD, SP_END_DD, SP_END_DL, SP_START_IL, SP_END_IL, SP_END_SECT, SP_REFER }; enum OutputFormats OutputFormat = F_NONE; char *Progname = NULL; short Debug = 0; /* skip whitespace */ static char *skip_ws(char *s) { while (*s && isspace((unsigned char) *s)) s++; return s; } /* isolate a token */ static char single_char_tokens[] = "[]{},;|"; static char *get_token(char *d, size_t l, char *s) { char *t = NULL; bool is_quoted = false; char *dd = d; if (Debug) fprintf(stderr, "%s: get_token called for `%s'.\n", Progname, s); s = skip_ws(s); if (Debug > 1) fprintf(stderr, "%s: argument after skip_ws(): `%s'.\n", Progname, s); if (!*s) { if (Debug) fprintf(stderr, "%s: no more tokens on this line.\n", Progname); return NULL; } if (strchr(single_char_tokens, *s)) { if (Debug) { fprintf(stderr, "%s: found single character token `%c'.\n", Progname, *s); } d[0] = *s++; d[1] = '\0'; return s; } if (*s == '"') { if (Debug) { fprintf(stderr, "%s: found quote character.\n", Progname); } s++; is_quoted = true; } for (t = s; *t && --l > 0; t++) { if (*t == '\\' && !t[1]) break; if (is_quoted && *t == '\\') { switch ((*d = *++t)) { case 'n': *d = '\n'; break; case 't': *d = '\t'; break; case 'r': *d = '\r'; break; case 'a': *d = '\a'; break; } d++; continue; } if (is_quoted && *t == '"') { t++; break; } else if (!is_quoted && strchr(single_char_tokens, *t)) break; else if (!is_quoted && isspace((unsigned char) *t)) break; else *d++ = *t; } *d = '\0'; if (Debug) { fprintf(stderr, "%s: Got %stoken: `%s'.\n", Progname, is_quoted ? "quoted " : "", dd); fprintf(stderr, "%s: Remainder: `%s'.\n", Progname, t); } return t; } static int sgml_fputc(int c, FILE *out) { switch (c) { /* the bare minimum for escaping */ case '<': return fputs("<", out); case '>': return fputs(">", out); case '&': return fputs("&", out); default: return fputc(c, out); } } static int sgml_fputs(const char *s, FILE *out) { for (; *s; s++) if (sgml_fputc((unsigned int) *s, out) == EOF) return EOF; return 0; } /* print something. */ static int print_it(int special, char *str, FILE *out, int docstat) { int onl = docstat & (D_NL | D_NP); docstat &= ~(D_NL | D_NP | D_INIT); switch (OutputFormat) { /* configuration file */ case F_CONF: { switch (special) { static int Continuation = 0; case SP_END_FT: docstat &= ~(D_EM | D_BF | D_TT); break; case SP_START_BF: docstat |= D_BF; break; case SP_START_EM: docstat |= D_EM; break; case SP_START_TT: docstat |= D_TT; break; case SP_NEWLINE: { if (onl) docstat |= onl; else { fputs("\n# ", out); docstat |= D_NL; } if (docstat & D_DL) Continuation++; break; } case SP_NEWPAR: { if (onl & D_NP) { docstat |= onl; break; } if (!(onl & D_NL)) fputs("\n# ", out); fputs("\n# ", out); docstat |= D_NP; break; } case SP_START_TAB: { if (!onl) fputs("\n# ", out); docstat |= D_TAB; break; } case SP_END_TAB: { docstat &= ~D_TAB; docstat |= D_NL; break; } case SP_START_DL: { docstat |= D_DL; break; } case SP_DT: { Continuation = 0; docstat |= D_DT; break; } case SP_DD: { if (docstat & D_IL) fputs("- ", out); Continuation = 0; break; } case SP_END_DL: { Continuation = 0; docstat &= ~D_DL; break; } case SP_START_IL: { docstat |= D_IL; break; } case SP_END_IL: { Continuation = 0; docstat &= ~D_IL; break; } case SP_STR: { if (Continuation) { Continuation = 0; fputs(" ", out); } fputs(str, out); if (docstat & D_DT) { for (int i = strlen(str); i < 8; i++) putc(' ', out); docstat &= ~D_DT; docstat |= D_NL; } break; } } break; } /* manual page */ case F_MAN: { switch (special) { case SP_END_FT: { fputs("\\fP", out); docstat &= ~(D_EM | D_BF | D_TT); break; } case SP_START_BF: { fputs("\\fB", out); docstat |= D_BF; docstat &= ~(D_EM | D_TT); break; } case SP_START_EM: { fputs("\\fI", out); docstat |= D_EM; docstat &= ~(D_BF | D_TT); break; } case SP_START_TT: { fputs("\\fC", out); docstat |= D_TT; docstat &= ~(D_BF | D_EM); break; } case SP_NEWLINE: { if (onl) docstat |= onl; else { fputc('\n', out); docstat |= D_NL; } break; } case SP_NEWPAR: { if (onl & D_NP) { docstat |= onl; break; } if (!(onl & D_NL)) fputc('\n', out); fputs(".IP\n", out); docstat |= D_NP; break; } case SP_START_TAB: { fputs("\n.IP\n.EX\n", out); docstat |= D_TAB | D_NL; break; } case SP_END_TAB: { fputs("\n.EE\n", out); docstat &= ~D_TAB; docstat |= D_NL; break; } case SP_START_DL: { fputs(".RS\n.PD 0\n", out); docstat |= D_DL; break; } case SP_DT: { fputs(".TP\n", out); break; } case SP_DD: { if (docstat & D_IL) fputs(".TP\n\\(hy ", out); else fputs("\n", out); break; } case SP_END_DL: { fputs(".RE\n.PD 1", out); docstat &= ~D_DL; break; } case SP_START_IL: { fputs(".RS\n.PD 0\n", out); docstat |= D_IL; break; } case SP_END_IL: { fputs(".RE\n.PD 1", out); docstat &= ~D_DL; break; } case SP_STR: { while (*str) { for (; *str; str++) { if (*str == '"') fputs("\"", out); else if (*str == '\\') fputs("\\\\", out); else if (*str == '-') fputs("\\-", out); else if (strncmp(str, "``", 2) == 0) { fputs("\\(lq", out); str++; } else if (strncmp(str, "''", 2) == 0) { fputs("\\(rq", out); str++; } else fputc(*str, out); } } break; } } break; } /* SGML based manual */ case F_SGML: { switch (special) { case SP_END_FT: { if (docstat & D_EM) fputs("", out); if (docstat & D_BF) fputs("", out); if (docstat & D_TT) fputs("", out); docstat &= ~(D_EM | D_BF | D_TT); break; } case SP_START_BF: { fputs("", out); docstat |= D_BF; docstat &= ~(D_EM | D_TT); break; } case SP_START_EM: { fputs("", out); docstat |= D_EM; docstat &= ~(D_BF | D_TT); break; } case SP_START_TT: { fputs("", out); docstat |= D_TT; docstat &= ~(D_BF | D_EM); break; } case SP_NEWLINE: { if (onl) docstat |= onl; else { fputc('\n', out); docstat |= D_NL; } break; } case SP_NEWPAR: { if (onl & D_NP) { docstat |= onl; break; } if (!(onl & D_NL)) fputc('\n', out); if (docstat & D_PA) fputs("\n", out); fputs("\n", out); docstat |= D_NP; docstat |= D_PA; break; } case SP_END_PAR: { fputs("\n", out); docstat &= ~D_PA; break; } case SP_START_TAB: { if (docstat & D_PA) { fputs("\n\n", out); docstat &= ~D_PA; } fputs("\n\n", out); docstat |= D_TAB | D_NL; break; } case SP_END_TAB: { fputs("", out); docstat &= ~D_TAB; docstat |= D_NL; break; } case SP_START_DL: { if (docstat & D_PA) { fputs("\n\n", out); docstat &= ~D_PA; } fputs("\n\n\n\n", out); docstat |= D_DL; break; } case SP_DT: { fputs("", out); break; } case SP_DD: { docstat |= D_DD; if (docstat & D_DL) fputs("", out); else fputs("", out); break; } case SP_END_DD: { if (docstat & D_DL) fputs("\n", out); else fputs("", out); docstat &= ~D_DD; break; } case SP_END_DL: { fputs("\n", out); docstat &= ~(D_DD | D_DL); break; } case SP_START_IL: { if (docstat & D_PA) { fputs("\n\n", out); docstat &= ~D_PA; } fputs("\n\n", out); docstat |= D_IL; break; } case SP_END_IL: { fputs("\n", out); docstat &= ~(D_DD | D_DL); break; } case SP_END_SECT: { fputs("", out); break; } case SP_STR: { if (docstat & D_TAB) sgml_fputs(str, out); else { while (*str) { for (; *str; str++) { if (strncmp(str, "``", 2) == 0) { fputs("", out); str++; } else if (strncmp(str, "''", 2) == 0) { fputs("", out); str++; } else sgml_fputc(*str, out); } } } break; } } break; } /* make gcc happy (unreached) */ default: break; } return docstat; } /* close eventually-open environments. */ static int fd_recurse = 0; static int flush_doc(int docstat, FILE *out) { if (docstat & D_INIT) return D_INIT; if (fd_recurse++) { fprintf(stderr, "%s: Internal error, recursion in flush_doc()!\n", Progname); exit(1); } if (docstat & (D_PA)) docstat = print_it(SP_END_PAR, NULL, out, docstat); if (docstat & (D_TAB)) docstat = print_it(SP_END_TAB, NULL, out, docstat); if (docstat & (D_DL)) docstat = print_it(SP_END_DL, NULL, out, docstat); if (docstat & (D_EM | D_BF | D_TT)) docstat = print_it(SP_END_FT, NULL, out, docstat); docstat = print_it(SP_END_SECT, NULL, out, docstat); docstat = print_it(SP_NEWLINE, NULL, out, 0); fd_recurse--; return D_INIT; } static int commit_buf(char *buf, char **d, FILE *out, int docstat) { if (*d > buf) { **d = '\0'; docstat = print_it(SP_STR, buf, out, docstat); *d = buf; } return docstat; } /** ** Documentation line parser ** ** The following code parses specially formatted documentation ** comments in init.h. ** ** The format is very remotely inspired by nroff. Most important, it's ** easy to parse and convert, and it was easy to generate from the SGML ** source of neomutt's original manual. ** ** - \fI switches to italics ** - \fB switches to boldface ** - \fP switches to normal display ** - .dl on a line starts a definition list (name taken taken from HTML). ** - .dt starts a term in a definition list. ** - .dd starts a definition in a definition list. ** - .de on a line finishes a definition list. ** - .il on a line starts an itemized list ** - .dd starts an item in an itemized list ** - .ie on a line finishes an itemized list ** - .ts on a line starts a "tscreen" environment (name taken from SGML). ** - .te on a line finishes this environment. ** - .pp on a line starts a paragraph. ** - \$word will be converted to a reference to word, where appropriate. ** Note that \$$word is possible as well. ** - '. ' in the beginning of a line expands to two space characters. ** This is used to protect indentations in tables. **/ /** * sgml_id_fputs - reduce CDATA to ID */ static int sgml_id_fputs(const char *s, FILE *out) { char id; if (*s == '<') s++; for (; *s; s++) { if (*s == '_') id = '-'; else id = *s; if (*s == '>' && !*(s + 1)) break; if (fputc((unsigned int) id, out) == EOF) return EOF; } return 0; } void print_ref(FILE *out, int output_dollar, const char *ref) { switch (OutputFormat) { case F_CONF: case F_MAN: if (output_dollar) putc('$', out); fputs(ref, out); break; case F_SGML: fputs("", out); if (output_dollar) fputc('$', out); sgml_fputs(ref, out); fputs("", out); break; default: break; } } static int handle_docline(char *l, FILE *out, int docstat) { char buf[BUFSIZE]; char *s = NULL, *d = NULL; l = skip_ws(l); if (Debug) fprintf(stderr, "%s: handle_docline `%s'\n", Progname, l); if (strncmp(l, ".pp", 3) == 0) return print_it(SP_NEWPAR, NULL, out, docstat); else if (strncmp(l, ".ts", 3) == 0) return print_it(SP_START_TAB, NULL, out, docstat); else if (strncmp(l, ".te", 3) == 0) return print_it(SP_END_TAB, NULL, out, docstat); else if (strncmp(l, ".dl", 3) == 0) return print_it(SP_START_DL, NULL, out, docstat); else if (strncmp(l, ".de", 3) == 0) return print_it(SP_END_DL, NULL, out, docstat); else if (strncmp(l, ".il", 3) == 0) return print_it(SP_START_IL, NULL, out, docstat); else if (strncmp(l, ".ie", 3) == 0) return print_it(SP_END_IL, NULL, out, docstat); else if (strncmp(l, ". ", 2) == 0) *l = ' '; for (s = l, d = buf; *s; s++) { if (strncmp(s, "\\(as", 4) == 0) { *d++ = '*'; s += 3; } else if (strncmp(s, "\\(rs", 4) == 0) { *d++ = '\\'; s += 3; } else if (strncmp(s, "\\fI", 3) == 0) { docstat = commit_buf(buf, &d, out, docstat); docstat = print_it(SP_START_EM, NULL, out, docstat); s += 2; } else if (strncmp(s, "\\fB", 3) == 0) { docstat = commit_buf(buf, &d, out, docstat); docstat = print_it(SP_START_BF, NULL, out, docstat); s += 2; } else if (strncmp(s, "\\fC", 3) == 0) { docstat = commit_buf(buf, &d, out, docstat); docstat = print_it(SP_START_TT, NULL, out, docstat); s += 2; } else if (strncmp(s, "\\fP", 3) == 0) { docstat = commit_buf(buf, &d, out, docstat); docstat = print_it(SP_END_FT, NULL, out, docstat); s += 2; } else if (strncmp(s, ".dt", 3) == 0) { if (docstat & D_DD) { docstat = commit_buf(buf, &d, out, docstat); docstat = print_it(SP_END_DD, NULL, out, docstat); } docstat = commit_buf(buf, &d, out, docstat); docstat = print_it(SP_DT, NULL, out, docstat); s += 3; } else if (strncmp(s, ".dd", 3) == 0) { if ((docstat & D_IL) && (docstat & D_DD)) { docstat = commit_buf(buf, &d, out, docstat); docstat = print_it(SP_END_DD, NULL, out, docstat); } docstat = commit_buf(buf, &d, out, docstat); docstat = print_it(SP_DD, NULL, out, docstat); s += 3; } else if (*s == '$') { bool output_dollar = false; char *ref = NULL; char save; s++; if (*s == '$') { output_dollar = true; s++; } if (*s == '$') { *d++ = '$'; } else { ref = s; while (isalnum((unsigned char) *s) || (*s && strchr("-_<>", *s))) s++; docstat = commit_buf(buf, &d, out, docstat); save = *s; *s = '\0'; print_ref(out, output_dollar, ref); *s = save; s--; } } else *d++ = *s; } docstat = commit_buf(buf, &d, out, docstat); return print_it(SP_NEWLINE, NULL, out, docstat); } /* note: the following enum must be in the same order as the * following string definitions! */ /** * enum DataType - User-variable types */ enum DataType { DT_NONE = 0, DT_BOOL, DT_NUMBER, DT_STRING, DT_PATH, DT_QUAD, DT_SORT, DT_REGEX, DT_MAGIC, DT_SYNONYM, DT_ADDRESS, DT_MBTABLE }; struct VariableTypes { char *machine; char *human; } types[] = { { "DT_NONE", "-none-" }, { "DT_BOOL", "boolean" }, { "DT_NUMBER", "number" }, { "DT_STRING", "string" }, { "DT_PATH", "path" }, { "DT_QUAD", "quadoption" }, { "DT_SORT", "sort order" }, { "DT_REGEX", "regular expression" }, { "DT_MAGIC", "folder magic" }, { "DT_SYNONYM", NULL }, { "DT_ADDRESS", "e-mail address" }, { "DT_MBTABLE", "string" }, { NULL, NULL }, }; static int buf_to_type(const char *s) { for (int type = DT_NONE; types[type].machine; type++) if (strcmp(types[type].machine, s) == 0) return type; return DT_NONE; } static void pretty_default(char *t, size_t l, const char *s, int type) { memset(t, 0, l); l--; switch (type) { case DT_QUAD: { if (strcasecmp(s, "MUTT_YES") == 0) strncpy(t, "yes", l); else if (strcasecmp(s, "MUTT_NO") == 0) strncpy(t, "no", l); else if (strcasecmp(s, "MUTT_ASKYES") == 0) strncpy(t, "ask-yes", l); else if (strcasecmp(s, "MUTT_ASKNO") == 0) strncpy(t, "ask-no", l); break; } case DT_BOOL: { if (atoi(s)) strncpy(t, "yes", l); else strncpy(t, "no", l); break; } case DT_SORT: { /* heuristic! */ if (strncmp(s, "SORT_", 5) != 0) fprintf(stderr, "WARNING: expected prefix of SORT_ for type DT_SORT " "instead of %s\n", s); strncpy(t, s + 5, l); for (; *t; t++) *t = tolower((unsigned char) *t); break; } case DT_MAGIC: { /* heuristic! */ if (strncmp(s, "MUTT_", 5) != 0) fprintf(stderr, "WARNING: expected prefix of MUTT_ for type DT_MAGIC " "instead of %s\n", s); strncpy(t, s + 5, l); for (; *t; t++) *t = tolower((unsigned char) *t); break; } case DT_STRING: case DT_REGEX: case DT_ADDRESS: case DT_PATH: case DT_MBTABLE: { if (strcmp(s, "0") == 0) break; /* fallthrough */ } default: { strncpy(t, s, l); break; } } } static void char_to_escape(char *dest, unsigned int c) { switch (c) { case '\r': strcpy(dest, "\\r"); break; case '\n': strcpy(dest, "\\n"); break; case '\t': strcpy(dest, "\\t"); break; case '\f': strcpy(dest, "\\f"); break; default: sprintf(dest, "\\%03o", c); break; } } static void conf_char_to_escape(unsigned int c, FILE *out) { char buf[16]; char_to_escape(buf, c); fputs(buf, out); } static void conf_print_strval(const char *v, FILE *out) { for (; *v; v++) { if (*v < ' ' || *v & 0x80) { conf_char_to_escape((unsigned int) *v, out); continue; } if (*v == '"' || *v == '\\') fputc('\\', out); fputc(*v, out); } } static const char *type2human(int type) { return types[type].human; } /** ** Configuration line parser ** ** The following code parses a line from init.h which declares ** a configuration variable. ** **/ static void man_print_strval(const char *v, FILE *out) { for (; *v; v++) { if (*v < ' ' || *v & 0x80) { fputc('\\', out); conf_char_to_escape((unsigned int) *v, out); continue; } if (*v == '"') fputs("\"", out); else if (*v == '\\') fputs("\\\\", out); else if (*v == '-') fputs("\\-", out); else fputc(*v, out); } } static void sgml_print_strval(const char *v, FILE *out) { char buf[16]; for (; *v; v++) { if (*v < ' ' || *v & 0x80) { char_to_escape(buf, (unsigned int) *v); sgml_fputs(buf, out); continue; } sgml_fputc((unsigned int) *v, out); } } static void print_confline(const char *varname, int type, const char *val, FILE *out) { if (type == DT_SYNONYM) return; switch (OutputFormat) { /* configuration file */ case F_CONF: { if (type == DT_STRING || type == DT_REGEX || type == DT_ADDRESS || type == DT_PATH || type == DT_MBTABLE) { fprintf(out, "\n# set %s=\"", varname); conf_print_strval(val, out); fputs("\"", out); } else if (type != DT_SYNONYM) fprintf(out, "\n# set %s=%s", varname, val); fprintf(out, "\n#\n# Name: %s", varname); fprintf(out, "\n# Type: %s", type2human(type)); if (type == DT_STRING || type == DT_REGEX || type == DT_ADDRESS || type == DT_PATH || type == DT_MBTABLE) { fputs("\n# Default: \"", out); conf_print_strval(val, out); fputs("\"", out); } else fprintf(out, "\n# Default: %s", val); fputs("\n# ", out); break; } /* manual page */ case F_MAN: { fprintf(out, "\n.TP\n.B %s\n", varname); fputs(".nf\n", out); fprintf(out, "Type: %s\n", type2human(type)); if (type == DT_STRING || type == DT_REGEX || type == DT_ADDRESS || type == DT_PATH || type == DT_MBTABLE) { fputs("Default: \"", out); man_print_strval(val, out); fputs("\"\n", out); } else { fputs("Default: ", out); man_print_strval(val, out); fputs("\n", out); } fputs(".fi", out); break; } /* SGML based manual */ case F_SGML: { fputs("\n\n", out); sgml_fputs(varname, out); fprintf(out, "\nType: %s", type2human(type)); if (type == DT_STRING || type == DT_REGEX || type == DT_ADDRESS || type == DT_PATH || type == DT_MBTABLE) { if (val && *val) { fputs("\nDefault: ", out); sgml_print_strval(val, out); fputs("", out); } else { fputs("\nDefault: (empty)", out); } fputs("\n", out); } else fprintf(out, "\nDefault: %s\n", val); break; } /* make gcc happy */ default: break; } } static void handle_confline(char *s, FILE *out) { char varname[BUFSIZE]; char buf[BUFSIZE]; char tmp[BUFSIZE]; int type; char val[BUFSIZE]; /* xxx - put this into an actual state machine? */ /* variable name */ s = get_token(varname, sizeof(varname), s); if (!s) return; /* comma */ s = get_token(buf, sizeof(buf), s); if (!s) return; /* type */ s = get_token(buf, sizeof(buf), s); if (!s) return; type = buf_to_type(buf); /* possibly a "|" or comma */ s = get_token(buf, sizeof(buf), s); if (!s) return; if (strcmp(buf, "|") == 0) { if (Debug) fprintf(stderr, "%s: Expecting .\n", Progname); /* ignore subtype and comma */ s = get_token(buf, sizeof(buf), s); if (!s) return; s = get_token(buf, sizeof(buf), s); if (!s) return; } /* redraw, comma */ while (true) { s = get_token(buf, sizeof(buf), s); if (!s) return; if (strcmp(buf, ",") == 0) break; } /* option name or UL &address */ s = get_token(buf, sizeof(buf), s); if (!s) return; if (strcmp(buf, "UL") == 0) { s = get_token(buf, sizeof(buf), s); if (!s) return; } /* comma */ s = get_token(buf, sizeof(buf), s); if (!s) return; if (Debug) fprintf(stderr, "%s: Expecting default value.\n", Progname); /* or UL */ s = get_token(buf, sizeof(buf), s); if (!s) return; if (strcmp(buf, "UL") == 0) { if (Debug) fprintf(stderr, "%s: Skipping UL.\n", Progname); s = get_token(buf, sizeof(buf), s); if (!s) return; } memset(tmp, 0, sizeof(tmp)); do { if (strcmp(buf, "}") == 0) break; strncpy(tmp + strlen(tmp), buf, sizeof(tmp) - strlen(tmp)); } while ((s = get_token(buf, sizeof(buf), s))); pretty_default(val, sizeof(val), tmp, type); print_confline(varname, type, val, out); } static void makedoc(FILE *in, FILE *out) { char buffer[BUFSIZE]; char token[BUFSIZE]; char *p = NULL; int active = 0; int line = 0; int docstat = D_INIT; while ((fgets(buffer, sizeof(buffer), in))) { line++; p = strchr(buffer, '\n'); if (!p) { fprintf(stderr, "%s: Line %d too long. Ask a wizard to enlarge\n" "%s: my buffer size.\n", Progname, line, Progname); exit(1); } else *p = '\0'; p = get_token(token, sizeof(token), buffer); if (!p) continue; if (Debug) { fprintf(stderr, "%s: line %d. first token: \"%s\".\n", Progname, line, token); } if (strcmp(token, "/*++*/") == 0) active = 1; else if (strcmp(token, "/*--*/") == 0) { docstat = flush_doc(docstat, out); active = 0; } else if (active && ((strcmp(token, "/**") == 0) || (strcmp(token, "**") == 0))) docstat = handle_docline(p, out, docstat); else if (active && (strcmp(token, "{") == 0)) { docstat = flush_doc(docstat, out); handle_confline(p, out); } } flush_doc(docstat, out); fputs("\n", out); } int main(int argc, char *argv[]) { int c; FILE *f = NULL; if ((Progname = strrchr(argv[0], '/'))) Progname++; else Progname = argv[0]; while ((c = getopt(argc, argv, "cmsd")) != EOF) { switch (c) { case 'c': OutputFormat = F_CONF; break; case 'm': OutputFormat = F_MAN; break; case 's': OutputFormat = F_SGML; break; case 'd': Debug++; break; default: { fprintf(stderr, "%s: bad command line parameter.\n", Progname); exit(1); } } } if (optind != argc) { f = fopen(argv[optind], "r"); if (!f) { fprintf(stderr, "%s: Can't open %s (%s).\n", Progname, argv[optind], strerror(errno)); exit(1); } } else f = stdin; switch (OutputFormat) { case F_CONF: case F_MAN: case F_SGML: makedoc(f, stdout); break; default: { fprintf(stderr, "%s: No output format specified.\n", Progname); exit(1); } } if (f != stdin) fclose(f); return 0; } neomutt-neomutt-20171215/doc/makedoc_defs.h000066400000000000000000000035541321473123000204670ustar00rootroot00000000000000/** * @file * Helper for makedoc to enable all code paths * * @authors * @copyright * 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, see . */ /* build complete documentation */ #ifndef _MUTT_MAKEDOC_DEFS_H #define _MUTT_MAKEDOC_DEFS_H #ifdef MAKEDOC_FULL #ifndef CRYPT_BACKEND_GPGME #define CRYPT_BACKEND_GPGME #endif #ifndef USE_IMAP #define USE_IMAP #endif #ifndef MIXMASTER #define MIXMASTER "mixmaster" #endif #ifndef USE_POP #define USE_POP #endif #ifndef USE_SMTP #define USE_SMTP #endif #ifndef USE_SSL_OPENSSL #define USE_SSL_OPENSSL #endif #ifndef HAVE_SSL_PARTIAL_CHAIN #define HAVE_SSL_PARTIAL_CHAIN #endif #ifndef USE_SSL_GNUTLS #define USE_SSL_GNUTLS #endif #ifndef USE_SSL #define USE_SSL #endif #ifndef USE_SOCKET #define USE_SOCKET #endif #ifndef USE_HCACHE #define USE_HCACHE #endif #ifndef HAVE_BDB #define HAVE_BDB #endif #ifndef HAVE_GDBM #define HAVE_GDBM #endif #ifndef HAVE_QDBM #define HAVE_QDBM #endif #ifndef HAVE_LIBIDN #define HAVE_LIBIDN #endif #ifndef HAVE_GETADDRINFO #define HAVE_GETADDRINFO #endif #ifndef USE_SASL #define USE_SASL #endif #ifndef USE_SIDEBAR #define USE_SIDEBAR #endif #ifndef USE_COMPRESSED #define USE_COMPRESSED #endif #ifndef USE_LUA #define USE_LUA #endif #ifndef USE_NOTMUCH #define USE_NOTMUCH #endif #endif #endif /* _MUTT_MAKEDOC_DEFS_H */ neomutt-neomutt-20171215/doc/manual.xml.head000066400000000000000000023602421321473123000206130ustar00rootroot00000000000000 The NeoMutt E-Mail Client Michael Elkins me@cs.hmc.edu version @VERSION@ All mail clients suck. This one just sucks less.— me, circa 1995 Introduction NeoMutt is a small but very powerful text-based MIME mail client. NeoMutt is highly configurable, and is well suited to the mail power user with advanced features like key bindings, keyboard macros, mail threading, regular expression searches and a powerful pattern matching language for selecting groups of messages. NeoMutt Home Page The homepage can be found at https://www.neomutt.org/. Mailing Lists neomutt-users@neomutt.org — help, bug reports and feature requests. To subscribe to this list, please send a mail to neomutt-users-request@neomutt.org with the subject "subscribe". neomutt-devel@neomutt.org — development mailing list. To subscribe to this list, please send a mail to neomutt-devel-request@neomutt.org with the subject "subscribe". NeoMutt Online Resources Issue Tracking System Bugs may be reported on the devel mailing list, or on GitHub: https://github.com/neomutt/neomutt/issues IRC For the IRC user community, visit channel #neomutt on irc.freenode.net. Contributing to NeoMutt There are various ways to contribute to the NeoMutt project. Especially for new users it may be helpful to meet other new and experienced users to chat about NeoMutt, talk about problems and share tricks. Since translations of NeoMutt into other languages are highly appreciated, the NeoMutt developers always look for skilled translators that help improve and continue to maintain stale translations. For contributing code patches for new features and bug fixes, please refer to the developer pages at https://www.neomutt.org/dev.html for more details. Typographical Conventions This section lists typographical conventions followed throughout this manual. See table for typographical conventions for special terms. Typographical conventions for special terms Item Refers to... printf(3) UNIX manual pages, execute man 3 printf <PageUp> named keys <create-alias> named NeoMutt function ^G Control+G key combination $mail_check NeoMutt configuration option $HOME environment variable
Examples are presented as: neomutt -v Within command synopsis, curly brackets ( {}) denote a set of options of which one is mandatory, square brackets ( []) denote optional arguments, three dots denote that the argument may be repeated arbitrary times.
Copyright NeoMutt is Copyright © 1996-2016 Michael R. Elkins me@neomutt.org and others. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Getting Started This section is intended as a brief overview of how to use NeoMutt. There are many other features which are described elsewhere in the manual. There is even more information available in the NeoMutt FAQ and various web pages. See the NeoMutt homepage for more details. The keybindings described in this section are the defaults as distributed. Your local system administrator may have altered the defaults for your site. You can always type ?in any menu to display the current bindings. The first thing you need to do is invoke NeoMutt, simply by typing neomutt at the command line. There are various command-line options, see either the NeoMutt man page or the reference. Core Concepts NeoMutt is a text-based application which interacts with users through different menus which are mostly line-/entry-based or page-based. A line-based menu is the so-called index menu (listing all messages of the currently opened folder) or the alias menu (allowing you to select recipients from a list). Examples for page-based menus are the pager(showing one message at a time) or the help menu listing all available key bindings. The user interface consists of a context sensitive help line at the top, the menu's contents followed by a context sensitive status line and finally the command line. The command line is used to display informational and error messages as well as for prompts and for entering interactive commands. NeoMutt is configured through variables which, if the user wants to permanently use a non-default value, are written to configuration files. NeoMutt supports a rich config file syntax to make even complex configuration files readable and commentable. Because NeoMutt allows for customizing almost all key bindings, there are so-called functions which can be executed manually (using the command line) or in macros. Macros allow the user to bind a sequence of commands to a single key or a short key sequence instead of repeating a sequence of actions over and over. Many commands (such as saving or copying a message to another folder) can be applied to a single message or a set of messages (so-called tagged messages). To help selecting messages, NeoMutt provides a rich set of message patterns (such as recipients, sender, body contents, date sent/received, etc.) which can be combined into complex expressions using the boolean and and or operations as well as negating. These patterns can also be used to (for example) search for messages or to limit the index to show only matching messages. NeoMutt supports a hook concept which allows the user to execute arbitrary configuration commands and functions in certain situations such as entering a folder, starting a new message or replying to an existing one. These hooks can be used to highly customize NeoMutt's behavior including managing multiple identities, customizing the display for a folder or even implementing auto-archiving based on a per-folder basis and much more. Besides an interactive mode, NeoMutt can also be used as a command-line tool only send messages. It also supports a mailx(1)-compatible interface, see for a complete list of command-line options. Screens and Menus Index The index is the screen that you usually see first when you start NeoMutt. It gives an overview over your emails in the currently opened mailbox. By default, this is your system mailbox. The information you see in the index is a list of emails, each with its number on the left, its flags (new email, important email, email that has been forwarded or replied to, tagged email, ...), the date when email was sent, its sender, the email size, and the subject. Additionally, the index also shows thread hierarchies: when you reply to an email, and the other person replies back, you can see the other person's email in a "sub-tree" below. This is especially useful for personal email between a group of people or when you've subscribed to mailing lists. Pager The pager is responsible for showing the email content. On the top of the pager you have an overview over the most important email headers like the sender, the recipient, the subject, and much more information. How much information you actually see depends on your configuration, which we'll describe below. Below the headers, you see the email body which usually contains the message. If the email contains any attachments, you will see more information about them below the email body, or, if the attachments are text files, you can view them directly in the pager. To give the user a good overview, it is possible to configure NeoMutt to show different things in the pager with different colors. Virtually everything that can be described with a regular expression can be colored, e.g. URLs, email addresses or smileys. File Browser The file browser is the interface to the local or remote file system. When selecting a mailbox to open, the browser allows custom sorting of items, limiting the items shown by a regular expression and a freely adjustable format of what to display in which way. It also allows for easy navigation through the file system when selecting file(s) to attach to a message, select multiple files to attach and many more. Sidebar The Sidebar shows a list of all your mailboxes. The list can be turned on and off, it can be themed and the list style can be configured. This part of the manual is suitable for beginners. If you already know NeoMutt you could skip ahead to the main Sidebar guide. If you just want to get started, you could use the sample Sidebar neomuttrc. To check if NeoMutt supports Sidebar, look for the string +sidebar in the neomutt version. neomutt -v Let's turn on the Sidebar: set sidebar_visible set sidebar_format = "%B%?F? [%F]?%* %?N?%N/?%S" set mail_check_stats You will see something like this. A list of mailboxes on the left. A list of emails, from the selected mailbox, on the right. Fruit [1] 3/8| 1 + Jan 24 Rhys Lee (192) Yew Animals [1] 2/6| 2 + Feb 11 Grace Hall (167) Ilama Cars 4| 3 Feb 23 Aimee Scott (450) Nectarine Seas 1/7| 4 ! Feb 28 Summer Jackson (264) Lemon | 5 Mar 07 Callum Harrison (464) Raspberry | 6 N + Mar 24 Samuel Harris (353) Tangerine | 7 N + Sep 05 Sofia Graham (335) Cherry | 8 N Sep 16 Ewan Brown (105) Ugli | | This user has four mailboxes: Fruit, Cars, Animals and Seas. The current, open, mailbox is Fruit. We can also see information about the other mailboxes. For example: The Animals mailbox contains, 1 flagged email, 2 new emails out of a total of 6 emails. Navigation The Sidebar adds some new functions to NeoMutt. The user pressed the c key to <change-folder>to the Animals mailbox. The Sidebar automatically updated the indicator to match. Fruit [1] 3/8| 1 Jan 03 Tia Gibson (362) Caiman Animals [1] 2/6| 2 + Jan 22 Rhys Lee ( 48) Dolphin Cars 4| 3 ! Aug 16 Ewan Brown (333) Hummingbird Seas 1/7| 4 Sep 25 Grace Hall ( 27) Capybara | 5 N + Nov 12 Evelyn Rogers (453) Tapir | 6 N + Nov 16 Callum Harrison (498) Hedgehog | | | | Let's map some functions: bind index,pager \CP sidebar-prev # Ctrl-Shift-P - Previous Mailbox bind index,pager \CN sidebar-next # Ctrl-Shift-N - Next Mailbox bind index,pager \CO sidebar-open # Ctrl-Shift-O - Open Highlighted Mailbox Press Ctrl-Shift-N(Next mailbox) twice will move the Sidebar highlight to down to the Seas mailbox. Fruit [1] 3/8| 1 Jan 03 Tia Gibson (362) Caiman Animals [1] 2/6| 2 + Jan 22 Rhys Lee ( 48) Dolphin Cars 4| 3 ! Aug 16 Ewan Brown (333) Hummingbird Seas 1/7| 4 Sep 25 Grace Hall ( 27) Capybara | 5 N + Nov 12 Evelyn Rogers (453) Tapir | 6 N + Nov 16 Callum Harrison (498) Hedgehog | | | | Functions <sidebar-next>and <sidebar-prev>move the Sidebar highlight. They do not change the open mailbox. Press Ctrl-Shift-O( <sidebar-open>) to open the highlighted mailbox. Fruit [1] 3/8| 1 ! Mar 07 Finley Jones (139) Molucca Sea Animals [1] 2/6| 2 + Mar 24 Summer Jackson ( 25) Arafura Sea Cars 4| 3 + Feb 28 Imogen Baker (193) Pechora Sea Seas 1/7| 4 N + Feb 23 Isla Hussain (348) Balearic Sea | | | | | | Features The Sidebar shows a list of mailboxes in a panel. Everything about the Sidebar can be configured. <link linkend="intro-sidebar-basics">State of the Sidebar</link> Visibility Width <link linkend="intro-sidebar-limit">Which mailboxes are displayed</link> Display all Limit to mailboxes with new mail Whitelist mailboxes to display always <link linkend="sidebar-sort">The order in which mailboxes are displayed</link> Unsorted (order of mailboxes commands) Sorted alphabetically Sorted by number of new mails <link linkend="intro-sidebar-colors">Color</link> Sidebar indicators and divider Mailboxes depending on their type Mailboxes depending on their contents <link linkend="sidebar-functions">Key bindings</link> Hide/Unhide the Sidebar Select previous/next mailbox Select previous/next mailbox with new mail Page up/down through a list of mailboxes Misc Formatting string for mailbox Wraparound searching Flexible mailbox abbreviations Support for Unicode mailbox names (UTF-8) Display Everything about the Sidebar can be configured. For a quick reference: Sidebar variables to set Sidebar colors to apply Sidebar sort methods Sidebar Basics The most important variable is $sidebar_visible. You can set this in your neomuttrc, or bind a key to the function <sidebar-toggle-visible>. set sidebar_visible # Make the Sidebar visible by default bind index,pager B sidebar-toggle-visible # Use 'B' to switch the Sidebar on and off Next, decide how wide you want the Sidebar to be. 25 characters might be enough for the mailbox name and some numbers. Remember, you can hide/show the Sidebar at the press of button. Finally, you might want to change the divider character. By default, Sidebar draws an ASCII line between it and the Index panel If your terminal supports it, you can use a Unicode line-drawing character. set sidebar_width = 25 # Plenty of space set sidebar_divider_char = '│' # Pretty line-drawing character Sidebar Format String $sidebar_format allows you to customize the Sidebar display. For an introduction, read format strings including the section about conditionals. The default value is: %B%* %n A more detailed value is: %B%?F? [%F]?%* %?N?%N/?%S Which breaks down as: %B- Mailbox name %?F? [%F]?- If flagged emails [%F], otherwise nothing %*- Pad with spaces %?N?%N/?- If new emails %N/, otherwise nothing %S- Total number of emails sidebar_format Format Notes Description %B Name of the mailbox %S * † Size of mailbox (total number of messages) %F * † Number of Flagged messages in the mailbox %N * † Number of New messages in the mailbox %n * If there's new mail, display N, otherwise nothing %! !: one flagged message; !!: two flagged messages; n!: n flagged messages (for n > 2). Otherwise prints nothing. %d * ‡ Number of deleted messages %L * ‡ Number of messages after limiting %t * ‡ Number of tagged messages %>X Right justify the rest of the string and pad with X %|X Pad to the end of the line with X %*X Soft-fill with character X as pad
* = Can be optionally printed if nonzero † = To use this expandos, you must first: set mail_check_stats ‡ = Only applicable to the current folder Here are some examples. They show the number of (F)lagged, (N)ew and (S)ize. sidebar_format Format Example %B%?F? [%F]?%* %?N?%N/?%S mailbox [F] N/S %B%* %F:%N:%S mailbox F:N:S %B %?N?(%N)?%* %S mailbox (N) S %B%* ?F?%F/?%N mailbox F/S
Abbreviating Mailbox Names $sidebar_delim_chars tells Sidebar how to split up mailbox paths. For local directories use /; for IMAP folders use . Example 1 This example works well if your mailboxes have unique names after the last separator. Add some mailboxes of different depths. set folder="~/mail" mailboxes =fruit/apple =fruit/banana =fruit/cherry mailboxes =water/sea/sicily =water/sea/archipelago =water/sea/sibuyan mailboxes =water/ocean/atlantic =water/ocean/pacific =water/ocean/arctic Shorten the names: set sidebar_short_path # Shorten mailbox names (truncate all subdirs) set sidebar_component_depth=1 # Shorten mailbox names (truncate 1 subdirs) set sidebar_delim_chars="/" # Delete everything up to the last or Nth / character The screenshot below shows what the Sidebar would look like before and after shortening using sidebar_short_path. |fruit/apple |apple |fruit/banana |banana |fruit/cherry |cherry |water/sea/sicily |sicily |water/sea/archipelago |archipelago |water/sea/sibuyan |sibuyan |water/ocean/atlantic |atlantic |water/ocean/pacific |pacific |water/ocean/arctic |arctic The screenshot below shows what the Sidebar would look like before and after shortening using sidebar_component_depth=1. |fruit/apple |apple |fruit/banana |banana |fruit/cherry |cherry |water/sea/sicily |sea/sicily |water/sea/archipelago |sea/archipelago |water/sea/sibuyan |sea/sibuyan |water/ocean/atlantic |ocean/atlantic |water/ocean/pacific |ocean/pacific |water/ocean/arctic |ocean/arctic Example 2 This example works well if you have lots of mailboxes which are arranged in a tree. Add some mailboxes of different depths. set folder="~/mail" mailboxes =fruit mailboxes =fruit/apple =fruit/banana =fruit/cherry mailboxes =water mailboxes =water/sea mailboxes =water/sea/sicily =water/sea/archipelago =water/sea/sibuyan mailboxes =water/ocean mailboxes =water/ocean/atlantic =water/ocean/pacific =water/ocean/arctic Shorten the names: set sidebar_short_path # Shorten mailbox names set sidebar_delim_chars="/" # Delete everything up to the last / character set sidebar_folder_indent # Indent folders whose names we've shortened set sidebar_indent_string=" " # Indent with two spaces The screenshot below shows what the Sidebar would look like before and after shortening. |fruit |fruit |fruit/apple | apple |fruit/banana | banana |fruit/cherry | cherry |water |water |water/sea | sea |water/sea/sicily | sicily |water/sea/archipelago | archipelago |water/sea/sibuyan | sibuyan |water/ocean | ocean |water/ocean/atlantic | atlantic |water/ocean/pacific | pacific |water/ocean/arctic | arctic Sometimes, it will be necessary to add mailboxes, that you don't use, to fill in part of the tree. This will trade vertical space for horizontal space (but it looks good). Limiting the Number of Mailboxes If you have a lot of mailboxes, sometimes it can be useful to hide the ones you aren't using. $sidebar_new_mail_only tells Sidebar to only show mailboxes that contain new, or flagged, email. If you want some mailboxes to be always visible, then use the sidebar_whitelist command. It takes a list of mailboxes as parameters. set sidebar_new_mail_only # Only mailboxes with new/flagged email sidebar_whitelist fruit fruit/apple # Always display these two mailboxes
Colors Here is a sample color scheme: color sidebar_indicator default color17 # Dark blue background color sidebar_highlight white color238 # Grey background color sidebar_spoolfile yellow default # Yellow color sidebar_new green default # Green color sidebar_ordinary default default # Default colors color sidebar_flagged red default # Red color sidebar_divider color8 default # Dark grey There is a priority order when coloring Sidebar mailboxes. e.g. If a mailbox has new mail it will have the sidebar_new color, even if it also contains flagged mails. Sidebar Color Priority Priority Color Description Highest sidebar_indicator Mailbox is open sidebar_highlight Mailbox is highlighted sidebar_spoolfile Mailbox is the spoolfile (receives incoming mail) sidebar_new Mailbox contains new mail sidebar_flagged Mailbox contains flagged mail Lowest sidebar_ordinary Mailbox does not match above
Config Changes If you haven't used Sidebar before, you can ignore this section. Some of the Sidebar config has been changed to make its meaning clearer. These changes have been made since the previous Sidebar release: 2015-11-11. Config Changes Old Name New Name $sidebar_delim $sidebar_divider_char $sidebar_folderindent $sidebar_folder_indent $sidebar_indentstr $sidebar_indent_string $sidebar_newmail_only $sidebar_new_mail_only $sidebar_shortpath $sidebar_short_path $sidebar_sort $sidebar_sort_method <sidebar-scroll-down> <sidebar-page-down> <sidebar-scroll-up> <sidebar-page-up>
Help The help screen is meant to offer a quick help to the user. It lists the current configuration of key bindings and their associated commands including a short description, and currently unbound functions that still need to be associated with a key binding (or alternatively, they can be called via the NeoMutt command prompt). Compose Menu The compose menu features a split screen containing the information which really matter before actually sending a message by mail: who gets the message as what (recipients and who gets what kind of copy). Additionally, users may set security options like deciding whether to sign, encrypt or sign and encrypt a message with/for what keys. Also, it's used to attach messages, to re-edit any attachment including the message itself. Alias Menu The alias menu is used to help users finding the recipients of messages. For users who need to contact many people, there's no need to remember addresses or names completely because it allows for searching, too. The alias mechanism and thus the alias menu also features grouping several addresses by a shorter nickname, the actual alias, so that users don't have to select each single recipient manually. Attachment Menu As will be later discussed in detail, NeoMutt features a good and stable MIME implementation, that is, it supports sending and receiving messages of arbitrary MIME types. The attachment menu displays a message's structure in detail: what content parts are attached to which parent part (which gives a true tree structure), which type is of what type and what size. Single parts may saved, deleted or modified to offer great and easy access to message's internals.
Moving Around in Menus The most important navigation keys common to line- or entry-based menus are shown in and in for page-based menus. Most common navigation keys in entry-based menus Key Function Description j or <Down> <next-entry> move to the next entry k or <Up> <previous-entry> move to the previous entry z or <PageDn> <page-down> go to the next page Z or <PageUp> <page-up> go to the previous page = or <Home> <first-entry> jump to the first entry * or <End> <last-entry> jump to the last entry q <quit> exit the current menu ? <help> list all keybindings for the current menu
Most common navigation keys in page-based menus Key Function Description J or <Return> <next-line> scroll down one line <Backspace> <previous-line> scroll up one line K, <Space> or <PageDn> <next-page> move to the next page - or <PageUp> <previous-page> move the previous page <Home> <top> move to the top <End> <bottom> move to the bottom
Editing Input Fields Introduction NeoMutt has a built-in line editor for inputting text, e.g. email addresses or filenames. The keys used to manipulate text input are very similar to those of Emacs. See for a full reference of available functions, their default key bindings, and short descriptions. Most common line editor keys Key Function Description ^A or <Home> <bol> move to the start of the line ^B or <Left> <backward-char> move back one char Esc B <backward-word> move back one word ^D or <Delete> <delete-char> delete the char under the cursor ^E or <End> <eol> move to the end of the line ^F or <Right> <forward-char> move forward one char Esc F <forward-word> move forward one word <Tab> <complete> complete filename, alias, or label ^T <complete-query> complete address with query ^K <kill-eol> delete to the end of the line Esc d <kill-eow> delete to the end of the word ^W <kill-word> kill the word in front of the cursor ^U <kill-line> delete entire line ^V <quote-char> quote the next typed key <Up> <history-up> recall previous string from history <Down> <history-down> recall next string from history <BackSpace> <backspace> kill the char in front of the cursor Esc u <upcase-word> convert word to upper case Esc l <downcase-word> convert word to lower case Esc c <capitalize-word> capitalize the word ^G n/a abort <Return> n/a finish editing
You can remap the editor functions using the bind command. For example, to make the <Delete> key delete the character in front of the cursor rather than under, you could use: bind editor <delete> backspace
History NeoMutt maintains a history for the built-in editor. The number of items is controlled by the $history variable and can be made persistent using an external file specified using $history_file and $save_history. You may cycle through them at an editor prompt by using the <history-up>and/or <history-down>commands. NeoMutt will remember the currently entered text as you cycle through history, and will wrap around to the initial entry line. NeoMutt maintains several distinct history lists, one for each of the following categories: .neomuttrc commands addresses and aliases shell commands filenames patterns everything else NeoMutt automatically filters out consecutively repeated items from the history. If $history_remove_dups is set, all repeated items are removed from the history. It also mimics the behavior of some shells by ignoring items starting with a space. The latter feature can be useful in macros to not clobber the history's valuable entries with unwanted entries.
Reading Mail Similar to many other mail clients, there are two modes in which mail is read in NeoMutt. The first is a list of messages in the mailbox, which is called the index menu in NeoMutt. The second mode is the display of the message contents. This is called the pager. The next few sections describe the functions provided in each of these modes. The Message Index Common keys used to navigate through and manage messages in the index are shown in . How messages are presented in the index menu can be customized using the $index_format variable. Most common message index keys Key Description c change to a different mailbox Esc c change to a folder in read-only mode C copy the current message to another mailbox Esc C decode a message and copy it to a folder Esc s decode a message and save it to a folder D delete messages matching a pattern d delete the current message F mark as important l show messages matching a pattern N mark message as new o change the current sort method O reverse sort the mailbox q save changes and exit s save-message T tag messages matching a pattern t toggle the tag on a message Esc t toggle tag on entire message thread U undelete messages matching a pattern u undelete-message v view-attachments x abort changes and exit <Return> display-message <Tab> jump to the next new or unread message @ show the author's full e-mail address $ save changes to mailbox / search Esc / search-reverse ^L clear and redraw the screen ^T untag messages matching a pattern
In addition to who sent the message and the subject, a short summary of the disposition of each message is printed beside the message number. Zero or more of the flags in may appear, some of which can be turned on or off using these functions: <set-flag>and <clear-flag>bound by default to w and W respectively. Furthermore, the flags in reflect who the message is addressed to. They can be customized with the $to_chars variable. Message status flags Flag Description D message is deleted (is marked for deletion) d message has attachments marked for deletion K contains a PGP public key N message is new O message is old P message is PGP encrypted r message has been replied to S message is signed, and the signature is successfully verified s message is signed ! message is flagged * message is tagged n thread contains new messages (only if collapsed) o thread contains old messages (only if collapsed)
Message recipient flags Flag Description + message is to you and you only T message is to you, but also to or CC'ed to others C message is CC'ed to you F message is from you L message is sent to a subscribed mailing list
The Pager By default, NeoMutt uses its built-in pager to display the contents of messages (an external pager such as less(1)can be configured, see $pager variable). The pager is very similar to the Unix program less(1)though not nearly as featureful. Most common pager keys Key Description <Return> go down one line <Space> display the next page (or next message if at the end of a message) - go back to the previous page n search for next match S skip beyond quoted text T toggle display of quoted text ? show keybindings / regular expression search Esc / backward regular expression search \ toggle highlighting of search matches ^ jump to the top of the message
In addition to key bindings in , many of the functions from the index menu are also available in the pager, such as <delete-message>or <copy-message>(this is one advantage over using an external pager to view messages). Also, the internal pager supports a couple other advanced features. For one, it will accept and translate the standard nroff sequences for bold and underline. These sequences are a series of either the letter, backspace ( ^H), the letter again for bold or the letter, backspace, _ for denoting underline. NeoMutt will attempt to display these in bold and underline respectively if your terminal supports them. If not, you can use the bold and underline color objects to specify a color or mono attribute for them. Additionally, the internal pager supports the ANSI escape sequences for character attributes. NeoMutt translates them into the correct color and character settings. The sequences NeoMutt supports are: \e[ Ps; Ps;.. Ps;m where Ps can be one of the codes shown in . ANSI escape sequences Escape code Description 0 All attributes off 1 Bold on 4 Underline on 5 Blink on 7 Reverse video on 3 <color> Foreground color is <color>(see ) 4 <color> Background color is <color>(see )
Color sequences Color code Color 0 Black 1 Red 2 Green 3 Yellow 4 Blue 5 Magenta 6 Cyan 7 White
NeoMutt uses these attributes for handling text/enriched messages, and they can also be used by an external autoview script for highlighting purposes. If you change the colors for your display, for example by changing the color associated with color2 for your xterm, then that color will be used instead of green. Note that the search commands in the pager take regular expressions, which are not quite the same as the more complex patterns used by the search command in the index. This is because patterns are used to select messages by criteria whereas the pager already displays a selected message.
Threaded Mode So-called threads provide a hierarchy of messages where replies are linked to their parent message(s). This organizational form is extremely useful in mailing lists where different parts of the discussion diverge. NeoMutt displays threads as a tree structure. In NeoMutt, when a mailbox is sorted by threads, there are a few additional functions available in the index and pager modes as shown in . Most common thread mode keys Key Function Description ^D <delete-thread> delete all messages in the current thread ^U <undelete-thread> undelete all messages in the current thread ^N <next-thread> jump to the start of the next thread ^P <previous-thread> jump to the start of the previous thread ^R <read-thread> mark the current thread as read Esc d <delete-subthread> delete all messages in the current subthread Esc u <undelete-subthread> undelete all messages in the current subthread Esc n <next-subthread> jump to the start of the next subthread Esc p <previous-subthread> jump to the start of the previous subthread Esc r <read-subthread> mark the current subthread as read Esc t <tag-thread> toggle the tag on the current thread Esc v <collapse-thread> toggle collapse for the current thread Esc V <collapse-all> toggle collapse for all threads P <parent-message> jump to parent message in thread
Collapsing a thread displays only the first message in the thread and hides the others. This is useful when threads contain so many messages that you can only see a handful of threads on the screen. See %M in $index_format. For example, you could use %?M?(#%03M)&(%4l)?in $index_format to optionally display the number of hidden messages if the thread is collapsed. The %?<char>?<if-part>&<else-part>?syntax is explained in detail in format string conditionals. Technically, every reply should contain a list of its parent messages in the thread tree, but not all do. In these cases, NeoMutt groups them by subject which can be controlled using the $strict_threads variable.
Miscellaneous Functions In addition, the index and pager menus have these interesting functions: <create-alias> (default: a) Creates a new alias based upon the current message (or prompts for a new one). Once editing is complete, an alias command is added to the file specified by the $alias_file variable for future use NeoMutt does not read the $alias_file upon startup so you must explicitly source the file. <check-traditional-pgp> (default: Esc P) This function will search the current message for content signed or encrypted with PGP the traditional way, that is, without proper MIME tagging. Technically, this function will temporarily change the MIME content types of the body parts containing PGP data; this is similar to the <edit-type> function's effect. <edit-raw-message> This command (available in the index and pager) allows you to edit the raw current message as it's present in the mail folder. After you have finished editing, the changed message will be appended to the current folder, and the original message will be marked for deletion; if the message is unchanged it won't be replaced. <edit> is a synonym of this for backwards compatibility. See also <edit-or-view-raw-message> , <view-raw-message> . <edit> Alias of <edit-raw-message> for backwards compatibility. <edit-or-view-raw-message> (default: e) This command (available in the index and pager) is the same as <edit-raw-message> if the mailbox is writable, otherwise it the same as <view-raw-message> . <edit-type> (default: ^E on the attachment menu, and in the pager and index menus; ^T on the compose menu) This command is used to temporarily edit an attachment's content type to fix, for instance, bogus character set parameters. When invoked from the index or from the pager, you'll have the opportunity to edit the top-level attachment's content type. On the attachment menu, you can change any attachment's content type. These changes are not persistent, and get lost upon changing folders. Note that this command is also available on the compose menu. There, it's used to fine-tune the properties of attachments you are going to send. <enter-command> (default: :) This command is used to execute any command you would normally put in a configuration file. A common use is to check the settings of variables, or in conjunction with macros to change settings on the fly. <extract-keys> (default: ^K) This command extracts PGP public keys from the current or tagged message(s) and adds them to your PGP public key ring. <forget-passphrase> (default: ^F) This command wipes the passphrase(s) from memory. It is useful, if you misspelled the passphrase. <list-reply> (default: L) Reply to the current or tagged message(s) by extracting any addresses which match the regular expressions given by the lists or subscribecommands, but also honor any Mail-Followup-To header(s) if the $honor_followup_to configuration variable is set. In addition, the List-Post header field is examined for mailto:URLs specifying a mailing list address. Using this when replying to messages posted to mailing lists helps avoid duplicate copies being sent to the author of the message you are replying to. <pipe-message> (default: |) Asks for an external Unix command and pipes the current or tagged message(s) to it. The variables $pipe_decode, $pipe_split, $pipe_sep and $wait_key control the exact behavior of this function. <resend-message> (default: Esc e) NeoMutt takes the current message as a template for a new message. This function is best described as "recall from arbitrary folders". It can conveniently be used to forward MIME messages while preserving the original mail structure. Note that the amount of headers included here depends on the value of the $weed variable. This function is also available from the attachment menu. You can use this to easily resend a message which was included with a bounce message as a message/rfc822 body part. <shell-escape> (default: !) Asks for an external Unix command and executes it. The $wait_key can be used to control whether NeoMutt will wait for a key to be pressed when the command returns (presumably to let the user read the output of the command), based on the return status of the named command. If no command is given, an interactive shell is executed. <toggle-quoted> (default: T) The pager uses the $quote_regexp variable to detect quoted text when displaying the body of the message. This function toggles the display of the quoted material in the message. It is particularly useful when being interested in just the response and there is a large amount of quoted text in the way. <view-raw-message> This command (available in the index and pager) opens the raw message read-only in an editor. This command does not allow editing the message, use <edit-raw-message> for this. See also <edit-raw-message> , <edit-or-view-raw-message> . <skip-quoted> (default: S) This function will go to the next line of non-quoted text which comes after a line of quoted text in the internal pager.
Sending Mail Introduction The bindings shown in are available in the index and pager to start a new message. Most common mail sending keys Key Function Description m <compose> compose a new message r <reply> reply to sender g <group-reply> reply to all recipients L <list-reply> reply to mailing list address f <forward> forward message b <bounce> bounce (remail) message Esc k <mail-key> mail a PGP public key to someone
Bouncing a message sends the message as-is to the recipient you specify. Forwarding a message allows you to add comments or modify the message you are forwarding. These items are discussed in greater detail in the next section Forwarding and Bouncing Mail. NeoMutt will then enter the compose menu and prompt you for the recipients to place on the To:header field when you hit m to start a new message. Next, it will ask you for the Subject:field for the message, providing a default if you are replying to or forwarding a message. You again have the chance to adjust recipients, subject, and security settings right before actually sending the message. See also $askcc, $askbcc, $autoedit, $bounce, $fast_reply, and $include for changing how and if NeoMutt asks these questions. When replying, NeoMutt fills these fields with proper values depending on the reply type. The types of replying supported are: Simple reply Reply to the author directly. Group reply Reply to the author as well to all recipients except you; this consults alternates . List reply Reply to all mailing list addresses found, either specified via configuration or auto-detected. See for details. After getting recipients for new messages, forwards or replies, NeoMutt will then automatically start your $editor on the message body. If the $edit_headers variable is set, the headers will be at the top of the message in your editor; the message body should start on a new line after the existing blank line at the end of headers. Any messages you are replying to will be added in sort order to the message, with appropriate $attribution, $indent_string and $post_indent_string. When forwarding a message, if the $mime_forward variable is unset, a copy of the forwarded message will be included. If you have specified a $signature, it will be appended to the message. Once you have finished editing the body of your mail message, you are returned to the compose menu providing the functions shown in to modify, send or postpone the message. Most common compose menu keys Key Function Description a <attach-file> attach a file A <attach-message> attach message(s) to the message Esc k <attach-key> attach a PGP public key d <edit-description> edit description on attachment D <detach-file> detach a file t <edit-to> edit the To field Esc f <edit-from> edit the From field r <edit-reply-to> edit the Reply-To field c <edit-cc> edit the Cc field b <edit-bcc> edit the Bcc field y <send-message> send the message s <edit-subject> edit the Subject S <smime-menu> select S/MIME options f <edit-fcc> specify an Fcc mailbox p <pgp-menu> select PGP options P <postpone-message> postpone this message until later q <quit> quit (abort) sending the message w <write-fcc> write the message to a folder i <ispell> check spelling (if available on your system) ^F <forget-passphrase> wipe passphrase(s) from memory
The compose menu is also used to edit the attachments for a message which can be either files or other messages. The <attach-message>function to will prompt you for a folder to attach messages from. You can now tag messages in that folder and they will be attached to the message you are sending. Note that certain operations like composing a new mail, replying, forwarding, etc. are not permitted when you are in that folder. The %r in $status_format will change to a A to indicate that you are in attach-message mode.
Editing the Message Header When editing the header because of $edit_headers being set, there are a several pseudo headers available which will not be included in sent messages but trigger special NeoMutt behavior. Fcc: Pseudo Header If you specify Fcc: filename as a header, NeoMutt will pick up filename just as if you had used the <edit-fcc>function in the compose menu. It can later be changed from the compose menu. Attach: Pseudo Header You can also attach files to your message by specifying Attach: filename[ description] where filename is the file to attach and description is an optional string to use as the description of the attached file. Spaces in filenames have to be escaped using backslash ( \). The file can be removed as well as more added from the compose menu. Pgp: Pseudo Header If you want to use PGP, you can specify Pgp:[ E| S| S <id>] E selects encryption, S selects signing and S<id>selects signing with the given key, setting $pgp_sign_as permanently. The selection can later be changed in the compose menu. In-Reply-To: Header When replying to messages, the In-Reply-To:header contains the Message-Id of the message(s) you reply to. If you remove or modify its value, NeoMutt will not generate a References:field, which allows you to create a new message thread, for example to create a new message to a mailing list without having to enter the mailing list's address. If you intend to start a new thread by replying, please make really sure you remove the In-Reply-To:header in your editor. Otherwise, though you'll produce a technically valid reply, some netiquette guardians will be annoyed by this so-called thread hijacking. Sending Cryptographically Signed/Encrypted Messages If you have told NeoMutt to PGP or S/MIME encrypt a message, it will guide you through a key selection process when you try to send the message. NeoMutt will not ask you any questions about keys which have a certified user ID matching one of the message recipients' mail addresses. However, there may be situations in which there are several keys, weakly certified user ID fields, or where no matching keys can be found. In these cases, you are dropped into a menu with a list of keys from which you can select one. When you quit this menu, or NeoMutt can't find any matching keys, you are prompted for a user ID. You can, as usually, abort this prompt using ^G. When you do so, NeoMutt will return to the compose screen. Once you have successfully finished the key selection, the message will be encrypted using the selected public keys when sent out. Most fields of the entries in the key selection menu (see also $pgp_entry_format) have obvious meanings. But some explanations on the capabilities, flags, and validity fields are in order. The flags sequence ( %f) will expand to one of the flags in . PGP key menu flags Flag Description R The key has been revoked and can't be used. X The key is expired and can't be used. d You have marked the key as disabled. c There are unknown critical self-signature packets.
The capabilities field ( %c) expands to a two-character sequence representing a key's capabilities. The first character gives the key's encryption capabilities: A minus sign ( -) means that the key cannot be used for encryption. A dot ( .) means that it's marked as a signature key in one of the user IDs, but may also be used for encryption. The letter e indicates that this key can be used for encryption. The second character indicates the key's signing capabilities. Once again, a -implies not for signing, .implies that the key is marked as an encryption key in one of the user-ids, and s denotes a key which can be used for signing. Finally, the validity field ( %t) indicates how well-certified a user-id is. A question mark ( ?) indicates undefined validity, a minus character ( -) marks an untrusted association, a space character means a partially trusted association, and a plus character ( +) indicates complete validity.
Sending Format=Flowed Messages Concept format=flowed-style messages (or f=f for short) are text/plain messages that consist of paragraphs which a receiver's mail client may reformat to its own needs which mostly means to customize line lengths regardless of what the sender sent. Technically this is achieved by letting lines of a flowable paragraph end in spaces except for the last line. While for text-mode clients like NeoMutt it's the best way to assume only a standard 80x25 character cell terminal, it may be desired to let the receiver decide completely how to view a message. NeoMutt Support NeoMutt only supports setting the required format=flowed MIME parameter on outgoing messages if the $text_flowed variable is set, specifically it does not add the trailing spaces. After editing the initial message text and before entering the compose menu, NeoMutt properly space-stuffs the message. Space-stuffing is required by RFC3676 defining format=flowed and means to prepend a space to: all lines starting with a space lines starting with the word From followed by space all lines starting with > which is not intended to be a quote character NeoMutt only supports space-stuffing for the first two types of lines but not for the third: It is impossible to safely detect whether a leading >character starts a quote or not. Furthermore, NeoMutt only applies space-stuffing once after the initial edit is finished. All leading spaces are to be removed by receiving clients to restore the original message prior to further processing. Editor Considerations As NeoMutt provides no additional features to compose f=f messages, it's completely up to the user and his editor to produce proper messages. Please consider your editor's documentation if you intend to send f=f messages. Please note that when editing messages from the compose menu several times before really sending a mail, it's up to the user to ensure that the message is properly space-stuffed. For example, vim provides the w flag for its formatoptions setting to assist in creating f=f messages, see :help fo-table for details. Reformatting NeoMutt has some support for reformatting when viewing and replying to format=flowed messages. In order to take advantage of these, $reflow_text must be set. Paragraphs are automatically reflowed and wrapped at a width specified by $reflow_wrap. In its original format, the quoting style of format=flowed messages can be difficult to read, and doesn't intermix well with non-flowed replies. Setting $reflow_space_quotes adds spaces after each level of quoting when in the pager and replying in a non-flowed format (i.e. with $text_flowed unset). If $reflow_space_quotes is unset, NeoMutt will still add one trailing space after all the quotes in the pager (but not when replying).
Forwarding and Bouncing Mail Bouncing and forwarding let you send an existing message to recipients that you specify. Bouncing a message sends a verbatim copy of a message to alternative addresses as if they were the message's original recipients specified in the Bcc header. Forwarding a message, on the other hand, allows you to modify the message before it is resent (for example, by adding your own comments). Bouncing is done using the <bounce>function and forwarding using the <forward>function bound to b and f respectively. Forwarding can be done by including the original message in the new message's body (surrounded by indicating lines) or including it as a MIME attachment, depending on the value of the $mime_forward variable. Decoding of attachments, like in the pager, can be controlled by the $forward_decode and $mime_forward_decode variables, respectively. The desired forwarding format may depend on the content, therefore $mime_forward is a quadoption which, for example, can be set to ask-no. The inclusion of headers is controlled by the current setting of the $weed variable, unless $mime_forward is set. By default a forwarded message does not reference the messages it contains. When $forward_references is set, a forwarded message includes the In-Reply-To:and References:headers, just like a reply would. Hence the forwarded message becomes part of the original thread instead of starting a new one. Editing the message to forward follows the same procedure as sending or replying to a message does. Postponing Mail At times it is desirable to delay sending a message that you have already begun to compose. When the <postpone-message>function is used in the compose menu, the body of your message and attachments are stored in the mailbox specified by the $postponed variable. This means that you can recall the message even if you exit NeoMutt and then restart it at a later time. Once a message is postponed, there are several ways to resume it. From the command line you can use the -p option, or if you compose a new message from the index or pager you will be prompted if postponed messages exist. If multiple messages are currently postponed, the postponed menu will pop up and you can select which message you would like to resume. If you postpone a reply to a message, the reply setting of the message is only updated when you actually finish the message and send it. Also, you must be in the same folder with the message you replied to for the status of the message to be updated. See also the $postpone quad-option.
Configuration Location of Initialization Files When NeoMutt starts up it looks for two configuration files -- one system file and one user file. NeoMutt searches for several different file names when looking for config. It looks for NeoMutt config files before Mutt config files and versioned config before plain config. For example: NeoMutt config file search order neomuttrc muttrc
This allows the user to create separate NeoMutt and Mutt config files on the same system. Location of system config files NeoMutt will search for a system config file in a neomutt directory in several places. First it searches the locations specified in the XDG_CONFIG_DIRS environment variable, which defaults to /etc/xdg. Next, it looks in /etc. Finally, it tries /usr/share. The system config file will not be read if the -n option is used on the command line. NeoMutt will read just one file, the first file it finds, from the list below. NeoMutt system config file locations File Location Notes /etc/xdg/neomutt/neomuttrc /etc/xdg/neomutt/Muttrc Note the case of the filename /etc/neomuttrc /etc/Muttrc Note the case of the filename /usr/share/neomutt/neomuttrc /usr/share/neomutt/Muttrc Note the case of the filename
Location of user config files NeoMutt will search for a user config file in several places. First it looks in the directory specified in the XDG_CONFIG_HOME environment variable, which defaults to ~/.config/neomutt. Next, it looks in ~ (your home directory). Finally, it tries ~/.neomutt. You may specify your own location for the user config file using the -F option on the command line. NeoMutt will read just one file, the first file it finds, from the list below. NeoMutt user config file locations File Location ~/.config/neomutt/neomuttrc ~/.config/neomutt/muttrc ~/.config/mutt/neomuttrc ~/.config/mutt/muttrc ~/.neomutt/neomuttrc ~/.neomutt/muttrc ~/.mutt/neomuttrc ~/.mutt/muttrc ~/.neomuttrc ~/.muttrc
Syntax of Initialization Files An initialization file consists of a series of commands. Each line of the file may contain one or more commands. When multiple commands are used, they must be separated by a semicolon ( ;). Multiple configuration commands per line set realname='John Smith' ; ignore x- The hash mark, or pound sign ( #), is used as a comment character. You can use it to annotate your initialization file. All text after the comment character to the end of the line is ignored. Commenting configuration files my_hdr X-Disclaimer: Why are you listening to me? # This is a comment Single quotes ( ') and double quotes ( ") can be used to quote strings which contain spaces or other special characters. The difference between the two types of quotes is similar to that of many popular shell programs, namely that a single quote is used to specify a literal string (one that is not interpreted for shell variables or quoting with a backslash [see next paragraph]), while double quotes indicate a string for which should be evaluated. For example, backticks are evaluated inside of double quotes, but not for single quotes. \quotes the next character, just as in shells such as bash and zsh. For example, if want to put quotes "inside of a string, you can use \to force the next character to be a literal instead of interpreted character. Escaping quotes in configuration files set realname="Michael \"MuttDude\" Elkins" \\means to insert a literal \into the line. \n and \r have their usual C meanings of linefeed and carriage-return, respectively. A \at the end of a line can be used to split commands over multiple lines as it escapes the line end, provided that the split points don't appear in the middle of command names. Lines are first concatenated before interpretation so that a multi-line can be commented by commenting out the first line only. Splitting long configuration commands over several lines set status_format="some very \ long value split \ over several lines" It is also possible to substitute the output of a Unix command in an initialization file. This is accomplished by enclosing the command in backticks (``). In , the output of the Unix command uname -a will be substituted before the line is parsed. Since initialization files are line oriented, only the first line of output from the Unix command will be substituted. Using external command's output in configuration files my_hdr X-Operating-System: `uname -a` Both environment variables and NeoMutt variables can be accessed by prepending $to the name of the variable. For example, Using environment variables in configuration files set record=+sent_on_$HOSTNAME will cause NeoMutt to save outgoing messages to a folder named sent_on_kremvax if the environment variable $HOSTNAME is set to kremvax.(See $record for details.) NeoMutt expands the variable when it is assigned, not when it is used. If the value of a variable on the right-hand side of an assignment changes after the assignment, the variable on the left-hand side will not be affected. The commands understood by NeoMutt are explained in the next paragraphs. For a complete list, see the command reference. All configuration files are expected to be in the current locale as specified by the $charset variable which doesn't have a default value since it's determined by NeoMutt at startup. If a configuration file is not encoded in the same character set the $config_charset variable should be used: all lines starting with the next are recoded from $config_charset to $charset. This mechanism should be avoided if possible as it has the following implications: These variables should be set early in a configuration file with $charset preceding $config_charset so NeoMutt knows what character set to convert to. If $config_charset is set, it should be set in each configuration file because the value is global and not per configuration file. Because NeoMutt first recodes a line before it attempts to parse it, a conversion introducing question marks or other characters as part of errors (unconvertable characters, transliteration) may introduce syntax errors or silently change the meaning of certain tokens (e.g. inserting question marks into regular expressions). Address Groups Usage: group name expr expr ungroup name * expr expr NeoMutt supports grouping addresses logically into named groups. An address or address pattern can appear in several groups at the same time. These groups can be used in patterns(for searching, limiting and tagging) and in hooks by using group patterns. This can be useful to classify mail and take certain actions depending on in what groups the message is. For example, the NeoMutt user's mailing list would fit into the categories mailing list and NeoMutt-related. Using send-hook , the sender can be set to a dedicated one for writing mailing list messages, and the signature could be set to a NeoMutt-related one for writing to a NeoMutt list — for other lists, the list sender setting still applies but a different signature can be selected. Or, given a group only containing recipients known to accept encrypted mail, auto-encryption can be achieved easily. The group command is used to directly add either addresses or regular expressions to the specified group or groups. The different categories of arguments to the group command can be in any order. The flags -rx and -addr specify what the following strings (that cannot begin with a hyphen) should be interpreted as: either a regular expression or an email address, respectively. These address groups can also be created implicitly by the alias , lists , subscribe and alternates commands by specifying the optional -group option. For example, alternates -group me address1 address2 alternates -group me -group work address3 would create a group named me which contains all your addresses and a group named work which contains only your work address address3. Besides many other possibilities, this could be used to automatically mark your own messages in a mailing list folder as read or use a special signature for work-related messages. The ungroup command is used to remove addresses or regular expressions from the specified group or groups. The syntax is similar to the group command, however the special character *can be used to empty a group of all of its contents. As soon as a group gets empty because all addresses and regular expressions have been removed, it'll internally be removed, too (i.e. there cannot be an empty group). When removing regular expressions from a group, the pattern must be specified exactly as given to the group command or -group argument. Defining/Using Aliases Usage: alias name key address address unalias name * key It's usually very cumbersome to remember or type out the address of someone you are communicating with. NeoMutt allows you to create aliases which map a short string to a full address. If you want to create an alias for more than one address, you must separate the addresses with a comma ( ,). The optional -group argument to alias causes the aliased address(es) to be added to the named group. To remove an alias or aliases ( *means all aliases): alias muttdude me@cs.hmc.edu (Michael Elkins) alias theguys manny, moe, jack Unlike other mailers, NeoMutt doesn't require aliases to be defined in a special file. The alias command can appear anywhere in a configuration file, as long as this file is sourced. Consequently, you can have multiple alias files, or you can have all aliases defined in your .neomuttrc. On the other hand, the <create-alias> function can use only one file, the one pointed to by the $alias_file variable (which is ~/.neomuttrc by default). This file is not special either, in the sense that NeoMutt will happily append aliases to any file, but in order for the new aliases to take effect you need to explicitly source this file too. Configuring external alias files source /usr/local/share/NeoMutt.aliases source ~/.mail_aliases set alias_file=~/.mail_aliases To use aliases, you merely use the alias at any place in NeoMutt where NeoMutt prompts for addresses, such as the To:or Cc:prompt. You can also enter aliases in your editor at the appropriate headers if you have the $edit_headers variable set. In addition, at the various address prompts, you can use the tab character to expand a partial alias to the full alias. If there are multiple matches, NeoMutt will bring up a menu with the matching aliases. In order to be presented with the full list of aliases, you must hit tab without a partial alias, such as at the beginning of the prompt or after a comma denoting multiple addresses. In the alias menu, you can select as many aliases as you want with the select-entry key (default: <Return>), and use the exit key (default: q) to return to the address prompt. Changing the Default Key Bindings Usage: bind map key function This command allows you to change the default key bindings (operation invoked when pressing a key). map specifies in which menu the binding belongs. Multiple maps may be specified by separating them with commas (no additional whitespace is allowed). The currently defined maps are: generic This is not a real menu, but is used as a fallback for all of the other menus except for the pager and editor modes. If a key is not defined in another menu, NeoMutt will look for a binding to use in this menu. This allows you to bind a key to a certain function in multiple menus instead of having multiple bind statements to accomplish the same task. alias The alias menu is the list of your personal aliases as defined in your .neomuttrc. It is the mapping from a short alias name to the full email address(es) of the recipient(s). attach The attachment menu is used to access the attachments on received messages. browser The browser is used for both browsing the local directory structure, and for listing all of your incoming mailboxes. editor The editor is used to allow the user to enter a single line of text, such as the To or Subject prompts in the compose menu. index The index is the list of messages contained in a mailbox. compose The compose menu is the screen used when sending a new message. pager The pager is the mode used to display message/attachment data, and help listings. pgp The pgp menu is used to select the OpenPGP keys used to encrypt outgoing messages. smime The smime menu is used to select the OpenSSL certificates used to encrypt outgoing messages. postpone The postpone menu is similar to the index menu, except is used when recalling a message the user was composing, but saved until later. query The query menu is the browser for results returned by $query_command. mix The mixmaster screen is used to select remailer options for outgoing messages (if NeoMutt is compiled with Mixmaster support). key is the key (or key sequence) you wish to bind. To specify a control character, use the sequence \Cx, where x is the letter of the control character (for example, to specify control-A use \Ca). Note that the case of x as well as \C is ignored, so that \CA, \Ca, \cA and \ca are all equivalent. An alternative form is to specify the key as a three digit octal number prefixed with a \(for example \177 is equivalent to \c?). In addition, key may be a symbolic name as shown in . Symbolic key names Symbolic name Meaning \t tab <tab> tab <backtab> backtab / shift-tab \r carriage return \n newline \e escape/alt <esc> escape/alt <up> up arrow <down> down arrow <left> left arrow <right> right arrow <pageup> Page Up <pagedown> Page Down <backspace> Backspace <delete> Delete <insert> Insert <enter> Enter <return> Return <home> Home <end> End <space> Space bar <f1> function key 1 <f10> function key 10
The <what-key>function can be used to explore keycode and symbolic names for other keys on your keyboard. Executing this function will display information about each key pressed, until terminated by ^G. key does not need to be enclosed in quotes unless it contains a space (  ) or semi-colon ( ;). function specifies which action to take when key is pressed. For a complete list of functions, see the reference. Note that the bind expects function to be specified without angle brackets. The special function <noop>unbinds the specified key sequence. Warnings about Duplicated Bindings Due to a limitation of NeoMutt, creating key bindings, or macros, will overwrite existing mappings with similar, shorter, names. bind index g group-reply bind index gg first-entry In this example, the g binding will be overwritten and cannot be used. Newer versions of NeoMutt will warn the user about this. To avoid warnings on startup, first set the shorter binding to noop (no operation). bind index g noop bind index gg first-entry
Defining Aliases for Character Sets Usage: charset-hook alias charset iconv-hook charset local-charset The charset-hook command defines an alias for a character set. This is useful to properly display messages which are tagged with a character set name not known to NeoMutt. The iconv-hook command defines a system-specific name for a character set. This is helpful when your systems character conversion library insists on using strange, system-specific names for character sets. Setting Variables Based Upon Mailbox Usage: folder-hook [!]regex command It is often desirable to change settings based on which mailbox you are reading. The folder-hook command provides a method by which you can execute any configuration command. regex is a regular expression specifying in which mailboxes to execute command before loading. If a mailbox matches multiple folder-hooks, they are executed in the order given in the .neomuttrc. The regex parameter has mailbox shortcut expansion performed on the first character. See for more details. If you use the !shortcut for $spoolfile at the beginning of the pattern, you must place it inside of double or single quotes in order to distinguish it from the logical not operator for the expression. Settings are not restored when you leave the mailbox. For example, a command action to perform is to change the sorting method based upon the mailbox being read: folder-hook work "set sort=threads" However, the sorting method is not restored to its previous value when reading a different mailbox. To specify a default command, use the pattern .before other folder-hooks adjusting a value on a per-folder basis because folder-hooks are evaluated in the order given in the configuration file. The keyboard buffer will not be processed until after all hooks are run; multiple push or exec commands will end up being processed in reverse order. The following example will set the sort variable to date-sent for all folders but to threads for all folders containing work in their name. Setting sort method based on mailbox name folder-hook . "set sort=date-sent" folder-hook work "set sort=threads" Keyboard Macros Usage: macro menu key sequence description Macros are useful when you would like a single key to perform a series of actions. When you press key in menu menu, NeoMutt will behave as if you had typed sequence. So if you have a common sequence of commands you type, you can create a macro to execute those commands with a single key or fewer keys. menu is the map which the macro will be bound in. Multiple maps may be specified by separating multiple menu arguments by commas. Whitespace may not be used in between the menu arguments and the commas separating them. key and sequence are expanded by the same rules as the key bindings with some additions. The first is that control characters in sequence can also be specified as ^x. In order to get a caret ( ^) you need to use ^^. Secondly, to specify a certain key such as up or to invoke a function directly, you can use the format <key name>and <function name>. For a listing of key names see the section on key bindings. Functions are listed in the reference. The advantage with using function names directly is that the macros will work regardless of the current key bindings, so they are not dependent on the user having particular key definitions. This makes them more robust and portable, and also facilitates defining of macros in files used by more than one user (e.g., the system neomuttrc). Optionally you can specify a descriptive text after sequence, which is shown in the help screens if they contain a description. Macro definitions (if any) listed in the help screen(s), are silently truncated at the screen width, and are not wrapped. Using Color and Mono Video Attributes Usage: color object foreground background color foreground background regex color foreground background pattern color composeobject foreground background uncolor * pattern If your terminal supports color, you can spice up NeoMutt by creating your own color scheme. To define the color of an object (type of information), you must specify both a foreground color and a background color (it is not possible to only specify one or the other). header and body match regex in the header/body of a message, index-object can match pattern(see ) in the message index. Note that IMAP server-side searches (=b, =B, =h) are not supported for color index patterns. When $header_color_partial is unset (the default), a header matched by regex will have color applied to the entire header. When set, color is applied only to the exact text matched by regex. object can be one of: attachment bold (highlighting bold patterns in the body of messages) error (error messages printed by NeoMutt) hdrdefault (default color of the message header in the pager) index_author (color of the author name in the index, uses pattern) index_collapsed (the number of messages in a collapsed thread in the index) index_date (color of the date field in the index) index_flags (color of the message flags in the index) index_label (color of the message label in the index) index_number (color of the message number in the index) index_size (color of the message size and line number in the index) index_subject (color of the subject in the index, uses pattern) indicator (arrow or bar used to indicate the current item in a menu) markers (the +markers at the beginning of wrapped lines in the pager) message (informational messages) normal progress(visual progress bar) prompt quoted (text matching $quote_regexp in the body of a message) quoted1, quoted2, ..., quoted N(higher levels of quoting) search (highlighting of words in the pager) signature status (mode lines used to display info about the mailbox or message) tilde (the ~used to pad blank lines in the pager) tree (thread tree drawn in the message index and attachment menu) underline (highlighting underlined patterns in the body of messages) composeobject can be one of: header security_encrypt security_sign security_both security_none index-object can be one of the following: index (default highlighting of the entire index line, uses pattern) index_date (the date field) index_flags (the message flags, %S %Z, uses pattern) index_number (the message number, %C) index_collapsed (the number of messages in a collapsed thread, %M) index_author (the author name, %A %a %F %L %n, uses pattern) index_subject (the subject, %s, uses pattern) index_size (the message size, %c %l) index_label (the message label, %y %Y) index_tags (the transformed message tags, %g) index_tag (an individual message tag, %G, uses pattern / tag name) foreground and background can be one of the following: white black green magenta blue cyan yellow red default color x foreground can optionally be prefixed with the keyword bright to make the foreground color boldfaced (e.g., brightred). If your terminal supports it, the special keyword default can be used as a transparent color. The value brightdefault is also valid. If NeoMutt is linked against the S-Lang library, you also need to set the $COLORFGBG environment variable to the default colors of your terminal for this to work; for example (for Bourne-like shells): set COLORFGBG="green;black" export COLORFGBG The S-Lang library requires you to use the lightgray and brown keywords instead of white and yellow when setting this variable. The uncolor command can be applied to the index, header and body objects only. It removes entries from the list. You must specify the same pattern specified in the color command for it to be removed. The pattern *is a special token which means to clear the color list of all entries. NeoMutt also recognizes the keywords color0, color1, ..., color N-1( N being the number of colors supported by your terminal). This is useful when you remap the colors for your display (for example by changing the color associated with color2 for your xterm), since color names may then lose their normal meaning. If your terminal does not support color, it is still possible change the video attributes through the use of the mono command. Usage: mono object attribute mono attribute regex mono attribute pattern unmono * pattern For object, see the color command. attribute can be one of the following: none bold underline reverse standout Message Header Display Header Display When displaying a message in the pager, NeoMutt folds long header lines at $wrap columns. Though there're precise rules about where to break and how, NeoMutt always folds headers using a tab for readability. (Note that the sending side is not affected by this, NeoMutt tries to implement standards compliant folding.) Selecting Headers Usage: ignore pattern pattern unignore * pattern Messages often have many header fields added by automatic processing systems, or which may not seem useful to display on the screen. This command allows you to specify header fields which you don't normally want to see in the pager. You do not need to specify the full header field name. For example, ignore content-will ignore all header fields that begin with the pattern content-. ignore *will ignore all headers. To remove a previously added token from the list, use the unignore command. The unignore command will make NeoMutt display headers with the given pattern. For example, if you do ignore x-it is possible to unignore x-mailer. unignore *will remove all tokens from the ignore list. Header weeding # Sven's draconian header weeding ignore * unignore from date subject to cc unignore organization organisation x-mailer: x-newsreader: x-mailing-list: unignore posted-to: Ordering Displayed Headers Usage: hdr_order header header unhdr_order * header With the hdr_order command you can specify an order in which NeoMutt will attempt to present these headers to you when viewing messages. unhdr_order*will clear all previous headers from the order list, thus removing the header order effects set by the system-wide startup file. Configuring header display order hdr_order From Date: From: To: Cc: Subject: Alternative Addresses Usage: alternates name regex regex unalternates name * regex With various functions, NeoMutt will treat messages differently, depending on whether you sent them or whether you received them from someone else. For instance, when replying to a message that you sent to a different party, NeoMutt will automatically suggest to send the response to the original message's recipients — responding to yourself won't make much sense in many cases. (See $reply_to.) Many users receive e-mail under a number of different addresses. To fully use NeoMutt's features here, the program must be able to recognize what e-mail addresses you receive mail under. That's the purpose of the alternates command: It takes a list of regular expressions, each of which can identify an address under which you receive e-mail. As addresses are matched using regular expressions and not exact strict comparisons, you should make sure you specify your addresses as precise as possible to avoid mismatches. For example, if you specify: alternates user@example NeoMutt will consider some-user@example as being your address, too which may not be desired. As a solution, in such cases addresses should be specified as: alternates '^user@example$' The -group flag causes all of the subsequent regular expressions to be added to the named group. The unalternates command can be used to write exceptions to alternates patterns. If an address matches something in an alternates command, but you nonetheless do not think it is from you, you can list a more precise pattern under an unalternates command. To remove a regular expression from the alternates list, use the unalternates command with exactly the same regex. Likewise, if the regex for an alternates command matches an entry on the unalternates list, that unalternates entry will be removed. If the regex for unalternates is *, all entries on alternates will be removed. Mailing Lists Usage: lists name regex regex unlists * regex subscribe name regex regex unsubscribe * regex NeoMutt has a few nice features for handling mailing lists. In order to take advantage of them, you must specify which addresses belong to mailing lists, and which mailing lists you are subscribed to. NeoMutt also has limited support for auto-detecting mailing lists: it supports parsing mailto:links in the common List-Post:header which has the same effect as specifying the list address via the lists command (except the group feature). Once you have done this, the <list-reply> function will work for all known lists. Additionally, when you send a message to a subscribed list, NeoMutt will add a Mail-Followup-To header to tell other users' mail user agents not to send copies of replies to your personal address. The Mail-Followup-To header is a non-standard extension which is not supported by all mail user agents. Adding it is not bullet-proof against receiving personal CCs of list messages. Also note that the generation of the Mail-Followup-To header is controlled by the $followup_to configuration variable since it's common practice on some mailing lists to send Cc upon replies (which is more a group- than a list-reply). More precisely, NeoMutt maintains lists of patterns for the addresses of known and subscribed mailing lists. Every subscribed mailing list is known. To mark a mailing list as known, use the list command. To mark it as subscribed, use subscribe. You can use regular expressions with both commands. To mark all messages sent to a specific bug report's address on Debian's bug tracking system as list mail, for instance, you could say subscribe [0-9]+.*@bugs.debian.org as it's often sufficient to just give a portion of the list's e-mail address. Specify as much of the address as you need to to remove ambiguity. For example, if you've subscribed to the NeoMutt mailing list, you will receive mail addressed to neomutt-users@neomutt.org. So, to tell NeoMutt that this is a mailing list, you could add lists neomutt-users@to your initialization file. To tell NeoMutt that you are subscribed to it, add subscribe neomutt-users to your initialization file instead. If you also happen to get mail from someone whose address is neomutt-users@example.com, you could use lists ^neomutt-users@neomutt\\.org$or subscribe ^neomutt-users@neomutt\\.org$to match only mail from the actual list. The -group flag adds all of the subsequent regular expressions to the named address group in addition to adding to the specified address list. The unlists command is used to remove a token from the list of known and subscribed mailing-lists. Use unlists *to remove all tokens. To remove a mailing list from the list of subscribed mailing lists, but keep it on the list of known mailing lists, use unsubscribe. Using Multiple Spool Mailboxes Usage: mbox-hook [!]regex mailbox This command is used to move read messages from a specified mailbox to a different mailbox automatically when you quit or change folders. regex is a regular expression specifying the mailbox to treat as a spool mailbox and mailbox specifies where mail should be saved when read. The regex parameter has mailbox shortcut expansion performed on the first character. See for more details. Note that execution of mbox-hooks is dependent on the $move configuration variable. If set to no(the default), mbox-hooks will not be executed. Unlike some of the other hook commands, only the first matching regex is used (it is not possible to save read mail in more than a single mailbox). Monitoring Incoming Mail Usage: mailboxes mailbox mailbox unmailboxes * mailbox This command specifies folders which can receive mail and which will be checked for new messages periodically. folder can either be a local file or directory (Mbox/Mmdf or Maildir/Mh). If NeoMutt was built with POP and/or IMAP support, folder can also be a POP/IMAP folder URL. The URL syntax is described in , POP and IMAP are described in and respectively. NeoMutt provides a number of advanced features for handling (possibly many) folders and new mail within them, please refer to for details (including in what situations and how often NeoMutt checks for new mail). The unmailboxes command is used to remove a token from the list of folders which receive mail. Use unmailboxes *to remove all tokens. The folders in the mailboxes command are resolved when the command is executed, so if these names contain shortcut characters(such as =and !), any variable definition that affects these characters (like $folder and $spoolfile) should be set before the mailboxes command. If none of these shortcuts are used, a local path should be absolute as otherwise NeoMutt tries to find it relative to the directory from where NeoMutt was started which may not always be desired. User-Defined Headers Usage: my_hdr string unmy_hdr * field The my_hdr command allows you to create your own header fields which will be added to every message you send and appear in the editor if $edit_headers is set. For example, if you would like to add an Organization:header field to all of your outgoing messages, you can put the command something like shown in in your .neomuttrc. Defining custom headers my_hdr Organization: A Really Big Company, Anytown, USA Space characters are not allowed between the keyword and the colon ( :). The standard for electronic mail (RFC2822) says that space is illegal there, so NeoMutt enforces the rule. If you would like to add a header field to a single message, you should either set the $edit_headers variable, or use the <edit-headers>function (default: E) in the compose menu so that you can edit the header of your message along with the body. To remove user defined header fields, use the unmy_hdr command. You may specify an asterisk ( *) to remove all header fields, or the fields to remove. For example, to remove all To and Cc header fields, you could use: unmy_hdr to cc Specify Default Save Mailbox Usage: save-hook [!]pattern mailbox This command is used to override the default mailbox used when saving messages. mailbox will be used as the default if the message matches pattern, see for information on the exact format. To provide more flexibility and good defaults, NeoMutt applies the expandos of $index_format to mailbox after it was expanded. Using %-expandos in <command>save-hook</command> # default: save all to ~/Mail/<author name> save-hook . ~/Mail/%F # save from me@turing.cs.hmc.edu and me@cs.hmc.edu to $folder/elkins save-hook me@(turing\\.)?cs\\.hmc\\.edu$ +elkins # save from aol.com to $folder/spam save-hook aol\\.com$ +spam Also see the fcc-save-hook command. Specify Default Fcc: Mailbox When Composing Usage: fcc-hook [!]pattern mailbox This command is used to save outgoing mail in a mailbox other than $record. NeoMutt searches the initial list of message recipients for the first matching pattern and uses mailbox as the default Fcc: mailbox. If no match is found the message will be saved to $record mailbox. To provide more flexibility and good defaults, NeoMutt applies the expandos of $index_format to mailbox after it was expanded. See for information on the exact format of pattern. fcc-hook [@.]aol\\.com$ +spammers ...will save a copy of all messages going to the aol.com domain to the `+spammers' mailbox by default. Also see the fcc-save-hook command. Specify Default Save Filename and Default Fcc: Mailbox at Once Usage: fcc-save-hook [!]pattern mailbox This command is a shortcut, equivalent to doing both a fcc-hook and a save-hook with its arguments, including %-expansion on mailbox according to $index_format. Change Settings Based Upon Message Recipients Usage: reply-hook [!]pattern command send-hook [!]pattern command send2-hook [!]pattern command These commands can be used to execute arbitrary configuration commands based upon recipients of the message. pattern is used to match the message, see for details. command is executed when pattern matches. reply-hook is matched against the message you are replying to, instead of the message you are sending. send-hook is matched against all messages, both new and replies. reply-hooks are matched before the send-hook, regardless of the order specified in the user's configuration file. However, you can inhibit send-hook in the reply case by using the pattern '! ~Q'( not replied, see ) in the send-hook to tell when reply-hook have been executed. send2-hook is matched every time a message is changed, either by editing it, or by using the compose menu to change its recipients or subject. send2-hook is executed after send-hook, and can, e.g., be used to set parameters such as the $sendmail variable depending on the message's sender address. For each type of send-hook or reply-hook, when multiple matches occur, commands are executed in the order they are specified in the .neomuttrc(for that type of hook). Example: send-hook work " set mime_forward signature=''" Another typical use for this command is to change the values of the $attribution, $attribution_locale, and $signature variables in order to change the language of the attributions and signatures based upon the recipients. send-hook's are only executed once after getting the initial list of recipients. Adding a recipient after replying or editing the message will not cause any send-hook to be executed, similarly if $autoedit is set (as then the initial list of recipients is empty). Also note that my_hdr commands which modify recipient headers, or the message's subject, don't have any effect on the current message when executed from a send-hook. Change Settings Before Formatting a Message Usage: message-hook [!]pattern command This command can be used to execute arbitrary configuration commands before viewing or formatting a message based upon information about the message. command is executed if the pattern matches the message to be displayed. When multiple matches occur, commands are executed in the order they are specified in the .neomuttrc. See for information on the exact format of pattern. Example: message-hook ~A 'set pager=builtin' message-hook '~f freshmeat-news' 'set pager="less \"+/^ subject: .*\""' Choosing the Cryptographic Key of the Recipient Usage: crypt-hook regex keyid When encrypting messages with PGP/GnuPG or OpenSSL, you may want to associate a certain key with a given e-mail address automatically, either because the recipient's public key can't be deduced from the destination address, or because, for some reasons, you need to override the key NeoMutt would normally use. The crypt-hook command provides a method by which you can specify the ID of the public key to be used when encrypting messages to a certain recipient. You may use multiple crypt-hooks with the same regex; multiple matching crypt-hooks result in the use of multiple keyids for a recipient. During key selection, NeoMutt will confirm whether each crypt-hook is to be used (unless the $crypt_confirmhook option is unset). If all crypt-hooks for a recipient are declined, NeoMutt will use the original recipient address for key selection instead. The meaning of keyid is to be taken broadly in this context: You can either put a numerical key ID or fingerprint here, an e-mail address, or even just a real name. Adding Key Sequences to the Keyboard Buffer Usage: push string This command adds the named string to the beginning of the keyboard buffer. The string may contain control characters, key names and function names like the sequence string in the macro command. You may use it to automatically run a sequence of commands at startup, or when entering certain folders. For example, shows how to automatically collapse all threads when entering a folder. Embedding <command>push</command> in <command>folder-hook</command> folder-hook . 'push <collapse-all>' For using functions like shown in the example, it's important to use angle brackets ( <and >) to make NeoMutt recognize the input as a function name. Otherwise it will simulate individual just keystrokes, i.e. push collapse-all would be interpreted as if you had typed c, followed by o, followed by l, ..., which is not desired and may lead to very unexpected behavior. Keystrokes can be used, too, but are less portable because of potentially changed key bindings. With default bindings, this is equivalent to the above example: folder-hook . 'push \eV' because it simulates that Esc+V was pressed (which is the default binding of <collapse-all>). Executing Functions Usage: exec function function This command can be used to execute any function. Functions are listed in the function reference. exec function is equivalent to push <function> . Message Scoring Usage: score pattern value unscore * pattern The score commands adds value to a message's score if pattern matches it. pattern is a string in the format described in the patterns section (note: For efficiency reasons, patterns which scan information not available in the index, such as ~b, ~B, ~h, or ~X may not be used). value is a positive or negative integer. A message's final score is the sum total of all matching score entries. However, you may optionally prefix value with an equal sign ( =) to cause evaluation to stop at a particular entry if there is a match. Negative final scores are rounded up to 0. The unscore command removes score entries from the list. You must specify the same pattern specified in the score command for it to be removed. The pattern *is a special token which means to clear the list of all score entries. Scoring occurs as the messages are read in, before the mailbox is sorted. Because of this, patterns which depend on threading, such as ~=, ~$, and ~(), will not work by default. A workaround is to push the scoring command in a folder hook. This will cause the mailbox to be rescored after it is opened and input starts being processed: folder-hook . 'push "<enter-command>score ~= 10<enter>"' Spam Detection Usage: spam pattern format nospam * pattern NeoMutt has generalized support for external spam-scoring filters. By defining your spam patterns with the spam and nospam commands, you can limit, search, and sort your mail based on its spam attributes, as determined by the external filter. You also can display the spam attributes in your index display using the %H selector in the $index_format variable. (Tip: try %?H?[%H] ?to display spam tags only when they are defined for a given message.) Your first step is to define your external filter's spam patterns using the spam command. pattern should be a regular expression that matches a header in a mail message. If any message in the mailbox matches this regular expression, it will receive a spam tag or spam attribute(unless it also matches a nospam pattern — see below.) The appearance of this attribute is entirely up to you, and is governed by the format parameter. format can be any static text, but it also can include back-references from the pattern expression. (A regular expression back-reference refers to a sub-expression contained within parentheses.) %1 is replaced with the first back-reference in the regex, %2 with the second, etc. To match spam tags, NeoMutt needs the corresponding header information which is always the case for local and POP folders but not for IMAP in the default configuration. Depending on the spam header to be analyzed, $imap_headers may need to be adjusted. If you're using multiple spam filters, a message can have more than one spam-related header. You can define spam patterns for each filter you use. If a message matches two or more of these patterns, and the $spam_separator variable is set to a string, then the message's spam tag will consist of all the format strings joined together, with the value of $spam_separator separating them. For example, suppose one uses DCC, SpamAssassin, and PureMessage, then the configuration might look like in . Configuring spam detection spam "X-DCC-.*-Metrics:.*(....)=many" "90+/DCC-%1" spam "X-Spam-Status: Yes" "90+/SA" spam "X-PerlMX-Spam: .*Probability=([0-9]+)%" "%1/PM" set spam_separator=", " If then a message is received that DCC registered with many hits under the Fuz2 checksum, and that PureMessage registered with a 97% probability of being spam, that message's spam tag would read 90+/DCC-Fuz2, 97/PM. (The four characters before =many in a DCC report indicate the checksum used — in this case, Fuz2.) If the $spam_separator variable is unset, then each spam pattern match supersedes the previous one. Instead of getting joined format strings, you'll get only the last one to match. The spam tag is what will be displayed in the index when you use %H in the $index_format variable. It's also the string that the ~H pattern-matching expression matches against for <search>and <limit>functions. And it's what sorting by spam attribute will use as a sort key. That's a pretty complicated example, and most people's actual environments will have only one spam filter. The simpler your configuration, the more effective NeoMutt can be, especially when it comes to sorting. Generally, when you sort by spam tag, NeoMutt will sort lexically— that is, by ordering strings alphanumerically. However, if a spam tag begins with a number, NeoMutt will sort numerically first, and lexically only when two numbers are equal in value. (This is like UNIX's sort -n.) A message with no spam attributes at all — that is, one that didn't match any of your spam patterns — is sorted at lowest priority. Numbers are sorted next, beginning with 0 and ranging upward. Finally, non-numeric strings are sorted, with a taking lower priority than z. Clearly, in general, sorting by spam tags is most effective when you can coerce your filter to give you a raw number. But in case you can't, NeoMutt can still do something useful. The nospam command can be used to write exceptions to spam patterns. If a header pattern matches something in a spam command, but you nonetheless do not want it to receive a spam tag, you can list a more precise pattern under a nospam command. If the pattern given to nospam is exactly the same as the pattern on an existing spam list entry, the effect will be to remove the entry from the spam list, instead of adding an exception. Likewise, if the pattern for a spam command matches an entry on the nospam list, that nospam entry will be removed. If the pattern for nospam is *, all entries on both lists will be removed. This might be the default action if you use spam and nospam in conjunction with a folder-hook. You can have as many spam or nospam commands as you like. You can even do your own primitive spam detection within NeoMutt — for example, if you consider all mail from MAILER-DAEMON to be spam, you can use a spam command like this: spam "^From: .*MAILER-DAEMON" "999" Setting and Querying Variables Variable Types NeoMutt supports these types of configuration variables: boolean A boolean expression, either yes or no. number A signed integer number in the range -32768 to 32767. string Arbitrary text. path A specialized string for representing paths including support for mailbox shortcuts (see ) as well as tilde ( ~) for a user's home directory and more. quadoption Like a boolean but triggers a prompt when set to ask-yes or ask-no with yes and no preselected respectively. sort order A specialized string allowing only particular words as values depending on the variable. regular expression A regular expression, see for an introduction. folder magic Specifies the type of folder to use: mbox, mmdf, mh or maildir. Currently only used to determine the type for newly created folders. e-mail address An e-mail address either with or without realname. The older user@example.org (Joe User) form is supported but strongly deprecated. user-defined Arbitrary text, see for details. Commands The following commands are available to manipulate and query variables: Usage: set variable variable=value toggle variable variable unset variable variable reset variable variable This command is used to set (and unset) configuration variables. There are four basic types of variables: boolean, number, string and quadoption. boolean variables can be set(true) or unset(false). number variables can be assigned a positive integer value. string variables consist of any number of printable characters and must be enclosed in quotes if they contain spaces or tabs. You may also use the escape sequences \n and \t for newline and tab, respectively. quadoption variables are used to control whether or not to be prompted for certain actions, or to specify a default action. A value of yes will cause the action to be carried out automatically as if you had answered yes to the question. Similarly, a value of no will cause the action to be carried out as if you had answered no.A value of ask-yes will cause a prompt with a default answer of yes and ask-no will provide a default answer of no. Prefixing a variable with no will unset it. Example: set noaskbcc. For boolean variables, you may optionally prefix the variable name with inv to toggle the value (on or off). This is useful when writing macros. Example: set invsmart_wrap. The toggle command automatically prepends the inv prefix to all specified variables. The unset command automatically prepends the no prefix to all specified variables. Using the <enter-command>function in the index menu, you can query the value of a variable by prefixing the name of the variable with a question mark: set ?allow_8bit The question mark is actually only required for boolean and quadoption variables. The reset command resets all given variables to the compile time defaults (hopefully mentioned in this manual). If you use the command set and prefix the variable with &this has the same behavior as the reset command. With the reset command there exists the special variable all, which allows you to reset all variables to their system defaults. User-Defined Variables Introduction Along with the variables listed in the Configuration variables section, NeoMutt supports user-defined variables with names starting with my_ as in, for example, my_cfgdir. The set command either creates a custom my_ variable or changes its value if it does exist already. The unset and reset commands remove the variable entirely. Since user-defined variables are expanded in the same way that environment variables are (except for the shell-escape command and backtick expansion), this feature can be used to make configuration files more readable. Examples The following example defines and uses the variable my_cfgdir to abbreviate the calls of the source command: Using user-defined variables for config file readability set my_cfgdir = $HOME/neomutt/config source $my_cfgdir/hooks $my_cfgdir/macros # more source commands... A custom variable can also be used in macros to backup the current value of another variable. In the following example, the value of the $delete is changed temporarily while its original value is saved as my_delete. After the macro has executed all commands, the original value of $delete is restored. Using user-defined variables for backing up other config option values macro pager ,x '\ <enter-command>set my_delete=$delete<enter>\ <enter-command>set delete=yes<enter>\ ...\ <enter-command>set delete=$my_delete<enter>' Since NeoMutt expands such values already when parsing the configuration file(s), the value of $my_delete in the last example would be the value of $delete exactly as it was at that point during parsing the configuration file. If another statement would change the value for $delete later in the same or another file, it would have no effect on $my_delete. However, the expansion can be deferred to runtime, as shown in the next example, when escaping the dollar sign. Deferring user-defined variable expansion to runtime macro pager <PageDown> "\ <enter-command> set my_old_pager_stop=\$pager_stop pager_stop<Enter>\ <next-page>\ <enter-command> set pager_stop=\$my_old_pager_stop<Enter>\ <enter-command> unset my_old_pager_stop<Enter>" Note that there is a space between <enter-command>and the set configuration command, preventing NeoMutt from recording the macro's commands into its history. Type Conversions Variables are always assigned string values which NeoMutt parses into its internal representation according to the type of the variable, for example an integer number for numeric types. For all queries (including $-expansion) the value is converted from its internal type back into string. As a result, any variable can be assigned any value given that its content is valid for the target. This also counts for custom variables which are of type string. In case of parsing errors, NeoMutt will print error messages. demonstrates type conversions. Type conversions using variables set my_lines = "5" # value is string "5" set pager_index_lines = $my_lines # value is integer 5 set my_sort = "date-received" # value is string "date-received" set sort = "last-$my_sort" # value is sort last-date-received set my_inc = $read_inc # value is string "10" (default of $read_inc) set my_foo = $my_inc # value is string "10" These assignments are all valid. If, however, the value of $my_lines would have been five(or something else that cannot be parsed into a number), the assignment to $pager_index_lines would have produced an error message. Type conversion applies to all configuration commands which take arguments. But please note that every expanded value of a variable is considered just a single token. A working example is: set my_pattern = "~A" set my_number = "10" # same as: score ~A +10 score $my_pattern +$my_number What does not work is: set my_mx = "+mailbox1 +mailbox2" mailboxes $my_mx +mailbox3 because the value of $my_mx is interpreted as a single mailbox named +mailbox1 +mailbox2 and not two distinct mailboxes. Reading Initialization Commands From Another File Usage: source file This command allows the inclusion of initialization commands from other files. For example, I place all of my aliases in ~/.mail_aliases so that I can make my ~/.neomuttrc readable and keep my aliases private. If the filename begins with a tilde ( ~), it will be expanded to the path of your home directory. If the filename ends with a vertical bar ( |), then filename is considered to be an executable program from which to read input (e.g. source~/bin/myscript|). Removing Hooks Usage: unhook * hook-type This command permits you to flush hooks you have previously defined. You can either remove all hooks by giving the *character as an argument, or you can remove all hooks of a specific type by saying something like unhook send-hook. Format Strings Basic usage Format strings are a general concept you'll find in several locations through the NeoMutt configuration, especially in the $index_format, $pager_format, $status_format, and other related variables. These can be very straightforward, and it's quite possible you already know how to use them. The most basic format string element is a percent symbol followed by another character. For example, %s represents a message's Subject: header in the $index_format variable. The expandos available are documented with each format variable, but there are general modifiers available with all formatting expandos, too. Those are our concern here. Some of the modifiers are borrowed right out of C (though you might know them from Perl, Python, shell, or another language). These are the [-]m.n modifiers, as in %-12.12s. As with such programming languages, these modifiers allow you to specify the minimum and maximum size of the resulting string, as well as its justification. If the -sign follows the percent, the string will be left-justified instead of right-justified. If there's a number immediately following that, it's the minimum amount of space the formatted string will occupy — if it's naturally smaller than that, it will be padded out with spaces. If a decimal point and another number follow, that's the maximum space allowable — the string will not be permitted to exceed that width, no matter its natural size. Each of these three elements is optional, so that all these are legal format strings: %-12s, %4c, %.15F and %-12.15L. NeoMutt adds some other modifiers to format strings. If you use an equals symbol ( =) as a numeric prefix (like the minus above), it will force the string to be centered within its minimum space range. For example, %=14y will reserve 14 characters for the %y expansion — that's the set of message keywords (formerly X-Label). If the expansion results in a string less than 14 characters, it will be centered in a 14-character space. If the X-Label for a message were test, that expansion would look like      test     . There are two very little-known modifiers that affect the way that an expando is replaced. If there is an underline ( _) character between any format modifiers (as above) and the expando letter, it will expands in all lower case. And if you use a colon ( :), it will replace all decimal points with underlines. Conditionals Depending on the format string variable, some of its sequences can be used to optionally print a string if their value is nonzero. For example, you may only want to see the number of flagged messages if such messages exist, since zero is not particularly meaningful. To optionally print a string based upon one of the above sequences, the following construct is used: %?<sequence_char>?<optional_string>? where sequence_char is an expando, and optional_string is the string you would like printed if sequence_char is nonzero. optional_string may contain other sequences as well as normal text, but you may not nest optional strings. Here is an example illustrating how to optionally print the number of new messages in a mailbox in $status_format: %?n?%n new messages.? You can also switch between two strings using the following construct: %?<sequence_char>?<if_string>&<else_string>? If the value of sequence_char is non-zero, if_string will be expanded, otherwise else_string will be expanded. The conditional sequences can also be nested by using the %< and > operators. The %? notation can still be used but requires quoting. For example: %<x?true&false> %<x?%<y?%<z?xyz&xy>&x>&none> For more examples, see Filters Any format string ending in a vertical bar ( |) will be expanded and piped through the first word in the string, using spaces as separator. The string returned will be used for display. If the returned string ends in %, it will be passed through the formatter a second time. This allows the filter to generate a replacement format string including % expandos. All % expandos in a format string are expanded before the script is called so that: Using external filters in format strings set status_format="script.sh '%r %f (%L)'|" will make NeoMutt expand %r, %f and %L before calling the script. The example also shows that arguments can be quoted: the script will receive the expanded string between the single quotes as the only argument. A practical example is the mutt_xtitle script installed in the samples subdirectory of the NeoMutt documentation: it can be used as filter for $status_format to set the current terminal's title, if supported. Padding In most format strings, NeoMutt supports different types of padding using special %-expandos: %|X When this occurs, NeoMutt will fill the rest of the line with the character X. For example, filling the rest of the line with dashes is done by setting: set status_format = "%v on %h: %B: %?n?%n&no? new messages %|-" %>X Since the previous expando stops at the end of line, there must be a way to fill the gap between two items via the %>X expando: it puts as many characters X in between two items so that the rest of the line will be right-justified. For example, to not put the version string and hostname the above example on the left but on the right and fill the gap with spaces, one might use (note the space after %>): set status_format = "%B: %?n?%n&no? new messages %> (%v on %h)" %*X Normal right-justification will print everything to the left of the %>, displaying padding and whatever lies to the right only if there's room. By contrast, soft-fill gives priority to the right-hand side, guaranteeing space to display it and showing padding only if there's still room. If necessary, soft-fill will eat text leftwards to make room for rightward text. For example, to right-justify the subject making sure as much as possible of it fits on screen, one might use (note two spaces after %*: the second ensures there's a space between the truncated right-hand side and the subject): set index_format="%4C %Z %{%b %d} %-15.15L (%?l?%4l&%4c?)%* %s" Conditional Dates This feature allows the format of dates in the index to vary based on how recent the message is. This is especially useful in combination with the nested-if feature. For example, using %<[y?%<[d?%[%H:%M]&%[%m/%d]>&%[%y.%m]>for the date in the $index_format will produce a display like: 1 + 14.12 Grace Hall ( 13) Gulliver's Travels 2 + 10/02 Callum Harrison ( 48) Huckleberry Finn 3 12:17 Rhys Lee ( 42) The Lord Of The Rings Control allowed header fields in a mailto: URL Usage: mailto_allow * header-field unmailto_allow * header-field As a security measure, NeoMutt will only add user-approved header fields from a mailto:URL. This is necessary since NeoMutt will handle certain header fields, such as Attach:, in a special way. The mailto_allow and unmailto_allow commands allow the user to modify the list of approved headers. NeoMutt initializes the default list to contain only the Subject and Body header fields, which are the only requirement specified by the mailto:specification in RFC2368, and the Cc, In-Reply-To, References headers to aid with replies to mailing lists.
Advanced Usage Character Set Handling A character set is basically a mapping between bytes and glyphs and implies a certain character encoding scheme. For example, for the ISO 8859 family of character sets, an encoding of 8bit per character is used. For the Unicode character set, different character encodings may be used, UTF-8 being the most popular. In UTF-8, a character is represented using a variable number of bytes ranging from 1 to 4. Since NeoMutt is a command-line tool run from a shell, and delegates certain tasks to external tools (such as an editor for composing/editing messages), all of these tools need to agree on a character set and encoding. There exists no way to reliably deduce the character set a plain text file has. Interoperability is gained by the use of well-defined environment variables. The full set can be printed by issuing locale on the command line. Upon startup, NeoMutt determines the character set on its own using routines that inspect locale-specific environment variables. Therefore, it is generally not necessary to set the $charset variable in NeoMutt. It may even be counter-productive as NeoMutt uses system and library functions that derive the character set themselves and on which NeoMutt has no influence. It's safest to let NeoMutt work out the locale setup itself. If you happen to work with several character sets on a regular basis, it's highly advisable to use Unicode and an UTF-8 locale. Unicode can represent nearly all characters in a message at the same time. When not using a Unicode locale, it may happen that you receive messages with characters not representable in your locale. When displaying such a message, or replying to or forwarding it, information may get lost possibly rendering the message unusable (not only for you but also for the recipient, this breakage is not reversible as lost information cannot be guessed). A Unicode locale makes all conversions superfluous which eliminates the risk of conversion errors. It also eliminates potentially wrong expectations about the character set between NeoMutt and external programs. The terminal emulator used also must be properly configured for the current locale. Terminal emulators usually do not derive the locale from environment variables, they need to be configured separately. If the terminal is incorrectly configured, NeoMutt may display random and unexpected characters (question marks, octal codes, or just random glyphs), format strings may not work as expected, you may not be abled to enter non-ascii characters, and possible more. Data is always represented using bytes and so a correct setup is very important as to the machine, all character sets look the same. Warning: A mismatch between what system and library functions think the locale is and what NeoMutt was told what the locale is may make it behave badly with non-ascii input: it will fail at seemingly random places. This warning is to be taken seriously since not only local mail handling may suffer: sent messages may carry wrong character set information the receiver has too deal with. The need to set $charset directly in most cases points at terminal and environment variable setup problems, not NeoMutt problems. A list of officially assigned and known character sets can be found at IANA, a list of locally supported locales can be obtained by running locale -a. Regular Expressions All string patterns in NeoMutt including those in more complex patterns must be specified using regular expressions (regex) in the POSIX extended syntax (which is more or less the syntax used by egrep and GNU awk). For your convenience, we have included below a brief description of this syntax. The search is case sensitive if the pattern contains at least one upper case letter, and case insensitive otherwise. \must be quoted if used for a regular expression in an initialization command: \\. A regular expression is a pattern that describes a set of strings. Regular expressions are constructed analogously to arithmetic expressions, by using various operators to combine smaller expressions. The regular expression can be enclosed/delimited by either " or ' which is useful if the regular expression includes a white-space character. See for more information on " and ' delimiter processing. To match a literal " or ' you must preface it with \ (backslash). The fundamental building blocks are the regular expressions that match a single character. Most characters, including all letters and digits, are regular expressions that match themselves. Any metacharacter with special meaning may be quoted by preceding it with a backslash. The period .matches any single character. The caret ^and the dollar sign $are metacharacters that respectively match the empty string at the beginning and end of a line. A list of characters enclosed by [and ]matches any single character in that list; if the first character of the list is a caret ^then it matches any character not in the list. For example, the regular expression [0123456789]matches any single digit. A range of ASCII characters may be specified by giving the first and last characters, separated by a hyphen -. Most metacharacters lose their special meaning inside lists. To include a literal ]place it first in the list. Similarly, to include a literal ^place it anywhere but first. Finally, to include a literal hyphen -place it last. Certain named classes of characters are predefined. Character classes consist of [:, a keyword denoting the class, and :]. The following classes are defined by the POSIX standard in POSIX regular expression character classes Character class Description [:alnum:] Alphanumeric characters [:alpha:] Alphabetic characters [:blank:] Space or tab characters [:cntrl:] Control characters [:digit:] Numeric characters [:graph:] Characters that are both printable and visible. (A space is printable, but not visible, while an a is both) [:lower:] Lower-case alphabetic characters [:print:] Printable characters (characters that are not control characters) [:punct:] Punctuation characters (characters that are not letter, digits, control characters, or space characters) [:space:] Space characters (such as space, tab and formfeed, to name a few) [:upper:] Upper-case alphabetic characters [:xdigit:] Characters that are hexadecimal digits
A character class is only valid in a regular expression inside the brackets of a character list. Note that the brackets in these class names are part of the symbolic names, and must be included in addition to the brackets delimiting the bracket list. For example, [[:digit:]]is equivalent to [0-9]. Two additional special sequences can appear in character lists. These apply to non-ASCII character sets, which can have single symbols (called collating elements) that are represented with more than one character, as well as several characters that are equivalent for collating or sorting purposes: Collating Symbols A collating symbol is a multi-character collating element enclosed in [.and .]. For example, if ch is a collating element, then [[.ch.]]is a regex that matches this collating element, while [ch]is a regex that matches either c or h. Equivalence Classes An equivalence class is a locale-specific name for a list of characters that are equivalent. The name is enclosed in [=and =]. For example, the name e might be used to represent all of e with grave ( è), e with acute ( é) and e. In this case, [[=e=]]is a regex that matches any of: e with grave ( è), e with acute ( é) and e. A regular expression matching a single character may be followed by one of several repetition operators described in . Regular expression repetition operators Operator Description ? The preceding item is optional and matched at most once * The preceding item will be matched zero or more times + The preceding item will be matched one or more times {n} The preceding item is matched exactly n times {n,} The preceding item is matched n or more times {,m} The preceding item is matched at most m times {n,m} The preceding item is matched at least n times, but no more than m times
Two regular expressions may be concatenated; the resulting regular expression matches any string formed by concatenating two substrings that respectively match the concatenated subexpressions. Two regular expressions may be joined by the infix operator |; the resulting regular expression matches any string matching either subexpression. Repetition takes precedence over concatenation, which in turn takes precedence over alternation. A whole subexpression may be enclosed in parentheses to override these precedence rules. If you compile NeoMutt with the included regular expression engine, the following operators may also be used in regular expressions as described in . GNU regular expression extensions Expression Description \\y Matches the empty string at either the beginning or the end of a word \\B Matches the empty string within a word \\< Matches the empty string at the beginning of a word \\> Matches the empty string at the end of a word \\w Matches any word-constituent character (letter, digit, or underscore) \\W Matches any character that is not word-constituent \\` Matches the empty string at the beginning of a buffer (string) \\' Matches the empty string at the end of a buffer
Please note however that these operators are not defined by POSIX, so they may or may not be available in stock libraries on various systems.
Patterns: Searching, Limiting and Tagging Pattern Modifier Many of NeoMutt's commands allow you to specify a pattern to match ( limit, tag-pattern, delete-pattern, etc.). shows several ways to select messages. Pattern modifiers Pattern modifier Description ~A all messages ~b EXPR messages which contain EXPR in the message body =b STRING messages which contain STRING in the message body. If IMAP is enabled, searches for STRING on the server, rather than downloading each message and searching it locally. ~B EXPR messages which contain EXPR in the whole message =B STRING messages which contain STRING in the whole message. If IMAP is enabled, searches for STRING on the server, rather than downloading each message and searching it locally. ~c EXPR messages carbon-copied to EXPR %c GROUP messages carbon-copied to any member of GROUP ~C EXPR messages either to: or cc: EXPR %C GROUP messages either to: or cc: to any member of GROUP ~d [ MIN]-[ MAX] messages with date-sent in a Date range ~D deleted messages ~e EXPR messages which contains EXPR in the Sender field %e GROUP messages which contain a member of GROUP in the Sender field ~E expired messages ~F flagged messages ~f EXPR messages originating from EXPR %f GROUP messages originating from any member of GROUP ~g cryptographically signed messages ~G cryptographically encrypted messages ~h EXPR messages which contain EXPR in the message header =h STRING messages which contain STRING in the message header. If IMAP is enabled, searches for STRING on the server, rather than downloading each message and searching it locally; STRING must be of the form header: substring(see below). ~H EXPR messages with a spam attribute matching EXPR ~i EXPR messages which match EXPR in the Message-ID field ~k messages which contain PGP key material ~L EXPR messages either originated or received by EXPR %L GROUP message either originated or received by any member of GROUP ~l messages addressed to a known mailing list ~m [ MIN]-[ MAX] messages with numbers in the range MIN to MAX***) ~m <[ MAX] messages with numbers less than MAX***) ~m >[ MIN] messages with numbers greater than MIN***) ~m [ M] just message number M***) ~m [ MIN],[ MAX] messages with offsets (from selected message) in the range MIN to MAX***) ~n [ MIN]-[ MAX] messages with a score in the range MIN to MAX*) ~N new messages ~O old messages ~p messages addressed to you (consults alternates) ~P messages from you (consults alternates) ~Q messages which have been replied to ~r [ MIN]-[ MAX] messages with date-received in a Date range ~R read messages ~s EXPR messages having EXPR in the Subject field. ~S superseded messages ~t EXPR messages addressed to EXPR ~T tagged messages ~u messages addressed to a subscribed mailing list ~U unread messages ~v messages part of a collapsed thread. ~V cryptographically verified messages ~x EXPR messages which contain EXPR in the References or In-Reply-To field ~X [ MIN]-[ MAX] messages with MIN to MAX attachments *) ~y EXPR messages which contain EXPR in their keywords ~z [ MIN]-[ MAX] messages with a size in the range MIN to MAX*) **) =/ STRING IMAP custom server-side search for STRING. Currently only defined for Gmail. See: GMail Patterns ~= duplicated messages (see $duplicate_threads) ~# broken threads (see $strict_threads) ~$ unreferenced messages (requires threaded view) ~( PATTERN) messages in threads containing messages matching PATTERN, e.g. all threads containing messages from you: ~(~P) ~<( PATTERN) messages whose immediate parent matches PATTERN, e.g. replies to your messages: ~<(~P) ~>( PATTERN) messages having an immediate child matching PATTERN, e.g. messages you replied to: ~>(~P)
Where EXPR is a regular expression, and GROUP is an address group. *) The forms <[ MAX], >[ MIN], [ MIN]-and -[ MAX]are allowed, too. **) The suffixes K and M are allowed to specify kilobyte and megabyte respectively. ***) The message number ranges (introduced by ~m) are even more general and powerful than the other types of ranges. Read on and see below. Special attention has to be paid when using regular expressions inside of patterns. Specifically, NeoMutt's parser for these patterns will strip one level of backslash ( \), which is normally used for quoting. If it is your intention to use a backslash in the regular expression, you will need to use two backslashes instead ( \\). You can force NeoMutt to treat EXPR as a simple substring instead of a regular expression by using = instead of ~ in the pattern name. For example, =b *.*will find all messages that contain the literal string *.*. Simple string matches are less powerful than regular expressions but can be considerably faster. This is especially true for IMAP folders, because string matches can be performed on the server instead of by fetching every message. IMAP treats =h specially: it must be of the form header: substring and will not partially match header names. The substring part may be omitted if you simply wish to find messages containing a particular header without regard to its value. Patterns matching lists of addresses (notably c, C, p, P and t) match if there is at least one match in the whole list. If you want to make sure that all elements of that list match, you need to prefix your pattern with ^. This example matches all mails which only has recipients from Germany. Matching all addresses in address lists ^~C \.de$ You can restrict address pattern matching to aliases that you have defined with the "@" modifier. This example matches messages whose recipients are all from Germany, and who are known to your alias list. Matching restricted to aliases ^@~C \.de$ To match any defined alias, use a regular expression that matches any string. This example matches messages whose senders are known aliases. Matching any defined alias @~f . Message Ranges If a message number range (from now on: MNR) contains a comma ( ,), it is a relative MNR. That means the numbers denote offsets from the highlighted message. For example: Relative Message Number Ranges Pattern Explanation ~m -2,2 Previous 2, highlighted and next 2 emails ~m 0,1 Highlighted and next email
In addition to numbers, either side of the range can also contain one of the special characters (shortcuts) .^$. The meaning is: Message Number Shortcuts Shortcut Explanation Example Meaning . Current / Highlighted ~m -3,. Previous 3 emails plus the highlighted one $ Last ~m .,$ Highlighted email and all the later ones ^ First ~m ^,1 Highlighted, next and all preceding ones
Lastly, you can also leave either side of the range blank, to make it extend as far as possible. For example, ~m ,1 has the same meaning as the last example in . Otherwise, if a MNR doesn't contain a comma, the meaning is similar to other ranges, except that the shortcuts are still available. Examples: Absolute Message Number Ranges Pattern Explanation ~m 3-10 Emails 3 to 10 ~m -10 Emails 1 to 10 ~m 10- Emails 10 to last ~m <3 First and second email ~m ^-2 First and second email ~m >1 Everything but first email ~m 2-$ Everything but first email ~m 2 Just the second email
Simple Searches NeoMutt supports two versions of so called simple searches. These are issued if the query entered for searching, limiting and similar operations does not seem to contain a valid pattern modifier (i.e. it does not contain one of these characters: ~, =or %). If the query is supposed to contain one of these special characters, they must be escaped by prepending a backslash ( \). The first type is by checking whether the query string equals a keyword case-insensitively from : If that is the case, NeoMutt will use the shown pattern modifier instead. If a keyword would conflict with your search keyword, you need to turn it into a regular expression to avoid matching the keyword table. For example, if you want to find all messages matching flag(using $simple_search) but don't want to match flagged messages, simply search for [f]lag . Simple search keywords Keyword Pattern modifier all ~A . ~A ^ ~A del ~D flag ~F new ~N old ~O repl ~Q read ~R tag ~T unread ~U
The second type of simple search is to build a complex search pattern using $simple_search as a template. NeoMutt will insert your query properly quoted and search for the composed complex query.
Nesting and Boolean Operators Logical AND is performed by specifying more than one criterion. For example: ~t work ~f elkins would select messages which contain the word work in the list of recipients and that have the word elkins in the From header field. NeoMutt also recognizes the following operators to create more complex search patterns: ! — logical NOT operator | — logical OR operator () — logical grouping operator Here is an example illustrating a complex search pattern. This pattern will select all messages which do not contain work in the To or Cc field and which are from elkins. Using boolean operators in patterns !(~t work|~c work) ~f elkins Here is an example using white space in the regular expression (note the 'and "delimiters). For this to match, the mail's subject must match the ^Junk +From +Me$and it must be from either Jim +Somebody or Ed +SomeoneElse: '~s "^Junk +From +Me$" ~f ("Jim +Somebody"|"Ed +SomeoneElse")' If a regular expression contains parenthesis, or a vertical bar ("|"), you must enclose the expression in double or single quotes since those characters are also used to separate different parts of NeoMutt's pattern language. For example: ~f "user@(home\.org|work\.com)"Without the quotes, the parenthesis wouldn't end. This would be separated to two OR'd patterns: ~f user@(home\.org and work\.com). They are never what you want. Searching by Date NeoMutt supports two types of dates, absolute and relative. Absolute Dates Dates must be in DD/MM/YY format (month and year are optional, defaulting to the current month and year). An example of a valid range of dates is: Limit to messages matching: ~d 20/1/95-31/10 If you omit the minimum (first) date, and just specify -DD/MM/YY, all messages before the given date will be selected. If you omit the maximum (second) date, and specify DD/MM/YY-, all messages after the given date will be selected. If you specify a single date with no dash ( -), only messages sent on the given date will be selected. You can add error margins to absolute dates. An error margin is a sign (+ or -), followed by a digit, followed by one of the units in . As a special case, you can replace the sign by a *character, which is equivalent to giving identical plus and minus error margins. Date units Unit Description y Years m Months w Weeks d Days
Example: To select any messages two weeks around January 15, 2001, you'd use the following pattern: Limit to messages matching: ~d 15/1/2001*2w
Relative Dates This type of date is relative to the current date, and may be specified as: > offset for messages older than offset units < offset for messages newer than offset units = offset for messages exactly offset units old offset is specified as a positive number with one of the units from . Example: to select messages less than 1 month old, you would use Limit to messages matching: ~d <1m All dates used when searching are relative to the local time zone, so unless you change the setting of your $index_format to include a %[...]format, these are not the dates shown in the main index.
GMail Patterns =/ "search terms" invokes server-side search, passing along the search terms provided. Search results are constrained by IMAP to be within the current folder. At present this only supports Gmail's search API IMAP extension. The search language is entirely up to the mail provider and changes at their discretion. Using ~/ will silently fail. For up-to-date information about searching, see: GMail's Support Page. You will need to (once) use a web-browser to visit Settings/Labels and enable "Show in IMAP" for "All Mail". When searching, visit that folder in NeoMutt to most closely match Gmail search semantics. GMail Example Patterns Pattern Matches =/ "list:foo.example.org has:attachment is:important" the foo.example.org mailing-list per Gmail's definitions, and has an attachment, and has been marked as important =/ "{has:purple-star has:yellow-star} older_than:2m" is older than two months and has either a purple-star or a yellow-star
Marking Messages There are times that it's useful to ask NeoMutt to "remember" which message you're currently looking at, while you move elsewhere in your mailbox. You can do this with the mark-message operator, which is bound to the ~key by default. Press this key to enter an identifier for the marked message. When you want to return to this message, press 'and the name that you previously entered. (Message marking is really just a shortcut for defining a macro that returns you to the current message by searching for its Message-ID. You can choose a different prefix by setting the $mark_macro_prefix variable.) Using Tags Sometimes it is desirable to perform an operation on a group of messages all at once rather than one at a time. An example might be to save messages to a mailing list to a separate folder, or to delete all messages with a given subject. To tag all messages matching a pattern, use the <tag-pattern>function, which is bound to shift-T by default. Or you can select individual messages by hand using the <tag-message>function, which is bound to t by default. See patterns for NeoMutt's pattern matching syntax. Once you have tagged the desired messages, you can use the tag-prefix operator, which is the ;(semicolon) key by default. When the tag-prefix operator is used, the next operation will be applied to all tagged messages if that operation can be used in that manner. If the $auto_tag variable is set, the next operation applies to the tagged messages automatically, without requiring the tag-prefix. In macrosor push commands, you can use the <tag-prefix-cond>operator. If there are no tagged messages, NeoMutt will eat the rest of the macro to abort it's execution. NeoMutt will stop eating the macro when it encounters the <end-cond>operator; after this operator the rest of the macro will be executed as normal. Using Hooks A hook is a concept found in many other programs which allows you to execute arbitrary commands before performing some operation. For example, you may wish to tailor your configuration based upon which mailbox you are reading, or to whom you are sending mail. In the NeoMutt world, a hook consists of a regular expression or pattern along with a configuration option/command. See: account-hook append-hook charset-hook close-hook crypt-hook fcc-hook fcc-save-hook folder-hook iconv-hook mbox-hook message-hook open-hook reply-hook save-hook send-hook send2-hook for specific details on each type of hook available. If a hook changes configuration settings, these changes remain effective until the end of the current NeoMutt session. As this is generally not desired, a default hook needs to be added before all other hooks of that type to restore configuration defaults. Specifying a <quote>default</quote> hook send-hook . 'unmy_hdr From:' send-hook ~C'^b@b\.b$' my_hdr from: c@c.c In , by default the value of $from and $realname is not overridden. When sending messages either To: or Cc: to <b@b.b>, the From: header is changed to <c@c.c>. Message Matching in Hooks Hooks that act upon messages ( message-hook, reply-hook, send-hook, send2-hook, save-hook, fcc-hook) are evaluated in a slightly different manner. For the other types of hooks, a regular expression is sufficient. But in dealing with messages a finer grain of control is needed for matching since for different purposes you want to match different criteria. NeoMutt allows the use of the search pattern language for matching messages in hook commands. This works in exactly the same way as it would when limiting or searching the mailbox, except that you are restricted to those operators which match information NeoMutt extracts from the header of the message (i.e., from, to, cc, date, subject, etc.). For example, if you wanted to set your return address based upon sending mail to a specific address, you could do something like: send-hook '~t ^user@work\.com$' 'my_hdr From: John Smith <user@host>' which would execute the given command when sending mail to user@work.com. However, it is not required that you write the pattern to match using the full searching language. You can still specify a simple regular expression like the other hooks, in which case NeoMutt will translate your pattern into the full language, using the translation specified by the $default_hook variable. The pattern is translated at the time the hook is declared, so the value of $default_hook that is in effect at that time will be used. Mailbox Matching in Hooks Hooks that match against mailboxes ( folder-hook, mbox-hook) apply both regular expression syntax as well as mailbox shortcut expansion on the regex parameter. There is some overlap between these, so special attention should be paid to the first character of the regex. # Here, ^ will expand to "the current mailbox" not "beginning of string": folder-hook ^/home/user/Mail/bar "set sort=threads" # If you want ^ to be interpreted as "beginning of string", one workaround # is to enclose the regex in parenthesis: folder-hook (^/home/user/Mail/bar) "set sort=threads" # This will expand to the default save folder for the alias "imap.example.com", which # is probably not what you want: folder-hook @imap.example.com "set sort=threads" # A workaround is to use parenthesis or a backslash: folder-hook (@imap.example.com) "set sort=threads" folder-hook '\@imap.example.com' "set sort=threads" Keep in mind that mailbox shortcut expansion on the regex parameter takes place when the hook is initially parsed, not when the hook is matching against a mailbox. When NeoMutt starts up and is reading the .neomuttrc, some mailbox shortcuts may not be usable. For example, the "current mailbox" shortcut, ^, will expand to an empty string because no mailbox has been opened yet. NeoMutt will issue an error for this case or if the mailbox shortcut results in an empty regex. Managing the Environment You can alter the environment that NeoMutt passes on to its child processes using the setenv and unsetenv operators. (N.B. These follow NeoMutt-style syntax, not shell-style!) You can also query current environment values by prefixing a ?character. setenv TERM vt100 setenv ORGANIZATION "The NeoMutt Development Team" unsetenv DISPLAY setenv ?LESS External Address Queries NeoMutt supports connecting to external directory databases such as LDAP, ph/qi, bbdb, or NIS through a wrapper script which connects to NeoMutt using a simple interface. Using the $query_command variable, you specify the wrapper command to use. For example: set query_command = "mutt_ldap_query.pl %s" The wrapper script should accept the query on the command-line. It should return a one line message, then each matching response on a single line, each line containing a tab separated address then name then some other optional information. On error, or if there are no matching addresses, return a non-zero exit code and a one line error message. An example multiple response output: Searching database ... 20 entries ... 3 matching: me@cs.hmc.edu Michael Elkins mutt dude blong@fiction.net Brandon Long mutt and more roessler@does-not-exist.org Thomas Roessler mutt pgp There are two mechanisms for accessing the query function of NeoMutt. One is to do a query from the index menu using the <query>function (default: Q). This will prompt for a query, then bring up the query menu which will list the matching responses. From the query menu, you can select addresses to create aliases, or to mail. You can tag multiple addresses to mail, start a new query, or have a new query appended to the current responses. The other mechanism for accessing the query function is for address completion, similar to the alias completion. In any prompt for address entry, you can use the <complete-query>function (default: ^T) to run a query based on the current address you have typed. Like aliases, NeoMutt will look for what you have typed back to the last space or comma. If there is a single response for that query, NeoMutt will expand the address in place. If there are multiple responses, NeoMutt will activate the query menu. At the query menu, you can select one or more addresses to be added to the prompt. Mailbox Formats NeoMutt supports reading and writing of four different local mailbox formats: mbox, MMDF, MH and Maildir. The mailbox type is auto detected, so there is no need to use a flag for different mailbox types. When creating new mailboxes, NeoMutt uses the default specified with the $mbox_type variable. A short description of the formats follows. mbox. This is a widely used mailbox format for UNIX. All messages are stored in a single file. Each message has a line of the form: From me@cs.hmc.edu Fri, 11 Apr 1997 11:44:56 PST to denote the start of a new message (this is often referred to as the From_ line). The mbox format requires mailbox locking, is prone to mailbox corruption with concurrently writing clients or misinterpreted From_ lines. Depending on the environment, new mail detection can be unreliable. Mbox folders are fast to open and easy to archive. MMDF. This is a variant of the mbox format. Each message is surrounded by lines containing ^A^A^A^A(four times control-A's). The same problems as for mbox apply (also with finding the right message separator as four control-A's may appear in message bodies). MH. A radical departure from mbox and MMDF, a mailbox consists of a directory and each message is stored in a separate file. The filename indicates the message number (however, this is may not correspond to the message number NeoMutt displays). Deleted messages are renamed with a comma ( ,) prepended to the filename. NeoMutt detects this type of mailbox by looking for either .mh_sequences or .xmhcache files (needed to distinguish normal directories from MH mailboxes). MH is more robust with concurrent clients writing the mailbox, but still may suffer from lost flags; message corruption is less likely to occur than with mbox/mmdf. It's usually slower to open compared to mbox/mmdf since many small files have to be read (NeoMutt provides to greatly speed this process up). Depending on the environment, MH is not very disk-space efficient. Maildir. The newest of the mailbox formats, used by the Qmail MTA (a replacement for sendmail). Similar to MH, except that it adds three subdirectories of the mailbox: tmp, new and cur. Filenames for the messages are chosen in such a way they are unique, even when two programs are writing the mailbox over NFS, which means that no file locking is needed and corruption is very unlikely. Maildir maybe slower to open without caching in NeoMutt, it too is not very disk-space efficient depending on the environment. Since no additional files are used for metadata (which is embedded in the message filenames) and Maildir is locking-free, it's easy to sync across different machines using file-level synchronization tools. Mailbox Shortcuts There are a number of built in shortcuts which refer to specific mailboxes. These shortcuts can be used anywhere you are prompted for a file or mailbox path or in path-related configuration variables. Note that these only work at the beginning of a string. Mailbox shortcuts Shortcut Refers to... ! your $spoolfile(incoming) mailbox > your $mbox file < your $record file ^ the current mailbox -or !! the file you've last visited ~ your home directory =or + your $folder directory @alias to the default save folder as determined by the address of the alias
For example, to store a copy of outgoing messages in the folder they were composed in, a folder-hook can be used to set $record: folder-hook . 'set record=^'
Handling Mailing Lists NeoMutt has a few configuration options that make dealing with large amounts of mail easier. The first thing you must do is to let NeoMutt know what addresses you consider to be mailing lists (technically this does not have to be a mailing list, but that is what it is most often used for), and what lists you are subscribed to. This is accomplished through the use of the lists and subscribecommands in your .neomuttrc. Now that NeoMutt knows what your mailing lists are, it can do several things, the first of which is the ability to show the name of a list through which you received a message (i.e., of a subscribed list) in the index menu display. This is useful to distinguish between personal and list mail in the same mailbox. In the $index_format variable, the expando %L will print the string To <list>when list appears in the To field, and Cc <list>when it appears in the Cc field (otherwise it prints the name of the author). Often times the To and Cc fields in mailing list messages tend to get quite large. Most people do not bother to remove the author of the message they reply to from the list, resulting in two or more copies being sent to that person. The <list-reply>function, which by default is bound to L in the index menu and pager, helps reduce the clutter by only replying to the known mailing list addresses instead of all recipients (except as specified by Mail-Followup-To, see below). NeoMutt also supports the Mail-Followup-To header. When you send a message to a list of recipients which includes one or several subscribed mailing lists, and if the $followup_to option is set, NeoMutt will generate a Mail-Followup-To header which contains all the recipients to whom you send this message, but not your address. This indicates that group-replies or list-replies (also known as followups) to this message should only be sent to the original recipients of the message, and not separately to you - you'll receive your copy through one of the mailing lists you are subscribed to. Conversely, when group-replying or list-replying to a message which has a Mail-Followup-To header, NeoMutt will respect this header if the $honor_followup_to configuration variable is set. Using list-reply will in this case also make sure that the reply goes to the mailing list, even if it's not specified in the list of recipients in the Mail-Followup-To. When header editing is enabled, you can create a Mail-Followup-To header manually. NeoMutt will only auto-generate this header if it doesn't exist when you send the message. The other method some mailing list admins use is to generate a Reply-To field which points back to the mailing list address rather than the author of the message. This can create problems when trying to reply directly to the author in private, since most mail clients will automatically reply to the address given in the Reply-To field. NeoMutt uses the $reply_to variable to help decide which address to use. If set to ask-yes or ask-no, you will be prompted as to whether or not you would like to use the address given in the Reply-To field, or reply directly to the address given in the From field. When set to yes, the Reply-To field will be used when present. You can change or delete the X-Label:field within NeoMutt using the edit-label command, bound to the y key by default. This works for tagged messages, too. While in the edit-label function, pressing the <complete> binding (TAB, by default) will perform completion against all labels currently in use. Lastly, NeoMutt has the ability to sort the mailbox into threads. A thread is a group of messages which all relate to the same subject. This is usually organized into a tree-like structure where a message and all of its replies are represented graphically. If you've ever used a threaded news client, this is the same concept. It makes dealing with large volume mailing lists easier because you can easily delete uninteresting threads and quickly find topics of value. Display Munging Working within the confines of a console or terminal window, it is often useful to be able to modify certain information elements in a non-destructive way -- to change how they display, without changing the stored value of the information itself. This is especially so of message subjects, which may often be polluted with extraneous metadata that either is reproduced elsewhere, or is of secondary interest. subjectrx pattern replacement unsubjectrx * pattern subjectrx specifies a regular expression pattern which, if detected in a message subject, causes the subject to be replaced with the replacement value. The replacement is subject to substitutions in the same way as for the spam command: %L for the text to the left of the match, %R for text to the right of the match, and %1 for the first subgroup in the match (etc). If you simply want to erase the match, set it to %L%R. Any number of subjectrx commands may coexist. Note this well: the replacement value replaces the entire subject, not just the match! unsubjectrx removes a given subjectrx from the substitution list. If *is used as the pattern, all substitutions will be removed. Subject Munging # Erase [rt #12345] tags from Request Tracker (RT) e-mails subjectrx '\[rt #[0-9]+\] *' '%L%R' # Servicedesk is another RT that sends more complex subjects. # Keep the ticket number. subjectrx '\[servicedesk #([0-9]+)\] ([^.]+)\.([^.]+) - (new|open|pending|update) - ' '%L[#%1] %R' # Strip out annoying [listname] prefixes in subjects subjectrx '\[[^\]]*\]:? *' '%L%R' New Mail Detection NeoMutt supports setups with multiple folders, allowing all of them to be monitored for new mail (see for details). How New Mail Detection Works For Mbox and Mmdf folders, new mail is detected by comparing access and/or modification times of files: NeoMutt assumes a folder has new mail if it wasn't accessed after it was last modified. Utilities like biff or frm or any other program which accesses the mailbox might cause NeoMutt to never detect new mail for that mailbox if they do not properly reset the access time. Other possible causes of NeoMutt not detecting new mail in these folders are backup tools (updating access times) or filesystems mounted without access time update support (for Linux systems, see the relatime option). Contrary to older NeoMutt releases, it now maintains the new mail status of a folder by properly resetting the access time if the folder contains at least one message which is neither read, nor deleted, nor marked as old. In cases where new mail detection for Mbox or Mmdf folders appears to be unreliable, the $check_mbox_size option can be used to make NeoMutt track and consult file sizes for new mail detection instead which won't work for size-neutral changes. New mail for Maildir is assumed if there is one message in the new/subdirectory which is not marked deleted (see $maildir_trash). For MH folders, a mailbox is considered having new mail if there's at least one message in the unseen sequence as specified by $mh_seq_unseen. Optionally, $new_mail_command can be configured to execute an external program every time new mail is detected in the current inbox. NeoMutt does not poll POP3 folders for new mail, it only periodically checks the currently opened folder (if it's a POP3 folder). For IMAP, by default NeoMutt uses recent message counts provided by the server to detect new mail. If the $imap_idle option is set, it'll use the IMAP IDLE extension if advertised by the server. The $mail_check_recent option changes whether NeoMutt will notify you of new mail in an already visited mailbox. When set (the default) it will only notify you of new mail received since the last time you opened the mailbox. When unset, NeoMutt will notify you of any new mail in the mailbox. Polling For New Mail When in the index menu and being idle (also see $timeout), NeoMutt periodically checks for new mail in all folders which have been configured via the mailboxes command. The interval depends on the folder type: for local/IMAP folders it consults $mail_check and $pop_checkinterval for POP folders. Outside the index menu the directory browser supports checking for new mail using the <check-new>function which is unbound by default. Pressing TAB will bring up a menu showing the files specified by the mailboxes command, and indicate which contain new messages. NeoMutt will automatically enter this mode when invoked from the command line with the -y option. For the pager, index and directory browser menus, NeoMutt contains the <buffy-list>function (bound to .by default) which will print a list of folders with new mail in the command line at the bottom of the screen. For the index, by default NeoMutt displays the number of mailboxes with new mail in the status bar, please refer to the $status_format variable for details. When changing folders, NeoMutt fills the prompt with the first folder from the mailboxes list containing new mail (if any), pressing <Space>will cycle through folders with new mail. The (by default unbound) function <next-unread-mailbox>in the index can be used to immediately open the next folder with unread mail (if any). Calculating Mailbox Message Counts If $mail_check_stats is set, NeoMutt will periodically calculate the unread, flagged, and total message counts for each mailbox watched by the mailboxes command. This calculation takes place at the same time as new mail polling, but is controlled by a separate timer: $mail_check_stats_interval. The sidebar can display these message counts. See $sidebar_format. Editing Threads NeoMutt has the ability to dynamically restructure threads that are broken either by misconfigured software or bad behavior from some correspondents. This allows to clean your mailboxes from these annoyances which make it hard to follow a discussion. Linking Threads Some mailers tend to forget to correctly set the In-Reply-To:and References:headers when replying to a message. This results in broken discussions because NeoMutt has not enough information to guess the correct threading. You can fix this by tagging the reply, then moving to the parent message and using the <link-threads>function (bound to & by default). The reply will then be connected to this parent message. You can also connect multiple children at once, tagging them and using the <tag-prefix>command ( ;) or the $auto_tag option. Breaking Threads On mailing lists, some people are in the bad habit of starting a new discussion by hitting reply to any message from the list and changing the subject to a totally unrelated one. You can fix such threads by using the <break-thread>function (bound by default to #), which will turn the subthread starting from the current message into a whole different thread. Delivery Status Notification (DSN) Support RFC1894 defines a set of MIME content types for relaying information about the status of electronic mail messages. These can be thought of as return receipts. To support DSN, there are two variables. $dsn_notify is used to request receipts for different results (such as failed message, message delivered, etc.). $dsn_return requests how much of your message should be returned with the receipt (headers or full message). When using $sendmail for mail delivery, you need to use either Berkeley sendmail 8.8.x (or greater) a MTA supporting DSN command line options compatible to Sendmail: The -N and -R options can be used by the mail client to make requests as to what type of status messages should be returned. Please consider your MTA documentation whether DSN is supported. For SMTP delivery using $smtp_url, it depends on the capabilities announced by the server whether NeoMutt will attempt to request DSN or not. Start a WWW Browser on URLs If a message contains URLs, it is efficient to get a menu with all the URLs and start a WWW browser on one of them. This functionality is provided by the external urlview program which can be retrieved at ftp://ftp.mutt.org/mutt/contrib/and the configuration commands: macro index \cb |urlview\n macro pager \cb |urlview\n Miscellany This section documents various features that fit nowhere else. Address normalization NeoMutt normalizes all e-mail addresses to the simplest form possible. If an address contains a realname, the form Joe User <joe@example.com>is used and the pure e-mail address without angle brackets otherwise, i.e. just joe@example.com. This normalization affects all headers NeoMutt generates including aliases. Initial folder selection The folder NeoMutt opens at startup is determined as follows: the folder specified in the $MAIL environment variable if present. Otherwise, the value of $MAILDIR is taken into account. If that isn't present either, NeoMutt takes the user's mailbox in the mailspool as determined at compile-time (which may also reside in the home directory). The $spoolfile setting overrides this selection. Highest priority has the mailbox given with the -f command line option.
NeoMutt's MIME Support Quite a bit of effort has been made to make NeoMutt the premier text-mode MIME MUA. Every effort has been made to provide the functionality that the discerning MIME user requires, and the conformance to the standards wherever possible. When configuring NeoMutt for MIME, there are two extra types of configuration files which NeoMutt uses. One is the mime.types file, which contains the mapping of file extensions to IANA MIME types. The other is the mailcap file, which specifies the external commands to use for handling specific MIME types. Using MIME in NeoMutt MIME Overview MIME is short for Multipurpose Internet Mail Extension and describes mechanisms to internationalize and structure mail messages. Before the introduction of MIME, messages had a single text part and were limited to us-ascii header and content. With MIME, messages can have attachments (and even attachments which itself have attachments and thus form a tree structure), nearly arbitrary characters can be used for sender names, recipients and subjects. Besides the handling of non-ascii characters in message headers, to NeoMutt the most important aspect of MIME are so-called MIME types. These are constructed using a major and minor type separated by a forward slash. These specify details about the content that follows. Based upon these, NeoMutt decides how to handle this part. The most popular major type is text with minor types for plain text, HTML and various other formats. Major types also exist for images, audio, video and of course general application data (e.g. to separate cryptographically signed data with a signature, send office documents, and in general arbitrary binary data). There's also the multipart major type which represents the root of a subtree of MIME parts. A list of supported MIME types can be found in . MIME also defines a set of encoding schemes for transporting MIME content over the network: 7bit, 8bit, quoted-printable, base64 and binary. There're some rules when to choose what for encoding headers and/or body (if needed), and NeoMutt will in general make a good choice. NeoMutt does most of MIME encoding/decoding behind the scenes to form messages conforming to MIME on the sending side. On reception, it can be flexibly configured as to how what MIME structure is displayed (and if it's displayed): these decisions are based on the content's MIME type. There are three areas/menus in dealing with MIME: the pager (while viewing a message), the attachment menu and the compose menu. Viewing MIME Messages in the Pager When you select a message from the index and view it in the pager, NeoMutt decodes as much of a message as possible to a text representation. NeoMutt internally supports a number of MIME types, including the text major type (with all minor types), the message/rfc822(mail messages) type and some multipart types. In addition, it recognizes a variety of PGP MIME types, including PGP/MIME and application/pgp. NeoMutt will denote attachments with a couple lines describing them. These lines are of the form: [-- Attachment #1: Description --] [-- Type: text/plain, Encoding: 7bit, Size: 10000 --] Where the Description is the description or filename given for the attachment, and the Encoding is one of the already mentioned content encodings. If NeoMutt cannot deal with a MIME type, it will display a message like: [-- image/gif is unsupported (use 'v' to view this part) --] The Attachment Menu The default binding for <view-attachments>is v, which displays the attachment menu for a message. The attachment menu displays a list of the attachments in a message. From the attachment menu, you can save, print, pipe, delete, and view attachments. You can apply these operations to a group of attachments at once, by tagging the attachments and by using the <tag-prefix>operator. You can also reply to the current message from this menu, and only the current attachment (or the attachments tagged) will be quoted in your reply. You can view attachments as text, or view them using the mailcap viewer definition (the mailcap mechanism is explained later in detail). Finally, you can apply the usual message-related functions (like <resend-message> , and the <reply>and <forward>functions) to attachments of type message/rfc822. See table for all available functions. The Compose Menu The compose menu is the menu you see before you send a message. It allows you to edit the recipient list, the subject, and other aspects of your message. It also contains a list of the attachments of your message, including the main body. From this menu, you can print, copy, filter, pipe, edit, compose, review, and rename an attachment or a list of tagged attachments. You can also modifying the attachment information, notably the type, encoding and description. Attachments appear as follows by default: - 1 [text/plain, 7bit, 1K] /tmp/neomutt-euler-8082-0 <no description> 2 [applica/x-gunzip, base64, 422K] ~/src/neomutt-0.85.tar.gz <no description> The -denotes that NeoMutt will delete the file after sending (or postponing, or canceling) the message. It can be toggled with the <toggle-unlink>command (default: u). The next field is the MIME content-type, and can be changed with the <edit-type>command (default: ^T). The next field is the encoding for the attachment, which allows a binary message to be encoded for transmission on 7bit links. It can be changed with the <edit-encoding>command (default: ^E). The next field is the size of the attachment, rounded to kilobytes or megabytes. The next field is the filename, which can be changed with the <rename-file>command (default: R). The final field is the description of the attachment, and can be changed with the <edit-description>command (default: d). See $attach_format for a full list of available expandos to format this display to your needs. MIME Type Configuration with <literal>mime.types</literal> To get most out of MIME, it's important that a MIME part's content type matches the content as closely as possible so that the recipient's client can automatically select the right viewer for the content. However, there's no reliable for NeoMutt to know how to detect every possible file type. Instead, it uses a simple plain text mapping file that specifies what file extension corresponds to what MIME type. This file is called mime.types. When you add an attachment to your mail message, NeoMutt searches your personal mime.types file at $HOME/.mime.types, and then the system mime.types file at /usr/local/share/neomutt/mime.types or /etc/mime.types Each line starts with the full MIME type, followed by a space and space-separated list of file extensions. For example you could use: <literal>mime.types</literal> application/postscript ps eps application/pgp pgp audio/x-aiff aif aifc aiff A sample mime.types file comes with the NeoMutt distribution, and should contain most of the MIME types you are likely to use. If NeoMutt can not determine the MIME type by the extension of the file you attach, it will run the command specified in $mime_type_query_command. If that command is not specified, NeoMutt will look at the file. If the file is free of binary information, NeoMutt will assume that the file is plain text, and mark it as text/plain. If the file contains binary information, then NeoMutt will mark it as application/octet-stream. You can change the MIME type that NeoMutt assigns to an attachment by using the <edit-type>command from the compose menu (default: ^T), see for supported major types. NeoMutt recognizes all of these if the appropriate entry is found in the mime.types file. Non-recognized mime types should only be used if the recipient of the message is likely to be expecting such attachments. Supported MIME types MIME major type Standard Description application yes General application data audio yes Audio data image yes Image data message yes Mail messages, message status information model yes VRML and other modeling data multipart yes Container for other MIME parts text yes Text data video yes Video data chemical no Mostly molecular data
MIME types are not arbitrary, they need to be assigned by IANA.
MIME Viewer Configuration with Mailcap NeoMutt supports RFC 1524 MIME Configuration, in particular the Unix specific format specified in Appendix A of RFC 1524. This file format is commonly referred to as the mailcap format. Many MIME compliant programs utilize the mailcap format, allowing you to specify handling for all MIME types in one place for all programs. Programs known to use this format include Firefox, lynx and metamail. In order to handle various MIME types that NeoMutt doesn't have built-in support for, it parses a series of external configuration files to find an external handler. The default search string for these files is a colon delimited list containing the following files: $HOME/.mailcap $PKGDATADIR/mailcap $SYSCONFDIR/mailcap /etc/mailcap /usr/etc/mailcap /usr/local/etc/mailcap where $HOME is your home directory. The $PKGDATADIR and the $SYSCONFDIR directories depend on where NeoMutt is installed: the former is the default for shared data, the latter for system configuration files. The default search path can be obtained by running the following command: neomutt -nF /dev/null -Q mailcap_path In particular, the metamail distribution will install a mailcap file, usually as /usr/local/etc/mailcap, which contains some baseline entries. The Basics of the Mailcap File A mailcap file consists of a series of lines which are comments, blank, or definitions. A comment line consists of a # character followed by anything you want. A blank line is blank. A definition line consists of a content type, a view command, and any number of optional fields. Each field of a definition line is divided by a semicolon ;character. The content type is specified in the MIME standard type/subtype notation. For example, text/plain, text/html, image/gif, etc. In addition, the mailcap format includes two formats for wildcards, one using the special *subtype, the other is the implicit wild, where you only include the major type. For example, image/*, or video will match all image types and video types, respectively. The view command is a Unix command for viewing the type specified. There are two different types of commands supported. The default is to send the body of the MIME message to the command on stdin. You can change this behavior by using %s as a parameter to your view command. This will cause NeoMutt to save the body of the MIME message to a temporary file, and then call the view command with the %s replaced by the name of the temporary file. In both cases, NeoMutt will turn over the terminal to the view program until the program quits, at which time NeoMutt will remove the temporary file if it exists. This means that mailcap does not work out of the box with programs which detach themselves from the terminal right after starting, like open on Mac OS X. In order to nevertheless use these programs with mailcap, you probably need custom shell scripts. So, in the simplest form, you can send a text/plain message to the external pager more on standard input: text/plain; more Or, you could send the message as a file: text/plain; more %s Perhaps you would like to use lynx to interactively view a text/html message: text/html; lynx %s In this case, lynx does not support viewing a file from standard input, so you must use the %s syntax. Some older versions of lynx contain a bug where they will check the mailcap file for a viewer for text/html. They will find the line which calls lynx, and run it. This causes lynx to continuously spawn itself to view the object. On the other hand, maybe you don't want to use lynx interactively, you just want to have it convert the text/html to text/plain, then you can use: text/html; lynx -dump %s | more Perhaps you wish to use lynx to view text/html files, and a pager on all other text formats, then you would use the following: text/html; lynx %s text/*; more Secure Use of Mailcap The interpretation of shell meta-characters embedded in MIME parameters can lead to security problems in general. NeoMutt tries to quote parameters in expansion of %s syntaxes properly, and avoids risky characters by substituting them, see the $mailcap_sanitize variable. Although NeoMutt's procedures to invoke programs with mailcap seem to be safe, there are other applications parsing mailcap, maybe taking less care of it. Therefore you should pay attention to the following rules: Keep the %-expandos away from shell quoting.Don't quote them with single or double quotes. NeoMutt does this for you, the right way, as should any other program which interprets mailcap. Don't put them into backtick expansions. Be highly careful with evil statements, and avoid them if possible at all. Trying to fix broken behavior with quotes introduces new leaks — there is no alternative to correct quoting in the first place. If you have to use the %-expandos' values in context where you need quoting or backtick expansions, put that value into a shell variable and reference the shell variable where necessary, as in the following example (using $charset inside the backtick expansion is safe, since it is not itself subject to any further expansion): text/test-mailcap-bug; cat %s; copiousoutput; test=charset=%{charset} \ && test "`echo $charset | tr '[A-Z]' '[a-z]'`" != iso-8859-1 Advanced Mailcap Usage Optional Fields In addition to the required content-type and view command fields, you can add semi-colon ;separated fields to set flags and other options. NeoMutt recognizes the following optional fields: copiousoutput This flag tells NeoMutt that the command passes possibly large amounts of text on standard output. This causes NeoMutt to invoke a pager (either the internal pager or the external pager defined by the pager variable) on the output of the view command. Without this flag, NeoMutt assumes that the command is interactive. One could use this to replace the pipe to more in the lynx -dump example in the Basic section: text/html; lynx -dump %s ; copiousoutput This will cause lynx to format the text/html output as text/plain and NeoMutt will use your standard pager to display the results. NeoMutt will set the COLUMNS environment variable to the width of the pager. Some programs make use of this environment variable automatically. Others provide a command line argument that can use this to set the output width: text/html; lynx -dump -width ${COLUMNS:-80} %s; copiousoutput Note that when using the built-in pager, only entries with this flag will be considered a handler for a MIME type — all other entries will be ignored. needsterminal NeoMutt uses this flag when viewing attachments with auto_view , in order to decide whether it should honor the setting of the $wait_key variable or not. When an attachment is viewed using an interactive program, and the corresponding mailcap entry has a needsterminal flag, NeoMutt will use $wait_key and the exit status of the program to decide if it will ask you to press a key after the external program has exited. In all other situations it will not prompt you for a key. compose=<command> This flag specifies the command to use to create a new attachment of a specific MIME type. NeoMutt supports this from the compose menu. composetyped=<command> This flag specifies the command to use to create a new attachment of a specific MIME type. This command differs from the compose command in that NeoMutt will expect standard MIME headers on the data. This can be used to specify parameters, filename, description, etc. for a new attachment. NeoMutt supports this from the compose menu. print=<command> This flag specifies the command to use to print a specific MIME type. NeoMutt supports this from the attachment and compose menus. edit=<command> This flag specifies the command to use to edit a specific MIME type. NeoMutt supports this from the compose menu, and also uses it to compose new attachments. NeoMutt will default to the defined $editor for text attachments. nametemplate=<template> This field specifies the format for the file denoted by %s in the command fields. Certain programs will require a certain file extension, for instance, to correctly view a file. For instance, lynx will only interpret a file as text/html if the file ends in .html. So, you would specify lynx as a text/html viewer with a line in the mailcap file like: text/html; lynx %s; nametemplate=%s.html test=<command> This field specifies a command to run to test whether this mailcap entry should be used. The command is defined with the command expansion rules defined in the next section. If the command returns 0, then the test passed, and NeoMutt uses this entry. If the command returns non-zero, then the test failed, and NeoMutt continues searching for the right entry. Note that the content-type must match before NeoMutt performs the test. For example: text/html; firefox -remote 'openURL(%s)' ; test=RunningX text/html; lynx %s In this example, NeoMutt will run the program RunningX which will return 0 if the X Window manager is running, and non-zero if it isn't. If RunningX returns 0, then NeoMutt will run firefox to display the text/html object. If RunningX doesn't return 0, then NeoMutt will go on to the next entry and use lynx to display the text/html object. Search Order When searching for an entry in the mailcap file, NeoMutt will search for the most useful entry for its purpose. For instance, if you are attempting to print an image/gif, and you have the following entries in your mailcap file, NeoMutt will search for an entry with the print command: image/*; xv %s image/gif; ; print= anytopnm %s | pnmtops | lpr; \ nametemplate=%s.gif NeoMutt will skip the image/*entry and use the image/gif entry with the print command. In addition, you can use this with auto_view to denote two commands for viewing an attachment, one to be viewed automatically, the other to be viewed interactively from the attachment menu using the <view-mailcap>function (bound to m by default). In addition, you can then use the test feature to determine which viewer to use interactively depending on your environment. text/html; firefox -remote 'openURL(%s)' ; test=RunningX text/html; lynx %s; nametemplate=%s.html text/html; lynx -dump %s; nametemplate=%s.html; copiousoutput For auto_view , NeoMutt will choose the third entry because of the copiousoutput tag. For interactive viewing, NeoMutt will run the program RunningX to determine if it should use the first entry. If the program returns non-zero, NeoMutt will use the second entry for interactive viewing. The last entry is for inline display in the pager and the <view-attach>function in the attachment menu. Entries with the copiousoutput tag should always be specified as the last one per type. For non-interactive use, the last entry will then actually be the first matching one with the tag set. For non-interactive use, only copiousoutput-tagged entries are considered. For interactive use, NeoMutt ignores this tag and treats all entries equally. Therefore, if not specified last, all following entries without this tag would never be considered for <view-attach>because the copiousoutput before them matched already. Command Expansion The various commands defined in the mailcap files are passed to the /bin/sh shell using the system(3)function. Before the command is passed to /bin/sh -c, it is parsed to expand various special parameters with information from NeoMutt. The keywords NeoMutt expands are: %s As seen in the basic mailcap section, this variable is expanded to a filename specified by the calling program. This file contains the body of the message to view/print/edit or where the composing program should place the results of composition. In addition, the use of this keyword causes NeoMutt to not pass the body of the message to the view/print/edit program on stdin. %t NeoMutt will expand %t to the text representation of the content type of the message in the same form as the first parameter of the mailcap definition line, i.e. text/html or image/gif. %{<parameter>} NeoMutt will expand this to the value of the specified parameter from the Content-Type: line of the mail message. For instance, if your mail message contains: Content-Type: text/plain; charset=iso-8859-1 then NeoMutt will expand %{charset}to iso-8859-1. The default metamail mailcap file uses this feature to test the charset to spawn an xterm using the right charset to view the message. \% This will be replaced by a literal %. NeoMutt does not currently support the %F and %n keywords specified in RFC 1524. The main purpose of these parameters is for multipart messages, which is handled internally by NeoMutt. Example Mailcap Files This mailcap file is fairly simple and standard: # I'm always running X :) video/*; xanim %s > /dev/null image/*; xv %s > /dev/null # I'm always running firefox (if my computer had more memory, maybe) text/html; firefox -remote 'openURL(%s)' This mailcap file shows quite a number of examples: # Use xanim to view all videos Xanim produces a header on startup, # send that to /dev/null so I don't see it video/*; xanim %s > /dev/null # Send html to a running firefox by remote text/html; firefox -remote 'openURL(%s)'; test=RunningFirefox # If I'm not running firefox but I am running X, start firefox on the # object text/html; firefox %s; test=RunningX # Else use lynx to view it as text text/html; lynx %s # This version would convert the text/html to text/plain text/html; lynx -dump %s; copiousoutput # I use enscript to print text in two columns to a page text/*; more %s; print=enscript -2Gr %s # Firefox adds a flag to tell itself to view jpegs internally image/jpeg;xv %s; x-mozilla-flags=internal # Use xv to view images if I'm running X # In addition, this uses the \ to extend the line and set my editor # for images image/*;xv %s; test=RunningX; \ edit=xpaint %s # Convert images to text using the netpbm tools image/*; (anytopnm %s | pnmscale -xysize 80 46 | ppmtopgm | pgmtopbm | pbmtoascii -1x2 ) 2>&1 ; copiousoutput # Send excel spreadsheets to my NT box application/ms-excel; open.pl %s MIME Autoview Usage: auto_view mimetype mimetype unauto_view * mimetype In addition to explicitly telling NeoMutt to view an attachment with the MIME viewer defined in the mailcap file from the attachments menu, NeoMutt has support for automatically viewing MIME attachments while in the pager. For this to work, you must define a viewer in the mailcap file which uses the copiousoutput option to denote that it is non-interactive. Usually, you also use the entry to convert the attachment to a text representation which you can view in the pager. You then use the auto_view configuration command to list the content-types that you wish to view automatically. For instance, if you set it to: auto_view text/html application/x-gunzip \ application/postscript image/gif application/x-tar-gz ...NeoMutt would try to find corresponding entries for rendering attachments of these types as text. A corresponding mailcap could look like: text/html; lynx -dump %s; copiousoutput; nametemplate=%s.html image/*; anytopnm %s | pnmscale -xsize 80 -ysize 50 | ppmtopgm | \ pgmtopbm | pbmtoascii ; copiousoutput application/x-gunzip; gzcat; copiousoutput application/x-tar-gz; gunzip -c %s | tar -tf - ; copiousoutput application/postscript; ps2ascii %s; copiousoutput unauto_view can be used to remove previous entries from the auto_view list. This can be used with message-hook to autoview messages based on size, etc. unauto_view*will remove all previous entries. MIME Multipart/Alternative The multipart/alternative container type only has child MIME parts which represent the same content in an alternative way. This is often used to send HTML messages which contain an alternative plain text representation. NeoMutt has some heuristics for determining which attachment of a multipart/alternative type to display: First, NeoMutt will check the alternative_order list to determine if one of the available types is preferred. It consists of a number of MIME types in order, including support for implicit and explicit wildcards. For example: alternative_order text/enriched text/plain text \ application/postscript image/* Next, NeoMutt will check if any of the types have a defined auto_view , and use that. Failing that, NeoMutt will look for any text type. As a last attempt, NeoMutt will look for any type it knows how to handle. To remove a MIME type from the alternative_order list, use the unalternative_order command. Attachment Searching and Counting If you ever lose track of attachments in your mailboxes, NeoMutt's attachment-counting and -searching support might be for you. You can make your message index display the number of qualifying attachments in each message, or search for messages by attachment count. You also can configure what kinds of attachments qualify for this feature with the attachments and unattachments commands. In order to provide this information, NeoMutt needs to fully MIME-parse all messages affected first. This can slow down operation especially for remote mail folders such as IMAP because all messages have to be downloaded first regardless whether the user really wants to view them or not though using usually means to download the message just once. The syntax is: attachments { + | - }disposition mime-type unattachments { + | - }disposition mime-type attachments ? disposition is the attachment's Content-Disposition type — either inline or attachment. You can abbreviate this to I or A. Disposition is prefixed by either a +symbol or a -symbol. If it's a +, you're saying that you want to allow this disposition and MIME type to qualify. If it's a -, you're saying that this disposition and MIME type is an exception to previous +rules. There are examples below of how this is useful. mime-type is the MIME type of the attachment you want the command to affect. A MIME type is always of the format major/minor, where major describes the broad category of document you're looking at, and minor describes the specific type within that category. The major part of mime-type must be literal text (or the special token * ), but the minor part may be a regular expression. (Therefore, */.* matches any MIME type.) The MIME types you give to the attachments directive are a kind of pattern. When you use the attachments directive, the patterns you specify are added to a list. When you use unattachments, the pattern is removed from the list. The patterns are not expanded and matched to specific MIME types at this time — they're just text in a list. They're only matched when actually evaluating a message. Some examples might help to illustrate. The examples that are not commented out define the default configuration of the lists. Attachment counting # Removing a pattern from a list removes that pattern literally. It # does not remove any type matching the pattern. # # attachments +A */.* # attachments +A image/jpeg # unattachments +A */.* # # This leaves "attached" image/jpeg files on the allowed attachments # list. It does not remove all items, as you might expect, because the # second */.* is not a matching expression at this time. # # Remember: "unattachments" only undoes what "attachments" has done! # It does not trigger any matching on actual messages. # Qualify any MIME part with an "attachment" disposition, EXCEPT for # text/x-vcard and application/pgp parts. (PGP parts are already known # to NeoMutt, and can be searched for with ~g, ~G, and ~k.) # # I've added x-pkcs7 to this, since it functions (for S/MIME) # analogously to PGP signature attachments. S/MIME isn't supported # in a stock NeoMutt build, but we can still treat it specially here. # attachments +A */.* attachments -A text/x-vcard application/pgp.* attachments -A application/x-pkcs7-.* # Discount all MIME parts with an "inline" disposition, unless they're # text/plain. (Why inline a text/plain part unless it's external to the # message flow?) attachments +I text/plain # These two lines make NeoMutt qualify MIME containers. (So, for example, # a message/rfc822 forward will count as an attachment.) The first # line is unnecessary if you already have "attach-allow */.*", of # course. These are off by default! The MIME elements contained # within a message/* or multipart/* are still examined, even if the # containers themselves don't qualify. #attachments +A message/.* multipart/.* #attachments +I message/.* multipart/.* ## You probably don't really care to know about deleted attachments. attachments -A message/external-body attachments -I message/external-body Entering the command attachments?as a command will list your current settings in neomuttrc format, so that it can be pasted elsewhere. MIME Lookup Usage: mime_lookup mimetype mimetype unmime_lookup * mimetype NeoMutt's mime_lookup list specifies a list of MIME types that should not be treated according to their mailcap entry. This option is designed to deal with binary types such as application/octet-stream. When an attachment's MIME type is listed in mime_lookup, then the extension of the filename will be compared to the list of extensions in the mime.types file. The MIME type associated with this extension will then be used to process the attachment according to the rules in the mailcap file and according to any other configuration options (such as auto_view) specified. Common usage would be: mime_lookup application/octet-stream application/X-Lotus-Manuscript In addition, the unmime_lookup command may be used to disable this feature for any particular MIME type if it had been set, for example, in a global .neomuttrc.
Optional Features General Notes Enabling/Disabling Features NeoMutt supports several of optional features which can be enabled or disabled at compile-time by giving the configure script certain arguments. These are listed in the Optional features section of the configure --help output. Which features are enabled or disabled can later be determined from the output of neomutt -v. If a compile option starts with +it is enabled and disabled if prefixed with -. For example, if NeoMutt was compiled using GnuTLS for encrypted communication instead of OpenSSL, neomutt -v would contain: -openssl +gnutls URL Syntax NeoMutt optionally supports the IMAP, POP3 and SMTP protocols which require to access servers using URLs. The canonical syntax for specifying URLs in NeoMutt is (an item enclosed in []means it is optional and may be omitted): proto[s]://[username[:password]@]server[:port][/path] proto is the communication protocol: imap for IMAP, pop for POP3 and smtp for SMTP. If s for secure communication is appended, NeoMutt will attempt to establish an encrypted communication using SSL or TLS. Since all protocols supported by NeoMutt support/require authentication, login credentials may be specified in the URL. This has the advantage that multiple IMAP, POP3 or SMTP servers may be specified (which isn't possible using, for example, $imap_user). The username may contain the @symbol being used by many mail systems as part of the login name. The special characters /( %2F), :( %3A) and %( %25) have to be URL-encoded in usernames using the %-notation. A password can be given, too but is not recommended if the URL is specified in a configuration file on disk. If no port number is given, NeoMutt will use the system's default for the given protocol (usually consulting /etc/services). The optional path is only relevant for IMAP and ignored elsewhere. URLs pops://host/ imaps://user@host/INBOX/Sent smtp://user@host:587/ SSL/TLS Support If NeoMutt is compiled with IMAP, POP3 and/or SMTP support, it can also be compiled with support for SSL or TLS using either OpenSSL or GnuTLS ( by running the configure script with the --enable-ssl=...option for OpenSSL or --enable-gnutls=...for GnuTLS). NeoMutt can then attempt to encrypt communication with remote servers if these protocols are suffixed with s for secure communication. POP3 Support NeoMutt has POP3 support and has the ability to work with mailboxes located on a remote POP3 server and fetch mail for local browsing. Remote POP3 servers can be accessed using URLs with the pop protocol for unencrypted and pops for encrypted communication, see for details. Polling for new mail is more expensive over POP3 than locally. For this reason the frequency at which NeoMutt will check for mail remotely can be controlled by the $pop_checkinterval variable, which defaults to every 60 seconds. POP is read-only which doesn't allow for some features like editing messages or changing flags. However, using and NeoMutt simulates the new/old/read flags as well as flagged and replied. NeoMutt applies some logic on top of remote messages but cannot change them so that modifications of flags are lost when messages are downloaded from the POP server (either by NeoMutt or other tools). Another way to access your POP3 mail is the <fetch-mail>function (default: G). It allows to connect to $pop_host, fetch all your new mail and place it in the local $spoolfile. After this point, NeoMutt runs exactly as if the mail had always been local. If you only need to fetch all messages to a local mailbox you should consider using a specialized program, such as fetchmail(1), getmail(1)or similar. IMAP Support NeoMutt has IMAP support and has the ability to work with folders located on a remote IMAP server. You can access the remote inbox by selecting the folder by its URL (see for details) using the imap or imaps protocol. Alternatively, a pine-compatible notation is also supported, i.e. {[username@]imapserver[:port][/ssl]}path/to/folder Note that not all servers use /as the hierarchy separator. NeoMutt should correctly notice which separator is being used by the server and convert paths accordingly. When browsing folders on an IMAP server, you can toggle whether to look at only the folders you are subscribed to, or all folders with the toggle-subscribed command. See also the $imap_list_subscribed variable. Polling for new mail on an IMAP server can cause noticeable delays. So, you'll want to carefully tune the $mail_check and $timeout variables. Reasonable values are: set mail_check=90 set timeout=15 with relatively good results even over slow modem lines. Note that if you are using mbox as the mail store on UW servers prior to v12.250, the server has been reported to disconnect a client if another client selects the same folder. The IMAP Folder Browser As of version 1.2, NeoMutt supports browsing mailboxes on an IMAP server. This is mostly the same as the local file browser, with the following differences: In lieu of file permissions, NeoMutt displays the string IMAP, possibly followed by the symbol +, indicating that the entry contains both messages and subfolders. On Cyrus-like servers folders will often contain both messages and subfolders. For the case where an entry can contain both messages and subfolders, the selection key (bound to enter by default) will choose to descend into the subfolder view. If you wish to view the messages in that folder, you must use view-file instead (bound to space by default). You can create, delete and rename mailboxes with the <create-mailbox>, <delete-mailbox>, and <rename-mailbox>commands (default bindings: C, d and r, respectively). You may also <subscribe>and <unsubscribe>to mailboxes (normally these are bound to s and u, respectively). Authentication NeoMutt supports four authentication methods with IMAP servers: SASL, GSSAPI, CRAM-MD5, and LOGIN. There is also support for the pseudo-protocol ANONYMOUS, which allows you to log in to a public IMAP server without having an account. To use ANONYMOUS, simply make your username blank or anonymous. SASL is a special super-authenticator, which selects among several protocols (including GSSAPI, CRAM-MD5, ANONYMOUS, and DIGEST-MD5) the most secure method available on your host and the server. Using some of these methods (including DIGEST-MD5 and possibly GSSAPI), your entire session will be encrypted and invisible to those teeming network snoops. It is the best option if you have it. To use it, you must have the Cyrus SASL library installed on your system and compile NeoMutt with the --with-sasl flag. NeoMutt will try whichever methods are compiled in and available on the server, in the following order: SASL, ANONYMOUS, GSSAPI, CRAM-MD5, LOGIN. There are a few variables which control authentication: $imap_user- controls the username under which you request authentication on the IMAP server, for all authenticators. This is overridden by an explicit username in the mailbox path (i.e. by using a mailbox name of the form {user@host}). $imap_pass- a password which you may preset, used by all authentication methods where a password is needed. $imap_authenticators- a colon-delimited list of IMAP authentication methods to try, in the order you wish to try them. If specified, this overrides NeoMutt's default (attempt everything, in the order listed above). SMTP Support Besides supporting traditional mail delivery through a sendmail-compatible program, NeoMutt supports delivery through SMTP. If the configuration variable $smtp_url is set, NeoMutt will contact the given SMTP server to deliver messages; if it is unset, NeoMutt will use the program specified by $sendmail. For details on the URL syntax, please see . The built-in SMTP support supports encryption (the smtps protocol using SSL or TLS) as well as SMTP authentication using SASL. The authentication mechanisms for SASL are specified in $smtp_authenticators defaulting to an empty list which makes NeoMutt try all available methods from most-secure to least-secure. Managing Multiple Accounts Usage: account-hook regex command If you happen to have accounts on multiple IMAP, POP and/or SMTP servers, you may find managing all the authentication settings inconvenient and error-prone. The account-hook command may help. This hook works like folder-hook but is invoked whenever NeoMutt needs to access a remote mailbox (including inside the folder browser), not just when you open the mailbox. This includes (for example) polling for new mail, storing Fcc messages and saving messages to a folder. As a consequence, account-hook should only be used to set connection-related settings such as passwords or tunnel commands but not settings such as sender address or name (because in general it should be considered unpredictable which account-hook was last used). Some examples: account-hook . 'unset imap_user; unset imap_pass; unset tunnel' account-hook imap://host1/ 'set imap_user=me1 imap_pass=foo' account-hook imap://host2/ 'set tunnel="ssh host2 /usr/libexec/imapd"' account-hook smtp://user@host3/ 'set tunnel="ssh host3 /usr/libexec/smtpd"' To manage multiple accounts with, for example, different values of $record or sender addresses, folder-hook has to be be used together with the mailboxes command. Managing multiple accounts mailboxes imap://user@host1/INBOX folder-hook imap://user@host1/ 'set folder=imap://host1/ ; set record=+INBOX/Sent' mailboxes imap://user@host2/INBOX folder-hook imap://user@host2/ 'set folder=imap://host2/ ; set record=+INBOX/Sent' In example the folders are defined using mailboxes so NeoMutt polls them for new mail. Each folder-hook triggers when one mailbox below each IMAP account is opened and sets $folder to the account's root folder. Next, it sets $record to the INBOX/Sent folder below the newly set $folder. Please notice that the value the + mailbox shortcut refers to depends on the current value of $folder and therefore has to be set separately per account. Setting other values like $from or $signature is analogous to setting $record. Local Caching NeoMutt contains two types of local caching: (1)the so-called header caching and (2)the so-called body caching which are both described in this section. Header caching is optional as it depends on external libraries, body caching is always enabled if NeoMutt is compiled with POP and/or IMAP support as these use it (body caching requires no external library). Header Caching NeoMutt provides optional support for caching message headers for the following types of folders: IMAP, POP, Maildir and MH. Header caching greatly speeds up opening large folders because for remote folders, headers usually only need to be downloaded once. For Maildir and MH, reading the headers from a single file is much faster than looking at possibly thousands of single files (since Maildir and MH use one file per message.) Header caching can be enabled by configuring one of the database backends. One of tokyocabinet, kyotocabinet, qdbm, gdbm, lmdb or bdb. If enabled, $header_cache can be used to either point to a file or a directory. If set to point to a file, one database file for all folders will be used (which may result in lower performance), but one file per folder if it points to a directory. Additionally, $header_cache_backend can be used to specify which backend to use. The list of available backends can be specified at configure time with a set of --with-<backend> options. Currently, the following backends are supported: tokyocabinet, kyotocabinet, qdbm, gdbm, bdb, lmdb. Body Caching Both cache methods can be combined using the same directory for storage (and for IMAP/POP even provide meaningful file names) which simplifies manual maintenance tasks. In addition to caching message headers only, NeoMutt can also cache whole message bodies. This results in faster display of messages for POP and IMAP folders because messages usually have to be downloaded only once. For configuration, the variable $message_cachedir must point to a directory. There, NeoMutt will create a hierarchy of subdirectories named like the account and mailbox path the cache is for. Cache Directories For using both, header and body caching, $header_cache and $message_cachedir can be safely set to the same value. In a header or body cache directory, NeoMutt creates a directory hierarchy named like: proto:user@hostname where proto is either pop or imap.Within there, for each folder, NeoMutt stores messages in single files and header caches in files with the .hcache extension. All files can be removed as needed if the consumed disk space becomes an issue as NeoMutt will silently fetch missing items again. Pathnames are always stored in UTF-8 encoding. For Maildir and MH, the header cache files are named after the MD5 checksum of the path. Maintenance NeoMutt does not (yet) support maintenance features for header cache database files so that files have to be removed in case they grow too big. It depends on the database library used for header caching whether disk space freed by removing messages is re-used. For body caches, NeoMutt can keep the local cache in sync with the remote mailbox if the $message_cache_clean variable is set. Cleaning means to remove messages from the cache which are no longer present in the mailbox which only happens when other mail clients or instances of NeoMutt using a different body cache location delete messages (NeoMutt itself removes deleted messages from the cache when syncing a mailbox). As cleaning can take a noticeable amount of time, it should not be set in general but only occasionally. Sending Anonymous Messages via Mixmaster You may also have compiled NeoMutt to co-operate with Mixmaster, an anonymous remailer. Mixmaster permits you to send your messages anonymously using a chain of remailers. Mixmaster support in NeoMutt is for mixmaster version 2.04 or later. To use it, you'll have to obey certain restrictions. Most important, you cannot use the Cc and Bcc headers. To tell NeoMutt to use mixmaster, you have to select a remailer chain, using the mix function on the compose menu. The chain selection screen is divided into two parts. In the (larger) upper part, you get a list of remailers you may use. In the lower part, you see the currently selected chain of remailers. You can navigate in the chain using the <chain-prev>and <chain-next>functions, which are by default bound to the left and right arrows and to the h and l keys (think vi keyboard bindings). To insert a remailer at the current chain position, use the <insert>function. To append a remailer behind the current chain position, use <select-entry>or <append>. You can also delete entries from the chain, using the corresponding function. Finally, to abandon your changes, leave the menu, or <accept>them pressing (by default) the Return key. Note that different remailers do have different capabilities, indicated in the %c entry of the remailer menu lines (see $mix_entry_format). Most important is the middleman capability, indicated by a capital M: This means that the remailer in question cannot be used as the final element of a chain, but will only forward messages to other mixmaster remailers. For details on the other capabilities, please have a look at the mixmaster documentation. Attach Headers Color Feature Color attachment headers using regex, just like mail bodies Support Since:NeoMutt 2016-09-10 Dependencies:None Introduction This feature allows specifying regexes to color attachment headers just like the mail body would. The headers are the parts colored by the attachment color. Coloring them is useful to highlight the results of GPGME's signature checks or simply the mimetype or size of the attachment. Only the part matched by the regex is colored. Usage The attach_headers color should be used just like the body color. color attach_headers foreground background pattern neomuttrc # Example NeoMutt config file for the attach-headers-color feature. # Color if the attachment is autoviewed color attach_headers brightgreen default "Autoview" # Color only the brackets around the headers color attach_headers brightyellow default "^\\[--" color attach_headers brightyellow default "--]$" # Color the mime type and the size color attach_headers green default "Type: [a-z]+/[a-z0-9\-]+" color attach_headers green default "Size: [0-9\.]+[KM]" # Color GPGME signature checks color attach_headers brightgreen default "Good signature from.*" color attach_headers brightred default "Bad signature from.*" color attach_headers brightred default "BAD signature from.*" color attach_headers brightred default "Note: This key has expired!" color attach_headers brightmagenta default "Problem signature from.*" color attach_headers brightmagenta default "WARNING: This key is not certified with a trusted signature!" color attach_headers brightmagenta default " There is no indication that the signature belongs to the owner." color attach_headers brightmagenta default "can't handle these multiple signatures" color attach_headers brightmagenta default "signature verification suppressed" color attach_headers brightmagenta default "invalid node with packet of type" # vim: syntax=neomuttrc See Also Color command Regular Expressions Known Bugs None Credits Guillaume Brogi gui-gui@netcourrier.com Compose to Sender Feature Send new mail to the sender of the current mail Support Since:NeoMutt 2016-10-02 Dependencies:None Introduction The compose-to-sender feature adds a new command to start composing a new email to the sender of the current message. This is not a reply, but a new, separate, message. It works on tagged messages too, sending one email to all of the senders of the tagged messages. Functions compose-to-sender adds the following function to NeoMutt. By default, it is not bound to a key. compose-to-sender Functions Menus Function Description index,pager <compose-to-sender> compose a new email to the sender of the current email
neomuttrc # Example NeoMutt config file for the compose-to-sender feature. # -------------------------------------------------------------------------- # FUNCTIONS - shown with an example mapping # -------------------------------------------------------------------------- # Compose a new email (not a reply) to the sender bind index,pager @ compose-to-sender # vim: syntax=neomuttrc Known Bugs None Credits Brian Medley Guillaume Brogi gui-gui@netcourrier.com
Compressed Folders Feature Read from/write to compressed mailboxes Support Since:NeoMutt 2016-05-30 Dependencies:None Introduction The Compressed Folder feature allows NeoMutt to read mailbox files that are compressed. But it isn't limited to compressed files. It works well with encrypted files, too. In fact, if you can create a program/script to convert to and from your format, then NeoMutt can read it. The feature adds three hooks to NeoMutt: open-hook, close-hook and append-hook. They define commands to: uncompress a file; compress a file; append messages to an already compressed file. There are some examples of both compressed and encrypted files, later. For now, the documentation will just concentrate on compressed files. Commands open-hook pattern shell-command close-hook pattern shell-command append-hook pattern shell-command The shell-command must contain two placeholders for filenames: %f and %t. These represent from and to filenames. These placeholders should be placed inside single-quotes to prevent unintended shell expansions. If you need the exact string %f or %t in your command, simply double up the %character, e.g. %%f or %%t. Not all Hooks are Required Open Close Append Effect Useful if Open - - Folder is readonly The folder is just a backup Open Close - Folder is read/write, but the entire folder must be written if anything is changed Your compression format doesn't support appending Open Close Append Folder is read/write and emails can be efficiently added to the end Your compression format supports appending Open - Append Folder is readonly, but can be appended to You want to store emails, but never change them
The command: should return a non-zero exit status on failure should not delete any files Read from compressed mailbox open-hook regex shell-command If NeoMutt is unable to open a file, it then looks for open-hook that matches the filename. If your compression program doesn't have a well-defined extension, then you can use .as the regex. Example of <literal>open-hook</literal> open-hook '\.gz$' "gzip --stdout --decompress '%f' > '%t'" NeoMutt finds a file, example.gz, that it can't read NeoMutt has an open-hook whose regex matches the filename: \.gz$ NeoMutt uses the command gzip -cd to create a temporary file that it can read Write to a compressed mailbox close-hook regex shell-command When NeoMutt has finished with a compressed mail folder, it will look for a matching close-hook to recompress the file. This hook is optional. If the folder has not been modified, the close-hook will not be called. Example of <literal>close-hook</literal> close-hook '\.gz$' "gzip --stdout '%t' > '%f'" NeoMutt has finished with a folder, example.gz, that it opened with open-hook The folder has been modified NeoMutt has a close-hook whose regex matches the filename: \.gz$ NeoMutt uses the command gzip -c to create a new compressed file The close-hook can also include extra options, e.g. compression level: --best Append to a compressed mailbox append-hook regex shell-command When NeoMutt wants to append an email to a compressed mail folder, it will look for a matching append-hook. This hook is optional. Using the append-hook will save time, but NeoMutt won't be able to determine the type of the mail folder inside the compressed file. NeoMutt will assume the type to be that of the $mbox_type variable. NeoMutt also uses this type for temporary files. NeoMutt will only use the append-hook for existing files. The close-hook will be used for empty, or missing files. If your command writes to stdout, it is vital that you use >>in the append-hook. If not, data will be lost. Example of <literal>append-hook</literal> append-hook '\.gz$' "gzip --stdout '%t' >> '%f'" NeoMutt wants to append an email to a folder, example.gz, that it opened with open-hook NeoMutt has an append-hook whose regex matches the filename: \.gz$ NeoMutt knows the mailbox type from the $mbox variable NeoMutt uses the command gzip -c to append to an existing compressed file The append-hook can also include extra options, e.g. compression level: --best Empty Files NeoMutt assumes that an empty file is not compressed. In this situation, unset $save_empty, so that the compressed file will be removed if you delete all of the messages. Security Encrypted files are decrypted into temporary files which are stored in the $tmpdir directory. This could be a security risk.
neomuttrc # Example NeoMutt config file for the compress feature. # This feature adds three hooks to NeoMutt which allow it to # work with compressed, or encrypted, mailboxes. # The hooks are of the form: # open-hook regex "shell-command" # close-hook regex "shell-command" # append-hook regex "shell-command" # The 'append-hook' is optional. # Handler for gzip compressed mailboxes open-hook '\.gz$' "gzip --stdout --decompress '%f' > '%t'" close-hook '\.gz$' "gzip --stdout '%t' > '%f'" append-hook '\.gz$' "gzip --stdout '%t' >> '%f'" # Handler for bzip2 compressed mailboxes open-hook '\.bz2$' "bzip2 --stdout --decompress '%f' > '%t'" close-hook '\.bz2$' "bzip2 --stdout '%t' > '%f'" append-hook '\.bz2$' "bzip2 --stdout '%t' >> '%f'" # Handler for xz compressed mailboxes open-hook '\.xz$' "xz --stdout --decompress '%f' > '%t'" close-hook '\.xz$' "xz --stdout '%t' > '%f'" append-hook '\.xz$' "xz --stdout '%t' >> '%f'" # Handler for pgp encrypted mailboxes # PGP does not support appending to an encrypted file open-hook '\.pgp$' "pgp -f < '%f' > '%t'" close-hook '\.pgp$' "pgp -fe YourPgpUserIdOrKeyId < '%t' > '%f'" # Handler for gpg encrypted mailboxes # gpg does not support appending to an encrypted file open-hook '\.gpg$' "gpg --decrypt < '%f' > '%t'" close-hook '\.gpg$' "gpg --encrypt --recipient YourGpgUserIdOrKeyId < '%t' > '%f'" # vim: syntax=neomuttrc See Also Regular Expressions $tmpdir $mbox_type $save_empty folder-hook Credits Roland Rosenfeld roland@spinnaker.de Alain Penders Alain@Finale-Dev.com Christoph Myon Berg myon@debian.org Evgeni Golov evgeni@debian.org Richard Russon rich@flatcap.org
Conditional Dates Feature Use rules to choose date format Support Since:NeoMutt 2016-03-07 Dependencies: nested-if feature Introduction The Conditional Dates feature allows you to construct $index_format expressions based on the age of the email. NeoMutt's default $index_format displays email dates in the form: abbreviated-month day-of-month — Jan 14. The format is configurable but only per-mailbox. This feature allows you to configure the display depending on the age of the email. Potential Formatting Scheme Email Sent Format Example Today %H:%M 13:23 This Month %a %d Thu 17 This Year %b %d Dec 10 Older than 1 Year %m/%y 06/14
For an explanation of the date formatting strings, see strftime(3). By carefully picking your formats, the dates can remain unambiguous and compact. NeoMutt's conditional format strings have the form: (whitespace introduced for clarity) %? TEST ? TRUE & FALSE ? The examples below use the test %[— the date of the message in the local timezone. They will also work with %(— the local time that the message arrived. The date tests are of the form: %[nX? TRUE & FALSE ? n is an optional count (defaults to 1 if missing) X is the time period Date Formatting Codes Letter Time Period y Years m Months w Weeks d Days H Hours M Minutes
Example Date Tests Test Meaning %[y This year %[1y This year %[6m In the last 6 months %[w This week %[d Today %[4H In the last 4 hours
Example 1 We start with a one-condition test. Example 1 Test Date Range Format String Example %[1m This month %[%b %d] Dec 10 Older %[%Y-%m-%d] 2015-04-23
The $index_format string would contain: %?[1m?%[%b %d]&%[%Y-%m-%d]? Reparsed a little, for clarity, you can see the test condition and the two format strings. %?[1m? & ? %[%b %d] %[%Y-%m-%d]
Example 2 This example contains three test conditions and four date formats. Example 2 Test Date Range Format String Example %[d Today %[%H:%M ] 12:34 %[m This month %[%a %d] Thu 12 %[y This year %[%b %d] Dec 10 Older %[%m/%y ] 06/15
The $index_format string would contain: %<[y?%<[m?%<[d?%[%H:%M ]&%[%a %d]>&%[%b %d]>&%[%m/%y ]> Reparsed a little, for clarity, you can see the test conditions and the four format strings. %<[y? &%[%m/%y ]> Older %<[m? &%[%b %d]> This year %<[d? &%[%a %d]> This month %[%H:%M ] Today This a another view of the same example, with some whitespace for clarity. %<[y? %<[m? %<[d? AAA & BBB > & CCC > & DDD > AAA = %[%H:%M ] BBB = %[%a %d] CCC = %[%b %d] DDD = %[%m/%y ]
Variables The Conditional Dates feature doesn't have any config of its own. It modifies the behavior of the format strings. neomuttrc # Example NeoMutt config file for the cond-date feature. # # The default index_format is: # '%4C %Z %{%b %d} %-15.15L (%?l?%4l&%4c?) %s' # # We replace the date field '%{%b %d}', giving: set index_format='%4C %Z %<[y?%<[m?%<[d?%[%H:%M ]&%[%a %d]>&%[%b %d]>&%[%m/%y ]> %-15.15L (%?l?%4l&%4c?) %s' # Test Date Range Format String Example # -------------------------------------------- # %[d Today %[%H:%M ] 12:34 # %[m This month %[%a %d] Thu 12 # %[y This year %[%b %d] Dec 10 # - Older %[%m/%y ] 06/15 # vim: syntax=neomuttrc See Also $index_format nested-if feature strftime(3) Known Bugs Date parsing doesn't quite do what you expect. 1w doesn't mean the in the last 7 days, but this week. This doesn't match the normal NeoMutt behavior: for example ~d>1w means emails dated in the last 7 days. Credits Aaron Schrab aaron@schrab.com Eric Davis edavis@insanum.com Richard Russon rich@flatcap.org
Encrypt-to-Self Feature Save a self-encrypted copy of emails Support Since:NeoMutt 2016-07-23 Dependencies:None Introduction Once you encrypt an email to someone you cannot read it. This is good for security, but bad for record-keeping. If you wanted to keep a copy of an encrypted email you could set $fcc_clear. A better option is to enable $smime_self_encrypt, then set $smime_self_encrypt_as to your personal S/MIME key id. set smime_self_encrypt = yes set smime_self_encrypt_as = bb345e23.0 Or, if you use PGP, $pgp_self_encrypt, then set $pgp_self_encrypt_as to your personal PGP key id. set pgp_self_encrypt = yes set pgp_self_encrypt_as = A4AF18C5582473BD35A1E9CE78BB3D480042198E Variables encrypt-self Variables Name Type Default smime_self_encrypt boolean no smime_self_encrypt_as string (empty) pgp_self_encrypt boolean no pgp_self_encrypt_as string (empty)
neomuttrc # Example NeoMutt config file for the encrypt-to-self feature. # -------------------------------------------------------------------------- # VARIABLES - shown with their default values # -------------------------------------------------------------------------- # Save a copy of outgoing email, encrypted to yourself set smime_self_encrypt = "no" # set smime_self_encrypt_as = "SMIME-KEY" # Save a copy of outgoing email, encrypted to yourself set pgp_self_encrypt = "no" set pgp_self_encrypt_as = "PGP-KEY" # vim: syntax=neomuttrc Known Bugs None Credits Omen Wild omen@mandarb.com Richard Russon rich@flatcap.org Guillaume Brogi gui-gui@netcourrier.com
Fmemopen Feature Replace some temporary files with memory buffers Support Since:NeoMutt 2016-03-07 Dependencies: open_memstream(), fmemopen()from glibc This feature can be enabled by running configure with the option --enable-fmemopen Introduction The fmemopen feature speeds up some searches. This feature changes a few places where NeoMutt creates temporary files. It replaces them with in-memory buffers. This should improve the performance when searching the header or body using the $thorough_search option. There are no user-configurable parts. This feature depends on open_memstream()and fmemopen(). They are provided by glibc. Without them, NeoMutt will simply create temporary files. See Also Compile-Time Features fmemopen(3) Known Bugs debian bug 834408 Credits Julius Plenz plenz@cis.fu-berlin.de Richard Russon rich@flatcap.org Forgotten Attachment Feature Alert user when (s)he forgets to attach a file to an outgoing email. Support Since:NeoMutt 2016-09-10 Dependencies:None Introduction The forgotten-attachment feature provides a new setting for NeoMutt that alerts the user if the message body contains a certain keyword but there are no attachments added. This is meant to ensure that the user does not forget to attach a file after promising to do so in the mail. The attachment keyword will not be scanned in text matched by $quote_regexp. Variables forgotten-attachment Variables Name Type Default attach_keyword regular expression \\<(attach|attached|attachments?)\\> abort_noattach quadoption no
neomuttrc # Example NeoMutt config file for the forgotten-attachment feature. # The 'forgotten-attachment' feature provides a new setting for NeoMutt that # alerts the user if the message body contains a certain regular expression but there are # no attachments added. This is meant to ensure that the user does not forget # to attach a file after promising to do so in the mail. # Ask if the user wishes to abort sending if $attach_keyword is found in the # body, but no attachments have been added # It can be set to: # "yes" : always abort # "ask-yes" : ask whether to abort # "no" : send the mail set abort_noattach = no # Search for the following regular expression in the body of the email # English: attach, attached, attachment, attachments set attach_keyword = "\\<attach(|ed|ments?)\\>" # Nederlands: # set attach_keyword = "\\<(bijvoegen|bijgevoegd|bijlage|bijlagen)\\>" # Deutsch: # set attach_keyword = "\\<(anhängen|angehängt|anhang|anhänge|hängt an)\\>" # Français: # set attach_keyword = "\\<(attaché|attachés|attache|attachons|joint|jointe|joints|jointes|joins|joignons)\\>" # vim: syntax=neomuttrc See Also The Attachment Menu The Attachment Menu key mappings Known Bugs None Credits Darshit Shah darnir@gmail.com Richard Russon rich@flatcap.org Johannes Weißl jargon@molb.org Steven! Ragnarök steven@nuclearsandwich.com
Global Hooks Define actions to run globally within NeoMutt Introduction These hooks are called when global events take place in NeoMutt. Run a command... timeout-hook- periodically startup-hook- when NeoMutt starts up, before opening the first mailbox shutdown-hook- NeoMutt shuts down, before closing the last mailbox Timeout Hook Run a command periodically Since:NeoMutt 2016-08-08 This feature implements a new hook that is called periodically when NeoMutt checks for new mail. This hook is called every $timeout seconds. Startup Hook Run a command when NeoMutt starts up, before opening the first mailbox Since:NeoMutt 2016-11-25 This feature implements a new hook that is called when NeoMutt first starts up, but before opening the first mailbox. This is most likely to be useful to users of notmuch. Shutdown Hook Run a command when NeoMutt shuts down, before closing the last mailbox Since:NeoMutt 2016-11-25 This feature implements a hook that is called when NeoMutt shuts down, but before closing the first mailbox. This is most likely to be useful to users of notmuch. Commands timeout-hook NEOMUTT-COMMAND startup-hook NEOMUTT-COMMAND shutdown-hook NEOMUTT-COMMAND neomuttrc # Example NeoMutt config file for the global hooks feature. # -------------------------------------------------------------------------- # COMMANDS - shown with an example argument # -------------------------------------------------------------------------- # After $timeout seconds of inactivity, run this NeoMutt command timeout-hook 'exec sync-mailbox' # When NeoMutt first loads, run this NeoMutt command startup-hook 'exec sync-mailbox' # When NeoMutt quits, run this NeoMutt command shutdown-hook 'exec sync-mailbox' # vim: syntax=neomuttrc See Also $timeout Known Bugs None Credits Armin Wolfermann armin@wolfermann.org Richard Russon rich@flatcap.org Thomas Adam thomas@xteddy.org Ifdef Feature Conditional config options Support Since:NeoMutt 2016-03-07 Dependencies:None Introduction The ifdef feature introduces three new commands to NeoMutt and allow you to share one config file between versions of NeoMutt that may have different features compiled in. ifdef symbol config-command [args...] # If a symbol is defined ifndef symbol config-command [args...] # If a symbol is not defined finish # Finish reading the current file Here a symbol can be a $variable, <function>, command or compile-time symbol, such as imap. A list of compile-time symbols can be seen in the output of the command neomutt -v (in the Compile options section). finish is particularly useful when combined with ifndef. e.g. # Sidebar config file ifndef sidebar finish Commands ifdef symbol "config-command [args]" ifndef symbol "config-command [args]" finish neomuttrc # Example NeoMutt config file for the ifdef feature. # This feature introduces three useful commands which allow you to share # one config file between versions of NeoMutt that may have different # features compiled in. # ifdef symbol config-command [args...] # ifndef symbol config-command [args...] # finish # The 'ifdef' command tests whether NeoMutt understands the name of # a variable, function, command or compile-time symbol. # If it does, then it executes a config command. # The 'ifndef' command tests whether a symbol does NOT exist. # The 'finish' command tells NeoMutt to stop reading current config file. # If the 'trash' variable exists, set it. ifdef trash 'set trash=~/Mail/trash' # If the 'tag-pattern' function exists, bind a key to it. ifdef tag-pattern 'bind index <F6> tag-pattern' # If the 'imap-fetch-mail' command exists, read my IMAP config. ifdef imap-fetch-mail 'source ~/.neomutt/imap.rc' # If the compile-time symbol 'sidebar' does not exist, then # stop reading the current config file. ifndef sidebar finish # vim: syntax=neomuttrc Known Bugs None Credits Cedric Duval cedricduval@free.fr Matteo F. Vescovi mfvescovi@gmail.com Richard Russon rich@flatcap.org Index Color Feature Custom rules for theming the email index Support Since:NeoMutt 2016-03-07 Dependencies: status-color feature Introduction The index-color feature allows you to specify colors for individual parts of the email index. e.g. Subject, Author, Flags. First choose which part of the index you'd like to color. Then, if needed, pick a pattern to match. Note: The pattern does not have to refer to the object you wish to color. e.g. color index_author red default "~sneomutt" The author appears red when the subject (~s) contains neomutt. Colors All the colors default to default, i.e. unset. The index objects can be themed using the color command. Some objects require a pattern. color index-object foreground background color index-object foreground background pattern Index Colors Object Pattern Highlights index yes Entire index line index_author yes Author name, %A %a %F %L %n index_collapsed no Number of messages in a collapsed thread, %M index_date no Date field index_flags yes Message flags, %S %Z index_label no Message label, %y %Y index_number no Message number, %C index_size no Message size, %c %l index_subject yes Subject, %s
neomuttrc # Example NeoMutt config file for the index-color feature. # Entire index line color index white black '.*' # Author name, %A %a %F %L %n # Give the author column a dark grey background color index_author default color234 '.*' # Highlight a particular from (~f) color index_author brightyellow color234 '~fRay Charles' # Message flags, %S %Z # Highlight the flags for flagged (~F) emails color index_flags default red '~F' # Subject, %s # Look for a particular subject (~s) color index_subject brightcyan default '~s\(closes #[0-9]+\)' # Number of messages in a collapsed thread, %M color index_collapsed default brightblue # Date field color index_date green default # Message label, %y %Y color index_label default brightgreen # Message number, %C color index_number red default # Message size, %c %l color index_size cyan default # vim: syntax=neomuttrc See Also Regular Expressions Patterns $index_format Color command Status-Color feature Known Bugs None Credits Christian Aichinger Greek0@gmx.net Christoph Myon Berg myon@debian.org Elimar Riesebieter riesebie@lxtec.de Eric Davis edavis@insanum.com Vladimir Marek Vladimir.Marek@oracle.com Richard Russon rich@flatcap.org
Initials Expando Feature Expando for author's initials Support Since:NeoMutt 2016-03-07 Dependencies:None Introduction The initials feature adds an expando (%I) for an author's initials. The index panel displays a list of emails. Its layout is controlled by the $index_format variable. Using this expando saves space in the index panel. This can be useful if you are regularly working with a small set of people. Variables This feature has no config of its own. It adds an expando which can be used in the $index_format variable. neomuttrc # Example NeoMutt config file for the initials feature. # The 'initials' feature has no config of its own. # It adds an expando for an author's initials, # which can be used in the 'index_format' variable. # The default 'index_format' is: set index_format='%4C %Z %{%b %d} %-15.15L (%?l?%4l&%4c?) %s' # Where %L represents the author/recipient # This might look like: # 1 + Nov 17 David Bowie Changesbowie ( 689) # 2 ! Nov 17 Stevie Nicks Rumours ( 555) # 3 + Nov 16 Jimi Hendrix Voodoo Child ( 263) # 4 + Nov 16 Debbie Harry Parallel Lines ( 540) # Using the %I expando: set index_format='%4C %Z %{%b %d} %I (%?l?%4l&%4c?) %s' # This might look like: # 1 + Nov 17 DB Changesbowie ( 689) # 2 ! Nov 17 SN Rumours ( 555) # 3 + Nov 16 JH Voodoo Child ( 263) # 4 + Nov 16 DH Parallel Lines ( 540) # vim: syntax=neomuttrc See Also $index_format index-color feature folder-hook Known Bugs None Credits Vsevolod Volkov vvv@mutt.org.ua Richard Russon rich@flatcap.org Kyoto Cabinet Feature Kyoto Cabinet backend for the header cache Support Since:NeoMutt 2016-10-02 Dependencies: Kyoto Cabinet libraries To check if NeoMutt supports Kyoto Cabinet, look for kyoto in the NeoMutt version. +hcache in the compile options hcache backend: kyotocabinet in the NeoMutt version Introduction This feature adds support for using Kyoto Cabinet, the successor to Tokyo Cabinet, as a storage backend for NeoMutt's header cache (hcache). It is enabled at configure time with the --with-kyotocabinet=<path>switch. See Also Local Caching Kyoto Cabinet Known Bugs None Credits Clemens Lang neverpanic@gmail.com Limit Current Thread Feature Focus on one Email Thread Support Since:NeoMutt 2016-03-28 Dependencies:None Introduction This feature adds a new way of using the Limit Command. The <limit-current-thread>function restricts the view to just the current thread. Setting the limit (the l key) to all will restore the full email list. Functions Limit-current-thread adds the following function to NeoMutt. By default, it is not bound to a key. Limit-Current-Thread Functions Menus Function Description index <limit-current-thread> Limit view to current thread
neomuttrc # Example NeoMutt config file for the limit-current-thread feature. # Limit view to current thread bind index <esc>L limit-current-thread # vim: syntax=neomuttrc Known Bugs None Credits David Sterba dsterba@suse.cz Richard Russon rich@flatcap.org
LMDB Feature LMDB backend for the header cache Support Since:NeoMutt 2016-07-23 Dependencies:None Introduction This feature adds support for using LMDB as a storage backend for NeoMutt's header cache (hcache). It is enabled at configure time with the --with-lmdb=<path>switch. It is not recommended to store the lmdb database on a shared drive. See Also Local Caching Known Bugs None Credits Pietro Cerutti gahr@gahr.ch Jan-Piet Mens jp@mens.de Clemens Lang neverpanic@gmail.com Multiple FCC Feature Save multiple copies of outgoing mail Support Since:NeoMutt 2016-08-08 Dependencies:None Introduction This feature allows the user to save outgoing emails in multiple folders. Folders should be listed separated by commas, but no spaces. The fcc field of an email can be set in two ways: The <edit-fcc> command in the compose menu (default key: f) Creating a fcc-hook in your .neomuttrc See Also $record fcc-hook Known Bugs None Credits Omen Wild omen@mandarb.com Richard Russon rich@flatcap.org Nested If Feature Allow complex nested conditions in format strings Support Since:NeoMutt 2016-03-07 Dependencies:None Introduction NeoMutt's format strings can contain embedded if-then-else conditions. They are of the form: %?VAR?TRUE&FALSE? If the variable VAR has a value greater than zero, print the TRUE string, otherwise print the FALSE string. e.g. %?S?Size: %S&Empty? Which can be read as: if (%S > 0) { print "Size: %S" } else { print "Empty" } These conditions are useful, but in NeoMutt they cannot be nested within one another. This feature uses the notation %<VAR?TRUE&FALSE>and allows them to be nested. The %<...>notation was used to format the current local time. but that's not really very useful since NeoMutt has no means of refreshing the screen periodically. A simple nested condition might be: (Some whitespace has been introduced for clarity) %<x? %<y? XY & X > & %<y? Y & NONE > > Conditions %<y? XY & X > x>0 XY x>0,y>0 X x>0,y=0 %<x? %<y? XY & X > & %<y? Y & NONE > > Conditions %<y? Y & NONE > x=0 Y x=0,y>0 NONE x=0,y=0 Equivalent to: if (x > 0) { if (y > 0) { print 'XY' } else { print 'X' } } else { if (y > 0) { print 'Y' } else { print 'NONE' } } Examples: set index_format='%4C %Z %{%b %d} %-25.25n %s%> %<M?%M Msgs &%<l?%l Lines&%c Bytes>>' if a thread is folded display the number of messages (%M) else if we know how many lines in the message display lines in message (%l) else display the size of the message in bytes (%c) set index_format='%4C %Z %{%b %d} %-25.25n %<M?[%M] %s&%s%* %<l?%l&%c>>' if a thread is folded display the number of messages (%M) and the subject (%s) else if we know how many lines are in the message display subject (%s) and the lines in message (%l) else display the subject (%s) and the size of the message in bytes (%c) If you wish to use angle brackets < > in a nested condition, then it's necessary to escape them, e.g. set index_format='%<M?\<%M\>&%s>' Variables The nested-if feature doesn't have any config of its own. It modifies the behavior of the format strings. neomuttrc # Example NeoMutt config file for the nested-if feature. # This feature uses the format: '%<VAR?TRUE&FALSE>' for conditional # format strings that can be nested. # Example 1 # if a thread is folded # display the number of messages (%M) # else if we know how many lines in the message # display lines in message (%l) # else display the size of the message in bytes (%c) set index_format='%4C %Z %{%b %d} %-25.25n %s%> %<M?%M Msgs &%<l?%l Lines&%c Bytes>>' # Example 2 # if a thread is folded # display the number of messages (%M) # display the subject (%s) # else if we know how many lines in the message # display lines in message (%l) # else # display the size of the message in bytes (%c) set index_format='%4C %Z %{%b %d} %-25.25n %<M?[%M] %s&%s%* %<l?%l&%c>>' # vim: syntax=neomuttrc See Also cond-date feature $index_format $status_format Known Bugs This feature is hard to understand Credits David Champion dgc@uchicago.edu Richard Russon rich@flatcap.org New Mail Feature Execute a command upon the receipt of new mail. Support Since:NeoMutt 2016-07-23 Dependencies:None Introduction This feature enables the new_mail_command setting, which can be used to execute a custom script (e.g. a notification handler) upon receiving a new mail. The command string can contain expandos, such as %n for the number of new messages. For a complete list, see: $status_format. When the notification is sent, the folder of the new mail is no longer known. This is a limitation of NeoMutt. The `%f` expando will show the open folder. For example in Linux you can use (most distributions already provide notify-send): set new_mail_command="notify-send --icon='/home/santiago/Pictures/neomutt.png' 'New Email' '%n new messages, %u unread.' &" And in OS X you will need to install a command line interface for Notification Center, for example terminal-notifier: set new_mail_command="terminal-notifier -title '%v' -subtitle 'New Mail' -message '%n new messages, %u unread.' -activate 'com.apple.Terminal'" Variables New Mail Command Variables Name Type Default new_mail_command string (empty)
neomuttrc # Example NeoMutt config file for the new-mail feature. # -------------------------------------------------------------------------- # VARIABLES - shown with their default values # -------------------------------------------------------------------------- # Set the command you want NeoMutt to execute upon the receipt of a new email set new_mail_command = "" # Linux example: # set new_command="notify-send --icon='/home/santiago/Pictures/neomutt.png' 'New Email in %f' '%n new messages, %u unread.' &" # OS X example: # set new_mail_command="terminal-notifier -title '%v' -subtitle 'New Mail in %f' -message '%n new messages, %u unread.' -activate 'com.apple.Terminal'" # -------------------------------------------------------------------------- # vim: syntax=neomuttrc See Also folder-hook Known Bugs None Credits Yoshiki Vazquez-Baeza yoshiki@ucsd.edu Santiago Torres-Arias santiago@nyu.edu Richard Russon rich@flatcap.org
NNTP Feature Talk to a Usenet news server Support Since:NeoMutt 2016-05-30 Dependencies:None Introduction Reading news via NNTP NeoMutt can read from a news server using NNTP. The default news server can be obtained from the $NNTPSERVER environment variable or from the /etc/nntpserver file. Like in other news readers, information about the subscribed newsgroups is saved in the file specified by the $newsrc variable. You can open a newsgroup with the function <change-newsgroup> The variable $news_cache_dir can be used to point to a directory. NeoMutt will create a hierarchy of subdirectories named like the account and newsgroup the cache is for. The hierarchy is also used to store header cache if NeoMutt was compiled with header cache support. Variables NNTP Variables Name Type Default ask_follow_up boolean no ask_x_comment_to boolean no catchup_newsgroup quad ask-yes followup_to_poster quad ask-yes group_index_format string %4C %M%N %5s %-45.45f %d inews string (empty) mime_subject boolean yes newsgroups_charset string utf-8 newsrc string ~/.newsrc news_cache_dir string ~/.neomutt news_server string (empty) nntp_authenticators string (empty) nntp_context number 1000 nntp_listgroup boolean yes nntp_load_description boolean yes nntp_pass string (empty) nntp_poll number 60 nntp_user string (empty) post_moderated quad ask-yes save_unsubscribed boolean no show_new_news boolean yes show_only_unread boolean no x_comment_to boolean no
Functions NNTP adds the following functions to NeoMutt. By default, none of them are bound to keys. NNTP Functions Menus Function Description browser,index <catchup> mark all articles in newsgroup as read index,pager <change-newsgroup> open a different newsgroup compose <edit-followup-to> edit the Followup-To field compose <edit-newsgroups> edit the newsgroups list compose <edit-x-comment-to> edit the X-Comment-To field attach,index,pager <followup-message> followup to newsgroup index,pager <post-message> post message to newsgroup browser <reload-active> load list of all newsgroups from NNTP server browser <subscribe> subscribe to current mbox (IMAP/NNTP only) browser <subscribe-pattern> subscribe to newsgroups matching a pattern browser <uncatchup> mark all articles in newsgroup as unread browser <unsubscribe> unsubscribe from current mbox (IMAP/NNTP only) browser <unsubscribe-pattern> unsubscribe from newsgroups matching a pattern index,pager <change-newsgroup-readonly> open a different newsgroup in read only mode attach,index,pager <forward-to-group> forward to newsgroup index <get-children> get all children of the current message index <get-parent> get parent of the current message index <reconstruct-thread> reconstruct thread containing current message index <get-message> get message with Message-Id
neomuttrc # Example NeoMutt config file for the nntp feature. # -------------------------------------------------------------------------- # VARIABLES - shown with their default values # -------------------------------------------------------------------------- set ask_follow_up = no set ask_x_comment_to = no set catchup_newsgroup = ask-yes set followup_to_poster = ask-yes set group_index_format = '%4C %M%N %5s %-45.45f %d' set inews = '' set mime_subject = yes set newsgroups_charset = utf-8 set newsrc = '~/.newsrc' set news_cache_dir = '~/.neomutt' set news_server = '' set nntp_authenticators = '' set nntp_context = 1000 set nntp_listgroup = yes set nntp_load_description = yes set nntp_pass = '' set nntp_poll = 60 set nntp_user = '' set post_moderated = ask-yes set save_unsubscribed = no set show_new_news = yes set show_only_unread = no set x_comment_to = no # -------------------------------------------------------------------------- # FUNCTIONS - shown with an example mapping # -------------------------------------------------------------------------- # mark all articles in newsgroup as read bind browser,index y catchup # open a different newsgroup bind index,pager i change-newsgroup # edit the Followup-To field bind compose o edit-followup-to # edit the newsgroups list bind compose N edit-newsgroups # edit the X-Comment-To field bind compose x edit-x-comment-to # followup to newsgroup bind attach,index,pager F followup-message # post message to newsgroup bind index,pager P post-message # load list of all newsgroups from NNTP server bind browser g reload-active # subscribe to current mbox (IMAP/NNTP only) bind browser s subscribe # subscribe to newsgroups matching a pattern bind browser S subscribe-pattern # mark all articles in newsgroup as unread bind browser Y uncatchup # unsubscribe from current mbox (IMAP/NNTP only) bind browser u unsubscribe # unsubscribe from newsgroups matching a pattern bind browser U unsubscribe-pattern # open a different newsgroup in read only mode bind index,pager \ei change-newsgroup-readonly # forward to newsgroup bind attach,index,pager \eF forward-to-group # get all children of the current message # bind index ??? get-children # get parent of the current message bind index \eG get-parent # reconstruct thread containing current message # bind index ??? reconstruct-thread # get message with Message-Id bind index \CG get-message # -------------------------------------------------------------------------- # vim: syntax=neomuttrc Known Bugs None Credits Vsevolod Volkov vvv@mutt.org.ua Felix von Leitner leitner@fefe.de Richard Russon rich@flatcap.org
Custom backend based Tags Feature Implements Notmuch tags and Imap keywords Support Since:NeoMutt 2017-10-16 Dependencies: quasi-delete feature index-color feature Introduction Some backends allow to index and tags mail without storing the tags within the mail envelope. Two backends are currently implementing this feature. Notmuch handles them natively and IMAP stores them in custom IMAP keywords. Variables Custom tags Variables Name Type Default hidden_tags string unread,draft,flagged,passed,replied,attachment,signed,encrypted
Functions Notmuch adds the following functions to NeoMutt. By default, none of them are bound to keys. Notmuch/IMAP Functions Menus Function Description index,pager <modify-labels> add, remove, or toggle tags: IMAP: edit the tags list Notmuch: [+]<tag> to add, -<tag> to remove, !<tag> to toggle(notmuch) tags. Note: Tab completion of tag names is available index,pager <modify-labels-then-hide> add, remove, or toggle tags IMAP: edit the tags list Notmuch: [+]<tag> to add, -<tag> to remove, !<tag> to toggle labels and then hide or unhide the message by changing the "quasi-deleted" to match if it would be shown when requerying. Normal redisplay rules apply here, so the user must call <sync-mailbox> for the changes to be displayed. Note: Tab completion of tag names is available.
Commands tag-transforms tag transformed-string tag transformed-string tag-formats tag format-string tag format-string Colors Adds these to index-color feature: Index Colors Object Pattern Highlights index_tag yes an individual message tag, %G, uses tag name index_tags no the transformed message tags, %g or %J
neomuttrc # Example NeoMutt config file for the custom tags feature. # VARIABLES - shown with their default values # -------------------------------------------------------------------------- # This variable specifies private notmuch tags which should not be printed # on screen (index, pager). set nm_hidden_tags = "unread,draft,flagged,passed,replied,attachment,signed,encrypted" # -------------------------------------------------------------------------- # FUNCTIONS - shown with an example mapping # -------------------------------------------------------------------------- # modify (notmuch/imap) tags bind index,pager \` modify-labels # modify (notmuch/imap) tag non-interactively. bind index,pager tt "<modify-labels>!todo\n" "Toggle the 'todo' tag" # modify labels and then hide message # bind index,pager ??? modify-labels-then-hide # -------------------------------------------------------------------------- # COMMANDS - shown with an example # -------------------------------------------------------------------------- # Replace some tags with icons # tag-transforms tag transformed-string { tag transformed-string ...} # tag-transforms "inbox" "i" \ # "unread" "u" \ # "replied" "↻ " \ # "sent" "➥ " \ # "todo" "T" \ # "deleted" "DEL" \ # "invites" "CAL" # The formats must start with 'G' and the entire sequence is case sensitive. # tag-formats tag format-string { tag format-string ...} # tag-formats "inbox" "GI" \ # "unread" "GU" \ # "replied" "GR" \ # "sent" "GS" \ # "todo" "Gt" \ # "deleted" "GD" \ # "invites" "Gi" # Now instead of using '%g' or '%J' in your $index_format, which lists all tags # in a non-deterministic order, you can something like the following which puts # a transformed tag name in a specific spot on the index line: # set index_format='%4C %S %[%y.%m.%d] %-18.18n %?GU?%GU& ? %?GR?%GR& ? %?GI?%GI& ? %s' # The %G formatting sequence may display all tags including tags hidden by # nm_hidden_tags. # # -------------------------------------------------------------------------- # COLORS - some unpleasant examples are given # -------------------------------------------------------------------------- # These symbols are added to the index-color feature: # an individual message tag, %G, uses tag name # this symbol uses a pattern color index_tag red white "inbox" # the transformed message tags, %g # this symbol does not use a pattern color index_tags green default # -------------------------------------------------------------------------- # vim: syntax=neomuttrc Credits Mehdi Abaakouk sileht@sileht.net Richard Russon rich@flatcap.org Bernard 'Guyzmo' Pratz guyzmo+github+pub@m0g.net
Notmuch Feature Email search engine Support Since:NeoMutt 2016-03-17 Dependencies: quasi-delete feature index-color feature Notmuch libraries Introduction Notmuch is an email fulltext indexing and tagging engine. For more information, see: https://notmuchmail.org/ More examples: https://notmuchmail.org/mutttips/ Using Notmuch Folders URI notmuch://[<path>][?<item>=<name>[& ...]] The <path> is an absolute path to the directory where the notmuch database is found as returned by notmuch config get database.path command. Note that the <path> should NOT include .notmuch directory name. If the "<path>" is not defined then $nm_default_uri or $folder is used, for example: set nm_default_uri = "notmuch:///home/foo/maildir" virtual-mailboxes "My INBOX" "notmuch://?query=tag:inbox" Items query=<string> See SEARCH SYNTAX in notmuch man page. Don't forget to use operators ( and/ or) in your queries. Note that proper URI should not contain blank space and all bad chars should be encoded, for example tag:AAA and tag:BBB--encoding-> tag:AAA%20and%20tag:BBB but NeoMutt config file parser is smart enough to accept space in quoted strings. It means that you can use notmuch:///foo?query=tag:AAA and tag:BBB in your config files to keep things readable. For more details about Xapian queries, see: https://xapian.org/docs/queryparser.html limit=<number> Restricts number of messages/threads in the result. The default limit is nm_db_limit. type=<threads|messages> Reads all matching messages or whole-threads. The default is 'messages' or nm_query_type. Format String for the Notmuch Browser Default: %2C %?n?%4n/& ?%4m %f This variable allows you to customize the browser display to your personal taste. This string is similar to $index_format, but has its own set of printf(3)-like sequences: %C current file number %f folder name (description) %m number of messages in the mailbox * %n number of unread messages in the mailbox * %N N if mailbox has new mail, blank otherwise %>X right justify the rest of the string and pad with character X %|X pad to the end of the line with character X %*X soft-fill with character X as pad For an explanation of soft-fill, see the $index_format documentation. * = can be optionally printed if nonzero Variables Notmuch Variables Name Type Default nm_db_limit number 0 nm_default_uri string (empty) nm_exclude_tags string (empty) nm_open_timeout number 5 nm_query_type string messages nm_record boolean no nm_record_tags string (empty) nm_unread_tag string unread vfolder_format string %6n(%6N) %f virtual_spoolfile boolean no
More variables about tags configuration can be found in Custom backend Tags Feature
Functions Notmuch adds the following functions to NeoMutt. By default, none of them are bound to keys. Notmuch Functions Menus Function Description index,pager <change-vfolder> switch to another virtual folder, a new folder maybe be specified by vfolder description (see virtual-mailboxes) or URI. the default is next vfolder with unread messages index,pager <entire-thread> read entire thread of the current message index,pager <sidebar-toggle-virtual> toggle between mailboxes and virtual mailboxes index,pager <vfolder-from-query> generate virtual folder from notmuch search query. Note: TAB completion of 'tag:' names is available. index,pager <vfolder-window-forward> generate virtual folder by moving the query's time window forward index,pager <vfolder-window-backward> generate virtual folder by moving the query's time window backward
More functions about tags can be found in Custom backend Tags Feature
Commands virtual-mailboxes description notmuch-URI description notmuch-URI unvirtual-mailboxes * mailbox More commands about tags can be found in Custom backend Tags Feature Colors See Custom backend Tags colors neomuttrc # Example NeoMutt config file for the notmuch feature. # -------------------------------------------------------------------------- # VARIABLES - shown with their default values # -------------------------------------------------------------------------- # This variable specifies notmuch query limit. set nm_db_limit = 0 # This variable specifies the default Notmuch database in format: # notmuch://<absolute path> set nm_default_uri = "" # The messages tagged with these tags are excluded and not loaded # from notmuch DB to NeoMutt unless specified explicitly. set nm_exclude_tags = "" # This option specifies timeout for Notmuch database. Default is 5 seconds. set nm_open_timeout = 5 # This variable specifies notmuch query type, supported types: 'threads' and # 'messages'. set nm_query_type = messages # Add messages stored to the NeoMutt record (see $record in the NeoMutt docs) also to notmuch DB. If you reply to an email then the new email inherits tags from the original email. set nm_record = no # Tags that should be removed or added to the to the messages stored in the NeoMutt record. example: set record = "~/sent-mails" set nm_record = yes set nm_record_tags = "-inbox,archive,me" set nm_record_tags = "" # This variable specifies notmuch tag which is used for unread messages. set nm_unread_tag = unread # This variable allows you to customize the file browser display for virtual # folders to your personal taste. # %C current folder number # %f folder name (description) # %m number of messages in the mailbox * # %n number of unread messages in the mailbox * # %N N if mailbox has new mail, blank otherwise # %>X right justify the rest of the string and pad with character ``X'' # %|X pad to the end of the line with character ``X'' # %*X soft-fill with character ``X'' as pad set vfolder_format = "%6n(%6N) %f" # When set, NeoMutt will use the first virtual mailbox (see virtual-mailboxes) # as a spoolfile. set virtual_spoolfile = no # setup time window preferences # first setup the duration, and then the time unit of that duration # when set to 0 (the default) the search window feature is disabled set nm_query_window_duration=2 set nm_query_window_timebase="week" # or "hour", "day", "week", "month", "year" # -------------------------------------------------------------------------- # FUNCTIONS - shown with an example mapping # -------------------------------------------------------------------------- # open a different virtual folder bind index,pager X change-vfolder # read entire thread of the current message bind index,pager + entire-thread # generate virtual folder from query bind index,pager \eX vfolder-from-query # generate virtual folder from query with time window bind index,pager < vfolder-window-backward bind index,pager > vfolder-window-forward # toggle between mailboxes and virtual mailboxes # bind index,pager ??? sidebar-toggle-virtual # -------------------------------------------------------------------------- # COMMANDS - shown with an example # -------------------------------------------------------------------------- # virtual-mailboxes description notmuch-URI { description notmuch-URI ...} # virtual-mailboxes "Climbing" "notmuch://?query=climbing" # unvirtual-mailboxes { * | mailbox ...} # # -------------------------------------------------------------------------- # vim: syntax=neomuttrc See Also Compile-Time Features Known Bugs None Credits Karel Zak kzak@redhat.com Chris Mason clm@fb.com Christoph Rissner cri@visotech.at David Riebenbauer davrieb@liegesta.at David Sterba dsterba@suse.cz David Wilson dw@botanicus.net Don Zickus dzickus@redhat.com Eric Davis edavis@insanum.com Jan Synacek jsynacek@redhat.com Jeremiah C. Foster jeremiah@jeremiahfoster.com Josh Poimboeuf jpoimboe@redhat.com Kirill A. Shutemov kirill@shutemov.name Luke Macken lmacken@redhat.com Mantas Mikulėnas grawity@gmail.com Patrick Brisbin pbrisbin@gmail.com Philippe Le Brouster plb@nebkha.net Raghavendra D Prabhu rprabhu@wnohang.net Sami Farin hvtaifwkbgefbaei@gmail.com Stefan Assmann sassmann@kpanic.de Stefan Kuhn p_regius@gmx.ch Tim Stoakes tim@stoakes.net Vladimir Marek Vladimir.Marek@oracle.com Víctor Manuel Jáquez Leal vjaquez@igalia.com Richard Russon rich@flatcap.org Bernard 'Guyzmo' Pratz guyzmo+github+pub@m0g.net
Progress Bar Feature Show a visual progress bar on slow operations Support Since:NeoMutt 2016-03-07 Dependencies:None Introduction The progress feature shows a visual progress bar on slow tasks, such as indexing a large folder over the net. Colors Progress Colors Name Default Color Description progress default Visual progress bar
neomuttrc # Example NeoMutt config file for the progress feature. # The 'progress' feature provides clear visual feedback for # slow tasks, such as indexing a large folder over the net. # Set the color of the progress bar # White text on a red background color progress white red # vim: syntax=neomuttrc See Also Color command Known Bugs None Credits Rocco Rutte pdmef@gmx.net Vincent Lefevre vincent@vinc17.org Stefan Kuhn wuodan@hispeed.ch Karel Zak kzak@redhat.com Richard Russon rich@flatcap.org
Quasi-Delete Feature Mark emails that should be hidden, but not deleted Support Since:NeoMutt 2016-03-07 Dependencies:None Introduction The quasi-delete function marks an email that should be hidden from the index, but NOT deleted. The email will disappear from the index when <sync-mailbox> is called. On its own, this feature isn't very useful. It forms a useful part of the notmuch plugin. Functions Quasi-Delete Functions Menus Default Key Function Description index,pager (none) <quasi-delete> delete from NeoMutt, don't touch on disk
neomuttrc # Example NeoMutt config file for the quasi-delete feature. # The 'quasi-delete' function marks an email that should be hidden # from the index, but NOT deleted. bind index,pager Q quasi-delete # vim: syntax=neomuttrc See Also notmuch feature Known Bugs None Credits Karel Zak kzak@redhat.com Richard Russon rich@flatcap.org
Reply With X-Original-To Feature Direct reply to email using X-Original-To header Support Since:NeoMutt 2016-09-10 Dependencies:None Introduction Adds a reply_with_xorig for NeoMutt configuration files. If enabled, allows to reply to an email using the email address in the first X-Original-To: header of a mail as the From: header of the answer. Variables Reply With X-Original-To Variables Name Type Default reply_with_xorig Boolean no
neomuttrc # Example NeoMutt config file for the reply-with-xorig feature. # -------------------------------------------------------------------------- # VARIABLES - shown with their default values # -------------------------------------------------------------------------- # Use X-Original-To header to reply when reverse is disabled or no alternate # is found. set reply_with_xorig = "yes" # vim: syntax=neomuttrc Credits Pierre-Elliott Bécue becue@crans.org
Sensible Browser Feature Make the file browser behave Support Since:NeoMutt 2016-09-10 Dependencies:None Introduction The sensible browser is a set of small changes to NeoMutt's mailbox browser which make the browser behave in a more predictable way. The behavior is divided into two use cases: Fixed Order; Variable Order. A Fixed Order of Mailboxes This is for users who like their mailboxes in a fixed order, e.g. alphabetical, or unsorted (in the order of the config file). # Fixed order set sort_browser="alpha" set sort_browser="unsorted" When you first start the browser, e.g. c?your current mailbox will be highlighted. When you navigate to a parent mailbox ( ..) your old mailbox will be highlighted. ..will always be listed at the top, however the rest of the list is sorted. A Variable Order of Mailboxes This is for users who like their mailboxes sorted by a characteristic that changes, e.g. count of new mail, or the size of mailbox. # Variable order set sort_browser="reverse-count" set sort_browser="reverse-size" When you first start the browser, e.g. c?the highlight will be on the first mailbox, e.g. the one with the most new mail. When you navigate to a parent mailbox ( ..) your old mailbox will be highlighted. ..will always be listed at the top, however the rest of the list is sorted. See Also $folder_format Known Bugs None Credits Pierre-Elliott Bécue becue@crans.org Haakon Riiser haakon.riiser@fys.uio.no Richard Russon rich@flatcap.org Sidebar Feature Overview of mailboxes Support Since:NeoMutt 2016-09-10, NeoMutt 1.7.0 Dependencies:None Introduction The Sidebar shows a list of all your mailboxes. The list can be turned on and off, it can be themed and the list style can be configured. This part of the manual is a reference guide. If you want a simple introduction with examples see the Sidebar Howto. If you just want to get started, you could use the sample Sidebar neomuttrc. Variables Sidebar Variables Name Type Default sidebar_delim_chars string /. sidebar_divider_char string | sidebar_folder_indent boolean no sidebar_format string %B%* %n sidebar_indent_string string   (two spaces) sidebar_new_mail_only boolean no sidebar_next_new_wrap boolean no sidebar_on_right boolean no sidebar_short_path boolean no sidebar_component_depth number 0 sidebar_sort_method enum unsorted sidebar_visible boolean no sidebar_width number 20
For more details, and examples, about the $sidebar_format, see the Sidebar Intro.
Functions Sidebar adds the following functions to NeoMutt. By default, none of them are bound to keys. Sidebar Functions Menus Function Description index,pager <sidebar-next> Move the highlight to next mailbox index,pager <sidebar-next-new> Move the highlight to next mailbox with new mail index,pager <sidebar-open> Open highlighted mailbox index,pager <sidebar-page-down> Scroll the Sidebar down 1 page index,pager <sidebar-page-up> Scroll the Sidebar up 1 page index,pager <sidebar-prev> Move the highlight to previous mailbox index,pager <sidebar-prev-new> Move the highlight to previous mailbox with new mail index,pager <sidebar-toggle-visible> Make the Sidebar (in)visible
Commands sidebar_whitelist mailbox mailbox unsidebar_whitelist * mailbox This command specifies mailboxes that will always be displayed in the sidebar, even if $sidebar_new_mail_only is set and the mailbox does not contain new mail. The unsidebar_whitelist command is used to remove a mailbox from the list of whitelisted mailboxes. Use unsidebar_whitelist *to remove all mailboxes. Colors Sidebar Colors Name Default Color Description sidebar_divider default The dividing line between the Sidebar and the Index/Pager panels sidebar_flagged default Mailboxes containing flagged mail sidebar_highlight underline Cursor to select a mailbox sidebar_indicator neomutt indicator The mailbox open in the Index panel sidebar_new default Mailboxes containing new mail sidebar_ordinary default Mailboxes that have no new/flagged mails, etc. sidebar_spoolfile default Mailbox that receives incoming mail
If the sidebar_indicator color isn't set, then the default NeoMutt indicator color will be used (the color used in the index panel).
Sort Sidebar Sort Sort Description alpha Alphabetically by path count Total number of messages flagged Number of flagged messages name Alphabetically by path new Number of unread messages path Alphabetically by path unread Number of unread messages unsorted Order of the mailboxes command
neomuttrc # Example NeoMutt config file for the sidebar feature. # -------------------------------------------------------------------------- # VARIABLES - shown with their default values # -------------------------------------------------------------------------- # Should the Sidebar be shown? set sidebar_visible = no # How wide should the Sidebar be in screen columns? # Note: Some characters, e.g. Chinese, take up two columns each. set sidebar_width = 20 # Should the mailbox paths be abbreviated? set sidebar_short_path = no # Number of top-level mailbox path subdirectories to truncate for display set sidebar_component_depth = 0 # When abbreviating mailbox path names, use any of these characters as path # separators. Only the part after the last separators will be shown. # For file folders '/' is good. For IMAP folders, often '.' is useful. set sidebar_delim_chars = '/.' # If the mailbox path is abbreviated, should it be indented? set sidebar_folder_indent = no # Indent mailbox paths with this string. set sidebar_indent_string = ' ' # Make the Sidebar only display mailboxes that contain new, or flagged, # mail. set sidebar_new_mail_only = no # Any mailboxes that are whitelisted will always be visible, even if the # sidebar_new_mail_only option is enabled. sidebar_whitelist '/home/user/mailbox1' sidebar_whitelist '/home/user/mailbox2' # When searching for mailboxes containing new mail, should the search wrap # around when it reaches the end of the list? set sidebar_next_new_wrap = no # Show the Sidebar on the right-hand side of the screen set sidebar_on_right = no # The character to use as the divider between the Sidebar and the other NeoMutt # panels. set sidebar_divider_char = '|' # Enable extended buffy mode to calculate total, new, and flagged # message counts for each mailbox. set mail_check_stats # Display the Sidebar mailboxes using this format string. set sidebar_format = '%B%?F? [%F]?%* %?N?%N/?%S' # Sort the mailboxes in the Sidebar using this method: # count - total number of messages # flagged - number of flagged messages # new - number of new messages # path - mailbox path # unsorted - do not sort the mailboxes set sidebar_sort_method = 'unsorted' # -------------------------------------------------------------------------- # FUNCTIONS - shown with an example mapping # -------------------------------------------------------------------------- # Move the highlight to the previous mailbox bind index,pager \Cp sidebar-prev # Move the highlight to the next mailbox bind index,pager \Cn sidebar-next # Open the highlighted mailbox bind index,pager \Co sidebar-open # Move the highlight to the previous page # This is useful if you have a LOT of mailboxes. bind index,pager <F3> sidebar-page-up # Move the highlight to the next page # This is useful if you have a LOT of mailboxes. bind index,pager <F4> sidebar-page-down # Move the highlight to the previous mailbox containing new, or flagged, # mail. bind index,pager <F5> sidebar-prev-new # Move the highlight to the next mailbox containing new, or flagged, mail. bind index,pager <F6> sidebar-next-new # Toggle the visibility of the Sidebar. bind index,pager B sidebar-toggle-visible # -------------------------------------------------------------------------- # COLORS - some unpleasant examples are given # -------------------------------------------------------------------------- # Note: All color operations are of the form: # color OBJECT FOREGROUND BACKGROUND # Color of the current, open, mailbox # Note: This is a general NeoMutt option which colors all selected items. color indicator cyan black # Color of the highlighted, but not open, mailbox. color sidebar_highlight black color8 # Color of the divider separating the Sidebar from NeoMutt panels color sidebar_divider color8 black # Color to give mailboxes containing flagged mail color sidebar_flagged red black # Color to give mailboxes containing new mail color sidebar_new green black # Color to give mailboxes containing no new/flagged mail, etc. color sidebar_ordinary color245 default # -------------------------------------------------------------------------- # vim: syntax=neomuttrc See Also Regular Expressions Patterns Color command notmuch feature Known Bugs None Credits Justin Hibbits jrh29@po.cwru.edu Thomer M. Gil mutt@thomer.com David Sterba dsterba@suse.cz Evgeni Golov evgeni@debian.org Fabian Groffen grobian@gentoo.org Jason DeTiberus jdetiber@redhat.com Stefan Assmann sassmann@kpanic.de Steve Kemp steve@steve.org.uk Terry Chan tchan@lunar-linux.org Tyler Earnest tylere@rne.st Richard Russon rich@flatcap.org
Skip Quoted Feature Leave some context visible Support Since:NeoMutt 2016-03-28 Dependencies:None Introduction When viewing an email, the <skip-to-quoted>function (by default the S key) will scroll past any email headers or quoted text. Sometimes, a little context is useful. By setting the $skip_quoted_offset variable, you can select how much of the quoted text is left visible. Variables Skip-Quoted Variables Name Type Default skip_quoted_offset number 0
neomuttrc # Example NeoMutt config file for the skip-quoted feature. # The 'S' (skip-quoted) command scrolls the pager past the quoted text (usually # indented with '> '. Setting 'skip_quoted_offset' leaves some lines of quoted # text on screen for context. # Show three quoted lines before the reply set skip_quoted_offset = 3 # vim: syntax=neomuttrc Known Bugs None Credits David Sterba dsterba@suse.cz Richard Russon rich@flatcap.org
Status Color Feature Custom rules for theming the status bar Support Since:NeoMutt 2016-03-07 Dependencies:None Introduction The status-color feature allows you to theme different parts of the status bar (also when it's used by the index). Unlike normal color commands, color status can now take up to 2 extra parameters (regex, num). Commands color foreground background regex num With zero parameters, NeoMutt will set the default color for the entire status bar. With one parameter, NeoMutt will only color the parts matching the regex. With two parameters, NeoMutt will only color the num'th sub-match of the regex. Colors Status Colors Name Default Color Description status reverse Status bar
neomuttrc # Example NeoMutt config file for the status-color feature. # The 'status-color' feature allows you to theme different parts of # the status bar (also when it's used by the index). # For the examples below, set some defaults set status_format='-%r-NeoMutt: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b?%?l? %l?]---(%s/%S)-%>-(%P)---' set index_format='%4C %Z %{%b %d} %-15.15L (%?l?%4l&%4c?) %s' set sort=threads set sort_aux=last-date-received # 'status color' can take up to 2 extra parameters # color status foreground background [ regex [ num ]] # 0 extra parameters # Set the default color for the entire status line color status blue white # 1 extra parameter # Set the color for a matching pattern # color status foreground background regex # Highlight New, Deleted, or Flagged emails color status brightred white '(New|Del|Flag):[0-9]+' # Highlight mailbox ordering if it's different from the default # First, highlight anything (*/*) color status brightred default '\([^)]+/[^)]+\)' # Then override the color for one specific case color status default default '\(threads/last-date-received\)' # 2 extra parameters # Set the color for the nth submatch of a pattern # color status foreground background regex num # Highlight the contents of the []s but not the [] themselves color status red default '\[([^]]+)\]' 1 # The '1' refers to the first regex submatch, which is the inner # part in ()s # Highlight the mailbox color status brightwhite default 'NeoMutt: ([^ ]+)' 1 # Search for 'NeoMutt: ' but only highlight what comes after it # vim: syntax=neomuttrc See Also Compile-Time Features Regular Expressions Patterns index-color feature Color command Known Bugs None Credits David Sterba dsterba@suse.cz Thomas Glanzmann thomas@glanzmann.de Kirill A. Shutemov kirill@shutemov.name Richard Russon rich@flatcap.org
TLS-SNI Feature Negotiate with a server for a TLS/SSL certificate Support Since:NeoMutt 2016-03-07 Dependencies: OpenSSL Introduction The TLS-SNI feature adds support for TLS virtual hosting. If your mail server doesn't support this everything will still work normally. TLS supports sending the expected server hostname during the handshake, via the SNI extension. This can be used to select a server certificate to issue to the client, permitting virtual-hosting without requiring multiple IP addresses. This has been tested against Exim 4.80, which optionally logs SNI and can perform vhosting. To verify TLS SNI support by a server, you can use: openssl s_client -host <imap server> -port <port> -tls1 -servername <imap server> Known Bugs None Credits Jeremy Katz katzj@linuxpower.org Phil Pennock mutt-dev@spodhuis.demon.nl Richard Russon rich@flatcap.org Trash Folder Feature Automatically move deleted emails to a trash bin Support Since:NeoMutt 2016-09-10, NeoMutt 1.7.0 Dependencies: If IMAP is enabled, the trash folder will use it wisely Introduction In NeoMutt, when you delete an email it is first marked deleted. The email isn't really gone until <sync-mailbox>is called. This happens when the user leaves the folder, or the function is called manually. After <sync-mailbox>has been called the email is gone forever. The $trash variable defines a folder in which to keep old emails. As before, first you mark emails for deletion. When <sync-mailbox> is called the emails are moved to the trash folder. The $trash path can be either a full directory, or be relative to the $folder variable, like the mailboxes command. Emails deleted from the trash folder are gone forever. Variables Trash Variables Name Type Default trash string (none)
Functions Trash Functions Menus Default Key Function Description index,pager (none) <purge-message> really delete the current entry, bypassing the trash folder
neomuttrc # Example NeoMutt config file for the 'trash' feature. # This feature defines a new 'trash' folder. # When mail is deleted it will be moved to this folder. # Folder in which to put deleted emails set trash='+Trash' set trash='/home/flatcap/Mail/Trash' # The default delete key 'd' will move an email to the 'trash' folder # Bind 'D' to REALLY delete an email bind index D purge-message # Note: Deleting emails from the 'trash' folder will REALLY delete them. # vim: syntax=neomuttrc See Also folder-hook Known Bugs None Credits Cedric Duval cedricduval@free.fr Benjamin Kuperman kuperman@acm.org Paul Miller paul@voltar.org Richard Russon rich@flatcap.org
Security Considerations First of all, NeoMutt contains no security holes included by intention but may contain unknown security holes. As a consequence, please run NeoMutt only with as few permissions as possible. Especially, do not run NeoMutt as the super user. When configuring NeoMutt, there're some points to note about secure setups so please read this chapter carefully. Passwords Although NeoMutt can be told the various passwords for accounts, please never store passwords in configuration files. Besides the fact that the system's operator can always read them, you could forget to mask it out when reporting a bug or asking for help via a mailing list. Even worse, your mail including your password could be archived by internet search engines, mail-to-news gateways etc. It may already be too late before you notice your mistake. Temporary Files NeoMutt uses many temporary files for viewing messages, verifying digital signatures, etc. As long as being used, these files are visible by other users and maybe even readable in case of misconfiguration. Also, a different location for these files may be desired which can be changed via the $tmpdir variable. Information Leaks Message-Id: headers Message-Id: headers contain a local part that is to be created in a unique fashion. In order to do so, NeoMutt will leak some information to the outside world when sending messages: the generation of this header includes a step counter which is increased (and rotated) with every message sent. In a longer running NeoMutt session, others can make assumptions about your mailing habits depending on the number of messages sent. If this is not desired, the header can be manually provided using $edit_headers(though not recommended). <literal>mailto:</literal>-style Links As NeoMutt be can be set up to be the mail client to handle mailto:style links in websites, there're security considerations, too. Arbitrary header fields can be embedded in these links which could override existing header fields or attach arbitrary files using the Attach: pseudoheader. This may be problematic if the $edit-headers variable is unset, i.e. the user doesn't want to see header fields while editing the message and doesn't pay enough attention to the compose menu's listing of attachments. For example, following a link like mailto:joe@host?Attach=~/.gnupg/secring.gpg will send out the user's private gnupg keyring to joe@host if the user doesn't follow the information on screen carefully enough. To prevent these issues, NeoMutt by default only accepts the Subject and Body headers. Allowed headers can be adjusted with the mailto_allow and unmailto_allow commands. External Applications NeoMutt in many places has to rely on external applications or for convenience supports mechanisms involving external applications. One of these is the mailcap mechanism as defined by RFC1524. Details about a secure use of the mailcap mechanisms is given in . Besides the mailcap mechanism, NeoMutt uses a number of other external utilities for operation, for example to provide crypto support, in backtick expansion in configuration files or format string filters. The same security considerations apply for these as for tools involved via mailcap. Performance Tuning Reading and Writing Mailboxes NeoMutt's performance when reading mailboxes can be improved in two ways: For remote folders (IMAP and POP) as well as folders using one-file-per message storage (Maildir and MH), NeoMutt's performance can be greatly improved using header caching. using a single database per folder. NeoMutt provides the $read_inc and $write_inc variables to specify at which rate to update progress counters. If these values are too low, NeoMutt may spend more time on updating the progress counter than it spends on actually reading/writing folders. For example, when opening a maildir folder with a few thousand messages, the default value for $read_inc may be too low. It can be tuned on on a folder-basis using folder-hooks: # use very high $read_inc to speed up reading hcache'd maildirs folder-hook . 'set read_inc=1000' # use lower value for reading slower remote IMAP folders folder-hook ^imap 'set read_inc=100' # use even lower value for reading even slower remote POP folders folder-hook ^pop 'set read_inc=1' These settings work on a per-message basis. However, as messages may greatly differ in size and certain operations are much faster than others, even per-folder settings of the increment variables may not be desirable as they produce either too few or too much progress updates. Thus, NeoMutt allows to limit the number of progress updates per second it'll actually send to the terminal using the $time_inc variable. Reading Messages from Remote Folders Reading messages from remote folders such as IMAP an POP can be slow especially for large mailboxes since NeoMutt only caches a very limited number of recently viewed messages (usually 10) per session (so that it will be gone for the next session.) To improve performance and permanently cache whole messages, please refer to NeoMutt's so-called body caching for details. Searching and Limiting When searching mailboxes either via a search or a limit action, for some patterns NeoMutt distinguishes between regular expression and string searches. For regular expressions, patterns are prefixed with ~and with =for string searches. Even though a regular expression search is fast, it's several times slower than a pure string search which is noticeable especially on large folders. As a consequence, a string search should be used instead of a regular expression search if the user already knows enough about the search pattern. For example, when limiting a large folder to all messages sent to or by an author, it's much faster to search for the initial part of an e-mail address via =Luser@instead of ~Luser@. This is especially true for searching message bodies since a larger amount of input has to be searched. As for regular expressions, a lower case string search pattern makes NeoMutt perform a case-insensitive search except for IMAP (because for IMAP NeoMutt performs server-side searches which don't support case-insensitivity). Reference Command-Line Options Running neomutt with no arguments will make NeoMutt attempt to read your spool mailbox. However, it is possible to read other mailboxes and to send messages from the command line as well. Command line options Option Description -A expand an alias -a attach a file to a message -b specify a blind carbon-copy (BCC) address -c specify a carbon-copy (Cc) address -d log debugging output to ~/.neomuttdebug0 if NeoMutt was compiled with +debug; it can range from 1-5 and affects verbosity (a value of 2 is recommended) -D print the value of all NeoMutt variables to stdout -D -S like -D but hide the value of sensitive variables -E edit the draft (-H) or include (-i) file -e specify a config command to be run after initialization files are read -f specify a mailbox to load -F specify an alternate file to read initialization commands -h print help on command line options -H specify a draft file from which to read a header and body -i specify a file to include in a message composition -m specify a default mailbox type -n do not read the system neomuttrc -p recall a postponed message -Q query a configuration variable -R open mailbox in read-only mode -s specify a subject (enclose in quotes if it contains spaces) -v show version number and compile-time definitions -x simulate the mailx(1) compose mode -y show a menu containing the files specified by the mailboxes command -z exit immediately if there are no messages in the mailbox -Z open the first folder with new message, exit immediately if none
To read messages in a mailbox neomutt neomuttrc type mailbox To compose a new message neomutt neomuttrc address filename subject file -- address mailto_url NeoMutt also supports a batch mode to send prepared messages. Simply redirect input from the file you wish to send. For example, neomutt -s "data set for run #2" professor@bigschool.edu < ~/run2.dat will send a message to <professor@bigschool.edu>with a subject of data set for run #2. In the body of the message will be the contents of the file ~/run2.dat. An include file passed with -i will be used as the body of the message. When combined with -E, the include file will be directly edited during message composition. The file will be modified regardless of whether the message is sent or aborted. A draft file passed with -H will be used as the initial header and body for the message. Multipart messages can be used as a draft file. When combined with -E, the draft file will be updated to the final state of the message after composition, regardless of whether the message is sent, aborted, or even postponed. Note that if the message is sent encrypted or signed, the draft file will be saved that way too. All files passed with -a file will be attached as a MIME part to the message. To attach a single or several files, use --to separate files and recipient addresses: neomutt -a image.png -- some@one.org or neomutt -a *.png -- some@one.org The -a option must be last in the option list. In addition to accepting a list of email addresses, NeoMutt also accepts a URL with the mailto:schema as specified in RFC2368. This is useful when configuring a web browser to launch NeoMutt when clicking on mailto links. neomutt mailto:some@one.org?subject=test&cc=other@one.org
Configuration Commands The following are the commands understood by NeoMutt: account-hook regex command alias name key address address unalias name * key alternates name regex regex unalternates name * regex alternative_order mimetype mimetype unalternative_order * mimetype attachments { + | - }disposition mime-type unattachments { + | - }disposition mime-type append-hook pattern shell-command auto_view mimetype mimetype unauto_view * mimetype bind map key function charset-hook alias charset iconv-hook charset local-charset close-hook pattern shell-command color object foreground background color foreground background regex color foreground background pattern uncolor * pattern open-hook pattern shell-command crypt-hook regex keyid exec function function fcc-hook [!]pattern mailbox fcc-save-hook [!]pattern mailbox folder-hook [!]regex command group name expr expr ungroup name * expr expr hdr_order header header unhdr_order * header ifdef item "config-command [args]" ignore pattern pattern unignore * pattern lists name regex regex unlists name * regex macro menu key sequence description mailboxes mailbox mailbox unmailboxes * mailbox mailto_allow * header-field unmailto_allow * header-field mbox-hook [!]regex mailbox message-hook [!]pattern command mime_lookup mimetype mimetype unmime_lookup * mimetype mono object attribute mono attribute regex mono attribute pattern unmono * pattern my_hdr string unmy_hdr * field push string save-hook [!]pattern mailbox score pattern value unscore * pattern reply-hook [!]pattern command send-hook [!]pattern command send2-hook [!]pattern command set variable variable=value toggle variable variable unset variable variable reset variable variable setenv [?]variable value unsetenv variable sidebar_whitelist mailbox mailbox unsidebar_whitelist * mailbox source filename spam pattern format nospam * pattern subjectrx pattern replacement unsubjectrx * pattern subscribe name regex regex unsubscribe name * regex unhook * hook-type Configuration Variables neomutt-neomutt-20171215/doc/manual.xml.tail000066400000000000000000000147351321473123000206440ustar00rootroot00000000000000 Functions The following is the list of available functions listed by the mapping in which they are available. The default key setting is given, and an explanation of what the function does. The key bindings of these functions can be changed with the bind command. __print_map(generic) __print_map(index) __print_map(pager) __print_map(alias) __print_map(query) __print_map(attachment) __print_map(compose) __print_map(postpone) __print_map(browser) __print_map(pgp) __print_map(smime) __print_map(mixmaster) __print_map(editor)
Miscellany Acknowledgements Kari Hurtta kari.hurtta@fmi.fi co-developed the original MIME parsing code back in the ELM-ME days. The following people have been very helpful to the development of Mutt: Vikas Agnihotri vikasa@writeme.com Francois Berjon Francois.Berjon@aar.alcatel-alsthom.fr Aric Blumer aric@fore.com John Capo jc@irbs.com David Champion dgc@uchicago.edu Brendan Cully brendan@kublai.com Liviu Daia daia@stoilow.imar.ro Thomas E. Dickey dickey@herndon4.his.com David DeSimone fox@convex.hp.com Nickolay N. Dudorov nnd@wint.itfs.nsk.su Ruslan Ermilov ru@freebsd.org Edmund Grimley Evans edmundo@rano.org Michael Finken finken@conware.de Sven Guckes guckes@math.fu-berlin.de Lars Hecking lhecking@nmrc.ie Mark Holloman holloman@nando.net Andreas Holzmann holzmann@fmi.uni-passau.de Marco d'Itri md@linux.it Björn Jacke bjacke@suse.com Byrial Jensen byrial@image.dk David Jeske jeske@igcom.net Christophe Kalt kalt@hugo.int-evry.fr Tommi Komulainen Tommi.Komulainen@iki.fi Felix von Leitner (a.k.a Fefe) leitner@math.fu-berlin.de Brandon Long blong@fiction.net Jimmy Mäkelä jmy@flashback.net Lars Marowsky-Bree lmb@pointer.in-minden.de Thomas Mike Michlmayr mike@cosy.sbg.ac.at Andrew W. Nosenko awn@bcs.zp.ua David O'Brien obrien@Nuxi.cs.ucdavis.edu Clint Olsen olsenc@ichips.intel.com Park Myeong Seok pms@romance.kaist.ac.kr Thomas Parmelan tom@ankh.fr.eu.org Ollivier Robert roberto@keltia.freenix.fr Thomas Roessler roessler@does-not-exist.org Roland Rosenfeld roland@spinnaker.de Rocco Rutte pdmef@gmx.net TAKIZAWA Takashi taki@luna.email.ne.jp Allain Thivillon Allain.Thivillon@alma.fr Gero Treuner gero@70t.de Vsevolod Volkov vvv@lucky.net Ken Weinert kenw@ihs.com About This Document This document was written in DocBook, and then rendered using the Gnome XSLT toolkit.
neomutt-neomutt-20171215/doc/mbox.5000066400000000000000000000121441321473123000167400ustar00rootroot00000000000000'\" t .\" -*-nroff-*- .\" .\" Copyright (C) 2000 Thomas Roessler .\" .\" This document is in the public domain and may be distributed and .\" changed arbitrarily. .\" .TH mbox 5 "February 19th, 2002" Unix "User Manuals" .\" .SH NAME mbox \- Format for mail message storage. .\" .SH DESCRIPTION This document describes the format traditionally used by Unix hosts to store mail messages locally. .B mbox files typically reside in the system's mail spool, under various names in users' Mail directories, and under the name .B mbox in users' home directories. .PP An .B mbox is a text file containing an arbitrary number of e-mail messages. Each message consists of a postmark, followed by an e-mail message formatted according to \fBRFC822\fP, \fBRFC2822\fP. The file format is line-oriented. Lines are separated by line feed characters (ASCII 10). .PP A postmark line consists of the four characters "From", followed by a space character, followed by the message's envelope sender address, followed by whitespace, and followed by a time stamp. This line is often called From_ line. .PP The sender address is expected to be .B addr-spec as defined in \fBRFC2822\fP 3.4.1. The date is expected to be .B date-time as output by .BR asctime(3) . For compatibility reasons with legacy software, two-digit years greater than or equal to 70 should be interpreted as the years 1970+, while two-digit years less than 70 should be interpreted as the years 2000-2069. Software reading files in this format should also be prepared to accept non-numeric timezone information such as "CET DST" for Central European Time, daylight saving time. .PP Example: .IP "" 1 >From example@example.com Fri Jun 23 02:56:55 2000 .PP In order to avoid misinterpretation of lines in message bodies which begin with the four characters "From", followed by a space character, the mail delivery agent must quote any occurrence of "From " at the start of a body line. .sp There are two different quoting schemes, the first (\fBMBOXO\fP) only quotes plain "From " lines in the body by prepending a '>' to the line; the second (\fBMBOXRD\fP) also quotes already quoted "From " lines by prepending a '>' (i.e. ">From ", ">>From ", ...). The later has the advantage that lines like .IP "" 1 >From the command line you can use the '\-p' option .PP aren't dequoted wrongly as a \fBMBOXRD\fP-MDA would turn the line into .IP "" 1 >>From the command line you can use the '\-p' option .PP before storing it. Besides \fBMBOXO\fP and \fBMBOXRD\fP there is also \fBMBOXCL\fP which is \fBMBOXO\fP with a "Content-Length:"\-field with the number of bytes in the message body; some MUAs (like .BR mutt (1)) do automatically transform \fBMBOXO\fP mailboxes into \fBMBOXCL\fP ones when ever they write them back as \fBMBOXCL\fP can be read by any \fBMBOXO\fP-MUA without any problems. .PP If the modification-time (usually determined via .BR stat (2)) of a nonempty .B mbox file is greater than the access-time the file has new mail. Many MUAs place a Status: header in each message to indicate which messages have already been read. .\" .SH LOCKING Since .B mbox files are frequently accessed by multiple programs in parallel, .B mbox files should generally not be accessed without locking. .PP Three different locking mechanisms (and combinations thereof) are in general use: .IP "\(bu" .BR fcntl (2) locking is mostly used on recent, POSIX-compliant systems. Use of this locking method is, in particular, advisable if .B mbox files are accessed through the Network File System (NFS), since it seems the only way to reliably invalidate NFS clients' caches. .IP "\(bu" .BR flock (2) locking is mostly used on BSD-based systems. .PP If multiple methods are combined, implementors should make sure to use the non-blocking variants of the .BR fcntl (2) and .BR flock (2) system calls in order to avoid deadlocks. .PP If multiple methods are combined, an .B mbox file must not be considered to have been successfully locked before all individual locks were obtained. When one of the individual locking methods fails, an application should release all locks it acquired successfully, and restart the entire locking procedure from the beginning, after a suitable delay. .PP The locking mechanism used on a particular system is a matter of local policy, and should be consistently used by all applications installed on the system which access .B mbox files. Failure to do so may result in loss of e-mail data, and in corrupted .B mbox files. .\" .SH FILES .IR /var/spool/mail/$LOGNAME .RS \fB$LOGNAME\fP's incoming mail folder. .RE .PP .IR $HOME/mbox .RS user's archived mail messages, in his \fB$HOME\fP directory. .RE .PP .IR $HOME/Mail/ .RS A directory in user's \fB$HOME\fP directory which is commonly used to hold .B mbox format folders. .RE .PP .\" .SH "SEE ALSO" .BR mutt (1), .BR fcntl (2), .BR flock (2), .BR link (2), .BR stat (2), .BR asctime (3), .BR maildir (5), .BR mmdf (5), .BR RFC822 , .BR RFC976 , .BR RFC2822 .\" .SH AUTHOR Thomas Roessler , Urs Janssen .\" .SH HISTORY The .B mbox format occurred in Version 6 AT&T Unix. .br A variant of this format was documented in \fBRFC976\fP. neomutt-neomutt-20171215/doc/mime.types000066400000000000000000000061221321473123000177210ustar00rootroot00000000000000# # sample mime.types # application/andrew-inset ez application/excel xls application/octet-stream bin application/oda oda application/pdf pdf application/pgp pgp application/postscript ps PS eps application/rdf+xml rdf application/rss+xml rss application/rtf rtf application/vnd.mozilla.xul+xml xul application/vnd.oasis.opendocument.chart odc application/vnd.oasis.opendocument.database odb application/vnd.oasis.opendocument.formula odf application/vnd.oasis.opendocument.graphics odg application/vnd.oasis.opendocument.graphics-template otg application/vnd.oasis.opendocument.image odi application/vnd.oasis.opendocument.presentation odp application/vnd.oasis.opendocument.presentation-template otp application/vnd.oasis.opendocument.spreadsheet ods application/vnd.oasis.opendocument.spreadsheet-template ots application/vnd.oasis.opendocument.text odt application/vnd.oasis.opendocument.text-master odm application/vnd.oasis.opendocument.text-template ott application/vnd.oasis.opendocument.text-web oth application/vnd.sun.xml.calc sxc application/vnd.sun.xml.calc.template stc application/vnd.sun.xml.draw sxd application/vnd.sun.xml.draw.template std application/vnd.sun.xml.impress sxi application/vnd.sun.xml.impress.template sti application/vnd.sun.xml.writer sxw application/vnd.sun.xml.writer.global sxg application/vnd.sun.xml.writer.math sxm application/vnd.sun.xml.writer.template stw application/x-arj-compressed arj application/x-bcpio bcpio application/x-chess-pgn pgn application/x-cpio cpio application/x-csh csh application/x-debian-package deb application/x-msdos-program com exe bat application/x-dvi dvi application/x-gtar gtar application/x-gunzip gz application/x-hdf hdf application/x-latex latex application/x-mif mif application/x-netcdf cdf nc application/x-perl pl pm application/x-rar-compressed rar application/x-sh sh application/x-shar shar application/x-sv4cpio sv4cpio application/x-sv4crc sv4crc application/x-tar tar application/x-tar-gz tgz tar.gz application/x-tcl tcl application/x-tex tex application/x-texinfo texi texinfo application/x-troff t tr roff application/x-troff-man man application/x-troff-me me application/x-troff-ms ms application/x-ustar ustar application/x-wais-source src application/x-zip-compressed zip application/xhtml+xml xhtml xht application/xml xml xsl audio/basic snd audio/midi mid midi audio/ulaw au audio/x-aiff aif aifc aiff audio/x-wav wav image/gif gif image/ief ief image/jpeg jpe jpeg jpg image/png png image/svg+xml svg svgz image/tiff tif tiff image/x-cmu-raster ras image/x-portable-anymap pnm image/x-portable-bitmap pbm image/x-portable-graymap pgm image/x-portable-pixmap ppm image/x-rgb rgb image/x-xbitmap xbm image/x-xpixmap xpm image/x-xwindowdump xwd text/html html htm text/plain asc txt text/richtext rtx text/tab-separated-values tsv text/x-setext etx video/dl dl video/fli fli video/gl gl video/mpeg mp2 mpe mpeg mpg video/quicktime mov qt video/x-msvideo avi video/x-sgi-movie movie x-world/x-vrml vrm vrml wrl neomutt-neomutt-20171215/doc/mmdf.5000066400000000000000000000066521321473123000167250ustar00rootroot00000000000000.\" Project : tin .\" Module : mmdf.5 .\" Author : U. Janssen .\" Created : 2002-02-18 .\" Updated : .\" Notes : needs a lot of work .\" .TH mmdf 5 "February 18th, 2002" "Unix" "User Manuals" .\" .SH NAME MMDF \- Multi\-channel Memorandum Distribution Facility mailbox format .\" .SH DESCRIPTION This document describes the .B MMDF mailbox format used by some MTAs and MUAs (i.e. .BR scomail (1)) to store mail messages locally. .PP An .B MMDF mailbox is a text file containing an arbitrary number of e-mail messages. Each message consists of a postmark, followed by an e-mail message formatted according to \fBRFC822\fP / \fBRFC2822\fP, followed by a postmark. The file format is line-oriented. Lines are separated by line feed characters (ASCII 10). A postmark line consists of the four characters "^A^A^A^A" (Control-A; ASCII 1). .TP Example of a \fBMMDF\fP mailbox holding two mails: .RS .nf .sp ^A^A^A^A .br From: example@example.com .br To: example@example.org .br Subject: test .br .sp .br >From what I learned about the MMDF-format: .br .br ^A^A^A^A .br ^A^A^A^A .br From: example@example.com .br To: example@example.org .br Subject: test 2 .br .sp .br bar .br ^A^A^A^A .fi .RE .PP In contrast to most other single file mailbox formats like MBOXO and MBOXRD (see .BR mbox (5)) there is no need to quote/dequote "From "\-lines in .B MMDF mailboxes as such lines have no special meaning in this format. .PP If the modification-time (usually determined via .BR stat (2)) of a nonempty mailbox file is greater than the access-time the file has new mail. Many MUAs place a Status: header in each message to indicate which messages have already been read. .\" .SH LOCKING Since .B MMDF files are frequently accessed by multiple programs in parallel, .B MMDF files should generally not be accessed without locking. .PP Three different locking mechanisms (and combinations thereof) are in general use: .IP "\(bu" .BR fcntl (2) locking is mostly used on recent, POSIX-compliant systems. Use of this locking method is, in particular, advisable if .B MMDF files are accessed through the Network File System (NFS), since it seems the only way to reliably invalidate NFS clients' caches. .IP "\(bu" .BR flock (2) locking is mostly used on BSD-based systems. .PP If multiple methods are combined, implementors should make sure to use the non-blocking variants of the .BR fcntl (2) and .BR flock (2) system calls in order to avoid deadlocks. .PP If multiple methods are combined, an .B MMDF file must not be considered to have been successfully locked before all individual locks were obtained. When one of the individual locking methods fails, an application should release all locks it acquired successfully, and restart the entire locking procedure from the beginning, after a suitable delay. .PP The locking mechanism used on a particular system is a matter of local policy, and should be consistently used by all applications installed on the system which access .B MMDF files. Failure to do so may result in loss of e-mail data, and in corrupted .B MMDF files. .\" .\" .SH FILES .\" /usr/spool/mmdf/lock/home .\" $HOME/Mail/ .\" .\" .SH SECURITY .\" .SH "CONFORMING TO" .B MMDF is not part of any currently supported standard. .\" .SH HISTORY .B MMDF was developed at the University of Delaware by Dave Crocker. .\" .SH "SEE ALSO" .BR scomail (1), .BR fcntl (2), .BR flock (2), .BR link (2), .BR stat (2), .BR mbox (5), .BR RFC822 , .BR RFC2822 .SH AUTHOR Urs Janssen neomutt-neomutt-20171215/doc/neomutt.css000066400000000000000000000032071321473123000201120ustar00rootroot00000000000000body { margin-left:2%; margin-right:2%; font-family:serif; } .toc, .list-of-tables, .list-of-examples { font-family:sans-serif; } h1, h2, h3, h4, h5, h6 { font-family:sans-serif; } p { text-align:justify; } div.table p.title, div.example p.title { font-size:smaller; font-family:sans-serif; } .email, .email a { font-family:monospace; } div.table-contents table, div.informaltable table { border-collapse:collapse; border:1px solid #c0c0c0; } div.table-contents table td, div.informaltable td, div.table-contents table th, div.informaltable table th { padding:5px; text-align:left; } div.table-contents table th, div.informaltable table th { font-family:sans-serif; background:#d0d0d0; font-weight:bold; vertical-align:top; } div.cmdsynopsis { border-left:1px solid #707070; padding-left: 1em; } li div.cmdsynopsis { border-left:none; padding-left:0px; } li p { margin: 0; } pre.screen, div.note { border:1px solid #c0c0c0; margin-left:2%; margin-right:2%; } pre.screen { color: #ffffff; background:#000000; padding: 0.5em; } div.note { background:#ffff80; padding: 0.5em; } div.example p.title { margin-left:2%; } div.note h3 { font-size:small; font-style:italic; font-variant: small-caps; } div.note h3:after { content: ":" } div.note { margin-bottom: 5px; } div.literallayout, .command { font-family: monospace; font-weight: normal; } .command strong { font-weight: normal; } tr { vertical-align: top; } .comment { color:#00c000; } code.literal { background: #f0f0f0; color: #000000; } span.indicator { background: #000060; color: #ffffff; } span.highlight { background: #404040; color: #ffffff; } span.reverse { background: #ffffff; color: #000000; } neomutt-neomutt-20171215/doc/neomutt.man000066400000000000000000000152541321473123000201020ustar00rootroot00000000000000.\" -*-nroff-*- .\" .\" .\" Copyright (C) 1996-2016 Michael R. Elkins .\" .\" 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .TH neomutt 1 "January 2009" Unix "User Manuals" .SH NAME neomutt \- The Neomutt Mail User Agent .SH SYNOPSIS .PP .B neomutt [\-GnRyzZ] [\-e \fIcmd\fP] [\-F \fIfile\fP] [\-g \fIserver\fP] [\-m \fItype\fP] [\-f \fIfile\fP] .PP .B neomutt [\-Enx] [\-e \fIcmd\fP] [\-F \fIfile\fP] [\-H \fIfile\fP] [\-i \fIfile\fP] [\-s \fIsubj\fP] [\-b \fIaddr\fP] [\-c \fIaddr\fP] [\-a \fIfile\fP [...] \-\-] \fIaddr|mailto_url\fP [...] .PP .B neomutt [\-nx] [\-e \fIcmd\fP] [\-F \fIfile\fP] [\-s \fIsubj\fP] [\-b \fIaddr\fP] [\-c \fIaddr\fP] [\-a \fIfile\fP [...] \-\-] \fIaddr|mailto_url\fP [...] < message .PP .B neomutt [\-n] [\-e \fIcmd\fP] [\-F \fIfile\fP] \-p .PP .B neomutt [\-n] [\-e \fIcmd\fP] [\-F \fIfile\fP] \-A \fIalias\fP .PP .B neomutt [\-n] [\-e \fIcmd\fP] [\-F \fIfile\fP] \-Q \fIquery\fP .PP .B neomutt \-v[v] .PP .B neomutt \-D [\-S] .SH DESCRIPTION .PP Neomutt is a small but very powerful text based program for reading and sending electronic mail under unix operating systems, including support for color terminals, MIME, OpenPGP, and a threaded sorting mode. .PP .I Note: .IR This manual page gives a brief overview of neomutt's command line options. You should find a copy of the full manual in @docdir@, in text, HTML, and/or PDF format. .SH OPTIONS .PP .IP "-A \fIalias\fP" An expanded version of the given alias is passed to stdout. .IP "-a \fIfile\fP [...]" Attach a file to your message using MIME. When attaching single or multiple files, separating filenames and recipient addresses with "\-\-" is mandatory, e.g. \fBneomutt \-a image.jpg \-\- addr1\fP or \fBneomutt \-a img.jpg *.png \-\- addr1 addr2\fP. The \-a option must be placed at the end of command line options. .IP "-b \fIaddress\fP" Specify a blind-carbon-copy (BCC) recipient .IP "-c \fIaddress\fP" Specify a carbon-copy (CC) recipient .IP "-d \fIlevel\fP" log debugging output to ~/.neomuttdebug0. \fILevel\fP can range from 1-5 and effects verbosity. A value of 2 is recommended. .IP "-D" Print the value of all configuration options to stdout. .IP "-D -S" like -D but hide the value of sensitive variables .IP "-E" Causes the draft file specified by -H or include file specified by -i to be edited during message composition. .IP "-e \fIcommand\fP" Specify a configuration command to be run after processing of initialization files. .IP "-f \fImailbox\fP" Specify which mailbox to load. .IP "-F \fIneomuttrc\fP" Specify an initialization file to read instead of ~/.neomuttrc .IP "-g \fIserver\fP" Start Neomutt with a listing of subscribed newsgroups at specified news server. .IP "-G" Start Neomutt with a listing of subscribed newsgroups. .IP "-h" Display help. .IP "-H \fIdraft\fP" Specify a draft file which contains header and body to use to send a message. .IP "-i \fIinclude\fP" Specify a file to include into the body of a message. .IP "-m \fItype\fP " specify a default mailbox type for newly created folders. .IP "-n" Causes Neomutt to bypass the system configuration file. .IP "-p" Resume a postponed message. .IP "-Q \fIquery\fP" Query a configuration variables value. The query is executed after all configuration files have been parsed, and any commands given on the command line have been executed. .IP "-R" Open a mailbox in \fIread-only\fP mode. .IP "-s \fIsubject\fP" Specify the subject of the message. .IP "-v" Display the Neomutt version number and compile-time definitions. .IP "-vv" Display license and copyright information. .IP "-x" Emulate the mailx compose mode. .IP "-y" Start Neomutt with a listing of all mailboxes specified by the \fImailboxes\fP command. .IP "-z" When used with \-f, causes Neomutt not to start if there are no messages in the mailbox. .IP "-Z" Causes Neomutt to open the first mailbox specified by the \fImailboxes\fP command which contains new mail. .IP "--" Treat remaining arguments as \fIaddr\fP even if they start with a dash. See also "\-a" above. .SH ENVIRONMENT .PP .IP "EDITOR" Specifies the editor to use if VISUAL is unset. .IP "EMAIL" The user's e-mail address. .IP "HOME" Full path of the user's home directory. .IP "MAIL" Full path of the user's spool mailbox. .IP "MAILDIR" Full path of the user's spool mailbox if MAIL is unset. Commonly used when the spool mailbox is a .B maildir (5) folder. .IP "MAILCAPS" Path to search for mailcap files. .IP "MM_NOASK" If this variable is set, mailcap are always used without prompting first. .IP "PGPPATH" Directory in which the user's PGP public keyring can be found. When used with the original PGP program, neomutt and .B pgpring (1) rely on this being set. .IP "TMPDIR" Directory in which temporary files are created. .IP "REPLYTO" Default Reply-To address. .IP "VISUAL" Specifies the editor to use when composing messages. .SH FILES .PP .IP "~/.neomuttrc or ~/.neomutt/neomuttrc" User configuration file. .IP "@sysconfdir@/neomuttrc" System-wide configuration file. .IP "/tmp/neomuttXXXXXX" Temporary files created by Neomutt. .IP "~/.mailcap" User definition for handling non-text MIME types. .IP "@sysconfdir@/mailcap" System definition for handling non-text MIME types. .IP "~/.mime.types" User's personal mapping between MIME types and file extensions. .IP "@sysconfdir@/mime.types" System mapping between MIME types and file extensions. .IP "@docdir@/manual.txt" The Neomutt manual. .SH BUGS .PP See https://github.com/neomutt/neomutt/issues .SH NO WARRANTIES 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. .SH SEE ALSO .PP .BR curses (3), .BR mailcap (5), .BR maildir (5), .BR notmuch (1), .BR msmtp (1), .BR mbox (5), .BR neomuttrc (5), .BR ncurses (3), .BR sendmail (1), .BR smail (1). .PP Neomutt Home Page: http://www.neomutt.org/ .PP The Neomutt manual .SH AUTHOR .PP Michael Elkins, and others. Use to contact the developers. neomutt-neomutt-20171215/doc/neomutt.pwl000066400000000000000000000075071321473123000201330ustar00rootroot00000000000000personal_ws-1.1 en 515 unconvertable fIask fPcollapsed nosubject retainable multi CCs aol xysize onlyfirst vcard DCC PGPPASSFD delim fCmultipart fIoptional Debian's Bourne fCtext dat cfgdir encodings BackSpace fCstrftime unmono ungroup filenames mmdf formatoptions remail usernames usesystemcerts hdrs brightred bol del subexpressions fCsamples subthread invsmart unsubscribe autoencrypt cmd des GmbH resizes dev IANA abled fIfull proto awk imapd fBplease startup tokyocabinet fCSTARTTLS charset backends forw imaps fcc subtree anytopnm OpenSSL CC'ed cc'ed expandos confirmappend gpgme pathname unalternates SortMethods CTYPE crt netizens DNS subparts boolean keyid fIC pbmtoascii PerlMX fIpager autopgp smtp fBunread getkeys dsn Exim eol mutthistory unhdr fISetting keepalive theguys openURL esc eow fBmay gid eps bitmime gif fCenscript undeleted subdirectories builtin mailspool crypto xdigit uncolor euc fBdeprecated fmt fIdelay COLORFGBG hdr IDNs unignore fCpgpring expr frm preconnect fCgpg clearsigned fCstat idn subshell gpg Ezip replysignencrypted gecos getmail ESMTP ansi fIsusp GNU's regex Postfix checkinterval Fuz fCsmime fCapplication subprocess fImust mailto gnupg GnuPG fIfolder prepending organisation tlsv TLSv tmpdir apop Msgs composetyped jis fBnot inv inbox IRC runtime sslv SSLv fIthreads Online ipv IPv joe iso terminal's passwd replyencrypt UNIX's PureMessage printf autosign unscore RunningFirefox copiousoutput unauto fIbefore metadata secring gunzip nospam koi nroff egrep Luser backtick fCprintf sendmail formfeed defs subkeys fInew fCxterm metacharacters noop LIBIDN there're mozilla unattachments TripleDES prepend mh's fBstrongly netpbm ysize stdout fIusername backend fCmutt lpr imap NFS makedoc CApath moe qdbm stdin fCmessage preauthenticated oem NIS fIold unsubscribed keybindings spoolfile fInot unmailboxes punct mailhost verifyable Diffie muttdude MuttDude fBWarning euler subthreads MTA lastname msg lightgray MUA PGP's SYSCONFDIR smime autosmime Atts hdrdefault firefox smarthost pgp jpegs auth unmy starttls pka flowable fIindex variable's servernoise pkcs OR'd firstname ispell png NTLM clearsign RFC rfc syntaxes untag resolv fIsuccess hostname fIset fBNote lookup html muttrc Qmail config spammers fCset hcache'd sep gzcat cond enscript conf sig xsize maildirs fImaildir ascii fCLAST subexpression pubring fBif ldap noaskbcc wiki unsetting subfolders FCCs fCcopiousoutput xtitle wildcards relatime GnuTLS gnutls Afterwards undelete upcase realname cumulate uncollapse fCssh uid pnmtops tls tmp ssl mailx Anytown fIevery GETADDRINFO str metoo fIelse fInever POSIX stevef pipelined brightdefault jpeg quadoption commentable timestamp repl url misconfigured Pathnames UTF utf xanim SpamAssassin usr internet fIUnset fIunset fIyes whitespace juser replysign subdirectory mixmaster xpaint prev urlview sasl metamail VRML gssapi command's pagedown PageDown RunningX fIfirst featureful xmhcache mimetype fCuname cA checksum replyinline unlists downcase cb formatter gdbm fIyou filename askcc de kremvax mbox dh backtab fIgenerates dl PageDn fC fetchmail wrapmargin mailcap myscript Fi fI Cx dt buffy manny applica autoview diskspace fo fP fIif fIneedsterminal eV smtpd ie ppmtopgm addr unalternative stuffes il PKGDATADIR gz unmime smtps freshmeat jp fIMH preprocessing fIsequence md metacharacter mh backticks nF zsh libexec fIno remailers remailer's ph mx needsterminal rc pseudoheader alnum unalias qi nz Roessler ps fBDON'T TCFL hcache maildir elkins fIhdrs te fIexternal tf subfolder th bbdb rx fIun SomeoneElse autoedit un pageup PageUp inline iconv xf fIACS UW pgmtopbm autoinline OpenPGP sigs fIfailure nametemplate localhost multipart cachedir remailer YY filesystems pnmscale bcc substrings substring procmail bdb uname fIand fCsendmail aif CAfile fIenvelope aifc confirmcreate aiff username prepended fIFrom passphrase expando prepends login cntrl askbcc pagesize neomutt-neomutt-20171215/doc/neomutt.xsl000066400000000000000000000007151321473123000201310ustar00rootroot00000000000000 ]> text/css &css; neomutt-neomutt-20171215/doc/neomuttrc.head000066400000000000000000000071371321473123000205560ustar00rootroot00000000000000# # System configuration file for Mutt # # Default list of header fields to weed when displaying. # Ignore all lines by default... ignore * # ... then allow these through. unignore from: subject to cc date x-mailer x-url user-agent # Display the fields in this order hdr_order date from to cc subject # imitate the old search-body function macro index \eb "~b " "search in message bodies" # simulate the old url menu macro index,pager,attach,compose \cb "\ set my_pipe_decode=\$pipe_decode pipe_decode\ urlview\ set pipe_decode=\$my_pipe_decode; unset my_pipe_decode" \ "call urlview to extract URLs out of a message" # Show documentation when pressing F1 macro generic,pager " less @docdir@/manual.txt" "show Mutt documentation" # show the incoming mailboxes list (just like "mutt -y") and back when pressing "y" macro index,pager y "?" "show incoming mailboxes list" bind browser y exit # Handler for gzip compressed mailboxes # open-hook '\.gz$' "gzip --stdout --decompress '%f' > '%t'" # close-hook '\.gz$' "gzip --stdout '%t' > '%f'" # append-hook '\.gz$' "gzip --stdout '%t' >> '%f'" # If Mutt is unable to determine your site's domain name correctly, you can # set the default here. # # set hostname=cs.hmc.edu # If your sendmail supports the -B8BITMIME flag, enable the following # # set use_8bitmime # Use mime.types to look up handlers for application/octet-stream. Can # be undone with unmime_lookup. mime_lookup application/octet-stream ## ## *** DEFAULT SETTINGS FOR THE ATTACHMENTS PATCH *** ## ## ## Please see the manual (section "attachments") for detailed ## documentation of the "attachments" command. ## ## Removing a pattern from a list removes that pattern literally. It ## does not remove any type matching the pattern. ## ## attachments +A */.* ## attachments +A image/jpeg ## unattachments +A */.* ## ## This leaves "attached" image/jpeg files on the allowed attachments ## list. It does not remove all items, as you might expect, because the ## second */.* is not a matching expression at this time. ## ## Remember: "unattachments" only undoes what "attachments" has done! ## It does not trigger any matching on actual messages. ## Qualify any MIME part with an "attachment" disposition, EXCEPT for ## text/x-vcard and application/pgp parts. (PGP parts are already known ## to mutt, and can be searched for with ~g, ~G, and ~k.) ## ## I've added x-pkcs7 to this, since it functions (for S/MIME) ## analogously to PGP signature attachments. S/MIME isn't supported ## in a stock mutt build, but we can still treat it specially here. ## attachments +A */.* attachments -A text/x-vcard application/pgp.* attachments -A application/x-pkcs7-.* ## Discount all MIME parts with an "inline" disposition, unless they're ## text/plain. (Why inline a text/plain part unless it's external to the ## message flow?) ## attachments +I text/plain ## These two lines make Mutt qualify MIME containers. (So, for example, ## a message/rfc822 forward will count as an attachment.) The first ## line is unnecessary if you already have "attach-allow */.*", of ## course. These are off by default! The MIME elements contained ## within a message/* or multipart/* are still examined, even if the ## containers themselves don't qualify. ## #attachments +A message/.* multipart/.* #attachments +I message/.* multipart/.* ## You probably don't really care to know about deleted attachments. attachments -A message/external-body attachments -I message/external-body ## ## More settings ## neomutt-neomutt-20171215/doc/neomuttrc.man.head000066400000000000000000000650421321473123000213270ustar00rootroot00000000000000'\" t .\" -*-nroff-*- .\" .\" Copyright (C) 1996-2000 Michael R. Elkins .\" Copyright (C) 1999-2000 Thomas Roessler .\" .\" 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .de EX .nf .ft CW .. .de EE .ft .fi .. .TH neomuttrc 5 "September 2002" Unix "User Manuals" .SH NAME neomuttrc \- Configuration file for the Mutt Mail User Agent .SH DESCRIPTION .PP A neomutt configuration file consists of a series of \(lqcommands\(rq. Each line of the file may contain one or more commands. When multiple commands are used, they must be separated by a semicolon (\(lq\fB;\fP\(rq). .PP The hash mark, or pound sign (\(lq\fB#\fP\(rq), is used as a \(lqcomment\(rq character. You can use it to annotate your initialization file. All text after the comment character to the end of the line is ignored. .PP Single quotes (\(lq\fB'\fP\(rq) and double quotes (\(lq\fB"\fP\(rq) can be used to quote strings which contain spaces or other special characters. The difference between the two types of quotes is similar to that of many popular shell programs, namely that a single quote is used to specify a literal string (one that is not interpreted for shell variables or quoting with a backslash [see next paragraph]), while double quotes indicate a string which should be evaluated. For example, backticks are evaluated inside of double quotes, but not single quotes. .PP \fB\(rs\fP quotes the next character, just as in shells such as bash and zsh. For example, if want to put quotes (\(lq\fB"\fP\(rq) inside of a string, you can use \(lq\fB\(rs\fP\(rq to force the next character to be a literal instead of interpreted character. .PP \(lq\fB\(rs\(rs\fP\(rq means to insert a literal \(lq\fB\(rs\fP\(rq into the line. \(lq\fB\(rsn\fP\(rq and \(lq\fB\(rsr\fP\(rq have their usual C meanings of linefeed and carriage-return, respectively. .PP A \(lq\fB\(rs\fP\(rq at the end of a line can be used to split commands over multiple lines, provided that the split points don't appear in the middle of command names. .PP It is also possible to substitute the output of a Unix command in an initialization file. This is accomplished by enclosing the command in backticks (\fB`\fP\fIcommand\fP\fB`\fP). .PP UNIX environment variables can be accessed like the way it is done in shells like sh and bash: Prepend the name of the variable by a dollar (\(lq\fB\(Do\fP\(rq) sign. .PP .SH COMMANDS .PP .nf \fBalias\fP [\fB-group\fP \fIname\fP [...]] \fIkey\fP \fIaddress\fP [\fB,\fP \fIaddress\fP [ ... ]] \fBunalias\fP [\fB * \fP | \fIkey\fP ] .fi .IP \fBalias\fP defines an alias \fIkey\fP for the given addresses. Each \fIaddress\fP will be resolved into either an email address (user@example.com) or a named email address (User Name ). The address may be specified in either format, or in the format \(lquser@example.com (User Name)\(rq. \fBunalias\fP removes the alias corresponding to the given \fIkey\fP or all aliases when \(lq\fB*\fP\(rq is used as an argument. The optional \fB-group\fP argument to \fBalias\fP causes the aliased address(es) to be added to the named \fIgroup\fP. .PP .nf \fBgroup\fP [\fB-group\fP \fIname\fP] [\fB-rx\fP \fIEXPR\fP [ \fI...\fP ]] [\fB-addr\fP \fIaddress\fP [ \fI...\fP ]] \fBungroup\fP [\fB-group\fP \fIname\fP ] [ \fB*\fP | [[\fB-rx\fP \fIEXPR\fP [ \fI...\fP ]] [\fB-addr\fP \fIaddress\fP [ \fI...\fP ]]] .fi .IP \fBgroup\fP is used to directly add either addresses or regular expressions to the specified group or groups. The different categories of arguments to the \fBgroup\fP command can be in any order. The flags \fI-rx\fP and \fI-addr\fP specify what the following strings (that cannot begin with a hyphen) should be interpreted as: either a regular expression or an email address, respectively. \fBungroup\fP is used to remove addresses or regular expressions from the specified group or groups. The syntax is similar to the \fBgroup\fP command, however the special character \fB*\fP can be used to empty a group of all of its contents. .IP These address groups can also be created implicitly by the \fBalias\fP, \fBlists\fP, \fBsubscribe\fP and \fBalternates\fP commands by specifying the optional \fI-group\fP option. .IP Once defined, these address groups can be used in patterns to search for and limit the display to messages matching a group. .PP .nf \fBalternates\fP [\fB-group\fP \fIname\fP] \fIregex\fP [ \fIregex\fP [ ... ]] \fBunalternates\fP [\fB * \fP | \fIregex\fP [ \fIregex\fP [ ... ]] ] .fi .IP \fBalternates\fP is used to inform neomutt about alternate addresses where you receive mail; you can use regular expressions to specify alternate addresses. This affects neomutt's idea about messages from you, and messages addressed to you. \fBunalternates\fP removes a regular expression from the list of known alternates. The \fB-group\fP flag causes all of the subsequent regular expressions to be added to the named group. .PP .nf \fBalternative_order\fP \fItype\fP[\fB/\fP\fIsubtype\fP] [ ... ] \fBunalternative_order\fP [\fB * \fP | \fItype\fP/\fIsubtype\fP] [...] .fi .IP \fBalternative_order\fP command permits you to define an order of preference which is used by neomutt to determine which part of a \fBmultipart/alternative\fP body to display. A subtype of \(lq\fB*\fP\(rq matches any subtype, as does an empty subtype. \fBunalternative_order\fP removes entries from the ordered list or deletes the entire list when \(lq\fB*\fP\(rq is used as an argument. .PP .nf \fBauto_view\fP \fItype\fP[\fB/\fP\fIsubtype\fP] [ ... ] \fBunauto_view\fP \fItype\fP[\fB/\fP\fIsubtype\fP] [ ... ] .fi .IP This commands permits you to specify that neomutt should automatically convert the given MIME types to text/plain when displaying messages. For this to work, there must be a .BR mailcap (5) entry for the given MIME type with the .B copiousoutput flag set. A subtype of \(lq\fB*\fP\(rq matches any subtype, as does an empty subtype. .PP .nf \fBmime_lookup\fP \fItype\fP[\fB/\fP\fIsubtype\fP] [ ... ] \fBunmime_lookup\fP \fItype\fP[\fB/\fP\fIsubtype\fP] [ ... ] .fi .IP This command permits you to define a list of "data" MIME content types for which neomutt will try to determine the actual file type from the file name, and not use a .BR mailcap (5) entry given for the original MIME type. For instance, you may add the \fBapplication/octet-stream\fP MIME type to this list. .TP \fBbind\fP \fImap1,map2,...\fP \fIkey\fP \fIfunction\fP This command binds the given \fIkey\fP for the given \fImap\fP or maps to the given \fIfunction\fP. Multiple maps may be specified by separating them with commas (no whitespace is allowed). .IP Valid maps are: .BR generic ", " alias ", " attach ", " .BR browser ", " editor ", " .BR index ", " compose ", " .BR pager ", " pgp ", " postpone ", " .BR mix . .IP For more information on keys and functions, please consult the Mutt Manual. Note that the function name is to be specified without angle brackets. .TP \fBaccount-hook\fP [\fB!\fP]\fIregex\fP \fIcommand\fP This hook is executed whenever you access a remote mailbox. Useful to adjust configuration settings to different IMAP or POP servers. .TP \fBcharset-hook\fP \fIalias\fP \fIcharset\fP This command defines an alias for a character set. This is useful to properly display messages which are tagged with a character set name not known to neomutt. .TP \fBiconv-hook\fP \fIcharset\fP \fIlocal-charset\fP This command defines a system-specific name for a character set. This is useful when your system's .BR iconv (3) implementation does not understand MIME character set names (such as .BR iso-8859-1 ), but instead insists on being fed with implementation-specific character set names (such as .BR 8859-1 ). In this specific case, you'd put this into your configuration file: .IP .B "iconv-hook iso-8859-1 8859-1" .TP \fBmessage-hook\fP [\fB!\fP]\fIpattern\fP \fIcommand\fP Before neomutt displays (or formats for replying or forwarding) a message which matches the given \fIpattern\fP (or, when it is preceded by an exclamation mark, does not match the \fIpattern\fP), the given \fIcommand\fP is executed. When multiple \fBmessage-hook\fPs match, they are executed in the order in which they occur in the configuration file. .TP \fBtimeout-hook\fP \fIcommand\fP Run a command periodically when Mutt checks for new mail. This hook is called every $timeout seconds. .TP \fBstartup-hook\fP \fIcommand\fP Before neomutt opens the first mailbox when first starting, mutt will run the startup hook for the given \fIcommand\fP. .TP \fBshutdown-hook\fP \fIcommand\fP Before neomutt is about to exit, and before the mailbox is closed, mutt will run the shutdown hook for the given \fIcommand\fP. .TP \fBfolder-hook\fP [\fB!\fP]\fIregex\fP \fIcommand\fP When neomutt enters a folder which matches \fIregex\fP (or, when \fIregex\fP is preceded by an exclamation mark, does not match \fIregex\fP), the given \fIcommand\fP is executed. .IP When several \fBfolder-hook\fPs match a given mail folder, they are executed in the order given in the configuration file. .TP \fBmacro\fP \fImap\fP \fIkey\fP \fIsequence\fP [ \fIdescription\fP ] This command binds the given \fIsequence\fP of keys to the given \fIkey\fP in the given \fImap\fP or maps. For valid maps, see \fBbind\fP. To specify multiple maps, put only a comma between the maps. .PP .nf \fBcolor\fP \fIobject\fP \fIforeground\fP \fIbackground\fP [ \fIregex\fP ] \fBcolor\fP index \fIforeground\fP \fIbackground\fP [ \fIpattern\fP ] \fBuncolor\fP index \fIpattern\fP [ \fIpattern\fP ... ] .fi .IP If your terminal supports color, these commands can be used to assign \fIforeground\fP/\fIbackground\fP combinations to certain objects. Valid objects are: .BR attachment ", " body ", " bold ", " error ", " header ", " .BR hdrdefault ", " index ", " indicator ", " markers ", " .BR message ", " normal ", " prompt ", " quoted ", " quoted\fIN\fP ", " .BR search ", " signature ", " status ", " tilde ", " tree ", " .BR underline . If the sidebar is enabled the following objects are also valid: .BR sidebar_divider ", " sidebar_flagged ", " sidebar_highlight ", " .BR sidebar_indicator ", " sidebar_new ", " sidebar_spoolfile . The .BR body " and " header objects allow you to restrict the colorization to a regular expression. The \fBindex\fP object permits you to select colored messages by pattern. .IP Valid colors include: .BR white ", " black ", " green ", " magenta ", " blue ", " .BR cyan ", " yellow ", " red ", " default ", " color\fIN\fP . .PP .nf \fBmono\fP \fIobject\fP \fIattribute\fP [ \fIregex\fP ] \fBmono\fP index \fIattribute\fP [ \fIpattern\fP ] .fi .IP For terminals which don't support color, you can still assign attributes to objects. Valid attributes include: .BR none ", " bold ", " underline ", " .BR reverse ", and " standout . .TP [\fBun\fP]\fBignore\fP \fIpattern\fP [ \fIpattern\fP ... ] The \fBignore\fP command permits you to specify header fields which you usually don't wish to see. Any header field whose tag \fIbegins\fP with an \(lqignored\(rq pattern will be ignored. .IP The \fBunignore\fP command permits you to define exceptions from the above mentioned list of ignored headers. .PP .nf \fBlists\fP [\fB-group\fP \fIname\fP] \fIregex\fP [ \fIregex\fP ... ] \fBunlists\fP \fIregex\fP [ \fIregex\fP ... ] \fBsubscribe\fP [\fB-group\fP \fIname\fP] \fIregex\fP [ \fIregex\fP ... ] \fBunsubscribe\fP \fIregex\fP [ \fIregex\fP ... ] .fi .IP Mutt maintains two lists of mailing list address patterns, a list of subscribed mailing lists, and a list of known mailing lists. All subscribed mailing lists are known. Patterns use regular expressions. .IP The \fBlists\fP command adds a mailing list address to the list of known mailing lists. The \fBunlists\fP command removes a mailing list from the lists of known and subscribed mailing lists. The \fBsubscribe\fP command adds a mailing list to the lists of known and subscribed mailing lists. The \fBunsubscribe\fP command removes it from the list of subscribed mailing lists. The \fB-group\fP flag adds all of the subsequent regular expressions to the named group. .TP \fBmbox-hook\fP [\fB!\fP]\fIregex\fP \fImailbox\fP When neomutt changes to a mail folder which matches \fIregex\fP, \fImailbox\fP will be used as the \(lqmbox\(rq folder, i.e., read messages will be moved to that folder when the mail folder is left. .IP The first matching \fBmbox-hook\fP applies. .PP .nf \fBmailboxes\fP \fIfilename\fP [ \fIfilename\fP ... ] \fBunmailboxes\fP [ \fB*\fP | \fIfilename\fP ... ] .fi .IP The \fBmailboxes\fP specifies folders which can receive mail and which will be checked for new messages. When changing folders, pressing space will cycle through folders with new mail. The \fBunmailboxes\fP command is used to remove a file name from the list of folders which can receive mail. If "\fB*\fP" is specified as the file name, the list is emptied. .PP .nf \fBmy_hdr\fP \fIstring\fP \fBunmy_hdr\fP \fIfield\fP .fi .IP Using \fBmy_hdr\fP, you can define headers which will be added to the messages you compose. \fBunmy_hdr\fP will remove the given user-defined headers. .TP \fBhdr_order\fP \fIheader1\fP \fIheader2\fP [ ... ] With this command, you can specify an order in which neomutt will attempt to present headers to you when viewing messages. .TP \fBsave-hook\fP [\fB!\fP]\fIpattern\fP \fIfilename\fP When a message matches \fIpattern\fP, the default file name when saving it will be the given \fIfilename\fP. .TP \fBfcc-hook\fP [\fB!\fP]\fIpattern\fP \fIfilename\fP When an outgoing message matches \fIpattern\fP, the default file name for storing a copy (fcc) will be the given \fIfilename\fP. .TP \fBfcc-save-hook\fP [\fB!\fP]\fIpattern\fP \fIfilename\fP This command is an abbreviation for identical \fBfcc-hook\fP and \fBsave-hook\fP commands. .TP \fBsend-hook\fP [\fB!\fP]\fIpattern\fP \fIcommand\fP When composing a message matching \fIpattern\fP, \fIcommand\fP is executed. When multiple \fBsend-hook\fPs match, they are executed in the order in which they occur in the configuration file. .TP \fBsend2-hook\fP [\fB!\fP]\fIpattern\fP \fIcommand\fP Whenever a message matching \fIpattern\fP is changed (either by editing it or by using the compose menu), \fIcommand\fP is executed. When multiple \fBsend2-hook\fPs match, they are executed in the order in which they occur in the configuration file. Possible applications include setting the $sendmail variable when a message's from header is changed. .IP \fBsend2-hook\fP execution is not triggered by use of \fBenter-command\fP from the compose menu. .TP \fBreply-hook\fP [\fB!\fP]\fIpattern\fP \fIcommand\fP When replying to a message matching \fIpattern\fP, \fIcommand\fP is executed. When multiple \fBreply-hook\fPs match, they are executed in the order in which they occur in the configuration file, but all \fBreply-hook\fPs are matched and executed before \fBsend-hook\fPs, regardless of their order in the configuration file. .TP \fBcrypt-hook\fP \fIregex\fP \fIkey-id\fP The crypt-hook command provides a method by which you can specify the ID of the public key to be used when encrypting messages to a certain recipient. The meaning of "key ID" is to be taken broadly: This can be a different e-mail address, a numerical key ID, or even just an arbitrary search string. You may use multiple \fBcrypt-hook\fPs with the same \fIregex\fP; multiple matching \fBcrypt-hook\fPs result in the use of multiple \fIkey-id\fPs for a recipient. .PP .nf \fBopen-hook\fP \fIregex\fP "\fIcommand\fP" \fBclose-hook\fP \fIregex\fP "\fIcommand\fP" \fBappend-hook\fP \fIregex\fP "\fIcommand\fP" .fi .IP These commands provide a way to handle compressed folders. The given \fBregex\fP specifies which folders are taken as compressed (e.g. "\fI\\\\.gz$\fP"). The commands tell Mutt how to uncompress a folder (\fBopen-hook\fP), compress a folder (\fBclose-hook\fP) or append a compressed mail to a compressed folder (\fBappend-hook\fP). The \fIcommand\fP string is the .BR printf (3) like format string, and it should accept two parameters: \fB%f\fP, which is replaced with the (compressed) folder name, and \fB%t\fP which is replaced with the name of the temporary folder to which to write. .TP \fBpush\fP \fIstring\fP This command adds the named \fIstring\fP to the keyboard buffer. .PP .nf \fBset\fP [\fBno\fP|\fBinv\fP|\fB&\fP|\fB?\fP]\fIvariable\fP[=\fIvalue\fP] [ ... ] \fBtoggle\fP \fIvariable\fP [ ... ] \fBunset\fP \fIvariable\fP [ ... ] \fBreset\fP \fIvariable\fP [ ... ] .fi .IP These commands are used to set and manipulate configuration variables. .IP Mutt knows four basic types of variables: boolean, number, string and quadoption. Boolean variables can be \fBset\fP (true), \fBunset\fP (false), or \fBtoggle\fPd. Number variables can be assigned a positive integer value. .IP String variables consist of any number of printable characters. Strings must be enclosed in quotes if they contain spaces or tabs. You may also use the \(lqC\(rq escape sequences \fB\\n\fP and \fB\\t\fP for newline and tab, respectively. .IP Quadoption variables are used to control whether or not to be prompted for certain actions, or to specify a default action. A value of \fByes\fP will cause the action to be carried out automatically as if you had answered yes to the question. Similarly, a value of \fBno\fP will cause the the action to be carried out as if you had answered \(lqno.\(rq A value of \fBask-yes\fP will cause a prompt with a default answer of \(lqyes\(rq and \fBask-no\fP will provide a default answer of \(lqno.\(rq .IP The \fBreset\fP command resets all given variables to the compile time defaults. If you reset the special variable \fBall\fP, all variables will reset to their compile time defaults. .TP \fBsource\fP \fIfilename\fP The given file will be evaluated as a configuration file. .PP .nf \fBspam\fP \fIpattern\fP \fIformat\fP \fBnospam\fP \fIpattern\fP .fi .IP These commands define spam-detection patterns from external spam filters, so that neomutt can sort, limit, and search on ``spam tags'' or ``spam attributes'', or display them in the index. See the Mutt manual for details. .TP \fBunhook\fP [\fB * \fP | \fIhook-type\fP ] This command will remove all hooks of a given type, or all hooks when \(lq\fB*\fP\(rq is used as an argument. \fIhook-type\fP can be any of the \fB-hook\fP commands documented above. .PP .nf \fBmailto_allow\fP \fIheader-field\fP [ ... ] \fBunmailto_allow\fP [ \fB*\fP | \fIheader-field\fP ... ] .fi .IP These commands allow the user to modify the list of allowed header fields in a \fImailto:\fP URL that Mutt will include in the the generated message. By default the list contains \fBsubject\fP and \fBbody\fP, as specified by RFC2368; and \fBcc\fP, \fBin-reply-to\fP, and \fBreferences\fP to aid with mailto links from mailing lists. .SH PATTERNS .PP In various places with neomutt, including some of the above mentioned \fBhook\fP commands, you can specify patterns to match messages. .SS Constructing Patterns .PP A simple pattern consists of an operator of the form \(lq\fB~\fP\fIcharacter\fP\(rq, possibly followed by a parameter against which neomutt is supposed to match the object specified by this operator. For some \fIcharacter\fPs, the \fB~\fP may be replaced by another character to alter the behavior of the match. These are described in the list of operators, below. .PP With some of these operators, the object to be matched consists of several e-mail addresses. In these cases, the object is matched if at least one of these e-mail addresses matches. You can prepend a hat (\(lq\fB^\fP\(rq) character to such a pattern to indicate that \fIall\fP addresses must match in order to match the object. .PP You can construct complex patterns by combining simple patterns with logical operators. Logical AND is specified by simply concatenating two simple patterns, for instance \(lq~C neomutt-dev ~s bug\(rq. Logical OR is specified by inserting a vertical bar (\(lq\fB|\fP\(rq) between two patterns, for instance \(lq~C neomutt-dev | ~s bug\(rq. Additionally, you can negate a pattern by prepending a bang (\(lq\fB!\fP\(rq) character. For logical grouping, use braces (\(lq()\(rq). Example: \(lq!(~t neomutt|~c mutt) ~f elkins\(rq. .SS Simple Patterns .PP Mutt understands the following simple patterns: .P .PD 0 .TP 12 ~A all messages .TP ~b \fIEXPR\fP messages which contain \fIEXPR\fP in the message body. .TP =b \fISTRING\fP messages which contain \fISTRING\fP in the message body. If IMAP is enabled, searches for \fISTRING\fP on the server, rather than downloading each message and searching it locally. .TP ~B \fIEXPR\fP messages which contain \fIEXPR\fP in the whole message. .TP ~c \fIEXPR\fP messages carbon-copied to \fIEXPR\fP .TP %c \fIGROUP\fP messages carbon-copied to any member of \fIGROUP\fP .TP ~C \fIEXPR\fP messages either to: or cc: \fIEXPR\fP .TP %C \fIGROUP\fP messages either to: or cc: to any member of \fIGROUP\fP .TP ~d \fIMIN\fP-\fIMAX\fP messages with \(lqdate-sent\(rq in a Date range .TP ~D deleted messages .TP ~e \fIEXPR\fP messages which contain \fIEXPR\fP in the \(lqSender\(rq field .TP %e \fIGROUP\fP messages which contain a member of \fIGROUP\fP in the \(lqSender\(rq field .TP ~E expired messages .TP ~f \fIEXPR\fP messages originating from \fIEXPR\fP .TP %f \fIGROUP\fP messages originating from any member of \fIGROUP\fP .TP ~F flagged messages .TP ~g PGP signed messages .TP ~G PGP encrypted messages .TP ~h \fIEXPR\fP messages which contain \fIEXPR\fP in the message header .TP ~H \fIEXPR\fP messages with spam tags matching \fIEXPR\fP .TP ~i \fIEXPR\fP messages which match \fIEXPR\fP in the \(lqMessage-ID\(rq field .TP ~k messages containing PGP key material .TP ~l messages addressed to a known mailing list (defined by either \fBsubscribe\fP or \fBlist\fP) .TP ~L \fIEXPR\fP messages either originated or received by \fIEXPR\fP .TP %L \fIGROUP\fP messages either originated or received by any member of \fIGROUP\fP .TP ~m \fIMIN\fP-\fIMAX\fP message in the range \fIMIN\fP to \fIMAX\fP .TP ~n \fIMIN\fP-\fIMAX\fP messages with a score in the range \fIMIN\fP to \fIMAX\fP .TP ~N new messages .TP ~O old messages .TP ~p messages addressed to you (as defined by \fBalternates\fP) .TP ~P messages from you (as defined by \fBalternates\fP) .TP ~Q messages which have been replied to .TP ~r \fIMIN\fP-\fIMAX\fP messages with \(lqdate-received\(rq in a Date range .TP ~R read messages .TP ~s \fIEXPR\fP messages having \fIEXPR\fP in the \(lqSubject\(rq field. .TP ~S superseded messages .TP ~t \fIEXPR\fP messages addressed to \fIEXPR\fP .TP ~T tagged messages .TP ~u messages addressed to a subscribed mailing list (defined by \fBsubscribe\fP commands) .TP ~U unread messages .TP ~v message is part of a collapsed thread. .TP ~V cryptographically verified messages .TP ~x \fIEXPR\fP messages which contain \fIEXPR\fP in the \(lqReferences\(rq or \(lqIn-Reply-To\(rq field .TP ~X \fIMIN\fP-\fIMAX\fP messages with MIN - MAX attachments .TP ~y \fIEXPR\fP messages which contain \fIEXPR\fP in the \(lqX-Label\(rq field .TP ~z \fIMIN\fP-\fIMAX\fP messages with a size in the range \fIMIN\fP to \fIMAX\fP .TP ~= duplicated messages (see $duplicate_threads) .TP ~$ unreferenced message (requires threaded view) .TP ~(PATTERN) messages in threads containing messages matching a certain pattern, e.g. all threads containing messages from you: ~(~P) .TP ~<(PATTERN) messages whose immediate parent matches PATTERN, e.g. replies to your messages: ~<(~P) .TP ~>(PATTERN) messages having an immediate child matching PATTERN, e.g. messages you replied to: ~>(~P) .PD 1 .DT .PP In the above, \fIEXPR\fP is a regular expression. .PP With the \fB~d\fP, \fB~m\fP, \fB~n\fP, \fB~r\fP, \fB~X\fP, and \fB~z\fP operators, you can also specify ranges in the forms \fB<\fP\fIMAX\fP, \fB>\fP\fIMIN\fP, \fIMIN\fP\fB-\fP, and \fB-\fP\fIMAX\fP. .PP With the \fB~z\fP operator, the suffixes \(lqK\(rq and \(lqM\(rq are allowed to specify kilobyte and megabyte respectively. .SS Matching dates .PP The \fB~d\fP and \fB~r\fP operators are used to match date ranges, which are interpreted to be given in your local time zone. .PP A date is of the form \fIDD\fP[\fB/\fP\fIMM\fP[\fB/\fP[\fIcc\fP]\fIYY\fP]], that is, a two-digit date, optionally followed by a two-digit month, optionally followed by a year specifications. Omitted fields default to the current month and year. .PP Mutt understands either two or four digit year specifications. When given a two-digit year, neomutt will interpret values less than 70 as lying in the 21st century (i.e., \(lq38\(rq means 2038 and not 1938, and \(lq00\(rq is interpreted as 2000), and values greater than or equal to 70 as lying in the 20th century. .PP Note that this behavior \fIis\fP Y2K compliant, but that neomutt \fIdoes\fP have a Y2.07K problem. .PP If a date range consists of a single date, the operator in question will match that precise date. If the date range consists of a dash (\(lq\fB-\fP\(rq), followed by a date, this range will match any date before and up to the date given. Similarly, a date followed by a dash matches the date given and any later point of time. Two dates, separated by a dash, match any date which lies in the given range of time. .PP You can also modify any absolute date by giving an error range. An error range consists of one of the characters .BR + , .BR - , .BR * , followed by a positive number, followed by one of the unit characters .BR y , .BR m , .BR w ", or" .BR d , specifying a unit of years, months, weeks, or days. .B + increases the maximum date matched by the given interval of time, .B - decreases the minimum date matched by the given interval of time, and .B * increases the maximum date and decreases the minimum date matched by the given interval of time. It is possible to give multiple error margins, which cumulate. Example: .B "1/1/2001-1w+2w*3d" .PP You can also specify offsets relative to the current date. An offset is specified as one of the characters .BR < , .BR > , .BR = , followed by a positive number, followed by one of the unit characters .BR y , .BR m , .BR w ", or" .BR d . .B > matches dates which are older than the specified amount of time, an offset which begins with the character .B < matches dates which are more recent than the specified amount of time, and an offset which begins with the character .B = matches points of time which are precisely the given amount of time ago. .SH CONFIGURATION VARIABLES neomutt-neomutt-20171215/doc/neomuttrc.man.tail000066400000000000000000000005361321473123000213540ustar00rootroot00000000000000.\" -*-nroff-*- .SH SEE ALSO .PP .BR iconv (1), .BR iconv (3), .BR mailcap (5), .BR maildir (5), .BR mbox (5), .BR mutt (1), .BR printf (3), .BR regex (7), .BR strftime (3) .PP The Neomutt Manual .PP The Neomutt home page: http://www.neomutt.org/ .SH AUTHOR .PP Michael Elkins, and others. Use to contact the developers. neomutt-neomutt-20171215/doc/pgpewrap.1000066400000000000000000000031241321473123000176120ustar00rootroot00000000000000.\" -*-nroff-*- .\" .\" pgpewrap, a command line munging tool .\" Manpage Copyright (c) 2013 Honza Horak .\" .\" 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .TH pgpewrap 1 "May 2013" Unix "User Manuals" .SH NAME pgpewrap \- Mutt command line munging tool .SH SYNTAX .PP \fBpgpewrap\fP [ \fBflags\fP ] \-\- \fBprefix\fP [ \fBrecipients\fP ] .SH DESCRIPTION .PP This is a little C program which does some command line munging: The first argument is a command to be executed. When \fBpgpewrap\fP encounters a "\-\-" (dash\-dash) argument, it will interpret the next argument as a prefix which is put in front of all following arguments. .SH EXAMPLE pgpewrap pgpe file \-\- \-r a b c will execute: pgpe file -r a -r b -r c This script is needed with PGP 5 and with GPG, since their command line interfaces can't be properly served by mutt's format mechanism. neomutt-neomutt-20171215/doc/pgpring.1000066400000000000000000000035151321473123000174370ustar00rootroot00000000000000.\" -*-nroff-*- .\" .\" pgpring, a key ring dumper .\" Manpage Copyright (c) 2004-2013 Matthew Wilcox, Honza Horak .\" .\" 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .TH pgpring 1 "May 2013" Unix "User Manuals" .SH NAME pgpring \- Mutt key ring dumper .SH SYNTAX .PP \fBpgpring\fP [ \fB\-k\fP \fIkeyring\fP ] [ \fB\-2\fP | \fB\-5\fP ] [ \fB\-s\fP ] [ \fB\-S\fP ] [ \fB\-f\fP ] .SH DESCRIPTION .PP pgpring is a key ring dumper. It extracts information from PGP's binary key ring and emits it in an (almost) readable output format understood by mutt's key selection routines. This output format mimics the one used by the GNU Privacy Guard (GPG). .SH OPTIONS .TP .BI \-k " keyring" Dump the contents of the specified keyring. .TP .B \-2 Use the default keyring for PGP 2.x. .TP .B \-5 Use the default keyring for PGP 5. .TP .B \-s Dump the secret keyring. .TP .B \-S Dump signatures. .TP .B \-f Dump fingerprints. .SH ENVIRONMENT .PP .IP "HOME" Full path of the user's home directory. .IP "PGPPATH" Directory in which the user's PGP public keyring can be found. .SH AUTHORS Thomas Roessler neomutt-neomutt-20171215/doc/smime-notes.txt000066400000000000000000000102021321473123000206770ustar00rootroot00000000000000How to use mutt's S/MIME capabilities - Add the contents of contrib/smime.rc to your .muttrc. Don't worry about changing the smime_default_key line at this point -- you'll change it later. - Run 'smime_keys init'. - Download and install OpenSSL. - Get yourself a certificate. (You can get one for free from Comodo, or pay for one from VeriSign or one of its competitors) The way the process generally works, the certificate will be installed "into" your web browser. - Assuming you are using Firefox, follow the instructions at https://www.sslsupportdesk.com/export-certificate-firefox/ to export the certificate into a file called cert.p12. If you don't use Firefox, you're on your own. - Run "smime_keys add_p12 cert.p12" * When the script asks for the "Import password", enter the one you provided when you exported the certificate. * When it asks for a "PEM pass phrase", make up a new password. Every time you sign or decrypt a message, mutt will ask for the PEM pass phrase. * Finally, when the script asks for a label, enter an easy-to-remember name for the certificate, such as "me". The script output will include a line like: added private key: /home/raldi/.smime/keys/12345678.0 for raldi@verisignlabs.com The number (including the ".0" at the end) is your keyid. You will need this number in the next step. - Edit the smime_default_key line in your muttrc, replacing the keyid with your own. - You probably want to import trusted root certificates from Mozilla or the system. This makes you trust anything that was ultimately signed by one of them. You can use "smime_keys add_root" to do so, or just download https://curl.haxx.se/ca/cacert.pem into the place you point mutt's smime_ca_location variable to. Other notes Key management is done in a way similar to OpenSSL's CA directory. Private keys and certificates are stored in different directories, as OpenSSL expects either to be supplied in a (distinct) file. Each directory contains an unsorted file named '.index' wherein each line has several fields: mailbox, keyid, label, id of the intermediate certificate and keyflags. * Keyid is a hashvalue derived from the subject field of a certificate and supplied by OpenSSL. * The mailbox address is derived from either From or Sender field of the message, and matched with the email field of the certificate. Non matching address pairs get rejected, as get certificates not containing a mailbox address at all. (These are security issues, that perhaps should be configurable.) * Label is set by the perl script (it will ask you to supply one), when you add your keypair to the database. So are the remaining two fields. * keyflags are set with certificate verification option of the perl script. It may take as value one of the following: i: invalid (verification failed), r: revoked, e: expired, u: unverified, v: successfully verified and finally t: trusted, in case it was successfully verified and you chose to trust the certificate (the script will ask you). Mutt will not use invalid, revoked or expired certificates for signing or encryption. It will ask for confirmation before using unverified certificates, and finally it will issue a warning before using successfully verified but untrusted certificates. The purpose fields of a certificate do not get verified yet, also there is no real check if the given file is a certificate at all. Key retrieval is done obviously by searching the index file for a given mailbox. If none is found, the user is presented a list of available keys and asked to select one of those. The certificate and key directories specified in muttrc have to exist. Mutt will not create them. If you wish to sign messages yourself, note that this mutt does not address any PKCS10 or PKCS12 issues (yet?); that is, you have to get a valid certificate outside of mutt. (See above) A certificate can be viewed by adding the following to your ~/.mailcap: application/x-pkcs7-signature;openssl pkcs7 -in %s -inform der -noout \ -print_certs -text | less; needsterminal neomutt-neomutt-20171215/doc/smime_keys.1000066400000000000000000000051251321473123000201350ustar00rootroot00000000000000.\" -*-nroff-*- .\" .\" .\" Copyright (C) 2001,2002 Oliver Ehli .\" Copyright (C) 2001 Mike Schiraldi .\" Copyright (C) 2003 Bjoern Jacke .\" .\" 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .TH smime_keys 1 "May 2009" Unix "User Manuals" .SH "NAME" smime_keys \- Utility to add S/MIME certificate to the internal database used by mutt .SH SYNOPSIS .PP .B smime_keys [file(s) | keyID [file(s)]] .SH "DESCRIPTION" The purpose of this tool is to manipulate the internal database of S/MIME certificates used by mutt to sign mail messages which will be sent or to verify mail messages received and signed with S/MIME .SH OPTIONS .PP .IP \fBinit\fP no files needed, inits directory structure. .IP \fBlist\fP lists the certificates stored in database. .IP \fBlabel\fP keyID required. changes/removes/adds label. .IP \fBremove\fP keyID required. .IP \fBverify\fP 1=keyID and optionally 2=CRL Verifies the certificate chain, and optionally whether this certificate is included in supplied CRL (PEM format). Note: to verify all certificates at the same time, replace keyID with "all" .IP \fBadd_cert\fP certificate required. .IP \fBadd_chain\fP three files reqd: 1=Key, 2=certificate plus 3=intermediate certificate(s). .IP \fBadd_p12\fP one file reqd. Adds keypair to database. file is PKCS12 (e.g. export from netscape). .IP \fBadd_pem\fP one file reqd. Adds keypair to database. (file was converted from e.g. PKCS12). .IP \fBadd_root\fP one file reqd. Adds PEM root certificate to the location specified within muttrc (smime_verify_* command) .SH NO WARRANTIES 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. .PP Mutt Home Page: http://www.mutt.org/ neomutt-neomutt-20171215/doxygen/000077500000000000000000000000001321473123000166135ustar00rootroot00000000000000neomutt-neomutt-20171215/doxygen/doxygen.conf000066400000000000000000002333701321473123000211470ustar00rootroot00000000000000# Doxyfile 1.8.13 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "NeoMutt" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = 2017-12-15 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Teaching an old dog new tricks" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = contrib/logo/neomutt-64.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 0. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = YES # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = doxygen/layout.xml # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. FILE_PATTERNS = *.c *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = autosetup doc pgpewrap.c pgppubring.c # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = "sed -e 's/^ \* \(struct\|enum\|union\) [^ ]\+ - / * /' -e 's/^ \* [a-zA-Z0-9_]\+ - / * /' -e 's/^ \* Copyright ([Cc]) [-0-9,]\+ \([^<]*\)\( <.*>\)*/ * - \1/'" # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = doxygen/header.html # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = doxygen/footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = doxygen/doxygen.css # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 120 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 150 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 100 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO GENERATE_HTMLHELP = NO # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 200 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES USE_MATHJAX = NO # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /