/**
* Copyright (c) 2009--2012 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package com.redhat.rhn.domain.kickstart.cobbler;
import com.redhat.rhn.common.conf.ConfigDefaults;
import com.redhat.rhn.common.localization.LocalizationService;
import com.redhat.rhn.common.util.FileUtils;
import com.redhat.rhn.common.util.StringUtil;
import com.redhat.rhn.common.validator.ValidatorException;
import com.redhat.rhn.domain.org.Org;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import java.io.File;
/**
* CobblerSnippet - Class representation of a Cobbler snippet
* @version $Rev: 1 $
*/
public class CobblerSnippet implements Comparable {
private File path;
private Org org;
/**
* Method to return the main cobbler snippets dir
* (i.e. /var/lib/cobbler/snippets) .. Reason this is
* a method is because we want it to be
* changeable for unit tests..
* @return the cobbler snippets dir
*/
public static File getCobblerSnippetsDir() {
File fl = new File(ConfigDefaults.get().getCobblerSnippetsDir());
return fl;
}
/**
* Method to return the base spacewalk snippets dir
* (i.e. /var/lib/cobbler/snippets/spacewalk) ..
* Reason this is
* a method is because we want it to be
* changeable for unit tests..
* @return the spacewalk snippets dir
*/
public static File getSpacewalkSnippetsDir() {
return new File(getCobblerSnippetsDir(), "spacewalk");
}
/**
* Cobbler Snippet method to be called
* when creating or updating the actual instance of a cobbler snippet.
* This is in a typical case would be considered a
* manager layer method but its small enough to merit not creating
* another command/class for it.
* @param create True if we are creating a new editable snippet
* False updating an existing editable snippet
* @param name the name of the snippet.
* @param contents the contents of the snippet.
* @param org the org of the editable snippet.
* @return the newly Created or updated cobbler snippet..
*/
public static CobblerSnippet createOrUpdate(boolean create,
String name, String contents, Org org) {
CobblerSnippet snip = loadEditable(name, org);
if (create) {
validateNonExistence(snip.path);
}
snip.writeContents(contents);
return snip;
}
/**
* Renames cobbler snippet to a new name..
* @param name the name fo the new cobbler snippet
*/
public void rename(String name) {
verifyEditable();
if (!getName().equals(name)) {
CobblerSnippet snip = loadEditable(name, org);
validateNonExistence(snip.path);
if (path.exists() && !path.renameTo(snip.path)) {
ValidatorException.raiseException("cobbler.snippet.rename-error",
getName(), name);
}
path = snip.path;
}
}
private static void validateNonExistence(File path) {
if (path.exists()) {
ValidatorException.
raiseException("cobbler.snippet.filenameexists.message",
path.getName());
}
}
private CobblerSnippet() {
}
/**
* Constructor to load a spacewalk Editable (as in Org Based) cobbler snippet..
* These are snippets that reside in
* /var/lib/cobbler/snippets/spacewalk/${org.id}/....
* Note validation errors will be raised if the name contains slashes
* /var/lib/cobbler/snippets/spacewalk/${org.id}/${name}
* @param nameIn the snippet name ${name} in
* ${spacewalk.snippets.dir}/${org.id}/${name}
* @param orgIn the org in ${spacewalk.snippets.dir}/${org.id}/${name}
* @return the cobbler snippet.
*/
public static CobblerSnippet loadEditable(String nameIn, Org orgIn) {
validateFileName(nameIn);
CobblerSnippet snippy = new CobblerSnippet();
snippy.org = orgIn;
snippy.path = new File(getPrefixFor(snippy.org) + "/" + nameIn);
return snippy;
}
/**
* Performs the same function as loadEditable, except it will return null instead
* of a bad snippet
* @param nameIn the snippet name ${name} in
* ${spacewalk.snippets.dir}/${org.id}/${name}
* @param orgIn the org in ${spacewalk.snippets.dir}/${org.id}/${name}
* @return the cobbler snippet. or null if doesn't exist
*/
public static CobblerSnippet loadEditableIfExists(String nameIn, Org orgIn) {
File f = new File(getPrefixFor(orgIn) + "/" + nameIn);
if (f.exists()) {
return loadEditable(nameIn, orgIn);
}
return null;
}
/**
* Constructor load a non editable spacewalk cobbler snippets
* as in all the snippets that reside under
* /var/lib/cobbler/snippets/ except
* /var/lib/cobbler/snippets/spacewalk
* Idea here is that this list is read only
* and operations such as write operations cannot be performed..
* @param pathIn /var/lib/cobbler/snippets/foo/bar
* @return the cobbler snippet.
*/
public static CobblerSnippet loadReadOnly(File pathIn) {
validateCommonPath(pathIn);
CobblerSnippet snippy = new CobblerSnippet();
snippy.path = pathIn;
return snippy;
}
/**
* The path for display purposes
* @return the display path
*/
public String getDisplayPath() {
return getPath().getAbsolutePath();
}
/**
* Returns the org associated to this snippet
* or null if none is associated
* @return the org or null
*/
public Org getOrg() {
return org;
}
/**
* Getter for name
* @return String to get
*/
public File getPath() {
return this.path;
}
/**
* Getter for contents
* @return String to get
*/
public String getContents() {
if (path.exists()) {
return FileUtils.readStringFromFile(path.getAbsolutePath());
}
return null;
}
/**
* Basically writes the snippet contents sent to this method
* to the disk..
* Note: only editable snippets can be updated,
* i.e. snippets under
* ${spacewalk.snippets.dir}/${org.id}/${name}
* @param contents the contents of the snippet
*/
public void writeContents(String contents) {
verifyEditable();
if (!path.exists()) {
path.getParentFile().mkdirs();
}
FileUtils.writeStringToFile(StringUtil.webToLinux(contents),
path.getAbsolutePath());
}
/**
* Method to allow you to delete the snippet.
* Note: only editable snippets can be deleted, i.e. snippets under
* ${spacewalk.snippets.dir}/${org.id}/${name}
*/
public void delete() {
verifyEditable();
if (!path.exists() || !path.delete()) {
ValidatorException.raiseException("cobbler.snippet.couldnotdelete.message",
getName());
}
}
/**
* Note: only snippets under
* ${spacewalk.snippets.dir}/${org.id}/${name}
* are editable..
* @return true if this cobbler snippet is editable..
*/
public boolean isEditable() {
return !isCommonPath(path);
}
/**
* Returns just the name of the snippet file (same as basename)
* i.e. returns ${name} in ${spacewalk.snippets.dir}/${org.id}/${name}
* @return the base name of the snippet file
*/
public String getName() {
if (path.getParentFile().equals(getSpacewalkSnippetsDir())) {
return getSpacewalkSnippetsDir().getName() +
File.separator + path.getName();
}
return path.getName();
}
/**
* Returns the name of the directory hosting the snippet file (same as dirname)
* i.e. returns ${spacewalk.snippets.dir}/${org.id}
* in ${spacewalk.snippets.dir}/${org.id}/${name}
* @return the name of the directory hosting the snippet file
*/
public String getPrefix() {
return path.getParent();
}
/**
* Returns the display name used by the UI
* Seems like a useful method reused in different places
* in the UI thought this would be a good place..
* @return the display name
*/
public String getDisplayName() {
LocalizationService ls = LocalizationService.getInstance();
return ls.getMessage("cobbler.snippet.header.name", getName());
}
/**
* The actual cobbler fragment associated to this snippet
* @return cobbler code fragment
*/
public String getFragment() {
String snipPath = getDisplayPath().substring(
getCobblerSnippetsDir().getPath().length() + 1);
return makeFragment(snipPath);
}
/**
* Returns a Cobbler snippet fragment with the given path
* @param path the path to make a snippet of
* @return the snippet fragment
*/
public static String makeFragment(String path) {
return String.format("$SNIPPET('%s')", path);
}
/**
* Returns the name of the dir that should be hosting scripts
* for the snippet. This is useful for example while
* creating snippets.
* @param org the org hosting the snippet, or null if its a common org
* @return the name of the directory that should host the snippet file
*/
public static String getPrefixFor(Org org) {
if (org == null) {
return getCobblerSnippetsDir().getAbsolutePath();
}
return getSpacewalkSnippetsDir().getAbsolutePath() + "/" + org.getId();
}
private static void validateFileName(String name) {
// file names can have no slashes/ can't be blan or
// can't start with a period (for it'll mean its hidden)
// can't contain " to protect against xss
if (StringUtils.isBlank(name) || name.contains("/") || name.startsWith(".") ||
name.contains("\"") || name.contains("&")) {
ValidatorException.raiseException("cobbler.snippet.invalidfilename.message");
}
}
private static void validateCommonPath(File path) {
if (!path.exists() || path.isHidden() || !path.isFile() || !isCommonPath(path) ||
path.getAbsolutePath().contains("\"") ||
path.getAbsolutePath().contains("&")) {
ValidatorException.raiseException("cobbler.snippet.invalidfilename.message");
}
}
private static boolean isCommonPath(File path) {
boolean isFileInsideSpacewalkTopLevelDir = path.isFile() &&
path.getParentFile().equals(getSpacewalkSnippetsDir());
if (isFileInsideSpacewalkTopLevelDir) {
return true;
}
return !path.getAbsolutePath().startsWith(
getSpacewalkSnippetsDir().getAbsolutePath()) &&
path.getAbsolutePath().
startsWith(getCobblerSnippetsDir().getAbsolutePath());
}
private void verifyEditable() {
if (!isEditable()) {
ValidatorException.raiseException("cobbler.snippet.invalidfilename.message");
}
}
/**
* {@inheritDoc}
*/
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CobblerSnippet)) {
return false;
}
CobblerSnippet that = (CobblerSnippet) o;
return getPath().equals(that.getPath());
}
/**
* {@inheritDoc}
*/
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getPath());
return b.toHashCode();
}
/**
* {@inheritDoc}
*/
public int compareTo(Object o) {
if (equals(o)) {
return 0;
}
CobblerSnippet that = (CobblerSnippet) o;
return that.getPath().compareTo(getPath());
}
}