#ifndef _NAP_CMDS_H
#define _NAP_CMDS_H

/* Copyright (c) 2000  Kevin Sullivan <nite@gis.net>
 *
 * Please refer to the COPYRIGHT file for more information.
 */

#include <sys/time.h>
#include <netinet/in.h>
#include "scheck.h"

struct out_nap_cmd_s 
{
  const char *nm;
  unsigned char a;
  int (*func)(int, char *, char **, int, WINDOW *);
  const char *help;
};
typedef struct out_nap_cmd_s out_nap_cmd_t;

struct user_s 
{
  char *nm, *addr;
  unsigned char conn;
  unsigned int scount;
  unsigned char flag;
  struct user_s *next;
};
typedef struct user_s user_t;

struct hotlist_s 
{
  char *nm; /* allocated */
  int conn;
  int on;
  struct hotlist_s *next;
};
typedef struct hotlist_s hotlist_t;

/* this is the type of channels as well as queries. A query is a
   (virtual) connection to a single user */
struct chans_s 
{
  char *nm;                   /* name of channel/query - case insensitive */
  char *topic;
  user_t *users;
  unsigned char q;            /* 0: napster channel, 1: query, 2: irc (?) */
  unsigned char flag;         /* ? */
  int l;                      /* ? */
  char *key;                  /* ? */
  unsigned char p;            /* ? */
  struct chans_s *next;
}; 
typedef struct chans_s chans_t;

struct scount_s 
{
  unsigned long libraries, songs, gigs;
};
typedef struct scount_s scount_t;

struct stringlist_s
{
  char *s;
  struct stringlist_s *next;
};
typedef struct stringlist_s stringlist_t;

/* ---------------------------------------------------------------------- */
/* the data structure for search result items. Note: the convention is
   that the items of the search result list are numbered sequentially,
   starting from *1*. However, the list macros start numbering from
   *0*. Thus, careful when looking for item number n. */
struct ssearch_s 
{
  char *song;               /* title with pathname componentes stripped */
  char *rfn;                /* title as sent by server */
  char *nick;               /* nickname of remote user */
  int conn;                 /* connection speed of remote client */
  size_t sz;                /* total size */
  int brate;                /* bitrate (e.g. 128) */
  int freq;                 /* sample frequency (e.g. 44100) */
  int time;                 /* length, in seconds */
  unsigned long nip;        /* ip address of remote client, in network
			       byte order (formerly this field was
			       called ip and was in non-host byte
			       order) */
  int ping;                 /* ping time (in milliseconds). -3 or -2 if none
                               requested, -1 if infinity */
  struct ssearch_s *next;   /* next item in list */
  struct ssearch_s *next1;  /* only used for sorting; need not initialize */
};
typedef struct ssearch_s ssearch_t;

/* ---------------------------------------------------------------------- */
/* the data structure for incomplete-tags: this is a special tag
   appended to an incomplete file, which facilitates resumes. The tag
   has an ASCII form and an internal form. Note the "lfn" and "next"
   fields are not represented in the ASCII tag. */

struct itag_s
{
  int n;               /* number of available hashes (up to 3) */
  char *hash[3];       /* array of hashes */
  char *lfn;           /* path to the incomplete file */
  char *fn;            /* original filename */
  struct itag_s *next; /* linked lists */
};
typedef struct itag_s itag_t;

