Skip to content

Implementation of XDG File specification and a basic implementation of HTTPCookieStorage #889

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions CoreFoundation/Base.subproj/CFFileUtilities.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>

#if DEPLOYMENT_TARGET_WINDOWS
#include <io.h>
Expand Down Expand Up @@ -1166,3 +1167,181 @@ CF_PRIVATE void _CFIterateDirectory(CFStringRef directoryPath, Boolean appendSla
#endif
}

#if DEPLOYMENT_RUNTIME_SWIFT

// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
// Version 0.8

// This may not be safe to assume
#define _kCFXDGStringEncoding kCFStringEncodingUTF8

// All paths set in these environment variables must be absolute. If an implementation encounters a relative path in any of these variables it should consider the path invalid and ignore it.
static CFStringRef _CFXDGCreateHome(void) {
const char *home = __CFgetenv("HOME");
if (home && strnlen(home, CFMaxPathSize) > 0) {
return CFStringCreateWithCString(kCFAllocatorSystemDefault, home, _kCFXDGStringEncoding);
} else {
return CFRetain(CFSTR(""));
}
}

/// a single base directory relative to which user-specific data files should be written. This directory is defined by the environment variable $XDG_DATA_HOME.
CF_SWIFT_EXPORT
CFStringRef _CFXDGCreateDataHomePath(void) {
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
const char *dataHome = __CFgetenv("XDG_DATA_HOME");
if (dataHome && strnlen(dataHome, CFMaxPathSize) > 1 && dataHome[0] == '/') {
return CFStringCreateWithCString(kCFAllocatorSystemDefault, dataHome, _kCFXDGStringEncoding);
} else {
CFStringRef home = _CFXDGCreateHome();
CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/.local/share"), home);
CFRelease(home);
return result;
}
}

/// a single base directory relative to which user-specific configuration files should be written. This directory is defined by the environment variable $XDG_CONFIG_HOME.
CF_SWIFT_EXPORT
CFStringRef _CFXDGCreateConfigHomePath(void) {
// $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used.
const char *configHome = __CFgetenv("XDG_CONFIG_HOME");
if (configHome && strnlen(configHome, CFMaxPathSize) > 1 && configHome[0] == '/') {
return CFStringCreateWithCString(kCFAllocatorSystemDefault, configHome, _kCFXDGStringEncoding);
} else {
CFStringRef home = _CFXDGCreateHome();
CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/.config"), home);
CFRelease(home);
return result;
}
}

/// a set of preference ordered base directories relative to which data files should be searched. This set of directories is defined by the environment variable $XDG_DATA_DIRS.
CF_SWIFT_EXPORT
CFArrayRef _CFXDGCreateDataDirectoriesPaths(void) {
// $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory. The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
// If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
const char *dataDirectoriesPaths = __CFgetenv("XDG_DATA_DIRS");
CFStringRef defaultPath[2];
defaultPath[0] = CFSTR("/usr/local/share");
defaultPath[1] = CFSTR("/usr/share");
if ((dataDirectoriesPaths == NULL) || (dataDirectoriesPaths[0] == '\0')) {
// Environmental variable not set. Return default value.
return CFArrayCreate(kCFAllocatorSystemDefault, (const void **)defaultPath, 2, &kCFTypeArrayCallBacks);
}
CFArrayRef dataDirPathsArray = _CFCreateCFArrayByTokenizingString(dataDirectoriesPaths, ':');
if(CFArrayGetCount(dataDirPathsArray) == 0) {
CFStringRef logMessage = CFSTR("Value set in XDG_DATA_DIRS variable not honoured. Returning the default.");
CFLog(kCFLogLevelWarning, CFSTR("%@"), logMessage);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of the pathCount > 64 or all the paths in the variable turn out to be invalid, an empty array is returned. At such times the default path is returned although the variable was set. The log warning message gives a hint.

CFRelease(dataDirPathsArray);
return CFArrayCreate(kCFAllocatorSystemDefault, (const void **)defaultPath, 2, &kCFTypeArrayCallBacks);
}
return dataDirPathsArray;
}


