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

  Description: nickserv group command

 *  $Id: ns_group.c,v 1.18 2005/10/18 16:25:06 jpinto Exp $
*/

#include "module.h"
/* modules we depend on */
#include "my_sql.h"
#include "nsmacros.h"
#include "dbconf.h"
/* lang files */
#include "lang/common.lh"
#include "lang/ns_group.lh"

SVS_Module mod_info =
/* module, version, description */
{"ns_group", "3.2", "nickserv group command" };

#define DB_VERSION 	3

/* Change Log
  3.2 - #22 : e_nick_recognize to distinguish +r nick recognitions
  3.1 - 0000350: ns_group subcommands detailed help
        0000337: Option to set maxusers on groups
        0000328: group members can be added with a given expire time
          Added ns_group.3.sql changes
  3.0 - 0000308: group names can have "@server" for "from server" restriction
        0000307: support for auto user modes on groups
        0000305: foreign keys for data integrity
          Added ns_group.2.sql changes
  2.0 - 0000265: remove nickserv cache system
  1.2 - we don't need irc_lower()
  1.1 - 0000247: cache nick groups info
*/

#define ED_GROUPS       0
    
/** functions and events we require **/
ServiceUser* (*nickserv_suser)(void);
static int e_nick_identify;
static int e_nick_recognize;
static int e_expire;


MOD_REQUIRES
  DBCONF_FUNCTIONS
  MOD_FUNC(e_expire) /* we need this to run the expire routines */
  MOD_FUNC(nickserv_suser)
  MOD_FUNC(e_nick_identify)
  MOD_FUNC(e_nick_recognize)
MOD_END

/* functions and events we provide */
int is_sadmin(u_int32_t snid);
int is_sroot(u_int32_t snid);
int is_soper(u_int32_t snid);
u_int32_t find_group(char *name);
int is_member_of(u_int32_t snid, u_int32_t sgid);

MOD_PROVIDES
   MOD_FUNC(is_sadmin)
   MOD_FUNC(is_sroot)
   MOD_FUNC(is_soper)
   MOD_FUNC(find_group)
   MOD_FUNC(is_member_of)
MOD_END

/** Internal functions declaration **/
void create_core_groups(void);
void ns_group(IRC_User *s, IRC_User *u);
int is_member_of_online(IRC_User *user, u_int32_t sgid);
int group_create(char *name, u_int32_t master_sgid, char *gdesc, char *umodes);
int add_to_group(u_int32_t sgid, u_int32_t snid, time_t t_expire);
int del_from_group(u_int32_t sgid, u_int32_t snid);
int drop_group(u_int32_t sgid);
int is_master(u_int32_t snid, u_int32_t sgid);
int ev_ns_group_nick_identify(IRC_User* u, u_int32_t* snid);
u_int32_t find_group(char *name);
int ev_ns_group_expire(void* dummy1, void* dummy2);
int group_is_full(u_int32_t sgid);

/** Local variables **/
ServiceUser *nsu;
int ns_log;

/* Remote config */
static char* Root;
DBCONF_REQUIRES
  DBCONF_GET("nickserv", Root)
DBCONF_END
/* Local config*/
static int ExpireWarningTime;
DBCONF_PROVIDES
  DBCONF_TIME(ExpireWarningTime, "5d",
    "Warn users with group membership exping in less than ExpireWarningTime")
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;
  }

  if(dbconf_get_or_build(mod_info.name, dbconf_provides) < 0 )
  {
    errlog("Error reading dbconf!");
    return -1;
  }  
  return 0;
}

