00001 /* ==================================================================== 00002 * The Kannel Software License, Version 1.0 00003 * 00004 * Copyright (c) 2001-2008 Kannel Group 00005 * Copyright (c) 1998-2001 WapIT Ltd. 00006 * All rights reserved. 00007 * 00008 * Redistribution and use in source and binary forms, with or without 00009 * modification, are permitted provided that the following conditions 00010 * are met: 00011 * 00012 * 1. Redistributions of source code must retain the above copyright 00013 * notice, this list of conditions and the following disclaimer. 00014 * 00015 * 2. Redistributions in binary form must reproduce the above copyright 00016 * notice, this list of conditions and the following disclaimer in 00017 * the documentation and/or other materials provided with the 00018 * distribution. 00019 * 00020 * 3. The end-user documentation included with the redistribution, 00021 * if any, must include the following acknowledgment: 00022 * "This product includes software developed by the 00023 * Kannel Group (http://www.kannel.org/)." 00024 * Alternately, this acknowledgment may appear in the software itself, 00025 * if and wherever such third-party acknowledgments normally appear. 00026 * 00027 * 4. The names "Kannel" and "Kannel Group" must not be used to 00028 * endorse or promote products derived from this software without 00029 * prior written permission. For written permission, please 00030 * contact org@kannel.org. 00031 * 00032 * 5. Products derived from this software may not be called "Kannel", 00033 * nor may "Kannel" appear in their name, without prior written 00034 * permission of the Kannel Group. 00035 * 00036 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 00037 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00038 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00039 * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 00040 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 00041 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 00042 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 00043 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 00044 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 00045 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 00046 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00047 * ==================================================================== 00048 * 00049 * This software consists of voluntary contributions made by many 00050 * individuals on behalf of the Kannel Group. For more information on 00051 * the Kannel Group, please see <http://www.kannel.org/>. 00052 * 00053 * Portions of this software are based upon software originally written at 00054 * WapIT Ltd., Helsinki, Finland for the Kannel project. 00055 */ 00056 00057 /* 00058 * xmlrpc.h - XML-RPC functions 00059 * 00060 * Functions to handle XML-RPC structure - building and parsing 00061 * 00062 * XML-RPC is HTTP-based XML defination to handle remote procedure calls, 00063 * and is defined in http://www.xml-rpc.org 00064 * 00065 * The current implementation is not yet ready (it does not, for example, 00066 * do any parsing nor building the tree), and is not used for any real use, 00067 * yet, but probably future interfaces might be able to use this, too 00068 * 00069 * 00070 * Kalle Marjola 2001 for project Kannel 00071 * Robert Gałach <robert.galach@my.tenbit.pl> 00072 */ 00073 00074 #ifndef __XMLRPC_H 00075 #define __XMLRPC_H 00076 00077 #include "gwlib/gwlib.h" 00078 00079 /* 00080 * types and structures defined by www.xml-rpc.com 00081 */ 00082 typedef struct xmlrpc_document XMLRPCDocument; 00083 typedef struct xmlrpc_value XMLRPCValue; 00084 typedef struct xmlrpc_scalar XMLRPCScalar; 00085 00086 enum { 00087 xr_undefined, xr_scalar, xr_array, xr_struct, 00088 xr_string, xr_int, xr_bool, xr_double, xr_date, xr_base64, 00089 xr_methodcall, xr_methodresponse 00090 }; 00091 00092 /* 00093 * status codes while parsing 00094 */ 00095 enum { 00096 XMLRPC_COMPILE_OK, 00097 XMLRPC_XMLPARSE_FAILED, 00098 XMLRPC_PARSING_FAILED 00099 }; 00100 00101 00102 00103 /*** DOCUMENTS ***/ 00104 00105 /* Create new XMLRPCDocument object of undefined_type */ 00106 XMLRPCDocument *xmlrpc_doc_create(void); 00107 /* Create new MethodCall with given name */ 00108 XMLRPCDocument *xmlrpc_doc_create_call(Octstr *name); 00109 /* Create new MethodResponse */ 00110 XMLRPCDocument *xmlrpc_doc_create_response(void); 00111 /* Create new fault MethodResponse with given code and fault string */ 00112 XMLRPCDocument *xmlrpc_doc_create_faultresponse(long faultcode, Octstr *faultstring); 00113 00114 /* Create new XMLRPCDocument object from given body of text/xml, 00115 * d_type is expected document type: xr_methodcall, xr_methodresponse 00116 or xr_undefined if any 00117 */ 00118 XMLRPCDocument *xmlrpc_doc_parse(Octstr *post_body, int d_type); 00119 00120 /* Destroy XMLRPCDocument object */ 00121 void xmlrpc_doc_destroy(XMLRPCDocument *xrdoc, int d_type); 00122 00123 /* Add a scalar param to XMLRPCDocument object. 00124 * d_type is expected document type: xr_methodcall or xr_methodresponse. 00125 * Return 0 if ok or -1 if something wrong (e.g. xrdoc is null or faultresponse) 00126 */ 00127 int xmlrpc_doc_add_scalar(XMLRPCDocument *xrdoc, int d_type, int type, void *arg); 00128 00129 /* Add given XMLRPCValue param to XMLRPCDocument object. 00130 * d_type is expected document type: xr_methodcall or xr_methodresponse. 00131 * Return 0 if ok or -1 if something wrong. 00132 * NOTE that value is NOT duplicated 00133 */ 00134 int xmlrpc_doc_add_value(XMLRPCDocument *xrdoc, int d_type, XMLRPCValue *value); 00135 00136 /* Create Octstr (text/xml string) out of given XMLRPCDocument. 00137 * d_type is expected document type. 00138 * level is the indent width. 00139 * Caller must free returned Octstr. 00140 */ 00141 Octstr *xmlrpc_doc_print(XMLRPCDocument *xrdoc, int d_type, int level); 00142 00143 /* Send XMLRPCDocument to given url with given headers. 00144 * d_type is expected document type. 00145 * Note: adds XML-RPC specified headers into given list if needed. 00146 * and if NULL when this function called, automatically generated 00147 * 00148 * Return 0 if all went fine, -1 if failure. As user reference, uses *void 00149 */ 00150 int xmlrpc_doc_send(XMLRPCDocument *xrdoc, int d_type, HTTPCaller *http_ref, 00151 Octstr *url, List *headers, void *ref); 00152 00153 00154 /*** METHOD CALLS ***/ 00155 00156 /* Create new MethodCall with given name and no params */ 00157 #define xmlrpc_create_call(method) \ 00158 xmlrpc_doc_create_call(method) 00159 00160 /* Create new MethodCall from given body of text/xml */ 00161 #define xmlrpc_parse_call(post_body) \ 00162 xmlrpc_doc_parse(post_body, xr_methodcall) 00163 00164 /* Destroy MethodCall */ 00165 #define xmlrpc_destroy_call(call) \ 00166 xmlrpc_doc_destroy(call, xr_methodcall) 00167 00168 /* Add a scalar param to MethodCall. 00169 * type is scalar type: xr_string, xr_int, xr_bool, xr_double, xr_date or xr_base64 00170 * arg is pointer to value of given type: Octstr*, long*, int*, double*, Octstr* or Octstr* 00171 * respectively 00172 * Return 0 if ok or -1 if something wrong. 00173 */ 00174 #define xmlrpc_add_call_scalar(call, type, arg) \ 00175 xmlrpc_doc_add_scalar(call, xr_methodcall, type, arg) 00176 00177 /* Add given XMLRPCValue param to MethodCall. 00178 * Return 0 if ok or -1 if something wrong. 00179 * NOTE: value is NOT duplicated 00180 */ 00181 #define xmlrpc_add_call_value(call, value) \ 00182 xmlrpc_doc_add_value(call, xr_methodcall, value) 00183 00184 /* Create Octstr (text/xml string) out of given MethodCall. Caller 00185 * must free returned Octstr 00186 */ 00187 #define xmlrpc_print_call(call) \ 00188 xmlrpc_doc_print(call, xr_methodcall, 0) 00189 00190 /* Send MethodCall to given url with given headers. 00191 * d_type is expected document type. 00192 * Note: adds XML-RPC specified headers into given list if needed. 00193 * and if NULL when this function called, automatically generated 00194 * 00195 * Return 0 if all went fine, -1 if failure. As user reference, uses *void 00196 */ 00197 #define xmlrpc_send_call(call,http_ref, url, headers, ref) \ 00198 xmlrpc_doc_send(call, xr_methodcall, http_ref, url, headers, ref) 00199 00200 /* Return the name of the method requested or NULL if document is not method call */ 00201 Octstr *xmlrpc_get_call_name(XMLRPCDocument *call); 00202 00203 00204 00205 /*** METHOD RESPONSES ***/ 00206 00207 /* Create a new MethodResponse with no param value */ 00208 #define xmlrpc_create_response() \ 00209 xmlrpc_doc_create_response() 00210 00211 /* Create a new fault MethodResponse with given faultcode and faultstring */ 00212 #define xmlrpc_create_faultresponse(faultcode, faultstring) \ 00213 xmlrpc_doc_create_faultresponse(faultcode, faultstring) 00214 00215 /* Create a new MethodResponse from given text/xml string */ 00216 #define xmlrpc_parse_response(post_body) \ 00217 xmlrpc_doc_parse(post_body, xr_methodresponse) 00218 00219 /* Destroy MethodResponse */ 00220 #define xmlrpc_destroy_response(response) \ 00221 xmlrpc_doc_destroy(response, xr_methodresponse) 00222 00223 /* Add a scalar param to MethodResponse. 00224 * type is scalar type: xr_string, xr_int, xr_bool, xr_double, xr_date or xr_base64 00225 * arg is pointer to value of given type: Octstr*, long*, int*, double*, Octstr* or Octstr* 00226 * respectively 00227 * Return 0 if ok or -1 if something wrong. 00228 */ 00229 #define xmlrpc_add_response_scalar(response, type, arg) \ 00230 xmlrpc_doc_add_scalar(response, xr_methodresponse, type, arg) 00231 00232 /* Add given XMLRPCValue param to MethodResponse. 00233 * Return 0 if ok or -1 if something wrong. 00234 * NOTE: value is NOT duplicated 00235 */ 00236 #define xmlrpc_add_response_value(response, value) \ 00237 xmlrpc_doc_add_value(response, xr_methodresponse, value) 00238 00239 /* Create Octstr (text/xml string) out of given MethodCall. Caller 00240 * must free returned Octstr 00241 */ 00242 #define xmlrpc_print_response(response) \ 00243 xmlrpc_doc_print(response, xr_methodresponse, 0) 00244 00245 /* Send MethodResponse to given url with given headers. 00246 * d_type is expected document type. 00247 * Note: adds XML-RPC specified headers into given list if needed. 00248 * and if NULL when this function called, automatically generated 00249 * 00250 * Return 0 if all went fine, -1 if failure. As user reference, uses *void 00251 */ 00252 #define xmlrpc_send_response(response, http_ref, url, headers, ref) \ 00253 xmlrpc_doc_send(call, xr_methodresponse, http_ref, url, headers, ref) 00254 00255 00256 00257 /*** PARAMS HANDLING ***/ 00258 00259 /* Return -1 if XMLRPCDocument can't have params or number of params */ 00260 int xmlrpc_count_params(XMLRPCDocument *xrdoc); 00261 00262 /* Return i'th MethodCall/MethodResponse param 00263 * or NULL if something wrong 00264 */ 00265 XMLRPCValue *xmlrpc_get_param(XMLRPCDocument *xrdoc, int i); 00266 00267 /* Return type of i'th MethodCall/MethodResponse param: xr_scalar, xr_array or xr_struct 00268 * or -1 if no param 00269 */ 00270 int xmlrpc_get_type_param(XMLRPCDocument *xrdoc, int i); 00271 00272 /* Return content of i'th MethodCall/MethodResponse param: 00273 * XMLRPCScalar if xr_scalar, List of XMLRPCValues if xr_array 00274 * or Dict of XMLRPCValues if xr_struct (member names as keys) 00275 * or NULL if no param 00276 */ 00277 void *xmlrpc_get_content_param(XMLRPCDocument *xrdoc, int i); 00278 00279 /* Identify d_type of given XMLRPCDocument and add a scalar param. 00280 * type is scalar type: xr_string, xr_int, xr_bool, xr_double, xr_date or xr_base64 00281 * arg is pointer to value of given type: Octstr*, long*, int*, double*, Octstr* or Octstr* 00282 * respectively 00283 * Return 0 if ok or -1 if something wrong. 00284 */ 00285 #define xmlrpc_add_scalar_param(xrdoc, type, arg) \ 00286 xmlrpc_doc_add_scalar(xrdoc, xr_undefined, type, arg) 00287 00288 /* Identify d_type of given XMLRPCDocument and add XMLRPCValue param. 00289 * Return 0 if ok or -1 if something wrong. 00290 * NOTE: value is NOT duplicated 00291 */ 00292 #define xmlrpc_add_param(xrdoc, value) \ 00293 xmlrpc_doc_add_value(xrdoc, xr_undefined, value) 00294 00295 00296 00297 /*** VALUES HANDLING ***/ 00298 00299 /* Create a new XMLRPCValue object of undefined type */ 00300 XMLRPCValue *xmlrpc_value_create(void); 00301 00302 /* Destroy given XMLRPCValue object */ 00303 void xmlrpc_value_destroy(XMLRPCValue *val); 00304 00305 /* Wrapper for destroy */ 00306 void xmlrpc_value_destroy_item(void *val); 00307 00308 /* Set type of XMLRPCValue: xr_scalar, xr_array or xr_struct 00309 * Return 0 if ok or -1 if something wrong. 00310 */ 00311 int xmlrpc_value_set_type(XMLRPCValue *val, int v_type); 00312 00313 /* Set XMLRPCValue content: 00314 * XMLRPCScalar if xr_scalar, List of XMLRPCValues if xr_array 00315 * or Dict of XMLRPCValues if xr_struct (member names as keys) 00316 * Return 0 if ok or -1 if something wrong. 00317 */ 00318 int xmlrpc_value_set_content(XMLRPCValue *val, void *content); 00319 00320 /* Return type of XMLRPCValue: xr_scalar, xr_array or xr_struct */ 00321 int xmlrpc_value_get_type(XMLRPCValue *val); 00322 00323 /* Return leaf type of XMLRPCValue: 00324 * as above, but if value is xr_scalar return type of scalar 00325 */ 00326 int xmlrpc_value_get_type_smart(XMLRPCValue *val); 00327 00328 /* Return XMLRPCValue content: 00329 * XMLRPCScalar if xr_scalar, List of XMLRPCValues if xr_array 00330 * or Dict of XMLRPCValues if xr_struct (member names as keys) 00331 * or NULL if something wrong. 00332 */ 00333 void *xmlrpc_value_get_content(XMLRPCValue *val); 00334 00335 /* Create Octstr (text/xml string) out of given XMLRPCValue. Caller 00336 * must free returned Octstr 00337 */ 00338 Octstr *xmlrpc_value_print(XMLRPCValue *val, int level); 00339 00340 00341 /*** STRUCT VALUE HANDLING ***/ 00342 00343 /* Create a new XMLRPCValue object of xr_struct type. 00344 * size is expected number of struct members 00345 */ 00346 XMLRPCValue *xmlrpc_create_struct_value(int size); 00347 00348 /* Return -1 if not a struct or number of members */ 00349 long xmlrpc_count_members(XMLRPCValue *xrstruct); 00350 00351 /* Add member with given name and value to the struct */ 00352 int xmlrpc_add_member(XMLRPCValue *xrstruct, Octstr *name, XMLRPCValue *value); 00353 00354 /* Add member with given name and scalar value built with type and arg to the struct */ 00355 int xmlrpc_add_member_scalar(XMLRPCValue *xrstruct, Octstr *name, int type, void *arg); 00356 00357 /* Return value of member with given name or NULL if not found */ 00358 XMLRPCValue *xmlrpc_get_member(XMLRPCValue *xrstruct, Octstr *name); 00359 00360 /* Return type of member with given name (xr_scalar, xr_array or xr_struct) 00361 * or -1 if not found 00362 */ 00363 int xmlrpc_get_member_type(XMLRPCValue *xrstruct, Octstr *name); 00364 00365 /* Return content of member with given name: 00366 * XMLRPCScalar if xr_scalar, List of XMLRPCValues if xr_array 00367 * or Dict of XMLRPCValues if xr_struct (member names as keys) 00368 * or NULL if not found. 00369 */ 00370 void *xmlrpc_get_member_content(XMLRPCValue *xrstruct, Octstr *name); 00371 00372 00373 /* Create Octstr (text/xml string) out of struct. Caller 00374 * must free returned Octstr. 00375 */ 00376 Octstr *xmlrpc_print_struct(Dict *members, int level); 00377 00378 00379 /*** ARRAY VALUE HANDLING ***/ 00380 00381 /* Create a new XMLRPCValue object of xr_array type. */ 00382 XMLRPCValue *xmlrpc_create_array_value(void); 00383 00384 /* Return -1 if not an array or number of elements */ 00385 int xmlrpc_count_elements(XMLRPCValue *xrarray); 00386 00387 /* Add XMLRPCValue element to the end of array */ 00388 int xmlrpc_add_element(XMLRPCValue *xrarray, XMLRPCValue *value); 00389 00390 /* Build scalar XMLRPCValue with type and arg, 00391 *and add this element to the end of array 00392 */ 00393 int xmlrpc_add_element_scalar(XMLRPCValue *xrarray, int type, void *arg); 00394 00395 /* Return value of i'th element in array or NULL if something wrong*/ 00396 XMLRPCValue *xmlrpc_get_element(XMLRPCValue *xrarray, int i); 00397 00398 /* Return type of i'th element in array (xr_scalar, xr_array or xr_struct) 00399 * or -1 if not found 00400 */ 00401 int xmlrpc_get_element_type(XMLRPCValue *xrarray, int i); 00402 00403 /* Return content of i'th element: 00404 * XMLRPCScalar if xr_scalar, List of XMLRPCValues if xr_array 00405 * or Dict of XMLRPCValues if xr_struct (member names as keys) 00406 * or NULL if not found. 00407 */ 00408 void *xmlrpc_get_element_content(XMLRPCValue *xrarray, int i); 00409 00410 /* Create Octstr (text/xml string) out of array. Caller 00411 * must free returned Octstr. 00412 */ 00413 Octstr *xmlrpc_print_array(List *elements, int level); 00414 00415 00416 /*** SCALAR HANDLING ***/ 00417 00418 /* Create a new scalar of given type and value 00419 * (which must be in right format) 00420 * type is scalar type: xr_string, xr_int, xr_bool, xr_double, xr_date or xr_base64 00421 * arg is pointer to value of given type: Octstr*, long*, int*, double*, Octstr* or Octstr* 00422 * respectively 00423 * Return NULL if something wrong. 00424 */ 00425 XMLRPCScalar *xmlrpc_scalar_create(int type, void *arg); 00426 00427 /* Destroy XMLRPCScalar */ 00428 void xmlrpc_scalar_destroy(XMLRPCScalar *scalar); 00429 00430 /* Return type of scalar or -1 if scalar is NULL */ 00431 int xmlrpc_scalar_get_type(XMLRPCScalar *scalar); 00432 00433 /* Return content of scalar 00434 * s_type is expected type of scalar 00435 */ 00436 void *xmlrpc_scalar_get_content(XMLRPCScalar *scalar, int s_type); 00437 00438 /* Create Octstr (text/xml string) out of scalar. Caller 00439 * must free returned Octstr. 00440 */ 00441 Octstr *xmlrpc_scalar_print(XMLRPCScalar *scalar, int level); 00442 00443 /* Wrappers to get scalar content of proper type 00444 * NOTE: returned values are copies, caller must free returned Octstr 00445 */ 00446 #define xmlrpc_scalar_get_double(scalar) \ 00447 *(double *)xmlrpc_scalar_get_content(scalar, xr_double) 00448 00449 #define xmlrpc_scalar_get_int(scalar) \ 00450 *(long *)xmlrpc_scalar_get_content(scalar, xr_int) 00451 00452 #define xmlrpc_scalar_get_bool(scalar) \ 00453 *(int *)xmlrpc_scalar_get_content(scalar, xr_bool) 00454 00455 #define xmlrpc_scalar_get_date(scalar) \ 00456 octstr_duplicate((Octstr *)xmlrpc_scalar_get_content(scalar, xr_date)) 00457 00458 #define xmlrpc_scalar_get_string(scalar) \ 00459 octstr_duplicate((Octstr *)xmlrpc_scalar_get_content(scalar, xr_string)) 00460 00461 #define xmlrpc_scalar_get_base64(scalar) \ 00462 octstr_duplicate((Octstr *)xmlrpc_scalar_get_content(scalar, xr_base64)) 00463 00464 00465 /*** SCALAR VALUE HANDLING ***/ 00466 00467 /* Create XMLRPCScalar with type and arg, 00468 * and then create XMLRPCValue with xr_scalar type and 00469 * created XMLRPCScalar as content 00470 */ 00471 XMLRPCValue *xmlrpc_create_scalar_value(int type, void *arg); 00472 00473 /* As above, but scalar is xr_double type */ 00474 XMLRPCValue *xmlrpc_create_double_value(double val); 00475 00476 /* As above, but scalar is xr_int type */ 00477 XMLRPCValue *xmlrpc_create_int_value(long val); 00478 00479 /* As above, but scalar is xr_string type */ 00480 XMLRPCValue *xmlrpc_create_string_value(Octstr *val); 00481 00482 /* Return type of scalar in given XMLRPCValue */ 00483 #define xmlrpc_get_scalar_value_type(value) \ 00484 xmlrpc_scalar_get_type(xmlrpc_value_get_content(value)) 00485 00486 /* Wrappers to get scalar content of proper type from XMLRPCValue */ 00487 #define xmlrpc_get_double_value(value) \ 00488 xmlrpc_scalar_get_double(xmlrpc_value_get_content(value)) 00489 #define xmlrpc_get_int_value(value) \ 00490 xmlrpc_scalar_get_int(xmlrpc_value_get_content(value)) 00491 #define xmlrpc_get_string_value(value) \ 00492 xmlrpc_scalar_get_string(xmlrpc_value_get_content(value)) 00493 #define xmlrpc_get_base64_value(value) \ 00494 xmlrpc_scalar_get_base64(xmlrpc_value_get_content(value)) 00495 00496 00497 /*** FAULT HANDLING ***/ 00498 00499 /* Return 1 if XMLRPCDocument is fault MethodResponse */ 00500 int xmlrpc_is_fault(XMLRPCDocument *response); 00501 00502 /* Return faultcode from fault MethodResponse 00503 * or -1 if XMLRPCDocument is not valid fault MethodResponse 00504 */ 00505 long xmlrpc_get_faultcode(XMLRPCDocument *faultresponse); 00506 00507 /* Return faultstring from fault MethodResponse 00508 * or NULL if XMLRPCDocument is not valid fault MethodResponse 00509 */ 00510 Octstr *xmlrpc_get_faultstring(XMLRPCDocument *faultresponse); 00511 00512 00513 00514 /*** PARSE STATUS HANDLING***/ 00515 00516 /* 00517 * Check if parsing had any errors, return status code of parsing by 00518 * returning one of the following: 00519 * XMLRPC_COMPILE_OK, 00520 * XMLRPC_XMLPARSE_FAILED, 00521 * XMLRPC_PARSING_FAILED 00522 * -1 if call has been NULL 00523 */ 00524 int xmlrpc_parse_status(XMLRPCDocument *xrdoc); 00525 00526 /* Return parser error string if parse_status != XMLRPC_COMPILE_OK */ 00527 /* return NULL if no error occured or no error string was available */ 00528 Octstr *xmlrpc_parse_error(XMLRPCDocument *xrdoc); 00529 00530 #endif