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

Revision 792, 11.1 KB checked in by gbooker, 6 years ago (diff)

Catch Exceptions surrounding this function which is executed on main thread.

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