/// a set of preference ordered base directories relative to which configuration files should be searched. This set of directories is defined by the environment variable $XDG_CONFIG_DIRS.
CF_SWIFT_EXPORT
CFArrayRef _CFXDGCreateConfigDirectoriesPaths(void) {
// $XDG_CONFIG_DIRS defines the preference-ordered set of base directories to search for configuration files in addition to the $XDG_CONFIG_HOME base directory. The directories in $XDG_CONFIG_DIRS should be seperated with a colon ':'.
// If $XDG_CONFIG_DIRS is either not set or empty, a value equal to /etc/xdg should be used.
const char *configDirectoriesPaths = __CFgetenv("XDG_CONFIG_DIRS");
CFStringRef defaultPath[1];
defaultPath[0] = CFSTR("/etc/xdg");
if ((configDirectoriesPaths == NULL) || (configDirectoriesPaths[0] == '\0')) {
//Environmental variable not set. Return default value.
return CFArrayCreate(kCFAllocatorSystemDefault, (const void **)defaultPath, 1, &kCFTypeArrayCallBacks);
}
CFArrayRef configDirPathsArray = _CFCreateCFArrayByTokenizingString(configDirectoriesPaths, ':');
if(CFArrayGetCount(configDirPathsArray) == 0) {
CFStringRef logMessage = CFSTR("Value set in XDG_CONFIG_DIRS variable not honoured. Returning the default.");
CFLog(kCFLogLevelWarning, CFSTR("%@"), logMessage);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of the pathCount > 64 or all the paths in the variable turn out to be invalid, an empty array is returned. At such times the default path is returned although the variable was set. The log warning message gives a hint.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok; one more thing now - we need to release configDirPathsArray here before returning, otherwise we leak it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@parkera Sorry I missed that. Done now.

CFRelease(configDirPathsArray);
return CFArrayCreate(kCFAllocatorSystemDefault, (const void **)defaultPath, 1, &kCFTypeArrayCallBacks);
}
return configDirPathsArray;
}

/// a single base directory relative to which user-specific non-essential (cached) data should be written. This directory is defined by the environment variable $XDG_CACHE_HOME.
CF_SWIFT_EXPORT
CFStringRef _CFXDGCreateCacheDirectoryPath(void) {
//$XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to $HOME/.cache should be used.
const char *cacheHome = __CFgetenv("XDG_CACHE_HOME");
const char *path = __CFgetenv("PATH");
if (cacheHome && strnlen(cacheHome, CFMaxPathSize) > 1 && cacheHome[0] == '/') {
return CFStringCreateWithCString(kCFAllocatorSystemDefault, cacheHome, _kCFXDGStringEncoding);
} else {
CFStringRef home = _CFXDGCreateHome();
CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/.cache"), home);
CFRelease(home);
return result;
}
}

/// a single base directory relative to which user-specific runtime files and other file objects should be placed. This directory is defined by the environment variable $XDG_RUNTIME_DIR.
CF_SWIFT_EXPORT
CFStringRef _CFXDGCreateRuntimeDirectoryPath(void) {
const char *runtimeDir = __CFgetenv("XDG_RUNTIME_DIR");
if (runtimeDir && strnlen(runtimeDir, CFMaxPathSize) > 1 && runtimeDir[0] == '/') {
return CFStringCreateWithCString(kCFAllocatorSystemDefault, runtimeDir, _kCFXDGStringEncoding);
} else {
// If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with similar capabilities and print a warning message.
return CFStringCreateWithCString(kCFAllocatorSystemDefault, "", _kCFXDGStringEncoding);
}
}


