PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/UploadersLib/FileUploaders/Dropbox.cs

https://github.com/llehouerou/ShareX
C# | 455 lines | 326 code | 82 blank | 47 comment | 44 complexity | 33194a5e6f2b9ee4a57a1a50d4fd374b MD5 | raw file
Possible License(s): GPL-3.0
  1. #region License Information (GPL v3)
  2. /*
  3. ShareX - A program that allows you to take screenshots and share any file type
  4. Copyright (C) 2007-2014 ShareX Developers
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. Optionally you can also view the license at <http://www.gnu.org/licenses/>.
  17. */
  18. #endregion License Information (GPL v3)
  19. using HelpersLib;
  20. using Newtonsoft.Json;
  21. using System;
  22. using System.Collections.Generic;
  23. using System.Collections.Specialized;
  24. using System.IO;
  25. using System.Text.RegularExpressions;
  26. using UploadersLib.HelperClasses;
  27. namespace UploadersLib.FileUploaders
  28. {
  29. public sealed class Dropbox : FileUploader, IOAuth2Basic
  30. {
  31. public OAuth2Info AuthInfo { get; set; }
  32. public DropboxAccountInfo AccountInfo { get; set; }
  33. public string UploadPath { get; set; }
  34. public bool AutoCreateShareableLink { get; set; }
  35. public DropboxURLType ShareURLType { get; set; }
  36. private const string APIVersion = "1";
  37. private const string Root = "dropbox"; // dropbox or sandbox
  38. private const string URLWEB = "https://www.dropbox.com/" + APIVersion;
  39. private const string URLAPI = "https://api.dropbox.com/" + APIVersion;
  40. private const string URLAPIContent = "https://api-content.dropbox.com/" + APIVersion;
  41. private const string URLAccountInfo = URLAPI + "/account/info";
  42. private const string URLFiles = URLAPIContent + "/files/" + Root;
  43. private const string URLMetaData = URLAPI + "/metadata/" + Root;
  44. private const string URLShares = URLAPI + "/shares/" + Root;
  45. private const string URLCopy = URLAPI + "/fileops/copy";
  46. private const string URLCreateFolder = URLAPI + "/fileops/create_folder";
  47. private const string URLDelete = URLAPI + "/fileops/delete";
  48. private const string URLMove = URLAPI + "/fileops/move";
  49. private const string URLPublicDirect = "https://dl.dropboxusercontent.com/u";
  50. private const string URLShareDirect = "https://dl.dropboxusercontent.com/s";
  51. public Dropbox(OAuth2Info oauth)
  52. {
  53. AuthInfo = oauth;
  54. }
  55. public Dropbox(OAuth2Info oauth, DropboxAccountInfo accountInfo)
  56. : this(oauth)
  57. {
  58. AccountInfo = accountInfo;
  59. }
  60. // https://www.dropbox.com/developers/core/docs#oa2-authorize
  61. public string GetAuthorizationURL()
  62. {
  63. Dictionary<string, string> args = new Dictionary<string, string>();
  64. args.Add("response_type", "code");
  65. args.Add("client_id", AuthInfo.Client_ID);
  66. return CreateQuery(URLWEB + "/oauth2/authorize", args);
  67. }
  68. // https://www.dropbox.com/developers/core/docs#oa2-token
  69. public bool GetAccessToken(string code)
  70. {
  71. Dictionary<string, string> args = new Dictionary<string, string>();
  72. args.Add("client_id", AuthInfo.Client_ID);
  73. args.Add("client_secret", AuthInfo.Client_Secret);
  74. args.Add("grant_type", "authorization_code");
  75. args.Add("code", code);
  76. string response = SendRequest(HttpMethod.POST, URLAPI + "/oauth2/token", args);
  77. if (!string.IsNullOrEmpty(response))
  78. {
  79. OAuth2Token token = JsonConvert.DeserializeObject<OAuth2Token>(response);
  80. if (token != null && !string.IsNullOrEmpty(token.access_token))
  81. {
  82. AuthInfo.Token = token;
  83. return true;
  84. }
  85. }
  86. return false;
  87. }
  88. private NameValueCollection GetAuthHeaders()
  89. {
  90. NameValueCollection headers = new NameValueCollection();
  91. headers.Add("Authorization", "Bearer " + AuthInfo.Token.access_token);
  92. return headers;
  93. }
  94. /* OAuth 1.0
  95. // https://www.dropbox.com/developers/core/docs#request-token
  96. // https://www.dropbox.com/developers/core/docs#authorize
  97. public string GetAuthorizationURL()
  98. {
  99. return GetAuthorizationURL(URLAPI + "/oauth/request_token", URLWEB + "/oauth/authorize", AuthInfo);
  100. }
  101. // https://www.dropbox.com/developers/core/docs#access-token
  102. public bool GetAccessToken(string verificationCode = null)
  103. {
  104. AuthInfo.AuthVerifier = verificationCode;
  105. return GetAccessToken(URLAPI + "/oauth/access_token", AuthInfo);
  106. }
  107. */
  108. #region Dropbox accounts
  109. // https://www.dropbox.com/developers/core/docs#account-info
  110. public DropboxAccountInfo GetAccountInfo()
  111. {
  112. DropboxAccountInfo account = null;
  113. if (OAuth2Info.CheckOAuth(AuthInfo))
  114. {
  115. string response = SendRequest(HttpMethod.GET, URLAccountInfo, headers: GetAuthHeaders());
  116. if (!string.IsNullOrEmpty(response))
  117. {
  118. account = JsonConvert.DeserializeObject<DropboxAccountInfo>(response);
  119. if (account != null)
  120. {
  121. AccountInfo = account;
  122. }
  123. }
  124. }
  125. return account;
  126. }
  127. #endregion Dropbox accounts
  128. #region Files and metadata
  129. // https://www.dropbox.com/developers/core/docs#files-GET
  130. public bool DownloadFile(string path, Stream downloadStream)
  131. {
  132. if (!string.IsNullOrEmpty(path) && OAuth2Info.CheckOAuth(AuthInfo))
  133. {
  134. string url = URLHelpers.CombineURL(URLFiles, URLHelpers.URLPathEncode(path));
  135. return SendRequest(HttpMethod.GET, downloadStream, url, headers: GetAuthHeaders());
  136. }
  137. return false;
  138. }
  139. // https://www.dropbox.com/developers/core/docs#files_put
  140. public UploadResult UploadFile(Stream stream, string path, string fileName, bool createShareableURL = false, DropboxURLType urlType = DropboxURLType.Default)
  141. {
  142. if (!OAuth2Info.CheckOAuth(AuthInfo))
  143. {
  144. Errors.Add("Dropbox login is required.");
  145. return null;
  146. }
  147. string url = URLHelpers.CombineURL(URLFiles, URLHelpers.URLPathEncode(path));
  148. // There's a 150MB limit to all uploads through the API.
  149. UploadResult result = UploadData(stream, url, fileName, headers: GetAuthHeaders());
  150. if (result.IsSuccess)
  151. {
  152. DropboxContentInfo content = JsonConvert.DeserializeObject<DropboxContentInfo>(result.Response);
  153. if (content != null)
  154. {
  155. if (createShareableURL)
  156. {
  157. result.URL = CreateShareableLink(content.Path, urlType);
  158. }
  159. else
  160. {
  161. result.URL = GetPublicURL(content.Path);
  162. }
  163. }
  164. }
  165. return result;
  166. }
  167. // https://www.dropbox.com/developers/core/docs#metadata
  168. public DropboxContentInfo GetMetadata(string path, bool list)
  169. {
  170. DropboxContentInfo contentInfo = null;
  171. if (OAuth2Info.CheckOAuth(AuthInfo))
  172. {
  173. string url = URLHelpers.CombineURL(URLMetaData, URLHelpers.URLPathEncode(path));
  174. Dictionary<string, string> args = new Dictionary<string, string>();
  175. args.Add("list", list ? "true" : "false");
  176. string response = SendRequest(HttpMethod.GET, url, args, headers: GetAuthHeaders());
  177. if (!string.IsNullOrEmpty(response))
  178. {
  179. contentInfo = JsonConvert.DeserializeObject<DropboxContentInfo>(response);
  180. }
  181. }
  182. return contentInfo;
  183. }
  184. public bool IsExists(string path)
  185. {
  186. DropboxContentInfo contentInfo = GetMetadata(path, false);
  187. return contentInfo != null && !contentInfo.Is_deleted;
  188. }
  189. // https://www.dropbox.com/developers/core/docs#shares
  190. public string CreateShareableLink(string path, DropboxURLType urlType)
  191. {
  192. if (!string.IsNullOrEmpty(path) && OAuth2Info.CheckOAuth(AuthInfo))
  193. {
  194. string url = URLHelpers.CombineURL(URLShares, URLHelpers.URLPathEncode(path));
  195. Dictionary<string, string> args = new Dictionary<string, string>();
  196. args.Add("short_url", urlType == DropboxURLType.Shortened ? "true" : "false");
  197. string response = SendRequest(HttpMethod.POST, url, args, headers: GetAuthHeaders());
  198. if (!string.IsNullOrEmpty(response))
  199. {
  200. DropboxShares shares = JsonConvert.DeserializeObject<DropboxShares>(response);
  201. if (urlType == DropboxURLType.Direct)
  202. {
  203. Match match = Regex.Match(shares.URL, @"https?://(?:www\.)?dropbox.com/s/(?<path>\w+/.+)");
  204. if (match.Success)
  205. {
  206. string urlPath = match.Groups["path"].Value;
  207. if (!string.IsNullOrEmpty(urlPath))
  208. {
  209. return URLHelpers.CombineURL(URLShareDirect, urlPath);
  210. }
  211. }
  212. }
  213. else
  214. {
  215. return shares.URL;
  216. }
  217. }
  218. }
  219. return null;
  220. }
  221. #endregion Files and metadata
  222. #region File operations
  223. // https://www.dropbox.com/developers/core/docs#fileops-copy
  224. public DropboxContentInfo Copy(string from_path, string to_path)
  225. {
  226. DropboxContentInfo contentInfo = null;
  227. if (!string.IsNullOrEmpty(from_path) && !string.IsNullOrEmpty(to_path) && OAuth2Info.CheckOAuth(AuthInfo))
  228. {
  229. Dictionary<string, string> args = new Dictionary<string, string>();
  230. args.Add("root", Root);
  231. args.Add("from_path", from_path);
  232. args.Add("to_path", to_path);
  233. string response = SendRequest(HttpMethod.POST, URLCopy, args, headers: GetAuthHeaders());
  234. if (!string.IsNullOrEmpty(response))
  235. {
  236. contentInfo = JsonConvert.DeserializeObject<DropboxContentInfo>(response);
  237. }
  238. }
  239. return contentInfo;
  240. }
  241. // https://www.dropbox.com/developers/core/docs#fileops-create-folder
  242. public DropboxContentInfo CreateFolder(string path)
  243. {
  244. DropboxContentInfo contentInfo = null;
  245. if (!string.IsNullOrEmpty(path) && OAuth2Info.CheckOAuth(AuthInfo))
  246. {
  247. Dictionary<string, string> args = new Dictionary<string, string>();
  248. args.Add("root", Root);
  249. args.Add("path", path);
  250. string response = SendRequest(HttpMethod.POST, URLCreateFolder, args, headers: GetAuthHeaders());
  251. if (!string.IsNullOrEmpty(response))
  252. {
  253. contentInfo = JsonConvert.DeserializeObject<DropboxContentInfo>(response);
  254. }
  255. }
  256. return contentInfo;
  257. }
  258. // https://www.dropbox.com/developers/core/docs#fileops-delete
  259. public DropboxContentInfo Delete(string path)
  260. {
  261. DropboxContentInfo contentInfo = null;
  262. if (!string.IsNullOrEmpty(path) && OAuth2Info.CheckOAuth(AuthInfo))
  263. {
  264. Dictionary<string, string> args = new Dictionary<string, string>();
  265. args.Add("root", Root);
  266. args.Add("path", path);
  267. string response = SendRequest(HttpMethod.POST, URLDelete, args, headers: GetAuthHeaders());
  268. if (!string.IsNullOrEmpty(response))
  269. {
  270. contentInfo = JsonConvert.DeserializeObject<DropboxContentInfo>(response);
  271. }
  272. }
  273. return contentInfo;
  274. }
  275. // https://www.dropbox.com/developers/core/docs#fileops-move
  276. public DropboxContentInfo Move(string from_path, string to_path)
  277. {
  278. DropboxContentInfo contentInfo = null;
  279. if (!string.IsNullOrEmpty(from_path) && !string.IsNullOrEmpty(to_path) && OAuth2Info.CheckOAuth(AuthInfo))
  280. {
  281. Dictionary<string, string> args = new Dictionary<string, string>();
  282. args.Add("root", Root);
  283. args.Add("from_path", from_path);
  284. args.Add("to_path", to_path);
  285. string response = SendRequest(HttpMethod.POST, URLMove, args, headers: GetAuthHeaders());
  286. if (!string.IsNullOrEmpty(response))
  287. {
  288. contentInfo = JsonConvert.DeserializeObject<DropboxContentInfo>(response);
  289. }
  290. }
  291. return contentInfo;
  292. }
  293. #endregion File operations
  294. public override UploadResult Upload(Stream stream, string fileName)
  295. {
  296. return UploadFile(stream, UploadPath, fileName, AutoCreateShareableLink, ShareURLType);
  297. }
  298. public string GetPublicURL(string path)
  299. {
  300. return GetPublicURL(AccountInfo.Uid, path);
  301. }
  302. public static string GetPublicURL(long userID, string path)
  303. {
  304. if (!string.IsNullOrEmpty(path))
  305. {
  306. path = path.Trim('/');
  307. if (path.StartsWith("Public/", StringComparison.InvariantCultureIgnoreCase))
  308. {
  309. path = URLHelpers.URLPathEncode((path.Substring(7)));
  310. return URLHelpers.CombineURL(URLPublicDirect, userID.ToString(), path);
  311. }
  312. }
  313. return "Upload path is private. Use \"Public\" folder to get public URL.";
  314. }
  315. public static string TidyUploadPath(string uploadPath)
  316. {
  317. if (!string.IsNullOrEmpty(uploadPath))
  318. {
  319. return uploadPath.Trim().Replace('\\', '/').Trim('/') + "/";
  320. }
  321. return string.Empty;
  322. }
  323. }
  324. public enum DropboxURLType
  325. {
  326. Default,
  327. Shortened,
  328. Direct
  329. }
  330. public class DropboxAccountInfo
  331. {
  332. public string Referral_link { get; set; } // The user's referral link.
  333. public string Display_name { get; set; } // The user's display name.
  334. public long Uid { get; set; } // The user's unique Dropbox ID.
  335. public string Country { get; set; } // The user's two-letter country code, if available.
  336. public DropboxQuotaInfo Quota_info { get; set; }
  337. public string Email { get; set; }
  338. }
  339. public class DropboxQuotaInfo
  340. {
  341. public long Normal { get; set; } // The user's used quota outside of shared folders (bytes).
  342. public long Shared { get; set; } // The user's used quota in shared folders (bytes).
  343. public long Quota { get; set; } // The user's total quota allocation (bytes).
  344. }
  345. public class DropboxContentInfo
  346. {
  347. public string Size { get; set; } // A human-readable description of the file size (translated by locale).
  348. public long Bytes { get; set; } // The file size in bytes.
  349. public string Path { get; set; } // Returns the canonical path to the file or directory.
  350. public bool Is_dir { get; set; } // Whether the given entry is a folder or not.
  351. public bool Is_deleted { get; set; } // Whether the given entry is deleted (only included if deleted files are being returned).
  352. public string Rev { get; set; } // A unique identifier for the current revision of a file. This field is the same rev as elsewhere in the API and can be used to detect changes and avoid conflicts.
  353. public string Hash { get; set; } // A folder's hash is useful for indicating changes to the folder's contents in later calls to /metadata. This is roughly the folder equivalent to a file's rev.
  354. public bool Thumb_exists { get; set; } // True if the file is an image can be converted to a thumbnail via the /thumbnails call.
  355. public string Icon { get; set; } // The name of the icon used to illustrate the file type in Dropbox's icon library.
  356. public string Modified { get; set; } // The last time the file was modified on Dropbox, in the standard date format (not included for the root folder).
  357. public string Client_mtime { get; set; } // For files, this is the modification time set by the desktop client when the file was added to Dropbox, in the standard date format. Since this time is not verified (the Dropbox server stores whatever the desktop client sends up), this should only be used for display purposes (such as sorting) and not, for example, to determine if a file has changed or not.
  358. public string Root { get; set; } // The root or top-level folder depending on your access level. All paths returned are relative to this root level. Permitted values are either dropbox or app_folder.
  359. public long Revision { get; set; } // A deprecated field that semi-uniquely identifies a file. Use rev instead.
  360. public string Mime_type { get; set; }
  361. public DropboxContentInfo[] Contents { get; set; }
  362. }
  363. public class DropboxShares
  364. {
  365. public string URL { get; set; }
  366. public string Expires { get; set; }
  367. }
  368. }