Mercurial > hg > nodescore
comparison oscgroups/GroupServer.cpp @ 76:0ae87af84e2f
added oscgroups
author | Rob Canning <rob@foo.net> |
---|---|
date | Sun, 13 Jul 2014 10:07:41 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
75:3a2845e3156e | 76:0ae87af84e2f |
---|---|
1 /* | |
2 OSCgroups -- open sound control groupcasting infrastructure | |
3 Copyright (C) 2005 Ross Bencina | |
4 | |
5 This program is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU General Public License | |
7 as published by the Free Software Foundation; either version 2 | |
8 of the License, or (at your option) any later version. | |
9 | |
10 This program is distributed in the hope that it will be useful, | |
11 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 GNU General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU General Public License | |
16 along with this program; if not, write to the Free Software | |
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 */ | |
19 | |
20 #include "GroupServer.h" | |
21 | |
22 #include <string> | |
23 #include <set> | |
24 #include <iostream> | |
25 #include <ctime> | |
26 #include <algorithm> | |
27 #include <iterator> | |
28 #include <cassert> | |
29 #include <cstring> | |
30 | |
31 #include "ip/NetworkingUtils.h" | |
32 | |
33 #if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug | |
34 namespace std { | |
35 using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'. | |
36 } | |
37 #endif | |
38 | |
39 /* | |
40 TODO: | |
41 switch to using char * instead of string:: to avoid allocating new strings | |
42 every time we execute a find() | |
43 */ | |
44 | |
45 | |
46 static std::ostream& Log() | |
47 { | |
48 std::time_t t; | |
49 std::time( &t ); | |
50 | |
51 // ctime() returns a constant width 26 char string including trailing \0 | |
52 // the fields are all constant width. | |
53 char s[26]; | |
54 std::strcpy( s, std::ctime( &t ) ); | |
55 s[24] = '\0'; // remove trailing null | |
56 | |
57 std::cout << s << ": "; | |
58 return std::cout; | |
59 } | |
60 | |
61 | |
62 GroupServer::GroupServer( int timeoutSeconds, int maxUsers, int maxGroups ) | |
63 : timeoutSeconds_( timeoutSeconds ) | |
64 , maxUsers_( maxUsers ) | |
65 , maxGroups_( maxGroups ) | |
66 , userCount_( 0 ) | |
67 , groupCount_( 0 ) | |
68 { | |
69 | |
70 } | |
71 | |
72 | |
73 GroupServer::~GroupServer() | |
74 { | |
75 for( const_user_iterator i = users_begin(); i != users_end(); ++i ) | |
76 delete i->second; | |
77 for( const_group_iterator i = groups_begin(); i != groups_end(); ++i ) | |
78 delete i->second; | |
79 } | |
80 | |
81 | |
82 User *GroupServer::CreateUser( const char *userName, const char *userPassword ) | |
83 { | |
84 Log() << "creating user '" << userName << "'." << std::endl; | |
85 | |
86 User *user = new User( userName, userPassword ); | |
87 std::pair<user_iterator,bool> r = | |
88 users_.insert( std::make_pair( user->name, user ) ); | |
89 // we assume the caller didn't try to create a user with an existing name | |
90 assert( r.second == true ); | |
91 ++userCount_; | |
92 | |
93 return user; | |
94 } | |
95 | |
96 | |
97 User *GroupServer::FindUser( const char *userName ) | |
98 { | |
99 user_iterator user = users_.find( userName ); | |
100 | |
101 return ( user != users_.end() ) ? user->second : (User*) 0; | |
102 } | |
103 | |
104 | |
105 void GroupServer::PurgeStaleUsers() | |
106 { | |
107 std::time_t currentTime = std::time(0); | |
108 | |
109 for( std::map< std::string, User* >::iterator i = users_.begin(); | |
110 i != users_.end(); /* nothing */ ){ | |
111 | |
112 unsigned long inactiveSeconds = | |
113 i->second->SecondsSinceLastAliveReceived( currentTime ); | |
114 if( inactiveSeconds >= (unsigned int)timeoutSeconds_ ){ | |
115 Log() << "purging user '" << i->second->name << "' after " | |
116 << inactiveSeconds << " seconds of inactivity." << std::endl; | |
117 | |
118 SeparateUserFromAllGroups( i->second ); | |
119 delete i->second; | |
120 --userCount_; | |
121 | |
122 // i think this is safe, we increment i before erasing the | |
123 // element referenced by its old value... | |
124 std::map< std::string, User* >::iterator j = i; | |
125 ++i; | |
126 users_.erase( j ); | |
127 }else{ | |
128 ++i; | |
129 } | |
130 } | |
131 } | |
132 | |
133 | |
134 Group *GroupServer::CreateGroup( | |
135 const char *groupName, const char *groupPassword ) | |
136 { | |
137 Log() << "creating group '" << groupName << "'." << std::endl; | |
138 | |
139 Group *group = new Group( groupName, groupPassword ); | |
140 std::pair<group_iterator, bool> r = | |
141 groups_.insert( std::make_pair( group->name, group ) ); | |
142 // we assume the caller didn't try to create an existing group | |
143 assert( r.second == true ); | |
144 ++groupCount_; | |
145 | |
146 return group; | |
147 } | |
148 | |
149 | |
150 Group *GroupServer::FindGroup( const char *groupName ) | |
151 { | |
152 group_iterator group = groups_.find( groupName ); | |
153 | |
154 return ( group != groups_.end() ) ? group->second : (Group*) 0; | |
155 } | |
156 | |
157 | |
158 // The User <-> Group relation is managed as a bidirectional link (each User | |
159 // contains a set of groups which it is associated with, and each Group | |
160 // maintains a set of Users which it is associated with). The Associate* | |
161 // and Separate* methods below create and destroy the bidirectional link | |
162 // RemoveUserReferenceFromGroup is a helper function which only destroys | |
163 // one side of the link. | |
164 | |
165 void GroupServer::AssociateUserWithGroup( User *user, Group* group ) | |
166 { | |
167 Log() << "adding user '" << user->name | |
168 << "' to group '" << group->name << "'." << std::endl; | |
169 | |
170 user->groups_.insert( group ); | |
171 group->users_.insert( user ); | |
172 } | |
173 | |
174 | |
175 void GroupServer::RemoveUserReferenceFromGroup( User *user, Group* group ) | |
176 { | |
177 Log() << "removing user '" << user->name | |
178 << "' from group '" << group->name << "'." << std::endl; | |
179 | |
180 std::set< User* >::iterator i = group->users_.find( user ); | |
181 assert( i != group->users_.end() ); | |
182 | |
183 group->users_.erase( i ); | |
184 | |
185 if( group->users_.empty() ){ | |
186 Log() << "purging empty group '" << group->name << "'." << std::endl; | |
187 | |
188 groups_.erase( group->name ); | |
189 delete group; | |
190 --groupCount_; | |
191 } | |
192 } | |
193 | |
194 | |
195 void GroupServer::SeparateUserFromGroup( User *user, Group* group ) | |
196 { | |
197 user->groups_.erase( group ); | |
198 RemoveUserReferenceFromGroup( user, group ); | |
199 } | |
200 | |
201 | |
202 void GroupServer::SeparateUserFromAllGroups( User *user ) | |
203 { | |
204 for( std::set<Group*>::iterator i = user->groups_.begin(); | |
205 i != user->groups_.end(); ++i ){ | |
206 | |
207 RemoveUserReferenceFromGroup( user, *i); | |
208 } | |
209 | |
210 user->groups_.clear(); | |
211 } | |
212 | |
213 | |
214 static void UpdateEndpoint( IpEndpointName& dest, const IpEndpointName& src, | |
215 const char *userName, const char *whichEndpoint ) | |
216 { | |
217 if( dest != src ){ | |
218 | |
219 char endpointString[ IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH ]; | |
220 src.AddressAndPortAsString( endpointString ); | |
221 | |
222 Log() << "updating " << whichEndpoint << " endpoint for user '" | |
223 << userName << "' to " << endpointString << "." << std::endl; | |
224 | |
225 dest = src; | |
226 } | |
227 } | |
228 | |
229 | |
230 GroupServer::UserStatus GroupServer::UserAlive( | |
231 const char *userName, const char *userPassword, | |
232 const IpEndpointName& privateEndpoint, | |
233 const IpEndpointName& publicEndpoint, | |
234 const char **groupNamesAndPasswords, | |
235 UserStatus *userGroupsStatus, int groupCount ) | |
236 { | |
237 // find or create the user, aborting if the password for an existing | |
238 // user is incorrect, or if the maximum number of users has been reached | |
239 | |
240 User *user = FindUser( userName ); | |
241 if( user ){ | |
242 if( user->password.compare( userPassword ) != 0 ){ | |
243 Log() << "attempt to update user '" | |
244 << userName << "' with incorrect password." << std::endl; | |
245 return USER_STATUS_WRONG_PASSWORD; | |
246 } | |
247 }else{ | |
248 if( userCount_ == maxUsers_ ){ | |
249 Log() << "user limit reached, user '" | |
250 << userName << "' not admitted." << std::endl; | |
251 return USER_STATUS_SERVER_LIMIT_REACHED; | |
252 }else{ | |
253 user = CreateUser( userName, userPassword ); | |
254 } | |
255 } | |
256 | |
257 UpdateEndpoint( | |
258 user->privateEndpoint, privateEndpoint, userName, "private" ); | |
259 UpdateEndpoint( user->publicEndpoint, publicEndpoint, userName, "public" ); | |
260 | |
261 user->lastAliveMessageArrivalTime = time(0); | |
262 | |
263 | |
264 // previousButNotCurrentGroups begins containing all the groups the user | |
265 // was in before the current message was received. We remove those which | |
266 // the user is still a member of, leaving the set that the user is no | |
267 // longer a member of. We then use the set to remove associations with | |
268 // non-current groups. | |
269 | |
270 std::set<Group*> previousButNotCurrentGroups( user->groups_ ); | |
271 | |
272 for( int i=0; i < groupCount; ++i ){ | |
273 const char *groupName = groupNamesAndPasswords[i*2]; | |
274 const char *groupPassword = groupNamesAndPasswords[i*2 + 1]; | |
275 | |
276 Group *group = FindGroup( groupName ); | |
277 if( group ){ | |
278 if( user->IsMemberOf( group ) ){ | |
279 // check that previousButNotCurrentGroups contains group before | |
280 // removing it. it won't if we were passed the same group | |
281 // multiple times | |
282 std::set<Group*>::iterator j = | |
283 previousButNotCurrentGroups.find( group ); | |
284 if( j != previousButNotCurrentGroups.end() ) | |
285 previousButNotCurrentGroups.erase( j ); | |
286 | |
287 userGroupsStatus[i] = USER_STATUS_OK; | |
288 }else{ | |
289 // user isn't in group so join it | |
290 if( group->password.compare( groupPassword ) == 0 ){ | |
291 AssociateUserWithGroup( user, group ); | |
292 userGroupsStatus[i] = USER_STATUS_OK; | |
293 }else{ | |
294 userGroupsStatus[i] = USER_STATUS_WRONG_PASSWORD; | |
295 } | |
296 } | |
297 }else{ // group doesn't exist | |
298 if( groupCount_ == maxGroups_ ){ | |
299 Log() << "group limit reached, group '" | |
300 << groupName << "' not created." << std::endl; | |
301 userGroupsStatus[i] = USER_STATUS_SERVER_LIMIT_REACHED; | |
302 }else{ | |
303 group = CreateGroup( groupName, groupPassword ); | |
304 AssociateUserWithGroup( user, group ); | |
305 userGroupsStatus[i] = USER_STATUS_OK; | |
306 } | |
307 } | |
308 } | |
309 | |
310 for( std::set<Group*>::iterator j = previousButNotCurrentGroups.begin(); | |
311 j != previousButNotCurrentGroups.end(); ++j ){ | |
312 | |
313 SeparateUserFromGroup( user, *j ); | |
314 } | |
315 | |
316 return USER_STATUS_OK; | |
317 } | |
318 |