/* ---------------------------------------------------------------------- */
/* The task lists are two of the three "backbone" data structures of
   nap (the remaining one is the connection list, type sock_t, see
   scheck.h). A "task" is an upload or a download. Tasks are kept in
   two global linked lists, "up" and "down".

   The following is my attempt at a specification of what these data
   structures represent, and how they are updated in reaction to user
   events, server events, and timeouts.

   A task can be in several different states. For download tasks,
   these are:
   
   QUEUED: requested by the user, but not yet requested from server.
   RQUEUED: "remotely queued". Try to re-request later.
   RRQUEUED: "remotely queued" and requested. 
   REQUESTED: requested from server, but not yet acknowledged by server.
   WAITING: waiting for remote client to contact us.
   CONNECTING: contacted remote client and waiting for a response.
   IN PROGRESS: reading file from the remote client.
   COMPLETE: file successfully received.
   INCOMPLETE: we were disconnected after receiving a partial file.
   FAILED: error before receiving file (e.g. it did not exist)
   TIMED_OUT: waited too long for server or remote client.

   For upload tasks, the possible states are very similar:

   WAITING: waiting for remote client to contact us.
   CONNECTING: contacted remote client and waiting for a response.
   IN PROGRESS: sending file to the remote client.
   COMPLETE: file successfully sent.
   INCOMPLETE: disconnected after sending partial file.
   FAILED: error before sending file (e.g. it did not exist)
   TIMED_OUT: waited too long for server or remote client.

   Note that there are no QUEUED, RQUEUED, RRQUEUED or REQUESTED
   states for uploads, since uploads are always initiated by the
   server (we do not queue uploads).

   Not all of the above states need to be explicitly represented in
   the data structures. For instance, one may choose to implement
   COMPLETE, INCOMPLETE, FAILED, and TIMED_OUT simply by deleting the
   task. On the other hand, a clever client might want to try to
   something smart with an INCOMPLETE, for instance, try to resume it. 

   Much of the napster protocol has to do with what messages to expect
   and send in each state, and how to change states. The following
   model explains how to react to each condition in each state.
   This could be represented as a flow graph, but that's difficult to
   do in ASCII. 

   -------------------------------------------------------------------

   1. Downloads.

   [no state]: user download request (if limit exceeded) -> create
   task -> QUEUED

   [no state]: user download request (if limit ok, and there is not
   already an R*QUEUED item by this nick) -> create task -> send 203
   (download request) to server -> REQUESTED

   [no state]: user download request (if limit ok, and there is
   already an R*QUEUED item by this nick) -> create task -> RQUEUED
   (with timer set to retry this item).

   QUEUED: (when a slot becomes available) -> send 203 (download
   request) to server -> REQUESTED

   RQUEUED: (when timer due and slot available) -> send 203 (download
   request) to server -> RRQUEUED

   REQUESTED or RRQUEUED: receive 206 (get error) from server -> FAILED

   REQUESTED or RRQUEUED: receive 609 (accept failure) from server -> FAILED

   REQUESTED or RRQUEUED: receive 620 (queue limit) from server (if
   remote upload limit >= 1) -> RQUEUED (with timer set to retry this
   item).

   REQUESTED or RRQUEUED: receive 620 (queue limit) from server (if
   remote upload limit <= 0) -> FAILED

   REQUESTED or RRQUEUED: receive 204 (download ack) from server (if
   remote party is firewalled) -> send 500 (alt download request) to
   server -> WAITING

   REQUESTED or RRQUEUED: receive 204 (download ack) from server (if
   remote party is not firewalled) -> contact remote client ->
   CONNECTING

   REQUESTED: (timed out) -> resend 203 to server -> REQUESTED

   RRQUEUED: (timed out) -> resend 203 to server -> RRQUEUED

   WAITING: remote client connects to us (if negotiation fails) -> FAILED

   WAITING: remote client connects to us (if negotiation succeeds) ->
   send 218 (downloading) to sever -> IN PROGRESS

   WAITING: (timed out) -> TIMED_OUT

   CONNECTING: remote client responds (if negotiation fails) -> FAILED

   CONNECTING: remote client responds (if negotiation succeeds) -> 
   send 218 (downloading) to sever -> IN PROGRESS

   CONNECTING: (timed out) -> TIMED_OUT

   IN PROGRESS: receive data -> IN PROGRESS

   IN PROGRESS: (transfer complete) -> send 219 (download complete) to
   server -> COMPLETE

   IN PROGRESS: (transfer interrupted) -> send 219 (download complete) to
   server -> INCOMPLETE

   FAILED or COMPLETE or INCOMPLETE or TIMED_OUT: -> purge/delete -> [no state]

   -------------------------------------------------------------------

   2. Uploads.

   [no state]: receive 607 (upload request) from server (if we don't
   have the file, or /tquit is currently in effect) -> send 608
   (accept upload request) to server -> [no state]

   (Note: there is no "reject upload request" packet!)

   [no state]: receive 607 (upload request) from server (if upload
   limit exceeded) -> send 619 (queue limit) to server -> [no state]

   [no state]: receive 607 (upload request) from server (otherwise) ->
   create task -> send 608 (accept upload request) to server -> WAITING

   WAITING: remote client connects to us (if negotiation fails) -> FAILED

   WAITING: remote client connects to us (if negotiation succeeds) ->
   send 220 (uploading) to server and go to IN PROGRESS

   WAITING: receive 501 (alt download ack or "push request") from
   server -> contact remote client -> CONNECTING

   WAITING: (timed out) -> TIMED_OUT

   (Note: apparently, 501 is only sent after 607. But we may also
   choose to accept 501 in [no state] - are not currently doing so,
   however.)

   CONNECTING: remote client responds (if negotiation fails) -> FAILED

   (Note: there might be no harm in allowing an upload of a shared
   file even if it does not seem to have been initiated by the server,
   *provided* that we have space available. Usually this happens if a
   remote client sent an upload request, we received it, then we quit
   and restarted. The remote client will now try to connect to us, but
   we have erased our upload list!)

   CONNECTING: remote client responds (if negotiation succeeds) ->
   CONNECTING1 -> (continue negotiation) -> send 220 (uploading) to
   server -> IN PROGRESS

   CONNECTING: (timed out) -> TIMED_OUT

   IN PROGRESS: send data -> IN PROGRESS

   IN PROGRESS: (transfer complete) -> send 221 (upload complete) to
   server -> COMPLETE

   IN PROGRESS: (disconnected) -> send 221 (upload complete) to server
   -> INCOMPLETE

   FAILED, COMPLETE, INCOMPLETE, TIMEOUT: -> purge -> [no state]

   -------------------------------------------------------------------

   Some states may split into even more sub-states, for instance,
   CONNECTING usually involves first sending a GET and then waiting
   one more time for the remote client's response. This is implemented
   by remaining in the same state, but updating the (sock_t) "func"
   entry.

*/

