/*
 * $Id: protocol.h,v 1.36 2003/11/23 20:13:44 jasta Exp $
 *
 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

#ifndef __PROTOCOL_H
#define __PROTOCOL_H

/*****************************************************************************/

/**
 * @file protocol.h
 *
 * @brief Provides a shared method definition for plugins and the daemon to
 *        use for all communication.
 *
 * The protocol structure unifies all the API that plugins will use to
 * interact directly with the daemon for clarity and portability reasons.
 */

/*****************************************************************************/

/*
 * We don't need LIBGIFTPROTO_EXPORT for anything but building the DLL on
 * win32.
 */
#ifndef LIBGIFTPROTO_EXPORT
# define LIBGIFTPROTO_EXPORT
#endif

#ifndef GIFTD_EXPORT
# define GIFTD_EXPORT
#endif

/*
 * Define GIFT_PLUGIN_EXPORT for plugins to export their init function.  We
 * are using GIFT_PLUGIN as the prefix here (instead of GIFTPLUGIN) to match
 * the #define GIFT_PLUGIN that giFT protocol plugins must already use.
 */
#ifdef GIFT_PLUGIN
# ifdef _MSC_VER
#  define GIFT_PLUGIN_EXPORT __declspec(dllexport)
# else
#  define GIFT_PLUGIN_EXPORT
# endif
#endif

/*****************************************************************************/

struct protocol;
struct if_event;
struct file_share;

#include "transfer_api.h"
#include "if_event_api.h"

#include "share_hash.h"

/*****************************************************************************/

/* provide LIBGIFTPROTO_VERSION and LIBGIFTPROTO_MKVERSION */
#include "protocol_ver.h"              /* generated by autoconf */

/*****************************************************************************/

/**
 * Protocol structure containing methods for bidirectional communication
 * between daemon and protocol plugin.
 */
