/*
** Copyright (C) 2003-2008 by Carnegie Mellon University.
**
** @OPENSOURCE_HEADER_START@
**
** Use of the SILK system and related source code is subject to the terms
** of the following licenses:
**
** GNU Public License (GPL) Rights pursuant to Version 2, June 1991
** Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013
**
** NO WARRANTY
**
** ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER
** PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY
** PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN
** "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
** KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT
** LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE,
** MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE
** OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT,
** SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY
** TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF
** WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
** LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF
** CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON
** CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE
** DELIVERABLES UNDER THIS LICENSE.
**
** Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie
** Mellon University, its trustees, officers, employees, and agents from
** all claims or demands made against them (and any related losses,
** expenses, or attorney's fees) arising out of, or relating to Licensee's
** and/or its sub licensees' negligent use or willful misuse of or
** negligent conduct or willful misconduct regarding the Software,
** facilities, or other rights or assistance granted by Carnegie Mellon
** University under this License, including, but not limited to, any
** claims of product liability, personal injury, death, damage to
** property, or violation of any laws or regulations.
**
** Carnegie Mellon University Software Engineering Institute authored
** documents are sponsored by the U.S. Department of Defense under
** Contract F19628-00-C-0003. Carnegie Mellon University retains
** copyrights in all material produced under this contract. The U.S.
** Government retains a non-exclusive, royalty-free license to publish or
** reproduce these documents, or allow others to do so, for U.S.
** Government purposes only pursuant to the copyright license under the
** contract clause at 252.227.7013.
**
** @OPENSOURCE_HEADER_END@
*/
#ifndef _SKTRANSFER_H
#define _SKTRANSFER_H

#include "silk.h"

RCSIDENTVAR(rcsID_SKTRANSFER_H, "$SiLK: sktransfer.h 11437 2008-05-01 17:41:25Z mthomas $");

/*
**  sktransfer.h
**
**  SiLK file transfer protocol
**
*/

#include "skdeque.h"


#if !defined(INADDR_NONE)
#define INADDR_NONE 0xffffffff
#endif

/* The type of transfer objects */
typedef struct sk_transfer_st *skTransfer_t;

/* Error values */
typedef enum sk_transfer_error {
    /* Success */
    SK_TRANSFER_ESUCCESS = 0,

    /* Received shutdown message */
    SK_TRANSFER_ESHUTDOWN = 1,

    /* Shutdown by program */
    SK_TRANSFER_ESHUTDOWN_EXTERNAL = 2,

    /* Bad argument to function */
    SK_TRANSFER_EBADARG = -1,

    /* File failed, but should be retried.  Sock in consistant
     * state. */
    SK_TRANSFER_EFAILED = -2,

    /* File failed, but should be retried; Sock in inconsistant
     * state. */
    SK_TRANSFER_EFAILED_CLOSE = -3,

    /* System failed badly.  errno should be set properly.  Sock
     * should bs considered inconsistant. */
    SK_TRANSFER_ESYSTEM = -4,

    /* System failed badly in thread call.  errno will be set to a
     * Posix thread error value..  Sock should bs considered
     * inconsistant. */
    SK_TRANSFER_ESYSTEM_THREAD = -5,

    /* File failed fatally.  Sock in consistant state. */
    SK_TRANSFER_EFATAL = -6,

    /* File failed fatally.  Sock in inconsistant state. */
    SK_TRANSFER_EFATAL_CLOSE = -7,

    /* Timeout */
    SK_TRANSFER_ETIMEOUT = -8
} sktErr_t;

