OSDN Git Service

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