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 NewNews(string argsText) { /* Rfc 977 3.8 Responses: 230 list of new articles by message-id follows NEWNEWS newsgroups date time [GMT] [<distribution>] A list of message-ids of articles posted or received to the specified newsgroup since "date" will be listed. The format of the listing will be one message-id per line, as though text were being sent. A single line consisting solely of one period followed by CR-LF will terminate the list. Date and time are in the same format as the NEWGROUPS command. A newsgroup name containing a "*" (an asterisk) may be specified to broaden the article search to some or all newsgroups. The asterisk will be extended to match any part of a newsgroup name (e.g., net.micro* will match net.micro.wombat, net.micro.apple, etc). Thus if only an asterisk is given as the newsgroup name, all newsgroups will be searched for new news. (Please note that the asterisk "*" expansion is a general replacement; in particular, the specification of e.g., net.*.unix should be correctly expanded to embrace names such as net.wombat.unix and net.whocares.unix.) Conversely, if no asterisk appears in a given newsgroup name, only the specified newsgroup will be searched for new articles. Newsgroup names must be chosen from those returned in the listing of available groups. Multiple newsgroup names (including a "*") may be specified in this command, separated by a comma. No comma shall appear after the last newsgroup in the list. [Implementors are cautioned to keep the 512 character command length limit in mind.] The exclamation point ("!") may be used to negate a match. This can be used to selectively omit certain newsgroups from an otherwise larger list. For example, a newsgroups specification of "net.*,mod.*,!mod.map.*" would specify that all net.<anything> and all mod.<anything> EXCEPT mod.map.<anything> newsgroup names would be matched. If used, the exclamation point must appear as the first character of the given newsgroup name or pattern. The optional parameter "distributions" is a list of distribution groups, enclosed in angle brackets. If specified, the distribution portion of an article's newsgroup (e.g, 'net' in 'net.wombat') will be examined for a match with the distribution categories listed, and only those articles which have at least one newsgroup belonging to the list of distributions will be listed. If more than one distribution group is to be supplied, they must be separated by commas within the angle brackets. The use of the IHAVE, NEWNEWS, and NEWGROUPS commands to distribute news is discussed in an earlier part of this document. Please note that an empty list (i.e., the text body returned by this command consists only of the terminating period) is a possible valid response, and indicates that there is currently no new news. */ string newsgroups = ""; if(argsText.IndexOf("<") > -1) { newsgroups = argsText.Substring(argsText.IndexOf("<") + 1,argsText.IndexOf(">") - argsText.IndexOf("<") - 1); argsText.Substring(0,argsText.IndexOf("<")); } newsgroups = argsText.Substring(0,argsText.IndexOf(" ")).Trim(); argsText = argsText.Substring(argsText.IndexOf(" ")).Trim(); //argsText = "20" + argsText.Substring(0,2) + "/" + argsText.Substring(2,2) + "/" + argsText.Substring(4,2) +" " + argsText.Substring(7,2) + ":" + argsText.Substring(9,2) + ":" + argsText.Substring(11,2); string[] dateFormats = new string[]{"yyMMdd HHmmss","yyMMdd HHmmss 'GMT'"}; DateTime since = DateTime.Today; since = DateTime.ParseExact(argsText,dateFormats,System.Globalization.DateTimeFormatInfo.InvariantInfo,System.Globalization.DateTimeStyles.AllowWhiteSpaces); m_pArticles = new NNTP_Articles(); m_pArticles = m_pNNTP_Server.OnGetNewNews(this,newsgroups,since).Articles; SendData("231 list of new articles follow\r\n"); foreach(NNTP_Article article in m_pArticles.Articles) { SendData(article.ID + "\r\n"); } // Send list end SendData(".\r\n"); }
private void Group(string argsText) { /* Rfc 977 3.2.1 GROUP ggg Responses: 211 n f l s group selected (n = estimated number of articles in group, f = first article number in the group, l = last article number in the group, s = name of the group.) 411 no such news group The required parameter ggg is the name of the newsgroup to be selected (e.g. "net.news"). A list of valid newsgroups may be obtained from the LIST command. The successful selection response will return the article numbers of the first and last articles in the group, and an estimate of the number of articles on file in the group. It is not necessary that the estimate be correct, although that is helpful; it must only be equal to or larger than the actual number of articles on file. (Some implementations will actually count the number of articles on file. Others will just subtract first article number from last to get an estimate.) When a valid group is selected by means of this command, the internally maintained "current article pointer" is set to the first article in the group. If an invalid group is specified, the previously selected group and article remain selected. If an empty newsgroup is selected, the "current article pointer" is in an indeterminate state and should not be used. Note that the name of the newsgroup is not case-dependent. It must otherwise match a newsgroup obtained from the LIST command or an error will result. */ NNTP_NewsGroup grp = m_pNNTP_Server.OnGetGroupInfo(this,argsText); if(grp != null){ m_SelectedNewsGroup = argsText; m_CurrentArticle = (grp.ArticlesCount > 0) ? 1 : -1; //Load all messages for selected group... //This needs some testing with big groups (>10000 articles maybe) for performace NNTP_Articles_eArgs args = m_pNNTP_Server.OnGetXOVER(this,this.m_SelectedNewsGroup); m_pArticles = new NNTP_Articles(); m_pArticles = args.Articles; SendData("211 " + grp.ArticlesCount + " " + grp.FirstArticleNo + " " + grp.LastArticleNo + " " + argsText + " group selected\r\n"); } else{ SendData("411 no such news group\r\n"); } }
private void XOVER(string argsText) { /*RFC 2980 2.8 XOVER XOVER [range] The XOVER command returns information from the overview database for the article(s) specified. This command was originally suggested as part of the OVERVIEW work described in "The Design of a Common Newsgroup Overview Database for Newsreaders" by Geoff Collyer. This document is distributed in the Cnews distribution. The optional range argument may be any of the following: an article number an article number followed by a dash to indicate all following an article number followed by a dash followed by another article number If no argument is specified, then information from the current article is displayed. Successful responses start with a 224 response followed by the overview information for all matched messages. Once the output is complete, a period is sent on a line by itself. If no argument is specified, the information for the current article is returned. A news group must have been selected earlier, else a 412 error response is returned. If no articles are in the range specified, a 420 error response is returned by the server. A 502 response will be returned if the client only has permission to transfer articles. Each line of output will be formatted with the article number, followed by each of the headers in the overview database or the article itself (when the data is not available in the overview database) for that article separated by a tab character. The sequence of fields must be in this order: subject, author, date, message-id, references, byte count, and line count. Other optional fields may follow line count. Other optional fields may follow line count. These fields are specified by examining the response to the LIST OVERVIEW.FMT command. Where no data exists, a null field must be provided (i.e. the output will have two tab characters adjacent to each other). Servers should not output fields for articles that have been removed since the XOVER database was created. The LIST OVERVIEW.FMT command should be implemented if XOVER is implemented. A client can use LIST OVERVIEW.FMT to determine what optional fields and in which order all fields will be supplied by the XOVER command. See Section 2.1.7 for more details about the LIST OVERVIEW.FMT command.*/ //NNTP_Articles_eArgs args = OnGetXOVER(this,this.m_SelectedNewsGroup); //NNTP_Articles artcls = args.Articles; try { SendData("224 data follows\r\n"); string From = argsText.Substring(0,argsText.IndexOf("-",0)); string cTo = argsText.Substring(argsText.IndexOf("-",0)+1); int to = Convert.ToInt32(cTo); int from = Convert.ToInt32(From); 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; } for(int counter = from;counter <= to;counter++) { SendData(m_pArticles[counter].Number + "\t" + m_pArticles[counter].Subject + "\t" + m_pArticles[counter].Author + "\t" + m_pArticles[counter].Date + "\t" + m_pArticles[counter].ID + "\t" + m_pArticles[counter].References + "\t" + m_pArticles[counter].ByteCount + "\t" + m_pArticles[counter].Lines + "\r\n"); } SendData(".\r\n"); } catch(Exception x) { } }
/// <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()); } } } }
/// <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()); } }
public NNTP_Articles_eArgs(NNTP_Articles articles,string newsgroup) { m_pArticels = articles; m_Newsgroup = newsgroup; }