diff --git a/cobbler/action_reposync.py b/cobbler/action_reposync.py
index f8f40e5..201dba3 100644
--- a/cobbler/action_reposync.py
+++ b/cobbler/action_reposync.py
@@ -143,22 +143,6 @@ class RepoSync:
 
     # ==================================================================================
 
-    def createrepo_walker(self, repo, dirname, fnames):
-        """
-        Used to run createrepo on a copied mirror.
-        """
-        if os.path.exists(dirname) or repo['breed'] == 'rsync':
-            utils.remove_yum_olddata(dirname)
-            try:
-                cmd = "createrepo %s %s" % (repo.createrepo_flags, dirname)
-                print _("- %s") % cmd
-                sub_process.call(cmd, shell=True)
-            except:
-                print _("- createrepo failed.  Is it installed?")
-            del fnames[:] # we're in the right place
-
-    # ==================================================================================
-
     def update_permissions(self, repo_path):
         """
         Verifies that permissions and contexts after an rsync are as expected.
diff --git a/cobbler/codes.py b/cobbler/codes.py
index ff4e1d7..3a858b4 100644
--- a/cobbler/codes.py
+++ b/cobbler/codes.py
@@ -51,3 +51,7 @@ VALID_OS_VERSIONS = {
     "other"   : [ "msdos", "netware4", "netware5", "netware6", "generic", "other" ]
 }
 
+VALID_REPO_BREEDS = [
+    "rsync", "rhn", "yum", "apt"
+]
+
diff --git a/cobbler/collection_repos.py b/cobbler/collection_repos.py
index 9435ff7..d54d2c6 100644
--- a/cobbler/collection_repos.py
+++ b/cobbler/collection_repos.py
@@ -45,8 +45,14 @@ class Repos(collection.Collection):
         if seed_data.has_key('breed'):
             if seed_data['breed'] == "rsync":
                 return repo.RsyncRepo(config).from_datastruct(seed_data)
-            elif seed_data['breed'] == "redhat":
+            elif seed_data['breed'] == "rhn":
+                return repo.RhnRepo(config).from_datastruct(seed_data)
+            elif seed_data['breed'] == "yum":
                 return repo.YumRepo(config).from_datastruct(seed_data)
+            elif seed_data['breed'] == "apt":
+                return repo.AptRepo(config).from_datastruct(seed_data)
+            else:
+                raise CX(_("Uknown repository breed %s")%seed_data['breed'])
         # Required until rsync breed is properly set on every required case
         # Block, taken from is_rsync_mirror
         lower = seed_data.get("mirror")
diff --git a/cobbler/item_repo.py b/cobbler/item_repo.py
index 0158a82..ce9b725 100644
--- a/cobbler/item_repo.py
+++ b/cobbler/item_repo.py
@@ -154,7 +154,8 @@ class Repo(item.Item):
         return True
 
     def set_breed(self,breed):
-        return utils.set_breed(self,breed)
+        if breed:
+            return utils.set_repo_breed(self,breed)
 
     def set_arch(self,arch):
         """
@@ -348,12 +349,26 @@ class Repo(item.Item):
         # now run createrepo to rebuild the index
 
         if self.mirror_locally:
-            os.path.walk(dest_path, obj_sync.createrepo_walker, self)
+            os.path.walk(dest_path, self.createrepo_walker, self)
 
         # create the config file the hosts will use to access the repository.
 
         obj_sync.create_local_file(self, dest_path)
  
+    def createrepo_walker(self, repo, dirname, fnames):
+        """
+        Used to run createrepo on a copied mirror.
+        """
+        if os.path.exists(dirname) or repo['breed'] == 'rsync':
+            utils.remove_yum_olddata(dirname)
+            try:
+                cmd = "createrepo %s %s" % (repo.createrepo_flags, dirname)
+                print _("- %s") % cmd
+                sub_process.call(cmd, shell=True)
+            except:
+                print _("- createrepo failed.  Is it installed?")
+            del fnames[:] # we're in the right place
+
 
 class RsyncRepo(Repo):
 
@@ -380,11 +395,225 @@ class RsyncRepo(Repo):
         if rc !=0:
             raise CX(_("cobbler reposync failed"))
         print _("- walking: %s") % dest_path
-        os.path.walk(dest_path, obj_sync.createrepo_walker, self)
+        os.path.walk(dest_path, self.createrepo_walker, self)
         obj_sync.create_local_file(self, dest_path)
     