#define QUEUED 0
#define RQUEUED 1
#define IN_PROGRESS 2
#define RRQUEUED 3
#define REQUESTED 4
#define WAITING 5
#define CONNECTING 6
#define CONNECTING1 7
#define FAILED -1
#define INCOMPLETE -2
#define COMPLETE -3
#define TIMED_OUT -4

#define STOPPED(x) ((x)<0)    /* FAILED, INCOMPLETE, COMPLETE, TIMED_OUT */
#define ACTIVE(x) ((x)>=2)    /* not STOPPED or QUEUED or RQUEUED */
#define TIMED(x) ((x)>=3)     /* RRQUEUED, REQUESTED, WAITING, CONNECTING(1) */

/* note: in the following, if a certain field is marked "in state
   xxx", then this means the field's value is undefined in any other
   state. In particular, such fields need not be (and are not usually)
   initialized outside their own state. All functions that update the
   state MUST define and undefine such fields as appropriate; in
   particular, if a field becomes undefined as the result of a state
   change, but contains an allocated value (such as a string), then
   that value MUST be deallocated at that time. */

/* The "new" task structure for uploads */

/* note: CONNECTING1 is identical to CONNECTING, except that the local
   file and file size have already been filled in */

struct upload_s
{
  int state;                   /* WAITING, CONNECTING, CONNECTING1,
				  IN_PROGRESS, FAILED, INCOMPLETE,
				  COMPELTE, TIMED_OUT */
  
  /* in all states: */
  char *nick;                  /* nickname of remote user */
  char *rfn;                   /* remote filename; the pair nick/rfn
                                  must be unique among non-stopped
                                  uploads */
  char *fn;                    /* filename without directory components */
  char *lfn;                   /* local filename to read from */
  int linespeed;               /* remote client's connection speed */    
  
