=head1 NAME

libinn - InterNetNews library routines

=head1 SYNOPSIS

    #include "inn/libinn.h"

    #define ISWHITE(c) ...

    /* Headers-related routines. */
    extern char *GenerateMessageID(char *domain);
    extern void HeaderCleanFrom(char *from);
    extern struct _DDHANDLE *DDstart(FILE *FromServer, FILE *ToServer);
    extern void DDcheck(struct _DDHANDLE *h, char *group);
    extern char *DDend(struct _DDHANDLE *h);

    /* Cancel-Lock routines. */
    extern bool gen_cancel_lock(const char *msgid, const char *username,
                                char **canbuff);
    extern bool gen_cancel_key(const char *hdrcontrol,
                               const char *hdrsupersedes,
                               const char *username, char **canbuff);
    extern bool verify_cancel_key(const char *c_key_header,
                                  const char *c_lock_header);

    /* Opening the active file on a client. */
    extern FILE *CAopen(FILE *FromServer, FILE *ToServer);
    extern FILE *CAlistopen(FILE *FromServer, FILE *ToServer,
                            const char *request);
    extern void CAclose(void);

    /* File locking. */
    enum inn_locktype
    {
        INN_LOCK_READ,
        INN_LOCK_WRITE,
        INN_LOCK_UNLOCK
    };

    extern bool inn_lock_file(int fd, enum inn_locktype type,
                              bool block);

    /* NNTP functions. */
    extern int NNTPlocalopen(FILE **FromServerp, FILE **ToServerp,
                             char *errbuff, size_t len);
    extern int NNTPremoteopen(int port, FILE **FromServerp,
                              FILE **ToServerp, char *errbuff,
                              size_t len);
    extern int NNTPconnect(const char *host, int port,
                           FILE **FromServerp, FILE **ToServerp,
                           char *errbuff, size_t len);
    extern int NNTPsendarticle(char *text, FILE *ToServer,
                               bool terminate);
    extern int NNTPsendpassword(char *server, FILE *FromServer,
                                FILE *ToServer);

    /* Hash functions. */
    typedef struct {
        char hash[16];
    } HASH;

    extern HASH HashMessageID(const char *MessageID);

    /* Other useful functions. */
    extern char *inn_getfqdn(const char *domain);
    extern char *GetModeratorAddress(FILE *FromServer, FILE *ToServer,
                                     char *group, char *moderatormailer);

    /* Miscellaneous. */
    extern int GetResourceUsage(double *usertime, double *systime);
    extern void Radix32(unsigned long value, char *buff);
    extern char *ReadInDescriptor(int fd, struct stat *Sbp);
    extern char *ReadInFile(const char *name, struct stat *Sbp);

    /* Setting or clearing file descriptor flags. */
    #include "inn/fdflag.h"

    bool fdflag_close_exec(int fd, bool flag);
    bool fdflag_nonblocking(socket_type fd, bool flag);

=head1 DESCRIPTION

I<libinn> is a library of utility routines for manipulating Usenet articles
and related data.  The whole documentation of I<libinn> routines is split
into several specific man pages besides this one: libinn_clientlib(3),
libinn_dbz(3), libinn_inndcomm(3), libinn_list(3), libinn_qio(3),
libinn_tst(3) and libinn_uwildmat(3).

=head2 MACROS

C<ISWHITE> is a macro which tests whether its char argument is a space or a
tabulation.

=head2 HEADERS-RELATED ROUTINES

B<GenerateMessageID> uses the current time, B<nnrpd>'s process ID, a global
static counter incrementing at each post in the same NNTP session, and a fully
qualified domain name, which is passed as an argument, to create a Message-ID
header field that is highly likely to be unique.  The returned value points
to static space that is reused on subsequent calls.  If the argument is NULL,
B<inn_getfqdn> is called to find a suitable FQDN (using I<domain> as set in
F<inn.conf> to qualify the local host name if local host cannot be resolved
in DNS).

B<HeaderCleanFrom> removes the extraneous information from the value of a From
or Reply-To header field and leaves just the official mailing address.  In
particular, the following transformations are made to the I<from> parameter:

=over 4

=item *

address --> address

=item *

address (stuff) --> address

=item *

stuff <address> --> address

=back

The transformations are simple, based on S<RFC 5536> which limits the format
of the header field.

B<DDstart>, B<DDcheck>, and B<DDend> are used to set the Distribution header
field; the C<DD> stands for I<Default Distribution>.  The F<distrib.pats>
file is consulted to determine the proper value for the Distribution header
field after all newsgroups have been checked.  B<DDstart> begins the parsing.
It returns a pointer to an opaque handle that should be used on subsequent
calls.  The I<FromServer> and I<ToServer> parameters should be C<FILE>'s
connected to the NNTP server for input and output, respectively.  If either
parameter is NULL, then an empty default will ultimately be returned if the
file is not locally available.

