Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ssi.c Source File

ssi.c

Go to the documentation of this file.
00001 /**
00002  * @file ssi.c
00003  * @brief SSI (Server Side Includes)
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @section Description
00026  *
00027  * Server Side Includes (SSI) is a simple interpreted server-side scripting
00028  * language used to generate dynamic content to web pages
00029  *
00030  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00031  * @version 1.7.6
00032  **/
00033 
00034 //Switch to the appropriate trace level
00035 #define TRACE_LEVEL HTTP_TRACE_LEVEL
00036 
00037 //Dependencies
00038 #include "core/net.h"
00039 #include "http/http_server.h"
00040 #include "http/http_server_misc.h"
00041 #include "http/mime.h"
00042 #include "http/ssi.h"
00043 #include "str.h"
00044 #include "debug.h"
00045 
00046 //File system support?
00047 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00048    #include "fs_port.h"
00049 #else
00050    #include "resource_manager.h"
00051 #endif
00052 
00053 //Check TCP/IP stack configuration
00054 #if (HTTP_SERVER_SUPPORT == ENABLED && HTTP_SERVER_SSI_SUPPORT == ENABLED)
00055 
00056 
00057 /**
00058  * @brief Execute SSI script
00059  * @param[in] connection Structure representing an HTTP connection
00060  * @param[in] uri NULL-terminated string containing the file to process
00061  * @param[in] level Current level of recursion
00062  * @return Error code
00063  **/
00064 
00065 error_t ssiExecuteScript(HttpConnection *connection, const char_t *uri, uint_t level)
00066 {
00067    error_t error;
00068    size_t length;
00069 
00070 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00071    bool_t more;
00072    uint_t pos;
00073    uint_t n;
00074    char_t *buffer;
00075    FsFile *file;
00076 #else
00077    uint_t i;
00078    uint_t j;
00079    char_t *data;
00080 #endif
00081 
00082    //Recursion limit exceeded?
00083    if(level >= HTTP_SERVER_SSI_MAX_RECURSION)
00084       return NO_ERROR;
00085 
00086    //Retrieve the full pathname
00087    httpGetAbsolutePath(connection, uri,
00088       connection->buffer, HTTP_SERVER_BUFFER_SIZE);
00089 
00090 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00091    //Open the file for reading
00092    file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ);
00093    //Failed to open the file?
00094    if(file == NULL)
00095       return ERROR_NOT_FOUND;
00096 
00097    //Allocate a memory buffer
00098    buffer = osAllocMem(HTTP_SERVER_BUFFER_SIZE);
00099    //Failed to allocate memory?
00100    if(buffer == NULL)
00101    {
00102       //Close the file
00103       fsCloseFile(file);
00104       //Report an error
00105       return ERROR_OUT_OF_MEMORY;
00106    }
00107 #else
00108    //Get the resource data associated with the URI
00109    error = resGetData(connection->buffer, (uint8_t **) &data, &length);
00110    //The specified URI cannot be found?
00111    if(error)
00112       return error;
00113 #endif
00114 
00115    //Send the HTTP response header before executing the script
00116    if(!level)
00117    {
00118       //Format HTTP response header
00119       connection->response.statusCode = 200;
00120       connection->response.contentType = mimeGetType(uri);
00121       connection->response.chunkedEncoding = TRUE;
00122 
00123       //Send the header to the client
00124       error = httpWriteHeader(connection);
00125       //Any error to report?
00126       if(error)
00127       {
00128 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00129          //Close the file
00130          fsCloseFile(file);
00131          //Release memory buffer
00132          osFreeMem(buffer);
00133 #endif
00134          //Return status code
00135          return error;
00136       }
00137    }
00138 
00139 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00140    //Point to the beginning of the buffer
00141    pos = 0;
00142    length = 0;
00143 
00144    //This flag indicates whether data should be read
00145    more = TRUE;
00146 
00147    //Parse the specified file
00148    while(1)
00149    {
00150       //Read more data if needed
00151       if(more)
00152       {
00153          //Check whether the current position is aligned on 32-bit boundaries
00154          n = 4 - ((pos + length) % 4);
00155 
00156          //Maintain proper alignment
00157          if(n != 4)
00158          {
00159             memmove(buffer + pos + n, buffer + pos, length);
00160             pos += n;
00161          }
00162 
00163          //Read data from the specified file
00164          error = fsReadFile(file, buffer + pos + length,
00165             HTTP_SERVER_BUFFER_SIZE - (pos + length), &n);
00166 
00167          //End of input stream?
00168          if(error)
00169          {
00170             //Purge data buffer
00171             error = httpWriteStream(connection, buffer + pos, length);
00172             //Exit immediately
00173             break;
00174          }
00175 
00176          //Adjust the length of the buffer
00177          length += n;
00178          //Clear flag
00179          more = FALSE;
00180       }
00181 
00182       //Search for any SSI tags
00183       error = ssiSearchTag(buffer + pos, length, "<!--#", 5, &n);
00184 
00185       //Full match?
00186       if(error == NO_ERROR)
00187       {
00188          //Send the part of the file that precedes the tag
00189          error = httpWriteStream(connection, buffer + pos, n);
00190          //Failed to send data?
00191          if(error)
00192             break;
00193 
00194          //Advance data pointer
00195          pos += n;
00196          length -= n;
00197 
00198          //Search for the comment terminator
00199          error = ssiSearchTag(buffer + pos + 5, length - 5, "-->", 3, &n);
00200 
00201          //Full match?
00202          if(error == NO_ERROR)
00203          {
00204             //Advance data pointer over the opening identifier
00205             pos += 5;
00206             length -= 5;
00207 
00208             //Process SSI directive
00209             error = ssiProcessCommand(connection, buffer + pos, n, uri, level);
00210             //Any error to report?
00211             if(error)
00212                break;
00213 
00214             //Advance data pointer over the SSI tag
00215             pos += n + 3;
00216             length -= n + 3;
00217          }
00218          //No match or partial match?
00219          else
00220          {
00221             if(pos > 0)
00222             {
00223                //Move the remaining bytes to the start of the buffer
00224                memmove(buffer, buffer + pos, length);
00225                //Rewind to the beginning of the buffer
00226                pos = 0;
00227                //More data are needed
00228                more = TRUE;
00229             }
00230             else
00231             {
00232                //Send data to the client
00233                error = httpWriteStream(connection, buffer + pos, length);
00234                //Any error to report?
00235                if(error)
00236                   break;
00237 
00238                //Rewind to the beginning of the buffer
00239                pos = 0;
00240                length = 0;
00241                //More data are needed
00242                more = TRUE;
00243             }
00244          }
00245       }
00246       //Partial match?
00247       else if(error == ERROR_PARTIAL_MATCH)
00248       {
00249          //Send the part of the file that precedes the tag
00250          error = httpWriteStream(connection, buffer + pos, n);
00251          //Failed to send data?
00252          if(error)
00253             break;
00254 
00255          //Advance data pointer
00256          pos += n;
00257          length -= n;
00258 
00259          //Move the remaining bytes to the start of the buffer
00260          memmove(buffer, buffer + pos, length);
00261          //Rewind to the beginning of the buffer
00262          pos = 0;
00263          //More data are needed
00264          more = TRUE;
00265       }
00266       //No match?
00267       else
00268       {
00269          //Send data to the client
00270          error = httpWriteStream(connection, buffer + pos, length);
00271          //Any error to report?
00272          if(error)
00273             break;
00274 
00275          //Rewind to the beginning of the buffer
00276          pos = 0;
00277          length = 0;
00278          //More data are needed
00279          more = TRUE;
00280       }
00281    }
00282 
00283    //Close the file
00284    fsCloseFile(file);
00285    //Release memory buffer
00286    osFreeMem(buffer);
00287 
00288    //Properly close the output stream
00289    if(!level && error == NO_ERROR)
00290       error = httpCloseStream(connection);
00291 #else
00292    //Parse the specified file
00293    while(length > 0)
00294    {
00295       //Search for any SSI tags
00296       error = ssiSearchTag(data, length, "<!--#", 5, &i);
00297 
00298       //Opening identifier found?
00299       if(!error)
00300       {
00301          //Search for the comment terminator
00302          error = ssiSearchTag(data + i + 5, length - i - 5, "-->", 3, &j);
00303       }
00304 
00305       //Check whether a valid SSI tag has been found?
00306       if(!error)
00307       {
00308          //Send the part of the file that precedes the tag
00309          error = httpWriteStream(connection, data, i);
00310          //Failed to send data?
00311          if(error)
00312             return error;
00313 
00314          //Advance data pointer over the opening identifier
00315          data += i + 5;
00316          length -= i + 5;
00317 
00318          //Process SSI directive
00319          error = ssiProcessCommand(connection, data, j, uri, level);
00320          //Any error to report?
00321          if(error)
00322             return error;
00323 
00324          //Advance data pointer over the SSI tag
00325          data += j + 3;
00326          length -= j + 3;
00327       }
00328       else
00329       {
00330          //Send the rest of the file
00331          error = httpWriteStream(connection, data, length);
00332          //Failed to send data?
00333          if(error)
00334             return error;
00335 
00336          //Advance data pointer
00337          data += length;
00338          length = 0;
00339       }
00340    }
00341 
00342    //Properly close the output stream
00343    if(!level)
00344       error = httpCloseStream(connection);
00345 #endif
00346 
00347    //Return status code
00348    return error;
00349 }
00350 
00351 
00352 /**
00353  * @brief Process SSI directive
00354  * @param[in] connection Structure representing an HTTP connection
00355  * @param[in] tag Pointer to the SSI tag
00356  * @param[in] length Total length of the SSI tag
00357  * @param[in] uri NULL-terminated string containing the file being processed
00358  * @param[in] level Current level of recursion
00359  * @return Error code
00360  **/
00361 
00362 error_t ssiProcessCommand(HttpConnection *connection,
00363    const char_t *tag, size_t length, const char_t *uri, uint_t level)
00364 {
00365    error_t error;
00366 
00367    //Include command found?
00368    if(length > 7 && !strncasecmp(tag, "include", 7))
00369    {
00370       //Process SSI include directive
00371       error = ssiProcessIncludeCommand(connection, tag, length, uri, level);
00372    }
00373    //Echo command found?
00374    else if(length > 4 && !strncasecmp(tag, "echo", 4))
00375    {
00376       //Process SSI echo directive
00377       error = ssiProcessEchoCommand(connection, tag, length);
00378    }
00379    //Exec command found?
00380    else if(length > 4 && !strncasecmp(tag, "exec", 4))
00381    {
00382       //Process SSI exec directive
00383       error = ssiProcessExecCommand(connection, tag, length);
00384    }
00385    //Unknown command?
00386    else
00387    {
00388       //The server is unable to decode the SSI tag
00389       error = ERROR_INVALID_TAG;
00390    }
00391 
00392    //Invalid SSI directive?
00393    if(error == ERROR_INVALID_TAG)
00394    {
00395       //Report a warning to the user
00396       error = httpWriteStream(connection, "Warning: Invalid SSI Tag", 24);
00397    }
00398 
00399    //Return status code
00400    return error;
00401 }
00402 
00403 
00404 /**
00405  * @brief Process SSI include directive
00406  *
00407  * This include directive allows the content of one document to be included
00408  * in another. The file parameter defines the included file as relative to
00409  * the document path. The virtual parameter defines the included file as
00410  * relative to the document root
00411  *
00412  * @param[in] connection Structure representing an HTTP connection
00413  * @param[in] tag Pointer to the SSI tag
00414  * @param[in] length Total length of the SSI tag
00415  * @param[in] uri NULL-terminated string containing the file being processed
00416  * @param[in] level Current level of recursion
00417  * @return Error code
00418  **/
00419 
00420 error_t ssiProcessIncludeCommand(HttpConnection *connection,
00421    const char_t *tag, size_t length, const char_t *uri, uint_t level)
00422 {
00423    error_t error;
00424    char_t *separator;
00425    char_t *attribute;
00426    char_t *value;
00427    char_t *path;
00428    char_t *p;
00429 
00430    //Discard invalid SSI directives
00431    if(length < 7 || length >= HTTP_SERVER_BUFFER_SIZE)
00432       return ERROR_INVALID_TAG;
00433 
00434    //Skip the SSI include command (7 bytes)
00435    memcpy(connection->buffer, tag + 7, length - 7);
00436    //Ensure the resulting string is NULL-terminated
00437    connection->buffer[length - 7] = '\0';
00438 
00439    //Check whether a separator is present
00440    separator = strchr(connection->buffer, '=');
00441    //Separator not found?
00442    if(!separator)
00443       return ERROR_INVALID_TAG;
00444 
00445    //Split the tag
00446    *separator = '\0';
00447 
00448    //Get attribute name and value
00449    attribute = strTrimWhitespace(connection->buffer);
00450    value = strTrimWhitespace(separator + 1);
00451 
00452    //Remove leading simple or double quote
00453    if(value[0] == '\'' || value[0] == '\"')
00454       value++;
00455 
00456    //Get the length of the attribute value
00457    length = strlen(value);
00458 
00459    //Remove trailing simple or double quote
00460    if(length > 0)
00461    {
00462       if(value[length - 1] == '\'' || value[length - 1] == '\"')
00463          value[length - 1] = '\0';
00464    }
00465 
00466    //Check the length of the filename
00467    if(strlen(value) > HTTP_SERVER_URI_MAX_LEN)
00468       return ERROR_INVALID_TAG;
00469 
00470    //The file parameter defines the included file as relative to the document path
00471    if(!strcasecmp(attribute, "file"))
00472    {
00473       //Allocate a buffer to hold the path to the file to be included
00474       path = osAllocMem(strlen(uri) + strlen(value) + 1);
00475       //Failed to allocate memory?
00476       if(path == NULL)
00477          return ERROR_OUT_OF_MEMORY;
00478 
00479       //Copy the path identifying the script file being processed
00480       strcpy(path, uri);
00481       //Search for the last slash character
00482       p = strrchr(path, '/');
00483 
00484       //Remove the filename from the path if applicable
00485       if(p)
00486          strcpy(p + 1, value);
00487       else
00488          strcpy(path, value);
00489    }
00490    //The virtual parameter defines the included file as relative to the document root
00491    else if(!strcasecmp(attribute, "virtual"))
00492    {
00493       //Copy the absolute path
00494       path = strDuplicate(value);
00495       //Failed to duplicate the string?
00496       if(path == NULL)
00497          return ERROR_OUT_OF_MEMORY;
00498    }
00499    //Unknown parameter...
00500    else
00501    {
00502       //Report an error
00503       return ERROR_INVALID_TAG;
00504    }
00505 
00506    //Use server-side scripting to dynamically generate HTML code?
00507    if(httpCompExtension(value, ".stm") ||
00508       httpCompExtension(value, ".shtm") ||
00509       httpCompExtension(value, ".shtml"))
00510    {
00511       //SSI processing (Server Side Includes)
00512       error = ssiExecuteScript(connection, path, level + 1);
00513    }
00514    else
00515    {
00516 #if (HTTP_SERVER_FS_SUPPORT == ENABLED)
00517       FsFile *file;
00518 
00519       //Retrieve the full pathname
00520       httpGetAbsolutePath(connection, path,
00521          connection->buffer, HTTP_SERVER_BUFFER_SIZE);
00522 
00523       //Open the file for reading
00524       file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ);
00525 
00526       //Successful operation?
00527       if(file)
00528       {
00529          //Send the contents of the requested file
00530          while(1)
00531          {
00532             //Read data from the specified file
00533             error = fsReadFile(file, connection->buffer, HTTP_SERVER_BUFFER_SIZE, &length);
00534             //End of input stream?
00535             if(error)
00536                break;
00537 
00538             //Send data to the client
00539             error = httpWriteStream(connection, connection->buffer, length);
00540             //Any error to report?
00541             if(error)
00542                break;
00543          }
00544 
00545          //Close the file
00546          fsCloseFile(file);
00547 
00548          //Successful file transfer?
00549          if(error == ERROR_END_OF_FILE)
00550             error = NO_ERROR;
00551       }
00552       else
00553       {
00554          //The specified URI cannot be found
00555          error = ERROR_NOT_FOUND;
00556       }
00557 #else
00558       uint8_t *data;
00559 
00560       //Retrieve the full pathname
00561       httpGetAbsolutePath(connection, path,
00562          connection->buffer, HTTP_SERVER_BUFFER_SIZE);
00563 
00564       //Get the resource data associated with the file
00565       error = resGetData(connection->buffer, &data, &length);
00566 
00567       //Send the contents of the requested file
00568       if(!error)
00569          error = httpWriteStream(connection, data, length);
00570 #endif
00571    }
00572 
00573    //Cannot found the specified resource?
00574    if(error == ERROR_NOT_FOUND)
00575       error = ERROR_INVALID_TAG;
00576 
00577    //Release previously allocated memory
00578    osFreeMem(path);
00579    //return status code
00580    return error;
00581 }
00582 
00583 
00584 /**
00585  * @brief Process SSI echo directive
00586  *
00587  * This echo directive displays the contents of a specified
00588  * HTTP environment variable
00589  *
00590  * @param[in] connection Structure representing an HTTP connection
00591  * @param[in] tag Pointer to the SSI tag
00592  * @param[in] length Total length of the SSI tag
00593  * @return Error code
00594  **/
00595 
00596 error_t ssiProcessEchoCommand(HttpConnection *connection, const char_t *tag, size_t length)
00597 {
00598    error_t error;
00599    char_t *separator;
00600    char_t *attribute;
00601    char_t *value;
00602 
00603    //Discard invalid SSI directives
00604    if(length < 4 || length >= HTTP_SERVER_BUFFER_SIZE)
00605       return ERROR_INVALID_TAG;
00606 
00607    //Skip the SSI echo command (4 bytes)
00608    memcpy(connection->buffer, tag + 4, length - 4);
00609    //Ensure the resulting string is NULL-terminated
00610    connection->buffer[length - 4] = '\0';
00611 
00612    //Check whether a separator is present
00613    separator = strchr(connection->buffer, '=');
00614    //Separator not found?
00615    if(!separator)
00616       return ERROR_INVALID_TAG;
00617 
00618    //Split the tag
00619    *separator = '\0';
00620 
00621    //Get attribute name and value
00622    attribute = strTrimWhitespace(connection->buffer);
00623    value = strTrimWhitespace(separator + 1);
00624 
00625    //Remove leading simple or double quote
00626    if(value[0] == '\'' || value[0] == '\"')
00627       value++;
00628 
00629    //Get the length of the attribute value
00630    length = strlen(value);
00631 
00632    //Remove trailing simple or double quote
00633    if(length > 0)
00634    {
00635       if(value[length - 1] == '\'' || value[length - 1] == '\"')
00636          value[length - 1] = '\0';
00637    }
00638 
00639    //Enforce attribute name
00640    if(strcasecmp(attribute, "var"))
00641       return ERROR_INVALID_TAG;
00642 
00643    //Remote address?
00644    if(!strcasecmp(value, "REMOTE_ADDR"))
00645    {
00646       //The IP address of the host making this request
00647       ipAddrToString(&connection->socket->remoteIpAddr, connection->buffer);
00648    }
00649    //Remote port?
00650    else if(!strcasecmp(value, "REMOTE_PORT"))
00651    {
00652       //The port number used by the remote host when making this request
00653       sprintf(connection->buffer, "%" PRIu16, connection->socket->remotePort);
00654    }
00655    //Server address?
00656    else if(!strcasecmp(value, "SERVER_ADDR"))
00657    {
00658       //The IP address of the server for this URL
00659       ipAddrToString(&connection->socket->localIpAddr, connection->buffer);
00660    }
00661    //Server port?
00662    else if(!strcasecmp(value, "SERVER_PORT"))
00663    {
00664       //The port number on this server to which this request was directed
00665       sprintf(connection->buffer, "%" PRIu16, connection->socket->localPort);
00666    }
00667    //Request method?
00668    else if(!strcasecmp(value, "REQUEST_METHOD"))
00669    {
00670       //The method used for this HTTP request
00671       strcpy(connection->buffer, connection->request.method);
00672    }
00673    //Document root?
00674    else if(!strcasecmp(value, "DOCUMENT_ROOT"))
00675    {
00676       //The root directory
00677       strcpy(connection->buffer, connection->settings->rootDirectory);
00678    }
00679    //Document URI?
00680    else if(!strcasecmp(value, "DOCUMENT_URI"))
00681    {
00682       //The URI for this request relative to the root directory
00683       strcpy(connection->buffer, connection->request.uri);
00684    }
00685    //Document name?
00686    else if(!strcasecmp(value, "DOCUMENT_NAME"))
00687    {
00688       //The full physical path and filename of the document requested
00689       httpGetAbsolutePath(connection, connection->request.uri,
00690          connection->buffer, HTTP_SERVER_BUFFER_SIZE);
00691    }
00692    //Query string?
00693    else if(!strcasecmp(value, "QUERY_STRING"))
00694    {
00695       //The information following the "?" in the URL for this request
00696       strcpy(connection->buffer, connection->request.queryString);
00697    }
00698    //User name?
00699    else if(!strcasecmp(value, "AUTH_USER"))
00700    {
00701 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
00702       //The username provided by the user to the server
00703       strcpy(connection->buffer, connection->request.auth.user);
00704 #else
00705       //Basic access authentication is not supported
00706       connection->buffer[0] = '\0';
00707 #endif
00708    }
00709    //GMT time?
00710    else if(!strcasecmp(value, "DATE_GMT"))
00711    {
00712       //The current date and time in Greenwich Mean Time
00713       connection->buffer[0] = '\0';
00714    }
00715    //Local time?
00716    else if(!strcasecmp(value, "DATE_LOCAL"))
00717    {
00718       //The current date and time in the local timezone
00719       connection->buffer[0] = '\0';
00720    }
00721    //Unknown variable?
00722    else
00723    {
00724       //Report an error
00725       return ERROR_INVALID_TAG;
00726    }
00727 
00728    //Get the length of the resulting string
00729    length = strlen(connection->buffer);
00730 
00731    //Send the contents of the specified environment variable
00732    error = httpWriteStream(connection, connection->buffer, length);
00733    //Failed to send data?
00734    if(error)
00735       return error;
00736 
00737    //Successful processing
00738    return NO_ERROR;
00739 }
00740 
00741 
00742 /**
00743  * @brief Process SSI exec directive
00744  *
00745  * This exec directive executes a program, script, or shell command on
00746  * the server. The cmd parameter specifies a server-side command. The
00747  * cgi parameter specifies the path to a CGI script
00748  *
00749  * @param[in] connection Structure representing an HTTP connection
00750  * @param[in] tag Pointer to the SSI tag
00751  * @param[in] length Total length of the SSI tag
00752  * @return Error code
00753  **/
00754 
00755 error_t ssiProcessExecCommand(HttpConnection *connection, const char_t *tag, size_t length)
00756 {
00757    char_t *separator;
00758    char_t *attribute;
00759    char_t *value;
00760 
00761    //First, check whether CGI is supported by the server
00762    if(connection->settings->cgiCallback == NULL)
00763       return ERROR_INVALID_TAG;
00764 
00765    //Discard invalid SSI directives
00766    if(length < 4 || length >= HTTP_SERVER_BUFFER_SIZE)
00767       return ERROR_INVALID_TAG;
00768 
00769    //Skip the SSI exec command (4 bytes)
00770    memcpy(connection->buffer, tag + 4, length - 4);
00771    //Ensure the resulting string is NULL-terminated
00772    connection->buffer[length - 4] = '\0';
00773 
00774    //Check whether a separator is present
00775    separator = strchr(connection->buffer, '=');
00776    //Separator not found?
00777    if(!separator)
00778       return ERROR_INVALID_TAG;
00779 
00780    //Split the tag
00781    *separator = '\0';
00782 
00783    //Get attribute name and value
00784    attribute = strTrimWhitespace(connection->buffer);
00785    value = strTrimWhitespace(separator + 1);
00786 
00787    //Remove leading simple or double quote
00788    if(value[0] == '\'' || value[0] == '\"')
00789       value++;
00790 
00791    //Get the length of the attribute value
00792    length = strlen(value);
00793 
00794    //Remove trailing simple or double quote
00795    if(length > 0)
00796    {
00797       if(value[length - 1] == '\'' || value[length - 1] == '\"')
00798          value[length - 1] = '\0';
00799    }
00800 
00801    //Enforce attribute name
00802    if(strcasecmp(attribute, "cgi") && strcasecmp(attribute, "cmd") && strcasecmp(attribute, "cmd_argument"))
00803       return ERROR_INVALID_TAG;
00804    //Check the length of the CGI parameter
00805    if(strlen(value) > HTTP_SERVER_CGI_PARAM_MAX_LEN)
00806       return ERROR_INVALID_TAG;
00807 
00808    //The scratch buffer may be altered by the user-defined callback.
00809    //So the CGI parameter must be copied prior to function invocation
00810    strcpy(connection->cgiParam, value);
00811 
00812    //Invoke user-defined callback
00813    return connection->settings->cgiCallback(connection, connection->cgiParam);
00814 }
00815 
00816 
00817 /**
00818  * @brief Search a string for a given tag
00819  * @param[in] s String to search
00820  * @param[in] sLen Length of the string to search
00821  * @param[in] tag String containing the tag to search for
00822  * @param[in] tagLen Length of the tag
00823  * @param[out] pos The index of the first occurrence of the tag in the string,
00824  * @retval NO_ERROR if the specified tag has been found
00825  * @retval ERROR_PARTIAL_MATCH if a partial match occurs
00826  * @retval ERROR_NO_MATCH if the tag does not appear in the string
00827  **/
00828 
00829 error_t ssiSearchTag(const char_t *s, size_t sLen, const char_t *tag, size_t tagLen, uint_t *pos)
00830 {
00831    uint_t i;
00832    uint_t j;
00833 
00834    //Parse the input string
00835    for(i = 0; i <= sLen; i++)
00836    {
00837       //Compare current substring with the given tag
00838       for(j = 0; (i + j) < sLen && j < tagLen; j++)
00839       {
00840          if(s[i + j] != tag[j])
00841             break;
00842       }
00843 
00844       //Check whether a full match occurred
00845       if(j == tagLen)
00846       {
00847          //Save the position of the first character
00848          *pos = i;
00849          //The specified tag has been found
00850          return NO_ERROR;
00851       }
00852       //Check whether a partial match occurred
00853       else if((i + j) == sLen && j > 0)
00854       {
00855          //Save the position of the first character
00856          *pos = i;
00857          //The beginning of the tag matches the end of the string
00858          return ERROR_PARTIAL_MATCH;
00859       }
00860    }
00861 
00862    //The tag does not appear in the string
00863    return ERROR_NO_MATCH;
00864 }
00865 
00866 #endif
00867