/*
 * Skip RFC822 Commenting Folding Whitespace.
 * 
 * data - pointer into header body
 * len - length of data, minus any trailing (not required) NULL.
 * result - on success is set to first character after skipped space
 * rlen - on success returns the length of skipped space
 * 
 * Note that there may be no space to be skipped, so result will
 * be equal to data and rlen will be 0. If all of the input is
 * consumed while within a comment, result is set to NULL and rlen
 */
void
skip_rfc822_cfws(const char* data, unsigned int len, const char** result,
                 unsigned int* rlen)
{
	const char* p = data;
	const char* end = data + len;
	int clevel = 0;
	int escape = 0;
	int newline = 0;

	while (p < end)
	{
		if (escape)
		{
			/*
			 * Previously read character was a \ within a comment.
			 * All characters are escapable except carriage
			 * return and newline, so check for those here. If we
			 * find either one, we don't increment p, and continue
			 * the loop, so that the improperly escaped character
			 * gets processed as if it was not escaped.
			 */
			escape = 0;

			if ( (*p != '\r') && (*p != '\n') )
			{
				p++;
				continue;
			}
			/*
			 * Not a legally escapable character, so fall through for standard
			 * processing. 
			 */
			continue;
		}
		else if (newline)
		{
			/*
			 * Previously read character was a newline. In this
			 * case we need to check if the newline is followed
			 * by whitespace characters, which indicate folding
			 * whitespace. Otherwise we have reached the end of
			 * this header and must stop.
			 */
			newline = 0;

			if ( (*p == ' ') || (*p == '\t') )
			{
				p++;
				continue;
			}
			else
			{
				*result = p;
				*rlen = p - data;
				return;
			}
		}
		else
		{
			switch (*p)
			{
				case ' ':
				case '\t':
				case '\r':
					p++;
					break;
				case '\n':
					newline = 1;
					p++;
					break;
				case '(':
					clevel++;
					p++;
					break;
				case ')':
					clevel--;
					p++;
					break;
				case '\\':
					/* An escape can only be skipped if it is within a comment. */
					if (clevel > 0)
					{
						escape = 1;
						p++;
						break;
					}
					else
					{
						*result = p;
						*rlen = p - data;
						return;
					}
				default:
					if (clevel > 0)
					{
						p++;
						break;
					}
					else
					{
						*result = p;
						*rlen = p - data;
						return;
					}
			}
		}
	}

	if (clevel == 0)
	{
		*result = p;
		*rlen = p - data;
	}
	else
	{
		*result = '\0';
		*rlen = 0;
	}

	return;
}