CF_PRIVATE CFArrayRef _CFCreateCFArrayByTokenizingString(const char *values, char delimiter) {
size_t pathCount = 0;
char* tmpDirectoriesPaths = (char*)values;
char* last_colon = 0;
// Count how many paths will be extracted.
while (*tmpDirectoriesPaths)
{
if (*tmpDirectoriesPaths == delimiter)
{
pathCount++;
last_colon = tmpDirectoriesPaths;
}
tmpDirectoriesPaths++;
}
// Add count for trailing path unless ending with colon.
pathCount += last_colon < (values + strlen(values) - 1);
if (pathCount > 64) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Limits the number of paths to 64 and returns an empty array if pathCount exceeds 64.

return CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks);
}
if (pathCount > 0)
{
size_t validPathCount = 0;
CFStringRef pathList[pathCount];
char* copyDirPath = strdup(values);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another free needed for this.

char delimiterStr[2];
delimiterStr[0] = delimiter;
delimiterStr[1] = '\0';
char* path = strtok(copyDirPath, delimiterStr);
while (path)
{
assert(validPathCount < pathCount);
char* pathString = strdup(path);
CFStringRef dirPath = CFStringCreateWithCString(kCFAllocatorSystemDefault, pathString, _kCFXDGStringEncoding);
CFStringRef slash = CFSTR("/");
CFStringRef tilde = CFSTR("~");
// Check for absolutePath, if not ignore.
if (CFStringHasPrefix(dirPath, slash) || CFStringHasPrefix(dirPath, tilde) ) {
pathList[validPathCount++] = dirPath;
} else {
CFRelease(dirPath);
}
path = strtok(NULL, delimiterStr);
free(pathString);
}
free(copyDirPath);
CFArrayRef pathArray = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)pathList , validPathCount, &kCFTypeArrayCallBacks);
for(int i = 0; i < validPathCount; i ++) {
CFRelease(pathList[i]);
}
return pathArray;
}
return CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks);
}

#endif
1 change: 1 addition & 0 deletions CoreFoundation/Base.subproj/CFInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ CF_PRIVATE CFIndex _CFLengthAfterDeletingPathExtension2(CFStringRef path);
CF_EXPORT CFIndex _CFStartOfPathExtension(UniChar *unichars, CFIndex length);
CF_PRIVATE CFIndex _CFStartOfPathExtension2(CFStringRef path);
CF_EXPORT CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex length);
CF_PRIVATE CFArrayRef _CFCreateCFArrayByTokenizingString(const char *values, char delimiter);

#if __BLOCKS__
#if DEPLOYMENT_TARGET_WINDOWS
Expand Down
9 changes: 9 additions & 0 deletions CoreFoundation/Base.subproj/CFRuntime.c
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,15 @@ static struct {
{"CFNumberDisableCache", NULL},
{"__CFPREFERENCES_AVOID_DAEMON", NULL},
{"APPLE_FRAMEWORKS_ROOT", NULL},
#if DEPLOYMENT_RUNTIME_SWIFT
{"HOME", NULL},
{"XDG_DATA_HOME", NULL},
{"XDG_CONFIG_HOME", NULL},
{"XDG_DATA_DIRS", NULL},
{"XDG_CONFIG_DIRS", NULL},
{"XDG_CACHE_HOME", NULL},
{"XDG_RUNTIME_DIR", NULL},
#endif
{NULL, NULL}, // the last one is for optional "COMMAND_MODE" "legacy", do not use this slot, insert before
};

Expand Down
23 changes: 23 additions & 0 deletions CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,29 @@ CF_EXPORT _Nullable CFErrorRef _CFReadStreamCopyError(CFReadStreamRef stream);

CF_EXPORT _Nullable CFErrorRef _CFWriteStreamCopyError(CFWriteStreamRef stream);

// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
// Version 0.8

// note: All paths set in these environment variables must be absolute.

/// a single base directory relative to which user-specific data files should be written. This directory is defined by the environment variable $XDG_DATA_HOME.
CF_EXPORT CFStringRef _CFXDGCreateDataHomePath(void);

