/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            socket.cc
 *
 *  Mon Nov  8 10:49:33 CET 2004
 *  Copyright  2004 Bent Bisballe
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *    This file is part of MIaV.
 *
 *    MIaV 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.
 *
 *    MIaV 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 MIaV; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include "socket.h"

#include <errno.h>

// for gethostbyname
#include <netdb.h>

// for inet_addr
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

Socket::Socket(int port, std::string addr)
{
  this->prt = port;
  this->addr = addr;
  connected = false;
}

Socket::~Socket()
{
  disconnect();
}

int Socket::setPort(int port)
{
  if(connected) {
    strerr = "Cannot change port, while socket is connected.";
    return 1;
  }

  this->prt = port;
  return 0; 
}

int Socket::port()
{
  return prt;
}

int Socket::setAddress(std::string addr)
{
  if(connected) {
    strerr = "Cannot change address, while socket is connected.";
    return 1;
  }

  this->addr = addr;
  return 0;
}

std::string Socket::address()
{
  return addr;
}

static int _connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
{return connect(sockfd, serv_addr, addrlen);}
int Socket::connect()
{
  if(connected) {
    strerr = "Could not connect, socket is already connected.";
    return 1;
  }

  // Do DNS lookup
  char *ip;
  struct in_addr **addr_list;
  struct hostent *he;
  he = gethostbyname(addr.c_str());
  if(!he || !he->h_length) {
    strerr = hstrerror(h_errno);
    return 1;
  }

  addr_list = (struct in_addr **)he->h_addr_list;
  //  for(int i = 0; addr_list[i] != NULL; i++) {
  ip = inet_ntoa(*addr_list[0]);
    //  }

  struct sockaddr_in socketaddr;
  memset((char *) &socketaddr, sizeof(socketaddr), 0);
  socketaddr.sin_family = AF_INET;
  socketaddr.sin_port = htons(prt);
  socketaddr.sin_addr.s_addr = inet_addr(ip);
  
  if(_connect(sock, (struct sockaddr*)&socketaddr, sizeof(socketaddr))) {
    strerr = strerror(errno);
    return 1;
  }

  connected = true;

  return 0;
}

int Socket::disconnect()
{
  if(!connected) {
    strerr = "Could not disconnect, socket is already disconnected.";
    return 1;
  }

  close(sock);
  connected = false;

  return 0;
}

std::string Socket::error()
{
  return strerr;
}  





















/*

#include "info.h"

#include <errno.h>


Socket::Socket()
{
  connected = false;
  err = 0;
}

Socket::Socket(u_short port)
{
  connected = false;
  err = 0;

  // create socket
  ssocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
  // PF_INET: ipv4, PF_INET6: ipv6
  // tcp: IPPROTO_TCP
  // upd: IPPROTO_UDP

  if (ssocket < 0) {
    err = 1;
    MIaV::info->error("Socket: socket() failed!");
  }

  socketaddr.sin_family = AF_INET; // Use "internet protocol" IP
  socketaddr.sin_port = htons(port);  // connect to that port
  socketaddr.sin_addr.s_addr = INADDR_ANY;
  // INADDR_ANY puts your IP address automatically
}


Socket::~Socket()
{
  //  if(err) perror("Socket: No socket to kill");
  //  printf("Socket: I'm melting...[%d]\n", ssocket);
  if(ssocket >= 0) close(ssocket);  // close server socket
}


Socket Socket::slisten()
{
  Socket s;

  if(err) {
    // MIaV::info->error("Socket: No socket present!");
    return s;
  }
  if(!connected) {
    // bind socket to address specified by "sa" parameter
    err = bind(ssocket, (struct sockaddr*)&socketaddr, sizeof(socketaddr));
    
    if (err) {
      MIaV::info->error("Socket: bind() failed! %s", strerror(errno));
      return s;
    }
    
    // start listen for connection - kernel will accept connection 
    // requests (max 5 in queue)
    err = listen(ssocket, 5);
    if(err) {
      MIaV::info->error("Socket: listen() failed! %s", strerror(errno));
      return s;
    }
  }

  // accept new connection and get its connection descriptor
  int csalen = sizeof(s.socketaddr);

  s.ssocket = accept(ssocket, 
                     (struct sockaddr*)&s.socketaddr, 
                     (socklen_t*)&csalen);

  if (s.ssocket < 0) {
    s.connected = false;
    err = 1;
    MIaV::info->error("Socket: accept() failed! %s", strerror(errno));
    return s;
  }

  connected = true;
  s.connected = true;
  return s;
}


int Socket::sconnect(char *ip)
{
  if(err) {
    connected = false;
    MIaV::info->error("Socket: No socket present!");
    return err;
  }

  // FIXME: gethostbyname()
  socketaddr.sin_addr.s_addr = inet_addr(ip); 
  //inet_aton (ip, &socketaddr.sin_addr);
  
  err = connect(ssocket, (struct sockaddr*)&socketaddr, sizeof(socketaddr));
  if (err) {
    connected = false;
    MIaV::info->error("Socket: connect() failed! %s", strerror(errno));
    return err;
  }
  //  fprintf(stderr, "Socket connected\n");
  connected = true;
  return 0;
}


bool Socket::isConnected()
{
  return connected;
}

bool Socket::hasError()
{
  return err != 0;
}

*/