OSDN Git Service

c3f2a5ddccb629cd9984b6684cec1e544b3b6232
[opentween/open-tween.git] / OpenTween / MyCommon.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 //           (c) 2011      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
9 // All rights reserved.
10 // 
11 // This file is part of OpenTween.
12 // 
13 // This program is free software; you can redistribute it and/or modify it
14 // under the terms of the GNU General public License as published by the Free
15 // Software Foundation; either version 3 of the License, or (at your option)
16 // any later version.
17 // 
18 // This program is distributed in the hope that it will be useful, but
19 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License
21 // for more details. 
22 // 
23 // You should have received a copy of the GNU General public License along
24 // with this program. if not, see <http://www.gnu.org/licenses/>, or write to
25 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
26 // Boston, MA 02110-1301, USA.
27
28 using System;
29 using System.Collections.Generic;
30 using System.Linq;
31 using System.Text;
32 using System.IO;
33 using System.Windows.Forms;
34 using System.Web;
35 using System.Globalization;
36 using System.Security.Cryptography;
37 using System.Drawing;
38 using System.Drawing.Imaging;
39 using System.Collections;
40 using System.Security.Principal;
41 using System.Runtime.Serialization.Json;
42 using System.Reflection;
43 using System.Diagnostics;
44 using System.Text.RegularExpressions;
45 using System.Net.NetworkInformation;
46 using System.Runtime.InteropServices;
47 using OpenTween.Api;
48
49 namespace OpenTween
50 {
51     public sealed class MyCommon
52     {
53         private static readonly object LockObj = new object();
54         public static bool _endingFlag;        //終了フラグ
55         public static string cultureStr = null;
56         public static string settingPath;
57
58         public enum IconSizes
59         {
60             IconNone = 0,
61             Icon16 = 1,
62             Icon24 = 2,
63             Icon48 = 3,
64             Icon48_2 = 4,
65         }
66
67         public enum NameBalloonEnum
68         {
69             None,
70             UserID,
71             NickName,
72         }
73
74         public enum DispTitleEnum
75         {
76             None,
77             Ver,
78             Post,
79             UnreadRepCount,
80             UnreadAllCount,
81             UnreadAllRepCount,
82             UnreadCountAllCount,
83             OwnStatus,
84         }
85
86         public enum LogUnitEnum
87         {
88             Minute,
89             Hour,
90             Day,
91         }
92
93         public enum UploadFileType
94         {
95             Invalid,
96             Picture,
97             MultiMedia,
98         }
99
100         public enum UrlConverter
101         {
102             TinyUrl,
103             Isgd,
104             Twurl,
105             Bitly,
106             Jmp,
107             Uxnu,
108             //特殊
109             Nicoms,
110             //廃止
111             Unu = -1,
112         }
113
114         public enum OutputzUrlmode
115         {
116             twittercom,
117             twittercomWithUsername,
118         }
119
120         public enum HITRESULT
121         {
122             None,
123             Copy,
124             CopyAndMark,
125             Move,
126             Exclude,
127         }
128
129         public enum HttpTimeOut
130         {
131             MinValue = 10,
132             MaxValue = 120,
133             DefaultValue = 20,
134         }
135
136         //Backgroundworkerへ処理種別を通知するための引数用enum
137         public enum WORKERTYPE
138         {
139             Timeline,                //タイムライン取得
140             Reply,                   //返信取得
141             DirectMessegeRcv,        //受信DM取得
142             DirectMessegeSnt,        //送信DM取得
143             PostMessage,             //発言POST
144             FavAdd,                  //Fav追加
145             FavRemove,               //Fav削除
146             Follower,                //Followerリスト取得
147             Favorites,               //Fav取得
148             Retweet,                 //Retweetする
149             PublicSearch,            //公式検索
150             List,                    //Lists
151             Related,                 //関連発言
152             UserStream,              //UserStream
153             UserTimeline,            //UserTimeline
154             BlockIds,                //Blocking/ids
155             Configuration,           //Twitter Configuration読み込み
156             NoRetweetIds,            //RT非表示ユーザー取得
157             //////
158             ErrorState,              //エラー表示のみで後処理終了(認証エラー時など)
159         }
160
161         public static class DEFAULTTAB
162         {
163             public const string RECENT = "Recent";
164             public const string REPLY = "Reply";
165             public const string DM = "Direct";
166             public const string FAV = "Favorites";
167
168             //private string dummy;
169
170             //private object ReferenceEquals()
171             //{
172             //    return new object();
173             //}
174             //private object Equals()
175             //{
176             //    return new object();
177             //}
178         }
179
180         public static readonly object Block = null;
181         public static bool TraceFlag = false;
182
183 #if DEBUG
184         public static bool DebugBuild = true;
185 #else
186         public static bool DebugBuild = false;
187 #endif
188
189         public enum ACCOUNT_STATE
190         {
191             Valid,
192             Invalid,
193         }
194
195         public enum REPLY_ICONSTATE
196         {
197             None,
198             StaticIcon,
199             BlinkIcon,
200         }
201
202         [FlagsAttribute()]
203         public enum EVENTTYPE
204         {
205             None = 0,
206             Favorite = 1,
207             Unfavorite = 2,
208             Follow = 4,
209             ListMemberAdded = 8,
210             ListMemberRemoved = 16,
211             Block = 32,
212             Unblock = 64,
213             UserUpdate = 128,
214             Deleted = 256,
215             ListCreated = 512,
216             ListUpdated = 1024,
217             Unfollow = 2048,
218             ListUserSubscribed = 4096,
219             ListUserUnsubscribed = 8192,
220
221             All = (None | Favorite | Unfavorite | Follow | ListMemberAdded | ListMemberRemoved |
222                    Block | Unblock | UserUpdate | Deleted | ListCreated | ListUpdated | Unfollow |
223                    ListUserSubscribed | ListUserUnsubscribed),
224         }
225
226         public static string GetErrorLogPath()
227         {
228             return Path.Combine(Path.GetDirectoryName(MyCommon.EntryAssembly.Location), "ErrorLogs");
229         }
230
231         public static void TraceOut(WebApiException ex)
232         {
233             var message = ExceptionOutMessage(ex);
234
235             if (ex.ResponseText != null)
236                 message += Environment.NewLine + "------- Response Data -------" + Environment.NewLine + ex.ResponseText;
237
238             TraceOut(TraceFlag, message);
239         }
240
241         public static void TraceOut(Exception ex, string Message)
242         {
243             var buf = ExceptionOutMessage(ex);
244             TraceOut(TraceFlag, Message + Environment.NewLine + buf);
245         }
246
247         public static void TraceOut(string Message)
248         {
249             TraceOut(TraceFlag, Message);
250         }
251
252         public static void TraceOut(bool OutputFlag, string Message)
253         {
254             lock (LockObj)
255             {
256                 if (!OutputFlag) return;
257
258                 var logPath = MyCommon.GetErrorLogPath();
259                 if (!Directory.Exists(logPath))
260                     Directory.CreateDirectory(logPath);
261
262                 var now = DateTime.Now;
263                 var fileName = string.Format("{0}Trace-{1:0000}{2:00}{3:00}-{4:00}{5:00}{6:00}.log", GetAssemblyName(), now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second);
264                 fileName = Path.Combine(logPath, fileName);
265
266                 using (var writer = new StreamWriter(fileName))
267                 {
268                     writer.WriteLine("**** TraceOut: {0} ****", DateTime.Now.ToString());
269                     writer.WriteLine(Properties.Resources.TraceOutText1, ApplicationSettings.FeedbackEmailAddress);
270                     writer.WriteLine(Properties.Resources.TraceOutText2, ApplicationSettings.FeedbackTwitterName);
271                     writer.WriteLine();
272                     writer.WriteLine(Properties.Resources.TraceOutText3);
273                     writer.WriteLine(Properties.Resources.TraceOutText4, Environment.OSVersion.VersionString);
274                     writer.WriteLine(Properties.Resources.TraceOutText5, Environment.Version.ToString());
275                     writer.WriteLine(Properties.Resources.TraceOutText6, MyCommon.GetAssemblyName(), fileVersion);
276                     writer.WriteLine(Message);
277                     writer.WriteLine();
278                 }
279             }
280         }
281
282         // エラー内容をバッファに書き出し
283         // 注意:最終的にファイル出力されるエラーログに記録されるため次の情報は書き出さない
284         // 文頭メッセージ、権限、動作環境
285         // Dataプロパティにある終了許可フラグのパースもここで行う
286
287         public static string ExceptionOutMessage(Exception ex)
288         {
289             bool IsTerminatePermission = true;
290             return ExceptionOutMessage(ex, ref IsTerminatePermission);
291         }
292
293         public static string ExceptionOutMessage(Exception ex, ref bool IsTerminatePermission)
294         {
295             if (ex == null) return "";
296
297             var buf = new StringBuilder();
298
299             buf.AppendFormat(Properties.Resources.UnhandledExceptionText8, ex.GetType().FullName, ex.Message);
300             buf.AppendLine();
301             if (ex.Data != null)
302             {
303                 var needHeader = true;
304                 foreach (DictionaryEntry dt in ex.Data)
305                 {
306                     if (needHeader)
307                     {
308                         buf.AppendLine();
309                         buf.AppendLine("-------Extra Information-------");
310                         needHeader = false;
311                     }
312                     buf.AppendFormat("{0}  :  {1}", dt.Key, dt.Value);
313                     buf.AppendLine();
314                     if (dt.Key.Equals("IsTerminatePermission"))
315                     {
316                         IsTerminatePermission = (bool)dt.Value;
317                     }
318                 }
319                 if (!needHeader)
320                 {
321                     buf.AppendLine("-----End Extra Information-----");
322                 }
323             }
324             buf.AppendLine(ex.StackTrace);
325             buf.AppendLine();
326
327             //InnerExceptionが存在する場合書き出す
328             var _ex = ex.InnerException;
329             var nesting = 0;
330             while (_ex != null)
331             {
332                 buf.AppendFormat("-----InnerException[{0}]-----\r\n", nesting);
333                 buf.AppendLine();
334                 buf.AppendFormat(Properties.Resources.UnhandledExceptionText8, _ex.GetType().FullName, _ex.Message);
335                 buf.AppendLine();
336                 if (_ex.Data != null)
337                 {
338                     var needHeader = true;
339
340                     foreach (DictionaryEntry dt in _ex.Data)
341                     {
342                         if (needHeader)
343                         {
344                             buf.AppendLine();
345                             buf.AppendLine("-------Extra Information-------");
346                             needHeader = false;
347                         }
348                         buf.AppendFormat("{0}  :  {1}", dt.Key, dt.Value);
349                         if (dt.Key.Equals("IsTerminatePermission"))
350                         {
351                             IsTerminatePermission = (bool)dt.Value;
352                         }
353                     }
354                     if (!needHeader)
355                     {
356                         buf.AppendLine("-----End Extra Information-----");
357                     }
358                 }
359                 buf.AppendLine(_ex.StackTrace);
360                 buf.AppendLine();
361                 nesting++;
362                 _ex = _ex.InnerException;
363             }
364             return buf.ToString();
365         }
366
367         public static bool ExceptionOut(Exception ex)
368         {
369             lock (LockObj)
370             {
371                 var IsTerminatePermission = true;
372
373                 var logPath = MyCommon.GetErrorLogPath();
374                 if (!Directory.Exists(logPath))
375                     Directory.CreateDirectory(logPath);
376
377                 var now = DateTime.Now;
378                 var fileName = string.Format("{0}-{1:0000}{2:00}{3:00}-{4:00}{5:00}{6:00}.log", GetAssemblyName(), now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second);
379                 fileName = Path.Combine(logPath, fileName);
380
381                 using (var writer = new StreamWriter(fileName))
382                 {
383                     var ident = WindowsIdentity.GetCurrent();
384                     var princ = new WindowsPrincipal(ident);
385
386                     writer.WriteLine(Properties.Resources.UnhandledExceptionText1, DateTime.Now.ToString());
387                     writer.WriteLine(Properties.Resources.UnhandledExceptionText2, ApplicationSettings.FeedbackEmailAddress);
388                     writer.WriteLine(Properties.Resources.UnhandledExceptionText3, ApplicationSettings.FeedbackTwitterName);
389                     // 権限書き出し
390                     writer.WriteLine(Properties.Resources.UnhandledExceptionText11 + princ.IsInRole(WindowsBuiltInRole.Administrator).ToString());
391                     writer.WriteLine(Properties.Resources.UnhandledExceptionText12 + princ.IsInRole(WindowsBuiltInRole.User).ToString());
392                     writer.WriteLine();
393                     // OSVersion,AppVersion書き出し
394                     writer.WriteLine(Properties.Resources.UnhandledExceptionText4);
395                     writer.WriteLine(Properties.Resources.UnhandledExceptionText5, Environment.OSVersion.VersionString);
396                     writer.WriteLine(Properties.Resources.UnhandledExceptionText6, Environment.Version.ToString());
397                     writer.WriteLine(Properties.Resources.UnhandledExceptionText7, MyCommon.GetAssemblyName(), fileVersion);
398
399                     writer.Write(ExceptionOutMessage(ex, ref IsTerminatePermission));
400                     writer.Flush();
401                 }
402
403                 switch (MessageBox.Show(MyCommon.ReplaceAppName(string.Format(Properties.Resources.UnhandledExceptionText9, fileName, ApplicationSettings.FeedbackEmailAddress, ApplicationSettings.FeedbackTwitterName, Environment.NewLine)),
404                                    Properties.Resources.UnhandledExceptionText10, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Error))
405                 {
406                     case DialogResult.Yes:
407                         Process.Start(fileName);
408                         return false;
409                     case DialogResult.No:
410                         return false;
411                     case DialogResult.Cancel:
412                         return IsTerminatePermission;
413                     default:
414                         throw new Exception("");
415                 }
416             }
417         }
418
419         /// <summary>
420         /// URLに含まれているマルチバイト文字列を%xx形式でエンコードします。
421         /// <newpara>
422         /// マルチバイト文字のコードはUTF-8またはUnicodeで自動的に判断します。
423         /// </newpara>
424         /// </summary>
425         /// <param name="_input">エンコード対象のURL</param>
426         /// <returns>マルチバイト文字の部分をUTF-8/%xx形式でエンコードした文字列を返します。</returns>
427
428         public static string urlEncodeMultibyteChar(string _input)
429         {
430             Uri uri = null;
431             var sb = new StringBuilder(256);
432             var result = "";
433             var c_ = 'd';
434             foreach (var c in _input)
435             {
436                 c_ = c;
437                 if (Convert.ToInt32(c) > 127 || c == '%') break;
438             }
439             if (Convert.ToInt32(c_) <= 127 && c_ != '%') return _input;
440
441             var input = Uri.UnescapeDataString(_input);
442         retry:
443             foreach (char c in input)
444             {
445                 if (Convert.ToInt32(c) > 255)
446                 {
447                     // Unicodeの場合(1charが複数のバイトで構成されている)
448                     // Uriクラスをnewして再構成し、入力をPathAndQueryのみとしてやり直す
449                     foreach (var b in Encoding.UTF8.GetBytes(c.ToString()))
450                     {
451                         sb.AppendFormat("%{0:X2}", b);
452                     }
453                 }
454                 else if (Convert.ToInt32(c) > 127 || c == '%')
455                 {
456                     // UTF-8の場合
457                     // Uriクラスをnewして再構成し、入力をinputからAuthority部分を除去してやり直す
458                     if (uri == null)
459                     {
460                         uri = new Uri(input);
461                         input = input.Remove(0, uri.GetLeftPart(UriPartial.Authority).Length);
462                         sb.Length = 0;
463                         goto retry;
464                     }
465                     else
466                     {
467                         sb.Append("%" + Convert.ToInt16(c).ToString("X2").ToUpper());
468                     }
469                 }
470                 else
471                 {
472                     sb.Append(c);
473                 }
474             }
475
476             if (uri == null)
477             {
478                 result = sb.ToString();
479             }
480             else
481             {
482                 result = uri.GetLeftPart(UriPartial.Authority) + sb.ToString();
483             }
484
485             return result;
486         }
487
488         /// <summary>
489         /// URLのドメイン名をPunycode展開します。
490         /// <para>
491         /// ドメイン名がIDNでない場合はそのまま返します。
492         /// ドメインラベルの区切り文字はFULLSTOP(.、U002E)に置き換えられます。
493         /// </para>
494         /// </summary>
495         /// <param name="input">展開対象のURL</param>
496         /// <returns>IDNが含まれていた場合はPunycodeに展開したURLをを返します。Punycode展開時にエラーが発生した場合はnullを返します。</returns>
497         public static string IDNEncode(string inputUrl)
498         {
499             try
500             {
501                 var uriBuilder = new UriBuilder(inputUrl);
502
503                 var idnConverter = new IdnMapping();
504                 uriBuilder.Host = idnConverter.GetAscii(uriBuilder.Host);
505
506                 return uriBuilder.Uri.ToString();
507             }
508             catch (Exception)
509             {
510                 return null;
511             }
512         }
513
514         public static string IDNDecode(string inputUrl)
515         {
516             try
517             {
518                 var uriBuilder = new UriBuilder(inputUrl);
519
520                 var idnConverter = new IdnMapping();
521                 uriBuilder.Host = idnConverter.GetUnicode(uriBuilder.Host);
522
523                 return uriBuilder.Uri.ToString();
524             }
525             catch (Exception)
526             {
527                 return null;
528             }
529         }
530
531         /// <summary>
532         /// URL を画面上で人間に読みやすい文字列に変換する(エスケープ解除など)
533         /// </summary>
534         public static string ConvertToReadableUrl(string inputUrl)
535         {
536             var outputUrl = inputUrl;
537
538             // Punycodeをデコードする
539             outputUrl = MyCommon.IDNDecode(outputUrl);
540
541             // URL内で特殊な意味を持つ記号は元の文字に変換されることを避けるために二重エスケープする
542             // 参考: Firefoxの losslessDecodeURI() 関数
543             //   http://hg.mozilla.org/mozilla-central/annotate/FIREFOX_AURORA_27_BASE/browser/base/content/browser.js#l2128
544             outputUrl = Regex.Replace(outputUrl, @"%(2[3456BCF]|3[ABDF]|40)", @"%25$1", RegexOptions.IgnoreCase);
545
546             // エスケープを解除する
547             outputUrl = Uri.UnescapeDataString(outputUrl);
548
549             return outputUrl;
550         }
551
552         public static void MoveArrayItem(int[] values, int idx_fr, int idx_to)
553         {
554             var moved_value = values[idx_fr];
555             var num_moved = Math.Abs(idx_fr - idx_to);
556
557             if (idx_to < idx_fr)
558             {
559                 Array.Copy(values, idx_to, values,
560                     idx_to + 1, num_moved);
561             }
562             else
563             {
564                 Array.Copy(values, idx_fr + 1, values,
565                     idx_fr, num_moved);
566             }
567
568             values[idx_to] = moved_value;
569         }
570
571         public static string EncryptString(string str)
572         {
573             if (string.IsNullOrEmpty(str)) return "";
574
575             //文字列をバイト型配列にする
576             var bytesIn = Encoding.UTF8.GetBytes(str);
577
578             //DESCryptoServiceProviderオブジェクトの作成
579             using (var des = new DESCryptoServiceProvider())
580             {
581                 //共有キーと初期化ベクタを決定
582                 //パスワードをバイト配列にする
583                 var bytesKey = Encoding.UTF8.GetBytes("_tween_encrypt_key_");
584                 //共有キーと初期化ベクタを設定
585                 des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
586                 des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
587
588                 MemoryStream msOut = null;
589                 ICryptoTransform desdecrypt = null;
590
591                 try
592                 {
593                     //暗号化されたデータを書き出すためのMemoryStream
594                     msOut = new MemoryStream();
595
596                     //DES暗号化オブジェクトの作成
597                     desdecrypt = des.CreateEncryptor();
598
599                     //書き込むためのCryptoStreamの作成
600                     using (CryptoStream cryptStream = new CryptoStream(msOut, desdecrypt, CryptoStreamMode.Write))
601                     {
602                         //Disposeが重複して呼ばれないようにする
603                         MemoryStream msTmp = msOut;
604                         msOut = null;
605                         desdecrypt = null;
606
607                         //書き込む
608                         cryptStream.Write(bytesIn, 0, bytesIn.Length);
609                         cryptStream.FlushFinalBlock();
610                         //暗号化されたデータを取得
611                         var bytesOut = msTmp.ToArray();
612
613                         //Base64で文字列に変更して結果を返す
614                         return Convert.ToBase64String(bytesOut);
615                     }
616                 }
617                 finally
618                 {
619                     if (msOut != null) msOut.Dispose();
620                     if (desdecrypt != null) desdecrypt.Dispose();
621                 }
622             }
623         }
624
625         public static string DecryptString(string str)
626         {
627             if (string.IsNullOrEmpty(str)) return "";
628
629             //DESCryptoServiceProviderオブジェクトの作成
630             using (var des = new System.Security.Cryptography.DESCryptoServiceProvider())
631             {
632                 //共有キーと初期化ベクタを決定
633                 //パスワードをバイト配列にする
634                 var bytesKey = Encoding.UTF8.GetBytes("_tween_encrypt_key_");
635                 //共有キーと初期化ベクタを設定
636                 des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
637                 des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
638
639                 //Base64で文字列をバイト配列に戻す
640                 var bytesIn = Convert.FromBase64String(str);
641
642                 MemoryStream msIn = null;
643                 ICryptoTransform desdecrypt = null;
644                 CryptoStream cryptStreem = null;
645
646                 try
647                 {
648                     //暗号化されたデータを読み込むためのMemoryStream
649                     msIn = new MemoryStream(bytesIn);
650                     //DES復号化オブジェクトの作成
651                     desdecrypt = des.CreateDecryptor();
652                     //読み込むためのCryptoStreamの作成
653                     cryptStreem = new CryptoStream(msIn, desdecrypt, CryptoStreamMode.Read);
654
655                     //Disposeが重複して呼ばれないようにする
656                     msIn = null;
657                     desdecrypt = null;
658
659                     //復号化されたデータを取得するためのStreamReader
660                     using (StreamReader srOut = new StreamReader(cryptStreem, Encoding.UTF8))
661                     {
662                         //Disposeが重複して呼ばれないようにする
663                         cryptStreem = null;
664
665                         //復号化されたデータを取得する
666                         var result = srOut.ReadToEnd();
667
668                         return result;
669                     }
670                 }
671                 finally
672                 {
673                     if (msIn != null) msIn.Dispose();
674                     if (desdecrypt != null) desdecrypt.Dispose();
675                     if (cryptStreem != null) cryptStreem.Dispose();
676                 }
677             }
678         }
679
680         public static byte[] ResizeBytesArray(byte[] bytes,
681                                     int newSize)
682         {
683             var newBytes = new byte[newSize];
684             if (bytes.Length <= newSize)
685             {
686                 foreach (var i in Enumerable.Range(0, bytes.Length))
687                 {
688                     newBytes[i] = bytes[i];
689                 }
690             }
691             else
692             {
693                 var pos = 0;
694                 foreach (var i in Enumerable.Range(0, bytes.Length))
695                 {
696                     newBytes[pos] = unchecked((byte)(newBytes[pos] ^ bytes[i]));
697                     pos++;
698                     if (pos >= newBytes.Length)
699                     {
700                         pos = 0;
701                     }
702                 }
703             }
704             return newBytes;
705         }
706
707         public static bool IsNT6()
708         {
709             //NT6 kernelかどうか検査
710             return Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major == 6;
711         }
712
713         [FlagsAttribute()]
714         public enum TabUsageType
715         {
716             Undefined = 0,
717             Home = 1,      //Unique
718             Mentions = 2,     //Unique
719             DirectMessage = 4,   //Unique
720             Favorites = 8,       //Unique
721             UserDefined = 16,
722             LocalQuery = 32,      //Pin(no save/no save query/distribute/no update(normal update))
723             Profile = 64,         //Pin(save/no distribute/manual update)
724             PublicSearch = 128,    //Pin(save/no distribute/auto update)
725             Lists = 256,
726             Related = 512,
727             UserTimeline = 1024,
728             //RTMyTweet
729             //RTByOthers
730             //RTByMe
731         }
732
733         public static string fileVersion = "";
734
735         public static string GetUserAgentString()
736         {
737             if (string.IsNullOrEmpty(fileVersion))
738             {
739                 throw new Exception("fileversion is not Initialized.");
740             }
741             return GetAssemblyName() + "/" + fileVersion;
742         }
743
744         public static TwitterApiStatus TwitterApiInfo = new TwitterApiStatus();
745
746         public static bool IsAnimatedGif(string filename)
747         {
748             Image img = null;
749             try
750             {
751                 img = Image.FromFile(filename);
752                 if (img == null) return false;
753                 if (img.RawFormat.Guid == ImageFormat.Gif.Guid)
754                 {
755                     var fd = new FrameDimension(img.FrameDimensionsList[0]);
756                     var fd_count = img.GetFrameCount(fd);
757                     if (fd_count > 1)
758                     {
759                         return true;
760                     }
761                     else
762                     {
763                         return false;
764                     }
765                 }
766                 return false;
767             }
768             catch (Exception)
769             {
770                 return false;
771             }
772             finally
773             {
774                 if (img != null) img.Dispose();
775             }
776         }
777
778         public static DateTime DateTimeParse(string input)
779         {
780             DateTime rslt;
781             string[] format = {
782                 "ddd MMM dd HH:mm:ss zzzz yyyy",
783                 "ddd, d MMM yyyy HH:mm:ss zzzz",
784             };
785             foreach (var fmt in format)
786             {
787                 if (DateTime.TryParseExact(input,
788                                           fmt,
789                                           DateTimeFormatInfo.InvariantInfo,
790                                           DateTimeStyles.None,
791                                           out rslt))
792                 {
793                     return rslt;
794                 }
795                 else
796                 {
797                     continue;
798                 }
799             }
800             TraceOut("Parse Error(DateTimeFormat) : " + input);
801             return new DateTime();
802         }
803
804         public static T CreateDataFromJson<T>(string content)
805         {
806             T data;
807             var buf = Encoding.Unicode.GetBytes(content);
808             using (var stream = new MemoryStream(buf))
809             {
810                 data = (T)((new DataContractJsonSerializer(typeof(T))).ReadObject(stream));
811             }
812             return data;
813         }
814
815         public static bool IsNetworkAvailable()
816         {
817             try
818             {
819                 return NetworkInterface.GetIsNetworkAvailable();
820             }
821             catch(Exception)
822             {
823                 return false;
824             }
825         }
826
827         public static bool IsValidEmail(string strIn)
828         {
829             // Return true if strIn is in valid e-mail format.
830             return Regex.IsMatch(strIn,
831                    @"^(?("")("".+?""@)|(([0-9a-zA-Z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])@))" +
832                    @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$");
833         }
834
835         /// <summary>
836         /// 指定された修飾キーが押されている状態かを取得します。
837         /// </summary>
838         /// <param name="keys">状態を調べるキー</param>
839         /// <returns><paramref name="keys"/> で指定された修飾キーがすべて押されている状態であれば true。それ以外であれば false。</returns>
840         public static bool IsKeyDown(params Keys[] keys)
841         {
842             return MyCommon._IsKeyDown(Control.ModifierKeys, keys);
843         }
844
845         internal static bool _IsKeyDown(Keys modifierKeys, Keys[] targetKeys)
846         {
847             foreach (Keys key in targetKeys)
848             {
849                 if ((modifierKeys & key) != key)
850                 {
851                     return false;
852                 }
853             }
854             return true;
855         }
856
857         /// <summary>
858         /// アプリケーションのアセンブリ名を取得します。
859         /// </summary>
860         /// <remarks>
861         /// VB.NETの<code>My.Application.Info.AssemblyName</code>と(ほぼ)同じ動作をします。
862         /// </remarks>
863         /// <returns>アプリケーションのアセンブリ名</returns>
864         public static string GetAssemblyName()
865         {
866             return MyCommon.EntryAssembly.GetName().Name;
867         }
868
869         internal static _Assembly EntryAssembly = Assembly.GetEntryAssembly();
870
871         /// <summary>
872         /// 文字列中に含まれる %AppName% をアプリケーション名に置換する
873         /// </summary>
874         /// <param name="orig">対象となる文字列</param>
875         /// <returns>置換後の文字列</returns>
876         public static string ReplaceAppName(string orig)
877         {
878             return MyCommon.ReplaceAppName(orig, Application.ProductName);
879         }
880
881         /// <summary>
882         /// 文字列中に含まれる %AppName% をアプリケーション名に置換する
883         /// </summary>
884         /// <param name="orig">対象となる文字列</param>
885         /// <param name="appname">アプリケーション名</param>
886         /// <returns>置換後の文字列</returns>
887         public static string ReplaceAppName(string orig, string appname)
888         {
889             return orig.Replace("%AppName%", appname);
890         }
891
892         /// <summary>
893         /// 表示用のバージョン番号の文字列を生成する
894         /// </summary>
895         /// <remarks>
896         /// バージョン1.0.0.1のように末尾が0でない(=開発版)の場合は「1.0.1-beta1」が出力される
897         /// </remarks>
898         /// <returns>
899         /// 生成されたバージョン番号の文字列
900         /// </returns>
901         public static string GetReadableVersion(string fileVersion = null)
902         {
903             if (fileVersion == null)
904             {
905                 fileVersion = MyCommon.fileVersion;
906             }
907
908             if (string.IsNullOrEmpty(fileVersion))
909             {
910                 return null;
911             }
912
913             int[] version = fileVersion.Split('.')
914                 .Select(x => int.Parse(x)).ToArray();
915
916             if (version[3] == 0)
917             {
918                 return string.Format("{0}.{1}.{2}", version[0], version[1], version[2]);
919             }
920             else
921             {
922                 version[2] = version[2] + 1;
923
924                 // 10を越えたら桁上げ
925                 if (version[2] >= 10)
926                 {
927                     version[1] += version[2] / 10;
928                     version[2] %= 10;
929
930                     if (version[1] >= 10)
931                     {
932                         version[0] += version[1] / 10;
933                         version[1] %= 10;
934                     }
935                 }
936
937                 return string.Format("{0}.{1}.{2}-beta{3}", version[0], version[1], version[2], version[3]);
938             }
939         }
940
941         public const string TwitterUrl = "https://twitter.com/";
942
943         public static string GetStatusUrl(PostClass post)
944         {
945             if (post.RetweetedId == null)
946                 return GetStatusUrl(post.ScreenName, post.StatusId);
947             else
948                 return GetStatusUrl(post.ScreenName, post.RetweetedId.Value);
949         }
950
951         public static string GetStatusUrl(string screenName, long statusId)
952         {
953             return TwitterUrl + screenName + "/status/" + statusId.ToString();
954         }
955     }
956 }