/* module load code */
int mod_load(void)
{
  int r;
  
  ns_log = log_handle("nickserv");
  
  r = sql_check_inst_upgrade(mod_info.name, DB_VERSION, NULL);
  
  if(r < 0)
    return -4;
  else if(r == 1) /* table was installed */
    create_core_groups();
  else
    {
      if(Root)
        stdlog(L_WARN, "Root is defined, please add a nick to the Root group and disable the setting");
    }
      
  nsu = nickserv_suser();
  
  suser_add_cmd(nsu, "GROUP", ns_group, NS_GROUP_SUMMARY, NS_GROUP_HELP);

  /* Add event actions */    
  mod_add_event_action(e_nick_identify, (ActionHandler) ev_ns_group_nick_identify);
  mod_add_event_action(e_nick_recognize, (ActionHandler) ev_ns_group_nick_identify);
  mod_add_event_action(e_expire, (ActionHandler) ev_ns_group_expire);

  /* Add subcommands help */ 
  suser_add_help(nsu, "GROUP ADD", NS_GROUP_ADD_HELP);
  suser_add_help(nsu, "GROUP CREATE", NS_GROUP_CREATE_HELP);
  suser_add_help(nsu, "GROUP DROP", NS_GROUP_DROP_HELP);
  suser_add_help(nsu, "GROUP DEL", NS_GROUP_DEL_HELP);
  suser_add_help(nsu, "GROUP INFO", NS_GROUP_INFO_HELP);
  suser_add_help(nsu, "GROUP LIST", NS_GROUP_LIST_HELP);
  suser_add_help(nsu, "GROUP SET", NS_GROUP_SET_HELP);
  suser_add_help(nsu, "GROUP SHOW", NS_GROUP_SHOW_HELP);  
  
  return 0;
}

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

/** internal functions implementation starts here **/

#define STRING_SET(x,y,z) \
  { \
    if(IsNull(value)) \
      { \
        log_log(ns_log, mod_info.name, "%s GROUP %s UNSET %s", \
          u->nick, gname, option);\
        send_lang(u, s, (y), gname); \
      } \
    else \
      { \
        log_log(ns_log, mod_info.name, "%s GROUP %s SET %s %s", \
          u->nick, gname, option, value);\
        send_lang(u, s, (z), gname, value); \
      } \
     sql_execute("UPDATE ns_group SET %s=%s "\
       "WHERE sgid=%d", (x), sql_str(value), sgid);\
  }

#define INT_SET(x,y) \
  { \
    if(IsNull(value)) \
      send_lang(u, s, REQUIRES_NUMERIC_X, option); \
    else \
      { \
        log_log(ns_log, mod_info.name, "%s GROUP %s SET %s %d", \
          u->nick, gname, option, atoi(value));\
        send_lang(u, s, (y), gname, atoi(value)); \
        sql_execute("UPDATE ns_group SET %s=%d " \
          "WHERE sgid=%d", (x), atoi(value), sgid); \
      } \
  }

/* s = service the command was sent to
   u = user the command was sent from */
