3 * see AUTHORS for the list of contributors
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 package org.sonews.daemon.command;
21 import java.io.IOException;
22 import java.sql.SQLException;
23 import java.util.List;
24 import org.sonews.util.Log;
25 import org.sonews.daemon.NNTPConnection;
26 import org.sonews.daemon.storage.Article;
27 import org.sonews.daemon.storage.ArticleHead;
28 import org.sonews.daemon.storage.Headers;
29 import org.sonews.util.Pair;
32 * Class handling the OVER/XOVER command.
34 * Description of the XOVER command:
38 * The XOVER command returns information from the overview
39 * database for the article(s) specified.
41 * The optional range argument may be any of the following:
43 * an article number followed by a dash to indicate
45 * an article number followed by a dash followed by
46 * another article number
48 * If no argument is specified, then information from the
49 * current article is displayed. Successful responses start
50 * with a 224 response followed by the overview information
51 * for all matched messages. Once the output is complete, a
52 * period is sent on a line by itself. If no argument is
53 * specified, the information for the current article is
54 * returned. A news group must have been selected earlier,
55 * else a 412 error response is returned. If no articles are
56 * in the range specified, a 420 error response is returned
57 * by the server. A 502 response will be returned if the
58 * client only has permission to transfer articles.
60 * Each line of output will be formatted with the article number,
61 * followed by each of the headers in the overview database or the
62 * article itself (when the data is not available in the overview
63 * database) for that article separated by a tab character. The
64 * sequence of fields must be in this order: subject, author,
65 * date, message-id, references, byte count, and line count. Other
66 * optional fields may follow line count. Other optional fields may
67 * follow line count. These fields are specified by examining the
68 * response to the LIST OVERVIEW.FMT command. Where no data exists,
69 * a null field must be provided (i.e. the output will have two tab
70 * characters adjacent to each other). Servers should not output
71 * fields for articles that have been removed since the XOVER database
74 * The LIST OVERVIEW.FMT command should be implemented if XOVER
75 * is implemented. A client can use LIST OVERVIEW.FMT to determine
76 * what optional fields and in which order all fields will be
77 * supplied by the XOVER command.
79 * Note that any tab and end-of-line characters in any header
80 * data that is returned will be converted to a space character.
84 * 224 Overview information follows
85 * 412 No news group current selected
86 * 420 No article(s) selected
89 * OVER defines additional responses:
91 * First form (message-id specified)
92 * 224 Overview information follows (multi-line)
93 * 430 No article with that message-id
95 * Second form (range specified)
96 * 224 Overview information follows (multi-line)
97 * 412 No newsgroup selected
98 * 423 No articles in that range
100 * Third form (current article number used)
101 * 224 Overview information follows (multi-line)
102 * 412 No newsgroup selected
103 * 420 Current article number is invalid
106 * @author Christian Lins
107 * @since sonews/0.5.0
109 public class OverCommand extends AbstractCommand
112 public static final int MAX_LINES_PER_DBREQUEST = 100;
114 public OverCommand(final NNTPConnection conn)
120 public boolean hasFinished()
126 public void processLine(final String line)
127 throws IOException, SQLException
129 if(getCurrentGroup() == null)
131 printStatus(412, "No news group current selected");
135 String[] command = line.split(" ");
137 // If no parameter was specified, show information about
138 // the currently selected article(s)
139 if(command.length == 1)
141 final Article art = getCurrentArticle();
144 printStatus(420, "No article(s) selected");
148 println(buildOverview(art, -1));
150 // otherwise print information about the specified range
154 int artEnd = getCurrentGroup().getLastArticleNumber();
155 String[] nums = command[1].split("-");
160 artStart = Integer.parseInt(nums[0]);
162 catch(NumberFormatException e)
164 Log.msg(e.getMessage(), true);
165 artStart = Integer.parseInt(command[1]);
170 artStart = getCurrentGroup().getFirstArticleNumber();
177 artEnd = Integer.parseInt(nums[1]);
179 catch(NumberFormatException e)
185 if(artStart > artEnd)
187 if(command[0].equalsIgnoreCase("OVER"))
189 printStatus(423, "No articles in that range");
193 printStatus(224, "(empty) overview information follows:");
199 for(int n = artStart; n <= artEnd; n += MAX_LINES_PER_DBREQUEST)
201 int nEnd = Math.min(n + MAX_LINES_PER_DBREQUEST - 1, artEnd);
202 List<Pair<Long, ArticleHead>> articleHeads = getCurrentGroup()
203 .getArticleHeads(n, nEnd);
204 if(articleHeads.isEmpty() && n == artStart
205 && command[0].equalsIgnoreCase("OVER"))
207 // This reply is only valid for OVER, not for XOVER command
208 printStatus(423, "No articles in that range");
211 else if(n == artStart)
213 // XOVER replies this although there is no data available
214 printStatus(224, "Overview information follows");
217 for(Pair<Long, ArticleHead> article : articleHeads)
219 String overview = buildOverview(article.getB(), article.getA());
229 private String buildOverview(ArticleHead art, long nr)
231 StringBuilder overview = new StringBuilder();
233 overview.append('\t');
235 String subject = art.getHeader(Headers.SUBJECT)[0];
236 if("".equals(subject))
240 overview.append(escapeString(subject));
241 overview.append('\t');
243 overview.append(escapeString(art.getHeader(Headers.FROM)[0]));
244 overview.append('\t');
245 overview.append(escapeString(art.getHeader(Headers.DATE)[0]));
246 overview.append('\t');
247 overview.append(escapeString(art.getHeader(Headers.MESSAGE_ID)[0]));
248 overview.append('\t');
249 overview.append(escapeString(art.getHeader(Headers.REFERENCES)[0]));
250 overview.append('\t');
252 String bytes = art.getHeader(Headers.BYTES)[0];
257 overview.append(escapeString(bytes));
258 overview.append('\t');
260 String lines = art.getHeader(Headers.LINES)[0];
265 overview.append(escapeString(lines));
266 overview.append('\t');
267 overview.append(escapeString(art.getHeader(Headers.XREF)[0]));
269 // Remove trailing tabs if some data is empty
270 return overview.toString().trim();
273 private String escapeString(String str)
275 String nstr = str.replace("\r", "");
276 nstr = nstr.replace('\n', ' ');
277 nstr = nstr.replace('\t', ' ');