src/org/sonews/storage/Article.java
author František Kučera <franta-hg@frantovo.cz>
Sun Nov 06 00:08:05 2011 +0100 (2011-11-06)
changeset 116 4ddc1020a154
parent 51 be419cf170d6
child 117 79ce65d63cce
permissions -rwxr-xr-x
Podpora Markdown syntaxe
na začátek odesílané zprávy stačí přidat:
#!markdown
a zpráva se pak prožene Markdown procesorem (spouštěno přes sudo pod jiným uživatelem)
a pak teprve přes standardní XSL transformaci.

1) Vytvořit uživatele a skupinu markdown

2) Do /etc/sudoers přidat:
Cmnd_Alias MARKDOWN = /usr/bin/markdown
%markdown ALL = (%markdown) NOPASSWD : MARKDOWN

3) Uivatele, pod kterým běží NNTP démon, přidat do skupiny markdown
     1 /*
     2  *   SONEWS News Server
     3  *   see AUTHORS for the list of contributors
     4  *
     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.
     9  *
    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.
    14  *
    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/>.
    17  */
    18 package org.sonews.storage;
    19 
    20 import java.io.ByteArrayInputStream;
    21 import java.io.ByteArrayOutputStream;
    22 import java.io.IOException;
    23 import java.security.MessageDigest;
    24 import java.security.NoSuchAlgorithmException;
    25 import java.util.UUID;
    26 import java.util.ArrayList;
    27 import java.util.Enumeration;
    28 import java.util.List;
    29 import java.util.logging.Level;
    30 import javax.mail.Header;
    31 import javax.mail.Message;
    32 import javax.mail.MessagingException;
    33 import javax.mail.internet.InternetHeaders;
    34 import org.sonews.config.Config;
    35 import org.sonews.util.Log;
    36 
    37 /**
    38  * Represents a newsgroup article.
    39  * @author Christian Lins
    40  * @author Denis Schwerdel
    41  * @since n3tpd/0.1
    42  */
    43 public class Article extends ArticleHead {
    44 	
    45 	private String authenticatedUser;
    46 
    47 	/**
    48 	 * Loads the Article identified by the given ID from the JDBCDatabase.
    49 	 * @param messageID
    50 	 * @return null if Article is not found or if an error occurred.
    51 	 */
    52 	public static Article getByMessageID(final String messageID) {
    53 		try {
    54 			return StorageManager.current().getArticle(messageID);
    55 		} catch (StorageBackendException ex) {
    56 			Log.get().log(Level.WARNING, ex.getLocalizedMessage(), ex);
    57 			return null;
    58 		}
    59 	}
    60 	private byte[] body = new byte[0];
    61 
    62 	/**
    63 	 * Default constructor.
    64 	 */
    65 	public Article() {
    66 	}
    67 
    68 	/**
    69 	 * Creates a new Article object using the date from the given
    70 	 * raw data.
    71 	 */
    72 	public Article(String headers, byte[] body) {
    73 		try {
    74 			this.body = body;
    75 
    76 			// Parse the header
    77 			this.headers = new InternetHeaders(
    78 					new ByteArrayInputStream(headers.getBytes()));
    79 
    80 			this.headerSrc = headers;
    81 		} catch (MessagingException ex) {
    82 			Log.get().log(Level.WARNING, ex.getLocalizedMessage(), ex);
    83 		}
    84 	}
    85 
    86 	/**
    87 	 * Creates an Article instance using the data from the javax.mail.Message
    88 	 * object. This constructor is called by the Mailinglist gateway.
    89 	 * @see javax.mail.Message
    90 	 * @param msg
    91 	 * @throws IOException
    92 	 * @throws MessagingException
    93 	 */
    94 	public Article(final Message msg)
    95 			throws IOException, MessagingException {
    96 		this.headers = new InternetHeaders();
    97 
    98 		for (Enumeration e = msg.getAllHeaders(); e.hasMoreElements();) {
    99 			final Header header = (Header) e.nextElement();
   100 			this.headers.addHeader(header.getName(), header.getValue());
   101 		}
   102 
   103 		// Reads the raw byte body using Message.writeTo(OutputStream out)
   104 		this.body = readContent(msg);
   105 
   106 		// Validate headers
   107 		validateHeaders();
   108 	}
   109 
   110 	/**
   111 	 * Reads from the given Message into a byte array.
   112 	 * @param in
   113 	 * @return
   114 	 * @throws IOException
   115 	 */
   116 	private byte[] readContent(Message in)
   117 			throws IOException, MessagingException {
   118 		ByteArrayOutputStream out = new ByteArrayOutputStream();
   119 		in.writeTo(out);
   120 		return out.toByteArray();
   121 	}
   122 
   123 	/**
   124 	 * Removes the header identified by the given key.
   125 	 * @param headerKey
   126 	 */
   127 	public void removeHeader(final String headerKey) {
   128 		this.headers.removeHeader(headerKey);
   129 		this.headerSrc = null;
   130 	}
   131 
   132 	/**
   133 	 * Generates a message id for this article and sets it into
   134 	 * the header object. You have to update the JDBCDatabase manually to make this
   135 	 * change persistent.
   136 	 * Note: a Message-ID should never be changed and only generated once.
   137 	 */
   138 	private String generateMessageID() {
   139 		String randomString;
   140 		MessageDigest md5;
   141 		try {
   142 			md5 = MessageDigest.getInstance("MD5");
   143 			md5.reset();
   144 			md5.update(getBody());
   145 			md5.update(getHeader(Headers.SUBJECT)[0].getBytes());
   146 			md5.update(getHeader(Headers.FROM)[0].getBytes());
   147 			byte[] result = md5.digest();
   148 			StringBuilder hexString = new StringBuilder();
   149 			for (int i = 0; i < result.length; i++) {
   150 				hexString.append(Integer.toHexString(0xFF & result[i]));
   151 			}
   152 			randomString = hexString.toString();
   153 		} catch (NoSuchAlgorithmException ex) {
   154 			Log.get().log(Level.WARNING, ex.getLocalizedMessage(), ex);
   155 			randomString = UUID.randomUUID().toString();
   156 		}
   157 		String msgID = "<" + randomString + "@"
   158 				+ Config.inst().get(Config.HOSTNAME, "localhost") + ">";
   159 
   160 		this.headers.setHeader(Headers.MESSAGE_ID, msgID);
   161 
   162 		return msgID;
   163 	}
   164 
   165 	/**
   166 	 * Returns the body string.
   167 	 */
   168 	public byte[] getBody() {
   169 		return body;
   170 	}
   171 
   172 	/**
   173 	 * @return Numerical IDs of the newsgroups this Article belongs to.
   174 	 */
   175 	public List<Group> getGroups() {
   176 		String[] groupnames = getHeader(Headers.NEWSGROUPS)[0].split(",");
   177 		ArrayList<Group> groups = new ArrayList<Group>();
   178 
   179 		try {
   180 			for (String newsgroup : groupnames) {
   181 				newsgroup = newsgroup.trim();
   182 				Group group = StorageManager.current().getGroup(newsgroup);
   183 				if (group != null && // If the server does not provide the group, ignore it
   184 						!groups.contains(group)) // Yes, there may be duplicates
   185 				{
   186 					groups.add(group);
   187 				}
   188 			}
   189 		} catch (StorageBackendException ex) {
   190 			Log.get().log(Level.WARNING, ex.getLocalizedMessage(), ex);
   191 			return null;
   192 		}
   193 		return groups;
   194 	}
   195 
   196 	public void setBody(byte[] body) {
   197 		this.body = body;
   198 	}
   199 
   200 	/**
   201 	 *
   202 	 * @param groupname Name(s) of newsgroups
   203 	 */
   204 	public void setGroup(String groupname) {
   205 		this.headers.setHeader(Headers.NEWSGROUPS, groupname);
   206 	}
   207 
   208 	/**
   209 	 * Returns the Message-ID of this Article. If the appropriate header
   210 	 * is empty, a new Message-ID is created.
   211 	 * @return Message-ID of this Article.
   212 	 */
   213 	public String getMessageID() {
   214 		String[] msgID = getHeader(Headers.MESSAGE_ID);
   215 		return msgID[0].equals("") ? generateMessageID() : msgID[0];
   216 	}
   217 
   218 	/**
   219 	 * @return String containing the Message-ID.
   220 	 */
   221 	@Override
   222 	public String toString() {
   223 		return getMessageID();
   224 	}
   225 
   226 	/**
   227 	 * @return username of currently logged user – or null, if user is not authenticated.
   228 	 */
   229 	public String getAuthenticatedUser() {
   230 		return authenticatedUser;
   231 	}
   232 	
   233 	/**
   234 	 * This method is to be called from POST Command implementation.
   235 	 * @param authenticatedUser current username – or null, if user is not authenticated.
   236 	 */
   237 	public void setAuthenticatedUser(String authenticatedUser) {
   238 		this.authenticatedUser = authenticatedUser;
   239 	}
   240 }