com.redhat.rhn.frontend.xmlrpc.errata.ErrataHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.rhn.frontend.xmlrpc.errata.ErrataHandler.java

Source

/**
 * Copyright (c) 2009--2014 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.
 */
/*
 * Copyright (c) 2010 SUSE LINUX Products GmbH, Nuernberg, Germany.
 */
package com.redhat.rhn.frontend.xmlrpc.errata;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.redhat.rhn.FaultException;
import com.redhat.rhn.common.db.datasource.DataResult;
import com.redhat.rhn.common.hibernate.HibernateFactory;
import com.redhat.rhn.common.hibernate.LookupException;
import com.redhat.rhn.common.localization.LocalizationService;
import com.redhat.rhn.domain.channel.Channel;
import com.redhat.rhn.domain.channel.ChannelFactory;
import com.redhat.rhn.domain.channel.ClonedChannel;
import com.redhat.rhn.domain.channel.InvalidChannelRoleException;
import com.redhat.rhn.domain.errata.Bug;
import com.redhat.rhn.domain.errata.Cve;
import com.redhat.rhn.domain.errata.CveFactory;
import com.redhat.rhn.domain.errata.Errata;
import com.redhat.rhn.domain.errata.ErrataFactory;
import com.redhat.rhn.domain.errata.Keyword;
import com.redhat.rhn.domain.org.Org;
import com.redhat.rhn.domain.rhnpackage.Package;
import com.redhat.rhn.domain.rhnpackage.PackageFactory;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.frontend.dto.CVE;
import com.redhat.rhn.frontend.xmlrpc.BaseHandler;
import com.redhat.rhn.frontend.xmlrpc.DuplicateErrataException;
import com.redhat.rhn.frontend.xmlrpc.InvalidAdvisoryReleaseException;
import com.redhat.rhn.frontend.xmlrpc.InvalidAdvisoryTypeException;
import com.redhat.rhn.frontend.xmlrpc.InvalidChannelException;
import com.redhat.rhn.frontend.xmlrpc.InvalidChannelLabelException;
import com.redhat.rhn.frontend.xmlrpc.InvalidErrataException;
import com.redhat.rhn.frontend.xmlrpc.InvalidPackageException;
import com.redhat.rhn.frontend.xmlrpc.InvalidParameterException;
import com.redhat.rhn.frontend.xmlrpc.MissingErrataAttributeException;
import com.redhat.rhn.frontend.xmlrpc.NoChannelsSelectedException;
import com.redhat.rhn.frontend.xmlrpc.NoSuchChannelException;
import com.redhat.rhn.frontend.xmlrpc.PermissionCheckFailureException;
import com.redhat.rhn.frontend.xmlrpc.packages.PackageHelper;
import com.redhat.rhn.manager.errata.ErrataManager;
import com.redhat.rhn.manager.errata.cache.ErrataCacheManager;
import com.redhat.rhn.manager.rhnpackage.PackageManager;
import com.redhat.rhn.manager.user.UserManager;

/**
 * ErrataHandler - provides methods to access errata information.
 * @version $Rev$
 * @xmlrpc.namespace errata
 * @xmlrpc.doc Provides methods to access and modify errata.
 */
public class ErrataHandler extends BaseHandler {

    /**
     * Returns an OVAL metadata file for a given errata or CVE
     * @param loggedInUser The current user
     * @param identifier Errata identifier (either id, CVE/CAN, or Advisory name)
     * @return Escaped XML representing the OVAL metadata document
     * @throws IOException error building XML file
     * @throws FaultException general error occurred
     *
     * @xmlrpc.doc Retrieves the OVAL metadata associated with one or more erratas.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param_desc("string", "identifier", "Can either be an erratum's ID,
     *              CVE/CAN, or advisory name.  In the case of CVE/CAN, all dashes must be
     *             removed from the name.Numeric advisory IDs and advisory names
     *              (RHSA-2006:011) can be submitted as they are.")
     * @xmlrpc.returntype string - The OVAL metadata document in escaped XML form.
     */
    /**
     * The getOval method is being commented out due to bugzilla 504054.  This bug
     * raises an issue of a null exception being generated on execution.  The method
     * has been updated to address that exception; however, there is a larger issue at
     * hand in that the OVAL functionality is not fully supported by the application.
     * For example, the OVAL meta data is not synced to the database; therefore, there
     * will never be data to return by the method.  So it is better to comment it out
     * than to have the method that cannot return any data. :)  It is, however,
     * desirable to support this in the future, so we don't want to lose the logic.
     *
    public String getOval(User loggedInUser, String identifier) throws IOException,
        FaultException {
    User loggedInUser = getLoggedInUser(sessionKey);
        
    String retval = "";
    List<Errata> erratas = ErrataManager.lookupErrataByIdentifier(identifier);
    for (Errata errata : erratas) {
        if (errata.getOrg() != null &&
                !errata.getOrg().equals(loggedInUser.getOrg())) {
            erratas.remove(errata);
        }
    }
        
    if (erratas == null) {
        throw new FaultException(-1, "errataNotFound",
                "No erratas found for given identifier");
    }
    List files = new LinkedList();
    if (erratas != null) {
        for (Iterator iter = erratas.iterator(); iter.hasNext();) {
            Errata e = (Errata) iter.next();
            List tmp =
                ErrataFactory.lookupErrataFilesByErrataAndFileType(e.getId(),
                        "oval");
            if ((tmp != null && tmp.size() > 0) &&
                    (e.getOrg() == null || e.getOrg().equals(loggedInUser.getOrg()))) {
                files.addAll(tmp);
            }
        }
        files = ErrataManager.resolveOvalFiles(files);
        if (files != null) {
            if (files.size() == 0) {
                throw new FaultException(-1, "ovalNotFound",
                        "No OVAL files found for given errata");
            }
            else if (files.size() == 1) {
                File f = (File) files.get(0);
                if (f != null) {
                    InputStream in = null;
                    byte[] buf = new byte[4096];
                    int readsize = 0;
                    ByteArrayOutputStream accum  = new ByteArrayOutputStream();
                    try {
                        in = new FileInputStream(f);
                        while ((readsize = in.read(buf)) > -1) {
                            accum.write(buf, 0, readsize);
                        }
                        retval = new String(accum.toByteArray(), "UTF-8");
                    }
                    finally {
                        if (in != null) {
                            in.close();
                        }
                    }
                }
            }
            else if (files.size() > 1) {
                try {
                    OvalFileAggregator agg = new OvalFileAggregator();
                    for (Iterator iter = files.iterator(); iter.hasNext();) {
                        File f = (File) iter.next();
                        if (f != null && !f.getPath().endsWith("test-5.xml")) {
                            agg.add(f);
                        }
                    }
                    retval = StringEscapeUtils.escapeXml(agg.finish(false));
                }
                catch (JDOMException e) {
                    throw new FaultException(-1, "err_building_oval", e.getMessage());
                }
            }
        }
    }
    return retval;
    }
     */

