source: trunk/SapphireFrappliance/FRAppliance/SapphireImportHelper.m @ 1049

Revision 1049, 10.7 KB checked in by gbooker, 5 years ago (diff)

If a superclass implements a protocol, the subclass does not need to as well to be classified as implementing it. This eliminates all the super calls to make the compiler shut up. Additionally, created a common initializer for the all importer instead of creating it in three different places.

Line 
1/*
2 * SapphireImportHelper.m
3 * Sapphire
4 *
5 * Created by Graham Booker on Dec. 8, 2007.
6 * Copyright 2007 Sapphire Development Team and/or www.nanopi.net
7 * All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
10 * General Public License as published by the Free Software Foundation; either version 3 of the License,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
14 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
15 * Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with this program; if not,
18 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21#import <Security/Security.h>
22#include <sys/stat.h>
23#include <sys/mount.h>
24
25#import "SapphireImportHelper.h"
26#import "SapphireMetaData.h"
27#import "SapphireAllImporter.h"
28#import "SapphireFileMetaData.h"
29#import "SapphireMetaDataSupport.h"
30#import "SapphireApplianceController.h"
31
32#define CONNECTION_NAME @"Sapphire Server"
33
34@interface SapphireImportFile : NSObject <SapphireImportFileProtocol>{
35        NSString                                                                *path;
36        id <SapphireImporterBackgroundProtocol> informer;
37        ImportType                                                              type;
38}
39- (id)initWithPath:(NSString *)aPath informer:(id <SapphireImporterBackgroundProtocol>)aInformer type:(ImportType)aType;
40@end
41
42@interface SapphireImportHelperServer ()
43- (void)startClient;
44@end
45
46@implementation SapphireImportHelper
47
48static SapphireImportHelper *shared = nil;
49
50+ (SapphireImportHelper *)sharedHelperForContext:(NSManagedObjectContext *)moc
51{
52        if(shared == nil && moc != nil)
53                shared = [[SapphireImportHelperServer alloc] initWithContext:moc];
54
55        return shared;
56}
57
58+ (void)relinquishHelper
59{
60        if(shared != nil)
61                [shared relinquishHelper];
62}
63
64- (void)relinquishHelper
65{
66}
67
68- (BOOL)importFileData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
69{
70        return YES;
71}
72
73- (void)importAllData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
74{
75}
76
77- (void)removeObjectsWithInform:(id <SapphireImporterBackgroundProtocol>)inform
78{
79}
80
81@end
82
83@implementation SapphireImportHelperClient
84
85- (id)initWithContext:(NSManagedObjectContext *)context
86{
87        self = [super init];
88        if(!self)
89                return nil;
90       
91        moc = [context retain];
92        allImporter = [[SapphireAllImporter alloc] init];
93        keepRunning = YES;
94       
95        return self;
96}
97- (void) dealloc
98{
99        [server release];
100        [allImporter release];
101        [moc release];
102        [super dealloc];
103}
104
105- (BOOL)importFileData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
106{
107        updateMetaData(file);
108        return YES;
109}
110
111- (void)startChild
112{
113        /*Child here*/
114        @try {
115                NSConnection *connection = [NSConnection connectionWithRegisteredName:CONNECTION_NAME host:nil];
116                id serverobj = [[connection rootProxy] retain];
117                [serverobj setProtocolForProxy:@protocol(SapphireImportServer)];
118                shared = self;
119                [serverobj setClient:(SapphireImportHelperClient *)shared];
120                server = serverobj;     
121                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionDidDie:) name:NSConnectionDidDieNotification object:nil];           
122        }
123        @catch (NSException * e) {
124                keepRunning = NO;
125        }
126}
127
128- (BOOL)keepRunning
129{
130        return keepRunning;
131}
132
133- (void)connectionDidDie:(NSNotification *)note
134{
135        [self exitChild];
136}
137
138- (oneway void)exitChild
139{
140        keepRunning = NO;
141}
142
143- (void)realStartQueue
144{
145        @try {
146                id <SapphireImportFileProtocol> file;
147                while((file = [server nextFile]) != nil)
148                {
149                        [moc reset];
150                        NSAutoreleasePool *singleImportPool = [[NSAutoreleasePool alloc] init];
151                        ImportType type = [file importType];
152                        BOOL ret;
153                        NSString *path = [file path];
154                        SapphireFileMetaData *file = [SapphireFileMetaData fileWithPath:path inContext:moc];
155                        [moc refreshObject:file mergeChanges:YES];
156                        if(type == IMPORT_TYPE_FILE_DATA)
157                                ret = updateMetaData(file);
158                        else
159                                ret = ([allImporter importMetaData:file path:[file path]] == ImportStateUpdated);
160                        NSDictionary *changes = [SapphireMetaDataSupport changesDictionaryForContext:moc];
161                        [server importCompleteWithChanges:changes updated:ret];
162                        [singleImportPool release];
163                }
164        }
165        @catch (NSException * e) {
166                [SapphireApplianceController logException:e];
167                keepRunning = NO;
168        }
169}
170
171- (oneway void)startQueue
172{
173        [self performSelectorOnMainThread:@selector(realStartQueue) withObject:nil waitUntilDone:NO];
174}
175@end
176
177@implementation SapphireImportHelperServer
178
179- (id)initWithContext:(NSManagedObjectContext *)context
180{
181        self = [super init];
182        if (self == nil)
183                return nil;
184       
185        queue = [[NSMutableArray alloc] init];
186        queueSuspended = NO;
187       
188        serverConnection = [NSConnection defaultConnection];
189        [serverConnection setRootObject:self];
190        if([serverConnection registerName:CONNECTION_NAME] == NO)
191                SapphireLog(SAPPHIRE_LOG_GENERAL, SAPPHIRE_LOG_LEVEL_ERROR, @"Register failed");
192       
193        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionDidDie:) name:NSConnectionDidDieNotification object:nil];
194        moc = [context retain];
195       
196        [self startClient];
197       
198        return self;
199}
200
201- (void) dealloc
202{
203        [[NSNotificationCenter defaultCenter] removeObserver:self];
204        [client release];
205        [moc release];
206        [queue release];
207        [currentImporting release];
208        [super dealloc];
209}
210
211- (void)relinquishHelper
212{
213        [client exitChild];
214        [serverConnection registerName:nil];
215        [serverConnection setRootObject:nil];
216        [shared autorelease];
217        shared = nil;
218}
219
220- (BOOL)isSlashReadOnly
221{
222        struct statfs *mntbufp;
223       
224    int i, mountCount = getmntinfo(&mntbufp, MNT_NOWAIT);
225        for(i=0; i<mountCount; i++)
226        {
227                if(!strcmp(mntbufp[i].f_mntonname, "/"))
228                        return (mntbufp[i].f_flags & MNT_RDONLY) ? YES : NO;
229        }
230       
231        return NO;
232}
233
234- (BOOL)fixClientPermissions:(NSString *)path
235{
236        /* Permissions are incorrect */
237        AuthorizationItem authItems[2] = {
238                {kAuthorizationEnvironmentUsername, strlen("frontrow"), "frontrow", 0},
239                {kAuthorizationEnvironmentPassword, strlen("frontrow"), "frontrow", 0},
240        };
241        AuthorizationEnvironment environ = {2, authItems};
242        AuthorizationItem rightSet[] = {{kAuthorizationRightExecute, 0, NULL, 0}};
243        AuthorizationRights rights = {1, rightSet};
244        AuthorizationRef auth;
245        OSStatus result = AuthorizationCreate(&rights, &environ, kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights, &auth);
246        if(result == errAuthorizationSuccess)
247        {
248                BOOL roslash = [self isSlashReadOnly];
249                if(roslash)
250                {
251                        char *command = "mount -uw /";
252                        char *arguments[] = {"-c", command, NULL};
253                        AuthorizationExecuteWithPrivileges(auth, "/bin/sh", kAuthorizationFlagDefaults, arguments, NULL);
254                }
255                char *command = "chmod +rx \"$HELP\"";
256                setenv("HELP", [path fileSystemRepresentation], 1);
257                char *arguments[] = {"-c", command, NULL};
258                result = AuthorizationExecuteWithPrivileges(auth, "/bin/sh", kAuthorizationFlagDefaults, arguments, NULL);
259                unsetenv("HELP");
260                if(roslash)
261                {
262                        char *command = "mount -ur /";
263                        char *arguments[] = {"-c", command, NULL};
264                        AuthorizationExecuteWithPrivileges(auth, "/bin/sh", kAuthorizationFlagDefaults, arguments, NULL);
265                }
266        }
267        if(result != errAuthorizationSuccess)
268                return NO;
269       
270        return YES;
271}
272
273- (void)startClient
274{
275        NSString *path = [[NSBundle bundleForClass:[SapphireImportHelper class]] pathForResource:@"ImportHelper" ofType:@""];
276        NSDictionary *attrs = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES];
277        if(([[attrs objectForKey:NSFilePosixPermissions] intValue] | S_IXOTH) || [self fixClientPermissions:path])
278        {
279                @try {
280                        [NSTask launchedTaskWithLaunchPath:path arguments:[NSArray array]];
281                }
282                @catch (NSException * e) {
283                        SapphireLog(SAPPHIRE_LOG_GENERAL, SAPPHIRE_LOG_LEVEL_ERROR, @"Could not launch helper because of exception %@ launching %@.  Make this file executable", e, path);
284                }               
285        }
286        else
287                SapphireLog(SAPPHIRE_LOG_GENERAL, SAPPHIRE_LOG_LEVEL_ERROR, @"Could not correct helper permissions on %@.  Make this file executable!", path);
288}
289
290- (void)connectionDidDie:(NSNotification *)note
291{
292        [client release];
293        client = nil;
294        /*Inform that import completed (since it crashed, no update done)*/
295        [self importCompleteWithChanges:nil updated:NO];
296        if(shared != nil)
297                /* Don't start it again if we are shutting down*/
298                [self startClient];
299}
300
301- (void)itemAdded
302{
303        if(!queueSuspended)
304                return;
305        queueSuspended = NO;
306        [SapphireMetaDataSupport save:moc];
307        [client startQueue];
308}
309
310- (BOOL)importFileData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
311{
312        SapphireImportFile *item = [[SapphireImportFile alloc] initWithPath:[file path] informer:inform  type:IMPORT_TYPE_FILE_DATA];
313        [queue addObject:item];
314        [item release];
315        [self itemAdded];
316        return NO;
317}
318
319- (void)importAllData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
320{
321        SapphireImportFile *item = [[SapphireImportFile alloc] initWithPath:[file path] informer:inform type:IMPORT_TYPE_ALL_DATA];
322        [queue addObject:item];
323        [item release];
324        [self itemAdded];
325}
326
327- (void)removeObjectsWithInform:(id <SapphireImporterBackgroundProtocol>)inform
328{
329        if(inform == nil)
330                return;
331       
332        int i, count=[queue count];
333        for(i=0; i<count; i++)
334        {
335                id <SapphireImportFileProtocol> file = [queue objectAtIndex:i];
336                if([file informer] == inform)
337                {
338                        [queue removeObjectAtIndex:i];
339                        i--;
340                        count--;
341                }
342        }
343        if([currentImporting informer] == inform)
344        {
345                [currentImporting release];
346                currentImporting = nil;
347        }
348}
349
350- (id <SapphireImportFileProtocol>)nextFile
351{
352        if(![queue count])
353        {
354                queueSuspended = YES;
355                return nil;
356        }
357        [currentImporting release];
358        currentImporting = [[queue objectAtIndex:0] retain];
359        [queue removeObjectAtIndex:0];
360        return currentImporting;
361}
362
363- (oneway void)setClient:(id <SapphireImportClient>)aClient
364{
365        if(shared == nil)
366        {
367                [aClient exitChild];
368                return;
369        }
370        client = [aClient retain];
371        if([queue count])
372        {
373                queueSuspended = NO;
374                [client startQueue];
375        }
376        else
377                queueSuspended = YES;
378}
379
380- (void)importCompleteWithChanges:(bycopy NSDictionary *)changes updated:(BOOL)updated
381{
382        if(changes != nil)
383                [SapphireMetaDataSupport applyChanges:changes toContext:moc];
384        if(currentImporting == nil)
385                return;
386        [[currentImporting informer] informComplete:updated onPath:[currentImporting path]];
387        [currentImporting release];
388        currentImporting = nil;
389}
390
391@end
392
393@implementation SapphireImportFile
394- (id)initWithPath:(NSString *)aPath informer:(id <SapphireImporterBackgroundProtocol>)aInformer type:(ImportType)aType;
395{
396        self = [super init];
397        if(!self)
398                return nil;
399       
400        path = [aPath retain];
401        informer = [aInformer retain];
402        type = aType;
403       
404        return self;
405}
406- (void) dealloc
407{
408        [path release];
409        [informer release];
410        [super dealloc];
411}
412
413- (bycopy NSString *)path
414{
415        return path;
416}
417- (id <SapphireImporterBackgroundProtocol>)informer
418{
419        return informer;
420}
421
422- (ImportType)importType
423{
424        return type;
425}
426
427@end
Note: See TracBrowser for help on using the repository browser.