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

   dprintf'ized, 10nov95
*/

#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 "proto.h"

extern char botname[];
extern char botuser[];
extern int serv;
extern char curchan[];
extern struct dcc_t dcc[];
extern int dcc_total;
extern char userfile[];
extern int cx_line;
extern char cx_file[];
extern int bitch;
extern int require_p;
extern int require_x;

/* 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);
    strcpy(x->item,s); s[0]=0; strcpy(s,s1);
  } while (s[0]);
  return x;
}

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,i; struct userrec *u; struct eggqueue *s;
  tot=0;
  for (i=0; i<dcc_total+1; i++) {
    if (i==dcc_total) u=userlist;
    else {
      if (dcc[i].type==DCC_TANDEM) u=dcc[i].u.tand->user;
      else u=NULL;
    }
    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;
    }
  }
  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]=='t') f|=USER_BOT;     /* backwards compatability */
    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_AUTO;
    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]==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_AUTO) 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_user(handle)
char *handle;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return 0;
  else return 1;
}

/* 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("nopass",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;
  u=get_user_by_host(host);
  if (u==NULL) return 0;
  if (!bitch && (strcasecmp(u->pass,"nopass")==0)) return 1;
  if (strcasecmp(u->pass,pass)!=0) return 0;
  else return 1;
}

int pass_match_by_handle(pass,handle)
char *pass,*handle;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return 0;
  if (!bitch && (strcasecmp(u->pass,"nopass")==0)) return 1;
  if (strcasecmp(u->pass,pass)!=0) return 0;
  else return 1;
}

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;
/*  if (u->flags&USER_PURGE) return; */
  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,"%-10snone %-10s%-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");
  if (f==NULL) {
    log(LOG_MISC,"ERROR in writing user file.");
    return;
  }
  log(LOG_MISC,"Writing user file ...");
  tt=time(NULL); strcpy(s1,ctime(&tt));
  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;
  if (!ok) {
    log(LOG_MISC,"ERROR in writing user file.");
    return;
  }
  fclose(f); unlink(userfile);
  sprintf(s,"%s~new",userfile);
#ifdef RENAME
  rename(s,userfile);
#else
  movefile(s,userfile);
#endif
  chmod(userfile,0600);   /* make it -rw------- */
  context;
}

/* note: save-user no longer exists */
/* note: purge-user no longer exists */
	
void change_pass_by_handle(handle,pass)
char *handle,*pass;
{
  struct userrec *u;
  u=get_user_by_handle(userlist,handle);
  if (u==NULL) return;
  strcpy(u->pass,pass);
  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;
  u=get_user_by_host(host);
  if (u==NULL) return;
  strcpy(u->pass,pass);
  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);
  if ((!noshare) && (!(u->flags&USER_BOT)))
    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]!='*') && (!(flags&USER_BOT)))
    shareout("newuser %s %s %s %d\n",handle,host,pass,flags);
  x=bu; if (x!=NULL) {
    while (x->next!=NULL) x=x->next;
    x->next=u;
  }
  else bu=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));
      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;
{
  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]!='*') && (!(u->flags&USER_BOT)))
    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) && (!(u->flags&USER_BOT)))
    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 ;) */
  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) && (!(u->flags&USER_BOT)))
    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;
}

void get_handle_xtra(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->xtra==NULL) { s[0]=0; return; }
  strcpy(s,u->xtra); return;
}

/* 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)))
    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)))
    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)))
    shareout("chcomment %s %s\n",handle,comment);
}

void set_handle_info(bu,handle,info)
struct userrec *bu; char *handle,*info;
{
  struct userrec *u; char *p;
  if (strlen(info)>80) info[80]=0;
  for (p=info; *p; ) {
    if ((*p < 32) || (*p > 126)) 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) && (!(u->flags&USER_BOT)))
    shareout("chinfo %s %s\n",handle,info);
}

void set_handle_xtra(bu,handle,xtra)
struct userrec *bu; char *handle,*xtra;
{
  struct userrec *u;
  if (strlen(xtra)>500) xtra[500]=0;
  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)))
    shareout("chxtra %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) && (!(u->flags&USER_BOT)))
      shareout("+attr %s %d\n",u->handle,which);
    return 1;
  }
  else {
    if (!(u->flags&which)) return 0;
    u->flags &= ~which; 
    if ((!noshare) && (!(u->flags&USER_BOT)))
      shareout("-attr %s %d\n",u->handle,which);
    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) && (!(u->flags&USER_BOT)))
    shareout("chattr %s %d\n",u->handle,flags);
}

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);
}
