/*
   
 Arbitrary Attribute Interchange Protocol , AAIP version 0.2
 Demonstration program for encoding and decoding EA and ACL.
   
 See http://libburnia-project.org/wiki/AAIP

 test/aaip_0_2_test.c - Main program for test binary

 Compile: 
   cc -g -Wall -o test/aaip test/aaip_0_2.c test/aaip_0_2_test.c -lacl
   
 Usage:            ./aaip name value
 Long parameters   ./aaip -name"x100" -value"x100"

 >>> ACLs

*/

#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#include <sys/acl.h>

#include "aaip_0_2.h"

#define Aaip_test_name_sizE 1024
#define Aaip_test_value_sizE 1024


static int print_result(unsigned char *result, size_t result_len, int flag)
{
 int i;

 printf(
     " - - - - - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9\n");
 printf("\n");
 printf("%4u :     ", 0);
 for(i= 0; i < result_len; i++) {
   if(result[i] >= 32 && result[i] <= 126)
     printf("'%c'   ", result[i]);
   else
     printf("%3u   ", (unsigned int) ((unsigned char *) result)[i]);
   if((i % 10) == 9)
     printf("\n%4u :     ", (unsigned int) (i + 1));
 }
 printf("\n\n");
 printf(
     " - - - - - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9\n");
 printf("\n");
 return(1);
}


static int print_attrs(size_t num_attrs, char **names, size_t *value_lengths,
                       char **values, int flag)
{
 size_t i, j;

 for(i= 0; i < num_attrs; i++) {
   printf("name='");
   for(j= 0; names[i][j] != 0; j++) {
     if(names[i][j] >= 32 && names[i][j] <= 126 &&
        names[i][j] != '\\' && names[i][j] != '\'')
       printf("%c", names[i][j]);
     else
       printf("\\%o", (unsigned int) ((unsigned char **) names)[i][j]);
   }
   printf("' (%d)\n", (int) j);
   printf("value='");
   for(j= 0; j < value_lengths[i]; j++) {
     if(values[i][j] >= 32 && values[i][j] <= 126 &&
        values[i][j] != '\\' && values[i][j] != '\'')
       printf("%c", values[i][j]);
     else
       printf("\\%3.3o", (unsigned int) ((unsigned char **) values)[i][j]);
   }
   printf("' (%d)\n\n", (int) j);
 }
 return(1);
}


static int do_touch(char *path, int flag)
{
 FILE *fp;

 fp= fopen(path, "a");
 if(fp == NULL) {
   fprintf(stderr, "fopen(\"%s\") failed: %d  %s\n",
           path, errno, errno != 0 ? strerror(errno) : "");
   return(0);
 }
 fclose(fp);
 return(1);
}


static int decode_acl(unsigned char *result, size_t result_len,
                      char *out_path, int flag)
{
 int ret;
 size_t consumed, text_fill;
 char *text= NULL;

 ret= aaip_decode_acl(result, result_len, &consumed, NULL, 0, &text_fill, 1);
 if(ret <= 0) {
   fprintf(stderr, "aaip_decode_acl(,1) failed: ret= %d\n", ret);
   ret= 0; goto ex;
 }
 text= calloc(text_fill, 1);
 ret= aaip_decode_acl(result, result_len, &consumed, text, text_fill,
                      &text_fill, 0);
 if(ret <= 0) {
   fprintf(stderr, "aaip_decode_acl(,0) failed: ret= %d\n", ret);
   ret= 0; goto ex;
 }
 printf("--- ret= %d , text=\n%s--- end of text\n\n", ret, text);

 if(out_path == NULL)
   {ret= 1; goto ex;}

 ret= do_touch(out_path, 0);
 if(ret <= 0)
   goto ex;

 ret= aaip_set_acl_text(out_path, text, 0);
 if(ret <= 0) {
   fprintf(stderr, "aaip_set_acl_text() failed");
   if(ret == -1)
     fprintf(stderr, ": %d  %s\n", errno, errno != 0 ? strerror(errno) : "");
   else
     fprintf(stderr, "\n");
   ret= 0; goto ex;
 }
 ret= 1;
ex:
 if(text != NULL)
   free(text);
 return(ret);
}