  /* in states WAITING, CONNECTING, and CONNECTING1 */
  time_t c_time;               /* time state entered */

  /* in states CONNECTING, CONNECTING1, and IN_PROGRESS */
  struct sock_s *sk;           /* this task's client-client connection */

  /* in states CONNECTING1 and IN_PROGRESS */
  FILE *f;                     /* local file to read from */
  size_t size;                 /* total file size */

  /* in state IN_PROGRESS */
  size_t bsz;                  /* first byte requested */
  size_t pos;                  /* position of next byte to send */
  time_t p_time;               /* time IN_PROGRESS started */

  /* in states COMPLETE, INCOMPLETE, FAILED, and TIMED_OUT (STOPPED): */
  time_t d_time;               /* time STOPPED state was entered */

  int tmp;                     /* used locally by ddup() */
  struct upload_s *next;
};
typedef struct upload_s upload_t;

/* The "new" task structure for downloads. */

/* note: even though c_time, p_time, r_time, and d_time are mutually
   exclusive, I resisted the temptation to merge them to a single
   field.  This makes debugging easier. Also note: states REQUESTED
   and RRQUEUED are almost identical, except in the amount of
   verbosity they produce. We don't keep informing the user about
   re-requests for a remotely queued item. */

struct download_s
{
  int state;                   /* QUEUED, RQUEUED, RRQUEUED, REQUESTED, 
				  WAITING, CONNECTING, IN_PROGRESS,
                                  FAILED, INCOMPLETE, COMPLETE,
                                  TIMED_OUT */
  
  /* in all states: */
  char *nick;                  /* nickname of remote user */
  char *rfn;                   /* remote filename.  The pair
				  (nick,rfn) must not occur more than
				  once (for a non-stopped item) in the
				  download list, since this is how the
				  server refers to a particular
				  download. */
  char *fn;                    /* filename without directory
				  components */

  /* in state RQUEUED: */
  time_t r_time;               /* time this item was RQUEUED */

  /* in states REQUESTED, RRQUEUED, WAITING, CONNECTING */
  time_t c_time;               /* time REQUESTED or RRQUEUED state was
                                  entered. */

  /* in states WAITING, CONNECTING, IN PROGRESS: */
  struct sockaddr_in addr;     /* remote IP and port (see ip(4)) */
  char *check;                 /* remote client's MD5 hash (or NULL) */
  int linespeed;               /* remote client's connection speed */

  /* in states CONNECTING and IN PROGRESS */
  struct sock_s *sk;           /* connection for client-to-client transfer */

  /* in state IN PROGRESS (filled during negotiation) */
  char *lfn;                   /* local filename to write to */
  FILE *f;                     /* local file to write to */
  size_t size;                 /* file size as reported by remote client */
  size_t bsz;                  /* first byte to get */
  size_t pos;                  /* position of next byte to get
                                  (current size of local file) */
  time_t p_time;               /* time when download started */

  /* in states COMPLETE, INCOMPLETE, FAILED, and TIMED_OUT (STOPPED): */
  time_t d_time;               /* time STOPPED state was entered */

  int tmp;                     /* used locally by dddown() */
  struct download_s *next;
};
typedef struct download_s download_t;

/* ---------------------------------------------------------------------- */
/* struct to hold state for an incoming direct browse connection (i.e. one 
 * requested by us). This is similar to a download task, but because we only 
 * allow one direct browse request at a time, we use a single global variable
 * called directbrowse (initialized in cmds.c) to store the state for the
 * current request. (In comparison, download task structures are kept in a
 * linked list.) */ 
struct inbrowse_s {
  int state;      /* States: REQUESTED, CONNECTING, IN_PROGRESS, TIMED_OUT,
                     COMPLETE, FAILED. */
  struct sock_s *sk;
  time_t reqtime; /* time direct browse request was sent */
  char *nick;     /* nick of remote client we want to browse */
  FILE *f;        /* stream associated with socket's file descriptor */
};
typedef struct inbrowse_s inbrowse_t;