    /**
     * GetDetails - Retrieves the details for a given errata.
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the errata
     * @return Returns a map containing the details of the errata
     * @throws FaultException A FaultException is thrown if the errata
     * corresponding to advisoryName cannot be found.
     *
     * @xmlrpc.doc Retrieves the details for the erratum matching the given
     * advisory name.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.returntype
     *      #struct("erratum")
     *          #prop("int", "id")
     *          #prop("string", "issue_date")
     *          #prop("string", "update_date")
     *          #prop_desc("string", "last_modified_date", "This date is only included for
     *          published erratum and it represents the last time the erratum was
     *          modified.")
     *          #prop("string", "synopsis")
     *          #prop("int", "release")
     *          #prop("string", "type")
     *          #prop("string", "product")
     *          #prop("string", "errataFrom")
     *          #prop("string", "topic")
     *          #prop("string", "description")
     *          #prop("string", "references")
     *          #prop("string", "notes")
     *          #prop("string", "solution")
     *     #struct_end()
     */
    public Map<String, Object> getDetails(User loggedInUser, String advisoryName) throws FaultException {
        // Get the logged in user. We don't care what roles this user has, we
        // just want to make sure the caller is logged in.

        Errata errata = lookupErrataReadOnly(advisoryName, loggedInUser.getOrg());

        Map<String, Object> errataMap = new HashMap<String, Object>();

        errataMap.put("id", errata.getId());
        if (errata.getIssueDate() != null) {
            errataMap.put("issue_date", LocalizationService.getInstance().formatShortDate(errata.getIssueDate()));
        }
        if (errata.getUpdateDate() != null) {
            errataMap.put("update_date", LocalizationService.getInstance().formatShortDate(errata.getUpdateDate()));
        }
        if (errata.getLastModified() != null) {
            errataMap.put("last_modified_date", errata.getLastModified().toString());
        }
        if (errata.getAdvisoryRel() != null) {
            errataMap.put("release", errata.getAdvisoryRel());
        }
        errataMap.put("product", StringUtils.defaultString(errata.getProduct()));
        errataMap.put("errataFrom", StringUtils.defaultString(errata.getErrataFrom()));
        errataMap.put("solution", StringUtils.defaultString(errata.getSolution()));
        errataMap.put("description", StringUtils.defaultString(errata.getDescription()));
        errataMap.put("synopsis", StringUtils.defaultString(errata.getSynopsis()));
        errataMap.put("topic", StringUtils.defaultString(errata.getTopic()));
        errataMap.put("references", StringUtils.defaultString(errata.getRefersTo()));
        errataMap.put("notes", StringUtils.defaultString(errata.getNotes()));
        errataMap.put("type", StringUtils.defaultString(errata.getAdvisoryType()));

        return errataMap;
    }

