/* PIPClient - A client for finger protocol v2
 *
 * Copyright (C) 1999 Michael Baumer
 *
 * 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; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <config.h>

#include <gtk/gtk.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>

#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
#ifndef WIFEXITED
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif

#include "parser.h"
#include "configfile.h"
#include "conn.h"
#include "request.h"
#include "compat.h"

#ifndef HAVE_GETOPT_DECL
int getopt(int argc, char **argv, char *optstring);
extern char *optarg;
extern int optind;
#endif 

int debug = 0;
int gnufinger = 0;
int realnames = 0;
int test = 0;
Node *cf;
char *configfile;

extern void addText(char *txt, int font);
extern void create_gui_gtk();
extern void gui_add_user(char *name, int status);

/* Create the GUI
 */
void create_gui()
{
  create_gui_gtk();
}

/* Start the in.fingerd via exec & pipes
 *
 * Used for testing a fresh compilation of in.fingerd
 * Note: in.fingerd is started in the current working dir!
 *
 * parameters:
 *    debug: verbose output or not
 *    cmd  : the command string to send. Must include "</pip>"
 */
void test_connection(int debug, char *cmd)
{
  /* make a sample connection for testing the client */
  
  int pid;
  int status;
  int pin[2];
  int pout[2];

  if (setvbuf(stdout, NULL, _IOLBF, 0) < 0) {
    perror("setvbuf(stdout)");
    exit(1);
  }

  /* open pipes */
  if (pipe(pin) ||
      pipe(pout)) {
    perror("opening pipe");
    exit(1);
  }
  
  /* fork */
  pid = fork();
  if(!pid) {
    /* child */

    /* redirect filedes */
   
    /* pin[read] auf stdin legen*/
    close(0);
    dup(pin[0]);

    /*pout[write] auf stdout legen*/
    close(1);
    dup(pout[1]);

    close(pin[1]);
    close(pout[0]);
    close(pin[0]);
    close(pout[1]);

    if (gnufinger)
      execl("in.fingerd", "in.fingerd", "-g", NULL); 
    else
      execl("in.fingerd", "in.fingerd", NULL); 
    perror("Starting child");
    exit(-15);
  }

  if (debug)
    printf("started client : pid=%d\n", pid);

  /*
   * parent
   */

  close(pin[0]);
  close(pout[1]);

  send_request(pin[1], pout[0], cmd);

  if (debug)
    printf("Waiting for Child to terminate\n");
  wait(&status);
  if (debug)
    printf("Child terminated with status: %d\n", status);

  /* close all open filedesc */
  close(pin[1]);
  close(pout[0]);
}

void show_online_user(Node *node)
{
  Node *subnode;
  Node *s2node;
  char *login;
  char *realname;
  int status;

  subnode = node->child;
  while(subnode) {
    login = NULL;
    status = 0;

    s2node = subnode->child;
    while (s2node) {
      if (!strcmp(s2node->id, "login")) {
	login = s2node->value;
      }
      else 
	if (!strcmp(s2node->id, "status")) {
	  if (!strcmp(s2node->value, "active"))
	    status = 1;
	}
	else 
	  if (!strcmp(s2node->id, "realname")) {
	    realname = s2node->value;
	  }
	  

      s2node = s2node->next;
    }
    if (login) {
      if (realnames)
	gui_add_user(realname, status);
      else
	gui_add_user(login, status);
    }

    subnode = subnode->next;
  }
}

/* Show userinfo */
void show_userinfo(Node *node)
{
  Node *cur_node;
  char txt[120];

  /* Output everything */
  cur_node = node->child;
  while (cur_node) {
    if (!strcmp(cur_node->id, "userinfo")) {
      cur_node = cur_node->child;
      while(cur_node) {

	/* Handle specials */
	/*
	if (strcmp(cur_node->id, "first") && 
	    strcmp(cur_node->id, "last")) {
	*/
	  /* Make it simple */
	  
	  /* this is probably a hack */
	  cur_node->id[0] = toupper(cur_node->id[0]);
	  
	  if (!*cur_node->value) {
	    sprintf(txt, "\n%s\n", cur_node->id);
	    addText(txt, 2);
	  }
	  else {
	    if (index(cur_node->value, '\n')) 
	      sprintf(txt, "%s: \n", cur_node->id);
	    else
	      sprintf(txt, "%s: ", cur_node->id);
	    addText(txt, (strcmp(cur_node->id, "Plan")) ? 0 : 2);
	    addText(cur_node->value, (strcmp(cur_node->id, "Plan")) ? 1 : 0);
	    addText("\n", 0);
	  }
	  /*	
	}
	  */
	if (cur_node->child) 
	  cur_node = cur_node->child;
	else {
	  while (!cur_node->next) {
	    if (!cur_node->parent)
	      break;
	    cur_node = cur_node->parent;
	  }
	  cur_node = cur_node->next;
	}
      }
    } else
      cur_node = cur_node->next;
  }
}