void ns_group(IRC_User *s, IRC_User *u)
{
  u_int32_t source_snid;
  u_int32_t snid;
  char *cmd;
  char *gname;
  char *nick;
  int memberc = 0;
  u_int32_t master_sgid;
  u_int32_t sgid;

  CHECK_IF_IDENTIFIED_NICK
  
  cmd = strtok(NULL, " ");
  gname = strtok(NULL, " ");
  
  /* base syntax validation */
  if(IsNull(cmd))
    send_lang(u, s, NS_GROUP_SYNTAX);
  else if(strcasecmp(cmd,"CREATE") == 0)
    {
      char *master;
      char *gdesc;
      char *umodes = NULL;
      master = strtok(NULL, " ");
      gdesc = strtok(NULL, ""); 
      if(gname) /* first check if the name contains umodes */
      {
        char *pumodes;
        char *eumodes;
        pumodes = strchr(gname,'[');
        if(pumodes && pumodes[0])
        {
          *(pumodes++) = '\0';
          eumodes = strchr(pumodes,']');
          if(eumodes)
          {
            *eumodes = '\0';
            umodes = pumodes;
          }
        }
      }
      /* syntax validation */
      if(IsNull(gname) || IsNull(master))
        send_lang(u, s, NS_GROUP_CREATE_SYNTAX);
      /* permissions validation */
      else if(!is_sroot(source_snid))
        send_lang(u, s, NICK_NOT_ROOT);
      /* check requirements */
      else if((master_sgid = find_group(master)) == 0)
        send_lang(u, s, NS_GROUP_MASTER_NOT_FOUND, master);
      /* avoid duplicates */
      else if((sgid = find_group(gname)) != 0)
        send_lang(u, s, NS_GROUP_ALREADY_EXISTS, gname);
      /* execute operation */
      else if(group_create(gname, master_sgid, gdesc, umodes) > 0)
      /* report operation status */
        send_lang(u, s, NS_GROUP_CREATE_OK, gname);
      else 
        send_lang(u, s, UPDATE_FAIL);
    }
  else if(strcasecmp(cmd,"ADD") == 0)
    {
      u_int32_t duration = 0;
      time_t master_expire = 0;              
      u_int32_t is_master_sgid;
      char *duration_str;
      
      nick = strtok(NULL, " ");
      duration_str =  strtok(NULL, " ");
      if(duration_str)
        duration = time_str(duration_str);
        
      /* syntax validation */
      if(IsNull(gname) || IsNull(nick))
        send_lang(u, s, NS_GROUP_ADD_SYNTAX);
      /* check requirements */
      else if((snid = nick2snid(nick)) == 0)
        send_lang(u, s, NO_SUCH_NICK_X, nick);      
      else if((sgid = find_group(gname)) == 0)
        send_lang(u, s, NO_SUCH_GROUP_X, gname);
      /* privileges validation */
      else if(group_is_full(sgid))
        send_lang(u, s, NS_GROUP_IS_FULL_X);
      else if(((is_master_sgid = is_master(source_snid, sgid))== 0) 
        && !is_sroot(source_snid))
          send_lang(u, s, NOT_MASTER_OF_X, gname);
      /* avoid duplicates */
      else if(sql_singlequery("SELECT t_expire FROM ns_group_users "
        " WHERE sgid=%d AND snid=%d", is_master_sgid, source_snid)
        && (master_expire = sql_field_i(0)) && duration)
        send_lang(u, s, NS_GROUP_CANT_DEFINE_TIME_X, gname);
      else if(is_member_of(snid, sgid))
        send_lang(u, s, NICK_X_ALREADY_ON_X, nick, gname);
      /* execute operation */
      else
      {
        time_t t_expire = 0;
        if(master_expire)
          t_expire = master_expire;
        else if(duration)
          t_expire = irc_CurrentTime + duration;
        if(add_to_group(sgid, snid, t_expire) > 0)
        /* report operation status */
        {   
          char *server = strchr(gname, '@');      
          IRC_User *user = irc_FindUser(nick);   
          send_lang(u, s, NICK_ADDED_X_X, nick, gname);
          if(server) /* we have a server rule to be validated */
            ++server;
          if(user && (!server || (strcasecmp(server,u->server->sname) == 0)))
          {
            if(user->extra[ED_GROUPS] == NULL)
            {
              user->extra[ED_GROUPS] = malloc(sizeof(darray));
              array_init(user->extra[ED_GROUPS], 1, DA_INT);
            }
            array_add_int(user->extra[ED_GROUPS], sgid);
          }
        }
      	else
          send_lang(u, s, UPDATE_FAIL);        
      }
    }
  else if(strcasecmp(cmd,"DEL") == 0)
    {
      nick = strtok(NULL, " ");
      /* syntax validation */ 
      if(IsNull(gname) || IsNull(nick))
        send_lang(u, s, NS_GROUP_DEL_SYNTAX);
      /* check requirements */
      else if((sgid = find_group(gname)) == 0)
        send_lang(u, s, NO_SUCH_GROUP_X, gname);
      else if((snid = nick2snid(nick)) == 0)
        send_lang(u, s, NO_SUCH_NICK_X, nick);        
      /* privileges validation */
      else if(!is_sroot(source_snid) && !is_master(source_snid, sgid))
        send_lang(u, s, NOT_MASTER_OF_X, gname);
      else if(!is_member_of(snid, sgid))
        send_lang(u, s, NICK_X_NOT_ON_GROUP_X, nick, gname);
      /* execute operation */
      else if(del_from_group(sgid, snid) > 0)
      /* report operation status */
        {
          IRC_User *user = irc_FindUser(nick);
          send_lang(u, s, NICK_DEL_X_X, nick, gname);
          if(user)
            array_del_int(user->extra[ED_GROUPS], sgid);
        }
      else
        send_lang(u, s, UPDATE_FAIL);
    }
  else if(strcasecmp(cmd,"INFO") == 0)
    {
      /* syntax validation */
      if(IsNull(gname))
        send_lang(u, s, NS_GROUP_INFO_SYNTAX);
      /* check requirements */
      else if((sgid = find_group(gname)) == 0)
        send_lang(u, s, NO_SUCH_GROUP_X, gname);
      /*  check privileges */
      else if(!is_master(source_snid, sgid) && 
              !is_member_of(source_snid, sgid))
        send_lang(u, s, NOT_MASTER_OR_MEMBER_X, gname);      
      else if((sgid = find_group(gname))) /* we need to get the group description */
        {
          /* execute operation */  
          MYSQL_RES* res;
          master_sgid = 0;
          sql_singlequery("SELECT gdesc, master_sgid FROM ns_group WHERE sgid=%d", 
            sgid);  
          send_lang(u, s, NS_GROUP_INFO_X, gname);        
          if(sql_field(0))
            send_lang(u, s, NS_GROUP_INFO_DESC_X, sql_field(0));
          master_sgid = sql_field_i(1);
          if(master_sgid != 0)
          {
            if(sql_singlequery("SELECT name FROM ns_group WHERE sgid=%d", 
                master_sgid) > 0)
            {
              send_lang(u, s, NS_GROUP_INFO_MASTER_X, sql_field(0));              
            }
          }
          res = sql_query("SELECT n.nick, gm.t_expire FROM "
            "nickserv n, ns_group_users gm WHERE gm.sgid=%d AND n.snid=gm.snid", 
            sgid);
          if(sql_next_row(res) == NULL)
            send_lang(u, s, NS_GROUP_EMPTY);
          else
          {
            do
            {
      				char buf[64];
      				struct tm *tm;
      				time_t t_expire = sql_field_i(1);
							buf[0] = '\0';
							if(t_expire)
							{
      					tm = localtime(&t_expire);
      					strftime(buf, sizeof(buf), format_str(u, DATE_FORMAT), tm);
              	send_lang(u,s, NS_GROUP_ITEM_X_X, sql_field(0), buf);
							} else
                send_lang(u,s, NS_GROUP_ITEM_X, sql_field(0));
              ++memberc;
            } while(sql_next_row(res));
            send_lang(u, s, NS_GROUP_MEMBERS_TAIL_X, memberc);
          }
          sql_free(res);
        }  
    }
  else if(strcasecmp(cmd,"DROP") == 0)
    {
      /* syntax validation */
      if(IsNull(gname))
        send_lang(u, s, NS_GROUP_DROP_SYNTAX);
      /* privileges validation */
      else if(!is_sroot(source_snid))
        send_lang(u, s, NICK_NOT_ROOT);
      /* check requirements */
      else if((sgid = find_group(gname)) == 0)
        send_lang(u, s, NO_SUCH_GROUP_X, gname);
      /* NOTE: The following sql_field( depends on previous find_group( */
      else if(!sql_field(2) || (master_sgid = atoi(sql_field(2))) == 0)
        send_lang(u, s, CANT_DROP_ROOT);        
      /* execute operation */
      else if(drop_group(sgid)>0)      
      /* report operation status */
        send_lang(u, s, NS_GROUP_DROPPED_X, gname);
      else
        send_lang(u, s, UPDATE_FAIL);
    }
  else if(strcasecmp(cmd,"LIST") == 0) /* List groups */
    {
      MYSQL_RES* res;
      MYSQL_ROW row;
      /* privileges validation */
      if(!is_sroot(source_snid))
        send_lang(u, s, NICK_NOT_ROOT);
      else 
        {
          res = sql_query("SELECT name, master_sgid, gdesc FROM ns_group");
          send_lang(u, s, NS_GROUP_LIST_HEADER);
          while((row = sql_next_row(res)))
            {
              char* mname = "";
              if(row[1] && sql_singlequery("SELECT name FROM ns_group WHERE sgid=%d", 
                atoi(row[1])) > 0)
                mname = sql_field(0);          
              send_lang(u, s, NS_GROUP_LIST_X_X_X, row[0], mname, 
                row[2] ? row[2] : "");
            }
          send_lang(u, s, NS_GROUP_LIST_TAIL);
          sql_free(res);          
        }
    }
  else if(strcasecmp(cmd,"SHOW") == 0) /* Show groups we belong to */
    {
      /* groups count */
      int gc = array_count(u->extra[ED_GROUPS]);
      if(gc == 0)
        send_lang(u, s, NO_GROUPS);
      else
      {
        MYSQL_RES *res;
        MYSQL_ROW row;
        char buf[64];
        struct tm *tm;
        time_t t_expire;
#if 0        
        int i;        
        u_int32_t* data = array_data_int(u->extra[ED_GROUPS]);
#endif        
        send_lang(u, s, NS_GROUP_SHOW_HEADER);
#if 0        
        for(i = 0; i < gc; ++i)
        {
          if(sql_singlequery("SELECT name,gdesc FROM ns_group WHERE sgid=%d", 
            data[i]) > 0 )              
              send_lang(u, s, NS_GROUP_SHOW_X_X, sql_field(0), 
                sql_field(1) ? sql_field(1) : "");
        }
#endif
        res = sql_query("SELECT g.name, g.gdesc, gu.t_expire FROM ns_group g, ns_group_users gu"
          " WHERE gu.snid=%d AND g.sgid=gu.sgid ORDER BY g.master_sgid",
          source_snid);
        while((row = sql_next_row(res)))
        {
          t_expire = sql_field_i(2);
					buf[0] = '\0';
					if(t_expire)
					{
      		  tm = localtime(&t_expire);
            strftime(buf, sizeof(buf), format_str(u, DATE_FORMAT), tm);
            send_lang(u,s, NS_GROUP_SHOW_X_X_X, row[0], row[1] ? row[1] : "", buf);        
          }
          else
            send_lang(u, s, NS_GROUP_SHOW_X_X, row[0], row[1] ? row[1] : "");
        }
        send_lang(u, s, NS_GROUP_SHOW_TAIL);
        sql_free(res);
      }
    }
  else if(strcasecmp(cmd,"SET") == 0)
  {
    char *option;
    char *value ;
    option = strtok(NULL, " ");
    value = strtok(NULL, " ");
    /* syntax validation */
    if(IsNull(gname) || IsNull(option))
      send_lang(u, s, NS_GROUP_SET_SYNTAX);
    /* privileges validation */
    else if(!is_sroot(source_snid))
      send_lang(u, s, NICK_NOT_ROOT);
    /* check requirements */
    else if((sgid = find_group(gname)) == 0)
      send_lang(u, s, NO_SUCH_GROUP_X, gname);
    else
    {
      if(strcasecmp(option,"AUTOMODES") == 0)
        STRING_SET("autoumodes", AUTOMODES_X_UNSET, AUTOMODES_X_CHANGED_TO_X)
      else if(strcasecmp(option,"DESC") == 0)
        STRING_SET("gdesc", DESC_X_UNSET, DESC_X_CHANGED_TO_X)
      else if(strcasecmp(option, "MAXUSERS") == 0)
        INT_SET("maxusers", NS_GROUP_SET_MAXUSERS_SET_X_X)
      else
        send_lang(u, s, SET_INVALID_OPTION_X, option);
    }
  }
  else
    send_lang(u, s, NS_GROUP_SYNTAX);
}

