/*-*-linux-c-*-*/

/*
 *  gnewtellium - Newtella for Unix
 *  Copyright (C) 2001 Elias Athanasopoulos
 *
 *  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 of the License, 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#include "newtella.h"
#include "file.h"
#include "gui.h"
#include "guid.h"
#include "http.h"
#include "uploads.h"

/* General functions for Upload Connections */

struct file_entry *upload_find_file(guint32 indx)
{
	/* finds a file in the database
	   using its index.
	*/
	struct file_entry *fe;

	GList *iter;

	iter = g_list_first(gl_file_db);
	while (iter) {
		fe = iter->data;
		if (fe->index == indx)
			return fe;

		iter = g_list_next(iter);
	}

	/* if we reach uphere then something
	   _really_ bad has happened. :-(
	*/
	return NULL;
}

gint upload_write(gpointer gdata, gint s, GdkInputCondition cond)
{
        struct newtella_connection *ucon = (struct newtella_connection *)gdata;
	struct gpacket *packet;
	gchar data[1024];
	gint bytes_readed, r;

        if (cond == GDK_INPUT_EXCEPTION) {
		upload_stop(ucon);
		ucon->state = GS_FAILED;
		gui_update_upload(ucon);
		return -1;
        }

	if (g_list_length(ucon->queue)) {

		packet = (struct gpacket *) g_list_last(ucon->queue)->data;

		r = send(ucon->s, packet->data, packet->len, 0);

		if (r != packet->len)
			upload_stop(ucon);
		else
			ucon->state = GS_CONNECTED;

		ucon->queue = g_list_remove(ucon->queue, packet);

		newtella_free(packet->data);
		newtella_free(packet);

		return 1;
	}

	if (!ucon->fi->des)
		return 1;

	bytes_readed = read(ucon->fi->des, &data, 1024);

	if (bytes_readed && send(ucon->s, data, bytes_readed, 0) > 0) {
		ucon->total_sent += bytes_readed;
	} else {
		// Igor: Damit der richtige Wert angezeigt wird!
		upload_stop(ucon);

		if (ucon->total_sent >= ucon->u_file_size){
			ucon->state = GS_COMPLETE;
		}
		else {
			ucon->state = GS_FAILED;
		}

		// Igor: Vormals hier (s.o.)
		//upload_stop(ucon);
        }

	/* gui update */
	gui_update_upload(ucon);

        return 1;
}

gint upload_add(uint8_t *ip, guint16 port, guint32 indx, gchar *guid)
{
	struct http_header *http = newtella_malloc(sizeof(struct http_header));
	struct file_entry *fe = upload_find_file(indx);
	struct newtella_connection *ucon;

	/* file not found */
	if (!fe)
		return -1;

	ucon = newtella_malloc(sizeof(struct newtella_connection));
	bzero(ucon->ip, sizeof(ucon->ip));
	memcpy(ucon->ip, ip, sizeof(ucon->ip));
        ucon->port = port;

	con_zero_stats(ucon);
	con_init_queue(ucon);

	ucon->state = GS_HANDSHAKE;
	ucon->type  = CT_UPLOAD;
	ucon->u_file_size = fe->size;

	// Igor: Neu eingebaut
	if (gl_options->ucons == 0)
		gl_up_con = g_list_append(NULL, ucon);
	else
		gl_up_con = g_list_append(gl_up_con, ucon);

	//gl_up_con = g_list_append(gl_up_con, ucon);

        ucon->s = newtella_force_connect(ip, port);

	ucon->write_tag = gdk_input_add(ucon->s, GDK_INPUT_WRITE | GDK_INPUT_EXCEPTION,
					(GdkInputFunction) newtella_connect, (gpointer)ucon);

	/* build the http msg */
	http->func = HTTP_GIV;
	http->file_index = indx;
	http->file_name = fe->name;
	guidcpy(http->guid, guid);

	con_send_packet(ucon, http_header_build(http),
			strlen(http_header_build(http)), 0);


	return 1;
}

