=head1 Hacking INN

This file is for people who are interested in making modifications to INN.
Normal users can safely skip reading it.  It is intended primarily as a
guide, resource, and accumulation of tips for maintainers and
contributors, and secondarily as documentation of some of INN's internals.

First of all, if you plan on working on INN source, please start from the
current development tree.  There may be significant changes from the
previous full release, so starting from development sources will make it
considerably easier to integrate your work.  You can check out or clone
the current development tree using Git from:

    <https://github.com/InterNetNews/inn.git>

or browse the current development source on Github at:

    <https://github.com/InterNetNews/inn>

You will need S<autoconf 2.64> or later to use the development tree.  After
checking out the tree, run C<./autogen> to generate the necessary autoconf
files.

Pull requests must complete an automatic build/test cycle with warnings
enabled.  You can run an equivalent build with C<./ci/test>.

Daily snapshots can be found at:

    <https://ftp.isc.org/isc/inn/snapshots/>

=head1 Configuring and Portability

All INN code should be written expecting S<ANSI C> and POSIX.  There is no
need to attempt to support pre-ANSI compilers, and ANSI-only features such
as F<< <stdarg.h> >>, string concatenation, C<#elif>, and token pasting
may be used freely.  So far as possible, INN is written to attempt to be
portable to any system new enough that someone is likely to want to run a
news server on it, but whenever possible this portability should be
provided by checking for standard behavior in F<configure> and supplying
replacements for standard functions that are missing.

When there is a conflict between S<ANSI C> and C99, INN code should be
written expecting C99 and B<autoconf> used to patch up the differences.

Try to avoid using C<#ifdef> and the like in the middle of code as much as
possible.  Instead, try to isolate the necessary portability bits and
include them in B<libinn> or at least in conditional macros separate from
the code.  Trying to read code littered with conditional compilation
directives is much more difficult.

The shell script F<configure> at the top level of the source tree is
generated by B<autoconf> from F<configure.ac> and the additional macros in
the F<m4> directory, and F<include/config.h.in> is generated by
B<autoheader> from F<configure.ac>.  At configure time, F<configure>
generates F<include/config.h> and several other files based on options it
was given and what it discovers about the target system.

All modifications to F<configure> should instead be made to
F<configure.ac>.  The B<autoconf> manual (available using C<info autoconf>
if you have B<autoconf> and the GNU B<info> utilities installed on your
system) is a valuable reference when making any modifications.

To regenerate F<configure>, just run C<autoconf>.  To regenerate
F<include/config.h.in>, run C<autoheader>.  Alternately, to regenerate
both, you can run C<./autogen>.  Please don't include patches to either
F<configure> or F<include/config.h.in> when sending patches to INN;
instead, note in your patch that those files must be regenerated.  These
(and all other) generated files should not be checked into Git.

At the time of this writing, S<autoconf 2.64> or later is required.

The supporting files for B<autoconf> are in the F<support>
subdirectory, including the files F<config.guess> and F<config.sub>
to determine the system name and F<ltmain.sh> for Libtool support.
The latter file comes from the Debian package of Libtool, available from
L<https://packages.debian.org/libtool> (F<ltmain.sh> is generated in the
F<build-aux> subdirectory after building the package with for instance
B<debuild -us -uc>); the canonical versions of the former two files
are available from L<https://ftp.gnu.org/gnu/config/> (which currently
redirects to L<https://git.savannah.gnu.org/cgit/config.git/tree/>).

In addition, F<m4/libtool.m4> and a few other m4 files used by INN are
just a copy of the corresponding files in the F<m4> subdirectory of
the Debian package of the Libtool distribution.  (Using Libtool without
using B<automake> requires a few odd hacks.)  New versions
should be checked in periodically when available.  There are no
INN-specific modifications to those files except for F<ltmain.sh>
which recognizes the additional B<-S> flag that INN's B<install-sh>
script uses, and a fix for a proper handling of circular dependencies.

This B<install-sh> script should also be updated at the same time from
L<https://git.savannah.gnu.org/cgit/automake.git/tree/>;
it similarly contains a local modification mentioned in a comment
at the beginning of the file.

Finally, the F<m4/pkg.m4> macro should be updated from
L<https://gitlab.freedesktop.org/pkg-config/pkg-config/>.

=head1 Autobuilds

Automatic build logs on several platforms are available at
L<http://usenet.trigofacile.com/autobuild/inn/>.

=head1 Documentation