/*
 * Detailed explanation of the above error conditions:
 *
 * ESUCCESS : This is not an error.  Everything worked properly.
 *            Guaranteed to be equivalent to zero.
 *
 * ESHUTDOWN : Only returned by skRcvFile.  Indicates the the receiver
 *             received a shutdown message.  Indicates the end of a
 *             transfer session.
 *
 * ESHUTDOWN_EXTERNAL : Indicates that the transfer session was
 *                      stopped by the program via skDestroyTransfer
 *                      or skTransferStop.
 *
 * EBADARG : Programmer error.  The function was passed an argument it
 *           did not expect, or cannor handle.
 *
 * EFAILED : Transfer of the file failed, but can (should?) be
 *           retried.  The transfer session is still in a consitant
 *           state.  This could be caused by network transmission
 *           errors (random bitflip errors).
 *
 * EFAILED_CLOSE : Transfer of the file failed, but can (should?) be
 *                 retried.  The transfer session is no longer in a
 *                 consistant state, however, and should be
 *                 reinitiated.
 *
 * ESYSTEM : A low level system call which was expected to succeed has
 *           failed.  The errno value contains the system level error.
 *           Usually this means that something is either dreadfully
 *           wrong with the system, or with the library itself.
 *           Should probably be responded to by a shutdown of the
 *           program.
 *
 * ESYSTEM_THREAD : A thread system call which was expected to succeed
 *                  has failed.  The errno value contains the thread
 *                  error.  Be warned that this is not necessary an
 *                  ordinary system errno, and as such should not be
 *                  used with functions like strerror.  See the POSIX
 *                  threads specification for more information on
 *                  error values.  Usually this error means that
 *                  something is either dreadfully wrong with the
 *                  system, or with the library itself.  Should
 *                  probably be responded to by a shutdown of the
 *                  program.
 *
 * EFATAL : Transfer of the file failed, and has failed in such a way
 *          that the file should not be retried.  The transfer session
 *          is still in a consistant state, however.  This error can
 *          be triggered by a file which has failed through multiple
 *          retries.
 *
 * EFATAL_CLOSE : Transfer of the file failed, and has failed in such
 *                a way that the file should not be retried.  The
 *                transfer session is no longer in a consistant state,
 *                and should be reinitiated.
 *
 * ETIMEOUT : The function or file did not complete, due to a timeout.
 *            In a transfer session, this implies that the session did
 *            not complete properly, and therefore should be
 *            considered inconsistant, and should be reinitiated.
 *
 */


/* The type of transfer callback functions */
typedef void (*skt_callback_t)(
    char           *file,
    void           *item,
    sktErr_t        error,
    skTransfer_t    transfer);

/* The type of logging functions */
typedef sk_msg_fn_t skt_logfn_t;

/* The type of pathname extraction functions */
typedef void (*skt_filename_fn_t)(void *, char *path, char *name);

/* The type of filename transformation functions */
typedef void (*skt_fn_xform_fn_t)(char *oldname, char *newname);

/****************************/
/*** High-level functions ***/
/****************************/

/*
 * There are two components here, which come in two different
 * varieties each. There is a sender, whose job is to send files, and
 * a receiver, whose job is to receive them.  With the high-level
 * functions, they act as daemons, connecting and sending/receiving
 * until they are told to stop.  As a result, each comes in a server
 * and a client variation.  The server will bind to a port and listen
 * for connections, while a client will be the one connecting to the
 * bound port.
 *
 * Each client will attempt to reconnect to the server if its session
 * is disrupted.
 */


/*** SENDERS ***/

/*
 * Creates a sender transfer session server object.  You should add a
 * queue using skTransferSetQueue before starting a sender server.
 *
 * skTransfer_t *sender
 *     The skTransfer_t object whose address is passed through this
 *     argument will be initialized to a handle which can be used to
 *     communicate with the session.  (I.e., you can query or stop the
 *     sender using this handle.
 *
 * int port
 *     The port upon which to listen for incoming connections.
 */

sktErr_t skCreateSenderServer(
    skTransfer_t   *sender,       /* returns sender handle */
    int             port);        /* Port to listen upon */


/*
 * Creates a sender transfer session client object.  You should add a
 * queue using skTransferSetQueue before starting a sender server.
 *
 * skTransfer_t *sender
 *     The skTransfer_t object whose address is passed through this
 *     argument will be initialized to a handle which can be used to
 *     communicate with the session.  (I.e., you can query or stop the
 *     sender using this handle.
 *
 * int port
 *     The receiver server to which the client is connecting's port.
 *
 * in_addr_t server
 *     The address of the server to which to connect.
 */