/**
  Checks if a given nick is services root 
  Ret:
    0 = no
    >0 = yes
*/
int is_sadmin(u_int32_t snid)
{

  if(snid)
    return is_member_of(snid, find_group("Admin"));
    
  return 0;
};

/**
  Checks if a given snid is services root 
  Ret:
    0 = no
    >0 = yes
*/
int is_sroot(u_int32_t snid)
{
  if(snid == 0)
    return 0;

  if(Root)
  { 
    u_int32_t root_snid = nick2snid(Root);
    if(root_snid && (root_snid == snid))
      return 1;
  }
    
  return is_member_of(snid, find_group("Root"));  
}

/**
  Checks if a given snid is oper
  Ret:
    0 = no
    >0 = yes
*/
int is_soper(u_int32_t snid)
{
  if(snid == 0)
    return 0;
    
  if(Root)
  { 
    u_int32_t root_snid = nick2snid(Root);
    if(root_snid && (root_snid == snid))
      return 1;
  }      
  
  return is_member_of(snid, find_group("Oper"));  
}

void create_core_groups(void)
{
  u_int32_t lastg;
  log_log(ns_log, mod_info.name, "Creating core groups");
  lastg = group_create("Root", 0, "Services Root", NULL);
  lastg = group_create("Admin", lastg, "Services Administrators", NULL);
  lastg = group_create("Oper", lastg, "Services Operators", NULL);
}

