Index: contrib/psync/src/main/java/br/com/prognus/psync/items/dao/PIMContactDAO.java
===================================================================
--- contrib/psync/src/main/java/br/com/prognus/psync/items/dao/PIMContactDAO.java	(revision 1103)
+++ contrib/psync/src/main/java/br/com/prognus/psync/items/dao/PIMContactDAO.java	(revision 1545)
@@ -32,12 +32,18 @@
 import br.com.prognus.psync.exception.PIMDBAccessException;
 import br.com.prognus.psync.items.model.ContactWrapper;
+import br.com.prognus.psync.util.Country;
 import br.com.prognus.psync.util.Def;
-
+import br.com.prognus.psync.util.TypeAddress;
+
+import com.funambol.common.pim.common.Property;
+import com.funambol.common.pim.contact.Address;
 import com.funambol.common.pim.contact.BusinessDetail;
 import com.funambol.common.pim.contact.Contact;
 import com.funambol.common.pim.contact.Email;
 import com.funambol.common.pim.contact.Name;
+import com.funambol.common.pim.contact.Note;
 import com.funambol.common.pim.contact.PersonalDetail;
 import com.funambol.common.pim.contact.Phone;
+import com.funambol.common.pim.contact.Photo;
 import com.funambol.framework.security.Sync4jPrincipal;
 import com.funambol.framework.server.store.NotFoundException;
@@ -55,5 +61,5 @@
 			+ "WHERE id_owner = ?";
 
-	private static final String SQL_GET_CONTACT_BY_ID_USER = "SELECT id_contact, id_owner, last_update, last_status, given_names, family_names, birthdate, category FROM phpgw_cc_contact WHERE id_contact = ? AND id_owner = ? LIMIT 1";
+	private static final String SQL_GET_CONTACT_BY_ID_USER = "SELECT id_contact, id_owner, last_update, last_status, given_names, family_names, birthdate, category, photo, notes, alias FROM phpgw_cc_contact WHERE id_contact = ? AND id_owner = ? LIMIT 1";
 
 	private static final String SQL_GET_CONTACT_ITEM_BY_ID = "SELECT connection_name, connection_value, id_typeof_contact_connection FROM phpgw_cc_connections AS CO LEFT JOIN phpgw_cc_contact_conns AS CC ON CO.id_connection = CC.id_connection WHERE id_contact = ?";
@@ -73,6 +79,6 @@
 
 	private static final String SQL_INSERT_INTO_FNBL_PIM_CONTACT = "INSERT INTO phpgw_cc_contact "
-			+ "(id_contact, id_owner, id_status, given_names, family_names, names_ordered, birthdate, category, last_update, last_status ) "
-			+ "VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ";
+			+ "(id_contact, id_owner, id_status, given_names, family_names, names_ordered, birthdate, category, photo, notes, alias, last_update, last_status ) "
+			+ "VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ";
 
 	private static final String SQL_INSERT_INTO_CONTACT_ITEM = "INSERT INTO phpgw_cc_connections "
@@ -87,5 +93,28 @@
 
 	private static final String SQL_UPDATE_FNBL_PIM_CONTACT_END = " WHERE id_contact = ? AND id_owner = ? ";
