GNU libmicrohttpd 0.9.5
|
00001 /* 00002 This file is part of libmicrohttpd 00003 (C) 2006, 2007, 2008, 2009, 2010 Christian Grothoff (and other contributing authors) 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the Free Software 00017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00018 */ 00019 00072 #ifndef MHD_MICROHTTPD_H 00073 #define MHD_MICROHTTPD_H 00074 00075 #ifdef __cplusplus 00076 extern "C" 00077 { 00078 #if 0 /* keep Emacsens' auto-indent happy */ 00079 } 00080 #endif 00081 #endif 00082 00083 /* While we generally would like users to use a configure-driven 00084 build process which detects which headers are present and 00085 hence works on any platform, we use "standard" includes here 00086 to build out-of-the-box for beginning users on common systems. 00087 00088 Once you have a proper build system and go for more exotic 00089 platforms, you should define MHD_PLATFORM_H in some header that 00090 you always include *before* "microhttpd.h". Then the following 00091 "standard" includes won't be used (which might be a good 00092 idea, especially on platforms where they do not exist). */ 00093 #ifndef MHD_PLATFORM_H 00094 #include <unistd.h> 00095 #include <stdarg.h> 00096 #include <stdint.h> 00097 #ifdef __MINGW32__ 00098 #include <ws2tcpip.h> 00099 #else 00100 #include <sys/time.h> 00101 #include <sys/types.h> 00102 #include <sys/socket.h> 00103 #endif 00104 #endif 00105 00109 #define MHD_VERSION 0x00090600 00110 00114 #define MHD_YES 1 00115 00119 #define MHD_NO 0 00120 00124 #define MHD_INVALID_NONCE -1 00125 00130 #ifdef UINT64_MAX 00131 #define MHD_SIZE_UNKNOWN UINT64_MAX 00132 #else 00133 #define MHD_SIZE_UNKNOWN ((uint64_t) -1LL) 00134 #endif 00135 00136 #ifdef SIZE_MAX 00137 #define MHD_CONTENT_READER_END_OF_STREAM SIZE_MAX 00138 #define MHD_CONTENT_READER_END_WITH_ERROR (SIZE_MAX - 1) 00139 #else 00140 #define MHD_CONTENT_READER_END_OF_STREAM ((size_t) -1LL) 00141 #define MHD_CONTENT_READER_END_WITH_ERROR (((size_t) -1LL) - 1) 00142 #endif 00143 00149 #ifndef MHD_LONG_LONG 00150 #define MHD_LONG_LONG long long 00151 #endif 00152 #ifndef MHD_LONG_LONG_PRINTF 00153 00157 #define MHD_LONG_LONG_PRINTF "ll" 00158 #endif 00159 00160 00164 #define MHD_HTTP_CONTINUE 100 00165 #define MHD_HTTP_SWITCHING_PROTOCOLS 101 00166 #define MHD_HTTP_PROCESSING 102 00167 00168 #define MHD_HTTP_OK 200 00169 #define MHD_HTTP_CREATED 201 00170 #define MHD_HTTP_ACCEPTED 202 00171 #define MHD_HTTP_NON_AUTHORITATIVE_INFORMATION 203 00172 #define MHD_HTTP_NO_CONTENT 204 00173 #define MHD_HTTP_RESET_CONTENT 205 00174 #define MHD_HTTP_PARTIAL_CONTENT 206 00175 #define MHD_HTTP_MULTI_STATUS 207 00176 00177 #define MHD_HTTP_MULTIPLE_CHOICES 300 00178 #define MHD_HTTP_MOVED_PERMANENTLY 301 00179 #define MHD_HTTP_FOUND 302 00180 #define MHD_HTTP_SEE_OTHER 303 00181 #define MHD_HTTP_NOT_MODIFIED 304 00182 #define MHD_HTTP_USE_PROXY 305 00183 #define MHD_HTTP_SWITCH_PROXY 306 00184 #define MHD_HTTP_TEMPORARY_REDIRECT 307 00185 00186 #define MHD_HTTP_BAD_REQUEST 400 00187 #define MHD_HTTP_UNAUTHORIZED 401 00188 #define MHD_HTTP_PAYMENT_REQUIRED 402 00189 #define MHD_HTTP_FORBIDDEN 403 00190 #define MHD_HTTP_NOT_FOUND 404 00191 #define MHD_HTTP_METHOD_NOT_ALLOWED 405 00192 #define MHD_HTTP_METHOD_NOT_ACCEPTABLE 406 00193 #define MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED 407 00194 #define MHD_HTTP_REQUEST_TIMEOUT 408 00195 #define MHD_HTTP_CONFLICT 409 00196 #define MHD_HTTP_GONE 410 00197 #define MHD_HTTP_LENGTH_REQUIRED 411 00198 #define MHD_HTTP_PRECONDITION_FAILED 412 00199 #define MHD_HTTP_REQUEST_ENTITY_TOO_LARGE 413 00200 #define MHD_HTTP_REQUEST_URI_TOO_LONG 414 00201 #define MHD_HTTP_UNSUPPORTED_MEDIA_TYPE 415 00202 #define MHD_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE 416 00203 #define MHD_HTTP_EXPECTATION_FAILED 417 00204 #define MHD_HTTP_UNPROCESSABLE_ENTITY 422 00205 #define MHD_HTTP_LOCKED 423 00206 #define MHD_HTTP_FAILED_DEPENDENCY 424 00207 #define MHD_HTTP_UNORDERED_COLLECTION 425 00208 #define MHD_HTTP_UPGRADE_REQUIRED 426 00209 #define MHD_HTTP_RETRY_WITH 449 00210 00211 #define MHD_HTTP_INTERNAL_SERVER_ERROR 500 00212 #define MHD_HTTP_NOT_IMPLEMENTED 501 00213 #define MHD_HTTP_BAD_GATEWAY 502 00214 #define MHD_HTTP_SERVICE_UNAVAILABLE 503 00215 #define MHD_HTTP_GATEWAY_TIMEOUT 504 00216 #define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED 505 00217 #define MHD_HTTP_VARIANT_ALSO_NEGOTIATES 506 00218 #define MHD_HTTP_INSUFFICIENT_STORAGE 507 00219 #define MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509 00220 #define MHD_HTTP_NOT_EXTENDED 510 00221 00227 #define MHD_ICY_FLAG ((uint32_t)(1 << 31)) 00228 00229 /* See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ 00230 #define MHD_HTTP_HEADER_ACCEPT "Accept" 00231 #define MHD_HTTP_HEADER_ACCEPT_CHARSET "Accept-Charset" 00232 #define MHD_HTTP_HEADER_ACCEPT_ENCODING "Accept-Encoding" 00233 #define MHD_HTTP_HEADER_ACCEPT_LANGUAGE "Accept-Language" 00234 #define MHD_HTTP_HEADER_ACCEPT_RANGES "Accept-Ranges" 00235 #define MHD_HTTP_HEADER_AGE "Age" 00236 #define MHD_HTTP_HEADER_ALLOW "Allow" 00237 #define MHD_HTTP_HEADER_AUTHORIZATION "Authorization" 00238 #define MHD_HTTP_HEADER_CACHE_CONTROL "Cache-Control" 00239 #define MHD_HTTP_HEADER_CONNECTION "Connection" 00240 #define MHD_HTTP_HEADER_CONTENT_ENCODING "Content-Encoding" 00241 #define MHD_HTTP_HEADER_CONTENT_LANGUAGE "Content-Language" 00242 #define MHD_HTTP_HEADER_CONTENT_LENGTH "Content-Length" 00243 #define MHD_HTTP_HEADER_CONTENT_LOCATION "Content-Location" 00244 #define MHD_HTTP_HEADER_CONTENT_MD5 "Content-MD5" 00245 #define MHD_HTTP_HEADER_CONTENT_RANGE "Content-Range" 00246 #define MHD_HTTP_HEADER_CONTENT_TYPE "Content-Type" 00247 #define MHD_HTTP_HEADER_COOKIE "Cookie" 00248 #define MHD_HTTP_HEADER_DATE "Date" 00249 #define MHD_HTTP_HEADER_ETAG "ETag" 00250 #define MHD_HTTP_HEADER_EXPECT "Expect" 00251 #define MHD_HTTP_HEADER_EXPIRES "Expires" 00252 #define MHD_HTTP_HEADER_FROM "From" 00253 #define MHD_HTTP_HEADER_HOST "Host" 00254 #define MHD_HTTP_HEADER_IF_MATCH "If-Match" 00255 #define MHD_HTTP_HEADER_IF_MODIFIED_SINCE "If-Modified-Since" 00256 #define MHD_HTTP_HEADER_IF_NONE_MATCH "If-None-Match" 00257 #define MHD_HTTP_HEADER_IF_RANGE "If-Range" 00258 #define MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since" 00259 #define MHD_HTTP_HEADER_LAST_MODIFIED "Last-Modified" 00260 #define MHD_HTTP_HEADER_LOCATION "Location" 00261 #define MHD_HTTP_HEADER_MAX_FORWARDS "Max-Forwards" 00262 #define MHD_HTTP_HEADER_PRAGMA "Pragma" 00263 #define MHD_HTTP_HEADER_PROXY_AUTHENTICATE "Proxy-Authenticate" 00264 #define MHD_HTTP_HEADER_PROXY_AUTHORIZATION "Proxy-Authorization" 00265 #define MHD_HTTP_HEADER_RANGE "Range" 00266 #define MHD_HTTP_HEADER_REFERER "Referer" 00267 #define MHD_HTTP_HEADER_RETRY_AFTER "Retry-After" 00268 #define MHD_HTTP_HEADER_SERVER "Server" 00269 #define MHD_HTTP_HEADER_SET_COOKIE "Set-Cookie" 00270 #define MHD_HTTP_HEADER_SET_COOKIE2 "Set-Cookie2" 00271 #define MHD_HTTP_HEADER_TE "TE" 00272 #define MHD_HTTP_HEADER_TRAILER "Trailer" 00273 #define MHD_HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding" 00274 #define MHD_HTTP_HEADER_UPGRADE "Upgrade" 00275 #define MHD_HTTP_HEADER_USER_AGENT "User-Agent" 00276 #define MHD_HTTP_HEADER_VARY "Vary" 00277 #define MHD_HTTP_HEADER_VIA "Via" 00278 #define MHD_HTTP_HEADER_WARNING "Warning" 00279 #define MHD_HTTP_HEADER_WWW_AUTHENTICATE "WWW-Authenticate" 00280 00285 #define MHD_HTTP_VERSION_1_0 "HTTP/1.0" 00286 #define MHD_HTTP_VERSION_1_1 "HTTP/1.1" 00287 00291 #define MHD_HTTP_METHOD_CONNECT "CONNECT" 00292 #define MHD_HTTP_METHOD_DELETE "DELETE" 00293 #define MHD_HTTP_METHOD_GET "GET" 00294 #define MHD_HTTP_METHOD_HEAD "HEAD" 00295 #define MHD_HTTP_METHOD_OPTIONS "OPTIONS" 00296 #define MHD_HTTP_METHOD_POST "POST" 00297 #define MHD_HTTP_METHOD_PUT "PUT" 00298 #define MHD_HTTP_METHOD_TRACE "TRACE" 00299 00304 #define MHD_HTTP_POST_ENCODING_FORM_URLENCODED "application/x-www-form-urlencoded" 00305 #define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA "multipart/form-data" 00306 00317 enum MHD_FLAG 00318 { 00322 MHD_NO_FLAG = 0, 00323 00329 MHD_USE_DEBUG = 1, 00330 00334 MHD_USE_SSL = 2, 00335 00339 MHD_USE_THREAD_PER_CONNECTION = 4, 00340 00344 MHD_USE_SELECT_INTERNALLY = 8, 00345 00350 MHD_USE_IPv6 = 16, 00351 00361 MHD_USE_PEDANTIC_CHECKS = 32, 00362 00368 MHD_USE_POLL = 64 00369 }; 00370 00375 enum MHD_OPTION 00376 { 00377 00382 MHD_OPTION_END = 0, 00383 00388 MHD_OPTION_CONNECTION_MEMORY_LIMIT = 1, 00389 00394 MHD_OPTION_CONNECTION_LIMIT = 2, 00395 00401 MHD_OPTION_CONNECTION_TIMEOUT = 3, 00402 00415 MHD_OPTION_NOTIFY_COMPLETED = 4, 00416 00427 MHD_OPTION_PER_IP_CONNECTION_LIMIT = 5, 00428 00434 MHD_OPTION_SOCK_ADDR = 6, 00435 00457 MHD_OPTION_URI_LOG_CALLBACK = 7, 00458 00465 MHD_OPTION_HTTPS_MEM_KEY = 8, 00466 00473 MHD_OPTION_HTTPS_MEM_CERT = 9, 00474 00480 MHD_OPTION_HTTPS_CRED_TYPE = 10, 00481 00486 MHD_OPTION_HTTPS_PRIORITIES = 11, 00487 00494 MHD_OPTION_LISTEN_SOCKET = 12, 00495 00508 MHD_OPTION_EXTERNAL_LOGGER = 13, 00509 00518 MHD_OPTION_THREAD_POOL_SIZE = 14, 00519 00539 MHD_OPTION_ARRAY = 15, 00540 00559 MHD_OPTION_UNESCAPE_CALLBACK = 16, 00560 00570 MHD_OPTION_DIGEST_AUTH_RANDOM = 17, 00571 00577 MHD_OPTION_NONCE_NC_SIZE = 18, 00578 00583 MHD_OPTION_THREAD_STACK_SIZE = 19, 00584 00590 MHD_OPTION_HTTPS_MEM_TRUST =20 00591 }; 00592 00593 00597 struct MHD_OptionItem 00598 { 00603 enum MHD_OPTION option; 00604 00610 intptr_t value; 00611 00616 void *ptr_value; 00617 00618 }; 00619 00620 00625 enum MHD_ValueKind 00626 { 00627 00631 MHD_RESPONSE_HEADER_KIND = 0, 00632 00636 MHD_HEADER_KIND = 1, 00637 00642 MHD_COOKIE_KIND = 2, 00643 00652 MHD_POSTDATA_KIND = 4, 00653 00657 MHD_GET_ARGUMENT_KIND = 8, 00658 00662 MHD_FOOTER_KIND = 16 00663 }; 00664 00669 enum MHD_RequestTerminationCode 00670 { 00671 00675 MHD_REQUEST_TERMINATED_COMPLETED_OK = 0, 00676 00682 MHD_REQUEST_TERMINATED_WITH_ERROR = 1, 00683 00689 MHD_REQUEST_TERMINATED_TIMEOUT_REACHED = 2, 00690 00695 MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN = 3 00696 00697 }; 00698 00699 00704 enum MHD_ConnectionInfoType 00705 { 00710 MHD_CONNECTION_INFO_CIPHER_ALGO, 00711 00716 MHD_CONNECTION_INFO_PROTOCOL, 00717 00722 MHD_CONNECTION_INFO_CLIENT_ADDRESS, 00723 00727 MHD_CONNECTION_INFO_GNUTLS_SESSION, 00728 00732 MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT 00733 }; 00734 00739 enum MHD_DaemonInfoType 00740 { 00747 MHD_DAEMON_INFO_KEY_SIZE, 00748 00755 MHD_DAEMON_INFO_MAC_KEY_SIZE, 00756 00761 MHD_DAEMON_INFO_LISTEN_FD 00762 }; 00763 00764 00765 00769 struct MHD_Daemon; 00770 00777 struct MHD_Connection; 00778 00782 struct MHD_Response; 00783 00787 struct MHD_PostProcessor; 00788 00796 typedef void (*MHD_PanicCallback) (void *cls, 00797 const char *file, 00798 unsigned int line, 00799 const char *reason); 00800 00808 typedef int 00809 (*MHD_AcceptPolicyCallback) (void *cls, 00810 const struct sockaddr * addr, 00811 socklen_t addrlen); 00812 00848 typedef int 00849 (*MHD_AccessHandlerCallback) (void *cls, 00850 struct MHD_Connection * connection, 00851 const char *url, 00852 const char *method, 00853 const char *version, 00854 const char *upload_data, 00855 size_t *upload_data_size, 00856 void **con_cls); 00857 00869 typedef void 00870 (*MHD_RequestCompletedCallback) (void *cls, 00871 struct MHD_Connection * connection, 00872 void **con_cls, 00873 enum MHD_RequestTerminationCode toe); 00874 00885 typedef int 00886 (*MHD_KeyValueIterator) (void *cls, 00887 enum MHD_ValueKind kind, 00888 const char *key, const char *value); 00889 00937 typedef ssize_t 00938 (*MHD_ContentReaderCallback) (void *cls, 00939 uint64_t pos, 00940 char *buf, 00941 size_t max); 00942 00949 typedef void (*MHD_ContentReaderFreeCallback) (void *cls); 00950 00970 typedef int 00971 (*MHD_PostDataIterator) (void *cls, 00972 enum MHD_ValueKind kind, 00973 const char *key, 00974 const char *filename, 00975 const char *content_type, 00976 const char *transfer_encoding, 00977 const char *data, uint64_t off, size_t size); 00978 00979 /* **************** Daemon handling functions ***************** */ 00980 00997 struct MHD_Daemon *MHD_start_daemon_va (unsigned int options, 00998 uint16_t port, 00999 MHD_AcceptPolicyCallback apc, 01000 void *apc_cls, 01001 MHD_AccessHandlerCallback dh, 01002 void *dh_cls, va_list ap); 01003 01019 struct MHD_Daemon *MHD_start_daemon (unsigned int flags, 01020 uint16_t port, 01021 MHD_AcceptPolicyCallback apc, 01022 void *apc_cls, 01023 MHD_AccessHandlerCallback dh, 01024 void *dh_cls, ...); 01025 01031 void MHD_stop_daemon (struct MHD_Daemon *daemon); 01032 01033 01047 int 01048 MHD_get_fdset (struct MHD_Daemon *daemon, 01049 fd_set * read_fd_set, 01050 fd_set * write_fd_set, fd_set * except_fd_set, int *max_fd); 01051 01064 int MHD_get_timeout (struct MHD_Daemon *daemon, 01065 unsigned MHD_LONG_LONG *timeout); 01066 01067 01079 int MHD_run (struct MHD_Daemon *daemon); 01080 01081 01082 /* **************** Connection handling functions ***************** */ 01083 01094 int 01095 MHD_get_connection_values (struct MHD_Connection *connection, 01096 enum MHD_ValueKind kind, 01097 MHD_KeyValueIterator iterator, void *iterator_cls); 01098 01128 int 01129 MHD_set_connection_value (struct MHD_Connection *connection, 01130 enum MHD_ValueKind kind, 01131 const char *key, const char *value); 01132 01148 void MHD_set_panic_func (MHD_PanicCallback cb, void *cls); 01149 01159 const char *MHD_lookup_connection_value (struct MHD_Connection *connection, 01160 enum MHD_ValueKind kind, 01161 const char *key); 01162 01173 int 01174 MHD_queue_response (struct MHD_Connection *connection, 01175 unsigned int status_code, struct MHD_Response *response); 01176 01177 01178 /* **************** Response manipulation functions ***************** */ 01179 01195 struct MHD_Response *MHD_create_response_from_callback (uint64_t size, 01196 size_t block_size, 01197 MHD_ContentReaderCallback 01198 crc, void *crc_cls, 01199 MHD_ContentReaderFreeCallback 01200 crfc); 01201 01202 01203 01217 struct MHD_Response *MHD_create_response_from_data (size_t size, 01218 void *data, 01219 int must_free, 01220 int must_copy); 01221 01222 01227 enum MHD_ResponseMemoryMode { 01228 01234 MHD_RESPMEM_PERSISTENT, 01235 01241 MHD_RESPMEM_MUST_FREE, 01242 01249 MHD_RESPMEM_MUST_COPY 01250 01251 }; 01252 01253 01263 struct MHD_Response * 01264 MHD_create_response_from_buffer (size_t size, 01265 void *buffer, 01266 enum MHD_ResponseMemoryMode mode); 01267 01268 01278 struct MHD_Response *MHD_create_response_from_fd (size_t size, 01279 int fd); 01280 01281 01291 struct MHD_Response *MHD_create_response_from_fd_at_offset (size_t size, 01292 int fd, 01293 off_t offset); 01294 01295 01304 void MHD_destroy_response (struct MHD_Response *response); 01305 01315 int 01316 MHD_add_response_header (struct MHD_Response *response, 01317 const char *header, const char *content); 01318 01319 01328 int 01329 MHD_add_response_footer (struct MHD_Response *response, 01330 const char *footer, const char *content); 01331 01332 01341 int 01342 MHD_del_response_header (struct MHD_Response *response, 01343 const char *header, const char *content); 01344 01354 int 01355 MHD_get_response_headers (struct MHD_Response *response, 01356 MHD_KeyValueIterator iterator, void *iterator_cls); 01357 01358 01366 const char *MHD_get_response_header (struct MHD_Response *response, 01367 const char *key); 01368 01369 01370 /* ********************** PostProcessor functions ********************** */ 01371 01395 struct MHD_PostProcessor *MHD_create_post_processor (struct MHD_Connection 01396 *connection, 01397 size_t buffer_size, 01398 MHD_PostDataIterator 01399 iter, void *cls); 01400 01415 int 01416 MHD_post_process (struct MHD_PostProcessor *pp, 01417 const char *post_data, size_t post_data_len); 01418 01428 int MHD_destroy_post_processor (struct MHD_PostProcessor *pp); 01429 01430 01431 /* ********************* Digest Authentication functions *************** */ 01432 01433 01438 #define MHD_INVALID_NONCE -1 01439 01440 01448 char * 01449 MHD_digest_auth_get_username (struct MHD_Connection *connection); 01450 01451 01464 int 01465 MHD_digest_auth_check (struct MHD_Connection *connection, 01466 const char *realm, 01467 const char *username, 01468 const char *password, 01469 unsigned int nonce_timeout); 01470 01471 01485 int 01486 MHD_queue_auth_fail_response (struct MHD_Connection *connection, 01487 const char *realm, 01488 const char *opaque, 01489 struct MHD_Response *response, 01490 int signal_stale); 01491 01492 01501 char * 01502 MHD_basic_auth_get_username_password(struct MHD_Connection *connection, 01503 char** password); 01504 01512 int 01513 MHD_queue_basic_auth_fail_response(struct MHD_Connection *connection, 01514 const char *realm, 01515 struct MHD_Response *response); 01516 01517 /* ********************** generic query functions ********************** */ 01518 01522 union MHD_ConnectionInfo 01523 { 01524 01528 int /* enum gnutls_cipher_algorithm */ cipher_algorithm; 01529 01533 int /* enum gnutls_protocol */ protocol; 01534 01538 void * /* gnutls_session_t */ tls_session; 01539 01543 void * /* gnutls_x509_crt_t */ client_cert; 01544 01548 struct sockaddr_in * client_addr; 01549 }; 01550 01560 const union MHD_ConnectionInfo *MHD_get_connection_info (struct MHD_Connection 01561 *connection, 01562 enum 01563 MHD_ConnectionInfoType 01564 infoType, ...); 01565 01566 01570 union MHD_DaemonInfo 01571 { 01575 size_t key_size; 01576 01580 size_t mac_key_size; 01581 01585 int listen_fd; 01586 }; 01587 01598 const union MHD_DaemonInfo *MHD_get_daemon_info (struct MHD_Daemon *daemon, 01599 enum MHD_DaemonInfoType 01600 infoType, ...); 01601 01607 const char* MHD_get_version(void); 01608 01609 #if 0 /* keep Emacsens' auto-indent happy */ 01610 { 01611 #endif 01612 #ifdef __cplusplus 01613 } 01614 #endif 01615 01616 #endif