B<DDcheck> should be called with the handle, I<h>, returned by B<DDstart> and
a newsgroup, I<group>, to check.  It can be called as often as necessary.

B<DDend> releases any state maintained in the handle and returns an allocated
copy of the text that should be used for the Distribution header field.

=head2 CANCEL-LOCK

These routines are available in I<libinn> if INN was built with Cancel-Lock
support (otherwise they are stub functions which are always returning false).
The C<configure> script automatically enables that support if it finds
libcanlock.

B<gen_cancel_lock> expects a Message-ID, a username (possibly NULL) and the
address of a pointer where it will write elements suitable for the body of a
Cancel-Lock header field.

B<gen_cancel_key> expects a Control header field body (possibly NULL), a
Supersedes header field body (possibly NULL), a username (possibly NULL) and
the address of a pointer where it will write elements suitable for the body
of a Cancel-Key header field.  This function extracts the Message-ID from the
Control header field body or, if NULL, from the Supersedes header field body.
If the header field used for that extraction is not correctly formatted, or
they are both NULL, or no Message-ID is found, it will result in a failure.

These functions return true if all the elements were written, false on
failure.  The I<canbuff> string will be allocated by these functions, unless
they return false.  The caller is responsible for freeing it.

For each admin secret in the I<canlockadmin> vector set in F<inn.conf>,
and for each user secret set in the I<canlockuser> vector set in F<inn.conf>
if I<username> is not NULL, B<gen_cancel_lock> will generate in I<canbuff>
both sha1 and sha256 Base64-encoded hashes.  Regarding B<gen_cancel_key>,
it will be for each admin secret in the I<canlockadmin> vector if I<username>
is NULL or for each user secret in the I<canlockuser> vector if I<username>
is not NULL.

B<verify_cancel_lock> expects pointers to Cancel-Key and Cancel-Lock header
field bodies.  This function verifies that I<c_key_header> contains an element
matching one of those present in I<c_lock_header>.  It returns true if at
least one element matches.  Otherwise, false is returned, that is to say the
cancel or supersede request cannot be authenticated.

=head2 CLIENT ACTIVE FILE

B<CAopen> and B<CAclose> provide news clients with access to the F<active>
file; the C<CA> stands for I<Client Active>.  B<CAopen> opens the F<active>
file for reading.  It returns a pointer to an open C<FILE>, or NULL on
error.  If a local or NFS-mounted copy exists, B<CAopen> will use that file.
The I<FromServer> and I<ToServer> parameters should be C<FILE>'s connected
to the NNTP server for input and output, respectively.  See B<NNTPremoteopen>
or B<NNTPlocalopen>, below.  If either parameter is NULL, then B<CAopen> will
just return NULL if the file is not locally available.  If they are not NULL,
B<CAopen> will use them to query the NNTP server using the LIST command to
make a local temporary copy.

The B<CAlistopen> sends a LIST command to the server and returns a temporary
file containing the results.  The I<request> parameter, if not NULL, will be
sent as an argument to the command.  Unlike B<CAopen>, this routine will never
use a locally-available copy of the F<active> file.

B<CAclose> closes the F<active> file and removes any temporary file that might
have been created by B<CAopen> or B<CAlistopen>.

=head2 FILE LOCKING

B<inn_lock_file> tries to lock the file descriptor I<fd>.  If I<block> is
true, it will block until the lock can be made, otherwise it will return
false if the file cannot be locked.  I<type> is one of C<INN_LOCK_READ>,
C<INN_LOCK_WRITE> or C<INN_LOCK_UNLOCK>.  It returns false on failure or true
on success.

=head2 NNTP FUNCTIONS

B<NNTPlocalopen> opens a connection to the private port of an InterNetNews
server running on the local host, if C<HAVE_UNIX_DOMAIN_SOCKETS> in
F<include/config.h> is defined.  It returns C<-1> on failure, or C<0> on
success.  I<FromServerp> and I<ToServerp> will be filled in with C<FILE>'s
which can be used to communicate with the server.  I<errbuff> can either be
NULL or a pointer to a buffer at least 512 bytes long.  If not NULL, and the
server refuses the connection, then it will be filled in with the text of
the server's reply.  I<len> should be the length of the I<errbuff> buffer.
This routine is not for general use.  If C<HAVE_UNIX_DOMAIN_SOCKETS> in
F<include/config.h> is not defined, this is a stub routine, for compatibility
with systems that have Unix-domain stream sockets, and it then always returns
C<-1>.

B<NNTPremoteopen> does the same, except that it uses the I<server> parameter
set in F<inn.conf> as the local server, and opens a connection to the I<port>.
Any client program can use this routine.  It returns C<-1> on failure, or C<0>
on success.

B<NNTPconnect> is the same as B<NNTPremoteopen> except that the desired host
is given as the I<host> parameter.