-
+	
+	
+	private static final String SQL_GET_CONTACT_ADDRESS_ITEM = " select ad.complement, ad.address1, ad.postal_code, ca.id_typeof_contact_address, city_name, state_name, cy.id_country from phpgw_cc_addresses AS ad JOIN phpgw_cc_contact_addrs AS ca ON ad.id_address=ca.id_address, phpgw_cc_city AS cy JOIN phpgw_cc_state AS st ON cy.id_state=st.id_state where cy.id_city=ad.id_city and ad.id_address in (select min(ca.id_address) from phpgw_cc_contact AS cc JOIN phpgw_cc_contact_addrs AS ca ON cc.id_contact=ca.id_contact JOIN phpgw_cc_addresses AS ad ON ca.id_address=ad.id_address where cc.id_contact = ? AND cc.id_owner = ? and ca.id_typeof_contact_address = ? )";
+
+	private static final String SQL_GET_STATE_ITEM = "SELECT id_state, id_country, state_name, state_symbol FROM phpgw_cc_state ";
+	
+	private static final String SQL_GET_CITY_ITEM = "SELECT id_city, id_state, id_country, city_timezone, city_geo_location, city_name FROM phpgw_cc_city ";
+
+	private static final String SQL_INSERT_STATE = "INSERT INTO phpgw_cc_state(id_state, id_country, state_name, state_symbol) VALUES (?, ?, ?, ?)";
+	
+	private static final String SQL_INSERT_CITY = "INSERT INTO phpgw_cc_city(id_city, id_state, id_country, city_timezone, city_geo_location, city_name) VALUES (?, ?, ?, ?, ?, ?)";
+	
+	private static final String SQL_INSERT_ADDRESS = "INSERT INTO phpgw_cc_addresses(id_address, id_city, id_state, id_country, address1, complement, postal_code, address_is_default) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+	
+	private static final String SQL_INSERT_CONTACT_ADDRESS = "INSERT INTO phpgw_cc_contact_addrs(id_contact, id_address, id_typeof_contact_address) VALUES (?, ?, ?)";
+
+	private static final int SQL_ADDRESS_DIM = 60;
+	
+	private static final String SQL_ID_MAX_ADDRESS = "select max(id_address) FROM phpgw_cc_addresses";
+	
+	private static final String SQL_ID_MAX_STATE = "select max(id_state) FROM phpgw_cc_state";
+	
+	private static final String SQL_ID_MAX_CITY = "select max(id_city) FROM phpgw_cc_city";
+	
 	private static final String SQL_EQUALS_QUESTIONMARK = " = ?";
 
@@ -111,4 +140,8 @@
 
 	protected static final String SQL_FIELD_CATEGORY = "category";
+	
+	protected static final String SQL_FIELD_PHOTO = "photo";
+	
+	protected static final String SQL_FIELD_NOTES = "notes";
 
 	protected static final int SQL_FIRSTNAME_DIM = 49;
@@ -121,4 +154,6 @@
 
 	protected static final int SQL_CATEGORY_DIM = 20;
+	
+	protected static final int SQL_NICK_DIM = 30;
 
 	protected static final String TYPE_EMAIL_1_ADDRESS = "Principal";
@@ -159,4 +194,22 @@
 
 	protected static final String SQL_FIELD_ITEM_TYPE = "id_typeof_contact_connection";
+	
+	protected static final String SQL_FIELD_TYPEOF = "id_typeof_contact_address";
+	
+	protected static final String SQL_FIELD_COMPLEMENT = "complement";
+	
+	protected static final String SQL_FIELD_ADDRESS1 = "address1";
+		
+	protected static final String SQL_FIELD_POSTALCODE = "postal_code";
+	
+	protected static final String SQL_FIELD_CITY_NAME = "city_name";
+	
+	protected static final String SQL_FIELD_STATE_NAME = "state_name";
+	
+	protected static final String SQL_FIELD_COUNTRY_ID = "id_country";
+	
+	protected static final String SQL_FIELD_STATE_ID = "id_state";
+	
+	protected static final String SQL_FIELD_CITY_ID = "id_city";
 
 	private Sync4jPrincipal principal = null;
@@ -193,4 +246,5 @@
 	 * @see ContactWrapper
 	 */
+	@SuppressWarnings("unchecked")
 	public void addItem(ContactWrapper cw) throws PIMDBAccessException {
 
@@ -223,4 +277,7 @@
 		StringBuffer fullName = null;
 		Date anniversary = null;
+		byte[] imagePhoto = null;
+		String note = null;
+		String alias = null;
 
 		try {
@@ -232,4 +289,15 @@
 			Timestamp lastUpdate = cw.getLastUpdate();
 
+			try {
+				List<Note> notes = c.getNotes();
+				int i = notes.size() - 1;
+				note = notes.get(i).getPropertyValueAsString();
+			} catch (Exception e) {
+//				log.error(e.getMessage(),e);
+			}
+			if(note==null) {
+				note="";
+			}
+			
 			personalDetail = c.getPersonalDetail();
 			businessDetail = c.getBusinessDetail();
@@ -244,4 +312,5 @@
 				birthday = personalDetail.getBirthday();
 				category = stringFrom(c.getCategories());
+				imagePhoto = personalDetail.getPhotoObject().getImage();
 			}
 
@@ -254,4 +323,5 @@
 				firstName = stringFrom(name.getFirstName());
 				lastName = stringFrom(name.getLastName());
+				alias = stringFrom(name.getNickname());
 			}
 