    /**
     * Set erratum details.
     *
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the errata
     * @param details Map of (optional) erratum details to be set.
     * @return 1 on success, exception thrown otherwise.
     *
     * @xmlrpc.doc Set erratum details. All arguments are optional and will only be modified
     * if included in the struct. This method will only allow for modification of custom
     * errata created either through the UI or API.
     * @xmlrpc.param #param("string", "sessionKey")
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.param
     *      #struct("errata details")
     *          #prop("string", "synopsis")
     *          #prop("string", "advisory_name")
     *          #prop("int", "advisory_release")
     *          #prop_desc("string", "advisory_type", "Type of advisory (one of the
     *                  following: 'Security Advisory', 'Product Enhancement Advisory',
     *                  or 'Bug Fix Advisory'")
     *          #prop("string", "product")
     *          #prop("dateTime.iso8601", "issue_date")
     *          #prop("dateTime.iso8601", "update_date")
     *          #prop("string", "errataFrom")
     *          #prop("string", "topic")
     *          #prop("string", "description")
     *          #prop("string", "references")
     *          #prop("string", "notes")
     *          #prop("string", "solution")
     *          #prop_desc("array", "bugs", "'bugs' is the key into the struct")
     *              #array()
     *                 #struct("bug")
     *                    #prop_desc("int", "id", "Bug Id")
     *                    #prop("string", "summary")
     *                    #prop("string", "url")
     *                 #struct_end()
     *              #array_end()
     *          #prop_desc("array", "keywords", "'keywords' is the key into the struct")
     *              #array_single("string", "keyword - List of keywords to associate
     *                  with the errata.")
     *          #prop_desc("array", "CVEs", "'cves' is the key into the struct")
     *              #array_single("string", "cves - List of CVEs to associate
     *                  with the errata. (valid only for published errata)")
     *     #struct_end()
     *
     *  @xmlrpc.returntype #return_int_success()
     */
    public Integer setDetails(User loggedInUser, String advisoryName, Map<String, Object> details) {

        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        if (errata.getOrg() == null) {
            // Errata in the null org should not be modified; therefore, this is
            // considered an invalid errata for this request
            throw new InvalidErrataException(errata.getAdvisoryName());
        }

        // confirm that the user only provided valid keys in the map
        Set<String> validKeys = new HashSet<String>();
        validKeys.add("synopsis");
        validKeys.add("advisory_name");
        validKeys.add("advisory_release");
        validKeys.add("advisory_type");
        validKeys.add("product");
        validKeys.add("issue_date");
        validKeys.add("update_date");
        validKeys.add("errataFrom");
        validKeys.add("topic");
        validKeys.add("description");
        validKeys.add("references");
        validKeys.add("notes");
        validKeys.add("solution");
        validKeys.add("bugs");
        validKeys.add("keywords");
        if (errata.isPublished()) {
            validKeys.add("cves");
        }
        validateMap(validKeys, details);

        validKeys.clear();
        validKeys.add("id");
        validKeys.add("summary");
        validKeys.add("url");
        if (details.containsKey("bugs")) {
            for (Map<String, Object> bugMap : (ArrayList<Map<String, Object>>) details.get("bugs")) {

                validateMap(validKeys, bugMap);
            }
        }

        if (details.containsKey("issue_date")) {
            try {
                errata.setIssueDate((Date) details.get("issue_date"));
            } catch (ClassCastException e) {
                throw new InvalidParameterException("Wrong 'issue_date' format.");
            }
        }

        if (details.containsKey("update_date")) {
            try {
                errata.setUpdateDate((Date) details.get("update_date"));
            } catch (ClassCastException e) {
                throw new InvalidParameterException("Wrong 'update_date' format.");
            }
        }

        if (details.containsKey("synopsis")) {
            if (StringUtils.isBlank((String) details.get("synopsis"))) {
                throw new InvalidParameterException("Synopsis is required.");
            }
            errata.setSynopsis((String) details.get("synopsis"));
        }
        if (details.containsKey("advisory_name")) {
            if (StringUtils.isBlank((String) details.get("advisory_name"))) {
                throw new InvalidParameterException("Advisory name is required.");
            }
            errata.setAdvisoryName((String) details.get("advisory_name"));
        }
        if (details.containsKey("advisory_release")) {
            Long rel = new Long((Integer) details.get("advisory_release"));
            if (rel.longValue() > ErrataManager.MAX_ADVISORY_RELEASE) {
                throw new InvalidAdvisoryReleaseException(rel.longValue());
            }
            errata.setAdvisoryRel(rel);
        }
        if (details.containsKey("advisory_type")) {
            String pea = "Product Enhancement Advisory"; // hack for checkstyle
            if (!((String) details.get("advisory_type")).equals("Security Advisory")
                    && !((String) details.get("advisory_type")).equals(pea)
                    && !((String) details.get("advisory_type")).equals("Bug Fix Advisory")) {
                throw new InvalidParameterException("Invalid advisory type");
            }
            errata.setAdvisoryType((String) details.get("advisory_type"));
        }
        if (details.containsKey("product")) {
            if (StringUtils.isBlank((String) details.get("product"))) {
                throw new InvalidParameterException("Product name is required.");
            }
            errata.setProduct((String) details.get("product"));
        }
        if (details.containsKey("errataFrom")) {
            errata.setErrataFrom((String) details.get("errataFrom"));
        }
        if (details.containsKey("topic")) {
            if (StringUtils.isBlank((String) details.get("topic"))) {
                throw new InvalidParameterException("Topic is required.");
            }
            errata.setTopic((String) details.get("topic"));
        }
        if (details.containsKey("description")) {
            if (StringUtils.isBlank((String) details.get("description"))) {
                throw new InvalidParameterException("Description is required.");
            }
            errata.setDescription((String) details.get("description"));
        }
        if (details.containsKey("solution")) {
            if (StringUtils.isBlank((String) details.get("solution"))) {
                throw new InvalidParameterException("Solution is required.");
            }
            errata.setSolution((String) details.get("solution"));
        }
        if (details.containsKey("references")) {
            errata.setRefersTo((String) details.get("references"));
        }
        if (details.containsKey("notes")) {
            errata.setNotes((String) details.get("notes"));
        }
        if (details.containsKey("bugs")) {

            if (errata.getBugs() != null) {
                errata.getBugs().clear();
                HibernateFactory.getSession().flush();
            }

            for (Map<String, Object> bugMap : (ArrayList<Map<String, Object>>) details.get("bugs")) {

                if (bugMap.containsKey("id") && bugMap.containsKey("summary")) {
                    String url = null;
                    if (bugMap.containsKey("url")) {
                        url = (String) bugMap.get("url");
                    }

                    Bug bug = ErrataFactory.createPublishedBug(new Long((Integer) bugMap.get("id")),
                            (String) bugMap.get("summary"), url);

                    errata.addBug(bug);
                }
            }
        }
        if (details.containsKey("keywords")) {
            if (errata.getKeywords() != null) {
                errata.getKeywords().clear();
                HibernateFactory.getSession().flush();
            }
            for (String keyword : (ArrayList<String>) details.get("keywords")) {
                errata.addKeyword(keyword);
            }
        }
        if (details.containsKey("cves")) {
            if (errata.getCves() != null) {
                errata.getCves().clear();
                HibernateFactory.getSession().flush();
            }
            for (String cveName : (ArrayList<String>) details.get("cves")) {
                Cve c = CveFactory.lookupByName(cveName);
                if (c == null) {
                    c = new Cve();
                    c.setName(cveName);
                    CveFactory.save(c);
                }
                errata.getCves().add(c);
            }
        }

        // ALWAYS change the advisory to match, as we do in the UI.
        errata.setAdvisory(errata.getAdvisoryName() + "-" + errata.getAdvisoryRel().toString());

        //Save the errata
        ErrataManager.storeErrata(errata);

        return 1;
    }

    /**
     * ListAffectedSystems
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the errata
     * @return Returns an object array containing the system ids and system name
     * @throws FaultException A FaultException is thrown if the errata corresponding to
     * advisoryName cannot be found.
     *
     * @xmlrpc.doc Return the list of systems affected by the erratum with
     * advisory name.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.returntype
     *      #array()
     *          $SystemOverviewSerializer
     *      #array_end()
     */
    public Object[] listAffectedSystems(User loggedInUser, String advisoryName) throws FaultException {

        // Get the logged in user
        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        DataResult dr = ErrataManager.systemsAffectedXmlRpc(loggedInUser, errata.getId());

        return dr.toArray();
    }

    /**
     * Get the Bugzilla fixes for a given errata
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the errata
     * @return Returns a map containing the Bugzilla id and summary for each bug
     * @throws FaultException A FaultException is thrown if the errata
     * corresponding to the given advisoryName cannot be found.
     *
     * @xmlrpc.doc Get the Bugzilla fixes for an erratum matching the given
     * advisoryName. The bugs will be returned in a struct where the bug id is
     * the key.  i.e. 208144="errata.bugzillaFixes Method Returns different
     * results than docs say"
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.returntype
     *      #struct("Bugzilla info")
     *          #prop_desc("string", "bugzilla_id", "actual bug number is the key into the
     *                      struct")
     *          #prop_desc("string", "bug_summary", "summary who's key is the bug id")
     *      #struct_end()
     */
    public Map<Long, String> bugzillaFixes(User loggedInUser, String advisoryName) throws FaultException {

        // Get the logged in user
        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        Set<Bug> bugs = errata.getBugs();
        Map<Long, String> returnMap = new HashMap<Long, String>();

        /*
         * Loop through and stick the bug ids and summaries into a map. This
         * is ok since (afaict) there isn't an unreasonable number of bugs
         * attatched to any erratum.
         */
        for (Iterator<Bug> itr = bugs.iterator(); itr.hasNext();) {
            Bug bug = itr.next();
            returnMap.put(bug.getId(), bug.getSummary());
        }

        return returnMap;
    }

