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