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