package com.perforce;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.perforce.model.License;
import com.perforce.model.Notification;
import com.perforce.model.UserInfo;
import com.perforce.p4java.admin.IProperty;
import com.perforce.p4java.core.IUser;
import com.perforce.p4java.core.IUserGroup;
import com.perforce.p4java.core.IUserSummary;
import com.perforce.p4java.exception.AccessException;
import com.perforce.p4java.exception.ConfigException;
import com.perforce.p4java.exception.ConnectionException;
import com.perforce.p4java.exception.P4JavaException;
import com.perforce.p4java.exception.RequestException;
import com.perforce.p4java.impl.generic.core.User;
import com.perforce.p4java.impl.generic.core.UserGroup;
import com.perforce.p4java.option.UsageOptions;
import com.perforce.p4java.option.server.GetPropertyOptions;
import com.perforce.p4java.option.server.GetUsersOptions;
import com.perforce.p4java.option.server.LoginOptions;
import com.perforce.p4java.option.server.PropertyOptions;
import com.perforce.p4java.option.server.UpdateUserGroupOptions;
import com.perforce.p4java.server.IOptionsServer;
import com.perforce.p4java.server.ServerFactory;
/**
* @author apetersen
*
*/
public class P4Helper {
static Logger logger = Logger.getLogger(P4Helper.class.getName());
private IOptionsServer server;
private UserInfo userInfo;
/**
* Constructor creates a new P4Helper instance.
* @throws ApplicationException
*/
public P4Helper() {
logger.debug("P4Helper()");
}
/**
* Used to create a reference to the Perforce server. This method uses the
* attributes stored in the ServletContext that were read from the configuration
* file.
*
* @return IOptionsServer
* @throws ApplicationException
*/
private IOptionsServer getServer() throws ApplicationException {
try {
String host = Utility.getStringAttribute(Constants.HOSTNAME);
String port = Utility.getStringAttribute(Constants.PORTNUM);
String uri = String.format("p4java://%s:%s", host, port);
return ServerFactory.getOptionsServer(uri, null, new UsageOptions(null).setProgramName("P4SelfService").setProgramVersion("1.0"));
} catch (Exception e) {
throw new ApplicationException(e);
}
}
/**
* Used to create a reference to the Perforce server with the user info
*
* @param UserInfo ui
* @return IOptionsServer
* @throws ApplicationException
*/
private IOptionsServer getUserServer(UserInfo ui) throws ApplicationException {
IOptionsServer server = getServer();
server.setUserName(ui.getName());
server.setAuthTicket(ui.getTicket());
try {
server.connect();
} catch (ConnectionException e) {
throw new ApplicationException(e);
} catch (AccessException e) {
throw new ApplicationException(e);
} catch (RequestException e) {
throw new ApplicationException(e);
} catch (ConfigException e) {
throw new ApplicationException(e);
}
return server;
}
/**
* @param String name
* @return boolean
*/
private boolean attributeExists(String name) {
boolean exists = false;
Enumeration<String> names = ServletContextHolder.getInstance().getServletContext().getAttributeNames();
while(names.hasMoreElements()) {
if(names.nextElement().equals(name)) {
exists = true;
break;
}
}
return exists;
}
/**
* Returns the maximum protection for the current user
*
* @return String
* @throws ApplicationException
*/
public String getMaxProtects() throws ApplicationException {
return getMaxProtects(null);
}
/**
* Returns the maximum protections for the specified user. If user is
* null, then the current user is used.
*
* @param String user
* @return String
* @throws ApplicationException
*/
public String getMaxProtects(String user) throws ApplicationException {
if(user == null) {
user = userInfo.getName();
}
String result = executeP4Command(false, null, "protects","-u",user,"-m");
return result.trim();
}
/**
* Setter for the userInfo instance variable
*
* @param UserInfo userInfo
*/
public void setUserInfo(UserInfo userInfo) {
this.userInfo = userInfo;
}
/**
* Getter for the userInfo instance variable
*
* @return UserInfo
*/
public UserInfo getUserInfo() {
return userInfo;
}
/**
* Method to configure the server using the admin username/password
*
* @throws ApplicationException
*/
public void configServer() throws ApplicationException {
logger.debug("config()");
try {
server = getServer();
server.connect();
server.setUserName(Utility.getStringAttribute(Constants.ADMINUSER));
server.login(Utility.getStringAttribute(Constants.ADMINPASS));
} catch (Exception ex) {
throw new ApplicationException(ex);
}
}
/**
* Method to determine if the host/port is pingable
*
* @return boolean
*/
public boolean canPing() {
boolean pingable = false;
Socket socket = new Socket();
String host = Utility.getStringAttribute(Constants.HOSTNAME);
String portStr = Utility.getStringAttribute(Constants.PORTNUM);
logger.debug("attempting ping of " + host + ":" + portStr);
int port = Integer.parseInt(portStr);
try {
socket.connect(new InetSocketAddress(host, port), 1000);
pingable = true;
} catch (Exception ex) {
logger.debug(ex.getMessage());
} finally {
try {
socket.close();
} catch (IOException e) {}
}
return pingable;
}
/**
* Method to determine if the server is responsive
*
* @return boolean
*/
public boolean isConnected() {
logger.debug("isConnected()");
boolean connected = false;
try {
server.getServerInfo();
connected = true;
} catch (Exception ex) {
logger.error("cannot get connection to server");
}
return connected;
}
private String p4port = null;
private String getP4Port() {
if(p4port == null) {
String host = Utility.getStringAttribute(Constants.HOSTNAME);
String port = Utility.getStringAttribute(Constants.PORTNUM);
if(attributeExists(Constants.PROTOCOL)) {
String proto = Utility.getStringAttribute(Constants.PROTOCOL);
p4port = String.format("%s:%s:%s", proto, host, port);
} else {
p4port = String.format("%s:%s", host, port);
}
}
return p4port;
}
/**
* @param boolean ztag
* @param String user
* @param String command
* @param commandArgs
* @return String
* @throws ApplicationException
*/
private String executeP4Command(boolean ztag, String user, String command, String... commandArgs) throws ApplicationException {
List<String> cmd = new ArrayList<String>();
String p4 = Utility.getStringAttribute(Constants.P4_PATH);
cmd.add(p4);
if(ztag) {
cmd.add("-Ztag");
}
cmd.add("-p");
cmd.add(getP4Port());
cmd.add("-u");
if(user != null) {
cmd.add(user);
} else {
cmd.add(Utility.getStringAttribute(Constants.ADMINUSER));
}
if(command == null) {
throw new ApplicationException("Command cannot be null");
}
cmd.add(command);
for(String arg : commandArgs) {
cmd.add(arg);
}
StringBuffer output = new StringBuffer();
Process p;
try {
logger.debug(StringUtils.join(cmd, " "));
p = Runtime.getRuntime().exec(StringUtils.join(cmd, " "));
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
public boolean userExists(String user) throws ApplicationException {
boolean exists = false;
try {
GetUsersOptions options = new GetUsersOptions();
options.setIncludeServiceUsers(true);
List<IUserSummary> userlist = server.getUsers(null, options);
for (IUserSummary u : userlist) {
if (u.getLoginName().equals(user)) {
exists = true;
break;
}
}
} catch (P4JavaException e) {
throw new ApplicationException(e);
}
return exists;
}
public UserInfo getUser(String user) throws ApplicationException {
UserInfo ui = null;
if(user != null && userExists(user)) {
try {
IUser u = server.getUser(user);
ui = new UserInfo();
ui.setName(u.getLoginName());
ui.setFullName(u.getFullName());
ui.setEmail(u.getEmail());
} catch (Exception ex) {
throw new ApplicationException(ex);
}
}
return ui;
}
public UserInfo getUserByEmail(String email) throws ApplicationException {
UserInfo ui = null;
if(email != null) {
try {
GetUsersOptions options = new GetUsersOptions();
options.setIncludeServiceUsers(true);
List<IUserSummary> userlist = server.getUsers(null, options);
for (IUserSummary u : userlist) {
if (u.getEmail().equals(email)) {
ui = new UserInfo();
ui.setName(u.getLoginName());
ui.setFullName(u.getFullName());
ui.setEmail(u.getEmail());
break;
}
}
} catch (Exception ex) {
throw new ApplicationException(ex);
}
}
return ui;
}
public boolean userEmailExists(String email) throws ApplicationException {
boolean exists = false;
UserInfo ui = getUserByEmail(email);
if(ui != null) {
exists = true;
}
return exists;
}
public UserInfo loginUser(String username, String password) throws ApplicationException {
UserInfo ui = null;
LoginOptions options = new LoginOptions(false, true);
StringBuffer ticket = new StringBuffer();
try {
IOptionsServer s = getServer();
s.connect();
s.setUserName(username);
s.login(password, ticket, options);
IUser u = s.getUser(username);
s.disconnect();
ui = new UserInfo();
ui.setName(u.getLoginName());
ui.setFullName(u.getFullName());
ui.setEmail(u.getEmail());
ui.setTicket(ticket.toString());
} catch (Exception e) {
e.printStackTrace();
throw new ApplicationException(e);
}
return ui;
}
public boolean groupExists(String groupname) throws ApplicationException {
return getGroups(true).contains(groupname);
}
public List<String> getGroups(boolean showHidden) throws ApplicationException {
return getUserGroups(null, showHidden);
}
public List<String> getUserGroups(String user, boolean showHidden) throws ApplicationException {
List<String> groups = new ArrayList<String>();
try {
List<IUserGroup> userGroups = server.getUserGroups(user, false, false, 0);
for (IUserGroup g : userGroups) {
groups.add(g.getName());
}
} catch (Exception e) {
throw new ApplicationException(e);
}
if(!showHidden) {
groups.removeAll(getHiddenGroups());
}
return groups;
}
public void createGroup(String groupname, List<String> owners, List<String> members) throws ApplicationException {
IUserGroup group = new UserGroup();
group.setName(groupname);
group.setUsers(members);
group.setOwners(owners);
try {
server.createUserGroup(group);
} catch (Exception ex) {
throw new ApplicationException(ex);
}
}
public void deleteGroup(String groupname) throws ApplicationException {
try {
IUserGroup group = server.getUserGroup(groupname);
if(group == null) {
throw new ApplicationException("group not found");
}
server.deleteUserGroup(group);
} catch (Exception ex) {
throw new ApplicationException(ex);
}
}
public void removeUserFromGroup(String username, String groupname) throws ApplicationException {
if(username == null) {
throw new ApplicationException("user not specified");
}
if(groupname == null) {
throw new ApplicationException("group not specified");
}
if(!groupExists(groupname)) {
throw new ApplicationException("group does not exist");
}
try {
IUserGroup group = server.getUserGroup(groupname);
if(group.getUsers() == null || (!group.getUsers().contains(username))) {
throw new ApplicationException("user is not in the group");
}
group.getUsers().remove(username);
if(group.getUsers().size() == 0) {
// need to prevent auto-deletion of group if this is the last user
if(group.getOwners() == null || group.getOwners().size() == 0) {
// if there is no owner, set the default group owner
List<String> owners = new ArrayList<String>();
owners.add(Utility.getStringAttribute(Constants.DEFAULT_GROUP_OWNER));
group.setOwners(owners);
}
}
UpdateUserGroupOptions options = new UpdateUserGroupOptions();
server.updateUserGroup(group, options);
} catch (Exception ex) {
throw new ApplicationException(ex);
}
}
public void addUserToGroup(String username, String groupname) throws ApplicationException {
if(username == null) {
throw new ApplicationException("user not specified");
}
if(groupname == null) {
throw new ApplicationException("group not specified");
}
if(!groupExists(groupname)) {
throw new ApplicationException("group does not exist");
}
try {
IUserGroup group = server.getUserGroup(groupname);
if(group.getUsers() == null) {
group.setUsers(new ArrayList<String>());
}
if(group.getUsers().contains(username)) {
throw new ApplicationException("user is already in group");
}
group.getUsers().add(username);
UpdateUserGroupOptions options = new UpdateUserGroupOptions();
server.updateUserGroup(group, options);
} catch (P4JavaException ex) {
throw new ApplicationException(ex);
}
}
public void setUserGroupRequest(String userId, String groupName) throws ApplicationException {
if(userId == null) {
throw new ApplicationException("user not specified");
}
if(groupName == null) {
throw new ApplicationException("group not specified");
}
if(!groupExists(groupName)) {
throw new ApplicationException("group does not exist");
}
try {
PropertyOptions options = new PropertyOptions();
options.setGroup(groupName);
String propName = Constants.GROUP_REQUEST_PREFIX + userId;
long unixTime = System.currentTimeMillis() / 1000L;
server.setProperty(propName, Long.toString(unixTime), options);
} catch (P4JavaException pe) {
throw new ApplicationException(pe);
}
}
public List<IProperty> getGroupRequests(String groupName) throws ApplicationException {
return getProperties(Constants.GROUP_REQUEST_PREFIX, groupName, null);
}
public List<IProperty> getUserGroupRequests(String userId, String groupName) throws ApplicationException {
String propName = Constants.GROUP_REQUEST_PREFIX + userId;
return getProperties(propName, groupName, null);
}
public List<String> getHiddenGroups() {
List<String> hidden = new ArrayList<String>();
if(attributeExists(Constants.HIDDEN_GROUPS)) {
String[] parts = Utility.getStringAttribute(Constants.HIDDEN_GROUPS).split(",");
hidden = Arrays.asList(parts);
}
return hidden;
}
public List<String> getAutosubscribeGroups() {
List<String> groups = new ArrayList<String>();
if(attributeExists(Constants.AUTOSUBSCRIBE_GROUPS)) {
String[] parts = Utility.getStringAttribute(Constants.AUTOSUBSCRIBE_GROUPS).split(",");
groups = Arrays.asList(parts);
}
return groups;
}
public boolean isSuper() throws ApplicationException {
return isSuper(null);
}
public boolean isSuper(String user) throws ApplicationException {
boolean userIsSuper = false;
String max = getMaxProtects(user);
if("super".equals(max)) {
userIsSuper = true;
}
return userIsSuper;
}
public boolean isAdmin() throws ApplicationException {
return isAdmin(null);
}
public boolean isAdmin(String user) throws ApplicationException {
boolean userIsAdmin = false;
String max = getMaxProtects(user);
logger.debug("max=" + max);
if("super".equals(max) || "admin".equals(max)) {
userIsAdmin = true;
}
logger.debug("userIsAdmin=" + userIsAdmin);
return userIsAdmin;
}
public boolean isServerStoringPasswords() {
return Utility.getBooleanAttribute(Constants.PERFORCE_PASSWORDS, false);
}
public boolean isEnforceUniqueEmails() {
return Utility.getBooleanAttribute(Constants.UNIQUE_EMAILS, true);
}
public boolean isRequireAccountApproval() {
return Utility.getBooleanAttribute(Constants.REQUIRE_ACCOUNT_APPROVAL, true);
}
public void changePassword(String username, String newPassword) throws ApplicationException {
try {
server.changePassword(null, newPassword, username);
} catch (Exception e) {
throw new ApplicationException(e);
}
}
/**
* @param user
* @param notification
* @throws ApplicationException
*/
public void addUserNotification(String user, Notification notification) throws ApplicationException {
List<IProperty> properties = getNotificationProperties(user);
int sequence = getMaxSequence(properties) + 1;
notification.setSequence(sequence);
PropertyOptions options = new PropertyOptions();
options.setUser(user);
options.setSequence(sequence);
try {
server.setProperty(Constants.NOTIFICATION_PREFIX, Utility.convertToJson(notification), options);
} catch (P4JavaException e) {
throw new ApplicationException(e);
}
}
private int getMaxSequence(List<IProperty> properties) {
int max = 0;
for(IProperty p : properties) {
if(p.getSequence() > max) {
max = p.getSequence();
}
}
return max;
}
public List<Notification> getNotifications(String user) throws ApplicationException {
List<Notification> notifications = new ArrayList<Notification>();
List<IProperty> properties = getNotificationProperties(user);
for(IProperty p : properties) {
String json = p.getValue();
logger.debug("#### JSON=" + json);
Notification n = (Notification) Utility.convertFromJson(json, Notification.class);
notifications.add(n);
}
return notifications;
}
private List<IProperty> getNotificationProperties(String user) throws ApplicationException {
GetPropertyOptions options = new GetPropertyOptions();
options.setUser(user);
options.setListAll(true);
options.setName(Constants.NOTIFICATION_PREFIX);
try {
return server.getProperty(options);
} catch (P4JavaException e) {
throw new ApplicationException(e);
}
}
public void getUserNotifications(UserInfo ui) throws ApplicationException {
List<IProperty> properties = getUserProperties(ui, Constants.NOTIFICATION_PREFIX);
}
public String requestPasswordReset(String userId) throws ApplicationException {
if(userExists(userId)) {
UUID uuid = UUID.randomUUID();
String propName = Constants.PASSWORD_REQUEST_PREFIX + userId;
PropertyOptions options = new PropertyOptions();
options.setUser(userId);
try {
server.setProperty(propName, uuid.toString(), options);
} catch (P4JavaException e) {
throw new ApplicationException(e);
}
return uuid.toString();
} else {
throw new ApplicationException("error requesting password reset");
}
}
public void setAccountRequest(UserInfo ui) throws ApplicationException {
logger.debug("requesting account");
String propName = "approval.account." + ui.getName();
String json = Utility.convertToJson(ui);
String encrypted = Utility.encrypt(json);
PropertyOptions options = new PropertyOptions();
options.setGroup(Utility.getStringAttribute(Constants.ACCOUNT_APPROVER_GROUP));
try {
server.setProperty(propName, encrypted, options);
logger.debug("set property " + propName + " to " + encrypted);
} catch (P4JavaException ex) {
throw new ApplicationException(ex);
}
}
public void approveAccountRequest(String approval) throws ApplicationException {
try {
GetPropertyOptions options = new GetPropertyOptions();
options.setName(approval);
server.getProperty(options);
} catch (Exception e) {
throw new ApplicationException(e);
}
}
public void denyAccountRequest(UserInfo info) {
}
public List<IProperty> getProperties() throws ApplicationException {
return getProperties(null, null, null);
}
public List<IProperty> getProperties(String name, String group, String user) throws ApplicationException {
try {
GetPropertyOptions options = new GetPropertyOptions();
options.setListAll(true);
if(name != null) {
options.setName(name);
}
if(user != null) {
options.setUser(user);
}
if(group != null) {
options.setGroup(group);
}
return server.getProperty(options);
} catch (Exception ex) {
throw new ApplicationException(ex);
}
}
public List<IProperty> getUserProperties(UserInfo ui) throws ApplicationException {
return getUserProperties(ui, null);
}
public List<IProperty> getUserProperties(UserInfo ui, String name) throws ApplicationException {
IOptionsServer uServer = null;
try {
uServer = getUserServer(ui);
GetPropertyOptions options = new GetPropertyOptions();
if(name != null) {
options.setName(name);
}
return uServer.getProperty(options);
} catch (Exception ex) {
throw new ApplicationException(ex);
} finally {
if(uServer != null) {
try {
uServer.disconnect();
} catch (ConnectionException | AccessException e) {
e.printStackTrace();
}
}
}
}
public Properties getApplicationSettings() throws ApplicationException {
Properties props = new Properties();
List<IProperty> list = getProperties(Constants.BASE_PROPERTY_PREFIX, null, Utility.getStringAttribute(Constants.ADMINUSER));
for(IProperty p : list) {
props.setProperty(p.getName(), p.getValue());
}
return props;
}
public void storeApplicationSettings(Properties props) throws ApplicationException {
try {
PropertyOptions options = new PropertyOptions();
options.setUser(Utility.getStringAttribute(Constants.ADMINUSER));
Enumeration<?> propNames = props.propertyNames();
while(propNames.hasMoreElements()) {
String name = (String) propNames.nextElement();
String value = props.getProperty(name);
server.setProperty(name, value, options);
}
} catch (P4JavaException pe) {
throw new ApplicationException(pe);
}
}
// public void listProperties() throws ApplicationException {
// try {
// GetPropertyOptions options = new GetPropertyOptions();
// List<IProperty> props = server.getProperty(options);
// for(IProperty p : props) {
// logger.debug(String.format("%s : %s", p.getName(), p.getValue()));
// }
// } catch (Exception ex) {
// throw new ApplicationException(ex);
// }
// }
public void createUser(UserInfo ui) throws ApplicationException {
License lic = getServerLicense();
if(lic.canAddUser()) {
IUser u = new User();
u.setLoginName(ui.getName());
u.setFullName(ui.getFullName());
u.setEmail(ui.getEmail());
try {
server.createUser(u, true);
if(isServerStoringPasswords() && ui.getPassword() != null) {
changePassword(ui.getName(), ui.getPassword());
}
} catch (P4JavaException ex) {
throw new ApplicationException(ex);
}
} else {
throw new ApplicationException("Cannot create new user -- no available user slots");
}
}
public void deleteUser(String username) throws ApplicationException {
try {
server.deleteUser(username, true);
} catch (Exception ex) {
throw new ApplicationException(ex);
}
}
public License getServerLicense() throws ApplicationException {
try {
String result = executeP4Command(true, null, "license", "-u");
License lic = new License(result);
lic.setMinFreeUsers(Utility.getStringAttribute(Constants.FREE_USERS));
return lic;
} catch (Exception e) {
throw new ApplicationException(e);
}
}
}