sktErr_t skCreateSenderClient(
    skTransfer_t   *sender,     /* returns sender handle */
    int             port,       /* Port to connect to */
    in_addr_t       server);    /* server address */


/*
 *  This sets the item->filename conversion function.  This function
 *  is handed items from the sendQ deque, and is required to fill two
 *  strings.  The first is a string which represents a path to the
 *  file to be sent.  The second indicates what the name the file
 *  should be given in the transfer session.  If this function is not
 *  set, then items on the deque are assumed to already be c-strings,
 *  with the sending name identical to the filename.
 */
sktErr_t skSenderSetFilenameFn(
    skTransfer_t        transfer,
    skt_filename_fn_t   filename_function);


/*** RECEIVERS ***/

/*
 * Creates a receiver transfer session server object.
 *
 * skTransfer_t *rcvr
 *
 *     The skTransfer_t object whose address is passed through this
 *     argument will be initialized to a handle which can be used to
 *     communicate with the session.  (I.e., you can query or stop the
 *     receiver using this handle.
 *
 * int port
 *
 *     The port upon which to listen for incoming connections.
 *
 * uint8_t maxclients
 *
 *     The maximum number of sender clients allowed to connect.
 *
 * char *where
 *
 *     The location (path) in which to place files as they are
 *     received.
 *
 */
sktErr_t skCreateReceiverServer(
    skTransfer_t   *rcvr,       /* returns rcvr handle */
    int             port,       /* Port to listen upon */
    uint8_t         maxclients, /* Max number of clients */
    const char     *where);     /* Where to place files */


/*
 * Creates a receiver transfer session client.
 *
 * skTransfer_t *rcvr
 *
 *     The skTransfer_t object whose address is passed through this
 *     argument will be initialized to a handle which can be used to
 *     communicate with the session.  (I.e., you can query or stop the
 *     receiver using this handle.
 *
 * int port
 *
 *     The sender server to which the client is connecting's port.
 *
 * in_addr_t server
 *
 *     The address of the server to which to connect.
 *
 * char *where
 *
 *     The location (path) in which to place files as they are
 *     received.
 */
sktErr_t skCreateReceiverClient(
    skTransfer_t   *rcvr,       /* returns rcvr handle */
    int             port,       /* Port to connect to */
    in_addr_t       saddr,      /* server address */
    const char     *where);     /* Where to place files */


/*
 * Sets the receiver's filename generator.  If this function is
 * non-null, it will be used to generate a new path based on the
 * transfered filename.  The first argument is the original
 * constructed path of the file.  The second argument is a pointer to
 * a buffer in which to store the new file name.  If the second
 * argument is set to the empty string, the name is taken to be the
 * last element of the path.  The buffers are of size PATH_MAX.
 */
sktErr_t skReceiverSetTransformFn(
    skTransfer_t        rcvr,
    skt_fn_xform_fn_t   fn_xform_function);


/*** SERVER FUNCTIONS ***/

/*
 * Adds a valid client address to the server object.  This will revert
 * a server that is currently accepting connections from any client
 * address.  Server must not yet have been started.
 */
sktErr_t skServerValidClientAdd(skTransfer_t server, in_addr_t client);

/*
 * Removes a valid client address from the server object.  This has no
 * effect on a server which is currently acccepting connections from
 * any client.   Server must not yet have been started.
 */
sktErr_t skServerValidClientRemove(skTransfer_t server, in_addr_t client);

/*
 * Sets the server to accept connections from any client.  This is the
 * default.  Server must not yet have been started.
 */
sktErr_t skServerValidClientAny(skTransfer_t server);

/*
 * Sets the local address the listening port is tied to.  The default
 * it for the listening port to listen across all valid local
 * addresses.  Server must not yet have been started.
 */
sktErr_t skServerSetListeningAddress(skTransfer_t server, in_addr_t addr);


/*** TRANSFER OBJECT FUNCTIONS ***/