typedef struct protocol
{
	/*************************************************************************/
	/**
	 * @name Public
	 * Public members.
	 */

	char     *name;                    /**< protocol name */
	char     *version_str;             /**< protocol version, will be shown
	                                    *   by giFT -V */
	void     *udata;                   /**< arbitrary data associated by the
	                                    *   plugin */

	/**
	 * @name Private
	 * Private members.
	 */

	void     *handle;                  /**< lt_dlopen handle (NOTE: we cant
	                                    *   use lt_dlhandle here because
	                                    *   plugins depend on the size of
	                                    *   this structure, but may not
	                                    *   have lt_dlopen available!) */

	Dataset  *hashes;                  /**< alternate index of hashes on
	                                    *   a per-protocol basis */

	/**
	 * Protocol features
	 *
	 * @param static-chunks
	 * @param range-get
	 * @param user-browse
	 * @param hash-unique
	 * @param chat-user
	 * @param chat-group
	 */
	Dataset *features;

	/*************************************************************************/
	/**
	 * @name giFT Assigned Methods
	 *
	 * Communication methods defined by giFT before protocol's
	 * initialization.  No protocol should override these, however, most
	 * protocols will want to call at least some of these throughout the
	 * course of their protocol's lifespan.
	 */

	/**
	 * Initialization function.  This should not actually "start" the
	 * protocol, merely setup the Protocol structure passed.  Note that giFT
	 * assigns this value before plugin initialization is called, and will
	 * always be set to your protocol's fully qualified case-sensitive name
	 * with the string literal "_init" appened to the ending.  So, for
	 * OpenFT's case, this value will be set to OpenFT_init prior to the
	 * calling of said function.
	 *
	 * @param p        Protocol pointer associated with the plugin being
	 *                 initialized.  This object is a private communication
	 *                 stream between giFT and the plugin exclusively.
	 *
	 * @return Boolean success or failure.
	 */
	BOOL (*init) (struct protocol *p);

	/* simple shorthand for the macros below */
#define FILE_LINE_FUNC __FILE__,__LINE__,__PRETTY_FUNCTION__

#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
# define DBGFN(p,...) trace(p,FILE_LINE_FUNC,__VA_ARGS__)
# define DBGSOCK(p,c,...) tracesock(p,c,FILE_LINE_FUNC,__VA_ARGS__)
#elif defined (__GNUC__)
# define DBGFN(p,fmt...) trace(p,FILE_LINE_FUNC,fmt)
# define DBGSOCK(p,c,fmt...) tracesock(p,c,FILE_LINE_FUNC,fmt)
#else
# define DBGFN dbg
# define DBGSOCK dbgsock
#endif

	/**
	 * Method for producing informative log lines from within a protocol
	 * plugin.  Do not use this function directly, see ::DBGFN instead.
	 */
	int (*trace) (struct protocol *p, char *file, int line, char *func,
	              char *fmt, ...);

	/**
	 * Special debugging method that will automatically format and display
	 * the peer information.  This would not be provided unless I was pretty
	 * sure every plugin will use remote sockets <g>.
	 */
	int (*tracesock) (struct protocol *p, TCPC *c,
	                  char *file, int line, char *func, char *fmt, ...);

	/**
	 * Print debugging information to the appropriate logging channel.  This
	 * data does not include information about the line that produced the log
	 * message.
	 */
	int (*dbg) (struct protocol *p, char *fmt, ...);

	/**
	 * See Protocol::dbg and Protocol::tracesock.
	 */
	int (*dbgsock) (struct protocol *p, TCPC *c, char *fmt, ...);

	/**
	 * Logging facility using the warning log channel.
	 */
	int (*warn) (struct protocol *p, char *fmt, ...);

	/**
	 * Logging facility using the error log channel.
	 */
	int (*err) (struct protocol *p, char *fmt, ...);

#define HASH_PRIMARY   0x01
#define HASH_SECONDARY 0x02
#define HASH_LOCAL     0x04            /* HASH_PRIMARY implicitly adds this */

	/**
	 * Adds a new hashing algorithm handler for this plugin.  Please note
	 * that while only one primary hashing routine may be specified, multiple
	 * secondary routines are allowed.
	 *
	 * @param type    String literal to represent the hashing algorithm.
	 *                Common names are SHA1, MD5, etc.
	 * @param opt     Handler options specific to the way giFT understands what
	 *                your plugin needs.  Using HASH_PRIMARY will cause all
	 *                local shares to be hashed, however, HASH_SECONDARY algos
	 *                will still be able to verify download integrity.  If
	 *                the HASH_LOCAL flag is present, local shares will be
	 *                hashed using the supplied secondary algorithm.
	 * @param algofn
	 * @param dspfn   Callback handler used to create an ASCII string
	 *                representation of the hash supplied.  If NULL is
	 *                supplied, a simple hex conversion will take place.
	 *
	 * @return Boolean success or failure.
	 */
	BOOL (*hash_handler) (struct protocol *p, const char *type, int opt,
	                      HashFn algofn, HashDspFn dspfn);

	/**
	 * Add a protocol feature.  This informs giFT of the various
	 * protocol/network-specific features this plugin can or can not
	 * successfully implement so as to modify giFT's behaviour.  Currently,
	 * none of the feature-set defined by the default plugin (OpenFT) is
	 * actually used conditionally, however there will come a time when that
	 * is the case.  For now it is probably safe just to ignore this. For a
	 * detailed list of supported options, see Protocol::features.
	 *
	 * @param p
	 * @param feature  Feature's name, this is not an arbitrary string and
	 *                 should be selected from a determined list that we
	 *                 will eventually provide.
	 * @param spt      Boolean value for this feature's support.
	 */
	void (*support) (struct protocol *p, const char *feature, BOOL spt);

#define SHARE_LOOKUP_HASH    (1)       /**< By stored hash */
#define SHARE_LOOKUP_HPATH   (2)       /**< By exclusion (hidden) path */
#define SHARE_LOOKUP_PATH    (3)       /**< Full local path */
	/**
	 * Lookup a locally shared file based on `command' criteria.  This is the
	 * only interface to directly query the internally managed share cache.
	 *
	 * \example
	 * share = p->share_lookup (p, SHARE_LOOKUP_HASH, "MD5", md5sum, 16);
	 * \endexample
	 *
	 * I apologize for the relative lack of documentation provided for this
	 * function.  Hopefully I will get around to it soon (boy do I say that a
	 * lot *grin*).
	 *
	 * @param p
	 * @param command  Lookup type to perform.  Possible commands are defined
	 *                 above.
	 * @param ...      Command-specific lookup arguments.
	 *
	 * @return Direct pointer into the local share cache or NULL if the
	 *         entry was not found.
	 */
	struct file_share* (*share_lookup) (struct protocol *p, int command, ...);

#define UPLOAD_AUTH_ALLOW          (0)
#define UPLOAD_AUTH_STALE         (-1)
#define UPLOAD_AUTH_MAX           (-2)
#define UPLOAD_AUTH_MAX_PERUSER   (-3)
#define UPLOAD_AUTH_NOTSHARED     (-4)
#define UPLOAD_AUTH_HIDDEN        (-5)
	/**
	 * Inform giFT of a new upload request, specifically one which needs
	 * access to a locally shared object (that is, something in
	 * gift:/sharing/root).  The return value from this function will help
	 * determine how the plugin should proceed.
	 *
	 * @param p
	 * @param user
	 *
	 *   Username that can be compared with previously registered uploads.
	 *   Consider setting and using Protocol::user_cmp for this to work well.
	 *
	 * @param share      Share that we are considering for upload.
	 * @param auth_info
	 *
	 *   Optional interface for retrieving additional information from the
	 *   auth call, such as which queue position the request *has been placed
	 *   in for UPLOAD_AUTH_MAX.
	 *
	 * @retval UPLOAD_AUTH_ALLOW
	 *
	 *   Upload has been accepted.  You still need to call
	 *   Protocol::upload_start when the transfer should be displayed. Please
	 *   note that you must minimize the window between an upload_request
	 *   call and an upload_start call to prevent users from slipping under
	 *   the queue.
	 *
	 * @retval UPLOAD_AUTH_STALE
	 *
	 *   The share requested has changed since the last share sync.  This
	 *   triggers an immediate non-blocking share sync, and the transfer
	 *   should be temporarily aborted.
	 *
	 * @retval UPLOAD_AUTH_MAX
	 *
	 *   Maximum uploads currently reached.  Please note that an internally
	 *   managed fair upload queue is maintained and can be access using
	 *   Protocol::upload_queue.  This queue is based on the assumption that
	 *   the download will be retried until allowed.  You may disable this
	 *   functionality through the features dataset.
	 *
	 * @retval UPLOAD_AUTH_MAX_PERUSER
	 *
	 *   The user is already downloading a file from us, and will not be
	 *   allowed to occupy two positions at once.  A local queue should be
	 *   implemented in whichever client made this request.
	 *
	 * @retval UPLOAD_AUTH_NOTSHARED
	 *
	 *   The requested object is not a valid share according to the currently
	 *   loaded shares database.  No action is taken to resync automatically.
	 *
	 * @retval UPLOAD_AUTH_HIDDEN
	 *
	 *   The user is not sharing any files at the moment, but may share
	 *   in the future.
	 */
	int (*upload_auth) (struct protocol *p, const char *user,
	                    struct file_share *share, upload_auth_t *auth_info);

	/**
	 * Inform giFT of a new upload which is about to begin.  Please note that
	 * this does not tackle authorization of the share and in fact allows
	 * subversion of that entire system if you so wish.  For normal uploads
	 * you will definitely want to obey the auth rules so I highly suggest
	 * calling and properly handling Protocol::upload_request prior to using
	 * this method.
	 *
	 * @param p
	 * @param chunk  Storage location for the chunk handle of the newly
	 *               created transfer.
	 * @param user   Username that will be reported to the interface protocol.
	 * @param share
	 *
	 *   Shared object used to specify the upload.  Under normal
	 * 	 circumstances this should be returned to you from
	 * 	 Protocol::share_lookup.  Please note that the hash value will be
	 * 	 extracted based on the registered hashing algorithms of this
	 * 	 protocol.
	 *
	 *   This interface does not require that you use a share from
	 *   Protocol::share_lookup, only that it is recommended.  You may
	 *   optionally provide your own protocol-defined upload by filling in
	 *   the following fields:
	 *
	 *   Share::path
	 *   Share::mime
	 *   Share::size
	 *
	 *   You must also set a valid hash for the primary registered hashing
	 *   algorithm for your plugin.
	 *
	 * @param start  Beginning file offset that will be uploaded.
	 * @param stop   End offset to be delivered.
	 *
	 * @return Transfer handle to use with the subsequent called to
	 *         Protocol::chunk_write.  You may at any time receive a
	 *         Protocol::upload_stop call that will request you clean up
	 *         any data you associate after the transfer has begun.
	 */
	struct transfer* (*upload_start) (struct protocol *p,
									  struct chunk **chunk,
	                                  const char *user,
	                                  struct file_share *share,
	                                  off_t start, off_t stop);

	/**
	 * Write data to the specified bidirectional chunk object.  This is used
	 * for transfers when data has either been completely received or sent
	 * out (not considering kernel-level socket buffering).  giFT will
	 * internally track transfer progress and/or write the necessary data to
	 * file.
	 *
	 * @param p
	 * @param transfer
	 * @param chunk
	 * @param source
	 * @param data
	 * @param len
	 */
	void (*chunk_write) (struct protocol *p,
	                     struct transfer *transfer,
	                     struct chunk *chunk, struct source *source,
	                     unsigned char *data, size_t len);

	/**
	 * Cancel a source as though the user had requested its removal.  This is
	 * useful when your plugin knows (much better than giFT) that attempting
	 * to retrieve from this source is futile or that it could be dangerous.
	 * Please note that giFT has an internal automagic timeout for sources
	 * that do not have data throughput.
	 *
	 * @param p
	 * @param transfer
	 * @param source
	 */
	void (*source_abort) (struct protocol *p,
						  struct transfer *transfer, struct source *source);

	/**
	 * Set the source status.
	 *
	 * @param p
	 * @param source
	 * @param klass   Basic status grouping so that giFT may still apply
	 *                some broad logic based on transfer progress/status.
	 * @param disptxt Literal text to display to the giFT clients.
	 */
	void (*source_status) (struct protocol *p, struct source *source,
	                       enum source_status klass, const char *disptxt);

	/**
	 * Send a search result from the plugin.  This communication is used by
	 * the protocol when results need to be delivered to the interface
	 * protocol.
	 *
	 * @param p
	 * @param event Internal event delivered by the search request.
	 * @param user  Arbitrary protocol-specific string attempting to uniquely
	 *              represent the user which has the result.
	 * @param node  Parent node or server which returned the result.  May be
	 *              NULL.
	 * @param href  Protocol-specific reference string.  Example:
	 *              Napster://jasta[gnapster]@NecessaryEvil/file.mp3
	 * @param avail Availability of this file.  If the protocol has no concept
	 *              of this, use 0.  You should also inform giFT that your
	 *              protocol has no support for availability through
	 *              Protocol::support.
	 * @param file  Complete FileShare structure.  See Protocol::share_new.
	 */
	void (*search_result) (struct protocol *p, struct if_event *event,
	                       char *user, char *node, char *href,
	                       unsigned int avail, struct file_share *file);

	/**
	 * Inform giFT that all search results that can be delivered have been
	 * delivered, or there is some reasonable reason to assume we have
	 * received the bulk of all results (if your protocol has no concept of
	 * clean search termination).  This will "cancel" the search event
	 * gracefully and return to the interface protocol that your plugin has
	 * terminated.
	 */
	void (*search_complete) (struct protocol *p, struct if_event *event);

	/**
	 * Deliver a raw message to the interface protocol.
	 *
	 * @param p
	 * @param message
	 * @param persist If true, this message will be delivered each time a user
	 *                attaches to the running daemon.
	 */
	void (*message) (struct protocol *p, char *message);

	/*************************************************************************/
	/**
	 * @name Protocol-specified Communication
	 *
	 * These methods will be defined by your protocol at the time of
	 * Protocol::init. You should understand that you do not need to
	 * initialize all these values, as giFT will provide dummy
	 * implementations for them as to guarantee no method points to NULL.
	 * However, you should note that a failure to set certain key
	 * communication methods will result in a nearly worthless protocol
	 * plugin.
	 */

	/**
	 * Start the protocol connections.  See Protocol::init for a more
	 * detailed description as to why this separation was decided upon.
	 *
	 * @return Boolean success or failure.
	 */
	BOOL (*start) (struct protocol *p);

	/**
	 * Cleanup everything you have initialized/allocated.  This will
	 * generally mean that your protocol is being unloaded either by a giFT
	 * shutdown or by a simple unload directive to this specific protocol.
	 */
	void (*destroy) (struct protocol *p);

	/**
	 * Begin a new chunk transfer.  Note that the protocol is not allowed to
	 * differentiate between multi-sourced and single-sourced transfers, you
	 * must simply obey what is specified by the \em chunk argument.
	 *
	 * @param p
	 * @param transfer
	 * @param chunk
	 * @param source
	 *
	 * @return Boolean success or failure.
	 */
	BOOL (*download_start) (struct protocol *p,
	                        struct transfer *transfer,
	                        struct chunk *chunk, struct source *source);

	/**
	 * Cancel an active download.  Protocols are expected to cleanup all
	 * sockets and internal data associated with this chunk.  Please note
	 * that OpenFT violates this specification by attempting to use
	 * persisting HTTP connections for transfers.
	 *
	 * @param p
	 * @param transfer
	 * @param chunk
	 * @param source
	 * @param complete Indicates whether or not this chunk completed
	 *                 successfully.  Do not attempt to figure this out
	 *                 on your own using chunk->start/stop as this flag
	 *                 has other considerations.
	 */
	void (*download_stop) (struct protocol *p,
	                       struct transfer *transfer,
	                       struct chunk *chunk, struct source *source,
	                       int complete);

	/**
	 * See Protocol::download_stop.
	 */
	void (*upload_stop) (struct protocol *p,
	                     struct transfer *transfer,
	                     struct chunk *chunk, struct source *source);

	/**
	 * Notify the protocol of upload availability changes.  Protocols
	 * such as OpenFT take advantage of this by delivering to parent search
	 * nodes so that search results for other users can indicate the
	 * availability before download.  Any protocol that has no concept of
	 * upload eligibility/availability, can safely ignore this.
	 *
	 * @param p
	 * @param avail  Total number of available upload positions.
	 */
	void (*upload_avail) (struct protocol *p, unsigned long avail);

	/**
	 * Suspend input/output on the specified chunk until resumed later.  giFT
	 * uses these methods for managing transfer bandwidth usage (user-space
	 * throttling).  Protocols should do their best to implement this.
	 *
	 * @param p
	 * @param transfer
	 * @param chunk
	 * @param source
	 *
	 * @return Boolean success or failure.  Return FALSE if your protocol
	 *         does not support this feature.
	 */
	BOOL (*chunk_suspend) (struct protocol *p,
	                       struct transfer *transfer,
	                       struct chunk *chunk, struct source *source);

	/**
	 * Resume a suspended transfer.  See Protocol::chunk_suspend.
	 */
	BOOL (*chunk_resume) (struct protocol *p,
	                      struct transfer *transfer,
	                      struct chunk *chunk, struct source *source);

	/**
	 * Notify a protocol about a new source being added to a transfer.  This
	 * affects only downloads.
	 *
	 * @return Return FALSE if giFT should abort adding this source,
	 *         effectively ignoring that addsource request.  Please note that
	 *         at the time of this writing, a return value of FALSE is not
	 *         implemented for internal reasons.  If you encounter this to
	 *         still be the case, notify the developers and the feature will
	 *         be finished.
	 */
	BOOL (*source_add) (struct protocol *p,
	                    struct transfer *transfer, struct source *source);

	/**
	 * Remove all activity and pending data for the specified source.  This
	 * method is used when giFT has been instructed to remove a source from
	 * the specified transfer (you may assume that this is a download), or
	 * when a transfer has completed all sources will be removed.
	 */
	void (*source_remove) (struct protocol *p,
	                       struct transfer *transfer, struct source *source);

	/**
	 * Search request.  Please note that Protocol::browse and
	 * Protocol::locate use the same result and completion routines as they
	 * are actually the same in giFT space.
	 *
	 * @param p
	 * @param event   Internal event used for search response.
	 * @param query   Query string.
	 * @param exclude Exclude string.
	 * @param realm   Filtering by group.
	 * @param meta    Extended search information.
	 *
	 * @return If true, the search will be assumed to have succeeded pending
	 *         a response from the protocol at some later time.  Otherwise, the
	 *         search is terminated immediately afterwards and anymore
	 *         replies to this event will be considered invalid.
	 */
	BOOL (*search) (struct protocol *p,
	                struct if_event *event, char *query, char *exclude,
	                char *realm, Dataset *meta);

	/**
	 * Browse an individual users content.  The response to this event uses
	 * the same systems as a search.
	 *
	 * @param p
	 * @param event
	 * @param user  User to browse.  You should expect this in the form you
	 *              return from search results.
	 * @param node  Node/server that reported this users shares, if available.
	 *              If possible, do not rely on this value as it is only
	 *              provided for search results.
	 */
	BOOL (*browse) (struct protocol *p,
	                struct if_event *event, char *user, char *node);

	/**
	 * Locate a file on the network by some unique content.  See
	 * Protocol::browse and Protocol::search.
	 *
	 * @param p
	 * @param event
	 * @param htype  Optional hashing algorithm name.  If NULL, assume
	 *               that the hashing type is whatever you registered using
	 *               HASH_PRIMARY.
	 * @param hash   NUL-terminated ASCII string representing the hash to
	 *               search for.
	 */
	BOOL (*locate) (struct protocol *p,
	                struct if_event *event, char *htype, char *hash);

	/**
	 * Cancel a previously requested search.
	 *
	 * @param p
	 * @param event Internal event delivered by the search request.
	 */
	void (*search_cancel) (struct protocol *p, struct if_event *event);

	/**
	 * Allocate new protocol-specific data to be associated with \em file.
	 *
	 * @return Pointer to protocol-specific dynamically allocated memory.
	 */
	void* (*share_new) (struct protocol *p, struct file_share *file);

	/**
	 * Destroy data returned by Protocol::share_new.
	 */
	void (*share_free) (struct protocol *p, struct file_share *file,
	                    void *data);

	/**
	 * Add a new locally shared file to the network.
	 *
	 * @param p
	 * @param file
	 * @param data  Arbitrary data returned from Protocol::share_new.
	 *
	 * @return Boolean success or failure.
	 */
	BOOL (*share_add) (struct protocol *p, struct file_share *file, void *data);

	/**
	 * Remove a locally shared file from the network.  See Protocol::share_add.
	 */
	BOOL (*share_remove) (struct protocol *p, struct file_share *file,
	                      void *data);

	/**
	 * giFT has begun syncing shares.  Expect Protocol::share_add and
	 * Protocol::share_remove calls until \em begin is FALSE, in which case
	 * the sync has completed.  This is to be considered an effective
	 * modification of the current local shares and should be sent out over
	 * the socket if said protocol requires this.
	 *
	 * @param p
	 * @param begin
	 */
	void (*share_sync) (struct protocol *p, int begin);

	/**
	 * Temporarily unshare all files.  Some protocols (eg OpenFT) implement
	 * this with a single command disabling shares instead of removing them.
	 * To my knowledge, most other protocols will require you actually
	 * unshare every file manually.
	 */
	void (*share_hide) (struct protocol *p);

	/**
	 * Enable sharing after it was previously hidden.
	 */
	void (*share_show) (struct protocol *p);

	/**
	 * Request protocol statistics.
	 *
	 * @param p
	 * @param users  Storage location for the number of users.
	 * @param files  Storage location for the number of files.
	 * @param size   Storage location for the total shared size (GB).
	 * @param extra  Extra information this protocol would like to be
	 *               reported.  Please use sparingly.
	 *
	 * @return Number of connections currently established.  Any value above
	 *         zero will be considered "Online".
	 */
	int (*stats) (struct protocol *p,
	              unsigned long *users, unsigned long *files,
	              double *size, Dataset **extra);

	/*************************************************************************/
	/**
	 * @name Optional Protocol Communication
	 *
	 * Methods which can (and probably should) be specified by each protocol,
	 * but will have sane and useful default implementations specified by
	 * giFT.  Your protocol will not be adversely affected if you leave these
	 * values as is.
	 */

	/**
	 * Compare two sources.  This will be used when selecting how to manage
	 * multiple "similar" sources to a single transfer.  The sources should
	 * compare equal (return value of 0) when it can be determined that they
	 * are the same user sharing the same file.
	 *
	 * @return Identical to strcmp.
	 */
	int (*source_cmp) (struct protocol *p, struct source *a, struct source *b);

	/**
	 * Compare two users.  This was specially added for OpenFT so that node
	 * aliases could be weeded out as a significant portion of the username
	 * field.  Default implementation here is a simple strcmp.
	 */
	int (*user_cmp) (struct protocol *p, const char *a, const char *b);
} Protocol;