int is_member_of(u_int32_t snid, u_int32_t sgid)
{
  MYSQL_RES *res;
  MYSQL_ROW row;
  int is = 0;
  res = sql_query("SELECT snid FROM ns_group_users WHERE snid=%d and sgid=%d",
      snid, sgid);
  row = sql_next_row(res);
  if(row)
    is = 1;
  sql_free(res);
  return is;
}

int is_member_of_online(IRC_User* user, u_int32_t sgid)
{
  return (array_find_int(user->extra[ED_GROUPS], sgid) != -1);
}

/* Creates a nick group 
 Returns: 0<= on error
     1 on sucess
*/
int group_create(char *name, u_int32_t master_sgid, char *gdesc, char *umodes)
{

  if(strlen(name) > 128) /* truncate description */
    name[32] = '\0';  
 
  if(gdesc && strlen(gdesc) > 128) /* truncate description */
    gdesc[255] = '\0';    
  
  /* all conditions are met, lets create the group */
  return sql_execute("INSERT INTO ns_group (name, master_sgid, gdesc, autoumodes, maxusers)"
    "VALUES (%s, %d, %s, %s, 9999)", sql_str(name), master_sgid, sql_str(gdesc),
      umodes ? sql_str(umodes) : "NULL");

}

int add_to_group(u_int32_t sgid, u_int32_t snid, time_t t_expire)
{
  
  return sql_execute("INSERT INTO ns_group_users (sgid, snid, t_expire) VALUES (%d, %d, %d)",
    sgid, snid, t_expire);
}

