/**********************************************************************
 * PTlink IRC Services is (C) CopyRight PTlink IRC Software 1999-2006 *
 *                     http://software.pt-link.net                    *
 * This program is distributed under GNU Public License               *
 * Please read the file COPYING for copyright information.            *
 **********************************************************************  

  Description: chanserv set command

*/

#include "module.h"
#include "chanserv.h"
#include "nickserv.h"  /* need IsAuthenticated() */
#include "encrypt.h"
#include "chanrecord.h"
#include "nsmacros.h"
#include "cs_role.h" /* we need P_SET */
#include "ns_group.h" /* we need the is_sadmin() */
#include "my_sql.h"
#include "dbconf.h"
/* lang files */
#include "lang/common.lh"
#include "lang/cscommon.lh"
#include "lang/cs_set.lh"

SVS_Module mod_info =
/* module, version, description */
{"cs_set", "2.3", "chanserv set/sset command" };

/* Change Log
  2.3   #80 Chanserv sset on entrymsg now does not show only the first word
        #81 Chanserv sset with mlock now also acepts extra parameters    
  2.2 - 0000334: review code to use local bot when possible
  2.1 - 0000295: mlock support for +k,+l,+f
  2.0 - 0000278: secureops option on chanserv
        0000265: remove nickserv cache system
        0000261: mlock option
	0000281: no auth nick can't use chanserv
  1.2 - 0000258: chanserv topiclock option
  1.1 - 0000246: help display with group filter
*/
  
/* external functions we need */
ServiceUser* (*chanserv_suser)(void);
u_int32_t (*find_group)(char *name);

int DisableNickNickSecurityCode = 0; 
int ChanServNeedsAuth = 0;

MOD_REQUIRES
  MOD_FUNC(dbconf_get)
  MOD_FUNC(chanserv_suser)
  MOD_FUNC(role_with_permission)  
  MOD_FUNC(is_sadmin)
  MOD_FUNC(find_group)
MOD_END

/* internal functions */
void set_command(IRC_User *u, IRC_User *s, ChanRecord *cr, char *option, char *value, int is_sset);

void cs_set(IRC_User *s, IRC_User *u);
void cs_sset(IRC_User *s, IRC_User *u); /* sadmin set */

/* local variables */
ServiceUser* csu;
int cs_log;

/* Remote config */
static int NeedsAuth;
static int NickSecurityCode;
DBCONF_REQUIRES
  DBCONF_GET("chanserv", NeedsAuth)
  DBCONF_GET("nickserv", NickSecurityCode)
DBCONF_END

/* this is called before load and at services rehash */
int mod_rehash(void)
{
  if(dbconf_get(dbconf_requires) < 0 )
  {
    errlog("Error reading dbconf!");
    return -1;
  }
  return 0;
}

int mod_load(void)
{
  cs_log = log_handle("chanserv");
  csu = chanserv_suser();
  suser_add_cmd(csu, "SET", cs_set, SET_SUMMARY, SET_HELP);
  suser_add_cmd_g(csu, "SSET", cs_sset, SSET_SUMMARY, SSET_HELP,
    find_group("Admin"));
  /* we take care of the mlock, lets set the mlocker here */
  irc_SetChanMlocker(csu->u);
  return 0;
}

void mod_unload(void)
{
  suser_del_mod_cmds(csu, &mod_info);
}

#define FLAG_SET(x,y) \
  { \
    if(IsNull(value)) \
      send_lang(u, s, VALUE_ON_OR_OFF); \
    else \
    if(strcasecmp(value,"on") == 0) \
      { \
        log_log(cs_log, mod_info.name, "%s %s %s %s %s", \
          u->nick, is_sset ? "SSET" : "SET", cr->name, y, value); \
        cr->flags |= (x); \
        UpdateCR(cr); \
        send_lang(u, s, OPTION_X_ON, (y)); \
      } else \
    if(strcasecmp(value,"off")  == 0) \
      { \
        log_log(cs_log, mod_info.name, "%s %s %s %s %s", \
          u->nick, is_sset ? "SSET" : "SET", cr->name, y, value); \
        cr->flags &= ~(x); \
        UpdateCR(cr); \
        send_lang(u, s, OPTION_X_OFF, (y)); \
      } else \
      send_lang(u, s, VALUE_ON_OR_OFF); \
  }

#define STRING_SET(x,y,z) \
  { \
    FREE((x)); \
    if(IsNull(value)) \
      { \
        send_lang(u, s, (y)); \
        log_log(cs_log, mod_info.name, "%s %s %s %s", \
          u->nick, is_sset ? "SSET" : "SET", cr->name, option); \
      } \
    else \
      { \
        log_log(cs_log, mod_info.name, "%s %s %s %s %s", \
          u->nick, is_sset ? "SSET" : "SET", cr->name, option, value); \
        (x) = strdup(value); \
        send_lang(u, s, (z), value); \
      } \
    UpdateCR(cr); \
  }  
  
