--- jspwiki-2.8.0.orig/etc/i18n/CoreResources_en.properties +++ jspwiki-2.8.0/etc/i18n/CoreResources_en.properties @@ -0,0 +1,198 @@ +# +# This is the main i18n resource file for JSPWiki internal +# code, the so-called core code. +# +# Please, do not forget to use proper HTML entities, so +# " = " +# ' = ' Note that ' is NOT HTML, and does not necessarily work in all browsers. + + + +# +# Time/date formatting. Some plugins might be using some specific formats, +# but you are free to use these times and dates. +# + +# Timedate format used when a full time/date is used +common.datetimeformat = HH:mm:ss dd-MMM-yyyy zzz + +# Time stamp format when a simple time stamp is needed (without date) +common.timeformat = HH:mm:ss + +# Date stamp format when showing just the date. +common.dateformat = EEEE, MMMM d, yyyy + +# "Unknown author" + +common.unknownauthor = UnknownAuthor + +# Decisions: {0}=task owner; {1}=requester; {2-9}=additional message arguments for workflow +decision.editWikiApproval=Edit page {2} + +# Security + +security.error.noaccess.logged=You don't have access to '{0}'. Do you want to log in as another user?. +security.error.noaccess=You don't have access to '{0}'. Please log in first. +security.error.wrongip=Attempt to post from a different IP address than where the page was originally fetched. + +security.error.createprofilebeforelogin=You must log in before creating a profile. +security.error.blankpassword=Password cannot be blank +security.error.passwordnomatch=Passwords don't match + +security.error.illegalfullname=Full name "{0}" is illegal +security.error.illegalloginname=Login name "{0}" is illegal + +# These should roughly match whatever is used in default.properties for UserProfile.jsp +security.user.loginname=Login name +security.user.fullname=Full name +security.user.email=Email address + +# Renaming + +rename.empty=New page name empty. +rename.error.title=Unable to rename page +rename.error.reason=Reason: +rename.identical=Page names identical. +rename.exists=Page "{0}" already exists. Please change the new name or delete the page "{0}" first. +rename.unknownerror=An unknown error occurred ({0}) + +# Login.jsp +#login.error.capslock=Invalid login (please check your Caps Lock key) #obsolete +login.error.password=Not a valid login. +login.error.noaccess=It seems you don't have access to that. Sorry. + +# Lostpassword.jsp + +# 0 = login name, 1 = password, 2 = URL, 3 = Application name (as signature) +# This is text, not HTML. +lostpwd.newpassword.email=As requested, your new password for login "{0}" is "{1}" \ + \n\nYou may log in at {2}.\n\n-- {3} +lostpwd.newpassword.subject=New password for {0} +lostpwd.nouser=No user or email "{0}" was found. +lostpwd.nomail=Internal error: couldn't send the email! Contact the site administrator, please. +lostpwd.emailed=A new password has been emailed to the requested account. + +lostpwd.reset.title=Password reset +lostpwd.reset.unable=Unable to reset password. Please try again. + +lostpwd.reset.blurb=Lost or forgot your password? Enter your account name or email here: +lostpwd.reset.submit=Reset password! + +# TraditionalDiffProvider + +diff.traditional.added=At line {0} added {1} +diff.traditional.changed=At line {0} changed {1} +diff.traditional.removed=At line {0} removed {1} +diff.traditional.oneline=one line +diff.traditional.lines={2} lines + +# InputValidator + +validate.unsafechars={0} cannot contain these characters: {1} +validate.invalidemail={0} is not valid +validate.cantbenull={0} cannot be blank + +# UserProfileTag + +userprofile.nogroups=(none) +userprofile.noroles=(none) + +# NewGroup.jsp +newgroup.exists=Group {0} already exists. Try another name. + +# JSPWikiMarkupParser + +markupparser.error.invalidset = Invalid SET found: {0} +markupparser.error.nointerwikiref = No InterWiki reference defined in properties for Wiki called "{0}"! +markupparser.error.parserfailure = Parser failed: {0} +markupparser.error.javascriptattempt = Attempt to output javascript! +markupparser.link.create = Create "{0}" + +# Captcha.jsp +captcha.js.humancheckcomplete.alert=Please correctly identify the cats. +captcha.description=We believe you may be a robot or a spammer. Could you please pick out the kittens from the below \ + set of images, so we know you are a normal human being? +captcha.asirra.please.select=Please select all the cat photos: +captcha.asirra.adopt.me=Adopt me +captcha.asirra.a.get.challenge=Request different images. +captcha.asirra.a.whatsthis=What is this? +captcha.submit=Submit + +# Install.jsp +install.jsp.title=JSPWiki Installer +install.jsp.intro.title=JSPWiki Installer +install.jsp.intro.p1=Welcome! This little JSP page is here to help you do the first difficult stage of JSPWiki \ + installation. If you're seeing this page, you have already installed JSPWiki correctly \ + inside your container. +install.jsp.intro.p2=There are now some things that you should configure. When you press Configure, the \ + jspwiki.properties file from the distribution will be modified, or if it \ + can't be found, a new one will be created. +install.jsp.intro.p3=This setup system is really meant for people who just want to be up and running really quickly. \ + If you want to integrate JSPWiki with an existing system, I would recommend that you go and edit \ + the jspwiki.properties file directly. You can find a sample config file from \ + yourwiki/WEB-INF/. +install.jsp.install.info=Good news: +install.jsp.install.warning=Warning: +install.jsp.install.error=Could not save configuration: +install.jsp.install.msg.rnd.pwd=Because no administrator account exists yet, JSPWiki created one for you, with a \ + random password. You can change this password later, of course. The account''s id is \ + {0} and the password is {1}. Please write this information down and keep it in a \ + safe place. JSPWiki also created a wiki group called {2} that contains this user. +install.jsp.install.msg.admin.notexists=Is this the first time you've run the Installer? If it is, you should know that \ + after JSPWiki validates and saves your configuration for the first time, you will \ + need administrative privileges to access this page again. We do this to prevent \ + random people on the Internet from doing bad things to your wiki. + +install.jsp.basics.title=Basics +install.jsp.basics.appname.label=Application Name +install.jsp.basics.appname.desc=What should your wiki be called? Try and make this a relatively short name. +install.jsp.basics.baseurl.label=Base URL +install.jsp.basics.baseurl.desc=Please tell JSPWiki where your wiki is located. +install.jsp.basics.page.storage.label=Page storage +install.jsp.basics.page.storage.desc=By default, JSPWiki will use the VersioningFileProvider that stores files in a particular \ + directory on your hard drive. If you specify a directory that does not exist, JSPWiki will \ + create one for you. All attachments will also be put in the same directory. + +install.jsp.security.title=Security +install.jsp.security.sec.conf.label=Security configuration +install.jsp.security.sec.conf.opt1=JAAS plus container security (default) +install.jsp.security.sec.conf.opt2=Container security only +install.jsp.security.sec.conf.desc=By default, JSPWiki manages access to resources using a JAAS-based security system. \ + It will also respect any container security constraints you might have, \ + if you've enabled them in your web.xml file. If you disable JAAS security, \ + JSPWiki might not work as you expect. But sometimes you might want to do this if you're \ + trying to troubleshoot. +install.jsp.security.admaccount.label=Administrator account +install.jsp.security.admaccount.enabled=Enabled +install.jsp.security.admaccount.notenabled=Not enabled +install.jsp.security.admaccount.enabled.desc=This wiki has an administrator account named admin that is part of \ + the wiki group Admin. By default, JSPWiki's security policy grants \ + all members of the Admin group the all-powerful AllPermission. +install.jsp.security.admaccount.notenabled.desc=This wiki doesn't seem to have an administrator account. When you click Configure!,\ + JSPWiki will create one for you. + +install.jsp.adv.settings.title=Advanced Settings +install.jsp.adv.settings.logfile.label=Log files +install.jsp.adv.settings.logfile.desc=JSPWiki uses Jakarta Log4j for logging. Please tell JSPWiki where the log files should go. +install.jsp.adv.settings.workdir.label=Work directory +install.jsp.adv.settings.workdir.desc=This is the place where all caches and other runtime stuff is stored. +install.jsp.instr.desc=After you click Configure!, the installer will write your settings to {0}. \ + It will also create an Administrator account with a random password and a corresponding Admin group. +install.jsp.instr.submit=Configure! +install.jsp.validated.new.props=Here is your new jspwiki.properties + +# Installer.java +install.installer.unable.read.props=Unable to read properties: {0} +install.installer.default.appname=MyWiki +install.installer.default.pagedir=Please configure me! +install.installer.props.saved=Your new properties have been saved. Please restart your container (unless this was your first \ + install). Scroll down a bit to see your new jspwiki.properties. +install.installer.props.notsaved=Unable to write properties: {0}. Please copy the file below as your jspwiki.properties:\n{1} +install.installer.validate.baseurl=You must define the base URL for this wiki. +install.installer.validate.pagedir=You must define the location where the files are stored. +install.installer.validate.appname=You must define the application name. +install.installer.validate.workdir=You must define a work directory. +install.installer.validate.logdir=You must define a log directory. + +# RSSImageLinkTag +rss.title=RSS feed for page {0} --- jspwiki-2.8.0.orig/etc/i18n/plugin/PluginResources_en.properties +++ jspwiki-2.8.0/etc/i18n/plugin/PluginResources_en.properties @@ -0,0 +1,49 @@ +# This is the English language bundle for JSPWiki core plugins +# Don't add your own plugins to this file; create your own bundle, please. + +# Errors rendering plugins + +plugin.error.insertionfailed = Plugin insertion failed: {0} +plugin.error.cannotinstantiate = Cannot instantiate plugin {0} +plugin.error.notallowed = Not allowed to access plugin {0} +plugin.error.instantationfailed = Instantiation of plugin {0} failed. +plugin.error.failed = Plugin failed +plugin.error.couldnotfind = Could not find plugin {0} +plugin.error.notawikiplugin = Class {0} is not a Wiki plugin. +plugin.error.missingparameter = Missing parameter in plugin definition: {0} +plugin.error.parsingarguments = Zyrf. Problems with parsing arguments: {0} + +# TableOfContents +tableofcontents.title = Table of Contents + +# BugReportHandler + +bugreporthandler.new = A new page has been created: {0} +bugreporthandler.unable = Unable to create a new page! +bugreporthandler.titlerequired = Title is required! + +# CurrentTimePlugin + +currenttimeplugin.badformat = You specified a bad format + +# WeblogEntryPlugin + +weblogentryplugin.newentry = New entry + +# ReferringPagesPlugin + +# {0} is where you put in the number how many links are still there +referringpagesplugin.more = ...and {0} more +referringpagesplugin.nobody = ...nobody + +# Forms plugins +formclose.noneedtoshow = (no need to show close now) +forminput.namemissing = Input element is missing parameter 'name'. +forminput.noneedtoshow = (no need to show input field now) +formopen.missingparam = The FormOpen element is missing the '{0}' parameter. +formopen.postorgetonly = Method must be either 'post' or 'get' +formopen.noneedtoshow = (no need to show form open now) +formoutput.missingargument = Argument '{0}' required for Form plugin +formselect.namemissing = Select element is missing parameter 'name'. +formtextarea.noneedtoshow = (no need to show textarea field now) +formtextarea.namemissing = Textarea element is missing parameter 'name'. \ No newline at end of file --- jspwiki-2.8.0.orig/etc/i18n/templates/default_en.properties +++ jspwiki-2.8.0/etc/i18n/templates/default_en.properties @@ -0,0 +1,658 @@ +# +# This file contains the localized resource definitions for +# the "default" template. Any other template i18n file should +# be renamed in a similar way, e.g. "mytemplate.properties" under i18n/templates +# +# Common things + +common.nopage=This page does not exist. Why don’t you go and {0}? +common.createit=create it +common.more=More... + + +# AttachmentTab.jsp + +attach.tab=Attach +attach.list=List of attachments +attach.add=Add new attachment +attach.add.info=In order to upload a new attachment to this page, \ + please use the following box to find the file, then click on “Upload”. +attach.add.permission=Only authorized users are allowed to upload new attachments. +attach.bytes=bytes +attach.add.selectfile=Select file: +attach.add.changenote=Change Note: +attach.add.submit=Upload +attach.preview=Image preview +attach.deleteconfirm=Please confirm that you want to delete this attachment permanently! +attach.delete=Delete +attach.moreinfo.title=Attachment info and version history + +# CommentContent.jsp + +comment.title.comment={0}: Comment: {1} +comment.tab.discussionpage=Discussion page +comment.tab.addcomment=Add comment +comment.edithelpmissing=Ho hum, it seems that the {0} page is missing. \ + Someone must’ve done something to the installation...\ +

\ + You can copy the text from the \ + EditPageHelp page \ + on jspwiki.org. + +# ConflictContent.jsp + +conflict.oops.title=Oops! +conflict.oops=

Oops! Someone modified the page while you were editing it!

\ +

Since I am stupid and can’t figure out what the difference \ + between those pages is, you will need to do that for me. I’ve \ + printed here the text (in Wiki) of the new page, and the \ + modifications you made. You’ll now need to copy the text onto a \ + scratch pad (Notepad or emacs will do just fine), and then edit \ + the page again.

\ +

Note that when you go back into the editing mode, someone might have \ + changed the page again. So be quick.

+conflict.goedit=

Go edit {0}

+conflict.modified=Modified by someone else +conflict.yourtext=Here’s your text + +# DiffContent.jsp + +#diff.tab=Version changes +diff.difference=Difference between version {0} and {1} +diff.gotofirst.title=Go to first change in this document +diff.gotofirst=View first change +diff.goback=Back to {0}, or {1} +diff.versionhistory={0} page info +diff.nodiff=No difference detected. + +# EditContent.jsp + +edit.locked=User “{0}” has started to edit this page, but has not yet \ + saved. I won’t stop you from editing this page anyway, BUT be aware that \ + the other person might be quite annoyed. It would be courteous to wait for the lock \ + to expire or until the person stops editing the page. The lock expires in \ + {1} minutes. +edit.restoring=You are about to restore version {0}. \ + Click on “Save” to restore. You may also edit the page before restoring it. +edit.chooseeditor=Editor +edit.tab.attachments=Attachments +edit.tab.help=Help +edit.tab.edit=Edit +edit.tab.findreplacehelp=Find and Replace help + +# EditTemplate.jsp +# 0 : Application name +# 1 : Page name +edit.title.edit={0}: Edit: {1} +edit.sections=Sections + +# EditGroup.jsp +editgroup.tab=Edit Group +editgroup.heading.edit=Edit Group {0} +editgroup.instructions=This page allows you to add or edit members for the wiki group called \ + {0}. Generally, only members of the group can edit the \ + membership list. By default, the person who creates the group is a member. +editgroup.saveerror=Could not save group:  +editgroup.memberlist=The membership for this group. Enter each user’s name or \ + wiki name, separated by carriage returns. +editgroup.savehelp=When you click “Save group,” this group will be saved as a group \ + called {0}. You can specify this \ + name in page access control lists (ACLs). +editgroup.submit.save=Save group + +# Favorites.jsp + +fav.myfavorites=My Favorites +fav.nomenu=Please make a {0} +fav.greet.anonymous=G’day (anonymous guest) +fav.greet.asserted=G’day, {0} (not logged in) +fav.greet.authenticated=G’day, {0} (authenticated) +fav.aggregatewiki.title=Aggregate the RSS feed of the entire wiki + +# FindContent.jsp + +find.tab=Search Wiki +find.tab.help=Help +find.input=Enter your query here: +find.heading.results=Search results for “{0}” +find.resultsstart=Found {0} hits, here are the results from {1} to {2}. +find.results.page=Page +find.results.score=Score +find.noresults=No results were found. It could be that either this wiki does not have the \ + document you were looking for, or that you do not have permission to view the documents you were looking for. +find.getprevious=Get previous {0} results +find.getnext=Get next {0} results +find.externalsearch=Try this same search on : +find.submit.find=Find! +find.submit.go=Go! +find.details=Show details +find.scope.all=Search Everywhere +find.scope.authors=Authors +find.scope.pagename=Page Names +find.scope.content=Page Content +find.scope.attach=Attachments + +# GroupContent.jsp + +group.tab=View Group +#replaced by grp.deletegroup.confirm +#group.areyousure=Are you sure you want to permanently delete group "{0}"? \ +# Users might not be able to access pages whose ACLS contain this group. \\n\\n \ +# If you click OK, the group will be removed immediately. +group.doesnotexist=This group does not exist. +group.createsuggestion=Why don’t you go and {0} +group.createit=create it? +group.errorprefix=Error  +group.groupintro=This is the wiki group called {0}. Only members of this group can edit it. + +group.name=Group Name +group.members=Group Members +group.membership=The group’s membership. +group.modifier={0} saved this group on {1} +group.creator={0} created it on {1}. + +# GroupTab.jsp +grp.createdon=Created on {0} by {1} +grp.lastmodified=
Last modified on {0} by {1} +grp.groupnames.title=Click to edit this group +grp.newgroupname=(new group name) +grp.savegroup=Save Group +grp.savenewgroup=Save New Group +grp.cancel=Cancel +grp.deletegroup=Delete Group +grp.deletegroup.confirm=Please confirm that you want to delete this group permanently! +grp.formhelp=The membership for this group. Only members of this group can edit it. \ +
Enter each user’s wiki name or full name, separated by carriage returns. +grp.allgroups=All Groups + +# Header.jsp +header.yourtrail=Your trail:  +header.homepage.title=Home page of this wiki + +# InfoContent.jsp +info.pagename=Page name +info.parent=Parent page +info.lastmodified=This page (revision-{0}) was last changed on {1} by {2} +info.createdon=This page was created on {0} by {1} +info.current=Current page version +info.noversions=No versions. +info.feed=Page feed + +info.pagediff.title=Show changes of last page update + +info.rename.submit=Rename page +info.updatereferrers=Update referrers? +info.rename.permission=Only authorized users are allowed to rename pages. + + +info.delete.submit=Delete entire page +#info.delete.attachmentwarning=First delete all attachments of this page +info.confirmdelete=Please confirm that you want to delete this content permanently! +info.delete.permission=Only authorized users are allowed to delete pages. + +info.moreinfo=More Info... +info.backtomainpage=Back to {0} +info.backtoparentpage=Back to {0} (parent page) + +info.history=Page revision history +info.noversions=This page has only one version +info.showrevisions=Show all revisions from {0} down to {1} +info.showfrom=Show {0} revisions from {1} to {2} + +info.pagination=Pagination: +info.pagination.first=First +info.pagination.last=Last +info.pagination.previous=Previous +info.pagination.next=Next +info.pagination.all=all +info.pagination.total= (Total items: {0} ) +info.pagination.show.title=Show items from {0} to {1} +info.pagination.showall.title=Show all items + +info.version=Version +info.date=Date Modified +info.size=Size +info.author=Author +info.changes=Changes ... +info.changenote=Change note +info.actions=Actions +info.difftoprev=to previous +info.difftolast=to last + +info.tab=Info +info.attachment.tab=Attachment Info +info.tab.links=Links +info.tab.outgoing=Outgoing links +info.tab.incoming=Incoming links +info.tab.attachments=Attachments + +info.uploadnew=Upload new version +#info.uploadnew.submit=Upload new attachment +info.uploadnew.filename=Select file: +info.uploadnew.changenote=Change note: +info.uploadnew.help=In order to upload a new attachment to this page, \ + please use the following box to find the file, then click on “Upload”. +info.uploadnew.nopermission=Only authorized users are allowed to upload new attachment versions. + +info.deleteattachment=Delete attachment +info.deleteattachment.submit=Delete attachment + +info.attachment.history=Attachment revision history +info.attachment.type=Kind +info.attachment.name=Attachment Name + +info.kilobytes=kB + +# LoginContent.jsp + +login.tab=Login +login.title=Login +login.heading.login=Sign in to {0} + +login.help=Please sign in with your login name and password. +login.errorprefix=Error:  +login.login=Login +login.password=Password +login.remember=Remember me? + +login.submit.login=Login +login.invite=Wanna login? +login.nopassword=Don’t have an account ? +login.registernow=Join {0} now! +login.registernow.title=Register a new user! + +login.lostpw=Lost your password? +login.lostpw.tab=Lost password +login.lostpw.title=Lost your password? +login.lostpw.getnew=Get a new one! +login.lostpw.heading=Reset Password +login.lostpw.help=Lost or forgot your password? Enter the email address you used to register with here. +login.lostpw.name=Account email +login.lostpw.submit=Reset password ! +login.lostpw.reset.clickhere=Click here +login.lostpw.reset.login={0} to log in once you retrieve your new password. + +login.register.tab=Register New User + +login.tab.help=Help +login.loginhelpmissing=Ho hum, it seems that the {0} page is missing. \ + Someone must’ve done something to the installation...\ +

\ + You can copy the text from the \ + LoginHelp page \ + on jspwiki.org. + +# NewGroupContent.jsp +newgroup.heading.create=Create New Group +newgroup.errorprefix=Error:  +newgroup.instructions.start=This page allows you to create a new wiki group. +newgroup.name=Name +newgroup.name.description=The name of the new group. +newgroup.members.description=The membership for this group. \ + Enter each user’s name or wiki name, separated by carriage returns. +newgroup.instructions.end=When you click “Save group,” \ + this group will be saved as a group. You can specify the group’s name \ + in page access control lists (ACLs). +newgroup.creategroup=Create group +newgroup.defaultgroupname=MyGroup + + +# PageActions.jsp + +actions.gototop=Go to top +actions.gotobottom=Go to bottom +actions.view=View +actions.view.title=View current page [ v ] +actions.viewparent.title=View parent page [ v ] +actions.home=Home +actions.home.title=Go to home page {0} +actions.edit=Edit +actions.edit.title=Edit current page [ e ] +actions.editparent=Edit parent page +actions.editparent.title=Edit parent page [ e ] +actions.index=Alphabetic Index +actions.index.title=Alphabetically sorted list of all pages +actions.recentchanges=Recent Changes +actions.recentchanges.title=Pages sorted by modification date + +actions.more=More... +actions.separator= - - - - - - +actions.comment=Add Comment +actions.comment.title=Add Comment +actions.addcommenttoparent=Add Comment to parent page +actions.upload=Attach File +actions.info=Info +actions.info.title=Additonal Page Info and Version history [ i ] +actions.prefs=My Prefs +actions.prefs.title=Manage your preferences [ p ] +actions.editgroup=Edit group +actions.deletegroup=Delete group +actions.viewgroup=View group +actions.creategroup=Create group +actions.creategroup.title=Create new authorisation group +actions.login=Log in +actions.login.title=Log in or Register as new user +actions.logout=Log out +actions.logout.title=Log out +actions.publishedon=This particular version was published on {0} by {1}. +actions.notcreated=Page not created yet. +actions.workflow=Workflow +actions.workflow.title=Workflow +actions.systeminfo=JSPWiki System Info +actions.systeminfo.title=Show JSPWiki System settings & info +actions.rawpage=View Page Source +actions.rawpage.title=View Page Source + +# PageContent.jsp + +view.oldversion=This is version {0}. It is not the current version, and thus it cannot be edited. +view.backtocurrent=[Back to current version] +view.restore=[Restore this version] +view.heading.attachments=Attachments + +# PageTab.jsp +view.tab=View + +# PreferencesContent.jsp + +# 0: applicationname +prefs.heading=User Preferences +prefs.clear.heading=Clear User Preferences +prefs.tab.prefs=Preferences +prefs.tab.profile=Profile + +prefs.cookies=Your choices will be saved in your browser as cookies. + +prefs.user.skin=Skin +prefs.user.orientation=Favorites style +prefs.user.orientation.left=Left +prefs.user.orientation.right=Right +#prefs.user.editorareaheight=Editor area height //not user anymore +prefs.user.sectionediting=Section Editing +prefs.user.sectionediting.text=Enable section editing via [edit] links +prefs.user.timeformat=Time Format +prefs.user.timezone=Time Zone +prefs.user.timezone.server=[SERVER] +prefs.user.language=Language +prefs.user.language.default=[DEFAULT] + +prefs.instructions=Set your user preferences here. Your choices will be saved in your browser as cookies. +prefs.assertedname=Name +prefs.assertedname.description=Your name. \ + If you haven’t created a user profile yet, you can tell {0} \ + who you are by ‘asserting’ an identity. You wouldn’t lie to us would you? \ +
\ + Note that setting your user name this way isn’t a \ + real method of authentication, because it just sets a cookie in your browser \ + without requiring a password. Depending on the security policy, the wiki may grant \ + you fewer privileges as an “asserted” user. {0} if \ + you would prefer a traditional username and password, which is more \ + secure. +prefs.assertedname.create=Create a new user profile +prefs.submit.setname=Set user name +prefs.save.prefs.submit=Save User Preferences +prefs.clear.submit=Clear User Preferences +prefs.clear.description=\ + In some cases, you may need to remove your ‘asserted’ user name and \ + your user preferences from this computer. \ + Click the ‘Clear User Preferences’ button to do that. \ +
\ + Note that it will remove all user preferences you’ve set up, permanently. \ + You will need to enter them again. + +prefs.newprofile=Create a new user profile +prefs.oldprofile=Update your wiki profile. + +prefs.errorprefix.prefs=Could not save preferences:  +prefs.errorprefix.profile=Could not save profile:  +prefs.errorprefix.rename=Could not rename the page:  + +prefs.loginname=Login name * +prefs.loginname.description=This is your login id. +prefs.loginname.cannotset.new=You cannot set your login name because you are not \ + logged in yet. You should log in first, using the credentials supplied by \ + your administrator. +prefs.loginname.cannotset.exists=You cannot set your login name because \ + your credentials are managed by the web container, not the wiki. + +prefs.password=Password * +prefs.password.description=Sets your account password. It may not be blank. +prefs.password2=Password verify * +prefs.password2.description=Re-type your password for verification. + +prefs.fullname=Name * +prefs.fullname.description=Use your name in Access Control Lists or wiki Groups. + +prefs.email=E-mail address +prefs.email.description= (optional). If you lose your password, \ + you can ask to have a new, random password sent to this address. + +prefs.cookie.info=This wiki automatically remembers you using cookies, \ + without requiring additional authentication. To use this \ + feature, your browser must accept cookies from this \ + website. When you click “save profile,” the cookie \ + will be saved by your browser. +prefs.acl.info=Access control lists or wiki groups containing your identity \ + should specify your name or wiki name. You are also member \ + of the above Roles and Groups. + +prefs.lastmodified=You created your profile on {0}, and last saved it on {1} +prefs.save.description=Click “save profile” to save your wiki profile. + +prefs.save.submit=Save profile +prefs.create.submit=Create New User Profile + +prefs.roles=Roles +prefs.groups=Groups +prefs.creationdate=Creation date +prefs.profile.lastmodified=Last modified + +# Workflow generic messages +workflow.tab=Workflow +workflow.heading=Your Workflow Items +workflow.decisions.heading=Inbox +workflow.workflows.heading=Outbox +workflow.instructions=This page contains information \ + on workflows that you initiated, or have been asked to act on. +workflow.noinstructions=You do not have any workflow items. +workflow.actor.instructions=You need to take action on the following workflows: +workflow.owner.instructions=You started the following workflows: +workflow.id=ID +workflow.requester=Requester +workflow.item=Item +workflow.startTime=Received +workflow.actions=Actions +workflow.actor=Current actor +workflow.submit=Do it +outcome.step.abort=Abort +outcome.step.complete=Completed +outcome.step.continue=Continue +outcome.decision.acknowledge=OK +outcome.decision.approve=Approve +outcome.decision.deny=Deny +outcome.decision.hold=Hold +outcome.decision.reassign=Reassign + +workflow.details.title=Show or hide details +workflow.details=details... + +# Specific workflow/decision messages +workflow.saveWikiPage=Save wiki page {2} +decision.saveWikiPage=Approve page {2} +notification.saveWikiPage.reject=Your request to save page {2} was rejected. +fact.pageName=Page name +fact.diffText=Difference between proposed and current version +fact.proposedText=Proposed +fact.currentText=Current +fact.isAuthenticated=Is Authenticated? + +select.one=(select one) +workflow.creatUserProfile=New user profile {2} +decision.createUserProfile=New user profile {2} +notification.createUserProfile.reject=Your request to create a user profile was rejected. +fact.submitter=Submitter + +# PreviewContent.jsp +preview.tab=Preview +preview.info=This is a preview! \ + Hit “Keep Editing” to go back to the editor, \ + or hit “Save” if you’re happy with what you see. + +# SearchBox.jsp +sbox.search.submit=Quick Navigation +sbox.view=view +sbox.view.title=View the selected page +sbox.edit=edit +sbox.edit.title=Edit the selected page +sbox.clone=clone +sbox.clone.title=Clone the selected page +sbox.find=find +sbox.find.title=Advanced Search +sbox.search.result=Quick search +sbox.search.target=(type ahead) +sbox.clearrecent=(Clear) +sbox.recentsearches=Recent Searches + +# UploadTemplate.jsp +# move all to attach. +upload.title={0}: Add Attachment +upload.heading.upload=Upload new attachment to {0} +upload.attachments=Current attachments +upload.info=In order to upload a new attachment to this page, please use \ + the following box to find the file, then click on “Upload”. +upload.submit=Upload +upload.done=If you are done uploading, you may wish to return to {0} + + +# ViewTemplate.jsp +# 0 : Application Name +# 1 : Page name +view.title.view={0}: {1} + + +# The built-in editors also have their localized strings in this file. + +editor.plain.name=Your name +editor.plain.remember=Remember me? +editor.plain.email=Homepage or email +editor.plain.save.submit=Save +editor.plain.save.title=Save [ s ] +editor.plain.preview.submit=Preview +editor.plain.preview.title=Preview [ v ] +editor.plain.cancel.submit=Cancel +editor.plain.cancel.title=Cancel editing. Your changes will be lost. [ q ] + +editor.plain.changenote=Change Note +editor.commentsignature=Comment signature +editor.plain.toolbar=Toolbar +editor.plain.find=Find +editor.plain.replace=Replace +editor.plain.matchcase=Match Case +editor.plain.regexp=RegExp +editor.plain.global=Replace all +editor.plain.find.submit=Replace +editor.plain.redo.submit=Redo +editor.plain.undo.submit=Undo +editor.plain.redo.title=Redo last Undo +editor.plain.undo.title=Undo last replace [ z ] + +#new since v2.5.100 +editor.plain.posteditor=Enter Keyword+Tab: +editor.plain.posteditor.title=shift+enter for next next field + +editor.plain.smartpairs= Smart Typing Pairs +editor.plain.smartpairs.title= Auto pairing of () [] {} <> "" '' +editor.plain.tabcompletion=Tab Completion (keyword+Tab) +editor.plain.tabcompletion.title=Auto expansion of keyword to Wiki Markup + +#editor.plain.editassist=Edit Assist +#editor.plain.editassist.title=Toggle Edit Assist buttons +editor.plain.sneakpreview=Sneak Preview +editor.plain.sneakpreview.title=Sneak Preview. \ + Click outside the textarea to refresh the sneak preview area. +editor.plain.tbLink.title=link - Insert wiki link +editor.plain.tbH1.title=h1 - Insert heading1 +editor.plain.tbH2.title=h2 - Insert heading2 +editor.plain.tbH3.title=h3 - Insert heading3 +editor.plain.tbHR.title=hr - Insert horizontal ruler +editor.plain.tbBR.title=br - Insert line break +editor.plain.tbPRE.title=pre - Insert preformatted block +editor.plain.tbDL.title=dl - Insert definition list +editor.plain.tbB.title=bold +editor.plain.tbI.title=italic +editor.plain.tbMONO.title=mono - monospace +editor.plain.tbSUP.title=sup - superscript +editor.plain.tbSUB.title=sub - subscript +editor.plain.tbSTRIKE.title=strike - strikethrough +editor.plain.tbTOC.title=toc - Insert table of contents +editor.plain.tbTAB.title=tab - Insert tabbed section +editor.plain.tbTABLE.title=table - Insert table +editor.plain.tbIMG.title=img - Insert image +editor.plain.tbCODE.title=code - Insert code block +editor.plain.tbQUOTE.title=quote - Insert quoted block +editor.plain.tbSIGN.title=sign - Insert your signature + +editor.preview.edit.submit=Keep editing +editor.preview.edit.title=Continue to edit the current page [ e ] +editor.preview.save.submit=Save +editor.preview.save.title=Save the current page [ s ] +editor.preview.cancel.submit=Cancel +editor.preview.cancel.title=Cancel editing. Your changes will be lost. [ q ] + +editor.fck.noscript=You need to enable Javascript in your browser to use the FCK editor +editor.wikiwizard.noscript=You need to enable Javascript in your browser to use the WikiWizard editor +editor.wikiwizard.noapplet=Applets are currently not supported by your browser. \ + Please download Java, so you can use the WikiWizard editor. + +#blog texts in various JSPs +blog.commenttitle=comments +blog.backtomain=Back to main blog page +blog.addcomments=Add new comments + + +# +# The Javascript stuff +# +javascript.sbox.clone.suffix=-New + +javascript.edit.allsections=( All ) +javascript.edit.startOfPage=( Start of page ) +javascript.edit.findandreplace.nomatch=No match found! +javascript.edit.toolbar.makeSelection=Please make first a selection. +javascript.edit.resize=Drag to resize the text area +javascript.edit.areyousure=Without clicking the Save button, your changes will be lost. \ + Are you sure you want to exit this page? + +javascript.favs.show=Click to show Favorites +javascript.favs.hide=Click to hide Favorites + +javascript.quick.edit=[Edit] +javascript.quick.edit.title=Edit section {0}... + +javascript.collapse=Click to collapse +javascript.expand=Click to expand + +javascript.sort.click=Click to sort +javascript.sort.ascending=Ascending order. Click to reverse sort order +javascript.sort.descending=Descending order. Click to reverse sort order +javascript.filter.all=( All ) + +javascript.group.validName=Please provide a valid name for the new Group + +javascript.category.title=Click to show category [{0}] ... + +javascript.slimbox.info=Image {0} of {1} +javascript.slimbox.error=

