@@ -20,8 +20,14 @@ @implementation SWBAppDelegate {
20
20
NSMenuItem *statusMenuItem;
21
21
NSMenuItem *enableMenuItem;
22
22
BOOL isRunning;
23
+ NSData *originalPACData;
24
+ FSEventStreamRef fsEventStream;
25
+ NSString *configPath;
26
+ NSString *PACPath;
23
27
}
24
28
29
+ static SWBAppDelegate *appDelegate;
30
+
25
31
- (void )applicationDidFinishLaunching : (NSNotification *)aNotification {
26
32
[[NSAppleEventManager sharedAppleEventManager ] setEventHandler: self andSelector: @selector (handleURLEvent:withReplyEvent: ) forEventClass: kInternetEventClass andEventID: kAEGetURL ];
27
33
@@ -31,10 +37,10 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
31
37
[self runProxy ];
32
38
});
33
39
34
- NSData *pacData = [[NSData dataWithContentsOfURL: [[NSBundle mainBundle ] URLForResource: @" proxy" withExtension: @" pac.gz" ]] gunzippedData ];
40
+ originalPACData = [[NSData dataWithContentsOfURL: [[NSBundle mainBundle ] URLForResource: @" proxy" withExtension: @" pac.gz" ]] gunzippedData ];
35
41
GCDWebServer *webServer = [[GCDWebServer alloc ] init ];
36
42
[webServer addHandlerForMethod: @" GET" path: @" /proxy.pac" requestClass: [GCDWebServerRequest class ] processBlock: ^GCDWebServerResponse *(GCDWebServerRequest *request) {
37
- return [GCDWebServerDataResponse responseWithData: pacData contentType: @" application/x-ns-proxy-autoconfig" ];
43
+ return [GCDWebServerDataResponse responseWithData: [ self PACData ] contentType: @" application/x-ns-proxy-autoconfig" ];
38
44
}
39
45
];
40
46
@@ -53,6 +59,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
53
59
[menu addItem: enableMenuItem];
54
60
[menu addItem: [NSMenuItem separatorItem ]];
55
61
[menu addItemWithTitle: _L (Open Server Preferences...) action: @selector (showConfigWindow ) keyEquivalent: @" " ];
62
+ [menu addItemWithTitle: _L (Edit PAC...) action: @selector (editPAC ) keyEquivalent: @" " ];
56
63
[menu addItemWithTitle: _L (Show Logs...) action: @selector (showLogs ) keyEquivalent: @" " ];
57
64
[menu addItemWithTitle: _L (Help) action: @selector (showHelp ) keyEquivalent: @" " ];
58
65
[menu addItem: [NSMenuItem separatorItem ]];
@@ -64,6 +71,19 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
64
71
configWindowController = [[SWBConfigWindowController alloc ] initWithWindowNibName: @" ConfigWindow" ];
65
72
66
73
[self updateMenu ];
74
+
75
+ configPath = [NSString stringWithFormat: @" %@ /%@ " , NSHomeDirectory (), @" .ShadowsocksX" ];
76
+ PACPath = [NSString stringWithFormat: @" %@ /%@ " , configPath, @" gfwlist.js" ];
77
+ [self monitorPAC: configPath];
78
+ appDelegate = self;
79
+ }
80
+
81
+ - (NSData *)PACData {
82
+ if ([[NSFileManager defaultManager ] fileExistsAtPath: PACPath]) {
83
+ return [NSData dataWithContentsOfFile: PACPath];
84
+ } else {
85
+ return originalPACData;
86
+ }
67
87
}
68
88
69
89
- (void )toggleRunning {
@@ -86,6 +106,60 @@ - (void)updateMenu {
86
106
}
87
107
}
88
108
109
+ void onPACChange (
110
+ ConstFSEventStreamRef streamRef,
111
+ void *clientCallBackInfo,
112
+ size_t numEvents,
113
+ void *eventPaths,
114
+ const FSEventStreamEventFlags eventFlags[],
115
+ const FSEventStreamEventId eventIds[])
116
+ {
117
+ [appDelegate reloadPAC ];
118
+ }
119
+
120
+ - (void )reloadPAC {
121
+ if (isRunning) {
122
+ [self toggleSystemProxy: NO ];
123
+ [self toggleSystemProxy: YES ];
124
+ }
125
+ }
126
+
127
+ - (void )monitorPAC : (NSString *)pacPath {
128
+ if (fsEventStream) {
129
+ return ;
130
+ }
131
+ CFStringRef mypath = (__bridge CFStringRef )(pacPath);
132
+ CFArrayRef pathsToWatch = CFArrayCreate (NULL , (const void **)&mypath, 1 , NULL );
133
+ void *callbackInfo = NULL ; // could put stream-specific data here.
134
+ CFAbsoluteTime latency = 3.0 ; /* Latency in seconds */
135
+
136
+ /* Create the stream, passing in a callback */
137
+ fsEventStream = FSEventStreamCreate (NULL ,
138
+ &onPACChange,
139
+ callbackInfo,
140
+ pathsToWatch,
141
+ kFSEventStreamEventIdSinceNow , /* Or a previous event ID */
142
+ latency,
143
+ kFSEventStreamCreateFlagNone /* Flags explained in reference */
144
+ );
145
+ FSEventStreamScheduleWithRunLoop (fsEventStream, [[NSRunLoop mainRunLoop ] getCFRunLoop ], (__bridge CFStringRef )NSDefaultRunLoopMode );
146
+ FSEventStreamStart (fsEventStream);
147
+ }
148
+
149
+ - (void )editPAC {
150
+
151
+ if (![[NSFileManager defaultManager ] fileExistsAtPath: PACPath]) {
152
+ NSError *error = nil ;
153
+ [[NSFileManager defaultManager ] createDirectoryAtPath: configPath withIntermediateDirectories: NO attributes: nil error: &error];
154
+ // TODO check error
155
+ [originalPACData writeToFile: PACPath atomically: YES ];
156
+ }
157
+ [self monitorPAC: configPath];
158
+
159
+ NSArray *fileURLs = @[[NSURL fileURLWithPath: PACPath]];
160
+ [[NSWorkspace sharedWorkspace ] activateFileViewerSelectingURLs: fileURLs];
161
+ }
162
+
89
163
- (void )showLogs {
90
164
[[NSWorkspace sharedWorkspace ] launchApplication: @" /Applications/Utilities/Console.app" ];
91
165
}
0 commit comments