annotate oscgroups/GroupServer.cpp @ 101:52e44ee1c791 tip master

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