]> err.no Git - sope/blob - sopex/SOPEX/SOPEXSNSController.m
fixed a warning
[sope] / sopex / SOPEX / SOPEXSNSController.m
1 /*
2  Copyright (C) 2000-2003 SKYRIX Software AG
3
4  This file is part of OGo
5
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
9  later version.
10
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.
15
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
19  02111-1307, USA.
20  */
21 // $Id: SOPEXSNSController.m 1 2004-08-20 11:17:52Z znek $
22 //  Created by znek on Wed Feb 11 2004.
23
24 #import "SOPEXSNSController.h"
25 #include <netinet/in.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28
29
30 #define DNC [NSNotificationCenter defaultCenter]
31 #define UD [NSUserDefaults standardUserDefaults]
32
33
34 NSString *SNSApplicationNameKey    = @"Name";
35 NSString *SNSApplicationPathKey    = @"Path";
36 NSString *SNSApplicationPIDKey     = @"PID";
37 NSString *SNSApplicationAddressKey = @"Address";
38
39
40 typedef enum {
41     SNSUnregisterInstance = 0,
42     SNSRegisterInstance   = 1,
43     SNSRegisterSession    = 2,
44     SNSExpireSession      = 3,
45     SNSTerminateSession   = 4,
46     SNSLookupSession      = 50,
47     SNSInstanceAlive      = 100
48 } SNSMessageCode;
49
50
51 @interface NSFileHandle (SOPEXSNSControllerPrivate)
52 - (NSData *)_safeReadDataOfLength:(unsigned int)length;
53 - (NSData *)_snsGetData;
54 - (NSString *)_snsGetString;
55 - (int)_snsGetInt;
56 @end
57
58 @implementation NSFileHandle (SOPEXSNSControllerPrivate)
59 - (NSData *)_safeReadDataOfLength:(unsigned int)length
60 {
61     NSMutableData *safeData;
62     NSData *data;
63     int stillNeeded;
64
65     data = [self readDataOfLength:length];
66     stillNeeded = length - [data length];
67     
68     if(stillNeeded == 0)
69         return data;
70
71     safeData = [[NSMutableData alloc] initWithData:data];
72     while(stillNeeded > 0)
73     {
74         data = [self readDataOfLength:stillNeeded];
75         [safeData appendData:data];
76         stillNeeded -= [data length];
77     }
78     return [safeData autorelease];
79 }
80
81 - (NSData *)_snsGetData
82 {
83     NSData *data;
84     int length;
85     
86     // Application Name
87     data = [self _safeReadDataOfLength:sizeof(int)];
88     length = *(int *)[data bytes];
89     data = [self _safeReadDataOfLength:length];
90     return data;
91 }
92 - (NSString *)_snsGetString
93 {
94     NSData *data = [self _snsGetData];
95     return [[[NSString alloc] initWithCString:(const char *)[data bytes] length:[data length]] autorelease];
96 }
97 - (int)_snsGetInt
98 {
99     NSData *data;
100     int integer;
101
102     data = [self _safeReadDataOfLength:sizeof(int)];
103     integer = *(int *)[data bytes];
104     return integer;
105 }
106
107 @end
108
109
110 @implementation SOPEXSNSController
111
112 #pragma mark -
113 #pragma mark ### INIT & DEALLOC ###
114
115
116 - (id)init
117 {
118     [super init];
119     self->connectionLUT = [[NSMutableDictionary alloc] initWithCapacity:1];
120     return self;
121 }
122
123 - (void)dealloc
124 {
125     [self stop];
126     [self->connectionLUT release];
127     [super dealloc];
128 }
129
130
131 #pragma mark -
132 #pragma mark ### DELEGATE ###
133
134
135 - (void)setDelegate:(id)_delegate
136 {
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:)];
144 }
145
146 - (id)delegate
147 {
148     return self->delegate;
149 }
150
151
152 #pragma mark -
153 #pragma mark ### START & STOP ###
154
155
156 - (void)start
157 {
158     int sd;
159     struct sockaddr_in sockaddr;
160
161     // create socket
162     sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
163     NSAssert1(sd >= 0, @"Couldn't create server socket: %s", strerror(errno));
164
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"];
169
170     // bind
171     NSAssert1(bind(sd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, @"Couldn't bind socket: %s", strerror(errno));
172
173     // listen with backlog of 5
174     NSAssert1(listen(sd, 5) != -1, @"Couldn't listen on socket: %s", strerror(errno));
175
176     // create NSFileHandle if all is well
177     self->serverSocket = [[NSFileHandle alloc] initWithFileDescriptor:sd closeOnDealloc:YES];
178
179     // we're ready to accept connections now
180     [DNC addObserver:self selector:@selector(acceptConnection:) name:NSFileHandleConnectionAcceptedNotification object:self->serverSocket];
181     [self->serverSocket acceptConnectionInBackgroundAndNotify];
182 }
183
184 - (void)stop
185 {
186     [DNC removeObserver:self];
187     [self->serverSocket release];
188     self->serverSocket = nil;
189 }
190
191
192 #pragma mark -
193 #pragma mark ### ACCESSORS ###
194
195
196 - (NSString *)socketAddress
197 {
198     int sockaddrLength;
199     struct sockaddr_in sockaddr;
200     
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)];
204 }
205
206
207 #pragma mark -
208 #pragma mark ### SNSD PROTOCOL ###
209
210
211 - (NSDictionary *)_instanceDescriptionForFileHandle:(NSFileHandle *)fileHandle
212 {
213     return [self->connectionLUT objectForKey:[NSNumber numberWithInt:[fileHandle fileDescriptor]]];
214 }
215
216 - (void)_unregisterInstance:(NSFileHandle *)fileHandle
217 {
218     if(self->dflags.respondsToUnregisterInstance)
219         [self->delegate snsController:self unregisterInstance:[self _instanceDescriptionForFileHandle:fileHandle]];
220     [self->connectionLUT removeObjectForKey:[NSNumber numberWithInt:[fileHandle fileDescriptor]]];
221 }
222
223 - (void)_registerInstance:(NSFileHandle *)fileHandle description:(NSDictionary *)instanceDescription
224 {
225     [self->connectionLUT setObject:instanceDescription forKey:[NSNumber numberWithInt:[fileHandle fileDescriptor]]];
226     if(self->dflags.respondsToRegisterInstance)
227         [self->delegate snsController:self registerInstance:instanceDescription];
228 }
229
230
231 - (void)acceptConnection:(NSNotification *)notification
232 {
233     NSFileHandle *remote;
234
235     remote = [[notification userInfo] objectForKey:NSFileHandleNotificationFileHandleItem];
236     [remote retain];
237
238     [DNC addObserver:self selector:@selector(availableData:) name:NSFileHandleDataAvailableNotification object:remote];
239     [remote waitForDataInBackgroundAndNotify];
240     [self->serverSocket acceptConnectionInBackgroundAndNotify];
241 }
242
243 - (void)availableData:(NSNotification *)notification
244 {
245     NSFileHandle *remote;
246     NSData *data;
247     SNSMessageCode msg;
248     
249     remote = [notification object];
250     data = [remote readDataOfLength:1];
251     if([data length] == 0)
252     {
253 #if 1
254         NSLog(@"%s remote end did die!", __PRETTY_FUNCTION__);
255 #endif
256         [DNC removeObserver:self name:NSFileHandleDataAvailableNotification object:remote];
257         [self _unregisterInstance:remote];
258         [remote release];
259         return;
260     }
261     
262     msg = *(char *)[data bytes];
263     if(msg == SNSInstanceAlive)
264     {
265         if(self->dflags.respondsToInstanceIsAlive)
266             [self->delegate snsController:self instanceIsAlive:[self _instanceDescriptionForFileHandle:remote]]; 
267     }
268     else if(msg == SNSRegisterSession)
269     {
270         NSString *sessionID;
271         
272         sessionID = [remote _snsGetString];
273         if(self->dflags.respondsToRegisterSession)
274             [self->delegate snsController:self instance:[self _instanceDescriptionForFileHandle:remote] sessionDidCreate:sessionID];
275     }
276     else if(msg == SNSExpireSession)
277     {
278         NSString *sessionID;
279         
280         sessionID = [remote _snsGetString];
281         if(self->dflags.respondsToExpireSession)
282             [self->delegate snsController:self instance:[self _instanceDescriptionForFileHandle:remote] sessionDidExpire:sessionID];
283     }
284     else if(msg == SNSTerminateSession)
285     {
286         NSString *sessionID;
287         
288         sessionID = [remote _snsGetString];
289         if(self->dflags.respondsToTerminateSession)
290             [self->delegate snsController:self instance:[self _instanceDescriptionForFileHandle:remote] sessionDidTerminate:sessionID];
291     }
292     else if(msg == SNSRegisterInstance)
293     {
294         NSMutableDictionary *instanceDescription;
295         id tmp, applicationAddress;
296         int pid;
297
298         instanceDescription = [[NSMutableDictionary alloc] initWithCapacity:3];
299
300         // Application Name
301         tmp = [remote _snsGetString];
302         [instanceDescription setObject:tmp forKey:SNSApplicationNameKey];
303
304         // Application Path
305         tmp = [remote _snsGetString];
306         [instanceDescription setObject:tmp forKey:SNSApplicationPathKey];
307
308         // Application PID
309         pid = [remote _snsGetInt];
310         [instanceDescription setObject:[NSNumber numberWithInt:pid] forKey:SNSApplicationPIDKey];
311
312         // Application Address
313         tmp = [remote _snsGetData];
314         applicationAddress = [NSUnarchiver unarchiveObjectWithData:tmp];
315         [instanceDescription setObject:applicationAddress forKey:SNSApplicationAddressKey];
316
317         [self _registerInstance:remote description:instanceDescription];
318         [instanceDescription release];
319     }
320     else if(msg == SNSUnregisterInstance)
321     {
322         [self _unregisterInstance:remote];
323     }
324     else
325     {
326         NSLog(@"%s ignoring unknown messageCode:%d Dropping %d bytes.", __PRETTY_FUNCTION__, msg, [[remote availableData] length]);
327     }
328     [remote waitForDataInBackgroundAndNotify];
329 }
330
331 @end