001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.xmlrpc;
021:
022: import java.io.UnsupportedEncodingException;
023: import java.util.*;
024:
025: import org.apache.log4j.Logger;
026: import org.apache.xmlrpc.XmlRpcException;
027:
028: import com.ecyrd.jspwiki.*;
029: import com.ecyrd.jspwiki.attachment.Attachment;
030: import com.ecyrd.jspwiki.auth.permissions.PagePermission;
031: import com.ecyrd.jspwiki.auth.permissions.PermissionFactory;
032:
033: /**
034: * Provides handlers for all RPC routines.
035: *
036: * @author Janne Jalkanen
037: * @since 1.6.6
038: */
039: // We could use WikiEngine directly, but because of introspection it would
040: // show just too many methods to be safe.
041: public class RPCHandler extends AbstractRPCHandler {
042: Logger log = Logger.getLogger(RPCHandler.class);
043:
044: public void initialize(WikiContext ctx) {
045: super .initialize(ctx);
046: }
047:
048: /**
049: * Converts Java string into RPC string.
050: */
051: private String toRPCString(String src) {
052: return TextUtil.urlEncodeUTF8(src);
053: }
054:
055: /**
056: * Converts RPC string (UTF-8, url encoded) into Java string.
057: */
058: private String fromRPCString(String src) {
059: return TextUtil.urlDecodeUTF8(src);
060: }
061:
062: /**
063: * Transforms a Java string into UTF-8.
064: */
065: private byte[] toRPCBase64(String src) {
066: try {
067: return src.getBytes("UTF-8");
068: } catch (UnsupportedEncodingException e) {
069: //
070: // You shouldn't be running JSPWiki on a platform that does not
071: // use UTF-8. We revert to platform default, so that the other
072: // end might have a chance of getting something.
073: //
074: log
075: .fatal("Platform does not support UTF-8, reverting to platform default");
076: return src.getBytes();
077: }
078: }
079:
080: public String getApplicationName() {
081: checkPermission(PagePermission.VIEW);
082: return toRPCString(m_engine.getApplicationName());
083: }
084:
085: public Vector getAllPages() {
086: checkPermission(PagePermission.VIEW);
087: Collection pages = m_engine.getRecentChanges();
088: Vector result = new Vector();
089:
090: for (Iterator i = pages.iterator(); i.hasNext();) {
091: WikiPage p = (WikiPage) i.next();
092: if (!(p instanceof Attachment)) {
093: result.add(toRPCString(p.getName()));
094: }
095: }
096:
097: return result;
098: }
099:
100: /**
101: * Encodes a single wiki page info into a Hashtable.
102: */
103: protected Hashtable encodeWikiPage(WikiPage page) {
104: Hashtable ht = new Hashtable();
105:
106: ht.put("name", toRPCString(page.getName()));
107:
108: Date d = page.getLastModified();
109:
110: //
111: // Here we reset the DST and TIMEZONE offsets of the
112: // calendar. Unfortunately, I haven't thought of a better
113: // way to ensure that we're getting the proper date
114: // from the XML-RPC thingy, except to manually adjust the date.
115: //
116:
117: Calendar cal = Calendar.getInstance();
118: cal.setTime(d);
119: cal.add(Calendar.MILLISECOND,
120: -(cal.get(Calendar.ZONE_OFFSET) + (cal.getTimeZone()
121: .inDaylightTime(d) ? cal
122: .get(Calendar.DST_OFFSET) : 0)));
123:
124: ht.put("lastModified", cal.getTime());
125: ht.put("version", new Integer(page.getVersion()));
126:
127: if (page.getAuthor() != null) {
128: ht.put("author", toRPCString(page.getAuthor()));
129: }
130:
131: return ht;
132: }
133:
134: public Vector getRecentChanges(Date since) {
135: checkPermission(PagePermission.VIEW);
136: Collection pages = m_engine.getRecentChanges();
137: Vector result = new Vector();
138:
139: Calendar cal = Calendar.getInstance();
140: cal.setTime(since);
141:
142: //
143: // Convert UTC to our time.
144: //
145: cal.add(Calendar.MILLISECOND,
146: (cal.get(Calendar.ZONE_OFFSET) + (cal.getTimeZone()
147: .inDaylightTime(since) ? cal
148: .get(Calendar.DST_OFFSET) : 0)));
149: since = cal.getTime();
150:
151: for (Iterator i = pages.iterator(); i.hasNext();) {
152: WikiPage page = (WikiPage) i.next();
153:
154: if (page.getLastModified().after(since)
155: && !(page instanceof Attachment)) {
156: result.add(encodeWikiPage(page));
157: }
158: }
159:
160: return result;
161: }
162:
163: /**
164: * Simple helper method, turns the incoming page name into
165: * normal Java string, then checks page condition.
166: *
167: * @param pagename Page Name as an RPC string (URL-encoded UTF-8)
168: * @return Real page name, as Java string.
169: * @throws XmlRpcException, if there is something wrong with the page.
170: */
171: private String parsePageCheckCondition(String pagename)
172: throws XmlRpcException {
173: pagename = fromRPCString(pagename);
174:
175: if (!m_engine.pageExists(pagename)) {
176: throw new XmlRpcException(ERR_NOPAGE, "No such page '"
177: + pagename + "' found, o master.");
178: }
179:
180: WikiPage p = m_engine.getPage(pagename);
181:
182: checkPermission(PermissionFactory.getPagePermission(p,
183: PagePermission.VIEW_ACTION));
184:
185: return pagename;
186: }
187:
188: public Hashtable getPageInfo(String pagename)
189: throws XmlRpcException {
190: pagename = parsePageCheckCondition(pagename);
191: return encodeWikiPage(m_engine.getPage(pagename));
192: }
193:
194: public Hashtable getPageInfoVersion(String pagename, int version)
195: throws XmlRpcException {
196: pagename = parsePageCheckCondition(pagename);
197:
198: return encodeWikiPage(m_engine.getPage(pagename, version));
199: }
200:
201: public byte[] getPage(String pagename) throws XmlRpcException {
202: pagename = parsePageCheckCondition(pagename);
203:
204: String text = m_engine.getPureText(pagename, -1);
205:
206: return toRPCBase64(text);
207: }
208:
209: public byte[] getPageVersion(String pagename, int version)
210: throws XmlRpcException {
211: pagename = parsePageCheckCondition(pagename);
212:
213: return toRPCBase64(m_engine.getPureText(pagename, version));
214: }
215:
216: public byte[] getPageHTML(String pagename) throws XmlRpcException {
217: pagename = parsePageCheckCondition(pagename);
218:
219: return toRPCBase64(m_engine.getHTML(pagename));
220: }
221:
222: public byte[] getPageHTMLVersion(String pagename, int version)
223: throws XmlRpcException {
224: pagename = parsePageCheckCondition(pagename);
225:
226: return toRPCBase64(m_engine.getHTML(pagename, version));
227: }
228:
229: public Vector listLinks(String pagename) throws XmlRpcException {
230: pagename = parsePageCheckCondition(pagename);
231:
232: WikiPage page = m_engine.getPage(pagename);
233: String pagedata = m_engine.getPureText(page);
234:
235: LinkCollector localCollector = new LinkCollector();
236: LinkCollector extCollector = new LinkCollector();
237: LinkCollector attCollector = new LinkCollector();
238:
239: WikiContext context = new WikiContext(m_engine, page);
240: context.setVariable(WikiEngine.PROP_REFSTYLE, "absolute");
241:
242: m_engine.textToHTML(context, pagedata, localCollector,
243: extCollector, attCollector);
244:
245: Vector result = new Vector();
246:
247: //
248: // Add local links.
249: //
250: for (Iterator i = localCollector.getLinks().iterator(); i
251: .hasNext();) {
252: String link = (String) i.next();
253: Hashtable ht = new Hashtable();
254: ht.put("page", toRPCString(link));
255: ht.put("type", LINK_LOCAL);
256:
257: //
258: // FIXME: This is a kludge. The link format should really be queried
259: // from the TranslatorReader itself. Also, the link format should probably
260: // have information on whether the page exists or not.
261: //
262:
263: //
264: // FIXME: The current link collector interface is not very good, since
265: // it causes this.
266: //
267:
268: if (m_engine.pageExists(link)) {
269: ht.put("href", context.getURL(WikiContext.VIEW, link));
270: } else {
271: ht.put("href", context.getURL(WikiContext.EDIT, link));
272: }
273:
274: result.add(ht);
275: }
276:
277: //
278: // Add links to inline attachments
279: //
280: for (Iterator i = attCollector.getLinks().iterator(); i
281: .hasNext();) {
282: String link = (String) i.next();
283:
284: Hashtable ht = new Hashtable();
285:
286: ht.put("page", toRPCString(link));
287: ht.put("type", LINK_LOCAL);
288: ht.put("href", context.getURL(WikiContext.ATTACH, link));
289:
290: result.add(ht);
291: }
292:
293: //
294: // External links don't need to be changed into XML-RPC strings,
295: // simply because URLs are by definition ASCII.
296: //
297:
298: for (Iterator i = extCollector.getLinks().iterator(); i
299: .hasNext();) {
300: String link = (String) i.next();
301:
302: Hashtable ht = new Hashtable();
303:
304: ht.put("page", link);
305: ht.put("type", LINK_EXTERNAL);
306: ht.put("href", link);
307:
308: result.add(ht);
309: }
310:
311: return result;
312: }
313: }
|