+class RhnRepo(Repo):
+
+    def sync(self, repo_mirror, obj_sync):
+
+        """
+        Handle copying of http:// and ftp:// RHN repos.
+        """
+
+        # warn about not having yum-utils.  We don't want to require it in the package because
+        # RHEL4 and RHEL5U0 don't have it.
+
+        if not os.path.exists("/usr/bin/reposync"):
+            raise CX(_("no /usr/bin/reposync found, please install yum-utils"))
+
+        cmd = ""                  # command to run
+        has_rpm_list = False      # flag indicating not to pull the whole repo
+
+        # detect cases that require special handling
+
+        if self.rpm_list != "":
+            has_rpm_list = True
+
+        # create yum config file for use by reposync
+        dest_path = os.path.join(repo_mirror, self.name)
+        temp_path = os.path.join(repo_mirror, ".origin")
+        if not os.path.isdir(temp_path) and self.mirror_locally:
+            # FIXME: there's a chance this might break the RHN D/L case
+            os.makedirs(temp_path)
+         
+        # how we invoke yum-utils depends on whether this is RHN content or not.
+
+       
+        # this is the somewhat more-complex RHN case.
+        # NOTE: this requires that you have entitlements for the server and you give the mirror as rhn://$channelname
+        if not self.mirror_locally:
+            raise CX(_("rhn:// repos do not work with --mirror-locally=1"))
+
+        if has_rpm_list:
+            print _("- warning: --rpm-list is not supported for RHN content")
+        rest = self.mirror[6:] # everything after rhn://
+        cmd = "/usr/bin/reposync %s -r %s --download_path=%s" % (obj_sync.rflags, rest, repo_mirror)
+        if self.name != rest:
+            args = { "name" : self.name, "rest" : rest }
+            raise CX(_("ERROR: repository %(name)s needs to be renamed %(rest)s as the name of the cobbler repository must match the name of the RHN channel") % args)
+
+        if self.arch == "i386":
+            # counter-intuitive, but we want the newish kernels too
+            self.arch = "i686"
+
+        if self.arch != "":
+            cmd = "%s -a %s" % (cmd, self.arch)
+
+        # now regardless of whether we're doing yumdownloader or reposync
+        # or whether the repo was http://, ftp://, or rhn://, execute all queued
+        # commands here.  Any failure at any point stops the operation.
+
+        if self.mirror_locally:
+            rc = sub_process.call(cmd, shell=True)
+            if rc !=0:
+                raise CX(_("cobbler reposync failed"))
+
+        # some more special case handling for RHN.
+        # create the config file now, because the directory didn't exist earlier
+
+        temp_file = obj_sync.create_local_file(self, temp_path, output=False)
+
+        # now run createrepo to rebuild the index
+
+        if self.mirror_locally:
+            os.path.walk(dest_path, self.createrepo_walker, self)
+
+        # create the config file the hosts will use to access the repository.
+
+        obj_sync.create_local_file(self, dest_path)
+ 
+
 class YumRepo(Repo):
 
     def sync(self, repo_mirror, obj_sync):
-        raise CX(_("Trying to sync repo %s"%self))
 
