/* Josh Pieper, (c) 2000 */

/* This file is distributed under the GPL, see file COPYING for details. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "conf.h"
#include "gnut_lib.h"
#include "Gnut_List.h"
#include "protocol.h"
#include "share.h"

void free_gpa(gnutella_packet **x, int bugnum)
{
  yfree((void **) x, bugnum);
}

/* int gp_print(gnutella_packet *gpa)
 *
 * writes to standard out a textual version of the gnutella_packet gpa */
int gp_print(gnutella_packet *gpa)
{
  /*  gnutella_results *gr; */
  gnutella_servent *gm;
  /*  gnutella_results_name *grn; */
  /*  gnutella_results_suffix *grs; */
  gnutella_filereq *gf;
  uchar *buf=NULL;
  uint32 dlen;
  uint32 t,t2;
  uint16 st;
  int i;
  
  memcpy(&dlen,gpa->gh.dlen,4);
  dlen=GUINT32_FROM_LE(dlen);
  
  fprintf(stderr,"GUID: ");
  for (i=0;i<16;i++) fprintf(stderr,"%02X",gpa->gh.guid[i]);
  fprintf(stderr,"\n func: %i  ttl: %i  hops: %i  dlen:  %i\n",
    gpa->gh.func,gpa->gh.ttl,gpa->gh.hops,dlen);
    
  if (dlen>0) {
  switch(gpa->gh.func) {
  case 0:
    break;
  case 1:
    gm=(gnutella_servent *) gpa->data;
    
    memcpy(&t,gm->files,4);
    t=GUINT32_FROM_LE(t);
    
    memcpy(&t2,gm->bytes,4);
    t2=GUINT32_FROM_LE(t2);
    
    memcpy(&st,gm->port,2);
    st=GUINT16_FROM_LE(st);
    
    fprintf(stderr," Servent Descriptor: %i.%i.%i.%i:%i files: %i  bytes: %ik\n",
      gm->ip[0],gm->ip[1],gm->ip[2],gm->ip[3],st,t,t2);
    break;
  case 64:
    if (dlen<sizeof(gnutella_filereq)) {
      gd_s(2,"bad push request!\n");
      break;
    }
    gf=(gnutella_filereq *) gpa->data;
    fprintf(stderr," File Request: GUID: ");
    for (i=0;i<16;i++) fprintf(stderr,"%02x",gf->guid[i]);
    
    fprintf(stderr,"\n");
    
    memcpy(&st,gf->port,2);
    st=GUINT16_FROM_LE(st);
    
    
    memcpy(&t,gf->ref,4);
    t=GUINT32_FROM_LE(t);
    
    fprintf(stderr,"  ref: %i  ip: %i.%i.%i.%i:%i\n\n",t,gf->ip[0],gf->ip[1],
      gf->ip[2],gf->ip[3],st);
    
    break;
  case 128:
    if  (dlen>2) {
      buf=gpa->data;
      fprintf(stderr," Search query: %s\n",&buf[2]);
    }
    break;
  default:
    if (dlen>1000) {
      gd_s(3,"capping debug dump at 1000 bytes\n");
      dlen=1000;
    }
    buf=gpa->data;
    fprintf(stderr," Unknown: func: %i  dump\n",gpa->gh.func);
    for (i=0;i<dlen;i++) fprintf(stderr,"%02x ",buf[i]);
    fprintf(stderr,"\n ");
    for (i=0;i<dlen;i++) if (isprint(buf[i])) fprintf(stderr,"%c",buf[i]);
    fprintf(stderr,"\n");
    break;
  }
  }
  fprintf(stderr,"\n");    
  return 0;
}

/* gnutella_packet * gp_reply_make(gchar *guid, GSList *results,
 *      guchar ip[4], guint16 port, guint speed, gchar *mguid, gint ttl)
 *
 * Makes a query reply packet based on the results in the GSList
 * results.  Returns the packet. */