int upload_start(struct newtella_connection *ucon, gchar *buf)
{
	gchar *up_file;
	struct http_header *http        = newtella_malloc(sizeof *http);
	struct http_header *get_request;
	struct file_entry  *fe;

	get_request = http_header_parse(buf);

	//g_print("***Upload\n%s\n", buf);
	/* find the requested file */
	fe = upload_find_file(get_request->file_index);

	ucon->write_tag = gdk_input_add(ucon->s, GDK_INPUT_WRITE | GDK_INPUT_EXCEPTION,
				(GdkInputFunction) upload_write, (gpointer)ucon);
        /* file not found */
	if (!fe) {
		http->func = HTTP_RESPONSE;
		http->code = 404; /* Not Found */
		http->user_agent = GNEWTELLIUM;
		con_send_packet(ucon, http_header_build(http),
				strlen(http_header_build(http)), 0);

		ucon->state = GS_FAILED;

		return -1;
	}

	/* reject upload */
	if (gl_options->current_up_con >= gl_options->max_uploads) {
		http->func = HTTP_RESPONSE;
		http->code = 503; /* Busy */
		http->user_agent = GNEWTELLIUM;
		con_send_packet(ucon, http_header_build(http),
				strlen(http_header_build(http)), 0);

		ucon->state = GS_FAILED;

		return -1;
	}

	// Igor: Hier wurde der falsche Wert mit CT_UPLOAD verglichen (glaub ich jedenfalls :))
	// ORI: if (ucon->state != CT_UPLOAD) {
	if (ucon->type != CT_UPLOAD) {
		/* this is a direct upload connection so
		   it comes from an ordinary one. We have to
		   add it in the upload list and kill the
		   ordinary one.
		*/
		ucon->state = GS_HANDSHAKE;
		ucon->type  = CT_UPLOAD;
		ucon->u_file_size = fe->size;

		if (gl_options->ucons == 0)
		//if (gl_options->dcons == 1)
			gl_up_con = g_list_append(NULL, ucon);
		else
			gl_up_con = g_list_append(gl_up_con, ucon);

		//gl_up_con = g_list_append(gl_up_con, ucon);

		gl_con = g_list_remove(gl_con, ucon);

		gui_remove_con_from_list(ucon);

	}

       	gui_add_upload(ucon, fe->name);

       	up_file = newtella_malloc(strlen(fe->path)+strlen(fe->name)+1);
	up_file = strcpy(up_file, fe->path);
	up_file = strcat(up_file, fe->name);

	ucon->fi = newtella_malloc(sizeof(struct file_found));
	ucon->fi->des = open(up_file, O_RDONLY);
	if (get_request->range_start) {
		lseek(ucon->fi->des, get_request->range_start, SEEK_SET);
		ucon->total_sent = get_request->range_start;
	}

        /* approve upload */
	http->func = HTTP_RESPONSE;
	if (!get_request->range_start) {
		http->code = 200; /* OK */
		http->content_length = fe->size;
	}
	else {
		http->code = 206; /* Resume */
		http->content_length = fe->size - get_request->range_start;
	}


	http->user_agent = GNEWTELLIUM;
	http->file_name = fe->name;

	con_send_packet(ucon, http_header_build(http),
			strlen(http_header_build(http)), 0);

	return 1;
}

int upload_stop(struct newtella_connection *ucon)
{
	if (ucon->write_tag)
		gdk_input_remove(ucon->write_tag);

	//
	if (ucon->read_tag)
		gdk_input_remove(ucon->read_tag);

	//
	close(ucon->s);

	ucon->state = GS_STOPPED;

	gui_update_upload(ucon);

	return 1;
}

int upload_kill(struct newtella_connection *ucon)
{
	if (ucon->state != GS_STOPPED)
		upload_stop(ucon);

	gl_up_con = g_list_remove(gl_up_con, ucon);

	gui_remove_upload(ucon);

	newtella_free(ucon->queue);
	newtella_free(ucon->fi);
	newtella_free(ucon);

	return 1;
}

int upload_active_con(void)
{
	struct newtella_connection *ucon;
	gint ucons = 0;
	GList *iter = g_list_first(gl_up_con);

	while (iter) {
		ucon = iter->data;

		if (ucon->state == GS_CONNECTED)
			ucons++;

		iter = g_list_next(iter);
	}

	return ucons;
}
