OSDN Git Service

Merge branch 'api11'
[opentween/open-tween.git] / OpenTween / Twitter.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 //           (c) 2008-2011 Moz (@syo68k)
4 //           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 //           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 //           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 //           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
8 // All rights reserved.
9 //
10 // This file is part of OpenTween.
11 //
12 // This program is free software; you can redistribute it and/or modify it
13 // under the terms of the GNU General Public License as published by the Free
14 // Software Foundation; either version 3 of the License, or (at your option)
15 // any later version.
16 //
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25 // Boston, MA 02110-1301, USA.
26
27 using System.Diagnostics;
28 using System.IO;
29 using System.Linq;
30 using System.Net;
31 using System.Runtime.Serialization;
32 using System.Runtime.Serialization.Json;
33 using System.Text;
34 using System.Text.RegularExpressions;
35 using System.Threading;
36 using System.Web;
37 using System.Xml;
38 using System.Xml.Linq;
39 using System;
40 using System.Reflection;
41 using System.Collections.Generic;
42 using System.Drawing;
43 using System.Windows.Forms;
44 using OpenTween.Api;
45
46 namespace OpenTween
47 {
48     public class Twitter : IDisposable
49     {
50         #region Regexp from twitter-text-js
51
52         // The code in this region code block incorporates works covered by
53         // the following copyright and permission notices:
54         //
55         //   Copyright 2011 Twitter, Inc.
56         //
57         //   Licensed under the Apache License, Version 2.0 (the "License"); you
58         //   may not use this work except in compliance with the License. You
59         //   may obtain a copy of the License in the LICENSE file, or at:
60         //
61         //   http://www.apache.org/licenses/LICENSE-2.0
62         //
63         //   Unless required by applicable law or agreed to in writing, software
64         //   distributed under the License is distributed on an "AS IS" BASIS,
65         //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
66         //   implied. See the License for the specific language governing
67         //   permissions and limitations under the License.
68
69         //Hashtag用正規表現
70         private const string LATIN_ACCENTS = @"\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253\u0254\u0256\u0257\u0259\u025b\u0263\u0268\u026f\u0272\u0289\u028b\u02bb\u1e00-\u1eff";
71         private const string NON_LATIN_HASHTAG_CHARS = @"\u0400-\u04ff\u0500-\u0527\u1100-\u11ff\u3130-\u3185\uA960-\uA97F\uAC00-\uD7AF\uD7B0-\uD7FF";
72         //private const string CJ_HASHTAG_CHARACTERS = @"\u30A1-\u30FA\uFF66-\uFF9F\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\u3041-\u3096\u3400-\u4DBF\u4E00-\u9FFF\u20000-\u2A6DF\u2A700-\u2B73F\u2B740-\u2B81F\u2F800-\u2FA1F";
73         private const string CJ_HASHTAG_CHARACTERS = @"\u30A1-\u30FA\u30FC\u3005\uFF66-\uFF9F\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\u3041-\u309A\u3400-\u4DBF\p{IsCJKUnifiedIdeographs}";
74         private const string HASHTAG_BOUNDARY = @"^|$|\s|「|」|。|\.|!";
75         private const string HASHTAG_ALPHA = "[a-z_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
76         private const string HASHTAG_ALPHANUMERIC = "[a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
77         private const string HASHTAG_TERMINATOR = "[^a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
78         public const string HASHTAG = "(" + HASHTAG_BOUNDARY + ")(#|#)(" + HASHTAG_ALPHANUMERIC + "*" + HASHTAG_ALPHA + HASHTAG_ALPHANUMERIC + "*)(?=" + HASHTAG_TERMINATOR + "|" + HASHTAG_BOUNDARY + ")";
79         //URL正規表現
80         private const string url_valid_preceding_chars = @"(?:[^A-Za-z0-9@@$##\ufffe\ufeff\uffff\u202a-\u202e]|^)";
81         public const string url_invalid_without_protocol_preceding_chars = @"[-_./]$";
82         private const string url_invalid_domain_chars = @"\!'#%&'\(\)*\+,\\\-\.\/:;<=>\?@\[\]\^_{|}~\$\u2000-\u200a\u0009-\u000d\u0020\u0085\u00a0\u1680\u180e\u2028\u2029\u202f\u205f\u3000\ufffe\ufeff\uffff\u202a-\u202e";
83         private const string url_valid_domain_chars = @"[^" + url_invalid_domain_chars + "]";
84         private const string url_valid_subdomain = @"(?:(?:" + url_valid_domain_chars + @"(?:[_-]|" + url_valid_domain_chars + @")*)?" + url_valid_domain_chars + @"\.)";
85         private const string url_valid_domain_name = @"(?:(?:" + url_valid_domain_chars + @"(?:-|" + url_valid_domain_chars + @")*)?" + url_valid_domain_chars + @"\.)";
86         private const string url_valid_GTLD = @"(?:(?:aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|xxx)(?=[^0-9a-zA-Z]|$))";
87         private const string url_valid_CCTLD = @"(?:(?:ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|ss|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)(?=[^0-9a-zA-Z]|$))";
88         private const string url_valid_punycode = @"(?:xn--[0-9a-z]+)";
89         private const string url_valid_domain = @"(?<domain>" + url_valid_subdomain + "*" + url_valid_domain_name + "(?:" + url_valid_GTLD + "|" + url_valid_CCTLD + ")|" + url_valid_punycode + ")";
90         public const string url_valid_ascii_domain = @"(?:(?:[a-z0-9" + LATIN_ACCENTS + @"]+)\.)+(?:" + url_valid_GTLD + "|" + url_valid_CCTLD + "|" + url_valid_punycode + ")";
91         public const string url_invalid_short_domain = "^" + url_valid_domain_name + url_valid_CCTLD + "$";
92         private const string url_valid_port_number = @"[0-9]+";
93
94         private const string url_valid_general_path_chars = @"[a-z0-9!*';:=+,.$/%#\[\]\-_~|&" + LATIN_ACCENTS + "]";
95         private const string url_balance_parens = @"(?:\(" + url_valid_general_path_chars + @"+\))";
96         private const string url_valid_path_ending_chars = @"(?:[+\-a-z0-9=_#/" + LATIN_ACCENTS + "]|" + url_balance_parens + ")";
97         private const string pth = "(?:" +
98             "(?:" +
99                 url_valid_general_path_chars + "*" +
100                 "(?:" + url_balance_parens + url_valid_general_path_chars + "*)*" +
101                 url_valid_path_ending_chars +
102                 ")|(?:@" + url_valid_general_path_chars + "+/)" +
103             ")";
104         private const string qry = @"(?<query>\?[a-z0-9!?*'();:&=+$/%#\[\]\-_.,~|]*[a-z0-9_&=#/])?";
105         public const string rgUrl = @"(?<before>" + url_valid_preceding_chars + ")" +
106                                     "(?<url>(?<protocol>https?://)?" +
107                                     "(?<domain>" + url_valid_domain + ")" +
108                                     "(?::" + url_valid_port_number + ")?" +
109                                     "(?<path>/" + pth + "*)?" +
110                                     qry +
111                                     ")";
112
113         #endregion
114
115         /// <summary>
116         /// Twitter API のステータスページのURL
117         /// </summary>
118         public const string ServiceAvailabilityStatusUrl = "https://status.io.watchmouse.com/7617";
119
120         /// <summary>
121         /// ツイートへのパーマリンクURLを判定する正規表現
122         /// </summary>
123         public static readonly Regex StatusUrlRegex = new Regex(@"https?://([^.]+\.)?twitter\.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)/status(es)?/(?<StatusId>[0-9]+)(/photo)?", RegexOptions.IgnoreCase);
124
125         /// <summary>
126         /// FavstarやaclogなどTwitter関連サービスのパーマリンクURLからステータスIDを抽出する正規表現
127         /// </summary>
128         public static readonly Regex ThirdPartyStatusUrlRegex = new Regex(@"https?://(?:[^.]+\.)?(?:
129   favstar\.fm/users/[a-zA-Z0-9_]+/status/       # Favstar
130 | favstar\.fm/t/                                # Favstar (short)
131 | aclog\.koba789\.com/i/                        # aclog
132 | frtrt\.net/solo_status\.php\?status=          # RtRT
133 )(?<StatusId>[0-9]+)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
134
135         delegate void GetIconImageDelegate(PostClass post);
136         private readonly object LockObj = new object();
137         private List<long> followerId = new List<long>();
138         private bool _GetFollowerResult = false;
139         private List<long> noRTId = new List<long>();
140         private bool _GetNoRetweetResult = false;
141
142         private int _followersCount = 0;
143         private int _friendsCount = 0;
144         private int _statusesCount = 0;
145         private string _location = "";
146         private string _bio = "";
147         private string _protocol = "https://";
148
149         //プロパティからアクセスされる共通情報
150         private string _uname;
151         private int _iconSz;
152         private bool _getIcon;
153
154         private bool _tinyUrlResolve;
155         private bool _restrictFavCheck;
156
157         private bool _readOwnPost;
158         private List<string> _hashList = new List<string>();
159
160         private Outputz op = new Outputz();
161         //max_idで古い発言を取得するために保持(lists分は個別タブで管理)
162         private long minHomeTimeline = long.MaxValue;
163         private long minMentions = long.MaxValue;
164         private long minDirectmessage = long.MaxValue;
165         private long minDirectmessageSent = long.MaxValue;
166
167         //private FavoriteQueue favQueue;
168
169         private HttpTwitter twCon = new HttpTwitter();
170
171         //private List<PostClass> _deletemessages = new List<PostClass>();
172
173         public TwitterApiAccessLevel AccessLevel
174         {
175             get
176             {
177                 if (HttpTwitter.API11Enabled)
178                     return MyCommon.TwitterApiInfo11.AccessLevel;
179                 else
180                     return MyCommon.TwitterApiInfo.AccessLevel;
181             }
182         }
183
184         protected void ResetApiStatus()
185         {
186             if (HttpTwitter.API11Enabled)
187                 MyCommon.TwitterApiInfo11.Reset();
188             else
189                 MyCommon.TwitterApiInfo.Reset();
190         }
191
192         public string Authenticate(string username, string password)
193         {
194             HttpStatusCode res;
195             var content = "";
196
197             this.ResetApiStatus();
198             try
199             {
200                 res = twCon.AuthUserAndPass(username, password, ref content);
201             }
202             catch(Exception ex)
203             {
204                 return "Err:" + ex.Message;
205             }
206
207             switch (res)
208             {
209             case HttpStatusCode.OK:
210                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
211                 _uname = username.ToLower();
212                 if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
213                 return "";
214             case HttpStatusCode.Unauthorized:
215                 {
216                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
217                     var errMsg = GetErrorMessageJson(content);
218                     if (string.IsNullOrEmpty(errMsg))
219                     {
220                         return Properties.Resources.Unauthorized + Environment.NewLine + content;
221                     }
222                     else
223                     {
224                         return "Auth error:" + errMsg;
225                     }
226                 }
227             case HttpStatusCode.Forbidden:
228                 {
229                     var errMsg = GetErrorMessageJson(content);
230                     if (string.IsNullOrEmpty(errMsg))
231                     {
232                         return "Err:Forbidden";
233                     }
234                     else
235                     {
236                         return "Err:" + errMsg;
237                     }
238                 }
239             default:
240                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
241             }
242         }
243
244         public string StartAuthentication(ref string pinPageUrl)
245         {
246             //OAuth PIN Flow
247             bool res;
248
249             this.ResetApiStatus();
250             try
251             {
252                 res = twCon.AuthGetRequestToken(ref pinPageUrl);
253             }
254             catch(Exception)
255             {
256                 return "Err:" + "Failed to access auth server.";
257             }
258
259             return "";
260         }
261
262         public string Authenticate(string pinCode)
263         {
264             HttpStatusCode res;
265             var content = "";
266
267             this.ResetApiStatus();
268             try
269             {
270                 res = twCon.AuthGetAccessToken(pinCode);
271             }
272             catch(Exception)
273             {
274                 return "Err:" + "Failed to access auth acc server.";
275             }
276
277             switch (res)
278             {
279             case HttpStatusCode.OK:
280                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
281                 _uname = Username.ToLower();
282                 if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
283                 return "";
284             case HttpStatusCode.Unauthorized:
285                 {
286                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
287                     var errMsg = GetErrorMessageJson(content);
288                     if (string.IsNullOrEmpty(errMsg))
289                     {
290                         return "Check the PIN or retry." + Environment.NewLine + content;
291                     }
292                     else
293                     {
294                         return "Auth error:" + errMsg;
295                     }
296                 }
297             case HttpStatusCode.Forbidden:
298                 {
299                     var errMsg = GetErrorMessageJson(content);
300                     if (string.IsNullOrEmpty(errMsg))
301                     {
302                         return "Err:Forbidden";
303                     }
304                     else
305                     {
306                         return "Err:" + errMsg;
307                     }
308                 }
309             default:
310                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
311             }
312         }
313
314         public void ClearAuthInfo()
315         {
316             Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
317             this.ResetApiStatus();
318             twCon.ClearAuthInfo();
319         }
320
321         public void VerifyCredentials()
322         {
323             HttpStatusCode res = HttpStatusCode.BadRequest;
324             var content = "";
325
326             try
327             {
328                 res = twCon.VerifyCredentials(ref content);
329             }
330             catch(Exception)
331             {
332                 return;
333             }
334
335             if (res == HttpStatusCode.OK)
336             {
337                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
338                 TwitterDataModel.User user;
339                 try
340                 {
341                     user = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
342                 }
343                 catch(SerializationException)
344                 {
345                     return;
346                 }
347                 twCon.AuthenticatedUserId = user.Id;
348             }
349         }
350
351         private string GetErrorMessageJson(string content)
352         {
353             try
354             {
355                 if (!string.IsNullOrEmpty(content))
356                 {
357                     using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(content), XmlDictionaryReaderQuotas.Max))
358                     {
359                         var xElm = XElement.Load(jsonReader);
360                         if (xElm.Element("error") != null)
361                         {
362                             return xElm.Element("error").Value;
363                         }
364                         else
365                         {
366                             return "";
367                         }
368                     }
369                 }
370                 else
371                 {
372                     return "";
373                 }
374             }
375             catch(Exception)
376             {
377                 return "";
378             }
379         }
380
381         public void Initialize(string token, string tokenSecret, string username, long userId)
382         {
383             //OAuth認証
384             if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(tokenSecret) || string.IsNullOrEmpty(username))
385             {
386                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
387             }
388             this.ResetApiStatus();
389             twCon.Initialize(token, tokenSecret, username, userId);
390             _uname = username.ToLower();
391             if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
392         }
393
394         public string PreProcessUrl(string orgData)
395         {
396             int posl1;
397             var posl2 = 0;
398             //var IDNConveter = new IdnMapping();
399             var href = "<a href=\"";
400
401             while (true)
402             {
403                 if (orgData.IndexOf(href, posl2, StringComparison.Ordinal) > -1)
404                 {
405                     var urlStr = "";
406                     // IDN展開
407                     posl1 = orgData.IndexOf(href, posl2, StringComparison.Ordinal);
408                     posl1 += href.Length;
409                     posl2 = orgData.IndexOf("\"", posl1, StringComparison.Ordinal);
410                     urlStr = orgData.Substring(posl1, posl2 - posl1);
411
412                     if (!urlStr.StartsWith("http://") && !urlStr.StartsWith("https://") && !urlStr.StartsWith("ftp://"))
413                     {
414                         continue;
415                     }
416
417                     var replacedUrl = MyCommon.IDNDecode(urlStr);
418                     if (replacedUrl == null) continue;
419                     if (replacedUrl == urlStr) continue;
420
421                     orgData = orgData.Replace("<a href=\"" + urlStr, "<a href=\"" + replacedUrl);
422                     posl2 = 0;
423                 }
424                 else
425                 {
426                     break;
427                 }
428             }
429             return orgData;
430         }
431
432         private string GetPlainText(string orgData)
433         {
434             return WebUtility.HtmlDecode(Regex.Replace(orgData, "(?<tagStart><a [^>]+>)(?<text>[^<]+)(?<tagEnd></a>)", "${text}"));
435         }
436
437         // htmlの簡易サニタイズ(詳細表示に不要なタグの除去)
438
439         private string SanitizeHtml(string orgdata)
440         {
441             var retdata = orgdata;
442
443             retdata = Regex.Replace(retdata, "<(script|object|applet|image|frameset|fieldset|legend|style).*" +
444                 "</(script|object|applet|image|frameset|fieldset|legend|style)>", "", RegexOptions.IgnoreCase);
445
446             retdata = Regex.Replace(retdata, "<(frame|link|iframe|img)>", "", RegexOptions.IgnoreCase);
447
448             return retdata;
449         }
450
451         private string AdjustHtml(string orgData)
452         {
453             var retStr = orgData;
454             //var m = Regex.Match(retStr, "<a [^>]+>[#|#](?<1>[a-zA-Z0-9_]+)</a>");
455             //while (m.Success)
456             //{
457             //    lock (LockObj)
458             //    {
459             //        _hashList.Add("#" + m.Groups(1).Value);
460             //    }
461             //    m = m.NextMatch;
462             //}
463             retStr = Regex.Replace(retStr, "<a [^>]*href=\"/", "<a href=\"https://twitter.com/");
464             retStr = retStr.Replace("<a href=", "<a target=\"_self\" href=");
465             retStr = Regex.Replace(retStr, @"(\r\n?|\n)", "<br>"); // CRLF, CR, LF は全て <br> に置換する
466
467             //半角スペースを置換(Thanks @anis774)
468             var ret = false;
469             do
470             {
471                 ret = EscapeSpace(ref retStr);
472             } while (!ret);
473
474             return SanitizeHtml(retStr);
475         }
476
477         private bool EscapeSpace(ref string html)
478         {
479             //半角スペースを置換(Thanks @anis774)
480             var isTag = false;
481             for (int i = 0; i < html.Length; i++)
482             {
483                 if (html[i] == '<')
484                 {
485                     isTag = true;
486                 }
487                 if (html[i] == '>')
488                 {
489                     isTag = false;
490                 }
491
492                 if ((!isTag) && (html[i] == ' '))
493                 {
494                     html = html.Remove(i, 1);
495                     html = html.Insert(i, "&nbsp;");
496                     return false;
497                 }
498             }
499             return true;
500         }
501
502         private struct PostInfo
503         {
504             public string CreatedAt;
505             public string Id;
506             public string Text;
507             public string UserId;
508             public PostInfo(string Created, string IdStr, string txt, string uid)
509             {
510                 CreatedAt = Created;
511                 Id = IdStr;
512                 Text = txt;
513                 UserId = uid;
514             }
515             public bool Equals(PostInfo dst)
516             {
517                 if (this.CreatedAt == dst.CreatedAt && this.Id == dst.Id && this.Text == dst.Text && this.UserId == dst.UserId)
518                 {
519                     return true;
520                 }
521                 else
522                 {
523                     return false;
524                 }
525             }
526         }
527
528         static private PostInfo _prev = new PostInfo("", "", "", "");
529         private bool IsPostRestricted(TwitterDataModel.Status status)
530         {
531             var _current = new PostInfo("", "", "", "");
532
533             _current.CreatedAt = status.CreatedAt;
534             _current.Id = status.IdStr;
535             if (status.Text == null)
536             {
537                 _current.Text = "";
538             }
539             else
540             {
541                 _current.Text = status.Text;
542             }
543             _current.UserId = status.User.IdStr;
544
545             if (_current.Equals(_prev))
546             {
547                 return true;
548             }
549             _prev.CreatedAt = _current.CreatedAt;
550             _prev.Id = _current.Id;
551             _prev.Text = _current.Text;
552             _prev.UserId = _current.UserId;
553
554             return false;
555         }
556
557         public string PostStatus(string postStr, long reply_to)
558         {
559
560             if (MyCommon._endingFlag) return "";
561
562             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
563
564             postStr = postStr.Trim();
565
566             if (Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline).Success)
567             {
568                 return SendDirectMessage(postStr);
569             }
570
571             HttpStatusCode res = HttpStatusCode.BadRequest;
572             var content = "";
573             try
574             {
575                 res = twCon.UpdateStatus(postStr, reply_to, ref content);
576             }
577             catch(Exception ex)
578             {
579                 return "Err:" + ex.Message;
580             }
581
582             switch (res)
583             {
584             case HttpStatusCode.OK:
585                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
586                 TwitterDataModel.Status status;
587                 try
588                 {
589                     status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
590                 }
591                 catch(SerializationException ex)
592                 {
593                     MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
594                     return "Err:Json Parse Error(DataContractJsonSerializer)";
595                 }
596                 catch(Exception ex)
597                 {
598                     MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
599                     return "Err:Invalid Json!";
600                 }
601                 _followersCount = status.User.FollowersCount;
602                 _friendsCount = status.User.FriendsCount;
603                 _statusesCount = status.User.StatusesCount;
604                 _location = status.User.Location;
605                 _bio = status.User.Description;
606
607                 if (IsPostRestricted(status))
608                 {
609                     return "OK:Delaying?";
610                 }
611                 if (op.Post(postStr.Length))
612                 {
613                     return "";
614                 }
615                 else
616                 {
617                     return "Outputz:Failed";
618                 }
619             case HttpStatusCode.NotFound:
620                 return "";
621             case HttpStatusCode.Forbidden:
622             case HttpStatusCode.BadRequest:
623                 {
624                     var errMsg = GetErrorMessageJson(content);
625                     if (string.IsNullOrEmpty(errMsg))
626                     {
627                         return "Warn:" + res.ToString();
628                     }
629                     else
630                     {
631                         return "Warn:" + errMsg;
632                     }
633                 }
634             case HttpStatusCode.Conflict:
635             case HttpStatusCode.ExpectationFailed:
636             case HttpStatusCode.Gone:
637             case HttpStatusCode.LengthRequired:
638             case HttpStatusCode.MethodNotAllowed:
639             case HttpStatusCode.NotAcceptable:
640             case HttpStatusCode.PaymentRequired:
641             case HttpStatusCode.PreconditionFailed:
642             case HttpStatusCode.RequestedRangeNotSatisfiable:
643             case HttpStatusCode.RequestEntityTooLarge:
644             case HttpStatusCode.RequestTimeout:
645             case HttpStatusCode.RequestUriTooLong:
646                 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
647                 return "Warn:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
648             case HttpStatusCode.Unauthorized:
649                 {
650                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
651                     var errMsg = GetErrorMessageJson(content);
652                     if (string.IsNullOrEmpty(errMsg))
653                     {
654                         return Properties.Resources.Unauthorized;
655                     }
656                     else
657                     {
658                         return "Auth err:" + errMsg;
659                     }
660                 }
661             default:
662                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
663             }
664         }
665
666         public string PostStatusWithMedia(string postStr, long reply_to, FileInfo mediaFile)
667         {
668             if (MyCommon._endingFlag) return "";
669
670             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
671
672             postStr = postStr.Trim();
673
674             HttpStatusCode res = HttpStatusCode.BadRequest;
675             var content = "";
676             try
677             {
678                 res = twCon.UpdateStatusWithMedia(postStr, reply_to, mediaFile, ref content);
679             }
680             catch(Exception ex)
681             {
682                 return "Err:" + ex.Message;
683             }
684
685             switch (res)
686             {
687             case HttpStatusCode.OK:
688                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
689                 TwitterDataModel.Status status;
690                 try
691                 {
692                     status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
693                 }
694                 catch(SerializationException ex)
695                 {
696                     MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
697                     return "Err:Json Parse Error(DataContractJsonSerializer)";
698                 }
699                 catch(Exception ex)
700                 {
701                     MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
702                     return "Err:Invalid Json!";
703                 }
704                 _followersCount = status.User.FollowersCount;
705                 _friendsCount = status.User.FriendsCount;
706                 _statusesCount = status.User.StatusesCount;
707                 _location = status.User.Location;
708                 _bio = status.User.Description;
709
710                 if (IsPostRestricted(status))
711                 {
712                     return "OK:Delaying?";
713                 }
714                 if (op.Post(postStr.Length))
715                 {
716                     return "";
717                 }
718                 else
719                 {
720                     return "Outputz:Failed";
721                 }
722             case HttpStatusCode.NotFound:
723                 return "";
724             case HttpStatusCode.Forbidden:
725             case HttpStatusCode.BadRequest:
726                 {
727                     var errMsg = GetErrorMessageJson(content);
728                     if (string.IsNullOrEmpty(errMsg))
729                     {
730                         return "Warn:" + res.ToString();
731                     }
732                     else
733                     {
734                         return "Warn:" + errMsg;
735                     }
736                 }
737             case HttpStatusCode.Conflict:
738             case HttpStatusCode.ExpectationFailed:
739             case HttpStatusCode.Gone:
740             case HttpStatusCode.LengthRequired:
741             case HttpStatusCode.MethodNotAllowed:
742             case HttpStatusCode.NotAcceptable:
743             case HttpStatusCode.PaymentRequired:
744             case HttpStatusCode.PreconditionFailed:
745             case HttpStatusCode.RequestedRangeNotSatisfiable:
746             case HttpStatusCode.RequestEntityTooLarge:
747             case HttpStatusCode.RequestTimeout:
748             case HttpStatusCode.RequestUriTooLong:
749                 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
750                 return "Warn:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
751             case HttpStatusCode.Unauthorized:
752                 {
753                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
754                     var errMsg = GetErrorMessageJson(content);
755                     if (string.IsNullOrEmpty(errMsg))
756                     {
757                         return Properties.Resources.Unauthorized;
758                     }
759                     else
760                     {
761                         return "Auth err:" + errMsg;
762                     }
763                 }
764             default:
765                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
766             }
767         }
768
769         public string SendDirectMessage(string postStr)
770         {
771             if (MyCommon._endingFlag) return "";
772
773             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
774
775             if (this.AccessLevel == TwitterApiAccessLevel.Read || this.AccessLevel == TwitterApiAccessLevel.ReadWrite)
776             {
777                 return "Auth Err:try to re-authorization.";
778             }
779
780             postStr = postStr.Trim();
781
782             HttpStatusCode res = HttpStatusCode.BadRequest;
783             var content = "";
784
785             var mc = Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
786
787             try
788             {
789                 res = twCon.SendDirectMessage(mc.Groups["body"].Value, mc.Groups["id"].Value, ref content);
790             }
791             catch(Exception ex)
792             {
793                 return "Err:" + ex.Message;
794             }
795
796             switch (res)
797             {
798             case HttpStatusCode.OK:
799                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
800                 TwitterDataModel.Directmessage status;
801                 try
802                 {
803                     status = MyCommon.CreateDataFromJson<TwitterDataModel.Directmessage>(content);
804                 }
805                 catch(SerializationException ex)
806                 {
807                     MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
808                     return "Err:Json Parse Error(DataContractJsonSerializer)";
809                 }
810                 catch(Exception ex)
811                 {
812                     MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
813                     return "Err:Invalid Json!";
814                 }
815                 _followersCount = status.Sender.FollowersCount;
816                 _friendsCount = status.Sender.FriendsCount;
817                 _statusesCount = status.Sender.StatusesCount;
818                 _location = status.Sender.Location;
819                 _bio = status.Sender.Description;
820
821                 if (op.Post(postStr.Length))
822                 {
823                     return "";
824                 }
825                 else
826                 {
827                     return "Outputz:Failed";
828                 }
829             case HttpStatusCode.Forbidden:
830             case HttpStatusCode.BadRequest:
831                 {
832                     var errMsg = GetErrorMessageJson(content);
833                     if (string.IsNullOrEmpty(errMsg))
834                     {
835                         return "Warn:" + res.ToString();
836                     }
837                     else
838                     {
839                         return "Warn:" + errMsg;
840                     }
841                 }
842             case HttpStatusCode.Conflict:
843             case HttpStatusCode.ExpectationFailed:
844             case HttpStatusCode.Gone:
845             case HttpStatusCode.LengthRequired:
846             case HttpStatusCode.MethodNotAllowed:
847             case HttpStatusCode.NotAcceptable:
848             case HttpStatusCode.NotFound:
849             case HttpStatusCode.PaymentRequired:
850             case HttpStatusCode.PreconditionFailed:
851             case HttpStatusCode.RequestedRangeNotSatisfiable:
852             case HttpStatusCode.RequestEntityTooLarge:
853             case HttpStatusCode.RequestTimeout:
854             case HttpStatusCode.RequestUriTooLong:
855                 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
856                 return "Warn:" + res.ToString();
857             case HttpStatusCode.Unauthorized:
858                 {
859                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
860                     var errMsg = GetErrorMessageJson(content);
861                     if (string.IsNullOrEmpty(errMsg))
862                     {
863                         return Properties.Resources.Unauthorized;
864                     }
865                     else
866                     {
867                         return "Auth err:" + errMsg;
868                     }
869                 }
870             default:
871                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
872             }
873         }
874
875         public string RemoveStatus(long id)
876         {
877             if (MyCommon._endingFlag) return "";
878
879             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
880
881             HttpStatusCode res = HttpStatusCode.BadRequest;
882
883             try
884             {
885                 res = twCon.DestroyStatus(id);
886             }
887             catch(Exception ex)
888             {
889                 return "Err:" + ex.Message;
890             }
891
892             switch (res)
893             {
894             case HttpStatusCode.OK:
895                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
896                 return "";
897             case HttpStatusCode.Unauthorized:
898                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
899                 return Properties.Resources.Unauthorized;
900             case HttpStatusCode.NotFound:
901                 return "";
902             default:
903                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
904             }
905         }
906
907         public string PostRetweet(long id, bool read)
908         {
909             if (MyCommon._endingFlag) return "";
910             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
911
912             //データ部分の生成
913             var target = id;
914             var post = TabInformations.GetInstance()[id];
915             if (post == null)
916             {
917                 return "Err:Target isn't found.";
918             }
919             if (TabInformations.GetInstance()[id].RetweetedId > 0)
920             {
921                 target = TabInformations.GetInstance()[id].RetweetedId; //再RTの場合は元発言をRT
922             }
923
924             HttpStatusCode res = HttpStatusCode.BadRequest;
925             var content = "";
926             try
927             {
928                 res = twCon.RetweetStatus(target, ref content);
929             }
930             catch(Exception ex)
931             {
932                 return "Err:" + ex.Message;
933             }
934
935             if (res == HttpStatusCode.Unauthorized)
936             {
937                 //Blockユーザーの発言をRTすると認証エラー返る
938                 //Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid
939                 return Properties.Resources.Unauthorized + " or blocked user.";
940             }
941             else if (res != HttpStatusCode.OK)
942             {
943                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
944             }
945
946             Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
947
948             TwitterDataModel.Status status;
949             try
950             {
951                 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
952             }
953             catch(SerializationException ex)
954             {
955                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
956                 return "Err:Json Parse Error(DataContractJsonSerializer)";
957             }
958             catch(Exception ex)
959             {
960                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
961                 return "Err:Invalid Json!";
962             }
963
964             //ReTweetしたものをTLに追加
965             post = CreatePostsFromStatusData(status);
966             if (post == null) return "Invalid Json!";
967
968             //二重取得回避
969             lock (LockObj)
970             {
971                 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) return "";
972             }
973             //Retweet判定
974             if (post.RetweetedId == 0) return "Invalid Json!";
975             //ユーザー情報
976             post.IsMe = true;
977
978             post.IsRead = read;
979             post.IsOwl = false;
980             if (_readOwnPost) post.IsRead = true;
981             post.IsDm = false;
982
983             TabInformations.GetInstance().AddPost(post);
984
985             return "";
986         }
987
988         public string RemoveDirectMessage(long id, PostClass post)
989         {
990             if (MyCommon._endingFlag) return "";
991
992             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
993
994             if (this.AccessLevel == TwitterApiAccessLevel.Read || this.AccessLevel == TwitterApiAccessLevel.ReadWrite)
995             {
996                 return "Auth Err:try to re-authorization.";
997             }
998
999             HttpStatusCode res = HttpStatusCode.BadRequest;
1000
1001             //if (post.IsMe)
1002             //    _deletemessages.Add(post)
1003             //}
1004             try
1005             {
1006                 res = twCon.DestroyDirectMessage(id);
1007             }
1008             catch(Exception ex)
1009             {
1010                 return "Err:" + ex.Message;
1011             }
1012
1013             switch (res)
1014             {
1015             case HttpStatusCode.OK:
1016                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1017                 return "";
1018             case HttpStatusCode.Unauthorized:
1019                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1020                 return Properties.Resources.Unauthorized;
1021             case HttpStatusCode.NotFound:
1022                 return "";
1023             default:
1024                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1025             }
1026         }
1027
1028         public string PostFollowCommand(string screenName)
1029         {
1030             if (MyCommon._endingFlag) return "";
1031
1032             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1033
1034             HttpStatusCode res = HttpStatusCode.BadRequest;
1035             var content = "";
1036
1037             try
1038             {
1039                 res = twCon.CreateFriendships(screenName, ref content);
1040             }
1041             catch(Exception ex)
1042             {
1043                 return "Err:" + ex.Message;
1044             }
1045
1046             switch (res)
1047             {
1048             case HttpStatusCode.OK:
1049                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1050                 return "";
1051             case HttpStatusCode.Unauthorized:
1052                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1053                 return Properties.Resources.Unauthorized;
1054             case HttpStatusCode.Forbidden:
1055                 var errMsg = GetErrorMessageJson(content);
1056                 if (string.IsNullOrEmpty(errMsg))
1057                 {
1058                     return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1059                 }
1060                 else
1061                 {
1062                     return "Err:" + errMsg;
1063                 }
1064             default:
1065                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1066             }
1067         }
1068
1069         public string PostRemoveCommand(string screenName)
1070         {
1071             if (MyCommon._endingFlag) return "";
1072
1073             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1074
1075             HttpStatusCode res = HttpStatusCode.BadRequest;
1076             var content = "";
1077
1078             try
1079             {
1080                 res = twCon.DestroyFriendships(screenName, ref content);
1081             }
1082             catch(Exception ex)
1083             {
1084                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1085             }
1086
1087             switch (res)
1088             {
1089             case HttpStatusCode.OK:
1090                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1091                 return "";
1092             case HttpStatusCode.Unauthorized:
1093                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1094                 return Properties.Resources.Unauthorized;
1095             case HttpStatusCode.Forbidden:
1096                 var errMsg = GetErrorMessageJson(content);
1097                 if (string.IsNullOrEmpty(errMsg))
1098                 {
1099                     return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1100                 }
1101                 else
1102                 {
1103                     return "Err:" + errMsg;
1104                 }
1105             default:
1106                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1107             }
1108         }
1109
1110         public string PostCreateBlock(string screenName)
1111         {
1112             if (MyCommon._endingFlag) return "";
1113
1114             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1115
1116             HttpStatusCode res = HttpStatusCode.BadRequest;
1117             var content = "";
1118
1119             try
1120             {
1121                 res = twCon.CreateBlock(screenName, ref content);
1122             }
1123             catch(Exception ex)
1124             {
1125                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1126             }
1127
1128             switch (res)
1129             {
1130                 case HttpStatusCode.OK:
1131                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1132                     return "";
1133                 case HttpStatusCode.Unauthorized:
1134                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1135                     return Properties.Resources.Unauthorized;
1136                 case HttpStatusCode.Forbidden:
1137                     var errMsg = GetErrorMessageJson(content);
1138                     if (string.IsNullOrEmpty(errMsg))
1139                     {
1140                         return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1141                     }
1142                     else
1143                     {
1144                         return "Err:" + errMsg;
1145                     }
1146                 default:
1147                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1148             }
1149         }
1150
1151         public string PostDestroyBlock(string screenName)
1152         {
1153             if (MyCommon._endingFlag) return "";
1154
1155             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1156
1157             HttpStatusCode res = HttpStatusCode.BadRequest;
1158             var content = "";
1159
1160             try
1161             {
1162                 res = twCon.DestroyBlock(screenName, ref content);
1163             }
1164             catch(Exception ex)
1165             {
1166                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1167             }
1168
1169             switch (res)
1170             {
1171             case HttpStatusCode.OK:
1172                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1173                 return "";
1174             case HttpStatusCode.Unauthorized:
1175                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1176                 return Properties.Resources.Unauthorized;
1177             case HttpStatusCode.Forbidden:
1178                 var errMsg = GetErrorMessageJson(content);
1179                 if (string.IsNullOrEmpty(errMsg))
1180                 {
1181                     return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1182                 }
1183                 else
1184                 {
1185                     return "Err:" + errMsg;
1186                 }
1187             default:
1188                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1189             }
1190         }
1191
1192         public string PostReportSpam(string screenName)
1193         {
1194             if (MyCommon._endingFlag) return "";
1195
1196             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1197
1198             HttpStatusCode res = HttpStatusCode.BadRequest;
1199             var content = "";
1200
1201             try
1202             {
1203                 res = twCon.ReportSpam(screenName, ref content);
1204             }
1205             catch(Exception ex)
1206             {
1207                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1208             }
1209
1210             switch (res)
1211             {
1212             case HttpStatusCode.OK:
1213                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1214                 return "";
1215             case HttpStatusCode.Unauthorized:
1216                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1217                 return Properties.Resources.Unauthorized;
1218             case HttpStatusCode.Forbidden:
1219                 var errMsg = GetErrorMessageJson(content);
1220                 if (string.IsNullOrEmpty(errMsg))
1221                 {
1222                     return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1223                 }
1224                 else
1225                 {
1226                     return "Err:" + errMsg;
1227                 }
1228             default:
1229                 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1230             }
1231         }
1232
1233         public string GetFriendshipInfo(string screenName, ref bool isFollowing, ref bool isFollowed)
1234         {
1235             if (MyCommon._endingFlag) return "";
1236
1237             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1238
1239             HttpStatusCode res = HttpStatusCode.BadRequest;
1240             var content = "";
1241             try
1242             {
1243                 res = twCon.ShowFriendships(_uname, screenName, ref content);
1244             }
1245             catch(Exception ex)
1246             {
1247                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1248             }
1249
1250             switch (res)
1251             {
1252                 case HttpStatusCode.OK:
1253                     try
1254                     {
1255                         var relation = MyCommon.CreateDataFromJson<TwitterDataModel.Relationship>(content);
1256                         isFollowing = relation.relationship.Source.Following;
1257                         isFollowed = relation.relationship.Source.FollowedBy;
1258                         return "";
1259                     }
1260                     catch(SerializationException ex)
1261                     {
1262                         MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1263                         return "Err:Json Parse Error(DataContractJsonSerializer)";
1264                     }
1265                     catch(Exception ex)
1266                     {
1267                         MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1268                         return "Err:Invalid Json!";
1269                     }
1270                 case HttpStatusCode.BadRequest:
1271                     return "Err:API Limits?";
1272                 case HttpStatusCode.Unauthorized:
1273                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1274                     return Properties.Resources.Unauthorized;
1275                 default:
1276                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1277             }
1278         }
1279
1280         public string GetUserInfo(string screenName, ref TwitterDataModel.User user)
1281         {
1282             if (MyCommon._endingFlag) return "";
1283
1284             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1285
1286             HttpStatusCode res = HttpStatusCode.BadRequest;
1287             var content = "";
1288             user = null;
1289             try
1290             {
1291                 res = twCon.ShowUserInfo(screenName, ref content);
1292             }
1293             catch(Exception ex)
1294             {
1295                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1296             }
1297
1298             switch (res)
1299             {
1300                 case HttpStatusCode.OK:
1301                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1302                     try
1303                     {
1304                         user = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
1305                     }
1306                     catch (SerializationException ex)
1307                     {
1308                         MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1309                         return "Err:Json Parse Error(DataContractJsonSerializer)";
1310                     }
1311                     catch (Exception ex)
1312                     {
1313                         MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1314                         return "Err:Invalid Json!";
1315                     }
1316                     return "";
1317                 case HttpStatusCode.BadRequest:
1318                     return "Err:API Limits?";
1319                 case HttpStatusCode.Unauthorized:
1320                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1321                     var errMsg = GetErrorMessageJson(content);
1322                     if (string.IsNullOrEmpty(errMsg))
1323                     {
1324                         return Properties.Resources.Unauthorized;
1325                     }
1326                     else
1327                     {
1328                         return "Auth err:" + errMsg;
1329                     }
1330                 default:
1331                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1332             }
1333         }
1334
1335         public string GetStatus_Retweeted_Count(long StatusId, ref int retweeted_count)
1336         {
1337             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1338
1339             if (MyCommon._endingFlag) return "";
1340
1341             HttpStatusCode res = HttpStatusCode.BadRequest;
1342             var content = "";
1343
1344             try
1345             {
1346                 res = twCon.ShowStatuses(StatusId, ref content);
1347             }
1348             catch (Exception ex)
1349             {
1350                 return "Err:" + ex.Message;
1351             }
1352             switch (res)
1353             {
1354                 case HttpStatusCode.OK:
1355                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1356                     break;
1357                 case HttpStatusCode.Unauthorized:
1358                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1359                     return Properties.Resources.Unauthorized;
1360                 case HttpStatusCode.BadRequest:
1361                     return "Err:API Limits?";
1362                 case HttpStatusCode.Forbidden:
1363                     return "Err:protected user's tweet";
1364                 default:
1365                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1366             }
1367
1368             TwitterDataModel.Status status;
1369             try
1370             {
1371                 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
1372             }
1373             catch (SerializationException ex)
1374             {
1375                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1376                 return "Json Parse Error(DataContractJsonSerializer)";
1377             }
1378             catch (Exception ex)
1379             {
1380                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1381                 return "Invalid Json!";
1382             }
1383             int tmp;
1384             if (int.TryParse(status.RetweetCount, out tmp))
1385                 retweeted_count = tmp;
1386             return "";
1387         }
1388
1389         public string PostFavAdd(long id)
1390         {
1391             if (MyCommon._endingFlag) return "";
1392
1393             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1394
1395             //if (this.favQueue == null) this.favQueue = new FavoriteQueue(this)
1396
1397             //if (this.favQueue.Contains(id)) this.favQueue.Remove(id)
1398
1399             HttpStatusCode res = HttpStatusCode.BadRequest;
1400             var content = "";
1401             try
1402             {
1403                 res = twCon.CreateFavorites(id, ref content);
1404             }
1405             catch(Exception ex)
1406             {
1407                 //this.favQueue.Add(id)
1408                 //return "Err:->FavoriteQueue:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1409                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1410             }
1411
1412             switch (res)
1413             {
1414                 case HttpStatusCode.OK:
1415                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1416                     //this.favQueue.FavoriteCacheStart();
1417                     if (!_restrictFavCheck) return "";
1418                     break;
1419                 case HttpStatusCode.Unauthorized:
1420                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1421                     return Properties.Resources.Unauthorized;
1422                 case HttpStatusCode.Forbidden:
1423                     var errMsg = GetErrorMessageJson(content);
1424                     if (string.IsNullOrEmpty(errMsg))
1425                     {
1426                         return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1427                     }
1428                     else
1429                     {
1430                         //if (errMsg.Contains("It's great that you like so many updates"))
1431                         //    //this.favQueue.Add(id)
1432                         //    return "Err:->FavoriteQueue:" + errMsg;
1433                         //}
1434                         return "Err:" + errMsg;
1435                     }
1436                 //Case HttpStatusCode.BadGateway, HttpStatusCode.ServiceUnavailable, HttpStatusCode.InternalServerError, HttpStatusCode.RequestTimeout
1437                 //    //this.favQueue.Add(id)
1438                 //    return "Err:->FavoriteQueue:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1439                 default:
1440                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1441             }
1442
1443             //http://twitter.com/statuses/show/id.xml APIを発行して本文を取得
1444
1445             //var content = "";
1446             content = "";
1447             try
1448             {
1449                 res = twCon.ShowStatuses(id, ref content);
1450             }
1451             catch(Exception ex)
1452             {
1453                 return "Err:" + ex.Message;
1454             }
1455
1456             switch (res)
1457             {
1458                 case HttpStatusCode.OK:
1459                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1460                     TwitterDataModel.Status status;
1461                     try
1462                     {
1463                         status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
1464                     }
1465                     catch (SerializationException ex)
1466                     {
1467                         MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1468                         return "Err:Json Parse Error(DataContractJsonSerializer)";
1469                     }
1470                     catch (Exception ex)
1471                     {
1472                         MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1473                         return "Err:Invalid Json!";
1474                     }
1475                     if (status.Favorited)
1476                     {
1477                         return "";
1478                     }
1479                     else
1480                     {
1481                         return "NG(Restricted?)";
1482                     }
1483                 case HttpStatusCode.Unauthorized:
1484                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1485                     return Properties.Resources.Unauthorized;
1486                 case HttpStatusCode.BadRequest:
1487                     return "Err:API Limits?";
1488                 default:
1489                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1490             }
1491         }
1492
1493         public string PostFavRemove(long id)
1494         {
1495             if (MyCommon._endingFlag) return "";
1496
1497             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1498
1499             //if (this.favQueue == null) this.favQueue = new FavoriteQueue(this)
1500
1501             //if (this.favQueue.Contains(id))
1502             //    this.favQueue.Remove(id)
1503             //    return "";
1504             //}
1505
1506             HttpStatusCode res = HttpStatusCode.BadRequest;
1507             var content = "";
1508             try
1509             {
1510                 res = twCon.DestroyFavorites(id, ref content);
1511             }
1512             catch(Exception ex)
1513             {
1514                 return "Err:" + ex.Message;
1515             }
1516
1517             switch (res)
1518             {
1519                 case HttpStatusCode.OK:
1520                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1521                     return "";
1522                 case HttpStatusCode.Unauthorized:
1523                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1524                     return Properties.Resources.Unauthorized;
1525                 case HttpStatusCode.Forbidden:
1526                     var errMsg = GetErrorMessageJson(content);
1527                     if (string.IsNullOrEmpty(errMsg))
1528                     {
1529                         return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1530                     }
1531                     else
1532                     {
1533                         return "Err:" + errMsg;
1534                     }
1535                 default:
1536                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1537             }
1538         }
1539
1540         public string PostUpdateProfile(string name, string url, string location, string description)
1541         {
1542             if (MyCommon._endingFlag) return "";
1543
1544             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1545
1546             HttpStatusCode res = HttpStatusCode.BadRequest;
1547             var content = "";
1548             try
1549             {
1550                 res = twCon.UpdateProfile(name, url, location, description, ref content);
1551             }
1552             catch(Exception ex)
1553             {
1554                 return "Err:" + ex.Message;
1555             }
1556
1557             switch (res)
1558             {
1559                 case HttpStatusCode.OK:
1560                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1561                     return "";
1562                 case HttpStatusCode.Unauthorized:
1563                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1564                     return Properties.Resources.Unauthorized;
1565                 case HttpStatusCode.Forbidden:
1566                     var errMsg = GetErrorMessageJson(content);
1567                     if (string.IsNullOrEmpty(errMsg))
1568                     {
1569                         return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1570                     }
1571                     else
1572                     {
1573                         return "Err:" + errMsg;
1574                     }
1575                 default:
1576                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1577             }
1578         }
1579
1580         public string PostUpdateProfileImage(string filename)
1581         {
1582             if (MyCommon._endingFlag) return "";
1583
1584             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1585
1586             HttpStatusCode res = HttpStatusCode.BadRequest;
1587             var content = "";
1588             try
1589             {
1590                 res = twCon.UpdateProfileImage(new FileInfo(filename), ref content);
1591             }
1592             catch(Exception ex)
1593             {
1594                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1595             }
1596
1597             switch (res)
1598             {
1599                 case HttpStatusCode.OK:
1600                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1601                     return "";
1602                 case HttpStatusCode.Unauthorized:
1603                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1604                     return Properties.Resources.Unauthorized;
1605                 case HttpStatusCode.Forbidden:
1606                     var errMsg = GetErrorMessageJson(content);
1607                     if (string.IsNullOrEmpty(errMsg))
1608                     {
1609                         return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1610                     }
1611                     else
1612                     {
1613                         return "Err:" + errMsg;
1614                     }
1615                 default:
1616                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1617             }
1618         }
1619
1620         public string Username
1621         {
1622             get
1623             {
1624                 return twCon.AuthenticatedUsername;
1625             }
1626         }
1627
1628         public long UserId
1629         {
1630             get
1631             {
1632                 return twCon.AuthenticatedUserId;
1633             }
1634         }
1635
1636         public string Password
1637         {
1638             get
1639             {
1640                 return twCon.Password;
1641             }
1642         }
1643
1644         private static MyCommon.ACCOUNT_STATE _accountState = MyCommon.ACCOUNT_STATE.Valid;
1645         public static MyCommon.ACCOUNT_STATE AccountState
1646         {
1647             get
1648             {
1649                 return _accountState;
1650             }
1651             set
1652             {
1653                 _accountState = value;
1654             }
1655         }
1656
1657         public bool GetIcon
1658         {
1659             set
1660             {
1661                 _getIcon = value;
1662             }
1663         }
1664
1665         public bool TinyUrlResolve
1666         {
1667             set
1668             {
1669                 _tinyUrlResolve = value;
1670             }
1671         }
1672
1673         public bool RestrictFavCheck
1674         {
1675             set
1676             {
1677                 _restrictFavCheck = value;
1678             }
1679         }
1680
1681         public int IconSize
1682         {
1683             set
1684             {
1685                 _iconSz = value;
1686             }
1687         }
1688
1689 #region "バージョンアップ"
1690         public string GetVersionInfo()
1691         {
1692             var content = "";
1693             if (!(new HttpVarious()).GetData(ApplicationSettings.VersionInfoUrl + "?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), null, out content, MyCommon.GetUserAgentString()))
1694             {
1695                 throw new Exception("GetVersionInfo Failed");
1696             }
1697             return content;
1698         }
1699
1700         public string GetTweenBinary(string strVer)
1701         {
1702             try
1703             {
1704                 //本体
1705                 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/Tween" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1706                                                     Path.Combine(MyCommon.settingPath, "TweenNew.exe")))
1707                 {
1708                     return "Err:Download failed";
1709                 }
1710                 //英語リソース
1711                 if (!Directory.Exists(Path.Combine(MyCommon.settingPath, "en")))
1712                 {
1713                     Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, "en"));
1714                 }
1715                 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenResEn" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1716                                                     Path.Combine(Path.Combine(MyCommon.settingPath, "en"), "Tween.resourcesNew.dll")))
1717                 {
1718                     return "Err:Download failed";
1719                 }
1720                 //その他言語圏のリソース。取得失敗しても継続
1721                 //UIの言語圏のリソース
1722                 var curCul = "";
1723                 if (!Thread.CurrentThread.CurrentUICulture.IsNeutralCulture)
1724                 {
1725                     var idx = Thread.CurrentThread.CurrentUICulture.Name.LastIndexOf('-');
1726                     if (idx > -1)
1727                     {
1728                         curCul = Thread.CurrentThread.CurrentUICulture.Name.Substring(0, idx);
1729                     }
1730                     else
1731                     {
1732                         curCul = Thread.CurrentThread.CurrentUICulture.Name;
1733                     }
1734                 }
1735                 else
1736                 {
1737                     curCul = Thread.CurrentThread.CurrentUICulture.Name;
1738                 }
1739                 if (!string.IsNullOrEmpty(curCul) && curCul != "en" && curCul != "ja")
1740                 {
1741                     if (!Directory.Exists(Path.Combine(MyCommon.settingPath, curCul)))
1742                     {
1743                         Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul));
1744                     }
1745                     if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1746                                                         Path.Combine(Path.Combine(MyCommon.settingPath, curCul), "Tween.resourcesNew.dll")))
1747                     {
1748                         //return "Err:Download failed";
1749                     }
1750                 }
1751                 //スレッドの言語圏のリソース
1752                 string curCul2;
1753                 if (!Thread.CurrentThread.CurrentCulture.IsNeutralCulture)
1754                 {
1755                     var idx = Thread.CurrentThread.CurrentCulture.Name.LastIndexOf('-');
1756                     if (idx > -1)
1757                     {
1758                         curCul2 = Thread.CurrentThread.CurrentCulture.Name.Substring(0, idx);
1759                     }
1760                     else
1761                     {
1762                         curCul2 = Thread.CurrentThread.CurrentCulture.Name;
1763                     }
1764                 }
1765                 else
1766                 {
1767                     curCul2 = Thread.CurrentThread.CurrentCulture.Name;
1768                 }
1769                 if (!string.IsNullOrEmpty(curCul2) && curCul2 != "en" && curCul2 != curCul)
1770                 {
1771                     if (!Directory.Exists(Path.Combine(MyCommon.settingPath, curCul2)))
1772                     {
1773                         Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul2));
1774                     }
1775                     if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul2 + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1776                                                     Path.Combine(Path.Combine(MyCommon.settingPath, curCul2), "Tween.resourcesNew.dll")))
1777                     {
1778                         //return "Err:Download failed";
1779                     }
1780                 }
1781
1782                 //アップデータ
1783                 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenUp3.gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1784                                                     Path.Combine(MyCommon.settingPath, "TweenUp3.exe")))
1785                 {
1786                     return "Err:Download failed";
1787                 }
1788                 //シリアライザDLL
1789                 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenDll" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1790                                                     Path.Combine(MyCommon.settingPath, "TweenNew.XmlSerializers.dll")))
1791                 {
1792                     return "Err:Download failed";
1793                 }
1794                 return "";
1795             }
1796             catch(Exception)
1797             {
1798                 return "Err:Download failed";
1799             }
1800         }
1801 #endregion
1802
1803         public bool ReadOwnPost
1804         {
1805             get
1806             {
1807                 return _readOwnPost;
1808             }
1809             set
1810             {
1811                 _readOwnPost = value;
1812             }
1813         }
1814
1815         public int FollowersCount
1816         {
1817             get
1818             {
1819                 return _followersCount;
1820             }
1821         }
1822
1823         public int FriendsCount
1824         {
1825             get
1826             {
1827                 return _friendsCount;
1828             }
1829         }
1830
1831         public int StatusesCount
1832         {
1833             get
1834             {
1835                 return _statusesCount;
1836             }
1837         }
1838
1839         public string Location
1840         {
1841             get
1842             {
1843                 return _location;
1844             }
1845         }
1846
1847         public string Bio
1848         {
1849             get
1850             {
1851                 return _bio;
1852             }
1853         }
1854
1855         public bool UseSsl
1856         {
1857             set
1858             {
1859                 HttpTwitter.UseSsl = value;
1860                 if (value)
1861                 {
1862                     _protocol = "https://";
1863                 }
1864                 else
1865                 {
1866                     _protocol = "http://";
1867                 }
1868             }
1869         }
1870
1871         public string GetTimelineApi(bool read,
1872                                 MyCommon.WORKERTYPE gType,
1873                                 bool more,
1874                                 bool startup)
1875         {
1876             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1877
1878             if (MyCommon._endingFlag) return "";
1879
1880             HttpStatusCode res;
1881             var content = "";
1882             var count = AppendSettingDialog.Instance.CountApi;
1883             if (gType == MyCommon.WORKERTYPE.Reply) count = AppendSettingDialog.Instance.CountApiReply;
1884             if (AppendSettingDialog.Instance.UseAdditionalCount)
1885             {
1886                 if (more && AppendSettingDialog.Instance.MoreCountApi != 0)
1887                 {
1888                     count = AppendSettingDialog.Instance.MoreCountApi;
1889                 }
1890                 else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0 && gType == MyCommon.WORKERTYPE.Timeline)
1891                 {
1892                     count = AppendSettingDialog.Instance.FirstCountApi;
1893                 }
1894             }
1895             try
1896             {
1897                 if (gType == MyCommon.WORKERTYPE.Timeline)
1898                 {
1899                     if (more)
1900                     {
1901                         res = twCon.HomeTimeline(count, this.minHomeTimeline, 0, ref content);
1902                     }
1903                     else
1904                     {
1905                         res = twCon.HomeTimeline(count, 0, 0, ref content);
1906                     }
1907                 }
1908                 else
1909                 {
1910                     if (more)
1911                     {
1912                         res = twCon.Mentions(count, this.minMentions, 0, ref content);
1913                     }
1914                     else
1915                     {
1916                         res = twCon.Mentions(count, 0, 0, ref content);
1917                     }
1918                 }
1919             }
1920             catch(Exception ex)
1921             {
1922                 return "Err:" + ex.Message;
1923             }
1924             switch (res)
1925             {
1926                 case HttpStatusCode.OK:
1927                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1928                     break;
1929                 case HttpStatusCode.Unauthorized:
1930                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1931                     return Properties.Resources.Unauthorized;
1932                 case HttpStatusCode.BadRequest:
1933                     return "Err:API Limits?";
1934                 default:
1935                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1936             }
1937
1938             if (gType == MyCommon.WORKERTYPE.Timeline)
1939             {
1940                 return CreatePostsFromJson(content, gType, null, read, count, ref this.minHomeTimeline);
1941             }
1942             else
1943             {
1944                 return CreatePostsFromJson(content, gType, null, read, count, ref this.minMentions);
1945             }
1946         }
1947
1948         public string GetUserTimelineApi(bool read,
1949                                          int count,
1950                                          string userName,
1951                                          TabClass tab,
1952                                          bool more)
1953         {
1954             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1955
1956             if (MyCommon._endingFlag) return "";
1957
1958             HttpStatusCode res = HttpStatusCode.BadRequest;
1959             var content = "";
1960
1961             if (count == 0) count = 20;
1962             try
1963             {
1964                 if (string.IsNullOrEmpty(userName))
1965                 {
1966                     var target = tab.User;
1967                     if (string.IsNullOrEmpty(target)) return "";
1968                     userName = target;
1969                     res = twCon.UserTimeline(0, target, count, 0, 0, ref content);
1970                 }
1971                 else
1972                 {
1973                     if (more)
1974                     {
1975                         res = twCon.UserTimeline(0, userName, count, tab.OldestId, 0, ref content);
1976                     }
1977                     else
1978                     {
1979                         res = twCon.UserTimeline(0, userName, count, 0, 0, ref content);
1980                     }
1981                 }
1982             }
1983             catch(Exception ex)
1984             {
1985                 return "Err:" + ex.Message;
1986             }
1987             switch (res)
1988             {
1989                 case HttpStatusCode.OK:
1990                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1991                     break;
1992                 case HttpStatusCode.Unauthorized:
1993                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1994                     return "Err:@" + userName + "'s Tweets are protected.";
1995                 case HttpStatusCode.BadRequest:
1996                     return "Err:API Limits?";
1997                 default:
1998                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1999             }
2000
2001             List<TwitterDataModel.Status> items;
2002             try
2003             {
2004                 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
2005             }
2006             catch(SerializationException ex)
2007             {
2008                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2009                 return "Json Parse Error(DataContractJsonSerializer)";
2010             }
2011             catch(Exception ex)
2012             {
2013                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2014                 return "Invalid Json!";
2015             }
2016
2017             foreach (var status in items)
2018             {
2019                 var item = CreatePostsFromStatusData(status);
2020                 if (item == null) continue;
2021                 if (item.StatusId < tab.OldestId) tab.OldestId = item.StatusId;
2022                 item.IsRead = read;
2023                 if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
2024                 if (tab != null) item.RelTabName = tab.TabName;
2025                 //非同期アイコン取得&StatusDictionaryに追加
2026                 TabInformations.GetInstance().AddPost(item);
2027             }
2028
2029             return "";
2030         }
2031
2032         public string GetStatusApi(bool read,
2033                                    Int64 id,
2034                                    ref PostClass post)
2035         {
2036             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2037
2038             if (MyCommon._endingFlag) return "";
2039
2040             HttpStatusCode res = HttpStatusCode.BadRequest;
2041             var content = "";
2042
2043             try
2044             {
2045                 res = twCon.ShowStatuses(id, ref content);
2046             }
2047             catch(Exception ex)
2048             {
2049                 return "Err:" + ex.Message;
2050             }
2051             switch (res)
2052             {
2053                 case HttpStatusCode.OK:
2054                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2055                     break;
2056                 case HttpStatusCode.Unauthorized:
2057                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2058                     return Properties.Resources.Unauthorized;
2059                 case HttpStatusCode.BadRequest:
2060                     return "Err:API Limits?";
2061                 case HttpStatusCode.Forbidden:
2062                     return "Err:protected user's tweet";
2063                 default:
2064                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2065             }
2066
2067             TwitterDataModel.Status status;
2068             try
2069             {
2070                 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
2071             }
2072             catch(SerializationException ex)
2073             {
2074                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2075                 return "Json Parse Error(DataContractJsonSerializer)";
2076             }
2077             catch(Exception ex)
2078             {
2079                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2080                 return "Invalid Json!";
2081             }
2082
2083             var item = CreatePostsFromStatusData(status);
2084             if (item == null) return "Err:Can't create post";
2085             item.IsRead = read;
2086             if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
2087
2088             post = item;
2089             return "";
2090         }
2091
2092         public string GetStatusApi(bool read,
2093                                    Int64 id,
2094                                    TabClass tab)
2095         {
2096             PostClass post = null;
2097             var r = this.GetStatusApi(read, id, ref post);
2098
2099             if (string.IsNullOrEmpty(r))
2100             {
2101                 if (tab != null) post.RelTabName = tab.TabName;
2102                 //非同期アイコン取得&StatusDictionaryに追加
2103                 TabInformations.GetInstance().AddPost(post);
2104             }
2105
2106             return r;
2107         }
2108
2109         private PostClass CreatePostsFromStatusData(TwitterDataModel.Status status)
2110         {
2111             var post = new PostClass();
2112             TwitterDataModel.Entities entities;
2113
2114             post.StatusId = status.Id;
2115             if (status.RetweetedStatus != null)
2116             {
2117                 var retweeted = status.RetweetedStatus;
2118
2119                 post.CreatedAt = MyCommon.DateTimeParse(retweeted.CreatedAt);
2120
2121                 //Id
2122                 post.RetweetedId = retweeted.Id;
2123                 //本文
2124                 post.TextFromApi = retweeted.Text;
2125                 entities = retweeted.Entities;
2126                 //Source取得(htmlの場合は、中身を取り出し)
2127                 post.Source = retweeted.Source;
2128                 //Reply先
2129                 long inReplyToStatusId;
2130                 long.TryParse(retweeted.InReplyToStatusId, out inReplyToStatusId);
2131                 post.InReplyToStatusId = inReplyToStatusId;
2132                 post.InReplyToUser = retweeted.InReplyToScreenName;
2133                 long inReplyToUserId;
2134                 long.TryParse(status.InReplyToUserId, out inReplyToUserId);
2135                 post.InReplyToUserId = inReplyToUserId;
2136
2137                 //幻覚fav対策
2138                 var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
2139                 post.IsFav = tc.Contains(post.RetweetedId);
2140
2141                 if (retweeted.Geo != null) post.PostGeo = new PostClass.StatusGeo {Lat = retweeted.Geo.Coordinates[0], Lng = retweeted.Geo.Coordinates[1]};
2142
2143                 //以下、ユーザー情報
2144                 var user = retweeted.User;
2145
2146                 if (user.ScreenName == null || status.User.ScreenName == null) return null;
2147
2148                 post.UserId = user.Id;
2149                 post.ScreenName = user.ScreenName;
2150                 post.Nickname = user.Name.Trim();
2151                 post.ImageUrl = user.ProfileImageUrlHttps;
2152                 post.IsProtect = user.Protected;
2153
2154                 //Retweetした人
2155                 post.RetweetedBy = status.User.ScreenName;
2156                 post.RetweetedByUserId = status.User.Id;
2157                 post.IsMe = post.RetweetedBy.ToLower().Equals(_uname);
2158             }
2159             else
2160             {
2161                 post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
2162                 //本文
2163                 post.TextFromApi = status.Text;
2164                 entities = status.Entities;
2165                 //Source取得(htmlの場合は、中身を取り出し)
2166                 post.Source = status.Source;
2167                 long inReplyToStatusId;
2168                 long.TryParse(status.InReplyToStatusId, out inReplyToStatusId);
2169                 post.InReplyToStatusId = inReplyToStatusId;
2170                 post.InReplyToUser = status.InReplyToScreenName;
2171                 long inReplyToUserId;
2172                 long.TryParse(status.InReplyToUserId, out inReplyToUserId);
2173                 post.InReplyToUserId = inReplyToUserId;
2174
2175                 if (status.Geo != null) post.PostGeo = new PostClass.StatusGeo {Lat = status.Geo.Coordinates[0], Lng = status.Geo.Coordinates[1]};
2176
2177                 //以下、ユーザー情報
2178                 var user = status.User;
2179
2180                 if (user.ScreenName == null) return null;
2181
2182                 post.UserId = user.Id;
2183                 post.ScreenName = user.ScreenName;
2184                 post.Nickname = user.Name.Trim();
2185                 post.ImageUrl = user.ProfileImageUrlHttps;
2186                 post.IsProtect = user.Protected;
2187                 post.IsMe = post.ScreenName.ToLower().Equals(_uname);
2188
2189                 //幻覚fav対策
2190                 var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
2191                 post.IsFav = tc.Contains(post.StatusId) && TabInformations.GetInstance()[post.StatusId].IsFav;
2192             }
2193             //HTMLに整形
2194             string textFromApi = post.TextFromApi;
2195             post.Text = CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
2196             post.TextFromApi = textFromApi;
2197             post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
2198             post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
2199             post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
2200
2201             //Source整形
2202             CreateSource(post);
2203
2204             post.IsReply = post.ReplyToList.Contains(_uname);
2205             post.IsExcludeReply = false;
2206
2207             if (post.IsMe)
2208             {
2209                 post.IsOwl = false;
2210             }
2211             else
2212             {
2213                 if (followerId.Count > 0) post.IsOwl = !followerId.Contains(post.UserId);
2214             }
2215
2216             post.IsDm = false;
2217             return post;
2218         }
2219
2220         private string CreatePostsFromJson(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId)
2221         {
2222             List<TwitterDataModel.Status> items;
2223             try
2224             {
2225                 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
2226             }
2227             catch(SerializationException ex)
2228             {
2229                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2230                 return "Json Parse Error(DataContractJsonSerializer)";;
2231             }
2232             catch(Exception ex)
2233             {
2234                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2235                 return "Invalid Json!";
2236             }
2237
2238             foreach (var status in items)
2239             {
2240                 PostClass post = null;
2241                 post = CreatePostsFromStatusData(status);
2242                 if (post == null) continue;
2243
2244                 if (minimumId > post.StatusId) minimumId = post.StatusId;
2245                 //二重取得回避
2246                 lock (LockObj)
2247                 {
2248                     if (tab == null)
2249                     {
2250                         if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2251                     }
2252                     else
2253                     {
2254                         if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2255                     }
2256                 }
2257
2258                 //RT禁止ユーザーによるもの
2259                 if (post.RetweetedId > 0 && this.noRTId.Contains(post.RetweetedByUserId)) continue;
2260
2261                 post.IsRead = read;
2262                 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2263
2264                 if (tab != null) post.RelTabName = tab.TabName;
2265                 //非同期アイコン取得&StatusDictionaryに追加
2266                 TabInformations.GetInstance().AddPost(post);
2267             }
2268
2269             return "";
2270         }
2271
2272         // API v1.1
2273         private string CreatePostsFromSearch11Json(string content, TabClass tab, bool read, int count, ref long minimumId, bool more)
2274         {
2275             TwitterDataModel.SearchResult11 items;
2276             try
2277             {
2278                 items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResult11>(content);
2279             }
2280             catch (SerializationException ex)
2281             {
2282                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2283                 return "Json Parse Error(DataContractJsonSerializer)";
2284             }
2285             catch (Exception ex)
2286             {
2287                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2288                 return "Invalid Json!";
2289             }
2290             foreach (var result in items.Statuses)
2291             {
2292                 PostClass post = null;
2293                 post = CreatePostsFromStatusData(result);
2294                 if (post == null) continue;
2295
2296                 if (minimumId > post.StatusId) minimumId = post.StatusId;
2297                 if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId;
2298                 //二重取得回避
2299                 lock (LockObj)
2300                 {
2301                     if (tab == null)
2302                     {
2303                         if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2304                     }
2305                     else
2306                     {
2307                         if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2308                     }
2309                 }
2310
2311                 post.IsRead = read;
2312                 if ((post.IsMe && !read) && this._readOwnPost) post.IsRead = true;
2313
2314                 if (tab != null) post.RelTabName = tab.TabName;
2315                 //非同期アイコン取得&StatusDictionaryに追加
2316                 TabInformations.GetInstance().AddPost(post);
2317             }
2318
2319             return "";
2320         }
2321
2322         private string CreatePostsFromSearchJson(string content, TabClass tab, bool read, int count, ref long minimumId, bool more)
2323         {
2324             TwitterDataModel.SearchResult items;
2325             try
2326             {
2327                 items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResult>(content);
2328             }
2329             catch (SerializationException ex)
2330             {
2331                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2332                 return "Json Parse Error(DataContractJsonSerializer)";
2333             }
2334             catch (Exception ex)
2335             {
2336                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2337                 return "Invalid Json!";
2338             }
2339             foreach (var result in items.Results)
2340             {
2341                 PostClass post = null;
2342                 post = CreatePostsFromSearchResultData(result);
2343                 if (post == null) continue;
2344
2345                 if (minimumId > post.StatusId) minimumId = post.StatusId;
2346                 if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId;
2347                 //二重取得回避
2348                 lock (LockObj)
2349                 {
2350                     if (tab == null)
2351                     {
2352                         if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2353                     }
2354                     else
2355                     {
2356                         if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2357                     }
2358                 }
2359
2360                 post.IsRead = read;
2361                 if ((post.IsMe && !read) && this._readOwnPost) post.IsRead = true;
2362
2363                 if (tab != null) post.RelTabName = tab.TabName;
2364                 //非同期アイコン取得&StatusDictionaryに追加
2365                 TabInformations.GetInstance().AddPost(post);
2366             }
2367
2368             return "";
2369         }
2370
2371         private PostClass CreatePostsFromSearchResultData(TwitterDataModel.SearchResultData status)
2372         {
2373             var post = new PostClass();
2374             post.StatusId = status.Id;
2375             post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
2376             //本文
2377             post.TextFromApi = status.Text;
2378             var entities = status.Entities;
2379             post.Source = WebUtility.HtmlDecode(status.Source);
2380             post.InReplyToStatusId = status.InReplyToStatusId;
2381             post.InReplyToUser = status.ToUser;
2382             post.InReplyToUserId = !status.ToUserId.HasValue ? 0 : (long)status.ToUserId;
2383
2384             if (status.Geo != null) post.PostGeo = new PostClass.StatusGeo { Lat = status.Geo.Coordinates[0], Lng = status.Geo.Coordinates[1] };
2385
2386             if (status.FromUser == null) return null;
2387
2388             post.UserId = status.FromUserId;
2389             post.ScreenName = status.FromUser;
2390             post.Nickname = status.FromUserName.Trim();
2391             post.ImageUrl = status.ProfileImageUrl;
2392             post.IsProtect = false;
2393             post.IsMe = post.ScreenName.ToLower().Equals(this._uname);
2394
2395             //幻覚fav対策
2396             var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
2397             post.IsFav = tc.Contains(post.StatusId) && TabInformations.GetInstance()[post.StatusId].IsFav;
2398
2399             //HTMLに整形
2400             string textFromApi = post.TextFromApi;
2401             post.Text = this.CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
2402             post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
2403             post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
2404             post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
2405
2406             //Source整形
2407             this.CreateSource(post);
2408
2409             post.IsReply = post.ReplyToList.Contains(this._uname);
2410             post.IsExcludeReply = false;
2411             post.IsOwl = false;
2412             post.IsDm = false;
2413
2414             return post;
2415         }
2416
2417         private string CreatePostsFromPhoenixSearch(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId, ref string nextPageQuery)
2418         {
2419             TwitterDataModel.SearchResultPhoenix items;
2420             try
2421             {
2422                 items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResultPhoenix>(content);
2423             }
2424             catch(SerializationException ex)
2425             {
2426                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2427                 return "Json Parse Error(DataContractJsonSerializer)";
2428             }
2429             catch(Exception ex)
2430             {
2431                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2432                 return "Invalid Json!";
2433             }
2434
2435             nextPageQuery = items.NextPage;
2436
2437             foreach (var status in items.Statuses)
2438             {
2439                 PostClass post = null;
2440                 post = CreatePostsFromStatusData(status);
2441                 if (post == null) continue;
2442
2443                 if (minimumId > post.StatusId) minimumId = post.StatusId;
2444                 //二重取得回避
2445                 lock (LockObj)
2446                 {
2447                     if (tab == null)
2448                     {
2449                         if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2450                     }
2451                     else
2452                     {
2453                         if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2454                     }
2455                 }
2456
2457                 post.IsRead = read;
2458                 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2459
2460                 if (tab != null) post.RelTabName = tab.TabName;
2461                 //非同期アイコン取得&StatusDictionaryに追加
2462                 TabInformations.GetInstance().AddPost(post);
2463             }
2464
2465             return string.IsNullOrEmpty(items.ErrMsg) ? "" : "Err:" + items.ErrMsg;
2466         }
2467
2468         public string GetListStatus(bool read,
2469                                 TabClass tab,
2470                                 bool more,
2471                                 bool startup)
2472         {
2473             if (MyCommon._endingFlag) return "";
2474
2475             HttpStatusCode res;
2476             var content = "";
2477             int count;
2478             if (AppendSettingDialog.Instance.UseAdditionalCount)
2479             {
2480                 count = AppendSettingDialog.Instance.ListCountApi;
2481                 if (count == 0)
2482                 {
2483                     if (more && AppendSettingDialog.Instance.MoreCountApi != 0)
2484                     {
2485                         count = AppendSettingDialog.Instance.MoreCountApi;
2486                     }
2487                     else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0)
2488                     {
2489                         count = AppendSettingDialog.Instance.FirstCountApi;
2490                     }
2491                     else
2492                     {
2493                         count = AppendSettingDialog.Instance.CountApi;
2494                     }
2495                 }
2496             }
2497             else
2498             {
2499                 count = AppendSettingDialog.Instance.CountApi;
2500             }
2501             try
2502             {
2503                 if (more)
2504                 {
2505                     res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, tab.OldestId, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content);
2506                 }
2507                 else
2508                 {
2509                     res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, 0, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content);
2510                 }
2511             }
2512             catch(Exception ex)
2513             {
2514                 return "Err:" + ex.Message;
2515             }
2516             switch (res)
2517             {
2518                 case HttpStatusCode.OK:
2519                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2520                     break;
2521                 case HttpStatusCode.Unauthorized:
2522                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2523                     return Properties.Resources.Unauthorized;
2524                 case HttpStatusCode.BadRequest:
2525                     return "Err:API Limits?";
2526                 default:
2527                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2528             }
2529
2530             return CreatePostsFromJson(content, MyCommon.WORKERTYPE.List, tab, read, count, ref tab.OldestId);
2531         }
2532
2533         /// <summary>
2534         /// startStatusId からリプライ先の発言を辿る。発言は posts 以外からは検索しない。
2535         /// </summary>
2536         /// <returns>posts の中から検索されたリプライチェインの末端</returns>
2537         internal static PostClass FindTopOfReplyChain(IDictionary<Int64, PostClass> posts, Int64 startStatusId)
2538         {
2539             if (!posts.ContainsKey(startStatusId))
2540                 throw new ArgumentException("startStatusId (" + startStatusId + ") が posts の中から見つかりませんでした。");
2541
2542             var nextPost = posts[startStatusId];
2543             while (nextPost.InReplyToStatusId != 0)
2544             {
2545                 if (!posts.ContainsKey(nextPost.InReplyToStatusId))
2546                     break;
2547                 nextPost = posts[nextPost.InReplyToStatusId];
2548             }
2549
2550             return nextPost;
2551         }
2552
2553         public string GetRelatedResult(bool read, TabClass tab)
2554         {
2555             var rslt = "";
2556             var relPosts = new Dictionary<Int64, PostClass>();
2557             if (tab.RelationTargetPost.TextFromApi.Contains("@") && tab.RelationTargetPost.InReplyToStatusId == 0)
2558             {
2559                 //検索結果対応
2560                 var p = TabInformations.GetInstance()[tab.RelationTargetPost.StatusId];
2561                 if (p != null && p.InReplyToStatusId > 0)
2562                 {
2563                     tab.RelationTargetPost = p;
2564                 }
2565                 else
2566                 {
2567                     rslt = this.GetStatusApi(read, tab.RelationTargetPost.StatusId, ref p);
2568                     if (!string.IsNullOrEmpty(rslt)) return rslt;
2569                     tab.RelationTargetPost = p;
2570                 }
2571             }
2572             relPosts.Add(tab.RelationTargetPost.StatusId, tab.RelationTargetPost.Clone());
2573
2574             // 一周目: 非公式な related_results API を使用してリプライチェインを辿る
2575             var nextPost = relPosts[tab.RelationTargetPost.StatusId];
2576             var loopCount = 1;
2577             do
2578             {
2579                 rslt = this.GetRelatedResultsApi(nextPost, relPosts);
2580                 if (!string.IsNullOrEmpty(rslt)) break;
2581                 nextPost = FindTopOfReplyChain(relPosts, nextPost.StatusId);
2582             } while (nextPost.InReplyToStatusId != 0 && loopCount++ <= 5);
2583
2584             // 二周目: in_reply_to_status_id を使用してリプライチェインを辿る
2585             nextPost = FindTopOfReplyChain(relPosts, tab.RelationTargetPost.StatusId);
2586             loopCount = 1;
2587             while (nextPost.InReplyToStatusId != 0 && loopCount++ <= 20)
2588             {
2589                 var inReplyToId = nextPost.InReplyToStatusId;
2590
2591                 var inReplyToPost = TabInformations.GetInstance()[inReplyToId];
2592                 if (inReplyToPost != null)
2593                 {
2594                     inReplyToPost = inReplyToPost.Clone();
2595                 }
2596                 else
2597                 {
2598                     var errorText = this.GetStatusApi(read, inReplyToId, ref inReplyToPost);
2599                     if (!string.IsNullOrEmpty(errorText))
2600                     {
2601                         rslt = errorText;
2602                         break;
2603                     }
2604                 }
2605
2606                 relPosts.Add(inReplyToPost.StatusId, inReplyToPost);
2607
2608                 nextPost = FindTopOfReplyChain(relPosts, nextPost.StatusId);
2609             }
2610
2611             //MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む
2612             var text = tab.RelationTargetPost.Text;
2613             var ma = Twitter.StatusUrlRegex.Matches(text).Cast<Match>()
2614                 .Concat(Twitter.ThirdPartyStatusUrlRegex.Matches(text).Cast<Match>());
2615             foreach (var _match in ma)
2616             {
2617                 Int64 _statusId;
2618                 if (Int64.TryParse(_match.Groups["StatusId"].Value, out _statusId))
2619                 {
2620                     if (relPosts.ContainsKey(_statusId))
2621                         continue;
2622
2623                     PostClass p = null;
2624                     var _post = TabInformations.GetInstance()[_statusId];
2625                     if (_post == null)
2626                     {
2627                         this.GetStatusApi(read, _statusId, ref p);
2628                     }
2629                     else
2630                     {
2631                         p = _post.Clone();
2632                     }
2633
2634                     if (p != null)
2635                         relPosts.Add(p.StatusId, p);
2636                 }
2637             }
2638
2639             relPosts.Values.ToList().ForEach(p =>
2640             {
2641                 if (p.IsMe && !read && this._readOwnPost)
2642                     p.IsRead = true;
2643                 else
2644                     p.IsRead = read;
2645
2646                 p.RelTabName = tab.TabName;
2647                 TabInformations.GetInstance().AddPost(p);
2648             });
2649
2650             return rslt;
2651         }
2652
2653         private string GetRelatedResultsApi(PostClass post,
2654                                             IDictionary<Int64, PostClass> relatedPosts)
2655         {
2656             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2657
2658             if (MyCommon._endingFlag) return "";
2659
2660             HttpStatusCode res = HttpStatusCode.BadRequest;
2661             var content = "";
2662             try
2663             {
2664                 if (post.RetweetedId > 0)
2665                 {
2666                     res = twCon.GetRelatedResults(post.RetweetedId, ref content);
2667                 }
2668                 else
2669                 {
2670                     res = twCon.GetRelatedResults(post.StatusId, ref content);
2671                 }
2672             }
2673             catch(Exception ex)
2674             {
2675                 return "Err:" + ex.Message;
2676             }
2677             switch (res)
2678             {
2679                 case HttpStatusCode.OK:
2680                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2681                     break;
2682                 case HttpStatusCode.Unauthorized:
2683                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2684                     return Properties.Resources.Unauthorized;
2685                 case HttpStatusCode.BadRequest:
2686                     return "Err:API Limits?";
2687                 default:
2688                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2689             }
2690
2691             List<TwitterDataModel.RelatedResult> items;
2692             try
2693             {
2694                 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.RelatedResult>>(content);
2695             }
2696             catch(SerializationException ex)
2697             {
2698                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2699                 return "Json Parse Error(DataContractJsonSerializer)";
2700             }
2701             catch(Exception ex)
2702             {
2703                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2704                 return "Invalid Json!";
2705             }
2706
2707             foreach (var relatedData in items)
2708             {
2709                 foreach (var result in relatedData.Results)
2710                 {
2711                     var item = CreatePostsFromStatusData(result.Status);
2712                     if (item == null) continue;
2713                     //非同期アイコン取得&StatusDictionaryに追加
2714                     if (!relatedPosts.ContainsKey(item.StatusId))
2715                         relatedPosts.Add(item.StatusId, item);
2716                 }
2717             }
2718
2719             return "";
2720         }
2721
2722         public string GetSearch(bool read,
2723                             TabClass tab,
2724                             bool more)
2725         {
2726             if (MyCommon._endingFlag) return "";
2727
2728             HttpStatusCode res;
2729             var content = "";
2730             var page = 0;
2731             var sinceId = 0L;
2732             var count = 100;
2733             if (AppendSettingDialog.Instance.UseAdditionalCount &&
2734                 AppendSettingDialog.Instance.SearchCountApi != 0)
2735             {
2736                 count = AppendSettingDialog.Instance.SearchCountApi;
2737             }
2738             else
2739             {
2740                 count = AppendSettingDialog.Instance.CountApi;
2741             }
2742             if (more)
2743             {
2744                 page = tab.GetSearchPage(count);
2745             }
2746             else
2747             {
2748                 sinceId = tab.SinceId;
2749             }
2750
2751             try
2752             {
2753                 // TODO:一時的に40>100件に 件数変更UI作成の必要あり
2754                 res = twCon.Search(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content);
2755             }
2756             catch(Exception ex)
2757             {
2758                 return "Err:" + ex.Message;
2759             }
2760             switch (res)
2761             {
2762                 case HttpStatusCode.BadRequest:
2763                     return "Invalid query";
2764                 case HttpStatusCode.NotFound:
2765                     return "Invalid query";
2766                 case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある
2767                     return "Search API Limit?";
2768                 case HttpStatusCode.OK:
2769                     break;
2770                 default:
2771                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2772             }
2773
2774             if (!TabInformations.GetInstance().ContainsTab(tab)) return "";
2775
2776             if (HttpTwitter.API11Enabled)
2777                 return this.CreatePostsFromSearch11Json(content, tab, read, count, ref tab.OldestId, more);
2778             else
2779                 return this.CreatePostsFromSearchJson(content, tab, read, count, ref tab.OldestId, more);
2780         }
2781
2782         public string GetPhoenixSearch(bool read,
2783                                 TabClass tab,
2784                                 bool more)
2785         {
2786             if (MyCommon._endingFlag) return "";
2787
2788             HttpStatusCode res;
2789             var content = "";
2790             var page = 0;
2791             var sinceId = 0L;
2792             var count = 100;
2793             var querystr = "";
2794             if (AppendSettingDialog.Instance.UseAdditionalCount &&
2795                 AppendSettingDialog.Instance.SearchCountApi != 0)
2796             {
2797                 count = AppendSettingDialog.Instance.SearchCountApi;
2798             }
2799             if (more)
2800             {
2801                 page = tab.GetSearchPage(count);
2802                 if (!string.IsNullOrEmpty(tab.NextPageQuery))
2803                 {
2804                     querystr = tab.NextPageQuery;
2805                 }
2806             }
2807             else
2808             {
2809                 sinceId = tab.SinceId;
2810             }
2811
2812             try
2813             {
2814                 if (string.IsNullOrEmpty(querystr))
2815                 {
2816                     res = twCon.PhoenixSearch(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content);
2817                 }
2818                 else
2819                 {
2820                     res = twCon.PhoenixSearch(querystr, ref content);
2821                 }
2822             }
2823             catch(Exception ex)
2824             {
2825                 return "Err:" + ex.Message;
2826             }
2827             switch (res)
2828             {
2829                 case HttpStatusCode.BadRequest:
2830                     return "Invalid query";
2831                 case HttpStatusCode.NotFound:
2832                     return "Invalid query";
2833                 case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある
2834                     return "Search API Limit?";
2835                 case HttpStatusCode.OK:
2836                     break;
2837                 default:
2838                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2839             }
2840
2841             if (!TabInformations.GetInstance().ContainsTab(tab)) return "";
2842
2843             //// TODO
2844             //// 遡るための情報max_idやnext_pageの情報を保持する
2845
2846             string nextPageQuery = tab.NextPageQuery;
2847             var ret = CreatePostsFromPhoenixSearch(content, MyCommon.WORKERTYPE.PublicSearch, tab, read, count, ref tab.OldestId, ref nextPageQuery);
2848             tab.NextPageQuery = nextPageQuery;
2849             return ret;
2850         }
2851
2852         private string CreateDirectMessagesFromJson(string content, MyCommon.WORKERTYPE gType, bool read)
2853         {
2854             List<TwitterDataModel.Directmessage> item;
2855             try
2856             {
2857                 if (gType == MyCommon.WORKERTYPE.UserStream)
2858                 {
2859                     var itm = MyCommon.CreateDataFromJson<List<TwitterDataModel.DirectmessageEvent>>(content);
2860                     item = new List<TwitterDataModel.Directmessage>();
2861                     foreach (var dat in itm)
2862                     {
2863                         item.Add(dat.Directmessage);
2864                     }
2865                 }
2866                 else
2867                 {
2868                     item = MyCommon.CreateDataFromJson<List<TwitterDataModel.Directmessage>>(content);
2869                 }
2870             }
2871             catch(SerializationException ex)
2872             {
2873                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2874                 return "Json Parse Error(DataContractJsonSerializer)";
2875             }
2876             catch(Exception ex)
2877             {
2878                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2879                 return "Invalid Json!";
2880             }
2881
2882             foreach (var message in item)
2883             {
2884                 var post = new PostClass();
2885                 try
2886                 {
2887                     post.StatusId = message.Id;
2888                     if (gType != MyCommon.WORKERTYPE.UserStream)
2889                     {
2890                         if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2891                         {
2892                             if (minDirectmessage > post.StatusId) minDirectmessage = post.StatusId;
2893                         }
2894                         else
2895                         {
2896                             if (minDirectmessageSent > post.StatusId) minDirectmessageSent = post.StatusId;
2897                         }
2898                     }
2899
2900                     //二重取得回避
2901                     lock (LockObj)
2902                     {
2903                         if (TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.DirectMessage).Contains(post.StatusId)) continue;
2904                     }
2905                     //sender_id
2906                     //recipient_id
2907                     post.CreatedAt = MyCommon.DateTimeParse(message.CreatedAt);
2908                     //本文
2909                     post.TextFromApi = message.Text;
2910                     //HTMLに整形
2911                     post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, post.Media);
2912                     post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
2913                     post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
2914                     post.IsFav = false;
2915
2916                     //以下、ユーザー情報
2917                     TwitterDataModel.User user;
2918                     if (gType == MyCommon.WORKERTYPE.UserStream)
2919                     {
2920                         if (twCon.AuthenticatedUsername.Equals(message.Recipient.ScreenName, StringComparison.CurrentCultureIgnoreCase))
2921                         {
2922                             user = message.Sender;
2923                             post.IsMe = false;
2924                             post.IsOwl = true;
2925                         }
2926                         else
2927                         {
2928                             user = message.Recipient;
2929                             post.IsMe = true;
2930                             post.IsOwl = false;
2931                         }
2932                     }
2933                     else
2934                     {
2935                         if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2936                         {
2937                             user = message.Sender;
2938                             post.IsMe = false;
2939                             post.IsOwl = true;
2940                         }
2941                         else
2942                         {
2943                             user = message.Recipient;
2944                             post.IsMe = true;
2945                             post.IsOwl = false;
2946                         }
2947                     }
2948
2949                     post.UserId = user.Id;
2950                     post.ScreenName = user.ScreenName;
2951                     post.Nickname = user.Name.Trim();
2952                     post.ImageUrl = user.ProfileImageUrlHttps;
2953                     post.IsProtect = user.Protected;
2954                 }
2955                 catch(Exception ex)
2956                 {
2957                     MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2958                     MessageBox.Show("Parse Error(CreateDirectMessagesFromJson)");
2959                     continue;
2960                 }
2961
2962                 post.IsRead = read;
2963                 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2964                 post.IsReply = false;
2965                 post.IsExcludeReply = false;
2966                 post.IsDm = true;
2967
2968                 TabInformations.GetInstance().AddPost(post);
2969             }
2970
2971             return "";
2972
2973         }
2974
2975         public string GetDirectMessageApi(bool read,
2976                                 MyCommon.WORKERTYPE gType,
2977                                 bool more)
2978         {
2979             if (MyCommon._endingFlag) return "";
2980
2981             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2982
2983             if (this.AccessLevel == TwitterApiAccessLevel.Read || this.AccessLevel == TwitterApiAccessLevel.ReadWrite)
2984             {
2985                 return "Auth Err:try to re-authorization.";
2986             }
2987
2988             HttpStatusCode res = HttpStatusCode.BadRequest;
2989             var content = "";
2990
2991             try
2992             {
2993                 if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2994                 {
2995                     if (more)
2996                     {
2997                         res = twCon.DirectMessages(20, minDirectmessage, 0, ref content);
2998                     }
2999                     else
3000                     {
3001                         res = twCon.DirectMessages(20, 0, 0, ref content);
3002                     }
3003                 }
3004                 else
3005                 {
3006                     if (more)
3007                     {
3008                         res = twCon.DirectMessagesSent(20, minDirectmessageSent, 0, ref content);
3009                     }
3010                     else
3011                     {
3012                         res = twCon.DirectMessagesSent(20, 0, 0, ref content);
3013                     }
3014                 }
3015             }
3016             catch(Exception ex)
3017             {
3018                 return "Err:" + ex.Message;
3019             }
3020
3021             switch (res)
3022             {
3023                 case HttpStatusCode.OK:
3024                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3025                     break;
3026                 case HttpStatusCode.Unauthorized:
3027                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3028                     return Properties.Resources.Unauthorized;
3029                 case HttpStatusCode.BadRequest:
3030                     return "Err:API Limits?";
3031                 default:
3032                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3033             }
3034
3035             return CreateDirectMessagesFromJson(content, gType, read);
3036         }
3037
3038         static int page_ = 1;
3039         public string GetFavoritesApi(bool read,
3040                             MyCommon.WORKERTYPE gType,
3041                             bool more)
3042         {
3043             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3044
3045             if (MyCommon._endingFlag) return "";
3046
3047             HttpStatusCode res;
3048             var content = "";
3049             var count = AppendSettingDialog.Instance.CountApi;
3050             if (AppendSettingDialog.Instance.UseAdditionalCount &&
3051                 AppendSettingDialog.Instance.FavoritesCountApi != 0)
3052             {
3053                 count = AppendSettingDialog.Instance.FavoritesCountApi;
3054             }
3055
3056             // 前ページ取得の場合はページカウンタをインクリメント、それ以外の場合はページカウンタリセット
3057             if (more)
3058             {
3059                 page_++;
3060             }
3061             else
3062             {
3063                 page_ = 1;
3064             }
3065
3066             try
3067             {
3068                 res = twCon.Favorites(count, page_, ref content);
3069             }
3070             catch(Exception ex)
3071             {
3072                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3073             }
3074
3075             switch (res)
3076             {
3077                 case HttpStatusCode.OK:
3078                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3079                     break;
3080                 case HttpStatusCode.Unauthorized:
3081                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3082                     return Properties.Resources.Unauthorized;
3083                 case HttpStatusCode.BadRequest:
3084                     return "Err:API Limits?";
3085                 default:
3086                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3087             }
3088
3089             var serializer = new DataContractJsonSerializer(typeof(List<TwitterDataModel.Status>));
3090             List<TwitterDataModel.Status> item;
3091
3092             try
3093             {
3094                 item = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
3095             }
3096             catch(SerializationException ex)
3097             {
3098                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3099                 return "Json Parse Error(DataContractJsonSerializer)";
3100             }
3101             catch(Exception ex)
3102             {
3103                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3104                 return "Invalid Json!";
3105             }
3106
3107             foreach (var status in item)
3108             {
3109                 var post = new PostClass();
3110                 TwitterDataModel.Entities entities;
3111
3112                 try
3113                 {
3114                     post.StatusId = status.Id;
3115                     //二重取得回避
3116                     lock (LockObj)
3117                     {
3118                         if (TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Contains(post.StatusId)) continue;
3119                     }
3120                     //Retweet判定
3121                     if (status.RetweetedStatus != null)
3122                     {
3123                         var retweeted = status.RetweetedStatus;
3124                         post.CreatedAt = MyCommon.DateTimeParse(retweeted.CreatedAt);
3125
3126                         //Id
3127                         post.RetweetedId = post.StatusId;
3128                         //本文
3129                         post.TextFromApi = retweeted.Text;
3130                         entities = retweeted.Entities;
3131                         //Source取得(htmlの場合は、中身を取り出し)
3132                         post.Source = retweeted.Source;
3133                         //Reply先
3134                         long inReplyToStatusId;
3135                         long.TryParse(retweeted.InReplyToStatusId, out inReplyToStatusId);
3136                         post.InReplyToStatusId = inReplyToStatusId;
3137                         post.InReplyToUser = retweeted.InReplyToScreenName;
3138                         long inReplyToUserId;
3139                         long.TryParse(retweeted.InReplyToUserId, out inReplyToUserId);
3140                         post.InReplyToUserId = inReplyToUserId;
3141                         post.IsFav = true;
3142
3143                         //以下、ユーザー情報
3144                         var user = retweeted.User;
3145                         post.UserId = user.Id;
3146                         post.ScreenName = user.ScreenName;
3147                         post.Nickname = user.Name.Trim();
3148                         post.ImageUrl = user.ProfileImageUrlHttps;
3149                         post.IsProtect = user.Protected;
3150
3151                         //Retweetした人
3152                         post.RetweetedBy = status.User.ScreenName;
3153                         post.IsMe = post.RetweetedBy.ToLower().Equals(_uname);
3154                     }
3155                     else
3156                     {
3157                         post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
3158
3159                         //本文
3160                         post.TextFromApi = status.Text;
3161                         entities = status.Entities;
3162                         //Source取得(htmlの場合は、中身を取り出し)
3163                         post.Source = status.Source;
3164                         long inReplyToStatusId;
3165                         long.TryParse(status.InReplyToStatusId, out inReplyToStatusId);
3166                         post.InReplyToStatusId = inReplyToStatusId;
3167                         post.InReplyToUser = status.InReplyToScreenName;
3168                         long inReplyToUserId;
3169                         long.TryParse(status.InReplyToUserId, out inReplyToUserId);
3170                         post.InReplyToUserId = inReplyToUserId;
3171
3172                         post.IsFav = true;
3173
3174                         //以下、ユーザー情報
3175                         var user = status.User;
3176                         post.UserId = user.Id;
3177                         post.ScreenName = user.ScreenName;
3178                         post.Nickname = user.Name.Trim();
3179                         post.ImageUrl = user.ProfileImageUrlHttps;
3180                         post.IsProtect = user.Protected;
3181                         post.IsMe = post.ScreenName.ToLower().Equals(_uname);
3182                     }
3183                     //HTMLに整形
3184                     string textFromApi = post.TextFromApi;
3185                     post.Text = CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
3186                     post.TextFromApi = textFromApi;
3187                     post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
3188                     post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
3189                     post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
3190                     //Source整形
3191                     CreateSource(post);
3192
3193                     post.IsRead = read;
3194                     post.IsReply = post.ReplyToList.Contains(_uname);
3195                     post.IsExcludeReply = false;
3196
3197                     if (post.IsMe)
3198                     {
3199                         post.IsOwl = false;
3200                     }
3201                     else
3202                     {
3203                         if (followerId.Count > 0) post.IsOwl = !followerId.Contains(post.UserId);
3204                     }
3205
3206                     post.IsDm = false;
3207                 }
3208                 catch(Exception ex)
3209                 {
3210                     MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3211                     continue;
3212                 }
3213
3214                 TabInformations.GetInstance().AddPost(post);
3215
3216             }
3217
3218             return "";
3219         }
3220
3221         private string ReplaceTextFromApi(string text, TwitterDataModel.Entities entities)
3222         {
3223             if (entities != null)
3224             {
3225                 if (entities.Urls != null)
3226                 {
3227                     foreach (var m in entities.Urls)
3228                     {
3229                         if (!string.IsNullOrEmpty(m.DisplayUrl)) text = text.Replace(m.Url, m.DisplayUrl);
3230                     }
3231                 }
3232                 if (entities.Media != null)
3233                 {
3234                     foreach (var m in entities.Media)
3235                     {
3236                         if (!string.IsNullOrEmpty(m.DisplayUrl)) text = text.Replace(m.Url, m.DisplayUrl);
3237                     }
3238                 }
3239             }
3240             return text;
3241         }
3242
3243         public string GetFollowersApi()
3244         {
3245             if (MyCommon._endingFlag) return "";
3246             long cursor = -1;
3247             var tmpFollower = new List<long>(followerId);
3248
3249             followerId.Clear();
3250             do
3251             {
3252                 var ret = FollowerApi(ref cursor);
3253                 if (!string.IsNullOrEmpty(ret))
3254                 {
3255                     followerId.Clear();
3256                     followerId.AddRange(tmpFollower);
3257                     _GetFollowerResult = false;
3258                     return ret;
3259                 }
3260             } while (cursor > 0);
3261
3262             TabInformations.GetInstance().RefreshOwl(followerId);
3263
3264             _GetFollowerResult = true;
3265             return "";
3266         }
3267
3268         public bool GetFollowersSuccess
3269         {
3270             get
3271             {
3272                 return _GetFollowerResult;
3273             }
3274         }
3275
3276         private string FollowerApi(ref long cursor)
3277         {
3278             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3279
3280             HttpStatusCode res = HttpStatusCode.BadRequest;
3281             var content = "";
3282             try
3283             {
3284                 res = twCon.FollowerIds(cursor, ref content);
3285             }
3286             catch(Exception ex)
3287             {
3288                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3289             }
3290
3291             switch (res)
3292             {
3293                 case HttpStatusCode.OK:
3294                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3295                     break;
3296                 case HttpStatusCode.Unauthorized:
3297                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3298                     return Properties.Resources.Unauthorized;
3299                 case HttpStatusCode.BadRequest:
3300                     return "Err:API Limits?";
3301                 default:
3302                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3303             }
3304
3305             try
3306             {
3307                 var followers = MyCommon.CreateDataFromJson<TwitterDataModel.Ids>(content);
3308                 followerId.AddRange(followers.Id);
3309                 cursor = followers.NextCursor;
3310                 return "";
3311             }
3312             catch(SerializationException ex)
3313             {
3314                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3315                 return "Err:Json Parse Error(DataContractJsonSerializer)";
3316             }
3317             catch(Exception ex)
3318             {
3319                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3320                 return "Err:Invalid Json!";
3321             }
3322         }
3323
3324         public string GetNoRetweetIdsApi()
3325         {
3326             if (MyCommon._endingFlag) return "";
3327             long cursor = -1;
3328             var tmpIds = new List<long>(noRTId);
3329
3330             noRTId.Clear();
3331             do
3332             {
3333                 var ret = NoRetweetApi(ref cursor);
3334                 if (!string.IsNullOrEmpty(ret))
3335                 {
3336                     noRTId.Clear();
3337                     noRTId.AddRange(tmpIds);
3338                     _GetNoRetweetResult = false;
3339                     return ret;
3340                 }
3341             } while (cursor > 0);
3342
3343             _GetNoRetweetResult = true;
3344             return "";
3345         }
3346
3347         private string NoRetweetApi(ref long cursor)
3348         {
3349             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3350
3351             HttpStatusCode res = HttpStatusCode.BadRequest;
3352             var content = "";
3353             try
3354             {
3355                 res = twCon.NoRetweetIds(cursor, ref content);
3356             }
3357             catch(Exception ex)
3358             {
3359                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3360             }
3361
3362             switch (res)
3363             {
3364                 case HttpStatusCode.OK:
3365                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3366                     break;
3367                 case HttpStatusCode.Unauthorized:
3368                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3369                     return Properties.Resources.Unauthorized;
3370                 case HttpStatusCode.BadRequest:
3371                     return "Err:API Limits?";
3372                 default:
3373                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3374             }
3375
3376             try
3377             {
3378                 var ids = MyCommon.CreateDataFromJson<long[]>(content);
3379                 noRTId.AddRange(ids);
3380                 cursor = 0;  //0より小さければ何でも良い。
3381                 return "";
3382             }
3383             catch(SerializationException ex)
3384             {
3385                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3386                 return "Err:Json Parse Error(DataContractJsonSerializer)";
3387             }
3388             catch(Exception ex)
3389             {
3390                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3391                 return "Err:Invalid Json!";
3392             }
3393         }
3394
3395         public bool GetNoRetweetSuccess
3396         {
3397             get
3398             {
3399                 return _GetNoRetweetResult;
3400             }
3401         }
3402
3403         public string ConfigurationApi()
3404         {
3405             HttpStatusCode res;
3406             var content = "";
3407             try
3408             {
3409                 res = twCon.GetConfiguration(ref content);
3410             }
3411             catch(Exception ex)
3412             {
3413                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3414             }
3415
3416             switch (res)
3417             {
3418                 case HttpStatusCode.OK:
3419                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3420                     break;
3421                 case HttpStatusCode.Unauthorized:
3422                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3423                     return Properties.Resources.Unauthorized;
3424                 case HttpStatusCode.BadRequest:
3425                     return "Err:API Limits?";
3426                 default:
3427                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3428             }
3429
3430             try
3431             {
3432                 AppendSettingDialog.Instance.TwitterConfiguration = MyCommon.CreateDataFromJson<TwitterDataModel.Configuration>(content);
3433                 return "";
3434             }
3435             catch(SerializationException ex)
3436             {
3437                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3438                 return "Err:Json Parse Error(DataContractJsonSerializer)";
3439             }
3440             catch(Exception ex)
3441             {
3442                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3443                 return "Err:Invalid Json!";
3444             }
3445         }
3446
3447         public string GetListsApi()
3448         {
3449             return HttpTwitter.API11Enabled ? this.GetListsApi11() : this.GetListsApi10();
3450         }
3451
3452         private string GetListsApi10()
3453         {
3454             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3455
3456             HttpStatusCode res = HttpStatusCode.BadRequest;
3457             var content = "";
3458             long cursor = -1;
3459
3460             var lists = new List<ListElement>();
3461             do
3462             {
3463                 try
3464                 {
3465                     res = twCon.GetLists(this.Username, cursor, ref content);
3466                 }
3467                 catch(Exception ex)
3468                 {
3469                     return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3470                 }
3471
3472                 switch (res)
3473                 {
3474                     case HttpStatusCode.OK:
3475                         Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3476                         break;
3477                     case HttpStatusCode.Unauthorized:
3478                         Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3479                         return Properties.Resources.Unauthorized;
3480                     case HttpStatusCode.BadRequest:
3481                         return "Err:API Limits?";
3482                     default:
3483                         return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3484                 }
3485
3486                 try
3487                 {
3488                     var lst = MyCommon.CreateDataFromJson<TwitterDataModel.Lists>(content);
3489                     lists.AddRange(from le in lst.lists select new ListElement(le, this));
3490                     cursor = lst.NextCursor;
3491                 }
3492                 catch(SerializationException ex)
3493                 {
3494                     MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3495                     return "Err:Json Parse Error(DataContractJsonSerializer)";
3496                 }
3497                 catch(Exception ex)
3498                 {
3499                     MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3500                     return "Err:Invalid Json!";
3501                 }
3502             } while (cursor != 0);
3503
3504             cursor = -1;
3505             content = "";
3506             do
3507             {
3508                 try
3509                 {
3510                     res = twCon.GetListsSubscriptions(this.Username, cursor, ref content);
3511                 }
3512                 catch(Exception ex)
3513                 {
3514                     return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3515                 }
3516
3517                 switch (res)
3518                 {
3519                     case HttpStatusCode.OK:
3520                         Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3521                         break;
3522                     case HttpStatusCode.Unauthorized:
3523                         Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3524                         return Properties.Resources.Unauthorized;
3525                     case HttpStatusCode.BadRequest:
3526                         return "Err:API Limits?";
3527                     default:
3528                         return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3529                 }
3530
3531                 try
3532                 {
3533                     var lst = MyCommon.CreateDataFromJson<TwitterDataModel.Lists>(content);
3534                     lists.AddRange(from le in lst.lists select new ListElement(le, this));
3535                     cursor = lst.NextCursor;
3536                 }
3537                 catch(SerializationException ex)
3538                 {
3539                     MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3540                     return "Err:Json Parse Error(DataContractJsonSerializer)";
3541                 }
3542                 catch(Exception ex)
3543                 {
3544                     MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3545                     return "Err:Invalid Json!";
3546                 }
3547             } while (cursor != 0);
3548
3549             TabInformations.GetInstance().SubscribableLists = lists;
3550             return "";
3551         }
3552
3553         private string GetListsApi11()
3554         {
3555             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3556
3557             HttpStatusCode res = HttpStatusCode.BadRequest;
3558             IEnumerable<ListElement> lists;
3559             var content = "";
3560
3561             try
3562             {
3563                 res = twCon.GetLists(this.Username, null, ref content);
3564             }
3565             catch (Exception ex)
3566             {
3567                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3568             }
3569
3570             switch (res)
3571             {
3572                 case HttpStatusCode.OK:
3573                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3574                     break;
3575                 case HttpStatusCode.Unauthorized:
3576                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3577                     return Properties.Resources.Unauthorized;
3578                 case HttpStatusCode.BadRequest:
3579                     return "Err:API Limits?";
3580                 default:
3581                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3582             }
3583
3584             try
3585             {
3586                 lists = MyCommon.CreateDataFromJson<List<TwitterDataModel.ListElementData>>(content)
3587                     .Select(x => new ListElement(x, this));
3588             }
3589             catch (SerializationException ex)
3590             {
3591                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3592                 return "Err:Json Parse Error(DataContractJsonSerializer)";
3593             }
3594             catch (Exception ex)
3595             {
3596                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3597                 return "Err:Invalid Json!";
3598             }
3599
3600             content = "";
3601             try
3602             {
3603                 res = twCon.GetListsSubscriptions(this.Username, null, ref content);
3604             }
3605             catch (Exception ex)
3606             {
3607                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3608             }
3609
3610             switch (res)
3611             {
3612                 case HttpStatusCode.OK:
3613                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3614                     break;
3615                 case HttpStatusCode.Unauthorized:
3616                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3617                     return Properties.Resources.Unauthorized;
3618                 case HttpStatusCode.BadRequest:
3619                     return "Err:API Limits?";
3620                 default:
3621                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3622             }
3623
3624             try
3625             {
3626                 lists = lists.Concat(MyCommon.CreateDataFromJson<List<TwitterDataModel.ListElementData>>(content)
3627                     .Select(x => new ListElement(x, this)));
3628             }
3629             catch (SerializationException ex)
3630             {
3631                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3632                 return "Err:Json Parse Error(DataContractJsonSerializer)";
3633             }
3634             catch (Exception ex)
3635             {
3636                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3637                 return "Err:Invalid Json!";
3638             }
3639
3640             TabInformations.GetInstance().SubscribableLists = lists.ToList();
3641             return "";
3642         }
3643
3644         public string DeleteList(string list_id)
3645         {
3646             HttpStatusCode res = HttpStatusCode.BadRequest;
3647             var content = "";
3648
3649             try
3650             {
3651                 res = twCon.DeleteListID(this.Username, list_id, ref content);
3652             }
3653             catch(Exception ex)
3654             {
3655                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3656             }
3657
3658             switch (res)
3659             {
3660                 case HttpStatusCode.OK:
3661                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3662                     break;
3663                 case HttpStatusCode.Unauthorized:
3664                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3665                     return Properties.Resources.Unauthorized;
3666                 case HttpStatusCode.BadRequest:
3667                     return "Err:API Limits?";
3668                 default:
3669                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3670             }
3671
3672             return "";
3673         }
3674
3675         public string EditList(string list_id, string new_name, bool isPrivate, string description, ref ListElement list)
3676         {
3677             HttpStatusCode res = HttpStatusCode.BadRequest;
3678             var content = "";
3679
3680             try
3681             {
3682                 res = twCon.UpdateListID(this.Username, list_id, new_name, isPrivate, description, ref content);
3683             }
3684             catch(Exception ex)
3685             {
3686                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3687             }
3688
3689             switch (res)
3690             {
3691                 case HttpStatusCode.OK:
3692                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3693                     break;
3694                 case HttpStatusCode.Unauthorized:
3695                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3696                     return Properties.Resources.Unauthorized;
3697                 case HttpStatusCode.BadRequest:
3698                     return "Err:API Limits?";
3699                 default:
3700                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3701             }
3702
3703             try
3704             {
3705                 var le = MyCommon.CreateDataFromJson<TwitterDataModel.ListElementData>(content);
3706                 var newList = new ListElement(le, this);
3707                 list.Description = newList.Description;
3708                 list.Id = newList.Id;
3709                 list.IsPublic = newList.IsPublic;
3710                 list.MemberCount = newList.MemberCount;
3711                 list.Name = newList.Name;
3712                 list.SubscriberCount = newList.SubscriberCount;
3713                 list.Slug = newList.Slug;
3714                 list.Nickname = newList.Nickname;
3715                 list.Username = newList.Username;
3716                 list.UserId = newList.UserId;
3717                 return "";
3718             }
3719             catch(SerializationException ex)
3720             {
3721                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3722                 return "Err:Json Parse Error(DataContractJsonSerializer)";
3723             }
3724             catch(Exception ex)
3725             {
3726                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3727                 return "Err:Invalid Json!";
3728             }
3729
3730         }
3731
3732         public string GetListMembers(string list_id, List<UserInfo> lists, ref long cursor)
3733         {
3734             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3735
3736             HttpStatusCode res = HttpStatusCode.BadRequest;
3737             var content = "";
3738
3739             //Do
3740             try
3741             {
3742                 res = twCon.GetListMembers(this.Username, list_id, cursor, ref content);
3743             }
3744             catch(Exception ex)
3745             {
3746                 return "Err:" + ex.Message;
3747             }
3748
3749             switch (res)
3750             {
3751                 case HttpStatusCode.OK:
3752                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3753                     break;
3754                 case HttpStatusCode.Unauthorized:
3755                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3756                     return Properties.Resources.Unauthorized;
3757                 case HttpStatusCode.BadRequest:
3758                     return "Err:API Limits?";
3759                 default:
3760                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3761             }
3762
3763             try
3764             {
3765                 var users = MyCommon.CreateDataFromJson<TwitterDataModel.Users>(content);
3766                 Array.ForEach<TwitterDataModel.User>(
3767                     users.users,
3768                     new Action<TwitterDataModel.User>(u => lists.Add(new UserInfo(u))));
3769                 cursor = users.NextCursor;
3770                 return "";
3771             }
3772             catch(SerializationException ex)
3773             {
3774                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3775                 return "Err:Json Parse Error(DataContractJsonSerializer)";
3776             }
3777             catch(Exception ex)
3778             {
3779                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3780                 return "Err:Invalid Json!";
3781             }
3782         }
3783
3784         public string CreateListApi(string listName, bool isPrivate, string description)
3785         {
3786             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3787
3788             HttpStatusCode res = HttpStatusCode.BadRequest;
3789             var content = "";
3790
3791             try
3792             {
3793                 res = twCon.CreateLists(listName, isPrivate, description, ref content);
3794             }
3795             catch(Exception ex)
3796             {
3797                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3798             }
3799
3800             switch (res)
3801             {
3802                 case HttpStatusCode.OK:
3803                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3804                     break;
3805                 case HttpStatusCode.Unauthorized:
3806                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3807                     return Properties.Resources.Unauthorized;
3808                 case HttpStatusCode.BadRequest:
3809                     return "Err:API Limits?";
3810                 default:
3811                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3812             }
3813
3814             try
3815             {
3816                 var le = MyCommon.CreateDataFromJson<TwitterDataModel.ListElementData>(content);
3817                 TabInformations.GetInstance().SubscribableLists.Add(new ListElement(le, this));
3818                 return "";
3819             }
3820             catch(SerializationException ex)
3821             {
3822                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3823                 return "Err:Json Parse Error(DataContractJsonSerializer)";
3824             }
3825             catch(Exception ex)
3826             {
3827                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3828                 return "Err:Invalid Json!";
3829             }
3830         }
3831
3832         public string ContainsUserAtList(string listId, string user, ref bool value)
3833         {
3834             value = false;
3835
3836             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3837
3838             HttpStatusCode res = HttpStatusCode.BadRequest;
3839             var content = "";
3840
3841             try
3842             {
3843                 res = this.twCon.ShowListMember(listId, user, ref content);
3844             }
3845             catch(Exception ex)
3846             {
3847                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3848             }
3849
3850             switch (res)
3851             {
3852                 case HttpStatusCode.OK:
3853                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3854                     break;
3855                 case HttpStatusCode.Unauthorized:
3856                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3857                     return Properties.Resources.Unauthorized;
3858                 case HttpStatusCode.BadRequest:
3859                     return "Err:API Limits?";
3860                 case HttpStatusCode.NotFound:
3861                     value = false;
3862                     return "";
3863                 default:
3864                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3865             }
3866
3867             try
3868             {
3869                 var u = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
3870                 value = true;
3871                 return "";
3872             }
3873             catch(Exception)
3874             {
3875                 value = false;
3876                 return "";
3877             }
3878         }
3879
3880         public string AddUserToList(string listId, string user)
3881         {
3882             var content = "";
3883             HttpStatusCode res = HttpStatusCode.BadRequest;
3884
3885             try
3886             {
3887                 res = twCon.CreateListMembers(listId, user, ref content);
3888             }
3889             catch(Exception ex)
3890             {
3891                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3892             }
3893
3894             switch (res)
3895             {
3896                 case HttpStatusCode.OK:
3897                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3898                     break;
3899                 case HttpStatusCode.Unauthorized:
3900                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3901                     return Properties.Resources.Unauthorized;
3902                 case HttpStatusCode.BadRequest:
3903                     return "Err:" + GetErrorMessageJson(content);
3904                 default:
3905                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3906             }
3907
3908             return "";
3909         }
3910
3911         public string RemoveUserToList(string listId, string user)
3912         {
3913
3914             var content = "";
3915             HttpStatusCode res = HttpStatusCode.BadRequest;
3916
3917             try
3918             {
3919                 res = twCon.DeleteListMembers(listId, user, ref content);
3920             }
3921             catch(Exception ex)
3922             {
3923                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3924             }
3925
3926             switch (res)
3927             {
3928                 case HttpStatusCode.OK:
3929                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3930                     break;
3931                 case HttpStatusCode.Unauthorized:
3932                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3933                     return Properties.Resources.Unauthorized;
3934                 case HttpStatusCode.BadRequest:
3935                     return "Err:" + GetErrorMessageJson(content);
3936                 default:
3937                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3938             }
3939
3940             return "";
3941         }
3942
3943         private class range
3944         {
3945             public int fromIndex { get; set; }
3946             public int toIndex { get; set; }
3947             public range(int fromIndex, int toIndex)
3948             {
3949                 this.fromIndex = fromIndex;
3950                 this.toIndex = toIndex;
3951             }
3952         }
3953         public string CreateHtmlAnchor(string Text, List<string> AtList, Dictionary<string, string> media)
3954         {
3955             if (Text == null) return null;
3956             var retStr = Text.Replace("&gt;", "<<<<<tweenだいなり>>>>>").Replace("&lt;", "<<<<<tweenしょうなり>>>>>");
3957             //uriの正規表現
3958             //const string url_valid_domain = "(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)"
3959             //const string url_valid_general_path_chars = "[a-z0-9!*';:=+$/%#\[\]\-_,~]"
3960             //const string url_balance_parens = "(?:\(" + url_valid_general_path_chars + "+\))"
3961             //const string url_valid_url_path_ending_chars = "(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")"
3962             //const string pth = "(?:" + url_balance_parens +
3963             //    "|@" + url_valid_general_path_chars + "+/" +
3964             //    "|[.,]?" + url_valid_general_path_chars + "+" +
3965             //    ")"
3966             //const string pth2 = "(/(?:" +
3967             //    pth + "+" + url_valid_url_path_ending_chars + "|" +
3968             //    pth + "+" + url_valid_url_path_ending_chars + "?|" +
3969             //    url_valid_url_path_ending_chars +
3970             //    ")?)?"
3971             //const string qry = "(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?"
3972             //const string rgUrl = "(?<before>(?:[^\""':!=#]|^|\:/))" +
3973             //                            "(?<url>(?<protocol>https?://)" +
3974             //                            url_valid_domain +
3975             //                            pth2 +
3976             //                            qry +
3977             //                            ")"
3978             //const string rgUrl = "(?<before>(?:[^\""':!=#]|^|\:/))" +
3979             //                            "(?<url>(?<protocol>https?://|www\.)" +
3980             //                            url_valid_domain +
3981             //                            pth2 +
3982             //                            qry +
3983             //                            ")"
3984             //絶対パス表現のUriをリンクに置換
3985             retStr = Regex.Replace(retStr,
3986                                    rgUrl,
3987                                    new MatchEvaluator((Match mu) =>
3988                                                       {
3989                                                           var sb = new StringBuilder(mu.Result("${before}<a href=\""));
3990                                                           //if (mu.Result("${protocol}").StartsWith("w", StringComparison.OrdinalIgnoreCase))
3991                                                           //    sb.Append("http://");
3992                                                           //}
3993                                                           var url = mu.Result("${url}");
3994                                                           var title = ShortUrl.ResolveMedia(url, true);
3995                                                           if (url != title)
3996                                                           {
3997                                                               title = ShortUrl.ResolveMedia(title, false);
3998                                                           }
3999                                                           sb.Append(url + "\" title=\"" + title + "\">").Append(url).Append("</a>");
4000                                                           if (media != null && !media.ContainsKey(url)) media.Add(url, title);
4001                                                           return sb.ToString();
4002                                                       }),
4003                                    RegexOptions.IgnoreCase);
4004
4005             //@先をリンクに置換(リスト)
4006             retStr = Regex.Replace(retStr,
4007                                    @"(^|[^a-zA-Z0-9_/])([@@]+)([a-zA-Z0-9_]{1,20}/[a-zA-Z][a-zA-Z0-9\p{IsLatin-1Supplement}\-]{0,79})",
4008                                    "$1$2<a href=\"/$3\">$3</a>");
4009
4010             var m = Regex.Match(retStr, "(^|[^a-zA-Z0-9_])[@@]([a-zA-Z0-9_]{1,20})");
4011             while (m.Success)
4012             {
4013                 if (!AtList.Contains(m.Result("$2").ToLower())) AtList.Add(m.Result("$2").ToLower());
4014                 m = m.NextMatch();
4015             }
4016             //@先をリンクに置換
4017             retStr = Regex.Replace(retStr,
4018                                    "(^|[^a-zA-Z0-9_/])([@@])([a-zA-Z0-9_]{1,20})",
4019                                    "$1$2<a href=\"/$3\">$3</a>");
4020
4021             //ハッシュタグを抽出し、リンクに置換
4022             var anchorRange = new List<range>();
4023             for (int i = 0; i < retStr.Length; i++)
4024             {
4025                 var index = retStr.IndexOf("<a ", i);
4026                 if (index > -1 && index < retStr.Length)
4027                 {
4028                     i = index;
4029                     var toIndex = retStr.IndexOf("</a>", index);
4030                     if (toIndex > -1)
4031                     {
4032                         anchorRange.Add(new range(index, toIndex + 3));
4033                         i = toIndex;
4034                     }
4035                 }
4036             }
4037             //retStr = Regex.Replace(retStr,
4038             //                       "(^|[^a-zA-Z0-9/&])([##])([0-9a-zA-Z_]*[a-zA-Z_]+[a-zA-Z0-9_\xc0-\xd6\xd8-\xf6\xf8-\xff]*)",
4039             //                       new MatchEvaluator(Function(mh As Match)
4040             //                                              foreach (var rng in anchorRange)
4041             //                                              {
4042             //                                                  if (mh.Index >= rng.fromIndex &&
4043             //                                                   mh.Index <= rng.toIndex) return mh.Result("$0");
4044             //                                              }
4045             //                                              if (IsNumeric(mh.Result("$3"))) return mh.Result("$0");
4046             //                                              lock (LockObj)
4047             //                                              {
4048             //                                                  _hashList.Add("#" + mh.Result("$3"))
4049             //                                              }
4050             //                                              return mh.Result("$1") + "<a href=\"" + _protocol + "twitter.com/search?q=%23" + mh.Result("$3") + "\">" + mh.Result("$2$3") + "</a>";
4051             //                                          }),
4052             //                                      RegexOptions.IgnoreCase)
4053             retStr = Regex.Replace(retStr,
4054                                    HASHTAG,
4055                                    new MatchEvaluator(mh =>
4056                                                       {
4057                                                           foreach (var rng in anchorRange)
4058                                                           {
4059                                                               if (mh.Index >= rng.fromIndex &&
4060                                                                mh.Index <= rng.toIndex) return mh.Result("$0");
4061                                                           }
4062                                                           lock (LockObj)
4063                                                           {
4064                                                               _hashList.Add("#" + mh.Result("$3"));
4065                                                           }
4066                                                           return mh.Result("$1") + "<a href=\"" + _protocol + "twitter.com/search?q=%23" + mh.Result("$3") + "\">" + mh.Result("$2$3") + "</a>";
4067                                                       }),
4068                                                   RegexOptions.IgnoreCase);
4069
4070
4071             retStr = Regex.Replace(retStr, "(^|[^a-zA-Z0-9_/&##@@>=.~])(sm|nm)([0-9]{1,10})", "$1<a href=\"http://www.nicovideo.jp/watch/$2$3\">$2$3</a>");
4072
4073             retStr = retStr.Replace("<<<<<tweenだいなり>>>>>", "&gt;").Replace("<<<<<tweenしょうなり>>>>>", "&lt;");
4074
4075             //retStr = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(retStr), true)) //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
4076             retStr = AdjustHtml(PreProcessUrl(retStr)); //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
4077             return retStr;
4078         }
4079
4080         private class EntityInfo
4081         {
4082             public int StartIndex { get; set; }
4083             public int EndIndex { get; set; }
4084             public string Text { get; set; }
4085             public string Html { get; set; }
4086             public string Display { get; set; }
4087         }
4088         public string CreateHtmlAnchor(ref string Text, List<string> AtList, TwitterDataModel.Entities entities, Dictionary<string, string> media)
4089         {
4090             var ret = Text;
4091
4092             if (entities != null)
4093             {
4094                 var etInfo = new SortedList<int, EntityInfo>();
4095                 //URL
4096                 if (entities.Urls != null)
4097                 {
4098                     foreach (var ent in entities.Urls)
4099                     {
4100                         if (string.IsNullOrEmpty(ent.DisplayUrl))
4101                         {
4102                             etInfo.Add(ent.Indices[0],
4103                                        new EntityInfo {StartIndex = ent.Indices[0],
4104                                                        EndIndex = ent.Indices[1],
4105                                                        Text = ent.Url,
4106                                                        Html = "<a href=\"" + ent.Url + "\">" + ent.Url + "</a>"});
4107                         }
4108                         else
4109                         {
4110                             var expanded = ShortUrl.ResolveMedia(ent.ExpandedUrl, false);
4111                             etInfo.Add(ent.Indices[0],
4112                                        new EntityInfo {StartIndex = ent.Indices[0],
4113                                                        EndIndex = ent.Indices[1],
4114                                                        Text = ent.Url,
4115                                                        Html = "<a href=\"" + ent.Url + "\" title=\"" + expanded + "\">" + ent.DisplayUrl + "</a>",
4116                                                        Display = ent.DisplayUrl});
4117                             if (media != null && !media.ContainsKey(ent.Url)) media.Add(ent.Url, expanded);
4118                         }
4119                     }
4120                 }
4121                 if (entities.Hashtags != null)
4122                 {
4123                     foreach (var ent in entities.Hashtags)
4124                     {
4125                         var hash = Text.Substring(ent.Indices[0], ent.Indices[1] - ent.Indices[0]);
4126                         etInfo.Add(ent.Indices[0],
4127                                    new EntityInfo {StartIndex = ent.Indices[0],
4128                                                    EndIndex = ent.Indices[1],
4129                                                    Text = hash,
4130                                                    Html = "<a href=\"" + _protocol + "twitter.com/search?q=%23" + ent.Text + "\">" + hash + "</a>"});
4131                         lock (LockObj)
4132                         {
4133                             _hashList.Add("#" + ent.Text);
4134                         }
4135                     }
4136                 }
4137                 if (entities.UserMentions != null)
4138                 {
4139                     foreach (var ent in entities.UserMentions)
4140                     {
4141                         var screenName = Text.Substring(ent.Indices[0] + 1, ent.Indices[1] - ent.Indices[0] - 1);
4142                         etInfo.Add(ent.Indices[0] + 1,
4143                                    new EntityInfo {StartIndex = ent.Indices[0] + 1,
4144                                                    EndIndex = ent.Indices[1],
4145                                                    Text = ent.ScreenName,
4146                                                    Html = "<a href=\"/" + ent.ScreenName + "\">" + screenName + "</a>"});
4147                         if (!AtList.Contains(ent.ScreenName.ToLower())) AtList.Add(ent.ScreenName.ToLower());
4148                     }
4149                 }
4150                 if (entities.Media != null)
4151                 {
4152                     foreach (var ent in entities.Media)
4153                     {
4154                         if (ent.Type == "photo")
4155                         {
4156                             etInfo.Add(ent.Indices[0],
4157                                        new EntityInfo {StartIndex = ent.Indices[0],
4158                                                        EndIndex = ent.Indices[1],
4159                                                        Text = ent.Url,
4160                                                        Html = "<a href=\"" + ent.Url + "\" title=\"" + ent.ExpandedUrl + "\">" + ent.DisplayUrl + "</a>",
4161                                                        Display = ent.DisplayUrl});
4162                             if (media != null && !media.ContainsKey(ent.Url)) media.Add(ent.Url, ent.MediaUrl);
4163                         }
4164                     }
4165                 }
4166                 if (etInfo.Count > 0)
4167                 {
4168                     try
4169                     {
4170                         var idx = 0;
4171                         ret = "";
4172                         foreach (var et in etInfo)
4173                         {
4174                             ret += Text.Substring(idx, et.Key - idx) + et.Value.Html;
4175                             idx = et.Value.EndIndex;
4176                         }
4177                         ret += Text.Substring(idx);
4178                     }
4179                     catch(ArgumentOutOfRangeException)
4180                     {
4181                         //Twitterのバグで不正なエンティティ(Index指定範囲が重なっている)が返ってくる場合の対応
4182                         ret = Text;
4183                         entities = null;
4184                         if (media != null) media.Clear();
4185                     }
4186                 }
4187             }
4188
4189             ret = Regex.Replace(ret, "(^|[^a-zA-Z0-9_/&##@@>=.~])(sm|nm)([0-9]{1,10})", "$1<a href=\"http://www.nicovideo.jp/watch/$2$3\">$2$3</a>");
4190             ret = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(ret), false)); //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
4191
4192             return ret;
4193         }
4194
4195         //Source整形
4196         private void CreateSource(PostClass post)
4197         {
4198             if (string.IsNullOrEmpty(post.Source)) return;
4199
4200             if (post.Source.StartsWith("<"))
4201             {
4202                 if (!post.Source.Contains("</a>"))
4203                 {
4204                     post.Source += "</a>";
4205                 }
4206                 var mS = Regex.Match(post.Source, ">(?<source>.+)<");
4207                 if (mS.Success)
4208                 {
4209                     post.SourceHtml = ShortUrl.Resolve(PreProcessUrl(post.Source), false);
4210                     post.Source = WebUtility.HtmlDecode(mS.Result("${source}"));
4211                 }
4212                 else
4213                 {
4214                     post.Source = "";
4215                     post.SourceHtml = "";
4216                 }
4217             }
4218             else
4219             {
4220                 if (post.Source == "web")
4221                 {
4222                     post.SourceHtml = Properties.Resources.WebSourceString;
4223                 }
4224                 else if (post.Source == "Keitai Mail")
4225                 {
4226                     post.SourceHtml = Properties.Resources.KeitaiMailSourceString;
4227                 }
4228                 else
4229                 {
4230                     post.SourceHtml = post.Source;
4231                 }
4232             }
4233         }
4234
4235         public TwitterApiStatus GetInfoApi10()
4236         {
4237             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return null;
4238
4239             if (MyCommon._endingFlag) return null;
4240
4241             HttpStatusCode res = HttpStatusCode.BadRequest;
4242             var content = "";
4243             try
4244             {
4245                 res = twCon.RateLimitStatus(ref content);
4246             }
4247             catch(Exception)
4248             {
4249                 this.ResetApiStatus();
4250                 return null;
4251             }
4252
4253             if (res != HttpStatusCode.OK) return null;
4254
4255             try
4256             {
4257                 var limit = MyCommon.CreateDataFromJson<TwitterDataModel.RateLimitStatus>(content);
4258                 MyCommon.TwitterApiInfo.UpdateFromApi(limit);
4259
4260                 return MyCommon.TwitterApiInfo;
4261             }
4262             catch(Exception ex)
4263             {
4264                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
4265                 MyCommon.TwitterApiInfo.Reset();
4266                 return null;
4267             }
4268         }
4269
4270         public TwitterApiStatus11 GetInfoApi11()
4271         {
4272             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return null;
4273
4274             if (MyCommon._endingFlag) return null;
4275
4276             HttpStatusCode res = HttpStatusCode.BadRequest;
4277             var content = "";
4278             try
4279             {
4280                 res = twCon.RateLimitStatus(ref content);
4281             }
4282             catch (Exception)
4283             {
4284                 this.ResetApiStatus();
4285                 return null;
4286             }
4287
4288             if (res != HttpStatusCode.OK) return null;
4289
4290             try
4291             {
4292                 MyCommon.TwitterApiInfo11.UpdateFromJson(content);
4293                 return MyCommon.TwitterApiInfo11;
4294             }
4295             catch (Exception ex)
4296             {
4297                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
4298                 MyCommon.TwitterApiInfo.Reset();
4299                 return null;
4300             }
4301         }
4302
4303         public string GetBlockUserIds()
4304         {
4305             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
4306
4307             HttpStatusCode res = HttpStatusCode.BadRequest;
4308             var content = "";
4309
4310             try
4311             {
4312                 res = twCon.GetBlockUserIds(ref content);
4313             }
4314             catch(Exception ex)
4315             {
4316                 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
4317             }
4318
4319             switch (res)
4320             {
4321                 case HttpStatusCode.OK:
4322                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
4323                     break;
4324                 case HttpStatusCode.Unauthorized:
4325                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
4326                     return Properties.Resources.Unauthorized;
4327                 case HttpStatusCode.BadRequest:
4328                     return "Err:API Limits?";
4329                 default:
4330                     return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
4331             }
4332
4333             try
4334             {
4335                 var Ids = MyCommon.CreateDataFromJson<List<long>>(content);
4336                 if (Ids.Contains(this.UserId)) Ids.Remove(this.UserId);
4337                 TabInformations.GetInstance().BlockIds.AddRange(Ids);
4338                 return ("");
4339             }
4340             catch(SerializationException ex)
4341             {
4342                 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
4343                 return "Err:Json Parse Error(DataContractJsonSerializer)";
4344             }
4345             catch(Exception ex)
4346             {
4347                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
4348                 return "Err:Invalid Json!";
4349             }
4350
4351         }
4352
4353         public string[] GetHashList()
4354         {
4355             string[] hashArray;
4356             lock (LockObj)
4357             {
4358                 hashArray = _hashList.ToArray();
4359                 _hashList.Clear();
4360             }
4361             return hashArray;
4362         }
4363
4364         public string AccessToken
4365         {
4366             get
4367             {
4368                 return twCon.AccessToken;
4369             }
4370         }
4371
4372         public string AccessTokenSecret
4373         {
4374             get
4375             {
4376                 return twCon.AccessTokenSecret;
4377             }
4378         }
4379
4380 #region "UserStream"
4381         private string trackWord_ = "";
4382         public string TrackWord
4383         {
4384             get
4385             {
4386                 return trackWord_;
4387             }
4388             set
4389             {
4390                 trackWord_ = value;
4391             }
4392         }
4393         private bool allAtReply_ = false;
4394         public bool AllAtReply
4395         {
4396             get
4397             {
4398                 return allAtReply_;
4399             }
4400             set
4401             {
4402                 allAtReply_ = value;
4403             }
4404         }
4405
4406         public event Action NewPostFromStream;
4407         public event Action UserStreamStarted;
4408         public event Action UserStreamStopped;
4409         public event Action<long> PostDeleted;
4410         public event Action<FormattedEvent> UserStreamEventReceived;
4411         private DateTime _lastUserstreamDataReceived;
4412         private TwitterUserstream userStream;
4413
4414         public class FormattedEvent
4415         {
4416             public MyCommon.EVENTTYPE Eventtype { get; set; }
4417             public DateTime CreatedAt { get; set; }
4418             public string Event { get; set; }
4419             public string Username { get; set; }
4420             public string Target { get; set; }
4421             public Int64 Id { get; set; }
4422             public bool IsMe { get; set; }
4423         }
4424
4425         public List<FormattedEvent> storedEvent_ = new List<FormattedEvent>();
4426         public List<FormattedEvent> StoredEvent
4427         {
4428             get
4429             {
4430                 return storedEvent_;
4431             }
4432             set
4433             {
4434                 storedEvent_ = value;
4435             }
4436         }
4437
4438         private class EventTypeTableElement
4439         {
4440             public string Name;
4441             public MyCommon.EVENTTYPE Type;
4442
4443             public EventTypeTableElement(string name, MyCommon.EVENTTYPE type)
4444             {
4445                 this.Name = name;
4446                 this.Type = type;
4447             }
4448         }
4449
4450         private EventTypeTableElement[] EventTable = {
4451             new EventTypeTableElement("favorite", MyCommon.EVENTTYPE.Favorite),
4452             new EventTypeTableElement("unfavorite", MyCommon.EVENTTYPE.Unfavorite),
4453             new EventTypeTableElement("follow", MyCommon.EVENTTYPE.Follow),
4454             new EventTypeTableElement("list_member_added", MyCommon.EVENTTYPE.ListMemberAdded),
4455             new EventTypeTableElement("list_member_removed", MyCommon.EVENTTYPE.ListMemberRemoved),
4456             new EventTypeTableElement("block", MyCommon.EVENTTYPE.Block),
4457             new EventTypeTableElement("unblock", MyCommon.EVENTTYPE.Unblock),
4458             new EventTypeTableElement("user_update", MyCommon.EVENTTYPE.UserUpdate),
4459             new EventTypeTableElement("deleted", MyCommon.EVENTTYPE.Deleted),
4460             new EventTypeTableElement("list_created", MyCommon.EVENTTYPE.ListCreated),
4461             new EventTypeTableElement("list_updated", MyCommon.EVENTTYPE.ListUpdated),
4462             new EventTypeTableElement("unfollow", MyCommon.EVENTTYPE.Unfollow),
4463         };
4464
4465         public MyCommon.EVENTTYPE EventNameToEventType(string EventName)
4466         {
4467             return (from tbl in EventTable where tbl.Name.Equals(EventName) select tbl.Type).FirstOrDefault();
4468         }
4469
4470         public bool IsUserstreamDataReceived
4471         {
4472             get
4473             {
4474                 return DateTime.Now.Subtract(this._lastUserstreamDataReceived).TotalSeconds < 31;
4475             }
4476         }
4477
4478         private void userStream_StatusArrived(string line)
4479         {
4480             this._lastUserstreamDataReceived = DateTime.Now;
4481             if (string.IsNullOrEmpty(line)) return;
4482
4483             var isDm = false;
4484
4485             try
4486             {
4487                 using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(line), XmlDictionaryReaderQuotas.Max))
4488                 {
4489                     var xElm = XElement.Load(jsonReader);
4490                     if (xElm.Element("friends") != null)
4491                     {
4492                         Debug.WriteLine("friends");
4493                         return;
4494                     }
4495                     else if (xElm.Element("delete") != null)
4496                     {
4497                         Debug.WriteLine("delete");
4498                         Int64 id;
4499                         if (xElm.Element("delete").Element("direct_message") != null &&
4500                             xElm.Element("delete").Element("direct_message").Element("id") != null)
4501                         {
4502                             id = 0;
4503                             long.TryParse(xElm.Element("delete").Element("direct_message").Element("id").Value, out id);
4504                             if (PostDeleted != null)
4505                             {
4506                                 PostDeleted(id);
4507                             }
4508                         }
4509                         else if (xElm.Element("delete").Element("status") != null &&
4510                             xElm.Element("delete").Element("status").Element("id") != null)
4511                         {
4512                             id = 0;
4513                             long.TryParse(xElm.Element("delete").Element("status").Element("id").Value, out id);
4514                             if (PostDeleted != null)
4515                             {
4516                                 PostDeleted(id);
4517                             }
4518                         }
4519                         else
4520                         {
4521                             MyCommon.TraceOut("delete:" + line);
4522                             return;
4523                         }
4524                         for (int i = this.StoredEvent.Count - 1; i >= 0; i--)
4525                         {
4526                             var sEvt = this.StoredEvent[i];
4527                             if (sEvt.Id == id && (sEvt.Event == "favorite" || sEvt.Event == "unfavorite"))
4528                             {
4529                                 this.StoredEvent.RemoveAt(i);
4530                             }
4531                         }
4532                         return;
4533                     }
4534                     else if (xElm.Element("limit") != null)
4535                     {
4536                         Debug.WriteLine(line);
4537                         return;
4538                     }
4539                     else if (xElm.Element("event") != null)
4540                     {
4541                         Debug.WriteLine("event: " + xElm.Element("event").Value);
4542                         CreateEventFromJson(line);
4543                         return;
4544                     }
4545                     else if (xElm.Element("direct_message") != null)
4546                     {
4547                         Debug.WriteLine("direct_message");
4548                         isDm = true;
4549                     }
4550                     else if (xElm.Element("scrub_geo") != null)
4551                     {
4552                         try
4553                         {
4554                             TabInformations.GetInstance().ScrubGeoReserve(long.Parse(xElm.Element("scrub_geo").Element("user_id").Value),
4555                                                                         long.Parse(xElm.Element("scrub_geo").Element("up_to_status_id").Value));
4556                         }
4557                         catch(Exception)
4558                         {
4559                             MyCommon.TraceOut("scrub_geo:" + line);
4560                         }
4561                         return;
4562                     }
4563                 }
4564
4565                 var res = new StringBuilder();
4566                 res.Length = 0;
4567                 res.Append("[");
4568                 res.Append(line);
4569                 res.Append("]");
4570
4571                 if (isDm)
4572                 {
4573                     CreateDirectMessagesFromJson(res.ToString(), MyCommon.WORKERTYPE.UserStream, false);
4574                 }
4575                 else
4576                 {
4577                     long dummy = 0;
4578                     CreatePostsFromJson(res.ToString(), MyCommon.WORKERTYPE.Timeline, null, false, 0, ref dummy);
4579                 }
4580             }
4581             catch(NullReferenceException)
4582             {
4583                 MyCommon.TraceOut("NullRef StatusArrived: " + line);
4584             }
4585
4586             if (NewPostFromStream != null)
4587             {
4588                 NewPostFromStream();
4589             }
4590         }
4591
4592         private void CreateEventFromJson(string content)
4593         {
4594             TwitterDataModel.EventData eventData = null;
4595             try
4596             {
4597                 eventData = MyCommon.CreateDataFromJson<TwitterDataModel.EventData>(content);
4598             }
4599             catch(SerializationException ex)
4600             {
4601                 MyCommon.TraceOut(ex, "Event Serialize Exception!" + Environment.NewLine + content);
4602             }
4603             catch(Exception ex)
4604             {
4605                 MyCommon.TraceOut(ex, "Event Exception!" + Environment.NewLine + content);
4606             }
4607
4608             var evt = new FormattedEvent();
4609             evt.CreatedAt = MyCommon.DateTimeParse(eventData.CreatedAt);
4610             evt.Event = eventData.Event;
4611             evt.Username = eventData.Source.ScreenName;
4612             evt.IsMe = evt.Username.ToLower().Equals(this.Username.ToLower());
4613             evt.Eventtype = EventNameToEventType(evt.Event);
4614             switch (eventData.Event)
4615             {
4616                 case "access_revoked":
4617                     return;
4618                 case "follow":
4619                     if (eventData.Target.ScreenName.ToLower().Equals(_uname))
4620                     {
4621                         if (!this.followerId.Contains(eventData.Source.Id)) this.followerId.Add(eventData.Source.Id);
4622                     }
4623                     else
4624                     {
4625                         return;    //Block後のUndoをすると、SourceとTargetが逆転したfollowイベントが帰ってくるため。
4626                     }
4627                     evt.Target = "";
4628                     break;
4629                 case "unfollow":
4630                     return;
4631                 case "favorite":
4632                 case "unfavorite":
4633                     evt.Target = "@" + eventData.TargetObject.User.ScreenName + ":" + WebUtility.HtmlDecode(eventData.TargetObject.Text);
4634                     evt.Id = eventData.TargetObject.Id;
4635                     if (AppendSettingDialog.Instance.IsRemoveSameEvent)
4636                     {
4637                         if (StoredEvent.Any(ev =>
4638                                            {
4639                                                return ev.Username == evt.Username && ev.Eventtype == evt.Eventtype && ev.Target == evt.Target;
4640                                            })) return;
4641                     }
4642                     if (TabInformations.GetInstance().ContainsKey(eventData.TargetObject.Id))
4643                     {
4644                         var post = TabInformations.GetInstance()[eventData.TargetObject.Id];
4645                         if (eventData.Event == "favorite")
4646                         {
4647                             if (evt.Username.ToLower().Equals(_uname))
4648                             {
4649                                 post.IsFav = true;
4650                                 TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
4651                             }
4652                             else
4653                             {
4654                                 post.FavoritedCount++;
4655                                 if (!TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Contains(post.StatusId))
4656                                 {
4657                                     if (AppendSettingDialog.Instance.FavEventUnread && post.IsRead)
4658                                     {
4659                                         post.IsRead = false;
4660                                     }
4661                                     TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
4662                                 }
4663                                 else
4664                                 {
4665                                     if (AppendSettingDialog.Instance.FavEventUnread)
4666                                     {
4667                                         TabInformations.GetInstance().SetRead(false, TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).TabName, TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).IndexOf(post.StatusId));
4668                                     }
4669                                 }
4670                             }
4671                         }
4672                         else
4673                         {
4674                             if (evt.Username.ToLower().Equals(_uname))
4675                             {
4676                                 post.IsFav = false;
4677                             }
4678                             else
4679                             {
4680                                 post.FavoritedCount--;
4681                                 if (post.FavoritedCount < 0) post.FavoritedCount = 0;
4682                             }
4683                         }
4684                     }
4685                     break;
4686                 case "list_member_added":
4687                 case "list_member_removed":
4688                 case "list_updated":
4689                     evt.Target = eventData.TargetObject.FullName;
4690                     break;
4691                 case "block":
4692                     if (!TabInformations.GetInstance().BlockIds.Contains(eventData.Target.Id)) TabInformations.GetInstance().BlockIds.Add(eventData.Target.Id);
4693                     evt.Target = "";
4694                     break;
4695                 case "unblock":
4696                     if (TabInformations.GetInstance().BlockIds.Contains(eventData.Target.Id)) TabInformations.GetInstance().BlockIds.Remove(eventData.Target.Id);
4697                     evt.Target = "";
4698                     break;
4699                 case "user_update":
4700                     evt.Target = "";
4701                     break;
4702                 case "list_created":
4703                     evt.Target = "";
4704                     break;
4705                 default:
4706                     MyCommon.TraceOut("Unknown Event:" + evt.Event + Environment.NewLine + content);
4707                     break;
4708             }
4709             this.StoredEvent.Insert(0, evt);
4710             if (UserStreamEventReceived != null)
4711             {
4712                 UserStreamEventReceived(evt);
4713             }
4714         }
4715
4716         private void userStream_Started()
4717         {
4718             if (UserStreamStarted != null)
4719             {
4720                 UserStreamStarted();
4721             }
4722         }
4723
4724         private void userStream_Stopped()
4725         {
4726             if (UserStreamStopped != null)
4727             {
4728                 UserStreamStopped();
4729             }
4730         }
4731
4732         public bool UserStreamEnabled
4733         {
4734             get
4735             {
4736                 return userStream == null ? false : userStream.Enabled;
4737             }
4738         }
4739
4740         public void StartUserStream()
4741         {
4742             if (userStream != null)
4743             {
4744                 StopUserStream();
4745             }
4746             userStream = new TwitterUserstream(twCon);
4747             userStream.StatusArrived += userStream_StatusArrived;
4748             userStream.Started += userStream_Started;
4749             userStream.Stopped += userStream_Stopped;
4750             userStream.Start(this.AllAtReply, this.TrackWord);
4751         }
4752
4753         public void StopUserStream()
4754         {
4755             if (userStream != null) userStream.Dispose();
4756             userStream = null;
4757             if (!MyCommon._endingFlag)
4758             {
4759                 if (UserStreamStopped != null)
4760                 {
4761                     UserStreamStopped();
4762                 }
4763             }
4764         }
4765
4766         public void ReconnectUserStream()
4767         {
4768             if (userStream != null)
4769             {
4770                 this.StartUserStream();
4771             }
4772         }
4773
4774         private class TwitterUserstream : IDisposable
4775         {
4776             public event Action<string> StatusArrived;
4777             public event Action Stopped;
4778             public event Action Started;
4779             private HttpTwitter twCon;
4780
4781             private Thread _streamThread;
4782             private bool _streamActive;
4783
4784             private bool _allAtreplies = false;
4785             private string _trackwords = "";
4786
4787             public TwitterUserstream(HttpTwitter twitterConnection)
4788             {
4789                 twCon = (HttpTwitter)twitterConnection.Clone();
4790             }
4791
4792             public void Start(bool allAtReplies, string trackwords)
4793             {
4794                 this.AllAtReplies = allAtReplies;
4795                 this.TrackWords = trackwords;
4796                 _streamActive = true;
4797                 if (_streamThread != null && _streamThread.IsAlive) return;
4798                 _streamThread = new Thread(UserStreamLoop);
4799                 _streamThread.Name = "UserStreamReceiver";
4800                 _streamThread.IsBackground = true;
4801                 _streamThread.Start();
4802             }
4803
4804             public bool Enabled
4805             {
4806                 get
4807                 {
4808                     return _streamActive;
4809                 }
4810             }
4811
4812             public bool AllAtReplies
4813             {
4814                 get
4815                 {
4816                     return _allAtreplies;
4817                 }
4818                 set
4819                 {
4820                     _allAtreplies = value;
4821                 }
4822             }
4823
4824             public string TrackWords
4825             {
4826                 get
4827                 {
4828                     return _trackwords;
4829                 }
4830                 set
4831                 {
4832                     _trackwords = value;
4833                 }
4834             }
4835
4836             private void UserStreamLoop()
4837             {
4838                 var sleepSec = 0;
4839                 do
4840                 {
4841                     Stream st = null;
4842                     StreamReader sr = null;
4843                     try
4844                     {
4845                         if (!MyCommon.IsNetworkAvailable())
4846                         {
4847                             sleepSec = 30;
4848                             continue;
4849                         }
4850
4851                         if (Started != null)
4852                         {
4853                             Started();
4854                         }
4855                         var res = twCon.UserStream(ref st, _allAtreplies, _trackwords, MyCommon.GetUserAgentString());
4856
4857                         switch (res)
4858                         {
4859                             case HttpStatusCode.OK:
4860                                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
4861                                 break;
4862                             case HttpStatusCode.Unauthorized:
4863                                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
4864                                 sleepSec = 120;
4865                                 continue;
4866                         }
4867
4868                         if (st == null)
4869                         {
4870                             sleepSec = 30;
4871                             //MyCommon.TraceOut("Stop:stream is null")
4872                             continue;
4873                         }
4874
4875                         sr = new StreamReader(st);
4876
4877                         while (_streamActive && !sr.EndOfStream && Twitter.AccountState == MyCommon.ACCOUNT_STATE.Valid)
4878                         {
4879                             if (StatusArrived != null)
4880                             {
4881                                 StatusArrived(sr.ReadLine());
4882                             }
4883                             //this.LastTime = Now;
4884                         }
4885
4886                         if (sr.EndOfStream || Twitter.AccountState == MyCommon.ACCOUNT_STATE.Invalid)
4887                         {
4888                             sleepSec = 30;
4889                             //MyCommon.TraceOut("Stop:EndOfStream")
4890                             continue;
4891                         }
4892                         break;
4893                     }
4894                     catch(WebException ex)
4895                     {
4896                         if (ex.Status == WebExceptionStatus.Timeout)
4897                         {
4898                             sleepSec = 30;                        //MyCommon.TraceOut("Stop:Timeout")
4899                         }
4900                         else if (ex.Response != null && (int)((HttpWebResponse)ex.Response).StatusCode == 420)
4901                         {
4902                             //MyCommon.TraceOut("Stop:Connection Limit")
4903                             break;
4904                         }
4905                         else
4906                         {
4907                             sleepSec = 30;
4908                             //MyCommon.TraceOut("Stop:WebException " + ex.Status.ToString())
4909                         }
4910                     }
4911                     catch(ThreadAbortException)
4912                     {
4913                         break;
4914                     }
4915                     catch(IOException)
4916                     {
4917                         sleepSec = 30;
4918                         //MyCommon.TraceOut("Stop:IOException with Active." + Environment.NewLine + ex.Message)
4919                     }
4920                     catch(ArgumentException ex)
4921                     {
4922                         //System.ArgumentException: ストリームを読み取れませんでした。
4923                         //サーバー側もしくは通信経路上で切断された場合?タイムアウト頻発後発生
4924                         sleepSec = 30;
4925                         MyCommon.TraceOut(ex, "Stop:ArgumentException");
4926                     }
4927                     catch(Exception ex)
4928                     {
4929                         MyCommon.TraceOut("Stop:Exception." + Environment.NewLine + ex.Message);
4930                         MyCommon.ExceptionOut(ex);
4931                         sleepSec = 30;
4932                     }
4933                     finally
4934                     {
4935                         if (_streamActive)
4936                         {
4937                             if (Stopped != null)
4938                             {
4939                                 Stopped();
4940                             }
4941                         }
4942                         twCon.RequestAbort();
4943                         if (sr != null) sr.Close();
4944                         if (sleepSec > 0)
4945                         {
4946                             var ms = 0;
4947                             while (_streamActive && ms < sleepSec * 1000)
4948                             {
4949                                 Thread.Sleep(500);
4950                                 ms += 500;
4951                             }
4952                         }
4953                         sleepSec = 0;
4954                     }
4955                 } while (this._streamActive);
4956
4957                 if (_streamActive)
4958                 {
4959                     if (Stopped != null)
4960                     {
4961                         Stopped();
4962                     }
4963                 }
4964                 MyCommon.TraceOut("Stop:EndLoop");
4965             }
4966
4967 #region "IDisposable Support"
4968             private bool disposedValue; // 重複する呼び出しを検出するには
4969
4970             // IDisposable
4971             protected virtual void Dispose(bool disposing)
4972             {
4973                 if (!this.disposedValue)
4974                 {
4975                     if (disposing)
4976                     {
4977                         // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
4978                         _streamActive = false;
4979                         if (_streamThread != null && _streamThread.IsAlive)
4980                         {
4981                             _streamThread.Abort();
4982                         }
4983                     }
4984
4985                     // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
4986                     // TODO: 大きなフィールドを null に設定します。
4987                 }
4988                 this.disposedValue = true;
4989             }
4990
4991             // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
4992             //protected Overrides void Finalize()
4993             //{
4994             //    // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
4995             //    Dispose(false)
4996             //    MyBase.Finalize()
4997             //}
4998
4999             // このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
5000             public void Dispose()
5001             {
5002                 // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
5003                 Dispose(true);
5004                 GC.SuppressFinalize(this);
5005             }
5006 #endregion
5007
5008         }
5009 #endregion
5010
5011 #region "IDisposable Support"
5012         private bool disposedValue; // 重複する呼び出しを検出するには
5013
5014         // IDisposable
5015         protected virtual void Dispose(bool disposing)
5016         {
5017             if (!this.disposedValue)
5018             {
5019                 if (disposing)
5020                 {
5021                     // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
5022                     this.StopUserStream();
5023                 }
5024
5025                 // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
5026                 // TODO: 大きなフィールドを null に設定します。
5027             }
5028             this.disposedValue = true;
5029         }
5030
5031         // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
5032         //protected Overrides void Finalize()
5033         //{
5034         //    // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
5035         //    Dispose(false)
5036         //    MyBase.Finalize()
5037         //}
5038
5039         // このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
5040         public void Dispose()
5041         {
5042             // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
5043             Dispose(true);
5044             GC.SuppressFinalize(this);
5045         }
5046 #endregion
5047     }
5048 }