/* Copyright (c) 1993 Stephen F. White */

#include "cool.h"
#include "string.h"
#include "netio.h"
#include "buf.h"
#include "netio_private.h"
#include "servers_private.h"
#include "servers.h"

static Server *promiscuous_connect (struct sockaddr_in *from,
  const char *name);

static int send_to_server (Server * s, const char *buf)
{
  struct sockaddr_in addr;

  addr.sin_family = AF_INET;
  addr.sin_port = htons (s->port);
  addr.sin_addr.s_addr = htonl (s->addr);

  if (sendto (yo_sock, buf, strlen (buf), 0, (struct sockaddr *) &addr,
      sizeof (addr)) < 0) {
    return -1;
  } else {
    return 0;
  }
}

void connect_to_servers (void)
{
  char buf[128];
  Server *s;

  s = serv_id2server (0);
  sprintf (buf, "*connect %s\n", s->name);
  for (s = servers; s; s = s->next) {
    if (s->id == 0) {           /* skip local server */
      continue;
    }
    if (send_to_server (s, buf)) {
      writelog ();
      fprintf (stderr, "Couldn't send connect msg to server ");
      perror (s->name);
    }
  }
}

void disconnect_from_servers (void)
{
  char buf[128];
  Server *s;

  s = serv_id2server (0);
  sprintf (buf, "*disconnect %s\n", s->name);
  for (s = servers; s; s = s->next) {
    if (!s->id) {               /* skip local server */
      continue;
    }
    if (s->id && s->connected) {
      if (send_to_server (s, buf)) {
        writelog ();
        fprintf (stderr, "Couldn't send disconnect msg to server ");
        perror (s->name);
      }
    }
  }
}

int yo (Serverid server, const char *msg)
{
  Server *s;

  if (!(s = serv_id2server (server))) {
    return -1;
  } else if (!s->connected) {
    return -2;
  } else {
    if (send_to_server (s, msg)) {
      return -3;
    } else {
      return 0;
    }
  }
}

Server *promiscuous_connect (struct sockaddr_in * from, const char *name)
{
  Server *s = 0;

  if (promiscuous) {
    s = serv_add (from, name);
    if (!s) {
      writelog ();
      fprintf (stderr, "INVALID SERVER %s(%d), ignored\n",
        addr_htoa (ntohl (from->sin_addr.s_addr)), ntohs (from->sin_port));
    }
  } else {
    writelog ();
    fprintf (stderr, "UNKNOWN SERVER %s(%d), refused\n",
      addr_htoa (ntohl (from->sin_addr.s_addr)), htons (from->sin_port));
  }
  return s;
}

static void meta_command (struct sockaddr_in *from, char *command)
{
  Server *s = 0;
  char *cmd, *name, *dummy;

  parse_connect (command, &cmd, &name, &dummy);
  s = serv_name2server (name);
  if (!strcmp (cmd + 1, "connect")) {
    char buf[128];
    Server *local;

    if (!s) {
      s = promiscuous_connect (from, name);
      if (!s) {
        return;
      }
    } else if (verify_servers && !verify_server (s, from)) {
      return;
    }
    s->connected = 1;
    s->last_msgid = -1;
    writelog ();
    fprintf (stderr, "Server %s (%s %d) connected\n", s->name, s->hostname,
      s->port);
    local = serv_id2server (0);
    sprintf (buf, "*connectok %s\n", local->name);
    sendto (yo_sock, buf, strlen (buf), 0, (struct sockaddr *) from,
      sizeof (*from));
    connect_server (s->id);
  } else if (!strcmp (cmd + 1, "connectok")) {
    if (!s) {
      s = promiscuous_connect (from, name);
      if (!s) {
        return;
      }
    } else if (verify_servers && !verify_server (s, from)) {
      return;
    }
    s->connected = 1;
    s->last_msgid = -1;
    writelog ();
    fprintf (stderr, "Server %s (%s %d) connected ok\n", s->name,
      s->hostname, s->port);
    connect_server (s->id);
  } else if (!strcmp (cmd + 1, "disconnect")) {
    if (!s) {
      return;
    }
    s->connected = 0;
    writelog ();
    fprintf (stderr, "Server %s (%s %d) disconnected\n", s->name,
      s->hostname, s->port);
    disconnect_server (s->id);
  } else {
    if (!s) {
      return;
    }
    writelog ();
    fprintf (stderr, "Unknown metacommand \"%s\" from server %s\n",
      command, s->name);
  }
}

void server_command (struct sockaddr_in *from, char *cmd)
{
  Server *s;

  if (cmd[0] == '*') {
    meta_command (from, cmd);
  } else {
    s = serv_addr2server (from);
    /* quietly ignore messages from unconnected servers */
    if (s && s->connected) {
      receive_message (s->id, cmd);
    }
  }
}