root/oscgroups/trunk/GroupServer.cpp

Revision 31, 9.5 kB (checked in by ross, 4 years ago)

updated to use new osc/... ip/... include directory conventions from oscpack

  • Property svn:eol-style set to native
Line 
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 <time.h>
26 #include <algorithm>
27 #include <iterator>
28 #include <assert.h>
29
30 #include "ip/NetworkingUtils.h"
31
32 /*
33 TODO:
34     switch to using char * instead of string:: to avoid allocating new strings
35     every time we execute a find()
36 */
37
38
39 static std::ostream& Log()
40 {
41     std::time_t t;
42     time( &t );
43
44     // ctime() returns a constant width 26 char string including trailing \0
45     // the fields are all constant width.
46     char s[26];
47     strcpy( s, std::ctime( &t ) );
48     s[24] = '\0'; // remove trailing null
49
50     std::cout << s << ": ";
51     return std::cout;
52 }
53
54
55 GroupServer::GroupServer( int timeoutSeconds, int maxUsers, int maxGroups )
56     : timeoutSeconds_( timeoutSeconds )
57     , maxUsers_( maxUsers )
58     , maxGroups_( maxGroups )
59     , userCount_( 0 )
60     , groupCount_( 0 )
61 {
62
63 }
64
65
66 GroupServer::~GroupServer()
67 {
68     for( const_user_iterator i = users_begin(); i != users_end(); ++i )
69         delete i->second;
70     for( const_group_iterator i = groups_begin(); i != groups_end(); ++i )
71         delete i->second;
72 }
73
74
75 User *GroupServer::CreateUser( const char *userName, const char *userPassword )
76 {
77     Log() << "creating user '" << userName << "'." << std::endl;
78    
79     User *user = new User( userName, userPassword );
80     std::pair<user_iterator,bool> r =
81             users_.insert( std::make_pair( user->name, user ) );
82     // we assume the caller didn't try to create a user with an existing name
83     assert( r.second == true );
84     ++userCount_;
85
86     return user;
87 }
88
89
90 User *GroupServer::FindUser( const char *userName )
91 {
92     user_iterator user = users_.find( userName );
93
94     return ( user != users_.end() ) ? user->second : (User*) 0;
95 }
96
97
98 void GroupServer::PurgeStaleUsers()
99 {
100     unsigned long currentTime = time(0);
101    
102     for( std::map< std::string, User* >::iterator i = users_.begin();
103             i != users_.end(); /* nothing */ ){
104
105         unsigned long inactiveSeconds =
106                 i->second->SecondsSinceLastAliveReceived( currentTime );
107         if( inactiveSeconds >= (unsigned int)timeoutSeconds_ ){
108             Log() << "purging user '" << i->second->name << "' after "
109                     << inactiveSeconds << " seconds of inactivity." << std::endl;
110
111             SeparateUserFromAllGroups( i->second );
112             delete i->second;
113             --userCount_;
114                
115             // i think this is safe, we increment i before erasing the
116             // element referenced by its old value...
117             std::map< std::string, User* >::iterator j = i;
118             ++i;
119             users_.erase( j );
120         }else{
121             ++i;
122         }
123     }
124 }
125
126
127 Group *GroupServer::CreateGroup(
128         const char *groupName, const char *groupPassword )
129 {
130     Log() << "creating group '" << groupName << "'." << std::endl;
131    
132     Group *group = new Group( groupName, groupPassword );
133     std::pair<group_iterator, bool> r =
134             groups_.insert( std::make_pair( group->name, group ) );
135     // we assume the caller didn't try to create an existing group
136     assert( r.second == true );
137     ++groupCount_;
138
139     return group;
140 }
141
142
143 Group *GroupServer::FindGroup( const char *groupName )
144 {
145     group_iterator group = groups_.find( groupName );
146
147     return ( group != groups_.end() ) ? group->second : (Group*) 0;
148 }
149
150
151 // The User <-> Group relation is managed as a bidirectional link (each User
152 // contains a set of groups which it is associated with, and each Group
153 // maintains a set of Users which it is associated with). The Associate*
154 // and Separate* methods below create and destroy the bidirectional link
155 // RemoveUserReferenceFromGroup is a helper function which only destroys
156 // one side of the link.
157
158 void GroupServer::AssociateUserWithGroup( User *user, Group* group )
159 {
160     Log() << "adding user '" << user->name
161             << "' to group '" << group->name << "'." << std::endl;
162    
163     user->groups_.insert( group );
164     group->users_.insert( user );
165 }
166
167
168 void GroupServer::RemoveUserReferenceFromGroup( User *user, Group* group )
169 {
170     Log() << "removing user '" << user->name
171             << "' from group '" << group->name << "'." << std::endl;
172    
173     std::set< User* >::iterator i = group->users_.find( user );
174     assert( i != group->users_.end() );
175
176     group->users_.erase( i );
177
178     if( group->users_.empty() ){
179         Log() << "purging empty group '" << group->name << "'." << std::endl;
180        
181         groups_.erase( group->name );
182         delete group;
183         --groupCount_;
184     }
185 }
186
187
188 void GroupServer::SeparateUserFromGroup( User *user, Group* group )
189 {
190     user->groups_.erase( group );
191     RemoveUserReferenceFromGroup( user, group );
192 }
193
194
195 void GroupServer::SeparateUserFromAllGroups( User *user )
196 {
197     for( std::set<Group*>::iterator i = user->groups_.begin();
198             i != user->groups_.end(); ++i ){
199
200         RemoveUserReferenceFromGroup( user, *i);
201     }
202
203     user->groups_.clear();
204 }
205
206
207 static void UpdateEndpoint( IpEndpointName& dest, const IpEndpointName& src,
208         const char *userName, const char *whichEndpoint )
209 {
210     if( dest != src ){
211
212                 char endpointString[ IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH ];
213                 src.AddressAndPortAsString( endpointString );
214
215         Log() << "updating " << whichEndpoint << " endpoint for user '"
216                 << userName << "' to " << endpointString << "." << std::endl;
217
218         dest = src;
219     }
220 }
221
222
223 GroupServer::UserStatus GroupServer::UserAlive(
224         const char *userName, const char *userPassword,
225         const IpEndpointName& privateEndpoint,
226                 const IpEndpointName& publicEndpoint,
227         const char **groupNamesAndPasswords,
228         UserStatus *userGroupsStatus, int groupCount )
229 {
230     // find or create the user, aborting if the password for an existing
231     // user is incorrect, or if the maximum number of users has been reached
232
233     User *user = FindUser( userName );
234     if( user ){
235         if( user->password.compare( userPassword ) != 0 ){
236             Log() << "attempt to update user '"
237                     << userName << "' with incorrect password." << std::endl;
238             return USER_STATUS_WRONG_PASSWORD;
239         }
240     }else{
241         if( userCount_ == maxUsers_ ){
242             Log() << "user limit reached, user '"
243                     << userName << "' not admitted." << std::endl;
244             return USER_STATUS_SERVER_LIMIT_REACHED;
245         }else{
246             user = CreateUser( userName, userPassword );
247         }
248     }
249
250     UpdateEndpoint(
251             user->privateEndpoint, privateEndpoint, userName, "private" );
252     UpdateEndpoint( user->publicEndpoint, publicEndpoint, userName, "public" );
253
254     user->lastAliveMessageArrivalTime = time(0);
255
256
257     // previousButNotCurrentGroups begins containing all the groups the user
258     // was in before the current message was received. We remove those which
259     // the user is still a member of, leaving the set that the user is no
260     // longer a member of. We then use the set to remove associations with
261     // non-current groups.
262
263     std::set<Group*> previousButNotCurrentGroups( user->groups_ );
264
265     for( int i=0; i < groupCount; ++i ){
266         const char *groupName = groupNamesAndPasswords[i*2];
267         const char *groupPassword = groupNamesAndPasswords[i*2 + 1];
268
269         Group *group = FindGroup( groupName );
270         if( group ){
271             if( user->IsMemberOf( group ) ){
272                 // check that previousButNotCurrentGroups contains group before
273                 // removing it. it won't if we were passed the same group
274                 // multiple times
275                 std::set<Group*>::iterator j =
276                         previousButNotCurrentGroups.find( group );
277                 if( j != previousButNotCurrentGroups.end() )
278                     previousButNotCurrentGroups.erase( j );
279
280                 userGroupsStatus[i] = USER_STATUS_OK;
281             }else{
282                 // user isn't in group so join it
283                 if( group->password.compare( groupPassword ) == 0 ){
284                     AssociateUserWithGroup( user, group );
285                     userGroupsStatus[i] = USER_STATUS_OK;
286                 }else{                                   
287                     userGroupsStatus[i] = USER_STATUS_WRONG_PASSWORD;
288                 }
289             }
290         }else// group doesn't exist
291             if( groupCount_ == maxGroups_ ){
292                 Log() << "group limit reached, group '"
293                         << groupName << "' not created." << std::endl;
294                 userGroupsStatus[i] = USER_STATUS_SERVER_LIMIT_REACHED;
295             }else{
296                 group = CreateGroup( groupName, groupPassword );
297                 AssociateUserWithGroup( user, group );
298                 userGroupsStatus[i] = USER_STATUS_OK;
299             }
300         }
301     }
302
303     for( std::set<Group*>::iterator j = previousButNotCurrentGroups.begin();
304             j != previousButNotCurrentGroups.end(); ++j ){
305
306         SeparateUserFromGroup( user, *j );
307     }
308
309     return USER_STATUS_OK;
310 }
311
Note: See TracBrowser for help on using the browser.