/*
	BDM_UNIX - UNIX specific routines.

*/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include "bdm.h"

#define AUFFILE         "/sy99/dat/auf.dat"
#define GROUPFILE	"/etc/group"
#define GROUPSCR	"/etc/group.scr"

/* Global data definitions. */
extern struct sys_ident_node *sys_info; /* System information on idents */
extern char pname[PNAMESIZE];		/* Command parameter */
extern struct plist_type plist;
extern char username[USERNAMESIZE];
extern char ident[IDENTSIZE];
extern int  cl_size;
extern char owner[ACCOUNTNAMESIZE];
extern char partition[PNAMESIZE];		/* Command qualifier */
extern int  cl_user;
extern int  cl_administrator;
extern int  cl_full;
extern int  cl_brief;
extern int  cl_write;
extern int  cl_read;
extern char description[PDESCSIZE];
extern char my_account[ACCOUNTNAMESIZE];	/* Account name of caller */


/* Routine definitions. */
int create_ident();
void destroy_ident();
void get_my_account();
void get_space();
void get_sys_info();
int grant_ident();
void parse_command();
int revoke_ident();
int trans_account();
int tran_user();

/*
	Create_ident - Routine to create the identifier id$w, id$r,
	id$o, and id$a. Owners are granted id$o, administrators have
	id$a (and since the owners are administrators they also have
	id$a). Users who have write access (along with owners and
	administrators) have the identifier id$w. All remaining users
	have the id$r identifier.
	On Unix machines the identifiers id_W and id_R are created.
*/
int create_ident(id)
char *id;
{
  FILE *fp1,*fp2;
  char f1[255],f2[255];
  char string[BUFSIZ];
  int  ident_num;

  /* Open the original copy of the group file and a temporary copy of the */
  /* group file. Copy all of the data from the original to the temporary  */
  /* then add the new group to the end. Once finished rename the temp copy*/
  /* over the original copy.                                              */

  sprintf(f1,"%s",GROUPFILE);
  if(!(fp1 = fopen(f1,"r"))) {
    perror(f1);
    bdm_exit(0);
  }
  sprintf(f2,"%s",GROUPSCR);
  if(!(fp2 = fopen(f2,"w"))) {
    perror(f2);
    fclose(fp1);			/* Close the group file */
    bdm_exit(0);
  }

  while(fgets(string,BUFSIZ,fp1)) {
    if(!(fputs(string,fp2))) {
      perror(f2);
      fclose(fp1);
      fclose(fp2);
      bdm_exit(0);
    }
  }

  ident_num = 75;		/* Get ident number from LI */

  sprintf(string,"%s_W:*:%d:\n",id,ident_num); 
  if(!(fputs(string,fp2))) {
    perror(f2);
    fclose(fp1);
    fclose(fp2);
    bdm_exit(0);
  }

  sprintf(string,"%s_R:*:%d:\n",id,ident_num);
  if(!(fputs(string,fp2))) {
    perror(f2);
    fclose(fp1);
    fclose(fp2);
    bdm_exit(0);
  }


  fclose(fp1);
  fclose(fp2);
  rename(f2,f1);
  return(1);
} /*** End of routine create_ident ***/


/*
	Destroy_ident - Routine to destroy an identifier and remove all
		of the holder records associated with the identifier.
*/
void destroy_ident(id)
char *id;
{
  FILE *fp1,*fp2;
  char f1[255],f2[255];
  char string[BUFSIZ];
  char group[BUFSIZ];
  char *s;

  /* Open the original copy of the group file and a temporary copy of the */
  /* group file. Copy all of the data from the original to the temporary  */
  /* except the group to be removed. Once finished rename the temp copy   */
  /* over the original copy.                                              */
  sprintf(f1,"%s",GROUPFILE);
  if(!(fp1 = fopen(f1,"r"))) {
    perror(f1);
    bdm_exit(0);
  }
  sprintf(f2,"%s",GROUPSCR);
  if(!(fp2 = fopen(f2,"w"))) {
    perror(f2);
    fclose(fp1);                        /* Close the group file */
    bdm_exit(0);
  }
  while(fgets(string,BUFSIZ,fp1)) {
    if(s = (char *)index(string,':')) {	/* If line does not have : then skip */
      strncpy(group,string,s-string);
      group[s-string] = '\0';
      if((group[s-string-2] == '_') && ((group[s-string-1] == 'R') ||
                                        (group[s-string-1] == 'W')))
          group[s-string-2] = '\0';
      if(strcmp(group,id)) {		/* Write the record if not the group */
        if(!(fputs(string,fp2))) {
          perror(f2);
          fclose(fp1);
          fclose(fp2);
          bdm_exit(0);
	}
      }
    }
  }

  fclose(fp1);
  fclose(fp2);
  rename(f2,f1);

  return;
} /*** End of routine Destroy_ident ***/


