/*****************************************************************
 * PTlink IRCd is (C) CopyRight PTlink Coders Team 1999-2002     *
 * http://www.ptlink.net/Coders - coders@PTlink.net              *
 * This program is distributed under GNU Public License          *
 * Please read the file COPYING for copyright information.       *
 *****************************************************************
 
  File: m_svsmode.c
  Desc: services sent modes 
  Author: Lamego@PTlink.net
*/

#include <stdlib.h>
#include <string.h>
#include "m_commands.h" 
#include "common.h"
#include "struct.h"
#include "fdlist.h"
#include "client.h"
#include "ircd.h"
#include "channel.h"
#include "rhc.h"
#include "send.h"
#include "s_user.h"
#include "s_conf.h"
#include "irc_string.h"
#include "conf_vars.h"
#include "whowas.h" /* for get_history --hardman */

/* memory is cheap. map 0-255 to equivalent mode */

static int user_modes_from_c_to_bitmask[] =
{ 
  /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x0F */
  /* 0x10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1F */
  /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2F */
  /* 0x30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x3F */
  0,            /* @ */
  FLAGS_ADMIN,  /* A */
  UMODE_BOT,    /* B */
  0,            /* C */
  0,            /* D */
  UMODE_GRINGO, /* E */
  0,            /* F */
  0,            /* G */
  0,            /* H */
  0,            /* I */
  0,            /* J */
  0,            /* K */
  UMODE_LINKER, /* L */
  0,            /* M */
  FLAGS_NETADMIN,/* N */
  FLAGS_LOCOP,  /* O */
  0,            /* P */
  0,            /* Q */
  UMODE_REGMSG, /* R */
  UMODE_STEALTH,/* S */
  FLAGS_TECHADMIN,/* T */
  0,            /* U */
  FLAGS_VIPUSER,/* V */
  0,            /* W */
  UMODE_VADMIN, /* X */
  0,            /* Y */
  UMODE_ROOT, /* Z 0x5A */
  0, 0, 0, 0, 0, /* 0x5F */ 
  /* 0x60 */       0,
  FLAGS_SADMIN, /* a */
  FLAGS_BOTS,	/* b */
  FLAGS_CCONN,  /* c */
  FLAGS_DEBUG,  /* d */
  FLAGS_EXTERNAL,/* e */
  FLAGS_FULL,   /* f */
  0,            /* g */
  FLAGS_HELPER, /* h */
  FLAGS_INVISIBLE, /* i */
  FLAGS_REJ,    /* j */
  FLAGS_SKILL,  /* k */
  0,            /* l */
  0,            /* m */
  0, 			/* n */
  FLAGS_OPER,   /* o */
  UMODE_PRIVATE,/* p */
  0,            /* q */
  UMODE_IDENTIFIED, /* r */
  FLAGS_SERVNOTICE, /* s */
  0,            /* t */
  0,            /* u */
  UMODE_NODCC,  /* v */
  FLAGS_WALLOP, /* w */
  UMODE_VCOP,   /* x */
  FLAGS_SPY,    /* y */
  UMODE_ZOMBIE, /* z 0x7A */
  0,0,0,0,0,     /* 0x7B - 0x7F */

  /* 0x80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9F */
  /* 0x90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9F */
  /* 0xA0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xAF */
  /* 0xB0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xBF */
  /* 0xC0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xCF */
  /* 0xD0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xDF */
  /* 0xE0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xEF */
  /* 0xF0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0  /* 0xFF */
};

/*
 * m_svsmode()
 * parv[0] - sender
 * parv[1] - nick to change mode for
 * parv[2] - TS 	(optional, validated if numeric)
 * parv[3] - modes to change 
 * parv[4] - extra parameter for mode 
 */
