/*
 *  Create an emsMIMEtype structure to hold MIME information. Structure is
 *  initialized to values provided in RFC822 content-type header line. This
 *  includes all parameter name-value pairs.
 *   
 *  NOTE: The user of this function is responsible for calling
 *        free_mime_type() on returned structure.
 *
 *  Args:
 *   content_type [IN] The RFC822 content-type string to parse
 *
 *  Returns: Pointer to the created emsMIMEtype structure, NULL if error.
 */
emsMIMEtypeP parse_make_mime_type(const char *content_type)
{
	const char *kPrefixStr = "Content-Type:";
	const unsigned int kPrefixStrLen = strlen(kPrefixStr);

	if (cistrncmp(content_type, kPrefixStr, kPrefixStrLen) != 0)
		return NULL;

	char *cp = (char *) content_type + kPrefixStrLen;
	char *mime_type = NULL, *mime_subtype = NULL;

	mime_type = rfc822_extract_token(&cp);

	if ((strlen(mime_type) > 0) && ((*cp++) == '/'))
	{
		mime_subtype = rfc822_extract_token(&cp);

		if (strlen(mime_subtype) > 0)
		{
			// We have a type/subtype, so create emsMIMEtype structure
			emsMIMEtypeP mimePtr = make_mime_type(mime_type, mime_subtype, NULL);

			safefree(mime_type);
			safefree(mime_subtype);

			if (mimePtr)
			{
				char *name = NULL, *value = NULL;

				do {

					if ((*cp++) != ';') // Skip semi-colon
						break;
				
					if (!(name = rfc822_extract_token(&cp)))
						break;

					if ((*cp++) != '=') // Skip equals
						break;

					if (!(value = rfc822_extract_token(&cp)))
						break;

					if ((strlen(name) > 0) && (strlen(value) > 0))
						add_mime_parameter(mimePtr, name, value);

					safefree(name);
					safefree(value);
				} while (*cp);

				safefree(name);
				safefree(value);

				return mimePtr;
			}
		}
	}

	// If we get here then something went wrong
	// Do complete cleanup

	safefree(mime_type);
	safefree(mime_subtype);

	return (NULL); // ERROR
}
/*  DoParsePart (static)
 *
 *  Private function used to pipe the output of 1847 parsing into a file.
 */
