/// <summary> /// Persists an article to the backing store /// </summary> /// <param name="clientUsername">Authenticated username</param> /// <param name="data">Article data posted from NNTP client</param> /// <returns>String which will be returned to the client</returns> public string PostArticle(string clientUsername, string data) { try { if (data.Length > MaxPostLengthBytes) { //System.Diagnostics.Trace.WriteLine("Posting failed - maximum post length exceeded"); //return PostStatus.FailedExcessiveLength; return(GeneralResponses.PostingFailedExcessiveLength); } var articleBody = new StringBuilder(); var article = new Article { ContentType = string.Empty }; char[] delimiter = { '\r' }; bool isHeader = true; bool isMimePost = false; // undo dot stuffing //data = data.Replace("\r\n..\r\n", "\r\n.\r\n"); //data = data.Replace("\r\n..", "\r\n."); string[] lines = data.Split(delimiter); //Regex extendedChars = new Regex("[^\\u0009-\\u007e]", RegexOptions.Multiline | RegexOptions.IgnoreCase); string lastKeyValue = string.Empty; string line; foreach (var lineIter in lines) { line = lineIter.Replace("\n", string.Empty); //line = extendedChars.Replace(line, string.Empty); //System.Diagnostics.Trace.WriteLine("Processing line -|" + line + "|"); if (isHeader && string.IsNullOrEmpty(line)) { isHeader = false; continue; } if (isHeader) { var nameValuePair = new ArrayList(2); int sepPos = line.IndexOf(": "); if ((sepPos > 0) && (line.IndexOf(" ") > 0)) { nameValuePair.Add(line.Substring(0, sepPos).Trim()); nameValuePair.Add(line.Substring(sepPos + 2).Trim()); lastKeyValue = nameValuePair[0].ToString(); } else { nameValuePair.Add(string.Empty); nameValuePair.Add(line); } string keyValue = nameValuePair[0].ToString(); if (string.Compare(keyValue, HeaderNames.From, StringComparison.InvariantCultureIgnoreCase) == 0) { article.From = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.Date, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Date = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.Subject, StringComparison.InvariantCultureIgnoreCase) == 0) { // Also support newreaders with post the "subject" in multiple lines article.Subject = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.Newsgroups, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Newsgroups = nameValuePair[1].ToString().Replace("\"", string.Empty).Replace("'", string.Empty); } else if (string.Compare(keyValue, HeaderNames.UserAgent, StringComparison.InvariantCultureIgnoreCase) == 0) { article.UserAgent = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.XNewsreader, StringComparison.InvariantCultureIgnoreCase) == 0) { if (string.IsNullOrEmpty(article.UserAgent)) { article.UserAgent = nameValuePair[1].ToString(); // The "User-Agent" is embedded into the html, so either use "User-Agent" or "X-Newsreader" } article.XNewsreader = nameValuePair[1].ToString(); } // TODO: Also support of "X-Mailer:" !? else if (string.Compare(keyValue, HeaderNames.References, StringComparison.InvariantCultureIgnoreCase) == 0) { article.References = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.ContentType, StringComparison.InvariantCultureIgnoreCase) == 0) { // Also support newreaders with post the "content-type" in multiple lines, like: // Content-Type: text/plain; // format=flowed; // charset="iso-8859-1"; // reply-type=original article.ContentType = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.ContentTransferEncoding, StringComparison.InvariantCultureIgnoreCase) == 0) { article.ContentTransferEncoding = nameValuePair[1].ToString(); } else if (keyValue.Length == 0) { // Multi-Line Header: if (string.Compare(lastKeyValue, HeaderNames.From, StringComparison.InvariantCultureIgnoreCase) == 0) { article.From += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.Date, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Date += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.Subject, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Subject += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.Newsgroups, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Newsgroups += nameValuePair[1].ToString().Replace("\"", string.Empty).Replace("'", string.Empty); } else if (string.Compare(lastKeyValue, HeaderNames.UserAgent, StringComparison.InvariantCultureIgnoreCase) == 0) { article.UserAgent += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.XNewsreader, StringComparison.InvariantCultureIgnoreCase) == 0) { //if (string.IsNullOrEmpty(article.UserAgent)) // article.UserAgent = nameValuePair[1].ToString(); // The "User-Agent" is embedded into the html, so either use "User-Agent" or "X-Newsreader" article.XNewsreader += nameValuePair[1].ToString(); } // TODO: Also support of "X-Mailer:" !? else if (string.Compare(lastKeyValue, HeaderNames.References, StringComparison.InvariantCultureIgnoreCase) == 0) { article.References += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.ContentType, StringComparison.InvariantCultureIgnoreCase) == 0) { article.ContentType += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.ContentTransferEncoding, StringComparison.InvariantCultureIgnoreCase) == 0) { article.ContentTransferEncoding += nameValuePair[1].ToString(); } } else { } } // isHeader else { // Body: // undo dot stuff (remove the first dott, if there are two dots... if (line.IndexOf("..", StringComparison.InvariantCultureIgnoreCase) == 0) { line = line.Substring(1); } articleBody.Append(line + "\n"); } } // foreach article.Body = articleBody.ToString(); // Check for mimePostings: if (article.ContentType.IndexOf("multipart", StringComparison.InvariantCultureIgnoreCase) == 0) { isMimePost = true; } if (isMimePost) { var mime = new Mime { Text = article.Body }; // Exract boundary: var m2 = _bondaryRegex.Match(article.ContentType); if (m2.Success) { mime.Boundary = m2.Groups[1].Value; } string textPlain = null; string textPlainContentType = null; string textHtml = null; string textHtmlContentType = null; foreach (var mimePart in mime.MimeParts) { var ct = mimePart.GetPropertyValue("Content-Type"); if (ct.IndexOf("text/plain", StringComparison.InvariantCultureIgnoreCase) >= 0) { textPlainContentType = ct; textPlain = (string)mimePart.Decode(); } if (ct.IndexOf("text/html", StringComparison.InvariantCultureIgnoreCase) >= 0) { textHtmlContentType = ct; textHtml = (string)mimePart.Decode(); } if ((textPlain != null) && (textHtml != null)) { break; } } if ((textPlain == null) && (textHtml == null)) { //System.Diagnostics.Trace.WriteLine("Posting failed - text part not found"); //return PostStatus.FailedTextPartMissingInHtml; return(GeneralResponses.PostingFailedTextPartMissingInMime); } if (InMimeUseHtml && (textHtml != null)) { article.Body = textHtml; article.ContentType = textHtmlContentType; Traces.NntpServerTraceEvent(TraceEventType.Verbose, "MIME-Part: HTML selected"); } else { if (textPlain != null) { article.Body = textPlain; article.ContentType = textPlainContentType; Traces.NntpServerTraceEvent(TraceEventType.Verbose, "MIME-Part: plain/text selected"); } else { article.Body = textHtml; article.ContentType = textHtmlContentType; Traces.NntpServerTraceEvent(TraceEventType.Verbose, "MIME-Part: HTML selected (no plain/text available)"); } } } // Transcode the "body" according to the "charset", if one is specified: var charSetMatch = _charSetRegex.Match(article.ContentType); string charSet = Server.EncodingRecv.HeaderName; // default if (charSetMatch.Success) { charSet = charSetMatch.Groups[1].Value; } if (isMimePost == false) { if (article.ContentTransferEncoding.IndexOf("quoted-printable", StringComparison.InvariantCultureIgnoreCase) >= 0) { article.Body = MimePart.DecodeQuotedPrintable(article.Body); } else if (article.ContentTransferEncoding.IndexOf("base64", StringComparison.InvariantCultureIgnoreCase) >= 0) { article.Body = MimePart.DecodeBase64(article.Body, charSet); } } // Re-Encode after all data is now in the body... ReEncode(article, charSet); // post must have subject if (article.Subject.Trim().Length == 0) { //System.Diagnostics.Trace.WriteLine("Posting failed - no subject line"); //return PostStatus.FailedSubjectLineBlank; return(GeneralResponses.PostingFailedSubjectLineBlank); } // Decode Subject article.Subject = Mime.DecodeEncodedWordValue(article.Subject); var articles = new List <Article>(); // Disabled cross posting (2010-06-06) //// Cross-Postings (Multiple-Postings) are allowed for "primary messages": //if ( (string.IsNullOrEmpty(article.References)) && (article.Newsgroups.IndexOf(",", StringComparison.InvariantCultureIgnoreCase) > 0) ) //{ // string[] groupNames = article.Newsgroups.Split(','); // foreach (var groupName in groupNames) // { // Newsgroup group = GetNewsgroupFromCacheOrServer(groupName.Trim()); // if (group != null && group.PostingAllowed) // { // // copy the article... // var crossPostArticle = new Article(); // crossPostArticle.From = article.From; // crossPostArticle.Body = article.Body; // crossPostArticle.Date = article.Date; // crossPostArticle.Subject = article.Subject; // crossPostArticle.ParentNewsgroup = group.GroupName; // //crossPostArticle.References = article.References; // crossPostArticle.Newsgroups = article.Newsgroups; // crossPostArticle.UserAgent = article.UserAgent; // crossPostArticle.ContentType = article.ContentType; // crossPostArticle.ContentTransferEncoding = article.ContentTransferEncoding; // crossPostArticle.XNewsreader = article.XNewsreader; // // add cross-posted footnote // //crossPostArticle.Body += "\n\n[X-Posted To: " + article.Newsgroups + "]\n"; // articles.Add(crossPostArticle); // } // else // { // // indicate posting failure // //System.Diagnostics.Trace.WriteLine("Posting failed - user [" + clientUsername + "] does not have permission to post to group [" + groupName + "]"); // return PostStatus.FailedAccessDenied; // } // } // foreach // if (articles.Count <= 0) // { // return PostStatus.FailedGroupNotFound; // } // else // { // SaveArticles(clientUsername, articles); // } //} //else { // Only one group or a reply: if (article.Newsgroups.IndexOf(",", StringComparison.InvariantCultureIgnoreCase) > 0) { // Cross-Post are not allowed fro answers! return(GeneralResponses.PostingFailedAccessDeniedMultipost); } var group = GetNewsgroupFromCacheOrServer(article.Newsgroups); if (group != null && group.PostingAllowed) { article.ParentNewsgroup = group.GroupName; articles.Add(article); SaveArticles(clientUsername, articles); } else { // indicate posting failure if (group != null) { //System.Diagnostics.Trace.WriteLine("Posting failed - user [" + clientUsername + "] does not have permission to post to group [" + group.GroupName + "]"); //return PostStatus.FailedAccessDenied; return(GeneralResponses.PostingFailedAccessDenied); } //System.Diagnostics.Trace.WriteLine("Posting failed - newsgroup [" + article.Newsgroups + "] could not be found"); //return PostStatus.FailedGroupNotFound; return(GeneralResponses.PostingFailedGroupNotFound); } } return(GeneralResponses.ArticlePostedOk); } catch (Exception exp) { //System.Diagnostics.Trace.WriteLine("Error in DataProvider.PostArticle - " + ex.Message + " :: " + ex.StackTrace); Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "Error in DataProvider.PostArticle: {0}", Traces.ExceptionToString(exp)); var resp = string.Format("441 posting failed {0}\r\n", NntpServer.GetErrorResponseFromExeption(exp)); return(resp); } }
private MimePart[] GetMimeParts() { var mimeParts = new ArrayList(); MimePart mimePart = null; char[] separator = { '\n' }; string line; var lines = _text.Split(separator); var i = 0; var start = false; var startMimeHeader = false; var startMimeBody = false; var mimePartBody = new StringBuilder(); var mimePartCount = 0; string boundary = "------=_NextPart_"; if (string.IsNullOrEmpty(Boundary) == false) { boundary = Boundary.Trim('"', ' '); } while (i < lines.Length) { // get the next line line = lines[i].TrimEnd().Replace("\0", string.Empty); // check for header if (line == "This is a multi-part message in MIME format.") { if (start) { throw new Exception("Multiple MIME format headers detected"); } start = true; goto NextLine; } // new mime part detected if (line.IndexOf(boundary, StringComparison.InvariantCultureIgnoreCase) >= 0) { if (mimePartCount > 0) // not the first part { // save the body of the last mime part if (mimePart == null) { throw new ApplicationException("Should never happen..."); } mimePart.Body = mimePartBody.ToString(); // add the part to the collection mimeParts.Add(mimePart); // clear out for next part mimePartBody = new StringBuilder(); } // create a new mime part & increment the counter mimePart = new MimePart(); mimePartCount++; startMimeHeader = true; startMimeBody = false; goto NextLine; } // check for end of header if ((startMimeHeader) && (line.Trim().Length == 0 || line == "\0")) { startMimeHeader = false; startMimeBody = true; goto NextLine; } // copy header property if (startMimeHeader) { int sepPos = line.IndexOf(":"); if (sepPos > 0) { var propertyName = line.Substring(0, sepPos); var propertyValue = line.Substring(sepPos + 1); mimePart.AddProperty(propertyName.Trim(), propertyValue.Trim()); } else { // multi line header: var propertyValue = line; if (mimePart.Properties.Length > 0) { mimePart.AppendToLastProperty(propertyValue.Trim()); } } //if (line.StartsWith("\t")) //{ // propertyName = line.Substring(line.IndexOf("\t") + 1, line.IndexOf("=") - 1); // propertyValue = line.Substring(line.IndexOf("\"") + 1, line.Length - line.IndexOf("\"") - 2); // mimePart.AddProperty(propertyName.Trim(), propertyValue.Trim()); //} //else //{ // propertyName = line.Substring(0, line.IndexOf(":")); // propertyValue = line.Substring(line.IndexOf(":") + 1, line.Length - line.IndexOf(":") - 1); // if (propertyValue.Substring(propertyValue.Length - 1, 1) == ";") // { // propertyValue = propertyValue.Substring(0, propertyValue.Length - 1); // } // mimePart.AddProperty(propertyName.Trim(), propertyValue.Trim()); //} } if (startMimeBody) { mimePartBody.Append(line + "\n"); } NextLine: i++; } // commit the last part if (mimePart != null) { mimeParts.Add(mimePart); } return((MimePart[])mimeParts.ToArray(typeof(MimePart))); }