static int test_acl(char *in_path, char *out_path, int flag)
{
 int ret;
 char *acl_text= NULL;
 unsigned char *result= NULL;
 size_t result_len;

 ret= aaip_get_acl_text(in_path, &acl_text, 0);
 if(ret <= 0) {
   fprintf(stderr, "aaip_get_acl_text() failed");
   if(ret == -1)
     fprintf(stderr, ": %d  %s\n", errno, errno != 0 ? strerror(errno) : "");
   else
     fprintf(stderr, "\n");
   ret= 6; goto ex;
 }
 printf("--- ACL:\n%s--- end of ACL\n\n", acl_text);

 ret= aaip_encode_acl(acl_text, &result_len, &result, 0);
 if(ret <= 0) {
   fprintf(stderr, "aaip_encode_acl(text) failed: ret= %d\n", ret);
   ret= 7; goto ex;
 }
 print_result(result, result_len, 0);
 ret= decode_acl(result, result_len, out_path, 0);
 if(ret <= 0)
   {ret= 8; goto ex;}
 free(result); result= NULL;

 ret= aaip_encode_acl(acl_text, &result_len, &result, 2);
 if(ret <= 0) {
   fprintf(stderr, "aaip_encode_acl(num) failed: ret= %d\n", ret);
   ret= 9; goto ex;
 }
 print_result(result, result_len, 0);
 ret= decode_acl(result, result_len, out_path, 0);
 if(ret <= 0)
   {ret= 10; goto ex;}

 ret= 0;
ex:;
 aaip_get_acl_text("", &acl_text, 1 << 15);
 if(result != NULL)
   free(result);
 return(ret);
}


int synthetic_pairs(char *prog, int argc, char **argv, int flag)
{
 int  ret, l, mult= 0, k;
 size_t result_len, i;
 unsigned char *result= NULL;
 char **names= NULL, **values= NULL, *cpt, **param;
 size_t *value_lengths= NULL;

 struct aaip_state *aaip;
 size_t consumed= 0;
 char name[Aaip_test_name_sizE + 1];
 size_t name_fill;
 char value[Aaip_test_value_sizE + 1];
 size_t value_fill;
 size_t todo;
 int submit_data, is_done;
 unsigned char *rpt;
 unsigned int skipped, was_skipped= 0;

 aaip= (struct aaip_state *) calloc(aaip_sizeof_aaip_state(), 1);
 names= calloc(sizeof(char *), argc / 2);
 values= calloc(sizeof(char *), argc / 2);
 value_lengths= calloc(sizeof(size_t), argc / 2);
 
 for(i= 0; i < argc; i++) {
   if(i % 2)
     param= values + i / 2;
   else
     param= names + i / 2;
   (*param)= argv[i];
   if(argv[i][0] == '-') {
     cpt= strchr(argv[i], 'x');
     if(cpt != NULL) {
       l= cpt - argv[i] - 1;
       if(l > 0)
         sscanf(cpt + 1, "%d", &mult);
       if(l > 0 && mult > 0) {
         (*param)= calloc(1, l * mult + 1);
         if((*param) != NULL) {
           for(k= 0; k < mult; k++)
             memcpy((*param) + k * l, argv[i] + 1, l);
           (*param)[mult * l]= 0;
         } else
           (*param)= argv[i];
       }
     }
   }
   if(i % 2)
     value_lengths[i / 2]= strlen(values[i / 2]);
 }

 ret= aaip_encode("AA", (unsigned int) (argc / 2), names,
                  value_lengths, values, 
                  &result_len, &result, 0);
 if(ret <= 0) {
   fprintf(stderr, "%s : aaip_encode failed with ret= %d\n", prog, ret);
   return(2);
 }
 print_result(result, result_len, 0);

 aaip_init_aaip_state(aaip, "AA", 0);
 rpt= result;
 submit_data= 1;
 is_done= 0;
 while(!is_done) {
   if(submit_data) {
     todo= result_len - (rpt - result);
     if(todo > 2048) 
       todo= 2048;
     if(todo == 0) {
       fprintf(stderr, "%s : Out of data while still prompted to submit\n",
               prog);
       return(5);
     }
   } else
     todo= 0;
   ret= aaip_decode_pair(aaip, rpt, todo, &consumed,
                         name, Aaip_test_name_sizE, &name_fill,
                         value, Aaip_test_value_sizE, &value_fill, 0);
   skipped= aaip_get_pairs_skipped(aaip, 0);
   if(skipped > was_skipped) {
     printf("- skipped %d pair%s -\n\n", skipped - was_skipped,
            skipped - was_skipped > 1 ? "s" : "");
     was_skipped= skipped;
   }
   if(ret < 0) {
     fprintf(stderr, "%s : aaip_decode_pair failed with ret= %d\n", prog, ret);
     return(3);
   }
   rpt+= todo;
   if(ret == 0) {
     rpt-= todo;
     submit_data= 0;
 continue;
   } else if(ret == 1) {
     submit_data= 1;
 continue;
   } else if(ret == 2) {
     submit_data= 1;
   } else if(ret == 3) {
     submit_data= 0;
   } else if(ret == 4) {
     is_done= 1;
   } else if(ret == 5) {
     is_done= 1;
 break;
   } else {
     fprintf(stderr, "%s : Unknown return %d from aaip_decode_pair()\n",
             prog, ret);
     return(4);
   }
   name[name_fill]= 0;
   value[value_fill]= 0;
   if((name_fill < 1000 && value_fill < 1000)) {
     printf("name = '%s' (%lu)\n", name, (unsigned long) name_fill);
     printf("value= '%s' (%lu)\n", value, (unsigned long) value_fill);
   } else {
     printf("name = (%lu)\n", (unsigned long) name_fill);
     printf("value= (%lu)\n", (unsigned long) value_fill);
   }
   printf("\n");
 }

 return(0);
}