    /**
     * Get the keywords for a given erratum
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the erratum
     * @return Returns an array of keywords for the erratum
     * @throws FaultException A FaultException is thrown if the errata corresponding to the
     * given advisoryName cannot be fo
     *
     * @xmlrpc.doc Get the keywords associated with an erratum matching the
     * given advisory name.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.returntype #array_single("string", "Keyword associated with erratum.")
        
     */
    public Object[] listKeywords(User loggedInUser, String advisoryName) throws FaultException {

        // Get the logged in user
        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        Set<Keyword> keywords = errata.getKeywords();
        List<String> returnList = new ArrayList<String>();

        for (Iterator<Keyword> itr = keywords.iterator(); itr.hasNext();) {
            Keyword keyword = itr.next();
            returnList.add(keyword.getKeyword());
        }

        return returnList.toArray();
    }

    /**
     * Returns a list of channels (represented by a map) that the given erratum is
     * applicable to.
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the erratum
     * @return Returns an array of channels for the erratum
     * @throws FaultException A FaultException is thrown if the errata corresponding to the
     * given advisoryName cannot be found
     *
     * @xmlrpc.doc Returns a list of channels applicable to the erratum
     * with the given advisory name.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.returntype
     *      #array()
     *          #struct("channel")
     *              #prop("int", "channel_id")
     *              #prop("string", "label")
     *              #prop("string", "name")
     *              #prop("string", "parent_channel_label")
     *          #struct_end()
     *       #array_end()
     */
    public Object[] applicableToChannels(User loggedInUser, String advisoryName) throws FaultException {

        // Get the logged in user
        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        return ErrataManager.applicableChannels(errata.getId(), loggedInUser.getOrg().getId(), null, Map.class)
                .toArray();
    }

    /**
     * Returns a list of unpublished errata for the logged-in user's Org.
     * @param loggedInUser The current user
     * @return Returns an array of errata
     *
     * @xmlrpc.doc Returns a list of unpublished errata
     * @xmlrpc.param #session_key()
     * @xmlrpc.returntype
     *      #array()
     *          #struct("erratum")
     *              #prop("int", "id")
     *              #prop("int", "published")
     *              #prop("string", "advisory")
     *              #prop("string", "advisory_name")
     *              #prop("string", "advisory_type")
     *              #prop("string", "synopsis")
     *              #prop("dateTime.iso8601", "created")
     *              #prop("dateTime.iso8601", "update_date")
     *          #struct_end()
     *      #array_end()
     */
    public Object[] listUnpublishedErrata(User loggedInUser) {
        Map[] unpub = (Map[]) ErrataManager.unpublishedOwnedErrata(loggedInUser, Map.class).toArray(new Map[0]);

        for (Map errataItem : unpub) {
            // remove items that can be NULL to prevent xmlrpc failure
            Iterator<Map.Entry> itr = errataItem.entrySet().iterator();
            for (; itr.hasNext();) {
                if (itr.next().getValue() == null) {
                    itr.remove();
                }
            }
        }

        return unpub;
    }

    /**
     * Returns a list of CVEs for a given erratum
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the erratum
     * @return Returns a list of CVEs
     * @throws FaultException A FaultException is thrown if the errata corresponding to the
     * given advisoryName cannot be found
    throws FaultException {
     *
     * @xmlrpc.doc Returns a list of
     * <a href="http://www.cve.mitre.org/" target="_blank">CVE</a>s
     * applicable to the erratum with the given advisory name. CVEs may be associated
     * only with published errata.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string",  "advisoryName")
     * @xmlrpc.returntype
     *      #array_single("string", "cveName")
     *
     */
    public List listCves(User loggedInUser, String advisoryName) throws FaultException {
        // Get the logged in user
        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        DataResult dr = ErrataManager.errataCVEs(errata.getId());
        List returnList = new ArrayList();

        //Just return the name of the cve...
        for (Iterator itr = dr.iterator(); itr.hasNext();) {
            CVE cve = (CVE) itr.next();
            returnList.add(cve.getName());
        }

        return returnList;
    }

    /**
     * List the packages for a given erratum
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the erratum
     * @return Returns an Array of maps representing a package
     * @throws FaultException A FaultException is thrown if the errata corresponding to the
     * given advisoryName cannot be found
     *
     * @xmlrpc.doc Returns a list of the packages affected by the erratum
     * with the given advisory name.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.returntype
     *          #array()
     *              #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", "path")
     *                  #prop("string", "file")
     *                  #prop("string", "build_date")
     *                  #prop("string", "last_modified_date")
     *                  #prop("string", "size")
     *                  #prop("string", "payload_size")
     *               #struct_end()
     *           #array_end()
     */
    public List<Map> listPackages(User loggedInUser, String advisoryName) throws FaultException {
        // Get the logged in user
        Errata errata = lookupErrataReadOnly(advisoryName, loggedInUser.getOrg());

        List<Map> toRet = new ArrayList<Map>();
        for (Iterator iter = errata.getPackages().iterator(); iter.hasNext();) {
            Package pkg = (Package) iter.next();
            toRet.add(PackageHelper.packageToMap(pkg, loggedInUser));
        }
        return toRet;
    }

