// X2chAuthenticateThreadReader.cs namespace Twin.Bbs { using System; using System.Net; using System.IO.Compression; using Twin.Util; using System.IO; using System.Text; /// /// 認証を使って2ちゃんねる (www.2ch.net) のスレッドを読み込む機能を提供 /// public class X2chAuthenticateThreadReader : X2chThreadReader { private HttpWebResponse _res = null; private bool firstRead = true; public X2chRokkaResponseState RokkaResponseState { get; private set; } public X2chAuthenticateThreadReader() : base() { RokkaResponseState = X2chRokkaResponseState.None; } /// /// スレッドを開く /// /// public override bool Open(ThreadHeader header) { if (header == null) { throw new ArgumentNullException("header"); } if (IsOpen) { throw new InvalidOperationException("既にストリームが開かれています"); } headerInfo = header; firstRead = true; RokkaResponseState = X2chRokkaResponseState.None; if (header.Pastlog) return false; X2chAuthenticator authenticator = X2chAuthenticator.GetInstance(); if (authenticator.HasSession) { X2chThreadHeader x2chHeader = header as X2chThreadHeader; if (x2chHeader != null) { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(x2chHeader.AuthenticateUrl); req.Timeout = 15000; req.UserAgent = UserAgent; req.AllowAutoRedirect = false; // ** 9/26 削除 ** // req.Headers.Add("Accept-Encoding", "gzip"); // ** 9/26 追加 ** req.AutomaticDecompression = DecompressionMethods.GZip; req.Headers.Add("Pragma", "no-cache"); req.Headers.Add("Cache-Control", "no-cache"); if (x2chHeader.ETag != String.Empty) req.Headers.Add("If-None-Match", x2chHeader.ETag); _res = (HttpWebResponse)req.GetResponse(); //baseStream = _res.GetResponseStream(); baseStream = FileUtility.CreateMemoryStream( _res.GetResponseStream() ); baseStream.Position = 0; length = (int)baseStream.Length; RokkaResponseState = ParseRokkaFirstline( baseStream ); if (_res.StatusCode == HttpStatusCode.OK) { /* 9 26削除 using (GZipStream gzipInp = new GZipStream(_res.GetResponseStream(), CompressionMode.Decompress)) baseStream = FileUtility.CreateMemoryStream(gzipInp); */ x2chHeader.ETag = _res.Headers["ETag"]; x2chHeader.LastModified = _res.LastModified; index = header.GotResCount + 1; position = 0; isOpen = true; } else OnPastlog(new PastlogEventArgs(header)); } } else OnPastlog(new PastlogEventArgs(header)); // 過去ログなのでdat落ちに設定 headerInfo.Pastlog = true; return isOpen; } //public override int Read( ResSetCollection resSets , out int byteParsed ) //{ // if ( firstRead ) // { // byte[] buf = new byte[1]; // int c; // do // { // c = baseStream.Read( buf , 0 , buf.Length ); // // 最初の改行が来るまで読み飛ばす // } while ( c != 0 && buf[0] != '\n' ); // firstRead = false; // } // return base.Read( resSets , out byteParsed ); //} public override void Close() { base.Close(); if (_res != null) { _res.Close(); _res = null; } } private X2chRokkaResponseState ParseRokkaFirstline(Stream responseStream) { if (responseStream == null) throw new ArgumentNullException(); var statusbytes = new System.Collections.Generic.List(); byte[] buf = new byte[1]; int c; do { c = baseStream.Read( buf , 0 , buf.Length ); // 最初の改行が来るまで読み飛ばす // 2013/09/10 初めの行を処理結果コードエリアと定義&宣言したので文字に取り込んでおく // http://q...content-available-to-author-only...h.net/test/read.cgi/operate/1366640919/119 statusbytes.Add( buf[0] ); } while ( c != 0 && buf[0] != '\n' ); var line = base.dataParser.Encoding.GetString( statusbytes.ToArray() ); //byte[] buf = new byte[256]; //long firstpos = responseStream.Position; //responseStream.Read( buf , 0 , buf.Length ); //using ( StringReader s = new StringReader( base.dataParser.Encoding.GetString( buf ) ) ) //using ( StreamReader s = new StreamReader( responseStream ) ) //{ // string line = s.ReadLine(); //responseStream.Position += base.dataParser.Encoding.GetByteCount( line ); // 最初の1行分だけストリームを進めておく if (line.StartsWith("Success")) return X2chRokkaResponseState.Success; else if (line.StartsWith("Error 8008135")) return X2chRokkaResponseState.InvalidServerOrBoardOrThread; else if (line.StartsWith("Error 69")) return X2chRokkaResponseState.AuthenticationError; else if (line.StartsWith("Error 666")) return X2chRokkaResponseState.UrlError; else if (line.StartsWith("Error 420")) return X2chRokkaResponseState.TimeLimitError; else { // ステータスが見つからなかった場合は、ストリームの位置を元に戻しておく //responseStream.Position = firstpos; return X2chRokkaResponseState.Unknown; } //} } } /* "Success"  - The process has successfuly done. Following lines are achieved message with dat format Error codes:     inputError = "Error 8008135"      invalid SERVER or BOARD or THREAD     authenticationError = "Error 69"    invalid SID     urlError = "Error 666"         invalid OPTIONS     timeLimitError = "Error 420"       access too fast, interval between requests required */ public enum X2chRokkaResponseState { None = -2, Unknown = -1, // ステータスが不明 Success = 0, InvalidServerOrBoardOrThread = 8008135, AuthenticationError = 89, UrlError = 666, TimeLimitError = 420, } }