/**
* Copyright (c) 2009--2016 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.frontend.xmlrpc.packages;
import com.redhat.rhn.FaultException;
import com.redhat.rhn.common.conf.Config;
import com.redhat.rhn.common.conf.ConfigDefaults;
import com.redhat.rhn.common.db.datasource.DataResult;
import com.redhat.rhn.domain.rhnpackage.Package;
import com.redhat.rhn.domain.rhnpackage.PackageArch;
import com.redhat.rhn.domain.rhnpackage.PackageFactory;
import com.redhat.rhn.domain.rhnpackage.PackageSource;
import com.redhat.rhn.domain.role.RoleFactory;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.frontend.dto.PackageFileDto;
import com.redhat.rhn.frontend.xmlrpc.BaseHandler;
import com.redhat.rhn.frontend.xmlrpc.InvalidPackageArchException;
import com.redhat.rhn.frontend.xmlrpc.NoSuchPackageException;
import com.redhat.rhn.frontend.xmlrpc.PackageDownloadException;
import com.redhat.rhn.frontend.xmlrpc.PermissionCheckFailureException;
import com.redhat.rhn.frontend.xmlrpc.RhnXmlRpcServer;
import com.redhat.rhn.manager.download.DownloadManager;
import com.redhat.rhn.manager.rhnpackage.PackageManager;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* PackagesHandler
* @version $Rev$
* @xmlrpc.namespace packages
* @xmlrpc.doc Methods to retrieve information about the Packages contained
* within this server.
*/
public class PackagesHandler extends BaseHandler {
private static Logger logger = Logger.getLogger(PackagesHandler.class);
private static float freeMemCoeff = 0.9f;
/**
* Get Details - Retrieves the details for a given package
* @param loggedInUser The current user
* @param pid The id of the package you're looking for
* @return Returns a Map containing the details of the package
* @throws FaultException A FaultException is thrown if the errata corresponding to
* pid cannot be found.
*
* @xmlrpc.doc Retrieve details for the package with the ID.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "packageId")
* @xmlrpc.returntype
* #struct("package")
* #prop("int", "id")
* #prop("string", "name")
* #prop("string", "epoch")
* #prop("string", "version")
* #prop("string", "release")
* #prop("string", "arch_label")
* #prop_array("providing_channels", "string",
* "Channel label providing this package.")
* #prop("string", "build_host")
* #prop("string", "description")
* #prop("string", "checksum")
* #prop("string", "checksum_type")
* #prop("string", "vendor")
* #prop("string", "summary")
* #prop("string", "cookie")
* #prop("string", "license")
* #prop("string", "file")
* #prop("string", "build_date")
* #prop("string", "last_modified_date")
* #prop("string", "size")
* #prop_desc("string", "path", "The path on the Satellite's file system that
* the package resides.")
* #prop("string", "payload_size")
* #struct_end()
*/
public Map getDetails(User loggedInUser, Integer pid) throws FaultException {
// Get the logged in user
Package pkg = lookupPackage(loggedInUser, pid);
Map returnMap = PackageHelper.packageToMap(pkg, loggedInUser);
return returnMap;
}
/**
* List of Channels that provide a given package
* @param loggedInUser The current user
* @param pid The id of the package in question
* @return Returns an array of maps representing a channel
* @throws FaultException A FaultException is thrown if the errata corresponding to
* pid cannot be found.
*
* @xmlrpc.doc List the channels that provide the a package.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "packageId")
* @xmlrpc.returntype
* #array()
* #struct("channel")
* #prop("string", "label")
* #prop("string", "parent_label")
* #prop("string", "name")
* #struct_end()
* #array_end()
*/
public Object[] listProvidingChannels(User loggedInUser, Integer pid)
throws FaultException {
//Get the logged in user
Package pkg = lookupPackage(loggedInUser, pid);
DataResult dr = PackageManager.orgPackageChannels(loggedInUser.getOrg().getId(),
pkg.getId());
return dr.toArray();
}
/**
* List of Errata that provide the given package.
* @param loggedInUser The current user
* @param pid The id of the package in question
* @return Returns an array of maps representing an erratum
* @throws FaultException A FaultException is thrown if the errata corresponding to
* pid cannot be found.
*
* @xmlrpc.doc List the errata providing the a package.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "packageId")
* @xmlrpc.returntype
* #array()
* #struct("errata")
* #prop("string", "advisory")
* #prop("string", "issue_date")
* #prop("string", "last_modified_date")
* #prop("string", "update_date")
* #prop("string", "synopsis")
* #prop("string", "type")
* #struct_end()
* #array_end()
*/
public Object[] listProvidingErrata(User loggedInUser, Integer pid)
throws FaultException {
// Get the logged in user
Package pkg = lookupPackage(loggedInUser, pid);
DataResult dr = PackageManager.providingErrata(loggedInUser.getOrg().getId(),
pkg.getId());
return dr.toArray();
}
/**
* Get a list of files associated with a package
* @param loggedInUser The current user
* @param pid The id of the package you're looking for
* @return Returns an Array of maps representing a file
* @throws FaultException A FaultException is thrown if the errata corresponding to
* pid cannot be found.
*
* @xmlrpc.doc List the files associated with a package.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "packageId")
* @xmlrpc.returntype
* #array()
* #struct("file info")
* #prop("string", "path")
* #prop("string", "type")
* #prop("string", "last_modified_date")
* #prop("string", "checksum")
* #prop("string", "checksum_type")
* #prop("int", "size")
* #prop("string", "linkto")
* #struct_end()
* #array_end()
*/
public Object[] listFiles(User loggedInUser, Integer pid) throws FaultException {
// Get the logged in user
Package pkg = lookupPackage(loggedInUser, pid);
List<PackageFileDto> dr = PackageManager.packageFiles(pkg.getId());
List returnList = new ArrayList();
/*
* Loop through the data result and merge the data into the correct format
*/
for (PackageFileDto file : dr) {
Map<String, Object> row = new HashMap<String, Object>();
// Default items (mtime and file_size cannot be null)
row.put("path", StringUtils.defaultString(file.getName()));
row.put("last_modified_date", file.getMtime());
row.put("size", file.getFileSize());
row.put("linkto", StringUtils.defaultString(file.getLinkto()));
row.put("checksum", StringUtils.defaultString(file.getChecksum()));
row.put("checksum_type", StringUtils.defaultString(file.getChecksumtype()));
// Determine the file_type
if (file.getChecksum() != null) {
row.put("type", "file");
}
else {
if (file.getLinkto() != null) {
row.put("type", "symlink");
}
else {
row.put("type", "directory");
}
}
returnList.add(row);
}
return returnList.toArray();
}
/**
* Gets the change log for a given package
* @param loggedInUser The current user
* @param pid The id of the package you're looking for
* @return Returns a string with the changelog
* @throws FaultException A FaultException is thrown if the errata corresponding to
* pid cannot be found.
*
* @xmlrpc.doc List the change log for a package.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "packageId")
* @xmlrpc.returntype
* string
*/
public String listChangelog(User loggedInUser, Integer pid) throws FaultException {
// Get the logged in user
Package pkg = lookupPackage(loggedInUser, pid);
return PackageManager.getPackageChangeLog(pkg);
}
/**
* List dependencies for a given package.
* @param loggedInUser The current user
* @param pid The package id for the package in question
* @return Returns an array of maps representing a dependency
* @throws FaultException A FaultException is thrown if the errata corresponding to
* pid cannot be found.
*
* @xmlrpc.doc List the dependencies for a package.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "packageId")
* @xmlrpc.returntype
* #array()
* #struct("dependency")
* #prop("string", "dependency")
* #prop_desc("string", "dependency_type", "One of the following:")
* #options()
* #item("requires")
* #item("conflicts")
* #item("obsoletes")
* #item("provides")
* #item("recommends")
* #item("suggests")
* #item("supplements")
* #item("enhances")
* #options_end()
* #prop("string", "dependency_modifier")
* #struct_end()
* #array_end()
*/
public Object[] listDependencies(User loggedInUser, Integer pid) throws FaultException {
// Get the logged in user
Package pkg = lookupPackage(loggedInUser, pid);
// The list we'll eventually return
List returnList = new ArrayList();
/*
* Loop through each of the types of dependencies and create a map representing the
* dependency to add to the returnList
*/
for (int i = 0; i < PackageManager.DEPENDENCY_TYPES.length; i++) {
String depType = PackageManager.DEPENDENCY_TYPES[i];
DataResult dr = getDependencies(depType, pkg);
// In the off chance we get null back, we should skip the next loop
if (dr == null || dr.isEmpty()) {
continue;
}
/*
* Loop through each item in the dependencies data result, adding each row
* to the returnList
*/
for (Iterator resultItr = dr.iterator(); resultItr.hasNext();) {
Map row = new HashMap(); // The map we'll put into returnList
Map map = (Map) resultItr.next();
String name = (String) map.get("name");
String version = (String) map.get("version");
Long sense = (Long) map.get("sense");
row.put("dependency", StringUtils.defaultString(name));
row.put("dependency_type", depType);
// If the version for this dep isn't null, we need to calculate the modifier
String depmod = " ";
if (version != null) {
depmod = StringUtils.defaultString(
PackageManager.getDependencyModifier(sense, version));
}
row.put("dependency_modifier", depmod);
returnList.add(row);
}
}
return returnList.toArray();
}
/**
* Removes a package from the system based on package id
* @param loggedInUser The current user
* @param pid package id
* @throws FaultException something bad happens
* @return 1 on success.
*
* @xmlrpc.doc Remove a package from the satellite.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "packageId")
* @xmlrpc.returntype #return_int_success()
*/
public int removePackage(User loggedInUser, Integer pid) throws FaultException {
if (!loggedInUser.hasRole(RoleFactory.ORG_ADMIN)) {
throw new PermissionCheckFailureException();
}
Package pkg = lookupPackage(loggedInUser, pid);
if (pkg == null) {
throw new NoSuchPackageException();
}
try {
PackageManager.schedulePackageRemoval(loggedInUser, pkg);
}
catch (FaultException e) {
logger.error(e.getMessage(), e);
throw e;
}
catch (RuntimeException e) {
logger.error(e.getMessage(), e);
throw e;
}
catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RuntimeException(e);
}
return 1;
}
/**
* Removes a source package based on source package id
* @param loggedInUser The current user
* @param psid package source id
* @throws FaultException something bad happens
* @return 1 on success.
*
* @xmlrpc.doc Remove a source package.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "packageSourceId")
* @xmlrpc.returntype #return_int_success()
*/
public int removeSourcePackage(User loggedInUser, Integer psid) throws FaultException {
if (!loggedInUser.hasRole(RoleFactory.ORG_ADMIN)) {
throw new PermissionCheckFailureException();
}
PackageSource pkg = PackageFactory.lookupPackageSourceByIdAndOrg(
new Long(psid.longValue()), loggedInUser.getOrg());
if (pkg == null) {
throw new NoSuchPackageException();
}
try {
PackageManager.schedulePackageSourceRemoval(loggedInUser, pkg);
}
catch (FaultException e) {
logger.error(e.getMessage(), e);
throw e;
}
catch (RuntimeException e) {
logger.error(e.getMessage(), e);
throw e;
}
catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RuntimeException(e);
}
return 1;
}
/**
* List all source packages
* @param loggedInUser The current user
* @return Returns an array of source packages
* @throws FaultException A FaultException is thrown
* when user does not have permissions.
* @xmlrpc.doc List all source packages in user's organization.
* @xmlrpc.param #session_key()
* @xmlrpc.returntype
* #array()
* #struct("source_package")
* #prop("int", "id")
* #prop("string", "name")
* #struct_end()
* #array_end()
*/
public Object[] listSourcePackages(User loggedInUser) throws FaultException {
DataResult dr =
PackageManager.listCustomPackages(loggedInUser.getOrg().getId(), true);
return dr.toArray();
}
/**
* Private helper method to get a DataResult object for a given dependency type
* @param type The type in question
* @param pkg The package in question
* @return Returns the data result containing the dependencies of a specific type for a
* given package.
*/
private DataResult getDependencies(String type, Package pkg) {
if (type.equals("requires")) {
return PackageManager.packageRequires(pkg.getId());
}
else if (type.equals("provides")) {
return PackageManager.packageProvides(pkg.getId());
}
else if (type.equals("obsoletes")) {
return PackageManager.packageObsoletes(pkg.getId());
}
else if (type.equals("conflicts")) {
return PackageManager.packageConflicts(pkg.getId());
}
else if (type.equals("recommends")) {
return PackageManager.packageRecommends(pkg.getId());
}
else if (type.equals("suggests")) {
return PackageManager.packageSuggests(pkg.getId());
}
else if (type.equals("supplements")) {
return PackageManager.packageSupplements(pkg.getId());
}
else if (type.equals("enhances")) {
return PackageManager.packageEnhances(pkg.getId());
}
return null;
}
/**
* @param user The logged in user
* @param pid The id for the package
* @return Returns the package or a fault exception
* @throws FaultException Occurs when the package is not found
*/
private Package lookupPackage(User user, Integer pid) throws FaultException {
Package pkg = PackageManager.lookupByIdAndUser(new Long(pid.longValue()), user);
/*
* PackageManager.lookupByIdAndUser() could return null, so we need to check
* and throw a no_such_package exception if the package was not found.
*/
if (pkg == null) {
throw new FaultException(-208, "no_such_package",
"The package '" + pid + "' cannot be found.");
}
return pkg;
}
/**
* Lookup the details for packages with the given name, version,
* release, architecture label, and (optionally) epoch.
* @param loggedInUser The current user
* @param name - name of the package to search for
* @param version - version of the package to search for
* @param release release of the package to search for
* @param epoch if set to something other than an empty string (""), strict
* matching will be used and the epoch string must be correct. If set
* to an empty string, if the epoch is null or there is only one NEVRA
* combination, it will be returned. (Empty string is recommended.)
* @param archLabel the arch to search for
* @return the Package object requested
*
* @xmlrpc.doc Lookup the details for packages with the given name, version,
* release, architecture label, and (optionally) epoch.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("string", "name")
* @xmlrpc.param #param("string", "version")
* @xmlrpc.param #param("string", "release")
* @xmlrpc.param #param_desc("string", "epoch",
* "If set to something other than empty string,
* strict matching will be used and the epoch string must be correct.
* If set to an empty string, if the epoch is null or there is only one
* NVRA combination, it will be returned. (Empty string is recommended.)")
* @xmlrpc.param #param("string", "archLabel")
* @xmlrpc.returntype
* #array()
* $PackageSerializer
* #array_end()
*/
public List<Package> findByNvrea(User loggedInUser, String name, String version,
String release, String epoch, String archLabel) {
PackageArch arch = PackageFactory.lookupPackageArchByLabel(archLabel);
if (arch == null) {
throw new InvalidPackageArchException(archLabel);
}
if (epoch.equals("")) {
epoch = null;
}
List<Package> pkgs = PackageFactory.lookupByNevra(loggedInUser.getOrg(),
name, version, release, epoch, arch);
return pkgs;
}
/**
* get a package's download url
* @param loggedInUser The current user
* @param pid the package id
* @return the url
*
* @xmlrpc.doc Retrieve the url that can be used to download a package.
* This will expire after a certain time period.
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "package_id")
* @xmlrpc.returntype
* string - the download url
*
*/
public String getPackageUrl(User loggedInUser, Integer pid) {
Package pkg = lookupPackage(loggedInUser, pid);
return RhnXmlRpcServer.getProtocol() + "://" +
RhnXmlRpcServer.getServerName() +
DownloadManager.getPackageDownloadPath(pkg, loggedInUser);
}
/**
* download a binary package
* @param loggedInUser The current user
* @param pid the package id
* @return a byte array of the package
* @throws IOException if there is an exception
* @xmlrpc.doc Retrieve the package file associated with a package.
* (Consider using <a href ="#getPackageUrl">packages.getPackageUrl</a>
* for larger files.)
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("int", "package_id")
* @xmlrpc.returntype binary object - package file
*/
public byte[] getPackage(User loggedInUser, Integer pid) throws IOException {
Package pkg = lookupPackage(loggedInUser, pid);
String path = Config.get().getString(ConfigDefaults.MOUNT_POINT) + "/" +
pkg.getPath();
File file = new File(path);
if (file.length() > freeMemCoeff * Runtime.getRuntime().freeMemory()) {
throw new PackageDownloadException("api.package.download.toolarge");
}
byte[] toReturn = new byte[(int) file.length()];
BufferedInputStream br = new BufferedInputStream(new FileInputStream(file));
if (br.read(toReturn) != file.length()) {
throw new PackageDownloadException("api.package.download.ioerror");
}
return toReturn;
}
}