/// <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);
                        if (line.IndexOf(": ") > 0)
                        {
                            nameValuePair.Add(line.Substring(0, line.IndexOf(": ")).Trim());
                            lastKeyValue = nameValuePair[0].ToString();
                        }
                        else
                        {
                            nameValuePair.Add(string.Empty);
                        }
                        nameValuePair.Add(line.Substring(line.IndexOf(": ") + 2).Trim());

                        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 GeneralResponses.PostingFailed;

                return(resp);
            }
        }
Esempio n. 2
0
        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)));
        }