@@ -297,6 +367,9 @@
 			ps.setDate(7, anniversary); // birthdate
 			ps.setString(8, truncate(category, SQL_CATEGORY_DIM)); // category
-			ps.setLong(9, lastUpdate.getTime()); // last_update
-			ps.setString(10, String.valueOf(Def.PIM_STATE_NEW)); // last_status
+			ps.setBytes(9, imagePhoto); // photo
+			ps.setString(10, note); // note
+			ps.setString(11, truncate(alias, SQL_NICK_DIM)); // alias
+			ps.setLong(12, lastUpdate.getTime()); // last_update
+			ps.setString(13, String.valueOf(Def.PIM_STATE_NEW)); // last_status
 			ps.executeUpdate();
 
@@ -483,4 +556,9 @@
 		String category = null;
 		Date anniversary = null;
+		byte[] imagePhoto = null;
+		String note = null;
+		String alias = null;
+		Address persoAddress = null;
+		Address businAddress = null;
 
 		try {
@@ -495,4 +573,15 @@
 			Contact c = cw.getContact();
 
+			try {
+				List<Note> notes = c.getNotes();
+				int i = notes.size() - 1;
+				note = notes.get(i).getPropertyValueAsString();
+			} catch (Exception e) {
+//				log.error(e.getMessage(),e);
+			}
+			if(note==null) {
+				note="";
+			}
+			
 			personalDetail = c.getPersonalDetail();
 			businessDetail = c.getBusinessDetail();
@@ -505,4 +594,6 @@
 				birthday = personalDetail.getBirthday();
 				category = stringFrom(c.getCategories());
+				imagePhoto = personalDetail.getPhotoObject().getImage();
+				persoAddress = personalDetail.getAddress();
 			}
 
@@ -510,4 +601,5 @@
 				emails.addAll(businessDetail.getEmails());
 				phones.addAll(businessDetail.getPhones());
+				businAddress = businessDetail.getAddress();
 			}
 
@@ -515,4 +607,5 @@
 				firstName = stringFrom(name.getFirstName());
 				lastName = stringFrom(name.getLastName());
+				alias = stringFrom(name.getNickname());
 			}
 
@@ -525,4 +618,7 @@
 			if (category != null && category.length() > SQL_CATEGORY_DIM) {
 				category = category.substring(0, SQL_CATEGORY_DIM);
+			}
+			if (alias != null && alias.length() > SQL_NICK_DIM) {
+				alias = alias.substring(0, SQL_NICK_DIM);
 			}
 
@@ -563,4 +659,10 @@
 			queryUpdateFunPimContact.append(SQL_FIELD_CATEGORY
 					+ SQL_EQUALS_QUESTIONMARK_COMMA);
+			queryUpdateFunPimContact.append(SQL_FIELD_PHOTO
+					+ SQL_EQUALS_QUESTIONMARK_COMMA);
+			queryUpdateFunPimContact.append(SQL_FIELD_NOTES
+					+ SQL_EQUALS_QUESTIONMARK_COMMA);
+			queryUpdateFunPimContact.append(SQL_FIELD_NICK
+					+ SQL_EQUALS_QUESTIONMARK_COMMA);
 			queryUpdateFunPimContact
 					.append(SQL_FIELD_STATUS + SQL_EQUALS_QUESTIONMARK
@@ -575,7 +677,10 @@
 			ps.setDate(5, anniversary); // birthdate
 			ps.setString(6, category); // category
-			ps.setString(7, String.valueOf(Def.PIM_STATE_UPDATED)); // last_status
-			ps.setLong(8, id_contact); // id_contact
-			ps.setLong(9, Long.parseLong(userId)); // id_owner
+			ps.setBytes(7, imagePhoto); // photo
+			ps.setString(8, note); // notes
+			ps.setString(9, alias); // alias
+			ps.setString(10, String.valueOf(Def.PIM_STATE_UPDATED)); // last_status
+			ps.setLong(11, id_contact); // id_contact
+			ps.setLong(12, Long.parseLong(userId)); // id_owner
 			ps.executeUpdate();
 
@@ -730,4 +835,88 @@
 			}
 
