/*

        Nick Trown      10-19-92
        trown@cscihp.ecst.csuchico.edu
        RSA key manipulation
*/


#include "config.h"
#include <stdio.h>
#ifdef RSA
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/file.h>
#include "defs.h"
#include <string.h>
#include <fcntl.h>
#include "struct.h"
#include "data.h"
#include "proto.h"

#define CATALOG        "catalog"

void readkeys(int convert);
void do_usage(void);
void listkeys(void);
void motdlist(void);
void check_exist(char *name);
void catalog(int num, char *id);
void convert(void);
void deletekey(char *number);
void modifykey(char *number);
void extractkey(char *number, char *filename);
void savekeys(void);
void exist(int num);
void addkey(char *name);
void reorder(int argc, char *argv[]);


struct rsa_key key_list[1000] = { {"\0", "\0", "\0"} };
int key_count=0;

void readkeys(int convert)
{
    int fd;
    int done;

    fd = open(RSA_Key_File, O_RDONLY ,0600);
    if (fd < 0)
    {
        fprintf(stderr, "Can't open the RSA key file %s\n", RSA_Key_File);
        exit(1);
    }

    done = 0;
    do
    {
        if (!convert) {
                if (read(fd, key_list[key_count].client_type, KEY_SIZE) != KEY_SIZE)
                    done = 1;
                if (read(fd, key_list[key_count].architecture, KEY_SIZE) != KEY_SIZE)
                    done = 1;
        }
        if (read(fd, key_list[key_count].global, KEY_SIZE) != KEY_SIZE) {
                done = 1;
        }
        if (read(fd, key_list[key_count].public, KEY_SIZE) != KEY_SIZE) {
                done = 1;
        }
        key_count++;
    } while (! done);

    key_count--;

    close(fd);
}

int main(int argc, char *argv[])
{
    char mode;

    mode = 'l';

    if (argc>1)
        mode = *argv[1];

    getpath();
    switch (mode) {

        case 'l' : listkeys();
            break;
	case 'L' : motdlist();
	    break;
        case 'c' :
                  convert();
            break;
        case 'm' :
            if (argc>2) {
                modifykey(argv[2]);
            }
            else do_usage();
            break;
	case 'x':
	    if (argc>3) {
		extractkey(argv[2], argv[3]);
	    }
	    else do_usage();
	    break;
        case 'd' : {
            if (argc>2) {
                deletekey(argv[2]);
            }
            else do_usage();
            }
            break;
        case 'a' : {
            if (argc>2) {
                addkey(argv[2]);
            }
            else do_usage();
            }
            break;
        case 'v' :  {
            if (argc>2) {
                 check_exist(argv[2]);
            }
            else do_usage();
            }
            break;
	case 'o' :
	    reorder(argc,argv);
	    break;	

        default :
                do_usage();
    }
    return 1;	/* satisfy lint */
}


void do_usage(void)
{
    printf("usage: keys mode <key #> <key file>\n");
    printf("Modes:\n");
    printf("l - list keys (default)\n");
    printf("c - convert. Add type names to RSA file\n");
    printf("d - delete key <key #>\n");
    printf("a - add key <key file>\n");
    printf("v - verify existence of <key file> in RSA file\n");
    printf("m - modify existing key <key #>\n");
    printf("x - extract a key <key #> to a file <res file>\n");
    printf("o - reorder the keys <key number list>\n");
    printf("L - produce a motd listing\n");
    printf("    (note <res file> will have id strings... can be appended to .reserved)\n");
    exit(0);
}

void listkeys(void)
{
    int count;

    readkeys(0);

    printf("There are %d keys in the RSA file\n",key_count);


    for (count = 0; count<key_count; count++) {
        printf("%3d: (%17s | %s)\n",count,key_list[count].client_type,
                                     key_list[count].architecture);
    }

    printf("\n");
    exit(0);
}

void motdlist(void)
{
    int count;
    int c;
    int groups[40][20];
    int groupnum[40];
    char line[40];
    char title[MSG_LEN];
    int groupcount=0;
    int scan;
    int found=0;
	
    readkeys(0);


    for (count=0; count<key_count; count++) {
      for (scan=0; scan<groupcount; scan++) {
	if (!strcmp((char *)key_list[count].client_type,
		    (char *)key_list[groups[scan][0]].client_type)) {
		groups[scan][groupnum[scan]++]= count;
		found=1;
		break;
	}
      }
      if (!found) {
	groups[groupcount][0]=count;
	groupnum[groupcount++]++;
      }
      found=0;
    }

    for (count=0; count<groupcount; count++) {
	for (c=0; c<30; c++)
        	line[c]='-';
	line[30]='\0';
    	strcat(line,"[ ");

	sprintf(title,"%s %-15s ]",line,key_list[groups[count][0]].client_type);
	for (c=0;strlen(title)<MSG_LEN;c++)
	  strcat(title,"-");	
/*
	printf("\n%40ss\n",key_list[groups[count][0]].client_type);
*/
	printf("%s\n",title);
	for (scan=0; scan<groupnum[count];) {
		printf("%-35s",key_list[groups[count][scan++]].architecture);
		if (scan<groupnum[count])
		  printf("%-35s\n",key_list[groups[count][scan++]].architecture);
		else printf("\n");
	}
    }
    printf("\n");
    exit(0);
}
		
	
void check_exist(char *name)
{
    int fd;
    void exist();

    readkeys(0);

    fd = open(name, O_RDONLY,0600);
    if (fd < 0)
    {
        fprintf(stderr, "Can't open key file %s\n", name);
        exit(1);
    }


    if (read(fd,key_list[key_count].global,KEY_SIZE) != KEY_SIZE) {
                printf("global key wrong size\n");
                exit(0);
    }
    if (read(fd,key_list[key_count].public,KEY_SIZE) != KEY_SIZE) {
                printf("public key wrong size\n");
                exit(0);
    }

    exist(key_count);
    printf("Key %s not in RSA file\n",name);
}


