/* 
   userrec.c -- handles:
     add_q() del_q() str2flags() flags2str()
     a bunch of functions to find and change user records

   dprintf'ized, 10nov95
*/
/*
   This file is part of the eggdrop source code
   copyright (c) 1997 Robey Pointer
   and is distributed according to the GNU general public license.
   For full details, read the top of 'main.c' or the file called
   COPYING that was distributed with this code.
*/

#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "eggdrop.h"
#include "users.h"
#include "chan.h"
#include "proto.h"

extern char botname[];
extern char botuser[];
extern int serv;
extern struct dcc_t dcc[];
extern int dcc_total;
extern char userfile[];
extern int cx_line;
extern char cx_file[];
extern int require_p;
extern int require_x;
extern int share_greet;
extern struct chanset_t *chanset;
extern char ver[];
extern char origbotname[];
extern int nulluser;

/* don't send out to sharebots */
int noshare=1;
/* user records are stored here */
struct userrec *userlist=NULL;
/* last accessed user record */
struct userrec *lastuser=NULL;
struct userrec *banu=NULL,*ignu=NULL;
/* hey let's allow the config file to specify the names for flags! */
/* gee wally i like that idea, that could cause LOTS of trouble! */
char flag1='1';
char flag2='2';
char flag3='3';
char flag4='4';
char flag5='5';
char flag6='6';
char flag7='7';
char flag8='8';
char flag9='9';
char flag0='0';

/* temporary cache accounting */
int cache_hit=0,cache_miss=0;


struct eggqueue *add_q(ss,q)
char *ss; struct eggqueue *q;
{
  char s[512]; struct eggqueue *x; char s1[121],*p;
  strcpy(s,ss); do {
    p=strchr(s,','); if (p!=NULL) { *p=0; p++; strcpy(s1,p); } else s1[0]=0;
    rmspace(s); rmspace(s1);
    x=(struct eggqueue *)nmalloc(sizeof(struct eggqueue));
    x->next=q;
    x->item=(char *)nmalloc(strlen(s)+1);
    x->stamp=time(NULL);
    strcpy(x->item,s); strcpy(s,s1);
  } while (s[0]);
  return x;
}

void chg_q(q,new)
struct eggqueue *q; char *new;
{
  nfree(q->item); q->item=(char *)nmalloc(strlen(new)+1);
  strcpy(q->item,new);
}

struct eggqueue *del_q(s,q,ok)
char *s; struct eggqueue *q; int *ok;
{
  struct eggqueue *x,*ret,*old;
  x=q; ret=q; old=q; *ok=0; while (x!=NULL) {
    if (strcasecmp(x->item,s)==0) {
      if (x==ret) {
	ret=(x->next); nfree(x->item); nfree(x); x=ret;
      }
      else {
	old->next=x->next; nfree(x->item); nfree(x); x=old->next;
      }
      *ok=1;
    }
    else { old=x; x=x->next; }
  }
  return ret;
}

/* memory we should be using */
int expmem_users()
{
  int tot; struct userrec *u; struct eggqueue *s; struct chanset_t *chan;
  tot=0; u=userlist;
  while (u!=NULL) {
    if (u->email!=NULL) tot+=strlen(u->email)+1;
    if (u->dccdir!=NULL) tot+=strlen(u->dccdir)+1;
    if (u->comment!=NULL) tot+=strlen(u->comment)+1;
    if (u->info!=NULL) tot+=strlen(u->info)+1;
    if (u->xtra!=NULL) tot+=strlen(u->xtra)+1;
    s=u->host; while (s!=NULL) {
      tot+=strlen(s->item)+1;
      tot+=sizeof(struct eggqueue);
      s=s->next;
    }
    tot+=sizeof(struct userrec);
    u=u->next;
  }
  /* account for each channel's ban-list user */
  chan=chanset; while (chan!=NULL) {
    if (chan->bans->info!=NULL) tot+=strlen(chan->bans->info)+1;
    if (chan->bans->email!=NULL) tot+=strlen(chan->bans->email)+1;
    if (chan->bans->dccdir!=NULL) tot+=strlen(chan->bans->dccdir)+1;
    if (chan->bans->comment!=NULL) tot+=strlen(chan->bans->comment)+1;
    if (chan->bans->xtra!=NULL) tot+=strlen(chan->bans->xtra)+1;
    s=chan->bans->host; while (s!=NULL) {
      if (s->item!=NULL) tot+=strlen(s->item)+1;
      tot+=sizeof(struct eggqueue);
      s=s->next;
    }
    tot+=sizeof(struct userrec);
    chan=chan->next;
  }
  return tot;
}