/*
	Get_my_account - Routine to get the account name of the current
	process.
*/
void get_my_account()
{
  FILE *fp1;
  char f1[255];
  uid_t uid = getuid();
  uid_t aufuid;

   
  sprintf(f1,"%s",AUFFILE);
  if(!(fp1 = fopen(f1,"r"))) {
    perror(f1);
    exit(1);
  }
  while (fscanf(fp1,"%s%d%*s",my_account,&aufuid) != EOF) {
    if(aufuid == uid) {
      fclose(fp1);
      return;
    }
  }
  fclose(fp1);					/* Close the AUF */
  printf("Error getting account name\n");
  bdm_exit(0);					/* Exit with error */
  return;

} /*** End of get_my_account ***/

/*
	Get_space - Routine to get the amount of space available and used on a
		partition. The VMS version of this routine uses the volume
		name to locate the partition and then does a f$getdvi on the
		DISK$volname device. The unix version of this routine uses
		the location field to locate the partition and then call the
		get disk use routine (extracted from the df command).
*/
void get_space(available_space, used_space)
int *available_space;
int *used_space;
{
  int  maxblk;                   /* Capacity of the filesystem       */
  int  freeblk;                  /* Free space on the filesystem     */

  if(gdiskuse(plist.ploc.plistloc,&maxblk,available_space) == 0)
    *used_space = maxblk - *available_space;
  else
    *used_space = *available_space = 0;

  return;
    
} /*** End of routine get_space ***/

/*

        Get_sys_info - Routine to create a list of groups on the system
                       and for each list create a separate list containing
                       the names of all accounts that are in the group.
*/
void get_sys_info()
{
  FILE *fp1;
  char f1[255];
  char *tok;
  struct sys_ident_node *last_node;
  struct sys_user_node  *last_user;
  char string[BUFSIZ];
  char account[ACCOUNTNAMESIZE];

  sprintf(f1,"%s",GROUPFILE);
  if(!(fp1 = fopen(f1,"r"))) {
    perror(f1);
    bdm_exit(0);
  }

  while(fgets(string,BUFSIZ,fp1)) {
    /* Create the node in the list then fill it in... */
    if(!sys_info) {
      sys_info = (struct sys_ident_node *)malloc(sizeof(struct sys_ident_node));
      last_node = sys_info;
    } else {
      last_node->next =
                 (struct sys_ident_node *)malloc(sizeof(struct sys_ident_node));
      last_node = last_node->next;
    }
    tok = (char *)strtok(string,":");
    if(tok) strcpy(last_node->ident_name,tok);
    last_node->in_pif = 0;
    last_node->u_node = 0;
    last_node->next = 0;
    if(tok) tok = (char *)strtok(0,":");	/* Skip over password */
    if(tok) tok = (char *)strtok(0,":");	/* Skip over group number */
    for(tok = (char *)strtok(0,",");tok != NULL;tok = (char *)strtok(0,",")) {
      if(!isalnum(*tok)) continue;
      if(!isalnum(*(tok+strlen(tok)-1))) *(tok+strlen(tok)-1) = '\0';
      /* Place the accountname at the end of the userlist if one exists */
      /* Create the list if one does not exits.                       */
      if(last_node->u_node) {
        last_user->next =
                  (struct sys_user_node *)malloc(sizeof(struct sys_user_node));
        last_user = last_user->next;
      } else {
        last_node->u_node =
                  (struct sys_user_node *)malloc(sizeof(struct sys_user_node));
        last_user = last_node->u_node;
      }
      last_user->next = 0;
      last_user->in_pif = 0;
      if(trans_user(tok,account)) {
        strncpy(last_user->accountname,account,sizeof(last_user->accountname));
      } else {
        strncpy(last_user->accountname,tok,sizeof(last_user->accountname));
        printf("Unable to translate username");
        printf(" %s from /etc/group to accountname\n",tok);
      }
    }
  }

  fclose(fp1);
  return;
} /*** End of routine get_sys_info ***/


