Drupal: zprávy jsou multipart/alternative – jak prostý text, tak XHTML. TODO: filtry prostého textu a XHTML
zatím se do všech zpráv vkládá stejný vycpávkový text.
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/>.
18 package org.sonews.storage.impl;
20 import java.sql.Connection;
21 import java.sql.DriverManager;
22 import java.sql.PreparedStatement;
23 import java.sql.ResultSet;
24 import java.sql.Statement;
25 import java.text.SimpleDateFormat;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32 import javax.mail.Message;
33 import org.apache.commons.codec.net.QuotedPrintableCodec;
34 import org.sonews.config.Config;
35 import org.sonews.feed.Subscription;
36 import org.sonews.storage.Article;
37 import org.sonews.storage.ArticleHead;
38 import org.sonews.storage.DrupalArticle;
39 import org.sonews.storage.DrupalMessage;
40 import org.sonews.storage.Group;
41 import org.sonews.storage.Storage;
42 import org.sonews.storage.StorageBackendException;
43 import org.sonews.util.Pair;
47 * @author František Kučera (frantovo.cz)
49 public class DrupalDatabase implements Storage {
51 private static final Logger log = Logger.getLogger(DrupalDatabase.class.getName());
52 public static final String CHARSET = "UTF-8";
53 public static final String CRLF = "\r\n";
54 protected Connection conn = null;
55 private QuotedPrintableCodec qpc = new QuotedPrintableCodec(CHARSET);
56 private SimpleDateFormat RFC822_DATE = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);
57 // TODO: správná doména
58 private String myDomain = "nntp.i1984.cz";
60 public DrupalDatabase() throws StorageBackendException {
64 private void connectDatabase() throws StorageBackendException {
66 // Load database driver
67 String driverClass = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DBMSDRIVER, "java.lang.Object");
68 Class.forName(driverClass);
70 // Establish database connection
71 String url = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_DATABASE, "<not specified>");
72 String username = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_USER, "root");
73 String password = Config.inst().get(Config.LEVEL_FILE, Config.STORAGE_PASSWORD, "");
74 conn = DriverManager.getConnection(url, username, password);
76 conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
77 if (conn.getTransactionIsolation() != Connection.TRANSACTION_SERIALIZABLE) {
78 log.warning("Database is NOT fully serializable!");
80 } catch (Exception e) {
81 throw new StorageBackendException(e);
85 protected static void close(Connection connection, Statement statement, ResultSet resultSet) {
86 if (resultSet != null) {
89 } catch (Exception e) {
92 if (statement != null) {
95 } catch (Exception e) {
98 if (connection != null) {
101 } catch (Exception e) {
108 * @param messageID <{0}-{1}-{2}@domain.tld> where {0} is nntp_id and {1} is group_id and {2} is group_name
109 * @return array where [0] = nntp_id and [1] = group_id and [2] = group_name or returns null if messageID is invalid
111 private static String[] parseMessageID(String messageID) {
112 if (messageID.matches("<[0-9]+\\-[0-9]+\\-[a-z0-9\\.]+@.+>")) {
113 return messageID.substring(1).split("@")[0].split("\\-");
119 private static Long parseArticleID(String messageID) {
120 String[] localPart = parseMessageID(messageID);
121 if (localPart == null) {
124 return Long.parseLong(localPart[0]);
128 private static Long parseGroupID(String messageID) {
129 String[] localPart = parseMessageID(messageID);
130 if (localPart == null) {
133 return Long.parseLong(localPart[1]);
135 // parseGroupName() will be same as this method, just with:
136 // return localPart[2];
141 public List<Group> getGroups() throws StorageBackendException {
142 PreparedStatement ps = null;
145 ps = conn.prepareStatement("SELECT * FROM nntp_group");
146 rs = ps.executeQuery();
147 List<Group> skupiny = new ArrayList<Group>();
150 skupiny.add(new Group(rs.getString("name"), rs.getInt("id"), Group.READONLY));
154 } catch (Exception e) {
155 throw new StorageBackendException(e);
162 public Group getGroup(String name) throws StorageBackendException {
163 PreparedStatement ps = null;
166 ps = conn.prepareStatement("SELECT * FROM nntp_group WHERE name = ?");
167 ps.setString(1, name);
168 rs = ps.executeQuery();
171 return new Group(rs.getString("name"), rs.getInt("id"), Group.READONLY);
175 } catch (Exception e) {
176 throw new StorageBackendException(e);
183 public boolean isGroupExisting(String groupname) throws StorageBackendException {
184 return getGroup(groupname) != null;
188 public Article getArticle(String messageID) throws StorageBackendException {
189 Long articleID = parseArticleID(messageID);
190 Long groupID = parseGroupID(messageID);
192 if (articleID == null || groupID == null) {
193 log.log(Level.SEVERE, "Invalid messageID: {0}", new Object[]{messageID});
196 return getArticle(articleID, groupID);
201 public Article getArticle(long articleID, long groupID) throws StorageBackendException {
202 PreparedStatement ps = null;
205 ps = conn.prepareStatement("SELECT * FROM nntp_article WHERE id = ? AND group_id = ?");
206 ps.setLong(1, articleID);
207 ps.setLong(2, groupID);
208 rs = ps.executeQuery();
211 DrupalMessage m = new DrupalMessage(rs, myDomain, true);
212 return new DrupalArticle(m);
216 } catch (Exception e) {
217 throw new StorageBackendException(e);
224 public List<Pair<Long, ArticleHead>> getArticleHeads(Group group, long first, long last) throws StorageBackendException {
225 PreparedStatement ps = null;
228 ps = conn.prepareStatement("SELECT * FROM nntp_article WHERE group_id = ? AND id >= ? AND id <= ? ORDER BY id");
229 ps.setLong(1, group.getInternalID());
230 ps.setLong(2, first);
232 rs = ps.executeQuery();
234 List<Pair<Long, ArticleHead>> heads = new ArrayList<Pair<Long, ArticleHead>>();
237 DrupalMessage m = new DrupalMessage(rs, myDomain, false);
238 String headers = m.getHeaders();
239 heads.add(new Pair<Long, ArticleHead>(rs.getLong("id"), new ArticleHead(headers)));
243 } catch (Exception e) {
244 throw new StorageBackendException(e);
251 public long getArticleIndex(Article article, Group group) throws StorageBackendException {
252 Long id = parseArticleID(article.getMessageID());
254 throw new StorageBackendException("Invalid messageID: " + article.getMessageID());
261 public List<Long> getArticleNumbers(long groupID) throws StorageBackendException {
262 PreparedStatement ps = null;
265 ps = conn.prepareStatement("SELECT id FROM nntp_article WHERE group_id = ?");
266 ps.setLong(1, groupID);
267 rs = ps.executeQuery();
268 List<Long> articleNumbers = new ArrayList<Long>();
270 articleNumbers.add(rs.getLong(1));
272 return articleNumbers;
273 } catch (Exception e) {
274 throw new StorageBackendException(e);
281 public int getFirstArticleNumber(Group group) throws StorageBackendException {
282 PreparedStatement ps = null;
285 ps = conn.prepareStatement("SELECT min(id) FROM nntp_article WHERE group_id = ?");
286 ps.setLong(1, group.getInternalID());
287 rs = ps.executeQuery();
290 } catch (Exception e) {
291 throw new StorageBackendException(e);
298 public int getLastArticleNumber(Group group) throws StorageBackendException {
299 PreparedStatement ps = null;
302 ps = conn.prepareStatement("SELECT max(id) FROM nntp_article WHERE group_id = ?");
303 ps.setLong(1, group.getInternalID());
304 rs = ps.executeQuery();
307 } catch (Exception e) {
308 throw new StorageBackendException(e);
315 public boolean isArticleExisting(String messageID) throws StorageBackendException {
316 Long articleID = parseArticleID(messageID);
317 Long groupID = parseGroupID(messageID);
319 if (articleID == null || groupID == null) {
322 PreparedStatement ps = null;
325 ps = conn.prepareStatement("SELECT count(*) FROM nntp_article WHERE id = ? AND group_id = ?");
326 ps.setLong(1, articleID);
327 ps.setLong(2, groupID);
328 rs = ps.executeQuery();
331 return rs.getInt(1) == 1;
332 } catch (Exception e) {
333 throw new StorageBackendException(e);
341 public int countArticles() throws StorageBackendException {
342 PreparedStatement ps = null;
345 ps = conn.prepareStatement("SELECT count(*) FROM nntp_article");
346 rs = ps.executeQuery();
349 } catch (Exception e) {
350 throw new StorageBackendException(e);
357 public int countGroups() throws StorageBackendException {
358 PreparedStatement ps = null;
361 ps = conn.prepareStatement("SELECT count(*) FROM nntp_group");
362 rs = ps.executeQuery();
365 } catch (Exception e) {
366 throw new StorageBackendException(e);
373 public int getPostingsCount(String groupname) throws StorageBackendException {
374 PreparedStatement ps = null;
377 ps = conn.prepareStatement("SELECT count(*) FROM nntp_article WHERE group_name = ?");
378 ps.setString(1, groupname);
379 rs = ps.executeQuery();
382 } catch (Exception e) {
383 throw new StorageBackendException(e);
390 public List<Pair<Long, String>> getArticleHeaders(Group group, long start, long end, String header, String pattern) throws StorageBackendException {
391 log.log(Level.SEVERE, "TODO: getArticleHeaders {0} / {1} / {2} / {3} / {4}", new Object[]{group, start, end, header, pattern});
393 return Collections.emptyList();
397 public void addArticle(Article art) throws StorageBackendException {
398 log.log(Level.SEVERE, "TODO: addArticle {0}", new Object[]{art});
402 public void addEvent(long timestamp, int type, long groupID) throws StorageBackendException {
403 log.log(Level.SEVERE, "TODO: addEvent {0} / {1} / {2}", new Object[]{timestamp, type, groupID});
407 public void addGroup(String groupname, int flags) throws StorageBackendException {
408 log.log(Level.SEVERE, "TODO: addGroup {0} / {1}", new Object[]{groupname, flags});
412 public void delete(String messageID) throws StorageBackendException {
413 log.log(Level.SEVERE, "TODO: delete {0}", new Object[]{messageID});
417 public String getConfigValue(String key) throws StorageBackendException {
418 //log.log(Level.SEVERE, "TODO: getConfigValue {0}", new Object[]{key});
423 public void setConfigValue(String key, String value) throws StorageBackendException {
424 log.log(Level.SEVERE, "TODO: setConfigValue {0} = {1}", new Object[]{key, value});
428 public int getEventsCount(int eventType, long startTimestamp, long endTimestamp, Group group) throws StorageBackendException {
429 log.log(Level.SEVERE, "TODO: getEventsCount {0} / {1} / {2} / {3}", new Object[]{eventType, startTimestamp, endTimestamp, group});
434 public double getEventsPerHour(int key, long gid) throws StorageBackendException {
435 log.log(Level.SEVERE, "TODO: getEventsPerHour {0} / {1}", new Object[]{key, gid});
440 public List<String> getGroupsForList(String listAddress) throws StorageBackendException {
441 log.log(Level.SEVERE, "TODO: getGroupsForList {0}", new Object[]{listAddress});
442 return Collections.emptyList();
446 public List<String> getListsForGroup(String groupname) throws StorageBackendException {
447 log.log(Level.SEVERE, "TODO: getListsForGroup {0}", new Object[]{groupname});
448 return Collections.emptyList();
452 public String getOldestArticle() throws StorageBackendException {
453 log.log(Level.SEVERE, "TODO: getOldestArticle");
458 public List<Subscription> getSubscriptions(int type) throws StorageBackendException {
459 log.log(Level.SEVERE, "TODO: getSubscriptions {0}", new Object[]{type});
460 return Collections.emptyList();
464 public void purgeGroup(Group group) throws StorageBackendException {
465 log.log(Level.SEVERE, "TODO: purgeGroup {0}", new Object[]{group});
469 public boolean update(Article article) throws StorageBackendException {
470 log.log(Level.SEVERE, "TODO: update {0}", new Object[]{article});
471 throw new StorageBackendException("Not implemented yet.");
475 public boolean update(Group group) throws StorageBackendException {
476 log.log(Level.SEVERE, "TODO: update {0}", new Object[]{group});
477 throw new StorageBackendException("Not implemented yet.");