unsigned int str2flags(s)
char *s;
{
  unsigned int i,f=0;
  for (i=0; i<strlen(s); i++) {
    if (s[i]=='o') f|=USER_OP;
    if (s[i]=='d') f|=USER_DEOP;
    if (s[i]=='k') f|=USER_KICK;
    if (s[i]=='m') f|=USER_MASTER;
    if (s[i]=='f') f|=USER_FRIEND;
    if (s[i]=='x') f|=USER_XFER;
    if (s[i]=='b') f|=USER_BOT;
    if (s[i]=='p') f|=USER_PARTY;
    if (s[i]=='c') f|=USER_COMMON;
    if (s[i]=='n') f|=USER_OWNER;
    if (s[i]=='j') f|=USER_JANITOR;
    if (s[i]=='s') f|=BOT_SHARE;
    if (s[i]=='a') f|=BOT_ALT;
    if (s[i]=='l') f|=BOT_LEAF;
    if (s[i]=='r') f|=BOT_REJECT;
    if (s[i]=='h') f|=BOT_HUB;
    if (s[i]=='1') f|=USER_FLAG1;
    if (s[i]=='2') f|=USER_FLAG2;
    if (s[i]=='3') f|=USER_FLAG3;
    if (s[i]=='4') f|=USER_FLAG4;
    if (s[i]=='5') f|=USER_FLAG5;
    if (s[i]=='6') f|=USER_FLAG6;
    if (s[i]=='7') f|=USER_FLAG7;
    if (s[i]=='8') f|=USER_FLAG8;
    if (s[i]=='9') f|=USER_FLAG9;
    if (s[i]=='0') f|=USER_FLAG0;
    if (s[i]==flag1) f|=USER_FLAG1;
    if (s[i]==flag2) f|=USER_FLAG2;
    if (s[i]==flag3) f|=USER_FLAG3;
    if (s[i]==flag4) f|=USER_FLAG4;
    if (s[i]==flag5) f|=USER_FLAG5;
    if (s[i]==flag6) f|=USER_FLAG6;
    if (s[i]==flag7) f|=USER_FLAG7;
    if (s[i]==flag8) f|=USER_FLAG8;
    if (s[i]==flag9) f|=USER_FLAG9;
    if (s[i]==flag0) f|=USER_FLAG0;
  }
  return f;
}

void flags2str(f,s)
unsigned int f; char *s;
{
  s[0]=0;
  if (f&USER_OP) strcat(s,"o");
  if (f&USER_DEOP) strcat(s,"d");
  if (f&USER_KICK) strcat(s,"k");
  if (f&USER_MASTER) strcat(s,"m");
  if (f&USER_FRIEND) strcat(s,"f");
  if (f&USER_XFER) strcat(s,"x");
  if (f&USER_COMMON) strcat(s,"c");
  if (f&USER_JANITOR) strcat(s,"j");
#ifdef OWNER
  if (f&USER_OWNER) strcat(s,"n");
#endif
  if (f&USER_FLAG1) { strcat(s,"?"); s[strlen(s)-1]=flag1; }
  if (f&USER_FLAG2) { strcat(s,"?"); s[strlen(s)-1]=flag2; }
  if (f&USER_FLAG3) { strcat(s,"?"); s[strlen(s)-1]=flag3; }
  if (f&USER_FLAG4) { strcat(s,"?"); s[strlen(s)-1]=flag4; }
  if (f&USER_FLAG5) { strcat(s,"?"); s[strlen(s)-1]=flag5; }
  if (f&USER_FLAG6) { strcat(s,"?"); s[strlen(s)-1]=flag6; }
  if (f&USER_FLAG7) { strcat(s,"?"); s[strlen(s)-1]=flag7; }
  if (f&USER_FLAG8) { strcat(s,"?"); s[strlen(s)-1]=flag8; }
  if (f&USER_FLAG9) { strcat(s,"?"); s[strlen(s)-1]=flag9; }
  if (f&USER_FLAG0) { strcat(s,"?"); s[strlen(s)-1]=flag0; }
  if (f&USER_BOT) {
    strcat(s,"b");
    if (f&BOT_SHARE) strcat(s,"s");
    if (f&BOT_ALT) strcat(s,"a");
    if (f&BOT_LEAF) strcat(s,"l");
    if (f&BOT_REJECT) strcat(s,"r");
    if (f&BOT_HUB) strcat(s,"h");
  }
  else {
    if (f&USER_PARTY) strcat(s,"p");
  }
  if (s[0]==0) strcat(s,"-");
}