/*****************************************************************************/

EXTERN_C_BEGIN

/*****************************************************************************/

/**
 * Eased interface to maintain backwards compatibility and hide the link
 * check.
 */
#define protocol_compat(p,dev_ver) \
	protocol_compat_ex(p,LIBGIFTPROTO_VERSION,dev_ver)

/**
 * Attempts to determine if the communication defined here (libgiftproto) is
 * compatible with the current plugin or giftd binary.  All protocol plugins
 * are expected to call this first in Protocol::init and return FALSE from
 * there if the runtime versions are not compatible.
 *
 * Please note that you should specify the version that you know to be
 * compatible.  That is, the version that you were working from when
 * developing, or the maximum possible compatible version that you know of.
 * Please allow libgiftproto to determine compatibility beyond that point.
 *
 * @param p
 * @param link_ver  Version known at compile time.  Always pass
 *                  ::LIBGIFTPROTO_VERSION.
 * @param dev_ver   Last known compatible libgiftproto version known to the
 *                  author.  Use the macro ::LIBGIFTPROTO_MKVERSION.
 *
 * @return If the return value is less than, equal to, or greater than zero
 *         the version supplied is respectively too low, compatible, or
 *         too great for the current libgiftproto runtime.
 */
LIBGIFTPROTO_EXPORT
  int protocol_compat_ex (Protocol *p, uint32_t link_ver, uint32_t dev_ver);

/*****************************************************************************/

EXTERN_C_END

/*****************************************************************************/

#endif /* __PROTOCOL_H */
