/***************************************************************************
                          notification.cpp  -  description
                             -------------------
    begin                : Thu Nov 20 2003
    copyright            : (C) 2003 by Wei Zheng
    email                : v_zheng@yahoo.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "notification.h"
#define ENDSTRING "f680fa9ba884447fc820046655b2b9da"

/** No descriptions */
notification::notification(char * name,char * pass,char * addr){
  memset(address1,0,sizeof(address1));
  memset(address2,0,sizeof(address2));
  auth.get_namepass(name,pass);
  if(addr==NULL)
    initialize(NOTIFICATIONTCPADDR,NOTIFICATIONTCPPORT);
  else
    initialize(addr,NOTIFICATIONTCPPORT);
  communication();
}

/** No descriptions */
notification::~notification(){
}

/** No descriptions */
int notification::initialize(char * addr,int port){

  response response_line;

  if(mytcp.make_connect(addr,port)==-1)
  {
    mytcp.debug("ERR %s unable to connect\n",addr);
    return 1;
  }
  else
  {
    mytcp.debug("OK %s able to connect\n",addr);
    return 0;
  }
}

/** No descriptions */
int notification::communication(){

  int result;

  send_command("VER","MSNP9 MSNP8 MSNP7 CVR0\r\n");
  reci_response();

  send_command("CVR","0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s\r\n",auth.username);
  reci_response();

  send_command("USR","TWN I %s\r\n",auth.username);
  reci_response();

  if(strcmp(resp.point[0],"XFR")!=0 || strcmp(resp.point[2],"NS")!=0)
  {
    return 1;
  }

  memcpy(address1,resp.point[3],strstr(resp.point[3],":")-resp.point[3]);
  memcpy(address2,resp.point[5],strstr(resp.point[5],":")-resp.point[5]);

  if(mytcp.make_close()!=0)
  {
    printf("Connection fail\n");
    return 1;
  }

  result = initialize(address1,NOTIFICATIONTCPPORT);
  if(result!=0)
  {
    result = initialize(address2,NOTIFICATIONTCPPORT);
  }

  if(result!=0)
  {
    printf("Connection to notification server failed\n");
    return 1;
  }

  send_command("VER","MSNP9 MSNP8 MSNP7 CVR0\r\n");
  reci_response();

  send_command("CVR","0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s\r\n",auth.username);
  reci_response();

  send_command("USR","TWN I %s\r\n",auth.username);
  reci_response();

  if(strcmp(resp.point[0],"USR")!=0 || strcmp(resp.point[2],"TWN")!=0)
  {
    return 1;
  }

  auth.get_challenge(resp.point[4]);
  if(auth.do_auth()!=1)
  {
    printf("failed\n");
    return 1;
  }

  send_command("USR","TWN S %s\r\n",auth.challenge);
  reci_response();

  if(strcmp(resp.point[0],"USR")!=0 || strcmp(resp.point[2],"OK")!=0)
    return 1;

  send_command("SYN","1069\r\n");
  send_command("CHG","NLN 0\r\n");

  while(1)
  {
    reci_response();
    if(strcmp(resp.point[0],"MSG")==0)
        mytcp.reci_mesg(atoi(resp.point[3]));
    if(strcmp(resp.point[0],"CHL")==0)
    {
      strcat(ascii_original,resp.point[2]);
      strcat(ascii_original,"Q1P7W2E4J9R8U3S5");
      MD5Digest();
      send_command("QRY","msmsgs@msnmsgr.com %d\r\n%s",strlen(ascii_digest),ascii_digest);
      reci_response();
    }
    else if(strcmp(resp.point[0],"ILN") == 0)
        usersqueue.add_online_user(resp.point[3],resp.point[4]);
    else if(strcmp(resp.point[0],"NLN") == 0)
        usersqueue.add_online_user(resp.point[2],resp.point[3]);
    else if(strcmp(resp.point[0],"FLN") == 0)
        usersqueue.del_online_user(resp.point[1]);
    else if(strcmp(resp.point[0],"OUT") == 0)
        break;
    else if(strcmp(resp.point[0],"RNG")==0)
    {
      usersqueue.current_user=usersqueue.search_online_user(resp.point[5]);
      if(usersqueue.current_user==NULL)
      {
        usersqueue.add_online_user(resp.point[5],resp.point[6]);
        usersqueue.current_user=usersqueue.search_online_user(resp.point[5]);
      }
      memset(usersqueue.current_user->userinfop->ip,0,sizeof(usersqueue.current_user->userinfop->ip));
      strncat(usersqueue.current_user->userinfop->ip,resp.point[2],(strstr(resp.point[2],":")-resp.point[2])/sizeof(char));
      memset(usersqueue.current_user->userinfop->port,0,sizeof(usersqueue.current_user->userinfop->port));
      strcat(usersqueue.current_user->userinfop->port,strstr(resp.point[2],":")+1);
      memset(usersqueue.current_user->userinfop->sessionid,0,sizeof(usersqueue.current_user->userinfop->sessionid));
      strcat(usersqueue.current_user->userinfop->sessionid,resp.point[1]);
      memset(usersqueue.current_user->userinfop->authid,0,sizeof(usersqueue.current_user->userinfop->authid));
      strcat(usersqueue.current_user->userinfop->authid,resp.point[4]);
      if(usersqueue.current_user->pid!=0)
        kill(usersqueue.current_user->pid,SIGQUIT);
      if((usersqueue.current_user->pid=fork())==0)
      {
        dialog(usersqueue.current_user->userinfoid);
        exit(0);
      }
    }
  }
  return 0;
}

