1#include "mega/osx/osxutils.h"
2#if defined(__APPLE__) && !(TARGET_OS_IPHONE)
3#include <Cocoa/Cocoa.h>
4#include <SystemConfiguration/SystemConfiguration.h>
5#elif TARGET_OS_IOS
6#include <Foundation/Foundation.h>
7#endif
8
9#include "mega.h"
10
11using namespace mega;
12using namespace std;
13
14enum { HTTP_PROXY = 0, HTTPS_PROXY };
15
16void path2localMac(const string* path, string* local)
17{
18    if (!path->size())
19    {
20        *local = "";
21        return;
22    }
23    // Multiple calls to path2localMac cause a high memory usage on macOS. To avoid it, use autorelease pool to release any temp object at the end of the pool.
24    // At the end of the block, the temporary objects are released, which typically results in their deallocation thereby reducing the program’s memory footprint.
25    @autoreleasepool {
26
27        // Compatibility with new APFS filesystem
28        // Use the fileSystemRepresentation property of NSString objects when creating and opening
29        // files with lower-level filesystem APIs such as POSIX open(2), or when storing filenames externally from the filesystem`
30        NSString *tempPath = [[NSString alloc] initWithUTF8String:path->c_str()];
31        const char *pathRepresentation = NULL;
32        @try
33        {
34            pathRepresentation = [tempPath fileSystemRepresentation];
35        }
36        @catch (NSException *e)
37        {
38             LOG_err << "Failed getting file system representation (APFS filesystem)";
39             local->clear();
40    #if !__has_feature(objc_arc)
41             [tempPath release];
42    #endif
43             return;
44        }
45
46        if (pathRepresentation)
47        {
48           *local = pathRepresentation;
49        }
50        else
51        {
52            local->clear();
53        }
54    #if !__has_feature(objc_arc)
55        [tempPath release];
56    #endif
57
58    }
59}
60
61#if defined(__APPLE__) && !(TARGET_OS_IPHONE)
62
63CFTypeRef getValueFromKey(CFDictionaryRef dict, const void *key, CFTypeID type)
64{
65    CFTypeRef value = CFDictionaryGetValue(dict, key);
66    if (!value)
67    {
68        return NULL;
69    }
70
71    if ((CFGetTypeID(value) == type))
72    {
73        return value;
74    }
75
76    return NULL;
77}
78
79bool getProxyConfiguration(CFDictionaryRef dict, int proxyType, Proxy* proxy)
80{
81    int isEnabled = 0;
82    int port;
83
84    if (proxyType != HTTPS_PROXY && proxyType != HTTP_PROXY)
85    {
86        return false;
87    }
88
89    CFStringRef proxyEnableKey = proxyType == HTTP_PROXY ? kSCPropNetProxiesHTTPEnable : kSCPropNetProxiesHTTPSEnable;
90    CFStringRef proxyHostKey   = proxyType == HTTP_PROXY ? kSCPropNetProxiesHTTPProxy : kSCPropNetProxiesHTTPSProxy;
91    CFStringRef portKey        = proxyType == HTTP_PROXY ? kSCPropNetProxiesHTTPPort : kSCPropNetProxiesHTTPSPort;
92
93    CFNumberRef proxyEnabledRef = (CFNumberRef)getValueFromKey(dict, proxyEnableKey, CFNumberGetTypeID());
94    if (proxyEnabledRef && CFNumberGetValue(proxyEnabledRef, kCFNumberIntType, &isEnabled) && (isEnabled != 0))
95    {
96        if ([(NSDictionary*)dict valueForKey: proxyType == HTTP_PROXY ? @"HTTPUser" : @"HTTPSUser"] != nil)
97        {
98            // Username set, skip proxy configuration. We only allow proxies withouth user/pw credentials
99            // to not have to request the password to the user to read the keychain
100            return false;
101        }
102
103        CFStringRef hostRef = (CFStringRef)getValueFromKey(dict, proxyHostKey, CFStringGetTypeID());
104        if (hostRef)
105        {
106            CFIndex length = CFStringGetLength(hostRef);
107            CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
108            char *buffer = new char[maxSize];
109            if (CFStringGetCString(hostRef, buffer, maxSize, kCFStringEncodingUTF8))
110            {
111                CFNumberRef portRef = (CFNumberRef)getValueFromKey(dict, portKey, CFNumberGetTypeID());
112                if (portRef && CFNumberGetValue(portRef, kCFNumberIntType, &port))
113                {
114                    ostringstream oss;
115                    oss << (proxyType == HTTP_PROXY ? "http://" : "https://") << buffer << ":" << port;
116                    string link = oss.str();
117                    proxy->setProxyType(Proxy::CUSTOM);
118                    proxy->setProxyURL(&link);
119                    delete [] buffer;
120                    return true;
121                }
122            }
123            delete [] buffer;
124        }
125    }
126    return false;
127}
128
129void getOSXproxy(Proxy* proxy)
130{
131    CFDictionaryRef proxySettings = NULL;
132    proxySettings = SCDynamicStoreCopyProxies(NULL);
133    if (!proxySettings)
134    {
135        return;
136    }
137
138    if (!getProxyConfiguration(proxySettings, HTTPS_PROXY, proxy))
139    {
140        getProxyConfiguration(proxySettings, HTTP_PROXY, proxy);
141    }
142    CFRelease(proxySettings);
143}
144#endif
145