int m_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
  aClient *acptr;
  int   what, setflags;
  int	flag;
  int 	extrapar;
  char 	*m;
  time_t ts=0;
  char newmask[HOSTLEN+USERLEN+2];
  Link *lp;
  struct Channel *chptr;

  if (!IsServer(cptr) || !IsService(sptr)) 
	{	
	  if (IsServer(cptr))
		{ 
		  ts_warn("Got SVSMODE from non-service: %s", 
			sptr->name);
		  sendto_one(cptr, ":%s WALLOPS :ignoring SVSMODE from non-service %s",
			me.name, sptr->name);
		}
	  return 0;
	}
  

  if ( parc < 4 || !IsDigit(*parv[2]) )
	{
	  int i = (++parc);
	  while ( i > 2 ) 
		{
		  parv[i] = parv[i-1];
		  --i;
		}
	  parv[2] = "0";
	}

	
  if ( parc < 4) /* missing arguments */
	{	  
	  ts_warn("Invalid SVSMODE (%s) from %s",
		(parc==2 ) ? parv[1]: "-", parv[0]);
  	  return 0;
	}

  if ((acptr = find_person(parv[1], NULL)) && MyClient(acptr)) /* person found connected here */
	{
	  
	  ts = atol(parv[2]);			  
	  if (ts && (ts != acptr->tsinfo))	/* This is not the person the mode was sent for */
		{
		  sendto_realops_flags(FLAGS_DEBUG, 
			"Unmatched SVSMODE tsinfo for %s", parv[1]); 
		  return 0;						/* just ignore it */
		}
	
	  extrapar = parc - 4; /* extra mode parameter(s) */
  	
	  setflags = acptr->umodes;	/* save current flags to generate mode change */
	  what = MODE_ADD;	
  
	  for (m = parv[3]; *m; m++)
	  switch(*m)
		{
  	  	  case '+' :
        	what = MODE_ADD;
      		break;
          case '-' :
            what = MODE_DEL;
        	  break;
		  case 'n':
			if(extrapar>0 && acptr->user)
			  {
				acptr->user->news_mask = strtoul(parv[3+extrapar],NULL,0);
				--extrapar;
			  }

			break;
         case 'V' :
              if (what == MODE_ADD) {
              char tmp[NICKLEN+HOSTLEN+USERLEN+4];
              strcpy(acptr->host,spoofed2(acptr));
              MyFree((char *)acptr->user->prefix);
              ircsprintf(tmp, "%s!%s@%s", acptr->name, acptr->username, acptr->host);
              acptr->user->prefix = strdup(tmp);
              sendto_serv_butone(&me, ":%s NEWMASK %s %s@%s", me.name, acptr->name, acptr->username, acptr->host);
              SetVipUser(acptr);
              }
              else if (what == MODE_DEL) {
              char tmp[NICKLEN+HOSTLEN+USERLEN+4];
              strcpy(acptr->host,spoofed(acptr));
              MyFree((char *)acptr->user->prefix);
              ircsprintf(tmp, "%s!%s@%s", acptr->name, acptr->username, acptr->host);
              acptr->user->prefix = strdup(tmp);
              sendto_serv_butone(&me, ":%s NEWMASK %s %s@%s", me.name, acptr->name, acptr->username, acptr->host);
              ClearVipUser(acptr);
              }
              break;
          case 'O': case 'o' :
	        if(what == MODE_ADD)
              {
	            if(IsServer(cptr) && !IsOper(acptr))
  	              {
    	            ++Count.oper;
      	            SetOper(acptr);
        	      }
          	  }
        	else
          	  {
		      /* Only decrement the oper counts if an oper to begin with
               * found by Pat Szuta, Perly , perly@xnet.com 
               */

  	            if(!IsAnOper(acptr))
    	          break;

      	        acptr->umodes &= ~(FLAGS_OPER|FLAGS_LOCOP);

        	    Count.oper--;

                if (MyConnect(acptr))
	              {
  	                aClient *prev_cptr = (aClient *)NULL;
  		            aClient *cur_cptr = oper_cptr_list;

      	            fdlist_delete(acptr->fd, FDL_OPER | FDL_BUSY);
        	        detach_conf(acptr,acptr->confs->value.aconf);
          	        acptr->flags2 &= ~(FLAGS2_OPER_GLOBAL_KILL|
                                    FLAGS2_OPER_REMOTE|
                                    FLAGS2_OPER_UNKLINE|
                                    FLAGS2_OPER_GLINE|
                                    FLAGS2_OPER_K);
    	            while(cur_cptr)
      	              {
        	            if(acptr == cur_cptr) 
          	              {
            	            if(prev_cptr)
              	              prev_cptr->next_oper_client = cur_cptr->next_oper_client;
                	        else
                  	          oper_cptr_list = cur_cptr->next_oper_client;
                            cur_cptr->next_oper_client = (aClient *)NULL;
	                        break;
  	                      }
      	                else
        	              prev_cptr = cur_cptr;
          	            cur_cptr = cur_cptr->next_oper_client;
            	      }
                  }
	          }
	        break;
			
            case 'E' :		
              if ((what == MODE_ADD) && (!IsIdentified(acptr))) {
                      sendto_one(acptr, ":%s NOTICE %s :Você não poderá enviar mensagens ou entrar em canais enquanto não identificar seu nick ou registra-lo. Para mais informações, por favor digite \002/NICKSERV HELP\002", MENAMEmy(acptr), acptr->name);
                      sendto_one(acptr, ":%s NOTICE %s :You won't be able to send messages or join a channel while your nickname is not identified or registered. For more information please type \002/NICKSERV HELP\002", MENAMEmy(acptr), acptr->name);

                      if (acptr->user->channel != NULL) {
                          while ((lp = acptr->user->channel))
                          {
                              chptr = lp->value.chptr;
                              sendto_channel_butserv(chptr, acptr, ":%s PART %s",
                                              acptr->name, chptr->chname);
                              remove_user_from_channel(acptr, chptr, 0);
                          }
                          sendto_serv_butone(&me, ":%s JOIN 0", acptr->name);
                      }
                      SetGringo(acptr);
              } else ClearGringo(acptr);
              break;
          case 'r' :
                 if (what == MODE_ADD) {
                     if (IsGringo(acptr))
                         ClearGringo(acptr);
                     SetIdentified(acptr);
                 } else ClearIdentified(acptr);
                 break;
 	        /* we may not get these,
  	         * but they shouldnt be in default
                 */
          case ' ' :
          case '\n' :
          case '\r' :
          case '\t' :
  	        break;
          case 'B':
              if (what == MODE_ADD)
                set_rhc_status(ntohl(acptr->ip.s_addr), CACHE_BOT);
          default :
	        if( (flag = user_modes_from_c_to_bitmask[(unsigned char)*m]))
  	          {
    	        if (what == MODE_ADD)
      	          acptr->umodes |= flag;
          	    else
        	      acptr->umodes &= ~flag;  
              }
			break;
  	    } /* switch */		

     /*
	  * compare new flags with old flags and send string which
	  * will cause servers to update correctly.
  	  */
	  send_umode_out(acptr, acptr, setflags);

	  newmask[0]='\0';

	  
	  if (IsNetAdmin(acptr) && NetAdminMask)
		strncpy(newmask, NetAdminMask, HOSTLEN+USERLEN+2);
	  else if (IsTechAdmin(cptr) && TechAdminMask)
		strncpy(newmask, TechAdminMask, HOSTLEN+USERLEN+2);
	  else if (IsSAdmin(acptr) && SAdminMask)
		strncpy(newmask, SAdminMask, HOSTLEN+USERLEN+2);
	  else if (IsHelper(acptr) && HelperMask)
		strncpy(newmask, HelperMask, HOSTLEN+USERLEN+2);
	  if(newmask[0])
		{
                  parv[0] = me.name;
                  parv[1] = acptr->name;
                  parv[2] = newmask;
                  return m_newmask(&me, &me, 3, parv);
		}
	  		  
	}
   else if (acptr && (acptr != cptr)) /* nick was found but is not our client */ 
	{
  	  if ( (acptr->from != cptr)) /* this should never happen */
		sendto_one(acptr, 
                  ":%s SVSMODE %s %s %s :%s", 
                        parv[0], parv[1], parv[2], parv[3],
		  (parc>4) ? parv[4] :"" );	  
	}
  return 0;
} /* m_svsmode */
