/**
 * @function ferite_raise_script_error
 * @declaration void ferite_raise_script_error( FeriteScript *script, int err, char *fmt, ... )
 * @brief Raise an exception within the ferite engine.
 * @param FeriteScript *script The running script
 * @param int err	The error code
 * @param char *fmt	The format of the error string
 * @description Use the same formating codes as printf with this function
 */
void ferite_raise_script_error( FeriteScript *script, int err, char *fmt, ... )
{
	FeriteNamespaceBucket *nsb = NULL;
	FeriteVariable *global_error_object = NULL, *new_error_object = NULL, *backtrace = NULL;
	FeriteVariable *error_object_str = NULL, *error_object_num = NULL, *error_object_backtrace = NULL;
	FeriteBuffer *error_buffer = NULL;
	char *msg;
	va_list ap;

	FE_ENTER_FUNCTION;
	va_start( ap, fmt );

	error_buffer = ferite_buffer_new(script, 0);
	ferite_buffer_vprintf( script, error_buffer, fmt, &ap );
	msg = ferite_buffer_get( script, script->error, NULL );
	
	FUD(("ERROR RAISED: %s %d\n", msg, err ));

	nsb = ferite_namespace_element_exists( script, script->mainns, "err" );
	FE_ASSERT( nsb && nsb->type == FENS_VAR );
	global_error_object = nsb->data;
	script->error_state = FE_ERROR_THROWN;

	if( VAO(global_error_object) == NULL )
	{
		nsb = ferite_namespace_element_exists( script, script->mainns, "Error" );
		if( nsb == NULL )
		{
			FE_LEAVE_FUNCTION( NOWT );
			exit(1);
		}
		new_error_object = ferite_new_object( script, nsb->data, NULL );
		VAO(global_error_object) = VAO(new_error_object);
		FINCREF(VAO(global_error_object));
		ferite_variable_destroy( script, new_error_object );
	}

	error_object_str = ferite_object_get_var( script, VAO(global_error_object), "str" );
	ferite_str_set( script, VAS(error_object_str), msg, strlen(msg), FE_CHARSET_DEFAULT );
	ffree( msg );

	error_object_num = ferite_object_get_var( script, VAO(global_error_object), "num" );
	VAI(error_object_num) = err;
	
	backtrace = ferite_generate_backtrace( script, FE_FALSE );
	error_object_backtrace = ferite_object_get_var( script, VAO(global_error_object), "backtrace");
	ferite_variable_fast_assign( script, error_object_backtrace, backtrace );
	
	ferite_buffer_delete( script, error_buffer );
	
	FE_LEAVE_FUNCTION( NOWT );
}
/**
 * @function ferite_set_error
 * @declaration void ferite_set_error( FeriteScript *script, int num, char *fmt, ... )
 * @brief Same as ferite_error except this wont raise an exception at runtime
 */
