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

/module/ASC.Api/ASC.Api/Impl/ApiArgumentBuilder.cs

https://github.com/dc0d/ONLYOFFICE-Server
C# | 248 lines | 192 code | 15 blank | 41 comment | 42 complexity | 7dab33de8dfc45b3af53b5edc924c1cb MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /*
  2. (c) Copyright Ascensio System SIA 2010-2014
  3. This program is a free software product.
  4. You can redistribute it and/or modify it under the terms
  5. of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
  6. Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
  7. to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
  8. any third-party rights.
  9. This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty
  10. of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
  11. the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
  12. You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
  13. The interactive user interfaces in modified source and object code versions of the Program must
  14. display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
  15. Pursuant to Section 7(b) of the License you must retain the original Product logo when
  16. distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
  17. trademark law for use of our trademarks.
  18. All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
  19. content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
  20. International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
  21. */
  22. using System;
  23. using System.Collections.Generic;
  24. using System.Collections.Specialized;
  25. using System.IO;
  26. using System.Linq;
  27. using System.Net.Mime;
  28. using System.Runtime.Serialization;
  29. using System.Web;
  30. using System.Web.Routing;
  31. using System.Web.Script.Serialization;
  32. using System.Xml.Linq;
  33. using ASC.Api.Collections;
  34. using ASC.Api.Exceptions;
  35. using ASC.Api.Interfaces;
  36. using ASC.Api.Utils;
  37. using Newtonsoft.Json;
  38. using Newtonsoft.Json.Linq;
  39. namespace ASC.Api.Impl
  40. {
  41. public class ApiArgumentBuilder : IApiArgumentBuilder
  42. {
  43. public IEnumerable<object> BuildCallingArguments(RequestContext context, IApiMethodCall methodToCall)
  44. {
  45. var callArg = new List<object>();
  46. var requestParams = GetRequestParams(context);
  47. var methodParams = methodToCall.GetParams().Where(x => !x.IsRetval).OrderBy(x => x.Position);
  48. foreach (var parameterInfo in methodParams)
  49. {
  50. if (requestParams[parameterInfo.Name] != null)
  51. {
  52. //convert
  53. var values = requestParams.GetValues(parameterInfo.Name);
  54. if (values != null && values.Any())
  55. {
  56. try
  57. {
  58. callArg.Add(ConvertUtils.GetConverted(values.First(), parameterInfo));//NOTE; Get first value!
  59. }
  60. catch (ApiArgumentMismatchException)
  61. {
  62. //Failed to convert. Try bind
  63. callArg.Add(Utils.Binder.Bind(parameterInfo.ParameterType, requestParams, parameterInfo.Name));
  64. }
  65. }
  66. }
  67. else
  68. {
  69. var requestType = string.IsNullOrEmpty(context.HttpContext.Request.ContentType) ? new ContentType("text/plain") : new ContentType(context.HttpContext.Request.ContentType);
  70. //try get request param first. It may be form\url-encoded
  71. if (!string.IsNullOrEmpty(context.HttpContext.Request[parameterInfo.Name]))
  72. {
  73. //Drop to
  74. callArg.Add(ConvertUtils.GetConverted(context.HttpContext.Request[parameterInfo.Name], parameterInfo));
  75. }
  76. else if (parameterInfo.ParameterType == typeof(ContentType) && !string.IsNullOrEmpty(context.HttpContext.Request.ContentType))
  77. {
  78. callArg.Add(new ContentType(context.HttpContext.Request.ContentType));
  79. }
  80. else if (parameterInfo.ParameterType == typeof(ContentDisposition) && !string.IsNullOrEmpty(context.HttpContext.Request.Headers["Content-Disposition"]))
  81. {
  82. var disposition = new ContentDisposition(context.HttpContext.Request.Headers["Content-Disposition"]);
  83. disposition.FileName = HttpUtility.UrlDecode(disposition.FileName);//Decode uri name
  84. callArg.Add(disposition);
  85. }
  86. else if (parameterInfo.ParameterType.IsSubclassOf(typeof(HttpPostedFile)) && context.HttpContext.Request.Files[parameterInfo.Name] != null)
  87. {
  88. callArg.Add(context.HttpContext.Request.Files[parameterInfo.Name]);
  89. }
  90. else if (Utils.Binder.IsCollection(parameterInfo.ParameterType) && parameterInfo.ParameterType.IsGenericType && parameterInfo.ParameterType.GetGenericArguments().First() == typeof(HttpPostedFileBase))
  91. {
  92. //File catcher
  93. var files = new List<HttpPostedFileBase>(context.HttpContext.Request.Files.Count);
  94. files.AddRange(from string key in context.HttpContext.Request.Files select context.HttpContext.Request.Files[key]);
  95. callArg.Add(files);
  96. }
  97. else
  98. {
  99. if (parameterInfo.ParameterType.IsSubclassOf(typeof(Stream)) || parameterInfo.ParameterType == typeof(Stream))
  100. {
  101. //First try get files
  102. var file = context.HttpContext.Request.Files[parameterInfo.Name];
  103. callArg.Add(file != null ? file.InputStream : context.HttpContext.Request.InputStream);
  104. }
  105. else
  106. {
  107. //Try bind
  108. //Note: binding moved here
  109. if (IsTypeBindable(parameterInfo.ParameterType))
  110. {
  111. //Custom type
  112. var binded = Utils.Binder.Bind(parameterInfo.ParameterType,
  113. requestParams,
  114. Utils.Binder.IsCollection(parameterInfo.ParameterType) ? parameterInfo.Name : string.Empty);
  115. if (binded != null)
  116. {
  117. callArg.Add(binded);
  118. continue;//Go to next loop
  119. }
  120. }
  121. //Create null
  122. var obj = parameterInfo.ParameterType.IsValueType ? Activator.CreateInstance(parameterInfo.ParameterType) : null;
  123. callArg.Add(obj);
  124. }
  125. }
  126. }
  127. }
  128. return callArg;
  129. }
  130. private static bool IsTypeBindable(Type parameterInfo)
  131. {
  132. if (Binder.IsCollection(parameterInfo))
  133. {
  134. return true;
  135. }
  136. return parameterInfo.Namespace != null && !parameterInfo.Namespace.StartsWith("System", StringComparison.Ordinal);
  137. }
  138. private static NameValueCollection GetRequestParams(RequestContext context)
  139. {
  140. var requestType = string.IsNullOrEmpty(context.HttpContext.Request.ContentType) ? new ContentType("text/plain") : new ContentType(context.HttpContext.Request.ContentType);
  141. var collection = context.RouteData.Values.ToNameValueCollection();
  142. switch (requestType.MediaType)
  143. {
  144. case Constants.XmlContentType:
  145. {
  146. using (var reader = new StreamReader(context.HttpContext.Request.InputStream))
  147. {
  148. FillCollectionFromXElement(XDocument.Load(reader).Root.Elements(), string.Empty, collection);
  149. }
  150. }
  151. break;
  152. case Constants.JsonContentType:
  153. {
  154. using (var reader = new StreamReader(context.HttpContext.Request.InputStream))
  155. {
  156. var xdoc = JsonConvert.DeserializeXNode(reader.ReadToEnd(),"request",false);
  157. FillCollectionFromXElement(xdoc.Root.Elements(), string.Empty,collection);
  158. }
  159. }
  160. break;
  161. default:
  162. collection.Add(context.HttpContext.Request.QueryString);
  163. collection.Add(context.HttpContext.Request.Form);
  164. break;
  165. }
  166. return collection;
  167. }
  168. private static void FillCollectionFromXElement(IEnumerable<XElement> elements, string prefix, NameValueCollection collection)
  169. {
  170. foreach (var grouping in elements.GroupBy(x => x.Name))
  171. {
  172. if (grouping.Count() < 2)
  173. {
  174. //Single element
  175. var element = grouping.SingleOrDefault();
  176. if (element != null)
  177. {
  178. if (!element.HasElements)
  179. {
  180. //Last one in tree
  181. AddElement(prefix, collection, element);
  182. }
  183. else
  184. {
  185. FillCollectionFromXElement(element.Elements(), prefix + "." + element.Name.LocalName, collection);
  186. }
  187. }
  188. }
  189. else
  190. {
  191. //Grouping has more than one
  192. if (grouping.All(x => !x.HasElements))
  193. {
  194. //Simple collection
  195. foreach (XElement element in grouping)
  196. {
  197. AddElement(prefix, collection, element);
  198. }
  199. }
  200. else
  201. {
  202. var groupList = grouping.ToList();
  203. for (int i = 0; i < groupList.Count; i++)
  204. {
  205. FillCollectionFromXElement(groupList[i].Elements(), prefix + "." + grouping.Key + "[" + i + "]", collection);
  206. }
  207. }
  208. }
  209. }
  210. }
  211. private static void AddElement(string prefix, NameValueCollection collection, XElement element)
  212. {
  213. if (string.IsNullOrEmpty(prefix))
  214. collection.Add(element.Name.LocalName, element.Value);
  215. else
  216. {
  217. var prefixes = prefix.TrimStart('.').Split('.');
  218. string additional = string.Empty;
  219. if (prefixes.Length > 1)
  220. {
  221. additional = string.Join("", prefix.Skip(1).Select(x => "[" + x + "]").ToArray());
  222. }
  223. collection.Add(prefixes[0] + additional + "[" + element.Name.LocalName + "]", element.Value);
  224. }
  225. }
  226. }
  227. }