Webserver+3d print
cyclone_tcp/http/ssi.c@0:8918a71cdbe9, 2017-02-04 (annotated)
- Committer:
- Sergunb
- Date:
- Sat Feb 04 18:15:49 2017 +0000
- Revision:
- 0:8918a71cdbe9
nothing else
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Sergunb | 0:8918a71cdbe9 | 1 | /** |
Sergunb | 0:8918a71cdbe9 | 2 | * @file ssi.c |
Sergunb | 0:8918a71cdbe9 | 3 | * @brief SSI (Server Side Includes) |
Sergunb | 0:8918a71cdbe9 | 4 | * |
Sergunb | 0:8918a71cdbe9 | 5 | * @section License |
Sergunb | 0:8918a71cdbe9 | 6 | * |
Sergunb | 0:8918a71cdbe9 | 7 | * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. |
Sergunb | 0:8918a71cdbe9 | 8 | * |
Sergunb | 0:8918a71cdbe9 | 9 | * This file is part of CycloneTCP Open. |
Sergunb | 0:8918a71cdbe9 | 10 | * |
Sergunb | 0:8918a71cdbe9 | 11 | * This program is free software; you can redistribute it and/or |
Sergunb | 0:8918a71cdbe9 | 12 | * modify it under the terms of the GNU General Public License |
Sergunb | 0:8918a71cdbe9 | 13 | * as published by the Free Software Foundation; either version 2 |
Sergunb | 0:8918a71cdbe9 | 14 | * of the License, or (at your option) any later version. |
Sergunb | 0:8918a71cdbe9 | 15 | * |
Sergunb | 0:8918a71cdbe9 | 16 | * This program is distributed in the hope that it will be useful, |
Sergunb | 0:8918a71cdbe9 | 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
Sergunb | 0:8918a71cdbe9 | 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
Sergunb | 0:8918a71cdbe9 | 19 | * GNU General Public License for more details. |
Sergunb | 0:8918a71cdbe9 | 20 | * |
Sergunb | 0:8918a71cdbe9 | 21 | * You should have received a copy of the GNU General Public License |
Sergunb | 0:8918a71cdbe9 | 22 | * along with this program; if not, write to the Free Software Foundation, |
Sergunb | 0:8918a71cdbe9 | 23 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
Sergunb | 0:8918a71cdbe9 | 24 | * |
Sergunb | 0:8918a71cdbe9 | 25 | * @section Description |
Sergunb | 0:8918a71cdbe9 | 26 | * |
Sergunb | 0:8918a71cdbe9 | 27 | * Server Side Includes (SSI) is a simple interpreted server-side scripting |
Sergunb | 0:8918a71cdbe9 | 28 | * language used to generate dynamic content to web pages |
Sergunb | 0:8918a71cdbe9 | 29 | * |
Sergunb | 0:8918a71cdbe9 | 30 | * @author Oryx Embedded SARL (www.oryx-embedded.com) |
Sergunb | 0:8918a71cdbe9 | 31 | * @version 1.7.6 |
Sergunb | 0:8918a71cdbe9 | 32 | **/ |
Sergunb | 0:8918a71cdbe9 | 33 | |
Sergunb | 0:8918a71cdbe9 | 34 | //Switch to the appropriate trace level |
Sergunb | 0:8918a71cdbe9 | 35 | #define TRACE_LEVEL HTTP_TRACE_LEVEL |
Sergunb | 0:8918a71cdbe9 | 36 | |
Sergunb | 0:8918a71cdbe9 | 37 | //Dependencies |
Sergunb | 0:8918a71cdbe9 | 38 | #include "core/net.h" |
Sergunb | 0:8918a71cdbe9 | 39 | #include "http/http_server.h" |
Sergunb | 0:8918a71cdbe9 | 40 | #include "http/http_server_misc.h" |
Sergunb | 0:8918a71cdbe9 | 41 | #include "http/mime.h" |
Sergunb | 0:8918a71cdbe9 | 42 | #include "http/ssi.h" |
Sergunb | 0:8918a71cdbe9 | 43 | #include "str.h" |
Sergunb | 0:8918a71cdbe9 | 44 | #include "debug.h" |
Sergunb | 0:8918a71cdbe9 | 45 | |
Sergunb | 0:8918a71cdbe9 | 46 | //File system support? |
Sergunb | 0:8918a71cdbe9 | 47 | #if (HTTP_SERVER_FS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 48 | #include "fs_port.h" |
Sergunb | 0:8918a71cdbe9 | 49 | #else |
Sergunb | 0:8918a71cdbe9 | 50 | #include "resource_manager.h" |
Sergunb | 0:8918a71cdbe9 | 51 | #endif |
Sergunb | 0:8918a71cdbe9 | 52 | |
Sergunb | 0:8918a71cdbe9 | 53 | //Check TCP/IP stack configuration |
Sergunb | 0:8918a71cdbe9 | 54 | #if (HTTP_SERVER_SUPPORT == ENABLED && HTTP_SERVER_SSI_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 55 | |
Sergunb | 0:8918a71cdbe9 | 56 | |
Sergunb | 0:8918a71cdbe9 | 57 | /** |
Sergunb | 0:8918a71cdbe9 | 58 | * @brief Execute SSI script |
Sergunb | 0:8918a71cdbe9 | 59 | * @param[in] connection Structure representing an HTTP connection |
Sergunb | 0:8918a71cdbe9 | 60 | * @param[in] uri NULL-terminated string containing the file to process |
Sergunb | 0:8918a71cdbe9 | 61 | * @param[in] level Current level of recursion |
Sergunb | 0:8918a71cdbe9 | 62 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 63 | **/ |
Sergunb | 0:8918a71cdbe9 | 64 | |
Sergunb | 0:8918a71cdbe9 | 65 | error_t ssiExecuteScript(HttpConnection *connection, const char_t *uri, uint_t level) |
Sergunb | 0:8918a71cdbe9 | 66 | { |
Sergunb | 0:8918a71cdbe9 | 67 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 68 | size_t length; |
Sergunb | 0:8918a71cdbe9 | 69 | |
Sergunb | 0:8918a71cdbe9 | 70 | #if (HTTP_SERVER_FS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 71 | bool_t more; |
Sergunb | 0:8918a71cdbe9 | 72 | uint_t pos; |
Sergunb | 0:8918a71cdbe9 | 73 | uint_t n; |
Sergunb | 0:8918a71cdbe9 | 74 | char_t *buffer; |
Sergunb | 0:8918a71cdbe9 | 75 | FsFile *file; |
Sergunb | 0:8918a71cdbe9 | 76 | #else |
Sergunb | 0:8918a71cdbe9 | 77 | uint_t i; |
Sergunb | 0:8918a71cdbe9 | 78 | uint_t j; |
Sergunb | 0:8918a71cdbe9 | 79 | char_t *data; |
Sergunb | 0:8918a71cdbe9 | 80 | #endif |
Sergunb | 0:8918a71cdbe9 | 81 | |
Sergunb | 0:8918a71cdbe9 | 82 | //Recursion limit exceeded? |
Sergunb | 0:8918a71cdbe9 | 83 | if(level >= HTTP_SERVER_SSI_MAX_RECURSION) |
Sergunb | 0:8918a71cdbe9 | 84 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 85 | |
Sergunb | 0:8918a71cdbe9 | 86 | //Retrieve the full pathname |
Sergunb | 0:8918a71cdbe9 | 87 | httpGetAbsolutePath(connection, uri, |
Sergunb | 0:8918a71cdbe9 | 88 | connection->buffer, HTTP_SERVER_BUFFER_SIZE); |
Sergunb | 0:8918a71cdbe9 | 89 | |
Sergunb | 0:8918a71cdbe9 | 90 | #if (HTTP_SERVER_FS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 91 | //Open the file for reading |
Sergunb | 0:8918a71cdbe9 | 92 | file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ); |
Sergunb | 0:8918a71cdbe9 | 93 | //Failed to open the file? |
Sergunb | 0:8918a71cdbe9 | 94 | if(file == NULL) |
Sergunb | 0:8918a71cdbe9 | 95 | return ERROR_NOT_FOUND; |
Sergunb | 0:8918a71cdbe9 | 96 | |
Sergunb | 0:8918a71cdbe9 | 97 | //Allocate a memory buffer |
Sergunb | 0:8918a71cdbe9 | 98 | buffer = osAllocMem(HTTP_SERVER_BUFFER_SIZE); |
Sergunb | 0:8918a71cdbe9 | 99 | //Failed to allocate memory? |
Sergunb | 0:8918a71cdbe9 | 100 | if(buffer == NULL) |
Sergunb | 0:8918a71cdbe9 | 101 | { |
Sergunb | 0:8918a71cdbe9 | 102 | //Close the file |
Sergunb | 0:8918a71cdbe9 | 103 | fsCloseFile(file); |
Sergunb | 0:8918a71cdbe9 | 104 | //Report an error |
Sergunb | 0:8918a71cdbe9 | 105 | return ERROR_OUT_OF_MEMORY; |
Sergunb | 0:8918a71cdbe9 | 106 | } |
Sergunb | 0:8918a71cdbe9 | 107 | #else |
Sergunb | 0:8918a71cdbe9 | 108 | //Get the resource data associated with the URI |
Sergunb | 0:8918a71cdbe9 | 109 | error = resGetData(connection->buffer, (uint8_t **) &data, &length); |
Sergunb | 0:8918a71cdbe9 | 110 | //The specified URI cannot be found? |
Sergunb | 0:8918a71cdbe9 | 111 | if(error) |
Sergunb | 0:8918a71cdbe9 | 112 | return error; |
Sergunb | 0:8918a71cdbe9 | 113 | #endif |
Sergunb | 0:8918a71cdbe9 | 114 | |
Sergunb | 0:8918a71cdbe9 | 115 | //Send the HTTP response header before executing the script |
Sergunb | 0:8918a71cdbe9 | 116 | if(!level) |
Sergunb | 0:8918a71cdbe9 | 117 | { |
Sergunb | 0:8918a71cdbe9 | 118 | //Format HTTP response header |
Sergunb | 0:8918a71cdbe9 | 119 | connection->response.statusCode = 200; |
Sergunb | 0:8918a71cdbe9 | 120 | connection->response.contentType = mimeGetType(uri); |
Sergunb | 0:8918a71cdbe9 | 121 | connection->response.chunkedEncoding = TRUE; |
Sergunb | 0:8918a71cdbe9 | 122 | |
Sergunb | 0:8918a71cdbe9 | 123 | //Send the header to the client |
Sergunb | 0:8918a71cdbe9 | 124 | error = httpWriteHeader(connection); |
Sergunb | 0:8918a71cdbe9 | 125 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 126 | if(error) |
Sergunb | 0:8918a71cdbe9 | 127 | { |
Sergunb | 0:8918a71cdbe9 | 128 | #if (HTTP_SERVER_FS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 129 | //Close the file |
Sergunb | 0:8918a71cdbe9 | 130 | fsCloseFile(file); |
Sergunb | 0:8918a71cdbe9 | 131 | //Release memory buffer |
Sergunb | 0:8918a71cdbe9 | 132 | osFreeMem(buffer); |
Sergunb | 0:8918a71cdbe9 | 133 | #endif |
Sergunb | 0:8918a71cdbe9 | 134 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 135 | return error; |
Sergunb | 0:8918a71cdbe9 | 136 | } |
Sergunb | 0:8918a71cdbe9 | 137 | } |
Sergunb | 0:8918a71cdbe9 | 138 | |
Sergunb | 0:8918a71cdbe9 | 139 | #if (HTTP_SERVER_FS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 140 | //Point to the beginning of the buffer |
Sergunb | 0:8918a71cdbe9 | 141 | pos = 0; |
Sergunb | 0:8918a71cdbe9 | 142 | length = 0; |
Sergunb | 0:8918a71cdbe9 | 143 | |
Sergunb | 0:8918a71cdbe9 | 144 | //This flag indicates whether data should be read |
Sergunb | 0:8918a71cdbe9 | 145 | more = TRUE; |
Sergunb | 0:8918a71cdbe9 | 146 | |
Sergunb | 0:8918a71cdbe9 | 147 | //Parse the specified file |
Sergunb | 0:8918a71cdbe9 | 148 | while(1) |
Sergunb | 0:8918a71cdbe9 | 149 | { |
Sergunb | 0:8918a71cdbe9 | 150 | //Read more data if needed |
Sergunb | 0:8918a71cdbe9 | 151 | if(more) |
Sergunb | 0:8918a71cdbe9 | 152 | { |
Sergunb | 0:8918a71cdbe9 | 153 | //Check whether the current position is aligned on 32-bit boundaries |
Sergunb | 0:8918a71cdbe9 | 154 | n = 4 - ((pos + length) % 4); |
Sergunb | 0:8918a71cdbe9 | 155 | |
Sergunb | 0:8918a71cdbe9 | 156 | //Maintain proper alignment |
Sergunb | 0:8918a71cdbe9 | 157 | if(n != 4) |
Sergunb | 0:8918a71cdbe9 | 158 | { |
Sergunb | 0:8918a71cdbe9 | 159 | memmove(buffer + pos + n, buffer + pos, length); |
Sergunb | 0:8918a71cdbe9 | 160 | pos += n; |
Sergunb | 0:8918a71cdbe9 | 161 | } |
Sergunb | 0:8918a71cdbe9 | 162 | |
Sergunb | 0:8918a71cdbe9 | 163 | //Read data from the specified file |
Sergunb | 0:8918a71cdbe9 | 164 | error = fsReadFile(file, buffer + pos + length, |
Sergunb | 0:8918a71cdbe9 | 165 | HTTP_SERVER_BUFFER_SIZE - (pos + length), &n); |
Sergunb | 0:8918a71cdbe9 | 166 | |
Sergunb | 0:8918a71cdbe9 | 167 | //End of input stream? |
Sergunb | 0:8918a71cdbe9 | 168 | if(error) |
Sergunb | 0:8918a71cdbe9 | 169 | { |
Sergunb | 0:8918a71cdbe9 | 170 | //Purge data buffer |
Sergunb | 0:8918a71cdbe9 | 171 | error = httpWriteStream(connection, buffer + pos, length); |
Sergunb | 0:8918a71cdbe9 | 172 | //Exit immediately |
Sergunb | 0:8918a71cdbe9 | 173 | break; |
Sergunb | 0:8918a71cdbe9 | 174 | } |
Sergunb | 0:8918a71cdbe9 | 175 | |
Sergunb | 0:8918a71cdbe9 | 176 | //Adjust the length of the buffer |
Sergunb | 0:8918a71cdbe9 | 177 | length += n; |
Sergunb | 0:8918a71cdbe9 | 178 | //Clear flag |
Sergunb | 0:8918a71cdbe9 | 179 | more = FALSE; |
Sergunb | 0:8918a71cdbe9 | 180 | } |
Sergunb | 0:8918a71cdbe9 | 181 | |
Sergunb | 0:8918a71cdbe9 | 182 | //Search for any SSI tags |
Sergunb | 0:8918a71cdbe9 | 183 | error = ssiSearchTag(buffer + pos, length, "<!--#", 5, &n); |
Sergunb | 0:8918a71cdbe9 | 184 | |
Sergunb | 0:8918a71cdbe9 | 185 | //Full match? |
Sergunb | 0:8918a71cdbe9 | 186 | if(error == NO_ERROR) |
Sergunb | 0:8918a71cdbe9 | 187 | { |
Sergunb | 0:8918a71cdbe9 | 188 | //Send the part of the file that precedes the tag |
Sergunb | 0:8918a71cdbe9 | 189 | error = httpWriteStream(connection, buffer + pos, n); |
Sergunb | 0:8918a71cdbe9 | 190 | //Failed to send data? |
Sergunb | 0:8918a71cdbe9 | 191 | if(error) |
Sergunb | 0:8918a71cdbe9 | 192 | break; |
Sergunb | 0:8918a71cdbe9 | 193 | |
Sergunb | 0:8918a71cdbe9 | 194 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 195 | pos += n; |
Sergunb | 0:8918a71cdbe9 | 196 | length -= n; |
Sergunb | 0:8918a71cdbe9 | 197 | |
Sergunb | 0:8918a71cdbe9 | 198 | //Search for the comment terminator |
Sergunb | 0:8918a71cdbe9 | 199 | error = ssiSearchTag(buffer + pos + 5, length - 5, "-->", 3, &n); |
Sergunb | 0:8918a71cdbe9 | 200 | |
Sergunb | 0:8918a71cdbe9 | 201 | //Full match? |
Sergunb | 0:8918a71cdbe9 | 202 | if(error == NO_ERROR) |
Sergunb | 0:8918a71cdbe9 | 203 | { |
Sergunb | 0:8918a71cdbe9 | 204 | //Advance data pointer over the opening identifier |
Sergunb | 0:8918a71cdbe9 | 205 | pos += 5; |
Sergunb | 0:8918a71cdbe9 | 206 | length -= 5; |
Sergunb | 0:8918a71cdbe9 | 207 | |
Sergunb | 0:8918a71cdbe9 | 208 | //Process SSI directive |
Sergunb | 0:8918a71cdbe9 | 209 | error = ssiProcessCommand(connection, buffer + pos, n, uri, level); |
Sergunb | 0:8918a71cdbe9 | 210 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 211 | if(error) |
Sergunb | 0:8918a71cdbe9 | 212 | break; |
Sergunb | 0:8918a71cdbe9 | 213 | |
Sergunb | 0:8918a71cdbe9 | 214 | //Advance data pointer over the SSI tag |
Sergunb | 0:8918a71cdbe9 | 215 | pos += n + 3; |
Sergunb | 0:8918a71cdbe9 | 216 | length -= n + 3; |
Sergunb | 0:8918a71cdbe9 | 217 | } |
Sergunb | 0:8918a71cdbe9 | 218 | //No match or partial match? |
Sergunb | 0:8918a71cdbe9 | 219 | else |
Sergunb | 0:8918a71cdbe9 | 220 | { |
Sergunb | 0:8918a71cdbe9 | 221 | if(pos > 0) |
Sergunb | 0:8918a71cdbe9 | 222 | { |
Sergunb | 0:8918a71cdbe9 | 223 | //Move the remaining bytes to the start of the buffer |
Sergunb | 0:8918a71cdbe9 | 224 | memmove(buffer, buffer + pos, length); |
Sergunb | 0:8918a71cdbe9 | 225 | //Rewind to the beginning of the buffer |
Sergunb | 0:8918a71cdbe9 | 226 | pos = 0; |
Sergunb | 0:8918a71cdbe9 | 227 | //More data are needed |
Sergunb | 0:8918a71cdbe9 | 228 | more = TRUE; |
Sergunb | 0:8918a71cdbe9 | 229 | } |
Sergunb | 0:8918a71cdbe9 | 230 | else |
Sergunb | 0:8918a71cdbe9 | 231 | { |
Sergunb | 0:8918a71cdbe9 | 232 | //Send data to the client |
Sergunb | 0:8918a71cdbe9 | 233 | error = httpWriteStream(connection, buffer + pos, length); |
Sergunb | 0:8918a71cdbe9 | 234 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 235 | if(error) |
Sergunb | 0:8918a71cdbe9 | 236 | break; |
Sergunb | 0:8918a71cdbe9 | 237 | |
Sergunb | 0:8918a71cdbe9 | 238 | //Rewind to the beginning of the buffer |
Sergunb | 0:8918a71cdbe9 | 239 | pos = 0; |
Sergunb | 0:8918a71cdbe9 | 240 | length = 0; |
Sergunb | 0:8918a71cdbe9 | 241 | //More data are needed |
Sergunb | 0:8918a71cdbe9 | 242 | more = TRUE; |
Sergunb | 0:8918a71cdbe9 | 243 | } |
Sergunb | 0:8918a71cdbe9 | 244 | } |
Sergunb | 0:8918a71cdbe9 | 245 | } |
Sergunb | 0:8918a71cdbe9 | 246 | //Partial match? |
Sergunb | 0:8918a71cdbe9 | 247 | else if(error == ERROR_PARTIAL_MATCH) |
Sergunb | 0:8918a71cdbe9 | 248 | { |
Sergunb | 0:8918a71cdbe9 | 249 | //Send the part of the file that precedes the tag |
Sergunb | 0:8918a71cdbe9 | 250 | error = httpWriteStream(connection, buffer + pos, n); |
Sergunb | 0:8918a71cdbe9 | 251 | //Failed to send data? |
Sergunb | 0:8918a71cdbe9 | 252 | if(error) |
Sergunb | 0:8918a71cdbe9 | 253 | break; |
Sergunb | 0:8918a71cdbe9 | 254 | |
Sergunb | 0:8918a71cdbe9 | 255 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 256 | pos += n; |
Sergunb | 0:8918a71cdbe9 | 257 | length -= n; |
Sergunb | 0:8918a71cdbe9 | 258 | |
Sergunb | 0:8918a71cdbe9 | 259 | //Move the remaining bytes to the start of the buffer |
Sergunb | 0:8918a71cdbe9 | 260 | memmove(buffer, buffer + pos, length); |
Sergunb | 0:8918a71cdbe9 | 261 | //Rewind to the beginning of the buffer |
Sergunb | 0:8918a71cdbe9 | 262 | pos = 0; |
Sergunb | 0:8918a71cdbe9 | 263 | //More data are needed |
Sergunb | 0:8918a71cdbe9 | 264 | more = TRUE; |
Sergunb | 0:8918a71cdbe9 | 265 | } |
Sergunb | 0:8918a71cdbe9 | 266 | //No match? |
Sergunb | 0:8918a71cdbe9 | 267 | else |
Sergunb | 0:8918a71cdbe9 | 268 | { |
Sergunb | 0:8918a71cdbe9 | 269 | //Send data to the client |
Sergunb | 0:8918a71cdbe9 | 270 | error = httpWriteStream(connection, buffer + pos, length); |
Sergunb | 0:8918a71cdbe9 | 271 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 272 | if(error) |
Sergunb | 0:8918a71cdbe9 | 273 | break; |
Sergunb | 0:8918a71cdbe9 | 274 | |
Sergunb | 0:8918a71cdbe9 | 275 | //Rewind to the beginning of the buffer |
Sergunb | 0:8918a71cdbe9 | 276 | pos = 0; |
Sergunb | 0:8918a71cdbe9 | 277 | length = 0; |
Sergunb | 0:8918a71cdbe9 | 278 | //More data are needed |
Sergunb | 0:8918a71cdbe9 | 279 | more = TRUE; |
Sergunb | 0:8918a71cdbe9 | 280 | } |
Sergunb | 0:8918a71cdbe9 | 281 | } |
Sergunb | 0:8918a71cdbe9 | 282 | |
Sergunb | 0:8918a71cdbe9 | 283 | //Close the file |
Sergunb | 0:8918a71cdbe9 | 284 | fsCloseFile(file); |
Sergunb | 0:8918a71cdbe9 | 285 | //Release memory buffer |
Sergunb | 0:8918a71cdbe9 | 286 | osFreeMem(buffer); |
Sergunb | 0:8918a71cdbe9 | 287 | |
Sergunb | 0:8918a71cdbe9 | 288 | //Properly close the output stream |
Sergunb | 0:8918a71cdbe9 | 289 | if(!level && error == NO_ERROR) |
Sergunb | 0:8918a71cdbe9 | 290 | error = httpCloseStream(connection); |
Sergunb | 0:8918a71cdbe9 | 291 | #else |
Sergunb | 0:8918a71cdbe9 | 292 | //Parse the specified file |
Sergunb | 0:8918a71cdbe9 | 293 | while(length > 0) |
Sergunb | 0:8918a71cdbe9 | 294 | { |
Sergunb | 0:8918a71cdbe9 | 295 | //Search for any SSI tags |
Sergunb | 0:8918a71cdbe9 | 296 | error = ssiSearchTag(data, length, "<!--#", 5, &i); |
Sergunb | 0:8918a71cdbe9 | 297 | |
Sergunb | 0:8918a71cdbe9 | 298 | //Opening identifier found? |
Sergunb | 0:8918a71cdbe9 | 299 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 300 | { |
Sergunb | 0:8918a71cdbe9 | 301 | //Search for the comment terminator |
Sergunb | 0:8918a71cdbe9 | 302 | error = ssiSearchTag(data + i + 5, length - i - 5, "-->", 3, &j); |
Sergunb | 0:8918a71cdbe9 | 303 | } |
Sergunb | 0:8918a71cdbe9 | 304 | |
Sergunb | 0:8918a71cdbe9 | 305 | //Check whether a valid SSI tag has been found? |
Sergunb | 0:8918a71cdbe9 | 306 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 307 | { |
Sergunb | 0:8918a71cdbe9 | 308 | //Send the part of the file that precedes the tag |
Sergunb | 0:8918a71cdbe9 | 309 | error = httpWriteStream(connection, data, i); |
Sergunb | 0:8918a71cdbe9 | 310 | //Failed to send data? |
Sergunb | 0:8918a71cdbe9 | 311 | if(error) |
Sergunb | 0:8918a71cdbe9 | 312 | return error; |
Sergunb | 0:8918a71cdbe9 | 313 | |
Sergunb | 0:8918a71cdbe9 | 314 | //Advance data pointer over the opening identifier |
Sergunb | 0:8918a71cdbe9 | 315 | data += i + 5; |
Sergunb | 0:8918a71cdbe9 | 316 | length -= i + 5; |
Sergunb | 0:8918a71cdbe9 | 317 | |
Sergunb | 0:8918a71cdbe9 | 318 | //Process SSI directive |
Sergunb | 0:8918a71cdbe9 | 319 | error = ssiProcessCommand(connection, data, j, uri, level); |
Sergunb | 0:8918a71cdbe9 | 320 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 321 | if(error) |
Sergunb | 0:8918a71cdbe9 | 322 | return error; |
Sergunb | 0:8918a71cdbe9 | 323 | |
Sergunb | 0:8918a71cdbe9 | 324 | //Advance data pointer over the SSI tag |
Sergunb | 0:8918a71cdbe9 | 325 | data += j + 3; |
Sergunb | 0:8918a71cdbe9 | 326 | length -= j + 3; |
Sergunb | 0:8918a71cdbe9 | 327 | } |
Sergunb | 0:8918a71cdbe9 | 328 | else |
Sergunb | 0:8918a71cdbe9 | 329 | { |
Sergunb | 0:8918a71cdbe9 | 330 | //Send the rest of the file |
Sergunb | 0:8918a71cdbe9 | 331 | error = httpWriteStream(connection, data, length); |
Sergunb | 0:8918a71cdbe9 | 332 | //Failed to send data? |
Sergunb | 0:8918a71cdbe9 | 333 | if(error) |
Sergunb | 0:8918a71cdbe9 | 334 | return error; |
Sergunb | 0:8918a71cdbe9 | 335 | |
Sergunb | 0:8918a71cdbe9 | 336 | //Advance data pointer |
Sergunb | 0:8918a71cdbe9 | 337 | data += length; |
Sergunb | 0:8918a71cdbe9 | 338 | length = 0; |
Sergunb | 0:8918a71cdbe9 | 339 | } |
Sergunb | 0:8918a71cdbe9 | 340 | } |
Sergunb | 0:8918a71cdbe9 | 341 | |
Sergunb | 0:8918a71cdbe9 | 342 | //Properly close the output stream |
Sergunb | 0:8918a71cdbe9 | 343 | if(!level) |
Sergunb | 0:8918a71cdbe9 | 344 | error = httpCloseStream(connection); |
Sergunb | 0:8918a71cdbe9 | 345 | #endif |
Sergunb | 0:8918a71cdbe9 | 346 | |
Sergunb | 0:8918a71cdbe9 | 347 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 348 | return error; |
Sergunb | 0:8918a71cdbe9 | 349 | } |
Sergunb | 0:8918a71cdbe9 | 350 | |
Sergunb | 0:8918a71cdbe9 | 351 | |
Sergunb | 0:8918a71cdbe9 | 352 | /** |
Sergunb | 0:8918a71cdbe9 | 353 | * @brief Process SSI directive |
Sergunb | 0:8918a71cdbe9 | 354 | * @param[in] connection Structure representing an HTTP connection |
Sergunb | 0:8918a71cdbe9 | 355 | * @param[in] tag Pointer to the SSI tag |
Sergunb | 0:8918a71cdbe9 | 356 | * @param[in] length Total length of the SSI tag |
Sergunb | 0:8918a71cdbe9 | 357 | * @param[in] uri NULL-terminated string containing the file being processed |
Sergunb | 0:8918a71cdbe9 | 358 | * @param[in] level Current level of recursion |
Sergunb | 0:8918a71cdbe9 | 359 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 360 | **/ |
Sergunb | 0:8918a71cdbe9 | 361 | |
Sergunb | 0:8918a71cdbe9 | 362 | error_t ssiProcessCommand(HttpConnection *connection, |
Sergunb | 0:8918a71cdbe9 | 363 | const char_t *tag, size_t length, const char_t *uri, uint_t level) |
Sergunb | 0:8918a71cdbe9 | 364 | { |
Sergunb | 0:8918a71cdbe9 | 365 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 366 | |
Sergunb | 0:8918a71cdbe9 | 367 | //Include command found? |
Sergunb | 0:8918a71cdbe9 | 368 | if(length > 7 && !strncasecmp(tag, "include", 7)) |
Sergunb | 0:8918a71cdbe9 | 369 | { |
Sergunb | 0:8918a71cdbe9 | 370 | //Process SSI include directive |
Sergunb | 0:8918a71cdbe9 | 371 | error = ssiProcessIncludeCommand(connection, tag, length, uri, level); |
Sergunb | 0:8918a71cdbe9 | 372 | } |
Sergunb | 0:8918a71cdbe9 | 373 | //Echo command found? |
Sergunb | 0:8918a71cdbe9 | 374 | else if(length > 4 && !strncasecmp(tag, "echo", 4)) |
Sergunb | 0:8918a71cdbe9 | 375 | { |
Sergunb | 0:8918a71cdbe9 | 376 | //Process SSI echo directive |
Sergunb | 0:8918a71cdbe9 | 377 | error = ssiProcessEchoCommand(connection, tag, length); |
Sergunb | 0:8918a71cdbe9 | 378 | } |
Sergunb | 0:8918a71cdbe9 | 379 | //Exec command found? |
Sergunb | 0:8918a71cdbe9 | 380 | else if(length > 4 && !strncasecmp(tag, "exec", 4)) |
Sergunb | 0:8918a71cdbe9 | 381 | { |
Sergunb | 0:8918a71cdbe9 | 382 | //Process SSI exec directive |
Sergunb | 0:8918a71cdbe9 | 383 | error = ssiProcessExecCommand(connection, tag, length); |
Sergunb | 0:8918a71cdbe9 | 384 | } |
Sergunb | 0:8918a71cdbe9 | 385 | //Unknown command? |
Sergunb | 0:8918a71cdbe9 | 386 | else |
Sergunb | 0:8918a71cdbe9 | 387 | { |
Sergunb | 0:8918a71cdbe9 | 388 | //The server is unable to decode the SSI tag |
Sergunb | 0:8918a71cdbe9 | 389 | error = ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 390 | } |
Sergunb | 0:8918a71cdbe9 | 391 | |
Sergunb | 0:8918a71cdbe9 | 392 | //Invalid SSI directive? |
Sergunb | 0:8918a71cdbe9 | 393 | if(error == ERROR_INVALID_TAG) |
Sergunb | 0:8918a71cdbe9 | 394 | { |
Sergunb | 0:8918a71cdbe9 | 395 | //Report a warning to the user |
Sergunb | 0:8918a71cdbe9 | 396 | error = httpWriteStream(connection, "Warning: Invalid SSI Tag", 24); |
Sergunb | 0:8918a71cdbe9 | 397 | } |
Sergunb | 0:8918a71cdbe9 | 398 | |
Sergunb | 0:8918a71cdbe9 | 399 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 400 | return error; |
Sergunb | 0:8918a71cdbe9 | 401 | } |
Sergunb | 0:8918a71cdbe9 | 402 | |
Sergunb | 0:8918a71cdbe9 | 403 | |
Sergunb | 0:8918a71cdbe9 | 404 | /** |
Sergunb | 0:8918a71cdbe9 | 405 | * @brief Process SSI include directive |
Sergunb | 0:8918a71cdbe9 | 406 | * |
Sergunb | 0:8918a71cdbe9 | 407 | * This include directive allows the content of one document to be included |
Sergunb | 0:8918a71cdbe9 | 408 | * in another. The file parameter defines the included file as relative to |
Sergunb | 0:8918a71cdbe9 | 409 | * the document path. The virtual parameter defines the included file as |
Sergunb | 0:8918a71cdbe9 | 410 | * relative to the document root |
Sergunb | 0:8918a71cdbe9 | 411 | * |
Sergunb | 0:8918a71cdbe9 | 412 | * @param[in] connection Structure representing an HTTP connection |
Sergunb | 0:8918a71cdbe9 | 413 | * @param[in] tag Pointer to the SSI tag |
Sergunb | 0:8918a71cdbe9 | 414 | * @param[in] length Total length of the SSI tag |
Sergunb | 0:8918a71cdbe9 | 415 | * @param[in] uri NULL-terminated string containing the file being processed |
Sergunb | 0:8918a71cdbe9 | 416 | * @param[in] level Current level of recursion |
Sergunb | 0:8918a71cdbe9 | 417 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 418 | **/ |
Sergunb | 0:8918a71cdbe9 | 419 | |
Sergunb | 0:8918a71cdbe9 | 420 | error_t ssiProcessIncludeCommand(HttpConnection *connection, |
Sergunb | 0:8918a71cdbe9 | 421 | const char_t *tag, size_t length, const char_t *uri, uint_t level) |
Sergunb | 0:8918a71cdbe9 | 422 | { |
Sergunb | 0:8918a71cdbe9 | 423 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 424 | char_t *separator; |
Sergunb | 0:8918a71cdbe9 | 425 | char_t *attribute; |
Sergunb | 0:8918a71cdbe9 | 426 | char_t *value; |
Sergunb | 0:8918a71cdbe9 | 427 | char_t *path; |
Sergunb | 0:8918a71cdbe9 | 428 | char_t *p; |
Sergunb | 0:8918a71cdbe9 | 429 | |
Sergunb | 0:8918a71cdbe9 | 430 | //Discard invalid SSI directives |
Sergunb | 0:8918a71cdbe9 | 431 | if(length < 7 || length >= HTTP_SERVER_BUFFER_SIZE) |
Sergunb | 0:8918a71cdbe9 | 432 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 433 | |
Sergunb | 0:8918a71cdbe9 | 434 | //Skip the SSI include command (7 bytes) |
Sergunb | 0:8918a71cdbe9 | 435 | memcpy(connection->buffer, tag + 7, length - 7); |
Sergunb | 0:8918a71cdbe9 | 436 | //Ensure the resulting string is NULL-terminated |
Sergunb | 0:8918a71cdbe9 | 437 | connection->buffer[length - 7] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 438 | |
Sergunb | 0:8918a71cdbe9 | 439 | //Check whether a separator is present |
Sergunb | 0:8918a71cdbe9 | 440 | separator = strchr(connection->buffer, '='); |
Sergunb | 0:8918a71cdbe9 | 441 | //Separator not found? |
Sergunb | 0:8918a71cdbe9 | 442 | if(!separator) |
Sergunb | 0:8918a71cdbe9 | 443 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 444 | |
Sergunb | 0:8918a71cdbe9 | 445 | //Split the tag |
Sergunb | 0:8918a71cdbe9 | 446 | *separator = '\0'; |
Sergunb | 0:8918a71cdbe9 | 447 | |
Sergunb | 0:8918a71cdbe9 | 448 | //Get attribute name and value |
Sergunb | 0:8918a71cdbe9 | 449 | attribute = strTrimWhitespace(connection->buffer); |
Sergunb | 0:8918a71cdbe9 | 450 | value = strTrimWhitespace(separator + 1); |
Sergunb | 0:8918a71cdbe9 | 451 | |
Sergunb | 0:8918a71cdbe9 | 452 | //Remove leading simple or double quote |
Sergunb | 0:8918a71cdbe9 | 453 | if(value[0] == '\'' || value[0] == '\"') |
Sergunb | 0:8918a71cdbe9 | 454 | value++; |
Sergunb | 0:8918a71cdbe9 | 455 | |
Sergunb | 0:8918a71cdbe9 | 456 | //Get the length of the attribute value |
Sergunb | 0:8918a71cdbe9 | 457 | length = strlen(value); |
Sergunb | 0:8918a71cdbe9 | 458 | |
Sergunb | 0:8918a71cdbe9 | 459 | //Remove trailing simple or double quote |
Sergunb | 0:8918a71cdbe9 | 460 | if(length > 0) |
Sergunb | 0:8918a71cdbe9 | 461 | { |
Sergunb | 0:8918a71cdbe9 | 462 | if(value[length - 1] == '\'' || value[length - 1] == '\"') |
Sergunb | 0:8918a71cdbe9 | 463 | value[length - 1] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 464 | } |
Sergunb | 0:8918a71cdbe9 | 465 | |
Sergunb | 0:8918a71cdbe9 | 466 | //Check the length of the filename |
Sergunb | 0:8918a71cdbe9 | 467 | if(strlen(value) > HTTP_SERVER_URI_MAX_LEN) |
Sergunb | 0:8918a71cdbe9 | 468 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 469 | |
Sergunb | 0:8918a71cdbe9 | 470 | //The file parameter defines the included file as relative to the document path |
Sergunb | 0:8918a71cdbe9 | 471 | if(!strcasecmp(attribute, "file")) |
Sergunb | 0:8918a71cdbe9 | 472 | { |
Sergunb | 0:8918a71cdbe9 | 473 | //Allocate a buffer to hold the path to the file to be included |
Sergunb | 0:8918a71cdbe9 | 474 | path = osAllocMem(strlen(uri) + strlen(value) + 1); |
Sergunb | 0:8918a71cdbe9 | 475 | //Failed to allocate memory? |
Sergunb | 0:8918a71cdbe9 | 476 | if(path == NULL) |
Sergunb | 0:8918a71cdbe9 | 477 | return ERROR_OUT_OF_MEMORY; |
Sergunb | 0:8918a71cdbe9 | 478 | |
Sergunb | 0:8918a71cdbe9 | 479 | //Copy the path identifying the script file being processed |
Sergunb | 0:8918a71cdbe9 | 480 | strcpy(path, uri); |
Sergunb | 0:8918a71cdbe9 | 481 | //Search for the last slash character |
Sergunb | 0:8918a71cdbe9 | 482 | p = strrchr(path, '/'); |
Sergunb | 0:8918a71cdbe9 | 483 | |
Sergunb | 0:8918a71cdbe9 | 484 | //Remove the filename from the path if applicable |
Sergunb | 0:8918a71cdbe9 | 485 | if(p) |
Sergunb | 0:8918a71cdbe9 | 486 | strcpy(p + 1, value); |
Sergunb | 0:8918a71cdbe9 | 487 | else |
Sergunb | 0:8918a71cdbe9 | 488 | strcpy(path, value); |
Sergunb | 0:8918a71cdbe9 | 489 | } |
Sergunb | 0:8918a71cdbe9 | 490 | //The virtual parameter defines the included file as relative to the document root |
Sergunb | 0:8918a71cdbe9 | 491 | else if(!strcasecmp(attribute, "virtual")) |
Sergunb | 0:8918a71cdbe9 | 492 | { |
Sergunb | 0:8918a71cdbe9 | 493 | //Copy the absolute path |
Sergunb | 0:8918a71cdbe9 | 494 | path = strDuplicate(value); |
Sergunb | 0:8918a71cdbe9 | 495 | //Failed to duplicate the string? |
Sergunb | 0:8918a71cdbe9 | 496 | if(path == NULL) |
Sergunb | 0:8918a71cdbe9 | 497 | return ERROR_OUT_OF_MEMORY; |
Sergunb | 0:8918a71cdbe9 | 498 | } |
Sergunb | 0:8918a71cdbe9 | 499 | //Unknown parameter... |
Sergunb | 0:8918a71cdbe9 | 500 | else |
Sergunb | 0:8918a71cdbe9 | 501 | { |
Sergunb | 0:8918a71cdbe9 | 502 | //Report an error |
Sergunb | 0:8918a71cdbe9 | 503 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 504 | } |
Sergunb | 0:8918a71cdbe9 | 505 | |
Sergunb | 0:8918a71cdbe9 | 506 | //Use server-side scripting to dynamically generate HTML code? |
Sergunb | 0:8918a71cdbe9 | 507 | if(httpCompExtension(value, ".stm") || |
Sergunb | 0:8918a71cdbe9 | 508 | httpCompExtension(value, ".shtm") || |
Sergunb | 0:8918a71cdbe9 | 509 | httpCompExtension(value, ".shtml")) |
Sergunb | 0:8918a71cdbe9 | 510 | { |
Sergunb | 0:8918a71cdbe9 | 511 | //SSI processing (Server Side Includes) |
Sergunb | 0:8918a71cdbe9 | 512 | error = ssiExecuteScript(connection, path, level + 1); |
Sergunb | 0:8918a71cdbe9 | 513 | } |
Sergunb | 0:8918a71cdbe9 | 514 | else |
Sergunb | 0:8918a71cdbe9 | 515 | { |
Sergunb | 0:8918a71cdbe9 | 516 | #if (HTTP_SERVER_FS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 517 | FsFile *file; |
Sergunb | 0:8918a71cdbe9 | 518 | |
Sergunb | 0:8918a71cdbe9 | 519 | //Retrieve the full pathname |
Sergunb | 0:8918a71cdbe9 | 520 | httpGetAbsolutePath(connection, path, |
Sergunb | 0:8918a71cdbe9 | 521 | connection->buffer, HTTP_SERVER_BUFFER_SIZE); |
Sergunb | 0:8918a71cdbe9 | 522 | |
Sergunb | 0:8918a71cdbe9 | 523 | //Open the file for reading |
Sergunb | 0:8918a71cdbe9 | 524 | file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ); |
Sergunb | 0:8918a71cdbe9 | 525 | |
Sergunb | 0:8918a71cdbe9 | 526 | //Successful operation? |
Sergunb | 0:8918a71cdbe9 | 527 | if(file) |
Sergunb | 0:8918a71cdbe9 | 528 | { |
Sergunb | 0:8918a71cdbe9 | 529 | //Send the contents of the requested file |
Sergunb | 0:8918a71cdbe9 | 530 | while(1) |
Sergunb | 0:8918a71cdbe9 | 531 | { |
Sergunb | 0:8918a71cdbe9 | 532 | //Read data from the specified file |
Sergunb | 0:8918a71cdbe9 | 533 | error = fsReadFile(file, connection->buffer, HTTP_SERVER_BUFFER_SIZE, &length); |
Sergunb | 0:8918a71cdbe9 | 534 | //End of input stream? |
Sergunb | 0:8918a71cdbe9 | 535 | if(error) |
Sergunb | 0:8918a71cdbe9 | 536 | break; |
Sergunb | 0:8918a71cdbe9 | 537 | |
Sergunb | 0:8918a71cdbe9 | 538 | //Send data to the client |
Sergunb | 0:8918a71cdbe9 | 539 | error = httpWriteStream(connection, connection->buffer, length); |
Sergunb | 0:8918a71cdbe9 | 540 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 541 | if(error) |
Sergunb | 0:8918a71cdbe9 | 542 | break; |
Sergunb | 0:8918a71cdbe9 | 543 | } |
Sergunb | 0:8918a71cdbe9 | 544 | |
Sergunb | 0:8918a71cdbe9 | 545 | //Close the file |
Sergunb | 0:8918a71cdbe9 | 546 | fsCloseFile(file); |
Sergunb | 0:8918a71cdbe9 | 547 | |
Sergunb | 0:8918a71cdbe9 | 548 | //Successful file transfer? |
Sergunb | 0:8918a71cdbe9 | 549 | if(error == ERROR_END_OF_FILE) |
Sergunb | 0:8918a71cdbe9 | 550 | error = NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 551 | } |
Sergunb | 0:8918a71cdbe9 | 552 | else |
Sergunb | 0:8918a71cdbe9 | 553 | { |
Sergunb | 0:8918a71cdbe9 | 554 | //The specified URI cannot be found |
Sergunb | 0:8918a71cdbe9 | 555 | error = ERROR_NOT_FOUND; |
Sergunb | 0:8918a71cdbe9 | 556 | } |
Sergunb | 0:8918a71cdbe9 | 557 | #else |
Sergunb | 0:8918a71cdbe9 | 558 | uint8_t *data; |
Sergunb | 0:8918a71cdbe9 | 559 | |
Sergunb | 0:8918a71cdbe9 | 560 | //Retrieve the full pathname |
Sergunb | 0:8918a71cdbe9 | 561 | httpGetAbsolutePath(connection, path, |
Sergunb | 0:8918a71cdbe9 | 562 | connection->buffer, HTTP_SERVER_BUFFER_SIZE); |
Sergunb | 0:8918a71cdbe9 | 563 | |
Sergunb | 0:8918a71cdbe9 | 564 | //Get the resource data associated with the file |
Sergunb | 0:8918a71cdbe9 | 565 | error = resGetData(connection->buffer, &data, &length); |
Sergunb | 0:8918a71cdbe9 | 566 | |
Sergunb | 0:8918a71cdbe9 | 567 | //Send the contents of the requested file |
Sergunb | 0:8918a71cdbe9 | 568 | if(!error) |
Sergunb | 0:8918a71cdbe9 | 569 | error = httpWriteStream(connection, data, length); |
Sergunb | 0:8918a71cdbe9 | 570 | #endif |
Sergunb | 0:8918a71cdbe9 | 571 | } |
Sergunb | 0:8918a71cdbe9 | 572 | |
Sergunb | 0:8918a71cdbe9 | 573 | //Cannot found the specified resource? |
Sergunb | 0:8918a71cdbe9 | 574 | if(error == ERROR_NOT_FOUND) |
Sergunb | 0:8918a71cdbe9 | 575 | error = ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 576 | |
Sergunb | 0:8918a71cdbe9 | 577 | //Release previously allocated memory |
Sergunb | 0:8918a71cdbe9 | 578 | osFreeMem(path); |
Sergunb | 0:8918a71cdbe9 | 579 | //return status code |
Sergunb | 0:8918a71cdbe9 | 580 | return error; |
Sergunb | 0:8918a71cdbe9 | 581 | } |
Sergunb | 0:8918a71cdbe9 | 582 | |
Sergunb | 0:8918a71cdbe9 | 583 | |
Sergunb | 0:8918a71cdbe9 | 584 | /** |
Sergunb | 0:8918a71cdbe9 | 585 | * @brief Process SSI echo directive |
Sergunb | 0:8918a71cdbe9 | 586 | * |
Sergunb | 0:8918a71cdbe9 | 587 | * This echo directive displays the contents of a specified |
Sergunb | 0:8918a71cdbe9 | 588 | * HTTP environment variable |
Sergunb | 0:8918a71cdbe9 | 589 | * |
Sergunb | 0:8918a71cdbe9 | 590 | * @param[in] connection Structure representing an HTTP connection |
Sergunb | 0:8918a71cdbe9 | 591 | * @param[in] tag Pointer to the SSI tag |
Sergunb | 0:8918a71cdbe9 | 592 | * @param[in] length Total length of the SSI tag |
Sergunb | 0:8918a71cdbe9 | 593 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 594 | **/ |
Sergunb | 0:8918a71cdbe9 | 595 | |
Sergunb | 0:8918a71cdbe9 | 596 | error_t ssiProcessEchoCommand(HttpConnection *connection, const char_t *tag, size_t length) |
Sergunb | 0:8918a71cdbe9 | 597 | { |
Sergunb | 0:8918a71cdbe9 | 598 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 599 | char_t *separator; |
Sergunb | 0:8918a71cdbe9 | 600 | char_t *attribute; |
Sergunb | 0:8918a71cdbe9 | 601 | char_t *value; |
Sergunb | 0:8918a71cdbe9 | 602 | |
Sergunb | 0:8918a71cdbe9 | 603 | //Discard invalid SSI directives |
Sergunb | 0:8918a71cdbe9 | 604 | if(length < 4 || length >= HTTP_SERVER_BUFFER_SIZE) |
Sergunb | 0:8918a71cdbe9 | 605 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 606 | |
Sergunb | 0:8918a71cdbe9 | 607 | //Skip the SSI echo command (4 bytes) |
Sergunb | 0:8918a71cdbe9 | 608 | memcpy(connection->buffer, tag + 4, length - 4); |
Sergunb | 0:8918a71cdbe9 | 609 | //Ensure the resulting string is NULL-terminated |
Sergunb | 0:8918a71cdbe9 | 610 | connection->buffer[length - 4] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 611 | |
Sergunb | 0:8918a71cdbe9 | 612 | //Check whether a separator is present |
Sergunb | 0:8918a71cdbe9 | 613 | separator = strchr(connection->buffer, '='); |
Sergunb | 0:8918a71cdbe9 | 614 | //Separator not found? |
Sergunb | 0:8918a71cdbe9 | 615 | if(!separator) |
Sergunb | 0:8918a71cdbe9 | 616 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 617 | |
Sergunb | 0:8918a71cdbe9 | 618 | //Split the tag |
Sergunb | 0:8918a71cdbe9 | 619 | *separator = '\0'; |
Sergunb | 0:8918a71cdbe9 | 620 | |
Sergunb | 0:8918a71cdbe9 | 621 | //Get attribute name and value |
Sergunb | 0:8918a71cdbe9 | 622 | attribute = strTrimWhitespace(connection->buffer); |
Sergunb | 0:8918a71cdbe9 | 623 | value = strTrimWhitespace(separator + 1); |
Sergunb | 0:8918a71cdbe9 | 624 | |
Sergunb | 0:8918a71cdbe9 | 625 | //Remove leading simple or double quote |
Sergunb | 0:8918a71cdbe9 | 626 | if(value[0] == '\'' || value[0] == '\"') |
Sergunb | 0:8918a71cdbe9 | 627 | value++; |
Sergunb | 0:8918a71cdbe9 | 628 | |
Sergunb | 0:8918a71cdbe9 | 629 | //Get the length of the attribute value |
Sergunb | 0:8918a71cdbe9 | 630 | length = strlen(value); |
Sergunb | 0:8918a71cdbe9 | 631 | |
Sergunb | 0:8918a71cdbe9 | 632 | //Remove trailing simple or double quote |
Sergunb | 0:8918a71cdbe9 | 633 | if(length > 0) |
Sergunb | 0:8918a71cdbe9 | 634 | { |
Sergunb | 0:8918a71cdbe9 | 635 | if(value[length - 1] == '\'' || value[length - 1] == '\"') |
Sergunb | 0:8918a71cdbe9 | 636 | value[length - 1] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 637 | } |
Sergunb | 0:8918a71cdbe9 | 638 | |
Sergunb | 0:8918a71cdbe9 | 639 | //Enforce attribute name |
Sergunb | 0:8918a71cdbe9 | 640 | if(strcasecmp(attribute, "var")) |
Sergunb | 0:8918a71cdbe9 | 641 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 642 | |
Sergunb | 0:8918a71cdbe9 | 643 | //Remote address? |
Sergunb | 0:8918a71cdbe9 | 644 | if(!strcasecmp(value, "REMOTE_ADDR")) |
Sergunb | 0:8918a71cdbe9 | 645 | { |
Sergunb | 0:8918a71cdbe9 | 646 | //The IP address of the host making this request |
Sergunb | 0:8918a71cdbe9 | 647 | ipAddrToString(&connection->socket->remoteIpAddr, connection->buffer); |
Sergunb | 0:8918a71cdbe9 | 648 | } |
Sergunb | 0:8918a71cdbe9 | 649 | //Remote port? |
Sergunb | 0:8918a71cdbe9 | 650 | else if(!strcasecmp(value, "REMOTE_PORT")) |
Sergunb | 0:8918a71cdbe9 | 651 | { |
Sergunb | 0:8918a71cdbe9 | 652 | //The port number used by the remote host when making this request |
Sergunb | 0:8918a71cdbe9 | 653 | sprintf(connection->buffer, "%" PRIu16, connection->socket->remotePort); |
Sergunb | 0:8918a71cdbe9 | 654 | } |
Sergunb | 0:8918a71cdbe9 | 655 | //Server address? |
Sergunb | 0:8918a71cdbe9 | 656 | else if(!strcasecmp(value, "SERVER_ADDR")) |
Sergunb | 0:8918a71cdbe9 | 657 | { |
Sergunb | 0:8918a71cdbe9 | 658 | //The IP address of the server for this URL |
Sergunb | 0:8918a71cdbe9 | 659 | ipAddrToString(&connection->socket->localIpAddr, connection->buffer); |
Sergunb | 0:8918a71cdbe9 | 660 | } |
Sergunb | 0:8918a71cdbe9 | 661 | //Server port? |
Sergunb | 0:8918a71cdbe9 | 662 | else if(!strcasecmp(value, "SERVER_PORT")) |
Sergunb | 0:8918a71cdbe9 | 663 | { |
Sergunb | 0:8918a71cdbe9 | 664 | //The port number on this server to which this request was directed |
Sergunb | 0:8918a71cdbe9 | 665 | sprintf(connection->buffer, "%" PRIu16, connection->socket->localPort); |
Sergunb | 0:8918a71cdbe9 | 666 | } |
Sergunb | 0:8918a71cdbe9 | 667 | //Request method? |
Sergunb | 0:8918a71cdbe9 | 668 | else if(!strcasecmp(value, "REQUEST_METHOD")) |
Sergunb | 0:8918a71cdbe9 | 669 | { |
Sergunb | 0:8918a71cdbe9 | 670 | //The method used for this HTTP request |
Sergunb | 0:8918a71cdbe9 | 671 | strcpy(connection->buffer, connection->request.method); |
Sergunb | 0:8918a71cdbe9 | 672 | } |
Sergunb | 0:8918a71cdbe9 | 673 | //Document root? |
Sergunb | 0:8918a71cdbe9 | 674 | else if(!strcasecmp(value, "DOCUMENT_ROOT")) |
Sergunb | 0:8918a71cdbe9 | 675 | { |
Sergunb | 0:8918a71cdbe9 | 676 | //The root directory |
Sergunb | 0:8918a71cdbe9 | 677 | strcpy(connection->buffer, connection->settings->rootDirectory); |
Sergunb | 0:8918a71cdbe9 | 678 | } |
Sergunb | 0:8918a71cdbe9 | 679 | //Document URI? |
Sergunb | 0:8918a71cdbe9 | 680 | else if(!strcasecmp(value, "DOCUMENT_URI")) |
Sergunb | 0:8918a71cdbe9 | 681 | { |
Sergunb | 0:8918a71cdbe9 | 682 | //The URI for this request relative to the root directory |
Sergunb | 0:8918a71cdbe9 | 683 | strcpy(connection->buffer, connection->request.uri); |
Sergunb | 0:8918a71cdbe9 | 684 | } |
Sergunb | 0:8918a71cdbe9 | 685 | //Document name? |
Sergunb | 0:8918a71cdbe9 | 686 | else if(!strcasecmp(value, "DOCUMENT_NAME")) |
Sergunb | 0:8918a71cdbe9 | 687 | { |
Sergunb | 0:8918a71cdbe9 | 688 | //The full physical path and filename of the document requested |
Sergunb | 0:8918a71cdbe9 | 689 | httpGetAbsolutePath(connection, connection->request.uri, |
Sergunb | 0:8918a71cdbe9 | 690 | connection->buffer, HTTP_SERVER_BUFFER_SIZE); |
Sergunb | 0:8918a71cdbe9 | 691 | } |
Sergunb | 0:8918a71cdbe9 | 692 | //Query string? |
Sergunb | 0:8918a71cdbe9 | 693 | else if(!strcasecmp(value, "QUERY_STRING")) |
Sergunb | 0:8918a71cdbe9 | 694 | { |
Sergunb | 0:8918a71cdbe9 | 695 | //The information following the "?" in the URL for this request |
Sergunb | 0:8918a71cdbe9 | 696 | strcpy(connection->buffer, connection->request.queryString); |
Sergunb | 0:8918a71cdbe9 | 697 | } |
Sergunb | 0:8918a71cdbe9 | 698 | //User name? |
Sergunb | 0:8918a71cdbe9 | 699 | else if(!strcasecmp(value, "AUTH_USER")) |
Sergunb | 0:8918a71cdbe9 | 700 | { |
Sergunb | 0:8918a71cdbe9 | 701 | #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 702 | //The username provided by the user to the server |
Sergunb | 0:8918a71cdbe9 | 703 | strcpy(connection->buffer, connection->request.auth.user); |
Sergunb | 0:8918a71cdbe9 | 704 | #else |
Sergunb | 0:8918a71cdbe9 | 705 | //Basic access authentication is not supported |
Sergunb | 0:8918a71cdbe9 | 706 | connection->buffer[0] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 707 | #endif |
Sergunb | 0:8918a71cdbe9 | 708 | } |
Sergunb | 0:8918a71cdbe9 | 709 | //GMT time? |
Sergunb | 0:8918a71cdbe9 | 710 | else if(!strcasecmp(value, "DATE_GMT")) |
Sergunb | 0:8918a71cdbe9 | 711 | { |
Sergunb | 0:8918a71cdbe9 | 712 | //The current date and time in Greenwich Mean Time |
Sergunb | 0:8918a71cdbe9 | 713 | connection->buffer[0] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 714 | } |
Sergunb | 0:8918a71cdbe9 | 715 | //Local time? |
Sergunb | 0:8918a71cdbe9 | 716 | else if(!strcasecmp(value, "DATE_LOCAL")) |
Sergunb | 0:8918a71cdbe9 | 717 | { |
Sergunb | 0:8918a71cdbe9 | 718 | //The current date and time in the local timezone |
Sergunb | 0:8918a71cdbe9 | 719 | connection->buffer[0] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 720 | } |
Sergunb | 0:8918a71cdbe9 | 721 | //Unknown variable? |
Sergunb | 0:8918a71cdbe9 | 722 | else |
Sergunb | 0:8918a71cdbe9 | 723 | { |
Sergunb | 0:8918a71cdbe9 | 724 | //Report an error |
Sergunb | 0:8918a71cdbe9 | 725 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 726 | } |
Sergunb | 0:8918a71cdbe9 | 727 | |
Sergunb | 0:8918a71cdbe9 | 728 | //Get the length of the resulting string |
Sergunb | 0:8918a71cdbe9 | 729 | length = strlen(connection->buffer); |
Sergunb | 0:8918a71cdbe9 | 730 | |
Sergunb | 0:8918a71cdbe9 | 731 | //Send the contents of the specified environment variable |
Sergunb | 0:8918a71cdbe9 | 732 | error = httpWriteStream(connection, connection->buffer, length); |
Sergunb | 0:8918a71cdbe9 | 733 | //Failed to send data? |
Sergunb | 0:8918a71cdbe9 | 734 | if(error) |
Sergunb | 0:8918a71cdbe9 | 735 | return error; |
Sergunb | 0:8918a71cdbe9 | 736 | |
Sergunb | 0:8918a71cdbe9 | 737 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 738 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 739 | } |
Sergunb | 0:8918a71cdbe9 | 740 | |
Sergunb | 0:8918a71cdbe9 | 741 | |
Sergunb | 0:8918a71cdbe9 | 742 | /** |
Sergunb | 0:8918a71cdbe9 | 743 | * @brief Process SSI exec directive |
Sergunb | 0:8918a71cdbe9 | 744 | * |
Sergunb | 0:8918a71cdbe9 | 745 | * This exec directive executes a program, script, or shell command on |
Sergunb | 0:8918a71cdbe9 | 746 | * the server. The cmd parameter specifies a server-side command. The |
Sergunb | 0:8918a71cdbe9 | 747 | * cgi parameter specifies the path to a CGI script |
Sergunb | 0:8918a71cdbe9 | 748 | * |
Sergunb | 0:8918a71cdbe9 | 749 | * @param[in] connection Structure representing an HTTP connection |
Sergunb | 0:8918a71cdbe9 | 750 | * @param[in] tag Pointer to the SSI tag |
Sergunb | 0:8918a71cdbe9 | 751 | * @param[in] length Total length of the SSI tag |
Sergunb | 0:8918a71cdbe9 | 752 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 753 | **/ |
Sergunb | 0:8918a71cdbe9 | 754 | |
Sergunb | 0:8918a71cdbe9 | 755 | error_t ssiProcessExecCommand(HttpConnection *connection, const char_t *tag, size_t length) |
Sergunb | 0:8918a71cdbe9 | 756 | { |
Sergunb | 0:8918a71cdbe9 | 757 | char_t *separator; |
Sergunb | 0:8918a71cdbe9 | 758 | char_t *attribute; |
Sergunb | 0:8918a71cdbe9 | 759 | char_t *value; |
Sergunb | 0:8918a71cdbe9 | 760 | |
Sergunb | 0:8918a71cdbe9 | 761 | //First, check whether CGI is supported by the server |
Sergunb | 0:8918a71cdbe9 | 762 | if(connection->settings->cgiCallback == NULL) |
Sergunb | 0:8918a71cdbe9 | 763 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 764 | |
Sergunb | 0:8918a71cdbe9 | 765 | //Discard invalid SSI directives |
Sergunb | 0:8918a71cdbe9 | 766 | if(length < 4 || length >= HTTP_SERVER_BUFFER_SIZE) |
Sergunb | 0:8918a71cdbe9 | 767 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 768 | |
Sergunb | 0:8918a71cdbe9 | 769 | //Skip the SSI exec command (4 bytes) |
Sergunb | 0:8918a71cdbe9 | 770 | memcpy(connection->buffer, tag + 4, length - 4); |
Sergunb | 0:8918a71cdbe9 | 771 | //Ensure the resulting string is NULL-terminated |
Sergunb | 0:8918a71cdbe9 | 772 | connection->buffer[length - 4] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 773 | |
Sergunb | 0:8918a71cdbe9 | 774 | //Check whether a separator is present |
Sergunb | 0:8918a71cdbe9 | 775 | separator = strchr(connection->buffer, '='); |
Sergunb | 0:8918a71cdbe9 | 776 | //Separator not found? |
Sergunb | 0:8918a71cdbe9 | 777 | if(!separator) |
Sergunb | 0:8918a71cdbe9 | 778 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 779 | |
Sergunb | 0:8918a71cdbe9 | 780 | //Split the tag |
Sergunb | 0:8918a71cdbe9 | 781 | *separator = '\0'; |
Sergunb | 0:8918a71cdbe9 | 782 | |
Sergunb | 0:8918a71cdbe9 | 783 | //Get attribute name and value |
Sergunb | 0:8918a71cdbe9 | 784 | attribute = strTrimWhitespace(connection->buffer); |
Sergunb | 0:8918a71cdbe9 | 785 | value = strTrimWhitespace(separator + 1); |
Sergunb | 0:8918a71cdbe9 | 786 | |
Sergunb | 0:8918a71cdbe9 | 787 | //Remove leading simple or double quote |
Sergunb | 0:8918a71cdbe9 | 788 | if(value[0] == '\'' || value[0] == '\"') |
Sergunb | 0:8918a71cdbe9 | 789 | value++; |
Sergunb | 0:8918a71cdbe9 | 790 | |
Sergunb | 0:8918a71cdbe9 | 791 | //Get the length of the attribute value |
Sergunb | 0:8918a71cdbe9 | 792 | length = strlen(value); |
Sergunb | 0:8918a71cdbe9 | 793 | |
Sergunb | 0:8918a71cdbe9 | 794 | //Remove trailing simple or double quote |
Sergunb | 0:8918a71cdbe9 | 795 | if(length > 0) |
Sergunb | 0:8918a71cdbe9 | 796 | { |
Sergunb | 0:8918a71cdbe9 | 797 | if(value[length - 1] == '\'' || value[length - 1] == '\"') |
Sergunb | 0:8918a71cdbe9 | 798 | value[length - 1] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 799 | } |
Sergunb | 0:8918a71cdbe9 | 800 | |
Sergunb | 0:8918a71cdbe9 | 801 | //Enforce attribute name |
Sergunb | 0:8918a71cdbe9 | 802 | if(strcasecmp(attribute, "cgi") && strcasecmp(attribute, "cmd") && strcasecmp(attribute, "cmd_argument")) |
Sergunb | 0:8918a71cdbe9 | 803 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 804 | //Check the length of the CGI parameter |
Sergunb | 0:8918a71cdbe9 | 805 | if(strlen(value) > HTTP_SERVER_CGI_PARAM_MAX_LEN) |
Sergunb | 0:8918a71cdbe9 | 806 | return ERROR_INVALID_TAG; |
Sergunb | 0:8918a71cdbe9 | 807 | |
Sergunb | 0:8918a71cdbe9 | 808 | //The scratch buffer may be altered by the user-defined callback. |
Sergunb | 0:8918a71cdbe9 | 809 | //So the CGI parameter must be copied prior to function invocation |
Sergunb | 0:8918a71cdbe9 | 810 | strcpy(connection->cgiParam, value); |
Sergunb | 0:8918a71cdbe9 | 811 | |
Sergunb | 0:8918a71cdbe9 | 812 | //Invoke user-defined callback |
Sergunb | 0:8918a71cdbe9 | 813 | return connection->settings->cgiCallback(connection, connection->cgiParam); |
Sergunb | 0:8918a71cdbe9 | 814 | } |
Sergunb | 0:8918a71cdbe9 | 815 | |
Sergunb | 0:8918a71cdbe9 | 816 | |
Sergunb | 0:8918a71cdbe9 | 817 | /** |
Sergunb | 0:8918a71cdbe9 | 818 | * @brief Search a string for a given tag |
Sergunb | 0:8918a71cdbe9 | 819 | * @param[in] s String to search |
Sergunb | 0:8918a71cdbe9 | 820 | * @param[in] sLen Length of the string to search |
Sergunb | 0:8918a71cdbe9 | 821 | * @param[in] tag String containing the tag to search for |
Sergunb | 0:8918a71cdbe9 | 822 | * @param[in] tagLen Length of the tag |
Sergunb | 0:8918a71cdbe9 | 823 | * @param[out] pos The index of the first occurrence of the tag in the string, |
Sergunb | 0:8918a71cdbe9 | 824 | * @retval NO_ERROR if the specified tag has been found |
Sergunb | 0:8918a71cdbe9 | 825 | * @retval ERROR_PARTIAL_MATCH if a partial match occurs |
Sergunb | 0:8918a71cdbe9 | 826 | * @retval ERROR_NO_MATCH if the tag does not appear in the string |
Sergunb | 0:8918a71cdbe9 | 827 | **/ |
Sergunb | 0:8918a71cdbe9 | 828 | |
Sergunb | 0:8918a71cdbe9 | 829 | error_t ssiSearchTag(const char_t *s, size_t sLen, const char_t *tag, size_t tagLen, uint_t *pos) |
Sergunb | 0:8918a71cdbe9 | 830 | { |
Sergunb | 0:8918a71cdbe9 | 831 | uint_t i; |
Sergunb | 0:8918a71cdbe9 | 832 | uint_t j; |
Sergunb | 0:8918a71cdbe9 | 833 | |
Sergunb | 0:8918a71cdbe9 | 834 | //Parse the input string |
Sergunb | 0:8918a71cdbe9 | 835 | for(i = 0; i <= sLen; i++) |
Sergunb | 0:8918a71cdbe9 | 836 | { |
Sergunb | 0:8918a71cdbe9 | 837 | //Compare current substring with the given tag |
Sergunb | 0:8918a71cdbe9 | 838 | for(j = 0; (i + j) < sLen && j < tagLen; j++) |
Sergunb | 0:8918a71cdbe9 | 839 | { |
Sergunb | 0:8918a71cdbe9 | 840 | if(s[i + j] != tag[j]) |
Sergunb | 0:8918a71cdbe9 | 841 | break; |
Sergunb | 0:8918a71cdbe9 | 842 | } |
Sergunb | 0:8918a71cdbe9 | 843 | |
Sergunb | 0:8918a71cdbe9 | 844 | //Check whether a full match occurred |
Sergunb | 0:8918a71cdbe9 | 845 | if(j == tagLen) |
Sergunb | 0:8918a71cdbe9 | 846 | { |
Sergunb | 0:8918a71cdbe9 | 847 | //Save the position of the first character |
Sergunb | 0:8918a71cdbe9 | 848 | *pos = i; |
Sergunb | 0:8918a71cdbe9 | 849 | //The specified tag has been found |
Sergunb | 0:8918a71cdbe9 | 850 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 851 | } |
Sergunb | 0:8918a71cdbe9 | 852 | //Check whether a partial match occurred |
Sergunb | 0:8918a71cdbe9 | 853 | else if((i + j) == sLen && j > 0) |
Sergunb | 0:8918a71cdbe9 | 854 | { |
Sergunb | 0:8918a71cdbe9 | 855 | //Save the position of the first character |
Sergunb | 0:8918a71cdbe9 | 856 | *pos = i; |
Sergunb | 0:8918a71cdbe9 | 857 | //The beginning of the tag matches the end of the string |
Sergunb | 0:8918a71cdbe9 | 858 | return ERROR_PARTIAL_MATCH; |
Sergunb | 0:8918a71cdbe9 | 859 | } |
Sergunb | 0:8918a71cdbe9 | 860 | } |
Sergunb | 0:8918a71cdbe9 | 861 | |
Sergunb | 0:8918a71cdbe9 | 862 | //The tag does not appear in the string |
Sergunb | 0:8918a71cdbe9 | 863 | return ERROR_NO_MATCH; |
Sergunb | 0:8918a71cdbe9 | 864 | } |
Sergunb | 0:8918a71cdbe9 | 865 | |
Sergunb | 0:8918a71cdbe9 | 866 | #endif |
Sergunb | 0:8918a71cdbe9 | 867 |