2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
21 // $Id: SOPEXSNSController.m 1 2004-08-20 11:17:52Z znek $
22 // Created by znek on Wed Feb 11 2004.
24 #import "SOPEXSNSController.h"
25 #include <netinet/in.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
30 #define DNC [NSNotificationCenter defaultCenter]
31 #define UD [NSUserDefaults standardUserDefaults]
34 NSString *SNSApplicationNameKey = @"Name";
35 NSString *SNSApplicationPathKey = @"Path";
36 NSString *SNSApplicationPIDKey = @"PID";
37 NSString *SNSApplicationAddressKey = @"Address";
41 SNSUnregisterInstance = 0,
42 SNSRegisterInstance = 1,
43 SNSRegisterSession = 2,
45 SNSTerminateSession = 4,
46 SNSLookupSession = 50,
47 SNSInstanceAlive = 100
51 @interface NSFileHandle (SOPEXSNSControllerPrivate)
52 - (NSData *)_safeReadDataOfLength:(unsigned int)length;
53 - (NSData *)_snsGetData;
54 - (NSString *)_snsGetString;
58 @implementation NSFileHandle (SOPEXSNSControllerPrivate)
59 - (NSData *)_safeReadDataOfLength:(unsigned int)length
61 NSMutableData *safeData;
65 data = [self readDataOfLength:length];
66 stillNeeded = length - [data length];
71 safeData = [[NSMutableData alloc] initWithData:data];
72 while(stillNeeded > 0)
74 data = [self readDataOfLength:stillNeeded];
75 [safeData appendData:data];
76 stillNeeded -= [data length];
78 return [safeData autorelease];
81 - (NSData *)_snsGetData
87 data = [self _safeReadDataOfLength:sizeof(int)];
88 length = *(int *)[data bytes];
89 data = [self _safeReadDataOfLength:length];
92 - (NSString *)_snsGetString
94 NSData *data = [self _snsGetData];
95 return [[[NSString alloc] initWithCString:(const char *)[data bytes] length:[data length]] autorelease];
102 data = [self _safeReadDataOfLength:sizeof(int)];
103 integer = *(int *)[data bytes];
110 @implementation SOPEXSNSController
113 #pragma mark ### INIT & DEALLOC ###
119 self->connectionLUT = [[NSMutableDictionary alloc] initWithCapacity:1];
126 [self->connectionLUT release];
132 #pragma mark ### DELEGATE ###
135 - (void)setDelegate:(id)_delegate
137 self->delegate = _delegate;
138 self->dflags.respondsToUnregisterInstance = [_delegate respondsToSelector:@selector(snsController:unregisterInstance:)];
139 self->dflags.respondsToRegisterInstance = [_delegate respondsToSelector:@selector(snsController:registerInstance:)];
140 self->dflags.respondsToInstanceIsAlive = [_delegate respondsToSelector:@selector(snsController:instanceIsAlive:)];
141 self->dflags.respondsToRegisterSession = [_delegate respondsToSelector:@selector(snsController:instance:sessionDidCreate:)];
142 self->dflags.respondsToExpireSession = [_delegate respondsToSelector:@selector(snsController:instance:sessionDidExpire:)];
143 self->dflags.respondsToTerminateSession = [_delegate respondsToSelector:@selector(snsController:instance:sessionDidTerminate:)];
148 return self->delegate;
153 #pragma mark ### START & STOP ###
159 struct sockaddr_in sockaddr;
162 sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
163 NSAssert1(sd >= 0, @"Couldn't create server socket: %s", strerror(errno));
165 memset(&sockaddr, 0, sizeof(struct sockaddr_in));
166 sockaddr.sin_family = AF_INET;
167 sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
168 sockaddr.sin_port = [UD integerForKey:@"SNSPort"];
171 NSAssert1(bind(sd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, @"Couldn't bind socket: %s", strerror(errno));
173 // listen with backlog of 5
174 NSAssert1(listen(sd, 5) != -1, @"Couldn't listen on socket: %s", strerror(errno));
176 // create NSFileHandle if all is well
177 self->serverSocket = [[NSFileHandle alloc] initWithFileDescriptor:sd closeOnDealloc:YES];
179 // we're ready to accept connections now
180 [DNC addObserver:self selector:@selector(acceptConnection:) name:NSFileHandleConnectionAcceptedNotification object:self->serverSocket];
181 [self->serverSocket acceptConnectionInBackgroundAndNotify];
186 [DNC removeObserver:self];
187 [self->serverSocket release];
188 self->serverSocket = nil;
193 #pragma mark ### ACCESSORS ###
196 - (NSString *)socketAddress
199 struct sockaddr_in sockaddr;
201 sockaddrLength = sizeof(struct sockaddr_in);
202 NSAssert1(getsockname([self->serverSocket fileDescriptor], (struct sockaddr *)&sockaddr, &sockaddrLength) != -1, @"Cannot get local port number for socket: %s", strerror(errno));
203 return [NSString stringWithFormat:@"localhost:%d", ntohs(sockaddr.sin_port)];
208 #pragma mark ### SNSD PROTOCOL ###
211 - (NSDictionary *)_instanceDescriptionForFileHandle:(NSFileHandle *)fileHandle
213 return [self->connectionLUT objectForKey:[NSNumber numberWithInt:[fileHandle fileDescriptor]]];
216 - (void)_unregisterInstance:(NSFileHandle *)fileHandle
218 if(self->dflags.respondsToUnregisterInstance)
219 [self->delegate snsController:self unregisterInstance:[self _instanceDescriptionForFileHandle:fileHandle]];
220 [self->connectionLUT removeObjectForKey:[NSNumber numberWithInt:[fileHandle fileDescriptor]]];
223 - (void)_registerInstance:(NSFileHandle *)fileHandle description:(NSDictionary *)instanceDescription
225 [self->connectionLUT setObject:instanceDescription forKey:[NSNumber numberWithInt:[fileHandle fileDescriptor]]];
226 if(self->dflags.respondsToRegisterInstance)
227 [self->delegate snsController:self registerInstance:instanceDescription];
231 - (void)acceptConnection:(NSNotification *)notification
233 NSFileHandle *remote;
235 remote = [[notification userInfo] objectForKey:NSFileHandleNotificationFileHandleItem];
238 [DNC addObserver:self selector:@selector(availableData:) name:NSFileHandleDataAvailableNotification object:remote];
239 [remote waitForDataInBackgroundAndNotify];
240 [self->serverSocket acceptConnectionInBackgroundAndNotify];
243 - (void)availableData:(NSNotification *)notification
245 NSFileHandle *remote;
249 remote = [notification object];
250 data = [remote readDataOfLength:1];
251 if([data length] == 0)
254 NSLog(@"%s remote end did die!", __PRETTY_FUNCTION__);
256 [DNC removeObserver:self name:NSFileHandleDataAvailableNotification object:remote];
257 [self _unregisterInstance:remote];
262 msg = *(char *)[data bytes];
263 if(msg == SNSInstanceAlive)
265 if(self->dflags.respondsToInstanceIsAlive)
266 [self->delegate snsController:self instanceIsAlive:[self _instanceDescriptionForFileHandle:remote]];
268 else if(msg == SNSRegisterSession)
272 sessionID = [remote _snsGetString];
273 if(self->dflags.respondsToRegisterSession)
274 [self->delegate snsController:self instance:[self _instanceDescriptionForFileHandle:remote] sessionDidCreate:sessionID];
276 else if(msg == SNSExpireSession)
280 sessionID = [remote _snsGetString];
281 if(self->dflags.respondsToExpireSession)
282 [self->delegate snsController:self instance:[self _instanceDescriptionForFileHandle:remote] sessionDidExpire:sessionID];
284 else if(msg == SNSTerminateSession)
288 sessionID = [remote _snsGetString];
289 if(self->dflags.respondsToTerminateSession)
290 [self->delegate snsController:self instance:[self _instanceDescriptionForFileHandle:remote] sessionDidTerminate:sessionID];
292 else if(msg == SNSRegisterInstance)
294 NSMutableDictionary *instanceDescription;
295 id tmp, applicationAddress;
298 instanceDescription = [[NSMutableDictionary alloc] initWithCapacity:3];
301 tmp = [remote _snsGetString];
302 [instanceDescription setObject:tmp forKey:SNSApplicationNameKey];
305 tmp = [remote _snsGetString];
306 [instanceDescription setObject:tmp forKey:SNSApplicationPathKey];
309 pid = [remote _snsGetInt];
310 [instanceDescription setObject:[NSNumber numberWithInt:pid] forKey:SNSApplicationPIDKey];
312 // Application Address
313 tmp = [remote _snsGetData];
314 applicationAddress = [NSUnarchiver unarchiveObjectWithData:tmp];
315 [instanceDescription setObject:applicationAddress forKey:SNSApplicationAddressKey];
317 [self _registerInstance:remote description:instanceDescription];
318 [instanceDescription release];
320 else if(msg == SNSUnregisterInstance)
322 [self _unregisterInstance:remote];
326 NSLog(@"%s ignoring unknown messageCode:%d Dropping %d bytes.", __PRETTY_FUNCTION__, msg, [[remote availableData] length]);
328 [remote waitForDataInBackgroundAndNotify];