http://sourceforge.jp/projects/futonwriter/の旧リポジトリ
| Revision | f5bf5f659df1a3d169c28cb5f9aeaae696a5fcc9 (tree) |
|---|---|
| Time | 2011-05-10 19:03:33 |
| Author | azyobuzin <azyobuzin@user...> |
| Commiter | azyobuzin |
フォトライフへの投稿に成功。疲れた。WsseAtomConnectionの試行錯誤の結果。
TODO:いろいろ
| @@ -75,7 +75,9 @@ | ||
| 75 | 75 | <SubType>Code</SubType> |
| 76 | 76 | </Compile> |
| 77 | 77 | <Compile Include="Models\Hatena\BlogEntry.cs" /> |
| 78 | + <Compile Include="Models\Hatena\FotolifeEntry.cs" /> | |
| 78 | 79 | <Compile Include="Models\Hatena\HatenaDiary.cs" /> |
| 80 | + <Compile Include="Models\Hatena\HatenaFotolife.cs" /> | |
| 79 | 81 | <Compile Include="Models\Hatena\WsseAtomConnection.cs" /> |
| 80 | 82 | <Compile Include="Models\Hatena\XmlNamespaces.cs" /> |
| 81 | 83 | <Compile Include="Models\Model.cs" /> |
| @@ -8,7 +8,7 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 8 | 8 | public class BlogEntry |
| 9 | 9 | { |
| 10 | 10 | public DateTime Edited { set; get; } |
| 11 | - public string BlogPage { set; get; } | |
| 11 | + public string BlogPageUri { set; get; } | |
| 12 | 12 | public string Title { set; get; } |
| 13 | 13 | public string Html { set; get; } |
| 14 | 14 | public string HatenaSyntax { set; get; } |
| @@ -18,7 +18,7 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 18 | 18 | public BlogEntry(XElement xml) |
| 19 | 19 | { |
| 20 | 20 | this.Edited = DateTime.Parse(xml.Element(XmlNamespaces.AtomPub + "edited").Value); |
| 21 | - this.BlogPage = xml.Elements(XmlNamespaces.Atom + "link") | |
| 21 | + this.BlogPageUri = xml.Elements(XmlNamespaces.Atom + "link") | |
| 22 | 22 | .Where(_ => _.Attribute("rel").Value == "alternate") |
| 23 | 23 | .Select(_ => _.Attribute("href").Value) |
| 24 | 24 | .FirstOrDefault(); |
| @@ -0,0 +1,44 @@ | ||
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | +using System.Linq; | |
| 4 | +using System.Text; | |
| 5 | +using System.Xml.Linq; | |
| 6 | +using System.Text.RegularExpressions; | |
| 7 | + | |
| 8 | +namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | |
| 9 | +{ | |
| 10 | + public class FotolifeEntry | |
| 11 | + { | |
| 12 | + public string Title { set; get; } | |
| 13 | + public string PageUri { set; get; } | |
| 14 | + public string Id { set; get; } | |
| 15 | + public DateTime Issued { set; get; } | |
| 16 | + public string Folder { set; get; } | |
| 17 | + public string ImageUri { set; get; } | |
| 18 | + public string ThumbnailUri { set; get; } | |
| 19 | + public string HatenaSyntax { set; get; } | |
| 20 | + | |
| 21 | + public FotolifeEntry() { } | |
| 22 | + public FotolifeEntry(XElement xml, bool isRdf) | |
| 23 | + { | |
| 24 | + if (isRdf) | |
| 25 | + { | |
| 26 | + //TODO | |
| 27 | + } | |
| 28 | + else | |
| 29 | + { | |
| 30 | + this.Title = xml.Element(XmlNamespaces.Atom02Spec + "title").Value; | |
| 31 | + this.PageUri = xml.Elements(XmlNamespaces.Atom02Spec + "link") | |
| 32 | + .Where(_ => _.Attribute("rel").Value == "alternate") | |
| 33 | + .Select(_ => _.Attribute("href").Value) | |
| 34 | + .FirstOrDefault(); | |
| 35 | + this.Id = Regex.Match(this.PageUri, @"\d+$").ToString(); | |
| 36 | + this.Issued = DateTime.Parse(xml.Element(XmlNamespaces.Atom02Spec + "issued").Value); | |
| 37 | + this.Folder = xml.Element(XmlNamespaces.DublinCore + "subject").Value; | |
| 38 | + this.ImageUri = xml.Element(XmlNamespaces.HatenaNs + "imageurl").Value; | |
| 39 | + this.ThumbnailUri = xml.Element(XmlNamespaces.HatenaNs + "imageurlsmall").Value; | |
| 40 | + this.HatenaSyntax = xml.Element(XmlNamespaces.HatenaNs + "syntax").Value; | |
| 41 | + } | |
| 42 | + } | |
| 43 | + } | |
| 44 | +} |
| @@ -2,6 +2,7 @@ | ||
| 2 | 2 | using System.Collections.Generic; |
| 3 | 3 | using System.Linq; |
| 4 | 4 | using System.Net; |
| 5 | +using System.Text; | |
| 5 | 6 | using System.Text.RegularExpressions; |
| 6 | 7 | using System.Xml.Linq; |
| 7 | 8 |
| @@ -44,6 +45,7 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 44 | 45 | var reXml = WsseAtomConnection.Post( |
| 45 | 46 | string.Format("http://d.hatena.ne.jp/{0}/atom/blog", this.userName), |
| 46 | 47 | CreatePostEntryXml(title, content, updated), |
| 48 | + Encoding.UTF8, | |
| 47 | 49 | this.userName, |
| 48 | 50 | this.password); |
| 49 | 51 | return new BlogEntry(reXml.Root) |
| @@ -66,6 +68,7 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 66 | 68 | var reXml = WsseAtomConnection.Put( |
| 67 | 69 | string.Format("http://d.hatena.ne.jp/{0}/atom/blog/{1}", this.userName, dateId), |
| 68 | 70 | CreatePostEntryXml(title, content, updated), |
| 71 | + Encoding.UTF8, | |
| 69 | 72 | this.userName, |
| 70 | 73 | this.password); |
| 71 | 74 | return new BlogEntry(reXml.Root) |
| @@ -98,6 +101,7 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 98 | 101 | var reXml = WsseAtomConnection.Post( |
| 99 | 102 | string.Format("http://d.hatena.ne.jp/{0}/atom/draft", this.userName), |
| 100 | 103 | CreatePostEntryXml(title, content, updated), |
| 104 | + Encoding.UTF8, | |
| 101 | 105 | this.userName, |
| 102 | 106 | this.password); |
| 103 | 107 | return new BlogEntry(reXml.Root) |
| @@ -120,6 +124,7 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 120 | 124 | var reXml = WsseAtomConnection.Put( |
| 121 | 125 | string.Format("http://d.hatena.ne.jp/{0}/atom/draft/{1}", this.userName, id), |
| 122 | 126 | CreatePostEntryXml(title, content, updated), |
| 127 | + Encoding.UTF8, | |
| 123 | 128 | this.userName, |
| 124 | 129 | this.password); |
| 125 | 130 | return new BlogEntry(reXml.Root) |
| @@ -0,0 +1,90 @@ | ||
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | +using System.Linq; | |
| 4 | +using System.Text; | |
| 5 | +using System.IO; | |
| 6 | +using System.Xml.Linq; | |
| 7 | +using System.Net; | |
| 8 | + | |
| 9 | +namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | |
| 10 | +{ | |
| 11 | + public class HatenaFotolife | |
| 12 | + { | |
| 13 | + private string userName; | |
| 14 | + private string password; | |
| 15 | + | |
| 16 | + public HatenaFotolife(string userName, string password) | |
| 17 | + { | |
| 18 | + this.userName = userName; | |
| 19 | + this.password = password; | |
| 20 | + } | |
| 21 | + | |
| 22 | + public FotolifeEntry Upload(string title, string fileName, string folder = null) | |
| 23 | + { | |
| 24 | + string mimeType; | |
| 25 | + #region MIME Type判断 | |
| 26 | + switch (Path.GetExtension(fileName).TrimStart('.').ToLower()) | |
| 27 | + { | |
| 28 | + case "jpg": | |
| 29 | + case "jpeg": | |
| 30 | + mimeType = "image/jpeg"; | |
| 31 | + break; | |
| 32 | + case "gif": | |
| 33 | + mimeType = "image/gif"; | |
| 34 | + break; | |
| 35 | + case "png": | |
| 36 | + mimeType = "image/png"; | |
| 37 | + break; | |
| 38 | + case "bmp": | |
| 39 | + mimeType = "image/x-bmp"; | |
| 40 | + break; | |
| 41 | + case "mov": | |
| 42 | + mimeType = "video/quicktime"; | |
| 43 | + break; | |
| 44 | + case "mpg": | |
| 45 | + case "mpeg": | |
| 46 | + case "m1v": | |
| 47 | + mimeType = "video/mpeg"; | |
| 48 | + break; | |
| 49 | + case "wmv": | |
| 50 | + mimeType = "video/x-ms-wmv"; | |
| 51 | + break; | |
| 52 | + case "avi": | |
| 53 | + mimeType = "video/avi"; | |
| 54 | + break; | |
| 55 | + case "flv": | |
| 56 | + mimeType = "video/x-flv"; | |
| 57 | + break; | |
| 58 | + case "3gp": | |
| 59 | + case "3gpp": | |
| 60 | + mimeType = "video/3gpp"; | |
| 61 | + break; | |
| 62 | + case "3g2": | |
| 63 | + mimeType = "video/3gpp2"; | |
| 64 | + break; | |
| 65 | + default: | |
| 66 | + throw new ArgumentException("対応してないファイルです。"); | |
| 67 | + } | |
| 68 | + #endregion | |
| 69 | + | |
| 70 | + var entry = new XElement(XmlNamespaces.Atom02Spec + "entry", | |
| 71 | + new XElement(XmlNamespaces.Atom02Spec + "title", title), | |
| 72 | + new XElement(XmlNamespaces.Atom02Spec + "content", | |
| 73 | + new XAttribute("mode", "base64"), | |
| 74 | + new XAttribute("type", mimeType), | |
| 75 | + Convert.ToBase64String(File.ReadAllBytes(fileName))) | |
| 76 | + ); | |
| 77 | + | |
| 78 | + if (!string.IsNullOrEmpty(folder)) | |
| 79 | + entry.Add(new XElement(XmlNamespaces.DublinCore + "subject", folder)); | |
| 80 | + | |
| 81 | + var reXml = WsseAtomConnection.Post( | |
| 82 | + "http://f.hatena.ne.jp/atom/post", | |
| 83 | + new XDocument(entry), | |
| 84 | + Encoding.GetEncoding("shift_jis"), | |
| 85 | + this.userName, | |
| 86 | + this.password); | |
| 87 | + return new FotolifeEntry(reXml.Root, false); | |
| 88 | + } | |
| 89 | + } | |
| 90 | +} |
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | using System; |
| 2 | +using System.Linq; | |
| 2 | 3 | using System.Net; |
| 3 | 4 | using System.Security.Cryptography; |
| 4 | 5 | using System.Text; |
| @@ -12,18 +13,16 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 12 | 13 | |
| 13 | 14 | public static string CreateHeader(string userName, string password) |
| 14 | 15 | { |
| 15 | - var tmp = new byte[25]; | |
| 16 | - random.NextBytes(tmp); | |
| 17 | - var nonce = Convert.ToBase64String(tmp); | |
| 18 | - tmp = null; | |
| 16 | + var nonce = new byte[8]; | |
| 17 | + random.NextBytes(nonce); | |
| 19 | 18 | var created = DateTime.Now.ToUniversalTime().ToString("o"); |
| 20 | 19 | var digest = Convert.ToBase64String( |
| 21 | 20 | new SHA1Managed().ComputeHash( |
| 22 | - Encoding.ASCII.GetBytes(nonce + created + password) | |
| 21 | + nonce.Concat(Encoding.ASCII.GetBytes(created)).Concat(Encoding.ASCII.GetBytes(password)).ToArray() | |
| 23 | 22 | )); |
| 24 | 23 | return string.Format( |
| 25 | 24 | @"X-WSSE:UsernameToken Username=""{0}"", PasswordDigest=""{1}"", Nonce=""{2}"", Created=""{3}""", |
| 26 | - userName, digest, nonce, created); | |
| 25 | + userName, digest, Convert.ToBase64String(nonce), created); | |
| 27 | 26 | } |
| 28 | 27 | |
| 29 | 28 | public static XDocument Get(string reqUri, string userName, string password) |
| @@ -44,13 +43,13 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 44 | 43 | req.GetResponse().Close(); |
| 45 | 44 | } |
| 46 | 45 | |
| 47 | - private static XDocument PostOrPut(string reqUri, string method, XDocument reqData, string userName, string password) | |
| 46 | + private static XDocument PostOrPut(string reqUri, string method, XDocument reqData, Encoding enc, string userName, string password) | |
| 48 | 47 | { |
| 49 | 48 | var req = WebRequest.Create(reqUri); |
| 50 | 49 | req.Method = method; |
| 51 | 50 | req.Headers.Add(CreateHeader(userName, password)); |
| 52 | - req.ContentType = "application/x.atom+xml; charset=utf-8"; | |
| 53 | - var bs = Encoding.UTF8.GetBytes(reqData.ToString()); | |
| 51 | + req.ContentType = "application/x.atom+xml"; | |
| 52 | + var bs = Encoding.GetEncoding("shift_jis").GetBytes(reqData.ToString()); | |
| 54 | 53 | req.ContentLength = bs.Length; |
| 55 | 54 | using (var stream = req.GetRequestStream()) |
| 56 | 55 | stream.Write(bs, 0, bs.Length); |
| @@ -58,14 +57,14 @@ namespace Azyobuzi.HatenaDiaryClient.Models.Hatena | ||
| 58 | 57 | return XDocument.Load(res.GetResponseStream()); |
| 59 | 58 | } |
| 60 | 59 | |
| 61 | - public static XDocument Post(string reqUri, XDocument reqData, string userName, string password) | |
| 60 | + public static XDocument Post(string reqUri, XDocument reqData, Encoding enc, string userName, string password) | |
| 62 | 61 | { |
| 63 | - return PostOrPut(reqUri, "POST", reqData, userName, password); | |
| 62 | + return PostOrPut(reqUri, "POST", reqData, enc, userName, password); | |
| 64 | 63 | } |
| 65 | 64 | |
| 66 | - public static XDocument Put(string reqUri, XDocument reqData, string userName, string password) | |
| 65 | + public static XDocument Put(string reqUri, XDocument reqData, Encoding enc, string userName, string password) | |
| 67 | 66 | { |
| 68 | - return PostOrPut(reqUri, "PUT", reqData, userName, password); | |
| 67 | + return PostOrPut(reqUri, "PUT", reqData, enc, userName, password); | |
| 69 | 68 | } |
| 70 | 69 | } |
| 71 | 70 | } |
| \ No newline at end of file |