/*
 *	PROGRAM:	InterBase layered support library
 *	MODULE:		blob.epp
 *	DESCRIPTION:	Dynamic blob support
 *
 * The contents of this file are subject to the Interbase Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy
 * of the License at http://www.Inprise.com/IPL.html
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code was created by Inprise Corporation
 * and its predecessors. Portions created by Inprise Corporation are
 * Copyright (C) Inprise Corporation.
 *
 * All Rights Reserved.
 * Contributor(s): ______________________________________.
 *
 * 2001.09.10 Claudio Valderrama: get_name() was preventing the API calls
 *   isc_blob_default_desc, isc_blob_lookup_desc & isc_blob_set_desc
 *   from working properly with dialect 3 names. Therefore, incorrect names
 *   could be returned or a lookup for a blob field could fail. In addition,
 *   a possible buffer overrun due to unchecked bounds was closed. The fc
 *   get_name() as been renamed copy_exact_name().
 *
 */

#include "firebird.h"
#include "../jrd/ibase.h"
#include "../jrd/intl.h"
#include "../yvalve/blob_proto.h"
#include "../common/StatusArg.h"
#include "../common/utils_proto.h"

using namespace Firebird;

DATABASE DB = STATIC "yachts.lnk";

static void copy_exact_name (const UCHAR*, UCHAR*, SSHORT);
static ISC_STATUS error(ISC_STATUS* status, const Arg::StatusVector& v);


void API_ROUTINE isc_blob_default_desc(ISC_BLOB_DESC* desc,
									   const UCHAR* relation_name,
									   const UCHAR* field_name)
{
/**************************************
 *
 *	i s c _ b l o b _ d e f a u l t _ d e s c
 *
 **************************************
 *
 * Functional description
 *
 *	This function will set the default
 *	values in the blob_descriptor.
 *
 **************************************/

	desc->blob_desc_subtype = isc_blob_text;
	desc->blob_desc_charset = CS_dynamic;
	desc->blob_desc_segment_size = 80;

    copy_exact_name(field_name, desc->blob_desc_field_name, sizeof(desc->blob_desc_field_name));
    copy_exact_name(relation_name, desc->blob_desc_relation_name, sizeof(desc->blob_desc_relation_name));
}


ISC_STATUS API_ROUTINE isc_blob_gen_bpb(ISC_STATUS* status,
										const ISC_BLOB_DESC* to_desc,
										const ISC_BLOB_DESC* from_desc,
										USHORT bpb_buffer_length,
										UCHAR* bpb_buffer,
										USHORT* bpb_length)
{
/**************************************
 *
 *	i s c _ b l o b _ g e n _ b p b
 *
 **************************************
 *
 * Functional description
 *
 *  	This function will generate a bpb
 *	given a to_desc and a from_desc
 *	which contain the subtype and
 *	character set information.
 *
 **************************************/
	if (bpb_buffer_length < 17)
		return error(status, Arg::Gds(isc_random) << Arg::Str("BPB buffer too small"));

	UCHAR* p = bpb_buffer;
	*p++ = isc_bpb_version1;
	*p++ = isc_bpb_target_type;
	*p++ = 2;
	*p++ = (UCHAR)to_desc->blob_desc_subtype;
	*p++ = (UCHAR)(to_desc->blob_desc_subtype >> 8);
	*p++ = isc_bpb_source_type;
	*p++ = 2;
	*p++ = (UCHAR)from_desc->blob_desc_subtype;
	*p++ = (UCHAR)(from_desc->blob_desc_subtype >> 8);
	*p++ = isc_bpb_target_interp;
	*p++ = 2;
	*p++ = (UCHAR)to_desc->blob_desc_charset;
	*p++ = (UCHAR)(to_desc->blob_desc_charset >> 8);
	*p++ = isc_bpb_source_interp;
	*p++ = 2;
	*p++ = (UCHAR)from_desc->blob_desc_charset;
	*p++ = (UCHAR)(from_desc->blob_desc_charset >> 8);

	*bpb_length = p - bpb_buffer;

	return error(status, Arg::Gds(FB_SUCCESS));
}


