mirror of https://github.com/sipwise/jitsi.git
parent
dd8a7f9e96
commit
19ad20f4a1
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
/**
|
||||
* This exception is thrown if the protocol implementation is broken, i.e. when
|
||||
* the responses do not contain the expected results and consequently cannot be
|
||||
* parsed or resolve the situation properly.
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class BrokenFacebookProtocolException
|
||||
extends Exception
|
||||
{
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
public BrokenFacebookProtocolException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BrokenFacebookProtocolException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BrokenFacebookProtocolException(Throwable cause)
|
||||
{
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@ -1,479 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import javax.imageio.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* A simple, straightforward implementation of a facebook Contact. the contact
|
||||
* implementation encapsulate contact objects from the protocol stack and group
|
||||
* property values are returned after consulting the encapsulated object.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class ContactFacebookImpl
|
||||
implements Contact
|
||||
{
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(ContactFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* The facebook contact.
|
||||
*/
|
||||
private FacebookUser contactMetaInfo = null;
|
||||
|
||||
/**
|
||||
* The UNIQUE identifier of this contact, it should be the facebook user
|
||||
* number.
|
||||
*/
|
||||
private final String contactID;
|
||||
|
||||
/**
|
||||
* The facebook profile image
|
||||
*/
|
||||
private byte[] image = null;
|
||||
|
||||
/**
|
||||
* The provider that created us.
|
||||
*/
|
||||
private final ProtocolProviderServiceFacebookImpl parentProvider;
|
||||
|
||||
/**
|
||||
* The group that belong to.
|
||||
*/
|
||||
private ContactGroupFacebookImpl parentGroup = null;
|
||||
|
||||
/**
|
||||
* The presence status of the contact.
|
||||
*/
|
||||
private PresenceStatus presenceStatus = FacebookStatusEnum.ONLINE;
|
||||
|
||||
/**
|
||||
* Determines whether this contact is persistent, i.e. member of the contact
|
||||
* list or whether it is here only temporarily.
|
||||
*/
|
||||
private boolean isPersistent = true;
|
||||
|
||||
/**
|
||||
* Determines whether the contact has been resolved (i.e. we have a
|
||||
* confirmation that it is still on the server contact list).
|
||||
*/
|
||||
private boolean isResolved = true;
|
||||
|
||||
/**
|
||||
* Creates an instance of a meta contact with the specified string used as a
|
||||
* name and identifier.
|
||||
*
|
||||
* @param contactID the identifier of this contact (also used as a name).
|
||||
* @param parentProvider the provider that created us.
|
||||
*/
|
||||
public ContactFacebookImpl(
|
||||
String contactID,
|
||||
ProtocolProviderServiceFacebookImpl parentProvider)
|
||||
{
|
||||
this.contactID = contactID;
|
||||
this.parentProvider = parentProvider;
|
||||
|
||||
updateContactInfo();
|
||||
|
||||
logger.trace("init contactID: " + contactID);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only called when the contact is added to a new
|
||||
* <tt>ContactGroupFacebookImpl</tt> by the
|
||||
* <tt>ContactGroupFacebookImpl</tt> itself.
|
||||
*
|
||||
* @param newParentGroup the <tt>ContactGroupFacebookImpl</tt> that is now
|
||||
* parent of this <tt>ContactFacebookImpl</tt>
|
||||
*/
|
||||
void setParentGroup(ContactGroupFacebookImpl newParentGroup)
|
||||
{
|
||||
this.parentGroup = newParentGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String that can be used for uniquely identifying the contact.
|
||||
* It should be the unique number of the facebook user.
|
||||
*
|
||||
* @return a String id representing and uniquely identifying the contact.
|
||||
*/
|
||||
public String getAddress()
|
||||
{
|
||||
return contactID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String that could be used by any user interacting modules for
|
||||
* referring to this contact.
|
||||
*
|
||||
* @return a String that can be used for referring to this contact when
|
||||
* interacting with the user.
|
||||
* FIXME should be facebook "name"
|
||||
*/
|
||||
public String getDisplayName()
|
||||
{
|
||||
updateContactInfo();
|
||||
if (contactMetaInfo != null)
|
||||
return contactMetaInfo.name;
|
||||
else
|
||||
return contactID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user information we just got from the server. so this data
|
||||
* shouldn't be modified manually.
|
||||
*
|
||||
* @param newInfo
|
||||
*/
|
||||
public void setContactInfo(FacebookUser newInfo)
|
||||
{
|
||||
this.contactMetaInfo = newInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update contact info from the cache.
|
||||
*/
|
||||
private void updateContactInfo()
|
||||
{
|
||||
FacebookUser newInfo = parentProvider.getContactMetaInfoByID(contactID);
|
||||
|
||||
if(newInfo != null)
|
||||
this.contactMetaInfo = newInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* update the user information we just got from the server. so this data
|
||||
* shouldn't be modified manually.
|
||||
*/
|
||||
public FacebookUser getContactInfo()
|
||||
{
|
||||
return this.contactMetaInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array containing an image (most often a photo or an
|
||||
* avatar) that the contact uses as a representation.
|
||||
*
|
||||
* @return byte[] an image representing the contact.
|
||||
*/
|
||||
public byte[] getImage()
|
||||
{
|
||||
logger.trace("getImage()");
|
||||
|
||||
// TODO handle java.lang.NullPointerException
|
||||
/*
|
||||
* if(image == null) ssclCallback.addContactForImageUpdate(this);
|
||||
*/
|
||||
// if we've already gotten the avatar of our buddy,
|
||||
// just return it
|
||||
if (image != null)
|
||||
return image;
|
||||
|
||||
updateContactInfo();
|
||||
// if we have not gotten the information of this buddy,
|
||||
// return the default avatar
|
||||
if (contactMetaInfo == null)
|
||||
return null;
|
||||
|
||||
// if we get here, the contact is not null but the image is null.
|
||||
// That means we've gotten the information of this buddy,
|
||||
// but we still havn't fetch the avatar from the server,
|
||||
// so we do it now
|
||||
logger.trace("fetch the avatar from the server");
|
||||
ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
|
||||
try
|
||||
{
|
||||
BufferedImage newAvatar
|
||||
= ImageIO.read(new URL(contactMetaInfo.thumbSrc));
|
||||
|
||||
javax.imageio.ImageIO.write(newAvatar, "PNG", byteArrayOS);
|
||||
image = byteArrayOS.toByteArray();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn("IOException occured when loading avatar", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
byteArrayOS.close();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
logger
|
||||
.warn("Failed to close avatar ByteArrayOutputStream", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* get big version avatar.<br>
|
||||
* small avatar at http://profile.ak.facebook.com/profile6/1845/74/q800753867_2878.jpg<br>
|
||||
* bigger avatar at http://profile.ak.facebook.com/profile6/1845/74/s800753867_2878.jpg<br>
|
||||
* biggest avatar at http://profile.ak.facebook.com/profile6/1845/74/n800753867_2878.jpg<br>
|
||||
* default avatar at http://static.ak.fbcdn.net/pics/q_silhouette.gif<br>
|
||||
* default bigger avatar at http://static.ak.fbcdn.net/pics/d_silhouette.gif
|
||||
* @return byte[] an big image representing the contact.
|
||||
*/
|
||||
public byte[] getBigImage(){
|
||||
logger.trace("getBigImage()");
|
||||
updateContactInfo();
|
||||
// if we have not gotten the information of this buddy,
|
||||
// return the default avatar
|
||||
if (contactMetaInfo == null || contactMetaInfo.thumbSrc == null)
|
||||
return null;
|
||||
|
||||
String avatarSrcStr;
|
||||
|
||||
if(!contactMetaInfo.thumbSrc.equalsIgnoreCase(FacebookUser.defaultThumbSrc))
|
||||
{
|
||||
StringBuffer avatarSrcStrBuff = new StringBuffer(contactMetaInfo.thumbSrc);
|
||||
|
||||
int tempPos = avatarSrcStrBuff.lastIndexOf("/q");
|
||||
if(tempPos > 0)
|
||||
avatarSrcStrBuff.replace(tempPos, tempPos + 2, "/n");
|
||||
|
||||
avatarSrcStr = avatarSrcStrBuff.toString();
|
||||
}
|
||||
else
|
||||
avatarSrcStr = FacebookUser.defaultAvatarSrc;
|
||||
|
||||
logger.trace("fetch the avatar from the server");
|
||||
ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
|
||||
try
|
||||
{
|
||||
BufferedImage newAvatar = ImageIO.read(new URL(avatarSrcStr));
|
||||
|
||||
javax.imageio.ImageIO.write(newAvatar, "PNG", byteArrayOS);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn("IOException occured when loading avatar", e);
|
||||
// OK, we use the defaultAvatar temporarily
|
||||
return getImage();
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
byteArrayOS.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
byte[] bigImage = byteArrayOS.toByteArray();
|
||||
// failed to get the avatar
|
||||
if (bigImage == null)
|
||||
return getImage();
|
||||
|
||||
return bigImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to set the image of the contact if it is updated
|
||||
*
|
||||
* @param newImage a photo/avatar associated with this contact
|
||||
*/
|
||||
public void setImage(byte[] newImage)
|
||||
{
|
||||
this.image = newImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the contact.
|
||||
*
|
||||
* @return presence status,(online or offline)
|
||||
*/
|
||||
public PresenceStatus getPresenceStatus()
|
||||
{
|
||||
return this.presenceStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets <tt>facebookPresenceStatus</tt> as the PresenceStatus that this
|
||||
* contact is currently in.
|
||||
*
|
||||
* @param facebookPresenceStatus the <tt>FacebookPresenceStatus</tt>
|
||||
* currently valid for this contact.
|
||||
*/
|
||||
public void setPresenceStatus(PresenceStatus facebookPresenceStatus)
|
||||
{
|
||||
this.presenceStatus = facebookPresenceStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the protocol provider that created the contact.
|
||||
*
|
||||
* @return a refererence to an instance of the ProtocolProviderService
|
||||
*/
|
||||
public ProtocolProviderService getProtocolProvider()
|
||||
{
|
||||
return parentProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not this contact represents our own identity.
|
||||
*
|
||||
* @return true in case this is a contact that represents ourselves and
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean isLocal()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group that contains this contact.
|
||||
*
|
||||
* @return a reference to the <tt>ContactGroupFacebookImpl</tt> that
|
||||
* contains this contact.
|
||||
*/
|
||||
public ContactGroup getParentContactGroup()
|
||||
{
|
||||
return this.parentGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this contact, containing most of its
|
||||
* representative details.
|
||||
*
|
||||
* @return a string representation of this contact.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buff
|
||||
= new StringBuffer("ContactFacebookImpl[ DisplayName=")
|
||||
.append(getDisplayName())
|
||||
.append("]");
|
||||
|
||||
return buff.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not this contact is being stored by the server. Non
|
||||
* persistent contacts are common in the case of simple, non-persistent
|
||||
* presence operation sets. They could however also be seen in persistent
|
||||
* presence operation sets when for example we have received an event from
|
||||
* someone not on our contact list. Non persistent contacts are volatile
|
||||
* even when coming from a persistent presence op. set. They would only
|
||||
* exist until the application is closed and will not be there next time it
|
||||
* is loaded.
|
||||
*
|
||||
* @return true if the contact is persistent and false otherwise.
|
||||
*/
|
||||
public boolean isPersistent()
|
||||
{
|
||||
return isPersistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether or not this contact is being stored by the server. Non
|
||||
* persistent contacts are common in the case of simple, non-persistent
|
||||
* presence operation sets. They could however also be seen in persistent
|
||||
* presence operation sets when for example we have received an event from
|
||||
* someone not on our contact list. Non persistent contacts are volatile
|
||||
* even when coming from a persistent presence op. set. They would only
|
||||
* exist until the application is closed and will not be there next time it
|
||||
* is loaded.
|
||||
*
|
||||
* @param isPersistent true if the contact is persistent and false
|
||||
* otherwise.
|
||||
*/
|
||||
public void setPersistent(boolean isPersistent)
|
||||
{
|
||||
this.isPersistent = isPersistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null as no persistent data is required and the contact address is
|
||||
* sufficient for restoring the contact.
|
||||
* <p>
|
||||
*
|
||||
* @return null as no such data is needed.
|
||||
*/
|
||||
public String getPersistentData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not this contact has been resolved against the
|
||||
* server. Unresolved contacts are used when initially loading a contact
|
||||
* list that has been stored in a local file until the presence operation
|
||||
* set has managed to retrieve all the contact list from the server and has
|
||||
* properly mapped contacts to their on-line buddies.
|
||||
*
|
||||
* @return true if the contact has been resolved (mapped against a buddy)
|
||||
* and false otherwise.
|
||||
*/
|
||||
public boolean isResolved()
|
||||
{
|
||||
return isResolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current status message of this contact.
|
||||
*
|
||||
* @return null as the protocol has currently no support of status messages
|
||||
*/
|
||||
public String getStatusMessage()
|
||||
{
|
||||
updateContactInfo();
|
||||
if ((contactMetaInfo != null)
|
||||
&& (contactMetaInfo.status != null)
|
||||
&& !contactMetaInfo.status.trim().equals(""))
|
||||
return
|
||||
contactMetaInfo.status
|
||||
+ "[" + contactMetaInfo.statusTimeRel + "]";
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the contact resolved or unresolved.
|
||||
*
|
||||
* @param resolved true to make the contact resolved; false to make it
|
||||
* unresolved
|
||||
*/
|
||||
public void setResolved(boolean resolved)
|
||||
{
|
||||
this.isResolved = resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether some other object is "equal to" this one which in terms
|
||||
* of contacts translates to having equal ids. The resolved status of the
|
||||
* contacts deliberately ignored so that contacts would be declared equal
|
||||
* even if it differs.
|
||||
* <p>
|
||||
*
|
||||
* @param obj the reference object with which to compare.
|
||||
* @return <code>true</code> if this contact has the same id as that of
|
||||
* the <code>obj</code> argument.
|
||||
*/
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null || !(obj instanceof ContactFacebookImpl))
|
||||
return false;
|
||||
|
||||
ContactFacebookImpl facebookContact = (ContactFacebookImpl) obj;
|
||||
|
||||
return this.getAddress().equals(facebookContact.getAddress());
|
||||
}
|
||||
}
|
||||
@ -1,613 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* A simple, straightforward implementation of a facebook ContactGroup. We
|
||||
* simply store all group details in class fields. You should know that when
|
||||
* implementing a real protocol, the contact group implementation would rather
|
||||
* encapsulate group objects from the protocol stack and group property values
|
||||
* should be returned by consulting the encapsulated object.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class ContactGroupFacebookImpl
|
||||
implements ContactGroup
|
||||
{
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(ContactGroupFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* The name of this Facebook contact group.
|
||||
*/
|
||||
private String groupName = null;
|
||||
|
||||
/**
|
||||
* The list of this group's members.
|
||||
*/
|
||||
private List<Contact> contacts = new Vector<Contact>();
|
||||
|
||||
/**
|
||||
* The list of sub groups belonging to this group.
|
||||
*/
|
||||
private List<ContactGroup> subGroups = new Vector<ContactGroup>();
|
||||
|
||||
/**
|
||||
* The group that this group belongs to (or null if this is the root group).
|
||||
*/
|
||||
private ContactGroupFacebookImpl parentGroup = null;
|
||||
|
||||
/**
|
||||
* Determines whether this group is really in the contact list or whether
|
||||
* it is here only temporarily and will be gone next time we restart.
|
||||
*/
|
||||
private boolean isPersistent = true;
|
||||
|
||||
/**
|
||||
* The protocol provider that created us.
|
||||
*/
|
||||
private ProtocolProviderServiceFacebookImpl parentProvider = null;
|
||||
|
||||
/**
|
||||
* Determines whether this group has been resolved on the server.
|
||||
* Unresolved groups are groups that were available on previous runs and
|
||||
* that the meta contact list has stored. During all next runs, when
|
||||
* bootstrapping, the meta contact list would create these groups as
|
||||
* unresolved. Once a protocol provider implementation confirms that the
|
||||
* groups are still on the server, it would issue an event indicating that
|
||||
* the groups are now resolved.
|
||||
*/
|
||||
private boolean isResolved = true;
|
||||
|
||||
/**
|
||||
* An id uniquely identifying the group. For many protocols this could be
|
||||
* the group name itself.
|
||||
*/
|
||||
private String uid = null;
|
||||
private static final String UID_SUFFIX = ".uid";
|
||||
|
||||
/**
|
||||
* Creates a ContactGroupFacebookImpl with the specified name.
|
||||
*
|
||||
* @param groupName the name of the group.
|
||||
* @param parentProvider the protocol provider that created this group.
|
||||
*/
|
||||
public ContactGroupFacebookImpl(
|
||||
String groupName,
|
||||
ProtocolProviderServiceFacebookImpl parentProvider)
|
||||
{
|
||||
this.groupName = groupName;
|
||||
this.uid = groupName + UID_SUFFIX;
|
||||
this.parentProvider = parentProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the group may contain subgroups or not.
|
||||
*
|
||||
* @return always true in this implementation.
|
||||
*/
|
||||
public boolean canContainSubgroups()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the protocol provider that this group belongs to.
|
||||
* @return a regerence to the ProtocolProviderService instance that this
|
||||
* ContactGroup belongs to.
|
||||
*/
|
||||
public ProtocolProviderService getProtocolProvider()
|
||||
{
|
||||
return parentProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator over all contacts, member of this
|
||||
* <tt>ContactGroup</tt>.
|
||||
*
|
||||
* @return a java.util.Iterator over all contacts inside this
|
||||
* <tt>ContactGroup</tt>
|
||||
*/
|
||||
public Iterator<Contact> contacts()
|
||||
{
|
||||
return contacts.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified contact to this group.
|
||||
* @param contactToAdd the ContactFacebookImpl to add to this group.
|
||||
* @throws OperationFailedException
|
||||
*/
|
||||
public void addContact(ContactFacebookImpl contactToAdd) throws OperationFailedException
|
||||
{
|
||||
logger.trace("Adding contact " + contactToAdd.getDisplayName() + " to parent=" + this.toString());
|
||||
|
||||
//if the contact is already in the contact list and is not volatile,
|
||||
//then only broadcast an event
|
||||
ContactFacebookImpl existingContact = findContactByID(contactToAdd.getAddress());
|
||||
|
||||
if( existingContact != null)
|
||||
{
|
||||
logger.debug("Contact " + contactToAdd.getDisplayName() + " already exists.");
|
||||
// just update the information,(actually it means nothing)
|
||||
existingContact.setContactInfo(contactToAdd.getContactInfo());
|
||||
//this guy is our buddy
|
||||
existingContact.setPersistent(true);
|
||||
existingContact.setResolved(true);
|
||||
throw new OperationFailedException(
|
||||
"Contact " + contactToAdd.getDisplayName() + " already exists.",
|
||||
OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS);
|
||||
}
|
||||
//otherwise - we add it to the contact list
|
||||
this.contacts.add(contactToAdd);
|
||||
contactToAdd.setParentGroup(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of <tt>Contact</tt> members of this
|
||||
* <tt>ContactGroup</tt>
|
||||
*
|
||||
* @return an int indicating the number of <tt>Contact</tt>s, members of
|
||||
* this <tt>ContactGroup</tt>.
|
||||
*/
|
||||
public int countContacts()
|
||||
{
|
||||
return contacts.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of subgroups contained by this
|
||||
* <tt>ContactGroup</tt>.
|
||||
*
|
||||
* @return the number of subGroups currently added to this group.
|
||||
*/
|
||||
public int countSubgroups()
|
||||
{
|
||||
return subGroups.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified contact group to the contained by this group.
|
||||
* @param subgroup the ContactGroupFacebookImpl to add as a subgroup to this group.
|
||||
*/
|
||||
public void addSubgroup(ContactGroupFacebookImpl subgroup)
|
||||
{
|
||||
this.subGroups.add(subgroup);
|
||||
subgroup.setParentGroup(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the group that is the new parent of this group
|
||||
* @param parent ContactGroupFacebookImpl
|
||||
*/
|
||||
void setParentGroup(ContactGroupFacebookImpl parent)
|
||||
{
|
||||
this.parentGroup = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contact group that currently contains this group or null if
|
||||
* this is the root contact group.
|
||||
* @return the contact group that currently contains this group or null if
|
||||
* this is the root contact group.
|
||||
*/
|
||||
public ContactGroup getParentContactGroup()
|
||||
{
|
||||
return this.parentGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified contact group from the this group's subgroups.
|
||||
* @param subgroup the ContactGroupFacebookImpl subgroup to remove.
|
||||
*/
|
||||
public void removeSubGroup(ContactGroupFacebookImpl subgroup)
|
||||
{
|
||||
subgroup.setParentGroup(null);
|
||||
this.subGroups.remove(subgroup);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the <tt>Contact</tt> with the specified index.
|
||||
*
|
||||
* @param index the index of the <tt>Contact</tt> to return.
|
||||
* @return the <tt>Contact</tt> with the specified index.
|
||||
*/
|
||||
public Contact getContact(int index)
|
||||
{
|
||||
return (ContactFacebookImpl)contacts.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group that is parent of the specified facebookGroup or null
|
||||
* if no parent was found.
|
||||
* @param facebookGroup the group whose parent we're looking for.
|
||||
* @return the ContactGroupFacebookImpl instance that facebookGroup
|
||||
* belongs to or null if no parent was found.
|
||||
*/
|
||||
public ContactGroupFacebookImpl findGroupParent(
|
||||
ContactGroupFacebookImpl facebookGroup)
|
||||
{
|
||||
if ( subGroups.contains(facebookGroup) )
|
||||
return this;
|
||||
|
||||
Iterator<ContactGroup> subGroupsIter = subgroups();
|
||||
while (subGroupsIter.hasNext())
|
||||
{
|
||||
ContactGroupFacebookImpl subgroup
|
||||
= (ContactGroupFacebookImpl) subGroupsIter.next();
|
||||
|
||||
ContactGroupFacebookImpl parent
|
||||
= subgroup.findGroupParent(facebookGroup);
|
||||
|
||||
if(parent != null)
|
||||
return parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group that is parent of the specified facebookContact or
|
||||
* null if no parent was found.
|
||||
*
|
||||
* @param facebookContact the contact whose parent we're looking for.
|
||||
* @return the ContactGroupFacebookImpl instance that facebookContact
|
||||
* belongs to or <tt>null</tt> if no parent was found.
|
||||
*/
|
||||
public ContactGroupFacebookImpl findContactParent(
|
||||
ContactFacebookImpl facebookContact)
|
||||
{
|
||||
if ( contacts.contains(facebookContact) )
|
||||
return this;
|
||||
|
||||
Iterator<ContactGroup> subGroupsIter = subgroups();
|
||||
while (subGroupsIter.hasNext())
|
||||
{
|
||||
ContactGroupFacebookImpl subgroup
|
||||
= (ContactGroupFacebookImpl) subGroupsIter.next();
|
||||
|
||||
ContactGroupFacebookImpl parent
|
||||
= subgroup.findContactParent(facebookContact);
|
||||
|
||||
if(parent != null)
|
||||
return parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the <tt>Contact</tt> with the specified address or identifier.
|
||||
*
|
||||
* @param id the addres or identifier of the <tt>Contact</tt> we are
|
||||
* looking for.
|
||||
* @return the <tt>Contact</tt> with the specified id or address.
|
||||
*/
|
||||
public Contact getContact(String id)
|
||||
{
|
||||
Iterator<Contact> contactsIter = contacts();
|
||||
while (contactsIter.hasNext())
|
||||
{
|
||||
ContactFacebookImpl contact
|
||||
= (ContactFacebookImpl) contactsIter.next();
|
||||
if (contact.getAddress().equals(id))
|
||||
return contact;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subgroup with the specified index.
|
||||
*
|
||||
* @param index the index of the <tt>ContactGroup</tt> to retrieve.
|
||||
* @return the <tt>ContactGroup</tt> with the specified index.
|
||||
*/
|
||||
public ContactGroup getGroup(int index)
|
||||
{
|
||||
return subGroups.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subgroup with the specified name.
|
||||
*
|
||||
* @param groupName the name of the <tt>ContactGroup</tt> to retrieve.
|
||||
* @return the <tt>ContactGroup</tt> with the specified index.
|
||||
*/
|
||||
public ContactGroup getGroup(String groupName)
|
||||
{
|
||||
Iterator<ContactGroup> groupsIter = subgroups();
|
||||
while (groupsIter.hasNext())
|
||||
{
|
||||
ContactGroupFacebookImpl contactGroup
|
||||
= (ContactGroupFacebookImpl) groupsIter.next();
|
||||
if (contactGroup.getGroupName().equals(groupName))
|
||||
return contactGroup;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this group.
|
||||
*
|
||||
* @return a String containing the name of this group.
|
||||
*/
|
||||
public String getGroupName()
|
||||
{
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this group a new name.
|
||||
* @param newGrpName a String containing the new name of this group.
|
||||
*/
|
||||
public void setGroupName(String newGrpName)
|
||||
{
|
||||
this.groupName = newGrpName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the sub groups that this
|
||||
* <tt>ContactGroup</tt> contains.
|
||||
*
|
||||
* @return a java.util.Iterator over the <tt>ContactGroup</tt> children
|
||||
* of this group (i.e. subgroups).
|
||||
*/
|
||||
public Iterator<ContactGroup> subgroups()
|
||||
{
|
||||
return subGroups.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified contact from this group.
|
||||
* @param contact the ContactFacebookImpl to remove from this group
|
||||
*/
|
||||
public void removeContact(ContactFacebookImpl contact)
|
||||
{
|
||||
for(int i = this.contacts.size()-1; i >= 0; i--){
|
||||
if(((ContactFacebookImpl)this.contacts.get(i)).getAddress()
|
||||
.equals(contact.getAddress())){
|
||||
//set parent group to null
|
||||
((ContactFacebookImpl)this.contacts.get(i)).setParentGroup(null);
|
||||
this.contacts.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contact with the specified id or null if no such contact
|
||||
* exists.
|
||||
* @param id the id of the contact we're looking for.
|
||||
* @return ContactFacebookImpl
|
||||
*/
|
||||
public ContactFacebookImpl findContactByID(String id)
|
||||
{
|
||||
//first go through the contacts that are direct children.
|
||||
Iterator<Contact> contactsIter = contacts();
|
||||
|
||||
while(contactsIter.hasNext())
|
||||
{
|
||||
ContactFacebookImpl mContact = (ContactFacebookImpl)contactsIter.next();
|
||||
|
||||
if( mContact.getAddress().equals(id) )
|
||||
return mContact;
|
||||
}
|
||||
|
||||
//if we didn't find it here, let's try in the subougroups
|
||||
Iterator<ContactGroup> groupsIter = subgroups();
|
||||
|
||||
while( groupsIter.hasNext() )
|
||||
{
|
||||
ContactGroupFacebookImpl mGroup = (ContactGroupFacebookImpl)groupsIter.next();
|
||||
|
||||
ContactFacebookImpl mContact = mGroup.findContactByID(id);
|
||||
|
||||
if (mContact != null)
|
||||
return mContact;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a String representation of this group and the contacts it
|
||||
* contains (may turn out to be a relatively long string).
|
||||
* @return a String representing this group and its child contacts.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
|
||||
StringBuffer buff = new StringBuffer(getGroupName());
|
||||
buff.append(".subGroups=" + countSubgroups() + ":\n");
|
||||
|
||||
Iterator<ContactGroup> subGroups = subgroups();
|
||||
while (subGroups.hasNext())
|
||||
{
|
||||
ContactGroupFacebookImpl group = (ContactGroupFacebookImpl)subGroups.next();
|
||||
buff.append(group.toString());
|
||||
if (subGroups.hasNext())
|
||||
buff.append("\n");
|
||||
}
|
||||
|
||||
buff.append("\nChildContacts="+countContacts()+":[");
|
||||
|
||||
Iterator<Contact> contacts = contacts();
|
||||
while (contacts.hasNext())
|
||||
{
|
||||
ContactFacebookImpl contact = (ContactFacebookImpl) contacts.next();
|
||||
buff.append(contact.toString());
|
||||
if(contacts.hasNext())
|
||||
buff.append(", ");
|
||||
}
|
||||
return buff.append("]").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether or not this contact group is being stored by the server.
|
||||
* Non persistent contact groups are common in the case of simple,
|
||||
* non-persistent presence operation sets. They could however also be seen
|
||||
* in persistent presence operation sets when for example we have received
|
||||
* an event from someone not on our contact list and the contact that we
|
||||
* associated with that user is placed in a non persistent group. Non
|
||||
* persistent contact groups are volatile even when coming from a persistent
|
||||
* presence op. set. They would only exist until the application is closed
|
||||
* and will not be there next time it is loaded.
|
||||
*
|
||||
* @param isPersistent true if the contact group is to be persistent and
|
||||
* false otherwise.
|
||||
*/
|
||||
public void setPersistent(boolean isPersistent)
|
||||
{
|
||||
this.isPersistent = isPersistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not this contact group is being stored by the
|
||||
* server. Non persistent contact groups exist for the sole purpose of
|
||||
* containing non persistent contacts.
|
||||
* @return true if the contact group is persistent and false otherwise.
|
||||
*/
|
||||
public boolean isPersistent()
|
||||
{
|
||||
return isPersistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null as no persistent data is required and the contact address is
|
||||
* sufficient for restoring the contact.
|
||||
* <p>
|
||||
* @return null as no such data is needed.
|
||||
*/
|
||||
public String getPersistentData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not this contact has been resolved against the
|
||||
* server. Unresolved contacts are used when initially loading a contact
|
||||
* list that has been stored in a local file until the presence operation
|
||||
* set has managed to retrieve all the contact list from the server and has
|
||||
* properly mapped contacts to their on-line buddies.
|
||||
* @return true if the contact has been resolved (mapped against a buddy)
|
||||
* and false otherwise.
|
||||
*/
|
||||
public boolean isResolved()
|
||||
{
|
||||
return isResolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the group resolved or unresolved.
|
||||
*
|
||||
* @param resolved true to make the group resolved; false to
|
||||
* make it unresolved
|
||||
*/
|
||||
public void setResolved(boolean resolved)
|
||||
{
|
||||
this.isResolved = resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <tt>String</tt> that uniquely represnets the group inside
|
||||
* the current protocol. The string MUST be persistent (it must not change
|
||||
* across connections or runs of the application). In many cases (Jabber,
|
||||
* ICQ) the string may match the name of the group as these protocols
|
||||
* only allow a single level of contact groups and there is no danger of
|
||||
* having the same name twice in the same contact list. Other protocols
|
||||
* (no examples come to mind but that doesn't bother me ;) ) may be
|
||||
* supporting mutilple levels of grooups so it might be possible for group
|
||||
* A and group B to both contain groups named C. In such cases the
|
||||
* implementation must find a way to return a unique identifier in this
|
||||
* method and this UID should never change for a given group.
|
||||
*
|
||||
* @return a String representing this group in a unique and persistent
|
||||
* way.
|
||||
*/
|
||||
public String getUID()
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ugly but tricky conversion method.
|
||||
* @param uid the uid we'd like to get a name from
|
||||
* @return the name of the group with the specified <tt>uid</tt>.
|
||||
*/
|
||||
static String createNameFromUID(String uid)
|
||||
{
|
||||
return uid.substring(0, uid.length() - UID_SUFFIX.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether some other object is "equal to" this one which in terms
|
||||
* of contact groups translates to having the equal names and matching
|
||||
* subgroups and child contacts. The resolved status of contactgroups and
|
||||
* contacts is deliberately ignored so that groups and/or contacts would
|
||||
* be assumed equal even if it differs.
|
||||
* <p>
|
||||
* @param obj the reference object with which to compare.
|
||||
* @return <code>true</code> if this contact group has the equal child
|
||||
* contacts and subgroups to those of the <code>obj</code> argument.
|
||||
*/
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if(obj == null
|
||||
|| !(obj instanceof ContactGroupFacebookImpl))
|
||||
return false;
|
||||
|
||||
ContactGroupFacebookImpl facebookGroup
|
||||
= (ContactGroupFacebookImpl)obj;
|
||||
|
||||
if( ! facebookGroup.getGroupName().equals(getGroupName())
|
||||
|| ! facebookGroup.getUID().equals(getUID())
|
||||
|| facebookGroup.countContacts() != countContacts()
|
||||
|| facebookGroup.countSubgroups() != countSubgroups())
|
||||
return false;
|
||||
|
||||
//traverse child contacts
|
||||
Iterator<Contact> theirContacts = facebookGroup.contacts();
|
||||
|
||||
while(theirContacts.hasNext())
|
||||
{
|
||||
ContactFacebookImpl theirContact
|
||||
= (ContactFacebookImpl)theirContacts.next();
|
||||
|
||||
ContactFacebookImpl ourContact
|
||||
= (ContactFacebookImpl)getContact(theirContact.getAddress());
|
||||
|
||||
if(ourContact == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
//traverse subgroups
|
||||
Iterator<ContactGroup> theirSubgroups = facebookGroup.subgroups();
|
||||
|
||||
while(theirSubgroups.hasNext())
|
||||
{
|
||||
ContactGroupFacebookImpl theirSubgroup
|
||||
= (ContactGroupFacebookImpl)theirSubgroups.next();
|
||||
|
||||
ContactGroupFacebookImpl ourSubgroup
|
||||
= (ContactGroupFacebookImpl)getGroup(
|
||||
theirSubgroup.getGroupName());
|
||||
|
||||
if(ourSubgroup == null
|
||||
|| !ourSubgroup.equals(theirSubgroup))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
|
||||
/**
|
||||
* The Facebook implementation of a sip-communicator account id.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class FacebookAccountID
|
||||
extends AccountID
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates an account id from the specified id and account properties.
|
||||
*
|
||||
* @param userID
|
||||
* the user identifier corresponding to the account
|
||||
* @param accountProperties
|
||||
* any other properties necessary for the account.
|
||||
*/
|
||||
public FacebookAccountID(
|
||||
String userID,
|
||||
Map<String, String> accountProperties)
|
||||
{
|
||||
super(
|
||||
userID,
|
||||
accountProperties,
|
||||
ProtocolNames.FACEBOOK,
|
||||
"facebook.com");
|
||||
}
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.resources.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.osgi.framework.*;
|
||||
|
||||
/**
|
||||
* Loads the Facebook provider factory and registers its services in the OSGI
|
||||
* bundle context.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class FacebookActivator
|
||||
implements BundleActivator
|
||||
{
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(FacebookActivator.class);
|
||||
|
||||
/**
|
||||
* A reference to the registration of our Facebook protocol provider
|
||||
* factory.
|
||||
*/
|
||||
private ServiceRegistration facebookPpFactoryServReg = null;
|
||||
|
||||
/**
|
||||
* A reference to the Facebook protocol provider factory.
|
||||
*/
|
||||
private static ProtocolProviderFactoryFacebookImpl facebookProviderFactory;
|
||||
|
||||
/**
|
||||
* The currently valid bundle context.
|
||||
*/
|
||||
static BundleContext bundleContext = null;
|
||||
|
||||
private static ResourceManagementService resourceService;
|
||||
|
||||
/**
|
||||
* Called when this bundle is started. In here we'll export the facebook
|
||||
* ProtocolProviderFactory implementation so that it could be possible to
|
||||
* register accounts with it in SIP Communicator.
|
||||
*
|
||||
* @param context The execution context of the bundle being started.
|
||||
* @throws Exception If this method throws an exception, this bundle is
|
||||
* marked as stopped and the Framework will remove this bundle's
|
||||
* listeners, unregister all services registered by this bundle,
|
||||
* and release all services used by this bundle.
|
||||
*/
|
||||
public void start(BundleContext context) throws Exception
|
||||
{
|
||||
FacebookActivator.bundleContext = context;
|
||||
|
||||
facebookProviderFactory = new ProtocolProviderFactoryFacebookImpl();
|
||||
|
||||
// reg the facebook provider factory.
|
||||
Dictionary<String, String> properties = new Hashtable<String, String>();
|
||||
properties.put(ProtocolProviderFactory.PROTOCOL, "Facebook");
|
||||
|
||||
facebookPpFactoryServReg =
|
||||
context.registerService(ProtocolProviderFactory.class.getName(),
|
||||
facebookProviderFactory, properties);
|
||||
|
||||
logger.info("Facebook protocol implementation [STARTED].");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the bundle context that we were started with.
|
||||
*
|
||||
* @return a reference to the BundleContext instance that we were started
|
||||
* within.
|
||||
*/
|
||||
public static BundleContext getBundleContext()
|
||||
{
|
||||
return bundleContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the protocol provider factory that we have
|
||||
* registered.
|
||||
*
|
||||
* @return a reference to the <tt>ProtocolProviderFactoryJabberImpl</tt>
|
||||
* instance that we have registered from this package.
|
||||
*/
|
||||
public static ProtocolProviderFactoryFacebookImpl getProtocolProviderFactory()
|
||||
{
|
||||
return facebookProviderFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this bundle is stopped so the Framework can perform the
|
||||
* bundle-specific activities necessary to stop the bundle.
|
||||
*
|
||||
* @param context The execution context of the bundle being stopped.
|
||||
* @throws Exception If this method throws an exception, the bundle is still
|
||||
* marked as stopped, and the Framework will remove the bundle's
|
||||
* listeners, unregister all services registered by the bundle,
|
||||
* and release all services used by the bundle.
|
||||
*/
|
||||
public void stop(BundleContext context) throws Exception
|
||||
{
|
||||
facebookProviderFactory.stop();
|
||||
facebookPpFactoryServReg.unregister();
|
||||
logger.info("Facebook protocol implementation [STOPPED].");
|
||||
}
|
||||
|
||||
public static ResourceManagementService getResources()
|
||||
{
|
||||
if (resourceService == null)
|
||||
resourceService
|
||||
= ResourceManagementServiceUtils.getService(bundleContext);
|
||||
return resourceService;
|
||||
}
|
||||
}
|
||||
@ -1,397 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* Adapter for the Facebook protocol. This class works as a bridge between the
|
||||
* Facebook specific classes and the sip communication interfaces. It manages
|
||||
* the lifecycle of the classes responsible of accessing Facebook servers and
|
||||
* also manage the necessary {@link Thread threads} used to update the state
|
||||
* with the latest server changes.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Lubomir Marinov
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookAdapter
|
||||
implements FacebookSessionListener
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(FacebookAdapter.class);
|
||||
|
||||
private final OperationSetBasicInstantMessagingFacebookImpl
|
||||
basicInstantMessaging;
|
||||
|
||||
/**
|
||||
* Parent service provider
|
||||
*/
|
||||
private final ProtocolProviderServiceFacebookImpl parentProvider;
|
||||
|
||||
private final OperationSetPersistentPresenceFacebookImpl persistentPresence;
|
||||
|
||||
/**
|
||||
* The Facebook session
|
||||
*/
|
||||
private FacebookSession session;
|
||||
|
||||
private final OperationSetTypingNotificationsFacebookImpl
|
||||
typingNotifications;
|
||||
|
||||
/**
|
||||
* Cache of typing notifications
|
||||
*/
|
||||
private final Map<String, TypingNotificationRecord> typingNotificationRecord
|
||||
= new HashMap<String, TypingNotificationRecord>();
|
||||
|
||||
/**
|
||||
* Adapter for each Facebook Chat account.
|
||||
*
|
||||
* @param parentProvider the parent service provider
|
||||
* @param persistentPresence
|
||||
* @param basicInstantMessaging
|
||||
* @param typingNotifications
|
||||
*/
|
||||
public FacebookAdapter(
|
||||
ProtocolProviderServiceFacebookImpl parentProvider,
|
||||
OperationSetPersistentPresenceFacebookImpl persistentPresence,
|
||||
OperationSetBasicInstantMessagingFacebookImpl basicInstantMessaging,
|
||||
OperationSetTypingNotificationsFacebookImpl typingNotifications)
|
||||
{
|
||||
this.parentProvider = parentProvider;
|
||||
this.persistentPresence = persistentPresence;
|
||||
this.basicInstantMessaging = basicInstantMessaging;
|
||||
this.typingNotifications = typingNotifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facebook id of this account
|
||||
*
|
||||
* @return the facebook id of this account
|
||||
*/
|
||||
public String getUID()
|
||||
{
|
||||
return this.session.getUid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent service provider
|
||||
*
|
||||
* @return parent service provider
|
||||
*/
|
||||
public ProtocolProviderServiceFacebookImpl getParentProvider()
|
||||
{
|
||||
return parentProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the {@link FacebookSession},<br>
|
||||
* Initializes the {@link Thread}s to update the buddy list and to poll
|
||||
* messages
|
||||
*
|
||||
* @param email
|
||||
* @param password
|
||||
* @return true if the user is logged in
|
||||
* @throws IOException
|
||||
* @throws BrokenFacebookProtocolException
|
||||
*/
|
||||
public synchronized boolean initialize(
|
||||
final String email,
|
||||
final String password)
|
||||
throws OperationFailedException
|
||||
{
|
||||
if (this.session != null && this.session.isLoggedIn())
|
||||
return true;
|
||||
|
||||
logger.info("initializing facebook adapter. account");
|
||||
try
|
||||
{
|
||||
this.session = new FacebookSession();
|
||||
boolean loggedIn = session.login(email, password);
|
||||
if (loggedIn)
|
||||
session.addListener(this);
|
||||
return loggedIn;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw
|
||||
new OperationFailedException(
|
||||
"unable to initialize adapter",
|
||||
FacebookErrorException.kError_Login_GenericError,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post typing notification to the given contact.
|
||||
*
|
||||
* @param notifiedContact
|
||||
* the contact we want to notify
|
||||
* @param typingState
|
||||
* our current typing state(SC)
|
||||
* @throws HttpException
|
||||
* the http exception
|
||||
* @throws IOException
|
||||
* IO exception
|
||||
* @throws JSONException
|
||||
* JSON parsing exception
|
||||
* @throws Exception
|
||||
* the general exception
|
||||
*/
|
||||
public void postTypingNotification(Contact notifiedContact, int typingState)
|
||||
throws HttpException,
|
||||
IOException,
|
||||
JSONException,
|
||||
Exception
|
||||
{
|
||||
TypingNotificationRecord record
|
||||
= typingNotificationRecord.get(notifiedContact.getAddress());
|
||||
if (record == null)
|
||||
{
|
||||
record = new TypingNotificationRecord(-1l, -1);
|
||||
synchronized (typingNotificationRecord)
|
||||
{
|
||||
typingNotificationRecord
|
||||
.put(notifiedContact.getAddress(), record);
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getTime() < System.currentTimeMillis() - 1000)
|
||||
{
|
||||
FacebookOutgoingTypingNotification msg
|
||||
= new FacebookOutgoingTypingNotification(session);
|
||||
msg.setAddress(notifiedContact.getAddress());
|
||||
msg.setTypingState(typingState);
|
||||
msg.send();
|
||||
record.setTime(System.currentTimeMillis());
|
||||
record.setType(typingState);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatusMessage(String statusMessage)
|
||||
throws OperationFailedException
|
||||
{
|
||||
try
|
||||
{
|
||||
this.session.setStatusMessage(statusMessage);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw
|
||||
new OperationFailedException(
|
||||
"unable to change facebook status message",
|
||||
-1,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void shutdown()
|
||||
{
|
||||
if (session != null)
|
||||
{
|
||||
logger.info("shutting down facebook adapter");
|
||||
session.logout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post message to someone.
|
||||
*
|
||||
* @param message message to post
|
||||
* @param to contact
|
||||
* @return null if postMessage succeed, <tt>MessageDeliveryFailedEvent</tt>
|
||||
* otherwise
|
||||
*/
|
||||
public synchronized MessageDeliveryFailedEvent postMessage(
|
||||
Message message,
|
||||
Contact to)
|
||||
{
|
||||
FacebookOutgoingMailboxMessage msg
|
||||
= new FacebookOutgoingMailboxMessage(session);
|
||||
msg.setAddress(to.getAddress());
|
||||
msg.setContent(message.getContent());
|
||||
msg.setSubject(message.getSubject());
|
||||
msg.setUid(message.getMessageUID());
|
||||
try
|
||||
{
|
||||
msg.send();
|
||||
return null;
|
||||
}
|
||||
catch (BrokenFacebookProtocolException e)
|
||||
{
|
||||
logger.error(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (FacebookErrorException e)
|
||||
{
|
||||
return new MessageDeliveryFailedEvent(message, to, e.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public MessageDeliveryFailedEvent postFacebookChatMessage(
|
||||
Message message,
|
||||
Contact to)
|
||||
{
|
||||
FacebookOutgoingChatMessage msg
|
||||
= new FacebookOutgoingChatMessage(session);
|
||||
msg.setAddress(to.getAddress());
|
||||
msg.setContent(message.getContent());
|
||||
msg.setMessageUid(message.getMessageUID());
|
||||
try
|
||||
{
|
||||
msg.send();
|
||||
return null;
|
||||
}
|
||||
catch (BrokenFacebookProtocolException e)
|
||||
{
|
||||
logger.error(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn(e);
|
||||
return new MessageDeliveryFailedEvent(message, to, -1, System
|
||||
.currentTimeMillis(), e.getMessage());
|
||||
}
|
||||
catch (FacebookErrorException e)
|
||||
{
|
||||
return new MessageDeliveryFailedEvent(message, to, e.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes the incoming message to the GUI
|
||||
*
|
||||
* @see FacebookSessionListener#onIncomingChatMessage(FacebookMessage)
|
||||
*/
|
||||
public void onIncomingChatMessage(FacebookMessage msg)
|
||||
{
|
||||
if (!msg.getFrom().equals(this.session.getUid()))
|
||||
basicInstantMessaging.receivedInstantMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes the incoming notification to the GUI
|
||||
*
|
||||
* @see FacebookSessionListener#onIncomingTypingNotification(String, int)
|
||||
*/
|
||||
public void onIncomingTypingNotification(String buddyUid, int state)
|
||||
{
|
||||
if (!buddyUid.equals(this.session.getUid()))
|
||||
{
|
||||
Contact fromContact = persistentPresence.findContactByID(buddyUid);
|
||||
if (fromContact == null)
|
||||
fromContact
|
||||
= persistentPresence.createVolatileContact(buddyUid);
|
||||
|
||||
int typingState = OperationSetTypingNotifications.STATE_UNKNOWN;
|
||||
switch (state)
|
||||
{
|
||||
case 1:
|
||||
typingState = OperationSetTypingNotifications.STATE_TYPING;
|
||||
break;
|
||||
case 0:
|
||||
typingState = OperationSetTypingNotifications.STATE_STOPPED;
|
||||
break;
|
||||
default:
|
||||
typingState = OperationSetTypingNotifications.STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
typingNotifications
|
||||
.receivedTypingNotification(fromContact, typingState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notifies SC that the connection is lost
|
||||
*/
|
||||
public void onFacebookConnectionLost() {
|
||||
if (parentProvider.isRegistered())
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister();
|
||||
}
|
||||
catch (OperationFailedException e)
|
||||
{
|
||||
logger.error("unable to unregister", e);
|
||||
}
|
||||
}
|
||||
|
||||
// tag all the buddies as offline
|
||||
persistentPresence
|
||||
.setPresenceStatusForAllContacts(FacebookStatusEnum.OFFLINE);
|
||||
}
|
||||
|
||||
public void onBuddyListUpdated()
|
||||
{
|
||||
for (FacebookUser user : this.session.getBuddyList().getBuddies())
|
||||
{
|
||||
PresenceStatus newStatus;
|
||||
if (user.isOnline && user.isIdle)
|
||||
newStatus = FacebookStatusEnum.IDLE;
|
||||
else if (user.isOnline)
|
||||
newStatus = FacebookStatusEnum.ONLINE;
|
||||
else
|
||||
newStatus = FacebookStatusEnum.OFFLINE;
|
||||
|
||||
persistentPresence.setPresenceStatusForContact(user.uid, newStatus);
|
||||
}
|
||||
}
|
||||
|
||||
public FacebookSession getSession()
|
||||
{
|
||||
return session;
|
||||
}
|
||||
|
||||
private static class TypingNotificationRecord
|
||||
{
|
||||
private long time;
|
||||
private int type;
|
||||
|
||||
public TypingNotificationRecord(long time, int type)
|
||||
{
|
||||
this.time = time;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getTime()
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public int getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setTime(long time)
|
||||
{
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public void setType(int type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,287 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.entity.*;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.message.*;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
import org.apache.http.util.*;
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* Facebook buddy list that store the online buddies information we got from the
|
||||
* server since we logged in. Some information of ourselves also included. <br>
|
||||
* This class is responsible of establishing the http connections and updating
|
||||
* its state when requested.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookBuddyList
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(FacebookBuddyList.class);
|
||||
|
||||
/**
|
||||
* The url of the update
|
||||
*/
|
||||
private static final String BUDDYLIST_URL
|
||||
= "http://www.facebook.com/ajax/chat/buddy_list.php";
|
||||
|
||||
/**
|
||||
* The Facebook Session
|
||||
*/
|
||||
private final FacebookSession session;
|
||||
|
||||
/**
|
||||
* Our (online) buddies' information cache
|
||||
*/
|
||||
private transient Map<String, FacebookUser> cache
|
||||
= new LinkedHashMap<String, FacebookUser>();
|
||||
|
||||
/**
|
||||
* Some information of ourselves/myself.
|
||||
*/
|
||||
private transient FacebookUser me;
|
||||
|
||||
/**
|
||||
* Listener of this buddy list
|
||||
*/
|
||||
private List<FacebookSessionListener> listeners
|
||||
= new ArrayList<FacebookSessionListener>();
|
||||
|
||||
/**
|
||||
* Init the cache and the parent adapter.
|
||||
*
|
||||
* @param session the Facebook session
|
||||
*/
|
||||
public FacebookBuddyList(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void update()
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
// reconnecting
|
||||
this.session.reconnect();
|
||||
logger.info("Updating buddy list...");
|
||||
// perform POST request
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("buddy_list", "1"));
|
||||
nvps.add(new BasicNameValuePair("notifications", "1"));
|
||||
nvps.add(new BasicNameValuePair("force_render", "true"));
|
||||
nvps.add(new BasicNameValuePair("post_form_id", session.getFormId()));
|
||||
nvps.add(new BasicNameValuePair("user", session.getUid()));
|
||||
HttpPost post = new HttpPost(BUDDYLIST_URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = this.session.getHttpClient().execute(post);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
if (response.getStatusLine().getStatusCode() != 200)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException("Code "
|
||||
+ response.getStatusLine().getStatusCode()
|
||||
+ ". Unable to update Facebook buddy list.");
|
||||
}
|
||||
this.update(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meta info of this account
|
||||
*
|
||||
* @return meta info of this account
|
||||
*/
|
||||
public FacebookUser getMyMetaInfo()
|
||||
{
|
||||
return me;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our buddy who has the given id from the cache.
|
||||
*
|
||||
* @param uid the id we want to look up
|
||||
* @return the buddy who has the given id
|
||||
* @throws FacebookErrorException
|
||||
* @throws IOException
|
||||
* @throws BrokenFacebookProtocolException
|
||||
*/
|
||||
public FacebookUser getBuddyByUID(String uid)
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
FacebookUser buddy = this.cache.get(uid);
|
||||
if (buddy == null && this.me != null && this.me.uid.equals(uid))
|
||||
{
|
||||
buddy = me;
|
||||
}
|
||||
return buddy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the users in the cache
|
||||
*/
|
||||
public Collection<FacebookUser> getBuddies()
|
||||
{
|
||||
return Collections.unmodifiableCollection(this.cache.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the resource
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
for (FacebookUser u : this.cache.values())
|
||||
{
|
||||
u.isOnline = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the buddy list<br>
|
||||
* If the {@link FacebookSession} is logged out then this method returns
|
||||
* without modifying the state of the buddy list.
|
||||
*
|
||||
* @param body
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* @throws FacebookErrorException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void update(String body)
|
||||
throws BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
// the session might be getting closed by another thread
|
||||
// do nothing if it's logged out
|
||||
synchronized (session)
|
||||
{
|
||||
if (!session.isLoggedIn())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject jsonBuddyList = parseBody(body);
|
||||
try {
|
||||
/*
|
||||
* If listChanged, then we can get the buddies available via looking
|
||||
* at the nowAvailableList else. we can only get the buddies' info,
|
||||
* and the nowAvailableList is empty.
|
||||
*/
|
||||
JSONObject userInfos = (JSONObject) jsonBuddyList.get("userInfos");
|
||||
if (userInfos != null)
|
||||
{
|
||||
// Then add the new buddies and set them as online(constructor)
|
||||
Iterator<String> it = userInfos.keys();
|
||||
while (it.hasNext())
|
||||
{
|
||||
String key = it.next();
|
||||
JSONObject jsonUser = (JSONObject) userInfos.get(key);
|
||||
if (jsonUser == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to get user info. " + userInfos);
|
||||
}
|
||||
FacebookUser buddy = new FacebookUser(key, jsonUser);
|
||||
if (buddy.uid.equals(this.session.getUid()))
|
||||
{
|
||||
this.me = buddy;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.cache.put(key, buddy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject nowAvailableList = jsonBuddyList
|
||||
.getJSONObject("nowAvailableList");
|
||||
|
||||
if (nowAvailableList == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"Unable to read Facebook now available list");
|
||||
}
|
||||
|
||||
for (FacebookUser user : this.cache.values())
|
||||
{
|
||||
if (nowAvailableList.has(user.uid))
|
||||
{
|
||||
user.isOnline = true;
|
||||
user.lastSeen = Calendar.getInstance().getTimeInMillis();
|
||||
user.isIdle = nowAvailableList.getJSONObject(user.uid)
|
||||
.getBoolean("i");
|
||||
}
|
||||
else
|
||||
{
|
||||
user.isOnline = false;
|
||||
}
|
||||
}
|
||||
// notify listeners
|
||||
for (FacebookSessionListener l : this.listeners)
|
||||
l.onBuddyListUpdated();
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject parseBody(String body)
|
||||
throws BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
FacebookJsonResponse jsonResponse = new FacebookJsonResponse(session,
|
||||
body);
|
||||
try
|
||||
{
|
||||
JSONObject json = jsonResponse.getJson();
|
||||
JSONObject payload = (JSONObject) json.get("payload");
|
||||
if (payload == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse buddy list. there's no payload field. "
|
||||
+ jsonResponse);
|
||||
}
|
||||
JSONObject jsonBuddyList = (JSONObject) payload.get("buddy_list");
|
||||
if (jsonBuddyList == null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse buddy list. there's no buddy list field. "
|
||||
+ jsonResponse);
|
||||
}
|
||||
return jsonBuddyList;
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to parse json response");
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return this.cache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener which is notified when the buddy list state changes
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void addListener(FacebookSessionListener listener)
|
||||
{
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
/**
|
||||
* This exception is thrown when Facebook returns an error instead of the
|
||||
* expected response.
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class FacebookErrorException extends Exception
|
||||
{
|
||||
public static final int kError_Global_ValidationError = 1346001;
|
||||
|
||||
public static final int kError_Login_GenericError = 1348009;
|
||||
|
||||
public static final int kError_Platform_CallbackValidationFailure = 1349007;
|
||||
|
||||
public static final int kError_Platform_ApplicationResponseInvalid = 1349008;
|
||||
|
||||
public static final int kError_Chat_NotAvailable = 1356002;
|
||||
|
||||
public static final int kError_Chat_SendOtherNotAvailable = 1356003;
|
||||
|
||||
public static final int kError_Chat_TooManyMessages = 1356008;
|
||||
|
||||
public static final int kError_Async_NotLoggedIn = 1357001;
|
||||
|
||||
public static final int kError_Async_LoginChanged = 1357003;
|
||||
|
||||
public static final int kError_Async_CSRFCheckFailed = 1357004;
|
||||
// Bad Parameter; There was an error understanding
|
||||
// the request.
|
||||
public static final int Error_Async_BadParameter = 1357005;
|
||||
|
||||
public static final int Error_Global_NoError = 0;
|
||||
|
||||
public static final int Error_Async_HttpConnectionFailed = 1001;
|
||||
|
||||
public static final int Error_Async_UnexpectedNullResponse = 1002;
|
||||
|
||||
public static final int Error_System_UIDNotFound = 1003;
|
||||
|
||||
public static final int Error_System_ChannelNotFound = 1004;
|
||||
|
||||
public static final int Error_System_PostFormIDNotFound = 1005;
|
||||
|
||||
public static final int Error_Global_PostMethodError = 1006;
|
||||
|
||||
public static final int Error_Global_GetMethodError = 1007;
|
||||
|
||||
public static final int Error_Global_JSONError = 1008;
|
||||
|
||||
private FacebookSession session;
|
||||
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* Constructor which uses the given {@link FacebookSession} to try to take
|
||||
* the necessary actions according to the given error code
|
||||
*
|
||||
* @param session
|
||||
* @param code
|
||||
*/
|
||||
public FacebookErrorException(FacebookSession session, int code)
|
||||
{
|
||||
super("Facebook error " + code);
|
||||
this.code = code;
|
||||
if (code == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| code == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
session.logout();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public FacebookSession getSession()
|
||||
{
|
||||
return session;
|
||||
}
|
||||
}
|
||||
@ -1,321 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.util.*;
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* This poller checks for new messages in Facebook servers and notifies to the
|
||||
* registered {@link FacebookSessionListener listeners} if a new
|
||||
* {@link FacebookMessage} is found.<br>
|
||||
* Registered {@link FacebookSessionListener listeners} are notified upon
|
||||
* arrival of both messages from other buddies and own messages.<br>
|
||||
* It's the responsibility of the {@link FacebookSessionListener listeners} to
|
||||
* discard undesired notifications, e.g. own messages.
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookIncomingMessagePoller
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(FacebookIncomingMessagePoller.class);
|
||||
|
||||
/**
|
||||
* History url
|
||||
*/
|
||||
private static final String HISTORY_URL = "http://www.facebook.com/ajax/chat/history.php?id=";
|
||||
|
||||
/**
|
||||
* The Facebook session
|
||||
*/
|
||||
private final FacebookSession session;
|
||||
|
||||
/**
|
||||
* Listeners which are notified when a new message arrives
|
||||
*/
|
||||
private List<FacebookSessionListener> listeners = new ArrayList<FacebookSessionListener>();
|
||||
|
||||
/**
|
||||
* A queue of received messages used to maintain a record of the last
|
||||
* messages received
|
||||
*/
|
||||
private Queue<String> received = new LinkedList<String>();
|
||||
|
||||
/**
|
||||
* After refreshing messages must be retrieved by calling the history, which
|
||||
* doesn't inform the SEQ number, that's why we keep the last receive time
|
||||
* in order to inform to registered listeners messages which have a time
|
||||
* after the time of the last message received.
|
||||
*/
|
||||
private long lastReceiveTime = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* The sequence number of the last message received
|
||||
*/
|
||||
private int seq = -1;
|
||||
|
||||
/**
|
||||
* This random value generator is used when creating a URL to retrieve
|
||||
* messages, the random part of the URL is intended to avoid undesired
|
||||
* caching
|
||||
*/
|
||||
private Random random = new Random();
|
||||
|
||||
public FacebookIncomingMessagePoller(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls new messages from Facebook servers
|
||||
*
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void poll()
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.seq == -1)
|
||||
{
|
||||
this.seq = getNewestSeq();
|
||||
}
|
||||
logger.debug("polling seq " + this.seq);
|
||||
// poll messages
|
||||
HttpGet get = new HttpGet(this.getMessagePollUrl());
|
||||
HttpResponse response = this.session.getHttpClient().execute(get);
|
||||
|
||||
// validate response code
|
||||
if (response.getStatusLine().getStatusCode() != 200)
|
||||
{
|
||||
response.getEntity().consumeContent();
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to poll messages. http code "
|
||||
+ response.getStatusLine().getStatusCode());
|
||||
}
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
FacebookJsonResponse jsonResponse = new FacebookJsonResponse(
|
||||
this.session, body);
|
||||
JSONObject json = jsonResponse.getJson();
|
||||
|
||||
// parse json response
|
||||
if (json.has("t"))
|
||||
{
|
||||
String t = json.getString("t");
|
||||
if (t.equals("msg"))
|
||||
{
|
||||
JSONArray ms = (JSONArray) json.get("ms");
|
||||
for (int i = 0; i < ms.length(); i++)
|
||||
{
|
||||
JSONObject msg = ms.getJSONObject(i);
|
||||
if (msg.get("type").equals("typ"))
|
||||
{
|
||||
for (FacebookSessionListener l : this.listeners)
|
||||
{
|
||||
l.onIncomingTypingNotification(msg
|
||||
.getString("from"), msg.getInt("st"));
|
||||
}
|
||||
}
|
||||
else if (msg.get("type").equals("msg"))
|
||||
{
|
||||
// the message itself
|
||||
FacebookMessage fm = new FacebookMessage(msg);
|
||||
this.lastReceiveTime = fm.getTime();
|
||||
this.notifyListeners(fm);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("neither notification nor message "
|
||||
+ msg);
|
||||
}
|
||||
}
|
||||
this.seq++;
|
||||
}
|
||||
else if (t.equals("refresh"))
|
||||
{
|
||||
logger.debug("refreshing...");
|
||||
this.pollHistory();
|
||||
if (json.has("seq"))
|
||||
{
|
||||
this.seq = json.getInt("seq");
|
||||
}
|
||||
else
|
||||
{
|
||||
Integer newSeq = this.getNewestSeq();
|
||||
if (newSeq != null)
|
||||
{
|
||||
this.seq = newSeq;
|
||||
}
|
||||
}
|
||||
this.session.reconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SEQ number from the server via requesting a message with SEQ
|
||||
* =-1. The server will return the current SEQ number because -1 is a
|
||||
* invalid SEQ number.
|
||||
*
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* @throws IOException
|
||||
* @throws FacebookErrorException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private Integer getNewestSeq()
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
logger.debug("GET newest seq");
|
||||
Integer newest = null;
|
||||
try
|
||||
{
|
||||
String url = getMessagePollUrl();
|
||||
HttpPost post = new HttpPost(url);
|
||||
HttpResponse response = this.session.getHttpClient().execute(post);
|
||||
if (response.getStatusLine().getStatusCode() == 200)
|
||||
{
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
FacebookJsonResponse jsonResponse = new FacebookJsonResponse(
|
||||
this.session, body);
|
||||
JSONObject json = jsonResponse.getJson();
|
||||
if (json.has("seq"))
|
||||
{
|
||||
newest = json.getInt("seq");
|
||||
logger.debug("seq updated:" + newest);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response.getEntity().consumeContent();
|
||||
throw new BrokenFacebookProtocolException("http code "
|
||||
+ response.getStatusLine().getStatusCode());
|
||||
}
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(e);
|
||||
}
|
||||
return newest;
|
||||
}
|
||||
|
||||
public void addListener(FacebookSessionListener listener)
|
||||
{
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public int getSeq()
|
||||
{
|
||||
return seq;
|
||||
}
|
||||
|
||||
private String getMessagePollUrl()
|
||||
{
|
||||
return "http://0.channel" + this.session.getChannel()
|
||||
+ ".facebook.com/x/" + random.nextInt() + "/false/p_"
|
||||
+ this.session.getUid() + "=" + this.seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the listeners that a new message is available. This method
|
||||
* performs a validation to avoid publishing the same message more than
|
||||
* once.
|
||||
*
|
||||
* @param msg
|
||||
* the message to public
|
||||
*/
|
||||
private synchronized void notifyListeners(FacebookMessage msg)
|
||||
{
|
||||
logger.debug("message received " + msg.getMsgID());
|
||||
if (this.received.contains(msg.getMsgID()))
|
||||
{
|
||||
logger.debug("discarding duplicated message " + msg.getMsgID());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (FacebookSessionListener l : this.listeners)
|
||||
l.onIncomingChatMessage(msg);
|
||||
this.received.add(msg.getMsgID());
|
||||
if (this.received.size() > 20)
|
||||
{
|
||||
this.received.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the history from Facebook servers for every contact in the
|
||||
* buddy list
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* @throws FacebookErrorException
|
||||
*/
|
||||
public void pollHistory()
|
||||
throws IOException,
|
||||
BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
for (FacebookUser user : this.session.getBuddyList().getBuddies())
|
||||
{
|
||||
HttpGet get = new HttpGet(HISTORY_URL + user.uid);
|
||||
HttpResponse response = this.session.getHttpClient().execute(get);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
if (response.getStatusLine().getStatusCode() != 200)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException("http code "
|
||||
+ response.getStatusLine().getStatusCode() + ". "
|
||||
+ body);
|
||||
}
|
||||
try
|
||||
{
|
||||
FacebookJsonResponse jsonResponse = new FacebookJsonResponse(
|
||||
this.session, body);
|
||||
JSONArray history = jsonResponse.getJson().getJSONObject(
|
||||
"payload").getJSONArray("history");
|
||||
for (int i = 0; i < history.length(); i++)
|
||||
{
|
||||
JSONObject msg = history.getJSONObject(i);
|
||||
if (msg.getString("type").equals("msg"))
|
||||
{
|
||||
FacebookMessage fm = null;
|
||||
String fromUid = msg.getString("from");
|
||||
if (fromUid.equals(user.uid))
|
||||
{
|
||||
fm = new FacebookMessage(user, msg);
|
||||
if (fm.getTime() > this.lastReceiveTime)
|
||||
{
|
||||
this.lastReceiveTime = fm.getTime();
|
||||
this.notifyListeners(fm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException("response " + body, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* A simple parser which provides convenient cleanup of the json response from
|
||||
* Facebook.
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookJsonResponse
|
||||
{
|
||||
private final JSONObject json;
|
||||
|
||||
/**
|
||||
* Parses the response and creates the json object, if the response has
|
||||
* errors a {@link FacebookErrorException} is thrown
|
||||
*
|
||||
* @param session
|
||||
* @param body
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* if the text can not be converted to a json object
|
||||
* @throws FacebookErrorException
|
||||
* if the response has a facebook error code
|
||||
*/
|
||||
public FacebookJsonResponse(FacebookSession session, String body)
|
||||
throws BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
String prefix = "for (;;);";
|
||||
if (body.startsWith(prefix))
|
||||
{
|
||||
body = body.substring(prefix.length());
|
||||
}
|
||||
try
|
||||
{
|
||||
this.json = new JSONObject(body);
|
||||
if (this.json.has("error"))
|
||||
{
|
||||
int errorCode = getJson().getInt("error");
|
||||
if (errorCode != 0)
|
||||
{
|
||||
throw new FacebookErrorException(session, errorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"Unable to read error from response. " + this.getJson());
|
||||
}
|
||||
}
|
||||
|
||||
public JSONObject getJson()
|
||||
{
|
||||
return json;
|
||||
}
|
||||
}
|
||||
@ -1,181 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* Facebook meta message
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookMessage
|
||||
{
|
||||
private String text;
|
||||
|
||||
private long time;
|
||||
|
||||
private long clientTime;
|
||||
|
||||
private String msgID;
|
||||
|
||||
private String from;
|
||||
|
||||
private String to;
|
||||
|
||||
private String fromName;
|
||||
|
||||
private String toName;
|
||||
|
||||
private String fromFirstName;
|
||||
|
||||
private String toFirstName;
|
||||
|
||||
public FacebookMessage()
|
||||
{
|
||||
}
|
||||
|
||||
public FacebookMessage(FacebookUser fromUser, JSONObject json)
|
||||
throws JSONException
|
||||
{
|
||||
this.from = json.getString("from");
|
||||
this.to = json.getString("to");
|
||||
this.time = json.getLong("time");
|
||||
this.clientTime = time;
|
||||
// some messages come without user time
|
||||
JSONObject msg = (JSONObject) json.get("msg");
|
||||
this.text = msg.getString("text");
|
||||
if (msg.has("msgID"))
|
||||
{
|
||||
this.msgID = msg.getString("msgID");
|
||||
}
|
||||
else
|
||||
{
|
||||
// assign a random if it's not included in the message
|
||||
this.msgID = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
if (!fromUser.uid.equals(this.from))
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"the given message doesn't belong to the given user");
|
||||
}
|
||||
// envelope
|
||||
this.fromName = fromUser.name;
|
||||
this.fromFirstName = fromUser.firstName;
|
||||
}
|
||||
|
||||
public FacebookMessage(JSONObject json)
|
||||
throws JSONException
|
||||
{
|
||||
// envelope
|
||||
this.from = json.getString("from");
|
||||
this.to = json.getString("to");
|
||||
this.fromName = json.getString("from_name");
|
||||
this.toName = json.getString("to_name");
|
||||
this.fromFirstName = json.getString("from_first_name");
|
||||
this.toFirstName = json.getString("to_first_name");
|
||||
|
||||
// data
|
||||
JSONObject msg = (JSONObject) json.get("msg");
|
||||
this.text = msg.getString("text");
|
||||
this.time = msg.getLong("time");
|
||||
this.clientTime = msg.getLong("clientTime");
|
||||
this.msgID = msg.getString("msgID");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creat a facebook message with the given params.
|
||||
*
|
||||
* @param txt
|
||||
* message text
|
||||
* @param tm
|
||||
* message received time(?)
|
||||
* @param ct
|
||||
* client time(message sent time?)
|
||||
* @param id
|
||||
* message id generated randomly.
|
||||
* @param f
|
||||
* from uid
|
||||
* @param t
|
||||
* to uid
|
||||
* @param fn
|
||||
* from name
|
||||
* @param tn
|
||||
* to name
|
||||
* @param ffn
|
||||
* from first name
|
||||
* @param tfn
|
||||
* to first name
|
||||
*/
|
||||
public FacebookMessage(String txt, long tm, long ct, String id, String f,
|
||||
String t, String fn, String tn, String ffn, String tfn)
|
||||
{
|
||||
this.text = txt;
|
||||
this.time = tm;
|
||||
this.clientTime = ct;
|
||||
this.msgID = id;
|
||||
this.from = f;
|
||||
this.to = t;
|
||||
this.fromName = fn;
|
||||
this.toName = tn;
|
||||
this.fromFirstName = ffn;
|
||||
this.toFirstName = tfn;
|
||||
}
|
||||
|
||||
public String getText()
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
public long getTime()
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public long getClientTime()
|
||||
{
|
||||
return clientTime;
|
||||
}
|
||||
|
||||
public String getMsgID()
|
||||
{
|
||||
return msgID;
|
||||
}
|
||||
|
||||
public String getFrom()
|
||||
{
|
||||
return from;
|
||||
}
|
||||
|
||||
public String getTo()
|
||||
{
|
||||
return to;
|
||||
}
|
||||
|
||||
public String getFromName()
|
||||
{
|
||||
return fromName;
|
||||
}
|
||||
|
||||
public String getToName()
|
||||
{
|
||||
return toName;
|
||||
}
|
||||
|
||||
public String getFromFirstName()
|
||||
{
|
||||
return fromFirstName;
|
||||
}
|
||||
|
||||
public String getToFirstName()
|
||||
{
|
||||
return toFirstName;
|
||||
}
|
||||
}
|
||||
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.entity.*;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.message.*;
|
||||
import org.apache.http.protocol.*;
|
||||
import org.apache.http.util.*;
|
||||
|
||||
/**
|
||||
* An outgoing chat message
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookOutgoingChatMessage {
|
||||
|
||||
/**
|
||||
* The url of the login page
|
||||
*/
|
||||
private static final String SEND_URL = "http://www.facebook.com/ajax/chat/send.php";
|
||||
|
||||
private final FacebookSession session;
|
||||
|
||||
private String address;
|
||||
|
||||
private String content;
|
||||
|
||||
private String messageUid;
|
||||
|
||||
public FacebookOutgoingChatMessage(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the message to the given address.<br>
|
||||
* Only one message can be sent at a time for a given
|
||||
* {@link FacebookSession}
|
||||
*
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void send()
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
synchronized (this.session)
|
||||
{
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("msg_text", (content == null) ? ""
|
||||
: content));
|
||||
nvps.add(new BasicNameValuePair("msg_id", messageUid));
|
||||
nvps.add(new BasicNameValuePair("client_time", Long.toString(System
|
||||
.currentTimeMillis())));
|
||||
nvps.add(new BasicNameValuePair("to", address));
|
||||
nvps
|
||||
.add(new BasicNameValuePair("post_form_id", session
|
||||
.getFormId()));
|
||||
HttpPost post = new HttpPost(SEND_URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = this.session.getHttpClient().execute(post);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
if (response.getStatusLine().getStatusCode() != 200)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException("CODE "
|
||||
+ response.getStatusLine().getStatusCode());
|
||||
}
|
||||
new FacebookJsonResponse(this.session, body);
|
||||
}
|
||||
}
|
||||
|
||||
public String getAddress()
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address)
|
||||
{
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getContent()
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content)
|
||||
{
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getMessageUid()
|
||||
{
|
||||
return messageUid;
|
||||
}
|
||||
|
||||
public void setMessageUid(String messageUid)
|
||||
{
|
||||
this.messageUid = messageUid;
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.entity.*;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.message.*;
|
||||
import org.apache.http.protocol.*;
|
||||
import org.apache.http.util.*;
|
||||
|
||||
/**
|
||||
* FIXME not working
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookOutgoingMailboxMessage
|
||||
{
|
||||
private final static String URL = "http://www.facebook.com/ajax/inbox/ajax.php";
|
||||
|
||||
private final FacebookSession session;
|
||||
|
||||
private String uid;
|
||||
|
||||
private String subject;
|
||||
|
||||
private String content;
|
||||
|
||||
private String address;
|
||||
|
||||
public FacebookOutgoingMailboxMessage(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void send()
|
||||
throws IOException,
|
||||
BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("post_form_id", session.getFormId()));
|
||||
nvps.add(new BasicNameValuePair("message", (this.content == null) ? ""
|
||||
: this.content));
|
||||
nvps.add(new BasicNameValuePair("subject", (this.subject == null) ? ""
|
||||
: this.subject));
|
||||
nvps.add(new BasicNameValuePair("ids[0]", address));
|
||||
nvps.add(new BasicNameValuePair("action", "compose"));
|
||||
|
||||
HttpPost post = new HttpPost(URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = this.session.getHttpClient().execute(post);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
new FacebookJsonResponse(this.session, body);
|
||||
}
|
||||
|
||||
public String getUid()
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUid(String uid)
|
||||
{
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public String getSubject()
|
||||
{
|
||||
return subject;
|
||||
}
|
||||
|
||||
public void setSubject(String subject)
|
||||
{
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public String getContent()
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content)
|
||||
{
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getAddress()
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address)
|
||||
{
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.entity.*;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.message.*;
|
||||
import org.apache.http.protocol.*;
|
||||
import org.apache.http.util.*;
|
||||
|
||||
/**
|
||||
* An outgoing typing notification
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookOutgoingTypingNotification
|
||||
{
|
||||
private static Logger logger = Logger
|
||||
.getLogger(FacebookOutgoingTypingNotification.class);
|
||||
|
||||
private final static String URL = "http://www.facebook.com/ajax/chat/typ.php";
|
||||
|
||||
private final FacebookSession session;
|
||||
|
||||
private int typingState;
|
||||
|
||||
private String address;
|
||||
|
||||
public FacebookOutgoingTypingNotification(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void send()
|
||||
throws IOException,
|
||||
BrokenFacebookProtocolException,
|
||||
FacebookErrorException
|
||||
{
|
||||
synchronized (this.session)
|
||||
{
|
||||
logger.debug("sending typing notification " + typingState);
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("typ", typingState + ""));
|
||||
nvps.add(new BasicNameValuePair("to", address));
|
||||
nvps
|
||||
.add(new BasicNameValuePair("post_form_id", session
|
||||
.getFormId()));
|
||||
HttpPost post = new HttpPost(URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = this.session.getHttpClient().execute(post);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
new FacebookJsonResponse(this.session, body);
|
||||
}
|
||||
}
|
||||
|
||||
public int getTypingState()
|
||||
{
|
||||
return typingState;
|
||||
}
|
||||
|
||||
public void setTypingState(int typingState)
|
||||
{
|
||||
this.typingState = typingState;
|
||||
}
|
||||
|
||||
public String getAddress()
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address)
|
||||
{
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
@ -1,696 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.*;
|
||||
import org.apache.http.client.entity.*;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.client.params.*;
|
||||
import org.apache.http.conn.*;
|
||||
import org.apache.http.conn.scheme.*;
|
||||
import org.apache.http.conn.ssl.*;
|
||||
import org.apache.http.impl.client.*;
|
||||
import org.apache.http.impl.conn.tsccm.*;
|
||||
import org.apache.http.message.*;
|
||||
import org.apache.http.params.*;
|
||||
import org.apache.http.protocol.*;
|
||||
import org.apache.http.util.*;
|
||||
|
||||
/**
|
||||
* A Facebook Session.<br>
|
||||
* This class is responsible of performing the login, logout and eventual
|
||||
* reconnection to the server.<br>
|
||||
* Remember to {@link FacebookSession#logout() logout} the session after usage,
|
||||
* until logout this {@link FacebookSession} will maintain HTTP connections open
|
||||
* to retrieve messages and maintain the buddy list updated.<br>
|
||||
* In some cases, e.g. after repeated IO errors, the {@link FacebookSession}
|
||||
* will be closed automatically and the registered
|
||||
* {@link FacebookSessionListener listeners} will be notified.<br>
|
||||
* The {@link FacebookSession} will be automatically closed if a
|
||||
* {@link FacebookErrorException} is thrown with the code
|
||||
* {@link FacebookErrorException#kError_Async_NotLoggedIn}.
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookSession
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(FacebookSession.class);
|
||||
|
||||
/**
|
||||
* Pattern to find the UID
|
||||
*/
|
||||
private final static Pattern UID_PATTERN = Pattern
|
||||
.compile("profile.php\\?id=[\\d]+\\&ref=profile");
|
||||
|
||||
/**
|
||||
* Pattern to find the Channel
|
||||
*/
|
||||
private final static Pattern CHANNEL_PATTERN = Pattern
|
||||
.compile("\"channel[\\d]+\"");
|
||||
|
||||
/**
|
||||
* Pattern to find the Channel
|
||||
*/
|
||||
private final static Pattern FORMID_PATTERN = Pattern
|
||||
.compile("<input type=\\\"hidden\\\" id=\\\"post_form_id\\\" name=\\\"post_form_id\\\" value=\\\"[^\"]+\\\" />");
|
||||
|
||||
/**
|
||||
* The url of the login page
|
||||
*/
|
||||
private static final String LOGIN_PAGE_URL = "http://www.facebook.com/login.php";
|
||||
|
||||
/**
|
||||
* The url of the home page
|
||||
*/
|
||||
private static final String HOME_PAGE_URL = "http://www.facebook.com/home.php";
|
||||
|
||||
/**
|
||||
* The url of the home page
|
||||
*/
|
||||
private static final String POPOUT_PAGE_URL = "http://www.facebook.com/presence/popout.php";
|
||||
|
||||
/**
|
||||
* The url to logout
|
||||
*/
|
||||
private static final String LOGOUT_URL = "http://www.facebook.com/logout.php";
|
||||
|
||||
/**
|
||||
* The url to the settings page
|
||||
*/
|
||||
private static final String SETTINGS_URL = "http://www.facebook.com/ajax/chat/settings.php";
|
||||
|
||||
/**
|
||||
* The url to update the status
|
||||
*/
|
||||
private static final String UPDATE_STATUS_URL = "http://www.facebook.com/updatestatus.php";
|
||||
|
||||
/**
|
||||
* the url to reconnect
|
||||
*/
|
||||
private static final String RECONNECT_URL = "http://www.facebook.com/ajax/presence/reconnect.php";
|
||||
|
||||
/**
|
||||
* Listener registry
|
||||
*/
|
||||
private List<FacebookSessionListener> listeners = new ArrayList<FacebookSessionListener>();
|
||||
|
||||
/**
|
||||
* The {@link HttpClient} used to login
|
||||
*/
|
||||
private final HttpClient httpClient;
|
||||
|
||||
/**
|
||||
* Flag that indicates whether the current session represents a logged in
|
||||
* user
|
||||
*/
|
||||
private boolean loggedIn = false;
|
||||
|
||||
/**
|
||||
* The user id
|
||||
*/
|
||||
private String uid;
|
||||
|
||||
/**
|
||||
* The channel assigned by the server
|
||||
*/
|
||||
private String channel;
|
||||
|
||||
/**
|
||||
* The form id which must be used in every request
|
||||
*/
|
||||
private String formId;
|
||||
|
||||
/**
|
||||
* The buddy list of this account
|
||||
*/
|
||||
private final FacebookBuddyList buddyList;
|
||||
|
||||
/**
|
||||
* Runnable responsible of updating the buddy list
|
||||
*/
|
||||
private BuddyListRefresher buddyListRefresher;
|
||||
|
||||
/**
|
||||
* Message poller
|
||||
*/
|
||||
private final FacebookIncomingMessagePoller poller;
|
||||
|
||||
/**
|
||||
* Runnable which keeps requesting new messages.
|
||||
*/
|
||||
private PollerRefresher pollerRefresher;
|
||||
|
||||
public FacebookSession()
|
||||
{
|
||||
this.httpClient = createHttpClient();
|
||||
this.buddyList = new FacebookBuddyList(this);
|
||||
this.poller = new FacebookIncomingMessagePoller(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creat a http client with default settings
|
||||
*
|
||||
* @return a http client with default settings
|
||||
*/
|
||||
private final HttpClient createHttpClient()
|
||||
{
|
||||
HttpParams params = new BasicHttpParams();
|
||||
// prevent deadlocks caused by network failures
|
||||
params.setParameter("http.socket.timeout", 300000);
|
||||
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
|
||||
HttpProtocolParams.setContentCharset(params, "UTF-8");
|
||||
HttpProtocolParams.setUseExpectContinue(params, true);
|
||||
HttpProtocolParams.setHttpElementCharset(params, "UTF-8");
|
||||
HttpProtocolParams
|
||||
.setUserAgent(
|
||||
params,
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/2008052906 Firefox/3.0");
|
||||
|
||||
// Register the "http" and "https" protocol schemes, they are
|
||||
// required by the default operator to look up socket factories.
|
||||
SchemeRegistry supportedSchemes = new SchemeRegistry();
|
||||
SocketFactory sf = PlainSocketFactory.getSocketFactory();
|
||||
supportedSchemes.register(new Scheme("http", sf, 80));
|
||||
sf = SSLSocketFactory.getSocketFactory();
|
||||
supportedSchemes.register(new Scheme("https", sf, 443));
|
||||
|
||||
ClientConnectionManager connManager = new ThreadSafeClientConnManager(
|
||||
params, supportedSchemes);
|
||||
DefaultHttpClient httpClient = new DefaultHttpClient(connManager,
|
||||
params);
|
||||
httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY,
|
||||
CookiePolicy.BROWSER_COMPATIBILITY);
|
||||
httpClient.setRedirectHandler(new DefaultRedirectHandler());
|
||||
httpClient.setCookieStore(new BasicCookieStore());
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to login to Facebook with the given credentials
|
||||
*
|
||||
* @param email
|
||||
* the user email
|
||||
* @param password
|
||||
* the user password
|
||||
* @return true if the session was initiated successfully with the given
|
||||
* credentials
|
||||
* @throws BrokenFacebookProtocolException
|
||||
* if the protocol implementation is broken
|
||||
* @throws IOException
|
||||
* if there's an IO error
|
||||
*/
|
||||
public synchronized boolean login(String email, String password)
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException
|
||||
{
|
||||
if (this.isLoggedIn())
|
||||
throw new IllegalStateException("already logged in");
|
||||
|
||||
HttpGet loginGet = new HttpGet(LOGIN_PAGE_URL);
|
||||
HttpResponse getLoginResponse = httpClient.execute(loginGet);
|
||||
int getLoginStatusCode = getLoginResponse.getStatusLine()
|
||||
.getStatusCode();
|
||||
getLoginResponse.getEntity().consumeContent();
|
||||
if (getLoginStatusCode != 200)
|
||||
throw
|
||||
new BrokenFacebookProtocolException(
|
||||
"Unable to GET Facebook login page");
|
||||
|
||||
// POST credentials
|
||||
HttpPost httpost = new HttpPost(LOGIN_PAGE_URL);
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("email", email));
|
||||
nvps.add(new BasicNameValuePair("pass", password));
|
||||
nvps.add(new BasicNameValuePair("login", ""));
|
||||
httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse postLoginResponse = httpClient.execute(httpost);
|
||||
int postLoginStatusCode = postLoginResponse.getStatusLine()
|
||||
.getStatusCode();
|
||||
String postBody = EntityUtils.toString(postLoginResponse.getEntity());
|
||||
if (postLoginStatusCode >= 400)
|
||||
throw new BrokenFacebookProtocolException("Code "
|
||||
+ postLoginStatusCode
|
||||
+ ". Unable to POST Facebook login credentials.");
|
||||
|
||||
String uid = parseUid(postBody);
|
||||
String channel = parseChannel(postBody);
|
||||
String formId = parseFormId(postBody);
|
||||
|
||||
// Parse popout.php if needed
|
||||
if (uid == null || channel == null || formId == null)
|
||||
{
|
||||
HttpGet channelGet = new HttpGet(POPOUT_PAGE_URL);
|
||||
HttpResponse channelResponse = httpClient.execute(channelGet);
|
||||
String channelBody = EntityUtils.toString(channelResponse
|
||||
.getEntity());
|
||||
if (channel == null)
|
||||
channel = parseChannel(channelBody);
|
||||
if (formId == null)
|
||||
formId = parseFormId(channelBody);
|
||||
}
|
||||
|
||||
// Parse the HOME page if needed
|
||||
if (uid == null || channel == null || formId == null)
|
||||
{
|
||||
HttpGet homeGet = new HttpGet(HOME_PAGE_URL);
|
||||
HttpResponse homeResponse = httpClient.execute(homeGet);
|
||||
String homeBody = EntityUtils.toString(homeResponse.getEntity());
|
||||
if (uid == null)
|
||||
uid = parseUid(homeBody);
|
||||
if (channel == null)
|
||||
channel = parseChannel(homeBody);
|
||||
if (formId == null)
|
||||
formId = parseFormId(homeBody);
|
||||
}
|
||||
|
||||
if (uid != null && channel != null && formId != null)
|
||||
{
|
||||
this.uid = uid;
|
||||
this.channel = channel;
|
||||
this.formId = formId;
|
||||
|
||||
// update state
|
||||
this.loggedIn = true;
|
||||
|
||||
// start refreshing threads
|
||||
this.buddyListRefresher = new BuddyListRefresher(this);
|
||||
Thread buddyListThread = new Thread(this.buddyListRefresher,
|
||||
"facebook buddy list refresher " + this.hashCode());
|
||||
buddyListThread.start();
|
||||
this.pollerRefresher = new PollerRefresher(this);
|
||||
Thread pollerRefresher = new Thread(this.pollerRefresher,
|
||||
"facebook messages refresher " + this.hashCode());
|
||||
pollerRefresher.start();
|
||||
|
||||
// return result
|
||||
return this.loggedIn;
|
||||
}
|
||||
else if (uid != null | channel != null | formId != null)
|
||||
{
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"One of the session elements could not be read from the home page. UID: "
|
||||
+ uid + " - CHANNEL: " + channel + " - FORMID: "
|
||||
+ formId);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.loggedIn = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void reconnect()
|
||||
throws BrokenFacebookProtocolException,
|
||||
IOException,
|
||||
FacebookErrorException
|
||||
{
|
||||
HttpGet get = new HttpGet(RECONNECT_URL + "?reason=3&post_form_id="
|
||||
+ this.formId);
|
||||
HttpResponse response = httpClient.execute(get);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
new FacebookJsonResponse(this, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout the {@link FacebookSession} by stopping the threads and by
|
||||
* requesting Facebook server to logout<br>
|
||||
* It clears the {@link FacebookBuddyList} and notifies to registered
|
||||
* listeners that every contact is OFFLINE.
|
||||
*/
|
||||
public synchronized void logout()
|
||||
{
|
||||
if (this.buddyListRefresher != null)
|
||||
this.buddyListRefresher.setKeepRunning(false);
|
||||
if (this.pollerRefresher != null)
|
||||
this.pollerRefresher.setKeepRunning(false);
|
||||
|
||||
this.buddyList.clear();
|
||||
for (FacebookSessionListener l : this.listeners)
|
||||
l.onBuddyListUpdated();
|
||||
|
||||
if (this.loggedIn)
|
||||
{
|
||||
this.loggedIn = false;
|
||||
for (FacebookSessionListener l : this.listeners)
|
||||
l.onFacebookConnectionLost();
|
||||
try
|
||||
{
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("confirm", "1"));
|
||||
HttpPost post = new HttpPost(LOGOUT_URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = httpClient.execute(post);
|
||||
response.getEntity().consumeContent();
|
||||
if (response.getStatusLine().getStatusCode() != 200) {
|
||||
logger.warn("unable to send logout command");
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn("IOException during logout", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the UID from the home page
|
||||
*
|
||||
* @param body
|
||||
* the HTML body
|
||||
* @return the UID
|
||||
*/
|
||||
private static String parseUid(String body)
|
||||
{
|
||||
return parse(body, UID_PATTERN, 15, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the channel from the home page
|
||||
*
|
||||
* @param body
|
||||
* the HTML body
|
||||
* @return the Channel
|
||||
*/
|
||||
private static String parseChannel(String body)
|
||||
{
|
||||
return parse(body, CHANNEL_PATTERN, 8, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the form id from the home page
|
||||
*
|
||||
* @param body
|
||||
* the HTML body
|
||||
* @return the form id
|
||||
*/
|
||||
private static String parseFormId(String body)
|
||||
{
|
||||
return parse(body, FORMID_PATTERN, 66, 4);
|
||||
}
|
||||
|
||||
private static String parse(
|
||||
String body,
|
||||
Pattern pattern,
|
||||
int beginIndex,
|
||||
int excludedEndLength)
|
||||
{
|
||||
Matcher matcher = pattern.matcher(body);
|
||||
boolean found = matcher.find();
|
||||
if (found)
|
||||
{
|
||||
String str = body.substring(matcher.start(), matcher.end());
|
||||
str = str.substring(beginIndex, str.length() - excludedEndLength);
|
||||
|
||||
/*
|
||||
* String#substring(int, int) has created a reference to the
|
||||
* internal char array of body. But body is huge and is not
|
||||
* necessary once the data of interest has been extracted from it.
|
||||
* So make sure that the internal char array of body is not
|
||||
* referenced.
|
||||
*/
|
||||
return new String(str);
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getUid()
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
||||
public String getChannel()
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
|
||||
public String getFormId()
|
||||
{
|
||||
return formId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visibility.
|
||||
*
|
||||
* @param isVisible
|
||||
* true(visible) or false(invisible)
|
||||
* @throws IOException
|
||||
*/
|
||||
public void setVisibility(boolean isVisible)
|
||||
throws IOException,
|
||||
BrokenFacebookProtocolException
|
||||
{
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
nvps.add(new BasicNameValuePair("visibility", isVisible + ""));
|
||||
nvps.add(new BasicNameValuePair("post_form_id", this.formId));
|
||||
HttpPost post = new HttpPost(SETTINGS_URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = this.httpClient.execute(post);
|
||||
response.getEntity().consumeContent();
|
||||
if (response.getStatusLine().getStatusCode() != 200)
|
||||
throw new BrokenFacebookProtocolException(
|
||||
"unable to set visibility");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status messsage
|
||||
*
|
||||
* @param statusMsg
|
||||
* @throws IOException
|
||||
*/
|
||||
public void setStatusMessage(String statusMsg) throws IOException
|
||||
{
|
||||
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
if (statusMsg.length() < 1)
|
||||
nvps.add(new BasicNameValuePair("clear", "1"));
|
||||
else
|
||||
nvps.add(new BasicNameValuePair("status", statusMsg));
|
||||
nvps.add(new BasicNameValuePair("profile_id", this.formId));
|
||||
nvps.add(new BasicNameValuePair("home_tab_id", "1"));
|
||||
nvps.add(new BasicNameValuePair("test_name", "INLINE_STATUS_EDITOR"));
|
||||
nvps.add(new BasicNameValuePair("action", "HOME_UPDATE"));
|
||||
nvps.add(new BasicNameValuePair("post_form_id", this.formId));
|
||||
HttpPost post = new HttpPost(UPDATE_STATUS_URL);
|
||||
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
|
||||
HttpResponse response = this.httpClient.execute(post);
|
||||
response.getEntity().consumeContent();
|
||||
}
|
||||
|
||||
HttpClient getHttpClient()
|
||||
{
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
public boolean isLoggedIn()
|
||||
{
|
||||
return loggedIn;
|
||||
}
|
||||
|
||||
public FacebookBuddyList getBuddyList()
|
||||
{
|
||||
return buddyList;
|
||||
}
|
||||
|
||||
public FacebookUser getMetaInfo()
|
||||
{
|
||||
return this.buddyList.getMyMetaInfo();
|
||||
}
|
||||
|
||||
public FacebookIncomingMessagePoller getPoller()
|
||||
{
|
||||
return poller;
|
||||
}
|
||||
|
||||
public void addListener(FacebookSessionListener listener)
|
||||
{
|
||||
this.listeners.add(listener);
|
||||
this.poller.addListener(listener);
|
||||
this.buddyList.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the profile page for parsing. It's invoked when user opens contact
|
||||
* info box.
|
||||
*
|
||||
* @param contactAddress
|
||||
* the contact address
|
||||
* @return profile page string
|
||||
*/
|
||||
public String getProfilePage(String contactAddress)
|
||||
{
|
||||
// TODO if homePageUrl.contains("new.facebook.com") return;
|
||||
// because if we try to get the "new" page, the account's layout would
|
||||
// be set to new style.
|
||||
// Someone may not like this.
|
||||
|
||||
// http://www.new.facebook.com/profile.php?id=1190346972&v=info&viewas=1190346972
|
||||
// http://www.new.facebook.com/profile.php?id=1386786477&v=info
|
||||
|
||||
// facebookGetMethod(hostUrlNew + "/profile.php?id=" + contactAddress +
|
||||
// "&v=info") ;
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable to poll messages<br>
|
||||
* It keeps polling for new messages until the corresponding
|
||||
* {@link FacebookSession} is logged out
|
||||
*/
|
||||
private static class PollerRefresher
|
||||
implements Runnable
|
||||
{
|
||||
private final FacebookSession session;
|
||||
/**
|
||||
* Flag which indicates if the current runnable should keep running
|
||||
*/
|
||||
private boolean keepRunning = true;
|
||||
private int retries = 0;
|
||||
|
||||
public PollerRefresher(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
Thread t = Thread.currentThread();
|
||||
while (keepRunning & this.session.isLoggedIn() & t.isAlive()
|
||||
& !t.isInterrupted())
|
||||
{
|
||||
try
|
||||
{
|
||||
this.session.getPoller().poll();
|
||||
this.retries = 0;
|
||||
}
|
||||
catch (BrokenFacebookProtocolException e)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.session.logout();
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.retries > 3)
|
||||
{
|
||||
this.session.logout();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.retries++;
|
||||
Thread.sleep(3000);
|
||||
}
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
throw new RuntimeException(e1);
|
||||
}
|
||||
}
|
||||
catch (FacebookErrorException e)
|
||||
{
|
||||
// FIXME handle facebook error exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setKeepRunning(boolean keepRunning)
|
||||
{
|
||||
this.keepRunning = keepRunning;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable to refresh the buddy list.<br>
|
||||
* It refreshes the buddy list each minute until the corresponding
|
||||
* {@link FacebookSession} is logged out
|
||||
*/
|
||||
private static class BuddyListRefresher
|
||||
implements Runnable
|
||||
{
|
||||
private final FacebookSession session;
|
||||
/**
|
||||
* Flag which indicates if the current runnable should keep running
|
||||
*/
|
||||
private boolean keepRunning = true;
|
||||
private int retries = 0;
|
||||
|
||||
public BuddyListRefresher(FacebookSession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
Thread t = Thread.currentThread();
|
||||
|
||||
while (keepRunning & this.session.isLoggedIn() & t.isAlive()
|
||||
& !t.isInterrupted())
|
||||
{
|
||||
try
|
||||
{
|
||||
this.session.getBuddyList().update();
|
||||
this.retries = 0;
|
||||
}
|
||||
catch (BrokenFacebookProtocolException e)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.session.logout();
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
}
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
if (this.retries > 3)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.session.logout();
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
throw new RuntimeException(e1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.retries++;
|
||||
}
|
||||
}
|
||||
catch (FacebookErrorException e)
|
||||
{
|
||||
// FIXME handle exception
|
||||
}
|
||||
try
|
||||
{
|
||||
Thread.sleep(59 * 1000);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setKeepRunning(boolean keepRunning)
|
||||
{
|
||||
this.keepRunning = keepRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
/**
|
||||
* Listeners are notified of events occurred to the {@link FacebookSession}.
|
||||
*
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public interface FacebookSessionListener
|
||||
{
|
||||
|
||||
/**
|
||||
* Callback method which notifies when the connection is lost
|
||||
*/
|
||||
public void onFacebookConnectionLost();
|
||||
|
||||
/**
|
||||
* Callback method which notifies when the buddy list is updated
|
||||
*/
|
||||
public void onBuddyListUpdated();
|
||||
|
||||
/**
|
||||
* Callback method which notifies that a new {@link FacebookMessage} arrived
|
||||
*
|
||||
* @param message
|
||||
* the new {@link FacebookMessage}
|
||||
*/
|
||||
public void onIncomingChatMessage(FacebookMessage message);
|
||||
|
||||
/**
|
||||
* Callback method which notifies that a new typing notification arrived
|
||||
*
|
||||
* @param buddyUid
|
||||
* the buddy UID
|
||||
* @param state
|
||||
* the typing state
|
||||
*/
|
||||
public void onIncomingTypingNotification(String buddyUid, int state);
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* An implementation of <tt>PresenceStatus</tt> that enumerates all states
|
||||
* that a Facebook contact can fall into. There are only to status currently:<br>
|
||||
* <ol>
|
||||
* <li>Online</li>
|
||||
* <li>Offline</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class FacebookStatusEnum
|
||||
extends PresenceStatus
|
||||
{
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(FacebookStatusEnum.class);
|
||||
|
||||
/**
|
||||
* The Online status. Indicate that the user is able and willing to
|
||||
* communicate.
|
||||
*/
|
||||
public static final FacebookStatusEnum ONLINE =
|
||||
new FacebookStatusEnum(65, "Online",
|
||||
getImageInBytes("service.protocol.facebook.FACEBOOK_16x16"));
|
||||
|
||||
/**
|
||||
* The Invisible status. Indicates that the user has connectivity even
|
||||
* though it may appear otherwise to others, to whom she would appear to
|
||||
* be offline.
|
||||
*/
|
||||
|
||||
public static final FacebookStatusEnum INVISIBLE =
|
||||
new FacebookStatusEnum(45, "Invisible",
|
||||
getImageInBytes("service.protocol.facebook.INVISIBLE_STATUS_ICON"));
|
||||
|
||||
/**
|
||||
* The Idle status. Friends are considered idle when they have not taken
|
||||
* any action on the site in the last 10 minutes.
|
||||
*/
|
||||
public static final FacebookStatusEnum IDLE =
|
||||
new FacebookStatusEnum(40, "Idle",
|
||||
getImageInBytes("service.protocol.facebook.IDLE_STATUS_ICON"));
|
||||
/**
|
||||
* Indicates an Offline status or status with 0 connectivity.
|
||||
*/
|
||||
public static final FacebookStatusEnum OFFLINE =
|
||||
new FacebookStatusEnum(0, "Offline",
|
||||
getImageInBytes("service.protocol.facebook.OFFLINE_STATUS_ICON"));
|
||||
|
||||
/**
|
||||
* An Occupied status. Indicates that the user has connectivity and
|
||||
* communication is particularly unwanted.
|
||||
*/
|
||||
/*
|
||||
* public static final FacebookStatusEnum OCCUPIED = new FacebookStatusEnum(
|
||||
* 20 , "Occupied" , getImageInBytes("facebookOccupiedIcon"));
|
||||
*
|
||||
*//**
|
||||
* The DND status. Indicates that the user has connectivity but prefers
|
||||
* not to be contacted.
|
||||
*/
|
||||
/*
|
||||
* public static final FacebookStatusEnum DO_NOT_DISTURB = new
|
||||
* FacebookStatusEnum( 30 , "Do Not Disturb",
|
||||
* getImageInBytes("facebookDndIcon"));
|
||||
*
|
||||
*//**
|
||||
* The Not Available status. Indicates that the user has connectivity
|
||||
* but might not be able to immediately act (i.e. even less immediately
|
||||
* than when in an Away status ;-P ) upon initiation of communication.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* public static final FacebookStatusEnum NOT_AVAILABLE = new
|
||||
* FacebookStatusEnum( 35 , "Not Available" ,
|
||||
* getImageInBytes("facebookNaIcon"));
|
||||
*
|
||||
*//**
|
||||
* The Away status. Indicates that the user has connectivity but might
|
||||
* not be able to immediately act upon initiation of communication.
|
||||
*/
|
||||
/*
|
||||
* public static final FacebookStatusEnum AWAY = new FacebookStatusEnum( 40 ,
|
||||
* "Away" , getImageInBytes("facebookAwayIcon"));
|
||||
*
|
||||
*//**
|
||||
* The Free For Chat status. Indicates that the user is eager to
|
||||
* communicate.
|
||||
*/
|
||||
/*
|
||||
* public static final FacebookStatusEnum FREE_FOR_CHAT = new
|
||||
* FacebookStatusEnum( 85 , "Free For Chat" ,
|
||||
* getImageInBytes("facebookFfcIcon"));
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize the list of supported status states.
|
||||
*/
|
||||
private static final List<PresenceStatus> supportedStatusSet
|
||||
= new LinkedList<PresenceStatus>();
|
||||
static
|
||||
{
|
||||
supportedStatusSet.add(ONLINE);
|
||||
supportedStatusSet.add(IDLE);
|
||||
supportedStatusSet.add(INVISIBLE);
|
||||
supportedStatusSet.add(OFFLINE);
|
||||
/*
|
||||
* supportedStatusSet.add(OCCUPIED);
|
||||
* supportedStatusSet.add(DO_NOT_DISTURB);
|
||||
* supportedStatusSet.add(NOT_AVAILABLE); supportedStatusSet.add(AWAY);
|
||||
*
|
||||
* supportedStatusSet.add(FREE_FOR_CHAT);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of <tt>FacebookPresneceStatus</tt> with the
|
||||
* specified parameters.
|
||||
*
|
||||
* @param status the connectivity level of the new presence status instance
|
||||
* @param statusName the name of the presence status.
|
||||
* @param statusIcon the icon associated with this status
|
||||
*/
|
||||
private FacebookStatusEnum(int status, String statusName, byte[] statusIcon)
|
||||
{
|
||||
super(status, statusName, statusIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over all status instances supproted by the facebook
|
||||
* provider.
|
||||
*
|
||||
* @return an <tt>Iterator</tt> over all status instances supported by the
|
||||
* facebook provider.
|
||||
*/
|
||||
static Iterator<PresenceStatus> supportedStatusSet()
|
||||
{
|
||||
return supportedStatusSet.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte representation of the image corresponding to the given
|
||||
* identifier.
|
||||
*
|
||||
* @param imageID the identifier of the image
|
||||
* @return the byte representation of the image corresponding to the given
|
||||
* identifier.
|
||||
*/
|
||||
private static byte[] getImageInBytes(String imageID)
|
||||
{
|
||||
InputStream in =
|
||||
FacebookActivator.getResources().getImageInputStream(imageID);
|
||||
|
||||
if (in == null)
|
||||
return null;
|
||||
byte[] image = null;
|
||||
try
|
||||
{
|
||||
image = new byte[in.available()];
|
||||
|
||||
in.read(image);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.error("Failed to load image:" + imageID, e);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.json.*;
|
||||
|
||||
/**
|
||||
* A data structure that store Facebook user's information
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class FacebookUser
|
||||
{
|
||||
public static String defaultThumbSrc = "http://static.ak.fbcdn.net/pics/q_silhouette.gif";
|
||||
|
||||
public static String defaultAvatarSrc = "http://static.ak.fbcdn.net/pics/d_silhouette.gif";
|
||||
|
||||
public String uid;
|
||||
|
||||
public boolean isIdle;
|
||||
|
||||
public String name;
|
||||
|
||||
public String firstName;
|
||||
|
||||
public String thumbSrc;
|
||||
|
||||
public String status;
|
||||
|
||||
public Number statusTime;
|
||||
|
||||
public String statusTimeRel;
|
||||
|
||||
public long lastSeen;
|
||||
|
||||
public boolean isOnline;
|
||||
|
||||
/**
|
||||
* Creat a facebook user according to the given id and JSONObject.
|
||||
*
|
||||
* @param id
|
||||
* @param user
|
||||
* @throws JSONException
|
||||
*/
|
||||
public FacebookUser(String id, JSONObject user) throws JSONException
|
||||
{
|
||||
uid = id;
|
||||
isIdle = true;// default status is idle
|
||||
name = (String) user.get("name");
|
||||
firstName = (String) user.get("firstName");
|
||||
thumbSrc = (String) user.get("thumbSrc");
|
||||
if (user.has("status"))
|
||||
{
|
||||
status = user.getString("status");
|
||||
}
|
||||
else
|
||||
{
|
||||
status = "";
|
||||
}
|
||||
if (user.has("statusTime"))
|
||||
{
|
||||
statusTime = (Number) user.get("statusTime");
|
||||
}
|
||||
else
|
||||
{
|
||||
statusTime = -1;
|
||||
}
|
||||
if (user.has("statusTimeRel"))
|
||||
{
|
||||
statusTimeRel = (String) user.get("statusTimeRel");
|
||||
}
|
||||
else
|
||||
{
|
||||
statusTimeRel = "";
|
||||
}
|
||||
lastSeen = new Date().getTime();
|
||||
isOnline = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "FacebookUser [firstName=" + firstName + ", isIdle=" + isIdle
|
||||
+ ", isOnline=" + isOnline + ", lastSeen=" + lastSeen
|
||||
+ ", name=" + name + ", status=" + status + ", statusTime="
|
||||
+ statusTime + ", statusTimeRel=" + statusTimeRel
|
||||
+ ", thumbSrc=" + thumbSrc + ", uid=" + uid + "]";
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
|
||||
/**
|
||||
* Very simple message implementation for the Facebook protocol.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Edgar Poce
|
||||
*/
|
||||
public class MessageFacebookImpl
|
||||
extends AbstractMessage
|
||||
{
|
||||
public MessageFacebookImpl(
|
||||
String content,
|
||||
String contentType,
|
||||
String encoding,
|
||||
String subject)
|
||||
{
|
||||
super(content, contentType, encoding, subject);
|
||||
}
|
||||
}
|
||||
@ -1,265 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Instant messaging functionality for the Facebook protocol.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
* @author Lubomir Marinov
|
||||
*/
|
||||
public class OperationSetBasicInstantMessagingFacebookImpl
|
||||
extends AbstractOperationSetBasicInstantMessaging
|
||||
{
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(OperationSetBasicInstantMessagingFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* The currently valid persistent presence operation set..
|
||||
*/
|
||||
private OperationSetPersistentPresenceFacebookImpl opSetPersPresence = null;
|
||||
|
||||
/**
|
||||
* The protocol provider that created us.
|
||||
*/
|
||||
private ProtocolProviderServiceFacebookImpl parentProvider = null;
|
||||
|
||||
/**
|
||||
* Creates an instance of this operation set keeping a reference to the
|
||||
* parent protocol provider and presence operation set.
|
||||
*
|
||||
* @param provider The provider instance that creates us.
|
||||
* @param opSetPersPresence the currently valid
|
||||
* <tt>OperationSetPersistentPresenceFacebookImpl</tt>
|
||||
* instance.
|
||||
*/
|
||||
public OperationSetBasicInstantMessagingFacebookImpl(
|
||||
ProtocolProviderServiceFacebookImpl provider,
|
||||
OperationSetPersistentPresenceFacebookImpl opSetPersPresence)
|
||||
{
|
||||
this.opSetPersPresence = opSetPersPresence;
|
||||
this.parentProvider = provider;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements
|
||||
* AbstractOperationSetBasicInstantMessaging#createMessage(String, String,
|
||||
* String, String). Creates a new MessageFacebookImpl instance with the
|
||||
* specified properties.
|
||||
*/
|
||||
public Message createMessage(
|
||||
String content, String contentType, String encoding, String subject)
|
||||
{
|
||||
return
|
||||
new MessageFacebookImpl(content, contentType, encoding, subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the <tt>message</tt> to the destination indicated by the
|
||||
* <tt>to</tt> contact.
|
||||
*
|
||||
* @param to the <tt>Contact</tt> to send <tt>message</tt> to
|
||||
* @param message the <tt>Message</tt> to send.
|
||||
* @throws IllegalStateException if the underlying ICQ stack is not
|
||||
* registered and initialized.
|
||||
* @throws IllegalArgumentException if <tt>to</tt> is not an instance
|
||||
* belonging to the underlying implementation.
|
||||
*/
|
||||
public void sendInstantMessage(final Contact to, final Message message)
|
||||
throws IllegalStateException,
|
||||
IllegalArgumentException
|
||||
{
|
||||
if (!(to instanceof ContactFacebookImpl))
|
||||
throw new IllegalArgumentException(
|
||||
"The specified contact is not a Facebook contact." + to);
|
||||
|
||||
Thread sender = new Thread(new Runnable()
|
||||
{
|
||||
|
||||
public void run()
|
||||
{
|
||||
// deliver the facebook chat Message
|
||||
MessageDeliveryFailedEvent errorEvent = null;
|
||||
errorEvent =
|
||||
OperationSetBasicInstantMessagingFacebookImpl.this.parentProvider
|
||||
.getAdapter().postFacebookChatMessage(message, to);
|
||||
if (errorEvent == null)
|
||||
{
|
||||
fireMessageDelivered(message, to);
|
||||
return;
|
||||
}
|
||||
if(errorEvent.getErrorCode() == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| errorEvent.getErrorCode() == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister(RegistrationStateChangeEvent.REASON_MULTIPLE_LOGINS);
|
||||
}
|
||||
catch (OperationFailedException e1)
|
||||
{
|
||||
logger.error(
|
||||
"Unable to unregister the protocol provider: "
|
||||
+ this
|
||||
+ " due to the following exception: " + e1);
|
||||
}
|
||||
fireMessageDeliveryFailed(
|
||||
message,
|
||||
to,
|
||||
MessageDeliveryFailedEvent.PROVIDER_NOT_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
// if the above delivery failed, we try again!
|
||||
try
|
||||
{
|
||||
// wait a moment
|
||||
Thread.sleep(1000);
|
||||
errorEvent =
|
||||
OperationSetBasicInstantMessagingFacebookImpl.this.parentProvider
|
||||
.getAdapter().postFacebookChatMessage(message, to);
|
||||
if (errorEvent == null)
|
||||
{
|
||||
fireMessageDelivered(message, to);
|
||||
return;
|
||||
}
|
||||
if(errorEvent.getErrorCode() == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| errorEvent.getErrorCode() == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister(RegistrationStateChangeEvent.REASON_MULTIPLE_LOGINS);
|
||||
}
|
||||
catch (OperationFailedException e1)
|
||||
{
|
||||
logger.error(
|
||||
"Unable to unregister the protocol provider: "
|
||||
+ this
|
||||
+ " due to the following exception: " + e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
logger.warn(e.getMessage());
|
||||
}
|
||||
// if we get here, we have failed to deliver this message,
|
||||
// twice.
|
||||
fireMessageDeliveryFailed(
|
||||
message,
|
||||
to,
|
||||
MessageDeliveryFailedEvent.UNKNOWN_ERROR);
|
||||
// now do message delivery.----what's this?
|
||||
// in case that the "to" we deliver this message to is
|
||||
// ourselves?
|
||||
// deliverMessageToMyself(message, (ContactFacebookImpl)to);
|
||||
}
|
||||
});
|
||||
sender.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the facebook adapter when we got messages from the server.
|
||||
*
|
||||
* @param fbmsg the received Facebook instant message
|
||||
*/
|
||||
public void receivedInstantMessage(FacebookMessage fbmsg)
|
||||
{
|
||||
Message message = this.createMessage(fbmsg.getText());
|
||||
String fromID = fbmsg.getFrom();
|
||||
|
||||
// TODO handle the msgID.
|
||||
// it's generated when we createMessage() by now.
|
||||
// We should set the message id according to the fbmsg.msgID.
|
||||
// But it's not important.
|
||||
|
||||
Contact fromContact = opSetPersPresence.findContactByID(fromID);
|
||||
if (fromContact == null)
|
||||
{
|
||||
// from facebook user who are not on our contact list
|
||||
// TODO creat volatile contact, fire event.
|
||||
fromContact = opSetPersPresence.createVolatileContact(fromID);
|
||||
// opSetPersPresence.subscribe(fromID);
|
||||
}
|
||||
fireMessageReceived(message, fromContact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered message listeners that a message has been
|
||||
* delivered successfully to its addressee..
|
||||
*
|
||||
* @param message
|
||||
* the <tt>Message</tt> that has been delivered.
|
||||
* @param to
|
||||
* the <tt>Contact</tt> that <tt>message</tt> was delivered to.
|
||||
*/
|
||||
protected void fireMessageDelivered(Message message, Contact to)
|
||||
{
|
||||
// we succeeded in sending a message to contact "to",
|
||||
// so we know he is online
|
||||
// but -- if we support the "invisible" status,
|
||||
// this line could be commented
|
||||
opSetPersPresence
|
||||
.setPresenceStatusForContact(
|
||||
(ContactFacebookImpl) to,
|
||||
FacebookStatusEnum.ONLINE);
|
||||
|
||||
super.fireMessageDelivered(message, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered message listeners that a message has been
|
||||
* received.
|
||||
*
|
||||
* @param message the <tt>Message</tt> that has been received.
|
||||
* @param from the <tt>Contact</tt> that <tt>message</tt> was received
|
||||
* from.
|
||||
*/
|
||||
protected void fireMessageReceived(Message message, Contact from)
|
||||
{
|
||||
// we got a message from contact "from", so we know he is online
|
||||
opSetPersPresence
|
||||
.setPresenceStatusForContact(
|
||||
(ContactFacebookImpl) from,
|
||||
FacebookStatusEnum.ONLINE);
|
||||
|
||||
super.fireMessageReceived(message, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the protocol provider (or the protocol itself) support
|
||||
* sending and receiving offline messages. Most often this method would
|
||||
* return true for protocols that support offline messages and false for
|
||||
* those that don't. It is however possible for a protocol to support these
|
||||
* messages and yet have a particular account that does not (i.e. feature
|
||||
* not enabled on the protocol server). In cases like this it is possible
|
||||
* for this method to return true even when offline messaging is not
|
||||
* supported, and then have the sendMessage method throw an
|
||||
* OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED.
|
||||
*
|
||||
* @return <tt>true</tt> if the protocol supports offline messages and
|
||||
* <tt>false</tt> otherwise.
|
||||
*/
|
||||
public boolean isOfflineMessagingSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines wheter the protocol supports the supplied content type
|
||||
*
|
||||
* @param contentType the type we want to check
|
||||
* @return <tt>true</tt> if the protocol supports it and <tt>false</tt>
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isContentTypeSupported(String contentType)
|
||||
{
|
||||
return contentType.equals(DEFAULT_MIME_TYPE);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,475 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.imageio.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.ServerStoredDetails.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Operation Set Server Stored Contact Info Facebook Implementation
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class OperationSetServerStoredContactInfoFacebookImpl
|
||||
implements OperationSetServerStoredContactInfo
|
||||
{
|
||||
/**
|
||||
* Our class logger
|
||||
*/
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(OperationSetServerStoredContactInfoFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* A callback to the Facebook provider that created us.
|
||||
*/
|
||||
private final ProtocolProviderServiceFacebookImpl parentProvider;
|
||||
|
||||
/**
|
||||
* All the details retrieved so far is kept here
|
||||
*/
|
||||
private final Map<String, List<GenericDetail>> retreivedDetails
|
||||
= new Hashtable<String, List<GenericDetail>>();
|
||||
|
||||
/**
|
||||
* Details retreived addresses
|
||||
*/
|
||||
private final Set<String> detailsRetreivedAddresses = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* Host address for retreiving images
|
||||
*/
|
||||
private static String fileHostUrl = "http://static.ak.fbcdn.net";
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class using the specified parent
|
||||
* <tt>provider</tt>.
|
||||
*
|
||||
* @param provider the provider that's creating us.
|
||||
*/
|
||||
protected OperationSetServerStoredContactInfoFacebookImpl(
|
||||
ProtocolProviderServiceFacebookImpl provider)
|
||||
{
|
||||
this.parentProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user details from the specified class or its descendants
|
||||
* the class is one from the
|
||||
* net.java.sip.communicator.service.protocol.ServerStoredDetails
|
||||
* or implemented one in the operation set for the user info
|
||||
*
|
||||
* @param contact Contact
|
||||
* @param detailClass Class
|
||||
* @return Iterator
|
||||
*/
|
||||
public Iterator<GenericDetail> getDetailsAndDescendants(
|
||||
Contact contact,
|
||||
Class<? extends GenericDetail> detailClass)
|
||||
{
|
||||
List<GenericDetail> details = getContactDetails(contact.getAddress());
|
||||
List<GenericDetail> result = new LinkedList<GenericDetail>();
|
||||
|
||||
if (details == null)
|
||||
return result.iterator();
|
||||
|
||||
for (GenericDetail item : details)
|
||||
//the class AND its descendants
|
||||
if (detailClass.isInstance(item))
|
||||
result.add(item);
|
||||
|
||||
return result.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user details from the specified class
|
||||
* exactly that class not its descendants
|
||||
*
|
||||
* @param contact Contact
|
||||
* @param detailClass Class
|
||||
* @return Iterator
|
||||
*/
|
||||
public Iterator<GenericDetail> getDetails(
|
||||
Contact contact,
|
||||
Class<? extends GenericDetail> detailClass)
|
||||
{
|
||||
List<GenericDetail> details = getContactDetails(contact.getAddress());
|
||||
List<GenericDetail> result = new LinkedList<GenericDetail>();
|
||||
|
||||
if (details == null)
|
||||
return result.iterator();
|
||||
|
||||
for (GenericDetail item : details)
|
||||
//exactly that class not its descendants
|
||||
if (detailClass.equals(item.getClass()))
|
||||
result.add(item);
|
||||
|
||||
return result.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the full info for the given uin
|
||||
* waits and return this details
|
||||
*
|
||||
* @param contact Contact
|
||||
* @return Iterator
|
||||
*/
|
||||
public Iterator<GenericDetail> getAllDetailsForContact(Contact contact)
|
||||
{
|
||||
List<GenericDetail> details = getContactDetails(contact.getAddress());
|
||||
|
||||
if(details == null)
|
||||
return new LinkedList<GenericDetail>().iterator();
|
||||
else
|
||||
return new LinkedList<GenericDetail>(details).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the full info for the given contactAddress
|
||||
* waits and return this details
|
||||
*
|
||||
* @param contactAddress String
|
||||
* @return Vector the details
|
||||
*/
|
||||
private List<GenericDetail> getContactDetails(String contactAddress)
|
||||
{
|
||||
List<GenericDetail> result = retreivedDetails.get(contactAddress);
|
||||
|
||||
if ((result == null)
|
||||
|| !detailsRetreivedAddresses.contains(contactAddress))
|
||||
{
|
||||
result = new LinkedList<GenericDetail>();
|
||||
try
|
||||
{
|
||||
/**
|
||||
* Summary:
|
||||
* First Name;
|
||||
* Middle Name;
|
||||
* Last Name;
|
||||
* Gender;
|
||||
* Birth Date;
|
||||
* Age;
|
||||
* E-mail;
|
||||
* Phone..
|
||||
*
|
||||
* Extended:
|
||||
* ...
|
||||
*/
|
||||
FacebookAdapter adapter = parentProvider.getAdapter();
|
||||
OperationSetPersistentPresenceFacebookImpl presenceOS
|
||||
= getParentPresenceOperationSet();
|
||||
|
||||
if(adapter == null || presenceOS == null)
|
||||
return null;
|
||||
|
||||
String tmpValueStr = "Who Am I";
|
||||
ContactFacebookImpl contact = (ContactFacebookImpl)
|
||||
presenceOS.findContactByID(contactAddress);
|
||||
|
||||
//avatar, name, first name
|
||||
if(contact != null)
|
||||
{
|
||||
/*
|
||||
* TODO fixme "Avatar" should be loaded from resources.
|
||||
* properties
|
||||
*/
|
||||
byte[] imageBytes = contact.getBigImage();
|
||||
if(imageBytes != null && imageBytes.length > 0)
|
||||
result.add(new ServerStoredDetails.ImageDetail(
|
||||
"Avatar", imageBytes));
|
||||
|
||||
FacebookUser metaInfo = contact.getContactInfo();
|
||||
if(metaInfo == null)
|
||||
return null;
|
||||
|
||||
tmpValueStr = metaInfo.name;
|
||||
if(tmpValueStr != null)
|
||||
result
|
||||
.add(
|
||||
new ServerStoredDetails.DisplayNameDetail(
|
||||
tmpValueStr));
|
||||
|
||||
tmpValueStr = metaInfo.firstName;
|
||||
if(tmpValueStr != null)
|
||||
result
|
||||
.add(
|
||||
new ServerStoredDetails.FirstNameDetail(
|
||||
tmpValueStr));
|
||||
}
|
||||
|
||||
String profilePage
|
||||
= adapter.getSession().getProfilePage(contactAddress);
|
||||
|
||||
if(profilePage == null)
|
||||
throw new Exception("Failed to load profile page");
|
||||
|
||||
logger.trace("====== Profile Page: ======\n" + profilePage);
|
||||
|
||||
/**
|
||||
* @fixme should we fill the summary pannel?
|
||||
*/
|
||||
|
||||
//add this contact into the set,
|
||||
//so that we needn't to fetch his/her info again
|
||||
detailsRetreivedAddresses.add(contactAddress);
|
||||
|
||||
//class="profile_info"><dl class="info">
|
||||
//<div class="profile_info_container">
|
||||
String tmpPrefix = "<div class=\"profile_info_container\">";
|
||||
int beginPos = profilePage.indexOf(tmpPrefix);
|
||||
|
||||
if(beginPos >= 0)
|
||||
{
|
||||
//do something
|
||||
beginPos += tmpPrefix.length();
|
||||
if(beginPos >= profilePage.length())
|
||||
throw new Exception("Failed to load profile page");
|
||||
|
||||
String tmpLabelStr;
|
||||
int tmpLeft = profilePage.indexOf("<dt>", beginPos);
|
||||
int tmpRight = 0;
|
||||
while(tmpLeft >= 0 && tmpLeft < profilePage.length())
|
||||
{
|
||||
tmpRight = profilePage.indexOf("</dt>", tmpLeft);
|
||||
if(tmpRight >= 0)
|
||||
{
|
||||
tmpLabelStr
|
||||
= profilePage.substring(tmpLeft + 4, tmpRight);
|
||||
tmpLabelStr = getText(tmpLabelStr);
|
||||
if(tmpLabelStr.endsWith(":"))
|
||||
tmpLabelStr = tmpLabelStr
|
||||
.substring(0, tmpLabelStr.length()-1);
|
||||
//label done!
|
||||
|
||||
tmpLeft = profilePage.indexOf("<dd>", tmpRight);
|
||||
if(tmpLeft >= 0)
|
||||
{
|
||||
tmpRight
|
||||
= profilePage.indexOf("</dd>", tmpLeft);
|
||||
if(tmpRight >= 0)
|
||||
{
|
||||
tmpValueStr = profilePage.substring(
|
||||
tmpLeft + 4, tmpRight);
|
||||
if(tmpValueStr.startsWith("<img src=\"/")
|
||||
&& tmpValueStr.endsWith("\" border=0>"))
|
||||
{
|
||||
//<img src="/string_image.php?ct=AAAAAQAQVVaveiWcR6O91PkY7zr1NgAAABesyLV-PSZ6liviNVJWiOP6XeTgkFFyaMM%2C&fp=8.7&state=0&highlight=1386786477" border=0>
|
||||
tmpValueStr = tmpValueStr.substring(
|
||||
10, tmpValueStr.length() - 11);
|
||||
//"http://static.ak.fbcdn.net"
|
||||
byte[] imageBytes = getImage(
|
||||
fileHostUrl + tmpValueStr);
|
||||
if(imageBytes != null
|
||||
&& imageBytes.length > 0)
|
||||
result.add(new ServerStoredDetails
|
||||
.ImageDetail(
|
||||
tmpLabelStr, imageBytes));
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpValueStr = getText(tmpValueStr);
|
||||
//value done!
|
||||
result.add(new ServerStoredDetails
|
||||
.StringDetail(
|
||||
tmpLabelStr, tmpValueStr));
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
tmpLeft = profilePage.indexOf("<dt>", tmpRight + 5);
|
||||
}
|
||||
}
|
||||
/*//Gender
|
||||
String tmpPrefix = "<td class=\"data\"><div id=\'Gender-data"
|
||||
+"\'class=\"datawrap\">";
|
||||
String tmpPostfix = "</div>";
|
||||
int beginPos = profilePage.indexOf(tmpPrefix);
|
||||
int endPos;
|
||||
if(beginPos >= 0){
|
||||
endPos = profilePage.indexOf(tmpPostfix, beginPos);
|
||||
if(endPos >= 0){
|
||||
tmp = null;
|
||||
tmp = profilePage.substring(beginPos
|
||||
+ tmpPrefix.length(), endPos);
|
||||
if(tmp != null)
|
||||
result.add(new ServerStoredDetails
|
||||
.GenderDetail(getText(tmp)));
|
||||
}
|
||||
}
|
||||
|
||||
//birthday
|
||||
tmpPrefix = "<td class=\"data\"><div id=\'Birthday-data\'class"
|
||||
+"=\"datawrap\">";
|
||||
tmpPostfix = "</a></div>";
|
||||
beginPos = profilePage.indexOf(tmpPrefix);
|
||||
if(beginPos >= 0){
|
||||
endPos = profilePage.indexOf(tmpPostfix, beginPos);
|
||||
if(endPos >= 0){
|
||||
tmp = null;
|
||||
tmp = profilePage.substring(beginPos
|
||||
+ tmpPrefix.length(), endPos);
|
||||
if(tmp != null)
|
||||
//@fixme birthday label
|
||||
result.add(new ServerStoredDetails.StringDetail(
|
||||
"Birthday", getText(tmp)));
|
||||
}
|
||||
}
|
||||
|
||||
//Hometown
|
||||
tmpPrefix = "<td class=\"data\"><div id=\'Hometown-data\'class"
|
||||
+"=\"datawrap\">";
|
||||
tmpPostfix = "</a></div>";
|
||||
beginPos = profilePage.indexOf(tmpPrefix);
|
||||
if(beginPos >= 0){
|
||||
endPos = profilePage.indexOf(tmpPostfix, beginPos);
|
||||
if(endPos >= 0){
|
||||
tmp = null;
|
||||
tmp = profilePage.substring(beginPos
|
||||
+ tmpPrefix.length(), endPos);
|
||||
if(tmp != null)
|
||||
result.add(new ServerStoredDetails
|
||||
.StringDetail("Hometown", getText(tmp)));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
logger.error("Cannot load details for contact "
|
||||
+ contactAddress + " : " + exc.getMessage()
|
||||
, exc);
|
||||
}
|
||||
}
|
||||
|
||||
retreivedDetails.put(contactAddress, result);
|
||||
|
||||
return new LinkedList<GenericDetail>(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the persistent presence operation set that this contact belongs
|
||||
* to.
|
||||
*
|
||||
* @return the <tt>OperationSetPersistentPresenceFacebookImpl</tt> that
|
||||
* this contact belongs to.
|
||||
*/
|
||||
private OperationSetPersistentPresenceFacebookImpl
|
||||
getParentPresenceOperationSet()
|
||||
{
|
||||
return
|
||||
(OperationSetPersistentPresenceFacebookImpl)
|
||||
parentProvider
|
||||
.getOperationSet(OperationSetPersistentPresence.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get text from html formatted string
|
||||
*
|
||||
* @param srcStr the html formatted string
|
||||
* @return the plain text
|
||||
*/
|
||||
public static String getText(String srcStr)
|
||||
{
|
||||
String text = "";
|
||||
|
||||
int left = 0;
|
||||
int cur = srcStr.indexOf("<", left);
|
||||
while(cur >= 0)
|
||||
{
|
||||
text += srcStr.substring(left, cur);
|
||||
left = srcStr.indexOf(">", cur);
|
||||
if(left < 0)
|
||||
break;
|
||||
|
||||
left++;
|
||||
if(left < srcStr.length())
|
||||
{
|
||||
cur = srcStr.indexOf("<", left);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if(left >= 0)
|
||||
text += srcStr.substring(left);
|
||||
|
||||
/*
|
||||
* support for some special symbols, e.g.:
|
||||
* & = &
|
||||
* (space) =
|
||||
* > = >
|
||||
* < = <
|
||||
* " = "
|
||||
* ' = '
|
||||
* © = ©
|
||||
* ® = ®
|
||||
*/
|
||||
text = text.replaceAll("&", "&");
|
||||
text = text.replaceAll(" ", " ");
|
||||
text = text.replaceAll(">", ">");
|
||||
text = text.replaceAll("<", "<");
|
||||
text = text.replaceAll(""", "\"");
|
||||
text = text.replaceAll("'", "'");
|
||||
text = text.replaceAll("©", "©");
|
||||
text = text.replaceAll("®", "®");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bytes of the image at the specified <tt>urlStr</tt> location
|
||||
* or <tt>null</tt> if we fail to retrieve it for some reason.
|
||||
*
|
||||
* @param urlStr
|
||||
*
|
||||
* @return a <tt>byte[]</tt> array containing the bytes of the image at the
|
||||
* specified <tt>urlStr</tt> location or <tt>null</tt> if we fail to
|
||||
* retrieve it for some reason.
|
||||
*/
|
||||
public static byte[] getImage(String urlStr)
|
||||
{
|
||||
ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
|
||||
try
|
||||
{
|
||||
URL url = new URL(urlStr);
|
||||
BufferedImage newAvatar = ImageIO.read(url);
|
||||
|
||||
javax.imageio.ImageIO.write(newAvatar, "PNG", byteArrayOS);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.warn("IOException occured when loading image", e);
|
||||
// OK, we use the defaultAvatar temporarily
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
byteArrayOS.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return byteArrayOS.toByteArray();
|
||||
}
|
||||
}
|
||||
@ -1,230 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
public class OperationSetSmsMessagingFacebookImpl
|
||||
implements OperationSetSmsMessaging
|
||||
{
|
||||
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(OperationSetBasicInstantMessagingFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* Currently registered message listeners.
|
||||
*/
|
||||
private final List<MessageListener> messageListeners
|
||||
= new Vector<MessageListener>();
|
||||
|
||||
/**
|
||||
* The currently valid persistent presence operation set..
|
||||
*/
|
||||
private final OperationSetPersistentPresenceFacebookImpl opSetPersPresence;
|
||||
|
||||
/**
|
||||
* The protocol provider that created us.
|
||||
*/
|
||||
private final ProtocolProviderServiceFacebookImpl parentProvider;
|
||||
|
||||
public OperationSetSmsMessagingFacebookImpl(
|
||||
ProtocolProviderServiceFacebookImpl provider,
|
||||
OperationSetPersistentPresenceFacebookImpl opSetPersPresence)
|
||||
{
|
||||
this.parentProvider = provider;
|
||||
this.opSetPersPresence = opSetPersPresence;
|
||||
}
|
||||
|
||||
public void addMessageListener(MessageListener listener)
|
||||
{
|
||||
if (!messageListeners.contains(listener))
|
||||
messageListeners.add(listener);
|
||||
}
|
||||
|
||||
public Message createMessage(byte[] content, String contentType,
|
||||
String contentEncoding)
|
||||
{
|
||||
return new MessageFacebookImpl(new String(content), contentType,
|
||||
contentEncoding, "A message sent over SIP Communicator");
|
||||
}
|
||||
|
||||
public Message createMessage(String messageText)
|
||||
{
|
||||
return new MessageFacebookImpl(messageText, DEFAULT_MIME_TYPE,
|
||||
DEFAULT_MIME_ENCODING, "A message sent over SIP Communicator");
|
||||
}
|
||||
|
||||
public boolean isContentTypeSupported(String contentType)
|
||||
{
|
||||
if (contentType.equals(DEFAULT_MIME_TYPE))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeMessageListener(MessageListener listener)
|
||||
{
|
||||
messageListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* send as a message
|
||||
*
|
||||
*/
|
||||
public void sendSmsMessage(final Contact to, final Message message)
|
||||
throws IllegalStateException,
|
||||
IllegalArgumentException
|
||||
{
|
||||
if (!(to instanceof ContactFacebookImpl))
|
||||
throw new IllegalArgumentException(
|
||||
"The specified contact is not a Facebook contact." + to);
|
||||
|
||||
Thread sender = new Thread(new Runnable()
|
||||
{
|
||||
|
||||
public void run()
|
||||
{
|
||||
// deliver the facebook chat Message
|
||||
MessageDeliveryFailedEvent errorEvent = null;
|
||||
errorEvent =
|
||||
OperationSetSmsMessagingFacebookImpl.this.parentProvider
|
||||
.getAdapter().postMessage(message, to);
|
||||
if (errorEvent == null)
|
||||
{
|
||||
fireMessageDelivered(message, to);
|
||||
return;
|
||||
}
|
||||
// if we got the message:
|
||||
// Error(1357001): Not Logged In; You must be logged in to do that.
|
||||
// we just unregister.
|
||||
// and the reason we got this message may be "multiple logins"
|
||||
if(errorEvent.getErrorCode() == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| errorEvent.getErrorCode() == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister(RegistrationStateChangeEvent.REASON_MULTIPLE_LOGINS);
|
||||
}
|
||||
catch (OperationFailedException e1)
|
||||
{
|
||||
logger.error(
|
||||
"Unable to unregister the protocol provider: "
|
||||
+ this
|
||||
+ " due to the following exception: " + e1);
|
||||
}
|
||||
fireMessageDeliveryFailed(message, to, errorEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
// if the above delivery failed, we try again!
|
||||
// coding like this is so ugly! God, forgive me!
|
||||
try
|
||||
{
|
||||
// wait a moment
|
||||
Thread.sleep(1000);
|
||||
errorEvent =
|
||||
OperationSetSmsMessagingFacebookImpl.this.parentProvider
|
||||
.getAdapter().postMessage(message, to);
|
||||
if (errorEvent == null)
|
||||
{
|
||||
fireMessageDelivered(message, to);
|
||||
return;
|
||||
}
|
||||
if(errorEvent.getErrorCode() == FacebookErrorException.kError_Async_NotLoggedIn
|
||||
|| errorEvent.getErrorCode() == FacebookErrorException.kError_Async_LoginChanged)
|
||||
{
|
||||
try
|
||||
{
|
||||
parentProvider.unregister(RegistrationStateChangeEvent.REASON_MULTIPLE_LOGINS);
|
||||
}
|
||||
catch (OperationFailedException e1)
|
||||
{
|
||||
logger.error(
|
||||
"Unable to unregister the protocol provider: "
|
||||
+ this
|
||||
+ " due to the following exception: " + e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
logger.warn(e.getMessage());
|
||||
}
|
||||
// if we get here, we have failed to deliver this message,
|
||||
// twice.
|
||||
fireMessageDeliveryFailed(message, to, errorEvent);
|
||||
// now do message delivery.----what's this?
|
||||
// in case that the "to" we deliver this message to is
|
||||
// ourselves?
|
||||
// deliverMessageToMyself(message, (ContactFacebookImpl)to);
|
||||
}
|
||||
});
|
||||
sender.start();
|
||||
}
|
||||
|
||||
public void sendSmsMessage(String to, Message message)
|
||||
throws IllegalStateException,
|
||||
IllegalArgumentException
|
||||
{
|
||||
Contact toContact = opSetPersPresence.findContactByID(to);
|
||||
sendSmsMessage(toContact, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered message listeners that a message has been
|
||||
* delivered successfully to its addressee..
|
||||
*
|
||||
* @param message the <tt>Message</tt> that has been delivered.
|
||||
* @param to the <tt>Contact</tt> that <tt>message</tt> was delivered
|
||||
* to.
|
||||
*/
|
||||
private void fireMessageDelivered(Message message, Contact to)
|
||||
{
|
||||
// we succeeded in sending a message to contact "to",
|
||||
// so we know he is online
|
||||
// but -- if we support the "invisible" status,
|
||||
// this line could be commented
|
||||
opSetPersPresence.setPresenceStatusForContact((ContactFacebookImpl) to,
|
||||
FacebookStatusEnum.ONLINE);
|
||||
|
||||
MessageDeliveredEvent evt =
|
||||
new MessageDeliveredEvent(message, to);
|
||||
|
||||
Iterable<MessageListener> listeners;
|
||||
synchronized (messageListeners)
|
||||
{
|
||||
listeners = new ArrayList<MessageListener>(messageListeners);
|
||||
}
|
||||
|
||||
for (MessageListener listener : listeners)
|
||||
listener.messageDelivered(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered message listeners that a message has been
|
||||
* delivered successfully to its addressee..
|
||||
*
|
||||
* @param message the <tt>Message</tt> that has been delivered.
|
||||
* @param to the <tt>Contact</tt> that <tt>message</tt> was delivered
|
||||
* to.
|
||||
*/
|
||||
private void fireMessageDeliveryFailed(Message message, Contact to,
|
||||
MessageDeliveryFailedEvent event)
|
||||
{
|
||||
Iterable<MessageListener> listeners;
|
||||
synchronized (messageListeners)
|
||||
{
|
||||
listeners = new ArrayList<MessageListener>(messageListeners);
|
||||
}
|
||||
|
||||
for (MessageListener listener : listeners)
|
||||
listener.messageDeliveryFailed(event);
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Implements typing notifications for the Facebook protocol. The operation set
|
||||
* would simply mirror all outgoing typing notifications and make them appear as
|
||||
* incoming events generated by the contact that we are currently writing a
|
||||
* message to.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class OperationSetTypingNotificationsFacebookImpl
|
||||
extends AbstractOperationSetTypingNotifications<ProtocolProviderServiceFacebookImpl>
|
||||
{
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(OperationSetTypingNotificationsFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* Creates a new instance of this operation set and keeps the parent
|
||||
* provider as a reference.
|
||||
*
|
||||
* @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt> that
|
||||
* created us and that we'll use for retrieving the underlying
|
||||
* aim connection.
|
||||
*/
|
||||
OperationSetTypingNotificationsFacebookImpl(
|
||||
ProtocolProviderServiceFacebookImpl provider)
|
||||
{
|
||||
super(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* We've gotten a typing notification from the <tt>fromContact</tt>, so
|
||||
* just put it into the GUI via firing the corresponding event.
|
||||
*
|
||||
* @param fromContact who send us this typing notification
|
||||
* @param typingState this buddy's typing state
|
||||
* @throws IllegalStateException
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public void receivedTypingNotification(Contact fromContact, int typingState)
|
||||
throws IllegalStateException,
|
||||
IllegalArgumentException
|
||||
{
|
||||
if (!(fromContact instanceof ContactFacebookImpl))
|
||||
throw new IllegalArgumentException(
|
||||
"The specified contact is not a Facebook contact."
|
||||
+ fromContact);
|
||||
|
||||
// and now fire the message received event.
|
||||
fireTypingNotificationsEvent(fromContact, typingState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a notification to <tt>notifiedContatct</tt> that we have entered
|
||||
* <tt>typingState</tt>.
|
||||
*
|
||||
* @param notifiedContact the <tt>Contact</tt> to notify
|
||||
* @param typingState the typing state that we have entered.
|
||||
*
|
||||
* @throws java.lang.IllegalStateException if the underlying stack is not
|
||||
* registered and initialized.
|
||||
* @throws java.lang.IllegalArgumentException if <tt>notifiedContact</tt>
|
||||
* is not an instance belonging to the underlying
|
||||
* implementation.
|
||||
*/
|
||||
public void sendTypingNotification(final Contact notifiedContact,
|
||||
final int typingState)
|
||||
throws IllegalStateException,
|
||||
IllegalArgumentException
|
||||
{
|
||||
if (!(notifiedContact instanceof ContactFacebookImpl))
|
||||
throw new IllegalArgumentException(
|
||||
"The specified contact is not a Facebook contact."
|
||||
+ notifiedContact);
|
||||
|
||||
Thread notificator = new Thread(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
OperationSetTypingNotificationsFacebookImpl.this.parentProvider
|
||||
.getAdapter().postTypingNotification(notifiedContact,
|
||||
typingState);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
notificator.start();
|
||||
}
|
||||
}
|
||||
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.resources.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Represents the Facebook protocol icon. Implements the <tt>ProtocolIcon</tt>
|
||||
* interface in order to provide a facebook logo image in two different sizes.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class ProtocolIconFacebookImpl
|
||||
implements ProtocolIcon
|
||||
{
|
||||
private static Logger logger =
|
||||
Logger.getLogger(ProtocolIconFacebookImpl.class);
|
||||
|
||||
private static ResourceManagementService resources;
|
||||
|
||||
/**
|
||||
* A hash table containing the protocol icon in different sizes.
|
||||
*/
|
||||
private static final Map<String, byte[]> iconsTable
|
||||
= new Hashtable<String, byte[]>();
|
||||
static
|
||||
{
|
||||
iconsTable.put(ProtocolIcon.ICON_SIZE_16x16,
|
||||
getImageInBytes("service.protocol.facebook.FACEBOOK_16x16"));
|
||||
iconsTable.put(ProtocolIcon.ICON_SIZE_32x32,
|
||||
getImageInBytes("service.protocol.facebook.FACEBOOK_32x32"));
|
||||
iconsTable.put(ProtocolIcon.ICON_SIZE_48x48,
|
||||
getImageInBytes("service.protocol.facebook.FACEBOOK_48x48"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the <tt>ProtocolIcon.getSupportedSizes()</tt> method.
|
||||
* Returns an iterator to a set containing the supported icon sizes.
|
||||
*
|
||||
* @return an iterator to a set containing the supported icon sizes
|
||||
*/
|
||||
public Iterator<String> getSupportedSizes()
|
||||
{
|
||||
return iconsTable.keySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returne TRUE if a icon with the given size is supported, FALSE-otherwise.
|
||||
*/
|
||||
public boolean isSizeSupported(String iconSize)
|
||||
{
|
||||
return iconsTable.containsKey(iconSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon image in the given size.
|
||||
*
|
||||
* @param iconSize the icon size; one of ICON_SIZE_XXX constants
|
||||
*/
|
||||
public byte[] getIcon(String iconSize)
|
||||
{
|
||||
return iconsTable.get(iconSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon image used to represent the protocol connecting state.
|
||||
*
|
||||
* @return the icon image used to represent the protocol connecting state
|
||||
*/
|
||||
public byte[] getConnectingIcon()
|
||||
{
|
||||
return getImageInBytes("service.protocol.facebook.CONNECTING_ICON");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte representation of the image corresponding to the given
|
||||
* identifier.
|
||||
*
|
||||
* @param imageID the identifier of the image
|
||||
* @return the byte representation of the image corresponding to the given
|
||||
* identifier.
|
||||
*/
|
||||
private static byte[] getImageInBytes(String imageID)
|
||||
{
|
||||
InputStream in = getResources().getImageInputStream(imageID);
|
||||
|
||||
if (in == null)
|
||||
return null;
|
||||
byte[] image = null;
|
||||
try
|
||||
{
|
||||
image = new byte[in.available()];
|
||||
|
||||
in.read(image);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.error("Failed to load image:" + imageID, e);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
public static ResourceManagementService getResources()
|
||||
{
|
||||
if (resources == null)
|
||||
resources
|
||||
= ResourceManagementServiceUtils
|
||||
.getService(FacebookActivator.bundleContext);
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
@ -1,181 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
|
||||
import org.osgi.framework.*;
|
||||
|
||||
/**
|
||||
* The Facebook protocol provider factory creates instances of the Facebook
|
||||
* protocol provider service. One Service instance corresponds to one account.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class ProtocolProviderFactoryFacebookImpl
|
||||
extends ProtocolProviderFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates an instance of the ProtocolProviderFactoryFacebookImpl.
|
||||
*/
|
||||
public ProtocolProviderFactoryFacebookImpl()
|
||||
{
|
||||
super(FacebookActivator.getBundleContext(), ProtocolNames.FACEBOOK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements ProtocolProviderFactory#createAccountID(String, Map).
|
||||
* Initializes a new FacebookAccountID with the specified properties.
|
||||
*/
|
||||
protected AccountID createAccountID(
|
||||
String userID, Map<String, String> accountProperties)
|
||||
{
|
||||
return new FacebookAccountID(userID, accountProperties);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements ProtocolProviderFactory#createService(String, AccountID).
|
||||
* Initializes a new ProtocolProviderServiceFacebookImpl with the specified
|
||||
* properties.
|
||||
*/
|
||||
protected ProtocolProviderService createService(
|
||||
String userID, AccountID accountID)
|
||||
{
|
||||
ProtocolProviderServiceFacebookImpl service
|
||||
= new ProtocolProviderServiceFacebookImpl();
|
||||
|
||||
service.initialize(userID, accountID);
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialized and creates an account corresponding to the specified
|
||||
* accountProperties and registers the resulting ProtocolProvider in the
|
||||
* <tt>context</tt> BundleContext parameter.
|
||||
*
|
||||
* @param userIDStr the/a user identifier uniquely representing the newly
|
||||
* created account within the protocol namespace.
|
||||
* @param accountProperties a set of protocol (or implementation) specific
|
||||
* properties defining the new account.
|
||||
* @return the AccountID of the newly created account.
|
||||
*/
|
||||
public AccountID installAccount(
|
||||
String userIDStr, Map<String, String> accountProperties)
|
||||
{
|
||||
BundleContext context = FacebookActivator.getBundleContext();
|
||||
if (context == null)
|
||||
throw new NullPointerException(
|
||||
"The specified BundleContext was null");
|
||||
|
||||
if (userIDStr == null)
|
||||
throw new NullPointerException("The specified AccountID was null");
|
||||
|
||||
if (accountProperties == null)
|
||||
throw new NullPointerException(
|
||||
"The specified property map was null");
|
||||
|
||||
accountProperties.put(USER_ID, userIDStr);
|
||||
|
||||
AccountID accountID =
|
||||
new FacebookAccountID(userIDStr, accountProperties);
|
||||
|
||||
// make sure we haven't seen this account id before.
|
||||
if (registeredAccounts.containsKey(accountID))
|
||||
throw new IllegalStateException("An account for id " + userIDStr
|
||||
+ " was already installed!");
|
||||
|
||||
// first store the account and only then load it as the load generates
|
||||
// an osgi event, the osgi event triggers (through the UI) a call to the
|
||||
// ProtocolProviderService.register() method and it needs to acces
|
||||
// the configuration service and check for a stored password.
|
||||
this.storeAccount(accountID);
|
||||
|
||||
accountID = loadAccount(accountProperties);
|
||||
|
||||
return accountID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the account corresponding to the specified accountID. This
|
||||
* method is meant to be used to change properties of already existing
|
||||
* accounts. Note that if the given accountID doesn't correspond to any
|
||||
* registered account this method would do nothing.
|
||||
*
|
||||
* @param protocolProvider
|
||||
* @param accountProperties a set of protocol (or implementation) specific
|
||||
* properties defining the new account.
|
||||
*
|
||||
* @throws java.lang.NullPointerException if any of the arguments is null.
|
||||
*/
|
||||
public void modifyAccount(
|
||||
ProtocolProviderService protocolProvider,
|
||||
Map<String, String> accountProperties)
|
||||
{
|
||||
BundleContext context = FacebookActivator.getBundleContext();
|
||||
|
||||
if (context == null)
|
||||
throw new NullPointerException(
|
||||
"The specified BundleContext was null");
|
||||
|
||||
if (protocolProvider == null)
|
||||
throw new NullPointerException(
|
||||
"The specified Protocol Provider was null");
|
||||
|
||||
FacebookAccountID accountID =
|
||||
(FacebookAccountID) protocolProvider.getAccountID();
|
||||
|
||||
// If the given accountID doesn't correspond to an existing account
|
||||
// we return.
|
||||
if (!registeredAccounts.containsKey(accountID))
|
||||
return;
|
||||
|
||||
ServiceRegistration registration = registeredAccounts.get(accountID);
|
||||
|
||||
// kill the service
|
||||
if (registration != null)
|
||||
registration.unregister();
|
||||
|
||||
if (accountProperties == null)
|
||||
throw new NullPointerException(
|
||||
"The specified property map was null");
|
||||
|
||||
accountProperties.put(USER_ID, accountID.getUserID());
|
||||
|
||||
if (!accountProperties.containsKey(PROTOCOL))
|
||||
accountProperties.put(PROTOCOL, ProtocolNames.FACEBOOK);
|
||||
|
||||
accountID.setAccountProperties(accountProperties);
|
||||
|
||||
/*
|
||||
* First store the account and only then load it as the load generates
|
||||
* an OSGi event, the OSGi event triggers (through the UI) a call to the
|
||||
* register() method and it needs to access the configuration service
|
||||
* and check for a password.
|
||||
*/
|
||||
this.storeAccount(accountID);
|
||||
|
||||
((ProtocolProviderServiceFacebookImpl) protocolProvider).initialize(
|
||||
accountID.getUserID(), accountID);
|
||||
|
||||
// We store again the account in order to store all properties added
|
||||
// during the protocol provider initialization.
|
||||
this.storeAccount(accountID);
|
||||
|
||||
Dictionary<String, String> properties = new Hashtable<String, String>();
|
||||
properties.put(PROTOCOL, ProtocolNames.FACEBOOK);
|
||||
properties.put(USER_ID, accountID.getUserID());
|
||||
|
||||
registration =
|
||||
context.registerService(ProtocolProviderService.class.getName(),
|
||||
protocolProvider, properties);
|
||||
|
||||
// We store the modified account registration.
|
||||
registeredAccounts.put(accountID, registration);
|
||||
}
|
||||
}
|
||||
@ -1,381 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.facebook;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* A Facebook implementation of the ProtocolProviderService.
|
||||
*
|
||||
* @author Dai Zhiwei
|
||||
*/
|
||||
public class ProtocolProviderServiceFacebookImpl
|
||||
extends AbstractProtocolProviderService
|
||||
{
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(ProtocolProviderServiceFacebookImpl.class);
|
||||
|
||||
/**
|
||||
* The name of this protocol.
|
||||
*/
|
||||
public static final String FACEBOOK_PROTOCOL_NAME = "Facebook";
|
||||
|
||||
/**
|
||||
* The id of the account that this protocol provider represents.
|
||||
*/
|
||||
private AccountID accountID = null;
|
||||
|
||||
/**
|
||||
* We use this to lock access to initialization.
|
||||
*/
|
||||
private Object initializationLock = new Object();
|
||||
|
||||
/**
|
||||
* Indicates whether or not the provider is initialized and ready for use.
|
||||
*/
|
||||
private boolean isInitialized = false;
|
||||
|
||||
/**
|
||||
* The logo corresponding to the facebook protocol.
|
||||
*/
|
||||
private ProtocolIconFacebookImpl facebookIcon
|
||||
= new ProtocolIconFacebookImpl();
|
||||
|
||||
/**
|
||||
* The registration state that we are currently in. Note that in a real
|
||||
* world protocol implementation this field won't exist and the registration
|
||||
* state would be retrieved from the protocol stack.
|
||||
*/
|
||||
private RegistrationState currentRegistrationState
|
||||
= RegistrationState.UNREGISTERED;
|
||||
|
||||
private FacebookAdapter facebookAdapter;
|
||||
|
||||
/**
|
||||
* The default constructor for the Facebook protocol provider.
|
||||
*/
|
||||
public ProtocolProviderServiceFacebookImpl()
|
||||
{
|
||||
logger.trace("Creating a facebook provider.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the service implementation, and puts it in a sate where it
|
||||
* could interoperate with other services. It is strongly recommended that
|
||||
* properties in this Map be mapped to property names as specified by
|
||||
* <tt>AccountProperties</tt>.
|
||||
*
|
||||
* @param userID
|
||||
* the user id of the facebook account we're currently
|
||||
* initializing
|
||||
* @param accountID
|
||||
* the identifier of the account that this protocol provider
|
||||
* represents.
|
||||
*
|
||||
* @see net.java.sip.communicator.service.protocol.AccountID
|
||||
*/
|
||||
protected void initialize(String userID, AccountID accountID)
|
||||
{
|
||||
synchronized (initializationLock)
|
||||
{
|
||||
this.accountID = accountID;
|
||||
|
||||
// initialize the presence operationset
|
||||
OperationSetPersistentPresenceFacebookImpl persistentPresence
|
||||
= new OperationSetPersistentPresenceFacebookImpl(this);
|
||||
|
||||
addSupportedOperationSet(
|
||||
OperationSetPersistentPresence.class,
|
||||
persistentPresence);
|
||||
// register it once again for those that simply need presence and
|
||||
// won't be smart enough to check for a persistent presence
|
||||
// alternative
|
||||
addSupportedOperationSet(
|
||||
OperationSetPresence.class,
|
||||
persistentPresence);
|
||||
|
||||
// initialize the IM operation set
|
||||
OperationSetBasicInstantMessagingFacebookImpl basicInstantMessaging
|
||||
= new OperationSetBasicInstantMessagingFacebookImpl(
|
||||
this,
|
||||
persistentPresence);
|
||||
|
||||
addSupportedOperationSet(
|
||||
OperationSetBasicInstantMessaging.class,
|
||||
basicInstantMessaging);
|
||||
|
||||
// initialize the message operation set
|
||||
addSupportedOperationSet(
|
||||
OperationSetSmsMessaging.class,
|
||||
new OperationSetSmsMessagingFacebookImpl(
|
||||
this,
|
||||
persistentPresence));
|
||||
|
||||
// initialize the typing notifications operation set
|
||||
OperationSetTypingNotificationsFacebookImpl typingNotifications
|
||||
= new OperationSetTypingNotificationsFacebookImpl(this);
|
||||
|
||||
addSupportedOperationSet(
|
||||
OperationSetTypingNotifications.class,
|
||||
typingNotifications);
|
||||
|
||||
// initialize the server stored contact info operation set
|
||||
addSupportedOperationSet(
|
||||
OperationSetServerStoredContactInfo.class,
|
||||
new OperationSetServerStoredContactInfoFacebookImpl(this));
|
||||
|
||||
facebookAdapter
|
||||
= new FacebookAdapter(
|
||||
this,
|
||||
persistentPresence,
|
||||
basicInstantMessaging,
|
||||
typingNotifications);
|
||||
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AccountID that uniquely identifies the account represented by
|
||||
* this instance of the ProtocolProviderService.
|
||||
*
|
||||
* @return the id of the account represented by this provider.
|
||||
*/
|
||||
public AccountID getAccountID()
|
||||
{
|
||||
return accountID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the short name of the protocol that the implementation of this
|
||||
* provider is based upon (like SIP, Jabber, ICQ/AIM, or others for
|
||||
* example).
|
||||
*
|
||||
* @return a String containing the short name of the protocol this service
|
||||
* is implementing (most often that would be a name in
|
||||
* ProtocolNames).
|
||||
*/
|
||||
public String getProtocolName()
|
||||
{
|
||||
return FACEBOOK_PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the protocol display name. This is the name that would be used by
|
||||
* the GUI to display the protocol name.
|
||||
*
|
||||
* @return a String containing the display name of the protocol this service
|
||||
* is implementing
|
||||
*/
|
||||
public String getProtocolDisplayName()
|
||||
{
|
||||
return FACEBOOK_PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the registration of this protocol provider with the
|
||||
* corresponding registration service.
|
||||
*
|
||||
* @return ProviderRegistrationState
|
||||
*/
|
||||
public RegistrationState getRegistrationState()
|
||||
{
|
||||
return currentRegistrationState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the registration process.
|
||||
*
|
||||
* @param authority
|
||||
* the security authority that will be used for resolving any
|
||||
* security challenges that may be returned during the
|
||||
* registration or at any moment while wer're registered.
|
||||
* @throws OperationFailedException
|
||||
* with the corresponding code it the registration fails for
|
||||
* some reason (e.g. a networking error or an implementation
|
||||
* problem).
|
||||
*/
|
||||
public void register(SecurityAuthority authority)
|
||||
throws OperationFailedException
|
||||
{
|
||||
// verify whether a password has already been stored for this account
|
||||
String password
|
||||
= FacebookActivator
|
||||
.getProtocolProviderFactory().loadPassword(getAccountID());
|
||||
|
||||
// if we don't - retrieve it from the user through the security
|
||||
// authority
|
||||
if (password == null)
|
||||
{
|
||||
// create a default credentials object
|
||||
UserCredentials credentials = new UserCredentials();
|
||||
credentials.setUserName(getAccountID().getUserID());
|
||||
|
||||
// request a password from the user
|
||||
credentials
|
||||
= authority
|
||||
.obtainCredentials(
|
||||
ProtocolNames.FACEBOOK,
|
||||
credentials,
|
||||
SecurityAuthority.AUTHENTICATION_REQUIRED);
|
||||
|
||||
// extract the password the user passed us.
|
||||
char[] pass = credentials.getPassword();
|
||||
|
||||
// the user didn't provide us a password (canceled the operation)
|
||||
if (pass == null)
|
||||
{
|
||||
fireRegistrationStateChanged(getRegistrationState(),
|
||||
RegistrationState.UNREGISTERED,
|
||||
RegistrationStateChangeEvent.REASON_USER_REQUEST, "");
|
||||
return;
|
||||
}
|
||||
password = new String(pass);
|
||||
|
||||
// if the user indicated that the password should be saved, we'll
|
||||
// ask
|
||||
// the proto provider factory to store it for us.
|
||||
if (credentials.isPasswordPersistent())
|
||||
{
|
||||
FacebookActivator.getProtocolProviderFactory().storePassword(
|
||||
getAccountID(), password);
|
||||
}
|
||||
}
|
||||
|
||||
RegistrationState oldState = currentRegistrationState;
|
||||
|
||||
int initErrorCode = FacebookErrorException.Error_Global_NoError;
|
||||
|
||||
if (!facebookAdapter.initialize(getAccountID().getUserID(), password))
|
||||
{
|
||||
initErrorCode = FacebookErrorException.kError_Login_GenericError;
|
||||
}
|
||||
|
||||
if (initErrorCode == FacebookErrorException.Error_Global_NoError)
|
||||
{
|
||||
currentRegistrationState = RegistrationState.REGISTERED;
|
||||
fireRegistrationStateChanged(oldState, currentRegistrationState,
|
||||
RegistrationStateChangeEvent.REASON_USER_REQUEST, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
fireRegistrationStateChanged(getRegistrationState(),
|
||||
RegistrationState.UNREGISTERED,
|
||||
RegistrationStateChangeEvent.REASON_SERVER_NOT_FOUND, "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the service implementation close all open sockets and release any
|
||||
* resources that it might have taken and prepare for shutdown/garbage
|
||||
* collection.
|
||||
*/
|
||||
public void shutdown()
|
||||
{
|
||||
if (!isInitialized)
|
||||
return;
|
||||
|
||||
logger.trace("Killing the Facebook Protocol Provider.");
|
||||
|
||||
if (isRegistered())
|
||||
{
|
||||
try
|
||||
{
|
||||
// do the unregistration
|
||||
unregister();
|
||||
}
|
||||
catch (OperationFailedException ex)
|
||||
{
|
||||
// we're shutting down so we need to silence the exception here
|
||||
logger.error(
|
||||
"Failed to properly unregister before shutting down. "
|
||||
+ getAccountID(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
// shut down the http client
|
||||
this.facebookAdapter.shutdown();
|
||||
|
||||
isInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the registration of this protocol provider with the current
|
||||
* registration service.
|
||||
*
|
||||
* @throws OperationFailedException
|
||||
* with the corresponding code it the registration fails for
|
||||
* some reason (e.g. a networking error or an implementation
|
||||
* problem).
|
||||
*/
|
||||
public void unregister() throws OperationFailedException
|
||||
{
|
||||
unregister(RegistrationStateChangeEvent.REASON_USER_REQUEST);
|
||||
}
|
||||
|
||||
public void unregister(int reason)
|
||||
throws OperationFailedException
|
||||
{
|
||||
RegistrationState oldState = currentRegistrationState;
|
||||
currentRegistrationState = RegistrationState.UNREGISTERED;
|
||||
|
||||
fireRegistrationStateChanged(
|
||||
oldState,
|
||||
currentRegistrationState,
|
||||
reason,
|
||||
null);
|
||||
|
||||
// surspend/pause the httpclient
|
||||
this.facebookAdapter.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the facebook protocol icon.
|
||||
*
|
||||
* @return the facebook protocol icon
|
||||
*/
|
||||
public ProtocolIcon getProtocolIcon()
|
||||
{
|
||||
return facebookIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the information of the contact who has the specific id. Return
|
||||
* null if there's no this contact.
|
||||
*
|
||||
* @param contactID the id we wanna look up
|
||||
* @return the information of the contact who has the specific id
|
||||
*/
|
||||
public FacebookUser getContactMetaInfoByID(String contactID)
|
||||
{
|
||||
FacebookSession facebookSession = facebookAdapter.getSession();
|
||||
|
||||
if (facebookSession != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return facebookSession.getBuddyList().getBuddyByUID(contactID);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.warn("unable to get contact info", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the adapter of this account.
|
||||
*
|
||||
* @return the adapter of this account
|
||||
*/
|
||||
public FacebookAdapter getAdapter()
|
||||
{
|
||||
return this.facebookAdapter;
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
Bundle-Activator: net.java.sip.communicator.impl.protocol.facebook.FacebookActivator
|
||||
Bundle-Name: Facebook Protocol Provider
|
||||
Bundle-Description: A bundle providing support for the Facebook protocol.
|
||||
Bundle-Vendor: sip-communicator.org
|
||||
Bundle-Version: 0.0.1
|
||||
Import-Package: org.apache.commons.logging,
|
||||
org.apache.http,
|
||||
org.apache.http.entity,
|
||||
org.apache.http.impl,
|
||||
org.apache.http.impl.entity,
|
||||
org.apache.http.impl.io,
|
||||
org.apache.http.io,
|
||||
org.apache.http.message,
|
||||
org.apache.http.params,
|
||||
org.apache.http.protocol,
|
||||
org.apache.http.util,
|
||||
org.osgi.framework,
|
||||
net.java.sip.communicator.service.configuration,
|
||||
net.java.sip.communicator.service.configuration.event,
|
||||
net.java.sip.communicator.service.protocol,
|
||||
net.java.sip.communicator.service.protocol.event,
|
||||
net.java.sip.communicator.service.resources,
|
||||
net.java.sip.communicator.util,
|
||||
javax.imageio,
|
||||
javax.net.ssl
|
||||
Loading…
Reference in new issue