/*
* Copyright (C) 2012 maartenl
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mmud.rest.services;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import mmud.database.entities.characters.Person;
import mmud.database.entities.characters.User;
import mmud.database.entities.game.Mail;
import mmud.database.entities.web.CharacterInfo;
import mmud.database.entities.web.Family;
import mmud.database.entities.web.FamilyPK;
import mmud.database.entities.web.FamilyValue;
import mmud.exceptions.MudException;
import mmud.exceptions.MudWebException;
import mmud.rest.webentities.PrivateMail;
import mmud.rest.webentities.PrivatePerson;
/**
* Contains all rest calls that are available to a specific character, with
* authentication and authorization. You can find them at
* /karchangame/resources/private.
*
* @author maartenl
*/
@DeclareRoles("player")
@RolesAllowed("player")
@Stateless
@LocalBean
@Path("/private")
public class PrivateBean
{
@EJB
private MailBean mailBean;
@Resource
private SessionContext context;
/**
* Indicates that only 20 mails may be retrieved per time (basically a
* page).
*/
public static final int MAX_MAILS = 20;
@PersistenceContext(unitName = "karchangamePU")
private EntityManager em;
public PrivateBean()
{
// empty constructor, I don't know why, but it's necessary.
}
/**
* Returns the entity manager of JPA. This is defined in
* build/web/WEB-INF/classes/META-INF/persistence.xml.
*
* @return EntityManager
*/
protected EntityManager getEntityManager()
{
return em;
}
private static final Logger itsLog = Logger.getLogger(PrivateBean.class.getName());
/**
* This method should be called to verify that the target of a certain
* action is indeed a proper authenticated user.
*
* @param name the name to identify the person
* @return the User identified by the name.
* @throws WebApplicationException NOT_FOUND, if the user is either not
* found or is not a proper user. BAD_REQUEST if an unexpected exception
* crops up or provided info is really not proper. UNAUTHORIZED if session
* passwords do not match.
*/
public User authenticate(String name)
{
if (!getPlayerName().equals(name))
{
throw new MudWebException(name, "You are not logged in as " + name, Response.Status.UNAUTHORIZED);
}
User person = getEntityManager().find(User.class, name);
if (person == null)
{
throw new MudWebException(name, "User was not found.", "User was not found (" + name + ")", Status.NOT_FOUND);
}
if (!person.isUser())
{
throw new MudWebException(name, "User was not a user.", "User was not a user (" + name + ")", Status.BAD_REQUEST);
}
return person;
}
/**
* Authenticates a guildmaster.
*
* @param name the name to identify the person
* @return WebApplicationException NOT_FOUND if you are not the member of a guild
* UNAUTHORIZED if you are not the guild master of your guild.
* @see #authenticate(java.lang.String, java.lang.String)
*/
public User authenticateGuildMaster(String name)
{
if (!getPlayerName().equals(name))
{
throw new MudWebException(name, "You are not logged in as " + name, Response.Status.UNAUTHORIZED);
}
User person = authenticate(name);
if (person.getGuild() == null)
{
throw new MudWebException(name, name + " is not a member of a guild.", "Person (" + name + ") is not a member of a guild", Status.NOT_FOUND);
}
if (!person.getGuild().getBoss().getName().equals(person.getName()))
{
throw new MudWebException(name, name + " is not the guild master of " + person.getGuild().getTitle() + ".", "Person (" + name + ") is not the guild master of " + person.getGuild().getName(), Status.UNAUTHORIZED);
}
return person;
}
/**
* Returns a List of your mail, with a maximum of 20 mails and an offset for
* paging.
*
* @param name the name of the user
* @param offset the offset, default is 0 if not provided.
* @return a List of (max 20 by default) mails.
* @throws WebApplicationException <ul><li>UNAUTHORIZED, if the
* authorisation failed. </li><li>BAD_REQUEST if an unexpected exception
* crops up.</li></ul>
* @see #MAX_MAILS
*/
@GET
@Path("{name}/mail")
@Produces(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public List<PrivateMail> listMail(@PathParam("name") String name, @QueryParam("offset") Integer offset)
{
itsLog.finer("entering listMail");
User person = authenticate(name);
List<PrivateMail> res = new ArrayList<>();
try
{
Query query = getEntityManager().createNamedQuery("Mail.listmail");
query.setParameter("name", person);
query.setMaxResults(MAX_MAILS);
if (offset != null)
{
query.setFirstResult(offset);
}
List<Mail> list = query.getResultList();
for (Mail mail : list)
{
PrivateMail pmail = new PrivateMail(mail);
res.add(pmail);
}
// turn off the "newmail" sign.
query = getEntityManager().createNamedQuery("Mail.nonewmail");
query.setParameter("name", person);
query.executeUpdate();
} catch (WebApplicationException e)
{
//ignore
throw e;
} catch (Exception e)
{
throw new MudWebException(name, e, Response.Status.BAD_REQUEST);
}
return res;
}
/**
* Returns a boolean, indicating if you have new mail. (i.e. mail since you
* last checked)
*
* @param name the name of the user
* @return Response.ok if everything is okay.
* @throws WebApplicationException UNAUTHORIZED, if the authorisation
* failed. BAD_REQUEST if an unexpected exception crops up.
*/
@GET
@Path("{name}/newmail")
@Consumes(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public Response hasNewMail(@PathParam("name") String name)
{
itsLog.finer("entering hasNewMail");
Person person = authenticate(name);
boolean result = mailBean.hasNewMail(person);
if (!result)
{
return Response.noContent().build();
}
return Response.ok().build();
}
private Person getPerson(String name)
{
Person toperson = getEntityManager().find(Person.class, name);
if (toperson == null)
{
throw new MudWebException(name, name + " was not found.", "User was not found (" + name + ")", Response.Status.NOT_FOUND);
}
return toperson;
}
private User getUser(String name)
{
User toperson = getEntityManager().find(User.class, name);
if (toperson == null)
{
throw new MudWebException(name, name + " was not found.", "User was not found (" + name + ")", Response.Status.NOT_FOUND);
}
if (!toperson.isUser())
{
itsLog.log(Level.INFO, "user not proper user, {0}", name);
throw new MudWebException(name, name + " was not a proper user.", "User was not a proper user (" + name + ")", Response.Status.BAD_REQUEST);
}
return toperson;
}
/**
* Compose a new mail.
*
* @param newMail the new mail object received from the client.
* @param name the name of the
* @return Response.ok if everything's okay.
* @throws WebApplicationException UNAUTHORIZED, if the authorisation
* failed. BAD_REQUEST if an unexpected exception crops up.
*/
@POST
@Path("{name}/mail")
@Consumes(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public Response newMail(PrivateMail newMail, @PathParam("name") String name)
{
itsLog.finer("entering newMail");
User person = authenticate(name);
User toperson = getUser(newMail.toname);
try
{
Mail mail = new Mail();
mail.setBody(newMail.body);
mail.setDeleted(Boolean.FALSE);
mail.setHaveread(Boolean.FALSE);
mail.setItemDefinition(null);
mail.setId(null);
mail.setName(person);
mail.setNewmail(Boolean.TRUE);
mail.setSubject(newMail.subject);
mail.setToname(toperson);
mail.setWhensent(new Date());
getEntityManager().persist(mail);
} catch (WebApplicationException e)
{
//ignore
throw e;
} catch (Exception e)
{
throw new MudWebException(name, e, Response.Status.BAD_REQUEST);
}
itsLog.finer("exiting newMail");
return Response.ok().build();
}
private Mail getMail(String toname, Long id)
{
Mail mail = getEntityManager().find(Mail.class, id);
if (mail == null)
{
throw new MudWebException(toname, "Mail " + id + " not found.", Response.Status.NOT_FOUND);
}
if (mail.getDeleted())
{
throw new MudWebException(toname, "Mail with id " + id + " was deleted.", Response.Status.NOT_FOUND);
}
if (!mail.getToname().getName().equals(toname))
{
itsLog.log(Level.INFO, "mail {0} not for {1}", new Object[]
{
id, toname
});
throw new MudWebException(toname, "Mail with id " + id + " was not for " + toname + ".", Response.Status.UNAUTHORIZED);
}
return mail;
}
/**
* Returns a single mail based by id. Can only retrieve mail destined for
* the user requesting the mail.
*
* @param name the name of the user
* @param id the id of the mail to get
* @return A specific mail.
* @throws WebApplicationException UNAUTHORIZED, if the authorisation
* failed. BAD_REQUEST if an unexpected exception crops up.
*/
@GET
@Path("{name}/mail/{id}")
@Produces(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public PrivateMail getMailInfo(@PathParam("name") String name, @PathParam("id") long id)
{
itsLog.finer("entering getMail");
Person person = authenticate(name);
try
{
Mail mail = getMail(person.getName(), id);
// turn off the "have not read" sign.
mail.setHaveread(Boolean.TRUE);
itsLog.finer("exiting getMail");
return new PrivateMail(mail);
} catch (WebApplicationException e)
{
//ignore
throw e;
} catch (Exception e)
{
throw new MudWebException(name, e, Response.Status.BAD_REQUEST);
}
}
/**
* Creates an itemDefinitionId instance (and, if required, an
* itemDefinitionId definition) representing an in-game version of a single
* mail based by id. <img
* src="doc-files/PrivateBean_createMailItem.png">
*
* @param name the name of the user
* @param id the id of the mail to get
* @param itemDefinitionId the kind of itemDefinitionId that is to be made.
* It may be null, if there already is attached a item definition to the
* mail.
* @return Response.ok if everything is fine.
* @see Mail#ITEMS
* @throws WebApplicationException UNAUTHORIZED, if the authorisation
* failed. BAD_REQUEST if an unexpected exception crops up.
* @startuml doc-files/PrivateBean_createMailItem.png
* (*) --> "check params"
* --> "getMail"
* if "has Item Definition" then
* ->[true] "create instance object"
* else
* ->[false] "get maxid"
* --> "create itemDefinitionId definition"
* --> "set itemdefinition into the mail"
* --> "create instance object"
* endif
* --> "create inventory object"
* -->(*)
* @enduml
*/
@GET
@Path("{name}/mail/{id}/createMailItem/{item}")
@Consumes(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public Response createMailItem(@PathParam("name") String name, @PathParam("id") long id, @PathParam("item") int itemDefinitionId)
{
return Response.noContent().build();
// TODO MLE: this needs to get fixed.
//
// itsLog.finer("entering createMailItem");
// Person person = authenticate(name, lok);
//
// try
// {
// // get the specific mail with id {id}
// Mail mail = getMail(person.getName(), id);
//
// itsLog.finer("createMailItem: retrieve template item definition");
//
// ItemDefinition newdef = null;
// if (mail.getItemDefinition() == null)
// {
// if (itemDefinitionId >= Mail.ITEMS.length && itemDefinitionId < 0)
// {
// itsLog.finer("createMailItem: wrong item def");
// throw new WebApplicationException(Response.Status.BAD_REQUEST);
// }
//
// ItemDefinition definition = getEntityManager().find(ItemDefinition.class, Mail.ITEMS[itemDefinitionId]);
//
// int max_id = 0;
// // retrieve max item_idItemDefinition.maxid
// itsLog.finer("createMailItem: retrieve max id");
// Query query = getEntityManager().createNamedQuery("ItemDefinition.maxid");
// max_id = (Integer) query.getSingleResult();
//
// // create itemDefinitionId definition
// itsLog.finer("createMailItem: create item definition");
// newdef = new ItemDefinition();
// newdef.setId(max_id + 1);
// newdef.setName(definition.getName());
// newdef.setAdject1(definition.getAdject1());
// newdef.setAdject2(definition.getAdject2());
// newdef.setAdject3(definition.getAdject3());
// newdef.setGetable(definition.getGetable());
// newdef.setDropable(definition.getDropable());
// newdef.setVisible(definition.getVisible());
// newdef.setDescription(definition.getDescription());
// newdef.setReaddescription(definition.getReaddescription().replace("letterhead", "<div id=\"karchan_letterhead\">" + mail.getSubject() + "</div>").replace("letterbody", "<div id=\"karchan_letterbody\">" + mail.getBody() + "</div>").replace("letterfooter", "<div id=\"karchan_letterfooter\">" + mail.getName().getName() + "</div>"));
//
// newdef.setCopper(definition.getCopper());
// newdef.setOwner(definition.getOwner());
// newdef.setNotes(definition.getNotes());
// newdef.setCreation(new Date());
// getEntityManager().persist(newdef);
//
// // set itemDefinitionId definition into the mail
// itsLog.finer("createMailItem: set itemdefinition into the mail");
// mail.setItemDefinition(newdef);
// }
//
// // create itemDefinitionId instance
// itsLog.finer("createMailItem: create instance object " + mail.getItemDefinition() + " " + name);
//
// Item item = new Item();
// item.setOwner(getEntityManager().find(Admin.class, Admin.DEFAULT_OWNER));
// item.setItemDefinition(mail.getItemDefinition());
// item.setCreation(new Date());
// getEntityManager().persist(item);
//
// if (item.getId() != null)
// {
// // put item instance into inventory
// itsLog.finer("createMailItem: create inventory object for " + name);
//
// CharitemTable inventory = new CharitemTable();
// inventory.setId(item.getId());
// inventory.setBelongsto(person);
// inventory.setItem(item);
// getEntityManager().persist(inventory);
// }
// } catch (WebApplicationException e)
// {
// //ignore
// throw e;
// } catch (ConstraintViolationException e)
// {
// StringBuilder buffer = new StringBuilder("ConstraintViolationException:");
// for (ConstraintViolation<?> violation : e.getConstraintViolations())
// {
// buffer.append(violation);
// }
// throw new RuntimeException(buffer.toString(), e);
//
// } catch (Exception e)
// {
// itsLog.finer("createMailItem: throws ", e);
// throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
// }
// itsLog.finer("exiting createMailItem");
// return Response.ok().build();
}
/**
* Deletes a single mail based by id.
*
* @param name the name of the user
* @param id the id of the mail to delete
* @return Response.ok() if all is well.
* @throws WebApplicationException UNAUTHORIZED, if the authorisation
* failed. BAD_REQUEST if an unexpected exception crops up.
*/
@DELETE
@Path("{name}/mail/{id}")
@Consumes(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public Response deleteMail(@PathParam("name") String name, @PathParam("id") long id)
{
itsLog.finer("entering deleteMail");
Person person = authenticate(name);
// get the specific mail with id {id}
Mail mail = getMail(person.getName(), id);
mail.setDeleted(Boolean.TRUE);
itsLog.finer("exiting deleteMail");
return Response.ok().build();
}
/**
* Adds or updates your current character info.
*
* @param name the name of the user
* @param cinfo the object containing the new stuff to update.
* @return Response.ok if everything is okay.
* @throws WebApplicationException UNAUTHORIZED, if the authorisation
* failed. BAD_REQUEST if an unexpected exception crops up.
*/
@PUT
@Path("{name}/charactersheet")
@Consumes(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public Response updateCharacterSheet(@PathParam("name") String name, PrivatePerson cinfo)
{
itsLog.finer("entering updateCharacterSheet");
Person person = authenticate(name);
if (!person.getName().equals(cinfo.name))
{
throw new MudWebException(name, "User trying to update somebody elses charactersheet?",
person.getName() + " trying to update charactersheet of " + cinfo.name, Response.Status.UNAUTHORIZED);
}
try
{
CharacterInfo characterInfo = getEntityManager().find(CharacterInfo.class, person.getName());
boolean isNew = false;
if (characterInfo == null)
{
isNew = true;
characterInfo = new CharacterInfo();
characterInfo.setName(person.getName());
}
characterInfo.setImageurl(cinfo.imageurl);
person.setTitle(cinfo.title);
characterInfo.setHomepageurl(cinfo.homepageurl);
characterInfo.setDateofbirth(cinfo.dateofbirth);
characterInfo.setCityofbirth(cinfo.cityofbirth);
characterInfo.setStoryline(cinfo.storyline);
if (isNew)
{
getEntityManager().persist(characterInfo);
}
} catch (WebApplicationException e)
{
//ignore
throw e;
} catch (MudException e)
{
throw new MudWebException(name, e, Response.Status.BAD_REQUEST);
}
return Response.ok().build();
}
/**
* Add or updates some family values from your family tree.
*
* @param name the name of the user
* @param toname the name of the user you are related to
* @param description the description of the family relation, for example
* "mother".
* @return Response.ok if everything's okay.
* @throws WebApplicationException UNAUTHORIZED, if the authorisation
* failed. BAD_REQUEST if an unexpected exception crops up.
*/
@PUT
@Path("{name}/charactersheet/familyvalues/{toname}/{description}")
@Consumes(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public Response updateFamilyvalues(@PathParam("name") String name, @PathParam("toname") String toname, @PathParam("description") Integer description)
{
itsLog.finer("entering updateFamilyvalues");
if (description == null || description == 0)
{
throw new MudWebException(name, "An invalid relationship was provided.", Response.Status.BAD_REQUEST);
}
if (toname == null || "".equals(toname.trim()))
{
throw new MudWebException(name, "No person provided.", Response.Status.BAD_REQUEST);
}
User person = authenticate(name);
Person toperson = getPerson(toname);
try
{
FamilyValue familyValue = getEntityManager().find(FamilyValue.class, description);
if (familyValue == null)
{
throw new MudWebException(name, "Family value was not found.",
"Family value " + description + " was not found.",
Response.Status.BAD_REQUEST);
}
FamilyPK pk = new FamilyPK();
pk.setName(person.getName());
pk.setToname(toperson.getName());
Family family = getEntityManager().find(Family.class, pk);
boolean isNew = family == null;
if (family == null)
{
family = new Family();
family.setFamilyPK(pk);
}
family.setDescription(familyValue);
if (isNew)
{
getEntityManager().persist(family);
}
} catch (MudWebException e)
{
//ignore
throw e;
}
itsLog.finer("exiting updateFamilyvalues");
return Response.ok().build();
}
/**
* Deletes some family values from your family tree.
*
* @param name the name of the user
* @param toname the name of the user you are related to
* @return Response.ok if everything is okay.
* @throws WebApplicationException UNAUTHORIZED, if the authorisation
* failed. BAD_REQUEST if an unexpected exception crops up.
*/
@DELETE
@Path("{name}/charactersheet/familyvalues/{toname}")
@Consumes(
{
MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
})
public Response deleteFamilyvalues(@PathParam("name") String name, @PathParam("toname") String toname)
{
itsLog.finer("entering deleteFamilyValues");
Person person = authenticate(name);
FamilyPK pk = new FamilyPK();
pk.setName(person.getName());
pk.setToname(toname);
Family family = getEntityManager().find(Family.class, pk);
if (family == null)
{
throw new MudWebException(name, "Unable to delete family value. Family value not found.", Response.Status.NOT_FOUND);
}
getEntityManager().remove(family);
itsLog.finer("exiting deleteFamilyValues");
return Response.ok().build();
}
/**
* Provides the player who is logged in during this session.
*
* @return name of the player
* @throws IllegalStateException
*/
protected String getPlayerName() throws IllegalStateException
{
final String name = context.getCallerPrincipal().getName();
if (name.equals("ANONYMOUS"))
{
throw new MudWebException(null, "Not logged in.", Response.Status.UNAUTHORIZED);
}
return name;
}
}