B<NNTPsendarticle> writes I<text> on I<ToServer> using NNTP conventions for
line termination.  The text should consist of one or more lines ending with
a newline.  If I<terminate> is true, then the routine will also write the NNTP
data-termination marker on the stream.  It returns C<-1> on failure, or C<0>
on success.

B<NNTPsendpassword> sends authentication information to an NNTP server by
finding the appropriate entry in the F<passwd.nntp> file.  I<server> contains
the name of the host; the I<server> parameter in F<inn.conf> will be used if
I<server> is NULL.  I<FromServer> and I<ToServer> should be C<FILE>'s that
are connected to the server.  No action is taken if the specified host is not
listed in the password file.

=head2 HASHES

B<HashMessageID> returns hashed Message-ID using MD5.

=head2 OTHER USEFUL FUNCTIONS

B<inn_getfqdn> returns the fully qualified domain name of the local host.
I<domain> is used to qualify the local host name if local host cannot be
resolved in DNS.  The returned value points to newly-allocated memory that the
caller is responsible for freeing, or NULL on error.

B<GetModeratorAddress> returns the mailing address of the moderator for
specified I<group> or NULL on error.  I<moderatormailer> is used as its
address, if there is no matched moderator.  See moderators(5) for details on
how the address is determined.  B<GetModeratorAddress> does no checking to
see if the specified group is actually moderated.  The returned value points
to static space that is reused on subsequent calls.  The I<FromServer> and
I<ToServer> parameters should be C<FILE>'s connected to the NNTP server for
input and output, respectively.  If either of these parameters is NULL, then
an attempt to get the list from a local copy is made.

=head2 MISCELLANEOUS

B<GetResourceUsage> fills in the I<usertime> and I<systime> parameters with
the total user and system time used by the current process and any children
it may have spawned.  If C<HAVE_GETRUSAGE> in F<include/config.h> is defined,
it gets the values by doing a getrusage(2) system call; otherwise it calls
times(2).  It returns C<-1> on failure, or C<0> on success.

B<Radix32> converts the number in I<value> into a radix-32 string into the
buffer pointed to by I<buff>.  The number is split into five-bit pieces and
each piece is converted into a character using the alphabet C<0..9a..v> to
represent the numbers C<0..32>.  Only the lowest 32 bits of I<value> are used,
so I<buff> needs only pointing to a buffer of eight bytes (seven characters
and the trailing C<\0>).

B<ReadInFile> reads the file named I<name> into allocated memory, appending a
terminating C<\0> byte.  It returns a pointer to the space, or NULL on error.
If I<Sbp> is not NULL, it is taken as the address of a place to store the
results of a stat(2) call.

B<ReadInDescriptor> performs the same function as B<ReadInFile> except that
I<fd> refers to an already-open file.

=head2 FILE DESCRIPTOR FLAGS

B<fdflag_close_exec> can make a descriptor I<close-on-exec> so that it is not
shared with any child processes.  If the flag is true, the file is so marked;
if false, the I<close-on-exec> mode is cleared.  It returns false on failure
(or when the function is unsupported) or true on success.

B<fdflag_nonblocking> enables (if I<flag> is true) or disables (if I<flag>
is false) non-blocking I/O on the indicated C<socket_type> (which can be a
non-socket file descriptor on UNIX systems, but a C<socket_type> is expected
on Windows).  It returns false on failure or true on success.

=head1 EXAMPLES

    #include "inn/fdflag.h"
    #include "inn/libinn.h"

    char *p;
    char frombuff[256], errbuff[256];
    FILE *F;
    FILE *ToServer;
    FILE *FromServer;
    int port = 119;

    strlcpy(frombuff, HDR(HDR__FROM), sizeof(frombuff));
    HeaderCleanFrom(frombuff);

    if ((F = CAopen(FromServer, ToServer)) == NULL)
        Fatal("Can't open active file");

    /* Don't pass the file on to our children. */
    fdflag_close_exec(fileno(F), true);

    /* Make a local copy. */
    p = ReadInDescriptor(fileno(F), (struct stat *) NULL);

    /* Close the file. */
    CAclose();

    if (NNTPremoteopen(port, &FromServer, &ToServer, errbuff,
                       sizeof(errbuff)) < 0)
        Fatal("Can't connect to server");

    if ((p = GetModeratorAddress(NULL, NULL, "comp.sources.unix",
                                 "%s@example.com")) == NULL)
        Fatal("Can't find moderator's address");

=head1 HISTORY

Written by Rich $alz <rsalz@uunet.uu.net> for InterNetNews.  Rewritten into
POD by Julien Elie.

=head1 SEE ALSO

active(5), inn.conf(5), libinn_clientlib(3), libinn_dbz(3),
libinn_inndcomm(3), libinn_list(3), libinn_qio(3), libinn_tst(3),
libinn_uwildmat(3), moderators(5), passwd.nntp(5).

=cut