    /**
     * Add a set of packages to an erratum
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the erratum
     * @param packageIds The ids for packages to remove
     * @return Returns int - representing the number of packages added, exception otherwise
     * @throws FaultException A FaultException is thrown if the errata corresponding to the
     * given advisoryName cannot be found
     *
     * @xmlrpc.doc Add a set of packages to an erratum
     * with the given advisory name. This method will only allow for modification
     * of custom errata created either through the UI or API.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.param #array_single("int", "packageId")
     * @xmlrpc.returntype int - representing the number of packages added,
     * exception otherwise
     */
    public int addPackages(User loggedInUser, String advisoryName, List<Integer> packageIds) throws FaultException {

        // Get the logged in user
        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        if (errata.getOrg() == null) {
            // Errata in the null org should not be modified; therefore, this is
            // considered an invalid errata for this request
            throw new InvalidErrataException(errata.getAdvisoryName());
        }

        int packagesAdded = 0;
        for (Integer packageId : packageIds) {

            Package pkg = PackageManager.lookupByIdAndUser(new Long(packageId), loggedInUser);

            if ((pkg != null) && (!errata.getPackages().contains(pkg))) {
                errata.addPackage(pkg);
                packagesAdded++;
            }
        }

        //Update Errata Cache
        if ((packagesAdded > 0) && errata.isPublished() && (errata.getChannels() != null)) {
            ErrataCacheManager.updateCacheForChannelsAsync(errata.getChannels());
        }

        //Save the errata
        ErrataManager.storeErrata(errata);

        return packagesAdded;
    }

    /**
     * Remove a set of packages from an erratum
     * @param loggedInUser The current user
     * @param advisoryName The advisory name of the erratum
     * @param packageIds The ids for packages to remove
     * @return Returns int - representing the number of packages removed,
     * exception otherwise
     * @throws FaultException A FaultException is thrown if the errata corresponding to the
     * given advisoryName cannot be found
     *
     * @xmlrpc.doc Remove a set of packages from an erratum
     * with the given advisory name.  This method will only allow for modification
     * of custom errata created either through the UI or API.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.param #array_single("int", "packageId")
     * @xmlrpc.returntype int - representing the number of packages removed,
     * exception otherwise
     */
    public int removePackages(User loggedInUser, String advisoryName, List<Integer> packageIds)
            throws FaultException {

        // Get the logged in user
        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        if (errata.getOrg() == null) {
            // Errata in the null org should not be modified; therefore, this is
            // considered an invalid errata for this request
            throw new InvalidErrataException(errata.getAdvisoryName());
        }

        int packagesRemoved = 0;
        for (Integer packageId : packageIds) {

            Package pkg = PackageManager.lookupByIdAndUser(new Long(packageId), loggedInUser);

            if ((pkg != null) && (errata.getPackages().contains(pkg))) {
                errata.removePackage(pkg);
                packagesRemoved++;
            }
        }

        //Update Errata Cache
        if ((packagesRemoved > 0) && errata.isPublished() && (errata.getChannels() != null)) {
            ErrataCacheManager.updateCacheForChannelsAsync(errata.getChannels());
        }

        //Save the errata
        ErrataManager.storeErrata(errata);

        return packagesRemoved;
    }

    /**
     * Private helper method to lookup an errata and throw a Fault exception if it isn't
     * found
     * @param advisoryName The advisory name for the erratum you're looking for
     * @return Returns the errata or a Fault Exception
     * @throws FaultException Occurs when the erratum is not found
     */
    private Errata lookupErrata(String advisoryName, Org org) throws FaultException {
        Errata errata = ErrataManager.lookupByAdvisory(advisoryName);

        /*
         * ErrataManager.lookupByAdvisory() could return null, so we need to check
         * and throw a no_such_errata exception if the errata was not found.
         */
        if (errata == null) {
            throw new FaultException(-208, "no_such_errata", "The errata " + advisoryName + " cannot be found.");
        }
        /**
         * errata with org_id of null are public, but ones with an org id of !null are not
         * need to make sure here that everything is checked correclty
         */
        if (errata.getOrg() != null && !errata.getOrg().equals(org)) {
            throw new FaultException(-209, "no_rights_to_access",
                    "You don't have rights to access " + advisoryName + " errata.");
        }

        return errata;
    }

    /**
     * Private helper method to lookup an errata and throw a Fault exception if it isn't
     * found
     * @param advisoryName The advisory name for the erratum you're looking for
     * @return Returns the errata or a Fault Exception
     * @throws FaultException Occurs when the erratum is not found
     */
    private Errata lookupErrataReadOnly(String advisoryName, Org org) throws FaultException {
        Errata errata = ErrataManager.lookupByAdvisory(advisoryName);

        /*
         * ErrataManager.lookupByAdvisory() could return null, so we need to check
         * and throw a no_such_errata exception if the errata was not found.
         */
        if (errata == null) {
            throw new FaultException(-208, "no_such_errata", "The errata " + advisoryName + " cannot be found.");
        }
        /**
         * errata with org_id of null are public, but ones with an org id of !null are not
         * need to make sure here that everything is checked correclty
         */

        if (errata.getOrg() == null || errata.getOrg().equals(org)) {
            return errata;
        }
        Set<Channel> errataChannels = errata.getChannels();
        List<Channel> orgChannels = org.getAccessibleChannels();
        for (Channel channel : errataChannels) {
            if (orgChannels.contains(channel)) {
                return errata;
            }
        }
        throw new FaultException(-209, "no_rights_to_access",
                "You don't have rights to access " + advisoryName + " errata.");
    }

    /**
     * Clones a list of errata into a specified channel
     *
     * @param loggedInUser The current user
     * @param channelLabel the channel's label that we are cloning into
     * @param advisoryNames an array of String objects containing the advisory name
     *          of every errata you want to clone
     * @throws InvalidChannelRoleException if the user perms are incorrect
     * @return Returns an array of Errata objects, which get serialized into XMLRPC
     *
     * @xmlrpc.doc Clone a list of errata into the specified channel.
     *
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "channel_label")
     * @xmlrpc.param
     *     #array_single("string", " advisory - The advisory name of the errata to clone.")
     * @xmlrpc.returntype
     *          #array()
     *              $ErrataSerializer
     *          #array_end()
     */
    public Object[] clone(User loggedInUser, String channelLabel, List advisoryNames)
            throws InvalidChannelRoleException {
        return clone(loggedInUser, channelLabel, advisoryNames, false, false);
    }

    /**
     * Asynchronously clones a list of errata into a specified channel
     *
     * @param loggedInUser The current user
     * @param channelLabel the channel's label that we are cloning into
     * @param advisoryNames an array of String objects containing the advisory name
     *          of every errata you want to clone
     * @throws InvalidChannelRoleException if the user perms are incorrect
     * @return 1 on success, exception thrown otherwise.
     *
     * @xmlrpc.doc Asynchronously clone a list of errata into the specified channel.
     *
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "channel_label")
     * @xmlrpc.param
     *     #array_single("string", " advisory - The advisory name of the errata to clone.")
     * @xmlrpc.returntype
     *          #return_int_success()
     */
    public int cloneAsync(User loggedInUser, String channelLabel, List advisoryNames)
            throws InvalidChannelRoleException {
        clone(loggedInUser, channelLabel, advisoryNames, false, false);
        return 1;
    }