/// a single base directory relative to which user-specific configuration files should be written. This directory is defined by the environment variable $XDG_CONFIG_HOME.
CF_EXPORT CFStringRef _CFXDGCreateConfigHomePath(void);

/// a set of preference ordered base directories relative to which data files should be searched. This set of directories is defined by the environment variable $XDG_DATA_DIRS.
CF_EXPORT CFArrayRef _CFXDGCreateDataDirectoriesPaths(void);

/// a set of preference ordered base directories relative to which configuration files should be searched. This set of directories is defined by the environment variable $XDG_CONFIG_DIRS.
CF_EXPORT CFArrayRef _CFXDGCreateConfigDirectoriesPaths(void);

/// a single base directory relative to which user-specific non-essential (cached) data should be written. This directory is defined by the environment variable $XDG_CACHE_HOME.
CF_EXPORT CFStringRef _CFXDGCreateCacheDirectoryPath(void);

/// a single base directory relative to which user-specific runtime files and other file objects should be placed. This directory is defined by the environment variable $XDG_RUNTIME_DIR.
CF_EXPORT CFStringRef _CFXDGCreateRuntimeDirectoryPath(void);

_CF_EXPORT_SCOPE_END

#endif /* __COREFOUNDATION_FORSWIFTFOUNDATIONONLY__ */
4 changes: 4 additions & 0 deletions Foundation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
0383A1751D2E558A0052E5D1 /* TestNSStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0383A1741D2E558A0052E5D1 /* TestNSStream.swift */; };
1520469B1D8AEABE00D02E36 /* HTTPServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1520469A1D8AEABE00D02E36 /* HTTPServer.swift */; };
159884921DCC877700E3314C /* TestNSHTTPCookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159884911DCC877700E3314C /* TestNSHTTPCookieStorage.swift */; };
231503DB1D8AEE5D0061694D /* TestNSDecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231503DA1D8AEE5D0061694D /* TestNSDecimal.swift */; };
294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */; };
2EBE67A51C77BF0E006583D5 /* TestNSDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBE67A31C77BF05006583D5 /* TestNSDateFormatter.swift */; };
Expand Down Expand Up @@ -466,6 +467,7 @@
/* Begin PBXFileReference section */
0383A1741D2E558A0052E5D1 /* TestNSStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSStream.swift; sourceTree = "<group>"; };
1520469A1D8AEABE00D02E36 /* HTTPServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPServer.swift; sourceTree = "<group>"; };
159884911DCC877700E3314C /* TestNSHTTPCookieStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSHTTPCookieStorage.swift; sourceTree = "<group>"; };
22B9C1E01C165D7A00DECFF9 /* TestNSDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSDate.swift; sourceTree = "<group>"; };
231503DA1D8AEE5D0061694D /* TestNSDecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSDecimal.swift; sourceTree = "<group>"; };
294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSAttributedString.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1338,6 +1340,7 @@
EA66F65A1BF1976100136161 /* Tests */ = {
isa = PBXGroup;
children = (
159884911DCC877700E3314C /* TestNSHTTPCookieStorage.swift */,
D4FE895A1D703D1100DA7986 /* TestURLRequest.swift */,
C93559281C12C49F009FD6A9 /* TestNSAffineTransform.swift */,
EA66F63C1BF1619600136161 /* TestNSArray.swift */,
Expand Down Expand Up @@ -2235,6 +2238,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
159884921DCC877700E3314C /* TestNSHTTPCookieStorage.swift in Sources */,
5FE52C951D147D1C00F7D270 /* TestNSTextCheckingResult.swift in Sources */,
5B13B3451C582D4C00651CE2 /* TestNSString.swift in Sources */,
1520469B1D8AEABE00D02E36 /* HTTPServer.swift in Sources */,
Expand Down
Loading