/* handles a set command */
void set_command(IRC_User *u, IRC_User *s, ChanRecord *cr, char *option, char *value, int is_sset)
{
  
  if(strcasecmp(option,"URL") == 0)
    STRING_SET(cr->url, URL_UNSET, URL_CHANGED_TO_X)
  else if(strcasecmp(option,"ENTRYMSG") == 0)
    STRING_SET(cr->entrymsg, ENTRYMSG_UNSET, ENTRYMSG_CHANGED_TO_X)    
  else if(strcasecmp(option,"DESC") == 0)
    STRING_SET(cr->cdesc, DESC_UNSET, DESC_CHANGED_TO_X)
  else if(strcasecmp(option,"PRIVATE") == 0)
    FLAG_SET(CFL_PRIVATE, "PRIVATE")
  else if(strcasecmp(option,"OPNOTICE") == 0)
    FLAG_SET(CFL_OPNOTICE, "OPNOTICE")
  else if(strcasecmp(option,"RESTRICTED") == 0)
    FLAG_SET(CFL_RESTRICTED, "RESTRICTED")
  else if(strcasecmp(option,"TOPICLOCK") == 0)
    FLAG_SET(CFL_TOPICLOCK, "TOPICLOCK")
  else if(strcasecmp(option,"SECUREOPS") == 0)
    FLAG_SET(CFL_SECUREOPS, "SECUREOPS")    
  else if(strcasecmp(option,"MLOCK") == 0)
  {
    int r = 0;
    IRC_Chan *chan;
    if(value) /* lets remove duplicates from the string */
    {
      char *c = value;
      char *d;
      char *save = save = strchr(value, ' ');
      if(save)
        *save = '\0';
      while(*c && *c!=' ') /* don't check dup letters on params */
      {
        while((d = strchr(c+1, *c)))
        {
          int i = strlen(value)-1;
          *d = value[i];
          value[i] = '\0';
        }
        ++c;
      }
    if(save)
      *save = ' ';
    }

    chan = irc_FindChan(cr->name);
    r = irc_ChanMLockSet(s, chan, value ? value : "");
    switch(r)
    {
      case -1: send_lang(u, s, INVALID_MLOCK_LETTER); break;
      case -2: send_lang(u, s, MISSING_MLOCK_PARAMETER); break;      
      case -3: send_lang(u, s, INVALID_MLOCK_PARAMETER); break;
      case -4: send_lang(u, s, EXTRA_MLOCK_PARAMETER); break;
      case -5: send_lang(u, s, INVALID_MLOCK_CONFLICT); break;

      default:
        STRING_SET(cr->mlock, MLOCK_UNSET, MLOCK_CHANGED_TO_X)
        if(chan)
          irc_ChanMLockApply(chan->local_user ? chan->local_user : s, chan);
      break;
    }
  }
  else if(strcasecmp(option,"FOUNDER") == 0)
    {
      u_int32_t snid = u->snid;
      char* nick_sec = NULL;
      if(!is_sset && cr->founder != snid)
        {
          send_lang(u, s, ONLY_FOUNDER_X, cr->name);
          return;
        }    
      if(sql_singlequery("SELECT securitycode FROM nickserv_security WHERE snid=%d",
      	u->snid))
      {
      	if(sql_field(0))
        {
          nick_sec = malloc(16);
          memcpy(nick_sec, hex_bin(sql_field(0)), 16);
        }
      } 
      if(!is_sset && NickSecurityCode && nick_sec && IsAuthenticated(u))
        {
          char* securitycode = strtok(NULL, " ");
          if(IsNull(securitycode))
            {
              send_lang(u, s, SET_FOUNDER_SECURITY_REQUIRED);
	      FREE(nick_sec);
              return;
            }
          else if(memcmp(nick_sec, encrypted_password(securitycode), 16) != 0)
            {
              send_lang(u, s, INVALID_SECURITY_CODE);
	      FREE(nick_sec);
              return;
            }
        }
      FREE(nick_sec);
      /* syntax validation */ 
      if(IsNull(value))
        send_lang(u, s, SET_FOUNDER_SYNTAX);
      /* check requirements */
      else if((snid = nick2snid(value)) == 0)
        send_lang(u, s, NICK_X_NOT_REGISTERED, value);
      else
        {
	  if(snid == cr->successor)
            send_lang(u, s, ALREADY_SUCCESSOR);          
          else if(snid == cr->founder)
            send_lang(u, s, ALREADY_FOUNDER);
          else 
	    {
	      cr->founder = snid;
	      UpdateCR(cr);
	      send_lang(u, s, FOUNDER_X_CHANGED_X, cr->name, value);
	    }
        }
    }
  else if(strcasecmp(option,"SUCCESSOR") == 0)
    {
      char *nick_sec = NULL;
      u_int32_t snid = u->snid;
      if(!is_sset && cr->founder != snid)
      	{
	  send_lang(u, s, ONLY_FOUNDER_X, cr->name);
	  return;
	}   
      if(sql_singlequery("SELECT securitycode FROM nickserv_security WHERE snid=%d",
      	u->snid))
      {
      	if(sql_field(0))
        {
          nick_sec = malloc(16);
          memcpy(nick_sec, hex_bin(sql_field(0)), 16);
        }
      } 
      if(!is_sset && NickSecurityCode && nick_sec && IsAuthenticated(u))
        {
          char* securitycode = strtok(NULL, " ");
          if(IsNull(securitycode))
            {
              send_lang(u, s, SET_SUCCESSOR_SECURITY_REQUIRED);
	      FREE(nick_sec);
              return;
            }
          else if(memcmp(nick_sec, encrypted_password(securitycode), 16) != 0)
            {
              send_lang(u, s, INVALID_SECURITY_CODE);
	      FREE(nick_sec);
              return;
            }
        }
      FREE(nick_sec); 

    if(IsNull(value))
      send_lang(u, s, SET_SUCCESSOR_SYNTAX);
    else if((snid = nick2snid(value)) == 0)
      send_lang(u, s, NICK_X_NOT_REGISTERED, value);
    else
    {
    	if(snid == cr->founder)
    	  send_lang(u, s, ALREADY_FOUNDER);
	else if(snid == cr->successor)
	  send_lang(u, s, ALREADY_SUCCESSOR);
	else
	  {
	    cr->successor =snid;
	    UpdateCR(cr);
	    send_lang(u, s, SUCCESSOR_X_CHANGED_X, cr->name, value);
	  }
    }
  }
  else if(strcasecmp(option,"EMAIL") == 0)
    {
      if(value && !is_email(value))
        {
          send_lang(u, s, INVALID_EMAIL);
          return ;
        }
      FREE(cr->email);
      if(IsNull(value))
        {
          cr->email = NULL;
          send_lang(u, s, EMAIL_UNSET);
        }
      else
        {
          cr->email = strdup(value);
          send_lang(u, s, EMAIL_CHANGED_TO_X, value);
        }
      UpdateCR(cr);
    }
  else if(is_sset == 0)
    send_lang(u, s, UNKNOWN_OPTION_X, option);
  else if(strcasecmp(option,"NOEXPIRE") == 0)
    FLAG_SET(CFL_NOEXPIRE, "NOEXPIRE")     
  else
    send_lang(u, s, UNKNOWN_OPTION_X, option);
}

