isdn4k-utils/eurofile/src/eft/fileheader.c

556 lines
14 KiB
C

/* $Id: fileheader.c,v 1.4 2001/03/01 14:59:12 paul Exp $ */
/*
Copyright 1998 by Henner Eisen
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This code 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
A (rather incomplete) implementation of the T-protocol
defined in ETS 300 075.
This file containing stuff used for processing of file headers.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <pwd.h>
#include <string.h>
#include <tdu_user.h>
#include "tdu.h"
#include "fileheader.h"
/* Table with various information on tdu file headers */
static struct tdu_descr tdu_fh_descr[] = {
{"FILE_TYPE", TDU_FH_FILE_TYPE, 0},
{"EXECUTION_ORDER", TDU_FH_EXECUTION_ORDER, 0},
{"TRANSFER_NAME", TDU_FH_TRANSFER_NAME, 0},
{"FILE_NAME", TDU_FH_FILE_NAME, 0},
{"DATE", TDU_FH_DATE, 0},
{"FILE_LENGTH", TDU_FH_FILE_LENGTH, 0},
{"DESTINATION_CODE", TDU_FH_DESTINATION_CODE, 0},
{"FILE_CODING", TDU_FH_FILE_CODING, 0},
{"DESTINATION_NAME", TDU_FH_DESTINATION_NAME, 0},
{"COST", TDU_FH_COST, 0},
{"USER_FIELD", TDU_FH_USER_FIELD, 0},
{"LOAD_ADDRESS", TDU_FH_LOAD_ADDRESS, 0},
{"EXEC_ADDRESS_ABS", TDU_FH_EXEC_ADDRESS_ABS, 0},
{"EXEC_ADDRESS_REL", TDU_FH_EXEC_ADDRESS_REL, 0},
{"COMPRESSION_MODE", TDU_FH_COMPRESSION_MODE, 0},
{"DEVICE", TDU_FH_DEVICE, 0},
{"FILE_CHECKSUM", TDU_FH_FILE_CHECKSUM, 0},
{"AUTHOR_NAME", TDU_FH_AUTHOR_NAME, 0},
{"FUTURE_FILE_LENGTH", TDU_FH_FUTURE_FILE_LENGTH, 0},
{"PERMITTED_ACTIONS", TDU_FH_PERMITTED_ACTIONS, 0},
{"LEGAL_QUALIFICATION", TDU_FH_LEGAL_QUALIFICATION, 0},
{"CREATION", TDU_FH_CREATION, 0},
{"LAST_READ_ACCESS", TDU_FH_LAST_READ_ACCESS, 0},
{"ID_OF_LAST_MODIFIER", TDU_FH_ID_OF_LAST_MODIFIER, 0},
{"ID_OF_LAST_READER", TDU_FH_ID_OF_LAST_READER, 0},
{"RECIPIENT", TDU_FH_RECIPIENT, 0},
{"TELEMATIC_FT_VERSION",TDU_FH_TELEMATIC_FT_VERSION, 0},
{0,0,0}
};
static char * tdu_print_file_par( int ct, unsigned char *pkt, unsigned char *end,
int level )
{
unsigned char pi, *ret;
char *name;
int len=0;
if( pkt >= end ) goto packet_too_small;
pi = *(pkt++);
name = tdu_des(tdu_fh_descr,pi);
tdu_printf( ct, " %s(", name ? name : "FIle header parameter unknown " );
if( pkt >= end ) goto packet_too_small;
if( (len = *(pkt++)) == 0xff ) {
if( pkt+2 > end ) goto packet_too_small;
len = 0x100 * *(pkt++) + *(pkt++);
}
tdu_printf (ct,"len=%d)", len);
ret = pkt + len;
if( level < 3 ) {
return ret;
}
tdu_printf(ct,"=");
switch ( pi ) {
case TDU_FH_TRANSFER_NAME:
case TDU_FH_FILE_NAME:
case TDU_FH_DATE:
case TDU_FH_DESTINATION_NAME:
case TDU_FH_COST:
case TDU_FH_USER_FIELD:
case TDU_FH_AUTHOR_NAME:
case TDU_FH_LEGAL_QUALIFICATION:
case TDU_FH_CREATION:
case TDU_FH_LAST_READ_ACCESS:
case TDU_FH_ID_OF_LAST_MODIFIER:
case TDU_FH_ID_OF_LAST_READER:
case TDU_FH_RECIPIENT:
case TDU_FH_TELEMATIC_FT_VERSION:
tdu_print_txt(ct,pkt,ret);
break;
case TDU_FH_FILE_LENGTH:
case TDU_FH_FUTURE_FILE_LENGTH:
case TDU_FH_FILE_CHECKSUM:
tdu_print_li( ct, pkt, ret );
break;
default:
tdu_print_hex ( ct, pkt,ret );
};
return ret;
packet_too_small : tdu_printf( TDU_LOG_ERR, "FH packet to small!\n" );
return end;
}
unsigned char * tdu_print_file_header( int ct, unsigned char *pkt, unsigned char *end, int level )
{
unsigned char ci;
int len=0, garbage=0;
if( pkt >= end ) goto packet_too_small;
if( level < 1 ) {
tdu_printf(ct,"\n");
return pkt;
}
ci = *(pkt++);
tdu_printf( ct, " %s(", ci == 0x30 ? "File_Header" : "FH magic unknown" );
if( pkt < end ) len = *(pkt++);
if( len == 0xff ) {
if( pkt+2 >= end ) goto packet_too_small;
len = 0x100 * *(pkt) + *(pkt+1);
pkt += 2;
}
if( pkt+len < end ){
garbage = end - pkt - len;
/* remove trailing garbage */
end = pkt + len;
}
tdu_printf (ct, "len=%d", len);
if ( garbage ) tdu_printf(ct, "[+%d]", garbage);
tdu_printf( ct, ")" );
if( level < 2 ) {
tdu_printf(ct,"\n");
return pkt;
}
tdu_printf( ct, ": " );
while( pkt < end ) pkt = tdu_print_file_par( ct, pkt, end, level );
tdu_printf(ct,"\n");
return pkt;
packet_too_small : tdu_printf( TDU_LOG_ERR,"FH packet to small!\n" );
return end;
}
/*
* Header parser that just logs the header contents
*
* FIXME: integrate the logging functinality into the standard parser
* and remove this when the latter is done and working.
*/
int tdu_fh_parse_print(struct tdu_stream *ts, struct tdu_buf * tb, int maxlen)
{
unsigned char *fh_tail, *max_tail = tb->data + maxlen, *start=tb->data;
unsigned char ci = *(tb->data++);
int len;
tdu_printf( TDU_LOG_FH, " %s", ci == TDU_PI_FILE_HEADER ?
"File_Header" : "FH magic unknown" );
if( (len = tdu_parse_le(tb)) < 0 ) goto oflow;
tdu_printf( TDU_LOG_FH, "[%d](", len );
/* FIXME: parse headers extending more than one buffer */
fh_tail = tb->data + len;
if( fh_tail > max_tail ) goto oflow;
while( tb->data < fh_tail )
tb->data = tdu_print_file_par(TDU_LOG_FH, tb->data, fh_tail,4);
tdu_printf(TDU_LOG_FH,")\n");
ts->hdr.len = fh_tail - start;
ts->hdr.parse = NULL;
return (fh_tail - start);
oflow:
ts->hdr.len = 0;
ts->hdr.parse = NULL;
tdu_printf( TDU_LOG_ERR,
"Buffer overflow while parsing file header\n");
return -TDU_RE_SYNTAX_ERROR;
}
/*
* Parse an ETS 300-075 file header encoded time string and return
* the equivalent unix epoche time_t type;
*
*/
static time_t fh2timet(unsigned char *ts, int len){
int tmp;
struct tm t={0,0,0,0,0,0,0,0,0,0};
/*
* FIXME: (or better fix ETS protocol :-) this is not y2k safe due to
* limititations in ETSI protocol.
*
* The window boundary is chosen arbitrarily because I
* currently don't know of any official ETSI recommendation.
*
* This should be changed as soon as an official value
* agreed among all EUROFILE implementors is recommended.
*/
int y2k_wrap=70;
if(len>1){
tmp = 10*(ts[0]-'0') + (ts[1]-'0');
ts +=2;
len-=2;
/* y2k compatibilty hack */
if( tmp < y2k_wrap ) tmp += 100;
t.tm_year = tmp;
}
if(len>1){
tmp = 10*(ts[0]-'0') + (ts[1]-'0');
ts +=2;
len-=2;
t.tm_mon = tmp-1; /* tm_mon is in the range 0 to 11 */
}
if(len>1){
tmp = 10*(ts[0]-'0') + (ts[1]-'0');
ts +=2;
len-=2;
t.tm_mday = tmp;
}
if(len>1){
tmp = 10*(ts[0]-'0') + (ts[1]-'0');
ts +=2;
len-=2;
t.tm_hour = tmp;
}
if(len>1){
tmp = 10*(ts[0]-'0') + (ts[1]-'0');
ts +=2;
len-=2;
t.tm_min = tmp;
}
if(len>1){
tmp = 10*(ts[0]-'0') + (ts[1]-'0');
ts +=2;
len-=2;
t.tm_sec = tmp;
}
return mktime(&t);
}
/*
* Header parser that stores parameters in a struct fileheader_par where
* appropriate
*/
int tdu_parse_fh(struct fileheader_par * par, struct tdu_buf * tb)
{
int ci, pi, len;
char t[TDU_FH_PLEN_DATE+1];
unsigned char * start = tb->data, *fh_tail;
tdu_printf(TDU_LOG_TRC, "tdu_parse_fh()\n");
ci = *(tb->data++);
tdu_printf( TDU_LOG_DBG, " %s", ci == TDU_PI_FILE_HEADER ?
"File_Header" : "FH magic unknown" );
len = tdu_parse_le(tb);
/* FIXME: parse headers extending more than one buffer */
fh_tail = tb->data + len;
if(fh_tail > tb->tail) {
tdu_printf( TDU_LOG_ERR, "tdu_parse_fh: tb overflow\n");
return -TDU_RE_SYNTAX_ERROR;
}
tdu_printf( TDU_LOG_DBG|TDU_LOG_FH, "[%d](", len );
tb->pn = tb->data;
while( tb->data < fh_tail ) {
unsigned char * this = tb->data;
pi = tdu_get_next_pi(tb);
tdu_printf(TDU_LOG_DBG, "parsing parameter %d\n",pi);
if( pi < 0 ) return -pi;
switch( pi ){
case TDU_FH_FILE_LENGTH:
tdu_printf(TDU_LOG_DBG, "length\n");
len = tdu_parse_li(tb);
par->fh_s.st_size = len;
break;
case TDU_FH_TRANSFER_NAME:
tdu_printf(TDU_LOG_DBG, "transfer name\n");
len = tdu_parse_string(tb, par->fh_t_name,
TDU_PLEN_DESIGNATION);
par->fh_t_name[len]=0;
break;
case TDU_FH_FILE_NAME:
tdu_printf(TDU_LOG_DBG, "file name\n");
len = tdu_parse_string(tb, par->fh_name,
TDU_FH_PLEN_NAME);
par->fh_name[len]=0;
break;
case TDU_FH_DATE:
tdu_printf(TDU_LOG_DBG, "date \n");
len = tdu_parse_string(tb, t, TDU_FH_PLEN_DATE);
t[len]=0;
par->fh_s.st_mtime = fh2timet(t,len);
break;
case TDU_FH_FILE_TYPE:
tdu_printf(TDU_LOG_DBG, "type \n");
par->fh_type = tdu_parse_byte(tb);
break;
default:
/* FIXME: parse more parameters */
tdu_printf(TDU_LOG_DBG, "tdu_parse_fh(): skipping unexpected"
" parameter '%s' ignored\n",
tdu_param_descr(pi) );
tb->data = tb->pn;
};
tdu_print_file_par(TDU_LOG_FH, this, fh_tail,4);
}
tdu_printf( TDU_LOG_DBG|TDU_LOG_FH, ")");
return (tb->data - start);
}
/*
* Header usable by a tdu_stream object as parse method to parse
* a fileheader into file_header_par object. The tdu_streams hdr.data
* must point to an instance of a struct file_header_par.
*/
int tdu_fh_parse(struct tdu_stream *ts, struct tdu_buf * tb, int maxlen)
{
/* FIXME: parse headers extending several tb's */
unsigned char *fh_tail, *start=tb->data;
struct fileheader_par * par = ts->hdr.data;
int len;
len = tdu_parse_fh(par, tb);
fh_tail = start + len;
if( len < 0 ) {
/* probably buffer overflow or other parse error */
ts->hdr.len = 0;
ts->hdr.parse = NULL;
tdu_printf( TDU_LOG_ERR,
"tdu_fh_parse: error when parsing file header\n");
return len;
}
ts->hdr.len = fh_tail - start;
ts->hdr.parse = NULL;
return fh_tail - start;
}
/*
* used by tdu_stream object as parse method to prepend an empty file
* header to a file
*/
int tdu_fh_get_zero_hdr(struct tdu_stream *ts, unsigned char * buf, int maxlen)
{
*(buf++) = TDU_PI_FILE_HEADER;
*(buf++) = 0;
ts->hdr.len = 2;
ts->hdr.read = NULL;
return 2;
}
/*
* Make a fileheader from a struct fileheader_par and store it in a tdu_buf.
*/
int tdu_fh_stat(struct tdu_buf * tb, struct fileheader_par * fh)
{
unsigned char t[13], t_fmt[]="%y%m%d%H%M%S", *owner,
permissions[2];
struct tm * tm_time;
struct passwd * pwent;
int t_len, coding, type;
tm_time = gmtime(&fh->fh_s.st_atime);
t_len = strftime( t, 13, t_fmt, tm_time);
tdu_add_string_par(tb, TDU_FH_LAST_READ_ACCESS, t, t_len);
tm_time = gmtime(&fh->fh_s.st_ctime);
t_len = strftime( t, 13, t_fmt, tm_time);
tdu_add_string_par(tb, TDU_FH_CREATION, t, t_len);
pwent = getpwuid(fh->fh_s.st_uid);
if( pwent == NULL ){
snprintf(t,13,"%d",fh->fh_s.st_uid);
owner = t;
} else {
owner = pwent->pw_name;
}
tdu_add_string_par(tb, TDU_FH_AUTHOR_NAME, owner, strlen(owner));
/*
* permitted actions to be added
*/
permissions[0] = 0x40;
permissions[1] = 0x00;
if( access(fh->fh_name,R_OK) == 0 )
permissions[1] |= TDU_FH_PERMITTED_ACTION_READ;
if( access(fh->fh_name,W_OK) == 0 )
permissions[1] |= (
TDU_FH_PERMITTED_ACTION_EXTEND |
TDU_FH_PERMITTED_ACTION_INSERT |
TDU_FH_PERMITTED_ACTION_REPLACE ) ;
/*
* FIXME: additionally check permissions of the directory in order to set
* the TDU_FH_PERMITTED_ACTION_ERASE flag appropriately.
*/
tdu_add_string_par(tb, TDU_FH_PERMITTED_ACTIONS, permissions, 2);
/*
* several clients get confused when confronted with optional
* parameters before the mandatory ones.
* Unfortunatly, description file records and ordiary headers
* have different mandatory attributes. Thus, we need
* to support different order here.
*/
if(! fh->fh_xdirrec){
if(fh->fh_name[0] != 0 )
tdu_add_string_par(tb, TDU_FH_FILE_NAME, fh->fh_name,
strlen(fh->fh_name));
if(fh->fh_t_name[0] != 0 )
tdu_add_string_par(tb,TDU_FH_TRANSFER_NAME, fh->
fh_t_name,strlen(fh->fh_t_name));
}
/*
* file type/coding; default: data/bin,
* directory: text/printable,
* extended directory: description/not_present
*/
switch(fh->fh_type){
case TDU_FH_CLASS_EFT_DIR:
case TDU_FH_CLASS_EFT_LIST:
case TDU_FH_CLASS_EFT_STRING:
coding = TDU_FH_FILE_CODING_VTX;
type = TDU_FH_FILE_TYPE_TEXT;
break;
case TDU_FH_CLASS_EFT_DATA:
coding = TDU_FH_FILE_CODING_BINARY;
type = TDU_FH_FILE_TYPE_DATA;
break;
case TDU_FH_CLASS_EFT_XDIR:
case TDU_FH_CLASS_EFT_DESCR:
coding = -1 /* shall not be present */;
type = TDU_FH_FILE_TYPE_DESCRIPTION;
break;
default:
coding = TDU_FH_FILE_CODING_BINARY;
type = fh->fh_type;
};
if( coding >= 0 )
tdu_add_byte_par(tb, TDU_FH_FILE_CODING, coding);
tdu_add_byte_par(tb, TDU_FH_FILE_TYPE, type);
tm_time = gmtime(& fh->fh_s.st_mtime);
t_len = strftime( t, 13, t_fmt, tm_time);
tdu_add_string_par(tb, TDU_FH_DATE, t, t_len);
tdu_add_int_par(tb, TDU_FH_FILE_LENGTH, fh->fh_s.st_size);
if(fh->fh_xdirrec){
if(fh->fh_name[0] != 0 )
tdu_add_string_par(tb, TDU_FH_FILE_NAME, fh->fh_name,
strlen(fh->fh_name));
if(fh->fh_t_name[0] != 0 )
tdu_add_string_par(tb,TDU_FH_TRANSFER_NAME, fh->
fh_t_name,strlen(fh->fh_t_name));
}
tdu_add_ci_header(tb,TDU_PI_FILE_HEADER);
return 0;
}
int tdu_fh_get_fd_hdr(struct tdu_stream *ts, unsigned char * buf, int maxlen)
{
struct tdu_buf tb[1];
tdu_init_tb(tb);
tdu_fh_stat(tb,ts->hdr.data);
ts->hdr.len = tb->tail - tb->data;
/* FIXME:
* handle lenght appropriatly even when not
* fitting in on hdr.read() request
*/
if( ts->hdr.len <= maxlen ){
memcpy(buf,tb->data,ts->hdr.len);
} else {
tdu_printf(TDU_LOG_ERR, "tdu_fh_get_fd_hdr(): "
"header too long\n");
return tdu_fh_get_zero_hdr(ts, buf, maxlen);
}
ts->hdr.read = NULL;
return ts->hdr.len;
}
/*
* writes a file header to a stream (useful for creating description files/
* extended directory listings)
*/
void eft_fh_fwrite( FILE * str, unsigned char * tname, unsigned char * fname,
struct stat * s )
{
struct fileheader_par hp;
struct tdu_buf tb[1];
strncpy(hp.fh_t_name,tname, TDU_PLEN_DESIGNATION);
hp.fh_t_name[TDU_PLEN_DESIGNATION]=0;
strncpy(hp.fh_name, fname, TDU_FH_PLEN_NAME);
hp.fh_name[TDU_FH_PLEN_NAME]=0;
hp.fh_type = TDU_FH_CLASS_EFT_DATA;
hp.fh_s = *s;
hp.fh_xdirrec = 1;
tdu_init_tb(tb);
tdu_fh_stat(tb,&hp);
fwrite(tb->data, 1, tb->tail-tb->data, str);
}