/** No descriptions */
int get_tcp_line(int fd,char *buf,int len){
  int got=0;
  int err;
  while(1)
  {
    if(got==len)
      return(got);
    err=read(fd,&buf[got],1);
    if(err!=1)
    {
      if(errno!=EINTR)
        return(-1);
      return(-3);
    }
    if(buf[got] <= 0)
    {
      buf[got] = 32;
    }
    else if(buf[got]=='\n')
    {
      return(got+1);
    }
    got++;
  }
}

/** No descriptions */
void * thread_write_tcp(void * para)
{
  tcp * tcpp = (tcp *) para;
  char myline[256];
  int result;
  int position = 0;

  if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL)!= 0) {
    perror("Thread pthread_setcancelstate failed");
    pthread_exit(0);
  }

  if(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL)!= 0) {
    perror("Thread pthread_setcanceltype failed");
    pthread_exit(0);
  }

  while(1)
  {
    memset(myline,0,sizeof(myline));
    result = 0;
    result = get_tcp_line(tcpp->pipefd[0],myline,sizeof(myline));
    if(result > 0)
    {
      if(MESGSIZE - position <= result)
      {
        tcpp->send_mesg(tcpp->mesg,position);
        memset(tcpp->mesg,0,MESGSIZE);
        position = 0;
      }
      else
      {
        if(strstr(myline,ENDSTRING)!=NULL)
        {
          tcpp->send_mesg(tcpp->mesg,position);
          memset(tcpp->mesg,0,MESGSIZE);
          position = 0;
        }
        else
        {
          memcpy(tcpp->mesg+position,myline,result);
          position += result;
        }
      }
    }
  }
  pthread_exit(0);
}