Error

There was a problem with your request.
Please try again +javascript.slimbox.directLink=Direct Link to the target +javascript.slimbox.remoteRequest=Remote Request {0} of {1} +javascript.slimbox.previous=«Previous +javascript.slimbox.next=Next» +javascript.slimbox.close=Close × +javascript.slimbox.close.title=Close [Esc] + +javascript.sectionediting.label=Section Overview + +javascript.tip.default.title=More... + +javascript.prefs.areyousure=Without clicking the Save User Preferences button, \ + your changes will be lost. Are you sure you want to exit this page? --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/Revision.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/Revision.java @@ -0,0 +1,253 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +import org.apache.commons.jrcs.util.ToString; + + +/** + * A Revision holds the series of deltas that describe the differences + * between two sequences. + * + * @version $Revision: 1.8 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * + * @author Juanco Anez + * @author Brian McBride + * + * @see Delta + * @see Diff + * @see Chunk + * @see Revision + * + * modifications + * 27 Apr 2003 bwm + * + * Added visitor pattern Visitor interface and accept() method. + */ + +public class Revision + extends ToString +{ + + List deltas_ = new LinkedList(); + + + /** + * Creates an empty Revision. + */ + public Revision() + { + } + + + /** + * Adds a delta to this revision. + * @param delta the {@link Delta Delta} to add. + */ + public synchronized void addDelta(Delta delta) + { + if (delta == null) + { + throw new IllegalArgumentException("new delta is null"); + } + deltas_.add(delta); + } + + + /** + * Adds a delta to the start of this revision. + * @param delta the {@link Delta Delta} to add. + */ + public synchronized void insertDelta(Delta delta) + { + if (delta == null) + { + throw new IllegalArgumentException("new delta is null"); + } + deltas_.add(0, delta); + } + + + /** + * Retrieves a delta from this revision by position. + * @param i the position of the delta to retrieve. + * @return the specified delta + */ + public Delta getDelta(int i) + { + return (Delta) deltas_.get(i); + } + + /** + * Returns the number of deltas in this revision. + * @return the number of deltas. + */ + public int size() + { + return deltas_.size(); + } + + /** + * Applies the series of deltas in this revision as patches to + * the given text. + * @param src the text to patch, which the method doesn't change. + * @return the resulting text after the patches have been applied. + * @throws PatchFailedException if any of the patches cannot be applied. + */ + public Object[] patch(Object[] src) throws PatchFailedException + { + List target = new ArrayList(Arrays.asList(src)); + applyTo(target); + return target.toArray(); + } + + /** + * Applies the series of deltas in this revision as patches to + * the given text. + * @param target the text to patch. + * @throws PatchFailedException if any of the patches cannot be applied. + */ + public synchronized void applyTo(List target) throws PatchFailedException + { + ListIterator i = deltas_.listIterator(deltas_.size()); + while (i.hasPrevious()) + { + Delta delta = (Delta) i.previous(); + delta.patch(target); + } + } + + /** + * Converts this revision into its Unix diff style string representation. + * @param s a {@link StringBuffer StringBuffer} to which the string + * representation will be appended. + */ + public synchronized void toString(StringBuffer s) + { + Iterator i = deltas_.iterator(); + while (i.hasNext()) + { + ((Delta) i.next()).toString(s); + } + } + + /** + * Converts this revision into its RCS style string representation. + * @param s a {@link StringBuffer StringBuffer} to which the string + * representation will be appended. + * @param EOL the string to use as line separator. + */ + public synchronized void toRCSString(StringBuffer s, String EOL) + { + Iterator i = deltas_.iterator(); + while (i.hasNext()) + { + ((Delta) i.next()).toRCSString(s, EOL); + } + } + + /** + * Converts this revision into its RCS style string representation. + * @param s a {@link StringBuffer StringBuffer} to which the string + * representation will be appended. + */ + public void toRCSString(StringBuffer s) + { + toRCSString(s, Diff.NL); + } + + /** + * Converts this delta into its RCS style string representation. + * @param EOL the string to use as line separator. + */ + public String toRCSString(String EOL) + { + StringBuffer s = new StringBuffer(); + toRCSString(s, EOL); + return s.toString(); + } + + /** + * Converts this delta into its RCS style string representation + * using the default line separator. + */ + public String toRCSString() + { + return toRCSString(Diff.NL); + } + + /** + * Accepts a visitor. + * @param visitor the {@link Visitor} visiting this instance + */ + public void accept(RevisionVisitor visitor) { + visitor.visit(this); + Iterator iter = deltas_.iterator(); + while (iter.hasNext()) { + ((Delta) iter.next()).accept(visitor); + } + } + +} --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/RevisionVisitor.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/RevisionVisitor.java @@ -0,0 +1,73 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +/** + * Definition of a Visitor interface for {@link Revision Revisions} + * See "Design Patterns" by the Gang of Four + */ +public interface RevisionVisitor +{ + public void visit(Revision revision); + + public void visit(DeleteDelta delta); + + public void visit(ChangeDelta delta); + + public void visit(AddDelta delta); +} \ No newline at end of file --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/Chunk.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/Chunk.java @@ -0,0 +1,357 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * Holds a information about a parrt of the text involved in + * a differencing or patching operation. + * + * @version $Id: Chunk.java 146038 2003-10-13 08:00:44Z rdonkin $ + * @author Juanco Anez + * @see Diff + * @see Delta + */ +public class Chunk + extends org.apache.commons.jrcs.util.ToString +{ + + protected int anchor; + + protected int count; + + protected List chunk; + + /** + * Creates a chunk that doesn't copy the original text. + * @param pos the start position in the text. + * @param count the size of the chunk. + */ + public Chunk(int pos, int count) + { + this.anchor = pos; + this.count = (count >= 0 ? count : 0); + } + + /** + * Creates a chunk and saves a copy the original chunk's text. + * @param iseq the original text. + * @param pos the start position in the text. + * @param count the size of the chunk. + */ + public Chunk(Object[] iseq, int pos, int count) + { + this(pos, count); + chunk = slice(iseq, pos, count); + } + + /** + * Creates a chunk that will be displaced in the resulting text, + * and saves a copy the original chunk's text. + * @param iseq the original text. + * @param pos the start position in the text. + * @param count the size of the chunk. + * @param offset the position the chunk should have in the resulting text. + */ + public Chunk(Object[] iseq, int pos, int count, int offset) + { + this(offset, count); + chunk = slice(iseq, pos, count); + } + + /** + * Creates a chunk and saves a copy the original chunk's text. + * @param iseq the original text. + * @param pos the start position in the text. + * @param count the size of the chunk. + */ + public Chunk(List iseq, int pos, int count) + { + this(pos, count); + chunk = slice(iseq, pos, count); + } + + /** + * Creates a chunk that will be displaced in the resulting text, + * and saves a copy the original chunk's text. + * @param iseq the original text. + * @param pos the start position in the text. + * @param count the size of the chunk. + * @param offset the position the chunk should have in the resulting text. + */ + public Chunk(List iseq, int pos, int count, int offset) + { + this(offset, count); + chunk = slice(iseq, pos, count); + } + + /** + * Returns the anchor position of the chunk. + * @return the anchor position. + */ + public int anchor() + { + return anchor; + } + + /** + * Returns the size of the chunk. + * @return the size. + */ + public int size() + { + return count; + } + + /** + * Returns the index of the first line of the chunk. + */ + public int first() + { + return anchor(); + } + + /** + * Returns the index of the last line of the chunk. + */ + public int last() + { + return anchor() + size() - 1; + } + + /** + * Returns the from index of the chunk in RCS terms. + */ + public int rcsfrom() + { + return anchor + 1; + } + + /** + * Returns the to index of the chunk in RCS terms. + */ + public int rcsto() + { + return anchor + count; + } + + /** + * Returns the text saved for this chunk. + * @return the text. + */ + public List chunk() + { + return chunk; + } + + /** + * Verifies that this chunk's saved text matches the corresponding + * text in the given sequence. + * @param target the sequence to verify against. + * @return true if the texts match. + */ + public boolean verify(List target) + { + if (chunk == null) + { + return true; + } + if (last() > target.size()) + { + return false; + } + for (int i = 0; i < count; i++) + { + if (!target.get(anchor + i).equals(chunk.get(i))) + { + return false; + } + } + return true; + } + + /** + * Delete this chunk from he given text. + * @param target the text to delete from. + */ + public void applyDelete(List target) + { + for (int i = last(); i >= first(); i--) + { + target.remove(i); + } + } + + /** + * Add the text of this chunk to the target at the given position. + * @param start where to add the text. + * @param target the text to add to. + */ + public void applyAdd(int start, List target) + { + Iterator i = chunk.iterator(); + while (i.hasNext()) + { + target.add(start++, i.next()); + } + } + + /** + * Provide a string image of the chunk using the an empty prefix and + * postfix. + */ + public void toString(StringBuffer s) + { + toString(s, "", ""); + } + + /** + * Provide a string image of the chunk using the given prefix and + * postfix. + * @param s where the string image should be appended. + * @param prefix the text thatshould prefix each line. + * @param postfix the text that should end each line. + */ + public StringBuffer toString(StringBuffer s, String prefix, String postfix) + { + if (chunk != null) + { + Iterator i = chunk.iterator(); + while (i.hasNext()) + { + s.append(prefix); + s.append(i.next()); + s.append(postfix); + } + } + return s; + } + + /** + * Retreives the specified part from a {@link List List}. + * @param seq the list to retreive a slice from. + * @param pos the start position. + * @param count the number of items in the slice. + * @return a {@link List List} containing the specified items. + */ + public static List slice(List seq, int pos, int count) + { + if (count <= 0) + { + return new ArrayList(seq.subList(pos, pos)); + } + else + { + return new ArrayList(seq.subList(pos, pos + count)); + } + } + + /** + * Retrieves a slice from an {@link Object Object} array. + * @param seq the list to retreive a slice from. + * @param pos the start position. + * @param count the number of items in the slice. + * @return a {@link List List} containing the specified items. + */ + public static List slice(Object[] seq, int pos, int count) + { + return slice(Arrays.asList(seq), pos, count); + } + + /** + * Provide a string representation of the numeric range of this chunk. + */ + public String rangeString() + { + StringBuffer result = new StringBuffer(); + rangeString(result); + return result.toString(); + } + + /** + * Provide a string representation of the numeric range of this chunk. + * @param s where the string representation should be appended. + */ + public void rangeString(StringBuffer s) + { + rangeString(s, ","); + } + + /** + * Provide a string representation of the numeric range of this chunk. + * @param s where the string representation should be appended. + * @param separ what to use as line separator. + */ + public void rangeString(StringBuffer s, String separ) + { + if (size() <= 1) + { + s.append(Integer.toString(rcsfrom())); + } + else + { + s.append(Integer.toString(rcsfrom())); + s.append(separ); + s.append(Integer.toString(rcsto())); + } + } +} \ No newline at end of file --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/SimpleDiff.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/SimpleDiff.java @@ -0,0 +1,315 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +import java.util.*; + +/** + * Implements a simple differencing algortithm.

+ * + * @date $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * @version $Revision: 1.7 $ + * @author Juanco Anez + * + *

Overview of Algorithm

+ * + *

by bwm + *

+ * + *

The algorithm is optimised for situations where the input sequences + * have few repeated objects. If it is given input with many repeated + * objects it will report sub-optimal changes. However, given appropriate + * input, it is fast, and linear in memory usage.

+ * + *

The algorithm consists of the following steps:

+ *
    + *
  • compute an equivalence set for the input data
  • + *
  • translate each element of the orginal + * and revised input sequences to a member of the equivalence set + *
  • + *
  • match the the input sequences to determine the deltas, i.e. + * the differences between the original and revised sequences.
  • + *
+ * + *

The first step is to compute a an equivalence set for the input data. + * The equivalence set is computed from objects that are in the original + * input sequence

+ *
+ *   eq(x) = the index of the first occurence of x in the original sequence.
+ * 
+ * + *

With this equivalence function, the algorithm can compare integers rather + * than strings, which is considerably more efficient.

+ * + *

The second step is to compute the datastructure on which the + * algorithm will operate. Having computed the equivalence function + * in the previous step, we can compute two arrays where + * indx[i] = eqs(orig[i]) and jndx[i] = eqs(rev[i]). The algorithm can + * now operate on indx and jndx instead of orig and rev. Thus, comparisons + * are then on O(int == int) instead of O(Object.equals(Object)). + *

+ * + *

The algorithm now matches indx and jndx. Whilst indx[i] == jndx[i] + * it skips matching objects in the sequence. In seeking to match objects + * in the input sequence it assumes that each object is likely to be unique. + * It uses the known characteristics of the unique equivalence function. It can + * tell from the eq value if this object appeared in the other sequence + * at all. If it did not, there is no point in searching for a match.

+ * + *

Recall that the eq function value is the index earliest occurrence in + * the orig sequence. This information is used to search efficiently for + * the next match. The algorithm is perfect when all input objects are + * unique, but degrades when input objects are not unique. When input + * objects are not unique an optimal match may not be found, but a + * correct match will be.

+ * + *

Having identified common matching objects in the orig and revised + * sequences, the differences between them are easily computed. + *

+ * + * @see Delta + * @see Revision + * Modifications: + * + * 27/Apr/2003 bwm + * Added some comments whilst trying to figure out the algorithm + * + * 03 May 2003 bwm + * Created this implementation class by refactoring it out of the Diff + * class to enable plug in difference algorithms + * + */ +public class SimpleDiff + implements DiffAlgorithm +{ + + static final int NOT_FOUND_i = -2; + static final int NOT_FOUND_j = -1; + static final int EOS = Integer.MAX_VALUE; + + public SimpleDiff() + { + } + + protected int scan(int[] ndx, int i, int target) + { + while (ndx[i] < target) + { + i++; + } + return i; + } + + /** + * Compute the difference between original and revised sequences. + * + * @param orig The original sequence. + * @param rev The revised sequence to be compared with the original. + * @return A Revision object describing the differences. + * @throws DifferenciationFailedException if the diff could not be computed. + */ + public Revision diff(Object[] orig, Object[] rev) + throws DifferentiationFailedException + { + // create map eqs, such that for each item in both orig and rev + // eqs(item) = firstOccurrence(item, orig); + Map eqs = buildEqSet(orig, rev); + + // create an array such that + // indx[i] = NOT_FOUND_i if orig[i] is not in rev + // indx[i] = firstOccurrence(orig[i], orig) + int[] indx = buildIndex(eqs, orig, NOT_FOUND_i); + + // create an array such that + // jndx[j] = NOT_FOUND_j if orig[j] is not in rev + // jndx[j] = firstOccurrence(rev[j], orig) + int[] jndx = buildIndex(eqs, rev, NOT_FOUND_j); + + // what in effect has been done is to build a unique hash + // for each item that is in both orig and rev + // and to label each item in orig and new with that hash value + // or a marker that the item is not common to both. + + eqs = null; // let gc know we're done with this + + Revision deltas = new Revision(); //!!! new Revision() + int i = 0; + int j = 0; + + // skip matching + // skip leading items that are equal + // could be written + // for (i=0; indx[i] != EOS && indx[i] == jndx[i]; i++); + // j = i; + for (; indx[i] != EOS && indx[i] == jndx[j]; i++, j++) + { + /* void */ + } + + while (indx[i] != jndx[j]) + { // only equal if both == EOS + // they are different + int ia = i; + int ja = j; + + // size of this delta + do + { + // look down rev for a match + // stop at a match + // or if the FO(rev[j]) > FO(orig[i]) + // or at the end + while (jndx[j] < 0 || jndx[j] < indx[i]) + { + j++; + } + // look down orig for a match + // stop at a match + // or if the FO(orig[i]) > FO(rev[j]) + // or at the end + while (indx[i] < 0 || indx[i] < jndx[j]) + { + i++; + } + + // this doesn't do a compare each line with each other line + // so it won't find all matching lines + } + while (indx[i] != jndx[j]); + + // on exit we have a match + + // they are equal, reverse any exedent matches + // it is possible to overshoot, so count back matching items + while (i > ia && j > ja && indx[i - 1] == jndx[j - 1]) + { + --i; + --j; + } + + deltas.addDelta(Delta.newDelta(new Chunk(orig, ia, i - ia), + new Chunk(rev, ja, j - ja))); + // skip matching + for (; indx[i] != EOS && indx[i] == jndx[j]; i++, j++) + { + /* void */ + } + } + return deltas; + } + + /** + * create a Map from each common item in orig and rev + * to the index of its first occurrence in orig + * + * @param orig the original sequence of items + * @param rev the revised sequence of items + */ + protected Map buildEqSet(Object[] orig, Object[] rev) + { + // construct a set of the objects that orig and rev have in common + + // first construct a set containing all the elements in orig + Set items = new HashSet(Arrays.asList(orig)); + + // then remove all those not in rev + items.retainAll(Arrays.asList(rev)); + + Map eqs = new HashMap(); + for (int i = 0; i < orig.length; i++) + { + // if its a common item and hasn't been found before + if (items.contains(orig[i])) + { + // add it to the map + eqs.put(orig[i], new Integer(i)); + // and make sure its not considered again + items.remove(orig[i]); + } + } + return eqs; + } + + /** + * build a an array such each a[i] = eqs([i]) or NF if eqs([i]) undefined + * + * @param eqs a mapping from Object to Integer + * @param seq a sequence of objects + * @param NF the not found marker + */ + protected int[] buildIndex(Map eqs, Object[] seq, int NF) + { + int[] result = new int[seq.length + 1]; + for (int i = 0; i < seq.length; i++) + { + Integer value = (Integer) eqs.get(seq[i]); + if (value == null || value.intValue() < 0) + { + result[i] = NF; + } + else + { + result[i] = value.intValue(); + } + } + result[seq.length] = EOS; + return result; + } + +} --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/Diff.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/Diff.java @@ -0,0 +1,347 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +import java.util.*; + +import org.apache.commons.jrcs.diff.myers.MyersDiff; +import org.apache.commons.jrcs.util.ToString; + +/** + * Implements a differencing engine that works on arrays of {@link Object Object}. + * + *

Within this library, the word text means a unit of information + * subject to version control. + * + *

Text is represented as Object[] because + * the diff engine is capable of handling more than plain ascci. In fact, + * arrays of any type that implements + * {@link java.lang.Object#hashCode hashCode()} and + * {@link java.lang.Object#equals equals()} + * correctly can be subject to differencing using this + * library.

+ * + *

This library provides a framework in which different differencing + * algorithms may be used. If no algorithm is specififed, a default + * algorithm is used.

+ * + * @version $Revision: 1.16 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * @author Juanco Anez + * @see Delta + * @see DiffAlgorithm + * + * modifications: + * + * 27 Apr 2003 bwm + * + * Added some comments whilst trying to figure out the algorithm + * + * 03 May 2003 bwm + * + * Factored out the algorithm implementation into a separate difference + * algorithm class to allow pluggable algorithms. + */ + +public class Diff + extends ToString +{ + /** The standard line separator. */ + public static final String NL = System.getProperty("line.separator"); + + /** The line separator to use in RCS format output. */ + public static final String RCS_EOL = "\n"; + + /** The original sequence. */ + protected final Object[] orig; + + /** The differencing algorithm to use. */ + protected DiffAlgorithm algorithm; + + /** + * Create a differencing object using the default algorithm + * + * @param the original text that will be compared + */ + public Diff(Object[] original) + { + this(original, null); + } + + /** + * Create a differencing object using the given algorithm + * + * @param o the original text which will be compared against + * @param algorithm the difference algorithm to use. + */ + public Diff(Object[] original, DiffAlgorithm algorithm) + { + if (original == null) + { + throw new IllegalArgumentException(); + } + + this.orig = original; + if (algorithm != null) + this.algorithm = algorithm; + else + this.algorithm = defaultAlgorithm(); + } + + protected DiffAlgorithm defaultAlgorithm() + { + return new MyersDiff(); + } + + /** + * compute the difference between an original and a revision. + * + * @param orig the original + * @param rev the revision to compare with the original. + * @return a Revision describing the differences + */ + public static Revision diff(Object[] orig, Object[] rev) + throws DifferentiationFailedException + { + if (orig == null || rev == null) + { + throw new IllegalArgumentException(); + } + + return diff(orig, rev, null); + } + + /** + * compute the difference between an original and a revision. + * + * @param orig the original + * @param rev the revision to compare with the original. + * @param algorithm the difference algorithm to use + * @return a Revision describing the differences + */ + public static Revision diff(Object[] orig, Object[] rev, + DiffAlgorithm algorithm) + throws DifferentiationFailedException + { + if (orig == null || rev == null) + { + throw new IllegalArgumentException(); + } + + return new Diff(orig, algorithm).diff(rev); + } + + /** + * compute the difference between the original and a revision. + * + * @param rev the revision to compare with the original. + * @return a Revision describing the differences + */ + public Revision diff(Object[] rev) + throws DifferentiationFailedException + { + if (orig.length == 0 && rev.length == 0) + return new Revision(); + else + return algorithm.diff(orig, rev); + } + + /** + * Compares the two input sequences. + * @param orig The original sequence. + * @param rev The revised sequence. + * @return true if the sequences are identical. False otherwise. + */ + public static boolean compare(Object[] orig, Object[] rev) + { + if (orig.length != rev.length) + { + return false; + } + else + { + for (int i = 0; i < orig.length; i++) + { + if (!orig[i].equals(rev[i])) + { + return false; + } + } + return true; + } + } + + /** + * Converts an array of {@link Object Object} to a string + * using {@link Diff#NL Diff.NL} + * as the line separator. + * @param o the array of objects. + */ + public static String arrayToString(Object[] o) + { + return arrayToString(o, Diff.NL); + } + + /** + * Edits all of the items in the input sequence. + * @param text The input sequence. + * @return A sequence of the same length with all the lines + * differing from the corresponding ones in the input. + */ + public static Object[] editAll(Object[] text) + { + Object[] result = new String[text.length]; + + for(int i = 0; i < text.length; i++) + result[i] = text[i] + " "; + + return result; + } + + /** + * Performs random edits on the input sequence. Useful for testing. + * @param text The input sequence. + * @return The sequence with random edits performed. + */ + public static Object[] randomEdit(Object[] text) + { + return randomEdit(text, text.length); + } + + /** + * Performs random edits on the input sequence. Useful for testing. + * @param text The input sequence. + * @param seed A seed value for the randomizer. + * @return The sequence with random edits performed. + */ + public static Object[] randomEdit(Object[] text, long seed) + { + List result = new ArrayList(Arrays.asList(text)); + Random r = new Random(seed); + int nops = r.nextInt(10); + for (int i = 0; i < nops; i++) + { + boolean del = r.nextBoolean(); + int pos = r.nextInt(result.size() + 1); + int len = Math.min(result.size() - pos, 1 + r.nextInt(4)); + if (del && result.size() > 0) + { // delete + result.subList(pos, pos + len).clear(); + } + else + { + for (int k = 0; k < len; k++, pos++) + { + result.add(pos, + "[" + i + "] random edit[" + i + "][" + i + "]"); + } + } + } + return result.toArray(); + } + + /** + * Shuffles around the items in the input sequence. + * @param text The input sequence. + * @return The shuffled sequence. + */ + public static Object[] shuffle(Object[] text) + { + return shuffle(text, text.length); + } + + /** + * Shuffles around the items in the input sequence. + * @param text The input sequence. + * @param seed A seed value for randomizing the suffle. + * @return The shuffled sequence. + */ + public static Object[] shuffle(Object[] text, long seed) + { + List result = new ArrayList(Arrays.asList(text)); + Collections.shuffle(result); + return result.toArray(); + } + + /** + * Generate a random sequence of the given size. + * @param The size of the sequence to generate. + * @return The generated sequence. + */ + public static Object[] randomSequence(int size) + { + return randomSequence(size, size); + } + + /** + * Generate a random sequence of the given size. + * @param The size of the sequence to generate. + * @param seed A seed value for randomizing the generation. + * @return The generated sequence. + */ + public static Object[] randomSequence(int size, long seed) + { + Integer[] result = new Integer[size]; + Random r = new Random(seed); + for(int i = 0; i < result.length; i++) + { + result[i] = new Integer(r.nextInt(size)); + } + return result; + } + +} \ No newline at end of file --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/DifferentiationFailedException.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/DifferentiationFailedException.java @@ -0,0 +1,82 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +/** + * Thrown whenever the differencing engine cannot produce the differences + * between two revisions of ta text. + * + * @version $Revision: 1.3 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * + * @author Juanco Anez + * @see Diff + * @see DiffAlgorithm + */ +public class DifferentiationFailedException extends DiffException +{ + + public DifferentiationFailedException() + { + } + + public DifferentiationFailedException(String msg) + { + super(msg); + } +} + --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/PatchFailedException.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/PatchFailedException.java @@ -0,0 +1,82 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +/** + * Thrown whenever a delta cannot be applied as a patch to a given text. + * + * @version $Revision: 1.3 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * @author Juanco Anez + * @see Delta + * @see Diff + */ +public class PatchFailedException extends DiffException +{ + + public PatchFailedException() + { + } + + public PatchFailedException(String msg) + { + super(msg); + } +} + + + --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/ChangeDelta.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/ChangeDelta.java @@ -0,0 +1,134 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +import java.util.List; + +/** + * Holds an change-delta between to revisions of a text. + * + * @version $Id: ChangeDelta.java 146038 2003-10-13 08:00:44Z rdonkin $ + * @author Juanco Anez + * @see Delta + * @see Diff + * @see Chunk + */ +public class ChangeDelta extends Delta +{ + + ChangeDelta() + { + super(); + } + + public ChangeDelta(Chunk orig, Chunk rev) + { + init(orig, rev); + } + + public void verify(List target) throws PatchFailedException + { + if (!original.verify(target)) + { + throw new PatchFailedException(); + } + if (original.first() > target.size()) + { + throw new PatchFailedException("original.first() > target.size()"); + } + } + + public void applyTo(List target) + { + original.applyDelete(target); + revised.applyAdd(original.first(), target); + } + + public void toString(StringBuffer s) + { + original.rangeString(s); + s.append("c"); + revised.rangeString(s); + s.append(Diff.NL); + original.toString(s, "< ", "\n"); + s.append("---"); + s.append(Diff.NL); + revised.toString(s, "> ", "\n"); + } + + public void toRCSString(StringBuffer s, String EOL) + { + s.append("d"); + s.append(original.rcsfrom()); + s.append(" "); + s.append(original.size()); + s.append(EOL); + s.append("a"); + s.append(original.rcsto()); + s.append(" "); + s.append(revised.size()); + s.append(EOL); + revised.toString(s, "", EOL); + } + + public void accept(RevisionVisitor visitor) + { + visitor.visit(this); + } +} + --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/AddDelta.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/AddDelta.java @@ -0,0 +1,131 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +import java.util.List; + +/** + * Holds an add-delta between to revisions of a text. + * + * @version $Id: AddDelta.java 146038 2003-10-13 08:00:44Z rdonkin $ + * @author Juanco Anez + * @see Delta + * @see Diff + * @see Chunk + */ +public class AddDelta + extends Delta +{ + + AddDelta() + { + super(); + } + + public AddDelta(int origpos, Chunk rev) + { + init(new Chunk(origpos, 0), rev); + } + + public void verify(List target) throws PatchFailedException + { + if (original.first() > target.size()) + { + throw new PatchFailedException("original.first() > target.size()"); + } + } + + public void applyTo(List target) + { + revised.applyAdd(original.first(), target); + } + + public void toString(StringBuffer s) + { + s.append(original.anchor()); + s.append("a"); + s.append(revised.rangeString()); + s.append(Diff.NL); + revised.toString(s, "> ", Diff.NL); + } + + public void toRCSString(StringBuffer s, String EOL) + { + s.append("a"); + s.append(original.anchor()); + s.append(" "); + s.append(revised.size()); + s.append(EOL); + revised.toString(s, "", EOL); + } + + public void Accept(RevisionVisitor visitor) + { + visitor.visit(this); + } + + public void accept(RevisionVisitor visitor) + { + visitor.visit(this); + } +} + + + + + --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/Delta.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/Delta.java @@ -0,0 +1,255 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +import java.util.List; + +/** + * Holds a "delta" difference between to revisions of a text. + * + * @version $Revision: 1.6 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * + * @author Juanco Anez + * @author Brian McBride + * @see Diff + * @see Chunk + * @see Revision + * + * modifications + * + * 27 Apr 2003 bwm + * + * Added getOriginal() and getRevised() accessor methods + * Added visitor pattern accept() method + */ + +public abstract class Delta + extends org.apache.commons.jrcs.util.ToString +{ + + protected Chunk original; + + protected Chunk revised; + + static Class[][] DeltaClass; + + static + { + DeltaClass = new Class[2][2]; + try + { + DeltaClass[0][0] = org.apache.commons.jrcs.diff.ChangeDelta.class; + DeltaClass[0][1] = org.apache.commons.jrcs.diff.AddDelta.class; + DeltaClass[1][0] = org.apache.commons.jrcs.diff.DeleteDelta.class; + DeltaClass[1][1] = org.apache.commons.jrcs.diff.ChangeDelta.class; + } + catch (Throwable o) + { + + } + } + + /** + * Returns a Delta that corresponds to the given chunks in the + * original and revised text respectively. + * @param orig the chunk in the original text. + * @param rev the chunk in the revised text. + */ + public static Delta newDelta(Chunk orig, Chunk rev) + { + Class c = DeltaClass[orig.size() > 0 ? 1 : 0] + [rev.size() > 0 ? 1 : 0]; + Delta result; + try + { + result = (Delta) c.newInstance(); + } + catch (Throwable e) + { + return null; + } + result.init(orig, rev); + return result; + } + + /** + * Creates an uninitialized delta. + */ + protected Delta() + { + } + + /** + * Creates a delta object with the given chunks from the original + * and revised texts. + */ + protected Delta(Chunk orig, Chunk rev) + { + init(orig, rev); + } + + /** + * Initializaes the delta with the given chunks from the original + * and revised texts. + */ + protected void init(Chunk orig, Chunk rev) + { + original = orig; + revised = rev; + } + + /** + * Verifies that this delta can be used to patch the given text. + * @param target the text to patch. + * @throws PatchFailedException if the patch cannot be applied. + */ + public abstract void verify(List target) + throws PatchFailedException; + + /** + * Applies this delta as a patch to the given text. + * @param target the text to patch. + * @throws PatchFailedException if the patch cannot be applied. + */ + public final void patch(List target) + throws PatchFailedException + { + verify(target); + try + { + applyTo(target); + } + catch (Exception e) + { + throw new PatchFailedException(e.getMessage()); + } + } + + /** + * Applies this delta as a patch to the given text. + * @param target the text to patch. + * @throws PatchFailedException if the patch cannot be applied. + */ + public abstract void applyTo(List target); + + /** + * Converts this delta into its Unix diff style string representation. + * @param s a {@link StringBuffer StringBuffer} to which the string + * representation will be appended. + */ + public void toString(StringBuffer s) + { + original.rangeString(s); + s.append("x"); + revised.rangeString(s); + s.append(Diff.NL); + original.toString(s, "> ", "\n"); + s.append("---"); + s.append(Diff.NL); + revised.toString(s, "< ", "\n"); + } + + /** + * Converts this delta into its RCS style string representation. + * @param s a {@link StringBuffer StringBuffer} to which the string + * representation will be appended. + * @param EOL the string to use as line separator. + */ + public abstract void toRCSString(StringBuffer s, String EOL); + + /** + * Converts this delta into its RCS style string representation. + * @param EOL the string to use as line separator. + */ + public String toRCSString(String EOL) + { + StringBuffer s = new StringBuffer(); + toRCSString(s, EOL); + return s.toString(); + } + + /** + * Accessor method to return the chunk representing the original + * sequence of items + * + * @return the original sequence + */ + public Chunk getOriginal() + { + return original; + } + + /** + * Accessor method to return the chunk representing the updated + * sequence of items. + * + * @return the updated sequence + */ + public Chunk getRevised() + { + return revised; + } + + /** + * Accepts a visitor. + *

+ * See the Visitor pattern in "Design Patterns" by the GOF4. + * @param visitor The visitor. + */ + public abstract void accept(RevisionVisitor visitor); +} --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/package.html +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/package.html @@ -0,0 +1,106 @@ + + + + + + + + +

+ The {@link org.apache.maven.jrcs.diff diff} package implements + the differencing engine that JRCS uses. The engine has the power + of Unix diff, is simple to understand, and can be used + independently of the archive handling functionality. The entry + point to the differencing engine is class {@link + org.apache.maven.jrcs.diff.Diff Diff}. +

+

+ Text is represented as Object[] because the diff + engine is capable of handling more than plain ascci. In fact, + arrays of any type that implements {@link + java.lang.Object#hashCode hashCode()} and {@link + java.lang.Object#equals equals()} correctly can be subject to + differencing using this library. +

+

+ Two implementations of the differencing algorithm are provided. +

+
    +
  • + {@link SimpleDiff Simple Diff} is a verys imple algorithm that + is fast and works well with very large input sequences, but + that frequently produces result that are subotimal (at times + four or more times larger than GNU diff). +
  • +
  • + {@link org.apache.commons.jrcs.diff.myers.MyersDiff MyersDiff} + is an implementation of Gene Myers + differencing algorithm. Myer's algorithm produces optimum + results (minimum diffs), but consumes considerably more memory + than SimpleDiff, so its not suitable for very large files. +
  • +
+
+@author Juanco Anez
+@version $Id: package.html 146029 2003-05-10 18:56:10Z juanco $
+@see Diff 
+@see org.apache.maven.jrcs.rcs.Archive
+
+ + --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/DiffAlgorithm.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/DiffAlgorithm.java @@ -0,0 +1,84 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +/** + * A simple interface for implementations of differencing algorithms. + * + * @version $Revision: 1.6 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * + * @author Brian McBride + */ +public interface DiffAlgorithm +{ + /** + * Computes the difference between the original + * sequence and the revised sequence and returns it + * as a {@link org.apache.commons.jrcs.diff.Revision Revision} + * object. + *

+ * The revision can be used to construct the revised sequence + * from the original sequence. + * + * @param rev the revised text + * @return the revision script. + * @throws DifferentiationFailedException if the diff could not be computed. + */ + public abstract Revision diff(Object[] orig, Object[] rev) + throws DifferentiationFailedException; +} \ No newline at end of file --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/DiffException.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/DiffException.java @@ -0,0 +1,79 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +/** + * Base class for all exceptions emanating from this package. + * + * @version $Revision: 1.3 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * + * @author Juanco Anez + */ +public class DiffException extends Exception +{ + + public DiffException() + { + } + + public DiffException(String msg) + { + super(msg); + } +} + --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/DeleteDelta.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/DeleteDelta.java @@ -0,0 +1,121 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff; + +import java.util.List; + +/** + * Holds a delete-delta between to revisions of a text. + * + * @version $Id: DeleteDelta.java 146038 2003-10-13 08:00:44Z rdonkin $ + * @author Juanco Anez + * @see Delta + * @see Diff + * @see Chunk + */ +public class DeleteDelta + extends Delta +{ + + DeleteDelta() + { + super(); + } + + public DeleteDelta(Chunk orig) + { + init(orig, null); + } + + public void verify(List target) + throws PatchFailedException + { + if (!original.verify(target)) + { + throw new PatchFailedException(); + } + } + + public void applyTo(List target) + { + original.applyDelete(target); + } + + public void toString(StringBuffer s) + { + s.append(original.rangeString()); + s.append("d"); + s.append(revised.rcsto()); + s.append(Diff.NL); + original.toString(s, "< ", Diff.NL); + } + + public void toRCSString(StringBuffer s, String EOL) + { + s.append("d"); + s.append(original.rcsfrom()); + s.append(" "); + s.append(original.size()); + s.append(EOL); + } + + public void accept(RevisionVisitor visitor) + { + visitor.visit(this); + } +} \ No newline at end of file --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/myers/Snake.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/myers/Snake.java @@ -0,0 +1,97 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff.myers; + +/** + * Represents a snake in a diffpath. + *

+ * + * {@link DiffNode DiffNodes} and {@link Snake Snakes} allow for compression + * of diffpaths, as each snake is represented by a single {@link Snake Snake} + * node and each contiguous series of insertions and deletions is represented + * by a single {@link DiffNode DiffNodes}. + * + * @version $Revision: 1.2 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * @author Juanco Anez + * + */ +public final class Snake + extends PathNode +{ + /** + * Constructs a snake node. + * + * @param the position in the original sequence + * @param the position in the revised sequence + * @param prev the previous node in the path. + */ + public Snake(int i, int j, PathNode prev) + { + super(i, j, prev); + } + + /** + * {@inheritDoc} + * @return true always + */ + public boolean isSnake() + { + return true; + } + +} \ No newline at end of file --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/myers/DiffNode.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/myers/DiffNode.java @@ -0,0 +1,57 @@ +package org.apache.commons.jrcs.diff.myers; + +/** + *

Title:

+ *

Description:

+ *

Copyright: Copyright (c) 2002

+ *

Company:

+ * @author not attributable + * @version 1.0 + */ + +/** + * A diffnode in a diffpath. + *

+ * A DiffNode and its previous node mark a delta between + * two input sequences, that is, two differing subsequences + * between (possibly zero length) matching sequences. + * + * {@link DiffNode DiffNodes} and {@link Snake Snakes} allow for compression + * of diffpaths, as each snake is represented by a single {@link Snake Snake} + * node and each contiguous series of insertions and deletions is represented + * by a single {@link DiffNode DiffNodes}. + * + * @version $Revision: 1.1 $ $Date: 2003-05-10 21:56:10 +0300 (Sat, 10 May 2003) $ + * @author Juanco Anez + * + */ +public final class DiffNode + extends PathNode +{ + /** + * Constructs a DiffNode. + *

+ * DiffNodes are compressed. That means that + * the path pointed to by the prev parameter + * will be followed using {@link PathNode#previousSnake} + * until a non-diff node is found. + * + * @param the position in the original sequence + * @param the position in the revised sequence + * @param prev the previous node in the path. + */ + public DiffNode(int i, int j, PathNode prev) + { + super(i, j, (prev == null ? null : prev.previousSnake()) ); + } + + /** + * {@inheritDoc} + * @return false, always + */ + public boolean isSnake() + { + return false; + } + +} \ No newline at end of file --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/myers/MyersDiff.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/myers/MyersDiff.java @@ -0,0 +1,222 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff.myers; + +import org.apache.commons.jrcs.diff.*; + +/** + * A clean-room implementation of + * + * Eugene Myers differencing algorithm. + *

+ * See the paper at + * + * http://www.cs.arizona.edu/people/gene/PAPERS/diff.ps + * + * @version $Revision: 1.7 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * @author Juanco Anez + * @see Delta + * @see Revision + * @see Diff + */ +public class MyersDiff + implements DiffAlgorithm +{ + /** + * Constructs an instance of the Myers differencing algorithm. + */ + public MyersDiff() + { + } + + /** + * {@inheritDoc} + */ + public Revision diff(Object[] orig, Object[] rev) + throws DifferentiationFailedException + { + PathNode path = buildPath(orig, rev); + return buildRevision(path, orig, rev); + } + + /** + * Computes the minimum diffpath that expresses de differences + * between the original and revised sequences, according + * to Gene Myers differencing algorithm. + * + * @param orig The original sequence. + * @param rev The revised sequence. + * @return A minimum {@link PathNode Path} accross the differences graph. + * @throws DifferentiationFailedException if a diff path could not be found. + */ + public static PathNode buildPath(Object[] orig, Object[] rev) + throws DifferentiationFailedException + { + if (orig == null) + throw new IllegalArgumentException("original sequence is null"); + if (rev == null) + throw new IllegalArgumentException("revised sequence is null"); + + // these are local constants + final int N = orig.length; + final int M = rev.length; + + final int MAX = N + M + 1; + final int size = 1 + 2 * MAX; + final int middle = (size + 1) / 2; + final PathNode diagonal[] = new PathNode[size]; + + PathNode path = null; + + diagonal[middle + 1] = new Snake(0, -1, null); + for (int d = 0; d < MAX; d++) + { + for (int k = -d; k <= d; k += 2) + { + final int kmiddle = middle + k; + final int kplus = kmiddle + 1; + final int kminus = kmiddle - 1; + PathNode prev = null; + + int i; + if ( (k == -d) || + (k != d && diagonal[kminus].i < diagonal[kplus].i)) + { + i = diagonal[kplus].i; + prev = diagonal[kplus]; + } + else + { + i = diagonal[kminus].i + 1; + prev = diagonal[kminus]; + } + + diagonal[kminus] = null; // no longer used + + int j = i - k; + + PathNode node = new DiffNode(i, j, prev); + + // orig and rev are zero-based + // but the algorithm is one-based + // that's why there's no +1 when indexing the sequences + while (i < N && j < M && orig[i].equals(rev[j])) + { + i++; + j++; + } + if (i > node.i) + node = new Snake(i, j, node); + + diagonal[kmiddle] = node; + + if (i >= N && j >= M) + { + return diagonal[kmiddle]; + } + } + diagonal[middle+d-1] = null; + + } + // According to Myers, this cannot happen + throw new DifferentiationFailedException("could not find a diff path"); + } + + /** + * Constructs a {@link Revision} from a difference path. + * + * @param path The path. + * @param orig The original sequence. + * @param rev The revised sequence. + * @return A {@link Revision} script corresponding to the path. + * @throws DifferentiationFailedException if a {@link Revision} could + * not be built from the given path. + */ + public static Revision buildRevision(PathNode path, Object[] orig, Object[] rev) + { + if (path == null) + throw new IllegalArgumentException("path is null"); + if (orig == null) + throw new IllegalArgumentException("original sequence is null"); + if (rev == null) + throw new IllegalArgumentException("revised sequence is null"); + + Revision revision = new Revision(); + if (path.isSnake()) + path = path.prev; + while (path != null && path.prev != null && path.prev.j >= 0) + { + if(path.isSnake()) + throw new IllegalStateException("bad diffpath: found snake when looking for diff"); + int i = path.i; + int j = path.j; + + path = path.prev; + int ianchor = path.i; + int janchor = path.j; + + Delta delta = Delta.newDelta(new Chunk(orig, ianchor, i - ianchor), + new Chunk(rev, janchor, j - janchor)); + revision.insertDelta(delta); + if (path.isSnake()) + path = path.prev; + } + return revision; + } + +} --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/myers/package.html +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/myers/package.html @@ -0,0 +1,80 @@ + + + + + + + + +

+ The {@link org.apache.maven.jrcs.diff.myers diff.myers} package + implements Gene Myers' + differencing algorithm. +

+

+ Myer's algorithm produces optimum results (minimum diffs), but + consumes considerably more memory than SimpleDiff, so its not + suitable for very large files. +

+@author Juanco Anez +@version $Id: package.html 146029 2003-05-10 18:56:10Z juanco $ +@see Diff +@see org.apache.maven.jrcs.rcs.Archive + + --- jspwiki-2.8.0.orig/src/org/apache/commons/jrcs/diff/myers/PathNode.java +++ jspwiki-2.8.0/src/org/apache/commons/jrcs/diff/myers/PathNode.java @@ -0,0 +1,146 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.jrcs.diff.myers; + +/** + * A node in a diffpath. + * + * @version $Revision: 1.7 $ $Date: 2003-10-13 11:00:44 +0300 (Mon, 13 Oct 2003) $ + * @author Juanco Anez + * + * @see DiffNode + * @see Snake + * + */ +public abstract class PathNode +{ + /** Position in the original sequence. */ + public final int i; + /** Position in the revised sequence. */ + public final int j; + /** The previous node in the path. */ + public final PathNode prev; + + /** + * Concatenates a new path node with an existing diffpath. + * @param i The position in the original sequence for the new node. + * @param j The position in the revised sequence for the new node. + * @param prev The previous node in the path. + */ + public PathNode(int i, int j, PathNode prev) + { + this.i = i; + this.j = j; + this.prev = prev; + } + + /** + * Is this node a {@link Snake Snake node}? + * @return true if this is a {@link Snake Snake node} + */ + public abstract boolean isSnake(); + + /** + * Is this a bootstrap node? + *

+ * In bottstrap nodes one of the two corrdinates is + * less than zero. + * @return tru if this is a bootstrap node. + */ + public boolean isBootstrap() + { + return i < 0 || j < 0; + } + + /** + * Skips sequences of {@link DiffNode DiffNodes} until a + * {@link Snake} or bootstrap node is found, or the end + * of the path is reached. + * @return The next first {@link Snake} or bootstrap node in the path, or + * null + * if none found. + */ + public final PathNode previousSnake() + { + if (isBootstrap()) + return null; + if (!isSnake() && prev != null) + return prev.previousSnake(); + return this; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + StringBuffer buf = new StringBuffer("["); + PathNode node = this; + while (node != null) + { + buf.append("("); + buf.append(Integer.toString(node.i)); + buf.append(","); + buf.append(Integer.toString(node.j)); + buf.append(")"); + node = node.prev; + } + buf.append("]"); + return buf.toString(); + } +} \ No newline at end of file --- jspwiki-2.8.0.orig/src/org/freshcookies/security/cert/JarHelper.java +++ jspwiki-2.8.0/src/org/freshcookies/security/cert/JarHelper.java @@ -0,0 +1,249 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.cert; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + *

Extracts certificates used to sign a specified + * JAR file and optionally save them to disk. If the SecurityManager is running, the + * ProtectionDomain of this class must grant read access to any jar files + * passed to {@link #extractCACertificates(JarFile)} or + * {@link #extractSigningCertificates(JarFile)}. For example, to process + * the jar file /etc/myclasses.jar, the security + * policy file should grant the following permission to the + * freshcookies-securty-version.jar CodeSource and + * all preceding callers:

+ *
permission java.io.FilePermision "/etc/myclasses.jar", "read"
+ *

In addition, if the stand-alone {@link #main(String[])} method is used to automatically + * extract discovered certificates or add them to the system-wide trust store, this + * CodeSource and all preceding callsers require these additional privileges:

+ *
    + *
  • java.io.FilePermission "${java.home}/lib/security/cacerts", "read,write"
  • + *
  • java.lang.RuntimePermission "writeFileDescriptor"
  • + *
  • java.util.PropertyPermission "javax.net.ssl.trustStore", "read"
  • + *
  • java.util.PropertyPermission "java.home" "read"
  • + *
+ *

None of the methods in this class perform their actions inside + * doPrivileged blocks, so all {@link SecurityException} errors + * are propagated to callers.

+ * @author Andrew R. Jaquith + * @version $Revision: 1.4 $ $Date: 2007/02/24 17:27:28 $ + */ +public class JarHelper { + + private Map caCache = new HashMap(); + + private Map certCache = new HashMap(); + + private Set jars = new HashSet(); + + /** + * Constructs a new instance of JarHelper. + */ + public JarHelper() { + super(); + } + + /** + * Convenience main method that extracts the signing certificates from a jar file + * and optionally saves them to disk and the system trust store. + * @param args the absolute path to the jar file, or --help to print + * a short help message. + * @throws SecurityException when the SecurityManager is running, and the current + * policy does not grant this ProtectionDomain and preceding callers all of the following + * permissions: + *
    + *
  • java.io.FilePermission "${java.home}/lib/security/cacerts", "read,write"
  • + *
  • java.lang.RuntimePermission "writeFileDescriptor"
  • + *
  • java.util.PropertyPermission "javax.net.ssl.trustStore", "read"
  • + *
  • java.util.PropertyPermission "java.home" "read"
  • + *
+ */ + public static void main(String[] args) { + if (args.length == 0) { + System.err.println("FATAL: you must supply a jar file (e.g., foo.jar)"); + System.exit(1); + } + + // Display help if the user asked for it + if (args[0].equals("--help") | args[0].equals("-h")) { + System.out.println("Usage: JarHelper jarfile"); + System.exit(0); + } + + // Set the jar file + String file = args[0]; + + Trustee trustee = new Trustee(); + JarHelper helper = new JarHelper(); + + // Open the jar and get its signing certificates + System.out.print("Extracting signing certificates from " + file + "... "); + Set cas = new HashSet(); + Set signers = new HashSet(); + try { + JarFile jar = new JarFile(new File(file), true); + cas = helper.extractCACertificates(jar); + signers = helper.extractSigningCertificates(jar); + } + catch (IOException e) { + System.err.println("Couldn't get jar certificates: " + + e.getLocalizedMessage()); + } + + // Offer to add untrusted CA certificates to trust store + System.out.println("Found " + cas.size() + " CA certificates."); + boolean certsAdded = false; + int i = 0; + for (Iterator it = cas.iterator(); it.hasNext();) { + X509Certificate cert = (X509Certificate)it.next(); + System.out.println("CA certificate [" + i + "]:"); + System.out.println(Trustee.getCertificateInfo(cert)); + try { + certsAdded = certsAdded | trustee.trustCACertificate(cert); + } + catch (Exception e) { + System.err.println("Could not add certificate to trust store: " + + e.getLocalizedMessage()); + } + i++; + } + + // Tell the user about the signing certificates we found + System.out.println("Found " + signers.size() + " signing certificates."); + i = 0; + for (Iterator it = signers.iterator(); it.hasNext();) { + X509Certificate cert = (X509Certificate)it.next(); + System.out.println(Trustee.getCertificateInfo(cert)); + } + + // Now, save the CA certs and signing certs to disk + try { + for (Iterator it = cas.iterator(); it.hasNext();) { + X509Certificate cert = (X509Certificate)it.next(); + trustee.saveCertificate(cert); + } + for (Iterator it = signers.iterator(); it.hasNext();) { + X509Certificate cert = (X509Certificate)it.next(); + trustee.saveCertificate(cert); + } + } + catch (Exception e) { + System.err.println("Could not save certificate: " + + e.getLocalizedMessage()); + } + + // Commit the trust store changes + System.out.print("Committing changes to trust store... "); + if (trustee.commit()) { + System.err.println("done."); + } + else { + System.err.println("trust store not saved."); + } + + } + + /** + * Extracts the CA certificates from a Jar file. + * + * @param jar the jar file + * @return an array of CA {@link java.security.cert.X509Certificate} objects + * @throws IOException if the jar cannot be read + * @throws SecurityException when the SecurityManager is running, + * and it denies read access to the jar file + */ + public Set extractCACertificates(JarFile jar) + throws IOException { + if (!jars.contains(jar)) { + extractJarCerts(jar); + jars.add(jar); + } + return (Set)caCache.get(jar); + } + + /** + * Extracts the signing certificates from a Jar file. + * + * @param jar the jar file + * @return an array of {@link java.security.cert.X509Certificate} objects used to sign various files + * @throws IOException if the jar cannot be read + * @throws SecurityException when the SecurityManager is running, + * and it denies read access to the jar file + */ + public Set extractSigningCertificates(JarFile jar) + throws IOException { + if (!jars.contains(jar)) { + extractJarCerts(jar); + jars.add(jar); + } + return (Set)certCache.get(jar); + } + + /** + * Extracts signing certificates from a jar file. + * @param jar + * @throws IOException if the jar cannot be read + * @throws SecurityException when the SecurityManager (if running) denies access to the file + */ + private void extractJarCerts(JarFile jar) throws IOException { + Set cas = new HashSet(); + Set certs = new HashSet(); + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = (JarEntry) entries.nextElement(); + InputStream is = jar.getInputStream(entry); + byte[] buf = new byte[1024]; + while (is.read(buf) > 0) { + } + is.close(); + Certificate[] signingCerts = entry.getCertificates(); + if (signingCerts != null) { + for (int i = 0; i < signingCerts.length; i++) { + Certificate cert = signingCerts[i]; + if (cert instanceof X509Certificate) { + X509Certificate x509cert = (X509Certificate) cert; + if (i == 0 && !certs.contains(x509cert)) { + certs.add(x509cert); + } + else if (i > 0 && !cas.contains(x509cert)) { + cas.add(x509cert); + } + } + } + + // If the chain had only one cert, it's self-signed + if (signingCerts.length == 1) { + if (signingCerts[0] instanceof X509Certificate) { + cas.add(signingCerts[0]); + } + } + } + } + caCache.put(jar, cas); + certCache.put(jar, certs); + }} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/cert/SSLHelper.java +++ jspwiki-2.8.0/src/org/freshcookies/security/cert/SSLHelper.java @@ -0,0 +1,216 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.cert; + +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +import javax.net.SocketFactory; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +/** + * Extracts SSL certificates from a specified webserver and optionally adds them + * to the JSSE truststore. + * + * @author Andrew R. Jaquith + * @version $Revision: 1.3 $ $Date: 2007/02/24 17:27:28 $ + */ +public class SSLHelper { + + /** + * Default SSL port. + */ + protected static final int SSL_PORT = 443; + + /** + * Helper method that connects to a specified host using SSL and extracts the + * server's peer certificates. To do this, a new SSLContext is + * created using a "null" trust manager that accepts all peer SSL certificates + * as trusted. The null trust manager is provided by the method + * nullTrustManager. Note that in the array of Certificates + * returned by this method, the first certficate is the server's own + * certificate; the ones that follow are the certificate authorities in the + * chain. + * + * @param hostname + * @return the remote host's chain of SSL certificates + */ + public static Certificate[] extractSSLCertificates(String hostname, int port) { + Certificate certs[] = new Certificate[] {}; + TrustManager trustManager = nullTrustManager(); + SSLSocket socket = createSSLSocket(new TrustManager[] { + trustManager + }, hostname, port); + if (socket != null) { + try { + certs = socket.getSession().getPeerCertificates(); + socket.close(); + } + catch (SSLPeerUnverifiedException e) { + System.out.println("Could not verify peer: " + e.getMessage()); + } + catch (IOException e) { + System.out.println("Could not close socket: " + e.getMessage()); + } + } + else { + System.err.println("could not create SSL socket."); + } + return certs; + } + + public static void main(String[] args) { + if (args.length == 0) { + System.err + .println("FATAL: you must supply a host name (e.g., internal.atstake.com)"); + System.exit(1); + } + + // Display help if the user asked for it + if (args[0].equals("--help") | args[0].equals("-h")) { + System.out.println("Usage: SSLHelper hostname [port]"); + System.exit(0); + } + + // Set the host name and port (defaults to SSL_PORT) + String host = args[0]; + int port = SSL_PORT; + if (args.length > 1) { + port = Integer.parseInt(args[1]); + } + + // Connect to the host and get its SSL certificates + System.out.print("Extracting SSL certificates from " + host + ":" + port + + "... "); + Certificate[] certs = extractSSLCertificates(host, port); + if (certs.length == 0) { + System.err + .println("ERROR: No certificates found. Is there an SSL server running on this host?\n"); + System.exit(1); + } + System.out.println(certs.length + " certificate" + + (certs.length == 1 ? "" : "s") + " found."); + + // Loop through each certificate in the chain + boolean certsAdded = false; + + Trustee trustee = new Trustee(); + for (int i = 0; i < certs.length; i++) { + if (certs[i] instanceof X509Certificate) { + + // Write each cert to disk as a DER-encoded file + X509Certificate cert = (X509Certificate) certs[i]; + System.out.println("Certificate[" + i + "]:"); + System.out.println(Trustee.getCertificateInfo(cert)); + try { + trustee.saveCertificate(cert); + } + catch (Exception e) { + System.err.println("ERROR: could not save certificate. " + + e.getMessage()); + } + + // Test each CA certificate to see if it is trusted; offer to + // add it if not + if (i == 0) { + System.out.println("This is the server certificate."); + } + else { + try { + certsAdded = certsAdded | trustee.trustCACertificate(cert); + } + catch (KeyStoreException e) { + System.out.println(e.getLocalizedMessage()); + } + } + System.out.println(""); + } + } + + if (certs.length == 1) { + System.err + .println("WARNING: certificate chain did not include issuing CA certificate. How rude!\n"); + } + + // If changes were made to the trust store, write it to disk + if (certsAdded) { + trustee.commit(); + } + } + + /** + * Constructs a new SSLHelper. + */ + public SSLHelper() { + super(); + } + + /** + * Creates a new SSL socket to a specificed host using the supplied + * TrustManager. If a connection cannot be made, the method + * returns null. + * + * @param trustManager + * @param hostname + * @return + */ + private static SSLSocket createSSLSocket(TrustManager[] trustManager, + String hostname, int port) { + SSLSocket socket = null; + try { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustManager, new SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sslContext + .getSocketFactory()); + SocketFactory factory = sslContext.getSocketFactory(); + socket = (SSLSocket) factory.createSocket(hostname, port); + socket.startHandshake(); + } + catch (Exception e) { + + } + return socket; + } + + /** + * Creates and returns an implementation of a TrustManager that + * trusts all SSL certificates. This method is used in the initial handshake + * to the target SSL server, so that its certificates can be extracted. + * + * @return + */ + private static X509TrustManager nullTrustManager() { + return new X509TrustManager() { + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/cert/package.html +++ jspwiki-2.8.0/src/org/freshcookies/security/cert/package.html @@ -0,0 +1,109 @@ + + + + + + + + +

+ This package contains serveral useful utilities for managing + common security operations, such as SSL certificate extraction, + JAR certificate extraction, and policy file manipulation. +

+ +

+ JarHelper +

+ +

+ SSLHelper is a command-line based utility that examines the SSL + certificates and certificate chains for a given host and port. If + the SSL certificate chain is untrusted, the utility offers the + user the option of placing the certificates in the JSSE + certificate store. It also outputs all of the certificates it + finds (including the server's) as DER-encoded files in the + current directory. These files can then be double-clicked and + imported straight into the Windows certificate store, or appended + (using Keychain) to the Mac OS X trust anchors! Incredibly handy + for troubleshooting certificate trust issues with (for instance) + self-signed JNLP applications. +

+ +

+ Usage +

+ +

+ On the command line type the following: +

+ +
+

+ java -cp freshcookies-security-0.3.jar + org.freshcookies.security.cert.SSLHelper www.example.com +

+
+ +

+ SSLHelper will connect to the target website (here, + www.example.com) and download its SSL certificates and CA chains + (if provided in the handshake). An optional second parameter + specifies a port to use (the default is 443). +

+ +

+ In addition to the command-line version, the + SSLHelper class itself has a method called + extractSSLCertificates(String hostname, int port) + that performs the same operation.that returns an array of + java.security.cert.Certificate. (It does not offer + to add the certificates to the JSSE certificate store if they are + untrusted.) +

+ +

+ JarHelper +

+ +

+ JarHelper is a command-line utility that extracts certificates + used to sign a specified JAR file and, if any are found, saves + them to disk. If the certificate chain is untrusted, the utility + offers the user the option of placing the certificates in the + java certificate trust store. +

+ +

+ Usage +

+ +

+ On the command line type the following: +

+ +
+

+ java -cp freshcookies-security-0.3.jar + org.freshcookies.security.cert.JarHelper + mysignedcode.jar +

+
+ +

+ In addition to the command-line version, the + JarHelper class itself has two methods called + extractCACertificates(JarFile jar) and + extractSigningCertificates that returns arrays of + java.security.cert.Certificate. (They do not offer + to add the certificates to the JSSE certificate store if they are + untrusted.) +

+ +

+ @author Andrew R. Jaquith + @version $Revision: 1.1 $ $Date: 2006/05/02 04:36:39 $ +

+ + --- jspwiki-2.8.0.orig/src/org/freshcookies/security/cert/CertificateDN.java +++ jspwiki-2.8.0/src/org/freshcookies/security/cert/CertificateDN.java @@ -0,0 +1,174 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.cert; + +import java.security.Principal; +import java.security.cert.X509Certificate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Lightweight wrapper object for an X.509 certificate distinguished name. + * + * @author Andrew R. Jaquith + * @version $Revision: 1.1 $ $Date: 2006/04/19 23:34:44 $ + */ +public final class CertificateDN implements Principal { + + private String commonName = null; + + private String country = null; + + private String domainComponent = null; + + private String email = null; + + private String locality = null; + + private String name; + + private String organization = null; + + private String organizationalUnit = null; + + private String state = null; + + /** + * Constructs a new CertificateDN by parsing a supplied Principal from a + * certificate, such as {@link X509Certificate#getSubjectDN()} or + * {@link X509Certificate#getIssuerDN()}. + * + * @param dn a Principal representing a certificate distinguished name + * + */ + public CertificateDN(Principal dn) { + if (dn == null) { + throw new IllegalArgumentException("Disinguished name cannot be null."); + } + this.name = dn.getName(); + + Pattern p = Pattern.compile("(CN|E|OU|O|L|ST|C|DC)=(\"{0,1})(.+?)(\\2),"); + Matcher m = p.matcher(dn.getName() + ","); + while (m.find()) { + String key = m.group(1); + String value = m.group(3); + if ("CN".equals(key)) { + commonName = value; + } + else if ("E".equals(key)) { + email = value; + } + else if ("OU".equals(key)) { + organizationalUnit = value; + } + else if ("O".equals(key)) { + organization = value; + } + else if ("L".equals(key)) { + locality = value; + } + else if ("ST".equals(key)) { + state = value; + } + else if ("C".equals(key)) { + country = value; + } + else if ("DC".equals(key)) { + domainComponent = value; + } + } + } + + /** + * Returns the common name + * + * @return the common name + */ + public final String getCommonName() { + return commonName; + } + + /** + * Returns the country. + * + * @return the country + */ + public final String getCountry() { + return country; + } + + /** + * Returns the domain component. + * + * @return the domain component + */ + public final String getDomainComponent() { + return domainComponent; + } + + /** + * Returns the email. + * + * @return the email + */ + public final String getEmail() { + return email; + } + + /** + * Returns the locality. + * + * @return the locality + */ + public final String getLocality() { + return locality; + } + + /** + * Returns the distinguished name. + * + * @return the distinguished name. + */ + public final String getName() { + return name; + } + + /** + * Returns the organization. + * + * @return the organization + */ + public final String getOrganization() { + return organization; + } + + /** + * Returns the organizational unit. + * + * @return the organizational unit + */ + public final String getOrganizationalUnit() { + return organizationalUnit; + } + + /** + * Returns the state. + * + * @return the state + */ + public final String getState() { + return state; + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/cert/Trustee.java +++ jspwiki-2.8.0/src/org/freshcookies/security/cert/Trustee.java @@ -0,0 +1,457 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.cert; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.DateFormat; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +/** + *

Utility class that performs common certificate trust operations, such as + * writing to disk, updating the trust database and printing verbose certficate + * info.

+ *

This class requires privileges to run with a SecurityManager. + * At a mimumum, this ProtectionDomain and preceding callers must be granted the following + * permissions:

+ *
    + *
  • java.util.PropertyPermission "javax.net.ssl.trustStore", "read"
  • + *
  • java.util.PropertyPermission "java.home", "read"
  • + *
  • java.io.FilePermission "${java.home}/lib/security/cacerts", "read"
  • + *
+ *

In addition, if the method {@link #saveCertificate(X509Certificate)} + * is called, this ProtectionDomain and all preceding callers must also be + * granted these additional permissions:

+ *
    + *
  • java.lang.RuntimePermission "writeFileDescriptor"
  • + *
  • java.io.FilePermission "${user.dir}/*.cer", "write"
  • + *
+ *

The {@link #commit()} method requires that this ProtectionDomain and + * all preceding callers be granted these additional permisions:

+ *
    + *
  • java.lang.RuntimePermission "writeFileDescriptor"
  • + *
  • java.io.FilePermission "${java.home}/lib/security/cacerts", "write"
  • + *
+ *

None of the methods in this class perform their actions inside + * doPrivileged blocks, so all {@link SecurityException} errors + * are propagated to callers.

+ * @author Andrew R. Jaquith + * @version $Revision: 1.3 $ $Date: 2007/02/24 17:27:28 $ + */ +public class Trustee { + + private boolean certsAdded; + + private final X509TrustManager sslTrustManager; + + private String trustedCAPath; + + private final KeyStore trustedCAStore; + + /** + * Constructs a new Trustee instance, and initializes the system certificate + * authority (CA) keystore and SSL trust manager. + * @throws SecurityException when the SecurityManager is running, and the current + * policy does not grant this ProtectionDomain and preceding callers all of the following + * permissions: + *
  • java.util.PropertyPermission "javax.net.ssl.trustStore", "read"
  • + *
  • java.util.PropertyPermission "java.home" "read"
  • + *
  • java.io.FilePermission "${java.home}/lib/security/cacerts", "read"
+ */ + public Trustee() { + super(); + trustedCAStore = initSystemCAStore(); + sslTrustManager = initSSLTrustManager(); + certsAdded = false; + } + + /** + * Calculates an alias for a certificate by trying the common name, + * organizational unit, DC and serial number in succession. + * + * @param cert the certificate + * @return the alias + */ + public static String getAlias(X509Certificate cert) { + CertificateDN dn = new CertificateDN(cert.getSubjectDN()); + if (dn.getCommonName() != null) { + return dn.getCommonName(); + } + if (dn.getOrganizationalUnit() != null) { + return dn.getOrganizationalUnit() + "-" + + cert.getSerialNumber().toString(); + } + if (dn.getDomainComponent() != null) { + return dn.getDomainComponent() + "-" + cert.getSerialNumber().toString(); + } + return cert.getSerialNumber().toString(); + } + + /** + * Returns a string containing verbose certificate information. The format is + * identical to that produced by the J2SE keytool program. + * + * @param cert the certificate to examine + */ + public static String getCertificateInfo(X509Certificate cert) { + String date = DateFormat.getDateInstance(DateFormat.MEDIUM).format( + cert.getNotBefore()); + String info = "Creation date: " + date + "\n"; + info += "Owner:\n"; + CertificateDN s = new CertificateDN(cert.getSubjectDN()); + info += (s.getCommonName() != null) ? " CN=" + s.getCommonName() + + "\n" : ""; + info += (s.getOrganization() != null) ? " O=" + s.getOrganization() + + "\n" : ""; + info += (s.getOrganizationalUnit() != null) ? " OU=" + + s.getOrganizationalUnit() + "\n" : ""; + info += (s.getDomainComponent() != null) ? " DC=" + + s.getDomainComponent() + "\n" : ""; + info += (s.getLocality() != null) ? " L=" + s.getLocality() + "\n" + : ""; + info += (s.getState() != null) ? " S=" + s.getState() + "\n" : ""; + info += (s.getCountry() != null) ? " C=" + s.getCountry() + "\n" + : ""; + info += (s.getEmail() != null) ? " E=" + s.getEmail() + "\n" : ""; + info += "Issuer:\n"; + s = new CertificateDN(cert.getIssuerDN()); + info += (s.getCommonName() != null) ? " CN=" + s.getCommonName() + + "\n" : ""; + info += (s.getOrganization() != null) ? " O=" + s.getOrganization() + + "\n" : ""; + info += (s.getOrganizationalUnit() != null) ? " OU=" + + s.getOrganizationalUnit() + "\n" : ""; + info += (s.getDomainComponent() != null) ? " DC=" + + s.getDomainComponent() + "\n" : ""; + info += (s.getLocality() != null) ? " L=" + s.getLocality() + "\n" + : ""; + info += (s.getState() != null) ? " S=" + s.getState() + "\n" : ""; + info += (s.getCountry() != null) ? " C=" + s.getCountry() + "\n" + : ""; + info += (s.getEmail() != null) ? " E=" + s.getEmail() + "\n" : ""; + info += "Serial number: " + cert.getSerialNumber() + "\n"; + info += "Valid from: " + cert.getNotBefore() + " until: " + + cert.getNotAfter() + "\n"; + info += "Certificate fingerprints:\n"; + info += " MD5: " + getCertFingerPrint("MD5", cert) + "\n"; + info += " SHA1: " + getCertFingerPrint("SHA1", cert) + "\n"; + return info; + } + + /** + * Appends an ASCII representation of a byte to a supplied StringBuffer. + * + * @param byte0 the byte to encode + * @param stringbuffer the buffer to append to + */ + private static void byte2hex(byte byte0, StringBuffer stringbuffer) { + char ac[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F' + }; + int i = (byte0 & 0xf0) >> 4; + int j = byte0 & 0xf; + stringbuffer.append(ac[i]); + stringbuffer.append(ac[j]); + } + + /** + * Calculates the fingerprint for a certificate and returns it as a string. + * + * @param s the digest algorithm to use + * @param certificate the certificate + * @return the fingerprint + */ + private static String getCertFingerPrint(String s, Certificate certificate) { + try { + byte abyte0[] = certificate.getEncoded(); + MessageDigest messagedigest = MessageDigest.getInstance(s); + byte abyte1[] = messagedigest.digest(abyte0); + return toHexString(abyte1); + } + catch (Exception e) { + return "(error)"; + } + } + + /** + * Encodes an array of bytes and its ASCII string representation. + * + * @param abyte0 the array of bytes to encode + * @return the string + */ + private static String toHexString(byte abyte0[]) { + StringBuffer stringbuffer = new StringBuffer(); + int i = abyte0.length; + for (int j = 0; j < i; j++) { + byte2hex(abyte0[j], stringbuffer); + if (j < i - 1) + stringbuffer.append(":"); + } + + return stringbuffer.toString(); + } + + /** + *

+ * Adds a supplied CA certificate to the system certificate trust store as a + * "trusted CA certificate". This is equivalent to the following command-line + * action: + *

+ * keytool -import -trustcacerts -keystore $JAVA_HOME/lib/security/cacerts -file certificate + *

+ * Note: a useful argument for debugging is -Djavax.net.debug=all. + *

+ * + * @param cert the certificate to add to the trust store + * @return true if successfully added; false if + * not + * @throws KeyStoreException if the CA alias ould not be added to the trust store + */ + public boolean trustCACertificate(X509Certificate cert) + throws KeyStoreException { + if (!isTrustedCA(cert)) { + System.out.println("This is a CA certificate. It is NOT trusted."); + try { + if (yesResponse("Do you want to trust this certificate?")) { + String alias = Trustee.getAlias(cert); + System.out.println("Adding CA to trust store with alias " + alias); + trustedCAStore.setCertificateEntry(alias, cert); + System.out.println("..success"); + certsAdded = true; + return true; + } + } + catch (IOException e) { + System.out.println("Could not add certificate: " + + e.getLocalizedMessage()); + return false; + } + } + System.out.println("This is a CA certificate. It is already trusted."); + return false; + } + + /** + * Writes the trust store to disk. The caller must have write access to the + * systemwide trust store. + * @return true if the commit succeeds; false if + * not + * @throws SecurityException when the SecurityManager is running, and the current policy does + * not grant this ProtectionDomain and preceding callers all of the following + * permissions:
    + *
  • java.lang.RuntimePermission "writeFileDescriptor"
  • + *
  • java.io.FilePermission "${java.home}/lib/security/cacerts", "write"
+ */ + public boolean commit() { + if (!certsAdded) { + System.err.println("No need to commit (no certificates added)."); + return false; + } + try { + File file = new File(trustedCAPath); + if (file.canWrite()) { + OutputStream out = new FileOutputStream(trustedCAPath); + trustedCAStore.store(out, "changeit".toCharArray()); + out.close(); + return true; + } + System.out.println("FATAL: You do not have write privileges " + + "to the Java JSSE trust store " + trustedCAPath + + "\n\nTry running the application using sudo, or as root.\n"); + } + catch (CertificateException e) { + System.out.println("Certificate exception: " + e.getMessage()); + } + catch (NoSuchAlgorithmException e) { + System.out.println("No such algorithm: " + e.getMessage()); + } + catch (KeyStoreException e) { + System.out.println("Keystore exception: " + e.getMessage()); + } + catch (IOException e) { + System.out.println("IO exception: " + e.getMessage()); + } + return false; + } + + /** + * Saves an X.509 certificate as a binary file in the current directory. The + * name of the file is calculated by taking the certificate's common name and + * appending the suffix .cer. This suffix is sufficiently + * cross-platform that Mac OS X and Windows users alike can simply + * double-click on the file to install it using the respective certificate + * management tool. + * + * @param cert the certificate to save + * @throws IOException if the file could not be written + * @throws CertificateEncodingException if the certificate could not be properly encoded + * @throws SecurityException when the SecurityManager is running, and the current policy does + * not grant this ProtectionDomain and preceding callers all of the following + * permissions:
    + *
  • java.lang.RuntimePermission "writeFileDescriptor"
  • + *
  • java.io.FilePermission "${user.dir}/*.cer", "write"
+ */ + public void saveCertificate(X509Certificate cert) throws IOException, + CertificateEncodingException { + String alias = Trustee.getAlias(cert); + alias = alias.replaceAll("[,\\.\\\\/]", ""); + String file = System.getProperty("user.dir") + "/" + alias + ".cer"; + FileOutputStream out = new FileOutputStream(file); + out.write(cert.getEncoded()); + out.close(); + System.out.println("Saved certificate as " + file); + } + + /** + * Initializes and returns the system JSSE trust manager. The algorithm used + * is the default returned by the system property + * ssl.TrustManagerFactory.algorithm, which in normal + * cicrcumstances should be SunX509. The keystore used for + * trusted CA certificates is the one previously initialized by + * {@link #initSystemCAStore()}. + * @return the SSL trust manager, initialized + */ + private X509TrustManager initSSLTrustManager() { + TrustManager[] managers = new TrustManager[] {}; + try { + TrustManagerFactory factory = TrustManagerFactory + .getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init(trustedCAStore); + managers = factory.getTrustManagers(); + } + catch (KeyStoreException e) { + System.out + .println("Could not initialize trust manager with system keystore: " + + e.getMessage()); + } + catch (NoSuchAlgorithmException e) { + System.out.println("No such algorithm: " + e.getMessage()); + } + for (int i = 0; i < managers.length; i++) { + if (managers[i] instanceof X509TrustManager) { + return (X509TrustManager) managers[i]; + } + } + return null; + } + + /** + * Initializes and returns the KeyStore where JSSE trusted CA certificates are + * stored. The method first checks to see if the + * javax.net.ssl.trustStore property is set; if it is, the + * keystore at that location is loaded. If not, the keystore in the default + * location JAVA_HOME/lib/security/cacerts is + * loaded. + * + * @return the JSSE trust store, initialized + * @throws SecurityException when the SecurityManager is running, and the current policy does + * not grant this ProtectionDomain and preceding callers all of the following + * permissions: java.util.PropertyPermission "javax.net.ssl.trustStore", + * java.util.PropertyPermission "java.home", + * java.io.FilePermission "${java.home}/lib/security/cacerts", "read" + */ + private KeyStore initSystemCAStore() { + + // Ask where the JRE thinks the trust store is + this.trustedCAPath = System.getProperty("javax.net.ssl.trustStore"); + if (trustedCAPath == null) { + trustedCAPath = System.getProperty("java.home") + "/lib/security/cacerts"; + } + + // Now, open it + KeyStore store = null; + try { + store = KeyStore.getInstance(KeyStore.getDefaultType()); + + InputStream fis = new FileInputStream(trustedCAPath); + System.out.println("Locating certificate trust store: " + trustedCAPath); + store.load(fis, "changeit".toCharArray()); + fis.close(); + } + catch (FileNotFoundException e) { + System.out.println("Could not open keystore file: " + e.getMessage()); + } + catch (IOException e) { + System.out.println("IO exception: " + e.getMessage()); + } + catch (CertificateException e) { + System.out.println("Certificate exception: " + e.getMessage()); + } + catch (KeyStoreException e) { + System.out.println("Could not get keystore: " + e.getMessage()); + } + catch (NoSuchAlgorithmException e) { + System.out.println("No such algorithm: " + e.getMessage()); + } + return store; + } + + /** + * Determines whether a supplied CA certificate is considered "trusted" by a + * TrustManager. To determine whether the certificate is already trusted, the + * supplied TrustManager's getAcceptedIssuers method is + * consulted. + * + * @param cert the X.509 certificate to test + * @return true if the certificate is trusted, + * false if not + */ + private boolean isTrustedCA(X509Certificate cert) { + Certificate[] trustedCAs = sslTrustManager.getAcceptedIssuers(); + boolean isCertTrusted = false; + for (int j = 0; j < trustedCAs.length; j++) { + if (cert.equals(trustedCAs[j])) { + isCertTrusted = true; + break; + } + } + return isCertTrusted; + } + + /** + * Obtains a "yes" or "no" response from standard input. + * + * @param prompt the prompt + * @return true if the response is "yes", false + * otherwise + */ + private synchronized boolean yesResponse(String prompt) throws IOException { + String response = " "; + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + while (!"YES".equals(response) && !"NO".equals(response)) { + System.out.print(prompt + " (yes/no): "); + response = reader.readLine().toUpperCase().trim(); + } + return ("YES".equals(response)); + } +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/GenericPrincipal.java +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/GenericPrincipal.java @@ -0,0 +1,71 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.policy; + +import java.security.Principal; + +/** + * Simple principal class. + * + * @author Andrew Jaquith + * @version $Revision: 1.3 $ $Date: 2007/02/24 17:27:43 $ + */ +public final class GenericPrincipal implements Principal { + + private final String name; + + private final int hashCode; + + /** + * Constructs a new GenericPrincipal with a supplied name. + * + * @param name the name of the Principal + */ + public GenericPrincipal(String name) { + this.name = name; + this.hashCode = name.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public final boolean equals(Object obj) { + if (obj instanceof GenericPrincipal) { + return ((GenericPrincipal) obj).getName().equals(name); + } + return false; + } + + /** + * @see java.security.Principal#getName() + */ + public final String getName() { + return name; + } + + /** + * @see java.lang.Object#hashCode() + */ + public final int hashCode() { + return hashCode; + } + + /** + * @see java.lang.Object#toString() + */ + public final String toString() { + return "(GenericPrincipal=" + name + ")"; + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/LocalPolicy.java +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/LocalPolicy.java @@ -0,0 +1,312 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.policy; + +import java.io.File; +import java.io.IOException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + *

+ * Security policy class that implements a subset of + * {@link java.security.Policy} methods, using a standard Java security policy + * file as input. This class is ideally suited for "local authorization" cases + * where consultation of a global JVM policy is not necessary or desired, but + * where polices are expressed in the standard policy file grammar. This class + * differs from standard Policy implementations in that it does not extend the + * Policy superclass, and it implements only two of its methods: + * {@link #implies(ProtectionDomain, Permission)} and {@link #refresh()}. + * Because LocalPolicy does not extend Policy, it cannot be installed as a + * system-wide security policy (which is probably a good thing, because it is + * not optimized as expertly as Sun's standard PolicyFile implementation). + *

+ *

+ * The constructor of this class accepts a {@link java.io.File} from which the + * policy is parsed and read using the {@link PolicyReader} class. The policy is + * static and is derived only from the content of the file. If the + * policy file changes in the file system, callers may reload it by calling + * {@link #refresh()}. + *

+ *

+ * This class is not thread-safe. It requires the same permissions to run + * as the underlying PolicyReader used to parse the policy file. + * (See {@link PolicyReader} for more details. + *

+ * + * @author Andrew Jaquith + * @version $Revision: 1.2 $ $Date: 2007/04/09 02:34:52 $ + * @see PolicyReader + */ +public class LocalPolicy { + + private boolean loaded; + + private final File policyFile; + + private final Set pds; + + private final String charset; + + /** + * Constructs a new instance of a LocalPolicy object, whose policy rules are + * parsed from a supplied File using the standard Java platform encoding. + * The policy file's contents and structure are + * expected to be identical to those used in the J2SE default policy file + * implementation. + * + * @param file + * the file supplying the security policy + */ + public LocalPolicy(File file) { + this(file, (String)AccessController.doPrivileged( new PrivilegedAction() { + public Object run() { return System.getProperty("file.encoding"); } })); + } + + /** + * Constructs a new instance of a LocalPolicy object, whose policy rules are + * parsed from a supplied File using the standard Java platform encoding. + * The policy file's contents and structure are + * expected to be identical to those used in the J2SE default policy file + * implementation. + * + * @param file + * the file supplying the security policy + * @param encoding + * the charset name, such as UTF-8 or ISO-8859-1 + */ + public LocalPolicy(File file, String encoding) { + super(); + loaded = false; + policyFile = file; + pds = new HashSet(); + charset = encoding; + } + + /** + * Returns true if the security policy grants a particular + * Permission to a ProtectionDomain whose code source, certificates and + * classloader match the one supplied as a parameter. This method contains + * the exact same method signature as {@link java.security.Policy#implies(ProtectionDomain, Permission)} + * but differs in a key respect: Permissions for the supplied ProtectionDomain are + * looked up based on its CodeSource, ClassLoader and Principals, rather + * than looking for one that is logically equal to it. + * + * @param domain + * the protection domain + * @param permission + * the permission to check for + * @return true if the permission was granted, + * false if not + * @throws IllegalStateException if the policy needed refreshing but could + * not be refreshed + */ + public boolean implies(ProtectionDomain domain, Permission permission) { + + // If policy file not loaded, do it now + if (!loaded) { + try { + refresh(); + } + catch (PolicyException e) { + throw new IllegalStateException(e.getMessage()); + } + } + + // Iterate through the policy domains and find + // one whose codesource, principals and classloader match + for (Iterator it = pds.iterator(); it.hasNext();) { + LocalProtectionDomain pd = (LocalProtectionDomain)it.next(); + boolean samePrincipals = samePrincipals(pd.getPrincipals(), domain.getPrincipals()); + boolean sameCodeSource = pd.getCodeSource().implies(domain.getCodeSource()); + boolean sameClassLoader = pd.getClassLoader().equals(domain.getClassLoader()); + if (samePrincipals && sameCodeSource && sameClassLoader) { + + // Ok! All we have to do now is see if our local policy domain grants the permission + // (if not, keep going...) + PermissionCollection permissions = pd.getPermissions(); + if (permissions.implies(permission)) { + return true; + } + } + } + return false; + } + + /** + * Determines whether two Principal arrays contain the same items, which may appear in any order + * @param p1 the first array + * @param p2 the second array + * @return true if every item in the first array is equal to one other + * item in the second array; false otherwise + */ + protected boolean samePrincipals(Principal[] p1, Principal[] p2) { + if (p1.length != p2.length) { + return false; + } + + // Set up a list of 'leftover' items that we pop from every time we find a match + List leftovers = new ArrayList(); + for (int i = 0; i < p2.length; i++) { + leftovers.add(p2[i]); + } + + // Go through first array and remove the corresponding principal from leftover collection + for (int i = 0; i < p1.length; i++) { + Principal p = p1[i]; + for (Iterator it = leftovers.iterator(); it.hasNext();) { + Principal leftover = (Principal)it.next(); + if (p.equals(leftover)) { + it.remove(); + break; + } + } + } + // If nothing left in leftovers, everything must've matched + return (leftovers.size() == 0); + } + + /** + *

Reloads the policy from the filesystem. This method contains + * the exact same method signature as {@link java.security.Policy#refresh()} + * and is functionally equivalent. If the policy does not parse + * correctly, it will print any errors to standard out and throw an + * IllegalStateException. This method does not itself require security + * permissions to run, although the underlying PolicyReader used to parse + * the policy file does. (See {@link PolicyReader#read()} for + * more details.

+ * @throws PolicyException if the policy could not be refreshed or parsed + */ + public void refresh() throws PolicyException { + pds.clear(); + PolicyReader reader; + try { + reader = new PolicyReader(policyFile,charset); + reader.read(); + } catch (IOException e) { + String msg = "Problems refreshing policy from file " + policyFile + + ": " + e.getMessage(); + System.err.println(msg); + throw new PolicyException(msg); + } + + // If errors, print them out + if (!reader.isValid()) { + List messages = reader.getMessages(); + for (Iterator it = messages.iterator(); it.hasNext();) { + String message = (String) it.next(); + System.err.println(message); + } + StringBuffer s = new StringBuffer(); + for (Iterator it = messages.iterator(); it.hasNext();) { + s.append((String) it.next()); + } + throw new PolicyException("Errors parsing policy from file " + + policyFile + ": " + s.toString()); + } + + // It parsed and validated ok, so now stash local copies of the protection domains + ProtectionDomain[] parsedPds = reader.getProtectionDomains(); + for (int i = 0; i < parsedPds.length; i++) { + ProtectionDomain pd = parsedPds[i]; + LocalProtectionDomain localPd = new LocalProtectionDomain(pd.getCodeSource(), pd.getPermissions(), pd.getClassLoader(), pd.getPrincipals()); + pds.add(localPd); + } + + loaded = true; + } + + /** + * Lightweight re-implementation of {@link java.security.ProtectionDomain} that + * does not allow subsequent modifications to Permissions. The default ProtectionDomain + * is, unfortunately, unsuitable for use with LocalPolicy because default JVM permissions + * are added "dynamically" subsequent to creation. This class exhibits the same + * basic behaviors as ProtectionDomain: null values are allowed for the + * LocalProtectionDomain's Principals, CodeSource and PermissionCollection. Also, if the + * Principal array supplied is null, it will be converted to a zero-length + * array. + * @author Andrew Jaquith + */ + public static class LocalProtectionDomain { + + private final CodeSource codesource; + private final ClassLoader classloader; + private final PermissionCollection permissions; + private final Principal[] principals; + + /** + * Constructs a new LocalProtectionDomain. + * @param codesource the code source; may be null + * @param permissions the permission collection; may be null + * @param principals the principals to whom the permissions are granted; may be null + */ + public LocalProtectionDomain(CodeSource codesource, + PermissionCollection permissions, ClassLoader classloader, Principal[] principals) { + this.codesource = codesource; + this.permissions = permissions; + if (permissions != null && !permissions.isReadOnly()) { + permissions.setReadOnly(); + } + this.classloader = classloader; + this.principals = (principals == null) ? new Principal[0] : principals; + } + + /** + * Returns the ClassLoader used to construct the LocalProtectionDomain, + * which may be null. + * @return the class loader + */ + public ClassLoader getClassLoader() { + return classloader; + } + + /** + * Returns the Principal array used to construct the LocalProtectionDomain, + * if supplied, or a zero-length array if not supplied. + * @return the principals + */ + public Principal[] getPrincipals() { + return principals; + } + + /** + * Returns the PermissionCollection used to construct the LocalProtectionDomain, + * which may be null. + * @return the permissions + */ + public PermissionCollection getPermissions() { + return permissions; + } + + /** + * Returns the CodeSource used to construct the LocalProtectionDomain, + * which may be null. + * @return the principals + */ + public CodeSource getCodeSource() { + return codesource; + } + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/UnresolvedPrincipal.java +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/UnresolvedPrincipal.java @@ -0,0 +1,90 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.policy; + +import java.security.Principal; + +/** + * Represents a Principal whose class cannot be resolved by the classloader. + * + * @author Andrew Jaquith + * @version $Revision: 1.2 $ $Date: 2007/02/24 17:27:43 $ + */ +public final class UnresolvedPrincipal implements Principal { + + private final String name; + + private final String clazz; + + private final Exception exception; + + /** + * Constructs a new UnresolvedPrincipal. + * + * @param clazz the name of the unresolved class + * @param name the name of the Principal + */ + public UnresolvedPrincipal(String clazz, String name) { + this(clazz, name, null); + } + + /** + * Constructs a new UnresolvedPrincipal. + * @param clazz the name of the unresolved class + * @param name the name of the Principal + * @param e the Exception indicating the reason why the Principal could not be resolved + */ + public UnresolvedPrincipal(String clazz, String name, Exception e) + { + this.clazz = clazz; + this.name = name; + this.exception = e; + } + + /** + * Returns the Exception used to instantiate this UnresolvedPrincipal. + * @return the Exception, or null if not constructed with one. + */ + public final Exception getException() { + return exception; + } + + /** + * Returns the class of the unresolved Principal as a String. + * + * @return the String representation of the unresolved Principal class + */ + public final String getPrincipalClass() { + return clazz; + } + + /** + * Returns the name of the principal. + * + * @see java.security.Principal#getName() + */ + public final String getName() { + return name; + } + + /** + * Returns a string representation of this Principal. + * + * @see java.lang.Object#toString() + */ + public final String toString() { + return "(unresolved " + clazz + " " + name + ")"; + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/PolicyException.java +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/PolicyException.java @@ -0,0 +1,29 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.policy; + +/** + * Lightweight checked Exception subclass for propagating security policy parsing errors. + * @author Andrew Jaquith + * @version $Revision: 1.2 $ $Date: 2007/02/24 17:27:43 $ + * + */public class PolicyException extends Exception { + + private static final long serialVersionUID = 1L; + + public PolicyException(String message) { + super(message); + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/PolicyReader.java +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/PolicyReader.java @@ -0,0 +1,991 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.policy; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Parses a Java security policy file into memory. + *

If a SecurityManager is running, the security policy must grant + * PolicyReader's CodeSource the following permissions:

+ *
    + *
  • java.io.FilePermission "${java.home}/lib/security/java.policy", "read"
  • + *
  • java.io.FilePermission "${user.home}/.java.policy", "read"
  • + *
  • java.io.FilePermission "path-to-policy-file", "read"
  • + *
  • java.io.FilePermission "path-to-keystore", "read";
  • + *
  • java.util.PropertyPermission "file.encoding", "read"
  • + *
  • java.util.PropertyPermission "java.security.policy", "read"
  • + *
  • java.util.PropertyPermission "java.home", "read"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-permission-packages"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-principal-packages"
  • + *
  • java.lang.RuntimePermission "getProtectionDomain"
  • + *
  • java.net.NetPermission "specifyStreamHandler"
  • + *
+ *

...where path-to-policy-file is the policy file to parse, + * path-to-keystore is the path of the keystore named in the policy file (if any), + * and custom-permission-packages and custom-principal-packages + * denote the names of custom {@link java.security.Permission} and + * {@link java.security.Principal} classes to be loaded by the current classloader when the + * file is parsed. The methods that require these permissions enclose their operations inside + * of doPrivileged blocks, so calling classes' ProtectionDomains do not + * need to be granted these privileges.

+ * + * @author Andrew Jaquith + * @version $Revision: 1.5 $ $Date: 2007/04/09 02:34:52 $ + */ +public class PolicyReader { + + private static final Pattern COMMENTS_PATTERN = Pattern.compile( + "^\\s*\\/\\/.*$", Pattern.MULTILINE); + + private static final Pattern KEYSTORE_PATTERN = Pattern.compile( + "^keystore \"(.*?)\";.*$", Pattern.CASE_INSENSITIVE); + + private static final Pattern GRANT_PATTERN = Pattern + .compile("grant(.*?) \\{ *(.*?) *\\};"); + + private static final Pattern LINEBREAKS_PATTERN = Pattern.compile( + "[\\n|\\r\\n|\\u0085|\\u2028|\\u2029]", Pattern.MULTILINE); + + private static final String PROP_JAVA_HOME = "java.home"; + + private static final String PROP_USER_HOME = "user.home"; + + private static final String PROP_JAVA_SECURITY_POLICY = "java.security.policy"; + + private static final String REGEX_COMMA_DELIMITER = " *, *"; + + private static final String REGEX_SEMICOLON_DELIMITER = " *; *"; + + private static final String TOKEN_CODEBASE = "codeBase"; + + private static final String TOKEN_PRINCIPAL = "principal"; + + private static final String TOKEN_PERMISSION = "permission"; + + private static final String TOKEN_SIGNEDBY = "signedBy"; + + private static final String DOUBLE_QUOTE = "\""; + + private static final String ONE_SPACE = " "; + + private static final String WHITESPACE = "\\s+"; + + private static final int NOT_FOUND = -1; + + private final File policy; + + private final List domains; + + private KeyStore keystore = null; + + private File keystoreFile = null; + + private String policyString = null; + + private final List exceptions = new ArrayList(); + + private SecurityTokenFactory tokenFactory = null; + + private boolean validPolicy = false; + + private final String charset; + + /** + *

+ * Constructs a new PolicyReader for parsing a supplied policy File using the + * Java platform standard charset. + * @param file + * the policy file to be parsed + * @throws FileNotFoundException + * if the policy file does not exist in the filesystem + * @throws SecurityException if the running SecurityManager denied any of the required + * Permissions + */ + public PolicyReader(File file) throws FileNotFoundException { + this(file, (String)AccessController.doPrivileged( new PrivilegedAction() { + public Object run() { return System.getProperty("file.encoding"); } })); + } + + /** + *

+ * Constructs a new PolicyReader for parsing a supplied policy File. If the + * file does not exist, this constructor returns an + * {@linkplain IllegalArgumentException}. + *

+ *

If a SecurityManager is running, the security policy must grant + * PolicyReader's CodeSource the following permissions:

+ *
    + *
  • java.io.FilePermission "path-to-policy-file", "read"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-permission-packages"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-principal-packages"
  • + *
  • java.net.NetPermission "specifyStreamHandler"
  • + *
+ *

...where custom-permission-packages and custom-principal-packages + * are the names of custom {@link java.security.Permission} and + * {@link java.security.Principal} classes loaded from the current classloader.

+ * + * @param file + * the policy file to be parsed + * @throws FileNotFoundException + * if the policy file does not exist in the filesystem + * @throws SecurityException if the running SecurityManager denied any of the required + * Permissions + */ + public PolicyReader(File file, String charset) throws FileNotFoundException { + super(); + this.policy = file; + this.domains = new ArrayList(); + this.charset = charset; + boolean exists = secureExists(policy); + if (!exists) { + throw new IllegalArgumentException("File " + policy + + " does not exist, or the SecurityManager prohibited access to it."); + } + + // Set up the token factory; if the SecurityManager prohibits it, then propagate the exception + this.tokenFactory = + (SecurityTokenFactory)AccessController.doPrivileged(new PrivilegedAction() { + public Object run() throws SecurityException { + return new SecurityTokenFactory(new URL[0]); + } + + }); + } + + /** + * Examines a Keystore and returns the alias that matches a given signing + * Certificate. If no match is found, this method returns null. + * + * @param ks + * the keystore to search + * @param cert + * the certificate to match + * @return the alias corresponding to the certificate + */ + public static String findAlias(KeyStore ks, Certificate cert) { + try { + Enumeration aliases = ks.aliases(); + while (aliases.hasMoreElements()) { + String alias = (String) aliases.nextElement(); + if (ks.isKeyEntry(alias)) { + Certificate keystoreCert = ks.getCertificate(alias); + if (cert.equals(keystoreCert)) { + return alias; + } + } + } + } catch (KeyStoreException e) { + System.err.println(e.getMessage()); + } + return null; + } + + /** + * Returns the ProtectionDomains parsed by the {@link #read()} method. + * If the {@link #read()} method has not been called yet, this method + * will return a zero-length array. + * @return the ProtectionDomains parsed from the policy file + */ + public ProtectionDomain[] getProtectionDomains() { + return (ProtectionDomain[])domains.toArray(new ProtectionDomain[domains.size()]); + } + + /** + *

+ * Static method that identifies the active security policies for the JVM + * and returns an array of PolicyReader objects (one for each active + * policy). + *

+ *

+ * This method accesses the file system to determine whether certain default + * security policies exist, and it reads the java.home and + * user.home properties to determine the location of the + * system and user security policies. Therefore, to run this method under a + * SecurityManager, the security policy must grant PolicyReader's + * CodeSource the following permissions:

+ *

+ *
    + *
  • java.util.PropertyPermission "java.home", "read"
  • + *
  • java.util.PropertyPermission "java.security.policy", "read"
  • + *
  • java.util.PropertyPermission "user.home", "read"
  • + *
  • java.io.FilePermission "${java.home}/lib/security/java.policy", "read"
  • + *
  • java.io.FilePermission "${user.home}/.java.policy", "read"
  • + *
+ * + * @return PolicyReader objects for each active policy file + * @throws SecurityException if the running SecurityManager denied any of the required + * Permissions + */ + public static PolicyReader[] findPolicies() { + final Set files = new HashSet(); + final String fs = File.separator; + + AccessController.doPrivileged(new PrivilegedAction() { + + public Object run() { + // Add the system policy if it exists (it should!) + File systemPolicy = new File(System.getProperty(PROP_JAVA_HOME) + + fs + "lib" + fs + "security" + fs + "java.policy"); + if (systemPolicy.exists()) { + files.add(systemPolicy); + } + + // Add the user policy if it exists + File userPolicy = new File(System.getProperty(PROP_USER_HOME) + + fs + ".java.policy"); + if (userPolicy.exists()) { + files.add(userPolicy); + } + + // Add the file pointed at by the policy system property if it + // exists + String policyProp = System + .getProperty(PROP_JAVA_SECURITY_POLICY); + if (policyProp != null) { + if (policyProp.startsWith("file:")) { + policyProp = policyProp.substring(5); + } + if (policyProp.length() > 0) { + File propertyPolicy = new File(policyProp); + if (secureExists(propertyPolicy)) { + files.add(propertyPolicy); + } + } + } + return null; + } + }); + + // Now, create policy readers for each file we found + List readers = new ArrayList(); + for (Iterator it = files.iterator(); it.hasNext();) { + try { + readers.add(new PolicyReader((File) it.next())); + } + catch (FileNotFoundException e) { + // Didn't find the policy file/denied access? Ok, just skip it + } + } + return (PolicyReader[])readers.toArray(new PolicyReader[readers.size()]); + } + + /** + * Static method that returns the certificate used to sign a particular + * class. We determine the signing certificate by obtaining the class' + * {@link java.security.ProtectionDomain} first, then looking up the + * CodeSource and extracting certificates. If the code source was not + * signed, this method returns null. This method obtains the + * ProtectionDomain of the class. Therefore, to run this method under a + * SecurityManager, the security policy must grant PolicyReader's + * CodeSource the following permission:

+ *

+ *
    + *
  • java.lang.RuntimePermission "getProtectionDomain"
  • + *
+ * + * @param clazz the class whose signer should be returned + * @return the signing certificate, or null if no signer + * @throws SecurityException + * if the running SecurityManager denied this ProtectionDomain + * the permission java.lang.RuntimePermission "getProtectionDomain" + */ + public static Certificate getSigner(final Class clazz) { + ProtectionDomain pd = null; + + pd = (ProtectionDomain) AccessController + .doPrivileged(new PrivilegedAction() { + public Object run() { + return clazz.getProtectionDomain(); + } + }); + + if (pd != null) { + CodeSource cs = pd.getCodeSource(); + if (cs != null) { + Certificate[] certs = cs.getCertificates(); + if (certs != null && certs.length > 0) { + return certs[0]; + } + } + } + return null; + } + + /** + * Returns true if a class is digitally signed. This looks up + * the signer by delegating to {@link #getSigner(Class)}, and therefore + * requires the same privileges if run under a SecurityManager. + * + * @param clazz + * the class to check + * @return the result. + * @throws SecurityException + * if the running SecurityManager denied this ProtectionDomain + * the permission java.lang.RuntimePermission "getProtectionDomain" + * @see #getSigner(Class) + */ + public static boolean isSigned(Class clazz) { + Certificate cert = getSigner(clazz); + return (cert != null); + } + + /** + * Returns the File object used to instantiate this PolicyReader. + * + * @see #PolicyReader(File) + * @return the file + */ + public File getFile() { + return policy; + } + + /** + *

+ * Returns the keystore associated with the policy file, if one is specified + * in the policy. The strategy used to file the keystore follows the J2SE + * convention: if the file named in the keystore line of the + * policy file is not an absolute path name, the location for the file is + * assumed to be the same directory as the keystore. If the keystore cannot + * be located, this method throws an {@link java.io.IOException}. + *

+ * + *

+ * If the policy file does not specify a keystore, this method returns + * null, and + * calling methods should check for this result! + *

+ *

+ * This method attempts to read the keystore into memo from the file + * specified in the secuity policy. Therefore, to run this method under a + * SecurityManager, the security policy must grant PolicyReader's + * CodeSource the following permission:

+ *

+ *
    + *
  • java.io.FilePermission "/path-to-keystore", "read"
  • + *
+ * + * @return the keystore associated with the policy file, or + * null if not found + * @throws IOException + * if the keystore file cannot be read from the filesystem + * because it does not exist + * @throws SecurityException if the running SecurityManager denied the Permission + * java.io.FilePermission "path-to-keystore", "read" + */ + public KeyStore getKeyStore() throws IOException { + + if (keystore == null) { + + if (policyString == null) { + loadPolicy(policy); + } + + // Get keystore, it if exists + Matcher matcher = KEYSTORE_PATTERN.matcher(policyString); + if (matcher.matches()) { + String match = matcher.group(1); + keystoreFile = new File(match); + if (!keystoreFile.isAbsolute()) { + keystoreFile = new File(policy.getParentFile(), match); + if (!secureExists(keystoreFile)) { + throw new IOException("Couldn't find keystore " + + keystoreFile + ", or the SecurityManager prohibited access to it."); + } + } + } + + // If the keystore file is not null, load the keystore itself + if (keystoreFile != null) { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws IOException { + try { + FileInputStream is = new FileInputStream(keystoreFile); + keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + keystore.load(is, null); + return null; + } catch (CertificateException e) { + throw new IOException(e.getMessage()); + } catch (KeyStoreException e) { + throw new IOException(e.getMessage()); + } catch (NoSuchAlgorithmException e) { + throw new IOException(e.getMessage()); + } catch (SecurityException e) { + return new IOException("Security manager prohibited read access to " + + keystoreFile.getAbsolutePath()); + } + } + }); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } + } + return keystore; + } + + /** + *

+ * Parses the security policy file, and loads its contents into memory. Any + * errors encountered while reading the policy will be added to an + * internally cached set of error messages, which may be obtained by + * {@link #getMessages()}. + *

+ *

+ * Because this method attempts to read the policy file contents, it + * requires read permissions to the file containing the + * policy if a SecurityManager is running. It will also require + * read access to the keystore named in the policy, and will + * need to read named classes' ProtectionDomains to verify signatures if + * required by the policy. Therefore, to run this method under a + * SecurityManager, the security policy must grant PolicyReader's + * CodeSource the following permission:

+ *
    + *
  • permission java.io.FilePermission "path-to-policy-file", "read"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-permission-packages"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-principal-packages"
  • + *
  • java.lang.RuntimePermission "createClassLoader"
  • + *
  • java.lang.RuntimePermission "getProtectionDomain"
  • + *
  • java.net.NetPermission "specifyStreamHandler"
  • + *
+ * + * @throws IOException if the policy file cannot be read + * @throws SecurityException when the SecurityManager, if running, denies + * the ProtectionDomain of this class any of the required permissions + */ + public void read() throws IOException { + + if (policyString == null) { + loadPolicy(policy); + getKeyStore(); + } + + // Parse the grants + domains.clear(); + Matcher m = GRANT_PATTERN.matcher(policyString); + while (m.find()) { + String granteeString = m.group(1).trim(); + String permissionString = m.group(2).trim(); + ProtectionDomain domain = parseProtectionDomain(granteeString, permissionString); + domains.add(domain); + } + + if (exceptions.size() > 0) { + System.err.println("The parser returned these errors:"); + for (Iterator it = exceptions.iterator(); it.hasNext();) { + Exception e = (Exception) it.next(); + System.err.println(e.getMessage()); + } + } else { + // Hooray! Everything parsed nicely. + validPolicy = true; + } + + } + + /** + * The parsing errors encountered by PolicyReader when the {@link #read()} method + * was last invoked. The list of errors is reset every time read() + * is called. + * @return the list of Exception objects + */ + public List getMessages() { + return Collections.unmodifiableList(exceptions); + } + + /** + * Adds an Exception to the list of parsing errors. + * @param e the exception added + */ + protected void addMessage(Exception e) { + exceptions.add(e); + } + + /** + * Protected method that loads the policy file into memory, scrubs contents + * of line breaks and extra whitespace, and returns it as a string. If + * running under a SecurityManager, this method must have read access to the + * file. + * + * @param file the policy file to load + * @throws IOException + * if the file does not exist in the file system + * @throws SecurityException if the running SecurityManager denied the Permission + * java.io.FilePermission "path-to-file", "read" + */ + protected void loadPolicy(final File file) throws IOException { + StringBuffer contents = new StringBuffer(); + BufferedReader reader = null; + try { + reader = (BufferedReader) AccessController + .doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws SecurityException, UnsupportedEncodingException { + try { + BufferedReader in = new BufferedReader( + new InputStreamReader( new FileInputStream(file), charset ) ); + return in; + } catch (FileNotFoundException e) { + return null; + } + } + }); + } catch (PrivilegedActionException e) { + throw new SecurityException("Could not open policy " + + file.getAbsolutePath() + + "; access was denied by the SecurityManager."); + } + + // If reader was null, it means we could not find it in the filesystem + if (reader == null) { + throw new IOException("Could not open policy " + + file.getAbsolutePath() + "; it does not exist."); + } + + // Read the file and append its contents to a string buffer + int ch; + while ((ch = reader.read()) != NOT_FOUND) { + contents.append((char) ch); + } + reader.close(); + + // Scrub out all comments + Matcher matcher = COMMENTS_PATTERN.matcher(contents); + String contentsNoComments = matcher.replaceAll(ONE_SPACE); + + // Replace line breaks with spaces + matcher = LINEBREAKS_PATTERN.matcher(contentsNoComments); + String contentsNoLineBreaks = matcher.replaceAll(ONE_SPACE); + + // Replace all multi-character whitespace with one space + policyString = contentsNoLineBreaks.replaceAll(WHITESPACE, ONE_SPACE) + .trim(); + } + + /** + *

+ * Returns a valid ProtectionDomain by parsing a grant block + * that denotes the codebase, pricipals and signers to whom permissions + * should be granted. The grant block is comprised of two parts, + * which are passed to this method: the grantee string and the + * permissions string. + *

+ *

+ * The grantee is the codebase, principals and signers portion + * of the grant block, minus the word "grant" itself. + * The following are all valid grantee strings: + *

+ *
 codeBase "file:${user.home}/workspace/freshcookies-security/target-test/"
+	 * signedBy "testsigner", principal org.freshcookies.security.policy.GenericPrincipal "Foo", principal "Bar"
+	 * signedBy "testsigner"
+ *

+ * The grantee string is processed as follows: + *

+ *
    + *
  • If the grantee strings contains a codeBase token, this method + * will attempt to create a {@link java.security.CodeSource} object and + * stash it in the returned Grantee. It will not actually verify that the + * CodeSource exists, however.
  • + *
  • If the grantee line contains a signedBy token, this method + * will iterate through the aliases in the Keystore returned by + * {@link #getKeyStore()} and attempt to find a matching signer alias. If no + * match, an error message is added to the internal list.
  • + *
+ *

+ * The permission string is the portion of the grant statement + * between braces ({}). Permissions are delimited with a semicolons. + * The permissions string is processed as follows: + *

+ *
    + *
  • Each Permission is parsed into the correct class name, targets and actions.
  • + *
  • After the class name is determined, this method will attempt + * to load the specified permissions into memory using the current ClassLoader.
  • + *
  • If a particular permission statement contains a signedBy + * token, this method will also attempt to verify that the Permission was + * signed by the keystore alias specified in the statement.
  • + *
+ *

The ProtectionDomain that is returned by this method will be constructed with + * CodeSource, PermissionCollection, and Principal array parameters that correspond + * to those parsed from the grantee and permission strings. The ClassLoader passed + * to {@link java.security.ProtectionDomain#ProtectionDomain(CodeSource, PermissionCollection, ClassLoader, Principal[])} + * will be the current ClassLoader; that is, the one that + *

+ * This method needs certain privileges to run correctly under a + * SecurityManager. It requires the NetPermission + * specifyStreamHander to construct CodeSource URLs. It will + * also require permission to obtain the ProtectionDomain for loaded classes. + * For example, an appropriate grant statement might look like this: + *

+ *
grant codeBase "file:/path-to-this-jar/freshcookies-security-version.jar" {
+	 *   permission java.lang.RuntimePermission "getProtectionDomain";
+	 *   permission java.net.NetPermission "specifyStreamHandler";
+	 *};
+ * + * @param granteeString + * the grantee string, which is the portion of the grant block + * between the grant token and the opening brace ({) + * that begins the individual permission lines + * @param permissionString + * the permission string, which is the portion of the grant block + * between opening and closing braces({}). This string will + * be split into individual permissions and parsed + * @return the initialized ProtectionDomain + * @throws SecurityException when the SecurityManager, if running, denies + * the protection domain of this class any of the following permissions: + *
    + *
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-principal-packages"
  • + *
  • java.lang.RuntimePermission "createClassLoader"
  • + *
  • java.lang.RuntimePermission "getProtectionDomain"
  • + *
  • java.net.NetPermission "specifyStreamHandler"
  • + *
+ */ + protected ProtectionDomain parseProtectionDomain(String granteeString, String permissionString) { + + // Grantee variables: codebase, signers, principals + URL codeBaseUrl = null; + Certificate[] signers = null; + List principalsList = new ArrayList(); + + // Split the grantee string into tokens: one or more of codebase, signedby, principal blocks + String[] granteeStrings = granteeString.split(REGEX_COMMA_DELIMITER); + + // Parse each token + for (int i = 0; i < granteeStrings.length; i++) { + String grantee = granteeStrings[i].trim(); + + // Extract CodeBase URL, and construct + if (grantee.startsWith(TOKEN_CODEBASE)) { + codeBaseUrl = extractCodeBaseUrl(grantee); + } + + // Extract signing certificate alias, and resolve in keystore + else if (grantee.startsWith(TOKEN_SIGNEDBY)) { + signers = extractSigningCertificates(grantee); + } + + // Extract principals, and instantiate + else if (grantee.startsWith(TOKEN_PRINCIPAL)) { + Principal principal = extractPrincipal(grantee); + if (principal != null) { + principalsList.add(principal); + } + } + } + + // Ok, now build a CodeSource out of the URL and cert alias + // We need the certs at some point... + CodeSource codeSource = new CodeSource(codeBaseUrl, signers); + + // Turn our Principal list into an array (but zero-length means we just set it as null) + Principal[] principals = (Principal[]) principalsList.toArray(new Principal[principalsList.size()]); + if (principals.length == 0) { + principals = null; + } + + // Extract the Permissions assigned to the grantee and lock it + PermissionCollection permissions = extractPermissionCollection(permissionString); + permissions.setReadOnly(); + + // Create and return the ProtectionDomain + ClassLoader classLoader = PolicyReader.class.getClassLoader(); + ProtectionDomain pd = new ProtectionDomain(codeSource, permissions, classLoader, principals); + return pd; + } + + /** + * Returns true if a supplied File exists in the file system, and + * the SecurityManager (if running) has granted read access to it. + * @param file the file to check + * @return the result + * @throws SecurityException if the running SecurityManager denied the Permission + * java.io.FilePermission "path-to-file", "read" + */ + protected static boolean secureExists(final File file) throws SecurityException { + try { + Boolean exists = (Boolean) AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Object run() throws SecurityException { + return Boolean.valueOf(file.exists()); + } + }); + return exists.booleanValue(); + } + catch (PrivilegedActionException e) { + throw (SecurityException)e.getException(); + } + } + + /** + * Extracts the URL from a codeBase token string. + * Example: codeBase "file:${user.home}/workspace/freshcookies-security/target/classes/-". + * @param token the string containing the URL + * @return the constructed URL + */ + protected URL extractCodeBaseUrl(String token) { + URL url = null; + try { + final String s = extractTarget(token, TOKEN_CODEBASE); + url = (URL) AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws MalformedURLException { + return new URL(s); + } + }); + } catch (PolicyException e) { + addMessage(e); + } catch (PrivilegedActionException e) { + addMessage(e.getException()); + } + return url; + } + + /** + * Extracts the Principal from a principal token string. + * Example: principal org.freshcookies.security.policy.GenericPrincipal "Andrew Jaquith". + * @param token the string containing the Principal + * @return the constructed Principal + */ + protected Principal extractPrincipal(String token) { + Principal principal = null; + final String s = token.substring(TOKEN_PRINCIPAL.length()).trim(); + try { + principal = (Principal)AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws AccessControlException, ClassNotFoundException { + return tokenFactory.getPrincipal(s); + } + }); + } + catch (PrivilegedActionException e) { + addMessage(e.getException()); + } + return principal; + } + + /** + * Extracts and resolves signing Certificates from a signedBy token string. + * Example: signedBy "testsigner". + * @param token the string containing the certificate alias + * @return the resolved certificate chain + */ + protected Certificate[] extractSigningCertificates(String token) { + Certificate[] signers = null; + + // If keystore file is null, then signedby doesn't mean anything + if (keystoreFile == null) { + addMessage(new PolicyException( + "A 'signedBy' entry exists, but no keystore was specified; ignoring.")); + } else { + try { + String signedBy = extractTarget(token, TOKEN_SIGNEDBY); + signers = getKeyStore().getCertificateChain( signedBy); + + // It's an error if we can't find the 'signedBy' alias + if (signers == null) { + addMessage(new PolicyException( + "Certificate with alias '" + signedBy + + "' not found in keystore " + + keystoreFile)); + } + } catch (Exception e) { + addMessage(e); + } + } + return signers; + } + + /** + *

Parses permissions from a grant block and returns a PermissionCollection. + * The permissions are extracted from the string portion of the grant + * statement between braces ({}). Permissions are delimited with a semicolons. + * This method will attempt to load the specified permissions into memory using the + * current ClassLoader. If a particular permission statement contains a + * signedBy token, this method will also attempt to verify that the + * Permission was signed by the keystore alias specified in the statement.

+ * @param permissions the permissions string, which will be parsed into individual permissions + * and parsed. For example, the following string will be parsed as two separate permissions: + *
 permission java.util.PropertyPermission "java.version", "read";
+	 * permission java.util.PropertyPermission "java.vendor", "read";
+ * @return the instantiated collection of Permissions for the Grantee + * @throws SecurityException when the SecurityManager, if running, denies + * the protection domain of this class any of the following permissions: + *
    + *
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-permission-packages"
  • + *
  • java.lang.RuntimePermission "createClassLoader"
  • + *
  • java.lang.RuntimePermission "getProtectionDomain"
  • + *
+ * @param the + */ + protected PermissionCollection extractPermissionCollection(String permissions) { + String[] p = permissions.split(REGEX_SEMICOLON_DELIMITER); + PermissionCollection permissionCollection = new Permissions(); + + for (int j = 0; j < p.length; j++) { + + String item = p[j].trim(); + if (item.startsWith(TOKEN_PERMISSION)) { + + // Look and instantiate the Permisson + final String permissionString = item.substring( + TOKEN_PERMISSION.length()).trim(); + Permission permission = null; + try { + permission = (Permission)AccessController.doPrivileged(new PrivilegedExceptionAction(){ + public Object run() throws AccessControlException, ClassNotFoundException { + return tokenFactory.getPermission(permissionString); + } + }); + + // Verify signature if there is a 'signedBy' clause + try { + boolean isSigned = (permissionString + .indexOf(TOKEN_SIGNEDBY) != -1); + if (isSigned && permission != null) { + if (!isVerified(permission.getClass())) { + addMessage(new PolicyException( + "Could not verify permission class signature: " + + permissionString)); + } + } + } catch (IOException e) { + addMessage(new PolicyException( + "Could not instantiate permission: " + + permissionString)); + } + // Add the newly instantiated permission to the grants + // collection + permissionCollection.add(permission); + + } catch (PrivilegedActionException e) { + addMessage(e.getException()); + } + } + } + permissionCollection.setReadOnly(); + return permissionCollection; + } + + /** + * Returns true> if the security policy file was parsed + * correctly and validated without errors. If the {@link #read()} method has + * not yet been called, this method returns false. + * + * @return true if the policy file parsed correctly, + * false otherwise + */ + public boolean isValid() { + return validPolicy; + } + + /** + * Returns code if a supplied class' {@link CodeSource} is + * digitally signed, and the certificate used to sign it can be found in + * this PolicyReader's keystore. If the CodeSource is not + * signed, or if a matching certificate cannot be found, this method returns + * false. + * + * @param clazz + * the class to verify + * @return the result of the verification operation + * @throws IOException + * if the keystore cannot be read + * @throws SecurityException + * if the running SecurityManager denied this ProtectionDomain + * the permission java.lang.RuntimePermission "getProtectionDomain" + * @see #getKeyStore() + */ + public boolean isVerified(Class clazz) throws IOException { + Certificate cert = getSigner(clazz); + try { + Enumeration aliases = getKeyStore().aliases(); + while (aliases.hasMoreElements()) { + String alias = (String) aliases.nextElement(); + if (keystore.isKeyEntry(alias)) { + Certificate keystoreCert = keystore.getCertificate(alias); + if (cert.equals(keystoreCert)) { + return true; + } + } + } + } catch (KeyStoreException e) { + throw new IOException(e.getMessage()); + } + return false; + } + + /** + * Extracts a substring starting after a given start string. The start + * string must, in fact, start the overall string, and the + * extracted string must be surrounded by double quotes. For example, if + * this method is called with parameters signedBy "testsigner" + * and signedBy, it will return testsigner. + * + * @param s + * the string from which to extract the substring + * @param startAfter + * the start string; if null, the start point + * will be position 0 + * @return the extracted substring + * @throws PolicyException + * if the string is not well-formed + */ + private static String extractTarget(String s, String startAfter) + throws PolicyException { + String s2 = (startAfter == null) ? s : s.substring(startAfter.length()) + .trim(); + if (s2.startsWith(DOUBLE_QUOTE) && s2.endsWith(DOUBLE_QUOTE) + && s2.length() > 2) { + return s2.substring(1, s2.length() - 1); + } + throw new PolicyException("Policy string \"" + s + + "\" must be at least one character and surrounded by quotes."); + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/PrincipalComparator.java +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/PrincipalComparator.java @@ -0,0 +1,55 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.policy; + +import java.io.Serializable; +import java.security.Principal; +import java.text.Collator; +import java.util.Comparator; + +/** + * Comparator class for sorting objects of type Principal. + * Used for sorting arrays or collections of Principals. + * The principals are sorted according to their name values. If + * the names are the same, the principal with the first class name + * wins. + * @author Andrew Jaquith + * @version $Revision: 1.1 $ $Date: 2007/02/24 17:27:43 $ + */ +public class PrincipalComparator + implements Comparator, Serializable +{ + private static final long serialVersionUID = 1L; + + /** Constructs a new PrincipalComparator. */ + public PrincipalComparator() { + super(); + } + + public int compare( Object o1, Object o2 ) + { + Collator collator = Collator.getInstance(); + if ( o1 instanceof Principal && o2 instanceof Principal ) + { + int comparison = collator.compare( ((Principal)o1).getName(), ((Principal)o2).getName() ); + if ( comparison == 0 ) + { + return collator.compare(o1.getClass().getName(), o2.getClass().getName()); + } + return comparison; + } + throw new ClassCastException( "Objects must be of type Principal."); + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/package.html +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/package.html @@ -0,0 +1,19 @@ + + + + + + + + +

+ Utility classes that parse and manipulate Java security policy + files. +

+

+ @author Andrew R. Jaquith + @version $Revision: 1.1 $ $Date: 2006/05/02 04:36:39 $ +

+ + --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/SecurityTokenFactory.java +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/SecurityTokenFactory.java @@ -0,0 +1,489 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.policy; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.CodeSource; +import java.security.Permission; +import java.security.Principal; +import java.security.UnresolvedPermission; +import java.security.cert.Certificate; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.freshcookies.security.cert.JarHelper; + +/** + *

Utility class that dynamically looks up and returns Permission classes, + * Principal classes and CodeSources corresponding to supplied strings. The factory + * will attempt to resolve classes by consulting the parent classloader + * first, after which it will consult the URLs supplied to the class + * constructor {@link #SecurityTokenFactory(URL[])}. This allows the + * factory to load classes from arbirary JARs, although be warned that + * this is a potential security hazard and should only be used in + * carefully controlled situations.

+ *

If a SecurityManager is running, the security policy must grant + * this ProtectionDomain's CodeSource, and all preceding callers, + * the following permissions:

+ *
    + *
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-permission-packages"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-principal-packages"
  • + *
  • java.net.NetPermission "specifyStreamHandler"
  • + *
+ *

...where custom-permission-packages and + * custom-principal-packages are the names of + * custom {@link java.security.Permission} and {@link java.security.Principal} + * classes loaded from external URLs.

+ *

In addition, if the urls parameter passed to the constructor is non-null + * and has a length of 1 or more, the SecurityManager (if running) will require the + * ProtectionDomain for this SecurityTokenFactory and all preceding callers to be + * granted these permissions:

+ *
    + *
  • java.lang.RuntimePermission "createClassLoader"
  • + *
  • java.io.FilePermission "path-to-url.0", "read"
  • + *
  • java.io.FilePermission "path-to-url.1", "read"
  • + *
  • java.io.FilePermission "path-to-url.n", "read"
  • + *
+ *

... for all file URLs whose names end in .jar. This is so + * that the {@link #getCodeSource(String)} method can successfully resolve and + * verify any jar file signatures.

+ *

None of the methods in this class perform their actions inside + * doPrivileged blocks, so all {@link SecurityException} errors + * are propagated to callers.

+ * @author Andrew Jaquith + * @version $Revision: 1.3 $ $Date: 2007/04/09 02:34:52 $ + */ +public class SecurityTokenFactory { + + /** Constant that represents a single space character. */ + private static final String ONE_SPACE = " "; + + private static final int NOT_FOUND = -1; + + /** Constant that represents a double-quote character. */ + private static final String DOUBLE_QUOTE = "\""; + + /** + * Pattern that matches any valid Java type name, minus the + * .java or .class suffix. + */ + protected static final String REGEX_JAVA_TYPE = "((?:[a-zA-Z0-9$_]+\\.)*[A-Z$_][a-zA-Z0-9$_]*)"; + + /** + * Pattern that matches permission string e.g., + * javax.security.auth.AuthPermission "setLoginConfiguration". + */ + protected static final Pattern PERMISSION_PATTERN = Pattern.compile("^" + + REGEX_JAVA_TYPE + "(?: ?\"(.*?)\")??(?: ?, ?\"(.*?)\")??$"); + + private final ClassLoader classLoader; + + private final Map cachedCodeSources; + + private final Map cachedPermissions; + + private final Map cachedPrincipals; + + private final JarHelper jarHelper; + + private final Canonicalizer canonicalizer; + + /** + *

Constructs a new instance of this class with a supplied set of supplemental URLs for + * locating classes. The supplied URLs are used in addition to the + * parent ClassLoader that instantiated the SecurityTokenFactory.

+ *

If the urls parameter passed to the constructor is non-null + * and has a length of 1 or more, the SecurityManager (if running) will require the + * ProtectionDomain for this SecurityTokenFactory and all preceding callers to be + * granted these permissions:

+ *
    + *
  • java.lang.RuntimePermission "createClassLoader"
  • + *
  • java.io.FilePermission "path-to-url.0", "read"
  • + *
  • java.io.FilePermission "path-to-url.1", "read"
  • + *
  • java.io.FilePermission "path-to-url.n", "read"
  • + *
+ * @param urls + * the URLs used for resolving classes + * @throws SecurityException if a SecurityManager is running and any required + * permissions were not granted to this Protection Domain and all preceding callers + */ + public SecurityTokenFactory(URL[] urls) { + if (urls != null && urls.length > 0) { + this.classLoader = new URLClassLoader(urls); + } + else { + this.classLoader = null; + } + + this.cachedCodeSources = new HashMap(); + this.cachedPermissions = new HashMap(); + this.cachedPrincipals = new HashMap(); + this.canonicalizer = new Canonicalizer(); + this.jarHelper = new JarHelper(); + } + + /** + *

+ * Returns the Principal that corresponds to a supplied String containing + * the name of the Principal class and the Principal's name, separated by + * spaces. This method will attempt to instantiate the class using + * reflection, passing the Principal name into the constructor. If the + * Principal class is not specified, a generic Principal class will be + * instantiated instead. + *

+ *

+ * Principal Strings look like this: + *

+ *
    + *
  • com.ecyrd.jspwiki.auth.WikiPrincipal "Ernesto"
  • + *
  • javax.security.auth.kerberos.KerberosPrincipal "Ernesto@example.com"
  • + *
  • "Ernesto"
  • + *
+ * + * @param s + * the string specifying the Principal class and name + * @return the instantiated Principal + * @throws SecurityException when the SecurityManager, if running, denies + * the protection domain of this class (and of all preceding callers): + * @throws SecurityException when the SecurityManager, if running, denies + * the protection domain of this class (and of all preceding callers): + *
    + *
  • java.lang.RuntimePermission "createClassLoader"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-principal-packages"
  • + *
+ */ + public Principal getPrincipal(String s) { + + // If found in cache, return it + if (cachedPrincipals.containsKey(s)) { + return (Principal) cachedPrincipals.get(s); + } + + int space = s.indexOf(ONE_SPACE); + int quote = s.indexOf(DOUBLE_QUOTE); + Principal principal = null; + + // If no principal class supplied, prepend a generic principal class + if (space == NOT_FOUND || quote < space) { + s = GenericPrincipal.class.getName() + ONE_SPACE + s; + space = s.indexOf(ONE_SPACE); + } + + // Next, try to instantiate principal class with a 1-arg constructor + String principalClass = s.substring(0, space).trim(); + String principalName = s.substring(principalClass.length()).trim(); + if (principalName.startsWith(DOUBLE_QUOTE) + && principalName.endsWith(DOUBLE_QUOTE) + && principalName.length() > 2) { + principalName = principalName.substring(1, + principalName.length() - 1); + } + try { + Class clazz = findClass(principalClass); + Constructor c = clazz.getConstructor(new Class[] { String.class }); + Object p = c.newInstance(new Object[] { principalName }); + principal = (Principal) p; + } catch (Exception e) { + principal = new UnresolvedPrincipal(principalClass, principalName, + e); + } + + // Stash principal and return it + cachedPermissions.put(s, principal); + return principal; + } + + /** + * Returns the number of Principal objects cached by this factory since + * the last time {@link #reset()} was called. + * @return the number + */ + public int getPrincipalCount() { + return cachedPrincipals.size(); + } + + /** + *

+ * Returns the Permission that corresponds to a supplied String, without + * canonicalizing the Permission prior to returning it. + * See {@link #getPermission(String, boolean)}. + * @see #getPermission(String, boolean) + */ + public Permission getPermission(String permissionString) + throws ClassNotFoundException { + return getPermission(permissionString, false); + } + + /** + *

+ * Returns the Permission that corresponds to a supplied String. The String + * contains the full class name, target and actions, and is formatted + * exactly like a Java policy file grant statement. Permissions must have at + * least a target; actions are optional. + *

+ *

+ * Permission strings look like this: + *

+ *
    + *
  • java.util.PropertyPermission "java.security.auth.login.config", "write"
  • + *
  • javax.security.auth.AuthPermission "setLoginConfiguration"
  • + *
+ *

+ * Assuming the Permission string is well-formed, this method + * always returns a Permission, even if the Permission cannot be + * located by the classloader. If the Permission class cannot be loaded, the + * returned Permission will be of type + * {@link java.security.UnresolvedPermission}. Permission strings that + * contain just one argument, for example the class name but no target or + * actions, will be unresolved. + *

+ *

If the parameter canonicalize is true, the Permission + * will be canonicalized before it is returned. Specifically, if the Permission is + * of type {@link java.io.FilePermission}, its target will + * be the canonical path. Likewise, {@link java.net.SocketPermission} local host names + * 127.0.0.1 and "" will be converted to localhost.

+ * + * @param permissionString + * the Permission string + * @param canonicalize + * whether to canonicalize the Permission before returning it + * @return the instantiated Permission + * @throws ClassNotFoundException + * if the Permission string is malformed + * @throws SecurityException when the SecurityManager, if running, denies + * the protection domain of this class (and of all preceding callers): + *
    + *
  • java.lang.RuntimePermission "createClassLoader"
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • + *
  • java.lang.RuntimePermission "accessClassInPackage.custom-permission-packages"
  • + *
+ */ + public Permission getPermission(String permissionString, boolean canonicalize) + throws ClassNotFoundException { + + // If found in cache, return it + if (cachedPermissions.containsKey(permissionString)) { + return (Permission) cachedPermissions.get(permissionString); + } + + String permissionClass = null; + String target = null; + String actions = null; + + // Extract the permission class name, target and actions + Matcher m = PERMISSION_PATTERN.matcher(permissionString); + if (m.find()) { + permissionClass = m.group(1); + if (m.group(3) != null) { + target = m.group(2); + actions = m.group(3); + } else if (m.group(2) != null) { + target = m.group(2); + } + } else { + throw new ClassNotFoundException("Malformed permission: " + + permissionString); + } + + // Try and instantiate the class... if exceptions, return an + // UnresolvedPermission + Permission perm = null; + try { + // Find the class we need + Class clazz = findClass(permissionClass); + + // Prepare the constructor and arguments + Class[] constructorArgClasses = new Class[0]; + String[] constructorArgs = new String[0]; + + if (actions != null) { + constructorArgs = new String[] { target, actions }; + constructorArgClasses = new Class[] { String.class, + String.class }; + } else if (target != null) { + constructorArgs = new String[] { target }; + constructorArgClasses = new Class[] { String.class }; + } + Constructor c = clazz.getConstructor(constructorArgClasses); + + // Instantiate the class + perm = (Permission) c.newInstance(constructorArgs); + } catch (Exception e) { + perm = new UnresolvedPermission(permissionClass, target, actions, + null); + } + + // Canonicalize the permission if asked + if (canonicalize) { + perm = canonicalizer.canonicalize(perm); + } + + // Stash permission and return it + cachedPermissions.put(permissionString, perm); + return perm; + } + + /** + * Returns the number of Permission objects cached by this factory since + * the last time {@link #reset()} was called. + * @return the number + */ + public int getPermissonCount() { + return cachedPermissions.size(); + } + + + /** + *

+ * Returns the CodeSource that corresponds to a supplied String, without + * canonicalizing the path prior to returning it. + * See {@link #getCodeSource(String, boolean, boolean)}. + * @see #getCodeSource(String, boolean, boolean) + */ + public CodeSource getCodeSource(String path) throws IOException, MalformedURLException { + return getCodeSource(path, false); + } + + /** + *

Returns the CodeSource corresponding on a specified file path. If the + * path ends in .jar, this method will attempt to load the + * Jar file and instantiate the CodeSource with its associated certificates, + * if possible. To improve performance, CodeSources are cached.

+ *

If the parameter canonicalize is true, the path + * will be canonicalized before it is returned.

+ * + * @param path + * the absolute path of the jar + * @param canonicalize + * whether to canonicalize the Permission before returning it + * @return the CodeSource + * @throws IOException + * if the file cannot be found in the filesystem + * @throws MalformedURLException + * if the URL for the CodeSource is malformed + * @throws SecurityException when the SecurityManager, if running, denies + * the protection domain of this class (and of all preceding callers): + *
    + *
  • java.io.FilePermission "path", "read"
  • + *
  • java.net.NetPermission "specifyStreamHandler"
  • + *
+ * + */ + public CodeSource getCodeSource(String path, boolean canonicalize) throws IOException, + MalformedURLException { + // TODO: determine if this method is really needed... + + // Trim path to remove file: prefix + if (path.startsWith("file:")) { + path = path.substring(5); + } + + // If found in cache, return it + if (cachedCodeSources.containsKey(path)) { + return (CodeSource) cachedCodeSources.get(path); + } + + // Otherwise: create new code source + File file = new File(path); + if (!PolicyReader.secureExists(file)) { + throw new IOException("Path " + path + " does not exist."); + } + + // Canonicalize path if requested + if (canonicalize) { + path = file.getCanonicalPath(); + } + + // If path ends in .jar, try and load the Jar and its certificates + CodeSource cs = null; + if (path.endsWith(".jar")) { + JarFile jar = new JarFile(new File(path)); + Set certSet = jarHelper.extractSigningCertificates(jar); + Certificate[] certs = (Certificate[]) certSet + .toArray(new Certificate[certSet.size()]); + cs = new CodeSource(new URL("file:" + path), certs); + } else { + cs = new CodeSource(new URL("file:" + path), new Certificate[0]); + } + + // Stash codesource and return it + cachedCodeSources.put(path, cs); + return cs; + } + + /** + * Returns the number of CodeSource objects cached by this factory since + * the last time {@link #reset()} was called. + * @return the number + */ + public int getCodeSourceCount() { + // TODO: determine if this method is really needed... + return cachedCodeSources.size(); + } + + /** + * Looks up and returns a Class matching a supplied fully-qualified type + * name. The current ClassLoader is tried first; if not found, the classpath + * supplied in the URLs used to construct this ClassResolver will be tried + * next. + * + * @param className + * the name of the Class to find + * @return the resolved Class + * @throws ClassNotFoundException + * if the class cannot be found + * @throws SecurityException when the SecurityManager, if running, denies + * the protection domain of this class (and of all preceding callers) + * any of these permissions: java.lang.RuntimePermission "getClassLoader", + * java.lang.RuntimePermission "accessClassInPackage.package-name-of-className". + */ + public Class findClass(String className) throws ClassNotFoundException { + Class cls = null; + + // Try the parent classloader first + try { + cls = this.getClass().getClassLoader().loadClass(className); + return cls; + } catch (ClassNotFoundException e) { + // The parent classloader couldn't find it! + } + + // Try the URLs supplied in this resolver + if (classLoader != null) { + return classLoader.loadClass(className); + } + throw new ClassNotFoundException("Class not found: " + className); + } + + /** + * Flushes the SecurityTokenFactory's caches and resets all counters. + */ + public synchronized void reset() { + cachedCodeSources.clear(); + cachedPermissions.clear(); + cachedPrincipals.clear(); + } + +} --- jspwiki-2.8.0.orig/src/org/freshcookies/security/policy/Canonicalizer.java +++ jspwiki-2.8.0/src/org/freshcookies/security/policy/Canonicalizer.java @@ -0,0 +1,400 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.freshcookies.security.policy; + +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.SocketPermission; +import java.net.URL; +import java.net.UnknownHostException; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.cert.Certificate; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Properties; + +/** + * Utility class that canonicalizes Permissions and file paths, It will initializes a list of property substitutions that can be used when + * constructing file paths. This class can optionally dump a list of system properties (and their + * file path equivalents) to disk. + * @author Andrew Jaquith + * + */ +public class Canonicalizer { + + /** Static String that means "no codesource." */ + public static final String NO_CODESOURCE = "n/c"; + + private static final String CODEBASE_FILE_PREFIX = "file:"; + + private static final String CODEBASE_URL_PREFIX = "url:"; + + /** + * Constructs a new Canonicalizer instance. + */ + public Canonicalizer() + { + super(); + } + + /** + * Formats a ProtectionDomain, substituting properties into + * the CodeSource or path, as needed. + * + * @param pd the protection domain + * @param properties a Properties map containg key-value pairs of property names and + * associated file paths + * @return a revised CodeSource, with system properties substitutions as + * needed, or the same CodeSource if no properties match + */ + public final CodeSource propertize(CodeSource codeSource, Properties properties) { + // Bail if codesource location is null + if (codeSource == null || codeSource.getLocation() == null) { + return codeSource; + } + + for (Iterator it = properties.keySet().iterator(); it.hasNext();) { + String path = (String)it.next(); + String property = (String)properties.get(path); + String cs = codeSource.getLocation().toExternalForm(); + try { + // Does the codebase start with file: ? + String value = CODEBASE_FILE_PREFIX + path; + if (cs.startsWith(value)) { + URL url = new URL(CODEBASE_FILE_PREFIX + "${" + property + "}" + + cs.substring(value.length())); + Certificate[] certs = codeSource.getCertificates(); + return new CodeSource(url, certs); + } + + // Does the codebase start with url: ? + value = CODEBASE_URL_PREFIX + path; + if (cs.startsWith(value)) { + URL url = new URL(CODEBASE_URL_PREFIX + "${" + property + "}" + + cs.substring(value.length())); + return new CodeSource(url, codeSource.getCertificates()); + } + } + catch (MalformedURLException e) { + break; + } + } + return codeSource; + } + + /** + * Substitutes properties into a given FilePermission's + * URL or path, as needed. + * + * @param perm the permission + * @param properties a Properties map containg key-value pairs of property names and + * associated file paths + * @return a String representing the FilePermission's URL or + * file path, with system properties substitutions as needed + */ + public final Permission propertize(Permission perm, Properties properties) { + String target = perm.getName(); + + if (perm instanceof FilePermission) { + // See if we can substitute any properties + for (Iterator it = properties.keySet().iterator(); it.hasNext();) { + String path = (String)it.next(); + String property = (String)properties.get(path); + String value = path; + if (target.startsWith(value)) { + String newPath = "${" + property + "}" + target.substring(value.length()); + perm = new FilePermission(newPath, perm.getActions()); + break; + } + } + } + return perm; + } + +/** + * Formats a CodeSource for use with a policy file. The format is identical to + * that used in policy files. If the URL of the codesource ends with a file + * separator (/ or \), a "-" will be appended. The formatter will insert + * system properties (such as ${user.home}) as needed. + * + * @param cs the code source + * @return the code source, formatted nicely as a string + */ + public static final String format(CodeSource codeSource) { + if (codeSource == null || codeSource.getLocation() == null) { + return NO_CODESOURCE; + } + String cs = codeSource.getLocation().toExternalForm(); + if (cs.endsWith(File.separator)) { + cs += "-"; + } + return cs; + } + + /** + * Formats an array of Principals; the array may be null. + * + * @param principals the array of Principals to formatted + * @return a single String, with each Principal separated by a single space + */ + public static final String format(Principal[] principals) { + return format(principals, false); + } + + /** + * Formats an array of CachedPrincipals, optionally with line delimeters suitable + * for a policy file. The array may be null. + * + * @param principals the array of CachedPrincipals to formatted + * @param forPolicy true if the string should be formatted for + * a policy file, with indents and a linebreak after each principal + * @return a single String, with each Principal separated by a single space + */ + public static final String format(Principal[] principals, boolean forPolicy) { + String out = "-"; + if (principals != null) { + out = ""; + for (int i = 0; i < principals.length; i++) { + if (forPolicy) { + out += " principal " + format(principals[i]); + if (i < (principals.length - 1)) { + out += ','; + } + out += "\n"; + } + else { + out += format(principals[i]); + if (i < (principals.length - 1)) { + out += ' '; + } + } + } + } + return out; + } + + /** + * Returns a Map of key/value pairs that correspond the subset of + * System properties that correspond to valid file paths. + * @return the map + */ + public static final Properties getPathSubstitutions() { + final Properties substitutions = new Properties(); + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + Enumeration keys = System.getProperties().propertyNames(); + while (keys.hasMoreElements()) { + String substitutionProperty = (String) keys.nextElement(); + if (!substitutionProperty.equals("file.separator")) { + String path = System.getProperty(substitutionProperty); + if (path.length() > 0) { + // Try making a file out of it; if it exists, then dereference it + File file = new File(path); + if (file.exists()) { + try { + path = file.getCanonicalPath(); + } + catch (IOException e) { + System.err.println("Error de-referencing: " + e.getMessage()); + } + substitutions.put(path, substitutionProperty); + } + } + } + } + return null; + } + }); + return substitutions; + } + + /** + * Formats a CachedPrincipal for use with a policy file. The string returned + * is the principal class, plus a space, followed by the principal's getName() + * value in quotes. + * + * @param principal the Principal to format + * @return a nicely-formatted string representing the Principal + */ + public static final String format(Principal principal) { + return principal.getClass().getName() + ' ' + '\"' + principal.getName() + '\"'; + } + + /** + * Formats a CachedPermission for use in a policy file or log file. The format is + * identical to that used in policy files. + * @param perm the Permission to format + * @return a nicely-formatted string representing the Permission + */ + public static final String format(Permission permission) { + String out = permission.getClass().getName() + " "; + String target = permission.getName(); + String actions = permission.getActions(); + + boolean hasName = (target != null && target.length() != 0); + boolean hasActions = (actions != null && actions.length() != 0); + String formattedTarget; + if (hasName) { + formattedTarget = target.replaceAll("\"", "\\u0022"); + out += ("\"" + formattedTarget + "\""); + if (hasActions) { + out += (", "); + } + } + if (hasActions) { + out += ("\"" + actions + "\""); + } + return out; + } + + /** + * Canonicalizes a CodeSource. + * @param cs the CodeSource + * @param properties the properties to use for expansion + * @return the CodeSource with its file path + * @throws IOException + */ + public final CodeSource canonicalize(CodeSource codeSource, Properties properties) throws IOException { + + URL url = codeSource.getLocation(); + Certificate[] certs = codeSource.getCertificates(); + + // Canonicalize the URL + if (url != null) { + String cs = url.getPath(); + // Trim path to remove file: prefix + if (cs.startsWith(CODEBASE_FILE_PREFIX)) { + cs = cs.substring(5); + } + else if (cs.startsWith(CODEBASE_URL_PREFIX)) { + return codeSource; + } + + // See if we need to substitute any properties + for (Iterator it = properties.keySet().iterator(); it.hasNext();) { + String path = (String)it.next(); + String property = (String)properties.get(path); + // Does the codebase start with the property? + String value = "${" + property + "}"; + if (cs.startsWith(value)) { + if (!path.endsWith(File.separator) && (cs.length() <= value.length())) { + path += File.separator; + } + cs = path + cs.substring(value.length()); + break; + } + } + + // Create new file with canonical path + File file = new File(cs).getCanonicalFile(); + cs = file.getAbsolutePath(); + url = new URL("file:" + cs); + } + + return new CodeSource(url, certs); + } + + /** + *

+ * Normalizes permissions by changing permission targets to canonical forms. + * For example: + *

+ *
    + *
  • {@link java.io.FilePermission} paths are converted to canonical form + * (symbolic links dereferenced), if possible
  • + *
  • {@link java.net.SocketPermission} local host names 127.0.0.1 and "" + * are converted to localhost
  • + *
+ *

+ * All other permission types are returned unchanged. + *

+ * @param permission the permission to canonicalize + * @return the canonicalized permission + * @throws UnknownHostException + */ + public final Permission canonicalize(Permission permission) + { + // For file permissions, canonicalize the file path + if (permission instanceof FilePermission) { + File file = new File(permission.getName()); + try { + String canonicalName = file.getCanonicalPath(); + if (!permission.getName().equals(canonicalName)) { + String actions = permission.getActions(); + permission = new FilePermission(canonicalName, actions); + } + } + catch (IOException e) { + System.err.println(e.getMessage()); + } + } + + // For socket permissions, normalize "localhost" refs + else if (permission instanceof SocketPermission) { + String host = permission.getName(); + if (host.startsWith("localhost")) { + return permission; + } + + // Normalize + String port = null; + String target = permission.getName(); + int lastColon = target.lastIndexOf(":"); + if (lastColon > -1) + { + host = target.substring(0,lastColon); + port = target.substring(lastColon + 1, target.length()); + } + + final String testHost = host; + String resolvedHost = (String)AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + InetAddress addr = InetAddress.getByName(testHost); + if (addr.isLoopbackAddress()) + { + return "localhost"; + } + } + catch (AccessControlException e) + { + // If we don't have permission to resolve the host, it's ok + return testHost; + } + catch (UnknownHostException e) { + // It's ok if we can't resolve the host... + return testHost; + } + return testHost; + } + },null); + + target = (port == null) ? resolvedHost : resolvedHost + ":" + port; + String actions = permission.getActions(); + permission = new SocketPermission(target, actions); + } + + // All other permissions are unchanged + return permission; + } + +} --- jspwiki-2.8.0.orig/src/com/ecyrd/jspwiki/plugin/WeblogPlugin.java +++ jspwiki-2.8.0/src/com/ecyrd/jspwiki/plugin/WeblogPlugin.java @@ -273,7 +273,16 @@ for( Iterator i = blogEntries.iterator(); i.hasNext() && maxEntries-- > 0 ; ) { WikiPage p = (WikiPage) i.next(); - + String commentPageName = TextUtil.replaceString( p.getName(), + "blogentry", + "comments" ); + if( guessNumberOfComments( engine, commentPageName ) > 0 ) { + hasComments = true; + } + else + { + hasComments = false; + } if( mgr.checkPermission( context.getWikiSession(), new PagePermission(p, PagePermission.VIEW_ACTION) ) ) { @@ -329,7 +338,7 @@ String html = engine.getHTML( entryCtx, engine.getPage(entry.getName()) ); // Extract the first h1/h2/h3 as title, and replace with null - buffer.append("
\n"); + buffer.append("\n"); buffer.append("
\n"); --- jspwiki-2.8.0.orig/src/net/sf/akismet/Akismet.java +++ jspwiki-2.8.0/src/net/sf/akismet/Akismet.java @@ -0,0 +1,371 @@ +/** + * Copyright (c) 2005-2006, David A. Czarnecki + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of the "David A. Czarnecki" nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Products derived from this software may not be called "Akismet Java API", + * nor may "Akismet Java API" appear in their name, without prior written permission of + * David A. Czarnecki. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.sf.akismet; + +import org.apache.commons.httpclient.*; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.params.HttpClientParams; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +/** + * Akistmet Java API + *

+ * Akismet API documentation. + * + * @author David Czarnecki + * @version $Id: Akismet.java,v 1.4 2006/08/06 20:10:00 czarneckid Exp $ + */ +public class Akismet { + + private Log logger = LogFactory.getLog(Akismet.class); + + // Constants + private static final String USER_AGENT_HEADER = "User-Agent"; + private static final String USER_AGENT_VALUE = "Akismet Java API/1.02"; + + private static final String API_PARAMETER_KEY = "key"; + private static final String API_PARAMETER_BLOG = "blog"; + private static final String API_PARAMETER_USER_IP = "user_ip"; + private static final String API_PARAMETER_USER_AGENT = "user_agent"; + private static final String API_PARAMETER_REFERRER = "referrer"; + private static final String API_PARAMETER_PERMALINK = "permalink"; + private static final String API_PARAMETER_COMMENT_TYPE = "comment_type"; + private static final String API_PARAMETER_COMMENT_AUTHOR = "comment_author"; + private static final String API_PARAMETER_COMMENT_AUTHOR_EMAIL = "comment_author_email"; + private static final String API_PARAMETER_COMMENT_AUTHOR_URL = "comment_author_url"; + private static final String API_PARAMETER_COMMENT_CONTENT = "comment_content"; + + private static final String VALID_RESPONSE = "valid"; + private static final String FALSE_RESPONSE = "false"; + + public static final String COMMENT_TYPE_BLANK = ""; + public static final String COMMENT_TYPE_COMMENT = "comment"; + public static final String COMMENT_TYPE_TRACKBACK = "trackback"; + public static final String COMMENT_TYPE_PINGBACK = "pingback"; + + private HttpClient httpClient; + private String apiKey; + private String blog; + private boolean verifiedKey = false; + private int httpResult; + + /** + * Construct an instance to work with the Akismet API. + *

+ *
+     * Usage:
+     * 

+ * Akismet akismet = new Akismet("Your API key", "http://your.blog.com/"); + * System.out.println("Testing comment spam: " + akismet.commentCheck("x.y.z.w", "XXX", "", "", "", "", "", "", "VIAGRA! LOTS OF VIAGRA!", null)); + *

+ *

+ * You do not need to call {@link #verifyAPIKey()} before using the {@link #commentCheck(String, String, String, String, String, String, String, String, String, java.util.Map)}, + * {@link #submitSpam(String, String, String, String, String, String, String, String, String, java.util.Map)}, or + * {@link #submitHam(String, String, String, String, String, String, String, String, String, java.util.Map)} methods. + *

+ * + * @param apiKey Akismet API key + * @param blog Blog associated with the API key + * @throws IllegalArgumentException If either the API key or blog is null + */ + public Akismet(String apiKey, String blog) { + this.apiKey = apiKey; + this.blog = blog; + + if (apiKey == null) { + throw new IllegalArgumentException("API key cannot be null"); + } + + if (blog == null) { + throw new IllegalArgumentException("Blog cannot be null"); + } + + httpClient = new HttpClient(); + HttpClientParams httpClientParams = new HttpClientParams(); + DefaultHttpMethodRetryHandler defaultHttpMethodRetryHandler = new DefaultHttpMethodRetryHandler(0, false); + httpClientParams.setParameter(USER_AGENT_HEADER, USER_AGENT_VALUE); + httpClientParams.setParameter(HttpClientParams.RETRY_HANDLER, defaultHttpMethodRetryHandler); + httpClient.setParams(httpClientParams); + } + + /** + * Return the HTTP status code of the last operation + * + * @return HTTP status code + */ + public int getHttpResult() { + return httpResult; + } + + /** + * Check to see if the API key has been verified + * + * @return true if the API key has been verified, false otherwise + */ + public boolean isVerifiedKey() { + return verifiedKey; + } + + /** + * Sets proxy configuration information. This method must be called before + * any calls to the API if you require proxy configuration. + * + * @param proxyHost Proxy host + * @param proxyPort Proxy port + */ + public void setProxyConfiguration(String proxyHost, int proxyPort) { + HostConfiguration hostConfiguration = new HostConfiguration(); + hostConfiguration.setProxy(proxyHost, proxyPort); + + httpClient.setHostConfiguration(hostConfiguration); + } + + /** + * Check to see if the input is null or blank + * + * @param input Input + * @return true if input is null or blank, false otherwise + */ + private boolean checkNullOrBlank(String input) { + return (input == null || "".equals(input)); + } + + /** + * Sets proxy authentication information. This method must be called before any + * calls to the API if you require proxy authentication. + * + * @param proxyUsername Username to access proxy + * @param proxyPassword Password to access proxy + */ + public void setProxyAuthenticationConfiguration(String proxyUsername, String proxyPassword) { + httpClient.getState().setProxyCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxyUsername, proxyPassword)); + } + + /** + * Verify your API key + * + * @return true if the API key has been verified, false otherwise + */ + public boolean verifyAPIKey() { + boolean callResult = true; + + PostMethod post = new PostMethod("http://rest.akismet.com/1.1/verify-key"); + post.addParameter(API_PARAMETER_KEY, apiKey); + post.addParameter(API_PARAMETER_BLOG, blog); + + try { + httpResult = httpClient.executeMethod(post); + String result = post.getResponseBodyAsString(); + + if (logger.isDebugEnabled()) { + logger.debug("Akismet response: " + result); + } + + if (!checkNullOrBlank(result)) { + if (!VALID_RESPONSE.equals(result)) { + callResult = false; + } + } + } catch (IOException e) { + if (logger.isErrorEnabled()) { + logger.error(e); + } + + callResult = false; + } + + verifiedKey = callResult; + + return callResult; + } + + /** + * Generic call to Akismet + * + * @param function Function used in constructing the URL to Akismet for the proper function to call. Either "comment-check", "submit-spam", or "submit-ham". + * @param ipAddress IP address of the comment submitter + * @param userAgent User agent information + * @param referrer The content of the HTTP_REFERER header should be sent here + * @param permalink The permanent location of the entry the comment was submitted to + * @param commentType May be blank, comment, trackback, pingback, or a made up value like "registration" + * @param author Submitted name with the comment + * @param authorEmail Submitted email address + * @param authorURL Commenter URL + * @param commentContent The content that was submitted + * @param other In PHP there is an array of enviroment variables called $_SERVER which contains information about the web server itself as well as a key/value for every HTTP header sent with the request. This data is highly useful to Akismet as how the submited content interacts with the server can be very telling, so please include as much information as possible. + * @return true if the comment is identified by Akismet as spam, false otherwise + */ + protected boolean akismetCall(String function, String ipAddress, String userAgent, String referrer, String permalink, String commentType, + String author, String authorEmail, String authorURL, String commentContent, Map other) { + boolean callResult = false; + + String akismetURL = "http://" + apiKey + ".rest.akismet.com/1.1/" + function; + + PostMethod post = new PostMethod(akismetURL); + post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); + post.addParameter(new NameValuePair(API_PARAMETER_BLOG, blog)); + if (ipAddress != null) { + post.addParameter(new NameValuePair(API_PARAMETER_USER_IP, ipAddress)); + } else { + post.addParameter(new NameValuePair(API_PARAMETER_USER_IP, "")); + } + if (userAgent != null) { + post.addParameter(new NameValuePair(API_PARAMETER_USER_AGENT, userAgent)); + } + if (referrer != null) { + post.addParameter(new NameValuePair(API_PARAMETER_REFERRER, referrer)); + } + if (permalink != null) { + post.addParameter(new NameValuePair(API_PARAMETER_PERMALINK, permalink)); + } + if (commentType != null) { + post.addParameter(new NameValuePair(API_PARAMETER_COMMENT_TYPE, commentType)); + } + if (author != null) { + post.addParameter(new NameValuePair(API_PARAMETER_COMMENT_AUTHOR, author)); + } + if (authorEmail != null) { + post.addParameter(new NameValuePair(API_PARAMETER_COMMENT_AUTHOR_EMAIL, authorEmail)); + } + if (authorURL != null) { + post.addParameter(new NameValuePair(API_PARAMETER_COMMENT_AUTHOR_URL, authorURL)); + } + if (commentContent != null) { + post.addParameter(new NameValuePair(API_PARAMETER_COMMENT_CONTENT, commentContent)); + } + + if (other != null && other.size() > 0) { + Iterator keyIterator = other.keySet().iterator(); + while (keyIterator.hasNext()) { + String key = (String) keyIterator.next(); + if ((key != null) && (other.get(key) != null)) { + post.addParameter(new NameValuePair(key, other.get(key).toString())); + } + } + } + + try { + httpResult = httpClient.executeMethod(post); + String result = post.getResponseBodyAsString(); + + if (logger.isDebugEnabled()) { + logger.debug("Akismet response: " + result); + } + + if (!checkNullOrBlank(result)) { + result = result.trim(); + + if (!FALSE_RESPONSE.equals(result)) { + callResult = true; + } + } + } catch (IOException e) { + if (logger.isErrorEnabled()) { + logger.error(e); + } + + callResult = true; + } + + return callResult; + } + + /** + * From the API docs, This is basically the core of everything. This call takes a number of arguments and characteristics about the submitted content and then returns a thumbs up or thumbs down. Almost everything is optional, but performance can drop dramatically if you exclude certain elements. I would recommend erring on the side of too much data, as everything is used as part of the Akismet signature." + * + * @param ipAddress IP address of the comment submitter + * @param userAgent User agent information + * @param referrer The content of the HTTP_REFERER header should be sent here + * @param permalink The permanent location of the entry the comment was submitted to + * @param commentType May be blank, comment, trackback, pingback, or a made up value like "registration" + * @param author Submitted name with the comment + * @param authorEmail Submitted email address + * @param authorURL Commenter URL + * @param commentContent The content that was submitted + * @param other In PHP there is an array of enviroment variables called $_SERVER which contains information about the web server itself as well as a key/value for every HTTP header sent with the request. This data is highly useful to Akismet as how the submited content interacts with the server can be very telling, so please include as much information as possible. + * @return true if the comment is identified by Akismet as spam, false otherwise + */ + public boolean commentCheck(String ipAddress, String userAgent, String referrer, String permalink, String commentType, + String author, String authorEmail, String authorURL, String commentContent, Map other) { + return akismetCall("comment-check", ipAddress, userAgent, referrer, permalink, commentType, author, authorEmail, + authorURL, commentContent, other); + } + + /** + * From the API docs, This call is for submitting comments that weren't marked as spam but should have been. It takes identical arguments as comment check." + * + * @param ipAddress IP address of the comment submitter + * @param userAgent User agent information + * @param referrer The content of the HTTP_REFERER header should be sent here + * @param permalink The permanent location of the entry the comment was submitted to + * @param commentType May be blank, comment, trackback, pingback, or a made up value like "registration" + * @param author Submitted name with the comment + * @param authorEmail Submitted email address + * @param authorURL Commenter URL + * @param commentContent The content that was submitted + * @param other In PHP there is an array of enviroment variables called $_SERVER which contains information about the web server itself as well as a key/value for every HTTP header sent with the request. This data is highly useful to Akismet as how the submited content interacts with the server can be very telling, so please include as much information as possible. + * @return true if the comment is identified by Akismet as spam, false otherwise + */ + public void submitSpam(String ipAddress, String userAgent, String referrer, String permalink, String commentType, + String author, String authorEmail, String authorURL, String commentContent, Map other) { + akismetCall("submit-spam", ipAddress, userAgent, referrer, permalink, commentType, author, authorEmail, + authorURL, commentContent, other); + } + + /** + * From the API docs, This call is intended for the marking of false positives, things that were incorrectly marked as spam. It takes identical arguments as comment check and submit spam." + * + * @param ipAddress IP address of the comment submitter + * @param userAgent User agent information + * @param referrer The content of the HTTP_REFERER header should be sent here + * @param permalink The permanent location of the entry the comment was submitted to + * @param commentType May be blank, comment, trackback, pingback, or a made up value like "registration" + * @param author Submitted name with the comment + * @param authorEmail Submitted email address + * @param authorURL Commenter URL + * @param commentContent The content that was submitted + * @param other In PHP there is an array of enviroment variables called $_SERVER which contains information about the web server itself as well as a key/value for every HTTP header sent with the request. This data is highly useful to Akismet as how the submited content interacts with the server can be very telling, so please include as much information as possible. + * @return true if the comment is identified by Akismet as spam, false otherwise + */ + public void submitHam(String ipAddress, String userAgent, String referrer, String permalink, String commentType, + String author, String authorEmail, String authorURL, String commentContent, Map other) { + akismetCall("submit-ham", ipAddress, userAgent, referrer, permalink, commentType, author, authorEmail, + authorURL, commentContent, other); + } +} --- jspwiki-2.8.0.orig/debian/jspwiki.copy +++ jspwiki-2.8.0/debian/jspwiki.copy @@ -0,0 +1,32 @@ +#!/bin/sh + +if [ "z$1" == "z" ]; then + echo No application directory given. + exit 1 +fi + +mkdir /var/lib/tomcat4/webapps/$1 +ln -s /var/lib/tomcat4/webapps/JSPWiki/Diff.jsp /var/lib/tomcat4/webapps/$1/Diff.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/Edit.jsp /var/lib/tomcat4/webapps/$1/Edit.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/Error.jsp /var/lib/tomcat4/webapps/$1/Error.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/PageInfo.jsp /var/lib/tomcat4/webapps/$1/PageInfo.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/PageModified.jsp /var/lib/tomcat4/webapps/$1/PageModified.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/Preview.jsp /var/lib/tomcat4/webapps/$1/Preview.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/Search.jsp /var/lib/tomcat4/webapps/$1/Search.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/Upload.jsp /var/lib/tomcat4/webapps/$1/Upload.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/UserPreferences.jsp /var/lib/tomcat4/webapps/$1/UserPreferences.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/Wiki.jsp /var/lib/tomcat4/webapps/$1/Wiki.jsp +ln -s /var/lib/tomcat4/webapps/JSPWiki/images /var/lib/tomcat4/webapps/$1/images +ln -s /var/lib/tomcat4/webapps/JSPWiki/META-INF /var/lib/tomcat4/webapps/$1/META-INF +ln -s /var/lib/tomcat4/webapps/JSPWiki/images /var/lib/tomcat4/webapps/$1/images +mkdir /var/lib/tomcat4/webapps/$1/templates +ln -s /var/lib/tomcat4/webapps/JSPWiki/templates/default /var/lib/tomcat4/webapps/$1/templates/default +mkdir /var/lib/tomcat4/webapps/$1/WEB-INF +ln -s /var/lib/tomcat4/webapps/JSPWiki/WEB-INF/jspwiki.tld /var/lib/tomcat4/webapps/$1/WEB-INF/jspwiki.tld +ln -s /var/lib/tomcat4/webapps/JSPWiki/WEB-INF/lib /var/lib/tomcat4/webapps/$1/WEB-INF/lib +mkdir /etc/jspwiki/$1 +cp /etc/jspwiki/jspwiki.properties /etc/jspwiki/web.xml /etc/jspwiki/$1 +ln -s /etc/jspwiki/$1/jspwiki.properties /var/lib/tomcat4/webapps/$1/WEB-INF/jspwiki.properties +ln -s /etc/jspwiki/$1/web.xml /var/lib/tomcat4/webapps/$1/WEB-INF/web.xml + + --- jspwiki-2.8.0.orig/debian/tomcat.policy +++ jspwiki-2.8.0/debian/tomcat.policy @@ -0,0 +1,36 @@ +grant codeBase "file:/var/lib/tomcat6/webapps/JSPWiki/-" { + permission java.io.FilePermission "/var/lib/tomcat6/temp/-", "read,write"; + permission java.util.PropertyPermission "java.io.tmpdir", "read"; + permission java.util.PropertyPermission "user.dir", "read"; + permission java.util.PropertyPermission "jspwiki.propertyfile.cascade.1", "read"; + permission java.util.PropertyPermission "java.runtime.version", "read"; + permission java.io.FilePermission "/etc/jspwiki/-", "read"; + permission java.io.FilePermission "/var/log/tomcat6/-", "read,write"; + permission java.io.FilePermission "/var/lib/jspwiki", "read,write"; + permission java.io.FilePermission "/var/lib/jspwiki/-", "read,write"; + permission java.io.FilePermission "/usr/share/jspwiki/-", "read"; + permission java.io.FilePermission "/var/lib/tomcat6/webapps/JSPWiki/-", "read"; + permission java.security.SecurityPermission "getProperty.login.configuration.provider", "read,write"; + permission javax.security.auth.AuthPermission "*"; + permission javax.management.MBeanServerPermission "*"; + permission javax.management.MBeanPermission "*", "*"; + permission javax.management.MBeanTrustPermission "*"; +}; +grant codeBase "file:/usr/share/jspwiki/-" { + permission java.io.FilePermission "/var/lib/tomcat6/temp/-", "read,write"; + permission java.util.PropertyPermission "java.io.tmpdir", "read"; + permission java.util.PropertyPermission "user.dir", "read"; + permission java.util.PropertyPermission "jspwiki.propertyfile.cascade.1", "read"; + permission java.util.PropertyPermission "java.runtime.version", "read"; + permission java.io.FilePermission "/etc/jspwiki/-", "read"; + permission java.io.FilePermission "/var/log/tomcat6/-", "read,write"; + permission java.io.FilePermission "/var/lib/jspwiki", "read,write"; + permission java.io.FilePermission "/var/lib/jspwiki/-", "read,write"; + permission java.io.FilePermission "/usr/share/jspwiki/-", "read"; + permission java.io.FilePermission "/var/lib/tomcat6/webapps/JSPWiki/-", "read"; + permission java.security.SecurityPermission "getProperty.login.configuration.provider", "read,write"; + permission javax.security.auth.AuthPermission "*"; + permission javax.management.MBeanServerPermission "*"; + permission javax.management.MBeanPermission "*", "*"; + permission javax.management.MBeanTrustPermission "*"; +}; --- jspwiki-2.8.0.orig/debian/jspwiki.postrm +++ jspwiki-2.8.0/debian/jspwiki.postrm @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e + +if [ "$1" = purge ] && [ -e /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule + db_input critical jspwiki/purgewikifiles || true + db_go || true + db_get jspwiki/purgewikifiles || true + if [ "$RET" == "true" ]; then + cd /var/lib + rm -rf jspwiki/default + rmdir --ignore-fail-on-non-empty jspwiki + fi + db_purge +else if [[ $1 == "purge" ]]; +then + cd /var/lib + rmdir --ignore-fail-on-non-empty jspwiki/default + rmdir --ignore-fail-on-non-empty jspwiki +fi +fi + +#DEBHELPER# --- jspwiki-2.8.0.orig/debian/rules +++ jspwiki-2.8.0/debian/rules @@ -0,0 +1,90 @@ +#!/usr/bin/make -f + +export JAVA_HOME=/usr/lib/jvm/java-gcj + +patch: patch-stamp + +patch-stamp: + touch patch-stamp +# dpatch apply-all + +unpatch: +# dpatch deapply-all + rm -rf patch-stamp debian/patched + +build: build-arch build-indep +build-arch: build-stamp +build-indep: build-stamp +build-stamp: patch + dh_testdir +# mv lib lib.orig +# mkdir lib +# mv src/com/ecyrd/jspwiki/rpc/atom lib.orig/atom +# mv src/com/ecyrd/jspwiki/xmlrpc lib.orig/xmlrpc +# mv src/com/ecyrd/jspwiki/filters/PingWeblogsComFilter.java lib.orig/PingWeblogsComFilter.java + ant opened-war +# mv lib.orig/atom src/com/ecyrd/jspwiki/rpc/atom +# mv lib.orig/xmlrpc src/com/ecyrd/jspwiki/xmlrpc +# mv lib.orig/PingWeblogsComFilter.java src/com/ecyrd/jspwiki/filters/PingWeblogsComFilter.java +# rmdir lib +# mv lib.orig lib + +binary: binary-arch binary-indep + +binary-arch: + +binary-indep: build + +clean: clean-patched unpatch + +clean-patched: + dh_testdir + dh_testroot + dh_clean + JAVA_HOME=/usr/lib/jvm/default-java ant clean + +get-orig-source: + mkdir jspwiki-2.8.0 + cd jspwiki-2.8.0 + wget http://www.ecyrd.com/~jalkanen/JSPWiki/2.8.0/JSPWiki-2.8.0-src.zip + unzip JSPWiki-2.8.0-src.zip + rm JSPWiki-2.8.0-src.zip + cd .. + tar czf jspwiki_2.8.0.orig.tar.gz jspwiki-2.8.0 + rm -rf jspwiki-2.8.0 + +install: build + dh_testdir + dh_testroot + dh_prep + +binary-indep: build install + dh_testdir + dh_testroot + dh_installdirs -p jspwiki etc/jspwiki etc/tomcat6/policy.d var/lib/jspwiki var/lib/tomcat6/webapps usr/share/doc/jspwiki/examples usr/share + cp -r build/JSPWiki debian/jspwiki/usr/share/jspwiki + cp debian/jspwiki.xml debian/jspwiki/etc/jspwiki/jspwiki.xml + cp etc/groupdatabase.xml etc/userdatabase.xml debian/jspwiki/etc/jspwiki/ + rm -f debian/jspwiki/usr/share/jspwiki/WEB-INF/jspwiki.properties + + cp debian/jspwiki.properties debian/jspwiki/etc/jspwiki/jspwiki.properties + mv debian/jspwiki/usr/share/jspwiki/WEB-INF/web.xml debian/jspwiki/usr/share/jspwiki/WEB-INF/jspwiki.tld debian/jspwiki/etc/jspwiki/ + cp debian/tomcat.policy debian/jspwiki/etc/jspwiki/tomcat.policy + + dh_installdocs README doc/Compiling.txt debian/README.Debian + dh_installchangelogs ChangeLog + (cd src ; mv wikipages/en default ; tar --exclude .cvsignore --create --gzip --file ../debian/jspwiki/usr/share/doc/jspwiki/examples/wikipages.tar.gz default ; mv default wikipages/en ) + + dh_link + dh_compress + dh_fixperms + dh_installdeb + dh_installdebconf + dh_gencontrol + dh_md5sums + dh_builddeb + +binary-arch: build install + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install --- jspwiki-2.8.0.orig/debian/jspwiki.links +++ jspwiki-2.8.0/debian/jspwiki.links @@ -0,0 +1,21 @@ +etc/jspwiki/jspwiki.properties usr/share/jspwiki/WEB-INF/jspwiki.properties +etc/jspwiki/jspwiki.tld usr/share/jspwiki/WEB-INF/jspwiki.tld +etc/jspwiki/web.xml usr/share/jspwiki/WEB-INF/web.xml +etc/jspwiki/jspwiki.xml etc/tomcat6/Catalina/localhost/JSPWiki.xml +etc/jspwiki/tomcat.policy etc/tomcat6/policy.d/05jspwiki.policy +usr/share/java/log4j-1.2.jar usr/share/jspwiki/WEB-INF/lib/log4j-1.2.jar +usr/share/java/commons-codec.jar usr/share/jspwiki/WEB-INF/lib/commons-codec.jar +usr/share/java/commons-fileupload.jar usr/share/jspwiki/WEB-INF/lib/commons-fileupload.jar +usr/share/java/commons-httpclient.jar usr/share/jspwiki/WEB-INF/lib/commons-httpclient.jar +usr/share/java/commons-io.jar usr/share/jspwiki/WEB-INF/lib/commons-io.jar +usr/share/java/commons-lang.jar usr/share/jspwiki/WEB-INF/lib/commons-lang.jar +usr/share/java/jdom1.jar usr/share/jspwiki/WEB-INF/lib/jdom1.jar +usr/share/java/jaxen.jar usr/share/jspwiki/WEB-INF/lib/jaxen.jar +usr/share/java/jaxp-1.3.jar usr/share/jspwiki/WEB-INF/lib/jaxp-1.3.jar +usr/share/java/oro.jar usr/share/jspwiki/WEB-INF/lib/oro.jar +usr/share/java/oscache.jar usr/share/jspwiki/WEB-INF/lib/oscache.jar +usr/share/java/lucene-core.jar usr/share/jspwiki/WEB-INF/lib/lucene-core.jar +usr/share/java/lucene-highlighter.jar usr/share/jspwiki/WEB-INF/lib/lucene-highlighter.jar +usr/share/java/jabsorb.jar usr/share/jspwiki/WEB-INF/lib/jabsorb.jar +usr/share/tomcat6-webapps/jsp-examples/WEB-INF/lib/jstl.jar usr/share/jspwiki/WEB-INF/lib/jstl.jar +usr/share/tomcat6-webapps/jsp-examples/WEB-INF/lib/standard.jar usr/share/jspwiki/WEB-INF/lib/standard.jar --- jspwiki-2.8.0.orig/debian/jspwiki.config +++ jspwiki-2.8.0/debian/jspwiki.config @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +. /usr/share/debconf/confmodule +db_input high jspwiki/baseurl || true +db_input low jspwiki/applicationname || true +db_input low jspwiki/pageprovider || true +db_input low jspwiki/usepagecache || true +db_input low jspwiki/encoding || true +db_input low jspwiki/breaktitlewithspaces || true +db_input low jspwiki/matchenglishplurals || true +db_input low jspwiki/camelcaselinks || true +db_go || true --- jspwiki-2.8.0.orig/debian/NEWS.jspwiki +++ jspwiki-2.8.0/debian/NEWS.jspwiki @@ -0,0 +1,14 @@ +jspwiki (2.8.0-2) unstable; urgency=low + + * ATOM server support has been removed due to dependency issues. + + -- Kalle Kivimaa Sun, 26 Oct 2008 12:00:00 +0300 + +jspwiki (2.6.3-1) unstable; urgency=low + + * It is highly recommended that the users add the following parameter + to the jspwiki.properties, due to the CVE-2008-1230. The vulnerability + allows a user to cause arbitary JSP code executed on the server side. + jspwiki.attachment.forbidden=*.jsp + + -- Kalle Kivimaa Sun, 20 Jul 2008 12:00:00 +0300 --- jspwiki-2.8.0.orig/debian/jspwiki.conffiles +++ jspwiki-2.8.0/debian/jspwiki.conffiles @@ -0,0 +1 @@ +/etc/tomcat6/policy.d/05jspwiki.policy --- jspwiki-2.8.0.orig/debian/control +++ jspwiki-2.8.0/debian/control @@ -0,0 +1,23 @@ +Source: jspwiki +Section: contrib/web +Priority: optional +Maintainer: Debian Java Maintainers +Uploaders: Adnan Hodzic +Standards-Version: 3.9.3 +Build-Depends-Indep: tomcat6, java-gcj-compat-dev, liblucene2-java, liboro-java, liboscache-java, libjdom1-java, libjaxen-java, libgnujaf-java, libcommons-codec-java, libcommons-fileupload-java, libcommons-httpclient-java, libcommons-io-java, libcommons-lang-java, liblog4j1.2-java, wget, unzip +Build-Depends: ant, debhelper (>> 7) +Homepage: http://www.jspwiki.org +Vcs-Svn: svn://svn.debian.org/svn/pkg-java/trunk/jspwiki +Vcs-Browser: http://svn.debian.org/viewsvn/pkg-java/trunk/jspwiki + +Package: jspwiki +Architecture: all +Section: contrib/web +Priority: optional +Depends: ${misc:Depends}, libjabsorb-java, libcommons-codec-java, libcommons-fileupload-java, libcommons-httpclient-java, libcommons-io-java, libcommons-lang-java, libcommons-logging-java, libgnujaf-java, libjaxen-java, libjdom1-java, liblog4j1.2-java, liblucene2-java, libnekohtml-java, liboro-java, liboscache-java, tomcat6 +Suggests: rcs +Description: WikiWikiWeb clone written in Java + JSPWiki is a simple WikiWiki clone to be run in a Java servlet container + (eg. Tomcat). It keeps all of its formatting in Java Server Pages (JSP) + files, and uses a custom Java class called 'com.ecyrd.jspwiki.WikiEngine' + to all interfacing with the Wiki system. --- jspwiki-2.8.0.orig/debian/copyright +++ jspwiki-2.8.0/debian/copyright @@ -0,0 +1,146 @@ +This JSPWiki package is distributed under Apache License 2.0 +(see /usr/share/common-licenses/Apache-2.0 for details). + +Copyright © Janne Jalkanen and others, +2001-2008. + +jrcs-diff copyright © The Apache Software Foundation, distributed under +Apache License 1.1 (see below). + +Freshcookies files copyright © Andrew Jaquith. + +Akismet.java copyright © 2005-2006 David A. Czarnecki, copyright terms +below. + +mootools.js copyright © 2006 Valerio Proietti , +MIT license, license terms below. + +The Debian package is maintained by Kalle Kivimaa . + +French translation by Rémi Pannequin and +the Debian French localisation team . + +The original sources were fetched from +http://www.jspwiki.org/Wiki.jsp?page=JSPWikiDownload + +jrcs-diff available at https://svn.apache.org/repos/asf/commons/dormant/jrcs/tags/pre_ASF_2_license/src/java/org/apache/commons/jrcs/diff/ + +Freshcookies package available at +http://www.freshcookies.org/freshcookies-security/ + +Akismet package available at http://sourceforge.net/projects/akismet-java/ + +jrcs-diff: + + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + + +Akismet.java: + +Copyright (c) 2005-2006, David A. Czarnecki +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +Neither the name of the "David A. Czarnecki" nor the names of +its contributors may be used to endorse or promote products derived from +this software without specific prior written permission. +Products derived from this software may not be called "Akismet Java API", +nor may "Akismet Java API" appear in their name, without prior written +permission of David A. Czarnecki. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +mootools.js: + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. --- jspwiki-2.8.0.orig/debian/compat +++ jspwiki-2.8.0/debian/compat @@ -0,0 +1 @@ +7 --- jspwiki-2.8.0.orig/debian/jspwiki.xml +++ jspwiki-2.8.0/debian/jspwiki.xml @@ -0,0 +1,5 @@ + + + + --- jspwiki-2.8.0.orig/debian/jspwiki.postinst +++ jspwiki-2.8.0/debian/jspwiki.postinst @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e + +. /usr/share/debconf/confmodule + +# retrieve the tomcat6 user +db_get tomcat6/username && TOMCAT6_USER="$RET" || TOMCAT6_USER="tomcat6" + +cd /var/lib/jspwiki +if [[ ! -f default/Main.txt ]]; +then + tar --extract --gunzip --keep-old-files --file /usr/share/doc/jspwiki/examples/wikipages.tar.gz || true + chown -R $TOMCAT6_USER default +fi + +set +e +head -1 /etc/jspwiki/jspwiki.properties | grep -q "This file is managed by Debconf" || exit 0 +set -e + +db_get jspwiki/applicationname +APPLICATION="$RET" +db_get jspwiki/baseurl +BASEURL="$RET" +db_get jspwiki/pageprovider +PROVIDER="$RET" +db_get jspwiki/usepagecache +CACHE="$RET" +db_get jspwiki/encoding +ENCODING="$RET" +db_get jspwiki/breaktitlewithspaces +BREAKTITLE="$RET" +db_get jspwiki/matchenglishplurals +MATCHPLURALS="$RET" +db_get jspwiki/camelcaselinks +CAMELCASE="$RET" + +db_stop || true + +TEMPFILE=`tempfile` +sed -e "s/^jspwiki\.applicationName.*/jspwiki.applicationName = $APPLICATION/" -e "s!^jspwiki\.baseURL.*!jspwiki.baseURL = $BASEURL!" -e "s/^jspwiki.pageProvider.*/jspwiki.pageProvider = $PROVIDER/" -e "s/^jspwiki.usePageCache.*/jspwiki.usePageCache = $CACHE/" -e "s/^jspwiki.encoding.*/jspwiki.encoding = $ENCODING/" -e "s/^jspwiki.breakTitleWithSpaces.*/jspwiki.breakTitleWithSpaces = $BREAKTITLE/" -e "s/^jspwiki.translatorReader.matchEnglishPlurals.*/jspwiki.translatorReader.matchEnglishPlurals = $MATCHPLURALS/" -e "s/^jspwiki.translatorReader.camelCaseLinks.*/jspwiki.translatorReader.camelCaseLinks = $CAMELCASE/" /etc/jspwiki/jspwiki.properties > $TEMPFILE +mv $TEMPFILE /etc/jspwiki/jspwiki.properties +chmod go+r /etc/jspwiki/jspwiki.properties + +#DEBHELPER# --- jspwiki-2.8.0.orig/debian/changelog +++ jspwiki-2.8.0/debian/changelog @@ -0,0 +1,471 @@ +jspwiki (2.8.0-5) unstable; urgency=low + + * Team upload. + * debian/control: + - Add libjabsorb-java and tomcat6 to Depends. + - Add Vcs-svn and Vcs-Browser + - Bump Standards-Version to 3.9.3. + * debian/rules: + - Add build-arch and build-indep targets. + * Update Dutch translation debconf template nl.po (Closes: #654392) + - Thanks to Jeroen Schot + * Update Danish translation debconf template da.po (Closes: #619304) + - Thanks to Joe Dalton and Christian Perrier + * Add Polish translation debconf template pl.do (Closes: #664649) + - Thanks to Michał Kułach + + -- tony mancill Tue, 27 Mar 2012 13:43:36 -0700 + +jspwiki (2.8.0-4) unstable; urgency=low + + * Team upload. + * Changed Build-Depends + - Removed (Build-)Depends on libservlet2.3-java + (Closes: #576903, #581024) + - added tomcat6 + * Tailored package to tomcat6 instead of tomcat5.5 (Closes: #592677) + * With permission of maintainer Java Team is taking over the package + + -- Adnan Hodzic Thu, 25 Nov 2010 19:01:27 +0100 + +jspwiki (2.8.0-3.2) unstable; urgency=low + + * Non-maintainer upload. + * Fix pending l10n issues. Debconf translations: + - Spanish (Francisco Javier Cuadrado). Closes: #547625 + - Vietnamese (Clytie Siddall). Closes: #547996 + - Danish (Joe Hansen). Closes: #580408 + + -- Christian Perrier Tue, 11 May 2010 05:57:04 +0200 + +jspwiki (2.8.0-3.1) unstable; urgency=low + + * Non-maintainer upload. + * Lintian fixes: + - bump debhelper compatibility to 7 + - as a consequence, replace "dhclean -k" by dh_prep + - add ${misc:Depends} to binary package dependencies so that + debhelper-triggered dependencies are properly added + * Drop build dependency on obsolete tomcat5.5-webapps + (apparently useless) + * Drop dependency on tomcat5.5 packages. Use "Recommends: tomcat6" + instead. Closes: #526261 + * Set JAVA_HOME when calling "ant clean" to prevent FTBFS + Closes: #547485 + * Fix pending l10n issues. Debconf translations: + - German (Helge Kreutzmann). Closes: #509474 + - Portuguese (Miguel Figueiredo). Closes: #509737 + - French (Christian Perrier). Closes: #509862 + - Swedish (Martin Bagge). Closes: #511203 + - Russian (Yuri Kozlov). Closes: #529056 + - Italian (Luca Monducci). Closes: #544191 + - Czech (Miroslav Kure). Closes: #546613 + - Basque (Iñaki Larrañaga Murgoitio). Closes: #546779 + - Japanese (Hideki Yamane (Debian-JP)). Closes: #547420 + + -- Christian Perrier Sun, 20 Sep 2009 08:14:37 +0200 + +jspwiki (2.8.0-3) unstable; urgency=low + + * Removed ecs.jar symlink. Closes: #509300 + + -- Kalle Kivimaa Sun, 21 Dec 2008 10:00:00 +0300 + +jspwiki (2.8.0-2) unstable; urgency=low + + * Added baseURL setting from the installation + * wikipages.tar.gz fixed. Closes: #508828 + * Added debhelper build dependency. Closes: #507177 + * Updated the Swedish translation. Closes: #503355 + * Changed the runtime dependency from libjdom-java to libjdom1-java. + Closes: #508823 + * Fixed the license change from GPL to Apache. + * Removed XML-RPC capability for now. + * Removed ATOM capability for now. + * Included the source for freshcookies-security + * Included the source for akismet-java + * Included the source for jrcs-diff + * Fixed a typo in debian/rules to cause commons-httpclient binary + to be included. + + -- Kalle Kivimaa Fri, 19 Dec 2008 18:00:00 +0300 + +jspwiki (2.8.0-1) unstable; urgency=low + + * New upstream release. Closes: #489900 + * Moved package back to contrib as it requires jars which are + either not in Debian or in contrib. Closes: #491176 + * New release contains fixes for the CVE-2008-1229 and CVE-2008-1231. + The default properties have been changed to reject .jsp uploads, + which fixes CVE-2008-1230 (a NEWS.Debian entry has also been added). + Closes: #470477 + * control, rules and links modified to better support existing Debian + libraries. + * Added the missing jspwiki.properties + * ant build dependency moved to Build-Depends to conform to policy. + + -- Kalle Kivimaa Tue, 21 Oct 2008 12:00:00 +0300 + +jspwiki (2.5.139-1.1) unstable; urgency=low + + * Non-maintainer upload to fix pending l10n issues + * Debconf templates and debian/control reviewed by the debian-l10n- + english team as part of the Smith review project. + Closes: #450862, #452166 + * [Debconf translation updates] + - Galician. Closes: #451554 + - Portuguese. Closes: #451587 + - Finnish. Closes: #452591 + - German. Closes: #452165 + - Vietnamese. Closes: #453384 + - Dutch. Closes: #453697, #453699 + - Russian. Closes: #453717 + - Italian. Closes: #454040 + - French. Closes: #456392 + - Czech. Closes: #474618 + - Basque. Closes: #475680 + * [Lintian] Add copyright information to debian/copyright and make that + file more easily readable + * [Lintian] Convert debian/copyright to UTF-8 + + -- Christian Perrier Wed, 19 Mar 2008 20:53:33 +0100 + +jspwiki (2.5.139-1) unstable; urgency=low + + * New upstream release. Closes: #430066. + * Upstream fixed the XSS CVE's. Closes: #445477. + * Updated French translation. Closes: #435780. + * Updated German translation. Closes: #437111. + * Updated Portugese translation. Closes: #445350. + * Fixed debconf typos. Closes: #437113. + * doc/Templates.txt has been removed from the upstream. + * Fixed the tomcat.policy. + * Added the new security options to the jspwiki.properties. + * Added empty userdatabase and groupdatabase xml files. + + -- Kalle Kivimaa Sun, 21 Oct 2007 08:00:00 +0300 + +jspwiki (2.2.33.ds1-2) unstable; urgency=low + + * Depend on tomcat5.5 instead of tomcat5. + * Fixed some typos in deb debconf templates. Closes: #431880. + * Added portuguese debconf translation. Closes: #416328. + * Added german debconf translation. Closes: #431877. + * Used debian/compat instead of DH_COMPAT in debian/rules. + * Force build to use java-gcj-compat. + + -- Michael Koch Thu, 19 Jul 2007 07:16:17 +0200 + +jspwiki (2.2.33.ds1-1) unstable; urgency=low + + * Sourceful upload to correct the problem of moving the package + from contrib to main (see #397659). + * Fixed the tomcat.policy to include the correct codeBase (Closes: #386309). + + -- Kalle Kivimaa Thu, 9 Sep 2006 12:00:00 +0200 + +jspwiki (2.2.33-5) unstable; urgency=low + + * Bumped the Standards-Version from 3.6.1.0 to 3.7.2. No changes + were neccessary. + + -- Kalle Kivimaa Tue, 29 Aug 2006 12:00:00 +0300 + +jspwiki (2.2.33-4) unstable; urgency=low + + * Changed the compiler build-dep to be java-gcj-compat-dev (Closes: #384952) + + -- Kalle Kivimaa Mon, 28 Aug 2006 12:00:00 +0300 + +jspwiki (2.2.33-3) unstable; urgency=low + + * Fixed the tomcat.policy to allow Tomcat5 to run with the security + manager enabled. + * Fixed the old logging path in jspwiki.properties to be + /var/log/tomcat5/jspwiki.log + + -- Kalle Kivimaa Mon, 28 Aug 2006 08:00:00 +0300 + +jspwiki (2.2.33-2) unstable; urgency=low + + * Now depends on tomcat5 intead of tomcat4 (Closes: #334057) + * Moved to main as tomcat5 is in main too + * Updated the get-orig-source to reflect 2.2.33 + * Updated jspwiki.xml context to tomcat5 + + -- Kalle Kivimaa Thu, 17 Aug 2006 16:00:00 +0300 + +jspwiki (2.2.33-1) unstable; urgency=low + + * New upstream release. + * debconf-2.0 alternative added (Closes: #331874). + * Swedish translation included (Closes: #331075). + * Removed commons-logging-api.jar from WEB-INF/lib as it causes + tomcat4 to freeze at startup. + + -- Kalle Kivimaa Mon, 20 Dec 2005 23:00:00 +0200 + +jspwiki (2.2.20-1) unstable; urgency=low + + * New upstream release (Closes: #316707). + * Czech translation included (Closes: #319401). + + -- Kalle Kivimaa Wed, 31 Aug 2005 15:00:00 +0300 + +jspwiki (2.0.52-12) unstable; urgency=high + + * The source package is non-native again. + + -- Kalle Kivimaa Wed, 18 May 2005 16:30:00 +0300 + +jspwiki (2.0.52-11) unstable; urgency=high + + * Fixed the postinst script to handle the case of non-debconfed + configuration file (Closes: #308069) courtesy of Michael Blakeley + + -- Kalle Kivimaa Sun, 08 May 2005 22:00:00 +0300 + +jspwiki (2.0.52-10) unstable; urgency=high + + * Fixed the postinst script to correctly recognize debconf (Closes: #302198) + courtesy of Kenneth Pronovici + + -- Kalle Kivimaa Thu, 28 Apr 2005 22:00:00 +0300 + +jspwiki (2.0.52-9) unstable; urgency=high + + * Added dpatch build dependency (Closes: #296936) + * Compiled the package with JDK 1.4 instead of 1.5 (Closes: #296938) + + -- Kalle Kivimaa Mon, 28 Feb 2005 09:00:00 +0200 + +jspwiki (2.0.52-8) unstable; urgency=high + + * Added dpatch support. + * Fixed Search.jsp to plug the security hole + http://www.jspwiki.org/Wiki.jsp?page=BugXSSVulnerabilityInSearch.jsp + + -- Kalle Kivimaa Mon, 21 Feb 2005 11:00:00 +0200 + +jspwiki (2.0.52-7) unstable; urgency=low + + * Changed the postinst script to honor non-debconf changes to + /etc/jspwiki/jspwiki.properties (Closes: #238574). + * Upgraded the fr.po file as provided by the French translation + team (Closes: #238747) + * Put context configuration XML jspwiki.xml to /var/lib/tomcat4/webapps + (Closes: #248286) + + -- Kalle Kivimaa Thu, 3 Jun 2004 13:00:00 +0200 + +jspwiki (2.0.52-6) unstable; urgency=low + + * Installed the French debconf template transaltions (Closes: #235191). + + -- Kalle Kivimaa Fri, 13 Feb 2004 10:00:00 +0200 + +jspwiki (2.0.52-5) unstable; urgency=low + + * Updated the build depends and templates to conform with the Debian + localisation team standards (Closes: #232434). + * Improved the debconf extended descriptions. + + -- Kalle Kivimaa Fri, 13 Feb 2004 10:00:00 +0200 + +jspwiki (2.0.52-4) unstable; urgency=low + + * Changed debconf question priority from high/medium to low. + * Fixed incorrect version number in get-orig-source rules target. + + -- Kalle Kivimaa Thu, 22 Jan 2004 11:00:00 +0200 + +jspwiki (2.0.52-3) unstable; urgency=low + + * Removed direct support for Tomcat 3.x. + + -- Kalle Kivimaa Fri, 5 Dec 2003 12:00:00 +0200 + +jspwiki (2.0.52-2) unstable; urgency=low + + * Updated the standards field to the latest (3.6.1.0). + * Package no longer restarts Tomcat on installation. + + -- Kalle Kivimaa Sun, 19 Oct 2003 11:00:00 +0200 + +jspwiki (2.0.52-1) unstable; urgency=low + + * New upstream version. + * Maintainer address changed to killer@debian.org. + * Package sent to the Debian archive (Closes: #180340). + + -- Kalle Kivimaa Tue, 14 Oct 2003 15:00:00 +0200 + +jspwiki (2.0.49-0) unstable; urgency=low + + * New upstream version. + + -- Kalle Kivimaa Thu, 17 Jul 2003 10:00:00 +0200 + +jspwiki (2.0.45-0) unstable; urgency=low + + * New upstream version. + * Changed the version numbering to conform with the unofficial Debian + package policy. + + -- Kalle Kivimaa Wed, 4 Jun 2003 18:00:00 +0200 + +jspwiki (2.0.39-3) unstable; urgency=low + + * Added rudimentary debconf support. + * Started using dh_link in debian/rules. + * Moved jspwiki.tld to /etc/jspwiki. + * Changed the JSPWiki homepage to http://www.jspwiki.org + + -- Kalle Kivimaa Sat, 3 May 2003 18:00:00 +0200 + +jspwiki (2.0.39-2) unstable; urgency=low + + * Fixed binary* build targets to conform with policy. + * Added get-orig-source build target. + * Fixed versioning problems. + * Fixed the jspwiki.tld problem (wrong version in the build, took the + upstream fix). + + -- Kalle Kivimaa Mon, 21 Apr 2003 11:00:00 +0200 + +jspwiki (2.0.39-1) unstable; urgency=low + + * New upstream version. + + -- Kalle Kivimaa Sat, 12 Apr 2003 12:01:00 +0200 + +jspwiki (2.0.36-1) unstable; urgency=low + + * New upstream version. + + -- Kalle Kivimaa Sun, 23 Mar 2003 17:47:00 +0200 + +jspwiki (2.0.32-2) unstable; urgency=low + + * Packaging updates. + + -- Kalle Kivimaa Sat, 21 Feb 2003 09:20:00 +0200 + +jspwiki (2.0.32-1) unstable; urgency=low + + * New upstream version. + + -- Kalle Kivimaa Sat, 21 Feb 2003 09:20:00 +0200 + +jspwiki (2.0.31-1) unstable; urgency=low + + * New upstream version. + + -- Kalle Kivimaa Fri, 21 Feb 2003 09:20:00 +0200 + +jspwiki (2.0.27-2) unstable; urgency=low + + * Fixed packaging so that lintian and linda don't complain about + anything else except configuration files being misplaced. + + -- Kalle Kivimaa Sun, 09 Feb 2003 16:04:00 +0200 + +jspwiki (2.0.27-1) unstable; urgency=low + + * New upstream version. + * Packaging now (almost) conforms to Debian policy. + + -- Kalle Kivimaa Sun, 09 Feb 2003 13:50:00 +0200 + +jspwiki (2.0.25-3) unstable; urgency=low + + * Fixed various permissions from root to tomcat4. + * Fixed file layout to conform with Debian policy. + + -- Kalle Kivimaa Sat, 08 Feb 2003 18:46:00 +0200 + +jspwiki (2.0.25-2) unstable; urgency=low + + * Fixed the various scripts so that now the Debian specific stuff + really is only under debian/. + + -- Kalle Kivimaa Mon, 03 Feb 2003 13:17:00 +0200 + +jspwiki (2.0.25-1) unstable; urgency=low + + * Changed the packaging system to real Debian system. + + -- Kalle Kivimaa Sun, 02 Feb 2003 23:25:00 +0200 +jspwiki (2.5.139-1.2) unstable; urgency=low + + * Non-maintainer upload to fix pending l10n issues + * Debconf templates and debian/control reviewed by the debian-l10n- + english team as part of the Smith review project. + Closes: #450862, #452166 + * [Debconf translation updates] + - Galician. Closes: #451554 + - Portuguese. Closes: #451587 + - Finnish. Closes: #452591 + - German. Closes: #452165 + - Vietnamese. Closes: #453384 + - Dutch. Closes: #453697, #453699 + - Russian. Closes: #453717 + - Italian. Closes: #454040 + - French. Closes: #456392 + * [Lintian] Add copyright information to debian/copyright and make that + file more easily readable + * [Lintian] Convert debian/copyright to UTF-8 + * Czech. Closes: #474618 + + -- Christian Perrier Mon, 07 Apr 2008 07:24:59 +0200 +jspwiki (2.5.139-1.2) UNRELEASED; urgency=low + + * Non-maintainer upload to fix pending l10n issues + * Debconf templates and debian/control reviewed by the debian-l10n- + english team as part of the Smith review project. + Closes: #450862, #452166 + * [Debconf translation updates] + - Galician. Closes: #451554 + - Portuguese. Closes: #451587 + - Finnish. Closes: #452591 + - German. Closes: #452165 + - Vietnamese. Closes: #453384 + - Dutch. Closes: #453697, #453699 + - Russian. Closes: #453717 + - Italian. Closes: #454040 + - French. Closes: #456392 + * [Lintian] Add copyright information to debian/copyright and make that + file more easily readable + * [Lintian] Convert debian/copyright to UTF-8 + * Czech. Closes: #474618 + + -- Christian Perrier Mon, 07 Apr 2008 07:25:35 +0200 +jspwiki (2.5.139-1.2) unstable; urgency=low + + * Non-maintainer upload to fix pending l10n issues + * Debconf templates and debian/control reviewed by the debian-l10n- + english team as part of the Smith review project. + Closes: #450862, #452166 + * [Debconf translation updates] + - Galician. Closes: #451554 + - Portuguese. Closes: #451587 + - Finnish. Closes: #452591 + - German. Closes: #452165 + - Vietnamese. Closes: #453384 + - Dutch. Closes: #453697, #453699 + - Russian. Closes: #453717 + - Italian. Closes: #454040 + - French. Closes: #456392 + * [Lintian] Add copyright information to debian/copyright and make that + file more easily readable + * [Lintian] Convert debian/copyright to UTF-8 + * Czech. Closes: #474618 + + -- Christian Perrier Mon, 07 Apr 2008 07:27:51 +0200 +jspwiki (2.5.139-1.3) unstable; urgency=high + + * Non-maintainer upload. + * Czech. Closes: #474618 + * Czech. Closes: #474618 + + -- Christian Perrier Mon, 07 Apr 2008 07:28:10 +0200 --- jspwiki-2.8.0.orig/debian/jspwiki.properties +++ jspwiki-2.8.0/debian/jspwiki.properties @@ -0,0 +1,824 @@ +# This file is managed by Debconf. Remove this line if you don't want that!! +# If you leave the above line intact you risk losing your changes if you +# manually edit this file. YOU HAVE BEEN WARNED! Also, be aware that the +# first line must be the first line in this file for the Debconf to keep +# configuring this file. +# +########################################################################### +# +# This is the JSPWiki configuration file. You'll need to edit this +# a bit. The first few lines are the most important ones. +# +# Wherever it is said that an option can be "true" or "false", you can +# also use "yes"/"no", or "on/off". Just for some convenience. +# +# +# You can use this to override the default application name. It affects +# the HTML titles and logging, for example. It can be different from +# the actual web name (http://my.com/mywiki) of the application, but usually +# it is the same. +# +jspwiki.applicationName = JSPWiki + +# +# Which page provider class to use. Possibilities are: +# +# RCSFileProvider - for simple RCS-based file storage +# FileSystemProvider - for simple pure file storage with no version information +# VersioningFileProvider - for simple, non-RCS based versioning storage. +# +# Note that if you're upgrading from JSPWiki 1.x, then you need to remove the +# "com.ecyrd.jspwiki." part from the beginning of the path. +# +jspwiki.pageProvider = FileSystemProvider + +# +# Set to true, if you want to cache page data into memory. This is +# in general a good idea. +# +# Default is false (no cache). +# +# NB: This replaces the JSPWiki 1.x "CachingProvider" setting, since it +# probably was too confusing. +# +jspwiki.usePageCache = true + +# +# Define the time period for page cache checks in milliseconds. +# +#jspwiki.cachingProvider.cacheCheckInterval = 100000 + +# +# Determines where wiki files are kept for FileSystemProvider +# and RCSFileProvider +# +# If you're using Windows, then you must duplicate the backslashes. +# For example, use: +# +# jspwiki.fileSystemProvider.pageDir = C:\\Data\\jspwiki +# +jspwiki.fileSystemProvider.pageDir = /var/lib/jspwiki/default/en + +# +# The JSPWiki working directory. If not set, a temporary path will +# be used. You can see the location of the workdir in the logs. +# It is HIGHLY recommended that you set this. +# +# The working directory is used to cache things like Lucene search +# results. +# +#jspwiki.workDir = + +# +# ATTACHMENTS: +# +# Use the following property to define which attachment provider +# you want to use. You have basically two choices: +# * BasicAttachmentProvider - a simple, flat file, versioning provider +# * nothing - Set this property to empty, and the attachment functionality +# is disabled. +# +jspwiki.attachmentProvider = BasicAttachmentProvider + +# +# The BasicAttachmentProvider needs to know where to store the files +# the user has uploaded. It's okay to put these in the same directory +# as you put your text files (i.e. the pageDir setting above). +# +# If you're using Windows, then you must duplicate the backslashes. +# For example, use: +# +# jspwiki.basicAttachmentProvider.storageDir = C:\\Data\\jspwiki +# +jspwiki.basicAttachmentProvider.storageDir = /var/lib/jspwiki/default/en + +# +# You can tell the BasicAttachmentProvider to add a flag +# so that browsers do not cache certain (or all) attachment +# types. This is useful in intranet environments. You should activate +# this if your users complain that their excel files are not uploaded +# correctly and they still do have an old version: Usually the +# file was uploaded correctly, but they get the locally cached version +# +# You can use regular expressions to disable the cache, e.g the +# following example will disable browser cache for all excel and word files +# +# If you don't define this property, cache is enabled by default for +# all attachments +# +# jspwiki.basicAttachmentProvider.disableCache = .*\.xls|.*\.doc + +# +# You can limit the maximum size of an attachment by setting this +# value. The value is in bytes, and by default all attachments +# are accepted. +# +# The following line would limit the attachment size to 100,000 bytes +#jspwiki.attachment.maxsize=100000 + +# Don't allow JSP files to be uploaded. This represents a possible +# security issue. +jspwiki.attachment.forbidden=*.jsp + +# +# page Diff Representation +# +# To show differences between page versions, you can define a +# difference provider. +# The following choices are available: +# * TraditionalDiffProvider - Uses internal (java) diff +# to create a list of changes and shows it line by +# line colored. This is the default +# * ContextualDiffProvider - Uses internal (java) diff +# to create changes inline and shows it on a word by +# word basis using CSS. This is much superior to the +# traditional diff provider, however, it is still quite +# new and not much tested. YMMV. +# * ExternalDiffProvider - uses a system diff program (which +# can be configured using "jspwiki.diffCommand") to +# create an unified (!) diff. +# +# Example for a diff command: +# jspwiki.diffCommand = /usr/bin/diff -u %s1 %s2 +# +jspwiki.diffProvider = TraditionalDiffProvider + +# +# BaseURL can be used to rewrite all of JSPWiki's internal references. +# Sometimes, especially if you're behind a address-rewriting firewall, +# relative URLs don't work since the servlet container has no idea +# where it's actually located. +# +# Leave undefined if you want to rely on what your servlet container +# thinks of where your application lives. +# +# You MUST define this one if you want to enable RSS (see below). In +# general, this is a good idea to define it anyway. Do not forget the +# trailing slash. +# +# Example: +# jspwiki.baseURL = http://www.ecyrd.com/JSPWiki/ +# +jspwiki.baseURL= + +# Determines if you need to have relative urls or not. If the baseURL +# is not set, then this has no effect, but if you set the baseURL (which +# is highly recommended), you can use this to set relative urls. +# +# Possible values are "absolute" and "relative". +# +#jspwiki.referenceStyle=relative + +# +# Determines which character encoding JSPWiki should use. If you want +# to support all languages in your Wiki, you probably want to enable +# this. If you're upgrading, or are planning just to use the ISO-Latin1 +# character set (like most western people would), you can just leave +# it at the default. If you enable it, remember that most people won't +# be able to type in special characters anyway. +# +# Note that you can't switch these in the mean time, since the way the +# files are encoded on disk is incompatible between ISO-Latin1 and UTF-8. +# Don't try. You'll get all sorts of interesting problems, if you do. +# +# Possible values are 'ISO-8859-1' (default) and 'UTF-8'. + +jspwiki.encoding = UTF-8 + +# +# Determines whether raw HTML is allowed as Wiki input. +# +# THIS IS A DANGEROUS OPTION! +# +# If you decide to allow raw HTML, understand that ANY person who has +# access to your Wiki site can embed ANY sort of malicious JavaScript, +# or plugin, or ActiveX, or whatever on your site. They can even mess it +# up so royally it is impossible for you to replace the situation without +# the need of direct access to the repository. So think twice before +# allowing raw HTML on your own site. +# +# Most probably you want to use this on Intranets, or personal servers, +# where only a handful of people can access the wiki. +# +# Text between {{{ and }}} -options is not affected by this setting, so +# it's always safe to quote HTML code with those. +# +# The default for this option is "false". +# +jspwiki.translatorReader.allowHTML = false + +############################################################################ +# +# Usability niceties. +# +# +# If this property is set to "true", then page titles are rendered +# using an extra space between every capital letter. It may make +# page titles readable on some occasions, but it does have the +# drawback of breaking acronyms, for example. (RSSFile becomes R S S File). +# +jspwiki.breakTitleWithSpaces = false + +# +# If set to true, this property means that "WikiName" and "WikiNames" +# are considered equal when linking between them. Setting this to +# true does not prevent you from having both kinds of pages - we just +# fall back to the other one if the primary name does not exist. +# +# For any other language, you'll probably want to turn this off. +# +jspwiki.translatorReader.matchEnglishPlurals = true + +# +# If you set this to true, the Wiki translator will then also consider +# "traditional" WikiNames (that is, names of pages JustSmashedTogether +# without square brackets) as hyperlinks. This technique is also +# known as "CamelCase", or "BumpyCase", or "InterCapping". I personally +# like CamelCase as a word, which is why this property is named as it is :-). +# +# By default this is false, since traditional WikiLinks may confuse newbies. +# +jspwiki.translatorReader.camelCaseLinks = false + +# +# This sets the default template used by the Wiki engine. The templates +# live in templates/