/*
  @param flag bit0= use numeric qualifiers
*/
static int copy_all(char *in_path, char *out_path, int flag)
{
 int ret;
 struct aaip_state *aaip= NULL;
 size_t in_num_attrs, *in_value_lengths= NULL;
 char **in_names= NULL, **in_values= NULL;
 int is_done= 0, first_round= 1;
 unsigned char *result= NULL, *rpt;
 size_t result_len, todo, consumed;
 size_t out_num_attrs, *out_value_lengths= NULL;
 char **out_names= NULL, **out_values= NULL;
 
 ret= aaip_get_attr_list(in_path, &in_num_attrs, &in_names, &in_value_lengths,
                         &in_values, 1 | ((flag & 1) << 1));
 if(ret <= 0)
   {ret= 11; goto ex;}
 print_attrs(in_num_attrs, in_names, in_value_lengths, in_values, 0);

 ret= aaip_encode("AA", (unsigned int) in_num_attrs, in_names,
                  in_value_lengths, in_values, &result_len, &result, 0);
 if(ret == 0)
   {ret= 12; goto ex;}

 if(result_len <= 0) {
   fprintf(stderr, "No result\n");
   ret= 13; goto ex;
 }
 print_result(result, result_len, 0);

 rpt= result;
 while(!is_done) {
   todo= result_len - (rpt - result);
   if(todo > 2048) 
       todo= 2048;
   if(todo == 0) {
     fprintf(stderr, "Out of data while still prompted to submit\n");
     ret= 14; goto ex;
   }
   /* Allow 1 million bytes of memory consumption, 100,000 attributes */
   ret= aaip_decode_attrs(&aaip, "AA", (size_t) 1000000, (size_t) 100000,
                          rpt, todo, &consumed, first_round);
   rpt+= consumed;
   first_round= 0;
   if(ret == 1)
 continue;
   if(ret == 2)
 break;
   fprintf(stderr, "aaip_decode_attrs() returns %d\n", ret);
   ret= 15; goto ex;
 }
 if(rpt - result != result_len) {
   fprintf(stderr, "aaip_decode_attrs() returns 2 but %d bytes are left\n",
           (int) (result_len - (rpt - result)));
   ret= 16; goto ex;
 }
 ret= aaip_get_decoded_attrs(&aaip, &out_num_attrs, &out_names,
                             &out_value_lengths, &out_values, 0);

 if(ret != 1) {
   fprintf(stderr, "aaip_get_decoded_attrs() returns %d\n", ret);
   ret= 17; goto ex;
 }
 print_attrs(out_num_attrs, out_names, out_value_lengths, out_values, 0);

 ret= do_touch(out_path, 0);
 if(ret <= 0)
   {ret= 19; goto ex;}

 ret= aaip_set_attr_list(out_path, out_num_attrs, out_names, out_value_lengths,
                         out_values, 1 | 2);
 if(ret != 1) {
   fprintf(stderr, "aaip_set_attr_list() returns %d\n", ret);
   ret= 18; goto ex;
 }
 ret= 0;
ex:;
 if(in_names != NULL || in_value_lengths != NULL || in_values != NULL)
   aaip_get_attr_list(in_path, &in_num_attrs, &in_names, &in_value_lengths,
                      &in_values, 1 << 15);
 if(out_names != NULL || out_value_lengths != NULL || out_values != NULL)
   aaip_get_decoded_attrs(&aaip, &out_num_attrs, &out_names,
                          &out_value_lengths, &out_values, 15);
 aaip_decode_attrs(&aaip, "AA", (size_t) 0, (size_t) 0, NULL, (size_t) 0,
                   &consumed, 1 << 15);
 return(ret);
}


int main(int argc, char **argv)
{
 int ret;

 if(argc < 2) {
usage:
   fprintf(stderr, "usage : %s options\n", argv[0]);
   fprintf(stderr, "  -pairs [-]name[xNNN] [-]value[xNNN] [...]\n");
   fprintf(stderr, "  -copy_acl source_path target_path\n");
   fprintf(stderr, "  -copy source_path target_path\n");
   exit(1);
 }
 if(strcmp(argv[1], "-pairs") == 0) {
   if(argc < 4 || (argc % 2) != 0)
     goto usage;
   ret= synthetic_pairs(argv[0], argc - 2, argv + 2, 0);
 } else if(strcmp(argv[1], "-copy_acl") == 0) {
   if(argc != 4)
     goto usage;
   ret= test_acl(argv[2], argv[3], 0);
 } else if(strcmp(argv[1], "-copy") == 0) {
   if(argc != 4)
     goto usage;
   ret= copy_all(argv[2], argv[3], 1);
 } else
   goto usage;
 exit(ret);
}