/*
 * chanlev.c
 * (C) Christian Magnusson 2000, Email: d1mag@dtek.chalmers.se
 * This program is copyright, and covered by the Gnu Public License.
 * The Q bot.
 */

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "settings.h"
#include "bottypes.h"
#include "globals.h"
#include "dl.h"

#define UNKNOWN_OWNER "\02???\02"

void m_chanlev(char *a);

MODULE_PUBLIC int _init_chanlev(void *a, void *b)
{
  dl_command_entry_t *dl = a;
  if(!a) return 0;

  snprintf(dl->help, MAX_COMMAND_HELP_LEN,
	   "/msg %s CHANLEV <channel> [nick] [change flags]\nChange someone's flags/rights on a channel.\n"\
	   "/msg %s CHANLEV #chan              Dump all users on the channel.\n"\
	   "/msg %s CHANLEV #chan nick         View a user's flags (if authed)\n"\
	   "/msg %s CHANLEV #chan #nick        View a user's flags (if not authed)\n"\
	   "/msg %s CHANLEV #chan nick +-flag  Change a user's flags on the channel.\n"\
	   "Rights available are:-\n"\
	   " +a = autoop/autovoice on join.\n"\
	   " +b = banned. (Only works if they've AUTH'd.)\n"\
	   " +m = master. (Can make opers.)\n"\
	   " +n = owner. (Can make masters.)\n"\
	   " +o = op.\n"\
	   " +v = voice.\n"\
	   " +t = can set topic.\n"
	   , BOT_NAME, BOT_NAME, BOT_NAME, BOT_NAME, BOT_NAME);
  snprintf(dl->summary, MAX_COMMAND_SUMMARY_LEN,
	   "Change/view someone's rights on a channel");
  dl->level = 1;
#if 0
  dl->func = m_chanlev;
#else
  dl->func = (void (*)(char*)) DL_sym(dl->val, "m_chanlev");
#endif
  return 1;
}

MODULE_PUBLIC int _remove_chanlev(char *a, char *b)
{
  remove_command("chanlev");
  return 1;
}

