xmlsecurityengine/xmlsec/src/xmlsec_io.c
changeset 0 e35f40988205
child 20 889504eac4fb
equal deleted inserted replaced
-1:000000000000 0:e35f40988205
       
     1 /** 
       
     2  * XML Security Library (http://www.aleksey.com/xmlsec).
       
     3  *
       
     4  * Input uri transform and utility functions.
       
     5  *
       
     6  * This is free software; see Copyright file in the source
       
     7  * distribution for preciese wording.
       
     8  * 
       
     9  * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
       
    10  */
       
    11 #include "xmlsec_globals.h"
       
    12 
       
    13 #include <stdlib.h>
       
    14 #include <string.h> 
       
    15 #include <errno.h>
       
    16 
       
    17 #include <stdio.h> //added for symbian
       
    18 
       
    19 #include <libxml2_tree.h>
       
    20 #include <libxml2_uri.h>
       
    21 #include <libxml2_xmlio.h>
       
    22 #include <libxml2_globals.h>
       
    23 
       
    24 #ifdef LIBXML_HTTP_ENABLED
       
    25 #include <libxml2_nanohttp.h>
       
    26 #endif /* LIBXML_HTTP_ENABLED */
       
    27 
       
    28 #ifdef LIBXML_FTP_ENABLED
       
    29 #include <libxml2_nanoftp.h>
       
    30 #endif /* LIBXML_FTP_ENABLED */
       
    31 
       
    32 #include "xmlsec_xmlsec.h"
       
    33 #include "xmlsec_keys.h"
       
    34 #include "xmlsec_transforms.h"
       
    35 #include "xmlsec_keys.h"
       
    36 #include "xmlsec_io.h"
       
    37 #include "xmlsec_errors.h"
       
    38 
       
    39 /*******************************************************************
       
    40  *
       
    41  * Input I/O callback sets
       
    42  *
       
    43  ******************************************************************/
       
    44 typedef struct _xmlSecIOCallback {
       
    45     xmlInputMatchCallback matchcallback;
       
    46     xmlInputOpenCallback opencallback;
       
    47     xmlInputReadCallback readcallback;
       
    48     xmlInputCloseCallback closecallback;
       
    49 } xmlSecIOCallback, *xmlSecIOCallbackPtr;
       
    50 
       
    51 static xmlSecIOCallbackPtr	xmlSecIOCallbackCreate	(xmlInputMatchCallback matchFunc,
       
    52 							 xmlInputOpenCallback openFunc, 
       
    53 							 xmlInputReadCallback readFunc,
       
    54 							 xmlInputCloseCallback closeFunc);
       
    55 static void			xmlSecIOCallbackDestroy	(xmlSecIOCallbackPtr callbacks);
       
    56 
       
    57 static xmlSecIOCallbackPtr 
       
    58 xmlSecIOCallbackCreate(xmlInputMatchCallback matchFunc, xmlInputOpenCallback openFunc, 
       
    59 		       xmlInputReadCallback readFunc, xmlInputCloseCallback closeFunc) {
       
    60     xmlSecIOCallbackPtr callbacks;
       
    61     
       
    62     xmlSecAssert2(matchFunc != NULL, NULL);
       
    63     
       
    64     /* Allocate a new xmlSecIOCallback and fill the fields. */
       
    65     callbacks = (xmlSecIOCallbackPtr)xmlMalloc(sizeof(xmlSecIOCallback));
       
    66     if(callbacks == NULL) {
       
    67 	xmlSecError(XMLSEC_ERRORS_HERE,
       
    68 		    NULL,
       
    69 		    NULL,
       
    70 		    XMLSEC_ERRORS_R_MALLOC_FAILED,
       
    71 		    "sizeof(xmlSecIOCallback)=%d", 
       
    72 		    sizeof(xmlSecIOCallback));
       
    73 	return(NULL);
       
    74     }
       
    75     memset(callbacks, 0, sizeof(xmlSecIOCallback));    
       
    76 
       
    77     callbacks->matchcallback = matchFunc;
       
    78     callbacks->opencallback  = openFunc;
       
    79     callbacks->readcallback  = readFunc;
       
    80     callbacks->closecallback = closeFunc;
       
    81     
       
    82     return(callbacks);
       
    83 }
       
    84 
       
    85 static void 
       
    86 xmlSecIOCallbackDestroy(xmlSecIOCallbackPtr callbacks) {
       
    87     xmlSecAssert(callbacks != NULL);
       
    88 
       
    89     memset(callbacks, 0, sizeof(xmlSecIOCallback));    
       
    90     xmlFree(callbacks);    
       
    91 }
       
    92 
       
    93 /*******************************************************************
       
    94  *
       
    95  * Input I/O callback list
       
    96  *
       
    97  ******************************************************************/
       
    98 static xmlSecPtrListKlass xmlSecIOCallbackPtrListKlass = {
       
    99     BAD_CAST "io-callbacks-list",
       
   100     NULL, 						/* xmlSecPtrDuplicateItemMethod duplicateItem; */
       
   101     (xmlSecPtrDestroyItemMethod)xmlSecIOCallbackDestroy,/* xmlSecPtrDestroyItemMethod destroyItem; */
       
   102     NULL,  					  	/* xmlSecPtrDebugDumpItemMethod debugDumpItem; */
       
   103     NULL						/* xmlSecPtrDebugDumpItemMethod debugXmlDumpItem; */
       
   104 };
       
   105 
       
   106 #define xmlSecIOCallbackPtrListId	xmlSecIOCallbackPtrListGetKlass	()
       
   107 static xmlSecPtrListId 			xmlSecIOCallbackPtrListGetKlass	(void);
       
   108 static xmlSecIOCallbackPtr		xmlSecIOCallbackPtrListFind	(xmlSecPtrListPtr list,
       
   109 									 const char* uri);
       
   110 
       
   111 /**
       
   112  * xmlSecIOCallbackPtrListGetKlass: 
       
   113  *
       
   114  * The keys list klass.
       
   115  *
       
   116  * Returns keys list id.
       
   117  */
       
   118 static xmlSecPtrListId 
       
   119 xmlSecIOCallbackPtrListGetKlass(void) {
       
   120     return(&xmlSecIOCallbackPtrListKlass);
       
   121 }
       
   122 
       
   123 static xmlSecIOCallbackPtr 
       
   124 xmlSecIOCallbackPtrListFind(xmlSecPtrListPtr list, const char* uri) {
       
   125     xmlSecIOCallbackPtr callbacks;
       
   126     xmlSecSize i, size;
       
   127 
       
   128     xmlSecAssert2(xmlSecPtrListCheckId(list, xmlSecIOCallbackPtrListId), NULL);
       
   129     xmlSecAssert2(uri != NULL, NULL);
       
   130 
       
   131     size = xmlSecPtrListGetSize(list);
       
   132     for(i = 0; i < size; ++i) {
       
   133 	callbacks = (xmlSecIOCallbackPtr)xmlSecPtrListGetItem(list, i);
       
   134 	xmlSecAssert2(callbacks != NULL, NULL);	
       
   135 	xmlSecAssert2(callbacks->matchcallback != NULL, NULL);	
       
   136 	
       
   137 	if((callbacks->matchcallback(uri)) != 0) {
       
   138 	    return(callbacks);
       
   139 	}
       
   140     }
       
   141     return(NULL);
       
   142 }
       
   143 
       
   144 static xmlSecPtrList xmlSecAllIOCallbacks;
       
   145 
       
   146 /**
       
   147  * xmlSecIOInit:
       
   148  *
       
   149  * The IO initialization (called from #xmlSecInit function).
       
   150  * Applications should not call this function directly.
       
   151  *
       
   152  * Returns 0 on success or a negative value otherwise.
       
   153  */
       
   154 EXPORT_C 
       
   155 int
       
   156 xmlSecIOInit(void) {    
       
   157     int ret;
       
   158     
       
   159     ret = xmlSecPtrListInitialize(&xmlSecAllIOCallbacks, xmlSecIOCallbackPtrListId);
       
   160     if(ret < 0) {
       
   161 	xmlSecError(XMLSEC_ERRORS_HERE,
       
   162 		    NULL,
       
   163 		    "xmlSecPtrListPtrInitialize",
       
   164 		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
       
   165 		    XMLSEC_ERRORS_NO_MESSAGE);
       
   166         return(-1);
       
   167     }
       
   168 
       
   169 #ifdef LIBXML_HTTP_ENABLED
       
   170     xmlNanoHTTPInit();
       
   171 #endif /* LIBXML_HTTP_ENABLED */
       
   172 
       
   173 #ifdef LIBXML_FTP_ENABLED       
       
   174     xmlNanoFTPInit();
       
   175 #endif /* LIBXML_FTP_ENABLED */ 
       
   176 
       
   177     return(xmlSecIORegisterDefaultCallbacks());
       
   178 }
       
   179 
       
   180 /**
       
   181  * xmlSecIOShutdown:
       
   182  *
       
   183  * The IO clenaup (called from #xmlSecShutdown function).
       
   184  * Applications should not call this function directly.
       
   185  */
       
   186 EXPORT_C 
       
   187 void
       
   188 xmlSecIOShutdown(void) {
       
   189 
       
   190 #ifdef LIBXML_HTTP_ENABLED
       
   191     xmlNanoHTTPCleanup();
       
   192 #endif /* LIBXML_HTTP_ENABLED */
       
   193 
       
   194 #ifdef LIBXML_FTP_ENABLED       
       
   195     xmlNanoFTPCleanup();
       
   196 #endif /* LIBXML_FTP_ENABLED */ 
       
   197 
       
   198     xmlSecPtrListFinalize(&xmlSecAllIOCallbacks);
       
   199 }
       
   200 
       
   201 /**
       
   202  * xmlSecIOCleanupCallbacks:
       
   203  *
       
   204  * Clears the entire input callback table. this includes the
       
   205  * compiled-in I/O. 
       
   206  */
       
   207 EXPORT_C
       
   208 void
       
   209 xmlSecIOCleanupCallbacks(void) {
       
   210     xmlSecPtrListEmpty(&xmlSecAllIOCallbacks);
       
   211 }
       
   212 
       
   213 /**
       
   214  * xmlSecIORegisterCallbacks:
       
   215  * @matchFunc:  	the protocol match callback.
       
   216  * @openFunc:  		the open stream callback.
       
   217  * @readFunc:  		the read from stream callback.
       
   218  * @closeFunc:  	the close stream callback.
       
   219  *
       
   220  * Register a new set of I/O callback for handling parser input.
       
   221  *
       
   222  * Returns the 0 on success or a negative value if an error occurs.
       
   223  */
       
   224 EXPORT_C
       
   225 int
       
   226 xmlSecIORegisterCallbacks(xmlInputMatchCallback matchFunc,
       
   227 	xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
       
   228 	xmlInputCloseCallback closeFunc) {
       
   229     xmlSecIOCallbackPtr callbacks;
       
   230     int ret;
       
   231     
       
   232     xmlSecAssert2(matchFunc != NULL, -1);
       
   233     
       
   234     callbacks = xmlSecIOCallbackCreate(matchFunc, openFunc, readFunc, closeFunc);
       
   235     if(callbacks == NULL) {
       
   236 	xmlSecError(XMLSEC_ERRORS_HERE,
       
   237 		    NULL,
       
   238 		    "xmlSecIOCallbackCreate",
       
   239 		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
       
   240 		    XMLSEC_ERRORS_NO_MESSAGE);
       
   241 	return(-1);
       
   242     }
       
   243     
       
   244     ret = xmlSecPtrListAdd(&xmlSecAllIOCallbacks, callbacks);
       
   245     if(ret < 0) {
       
   246 	xmlSecError(XMLSEC_ERRORS_HERE,
       
   247 		    NULL,
       
   248 		    "xmlSecPtrListAdd",
       
   249 		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
       
   250 		    XMLSEC_ERRORS_NO_MESSAGE);
       
   251 	xmlSecIOCallbackDestroy(callbacks);
       
   252 	return(-1);
       
   253     }
       
   254     return(0);
       
   255 }
       
   256 
       
   257 
       
   258 /**
       
   259  * xmlSecIORegisterDefaultCallbacks:
       
   260  *
       
   261  * Registers the default compiled-in I/O handlers.
       
   262  *
       
   263  * Returns 0 on success or a negative value otherwise.
       
   264  */
       
   265 EXPORT_C
       
   266 int
       
   267 xmlSecIORegisterDefaultCallbacks(void) {
       
   268     int ret;
       
   269     
       
   270 #ifdef LIBXML_HTTP_ENABLED
       
   271     ret = xmlSecIORegisterCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
       
   272 	                      xmlIOHTTPRead, xmlIOHTTPClose);
       
   273     if(ret < 0) {
       
   274 	xmlSecError(XMLSEC_ERRORS_HERE,
       
   275 		    NULL,
       
   276 		    "xmlSecIORegisterCallbacks",
       
   277 		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
       
   278 		    "http");
       
   279 	return(-1);
       
   280     }
       
   281 #endif /* LIBXML_HTTP_ENABLED */
       
   282 
       
   283 #ifdef LIBXML_FTP_ENABLED
       
   284     ret = xmlSecIORegisterCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
       
   285 	                      xmlIOFTPRead, xmlIOFTPClose);
       
   286     if(ret < 0) {
       
   287 	xmlSecError(XMLSEC_ERRORS_HERE,
       
   288 		    NULL,
       
   289 		    "xmlSecIORegisterCallbacks",
       
   290 		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
       
   291 		    "ftp");
       
   292 	return(-1);
       
   293     }
       
   294 #endif /* LIBXML_FTP_ENABLED */
       
   295 
       
   296     ret = xmlSecIORegisterCallbacks(xmlFileMatch, xmlFileOpen,
       
   297 	                      xmlFileRead, xmlFileClose);
       
   298     if(ret < 0) {
       
   299 	xmlSecError(XMLSEC_ERRORS_HERE,
       
   300 		    NULL,
       
   301 		    "xmlSecIORegisterCallbacks",
       
   302 		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
       
   303 		    "file");
       
   304 	return(-1);
       
   305     }
       
   306 
       
   307     return(0);
       
   308 }
       
   309 
       
   310 
       
   311 
       
   312 								
       
   313 /**************************************************************
       
   314  *
       
   315  * Input URI Transform
       
   316  *
       
   317  * xmlSecInputURICtx is located after xmlSecTransform
       
   318  * 
       
   319  **************************************************************/
       
   320 typedef struct _xmlSecInputURICtx				xmlSecInputURICtx,
       
   321 								*xmlSecInputURICtxPtr;
       
   322 struct _xmlSecInputURICtx {
       
   323     xmlSecIOCallbackPtr		clbks;
       
   324     void*			clbksCtx;
       
   325 };
       
   326 #define xmlSecTransformInputUriSize \
       
   327 	(sizeof(xmlSecTransform) + sizeof(xmlSecInputURICtx))
       
   328 #define xmlSecTransformInputUriGetCtx(transform) \
       
   329     ((xmlSecTransformCheckSize((transform), xmlSecTransformInputUriSize)) ? \
       
   330 	(xmlSecInputURICtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform)) : \
       
   331 	(xmlSecInputURICtxPtr)NULL)
       
   332 
       
   333 static int		xmlSecTransformInputURIInitialize	(xmlSecTransformPtr transform);
       
   334 static void		xmlSecTransformInputURIFinalize		(xmlSecTransformPtr transform);
       
   335 static int		xmlSecTransformInputURIPopBin		(xmlSecTransformPtr transform, 
       
   336 								 xmlSecByte* data,
       
   337 								 xmlSecSize maxDataSize,
       
   338 								 xmlSecSize* dataSize,
       
   339 								 xmlSecTransformCtxPtr transformCtx);
       
   340 
       
   341 static xmlSecTransformKlass xmlSecTransformInputURIKlass = {
       
   342     /* klass/object sizes */
       
   343     sizeof(xmlSecTransformKlass),		/* xmlSecSize klassSize */
       
   344     xmlSecTransformInputUriSize,		/* xmlSecSize objSize */
       
   345 
       
   346     BAD_CAST "input-uri",			/* const xmlChar* name; */
       
   347     NULL,					/* const xmlChar* href; */
       
   348     0,						/* xmlSecAlgorithmUsage usage; */
       
   349 
       
   350     xmlSecTransformInputURIInitialize, 		/* xmlSecTransformInitializeMethod initialize; */
       
   351     xmlSecTransformInputURIFinalize,		/* xmlSecTransformFinalizeMethod finalize; */
       
   352     NULL,					/* xmlSecTransformNodeReadMethod readNode; */
       
   353     NULL,					/* xmlSecTransformNodeWriteMethod writeNode; */
       
   354     NULL,					/* xmlSecTransformSetKeyReqMethod setKeyReq; */
       
   355     NULL,					/* xmlSecTransformSetKeyMethod setKey; */
       
   356     NULL,					/* xmlSecTransformValidateMethod validate; */
       
   357     xmlSecTransformDefaultGetDataType,		/* xmlSecTransformGetDataTypeMethod getDataType; */
       
   358     NULL,					/* xmlSecTransformPushBinMethod pushBin; */
       
   359     xmlSecTransformInputURIPopBin,		/* xmlSecTransformPopBinMethod popBin; */
       
   360     NULL,					/* xmlSecTransformPushXmlMethod pushXml; */
       
   361     NULL,					/* xmlSecTransformPopXmlMethod popXml; */
       
   362     NULL,					/* xmlSecTransformExecuteMethod execute; */
       
   363     
       
   364     NULL,					/* void* reserved0; */
       
   365     NULL,					/* void* reserved1; */
       
   366 };
       
   367 
       
   368 /**
       
   369  * xmlSecTransformInputURIGetKlass:
       
   370  *
       
   371  * The input uri transform klass. Reads binary data from an uri.
       
   372  *
       
   373  * Returns input URI transform id.
       
   374  */
       
   375 EXPORT_C
       
   376 xmlSecTransformId 
       
   377 xmlSecTransformInputURIGetKlass(void) {
       
   378     return(&xmlSecTransformInputURIKlass);
       
   379 }
       
   380 
       
   381 /** 
       
   382  * xmlSecTransformInputURIOpen:
       
   383  * @transform: 		the pointer to IO transform.
       
   384  * @uri: 		the URL to open.
       
   385  *
       
   386  * Opens the given @uri for reading.
       
   387  *
       
   388  * Returns 0 on success or a negative value otherwise.
       
   389  */
       
   390 EXPORT_C
       
   391 int
       
   392 xmlSecTransformInputURIOpen(xmlSecTransformPtr transform, const xmlChar *uri) {
       
   393     xmlSecInputURICtxPtr ctx;
       
   394         
       
   395     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformInputURIId), -1);
       
   396     xmlSecAssert2(uri != NULL, -1);
       
   397 
       
   398     ctx = xmlSecTransformInputUriGetCtx(transform);
       
   399     xmlSecAssert2(ctx != NULL, -1);
       
   400     xmlSecAssert2(ctx->clbks == NULL, -1);
       
   401     xmlSecAssert2(ctx->clbksCtx == NULL, -1);
       
   402 
       
   403     /*
       
   404      * Try to find one of the input accept method accepting that scheme
       
   405      * Go in reverse to give precedence to user defined handlers.
       
   406      * try with an unescaped version of the uri
       
   407      */
       
   408     if(ctx->clbks == NULL) {
       
   409 	char *unescaped;
       
   410     
       
   411         unescaped = xmlURIUnescapeString((char*)uri, 0, NULL);
       
   412 	if (unescaped != NULL) {
       
   413 	    ctx->clbks = xmlSecIOCallbackPtrListFind(&xmlSecAllIOCallbacks, unescaped);
       
   414 	    if(ctx->clbks != NULL) {
       
   415 		ctx->clbksCtx = ctx->clbks->opencallback(unescaped);
       
   416 	    }
       
   417 	    xmlFree(unescaped);
       
   418 	}
       
   419     }
       
   420 
       
   421     /*
       
   422      * If this failed try with a non-escaped uri this may be a strange
       
   423      * filename
       
   424      */
       
   425     if (ctx->clbks == NULL) {
       
   426 	ctx->clbks = xmlSecIOCallbackPtrListFind(&xmlSecAllIOCallbacks, (char*)uri);
       
   427 	if(ctx->clbks != NULL) {
       
   428 	    ctx->clbksCtx = ctx->clbks->opencallback((char*)uri);
       
   429 	}
       
   430     }
       
   431 
       
   432     if((ctx->clbks == NULL) || (ctx->clbksCtx == NULL)) {
       
   433 	xmlSecError(XMLSEC_ERRORS_HERE,
       
   434 		    xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
       
   435 		    "opencallback",
       
   436 		    XMLSEC_ERRORS_R_IO_FAILED,
       
   437 		    "uri=%s;errno=%d", 
       
   438 		    xmlSecErrorsSafeString(uri),
       
   439 		    errno);
       
   440 	return(-1);
       
   441     }
       
   442     
       
   443     return(0);
       
   444 }
       
   445 
       
   446 static int
       
   447 xmlSecTransformInputURIInitialize(xmlSecTransformPtr transform) {
       
   448     xmlSecInputURICtxPtr ctx;
       
   449 
       
   450     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformInputURIId), -1);
       
   451 
       
   452     ctx = xmlSecTransformInputUriGetCtx(transform);
       
   453     xmlSecAssert2(ctx != NULL, -1);
       
   454     
       
   455     memset(ctx, 0, sizeof(xmlSecInputURICtx));
       
   456     return(0);
       
   457 }
       
   458 
       
   459 static void
       
   460 xmlSecTransformInputURIFinalize(xmlSecTransformPtr transform) {
       
   461     xmlSecInputURICtxPtr ctx;
       
   462 
       
   463     xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformInputURIId));
       
   464 
       
   465     ctx = xmlSecTransformInputUriGetCtx(transform);
       
   466     xmlSecAssert(ctx != NULL);
       
   467 
       
   468     if((ctx->clbksCtx != NULL) && (ctx->clbks != NULL) && (ctx->clbks->closecallback != NULL)) {
       
   469 	(ctx->clbks->closecallback)(ctx->clbksCtx);
       
   470     }
       
   471     memset(ctx, 0, sizeof(xmlSecInputURICtx));
       
   472 }
       
   473 
       
   474 static int 
       
   475 xmlSecTransformInputURIPopBin(xmlSecTransformPtr transform, xmlSecByte* data,
       
   476 			      xmlSecSize maxDataSize, xmlSecSize* dataSize, 
       
   477 			      xmlSecTransformCtxPtr transformCtx) {
       
   478     xmlSecInputURICtxPtr ctx;
       
   479 
       
   480     int ret;
       
   481     			    
       
   482     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformInputURIId), -1);
       
   483     xmlSecAssert2(data != NULL, -1);
       
   484     xmlSecAssert2(dataSize != NULL, -1);
       
   485     xmlSecAssert2(transformCtx != NULL, -1);
       
   486 
       
   487     ctx = xmlSecTransformInputUriGetCtx(transform);
       
   488     xmlSecAssert2(ctx != NULL, -1);
       
   489     
       
   490     if((ctx->clbksCtx != NULL) && (ctx->clbks != NULL) && (ctx->clbks->readcallback != NULL)) {
       
   491         ret = (ctx->clbks->readcallback)(ctx->clbksCtx, (char*)data, (int)maxDataSize);
       
   492 	if(ret < 0) {
       
   493 	    xmlSecError(XMLSEC_ERRORS_HERE,
       
   494 			xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
       
   495 			"readcallback",
       
   496 			XMLSEC_ERRORS_R_IO_FAILED,
       
   497 			"errno=%d", errno);
       
   498 	    return(-1);
       
   499 	}
       
   500 	(*dataSize) = ret;
       
   501     } else {
       
   502 	(*dataSize) = 0;
       
   503     }
       
   504     return(0);
       
   505 }
       
   506