1 |
/* gwsd.m |
2 |
* |
3 |
* Copyright (C) 2003 Free Software Foundation, Inc. |
4 |
* |
5 |
* Author: Enrico Sersale <enrico@imago.ro> |
6 |
* Date: August 2001 |
7 |
* |
8 |
* This file is part of the GNUstep gwsd tool |
9 |
* |
10 |
* This program is free software; you can redistribute it and/or modify |
11 |
* it under the terms of the GNU General Public License as published by |
12 |
* the Free Software Foundation; either version 2 of the License, or |
13 |
* (at your option) any later version. |
14 |
* |
15 |
* This program is distributed in the hope that it will be useful, |
16 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 |
* GNU General Public License for more details. |
19 |
* |
20 |
* You should have received a copy of the GNU General Public License |
21 |
* along with this program; if not, write to the Free Software |
22 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
23 |
*/ |
24 |
|
25 |
#include <AppKit/AppKit.h> |
26 |
#include "gwsd.h" |
27 |
#include "FileOp.h" |
28 |
#include "externs.h" |
29 |
#include "Functions.h" |
30 |
#include "GNUstep.h" |
31 |
#include <stdio.h> |
32 |
#include <unistd.h> |
33 |
#include <limits.h> |
34 |
#include <pwd.h> |
35 |
|
36 |
#ifndef LONG_DELAY |
37 |
#define LONG_DELAY 86400.0 |
38 |
#endif |
39 |
|
40 |
#define byname 0 |
41 |
#define bykind 1 |
42 |
#define bydate 2 |
43 |
#define bysize 3 |
44 |
#define byowner 4 |
45 |
|
46 |
#define MAX_FILE_SIZE 41200 |
47 |
|
48 |
#ifndef CACHED_MAX |
49 |
#define CACHED_MAX 20; |
50 |
#endif |
51 |
|
52 |
static GWSd *shared = nil; |
53 |
|
54 |
@implementation GWSd |
55 |
|
56 |
+ (void)initialize |
57 |
{ |
58 |
/* */ |
59 |
/* only the thread that sends the first message to the class */ |
60 |
/* will execute the initialize method. */ |
61 |
/* */ |
62 |
|
63 |
if ([self class] == [GWSd class]) { |
64 |
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
65 |
id entry = [defaults objectForKey: @"cachedmax"]; |
66 |
if (entry) { |
67 |
cachedMax = [entry intValue]; |
68 |
} else { |
69 |
cachedMax = CACHED_MAX; |
70 |
} |
71 |
|
72 |
cachedContents = [NSMutableDictionary new]; |
73 |
gwsdLock = [NSRecursiveLock new]; |
74 |
|
75 |
[GWSd sharedGWSd]; |
76 |
} |
77 |
} |
78 |
|
79 |
+ (GWSd *)sharedGWSd |
80 |
{ |
81 |
/* */ |
82 |
/* we use this shared istance for notifications that must */ |
83 |
/* be received by all the others. */ |
84 |
/* */ |
85 |
|
86 |
if (shared == nil) { |
87 |
shared = [[GWSd alloc] init]; |
88 |
} |
89 |
return shared; |
90 |
} |
91 |
|
92 |
- (id)initWithRemote:(id)remote |
93 |
connection:(NSConnection *)aConnection |
94 |
{ |
95 |
self = [super init]; |
96 |
|
97 |
if(self) { |
98 |
NSUserDefaults *defaults; |
99 |
id entry; |
100 |
|
101 |
defaults = [NSUserDefaults standardUserDefaults]; |
102 |
|
103 |
entry = [defaults objectForKey: @"defaultsorttype"]; |
104 |
if (entry == nil) { |
105 |
[defaults setObject: @"0" forKey: @"defaultsorttype"]; |
106 |
defSortType = byname; |
107 |
} else { |
108 |
defSortType = [entry intValue]; |
109 |
} |
110 |
|
111 |
entry = [defaults objectForKey: @"GSFileBrowserHideDotFiles"]; |
112 |
if (entry) { |
113 |
hideSysFiles = [entry boolValue]; |
114 |
} else { |
115 |
NSDictionary *domain = [defaults persistentDomainForName: NSGlobalDomain]; |
116 |
|
117 |
entry = [domain objectForKey: @"GSFileBrowserHideDotFiles"]; |
118 |
if (entry) { |
119 |
hideSysFiles = [entry boolValue]; |
120 |
} else { |
121 |
hideSysFiles = NO; |
122 |
} |
123 |
} |
124 |
|
125 |
entry = [defaults objectForKey: @"shellCommand"]; |
126 |
if (entry == nil) { |
127 |
NSLog(@"no shell defined.\nYou must set the default shell:"); |
128 |
NSLog(@"\"defaults write gwsd shellCommand \"/bin/bash\"\", for example."); |
129 |
exit(0); |
130 |
} |
131 |
ASSIGN (shellCommand, entry); |
132 |
|
133 |
gwsdClient = nil; |
134 |
|
135 |
if (remote) { |
136 |
/* */ |
137 |
/* We get here when a new GWSd istance is created for a new thread */ |
138 |
/* */ |
139 |
|
140 |
watchers = [NSMutableArray new]; |
141 |
watchTimers = [NSMutableArray new]; |
142 |
|
143 |
operations = [NSMutableArray new]; |
144 |
oprefnum = 0; |
145 |
|
146 |
shellTasks = [NSMutableArray new]; |
147 |
|
148 |
nc = [NSNotificationCenter defaultCenter]; |
149 |
dnc = [NSDistributedNotificationCenter defaultCenter]; |
150 |
fm = [NSFileManager defaultManager]; |
151 |
ws = [NSWorkspace sharedWorkspace]; |
152 |
|
153 |
sharedIstance = [GWSd sharedGWSd]; |
154 |
|
155 |
[dnc addObserver: self |
156 |
selector: @selector(fileSystemDidChange:) |
157 |
name: GWFileSystemDidChangeNotification |
158 |
object: nil]; |
159 |
|
160 |
conn = RETAIN (aConnection); |
161 |
[conn setIndependentConversationQueueing: YES]; |
162 |
[conn setRootObject: self]; |
163 |
[conn setDelegate: self]; |
164 |
|
165 |
[nc addObserver: self |
166 |
selector: @selector(connectionDidDie:) |
167 |
name: NSConnectionDidDieNotification |
168 |
object: conn]; |
169 |
|
170 |
[remote setProtocolForProxy: @protocol(GWSdClientProtocol)]; |
171 |
gwsdClient = (id <GWSdClientProtocol>)remote; |
172 |
|
173 |
clientLock = [NSRecursiveLock new]; |
174 |
} else { |
175 |
/* */ |
176 |
/* Here, only in the main thread, we create the connection that */ |
177 |
/* will be used by our clients to connect. */ |
178 |
/* This connection will be substitudted with a not named connection, */ |
179 |
/* that is, a new connection for each thread/client, */ |
180 |
/* in +newThreadWithRemote: */ |
181 |
/* */ |
182 |
|
183 |
NSProcessInfo *proc = [NSProcessInfo processInfo]; |
184 |
NSArray *args = [proc arguments]; |
185 |
NSString *passwd; |
186 |
|
187 |
if ([args count] < 2) { |
188 |
NSLog(@"Missing password\nUsage: gwsd PASSWORD"); |
189 |
exit(0); |
190 |
} |
191 |
|
192 |
passwd = [args objectAtIndex: 1]; |
193 |
|
194 |
if ([passwd cStringLength] < 8) { |
195 |
NSLog(@"Invalid password\nThe password must have altmost 8 characters"); |
196 |
exit(0); |
197 |
} |
198 |
|
199 |
ASSIGN (userPassword, passwd); |
200 |
ASSIGN (userName, NSUserName()); |
201 |
|
202 |
firstConn = [[NSConnection alloc] initWithReceivePort: [NSSocketPort port] |
203 |
sendPort: nil]; |
204 |
[firstConn enableMultipleThreads]; |
205 |
[firstConn setRootObject: self]; |
206 |
[firstConn registerName: @"gwsd"]; |
207 |
[firstConn setRequestTimeout: LONG_DELAY]; |
208 |
[firstConn setReplyTimeout: LONG_DELAY]; |
209 |
[firstConn setDelegate: self]; |
210 |
|
211 |
[[NSNotificationCenter defaultCenter] addObserver: self |
212 |
selector: @selector(connectionDidDie:) |
213 |
name: NSConnectionDidDieNotification |
214 |
object: firstConn]; |
215 |
} |
216 |
} |
217 |
|
218 |
return self; |
219 |
} |
220 |
|
221 |
+ (void)newThreadWithRemote:(id<GWSdClientProtocol>)remote |
222 |
{ |
223 |
/* */ |
224 |
/* Here, for each new client, we create a new connection and */ |
225 |
/* a new GWSd istance. */ |
226 |
/* After calling -setServerConnection: on the client, we can release */ |
227 |
/* both the GWSd object and the NSConnection, because they ere retained */ |
228 |
/* by the client. */ |
229 |
/* This means that, when the client exits, these objects are released */ |
230 |
/* and the thread exits (I hope...). */ |
231 |
/* */ |
232 |
|
233 |
NSAutoreleasePool *pool; |
234 |
NSConnection *connection; |
235 |
GWSd *gwsd; |
236 |
|
237 |
pool = [[NSAutoreleasePool alloc] init]; |
238 |
|
239 |
connection = [[NSConnection alloc] initWithReceivePort: [NSSocketPort port] |
240 |
sendPort: [NSSocketPort port]]; |
241 |
|
242 |
gwsd = [[GWSd alloc] initWithRemote: remote connection: connection]; |
243 |
|
244 |
[remote setServerConnection: connection]; |
245 |
|
246 |
RELEASE (gwsd); |
247 |
RELEASE (connection); |
248 |
|
249 |
[[NSRunLoop currentRunLoop] run]; |
250 |
[pool release]; |
251 |
} |
252 |
|
253 |
- (void)_registerRemoteClient:(id<GWSdClientProtocol>)remote |
254 |
{ |
255 |
/* */ |
256 |
/* This method is called, in the main thread, by each client that */ |
257 |
/* wants to connect. */ |
258 |
/* A new thread is detached for each client. */ |
259 |
/* */ |
260 |
|
261 |
if (([userName isEqual: [remote userName]] == NO) |
262 |
|| ([userPassword isEqual: [remote userPassword]] == NO)) { |
263 |
[remote connectionRefused]; |
264 |
return; |
265 |
} |
266 |
|
267 |
NS_DURING |
268 |
{ |
269 |
[NSThread detachNewThreadSelector: @selector(newThreadWithRemote:) |
270 |
toTarget: [self class] |
271 |
withObject: remote]; |
272 |
} |
273 |
NS_HANDLER |
274 |
{ |
275 |
NSLog(@"Error! A fatal error occured while detaching the thread.\nRecompile your runtime with threads support."); |
276 |
} |
277 |
NS_ENDHANDLER |
278 |
} |
279 |
|
280 |
- (id<GWSdClientProtocol>)gwsdClient |
281 |
{ |
282 |
return gwsdClient; |
283 |
} |
284 |
|
285 |
- (NSRecursiveLock *)clientLock |
286 |
{ |
287 |
return clientLock; |
288 |
} |
289 |
|
290 |
- (void)connectionDidDie:(NSNotification *)notification |
291 |
{ |
292 |
id diedconn = [notification object]; |
293 |
|
294 |
[[NSNotificationCenter defaultCenter] removeObserver: self |
295 |
name: NSConnectionDidDieNotification object: diedconn]; |
296 |
|
297 |
if (diedconn == firstConn) { |
298 |
NSLog(@"argh - gwsd server root connection has been destroyed."); |
299 |
exit(1); |
300 |
} else if (diedconn == conn) { |
301 |
|
302 |
|
303 |
} |
304 |
} |
305 |
|
306 |
|
307 |
// |
308 |
// Private methods |
309 |
// |
310 |
- (NSMutableDictionary *)cachedRepresentationForPath:(NSString *)path |
311 |
{ |
312 |
NSMutableDictionary *contents = [cachedContents objectForKey: path]; |
313 |
|
314 |
if (contents) { |
315 |
NSDate *modDate = [contents objectForKey: @"moddate"]; |
316 |
NSDate *date = [self modificationDateForPath: path]; |
317 |
|
318 |
if ([modDate isEqualToDate: date]) { |
319 |
return contents; |
320 |
} else { |
321 |
[cachedContents removeObjectForKey: path]; |
322 |
} |
323 |
} |
324 |
|
325 |
return nil; |
326 |
} |
327 |
|
328 |
- (void)removeCachedRepresentation |
329 |
{ |
330 |
NSArray *keys = [cachedContents allKeys]; |
331 |
|
332 |
if ([keys count]) { |
333 |
[cachedContents removeObjectForKey: [keys objectAtIndex: 0]]; |
334 |
} |
335 |
} |
336 |
|
337 |
- (BOOL)hideSysFiles |
338 |
{ |
339 |
return hideSysFiles; |
340 |
} |
341 |
|
342 |
- (NSArray *)checkHiddenFiles:(NSArray *)files atPath:(NSString *)path |
343 |
{ |
344 |
NSArray *checkedFiles; |
345 |
NSArray *hiddenFiles; |
346 |
NSString *h; |
347 |
|
348 |
h = [path stringByAppendingPathComponent: @".hidden"]; |
349 |
if ([fm fileExistsAtPath: h]) { |
350 |
h = [NSString stringWithContentsOfFile: h]; |
351 |
hiddenFiles = [h componentsSeparatedByString: @"\n"]; |
352 |
} else { |
353 |
hiddenFiles = nil; |
354 |
} |
355 |
|
356 |
if (hiddenFiles != nil || hideSysFiles) { |
357 |
NSMutableArray *mutableFiles = AUTORELEASE ([files mutableCopy]); |
358 |
|
359 |
if (hiddenFiles != nil) { |
360 |
[mutableFiles removeObjectsInArray: hiddenFiles]; |
361 |
} |
362 |
|
363 |
if (hideSysFiles) { |
364 |
int j = [mutableFiles count] - 1; |
365 |
|
366 |
while (j >= 0) { |
367 |
NSString *file = (NSString *)[mutableFiles objectAtIndex: j]; |
368 |
|
369 |
if ([file hasPrefix: @"."]) { |
370 |
[mutableFiles removeObjectAtIndex: j]; |
371 |
} |
372 |
j--; |
373 |
} |
374 |
} |
375 |
|
376 |
checkedFiles = mutableFiles; |
377 |
|
378 |
} else { |
379 |
checkedFiles = files; |
380 |
} |
381 |
|
382 |
return checkedFiles; |
383 |
} |
384 |
|
385 |
- (BOOL)verifyFileAtPath:(NSString *)path |
386 |
{ |
387 |
if ([fm fileExistsAtPath: path] == NO) { |
388 |
NSString *fileName = [path lastPathComponent]; |
389 |
NSString *message = [NSString stringWithFormat: @"%@: no such file or directory!", fileName]; |
390 |
|
391 |
[gwsdClient showErrorAlertWithMessage: message]; |
392 |
|
393 |
return NO; |
394 |
} |
395 |
|
396 |
return YES; |
397 |
} |
398 |
|
399 |
- (void)dealloc |
400 |
{ |
401 |
[nc removeObserver: self]; |
402 |
[dnc removeObserver: self]; |
403 |
|
404 |
TEST_RELEASE (userName); |
405 |
TEST_RELEASE (userPassword); |
406 |
|
407 |
TEST_RELEASE (watchers); |
408 |
TEST_RELEASE (watchTimers); |
409 |
|
410 |
TEST_RELEASE (operations); |
411 |
|
412 |
TEST_RELEASE (shellCommand); |
413 |
TEST_RELEASE (shellTasks); |
414 |
|
415 |
TEST_RELEASE (clientLock); |
416 |
|
417 |
DESTROY (firstConn); |
418 |
DESTROY (conn); |
419 |
|
420 |
[super dealloc]; |
421 |
} |
422 |
|
423 |
|
424 |
// |
425 |
// GWSDProtocol |
426 |
// |
427 |
- (void)registerRemoteClient:(id<GWSdClientProtocol>)remote |
428 |
{ |
429 |
return [self _registerRemoteClient: remote]; |
430 |
} |
431 |
|
432 |
- (NSString *)homeDirectory |
433 |
{ |
434 |
return NSHomeDirectory(); |
435 |
} |
436 |
|
437 |
- (BOOL)existsFileAtPath:(NSString *)path |
438 |
{ |
439 |
return [fm fileExistsAtPath: path]; |
440 |
} |
441 |
|
442 |
- (BOOL)existsAndIsDirectoryFileAtPath:(NSString *)path |
443 |
{ |
444 |
BOOL isDir; |
445 |
return ([fm fileExistsAtPath: path isDirectory: &isDir] && isDir); |
446 |
} |
447 |
|
448 |
- (NSString *)typeOfFileAt:(NSString *)path |
449 |
{ |
450 |
NSString *defApp, *type; |
451 |
|
452 |
[ws getInfoForFile: path application: &defApp type: &type]; |
453 |
|
454 |
return type; |
455 |
} |
456 |
|
457 |
- (BOOL)isPakageAtPath:(NSString *)path |
458 |
{ |
459 |
NSString *defApp, *type; |
460 |
|
461 |
[ws getInfoForFile: path application: &defApp type: &type]; |
462 |
|
463 |
if (type == NSApplicationFileType) { |
464 |
return YES; |
465 |
} else if (type == NSPlainFileType) { |
466 |
return [self existsAndIsDirectoryFileAtPath: path]; |
467 |
} |
468 |
|
469 |
return NO; |
470 |
} |
471 |
|
472 |
- (NSDictionary *)fileSystemAttributesAtPath:(NSString *)path |
473 |
{ |
474 |
return [fm fileSystemAttributesAtPath: path]; |
475 |
} |
476 |
|
477 |
- (BOOL)isWritableFileAtPath:(NSString *)path |
478 |
{ |
479 |
return [fm isWritableFileAtPath: path]; |
480 |
} |
481 |
|
482 |
- (NSDate *)modificationDateForPath:(NSString *)path |
483 |
{ |
484 |
NSDictionary *attributes = [fm fileAttributesAtPath: path traverseLink: YES]; |
485 |
|
486 |
return [attributes fileModificationDate]; |
487 |
} |
488 |
|
489 |
- (int)sortTypeForDirectoryAtPath:(NSString *)aPath |
490 |
{ |
491 |
if ([fm isWritableFileAtPath: aPath]) { |
492 |
NSString *dictPath = [aPath stringByAppendingPathComponent: @".gwsort"]; |
493 |
|
494 |
if ([fm fileExistsAtPath: dictPath]) { |
495 |
NSDictionary *sortDict = [NSDictionary dictionaryWithContentsOfFile: dictPath]; |
496 |
|
497 |
if (sortDict) { |
498 |
return [[sortDict objectForKey: @"sort"] intValue]; |
499 |
} |
500 |
} |
501 |
} |
502 |
|
503 |
return defSortType; |
504 |
} |
505 |
|
506 |
- (void)setSortType:(int)type forDirectoryAtPath:(NSString *)aPath |
507 |
{ |
508 |
if ([fm isWritableFileAtPath: aPath]) { |
509 |
NSString *sortstr = [NSString stringWithFormat: @"%i", type]; |
510 |
NSDictionary *dict = [NSDictionary dictionaryWithObject: sortstr |
511 |
forKey: @"sort"]; |
512 |
[dict writeToFile: [aPath stringByAppendingPathComponent: @".gwsort"] |
513 |
atomically: YES]; |
514 |
} |
515 |
|
516 |
// [[NSNotificationCenter defaultCenter] |
517 |
// postNotificationName: GWSortTypeDidChangeNotification |
518 |
// object: (id)aPath]; |
519 |
} |
520 |
|
521 |
- (NSDictionary *)directoryContentsAtPath:(NSString *)path |
522 |
{ |
523 |
NSMutableDictionary *contentsDict; |
524 |
NSArray *files; |
525 |
NSMutableArray *sortfiles; |
526 |
NSMutableArray *paths; |
527 |
NSString *s; |
528 |
int i, count; |
529 |
int stype; |
530 |
|
531 |
if ([self existsAndIsDirectoryFileAtPath: path] == NO) { |
532 |
return nil; |
533 |
} |
534 |
|
535 |
[gwsdLock lock]; |
536 |
contentsDict = [self cachedRepresentationForPath: path]; |
537 |
[gwsdLock unlock]; |
538 |
if (contentsDict) { |
539 |
return contentsDict; |
540 |
} |
541 |
|
542 |
files = [self checkHiddenFiles: [fm directoryContentsAtPath: path] |
543 |
atPath: path]; |
544 |
|
545 |
count = [files count]; |
546 |
if (count == 0) { |
547 |
return nil; |
548 |
} |
549 |
|
550 |
paths = [NSMutableArray arrayWithCapacity: count]; |
551 |
|
552 |
for (i = 0; i < count; ++i) { |
553 |
s = [path stringByAppendingPathComponent: [files objectAtIndex: i]]; |
554 |
[paths addObject: s]; |
555 |
} |
556 |
|
557 |
stype = [self sortTypeForDirectoryAtPath: path]; |
558 |
|
559 |
paths = AUTORELEASE ([[paths sortedArrayUsingFunction: (int (*)(id, id, void*))comparePaths |
560 |
context: (void *)stype] mutableCopy]); |
561 |
|
562 |
sortfiles = [NSMutableArray arrayWithCapacity: 1]; |
563 |
for (i = 0; i < count; ++i) { |
564 |
[sortfiles addObject: [[paths objectAtIndex: i] lastPathComponent]]; |
565 |
} |
566 |
|
567 |
contentsDict = [NSMutableDictionary dictionary]; |
568 |
[contentsDict setObject: [NSDate date] forKey: @"datestamp"]; |
569 |
[contentsDict setObject: [self modificationDateForPath: path] forKey: @"moddate"]; |
570 |
[contentsDict setObject: sortfiles forKey: @"files"]; |
571 |
|
572 |
[gwsdLock lock]; |
573 |
if ([cachedContents count] <= cachedMax) { |
574 |
[self removeCachedRepresentation]; |
575 |
} |
576 |
|
577 |
[cachedContents setObject: contentsDict forKey: path]; |
578 |
[gwsdLock unlock]; |
579 |
|
580 |
return contentsDict; |
581 |
} |
582 |
|
583 |
- (NSString *)contentsOfFileAt:(NSString *)path |
584 |
{ |
585 |
NSDictionary *attributes = [fm fileAttributesAtPath: path traverseLink: YES]; |
586 |
long fsize = [[attributes objectForKey: @"NSFileSize"] longValue]; |
587 |
|
588 |
if (fsize >= MAX_FILE_SIZE) { |
589 |
return nil; |
590 |
} else { |
591 |
return [NSString stringWithContentsOfFile: path]; |
592 |
} |
593 |
} |
594 |
|
595 |
- (BOOL)saveString:(NSString *)str atPath:(NSString *)path |
596 |
{ |
597 |
NSDictionary *attributes = [fm fileAttributesAtPath: path traverseLink: YES]; |
598 |
|
599 |
if ([str writeToFile: path atomically: YES]) { |
600 |
if (attributes) { |
601 |
[fm changeFileAttributes: attributes atPath: path]; |
602 |
} |
603 |
return YES; |
604 |
} |
605 |
|
606 |
return NO; |
607 |
} |
608 |
|
609 |
- (void)addWatcherForPath:(NSString *)path |
610 |
{ |
611 |
[self _addWatcherForPath: path]; |
612 |
} |
613 |
|
614 |
- (void)removeWatcherForPath:(NSString *)path |
615 |
{ |
616 |
[self _removeWatcherForPath: path]; |
617 |
} |
618 |
|
619 |
- (oneway void)performLocalFileOperationWithDictionary:(id)opdict |
620 |
{ |
621 |
LocalFileOp *op = [[LocalFileOp alloc] initWithOperationDescription: opdict |
622 |
forGWSd: self withClient: gwsdClient]; |
623 |
[operations addObject: op]; |
624 |
RELEASE (op); |
625 |
} |
626 |
|
627 |
- (BOOL)pauseFileOpeRationWithRef:(int)ref |
628 |
{ |
629 |
LocalFileOp *op = [self fileOpWithRef: ref]; |
630 |
return (op && [op pauseOperation]); |
631 |
} |
632 |
|
633 |
- (BOOL)continueFileOpeRationWithRef:(int)ref |
634 |
{ |
635 |
LocalFileOp *op = [self fileOpWithRef: ref]; |
636 |
return (op && [op continueOperation]); |
637 |
} |
638 |
|
639 |
- (BOOL)stopFileOpeRationWithRef:(int)ref |
640 |
{ |
641 |
LocalFileOp *op = [self fileOpWithRef: ref]; |
642 |
return (op && [op stopOperation]); |
643 |
} |
644 |
|
645 |
- (oneway void)renamePath:(NSString *)oldname toNewName:(NSString *)newname |
646 |
{ |
647 |
NSString *basepath = [oldname stringByDeletingLastPathComponent]; |
648 |
NSMutableDictionary *dict = [NSMutableDictionary dictionary]; |
649 |
NSString *message; |
650 |
|
651 |
if ([fm isWritableFileAtPath: oldname] == NO) { |
652 |
message = [NSString stringWithFormat: |
653 |
@"You have not write permission\nfor \"%@\"!", |
654 |
[oldname lastPathComponent]]; |
655 |
[gwsdClient showErrorAlertWithMessage: message]; |
656 |
return; |
657 |
|
658 |
} else if ([fm isWritableFileAtPath: basepath] == NO) { |
659 |
message = [NSString stringWithFormat: |
660 |
@"You have not write permission\nfor \"%@\"!", |
661 |
[basepath lastPathComponent]]; |
662 |
[gwsdClient showErrorAlertWithMessage: message]; |
663 |
return; |
664 |
|
665 |
} else { |
666 |
NSCharacterSet *notAllowSet = [NSCharacterSet characterSetWithCharactersInString: @"/\\*$|~\'\"`^!?"]; |
667 |
NSRange range = [[newname lastPathComponent] rangeOfCharacterFromSet: notAllowSet]; |
668 |
NSArray *dirContents = [fm directoryContentsAtPath: basepath]; |
669 |
|
670 |
if (range.length > 0) { |
671 |
[gwsdClient showErrorAlertWithMessage: @"Invalid char in name"]; |
672 |
return; |
673 |
} |
674 |
|
675 |
if ([dirContents containsObject: newname]) { |
676 |
if ([newname isEqualToString: oldname]) { |
677 |
return; |
678 |
} else { |
679 |
message = [NSString stringWithFormat: |
680 |
@"The name %@ is already in use!", [newname lastPathComponent]]; |
681 |
[gwsdClient showErrorAlertWithMessage: message]; |
682 |
return; |
683 |
} |
684 |
} |
685 |
|
686 |
[self suspendWatchingForPath: basepath]; |
687 |
|
688 |
[fm movePath: oldname toPath: newname handler: self]; |
689 |
|
690 |
[dict setObject: basepath forKey: @"path"]; |
691 |
[dict setObject: GWFileDeletedInWatchedDirectory forKey: @"event"]; |
692 |
[dict setObject: [NSArray arrayWithObject: [oldname lastPathComponent]] |
693 |
forKey: @"files"]; |
694 |
|
695 |
[gwsdClient server: self fileSystemDidChange: dict]; |
696 |
|
697 |
[[NSRunLoop currentRunLoop] |
698 |
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]]; |
699 |
|
700 |
[dict setObject: basepath forKey: @"path"]; |
701 |
[dict setObject: GWFileCreatedInWatchedDirectory forKey: @"event"]; |
702 |
[dict setObject: [NSArray arrayWithObject: [newname lastPathComponent]] |
703 |
forKey: @"files"]; |
704 |
|
705 |
[self restartWatchingForPath: basepath]; |
706 |
} |
707 |
} |
708 |
|
709 |
- (oneway void)newObjectAtPath:(NSString *)basePath isDirectory:(BOOL)directory |
710 |
{ |
711 |
NSString *fullPath; |
712 |
NSString *fileName; |
713 |
NSMutableDictionary *dict; |
714 |
int suff; |
715 |
|
716 |
if ([self verifyFileAtPath: basePath] == NO) { |
717 |
return; |
718 |
} |
719 |
|
720 |
if ([fm isWritableFileAtPath: basePath] == NO) { |
721 |
NSString *message = [NSString stringWithFormat: |
722 |
@"You have not write permission for %@!", basePath]; |
723 |
|
724 |
[gwsdClient showErrorAlertWithMessage: message]; |
725 |
return; |
726 |
} |
727 |
|
728 |
if (directory == YES) { |
729 |
fileName = @"NewFolder"; |
730 |
} else { |
731 |
fileName = @"NewFile"; |
732 |
} |
733 |
|
734 |
fullPath = [basePath stringByAppendingPathComponent: fileName]; |
735 |
|
736 |
if ([fm fileExistsAtPath: fullPath] == YES) { |
737 |
suff = 1; |
738 |
while (1) { |
739 |
NSString *s = [fileName stringByAppendingFormat: @"%i", suff]; |
740 |
fullPath = [basePath stringByAppendingPathComponent: s]; |
741 |
if ([fm fileExistsAtPath: fullPath] == NO) { |
742 |
fileName = [NSString stringWithString: s]; |
743 |
break; |
744 |
} |
745 |
suff++; |
746 |
} |
747 |
} |
748 |
|
749 |
[self suspendWatchingForPath: basePath]; |
750 |
|
751 |
if (directory == YES) { |
752 |
[fm createDirectoryAtPath: fullPath attributes: nil]; |
753 |
} else { |
754 |
[fm createFileAtPath: fullPath contents: nil attributes: nil]; |
755 |
} |
756 |
|
757 |
dict = [NSMutableDictionary dictionary]; |
758 |
[dict setObject: basePath forKey: @"path"]; |
759 |
[dict setObject: GWFileCreatedInWatchedDirectory forKey: @"event"]; |
760 |
[dict setObject: [NSArray arrayWithObject: [fullPath lastPathComponent]] |
761 |
forKey: @"files"]; |
762 |
|
763 |
[gwsdClient server: self fileSystemDidChange: dict]; |
764 |
|
765 |
[self restartWatchingForPath: basePath]; |
766 |
} |
767 |
|
768 |
- (oneway void)duplicateFiles:(NSArray *)files inDirectory:(NSString *)basePath |
769 |
{ |
770 |
NSMutableDictionary *opDict = [NSMutableDictionary dictionary]; |
771 |
|
772 |
if ([fm isWritableFileAtPath: basePath] == NO) { |
773 |
NSString *message = [NSString stringWithFormat: |
774 |
@"You have not write permission for %@!", basePath]; |
775 |
|
776 |
[gwsdClient showErrorAlertWithMessage: message]; |
777 |
return; |
778 |
} |
779 |
|
780 |
[opDict setObject: NSWorkspaceDuplicateOperation forKey: @"operation"]; |
781 |
[opDict setObject: basePath forKey: @"source"]; |
782 |
[opDict setObject: basePath forKey: @"destination"]; |
783 |
[opDict setObject: files forKey: @"files"]; |
784 |
|
785 |
[self performLocalFileOperationWithDictionary: opDict]; |
786 |
} |
787 |
|
788 |
- (oneway void)deleteFiles:(NSArray *)files inDirectory:(NSString *)basePath |
789 |
{ |
790 |
NSMutableDictionary *opDict = [NSMutableDictionary dictionary]; |
791 |
|
792 |
if ([fm isWritableFileAtPath: basePath] == NO) { |
793 |
NSString *message = [NSString stringWithFormat: |
794 |
@"You have not write permission for %@!", basePath]; |
795 |
|
796 |
[gwsdClient showErrorAlertWithMessage: message]; |
797 |
return; |
798 |
} |
799 |
|
800 |
[opDict setObject: NSWorkspaceDestroyOperation forKey: @"operation"]; |
801 |
[opDict setObject: basePath forKey: @"source"]; |
802 |
[opDict setObject: basePath forKey: @"destination"]; |
803 |
[opDict setObject: files forKey: @"files"]; |
804 |
|
805 |
[self performLocalFileOperationWithDictionary: opDict]; |
806 |
} |
807 |
|
808 |
- (BOOL)fileManager:(NSFileManager *)manager |
809 |
shouldProceedAfterError:(NSDictionary *)errorDict |
810 |
{ |
811 |
NSString *path = [errorDict objectForKey: @"Path"]; |
812 |
NSString *msg = [NSString stringWithFormat: |
813 |
@"File operation error: %@\nwith file: %@\n", |
814 |
[errorDict objectForKey: @"Error"], path]; |
815 |
|
816 |
return [gwsdClient requestUserConfirmationWithMessage: msg |
817 |
title: @"error"]; |
818 |
} |
819 |
|
820 |
- (void)fileManager:(NSFileManager *)manager willProcessPath:(NSString *)path |
821 |
{ |
822 |
} |
823 |
|
824 |
- (oneway void)openShellOnPath:(NSString *)path refNumber:(NSNumber *)ref |
825 |
{ |
826 |
[self _openShellOnPath: path refNumber: ref]; |
827 |
} |
828 |
|
829 |
- (oneway void)remoteShellWithRef:(NSNumber *)ref |
830 |
newCommandLine:(NSString *)line |
831 |
{ |
832 |
[self _remoteShellWithRef: ref newCommandLine: line]; |
833 |
} |
834 |
|
835 |
- (oneway void)closedRemoteTerminalWithRefNumber:(NSNumber *)ref |
836 |
{ |
837 |
[self _closedRemoteTerminalWithRefNumber: ref]; |
838 |
} |
839 |
|
840 |
@end |
841 |
|
842 |
|
843 |
int main(int argc, char** argv) |
844 |
{ |
845 |
GWSd *gwsd; |
846 |
|
847 |
switch (fork()) { |
848 |
case -1: |
849 |
fprintf(stderr, "gwsd - fork failed - bye.\n"); |
850 |
exit(1); |
851 |
|
852 |
case 0: |
853 |
setsid(); |
854 |
break; |
855 |
|
856 |
default: |
857 |
exit(0); |
858 |
} |
859 |
|
860 |
CREATE_AUTORELEASE_POOL (pool); |
861 |
gwsd = [[GWSd alloc] initWithRemote: nil connection: nil]; |
862 |
|
863 |
RELEASE (pool); |
864 |
|
865 |
if (gwsd != nil) { |
866 |
CREATE_AUTORELEASE_POOL (pool); |
867 |
[[NSRunLoop currentRunLoop] run]; |
868 |
RELEASE (pool); |
869 |
} |
870 |
|
871 |
exit(0); |
872 |
} |
873 |
|