From d7429fe1376f25d0c4420c35c69dc9edd651ab25 Mon Sep 17 00:00:00 2001 From: zapata Date: Wed, 16 Apr 2003 03:26:44 +0000 Subject: [PATCH] - experimental opensessions - experimental media in comments support - first attempts of replacing cos - support for Beans by the freemarker generator --- etc/bundles/producer_en.properties | 1 + etc/open/donecomment.template | 37 + etc/open/dupecomment.template | 37 + etc/open/editcomment.template | 234 ++ etc/producer/article.template | 27 +- lib/commons-beanutils-1.6.1.jar | Bin 0 -> 118726 bytes lib/commons-fileupload-1.0-beta-1.jar | Bin 0 -> 15807 bytes lib/commons-logging-1.0.3.jar | Bin 0 -> 31605 bytes source/OpenMir.java | 9 +- source/default.properties | 12 +- source/mir/entity/adapter/EntityAdapterExc.java | 31 + source/mir/generator/FreemarkerGenerator.java | 57 +- .../mir/generator/GeneratorLibraryRepository.java | 31 + source/mir/module/AbstractModule.java | 20 + source/mir/producer/RSSProducerNode.java | 31 + source/mir/producer/SimpleProducerVerb.java | 31 + .../mir/producer/reader/ProducerConfigReader.java | 3 +- source/mir/servlet/ServletModule.java | 2 - source/mir/session/CommonsUploadedFileAdapter.java | 27 + source/mir/session/HTTPAdapters.java | 91 + source/mir/session/Request.java | 9 + source/mir/session/Response.java | 8 + source/mir/session/Session.java | 8 + source/mir/session/SessionExc.java | 40 + source/mir/session/SessionFailure.java | 45 + source/mir/session/SessionHandler.java | 5 + source/mir/session/SimpleResponse.java | 32 + source/mir/session/UploadedFile.java | 8 + source/mir/storage/Database.java | 4 +- source/mir/storage/store/StoreContainer.java | 2 +- source/mir/util/FileFunctions.java | 31 + source/mir/util/GeneratorIntegerFunctions.java | 31 + source/mir/util/HTTPParsedRequest.java | 159 ++ source/mir/util/HTTPRequestParser.java | 147 +- source/mir/util/InternetFunctions.java | 31 + source/mir/util/JDBCStringRoutines.java | 31 + source/mir/util/PropertiesManipulator.java | 31 + source/mir/util/SQLQueryBuilder.java | 31 + source/mir/util/StringRoutines.java | 423 ++-- source/mir/util/URLBuilder.java | 31 + source/mir/util/UtilExc.java | 40 + source/mir/util/UtilFailure.java | 45 + source/mircoders/entity/EntityMedia.java | 152 +- source/mircoders/global/Abuse.java | 1254 +++++----- source/mircoders/localizer/MirLocalizer.java | 1 - .../localizer/MirOpenPostingLocalizer.java | 125 +- .../MirBasicCommentPostingSessionHandler.java | 307 +++ .../localizer/basic/MirBasicLocalizer.java | 6 +- .../basic/MirBasicOpenPostingLocalizer.java | 211 +- source/mircoders/media/MediaUploadProcessor.java | 115 + .../mircoders/media/UnsupportedMediaFormatExc.java | 31 + source/mircoders/module/ModuleContent.java | 166 +- source/mircoders/module/ModuleMediaType.java | 72 + source/mircoders/module/ModuleUploadedMedia.java | 105 +- source/mircoders/servlet/ServletHelper.java | 33 +- source/mircoders/servlet/ServletModuleAbuse.java | 309 +-- .../mircoders/servlet/ServletModuleOpenIndy.java | 2450 +++++++++++--------- source/tool/BundleTool.java | 31 + templates/admin/FUNCTIONS.template | 50 +- templates/admin/abuse.filters.template | 2 +- templates/admin/abuse.template | 77 +- templates/admin/articletypelist.template | 12 +- templates/admin/audio.template | 4 +- templates/admin/comment.template | 59 +- templates/admin/content.template | 91 +- templates/admin/image.template | 4 +- templates/admin/media.template | 4 +- templates/admin/mediafolder.template | 4 +- templates/admin/message.template | 2 +- templates/admin/topic.template | 2 +- templates/admin/video.template | 10 +- 71 files changed, 4732 insertions(+), 2830 deletions(-) create mode 100755 etc/open/donecomment.template create mode 100755 etc/open/dupecomment.template create mode 100755 etc/open/editcomment.template create mode 100755 lib/commons-beanutils-1.6.1.jar create mode 100755 lib/commons-fileupload-1.0-beta-1.jar create mode 100755 lib/commons-logging-1.0.3.jar create mode 100755 source/mir/session/CommonsUploadedFileAdapter.java create mode 100755 source/mir/session/HTTPAdapters.java create mode 100755 source/mir/session/Request.java create mode 100755 source/mir/session/Response.java create mode 100755 source/mir/session/Session.java create mode 100755 source/mir/session/SessionExc.java create mode 100755 source/mir/session/SessionFailure.java create mode 100755 source/mir/session/SessionHandler.java create mode 100755 source/mir/session/SimpleResponse.java create mode 100755 source/mir/session/UploadedFile.java create mode 100755 source/mir/util/HTTPParsedRequest.java create mode 100755 source/mir/util/UtilExc.java create mode 100755 source/mir/util/UtilFailure.java create mode 100755 source/mircoders/localizer/basic/MirBasicCommentPostingSessionHandler.java create mode 100755 source/mircoders/media/MediaUploadProcessor.java create mode 100755 source/mircoders/module/ModuleMediaType.java diff --git a/etc/bundles/producer_en.properties b/etc/bundles/producer_en.properties index 737e5dcb..aae675ff 100755 --- a/etc/bundles/producer_en.properties +++ b/etc/bundles/producer_en.properties @@ -40,6 +40,7 @@ article.homepage.prefix = Homepage: article.addcomment = Make a quick comment on this article article.send_as_email = Email this article to someone article.get_as_pdf = Download this article in pdf format +article.add_to_pdf = Add this article to your pdf newsletter selection newswirearchive.title = Newswire archive featurearchive.title = Feature archive diff --git a/etc/open/donecomment.template b/etc/open/donecomment.template new file mode 100755 index 00000000..bf9d1ed4 --- /dev/null +++ b/etc/open/donecomment.template @@ -0,0 +1,37 @@ + + + + + ${lang("commentdone.htmltitle")} + + + + + + + + + + + + + +
+ ${lang("commentdone.thanks")} +
+ +
+ ${lang("commentdone.wait")} +
+ ${lang("commentdone.criteria")} +
+ ${lang("commentdone.stay_calm")}
+
+ +
+ >> ${lang("commentdone.back")} +
+ + + diff --git a/etc/open/dupecomment.template b/etc/open/dupecomment.template new file mode 100755 index 00000000..2e9ce66e --- /dev/null +++ b/etc/open/dupecomment.template @@ -0,0 +1,37 @@ + + + + + ${lang("commentdupe.htmltitle")} + + + + + + + + + + + + + +
+ + ${lang("commentdupe.title")} + +
+ +
+ ${lang("commentdupe.explanation")} +
+
${lang("commentdupe.no_panic")}

+
+
+
+ >> ${lang("commentdupe.back")} +
+ + + diff --git a/etc/open/editcomment.template b/etc/open/editcomment.template new file mode 100755 index 00000000..54140176 --- /dev/null +++ b/etc/open/editcomment.template @@ -0,0 +1,234 @@ + + + + ${lang("comment.htmltitle")} + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ Your input had the following errors:

+ + + + ${lang("comment."+e.field)}: + + ${lang(e.message)}
+
+
+
  + ${lang("comment.note")} +
${data.passwd}
+
+
 
+ ${lang("comment.password")}: +  
+ + ${lang("comment.formtitle")} +  
 
  + R E Q U I R E D information +  
 
+ ${lang("comment.title")}: + + +
+ ${lang("comment.text")}: + + +
+ ${lang("comment.name")}:

+
+ +

+
  + Additional information +  
 
+ ${lang("comment.language")}:

+
+ +

+
 
  + Contact information +  
 
  + ${lang("comment.contact.info")} +  
+ ${lang("comment.email")}: + + +  
+ ${lang("comment.url")}: + + +  
+ ${lang("comment.address")}: + + +  
+ ${lang("comment.phone")}

+
+

+
 
+ ${lang("posting.media.title")} 1: + +
+
${lang("posting.media.media")} 1 + +
+ ${lang("posting.media.title")} 2: + +
+
${lang("posting.media.media")} 2 + +
+ ${lang("posting.media.title")} 3: + +
+
${lang("posting.media.media")} 3 + +
  + +  
 

