private void IHave(string argsText) { /* Rfc 977 3.4.1 IHAVE <messageid> Responses: 235 article transferred ok 335 send article to be transferred. End with <CR-LF>.<CR-LF> 435 article not wanted - do not send it 436 transfer failed - try again later 437 article rejected - do not try again The IHAVE command informs the server that the client has an article whose id is <messageid>. If the server desires a copy of that article, it will return a response instructing the client to send the entire article. If the server does not want the article (if, for example, the server already has a copy of it), a response indicating that the article is not wanted will be returned. If transmission of the article is requested, the client should send the entire article, including header and body, in the manner specified for text transmission from the server. A response code indicating success or failure of the transferral of the article will be returned. This function differs from the POST command in that it is intended for use in transferring already-posted articles between hosts. Normally it will not be used when the client is a personal newsreading program. In particular, this function will invoke the server's news posting program with the appropriate settings (flags, options, etc) to indicate that the forthcoming article is being forwarded from another host. The server may, however, elect not to post or forward the article if after further examination of the article it deems it inappropriate to do so. The 436 or 437 error codes may be returned as appropriate to the situation. Reasons for such subsequent rejection of an article may include such problems as inappropriate newsgroups or distributions, disk space limitations, article lengths, garbled headers, and the like. These are typically restrictions enforced by the server host's news software and not necessarily the NNTP server itself. */ //must this check in all newsgroups??? if(m_pArticles == null) { NNTP_Articles_eArgs args = m_pNNTP_Server.OnGetXOVER(this,this.m_SelectedNewsGroup); m_pArticles = new NNTP_Articles(); m_pArticles = args.Articles; } if(m_pArticles[argsText] == null) { SendData("335 send article to be transferred. End with <CR-LF>.<CR-LF>\r\n"); MemoryStream reply = null; ReadReplyCode replyCode = Core.ReadData(m_pClientSocket,out reply,null,m_pNNTP_Server.MaxMessageSize,m_pNNTP_Server.CommandIdleTimeOut,"\r\n.\r\n",".\r\n"); if(replyCode == ReadReplyCode.Ok) { long recivedCount = reply.Length; //------- Do period handling and raise store event --------// // If line starts with '.', mail client adds additional '.', // remove them. using(MemoryStream msgStream = Core.DoPeriodHandling(reply,false)) { reply.Close(); // get list of newsgroups to post to string[] newsgroups = HeaderParser.ParseHeaderFields("Newsgroups:",msgStream.ToArray()).Split(Convert.ToChar(",")); //get all the necessary headers HeaderParser hp = new HeaderParser(msgStream.ToArray()); string msgId = m_pNNTP_Server.OnStoreArticle(this,msgStream,newsgroups); string subject = hp.Subject; string from = hp.From; string date = hp.MessageDate.ToString("r",System.Globalization.DateTimeFormatInfo.InvariantInfo); string refs = hp.References; string lines = hp.Lines; //Add msg to current article list if(m_pNNTP_Server.LogCommands) { m_pLogWriter.AddEntry("big binary " + recivedCount.ToString() + " bytes" + "",this.SessionID,m_ConnectedIp,"S"); } SendData("240 Article posted successfully.\r\n"); } //----------------------------------------------------------// } else { if(replyCode == ReadReplyCode.LengthExceeded) { SendData("441 Requested action aborted: exceeded storage allocation\r\n"); } else { SendData("441 Error message not terminated with '.'\r\n"); } } } else { SendData("435 article not wanted - do not send it\r\n"); } }
private void Stat(string argsText) { /* Rfc 977 3.1.2 STAT <message-id> or [nnn] Responses: 220 n <a> article retrieved - head and body follow (n = article number, <a> = message-id) 221 n <a> article retrieved - head follows 222 n <a> article retrieved - body follows 223 n <a> article retrieved - request text separately 412 no newsgroup has been selected 420 no current article has been selected 423 no such article number in this group 430 no such article found The STAT command is similar to the ARTICLE command except that no text is returned. When selecting by message number within a group, the STAT command serves to set the current article pointer without sending text. The returned acknowledgement response will contain the message-id, which may be of some value. Using the STAT command to select by message-id is valid but of questionable value, since a selection by message-id does NOT alter the "current article pointer". */ if(argsText == "") { argsText = m_CurrentArticle.ToString(); } else { if(NNTP_API.IsNumeric(argsText)) { m_CurrentArticle = Convert.ToInt32(argsText); } } if(m_SelectedNewsGroup.Length == 0) { SendData("412 no newsgroup selected\r\n"); return; } string article = m_pNNTP_Server.OnGetArticle(this,argsText); if(article != "") { HeaderParser hp = new HeaderParser(System.Text.Encoding.ASCII.GetBytes(article)); SendData("223 " + argsText + " " + hp.MessageID + " article retrieved\r\n"); } else { SendData("430 no such article found\r\n"); } }
/// <summary> /// Gets new article info for newsgroups. /// </summary> public void GetNewNews(string groups,DateTime since,NNTP_Articles articles) { byte[] fileData = null; int msgNo = 1; Hashtable files = new Hashtable(); NNTP_NewsGroups newsgroups = ParseNewsGroups(groups); foreach(NNTP_NewsGroup group in newsgroups.Newsgroups) { string groupPath = group.Name.Replace(".","\\"); string path = m_NNTPStorePath + "groups\\" + groupPath; if(!Directory.Exists(path)){ Directory.CreateDirectory(path); AddGroup(group.Name); } string[] articls = Directory.GetFiles(path,"*.txt"); foreach(string articl in articls) { using(FileStream fs = File.OpenRead(articl)) { fileData = new byte[fs.Length]; fs.Read(fileData,0,(int)fs.Length); } HeaderParser hp = new HeaderParser(fileData); msgNo = Convert.ToInt32(Path.GetFileNameWithoutExtension(articl).Substring(0,Path.GetFileNameWithoutExtension(articl).IndexOf("_"))); string date = hp.MessageDate.ToString(); if(Convert.ToDateTime(date).CompareTo(since) > 0) { articles.Add(hp.MessageID,msgNo,hp.Subject,hp.From,date,hp.References,hp.Lines,fileData.Length.ToString()); } } } }
private void Post() { /* Rfc 977 3.10.1 Responses: 240 article posted ok 340 send article to be posted. End with <CR-LF>.<CR-LF> 440 posting not allowed 441 posting failed If posting is allowed, response code 340 is returned to indicate that the article to be posted should be sent. Response code 440 indicates that posting is prohibited for some installation-dependent reason. If posting is permitted, the article should be presented in the format specified by RFC850, and should include all required header lines. After the article's header and body have been completely sent by the client to the server, a further response code will be returned to indicate success or failure of the posting attempt. The text forming the header and body of the message to be posted should be sent by the client using the conventions for text received from the news server: A single period (".") on a line indicates the end of the text, with lines starting with a period in the original text having that period doubled during transmission. No attempt shall be made by the server to filter characters, fold or limit lines, or otherwise process incoming text. It is our intent that the server just pass the incoming message to be posted to the server installation's news posting software, which is separate from this specification. See RFC850 for more details. Since most installations will want the client news program to allow the user to prepare his message using some sort of text editor, and transmit it to the server for posting only after it is composed, the client program should take note of the herald message that greeted it when the connection was first established. This message indicates whether postings from that client are permitted or not, and can be used to caution the user that his access is read-only if that is the case. This will prevent the user from wasting a good deal of time composing a message only to find posting of the message was denied. The method and determination of which clients and hosts may post is installation dependent and is not covered by this specification. Example: C: POST S: 340 Continue posting; Period on a line by itself to end C: (transmits news article in RFC850 format) C: . S: 240 Article posted successfully. */ SendData("340 Continue posting; Period on a line by itself to end\r\n"); // Read message MemoryStream reply = null; ReadReplyCode replyCode = Core.ReadData(m_pClientSocket,out reply,null,m_pNNTP_Server.MaxMessageSize,m_pNNTP_Server.CommandIdleTimeOut,"\r\n.\r\n",".\r\n"); if(replyCode == ReadReplyCode.Ok){ long recivedCount = reply.Length; //------- Do period handling and raise store event --------// // If line starts with '.', mail client adds additional '.', // remove them. using(MemoryStream msgStream = Core.DoPeriodHandling(reply,false)){ reply.Close(); // get list of newsgroups to post to string[] newsgroups = HeaderParser.ParseHeaderFields("Newsgroups:",msgStream.ToArray()).Split(Convert.ToChar(",")); //get all the necessary headers HeaderParser hp = new HeaderParser(msgStream.ToArray()); string msgId = m_pNNTP_Server.OnStoreArticle(this,msgStream,newsgroups); string subject = hp.Subject; string from = hp.From; string date = hp.MessageDate.ToString("r",System.Globalization.DateTimeFormatInfo.InvariantInfo); string refs = hp.References; string lines = hp.Lines; //Add msg to current article list if(m_pNNTP_Server.LogCommands){ m_pLogWriter.AddEntry("big binary " + recivedCount.ToString() + " bytes" + "",this.SessionID,m_ConnectedIp,"S"); } SendData("240 Article posted successfully.\r\n"); } //----------------------------------------------------------// } else{ if(replyCode == ReadReplyCode.LengthExceeded){ SendData("441 Requested action aborted: exceeded storage allocation\r\n"); } else{ SendData("441 Error message not terminated with '.'\r\n"); } } }
/// <summary> /// Gets article info for a newsgroup. /// </summary> public void GetArticles(string group,NNTP_Articles articles) { byte[] fileData = null; int msgNo = 1; Hashtable files = new Hashtable(); group = group.Replace(".","\\"); string path = m_NNTPStorePath + "groups\\" + group; if(!Directory.Exists(path)) { Directory.CreateDirectory(path); AddGroup(group); } string[] articls = Directory.GetFiles(path,"*.txt"); foreach(string articl in articls) { using(FileStream fs = File.OpenRead(articl)) { fileData = new byte[fs.Length]; fs.Read(fileData,0,(int)fs.Length); } HeaderParser hp = new HeaderParser(fileData); msgNo = Convert.ToInt32(Path.GetFileNameWithoutExtension(articl).Substring(0,Path.GetFileNameWithoutExtension(articl).IndexOf("_"))); string date = hp.MessageDate.ToString(); articles.Add(msgNo,hp.MessageID,hp.Subject,hp.From,date,hp.References,hp.Lines,fileData.Length.ToString()); } }