1 /*****************************************************************************
2 *  Copyright 2005 Alt-N Technologies, Ltd.
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  This code incorporates intellectual property owned by Yahoo! and licensed
11 *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
12 *
13 *  Unless required by applicable law or agreed to in writing, software
14 *  distributed under the License is distributed on an "AS IS" BASIS,
15 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 *  See the License for the specific language governing permissions and
17 *  limitations under the License.
18 *
19 *****************************************************************************/
20 
21 #ifdef WIN32
22 #include <windows.h>
23 #pragma warning( disable: 4786 )
24 #else
25 #endif
26 
27 #include <string.h>
28 
29 #include "dkim.h"
30 #include "dkimbase.h"
31 
32 #include <algorithm>
33 
34 
CDKIMBase()35 CDKIMBase::CDKIMBase()
36 {
37 	m_From = NULL;
38 	m_Sender = NULL;
39 	m_hTag = NULL;
40 	m_hTagSize = 0;
41 	m_hTagPos = 0;
42 	m_Line = NULL;
43 	m_LineSize = 0;
44 	m_LinePos = 0;
45 	m_InHeaders = true;
46 }
47 
~CDKIMBase()48 CDKIMBase::~CDKIMBase()
49 {
50 	Free( m_Line );
51 	Free( m_From );
52 	Free( m_Sender );
53 	Free( m_hTag );
54 }
55 
Init(void)56 int CDKIMBase::Init(void)
57 {
58 	return DKIM_SUCCESS;
59 }
60 
61 ////////////////////////////////////////////////////////////////////////////////
62 //
63 // Alloc - allocate buffer
64 //
65 ////////////////////////////////////////////////////////////////////////////////
Alloc(char * & szBuffer,int nRequiredSize)66 int CDKIMBase::Alloc( char*& szBuffer, int nRequiredSize )
67 {
68 	szBuffer = new char[nRequiredSize];
69 
70 	return (szBuffer == NULL) ? DKIM_OUT_OF_MEMORY : DKIM_SUCCESS;
71 }
72 
73 
74 ////////////////////////////////////////////////////////////////////////////////
75 //
76 // ReAlloc - extend buffer if necessary, leaving room for future expansion
77 //
78 ////////////////////////////////////////////////////////////////////////////////
ReAlloc(char * & szBuffer,int & nBufferSize,int nRequiredSize)79 int CDKIMBase::ReAlloc( char*& szBuffer, int& nBufferSize, int nRequiredSize )
80 {
81 	if( nRequiredSize > nBufferSize )
82 	{
83 		char* newp;
84 		int nNewSize = nRequiredSize + BUFFER_ALLOC_INCREMENT;
85 
86 		if( Alloc( newp, nNewSize ) == DKIM_SUCCESS )
87 		{
88 			if( szBuffer != NULL && nBufferSize > 0 )
89 			{
90 				memcpy( newp, szBuffer, nBufferSize );
91 				delete[] szBuffer;
92 			}
93 			szBuffer = newp;
94 			nBufferSize  = nNewSize;
95 		}
96 		else
97 		{
98 			return DKIM_OUT_OF_MEMORY; // memory alloc error!
99 		}
100 	}
101 
102 	return DKIM_SUCCESS;
103 }
104 
105 ////////////////////////////////////////////////////////////////////////////////
106 //
107 // Process - split buffers into lines without any CRs or LFs at the end.
108 //
109 ////////////////////////////////////////////////////////////////////////////////
Free(char * szBuffer)110 void CDKIMBase::Free( char* szBuffer )
111 {
112 	if( szBuffer )
113 		delete[] szBuffer;
114 }
115 
116 ////////////////////////////////////////////////////////////////////////////////
117 //
118 // Process - split buffers into lines without any CRs or LFs at the end.
119 //
120 ////////////////////////////////////////////////////////////////////////////////
Process(char * szBuffer,int nBufLength,bool bEOF)121 int CDKIMBase::Process( char* szBuffer, int nBufLength, bool bEOF )
122 {
123 	char* p = szBuffer;
124 	char* e = szBuffer + nBufLength;
125 
126 	while( p < e )
127 	{
128 		if( *p != '\n' || m_LinePos == 0 || m_Line[m_LinePos-1] != '\r' )
129 		{
130 			// add char to line
131 			if (m_LinePos >= m_LineSize)
132 			{
133 				int nRet = ReAlloc( m_Line, m_LineSize, m_LinePos+1 );
134 				if (nRet != DKIM_SUCCESS)
135 					return nRet;
136 			}
137 			m_Line[m_LinePos++] = *p;
138 		}
139 		else
140 		{
141 			// back up past the CR
142 			m_LinePos--;
143 
144 			if (m_InHeaders)
145 			{
146 				// process header line
147 				if (m_LinePos == 0)
148 				{
149 					m_InHeaders = false;
150 					int Result = ProcessHeaders();
151 					if (Result != DKIM_SUCCESS)
152 						return Result;
153 				}
154 				else
155 				{
156 					// append the header to the headers list
157 					if ( m_Line[0] != ' ' && m_Line[0] != '\t' )
158 					{
159 						HeaderList.push_back( string( m_Line, m_LinePos ) );
160 					}
161 					else
162 					{
163 						if ( !HeaderList.empty() )
164 						{
165 							HeaderList.back().append( "\r\n", 2 ).append( m_Line, m_LinePos );
166 						}
167 						else
168 						{
169 							// no header to append to...
170 						}
171 					}
172 				}
173 			}
174 			else
175 			{
176 				// process body line
177 				int Result = ProcessBody(m_Line, m_LinePos, bEOF);
178 				if (Result != DKIM_SUCCESS)
179 				{
180 					m_LinePos = 0;
181 					return Result;
182 				}
183 			}
184 
185 			m_LinePos = 0;
186 		}
187 
188 		p++;
189 	}
190 
191 	return DKIM_SUCCESS;
192 }
193 
194 
195 ////////////////////////////////////////////////////////////////////////////////
196 //
197 // ProcessFinal - process leftovers if stopping before the body or mid-line
198 //
199 ////////////////////////////////////////////////////////////////////////////////
ProcessFinal(void)200 int CDKIMBase::ProcessFinal(void)
201 {
202 	if ( m_LinePos > 0 )
203 	{
204 		Process( "\r\n", 2, true );
205 	}
206 
207 	if( m_InHeaders )
208 	{
209 		m_InHeaders = false;
210 		ProcessHeaders();
211 		ProcessBody("", 0, true);
212 	}
213 
214 	return DKIM_SUCCESS;
215 }
216 
217 
218 ////////////////////////////////////////////////////////////////////////////////
219 //
220 // ProcessHeaders - process the headers (to be implemented by derived class)
221 //
222 ////////////////////////////////////////////////////////////////////////////////
ProcessHeaders()223 int CDKIMBase::ProcessHeaders()
224 {
225 	return DKIM_SUCCESS;
226 }
227 
228 
229 ////////////////////////////////////////////////////////////////////////////////
230 //
231 // ProcessBody - process body line (to be implemented by derived class)
232 //
233 ////////////////////////////////////////////////////////////////////////////////
ProcessBody(char * szBuffer,int nBufLength,bool bEOF)234 int CDKIMBase::ProcessBody( char* szBuffer, int nBufLength, bool bEOF )
235 {
236 	return DKIM_SUCCESS;
237 }
238 
239 
240 ////////////////////////////////////////////////////////////////////////////////
241 //
242 // RemoveSWSP - remove streaming white space from buffer/string inline
243 //
244 ////////////////////////////////////////////////////////////////////////////////
245 
246 struct isswsp
247 {
operator ()isswsp248 	bool operator()( char ch ) { return( ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' ); }
249 };
250 
RemoveSWSP(char * szBuffer)251 void CDKIMBase::RemoveSWSP( char* szBuffer )
252 {
253 	*remove_if( szBuffer, szBuffer+strlen(szBuffer), isswsp() ) = '\0';
254 }
255 
RemoveSWSP(char * pBuffer,int & nBufLength)256 void CDKIMBase::RemoveSWSP( char* pBuffer, int& nBufLength )
257 {
258 	nBufLength = remove_if( pBuffer, pBuffer+nBufLength, isswsp() ) - pBuffer;
259 }
260 
RemoveSWSP(string & sBuffer)261 void CDKIMBase::RemoveSWSP( string& sBuffer )
262 {
263 	sBuffer.erase( remove_if( sBuffer.begin(), sBuffer.end(), isswsp() ), sBuffer.end() );
264 }
265 
266 
267 //////////////////////////////////////////////////////////////////////////////////////////
268 //
269 // CompressSWSP - compress streaming white space into single spaces from buffer/string inline
270 //
271 //////////////////////////////////////////////////////////////////////////////////////////
272 
CompressSWSP(char * pBuffer,int & nBufLength)273 void CDKIMBase::CompressSWSP( char* pBuffer, int& nBufLength )
274 {
275 	char* pSrc = pBuffer;
276 	char* pDst = pBuffer;
277 	char* pEnd = pBuffer+nBufLength;
278 
279 	while (pSrc != pEnd)
280 	{
281 		if (isswsp()(*pSrc))
282 		{
283 
284 			do {
285 				++pSrc;
286 			} while (pSrc != pEnd && isswsp()(*pSrc));
287 
288 			if (pSrc == pEnd)
289 				break;
290 
291 			*pDst++ = ' ';
292 		}
293 
294 		*pDst++ = *pSrc++;
295 	}
296 
297 	nBufLength = pDst - pBuffer;
298 }
299 
CompressSWSP(string & sBuffer)300 void CDKIMBase::CompressSWSP( string& sBuffer )
301 {
302 	string::iterator iSrc = sBuffer.begin();
303 	string::iterator iDst = sBuffer.begin();
304 	string::iterator iEnd = sBuffer.end();
305 
306 	while (iSrc != iEnd)
307 	{
308 		if (isswsp()(*iSrc))
309 		{
310 
311 			do {
312 				++iSrc;
313 			} while (iSrc != iEnd && isswsp()(*iSrc));
314 
315 			if (iSrc == iEnd)
316 				break;
317 
318 			*iDst++ = ' ';
319 		}
320 
321 		*iDst++ = *iSrc++;
322 	}
323 
324 	sBuffer.erase(iDst, iEnd);
325 }
326 
327 //////////////////////////////////////////////////////////////////////////////////////////
328 //
329 // RelaxHeader - relax a header field (lower case the name, remove swsp before and after :)
330 //
331 // modified 4/21/06 STB to remove white space before colon
332 //
333 //////////////////////////////////////////////////////////////////////////////////////////
334 
RelaxHeader(const string & sHeader)335 string CDKIMBase::RelaxHeader( const string& sHeader )
336 {
337 	string sTemp = sHeader;
338 
339 	CompressSWSP(sTemp);
340 
341 	unsigned cpos = sTemp.find(':');
342 
343 	if (cpos == -1)
344 	{
345 		// no colon?!
346 	}
347 	else
348 	{
349 		// lower case the header field name
350 		for (unsigned i=0; i<cpos; i++)
351 		{
352 			if (sTemp[i] >= 'A' && sTemp[i] <= 'Z')
353 				sTemp[i] += 'a'-'A';
354 		}
355 
356 		// remove the space after the :
357 		if (cpos+1 < sTemp.length() && sTemp[cpos+1] == ' ')
358 			sTemp.erase(cpos+1, 1);
359 
360 		// remove the space before the :
361 		if (cpos > 0 && sTemp[cpos-1] == ' ')
362 			sTemp.erase(cpos-1, 1);
363 	}
364 
365 	return sTemp;
366 }
367 
368