/* ---------------------------------------------------------------------- */
/* struct to hold state for an outgoing direct browse connection (i.e. 
 * requested by a remote client). The sock_t for the connection keeps a
 * pointer to this structure. This is similar to an upload task. However, 
 * we don't create the structure upon accepting a direct browse request,
 * because there is no way to associate an incoming GETLIST command with the
 * nick of the requester. (For uploads, the remote client identifies itself
 * in the GET command, so an upload task in the WAITING state can be linked to
 * the connection's sock_t structure.) 
 * The outbrowse_t structure is created when the connection is already in the
 * IN_PROGRESS state, so we don't bother storing the actual state name at all */
struct outbrowse_s {
  FILE *g; /* stream associated with the shared library file 
              (each connection will be at a different position in the file) */
  /* may need more fields later... */
};
typedef struct outbrowse_s outbrowse_t;

#define O_NAP_FUNC(x) int (x)(int s, char *str, char **tok, int num, WINDOW *win)

O_NAP_FUNC(ddebug);
O_NAP_FUNC(djoin);
O_NAP_FUNC(dpart);
O_NAP_FUNC(dquit);
O_NAP_FUNC(dtell);
O_NAP_FUNC(dwhois);
O_NAP_FUNC(dfup);
O_NAP_FUNC(dfdown);
O_NAP_FUNC(dpup);
O_NAP_FUNC(dpdown);
O_NAP_FUNC(dsearch);
O_NAP_FUNC(dpvars);
O_NAP_FUNC(dkill);
O_NAP_FUNC(dban);
O_NAP_FUNC(dunban);
O_NAP_FUNC(ddatap);
O_NAP_FUNC(dtopic);
O_NAP_FUNC(dlevel);
O_NAP_FUNC(dg);
O_NAP_FUNC(dforce);
O_NAP_FUNC(dretry);
O_NAP_FUNC(dretryall);
O_NAP_FUNC(dopsay);
O_NAP_FUNC(ddns);
O_NAP_FUNC(dannounce);
O_NAP_FUNC(dpchans);
O_NAP_FUNC(dbanlist);
O_NAP_FUNC(dlspeed);
O_NAP_FUNC(ddup);
O_NAP_FUNC(dddown);
O_NAP_FUNC(dpurge);
O_NAP_FUNC(dpurgedown);
O_NAP_FUNC(dpurgeup);
O_NAP_FUNC(dpsocks);
O_NAP_FUNC(dmuzzle);
O_NAP_FUNC(dunmuzzle);
O_NAP_FUNC(ddisconnect);
O_NAP_FUNC(dreconnect);
O_NAP_FUNC(dbrowse);
O_NAP_FUNC(dbrowse2);
O_NAP_FUNC(dhelp);
O_NAP_FUNC(dping);
O_NAP_FUNC(duserpass);
O_NAP_FUNC(dreloadconf);
O_NAP_FUNC(dsver);
O_NAP_FUNC(dsetconf);
O_NAP_FUNC(dchanclear);
O_NAP_FUNC(dsetlevel);
O_NAP_FUNC(dme);
O_NAP_FUNC(dusers);
O_NAP_FUNC(dgusers);
O_NAP_FUNC(dclist);
O_NAP_FUNC(dclist2);
O_NAP_FUNC(dclear);
O_NAP_FUNC(dnews);
O_NAP_FUNC(dsraw);
O_NAP_FUNC(dquery);
O_NAP_FUNC(dcloak);
O_NAP_FUNC(dblocklist);
O_NAP_FUNC(dblock);
O_NAP_FUNC(dunblock);
O_NAP_FUNC(dwindow);
O_NAP_FUNC(dignore);
O_NAP_FUNC(dunignore);
O_NAP_FUNC(dignoreclear);
O_NAP_FUNC(dignorelist);
O_NAP_FUNC(dalias);
O_NAP_FUNC(dunalias);
O_NAP_FUNC(daliaslist);
O_NAP_FUNC(dhandler);
O_NAP_FUNC(dunhandler);
O_NAP_FUNC(dhandlerlist);
O_NAP_FUNC(dcbanlist);
O_NAP_FUNC(dcban);
O_NAP_FUNC(dcunban);
O_NAP_FUNC(dcbanclear);
O_NAP_FUNC(dkick);
O_NAP_FUNC(dloadalias);
O_NAP_FUNC(dsavealias);
O_NAP_FUNC(dclearalias);
O_NAP_FUNC(dloadhandler);
O_NAP_FUNC(dsavehandler);
O_NAP_FUNC(dclearhandler);
O_NAP_FUNC(dwstats);
O_NAP_FUNC(dnotify);
O_NAP_FUNC(dunnotify);
O_NAP_FUNC(dsay);
O_NAP_FUNC(dtquit);
O_NAP_FUNC(dunquit);
O_NAP_FUNC(dabout);
O_NAP_FUNC(dirc);
O_NAP_FUNC(dkickall);
O_NAP_FUNC(deval);
O_NAP_FUNC(dset);
O_NAP_FUNC(dunset);
O_NAP_FUNC(dsaveconfig);
O_NAP_FUNC(dloadconfig);
O_NAP_FUNC(dsavechannels);
O_NAP_FUNC(dloadchannels);
O_NAP_FUNC(decho);
O_NAP_FUNC(dresults);
O_NAP_FUNC(ddlul);
O_NAP_FUNC(dexec);
O_NAP_FUNC(dtimer);
O_NAP_FUNC(drepeat);
O_NAP_FUNC(ddtimer);
O_NAP_FUNC(dif);
O_NAP_FUNC(ddone);
O_NAP_FUNC(dwhile);
O_NAP_FUNC(dinc);
O_NAP_FUNC(ddec);
O_NAP_FUNC(dbreak);
O_NAP_FUNC(dlastlog);
O_NAP_FUNC(drebuild);
O_NAP_FUNC(dupdate);
O_NAP_FUNC(dchupload);
O_NAP_FUNC(dtlist);
O_NAP_FUNC(dnoprint);
O_NAP_FUNC(dstop);
O_NAP_FUNC(dserver);
O_NAP_FUNC(dgetservers);
#ifdef MEMWATCH
O_NAP_FUNC(dmemwatch);
#endif