int del_from_group(u_int32_t sgid, u_int32_t snid)
{
  return sql_execute("DELETE FROM ns_group_users WHERE sgid=%d AND snid=%d",
   sgid, snid);
}

int drop_group(u_int32_t sgid)
{
  u_int32_t master_sgid = 0;
  
  /* first retrieve the group master */
  if(sql_singlequery("SELECT master_sgid from ns_group WHERE sgid=%d", sgid) == 0)
    {
      log_log(ns_log, mod_info.name, "Attempt to drop masterless groupd %d", sgid);
      return 0;
    }  
  if(sql_field(0))
    master_sgid = atoi(sql_field(0));
  /* if it is master of some role, point his master to our own master */
  sql_execute("UPDATE ns_group SET master_sgid=%d WHERE master_sgid=%d", 
    master_sgid, sgid);
  return sql_execute("DELETE FROM ns_group WHERE sgid=%d", sgid);
}           


/** checks if a given snid is master of a given sgid
  Returns: 0 if not, sgid of the group which makes it master
 */
int is_master(u_int32_t snid, u_int32_t sgid)
{

  MYSQL_RES *res;
  MYSQL_ROW row;
  
  while(sgid != 0)
    {
      res = sql_query("SELECT master_sgid FROM ns_group WHERE sgid=%d", sgid);
      row = sql_next_row(res);
      if(row == NULL)
      {
        sql_free(res);
        return 0; /* reached a masterless group */
      }
        
      if(row[0] && atoi(row[0]))
        sgid = atoi(row[0]);
      else 
        sgid = 0;
      sql_free(res);
      
      /* check if snid is member of master */
      res = sql_query("SELECT snid FROM ns_group_users WHERE sgid=%d AND snid=%d",
          sgid, snid);
      
      if(sql_next_row(res))
      {
        sql_free(res);
        return sgid; /* found */
      }
      sql_free(res);
    }
    
  return 0;
}

