/// <summary> /// 新規スレッドを投稿 /// </summary> /// <param name="board">投稿先の板</param> /// <param name="thread">投稿する内容</param> public override void Post(BoardInfo board, PostThread thread) { try { // 投稿時刻を作成 int time = GetTime(Time); string[] dirbbs = board.Path.Split('/'); // CGIの存在するURLを作成 string uri = String.Format("http://{0}/bbs/write.cgi/{1}/new/", board.Server, board.Path); // 送信データを作成 StringBuilder sb = new StringBuilder(); sb.Append("SUBJECT=" + UrlEncode(thread.Subject)); sb.Append("&submit=" + UrlEncode("新規書き込み")); sb.Append("&NAME=" + UrlEncode(thread.From)); sb.Append("&MAIL=" + UrlEncode(thread.Email)); sb.Append("&MESSAGE=" + UrlEncode(thread.Body)); sb.Append("&DIR=" + dirbbs[0]); sb.Append("&BBS=" + dirbbs[1]); sb.Append("&TIME=" + time); bool retried = false; byte[] bytes = Encoding.GetBytes(sb.ToString()); Posting(board, bytes, uri, ref retried); } catch (Exception ex) { TwinDll.Output(ex); throw ex; } }
/// <summary> /// 次スレチェックスレッド /// </summary> protected virtual void Checking() { ThreadListReader listReader = null; List <ThreadHeader> tempItems = new List <ThreadHeader>(); BoardInfo board = header.BoardInfo; try { matchItems.Clear(); listReader = TypeCreator.CreateThreadListReader(board.Bbs); if (listReader.Open(board)) { while (listReader.Read(tempItems) != 0) { ; } matchItems = Check(header, tempItems); } OnSuccess(this, new ThreadHeaderEventArgs(matchItems)); } catch (Exception ex) { TwinDll.Output(ex); } finally { if (listReader != null) { listReader.Close(); } } }
/// <summary> /// メッセージを投稿 /// </summary> /// <param name="header">投稿先のスレッド</param> /// <param name="res">投稿する内容</param> public override void Post(ThreadHeader header, PostRes res) { if (header == null) { throw new ArgumentNullException("header"); } try { // 投稿時刻を作成 int time = GetTime(header.LastModified); // 送信データを作成 bool retried = false; string uri = String.Format("http://{0}/cgi-bin/test/bbs.cgi", header.BoardInfo.Server); StringBuilder sb = new StringBuilder(); sb.Append("submit=" + UrlEncode("つっこむ")); sb.Append("&FROM=" + UrlEncode(res.From)); sb.Append("&mail=" + UrlEncode(res.Email)); sb.Append("&MESSAGE=" + UrlEncode(res.Body)); sb.Append("&bbs=" + header.BoardInfo.Path); sb.Append("&key=" + header.Key); sb.Append("&time=" + time); byte[] bytes = Encoding.GetBytes(sb.ToString()); Posting(header.BoardInfo, bytes, uri, ref retried); } catch (Exception ex) { TwinDll.Output(ex); throw ex; } }
/// <summary> /// 新規スレッドを投稿 /// </summary> /// <param name="board">投稿先の板</param> /// <param name="thread">投稿する内容</param> public override void Post(BoardInfo board, PostThread thread) { try { // 投稿時刻を作成 int time = GetTime(Time); // 送信データを作成 bool retried = false; // CGIの存在するURLを作成 string uri = String.Format("http://{0}/cgi-bin/test/bbs.cgi", board.Server); StringBuilder sb = new StringBuilder(); sb.Append("subject=" + UrlEncode(thread.Subject)); sb.Append("&submit=" + UrlEncode("新規スレッド作成")); sb.Append("&FROM=" + UrlEncode(thread.From)); sb.Append("&mail=" + UrlEncode(thread.Email)); sb.Append("&MESSAGE=" + UrlEncode(thread.Body)); sb.Append("&bbs=" + board.Path); sb.Append("&time=" + time); byte[] bytes = Encoding.GetBytes(sb.ToString()); Posting(board, bytes, uri, ref retried); } catch (Exception ex) { TwinDll.Output(ex); throw ex; } }
protected virtual PostResponse Posting(BoardInfo board, byte[] data, string uri, ref bool retried) { HttpWebResponse res = null; PostResponseParser parser; const int timeout = 30000; string referer = board.Url + "index.html"; try { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri); req.Method = "POST"; req.Accept = "*/*"; req.ContentType = "application/x-www-form-urlencoded"; req.ContentLength = data.Length; req.Referer = referer; req.Timeout = timeout; req.ReadWriteTimeout = timeout; req.UserAgent = UserAgent; req.AllowAutoRedirect = false; req.Proxy = Proxy; Stream st = req.GetRequestStream(); st.Write(data, 0, data.Length); st.Close(); res = (HttpWebResponse)req.GetResponse(); // レスポンスを解析 using (TextReader reader = new StreamReader(res.GetResponseStream(), Encoding)) { parser = new PostResponseParser(reader.ReadToEnd()); response = parser.Response; // <TITLE>302 Found</TITLE>が返ってきたら書き込み成功 // if (res.StatusCode == HttpStatusCode.Found) // response = PostResponse.Success; } // 投稿イベントを発生させる PostEventArgs e = new PostEventArgs(response, parser.Title, parser.PlainText, null, -1); OnPosted(this, e); // 既にリトライされていたら無限ループ防止のためfalseに設定 retried = retried ? false : e.Retry; } catch (Exception ex) { TwinDll.Output(ex); OnError(this, new PostErrorEventArgs(ex)); } finally { if (res != null) { res.Close(); } } return(response); }
private void TracingThread() { try { if (tracer.Trace(source, true)) { success = true; } } catch (ThreadAbortException) {} catch (Exception ex) { TwinDll.ShowOutput(ex); } finally { Invoke(new MethodInvoker(OnFinally)); } }
/// <summary> /// メッセージを投稿 /// </summary> /// <param name="header">投稿先のスレッド</param> /// <param name="res">投稿する内容</param> public override void Post(ThreadHeader header, PostRes res) { if (header == null) { throw new ArgumentNullException("header"); } try { // 投稿時刻を作成 int time = GetTime(header.LastModified); // 送信データを作成 bool retried = false; Retry: if (retried) { Thread.Sleep(100); } string uri = String.Format("http://{0}/{1}", header.BoardInfo.Server, postCGIPath); StringBuilder sb = new StringBuilder(); // sb.Append("submit=" + (retried ? UrlEncode("全責任を負うことを承諾して書き込む") : UrlEncode("書き込む"))); sb.Append("submit=" + UrlEncode("書き込む")); sb.Append("&FROM=" + UrlEncode(res.From)); sb.Append("&mail=" + UrlEncode(res.Email)); sb.Append("&MESSAGE=" + UrlEncode(res.Body)); sb.Append("&bbs=" + header.BoardInfo.Path); sb.Append("&key=" + header.Key); sb.Append("&time=" + time); // sb.Append("&hana=mogera"); sb.Append(TwinDll.AditionalAgreementField); sb.Append(TwinDll.AddWriteSection); AddSessionId(sb); byte[] bytes = Encoding.GetBytes(sb.ToString()); Posting(header.BoardInfo, bytes, uri, ref retried); if (retried) { goto Retry; } } catch (Exception ex) { TwinDll.Output(ex); throw ex; } }
/// <summary> /// 指定した板の書き込み履歴に追加 /// </summary> /// <param name="board"></param> /// <param name="res"></param> public void Append(ThreadHeader header, WroteRes res) { try { string filePath = GetKomiPath(header.BoardInfo); StringBuilder sb = new StringBuilder(); sb.AppendFormat("Date: {0}\r\nSubject: {1}\r\nURL: {2}\r\nFrom: {3}\r\nEmail: {4}\r\n\r\n{5}\r\n------------------------\r\n", res.Date.ToString(), header.Subject, header.Url, res.From, res.Email, res.Message); FileUtility.Write(filePath, sb.ToString(), true); } catch (Exception ex) { TwinDll.Output(ex); } }
/// <summary> /// 新規スレッドを投稿 /// </summary> /// <param name="board">投稿先の板</param> /// <param name="thread">投稿する内容</param> public override void Post(BoardInfo board, PostThread thread) { try { // 投稿時刻を作成 int time = GetTime(Time); // 送信データを作成 bool retried = false; Retry: if (retried) { Thread.Sleep(1000); } // CGIの存在するURLを作成 string uri = String.Format("http://{0}/{1}", board.Server, postCGIPath); StringBuilder sb = new StringBuilder(); sb.Append("subject=" + UrlEncode(thread.Subject)); // sb.Append("&submit=" + (retried ? UrlEncode("全責任を負うことを承諾して書き込む") : UrlEncode("新規スレッド作成"))); sb.Append("&submit=" + UrlEncode("新規スレッド作成")); sb.Append("&FROM=" + UrlEncode(thread.From)); sb.Append("&mail=" + UrlEncode(thread.Email)); sb.Append("&MESSAGE=" + UrlEncode(thread.Body)); sb.Append("&bbs=" + board.Path); sb.Append("&time=" + time); sb.Append(TwinDll.AditionalAgreementField); sb.Append(TwinDll.AddWriteSection); AddSessionId(sb); byte[] bytes = Encoding.GetBytes(sb.ToString()); Posting(board, bytes, uri, ref retried); if (retried) { goto Retry; } } catch (Exception ex) { TwinDll.Output(ex); throw ex; } }
/// <summary> /// 更新イベント発生 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnTimer(object sender, ElapsedEventArgs e) { // Timer timer = (Timer)sender; // timer.Stop(); try { current.Stop(); ThreadControl thread = current.Thread; if (thread.IsOpen) { thread.Reload(); } } catch (Exception ex) { TwinDll.Output(ex); } }
protected override ThreadHeader[] ParseData(string dataText) { List <ThreadHeader> list = new List <ThreadHeader>(100); const string token = ".cgi,"; int begin = 0, index; try { // 123456.cgi,スレッド名(123) while (begin < dataText.Length && (index = newline.Search(dataText, begin)) != -1) { string data = dataText.Substring(begin, index - begin); int idxDot = data.IndexOf(token); int idxSep1 = data.LastIndexOf('('); int idxSep2 = data.LastIndexOf(')'); int idxSubj = idxDot + token.Length; if (idxDot >= 0 && idxSep1 >= 0 && idxSep2 >= 0) { ThreadHeader header = TypeCreator.CreateThreadHeader(bbsType); header.Key = data.Substring(0, idxDot); header.Subject = data.Substring(idxSubj, idxSep1 - idxSubj); int resCnt; if (Int32.TryParse(data.Substring(idxSep1 + 1, idxSep2 - (idxSep1 + 1)), out resCnt)) { header.ResCount = resCnt; } list.Add(header); } begin = (index + newline.Pattern.Length); } } catch (Exception ex) { TwinDll.Output(ex); } return(list.ToArray()); }
/// <summary> /// メッセージを投稿 /// </summary> /// <param name="header">投稿先のスレッド</param> /// <param name="res">投稿する内容</param> public override void Post(ThreadHeader header, PostRes res) { if (header == null) { throw new ArgumentNullException("header"); } try { BoardInfo board = header.BoardInfo; // 投稿時刻を作成 int time = GetTime(header.LastModified); // BoardInfo.PathをBBSとDIRに分割 string[] dirbbs = board.Path.Split('/'); Trace.Assert(dirbbs.Length == 2); // 送信データを作成 string uri = String.Format("http://{0}/bbs/write.cgi/{1}/{2}/", board.Server, board.Path, header.Key); StringBuilder sb = new StringBuilder(); sb.Append("submit=" + UrlEncode("書き込む")); sb.Append("&NAME=" + UrlEncode(res.From)); sb.Append("&MAIL=" + UrlEncode(res.Email)); sb.Append("&MESSAGE=" + UrlEncode(res.Body)); sb.Append("&DIR=" + dirbbs[0]); sb.Append("&BBS=" + dirbbs[1]); sb.Append("&KEY=" + header.Key); sb.Append("&TIME=" + time); bool retried = false; byte[] bytes = Encoding.GetBytes(sb.ToString()); Posting(header.BoardInfo, bytes, uri, ref retried); } catch (Exception ex) { TwinDll.Output(ex); throw ex; } }
/// <summary> /// dataを解析しコレクションに格納 /// </summary> /// <param name="bytes">解析するバイトデータ</param> /// <param name="length">dataの長さ</param> /// <param name="parsed">解析されたデータの長さ</param> /// <returns></returns> public virtual T[] Parse(byte[] data, int length, out int parsed) { if (data == null) { throw new ArgumentNullException("data"); } List <T> result = new List <T>(); int index = 0; // 前回の余りデータとdataを結合 if (memory.Length > 0) { memory.Write(data, 0, length); data = memory.ToArray(); length = data.Length; } // 解析可能な部分の終わりを探す int tokenLength; int token = GetEndToken(data, 0, length, out tokenLength); if (token != -1) { // 前回の余りデータの末尾に足して1つのデータにする index = token + tokenLength; // 余りの長さを求める remainderLength = length - index; // 文字列に変換後に解析 string dataText = encoding.GetString(data, 0, index); T[] array = ParseData(dataText); if (array != null) { result.AddRange(array); } else { TwinDll.Output("データの解析に失敗: " + dataText); } } else { // 解析可能な部分データがなければすべてを余りデータとして // 前回の余りデータに書き足し次回の解析に使用 remainderLength = length; index = 0; } // 解析できなかった余りデータをメモリに貯める memory.Close(); memory = new MemoryStream(capacity); memory.Write(data, index, remainderLength); // 実際に解析された長さを取得 parsed = (length - remainderLength); return(result.ToArray()); }
/// <summary> /// dataをサーバーに送信しレスポンスを得る /// </summary> /// <param name="data"></param> protected virtual PostResponse Posting(BoardInfo board, byte[] data, string uri, ref bool retried) { if (board == null) { throw new ArgumentNullException("board"); } if (data == null) { throw new ArgumentNullException("data"); } HttpWebResponse res = null; PostResponseParser parser = null; // タイムアウト値 const int timeout = 15000; // 15秒 // 再試行を行うかどうか bool is_retry = true; try { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri); req.Method = "POST"; req.ContentType = "application/x-www-form-urlencoded"; req.ContentLength = data.Length; req.Referer = board.Url + "index.html"; req.UserAgent = UserAgent; req.Timeout = timeout; req.ReadWriteTimeout = timeout; req.Proxy = Proxy; // req.Accept = "text/html, */*"; // req.Expect = null; // req.AllowAutoRedirect = false; // req.ProtocolVersion = HttpVersion.Version10; // NTwin 2011/05/31 //req.CookieContainer = GetCookie(board); req.CookieContainer = CookieManager.gCookies; #if DEBUG foreach (Cookie c in req.CookieContainer.GetCookies(req.RequestUri)) { Console.WriteLine("{0}={1}", c.Name, c.Value); } #endif SetHttpWebRequest(req); Stream st = req.GetRequestStream(); st.Write(data, 0, data.Length); st.Close(); res = (HttpWebResponse)req.GetResponse(); // レスポンスを解析するためのパーサを初期化。 using (TextReader reader = new StreamReader(res.GetResponseStream(), Encoding)) { parser = new PostResponseParser(reader.ReadToEnd()); response = parser.Response; if (response == PostResponse.Cookie) { foreach (KeyValuePair <string, string> kv in parser.HiddenParams) { if (Regex.IsMatch(kv.Key, "subject|FROM|mail|MESSAGE|bbs|time|key") == false) { TwinDll.AditionalAgreementField = String.Format("&{0}={1}", kv.Key, kv.Value); Console.WriteLine(TwinDll.AditionalAgreementField); break; } } } } if (res.StatusCode == HttpStatusCode.Found) { response = PostResponse.Success; } /* * board.CookieContainer = new CookieContainer(); * * bool ponIsExist = false; * * foreach (Cookie c in req.CookieContainer.GetCookies(req.RequestUri)) * { #if DEBUG * Console.WriteLine("{0}={1}", c.Name, c.Value); #endif * if (c.Name == "PON") * ponIsExist = true; * board.CookieContainer.Add(c); * } * * if (!ponIsExist && response == PostResponse.Cookie) * { * board.CookieContainer.Add(res.Cookies); * }*/ // 投稿イベントを発生させる PostEventArgs e = new PostEventArgs(response, parser.Title, parser.PlainText, null, parser.SambaCount); OnPosted(this, e); is_retry = e.Retry; } catch (Exception ex) { #if DEBUG WebException webex = ex as WebException; if (webex != null) { TwinDll.ShowOutput("Status " + webex.Status + ", " + webex.ToString()); } else { TwinDll.ShowOutput(ex); } #endif // タイムアウトやそれ以外の例外が発生したら無条件でリトライを中止 //is_retry = false; OnError(this, new PostErrorEventArgs(ex)); } finally { if (res != null) { res.Close(); } // クッキー確認などでの再試行処理 // ※既に再試行されていたら無限ループ防止のためfalseに設定 if (retried) { retried = false; } else { retried = is_retry; } } return(response); }
/// <summary> /// 指定した板の移転を追跡 /// </summary> /// <param name="board">追跡する板</param> /// <param name="recursive">移転先がさらに移転していた場合、再起追跡するかどうか</param> /// <returns>追跡できればtrue、失敗すればfalseを返す</returns> public bool Trace(BoardInfo board, bool recursive) { if (board == null) { throw new ArgumentNullException("board"); } traceList.Clear(); result = null; Check: // Htmlデータを取得 HttpWebRequest req = (HttpWebRequest)WebRequest.Create(board.Url); req.UserAgent = TwinDll.UserAgent; req.AddRange(0, 499); HttpWebResponse res = (HttpWebResponse)req.GetResponse(); string html; using (StreamReader sr = new StreamReader(res.GetResponseStream(), Encoding.GetEncoding("Shift_Jis"))) html = sr.ReadToEnd(); res.Close(); // サーバー移転 if (html.IndexOf("2chbbs..") >= 0) { TwinDll.Output("{0} が移転しています。", board.Url); // 移転先のURLを取得 Match m = Regex.Match(html, "<a href=\"(?<url>.+?)\">GO !</a>", RegexOptions.IgnoreCase); if (m.Success) { string newUrl = m.Groups["url"].Value; TwinDll.Output("移転先 {0} を発見しました。", newUrl); result = URLParser.ParseBoard(m.Groups["url"].Value); if (result != null) { result.Name = board.Name; traceList.Add(result); OnTracing(new ServerChangeEventArgs(board, result)); if (recursive) { board = result; html = null; goto Check; } } } } // 追跡終了した場合は板名を取得 else if (result != null) { if (String.IsNullOrEmpty(result.Name)) { Match m = Regex.Match(html, "<title>(?<t>.+?)</title>", RegexOptions.IgnoreCase); if (m.Success) { result.Name = m.Groups["t"].Value; } } TwinDll.Output("{0} の追跡に成功しました。", result.Name); } return((result != null) ? true : false); }