char *fixquotes(char *s);
void freechan(chans_t *c);
void nextchan();
void delchan(chans_t *c);
char **fxv(char **, int, int *);
int parseout(int, char *, WINDOW *);
int parseoutn(int, char *, WINDOW *);
char *gnum(int);
char *cstr(char *, int);
char *cspstr(char **, int, int);
int dupload(upload_t *);
int ddownload(WINDOW *win, download_t *task);
int interrupt_download(WINDOW *win, download_t *task);
int move_to_dir(WINDOW *win, download_t *task, const char *dir, char **lfn);
void move_to_downloaddir(WINDOW *win, download_t *task);
void mark_incomplete(WINDOW *win, download_t *task);
void tagincompletefile(char *fn, itag_t *itag);
void adduser(chans_t *, char *, int, unsigned char);
void deluser(chans_t *, char *);
user_t *finduser(chans_t *, char *);
void timed_dup(void *data);
void timed_ddown(void *data);
void timed_command(void *data);
int requestdownload(int s, ssearch_t *sitem);
int retrydownload(int s, download_t *dl);
int forcedownload(int s, download_t *dl);
int unused_filename(const char *path, const char *fn, const char *suffix, char **lfn, char **newfn);
int opendownloadfile(char *fn, FILE **f, char **lfn);
void qevent(void);
int parse_range(char *range);
int download_limit_reached(char *nick);
int upload_limit_reached(char *nick);
int purgedown(WINDOW *win);
int purgeup(void);
void rqueued_hook(char *nick);
int save_hotlist(WINDOW *win);
int metaserver(char *url, int timeout, const char **errmsg);
FILE *open_url(const char *url, int timeout, const char **errmsg);
char *format_time(time_t t);
int parse_time(char *str);

#endif /* _NAP_CMDS_H */