ISC_STATUS API_ROUTINE isc_blob_lookup_desc(ISC_STATUS* status,
											FB_API_HANDLE* db_handle,
											FB_API_HANDLE* trans_handle,
											const UCHAR* relation_name,
											const UCHAR* field_name,
											ISC_BLOB_DESC* desc, UCHAR* global)
{
/***********************************************
 *
 *	i s c _ b l o b _ l o o k u p _ d e s c
 *
 ***********************************************
 *
 * Functional description
 *
 *	This routine will lookup the subtype,
 *	character set and segment size information
 *	from the metadata, given a relation/procedure name
 *	and column/parameter name.  it will fill in the information
 *	in the BLOB_DESC.
 *
 ***********************************************/
	ISC_STATUS_ARRAY isc_status = {0};
	isc_db_handle DB = *db_handle;
	isc_req_handle handle = 0;

    copy_exact_name(field_name, desc->blob_desc_field_name, sizeof(desc->blob_desc_field_name));
    copy_exact_name(relation_name, desc->blob_desc_relation_name, sizeof(desc->blob_desc_relation_name));

	bool flag = false;

	FOR (REQUEST_HANDLE handle TRANSACTION_HANDLE *trans_handle)
		X IN RDB$RELATION_FIELDS CROSS Y IN RDB$FIELDS
		WITH X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_NAME AND
		X.RDB$RELATION_NAME EQ desc->blob_desc_relation_name AND
		X.RDB$FIELD_NAME EQ desc->blob_desc_field_name
		flag = true;

	    desc->blob_desc_subtype = Y.RDB$FIELD_SUB_TYPE;
        desc->blob_desc_charset = Y.RDB$CHARACTER_SET_ID;
        desc->blob_desc_segment_size = Y.RDB$SEGMENT_LENGTH;

        if (global) {
            copy_exact_name((UCHAR*) Y.RDB$FIELD_NAME, global, sizeof(Y.RDB$FIELD_NAME));
        }
	END_FOR
    ON_ERROR
		ISC_STATUS_ARRAY temp_status;
		isc_release_request(temp_status, &handle);
		fb_utils::copyStatus(status, ISC_STATUS_LENGTH, isc_status, ISC_STATUS_LENGTH);
		return status[1];
	END_ERROR;

	isc_release_request(isc_status, &handle);

	if (!flag)
	{
		handle = 0;

		FOR (REQUEST_HANDLE handle TRANSACTION_HANDLE *trans_handle)
			X IN RDB$PROCEDURE_PARAMETERS
			CROSS Y IN RDB$FIELDS
			WITH X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_NAME AND
				 X.RDB$PROCEDURE_NAME EQ desc->blob_desc_relation_name AND
				 X.RDB$PACKAGE_NAME MISSING AND
				 X.RDB$PARAMETER_NAME EQ desc->blob_desc_field_name
			flag = true;

			desc->blob_desc_subtype = Y.RDB$FIELD_SUB_TYPE;
			desc->blob_desc_charset = Y.RDB$CHARACTER_SET_ID;
			desc->blob_desc_segment_size = Y.RDB$SEGMENT_LENGTH;

			if (global)
				copy_exact_name((UCHAR*) Y.RDB$FIELD_NAME, global, sizeof(Y.RDB$FIELD_NAME));
		END_FOR
		ON_ERROR
			ISC_STATUS_ARRAY temp_status;
			isc_release_request(temp_status, &handle);
			fb_utils::copyStatus(status, ISC_STATUS_LENGTH, isc_status, ISC_STATUS_LENGTH);
			return status[1];
		END_ERROR;

		isc_release_request(isc_status, &handle);
	}

	if (!flag)
		return error(status, Arg::Gds(isc_fldnotdef) << Arg::Str((const char*)(desc->blob_desc_field_name)) <<
														Arg::Str((const char*)(desc->blob_desc_relation_name)));

	return error(status, Arg::Gds(FB_SUCCESS));
}


ISC_STATUS API_ROUTINE isc_blob_set_desc(ISC_STATUS* status,
										 const UCHAR* relation_name,
										 const UCHAR* field_name,
										 SSHORT subtype,
										 SSHORT charset,
										 SSHORT segment_size,
										 ISC_BLOB_DESC* desc)
{
/**************************************
 *
 *	i s c _ b l o b _ s e t _ d e s c
 *
 **************************************
 *
 * Functional description
 *
 *	This routine will set the subtype
 *	and character set information in the
 *	BLOB_DESC based on the information
 *	specifically passed in by the user.
 *
 **************************************/

    copy_exact_name(field_name, desc->blob_desc_field_name, sizeof(desc->blob_desc_field_name));
    copy_exact_name(relation_name, desc->blob_desc_relation_name, sizeof(desc->blob_desc_relation_name));

	desc->blob_desc_subtype = subtype;
	desc->blob_desc_charset = charset;
	desc->blob_desc_segment_size = segment_size;

	return error(status, Arg::Gds(FB_SUCCESS));
}




static void copy_exact_name (const UCHAR* from, UCHAR* to, SSHORT bsize)
{
/**************************************
 *
 *  c o p y _ e x a c t _ n a m e
 *
 **************************************
 *
 * Functional description
 *  Copy null terminated name ot stops at bsize - 1.
 *
 **************************************/
	const UCHAR* const from_end = from + bsize - 1;
	UCHAR* to2 = to - 1;
	while (*from && from < from_end)
	{
		if (*from != ' ') {
			to2 = to;
		}
		*to++ = *from++;
	}
	*++to2 = 0;
}


static ISC_STATUS error(ISC_STATUS* status, const Arg::StatusVector& v)
{
/**************************************
 *
 *	e r r o r
 *
 **************************************
 *
 * Functional description
 *	Stuff a status vector.
 *
 **************************************/
	return v.copyTo(status);
}