gnutella_packet * gp_reply_make(char *guid, Gnut_List *results,
      uchar *ip, uint16 port, uint32 speed, char *mguid, int ttl)
{
  gnutella_packet * gpa;
  gnutella_results *gr;
  gnutella_results_name *grn;
  gnutella_results_suffix *grs;
  Gnut_List *ltemp;
  char *name;
  share_item *si;
  uint16 st;
  uint32 dlen;
  int num;
  uint32 it;
  
  gd_s(3,"started\n");
  
  gpa=(gnutella_packet *) xmalloc(sizeof(gnutella_packet));
  
  /* first lets fill in the header info */
  
  memcpy(gpa->gh.guid,guid,16);
  gpa->gh.func=129;
  gpa->gh.ttl=ttl;
  gpa->gh.hops=0;

  /* Calculate the length we need to allocate. This requires counting the
   * length of all the result filenames */
  dlen=sizeof(gnutella_results);
  for (num=0,ltemp=results; ltemp; ltemp=gnut_list_next(ltemp)) {
    si=ltemp->data;
    dlen+=sizeof(share_item);

	name = si->path;
#ifndef WIN32
    if (gc_hide_pathname) {
	  name = strrchr(si->path, '/');
	  if (name) {
		name++;
	  } else {
		name = si->path;
	  }
	}
#endif /* WIN32 */

    dlen += strlen(name)+1;
    num++;
  }
  dlen+=sizeof(gnutella_results_suffix);
  
  gr=(gnutella_results *) xmalloc(dlen);
  
  gr->num=num;
  
  st=GUINT16_TO_LE(port);
  memcpy(gr->port,&st,2);
  
  it=GUINT32_TO_LE(speed);
  memcpy(gr->speed,&it,4);
  
  memcpy(gr->ip,ip,4);
  
  /* now we can loop through the results, and stick each one onto
   * the end of the query packet */
  grn=(gnutella_results_name *) ((char *)gr+sizeof(gnutella_results));
  for(ltemp=results; ltemp; ltemp=gnut_list_next(ltemp)) {
    si=ltemp->data;
    
	name = si->path;
#ifndef WIN32
    if (gc_hide_pathname) {
	  name = strrchr(si->path, '/');
	  if (name) {
		name++;
	  } else {
		name = si->path;
	  }
	}
#endif /* WIN32 */
    
    it=GUINT32_TO_LE(si->ref);
    memcpy(grn->ref,&it,4);
    
    it=GUINT32_TO_LE(si->size);
    memcpy(grn->size,&it,4);
    
    strcpy(grn->name,name);
    grn->name[strlen(name)+1]=0;
    grn=(gnutella_results_name *) ((char *) grn + sizeof(gnutella_results_name) + strlen(name) +1);
  }

  /* Finally, stick our host GUID on the end */
  grs=(gnutella_results_suffix *) grn;
  memcpy(grs->guid, mguid,16);
  
  it=GUINT32_TO_LE((char *) grs + 16 - (char *) gr);
  memcpy(gpa->gh.dlen,&it,4);
  
  gpa->data=gr;
  
  return gpa;  
}

/* gnutella_packet * gp_ping_make(char mac[6], int ttl)
 *
 * Makes a ping packet and returns it */
gnutella_packet * gp_ping_make(char *mac, int ttl)
{
  gnutella_packet * gpa;
  int i;
  
  gpa=(gnutella_packet *)calloc(sizeof(gnutella_packet),1);
    
  gpa->data=NULL;

  memcpy(gpa->gh.guid,mac,6);
  for (i=7;i<16;i++) gpa->gh.guid[i]=rand();
  
  gpa->gh.ttl=ttl;
  gpa->gh.hops=0;
  
  return gpa;
}

/* gnutella_packet * gp_request_make(char mac[6],char *name, int ttl)
 *
 * Makes a query (0x80) packet and returns it. */