    private Object[] clone(User loggedInUser, String channelLabel, List<String> advisoryNames,
            boolean inheritPackages, boolean asynchronous) {

        Logger log = Logger.getLogger(ErrataFactory.class);

        Channel channel = ChannelFactory.lookupByLabelAndUser(channelLabel, loggedInUser);

        if (channel == null) {
            throw new NoSuchChannelException();
        }

        //if calling cloneAsOriginal, do additional checks to verify a clone
        if (inheritPackages) {
            if (!channel.isCloned()) {
                throw new InvalidChannelException("Cloned channel expected: " + channel.getLabel());
            }

            Channel original = ChannelFactory.lookupOriginalChannel(channel);

            if (original == null) {
                throw new InvalidChannelException(
                        "Cannot access original " + "of the channel: " + channel.getLabel());
            }
            // check access to the original
            if (ChannelFactory.lookupByIdAndUser(original.getId(), loggedInUser) == null) {
                throw new LookupException("User " + loggedInUser.getLogin() + " does not have access to channel "
                        + original.getLabel());
            }
        }

        if (!UserManager.verifyChannelAdmin(loggedInUser, channel)) {
            throw new PermissionCheckFailureException();
        }

        List<Errata> errataToClone = new ArrayList<Errata>();
        List<Long> errataIds = new ArrayList<Long>();
        //We loop through once, making sure all the errata exist
        for (String advisory : advisoryNames) {
            Errata toClone = lookupErrata(advisory, loggedInUser.getOrg());
            errataToClone.add(toClone);
            errataIds.add(toClone.getId());
        }

        if (asynchronous) {
            ErrataManager.cloneErrataApiAsync(channel, errataIds, loggedInUser, inheritPackages);
            return new ArrayList<Errata>().toArray();
        } else if (ErrataManager.channelHasPendingAsyncCloneJobs(channel)) {
            throw new InvalidChannelException(
                    "Channel " + channel.getLabel() + " has pending asynchronous errata clone jobs. You must wait"
                            + "until asychronous errata clone jobs are done.");
        } else {
            return ErrataManager.cloneErrataApi(channel, errataToClone, loggedInUser, inheritPackages);
        }
    }

    /**
     * Clones a list of errata into a specified cloned channel
     * according the original erratas
     *
     * @param loggedInUser The current user
     * @param channelLabel the cloned channel's label that we are cloning into
     * @param advisoryNames an array of String objects containing the advisory name
     *          of every errata you want to clone
     * @throws InvalidChannelRoleException if the user perms are incorrect
     * @return Returns an array of Errata objects, which get serialized into XMLRPC
     *
     * @xmlrpc.doc Clones a list of errata into a specified cloned channel
     * according the original erratas.
     *
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "channel_label")
     * @xmlrpc.param
     *     #array_single("string", " advisory - The advisory name of the errata to clone.")
     * @xmlrpc.returntype
     *          #array()
     *              $ErrataSerializer
     *          #array_end()
     */
    public Object[] cloneAsOriginal(User loggedInUser, String channelLabel, List<String> advisoryNames)
            throws InvalidChannelRoleException {
        return clone(loggedInUser, channelLabel, advisoryNames, true, false);
    }

    /**
     * Asynchronously clones a list of errata into a specified cloned channel
     * according the original erratas
     *
     * @param loggedInUser The current user
     * @param channelLabel the cloned channel's label that we are cloning into
     * @param advisoryNames an array of String objects containing the advisory name
     *          of every errata you want to clone
     * @throws InvalidChannelRoleException if the user perms are incorrect
     * @return 1 on success, exception thrown otherwise.
     *
     * @xmlrpc.doc Asynchronously clones a list of errata into a specified cloned channel
     * according the original erratas
     *
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "channel_label")
     * @xmlrpc.param
     *     #array_single("string", " advisory - The advisory name of the errata to clone.")
     * @xmlrpc.returntype
     *          #return_int_success()
     */
    public int cloneAsOriginalAsync(User loggedInUser, String channelLabel, List<String> advisoryNames)
            throws InvalidChannelRoleException {
        clone(loggedInUser, channelLabel, advisoryNames, true, true);
        return 1;
    }

    private Object getRequiredAttribute(Map map, String attribute) {
        Object value = map.get(attribute);
        if (value == null || StringUtils.isEmpty(value.toString())) {
            throw new MissingErrataAttributeException(attribute);
        }
        return value;
    }

