/* grouphash.cc
 * This file belongs to Worker, a filemanager for UNIX/X11.
 * Copyright (C) 2001-2004 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
/* $Id: grouphash.cc,v 1.14 2004/10/27 21:07:34 ralf Exp $ */

#include "grouphash.h"
#include "aguix/lowlevelfunc.h"

GroupHash *ugdb;

GroupHash::GroupHash()
{
  size=1024;
  users=new struct scl*[size];
  groups=new struct scl*[size];
  usersgroups = new struct scl*[size];
  for(int i=0;i<size;i++) {
    users[i]=NULL;
    groups[i]=NULL;
    usersgroups[i] = NULL;
  }
  initUsersGroups();
}

GroupHash::~GroupHash()
{
  struct scl *te,*te2;
  for(int i=0;i<size;i++) {
    te=users[i];
    while(te!=NULL) {
      te2=te->next;
      _freesafe(te->name);
      _freesafe(te);
      te=te2;
    }
    te=groups[i];
    while(te!=NULL) {
      te2=te->next;
      _freesafe(te->name);
      _freesafe(te);
      te=te2;
    }
  }
  delete [] users;
  delete [] groups;
  freeUsersGroups();
}

char *GroupHash::getUserNameS(uid_t user)
{
  #ifdef DEBUG
//  printf("entering getUserNameS\n");
  #endif
  int entry=hash((int)user);
  if(entry<0) return NULL;
  bool found=false;
  struct scl *te,*te2;
  struct passwd *nus;
  te2=NULL;
  te=users[entry];
  #ifdef DEBUG
//  printf("  step 1\n");
  #endif
  while(te!=NULL) {
    if(te->uid==user) {
      found=true;
      break;
    }
    te2=te;
    te=te->next;
  }
  #ifdef DEBUG
//  printf("  step 2\n");
  #endif
  if(found==false) {
    // neuen Eintrag
    te=(struct scl*)_allocsafe(sizeof(struct scl));
    te->next=NULL;
    te->uid=user;
    nus=getpwuid(user);
    if(nus!=NULL) {
      te->name=dupstring(nus->pw_name);
    } else {
      te->name = (char*)_allocsafe( A_BYTESFORNUMBER( int ) );
      sprintf(te->name,"%d",(int)user);
    }
    if(te2==NULL) {
      users[entry]=te;
    } else {
      te2->next=te;
    }
  }
  #ifdef DEBUG
//  printf("leaving getUserNameS\n");
  #endif
  return te->name;
}

char *GroupHash::getUserName(uid_t user)
{
  char *tstr=getUserNameS(user);
  if(tstr==NULL) return NULL;
  return dupstring(tstr);
}

void GroupHash::getUserName(uid_t user,char *buf,int len)
{
  if(len<2) return;
  char *str=getUserNameS(user);
  if(str==NULL) {
    strcpy(buf,"");
  } else {
    strncpy(buf,str,len-1);
    buf[len-1]=0;
  }
}

char *GroupHash::getGroupNameS(gid_t gr)
{
#ifdef DEBUG
//printf("entering getGroupNameS\n");
#endif
  int entry=hash((int)gr);
  if(entry<0) return NULL;
  bool found=false;
  struct scl *te,*te2;
  struct group *ngr;
  te2=NULL;
  te=groups[entry];
  while(te!=NULL) {
    if(te->gid==gr) {
      found=true;
      break;
    }
    te2=te;
    te=te->next;
  }
  if(found==false) {
    // neuen Eintrag
    te=(struct scl*)_allocsafe(sizeof(struct scl));
    te->next=NULL;
    te->gid=gr;
    ngr=getgrgid(gr);
    if(ngr!=NULL) {
      te->name=dupstring(ngr->gr_name);
    } else {
      te->name = (char*)_allocsafe( A_BYTESFORNUMBER( int ) );
      sprintf(te->name,"%d",(int)gr);
    }
    if(te2==NULL) {
      groups[entry]=te;
    } else {
      te2->next=te;
    }
  }
#ifdef DEBUG
//printf("leaving getGroupNameS\n");
#endif
  return te->name;
}

char *GroupHash::getGroupName(gid_t gr)
{
  char *tstr=getGroupNameS(gr);
  if(tstr==NULL) return NULL;
  return dupstring(tstr);
}

void GroupHash::getGroupName(gid_t gr,char *buf,int len)
{
  if(len<2) return;
  char *str=getGroupNameS(gr);
  if(str==NULL) {
    strcpy(buf,"");
  } else {
    strncpy(buf,str,len-1);
    buf[len-1]=0;
  }
}

int GroupHash::hash(int id)
{
  //TODO: Nicht gerade die beste Hashfunktione :-(
  int hashcode=abs(id);
  hashcode%=size;
  if((hashcode<0)||(hashcode>=size)) hashcode=0;  // to be sure returning right
                                                  // values
  return hashcode;
}

void GroupHash::initUsersGroups()
{
  gid_t *l = NULL;
  struct scl *e1, *te;
  int h, i, s;
  gid_t effgr;

  s = getgroups( 0, l );
  if ( s > 0 ) {
    l = new gid_t[ s ];
    s = getgroups( s, l );
    for ( i = 0; i < s; i++ ) {
      e1 = new struct scl;
      e1->name = NULL;
      e1->uid = (uid_t)-1;
      e1->gid = l[i];
      e1->next = NULL;

      h = hash( l[i] );
      if ( usersgroups[h] == NULL ) {
	usersgroups[h] = e1;
      } else {
	te = usersgroups[h];
	while ( te->next != NULL ) te = te->next;
	te->next= e1;
      }
    }
    delete [] l;
  }
  
  effgr = getegid();
  h = hash( effgr );
  if ( usersgroups[h] == NULL ) {
    e1 = new struct scl;
    e1->name = NULL;
    e1->uid = (uid_t)-1;
    e1->gid = effgr;
    e1->next = NULL;
    usersgroups[h] = e1;
  } else {
    te = usersgroups[h];
    for (;;) {
      if ( te->gid == effgr ) break;
      if ( te->next != NULL ) {
	te = te->next;
	continue;
      }
      e1 = new struct scl;
      e1->name = NULL;
      e1->uid = (uid_t)-1;
      e1->gid = effgr;
      e1->next = NULL;
      te->next = e1;
      break;
    }
  }
}

bool GroupHash::isInGroup( gid_t gr )
{
  bool found = false;
  int h;
  struct scl *e1;

  h = hash( gr );
  e1 = usersgroups[h];
  while ( e1 != NULL ) {
    if ( e1->gid == gr ) {
      found = true;
      break;
    }
    e1 = e1->next;
  }
  return found;
}

void GroupHash::freeUsersGroups()
{
  struct scl *te,*te2;
  for(int i=0;i<size;i++) {
    te = usersgroups[i];
    while ( te != NULL ) {
      te2 = te->next;
      delete te;
      te = te2;
    }
  }
  delete [] usersgroups;
}
