inline Bool16 HasAuthentication(StringParser *theFullRequestPtr, StrPtrLen* namePtr, StrPtrLen* passwordPtr, StrPtrLen* outAuthTypePtr) { // Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== Bool16 hasAuthentication = false; StrPtrLen strPtr; StrPtrLen authType; StrPtrLen authString; while (theFullRequestPtr->GetDataRemaining() > 0) { theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntilWhitespace(&strPtr); if ( strPtr.Len == 0 || !strPtr.Equal(StrPtrLen("Authorization:")) ) continue; theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntilWhitespace(&authType); if ( authType.Len == 0 ) continue; theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntil(&authString, StringParser::sEOLMask); if ( authString.Len == 0 ) continue; if (outAuthTypePtr != NULL) outAuthTypePtr->Set(authType.Ptr, authType.Len); if (authType.Equal(StrPtrLen("Basic") ) ) { (void) ParseAuthNameAndPassword(&authString,namePtr, passwordPtr); if (namePtr->Len == 0) continue; hasAuthentication = true; break; } else if (authType.Equal(sAuthRef) ) { namePtr->Set(NULL,0); passwordPtr->Set(authString.Ptr, authString.Len); hasAuthentication = true; break; } }; return hasAuthentication; }
Bool16 IsAuthentic(QTSS_Filter_Params* inParams,StringParser *fullRequestPtr) { Bool16 isAuthentic = false; if (!sAuthenticationEnabled) // no authentication { isAuthentic = true; } else // must authenticate { StrPtrLen theClientIPAddressStr; (void) QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddrStr, 0, (void**)&theClientIPAddressStr.Ptr, &theClientIPAddressStr.Len); Bool16 isLocal = IPComponentStr(&theClientIPAddressStr).IsLocal(); StrPtrLen authenticateName; StrPtrLen authenticatePassword; StrPtrLen authType; Bool16 hasAuthentication = HasAuthentication(fullRequestPtr,&authenticateName,&authenticatePassword, &authType); if (hasAuthentication) { if (authType.Equal(sAuthRef)) { if (isLocal) isAuthentic = OSXAuthenticate(&authenticatePassword); } else isAuthentic = Authenticate(inParams->inRTSPRequest, &authenticateName,&authenticatePassword); } } // if (isAuthentic) // isAuthentic = AuthorizeAdminRequest(inParams->inRTSPRequest); (void) QTSS_SetValue(inParams->inRTSPRequest, sAuthenticatedID, 0, (void*)&isAuthentic, sizeof(isAuthentic)); return isAuthentic; }
void QTSServerPrefs::UpdateAuthScheme() { static StrPtrLen sNoAuthScheme("none"); static StrPtrLen sBasicAuthScheme("basic"); static StrPtrLen sDigestAuthScheme("digest"); // Get the auth scheme attribute StrPtrLen* theAuthScheme = this->GetValue(qtssPrefsAuthenticationScheme); if (theAuthScheme->Equal(sNoAuthScheme)) fAuthScheme = qtssAuthNone; else if (theAuthScheme->Equal(sBasicAuthScheme)) fAuthScheme = qtssAuthBasic; else if (theAuthScheme->Equal(sDigestAuthScheme)) fAuthScheme = qtssAuthDigest; }
QTSS_Error HTTPRequest::ParseRequestLine(StringParser* parser) { // Get the method - If the method is not one of the defined methods // then it doesn't return an error but sets fMethod to httpIllegalMethod StrPtrLen theParsedData; parser->ConsumeWord(&theParsedData); fMethod = HTTPProtocol::GetMethod(&theParsedData); //还有可能是HTTP Response类型 if((fMethod == httpIllegalMethod) && theParsedData.Equal("HTTP")) { parser->ConsumeUntilWhitespace();//过滤掉HTTP/1.1 parser->ConsumeUntilDigit(NULL); UInt32 statusCode = parser->ConsumeInteger(NULL); if( statusCode != 0 ) { fHTTPType = httpResponseType; parser->ConsumeWhitespace(); parser->ConsumeUntilWhitespace(NULL); // Go past the end of line if (!parser->ExpectEOL()) { fStatusCode = httpBadRequest; return QTSS_BadArgument; // Request line is not properly formatted! } return QTSS_NoErr; } } // Consume whitespace parser->ConsumeWhitespace(); // Parse the URI - If it fails returns an error after setting // the fStatusCode to the appropriate error code QTSS_Error err = ParseURI(parser); if (err != QTSS_NoErr) return err; // Consume whitespace parser->ConsumeWhitespace(); // If there is a version, consume the version string StrPtrLen versionStr; parser->ConsumeUntil(&versionStr, StringParser::sEOLMask); // Check the version if (versionStr.Len > 0) fVersion = HTTPProtocol::GetVersion(&versionStr); // Go past the end of line if (!parser->ExpectEOL()) { fStatusCode = httpBadRequest; return QTSS_BadArgument; // Request line is not properly formatted! } return QTSS_NoErr; }
inline Bool16 IsAdminRequest(StringParser *theFullRequestPtr) { Bool16 handleRequest = false; if (theFullRequestPtr != NULL) do { StrPtrLen strPtr; theFullRequestPtr->ConsumeWord(&strPtr); if ( !strPtr.Equal(StrPtrLen("GET")) ) break; //it's a "Get" request theFullRequestPtr->ConsumeWhitespace(); if ( !theFullRequestPtr->Expect('/') ) break; theFullRequestPtr->ConsumeWord(&strPtr); if ( strPtr.Len == 0 || !strPtr.Equal(StrPtrLen("modules") ) ) break; if (!theFullRequestPtr->Expect('/') ) break; theFullRequestPtr->ConsumeWord(&strPtr); if ( strPtr.Len == 0 || !strPtr.Equal(StrPtrLen("admin") ) ) break; handleRequest = true; } while (false); return handleRequest; }
// Parse out the URL from the HTTP GET line. Bool16 ParseURL(StrPtrLen& theRequest, char* outURL, UInt16 maxlen) { StringParser reqParse(&theRequest); StrPtrLen strPtr; ::memset(outURL, 0, maxlen); reqParse.ConsumeWord(&strPtr); if ( !strPtr.Equal(StrPtrLen("GET")) ) { return false; } reqParse.ConsumeWhitespace(); reqParse.ConsumeUntilWhitespace(&strPtr); if (strPtr.Len == 0) return false; else if ((UInt16)strPtr.Len > maxlen-1) strPtr.Len = maxlen-1; ::memcpy(outURL, strPtr.Ptr, strPtr.Len); return true; }
QTSS_ModulePrefsObject QTSSModuleUtils::GetModuleObjectByName(const StrPtrLen& inModuleName) { QTSS_ModuleObject theModule = NULL; UInt32 theLen = sizeof(theModule); for (int x = 0; QTSS_GetValue(sServer, qtssSvrModuleObjects, x, &theModule, &theLen) == QTSS_NoErr; x++) { Assert(theModule != NULL); Assert(theLen == sizeof(theModule)); StrPtrLen theName; QTSS_Error theErr = QTSS_GetValuePtr(theModule, qtssModName, 0, (void**)(void*)&theName.Ptr, &theName.Len); Assert(theErr == QTSS_NoErr); if (inModuleName.Equal(theName)) return theModule; #if DEBUG theModule = NULL; theLen = sizeof(theModule); #endif } return NULL; }
void SDPSourceInfo::Parse(char* sdpData, UInt32 sdpLen) { // // There are some situations in which Parse can be called twice. // If that happens, just return and don't do anything the second time. if (fSDPData.Ptr != NULL) return; Assert(fStreamArray == NULL); char *sdpDataCopy = new char[sdpLen]; Assert(sdpDataCopy != NULL); memcpy(sdpDataCopy,sdpData, sdpLen); fSDPData.Set(sdpDataCopy, sdpLen); // If there is no trackID information in this SDP, we make the track IDs start // at 1 -> N UInt32 currentTrack = 1; bool hasGlobalStreamInfo = false; StreamInfo theGlobalStreamInfo; //needed if there is one c= header independent of //individual streams StrPtrLen sdpLine; StringParser trackCounter(&fSDPData); StringParser sdpParser(&fSDPData); UInt32 theStreamIndex = 0; //walk through the SDP, counting up the number of tracks // Repeat until there's no more data in the SDP while (trackCounter.GetDataRemaining() > 0) { //each 'm' line in the SDP file corresponds to another track. trackCounter.GetThruEOL(&sdpLine); if ((sdpLine.Len > 0) && (sdpLine.Ptr[0] == 'm')) fNumStreams++; } //We should scale the # of StreamInfos to the # of trax, but we can't because //of an annoying compiler bug... fStreamArray = new StreamInfo[fNumStreams]; ::memset(fStreamArray, 0, sizeof(StreamInfo) * fNumStreams); // set the default destination as our default IP address and set the default ttl theGlobalStreamInfo.fDestIPAddr = INADDR_ANY; theGlobalStreamInfo.fTimeToLive = kDefaultTTL; //Set bufferdelay to default of 3 theGlobalStreamInfo.fBufferDelay = (Float32) eDefaultBufferDelay; //Now actually get all the data on all the streams while (sdpParser.GetDataRemaining() > 0) { sdpParser.GetThruEOL(&sdpLine); if (sdpLine.Len == 0) continue;//skip over any blank lines switch (*sdpLine.Ptr) { case 't': { StringParser mParser(&sdpLine); mParser.ConsumeUntil(NULL, StringParser::sDigitMask); UInt32 ntpStart = mParser.ConsumeInteger(NULL); mParser.ConsumeUntil(NULL, StringParser::sDigitMask); UInt32 ntpEnd = mParser.ConsumeInteger(NULL); SetActiveNTPTimes(ntpStart,ntpEnd); } break; case 'm': { if (hasGlobalStreamInfo) { fStreamArray[theStreamIndex].fDestIPAddr = theGlobalStreamInfo.fDestIPAddr; fStreamArray[theStreamIndex].fTimeToLive = theGlobalStreamInfo.fTimeToLive; } fStreamArray[theStreamIndex].fTrackID = currentTrack; currentTrack++; StringParser mParser(&sdpLine); //find out what type of track this is mParser.ConsumeLength(NULL, 2);//go past 'm=' StrPtrLen theStreamType; mParser.ConsumeWord(&theStreamType); if (theStreamType.Equal(sVideoStr)) fStreamArray[theStreamIndex].fPayloadType = qtssVideoPayloadType; else if (theStreamType.Equal(sAudioStr)) fStreamArray[theStreamIndex].fPayloadType = qtssAudioPayloadType; //find the port for this stream mParser.ConsumeUntil(NULL, StringParser::sDigitMask); SInt32 tempPort = mParser.ConsumeInteger(NULL); if ((tempPort > 0) && (tempPort < 65536)) fStreamArray[theStreamIndex].fPort = (UInt16) tempPort; // find out whether this is TCP or UDP mParser.ConsumeWhitespace(); StrPtrLen transportID; mParser.ConsumeWord(&transportID); static const StrPtrLen kTCPTransportStr("RTP/AVP/TCP"); if (transportID.Equal(kTCPTransportStr)) fStreamArray[theStreamIndex].fIsTCP = true; theStreamIndex++; } break; case 'a': { StringParser aParser(&sdpLine); aParser.ConsumeLength(NULL, 2);//go past 'a=' StrPtrLen aLineType; aParser.ConsumeWord(&aLineType); if (aLineType.Equal(sBroadcastControlStr)) { // found a control line for the broadcast (delete at time or delete at end of broadcast/server startup) // qtss_printf("found =%s\n",sBroadcastControlStr); aParser.ConsumeUntil(NULL,StringParser::sWordMask); StrPtrLen sessionControlType; aParser.ConsumeWord(&sessionControlType); if (sessionControlType.Equal(sAutoDisconnect)) { fSessionControlType = kRTSPSessionControl; } else if (sessionControlType.Equal(sAutoDisconnectTime)) { fSessionControlType = kSDPTimeControl; } } //if we haven't even hit an 'm' line yet, just ignore all 'a' lines if (theStreamIndex == 0) break; if (aLineType.Equal(sRtpMapStr)) { //mark the codec type if this line has a codec name on it. If we already //have a codec type for this track, just ignore this line if ((fStreamArray[theStreamIndex - 1].fPayloadName.Len == 0) && (aParser.GetThru(NULL, ' '))) { StrPtrLen payloadNameFromParser; (void)aParser.GetThruEOL(&payloadNameFromParser); char* temp = payloadNameFromParser.GetAsCString(); // qtss_printf("payloadNameFromParser (%x) = %s\n", temp, temp); (fStreamArray[theStreamIndex - 1].fPayloadName).Set(temp, payloadNameFromParser.Len); // qtss_printf("%s\n", fStreamArray[theStreamIndex - 1].fPayloadName.Ptr); } } else if (aLineType.Equal(sControlStr)) { // Modify By EasyDarwin //if ((fStreamArray[theStreamIndex - 1].fTrackName.Len == 0) && // (aParser.GetThru(NULL, ' '))) { StrPtrLen trackNameFromParser; aParser.ConsumeUntil(NULL,':'); aParser.ConsumeLength(NULL,1); aParser.GetThruEOL(&trackNameFromParser); char* temp = trackNameFromParser.GetAsCString(); // qtss_printf("trackNameFromParser (%x) = %s\n", temp, temp); (fStreamArray[theStreamIndex - 1].fTrackName).Set(temp, trackNameFromParser.Len); // qtss_printf("%s\n", fStreamArray[theStreamIndex - 1].fTrackName.Ptr); StringParser tParser(&trackNameFromParser); tParser.ConsumeUntil(NULL, '='); tParser.ConsumeUntil(NULL, StringParser::sDigitMask); fStreamArray[theStreamIndex - 1].fTrackID = tParser.ConsumeInteger(NULL); } } else if (aLineType.Equal(sBufferDelayStr)) { // if a BufferDelay is found then set all of the streams to the same buffer delay (it's global) aParser.ConsumeUntil(NULL, StringParser::sDigitMask); theGlobalStreamInfo.fBufferDelay = aParser.ConsumeFloat(); } } break; case 'c': { //get the IP address off this header StringParser cParser(&sdpLine); cParser.ConsumeLength(NULL, 9);//strip off "c=in ip4 " UInt32 tempIPAddr = SDPSourceInfo::GetIPAddr(&cParser, '/'); //grab the ttl SInt32 tempTtl = kDefaultTTL; if (cParser.GetThru(NULL, '/')) { tempTtl = cParser.ConsumeInteger(NULL); Assert(tempTtl >= 0); Assert(tempTtl < 65536); } if (theStreamIndex > 0) { //if this c= line is part of a stream, it overrides the //global stream information fStreamArray[theStreamIndex - 1].fDestIPAddr = tempIPAddr; fStreamArray[theStreamIndex - 1].fTimeToLive = (UInt16) tempTtl; } else { theGlobalStreamInfo.fDestIPAddr = tempIPAddr; theGlobalStreamInfo.fTimeToLive = (UInt16) tempTtl; hasGlobalStreamInfo = true; } } } } // Add the default buffer delay Float32 bufferDelay = (Float32) eDefaultBufferDelay; if (theGlobalStreamInfo.fBufferDelay != (Float32) eDefaultBufferDelay) bufferDelay = theGlobalStreamInfo.fBufferDelay; UInt32 count = 0; while (count < fNumStreams) { fStreamArray[count].fBufferDelay = bufferDelay; count ++; } }
void AccessChecker::GetAccessFileInfo(const char* inQTAccessDir) { Assert( fAccessFile != NULL); const int kBufLen = 2048; char buf[kBufLen]; StrPtrLen bufLine; ::qtss_printf("QTSSDemoODAuthModule: File Info\n"); while ( std::fgets(buf, kBufLen, fAccessFile) != NULL ) { bufLine.Set(buf, strlen(buf)); StringParser bufParser(&bufLine); //skip over leading whitespace bufParser.ConsumeUntil(NULL, StringParser::sWhitespaceMask); //skip over comments and blank lines... if ( (bufParser.GetDataRemaining() == 0) || (bufParser[0] == '#') || (bufParser[0] == '\0') ) continue; StrPtrLen word; bufParser.ConsumeWord(&word); bufParser.ConsumeWhitespace(); if ( word.Equal("AuthName") ) //realm name { bufParser.GetThruEOL(&word); fRealmHeader = word.Ptr; } else if ( word.Equal("AuthUserFile" ) ) //users name { char filePath[kBufLen]; bufParser.GetThruEOL(&word); if (word.Ptr[0] == '/') //absolute path { std::memcpy(filePath, word.Ptr, word.Len); filePath[word.Len] = '\0'; } else { std::snprintf(filePath, sizeof(filePath), "%s/%s", inQTAccessDir, word.Ptr); } fUsersFile = std::fopen(filePath, "r"); } else if ( word.Equal("AuthGroupFile") ) //groups name { char filePath[kBufLen]; bufParser.GetThruEOL(&word); if (word.Ptr[0] == '/') //absolute path { std::memcpy(filePath, word.Ptr, word.Len); filePath[word.Len] = '\0'; } else { std::snprintf(filePath, sizeof(filePath), "%s/%s", inQTAccessDir, word.Ptr); } fGroupsFile = std::fopen(filePath, "r"); } } if (fUsersFile == NULL) { fUsersFile = std::fopen(fUsersFilePath.c_str(), "r"); } if (fGroupsFile == NULL) { fGroupsFile = std::fopen(fGroupsFilePath.c_str(), "r"); } }
bool AccessChecker::CheckUserAccess(const char* inUsername) { ::qtss_printf("In QTSSDemoODAuthModule: Check User Access - start\n"); const int kBufLen = 2048; char buf[kBufLen]; StrPtrLen bufLine; if ( fAccessFile == NULL ) return false; std::rewind(fAccessFile); while ( std::fgets(buf, kBufLen, fAccessFile) != NULL ) { bufLine.Set(buf, strlen(buf)); StringParser bufParser(&bufLine); //skip over leading whitespace bufParser.ConsumeUntil(NULL, StringParser::sWhitespaceMask); //skip over comments and blank lines... if ((bufParser.GetDataRemaining() == 0) || (bufParser[0] == '#') || (bufParser[0] == '\0') ) continue; StrPtrLen word; bufParser.ConsumeWord(&word); if ( word.Equal("require") ) { bufParser.ConsumeWhitespace(); bufParser.ConsumeWord(&word); if ( word.Equal("user") ) { while (word.Len != 0) { bufParser.ConsumeWhitespace(); bufParser.ConsumeWord(&word); if (word.Equal(inUsername)) { ::qtss_printf("QTSSDemoODAuthModule in CheckUserAccess() : user %s found\n", inUsername); return true; } } } else if (word.Equal("valid-user")) { ::qtss_printf("QTSSDemoODAuthModule in CheckUserAccess(): valid-user\n"); return true; } else if ( word.Equal("group") ) { while (word.Len != 0) { bufParser.ConsumeWhitespace(); bufParser.ConsumeWord(&word); if ( this->CheckGroupMembership(inUsername, word.GetAsCString()) ) { ::qtss_printf("QTSSDemoODAuthModule in CheckUserAccess(): user is part of %s group\n", word.GetAsCString()); return true; } } } } } return false; }
// allocates memory for outUsersFilePath and outGroupsFilePath - remember to delete // returns the auth scheme QTSS_AuthScheme QTAccessFile::FindUsersAndGroupsFilesAndAuthScheme(char* inAccessFilePath, QTSS_ActionFlags inAction, char** outUsersFilePath, char** outGroupsFilePath) { QTSS_AuthScheme authScheme = qtssAuthNone; QTSS_ActionFlags currentFlags = qtssActionFlagsRead; if (inAccessFilePath == NULL) return authScheme; *outUsersFilePath = NULL; *outGroupsFilePath = NULL; //Assert(outUsersFilePath == NULL); //Assert(outGroupsFilePath == NULL); StrPtrLen accessFileBuf; (void)QTSSModuleUtils::ReadEntireFile(inAccessFilePath, &accessFileBuf); OSCharArrayDeleter accessFileBufDeleter(accessFileBuf.Ptr); StringParser accessFileParser(&accessFileBuf); StrPtrLen line; StrPtrLen word; while( accessFileParser.GetDataRemaining() != 0 ) { accessFileParser.GetThruEOL(&line); // Read each line StringParser lineParser(&line); lineParser.ConsumeWhitespace(); //skip over leading whitespace if (lineParser.GetDataRemaining() == 0) // must be an empty line continue; char firstChar = lineParser.PeekFast(); if ( (firstChar == '#') || (firstChar == '\0') ) continue; //skip over comments and blank lines... lineParser.ConsumeUntilWhitespace(&word); if ( word.Equal("<Limit") ) // a limit line { currentFlags = qtssActionFlagsNoFlags; // clear to no access lineParser.ConsumeWhitespace(); lineParser.ConsumeUntil( &word, sWhitespaceAndGreaterThanMask); // the flag <limit Read> or <limit Read > while (word.Len != 0) // compare each word in the line { if (word.Equal("WRITE") ) { currentFlags |= inAction & qtssActionFlagsWrite; // accept following lines if inFlags has write } if (word.Equal("READ") ) { currentFlags |= inAction & qtssActionFlagsRead; // accept following lines if inFlags has read } lineParser.ConsumeWhitespace(); lineParser.ConsumeUntil(&word, sWhitespaceAndGreaterThanMask); } continue; //done with limit line } if ( word.Equal("</Limit>") ) currentFlags = qtssActionFlagsRead; // set the current access state to the default of read access if (0 == (currentFlags & inAction)) continue; // ignore lines because inFlags doesn't match the current access state if (word.Equal("AuthUserFile") ) { lineParser.ConsumeWhitespace(); lineParser.GetThruEOL(&word); StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them. if(*outUsersFilePath != NULL) // we are encountering the AuthUserFile keyword twice! delete[] *outUsersFilePath; // The last one found takes precedence...delete the previous path *outUsersFilePath = word.GetAsCString(); continue; } if (word.Equal("AuthGroupFile") ) { lineParser.ConsumeWhitespace(); lineParser.GetThruEOL(&word); StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them. if(*outGroupsFilePath != NULL) // we are encountering the AuthGroupFile keyword twice! delete[] *outGroupsFilePath; // The last one found takes precedence...delete the previous path *outGroupsFilePath = word.GetAsCString(); continue; } if (word.Equal("AuthScheme") ) { lineParser.ConsumeWhitespace(); lineParser.GetThruEOL(&word); StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them. if (word.Equal("basic")) authScheme = qtssAuthBasic; else if (word.Equal("digest")) authScheme = qtssAuthDigest; continue; } } return authScheme; }
Bool16 QTAccessFile::AccessAllowed ( char *userName, char**groupArray, UInt32 numGroups, StrPtrLen *accessFileBufPtr, QTSS_ActionFlags inFlags,StrPtrLen* ioRealmNameStr, Bool16 *outAllowAnyUserPtr, void *extraDataPtr ) { if (NULL == accessFileBufPtr || NULL == accessFileBufPtr->Ptr || 0 == accessFileBufPtr->Len) return true; // nothing to check if (ioRealmNameStr != NULL && ioRealmNameStr->Ptr != NULL && ioRealmNameStr->Len > 0) ioRealmNameStr->Ptr[0] = 0; StringParser accessFileParser(accessFileBufPtr); QTSS_ActionFlags currentFlags = qtssActionFlagsRead; StrPtrLen line; StrPtrLen word; Bool16 haveUserName = false; Bool16 haveRealmResultBuffer = false; Bool16 haveGroups = false; *outAllowAnyUserPtr = false; haveUserName = HaveUser(userName, extraDataPtr); haveGroups = HaveGroups(groupArray, numGroups, extraDataPtr); haveRealmResultBuffer = HaveRealm(userName, ioRealmNameStr, extraDataPtr ); while( accessFileParser.GetDataRemaining() != 0 ) { accessFileParser.GetThruEOL(&line); // Read each line StringParser lineParser(&line); lineParser.ConsumeWhitespace();//skip over leading whitespace if (lineParser.GetDataRemaining() == 0) // must be an empty line continue; char firstChar = lineParser.PeekFast(); if ( (firstChar == '#') || (firstChar == '\0') ) continue; //skip over comments and blank lines... lineParser.ConsumeUntilWhitespace(&word); if ( word.Equal("<Limit") ) // a limit line { currentFlags = qtssActionFlagsNoFlags; // clear to no access lineParser.ConsumeWhitespace(); lineParser.ConsumeUntil( &word, sWhitespaceAndGreaterThanMask); // the flag <limit Read> or <limit Read > while (word.Len != 0) // compare each word in the line { if (word.Equal("WRITE") ) { currentFlags |= inFlags & qtssActionFlagsWrite; // accept following lines if inFlags has write } if (word.Equal("READ") ) { currentFlags |= inFlags & qtssActionFlagsRead; // accept following lines if inFlags has read } lineParser.ConsumeWhitespace(); lineParser.ConsumeUntil(&word, sWhitespaceAndGreaterThanMask); } continue; //done with limit line } if ( word.Equal("</Limit>") ) currentFlags = qtssActionFlagsRead; // set the current access state to the default of read access if (0 == (currentFlags & inFlags)) continue; // ignore lines because inFlags doesn't match the current access state if (haveRealmResultBuffer && word.Equal("AuthName") ) //realm name { lineParser.ConsumeWhitespace(); lineParser.GetThruEOL(&word); StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them. GetRealm(&word, ioRealmNameStr, userName, extraDataPtr ); // we don't change the buffer len ioRealmNameStr->Len because we might have another AuthName tag to copy continue; // done with AuthName (realm) } if (word.Equal("require") ) { lineParser.ConsumeWhitespace(); lineParser.ConsumeUntilWhitespace(&word); if (haveUserName && word.Equal("valid-user") ) { return true; } if ( word.Equal("any-user") ) { *outAllowAnyUserPtr = true; return true; } if (!haveUserName) continue; if (word.Equal("user") ) { lineParser.ConsumeWhitespace(); lineParser.ConsumeUntilWhitespace(&word); while (word.Len != 0) // compare each word in the line { if (TestUser(&word, userName, extraDataPtr )) return true; lineParser.ConsumeWhitespace(); lineParser.ConsumeUntilWhitespace(&word); } continue; // done with "require user" line } if (haveGroups && word.Equal("group")) // check if we have groups for the user { lineParser.ConsumeWhitespace(); lineParser.ConsumeUntilWhitespace(&word); while (word.Len != 0) // compare each word in the line { if (TestGroup(&word, userName, groupArray, numGroups, extraDataPtr) ) return true; lineParser.ConsumeWhitespace(); lineParser.ConsumeUntilWhitespace(&word); } continue; // done with "require group" line } if (TestExtraData(&word, &lineParser, extraDataPtr)) return true; continue; // done with unparsed "require" line } } return false; // user or group not found }
QTSS_Error FilterRequest(QTSS_Filter_Params* inParams) { #if HTTP_FILE_DEBUGGING qtss_printf("FilterRequest\n"); #endif static Bool16 sFalse = false; QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest; // Initial state. StrPtrLen theFullRequest; StrPtrLen reqMethod; StrPtrLen reqStr; StrPtrLen httpVersion; (void)QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len); StringParser fullRequest(&theFullRequest); // Parsing the HTTP request fullRequest.ConsumeWord(&reqMethod); if ( !(reqMethod.Equal(StrPtrLen("GET")) || reqMethod.Equal(StrPtrLen("HEAD"))) ) // It's not a "Get" or a "Head" request return QTSS_NoErr; fullRequest.ConsumeWhitespace(); if ( !fullRequest.Expect('/') ) // Improperly formed request return QTSS_NoErr; fullRequest.ConsumeUntil(&reqStr, StringParser::sEOLWhitespaceMask); if( reqStr.Len == 0 ) //if a file or directory name is not given, return return QTSS_NoErr; if ( !reqStr.Equal(StrPtrLen("Popular.smil")) ) return QTSS_NoErr; // If it's a "Head" request send the Head response header back and just return if ( reqMethod.Equal(StrPtrLen("HEAD")) ) { QTSS_Write(theRequest, sResponseHeader, ::strlen(sResponseHeader), NULL, 0); return QTSS_NoErr; } // Create a buffer to store data. char theFileBuffer[8192]; char contentLength[256]; // Before sending any response, set keep alive to off for this connection // Regardless of what the client sends, the server always closes the connection after sending the file (void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse)); #if HTTP_FILE_DEBUGGING qtss_printf("Creating a smil file\n"); #endif // Create a ref movie buffer for the single file. It is of the form: // rtsptext\r // rtsp://servername/filepath char smilFileBuf[8192] = {0}; GenerateHotHitSMIL(smilFileBuf); qtss_sprintf(contentLength, "%lu", strlen(smilFileBuf)); // Allocate memory for theFileBuffer // Write the HTTP header prefix into the buffer ::strcpy(theFileBuffer, sRespHeaderPrefix.Ptr); ::strcat(theFileBuffer, sContentLengthHeaderTag.Ptr); // Write the remaining part of the HTTP header into the file buffer ::strcat(theFileBuffer, contentLength); ::strcat(theFileBuffer, sContentTypeHeaderTag.Ptr); ::strcat(theFileBuffer, sSmilMimeType.Ptr); ::strcat(theFileBuffer, "\r\n\r\n"); // Write the smil file created above to the file buffer ::strcat(theFileBuffer, smilFileBuf); // Write the contents of the file buffer to the request stream and return QTSS_Write(theRequest, theFileBuffer, strlen(theFileBuffer), NULL, 0); #if HTTP_FILE_DEBUGGING qtss_printf("Wrote the smil file to the request stream. Successful!\n"); #endif return QTSS_NoErr; }
QTSS_Error FilterRequest(QTSS_Filter_Params* inParams) { UInt8 sParamStopMask[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-9 //stop unless a '\t', ' ', or '&' 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10-19 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, //30-39 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, 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, 1 //250-255 }; //check to see if we should handle this request. Invokation is triggered //by a "GET /" request QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest; StrPtrLen theFullRequest; (void)QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len); StringParser fullRequest(&theFullRequest); StrPtrLen strPtr; StrPtrLen paramName; StrPtrLen fieldsList; fullRequest.ConsumeWord(&strPtr); if ( strPtr.Equal(StrPtrLen("GET")) ) //it's a "Get" request { fullRequest.ConsumeWhitespace(); if ( fullRequest.Expect('/') ) { UInt32 refreshInterval = 0; Bool16 displayHelp = false; OSCharArrayDeleter theWebStatsURL(GetPrefAsString(sPrefs, sDefaultURLPrefName)); StrPtrLen theWebStatsURLPtr(theWebStatsURL.GetObject()); // If there isn't any web stats URL, we can just return at this point if (theWebStatsURLPtr.Len == 0) return QTSS_NoErr; fullRequest.ConsumeUntil(&strPtr, StringParser::sEOLWhitespaceQueryMask); if ( strPtr.Len != 0 && strPtr.Equal(theWebStatsURLPtr) ) //it's a "stats" request { if ( fullRequest.Expect('?') ) { do { fullRequest.ConsumeWord(¶mName); if( paramName.Len != 0) { if ( paramName.Equal(StrPtrLen("refresh",strlen("refresh"))) ) { if (fullRequest.Expect('=')) refreshInterval = fullRequest.ConsumeInteger(NULL); } else if ( paramName.Equal(StrPtrLen("help",strlen("help"))) ) { displayHelp = true; } else if ( paramName.Equal(StrPtrLen("fields",strlen("fields"))) ) { if (fullRequest.Expect('=')) fullRequest.ConsumeUntil(&fieldsList, (UInt8*)sParamStopMask); } } } while ( paramName.Len != 0 && fullRequest.Expect('&') ); } // Before sending a response, set keep alive to off for this connection (void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse)); SendStats(inParams->inRTSPRequest, refreshInterval, displayHelp, (fieldsList.Len != 0) ? &fieldsList : NULL); } } } return QTSS_NoErr; }
char* SDPSourceInfo::GetLocalSDP(UInt32* newSDPLen) { Assert(fSDPData.Ptr != NULL); bool appendCLine = true; UInt32 trackIndex = 0; char *localSDP = new char[fSDPData.Len * 2]; OSCharArrayDeleter charArrayPathDeleter(localSDP); StringFormatter localSDPFormatter(localSDP, fSDPData.Len * 2); StrPtrLen sdpLine; StringParser sdpParser(&fSDPData); char trackIndexBuffer[50]; // Only generate our own trackIDs if this file doesn't have 'em. // Our assumption here is that either the file has them, or it doesn't. // A file with some trackIDs, and some not, won't work. bool hasControlLine = false; while (sdpParser.GetDataRemaining() > 0) { //stop when we reach an empty line. sdpParser.GetThruEOL(&sdpLine); if (sdpLine.Len == 0) continue; switch (*sdpLine.Ptr) { case 'c': break;//ignore connection information case 'm': { //append new connection information right before the first 'm' if (appendCLine) { localSDPFormatter.Put(sCLine); localSDPFormatter.PutEOL(); if (!hasControlLine) { localSDPFormatter.Put(sControlLine); localSDPFormatter.PutEOL(); } appendCLine = false; } //the last "a=" for each m should be the control a= if ((trackIndex > 0) && (!hasControlLine)) { qtss_sprintf(trackIndexBuffer, "a=control:trackID=%" _S32BITARG_ "\r\n",trackIndex); localSDPFormatter.Put(trackIndexBuffer, ::strlen(trackIndexBuffer)); } //now write the 'm' line, but strip off the port information StringParser mParser(&sdpLine); StrPtrLen mPrefix; mParser.ConsumeUntil(&mPrefix, StringParser::sDigitMask); localSDPFormatter.Put(mPrefix); localSDPFormatter.Put("0", 1); (void)mParser.ConsumeInteger(NULL); localSDPFormatter.Put(mParser.GetCurrentPosition(), mParser.GetDataRemaining()); localSDPFormatter.PutEOL(); trackIndex++; break; } case 'a': { StringParser aParser(&sdpLine); aParser.ConsumeLength(NULL, 2);//go past 'a=' StrPtrLen aLineType; aParser.ConsumeWord(&aLineType); if (aLineType.Equal(sControlStr)) { aParser.ConsumeUntil(NULL, '='); aParser.ConsumeUntil(NULL, StringParser::sDigitMask); StrPtrLen aDigitType; (void)aParser.ConsumeInteger(&aDigitType); if (aDigitType.Len > 0) { localSDPFormatter.Put("a=control:trackID=", 18); localSDPFormatter.Put(aDigitType); localSDPFormatter.PutEOL(); hasControlLine = true; break; } } localSDPFormatter.Put(sdpLine); localSDPFormatter.PutEOL(); break; } default: { localSDPFormatter.Put(sdpLine); localSDPFormatter.PutEOL(); } } } if ((trackIndex > 0) && (!hasControlLine)) { qtss_sprintf(trackIndexBuffer, "a=control:trackID=%" _S32BITARG_ "\r\n",trackIndex); localSDPFormatter.Put(trackIndexBuffer, ::strlen(trackIndexBuffer)); } *newSDPLen = (UInt32)localSDPFormatter.GetCurrentOffset(); StrPtrLen theSDPStr(localSDP, *newSDPLen);//localSDP is not 0 terminated so initialize theSDPStr with the len. SDPContainer rawSDPContainer; (void) rawSDPContainer.SetSDPBuffer( &theSDPStr ); SDPLineSorter sortedSDP(&rawSDPContainer); return sortedSDP.GetSortedSDPCopy(); // return a new copy of the sorted SDP }
void SendStats(QTSS_StreamRef inStream, UInt32 refreshInterval, Bool16 displayHelp, StrPtrLen* fieldList) { struct FieldIndex { char* fieldName; int fieldIndex; }; const FieldIndex kFieldIndexes[] = { {"title", 1}, {"dnsname", 2}, {"curtime", 3}, {"", 4}, {"serververs", 5}, {"serverbornon", 6}, {"serverstatus", 7}, {"", 8}, {"", 9}, {"", 10}, {"", 11}, {"", 12}, {"", 13}, {"currtp", 14}, {"currtsp", 15}, {"currtsphttp", 16}, {"curthru", 17}, {"curpkts", 18}, {"totbytes", 19}, {"totconns", 20}, {"", 21}, {"connlimit", 22}, {"thrulimit", 23}, {"moviedir", 24}, {"rtspip", 25}, {"rtspport", 26}, {"rtsptimeout", 27}, {"rtptimeout", 28}, {"secstobuffer", 29}, {"", 30}, {"accesslog", 31}, {"accesslogdir",32}, {"accesslogname", 33}, {"accessrollsize", 34}, {"accessrollinterval", 35}, {"", 36}, {"errorlog", 37}, {"errorlogdir", 38}, {"errorlogname", 39}, {"errorrollsize", 40}, {"errorrollinterval", 41}, {"errorloglevel", 42}, {"", 43}, {"assertbreak", 44}, {"autostart", 45}, {"totbytesupdateinterval", 46}, {"reflectordelay", 47}, {"reflectorbucketsize", 48}, {"historyinterval", 49}, {"outoffiledesc", 50}, {"numudpsockets", 51}, {"apiversion", 52}, {"numreliableudpbuffers", 53}, {"reliableudpwastedbytes", 54}, {"numtaskthreads", 55} }; const int kMaxFieldNum = 55; static char* kEmptyStr = "?"; char* thePrefStr = kEmptyStr; char buffer[1024]; (void)QTSS_Write(inStream, sResponseHeader, ::strlen(sResponseHeader), NULL, 0); if (refreshInterval > 0) { qtss_sprintf(buffer, "<META HTTP-EQUIV=Refresh CONTENT=%"_U32BITARG_">\n", refreshInterval); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } //qtss_sprintf(buffer, "<body text=\"#000000\" bgcolor=\"#C0C0C0\" link=\"#0000FF\" vlink=\"#551A8B\" alink=\"#0000FF\">\n"); //(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); char *theHTML = "<HTML><BODY>\n"; (void)QTSS_Write(inStream, theHTML, ::strlen(theHTML), NULL, 0); if (displayHelp) { #ifndef __MacOSX__ static StrPtrLen sHelpLine1("<P><b>Streaming Server Statistics Help</b></P>\n"); #else static StrPtrLen sHelpLine1("<P><b>QuickTime Streaming Server Statistics Help</b></P>\n"); #endif static StrPtrLen sHelpLine2("<P>Example:</P>\n"); static StrPtrLen sHelpLine3("<BLOCKQUOTE><P>http://server/statsURL?help&refresh=15&fields=curtime,cpuload </P>\n"); static StrPtrLen sHelpLine4("\"?\" means that there are options being attached to the stats request.<BR>\n"); static StrPtrLen sHelpLine5("\"&\" separates multiple stats options<BR>\n<BR>\n"); static StrPtrLen sHelpLine6("<P>The three possible parameters to stats are:</P>\n"); static StrPtrLen sHelpLine7("<P>\"help\" -- shows the help information you're reading right now.</P>\n"); static StrPtrLen sHelpLine8("<P>\"refresh=[n]\" -- tells the browser to automatically update the page every [n] seconds.</P>\n"); static StrPtrLen sHelpLine9("<P>\"fields=[fieldList]\" -- show only the fields specified in comma delimited [fieldList]</P>\n"); static StrPtrLen sHelpLine10("<BLOCKQUOTE>The following fields are available for use with the \"fields\" option:</P><BLOCKQUOTE><DL>\n"); (void)QTSS_Write(inStream, sHelpLine1.Ptr, sHelpLine1.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine2.Ptr, sHelpLine2.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine3.Ptr, sHelpLine3.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine4.Ptr, sHelpLine4.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine5.Ptr, sHelpLine5.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine6.Ptr, sHelpLine6.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine7.Ptr, sHelpLine7.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine8.Ptr, sHelpLine8.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine9.Ptr, sHelpLine9.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine10.Ptr, sHelpLine10.Len, NULL, 0); for (short i = 0; i < kMaxFieldNum; i++) { qtss_sprintf(buffer, "<DT><I>%s</I></DT>\n", kFieldIndexes[i].fieldName); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } static StrPtrLen sHelpLine11("</DL></BLOCKQUOTE></BLOCKQUOTE></BLOCKQUOTE><BR><P><HR>"); (void)QTSS_Write(inStream, sHelpLine11.Ptr, sHelpLine11.Len, NULL, 0); } StringParser fieldNamesParser(fieldList); StrPtrLen fieldName; int fieldNum = 0; do { if (fieldList != NULL) { fieldNum = 0; fieldNamesParser.ConsumeWord(&fieldName); for (short i = 0; i < kMaxFieldNum; i++) { if ( fieldName.Equal(StrPtrLen(kFieldIndexes[i].fieldName, ::strlen(kFieldIndexes[i].fieldName))) ) { fieldNum = kFieldIndexes[i].fieldIndex; break; } } } else { fieldNum++; if ( fieldNum > kMaxFieldNum ) fieldNum = 0; } UInt32 theLen = 0; switch (fieldNum) { case 1: { #if __MacOSX__ static StrPtrLen sStatsLine1("<TITLE>QuickTime Streaming Server Stats</TITLE><BR>\n"); (void)QTSS_Write(inStream, sStatsLine1.Ptr, sStatsLine1.Len, NULL, 0); #else static StrPtrLen sStatsLine1("<TITLE>Streaming Server Stats</TITLE><BR>\n"); (void)QTSS_Write(inStream, sStatsLine1.Ptr, sStatsLine1.Len, NULL, 0); #endif #if __MacOSX__ static StrPtrLen sStatsLine2("<center><h1>QuickTime Streaming Server Statistics</h1></center>\n"); (void)QTSS_Write(inStream, sStatsLine2.Ptr, sStatsLine2.Len, NULL, 0); #else static StrPtrLen sStatsLine2("<center><h1>Streaming Server Statistics</h1></center>\n"); (void)QTSS_Write(inStream, sStatsLine2.Ptr, sStatsLine2.Len, NULL, 0); #endif } break; case 2: { StrPtrLen theDNS; (void)QTSS_GetValuePtr(sServer, qtssSvrDefaultDNSName, 0, (void**)&theDNS.Ptr, &theDNS.Len); if ( theDNS.Ptr == NULL ) { // no DNS, try for the IP address only. (void)QTSS_GetValuePtr(sServer, qtssSvrDefaultIPAddr, 0, (void**)&theDNS.Ptr, &theDNS.Len); } if ( theDNS.Ptr != NULL ) { qtss_sprintf(buffer, "<b>DNS Name (default): </b> %s<BR>\n", theDNS.Ptr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 3: { char uptimebuffer[1024]; time_t curTime = ::time(NULL); qtss_sprintf(uptimebuffer, "<b>Current Time: </b> %s<BR>\n", qtss_ctime(&curTime, buffer, sizeof(buffer))); (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0); time_t upTime = curTime - sStartupTime; #define kDaySeconds (24 * 60 * 60) #define kHourSeconds (60 * 60) #define kMinuteSeconds 60 UInt32 upTimeDays = upTime / kDaySeconds; UInt32 upTimeHours = (upTime % kDaySeconds) / kHourSeconds; UInt32 upTimeMinutes = (upTime % kHourSeconds) / kMinuteSeconds; UInt32 upTimeSeconds = (upTime % kMinuteSeconds); qtss_snprintf(uptimebuffer,sizeof(uptimebuffer), "<b>Up Time Total Seconds: </b> %"_U32BITARG_"<BR>\n", upTime); uptimebuffer[sizeof(uptimebuffer) -1] = 0; (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0); qtss_snprintf(uptimebuffer,sizeof(uptimebuffer), "<b>Up Time: </b> %"_U32BITARG_" days %"_U32BITARG_" hours %"_U32BITARG_" minutes %"_U32BITARG_" seconds <BR>\n", upTimeDays, upTimeHours,upTimeMinutes, upTimeSeconds); uptimebuffer[sizeof(uptimebuffer) -1] = 0; (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0); } break; case 4: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 5: { StrPtrLen theVersion; (void)QTSS_GetValuePtr(sServer, qtssSvrRTSPServerHeader, 0, (void**)&theVersion.Ptr, &theVersion.Len); Assert(theVersion.Ptr != NULL); if (theVersion.Len > 7) //Skip the "Server:" text theVersion.Ptr += 7; qtss_sprintf(buffer, "<b>Server Version: </b>%s<BR>\n", theVersion.Ptr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 6: { StrPtrLen theBuildDate; (void)QTSS_GetValuePtr(sServer, qtssSvrServerBuildDate, 0, (void**)&theBuildDate.Ptr, &theBuildDate.Len); Assert(theBuildDate.Ptr != NULL); qtss_sprintf(buffer, "<b>Server Build Date: </b> %s<BR>\n", theBuildDate.Ptr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 7: { char statusBuffer[1024]; const char* states[] = { "Starting Up", "Running", "Refusing Connections", "Fatal Error", "Shutting Down" }; QTSS_ServerState theState = qtssRunningState; theLen = sizeof(theState); (void)QTSS_GetValue(sServer, qtssSvrState, 0, &theState, &theLen); if (theState == qtssRunningState) { qtss_snprintf(statusBuffer, sizeof(statusBuffer), "<b>Status: </b> %s since %s<BR>", states[theState], qtss_ctime(&sStartupTime,buffer,sizeof(buffer))); } else qtss_snprintf(statusBuffer,sizeof(statusBuffer), "<b>Status: </b> %s<BR>", states[theState]); (void)QTSS_Write(inStream, statusBuffer, ::strlen(statusBuffer), NULL, 0); } break; case 8: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 9: { //NOOP } break; case 10: { //NOOP } break; case 11: { //NOOP } break; case 12: { /* struct vm_statistics vmStats = {}; if (vm_statistics (current_task (), &vmStats) != KERN_SUCCESS) memset (&stats, '\0', sizeof (vmStats)) ; */ (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 13: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; //********************************** case 14: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurConn, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Current RTP Connections: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 15: { (void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Current RTSP Connections: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 16: { (void)QTSS_GetValueAsString(sServer, qtssRTSPHTTPCurrentSessionCount, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Current RTSP over HTTP Connections: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 17: { UInt32 curBandwidth = 0; theLen = sizeof(curBandwidth); (void)QTSS_GetValue(sServer, qtssRTPSvrCurBandwidth, 0, &curBandwidth, &theLen); qtss_sprintf(buffer, "<b>Current Throughput: </b> %"_U32BITARG_" kbits<BR>\n", curBandwidth/1024); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 18: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurPackets, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Current Packets Per Second: </b> %s <BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 19: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalBytes, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Total Bytes Served: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 20: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalConn, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Total Connections: </b> %s<BR>", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 21: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; //************************************** case 22: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaximumConnections, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Maximum Connections: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 23: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaximumBandwidth, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Maximum Throughput: </b> %s Kbits<BR>\n",thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 24: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMovieFolder, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Movie Folder Path: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 25: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTSPIPAddr, 0, &thePrefStr); qtss_sprintf(buffer, "<b>RTSP IP Address: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 26: { static StrPtrLen sRTSPPortsStart("<b>RTSP Ports: </b> "); (void)QTSS_Write(inStream, sRTSPPortsStart.Ptr, sRTSPPortsStart.Len, NULL, 0); StrPtrLen thePort; for (UInt32 theIndex = 0; true; theIndex++) { QTSS_Error theErr = QTSS_GetValuePtr(sServer, qtssSvrRTSPPorts, theIndex, (void**)&thePort.Ptr, &thePort.Len); if (theErr != QTSS_NoErr) break; Assert(thePort.Ptr != NULL); char temp[20]; qtss_sprintf(temp, "%u ", *(UInt16*)thePort.Ptr); (void)QTSS_Write(inStream, temp, ::strlen(temp), NULL, 0); } static StrPtrLen sRTSPPortsEnd("<BR>\n"); (void)QTSS_Write(inStream, sRTSPPortsEnd.Ptr, sRTSPPortsEnd.Len, NULL, 0); } break; case 27: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTSPTimeout, 0, &thePrefStr); qtss_sprintf(buffer, "<b>RTP Timeout: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 28: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTPTimeout, 0, &thePrefStr); qtss_sprintf(buffer, "<b>RTP Timeout: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 29: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 30: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 31: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logging"); qtss_sprintf(buffer, "<b>Access Logging: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 32: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_dir"); qtss_sprintf(buffer, "<b>Access Log Directory: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 33: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_name"); qtss_sprintf(buffer, "<b>Access Log Name: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 34: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_size"); qtss_sprintf(buffer, "<b>Access Log Roll Size: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 35: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_interval"); qtss_sprintf(buffer, "<b>Access Log Roll Interval (days): </b> %s<BR>", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 36: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 37: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogEnabled, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Logging: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 38: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogDir, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Directory: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 39: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogName, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Name: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 40: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaxErrorLogSize, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Roll Size: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 41: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorRollInterval, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Roll Interval (days): </b> %s<BR>\n",thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 42: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogVerbosity, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Verbosity: </b> %s<BR>", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 43: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 44: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsBreakOnAssert, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Break On Assert: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 45: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsAutoRestart, 0, &thePrefStr); qtss_sprintf(buffer, "<b>AutoStart: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 46: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsTotalBytesUpdate, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Total Bytes Update Interval: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 47: { if (sReflectorPrefs != NULL) { thePrefStr = GetPrefAsString(sReflectorPrefs, "reflector_delay"); qtss_sprintf(buffer, "<b>Reflector Delay Time: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 48: { if (sReflectorPrefs != NULL) { thePrefStr = GetPrefAsString(sReflectorPrefs, "reflector_bucket_size"); qtss_sprintf(buffer, "<b>Reflector Bucket Size: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 49: { if ( sSvrControlPrefs != NULL) { thePrefStr = GetPrefAsString(sSvrControlPrefs, "history_update_interval"); qtss_sprintf(buffer, "<b>History Update Interval: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 50: { Bool16 isOutOfDescriptors = false; theLen = sizeof(isOutOfDescriptors); (void)QTSS_GetValue(sServer, qtssSvrIsOutOfDescriptors, 0, &isOutOfDescriptors, &theLen); qtss_sprintf(buffer, "<b>Out of file descriptors: </b> %d<BR>\n", isOutOfDescriptors); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 51: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrNumUDPSockets, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Number of UDP sockets: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 52: { UInt32 apiVersion = 0; UInt32 size = sizeof(UInt32); (void)QTSS_GetValue(sServer, qtssServerAPIVersion, 0, &apiVersion, &size); qtss_sprintf(buffer, "<b>API version: </b> %d.%d<BR>\n", (int)( (UInt32) (apiVersion & (UInt32) 0xFFFF0000L) >> 16), (int)(apiVersion &(UInt32) 0x0000FFFFL)); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 53: { UInt32 reliableUDPBuffers = 0; UInt32 blahSize = sizeof(reliableUDPBuffers); (void)QTSS_GetValue(sServer, qtssSvrNumReliableUDPBuffers, 0, &reliableUDPBuffers, &blahSize); qtss_sprintf(buffer, "<b>Num Reliable UDP Retransmit Buffers: </b> %"_U32BITARG_"<BR>\n", reliableUDPBuffers); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 54: { UInt32 wastedBufSpace = 0; UInt32 blahSize2 = sizeof(wastedBufSpace); (void)QTSS_GetValue(sServer, qtssSvrReliableUDPWastageInBytes, 0, &wastedBufSpace, &blahSize2); qtss_sprintf(buffer, "<b>Amount of buffer space being wasted in UDP Retrans buffers: </b> %"_U32BITARG_"<BR>\n", wastedBufSpace); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 55: { (void)QTSS_GetValueAsString(sServer, qtssSvrNumThreads, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Number of Task Threads: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; default: break; } //switch fieldNum if (fieldList != NULL && !fieldNamesParser.Expect(',')) fieldNum = 0; if (thePrefStr != kEmptyStr) delete [] thePrefStr; thePrefStr = kEmptyStr; } while (fieldNum != 0); theHTML = "</BODY></HTML>\n"; (void)QTSS_Write(inStream, theHTML, ::strlen(theHTML), NULL, 0); }
bool XMLTag::ParseTag(StringParser* parser, DTDVerifier* verifier, char* errorBuffer, int errorBufferSize) { while (true) { if (!parser->GetThru(NULL, '<')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Couldn't find a valid tag"); return false; // couldn't find beginning of tag } char c = parser->PeekFast(); if (c == '/') { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "End tag with no begin tag on line %d", parser->GetCurrentLineNumber()); return false; // we shouldn't be seeing a close tag here } if ((c != '!') && (c != '?')) break; // this should be the beginning of a regular tag ConsumeIfComment(parser); // otherwise this is a processing instruction or a c-data, so look for the next tag } int tagStartLine = parser->GetCurrentLineNumber(); StrPtrLen temp; parser->ConsumeUntil(&temp, sNonNameMask); if (temp.Len == 0) { if (errorBuffer != NULL) { if (parser->GetDataRemaining() == 0) qtss_snprintf(errorBuffer, errorBufferSize, "Unexpected end of file on line %d", parser->GetCurrentLineNumber()); else qtss_snprintf(errorBuffer, errorBufferSize,"Unexpected character (%c) on line %d", parser->PeekFast(), parser->GetCurrentLineNumber()); } return false; // bad file } fTag = temp.GetAsCString(); parser->ConsumeWhitespace(); while ((parser->PeekFast() != '>') && (parser->PeekFast() != '/')) { // we must have an attribute value for this tag XMLAttribute* attr = new XMLAttribute; fAttributes.EnQueue(&attr->fElem); parser->ConsumeUntil(&temp, sNonNameMask); if (temp.Len == 0) { if (errorBuffer != NULL) { if (parser->GetDataRemaining() == 0) qtss_snprintf(errorBuffer, errorBufferSize, "Unexpected end of file on line %d", parser->GetCurrentLineNumber()); else qtss_snprintf(errorBuffer, errorBufferSize,"Unexpected character (%c) on line %d", parser->PeekFast(), parser->GetCurrentLineNumber()); } return false; // bad file } attr->fAttrName = temp.GetAsCString(); if (!parser->Expect('=')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Missing '=' after attribute %s on line %d", attr->fAttrName, parser->GetCurrentLineNumber()); return false; // bad attribute specification } if (!parser->Expect('"')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Attribute %s value not in quotes on line %d", attr->fAttrName, parser->GetCurrentLineNumber()); return false; // bad attribute specification } parser->ConsumeUntil(&temp, '"'); attr->fAttrValue = temp.GetAsCString(); if (!parser->Expect('"')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Attribute %s value not in quotes on line %d", attr->fAttrName, parser->GetCurrentLineNumber()); return false; // bad attribute specification } if (verifier && !verifier->IsValidAttributeName(fTag, attr->fAttrName)) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Attribute %s not allowed in tag %s on line %d", attr->fAttrName, fTag, parser->GetCurrentLineNumber()); return false; // bad attribute specification } if (verifier && !verifier->IsValidAttributeValue(fTag, attr->fAttrName, attr->fAttrValue)) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Bad value for attribute %s on line %d", attr->fAttrName, parser->GetCurrentLineNumber()); return false; // bad attribute specification } parser->ConsumeWhitespace(); } if (parser->PeekFast() == '/') { // this is an empty element tag, i.e. no contents or end tag (e.g <TAG attr="value" /> parser->Expect('/'); if (!parser->Expect('>')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"'>' must follow '/' on line %d", parser->GetCurrentLineNumber()); return false; // bad attribute specification } return true; // we're done with this tag } if (!parser->Expect('>')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Bad format for tag <%s> on line %d", fTag, parser->GetCurrentLineNumber()); return false; // bad attribute specification } while(true) { parser->ConsumeUntil(&temp, '<'); // this is either value or whitespace if (parser->GetDataRemaining() < 4) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Reached end of file without end for tag <%s> declared on line %d", fTag, tagStartLine); return false; } if ((*parser)[1] == '/') { // we'll only assign a value if there were no embedded tags if (fEmbeddedTags.GetLength() == 0 && (!verifier || verifier->CanHaveValue(fTag))) fValue = temp.GetAsCString(); else { // otherwise this needs to have been just whitespace StringParser tempParser(&temp); tempParser.ConsumeWhitespace(); if (tempParser.GetDataRemaining() > 0) { if (errorBuffer) { if (fEmbeddedTags.GetLength() > 0) qtss_snprintf(errorBuffer, errorBufferSize,"Unexpected text outside of tag on line %d", tagStartLine); else qtss_snprintf(errorBuffer, errorBufferSize, "Tag <%s> on line %d not allowed to have data", fTag, tagStartLine); } } } break; // we're all done with this tag } if (((*parser)[1] != '!') && ((*parser)[1] != '?')) { // this must be the beginning of an embedded tag XMLTag* tag = NEW XMLTag(); fEmbeddedTags.EnQueue(&tag->fElem); if (!tag->ParseTag(parser, verifier, errorBuffer, errorBufferSize)) return false; if (verifier && !verifier->IsValidSubtag(fTag, tag->GetTagName())) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Tag %s not allowed in tag %s on line %d", tag->GetTagName(), fTag, parser->GetCurrentLineNumber()); return false; // bad attribute specification } } else { parser->ConsumeLength(NULL, 1); // skip '<' ConsumeIfComment(parser); } } parser->ConsumeLength(NULL, 2); // skip '</' parser->ConsumeUntil(&temp, sNonNameMask); if (!temp.Equal(fTag)) { char* newTag = temp.GetAsCString(); if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"End tag </%s> on line %d doesn't match tag <%s> declared on line %d", newTag, parser->GetCurrentLineNumber(),fTag, tagStartLine); delete newTag; return false; // bad attribute specification } if (!parser->GetThru(NULL, '>')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Couldn't find end of tag <%s> declared on line %d", fTag, tagStartLine); return false; // bad attribute specification } return true; }
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; } }