    /**
     * creates an errata
     * @param loggedInUser The current user
     * @param errataInfo map containing the following values:
     *  String "synopsis" short synopsis of the errata
     *  String "advisory_name" advisory name of the errata
     *  Integer "advisory_release" release number of the errata
     *  String "advisory_type" the type of advisory for the errata (Must be one of the
     *          following: "Security Advisory", "Product Enhancement Advisory", or
     *          "Bug Fix Advisory"
     *  String "product" the product the errata affects
     *  String "errataFrom" the author of the errata
     *  String "topic" the topic of the errata
     *  String "description" the description of the errata
     *  String "solution" the solution of the errata
     *  String "references" references of the errata to be created
     *  String "notes" notes on the errata
     * @param bugs a List of maps consisting of 'id' Integers and 'summary' strings
     * @param keywords a List of keywords for the errata
     * @param packageIds a List of package Id packageId Integers
     * @param publish should the errata be published
     * @param channelLabels an array of channel labels to publish to if the errata is to
     *          be published
     * @throws InvalidChannelRoleException if the user perms are incorrect
     * @return The errata created (whether published or unpublished)
     *
     * @xmlrpc.doc Create a custom errata.  If "publish" is set to true,
     *      the errata will be published as well
     * @xmlrpc.param #session_key()
     * @xmlrpc.param
     *      #struct("errata info")
     *          #prop("string", "synopsis")
     *          #prop("string", "advisory_name")
     *          #prop("int", "advisory_release")
     *          #prop_desc("string", "advisory_type", "Type of advisory (one of the
     *                  following: 'Security Advisory', 'Product Enhancement Advisory',
     *                  or 'Bug Fix Advisory'")
     *          #prop("string", "product")
     *          #prop("string", "errataFrom")
     *          #prop("string", "topic")
     *          #prop("string", "description")
     *          #prop("string", "references")
     *          #prop("string", "notes")
     *          #prop("string", "solution")
     *       #struct_end()
     *  @xmlrpc.param
     *       #array()
     *              #struct("bug")
     *                  #prop_desc("int", "id", "Bug Id")
     *                  #prop("string", "summary")
     *                  #prop("string", "url")
     *               #struct_end()
     *       #array_end()
     * @xmlrpc.param #array_single("string", "keyword - List of keywords to associate
     *              with the errata.")
     * @xmlrpc.param #array_single("int", "packageId")
     * @xmlrpc.param #param_desc("boolean", "publish", "Should the errata be published.")
     * @xmlrpc.param
     *       #array_single("string", "channelLabel - list of channels the errata should be
     *                  published too, ignored if publish is set to false")
     * @xmlrpc.returntype
     *      $ErrataSerializer
     */
    public Errata create(User loggedInUser, Map<String, String> errataInfo, List<Map<String, Object>> bugs,
            List<String> keywords, List<Integer> packageIds, boolean publish, List<String> channelLabels)
            throws InvalidChannelRoleException {

        // confirm that the user only provided valid keys in the map
        Set<String> validKeys = new HashSet<String>();
        validKeys.add("synopsis");
        validKeys.add("advisory_name");
        validKeys.add("advisory_release");
        validKeys.add("advisory_type");
        validKeys.add("product");
        validKeys.add("errataFrom");
        validKeys.add("topic");
        validKeys.add("description");
        validKeys.add("references");
        validKeys.add("notes");
        validKeys.add("solution");
        validateMap(validKeys, errataInfo);

        validKeys.clear();
        validKeys.add("id");
        validKeys.add("summary");
        validKeys.add("url");
        for (Map<String, Object> bugMap : bugs) {
            validateMap(validKeys, bugMap);
        }

        //Don't want them to publish an errata without any channels,
        //so check first before creating anything
        List<Channel> channels = null;
        if (publish) {
            channels = verifyChannelList(channelLabels, loggedInUser);
        }

        String synopsis = (String) getRequiredAttribute(errataInfo, "synopsis");
        String advisoryName = (String) getRequiredAttribute(errataInfo, "advisory_name");
        Integer advisoryRelease = (Integer) getRequiredAttribute(errataInfo, "advisory_release");
        if (advisoryRelease.longValue() > ErrataManager.MAX_ADVISORY_RELEASE) {
            throw new InvalidAdvisoryReleaseException(advisoryRelease.longValue());
        }
        String advisoryType = (String) getRequiredAttribute(errataInfo, "advisory_type");
        String product = (String) getRequiredAttribute(errataInfo, "product");
        String errataFrom = errataInfo.get("errataFrom");
        String topic = (String) getRequiredAttribute(errataInfo, "topic");
        String description = (String) getRequiredAttribute(errataInfo, "description");
        String solution = (String) getRequiredAttribute(errataInfo, "solution");
        String references = errataInfo.get("references");
        String notes = errataInfo.get("notes");

        Errata newErrata = ErrataManager.lookupByAdvisory(advisoryName);
        if (newErrata != null) {
            throw new DuplicateErrataException(advisoryName);
        }
        newErrata = ErrataManager.createNewErrata();
        newErrata.setOrg(loggedInUser.getOrg());

        //all required
        newErrata.setSynopsis(synopsis);
        newErrata.setAdvisory(advisoryName + "-" + advisoryRelease.toString());
        newErrata.setAdvisoryName(advisoryName);
        newErrata.setAdvisoryRel(new Long(advisoryRelease.longValue()));

        if (advisoryType.equals("Security Advisory") || advisoryType.equals("Product Enhancement Advisory")
                || advisoryType.equals("Bug Fix Advisory")) {

            newErrata.setAdvisoryType(advisoryType);
        } else {
            throw new InvalidAdvisoryTypeException(advisoryType);
        }

        newErrata.setProduct(product);
        newErrata.setTopic(topic);
        newErrata.setDescription(description);
        newErrata.setSolution(solution);
        newErrata.setIssueDate(new Date());
        newErrata.setUpdateDate(new Date());

        //not required
        newErrata.setErrataFrom(errataFrom);
        newErrata.setRefersTo(references);
        newErrata.setNotes(notes);

        for (Iterator<Map<String, Object>> itr = bugs.iterator(); itr.hasNext();) {
            Map<String, Object> bugMap = itr.next();
            String url = null;
            if (bugMap.containsKey("url")) {
                url = (String) bugMap.get("url");
            }

            Bug bug = ErrataFactory.createPublishedBug(new Long(((Integer) bugMap.get("id")).longValue()),
                    (String) bugMap.get("summary"), url);
            newErrata.addBug(bug);
        }
        for (Iterator<String> itr = keywords.iterator(); itr.hasNext();) {
            String keyword = itr.next();
            newErrata.addKeyword(keyword);
        }

        newErrata.setPackages(new HashSet());
        for (Iterator<Integer> itr = packageIds.iterator(); itr.hasNext();) {
            Integer pid = itr.next();
            Package pack = PackageFactory.lookupByIdAndOrg(new Long(pid.longValue()), loggedInUser.getOrg());
            if (pack != null) {
                newErrata.addPackage(pack);
            } else {
                throw new InvalidPackageException(pid.toString());
            }
        }

        ErrataFactory.save(newErrata);

        //if true, channels will not be null, but will be a List of channel objects
        if (publish) {
            return publish(newErrata, channels, loggedInUser, false);
        }
        return newErrata;
    }

    /**
     * Delete an erratum.
     * @param loggedInUser The current user
     * @param advisoryName The advisory Name of the erratum to delete
     * @throws FaultException if unknown or invalid erratum is provided.
     * @return 1 on success, exception thrown otherwise.
     *
     * @xmlrpc.doc Delete an erratum.  This method will only allow for deletion
     * of custom errata created either through the UI or API.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.returntype #return_int_success()
     */
    public Integer delete(User loggedInUser, String advisoryName) throws FaultException {
        Errata errata = lookupErrata(advisoryName, loggedInUser.getOrg());

        if (errata.getOrg() == null) {
            // Errata in the null org should not be modified; therefore, this is
            // considered an invalid errata for this request
            throw new InvalidErrataException(errata.getAdvisoryName());
        }

        ErrataManager.deleteErratum(loggedInUser, errata);
        return 1;
    }

