/** * Sets the name associated with this entry. * * @param name the name. * @throws NotConnectedException * @throws XMPPErrorException * @throws NoResponseException */ public synchronized void setName(String name) throws NotConnectedException, NoResponseException, XMPPErrorException { // Do nothing if the name hasn't changed. if (name != null && name.equals(this.name)) { return; } RosterPacket packet = new RosterPacket(); packet.setType(IQ.Type.set); // Create a new roster item with the current RosterEntry and the *new* name. Note that we can't set the name of // RosterEntry right away, as otherwise the updated event wont get fired, because equalsDeep would return true. packet.addRosterItem(toRosterItem(this, name)); connection().createPacketCollectorAndSend(packet).nextResultOrThrow(); // We have received a result response to the IQ set, the name was successfully changed this.name = name; }
/** * Reloads the entire roster from the server. This is an asynchronous operation, * which means the method will return immediately, and the roster will be * reloaded at a later point when the server responds to the reload request. * @throws NotLoggedInException If not logged in. * @throws NotConnectedException */ public void reload() throws NotLoggedInException, NotConnectedException{ final XMPPConnection connection = connection(); if (!connection.isAuthenticated()) { throw new NotLoggedInException(); } if (connection.isAnonymous()) { throw new IllegalStateException("Anonymous users can't have a roster."); } RosterPacket packet = new RosterPacket(); if (rosterStore != null && isRosterVersioningSupported()) { packet.setVersion(rosterStore.getRosterVersion()); } rosterState = RosterState.loading; connection.sendIqWithResponseCallback(packet, new RosterResultListener(), new ExceptionCallback() { @Override public void processException(Exception exception) { rosterState = RosterState.uninitialized; LOGGER.log(Level.SEVERE, "Exception reloading roster" , exception); } }); }
/** * Removes a roster entry from the roster. The roster entry will also be removed from the * unfiled entries or from any roster group where it could belong and will no longer be part * of the roster. Note that this is a synchronous call -- Smack must wait for the server * to send an updated subscription status. * * @param entry a roster entry. * @throws XMPPErrorException if an XMPP error occurs. * @throws NotLoggedInException if not logged in. * @throws NoResponseException SmackException if there was no response from the server. * @throws NotConnectedException * @throws IllegalStateException if connection is not logged in or logged in anonymously */ public void removeEntry(RosterEntry entry) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException { final XMPPConnection connection = connection(); if (!connection.isAuthenticated()) { throw new NotLoggedInException(); } if (connection.isAnonymous()) { throw new IllegalStateException("Anonymous users can't have a roster."); } // Only remove the entry if it's in the entry list. // The actual removal logic takes place in RosterPacketListenerprocess>>Packet(Packet) if (!entries.containsKey(entry.getUser())) { return; } RosterPacket packet = new RosterPacket(); packet.setType(IQ.Type.set); RosterPacket.Item item = RosterEntry.toRosterItem(entry); // Set the item type as REMOVE so that the server will delete the entry item.setItemType(RosterPacket.ItemType.remove); packet.addRosterItem(item); connection.createPacketCollectorAndSend(packet).nextResultOrThrow(); }
/** * Removes a roster entry from this group. If the entry does not belong to any other group * then it will be considered as unfiled, therefore it will be added to the list of unfiled * entries. * Note that this is a synchronous call -- Smack must wait for the server * to receive the updated roster. * * @param entry a roster entry. * @throws XMPPErrorException if an error occurred while trying to remove the entry from the group. * @throws NoResponseException if there was no response from the server. * @throws NotConnectedException */ public void removeEntry(RosterEntry entry) throws NoResponseException, XMPPErrorException, NotConnectedException { // Only remove the entry if it's in the entry list. // Remove the entry locally, if we wait for RosterPacketListenerprocess>>Packet(Packet) // to take place the entry will exist in the group until a packet is received from the // server. synchronized (entries) { if (entries.contains(entry)) { RosterPacket packet = new RosterPacket(); packet.setType(IQ.Type.set); RosterPacket.Item item = RosterEntry.toRosterItem(entry); item.removeGroupName(this.getName()); packet.addRosterItem(item); // Wait up to a certain number of seconds for a reply from the server. connection().createPacketCollectorAndSend(packet).nextResultOrThrow(); } } }
@Override public List<Item> getEntries() { List<Item> entries = new ArrayList<RosterPacket.Item>(); for (File file : fileDir.listFiles(rosterDirFilter)) { Item entry = readEntry(file); if (entry == null) { log("Roster store file '" + file + "' is invalid."); } else { entries.add(entry); } } return entries; }
/** * Remove all roster entries by iterating trough {@link Roster#getEntries()} * and simulating receiving roster pushes from the server. * * @param connection the dummy connection of which the provided roster belongs to. * @param roster the roster (or buddy list) which should be initialized. */ public static void removeAllRosterEntries(DummyConnection connection, Roster roster) throws InterruptedException, XMPPException { for(RosterEntry entry : roster.getEntries()) { // prepare the roster push packet final RosterPacket rosterPush= new RosterPacket(); rosterPush.setType(Type.set); rosterPush.setTo(connection.getUser()); // prepare the buddy's item entry which should be removed final RosterPacket.Item item = new RosterPacket.Item(entry.getUser(), entry.getName()); item.setItemType(ItemType.remove); rosterPush.addRosterItem(item); // simulate receiving the roster push connection.processPacket(rosterPush); } }
private RosterPacket.Item fromCursor(Cursor c) throws XmppStringprepException { String user = c.getString(0); String name = c.getString(1); RosterPacket.Item item = new RosterPacket.Item(JidCreate.bareFrom(user), name); String type = c.getString(2); if (type == null) type = RosterPacket.ItemType.none.toString(); item.setItemType(RosterPacket.ItemType.valueOf(type)); boolean pending = c.getInt(3) != 0; item.setSubscriptionPending(pending); boolean approved = c.getInt(4) != 0; item.setApproved(approved); String groups = c.getString(5); if (groups != null) { StringTokenizer tokenizer = new StringTokenizer(groups, ","); while (tokenizer.hasMoreTokens()) { item.addGroupName(tokenizer.nextToken()); } } return item; }
/** * Creates a new roster entry. * * @param user the user. * @param name the nickname for the entry. * @param type the subscription type. * @param status the subscription status (related to subscriptions pending to be approbed). * @param connection a connection to the XMPP server. */ RosterEntry(String user, String name, RosterPacket.ItemType type, RosterPacket.ItemStatus status, Roster roster, XMPPConnection connection) { super(connection); this.user = user; this.name = name; this.type = type; this.status = status; this.roster = roster; }
private static RosterPacket.Item toRosterItem(RosterEntry entry, String name) { RosterPacket.Item item = new RosterPacket.Item(entry.getUser(), name); item.setItemType(entry.getType()); item.setItemStatus(entry.getStatus()); // Set the correct group names for the item. for (RosterGroup group : entry.getGroups()) { item.addGroupName(group.getName()); } return item; }
/** * Ignore ItemTypes as of RFC 6121, 2.1.2.5. * * This is used by {@link RosterPushListener} and {@link RosterResultListener}. * */ private static boolean hasValidSubscriptionType(RosterPacket.Item item) { switch (item.getItemType()) { case none: case from: case to: case both: return true; default: return false; } }
/** * Sets the name of the group. Changing the group's name is like moving all the group entries * of the group to a new group specified by the new name. Since this group won't have entries * it will be removed from the roster. This means that all the references to this object will * be invalid and will need to be updated to the new group specified by the new name. * * @param name the name of the group. * @throws NotConnectedException * @throws XMPPErrorException * @throws NoResponseException */ public void setName(String name) throws NotConnectedException, NoResponseException, XMPPErrorException { synchronized (entries) { for (RosterEntry entry : entries) { RosterPacket packet = new RosterPacket(); packet.setType(IQ.Type.set); RosterPacket.Item item = RosterEntry.toRosterItem(entry); item.removeGroupName(this.name); item.addGroupName(name); packet.addRosterItem(item); connection().createPacketCollectorAndSend(packet).nextResultOrThrow(); } } }
/** * Adds a roster entry to this group. If the entry was unfiled then it will be removed from * the unfiled list and will be added to this group. * Note that this is a synchronous call -- Smack must wait for the server * to receive the updated roster. * * @param entry a roster entry. * @throws XMPPErrorException if an error occured while trying to add the entry to the group. * @throws NoResponseException if there was no response from the server. * @throws NotConnectedException */ public void addEntry(RosterEntry entry) throws NoResponseException, XMPPErrorException, NotConnectedException { // Only add the entry if it isn't already in the list. synchronized (entries) { if (!entries.contains(entry)) { RosterPacket packet = new RosterPacket(); packet.setType(IQ.Type.set); RosterPacket.Item item = RosterEntry.toRosterItem(entry); item.addGroupName(getName()); packet.addRosterItem(item); // Wait up to a certain number of seconds for a reply from the server. connection().createPacketCollectorAndSend(packet).nextResultOrThrow(); } } }
public void run() { try { while (true) { final Stanza packet = connection.getSentPacket(); if (packet instanceof RosterPacket && ((IQ) packet).getType() == Type.set) { final RosterPacket rosterRequest = (RosterPacket) packet; // Prepare and process the roster push final RosterPacket rosterPush = new RosterPacket(); final Item item = rosterRequest.getRosterItems().iterator().next(); if (item.getItemType() != ItemType.remove) { item.setItemType(ItemType.none); } rosterPush.setType(Type.set); rosterPush.setTo(connection.getUser()); rosterPush.addRosterItem(item); connection.processPacket(rosterPush); // Create and process the IQ response final IQ response = IQ.createResultIQ(rosterRequest); connection.processPacket(response); // Verify the roster update request assertSame("A roster set MUST contain one and only one <item/> element.", 1, rosterRequest.getRosterItemCount()); verifyUpdateRequest(rosterRequest); break; } } } catch (Throwable e) { exception = e; fail(e.getMessage()); } }
/** * Tests that a non-empty roster result empties the store. * @throws SmackException * @throws XMPPException */ @Test(timeout = 5000) public void testOtherVersionStored() throws InterruptedException, XMPPException, SmackException { Item vaglafItem = vaglafItem(); // We expect that the roster request is the only packet sent. This is not part of the specification, // but a shortcut in the test implementation. Stanza sentPacket = connection.getSentPacket(); if (sentPacket instanceof RosterPacket) { RosterPacket sentRP = (RosterPacket)sentPacket; RosterPacket answer = new RosterPacket(); answer.setStanzaId(sentRP.getStanzaId()); answer.setType(Type.result); answer.setTo(sentRP.getFrom()); answer.setVersion("newVersion"); answer.addRosterItem(vaglafItem); rosterListener.reset(); connection.processPacket(answer); rosterListener.waitUntilInvocationOrTimeout(); } else { assertTrue("Expected to get a RosterPacket ", false); } Roster roster = Roster.getInstanceFor(connection); assertEquals("Size of roster", 1, roster.getEntries().size()); RosterEntry entry = roster.getEntry(vaglafItem.getUser()); assertNotNull("Roster contains vaglaf entry", entry); assertEquals("vaglaf entry in roster equals the sent entry", vaglafItem, RosterEntry.toRosterItem(entry)); RosterStore store = roster.getRosterStore(); assertEquals("Size of store", 1, store.getEntries().size()); Item item = store.getEntry(vaglafItem.getUser()); assertNotNull("Store contains vaglaf entry"); assertEquals("vaglaf entry in store equals the sent entry", vaglafItem, item); }
private void answerWithEmptyRosterResult() throws InterruptedException { // We expect that the roster request is the only packet sent. This is not part of the specification, // but a shortcut in the test implementation. Stanza sentPacket = connection.getSentPacket(); if (sentPacket instanceof RosterPacket) { final IQ emptyIQ = IQ.createResultIQ((RosterPacket)sentPacket); connection.processPacket(emptyIQ); } else { assertTrue("Expected to get a RosterPacket ", false); } }
private SQLiteStatement prepareInsert(SQLiteDatabase db, RosterPacket.Item item) { if (mInsertStatement == null) { mInsertStatement = db.compileStatement("INSERT OR REPLACE INTO " + TABLE_ROSTER + " VALUES(?, ?, ?, ?, ?, ?)"); } else { mInsertStatement.clearBindings(); } int i = 0; mInsertStatement.bindString(++i, item.getJid().toString()); mInsertStatement.bindString(++i, item.getName()); mInsertStatement.bindString(++i, item.getItemType() != null ? item.getItemType().toString() : RosterPacket.ItemType.none.toString()); mInsertStatement.bindLong(++i, item.isSubscriptionPending() ? 1 : 0); mInsertStatement.bindLong(++i, item.isApproved() ? 1 : 0); Set<String> groups = item.getGroupNames(); if (groups != null) { mInsertStatement.bindString(++i, TextUtils.join(",", groups)); } else { mInsertStatement.bindNull(++i); } return mInsertStatement; }
private boolean addEntry(SQLiteDatabase db, RosterPacket.Item item) { synchronized (mInsertLock) { try { SQLiteStatement stm = prepareInsert(db, item); stm.executeInsert(); } catch (SQLiteException e) { return false; } // insert was successful return true; } }
@Override public boolean resetEntries(Collection<RosterPacket.Item> items, String version) { SQLiteDatabase db = getWritableDatabase(); beginTransaction(db); boolean success = false; try { db.execSQL("DELETE FROM " + TABLE_ROSTER); for (RosterPacket.Item item : items) { addEntry(db, item); } success = setTransactionSuccessful(db); if (success) { setRosterVersion(version); } } catch (SQLiteException e) { return false; } finally { endTransaction(db, success); } return false; }
@CommandHandler(name = ACTION_ROSTER) private boolean handleRoster(Intent intent, boolean canConnect) { if (canConnect && isConnected()) { Stanza iq = new RosterPacket(); String id = intent.getStringExtra(EXTRA_PACKET_ID); iq.setStanzaId(id); // iq default type is get sendPacket(iq); } return false; }
@CommandHandler(name = ACTION_ROSTER_STATUS) private boolean handleRosterStatus(Intent intent) { if (mRosterStore != null) { final String to = intent.getStringExtra(EXTRA_TO); RosterPacket.Item entry; try { entry = mRosterStore.getEntry(JidCreate.from(to)); } catch (XmppStringprepException e) { Log.w(TAG, "error parsing JID: " + e.getCausingString(), e); // report it because it's a big deal ReportingManager.logException(e); return false; } if (entry != null) { final String id = intent.getStringExtra(EXTRA_PACKET_ID); Intent i = new Intent(ACTION_ROSTER_STATUS); i.putExtra(EXTRA_FROM, to); i.putExtra(EXTRA_ROSTER_NAME, entry.getName()); RosterPacket.ItemType subscriptionType = entry.getItemType(); i.putExtra(EXTRA_SUBSCRIBED_FROM, subscriptionType == RosterPacket.ItemType.both || subscriptionType == RosterPacket.ItemType.from); i.putExtra(EXTRA_SUBSCRIBED_TO, subscriptionType == RosterPacket.ItemType.both || subscriptionType == RosterPacket.ItemType.to); // to keep track of request-reply i.putExtra(EXTRA_PACKET_ID, id); mLocalBroadcastManager.sendBroadcast(i); } } return false; }
private boolean isInvited(int userId) { QBRosterEntry rosterEntry = roster.getEntry(userId); if (rosterEntry == null) { return false; } boolean isSubscribedToUser = rosterEntry.getType() == RosterPacket.ItemType.from; boolean isBothSubscribed = rosterEntry.getType() == RosterPacket.ItemType.both; return isSubscribedToUser || isBothSubscribed; }
private static Contact.Subscription rosterToModelSubscription( boolean subscriptionPending, RosterPacket.ItemType type) { if (type == RosterPacket.ItemType.both || type == RosterPacket.ItemType.to || type == RosterPacket.ItemType.remove) return Contact.Subscription.SUBSCRIBED; if (subscriptionPending) return Contact.Subscription.PENDING; return Contact.Subscription.UNSUBSCRIBED; }
static RosterPacket.Item toRosterItem(RosterEntry entry) { return toRosterItem(entry, entry.getName()); }
private RosterPushListener() { super(RosterPacket.ELEMENT, RosterPacket.NAMESPACE, Type.set, Mode.sync); }
@Override public IQ handleIQRequest(IQ iqRequest) { final XMPPConnection connection = connection(); RosterPacket rosterPacket = (RosterPacket) iqRequest; // Roster push (RFC 6121, 2.1.6) // A roster push with a non-empty from not matching our address MUST be ignored String jid = XmppStringUtils.parseBareJid(connection.getUser()); String from = rosterPacket.getFrom(); if (from != null && !from.equals(jid)) { LOGGER.warning("Ignoring roster push with a non matching 'from' ourJid='" + jid + "' from='" + from + "'"); return IQ.createErrorResponse(iqRequest, new XMPPError(Condition.service_unavailable)); } // A roster push must contain exactly one entry Collection<Item> items = rosterPacket.getRosterItems(); if (items.size() != 1) { LOGGER.warning("Ignoring roster push with not exaclty one entry. size=" + items.size()); return IQ.createErrorResponse(iqRequest, new XMPPError(Condition.bad_request)); } Collection<String> addedEntries = new ArrayList<String>(); Collection<String> updatedEntries = new ArrayList<String>(); Collection<String> deletedEntries = new ArrayList<String>(); Collection<String> unchangedEntries = new ArrayList<String>(); // We assured above that the size of items is exaclty 1, therefore we are able to // safely retrieve this single item here. Item item = items.iterator().next(); RosterEntry entry = new RosterEntry(item.getUser(), item.getName(), item.getItemType(), item.getItemStatus(), Roster.this, connection); String version = rosterPacket.getVersion(); if (item.getItemType().equals(RosterPacket.ItemType.remove)) { deleteEntry(deletedEntries, entry); if (rosterStore != null) { rosterStore.removeEntry(entry.getUser(), version); } } else if (hasValidSubscriptionType(item)) { addUpdateEntry(addedEntries, updatedEntries, unchangedEntries, item, entry); if (rosterStore != null) { rosterStore.addEntry(item, version); } } removeEmptyGroups(); // Fire event for roster listeners. fireRosterChangedEvent(addedEntries, updatedEntries, deletedEntries); return IQ.createResultIQ(rosterPacket); }
@Override public RosterPacket parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException { RosterPacket roster = new RosterPacket(); RosterPacket.Item item = null; String version = parser.getAttributeValue("", "ver"); roster.setVersion(version); outerloop: while (true) { int eventType = parser.next(); switch(eventType) { case XmlPullParser.START_TAG: String startTag = parser.getName(); switch (startTag) { case "item": String jid = parser.getAttributeValue("", "jid"); String name = parser.getAttributeValue("", "name"); // Create packet. item = new RosterPacket.Item(jid, name); // Set status. String ask = parser.getAttributeValue("", "ask"); RosterPacket.ItemStatus status = RosterPacket.ItemStatus.fromString(ask); item.setItemStatus(status); // Set type. String subscription = parser.getAttributeValue("", "subscription"); RosterPacket.ItemType type = RosterPacket.ItemType.valueOf(subscription != null ? subscription : "none"); item.setItemType(type); break; case "group": // TODO item!= null final String groupName = parser.nextText(); if (groupName != null && groupName.trim().length() > 0) { item.addGroupName(groupName); } break; } break; case XmlPullParser.END_TAG: String endTag = parser.getName(); switch(endTag) { case "item": roster.addRosterItem(item); break; case "query": break outerloop; } } } return roster; }
@Override public DataForm parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException { DataForm.Type dataFormType = DataForm.Type.fromString(parser.getAttributeValue("", "type")); DataForm dataForm = new DataForm(dataFormType); outerloop: while (true) { int eventType = parser.next(); switch (eventType) { case XmlPullParser.START_TAG: String name = parser.getName(); String namespace = parser.getNamespace(); switch (name) { case "instructions": dataForm.addInstruction(parser.nextText()); break; case "title": dataForm.setTitle(parser.nextText()); break; case "field": dataForm.addField(parseField(parser)); break; case "item": dataForm.addItem(parseItem(parser)); break; case "reported": dataForm.setReportedData(parseReported(parser)); break; // See XEP-133 Example 32 for a corner case where the data form contains this extension. case RosterPacket.ELEMENT: if (namespace.equals(RosterPacket.NAMESPACE)) { dataForm.addExtensionElement(RosterPacketProvider.INSTANCE.parse(parser)); } break; // See XEP-141 Data Forms Layout case DataLayout.ELEMENT: if (namespace.equals(DataLayout.NAMESPACE)) { dataForm.addExtensionElement(DataLayoutProvider.parse(parser)); } break; } break; case XmlPullParser.END_TAG: if (parser.getDepth() == initialDepth) { break outerloop; } break; } } return dataForm; }
@Override public boolean addEntry(RosterPacket.Item item, String version) { SQLiteDatabase db = getWritableDatabase(); return addEntry(db, item) && setRosterVersion(version); }
private boolean isRosterEntrySubscribed(RosterEntry entry) { return (entry != null && (entry.getType() == RosterPacket.ItemType.to || entry.getType() == RosterPacket.ItemType.both) && !entry.isSubscriptionPending()); }
private boolean isAlreadyTrusted(Presence p) { RosterEntry entry = getRosterEntry(p.getFrom()); return (entry != null && (entry.getType() == RosterPacket.ItemType.to || entry.getType() == RosterPacket.ItemType.both)); }
public static Intent createIntent(Context ctx, Presence p, RosterEntry entry) { Intent i = new Intent(ACTION_PRESENCE); Presence.Type type = p.getType(); i.putExtra(EXTRA_TYPE, type != null ? type.name() : Presence.Type.available.name()); i.putExtra(EXTRA_PACKET_ID, p.getStanzaId()); i.putExtra(EXTRA_FROM, StringUtils.maybeToString(p.getFrom().toString())); i.putExtra(EXTRA_TO, StringUtils.maybeToString(p.getTo())); i.putExtra(EXTRA_STATUS, p.getStatus()); Presence.Mode mode = p.getMode(); i.putExtra(EXTRA_SHOW, mode != null ? mode.name() : Presence.Mode.available.name()); i.putExtra(EXTRA_PRIORITY, p.getPriority()); String jid = p.getFrom().asBareJid().toString(); long timestamp; DelayInformation delay = p.getExtension(DelayInformation.ELEMENT, DelayInformation.NAMESPACE); if (delay != null) { timestamp = delay.getStamp().getTime(); } else { // try last seen from database timestamp = UsersProvider.getLastSeen(ctx, jid); if (timestamp < 0) timestamp = System.currentTimeMillis(); } i.putExtra(EXTRA_STAMP, timestamp); // public key fingerprint String fingerprint = PublicKeyPresence.getFingerprint(p); if (fingerprint == null) { // try untrusted fingerprint from database fingerprint = Keyring.getFingerprint(ctx, jid, MyUsers.Keys.TRUST_UNKNOWN); } i.putExtra(EXTRA_FINGERPRINT, fingerprint); // subscription information if (entry != null) { i.putExtra(EXTRA_ROSTER_NAME, entry.getName()); RosterPacket.ItemType subscriptionType = entry.getType(); i.putExtra(EXTRA_SUBSCRIBED_FROM, subscriptionType == RosterPacket.ItemType.both || subscriptionType == RosterPacket.ItemType.from); i.putExtra(EXTRA_SUBSCRIBED_TO, subscriptionType == RosterPacket.ItemType.both || subscriptionType == RosterPacket.ItemType.to); } return i; }
public static boolean isOutgoingFriend(QBRosterEntry rosterEntry) { return RosterPacket.ItemStatus.subscribe.equals(rosterEntry.getStatus()); }