void ferite_set_error( FeriteScript *script, int num, char *fmt, ... )
{
	FeriteNamespaceBucket *nsb = NULL;
	FeriteVariable *global_error_object = NULL, *new_error_object = NULL;
	FeriteVariable *errstr = NULL, *erno = NULL;
	va_list ap;
	char *buf = NULL;

	FE_ENTER_FUNCTION;

	if( !script->is_being_deleted && (script->parent == NULL || !script->parent->is_being_deleted) )
	{
		buf = fmalloc( 4096 );
		va_start( ap, fmt );
		vsprintf( buf, fmt, ap );

		nsb = ferite_namespace_element_exists( script, script->mainns, "err" );
		FE_ASSERT( nsb && nsb->type == FENS_VAR );
		global_error_object = nsb->data;

		if( VAO(global_error_object) == NULL )
		{
			nsb = ferite_namespace_element_exists( script, script->mainns, "Error" );
			new_error_object = ferite_new_object( script, nsb->data, NULL );
			VAO(global_error_object) = VAO(new_error_object);
			FINCREF(VAO(global_error_object));
			ferite_variable_destroy( script, new_error_object );
		}

		errstr = ferite_object_get_var( script, VAO(global_error_object), "str" );
		ferite_str_set( script, VAS(errstr), buf, strlen(buf), FE_CHARSET_DEFAULT );

		erno = ferite_object_get_var( script, VAO(global_error_object), "num" );
		VAI(erno) = num;

		ffree( buf );
		va_end( ap );
	}
	FE_LEAVE_FUNCTION( NOWT );
}
FeriteVariable *create_ferite_content_object( FeriteScript *script, MAILSTREAM *stream, BODY *body, int msgno, char *sec )
{
	if( body != NULL ) {
		FeriteVariable *object = NULL,*v = NULL;
		FeriteNamespaceBucket *nsb = NULL;
		char *object_name = NULL;

		object_name = ( body->type == TYPEMULTIPART ) ? "Mail.MessageMultiPart" : "Mail.MessagePart" ;

		nsb = ferite_find_namespace( script, script->mainns, object_name , FENS_CLS );
		if( nsb == NULL )
			return NULL;
		object = ferite_build_object( script, (FeriteClass *)nsb->data );
		if( object == NULL )
			return NULL;

		v  = fe_new_lng("type",body->type);
		ferite_object_set_var(script, VAO(object), "type", v);
		output_printf(OUTPUT_DEBUG,"module.mail: setting type %d", body->type);
		if( body->subtype ) {
			v = fe_new_str( "subtype", body->subtype, 0, FE_CHARSET_DEFAULT );
			ferite_object_set_var( script, VAO(object), "subtype", v );
		}

		if( body->type == TYPEMULTIPART ) {
			PART *part = NULL;
			int i = 0;
			char sec2[200];
			FeriteVariable *ret = NULL, *parts = NULL;

			parts = ferite_hash_get(script,VAO(object)->variables->variables,"parts");
			part = body->nested.part;
			while( part ) {
				i++;
				if( sec ) {
					snprintf(sec2,200,"%s.%d",sec,i);
				} else {
					snprintf(sec2,200,"%d",i);
				}
				ret = create_ferite_content_object( script, stream, &part->body, msgno, sec2 );
				ferite_uarray_add( script, VAUA(parts), ret , NULL, FE_ARRAY_ADD_AT_END );
				part = part->next;
			}
			v = fe_new_lng("nparts", i);
			ferite_object_set_var(script, VAO(object), "nparts", v );
		}
		else
		{
			long len = 0,len2 = 0;
			unsigned char *buf = NULL, *buf2 = NULL;
			SIZEDTEXT src, dest;
			FeriteVariable *v = NULL;
			PARAMETER *param = NULL;
			int i = 0;

			if( sec == NULL )
				sec = "1";
			buf = mail_fetchbody( stream, msgno, sec, &len );

			switch(body->encoding){
				case ENCQUOTEDPRINTABLE:
					if( debug_cmail_module )
						output_printf(OUTPUT_DEBUG,"module.mail: Decoding from encoded quotable");
					buf2 = rfc822_qprint(buf,len,&len2);
					break;
				case ENCBASE64: 
					if( debug_cmail_module )
						output_printf(OUTPUT_DEBUG,"module.mail: Decoding from base64");
					buf2=rfc822_base64(buf,len,&len2);
					break;
				default: 
					buf2=buf;
					len2=len;
			}

			if( debug_cmail_module ) {
				output_printf(OUTPUT_DEBUG,"module.mail: id: %s, description: %s", body->id, body->description);
				output_printf(OUTPUT_DEBUG,"module.mail: block type: %d.%s", body->type, body->subtype);
			}
			if( body->parameter ) /* Try and get the content type correctly */ {
				PARAMETER *ptr = body->parameter;
				while( ptr != NULL ) {
					if( caseless_compare( ptr->attribute, "charset" ) ) {
						if( debug_cmail_module )
							output_printf(OUTPUT_DEBUG,"module.mail: Found content type for block: %s", ptr->value);
						v = fe_new_str("charset", ptr->value, 0, FE_CHARSET_DEFAULT);
						ferite_object_set_var(script, VAO(object), "charset", v);
					}
					ptr = ptr->next;
				}
			}
			v = fe_new_bin_str("content", buf2, len2, FE_CHARSET_DEFAULT );
			ferite_object_set_var(script, VAO(object), "content", v );

			v = fe_new_lng("encoding", body->encoding );
			ferite_object_set_var(script, VAO(object), "encoding", v );

			if( body->id ) {
				v = fe_new_str("ID", body->id, strlen(body->id), FE_CHARSET_DEFAULT);
				ferite_object_set_var(script, VAO(object), "ID", v);
			}
			
			if( body->disposition.type && strcasecmp(body->disposition.type, "attachment") == 0) {
				param = body->disposition.parameter;
				while(param){
					if( param->attribute && ((strcasecmp(param->attribute,"filename") == 0) ||
												(strcasecmp(param->attribute,"name") == 0) ||
												(strcasecmp(param->attribute,"name*") == 0) ||
												(strcasecmp(param->attribute,"filename*") == 0) )) {
						v = fe_new_str("filename", param->value, 0, FE_CHARSET_DEFAULT );
						ferite_object_set_var(script, VAO(object), "filename", v );
						break;
					}
					param=param->next;
				}
			}
			/* If filename was not found in Content-Disposition header search for it among the parameters */
			if( strcmp(VAS(ferite_object_get_var(script, VAO(object), "filename"))->data, "") == 0 ) {
				param = body->parameter;
				while(param){
					if( param->attribute && ((strcasecmp(param->attribute,"filename") == 0) || 
												(strcasecmp(param->attribute,"name") == 0))) {
						v = fe_new_str("filename", param->value, 0, FE_CHARSET_DEFAULT );
						ferite_object_set_var(script, VAO(object), "filename", v );
						break;
					}
					param = param->next;
				}
			}
		}
		return object;
	}
	return NULL;
}