    /**
     * Publishes an existing (unpublished) errata to a set of channels
     * @param loggedInUser The current user
     * @param advisory The advisory Name of the errata to publish
     * @param channelLabels List of channels to publish the errata to
     * @throws InvalidChannelRoleException if the user perms are incorrect
     * @return the published errata
     *
     * @xmlrpc.doc Publish an existing (unpublished) errata to a set of channels.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.param
     *      #array_single("string", "channelLabel - list of channel labels to publish to")
     * @xmlrpc.returntype
     *          $ErrataSerializer
     */
    public Errata publish(User loggedInUser, String advisory, List<String> channelLabels)
            throws InvalidChannelRoleException {
        List<Channel> channels = verifyChannelList(channelLabels, loggedInUser);
        Errata toPublish = lookupErrata(advisory, loggedInUser.getOrg());
        return publish(toPublish, channels, loggedInUser, false);
    }

    /**
     * Publishes an existing (unpublished) cloned errata to a set of cloned channels
     * according to its original erratum
     * @param loggedInUser The current user
     * @param advisory The advisory Name of the errata to publish
     * @param channelLabels List of channels to publish the errata to
     * @throws InvalidChannelRoleException if the user perms are incorrect
     * @return the published errata
     *
     * @xmlrpc.doc Publishes an existing (unpublished) cloned errata to a set of cloned
     * channels according to its original erratum
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "advisoryName")
     * @xmlrpc.param
     *      #array_single("string", "channelLabel - list of channel labels to publish to")
     * @xmlrpc.returntype
     *          $ErrataSerializer
     */
    public Errata publishAsOriginal(User loggedInUser, String advisory, List<String> channelLabels)
            throws InvalidChannelRoleException {
        List<Channel> channels = verifyChannelList(channelLabels, loggedInUser);
        for (Channel c : channels) {
            ClonedChannel cc = null;
            try {
                cc = (ClonedChannel) c;
            } catch (ClassCastException e) {
                // just catch, do not do anything
            } finally {
                if (cc == null || !cc.isCloned()) {
                    throw new InvalidChannelException("Cloned channel " + "expected: " + c.getLabel());
                }
            }
            Channel original = ChannelFactory.lookupOriginalChannel(c);
            if (original == null) {
                throw new InvalidChannelException("Cannot access original " + "of the channel: " + c.getLabel());
            }
            // check access to the original
            if (ChannelFactory.lookupByIdAndUser(original.getId(), loggedInUser) == null) {
                throw new LookupException("User " + loggedInUser.getLogin() + " does not have access to channel "
                        + original.getLabel());
            }
        }
        Errata toPublish = lookupErrata(advisory, loggedInUser.getOrg());
        if (!toPublish.isCloned()) {
            throw new InvalidErrataException("Cloned errata expected.");
        }
        return publish(toPublish, channels, loggedInUser, true);
    }

    /**
     * Verify a list of channels labels, and populate their corresponding
     *      Channel objects into a List.  This is primarily used before publishing
     *      to verify all channels are valid before starting the errata creation
     * @param channelsLabels the List of channel labels to verify
     * @param org the org of the user
     * @return a List of channel objects
     */
    private List<Channel> verifyChannelList(List<String> channelsLabels, User user) {
        if (channelsLabels.size() == 0) {
            throw new NoChannelsSelectedException();
        }

        List<Channel> resolvedList = new ArrayList<Channel>();
        for (Iterator<String> itr = channelsLabels.iterator(); itr.hasNext();) {
            String channelLabel = itr.next();
            Channel channel = ChannelFactory.lookupByLabelAndUser(channelLabel, user);
            if (channel == null) {
                throw new InvalidChannelLabelException();
            }
            if (!UserManager.verifyChannelAdmin(user, channel)) {
                throw new PermissionCheckFailureException();
            }
            resolvedList.add(channel);
        }
        return resolvedList;
    }

    /**
     * private helper method to publish the errata
     * @param errata the Unpublished errata to publish
     * @param channels A list of channel objects
     * @return The published Errata
     */
    private Errata publish(Errata errata, List<Channel> channels, User user, boolean inheritPackages) {
        Errata published = ErrataFactory.publish(errata);
        for (Channel chan : channels) {
            List<Errata> list = new ArrayList<Errata>();
            list.add(published);
            published = ErrataFactory.publishToChannel(list, chan, user, inheritPackages).get(0);

        }
        return published;
    }

    /**
     * list errata by date
     * @param loggedInUser The current user
     * @param channelLabel channel associated with the errata you are interested in.
     * @return List of Errata objects
     * @deprecated being replaced by channel.software.listErrata(User LoggedInUser,
     * string channelLabel)
     *
     * @xmlrpc.doc List errata that have been applied to a particular channel by date.
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "channelLabel")
     * @xmlrpc.returntype
     *          #array()
     *              $ErrataSerializer
     *          #array_end()
     */
    @Deprecated
    public List listByDate(User loggedInUser, String channelLabel) {
        Channel channel = ChannelFactory.lookupByLabel(loggedInUser.getOrg(), channelLabel);
        return ErrataFactory.lookupByChannelSorted(loggedInUser.getOrg(), channel);
    }

    /**
     * Lookup the details for errata associated with the given CVE.
     * @param loggedInUser The current user
     * @param cveName name of the CVE
     * @return List of Errata objects
     *
     * @xmlrpc.doc Lookup the details for errata associated with the given CVE
     * (e.g. CVE-2008-3270)
     * @xmlrpc.param #session_key()
     * @xmlrpc.param #param("string", "cveName")
     * @xmlrpc.returntype
     *          #array()
     *              $ErrataSerializer
     *          #array_end()
     */
    public List<Errata> findByCve(User loggedInUser, String cveName) {
        // Get the logged in user. We don't care what roles this user has, we
        // just want to make sure the caller is logged in.

        List<Errata> erratas = ErrataManager.lookupByCVE(cveName);
        for (Iterator<Errata> iter = erratas.iterator(); iter.hasNext();) {
            Errata errata = iter.next();
            // Remove errata that do not apply to the user's org
            if (errata.getOrg() != null && !errata.getOrg().equals(loggedInUser.getOrg())) {
                iter.remove();
            }
        }
        return erratas;
    }

}