OSDN Git Service

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