/*
 * Copyright (c) 2001 Tommy Bohlin <tommy@gatespace.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
/* irs.c
 */

#include <irda.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <strings.h>
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>

static int verbosity;
static int port;
static char *host = "localhost";

/**********************************************************************
 * OBEX server
 **********************************************************************/

static void objectToFile(OBEXServer* os, void* buf, int len)
{
  int i;
  char fname[32];
  FILE* f;

  for(i=0;;i++) {
    bool ok=TRUE;
    DIR *dir=opendir(".");
    struct dirent* de;

    sprintf(fname,"%d.obex",i);
    while((de=readdir(dir))) {
      if(!strcmp(fname,de->d_name)) {
	ok=FALSE;
	break;
      }
    }
    closedir(dir);
    if(ok) break;
  }

  birda_log("object %s\n",fname);

  f=fopen(fname,"w");
  fwrite(buf,1,len,f);
  fclose(f);
}

int in_address(char *host, short port, struct sockaddr_in *address )
{
#define HOST_NAME_LEN 128
    char host_name[HOST_NAME_LEN] ;
    char *the_host;
    struct hostent *hp;

    bzero((char *)address, sizeof(*address));

    /*
     * Determine the host name
     */
    if (host == NULL) {
	(void)gethostname(host_name, HOST_NAME_LEN);
	the_host = host_name;
    } else
	the_host = host;
	
#if 0    
    if (is_number_address(host))
	address->sin_addr.s_addr = inet_addr(host);
#endif
    if ((hp = gethostbyname(the_host)) == NULL) {
      birda_log("Cannot send obex object to socket: %s\n", strerror(errno));
      return -1;
    }
    bcopy(hp->h_addr, (char *)&address->sin_addr, hp->h_length);

    address->sin_family = AF_INET;
    address->sin_port = htons(port);

    return 0;
}


static void objectToSocket(OBEXServer* os, void* buf, int len)
{
  int s, n, offset = 0;
  struct sockaddr_in sa;
  u_char *cbuf = buf;

  birda_log("sending obex object to port %d\n", port);

  
  if (in_address(host, port, &sa) < 0 ||
      (s = socket(PF_INET, SOCK_STREAM, 0)) < 0 ||
      connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
    birda_log("Cannot send obex object to socket: %s\n", strerror(errno));
    return;
  }
  
  while ((n = write(s, &cbuf[offset], len - offset)) > 0) {
    offset += n;
  }
  close(s);
}

/**********************************************************************
 * COMM server
 **********************************************************************/

static void input(void* handle)
{
  COMMPort* cp=(COMMPort*)handle;
  u_char buf[1024];
  int len=read(fileno(stdin),buf,sizeof buf);

  if (len == 0) {
    if(verbosity>0)
      birda_log("comm select with no data to read, closing connection\n");
    evtRemoveSource(fileno(stdin));
    return;
  }

#if 0
  {
    int ix;
    birda_log("pc -> palm: len=%d\n", len);
    for (ix = 0; ix < len; ix++) {
      birda_log("%02x ", buf[ix]);
    }
    birda_log("\n");
  }
#endif
  if(len>0) commPortWrite(cp,buf,len);
}

static void commStatus(COMMPort* comm, int event)
{
  switch(event) {
  case COMM_CONNECTED:
    if(verbosity>0) birda_log("comm connected\n");
    evtAddSource(fileno(stdin),input,comm);
    break;
  case COMM_DISCONNECTED:
    evtRemoveSource(fileno(stdin));
    if(verbosity>0) birda_log("comm disconnected\n");
    break;
  }
}

static void commData(COMMPort* comm, void* buf, int len)
{
#if 0
  birda_log("palm -> pc: len=%d\n", len);
#endif
  write(fileno(stdout), buf, len);
#if 0
  fwrite(buf,1,len,stdout);
  fflush(stdout);
#endif
}

/**********************************************************************
 * LAP control
 **********************************************************************/

static bool exitOnDisconnect;

static void lapConnected(LAP* lap)
{
  if(verbosity>0) birda_log("LAP connected\n");
}

static void lapDisconnected(LAP* lap)
{
  if(verbosity>0) birda_log("LAP disconnected\n");
  if(exitOnDisconnect) exit(0);
}

/**********************************************************************
 * Main function
 **********************************************************************/

static const char* description="Accept incoming OBEX and COMM connections";

static Option options[] = {
  { OPTION_BOOL, 'o', 0, "OBEX server saving objects to file" },
  { OPTION_INT, 's', "port", "OBEX server writing objects to socket" },
  { OPTION_BOOL, 'c', 0, "COMM server" },
  { OPTION_BOOL, 'e', 0, "Exit on disconnect" },
  { OPTION_BOOL, 'i', 0, "Initiate LAP connection" },
  { OPTION_INT, 'O', "port", "Accept OBEX objects on this port and send them" }
};

int main(int argc, char** argv)
{
  doOptions(argc,argv,6,options,&verbosity,description);
  optLapConnected=lapConnected;
  optLapDisconnected=lapDisconnected;

  exitOnDisconnect=options[3].val.b;

  if(options[0].val.b) {
    OBEXServer* obexServ=createOBEXServer(optLap,optIas);
    obexServ->handle=0;
    obexServ->object=objectToFile;
    if(verbosity>0) obexServ->debug|=OBEX_DEBUG_INFO;
  }

  if(options[1].val.i) {
    OBEXServer* obexServ=createOBEXServer(optLap,optIas);
    obexServ->handle=0;
    obexServ->object=objectToSocket;
    port = options[1].val.i;
    if(verbosity>0) obexServ->debug|=OBEX_DEBUG_INFO;
  }

  if(options[2].val.b) {
    COMMServer* commServ=createCOMMServer(optLap,optIas);
    COMMPort* commPort=commSrvNewPort(commServ);
    commPort->status=commStatus;
    commPort->data=commData;
    if(verbosity>0) commServ->debug|=COMM_DEBUG_INFO;
  }

  if(options[4].val.b) doConnect();

  if(options[5].val.i) {
    createOBEXSender(options[5].val.i);
  }

  evtLoop();

  return 0;
}
