/* Copyright (C) 2000-1 drscholl@users.sourceforge.net
   This is free software distributed under the terms of the
   GNU Public License.  See the file COPYING for details.

   $Id: download.c,v 1.56 2001/02/15 08:39:45 drscholl Exp $ */

#include <string.h>
#include "opennap.h"
#include "debug.h"

/* 203 [ :<sender> ] <nick> "<filename>" */
/* 500 [ :<sender> ] <nick> "<filename>" */
/* handle client request for download of a file */
HANDLER (download)
{
    char   *av[2], *sender_name;
    USER   *user, *sender;
    DATUM  *info = 0;
    int     ac = -1;

    (void) len;
    ASSERT (validate_connection (con));
    if (pop_user_server (con, tag, &pkt, &sender_name, &sender))
	return;
    if (pkt)
	ac = split_line (av, sizeof (av) / sizeof (char *), pkt);

    if (ac < 2)
    {
	unparsable (con);
	return;
    }

    /* find the user to download from */
    user = hash_lookup (Users, av[0]);
    if (!user)
    {
	send_user (sender, MSG_SERVER_SEND_ERROR, "%s \"%s\"", av[0], av[1]);
	return;
    }

    /* if the user holding the requested file is local... */
    if (ISUSER (user->con))
    {
	if (is_ignoring (user->con->uopt->ignore, sender->nick))
	{
	    send_user (sender, MSG_SERVER_NOSUCH, "%s is ignoring you",
		       user->nick);
	    return;
	}

	/* check to make sure the user is actually sharing this file */
	info = hash_lookup (user->con->uopt->files, av[1]);
	if (!info)
	{
	    send_user (sender, MSG_SERVER_SEND_ERROR, "%s \"%s\"",
		       user->nick, av[1]);
	    return;
	}
    }

    if (tag == MSG_CLIENT_DOWNLOAD)
    {
	if (user->port == 0)
	{
	    /* uploader is firewalled, send file info so that downloader can
	       send the 500 request */
	    if (ISUSER (user->con))
	    {
		ASSERT (info != 0);
		send_user (sender, MSG_SERVER_FILE_READY /* 204 */ ,
			   "%s %u %hu \"%s\" %s %d", user->nick,
			   user->ip, user->port, av[1],
#if RESUME
			   info->hash,
#else
			   "00000000000000000000000000000000",
#endif
			   user->speed);
	    }
	    else
	    {
		/* not a local user, we have to relay this request since we
		   dont' have the file information local.  route it directly
		   to the server we know this user is behind. */
		ASSERT (ISSERVER (user->con));
		send_cmd (user->con, tag, ":%s %s \"%s\"", sender->nick,
			  user->nick, av[1]);
	    }
	    return;
	}
    }
    else
    {
	ASSERT (tag == MSG_CLIENT_DOWNLOAD_FIREWALL);
	if (user->port != 0)
	{
	    /* this user is not firewalled */
	    send_user (sender, MSG_SERVER_NOSUCH, "%s is not firewalled",
		       user->nick);
	    return;
	}
	if (sender->port == 0)
	{
	    /* error, both clients are firewalled */
	    ASSERT (ISUSER (con));
	    send_cmd (con, MSG_SERVER_FILE_READY /* 204 */ ,
		      "%s %u %hu \"%s\" firewallerror %d",
		      user->nick, user->ip, user->port, av[1], user->speed);
	    return;
	}
    }

    /* if the client holding the file is a local user, send the request
       directly */
    if (ISUSER (user->con))
    {
	send_cmd (user->con, MSG_SERVER_UPLOAD_REQUEST, "%s \"%s\"",
		  sender->nick, av[1]);
    }
    /* otherwise pass it to the peer servers for delivery */
    else
    {
	/* don't use direct delivery here because the server the client is
	   connected to needs to consult their db and rewrite this messsage */
	send_cmd (user->con, MSG_SERVER_UPLOAD_REQUEST, ":%s %s \"%s\"",
		  sender->nick, user->nick, av[1]);
    }
}

static USER *
transfer_count_wrapper (CONNECTION * con, char *pkt, int numeric)
{
    USER   *user;

    ASSERT (validate_connection (con));
    if (pop_user (con, &pkt, &user))
	return 0;
    /* relay to peer servers */
    pass_message_args (con, numeric, ":%s", user->nick);
    return user;
}

/* 220 [ :<user> ] */
HANDLER (upload_start)
{
    USER   *user;

    (void) tag;
    (void) len;
    ASSERT (validate_connection (con));
    user = transfer_count_wrapper (con, pkt, MSG_CLIENT_UPLOAD_START);
    if (!user)
	return;
    ASSERT (validate_user (user));
    user->uploads++;
    user->totalup++;
}

/* 221 [ :<user> ] */
HANDLER (upload_end)
{
    USER   *user;

    (void) tag;
    (void) len;
    ASSERT (validate_connection (con));
    user = transfer_count_wrapper (con, pkt, MSG_CLIENT_UPLOAD_END);
    if (!user)
	return;
    ASSERT (validate_user (user));
    if (user->uploads > 0)
	user->uploads--;
}