/*
        Grant_ident - Routine to grant the identifier to a account. The
	ident is the same as the Group name. Granting a user an ident
	name is the same as adding the user to the group name in the file
	/etc/group.
*/
int grant_ident(id, accountname)
char *id;
char *accountname;
{
  FILE *fp1,*fp2;
  char f1[255],f2[255];
  char string[BUFSIZ];
  char group[BUFSIZ];
  char uname[USERNAMESIZE];
  char username[USERNAMESIZE];
  char *s,*t;

  trans_account(username,accountname);

  sprintf(f1,"%s",GROUPFILE);
  if(!(fp1 = fopen(f1,"r"))) {
    perror(f1);
    bdm_exit(0);
  }
  sprintf(f2,"%s",GROUPSCR);
  if(!(fp2 = fopen(f2,"w"))) {
    perror(f2);
    fclose(fp1);                        /* Close the group file */
    bdm_exit(0);
  }
  while(fgets(string,BUFSIZ,fp1)) {
    if(s = (char *)index(string,':')) { /* If line does not have : then skip */
      strncpy(group,string,s-string);
      string[strlen(string)-1] = '\0'; /* End the string without the lf */
      group[s-string] = '\0';
      if(!(strcmp(group,id))) {      /* If correct group add username */
        /* First check to see if the user is already part of this group */
        /* S is already pointing to the first :, skip past the next 2 : */
        if(s = (char *)index(++s,':')) {
          if(s = (char *)index(++s,':')) {
            /* Loop thru each username in the group file */
	    while(s++ != '\0') {
              t = (char *)index(s,',');
	      /* Must copy the username to another area for comparison */
	      /* If we only compare the first t-s characters then test */
	      /* would pass if username from the group file was a subset */
	      /* of the username passed to the routine.                */
              if(t) {
                strncpy(uname,s,t-s);
                uname[t-s] = '\0';
              } else
                strcpy(uname,s);
              if(!(strcmp(uname,username))) return(TRUE);
	      s = t;
            }
	  }
        }
        if(string[strlen(string)-1] != ':') strcat(string,",");  
	strcat(string,username);
      }
      strcat(string,"\n");
      if(!(fputs(string,fp2))) {
        perror(f2);
        fclose(fp1);
        fclose(fp2);
        bdm_exit(0);
      }
    }
  }

  fclose(fp1);
  fclose(fp2);
  rename(f2,f1);

  return(TRUE);
} /*** End of routine grant_ident ***/

