OSDN Git Service

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