// Copyright 1997-2002 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import <OWF/OWProxyServer.h>

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>

#import <OWF/OWNetLocation.h>
#import <OWF/OWURL.h>

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OWF/Content.subproj/Address.subproj/OWProxyServer.m,v 1.25 2002/03/09 01:53:51 kc Exp $")

@interface OWProxyServer (Private)
+ (void)_startMonitoringProxySettings;
+ (void)_updateProxySettingsFromDictionary:(NSDictionary *)dictionary;
+ (void)_setProxySettingsDictionary:(NSDictionary *)newDictionary;
+ (NSDictionary *)_proxySettingsDictionary;
+ (OWURL *)_proxyURLWithHost:(NSString *)proxyHost port:(unsigned int)proxyPort;
@end

static NSLock *_proxySettingsDictionaryLock = nil;
static NSDictionary *_proxySettingsDictionary = nil;
static NSString *OWProxiesExceptionsListKey = @"ExceptionsList";

@implementation OWProxyServer

+ (void)initialize;
{
    OBINITIALIZE;

    _proxySettingsDictionaryLock = [[NSLock alloc] init];
    [self _startMonitoringProxySettings];
}

+ (OWURL *)proxyURLForURL:(OWURL *)aURL;
{
    NSDictionary *proxySettingsDictionary;
    OWURL *proxyURL;
    NSArray *proxyExceptions;

    // Look up the proxy server for the URL's scheme
    proxySettingsDictionary = [self _proxySettingsDictionary];
    proxyURL = [proxySettingsDictionary objectForKey:[aURL scheme]];
    if (proxyURL == nil)
        return aURL; // No proxy server for this scheme

    // Check whether the URL's hostname is listed in the "bypass proxy server" settings
    proxyExceptions = [proxySettingsDictionary objectForKey:OWProxiesExceptionsListKey];
    if (proxyExceptions && [proxyExceptions count] != 0) {
        unsigned int proxyExceptionIndex, proxyExceptionCount;
        OWNetLocation *urlNetLocation;
        NSString *urlHostname;

        urlNetLocation = [aURL parsedNetLocation];
        urlHostname = [[urlNetLocation hostname] lowercaseString];
        proxyExceptionCount = [proxyExceptions count];
        for (proxyExceptionIndex = 0; proxyExceptionIndex < proxyExceptionCount; proxyExceptionIndex++) {
            NSString *proxyException;

            proxyException = [proxyExceptions objectAtIndex:proxyExceptionIndex];
            if ([urlHostname hasSuffix:proxyException])
                return aURL; // The hostname matches the proxy exception list, so don't proxy it
        }
    }

    // Return the proxy server's URL
    return proxyURL;
}

// DEPRECATED

+ (void)registerProxyScheme:(NSString *)scheme;
{
}

@end

#define SCSTR(s) (NSString *)CFSTR(s)
#import <SystemConfiguration/SystemConfiguration.h>

@implementation OWProxyServer (Private)

static void OWProxyServerDynamicStoreCallBack(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info);

+ (void)_startMonitoringProxySettings;
{
    SCDynamicStoreRef store;
    CFStringRef proxiesKey;
    CFRunLoopSourceRef runLoopSource;
    CFRunLoopRef currentRunLoop;

    store = SCDynamicStoreCreate(NULL, (CFStringRef)[[NSProcessInfo processInfo] processName], OWProxyServerDynamicStoreCallBack, NULL);
    proxiesKey = SCDynamicStoreKeyCreateProxies(NULL);
    if (!SCDynamicStoreSetNotificationKeys(store, (CFArrayRef)[NSArray arrayWithObject:(id)proxiesKey], nil))
        NSLog(@"SCDynamicStoreSetNotificationKeys() failed: %s", SCErrorString(SCError()));
    [self _updateProxySettingsFromDictionary:(NSDictionary *)SCDynamicStoreCopyValue(store, proxiesKey)];
    runLoopSource = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
    currentRunLoop = CFRunLoopGetCurrent();
    if (runLoopSource != NULL && currentRunLoop != NULL)
        CFRunLoopAddSource(currentRunLoop, runLoopSource, kCFRunLoopDefaultMode);
}