int count_users(bu)
struct userrec *bu;
{ 
  int tot=0; struct userrec *u=bu;
  while (u!=NULL) {
    tot++;
    u=u->next;
  }
  return tot;
}

struct userrec *get_user_by_handle(bu,handle)
struct userrec *bu; char *handle;
{
  struct userrec *u=bu,*ret;
  if (handle==NULL) return NULL;
  rmspace(handle); if (!handle[0]) return NULL;
  if ((lastuser!=NULL) && (strcasecmp(lastuser->handle,handle)==0) &&
      (bu==userlist)) {
    cache_hit++; return lastuser;
  }
  if ((banu!=NULL) && (strcmp(handle,BAN_NAME)==0) && (bu==userlist)) {
    cache_hit++; return banu;
  }
  if ((ignu!=NULL) && (strcmp(handle,IGNORE_NAME)==0) && (bu==userlist)) {
    cache_hit++; return ignu;
  }
  if (bu==userlist) {
    ret=check_chanlist_hand(handle);
    if (ret!=NULL) { cache_hit++; return ret; }
    cache_miss++;
  }
  while (u!=NULL) {
    if (strcasecmp(u->handle,handle)==0) { 
      if ((strcmp(handle,BAN_NAME)==0) && (bu==userlist)) banu=u;
      else if ((strcmp(handle,IGNORE_NAME)==0) && (bu==userlist)) ignu=u;
      else if (bu==userlist) lastuser=u;
      return u; 
    }
    u=u->next;
  }
  return NULL;
}

int is_user2(bu,handle)
struct userrec *bu; char *handle;
{
  struct userrec *u;
  u=get_user_by_handle(bu,handle);
  if (u==NULL) return 0;
  else return 1;
}

int is_user(handle)
char *handle;
{
  return is_user2(userlist,handle);
}

/* fix capitalization, etc */
void correct_handle(handle)
char *handle;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return;
  strcpy(handle,u->handle);
}

void clear_userlist(bu)
struct userrec *bu;
{
  struct userrec *u=bu,*v;
  while (u!=NULL) {
    clearq(u->host);
    if (u->email!=NULL) nfree(u->email);
    if (u->dccdir!=NULL) nfree(u->dccdir);
    if (u->comment!=NULL) nfree(u->comment);
    if (u->info!=NULL) nfree(u->info);
    if (u->xtra!=NULL) nfree(u->xtra);
    v=u->next; nfree(u); u=v;
  }
  if (userlist==bu) {
    clear_chanlist();
    lastuser=banu=ignu=NULL;
  }
  /* remember to set your userlist to NULL after calling this */
}

/* find CLOSEST host match */
/* (if "*!*@*" and "*!*@*clemson.edu" both match, use the latter!) */
/* 26feb: CHECK THE CHANLIST FIRST to possibly avoid needless search */
struct userrec *get_user_by_host(host)
char *host;
{
  struct userrec *u=userlist,*ret; struct eggqueue *q; int cnt,i;
  if (host==NULL) return NULL;
  rmspace(host); if (!host[0]) return NULL;
  ret=check_chanlist(host); cnt=0;
  if (ret!=NULL) { cache_hit++; return ret; }
  cache_miss++;
  while (u!=NULL) {
    q=u->host;
    while (q!=NULL) {
      i=wild_match(q->item,host);
      if (i>cnt) { ret=u; cnt=i; }
      q=q->next;
    }
    u=u->next;
  }
  if (ret!=NULL) { lastuser=ret; set_chanlist(host,ret); }
  return ret;
}

void get_handle_by_host(nick,host)
char *nick,*host;
{
  struct userrec *u;
  u=get_user_by_host(host);
  if (u==NULL) { nick[0]='*'; nick[1]=0; return; }
  strcpy(nick,u->handle);
}

struct userrec *get_user_by_equal_host(host)
char *host;
{
  struct userrec *u=userlist; struct eggqueue *q;
  while (u!=NULL) {
    q=u->host;
    while (q!=NULL) {
      if (strcasecmp(q->item,host)==0) return u;
      q=q->next;
    }
    u=u->next;
  }
  return NULL;
}

/* try: pass_match_by_host("-",host)
   will return 1 if no password is set for that host   */
