OSDN Git Service

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