/*
	Parse_command - Routine to parse the command line. The Unix version
	of this routine must do all of the parsing (and checking) in this
	routine. The VMS version uses the CLI to do the checking.
*/
void parse_command(argc, argv, command)
  int argc;
  char *argv[];
  int *command;
{
  int i;

  if(argc < 2) {			/* Check how many parameters */
    printf("usage: bdm option ...\n");	/* Error if not enough */
    bdm_exit(0);
  }

  /* Initialize parameters */
  partition[0] = '\0';                  /* Set partition to null string */

  if(strncmp(argv[1],"create",6) == 0)  *command = CREATE_COMMAND;
  if(strncmp(argv[1],"destroy",7) == 0) *command = DESTROY_COMMAND;
  if(strncmp(argv[1],"add",3) == 0)     *command = ADD_COMMAND;
  if(strncmp(argv[1],"modify",6) == 0)  *command = MODIFY_COMMAND;
  if(strncmp(argv[1],"remove",6) == 0)  *command = REMOVE_COMMAND;
  if(strncmp(argv[1],"verify",6) == 0)  *command = VERIFY_COMMAND;
  if(strncmp(argv[1],"mount",5) == 0)   *command = MOUNT_COMMAND;
  if(strncmp(argv[1],"report",6) == 0)  *command = REPORT_COMMAND;
  if(strncmp(argv[1],"show",4) == 0)    *command = SHOW_COMMAND;
  if(*command == '\0') {
    fprintf(stderr,"Invalid option\n");
    bdm_exit(0);
  }

  /* Parse the qualifiers */
  for(i=2;((i<=argc) && (argv[i][0] == '-'));i++) {
    switch(argv[i][1]) {
    case 'a':
	      if((*command != ADD_COMMAND) && (*command != MODIFY_COMMAND)) {
		printf("-a not valid with %s\n",argv[1]);
		bdm_exit(0);
	      }
	      if(cl_user) {
		printf("-u and -a are mutually exclusive\n");
		bdm_exit(0);
	      }
	      if(cl_read) {
		printf("-a and -r are mutually exclusive\n");
		bdm_exit(0);
	      }
	      cl_administrator = TRUE;
	      cl_write = TRUE;
	      break;
    case 'b':
	      if(*command != REPORT_COMMAND) {
		printf("-b not valid with %s\n",argv[1]);
                bdm_exit(0);
              }
              if(cl_full) {
		printf("-b and -f are mutually exclusive\n");
		bdm_exit(0);
	      }
	      cl_brief = TRUE;
	      break;
    case 'd':
	      printf("Description qualifier specified\n");
	      break;
    case 'f':
              if(*command != REPORT_COMMAND) {
                printf("-f not valid with %s\n",argv[1]);
                bdm_exit(0);
              }
              if(cl_brief) {
                printf("-b and -f are mutually exclusive\n");
                bdm_exit(0);
              }
              cl_full = TRUE;
	      break;
    case 'n':
	      printf("Username qualifier specified\n");
	      break;
    case 'p':
	      if((*command != ADD_COMMAND) && (*command != MODIFY_COMMAND) &&
		(*command != REMOVE_COMMAND) && (*command != REPORT_COMMAND) &&
                (*command != VERIFY_COMMAND)) {
		printf("-p not valid with %s\n",argv[1]);
		bdm_exit(0);
	      }
	      if(argc < i+1) {
		printf("-p qualifier requires a partition name\n");
		bdm_exit(0);
	      }
	      strncpy(partition, argv[++i], sizeof(partition));
	      break;
    case 'r':
              if((*command != ADD_COMMAND) && (*command != MODIFY_COMMAND)) {
                printf("-r not valid with %s command\n",argv[1]);
                bdm_exit(0);
              }
	      if(cl_administrator) {
		printf("-r and -a are mutually exclusive\n");
		bdm_exit(0);
	      }
              if(cl_write) {
                printf("-w and -r are mutually exclusive\n");
                bdm_exit(0);
              }
              cl_read = TRUE;
              break;
    case 'u':
              if((*command != ADD_COMMAND) && (*command != MODIFY_COMMAND)) {
                printf("-u not valid with %s command\n",argv[1]);
                bdm_exit(0);
              }
              if(cl_administrator) {
                printf("-u and -a are mutually exclusive\n");
                bdm_exit(0);
              }
              cl_user = TRUE;
	      break;
    case 'w':
	      if((*command != ADD_COMMAND) && (*command != MODIFY_COMMAND)) {
                printf("-w not valid with %s command\n",argv[1]);
                bdm_exit(0);
              }
              if(cl_read) {
		printf("-w and -r are mutually exclusive\n");
                bdm_exit(0);
              }
              cl_write = TRUE;
	      break;
    default:
	      printf("No such qualifier %s\n",argv[i]);
	      bdm_exit(0);
	      break;
    }
  }

  /* Set the default for switches if not specified */
  if(!cl_administrator) cl_user = TRUE; 
  if(!cl_full) cl_brief = TRUE;

  /* Parse the Create command parameters */
  if(*command == CREATE_COMMAND) {
    if(argc < i+4) {
      printf("usage: bdm create pname ident size owner\n");
      bdm_exit(0);
    }
    strncpy(pname, argv[i++], sizeof(pname));
    strncpy(ident, argv[i++], sizeof(ident));
    cl_size = atoi(argv[i++]);
    if(!(trans_user(argv[i],owner))) {
        printf("No such username %12.12s\n",argv[i]);
        bdm_exit(0);
    }
  }

  /* Parse the destroy command */
  if(*command == DESTROY_COMMAND) {
    if(argc < i+1) {
      printf("usage: bdm destroy pname\n");
      bdm_exit(0);
    }
    strncpy(pname, argv[i++], sizeof(pname));
  }

  /* Parse the add command */
  if(*command == ADD_COMMAND) {
    if(argc < i+1) {
      printf("usage: bdm add username\n");
      bdm_exit(0);
    }
    strncpy(username, argv[i++], sizeof(username));
  }

  /* Parse the modify command */
  if(*command == MODIFY_COMMAND ) {
    if(argc < i+1) {
      printf("usage: bdm modify username\n");
      bdm_exit(0);
    }
    strncpy(username, argv[i++], sizeof(username));
  }

  /* Parse the remove command */
  if(*command == REMOVE_COMMAND) {
    if(argc < i+1) {
      printf("usage: bdm remove username\n");
      bdm_exit(0);
    }
    strncpy(username, argv[i++], sizeof(username));
  }

  /* Parse the mount command */
  if(*command == MOUNT_COMMAND) {
    if(argc < i+1) {
      printf("usage: bdm mount pname\n");
      bdm_exit(0);
    }
    strncpy(pname, argv[i++], sizeof(pname));
  }

  /* Parse the show command */
  if(*command == SHOW_COMMAND) {
    if(argc > i)
      strncpy(username, argv[i++], sizeof(username)); 
  }

  return;
}  /*** End of Parse_command ***/