INN's documentation is currently somewhat in a state of flux.  Much of the
documentation is still in the form of man pages written directly in nroff.
Some parts of the documentation have been rewritten in POD; that
documentation can be found in F<doc/pod>.  The canonical source for most
of the text documentation, including F<README>, F<INSTALL>, F<NEWS>, and
this file, is also POD.

If you're modifying some part of INN's documentation and see that it has a
POD version in F<doc/pod>, you should make the modifications to the POD
source and then regenerate the derived files.  For a quick introduction to
POD, see the perlpod(1) man page on your system (it should be installed if
you have Perl installed).

When writing new documentation, write in whatever format you care to; if
necessary, we can always convert it to POD or whatever else we want to
use.  Having the documentation exist in I<some> form is more important
than what language you write it in.  If you don't have a preference,
however, we prefer new documentation to be in POD.

If you use POD or regenerate POD documentation, please install something
close to the latest versions of the POD processing utilities to avoid
changes to the documentation depending on who generated it last.  You can
find the latest version of the C<podlators> distribution on CPAN
(L<https://metacpan.org/dist/podlators>).  For versions of Perl before 5.6.1
(as Perl 5.6.1 and later come with a recent enough version), you'll also need
C<Pod::Parser> (see L<https://metacpan.org/pod/Pod-Parser>).

There are makefile rules in F<doc/pod/Makefile> to build all of the
documentation whose master form is POD; if you add additional
documentation, please add a rule there as well.  Documentation should be
generated by cd'ing to F<doc/pod> and typing C<make file> where C<file> is
the relative path to the documentation file.  This will get all of the
various flags right for B<pod2text> or B<pod2man>.

=head1 Error Handling

INN has a set of generic error handling routines that should be used as
much as possible so that the same syntax can be used for reporting errors
everywhere in INN.  The six basic functions are notice, sysnotice, warn,
syswarn, die, and sysdie; notice prints or logs an informative message,
warn prints or logs a warning, and die does the same and then exits the
current program.  The sys* versions add a colon, a space, and the value of
strerror(errno) to the end of the message, and should be used to report
failing system calls.

All of the actual error reporting is done via error handlers, and a
program can register its own handlers in addition to or instead of the
default one.  The default error handler (message_log_stderr) prints to
stderr, prepending the value of message_program_name if it's set to
something other than NULL.  Several other error handlers are available,
particularly including ones that use syslog.  See
F<include/inn/messages.h> for all of the available options.

There is a different set of error handlers for notice/sysnotice,
warn/syswarn, and die/sysdie.  To set them, make calls like:

    message_handlers_warn(1, message_log_stderr);
    message_handlers_die(2, message_log_stderr, message_log_syslog_err);

The first argument is the number of handlers, and the remaining arguments
are pointers to functions taking an int (the length of the formatted
message), a const char * (the format), a va_list (the arguments), and an
int that's 0 if warn or die was called and equal to the value of errno if
syswarn or sysdie was called.  The length of the formatted message is
obtained by calling vsnprintf with the provided format and arguments, and
therefore is reliable to use as the size of a buffer to malloc to hold the
result of formatting the message provided that vsnprintf is used to format
it (warning: the system vsprintf may produce more output under some
circumstances, so always use vsnprintf).

The error handler can do anything it wishes; each error handler is called
in the sequence given.  Error handlers shouldn't call warn or die unless
great caution is taken to prevent infinite recursion.  Also be aware that
sysdie is called if malloc fails in xmalloc, so if the error handler needs
to allocate memory, it must not use xmalloc or a related function to do so
and it must not call die to report failure.  The default syslog handlers
report memory allocation failure to stderr and exit.

Finally, die and sysdie support an additional handler (the external
variable message_fatal_cleanup) that's called immediate before exiting,
takes no arguments, and returns an int which is used as the argument for
exit.  It can do any necessary global cleanup, call abort instead to
generate a core dump or the like.

The advantage of using this system everywhere in INN is that library code
can use notice, warn, and die to report messages and errors, and each
calling program can set up the handlers as appropriate to make sure the
errors go to the right place.  The default handler is fine for interactive
programs; for programs that run from interactive scripts, adding something
like:

    message_program_name = "program";

to the beginning of main (where program is the name of the program) will
make it easier to figure out which program the script calls is failing.
For programs that may also be called non-interactively, like rnews, one
may want to set up handlers like:

    message_handlers_notice(2, message_log_stdout, message_log_syslog_info);
    message_handlers_warn(2, message_log_stderr, message_log_syslog_warning);
    message_handlers_die(2, message_log_stderr, message_log_syslog_err);

Finally, for daemons and other non-interactive programs, one may want to
do:

    message_handlers_notice(1, message_log_syslog_info);
    message_handlers_warn(1, message_log_syslog_warning);
    message_handlers_die(1, message_log_syslog_err);

to report errors only via syslog.  (Note that if you use syslog error
handlers, the program should call openlog first thing to make sure they
are logged with the right facility.)

For historical reasons, error messages that are fatal to the news
subsystem are logged at the LOG_CRIT priority, and therefore die in innd
should use message_log_syslog_crit.  This seems like priority inflation
and may change in the future to message_log_syslog_err.

=head1 Test Suite

The test suite for INN is located in the F<tests> directory and is just
getting started.  The test suite consists of a set of programs listed in
F<tests/TESTS> and the scaffolding in the B<runtests> program.

Adding new tests is very straightforward and very flexible.  Just write a
program that tests some part of INN, put it in a directory under F<tests>
named after the part of INN it's testing, and have it output first a line
containing the count of test cases in that file, and then for each test a
line saying C<ok n> or C<not ok n> where C<n> is the test case number.
(If a test is skipped for some reason, such as a test of an optional
feature that wasn't compiled into INN, the test program should output C<ok
n # skip>.)  Add any rules necessary to build the test to
F<tests/Makefile> (note that for simplicity it doesn't recurse into
subdirectories) and make sure it creates an executable ending in F<.t>.
Then add the name of the test to F<tests/TESTS>, without the F<.t> ending.

For C tests, you probably want to use the functions in
F<tests/tap/basic.c> (prototypes in F<tests/tap/basic.h>) to handle
all of the output.  For shell script tests, F<tests/tap/libtap.sh>
contains helpful shell functions.  See the existing tests for some
hints about how to write new tests, as well as the F<tests/README> guide.

One naming convention: to distinguish more easily between for example
F<lib/error.c> (the implementation) and F<tests/lib/error-t.c> (the test
suite), we add F<-t> to the end of the test file names.  So
F<tests/lib/error-t.c> is the source that compiles into an executable
F<tests/lib/error.t> which is run by putting a line in F<tests/TESTS> of
just C<lib/error>.

Note that tests don't have to be written in C; in fact, F<lib/xmalloc.t>
is just a shell script (that calls a supporting C program).  Tests can be
written in shell or Perl (but other languages should be avoided because
someone who wants to run the test suite may not have it) and just have to
follow the above output conventions.

Additions to the test suite, no matter how simple, are very welcome.

Running the test suite is done with:

    make check

A single test can be run with verbose ouput via:

    tests/runtests -o <name-of-test>

Using B<runtests> ensures that necessary environment variables are set up.

=head1 Makefiles

All INN Makefiles include F<Makefile.global> at the top level, and only
that makefile is a F<configure> substitution target.  This has the
disadvantage that F<configure>'s normal support for building in a tree
outside of the source tree doesn't work, but it has the significant
advantage of making F<configure> run much faster and allowing one to run
C<make> in any subdirectory and pick up all the definitions and settings
from the top level configuration.

All INN Makefiles should also set C<$(top)> to be the path to the top of
the build directory (usually relative).  This path is used to find various
programs like B<fixscript> and Libtool so that the same macros (set in
F<Makefile.global>) can be used all over INN.

The format of INN's Makefiles is mostly standardized; the best examples of
the format are probably F<frontends/Makefile> and F<backends/Makefile>, at
least for directories with lots of separate programs.  The C<ALL> variable
holds all the files that should be generated, C<EXTRA> those additional
files that were generated by F<configure>, and C<SOURCES> the C source
files for generating tag information.

There are a set of standard installation commands defined in make
variables by F<Makefile.global>, and these should be used for all file
installations.  See the comment blocks in F<Makefile.global.in> for
information on what commands are available and when they should be used.
There are also variables set for each of the installation directories that
INN uses, for use in building the list of installed paths to files.

Each subdirectory makefile should have the targets C<all> (the default),
C<clean>, C<clobber>/C<distclean>, C<install>, and C<profiled>.  The
C<profiled> target generates a profiling version of the programs (although
this hasn't been tested much).  These rules should be present and empty in
those directories where they don't apply.

Be sure to test compiling with both static and dynamic libraries and make
sure that all the Libtool support works correctly.  All linking steps, and
the compile steps for all library source, should be done through C<$(LIBCC)>
and C<$(LIBLD)> (which will be set as appropriate in F<Makefile.global>).

=head1 Scripts

INN comes with and installs a large number of different scripts, both
Bourne shell and Perl, and also comes with support for Tcl scripts
(although it doesn't come with any).  Shell variables containing both
F<configure>-time information and configuration information from
F<inn.conf> are set by the B<innshellvars> support libraries, so the only
system-specific configuration that should have to be done is fixing the
right path to the interpreter and adding a line to load the appropriate
B<innshellvars>.

F<support/fixscript>, built by F<configure>, does this.  It takes a F<.in>
file and generates the final script (removing the F<.in>) by fixing the
path to the interpreter on the first line and replacing the second line,
whatever it is, with code to load the B<innshellvars> appropriate for that
interpreter.  (If invoked with B<-i>, it just fixes the interpreter path.)

Scripts should use B<innshellvars> (via B<fixscript>) to get the right
path and the right variables whenever possible, rather than having
F<configure> substitute values in them.  Any values needed at run-time
should instead be available from all of the different B<innshellvars>.

As for Perl, the C<INN::Config> module has the same features as
F<innshellvars.pl> (only kept for compatibility reasons with old scripts
not shipped with INN); however, it can be safely used with warnings on
in Perl scripts.

See the existing scripts for examples of how this is done.

=head1 Include Files

Include files relevant to all of INN, or relevant to the two libraries
built as part of INN (the utility B<libinn> library and the B<libinnstorage>
library that contains all storage and overview functions) are found in the
F<include> directory; other include files relevant only to a portion of INN
are found in the relevant directory.

Practically all INN source files will start with:

    #include "portable/system.h"

This header file includes F<config.h>, which picks up all defines
generated by B<autoconf> and is necessary for types that may not be
present on all systems (I<uid_t>, I<pid_t>, I<size_t>, I<uint32_t>, and
the like).  It therefore should be included before any other headers that
use those types, as well as to get general configuration information.
The F<config.h> header also includes F<inn/macros.h>, F<inn/options.h>,
F<inn/system.h> and F<portable/stdbool.h> which pick up additional
support macros and compile-time configuration.

Besides, F<portable/system.h> also includes all of the following headers,
portably:

   #include <inttypes.h>
   #include <limits.h>
   #include <stdarg.h>
   #include <stdbool.h>
   #include <stddef.h>
   #include <stdint.h>
   #include <stdio.h>
   #include <stdlib.h>
   #include <string.h>
   #include <strings.h>
   #include <sys/types.h>
   #include <unistd.h>

except that it doesn't include headers that are missing on a given system,
replaces functions not found on the system with the INN equivalents,
provides macros that INN assumes are available but which weren't found,
and defines some additional portability things.  Even if this is more
headers than the source file actually needs, it's generally better to just
include F<portable/system.h> rather than trying to duplicate the
B<autoconf>-driven hackery that it does to do things portably.  The
primary exception is for source files in F<lib> that only define a single
function and are used for portability; those may want to include only
F<config.h> so that they can be easily used in other projects that use
B<autoconf>.  F<config.h> is a fairly standard header name for this
purpose.

There are portable wrappers around several header files that have known
portability traps or that need some fixing up on some platforms.  Look in
F<include/portable> and familiarize yourself with them and use them where
appropriate.

Another frequently included header file is F<inn/libinn.h>, which among
other things defines xmalloc(), xrealloc(), xstrdup(), and xcalloc(),
which are checked versions of the standard memory allocation routines that
terminate the program if the memory allocation fails.  These should
generally always be used instead of the regular C versions.
F<inn/libinn.h> also provides various other utility functions that are
frequently used.

F<inn/paths.h> includes a wide variety of paths determined at configure
time, both default paths to various parts of INN and paths to programs.
Don't just use the default paths, though, if they're also configurable in
F<inn.conf>; instead, call innconf_read() and use the global I<innconf>
structure.

Other files in F<include> are interfaces to particular bits of INN library
functionality or are used for other purposes; see the comments in each
file.

Eventually, the header files will be separated into installed header files
and uninstalled header files; the latter are those headers that are used
only for compiling INN and aren't useful for users of INN's libraries
(such as F<innperl.h>).  All of the installed headers will live in
F<include/inn> and be installed in a subdirectory named F<inn> in the
configured F<include> directory.  This conversion is still in progress.

When writing header files, remember that C reserves all identifiers
beginning with two underscores and all identifiers beginning with an
underscore and a capital letter for the use of the implementation; don't
use any identifiers with names like that.  Additionally, any identifier
beginning with an underscore and a lower-case letter is reserved in file
scope, which means that such identifiers can only be used by INN for the
name of structure members or function arguments in function prototypes.

Try to pay attention to the impact of a header file on the program
namespace, particularly for installed header files in F<include/inn>.  All
symbols defined by a header file should ideally begin with C<INN_>,
C<inn_>, or some other unique prefix indicating the subsystem that symbol
is part of, to avoid accidental conflicts with symbols defined by the
program that uses that header file.

=head1 Libraries

INN includes three shared libraries, in F<lib>, F<storage>, and
F<history>.  Whenever any of the source to those libraries is modified,
the library version information must be updated at the top of the
F<Makefile> in that directory.  Follow the instructions in the Libtool
manual under Versioning / Updating version info, with the exception that,
contrary to point 2, it's useful to those running snapshots to update the
version information more frequently than for each public release.
Remember that the version information has to be updated to allow recovery
from a C<make update> command.

=head1 Coding Style

INN has quite a variety of coding styles intermixed.  As with all
programs, it's preferable when making minor modifications to keep the
coding style of the code you're modifying.  In INN, that will vary by
file.  (Over time we're trying to standardize on one coding style, so
changing the region you worked on to fit the general coding style is also
acceptable).

When modifying or writing new code in C, it should conform to the
I<Clang-format> style of the project.  Similarly, Perl, Python and shell
scripts should conform to respectively the I<perltidy>, I<Black> and I<shfmt>
code styles of the project.  Just run C<make reformat> at the top level of the
source tree to properly reformat all files.

If you're writing a substantial new piece of code, the prevailing
"standard" INN coding style is the following:

=over 3

=item *

Write in regular S<ANSI C> whenever possible.  Use the normal ANSI and
POSIX constructs and use B<autoconf> or portability wrappers to fix things
up beforehand so that the code itself can read like regular ANSI or POSIX
code.  Code should be written so that it works as expected on a modern
platform and is fixed up with portability tricks for older platforms, not
the other way around.  You may assume an S<ANSI C> compiler.

Try to use const wherever appropriate.  Don't use register; modern
compilers will do as good of a job as you will in choosing what to put
into a register.  Don't bother with restrict (at least yet).

=item *

Use string handling functions that take counts for the size of the buffer
whenever possible.  This means using snprintf in preference to sprintf and
using strlcpy and strlcat in preference to strcpy and strcat.  Also, use
strlcpy and strlcat instead of strncpy and strncat, as it is much easier
to audit uses of the former than the latter.  strlcpy is like strncpy
except that it always nul-terminates and doesn't fill the rest of the
buffer with nuls, making it more efficient.  strlcat is like strncat
except that it always nul-terminates and it takes the total size of the
buffer as its third argument rather than just the amount of space left.
All of these functions are guaranteed to be available; there are
replacements in F<lib> for systems that don't have them.

If you have to use a string copying routine that doesn't nul-terminate,
use memcpy instead.  Avoid introducing any uses of strcpy, strcat,
strncpy, or strncat so that we can use grep to find dangerous usages and
switch them to better functions.

=item *

Avoid C<#ifdef> and friends whenever possible.  Particularly avoid using
them in the middle of code blocks.  Try to hide all portability
preprocessor magic in header files or in portability code in F<lib>.  When
something just has to be done two completely different ways depending on
the platform or compile options or the like, try to abstract that
functionality out into a generic function and provide two separate
implementations using C<#ifdef>; then the main code can just call that
function.

If you do have to use preprocessor defines, note that if you always define
them to either C<0> or C<1> (never use C<#define> without a second
argument), you can use the preprocessor define in a regular if statement
rather than using C<#if> or C<#ifdef>.  Make use of this instead of
C<#ifdef> when possible, since that way the compiler will still
syntax-check the other branch for you and it makes it far easier to
convert the code to use a run-time check if necessary.  (Unfortunately,
this trick can't be used if one branch may call functions unavailable on a
particular platform.)

=item *

Avoid uses of fixed-width buffers except in performance-critical code, as
it's harder to be sure that such code is correct and it tends to be less
flexible later on.  If you need a reusable, resizable memory buffer, one
is provided in F<lib/buffer.c>.

=item *

Avoid uses of static variables whenever possible, particularly in
libraries, because it interferes with making the code re-entrant down the
road and makes it harder to follow what's going on.  Similarly, avoid
using global variables whenever possible, and if they are required, try to
wrap them into structures that could later be changed into arguments to
the affected functions.

=item *

Use a roughly BSD indentation style but with four-space indents.  This
means no space before the parenthesis around function arguments, open brace on
the same line as if/while/for, and close and open brace on the same line
as else.

=item *

Introductory comments for functions or files are generally written as:

    /*
    **  Introductory comment.
    */

Other multiline comments in the source are generally written as:

    /* This is a
       multiline comment. */

Comments before functions saying what they do are nice to have.

=item *

Checks for NULL pointers are preferably written out explicitly; in other
words, use:

    if (p != NULL)

rather than:

    if (p)

to make it clearer what the code is assuming.

=item *

It's better to always put the body of an C<if> statement on a separate
line, even if it's only a single line.  In other words, write:

    if (p != NULL)
        return p;

and not:

    if (p != NULL) return p;

This is in part for a practical reason: some code coverage analysis tools
like B<purecov> will count the second example above as a single line and
won't notice if the condition always evaluates the same way.

=item *

Plain structs make perfectly reasonable abstract data types; it's not
necessary to typedef the struct to something else.  Structs are actually
very useful for opaque data structures, since you can predeclare them and
then manipulate pointers to them without ever having to know what the
contents look like.  Please try to avoid typedefs except for function
pointers or other extremely confusing data types, or for data types where
we really gain some significant data abstraction from hiding the
underlying data type.  Also avoid using the C<_t> suffix for any type; all
types ending in C<_t> are reserved by POSIX.  For typedefs of function
pointer types, a suffix of C<_func> usually works.

This style point is currently widely violated inside of INN itself; INN
originally made extensive use of typedefs.

=item *

When noting something that should be improved later, add a comment
containing C<FIXME:> so that one can easily grep for such comments.

=back

For users of emacs cc-mode, use auto-formatting with clang-format and Black.

Finally, if possible, please don't use tabs in source files, since they
can expand differently in different environments.  In particular, please
try not to use the mix of tabs and spaces that is the default in emacs.
If you use emacs to edit INN code, you may want to put:

    ; Use only spaces when indenting or centering, no tabs.
    (setq-default indent-tabs-mode nil)

in your F<~/.emacs> file.

Note that this is only a rough guideline and the maintainers aren't style
nazis; we're more interested in your code contribution than in how you
write it.

=head1 Making a Release

This is a checklist that INN maintainers should go through when preparing
a new release of INN.

=over 4

=item 1.

Update the files shipped with INN, and that are maintained by external
projects.

=over 2

=item *

Make sure that F<support/config.guess>, F<support/config.sub>,
F<support/install-sh>, F<support/ltmain.sh>, Libtool m4 files
(F<libtool.m4>, F<ltoptions.m4>, F<ltsugar.m4>, F<ltversion.m4> and
F<lt~obsolete.m4>) and F<m4/pkg.m4> are the latest versions.  See the
instructions in L<"Configuring and Portability"> for details on how to
update these files.  When updating executable files, verify that the
committed version is still executable.

=item *

Make sure that the latest upstream version of the C TAP Harness
package is used for the test suite driver.  It is available at
L<https://www.eyrie.org/~eagle/software/c-tap-harness/>, and can be
easily synchronized with the script F<support/getc-tap-harness>; just
run it, have a look at the resulting changes in INN source code and,
if everything seems all right, commit these changes.

Parts specific to INN should be kept during an update (especially
sections relative to LIBTEST_NEW_FORMAT because the test suite has not
yet been fully updated to use the new format of C TAP Harness).

=item *

Make sure that the latest upstream version of the files
maintained in the rra-c-util package that INN uses are the
ones shipped with the release.  These files are available at
L<https://www.eyrie.org/~eagle/software/rra-c-util/> and can be easily
synchronized with the script F<support/getrra-c-util>; just run it, have
a look at the resulting changes in INN source code and, if everything
seems all right, commit these changes.

=item *

Make sure that F<control/pgpverify.in> is in sync with the master version at
L<https://ftp.isc.org/pub/pgpcontrol/>.

=item *

Make sure that F<samples/control.ctl> and F<samples/nocem.ctl> are in sync
with the master version at
L<https://ftp.isc.org/pub/usenet/CONFIG/control.ctl> and
L<http://rosalind.home.xs4all.nl/nocemreg/nocemreg.html>.

=back

=item 2.

Monitor changes carried on a few other projects.

=over 2

=item *

Check whether patches from OpenSSL (F<apps/lib/s_cb.c>, F<apps/s_server.c>
and F<crypto/bio/bio_dump.c> at L<https://github.com/openssl/openssl>) could
be merged into F<nnrpd/tls.c>, and new TLS versions could be supported in the
F<tlsprotocols> parameter in F<inn.conf>.

=item *

Check whether recent LibreSSL releases have backported new functions from
OpenSSL, that could then be enabled in B<nnrpd> for LibreSSL, if that's not
already the case.

=back

=item 3.

Build INN (including the F<contrib> and F<tests> directories) with
warnings on (C<make warnings>) and run the test suite (C<make tests>).
Fix any errors.

Several builds with different configure-time options should be tested,
so as to be sure they work fine (especially B<--enable-shared=no>,
B<--enable-tagged-hash>, B<--enable-keywords>, B<--enable-largefiles>
and B<--enable-reduced-depends>).

Ideally, ensure INN builds fine with the latest versions of external libraries
it has support for (S<Berkeley DB>, blacklistd, Cyrus SASL, libcanlock,
LibreSSL, MIT S<Kerberos v5>, OpenSSL, Perl, Python, SQLite, zlib).

=item 4.

Configure INN with all the external libraries it supports, and run
C<make depend> to ensure dependencies are properly defined in all INN
Makefiles.  Commit the changes, if appropriate.

=item 5.

Ensure that the source code is properly formatted with the latest versions of
reformatting programs (run C<make reformat>).

=item 6.

If possible, on a news server running the forthcoming release, run the
following commands to make sure they do not produce any errors:

    cnfsstat -a -v
    ctlinnd checkfile
    inncheck -a --perm --pedantic
    ovdb_stat -klmMtv
    ovsqlite-util -A
    scanlogs norotate
    scanspool -n -v
    tdx-util -A

=item 7.

Maintain up-to-date information in various files.

=over 2

=item *

Update F<INSTALL> with the list of supported systems.

=item *

Update copyright years in F<LICENSE>.

=item *

Proof-read and update F<doc/pod/news.pod>.  Add the release date when
generating the final release.

Be more detailed for a minor release than for a major release.  For a major
release, also add information on how to upgrade from the last major release,
including anything special to be aware of.  (Minor releases shouldn't require
any special care when upgrading.)

=item *

Bump the revision number in F<doc/FAQ> (subject 1.2) so that it could be
included in a final release.  It should not be changed for a beta or a release
candidate (the FAQ is indeed periodically posted to news.software.nntp from
the GitHub directory, and should not contain unreleased versions).

If making a major release, the revision numbers of the STABLE series
should also be bumped in F<doc/FAQ> at the end of subject 1.2.

=item *

Double-check that the library version information in
F<history/Makefile>, F<lib/Makefile>, and F<storage/Makefile> has
been updated since the previous release.  Follow the rules given at
L<https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html>.
The commit message usually contains a brief description of API changes, if
any, just for the record.

=back

=item 8.

If making a major release, create a new branch in Git named after the
major release.  This branch will be used for minor releases based on
that major release and can be done a little while before the C<.0>
release of that major release.

    git checkout -b Y.Y
    git push --set-upstream origin Y.Y

Then, in that newly created branch, adapt the first paragraph of
F<readme.pod>, remove its second paragraph which deals with development
versions, and update the URLs to point to the documentation of the new stable
version.

=item 9.

Check out a copy of the release branch, and perform the following actions.

=over 2

=item *

Run C<autogen> and C<configure> that are currently necessary to generate
F<Makefile.global>.

=item *

Then, run C<make warnings> to generate all necessary files.

=item *

Afterwards, run C<make check-manifest>.  There shouldn't be any
differences; otherwise, fix the F<MANIFEST> file.

=back

=item 10.

Run C<make release> for a final release, C<support/mksnapshot BETA b1> for
the first beta version of a new release, or C<support/mksnapshot RC rc1>
for the first release candidate version of a new release.

=item 11.

Generate an MD5 checksum of the release tarball.

    md5sum inn-Y.Y.Y.tar.gz > inn-Y.Y.Y.tar.gz.md5

=item 12.

Generate a diff between this release and the previous release if feasible
(always for minor releases, possibly not a good idea due to the length of
the diff for major releases).  You will need the tar file of the previous
release for the comparison.

    diff -Nurp inn-X.X.X inn-Y.Y.Y > inn-X.X.X-Y.Y.Y.diff
    gzip inn-X.X.X-Y.Y.Y.diff

=item 13.

For a testing release, make the resulting tar file, along with its MD5
checksum and the possible diff from the previous release, available
for testing into the F<testing> directory on ftp.isc.org and announce its
availability on inn-workers.  Install it on at least one system and make sure
that system runs fine for at least a few days.  This is also a good time to
send out a draft of the release announcement to inn-workers for proof-reading.

That's all for a testing release.  The following steps only concern final
releases.

=item 14.

When making the final release, empty the C<VERSION_EXTRA> variable in
F<Makefile.global.in> so that the C<prerelease> value does not appear in the
upcoming Git tag.

=item 15.

Create an annotated signed tag corresponding to the release tarball, using a
PGP key declared in the committer's GitHub profile:

    git tag Y.Y.Y <commit-hash> -s -m "INN Y.Y.Y release tag"
    git push origin Y.Y.Y

Check that it appears as C<Verified> in the GitHub interface, and then publish
a release in GitHub, based on that tag.  Include the description from F<NEWS>,
and attach the tarball.

=item 16.

Re-add the C<prerelease> value to the C<VERSION_EXTRA> variable in
F<Makefile.global.in>.

=item 17.

Bump revision numbers to reflect the one of the following release,
especially in F<doc/pod/install.pod> and F<readme.pod> for major releases,
F<configure.ac> and F<Makefile.global.in> for both minor and major releases.
(It is not necessary to bump the revision number if the release is supposed to
be the last one in a branch.)

=item 18.

Update milestones in the GitHub bug tracker to reflect the new release.

=item 19.

Make the previously generated release tar file, along with its MD5 checksum
and the possible diff from the previous release, available into the public
area of ftp.isc.org.  Sign the release, creating a F<.asc> file, using:

    gpg --detach-sign --armor inn-Y.Y.Y.tar.gz

Also create SHA-1, SHA-256, and SHA-512 files, with C<sha1sum>,
C<sha256sum>, and C<sha512sum>, respectively, and then sign them to create
corresponding F<.asc> files.  You don't need to retain the unsigned
checksum files.

Update the F<inn.tar.gz*> links and possibly move older releases off into
the F<OLD> directory.  Also remove testing versions from the F<testing>
directory.

(Currently, this is all done by Russ by updating the files in
L<https://archives.eyrie.org/software/inn/> and then letting ISC automatically
mirror them via rsync within a day.)

=item 20.

Now that the signature of the release has been generated, attach it to the
GitHub release.

=item 21.

After ftp.isc.org has correctly mirrored the release, send an announce
on inn-announce and in news.software.nntp (with a possible crosspost to
news.admin.announce).

The ISC web site does not need being updated as it does not directly link to
the release, nor mention the version of the last release.  (Just check the
information at L<https://www.isc.org/othersoftware/#INN> is still available
and accurate, though.)

=item 22.

For major releases, ping Russ to update the branch used for the
generation of STABLE snapshots.

=item 23.

Ping Russ to update L<https://www.eyrie.org/~eagle/software/inn/> with the
latest version information and, for a major release, clone the documentation
tree for CURRENT as a new stable documentation tree for the stable release
series.

Also ensure the URL redirection of the F<docs-Y.Y> subdirectory corresponding
to the next major release points to F<docs>.

=back

=head1 References

Some additional references that may be hard to find and may be of use to
people working on INN:

=over 4

=item L<https://www.eyrie.org/~eagle/nntp/>

The home page for the IETF NNTP standardization effort, including links
to the IETF NNTP working group archives and copies of the latest drafts
of the new NNTP standard.  The old archived mailing list traffic contains
a lot of interesting discussion of why NNTP is the way it is.

=item L<https://www.eyrie.org/~eagle/usefor/>

A collection of documents about the Usenet article format, including
most of the relevant RFCs and Internet-Drafts.

=item L<https://mailarchive.ietf.org/arch/browse/usefor/>

The archives for the USEFOR IETF working group, the working group for the
S<RFC 1036> replacement (the format of Usenet articles), now published as
S<RFC 5536> and S<RFC 5537>.

=item L<http://www.mibsoftware.com/userkt/userkt.html>

Forrest Cavalier provides several tools related to earlier versions
of INN (2.0 to 2.3.x).  His web site is a great source of information
about Usenet in general.

=item L<http://tools.ietf.org/html/draft-lutchann-ipv6-intro-00>

A primer on IPv6 with pointers to the appropriate places for more
technical details as needed, useful when working on IPv6 support in INN.

=back

=cut
