|  | using System; | 
|  | using System.Collections.Generic; | 
|  | using System.IO; | 
|  | using System.Security.Cryptography; | 
|  | using System.Text; | 
|  | using System.Text.RegularExpressions; | 
|  | using Microsoft.Deployment.WindowsInstaller; | 
|  |  | 
|  | namespace CustomAction | 
|  | { | 
|  | public class CustomActions | 
|  | { | 
|  | [CustomAction] | 
|  | public static ActionResult InitCookieValue(Session session) | 
|  | { | 
|  | try | 
|  | { | 
|  | byte[] buffer = new byte[16]; | 
|  | RandomNumberGenerator rng = RNGCryptoServiceProvider.Create(); | 
|  | rng.GetBytes(buffer); | 
|  | session["COOKIEVALUE"] = BitConverter.ToString(buffer).Replace("-", String.Empty); | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | session.Log("ERROR in custom action InitCookieValue {0}", | 
|  | ex.ToString()); | 
|  | return ActionResult.Failure; | 
|  | } | 
|  |  | 
|  | return ActionResult.Success; | 
|  | } | 
|  | [CustomAction] | 
|  | public static ActionResult WriteAdminIniFile(Session session) | 
|  | { | 
|  | try | 
|  | { | 
|  | if (!File.Exists(session.CustomActionData["ADMINCONFIGFILE"])) | 
|  | { | 
|  | using (StreamWriter writer = new StreamWriter(session.CustomActionData["ADMINCONFIGFILE"])) | 
|  | { | 
|  | writer.WriteLine("; CouchDB Windows installer-generated admin user"); | 
|  | writer.WriteLine("[admins]"); | 
|  | writer.WriteLine($"{session.CustomActionData["ADMINUSER"]} = {session.CustomActionData["ADMINPASSWORD"]}"); | 
|  | } | 
|  | } | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | session.Log("ERROR in custom action WriteAdminIniFile {0}", | 
|  | ex.ToString()); | 
|  | return ActionResult.Failure; | 
|  | } | 
|  |  | 
|  | return ActionResult.Success; | 
|  | } | 
|  |  | 
|  | [CustomAction] | 
|  | public static ActionResult WriteCookieToVmArgs(Session session) | 
|  | { | 
|  | try | 
|  | { | 
|  | string VMARGSFILE = Path.Combine(session.CustomActionData["ETCDIR"], "vm.args"); | 
|  | if (File.Exists(VMARGSFILE)) | 
|  | { | 
|  | session.Log("Patching erlang cookie in existing vm.args file"); | 
|  | PatchErlangCookie(session, VMARGSFILE); | 
|  | } | 
|  |  | 
|  | string VMFile = Path.Combine(session.CustomActionData["ETCDIR"], "vm.args.dist"); | 
|  | byte[] VMBuffer = File.ReadAllBytes(VMFile); | 
|  | string VMText = Regex.Replace(Encoding.UTF8.GetString(VMBuffer), @"# -setcookie", $"-setcookie {session.CustomActionData["COOKIEVALUE"]}"); | 
|  | File.WriteAllBytes(VMFile, Encoding.UTF8.GetBytes(VMText)); | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | session.Log("ERROR in custom action WriteCookieToVmArgs {0}", | 
|  | ex.ToString()); | 
|  | return ActionResult.Failure; | 
|  | } | 
|  |  | 
|  | return ActionResult.Success; | 
|  | } | 
|  |  | 
|  | private static void PatchErlangCookie(Session session, string file) | 
|  | { | 
|  | //Patching erlang cookie | 
|  | byte[] VMBuffer = File.ReadAllBytes(file); | 
|  | string VMText = Regex.Replace(Encoding.UTF8.GetString(VMBuffer), @"-setcookie \S*", $"-setcookie {session.CustomActionData["COOKIEVALUE"]}"); | 
|  | File.WriteAllBytes(file, Encoding.UTF8.GetBytes(VMText)); | 
|  | } | 
|  |  | 
|  | private static void PatchErlangInterface(Session session, string file) | 
|  | { | 
|  | byte[] VMBuffer = File.ReadAllBytes(file); | 
|  |  | 
|  | //Patching erlang interface | 
|  | string pattern = @"-kernel inet_dist_use_interface"; | 
|  | string input = Encoding.UTF8.GetString(VMBuffer); | 
|  | Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase); | 
|  | if (!m.Success) | 
|  | { | 
|  | session.Log("Pattern \"inet_dist_use_interface\" not found, appending fix."); | 
|  |  | 
|  | using (StreamWriter sw = File.AppendText(file)) | 
|  | { | 
|  | sw.WriteLine(); | 
|  | sw.WriteLine("# Which interfaces should the node listen on?"); | 
|  | sw.WriteLine("-kernel inet_dist_use_interface {127,0,0,1}"); | 
|  | sw.Close(); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | session.Log("Pattern \"inet_dist_use_interface\" found, skipping."); | 
|  | } | 
|  | } | 
|  |  | 
|  | [CustomAction] | 
|  | public static ActionResult MaybeCopyIniFiles(Session session) | 
|  | { | 
|  | try | 
|  | { | 
|  | string[] files = new string[2]; | 
|  | files[0] = "vm.args"; | 
|  | files[1] = "local.ini"; | 
|  |  | 
|  | string VMARGSFILE = Path.Combine(session.CustomActionData["ETCDIR"], files[0]); | 
|  |  | 
|  | if (File.Exists(VMARGSFILE)) | 
|  | { | 
|  | session.Log("Patching erlang interface in existing vm.args file"); | 
|  | PatchErlangInterface(session, VMARGSFILE); | 
|  | } | 
|  |  | 
|  | foreach (string file in files) | 
|  | { | 
|  | if (!File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file))) | 
|  | { | 
|  | File.Copy( | 
|  | Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist"), | 
|  | Path.Combine(session.CustomActionData["ETCDIR"], file) | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | session.Log("ERROR in custom action MaybeCopyIniFiles {0}", | 
|  | ex.ToString()); | 
|  | return ActionResult.Failure; | 
|  | } | 
|  | return ActionResult.Success; | 
|  | } | 
|  |  | 
|  | [CustomAction] | 
|  | public static ActionResult MaybeRemoveUserConfig(Session session) | 
|  | { | 
|  | try | 
|  | { | 
|  | string[] files = new string[2]; | 
|  | files[0] = "vm.args"; | 
|  | files[1] = "local.ini"; | 
|  |  | 
|  | foreach (string file in files) | 
|  | { | 
|  | if (File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file)) && | 
|  | File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist"))) | 
|  | { | 
|  | if (GetChecksum(Path.Combine(session.CustomActionData["ETCDIR"], file)) == | 
|  | GetChecksum(Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist"))) | 
|  | { | 
|  | File.Delete(Path.Combine(session.CustomActionData["ETCDIR"], file)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | session.Log("ERROR in custom action MaybeRemoveUserConfig {0}", | 
|  | ex.ToString()); | 
|  | return ActionResult.Failure; | 
|  | } | 
|  | return ActionResult.Success; | 
|  | } | 
|  |  | 
|  | private static string GetChecksum(string file) | 
|  | { | 
|  | using (FileStream stream = File.OpenRead(file)) | 
|  | { | 
|  | SHA256CryptoServiceProvider sha = new SHA256CryptoServiceProvider(); | 
|  | byte[] checksum = sha.ComputeHash(stream); | 
|  | return BitConverter.ToString(checksum).Replace("-", String.Empty); | 
|  | } | 
|  | } | 
|  | } | 
|  | } |