/*
*  XmNap  A Motif napster client
*
*  Copyright (C) 2000 Mats Peterson
*
*  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; see the file COPYING.  If not, write to
*  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
*  Boston, MA 02111-1307, USA.
*
*  Please send any comments/bug reports to
*  [email protected]  (Mats Peterson)
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>

#include "main.h"
#include "connect.h"
#include "message.h"
#include "msgbox.h"
#include "netutil.h"
#include "util.h"


int MakeSocket(unsigned short int port, int *sock)
{
   struct sockaddr_in name;
   int status;

   /* Create the socket. */
   *sock = socket(PF_INET, SOCK_STREAM, 0);
   if (*sock < 0)
       return errno;

   if (SetBlocked(*sock, 0))
       goto error;

   /* Give the socket a name. */
   name.sin_family = AF_INET;
   name.sin_port = htons(port);
   name.sin_addr.s_addr = htonl(INADDR_ANY);

   status = 1;
   (void)setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
           sizeof(status));

   if (bind (*sock, (struct sockaddr*)&name, sizeof(name)) < 0)
       goto error;

   return 0;

error:
   close(*sock);
   return errno;
}


int InitSockAddr(struct sockaddr_in *name, const char *hostName,
       unsigned short int port)
{
   struct hostent *hostInfo;

   name->sin_family = AF_INET;
   name->sin_port = htons(port);
   if (! (hostInfo = gethostbyname(hostName)))
       return h_errno;
   name->sin_addr = *(struct in_addr *) hostInfo->h_addr;
   return 0;
}


int ConnSock(char *server, int port, int *sock)
{
   struct sockaddr_in name;
   time_t start;
   int errVal = -1;

   *sock = socket(PF_INET, SOCK_STREAM, 0);
   if (*sock < 0) {
       ErrMsg(strerror(errno));
       return errVal;
   }

   if (SetBlocked(*sock, 0)) {
       ErrMsg(strerror(errno));
       goto error;
   }

   if (InitSockAddr(&name, server, port)) {
       ErrMsg("Host name lookup failure");
       goto error;
   }

   start = time(NULL);
   while (0 > connect(*sock, (struct sockaddr *) &name, sizeof(name))) {
       if ((errno != EINPROGRESS) && (errno != EALREADY)) {
           if (errno == EISCONN)
               break;
           /* FreeBSD returns EINVAL instead of ECONNREFUSED */
           if (errno == EINVAL)
               errno = ECONNREFUSED;
#ifdef AUTORECONNECT_ON_REFUSED
           if ((errno == ECONNREFUSED) && (sock == &srvSock)) {
               ShowMiscInfo(strerror(errno), 0);
               errVal = -2;
           } else
#endif
               ErrMsg(strerror(errno));
           goto error;
       }
       if ((time(NULL) - start) >= timeOut) {
           ErrMsg("Timeout");
           goto error;
       }
       while (XtAppPending(appCon))
           XtAppProcessEvent(appCon, XtIMAll);
       usleep(1000);
   }

   return 0;

error:
   close(*sock);
   return errVal;
}


int WriteSock(char *data, int len)
{
   int err, optLen = sizeof(int);

   if (WriteChars(srvSock, data, len) == -1)
       return -1;
   if (getsockopt(srvSock, SOL_SOCKET, SO_ERROR,
           (char*)&err, &optLen) == -1)
       return -1;
   if (err != 0) {
       errno = err;
       return -1;
   }
   return 0;
}


int SetBlocked(int fd, int blocked)
{
   int oldState, curState;

   if ((oldState = fcntl(fd, F_GETFL)) == -1)
       return errno;
   if (blocked)
       curState = oldState & ~(O_NONBLOCK);
   else
       curState = oldState | O_NONBLOCK;
   if (fcntl(fd, F_SETFL, curState) == -1)
       return errno;
   return 0;
}


void WaitVar(int *var, int timeout)
{
   time_t t;

   t = time(NULL);
   while (*var == -1) {
       if (XtAppPending(appCon))
           XtAppProcessEvent(appCon, XtIMAll);
       if ((time(NULL) - t) >= timeOut)
           break;
       usleep(1000);
   }
}