/* this event is called when a nick is identified 
   we will load the groups a nick belongs to
*/
int ev_ns_group_nick_identify(IRC_User* u, u_int32_t* snid)
{

  MYSQL_RES* res;
  MYSQL_ROW row;
  int rowc = 0;
  res = sql_query("SELECT gu.sgid, g.autoumodes, g.name, gu.t_expire FROM ns_group_users gu, ns_group g WHERE gu.snid=%d"
    " AND g.sgid=gu.sgid",
    u->snid);
  if(res)
    rowc = mysql_num_rows(res);
  if(u->extra[ED_GROUPS] != NULL)
    array_free(u->extra[ED_GROUPS]);
  u->extra[ED_GROUPS] = malloc(sizeof(darray));
  array_init(u->extra[ED_GROUPS], rowc, DA_INT);
  while((row = sql_next_row(res)))
  {
    char *gname = row[2];
    char *server = strchr(gname, '@');
    time_t t_expire = atoi(row[3]);
    if(server) /* we have a server rule to be validated */
    {
      ++server;
      if(strcasecmp(server, u->server->sname) && 
      (u->vlink && strcasecmp(server, u->vlink)))
        continue;
    }
    if(t_expire && ExpireWarningTime &&
      ((t_expire - irc_CurrentTime) < ExpireWarningTime))
      send_lang(u, nsu->u, NS_GROUP_X_EXPIRING_X,
        gname, (t_expire - irc_CurrentTime)/(24*3600));
    array_add_int(u->extra[ED_GROUPS], atoi(row[0]));
    if(row[1] && row[1][0])
      irc_SvsMode(u, nsu->u, row[1]);
  }
  sql_free(res);
  
  return 0;
}

/* check if a given group reached maxusers */
int group_is_full(u_int32_t sgid)
{
  int maxusers = 0;
  if(sql_singlequery("SELECT maxusers FROM ns_group WHERE sgid=%d", sgid))
    maxusers = sql_field_i(0);
  else
    return 1; /* this should never happen */
    
  if(maxusers == 0)
    return 0;
    
  sql_singlequery("SELECT count(*) FROM ns_group_users WHERE sgid=%d", sgid);
  
  return  (sql_field_i(0) >= maxusers);
}

u_int32_t find_group(char *name)
{
  MYSQL_RES *res;
  MYSQL_ROW row;
  u_int32_t sgid = 0;

  res = sql_query("SELECT sgid, gdesc, master_sgid "
    "FROM ns_group WHERE UPPER(name)=UPPER(%s)",
    sql_str(name));

  row = sql_next_row(res);
  if(row)
    sgid = atoi(row[0]);
    
  sql_free(res);
  
  return sgid;
}
#undef STRING_SET
#undef INT_SET

int ev_ns_group_expire(void* dummy1, void* dummy2)
{
  return sql_execute("DELETE FROM ns_group_users WHERE t_expire>0 and t_expire<%d",
    irc_CurrentTime);
}




syntax highlighted by Code2HTML, v. 0.9.1