void catalog(int num, char *id)
{
    FILE *fp;
    time_t curtime;

    if ((fp = fopen(CATALOG,"a+"))==NULL) {
        printf("Error opening %s\n",CATALOG);
        exit(0);
    }
    curtime = time((time_t *)0);

    fprintf(fp,"Client: %s\n", key_list[num].client_type);
    fprintf(fp,"\tCreator ID: %s\n", id);
    fprintf(fp,"\tAdded     : %s", ctime(&curtime));
    fprintf(fp,"\tArch/OS   : %s\n", key_list[num].architecture);
    fprintf(fp,"\tComments  : \n");

    fclose(fp);

}

void convert(void)
{
    char buffer[MSG_LEN];
    int count;
    void savekeys();

    printf ("WARNING: This should only be used if your .reserved is a file of raw keys!\n");
    printf ("WARNING: This will prepend client type and architecture/OS fields to keys in .reserved.\n");
    printf ("WARNING: This will appent entries to your .catalog file.\n");
    printf ("WARNING: Be certain you want to do this!!!!!!!\n");
    printf ("WARNING: This procedure will alter your RSA_Key_File. Continue?(y/n)");
    fgets(buffer, MSG_LEN, stdin);
    if (!strcmp(buffer, "n")) {
      printf ("Ok, exiting...\n");
      exit (0);
    }
    if (strcmp(buffer, "y")) {
      printf ("unrecognized response... must enter 'y' or 'n'... exiting.\n");
      exit(1);
    }

    readkeys(1);

    printf("There are %d unconverted keys\n",key_count);

    for (count = 0; count< key_count; count++) {

      printf ("Key %d\n", count);

      do {
          printf("Enter type of client: ");
          fgets(buffer, MSG_LEN, stdin);
          if (strlen(buffer) > KEY_SIZE)
                  printf("Client type must be less than %d characters.\n",KEY_SIZE);
      } while (strlen(buffer) > KEY_SIZE);
      strcpy((char *)key_list[count].client_type,buffer);

      do {
          printf("Enter architecture/OS: ");
          fgets(buffer, MSG_LEN, stdin);
          if (strlen(buffer) > KEY_SIZE)
                  printf("Architecture field must be less than %d characters.\n",KEY_SIZE);
      } while (strlen(buffer) > KEY_SIZE);
      strcpy((char *)key_list[count].architecture,buffer);


      do {
        printf("Enter the Creator's ID: ");
        fgets(buffer, MSG_LEN, stdin);
        if (strlen(buffer) > KEY_SIZE)
              printf("ID size too long. Use the creator's login\n");
      } while (strlen(buffer)>KEY_SIZE);

      catalog (count, buffer);
    }

    savekeys();
}

void deletekey(char *number)
{
    int num = atoi(number);
    void savekeys();

    readkeys(0);

    if (num <0) {
        printf("Must be positive\n");
        exit(0);
    }

    if (num >= key_count) {
        printf("Number to large\n");
        exit(0);
    }

    printf("Warning: The entry (%s | %s) in the catalog needs to be deleted.\n",
               key_list[num].client_type, key_list[num].architecture);

    key_list[num] = key_list[--key_count];
    savekeys();
}

void modifykey(char *number)
{
    int num = atoi(number);
    char type[80];
    void savekeys();

    if (num <0) {
        printf("Must be positive\n");
        exit(0);
    }

    readkeys(0);

    if (num >= key_count) {
        printf("Number to large\n");
        exit(0);
    }


    do {
	printf ("Client: %s\n", key_list[num].client_type);
        printf("Enter new type of client (<enter> for no change): ");
        fgets(type, 80, stdin);
        if (strlen(type) > KEY_SIZE)
                printf("Client type must be less than %d characters.\n",KEY_SIZE);
    } while (strlen(type) > KEY_SIZE);
    if (strlen(type)==0)
	printf ("Client type not modified.\n");
    else
        strcpy((char *)key_list[num].client_type,type);

    do {
	printf("Arch/OS: %s\n", key_list[num].architecture);
        printf("Enter new architecture/OS (<enter> for no change): ");
        fgets(type, 80, stdin);
        if (strlen(type) > KEY_SIZE)
                printf("Architecture field must be less than %d characters.\n",KEY_SIZE);
    } while (strlen(type) > KEY_SIZE);
    if (strlen(type)==0)
	printf ("Arch/OS not modified.\n");
    else
    	strcpy((char *)key_list[num].architecture,type);

    savekeys();

}