+        """
+        Handle copying of http:// and ftp:// yum repos.
+        """
+
+        # warn about not having yum-utils.  We don't want to require it in the package because
+        # RHEL4 and RHEL5U0 don't have it.
+
+        if not os.path.exists("/usr/bin/reposync"):
+            raise CX(_("no /usr/bin/reposync found, please install yum-utils"))
+
+        cmd = ""                  # command to run
+        has_rpm_list = False      # flag indicating not to pull the whole repo
+
+        # detect cases that require special handling
+
+        if self.rpm_list != "":
+            has_rpm_list = True
+
+        # create yum config file for use by reposync
+        dest_path = os.path.join(repo_mirror, self.name)
+        temp_path = os.path.join(repo_mirror, ".origin")
+        if not os.path.isdir(temp_path) and self.mirror_locally:
+            # FIXME: there's a chance this might break the RHN D/L case
+            os.makedirs(temp_path)
+         
+        # create the config file that yum will use for the copying
+
+        if self.mirror_locally:
+            temp_file = obj_sync.create_local_file(self, temp_path, output=False)
+
+        if not has_rpm_list and self.mirror_locally:
+            # if we have not requested only certain RPMs, use reposync
+            cmd = "/usr/bin/reposync %s --config=%s --repoid=%s --download_path=%s" % (obj_sync.rflags, temp_file, self.name, repo_mirror)
+            if self.arch != "":
+                if self.arch == "x86":
+                   self.arch = "i386" # FIX potential arch errors
+                if self.arch == "i386":
+                   # counter-intuitive, but we want the newish kernels too
+                   cmd = "%s -a i686" % (cmd)
+                else:
+                   cmd = "%s -a %s" % (cmd, self.arch)
+                    
+            print _("- %s") % cmd
+
+        elif self.mirror_locally:
+
+            # create the output directory if it doesn't exist
+            if not os.path.exists(dest_path):
+               os.makedirs(dest_path)
+
+            use_source = ""
+            if self.arch == "src":
+                use_source = "--source"
+ 
+            # older yumdownloader sometimes explodes on --resolvedeps
+            # if this happens to you, upgrade yum & yum-utils
+            extra_flags = obj_sync.settings.yumdownloader_flags
+            cmd = "/usr/bin/yumdownloader %s %s -c %s --destdir=%s %s" % (extra_flags, use_source, temp_file, dest_path, " ".join(self.rpm_list))
+            print _("- %s") % cmd
+
+        # now regardless of whether we're doing yumdownloader or reposync
+        # or whether the repo was http://, ftp://, or rhn://, execute all queued
+        # commands here.  Any failure at any point stops the operation.
+
+        if self.mirror_locally:
+            rc = sub_process.call(cmd, shell=True)
+            if rc !=0:
+                raise CX(_("cobbler reposync failed"))
+
+        # now run createrepo to rebuild the index
+
+        if self.mirror_locally:
+            os.path.walk(dest_path, self.createrepo_walker, self)
+
+        # create the config file the hosts will use to access the repository.
+
+        obj_sync.create_local_file(self, dest_path)
+ 
+class AptRepo(Repo):
+
+    def sync(self, repo_mirror, obj_sync):
+
+        """
+        Handle copying of http:// and ftp:// debian repos.
+        """
+
+        # warn about not having mirror program.
+
+        mirror_program = "/usr/bin/debmirror"
+        if not os.path.exists(mirror_program):
+            raise CX(_("no %s found, please install it")%(mirror_program))
+
+        cmd = ""                  # command to run
+        has_rpm_list = False      # flag indicating not to pull the whole repo
+
+        # detect cases that require special handling
+
+        if self.rpm_list != "":
+            raise CX(_("has_rpm_list not yet supported on apt repos"))
+
+        # create yum config file for use by reposync
+        dest_path = os.path.join(repo_mirror, self.name)
+         
+        if self.mirror_locally:
+            mirror = self.mirror
+
+            idx = mirror.find("://")
+            method = mirror[:idx]
+            mirror = mirror[idx+3:]
+
+            idx = mirror.find("/")
+            host = mirror[:idx]
+            mirror = mirror[idx+1:]
+
+            idx = mirror.rfind("/")
+            suite = mirror[idx+1:]
+            mirror = mirror[:idx]
+
+            mirror_data = "--method=%s --host=%s --root=%s --dist=%s " % ( method , host , mirror , suite )
+
+            # FIXME : flags should come from obj_sync instead of being hardcoded
+            rflags = "--passive --nocleanup --ignore-release-gpg --verbose"
+            cmd = "%s %s %s %s" % (mirror_program, rflags, mirror_data, dest_path)
+            if not self.arch:
+                raise CX(_("Architecture is required for apt repositories"))
+            if self.arch == "src":
+                cmd = "%s --source"
+            else:
+                use_source = "--source"
+                if self.arch == "x86":
+                   self.arch = "i386" # FIX potential arch errors
+                cmd = "%s -a %s" % (cmd, self.arch)
+                    
+            print _("- %s") % cmd
+
+            rc = sub_process.call(cmd, shell=True)
+            if rc !=0:
+                raise CX(_("cobbler reposync failed"))
+ 
diff --git a/cobbler/modules/cli_repo.py b/cobbler/modules/cli_repo.py
index dd34a10..245326e 100644
--- a/cobbler/modules/cli_repo.py
+++ b/cobbler/modules/cli_repo.py
@@ -47,7 +47,7 @@ class RepoFunction(commands.CobblerFunction):
 
 
         if not self.matches_args(args,["dumpvars","remove","report","list"]):
-
+            p.add_option("--breed",             dest="breed",             help="sets the breed of the repo")
             p.add_option("--arch",             dest="arch",             help="overrides repo arch if required")
         if self.matches_args(args,["add"]):
             p.add_option("--clobber", dest="clobber", help="allow add to overwrite existing objects", action="store_true")
@@ -92,6 +92,7 @@ class RepoFunction(commands.CobblerFunction):
         if self.matches_args(self.args,["dumpvars"]):
             return self.object_manipulator_finish(obj, self.api.profiles, self.options)
 
+        if self.options.breed:            obj.set_breed(self.options.breed)
         if self.options.arch:             obj.set_arch(self.options.arch)
         if self.options.createrepo_flags: obj.set_createrepo_flags(self.options.createrepo_flags)
         if self.options.rpm_list:         obj.set_rpm_list(self.options.rpm_list)
diff --git a/cobbler/utils.py b/cobbler/utils.py
index 05d458a..b8e54a0 100644
--- a/cobbler/utils.py
+++ b/cobbler/utils.py
@@ -903,6 +903,14 @@ def set_breed(self,breed):
    nicer = ", ".join(valid_breeds)
    raise CX(_("invalid value for --breed, must be one of %s, different breeds have different levels of support") % nicer)
 
+def set_repo_breed(self,breed):
+   valid_breeds = codes.VALID_REPO_BREEDS
+   if breed is not None and breed.lower() in valid_breeds:
+       self.breed = breed.lower()
+       return True
+   nicer = ", ".join(valid_breeds)
+   raise CX(_("invalid value for --breed, must be one of %s, different breeds have different levels of support") % nicer)
+
 def set_repos(self,repos,bypass_check=False):
    # WARNING: hack
    repos = fix_mod_python_select_submission(repos)