static int DoParsePart(BufTypePtr pInBuf,
				 FILE *fOut,
				 emsMIMEtypeP *mimePtr,
				 int bDeMime,
				 PartParseStatePtr pState)
{
	if (!pInBuf) // Input buf NULL indicates 'cleanup'
	{
		if (!pState->bFoundHeader)
			return (FALSE);
		else
			return (TRUE);
	}

	if (!pState->bFoundHeader)
	{
		resetpos_buf(pState->pSearchBuf);

		if (pState->nPrevEndMatch > 0)
		{
			/* Check for completion of span */

			// If doesn't continue match, returns zero
			// otherwise returns number of chars of pSearchBuf that have been matched
			unsigned int nNewMatched = completecount_buf(pInBuf, pState->pSearchBuf, pState->nPrevEndMatch);

			if (nNewMatched == buflen_buf(pState->pSearchBuf)) /* complete match made */
			{
				pState->bFoundHeader = TRUE;
				pState->nPrevEndMatch = 0;
				bufncat_buf(pState->pBuf, pInBuf, (buflen_buf(pState->pSearchBuf) - (pState->nPrevEndMatch)));
			}
			else if (nNewMatched != 0) /* Continued to match, but not completed yet -- the input buffer is smaller than the pSearchBuf */
			{
				bufncat_buf(pState->pBuf, pInBuf, poslen_buf(pInBuf));
				pState->nPrevEndMatch = nNewMatched;
				return (TRUE);
			}
			else /* No match continuation */
			{
				pState->nPrevEndMatch = 0;
			}
		}

		resetpos_buf(pState->pSearchBuf);

		// Still not found -- no span
		if (!pState->bFoundHeader)
		{
			// Find match of pSearchBuf, either complete or end-spanning
			// return number of chars to skip before match
			unsigned int nSkip = skipcount_buf(pInBuf, pState->pSearchBuf);
			unsigned int nRemain = poslen_buf(pInBuf) - nSkip;

			if (nRemain == 0) // Not found
			{
				bufncat_buf(pState->pBuf, pInBuf, poslen_buf(pInBuf));
				return (TRUE);
			}
			else
			if (nRemain > buflen_buf(pState->pSearchBuf)) /* Found 'complete' */
			{
				pState->bFoundHeader = TRUE;
				bufncat_buf(pState->pBuf, pInBuf, (nSkip + buflen_buf(pState->pSearchBuf)) );
			}
			else // Partial possible
			{
				pState->nPrevEndMatch = nRemain;
				bufncat_buf(pState->pBuf, pInBuf, poslen_buf(pInBuf));
				return (TRUE);
			}
		}

		// ---------- Now we know it is found ----------

		unsigned int nLen = buflen_buf(pState->pBuf);
		char *pHeader = (char *) malloc(nLen + 1);

		strncpy(pHeader, getbuf_buf(pState->pBuf), nLen);

		{
			char *pCT = rfc822_extract_header(pHeader, "Content-Type:");

			if (mimePtr)
			{
				if (pCT)
				{
					*mimePtr = parse_make_mime_type(pCT);
				}
				else
				{
					*mimePtr = make_mime_type("text", "plain", "1.0");
				}
			}

			safefree(pCT);
		}

		if (bDeMime)
		{
			{
				char *pCTE = rfc822_extract_cte(pHeader);
				if (pCTE)
					pState->cte = rfc822_parse_cte(pCTE);
				else
					pState->cte = CTE_NONE; /* No CTE header, so no encoding */
				safefree(pCTE);
			}

			switch (pState->cte)
			{
				// BASE64
				case CTE_Base64:
				{
					// BASE64 expands about 1/3
					unsigned int preEncBufLen = ((kBufferSize * 10) / 6);
					pState->preEncBuffer = (char *) malloc(preEncBufLen);

					pState->d64state = (Dec64Ptr) malloc(sizeof(Dec64)); // Used by Decode64()
					pState->d64state->decoderState = 0;
					pState->d64state->invalCount = 0;
					pState->d64state->padCount = 0;
					pState->d64state->partial = 0;
					pState->d64state->wasCR = FALSE;
				}
				break;

				// QUOTED-PRINTABLE
				case CTE_QP:
				{
					// QP expands max of 3 times
					unsigned int preEncBufLen = (kBufferSize * 4);
					pState->preEncBuffer = (char *) malloc(preEncBufLen);

					pState->dQPstate = (DecQPPtr) malloc(sizeof(EncQP));
					pState->dQPstate->CurState = qpNormal;
					pState->dQPstate->cLastChar = 0;
				}
				break;

				// Otherwise, no encoding
				default:
				break;
			}
		}
		else
		{
			fwrite(getbuf_buf(pState->pBuf), sizeof(char), nLen, fOut);
		}

		safefree(pHeader)
		free_buf(pState->pBuf);
	}

	// ---------- Now we know it is found and header is init'd ----------

	long errorcnt;

	switch (pState->cte)
	{
		case CTE_Base64:
		{
			unsigned int nReadLen = Decode64(getpos_buf(pInBuf), poslen_buf(pInBuf), pState->preEncBuffer, pState->d64state, &errorcnt);
			fwrite(pState->preEncBuffer, sizeof(char), nReadLen, fOut);
		}
		break;

		case CTE_QP:
		{
			unsigned int nReadLen = DecodeQP(getpos_buf(pInBuf), poslen_buf(pInBuf), pState->preEncBuffer, pState->dQPstate, &errorcnt);
			fwrite(pState->preEncBuffer, sizeof(char), nReadLen, fOut);
		}
		break;

		default: /* 7bit, 8bit, binary, none */
		{
			fwrite(getpos_buf(pInBuf), sizeof(char), poslen_buf(pInBuf), fOut);
		}
		break;
	}

	return (TRUE);
}