00001 // 00002 // Buffer.cpp 00003 // 00004 // Copyright (c) Shareaza Development Team, 2002-2005. 00005 // This file is part of SHAREAZA (www.shareaza.com) 00006 // 00007 // Shareaza is free software; you can redistribute it 00008 // and/or modify it under the terms of the GNU General Public License 00009 // as published by the Free Software Foundation; either version 2 of 00010 // the License, or (at your option) any later version. 00011 // 00012 // Shareaza is distributed in the hope that it will be useful, 00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 // GNU General Public License for more details. 00016 // 00017 // You should have received a copy of the GNU General Public License 00018 // along with Shareaza; if not, write to the Free Software 00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00020 // 00021 00022 // CBuffer holds some memory, and takes care of allocating and freeing it itself 00023 // http://wiki.shareaza.com/static/Developers.Code.CBuffer 00024 00025 // Copy in the contents of these files here before compiling 00026 #include "StdAfx.h" 00027 #include "Shareaza.h" 00028 #include "Settings.h" 00029 #include "Buffer.h" 00030 #include "Packet.h" 00031 #include "ZLib.h" 00032 #include "Statistics.h" 00033 #include <zlib.h> 00034 00035 // If we are compiling in debug mode, replace the text "THIS_FILE" in the code with the name of this file 00036 #ifdef _DEBUG 00037 #undef THIS_FILE 00038 static char THIS_FILE[]=__FILE__; 00039 #define new DEBUG_NEW 00040 #endif 00041 00042 // Define memory sizes to use in these methods 00043 #define TEMP_BUFFER 4096 // Use a 4 KB buffer as a temporary store between a socket and the buffer object 00044 #define BLOCK_SIZE 1024 // Change the allocated size of the buffer in 1 KB sized blocks 00045 #define BLOCK_MASK 0xFFFFFC00 // Aids in rounding to the next biggest KB of size 00046 00048 // CBuffer construction 00049 00050 // Takes access to a DWORD that is not used (do) 00051 // Makes a new blank CBuffer object with no memory block allocated yet 00052 CBuffer::CBuffer(DWORD* pLimit) 00053 { 00054 // Null pointers and zero counts 00055 m_pNext = NULL; // This object isn't in a list yet 00056 m_pBuffer = NULL; // No memory block has been allocated for this object yet 00057 m_nBuffer = 0; // The size of the memory block is 0 00058 m_nLength = 0; // No bytes have been written here yet 00059 } 00060 00061 // Delete this CBuffer object 00062 // Frees the memory taken up by the buffer 00063 CBuffer::~CBuffer() 00064 { 00065 // If the member variable points to some memory, free it 00066 if ( m_pBuffer ) free( m_pBuffer ); 00067 } 00068 00070 // CBuffer add 00071 00072 // Takes a pointer to memory, and how many bytes are stored there 00073 // Adds that memory to this buffer 00074 void CBuffer::Add(const void * pData, DWORD nLength) 00075 { 00076 // If the buffer isn't big enough to hold the new memory 00077 if ( m_nLength + nLength > m_nBuffer ) 00078 { 00079 // Set the buffer size to the size needed 00080 m_nBuffer = m_nLength + nLength; 00081 00082 // Make the size larger to the nearest multiple of 1024 bytes, or 1 KB 00083 m_nBuffer = ( m_nBuffer + BLOCK_SIZE - 1 ) & BLOCK_MASK; // 1-1024 becomes 1024, 1025 becomes 2048 00084 00085 // Allocate more space at the end of the memory block 00086 m_pBuffer = (BYTE*)realloc( m_pBuffer, m_nBuffer ); // This may move the block, returning a different pointer 00087 00088 } // If the buffer is larger than 512 KB, but what it needs to hold is less than 256 KB 00089 else if ( m_nBuffer > 0x80000 && m_nLength + nLength < 0x40000 ) 00090 { 00091 // Reallocate it to make it half as big 00092 m_nBuffer = 0x40000; 00093 m_pBuffer = (BYTE*)realloc( m_pBuffer, m_nBuffer ); // This may move the block, returning a different pointer 00094 } 00095 00096 // Copy the given memory into the end of the memory block 00097 CopyMemory( m_pBuffer + m_nLength, pData, nLength ); 00098 m_nLength += nLength; // Add the length of the new memory to the total length in the buffer 00099 } 00100 00102 // CBuffer insert 00103 00104 // Takes offset, a position in the memory block to insert some new memory at 00105 // Inserts the memory there, shifting anything after it further to the right 00106 void CBuffer::Insert(DWORD nOffset, const void * pData, DWORD nLength) 00107 { 00108 // If the buffer isn't big enough to hold the new memory 00109 if ( m_nLength + nLength > m_nBuffer ) 00110 { 00111 // Set the buffer size to the size needed 00112 m_nBuffer = m_nLength + nLength; 00113 00114 // Make the size larger to the nearest multiple of 1024 bytes, or 1 KB 00115 m_nBuffer = ( m_nBuffer + BLOCK_SIZE - 1 ) & BLOCK_MASK; // 1-1024 becomes 1024, 1025 becomes 2048 00116 00117 // Allocate more space at the end of the memory block 00118 m_pBuffer = (BYTE*)realloc( m_pBuffer, m_nBuffer ); // This may move the block, returning a different pointer 00119 00120 } // If the buffer is larger than 512 KB, but what it needs to hold is less than 256 KB 00121 else if ( m_nBuffer > 0x80000 && m_nLength + nLength < 0x40000 ) 00122 { 00123 // Reallocate it to make it half as big 00124 m_nBuffer = 0x40000; 00125 m_pBuffer = (BYTE*)realloc( m_pBuffer, m_nBuffer ); // This may move the block, returning a different pointer 00126 } 00127 00128 // Cut the memory block sitting in the buffer in two, slicing it at offset and shifting that part forward nLength 00129 MoveMemory( 00130 m_pBuffer + nOffset + nLength, // Destination is the offset plus the length of the memory block to insert 00131 m_pBuffer + nOffset, // Source is at the offset 00132 m_nLength - nOffset ); // Length is the size of the memory block beyond the offset 00133 00134 // Now that there is nLength of free space in the buffer at nOffset, copy the given memory to fill it 00135 CopyMemory( 00136 m_pBuffer + nOffset, // Destination is at the offset in the buffer 00137 pData, // Source is the given pointer to the memory to insert 00138 nLength ); // Length is the length of that memory 00139 00140 // Add the length of the new memory to the total length in the buffer 00141 m_nLength += nLength; 00142 } 00143 00145 // CBuffer remove 00146 00147 // Takes a number of bytes 00148 // Removes this number from the start of the buffer, shifting the memory after it to the start 00149 void CBuffer::Remove(DWORD nLength) 00150 { 00151 // Check the given length 00152 if ( nLength > m_nLength // We're being asked to remove more bytes than are stores in the buffer 00153 || nLength == 0 ) // We're being asked to remove nothing 00154 return; // Leave now 00155 00156 // Subtract the removed bytes from the count of how many are stored here 00157 m_nLength -= nLength; 00158 00159 // Shift the bytes at nLength in the buffer back up to the start of the buffer 00160 MoveMemory( 00161 m_pBuffer, // Destination is the start of the buffer 00162 m_pBuffer + nLength, // Source is nLength into the buffer 00163 m_nLength ); // Length to copy is the new adjusted length 00164 } 00165 00166 // Clears the memory from the buffer 00167 void CBuffer::Clear() 00168 { 00169 // Record that there are no bytes stored in the buffer 00170 m_nLength = 0; // Note that the buffer still has the same allocated size of m_nLength 00171 } 00172 00174 // CBuffer add utilities 00175 00176 // Takes ASCII text 00177 // Prints it into the buffer, does not write a null terminator 00178 void CBuffer::Print(LPCSTR pszText) 00179 { 00180 // If the text is blank, don't do anything 00181 if ( pszText == NULL ) return; 00182 00183 // Add the characters of the text to the buffer, each ASCII character takes up 1 byte 00184 Add( (void*)pszText, strlen( pszText ) ); // Length is 5 for "hello", this adds the characters without the null terminator 00185 } 00186 00187 // Takes Unicode text, along with the code page it uses 00188 // Converts it to ASCII and prints each ASCII character into the buffer, not printing a null terminator 00189 void CBuffer::Print(LPCWSTR pszText, UINT nCodePage) 00190 { 00191 // If the text is blank, don't do anything 00192 if ( pszText == NULL ) return; 00193 00194 // Find the number of wide characters in the Unicode text 00195 int nLength = wcslen(pszText); // Length of "hello" is 5, does not include null terminator 00196 00197 // Find out the required buffer size, in bytes, for the translated string 00198 int nBytes = WideCharToMultiByte( // Bytes required for "hello" is 5, does not include null terminator 00199 nCodePage, // Specify the code page used to perform the conversion 00200 0, // No special flags to handle unmapped characters 00201 pszText, // Wide character string to convert 00202 nLength, // The number of wide characters in that string 00203 NULL, // No output buffer given, we just want to know how long it needs to be 00204 0, 00205 NULL, // No replacement character given 00206 NULL ); // We don't want to know if a character didn't make it through the translation 00207 00208 // Make sure the buffer is big enough for this, making it larger if necessary 00209 EnsureBuffer( (DWORD)nBytes ); 00210 00211 // Convert the Unicode string into ASCII characters in the buffer 00212 WideCharToMultiByte( // Writes 5 bytes "hello", does not write a null terminator after that 00213 nCodePage, // Specify the code page used to perform the conversion 00214 0, // No special flags to handle unmapped characters 00215 pszText, // Wide character string to convert 00216 nLength, // The number of wide characters in that string 00217 (LPSTR)( m_pBuffer + m_nLength ), // Put the output ASCII characters at the end of the buffer 00218 nBytes, // There is at least this much space there 00219 NULL, // No replacement character given 00220 NULL ); // We don't want to know if a character didn't make it through the translation 00221 00222 // Add the newly written bytes to the buffer's record of how many bytes it is holding 00223 m_nLength += nBytes; 00224 } 00225 00226 // Takes another CBuffer object, and a number of bytes there to copy, or the default -1 to copy the whole thing 00227 // Moves the memory from pBuffer into this one 00228 // Returns the number of bytes moved 00229 DWORD CBuffer::AddBuffer(CBuffer* pBuffer, DWORD nLength) 00230 { 00231 // If the call specified a length, use it, otherwise use the length of pBuffer 00232 nLength = nLength < 0xFFFFFFFF ? ( min( pBuffer->m_nLength, nLength ) ) : pBuffer->m_nLength; 00233 00234 // Move nLength bytes from the start of pBuffer into this one 00235 Add( pBuffer->m_pBuffer, nLength ); // Copy the memory across 00236 pBuffer->Remove( nLength ); // Remove the memory from the source buffer 00237 00238 // Report how many bytes we moved 00239 return nLength; 00240 } 00241 00242 // Takes a pointer to some memory, and the number of bytes we can read there 00243 // Adds them to this buffer, except in reverse order 00244 void CBuffer::AddReversed(const void *pData, DWORD nLength) 00245 { 00246 // Make sure this buffer has enough memory allocated to hold another nLength bytes 00247 EnsureBuffer( nLength ); 00248 00249 // Copy nLength bytes from pData to the end of the buffer, except in reverse order 00250 ReverseBuffer( pData, m_pBuffer + m_nLength, nLength ); 00251 00252 // Record the new length 00253 m_nLength += nLength; 00254 } 00255 00256 // Takes ASCII text 00257 // Inserts it at the start of this buffer, shifting what is already here forward, does not write a null terminator 00258 void CBuffer::Prefix(LPCSTR pszText) 00259 { 00260 // If the text is blank, do nothing 00261 if ( NULL == pszText ) return; 00262 00263 // Insert the bytes of the text at 00264 Insert( // Insert memory in the middle of the filled block of a buffer, splitting the block to the right 00265 0, // Insert the bytes at position 0, the start 00266 (void*)pszText, // Insert the bytes of the ASCII text 00267 strlen( pszText ) ); // Insert each character byte, like 5 for "hello", does not insert a null terminator 00268 } 00269 00270 // Takes a number of new bytes we're about to add to this buffer 00271 // Makes sure the buffer will be big enough to hold them, allocating more memory if necessary 00272 void CBuffer::EnsureBuffer(DWORD nLength) 00273 { 00274 // If the size of the buffer minus the size filled is bigger than or big enough for the given length, do nothing 00275 if ( m_nBuffer - m_nLength >= nLength ) return; // There is enough room to write nLength bytes without allocating anything 00276 00277 // Make m_nBuffer the size of what's written plus what's requested 00278 m_nBuffer = m_nLength + nLength; 00279 00280 // Round that up to the nearest multiple of 1024, or 1 KB 00281 m_nBuffer = ( m_nBuffer + BLOCK_SIZE - 1 ) & BLOCK_MASK; 00282 00283 // Reallocate the memory block to this size 00284 m_pBuffer = (BYTE*)realloc( m_pBuffer, m_nBuffer ); // May return a different pointer 00285 } 00286 00288 // CBuffer read string helper 00289 00290 // Takes a maximum number of bytes to examine at the start of the buffer, and a code page which is ASCII by default 00291 // Reads the given number of bytes as ASCII characters, copying them into a string 00292 // Returns the text in a string, which will contain Unicode or ASCII characters depending on the compile 00293 CString CBuffer::ReadString(DWORD nBytes, UINT nCodePage) 00294 { 00295 // Make a new blank string to hold the text we find 00296 CString str; 00297 00298 // Set nSource to whichever is smaller, the number of bytes in the buffer, or the number we can look at there 00299 int nSource = (int)min( nBytes, m_nLength ); 00300 00301 // Find out how many wide characters a buffer must be able to hold to convert this text to Unicode, null terminator not included 00302 int nLength = MultiByteToWideChar( // If the bytes "hello" are in the buffer, and nSource is 5, nLength will be 5 also 00303 nCodePage, // Code page to use, CP_ACP ANSI code page for ASCII text, the default 00304 0, // No special options about difficult to translate characters 00305 (LPCSTR)m_pBuffer, // Use the start of this buffer as the source, where the ASCII text is 00306 nSource, // Convert this number of bytes there 00307 NULL, // No output buffer, we want to find out how long one must be 00308 0 ); 00309 00310 // Convert the ASCII characters at the start of this buffer to Unicode 00311 MultiByteToWideChar( // Convert ASCII text to Unicode 00312 nCodePage, // Code page to use, CP_ACP ANSI code page for ASCII text, the default 00313 0, // No special options about difficult to translate characters 00314 (LPCSTR)m_pBuffer, // Use the start of this buffer as the source, where the ASCII text is 00315 nSource, // Convert this number of bytes there 00316 str.GetBuffer( nLength ), // Get direct access to the memory buffer for the CString object, telling it to be able to hold nLength characters 00317 nLength ); // Size of the buffer in wide characters 00318 00319 // Release our direct manipulation of the CString's buffer 00320 str.ReleaseBuffer( nLength ); // Tell it how many wide characters we wrote there, null terminator not included 00321 00322 // Return the string 00323 return str; 00324 } 00325 00327 // CBuffer read line helper 00328 00329 // Takes access to a string, default peek false to move a line from the buffer to the string, and default CP_ACP to read ASCII text 00330 // Looks for bytes like "line\r\n" in the buffer, and moves them from the buffer to the string, throwing away the "\r\n" part 00331 // Returns true if a line was found and moved from the buffer to the string, false if there isn't a '\n' in the buffer right now 00332 BOOL CBuffer::ReadLine(CString& strLine, BOOL bPeek, UINT nCodePage) 00333 { 00334 // Empty the string, making it blank 00335 strLine.Empty(); 00336 00337 // If this buffer is empty, tell the caller we didn't find a complete line 00338 if ( ! m_nLength ) return FALSE; 00339 00340 // Scan down each byte in the buffer 00341 DWORD nLength; 00342 for ( nLength = 0 ; nLength < m_nLength ; nLength++ ) 00343 { 00344 // If the byte at this length is the newline character '\n', exit the loop 00345 if ( m_pBuffer[ nLength ] == '\n' ) break; 00346 } 00347 00348 // If the loop didn't find a '\n' and instead stopped because nLength grew to equal m_nLength 00349 if ( nLength >= m_nLength ) return FALSE; // There isn't an '\n' in the buffer, tell the caller we didn't find a complete line 00350 00351 // Convert the nLength ASCII characters in the buffer into wide characters in strLine 00352 int nWide = MultiByteToWideChar( nCodePage, 0, (LPCSTR)m_pBuffer, nLength, NULL, 0 ); 00353 MultiByteToWideChar( nCodePage, 0, (LPCSTR)m_pBuffer, nLength, strLine.GetBuffer( nWide ), nWide ); 00354 strLine.ReleaseBuffer( nWide ); 00355 00356 // Find the last carriage return '\r' character in the string 00357 int nCR = strLine.ReverseFind( '\r' ); // Find the distance to the last \r, "hello\r" would be 5 00358 if ( nCR >= 0 ) strLine.Truncate( nCR ); // Cut the string to that length, like "hello" 00359 00360 // Now that the line has been copied into the string, remove it and the '\n' from the buffer 00361 if ( ! bPeek ) Remove( nLength + 1 ); // Unless we're peeking, then leave it in the buffer 00362 00363 // Report that we found a line and moved it from the buffer to the string 00364 return TRUE; 00365 } 00366 00368 // CBuffer starts with helper 00369 00370 // Takes a pointer to ASCII text, and the option to remove these characters from the start of the buffer if they are found there 00371 // Looks at the bytes at the start of the buffer, and determines if they are the same as the given ASCII text 00372 // Returns true if the text matches, false if it doesn't 00373 BOOL CBuffer::StartsWith(LPCSTR pszString, BOOL bRemove) 00374 { 00375 // If the buffer isn't long enough to contain the given string, report the buffer doesn't start with it 00376 if ( m_nLength < (int)strlen( pszString ) ) return FALSE; 00377 00378 // If the first characters in the buffer don't match those in the ASCII string, return false 00379 if ( strncmp( // Returns 0 if all the characters are the same 00380 (LPCSTR)m_pBuffer, // Look at the start of the buffer as ASCII text 00381 (LPCSTR)pszString, // The given text 00382 strlen( pszString ) ) ) // Don't look too far into the buffer, we know it's long enough to hold the string 00383 return FALSE; // If one string would sort above another, the result is positive or negative 00384 00385 // If we got the option to remove the string if it matched, do it 00386 if ( bRemove ) Remove( strlen( pszString ) ); 00387 00388 // Report that the buffer does start with the given ASCII text 00389 return TRUE; 00390 } 00391 00393 // CBuffer socket receive 00394 00395 // Takes a handle to a socket 00396 // Reads in data from the socket, moving it into the buffer 00397 // Returns the number of bytes we got 00398 DWORD CBuffer::Receive(SOCKET hSocket) 00399 { 00400 // Make a local 4 KB buffer 00401 BYTE pData[TEMP_BUFFER]; 00402 00403 // Record how many bytes we get from the socket in this call to this method 00404 DWORD nTotal = 0; 00405 00406 // Loop forever 00407 while ( TRUE ) 00408 { 00409 // Move up to 4 KB of data from the socket to our pData buffer 00410 int nLength = recv( // Read data in from the socket, nLength is how many bytes we got 00411 hSocket, // The socket that is connected to a remote computer 00412 (char *)pData, // Put the data in our little local 4 KB buffer 00413 TEMP_BUFFER, // Tell recv that it has 4 KB of space there 00414 0 ); // No advanced options 00415 00416 // If we got 0 bytes, or SOCKET_ERROR -1, exit the loop 00417 if ( nLength <= 0 ) break; 00418 00419 // Copy the data from the 4 KB buffer into this CBuffer object 00420 Add( pData, nLength ); 00421 00422 // Record this method has read nLength more bytes 00423 nTotal += nLength; 00424 } 00425 00426 // Add the amount we read to the incoming bandwidth statistic, and return it 00427 Statistics.Current.Bandwidth.Incoming += nTotal; 00428 return nTotal; 00429 } 00430 00432 // CBuffer socket send 00433 00434 // Takes a handle to a socket 00435 // Sends all the data in this buffer to the remote computer at the other end of it 00436 // Returns how many bytes were sent 00437 DWORD CBuffer::Send(SOCKET hSocket) 00438 { 00439 // Record the total bytes we send in this call to this method 00440 DWORD nTotal = 0; 00441 00442 // Loop until this buffer is empty 00443 while ( m_nLength ) 00444 { 00445 // Copy the contents of this buffer into the socket 00446 int nLength = send( // Send data out through the socket, nLength will be how much was sent 00447 hSocket, // The socket that is connected to the remote computer 00448 (char *)m_pBuffer, // Send data from the start of this buffer 00449 m_nLength, // Try to send all the data in the buffer 00450 0 ); // No advanced options 00451 00452 // If no data was sent, or send returned SOCKET_ERROR -1, exit the loop 00453 if ( nLength <= 0 ) break; 00454 00455 // Remove the bytes that we copied into the socket from this buffer 00456 Remove( nLength ); 00457 00458 // Record that we sent these bytes 00459 nTotal += nLength; 00460 } 00461 00462 // Add the amount we sent to the outgoing bandwidth statistic, and return it 00463 Statistics.Current.Bandwidth.Outgoing += nTotal; 00464 return nTotal; 00465 } 00466 00468 // CBuffer deflate and inflate compression 00469 00470 // Takes an option to avoid compressing a small buffer and to make sure compressing didn't actually make it bigger 00471 // Compresses the data in this buffer in place 00472 // Returns true if the data is compressed, false if there was an error 00473 BOOL CBuffer::Deflate(BOOL bIfSmaller) 00474 { 00475 // If the caller requested we check for small buffers, and this one contains less than 45 bytes, return false 00476 if ( bIfSmaller && m_nLength < 45 ) return FALSE; // This buffer is too small for compression to work 00477 00478 // Compress this buffer 00479 DWORD nCompress = 0; // Compress will write the size of the buffer it allocates and returns in this variable 00480 BYTE* pCompress = CZLib::Compress( m_pBuffer, m_nLength, &nCompress ); // Returns a buffer we must free 00481 if ( ! pCompress ) return FALSE; // Compress had an error 00482 00483 // If compressing the data actually made it bigger, and we were told to watch for this happening 00484 if ( bIfSmaller && nCompress >= m_nLength ) 00485 { 00486 // Delete the buffer that Compress allocated, and report error 00487 delete [] pCompress; 00488 return FALSE; 00489 } 00490 00491 // Move the compressed data from the buffer Compress returned to this one 00492 m_nLength = 0; // Record that there is no memory stored in this buffer 00493 Add( pCompress, nCompress ); // Copy the compressed data into this buffer 00494 delete [] pCompress; // Free the memory that Compress allocated 00495 return TRUE; // Report success 00496 } 00497 00498 // Takes the size we think the data will be when decompressed, or 0 if we don't know 00499 // Decompresses the data in this buffer in place 00500 // Returns true if the data is decompressed, false if there was an error 00501 BOOL CBuffer::Inflate(DWORD nSuggest) 00502 { 00503 // The bytes in this buffer are compressed, decompress them 00504 DWORD nCompress = 0; // Decompress will write the size of the buffer it allocates and returns in this variable 00505 BYTE* pCompress = CZLib::Decompress( m_pBuffer, m_nLength, &nCompress, nSuggest ); // Returns a buffer we must free 00506 if ( pCompress == NULL ) return FALSE; // Decompress had an error 00507 00508 // Move the decompressed data from the buffer Decompress returned to this one 00509 m_nLength = 0; // Record that there is no memory stored in this buffer 00510 Add( pCompress, nCompress ); // Copy the decompressed data into this buffer 00511 delete [] pCompress; // Free the memory that Decompress allocated 00512 return TRUE; // Report success 00513 } 00514 00515 // If the contents of this buffer are between headers and compressed with gzip, this method can remove all that 00516 // Returns false on error 00517 BOOL CBuffer::Ungzip() 00518 { 00519 // Make sure there are at least 10 bytes in this buffer 00520 if ( m_nLength < 10 ) return FALSE; 00521 00522 // Make sure the first 3 bytes are not 1f8b08 00523 if ( m_pBuffer[0] != 0x1F || m_pBuffer[1] != 0x8B || m_pBuffer[2] != 8 ) return FALSE; 00524 00525 // At a distance of 3 bytes into the buffer, read the byte there and call it nFlags 00526 BYTE nFlags = m_pBuffer[3]; 00527 00528 // Remove the first 10 bytes of the buffer 00529 Remove( 10 ); 00530 00531 // If there is a 1 in position 0000 0100 in the flags byte 00532 if ( nFlags & 0x04 ) 00533 { 00534 // Make sure the buffer has 2 or more bytes 00535 if ( m_nLength < 2 ) return FALSE; 00536 00537 // Look at the first 2 bytes in the buffer as a word, this says how long the data it beyond it 00538 WORD nLen = *(WORD*)m_pBuffer; 00539 00540 // If the buffer has less data than it should, return false 00541 if ( (int)m_nLength < (int)nLen + 2 ) return FALSE; 00542 00543 // Remove the length word and the length it describes from the front of the buffer 00544 Remove( 2 + nLen ); 00545 } 00546 00547 // If there is a 1 in position 0000 1000 in the flags byte 00548 if ( nFlags & 0x08 ) 00549 { 00550 // Loop until after we remove a 0 byte from the buffer 00551 for ( ;; ) 00552 { 00553 // If the buffer is empty, return false 00554 if ( m_nLength == 0 ) return FALSE; 00555 00556 // Move the first byte of the buffer into an int 00557 int nChar = m_pBuffer[0]; // Copy one byte from the start of the buffer into an int named nChar 00558 Remove( 1 ); // Remove that first byte from the buffer 00559 00560 // If we just removed a 0 byte, exit the loop 00561 if ( nChar == 0 ) break; 00562 } 00563 } 00564 00565 // If there is a 1 in position 0001 0000 in the flags byte 00566 if ( nFlags & 0x10 ) 00567 { 00568 // Loop until after we remove a 0 byte from the buffer 00569 for ( ;; ) 00570 { 00571 // If the buffer is empty, return false 00572 if ( m_nLength == 0 ) return FALSE; 00573 00574 // Move the first byte of the buffer into an int 00575 int nChar = m_pBuffer[0]; // Copy one byte from the start of the buffer into an int named nChar 00576 Remove( 1 ); // Remove that first byte from the buffer 00577 00578 // If we just removed a 0 byte, exit the loop 00579 if ( nChar == 0 ) break; 00580 } 00581 } 00582 00583 // If there is a 1 in position 0000 0010 in the flags byte 00584 if ( nFlags & 0x02 ) 00585 { 00586 // Make sure the buffer has at least 2 bytes, and then remove them 00587 if ( m_nLength < 2 ) return FALSE; 00588 Remove( 2 ); 00589 } 00590 00591 // After removing all that header information from the front, remove the last 8 bytes from the end 00592 if ( m_nLength <= 8 ) return FALSE; // Make sure the buffer has more than 8 bytes 00593 m_nLength -= 8; // Remove the last 8 bytes in the buffer 00594 00595 // Setup a z_stream structure to perform a raw inflate 00596 z_stream pStream; 00597 ZeroMemory( &pStream, sizeof(pStream) ); 00598 if ( Z_OK != inflateInit2( // Initialize a stream inflation with more options than just inflateInit 00599 &pStream, // Stream structure to initialize 00600 -MAX_WBITS ) ) { // Window bits value of -15 to perform a raw inflate 00601 00602 // The Zlib function inflateInit2 returned something other than Z_OK, report error 00603 return FALSE; 00604 } 00605 00606 // Make a new buffer for the output 00607 CBuffer pOutput; 00608 pOutput.EnsureBuffer( m_nLength * 6 ); // Guess that inflating the data won't make it more than 6 times as big 00609 00610 // Tell the z_stream structure where to work 00611 pStream.next_in = m_pBuffer; // Decompress the memory here 00612 pStream.avail_in = m_nLength; // There is this much of it 00613 pStream.next_out = pOutput.m_pBuffer; // Write decompressed data here 00614 pStream.avail_out = pOutput.m_nBuffer; // Tell ZLib it has this much space, it make this smaller to show how much space is left 00615 00616 // Call ZLib inflate to decompress all the data, and see if it returns Z_STREAM_END 00617 BOOL bSuccess = ( Z_STREAM_END == inflate( &pStream, Z_FINISH ) ); 00618 00619 // The inflate call returned Z_STREAM_END 00620 if ( bSuccess ) 00621 { 00622 // Move the decompressed data from the output buffer into this one 00623 Clear(); // Record there are no bytes stored here, doesn't change the allocated block size 00624 Add(pOutput.m_pBuffer, // Add the memory at the start of the output buffer 00625 pOutput.m_nBuffer // The amount of space the buffer had when we gave it to Zlib 00626 - pStream.avail_out ); // Minus the amount it said it left, this is the number of bytes it wrote 00627 00628 // Close ZLib and report success 00629 inflateEnd( &pStream ); 00630 return TRUE; 00631 00632 } // The inflate call returned something else 00633 else 00634 { 00635 // Close ZLib and report error 00636 inflateEnd( &pStream ); 00637 return FALSE; 00638 } 00639 } 00640 00642 // CBuffer reverse buffer 00643 00644 // This method is static, which means you can call it like CBuffer::ReverseBuffer() without having a CBuffer object at all 00645 // Takes pointers to input memory and an output buffer, and a length, which is both the memory in input and the space in output 00646 // Copies the bytes from input to output, but in reverse order 00647 void CBuffer::ReverseBuffer(const void* pInput, void* pOutput, DWORD nLength) 00648 { 00649 // Point pInputWords at the end of the input memory block 00650 const DWORD* pInputWords = (const DWORD*)( (const BYTE*)pInput + nLength ); // This is a DWORD pointer, so it will move in steps of 4 00651 00652 // Point pOutputWords at the start of the output buffer 00653 DWORD* pOutputWords = (DWORD*)( pOutput ); 00654 00655 // Make a new local DWORD called nTemp, and request that Visual Studio place it in a machine register 00656 register DWORD nTemp; // The register keyword asks that nTemp be a machine register, making it really fast 00657 00658 // Loop while nLength is bigger than 4, grabbing bytes 4 at a time and reversing them 00659 while ( nLength > 4 ) 00660 { 00661 // Move pInputWords back 4 bytes, then copy the 4 bytes there into nTemp, the fast machine register DWORD 00662 nTemp = *--pInputWords; 00663 00664 // Have SWAP_LONG reverse the order of the 4 bytes, copy them under pOutputWords, and then move that pointer 4 bytes forward 00665 *pOutputWords++ = SWAP_LONG( nTemp ); // If nTemp is "ABCD", SWAP_LONG( nTemp ) will be "DCBA", bit order is not changed 00666 00667 // We've just reverse 4 bytes, subtract the length to reflect this 00668 nLength -= 4; 00669 } 00670 00671 // If there are still some input bytes to add reversed 00672 if ( nLength ) 00673 { 00674 // Point pInputBytes and pOutputBytes at the same places 00675 const BYTE* pInputBytes = (const BYTE*)pInputWords; // This is a byte pointer, so it will move in steps of 1 00676 BYTE* pOutputBytes = (BYTE*)pOutputWords; 00677 00678 // Loop until there are no more bytes to copy over 00679 while ( nLength-- ) 00680 { 00681 // Move pInputBytes back to grab a byte, copy it under pOutputBytes, then move pOutputBytes forward 00682 *pOutputBytes++ = *--pInputBytes; 00683 } 00684 } 00685 } 00686 00688 // CBuffer DIME handling 00689 00690 // DIME is a specification for sending and receiving SOAP messages along with additional attachments, like binary files or XML fragments 00691 // Takes information to create a DIME message 00692 // Composes the DIME message and writes it into this buffer 00693 void CBuffer::WriteDIME( 00694 DWORD nFlags, // 0, 1, or 2 00695 LPCSTR pszID, // Blank, or a GUID in hexadecimal encoding 00696 LPCSTR pszType, // "text/xml" or a URI to an XML specification 00697 LPCVOID pBody, // The XML fragment we're wrapping 00698 DWORD nBody) // How long it is 00699 { 00700 // Format lengths into the bytes of the DIME header 00701 EnsureBuffer( 12 ); // Make sure this buffer has at least 12 bytes of space 00702 BYTE* pOut = m_pBuffer + m_nLength; // Point pOut at the end of the memory block in this buffer 00703 *pOut++ = 0x08 | ( nFlags & 1 ? 4 : 0 ) | ( nFlags & 2 ? 2 : 0 ); // *pOut++ = 0x08 sets the byte at pOut and then moves the pointer forward 00704 *pOut++ = strchr( pszType, ':' ) ? 0x20 : 0x10; 00705 *pOut++ = 0x00; *pOut++ = 0x00; 00706 *pOut++ = ( ( strlen( pszID ) & 0xFF00 ) >> 8 ); 00707 *pOut++ = ( strlen( pszID ) & 0xFF ); 00708 *pOut++ = ( ( strlen( pszType ) & 0xFF00 ) >> 8 ); 00709 *pOut++ = ( strlen( pszType ) & 0xFF ); 00710 *pOut++ = (BYTE)( ( nBody & 0xFF000000 ) >> 24 ); 00711 *pOut++ = (BYTE)( ( nBody & 0x00FF0000 ) >> 16 ); 00712 *pOut++ = (BYTE)( ( nBody & 0x0000FF00 ) >> 8 ); 00713 *pOut++ = (BYTE)( nBody & 0x000000FF ); 00714 m_nLength += 12; // Record that we wrote 12 bytes, but we really only wrote 11 (do) 00715 00716 // Print pszID, which is blank or a GUID in hexadecimal encoding, and bytes of 0 until the total length we added is a multiple of 4 00717 Print( pszID ); 00718 DWORD nPad; 00719 for ( nPad = strlen( pszID ) ; nPad & 3 ; nPad++ ) Add( "", 1 ); // If we added "a", add "000" to get to the next group of 4 00720 00721 // Print pszType, which is "text/xml" or a URI to an XML specification, and bytes of 0 until the total length we added is a multiple of 4 00722 Print( pszType ); 00723 for ( nPad = strlen( pszType ) ; nPad & 3 ; nPad++ ) Add( "", 1 ); // If we added "abcdef", add "00" to get to the next group of 4 00724 00725 // If there is body text 00726 if ( pBody != NULL ) 00727 { 00728 // Add it, followed by bytes of 0 until the total length we added is a multiple of 4 00729 Add( pBody, nBody ); 00730 for ( nPad = nBody ; nPad & 3 ; nPad++ ) Add( "", 1 ); 00731 } 00732 } 00733 00734 // DIME is a specification for sending and receiving SOAP messages along with additional attachments, like binary files or XML fragments 00735 // If there is a DIME message sitting in this buffer, this method can read it 00736 // Takes DWORD and CString pointers to fill with information from the DIME message 00737 // Returns false if the DIME message wasn't formatted correctly 00738 BOOL CBuffer::ReadDIME( 00739 DWORD* pnFlags, // Writes the flags byte from the DIME message 00740 CString* psID, // Writes a GUID in hexadecimal encoding from the DIME message 00741 CString* psType, // Writes "text/xml" or a URI to an XML specification 00742 DWORD* pnBody) // Writes how long the body of the DIME message is 00743 { 00744 // Make sure the buffer has at least 12 bytes 00745 if ( m_nLength < 12 ) return FALSE; 00746 00747 // Point pIn at the start of this buffer 00748 BYTE* pIn = m_pBuffer; 00749 00750 // The first 5 bits of the first byte, 00000---, must not be 00001--- 00751 if ( ( *pIn & 0xF8 ) != 0x08 ) return FALSE; 00752 00753 // If this method was passed a pnFlags DWORD 00754 if ( pnFlags != NULL ) 00755 { 00756 // Write it for the caller 00757 *pnFlags = 0; // Start it out as 0 00758 if ( *pIn & 4 ) *pnFlags |= 1; // If the first byte in the buffer has a bit here -----1--, put one here -------1 in pnFlags 00759 if ( *pIn & 2 ) *pnFlags |= 2; // If the first byte in the buffer has a bit here ------1-, put one here ------1- in pnFlags 00760 } 00761 00762 // Move the pIn pointer to the second byte in the buffer, and make sure it's not 00001010 or 00010100 00763 pIn++; 00764 if ( *pIn != 0x10 && *pIn != 0x20 ) return FALSE; 00765 00766 // Make sure bytes 3 and 4 in the buffer aren't 0, and move pIn a distance of 4 bytes into the buffer, pointing at the 5th byte 00767 pIn++; 00768 if ( *pIn++ != 0x00 ) return FALSE; 00769 if ( *pIn++ != 0x00 ) return FALSE; 00770 00771 // Read nID, nType, and pnBody from the buffer, and move the pointer forward 8 bytes 00772 ASSERT( pnBody != NULL ); // Make sure the caller gave us access to a DWORD to write the body length 00773 WORD nID = ( pIn[0] << 8 ) + pIn[1]; pIn += 2; 00774 WORD nType = ( pIn[0] << 8 ) + pIn[1]; pIn += 2; 00775 *pnBody = ( pIn[0] << 24 ) + ( pIn[1] << 16 ) + ( pIn[2] << 8 ) + pIn[3]; // Write the body length in the DWORD from the caller 00776 pIn += 4; // Move forward another 4 bytes to total 8 bytes for this section 00777 00778 // Skip forward a distance determined by the lengths we just read 00779 DWORD nSkip = 12 + ( ( nID + 3 ) & ~3 ) + ( ( nType + 3 ) & ~3 ); 00780 if ( m_nLength < nSkip + ( ( *pnBody + 3 ) & ~3 ) ) return FALSE; // Make sure the buffer is big enough to skip this far forward 00781 00782 // Read psID, a GUID in hexadecimal encoding 00783 ASSERT( psID != NULL ); // Make sure the caller gave us access to a string to write the guid in hexadecimal encoding 00784 LPSTR pszID = new CHAR[ nID + 1 ]; // Make a new buffer for the text 00785 CopyMemory( pszID, pIn, nID ); // Copy the text into the buffer 00786 pszID[ nID ] = 0; // Set a null terminator 00787 *psID = pszID; // Copy the text into the string 00788 delete [] pszID; // Delete our temporary buffer 00789 pIn += nID; // Move pIn forward beyond the psID text 00790 while ( nID++ & 3 ) pIn++; // Move pIn forward to the next boundary of 4 bytes 00791 00792 // Read psType, a GUID in hexadecimal encoding 00793 ASSERT( psType != NULL ); // Make sure the caller gave us access to a string to write the message body 00794 LPSTR pszType = new CHAR[ nType + 1 ]; // Make a new buffer for the text 00795 CopyMemory( pszType, pIn, nType ); // Copy the text into the buffer 00796 pszType[ nType ] = 0; // Set a null terminator 00797 *psType = pszType; // Copy the text into the string 00798 delete [] pszType; // Delete our temporary buffer 00799 pIn += nType; // Move pIn forward beyond the pszType text 00800 while ( nType++ & 3 ) pIn++; // Move pIn forward to the next boundary of 4 bytes 00801 00802 // Remove the first part of the DIME message from the buffer, and report success 00803 Remove( nSkip ); 00804 return TRUE; 00805 }