+			
+			// endereco
+			boolean syncAddress = true;
+			if(syncAddress) {
+			// Apaga os contatos na tabela phpgw_cc_addresses
+			ps = con
+					.prepareStatement("DELETE FROM phpgw_cc_addresses WHERE id_address IN (SELECT id_address FROM phpgw_cc_contact_addrs WHERE id_contact = ?)");
+			ps.setLong(1, id_contact);
+			ps.executeUpdate();
+		
+			// Apaga os contatos na tabela phpgw_cc_contact_addrs
+			ps = con
+					.prepareStatement("DELETE FROM phpgw_cc_contact_addrs WHERE id_contact = ?");
+			ps.setLong(1, id_contact);
+			ps.executeUpdate();
+			
+			// Para cadas tipo de endereco RESIDENCIAL | COMERCIAL
+			for (TypeAddress typeAddress: TypeAddress.values()) {
+				
+				Address address = null;
+				boolean defaultAddress = false;
+				
+				// se ambos sao nulos não faz nada aqui
+				if (persoAddress == null && businAddress == null) {
+					break;
+				}
+				
+				if (typeAddress.equals(TypeAddress.RESIDENCIAL)) {
+					address=persoAddress;
+					defaultAddress = true;
+				} else if (typeAddress.equals(TypeAddress.COMERCIAL)) {
+					address=businAddress;
+				}
+				
+				// Pula para o proximo
+				if(address == null) {
+					continue;
+				}
+				
+				String addressValue = address.getStreet().getPropertyValueAsString();
+				addressValue  = truncate(addressValue, SQL_ADDRESS_DIM);
+	
+				if (addressValue != null && addressValue.length() != 0) {
+
+					// Verifica o id maximo da tabela phpgw_cc_addresses
+					ps = con.prepareStatement(SQL_ID_MAX_ADDRESS);
+					rs = ps.executeQuery();
+					long id_address = 0;
+					
+					if (rs.next()) {
+						long id = rs.getInt(1);
+						id_address = (id == 0) ? 1 : ++id;
+					}
+
+					// Insere o endereco do contato na tabela
+					String idCountry;
+					try {
+						idCountry = Country.getCountryByName(address
+								.getCountry().getPropertyValueAsString()).getCode();
+					} catch (Exception e) {
+						idCountry = "BR";
+					}
+					long idState = searchStateId(con, address.getState().getPropertyValueAsString(), idCountry);
+					long idCity = searchCityId(con, address.getCity().getPropertyValueAsString(), idState, idCountry);
+					
+					ps = con.prepareStatement(SQL_INSERT_ADDRESS);
+					ps.setLong(1, id_address);
+					ps.setLong(2, idCity);
+					ps.setLong(3, idState);
+					ps.setString(4, idCountry);
+					ps.setString(5, addressValue);
+					ps.setString(6, address.getExtendedAddress().getPropertyValueAsString());
+					ps.setString(7, address.getPostalCode().getPropertyValueAsString());
+					ps.setBoolean(8, defaultAddress);
+					ps.executeUpdate();
+
+					ps = con.prepareStatement(SQL_INSERT_CONTACT_ADDRESS);
+					ps.setLong(1, id_contact);
+					ps.setLong(2, id_address);
+					ps.setInt(3, typeAddress.getId());
+					ps.executeUpdate();
+				}
+			}
+			}
 			con.commit();
 			con.setAutoCommit(true);
@@ -743,4 +932,99 @@
 
 		return Long.toString(id_contact);
