184 lines
6.8 KiB
C++
184 lines
6.8 KiB
C++
|
/*
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_START@
|
||
|
*
|
||
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original Code
|
||
|
* as defined in and that are subject to the Apple Public Source License
|
||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||
|
* compliance with the License. Please obtain a copy of the License at
|
||
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||
|
* file.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||
|
* Please see the License for the specific language governing rights and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_END@
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
File: UserAgentParser.cpp
|
||
|
|
||
|
Contains: Parse the User-Agent: entry of an RTSP request.
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "StringParser.h"
|
||
|
#include "StringFormatter.h"
|
||
|
#include "StrPtrLen.h"
|
||
|
#include "UserAgentParser.h"
|
||
|
|
||
|
UserAgentParser::UserAgentFields UserAgentParser::sFieldIDs[] =
|
||
|
{ /* fAttrName, len, id */
|
||
|
{ "qtid", strlen("qtid"), eQtid },
|
||
|
{ "qtver", strlen("qtver"), eQtver },
|
||
|
{ "lang", strlen("lang"), eLang },
|
||
|
{ "os", strlen("os"), eOs },
|
||
|
{ "osver", strlen("osver"), eOsver },
|
||
|
{ "cpu", strlen("cpu"), eCpu }
|
||
|
};
|
||
|
|
||
|
|
||
|
UInt8 UserAgentParser::sEOLWhitespaceEqualMask[] =
|
||
|
{
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //0-9 // \t is a stop
|
||
|
1, 0, 0, 1, 0, 0, 0, 0, 0, 0, //10-19 //'\r' & '\n' are stop conditions
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
|
||
|
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, //30-39 ' ' is a stop
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40-49
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //50-59
|
||
|
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, //60-69 '=' is a stop
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70-79
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80-89
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90-99
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //100-109
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //110-119
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //120-129
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //130-139
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140-149
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150-159
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160-169
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170-179
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180-189
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190-199
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200-209
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210-219
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220-229
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230-239
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240-249
|
||
|
0, 0, 0, 0, 0, 0 //250-255
|
||
|
};
|
||
|
|
||
|
UInt8 UserAgentParser::sEOLSemicolonCloseParenMask[] =
|
||
|
{
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //0-9 // \t is a stop
|
||
|
1, 0, 0, 1, 0, 0, 0, 0, 0, 0, //10-19 //'\r' & '\n' are stop conditions
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30-39
|
||
|
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, //40-49 ')' is a stop
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //50-59 ';' is a stop
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //60-69
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70-79
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80-89
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90-99
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //100-109
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //110-119
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //120-129
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //130-139
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140-149
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150-159
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160-169
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170-179
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180-189
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190-199
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200-209
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210-219
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220-229
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230-239
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240-249
|
||
|
0, 0, 0, 0, 0, 0 //250-255
|
||
|
};
|
||
|
|
||
|
|
||
|
void UserAgentParser::Parse(StrPtrLen *inStream)
|
||
|
{
|
||
|
StrPtrLen tempID;
|
||
|
StrPtrLen tempData;
|
||
|
StringParser parser(inStream);
|
||
|
StrPtrLen startFields;
|
||
|
|
||
|
|
||
|
memset(&fFieldData,0,sizeof(fFieldData) );
|
||
|
|
||
|
parser.ConsumeUntil(&startFields, '(' ); // search for '(' if not found does nothing
|
||
|
|
||
|
// parse through everything between the '(' and ')'.
|
||
|
while (startFields.Len != 0)
|
||
|
{
|
||
|
//stop when we reach an empty line.
|
||
|
tempID.Set(NULL,0);
|
||
|
tempData.Set(NULL,0);
|
||
|
|
||
|
parser.ConsumeLength(NULL, 1); // step past '(' or ';' if not found or at end of line does nothing
|
||
|
parser.ConsumeWhitespace(); // search for non-white space if not found does nothing
|
||
|
parser.ConsumeUntil(&tempID, sEOLWhitespaceEqualMask ); // search for end of id (whitespace or =)if not found does nothing
|
||
|
if (tempID.Len == 0) break;
|
||
|
|
||
|
parser.ConsumeUntil(NULL, '=' ); // find the '='
|
||
|
parser.ConsumeLength(NULL, 1); // step past if not found or at end of line does nothing
|
||
|
parser.ConsumeUntil(&tempData, sEOLSemicolonCloseParenMask ); // search for end of data if not found does nothing
|
||
|
if (tempData.Len == 0) break;
|
||
|
|
||
|
StrPtrLen testID;
|
||
|
UInt32 fieldID;
|
||
|
for (short testField = 0; testField < UserAgentParser::eNumAttributes; testField++)
|
||
|
{
|
||
|
testID.Set(sFieldIDs[testField].fFieldName,sFieldIDs[testField].fLen);
|
||
|
fieldID = sFieldIDs[testField].fID;
|
||
|
if ( (fFieldData[fieldID].fFound == false) && testID.Equal(tempID) )
|
||
|
{
|
||
|
fFieldData[fieldID].fData = tempData;
|
||
|
fFieldData[fieldID].fFound = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
// If we parsed the OS field but not the OSVer field then check and see if
|
||
|
// the OS field contains the OS version. If it does copy it from there.
|
||
|
// (e.g. 'os=Mac%209.2.2' or 'os=Windows%20NT%204.0'.)
|
||
|
if (fFieldData[eOs].fFound && !fFieldData[eOsver].fFound)
|
||
|
{
|
||
|
UInt16 len = (UInt16)fFieldData[eOs].fData.Len;
|
||
|
char* cp = (char*)fFieldData[eOs].fData.Ptr;
|
||
|
// skip up to the blank space if it exists.
|
||
|
// (i.e. the blank is URL encoded as '%20')
|
||
|
while(*cp != '%')
|
||
|
{
|
||
|
len--;
|
||
|
if (*cp == '\0' || len == 0)
|
||
|
{
|
||
|
// no blank space...so we're all done.
|
||
|
return;
|
||
|
}
|
||
|
cp++;
|
||
|
}
|
||
|
// skip over the blank space.
|
||
|
cp += 3; len -= 3;
|
||
|
// the remaining string is the OS version.
|
||
|
fFieldData[eOsver].fData.Set(cp, len);
|
||
|
fFieldData[eOsver].fFound = true;
|
||
|
// and truncate the version from the OS field.
|
||
|
fFieldData[eOs].fData.Len -= len+3;
|
||
|
}
|
||
|
}
|