Introduce new support for Facebook, through xmpp. Remove old one.

cusax-fix
Damian Minkov 16 years ago
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]+\\&amp;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);
}
}

@ -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.:
* & = &amp;
* (space) = &nbsp;
* > = &gt;
* < = &lt;
* " = &quot;
* ' = &#039;
* © = &copy;
* ® = &reg;
*/
text = text.replaceAll("&amp;", "&");
text = text.replaceAll("&nbsp;", " ");
text = text.replaceAll("&gt;", ">");
text = text.replaceAll("&lt;", "<");
text = text.replaceAll("&quot;", "\"");
text = text.replaceAll("&#039;", "'");
text = text.replaceAll("&copy;", "©");
text = text.replaceAll("&reg;", "®");
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…
Cancel
Save