using System;
using System.Net;
using System.Collections;
using System.Data;
using System.IO;
using System.Security.Principal;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using UDDI;
using UDDI.Replication;
using UDDI.Diagnostics;
namespace UDDI.Tools
{
public class ReplicationConfigurationUtility
{
private static string executable = System.AppDomain.CurrentDomain.FriendlyName;
private static string filename = null;
private static string operatorKey = null;
private static string rcfFile = null;
private static bool overwrite = false;
private enum ModeType
{
None = 0,
ImportOperatorCertificate = 1,
ExportOperatorCertificate = 2,
ImportRCF = 3
}
private static ModeType mode = ModeType.None;
/// ****************************************************************
/// internal ProcessCommandLine [static]
/// ----------------------------------------------------------------
///
/// Parse the command-line.
///
/// ----------------------------------------------------------------
///
/// Command-line arguments.
///
/// ****************************************************************
internal static void ProcessCommandLine( string[] args )
{
int i = 0;
while( i < args.Length )
{
if( '-' == args[i][0] || '/' == args[i][0] )
{
//
// Process the switch.
//
switch( args[i].Substring( 1 ).ToLower() )
{
case "i":
if( i == args.Length - 1 )
throw new CommandLineException( "Missing required argument 'certfile'." );
mode = ModeType.ImportOperatorCertificate;
i ++;
filename = args[i];
if( !File.Exists( filename ) )
throw new CommandLineException( "Certificate file '" + filename + "' does not exist." );
break;
case "e":
if( i == args.Length - 1 )
throw new CommandLineException( "Missing required argument 'certfile'." );
mode = ModeType.ExportOperatorCertificate;
i ++;
filename = args[i];
break;
case "o":
if( i == args.Length - 1 )
throw new CommandLineException( "Missing required argument 'operatorkey'." );
i ++;
operatorKey = args[i];
//
// strip {'s and }'s
//
operatorKey = operatorKey.Replace("{", string.Empty);
operatorKey = operatorKey.Replace("}", string.Empty);
break;
case "r":
if( i == args.Length - 1 )
{
throw new CommandLineException( "Missing required argument 'path to RCF file'." );
}
i ++;
rcfFile = args[i];
mode = ModeType.ImportRCF;
if( !File.Exists( rcfFile ) )
throw new CommandLineException( "RCF file '" + rcfFile + "' does not exist." );
break;
case "y":
overwrite = true;
break;
case "?":
goto case "help";
case "help":
throw new CommandLineException( "" );
default:
throw new CommandLineException( "Unknown command-line parameter '" + args[i] + "'." );
}
}
i ++;
}
//
// Make sure the appropriate options were set.
//
//if()
// throw new CommandLineException( "Missing required command-line parameters." );
}
public static void ImportOperatorCertificate()
{
X509Certificate certificate = X509Certificate.CreateFromCertFile( filename );
SaveOperatorInfo( operatorKey, certificate );
}
public static void ImportRCF()
{
FileStream rcfStream = File.Open( rcfFile, FileMode.Open, FileAccess.Read, FileShare.Read );
try
{
//
// Validate the RCF file.
//
SchemaCollection.Validate( rcfStream );
//
//
// Open our RCF file, it has already been checked to make sure it exists.
//
XmlTextReader rcfReader = new XmlTextReader( rcfStream );
while( true == rcfReader.Read() && false == rcfReader.EOF )
{
if( rcfReader.Name.Equals( "operator" ) && rcfReader.NamespaceURI.Equals( UDDI.Replication.Constants.Namespace ) )
{
//
// For each operator node, we want the following information. These are all
// mandatory elements, so if any were missing we should not have passed schema
// validation.
//
// operatorNodeID (this is the operatorKey)
// operatorStatus
// soapReplicationUrl
// certificate
// operatorCustodyName (operatorName)
//
//
// Note that contacts are currently being ignored. This is because we are not sending
// the businessKey for the operator. Since we store contacts based on businessKey (actually businessID)
// we do not have a way of storing contacts. IN 70 says that the operatorNodeID should actually be this
// businessKey, so once we decide to implement this, we'll process contacts.
//
X509Certificate certificate = null;
string operatorKey = null;
string operatorName = null;
string soapReplicationUrl = null;
int operatorStatus = 0;
string localOperatorKey = Config.GetString( "OperatorKey" ).ToLower();
do
{
switch( rcfReader.Name )
{
case "operatorNodeID":
{
operatorKey = rcfReader.ReadElementString();
break;
}
case "operatorCustodyName":
{
operatorName = rcfReader.ReadElementString();
break;
}
case "operatorStatus":
{
operatorStatus = OperatorStatus2ID( rcfReader.ReadElementString() );
break;
}
case "soapReplicationURL":
{
soapReplicationUrl = rcfReader.ReadElementString();
break;
}
case "certificate":
{
//
// Read our data in 1024 byte chunks. Keep an array list of these
// chunks.
//
int bytesRead = 0;
int chunkSize = 1024;
ArrayList chunks = new ArrayList();
do
{
byte[] data = new byte[ chunkSize ];
bytesRead = rcfReader.ReadBase64( data, 0, chunkSize );
if( bytesRead > 0 )
{
chunks.Add( data );
}
}while( bytesRead != 0 );
//
// Allocate a buffer to hold all of our chunks.
//
byte[] certificateData = new byte[ chunks.Count * chunkSize ];
//
// Copy each chunk into our buffer. This buffer is our certificate.
//
int index = 0;
foreach( byte[] chunkData in chunks )
{
Array.Copy( chunkData, 0, certificateData, index, chunkData.Length );
index += chunkData.Length;
}
//
// Create a certificate from our byte data.
//
certificate = new X509Certificate( certificateData );
break;
}
}
}while( true == rcfReader.Read() && false == rcfReader.EOF && false == rcfReader.Name.Equals( "operator" ) );
//
// Make sure we identify the local operator.
//
if( false == operatorKey.ToLower().Equals( localOperatorKey ) )
{
//
// Import this operator
//
SaveOperatorInfo( operatorKey, operatorName, soapReplicationUrl, operatorStatus, certificate );
Console.WriteLine( "Successfully imported {0}.", operatorName );
}
else
{
SaveOperatorInfo( null, operatorName, soapReplicationUrl, operatorStatus, certificate );
Console.WriteLine( "Successfully update the local operator." );
}
}
}
}
catch( XmlException xmlException )
{
Console.WriteLine( "Exception processing the RCF: " );
Console.WriteLine( "\t" );
Console.WriteLine( xmlException.ToString() );
}
catch( XmlSchemaException schemaException )
{
Console.WriteLine( "The RCF did not pass schema validation: " );
Console.WriteLine( "\t" );
Console.WriteLine( schemaException.ToString() );
}
finally
{
rcfStream.Close();
}
}
public static void ExportOperatorCertificate()
{
if( null == operatorKey )
{
operatorKey = Config.GetString( "OperatorKey" );
}
if( File.Exists( filename ) && !overwrite )
{
Console.Write( "Overwrite '{0}' [y/n]? ", filename );
int choice = Console.Read();
if( 'y' != (char)choice && 'Y' != (char)choice )
{
Console.WriteLine();
Console.WriteLine( "Operation aborted." );
return;
}
}
byte[] data = null;
//
// Retrieve the certificate.
//
SqlStoredProcedureAccessor sp = new SqlStoredProcedureAccessor();
sp.ProcedureName = "net_operator_get";
sp.Parameters.Add( "@operatorKey", SqlDbType.UniqueIdentifier );
sp.Parameters.SetGuidFromString( "@operatorKey", operatorKey );
SqlDataReaderAccessor reader = sp.ExecuteReader();
try
{
if( reader.Read() )
data = reader.GetBinary( "certificate" );
}
finally
{
reader.Close();
}
FileStream file = File.Open( filename, FileMode.Create, FileAccess.Write, FileShare.None );
try
{
int filesize = (int)data.Length;
file.Write( data, 0, filesize );
Console.WriteLine( "Wrote {0} byte(s) to certificate file '{1}'.\r\nSource: {{{2}}}",
filesize,
filename,
operatorKey );
}
finally
{
file.Close();
}
}
private static int OperatorStatus2ID( string status )
{
//
// These values must match the values in UDO_operatorStatus
//
int id = 2;
switch( status )
{
case "disable":
{
id = 0;
break;
}
case "new":
{
id = 1;
break;
}
case "normal":
{
id = 2;
break;
}
case "resigned":
{
id = 3;
break;
}
}
return id;
}
private static void SaveOperatorInfo( string operatorKey, X509Certificate certificate)
{
//
// Default operator status to 2 (normal)
//
SaveOperatorInfo( operatorKey, operatorKey, null, 2, certificate );
}
private static void SaveOperatorInfo( string operatorKey,
string operatorName,
string soapReplicationUrl,
int operatorStatus,
X509Certificate certificate)
{
byte[] data = certificate.GetRawCertData();
ConnectionManager.BeginTransaction();
SqlStoredProcedureAccessor sp = new SqlStoredProcedureAccessor();
sp.ProcedureName = "net_operator_save";
if( null == operatorKey )
{
//
// Import a certificate for the local operator
//
operatorKey = Config.GetString( "OperatorKey" );
sp.Parameters.Add( "@operatorKey", SqlDbType.UniqueIdentifier );
sp.Parameters.Add( "@certSerialNo", SqlDbType.NVarChar, UDDI.Constants.Lengths.CertSerialNo );
sp.Parameters.Add( "@certIssuer", SqlDbType.NVarChar, UDDI.Constants.Lengths.CertIssuer );
sp.Parameters.Add( "@certSubject", SqlDbType.NVarChar, UDDI.Constants.Lengths.CertSubject );
sp.Parameters.Add( "@certificate", SqlDbType.Image );
sp.Parameters.SetGuidFromString( "@operatorKey", operatorKey );
sp.Parameters.SetString( "@certSerialNo", certificate.GetSerialNumberString() );
sp.Parameters.SetString( "@certIssuer", certificate.GetIssuerName() );
sp.Parameters.SetString( "@certSubject", certificate.GetName() );
sp.Parameters.SetBinary( "@certificate", data );
sp.ExecuteNonQuery();
}
else
{
//
// Create a new operator / publisher and import certificate
//
//
// First add a publisher for the new operator
//
SqlStoredProcedureAccessor sp2 = new SqlStoredProcedureAccessor();
sp2.ProcedureName = "UI_savePublisher";
sp2.Parameters.Add( "@PUID", SqlDbType.NVarChar, UDDI.Constants.Lengths.UserID );
sp2.Parameters.Add( "@name", SqlDbType.NVarChar, UDDI.Constants.Lengths.Name );
sp2.Parameters.Add( "@email", SqlDbType.NVarChar, UDDI.Constants.Lengths.Email );
//
// TODO: use UDDI.Constants.Lengths.Phone when the UI_savePublisher is fixed
//
sp2.Parameters.Add( "@phone", SqlDbType.VarChar, 20 );
sp2.Parameters.SetString( "@PUID", operatorKey );
sp2.Parameters.SetString( "@name", operatorKey );
sp2.Parameters.SetString( "@email", "" );
sp2.Parameters.SetString( "@phone", "" );
sp2.ExecuteNonQuery();
//
// Add the new operator and link it to the new publisher
//
sp.Parameters.Add( "@operatorKey", SqlDbType.UniqueIdentifier );
sp.Parameters.Add( "@operatorStatusID", SqlDbType.Int );
sp.Parameters.Add( "@PUID", SqlDbType.NVarChar, UDDI.Constants.Lengths.UserID );
sp.Parameters.Add( "@name", SqlDbType.NVarChar, UDDI.Constants.Lengths.Name );
sp.Parameters.Add( "@soapReplicationURL", SqlDbType.NVarChar, UDDI.Constants.Lengths.SoapReplicationURL );
sp.Parameters.Add( "@certSerialNo", SqlDbType.NVarChar, UDDI.Constants.Lengths.CertSerialNo );
sp.Parameters.Add( "@certIssuer", SqlDbType.NVarChar, UDDI.Constants.Lengths.CertIssuer );
sp.Parameters.Add( "@certSubject", SqlDbType.NVarChar, UDDI.Constants.Lengths.CertSubject );
sp.Parameters.Add( "@certificate", SqlDbType.Image );
sp.Parameters.SetGuidFromString( "@operatorKey", operatorKey );
sp.Parameters.SetInt( "@operatorStatusID", operatorStatus );
sp.Parameters.SetString( "@PUID", operatorKey );
sp.Parameters.SetString( "@name", operatorName );
sp.Parameters.SetString( "@soapReplicationURL", soapReplicationUrl );
sp.Parameters.SetString( "@certSerialNo", certificate.GetSerialNumberString() );
sp.Parameters.SetString( "@certIssuer", certificate.GetIssuerName() );
sp.Parameters.SetString( "@certSubject", certificate.GetName() );
sp.Parameters.SetBinary( "@certificate", data );
sp.ExecuteNonQuery();
}
ConnectionManager.Commit();
Console.WriteLine( "Wrote {0} byte(s) to operator key {{{1}}}.\r\nSource: '{2}'.",
data.Length,
operatorKey,
filename );
}
/// ****************************************************************
/// public Main [static]
/// ----------------------------------------------------------------
///
/// Program entry point.
///
/// ----------------------------------------------------------------
///
/// Command-line arguments.
///
/// ****************************************************************
///
public static void Main( string[] args )
{
try
{
ConnectionManager.Open( true, false );
Debug.VerifySetting( "OperatorKey" );
Console.WriteLine( "Microsoft (R) UDDI Replication Configuration Utility." );
Console.WriteLine( "Copyright (C) Microsoft Corp. 2002. All rights reserved." );
Console.WriteLine();
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal( identity );
Context.User.SetRole( principal );
if( !Context.User.IsAdministrator )
{
Console.WriteLine( "Access denied.\r\n\r\nThis program must be executed by a member of the '"
+ Config.GetString( "GroupName.Administrators" ) + "'\r\ngroup. The current user '"
+ identity.Name + "' is not a member of this group." );
return;
}
ProcessCommandLine( args );
switch( mode )
{
case ModeType.ImportOperatorCertificate:
ImportOperatorCertificate();
break;
case ModeType.ExportOperatorCertificate:
ExportOperatorCertificate();
break;
case ModeType.ImportRCF:
ImportRCF();
break;
default:
throw new CommandLineException( "" );
}
}
catch( CommandLineException e )
{
//
// Display command-line help.
//
Console.WriteLine( "Syntax:" );
Console.WriteLine( " " + executable + " [parameters]" );
Console.WriteLine();
Console.WriteLine( "Options:" );
Console.WriteLine( " -help Displays this help message." );
Console.WriteLine( " -i Import a certificate. If the -o option is not" );
Console.WriteLine( " used, the certificate is imported for the" );
Console.WriteLine( " local operator." );
Console.WriteLine( " -e Export a certificate. If the -o option is not" );
Console.WriteLine( " used, the certificate is exported from the" );
Console.WriteLine( " local operator." );
Console.WriteLine( " -o The operator key to import/export a certificate" );
Console.WriteLine( " Omit this parameter to import a certificate for the" );
Console.WriteLine( " local operator." );
Console.WriteLine( " -y Supress file overwrite prompt." );
Console.WriteLine( " -r Path to replication configuration file (RCF)." );
Console.WriteLine();
Console.WriteLine( "Examples:" );
Console.WriteLine( " " + executable + " -help" );
Console.WriteLine( " " + executable + " -o FF735874-28BD-41A0-96F3-02113FFD9D6C -i uddi.cer" );
Console.WriteLine( " " + executable + " -i uddi.cer" );
Console.WriteLine( " " + executable + " -r operators.xml" );
Console.WriteLine();
if( 0 != e.Message.Length )
Console.WriteLine( e.Message );
return;
}
catch( Exception e )
{
Console.WriteLine( e.ToString() );
return;
}
}
}
/// ****************************************************************
/// public class CommandLineException
/// ----------------------------------------------------------------
///
/// Exception class for errors encountered while parsing the
/// command-line.
///
/// ****************************************************************
///
public class CommandLineException : ApplicationException
{
/// ************************************************************
/// public CommandLineException [constructor]
/// ------------------------------------------------------------
///
/// CommandLineException constructor.
///
/// ************************************************************
///
public CommandLineException()
: base()
{
}
/// ************************************************************
/// public CommandLineException [constructor]
/// ------------------------------------------------------------
///
/// CommandLineException constructor.
///
/// ------------------------------------------------------------
///
/// Exception message.
///
/// ************************************************************
///
public CommandLineException( string message )
: base( message )
{
}
}
}