int pass_match_by_host(pass,host)
char *pass,*host;
{
  struct userrec *u; char new[20];
  u=get_user_by_host(host);
  if (u==NULL) return 0;
  if (strcmp(u->pass,"-")==0) return 1;
  else if ((pass[0]=='-') || !pass[0]) return 0;
  if (u->flags & USER_BOT) {
    if (strcmp(u->pass,pass)==0) return 1;
  }
  else {
    if (strlen(pass)>9) pass[9]=0;
    encrypt_pass(pass,new);
    if (strcmp(u->pass,new)==0) return 1;
  }
  return 0;
}

int pass_match_by_handle(pass,handle)
char *pass,*handle;
{
  struct userrec *u; char new[20];
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return 0;
  if (strcmp(u->pass,"-")==0) return 1;
  else if ((pass[0]=='-') || !pass[0]) return 0;
  if (u->flags & USER_BOT) {
    if (strcmp(u->pass,pass)==0) return 1;
  }
  else {
    if (strlen(pass)>9) pass[9]=0;
    encrypt_pass(pass,new);
    if (strcmp(u->pass,new)==0) return 1;
  }
  return 0;
}

void get_pass_by_handle(handle,pass)
char *handle,*pass;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) { pass[0]=0; return; }
  strcpy(pass,u->pass);
  return;
}

int write_user(u,f)
struct userrec *u; FILE *f;
{
  char s[181]; struct eggqueue *q;
  flags2str((u->flags & USER_MASK),s);
  /* for user-created flags that could have any name, depending: */
  if (u->flags & USER_FLAG1) strcat(s,"1");
  if (u->flags & USER_FLAG2) strcat(s,"2");
  if (u->flags & USER_FLAG3) strcat(s,"3");
  if (u->flags & USER_FLAG4) strcat(s,"4");
  if (u->flags & USER_FLAG5) strcat(s,"5");
  if (u->flags & USER_FLAG6) strcat(s,"6");
  if (u->flags & USER_FLAG7) strcat(s,"7");
  if (u->flags & USER_FLAG8) strcat(s,"8");
  if (u->flags & USER_FLAG9) strcat(s,"9");
  if (u->flags & USER_FLAG0) strcat(s,"0");
  if (fprintf(f,"%-10s%-20s%-25s%lu\n",u->handle,u->pass,s,u->laston)
      ==EOF)
    return 0;
  q=u->host; s[0]=0; while (q!=NULL) {
    if (!s[0]) { strcpy(s,"-         "); strcat(s,q->item); }
    else {
      if (strlen(s)+strlen(q->item)+2>70) {
	if (fprintf(f,"%s\n",s) == EOF) return 0;
	strcpy(s,"-         "); strcat(s,q->item);
      }
      else {
	strcat(s,", "); strcat(s,q->item);
      }
    }
    q=q->next;
  }
  if (s[0]) { if (fprintf(f,"%s\n",s)==EOF) return 0; }
  if (u->email!=NULL) {
    if (fprintf(f,"+         %s\n",u->email)==EOF) return 0;
  }
  if (u->dccdir!=NULL) {
    if (fprintf(f,"*         %s\n",u->dccdir)==EOF) return 0;
  }
  if (u->comment!=NULL) {
    if (fprintf(f,"=         %s\n",u->comment)==EOF) return 0;
  }
  if (u->info!=NULL) {
    if (fprintf(f,":         %s\n",u->info)==EOF) return 0;
  }
  if (u->xtra!=NULL) {
    char *p=u->xtra;
    while (strlen(p)>160) {
      char *q=p+160,c;
      while ((*q!=' ') && (q!=p)) q--;
      if (q==p) q=p+160; c=*q; *q=0;
      if (fprintf(f,".         %s\n",p)==EOF) { *q=c; return 0; }
      *q=c; if (c==' ') p=q+1; else p=q;
    }
    if (fprintf(f,".         %s\n",p)==EOF) return 0;
  }
  return 1;
}

/* rewrite the entire user file */
void write_userfile()
{
  FILE *f; char s[121],s1[81]; time_t tt; struct userrec *u; int ok;
  context;
  if (userlist==NULL) return;  /* no point in saving userfile */
  sprintf(s,"%s~new",userfile);
  f=fopen(s,"w");
  chmod(s,0600);   /* make it -rw------- */
  if (f==NULL) {
    putlog(LOG_MISC,"*","ERROR writing user file.");
    return;
  }
  putlog(LOG_MISC,"*","Writing user file ...");
  tt=time(NULL); strcpy(s1,ctime(&tt));
  fprintf(f,"#2v: %s -- %s -- written %s",ver,origbotname,s1);
  /* fprintf(f,"# wrote user file: %s",s1); */
  context;
  ok=1;
  u=userlist; while ((u!=NULL) && (ok)) {
    ok=write_user(u,f);
    u=u->next;
  }
  context;
  ok=write_chanbans(f);
  context;
  if (!ok) {
    fclose(f);
    putlog(LOG_MISC,"*","ERROR writing user file.");
    return;
  }
  fclose(f); unlink(userfile);
  sprintf(s,"%s~new",userfile);
#ifdef RENAME
  rename(s,userfile);
#else
  movefile(s,userfile);
#endif
  context;
}