/** No descriptions */
void * thread_execute_tcp(void * para)
{
  int fd;
  int i,exit_flag;
  char hostname[56];
  char username[56];
  char password[56];
  tcp * tcpp;
  tcpp = (tcp *) para;

  if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL)!= 0) {
    perror("Thread pthread_setcancelstate failed");
    pthread_exit(0);
  }

  if(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL)!= 0) {
    perror("Thread pthread_setcanceltype failed");
    pthread_exit(0);
  }

  START:
  tcpp->send_mesg("Protocol(ssh/ftp):",18);
  sem_wait(&(tcpp->bin_sem));

  if(strcmp(tcpp->finline,"ssh")==0)
  {
    memset(tcpp->finline,0,strlen(tcpp->finline));
    dup2(tcpp->pipefd[1],STDOUT_FILENO);
    dup2(tcpp->pipefd[1],STDERR_FILENO);
    close(tcpp->pipefd[1]);

    SSHSTART:

    memset(hostname,0,sizeof(hostname));
    memset(username,0,sizeof(username));

    tcpp->send_mesg("hostname:",9);
    sem_wait(&(tcpp->bin_sem));
    strncat(hostname,tcpp->finline,sizeof(hostname));
    memset(tcpp->finline,0,strlen(tcpp->finline));

    tcpp->send_mesg("username:",9);
    sem_wait(&(tcpp->bin_sem));
    strncat(username,tcpp->finline,sizeof(username));
    memset(tcpp->finline,0,strlen(tcpp->finline));

    if (-1 == (fd = exp_spawnl("ssh","ssh","-l",username,hostname,(char *)0)))
    {
      perror("ssh failed");
      exit(-1);
    }

    PASSWORD:
    switch(exp_expectl(fd,
      exp_glob,"*(yes/no)?",0,
      exp_glob,"*password:",1,
      exp_glob,"Temporary failure in name resolution",2,
      exp_end)){
    case 0:
      write(fd,"yes\r",4);
      goto PASSWORD;
      break;
    case 1:
      while(1)
      {
        tcpp->send_mesg("password:",9);
        sem_wait(&(tcpp->bin_sem));
        strcat(tcpp->finline,"\n\r");
        write(fd,tcpp->finline,strlen(tcpp->finline));
        memset(tcpp->finline,0,strlen(tcpp->finline));

        switch(exp_expectl(fd,
          exp_glob,"Last login:*",0,
          exp_glob,"again",1,
          exp_glob,"publickey,password,keyboard-interactive",2,
          exp_end)){
        case 0:
          i = 0;
          printf("%s\n",ENDSTRING);
          break;
        case 1:
          i = 1;
          printf("%s\n",ENDSTRING);
          break;
        case 2:
          i = 2;
          printf("%s\n",ENDSTRING);
          break;
        }
        if(i == 0)
          break;
        if(i == 2)
        {
          sleep(1);
          goto SSHSTART;
        }
      }
    break;
    case 2:
      printf("%s\n",ENDSTRING);
      sleep(1);
      goto SSHSTART;
    default:
      printf("%s\n",ENDSTRING);
      sleep(1);
      goto SSHSTART;
    }

    switch(exp_expectl(fd,exp_glob,"*]# ",0,exp_glob,"*]$ ",1,exp_end)){
      case 0:
        write(fd,"sh\n",3);
        break;
      case 1:
        write(fd,"sh\n",3);
        break;
    }

    while(1)
    {
      switch(exp_expectl(fd,
        exp_glob,"\n",0,
        exp_glob,"sh-2.05a# ",1,
        exp_glob,"sh-2.05a$ ",1,
	exp_glob,"sh-2.05b# ",1,
	exp_glob,"sh-2.05b$ ",1,
        exp_end)){
      case 0:
        break;
      case 1:
        printf("\n%s\n",ENDSTRING);
        sem_wait(&(tcpp->bin_sem));
        if(strncmp(tcpp->finline,"exit",4)==0)
          i = 1;
        else
          i = 0;
        strcat(tcpp->finline,"\n");
        write(fd,tcpp->finline,strlen(tcpp->finline));
        memset(tcpp->finline,0,strlen(tcpp->finline));
        break;
      case EXP_TIMEOUT:
        printf("\n%s\n",ENDSTRING);
        sem_wait(&(tcpp->bin_sem));
        if(strncmp(tcpp->finline,"exit",4)==0)
          i = 1;
        else
          i = 0;
        strcat(tcpp->finline,"\n");
        write(fd,tcpp->finline,strlen(tcpp->finline));
        memset(tcpp->finline,0,strlen(tcpp->finline));
        break;
      }
      if(i == 1)
      {
        close(fd);
        goto SSHSTART;
      }
    }
    exit_flag = 1;
  }
  else if(strcmp(tcpp->finline,"ftp")==0)
  {
    struct passwd *pw = NULL;
    struct spwd *sp;
    char *salt, *p;

    memset(tcpp->finline,0,strlen(tcpp->finline));

    PROMPTUSERNAME:
    pw = NULL;
    pw = getpwuid(getuid());

    sp = getspnam(pw->pw_name);
    pw->pw_passwd = sp->sp_pwdp;
    salt = pw->pw_passwd;

    memset(username,0,sizeof(username));
    memset(password,0,sizeof(password));
    sprintf(username,"Current username:%s password:??????",pw->pw_name);
    tcpp->send_mesg(username,strlen(username));
    sem_wait(&(tcpp->bin_sem));
    strncat(password,tcpp->finline,sizeof(password));
    memset(tcpp->finline,0,strlen(tcpp->finline));

    p = crypt(password,salt);
    if(strcmp(p,pw->pw_passwd)!=0)
    {
      tcpp->send_mesg("error:",6);
      goto PROMPTUSERNAME;
    }

    setenv("HOME", pw->pw_dir, 1);
    setenv("SHELL", pw->pw_shell, 1);
    setenv("USER", pw->pw_name, 1);
    setenv("LOGNAME", pw->pw_name, 1);

    chdir(pw->pw_dir);

    dup2(tcpp->pipefd[1],STDOUT_FILENO);
    dup2(tcpp->pipefd[1],STDERR_FILENO);
    close(tcpp->pipefd[1]);

    FTPSTART:

    if (-1 == (fd = exp_spawnl("ftp","ftp",(char *)0)))
    {
      perror("ftp failed");
      exit(-1);
    }

    exit_flag = 0;
    while(1)
    {
      switch(exp_expectl(fd,
        exp_glob,"ftp> ",0,
        exp_glob,"220 *Name*:",1,
        exp_glob,"331 *Password*:",1,
        exp_glob,"221 ",2,
        exp_end)){
      case 0:
        printf("\n%s\n",ENDSTRING);
        sem_wait(&(tcpp->bin_sem));
        if(strncmp(tcpp->finline,"exit",4)==0)
          exit_flag = 1;;
        strcat(tcpp->finline,"\n");
        write(fd,tcpp->finline,strlen(tcpp->finline));
        memset(tcpp->finline,0,strlen(tcpp->finline));
        break;
      case 1:
        printf("\n%s\n",ENDSTRING);
        sem_wait(&(tcpp->bin_sem));
        strcat(tcpp->finline,"\r");
        write(fd,tcpp->finline,strlen(tcpp->finline));
        memset(tcpp->finline,0,strlen(tcpp->finline));
        break;
      case 2:
        goto FTPSTART;
        break;
      case EXP_TIMEOUT:
        printf("\n%s\n",ENDSTRING);
        sem_wait(&(tcpp->bin_sem));
        if(strncmp(tcpp->finline,"exit",4)==0)
          exit_flag = 1;;
        strcat(tcpp->finline,"\n");
        write(fd,tcpp->finline,strlen(tcpp->finline));
        memset(tcpp->finline,0,strlen(tcpp->finline));
        break;
      }
      if(exit_flag == 1)
        goto FTPSTART;
    }
  }
  else
  {
    memset(tcpp->finline,0,strlen(tcpp->finline));
    goto START;
  }

  pthread_exit(0);
}

