Feb 28, 2007

.Net 2.0 Sending Email & MSMQ

.net 2.0 下发送邮件的方式

ref:
http://www.alixixi.com/Dev/Web/ASPNET/aspnet9/2007/200702069867.html

在.Net Framework 1.x 我们需要使用 System.Web.Mail 命名空间下的类 来进行发送邮件,但是功能比较弱,比如你的邮件服务器需要验证才能发送邮件,在.net 1.1 中,需要用下面的代码来做额外配置。
mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1");
mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", "my_username_here");
mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", "super_secret");

.net 1.x 下发送邮件的方式请参考:
http://blog.joycode.com/joy/archive/2004/01/14/11405.aspx

.Net Framework 2.0 下,在 System.Net.Mail 命名空间中提供了对邮件操作的支持,他的功能更强大。比如你的邮件服务器需要验证才能发送邮件,代码就只需简单成如下:

public static void SendSMTPEMail (string strSmtpServer, string strFrom, string strFromPass, string strto, string strSubject, string strBody)
{
System.Net.Mail.SmtpClient client = new SmtpClient(strSmtpServer);
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential(strFrom, strFromPass);
client.DeliveryMethod = SmtpDeliveryMethod.Network;
System.Net.Mail.MailMessage message = new MailMessage(strFrom, strto, strSubject, strBody);
message.BodyEncoding = System.Text.Encoding.UTF8;
message.IsBodyHtml = true;
client.Send(message);
}


我们可以通过修改 UseDefaultCredentials Credentials DeliveryMethod 等属性,方便的支持各种情况下发送邮件的方式。


一个发邮件的例子,涉及MSMQ,RSA,JMAIL
ref:
http://www.alixixi.com/Dev/Web/ASPNET/aspnet9/2007/200702069869.html




public void sendMSMQEmail()
{
RSACryptoServiceProvider crypt=new RSACryptoServiceProvider();
string publickey=crypt.ToXmlString(false);//(公钥)
string privatekey=crypt.ToXmlString(true);
crypt.Clear();
StreamWriter one=new StreamWriter(@"c:\a.txt",true,UTF8Encoding.UTF8);
one.Write(publickey);
StreamWriter two=new StreamWriter(@"c:\b.txt",true,UTF8Encoding.UTF8);
two.Write(privatekey);
one.Flush();
two.Flush();
one.Close();
two.Close();
Console.WriteLine("成功保存公匙和密匙!");

//2.对信息加密,然后用通过队列发送信息

string from=TextBoxFrom.Text+DropDownList2.SelectedValue;
string sub=textBoxSub.Text;
string bodys=TextBoxBody.Text;
string pwd=TextBoxPwd.Text;


StreamReader sr = new StreamReader(@"c:\a.txt",UTF8Encoding.UTF8);
string readpublickey = sr.ReadToEnd();
sr.Close();

RSACryptoServiceProvider crypt=new RSACryptoServiceProvider();
UTF8Encoding enc=new UTF8Encoding();
byte[] bytes=enc.GetBytes(pwd);
crypt.FromXmlString( readpublickey );//读取公钥
bytes = crypt.Encrypt( bytes,false ); //进行加密
string encryttext=Convert.ToBase64String(bytes); //转码

MailerInfo mf=new MailerInfo();
//mf.body=bodys;
mf.Body=bodys;
mf.From=from;
mf.Fromname=TextBoxFrom.Text;
mf.Password=encryttext;
mf.Sub=sub;


//CreateQueue(".\\myQueue");

SendMessage(mf);

}

public static void CreateQueue(string queuePath)
{
try
{
if(!MessageQueue.Exists(queuePath))
{
MessageQueue.Create(@".\private$\myQueue");
}
else
{
Console.WriteLine(queuePath + " already exists.");
}
}
catch (MessageQueueException e)
{
Console.WriteLine(e.Message);
}
}


public void SendMessage(MailerInfo mf)
{
try
{
MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");

System.Messaging.Message myMessage = new System.Messaging.Message(mf);
myQueue.Send(myMessage);
}
catch(ArgumentException e)
{
Console.WriteLine(e.Message);
}

return;
}

