
/****************************************************************/
/*								*/	
/*		M O D E						*/
/*								*/
/****************************************************************/


/* mode.c	-mode changes  from server

    distributed as part of _sula_.
    Author: Tano Fotang, 1997

    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.

    see the file sula/COPYING for more information.

*/

#include <forms.h>
#include <errno.h>
#include <ctype.h>
#include "aux.h"
#include "sula.h"
#include "chanwin.h"
#include "global.h"
#include "script.h"
#include "parser.h"
#include "mode.h"
#include "setting.h"
#include "ignore.h"

extern void  p_mode(void){
	char *changed=NULL, *changer=NULL, *mode=NULL, *buf=NULL;
	size_t ll;
	
	#if DEBUG>4
	fprintf(stderr, "p_mode 1\n");
	#endif
	ll=strlen(Msg);
	changer=(char *)malloc(sizeof(char)*ll);
	changed=(char *)malloc(sizeof(char)*ll);
	mode=(char *)malloc(sizeof(char)*ll);
	if(!changer || !changed  || !mode) {
	err_ret("%s %d",__FILE__, __LINE__);return;}
	if(sscanf(Msg, "%s MODE %s %s", changer,changed, mode)<3){
		return;
	}
	if(*changed!='#' && *changed!='&'){
		char *pmode=mode;
		if(*pmode==':') pmode++;
		if(!ignored(changer, IG_MISC) && !check_msg(MODE,Msg))
		if(gflags&SHOW_RAW_MSG) fl_addto_browser(Win->chanwin->brow2, Msg);
		else{
			fmt_string="%f%n%p$";
			buf=format_msg(fmt_usermode,changer, changed, pmode);
		  	if(buf){
			       if(X==1) fl_addto_browser(Win->chanwin->brow2, buf);
			       free(buf);
			       }
			else berr_msg("%s> %s", Win->server->servname, buf);
		}
		if(!strcasecmp(changed,Win->server->nick))
  			changemymode(Win, pmode, 0);
	}
	 else	{
		sscanf(Msg, "%*s MODE %*s %[^\r\n]\r\n", mode);
		if(!ignored(changer, IG_MISC) && !check_msg(MODE,Msg))
		if(gflags&SHOW_RAW_MSG) fl_addto_browser(Win->chanwin->brow2, Msg);
		else{
			char *nick, *login, *host;
			int i;
			size_t ll=strlen(changer);
			nick=(char *)malloc(sizeof(char)*ll);
			login=(char *)malloc(sizeof(char)*ll);
			host=(char *)malloc(sizeof(char)*ll);
			i=sscanf(Msg, "%[^!]!%[^@]@%s", nick,login,host);
			if(i==1){
			free(login); free(host); login=host=strdup("#unknown#");
			}
			else if(i==2){free(host); host=strdup("#unknown#");} 
			fmt_string="%n%l%h%p%c$";
			buf=format_msg( fmt_chanmode, nick, login,host,mode, changed);
		 	if(buf){
	 		 	fl_addto_browser(Win->chanwin->brow2, buf);
				free(buf);
				}
			free(nick) ;free(login); free(host);
	    	}
		if(
	 		changechanmode(Win, changed, mode, 0) && 
	 	#if 0
	 	/* RPL_CHANNELMODEIS ought to have taken care of this*/
	 	!strcasecmp(changed,Win->chanCur->name)
	 	#endif
	 		!strcasecmp(changed, Win->chanCur->name)
			)
	 	 		update_server_stuff(Win->server->fd);
		}
	free(changer);
	 free(changed);
	  free(mode);
	  /*free(buf);*/
	#if DEBUG>4
	fprintf(stderr, "p_mode 2\n");
	#endif
}