int write_tmp_userfile(fn,bu)
char *fn; struct userrec *bu;
{
  FILE *f; struct userrec *u; int ok;
  context;
  f=fopen(fn,"w");
  chmod(fn,0600);   /* make it -rw------- */
  if (f==NULL) {
    putlog(LOG_MISC,"*","ERROR writing userfile to transfer.");
    return 0;
  }
  fprintf(f,"#2v: %s -- %s -- transmit\n",ver,origbotname);
  ok=1; u=bu;
  while ((u!=NULL) && (ok)) {
    ok=write_user(u,f);
    u=u->next;
  }
  ok=write_chanbans(f);
  fclose(f);
  if (!ok) {
    putlog(LOG_MISC,"*","ERROR writing userfile to transfer.");
    return 0;
  }
  return ok;
}

void change_pass_by_handle(handle,pass)
char *handle,*pass;
{
  struct userrec *u; char new[20]; unsigned char *p;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return;
  if (strlen(pass)>9) pass[9]=0;
  if ((pass[0]=='-') || (u->flags & USER_BOT))
    strcpy(u->pass,pass);
  else {
    p=(unsigned char *)pass; while (*p) {
      if ((*p <= 32) || (*p == 127)) *p='?';
      p++;
    }
    encrypt_pass(pass,new);
    strcpy(u->pass,new);
  }
  if ((!noshare) && (!(u->flags&USER_BOT)))
    shareout("chpass %s %s\n",handle,pass);
}

void change_pass_by_host(host,pass)
char *host,*pass;
{
  struct userrec *u; char new[20]; unsigned char *p;
  u=get_user_by_host(host);
  if (u==NULL) return;
  if (strlen(pass)>9) pass[9]=0;
  if ((pass[0]=='-') || (u->flags & USER_BOT))
    strcpy(u->pass,pass);
  else {
    p=(unsigned char *)pass; while (*p++) {
      if ((*p <= 32) || (*p == 127)) *p='?';
    }
    encrypt_pass(pass,new);
    strcpy(u->pass,new);
  }
  if ((!noshare) && (!(u->flags&USER_BOT)))
    shareout("chpass %s %s\n",u->handle,pass);
}

int change_handle(oldh,newh)
char *oldh,*newh;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,oldh);
  if (u==NULL) return 0;
  /* nothing that will confuse the userfile */
  if ((newh[1]==0) && ((newh[0]=='+') || (newh[0]=='*') || (newh[0]==':') ||
		       (newh[0]=='=') || (newh[0]=='.') || (newh[0]=='-')))
    return 0;
  strcpy(u->handle,newh);
  /* yes, even send bot nick changes now: */
  if (!noshare)
    shareout("chhand %s %s\n",oldh,newh);
  return 1;
}

struct userrec *adduser(bu,handle,host,pass,flags)
struct userrec *bu; char *handle,*host,*pass; int flags;
{
  struct userrec *u,*x;
  u=(struct userrec *)nmalloc(sizeof(struct userrec));
  /* u->next=bu; bu=u; */
  strcpy(u->handle,handle); strcpy(u->pass,pass);
  u->host=NULL; u->next=NULL;
  /* strip out commas -- they're illegal */
  if (host[0]) {
    char *p=strchr(host,',');
    while (p!=NULL) { *p='?'; p=strchr(host,','); }
    u->host=add_q(host,u->host);
  }
  else { u->host=add_q("none",u->host); }
  u->flags=flags; u->laston=0L;
  u->email=u->dccdir=u->comment=u->info=u->xtra=NULL; 
  if (bu==userlist) clear_chanlist();
  if ((!noshare) && (handle[0]!='*') && (bu==userlist) && (!nulluser))
    shareout("newuser %s %s %s %d\n",handle,host,pass,flags);
  if (bu==NULL) bu=u;
  else {
    if ((bu==userlist) && (lastuser!=NULL)) x=lastuser;
    else x=bu;
    while (x->next!=NULL) x=x->next;
    x->next=u;
    if (bu==userlist) lastuser=u;
  }
  return bu;
}

