/* * Copyright 2012 The Stanford MobiSocial Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package mobisocial.musubi.objects; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import mobisocial.crypto.IBHashedIdentity; import mobisocial.crypto.IBHashedIdentity.Authority; import mobisocial.musubi.App; import mobisocial.musubi.Helpers; import mobisocial.musubi.feed.iface.DbEntryHandler; import mobisocial.musubi.model.MFeed; import mobisocial.musubi.model.MIdentity; import mobisocial.musubi.model.MObject; import mobisocial.musubi.model.helpers.FeedManager; import mobisocial.musubi.model.helpers.IdentitiesManager; import mobisocial.musubi.provider.MusubiContentProvider; import mobisocial.musubi.provider.MusubiContentProvider.Provided; import mobisocial.musubi.service.MusubiService; import mobisocial.musubi.util.Util; import mobisocial.socialkit.Obj; import mobisocial.socialkit.SignedObj; import mobisocial.socialkit.obj.MemObj; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.database.sqlite.SQLiteOpenHelper; import android.util.Base64; import android.util.Log; //TODO: this an absymally lame copy-file, override needed methods, delete unchange methods implementation //this needs hella cleanup /** * An object that provides information about a participant who is requesting to join a group. * For now we just accept them blindly (the obj pipeline runs regardless of the 'accepted' flag * on the feed, so there is no other possibility ATM). * * A capability is ofcourse required so it isnt totally bat shit insane. */ public class JoinRequestObj extends DbEntryHandler { public static final String TYPE = "join_request"; @Override public String getType() { return TYPE; } public static MemObj from(Collection<MIdentity> identities) { return new MemObj(TYPE, IntroductionObj.json(identities, false)); } @Override public boolean isRenderable(SignedObj obj) { // TODO Auto-generated method stub return super.isRenderable(obj); } @Override public boolean processObject(Context context, MFeed feed, MIdentity sender, MObject object) { boolean anyChanged = false; SQLiteOpenHelper databaseSource = App.getDatabaseSource(context); IdentitiesManager identitiesManager = new IdentitiesManager(databaseSource); FeedManager feedManager = new FeedManager(databaseSource); if(identitiesManager.isMe(IdentitiesManager.toIBHashedIdentity(sender, 0))) { return true; } if (object.json_ == null) { Log.w(TAG, "bad introduction format"); return false; } JSONObject json; try { json = new JSONObject(object.json_); } catch (JSONException e) { Log.e(TAG, "Bad json in database", e); return false; } JSONArray array; try { array = json.getJSONArray(IntroductionObj.IDENTITIES); } catch (JSONException e) { Log.e(TAG, "json identity array missing for join", e); return false; } LinkedList<MIdentity> joined_identities = new LinkedList<MIdentity>(); // TODO: use getIdentitiesForObj for(int i = 0; i < array.length(); ++i) { //TODO: enforce that a person can only join themself? It's kind of nice to be able //to join multiple people, but it enables annoying reflection attacks JSONObject identity; try { identity = array.getJSONObject(i); } catch (JSONException e) { Log.e(TAG, "identity entry in join access error", e); continue; } int authority = -1; String principalHashString = null; try { authority = identity.getInt(IntroductionObj.ID_AUTHORITY); principalHashString = identity.getString(IntroductionObj.ID_PRINCIPAL_HASH); } catch (JSONException e) { Log.e(TAG, "identity entry in introduction missing key fields", e); continue; } String principal = null; try { principal = identity.getString(IntroductionObj.ID_PRINCIPAL); } catch (JSONException e) { } String name = null; try { name = identity.getString(IntroductionObj.ID_NAME); } catch (JSONException e) { } if(name == null && principal == null) { //not much of an introduction continue; } byte[] principalHash = Base64.decode(principalHashString, Base64.DEFAULT); IBHashedIdentity hid = new IBHashedIdentity(Authority.values()[authority], principalHash, 0); MIdentity ident = identitiesManager.getIdentityForIBHashedIdentity(hid); if(ident == null) { //this introduction has to be sent to both participants, so the low level //will already have added the identity Log.e(TAG, "identity join for totally unseen identities"); continue; } if(ident.owned_) { //we won't have a received profile version, so owned keeps us from self updating continue; } //TODO: rely on deferred handling for gray list participants //TODO: check that the person is actually in the feed boolean changed = false; if(principal != null && ident.principal_ == null) { if(!Arrays.equals(Util.sha256(principal.getBytes()), principalHash)) { Log.e(TAG, "received mismatched principal and principal hash in join"); continue; } changed = true; ident.principal_ = principal; } if(name != null && ident.receivedProfileVersion_ == 0) { changed = true; //each time someone join us, we'll just accept the new name //as long as we never got a real profile. ident.musubiName_ = name; } //TODO: low level already added them...need to do this some other way //if(!feedManager.isFeedMember(feed.id_, ident.id_)) joined_identities.add(ident); if(changed) { identitiesManager.updateIdentity(ident); anyChanged = true; } } //we let the sucka in, so tell the other members of the feed Obj invitedObj = IntroductionObj.from(joined_identities, false); Helpers.sendToFeed(context, invitedObj, MusubiContentProvider.uriForItem(Provided.FEEDS_ID, feed.id_)); if(anyChanged) { context.getContentResolver().notifyChange(MusubiService.PRIMARY_CONTENT_CHANGED, null); } return true; } }