source: branches/CoreData/SapphireFrappliance/FRAppliance/SapphireImportHelper.m @ 659

Revision 659, 10.5 KB checked in by gbooker, 6 years ago (diff)

Corrected a case where import was never actually done in the background.

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