00001 00002 00003 00031 /********************************************************************* 00032 * Software License Agreement 00033 * 00034 * Copyright (C) 2002-2008 Microchip Technology Inc. All rights 00035 * reserved. 00036 * 00037 * Microchip licenses to you the right to use, modify, copy, and 00038 * distribute: 00039 * (i) the Software when embedded on a Microchip microcontroller or 00040 * digital signal controller product ("Device") which is 00041 * integrated into Licensee's product; or 00042 * (ii) ONLY the Software driver source files ENC28J60.c and 00043 * ENC28J60.h ported to a non-Microchip device used in 00044 * conjunction with a Microchip ethernet controller for the 00045 * sole purpose of interfacing with the ethernet controller. 00046 * 00047 * You should refer to the license agreement accompanying this 00048 * Software for additional information regarding your rights and 00049 * obligations. 00050 * 00051 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT 00052 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT 00053 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A 00054 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 00055 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR 00056 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF 00057 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS 00058 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE 00059 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER 00060 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT 00061 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE. 00062 * 00063 * 00064 * Author Date Comment 00065 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00066 * Nilesh Rajbharti 8/14/01 Original 00067 * Elliott Wood 6/4/07 Complete rewrite (known as HTTP2) 00068 ********************************************************************/ 00069 00070 #ifndef __HTTP2_H 00071 #define __HTTP2_H 00072 00073 #include "TCPIP.h" 00074 00075 #if defined(STACK_USE_HTTP2_SERVER) 00076 00077 /**************************************************************************** 00078 Section: 00079 Server Configuration Settings 00080 ***************************************************************************/ 00081 00082 #define HTTP_PORT (80u) // Listening port for HTTP server 00083 #define HTTPS_PORT (443u) // Listening port for HTTPS server (when SSL enabled) 00084 #define HTTP_MAX_DATA_LEN (100u) // Max bytes to store get and cookie args 00085 // MODIFIX: Increased define from 16 to 256. 00086 #define HTTP_MIN_CALLBACK_FREE (256u) // Min bytes free in TX FIFO before callbacks execute 00087 #define HTTP_CACHE_LEN ("600") // Max lifetime (sec) of static responses as string 00088 #define HTTP_TIMEOUT (45u) // Max time (sec) to await more data before 00089 00090 // Authentication requires Base64 decoding 00091 #if defined(HTTP_USE_AUTHENTICATION) 00092 #ifndef STACK_USE_BASE64_DECODE 00093 #define STACK_USE_BASE64_DECODE 00094 #endif 00095 #endif 00096 00097 /**************************************************************************** 00098 Section: 00099 Commands and Server Responses 00100 ***************************************************************************/ 00101 00102 //Supported Commands and Server Response Codes 00103 typedef enum 00104 { 00105 HTTP_GET = 0u, // GET command is being processed 00106 HTTP_POST, // POST command is being processed 00107 HTTP_UNAUTHORIZED, // 401 Unauthorized will be returned 00108 HTTP_NOT_FOUND, // 404 Not Found will be returned 00109 HTTP_OVERFLOW, // 414 Request-URI Too Long will be returned 00110 HTTP_INTERNAL_SERVER_ERROR, // 500 Internal Server Error will be returned 00111 HTTP_NOT_IMPLEMENTED, // 501 Not Implemented (not a GET or POST command) 00112 #if defined(HTTP_MPFS_UPLOAD) 00113 HTTP_MPFS_FORM, // Show the MPFS Upload form 00114 HTTP_MPFS_UP, // An MPFS Upload is being processed 00115 HTTP_MPFS_OK, // An MPFS Upload was successful 00116 HTTP_MPFS_ERROR, // An MPFS Upload was not a valid image 00117 #endif 00118 HTTP_REDIRECT, // 302 Redirect will be returned 00119 HTTP_SSL_REQUIRED // 403 Forbidden is returned, indicating SSL is required 00120 } HTTP_STATUS; 00121 00122 /**************************************************************************** 00123 Section: 00124 HTTP State Definitions 00125 ***************************************************************************/ 00126 00127 // Basic HTTP Connection State Machine 00128 typedef enum 00129 { 00130 SM_HTTP_IDLE = 0u, // Socket is idle 00131 SM_HTTP_PARSE_REQUEST, // Parses the first line for a file name and GET args 00132 SM_HTTP_PARSE_HEADERS, // Reads and parses headers one at a time 00133 SM_HTTP_AUTHENTICATE, // Validates the current authorization state 00134 SM_HTTP_PROCESS_GET, // Invokes user callback for GET args or cookies 00135 SM_HTTP_PROCESS_POST, // Invokes user callback for POSTed data 00136 SM_HTTP_PROCESS_REQUEST, // Begins the process of returning data 00137 SM_HTTP_SERVE_HEADERS, // Sends any required headers for the response 00138 SM_HTTP_SERVE_COOKIES, // Adds any cookies to the response 00139 SM_HTTP_SERVE_BODY, // Serves the actual content 00140 SM_HTTP_SEND_FROM_CALLBACK, // Invokes a dynamic variable callback 00141 SM_HTTP_DISCONNECT // Disconnects the server and closes all files 00142 } SM_HTTP2; 00143 00144 // Result states for execution callbacks 00145 typedef enum 00146 { 00147 HTTP_IO_DONE = 0u, // Finished with procedure 00148 HTTP_IO_NEED_DATA, // More data needed to continue, call again later 00149 HTTP_IO_WAITING // Waiting for asynchronous process to complete, call again later 00150 } HTTP_IO_RESULT; 00151 00152 // Result states for HTTPPostReadName and HTTPPostReadValue 00153 typedef enum 00154 { 00155 HTTP_READ_OK = 0u, // Read was successful 00156 HTTP_READ_TRUNCATED, // Buffer overflow prevented by truncating value 00157 HTTP_READ_INCOMPLETE // Entire object is not yet in the buffer. Try again later. 00158 } HTTP_READ_STATUS; 00159 00160 // File type definitions 00161 typedef enum 00162 { 00163 HTTP_TXT = 0u, // File is a text document 00164 HTTP_HTM, // File is HTML (extension .htm) 00165 HTTP_HTML, // File is HTML (extension .html) 00166 HTTP_CGI, // File is HTML (extension .cgi) 00167 HTTP_XML, // File is XML (extension .xml) 00168 HTTP_CSS, // File is stylesheet (extension .css) 00169 HTTP_GIF, // File is GIF image (extension .gif) 00170 HTTP_PNG, // File is PNG image (extension .png) 00171 HTTP_JPG, // File is JPG image (extension .jpg) 00172 HTTP_JAVA, // File is java (extension .java) 00173 HTTP_WAV, // File is audio (extension .wav) 00174 HTTP_UNKNOWN // File type is unknown 00175 } HTTP_FILE_TYPE; 00176 00177 // HTTP Connection Struct 00178 // Stores partial state data for each connection 00179 // Meant for storage in fast access RAM 00180 typedef struct 00181 { 00182 SM_HTTP2 sm; // Current connection state 00183 TCP_SOCKET socket; // Socket being served 00184 } HTTP_STUB; 00185 00186 #define sktHTTP httpStubs[curHTTPID].socket // Access the current socket 00187 #define smHTTP httpStubs[curHTTPID].sm // Access the current state machine 00188 00189 // Stores extended state data for each connection 00190 typedef struct 00191 { 00192 DWORD byteCount; // How many bytes have been read so far 00193 DWORD nextCallback; // Byte index of the next callback 00194 DWORD callbackID; // Callback ID to execute, also used as watchdog timer 00195 DWORD callbackPos; // Callback position indicator 00196 BYTE *ptrData; // Points to first free byte in data 00197 BYTE *ptrRead; // Points to current read location 00198 MPFS_HANDLE file; // File pointer for the file being served 00199 MPFS_HANDLE offsets; // File pointer for any offset info being used 00200 BYTE hasArgs; // True if there were get or cookie arguments 00201 BYTE isAuthorized; // 0x00-0x79 on fail, 0x80-0xff on pass 00202 HTTP_STATUS httpStatus; // Request method/status 00203 HTTP_FILE_TYPE fileType; // File type to return with Content-Type 00204 BYTE data[HTTP_MAX_DATA_LEN]; // General purpose data buffer 00205 #if defined(HTTP_USE_POST) 00206 BYTE smPost; // POST state machine variable 00207 #endif 00208 } HTTP_CONN; 00209 00210 #define RESERVED_HTTP_MEMORY ( (DWORD)MAX_HTTP_CONNECTIONS * (DWORD)sizeof(HTTP_CONN)) 00211 00212 /**************************************************************************** 00213 Section: 00214 Global HTTP Variables 00215 ***************************************************************************/ 00216 00217 extern HTTP_CONN curHTTP; 00218 extern HTTP_STUB httpStubs[MAX_HTTP_CONNECTIONS]; 00219 extern BYTE curHTTPID; 00220 00221 /**************************************************************************** 00222 Section: 00223 Function Prototypes 00224 ***************************************************************************/ 00225 00226 void HTTPInit(void); 00227 void HTTPServer(void); 00228 BYTE* HTTPURLDecode(BYTE* cData); 00229 BYTE* HTTPGetArg(BYTE* cData, BYTE* cArg); 00230 void HTTPIncFile(ROM BYTE* cFile); 00231 BOOL HTTPCheckForIdle(void); // MODIFIX: Added this prototype. 00232 00233 #if defined(__18CXX) 00234 BYTE* HTTPGetROMArg(BYTE* cData, ROM BYTE* cArg); 00235 #else 00236 // Non-ROM variant for C30 / C32 00237 #define HTTPGetROMArg(a,b) HTTPGetArg(a,(BYTE*)b) 00238 #endif 00239 00240 #if defined(HTTP_USE_POST) 00241 HTTP_READ_STATUS HTTPReadPostName(BYTE* cData, WORD wLen); 00242 HTTP_READ_STATUS HTTPReadPostValue(BYTE* cData, WORD wLen); 00243 #endif 00244 00245 /***************************************************************************** 00246 Function: 00247 HTTP_READ_STATUS HTTPReadPostPair(BYTE* cData, WORD wLen) 00248 00249 Summary: 00250 Reads a name and value pair from a URL encoded string in the TCP buffer. 00251 00252 Description: 00253 Reads a name and value pair from a URL encoded string in the TCP buffer. 00254 This function is meant to be called from an HTTPExecutePost callback to 00255 facilitate easier parsing of incoming data. This function also prevents 00256 buffer overflows by forcing the programmer to indicate how many bytes are 00257 expected. At least 2 extra bytes are needed in cData over the maximum 00258 length of data expected to be read. 00259 00260 This function will read until the next '&' character, which indicates the 00261 end of a value parameter. It assumes that the front of the buffer is 00262 the beginning of the name paramter to be read. 00263 00264 This function properly updates curHTTP.byteCount by decrementing it 00265 by the number of bytes read. It also removes the delimiting '&' from 00266 the buffer. 00267 00268 Once complete, two strings will exist in the cData buffer. The first is 00269 the parameter name that was read, while the second is the associated 00270 value. 00271 00272 Precondition: 00273 Front of TCP buffer is the beginning of a name parameter, and the rest of 00274 the TCP buffer contains a URL-encoded string with a name parameter 00275 terminated by a '=' character and a value parameter terminated by a '&'. 00276 00277 Parameters: 00278 cData - where to store the name and value strings once they are read 00279 wLen - how many bytes can be written to cData 00280 00281 Return Values: 00282 HTTP_READ_OK - name and value were successfully read 00283 HTTP_READ_TRUNCTATED - entire name and value could not fit in the buffer, 00284 so input was truncated and data has been lost 00285 HTTP_READ_INCOMPLETE - entire name and value was not yet in the buffer, 00286 so call this function again later to retrieve 00287 00288 Remarks: 00289 This function is aliased to HTTPReadPostValue, since they effectively 00290 perform the same task. The name is provided only for completeness. 00291 ***************************************************************************/ 00292 #define HTTPReadPostPair(cData, wLen) HTTPReadPostValue(cData, wLen) 00293 00294 00295 /**************************************************************************** 00296 Section: 00297 User-Implemented Callback Function Prototypes 00298 ***************************************************************************/ 00299 00300 /***************************************************************************** 00301 Function: 00302 HTTP_IO_RESULT HTTPExecuteGet(void) 00303 00304 Summary: 00305 Processes GET form field variables and cookies. 00306 00307 Description: 00308 This function is implemented by the application developer in 00309 CustomHTTPApp.c. Its purpose is to parse the data received from 00310 URL parameters (GET method forms) and cookies and perform any 00311 application-specific tasks in response to these inputs. Any 00312 required authentication has already been validated. 00313 00314 When this function is called, curHTTP.data contains sequential 00315 name/value pairs of strings representing the data received. In this 00316 format, HTTPGetArg and HTTPGetROMArg can be used to search for 00317 specific variables in the input. If data buffer space associated 00318 with this connection is required, curHTTP.data may be overwritten 00319 here once the application is done with the values. Any data placed 00320 there will be available to future callbacks for this connection, 00321 including HTTPExecutePost and any HTTPPrint_varname dynamic 00322 substitutions. 00323 00324 This function may also issue redirections by setting curHTTP.data 00325 to the destination file name or URL, and curHTTP.httpStatus to 00326 HTTP_REDIRECT. 00327 00328 Finally, this function may set cookies. Set curHTTP.data to a series 00329 of name/value string pairs (in the same format in which parameters 00330 arrive) and then set curHTTP.hasArgs equal to the number of cookie 00331 name/value pairs. The cookies will be transmitted to the browser, 00332 and any future requests will have those values available in 00333 curHTTP.data. 00334 00335 Precondition: 00336 None 00337 00338 Parameters: 00339 None 00340 00341 Return Values: 00342 HTTP_IO_DONE - application is done processing 00343 HTTP_IO_NEED_DATA - this value may not be returned because more data 00344 will not become available 00345 HTTP_IO_WAITING - the application is waiting for an asynchronous 00346 process to complete, and this function should be 00347 called again later 00348 00349 Remarks: 00350 This function is only called if variables are received via URL 00351 parameters or Cookie arguments. This function may NOT write to the 00352 TCP buffer. 00353 00354 This function may service multiple HTTP requests simultaneously. 00355 Exercise caution when using global or static variables inside this 00356 routine. Use curHTTP.callbackPos or curHTTP.data for storage associated 00357 with individual requests. 00358 ***************************************************************************/ 00359 HTTP_IO_RESULT HTTPExecuteGet(void); 00360 00361 /***************************************************************************** 00362 Function: 00363 HTTP_IO_RESULT HTTPExecutePost(void) 00364 00365 Summary: 00366 Processes POST form variables and data. 00367 00368 Description: 00369 This function is implemented by the application developer in 00370 CustomHTTPApp.c. Its purpose is to parse the data received from 00371 POST forms and perform any application-specific tasks in response 00372 to these inputs. Any required authentication has already been 00373 validated before this function is called. 00374 00375 When this function is called, POST data will be waiting in the 00376 TCP buffer. curHTTP.byteCount will indicate the number of bytes 00377 remaining to be received before the browser request is complete. 00378 00379 Since data is still in the TCP buffer, the application must call 00380 TCPGet or TCPGetArray in order to retrieve bytes. When this is done, 00381 curHTTP.byteCount MUST be updated to reflect how many bytes now 00382 remain. The functions TCPFind, TCPFindString, TCPFindROMString, 00383 TCPFindArray, and TCPFindROMArray may be helpful to locate data 00384 in the TCP buffer. 00385 00386 In general, data submitted from web forms via POST is URL encoded. 00387 The HTTPURLDecode function can be used to decode this information 00388 back to a standard string if required. If data buffer space 00389 associated with this connection is required, curHTTP.data may be 00390 overwritten here once the application is done with the values. 00391 Any data placed there will be available to future callbacks for 00392 this connection, including HTTPExecutePost and any 00393 HTTPPrint_varname dynamic substitutions. 00394 00395 Whenever a POST form is processed it is recommended to issue a 00396 redirect back to the browser, either to a status page or to 00397 the same form page that was posted. This prevents accidental 00398 duplicate submissions (by clicking refresh or back/forward) and 00399 avoids browser warnings about "resubmitting form data". Redirects 00400 may be issued to the browser by setting curHTTP.data to the 00401 destination file or URL, and curHTTP.httpStatus to HTTP_REDIRECT. 00402 00403 Finally, this function may set cookies. Set curHTTP.data to a series 00404 of name/value string pairs (in the same format in which parameters 00405 arrive) and then set curHTTP.hasArgs equal to the number of cookie 00406 name/value pairs. The cookies will be transmitted to the browser, 00407 and any future requests will have those values available in 00408 curHTTP.data. 00409 00410 Precondition: 00411 None 00412 00413 Parameters: 00414 None 00415 00416 Return Values: 00417 HTTP_IO_DONE - application is done processing 00418 HTTP_IO_NEED_DATA - more data is needed to continue, and this 00419 function should be called again later 00420 HTTP_IO_WAITING - the application is waiting for an asynchronous 00421 process to complete, and this function should 00422 be called again later 00423 00424 Remarks: 00425 This function is only called when the request method is POST, and is 00426 only used when HTTP_USE_POST is defined. This method may NOT write 00427 to the TCP buffer. 00428 00429 This function may service multiple HTTP requests simultaneously. 00430 Exercise caution when using global or static variables inside this 00431 routine. Use curHTTP.callbackPos or curHTTP.data for storage associated 00432 with individual requests. 00433 ***************************************************************************/ 00434 #if defined(HTTP_USE_POST) 00435 HTTP_IO_RESULT HTTPExecutePost(void); 00436 #endif 00437 00438 /***************************************************************************** 00439 Function: 00440 BYTE HTTPNeedsAuth(BYTE* cFile) 00441 00442 Summary: 00443 Determines if a given file name requires authentication 00444 00445 Description: 00446 This function is implemented by the application developer in 00447 CustomHTTPApp.c. Its function is to determine if a file being 00448 requested requires authentication to view. The user name and password, 00449 if supplied, will arrive later with the request headers, and will be 00450 processed at that time. 00451 00452 Return values 0x80 - 0xff indicate that authentication is not required, 00453 while values from 0x00 to 0x79 indicate that a user name and password 00454 are required before proceeding. While most applications will only use a 00455 single value to grant access and another to require authorization, the 00456 range allows multiple "realms" or sets of pages to be protected, with 00457 different credential requirements for each. 00458 00459 The return value of this function is saved as curHTTP.isAuthorized, and 00460 will be available to future callbacks, including HTTPCheckAuth and any 00461 of the HTTPExecuteGet, HTTPExecutePost, or HTTPPrint_varname callbacks. 00462 00463 Precondition: 00464 None 00465 00466 Parameters: 00467 cFile - the name of the file being requested 00468 00469 Return Values: 00470 <= 0x79 - valid authentication is required 00471 >= 0x80 - access is granted for this connection 00472 00473 Remarks: 00474 This function may NOT write to the TCP buffer. 00475 ***************************************************************************/ 00476 #if defined(HTTP_USE_AUTHENTICATION) 00477 BYTE HTTPNeedsAuth(BYTE* cFile); 00478 #endif 00479 00480 /***************************************************************************** 00481 Function: 00482 BYTE HTTPCheckAuth(BYTE* cUser, BYTE* cPass) 00483 00484 Summary: 00485 Performs validation on a specific user name and password. 00486 00487 Description: 00488 This function is implemented by the application developer in 00489 CustomHTTPApp.c. Its function is to determine if the user name and 00490 password supplied by the client are acceptable for this resource. 00491 00492 The value of curHTTP.isAuthorized will be set to the previous return 00493 value of HTTPRequiresAuthorization. This callback function can check 00494 this value to determine if only specific user names or passwords will 00495 be accepted for this resource. 00496 00497 Return values 0x80 - 0xff indicate that the credentials were accepted, 00498 while values from 0x00 to 0x79 indicate that authorization failed. 00499 While most applications will only use a single value to grant access, 00500 flexibility is provided to store multiple values in order to 00501 indicate which user (or user's group) logged in. 00502 00503 The return value of this function is saved as curHTTP.isAuthorized, and 00504 will be available to future callbacks, including any of the 00505 HTTPExecuteGet, HTTPExecutePost, or HTTPPrint_varname callbacks. 00506 00507 Precondition: 00508 None 00509 00510 Parameters: 00511 cUser - the user name supplied by the client 00512 cPass - the password supplied by the client 00513 00514 Return Values: 00515 <= 0x79 - the credentials were rejected 00516 >= 0x80 - access is granted for this connection 00517 00518 Remarks: 00519 This function is only called when an Authorization header is 00520 encountered. 00521 00522 This function may NOT write to the TCP buffer. 00523 ***************************************************************************/ 00524 #if defined(HTTP_USE_AUTHENTICATION) 00525 BYTE HTTPCheckAuth(BYTE* cUser, BYTE* cPass); 00526 #endif 00527 00528 /***************************************************************************** 00529 Function: 00530 void HTTPPrint_varname(void) 00531 void HTTPPrint_varname(WORD wParam1) 00532 void HTTPPrint_varname(WORD wParam1, WORD wParam2, ...) 00533 00534 Summary: 00535 Inserts dynamic content into a web page 00536 00537 Description: 00538 Functions in this style are implemented by the application developer in 00539 CustomHTTPApp.c. These functions generate dynamic content to be 00540 inserted into web pages and other files returned by the HTTP2 server. 00541 00542 Functions of this type are called when a dynamic variable is located 00543 in a web page. (ie, ~varname~ ) The name between the tilde '~' 00544 characters is appended to the base function name. In this example, the 00545 callback would be named HTTPPrint_varname. 00546 00547 The function prototype is located in your project's HTTPPrint.h, which 00548 is automatically generated by the MPFS2 Utility. The prototype will 00549 have WORD parameters included for each parameter passed in the dynamic 00550 variable. For example, the variable "~myArray(2,6)~" will generate the 00551 prototype "void HTTPPrint_varname(WORD, WORD);". 00552 00553 When called, this function should write its output directly to the TCP 00554 socket using any combination of TCPIsPutReady, TCPPut, TCPPutArray, 00555 TCPPutString, TCPPutROMArray, and TCPPutROMString. 00556 00557 Before calling, the HTTP2 server guarantees that at least 00558 HTTP_MIN_CALLBACK_FREE bytes (defaults to 16 bytes) are free in the 00559 output buffer. If the function is writing less than this amount, it 00560 should simply write the data to the socket and return. 00561 00562 In situations where a function needs to write more this amount, it 00563 must manage its output state using curHTTP.callbackPos. This value 00564 will be set to zero before the function is called. If the function is 00565 managing its output state, it must set this to a non-zero value before 00566 returning. Typically this is used to track how many bytes have been 00567 written, or how many remain to be written. If curHTTP.callbackPos is 00568 non-zero, the function will be called again when more buffer space is 00569 available. Once the callback completes, set this value back to zero 00570 to resume normal servicing of the request. 00571 00572 Precondition: 00573 None 00574 00575 Parameters: 00576 wParam1 - first parameter passed in the dynamic variable (if any) 00577 wParam2 - second parameter passed in the dynamic variable (if any) 00578 ... - additional parameters as necessary 00579 00580 Returns: 00581 None 00582 00583 Remarks: 00584 This function may service multiple HTTP requests simultaneously, 00585 especially when managing its output state. Exercise caution when using 00586 global or static variables inside this routine. Use curHTTP.callbackPos 00587 or curHTTP.data for storage associated with individual requests. 00588 ***************************************************************************/ 00589 #if defined(DOCUMENTATION_ONLY) 00590 void HTTPPrint_varname(WORD wParam1, WORD wParam2, ...); 00591 #endif 00592 00593 00594 #endif 00595 #endif
1.5.5