public void ReceiveMessage()
{

MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
myQueue.Formatter = new XmlMessageFormatter(new Type[]{typeof(MessageRec.MailerInfo)});
try
{
System.Messaging.Message myMessage = myQueue.Receive();
MailerInfo mf = (MailerInfo)myMessage.Body;
//解码

StreamReader sr = new StreamReader(@"c:\b.txt",UTF8Encoding.UTF8);
string readprivatekey = sr.ReadToEnd();
sr.Close();

RSACryptoServiceProvider crypt=new RSACryptoServiceProvider();
UTF8Encoding enc=new UTF8Encoding();
byte[] bytes = Convert.FromBase64String(mf.password);
crypt.FromXmlString ( readprivatekey ) ;
byte[] decryptbyte = crypt.Decrypt( bytes,false );
password=enc.GetString( decryptbyte );
from=mf.from;
fromname=mf.Fromname;
sub=mf.sub;
body=mf.body;
to="dankes@163.com";
}
catch (MessageQueueException)
{
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
}

//发送邮件

jmail.Message Jmail=new jmail.Message();
Jmail.Silent=false;
Jmail.Logging=true;
Jmail.Charset="GB2312";
Jmail.ContentType="text/html";
Jmail.AddRecipient(to,"","");
Jmail.From=from;
Jmail.MailServerUserName=fromname;
Jmail.MailServerPassWord=password;
Jmail.Subject=sub;
Jmail.Body=body;
string smtp="smtp.163.com";
if(from.EndsWith("tom.com"))
{
smtp="smtp.tom.com";
}
else if(from.EndsWith("21cn.com"))
{
smtp="smtp.21cn.com";
}
else if(from.EndsWith("sina.com"))
{
smtp="smtp.sina.com";
}
else if(from.EndsWith("263.com"))
{
smtp="smtp.263.com";
}
//开始发送邮件
int i=0;
try
{
Jmail.Send(smtp,false);
}
catch(Exception ee)
{
i=1;
}
Jmail.Close() ;

if(i==0)
Console.WriteLine("邮件发送成功"+"发送人:"+from+"接收方:"+to+"主题是:"+sub);
if(i==1)
Console.WriteLine("登陆失败,或者网络故障");


}



Queue MSMQ Messages from SQL Server
ref: http://www.15seconds.com/issue/040902.htm

download source code
(mirror)

If you've ever found yourself scratching your head, wondering how to queue a message from MS SQL Server, then you might find yourself writing your own plumbing. (Microsoft will most certainly be dealing with this boggle in future version). And I don't know how your boss operates, but I usually need it done like -- yesterday.

Using some of my, "Nothing is impossible with Visual Studio .NET" theory, we will create a console application to queue a message in Microsoft Message Queuing (MSMQ). Using the magic of an extended stored procedure, we can call our console application from an MS SQL trigger. We create our table in Enterprise Manager and then use the trigger to call our console application. You could also call a COM object for this example, but I choose a console application because calling in SQL syntax is straight forward.

Message queues are very cool because you can queue a message to a queue server, and it will remain there waiting to be processed. There are systems in place to make sure that message gets to where it belongs. If for some reason the message doesn't arrive, it will be sent again until it arrives at the intended destination. So for valuable mail-like checks from the lotto commission -- we want to ensure those messages get to where they need to go. So, what we want to do is build a process to check the queue and then process the message in it.

Message queues are very underutilized. But Visual Studio .NET makes the process of creating and using this technology much simpler. You can configure queues on your machine by right clicking on My Computer and then selecting Manage. Under the Services and Applications node you can find the "Message Queuing Node". Note: if you don't see a "Messaging Queuing Node" then you will need to install it from your windows CD.

To tell you the truth, it actually makes me feel warm inside when I fire up VisualStudio .NET. It is also moments like these that I know why Microsoft added the Win32 Console Application project type. It's a command shell application and looks like an MS-DOS window. (We'll never be without it). And since we have access to the .NET Framework from within our console application, we can use the Messaging classes to communicate with the queue server.

Installing a queue server is pretty straightforward, but I've found it can be difficult to write to Public queues from a Windows 2000 client if you are on an Active Directory (AD) network-that is, unless you have the AD installed on the domain controller. (The look on the network guy's face when you talk about touching the AD is priceless). We're fortunate that Windows XP and Microsoft Windows Server 2003 present no issues with writing to queues on other servers. If you are using Windows 2000, you can use Private queues pretty easily. You can certainly use Public Queues with a 2000 client, but there may be some more footwork involved.

On the SQL side, we've set-up a trigger within our table of e-mail addresses. Upon updating a record, it will get the ID of the changed record and then call the console application by using xp_cmdshell. xp_cmdshell is an extended stored procedure. This is a special DLL written in Microsoft Visual C++ .NET. It is comes standard with MSSQL Server. In SP3 for MSSQL Server you need to give the user calling this stored procedure rights to call console applications. For the purpose of this application, I made the SQL user a sysadmin. This will call our application and pass the updated ID of the record that we want to work with.


