1 /**
2  * @file
3  * Display version and copyright about NeoMutt
4  *
5  * @authors
6  * Copyright (C) 1996-2007 Michael R. Elkins <me@mutt.org>
7  * Copyright (C) 1999-2007 Thomas Roessler <roessler@does-not-exist.org>
8  * Copyright (C) 2016-2017 Richard Russon <rich@flatcap.org>
9  *
10  * @copyright
11  * This program is free software: you can redistribute it and/or modify it under
12  * the terms of the GNU General Public License as published by the Free Software
13  * Foundation, either version 2 of the License, or (at your option) any later
14  * version.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License along with
22  * this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 /**
26  * @page neo_version Display version and copyright about NeoMutt
27  *
28  * Display version and copyright about NeoMutt
29  */
30 
31 #include "config.h"
32 #include <errno.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/utsname.h>
37 #include <unistd.h>
38 #include "mutt/lib.h"
39 #include "gui/lib.h" // IWYU pragma: keep
40 #include "version.h"
41 #include "compress/lib.h"
42 #ifdef HAVE_LIBIDN
43 #include "address/lib.h"
44 #endif
45 #ifdef CRYPT_BACKEND_GPGME
46 #include "ncrypt/lib.h"
47 #endif
48 #ifdef HAVE_NOTMUCH
49 #include <notmuch.h>
50 #endif
51 #ifdef USE_SSL_OPENSSL
52 #include <openssl/opensslv.h>
53 #endif
54 #ifdef USE_SSL_GNUTLS
55 #include <gnutls/gnutls.h>
56 #endif
57 #ifdef HAVE_PCRE2
58 #define PCRE2_CODE_UNIT_WIDTH 8
59 #include <pcre2.h>
60 #endif
61 
62 /* #include "muttlib.h" */
63 const char *mutt_make_version(void);
64 /* #include "store/lib.h" */
65 const char *store_backend_list(void);
66 const char *store_compress_list(void);
67 
68 const int SCREEN_WIDTH = 80;
69 
70 extern unsigned char cc_cflags[];
71 extern unsigned char configure_options[];
72 
73 static const char *Copyright =
74     "Copyright (C) 1996-2020 Michael R. Elkins <me@mutt.org>\n"
75     "Copyright (C) 1996-2002 Brandon Long <blong@fiction.net>\n"
76     "Copyright (C) 1997-2009 Thomas Roessler <roessler@does-not-exist.org>\n"
77     "Copyright (C) 1998-2005 Werner Koch <wk@isil.d.shuttle.de>\n"
78     "Copyright (C) 1999-2017 Brendan Cully <brendan@kublai.com>\n"
79     "Copyright (C) 1999-2002 Tommi Komulainen <Tommi.Komulainen@iki.fi>\n"
80     "Copyright (C) 2000-2004 Edmund Grimley Evans <edmundo@rano.org>\n"
81     "Copyright (C) 2006-2009 Rocco Rutte <pdmef@gmx.net>\n"
82     "Copyright (C) 2014-2020 Kevin J. McCarthy <kevin@8t8.us>\n"
83     "Copyright (C) 2015-2020 Richard Russon <rich@flatcap.org>\n";
84 
85 static const char *Thanks =
86     N_("Many others not mentioned here contributed code, fixes,\n"
87        "and suggestions.\n");
88 
89 static const char *License = N_(
90     "    This program is free software; you can redistribute it and/or modify\n"
91     "    it under the terms of the GNU General Public License as published by\n"
92     "    the Free Software Foundation; either version 2 of the License, or\n"
93     "    (at your option) any later version.\n"
94     "\n"
95     "    This program is distributed in the hope that it will be useful,\n"
96     "    but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
97     "    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
98     "    GNU General Public License for more details.\n"
99     "\n"
100     "    You should have received a copy of the GNU General Public License\n"
101     "    along with this program; if not, write to the Free Software\n"
102     "    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  "
103     "02110-1301, USA.\n");
104 
105 static const char *ReachingUs =
106     N_("To learn more about NeoMutt, visit: https://neomutt.org\n"
107        "If you find a bug in NeoMutt, please raise an issue at:\n"
108        "    https://github.com/neomutt/neomutt/issues\n"
109        "or send an email to: <neomutt-devel@neomutt.org>\n");
110 
111 // clang-format off
112 static const char *Notice =
113     N_("Copyright (C) 1996-2020 Michael R. Elkins and others.\n"
114        "NeoMutt comes with ABSOLUTELY NO WARRANTY; for details type 'neomutt -vv'.\n"
115        "NeoMutt is free software, and you are welcome to redistribute it\n"
116        "under certain conditions; type 'neomutt -vv' for details.\n");
117 // clang-format on
118 
119 /**
120  * struct CompileOptions - List of built-in capabilities
121  */
122 struct CompileOptions
123 {
124   const char *name; ///< Option name
125   int enabled;      ///< 0 Disabled, 1 Enabled, 2 Devel only
126 };
127 
128 /* These are sorted by the display string */
129 
130 static struct CompileOptions comp_opts_default[] = {
131   { "attach_headers_color", 1 },
132   { "compose_to_sender", 1 },
133   { "compress", 1 },
134   { "cond_date", 1 },
135   { "debug", 1 },
136   { "encrypt_to_self", 1 },
137   { "forgotten_attachments", 1 },
138   { "forwref", 1 },
139   { "ifdef", 1 },
140   { "imap", 1 },
141   { "index_color", 1 },
142   { "initials", 1 },
143   { "limit_current_thread", 1 },
144   { "multiple_fcc", 1 },
145   { "nested_if", 1 },
146   { "new_mail", 1 },
147   { "nntp", 1 },
148   { "pop", 1 },
149   { "progress", 1 },
150   { "quasi_delete", 1 },
151   { "regcomp", 1 },
152   { "reply_with_xorig", 1 },
153   { "sensible_browser", 1 },
154   { "sidebar", 1 },
155   { "skip_quoted", 1 },
156   { "smtp", 1 },
157   { "status_color", 1 },
158   { "timeout", 1 },
159   { "tls_sni", 1 },
160   { "trash", 1 },
161   { NULL, 0 },
162 };
163 
164 static struct CompileOptions comp_opts[] = {
165 #ifdef USE_AUTOCRYPT
166   { "autocrypt", 1 },
167 #else
168   { "autocrypt", 0 },
169 #endif
170 #ifdef USE_FCNTL
171   { "fcntl", 1 },
172 #else
173   { "fcntl", 0 },
174 #endif
175 #ifdef USE_FLOCK
176   { "flock", 1 },
177 #else
178   { "flock", 0 },
179 #endif
180 #ifdef USE_FMEMOPEN
181   { "fmemopen", 1 },
182 #else
183   { "fmemopen", 0 },
184 #endif
185 #ifdef HAVE_FUTIMENS
186   { "futimens", 1 },
187 #else
188   { "futimens", 0 },
189 #endif
190 #ifdef HAVE_GETADDRINFO
191   { "getaddrinfo", 1 },
192 #else
193   { "getaddrinfo", 0 },
194 #endif
195 #ifdef USE_SSL_GNUTLS
196   { "gnutls", 1 },
197 #else
198   { "gnutls", 0 },
199 #endif
200 #ifdef CRYPT_BACKEND_GPGME
201   { "gpgme", 1 },
202 #else
203   { "gpgme", 0 },
204 #endif
205 #ifdef USE_GSS
206   { "gss", 1 },
207 #else
208   { "gss", 0 },
209 #endif
210 #ifdef USE_HCACHE
211   { "hcache", 1 },
212 #else
213   { "hcache", 0 },
214 #endif
215 #ifdef HOMESPOOL
216   { "homespool", 1 },
217 #else
218   { "homespool", 0 },
219 #endif
220 #ifdef HAVE_LIBIDN
221   { "idn", 1 },
222 #else
223   { "idn", 0 },
224 #endif
225 #ifdef USE_INOTIFY
226   { "inotify", 1 },
227 #else
228   { "inotify", 0 },
229 #endif
230 #ifdef LOCALES_HACK
231   { "locales_hack", 1 },
232 #else
233   { "locales_hack", 0 },
234 #endif
235 #ifdef USE_LUA
236   { "lua", 1 },
237 #else
238   { "lua", 0 },
239 #endif
240 #ifdef MIXMASTER
241   { "mixmaster", 1 },
242 #else
243   { "mixmaster", 0 },
244 #endif
245 #ifdef ENABLE_NLS
246   { "nls", 1 },
247 #else
248   { "nls", 0 },
249 #endif
250 #ifdef USE_NOTMUCH
251   { "notmuch", 1 },
252 #else
253   { "notmuch", 0 },
254 #endif
255 #ifdef USE_SSL_OPENSSL
256   { "openssl", 1 },
257 #else
258   { "openssl", 0 },
259 #endif
260 #ifdef HAVE_PCRE2
261   { "pcre2", 1 },
262 #endif
263 #ifdef CRYPT_BACKEND_CLASSIC_PGP
264   { "pgp", 1 },
265 #else
266   { "pgp", 0 },
267 #endif
268 #ifndef HAVE_PCRE2
269   { "regex", 1 },
270 #endif
271 #ifdef USE_SASL
272   { "sasl", 1 },
273 #else
274   { "sasl", 0 },
275 #endif
276 #ifdef CRYPT_BACKEND_CLASSIC_SMIME
277   { "smime", 1 },
278 #else
279   { "smime", 0 },
280 #endif
281 #ifdef USE_SQLITE
282   { "sqlite", 1 },
283 #else
284   { "sqlite", 0 },
285 #endif
286 #ifdef SUN_ATTACHMENT
287   { "sun_attachment", 1 },
288 #else
289   { "sun_attachment", 0 },
290 #endif
291   { NULL, 0 },
292 };
293 
294 static struct CompileOptions debug_opts[] = {
295 #ifdef USE_ASAN
296   { "asan", 2 },
297 #endif
298 #ifdef HAVE_LIBUNWIND
299   { "backtrace", 2 },
300 #endif
301 #ifdef USE_DEBUG_GRAPHVIZ
302   { "graphviz", 2 },
303 #endif
304 #ifdef USE_DEBUG_NOTIFY
305   { "notify", 2 },
306 #endif
307 #ifdef USE_DEBUG_PARSE_TEST
308   { "parse-test", 2 },
309 #endif
310 #ifdef USE_DEBUG_WINDOW
311   { "window", 2 },
312 #endif
313   { NULL, 0 },
314 };
315 
316 /**
317  * print_compile_options - Print a list of enabled/disabled features
318  * @param co Array of compile options
319  * @param fp file to write to
320  *
321  * Two lists are generated and passed to this function:
322  *
323  * One list which just uses the configure state of each feature.
324  * One list which just uses feature which are set to on directly in NeoMutt.
325  *
326  * The output is of the form: "+enabled_feature -disabled_feature" and is
327  * wrapped to SCREEN_WIDTH characters.
328  */
print_compile_options(struct CompileOptions * co,FILE * fp)329 static void print_compile_options(struct CompileOptions *co, FILE *fp)
330 {
331   if (!co || !fp)
332     return;
333 
334   size_t used = 2;
335   bool tty = isatty(fileno(fp));
336 
337   fprintf(fp, "  ");
338   for (int i = 0; co[i].name; i++)
339   {
340     const size_t len = strlen(co[i].name) + 2; /* +/- and a space */
341     if ((used + len) > SCREEN_WIDTH)
342     {
343       used = 2;
344       fprintf(fp, "\n  ");
345     }
346     used += len;
347     const char *fmt = "?%s ";
348     switch (co[i].enabled)
349     {
350       case 0: // Disabled
351         if (tty)
352           fmt = "\033[1;31m-%s\033[0m "; // Escape, red
353         else
354           fmt = "-%s ";
355         break;
356       case 1: // Enabled
357         if (tty)
358           fmt = "\033[1;32m+%s\033[0m "; // Escape, green
359         else
360           fmt = "+%s ";
361         break;
362       case 2: // Devel only
363         if (tty)
364           fmt = "\033[1;36m%s\033[0m "; // Escape, cyan
365         else
366           fmt = "%s ";
367         break;
368     }
369     fprintf(fp, fmt, co[i].name);
370   }
371   fprintf(fp, "\n");
372 }
373 
374 /**
375  * rstrip_in_place - Strip a trailing carriage return
376  * @param s  String to be modified
377  * @retval ptr The modified string
378  *
379  * The string has its last carriage return set to NUL.
380  */
rstrip_in_place(char * s)381 static char *rstrip_in_place(char *s)
382 {
383   if (!s)
384     return NULL;
385 
386   char *p = &s[strlen(s)];
387   if (p == s)
388     return s;
389   p--;
390   while ((p >= s) && ((*p == '\n') || (*p == '\r')))
391     *p-- = '\0';
392   return s;
393 }
394 
395 /**
396  * print_version - Print system and compile info to a file
397  * @param fp File to print to
398  * @retval true Text displayed
399  *
400  * Print information about the current system NeoMutt is running on.
401  * Also print a list of all the compile-time information.
402  */
print_version(FILE * fp)403 bool print_version(FILE *fp)
404 {
405   if (!fp)
406     return false;
407 
408   struct utsname uts;
409   bool tty = isatty(fileno(fp));
410   const char *fmt = "%s\n";
411 
412   if (tty)
413     fmt = "\033[1;36m%s\033[0m\n"; // Escape, cyan
414 
415   fprintf(fp, fmt, mutt_make_version());
416   fprintf(fp, "%s\n", _(Notice));
417 
418   uname(&uts);
419 
420 #ifdef SCO
421   fprintf(fp, "System: SCO %s", uts.release);
422 #else
423   fprintf(fp, "System: %s %s", uts.sysname, uts.release);
424 #endif
425 
426   fprintf(fp, " (%s)", uts.machine);
427 
428   fprintf(fp, "\nncurses: %s (compiled with %s.%d)", curses_version(),
429           NCURSES_VERSION, NCURSES_VERSION_PATCH);
430 
431 #ifdef _LIBICONV_VERSION
432   fprintf(fp, "\nlibiconv: %d.%d", _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 0xff);
433 #endif
434 
435 #ifdef HAVE_LIBIDN
436   fprintf(fp, "\n%s", mutt_idna_print_version());
437 #endif
438 
439 #ifdef CRYPT_BACKEND_GPGME
440   fprintf(fp, "\nGPGME: %s", mutt_gpgme_print_version());
441 #endif
442 
443 #ifdef USE_SSL_OPENSSL
444 #ifdef LIBRESSL_VERSION_TEXT
445   fprintf(fp, "\nLibreSSL: %s", LIBRESSL_VERSION_TEXT);
446 #endif
447 #ifdef OPENSSL_VERSION_TEXT
448   fprintf(fp, "\nOpenSSL: %s", OPENSSL_VERSION_TEXT);
449 #endif
450 #endif
451 
452 #ifdef USE_SSL_GNUTLS
453   fprintf(fp, "\nGnuTLS: %s", GNUTLS_VERSION);
454 #endif
455 
456 #ifdef HAVE_NOTMUCH
457   fprintf(fp, "\nlibnotmuch: %d.%d.%d", LIBNOTMUCH_MAJOR_VERSION,
458           LIBNOTMUCH_MINOR_VERSION, LIBNOTMUCH_MICRO_VERSION);
459 #endif
460 
461 #ifdef HAVE_PCRE2
462   {
463     char version[24];
464     pcre2_config(PCRE2_CONFIG_VERSION, version);
465     fprintf(fp, "\nPCRE2: %s", version);
466   }
467 #endif
468 
469 #ifdef USE_HCACHE
470   const char *backends = store_backend_list();
471   fprintf(fp, "\nstorage: %s", backends);
472   FREE(&backends);
473 #ifdef USE_HCACHE_COMPRESSION
474   backends = compress_list();
475   fprintf(fp, "\ncompression: %s", backends);
476   FREE(&backends);
477 #endif
478 #endif
479 
480   rstrip_in_place((char *) configure_options);
481   fprintf(fp, "\n\nConfigure options: %s\n", (char *) configure_options);
482 
483   rstrip_in_place((char *) cc_cflags);
484   fprintf(fp, "\nCompilation CFLAGS: %s\n", (char *) cc_cflags);
485 
486   fprintf(fp, "\n%s\n", _("Default options:"));
487   print_compile_options(comp_opts_default, fp);
488 
489   fprintf(fp, "\n%s\n", _("Compile options:"));
490   print_compile_options(comp_opts, fp);
491 
492   if (debug_opts[0].name)
493   {
494     fprintf(fp, "\n%s\n", _("Debug options:"));
495     print_compile_options(debug_opts, fp);
496   }
497 
498   fprintf(fp, "\n");
499 #ifdef DOMAIN
500   fprintf(fp, "DOMAIN=\"%s\"\n", DOMAIN);
501 #endif
502 #ifdef ISPELL
503   fprintf(fp, "ISPELL=\"%s\"\n", ISPELL);
504 #endif
505   fprintf(fp, "MAILPATH=\"%s\"\n", MAILPATH);
506 #ifdef MIXMASTER
507   fprintf(fp, "MIXMASTER=\"%s\"\n", MIXMASTER);
508 #endif
509   fprintf(fp, "PKGDATADIR=\"%s\"\n", PKGDATADIR);
510   fprintf(fp, "SENDMAIL=\"%s\"\n", SENDMAIL);
511   fprintf(fp, "SYSCONFDIR=\"%s\"\n", SYSCONFDIR);
512 
513   fprintf(fp, "\n");
514   fputs(_(ReachingUs), fp);
515 
516   fflush(fp);
517   return !ferror(fp);
518 }
519 
520 /**
521  * print_copyright - Print copyright message
522  * @retval true Text displayed
523  *
524  * Print the authors' copyright messages, the GPL license and some contact
525  * information for the NeoMutt project.
526  */
print_copyright(void)527 bool print_copyright(void)
528 {
529   puts(mutt_make_version());
530   puts(Copyright);
531   puts(_(Thanks));
532   puts(_(License));
533   puts(_(ReachingUs));
534 
535   fflush(stdout);
536   return !ferror(stdout);
537 }
538 
539 /**
540  * feature_enabled - Test if a compile-time feature is enabled
541  * @param name  Compile-time symbol of the feature
542  * @retval true  Feature enabled
543  * @retval false Feature not enabled, or not compiled in
544  *
545  * Many of the larger features of neomutt can be disabled at compile time.
546  * They define a symbol and use ifdef's around their code.
547  * The symbols are mirrored in "CompileOptions comp_opts[]" in this
548  * file.
549  *
550  * This function checks if one of these symbols is present in the code.
551  *
552  * These symbols are also seen in the output of "neomutt -v".
553  */
feature_enabled(const char * name)554 bool feature_enabled(const char *name)
555 {
556   if (!name)
557     return false;
558   for (int i = 0; comp_opts_default[i].name; i++)
559   {
560     if (mutt_str_equal(name, comp_opts_default[i].name))
561     {
562       return true;
563     }
564   }
565   for (int i = 0; comp_opts[i].name; i++)
566   {
567     if (mutt_str_equal(name, comp_opts[i].name))
568     {
569       return comp_opts[i].enabled;
570     }
571   }
572   return false;
573 }
574