Commit Diff


commit - 5413554083a9dca6bbfb3e043e73e6325c3d53f6
commit + 3b56f81c2a7ba1f40a6b2f6a057b17501957e549
blob - af3e377b932594e963ec7aec92c47e3902eded4d
blob + 438b2c1c652543466e89365170c4e90dc7180947
--- bin/configNow.pl
+++ bin/configNow.pl
@@ -7,17 +7,21 @@ use IRCNOW::ConfigNow;
 
 use File::Basename;
 use File::Path qw(make_path);
-my $account = shift || 'blacklock';
-my @users = qw( izzyb nathan ashley );
+
+my $account = shift || die "Usage: $0 <account> <user1> <user2> <user3> <...>\n";
+my $users =  \@ARGV;
+# Temporary hack to support custom Domains
+my $custDomain;
+$custDomain = 'bnsnet.ca' if ($account eq 'blacklock');
+
 my $domain = 'user.planetofnix.com';
-my $custDomain = 'bnsnet.ca';
 my %config=(
 	type=>'shell',
 	account => $account,
-	users => \@users,
+	users => $users,
 	gitAuthor => $account,
 	gitEmail => $account . "@" . $domain,
-	gitWorkDir => './configNow',
+	gitWorkDir => "./configNow/$account",
 	ipv4 => '38.87.162.191',
 	ipv6 => '2602:fccf:1:1191::',
 	domain => $domain,
@@ -25,7 +29,6 @@ my %config=(
 	StageDir => './stageNow',
 	DeployDir => './deployNow/',
 	Accounts => [ $account ],				# List of accounts to deploy
-
 );
 
 my $shellConfig = new IRCNOW::ConfigNow( %config );
@@ -45,15 +48,16 @@ print "\n" . $shellConfig->repo_log();
 
 
 if ($shellConfig->stage_ready()) {	# Verify stage repo is ready 
+}
 	print "Deploying config Change.\n";
-	$shellConfig->stage_pull($account => '../configNow');
+	$shellConfig->stage_pull($account => "../configNow/$account");
 	$shellConfig->stage_merge();
 	$shellConfig->stage_commit();
 	$shellConfig->deploy_system();
-}
 
 
 
+
 # Get list of files changed in a diff
 #$r->run(qw(git diff 0cd562e --name-only));
 