+
+ + diff --git a/etc/producer/article.template b/etc/producer/article.template index 83f90d8d..77aaf970 100755 --- a/etc/producer/article.template +++ b/etc/producer/article.template @@ -162,7 +162,7 @@
-   +   ${lang("article.addcomment")}>>
@@ -171,6 +171,31 @@
${utility.encodeHTML(c.title)}
${c.creationdate.formatted["dd.MM.yyyy HH:mm"]}
+ + + + ${media["title"]} - ${media["media_descr"]} ${media["human_readable_size"]} + + + + + + ${media["title"]} - ${media["media_descr"]} ${media["human_readable_size"]} + + + + + ${media["title"]} - ${media["media_descr"]} ${media["human_readable_size"]} + + + + ${image[ +
${image["title"]} +
+ + ${c.description_parsed} diff --git a/lib/commons-beanutils-1.6.1.jar b/lib/commons-beanutils-1.6.1.jar new file mode 100755 index 0000000000000000000000000000000000000000..795655a66ad6497fb7721403f5fc1aa0aa03f313 GIT binary patch literal 118726 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
  • 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 literal 0 HcmV?d00001 diff --git a/lib/commons-logging-1.0.3.jar b/lib/commons-logging-1.0.3.jar new file mode 100755 index 0000000000000000000000000000000000000000..b99c9375a488e556208556e44230d7983d0fac50 GIT binary patch literal 31605 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 && ((String) parts.get(0)).trim().toLowerCase().equals(MULTIPART_FORMDATA_CONTENTTYPE)) { + parseMultipartRequest(); + } + } + catch (Throwable t) { + t.printStackTrace(); + + throw new UtilFailure(t); + } + } + + protected void parseMultipartRequest() throws UtilExc, UtilFailure { + try { + FileUpload upload = new FileUpload(); + + upload.setSizeMax(maxUploadSize); + upload.setSizeThreshold(4096); + upload.setRepositoryPath(tempDir); + + List items = upload.parseRequest(request); + + Iterator i = items.iterator(); + while (i.hasNext()) { + FileItem item = (FileItem) i.next(); + + if (item.isFormField()) { + if (!stringValues.containsKey(item.getName())) { + stringValues.put(item.getFieldName(), item.getString()); + } + + List listValue = (List) listValues.get(item.getFieldName()); + if (listValue == null) { + listValue = new Vector(); + listValues.put(item.getFieldName(), listValue); + } + listValue.add(item.getString()); + } + else { + if (item.getSize()>0) + files.add(item); + } + } + } + catch (Throwable t) { + throw new UtilFailure(t); + } + } +} diff --git a/source/mir/util/HTTPRequestParser.java b/source/mir/util/HTTPRequestParser.java index 20bfd910..043dc7c1 100755 --- a/source/mir/util/HTTPRequestParser.java +++ b/source/mir/util/HTTPRequestParser.java @@ -1,58 +1,89 @@ -package mir.util; - -import javax.servlet.http.HttpServletRequest; - -public class HTTPRequestParser { - private HttpServletRequest request; - private String encoding; - - public HTTPRequestParser(HttpServletRequest aRequest) { - this(aRequest, aRequest.getCharacterEncoding()); - } - - public HTTPRequestParser(HttpServletRequest aRequest, String anEncoding) { - request = aRequest; - encoding = anEncoding; - } - - public boolean hasParameter(String aName) { - return request.getParameter(aName)!=null; - } - - public String getParameterWithDefault(String aName, String aDefault) { - if (hasParameter(aName)) - return getParameter(aName); - else - return aDefault; - } - - public String getParameter(String aName) { - try { - String result = request.getParameter(aName); - String requestEncoding = request.getCharacterEncoding(); - if (requestEncoding==null) - requestEncoding = "ISO-8859-1"; - - if (result != null && encoding!=null && !encoding.equals(requestEncoding)) { - result = new String(result.getBytes(requestEncoding), encoding); - } - - return result; - } - catch (Throwable t) { - throw new RuntimeException("HTTPRequestParser.getParameter: " + t.getMessage()); - } - } - - public int getIntegerWithDefault(String aName, int aDefault) { - int result = aDefault; - String value = getParameter(aName); - - try { - result = Integer.parseInt(value); - } - catch (Throwable t) { - } - return result; - } -} \ No newline at end of file +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mir.util; + +import javax.servlet.http.HttpServletRequest; + +public class HTTPRequestParser { + private HttpServletRequest request; + private String encoding; + + public HTTPRequestParser(HttpServletRequest aRequest) { + this(aRequest, aRequest.getCharacterEncoding()); + } + + public HTTPRequestParser(HttpServletRequest aRequest, String anEncoding) { + request = aRequest; + encoding = anEncoding; + } + + public boolean hasParameter(String aName) { + return request.getParameter(aName)!=null; + } + + public String getParameterWithDefault(String aName, String aDefault) { + if (hasParameter(aName)) + return getParameter(aName); + else + return aDefault; + } + + public String getParameter(String aName) { + try { + String result = request.getParameter(aName); + String requestEncoding = request.getCharacterEncoding(); + if (requestEncoding==null) + requestEncoding = "ISO-8859-1"; + + if (result != null && encoding!=null && !encoding.equals(requestEncoding)) { + result = new String(result.getBytes(requestEncoding), encoding); + } + + return result; + } + catch (Throwable t) { + throw new RuntimeException("HTTPRequestParser.getParameter: " + t.getMessage()); + } + } + + public int getIntegerWithDefault(String aName, int aDefault) { + int result = aDefault; + String value = getParameter(aName); + + try { + result = Integer.parseInt(value); + } + catch (Throwable t) { + } + return result; + } +} diff --git a/source/mir/util/InternetFunctions.java b/source/mir/util/InternetFunctions.java index 5193daa0..171e8f89 100755 --- a/source/mir/util/InternetFunctions.java +++ b/source/mir/util/InternetFunctions.java @@ -1,3 +1,34 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + package mir.util; import java.util.List; diff --git a/source/mir/util/JDBCStringRoutines.java b/source/mir/util/JDBCStringRoutines.java index 0e273f2a..a039d109 100755 --- a/source/mir/util/JDBCStringRoutines.java +++ b/source/mir/util/JDBCStringRoutines.java @@ -1,3 +1,34 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + package mir.util; /** diff --git a/source/mir/util/PropertiesManipulator.java b/source/mir/util/PropertiesManipulator.java index a60a8ca3..194f31b6 100755 --- a/source/mir/util/PropertiesManipulator.java +++ b/source/mir/util/PropertiesManipulator.java @@ -1,3 +1,34 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + package mir.util; import java.io.InputStream; diff --git a/source/mir/util/SQLQueryBuilder.java b/source/mir/util/SQLQueryBuilder.java index eef1844b..dcf3a2a5 100755 --- a/source/mir/util/SQLQueryBuilder.java +++ b/source/mir/util/SQLQueryBuilder.java @@ -1,3 +1,34 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + package mir.util; public class SQLQueryBuilder { diff --git a/source/mir/util/StringRoutines.java b/source/mir/util/StringRoutines.java index 4c58d6a0..579e9211 100755 --- a/source/mir/util/StringRoutines.java +++ b/source/mir/util/StringRoutines.java @@ -1,212 +1,213 @@ -/* - * Copyright (C) 2001, 2002 The Mir-coders group - * - * This file is part of Mir. - * - * Mir is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Mir is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Mir; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * In addition, as a special exception, The Mir-coders gives permission to link - * the code of this program with the com.oreilly.servlet library, any library - * licensed under the Apache Software License, The Sun (tm) Java Advanced - * Imaging library (JAI), The Sun JIMI library (or with modified versions of - * the above that use the same license as the above), and distribute linked - * combinations including the two. You must obey the GNU General Public - * License in all respects for all of the code used other than the above - * mentioned libraries. If you modify this file, you may extend this exception - * to your version of the file, but you are not obligated to do so. If you do - * not wish to do so, delete this exception statement from your version. - */ - -package mir.util; - -import gnu.regexp.RE; -import gnu.regexp.REException; - -import java.util.List; -import java.util.Vector; - -public class StringRoutines { - - private StringRoutines() { - } - - static int indexOfCharacters(String aString, char[] aCharacters, int aFrom) { - int i; - int result=-1; - int position; - - for (i=0; iseperateString("a:b:c", ":"); will lead to - * a List with 3 Strings: "a", "b" and "c" - * - * @param aString The string to split - * @param aSeparator - * @return - */ - - public static List splitString(String aString, String aSeparator) { - List result= new Vector(); - int previousPosition = 0; - int position; - int endOfNamePosition; - - while ((position = aString.indexOf(aSeparator, previousPosition))>=0) { - result.add(aString.substring(previousPosition, position)); - previousPosition = position + aSeparator.length(); - } - - result.add(aString.substring(previousPosition, aString.length())); - - return result; - } - - /** - * Separates a String into at most 2 parts based on a separator: - *
      - *
    • - * seperateString("a:b:c", ":"); will lead to - * a List with 2 Strings: "a" and "b:c" - *
    • - * seperateString("abc", ":"); will lead to - * a List with a single String: "abc" - *
    - * - * - * @param aString - * @param aSeparator - * @return - */ - public static List separateString(String aString, String aSeparator) { - List result= new Vector(); - int previousPosition = 0; - int position; - int endOfNamePosition; - - if((position = aString.indexOf(aSeparator, previousPosition))>=0) { - result.add(aString.substring(previousPosition, position)); - previousPosition = position + aSeparator.length(); - } - - result.add(aString.substring(previousPosition, aString.length())); - - return result; - } +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mir.util; + +import gnu.regexp.RE; +import gnu.regexp.REException; + +import java.util.List; +import java.util.Vector; + +public class StringRoutines { + + private StringRoutines() { + } + + static int indexOfCharacters(String aString, char[] aCharacters, int aFrom) { + int i; + int result=-1; + int position; + + for (i=0; iseperateString("a:b:c", ":"); will lead to + * a List with 3 Strings: "a", "b" and "c" + * + * @param aString The string to split + * @param aSeparator + * @return + */ + + public static List splitString(String aString, String aSeparator) { + List result= new Vector(); + int previousPosition = 0; + int position; + int endOfNamePosition; + + if (aString!=null) { + while ( (position = aString.indexOf(aSeparator, previousPosition)) >= 0) { + result.add(aString.substring(previousPosition, position)); + previousPosition = position + aSeparator.length(); + } + result.add(aString.substring(previousPosition, aString.length())); + } + + return result; + } + + /** + * Separates a String into at most 2 parts based on a separator: + *
      + *
    • + * seperateString("a:b:c", ":"); will lead to + * a List with 2 Strings: "a" and "b:c" + *
    • + * seperateString("abc", ":"); will lead to + * a List with a single String: "abc" + *
    + * + * + * @param aString + * @param aSeparator + * @return + */ + public static List separateString(String aString, String aSeparator) { + List result= new Vector(); + int previousPosition = 0; + int position; + int endOfNamePosition; + + if((position = aString.indexOf(aSeparator, previousPosition))>=0) { + result.add(aString.substring(previousPosition, position)); + previousPosition = position + aSeparator.length(); + } + + result.add(aString.substring(previousPosition, aString.length())); + + return result; + } } \ No newline at end of file diff --git a/source/mir/util/URLBuilder.java b/source/mir/util/URLBuilder.java index f64fb904..c36cdf9e 100755 --- a/source/mir/util/URLBuilder.java +++ b/source/mir/util/URLBuilder.java @@ -1,3 +1,34 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + package mir.util; import java.util.HashMap; diff --git a/source/mir/util/UtilExc.java b/source/mir/util/UtilExc.java new file mode 100755 index 00000000..7810effd --- /dev/null +++ b/source/mir/util/UtilExc.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mir.util; + +import multex.Exc; + +public class UtilExc extends Exc { + public UtilExc(String aMessage) { + super(aMessage); + } +} diff --git a/source/mir/util/UtilFailure.java b/source/mir/util/UtilFailure.java new file mode 100755 index 00000000..99902962 --- /dev/null +++ b/source/mir/util/UtilFailure.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mir.util; + +import multex.Failure; + +public class UtilFailure extends Failure { + + public UtilFailure(String aMessage, Throwable aCause) { + super (aMessage, aCause); + } + + public UtilFailure(Throwable aCause) { + this (aCause.getMessage(), aCause); + } +} diff --git a/source/mircoders/entity/EntityMedia.java b/source/mircoders/entity/EntityMedia.java index cf8f801b..0784743e 100755 --- a/source/mircoders/entity/EntityMedia.java +++ b/source/mircoders/entity/EntityMedia.java @@ -1,75 +1,77 @@ -/* - * Copyright (C) 2001, 2002 The Mir-coders group - * - * This file is part of Mir. - * - * Mir is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Mir is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Mir; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * In addition, as a special exception, The Mir-coders gives permission to link - * the code of this program with the com.oreilly.servlet library, any library - * licensed under the Apache Software License, The Sun (tm) Java Advanced - * Imaging library (JAI), The Sun JIMI library (or with modified versions of - * the above that use the same license as the above), and distribute linked - * combinations including the two. You must obey the GNU General Public - * License in all respects for all of the code used other than the above - * mentioned libraries. If you modify this file, you may extend this exception - * to your version of the file, but you are not obligated to do so. If you do - * not wish to do so, delete this exception statement from your version. - */ - -package mircoders.entity; - -import mir.entity.Entity; -import mir.storage.StorageObject; -import mir.storage.StorageObjectExc; -import mir.storage.StorageObjectFailure; -import mircoders.storage.DatabaseMedia; -/** - * Diese Klasse enth?lt die Daten eines MetaObjekts - * - * @author RK - * @version 29.6.1999 - */ - - -public class EntityMedia extends Entity -{ - - public EntityMedia(){ - super(); - } - - public EntityMedia(StorageObject theStorage) - { - this(); - setStorage(theStorage); - } - - /** - * fetches the MediaType entry assiciated w/ this media - * - * @return mir.entity.Entity - */ - public Entity getMediaType() throws StorageObjectFailure { - try { - return ((DatabaseMedia)theStorageObject).getMediaType(this); - } catch (StorageObjectFailure e) { - throw new StorageObjectFailure("getMediaType(): ",e); - } catch (StorageObjectExc e) { - throw new StorageObjectFailure("getMediaType(): ",e); - } - - } -} +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mircoders.entity; + +import mir.entity.Entity; +import mir.storage.StorageObject; +import mir.storage.StorageObjectExc; +import mir.storage.StorageObjectFailure; +import mircoders.storage.DatabaseMedia; +/** + * Diese Klasse enth?lt die Daten eines MetaObjekts + * + * @author RK + * @version 29.6.1999 + */ + + +public class EntityMedia extends Entity +{ + + public EntityMedia(){ + super(); + } + + public EntityMedia(StorageObject theStorage) + { + this(); + setStorage(theStorage); + } + + /** + * fetches the MediaType entry assiciated w/ this media + * + * @return mir.entity.Entity + */ + public Entity getMediaType() throws StorageObjectFailure { + try { + return ( (DatabaseMedia) theStorageObject).getMediaType(this); + } + catch (StorageObjectFailure e) { + throw new StorageObjectFailure("getMediaType(): ", e); + } + catch (StorageObjectExc e) { + throw new StorageObjectFailure("getMediaType(): ", e); + } + + } +} diff --git a/source/mircoders/global/Abuse.java b/source/mircoders/global/Abuse.java index 0d2e630f..b3e18550 100755 --- a/source/mircoders/global/Abuse.java +++ b/source/mircoders/global/Abuse.java @@ -1,601 +1,655 @@ -package mircoders.global; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Vector; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.collections.ExtendedProperties; - -import gnu.regexp.RE; - -import mir.entity.Entity; -import mir.log.LoggerWrapper; -import mir.util.DateToMapAdapter; -import mir.util.InternetFunctions; -import mir.util.StringRoutines; -import mircoders.entity.EntityComment; -import mircoders.entity.EntityContent; -import mircoders.localizer.MirAdminInterfaceLocalizer; - - -public class Abuse { - private List filters; - private int maxIdentifier; - private LoggerWrapper logger; - private int logSize; - private boolean logEnabled; - private boolean openPostingDisabled; - private boolean openPostingPassword; - private boolean cookieOnBlock; - private String articleBlockAction; - private String commentBlockAction; - private List log; - private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config"); - - - private static final String IP_FILTER_TYPE="ip"; - private static final String REGEXP_FILTER_TYPE="regexp"; - private static String cookieName=MirGlobal.config().getString("Abuse.CookieName"); - private static int cookieMaxAge = 60*60*MirGlobal.config().getInt("Abuse.CookieMaxAge"); - - public Abuse() { - logger = new LoggerWrapper("Global.Abuse"); - filters = new Vector(); - maxIdentifier = 0; - log = new Vector(); - - logSize = 100; - logEnabled = false; - articleBlockAction = ""; - commentBlockAction = ""; - openPostingPassword = false; - openPostingDisabled = false; - cookieOnBlock = false; - - load(); - } - - public boolean checkIpFilter(String anIpAddress) { - synchronized (filters) { - Iterator i = filters.iterator(); - - while (i.hasNext()) { - Filter filter = (Filter) i.next(); - - try { - if ( (filter.getType().equals(IP_FILTER_TYPE)) && - InternetFunctions.isIpAddressInNetwork(anIpAddress, filter.getExpression())) { - logger.debug("ip match on " + filter.getExpression()); - return true; - } - } - catch (Throwable t) { - logger.warn("error while checking ip address " + anIpAddress + " over network " + filter.expression + ": " + t.getMessage()); - } - } - - return false; - } - } - - private boolean checkRegExpFilter(Entity anEntity) { - synchronized (filters) { - Iterator i = filters.iterator(); - - while (i.hasNext()) { - Filter filter = (Filter) i.next(); - - if (filter.getType().equals(REGEXP_FILTER_TYPE)) { - try { - RE regularExpression = new RE(filter.getExpression()); - - Iterator j = anEntity.getFields().iterator(); - while (j.hasNext()) { - String field = anEntity.getValue( (String) j.next()); - - if (field != null && regularExpression.isMatch(field.toLowerCase())) { - logger.debug("regexp match on " + filter.getExpression()); - return true; - } - } - } - catch (Throwable t) { - logger.warn("error while checking entity with regexp " + filter.getExpression() + ": " + t.getMessage()); - } - } - } - - return false; - } - } - - private void setCookie(HttpServletResponse aResponse) { - Random random = new Random(); - - Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000))); - cookie.setMaxAge(cookieMaxAge); - cookie.setPath("/"); - aResponse.addCookie(cookie); - } - - private boolean checkCookie(List aCookies) { - if (getCookieOnBlock()) { - Iterator i = aCookies.iterator(); - - while (i.hasNext()) { - Cookie cookie = (Cookie) i.next(); - - if (cookie.getName().equals(cookieName)) { - logger.debug("cookie match"); - return true; - } - } - } - - return false; - } - - public void checkComment(EntityComment aComment, HttpServletRequest aRequest, HttpServletResponse aResponse) { - try { - long time = System.currentTimeMillis(); - - MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction); - - if (checkCookie(Arrays.asList(aRequest.getCookies())) || checkIpFilter(aRequest.getRemoteAddr()) || checkRegExpFilter(aComment)) { - operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("comment", aComment)); - setCookie(aResponse); - } - - logger.info("checkComment: " + (System.currentTimeMillis()-time) + "ms"); - - } - catch (Throwable t) { - logger.error("Abuse.checkComment: " + t.toString()); - } - } - - public void checkArticle(EntityContent anArticle, HttpServletRequest aRequest, HttpServletResponse aResponse) { - try { - long time = System.currentTimeMillis(); - - MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction); - - if (checkCookie(Arrays.asList(aRequest.getCookies())) || checkIpFilter(aRequest.getRemoteAddr()) || checkRegExpFilter(anArticle)) { - operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("content", anArticle)); - setCookie(aResponse); - } - - logger.info("checkArticle: " + (System.currentTimeMillis()-time) + "ms"); - } - catch (Throwable t) { - logger.error("Abuse.checkArticle: " + t.toString()); - } - } - - public boolean getLogEnabled() { - return logEnabled; - } - - public void setLogEnabled(boolean anEnabled) { - logEnabled = anEnabled; - truncateLog(); - } - - public int getLogSize() { - return logSize; - } - - public void setLogSize(int aSize) { - logSize = aSize; - truncateLog(); - } - - public boolean getOpenPostingDisabled() { - return openPostingDisabled; - } - - public void setOpenPostingDisabled(boolean anOpenPostingDisabled) { - openPostingDisabled = anOpenPostingDisabled; - } - - public boolean getOpenPostingPassword() { - return openPostingPassword; - } - - public void setOpenPostingPassword(boolean anOpenPostingPassword) { - openPostingPassword = anOpenPostingPassword; - } - - public boolean getCookieOnBlock() { - return cookieOnBlock; - } - - public void setCookieOnBlock(boolean aCookieOnBlock) { - cookieOnBlock = aCookieOnBlock; - } - - public String getArticleBlockAction() { - return articleBlockAction; - } - - public void setArticleBlockAction(String anAction) { - articleBlockAction = anAction; - } - - public String getCommentBlockAction() { - return commentBlockAction; - } - - public void setCommentBlockAction(String anAction) { - commentBlockAction = anAction; - } - - - public List getLog() { - synchronized(log) { - List result = new Vector(); - - Iterator i = log.iterator(); - while (i.hasNext()) { - LogEntry logEntry = (LogEntry) i.next(); - Map entry = new HashMap(); - - entry.put("ip", logEntry.getIpNumber()); - entry.put("id", logEntry.getId()); - entry.put("timestamp", new DateToMapAdapter(logEntry.getTimeStamp())); - if (logEntry.getIsArticle()) - entry.put("type", "content"); - else - entry.put("type", "comment"); - entry.put("browser", logEntry.getBrowserString()); - - result.add(entry); - } - - return result; - } - } - - public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) { - appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false)); - } - - public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) { - appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true)); - } - - public void load() { - try { - ExtendedProperties configuration = new ExtendedProperties(); - - try { - configuration = new ExtendedProperties(configFile); - } - catch (FileNotFoundException e) { - } - - getFilterConfig(filters, "abuse.filter", configuration); - - setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1")); - setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1")); - setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1")); - setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1")); - setLogSize(configuration.getInt("abuse.logSize", 10)); - setArticleBlockAction(configuration.getString("abuse.articleBlockAction", "")); - setCommentBlockAction(configuration.getString("abuse.commentBlockAction", "")); - } - catch (Throwable t) { - throw new RuntimeException(t.toString()); - } - } - public void save() { - try { - ExtendedProperties configuration = new ExtendedProperties(); - - setFilterConfig(filters, "abuse.filter", configuration); - - configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled()?"1":"0"); - configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword()?"1":"0"); - configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock()?"1":"0"); - configuration.addProperty("abuse.logEnabled", getLogEnabled()?"1":"0"); - configuration.addProperty("abuse.logSize", Integer.toString(getLogSize())); - configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction()); - configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction()); - - configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration"); - } - catch (Throwable t) { - throw new RuntimeException(t.toString()); - } - } - - public List getFilterTypes() { - List result = new Vector(); - - Map entry = new HashMap(); - entry.put("resource", "abuse.filtertype.ip"); - entry.put("id", IP_FILTER_TYPE); - result.add(entry); - - entry = new HashMap(); - entry.put("resource", "abuse.filtertype.regexp"); - entry.put("id", REGEXP_FILTER_TYPE); - result.add(entry); - - return result; - } - - public List getArticleActions() { - try { - List result = new Vector(); - - Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator(); - while (i.hasNext()) { - MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = - (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next(); - - Map action = new HashMap(); - action.put("resource", "content.operation."+operation.getName()); - action.put("identifier", operation.getName()); - - result.add(action); - } - - return result; - } - catch (Throwable t) { - throw new RuntimeException("can't get article actions"); - } - } - - public List getCommentActions() { - try { - List result = new Vector(); - - Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator(); - while (i.hasNext()) { - MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = - (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next(); - - Map action = new HashMap(); - action.put("resource", "comment.operation."+operation.getName()); - action.put("identifier", operation.getName()); - - result.add(action); - } - - return result; - } - catch (Throwable t) { - throw new RuntimeException("can't get comment actions"); - } - } - - public List getFilters() { - return getFiltersAsMaps(filters); - } - - public void addFilter(String aType, String anExpression) { - addFilter(filters, aType, anExpression); - } - - public void setFilter(String anIdentifier, String aType, String anExpression) { - setFilter(filters, anIdentifier, aType, anExpression); - } - - public void deleteFilter(String anIdentifier) { - deleteFilter(filters, anIdentifier); - } - - public void validateIpFilter(String anIdentifier, String anArticleAction, String aCommentAction) throws Exception { - } - - private List getFiltersAsMaps(List aFilters) { - synchronized(aFilters) { - List result = new Vector(); - - Iterator i = aFilters.iterator(); - while (i.hasNext()) { - Filter filter = (Filter) i.next(); - Map map = new HashMap(); - - map.put("id", filter.getId()); - map.put("expression", filter.getExpression()); - map.put("type", filter.getType()); - - result.add(map); - } - return result; - } - } - - private void addFilter(List aFilters, String aType, String anExpression) { - Filter filter = new Filter(); - - filter.setId(generateId()); - filter.setExpression(anExpression); - filter.setType(aType); - - synchronized (aFilters) { - aFilters.add(filter); - } - } - - private void setFilter(List aFilters, String anIdentifier, String aType, String anExpression) { - synchronized (aFilters) { - Filter filter = findFilter(aFilters, anIdentifier); - - if (filter!=null) { - filter.setExpression(anExpression); - filter.setType(aType); - } - } - } - - private Filter findFilter(List aFilters, String anIdentifier) { - synchronized (aFilters) { - Iterator i = aFilters.iterator(); - while (i.hasNext()) { - Filter filter = (Filter) i.next(); - - if (filter.getId().equals(anIdentifier)) { - return filter; - } - } - } - - return null; - } - - private void deleteFilter(List aFilters, String anIdentifier) { - synchronized (aFilters) { - Filter filter = findFilter(aFilters, anIdentifier); - - if (filter!=null) { - aFilters.remove(filter); - } - } - } - - private String generateId() { - synchronized(this) { - maxIdentifier = maxIdentifier+1; - - return Integer.toString(maxIdentifier); - } - } - - private static class Filter { - private String identifier; - private String expression; - private String type; - - public Filter() { - expression=""; - type=""; - identifier=""; - } - - public String getId() { - return identifier; - } - - public void setId(String anId) { - identifier = anId; - } - - public String getExpression() { - return expression; - } - - public void setExpression(String anExpression) { - expression = anExpression; - } - - public String getType() { - return type; - } - - public void setType(String aType) { - type = aType; - } - } - - private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) { - synchronized(aFilters) { - Iterator i = aFilters.iterator(); - - while (i.hasNext()) { - Filter filter = (Filter) i.next(); - - aConfiguration.addProperty(aConfigKey, filter.getType()+":"+filter.getExpression()); - } - } - } - - private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) { - synchronized(aFilters) { - aFilters.clear(); - - Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).iterator(); - - while (i.hasNext()) { - String filter = (String) i.next(); - List parts = StringRoutines.separateString(filter, ":"); - - if (parts.size()==2) { - addFilter( (String) parts.get(0), (String) parts.get(1)); - } - } - } - } - - private static class LogEntry { - private String ipNumber; - private String browserString; - private String id; - private Date timeStamp; - private boolean isArticle; - - public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) { - ipNumber = anIpNumber; - browserString = aBrowserString; - id = anId; - isArticle = anIsArticle; - timeStamp=aTimeStamp; - } - - public String getIpNumber() { - return ipNumber; - } - - public String getBrowserString() { - return browserString; - } - - public String getId() { - return id; - } - - public Date getTimeStamp() { - return timeStamp; - } - - public boolean getIsArticle() { - return isArticle; - } - } - - private void truncateLog() { - synchronized(log) { - if (!logEnabled) - log.clear(); - else { - while (log.size()>0 && log.size()>logSize) { - log.remove(0); - } - } - } - }; - - private void appendLog(LogEntry anEntry) { - synchronized (log) { - if (logEnabled) { - log.add(anEntry); - truncateLog(); - } - } - } - +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mircoders.global; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Vector; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.collections.ExtendedProperties; + +import gnu.regexp.RE; + +import mir.entity.Entity; +import mir.log.LoggerWrapper; +import mir.util.DateToMapAdapter; +import mir.util.InternetFunctions; +import mir.util.StringRoutines; +import mir.session.*; +import mircoders.entity.EntityComment; +import mircoders.entity.EntityContent; +import mircoders.localizer.MirAdminInterfaceLocalizer; + + +public class Abuse { + private List filters; + private int maxIdentifier; + private LoggerWrapper logger; + private int logSize; + private boolean logEnabled; + private boolean openPostingDisabled; + private boolean openPostingPassword; + private boolean cookieOnBlock; + private String articleBlockAction; + private String commentBlockAction; + private List log; + private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config"); + + + private static final String IP_FILTER_TYPE="ip"; + private static final String REGEXP_FILTER_TYPE="regexp"; + private static String cookieName=MirGlobal.config().getString("Abuse.CookieName"); + private static int cookieMaxAge = 60*60*MirGlobal.config().getInt("Abuse.CookieMaxAge"); + + public Abuse() { + logger = new LoggerWrapper("Global.Abuse"); + filters = new Vector(); + maxIdentifier = 0; + log = new Vector(); + + logSize = 100; + logEnabled = false; + articleBlockAction = ""; + commentBlockAction = ""; + openPostingPassword = false; + openPostingDisabled = false; + cookieOnBlock = false; + + load(); + } + + public boolean checkIpFilter(String anIpAddress) { + synchronized (filters) { + Iterator i = filters.iterator(); + + while (i.hasNext()) { + Filter filter = (Filter) i.next(); + + try { + if ( (filter.getType().equals(IP_FILTER_TYPE)) && + InternetFunctions.isIpAddressInNetwork(anIpAddress, filter.getExpression())) { + logger.debug("ip match on " + filter.getExpression()); + return true; + } + } + catch (Throwable t) { + logger.warn("error while checking ip address " + anIpAddress + " over network " + filter.expression + ": " + t.getMessage()); + } + } + + return false; + } + } + + private boolean checkRegExpFilter(Entity anEntity) { + synchronized (filters) { + Iterator i = filters.iterator(); + + while (i.hasNext()) { + Filter filter = (Filter) i.next(); + + if (filter.getType().equals(REGEXP_FILTER_TYPE)) { + try { + RE regularExpression = new RE(filter.getExpression()); + + Iterator j = anEntity.getFields().iterator(); + while (j.hasNext()) { + String field = anEntity.getValue( (String) j.next()); + + if (field != null && regularExpression.isMatch(field.toLowerCase())) { + logger.debug("regexp match on " + filter.getExpression()); + return true; + } + } + } + catch (Throwable t) { + logger.warn("error while checking entity with regexp " + filter.getExpression() + ": " + t.getMessage()); + } + } + } + + return false; + } + } + + private void setCookie(HttpServletResponse aResponse) { + Random random = new Random(); + + Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000))); + cookie.setMaxAge(cookieMaxAge); + cookie.setPath("/"); + + if (aResponse!=null) + aResponse.addCookie(cookie); + } + + private boolean checkCookie(List aCookies) { + if (getCookieOnBlock()) { + Iterator i = aCookies.iterator(); + + while (i.hasNext()) { + Cookie cookie = (Cookie) i.next(); + + if (cookie.getName().equals(cookieName)) { + logger.debug("cookie match"); + return true; + } + } + } + + return false; + } + + public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) { + try { + long time = System.currentTimeMillis(); + String address = "0.0.0.0"; + String browser = "unknown"; + Cookie[] cookies = {}; + + HttpServletRequest request = null; + + if (aRequest instanceof HTTPAdapters.HTTPParsedRequestAdapter) { + request = ((HTTPAdapters.HTTPParsedRequestAdapter) aRequest).getRequest(); + } + else if (aRequest instanceof HTTPAdapters.HTTPRequestAdapter) { + request = ((HTTPAdapters.HTTPRequestAdapter) aRequest).getRequest(); + } + if (request!=null) { + browser = (String) request.getHeader("User-Agent"); + address = request.getRemoteAddr(); + cookies = request.getCookies(); + } + + logComment(address, aComment.getId(), new Date(), browser); + + MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction); + + if (checkCookie(Arrays.asList(cookies)) || checkIpFilter(address) || checkRegExpFilter(aComment)) { + operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("comment", aComment)); + setCookie(aResponse); + } + + logger.info("checkComment: " + (System.currentTimeMillis()-time) + "ms"); + } + catch (Throwable t) { + logger.error("Abuse.checkComment: " + t.toString()); + } + } + + public void checkArticle(EntityContent anArticle, HttpServletRequest aRequest, HttpServletResponse aResponse) { + try { + long time = System.currentTimeMillis(); + + logArticle(aRequest.getRemoteAddr(), anArticle.getId(), new Date(), (String) aRequest.getHeader("User-Agent")); + + MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction); + + if (checkCookie(Arrays.asList(aRequest.getCookies())) || checkIpFilter(aRequest.getRemoteAddr()) || checkRegExpFilter(anArticle)) { + operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("content", anArticle)); + setCookie(aResponse); + } + + logger.info("checkArticle: " + (System.currentTimeMillis()-time) + "ms"); + } + catch (Throwable t) { + logger.error("Abuse.checkArticle: " + t.toString()); + } + } + + public boolean getLogEnabled() { + return logEnabled; + } + + public void setLogEnabled(boolean anEnabled) { + logEnabled = anEnabled; + truncateLog(); + } + + public int getLogSize() { + return logSize; + } + + public void setLogSize(int aSize) { + logSize = aSize; + truncateLog(); + } + + public boolean getOpenPostingDisabled() { + return openPostingDisabled; + } + + public void setOpenPostingDisabled(boolean anOpenPostingDisabled) { + openPostingDisabled = anOpenPostingDisabled; + } + + public boolean getOpenPostingPassword() { + return openPostingPassword; + } + + public void setOpenPostingPassword(boolean anOpenPostingPassword) { + openPostingPassword = anOpenPostingPassword; + } + + public boolean getCookieOnBlock() { + return cookieOnBlock; + } + + public void setCookieOnBlock(boolean aCookieOnBlock) { + cookieOnBlock = aCookieOnBlock; + } + + public String getArticleBlockAction() { + return articleBlockAction; + } + + public void setArticleBlockAction(String anAction) { + articleBlockAction = anAction; + } + + public String getCommentBlockAction() { + return commentBlockAction; + } + + public void setCommentBlockAction(String anAction) { + commentBlockAction = anAction; + } + + + public List getLog() { + synchronized(log) { + List result = new Vector(); + + Iterator i = log.iterator(); + while (i.hasNext()) { + LogEntry logEntry = (LogEntry) i.next(); + Map entry = new HashMap(); + + entry.put("ip", logEntry.getIpNumber()); + entry.put("id", logEntry.getId()); + entry.put("timestamp", new DateToMapAdapter(logEntry.getTimeStamp())); + if (logEntry.getIsArticle()) + entry.put("type", "content"); + else + entry.put("type", "comment"); + entry.put("browser", logEntry.getBrowserString()); + + result.add(entry); + } + + return result; + } + } + + public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) { + appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false)); + } + + public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) { + appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true)); + } + + public void load() { + try { + ExtendedProperties configuration = new ExtendedProperties(); + + try { + configuration = new ExtendedProperties(configFile); + } + catch (FileNotFoundException e) { + } + + getFilterConfig(filters, "abuse.filter", configuration); + + setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1")); + setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1")); + setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1")); + setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1")); + setLogSize(configuration.getInt("abuse.logSize", 10)); + setArticleBlockAction(configuration.getString("abuse.articleBlockAction", "")); + setCommentBlockAction(configuration.getString("abuse.commentBlockAction", "")); + } + catch (Throwable t) { + throw new RuntimeException(t.toString()); + } + } + public void save() { + try { + ExtendedProperties configuration = new ExtendedProperties(); + + setFilterConfig(filters, "abuse.filter", configuration); + + configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled()?"1":"0"); + configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword()?"1":"0"); + configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock()?"1":"0"); + configuration.addProperty("abuse.logEnabled", getLogEnabled()?"1":"0"); + configuration.addProperty("abuse.logSize", Integer.toString(getLogSize())); + configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction()); + configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction()); + + configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration"); + } + catch (Throwable t) { + throw new RuntimeException(t.toString()); + } + } + + public List getFilterTypes() { + List result = new Vector(); + + Map entry = new HashMap(); + entry.put("resource", "ip"); + entry.put("id", IP_FILTER_TYPE); + result.add(entry); + + entry = new HashMap(); + entry.put("resource", "regexp"); + entry.put("id", REGEXP_FILTER_TYPE); + result.add(entry); + + return result; + } + + public List getArticleActions() { + try { + List result = new Vector(); + + Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator(); + while (i.hasNext()) { + MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = + (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next(); + + Map action = new HashMap(); + action.put("resource", operation.getName()); + action.put("identifier", operation.getName()); + + result.add(action); + } + + return result; + } + catch (Throwable t) { + throw new RuntimeException("can't get article actions"); + } + } + + public List getCommentActions() { + try { + List result = new Vector(); + + Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator(); + while (i.hasNext()) { + MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = + (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next(); + + Map action = new HashMap(); + action.put("resource", operation.getName()); + action.put("identifier", operation.getName()); + + result.add(action); + } + + return result; + } + catch (Throwable t) { + throw new RuntimeException("can't get comment actions"); + } + } + + public List getFilters() { + return getFiltersAsMaps(filters); + } + + public void addFilter(String aType, String anExpression) { + addFilter(filters, aType, anExpression); + } + + public void setFilter(String anIdentifier, String aType, String anExpression) { + setFilter(filters, anIdentifier, aType, anExpression); + } + + public void deleteFilter(String anIdentifier) { + deleteFilter(filters, anIdentifier); + } + + public void validateIpFilter(String anIdentifier, String anArticleAction, String aCommentAction) throws Exception { + } + + private List getFiltersAsMaps(List aFilters) { + synchronized(aFilters) { + List result = new Vector(); + + Iterator i = aFilters.iterator(); + while (i.hasNext()) { + Filter filter = (Filter) i.next(); + Map map = new HashMap(); + + map.put("id", filter.getId()); + map.put("expression", filter.getExpression()); + map.put("type", filter.getType()); + + result.add(map); + } + return result; + } + } + + private void addFilter(List aFilters, String aType, String anExpression) { + Filter filter = new Filter(); + + filter.setId(generateId()); + filter.setExpression(anExpression); + filter.setType(aType); + + synchronized (aFilters) { + aFilters.add(filter); + } + } + + private void setFilter(List aFilters, String anIdentifier, String aType, String anExpression) { + synchronized (aFilters) { + Filter filter = findFilter(aFilters, anIdentifier); + + if (filter!=null) { + filter.setExpression(anExpression); + filter.setType(aType); + } + } + } + + private Filter findFilter(List aFilters, String anIdentifier) { + synchronized (aFilters) { + Iterator i = aFilters.iterator(); + while (i.hasNext()) { + Filter filter = (Filter) i.next(); + + if (filter.getId().equals(anIdentifier)) { + return filter; + } + } + } + + return null; + } + + private void deleteFilter(List aFilters, String anIdentifier) { + synchronized (aFilters) { + Filter filter = findFilter(aFilters, anIdentifier); + + if (filter!=null) { + aFilters.remove(filter); + } + } + } + + private String generateId() { + synchronized(this) { + maxIdentifier = maxIdentifier+1; + + return Integer.toString(maxIdentifier); + } + } + + private static class Filter { + private String identifier; + private String expression; + private String type; + + public Filter() { + expression=""; + type=""; + identifier=""; + } + + public String getId() { + return identifier; + } + + public void setId(String anId) { + identifier = anId; + } + + public String getExpression() { + return expression; + } + + public void setExpression(String anExpression) { + expression = anExpression; + } + + public String getType() { + return type; + } + + public void setType(String aType) { + type = aType; + } + } + + private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) { + synchronized(aFilters) { + Iterator i = aFilters.iterator(); + + while (i.hasNext()) { + Filter filter = (Filter) i.next(); + + aConfiguration.addProperty(aConfigKey, filter.getType()+":"+filter.getExpression()); + } + } + } + + private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) { + synchronized(aFilters) { + aFilters.clear(); + + Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).iterator(); + + while (i.hasNext()) { + String filter = (String) i.next(); + List parts = StringRoutines.separateString(filter, ":"); + + if (parts.size()==2) { + addFilter( (String) parts.get(0), (String) parts.get(1)); + } + } + } + } + + private static class LogEntry { + private String ipNumber; + private String browserString; + private String id; + private Date timeStamp; + private boolean isArticle; + + public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) { + ipNumber = anIpNumber; + browserString = aBrowserString; + id = anId; + isArticle = anIsArticle; + timeStamp=aTimeStamp; + } + + public String getIpNumber() { + return ipNumber; + } + + public String getBrowserString() { + return browserString; + } + + public String getId() { + return id; + } + + public Date getTimeStamp() { + return timeStamp; + } + + public boolean getIsArticle() { + return isArticle; + } + } + + private void truncateLog() { + synchronized(log) { + if (!logEnabled) + log.clear(); + else { + while (log.size()>0 && log.size()>logSize) { + log.remove(0); + } + } + } + }; + + private void appendLog(LogEntry anEntry) { + synchronized (log) { + if (logEnabled) { + log.add(anEntry); + truncateLog(); + } + } + } + } \ No newline at end of file diff --git a/source/mircoders/localizer/MirLocalizer.java b/source/mircoders/localizer/MirLocalizer.java index 4d487ae7..8e3cf154 100755 --- a/source/mircoders/localizer/MirLocalizer.java +++ b/source/mircoders/localizer/MirLocalizer.java @@ -38,5 +38,4 @@ public interface MirLocalizer { public MirProducerAssistantLocalizer producerAssistant() throws MirLocalizerFailure, MirLocalizerExc; public MirGeneratorLocalizer generators() throws MirLocalizerFailure, MirLocalizerExc; public MirDataModelLocalizer dataModel() throws MirLocalizerFailure, MirLocalizerExc; - } \ No newline at end of file diff --git a/source/mircoders/localizer/MirOpenPostingLocalizer.java b/source/mircoders/localizer/MirOpenPostingLocalizer.java index 49601623..b3232751 100755 --- a/source/mircoders/localizer/MirOpenPostingLocalizer.java +++ b/source/mircoders/localizer/MirOpenPostingLocalizer.java @@ -1,44 +1,81 @@ -/* - * Copyright (C) 2001, 2002 The Mir-coders group - * - * This file is part of Mir. - * - * Mir is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Mir is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Mir; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * In addition, as a special exception, The Mir-coders gives permission to link - * the code of this program with the com.oreilly.servlet library, any library - * licensed under the Apache Software License, The Sun (tm) Java Advanced - * Imaging library (JAI), The Sun JIMI library (or with modified versions of - * the above that use the same license as the above), and distribute linked - * combinations including the two. You must obey the GNU General Public - * License in all respects for all of the code used other than the above - * mentioned libraries. If you modify this file, you may extend this exception - * to your version of the file, but you are not obligated to do so. If you do - * not wish to do so, delete this exception statement from your version. - */ - -package mircoders.localizer; - -import javax.servlet.http.HttpServletRequest; - -import mircoders.entity.EntityComment; -import mircoders.entity.EntityContent; - -public interface MirOpenPostingLocalizer { - public void afterContentPosting(EntityContent aContent); - public void afterCommentPosting(EntityComment aComment); - - public String chooseOpenPostingLanguage(HttpServletRequest req); -} +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mircoders.localizer; + +import java.util.Arrays; +import java.util.List; + +import mir.session.Request; +import mir.session.Response; +import mir.session.*; +import mircoders.entity.EntityComment; +import mircoders.entity.EntityContent; + +public interface MirOpenPostingLocalizer { + + /** + * Class to encapsulate a validation error + * + *

    Title:

    + *

    Description:

    + *

    Copyright: Copyright (c) 2003

    + *

    Company:

    + * @author not attributable + * @version 1.0 + */ + + /** + * + * @param aRequest + * @param aSession + * @return + * @throws MirLocalizerExc + * @throws MirLocalizerFailure + */ + public SessionHandler getOpenSessionHandler(Request aRequest, Session aSession) throws MirLocalizerExc, MirLocalizerFailure; + + /** + * + * @param aComment + * @throws MirLocalizerExc + * @throws MirLocalizerFailure + */ + public void afterCommentPosting(EntityComment aComment) throws MirLocalizerExc, MirLocalizerFailure; + + /** + * + * @param aContent + * @throws MirLocalizerExc + * @throws MirLocalizerFailure + */ + public void afterContentPosting(EntityContent aContent) throws MirLocalizerExc, MirLocalizerFailure; +} diff --git a/source/mircoders/localizer/basic/MirBasicCommentPostingSessionHandler.java b/source/mircoders/localizer/basic/MirBasicCommentPostingSessionHandler.java new file mode 100755 index 00000000..bf219f84 --- /dev/null +++ b/source/mircoders/localizer/basic/MirBasicCommentPostingSessionHandler.java @@ -0,0 +1,307 @@ +package mircoders.localizer.basic; + +import java.util.*; +import mir.log.LoggerWrapper; +import mir.session.*; +import mir.config.*; +import mir.util.*; +import mir.entity.*; +import mircoders.storage.*; +import mircoders.global.*; +import mircoders.localizer.*; +import mircoders.entity.*; +import mircoders.module.*; +import mircoders.media.*; + +/** + * + *

    Title: Experimental session handler for comment postings

    + *

    Description:

    + *

    Copyright: Copyright (c) 2003

    + *

    Company:

    + * @author not attributable + * @version 1.0 + */ + +public class MirBasicCommentPostingSessionHandler implements SessionHandler { + protected LoggerWrapper logger; + protected MirPropertiesConfiguration configuration; + protected boolean initialRequest; + protected ModuleComment commentModule; + protected DatabaseCommentToMedia commentToMedia = DatabaseCommentToMedia.getInstance(); + + public MirBasicCommentPostingSessionHandler() { + logger = new LoggerWrapper("Localizer.OpenPosting.Comment"); + try { + configuration = MirPropertiesConfiguration.instance(); + } + catch (Throwable t) { + logger.fatal("Cannont load configuration: " + t.toString()); + + throw new RuntimeException("Cannont load configuration: " + t.toString()); + } + initialRequest= true; + commentModule= new ModuleComment(DatabaseComment.getInstance()); + } + + public void processRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure { + if (aSession.getAttribute("initialRequest")==null) { + initialRequest(aRequest, aSession, aResponse); + aSession.setAttribute("initialRequest", "no"); + } + else { + subsequentRequest(aRequest, aSession, aResponse); + } + }; + + public String generateOnetimePassword() { + Random r = new Random(); + int random = r.nextInt(); + + long l = System.currentTimeMillis(); + + l = (l*l*l*l)/random; + if (l<0) + l = l * -1; + + String returnString = ""+l; + + return returnString.substring(5); + } + + public void initializeResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure { + if (MirGlobal.abuse().getOpenPostingPassword()) { + String password = (String) aSession.getAttribute("password"); + if (password==null) { + password = generateOnetimePassword(); + aSession.setAttribute("password", password); + } + aResponse.setResponseValue("password", password); + } + else { + aResponse.setResponseValue("password", null); + aSession.deleteAttribute("password"); + } + + aResponse.setResponseValue("errors", null); + }; + + public void initialRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure{ + Iterator i = DatabaseContent.getInstance().getFields().iterator(); + while (i.hasNext()) { + aResponse.setResponseValue( (String) i.next(), null); + } + + String articleId = aRequest.getParameter("to_media"); + + if (articleId == null) + throw new SessionExc("MirBasicCommentPostingSessionHandler.initialRequest: article id not set!"); + + aSession.setAttribute("to_media", articleId); + + initializeResponseData(aRequest, aSession, aResponse); + + try { + aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.EditTemplate")); + } + catch (Throwable e) { + throw new SessionFailure("Can't get configuration: " + e.getMessage(), e); + } + + } + + public boolean testFieldExists(Request aRequest, String aFieldName, String anErrorMessageResource, List aValidationResults) { + Object value = aRequest.getParameter(aFieldName); + if (value==null || !(value instanceof String) || ((String) value).trim().length()==0) { + logger.debug(" missing field " + aFieldName + " value = " + value); + aValidationResults.add(new ValidationError(aFieldName, anErrorMessageResource)); + return false; + } + else + return true; + } + + public boolean testFieldIsNumeric(Request aRequest, String aFieldName, String anErrorMessageResource, List aValidationResults) { + Object value = aRequest.getParameter(aFieldName); + if (value!=null) { + try { + Integer.parseInt((String) value); + return true; + } + catch (Throwable t) { + logger.debug(" field not numeric: " + aFieldName + " value = " + value); + aValidationResults.add(new ValidationError(aFieldName, anErrorMessageResource)); + return false; + } + } + return true; + } + + + public List validate(Request aRequest, Session aSession) throws SessionExc, SessionFailure { + List result = new Vector(); + + testFieldExists(aRequest, "title", "validationerror.missing", result); + testFieldExists(aRequest, "description", "validationerror.missing", result); + testFieldExists(aRequest, "creator", "validationerror.missing", result); + + return result; + } + + public void subsequentRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure { + try { + Map commentFields = new HashMap(); + + Iterator i = DatabaseContent.getInstance().getFields().iterator(); + while (i.hasNext()) { + String field = (String) i.next(); + aResponse.setResponseValue(field, aRequest.getParameter(field)); + if (aRequest.getParameter(field)!=null) { + commentFields.put(field, aRequest.getParameter(field)); + } + } + + initializeResponseData(aRequest, aSession, aResponse); + + List validationErrors = validate(aRequest, aSession); + + if (validationErrors != null && validationErrors.size()>0) { + returnValidationErrors(aRequest, aSession, aResponse, validationErrors); + } + else { + EntityComment comment = (EntityComment) commentModule.createNew (); + comment.setValues(commentFields); + + finishComment(aRequest, aSession, comment); + + String id = comment.insert(); + if(id==null){ + afterDuplicateCommentPosting(aRequest, aSession, aResponse, comment); + logger.info("Dupe comment rejected"); + aSession.terminate(); + } + else { + i = aRequest.getUploadedFiles().iterator(); + while (i.hasNext()) { + UploadedFile file = (UploadedFile) i.next(); + processMediaFile(aRequest, aSession, comment, file); + } + + afterCommentPosting(aRequest, aSession, aResponse, comment); + MirGlobal.abuse().checkComment(comment, aRequest, null); + MirGlobal.localizer().openPostings().afterCommentPosting(comment); + logger.info("Comment posted"); + aSession.terminate(); + } + } + } + catch (Throwable t) { + ExceptionFunctions.traceCauseException(t).printStackTrace(); + + throw new SessionFailure("MirBasicCommentPostingSessionHandler.subsequentRequest: " + t.getMessage(), t); + } + } + + public void initializeCommentPosting(Request aRequest, Session aSession, Response aResponse) throws SessionFailure, SessionExc { + String articleId = aRequest.getParameter("to_media"); + if (articleId==null) + articleId = aRequest.getParameter("aid"); + + if (articleId==null) + throw new SessionExc("initializeCommentPosting: article id not set!"); + + aSession.setAttribute("to_media", articleId); + processCommentPosting(aRequest, aSession, aResponse); + }; + + public void returnValidationErrors(Request aRequest, Session aSession, Response aResponse, List aValidationErrors) throws SessionFailure, SessionExc { + aResponse.setResponseValue("errors", aValidationErrors); + aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.EditTemplate")); + }; + + public void processCommentPosting(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure { + if (MirGlobal.abuse().getOpenPostingPassword()) { + String password = generateOnetimePassword(); + aSession.setAttribute("password", password); + aResponse.setResponseValue("password", password); + aResponse.setResponseValue("passwd", password); + } + else { + aResponse.setResponseValue("password", null); + } + + aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.EditTemplate")); + }; + + public void processMediaFile(Request aRequest, Session aSession, EntityComment aComment, UploadedFile aFile) throws SessionExc, SessionFailure { + try { + Entity mediaItem = MediaUploadProcessor.processMediaUpload(aFile); + finishMedia(aRequest, aSession, aFile, mediaItem); + mediaItem.update(); + commentToMedia.addMedia(aComment.getId(), mediaItem.getId()); + } + catch (Throwable t) { + throw new SessionFailure(t); + } + } + + public void finishMedia(Request aRequest, Session aSession, UploadedFile aFile, Entity aMedia) throws SessionExc, SessionFailure { + } + + public void finishComment(Request aRequest, Session aSession, EntityComment aComment) throws SessionExc, SessionFailure { + if (aSession.getAttribute("to_media") == null) + throw new SessionExc("missing to_media"); + + aComment.setValueForProperty("is_published", "1"); + aComment.setValueForProperty("to_comment_status", "1"); + aComment.setValueForProperty("is_html","0"); + aComment.setValueForProperty("to_media", (String) aSession.getAttribute("to_media")); + }; + + public void addMedia(Request aRequest, Session aSession, EntityComment aComment) throws SessionExc, SessionFailure { + } + + public void afterCommentPosting(Request aRequest, Session aSession, Response aResponse, EntityComment aComment) { + DatabaseContent.getInstance().setUnproduced("id=" + aComment.getValue("to_media")); + aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.DoneTemplate")); + }; + + public void afterDuplicateCommentPosting(Request aRequest, Session aSession, Response aResponse, EntityComment aComment) { + aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.DupeTemplate")); + }; + + public class ValidationError { + private String field; + private String message; + private List parameters; + + public ValidationError(String aField, String aMessage) { + this (aField, aMessage, new String[] {}); + } + + public ValidationError(String aField, String aMessage, Object aParameter) { + this (aField, aMessage, new Object[] {aParameter}); + } + + public ValidationError(String aField, String aMessage, Object[] aParameters) { + field = aField; + message = aMessage; + parameters = Arrays.asList(aParameters); + } + + public String getMessage() { + return message; + } + + public String getField() { + return field; + } + + public List getParameters() { + return parameters; + } + } + + + +} \ No newline at end of file diff --git a/source/mircoders/localizer/basic/MirBasicLocalizer.java b/source/mircoders/localizer/basic/MirBasicLocalizer.java index 3dfb79a3..06823a1a 100755 --- a/source/mircoders/localizer/basic/MirBasicLocalizer.java +++ b/source/mircoders/localizer/basic/MirBasicLocalizer.java @@ -47,11 +47,11 @@ public class MirBasicLocalizer implements MirLocalizer { return new MirBasicProducerLocalizer(); } - public MirGeneratorLocalizer generators() { + public MirGeneratorLocalizer generators() throws MirLocalizerFailure, MirLocalizerExc { return new MirBasicGeneratorLocalizer(); } - public MirOpenPostingLocalizer openPostings() { + public MirOpenPostingLocalizer openPostings() throws MirLocalizerFailure, MirLocalizerExc { return new MirBasicOpenPostingLocalizer(); } @@ -59,7 +59,7 @@ public class MirBasicLocalizer implements MirLocalizer { return new MirBasicProducerAssistantLocalizer(); } - public MirDataModelLocalizer dataModel() { + public MirDataModelLocalizer dataModel() throws MirLocalizerFailure, MirLocalizerExc { return new MirBasicDataModelLocalizer(); }; diff --git a/source/mircoders/localizer/basic/MirBasicOpenPostingLocalizer.java b/source/mircoders/localizer/basic/MirBasicOpenPostingLocalizer.java index ad7dae97..f4b5d5c7 100755 --- a/source/mircoders/localizer/basic/MirBasicOpenPostingLocalizer.java +++ b/source/mircoders/localizer/basic/MirBasicOpenPostingLocalizer.java @@ -1,90 +1,121 @@ -/* - * Copyright (C) 2001, 2002 The Mir-coders group - * - * This file is part of Mir. - * - * Mir is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Mir is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Mir; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * In addition, as a special exception, The Mir-coders gives permission to link - * the code of this program with the com.oreilly.servlet library, any library - * licensed under the Apache Software License, The Sun (tm) Java Advanced - * Imaging library (JAI), The Sun JIMI library (or with modified versions of - * the above that use the same license as the above), and distribute linked - * combinations including the two. You must obey the GNU General Public - * License in all respects for all of the code used other than the above - * mentioned libraries. If you modify this file, you may extend this exception - * to your version of the file, but you are not obligated to do so. If you do - * not wish to do so, delete this exception statement from your version. - */ - -package mircoders.localizer.basic; - -import java.util.List; -import java.util.Locale; - -import javax.servlet.http.HttpServletRequest; - -import mir.log.LoggerWrapper; -import mircoders.entity.EntityComment; -import mircoders.entity.EntityContent; -import mircoders.global.MirGlobal; -import mircoders.global.ProducerEngine; -import mircoders.localizer.MirOpenPostingLocalizer; - -public class MirBasicOpenPostingLocalizer implements MirOpenPostingLocalizer { - private List afterContentProducerTasks; - private List afterCommentProducerTasks; - protected LoggerWrapper logger; - - - - public MirBasicOpenPostingLocalizer() { - logger = new LoggerWrapper("Localizer.Basic.OpenPosting"); - - try { - String contentProducers = MirGlobal.config().getString("Mir.Localizer.OpenPosting.ContentProducers"); - String commentProducers = MirGlobal.config().getString("Mir.Localizer.OpenPosting.CommentProducers"); - - afterContentProducerTasks = ProducerEngine.ProducerTask.parseProducerTaskList(contentProducers); - afterCommentProducerTasks = ProducerEngine.ProducerTask.parseProducerTaskList(commentProducers); - } - catch (Throwable t) { - logger.error("Setting up MirBasicOpenPostingLocalizer failed: " + t.getMessage()); - } - } - - public void afterContentPosting() { - MirGlobal.producerEngine().addTasks(afterContentProducerTasks); - } - - public void afterContentPosting(EntityContent aContent) { - afterContentPosting(); - } - - public void afterCommentPosting() { - MirGlobal.producerEngine().addTasks(afterCommentProducerTasks); - } - - public void afterCommentPosting(EntityComment aComment) { - afterCommentPosting(); - } - - public String chooseOpenPostingLanguage(HttpServletRequest req) { - Locale locale = req.getLocale(); - - return locale.getLanguage(); - } - -} +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mircoders.localizer.basic; + +import java.util.List; +import java.util.Locale; +import java.util.*; +import javax.servlet.http.HttpServletRequest; + +import mir.log.LoggerWrapper; +import mir.servlet.*; +import mir.config.*; +import mir.session.Request; +import mir.session.Response; +import mir.session.*; + +import mircoders.entity.EntityComment; +import mircoders.entity.EntityContent; +import mircoders.global.MirGlobal; +import mircoders.global.ProducerEngine; +import mircoders.localizer.*; + +public class MirBasicOpenPostingLocalizer implements MirOpenPostingLocalizer { + private List afterContentProducerTasks; + private List afterCommentProducerTasks; + protected LoggerWrapper logger; + protected MirPropertiesConfiguration configuration; + + + public MirBasicOpenPostingLocalizer() throws MirLocalizerExc, MirLocalizerFailure { + logger = new LoggerWrapper("Localizer.Basic.OpenPosting"); + + try { + configuration = MirPropertiesConfiguration.instance(); + } + catch (Throwable e) { + throw new MirLocalizerFailure("Can't get configuration: " + e.getMessage(), e); + } + + try { + String contentProducers = MirGlobal.config().getString("Mir.Localizer.OpenPosting.ContentProducers"); + String commentProducers = MirGlobal.config().getString("Mir.Localizer.OpenPosting.CommentProducers"); + + afterContentProducerTasks = ProducerEngine.ProducerTask.parseProducerTaskList(contentProducers); + afterCommentProducerTasks = ProducerEngine.ProducerTask.parseProducerTaskList(commentProducers); + } + catch (Throwable t) { + logger.error("Setting up MirBasicOpenPostingLocalizer failed: " + t.getMessage()); + + throw new MirLocalizerFailure(t); + } + } + + public SessionHandler getOpenSessionHandler(Request aRequest, Session aSession) throws MirLocalizerExc, MirLocalizerFailure { + if (aSession.getAttribute("handler")==null) + aSession.setAttribute("handler", new MirBasicCommentPostingSessionHandler()); + + return (SessionHandler) aSession.getAttribute("handler"); + } + + public void afterContentPosting() { + MirGlobal.producerEngine().addTasks(afterContentProducerTasks); + } + + public void afterContentPosting(EntityContent aContent) { + afterContentPosting(); + } + + public void afterCommentPosting(EntityComment aComment) { + afterCommentPosting(); + } + + public void afterCommentPosting() { + MirGlobal.producerEngine().addTasks(afterCommentProducerTasks); + } + + public String generateOnetimePassword() { + Random r = new Random(); + int random = r.nextInt(); + + long l = System.currentTimeMillis(); + + l = (l*l*l*l)/random; + if (l<0) + l = l * -1; + + String returnString = ""+l; + + return returnString.substring(5); + } + +} diff --git a/source/mircoders/media/MediaUploadProcessor.java b/source/mircoders/media/MediaUploadProcessor.java new file mode 100755 index 00000000..a23be012 --- /dev/null +++ b/source/mircoders/media/MediaUploadProcessor.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mircoders.media; + +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; + +import mir.entity.Entity; +import mir.media.MediaExc; +import mir.media.MediaFailure; +import mir.media.MediaHelper; +import mir.media.MirMedia; +import mir.misc.StringUtil; +import mir.session.UploadedFile; +import mir.storage.Database; + +import mircoders.module.ModuleMediaType; + +public class MediaUploadProcessor { + public static Entity processMediaUpload(UploadedFile aFile) throws MediaExc, MediaFailure { + String mediaId; + MirMedia mediaHandler; + Entity mediaType; + ModuleMediaType mediaTypeModule; + Database mediaStorage; + Map values = new HashMap(); + String MediaId; + Entity mediaEntity; + + + try { + String contentType = aFile.getContentType(); + + if (contentType.equals("text/plain") || + contentType.equals("application/octet-stream") || + contentType == null) { + throw new MediaExc("Invalid content-type: " + contentType); + } + + values.put("date", StringUtil.date2webdbDate(new GregorianCalendar())); + values.put("is_produced", "0"); + values.put("is_published", "1"); + values.put("to_publisher", "0"); + values.put("to_media_folder", "7"); + values.put("title", ""); + + mediaTypeModule = new ModuleMediaType(); + mediaType = mediaTypeModule.findMediaTypeForMimeType(contentType); + + try { + mediaHandler = MediaHelper.getHandler(mediaType); + mediaStorage = MediaHelper.getStorage(mediaType, "mircoders.storage.Database"); + } + catch (Throwable e) { + throw new MediaFailure(e); + } + + values.put("to_media_type", mediaType.getId()); + + try { + mediaEntity = (Entity) mediaStorage.getEntityClass().newInstance(); + mediaEntity.setStorage(mediaStorage); + } + catch (Throwable e) { + throw new MediaFailure(e); + } + + mediaEntity.setValues(values); + mediaId = mediaEntity.insert(); + + try { + mediaHandler.set(aFile.getInputStream(), mediaEntity, mediaType); + } + catch (Throwable e) { + throw new MediaFailure(e); + } + + return mediaEntity; + } + catch (Throwable e) { + throw new MediaFailure(e); + } + + } +} \ No newline at end of file diff --git a/source/mircoders/media/UnsupportedMediaFormatExc.java b/source/mircoders/media/UnsupportedMediaFormatExc.java index af8ec8c3..57237e3c 100755 --- a/source/mircoders/media/UnsupportedMediaFormatExc.java +++ b/source/mircoders/media/UnsupportedMediaFormatExc.java @@ -1,3 +1,34 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + package mircoders.media; import mir.media.MediaExc; diff --git a/source/mircoders/module/ModuleContent.java b/source/mircoders/module/ModuleContent.java index f697f8bf..cd4142e5 100755 --- a/source/mircoders/module/ModuleContent.java +++ b/source/mircoders/module/ModuleContent.java @@ -38,13 +38,14 @@ import mir.module.ModuleExc; import mir.module.ModuleFailure; import mir.storage.StorageObject; -/* - * ContentObjekt - - * - * @version $Id: ModuleContent.java,v 1.18 2003/03/09 19:14:21 idfx Exp $ - * - * @author RK, mir-coders +/** * + *

    Title: ModuleContent

    + *

    Description: helper routines to manage articles

    + *

    Copyright: Copyright (c) 2003

    + *

    Company:

    + * @author not attributable + * @version 1.0 */ public class ModuleContent extends AbstractModule @@ -59,110 +60,6 @@ public class ModuleContent extends AbstractModule this.theStorage = theStorage; } -// -// various methods to retrieve content entities - -// public EntityList getFeatures(int offset, int limit) throws ModuleExc, ModuleFailure { -// return getContent("is_published=true AND to_article_type=2", "webdb_create desc", -// offset, limit); -// } - -// public EntityList getNewsWire(int offset, int limit) throws ModuleExc, ModuleFailure { -// return getContent("is_published=true AND to_article_type = 1", -// "webdb_create desc",offset,limit); -// } - -// public EntityList getStartArticle() throws ModuleExc, ModuleFailure { -// EntityList returnList = getContent("is_published=true AND to_article_type=4", -// "webdb_create desc",0,1); -//if no startspecial exists -// if (returnList==null || returnList.size()==0) -// returnList = getContent("is_published=true AND to_article_type=3", -// "webdb_create desc",0,1); - -// return returnList; -// } - -/* - public EntityList getContent(Map searchValues, boolean concat, int offset, EntityUsers user) throws ModuleException { - - try { - - String whereClause ="", aField, aValue; - boolean first = true; - - Set set = searchValues.keySet(); - Iterator it = set.iterator(); - for (int i=0;iTitle:

    + *

    Description:

    + *

    Copyright: Copyright (c) 2003

    + *

    Company:

    + * @author the mir coders + * @version 1.0 + */ + +public class ModuleUploadedMedia extends AbstractModule +{ + static LoggerWrapper logger = new LoggerWrapper("Module.UploadedMedia"); + + public ModuleUploadedMedia(StorageObject aStorage) { + theStorage = aStorage; + } } \ No newline at end of file diff --git a/source/mircoders/servlet/ServletHelper.java b/source/mircoders/servlet/ServletHelper.java index efe8dbce..dbffa3ac 100755 --- a/source/mircoders/servlet/ServletHelper.java +++ b/source/mircoders/servlet/ServletHelper.java @@ -1,3 +1,34 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + package mircoders.servlet; import java.io.PrintWriter; @@ -88,7 +119,7 @@ public class ServletHelper { Generator generator; try { - generator = MirGlobal.localizer().generators().makeAdminGeneratorLibrary().makeGenerator(aGenerator); + generator = MirGlobal.localizer().generators().makeOpenPostingGeneratorLibrary().makeGenerator(aGenerator); generator.generate(aWriter, aGenerationData, new PrintWriter(new NullWriter())); } diff --git a/source/mircoders/servlet/ServletModuleAbuse.java b/source/mircoders/servlet/ServletModuleAbuse.java index e2d955b0..faf1e815 100755 --- a/source/mircoders/servlet/ServletModuleAbuse.java +++ b/source/mircoders/servlet/ServletModuleAbuse.java @@ -1,5 +1,36 @@ -package mircoders.servlet; - +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mircoders.servlet; + import java.util.Locale; import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -10,141 +41,141 @@ import mir.servlet.ServletModule; import mir.servlet.ServletModuleFailure; import mir.util.HTTPRequestParser; import mir.util.URLBuilder; -import mircoders.global.MirGlobal; - -public class ServletModuleAbuse extends ServletModule { - private static ServletModuleAbuse instance = new ServletModuleAbuse(); - public static ServletModule getInstance() { return instance; } - - private ServletModuleAbuse() { - logger = new LoggerWrapper("ServletModule.Abuse"); - defaultAction = "showsettings"; - } - - public void editfilter(HttpServletRequest aRequest, HttpServletResponse aResponse) { - HTTPRequestParser requestParser = new HTTPRequestParser(aRequest); - - String type=requestParser.getParameterWithDefault("type", ""); - String id=requestParser.getParameterWithDefault("id", ""); - String expression=requestParser.getParameterWithDefault("expression", ""); - - if (id.equals("")) { - MirGlobal.abuse().addFilter(type, expression); - } - else { - MirGlobal.abuse().setFilter(id, type, expression); - } - - MirGlobal.abuse().save(); - - showfilters(aRequest, aResponse); - } - - public void deletefilter(HttpServletRequest aRequest, HttpServletResponse aResponse) { - HTTPRequestParser requestParser = new HTTPRequestParser(aRequest); - - String id=requestParser.getParameterWithDefault("id", ""); - MirGlobal.abuse().deleteFilter(id); - - MirGlobal.abuse().save(); - - showfilters(aRequest, aResponse); - } - - public void showfilters(HttpServletRequest aRequest, HttpServletResponse aResponse) { - URLBuilder urlBuilder = new URLBuilder(); - - try { - Map responseData = ServletHelper.makeGenerationData(new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}); - - urlBuilder.setValue("module", "Abuse"); - urlBuilder.setValue("do", "showfilters"); - responseData.put("thisurl", urlBuilder.getQuery()); - - responseData.put("filters", MirGlobal.abuse().getFilters()); - responseData.put("filtertypes", MirGlobal.abuse().getFilterTypes()); - - ServletHelper.generateResponse(aResponse.getWriter(), responseData, "abuse.filters.template"); - } - catch (Throwable e) { - throw new ServletModuleFailure(e); - } - } - - - public void showsettings(HttpServletRequest aRequest, HttpServletResponse aResponse) { - URLBuilder urlBuilder = new URLBuilder(); - - try { - Map responseData = ServletHelper.makeGenerationData(new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}); - - urlBuilder.setValue("module", "Abuse"); - urlBuilder.setValue("do", "showsettings"); - - responseData.put("thisurl", urlBuilder.getQuery()); - - responseData.put("articleactions", MirGlobal.abuse().getArticleActions()); - responseData.put("commentactions", MirGlobal.abuse().getCommentActions()); - - responseData.put("disableop", new Boolean(MirGlobal.abuse().getOpenPostingDisabled())); - responseData.put("passwordop", new Boolean(MirGlobal.abuse().getOpenPostingPassword())); - responseData.put("logenabled", new Boolean(MirGlobal.abuse().getLogEnabled())); - responseData.put("logsize", Integer.toString(MirGlobal.abuse().getLogSize())); - responseData.put("usecookies", new Boolean(MirGlobal.abuse().getCookieOnBlock())); - responseData.put("articleaction", MirGlobal.abuse().getArticleBlockAction()); - responseData.put("commentaction", MirGlobal.abuse().getCommentBlockAction()); - - ServletHelper.generateResponse(aResponse.getWriter(), responseData, "abuse.template"); - } - catch (Throwable e) { - throw new ServletModuleFailure(e); - } - } - - public void savesettings(HttpServletRequest aRequest, HttpServletResponse aResponse) { - try { - HTTPRequestParser parser = new HTTPRequestParser(aRequest); - - MirGlobal.abuse().setOpenPostingDisabled(parser.getParameterWithDefault("disableop", "").equals("1")); - MirGlobal.abuse().setOpenPostingPassword(parser.getParameterWithDefault("passwordop", "").equals("1")); - MirGlobal.abuse().setLogEnabled(parser.getParameterWithDefault("logenabled", "").equals("1")); - - try { - MirGlobal.abuse().setLogSize(parser.getIntegerWithDefault("logsize", MirGlobal.abuse().getLogSize())); - } - catch (Throwable t) { - } - - MirGlobal.abuse().setCookieOnBlock(parser.getParameterWithDefault("usecookies", "").equals("1")); - - MirGlobal.abuse().setArticleBlockAction(parser.getParameter("articleaction")); - MirGlobal.abuse().setCommentBlockAction(parser.getParameter("commentaction")); - - MirGlobal.abuse().save(); - - showsettings(aRequest, aResponse); - } - catch (Throwable e) { - throw new ServletModuleFailure(e); - } - } - - public void showlog(HttpServletRequest aRequest, HttpServletResponse aResponse) { - URLBuilder urlBuilder = new URLBuilder(); - int count; - - try { - Map responseData = ServletHelper.makeGenerationData(new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}); - urlBuilder.setValue("module", "Abuse"); - urlBuilder.setValue("do", "showlog"); - responseData.put("thisurl", urlBuilder.getQuery()); - - responseData.put("log", MirGlobal.abuse().getLog()); - - ServletHelper.generateResponse(aResponse.getWriter(), responseData, "abuse.log.template"); - } - catch (Throwable e) { - throw new ServletModuleFailure(e); - } - } +import mircoders.global.MirGlobal; + +public class ServletModuleAbuse extends ServletModule { + private static ServletModuleAbuse instance = new ServletModuleAbuse(); + public static ServletModule getInstance() { return instance; } + + private ServletModuleAbuse() { + logger = new LoggerWrapper("ServletModule.Abuse"); + defaultAction = "showsettings"; + } + + public void editfilter(HttpServletRequest aRequest, HttpServletResponse aResponse) { + HTTPRequestParser requestParser = new HTTPRequestParser(aRequest); + + String type=requestParser.getParameterWithDefault("type", ""); + String id=requestParser.getParameterWithDefault("id", ""); + String expression=requestParser.getParameterWithDefault("expression", ""); + + if (id.equals("")) { + MirGlobal.abuse().addFilter(type, expression); + } + else { + MirGlobal.abuse().setFilter(id, type, expression); + } + + MirGlobal.abuse().save(); + + showfilters(aRequest, aResponse); + } + + public void deletefilter(HttpServletRequest aRequest, HttpServletResponse aResponse) { + HTTPRequestParser requestParser = new HTTPRequestParser(aRequest); + + String id=requestParser.getParameterWithDefault("id", ""); + MirGlobal.abuse().deleteFilter(id); + + MirGlobal.abuse().save(); + + showfilters(aRequest, aResponse); + } + + public void showfilters(HttpServletRequest aRequest, HttpServletResponse aResponse) { + URLBuilder urlBuilder = new URLBuilder(); + + try { + Map responseData = ServletHelper.makeGenerationData(new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}); + + urlBuilder.setValue("module", "Abuse"); + urlBuilder.setValue("do", "showfilters"); + responseData.put("thisurl", urlBuilder.getQuery()); + + responseData.put("filters", MirGlobal.abuse().getFilters()); + responseData.put("filtertypes", MirGlobal.abuse().getFilterTypes()); + + ServletHelper.generateResponse(aResponse.getWriter(), responseData, "abuse.filters.template"); + } + catch (Throwable e) { + throw new ServletModuleFailure(e); + } + } + + + public void showsettings(HttpServletRequest aRequest, HttpServletResponse aResponse) { + URLBuilder urlBuilder = new URLBuilder(); + + try { + Map responseData = ServletHelper.makeGenerationData(new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}); + + urlBuilder.setValue("module", "Abuse"); + urlBuilder.setValue("do", "showsettings"); + + responseData.put("thisurl", urlBuilder.getQuery()); + + responseData.put("articleactions", MirGlobal.abuse().getArticleActions()); + responseData.put("commentactions", MirGlobal.abuse().getCommentActions()); + + responseData.put("disableop", new Boolean(MirGlobal.abuse().getOpenPostingDisabled())); + responseData.put("passwordop", new Boolean(MirGlobal.abuse().getOpenPostingPassword())); + responseData.put("logenabled", new Boolean(MirGlobal.abuse().getLogEnabled())); + responseData.put("logsize", Integer.toString(MirGlobal.abuse().getLogSize())); + responseData.put("usecookies", new Boolean(MirGlobal.abuse().getCookieOnBlock())); + responseData.put("articleaction", MirGlobal.abuse().getArticleBlockAction()); + responseData.put("commentaction", MirGlobal.abuse().getCommentBlockAction()); + + ServletHelper.generateResponse(aResponse.getWriter(), responseData, "abuse.template"); + } + catch (Throwable e) { + throw new ServletModuleFailure(e); + } + } + + public void savesettings(HttpServletRequest aRequest, HttpServletResponse aResponse) { + try { + HTTPRequestParser parser = new HTTPRequestParser(aRequest); + + MirGlobal.abuse().setOpenPostingDisabled(parser.getParameterWithDefault("disableop", "").equals("1")); + MirGlobal.abuse().setOpenPostingPassword(parser.getParameterWithDefault("passwordop", "").equals("1")); + MirGlobal.abuse().setLogEnabled(parser.getParameterWithDefault("logenabled", "").equals("1")); + + try { + MirGlobal.abuse().setLogSize(parser.getIntegerWithDefault("logsize", MirGlobal.abuse().getLogSize())); + } + catch (Throwable t) { + } + + MirGlobal.abuse().setCookieOnBlock(parser.getParameterWithDefault("usecookies", "").equals("1")); + + MirGlobal.abuse().setArticleBlockAction(parser.getParameter("articleaction")); + MirGlobal.abuse().setCommentBlockAction(parser.getParameter("commentaction")); + + MirGlobal.abuse().save(); + + showsettings(aRequest, aResponse); + } + catch (Throwable e) { + throw new ServletModuleFailure(e); + } + } + + public void showlog(HttpServletRequest aRequest, HttpServletResponse aResponse) { + URLBuilder urlBuilder = new URLBuilder(); + int count; + + try { + Map responseData = ServletHelper.makeGenerationData(new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}); + urlBuilder.setValue("module", "Abuse"); + urlBuilder.setValue("do", "showlog"); + responseData.put("thisurl", urlBuilder.getQuery()); + + responseData.put("log", MirGlobal.abuse().getLog()); + + ServletHelper.generateResponse(aResponse.getWriter(), responseData, "abuse.log.template"); + } + catch (Throwable e) { + throw new ServletModuleFailure(e); + } + } } \ No newline at end of file diff --git a/source/mircoders/servlet/ServletModuleOpenIndy.java b/source/mircoders/servlet/ServletModuleOpenIndy.java index dac1b8e4..890c57c2 100755 --- a/source/mircoders/servlet/ServletModuleOpenIndy.java +++ b/source/mircoders/servlet/ServletModuleOpenIndy.java @@ -1,1159 +1,1291 @@ -/* - * Copyright (C) 2001, 2002 The Mir-coders group - * - * This file is part of Mir. - * - * Mir is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Mir is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Mir; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * In addition, as a special exception, The Mir-coders gives permission to link - * the code of this program with the com.oreilly.servlet library, any library - * licensed under the Apache Software License, The Sun (tm) Java Advanced - * Imaging library (JAI), The Sun JIMI library (or with modified versions of - * the above that use the same license as the above), and distribute linked - * combinations including the two. You must obey the GNU General Public - * License in all respects for all of the code used other than the above - * mentioned libraries. If you modify this file, you may extend this exception - * to your version of the file, but you are not obligated to do so. If you do - * not wish to do so, delete this exception statement from your version. - */ - -package mircoders.servlet; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.Vector; -import java.util.Locale; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import gnu.regexp.RE; -import gnu.regexp.REMatch; -import gnu.regexp.REMatchEnumeration; -import gnu.regexp.REException; - - -import org.apache.commons.net.smtp.SMTPClient; -import org.apache.commons.net.smtp.SMTPReply; -//import org.apache.fop.apps.Driver; -//import org.apache.fop.apps.XSLTInputHandler; -import org.apache.log.Hierarchy; -import org.apache.log.Priority; -import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.document.Document; -import org.apache.lucene.queryParser.QueryParser; -import org.apache.lucene.search.Hits; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Searcher; -import org.apache.struts.util.MessageResources; -import mir.entity.Entity; -import mir.entity.EntityList; -import mir.generator.Generator; -import mir.log.LoggerWrapper; -import mir.misc.FileHandler; -import mir.misc.StringUtil; -import mir.misc.WebdbMultipartRequest; -import mir.servlet.ServletModule; -import mir.servlet.ServletModuleExc; -import mir.servlet.ServletModuleFailure; -import mir.servlet.ServletModuleUserExc; -import mir.storage.StorageObjectFailure; -import mir.util.ExceptionFunctions; -import mir.util.HTTPRequestParser; -import mir.util.StringRoutines; -import mircoders.entity.EntityComment; -import mircoders.entity.EntityContent; -import mircoders.global.MirGlobal; -import mircoders.media.MediaRequest; -import mircoders.media.UnsupportedMediaFormatExc; -import mircoders.module.ModuleComment; -import mircoders.module.ModuleContent; -import mircoders.module.ModuleImages; -import mircoders.module.ModuleTopics; -import mircoders.pdf.PDFGenerator; -import mircoders.search.AudioSearchTerm; -import mircoders.search.ContentSearchTerm; -import mircoders.search.ImagesSearchTerm; -import mircoders.search.KeywordSearchTerm; -import mircoders.search.TextSearchTerm; -import mircoders.search.TopicSearchTerm; -import mircoders.search.UnIndexedSearchTerm; -import mircoders.search.VideoSearchTerm; -import mircoders.storage.DatabaseComment; -import mircoders.storage.DatabaseContent; -import mircoders.storage.DatabaseContentToMedia; -import mircoders.storage.DatabaseContentToTopics; -import mircoders.storage.DatabaseImages; -import mircoders.storage.DatabaseLanguage; -import mircoders.storage.DatabaseTopics; - -/* - * ServletModuleOpenIndy - - * is the open-access-servlet, which is responsible for - * adding comments to articles & - * open-postings to the newswire - * - * @author mir-coders group - * @version $Id: ServletModuleOpenIndy.java,v 1.73 2003/04/14 19:19:16 john Exp $ - * - */ - -public class ServletModuleOpenIndy extends ServletModule -{ - - private String commentFormTemplate, commentFormDoneTemplate, commentFormDupeTemplate; - private String postingFormTemplate, postingFormDoneTemplate, postingFormDupeTemplate; - private String searchResultsTemplate; - private String prepareMailTemplate,sentMailTemplate; - private ModuleContent contentModule; - private ModuleComment commentModule; - private ModuleImages imageModule; - private ModuleTopics topicsModule; - private String directOp ="yes"; - // Singelton / Kontruktor - private static ServletModuleOpenIndy instance = new ServletModuleOpenIndy(); - public static ServletModule getInstance() { return instance; } - - private ServletModuleOpenIndy() { - super(); - try { - logger = new LoggerWrapper("ServletModule.OpenIndy"); - - commentFormTemplate = configuration.getString("ServletModule.OpenIndy.CommentTemplate"); - commentFormDoneTemplate = configuration.getString("ServletModule.OpenIndy.CommentDoneTemplate"); - commentFormDupeTemplate = configuration.getString("ServletModule.OpenIndy.CommentDupeTemplate"); - postingFormTemplate = configuration.getString("ServletModule.OpenIndy.PostingTemplate"); - postingFormDoneTemplate = configuration.getString("ServletModule.OpenIndy.PostingDoneTemplate"); - postingFormDupeTemplate = configuration.getString("ServletModule.OpenIndy.PostingDupeTemplate"); - searchResultsTemplate = configuration.getString("ServletModule.OpenIndy.SearchResultsTemplate"); - prepareMailTemplate = configuration.getString("ServletModule.OpenIndy.PrepareMailTemplate"); - sentMailTemplate = configuration.getString("ServletModule.OpenIndy.SentMailTemplate"); - directOp = configuration.getString("DirectOpenposting").toLowerCase(); - mainModule = new ModuleComment(DatabaseComment.getInstance()); - contentModule = new ModuleContent(DatabaseContent.getInstance()); - topicsModule = new ModuleTopics(DatabaseTopics.getInstance()); - imageModule = new ModuleImages(DatabaseImages.getInstance()); - defaultAction="addposting"; - } - catch (StorageObjectFailure e) { - logger.error("servletmoduleopenindy could not be initialized: " + e.getMessage()); - } - } - - /** - * Method to return an "apology" when open postings are disabled - * - * @param aRequest - * @param aResponse - * @throws ServletModuleExc - * @throws ServletModuleUserExc - * @throws ServletModuleFailure - */ - public void openPostingDisabled(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { - deliver(aRequest, aResponse, (Map) null, null, - configuration.getString("ServletModule.OpenIndy.PostingDisabledTemplate")); - } - - - /** - * Method for making a comment - */ - - public void addcomment(HttpServletRequest req, HttpServletResponse res) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure - { - if (MirGlobal.abuse().getOpenPostingDisabled()) { - openPostingDisabled(req, res); - - return; - } - - String aid = req.getParameter("aid"); // the article id the comment will belong to -/* - String language = req.getParameter("language"); - */ - - if (aid!=null && !aid.equals("")) { - try { - Map mergeData = new HashMap(); - - // onetimepasswd - if (MirGlobal.abuse().getOpenPostingPassword()) { - String passwd = this.createOneTimePasswd(); - HttpSession session = req.getSession(false); - session.setAttribute("passwd", passwd); - mergeData.put("passwd", passwd); - } - else { - mergeData.put("passwd", (String) null); - } -/* - if (language != null) { - HttpSession session = req.getSession(false); - session.setAttribute("Locale", new Locale(language, "")); - session.setAttribute("language", language); - } -*/ - mergeData.put("aid", aid); - - Map extraInfo = new HashMap(); - extraInfo.put("languagePopUpData", DatabaseLanguage.getInstance().getPopupData()); - - deliver(req, res, mergeData, extraInfo, commentFormTemplate); - } - catch (Throwable t) { - throw new ServletModuleFailure("ServletModuleOpenIndy.addcomment: " + t.getMessage(), t); - } - } - else throw new ServletModuleExc("aid not set!"); - } - - /** - * Method for inserting a comment into the Database and delivering - * the commentDone Page - */ - - public void inscomment(HttpServletRequest req, HttpServletResponse res) - throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure - { - if (MirGlobal.abuse().getOpenPostingDisabled()) { - openPostingDisabled(req, res); - - return; - } - - String aid = req.getParameter("to_media"); // the article id the comment will belong to - if (aid!=null && !aid.equals("")) - { - // ok, collecting data from form - try { - Map withValues = getIntersectingValues(req, DatabaseComment.getInstance()); - - //no html in comments(for now) - for (Iterator i=withValues.keySet().iterator(); i.hasNext(); ){ - String k=(String)i.next(); - String v=(String)withValues.get(k); - - withValues.put(k,StringUtil.removeHTMLTags(v)); - } - withValues.put("is_published","1"); - withValues.put("to_comment_status","1"); - - //checking the onetimepasswd - HttpSession session = req.getSession(false); - String sessionPasswd = (String) session.getAttribute("passwd"); - if ( sessionPasswd != null){ - String passwd = req.getParameter("passwd"); - if ( passwd == null || passwd.length()==0) { - throw new ServletModuleUserExc("comment.error.missingpassword", new String[] {}); - } - if (!sessionPasswd.equals(passwd)) { - throw new ServletModuleUserExc("comment.error.invalidpassword", new String[] {}); - } - session.invalidate(); - } - - String id = mainModule.add(withValues); - - if(id==null){ - deliver(req, res, (Map) null, null, commentFormDupeTemplate); - } - else { - MirGlobal.abuse().logComment(req.getRemoteAddr(), id, new Date(), (String) req.getHeader("User-Agent")); - - DatabaseContent.getInstance().setUnproduced("id="+aid); - - try { - EntityComment comment = (EntityComment) DatabaseComment.getInstance().selectById(id); - MirGlobal.abuse().checkComment(comment, req, res); - MirGlobal.localizer().openPostings().afterCommentPosting(comment); - } - catch (Throwable t) { - throw new ServletModuleExc(t.getMessage()); - } - } - - // redirecting to url - // should implement back to article - Map mergeData = new HashMap(); - deliver(req, res, mergeData, null, commentFormDoneTemplate); - } - catch (Throwable e) { - throw new ServletModuleFailure(e); - } - } - else throw new ServletModuleExc("aid not set!"); - - } - - /** - * Method for delivering the form-Page for open posting - */ - - public void addposting(HttpServletRequest req, HttpServletResponse res) - throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure - { - try { - if (MirGlobal.abuse().getOpenPostingDisabled()) { - openPostingDisabled(req, res); - - return; - } - - Map mergeData = new HashMap(); - - // onetimepasswd - if (MirGlobal.abuse().getOpenPostingPassword()) { - String passwd = this.createOneTimePasswd(); - HttpSession session = req.getSession(false); - session.setAttribute("passwd", passwd); - mergeData.put("passwd", passwd); - } - else { - mergeData.put("passwd", (String)null); - } - - String maxMedia = configuration.getString("ServletModule.OpenIndy.MaxMediaUploadItems"); - String defaultMedia = configuration.getString("ServletModule.OpenIndy.DefaultMediaUploadItems"); - String numOfMedia = req.getParameter("medianum"); - - if (numOfMedia == null || numOfMedia.equals("")) { - numOfMedia = defaultMedia; - } - else if (Integer.parseInt(numOfMedia) > Integer.parseInt(maxMedia)) { - numOfMedia = maxMedia; - } - - int mediaNum = Integer.parseInt(numOfMedia); - List mediaFields = new Vector(); - for (int i = 0; i < mediaNum; i++) { - Integer mNum = new Integer(i + 1); - mediaFields.add(mNum.toString()); - } - mergeData.put("medianum", numOfMedia); - mergeData.put("mediafields", mediaFields); - mergeData.put("to_topic", null); - - Map extraInfo = new HashMap(); - extraInfo.put("languagePopUpData", DatabaseLanguage.getInstance().getPopupData()); - extraInfo.put("themenPopupData", topicsModule.getTopicsAsSimpleList()); - - extraInfo.put("topics", topicsModule.getTopicsList()); - deliver(req, res, mergeData, extraInfo, postingFormTemplate); - } - catch (Throwable t) { - throw new ServletModuleFailure(t); - } - } - - /** - * Method for inserting an open posting into the Database and delivering - * the postingDone Page - */ - - public void insposting(HttpServletRequest req, HttpServletResponse res) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure - { - if (MirGlobal.abuse().getOpenPostingDisabled()) { - openPostingDisabled(req, res); - - return; - } - - Map mergeData = new HashMap(); - boolean setMedia=false; - boolean setTopic = false; - - try { - - WebdbMultipartRequest mp = null; - EntityList mediaList = null; - try { - // new MediaRequest, "1" is the id for the openPosting user - MediaRequest mediaReq = new MediaRequest("1", true); - mp = new WebdbMultipartRequest(req, (FileHandler)mediaReq); - mediaList = mediaReq.getEntityList(); - } - catch (Throwable e) { - throw new ServletModuleFailure(e); - } - - Map withValues = mp.getParameters(); - - //checking the onetimepasswd - HttpSession session = req.getSession(false); - String sessionPasswd = (String) session.getAttribute("passwd"); - if (sessionPasswd != null){ - String passwd = (String) withValues.get("passwd"); - - logger.debug("session password = " + sessionPasswd + ", form password = " + passwd); - - if ( passwd == null || passwd.length()==0) { - throw new ServletModuleUserExc("posting.error.missingpassword", new String[] {}); - } - if (!sessionPasswd.equals(passwd)) { - throw new ServletModuleUserExc("posting.error.invalidpassword", new String[] {}); - } - session.invalidate(); - } - - if ((((String)withValues.get("title")).length() == 0) || - (((String)withValues.get("description")).length() == 0) || - (((String)withValues.get("content_data")).length() == 0)) - throw new ServletModuleUserExc("posting.error.missingfield", new String[] {}); - - // call the routines that escape html - - for (Iterator i=withValues.keySet().iterator(); i.hasNext(); ){ - String k=(String)i.next(); - String v=(String)withValues.get(k); - - if (k.equals("content_data")){ - //this doesn't quite work yet, so for now, all html goes - //withValues.put(k,StringUtil.approveHTMLTags(v)); - withValues.put(k,StringUtil.deleteForbiddenTags(v)); - } - else if (k.equals("description")) { - String tmp = StringUtil.deleteForbiddenTags(v); - withValues.put(k,StringUtil.deleteHTMLTableTags(tmp)); - } - else { - withValues.put(k,StringUtil.removeHTMLTags(v)); - } - - } - - withValues.put("date", StringUtil.date2webdbDate(new GregorianCalendar())); - withValues.put("publish_path", StringUtil.webdbDate2path((String)withValues.get("date"))); - withValues.put("is_produced", "0"); - withValues.put("is_published","1"); - if (directOp.equals("yes")) - withValues.put("to_article_type","1"); - - withValues.put("to_publisher","1"); - - // inserting content into database - String cid = contentModule.add(withValues); - logger.debug("id: "+cid); - //insert was not successfull - if(cid==null){ - - //How do we know that it was not succesful cause of a - //dupe, what if it failed cause of "No space left on device"? - //Or is there something I am missing? Wouldn't it be better - //to have an explicit dupe check and then insert? I have no - //idea what I am talking about. this comment is in case - //I forget to explicitely ask. -mh - deliver(req, res, mergeData, null, postingFormDupeTemplate); - return; - } - - MirGlobal.abuse().logArticle(req.getRemoteAddr(), cid, new Date(), (String) req.getHeader("User-Agent")); - - String[] to_topicsArr = mp.getParameterValues("to_topic"); - - if (to_topicsArr != null && to_topicsArr.length > 0) { - try{ - DatabaseContentToTopics.getInstance().setTopics(cid,to_topicsArr); - setTopic = true; - } - catch (Throwable e) { - logger.error("setting content_x_topic failed"); - contentModule.deleteById(cid); - throw new ServletModuleFailure("smod - openindy :: insposting: setting content_x_topic failed: "+e.toString(), e); - } //end try - } //end if - - //if we're here all is ok... associate the media to the article - for(int i=0;i= totalHits) - newPosition=totalHits-1; - session.setAttribute("positionInResults",new Integer(newPosition)); - } - else { - if (searchForwardValue != null){ - int totalHits = ((Integer) session.getAttribute("numberOfHits")).intValue(); - int newPosition=((Integer)session.getAttribute("positionInResults")).intValue()+increment; - if (newPosition<0) - newPosition=0; - if (newPosition >= totalHits) - newPosition=totalHits-1; - - session.setAttribute("positionInResults",new Integer(newPosition)); - } - else { - String indexPath=configuration.getString("IndexPath"); - - - String creatorFragment = creatorTerm.makeTerm(req); - if (creatorFragment != null){ - queryString = queryString + " +" + creatorFragment; - } - - // search title, description, and content for something - // the contentTerm uses param "search_boolean" to combine its terms - String contentFragment = contentTerm.makeTerm(req); - if (contentFragment != null){ - logger.debug("contentFragment: " + contentFragment); - queryString = queryString + " +" + contentFragment; - } - - String topicFragment = topicTerm.makeTerm(req); - if (topicFragment != null){ - queryString = queryString + " +" + topicFragment; - } - - String imagesFragment = imagesTerm.makeTerm(req); - if (imagesFragment != null){ - queryString = queryString + " +" + imagesFragment; - } - - String audioFragment = audioTerm.makeTerm(req); - if (audioFragment != null){ - queryString = queryString + " +" + audioFragment; - } - - String videoFragment = videoTerm.makeTerm(req); - if (videoFragment != null){ - queryString = queryString + " +" + videoFragment; - } - - if (queryString == null || queryString == ""){ - queryString = ""; - } - else{ - try{ - Searcher searcher = null; - try { - searcher = new IndexSearcher(indexPath); - } - catch(IOException e) { - logger.debug("Can't open indexPath: " + indexPath); - throw new ServletModuleExc("Problem with Search Index! : "+ e.toString()); - } - - Query query = null; - try { - query = QueryParser.parse(queryString, "content", new StandardAnalyzer()); - } - catch(Exception e) { - searcher.close(); - logger.debug("Query don't parse: " + queryString); - throw new ServletModuleExc("Problem with Query String! (was '"+queryString+"')"); - } - - Hits hits = null; - try { - hits = searcher.search(query); - } - catch(IOException e) { - searcher.close(); - logger.debug("Can't get hits: " + e.toString()); - throw new ServletModuleExc("Problem getting hits!"); - } - - int start = 0; - int end = hits.length(); - - String sortBy=req.getParameter("search_sort"); - if (sortBy == null || sortBy.equals("")){ - throw new ServletModuleExc("Please let me sort by something!(missing search_sort)"); - } - - // here is where the documents will go for storage across sessions - ArrayList theDocumentsSorted = new ArrayList(); - - if (sortBy.equals("score")){ - for(int i = start; i < end; i++) { - theDocumentsSorted.add(hits.doc(i)); - } - } - else{ - // then we'll sort by date! - Map dateToPosition = new HashMap(end,1.0F); //we know how big it will be - for(int i = start; i < end; i++) { - String creationDate=(hits.doc(i)).get("creationDate"); - // do a little dance in case two contents created at the same second! - if (dateToPosition.containsKey(creationDate)){ - ((ArrayList) (dateToPosition.get(creationDate))).add(new Integer(i)); - } - else{ - ArrayList thePositions = new ArrayList(); - thePositions.add(new Integer(i)); - dateToPosition.put(creationDate,thePositions); - } - } - Set keys = dateToPosition.keySet(); - ArrayList keyList= new ArrayList(keys); - Collections.sort(keyList); - if (sortBy.equals("date_desc")){ - Collections.reverse(keyList); - } - else{ - if (!sortBy.equals("date_asc")){ - throw new ServletModuleExc("don't know how to sort by: "+ sortBy); - } - } - ListIterator keyTraverser = keyList.listIterator(); - while (keyTraverser.hasNext()){ - ArrayList positions = (ArrayList)dateToPosition.get((keyTraverser.next())); - ListIterator positionsTraverser=positions.listIterator(); - while (positionsTraverser.hasNext()){ - theDocumentsSorted.add(hits.doc(((Integer)(positionsTraverser.next())).intValue())); - } - } - } - - try{ - searcher.close(); - } - catch (IOException e){ - logger.debug("Can't close searcher: " + e.toString()); - throw new ServletModuleFailure("Problem closing searcher(normal):" + e.getMessage(), e); - } - - - session.removeAttribute("numberOfHits"); - session.removeAttribute("theDocumentsSorted"); - session.removeAttribute("positionInResults"); - - session.setAttribute("numberOfHits",new Integer(end)); - session.setAttribute("theDocumentsSorted",theDocumentsSorted); - session.setAttribute("positionInResults",new Integer(0)); - - } - catch (IOException e){ - logger.debug("Can't close searcher: " + e.toString()); - throw new ServletModuleFailure("Problem closing searcher: " + e.getMessage(), e); - } - } - } - } - - try { - ArrayList theDocs = (ArrayList)session.getAttribute("theDocumentsSorted"); - if (theDocs != null){ - - mergeData.put("numberOfHits", ((Integer)session.getAttribute("numberOfHits")).toString()); - List theHits = new Vector(); - int pIR=((Integer)session.getAttribute("positionInResults")).intValue(); - int terminus; - int numHits=((Integer)session.getAttribute("numberOfHits")).intValue(); - - if (!(pIR+increment>=numHits)){ - mergeData.put("hasNext","y"); - } - else { - mergeData.put("hasNext", null); - } - if (pIR>0){ - mergeData.put("hasPrevious","y"); - } - else { - mergeData.put("hasPrevious", null); - } - - if ((pIR+increment)>numHits){ - terminus=numHits; - } - else { - terminus=pIR+increment; - } - for(int i = pIR; i < terminus; i++) { - Map h = new HashMap(); - Document theHit = (Document)theDocs.get(i); - whereTerm.returnMeta(h,theHit); - creatorTerm.returnMeta(h,theHit); - titleTerm.returnMeta(h,theHit); - descriptionTerm.returnMeta(h,theHit); - dateTerm.returnMeta(h,theHit); - imagesTerm.returnMeta(h,theHit); - audioTerm.returnMeta(h,theHit); - videoTerm.returnMeta(h,theHit); - theHits.add(h); - } - mergeData.put("hits",theHits); - } - } - catch (Throwable e) { - logger.error("Can't iterate over hits: " + e.toString()); - - throw new ServletModuleFailure("Problem getting hits: " + e.getMessage(), e); - } - - mergeData.put("queryString",queryString); - - deliver(req, res, mergeData, null, searchResultsTemplate); - } - catch (NullPointerException n){ - throw new ServletModuleFailure("Null Pointer: "+n.toString(), n); - } - } - - /* - * Method for dynamically generating a pdf using iText - */ - - - public void getpdf(HttpServletRequest req, HttpServletResponse res) - throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { - String ID_REQUEST_PARAM = "id"; - try { - String idParam = req.getParameter(ID_REQUEST_PARAM); - if (idParam != null) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - PDFGenerator pdfMaker = new PDFGenerator(out); - - RE re = new RE("[0-9]+"); - - - REMatch[] idMatches=re.getAllMatches(idParam); - - if (idMatches.length > 1){ - pdfMaker.addLine(); - for (int i = 0; i < idMatches.length; i++){ - REMatch aMatch = idMatches[i]; - String id=aMatch.toString(); - EntityContent contentEnt = (EntityContent)contentModule.getById(id); - pdfMaker.addIndexItem(contentEnt); - - } - } - - for (int i = 0; i < idMatches.length; i++){ - REMatch aMatch = idMatches[i]; - - String id=aMatch.toString(); - - EntityContent contentEnt = (EntityContent)contentModule.getById(id); - pdfMaker.add(contentEnt); - - } - - pdfMaker.stop(); - res.setContentType("application/pdf"); - byte[] content = out.toByteArray(); - res.setContentLength(content.length); - res.getOutputStream().write(content); - res.getOutputStream().flush(); - - } - else { - throw new ServletModuleExc("Missing id."); - } - } - catch (Throwable t) { - logger.error(t.toString()); - throw new ServletModuleFailure(t); - } - - } - - - /* - * Method for dynamically generating a pdf from a fo file - * (deprecated until fop gets its act together regarding floats) - */ - - /* - public void getpdf(HttpServletRequest req, HttpServletResponse res) - throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { - String ID_REQUEST_PARAM = "id"; - String language = req.getParameter("language"); - String generateFO=configuration.getString("GenerateFO"); - String generatePDF=configuration.getString("GeneratePDF"); - - - //don't do anything if we are not making FO files, or if we are - //pregenerating PDF's - if (generateFO.equals("yes") && generatePDF.equals("no")){ - //fop complains unless you do the logging this way - org.apache.log.Logger log = null; - Hierarchy hierarchy = Hierarchy.getDefaultHierarchy(); - log = hierarchy.getLoggerFor("fop"); - log.setPriority(Priority.WARN); - - String producerStorageRoot=configuration.getString("Producer.StorageRoot"); - String producerDocRoot=configuration.getString("Producer.DocRoot"); - // String templateDir=MirConfig.getPropWithHome("HTMLTemplateProcessor.Dir"); - String xslSheet=configuration.getString("Producer.HTML2FOStyleSheet"); - try { - String idParam = req.getParameter(ID_REQUEST_PARAM); - if (idParam != null) { - EntityContent contentEnt = - (EntityContent)contentModule.getById(idParam); - String publishPath = StringUtil.webdbDate2path(contentEnt.getValue("date")); - String foFile; - - if (language == null){ - foFile = producerStorageRoot + producerDocRoot + "/" - + publishPath + idParam + ".fo"; - } - else{ - foFile = producerStorageRoot + producerDocRoot + "/" - + language + publishPath + idParam + ".fo"; - } - logger.debug("USING FILES" + foFile + " and " + xslSheet); - XSLTInputHandler input = new XSLTInputHandler(new File(foFile), - new File(xslSheet)); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - res.setContentType("application/pdf"); - - Driver driver = new Driver(); - driver.setLogger(log); - driver.setRenderer(Driver.RENDER_PDF); - driver.setOutputStream(out); - driver.render(input.getParser(), input.getInputSource()); - - byte[] content = out.toByteArray(); - res.setContentLength(content.length); - res.getOutputStream().write(content); - res.getOutputStream().flush(); - } - else { - throw new ServletModuleExc("Missing id."); - } - } - catch (Throwable t) { - logger.error(t.toString()); - - throw new ServletModuleFailure(t); - } - } - else { - throw new ServletModuleExc("Can't generate a PDF because the config tells me not to."); - } - } - */ - protected String createOneTimePasswd(){ - Random r = new Random(); - int random = r.nextInt(); - long l = System.currentTimeMillis(); - l = (l*l*l*l)/random; - if(l<0) l = l * -1; - String returnString = ""+l; - - return returnString.substring(5); - } - - - /* this is an overwritten method of ServletModule in order - to use different bundles for open and admin */ -/* public void deliver(HttpServletRequest req, HttpServletResponse res, - TemplateModelRoot rtm, TemplateModelRoot popups, - String templateFilename) throws ServletModuleFailure - { - } -*/ - public void deliver(HttpServletRequest aRequest, HttpServletResponse aResponse, Map aData, Map anExtra, String aGenerator) - throws ServletModuleFailure { - try { - deliver(aResponse.getWriter(), aRequest, aResponse, aData, anExtra, aGenerator); - } - catch (Throwable t) { - throw new ServletModuleFailure(t); - } - } - - public void deliver(PrintWriter anOutputWriter, HttpServletRequest aRequest, HttpServletResponse aResponse, Map aData, Map anExtra, String aGenerator) - throws ServletModuleFailure { - try { - Map responseData = ServletHelper.makeGenerationData(new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}, "bundles.open"); - responseData.put("data", aData); - responseData.put("extra", anExtra); - - - Generator generator = MirGlobal.localizer().generators().makeOpenPostingGeneratorLibrary().makeGenerator(aGenerator); - generator.generate(anOutputWriter, responseData, logger.asPrintWriter(logger.INFO_MESSAGE)); - - anOutputWriter.close(); - } - catch (Throwable e) { - logger.error("Error while generating " + aGenerator + ": " + e.getMessage()); - - throw new ServletModuleFailure(e); - } - } - - public void handleError(HttpServletRequest aRequest, HttpServletResponse aResponse,PrintWriter out, Throwable anException) { - try { - logger.error("error: " + anException); - anException.printStackTrace(); - Map data = new HashMap(); - - data.put("errorstring", anException.getMessage()); - data.put("date", StringUtil.date2readableDateTime(new GregorianCalendar())); - - deliver(out, aRequest, aResponse, data, null, configuration.getString("ServletModule.OpenIndy.ErrorTemplate")); - } - catch (Throwable e) { - throw new ServletModuleFailure(e); - } - } - - public void handleUserError(HttpServletRequest aRequest, HttpServletResponse aResponse, - PrintWriter out, ServletModuleUserExc anException) { - try { - logger.warn("user error: " + anException.getMessage()); - Map data = new HashMap(); - - MessageResources messages = MessageResources.getMessageResources("bundles.open"); - data.put("errorstring", - messages.getMessage(getLocale(aRequest), anException.getMessage(), anException.getParameters()) - ); - data.put("date", StringUtil.date2readableDateTime(new GregorianCalendar())); - - deliver(out, aRequest, aResponse, data, null, configuration.getString("ServletModule.OpenIndy.UserErrorTemplate")); - } - catch (Throwable e) { - throw new ServletModuleFailure(e); - } - } -} +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + +package mircoders.servlet; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import java.util.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUtils; + +import gnu.regexp.RE; +import gnu.regexp.REMatch; +import gnu.regexp.REMatchEnumeration; +import gnu.regexp.REException; + +import org.apache.commons.net.smtp.SMTPClient; +import org.apache.commons.net.smtp.SMTPReply; +import org.apache.log.Hierarchy; +import org.apache.log.Priority; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.queryParser.QueryParser; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Searcher; +import org.apache.struts.util.MessageResources; + +import mir.entity.Entity; +import mir.entity.EntityList; +import mir.generator.Generator; +import mir.log.LoggerWrapper; +import mir.misc.FileHandler; +import mir.misc.StringUtil; +import mir.misc.WebdbMultipartRequest; +import mir.servlet.ServletModule; +import mir.servlet.ServletModuleExc; +import mir.servlet.ServletModuleFailure; +import mir.servlet.ServletModuleUserExc; +import mir.storage.StorageObjectFailure; +import mir.util.*; +import mircoders.pdf.PDFGenerator; +import mir.session.*; +import mir.util.HTTPRequestParser; +import mir.util.StringRoutines; +import mircoders.entity.EntityComment; +import mircoders.entity.EntityContent; +import mircoders.global.MirGlobal; +import mircoders.media.*; +import mircoders.media.UnsupportedMediaFormatExc; +import mircoders.module.ModuleComment; +import mircoders.module.ModuleContent; +import mircoders.module.ModuleImages; +import mircoders.module.ModuleTopics; +import mircoders.search.AudioSearchTerm; +import mircoders.search.ContentSearchTerm; +import mircoders.search.ImagesSearchTerm; +import mircoders.search.KeywordSearchTerm; +import mircoders.search.TextSearchTerm; +import mircoders.search.TopicSearchTerm; +import mircoders.search.UnIndexedSearchTerm; +import mircoders.search.VideoSearchTerm; +import mircoders.storage.DatabaseComment; +import mircoders.storage.DatabaseContent; +import mircoders.storage.DatabaseContentToMedia; +import mircoders.storage.DatabaseCommentToMedia; +import mircoders.storage.DatabaseContentToTopics; +import mircoders.storage.DatabaseImages; +import mircoders.storage.DatabaseLanguage; +import mircoders.storage.DatabaseTopics; + +/* + * ServletModuleOpenIndy - + * is the open-access-servlet, which is responsible for + * adding comments to articles & + * open-postings to the newswire + * + * @author mir-coders group + * @version $Id: ServletModuleOpenIndy.java,v 1.74 2003/04/16 03:26:46 zapata Exp $ + * + */ + +public class ServletModuleOpenIndy extends ServletModule +{ + + private String commentFormTemplate, commentFormDoneTemplate, commentFormDupeTemplate; + private String postingFormTemplate, postingFormDoneTemplate, postingFormDupeTemplate; + private String searchResultsTemplate; + private String prepareMailTemplate,sentMailTemplate; + private ModuleContent contentModule; + private ModuleComment commentModule; + private ModuleImages imageModule; + private ModuleTopics topicsModule; + private String directOp ="yes"; + // Singelton / Kontruktor + private static ServletModuleOpenIndy instance = new ServletModuleOpenIndy(); + public static ServletModule getInstance() { return instance; } + + private ServletModuleOpenIndy() { + super(); + try { + logger = new LoggerWrapper("ServletModule.OpenIndy"); + + commentFormTemplate = configuration.getString("ServletModule.OpenIndy.CommentTemplate"); + commentFormDoneTemplate = configuration.getString("ServletModule.OpenIndy.CommentDoneTemplate"); + commentFormDupeTemplate = configuration.getString("ServletModule.OpenIndy.CommentDupeTemplate"); + + postingFormTemplate = configuration.getString("ServletModule.OpenIndy.PostingTemplate"); + postingFormDoneTemplate = configuration.getString("ServletModule.OpenIndy.PostingDoneTemplate"); + postingFormDupeTemplate = configuration.getString("ServletModule.OpenIndy.PostingDupeTemplate"); + + searchResultsTemplate = configuration.getString("ServletModule.OpenIndy.SearchResultsTemplate"); + prepareMailTemplate = configuration.getString("ServletModule.OpenIndy.PrepareMailTemplate"); + sentMailTemplate = configuration.getString("ServletModule.OpenIndy.SentMailTemplate"); + directOp = configuration.getString("DirectOpenposting").toLowerCase(); + commentModule = new ModuleComment(DatabaseComment.getInstance()); + mainModule = commentModule; + contentModule = new ModuleContent(DatabaseContent.getInstance()); + topicsModule = new ModuleTopics(DatabaseTopics.getInstance()); + imageModule = new ModuleImages(DatabaseImages.getInstance()); + defaultAction="addposting"; + } + catch (StorageObjectFailure e) { + logger.error("servletmoduleopenindy could not be initialized: " + e.getMessage()); + } + } + + /** + * Method to return an "apology" when open postings are disabled + * + * @param aRequest + * @param aResponse + * @throws ServletModuleExc + * @throws ServletModuleUserExc + * @throws ServletModuleFailure + */ + public void openPostingDisabled(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { + deliver(aRequest, aResponse, (Map) null, null, + configuration.getString("ServletModule.OpenIndy.PostingDisabledTemplate")); + } + + /** + * Method for making a comment + */ + + public void addcomment(HttpServletRequest req, HttpServletResponse res) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { + if (MirGlobal.abuse().getOpenPostingDisabled()) { + openPostingDisabled(req, res); + + return; + } + + String aid = req.getParameter("aid"); // the article id the comment will belong to + + if (aid != null && !aid.equals("")) { + try { + Map mergeData = new HashMap(); + + // onetimepasswd + if (MirGlobal.abuse().getOpenPostingPassword()) { + String passwd = this.createOneTimePasswd(); + HttpSession session = req.getSession(false); + session.setAttribute("passwd", passwd); + mergeData.put("passwd", passwd); + } + else { + mergeData.put("passwd", (String)null); + } + mergeData.put("aid", aid); + + Map extraInfo = new HashMap(); + extraInfo.put("languagePopUpData", DatabaseLanguage.getInstance().getPopupData()); + + deliver(req, res, mergeData, extraInfo, commentFormTemplate); + } + catch (Throwable t) { + throw new ServletModuleFailure("ServletModuleOpenIndy.addcomment: " + t.getMessage(), t); + } + } + else + throw new ServletModuleExc("aid not set!"); + } + + /** + * Method for inserting a comment into the Database and delivering + * the commentDone Page + */ + + public void inscomment(HttpServletRequest req, HttpServletResponse res) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { + if (MirGlobal.abuse().getOpenPostingDisabled()) { + openPostingDisabled(req, res); + + return; + } + + String aid = req.getParameter("to_media"); // the article id the comment will belong to + if (aid != null && !aid.equals("")) { + // ok, collecting data from form + try { + Map withValues = getIntersectingValues(req, DatabaseComment.getInstance()); + + //no html in comments(for now) + for (Iterator i = withValues.keySet().iterator(); i.hasNext(); ) { + String k = (String) i.next(); + String v = (String) withValues.get(k); + + withValues.put(k, StringUtil.removeHTMLTags(v)); + } + withValues.put("is_published", "1"); + withValues.put("to_comment_status", "1"); + withValues.put("is_html", "0"); + + //checking the onetimepasswd + HttpSession session = req.getSession(false); + String sessionPasswd = (String) session.getAttribute("passwd"); + if (sessionPasswd != null) { + String passwd = req.getParameter("passwd"); + if (passwd == null || passwd.length() == 0) { + throw new ServletModuleUserExc("comment.error.missingpassword", new String[] {}); + } + if (!sessionPasswd.equals(passwd)) { + throw new ServletModuleUserExc("comment.error.invalidpassword", new String[] {}); + } + session.invalidate(); + } + + String id = mainModule.add(withValues); + + SimpleResponse response = new SimpleResponse(); + response.setResponseGenerator(commentFormDoneTemplate); + + if (id == null) { + deliver(req, res, (Map)null, null, commentFormDupeTemplate); + } + else { + DatabaseContent.getInstance().setUnproduced("id=" + aid); + + try { + EntityComment comment = (EntityComment) DatabaseComment.getInstance().selectById(id); + MirGlobal.localizer().openPostings().afterCommentPosting(comment); + MirGlobal.abuse().checkComment( + comment, new HTTPAdapters.HTTPRequestAdapter(req), res); + } + catch (Throwable t) { + throw new ServletModuleExc(t.getMessage()); + } + } + + // redirecting to url + // should implement back to article + deliver(req, res, response.getResponseValues(), null, response.getResponseGenerator()); + } + catch (Throwable e) { + throw new ServletModuleFailure(e); + } + } + else + throw new ServletModuleExc("aid not set!"); + + } + + /** + * Method for delivering the form-Page for open posting + */ + + public void addposting(HttpServletRequest req, HttpServletResponse res) + throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure + { + try { + if (MirGlobal.abuse().getOpenPostingDisabled()) { + openPostingDisabled(req, res); + + return; + } + + Map mergeData = new HashMap(); + + // onetimepasswd + if (MirGlobal.abuse().getOpenPostingPassword()) { + String passwd = generateOnetimePassword(); + HttpSession session = req.getSession(false); + session.setAttribute("passwd", passwd); + mergeData.put("passwd", passwd); + } + else { + mergeData.put("passwd", (String)null); + } + + String maxMedia = configuration.getString("ServletModule.OpenIndy.MaxMediaUploadItems"); + String defaultMedia = configuration.getString("ServletModule.OpenIndy.DefaultMediaUploadItems"); + String numOfMedia = req.getParameter("medianum"); + + if (numOfMedia == null || numOfMedia.equals("")) { + numOfMedia = defaultMedia; + } + else if (Integer.parseInt(numOfMedia) > Integer.parseInt(maxMedia)) { + numOfMedia = maxMedia; + } + + int mediaNum = Integer.parseInt(numOfMedia); + List mediaFields = new Vector(); + for (int i = 0; i < mediaNum; i++) { + Integer mNum = new Integer(i + 1); + mediaFields.add(mNum.toString()); + } + mergeData.put("medianum", numOfMedia); + mergeData.put("mediafields", mediaFields); + mergeData.put("to_topic", null); + + Map extraInfo = new HashMap(); + extraInfo.put("languagePopUpData", DatabaseLanguage.getInstance().getPopupData()); + extraInfo.put("themenPopupData", topicsModule.getTopicsAsSimpleList()); + + extraInfo.put("topics", topicsModule.getTopicsList()); + deliver(req, res, mergeData, extraInfo, postingFormTemplate); + } + catch (Throwable t) { + throw new ServletModuleFailure(t); + } + } + + /** + * Method for inserting an open posting into the Database and delivering + * the postingDone Page + */ + + public void insposting(HttpServletRequest req, HttpServletResponse res) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure + { + if (MirGlobal.abuse().getOpenPostingDisabled()) { + openPostingDisabled(req, res); + + return; + } + + Map mergeData = new HashMap(); + boolean setMedia=false; + boolean setTopic = false; + + try { + + WebdbMultipartRequest mp = null; + EntityList mediaList = null; + try { + // new MediaRequest, "1" is the id for the openPosting user + MediaRequest mediaReq = new MediaRequest("1", true); + mp = new WebdbMultipartRequest(req, (FileHandler)mediaReq); + mediaList = mediaReq.getEntityList(); + } + catch (Throwable e) { + throw new ServletModuleFailure(e); + } + + Map withValues = mp.getParameters(); + + //checking the onetimepasswd + HttpSession session = req.getSession(false); + String sessionPasswd = (String) session.getAttribute("passwd"); + if (sessionPasswd != null){ + String passwd = (String) withValues.get("passwd"); + + if ( passwd == null || passwd.length()==0) { + throw new ServletModuleUserExc("posting.error.missingpassword", new String[] {}); + } + if (!sessionPasswd.equals(passwd)) { + throw new ServletModuleUserExc("posting.error.invalidpassword", new String[] {}); + } + session.invalidate(); + } + + if ((((String)withValues.get("title")).length() == 0) || + (((String)withValues.get("description")).length() == 0) || + (((String)withValues.get("content_data")).length() == 0)) + throw new ServletModuleUserExc("posting.error.missingfield", new String[] {}); + + // call the routines that escape html + + for (Iterator i=withValues.keySet().iterator(); i.hasNext(); ){ + String k=(String)i.next(); + String v=(String)withValues.get(k); + + if (k.equals("content_data")){ + //this doesn't quite work yet, so for now, all html goes + //withValues.put(k,StringUtil.approveHTMLTags(v)); + withValues.put(k,StringUtil.deleteForbiddenTags(v)); + } + else if (k.equals("description")) { + String tmp = StringUtil.deleteForbiddenTags(v); + withValues.put(k,StringUtil.deleteHTMLTableTags(tmp)); + } + else { + withValues.put(k,StringUtil.removeHTMLTags(v)); + } + + } + + withValues.put("date", StringUtil.date2webdbDate(new GregorianCalendar())); + withValues.put("publish_path", StringUtil.webdbDate2path((String)withValues.get("date"))); + withValues.put("is_produced", "0"); + withValues.put("is_published","1"); + if (directOp.equals("yes")) + withValues.put("to_article_type","1"); + + withValues.put("to_publisher","1"); + + // inserting content into database + String cid = contentModule.add(withValues); + logger.debug("id: "+cid); + //insert was not successfull + if(cid==null){ + + //How do we know that it was not succesful cause of a + //dupe, what if it failed cause of "No space left on device"? + //Or is there something I am missing? Wouldn't it be better + //to have an explicit dupe check and then insert? I have no + //idea what I am talking about. this comment is in case + //I forget to explicitely ask. -mh + deliver(req, res, mergeData, null, postingFormDupeTemplate); + return; + } + + String[] to_topicsArr = mp.getParameterValues("to_topic"); + + if (to_topicsArr != null && to_topicsArr.length > 0) { + try{ + DatabaseContentToTopics.getInstance().setTopics(cid,to_topicsArr); + setTopic = true; + } + catch (Throwable e) { + logger.error("setting content_x_topic failed"); + contentModule.deleteById(cid); + throw new ServletModuleFailure("smod - openindy :: insposting: setting content_x_topic failed: "+e.toString(), e); + } //end try + } //end if + + //if we're here all is ok... associate the media to the article + for(int i=0;i0) { + MirGlobal.localizer().openPostings().processCommentPosting(request, session, response); + + ServletHelper.generateOpenPostingResponse(aResponse.getWriter(), response.getResponseValues(), response.getResponseGenerator()); + } + else { + EntityComment comment = (EntityComment) commentModule.createNew (); + comment.setValues(commentFields); + MirGlobal.abuse().checkComment(comment, aRequest, aResponse); + MirGlobal.localizer().openPostings().finishCommentPosting(request, session, comment); + + String id = comment.insert(); + if(id==null){ + MirGlobal.localizer().openPostings().afterDuplicateCommentPosting(request, session, response, comment); + + logger.info("Dupe comment rejected"); + + ServletHelper.generateOpenPostingResponse(aResponse.getWriter(), response.getResponseValues(), response.getResponseGenerator()); + } + else { + // media + List mediaItems = new Vector(); + i = request.getUploadedFiles().iterator(); + while (i.hasNext()) { + UploadedFile file = (UploadedFile) i.next(); + Entity mediaItem = MediaUploadProcessor.processMediaUpload(file); + DatabaseCommentToMedia.getInstance().addMedia(comment.getId(), mediaItem.getId()); + } + + MirGlobal.localizer().openPostings().afterCommentPosting(request, session, response, comment); + + MirGlobal.abuse().logComment(aRequest.getRemoteAddr(), id, new Date(), (String) aRequest.getHeader("User-Agent")); + DatabaseContent.getInstance().setUnproduced("id=" + comment.getValue("to_media")); + logger.info("Comment posted"); + ServletHelper.generateOpenPostingResponse(aResponse.getWriter(), response.getResponseValues(), response.getResponseGenerator()); + } + } + } + catch (Throwable t) { + ExceptionFunctions.traceCauseException(t).printStackTrace(); + + throw new ServletModuleFailure("ServletModuleOpenIndy.addcomment: " + t.getMessage(), t); + } + } +*/ + + private static final String SESSION_REQUEST_KEY="sessionid"; + + public void opensession(HttpServletRequest aRequest, HttpServletResponse aResponse) + throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { + + try { + Request request = new HTTPAdapters.HTTPParsedRequestAdapter(new HTTPParsedRequest(aRequest, 1000000, "/tmp")); + + if (aRequest.isRequestedSessionIdValid() && !aRequest.isRequestedSessionIdFromURL() && + !aRequest.getRequestedSessionId().equals(aRequest.getParameter(SESSION_REQUEST_KEY))) + aRequest.getSession().invalidate(); + + Session session = new HTTPAdapters.HTTPSessionAdapter(aRequest.getSession()); + + SimpleResponse response = new SimpleResponse( + ServletHelper.makeGenerationData(new Locale[] {getLocale(aRequest), getFallbackLocale(aRequest)}, + "bundles.open")); + + response.setResponseValue("actionURL", aResponse.encodeURL(HttpUtils.getRequestURL(aRequest).toString())+"?"+SESSION_REQUEST_KEY+"="+aRequest.getSession().getId()); + + SessionHandler handler = MirGlobal.localizer().openPostings().getOpenSessionHandler(request, session); + + handler.processRequest(request, session, response); + ServletHelper.generateOpenPostingResponse(aResponse.getWriter(), response.getResponseValues(), response.getResponseGenerator()); + } + catch (Throwable t) { + logger.error(t.toString()); + t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE)); + + throw new ServletModuleFailure(t); + } + } + + /** + * Method for preparing and sending a content as an email message + */ + + public void mail(HttpServletRequest req, HttpServletResponse res) + throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure + { + String aid = req.getParameter("mail_aid"); + if (aid == null){ + throw new ServletModuleExc("An article id must be specified in requests to email an article. Something therefore went badly wrong...."); + } + + String to = req.getParameter("mail_to"); + String from = req.getParameter("mail_from"); + String from_name = req.getParameter("mail_from_name"); + String comment = req.getParameter("mail_comment"); + String mail_language = req.getParameter("mail_language"); + + Map mergeData = new HashMap(); + + if (to == null || from == null || from_name == null|| to.equals("") || from.equals("") || from_name.equals("") || mail_language == null || mail_language.equals("")){ + + for (Enumeration theParams = req.getParameterNames(); theParams.hasMoreElements() ;) { + String pName=(String)theParams.nextElement(); + if (pName.startsWith("mail_")){ + mergeData.put( pName,req.getParameter(pName) ); + } + } + + deliver(req, res, mergeData, null, prepareMailTemplate); + } + else { + //run checks on to and from and mail_language to make sure no monkey business occurring + if (mail_language.indexOf('.') != -1 || mail_language.indexOf('/') != -1 ) { + throw new ServletModuleExc("Invalid language"); + } + if (to.indexOf('\n') != -1 + || to.indexOf('\r') != -1 + || to.indexOf(',') != -1) { + throw new ServletModuleUserExc("email.error.invalidtoaddress", new String[] {to}); + } + if (from.indexOf('\n') != -1 || from.indexOf('\r') != -1 || from.indexOf(',') != -1 ) { + throw new ServletModuleUserExc("email.error.invalidfromaddress", new String[] {from}); + } + + + EntityContent contentEnt; + try{ + contentEnt = (EntityContent)contentModule.getById(aid); + } + catch (Throwable e){ + throw new ServletModuleFailure("Couldn't get content for article "+aid + ": " + e.getMessage(), e); + } + String producerStorageRoot=configuration.getString("Producer.StorageRoot"); + String producerDocRoot=configuration.getString("Producer.DocRoot"); + String publishPath = contentEnt.getValue("publish_path"); + String txtFilePath = producerStorageRoot + producerDocRoot + "/" + mail_language + + publishPath + "/" + aid + ".txt"; + + + File inputFile = new File(txtFilePath); + String content; + + try{ + FileReader in = new FileReader(inputFile); + StringWriter out = new StringWriter(); + int c; + while ((c = in.read()) != -1) + out.write(c); + in.close(); + content= out.toString(); + } + catch (FileNotFoundException e){ + throw new ServletModuleFailure("No text file found in " + txtFilePath, e); + } + catch (IOException e){ + throw new ServletModuleFailure("Problem reading file in " + txtFilePath, e); + } + // add some headers + content = "To: " + to + "\nReply-To: "+ from + "\n" + content; + // put in the comment where it should go + if (comment != null) { + String commentTextToInsert = "\n\nAttached comment from " + from_name + ":\n" + comment; + try { + content=StringRoutines.performRegularExpressionReplacement(content,"!COMMENT!",commentTextToInsert); + } + catch (Throwable e){ + throw new ServletModuleFailure("Problem doing regular expression replacement " + e.toString(), e); + } + } + else{ + try { + content=StringRoutines.performRegularExpressionReplacement(content,"!COMMENT!",""); + } + catch (Throwable e){ + throw new ServletModuleFailure("Problem doing regular expression replacement " + e.toString(), e); + } + } + + SMTPClient client=new SMTPClient(); + try { + int reply; + client.connect(configuration.getString("ServletModule.OpenIndy.SMTPServer")); + + reply = client.getReplyCode(); + + if (!SMTPReply.isPositiveCompletion(reply)) { + client.disconnect(); + throw new ServletModuleExc("SMTP server refused connection."); + } + + client.sendSimpleMessage(configuration.getString("ServletModule.OpenIndy.EmailIsFrom"), to, content); + + client.disconnect(); + //mission accomplished + deliver(req, res, mergeData, null, sentMailTemplate); + } + catch(IOException e) { + if(client.isConnected()) { + try { + client.disconnect(); + } catch(IOException f) { + // do nothing + } + } + throw new ServletModuleFailure(e); + } + } + } + + + + /** + * Method for querying a lucene index + * + * @param req + * @param res + * @throws ServletModuleExc + * @throws ServletModuleUserExc + * @throws ServletModuleFailure + */ + + public void search(HttpServletRequest req, HttpServletResponse res) + throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { + try { + final String[] search_variables = { "search_content", "search_boolean", "search_creator", + "search_topic", "search_hasImages", "search_hasAudio", "search_hasVideo", "search_sort", + "search_submit", "search_back", "search_forward" }; + HTTPRequestParser requestParser = new HTTPRequestParser(req); + + int increment=10; + + HttpSession session = req.getSession(false); + + String queryString=""; + + Map mergeData = new HashMap(); + + KeywordSearchTerm dateTerm = new KeywordSearchTerm("date_formatted","search_date","webdb_create_formatted","webdb_create_formatted","webdb_create_formatted"); + UnIndexedSearchTerm whereTerm = new UnIndexedSearchTerm("","","","where","where"); + TextSearchTerm creatorTerm = new TextSearchTerm("creator","search_creator","creator","creator","creator"); + TextSearchTerm titleTerm = new TextSearchTerm("title","search_content","title","title","title"); + TextSearchTerm descriptionTerm = new TextSearchTerm("description","search_content","description","description","description"); + ContentSearchTerm contentTerm = new ContentSearchTerm("content_data","search_content","content","",""); + TopicSearchTerm topicTerm = new TopicSearchTerm(); + ImagesSearchTerm imagesTerm = new ImagesSearchTerm(); + AudioSearchTerm audioTerm = new AudioSearchTerm(); + VideoSearchTerm videoTerm = new VideoSearchTerm(); + + //make the query available to subsequent iterations + + Iterator j = Arrays.asList(search_variables).iterator(); + while (j.hasNext()) { + String variable = (String) j.next(); + + mergeData.put(variable, requestParser.getParameter(variable)); + } + + try{ + mergeData.put("topics", topicsModule.getTopicsAsSimpleList()); + } + catch(Throwable e) { + logger.debug("Can't get topics: " + e.toString()); + } + + String searchBackValue = req.getParameter("search_back"); + String searchForwardValue = req.getParameter("search_forward"); + + if (searchBackValue != null){ + int totalHits = ((Integer) session.getAttribute("numberOfHits")).intValue(); + int newPosition=((Integer)session.getAttribute("positionInResults")).intValue()-increment; + if (newPosition<0) + newPosition=0; + if (newPosition >= totalHits) + newPosition=totalHits-1; + session.setAttribute("positionInResults",new Integer(newPosition)); + } + else { + if (searchForwardValue != null){ + int totalHits = ((Integer) session.getAttribute("numberOfHits")).intValue(); + int newPosition=((Integer)session.getAttribute("positionInResults")).intValue()+increment; + if (newPosition<0) + newPosition=0; + if (newPosition >= totalHits) + newPosition=totalHits-1; + + session.setAttribute("positionInResults",new Integer(newPosition)); + } + else { + String indexPath=configuration.getString("IndexPath"); + + + String creatorFragment = creatorTerm.makeTerm(req); + if (creatorFragment != null){ + queryString = queryString + " +" + creatorFragment; + } + + // search title, description, and content for something + // the contentTerm uses param "search_boolean" to combine its terms + String contentFragment = contentTerm.makeTerm(req); + if (contentFragment != null){ + logger.debug("contentFragment: " + contentFragment); + queryString = queryString + " +" + contentFragment; + } + + String topicFragment = topicTerm.makeTerm(req); + if (topicFragment != null){ + queryString = queryString + " +" + topicFragment; + } + + String imagesFragment = imagesTerm.makeTerm(req); + if (imagesFragment != null){ + queryString = queryString + " +" + imagesFragment; + } + + String audioFragment = audioTerm.makeTerm(req); + if (audioFragment != null){ + queryString = queryString + " +" + audioFragment; + } + + String videoFragment = videoTerm.makeTerm(req); + if (videoFragment != null){ + queryString = queryString + " +" + videoFragment; + } + + if (queryString == null || queryString == ""){ + queryString = ""; + } + else{ + try{ + Searcher searcher = null; + try { + searcher = new IndexSearcher(indexPath); + } + catch(IOException e) { + logger.debug("Can't open indexPath: " + indexPath); + throw new ServletModuleExc("Problem with Search Index! : "+ e.toString()); + } + + Query query = null; + try { + query = QueryParser.parse(queryString, "content", new StandardAnalyzer()); + } + catch(Exception e) { + searcher.close(); + logger.debug("Query don't parse: " + queryString); + throw new ServletModuleExc("Problem with Query String! (was '"+queryString+"')"); + } + + Hits hits = null; + try { + hits = searcher.search(query); + } + catch(IOException e) { + searcher.close(); + logger.debug("Can't get hits: " + e.toString()); + throw new ServletModuleExc("Problem getting hits!"); + } + + int start = 0; + int end = hits.length(); + + String sortBy=req.getParameter("search_sort"); + if (sortBy == null || sortBy.equals("")){ + throw new ServletModuleExc("Please let me sort by something!(missing search_sort)"); + } + + // here is where the documents will go for storage across sessions + ArrayList theDocumentsSorted = new ArrayList(); + + if (sortBy.equals("score")){ + for(int i = start; i < end; i++) { + theDocumentsSorted.add(hits.doc(i)); + } + } + else{ + // then we'll sort by date! + Map dateToPosition = new HashMap(end,1.0F); //we know how big it will be + for(int i = start; i < end; i++) { + String creationDate=(hits.doc(i)).get("creationDate"); + // do a little dance in case two contents created at the same second! + if (dateToPosition.containsKey(creationDate)){ + ((ArrayList) (dateToPosition.get(creationDate))).add(new Integer(i)); + } + else{ + ArrayList thePositions = new ArrayList(); + thePositions.add(new Integer(i)); + dateToPosition.put(creationDate,thePositions); + } + } + Set keys = dateToPosition.keySet(); + ArrayList keyList= new ArrayList(keys); + Collections.sort(keyList); + if (sortBy.equals("date_desc")){ + Collections.reverse(keyList); + } + else{ + if (!sortBy.equals("date_asc")){ + throw new ServletModuleExc("don't know how to sort by: "+ sortBy); + } + } + ListIterator keyTraverser = keyList.listIterator(); + while (keyTraverser.hasNext()){ + ArrayList positions = (ArrayList)dateToPosition.get((keyTraverser.next())); + ListIterator positionsTraverser=positions.listIterator(); + while (positionsTraverser.hasNext()){ + theDocumentsSorted.add(hits.doc(((Integer)(positionsTraverser.next())).intValue())); + } + } + } + + try{ + searcher.close(); + } + catch (IOException e){ + logger.debug("Can't close searcher: " + e.toString()); + throw new ServletModuleFailure("Problem closing searcher(normal):" + e.getMessage(), e); + } + + + session.removeAttribute("numberOfHits"); + session.removeAttribute("theDocumentsSorted"); + session.removeAttribute("positionInResults"); + + session.setAttribute("numberOfHits",new Integer(end)); + session.setAttribute("theDocumentsSorted",theDocumentsSorted); + session.setAttribute("positionInResults",new Integer(0)); + + } + catch (IOException e){ + logger.debug("Can't close searcher: " + e.toString()); + throw new ServletModuleFailure("Problem closing searcher: " + e.getMessage(), e); + } + } + } + } + + try { + ArrayList theDocs = (ArrayList)session.getAttribute("theDocumentsSorted"); + if (theDocs != null){ + + mergeData.put("numberOfHits", ((Integer)session.getAttribute("numberOfHits")).toString()); + List theHits = new Vector(); + int pIR=((Integer)session.getAttribute("positionInResults")).intValue(); + int terminus; + int numHits=((Integer)session.getAttribute("numberOfHits")).intValue(); + + if (!(pIR+increment>=numHits)){ + mergeData.put("hasNext","y"); + } + else { + mergeData.put("hasNext", null); + } + if (pIR>0){ + mergeData.put("hasPrevious","y"); + } + else { + mergeData.put("hasPrevious", null); + } + + if ((pIR+increment)>numHits){ + terminus=numHits; + } + else { + terminus=pIR+increment; + } + for(int i = pIR; i < terminus; i++) { + Map h = new HashMap(); + Document theHit = (Document)theDocs.get(i); + whereTerm.returnMeta(h,theHit); + creatorTerm.returnMeta(h,theHit); + titleTerm.returnMeta(h,theHit); + descriptionTerm.returnMeta(h,theHit); + dateTerm.returnMeta(h,theHit); + imagesTerm.returnMeta(h,theHit); + audioTerm.returnMeta(h,theHit); + videoTerm.returnMeta(h,theHit); + theHits.add(h); + } + mergeData.put("hits",theHits); + } + } + catch (Throwable e) { + logger.error("Can't iterate over hits: " + e.toString()); + + throw new ServletModuleFailure("Problem getting hits: " + e.getMessage(), e); + } + + mergeData.put("queryString",queryString); + + deliver(req, res, mergeData, null, searchResultsTemplate); + } + catch (NullPointerException n){ + throw new ServletModuleFailure("Null Pointer: "+n.toString(), n); + } + } + + /* + * Method for dynamically generating a pdf using iText + */ + + + public void getpdf(HttpServletRequest req, HttpServletResponse res) + throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { + String ID_REQUEST_PARAM = "id"; + try { + String idParam = req.getParameter(ID_REQUEST_PARAM); + if (idParam != null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PDFGenerator pdfMaker = new PDFGenerator(out); + + RE re = new RE("[0-9]+"); + + + REMatch[] idMatches=re.getAllMatches(idParam); + + if (idMatches.length > 1){ + pdfMaker.addLine(); + for (int i = 0; i < idMatches.length; i++){ + REMatch aMatch = idMatches[i]; + String id=aMatch.toString(); + EntityContent contentEnt = (EntityContent)contentModule.getById(id); + pdfMaker.addIndexItem(contentEnt); + + } + } + + for (int i = 0; i < idMatches.length; i++){ + REMatch aMatch = idMatches[i]; + + String id=aMatch.toString(); + + EntityContent contentEnt = (EntityContent)contentModule.getById(id); + pdfMaker.add(contentEnt); + + } + + pdfMaker.stop(); + res.setContentType("application/pdf"); + byte[] content = out.toByteArray(); + res.setContentLength(content.length); + res.getOutputStream().write(content); + res.getOutputStream().flush(); + + } + else { + throw new ServletModuleExc("Missing id."); + } + } + catch (Throwable t) { + logger.error(t.toString()); + throw new ServletModuleFailure(t); + } + + } + + + /* + * Method for dynamically generating a pdf from a fo file + * (deprecated until fop gets its act together regarding floats) + */ + + /* + public void getpdf(HttpServletRequest req, HttpServletResponse res) + throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure { + String ID_REQUEST_PARAM = "id"; + String language = req.getParameter("language"); + String generateFO=configuration.getString("GenerateFO"); + String generatePDF=configuration.getString("GeneratePDF"); + + + //don't do anything if we are not making FO files, or if we are + //pregenerating PDF's + if (generateFO.equals("yes") && generatePDF.equals("no")){ + //fop complains unless you do the logging this way + org.apache.log.Logger log = null; + Hierarchy hierarchy = Hierarchy.getDefaultHierarchy(); + log = hierarchy.getLoggerFor("fop"); + log.setPriority(Priority.WARN); + + String producerStorageRoot=configuration.getString("Producer.StorageRoot"); + String producerDocRoot=configuration.getString("Producer.DocRoot"); + // String templateDir=MirConfig.getPropWithHome("HTMLTemplateProcessor.Dir"); + String xslSheet=configuration.getString("Producer.HTML2FOStyleSheet"); + try { + String idParam = req.getParameter(ID_REQUEST_PARAM); + if (idParam != null) { + EntityContent contentEnt = + (EntityContent)contentModule.getById(idParam); + String publishPath = StringUtil.webdbDate2path(contentEnt.getValue("date")); + String foFile; + + if (language == null){ + foFile = producerStorageRoot + producerDocRoot + "/" + + publishPath + idParam + ".fo"; + } + else{ + foFile = producerStorageRoot + producerDocRoot + "/" + + language + publishPath + idParam + ".fo"; + } + logger.debug("USING FILES" + foFile + " and " + xslSheet); + XSLTInputHandler input = new XSLTInputHandler(new File(foFile), + new File(xslSheet)); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + res.setContentType("application/pdf"); + + Driver driver = new Driver(); + driver.setLogger(log); + driver.setRenderer(Driver.RENDER_PDF); + driver.setOutputStream(out); + driver.render(input.getParser(), input.getInputSource()); + + byte[] content = out.toByteArray(); + res.setContentLength(content.length); + res.getOutputStream().write(content); + res.getOutputStream().flush(); + } + else { + throw new ServletModuleExc("Missing id."); + } + } + catch (Throwable t) { + logger.error(t.toString()); + + throw new ServletModuleFailure(t); + } + } + else { + throw new ServletModuleExc("Can't generate a PDF because the config tells me not to."); + } + } + */ + + public String generateOnetimePassword() { + Random r = new Random(); + int random = r.nextInt(); + + long l = System.currentTimeMillis(); + + l = (l * l * l * l) / random; + if (l < 0) + l = l * -1; + + String returnString = "" + l; + + return returnString.substring(5); + } + + public void deliver(HttpServletRequest aRequest, HttpServletResponse aResponse, Map aData, Map anExtra, String aGenerator) + throws ServletModuleFailure { + try { + deliver(aResponse.getWriter(), aRequest, aResponse, aData, anExtra, aGenerator); + } + catch (Throwable t) { + throw new ServletModuleFailure(t); + } + } + + public void deliver(PrintWriter anOutputWriter, HttpServletRequest aRequest, HttpServletResponse aResponse, Map aData, Map anExtra, String aGenerator) + throws ServletModuleFailure { + try { + Map responseData = ServletHelper.makeGenerationData(new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}, "bundles.open"); + responseData.put("data", aData); + responseData.put("extra", anExtra); + + + Generator generator = MirGlobal.localizer().generators().makeOpenPostingGeneratorLibrary().makeGenerator(aGenerator); + generator.generate(anOutputWriter, responseData, logger.asPrintWriter(logger.INFO_MESSAGE)); + + anOutputWriter.close(); + } + catch (Throwable e) { + logger.error("Error while generating " + aGenerator + ": " + e.getMessage()); + + throw new ServletModuleFailure(e); + } + } + + public void handleError(HttpServletRequest aRequest, HttpServletResponse aResponse,PrintWriter out, Throwable anException) { + try { + logger.error("error: " + anException); + Map data = new HashMap(); + + data.put("errorstring", anException.getMessage()); + data.put("date", StringUtil.date2readableDateTime(new GregorianCalendar())); + + deliver(out, aRequest, aResponse, data, null, configuration.getString("ServletModule.OpenIndy.ErrorTemplate")); + } + catch (Throwable e) { + throw new ServletModuleFailure(e); + } + } + + public void handleUserError(HttpServletRequest aRequest, HttpServletResponse aResponse, + PrintWriter out, ServletModuleUserExc anException) { + try { + logger.warn("user error: " + anException.getMessage()); + Map data = new HashMap(); + + MessageResources messages = MessageResources.getMessageResources("bundles.open"); + data.put("errorstring", + messages.getMessage(getLocale(aRequest), anException.getMessage(), anException.getParameters()) + ); + data.put("date", StringUtil.date2readableDateTime(new GregorianCalendar())); + + deliver(out, aRequest, aResponse, data, null, configuration.getString("ServletModule.OpenIndy.UserErrorTemplate")); + } + catch (Throwable e) { + throw new ServletModuleFailure(e); + } + } + + private String createOneTimePasswd() { + return ""; + } +} diff --git a/source/tool/BundleTool.java b/source/tool/BundleTool.java index 942a2b24..56c6e9b8 100755 --- a/source/tool/BundleTool.java +++ b/source/tool/BundleTool.java @@ -1,3 +1,34 @@ +/* + * Copyright (C) 2001, 2002 The Mir-coders group + * + * This file is part of Mir. + * + * Mir is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Mir is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mir; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, The Mir-coders gives permission to link + * the code of this program with the com.oreilly.servlet library, any library + * licensed under the Apache Software License, The Sun (tm) Java Advanced + * Imaging library (JAI), The Sun JIMI library (or with modified versions of + * the above that use the same license as the above), and distribute linked + * combinations including the two. You must obey the GNU General Public + * License in all respects for all of the code used other than the above + * mentioned libraries. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If you do + * not wish to do so, delete this exception statement from your version. + */ + package tool; import java.util.*; diff --git a/templates/admin/FUNCTIONS.template b/templates/admin/FUNCTIONS.template index 485a8340..51e10ba1 100755 --- a/templates/admin/FUNCTIONS.template +++ b/templates/admin/FUNCTIONS.template @@ -79,6 +79,23 @@ + + + + + ${label}: + + + + + + + + 4 functions to create Table rows with input-fields @@ -87,7 +104,7 @@ - + @@ -99,33 +116,52 @@ - + + + + + ${label}: + + + + checked>   + + + - - + ${label}: + +
    + ${hint} +
    - +
    - + + ${label}: + +
    + ${hint} +
    - +
    diff --git a/templates/admin/abuse.filters.template b/templates/admin/abuse.filters.template index b8410ad5..dc78231c 100755 --- a/templates/admin/abuse.filters.template +++ b/templates/admin/abuse.filters.template @@ -9,7 +9,7 @@ diff --git a/templates/admin/abuse.template b/templates/admin/abuse.template index 0c82b95e..25a8083f 100755 --- a/templates/admin/abuse.template +++ b/templates/admin/abuse.template @@ -25,79 +25,24 @@ ${lang("abuse.setting")} ${lang("abuse.value")} - - - ${lang("abuse.disableopenpostings")} - - - checked="1"> - - - - - ${lang("abuse.openpostingpassword")} - - - checked="1"> - - + + +   - - - - ${lang("abuse.logpostings")} - - - checked="1"> - - - - - ${lang("abuse.logsize")} - - - - - + + +   - - - ${lang("abuse.cookies")} - - - checked="1"> - - +   - - - - ${lang("abuse.articleaction")} - - - - - - - - ${lang("abuse.commentaction")} - - - - - + + + + diff --git a/templates/admin/articletypelist.template b/templates/admin/articletypelist.template index 73556119..904a74c5 100755 --- a/templates/admin/articletypelist.template +++ b/templates/admin/articletypelist.template @@ -6,6 +6,7 @@ + @@ -19,17 +20,16 @@ - + - + - - - + + +

    ${lang("no_matches_found")}

    - diff --git a/templates/admin/audio.template b/templates/admin/audio.template index d916214d..b18e018e 100755 --- a/templates/admin/audio.template +++ b/templates/admin/audio.template @@ -85,8 +85,8 @@ function openWin(url) { - - + +   diff --git a/templates/admin/comment.template b/templates/admin/comment.template index 517f6fe1..d96f46aa 100755 --- a/templates/admin/comment.template +++ b/templates/admin/comment.template @@ -31,52 +31,21 @@ ${utility.encodeHTML(utility.encodeHTML(comment.webdb_create))} - - - - - ${lang("comment.status")} - - - - - - - - - - - - - + + + + + + + + + + - - - - ${lang("comment.language")} - - - - - - - - - ${lang("comment.text")}
    - ${lang("comment.html")} checked> - - - - - + + + + diff --git a/templates/admin/content.template b/templates/admin/content.template index 944270ab..7b73ce28 100755 --- a/templates/admin/content.template +++ b/templates/admin/content.template @@ -59,39 +59,14 @@ ${lang("content.create_date")}: - ${utility.encodeHTML(article.webdb_create)}
    ${lang("edit")}: -   (yyyy-mm-dd [HH:mm]) + ${utility.encodeHTML(article.webdb_create)}
    ${lang("edit")}: +   (yyyy-mm-dd [HH:mm]) - - - ${lang("content.articletype")}: - - - - - - - - - ${lang("content.language")} - - - - - - - - + + + @@ -131,49 +106,23 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + - - ${lang("content.abstract")}: - - - - - - - - - ${lang("content.content")}: -
    - ${lang("content.html")}  - checked>   - - - - - - - - - ${lang("content.comment")}:
    - ${lang("content.internal")} - - - - - - diff --git a/templates/admin/image.template b/templates/admin/image.template index d73d1539..6972c927 100755 --- a/templates/admin/image.template +++ b/templates/admin/image.template @@ -91,8 +91,8 @@ function openWin(url) { - - + +   diff --git a/templates/admin/media.template b/templates/admin/media.template index a7e827b9..c2ec8cc1 100755 --- a/templates/admin/media.template +++ b/templates/admin/media.template @@ -87,8 +87,8 @@ function openWin(url) { - - + +   diff --git a/templates/admin/mediafolder.template b/templates/admin/mediafolder.template index 85e5145f..e832b7b2 100755 --- a/templates/admin/mediafolder.template +++ b/templates/admin/mediafolder.template @@ -19,8 +19,8 @@ - - + + diff --git a/templates/admin/message.template b/templates/admin/message.template index bf99bcd2..bccccf1e 100755 --- a/templates/admin/message.template +++ b/templates/admin/message.template @@ -31,7 +31,7 @@ - + diff --git a/templates/admin/topic.template b/templates/admin/topic.template index 4d062065..f27f39fd 100755 --- a/templates/admin/topic.template +++ b/templates/admin/topic.template @@ -20,7 +20,7 @@ - + diff --git a/templates/admin/video.template b/templates/admin/video.template index 71283a4d..7736abc9 100755 --- a/templates/admin/video.template +++ b/templates/admin/video.template @@ -85,8 +85,8 @@ function openWin(url) { - - + + @@ -133,11 +133,11 @@ function openWin(url) {
     
    - + - ${lang("media.is_published")} checked> -    + ${lang("media.is_published")} checked> +   
    -- 2.11.0
  • Vb7KW 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