/BlogEngine/DotNetSlave.BusinessLogic/API/MetaWeblog/XMLRPCRequest.cs
C# | 508 lines | 304 code | 65 blank | 139 comment | 56 complexity | e2dabc8553c7ffcded96993ceec14f6e MD5 | raw file
1namespace BlogEngine.Core.API.MetaWeblog 2{ 3 using System; 4 using System.Collections.Generic; 5 using System.Diagnostics; 6 using System.Globalization; 7 using System.Linq; 8 using System.Text; 9 using System.Web; 10 using System.Xml; 11 12 /// <summary> 13 /// Obejct is the incoming XML-RPC Request. Handles parsing the XML-RPC and 14 /// fills its properties with the values sent in the request. 15 /// </summary> 16 internal class XMLRPCRequest 17 { 18 #region Constants and Fields 19 20 /// <summary> 21 /// The input params. 22 /// </summary> 23 private List<XmlNode> inputParams; 24 25 #endregion 26 27 #region Constructors and Destructors 28 29 /// <summary> 30 /// Initializes a new instance of the <see cref="XMLRPCRequest"/> class. 31 /// Loads XMLRPCRequest object from HttpContext 32 /// </summary> 33 /// <param name="input"> 34 /// incoming HttpContext 35 /// </param> 36 public XMLRPCRequest(HttpContext input) 37 { 38 var inputXml = ParseRequest(input); 39 40 // LogMetaWeblogCall(inputXml); 41 this.LoadXmlRequest(inputXml); // Loads Method Call and Associated Variables 42 } 43 44 #endregion 45 46 #region Properties 47 48 /// <summary> 49 /// Gets AppKey is a key generated by the calling application. It is sent with blogger API calls. 50 /// </summary> 51 /// <remarks> 52 /// BlogEngine.NET doesn't require specific AppKeys for API calls. It is no longer standard practive. 53 /// </remarks> 54 public string AppKey { get; private set; } 55 56 /// <summary> 57 /// Gets ID of the Blog to call the function on. Since BlogEngine supports only a single blog instance, 58 /// this incoming parameter is not used. 59 /// </summary> 60 public string BlogID { get; private set; } 61 62 /// <summary> 63 /// Gets MediaObject is a struct sent by the metaWeblog.newMediaObject function. 64 /// It contains information about the media and the object in a bit array. 65 /// </summary> 66 public MWAMediaObject MediaObject { get; private set; } 67 68 /// <summary> 69 /// Gets Name of Called Metaweblog Function 70 /// </summary> 71 public string MethodName { get; private set; } 72 73 /// <summary> 74 /// Gets Number of post request by the metaWeblog.getRecentPosts function 75 /// </summary> 76 public int NumberOfPosts { get; private set; } 77 78 /// <summary> 79 /// Gets Metaweblog Page Struct 80 /// </summary> 81 public MWAPage Page { get; private set; } 82 83 /// <summary> 84 /// Gets PageID Guid in string format 85 /// </summary> 86 public string PageID { get; private set; } 87 88 /// <summary> 89 /// Gets Password for user validation 90 /// </summary> 91 public string Password { get; private set; } 92 93 /// <summary> 94 /// Gets Metaweblog Post struct containing information post including title, content, and categories. 95 /// </summary> 96 public MWAPost Post { get; private set; } 97 98 /// <summary> 99 /// Gets The PostID Guid in string format 100 /// </summary> 101 public string PostID { get; private set; } 102 103 /// <summary> 104 /// Gets a value indicating whether or not a post will be marked as published by BlogEngine. 105 /// </summary> 106 public bool Publish { get; private set; } 107 108 /// <summary> 109 /// Gets Login for user validation 110 /// </summary> 111 public string UserName { get; private set; } 112 113 #endregion 114 115 #region Methods 116 117 /// <summary> 118 /// Creates a Metaweblog Media object from the XML struct 119 /// </summary> 120 /// <param name="node"> 121 /// XML contains a Metaweblog MediaObject Struct 122 /// </param> 123 /// <returns> 124 /// Metaweblog MediaObject Struct Obejct 125 /// </returns> 126 private static MWAMediaObject GetMediaObject(XmlNode node) 127 { 128 var name = node.SelectSingleNode("value/struct/member[name='name']"); 129 var type = node.SelectSingleNode("value/struct/member[name='type']"); 130 var bits = node.SelectSingleNode("value/struct/member[name='bits']"); 131 var temp = new MWAMediaObject 132 { 133 name = name == null ? string.Empty : name.LastChild.InnerText, 134 type = type == null ? "notsent" : type.LastChild.InnerText, 135 bits = Convert.FromBase64String(bits == null ? string.Empty : bits.LastChild.InnerText) 136 }; 137 138 return temp; 139 } 140 141 /// <summary> 142 /// Creates a Metaweblog Page object from the XML struct 143 /// </summary> 144 /// <param name="node"> 145 /// XML contains a Metaweblog Page Struct 146 /// </param> 147 /// <returns> 148 /// Metaweblog Page Struct Obejct 149 /// </returns> 150 private static MWAPage GetPage(XmlNode node) 151 { 152 var temp = new MWAPage(); 153 154 // Require Title and Description 155 var title = node.SelectSingleNode("value/struct/member[name='title']"); 156 if (title == null) 157 { 158 throw new MetaWeblogException("06", "Page Struct Element, Title, not Sent."); 159 } 160 161 temp.title = title.LastChild.InnerText; 162 163 var description = node.SelectSingleNode("value/struct/member[name='description']"); 164 if (description == null) 165 { 166 throw new MetaWeblogException("06", "Page Struct Element, Description, not Sent."); 167 } 168 169 temp.description = description.LastChild.InnerText; 170 171 var link = node.SelectSingleNode("value/struct/member[name='link']"); 172 if (link != null) 173 { 174 temp.link = node.SelectSingleNode("value/struct/member[name='link']") == null ? null : link.LastChild.InnerText; 175 } 176 177 var dateCreated = node.SelectSingleNode("value/struct/member[name='dateCreated']"); 178 if (dateCreated != null) 179 { 180 try 181 { 182 var tempDate = dateCreated.LastChild.InnerText; 183 temp.pageDate = DateTime.ParseExact( 184 tempDate, 185 "yyyyMMdd'T'HH':'mm':'ss", 186 CultureInfo.InvariantCulture, 187 DateTimeStyles.AssumeUniversal); 188 } 189 catch (Exception ex) 190 { 191 // Ignore PubDate Error 192 Debug.WriteLine(ex.Message); 193 } 194 } 195 196 // Keywords 197 var keywords = node.SelectSingleNode("value/struct/member[name='mt_keywords']"); 198 temp.mt_keywords = keywords == null ? string.Empty : keywords.LastChild.InnerText; 199 200 var pageParentId = node.SelectSingleNode("value/struct/member[name='wp_page_parent_id']"); 201 temp.pageParentID = pageParentId == null ? null : pageParentId.LastChild.InnerText; 202 203 return temp; 204 } 205 206 /// <summary> 207 /// Creates a Metaweblog Post object from the XML struct 208 /// </summary> 209 /// <param name="node"> 210 /// XML contains a Metaweblog Post Struct 211 /// </param> 212 /// <returns> 213 /// Metaweblog Post Struct Obejct 214 /// </returns> 215 private static MWAPost GetPost(XmlNode node) 216 { 217 var temp = new MWAPost(); 218 219 // Require Title and Description 220 var title = node.SelectSingleNode("value/struct/member[name='title']"); 221 if (title == null) 222 { 223 throw new MetaWeblogException("05", "Page Struct Element, Title, not Sent."); 224 } 225 226 temp.title = title.LastChild.InnerText; 227 228 var description = node.SelectSingleNode("value/struct/member[name='description']"); 229 if (description == null) 230 { 231 throw new MetaWeblogException("05", "Page Struct Element, Description, not Sent."); 232 } 233 234 temp.description = description.LastChild.InnerText; 235 236 var link = node.SelectSingleNode("value/struct/member[name='link']"); 237 temp.link = link == null ? string.Empty : link.LastChild.InnerText; 238 239 var allowComments = node.SelectSingleNode("value/struct/member[name='mt_allow_comments']"); 240 temp.commentPolicy = allowComments == null ? string.Empty : allowComments.LastChild.InnerText; 241 242 var excerpt = node.SelectSingleNode("value/struct/member[name='mt_excerpt']"); 243 temp.excerpt = excerpt == null ? string.Empty : excerpt.LastChild.InnerText; 244 245 var slug = node.SelectSingleNode("value/struct/member[name='wp_slug']"); 246 temp.slug = slug == null ? string.Empty : slug.LastChild.InnerText; 247 248 var authorId = node.SelectSingleNode("value/struct/member[name='wp_author_id']"); 249 temp.author = authorId == null ? string.Empty : authorId.LastChild.InnerText; 250 251 var cats = new List<string>(); 252 var categories = node.SelectSingleNode("value/struct/member[name='categories']"); 253 if (categories != null) 254 { 255 var categoryArray = categories.LastChild; 256 var categoryArrayNodes = categoryArray.SelectNodes("array/data/value/string"); 257 if (categoryArrayNodes != null) 258 { 259 cats.AddRange(categoryArrayNodes.Cast<XmlNode>().Select( 260 catnode => catnode.InnerText)); 261 } 262 } 263 264 temp.categories = cats; 265 266 // postDate has a few different names to worry about 267 var dateCreated = node.SelectSingleNode("value/struct/member[name='dateCreated']"); 268 var pubDate = node.SelectSingleNode("value/struct/member[name='pubDate']"); 269 if (dateCreated != null) 270 { 271 try 272 { 273 var tempDate = dateCreated.LastChild.InnerText; 274 temp.postDate = DateTime.ParseExact( 275 tempDate, 276 "yyyyMMdd'T'HH':'mm':'ss", 277 CultureInfo.InvariantCulture, 278 DateTimeStyles.AssumeUniversal); 279 } 280 catch (Exception ex) 281 { 282 // Ignore PubDate Error 283 Debug.WriteLine(ex.Message); 284 } 285 } 286 else if (pubDate != null) 287 { 288 try 289 { 290 var tempPubDate = pubDate.LastChild.InnerText; 291 temp.postDate = DateTime.ParseExact( 292 tempPubDate, 293 "yyyyMMdd'T'HH':'mm':'ss", 294 CultureInfo.InvariantCulture, 295 DateTimeStyles.AssumeUniversal); 296 } 297 catch (Exception ex) 298 { 299 // Ignore PubDate Error 300 Debug.WriteLine(ex.Message); 301 } 302 } 303 304 // WLW tags implementation using mt_keywords 305 var tags = new List<string>(); 306 var keywords = node.SelectSingleNode("value/struct/member[name='mt_keywords']"); 307 if (keywords != null) 308 { 309 var tagsList = keywords.LastChild.InnerText; 310 foreach (var item in 311 tagsList.Split(',').Where(item => string.IsNullOrEmpty(tags.Find(t => t.Equals(item.Trim(), StringComparison.OrdinalIgnoreCase))))) 312 { 313 tags.Add(item.Trim()); 314 } 315 } 316 317 temp.tags = tags; 318 319 return temp; 320 } 321 322 /// <summary> 323 /// Loads object properties with contents of passed xml 324 /// </summary> 325 /// <param name="xml"> 326 /// xml doc with methodname and parameters 327 /// </param> 328 private void LoadXmlRequest(string xml) 329 { 330 var request = new XmlDocument(); 331 try 332 { 333 if (!(xml.StartsWith("<?xml") || xml.StartsWith("<method"))) 334 { 335 xml = xml.Substring(xml.IndexOf("<?xml")); 336 } 337 338 request.LoadXml(xml); 339 } 340 catch (Exception ex) 341 { 342 throw new MetaWeblogException("01", string.Format("Invalid XMLRPC Request. ({0})", ex.Message)); 343 } 344 345 // Method name is always first 346 if (request.DocumentElement != null) 347 { 348 this.MethodName = request.DocumentElement.ChildNodes[0].InnerText; 349 } 350 351 // Parameters are next (and last) 352 var xmlParams = request.SelectNodes("/methodCall/params/param"); 353 if (xmlParams != null) 354 { 355 this.inputParams = xmlParams.Cast<XmlNode>().ToList(); 356 } 357 358 // Determine what params are what by method name 359 switch (this.MethodName) 360 { 361 case "metaWeblog.newPost": 362 this.BlogID = this.inputParams[0].InnerText; 363 this.UserName = this.inputParams[1].InnerText; 364 this.Password = this.inputParams[2].InnerText; 365 this.Post = GetPost(this.inputParams[3]); 366 this.Publish = this.inputParams[4].InnerText != "0" && this.inputParams[4].InnerText != "false"; 367 368 break; 369 case "metaWeblog.editPost": 370 this.PostID = this.inputParams[0].InnerText; 371 this.UserName = this.inputParams[1].InnerText; 372 this.Password = this.inputParams[2].InnerText; 373 this.Post = GetPost(this.inputParams[3]); 374 this.Publish = this.inputParams[4].InnerText != "0" && this.inputParams[4].InnerText != "false"; 375 376 break; 377 case "metaWeblog.getPost": 378 this.PostID = this.inputParams[0].InnerText; 379 this.UserName = this.inputParams[1].InnerText; 380 this.Password = this.inputParams[2].InnerText; 381 break; 382 case "metaWeblog.newMediaObject": 383 this.BlogID = this.inputParams[0].InnerText; 384 this.UserName = this.inputParams[1].InnerText; 385 this.Password = this.inputParams[2].InnerText; 386 this.MediaObject = GetMediaObject(this.inputParams[3]); 387 break; 388 case "metaWeblog.getCategories": 389 case "wp.getAuthors": 390 case "wp.getPageList": 391 case "wp.getPages": 392 case "wp.getTags": 393 this.BlogID = this.inputParams[0].InnerText; 394 this.UserName = this.inputParams[1].InnerText; 395 this.Password = this.inputParams[2].InnerText; 396 break; 397 case "metaWeblog.getRecentPosts": 398 this.BlogID = this.inputParams[0].InnerText; 399 this.UserName = this.inputParams[1].InnerText; 400 this.Password = this.inputParams[2].InnerText; 401 this.NumberOfPosts = Int32.Parse(this.inputParams[3].InnerText, CultureInfo.InvariantCulture); 402 break; 403 case "blogger.getUsersBlogs": 404 case "metaWeblog.getUsersBlogs": 405 this.AppKey = this.inputParams[0].InnerText; 406 this.UserName = this.inputParams[1].InnerText; 407 this.Password = this.inputParams[2].InnerText; 408 break; 409 case "blogger.deletePost": 410 this.AppKey = this.inputParams[0].InnerText; 411 this.PostID = this.inputParams[1].InnerText; 412 this.UserName = this.inputParams[2].InnerText; 413 this.Password = this.inputParams[3].InnerText; 414 this.Publish = this.inputParams[4].InnerText != "0" && this.inputParams[4].InnerText != "false"; 415 416 break; 417 case "blogger.getUserInfo": 418 this.AppKey = this.inputParams[0].InnerText; 419 this.UserName = this.inputParams[1].InnerText; 420 this.Password = this.inputParams[2].InnerText; 421 break; 422 case "wp.newPage": 423 this.BlogID = this.inputParams[0].InnerText; 424 this.UserName = this.inputParams[1].InnerText; 425 this.Password = this.inputParams[2].InnerText; 426 this.Page = GetPage(this.inputParams[3]); 427 this.Publish = this.inputParams[4].InnerText != "0" && this.inputParams[4].InnerText != "false"; 428 429 break; 430 case "wp.getPage": 431 this.BlogID = this.inputParams[0].InnerText; 432 this.PageID = this.inputParams[1].InnerText; 433 this.UserName = this.inputParams[2].InnerText; 434 this.Password = this.inputParams[3].InnerText; 435 break; 436 case "wp.editPage": 437 this.BlogID = this.inputParams[0].InnerText; 438 this.PageID = this.inputParams[1].InnerText; 439 this.UserName = this.inputParams[2].InnerText; 440 this.Password = this.inputParams[3].InnerText; 441 this.Page = GetPage(this.inputParams[4]); 442 this.Publish = this.inputParams[5].InnerText != "0" && this.inputParams[5].InnerText != "false"; 443 444 break; 445 case "wp.deletePage": 446 this.BlogID = this.inputParams[0].InnerText; 447 this.UserName = this.inputParams[1].InnerText; 448 this.Password = this.inputParams[2].InnerText; 449 this.PageID = this.inputParams[3].InnerText; 450 break; 451 default: 452 throw new MetaWeblogException("02", string.Format("Unknown Method. ({0})", this.MethodName)); 453 } 454 } 455 456 /* 457 /// <summary> 458 /// The log meta weblog call. 459 /// </summary> 460 /// <param name="message"> 461 /// The message. 462 /// </param> 463 private void LogMetaWeblogCall(string message) 464 { 465 var saveFolder = HttpContext.Current.Server.MapPath(BlogSettings.Instance.StorageLocation); 466 var saveFile = Path.Combine(saveFolder, "lastmetaweblogcall.txt"); 467 468 try 469 { 470 // Save message to file 471 using (var fileWrtr = new FileStream(saveFile, FileMode.OpenOrCreate, FileAccess.Write)) 472 { 473 using (var streamWrtr = new StreamWriter(fileWrtr)) 474 { 475 streamWrtr.WriteLine(message); 476 } 477 } 478 } 479 catch 480 { 481 // Ignore all errors 482 } 483 } 484 */ 485 486 /// <summary> 487 /// Retrieves the content of the input stream 488 /// and return it as plain text. 489 /// </summary> 490 /// <param name="context"> 491 /// The context. 492 /// </param> 493 /// <returns> 494 /// The parse request. 495 /// </returns> 496 private static string ParseRequest(HttpContext context) 497 { 498 var buffer = new byte[context.Request.InputStream.Length]; 499 500 context.Request.InputStream.Position = 0; 501 context.Request.InputStream.Read(buffer, 0, buffer.Length); 502 503 return Encoding.UTF8.GetString(buffer); 504 } 505 506 #endregion 507 } 508}