CREATE TRIGGER GO ON [dbo].[email]
FOR UPDATE
AS

DECLARE @cmd sysname
DECLARE @ID int
Select @ID = ID from inserted

SET @cmd = 'c:\temp\queuethunder.exe ' + cast(@ID as varchar(10))
EXEC master..xp_cmdshell @cmd

When creating a console application in Visual Studio, the Sub Main routine is the start-up code for the application. It assesses the parameters passed to it in the args parameter. (These are actual command line parameters passed to the application). This code then looks at the ID of the record we passed from our SQL trigger, opens up a queue, and then inserts into the queue (as an object) the ID only.


Sub Main(ByVal args() As String)
'Dim sQueuePath As String = "your_machine_name\private$\happy"
'Dim sQueuePath As String = "your_machine_name\happy"
Dim sQueuePath As String = "Calvinlapxp4\happy"
Dim myEnumerator As System.Messaging.MessageEnumerator
Dim myQueue As New System.Messaging.MessageQueue(sQueuePath)
Dim oHappyQueue As System.Messaging.MessageQueue = GetQueue(sQueuePath)
Dim oObject As String

Try
oObject = args(0)

'Send id to queue
oHappyQueue.Send(oObject)

oHappyQueue.Close()

Catch ex As Exception
Console.WriteLine("System Error " + ex.Message)
Console.WriteLine("System Error " + ex.Source)
Console.WriteLine("Sub-System Error " + ex.InnerException.Message)

End Try



oHappyQueue = Nothing
End Sub
On the server side, we pick things back up in the queue to validate the e-mail addresses. First we'll need to create another console application and call it ProcessQueue. The Sub Main code will open the queue, cycle through each message, and then process it. Because the messages are queued, our SQL process runs asynchronously with this process. This means that SQL will be finished processing when the validate e-mail function is concurrently being processed in a different process.

Dim sQueuePath As String = "Calvinlapxp4\happy"
Dim myEnumerator As System.Messaging.MessageEnumerator
Dim myQueue As New System.Messaging.MessageQueue(sQueuePath)
Dim sResult As String
Dim oProjectThundercom As New com.projectthunder.www.ValidateEmail
Dim oData As New DataAccessLayer.DataAccess.SQLServer("Server",
"database", "username", "password")

'Get enumerator so we can go through the queue
myEnumerator = myQueue.GetMessageEnumerator()

'we are going through each message so we can so we can get the value
from the message then want to remove it so it isn't processed again
While myEnumerator.MoveNext()

'resulting id from e-mail record
sResult = StringFromMessage(myEnumerator.Current)
Dim semail As String

