OSDN Git Service

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