MODULE_PUBLIC void m_chanlev(char *a)
{
  ListElem *el;
  int i;
  char buffer[512];
  char *modestr;
  char *owner_str;
  char none[255];
  char channel[CHANNELLEN+1],victim[NICKLEN+2];
  unsigned int newlevel,slevel,vlevel;
  struct dbaseuser *d, *dp, *owner_d;
  struct chanpriv *p, *q;
  struct chanopts *c;
  struct chantype *ct;
  int oper;

  struct dbaseuser *db;
  db = sender_au->dbuser;
  if(db) {
    log("From: %s chanlev [%s]\n", db->nick, a);
  } else
    return;

  i = 0;
  strncpy(channel,getword(a,&i),CHANNELLEN);
  channel[CHANNELLEN] = '\0';
  if(!strlen(channel)) {
    say_sender("You forgot the channel parameter");
    say_sender_( "/msg %s CHANLEV #channel [nick] [+-mode]", BOT_NAME);
    return;
  }

  if(channel[0]!='#') {
    say_sender( "Couldn't find that channel (must begin with '#').");
    say_sender_( "/msg %s CHANLEV #channel [nick] [+-mode]", BOT_NAME);
    return;
  }
  c = findchannel(channel);
  if(!c) {
    say_sender("Couldn't find that channel");
    say_sender_( "/msg %s CHANLEV #channel [nick] [+-mode]", BOT_NAME);
    return;
  }

  oper = is_oper();
  slevel = userlevelonchan_d(sender_au->dbuser, channel);

  strncpy(victim, getword(a,&i), NICKLEN+1);
  victim[NICKLEN+1] = '\0';
  if(!strlen(victim)) {
    /*
      No nick argument was given. Dump all users on that channel.
    */
    /*
      Check to see if they have a high enough level to dump userlist
    */
    if ((slevel==0) || (slevel==CHAN_BANNED)) {
      if(!oper || (sender_au->auth<950)) {
	say_sender( "You are not known or banned on this channel and can't dump the userlist!");
	return;
      }
    }
    owner_d = finduserfromid(c->owner);
    if(owner_d) {
      owner_str = owner_d->nick;
    } else {
      strcpy(none, UNKNOWN_OWNER);
      owner_str = none;
    }
    say_sender_("Original founder: %s", owner_str);

    if(oper) {
      owner_d = finduserfromid(c->created_by);
      if(owner_d) {
	owner_str = owner_d->nick;
      } else {
	sprintf(none, "%s (id=%d)", UNKNOWN_OWNER, c->created_by);
	owner_str = none;
      }
      say_sender_("Added by operator: %s", owner_str);
    }

    el = NULL;
    ct = NULL;
    if(c->type) {
      el = GetHead(chantype_list);
      while(el) {
	ct = (struct chantype *)el->data;
	if(ct->id == c->type) {
	  break;
	}
	el = GetSucc(el);
      }
    }
    if(el) {
      say_sender_("Channeltype: %s", ct->name);
    } else {
      say_sender("Channeltype: (unknown)");
    }

    say_sender_("Known users on %s are: ", channel);

    el = GetHead(dbuser_list);
    while(el) {
      dp = (struct dbaseuser *)el->data;
      p = dp->privs;
      while(p) {
	if(!strcasecmp(p->channel, channel)) break;
	p = p->next;
      }
      if(p) {
	/*
	  user had some modes on that channel. Return them.
	*/
	sprintf(buffer,"%15s  Modes: ", dp->nick);
	createmodes(p->chanlevel, buffer+strlen(buffer));
	say_sender(buffer);
      }
      el = GetSucc(el);
    }
    say_sender( "End of list.");
    return;
  }

  /*
    '#' will look in the global database
    ' ' will look in active userlist.
  */
  if (victim[0]!='#') {
    d = finduserfromnick(victim);
  } else {
    d = finduserfromname(victim+1);
  }

  vlevel = userlevelonchan_d(d, channel);
  modestr = getword(a,&i);
  newlevel = vlevel;
  updatemodes(&newlevel, modestr);
  newlevel = (newlevel & CHAN_ALL);
  
  if (!d) {
    say_sender_("Can't find that nickname. If the user isn't logged on, but is known by %s", BOT_NAME);
    if(!strlen(modestr)) {
      say_sender_("Try: /msg %s CHANLEV #chan #nickname", BOT_NAME);
    } else {
      say_sender_("Try: /msg %s CHANLEV #chan #nickname +-flags", BOT_NAME);
    }
    return;
  }

  if(!strlen(modestr)) {
    if(slevel==0) {
      if(!oper || (sender_au->auth<950)) {
	/*
	  Check to see if they have a high enough level to dump userlist
	*/
	say_sender( "You are not known on that channel and can't view the channelflags!");
	return;
      }
    }
    /*
      No modestr was given. Just return current modes for a user.
    */
    if(vlevel) {
      /* get the pointer from database. */
      p = d->privs;
      while(p) {
	if(!strcasecmp(channel, p->channel)) break;
	p = p->next;
      }
      if(p) {
	/*
	  user had some modes on that channel. Return them.
	*/
	sprintf(buffer,"%s modes on channel %s: ", victim, channel);
	createmodes(p->chanlevel, buffer+strlen(buffer));
	say_sender(buffer);
      }
    } else {
      say_sender( "User is not known on that channel.");
    }
    return;
  }

  if(slevel==0) {
    if(!oper || (sender_au->auth<950)) {
      /*
	Check to see if they have a high enough level to dump userlist
      */
      say_sender( "You are not known on that channel and can't change the channelflags!");
      return;
    }
  }

  if(!oper && (sender_au->auth<950)) {
    if (!(slevel & (CHAN_MASTER | CHAN_OWNER))) {
      say_sender("You have to be master to change someone's flags!");
      return;
    }
    /*
    if ((!(vlevel & CHAN_MASTER)) && (newlevel & CHAN_MASTER)) {
    */
    if ((vlevel & CHAN_MASTER) || (newlevel & CHAN_MASTER)) {
      /* Someone's being made a master. */
      if (!(slevel & CHAN_OWNER) && (db!=d)) {
	say_sender("You have to be owner to change a master's flags!");
	return;
      }
    }
    if ((vlevel & CHAN_OWNER) || (newlevel & CHAN_OWNER)) {
      if (!(slevel & CHAN_OWNER) && (db!=d)) {
	say_sender("You have to be owner to change an owners flags!");
	return;
      }
    }
    if ((newlevel & CHAN_OWNER) && !(slevel & CHAN_OWNER)) {
      say_sender("You can't add a new owner!");
      return;
    }
    /*
      perhaps allow people to set and remove the AUTOOP flag for themself
    */
#if 0
    /* not correct !!! */
    if((newlevel == CHAN_AUTOOP) && (vlevel & (CHAN_OWNER|CHAN_MASTER|CHAN_OPER))) {
      say_sender("You have to be owner to change an owners flags!");
      return;
    }
#endif
  }

  if(c && c->suspended && !is_oper()) { say_sender("Channel is suspended"); return; }

  /*
    OK, the security stuff is out of the road, time to just make the
    damn change.
    Look at a users channel list and try to find victim on #channel
  */
  sqlqlog(QLOG_CHANLEV,sender_au->nick,sender_au->username,sender_au->hostname,"%s %s",
    db->nick,a);
  if(!vlevel) {
    /*
      couldn't find the channel for that user.
      Add it first in the linked list.
    */
    /* found the channel, then add the flags for the user */
    if(newlevel) {
      p = (struct chanpriv *)Malloc(sizeof(struct chanpriv));
      strncpy(p->channel,channel,CHANNELLEN);
      p->channel[CHANNELLEN] = '\0';
      p->chanlevel = newlevel;
      p->next = d->privs;
      d->privs = p;
    }
  } else {
    /*
      User is known in that channel
    */
    q = NULL;
    p = d->privs;
    while(p) {
      if(!strcasecmp(channel,p->channel)) break;
      q = p;
      p = p->next;
    }
    if(p) {
      if (newlevel==0) {
	/*
	  all modes on that channel are removed for that user now.
	*/
	if (q==NULL) {
	  d->privs = p->next;
	} else {
	  q->next = p->next;
	}
	Free(p);
	if(is_empty_channel(channel)) {
	  delchannel(channel);
	}
      } else {
	p->chanlevel = newlevel;
      }
    } else {
      /* should NOT happed since userlevelonchan() returns level=0
	 if channel couldn't be found
      */
      return;
    }
  }
  say_sender("Done.");
  return;
}