/* create a copy of the entire userlist (for sending user lists to
   clone bots) -- userlist is reversed in the process, which is OK
   because the receiving bot reverses the list AGAIN when saving */
/* t=1: copy only tandem-bots  --  t=0: copy everything BUT tandem-bots */
struct userrec *dup_userlist(t)
int t;
{
  struct userrec *u,*u1,*retu,*nu; struct eggqueue *q;
  nu=retu=NULL; u=userlist;
  while (u!=NULL) {
    if (((u->flags&USER_BOT) && (t)) ||
	(!(u->flags&USER_BOT) && (!t))) {
      u1=(struct userrec *)nmalloc(sizeof(struct userrec));
      u1->next=NULL;
      if (nu==NULL) nu=retu=u1;
      else { nu->next=u1; nu=nu->next; }
      /* u1->next=nu; nu=u1; */
      strcpy(nu->handle,u->handle); strcpy(nu->pass,u->pass);
      q=u->host; nu->host=NULL;
      while (q!=NULL) {
	nu->host=add_q(q->item,nu->host);
	q=q->next;
      }
      nu->flags=u->flags; nu->laston=u->laston;
      if (u->email!=NULL) {
	nu->email=(char *)nmalloc(strlen(u->email)+1);
	strcpy(nu->email,u->email);
      }
      else nu->email=NULL;
      if (u->dccdir!=NULL) {
	nu->dccdir=(char *)nmalloc(strlen(u->dccdir)+1);
	strcpy(nu->dccdir,u->dccdir);
      }
      else nu->dccdir=NULL;
      if (u->comment!=NULL) {
	nu->comment=(char *)nmalloc(strlen(u->comment)+1);
	strcpy(nu->comment,u->comment);
      }
      else nu->comment=NULL;
      if (u->info!=NULL) {
	nu->info=(char *)nmalloc(strlen(u->info)+1);
	strcpy(nu->info,u->info);
      }
      else nu->info=NULL;
      if (u->xtra!=NULL) {
	nu->xtra=(char *)nmalloc(strlen(u->xtra)+1);
	strcpy(nu->xtra,u->xtra);
      }
      else nu->xtra=NULL;
    }
    u=u->next;
  }
  return retu;
}

void freeuser(u)
struct userrec *u;
{
  if (u==NULL) return;
  clearq(u->host);
  if (u->email!=NULL) nfree(u->email);
  if (u->dccdir!=NULL) nfree(u->dccdir);
  if (u->comment!=NULL) nfree(u->comment);
  if (u->info!=NULL) nfree(u->info);
  if (u->xtra!=NULL) nfree(u->xtra);
  nfree(u);
}

int deluser(handle)
char *handle;
{
  struct userrec *u=userlist,*prev=NULL; int fnd=0;
  while ((u!=NULL) && (!fnd)) {
    if (strcasecmp(u->handle,handle)==0) fnd=1;
    else { prev=u; u=u->next; }
  }
  if (!fnd) return 0;
  if (prev==NULL) userlist=u->next;
  else prev->next=u->next;
  if ((!noshare) && (handle[0]!='*'))
    shareout("killuser %s\n",handle);
  freeuser(u);
  clear_chanlist(); lastuser=NULL;
  if (strcmp(handle,BAN_NAME)==0) banu=NULL;
  if (strcmp(handle,IGNORE_NAME)==0) ignu=NULL;
  return 1;
}

int delhost_by_handle(handle,host)
char *handle,*host;
{
  struct userrec *u; int i;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return 0;
  u->host=del_q(host,u->host,&i);
  if (u->host==NULL) u->host=add_q("none",u->host);
  if ((!noshare) && (i))
    shareout("-host %s %s\n",handle,host);
  clear_chanlist();
  return i;
}

int ishost_for_handle(handle,host)
char *handle,*host;
{
  struct userrec *u; struct eggqueue *q;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return 0;
  if (u->host==NULL) return 0;
  q=u->host; while (q!=NULL) {
    if (strcasecmp(q->item,host)==0) return 1;
    q=q->next;
  }
  return 0;
}

void addhost_by_handle2(bu,handle,hst)
struct userrec *bu; char *handle,*hst;
{
  struct userrec *u; int i; char *p; struct eggqueue *q; char host[161];
  u=get_user_by_handle(bu,handle); strcpy(host,hst);
  if (u==NULL) return;
  if (u->host!=NULL) if (strcmp(u->host->item,"none")==0)
    u->host=del_q("none",u->host,&i);
  p=strchr(host,',');   /* commas are forbidden */
  while (p!=NULL) { *p='?'; p=strchr(host,','); }
  /* fred1: check for redundant hostmasks with */
  /* controversial "superpenis" algorithm ;) */
  if ((strcasecmp(u->handle,BAN_NAME)!=0) && 
      (strcasecmp(u->handle,IGNORE_NAME)!=0)) {
    q=u->host; while (q!=NULL) {
      if (wild_match(host,q->item)) q=u->host=del_q(q->item,u->host,&i);
      else q=q->next;
    }
  }
  u->host=add_q(host,u->host);
}