/*
 * Starts a transfer session.  This creates its own threads and
 * immediately returns.
 */
sktErr_t skTransferStart(skTransfer_t transfer);

/* Stops a transfer session.   */
sktErr_t skTransferStop(skTransfer_t transfer, uint32_t timeout);

/* Shuts down and destroys a transfer session. */
sktErr_t skDestroyTransfer(skTransfer_t transfer);


/*
 * Sets the queue (implemented as a skDeque_t) for a transfer session.
 * This queue is used differently depending on whether the session is
 * a sender or a receiver.  Transfer session must not yet have been
 * started.
 *
 * If the session is a sender the queue will be used to commuinicate
 * the paths of the files to transmit.  The server will pop items off
 * the back of the deque to determine which file should be sent next.
 * The filename is extracted from the item using the current filename
 * function. If a filename function is not set (using
 * skSenderSetTransformFn), then the items are assumed to be c-strings
 * containing the path.  Items in the queue are never freed by this
 * library.
 *
 * If the session is a sender, the queue will be used to communicate
 * the paths of the files which have been transferred successfully.
 * The receiver will push pathnames onto the front of the deque to be
 * retrieved by the rest of the application.  These strings must be
 * freed by the application after use.
 */
sktErr_t skTransferSetQueue(skTransfer_t transfer, skDeque_t queue);

/*
 * Sets the minimum number of bits-per-second to expect for file
 * transfer on the transport medium.  This is used to calculate
 * timeouts for the sending of files.  The deafult if not set is
 * 2400.  Transfer session must not yet have been started.
 */
sktErr_t skTransferSetMinimumBitrate(skTransfer_t transfer, uint32_t bitrate);

/*
 * Sets the number of seconds to wait for an aknowledgement from the
 * receiver that it has successfully received the file.  A timeout of
 * zero implies that it should wait potentially forever.  The default
 * if not set is 3.  Transfer session must not yet have been
 * started.
 */
sktErr_t skTransferSetAckTimeout(skTransfer_t transfer, uint8_t timeout);

/*
 * Sets a function via which the transfer session can produce verbose
 * error messages in the event of error conditions.  If not set, there
 * will be no logging.  Transfer session must not yet have been
 * started.
 */
sktErr_t skTransferSetLogFn(skTransfer_t transfer, skt_logfn_t logfn);

/*
 * Sets a callback associated with the transfer session.  Transfer
 * session must not yet have been started.
 *
 * The function has the type:
 *
 *     void (*skt_callback_t)(char *file, void *item, sktErr_t error,
 *                            skTransfer_t transfer)
 *
 * If the session is a sender:
 *
 *     After a file is either sent, or has failed to be sent, this
 *     function will be called with the result.
 *
 *     The function will be called with the pathname it attempted to
 *     send, the item from which the pathname was generated (from the
 *     sendQ), the result of the sending of that file, and a handle to
 *     the session that tried to send it.  After this callback
 *     function is called, the item is no longer used, and can be
 *     freed from within the callback if desired.
 *
 *     In the event of a fatal error not connected with the sending of
 *     a file, the file and item values will be NULL.
 *
 * If the session is a receiver:
 *
 *     After a file has been received, the pathname is added to the
 *     deque (if successful), and the callback is executed.
 *
 *     The function will be called with the pathname it attempted to
 *     receive, a NULL for item, the error result of the receiving of
 *     that file, and a handle to the session that tried to send it.
 *     The path will be be freed by the library after the callback is
 *     executed, so copy it if you wish to store it past the lifetime
 *     of the function call.
 *
 *     In the event of a fatal error not connected with the receiving
 *     of a file, the file and item values will be NULL.
 *
 *
 * If the callback is not set, it is not used.
 */
sktErr_t skTransferSetCallback(skTransfer_t transfer, skt_callback_t callback);

/* Returns whether a transfer session object is a receiver. */
int skTransferIsReceiver(skTransfer_t transfer);

/* Returns whether a transfer session object is a sender. */
int skTransferIsSender(skTransfer_t transfer);

