inohilog

/var/log/inohiro.log

Post to Hatena-Diary / Hatena-Fotolife with Atom Publishing Protocol

Post to Hatena-Diary / Hatena-Fotolife with Atom Publishing Protocol
// AtomAPI での投稿は既に「はてなダイアリーにAtomPubで投稿する - INOHILOG」で実現していたんですが、今回ははてなfotolifeにAtomPubで投稿です。
とりあえず今回はC#で出来ておしまいでは無いので、説明はいつか書く。
このコードでアップロードされた画像 -> f:id:InoHiro:20090404041804p:image - InoHiro's fotolife

using System;
using System.Text;
using System.Net;
using System.IO;
using System.Security.Cryptography;

namespace HatenaFotolife_AtomPub
{
	class Program
	{
		static void Main( string[] args )
		{
			string username = "InoHiro";
			string password = "password";
			string title = "img_title";
			string imgDataPath = @"C:\titleimg.png";
			string folderName = "Development";
			string generator = "Console_Uploader";

			Console.WriteLine( Post( username, password, title, imgDataPath, folderName, generator ) );
			Console.ReadKey();
		}

		private static string Post( string username, string password, string title, string imgDataPath, string folderName, string generator )
		{
			string header = CreateWsseHeader( username, password );

                        // Base64 Encoding
			FileStream fileStm = new FileStream( imgDataPath, FileMode.Open, FileAccess.Read );
			byte[] bytes = new byte[fileStm.Length];
			int readBytes = fileStm.Read( bytes, 0, ( int )fileStm.Length );
			fileStm.Close();
			string encodedImgFile = System.Convert.ToBase64String( bytes );

			StringBuilder xml = new StringBuilder();
			xml.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" );
			xml.Append( "<entry xmlns=\"http://purl.org/atom/ns#\">" );
			xml.Append( String.Format( "<title>{0}</title>", title ) );
			xml.Append( String.Format( "<content type=\"image/png\" mode=\"base64\">{0}</content>", encodedImgFile ) );
			xml.Append( String.Format( "<dc:subject>{0}</dc:subject>", folderName ) );
			xml.Append( String.Format( "<generator>{0}</generator></entry>", generator ) );

			HttpWebRequest request = ( HttpWebRequest )WebRequest.Create( "http://f.hatena.ne.jp/atom/post" );
			request.Method = "POST";
			request.Headers.Add( "X-WSSE", header );
			request.ContentType = "application/x.atom+xml";

			Stream requestream = request.GetRequestStream();
			byte[] data = Encoding.UTF8.GetBytes( xml.ToString() );
			requestream.Write( data, 0, data.Length );
			requestream.Close();

			HttpWebResponse response = ( HttpWebResponse )request.GetResponse();
			if( response.StatusCode == HttpStatusCode.Created )
				return "created!";
			else
				return "failed...";
		}

		static string CreateWsseHeader( string username, string password )
		{
			// HTTPリクエスト毎に生成するセキュリティ・トークン(ランダム文字列)
			byte[] b_nonce = new byte[8];
			Random rand = new Random();
			rand.NextBytes( b_nonce );

			// nonce 生成時の日時
			string created = DateTime.Now.ToUniversalTime().ToString( "o" );
			byte[] b_created = Encoding.UTF8.GetBytes( created );

			byte[] b_password = Encoding.UTF8.GetBytes( password );
			SHA1Managed sh1 = new SHA1Managed();
			sh1.Initialize();

			// Nonce, Created, パスワードを連結
			byte[] origin = new byte[b_nonce.Length + b_created.Length + b_password.Length];
			Array.Copy( b_nonce, 0, origin, 0, b_nonce.Length );
			Array.Copy( b_created, 0, origin, b_nonce.Length, b_created.Length );
			Array.Copy( b_password, 0, origin, b_nonce.Length + b_created.Length, b_password.Length );

			// ハッシュ値を生成
			byte[] passwordDigest = sh1.ComputeHash( origin );

			string header = string.Format(
				"UsernameToken Username=\"{0}\", PasswordDigest=\"{1}\", Nonce=\"{2}\", Created=\"{3}\"",
				username, Convert.ToBase64String( passwordDigest ), Convert.ToBase64String( b_nonce ), created );

			return header;
		}
	}
}