#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFPriv.h>
#include <CoreFoundation/CFStreamPriv.h>
#include <CFNetwork/CFNetworkPriv.h>
#include "CFNetworkInternal.h"
#include "CFHTTPInternal.h"
#include <sys/stat.h>
#include <string.h> // For strcat
#include <stdlib.h> // for getenv
#if defined(__MACH__)
#include <mach-o/dyld.h>
#include <SystemConfiguration/SystemConfiguration.h>
#endif
#include <sys/types.h>
#if defined(__WIN32__)
#include <winsock2.h>
#include <ws2tcpip.h> // for ipv6
#include <wininet.h> // for InternetTimeToSystemTime
#include <objbase.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
#if defined(__MACH__)
void*
__CFNetworkLoadFramework(const char* const framework_path) {
return ((void*)NSAddImage(framework_path, NSADDIMAGE_OPTION_WITH_SEARCHING | NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME));
}
#endif
#if defined(__WIN32__)
typedef WSAAPI int (*getnameinfo_funcPtr)(const struct sockaddr*, socklen_t, char*, DWORD, char*, DWORD, int);
WINBOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved);
#else
typedef int (*getnameinfo_funcPtr)(const struct sockaddr *sa, socklen_t salen, char *node, socklen_t nodelen, char *service, socklen_t servicelen, int flags);
#endif
UInt8*
_CFStringGetOrCreateCString(CFAllocatorRef allocator, CFStringRef string, UInt8* buffer, CFIndex* bufferLength, CFStringEncoding encoding) {
CFIndex extra = 0;
if (!bufferLength)
bufferLength = &extra;
assert(string);
if (buffer && *bufferLength && CFStringGetCString(string, (char*)buffer, *bufferLength, encoding))
*bufferLength = strlen((const char*)buffer);
else {
UInt8* saved = buffer;
CFRange range = CFRangeMake(0, CFStringGetLength(string));
CFStringGetBytes(string, range, encoding, 0, FALSE, NULL, 0, bufferLength);
buffer = (UInt8*)CFAllocatorAllocate(allocator, *bufferLength + 1, 0);
if (buffer) {
*bufferLength = CFStringGetBytes(string, range, encoding, 0, FALSE, buffer, *bufferLength, NULL);
buffer[*bufferLength] = '\0';
*bufferLength = strlen((const char*)buffer);
}
else {
*bufferLength = 0;
buffer = saved;
if (buffer)
buffer[*bufferLength] = '\0';
}
}
return buffer;
}
CFStringRef
_CFNetworkCFStringCreateWithCFDataAddress(CFAllocatorRef alloc, CFDataRef addr) {
getnameinfo_funcPtr getnameinfo_func = NULL;
struct sockaddr* sa = (struct sockaddr*)CFDataGetBytePtr(addr);
CFIndex salen = CFDataGetLength(addr);
#if defined(__WIN32__)
static CFSpinLock_t lock = 0;
static Boolean beenHere = FALSE;
static getnameinfo_funcPtr getnameinfo_funcCache = NULL;
__CFSpinLock(&lock);
if (!beenHere) {
HMODULE module;
beenHere = TRUE;
module = GetModuleHandle("ws2_32");
if (module != NULL) {
getnameinfo_funcCache = (getnameinfo_funcPtr)GetProcAddress(module, "getnameinfo");
}
}
__CFSpinUnlock(&lock);
getnameinfo_func = getnameinfo_funcCache;
#else
getnameinfo_func = &getnameinfo;
#endif
CFStringRef name = NULL;
if (getnameinfo_func) {
char buffer[NI_MAXHOST];
if ((getnameinfo_func)(sa, salen, buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) == 0) {
name = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII);
}
} else {
if (sa->sa_family == AF_INET) {
char *cStr = inet_ntoa(((struct sockaddr_in*)sa)->sin_addr);
if (cStr)
name = CFStringCreateWithCString(NULL, cStr, kCFStringEncodingASCII);
}
}
return name;
}
#if 0
#endif
static const char* kDayStrs[] = {
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday",
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
static const char* kMonthStrs[] = {
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December",
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static const char* kUSTimeZones[] = {"PST", "PDT", "MST", "MDT", "CST", "CDT", "EST", "EDT"};
const UInt8*
_CFGregorianDateCreateWithBytes(CFAllocatorRef alloc, const UInt8* bytes, CFIndex length, CFGregorianDate* date, CFTimeZoneRef* tz) {
UInt8 buffer[256];
length = (length == 256) ? 255 : length;
memmove(buffer, bytes, length);
buffer[length] = '\0';
memset(date, 0, sizeof(date[0]));
if (tz) *tz = NULL;
do {
int i;
CFIndex scan = 0;
UInt8 c = buffer[scan];
while (isspace(c))
c = buffer[++scan];
if (!isdigit(c)) {
for (i = 0; i < (sizeof(kDayStrs) / sizeof(kDayStrs[0])); i++) {
if (!memcmp(kDayStrs[i], &buffer[scan], strlen(kDayStrs[i])))
break;
}
if (i >=(sizeof(kDayStrs) / sizeof(kDayStrs[0])))
break;
scan += strlen(kDayStrs[i]);
c = buffer[scan];
while (isspace(c) || c == ',')
c = buffer[++scan];
}
if (!isdigit(c)) {
for (i = 0; i < (sizeof(kMonthStrs) / sizeof(kMonthStrs[0])); i++) {
if (!memcmp(kMonthStrs[i], &buffer[scan], strlen(kMonthStrs[i])))
break;
}
if (i >= (sizeof(kMonthStrs) / sizeof(kMonthStrs[0])))
break;
date->month = (i % 12) + 1;
scan += strlen(kMonthStrs[i]);
c = buffer[scan];
while (isspace(c))
c = buffer[++scan];
if (!isdigit(c))
break;
}
for (i = 0; isdigit(c) && (i < 2); i++) {
date->day *= 10;
date->day += c - '0';
c = buffer[++scan];
}
while (isspace(c) || c == '-')
c = buffer[++scan];
if (date->month == 0) {
if (isdigit(c)) {
for (i = 0; isdigit(c) && (i < 2); i++) {
date->month *= 10;
date->month += c - '0';
c = buffer[++scan];
}
}
else {
for (i = 0; i < (sizeof(kMonthStrs) / sizeof(kMonthStrs[0])); i++) {
if (!memcmp(kMonthStrs[i], &buffer[scan], strlen(kMonthStrs[i])))
break;
}
if (i >= (sizeof(kMonthStrs) / sizeof(kMonthStrs[0])))
break;
date->month = (i % 12) + 1;
scan += strlen(kMonthStrs[i]);
c = buffer[scan];
}
while (isspace(c) || c == '-')
c = buffer[++scan];
for (i = 0; isdigit(c) && (i < 4); i++) {
date->year *= 10;
date->year += c - '0';
c = buffer[++scan];
}
while (isspace(c))
c = buffer[++scan];
}
for (i = 0; isdigit(c) && (i < 2); i++) {
date->hour *= 10;
date->hour += c - '0';
c = buffer[++scan];
}
if (c != ':')
break;
c = buffer[++scan];
for (i = 0; isdigit(c) && (i < 2); i++) {
date->minute *= 10;
date->minute += c - '0';
c = buffer[++scan];
}
if (c == ':') {
c = buffer[++scan];
for (i = 0; isdigit(c) && (i < 2); i++) {
date->second *= 10;
date->second += c - '0';
c = buffer[++scan];
}
c = buffer[++scan];
}
if (date->year == 0) {
while (isspace(c))
c = buffer[++scan];
for (i = 0; isdigit(c) && (i < 4); i++) {
date->year *= 10;
date->year += c - '0';
c = buffer[++scan];
}
}
if (date->year && date->year < 100) {
if (date->year < 70)
date->year += 2000;
else
date->year += 1900;
}
while (isspace(c))
c = buffer[++scan];
if (c && tz) {
if ((c == '+') || (c == '-')) {
char sign = c;
CFTimeInterval minutes = 0, offset = 0;
c = buffer[++scan];
for (i = 0; isdigit(c) && (i < 2); i++) {
offset *= 10;
offset += c - '0';
c = buffer[++scan];
}
for (i = 0; isdigit(c) && (i < 2); i++) {
minutes *= 10;
minutes += c - '0';
c = buffer[++scan];
}
offset *= 60;
offset += minutes;
if (sign == '-') offset *= -60;
else offset *= 60;
*tz = CFTimeZoneCreateWithTimeIntervalFromGMT(alloc, offset);
}
else if (!strncmp((const char*)(&buffer[scan]), "UT", 2)) {
*tz = CFTimeZoneCreateWithTimeIntervalFromGMT(alloc, 0);
scan += 2;
}
else if (!strncmp((const char*)(&buffer[scan]), "GMT", 3)) {
*tz = CFTimeZoneCreateWithTimeIntervalFromGMT(alloc, 0);
scan += 3;
}
else if (isalpha(c)) {
UInt8 next = buffer[scan + 1];
if ((c != 'J') && (!next || isspace(next) || (next == '*'))) {
if (c == 'Z')
*tz = CFTimeZoneCreateWithTimeIntervalFromGMT(alloc, 0);
else {
CFTimeInterval offset = (c < 'N') ? (c - 'A' + 1) : ('M' - c);
offset *= 60;
if (next == '*') {
scan++;
offset = (offset < 0) ? offset - 30 : offset + 30;
}
offset *= 60;
*tz = CFTimeZoneCreateWithTimeIntervalFromGMT(alloc, 0);
}
}
else {
for (i = 0; i < (sizeof(kUSTimeZones) / sizeof(kUSTimeZones[0])); i++) {
if (!memcmp(kUSTimeZones[i], &buffer[scan], strlen(kUSTimeZones[i]))) {
*tz = CFTimeZoneCreateWithTimeIntervalFromGMT(alloc, (-8 + (i >> 2) + (i & 0x1)) * 3600);
scan += strlen(kUSTimeZones[i]);
break;
}
}
}
}
}
if (!CFGregorianDateIsValid(*date, kCFGregorianAllUnits))
break;
return bytes + scan;
} while (1);
memset(date, 0, sizeof(date[0]));
if (tz) {
if (*tz) CFRelease(*tz);
*tz = NULL;
}
return bytes;
}
CFIndex
_CFGregorianDateCreateWithString(CFAllocatorRef alloc, CFStringRef str, CFGregorianDate* date, CFTimeZoneRef* tz) {
UInt8 buffer[256];
CFIndex length = CFStringGetLength(str);
CFIndex result = 0;
CFStringGetBytes(str, CFRangeMake(0, length), kCFStringEncodingASCII, 0, FALSE, buffer, sizeof(buffer), &length);
if (length)
result = _CFGregorianDateCreateWithBytes(alloc, buffer, length, date, tz) - buffer;
else {
memset(date, 0, sizeof(date[0]));
if (tz) *tz = NULL;
}
return result;
}
CFStringRef
_CFStringCreateRFC1123DateStringWithGregorianDate(CFAllocatorRef alloc, CFGregorianDate* date, CFTimeZoneRef tz) {
CFStringRef result = NULL;
int hour = 0;
int minute = 0;
if (tz) {
CFTimeInterval offset = CFTimeZoneGetSecondsFromGMT(tz, 0.0);
hour = offset / 3600;
minute = abs(offset - (hour * 3600));
}
if (CFGregorianDateIsValid(*date, kCFGregorianAllUnits)) {
result = CFStringCreateWithFormat(alloc,
NULL,
CFSTR("%02d %s %04ld %02d:%02d:%02d %+03d%02d"),
date->day,
kMonthStrs[date->month + 11],
date->year,
date->hour,
date->minute,
(int)date->second,
hour,
minute);
}
return result;
}
CFStringRef
_CFStringCreateRFC2616DateStringWithGregorianDate(CFAllocatorRef alloc, CFGregorianDate* date, CFTimeZoneRef tz) {
CFStringRef result = NULL;
if (CFGregorianDateIsValid(*date, kCFGregorianAllUnits)) {
CFAbsoluteTime t = CFGregorianDateGetAbsoluteTime(*date, tz);
SInt32 day = CFAbsoluteTimeGetDayOfWeek(t, NULL);
result = CFStringCreateWithFormat(alloc,
NULL,
CFSTR("%s, %02d %s %04ld %02d:%02d:%02d GMT"),
kDayStrs[6 + day],
date->day,
kMonthStrs[date->month + 11],
date->year,
date->hour,
date->minute,
(int)date->second);
}
return result;
}
#if defined(__WIN32__)
extern void _CFFTPCleanup(void);
WINBOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved) {
if (dwReason == DLL_PROCESS_DETACH) {
_CFHTTPMessageCleanup();
_CFHTTPStreamCleanup();
_CFFTPCleanup();
}
return TRUE;
}
#endif // __WIN32__