/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "Licence").
 * You may not use this file except in compliance with the Licence.
 *
 * You can obtain a copy of the licence at RISC OS path @.^.LICENCE
 * or  http://www.riscosdev.com/lanman98/LICENCE.CDDL
 * See the Licence for the specific language governing permissions
 * and limitations under the Licence.
 *
 * CDDL HEADER END
 */

/*
 *   Portions Copyright RISC OS Developments 2019+, credited to the RISC OS One Project.
 *
 *   Finding servers without being told where they are.
 *
 *   Machines that offer file sharing say so on the local network, and have
 *   done for years: a question is asked of everybody at once and whoever
 *   offers the service answers.  That is how a Mac, a Synology or a QNAP
 *   appears in somebody else's list without being configured anywhere.
 *
 *   The question goes to everybody, but because it is asked from an
 *   ordinary port rather than the one the service uses, the answers come
 *   back to that port alone.  Nothing has to be joined or listened to
 *   beyond the socket the question went out of, which is the only reason
 *   this fits in the space available.
 */

#ifndef _DISCOVER_
#define _DISCOVER_

typedef struct found_s
{
    struct found_s *next;
    char *name;                 /* what the machine calls itself */
    char addr[20];              /* its address, in dotted form */
    int port;
} *found_t;

found_t DiscoverServers(int wait_cs);
/*
    Ask, wait the given number of centiseconds, and return what answered.
    NULL if nothing did, which is not an error - a network may have nothing
    on it that announces itself.
*/

found_t DiscoverWSD(int wait_cs);
/*
    The same, asked the way Windows answers.

    Windows stopped keeping a browse list when the Computer Browser service
    was retired; what it does now is reply to a SOAP probe sent to
    239.255.255.250.  A machine that answers this one may well be silent to
    the question above, and the other way about, so both are worth asking.

    Costs one packet and asks nothing of the servers beyond a reply, so it
    is as safe to leave on a timer as the other one.
*/

found_t DiscoverBoth(int wait_cs);
/*
    Both multicast questions at once, merged.

    Each is one packet out and whatever comes back, so asking both costs
    what asking either costs: they go out together and one window collects
    the answers to both.  This is what a command should call; the two
    separately are for asking one of them on its own.
*/

found_t DiscoverAdd(found_t *head, const char *name, const char *addr, int port);
/*
    Put one machine on a list, from wherever it was heard of, and return
    the entry - existing or new.  A machine already on the list is not added
    twice; a name with no address gets the name as its address, which every
    connect path resolves anyway.
*/

void DiscoverMerge(found_t *head, found_t add);
/*
    Fold one list into another through DiscoverAdd and free the one folded
    in.  For putting what the several ways of asking turned up together.
*/

char *DiscoverResolve(const char *name, int wait_cs);
/*
    Ask the network what address one machine is at, by name, and return it
    in dotted form for the caller to free - or NULL if nothing answered.

    This is the same question the announcements are made of, asked about a
    single name rather than about everything at once, and it is how a name
    ending in .local is meant to be looked up.  It is also worth asking for
    a plain name: a machine that answers to "webserver" here is usually the
    same one that answers to "webserver.local", and on a network with no
    name server and no NetBIOS it is the only thing that will answer at
    all.
*/

void DiscoverFree(found_t list);

typedef struct sweep_s *sweep_t;

sweep_t DiscoverBegin(int wait_cs);
/*
    Ask, and return somewhere to collect the answers - the same question
    DiscoverServers asks, but without waiting for anybody to reply.  This
    is for a caller that cannot block: a sweep run from a callback asks
    here, returns to the machine, and comes back through DiscoverPoll.

    Throws if the question could not be asked at all.
*/

int DiscoverPoll(sweep_t sw);
/*
    Take whatever has arrived since last time, without waiting.  Returns
    non-zero while the collecting period asked for still has time left in
    it, so a caller loops until it returns 0 and then calls DiscoverEnd.
*/

found_t DiscoverEnd(sweep_t sw);
/*
    Finish a sweep and hand back what answered, for DiscoverFree.  The
    sweep itself is destroyed.
*/

void DiscoverAbandon(sweep_t sw);
/*
    Throw a sweep away along with anything it had collected.  Safe on NULL,
    and the way to drop one that is still in progress.
*/

#endif