/** tcp dialog*/
int notification::dialog(int userinfoid)
{
  int readfd;

  pthread_t execute_thread;
  pthread_t write_thread;
	void * thread_result;

  mytcp.userinfop = (userinfo *) shmat(userinfoid, (void *)0, 0);
	if (mytcp.userinfop == (void *)-1)  return -1;
	
  if(mytcp.make_connect(mytcp.userinfop->ip,atoi(mytcp.userinfop->port))==-1)
  {
    mytcp.debug("ERR %s:%s unable to connect\n",mytcp.userinfop->ip,mytcp.userinfop->port);
    return 1;
  }
  else
  {
    mytcp.debug("OK %s:%s able to connect\n",mytcp.userinfop->ip,mytcp.userinfop->port);
  }

  send_command("ANS","%s %s %s\r\n",auth.username,mytcp.userinfop->authid,mytcp.userinfop->sessionid);
	reci_response();
	if(strcmp(resp.point[0],"IRO")!=0 && strcmp(resp.point[4],mytcp.userinfop->username)!=0)
		return -1;
  reci_response();
  if(strcmp(resp.point[0],"ANS")!=0 && strcmp(resp.point[1],"OK")!=0 )
    return -1;

  mytcp.debug_switch = 0;
  pipe(mytcp.pipefd);

  if(pthread_create(&execute_thread, NULL, thread_execute_tcp, (void *) &mytcp) != 0)
  {
    perror("Thread creation failed");
    return -1;
  }

  if(pthread_create(&write_thread,NULL,thread_write_tcp,(void *) &mytcp) != 0)
  {
    perror("Thread creation failed");
    return -1;
  }

  while(1)
	{
		if(reci_response()!=-1)
		{
			if(strcmp(resp.point[0],"BYE")==0)
			{
        close(readfd);
				break;
			}
			else if(strcmp(resp.point[0],"MSG")==0)
			{
        if(mytcp.reci_mesg(atoi(resp.point[3]))!=0)
        {
          if(strlen(mytcp.tmpline)!=0 && strlen(mytcp.finline)==0)
          {
            strcat(mytcp.finline,mytcp.tmpline);
            sem_post(&mytcp.bin_sem);
          }
          else
            mytcp.debug("Drop: %s\n",mytcp.tmpline);
        }
			}
		}
		else
		{
			printf("read recv -1\n");
		}
	}

  if (pthread_cancel(execute_thread) != 0)
  {
    perror("Thread cancelation failed");
    return -1;
  }

  if (pthread_join(execute_thread, &thread_result) != 0)
  {
    perror("Thread join failed");
    return -1;
  }

  if (pthread_cancel(write_thread) != 0)
  {
    perror("Thread cancelation failed");
    return -1;
  }

  if (pthread_join(write_thread, &thread_result) != 0)
  {
    perror("Thread join failed");
    return -1;
  }

  sem_destroy(&mytcp.bin_sem);
	mytcp.make_close();
	return 0;
}

/** No descriptions */
int notification::reci_response(){
  int result;

  resp.flush();
  result = mytcp.get_line(mytcp.fd,resp.line,sizeof(resp.line));
  if(result!=-1)
  {
    resp.makearray();
  }
  return result;
}

/** No descriptions */
int notification::send_command(char * command, char * argument,...){

  char line[560];
  int result;

  int n, size = 560;
  char *p;
  va_list ap;

  memset(line,'\0',sizeof(line));
  mytcp.counter++;

  sprintf(line,"%s %d ",command,mytcp.counter);

  p=(char *)malloc(size);
  if((p=(char *)malloc(size))==NULL)
  {
    p=NULL;
    return 1;
  }
  while(1)
  {
    va_start(ap, argument);
    n = vsnprintf (p, size, argument, ap);
    va_end(ap);
    if (n > -1 && n < size)
      break;
    if (n > -1)
      size = n+1;
    else
      size *= 2;
    if ((p = (char *)realloc(p, size)) == NULL)
    {
      p=NULL;
      return 1;
    }
  }

  strcat(line,p);
  free(p);
  result = mytcp.send_line(line);
  return result;
}