/* Returns whether a transfer session object is a client. */
int skTransferIsClient(skTransfer_t transfer);

/* Returns whether a transfer session object is a server. */
int skTransferIsServer(skTransfer_t transfer);


/* Returns in `queue' the deque used by the transfer session. */
sktErr_t skTransferQueue(skTransfer_t transfer, skDeque_t *queue);


/***************************/
/*** Mid-level functions ***/
/***************************/

/*
 * Given a connected socket, will manage a file communications stream
 * (sending).  This function runs asynchronously, creating its own
 * threads.
 *
 * The calling convention is similar to that of the above sender
 * creation functions.  The only difference is that an already
 * connected socket is used.  If that socket dies, then the threads
 * will end, unless skTransferChangeSock is used to refurbish the
 * socket from within the callback function.
 */
sktErr_t skCreateSender(
    skTransfer_t *sender,       /* returns sender handle */
    int sock);                  /* Socket to create sender on */


/*
 * Given a connected socket, will manage a file communications stream
 * (receiving).  This function runs asynchronously, creating its own
 * threads.
 *
 * The calling convention is similar to that of the above receiver
 * creation functions.  The only difference is that an already
 * connected socket is used.  If that socket dies, then the threads
 * will end, unless skTransferChangeSock is used to refurbish the
 * socket from within the callback function.
 */
sktErr_t skCreateReceiver(
    skTransfer_t   *receiver,   /* returns receiver handle */
    int             sock,       /* Socket to create receiver on */
    const char     *where);     /* Where to place files */


/*
 * Changes the socket used by the session.  If possible, it will
 * complete any file currently being transferred, shutdown the
 * connection gracefully (but not close the socket), and send a new
 * handshake on the new socket.
 */
sktErr_t skTransferChangeSock(skTransfer_t transfer, int sock);



/***************************/
/*** Low-level functions ***/
/***************************/

/*
 * Explanation of the protocol:
 *
 * A protocol session requires an open stream of communications (an
 * opened socket) between the sending and receiving entities.  During
 * a single session, the roles of the sender and the receiver remain
 * constant.  I.E., one side is the sender, and the other side the
 * receiver throughout the entire session; their roles may not swap in
 * the course of the session.
 *
 * The receiver's half of a session proceeds as follows:
 *
 * skRcvHandshake  // Initiate the session
 * do {
 *    skRcvFile    // Receive a file)
 * } until a SK_TRANSFER_ESHUTDOWN is received
 *
 *
 * The sender's half of a session proceeds as follows:
 *
 * skSendHandshake // Initiate the session
 *
 * while (filesToSend) {
 *    skSendFile   // Send a file
 * }
 * skSendShutdown  // Terminate the session gracefully
 *
 */

/*
 * When using a control pipe, send SKT_NOTIFY_ABORT as an intention to
 * abort. (This won't abort a read or write in progress.)  Send
 * SKT_ABORT in order to do an immediate abort.
 */
#define SKT_NOTIFY_ABORT '\0'
#define SKT_ABORT        '\1'

/* Does the low-level work of sending a file.  */
sktErr_t skSendFile(
    int         sock,
    const char *path,
    const char *name,
    skt_logfn_t logfn,
    int        *control);

/* Sends a shutdown message. */
sktErr_t skSendShutdown(int sock, skt_logfn_t logfn, int *control);

/* Sends a handshake. */
sktErr_t skSendHandshake(int sock, skt_logfn_t logfn, int *control);

typedef struct skRcvInfo_st {
  char  path[PATH_MAX];
  int   fd;
} skRcvInfo_t;

/* Does the low-level work of receiving a file. */
sktErr_t skRcvFile(
    int             sock,
    char           *where,
    skRcvInfo_t    *file,
    skt_logfn_t     logfn,
    int            *control);

/* Receive a handshake. */
sktErr_t skRcvHandshake(int sock, skt_logfn_t logfn, int *control);

#endif /* _SKTRANSFER_H */

/*
** Local Variables:
** mode:c
** indent-tabs-mode:nil
** c-basic-offset:4
** End:
*/