gnutella_packet * gp_request_make(char *mac, char *name, int ttl)
{
  gnutella_packet * gpa;
  char *buf;
  uint32 t;
  int i;
  
  gpa=(gnutella_packet *)xmalloc(sizeof(gnutella_packet));
  
  gpa->gh.func=0x80;
  
  t=GUINT32_TO_LE(strlen(name)+3);
  memcpy(gpa->gh.dlen, &t, 4);
  
  buf=(char *) xmalloc(strlen(name)+5);
  buf[0]=0;
  buf[1]=0;
  strcpy(&buf[2], name);
  
  gpa->data=buf;
  
  memcpy(gpa->gh.guid, mac, 6);
  for (i=7;i<16;i++)
    gpa->gh.guid[i]=rand();
  
  gpa->gh.ttl=ttl;
  gpa->gh.hops=0;
  
  return gpa;
}

gnutella_packet * gp_push_make(char *mac,int ttl, char *guid,
  uint32 ref, uchar *ip, uint16 port)
{
  gnutella_packet *gpa;
  gnutella_push *gp;
  uint32 t;
  uint16 st;
  
  gpa=(gnutella_packet *) xmalloc(sizeof(gnutella_packet));
  gpa->gh.func=64;
  gpa->gh.ttl=ttl;
  gpa->gh.hops=0;
  
  t=GUINT32_TO_LE(sizeof(gnutella_push));
  memcpy(gpa->gh.dlen,&t,4);
  
  gp=(gnutella_push *) xmalloc(sizeof(gnutella_push));
  gpa->data=gp;
  memcpy(gp->guid,guid,16);
  memcpy(gp->ip,ip,4);
  
  st=GUINT16_TO_LE(port);
  memcpy(gp->port,&st,2);
  
  t=GUINT32_TO_LE(ref);
  memcpy(gp->ref,&t,4);
  
  return gpa;
}

/* gnutella_packet *gp_pong_make(uchar ip[4], char *guid, int files, 
 *        int bytes, int port, int ttl)
 * 
 * Makes a PONG packet, and returns it. */
gnutella_packet * gp_pong_make(char *guid, int files,
        int bytes, uchar *ip, uint16 port, int ttl)
{
  gnutella_packet * gpa;
  gnutella_servent *gm;
  
  uint32 t;
  uint16 st;
  
  gpa=(gnutella_packet *) xmalloc(sizeof(gnutella_packet));
  
  gm=(gnutella_servent *) xmalloc(sizeof(gnutella_servent));
  
  t=GUINT32_TO_LE(sizeof(gnutella_servent));
  memcpy(gpa->gh.dlen,&t,4);
  
  gpa->gh.func=1;
  
  t=GUINT32_TO_LE(files);
  memcpy(gm->files,&t,4);
  
  t=GUINT32_TO_LE(bytes);
  memcpy(gm->bytes,&t,4);
  
  memcpy(gm->ip,ip,4);
  
  st=GUINT16_TO_LE(port);
  memcpy(gm->port,&st,2);
  
  memcpy(gpa->gh.guid,guid,16);
  
  gpa->data=gm;
  
  gpa->gh.ttl=ttl;
  gpa->gh.hops=0;
  
  return gpa;
}

/* gnutella_packet * gp_dup(gnutella_packet *gp)
 *
 * duplicates the gnutella_packet gp, and returns a pointer to it */
gnutella_packet *gp_dup(gnutella_packet *gp)
{
  gnutella_packet *ngp;
  uint32 dlen;
  
  gd_s(3, "gp_dup entering\n");
  ngp=(gnutella_packet *) xmalloc(sizeof(gnutella_packet));
  
  memcpy(ngp,gp,sizeof(gnutella_packet));
  memcpy(&dlen,ngp->gh.dlen,4);
  dlen=GUINT32_FROM_LE(dlen);
  
  if (dlen>0) {
    ngp->data=xmalloc(dlen);
    memcpy(ngp->data,gp->data,dlen);
  } else {
    ngp->data = 0;
  }
  
  gd_s(3, "gp_dup returning success\n");
  return ngp;
}