'go get email address from record
Dim ods As DataSet = oData.runSQLDataSet("select * from email
where id = '" + sResult + "'")
If ods.Tables(0).Rows.Count > 0 Then
semail = ods.Tables(0).Rows(0).Item("email")

Else
Console.WriteLine("Record not found " + sResult)
End If

'Going out to get the result of testing the e-mail address
Dim statusresult As String =
oProjectThundercom.ChatMailServer(semail)

'update the record with result
oData.runSQLDataSet("update email set emailstatus = '" +
statusresult + "' where id = '" + sResult + "'")

'remove the message from the queue so we don't process it again
myEnumerator.RemoveCurrent()

ods = Nothing

End While
myQueue.Close()
myQueue = Nothing
oProjectThundercom = Nothing
oData = Nothing

Catch ex As Exception
Console.WriteLine("System Error Message " + ex.Message)
Console.WriteLine("System Error Source " + ex.Source)
Console.WriteLine("System Error Trace " + ex.StackTrace)
Console.WriteLine("Sub-System Error " + ex.InnerException.Message)


End Try
You can use this process to trigger off the insertion of new records that you want to be part of a calculation once the transaction is done. You may trigger off a new order record, but until the order details are written, you can't get a total. Or, you may trigger updates to other databases, instead of using SQL Server Link Server. In MSSQL you can link a different server then access the tables and data from that server by prefixing the object with the server name. You can also call the queue process from a Stored Procedure as part of a business process, such as sending a fax or an e-mail update to a manager. Sometimes doing simple tasks from the DBMS layer can notify issues or take care of important rules regardless of how the data was updated.

Feb 24, 2007

Create a self-signed SSL certificate with IIS 6.0 Resource Kit SelfSSL

ref: http://www.somacon.com/p42.php

Create a self-signed SSL certificate with IIS 6.0 Resource Kit SelfSSL

The IIS 6.0 Resource Kit version 1.0 was released 5/30/2003. It contains a utility called SelfSSL.exe for instantly creating and installing a self-signed testing certificate into IIS. The resource kit is freely downloadable from the Microsoft website. Although the tool is intended for IIS 6.0, it works just as well on IIS 5.1. It is so simple to use that no instructions are required beyond the pointer to the download.

  • Download IIS 6.0 Resource Kit Tools
  • Install the resource kit (requires Windows Server 2003, Windows XP)
  • From the Windows Start Menu, go to the "\Programs\IIS Resources\SelfSSL" folder and select "SelfSSL".
  • Instructions will be listed in a command prompt. Type "selfssl" to run the program.
  • Type "y" to confirm overriding/installing the certificate on the given site.
  • Test that it worked by visiting https://localhost/.

If you want hand-holding through these steps, read these instructions with screenshots by Jonathan Maltz.

Microsoft has also provided Microsoft Internet Information Services 6.0 Resource Kit the book for download via their website in whole or chapter.


Feb 12, 2007

IBM JDK 不兼容 Sun JDK 备忘录

reference: http://www.blogjava.net/chelsea/archive/2006/02/22/32011.html


工作中遇到一些Sun JDK与IBM JDK不兼容的问题,记下来做个备忘

1. String.getBytes(String encoding)

TestCase:
public static void main(String[] args) {
try {
RandomAccessFile f = new RandomAccessFile("test.txt", "rw");
f.write("tets".getBytes(null));
f.close();
} catch (IOException e) {
e.printStackTrace();
}
}
SUN JDK 1.4 : OK

IBM JDK 1.4 :

Exception in thread "main" java.lang.NullPointerException
at java.lang.StringCoding$EncoderCache.makeEncoder(StringCoding.java:198)
at java.lang.StringCoding$2.run(StringCoding.java:251)
at java.security.AccessController.doPrivileged1(Native Method)
at java.security.AccessController.doPrivileged(AccessController.java:351)
at java.lang.StringCoding$EncoderCache.getEncoder(StringCoding.java:248)
at java.lang.StringCoding.getEncoder(StringCoding.java:331)
at java.lang.StringCoding.encode(StringCoding.java:472)
at java.lang.String.getBytes(String.java:947)
at NullEncoding.main(NullEncoding.java:12)

2. 日文编码

Test Case:
public static void main(String[] args) {
try {
"".getBytes("Shift_JIS");
"".getBytes("Shift-JIS");
} catch (Exception e) {
e.printStackTrace();
}
}

SUN JDK 1.4 : OK

IBM JDK 1.4 :

java.io.UnsupportedEncodingException: Shift-JIS
at java.lang.StringCoding$EncoderCache.getEncoder(StringCoding.java:262)
at java.lang.StringCoding.getEncoder(StringCoding.java:331)
at java.lang.StringCoding.encode(StringCoding.java:472)
at java.lang.String.getBytes(String.java:947)
at ShiftEncoding.main(ShiftEncoding.java:17)

Oct 24, 2006

Uncide code in ASP & MSSQL

ref: http://support.jodohost.com/showthread.php?t=2356

Unicode has two encoding formats: UTF-8 and UCS-2. Web browsers speak UTF-8. On the other hand, most DBMSes speak UCS-2. In MS SQL 2000 Server, searching and string comparison work robustly if Unicode data were stored only in UCS-8 format. However, Internet Explorer can display Unicode data only in UTF-8.

Thus, the requirement is to convert between UTF-8 and UCS-2. Fortunately, in Microsoft-based web application, IIS can take responsibility of Unicode format conversion by adding "<% Session.Codepage=65001 %>" or "<%@ CodePage=65001 %>" to the server-side ASP script responsible for database manipulation.

eg.
<%@ Language=VBScript CODEPAGE=65001 %>
<%Session.Codepage=65001 %>

For more details, please visit
INF: Storing UTF-8 Data in SQL Server
http://support.microsoft.com/default...&Product=sql2k


If your application uses Active Server Pages (ASP) and you are using Internet Information Server (IIS) 5.0 and Microsoft Windows 2000, you can add "<% Session.Codepage=65001 %>" to your server-side ASP script. This instructs IIS to convert all dynamically generated strings (example: Response.Write) from UCS-2 to UTF-8 automatically before sending them to the client.

If you do not want to enable sessions, you can alternatively use the server-side directive "<%@ CodePage=65001 %>".

Any UTF-8 data sent from the client to the server via GET or POST is also converted to UCS-2 automatically. The Session.Codepage property is the recommended method to handle UTF-8 data within a web application. This Codepage setting is not available on IIS 4.0 and Windows NT 4.0. For additional information, see the following article in the Microsoft Knowledge Base:



In query statement, should use
select * from [TABLE] where [FIELDS] = N'[SEARCH_TEXT]'


Stored Procedures should be use adVarWChar instead of adVarChar

附:
'---- DataTypeEnum Values ----
Const adEmpty = 0
Const adTinyInt = 16
Const adSmallInt = 2
Const adInteger = 3
Const adBigInt = 20
Const adUnsignedTinyInt = 17
Const adUnsignedSmallInt = 18
Const adUnsignedInt = 19
Const adUnsignedBigInt = 21
Const adSingle = 4
Const adDouble = 5
Const adCurrency = 6
Const adDecimal = 14
Const adNumeric = 131
Const adBoolean = 11
Const adError = 10
Const adUserDefined = 132
Const adVariant = 12
Const adIDispatch = 9
Const adIUnknown = 13
Const adGUID = 72
Const adDate = 7
Const adDBDate = 133
Const adDBTime = 134
Const adDBTimeStamp = 135
Const adBSTR = 8
Const adChar = 129
Const adVarChar = 200
Const adLongVarChar = 201
Const adWChar = 130
Const adVarWChar = 202
Const adLongVarWChar = 203
Const adBinary = 128
Const adVarBinary = 204
Const adLongVarBinary = 205
Const adChapter = 136
Const adFileTime = 64
Const adDBFileTime = 137
Const adPropVariant = 138
Const adVarNumeric = 139


===========================================
After import data from Text file to database, NText is used
using
cast([FIELD_NAME] as nvarchar(%SIZE%))
to convert the fileds into nvarchar

Sep 26, 2006

after installed IBM RAD 6, WAS 6 failed to install

Reference: http://www.marksu.idv.tw/categories/ibm/rational/

又學了一招
Posted in IBM, Rational, Rational Software Architect by 馬克ㄙㄨ on the January 9th, 2006

由於上個週末將電腦重灌的關係
所以陸陸續續也要把相關的軟體灌好

不幸的是
在我重新安裝 RSA 的時候,就那麼剛好給我當機
結果在重新開機之後
雖然 RSA 可以順利的安裝好,但是 RSA 內附的 WAS6 測試環境就是怎麼樣也裝不起來
查看了一下 log
log 裡面說
“請指定另一個目錄或執行手動解除安裝來移除所有套件,之後,再重新安裝到相同目錄。”
找遍了 Windows 上面的 registry 和 temp 檔,就是找不到 WAS6 安裝過的蹤跡。
這個時候只好來找找 IBM WAS6 的 infocenter 了….雖然不是抱著很高的期望….
結果在 infocenter 的搜尋功能上面輸入了 “ISMPWarningDialogAction, wrn”
居然跳出了下面這個頁面
在安裝和建立設定檔時所發出的訊息
接著找到
指定另一個目錄或者以手動方式解除安裝
想不到,居然會有底下這個….

# 編輯 vpd.properties 檔。
這個檔案是位於作業系統的安裝目錄內,例如 C:\WINNT 目錄或 C:\windows 目錄。
請勿刪除 vpd.properties 檔案或將其更名,因為 InstallShield for MultiPlatforms (ISMP) 程式會在它安裝的其他產品使用這個檔案。
如果您要解除安裝的 WebSphere Application Server 產品,是在 vpd.properties 檔中有項目存在的唯一產品,您就可以刪除這個檔案。

看到這個內容
我小心翼翼的將 vpd.properties 裡面,有 base_v6 字樣的那幾行都給移除掉
最後再到 RSA 安裝光碟的第四片底下
在 \external\was6 這個路徑下執行

“f:\rsa6\install\disk4\external\was6\rat_wasinstall.exe”
-silent -options “f:\rsa6\install\disk4\external\was6\rat_responsefile.txt”
-P wasProductBean.installLocation=”D:\IBM\Rational\SDP\6.0\runtimes\base_v6″

這段字串,大家可以在 RSA 的安裝路徑下,找到 log 這個資料夾
應該會看到有一個 was_v6_out.log
打開之後應該可以找的到長的差不多的一串文字
然後在 DOS 下執行就成了