void extractkey(char *number, char *filename)
{
    int num = atoi(number);
    int fd;
    void savekeys();

    if (num <0) {
        printf("Must be positive\n");
        exit(0);
    }

    readkeys(0);

    if (num >= key_count) {
        printf("Number to large\n");
        exit(0);
    }

    fd = open(filename, O_CREAT | O_WRONLY | O_APPEND, 0600);
    if (fd < 0)
    {
        fprintf(stderr, "Can't open the new key file %s\n", filename);
        exit(1);
    }

    printf("saving key %d (%s | %s) in file %s\n", num, key_list[num].client_type,
                                                   key_list[num].architecture, filename);
    if (write(fd, &key_list[num], sizeof(struct rsa_key)) != sizeof(struct rsa_key))
        printf("Error during file write!\n");

    close(fd);
    exit(0);
}

void savekeys(void)
{
    int fd;
    int count;

    fd = open(RSA_Key_File, O_WRONLY|O_CREAT|O_TRUNC, 0600);
    if (fd < 0)
    {
        fprintf(stderr, "Can't open the RSA key file %s\n", RSA_Key_File);
        exit(1);
    }
    for (count=0; count<key_count; count++) {
        printf("saving key %d (%s | %s)\n", count, key_list[count].client_type,
                                                   key_list[count].architecture);
        if (write(fd, &key_list[count], sizeof(struct rsa_key)) != sizeof(struct rsa_key))
                printf("Error during file write!\n");
    }
    close(fd);
    exit(0);
}

void exist(int num)
{
    int count;

    for (count =0; count<key_count; count++) {
          if ((!memcmp(key_list[num].global,key_list[count].global,KEY_SIZE)) &&
             (!memcmp(key_list[num].public,key_list[count].public,KEY_SIZE))) {
                printf("That key is already in the database with type (%s | %s)\n"
                        ,key_list[count].client_type, key_list[count].architecture);
                exit(0);
          }
    }
}


void addkey(char *name)
{
    int fd;
    char type[80],
         id[KEY_SIZE]; /* creator id */

    readkeys(0);

    fd = open(name, O_RDONLY, 0600);
    if (fd < 0)
    {
        fprintf(stderr, "Can't open key file %s\n", name);
        exit(1);
    }


    if (read(fd,key_list[key_count].global,KEY_SIZE) != KEY_SIZE) {
                printf("global key wrong size\n");
                exit(0);
    }
    if (read(fd,key_list[key_count].public,KEY_SIZE) != KEY_SIZE) {
                printf("public key wrong size\n");
                exit(0);
    }
    exist(key_count);

    do {
      printf("Enter the Creator's ID: ");
      fgets(type, 80, stdin);
      if (strlen(type) > KEY_SIZE)
            printf("ID size too long. Use the creator's login\n");
    } while (strlen(type)>KEY_SIZE);
    strcpy (id, type);

    do {
        printf("Enter type of client:");
        fgets(type, 80, stdin);
        if (strlen(type) > KEY_SIZE)
                printf("Client type must be less than %d characters.\n",KEY_SIZE);
    } while (strlen(type) > KEY_SIZE);
    strcpy((char *)key_list[key_count].client_type,type);

    do {
        printf("Enter architecture/OS:");
        fgets(type, 80, stdin);
        if (strlen(type) > KEY_SIZE)
                printf("Architecture field must be less than %d characters.\n",KEY_SIZE);
    } while (strlen(type) > KEY_SIZE);
    strcpy((char *)key_list[key_count].architecture,type);


    close(fd);
    key_count++;
/*
    fd = open(RSA_Key_File, O_CREAT | O_WRONLY | O_APPEND, 0600);
    if (fd < 0)
    {
        fprintf(stderr, "Can't open the RSA key file %s\n",name);
        exit(1);
    }


    printf("saving key %d (%s | %s)\n", key_count, key_list[key_count].client_type,
                                                   key_list[key_count].architecture);
    if (write(fd, &key_list[key_count], sizeof(struct rsa_key)) != sizeof(struct rsa_key))
            printf("Error during file write!\n");

    close(fd);
*/

    savekeys();
    printf("added key to RSA file\n");

    catalog(key_count, id);

    exit(0);
}


void reorder(int argc, char *argv[])
{
    int count;
    struct rsa_key temp_list[1000];

    readkeys(0);
    if (argc-2<key_count) {
	printf("you must list all the keys\n");
	return;
    }


    memcpy(key_list,
           temp_list,
           key_count*(sizeof(struct rsa_key)));

    for (count=2; count<=argc; count++) {
        memcpy(&temp_list[atoi(argv[count])],
               &key_list[count-2],
               (size_t)sizeof(struct rsa_key));
    }

    savekeys();
}
#else
int main(void)
{
	printf("You don't have RSA verification on.\n");
	return 1;
}
#endif