blob - /dev/null
blob + de69b88350a3ff90f95af40634cc1bc914793005 (mode 644)
--- /dev/null
+++ Templates/etc/acme-client.conf_HEAD
@@ -0,0 +1,26 @@
+#
+# $OpenBSD: acme-client.conf,v 1.1 2019/01/08 07:14:10 florian Exp $
+#
+authority letsencrypt {
+	api url "https://acme-v02.api.letsencrypt.org/directory"
+	account key "/etc/acme/letsencrypt-privkey.pem"
+}
+
+authority letsencrypt-staging {
+	api url "https://acme-staging-v02.api.letsencrypt.org/directory"
+	account key "/etc/acme/letsencrypt-staging-privkey.pem"
+}
+
+authority buypass {
+        api url "https://api.buypass.com/acme/directory"
+        account key "/etc/acme/buypass-privkey.pem"
+        contact "mailto:support@planetofnix.com"
+}
+
+authority buypass-test {
+        api url "https://api.test4.buypass.no/acme/directory"
+        account key "/etc/acme/buypass-test-privkey.pem"
+        contact "mailto:support@planetofnix.com"
+}
+
+
blob - /dev/null
blob + 27ffe761684ec54b98e8d08fbe80cdf5486d38df (mode 644)
--- /dev/null
+++ Templates/etc/prosody/prosody.cfg.lua_HEAD
@@ -0,0 +1,308 @@
+-- Prosody Example Configuration File
+--
+-- Information on configuring Prosody can be found on our
+-- website at https://prosody.im/doc/configure
+--
+-- Tip: You can check that the syntax of this file is correct
+-- when you have finished by running this command:
+--     prosodyctl check config
+-- If there are any errors, it will let you know what and where
+-- they are, otherwise it will keep quiet.
+--
+-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
+-- blanks. Good luck, and happy Jabbering!
+
+
+---------- Server-wide settings ----------
+-- Settings in this section apply to the whole server and are the default settings
+-- for any virtual hosts
+
+-- This is a (by default, empty) list of accounts that are admins
+-- for the server. Note that you must create the accounts separately
+-- (see https://prosody.im/doc/creating_accounts for info)
+-- Example: admins = { "user1@example.com", "user2@example.net" }
+admins = {"izzyb@user.planetofnix.com"}
+support_contact = "izzyb@user.planetofnix.com"
+contact_info = {
+	abuse = { "mailto:abuse@planetofnix.com", "xmpp:izzyb@user.planetofnix.com" };
+	support = { "mailto:support@planetofnix.com", "xmpp:izzyb@user.planetofnix.com" };
+}
+-- Drop privileges
+prosody_user = "_prosody"
+prosody_group = "_prosody"
+
+-- Enable POSIX-only options
+pidfile = "/var/prosody/prosody.pid"
+
+-- This option allows you to specify additional locations where Prosody
+-- will search first for modules. For additional modules you can install, see
+-- the community module repository at https://modules.prosody.im/
+--plugin_paths = {}
+plugin_paths = { "/usr/local/lib/prosody/extras/" }
+-- This is the list of modules Prosody will load on startup.
+-- Documentation for bundled modules can be found at: https://prosody.im/doc/modules
+modules_enabled = {
+
+	-- Generally required
+		"disco"; -- Service discovery
+		"roster"; -- Allow users to have a roster. Recommended ;)
+		"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
+		"tls"; -- Add support for secure TLS on c2s/s2s connections
+
+	-- Not essential, but recommended
+		"blocklist"; -- Allow users to block communications with other users
+		"carbons"; -- Keep multiple online clients in sync
+		"dialback"; -- Support for verifying remote servers using DNS
+		"limits"; -- Enable bandwidth limiting for XMPP connections
+		"pep"; -- Allow users to store public and private data in their account
+		"private"; -- Legacy account storage mechanism (XEP-0049)
+		"smacks"; -- Stream management and resumption (XEP-0198)
+		"vcard4"; -- User profiles (stored in PEP)
+		"vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
+
+	-- Nice to have
+		"csi_simple"; -- Simple but effective traffic optimizations for mobile devices
+		"invites"; -- Create and manage invites
+		"invites_adhoc"; -- Allow admins/users to create invitations via their client
+		"invites_register"; -- Allows invited users to create accounts
+		"ping"; -- Replies to XMPP pings with pongs
+		"time"; -- Let others know the time here on this server
+		"uptime"; -- Report how long server has been running
+		"version"; -- Replies to server version requests
+		"mam"; -- Store recent messages to allow multi-device synchronization
+		--"turn_external"; -- Provide external STUN/TURN service for e.g. audio/video calls
+
+	-- Admin interfaces
+		"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
+		"admin_shell"; -- Allow secure administration via 'prosodyctl shell'
+
+	-- HTTP modules
+		"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
+		--IB Found on ircnow
+		--"http_files"; -- Serve static files from a directory over HTTP
+		--IB not on ircnow XXX
+		"http_openmetrics"; -- for exposing metrics to stats collectors
+		"websocket"; -- XMPP over WebSockets
+
+	-- Other specific functionality
+		"announce"; -- Send announcement to all online users
+		"groups"; -- Shared roster support
+		--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
+		--"mimicking"; -- Prevent address spoofing
+		--"motd"; -- Send a message to users when they log in
+		"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
+		--"s2s_bidi"; -- Bi-directional server-to-server (XEP-0288)
+		"server_contact_info"; -- Publish contact information for this service
+		--"tombstones"; -- Prevent registration of deleted accounts
+		"watchregistrations"; -- Alert admins of registrations
+		"welcome"; -- Welcome users who register accounts
+
+	-- Found on ircnow.org
+		"bookmarks2"; -- required for compliance
+		"cloud_notify"; -- Support push notifications
+		"csi"; -- Reduce network usage
+		"filter_chatstates"; -- filter chat states during inactivity
+                "smacks"; -- Improve experience on unstable networks 
+                "filter_chatstates"; -- filter chat states during inactivity
+                "auto_accept_subscriptions"; -- Auto accept subscriptions
+                --"group_bookmarks"; -- Group bookmarks
+                --"deny_omemo"; -- Deny OMEMO
+                --"roster_allinall"; -- Add everyone in roster
+                --"block_registrations"; -- block registrations using regex
+                "support_contact"; -- support contact
+                --"captcha_registration";
+                --"throttle_unsolicited"; -- throttle unsolicited messages
+                --"bookmarks";
+                "lastactivity"; -- handy, mkf
+                --"firewall";
+
+
+}
+
+-- These modules are auto-loaded, but should you want
+-- to disable them then uncomment them here:
+modules_disabled = {
+	-- "offline"; -- Store offline messages
+	-- "c2s"; -- Handle client connections
+	-- "s2s"; -- Handle server-to-server connections
+	-- "posix"; -- POSIX functionality, sends server to background, etc.
+}
+
+
+-- Server-to-server authentication
+-- Require valid certificates for server-to-server connections?
+-- If false, other methods such as dialback (DNS) may be used instead.
+--IB changed to false to match ircnow.org
+s2s_secure_auth = false
+
+
+
+-- Some servers have invalid or self-signed certificates. You can list
+-- remote domains here that will not be required to authenticate using
+-- certificates. They will be authenticated using other methods instead,
+-- even when s2s_secure_auth is enabled.
+
+--s2s_insecure_domains = { "insecure.example" }
+
+-- Even if you disable s2s_secure_auth, you can still require valid
+-- certificates for some domains by specifying a list here.
+
+--s2s_secure_domains = { "jabber.org" }
+
+--IB Found on ircnow.org
+-- Force clients to use encrypted connections? This option will
+-- prevent clients from authenticating unless they are using encryption.
+
+c2s_require_encryption = true
+
+-- Force servers to use encrypted connections? This option will
+-- prevent servers from authenticating unless they are using encryption.
+
+s2s_require_encryption = true
+
+-- required for compliance, enables support in legacy clients
+legacy_ssl_ports = { 5223 }
+legacy_ssl_ssl = {
+        key = "/etc/prosody/certs/xmpp.user.planetofnix.com.key";
+        certificate = "/etc/prosody/certs/xmpp.user.planetofnix.com.fullchain.pem";
+}
+
+--IB ----------------
+
+
+-- Rate limits
+-- Enable rate limits for incoming client and server connections. These help
+-- protect from excessive resource consumption and denial-of-service attacks.
+
+limits = {
+	c2s = {
+		rate = "10kb/s";
+	};
+	s2sin = {
+		rate = "30kb/s";
+	};
+}
+
+-- Authentication
+-- Select the authentication backend to use. The 'internal' providers
+-- use Prosody's configured data storage to store the authentication data.
+-- For more information see https://prosody.im/doc/authentication
+
+authentication = "internal_hashed"
+
+-- Many authentication providers, including the default one, allow you to
+-- create user accounts via Prosody's admin interfaces. For details, see the
+-- documentation at https://prosody.im/doc/creating_accounts
+
+
+-- Storage
+-- Select the storage backend to use. By default Prosody uses flat files
+-- in its configured data directory, but it also supports more backends
+-- through modules. An "sql" backend is included by default, but requires
+-- additional dependencies. See https://prosody.im/doc/storage for more info.
+
+--storage = "sql" -- Default is "internal"
+
+-- For the "sql" backend, you can uncomment *one* of the below to configure:
+--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
+--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
+--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
+
+
+-- Archiving configuration
+-- If mod_mam is enabled, Prosody will store a copy of every message. This
+-- is used to synchronize conversations between multiple clients, even if
+-- they are offline. This setting controls how long Prosody will keep
+-- messages in the archive before removing them.
+
+archive_expires_after = "4w" -- Remove archived messages after 1 week
+
+-- You can also configure messages to be stored in-memory only. For more
+-- archiving options, see https://prosody.im/doc/modules/mod_mam
+
+
+-- Audio/video call relay (STUN/TURN)
+-- To ensure clients connected to the server can establish connections for
+-- low-latency media streaming (such as audio and video calls), it is
+-- recommended to run a STUN/TURN server for clients to use. If you do this,
+-- specify the details here so clients can discover it.
+-- Find more information at https://prosody.im/doc/turn
+
+-- Specify the address of the TURN service (you may use the same domain as XMPP)
+--turn_external_host = "turn.example.com"
+
+-- This secret must be set to the same value in both Prosody and the TURN server
+--turn_external_secret = "your-secret-turn-access-token"
+
+
+-- Logging configuration
+-- For advanced logging see https://prosody.im/doc/logging
+log = {
+	info = "/var/prosody/prosody.log"; -- Change 'info' to 'debug' for verbose logging
+	error = "/var/prosody/prosody.err";
+	-- "*syslog"; -- Uncomment this for logging to syslog
+	-- "*console"; -- Log to the console, useful for debugging when running in the foreground
+}
+-- Uncomment to enable statistics
+-- For more info see https://prosody.im/doc/statistics
+-- statistics = "internal"
+
+
+-- Certificates
+-- Every virtual host and component needs a certificate so that clients and
+-- servers can securely verify its identity. Prosody will automatically load
+-- certificates/keys from the directory specified here.
+-- For more information, including how to use 'prosodyctl' to auto-import certificates
+-- (from e.g. Let's Encrypt) see https://prosody.im/doc/certificates
+
+-- Location of directory to find certificates in (relative to main config file):
+certificates = "certs"
+
+--IB Found on ircnow.org
+-- HTTPS currently only supports a single certificate, specify it here:
+-- Expects to find the key in same name .key
+https_certificate = "/etc/prosody/certs/xmpp.user.planetofnix.com.crt"
+
+----------- Virtual hosts -----------
+-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
+-- Settings under each VirtualHost entry apply *only* to that host.
+--IB for shellname@user.plantofnix.com addresses
+VirtualHost "user.planetofnix.com"
+
+key = "/etc/prosody/certs/xmpp.user.planetofnix.com.key"
+certificate = "/etc/prosody/certs/xmpp.user.planetofnix.com.fullchain.pem"
+--IB Found on ircnow.org
+disco_items = { 
+        { "xmpp.user.planetofnix.com" },
+}       
+http_host = "xmpp.user.planetofnix.com"
+--http_default_host = "ircnow.org"
+http_upload_access = {"xmpp.user.planetofnix.com"};
+http_upload_file_size_limit = 200*1024*1024 -- 200MB
+http_upload_expire_after = 60 * 60 * 24 * 60 -- 60 days in sec
+                
+----- Components ------
+-- You can specify components to add hosts that provide special services,
+-- like multi-user conferences, and transports.
+-- For more information on components, see https://prosody.im/doc/components
+                
+---Set up a MUC (multi-user chat) room server on conference.example.com:
+Component "xmpp.user.planetofnix.com" "muc"
+--- Store MUC messages in an archive and allow users to access it
+modules_enabled = { 
+                "muc_mam",
+                "vcard_muc", -- profile photos for groups
+        }
+
+---Set up an external component (default component port is 5347)
+--
+-- External components allow adding various services, such as gateways/
+-- transports to other networks like ICQ, MSN and Yahoo. For more info
+-- see: https://prosody.im/doc/components#adding_an_external_component
+--
+Component "xmpp.user.planetofnix.com"
+        component_secret = "redacted"
+
+Component "xmpp.user.planetofnix.com" "http_upload"
+
+
blob - /dev/null
blob + 28ad757f1207e3869b63b7f8cb1b2991bcb475ed (mode 644)
--- /dev/null
+++ Templates/var/nsd/zones/master/user.planetofnix.com_HEAD
@@ -0,0 +1,33 @@
+$ORIGIN user.planetofnix.com.
+user.planetofnix.com.     3600   SOA   ns1.user.planetofnix.com. root.planetofnix.com. (
+                            2023083101   ; serial YYYYMMDDnn
+                            1800        ; refresh
+                            3600         ; retry
+                            86400       ; expire
+                            3600 )      ; minimum TTL
+        3600    IN      A      38.87.162.191 
+        3600    IN      AAAA    2602:fccf:1:1191::
+        3600    IN      NS      ns1
+        3600    IN      NS      ns2
+		3600	IN		MX		10 mail
+ns1     3600    IN      A       198.251.81.44
+        3600    IN      AAAA    2605:6400:10:69d::
+ns2     3600    IN      A       198.251.81.44
+        3600    IN      AAAA    2605:6400:10:69d::
+mail    3600    IN      A       38.87.162.191
+        3600    IN      AAAA    2602:fccf:1:1191::
+imap    3600    IN      A       38.87.162.191
+        3600    IN      AAAA    2602:fccf:1:1191::
+pop     3600    IN      A       38.87.162.191
+        3600    IN      AAAA    2602:fccf:1:1191::
+pop3    3600    IN      A       38.87.162.191
+        3600    IN      AAAA    2602:fccf:1:1191::
+smtp    3600    IN      A       38.87.162.191
+        3600    IN      AAAA    2602:fccf:1:1191::
+_adsp._domainkey	3600	IN	TXT	"dkim=discardable;"
+_mail._domainkey	3600	IN	TXT	"k=rsa; t-s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJkGACi9cbYFFUYZ8Kn/UfVuveop1v9FD1jQGwWkIO3s5DMo+1uhOwsMtHfpOFzfIZrQAwNP8g/X4Gi7QRt/OIPRgPM6qV1vhABCXFzisYd/3hiMpPigK1n+0iagKJdFbvSw8RDroPk4G/63nJsg4vbda1R+zNEKeZeUv3xgu/eQIDAQAB"
+xmpp	3600	IN		A		38.87.162.191
+		3600	IN		AAAA	2602:fccf:1:1191::
+_xmpp-client._tcp	3600	IN	SRV	0	5	5222	xmpp
+_xmpp-server._tcp	3600	IN	SRV	0	5	5269	xmpp
+
blob - 2deb07066ae0be4666ae274b8a37d42ec46dad7b
blob + 44aa8bccf65653533ff64d8936004738a8f50322
--- lib/IRCNOW/ConfigNow.pm
+++ lib/IRCNOW/ConfigNow.pm
@@ -44,7 +44,9 @@ sub new {
 		$self->mod_load('mail','IRCNOW::ConfigNow::Module::SmtpDove');
 		$self->mod_load('xmpp','IRCNOW::ConfigNow::Module::Prosody');
 		$self->mod_load('acme','IRCNOW::ConfigNow::Module::AcmeClient');
-		$self->mod_load('custDomain','IRCNOW::ConfigNow::Module::CustDomain');
+		if (defined $vars->{custDomain}) {
+			$self->mod_load('custDomain','IRCNOW::ConfigNow::Module::CustDomain');
+		}
 	}
 	return $self;
 }
