A first implementation of AAIP 0.2 encoding and decoding
This commit is contained in:
parent
f348faeb5f
commit
ef5a1c48ba
688
test/aaip_0_2.c
Normal file
688
test/aaip_0_2.c
Normal file
@ -0,0 +1,688 @@
|
||||
|
||||
/*
|
||||
|
||||
Arbitrary Attribute Interchange Protocol , AAIP version 0.2
|
||||
Demonstration program for encoding and decoding EA and ACL.
|
||||
|
||||
See test/aaip_0_2.h
|
||||
http://libburnia-project.org/wiki/AAIP
|
||||
|
||||
>>> ACLs
|
||||
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* <<< */
|
||||
#define Aaip_encode_debuG 1
|
||||
|
||||
#include "aaip_0_2.h"
|
||||
|
||||
|
||||
/* --------------------------------- Encoder ---------------------------- */
|
||||
|
||||
|
||||
static int aaip_encode_pair(char *name, size_t attr_length, char *attr,
|
||||
unsigned int *num_recs, size_t *comp_size,
|
||||
unsigned char *result, size_t result_fill,
|
||||
int flag);
|
||||
|
||||
|
||||
/* Convert an array of Arbitrary Attributes into a series of AAIP fields.
|
||||
@param aa_name The 2 byte SUSP Signature Word of the fields
|
||||
@param num_attrs Number of attributes
|
||||
@param names Array of pointers to 0 terminated name strings
|
||||
@param attr_lengths Array of byte lengths for each attribute payload
|
||||
@param attrs Array of pointers to the attribute payload bytes
|
||||
@param result_len Number of bytes in the resulting SUSP field string
|
||||
@param result *result will point to the start of the result string.
|
||||
This is malloc() memory which needs to be freed when
|
||||
no longer needed
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= set CONTINUE bit of last AA field to 1
|
||||
@return >0 is the number of SUSP fields generated,
|
||||
0 means error
|
||||
*/
|
||||
unsigned int aaip_encode(char aa_name[2],
|
||||
unsigned int num_attrs, char **names,
|
||||
size_t *attr_lengths, char **attrs,
|
||||
size_t *result_len, unsigned char **result, int flag)
|
||||
{
|
||||
size_t mem_size= 0, comp_size;
|
||||
unsigned int number_of_fields, i, num_recs, total_recs= 0, ret;
|
||||
|
||||
/* Predict memory needs, number of SUSP fields and component records */
|
||||
*result_len= 0;
|
||||
for(i= 0; i < num_attrs; i++) {
|
||||
ret= aaip_encode_pair(names[i], attr_lengths[i], attrs[i],
|
||||
&num_recs, &comp_size, NULL, (size_t) 0, 1);
|
||||
if(ret <= 0)
|
||||
return(ret);
|
||||
mem_size+= comp_size;
|
||||
total_recs= num_recs;
|
||||
}
|
||||
number_of_fields= mem_size / 250 + !!(mem_size % 250);
|
||||
mem_size+= number_of_fields * 5;
|
||||
|
||||
#ifdef Aaip_encode_debuG
|
||||
*result= (unsigned char *) calloc(1, mem_size + 1024000);
|
||||
/* generous honeypot for overflows */
|
||||
#else
|
||||
*result= (unsigned char *) calloc(1, mem_size);
|
||||
#endif
|
||||
|
||||
|
||||
/* Encode pairs into result */
|
||||
for(i= 0; i < num_attrs; i++) {
|
||||
ret= aaip_encode_pair(names[i], attr_lengths[i], attrs[i],
|
||||
&num_recs, &comp_size, *result, *result_len, 0);
|
||||
if(ret <= 0)
|
||||
return(ret);
|
||||
(*result_len)+= comp_size;
|
||||
}
|
||||
|
||||
/* write the field headers */
|
||||
for(i= 0; i < number_of_fields; i++) {
|
||||
(*result)[i * 255 + 0]= aa_name[0];
|
||||
(*result)[i * 255 + 1]= aa_name[1];
|
||||
if(i < number_of_fields - 1 || (mem_size % 255) == 0)
|
||||
(*result)[i * 255 + 2]= 255;
|
||||
else
|
||||
(*result)[i * 255 + 2]= mem_size % 255;
|
||||
(*result)[i * 255 + 3]= 1;
|
||||
(*result)[i * 255 + 4]= (flag & 1) || (i < number_of_fields - 1);
|
||||
}
|
||||
(*result_len)+= number_of_fields * 5;
|
||||
|
||||
#ifdef Aaip_encode_debuG
|
||||
if(*result_len != mem_size) {
|
||||
fprintf(stderr, "aaip_encode(): MEMORY MISMATCH BY %d BYTES\n",
|
||||
(int) (mem_size - *result_len));
|
||||
}
|
||||
ret= 0;
|
||||
for(i= 0; i < *result_len; i+= ((unsigned char *) (*result))[i + 2])
|
||||
ret++;
|
||||
if(ret != number_of_fields) {
|
||||
fprintf(stderr, "aaip_encode(): WRONG NUMBER OF FIELDS %d <> %d\n",
|
||||
number_of_fields, ret);
|
||||
}
|
||||
#endif /* Aaip_encode_debuG */
|
||||
|
||||
return(number_of_fields);
|
||||
}
|
||||
|
||||
|
||||
static void aaip_encode_byte(unsigned char *result, size_t *result_fill,
|
||||
unsigned char value)
|
||||
{
|
||||
result[(*result_fill / 250) * 255 + 5 + (*result_fill % 250)]= value;
|
||||
(*result_fill)++;
|
||||
}
|
||||
|
||||
|
||||
static int aaip_encode_comp(unsigned char *result, size_t *result_fill,
|
||||
char *data, size_t l, int flag)
|
||||
{
|
||||
size_t todo;
|
||||
char *rpt, *comp_start;
|
||||
|
||||
if(l == 0) {
|
||||
aaip_encode_byte(result, result_fill, 0);
|
||||
aaip_encode_byte(result, result_fill, 0);
|
||||
return(1);
|
||||
}
|
||||
for(rpt= data; rpt - data < l;) {
|
||||
todo= l - (rpt - data);
|
||||
aaip_encode_byte(result, result_fill, (todo > 255));
|
||||
if(todo > 255)
|
||||
todo= 255;
|
||||
aaip_encode_byte(result, result_fill, todo);
|
||||
for(comp_start= rpt; rpt - comp_start < todo; rpt++)
|
||||
aaip_encode_byte(result, result_fill, *((unsigned char *) rpt));
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* Write the component records for name and attr. Skip the positions of
|
||||
AA field headers.
|
||||
@param flag bit0= only count but do not produce result
|
||||
*/
|
||||
static int aaip_encode_pair(char *name, size_t attr_length, char *attr,
|
||||
unsigned int *num_recs, size_t *comp_size,
|
||||
unsigned char *result, size_t result_fill,
|
||||
int flag)
|
||||
{
|
||||
size_t l;
|
||||
|
||||
l= strlen(name);
|
||||
*num_recs= l / 255 + (!!(l % 255)) + (l == 0) +
|
||||
attr_length / 255 + (!!(attr_length % 255)) + (attr_length == 0);
|
||||
*comp_size= l + attr_length + 2 * *num_recs;
|
||||
|
||||
if(flag & 1)
|
||||
return(1);
|
||||
|
||||
aaip_encode_comp(result, &result_fill, name, l, 0);
|
||||
aaip_encode_comp(result, &result_fill, attr, attr_length, 0);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* >>> Encoder for ACLs */;
|
||||
|
||||
|
||||
/* --------------------------------- Decoder ---------------------------- */
|
||||
|
||||
/* --- private --- */
|
||||
|
||||
/* Not less than 2 * 2048 */
|
||||
#define Aaip_buffer_sizE 4096
|
||||
|
||||
struct aaip_state {
|
||||
|
||||
/* AA field status */
|
||||
unsigned char aa_name[2];
|
||||
int aa_head_missing; /* number of bytes needed to complete AA field header */
|
||||
int aa_missing; /* number of bytes needed to complete current AA field */
|
||||
int aa_ends; /* 0= still AA fields expected, 1= last AA being processed,
|
||||
2= all AA fields processed, 3= all is delivered */
|
||||
|
||||
/* Buffer for component records */
|
||||
int recs_invalid; /* number of components to skip */
|
||||
unsigned char recs[Aaip_buffer_sizE];
|
||||
size_t recs_fill;
|
||||
int rec_head_missing; /* number of bytes needed to complete rec header */
|
||||
int rec_missing; /* number of bytes needed to complete current rec */
|
||||
int rec_ends;
|
||||
|
||||
/* Counter for completed data */
|
||||
unsigned int num_recs;
|
||||
size_t ready_bytes;
|
||||
|
||||
/* Counter and meaning for completed components */
|
||||
unsigned int num_components;
|
||||
int first_is_name;
|
||||
|
||||
/* Last return value of aaip_decode_pair() */
|
||||
int pair_status;
|
||||
unsigned int pairs_skipped;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* ------- functions ------ */
|
||||
|
||||
|
||||
size_t aaip_sizeof_aaip_state(void)
|
||||
{
|
||||
return((size_t) sizeof(struct aaip_state));
|
||||
}
|
||||
|
||||
|
||||
int aaip_init(struct aaip_state *aaip, char aa_name[2], int flag)
|
||||
{
|
||||
aaip->aa_name[0]= aa_name[0];
|
||||
aaip->aa_name[1]= aa_name[1];
|
||||
aaip->aa_head_missing= 5;
|
||||
aaip->aa_missing= 0;
|
||||
|
||||
aaip->recs_invalid= 0;
|
||||
memset(aaip->recs, 0, Aaip_buffer_sizE);
|
||||
aaip->recs_fill= 0;
|
||||
aaip->rec_head_missing= 2;
|
||||
aaip->rec_missing= 0;
|
||||
aaip->rec_ends= 0;
|
||||
|
||||
aaip->num_recs= 0;
|
||||
aaip->ready_bytes= 0;
|
||||
|
||||
aaip->num_components= 0;
|
||||
aaip->first_is_name= 1;
|
||||
|
||||
aaip->pair_status= 1;
|
||||
aaip->pairs_skipped= 0;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int aaip_consume_rec_head(struct aaip_state *aaip,
|
||||
unsigned char **data, size_t *num_data, int flag)
|
||||
{
|
||||
size_t todo, i;
|
||||
|
||||
todo= *num_data;
|
||||
if(todo > aaip->aa_missing)
|
||||
todo= aaip->aa_missing;
|
||||
if(todo >= aaip->rec_head_missing)
|
||||
todo= aaip->rec_head_missing;
|
||||
if(!aaip->recs_invalid) {
|
||||
memcpy(aaip->recs + aaip->recs_fill, *data + i, todo);
|
||||
aaip->recs_fill+= todo;
|
||||
}
|
||||
aaip->rec_head_missing-= todo;
|
||||
if(aaip->rec_head_missing == 0) {
|
||||
aaip->rec_missing= aaip->recs[aaip->recs_fill - 1];
|
||||
aaip->rec_ends= !(aaip->recs[aaip->recs_fill - 2] & 1);
|
||||
}
|
||||
aaip->aa_missing-= todo;
|
||||
(*num_data)-= todo;
|
||||
(*data)+= todo;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int aaip_consume_rec_data(struct aaip_state *aaip,
|
||||
unsigned char **data, size_t *num_data, int flag)
|
||||
{
|
||||
size_t todo, i;
|
||||
|
||||
todo= *num_data;
|
||||
if(todo > aaip->aa_missing)
|
||||
todo= aaip->aa_missing;
|
||||
if(todo > aaip->rec_missing)
|
||||
todo= aaip->rec_missing;
|
||||
if(!aaip->recs_invalid) {
|
||||
memcpy(aaip->recs + aaip->recs_fill, *data + i, todo);
|
||||
aaip->recs_fill+= todo;
|
||||
aaip->ready_bytes+= todo;
|
||||
}
|
||||
aaip->rec_missing-= todo;
|
||||
aaip->aa_missing-= todo;
|
||||
(*num_data)-= todo;
|
||||
(*data)+= todo;
|
||||
if(aaip->rec_missing <= 0) {
|
||||
if(aaip->recs_invalid > 0) {
|
||||
if(aaip->rec_ends)
|
||||
aaip->recs_invalid--;
|
||||
} else {
|
||||
aaip->num_recs++;
|
||||
if(aaip->rec_ends)
|
||||
aaip->num_components++;
|
||||
}
|
||||
aaip->rec_head_missing= 2;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static int aaip_consume_aa_head(struct aaip_state *aaip,
|
||||
unsigned char **data, size_t *num_data, int flag)
|
||||
{
|
||||
size_t todo, i;
|
||||
|
||||
todo= *num_data;
|
||||
if(todo >= aaip->aa_head_missing)
|
||||
todo= aaip->aa_head_missing;
|
||||
for(i= 0; i < todo; i++)
|
||||
aaip->recs[aaip->recs_fill++]= (*data)[i];
|
||||
aaip->aa_head_missing-= todo;
|
||||
if(aaip->aa_head_missing == 0) {
|
||||
if(aaip->recs[aaip->recs_fill - 5] != aaip->aa_name[0] ||
|
||||
aaip->recs[aaip->recs_fill - 4] != aaip->aa_name[1] ||
|
||||
aaip->recs[aaip->recs_fill - 2] != 1)
|
||||
return(-1);
|
||||
aaip->aa_missing= aaip->recs[aaip->recs_fill - 3];
|
||||
aaip->aa_ends= !(aaip->recs[aaip->recs_fill - 1] & 1);
|
||||
aaip->recs_fill-= 5; /* AA heads do not get delivered */
|
||||
if(aaip->aa_missing >= 5)
|
||||
aaip->aa_missing-= 5;
|
||||
else
|
||||
aaip->aa_missing= 0;
|
||||
}
|
||||
(*num_data)-= todo;
|
||||
(*data)+= todo;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int aaip_consume_aa_data(struct aaip_state *aaip,
|
||||
unsigned char **data, size_t *num_data, int flag)
|
||||
{
|
||||
while(*num_data > 0 && aaip->aa_missing > 0) {
|
||||
if(aaip->rec_head_missing > 0) {
|
||||
aaip_consume_rec_head(aaip, data, num_data, 0);
|
||||
if(*num_data == 0 || aaip->aa_missing <= 0)
|
||||
return(1);
|
||||
}
|
||||
aaip_consume_rec_data(aaip, data, num_data, 0);
|
||||
}
|
||||
if(aaip->aa_missing <= 0) {
|
||||
if(aaip->aa_ends) {
|
||||
|
||||
/* >>> Check for incomplete component or pair, discard it */;
|
||||
|
||||
aaip->aa_ends= 2;
|
||||
} else
|
||||
aaip->aa_head_missing= 5;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* Submit small data chunk for decoding.
|
||||
The return value will tell whether data are pending for being fetched.
|
||||
@param aaip The AAIP decoder context
|
||||
@param data Not more than 2048 bytes input for the decoder
|
||||
@parm num_data Number of bytes in data
|
||||
0 inquires the buffer status avoiding replies <= 0
|
||||
@param ready_bytes Number of decoded bytes ready for delivery
|
||||
@param flag Bitfield for control purposes
|
||||
@return -1= non-AA field detected
|
||||
*ready_bytes gives number of consumed bytes in data
|
||||
0= cannot accept data because buffer full
|
||||
1= no component record complete, submit more data
|
||||
2= component record complete, may be delivered
|
||||
3= component complete, may be delivered
|
||||
4= no component available, no more data expected, done
|
||||
*/
|
||||
int aaip_submit_data(struct aaip_state *aaip,
|
||||
unsigned char *data, size_t num_data,
|
||||
size_t *ready_bytes, int flag)
|
||||
{
|
||||
int ret;
|
||||
unsigned char *in_data;
|
||||
|
||||
if(aaip->aa_ends == 3)
|
||||
return(4);
|
||||
in_data= data;
|
||||
if(num_data == 0)
|
||||
goto ex;
|
||||
if(aaip->recs_fill + num_data > Aaip_buffer_sizE)
|
||||
return(0);
|
||||
|
||||
while(aaip->aa_missing == 0 && num_data > 0) {
|
||||
if(aaip->aa_head_missing > 0) {
|
||||
ret= aaip_consume_aa_head(aaip, &data, &num_data, 0);
|
||||
if(ret < 0) {
|
||||
*ready_bytes= data - in_data;
|
||||
return(-1);
|
||||
}
|
||||
if(num_data == 0 || aaip->aa_missing <= 0)
|
||||
goto ex;
|
||||
}
|
||||
aaip_consume_aa_data(aaip, &data, &num_data, 0);
|
||||
}
|
||||
ex:;
|
||||
*ready_bytes= aaip->ready_bytes;
|
||||
if(aaip->num_components > 0)
|
||||
return(3);
|
||||
if(aaip->num_recs > 0)
|
||||
return(2);
|
||||
if(aaip->aa_ends && aaip->aa_head_missing == 0 && aaip->aa_missing == 0)
|
||||
aaip->aa_ends= 2;
|
||||
if(aaip->aa_ends == 2 && aaip->num_recs == 0)
|
||||
aaip->aa_ends= 3;
|
||||
if(aaip->aa_ends == 3)
|
||||
return(4);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* Fetch the available part of current component.
|
||||
The return value will tell whether it belongs to name or to value and
|
||||
whether that name or value is completed now.
|
||||
@param aaip The AAIP decoder context
|
||||
@param result Has to point to storage for the component data
|
||||
@param result_size Gives the amount of provided result storage
|
||||
@param num_result Will tell the number of fetched result bytes
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= discard data rather than copying to result
|
||||
@return -2 = insufficient result_size
|
||||
-1 = no data ready for delivery
|
||||
0 = result holds the final part of a name
|
||||
1 = result holds an intermediate part of a name
|
||||
2 = result holds the final part of a value
|
||||
3 = result holds an intermediate part of a value
|
||||
*/
|
||||
int aaip_fetch_data(struct aaip_state *aaip,
|
||||
char *result, size_t result_size, size_t *num_result,
|
||||
int flag)
|
||||
{
|
||||
int ret= -1, complete= 0;
|
||||
unsigned int i, num_bytes= 0, h;
|
||||
|
||||
if(aaip->num_recs == 0)
|
||||
return(-1);
|
||||
|
||||
/* Copy data until end of buffer or end of component */
|
||||
h= 0;
|
||||
for(i= 0; i < aaip->num_recs && !complete; i++) {
|
||||
if(!(flag & 1)) {
|
||||
if(num_bytes + aaip->recs[h + 1] > result_size)
|
||||
return(-2);
|
||||
memcpy(result + num_bytes, aaip->recs + h + 2, aaip->recs[h + 1]);
|
||||
*num_result= num_bytes + aaip->recs[h + 1];
|
||||
}
|
||||
num_bytes+= aaip->recs[h + 1];
|
||||
if(!(aaip->recs[h] & 1))
|
||||
complete= 1;
|
||||
h+= aaip->recs[h + 1] + 2;
|
||||
}
|
||||
aaip->num_recs-= i;
|
||||
aaip->ready_bytes-= num_bytes;
|
||||
|
||||
/* Shift buffer */
|
||||
if(h < aaip->recs_fill)
|
||||
memmove(aaip->recs, aaip->recs + h, aaip->recs_fill - h);
|
||||
aaip->recs_fill-= h;
|
||||
|
||||
/* Compute reply */
|
||||
ret= 2 * !aaip->first_is_name;
|
||||
if(complete) {
|
||||
aaip->first_is_name= !aaip->first_is_name;
|
||||
if(aaip->num_components > 0)
|
||||
aaip->num_components--;
|
||||
} else
|
||||
ret|= 1;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
/* Skip the current component and eventually the following value component.
|
||||
This has to be called if fetching of a component shall be aborted
|
||||
but the next component resp. pair shall be fetchable again.
|
||||
aaip_submit_data() will not indicate readiness for fetching until all
|
||||
bytes of the skipped components are submitted. Those bytes get discarded.
|
||||
@param aaip The AAIP decoder context
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= do not skip value if current component is name
|
||||
@return <=0 error , 1= now in skip state, 2= not in skip state
|
||||
*/
|
||||
int aaip_skip_component(struct aaip_state *aaip, int flag)
|
||||
{
|
||||
int to_skip= 1;
|
||||
|
||||
if(aaip->recs_invalid) {
|
||||
aaip->recs_invalid+= 1 + (flag & 1);
|
||||
return(1);
|
||||
}
|
||||
if(aaip->first_is_name && !(flag & 1))
|
||||
to_skip= 2;
|
||||
|
||||
if(aaip->num_components) {
|
||||
/* null-fetch */
|
||||
aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1);
|
||||
to_skip--;
|
||||
}
|
||||
if(aaip->num_components && to_skip) {
|
||||
/* null-fetch */
|
||||
aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1);
|
||||
to_skip--;
|
||||
}
|
||||
if(to_skip) {
|
||||
aaip->recs_fill= 0;
|
||||
aaip->num_recs= 0;
|
||||
aaip->ready_bytes= 0;
|
||||
}
|
||||
aaip->recs_invalid= to_skip;
|
||||
if(aaip->aa_ends == 2 && aaip->num_recs == 0)
|
||||
aaip->aa_ends= 3;
|
||||
return(1 + (aaip->num_recs > 0));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@return see aaip_decode_pair
|
||||
*/
|
||||
static int aaip_advance_pair(struct aaip_state *aaip,
|
||||
char *name, size_t name_size, size_t *name_fill,
|
||||
char *value, size_t value_size, size_t *value_fill,
|
||||
int flag)
|
||||
{
|
||||
int ret;
|
||||
char *wpt;
|
||||
size_t size, num;
|
||||
|
||||
retry:;
|
||||
if(aaip->first_is_name) {
|
||||
wpt= name + *name_fill;
|
||||
size= name_size - *name_fill;
|
||||
} else {
|
||||
wpt= value + *value_fill;
|
||||
size= value_size - *value_fill;
|
||||
}
|
||||
ret= aaip_fetch_data(aaip, wpt, size, &num, 0);
|
||||
if(ret == -2) { /* insufficient result size */
|
||||
ret= aaip_skip_component(aaip, 0);
|
||||
*name_fill= *value_fill= 0;
|
||||
aaip->pairs_skipped++;
|
||||
if(ret == 2) /* Skip performed, valid data pending */
|
||||
goto retry;
|
||||
} else if(ret == -1) { /* No data ready for delivery : may not happen */
|
||||
return(-1);
|
||||
} else if(ret == 0) { /* result holds the final part of a name */
|
||||
(*name_fill)+= num;
|
||||
/* peek for value data */
|
||||
ret= aaip_submit_data(aaip, NULL, (size_t) 0, &num, 0);
|
||||
if(ret == 2 || ret == 3) {
|
||||
/* fetch value data */;
|
||||
ret= aaip_advance_pair(aaip, name, name_size, name_fill,
|
||||
value, value_size, value_fill, flag);
|
||||
return ret;
|
||||
} else if(ret == 4)
|
||||
return(5);
|
||||
} else if(ret == 1) { /* result holds an intermediate part of a name */
|
||||
(*name_fill)+= num;
|
||||
} else if(ret == 2) { /* result holds the final part of a value */
|
||||
(*value_fill)+= num;
|
||||
if(aaip->num_components >= 2)
|
||||
return(3);
|
||||
if(aaip->aa_ends == 2 && aaip->num_recs == 0)
|
||||
aaip->aa_ends= 3;
|
||||
if(aaip->aa_ends == 3)
|
||||
return(4);
|
||||
return(2);
|
||||
} else if(ret == 3) {
|
||||
/* result holds an intermediate part of a value */;
|
||||
(*value_fill)+= num;
|
||||
} else {
|
||||
return(-1); /* unknown reply from aaip_fetch_data() */
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* Accept raw input data and collect a pair of name and value.
|
||||
The return value will indicate whether the pair is complete, whether more
|
||||
pairs are complete or whether more data are desired. No input data will be
|
||||
accepted as long as complete pairs are pending. The end of the attribute
|
||||
list will be indicated.
|
||||
@param aaip The AAIP decoder context
|
||||
@param data The raw data to decode
|
||||
@param num_data Number of data bytes provided
|
||||
@param consumed Returns the number of consumed data bytes
|
||||
@param name Buffer to build the name string
|
||||
@param name_size Maximum number of bytes in name
|
||||
@param name_fill Holds the current buffer fill of name
|
||||
@param value Buffer to build the value string
|
||||
@param value_size Maximum number of bytes in value
|
||||
@param value_fill Holds the current buffer fill of value
|
||||
@param flag Bitfield for control purposes - submit 0 for now
|
||||
@return <0 error
|
||||
0 data not accepted, first fetch pending pairs with num_data == 0
|
||||
1 name and value are not valid yet, submit more data
|
||||
2 name and value are valid, submit more data
|
||||
3 name and value are valid, pairs pending, fetch with num_data == 0
|
||||
4 name and value are valid, no more data expected
|
||||
5 name and value are not valid, no more data expected
|
||||
*/
|
||||
int aaip_decode_pair(struct aaip_state *aaip,
|
||||
unsigned char *data, size_t num_data, size_t *consumed,
|
||||
char *name, size_t name_size, size_t *name_fill,
|
||||
char *value, size_t value_size, size_t *value_fill,
|
||||
int flag)
|
||||
{
|
||||
int ret;
|
||||
size_t ready_bytes;
|
||||
|
||||
*consumed= 0;
|
||||
if(aaip->pair_status < 0 || aaip->pair_status == 4 ||
|
||||
aaip->pair_status == 5) { /* dead ends */
|
||||
ret= aaip->pair_status;
|
||||
goto ex;
|
||||
} else if(aaip->pair_status == 2 || aaip->pair_status == 3) {
|
||||
if(aaip->pair_status == 3 && num_data > 0)
|
||||
{ret= 0; goto ex;}
|
||||
/* Start a new pair */
|
||||
if(!aaip->first_is_name) /* Eventually skip orphaned value */
|
||||
aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1);
|
||||
*name_fill= *value_fill= 0;
|
||||
}
|
||||
|
||||
if(num_data > 0) {
|
||||
ret= aaip_submit_data(aaip, data, num_data, &ready_bytes, 0);
|
||||
} else {
|
||||
ret= 1;
|
||||
if(aaip->num_components)
|
||||
ret= 3;
|
||||
else if(aaip->num_recs)
|
||||
ret= 2;
|
||||
}
|
||||
if(ret < 0) { /* non-AA field detected */
|
||||
*consumed= ready_bytes;
|
||||
{ret= -1; goto ex;}
|
||||
} else if(ret == 0) { /* buffer overflow */;
|
||||
/* should not happen with correct usage */
|
||||
{ret= -2; goto ex;}
|
||||
} else if(ret == 1) { /* no component record complete */
|
||||
goto ex;
|
||||
} else if(ret == 2) { /* component record complete, may be delivered */
|
||||
;
|
||||
} else if(ret == 3) { /* component complete, may be delivered */
|
||||
;
|
||||
} else if(ret == 4) { /* no component available, no more data expected */
|
||||
{ret= 5; goto ex;}
|
||||
} else
|
||||
{ret= -1; goto ex;} /* unknown reply from aaip_submit_data() */
|
||||
|
||||
*consumed= num_data;
|
||||
ret= aaip_advance_pair(aaip, name, name_size, name_fill,
|
||||
value, value_size, value_fill, 0);
|
||||
if(aaip->aa_ends == 3) {
|
||||
if(ret >= 2 && ret <= 4)
|
||||
ret= 4;
|
||||
else
|
||||
ret= 5;
|
||||
}
|
||||
ex:;
|
||||
aaip->pair_status= ret;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag)
|
||||
{
|
||||
return(aaip->pairs_skipped);
|
||||
}
|
||||
|
||||
|
||||
/* >>> Decoder for ACLs */;
|
||||
|
187
test/aaip_0_2.h
Normal file
187
test/aaip_0_2.h
Normal file
@ -0,0 +1,187 @@
|
||||
|
||||
/*
|
||||
|
||||
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.h - Public declarations
|
||||
|
||||
*/
|
||||
|
||||
#ifndef Aaip_h_is_includeD
|
||||
#define Aaip_h_is_includeD yes
|
||||
|
||||
|
||||
/* --------------------------------- Encoder ---------------------------- */
|
||||
|
||||
/* Convert an array of Arbitrary Attributes into a series of AAIP fields.
|
||||
@param aa_name The 2 byte SUSP Signature Word of the fields
|
||||
@param num_attrs Number of attributes
|
||||
@param names Array of pointers to 0 terminated name strings
|
||||
@param attr_lengths Array of byte lengths for each attribute payload
|
||||
@param attrs Array of pointers to the attribute payload bytes
|
||||
@param result_len Number of bytes in the resulting SUSP field string
|
||||
@param result *result will point to the start of the result string.
|
||||
This is malloc() memory which needs to be freed when
|
||||
no longer needed
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= set CONTINUE bit of last AA field to 1
|
||||
@return >0 is the number of SUSP fields generated,
|
||||
0 means error
|
||||
*/
|
||||
unsigned int aaip_encode(char aa_name[2],
|
||||
unsigned int num_attrs, char **names,
|
||||
size_t *attr_lengths, char **attrs,
|
||||
size_t *result_len, unsigned char **result, int flag);
|
||||
|
||||
|
||||
/* >>> Encoder for ACLs */;
|
||||
|
||||
|
||||
/* --------------------------------- Decoder ---------------------------- */
|
||||
|
||||
/*
|
||||
The AAIP decoder avoids the use of dynamic memory. It rather provides a
|
||||
stateful decoding context with a small buffer which delivers results to
|
||||
caller provided memory locations.
|
||||
This may be done stream-like via the Component Level Interface or to
|
||||
fixly sized storage for name and value via the Pair Level Interface.
|
||||
*/
|
||||
|
||||
|
||||
/* The AAIP decoder context.
|
||||
*/
|
||||
struct aaip_state;
|
||||
|
||||
|
||||
/* Obtain the size in bytes of an aaip_state object.
|
||||
*/
|
||||
size_t aaip_sizeof_aaip_state(void);
|
||||
|
||||
|
||||
/* Initialize a AAIP decoder context.
|
||||
This has to be done before the first AA field of a node is processed.
|
||||
The caller has to provide the storage of the struct aaip_state.
|
||||
*/
|
||||
int aaip_init(struct aaip_state *aaip, char aa_name[2], int flag);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Component Level Interface
|
||||
Provides support for unlimited component size but demands the caller
|
||||
to have a growing storage facility resp. to do own oversize handling.
|
||||
|
||||
This interface expects moderatly sized input pieces and will hand out
|
||||
moderately sized result pieces. The number of ransactions is virtually
|
||||
unlimited.
|
||||
*/
|
||||
|
||||
/* Submit small data chunk for decoding.
|
||||
The return value will tell whether data are pending for being fetched.
|
||||
@param aaip The AAIP decoder context
|
||||
@param data Not more than 2048 bytes input for the decoder
|
||||
@param num_data Number of bytes in data
|
||||
0 inquires the buffer status avoiding replies <= 0
|
||||
@param ready_bytes Number of decoded bytes ready for delivery
|
||||
@param flag Bitfield for control purposes
|
||||
@return -1= non-AA field detected
|
||||
*ready_bytes gives number of consumed bytes in data
|
||||
0= cannot accept data because buffer full
|
||||
1= no component record complete, submit more data
|
||||
2= component record complete, may be delivered
|
||||
3= component complete, may be delivered
|
||||
4= no component available, no more data expected, done
|
||||
*/
|
||||
int aaip_submit_data(struct aaip_state *aaip,
|
||||
unsigned char *data, size_t num_data,
|
||||
size_t *ready_bytes, int flag);
|
||||
|
||||
|
||||
/* Fetch the available part of current component.
|
||||
The return value will tell whether it belongs to name or to value and
|
||||
whether that name or value is completed now.
|
||||
@param aaip The AAIP decoder context
|
||||
@param result Has to point to storage for the component data
|
||||
@param result_size Gives the amount of provided result storage
|
||||
@param num_result Will tell the number of fetched result bytes
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= discard data rather than copying to result
|
||||
@return -2 = insufficient result_size
|
||||
-1 = no data ready for delivery
|
||||
0 = result holds the final part of a name
|
||||
1 = result holds an intermediate part of a name
|
||||
2 = result holds the final part of a value
|
||||
3 = result holds an intermediate part of a value
|
||||
*/
|
||||
int aaip_fetch_data(struct aaip_state *aaip,
|
||||
char *result, size_t result_size, size_t *num_result,
|
||||
int flag);
|
||||
|
||||
|
||||
/* Skip the current component and eventually the following value component.
|
||||
This has to be called if fetching of a component shall be aborted
|
||||
but the next component resp. pair shall be fetchable again.
|
||||
aaip_submit_data() will not indicate readiness for fetching until all
|
||||
bytes of the skipped components are submitted. Those bytes get discarded.
|
||||
@param aaip The AAIP decoder context
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= do not skip value if current component is name
|
||||
@return <=0 error , 1= now in skip state, 2= not in skip state
|
||||
*/
|
||||
int aaip_skip_component(struct aaip_state *aaip, int flag);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Pair Level Interface
|
||||
Provides support for names and values of limited size. The limits are
|
||||
given by the caller who has to provide the storage for name and value.
|
||||
|
||||
This interface expects moderatly sized input pieces.
|
||||
The number of input transcations is virtually unlimited.
|
||||
The number of pair transactions after aaip_init() should be limited
|
||||
to 4 billion.
|
||||
*/
|
||||
|
||||
|
||||
/* Accept raw input data and collect a pair of name and value.
|
||||
The return value iwill indicate whether the pair is complete, whether more
|
||||
pairs are complete or whether more data are desired. No input data will be
|
||||
accepted as long as complete pairs are pending. The end of the attribute
|
||||
list will be indicated.
|
||||
@param aaip The AAIP decoder context
|
||||
@param data The raw data to decode
|
||||
@param num_data Number of data bytes provided
|
||||
@param consumed Returns the number of consumed data bytes
|
||||
@param name Buffer to build the name string
|
||||
@param name_size Maximum number of bytes in name
|
||||
@param name_fill Holds the current buffer fill of name
|
||||
@param value Buffer to build the value string
|
||||
@param value_size Maximum number of bytes in value
|
||||
@param value_fill Holds the current buffer fill of value
|
||||
@param flag Bitfield for control purposes - submit 0 for now
|
||||
@return <0 error
|
||||
0 data not accepted, first fetch pending pairs with num_data == 0
|
||||
1 name and value are not valid yet, submit more data
|
||||
2 name and value are valid, submit more data
|
||||
3 name and value are valid, pairs pending, fetch with num_data == 0
|
||||
4 name and value are valid, no more data expected
|
||||
5 name and value are not valid, no more data expected
|
||||
*/
|
||||
int aaip_decode_pair(struct aaip_state *aaip,
|
||||
unsigned char *data, size_t num_data, size_t *consumed,
|
||||
char *name, size_t name_size, size_t *name_fill,
|
||||
char *value, size_t value_size, size_t *value_fill,
|
||||
int flag);
|
||||
|
||||
|
||||
/* Inquire the number of pairs which were skipped because being oversized.
|
||||
@param aaip The AAIP decoder context
|
||||
@param flag Bitfield for control purposes - submit 0 for now
|
||||
@return The number of pairs skipped since aaip_init()
|
||||
*/
|
||||
unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag);
|
||||
|
||||
#endif /* ! Aaip_h_is_includeD */
|
||||
|
179
test/aaip_0_2_test.c
Normal file
179
test/aaip_0_2_test.c
Normal file
@ -0,0 +1,179 @@
|
||||
|
||||
/*
|
||||
|
||||
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
|
||||
|
||||
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 "aaip_0_2.h"
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
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[1025];
|
||||
size_t name_size= 4;
|
||||
size_t name_fill;
|
||||
char value[1025];
|
||||
size_t value_size= 1024;
|
||||
size_t value_fill;
|
||||
size_t todo;
|
||||
int submit_data, is_done;
|
||||
unsigned char *rpt;
|
||||
unsigned int skipped, was_skipped= 0;
|
||||
|
||||
if(argc < 3 || (argc % 2) == 0) {
|
||||
fprintf(stderr, "usage: %s [-]name[xNNN] [-]value[xNNN] ...\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
aaip= (struct aaip_state *) calloc(aaip_sizeof_aaip_state(), 1);
|
||||
names= calloc(sizeof(char *), (argc - 1) / 2);
|
||||
values= calloc(sizeof(char *), (argc - 1) / 2);
|
||||
value_lengths= calloc(sizeof(size_t), (argc - 1) / 2);
|
||||
|
||||
for(i= 0; i < argc - 1; i++) {
|
||||
if(i % 2)
|
||||
param= values + i / 2;
|
||||
else
|
||||
param= names + i / 2;
|
||||
(*param)= argv[i + 1];
|
||||
if(argv[i + 1][0] == '-') {
|
||||
cpt= strchr(argv[i + 1], 'x');
|
||||
if(cpt != NULL) {
|
||||
l= cpt - argv[i + 1] - 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] + 1, l);
|
||||
(*param)[mult * l]= 0;
|
||||
} else
|
||||
(*param)= argv[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(i % 2)
|
||||
value_lengths[i / 2]= strlen(values[i / 2]);
|
||||
}
|
||||
|
||||
ret= aaip_encode("AA", (unsigned int) ((argc - 1) / 2), names,
|
||||
value_lengths, values,
|
||||
&result_len, &result, 0);
|
||||
if(ret <= 0) {
|
||||
fprintf(stderr, "%s : aaip_encode failed with ret= %d\n", argv[0], ret);
|
||||
exit(2);
|
||||
}
|
||||
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");
|
||||
|
||||
|
||||
aaip_init(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",
|
||||
argv[0]);
|
||||
exit(5);
|
||||
}
|
||||
} else
|
||||
todo= 0;
|
||||
ret= aaip_decode_pair(aaip, rpt, todo, &consumed,
|
||||
name, name_size, &name_fill,
|
||||
value, 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",
|
||||
argv[0], ret);
|
||||
exit(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",
|
||||
argv[0], ret);
|
||||
exit(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");
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user