#undef STRING_SET
#undef FLAG_SET
 
/* s = service the command was sent to
   u = user the command was sent from */
void cs_set(IRC_User *s, IRC_User *u)
{
  u_int32_t source_snid;
  ChanRecord *cr;
  char *option, *value;
  char *chname;

  CHECK_IF_IDENTIFIED_NICK  
  chname = strtok(NULL, " ");
  option = strtok(NULL, " ");
  if(!IsNull(option) && (
    (strcasecmp(option, "DESC") == 0) ||
    (strcasecmp(option, "ENTRYMSG") == 0) ||
    (strcasecmp(option, "MLOCK") == 0)
    ))
    value = strtok(NULL, "");
  else
    value = strtok(NULL, " ");
            
  if(ChanServNeedsAuth && !IsAuthenticated(u))
    send_lang(u, s, NEEDS_AUTH_NICK);
  else
  if(IsNull(chname) || IsNull(option))
    send_lang(u, s, CHAN_SET_SYNTAX);
  else
  if((cr = OpenCR(chname)) == NULL)
    send_lang(u, s, CHAN_X_NOT_REGISTERED, chname);
  else
    {
      if(role_with_permission(cr->scid, source_snid, P_SET) == 0)
          send_lang(u, s, NO_SET_PERM_ON_X, chname); 
      else /* everything is valid lets check the auth */
        {
          set_command(u, s, cr, option, value, 0);
        }
      CloseCR(cr);
    }
}

/* s = service the command was sent to
   u = user the command was sent from */
void cs_sset(IRC_User *s, IRC_User *u)
{
  ChanRecord *cr;
  u_int32_t source_snid;
  char *chname, *option, *value;
  
  chname = strtok(NULL, " ");
  option = strtok(NULL, " ");  
  
  if(!IsNull(chname) && !IsNull(option) && (
    (strcasecmp(option, "DESC") == 0) ||
    (strcasecmp(option, "ENTRYMSG") == 0) ||
    (strcasecmp(option, "MLOCK") == 0)
    ))
    value = strtok(NULL, "");
  else
    value = strtok(NULL, " ");

  CHECK_IF_IDENTIFIED_NICK
  
  if(IsNull(chname) || IsNull(option))
    send_lang(u, s, CHAN_SSET_SYNTAX);
  else
  if(!is_sadmin(source_snid))
    send_lang(u, s, ONLY_FOR_SADMINS);
  else  
  if((cr = OpenCR(chname)) == NULL)
    send_lang(u, s, CHAN_X_NOT_REGISTERED, chname);
  else
    {
      set_command(u, s, cr, option, value, 1);
      CloseCR(cr);
    }
}



syntax highlighted by Code2HTML, v. 0.9.1