/* 218 [ :<user> ] */
HANDLER (download_start)
{
    USER   *user;

    (void) tag;
    (void) len;
    ASSERT (validate_connection (con));
    user = transfer_count_wrapper (con, pkt, MSG_CLIENT_DOWNLOAD_START);
    if (!user)
	return;
    ASSERT (validate_user (user));
    user->downloads++;
    user->totaldown++;
}

/* 219 [ :<user> ] */
HANDLER (download_end)
{
    USER   *user;

    (void) tag;
    (void) len;
    ASSERT (validate_connection (con));
    user = transfer_count_wrapper (con, pkt, MSG_CLIENT_DOWNLOAD_END);
    if (!user)
	return;
    ASSERT (validate_user (user));
    if (user->downloads > 0)
	user->downloads--;
}

/* 600 <user> */
/* client is requesting the link speed of <user> */
HANDLER (user_speed)
{
    USER   *user;

    (void) tag;
    (void) len;
    CHECK_USER_CLASS ("user_speed");
    user = hash_lookup (Users, pkt);
    if (!user)
    {
	nosuchuser (con);
	return;
    }
    ASSERT (validate_user (user));
    send_cmd (con, MSG_SERVER_USER_SPEED /* 601 */ , "%s %d",
	      user->nick, user->speed);
}

/* 626 [ :<nick> ] <user> */
/* client is notifying other party of a failure to connect to their data
   port */
HANDLER (data_port_error)
{
    USER   *sender, *user;

    (void) tag;
    (void) len;

    ASSERT (validate_connection (con));
    if (pop_user (con, &pkt, &sender) != 0)
	return;
    ASSERT (validate_user (sender));
    user = hash_lookup (Users, pkt);
    if (!user)
    {
	nosuchuser (con);
	return;
    }
    ASSERT (validate_user (user));

    /* we pass this message to all servers so the mods can see it */
    pass_message_args (con, tag, ":%s %s", sender->nick, user->nick);

    notify_mods
	(PORTLOG_MODE,
	 "Notification from %s: %s (%s) - configured data port %hu is unreachable.",
	 sender->nick, user->nick, my_ntoa (BSWAP32 (user->ip)), user->port);

    /* if local, notify the target of the error */
    if (user->local)
	send_cmd (user->con, tag, "%s", sender->nick);
}

/* 607 :<sender> <recip> "<filename>" [speed] */
HANDLER (upload_request)
{
    char   *av[3], *sender_name;
    USER   *recip, *sender;
    int     ac = -1;

    (void) tag;
    (void) len;

    ASSERT (validate_connection (con));
    CHECK_SERVER_CLASS ("upload_request");
    if (pop_user_server (con, tag, &pkt, &sender_name, &sender))
	return;
    if (pkt)
	ac = split_line (av, sizeof (av) / sizeof (char *), pkt);

    if (ac < 2)
    {
	log ("upload_request: too few args");
	return;
    }
    recip = hash_lookup (Users, av[0]);
    if (!recip)
    {
	log ("upload_request: %s: no such user", av[0]);
	return;
    }
    ASSERT (validate_user (recip));

    /* if local user, deliver the message */
    if (ISUSER (recip->con))
    {
	/* make sure the user is actually sharing this file */
	DATUM  *info = hash_lookup (recip->con->uopt->files, av[1]);

	if (!info)
	{
	    log ("upload_request: %s is not sharing %s", recip->nick, av[1]);
	    return;
	}
	send_cmd (recip->con, MSG_SERVER_UPLOAD_REQUEST /* 607 */ ,
		  "%s \"%s\" %d", sender->nick, av[1], sender->speed);
    }
    else
    {
	/* route the request to the server which the uploader is behind */
	send_cmd (recip->con, MSG_SERVER_UPLOAD_REQUEST,
		  ":%s %s \"%s\" %d", sender->nick, recip->nick, av[1],
		  sender->speed);
    }
}

/* 619 <nick> <filename> <limit> */
HANDLER (queue_limit)
{
    char   *av[3];
    int     ac;
    USER   *recip;
    DATUM  *info;

    (void) tag;
    (void) len;
    ASSERT (validate_connection (con));
    CHECK_USER_CLASS ("queue_limit");
    ac = split_line (av, sizeof (av) / sizeof (char *), pkt);

    if (ac != 3)
    {
	log ("queue_limit(): wrong number of parameters");
	print_args (ac, av);
	unparsable (con);
	return;
    }
    recip = hash_lookup (Users, av[0]);
    if (!recip)
    {
	nosuchuser (con);
	return;
    }
    ASSERT (validate_user (recip));
    ASSERT (validate_connection (recip->con));

    /* look up the filesize in the db */
    info = hash_lookup (con->uopt->files, av[1]);
    if (!info)
    {
	send_cmd (con, MSG_SERVER_NOSUCH, "Not sharing that file");
	return;
    }

    /* deliver to user even if remote */
    send_user (recip, MSG_SERVER_LIMIT, "%s \"%s\" %u %s",
	       con->user->nick, av[1], info->size, av[2]);
}
