void HTTP_Stream::parseHttpHeadersIfNeeded(UInt8 *buf, CFIndex bufSize) { if (m_httpHeadersParsed) { return; } m_httpHeadersParsed = true; /* If the response has the "ICY 200 OK" string, * we are dealing with the ShoutCast protocol. * The HTTP headers won't be available. */ std::string header; for (CFIndex k=0; k < bufSize; k++) { UInt8 c = buf[k]; // Ignore non-ASCII chars if (c < 32 || c > 126) { continue; } header.push_back(c); } char *p = strstr(header.c_str(), "ICY 200 OK"); // This is an ICY stream, don't try to parse the HTTP headers if (p) { m_icyStream = true; return; } CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(m_readStream, kCFStreamPropertyHTTPResponseHeader); if (response) { /* * If the server responded with the icy-metaint header, the response * body will be encoded in the ShoutCast protocol. */ CFStringRef icyMetaIntString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("icy-metaint")); if (icyMetaIntString) { m_icyStream = true; m_icyHeadersParsed = true; m_icyHeadersRead = true; m_icyMetaDataInterval = CFStringGetIntValue(icyMetaIntString); CFRelease(icyMetaIntString); } CFStringRef icyNameString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("icy-name")); if (icyNameString) { if (m_icyName) { CFRelease(m_icyName); } m_icyName = icyNameString; if (m_delegate) { std::map<CFStringRef,CFStringRef> metadataMap; metadataMap[CFSTR("IcecastStationName")] = CFStringCreateCopy(kCFAllocatorDefault, m_icyName); m_delegate->streamMetaDataAvailable(metadataMap); } } CFStringRef contentTypeString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Type")); if (contentTypeString) { CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(contentTypeString), kCFStringEncodingUTF8) + 1; char *buf = new char[len]; if (CFStringGetCString(contentTypeString, buf, len, kCFStringEncodingUTF8)) { m_contentType.append(buf); } delete[] buf; CFRelease(contentTypeString); } CFStringRef contentLengthString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Length")); if (contentLengthString) { m_contentLength = CFStringGetIntValue(contentLengthString); CFRelease(contentLengthString); } CFRelease(response); } if (m_delegate) { m_delegate->streamIsReadyRead(); } }
void HTTP_Stream::parseICYStream(const UInt8 *buf, const CFIndex bufSize) { HS_TRACE("Parsing an IceCast stream, received %li bytes\n", bufSize); CFIndex offset = 0; CFIndex bytesFound = 0; if (!m_icyHeadersRead) { HS_TRACE("ICY headers not read, reading\n"); for (; offset < bufSize; offset++) { if (m_icyHeaderCR && buf[offset] == '\n') { if (bytesFound > 0) { m_icyHeaderLines.push_back(createMetaDataStringWithMostReasonableEncoding(&buf[offset-bytesFound-1], bytesFound)); bytesFound = 0; HS_TRACE_CFSTRING(m_icyHeaderLines[m_icyHeaderLines.size()-1]); continue; } HS_TRACE("End of ICY headers\n"); m_icyHeadersRead = true; break; } if (buf[offset] == '\r') { m_icyHeaderCR = true; continue; } else { m_icyHeaderCR = false; } bytesFound++; } } else if (!m_icyHeadersParsed) { HS_TRACE("ICY headers not parsed, parsing\n"); const CFStringRef icyContentTypeHeader = CFSTR("content-type:"); const CFStringRef icyMetaDataHeader = CFSTR("icy-metaint:"); const CFStringRef icyNameHeader = CFSTR("icy-name:"); const CFIndex icyContenTypeHeaderLength = CFStringGetLength(icyContentTypeHeader); const CFIndex icyMetaDataHeaderLength = CFStringGetLength(icyMetaDataHeader); const CFIndex icyNameHeaderLength = CFStringGetLength(icyNameHeader); for (std::vector<CFStringRef>::iterator h = m_icyHeaderLines.begin(); h != m_icyHeaderLines.end(); ++h) { CFStringRef line = *h; const CFIndex lineLength = CFStringGetLength(line); if (lineLength == 0) { continue; } HS_TRACE_CFSTRING(line); if (CFStringCompareWithOptions(line, icyContentTypeHeader, CFRangeMake(0, icyContenTypeHeaderLength), 0) == kCFCompareEqualTo) { if (m_contentType) { CFRelease(m_contentType), m_contentType = 0; } m_contentType = CFStringCreateWithSubstring(kCFAllocatorDefault, line, CFRangeMake(icyContenTypeHeaderLength, lineLength - icyContenTypeHeaderLength)); } if (CFStringCompareWithOptions(line, icyMetaDataHeader, CFRangeMake(0, icyMetaDataHeaderLength), 0) == kCFCompareEqualTo) { CFStringRef metadataInterval = CFStringCreateWithSubstring(kCFAllocatorDefault, line, CFRangeMake(icyMetaDataHeaderLength, lineLength - icyMetaDataHeaderLength)); if (metadataInterval) { m_icyMetaDataInterval = CFStringGetIntValue(metadataInterval); CFRelease(metadataInterval); } else { m_icyMetaDataInterval = 0; } } if (CFStringCompareWithOptions(line, icyNameHeader, CFRangeMake(0, icyNameHeaderLength), 0) == kCFCompareEqualTo) { if (m_icyName) { CFRelease(m_icyName); } m_icyName = CFStringCreateWithSubstring(kCFAllocatorDefault, line, CFRangeMake(icyNameHeaderLength, lineLength - icyNameHeaderLength)); } } m_icyHeadersParsed = true; offset++; if (m_delegate) { m_delegate->streamIsReadyRead(); } } Stream_Configuration *config = Stream_Configuration::configuration(); if (!m_icyReadBuffer) { m_icyReadBuffer = new UInt8[config->httpConnectionBufferSize]; } HS_TRACE("Reading ICY stream for playback\n"); UInt32 i=0; for (; offset < bufSize; offset++) { // is this a metadata byte? if (m_metaDataBytesRemaining > 0) { m_metaDataBytesRemaining--; if (m_metaDataBytesRemaining == 0) { m_dataByteReadCount = 0; if (m_delegate && !m_icyMetaData.empty()) { std::map<CFStringRef,CFStringRef> metadataMap; CFStringRef metaData = createMetaDataStringWithMostReasonableEncoding(&m_icyMetaData[0], m_icyMetaData.size()); if (!metaData) { // Metadata encoding failed, cannot parse. m_icyMetaData.clear(); continue; } CFArrayRef tokens = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, metaData, CFSTR(";")); for (CFIndex i=0, max=CFArrayGetCount(tokens); i < max; i++) { CFStringRef token = (CFStringRef) CFArrayGetValueAtIndex(tokens, i); CFRange foundRange; if (CFStringFindWithOptions(token, CFSTR("='"), CFRangeMake(0, CFStringGetLength(token)), NULL, &foundRange) == true) { CFRange keyRange = CFRangeMake(0, foundRange.location); CFStringRef metadaKey = CFStringCreateWithSubstring(kCFAllocatorDefault, token, keyRange); CFRange valueRange = CFRangeMake(foundRange.location + 2, CFStringGetLength(token) - keyRange.length - 3); CFStringRef metadaValue = CFStringCreateWithSubstring(kCFAllocatorDefault, token, valueRange); metadataMap[metadaKey] = metadaValue; } } CFRelease(tokens); CFRelease(metaData); if (m_icyName) { metadataMap[CFSTR("IcecastStationName")] = CFStringCreateCopy(kCFAllocatorDefault, m_icyName); } m_delegate->streamMetaDataAvailable(metadataMap); } m_icyMetaData.clear(); continue; } m_icyMetaData.push_back(buf[offset]); continue; } // is this the interval byte? if (m_icyMetaDataInterval > 0 && m_dataByteReadCount == m_icyMetaDataInterval) { m_metaDataBytesRemaining = buf[offset] * 16; if (m_metaDataBytesRemaining == 0) { m_dataByteReadCount = 0; } continue; } // a data byte m_dataByteReadCount++; m_icyReadBuffer[i++] = buf[offset]; } if (m_delegate && i > 0) { m_delegate->streamHasBytesAvailable(m_icyReadBuffer, i); } }
CFStringRef CopyNextWorkstationName(SCDynamicStoreRef store, CFStringRef currentName) { CFMutableStringRef computerName = NULL; CFStringRef macAddress = NULL; if (currentName) { /* Sanity check to make sure the current Workstation name is longer than the length of a MAC address string */ CFIndex totalLengh = CFStringGetLength(currentName); if (totalLengh >= (CFIndex)kMACAddressLengh) { /* Create a substring that chops off the MAC addres, giving us the Computer Name */ CFRange range = CFRangeMake(0, totalLengh - kMACAddressLengh); CFStringRef oldComputerName = CFStringCreateWithSubstring(NULL, currentName, range); /* If the Computer Name contains a trailing close paren it means that this name may have already experienced a name conflict which means it could have a trailing digit to increment */ if (CFStringHasSuffix(oldComputerName, CFSTR(")"))) { CFRange result; /* Search for the first open paren, starting the search from the end of the Computer Name */ if (CFStringFindWithOptions(oldComputerName, CFSTR("("), range, kCFCompareBackwards, &result)) { /* Create a substring which contains the contents between the open and close paren */ range = CFRangeMake(result.location + 1, CFStringGetLength(oldComputerName) - result.location - 2); CFStringRef countString = CFStringCreateWithSubstring(NULL, currentName, range); /* Try converting the substring to an integer */ SInt32 conflictCount = CFStringGetIntValue(countString); if (conflictCount) { /* Create a substring of just the Computer Name without the trailing open paren, conflict integer, close paren */ range = CFRangeMake(0, result.location); CFStringRef tempComputerName = CFStringCreateWithSubstring(NULL, oldComputerName, range); /* Create a mutable copy of the Computer Name base substring */ computerName = CFStringCreateMutableCopy(NULL, 0, tempComputerName); /* Create a string containing a space, open paren, previous conflict digit incremented by one, close paren */ CFStringRef numberString = CFStringCreateWithFormat(NULL, NULL, CFSTR(" (%d)"), ++conflictCount); /* Truncate the Computer Name base as neccessary to ensure we can append the conflict digits */ CFStringTruncateToUTF8Length(computerName, kMaxComputerName - CFStringGetLength(numberString)); /* Append the incremented conflict digit to the Computer Name base string */ CFStringAppend(computerName, numberString); } } } /* If computerName is NULL it means that the previous Computer Name didn't contain any conflict digits so append a " (2)" */ if (!computerName) { /* Create mutable copy of previous Computer Name */ computerName = CFStringCreateMutableCopy(NULL, 0, oldComputerName); /* Make sure we have enough room to append 4 characters to the name by truncating the Computer Name if neccessary */ CFStringTruncateToUTF8Length(computerName, kMaxComputerName - 4); /* Append the name conflict digits */ CFStringAppend(computerName, CFSTR(" (2)")); } CFRelease(oldComputerName); } else { DbgLog( kLogError, "Workstation name is shorter than a MAC address which shouldn't be possible" ); } } else { /* There's currently no registered Workstation name so get the Computer Name from the dynamic store */ CFStringRef tempName = SCDynamicStoreCopyComputerName(store, NULL); if (tempName) { /* Create a mutable copy of the Computer Name */ computerName = CFStringCreateMutableCopy(NULL, 0, tempName); CFRelease(tempName); /* Truncate the Computer Name to ensure we can append the MAC address */ CFStringTruncateToUTF8Length(computerName, kMaxComputerName); } else { return NULL; } } /* Copy the primary MAC address string */ macAddress = CopyPrimaryMacAddress(); if (!macAddress) { if (computerName) { CFRelease(computerName); } return NULL; } /* Append a space */ CFStringAppend(computerName, CFSTR(" ")); /* Append the MAC address string */ CFStringAppend(computerName, macAddress); CFRelease(macAddress); return computerName; }
void HTTP_Stream::parseHttpHeadersIfNeeded(const UInt8 *buf, const CFIndex bufSize) { if (m_httpHeadersParsed) { return; } m_httpHeadersParsed = true; /* If the response has the "ICY 200 OK" string, * we are dealing with the ShoutCast protocol. * The HTTP headers won't be available. */ if (bufSize >= 10 && buf[0] == 0x49 && buf[1] == 0x43 && buf[2] == 0x59 && buf[3] == 0x20 && buf[4] == 0x32 && buf[5] == 0x30 && buf[6] == 0x30 && buf[7] == 0x20 && buf[8] == 0x4F && buf[9] == 0x4B) { m_icyStream = true; HS_TRACE("Detected an IceCast stream\n"); // This is an ICY stream, don't try to parse the HTTP headers return; } HS_TRACE("A regular HTTP stream\n"); CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(m_readStream, kCFStreamPropertyHTTPResponseHeader); if (response) { /* * If the server responded with the icy-metaint header, the response * body will be encoded in the ShoutCast protocol. */ CFStringRef icyMetaIntString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("icy-metaint")); if (icyMetaIntString) { m_icyStream = true; m_icyHeadersParsed = true; m_icyHeadersRead = true; m_icyMetaDataInterval = CFStringGetIntValue(icyMetaIntString); CFRelease(icyMetaIntString); } HS_TRACE("icy-metaint: %zu\n", m_icyMetaDataInterval); CFStringRef icyNameString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("icy-name")); if (icyNameString) { if (m_icyName) { CFRelease(m_icyName); } m_icyName = icyNameString; if (m_delegate) { std::map<CFStringRef,CFStringRef> metadataMap; metadataMap[CFSTR("IcecastStationName")] = CFStringCreateCopy(kCFAllocatorDefault, m_icyName); m_delegate->streamMetaDataAvailable(metadataMap); } } if (m_contentType) { CFRelease(m_contentType); } m_contentType = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Type")); HS_TRACE("Content-type: "); HS_TRACE_CFSTRING(m_contentType); CFStringRef contentLengthString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Length")); if (contentLengthString) { m_contentLength = CFStringGetIntValue(contentLengthString); CFRelease(contentLengthString); } CFRelease(response); } if (m_delegate) { m_delegate->streamIsReadyRead(); } }
/* ------------------------------------------------------------------------------------------------------ SetDateValueFromFITSHeader: read FITS header and set striped string value to attribute name. ------------------------------------------------------------------------------------------------------ */ void SetDateValueFromFITSHeader(const char* filename, CFMutableDictionaryRef attributes, const char* importerAttrName, char* mainKeyword, char* secondaryKeyword1, char* secondaryKeyword2) { CFMutableStringRef headerValue = CFStringCreateMutable(kCFAllocatorDefault, (CFIndex)80); CFStringRef value1 = getCleanedHeaderValue(filename, mainKeyword); if (value1 != NULL) { CFIndex valueLength1 = CFStringGetLength(value1); if ((valueLength1 >= 19) || (valueLength1 == 10)) { CFStringAppend(headerValue, value1); // value of mainKeyword looks OK. } CFRelease(value1); } CFStringRef value2 = getCleanedHeaderValue(filename, secondaryKeyword1); if (value2 != NULL) { CFIndex valueLength2 = CFStringGetLength(value2); CFIndex headerValueLength = CFStringGetLength(headerValue); if (headerValueLength == 0 && valueLength2 >= 10) { CFStringAppend(headerValueLength, value2); // Append value of secondaryKeyword1 } else if (headerValueLength < 19 && valueLength2 >= 19) { CFRange range = CFRangeMake(0, headerValueLength); CFStringDelete(headerValue, range); CFStringAppend(headerValue, value2); // Replace value of current headerValue by value of secondaryKeyword1 } CFRelease(value2); } CFStringRef value3 = getCleanedHeaderValue(filename, secondaryKeyword2); if (value3 != NULL) { CFRange r = CFStringFind(value3, CFSTR(":"), 0); CFIndex headerValueLength = CFStringGetLength(headerValue); if (headerValueLength == 10 && r.location != kCFNotFound) { CFStringAppend(headerValue, CFSTR("T")); CFStringAppend(headerValue, value3); } CFRelease(value3); } CFIndex headerValueLength = CFStringGetLength(headerValue); if (headerValueLength > 0) { CFArrayRef tArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, headerValue, CFSTR("T")); CFArrayRef ymdArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, CFArrayGetValueAtIndex(tArray, 0), CFSTR("-")); CFGregorianDate gregDate; gregDate.year = 0; gregDate.month = 0; gregDate.day = 0.; if (CFStringGetLength(CFArrayGetValueAtIndex(ymdArray, 0)) > 0) { gregDate.year = CFStringGetIntValue(CFArrayGetValueAtIndex(ymdArray, 0)); } if (CFStringGetLength(CFArrayGetValueAtIndex(ymdArray, 1)) > 0) { gregDate.month = CFStringGetIntValue(CFArrayGetValueAtIndex(ymdArray, 1)); } if (CFStringGetLength(CFArrayGetValueAtIndex(ymdArray, 2)) > 0) { gregDate.day = CFStringGetIntValue(CFArrayGetValueAtIndex(ymdArray, 2)); } CFRelease(ymdArray); gregDate.hour = 0; gregDate.minute = 0; gregDate.second = 0.; CFIndex arraySize = CFArrayGetCount(tArray); if (arraySize == 2) { CFArrayRef hmsArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, CFArrayGetValueAtIndex(tArray, 1), CFSTR(":")); if (CFStringGetLength(CFArrayGetValueAtIndex(hmsArray, 0)) > 0) { gregDate.hour = CFStringGetIntValue(CFArrayGetValueAtIndex(hmsArray, 0)); } if (CFStringGetLength(CFArrayGetValueAtIndex(hmsArray, 1)) > 0) { gregDate.minute = CFStringGetIntValue(CFArrayGetValueAtIndex(hmsArray, 1)); } if (CFStringGetLength(CFArrayGetValueAtIndex(hmsArray, 2)) > 0) { gregDate.second = CFStringGetDoubleValue(CFArrayGetValueAtIndex(hmsArray, 2)); } CFRelease(hmsArray); } CFRelease(tArray); // printf("%i %i %i %i %i %f\n\n", gregDate.year, gregDate.month, gregDate.day, gregDate.hour, gregDate.minute, gregDate.second); if ((gregDate.year > 0) && (gregDate.month > 0) && (gregDate.day > 0)) { CFTimeZoneRef timeZone = CFTimeZoneCreateWithName(kCFAllocatorDefault, CFSTR("GMT"), true); CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(gregDate, timeZone); CFDateRef date = CFDateCreate(kCFAllocatorDefault, absTime); if (timeZone != NULL) { CFRelease(timeZone); } CFStringRef cfImporterAttrName = CFStringCreateWithCString(kCFAllocatorDefault, importerAttrName, kCFStringEncodingUTF8); CFDictionaryAddValue(attributes, cfImporterAttrName, date); CFRelease(cfImporterAttrName); CFRelease(date); } } CFRelease(headerValue); }