/*-*-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
 */

#ifndef GNUTELLA_HEADER_H
#define GNUTELLA_HEADER_H

#include "guid.h"
#include "connection.h"
//#include <inttypes.h>


/* QHD bits */
#define QHD_PUSH_BIT_MASK 1
#define QHD_BUSY_BIT_MASK 4
#define QHD_OD_SIZE 2


typedef enum {
        GNUTELLA_INIT           = 0x00,
	GNUTELLA_INITRESPONSE   = 0x01,
	GNUTELLA_PUSHREQUEST    = 0x40,
	GNUTELLA_SEARCH         = 0x80,
	GNUTELLA_SEARCHRESPONSE = 0x81
} gnutella_function;


/* Gnutella standard packets conforming to
   protocol specification 0.4.
*/

/* standard header */
struct gnutella_header {
	gchar   guid[GUID_LEN];
	guint8  f;
	guint8  ttl;
	guint8  hops;
	guint32 length;
};
#define GN_HEADER_SIZE 23

#define GNUTELLA_HDR_ATTACH(data, hdr)            \
{                                                 \
	guidcpy(data, hdr->guid);                 \
	data[16] = hdr->f;                        \
        data[17] = hdr->ttl;                      \
        data[18] = hdr->hops;                     \
        memcpy(data+19, &(hdr->length), 4);       \
}

#define GNUTELLA_HDR_GRAB(hdr, data)              \
{                                                 \
	guidcpy(hdr->guid, data);                 \
	hdr->f    = data[16];                     \
	hdr->ttl  = data[17];                     \
	hdr->hops = data[18];                     \
	memcpy(&(hdr->length), data+19, 4);       \
}

/* pong packet */
struct gnutella_pong_header {
	guint16 host_port;
	//guint32 host_ip;
	uint8_t host_ip[16];
	guint32 file_count;
	guint32 files_total_size;
};
//#define GN_PONG_SIZE 14
#define GN_PONG_SIZE 26
#define GNUTELLA_PONG_ATTACH(data, pong)                  \
{                                                         \
	memcpy(data,    &(pong->host_port),        2);    \
	memcpy(data+2,  &(pong->host_ip),         16);    \
	memcpy(data+18, &(pong->file_count),       4);    \
	memcpy(data+22, &(pong->files_total_size), 4);	  \					   }

#define GNUTELLA_PONG_GRAB(pong, data)                    \
{                                                         \
        memcpy(&(pong->host_port),           data, 2);    \
	memcpy(&(pong->host_ip),           data+2,16);    \
	memcpy(&(pong->file_count),        data+18,4);    \
	memcpy(&(pong->files_total_size), data+22, 4);	  \
}

/* the extend query hit descriptor
   introduced by BearShare
*/
struct gnutella_extended_qhd {
	gchar   vendor[4];
	guint8  open_data_size;
	guint16 open_data;
};
#define GN_EXTENDED_QHD_SIZE 7

/* gnutella search packet */
struct gnutella_query_header {
	guint16 min_speed;
	gchar  *name;
};
#define GN_QUERY_SIZE 2
#define GNUTELLA_QUERY_ATTACH(data, query)                \
{                                                         \
	memcpy(data, &(query->min_speed), 2);             \
	strncpy(data+2, query->name,                      \
		strlen(query->name));                     \
}

#define GNUTELLA_QUERY_GRAB(query, data)                  \
{                                                         \
        memcpy(&(query->min_speed), data, 2);             \
        query->name = newtella_malloc(strlen(data+2)+1);  \
        strncpy(query->name, data+2, strlen(data+2));     \
}

struct gnutella_hit {
	guint32 index;
	guint32 size;
	guint8 *name;
};
#define GN_HIT_SIZE 8
#define GNUTELLA_HIT_ATTACH(data, hit)                    \
{                                                         \
	memcpy(data,   &(hit->index), 4);                 \
	memcpy(data+4, &(hit->size),  4);                 \
	strncpy(data+8, hit->name, strlen(hit->name));    \
}
#define GNUTELLA_HIT_GRAB(hit, data)                      \
{                                                         \
	memcpy(&(hit->index), data,   4);                 \
	memcpy(&(hit->size),  data+4, 4);                 \
	hit->name = newtella_malloc(strlen(data+8)+1);    \
	strncpy(hit->name, data+8, strlen(data+8));       \
}

struct gnutella_hits {
	guint8  n;
	guint16 port;
	//guint32 ip;
	uint8_t ip[16];
	guint32 speed;
	GList  *rset;
	struct gnutella_extended_qhd *qhd;
};
//#define GN_HITS_SIZE 11
#define GN_HITS_SIZE 23
#define GNUTELLA_HITS_ATTACH(data, hits)                 \
{                                                        \
	data[0] = hits->n;                               \
	memcpy(data+1, &(hits->port),  2);               \
	memcpy(data+3, &(hits->ip),   16);               \
	memcpy(data+19,&(hits->speed), 4);               \
}
#define GNUTELLA_HITS_GRAB(hits, data)                   \
{                                                        \
	hits->n = data[0];                               \
	memcpy(&(hits->port),  data+1, 2);               \
	memcpy(&(hits->ip),    data+3,16);               \
	memcpy(&(hits->speed), data+19,4);               \
}

struct gnutella_push {
	gchar   guid[GUID_LEN];
	guint32 index;
	//guint32 ip;
	uint8_t ip[16];
	guint16 port;
};
//#define GN_PUSH_SIZE 26
#define GN_PUSH_SIZE 38
#define GNUTELLA_PUSH_ATTACH(data, push)                \
{                                                       \
	guidcpy(data, push->guid);                      \
	memcpy(data+GUID_LEN,   &(push->index), 4);     \
	memcpy(data+GUID_LEN+4, &(push->ip),   16);     \
	memcpy(data+GUID_LEN+20,&(push->port),  2);     \
}
#define GNUTELLA_PUSH_GRAB(push, data)                  \
{                                                       \
	guidcpy(push->guid, data);                      \
	memcpy(&(push->index), data+GUID_LEN,   4);     \
	memcpy(&(push->ip),    data+GUID_LEN+4,16);     \
	memcpy(&(push->port),  data+GUID_LEN+20,2);     \
}

struct squery {
	gchar  *keyword;
	gchar   guid[GUID_LEN];
	gchar   alive;
	guint16 rs;
};

gint gnutella_send_ping(struct newtella_connection *con, gint wake_up);
gint gnutella_send_pong(struct newtella_connection *con, gchar *guid, guint8 ttl);
gint gnutella_send_cached_host(struct newtella_connection *con, struct host_info *host);
gint gnutella_send_query(struct squery *query, struct newtella_connection *con, gint broadcast);
gint gnutella_send_push(struct newtella_connection *dcon);
gint gnutella_send_query_response(struct newtella_connection *con, GList *found_hits, gchar *guid);

gchar *translate_vendor_id(gchar *vid);
gint   rate_result_set(gint rs, gchar *open_data_byte, gchar *vendor);

struct IP {
	uint8_t uints[16];
};

#endif