void addhost_by_handle(handle,host)
char *handle,*host;
{
  struct userrec *u;
  addhost_by_handle2(userlist,handle,host);
  /* u will be cached, so really no overhead, even tho this looks dumb: */
  u=get_user_by_handle(userlist,handle);
  if (!noshare) {
    if (u->flags & USER_BOT) shareout("+bothost %s %s\n",handle,host);
    else shareout("+host %s %s\n",handle,host);
  }
  clear_chanlist();
}

void get_handle_email(handle,s)
char *handle; char *s;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) { s[0]=0; return; }
  if (u->email==NULL) { s[0]=0; return; }
  strcpy(s,u->email); return;
}

void get_handle_dccdir(handle,s)
char *handle; char *s;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) { s[0]=0; return; }
  if (u->dccdir==NULL) { s[0]=0; return; }
  strcpy(s,u->dccdir); return;
}

void get_handle_comment(handle,s)
char *handle; char *s;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) { s[0]=0; return; }
  if (u->comment==NULL) { s[0]=0; return; }
  strcpy(s,u->comment); return;
}

void get_handle_info(handle,s)
char *handle; char *s;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) { s[0]=0; return; }
  if (u->info==NULL) { s[0]=0; return; }
  strcpy(s,u->info); return;
}

/* returns possibly infinite-length string, please do not modify it */
char *get_handle_xtra(handle)
char *handle;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return NULL;
  if (u->xtra==NULL) return NULL;
  return u->xtra;
}

/* max length for these things is now 160 */

void set_handle_email(bu,handle,email)
struct userrec *bu; char *handle,*email;
{
  struct userrec *u;
  if (strlen(email)>160) email[160]=0;
  u=get_user_by_handle(bu,handle);
  if (u==NULL) return;
  if (u->email!=NULL) nfree(u->email);
  if (email[0]) {
    u->email=(char *)nmalloc(strlen(email)+1);
    strcpy(u->email,email);
  }
  else u->email=NULL;
  if ((!noshare) && (!(u->flags&USER_BOT)) && (bu==userlist))
    shareout("chemail %s %s\n",handle,email);
}

void set_handle_dccdir(bu,handle,dir)
struct userrec *bu; char *handle,*dir;
{
  struct userrec *u;
  if (strlen(dir)>160) dir[160]=0;
  u=get_user_by_handle(bu,handle);
  if (u==NULL) return;
  if (u->dccdir!=NULL) nfree(u->dccdir);
  if (dir[0]) {
    u->dccdir=(char *)nmalloc(strlen(dir)+1);
    strcpy(u->dccdir,dir);
  }
  else u->dccdir=NULL;
  if ((!noshare) && (!(u->flags&USER_BOT)) && (bu==userlist))
    shareout("chdccdir %s %s\n",handle,dir);
}

void set_handle_comment(bu,handle,comment)
struct userrec *bu; char *handle,*comment;
{
  struct userrec *u;
  if (strlen(comment)>160) comment[160]=0;
  u=get_user_by_handle(bu,handle);
  if (u==NULL) return;
  if (u->comment!=NULL) nfree(u->comment);
  if (comment[0]) {
    u->comment=(char *)nmalloc(strlen(comment)+1);
    strcpy(u->comment,comment);
  }
  else u->comment=NULL;
  if ((!noshare) && (!(u->flags&USER_BOT)) && (bu==userlist))
    shareout("chcomment %s %s\n",handle,comment);
}

void set_handle_info(bu,handle,info)
struct userrec *bu; char *handle,*info;
{
 /* Modified by crisk to allow 8-bit channels to be joined */
 /* Namely, the Hebrew channel on the Undernet. */
  struct userrec *u; unsigned char *p;
  if (strlen(info)>80) info[80]=0;
  for (p=info; *p; ) {
    if ((*p < 32) || ((*p > 126) && (*p < 224))) strcpy(p,p+1);
    else p++;
  }
  u=get_user_by_handle(bu,handle);
  if (u==NULL) return;
  if (u->info!=NULL) nfree(u->info);
  if (info[0]) {
    u->info=(char *)nmalloc(strlen(info)+1);
    strcpy(u->info,info);
  }
  else u->info=NULL;
  if ((!noshare) && (bu==userlist)) {
    if (u->flags & USER_BOT) shareout("chaddr %s %s\n",handle,info);
    else if (share_greet) shareout("chinfo %s %s\n",handle,info);
  }
}