/* Send a request in finger v1 protocol and hope...
 * Since the output is arbitrary this may fail miserably
 * If the user doesn't help us, we try to do the best... 
 *
 * At least we should be compatible to GNU Finger, Win NT Finger
 * and Sun's Finger.
 *
 * Since we only want to know the online status we dont ask for a username.
 * We can only check for a userlist because we simply search the output
 * for these names. If we want to have a list of all poeple online, we should
 * parse the output which is currently not implemented. This also means we 
 * don't have any idle time information.
 */
int send_request_v1(int fdin, int fdout, char **username)
{
  int retval = 0;
  char *nl = "\r\n";
  int nread;
  int len;
  char *scan;
  char *line;
  int offset;
  int i;
  char inbuf[2048];
  
  /* Send a newline to get all online users */  

  write(fdin, nl, sizeof(nl)+1);

  scan = inbuf;
  len = sizeof(inbuf) - 1;
  offset = 0;
  line = inbuf;

  while ((nread = read(fdout, inbuf+offset, len-offset))) {
    if (nread == -1) {
      perror("Reading from remote end");
      break;
    }
    offset += nread;
    inbuf[offset] = '\0';
    
    if (debug)
      printf("inbuf:%s\n", inbuf);
    
    while(scan < inbuf + offset) {
      if ((*scan == '\n') || (*scan == '\r')) {
	*scan = '\0';
	if (line<scan) {
	  i = 0;
	  while(username[i]) {
	    if (strlen(username[i]) &&
		(!strncmp(line, username[i], strlen(username[i])))) {
	      gui_add_user(username[i], 2);
	      username[i] = "";
	    }
	    i++;
	  }
	}
	scan++;
	line = scan;
      }
      else
	scan++;
    }
  }

  return retval;
}

void init()
{
  setHandler("online_reply", show_online_user);
  setHandler("finger_reply", show_userinfo);
}


void poll_servers()
{
  int s;
  char *cmd = "<online></online></pip>\n";
  char *usertest[3];

  usertest[0] = "felix";
  usertest[1] = "chris";
  usertest[2] = NULL;

  if (test) {
    /* Test the local in.fingerd via pipes 
     * Useful for testing the compilation before
     * installing it 
     */
    
    test_connection(0, cmd);
    s = make_conn("some.host", NULL);
  
    if (s >=0)
      send_request_v1(s, s, usertest);

    return;
  }

  s = make_conn("localhost", NULL);

  send_request(s, s, cmd);

  close(s);

return;

  /* only a test yet */

  s = make_conn("some.host", NULL);
  
  if (s >=0)
    send_request_v1(s, s, usertest);
}
  
void Usage(char *progname)
{
  printf("Usage: %s [-c file] [-h] [-V] [-d] [-t]\n", progname);
  printf("\t-c file\tuse \"file\" as configuration file\n"
	 "\t-h\tprint this screen and exit\n"
	 "\t-V\tprint version number and exit\n"
	 "\t-d\tdebug modus\n"
	 "\t-t\ttest modus: start in.fingerd locally\n");
}

int main( int   argc, char *argv[] )
{
  int flag;
  char *progname;
  char *home;

  /* extraxt base name */
  progname = strrchr(argv[0], '/');
  if (!progname)
    progname = argv[0];
  else
    progname++;

  /* set defaults */
  configfile = ".fingerconf";
  if ((home = getenv("HOME"))) {
    configfile = (char *)malloc(strlen(configfile) + strlen(home) + 3);
    sprintf(configfile, "%s/.fingerconf", home);
  }

  /* parse the command line arguments */
  while ((flag = getopt(argc, argv,
			"c:Vdghrt")) != EOF) 
    switch (flag) {
    case 'c':
      configfile = strdup(optarg);
      break;
    case 'V':
      printf("%s: %s Version %s\n", progname, PACKAGE, VERSION);
      exit(0);
    case 'd':
      debug = 1;
      break;
    case 'g':
      gnufinger = 1;
      break;
    case 't':
      test = 1;
      break;
    case 'r':
      realnames = 1;
      break;
    case 'h':
    default:
      Usage(progname);
      exit(0);
      break;
    }

  gtk_init (&argc, &argv);

  create_gui();
 
  init();

  cf = readFingerconf(configfile);
  if (cf) {
  }

  poll_servers();

  gtk_main();

  return(0);
}


syntax highlighted by Code2HTML, v. 0.9.1