+	}
+
+
+	private long searchStateId(Connection con, String stateName, String idCountry) throws Exception {
+		
+		PreparedStatement ps = null;
+		ResultSet rs = null;
+		long stateId = 0;
+		
+		try {
+			String SQL_GET_STATE = SQL_GET_STATE_ITEM + " where upper("
+					+ SQL_FIELD_STATE_NAME + ")" + SQL_EQUALS_QUESTIONMARK
+					+ " and " + SQL_FIELD_COUNTRY_ID + SQL_EQUALS_QUESTIONMARK;
+			ps = con.prepareStatement(SQL_GET_STATE);
+			ps.setString(1, stateName.toUpperCase());
+			ps.setString(2, idCountry);
+			rs = ps.executeQuery();
+			if (rs.next()) {
+				stateId = rs.getInt(SQL_FIELD_STATE_ID);
+				return stateId;
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+		ps = con.prepareStatement(SQL_ID_MAX_STATE);
+		rs = ps.executeQuery();
+		
+		if (rs.next()) {
+			long id = rs.getInt(1);
+			stateId = (id == 0) ? 1 : ++id;
+		}
+		else {
+			return 0;
+		}
+		
+		ps = con.prepareStatement(SQL_INSERT_STATE);
+		ps.setLong(1, stateId);
+		ps.setString(2, idCountry);
+		ps.setString(3, stateName);
+		ps.setString(4, stateName.substring(0, 2));
+		if(ps.executeUpdate() > 0) {
+			return stateId;
+		}
+		
+		return 0;
+	}
+
+	private long searchCityId(Connection con, String cityName, long idState, String idCountry) throws Exception {
+		
+		PreparedStatement ps = null;
+		ResultSet rs = null;
+		long cityId = 0;
+		
+		try {
+			String SQL_GET_CITY = SQL_GET_CITY_ITEM + " where "
+					+ SQL_FIELD_STATE_ID + SQL_EQUALS_QUESTIONMARK + " and "
+					+ SQL_FIELD_COUNTRY_ID + SQL_EQUALS_QUESTIONMARK
+					+ " and upper(" + SQL_FIELD_CITY_NAME + ")"
+					+ SQL_EQUALS_QUESTIONMARK;
+			ps = con.prepareStatement(SQL_GET_CITY);
+			ps.setLong(1, idState);
+			ps.setString(2, idCountry);
+			ps.setString(3, cityName.toUpperCase());
+			rs = ps.executeQuery();
+			if (rs.next()) {
+				cityId = rs.getInt(SQL_FIELD_CITY_ID);
+				return cityId;
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		ps = con.prepareStatement(SQL_ID_MAX_CITY);
+		rs = ps.executeQuery();
+		
+		if (rs.next()) {
+			long id = rs.getInt(1);
+			cityId = (id == 0) ? 1 : ++id;
+		}
+		else {
+			return 0;
+		}
+		
+		ps = con.prepareStatement(SQL_INSERT_CITY);
+		ps.setLong(1, cityId);
+		ps.setLong(2, idState);
+		ps.setString(3, idCountry);
+		ps.setNull(4, 0);
+		ps.setString(5, null);
+		ps.setString(6, cityName);
+		if(ps.executeUpdate() > 0) {
+			return cityId;
+		}
+		
+		return 0;
 	}
 
@@ -954,4 +1238,30 @@
 								.getSQLState());
 			}
+			
+			try {
+				ps = con.prepareStatement(SQL_GET_CONTACT_ADDRESS_ITEM);
+				ps.setLong(1, Long.parseLong(uid));
+				ps.setLong(2, Long.parseLong(userId));
+				ps.setInt(3, TypeAddress.RESIDENCIAL.getId());
+				rs = ps.executeQuery();
+				
+				Address address = c.getContact().getPersonalDetail().getAddress();
+				addAddress(address, rs);
+			} catch (Exception e) {
+				throw new SQLException("\nError while adding personal address information");
+			}
+
+			try {
+				ps = con.prepareStatement(SQL_GET_CONTACT_ADDRESS_ITEM);
+				ps.setLong(1, Long.parseLong(uid));
+				ps.setLong(2, Long.parseLong(userId));
+				ps.setInt(3, TypeAddress.COMERCIAL.getId());
+				rs = ps.executeQuery();
+				
+				Address address = c.getContact().getBusinessDetail().getAddress();
+				addAddress(address, rs);
+			} catch (Exception e) {
+				throw new SQLException("\nError while adding business address information");
+			}
 
 		} catch (Exception e) {
@@ -963,4 +1273,46 @@
 
 		return c;
+	}
+	
+	
+	private static void addAddress(Address address, ResultSet rs) throws Exception {
+		
+		ResultSetMetaData rsmd = rs.getMetaData();
+
+		String column = null;
+		int columnCount = 0;
+		
+		while (rs.next()) {
+
+			columnCount = rsmd.getColumnCount();
+			
+			if (rs.getString(SQL_FIELD_ADDRESS1)==null || (rs.getString(SQL_FIELD_ADDRESS1)!=null && "".equals(rs.getString(SQL_FIELD_ADDRESS1).trim()))) {
+				break;
+			}
+
+			for (int i = 1; i <= columnCount; ++i) {
+
+				column = rsmd.getColumnName(i);
+				if (SQL_FIELD_TYPEOF.equalsIgnoreCase(column)) {
+					continue;
+				} else if (SQL_FIELD_COMPLEMENT.equalsIgnoreCase(column)) {
+					address.getExtendedAddress().setPropertyValue(rs.getString(i));
+				} else if (SQL_FIELD_ADDRESS1.equalsIgnoreCase(column)) {
+					address.getStreet().setPropertyValue(rs.getString(i));
+				} else if (SQL_FIELD_POSTALCODE.equalsIgnoreCase(column)) {
+					address.getPostalCode().setPropertyValue(rs.getString(i));
+				} else if (SQL_FIELD_CITY_NAME.equalsIgnoreCase(column)) {
+					address.getCity().setPropertyValue(rs.getString(i));
+				} else if (SQL_FIELD_STATE_NAME.equalsIgnoreCase(column)) {
+					address.getState().setPropertyValue(rs.getString(i));
+				} else if (SQL_FIELD_COUNTRY_ID.equalsIgnoreCase(column)) {
+					Country country = Country.getCountryByCode(rs.getString(i));
+					address.getCountry().setPropertyValue(country.getName());
+				} else {
+					throw new SQLException("\n=> Unhandled column: " + column);
+				}
+				
+			}		
+		}
 	}
 
@@ -1169,4 +1521,35 @@
 			} else if (SQL_FIELD_CATEGORY.equalsIgnoreCase(column)) {
 				c.getCategories().setPropertyValue(rs.getString(i));
+			} else if (SQL_FIELD_PHOTO.equalsIgnoreCase(column)) {
+				if(c.getPersonalDetail()==null) {
+					PersonalDetail pd = new PersonalDetail();
+					c.setPersonalDetail(pd);
+				}
+				Photo photo = new Photo();
+				photo.setImage(rs.getBytes(i));
+				c.getPersonalDetail().setPhotoObject(photo);
+			} else if (SQL_FIELD_NOTES.equalsIgnoreCase(column)) {
+				
+				try {
+					if(rs.getString(i) != null) {
+						if (c.getNotes() == null
+								|| (c.getNotes() != null && c.getNotes().size() != 1)
+								|| (c.getNotes() != null
+										&& c.getNotes().size() == 1 && c.getNotes()
+										.get(0) == null)) {
+							List<Note> l = new ArrayList<Note>(1);
+							l.add(new Note());
+							c.setNotes(l);
+						}
+						Note n = (Note) c.getNotes().get(0);
+						n.setPropertyValue(rs.getString(i));
+						n.setPropertyType("Body"); // HARD CODE
+					}
+				} catch (Exception e) {
+					log.error(e.getMessage(),e);
+				}
+
+			} else if (SQL_FIELD_NICK.equalsIgnoreCase(column)) {
+				c.getName().getNickname().setPropertyValue(rs.getString(i));
 			} else {
 				throw new SQLException("\n=> Unhandled column: " + column);