@@ -100,26 +102,36 @@ sub vars {
 sub write_file {
 	my $self = shift;
 	my $filename = shift;
-	my $workDir=$self->{vars}->{gitWorkDir};
-	$filename = "$workDir/$filename";
 	my $output = shift;
+	my $workDir = shift || $self->{vars}->{gitWorkDir} || die 'No work directory specified';
+	$filename = "$workDir/$filename";
 	my $path = dirname($filename);
-#print "Making Directory: $path\n";
 	make_path($path);
-print "Writing: $filename\n";
-	#XXX add proper error handling
-	open my $FH, ">>$filename";
+	open my $FH, ">>$filename" || die "failed to open $filename for appending";
 	print $FH $output;
 	close $FH;
 }
 
+sub read_file {
+	my $self = shift;
+	my $filename = shift;
+	my $workDir = shift || $self->{vars}->{gitWorkDir} || die 'No work directory specified';
+	$filename = "$workDir/$filename";
+	my $output="";
+	open (my $FH, "<$filename") || die "failed to open $filename for reading";
+	while (<$FH>) {
+		$output.= $_;
+	}
+	close $FH;
+	return $output;
+}
+
 # Delete specified file
 sub delete_file {
 	my $self = shift;
 	my $filename = shift;
-	my $workDir=$self->{vars}->{gitWorkDir};
+	my $workDir= shift  || $self->{vars}->{gitWorkDir};
 	$filename = "$workDir/$filename";
-print "Deleting $filename\n";
 	unlink $filename;
 }
 
@@ -151,7 +163,6 @@ sub write_config {
 			}
 			# Output for $type = "custDomain"
 			if (exists $self->{vars}->{custDomain}) {
-warn "custDomain: $filename";
 				# generate output for type = 'custDomain'
 				my $out = $obj->output($filename, 'custDomain');
 				$output .= $out if defined $out;
@@ -221,7 +232,7 @@ sub repo_connect {
 	my $config=$self->{vars};
 	my $dir = shift || $config->{gitWorkDir};
 	return $self->{$repo} if defined $self->{$repo};
-	$self->{$repo} = Git::Repository->new(
+	my $r = Git::Repository->new(
 		work_tree => $dir,
 		{
 			env => {
@@ -230,14 +241,16 @@ sub repo_connect {
 			},
 		},
 	);
-	# Ready for changes.
-	return $self->{$repo};
+	# don't save temp repos.
+	$self->{$repo} = $r unless ($repo eq 'tmp');
+	return $r;
 }
 
 sub repo_commit {
 	my $self=shift;
-	my $r = $self->{repo};
-	my $workDir = $self->{vars}->{gitWorkDir};
+	my $repo = shift || 'repo';
+	my $workDir = shift || $self->{vars}->{gitWorkDir};
+	my $r = $self->{$repo} || $self->repo_connect($repo,$workDir);
 	# Check status of untracked files.
 	my $update=0; # Flag for changes to commit
 	my @output = $r->run(qw(status -su));
@@ -251,7 +264,7 @@ sub repo_commit {
 			} else {
 				print "... ignoring $1\n";
 			}
-		} elsif ($file =~ /^\s+M\s+/) {
+		} elsif ($file =~ /^\s?[MA]\s+/) { # File modified or added thats already tracked
 			$update = 1;
 		}
 	}
@@ -271,7 +284,10 @@ sub stage_ready {
 			# Initialize the config repo if it hasn't been created yet.
 			make_path($workDir);
 			make_path("$workDir/Accounts");
-			make_path("$workDir/Stage");
+			make_path("$workDir/Templates");
+			make_path("$workDir/Staged");
+			# XXX cheap hack to copy template files - should do this without the system()
+			system("cp -av ./Templates/* $workDir/Templates/");
 			Git::Repository->run(init => $workDir);
 		}
 		$r = $self->repo_connect('stage_repo', $workDir);
@@ -292,22 +308,103 @@ sub stage_ready {
 sub stage_pull {
 	my $self=shift;
 	my ($account, $upstream) = @_;
-	my $r = $self->{stage_repo};
 	my $account_repo="Accounts/$account";
-	if (not -d $account_repo) {
+	if (not -d $self->{vars}->{StageDir} . "/$account_repo") {
 		# Don't have this account so clone it
-		my @status = $r->run("submodule","add", $upstream,$account_repo);
-		use Data::Dumper;
-		print Dumper(@status);
-		return 1;
+		my $r = $self->{stage_repo};
+		my @results = $r->run("submodule","add", $upstream, $account_repo);
+	} else {
+		my $r = $self->repo_connect('tmp',$self->{vars}->{StageDir} . "/$account_repo");
+		my @results = $r->run("pull");
 	}
 }
 
 
+# Find any _HEAD templates for given file
+sub template_head {
+	my $self = shift;
+	my $filename=shift;
+	my $templates=$self->{vars}->{StageDir} . '/Templates';
+	my $output = "";
+	if (-e "$templates/$filename" . "_HEAD") {
+print "Reading: $templates/$filename" . "_HEAD\n";
+		open (my $FH, "<$templates/$filename" . "_HEAD");
+		while (<$FH>) {
+			$output.=$_;
+		}
+		close $FH;
+	}
+	return $output;
+}
+	
+
+
+
 sub stage_merge {
+	my $self = shift;
+	#	# abort of the repo isn't ready
+	#	return 0 unless $self->repo_ready();
+	# get the output for each module for this filename.
+	my $stagedDir=$self->{vars}->{StageDir} . "/Staged";
+	my $accountsDir = $self->{vars}->{StageDir} . "/Accounts";
+	for my $filename ($self->filenames()) {
+		#purge existing files before generation.
+		$self->delete_file($filename,$stagedDir);
+		# Output template header if one exists
+		my $output=$self->template_head($filename);
+		# Find configNow repos with content for this filename
+		opendir(my $DH, $accountsDir) || die "can't open directory $accountsDir: $!";
+		while (readdir $DH) {
+			next if $_ =~ /^\./;
+			if (-d "$accountsDir/$_" and -e "$accountsDir/$_/$filename") {
+				$output .= $self->read_file($filename, "$accountsDir/$_");
+			}
+		}
+		closedir $DH;
+		$self->write_file($filename, $output, $stagedDir)
+	}
+
+#			my $out = $obj->output($filename,$type);
+#			$output .= $out if defined $out;
+#			# Output for $type . "users" if we have a user list
+#			if (exists $self->{vars}->{users}) {
+#				for my $user (@{$self->{vars}->{users}}) {
+#					# set the username var to this user.
+#					$self->{vars}->{username} = $user;
+#					# generate output for $type . 'user'
+#					my $out = $obj->output($filename,$type . "user");
+#					$output .= $out if defined $out;
+#					# delete the username so it doesn't bleed anywhere
+#					delete $self->{vars}->{username};
+#				}
+#			}
+#			# Output for $type = "custDomain"
+#			if (exists $self->{vars}->{custDomain}) {
+#warn "custDomain: $filename";
+#				# generate output for type = 'custDomain'
+#				my $out = $obj->output($filename, 'custDomain');
+#				$output .= $out if defined $out;
+#			}
+#		}
+#		if (length($output)>0) {	# Should we support empty files?
+#			$self->write_file($filename,$output);
+#		}
+#	}
+#	return 1;
 }
+
+
+
+
 sub stage_commit {
+	my $self = shift;
+print "stage_commit(): " . $self->{vars}->{StageDir} . "\n";
+	return $self->repo_commit('stage_repo',$self->{vars}->{StageDir});
+
+	
 }
+
+
 sub deploy_system {
 }