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.
11 // This file is part of OpenTween.
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)
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
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.
29 using System.Collections.Generic;
33 using System.Windows.Forms;
35 using System.Globalization;
36 using System.Security.Cryptography;
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;
49 [Microsoft.VisualBasic.CompilerServices.StandardModule]
50 public sealed class MyCommon
52 private static readonly object LockObj = new object();
53 public static bool _endingFlag; //終了フラグ
54 public static string cultureStr = null;
55 public static string settingPath;
66 public enum NameBalloonEnum
73 public enum DispTitleEnum
85 public enum LogUnitEnum
92 public enum UploadFileType
99 public enum UrlConverter
113 public enum OutputzUrlmode
116 twittercomWithUsername,
119 public enum HITRESULT
128 public enum HttpTimeOut
135 //Backgroundworkerへ処理種別を通知するための引数用enum
136 public enum WORKERTYPE
140 DirectMessegeRcv, //受信DM取得
141 DirectMessegeSnt, //送信DM取得
142 PostMessage, //発言POST
145 Follower, //Followerリスト取得
152 UserStream, //UserStream
153 UserTimeline, //UserTimeline
154 BlockIds, //Blocking/ids
155 Configuration, //Twitter Configuration読み込み
157 ErrorState, //エラー表示のみで後処理終了(認証エラー時など)
160 public static class DEFAULTTAB
162 public const string RECENT = "Recent";
163 public const string REPLY = "Reply";
164 public const string DM = "Direct";
165 public const string FAV = "Favorites";
167 //private string dummy;
169 //private object ReferenceEquals()
171 // return new object();
173 //private object Equals()
175 // return new object();
179 public static readonly object Block = null;
180 public static bool TraceFlag = false;
183 public static bool DebugBuild = true;
185 public static bool DebugBuild = false;
188 public enum ACCOUNT_STATE
194 public enum REPLY_ICONSTATE
202 public enum EVENTTYPE
209 ListMemberRemoved = 16,
217 All = (None | Favorite | Unfavorite | Follow | ListMemberAdded | ListMemberRemoved |
218 Block | Unblock | UserUpdate | Deleted | ListCreated | ListUpdated),
221 public static void TraceOut(Exception ex, string Message)
223 var buf = ExceptionOutMessage(ex);
224 TraceOut(TraceFlag, Message + Environment.NewLine + buf);
227 public static void TraceOut(string Message)
229 TraceOut(TraceFlag, Message);
232 public static void TraceOut(bool OutputFlag, string Message)
236 if (!OutputFlag) return;
237 var now = DateTime.Now;
238 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);
240 using (var writer = new StreamWriter(fileName))
242 writer.WriteLine("**** TraceOut: {0} ****", DateTime.Now.ToString());
243 writer.WriteLine(Properties.Resources.TraceOutText1, ApplicationSettings.FeedbackEmailAddress);
244 writer.WriteLine(Properties.Resources.TraceOutText2, ApplicationSettings.FeedbackTwitterName);
246 writer.WriteLine(Properties.Resources.TraceOutText3);
247 writer.WriteLine(Properties.Resources.TraceOutText4, Environment.OSVersion.VersionString);
248 writer.WriteLine(Properties.Resources.TraceOutText5, Environment.Version.ToString());
249 writer.WriteLine(Properties.Resources.TraceOutText6, MyCommon.GetAssemblyName(), fileVersion);
250 writer.WriteLine(Message);
257 // 注意:最終的にファイル出力されるエラーログに記録されるため次の情報は書き出さない
259 // Dataプロパティにある終了許可フラグのパースもここで行う
261 public static string ExceptionOutMessage(Exception ex)
263 bool IsTerminatePermission = true;
264 return ExceptionOutMessage(ex, ref IsTerminatePermission);
267 public static string ExceptionOutMessage(Exception ex, ref bool IsTerminatePermission)
269 if (ex == null) return "";
271 var buf = new StringBuilder();
273 buf.AppendFormat(Properties.Resources.UnhandledExceptionText8, ex.GetType().FullName, ex.Message);
277 var needHeader = true;
278 foreach (DictionaryEntry dt in ex.Data)
283 buf.AppendLine("-------Extra Information-------");
286 buf.AppendFormat("{0} : {1}", dt.Key, dt.Value);
288 if (dt.Key.Equals("IsTerminatePermission"))
290 IsTerminatePermission = (bool)dt.Value;
295 buf.AppendLine("-----End Extra Information-----");
298 buf.AppendLine(ex.StackTrace);
301 //InnerExceptionが存在する場合書き出す
302 var _ex = ex.InnerException;
306 buf.AppendFormat("-----InnerException[{0}]-----\r\n", nesting);
308 buf.AppendFormat(Properties.Resources.UnhandledExceptionText8, _ex.GetType().FullName, _ex.Message);
310 if (_ex.Data != null)
312 var needHeader = true;
314 foreach (DictionaryEntry dt in _ex.Data)
319 buf.AppendLine("-------Extra Information-------");
322 buf.AppendFormat("{0} : {1}", dt.Key, dt.Value);
323 if (dt.Key.Equals("IsTerminatePermission"))
325 IsTerminatePermission = (bool)dt.Value;
330 buf.AppendLine("-----End Extra Information-----");
333 buf.AppendLine(_ex.StackTrace);
336 _ex = _ex.InnerException;
338 return buf.ToString();
341 public static bool ExceptionOut(Exception ex)
345 var IsTerminatePermission = true;
346 var now = DateTime.Now;
347 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);
349 using (var writer = new StreamWriter(fileName))
351 var ident = WindowsIdentity.GetCurrent();
352 var princ = new WindowsPrincipal(ident);
354 writer.WriteLine(Properties.Resources.UnhandledExceptionText1, DateTime.Now.ToString());
355 writer.WriteLine(Properties.Resources.UnhandledExceptionText2, ApplicationSettings.FeedbackEmailAddress);
356 writer.WriteLine(Properties.Resources.UnhandledExceptionText3, ApplicationSettings.FeedbackTwitterName);
358 writer.WriteLine(Properties.Resources.UnhandledExceptionText11 + princ.IsInRole(WindowsBuiltInRole.Administrator).ToString());
359 writer.WriteLine(Properties.Resources.UnhandledExceptionText12 + princ.IsInRole(WindowsBuiltInRole.User).ToString());
361 // OSVersion,AppVersion書き出し
362 writer.WriteLine(Properties.Resources.UnhandledExceptionText4);
363 writer.WriteLine(Properties.Resources.UnhandledExceptionText5, Environment.OSVersion.VersionString);
364 writer.WriteLine(Properties.Resources.UnhandledExceptionText6, Environment.Version.ToString());
365 writer.WriteLine(Properties.Resources.UnhandledExceptionText7, MyCommon.GetAssemblyName(), fileVersion);
367 writer.Write(ExceptionOutMessage(ex, ref IsTerminatePermission));
371 switch (MessageBox.Show(MyCommon.ReplaceAppName(string.Format(Properties.Resources.UnhandledExceptionText9, fileName, ApplicationSettings.FeedbackEmailAddress, ApplicationSettings.FeedbackTwitterName, Environment.NewLine)),
372 Properties.Resources.UnhandledExceptionText10, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Error))
374 case DialogResult.Yes:
375 Process.Start(fileName);
377 case DialogResult.No:
379 case DialogResult.Cancel:
380 return IsTerminatePermission;
382 throw new Exception("");
388 /// URLに含まれているマルチバイト文字列を%xx形式でエンコードします。
390 /// マルチバイト文字のコードはUTF-8またはUnicodeで自動的に判断します。
393 /// <param name = input>エンコード対象のURL</param>
394 /// <returns>マルチバイト文字の部分をUTF-8/%xx形式でエンコードした文字列を返します。</returns>
396 public static string urlEncodeMultibyteChar(string _input)
399 var sb = new StringBuilder(256);
402 foreach (var c in _input)
405 if (Convert.ToInt32(c) > 127) break;
407 if (Convert.ToInt32(c_) <= 127) return _input;
409 var input = HttpUtility.UrlDecode(_input);
411 foreach (char c in input)
413 if (Convert.ToInt32(c) > 255)
415 // Unicodeの場合(1charが複数のバイトで構成されている)
416 // Uriクラスをnewして再構成し、入力をPathAndQueryのみとしてやり直す
417 foreach (var b in Encoding.UTF8.GetBytes(c.ToString()))
419 sb.AppendFormat("%{0:X2}", b);
422 else if (Convert.ToInt32(c) > 127 || c == '%')
425 // Uriクラスをnewして再構成し、入力をinputからAuthority部分を除去してやり直す
428 uri = new Uri(input);
429 input = input.Remove(0, uri.GetLeftPart(UriPartial.Authority).Length);
435 sb.Append("%" + Convert.ToInt16(c).ToString("X2").ToUpper());
446 result = sb.ToString();
450 result = uri.GetLeftPart(UriPartial.Authority) + sb.ToString();
457 ////// URLのドメイン名をPunycode展開します。
459 ////// ドメイン名がIDNでない場合はそのまま返します。
460 ////// ドメインラベルの区切り文字はFULLSTOP(.、U002E)に置き換えられます。
463 ////// <param name="input">展開対象のURL</param>
464 ////// <returns>IDNが含まれていた場合はPunycodeに展開したURLをを返します。Punycode展開時にエラーが発生した場合はnullを返します。</returns>
466 public static string IDNDecode(string input)
468 var IDNConverter = new IdnMapping();
470 if (!input.Contains("://")) return null;
478 Domain = input.Split('/')[2];
479 AsciiDomain = IDNConverter.GetAscii(Domain);
486 return input.Replace("://" + Domain, "://" + AsciiDomain);
489 public static void MoveArrayItem(int[] values, int idx_fr, int idx_to)
491 var moved_value = values[idx_fr];
492 var num_moved = Math.Abs(idx_fr - idx_to);
496 Array.Copy(values, idx_to, values,
497 idx_to + 1, num_moved);
501 Array.Copy(values, idx_fr + 1, values,
505 values[idx_to] = moved_value;
508 public static string EncryptString(string str)
510 if (string.IsNullOrEmpty(str)) return "";
513 var bytesIn = Encoding.UTF8.GetBytes(str);
515 //DESCryptoServiceProviderオブジェクトの作成
516 using (var des = new DESCryptoServiceProvider())
520 var bytesKey = Encoding.UTF8.GetBytes("_tween_encrypt_key_");
522 des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
523 des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
525 MemoryStream msOut = null;
526 ICryptoTransform desdecrypt = null;
530 //暗号化されたデータを書き出すためのMemoryStream
531 msOut = new MemoryStream();
534 desdecrypt = des.CreateEncryptor();
536 //書き込むためのCryptoStreamの作成
537 using (CryptoStream cryptStream = new CryptoStream(msOut, desdecrypt, CryptoStreamMode.Write))
539 //Disposeが重複して呼ばれないようにする
540 MemoryStream msTmp = msOut;
545 cryptStream.Write(bytesIn, 0, bytesIn.Length);
546 cryptStream.FlushFinalBlock();
548 var bytesOut = msTmp.ToArray();
550 //Base64で文字列に変更して結果を返す
551 return Convert.ToBase64String(bytesOut);
556 if (msOut != null) msOut.Dispose();
557 if (desdecrypt != null) desdecrypt.Dispose();
562 public static string DecryptString(string str)
564 if (string.IsNullOrEmpty(str)) return "";
566 //DESCryptoServiceProviderオブジェクトの作成
567 using (var des = new System.Security.Cryptography.DESCryptoServiceProvider())
571 var bytesKey = Encoding.UTF8.GetBytes("_tween_encrypt_key_");
573 des.Key = ResizeBytesArray(bytesKey, des.Key.Length);
574 des.IV = ResizeBytesArray(bytesKey, des.IV.Length);
576 //Base64で文字列をバイト配列に戻す
577 var bytesIn = Convert.FromBase64String(str);
579 MemoryStream msIn = null;
580 ICryptoTransform desdecrypt = null;
581 CryptoStream cryptStreem = null;
585 //暗号化されたデータを読み込むためのMemoryStream
586 msIn = new MemoryStream(bytesIn);
588 desdecrypt = des.CreateDecryptor();
589 //読み込むためのCryptoStreamの作成
590 cryptStreem = new CryptoStream(msIn, desdecrypt, CryptoStreamMode.Read);
592 //Disposeが重複して呼ばれないようにする
596 //復号化されたデータを取得するためのStreamReader
597 using (StreamReader srOut = new StreamReader(cryptStreem, Encoding.UTF8))
599 //Disposeが重複して呼ばれないようにする
603 var result = srOut.ReadToEnd();
610 if (msIn != null) msIn.Dispose();
611 if (desdecrypt != null) desdecrypt.Dispose();
612 if (cryptStreem != null) cryptStreem.Dispose();
617 public static byte[] ResizeBytesArray(byte[] bytes,
620 var newBytes = new byte[newSize];
621 if (bytes.Length <= newSize)
623 foreach (var i in Enumerable.Range(0, bytes.Length))
625 newBytes[i] = bytes[i];
631 foreach (var i in Enumerable.Range(0, bytes.Length))
633 newBytes[pos] = unchecked((byte)(newBytes[pos] ^ bytes[i]));
635 if (pos >= newBytes.Length)
644 public static bool IsNT6()
647 return Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major == 6;
651 public enum TabUsageType
655 Mentions = 2, //Unique
656 DirectMessage = 4, //Unique
657 Favorites = 8, //Unique
659 LocalQuery = 32, //Pin(no save/no save query/distribute/no update(normal update))
660 Profile = 64, //Pin(save/no distribute/manual update)
661 PublicSearch = 128, //Pin(save/no distribute/auto update)
670 public static string fileVersion = "";
672 public static string GetUserAgentString()
674 if (string.IsNullOrEmpty(fileVersion))
676 throw new Exception("fileversion is not Initialized.");
678 return GetAssemblyName() + "/" + fileVersion;
681 public static ApiInformation TwitterApiInfo = new ApiInformation();
683 public static bool IsAnimatedGif(string filename)
688 img = Image.FromFile(filename);
689 if (img == null) return false;
690 if (img.RawFormat.Guid == ImageFormat.Gif.Guid)
692 var fd = new FrameDimension(img.FrameDimensionsList[0]);
693 var fd_count = img.GetFrameCount(fd);
711 if (img != null) img.Dispose();
715 public static DateTime DateTimeParse(string input)
719 "ddd MMM dd HH:mm:ss zzzz yyyy",
720 "ddd, d MMM yyyy HH:mm:ss zzzz",
722 foreach (var fmt in format)
724 if (DateTime.TryParseExact(input,
726 DateTimeFormatInfo.InvariantInfo,
737 TraceOut("Parse Error(DateTimeFormat) : " + input);
738 return new DateTime();
741 public static T CreateDataFromJson<T>(string content)
744 using (var stream = new MemoryStream())
746 var buf = Encoding.Unicode.GetBytes(content);
747 stream.Write(Encoding.Unicode.GetBytes(content), offset: 0, count: buf.Length);
748 stream.Seek(offset: 0, loc: SeekOrigin.Begin);
749 data = (T)((new DataContractJsonSerializer(typeof(T))).ReadObject(stream));
754 public static bool IsNetworkAvailable()
758 return NetworkInterface.GetIsNetworkAvailable();
766 static bool IsValidEmail(string strIn)
768 // Return true if strIn is in valid e-mail format.
769 return Regex.IsMatch(strIn,
770 @"^(?("")("".+?""@)|(([0-9a-zA-Z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])@))" +
771 @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$");
775 /// 指定された修飾キーが押されている状態かを取得します。
777 /// <param name="keys">状態を調べるキー</param>
778 /// <returns><paramref name="keys"/> で指定された修飾キーがすべて押されている状態であれば true。それ以外であれば false。</returns>
779 public static bool IsKeyDown(params Keys[] keys)
781 foreach (Keys key in keys)
783 if ((Control.ModifierKeys & key) != key)
792 /// アプリケーションのアセンブリ名を取得します。
795 /// VB.NETの<code>My.Application.Info.AssemblyName</code>と(ほぼ)同じ動作をします。
797 /// <returns>アプリケーションのアセンブリ名</returns>
798 public static string GetAssemblyName()
800 return Assembly.GetEntryAssembly().GetName().Name;
804 /// 文字列中に含まれる %AppName% をアプリケーション名に置換する
806 /// <param name="orig">対象となる文字列</param>
807 /// <returns>置換後の文字列</returns>
808 public static string ReplaceAppName(string orig)
810 return orig.Replace("%AppName%", Application.ProductName);
814 /// 表示用のバージョン番号の文字列を生成する
817 /// バージョン1.0.0.1のように末尾が0でない(=開発版)の場合は「1.0.1-beta1」が出力される
822 public static string GetReadableVersion(string fileVersion = null)
824 if (fileVersion == null)
826 fileVersion = MyCommon.fileVersion;
829 if (string.IsNullOrEmpty(fileVersion))
834 int[] version = fileVersion.Split('.')
835 .Select(x => int.Parse(x)).ToArray();
839 return string.Format("{0}.{1}.{2}", version[0], version[1], version[2]);
843 version[2] = version[2] + 1;
848 version[1] += version[2] / 10;
853 version[0] += version[1] / 10;
858 return string.Format("{0}.{1}.{2}-beta{3}", version[0], version[1], version[2], version[3]);
862 public const string TwitterUrl = "https://twitter.com/";
864 public static string GetStatusUrl(PostClass post)
866 if (post.RetweetedId == 0)
867 return GetStatusUrl(post.ScreenName, post.StatusId);
869 return GetStatusUrl(post.ScreenName, post.RetweetedId);
872 public static string GetStatusUrl(string screenName, long statusId)
874 return TwitterUrl + screenName + "/status/" + statusId.ToString();