+ (void)_updateProxySettingsFromDictionary:(NSDictionary *)dictionary;
{
    NSMutableDictionary *newProxySettingsDictionary;
    NSArray *exceptionsList;

    if (![dictionary isKindOfClass:[NSDictionary class]])
        return;

    // Build a new proxy settings dictionary
    newProxySettingsDictionary = [[NSMutableDictionary alloc] init];
    if ([dictionary boolForKey:kSCPropNetProxiesFTPEnable defaultValue:NO]) {
        OWURL *proxyURL;

        proxyURL = [self _proxyURLWithHost:[dictionary objectForKey:kSCPropNetProxiesFTPProxy] port:[dictionary intForKey:kSCPropNetProxiesFTPPort defaultValue:80]];
        [newProxySettingsDictionary setObject:proxyURL forKey:@"ftp"];
    }
    if ([dictionary boolForKey:kSCPropNetProxiesGopherEnable defaultValue:NO]) {
        OWURL *proxyURL;

        proxyURL = [self _proxyURLWithHost:[dictionary objectForKey:kSCPropNetProxiesGopherProxy] port:[dictionary intForKey:kSCPropNetProxiesGopherPort defaultValue:80]];
        [newProxySettingsDictionary setObject:proxyURL forKey:@"gopher"];
    }
    if ([dictionary boolForKey:kSCPropNetProxiesHTTPEnable defaultValue:NO]) {
        OWURL *proxyURL;

        proxyURL = [self _proxyURLWithHost:[dictionary objectForKey:kSCPropNetProxiesHTTPProxy] port:[dictionary intForKey:kSCPropNetProxiesHTTPPort defaultValue:80]];
        [newProxySettingsDictionary setObject:proxyURL forKey:@"http"];
    }
    if ([dictionary boolForKey:kSCPropNetProxiesHTTPSEnable defaultValue:NO]) {
        OWURL *proxyURL;

        proxyURL = [self _proxyURLWithHost:[dictionary objectForKey:kSCPropNetProxiesHTTPSProxy] port:[dictionary intForKey:kSCPropNetProxiesHTTPSPort defaultValue:80]];
        [newProxySettingsDictionary setObject:proxyURL forKey:@"https"];
    }
    exceptionsList = [dictionary objectForKey:kSCPropNetProxiesExceptionsList];
    if (exceptionsList)
        [newProxySettingsDictionary setObject:exceptionsList forKey:OWProxiesExceptionsListKey];

    [self _setProxySettingsDictionary:newProxySettingsDictionary];
    [newProxySettingsDictionary release];
}

+ (void)_setProxySettingsDictionary:(NSDictionary *)newDictionary;
{
    [_proxySettingsDictionaryLock lock];
    [_proxySettingsDictionary release];
    _proxySettingsDictionary = [[NSDictionary alloc] initWithDictionary:newDictionary];
    [_proxySettingsDictionaryLock unlock];
}

+ (NSDictionary *)_proxySettingsDictionary;
{
    NSDictionary *proxySettingsDictionary;

    [_proxySettingsDictionaryLock lock];
    proxySettingsDictionary = [_proxySettingsDictionary retain];
    [_proxySettingsDictionaryLock unlock];
    return [proxySettingsDictionary autorelease];
}

+ (OWURL *)_proxyURLWithHost:(NSString *)proxyHost port:(unsigned int)proxyPort;
{
#define HTTPS_PORT 443

    return [OWURL urlWithScheme:proxyPort == HTTPS_PORT ? @"https" : @"http" netLocation:[NSString stringWithFormat:@"%@:%d", proxyHost, proxyPort] path:nil params:nil query:nil fragment:nil];
}

static void OWProxyServerDynamicStoreCallBack(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
{
    OBASSERT([(NSArray *)changedKeys count] == 1); // We've only registered for one key
    [OWProxyServer _updateProxySettingsFromDictionary:(NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)[(NSArray *)changedKeys objectAtIndex:0])];
}

@end