/*
	Revoke_ident - Routine to revoke an identifer from an account.
*/
int revoke_ident(id, accountname)
char *id;
char *accountname;
{
  FILE *fp1,*fp2;
  char f1[255],f2[255];
  char newstring[BUFSIZ];
  char string[BUFSIZ];
  char group[BUFSIZ];
  char uname[USERNAMESIZE];
  char username[USERNAMESIZE];
  char *s,*t;

  trans_account(username,accountname);

  sprintf(f1,"%s",GROUPFILE);
  if(!(fp1 = fopen(f1,"r"))) {
    perror(f1);
    bdm_exit(0);
  }
  sprintf(f2,"%s",GROUPSCR);
  if(!(fp2 = fopen(f2,"w"))) {
    perror(f2);
    fclose(fp1);                        /* Close the group file */
    bdm_exit(0);
  }

  while(fgets(string,BUFSIZ,fp1)) {
    if(s = (char *)index(string,':')) { /* If line does not have : then skip */
      strncpy(group,string,s-string);
      string[strlen(string)-1] = '\0'; /* End the string without the lf */
      group[s-string] = '\0';
      if(!(strcmp(group,id))) {      /* If correct group revoke username */
        if(s = (char *)index(++s,':')) {
          if(s = (char *)index(++s,':')) {
	    strncpy(newstring,string,s-string+1);
	    newstring[s-string+1] = '\0';
            /* Loop thru each username in the group file */
            while(s++ != '\0') {
              t = (char *)index(s,',');
              if(t) {
                strncpy(uname,s,t-s);
		uname[t-s] = '\0';
	      } else
		strcpy(uname,s);
              if((strcmp(uname,username))) {
		if(newstring[strlen(newstring)-1] != ':') strcat(newstring,",");
                strcat(newstring,uname);
	      }
              s = t;
            }
	    strcpy(string,newstring);
          }
        }
      }
      strcat(string,"\n");
      if(!(fputs(string,fp2))) {
        perror(f2);
        fclose(fp1);
        fclose(fp2);
        bdm_exit(0);
      }
    }
  }

  fclose(fp1);
  fclose(fp2);
  rename(f2,f1);

  return(TRUE);
} /*** End of routine revoke_ident ***/


/*
	trans_account - Routine to translate an account name to a username.
*/
int trans_account(username,accountname)
char *username[];
char *accountname[];
{
  FILE *fp1;
  char f1[255];
  char account[ACCOUNTNAMESIZE];

  sprintf(f1,"%s",AUFFILE);
  if(!(fp1 = fopen(f1,"r"))) {
    perror(f1);
    exit(1);
  }
  while (fscanf(fp1,"%s%*d%s",account,username) != EOF) {
    if(strncmp(account,accountname,ACCOUNTNAMESIZE) == 0) {
      fclose(fp1);
      return(TRUE);
    }
  }
  fclose(fp1);                                  /* Close the AUF */
  return(FALSE);

} /*** End of trans_account ***/

/*
	trans_user - Routine to translate a username to an account name.
*/
int trans_user(username,accountname)
char *username[];
char *accountname[];
{
  FILE *fp1;
  char f1[255];
  char usern[USERNAMESIZE];

  sprintf(f1,"%s",AUFFILE);
  if(!(fp1 = fopen(f1,"r"))) {
    perror(f1);
    exit(1);
  }
  while (fscanf(fp1,"%s%*d%s",accountname,usern) != EOF) {
    if(strncmp(usern,username,USERNAMESIZE) == 0) {
      fclose(fp1);
      return(TRUE);
    }
  }
  fclose(fp1);                                  /* Close the AUF */
  return(FALSE);
} /*** End of trans_user ***/