void set_handle_xtra(bu,handle,xtra)
struct userrec *bu; char *handle,*xtra;
{
  struct userrec *u; char *p,*q;
  u=get_user_by_handle(bu,handle);
  if (u==NULL) return;
  if (u->xtra!=NULL) nfree(u->xtra);
  if (xtra[0]) {
    u->xtra=(char *)nmalloc(strlen(xtra)+1);
    strcpy(u->xtra,xtra);
  }
  else u->xtra=NULL;
  if ((!noshare) && (!(u->flags&USER_BOT)) && (bu==userlist)) {
    shareout("clrxtra %s\n",handle);
    /* only send 450 at a time */
    if (u->xtra != NULL) {
      p=u->xtra;
      while (strlen(p)>450) {
	q=p+450; while ((q!=p) && (*q != ' ')) q--;
	if (q==p) q=p+450;
	*q=0; shareout("addxtra %s %s\n",handle,p);
	*q=' '; p=q+1;
      }
      if (*p) shareout("addxtra %s %s\n",handle,p);
    }
  }
}

void add_handle_xtra(bu,handle,xtra)
struct userrec *bu; char *handle,*xtra;
{
  struct userrec *u; char *p;
  u=get_user_by_handle(bu,handle);
  if (u==NULL) return;
  if (u->xtra==NULL) {
    set_handle_xtra(bu,handle,xtra);
    return;
  }
  if (!xtra[0]) return;
  p=u->xtra;
  u->xtra=(char *)nmalloc(strlen(xtra)+strlen(p)+2);
  strcpy(u->xtra,p); strcat(u->xtra," "); strcat(u->xtra,xtra);
  nfree(p);
  if ((!noshare) && (!(u->flags&USER_BOT)) && (bu==userlist)) {
    shareout("addxtra %s %s\n",handle,xtra);
  }
}

int chg_attr_u(u,chg,which)
struct userrec *u; char chg; int which;
{
  if (u==NULL) return 0;
  if (chg=='+') {
    if (u->flags&which) return 0;
    u->flags |= which;
    if (!noshare)
      shareout("chattr %s %d\n",u->handle,u->flags&BOT_MASK);
    return 1;
  }
  else {
    if (!(u->flags&which)) return 0;
    u->flags &= ~which; 
    if (!noshare)
      shareout("chattr %s %d\n",u->handle,u->flags&BOT_MASK);
    return 1;
  }
}

int chg_attr_by_handle(handle,chg,which)
char *handle,chg; int which;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u!=NULL) return chg_attr_u(u,chg,which);
  else return 0;
}

int chg_attr_by_host(host,chg,which)
char *host,chg; int which;
{
  struct userrec *u;
  u=get_user_by_host(host);
  if (u!=NULL) return chg_attr_u(u,chg,which);
  else return 0;
}

/* return 1 if it was successful */
/* change attr by handle or host (depending on whether ident has '@') */
int change_attr(ident,chg,which)
char *ident,chg; int which;
{
  if (ident==NULL) return 0;
  if (!ident[0]) return 0;
  if (strchr(ident,'@')==NULL) {
    /* handle? */
    return chg_attr_by_handle(ident,chg,which);
  }
  else return chg_attr_by_host(ident,chg,which);
}

int get_attr_handle(handle)
char *handle;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return 0;
  return u->flags;
}

void set_attr_handle(handle,flags)
char *handle; unsigned int flags;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return;
  u->flags=flags;
  if (!noshare)
    shareout("chattr %s %d\n",u->handle,(flags&BOT_MASK));
}

int get_attr_host(host)
char *host;
{
  struct userrec *u;
  u=get_user_by_host(host);
  if (u==NULL) return 0;
  return u->flags;
}

int flags_ok(req,have)
int req,have;
{
  if (have&USER_OWNER) return 1;
  if ((have&USER_MASTER) && !(req&USER_OWNER)) return 1;
  if ((!require_x) && (have&USER_OP)) have|=USER_XFER;
  if ((!require_p) && (have&USER_OP)) have|=USER_PARTY;
  return ((have&req) == req);
}

int flags_eq(req,have)
int req,have;
{
  return ((have&req) == req);
}