extern void changemymode(Winstruct *win, char *mode, int reset){
/* change your server    mode 
*	reset>0: clear mode and set new mode
*/

	if(!mode) return;
	if(reset && Win->server->mode){/* free( Win->server->mode );*/
			Win->server->mode=NULL;}
	while(*mode){
 	   if(*mode=='+')
 		 while(*++mode && *mode!='-')
 		     addmode(&Win->server->mode,*mode);
 	   if(*mode=='-')
 		 while(*++mode && *mode!='+')
		     Win->server->mode=strdchr(Win->server->mode,*mode);
 	}
	
 	if(!(Win->server->mode) || !(*Win->server->mode)) Win->server->mode=NULL;
 	update_server_stuff(Win->server->fd);

}
extern int changechanmode(Winstruct *win, const char *channame, char *mode, int reset){
/* change channel modes 
   reset!=0: clear modes and update, this should be a response to
	RPL_CHANNELMODEIS. see numeric.c
*/	 
	 Channel *chan;
	 char update=0;	/* update display after this */
	 char usermode=0;
	 static void change_gokey(Winstruct *, const char *, const char *);

	 if(!mode) return 0; 
	 chan = get_chan_byname(win, channame);
	 if(chan==NULL) return 0;
	 if(reset && chan->modes ){
	   	/* RPL_CHANNELMODEIS doesnt supply the o and v modes, so save them
		first before clearing channel modes
		*/
	 	char t[3]/*=(char*)malloc(sizeof(char)*3)*/, *p=t;
		
	 	if(strchr(chan->modes, 'o')) *p++='o';
		if(strchr(chan->modes, 'v')) *p++='v';
		*p='\0';
		if(t[0]) chan->modes=strdup(t);
		else{ chan->modes=NULL;}
	 }
	 while(*mode){
		if(*mode=='+'){
			while(*++mode && *mode!='-' ){
			     if(!isgraph(*mode)) continue; /* skip space and ctrl-chars*/
			     switch(*mode){
			       case 'l':{
			       		char *l=nextstr(mode,2);
					if(!l) break;
					chan->limit=atoi(l);
					rmstr(&mode,2);
					break;
					}
			       case 'k':chan->key=nextstr(mode,2);
			       		if(chan->key==NULL) break;
			       		change_gokey(win, channame, chan->key);
					rmstr(&mode,2); break;
				
				case 'b': /* someone's been banned*/
					rmstr(&mode,2);
					break;
				case '+': break;
				case 'o':
				case 'v':{
					char *nick=nextstr(mode,2);
					if(nick==NULL) break;
					usermode=1;
					rmstr(&mode,2);
					if( !strcasecmp(win->server->nick, nick)){
						/* our mode has been changed */
					       addmode(&chan->modes,*mode);
					       update=1;
					}
					break;
					}
			       default: addmode(&chan->modes,*mode);break;
			      }
			}
		}/* if(*mode=='+') */

		if(*mode && *mode=='-'){
			 while(*++mode && *mode!='+'){
			     if(!isgraph(*mode)) {continue;}
			     switch(*mode){
				case 'l':chan->limit=0;break;
				case 'k': 
				 	chan->key=NULL; rmstr(&mode,2);
					change_gokey(win, channame, NULL);
					break;
				case 'b': /* someone's been unbanned*/
					rmstr(&mode,2);
					break;
				case '-': break;
				case 'o':
				case 'v':{
					char *nick=nextstr(mode,2);
					if(nick==NULL) break;
					usermode=1;
					rmstr(&mode,2);
					if( !strcasecmp(win->server->nick, nick)){
						/* our channel mode has been changed*/
					       chan->modes=strdchr(chan->modes,*mode);
					       update=1;
					}
					break;
					}
				 default: chan->modes=strdchr(chan->modes,*mode); 
				 	break;
			    }
			 }
		}
		else if(*mode && *mode!='+') mode++;
	}/*while(*mode)*/
	 if(!chan->modes || !(*chan->modes)) chan->modes=NULL;
	 if(usermode && !update) return 0;
	 return 1;
}
#if 0
static void remove_gokey(Winstruct *win, const char *chan){
	Go *p=win->goStart;
	while(p!=win->goEnd)
		if(strcasecmp(p->name, chan)){
			free(p->key); p->key=NULL;
			break;
		}else
		p=p->next;
}
#endif
static void change_gokey(Winstruct *win, const char *chan, const char *key){
	Go *p=win->goStart;
	while(p!=win->goEnd)
		if(strcasecmp(p->name, chan)){
			if(key){
				p->key=strdup(key);
			}else{
				free(p->key); p->key=NULL;
			}
			break;
		}else
		p=p->next;
}


extern void addmode(char **s, char c){
	size_t ll=0;
	if(!(*s)) *s=(char *)malloc(sizeof(char)*2); /* 2bytes wont kill ya.. */
	else{	if( strchr(*s, c) ) return;
		ll=strlen(*s);
		 *s=realloc(*s, (ll+1)*sizeof(char) );
		 /*		^^^^^|-> sizeof(*s)+1	*/
		 if(!(*s)) {err_ret("Error");return;}
	}
	*(*s+ ll++)=c;
	*(*s+ll)='\0';
}

extern void update_server_stuff(int fd){
	

	if(fd<0) return;
	else{
	WinList *p;
	build_winlistbyfd(fd);
	p=servWin;
	while( p){
	   UpdateStatus(p->win);
	   p=p->next;
	}
	}
}


