File Source: RollerAtomHandler.java
/*
P/P * Method: org.apache.roller.weblogger.webservices.atomprotocol.RollerAtomHandler$1__static_init
*/
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. The ASF licenses this file to You
4 * under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License. For additional information regarding
15 * copyright in this work, please see the NOTICE file in the top level
16 * directory of this distribution.
17 */
18 package org.apache.roller.weblogger.webservices.atomprotocol;
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.InputStream;
23 import java.sql.Timestamp;
24 import java.util.ArrayList;
25 import java.util.Date;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.StringTokenizer;
29 import java.util.Collections;
30 import javax.activation.MimetypesFileTypeMap;
31 import javax.servlet.http.HttpServletRequest;
32 import org.apache.commons.codec.binary.Base64;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.roller.weblogger.business.FileManager;
36 import org.apache.roller.weblogger.business.FileNotFoundException;
37 import org.apache.roller.weblogger.business.FilePathException;
38 import org.apache.roller.weblogger.business.Weblogger;
39 import org.apache.roller.weblogger.business.WebloggerFactory;
40 import org.apache.roller.weblogger.pojos.User;
41 import org.apache.roller.weblogger.pojos.WeblogPermission;
42 import org.apache.roller.weblogger.pojos.WeblogCategory;
43 import org.apache.roller.weblogger.pojos.WeblogEntry;
44 import org.apache.roller.weblogger.pojos.Weblog;
45 import org.apache.roller.weblogger.util.Utilities;
46 import org.apache.roller.weblogger.util.WSSEUtilities;
47 import com.sun.syndication.feed.atom.Content;
48 import com.sun.syndication.feed.atom.Category;
49 import com.sun.syndication.feed.atom.Entry;
50 import com.sun.syndication.feed.atom.Feed;
51 import com.sun.syndication.feed.atom.Link;
52 import com.sun.syndication.feed.atom.Person;
53 import java.text.SimpleDateFormat;
54 import java.util.Comparator;
55 import java.util.Map;
56 import java.util.SortedSet;
57 import java.util.TreeSet;
58 import java.util.UUID;
59 import javax.activation.FileTypeMap;
60 import org.apache.commons.lang.StringUtils;
61 import org.apache.roller.weblogger.WebloggerException;
62 import org.apache.roller.weblogger.business.FileIOException;
63 import org.apache.roller.weblogger.business.URLStrategy;
64 import org.apache.roller.weblogger.business.UserManager;
65 import org.apache.roller.weblogger.config.WebloggerConfig;
66 import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
67 import org.apache.roller.weblogger.business.WeblogManager;
68 import org.apache.roller.weblogger.business.search.IndexManager;
69 import org.apache.roller.weblogger.pojos.RuntimeConfigProperty;
70 import org.apache.roller.weblogger.pojos.WeblogEntryTag;
71 import org.apache.roller.weblogger.pojos.ThemeResource;
72 import org.apache.roller.weblogger.util.cache.CacheManager;
73
74 /**
75 * Weblogger's ROME-based Atom Protocol implementation.
76 *
77 * Each Weblogger workspace has two collections, one that accepts entries and
78 * that accepts everything. The entries collection represents the weblog
79 * entries in a single weblog and the everything collection represents that
80 * weblog's uploaded-files.
81 *
82 * Here are the APP URIs suppored by Weblogger:
83 *
84 * <pre>
85 * /roller-services/app
86 * Introspection doc
87 *
88 * /roller-services/app/[weblog-handle>/entries
89 * Entry collection for a blog
90 *
91 * /roller-services/app/[weblog-handle]/entries/[offset]
92 * Entry collection for a blog, with offset
93 *
94 * /roller-services/app/[weblog-handle]/entry/[id]
95 * Individual entry (i.e. edit URI)
96 *
97 * /roller-services/app/[weblog-handle]/resources
98 * Resource (i.e. file-uploads) collection for a blog
99 *
100 * /roller-services/app/[weblog-handle]/resources/[offset]
101 * Resource collection for a blog, with offset
102 *
103 * /roller-services/app/[weblog-handle]/resource/*.media-link[name]
104 * Individual resource metadata (i.e. edit URI)
105 *
106 * /roller-services/app/[weblog-handle]/resource/[name]
107 * Individual resource data (i.e. media-edit URI)
108 *
109 * </pre>
110 *
111 * @author David M Johnson
112 */
113 public class RollerAtomHandler implements AtomHandler {
114 private Weblogger roller;
115 private User user;
116 private int maxEntries = 20;
117
118 private final String atomURL;
119
/*
P/P * Method: org.apache.roller.weblogger.webservices.atomprotocol.RollerAtomHandler__static_init
*
* Presumptions:
* org.apache.commons.logging.LogFactory:getFactory(...)@122 != null
*
* Postconditions:
* init'ed(log)
* init'ed(throttle)
*/
120 private static boolean throttle = true;
121
122 private static Log log =
123 LogFactory.getFactory().getInstance(RollerAtomHandler.class);
124
125 static {
126 throttle = WebloggerConfig
127 .getBooleanProperty("webservices.atomprotocol.oneSecondThrottle");
128 }
129
130 //---------------------------------------------------------------- construction
131
132 /**
133 * Create Atom handler for a request and attempt to authenticate user.
134 * If user is authenticated, then getAuthenticatedUsername() will return
135 * then user's name, otherwise it will return null.
136 */
/*
P/P * Method: void org.apache.roller.weblogger.webservices.atomprotocol.RollerAtomHandler(HttpServletRequest)
*
* Preconditions:
* (soft) log != null
* (soft) request != null
*
* Presumptions:
* org.apache.roller.weblogger.business.Weblogger:getUrlStrategy(...)@151 != null
* org.apache.roller.weblogger.business.Weblogger:getUserManager(...)@145 != null
* org.apache.roller.weblogger.business.WebloggerFactory:getWeblogger(...)@138 != null
* org.apache.roller.weblogger.business.WebloggerFactory:getWeblogger(...)@151 != null
*
* Postconditions:
* init'ed(this.atomURL)
* this.maxEntries == 20
* (soft) this.roller != null
* init'ed(this.user)
*/
137 public RollerAtomHandler(HttpServletRequest request) {
138 roller = WebloggerFactory.getWeblogger();
139
140 // TODO: decide what to do about authentication, is WSSE going to fly?
141 //String userName = authenticateWSSE(request);
142 String userName = authenticateBASIC(request);
143 if (userName != null) {
144 try {
145 this.user = roller.getUserManager().getUserByUserName(userName);
146 } catch (Exception neverHappen) {
147 log.debug("Getting user", neverHappen);
148 }
149 }
150
151 atomURL = WebloggerFactory.getWeblogger().getUrlStrategy().getAtomProtocolURL(true);
152 }
153
154 /**
155 * Return weblogHandle of authenticated user or null if there is none.
156 */
157 public String getAuthenticatedUsername() {
/*
P/P * Method: String getAuthenticatedUsername()
*
* Preconditions:
* init'ed(this.user)
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* this.user: Addr_Set{null}, Inverse{null}
*/
158 String ret = null;
159 if (this.user != null) {
160 ret = user.getUserName();
161 }
162 return ret;
163 }
164
165 //---------------------------------------------------------------- introspection
166
167 /**
168 * Return Atom service document for site, getting blog-name from pathInfo.
169 * The workspace will contain collections for entries, categories and resources.
170 */
171 public AtomService getIntrospection() throws AtomException {
/*
P/P * Method: AtomService getIntrospection()
*
* Preconditions:
* log != null
* this.roller != null
* init'ed(this.user)
*
* Presumptions:
* dirs.length@239 <= 232-1
* java.lang.Boolean.TRUE != null
* java.util.Iterator:next(...)@188 != null
* java.util.Iterator:next(...)@211 != null
* org.apache.roller.weblogger.business.FileManager:getDirectories(...)@239 != null
* ...
*
* Postconditions:
* return_value == &new AtomService(getIntrospection#1)
* new ArrayList(AtomService#1) num objects == 1
* new AtomService(getIntrospection#1) num objects == 1
* new AtomService(getIntrospection#1).workspaces == &new ArrayList(AtomService#1)
*
* Test Vectors:
* java.lang.Boolean:equals(...)@191: {1}, {0}
* java.util.Iterator:hasNext(...)@187: {0}, {1}
* java.util.Iterator:hasNext(...)@210: {0}, {1}
* org.apache.roller.weblogger.business.UserManager:getAllPermissions(...)@176: Addr_Set{null}, Inverse{null}
*/
172 log.debug("Entering");
173 AtomService service = new AtomService();
174 List perms = null;
175 try {
176 perms = roller.getUserManager().getAllPermissions(user);
177 } catch (WebloggerException re) {
178 throw new AtomException("Getting user's weblogs", re);
179 }
+ 180 List uploadAccepts = new ArrayList();
181 try {
182 uploadAccepts = getAcceptedContentTypeRange();
183 } catch (WebloggerException re) {
184 throw new AtomException("Getting site's accept range", re);
185 }
186 if (perms != null) {
187 for (Iterator iter=perms.iterator(); iter.hasNext();) {
188 WeblogPermission perm = (WeblogPermission)iter.next();
189
190 // only include weblog's that have client API support enabled
191 if (!Boolean.TRUE.equals(perm.getWebsite().getEnableBloggerApi())) continue;
192
193 // Create workspace to represent weblog
194 Workspace workspace = new Workspace(
195 Utilities.removeHTML(perm.getWebsite().getName()), "text");
196 service.addWorkspace(workspace);
197
198 // Create collection for entries within that workspace
199 String handle = perm.getWebsite().getHandle();
200 Collection entryCol = new Collection("Weblog Entries", "text",
201 atomURL+"/"+handle+"/entries");
202 entryCol.addAccept("application/atom+xml;type=entry");
203 try {
204 // Add fixed categories using scheme that points to
205 // weblog because categories are weblog specific
206 Categories cats = new Categories();
207 cats.setFixed(true);
208 cats.setScheme(getWeblogCategoryScheme(perm.getWebsite()));
209 List rollerCats = roller.getWeblogManager().getWeblogCategories(perm.getWebsite(), false);
210 for (Iterator it = rollerCats.iterator(); it.hasNext();) {
211 WeblogCategory rollerCat = (WeblogCategory)it.next();
212 Category cat = new Category();
213 cat.setTerm(rollerCat.getPath().substring(1));
214 cat.setLabel(rollerCat.getName());
215 cats.addCategory(cat);
216 }
217 entryCol.addCategories(cats);
218
219 // Add tags as free-form categories using scheme that points
220 // to site because tags can be considered site-wide
221 Categories tags = new Categories();
222 tags.setFixed(false);
223 entryCol.addCategories(tags);
224
225 } catch (Exception e) {
226 throw new AtomException("Fetching weblog categories");
227 }
228 workspace.addCollection(entryCol);
229
230 // Add media collection for upload dir
231 Collection uploadCol = new Collection("Media Files", "text",
232 atomURL+"/"+handle+"/resources/");
233 uploadCol.setAccepts(uploadAccepts);
234 workspace.addCollection(uploadCol);
235
236 // And add one media collection for each of weblog's upload sub-directories
237 ThemeResource[] dirs;
238 try {
239 dirs = roller.getFileManager().getDirectories(perm.getWebsite());
240 for (int i=0; i<dirs.length; i++) {
+ 241 Collection uploadSubCol = new Collection(
242 "Media Files: " + dirs[i].getPath(), "text",
243 atomURL+"/"+handle+"/resources/" + dirs[i].getPath());
244 uploadSubCol.setAccepts(uploadAccepts);
245 workspace.addCollection(uploadSubCol);
246 }
247 } catch (FilePathException fpe) {
248 throw new AtomException("Getting uploads directories information", fpe);
249 } catch (FileNotFoundException fnfe) {
250 throw new AtomException("Getting uploads directories information", fnfe);
251 }
252
253 }
254 }
255 log.debug("Exiting");
256 return service;
257 }
258
259 /**
260 * Build accept range by taking things that appear to be content-type rules
261 * from site's file-upload allowed extensions.
262 */
263 private List getAcceptedContentTypeRange() throws WebloggerException {
/*
P/P * Method: List getAcceptedContentTypeRange()
*
* Presumptions:
* java.util.Map:get(...)@267 != null
* org.apache.commons.lang.StringUtils:split(...)@268 != null
* org.apache.roller.weblogger.business.PropertiesManager:getProperties(...)@266 != null
* org.apache.roller.weblogger.business.Weblogger:getPropertiesManager(...)@266 != null
* org.apache.roller.weblogger.business.WebloggerFactory:getWeblogger(...)@265 != null
* ...
*
* Postconditions:
* return_value == &new ArrayList(getAcceptedContentTypeRange#1)
* new ArrayList(getAcceptedContentTypeRange#1) num objects == 1
*/
264 List accepts = new ArrayList();
265 Weblogger roller = WebloggerFactory.getWeblogger();
266 Map config = roller.getPropertiesManager().getProperties();
267 String allows = ((RuntimeConfigProperty)config.get("uploads.types.allowed")).getValue();
268 String[] rules = StringUtils.split(StringUtils.deleteWhitespace(allows), ",");
269 for (int i=0; i<rules.length; i++) {
+ 270 if (rules[i].indexOf("/") == -1) continue;
271 accepts.add(rules[i]);
272 }
273 return accepts;
274 }
275
276 //----------------------------------------------------------------- collections
277
278 /**
279 * Return collection specified by pathinfo.
280 * <pre>
281 * Supports these URI forms:
282 * /<blog-name>/entries
283 * /<blog-name>/entries/offset
284 * /<blog-name>/resources
285 * /<blog-name>/resources/offset
286 * </pre>
287 */
288 public Feed getCollection(String[] pathInfo) throws AtomException {
/*
P/P * Method: Feed getCollection(String[])
*
* Preconditions:
* log != null
* pathInfo[1] != null
* pathInfo != null
* this.roller != null
* (soft) pathInfo[2] != null
* (soft) init'ed(pathInfo[...])
* (soft) pathInfo.length in 2..232-1
* (soft) init'ed(pathInfo[0])
* (soft) this.maxEntries <= 232-2
* (soft) init'ed(this.user)
*
* Presumptions:
* java.lang.String:equals(...)@291 == 1
*
* Postconditions:
* init'ed(pathInfo[1])
* init'ed(pathInfo[2])
* (soft) init'ed(pathInfo[...])
* return_value in Addr_Set{&new Feed(getCollectionOfEntries#4),&new Feed(getCollectionOfResources#5)}
* new Feed(getCollectionOfEntries#4) num objects <= 1
* new Feed(getCollectionOfResources#5) num objects <= 1
*
* Test Vectors:
* java.lang.String:equals(...)@289: {0}, {1}
*/
289 if (pathInfo.length > 0 && pathInfo[1].equals("entries")) {
290 return getCollectionOfEntries(pathInfo);
291 } else if (pathInfo.length > 0 && pathInfo[1].equals("resources")) {
292 return getCollectionOfResources(pathInfo);
293 }
294 throw new AtomNotFoundException("Cannot find collection specified");
295 }
296
297 /**
298 * Helper method that returns collection of entries, called by getCollection().
299 */
300 public Feed getCollectionOfEntries(String[] pathInfo) throws AtomException {
/*
P/P * Method: Feed getCollectionOfEntries(String[])
*
* Preconditions:
* log != null
* pathInfo != null
* pathInfo.length >= 1
* init'ed(pathInfo[0])
* this.maxEntries <= 232-2
* this.roller != null
* (soft) pathInfo[2] != null
* (soft) init'ed(this.user)
*
* Presumptions:
* java.util.Iterator:next(...)@350 != null
* org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@315 != null
* org.apache.roller.weblogger.business.WeblogManager:getWeblogEntries(...)@322 != null
* org.apache.roller.weblogger.business.Weblogger:getUserManager(...)@315 != null
* org.apache.roller.weblogger.business.Weblogger:getWeblogManager(...)@322 != null
*
* Postconditions:
* return_value == &new Feed(getCollectionOfEntries#4)
* new Feed(getCollectionOfEntries#4) num objects == 1
*
* Test Vectors:
* pathInfo.length: {1,2}, {3..+Inf}
* java.util.Iterator:hasNext(...)@349: {0}, {1}
* java.util.List:size(...)@377: {-231..0}, {1..232-1}
*/
301 log.debug("Entering");
302 try {
303 int start = 0;
304 int max = maxEntries;
305 if (pathInfo.length > 2) {
306 try {
307 String s = pathInfo[2].trim();
308 start = Integer.parseInt(s);
309 } catch (Throwable t) {
310 log.warn("Unparsable range: " + pathInfo[2]);
311 }
312 }
313 String handle = pathInfo[0];
314 String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
315 Weblog website = roller.getUserManager().getWebsiteByHandle(handle);
316 if (website == null) {
317 throw new AtomNotFoundException("Cannot find specified weblog");
318 }
319 if (!canView(website)) {
+ 320 throw new AtomNotAuthorizedException("Not authorized to access website: " + handle);
321 }
322 List entries = roller.getWeblogManager().getWeblogEntries(
323 website, // website
324 null, // user
325 null, // startDate
326 null, // endDate
327 null, // catName
328 null, // tags
329 null, // status
330 null, // text
331 "updateTime", // sortby
332 null,
333 null, // locale
334 start, // offset (for range paging)
335 max + 1); // maxEntries
336 Feed feed = new Feed();
337 feed.setId(atomURL
338 +"/"+website.getHandle() + "/entries/" + start);
339 feed.setTitle(website.getName());
340
341 Link link = new Link();
342 link.setHref(absUrl + "/" + website.getHandle());
343 link.setRel("alternate");
344 link.setType("text/html");
345 feed.setAlternateLinks(Collections.singletonList(link));
346
347 List atomEntries = new ArrayList();
348 int count = 0;
349 for (Iterator iter = entries.iterator(); iter.hasNext() && count < maxEntries; count++) {
350 WeblogEntry rollerEntry = (WeblogEntry)iter.next();
351 Entry entry = createAtomEntry(rollerEntry);
352 atomEntries.add(entry);
353 if (count == 0) {
354 // first entry is most recent
355 feed.setUpdated(entry.getUpdated());
356 }
357 }
358 List links = new ArrayList();
359 if (entries.size() > max) { // add next link
360 int nextOffset = start + max;
361 String url = atomURL+"/"
362 + website.getHandle() + "/entries/" + nextOffset;
363 Link nextLink = new Link();
364 nextLink.setRel("next");
365 nextLink.setHref(url);
366 links.add(nextLink);
367 }
368 if (start > 0) { // add previous link
369 int prevOffset = start > max ? start - max : 0;
370 String url = atomURL+"/"
371 +website.getHandle() + "/entries/" + prevOffset;
372 Link prevLink = new Link();
373 prevLink.setRel("previous");
374 prevLink.setHref(url);
375 links.add(prevLink);
376 }
377 if (links.size() > 0) feed.setOtherLinks(links);
378 // Use collection URI as id
379 feed.setEntries(atomEntries);
380
381 log.debug("Exiting");
382 return feed;
383
384 } catch (WebloggerException re) {
385 throw new AtomException("Getting entry collection");
386 }
387 }
388
389 /**
390 * Helper method that returns collection of resources, called by getCollection().
391 * /handle/resources/offset
392 * /handle/resources/path/offset
393 */
394 public Feed getCollectionOfResources(String[] rawPathInfo) throws AtomException {
/*
P/P * Method: Feed getCollectionOfResources(String[])
*
* Preconditions:
* log != null
* rawPathInfo != null
* init'ed(this.maxEntries)
* this.roller != null
* (soft) init'ed(rawPathInfo[...])
* (soft) rawPathInfo.length in 1..232-1
* (soft) init'ed(rawPathInfo[0])
* (soft) init'ed(this.user)
*
* Presumptions:
* files.length@434 <= 232-1
* init'ed(java.io.File.separator)
* java.lang.Integer:parseInt(...)@402 >= 0
* java.util.SortedSet:size(...)@451 >= 0
* java.util.SortedSet:toArray(...)@451 != null
* ...
*
* Postconditions:
* (soft) init'ed(rawPathInfo[...])
* return_value == &new Feed(getCollectionOfResources#5)
* new Feed(getCollectionOfResources#5) num objects == 1
*
* Test Vectors:
* rawPathInfo.length: {1,2}, {3..232-1}
* java.lang.String:equals(...)@410: {1}, {0}
* org.apache.roller.weblogger.business.FileManager:getFiles(...)@434: Addr_Set{null}, Inverse{null}
*/
395 log.debug("Entering");
396 try {
397 int start = 0;
398 int max = maxEntries;
399 String[] pathInfo = rawPathInfo;
400 if (rawPathInfo.length > 2) {
401 try {
402 start = Integer.parseInt(rawPathInfo[rawPathInfo.length - 1]);
403 pathInfo = new String[rawPathInfo.length - 1];
404 for (int i=0; i<rawPathInfo.length - 1; i++) {
405 pathInfo[i] = rawPathInfo[i];
406 }
407 } catch (Exception ingored) {}
408 }
409 String path = filePathFromPathInfo(pathInfo);
+ 410 if (!path.equals("")) path = path + File.separator;
411
412 String handle = pathInfo[0];
413 String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
414 Weblog website = roller.getUserManager().getWebsiteByHandle(handle);
415 if (website == null) {
416 throw new AtomNotFoundException("Cannot find weblog: " + handle);
417 }
+ 418 if (!canView(website)) {
419 throw new AtomNotAuthorizedException("Not authorized to access website");
420 }
421
422 Feed feed = new Feed();
423 feed.setId(atomURL
424 +"/"+website.getHandle() + "/resources/" + path + start);
425 feed.setTitle(website.getName());
426
427 Link link = new Link();
428 link.setHref(absUrl + "/" + website.getHandle());
429 link.setRel("alternate");
430 link.setType("text/html");
431 feed.setAlternateLinks(Collections.singletonList(link));
432
433 FileManager fmgr = roller.getFileManager();
434 ThemeResource[] files = fmgr.getFiles(website, path);
435
/*
P/P * Method: void org.apache.roller.weblogger.webservices.atomprotocol.RollerAtomHandler$1(RollerAtomHandler)
*/
436 SortedSet sortedSet = new TreeSet(new Comparator() {
437 public int compare(Object o1, Object o2) {
/*
P/P * Method: int compare(Object, Object)
*
* Preconditions:
* o1 != null
* o2 != null
*
* Postconditions:
* return_value in -1..1
*/
438 ThemeResource f1 = (ThemeResource)o1;
439 ThemeResource f2 = (ThemeResource)o2;
440 if (f1.getLastModified() < f2.getLastModified()) return 1;
441 else if (f1.getLastModified() == f2.getLastModified()) return 0;
442 else return -1;
443 }
444 });
445
446 if (files != null && start < files.length) {
447 for (int j=0; j<files.length; j++) {
448 sortedSet.add(files[j]);
449 }
450 int count = 0;
451 ThemeResource[] sortedResources =
452 (ThemeResource[])sortedSet.toArray(new ThemeResource[sortedSet.size()]);
453 List atomEntries = new ArrayList();
+ 454 for (int i=start; i<(start + max) && i<(sortedResources.length); i++) {
+ 455 Entry entry = createAtomResourceEntry(website, sortedResources[i]);
456 atomEntries.add(entry);
457 if (count == 0) {
458 // first entry is most recent
459 feed.setUpdated(entry.getUpdated());
460 }
+ 461 count++;
462 }
463
464 List otherLinks = new ArrayList();
465 if (start + count < files.length) { // add next link
466 int nextOffset = start + max;
467 String url = atomURL
468 +"/"+ website.getHandle() + "/resources/" + path + nextOffset;
469 Link nextLink = new Link();
470 nextLink.setRel("next");
471 nextLink.setHref(url);
472 otherLinks.add(nextLink);
473 }
474 if (start > 0) { // add previous link
475 int prevOffset = start > max ? start - max : 0;
476 String url = atomURL
477 +"/"+website.getHandle() + "/resources/" + path + prevOffset;
478 Link prevLink = new Link();
479 prevLink.setRel("previous");
480 prevLink.setHref(url);
481 otherLinks.add(prevLink);
482 }
483 feed.setOtherLinks(otherLinks);
484 feed.setEntries(atomEntries);
485 }
486
487 log.debug("Existing");
488 return feed;
489
490 } catch (WebloggerException re) {
491 throw new AtomException("Getting resource collection");
492 }
493 }
494
495 //--------------------------------------------------------------------- entries
496
497 /**
498 * Create entry in the entry collection (a Weblogger blog has only one).
499 */
500 public Entry postEntry(String[] pathInfo, Entry entry) throws AtomException {
/*
P/P * Method: Entry postEntry(String[], Entry)
*
* Preconditions:
* entry != null
* log != null
* pathInfo != null
* pathInfo.length >= 1
* init'ed(pathInfo[0])
* this.roller != null
* init'ed(throttle)
* (soft) init'ed(this.user)
*
* Presumptions:
* org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@505 != null
* org.apache.roller.weblogger.business.Weblogger:getIndexManager(...)@527 != null
* org.apache.roller.weblogger.business.Weblogger:getUserManager(...)@505 != null
* org.apache.roller.weblogger.business.Weblogger:getWeblogManager(...)@517 != null
*
* Postconditions:
* return_value == &new Entry(createAtomEntry#1)
* new Entry(createAtomEntry#1) num objects == 1
*
* Test Vectors:
* throttle: {0}, {1}
* org.apache.roller.weblogger.pojos.WeblogEntry:isPublished(...)@526: {0}, {1}
*/
501 log.debug("Entering");
502 try {
503 // authenticated client posted a weblog entry
504 String handle = pathInfo[0];
505 Weblog website =
506 roller.getUserManager().getWebsiteByHandle(handle);
507 if (website == null) {
508 throw new AtomNotFoundException("Cannot find weblog: " + handle);
509 }
510 if (!canEdit(website)) {
+ 511 throw new AtomNotAuthorizedException("Not authorized to access website: " + handle);
512 }
513
514 if (throttle) oneSecondThrottle();
515
516 // Save it and commit it
517 WeblogManager mgr = roller.getWeblogManager();
518 WeblogEntry rollerEntry = new WeblogEntry();
519 rollerEntry.setWebsite(website);
520 rollerEntry.setCreator(this.user);
521 copyToRollerEntry(entry, rollerEntry);
522 mgr.saveWeblogEntry(rollerEntry);
523 roller.flush();
524
525 CacheManager.invalidate(website);
526 if (rollerEntry.isPublished()) {
527 roller.getIndexManager().addEntryReIndexOperation(rollerEntry);
528 }
529
530 log.debug("Exiting");
531 return createAtomEntry(rollerEntry);
532
533 } catch (WebloggerException re) {
534 throw new AtomException("Posting entry", re);
535 }
536 }
537
538 /**
539 * Retrieve entry, URI like this /blog-name/entry/id
540 */
541 public Entry getEntry(String[] pathInfo) throws AtomException {
/*
P/P * Method: Entry getEntry(String[])
*
* Preconditions:
* log != null
* pathInfo != null
* pathInfo[1] != null
* this.roller != null
* (soft) pathInfo.length in 3..232-1
* (soft) init'ed(pathInfo[0])
* (soft) pathInfo[2] != null
* (soft) pathInfo[...] != null
* (soft) init'ed(this.user)
*
* Presumptions:
* java.lang.String:endsWith(...)@557 == 1
* java.lang.String:equals(...)@557 == 1
* java.lang.String:length(...)@559 - java.lang.String:length(...)@559 in -232+1..231
* org.apache.roller.weblogger.business.FileManager:getFile(...)@563 != null
* org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@561 != null
* ...
*
* Postconditions:
* return_value in Addr_Set{&new Entry(createAtomResourceEntry#3),&new Entry(createAtomEntry#1)}
* new Entry(createAtomEntry#1) num objects <= 1
* new Entry(createAtomResourceEntry#3) num objects <= 1
*
* Test Vectors:
* java.lang.String:equals(...)@546: {0}, {1}
*/
542 log.debug("Entering");
543 try {
544 if (pathInfo.length > 2) // URI is /blogname/entries/entryid
545 {
546 if (pathInfo[1].equals("entry")) {
547 WeblogEntry entry =
548 roller.getWeblogManager().getWeblogEntry(pathInfo[2]);
549 if (entry == null) {
550 throw new AtomNotFoundException("Cannot find specified entry/resource");
551 }
+ 552 if (!canView(entry)) {
553 throw new AtomNotAuthorizedException("Not authorized to view entry");
554 } else {
555 return createAtomEntry(entry);
556 }
557 } else if (pathInfo[1].equals("resource") && pathInfo[pathInfo.length - 1].endsWith(".media-link")) {
558 String filePath = filePathFromPathInfo(pathInfo);
+ 559 filePath = filePath.substring(0, filePath.length() - ".media-link".length());
560 String handle = pathInfo[0];
561 Weblog website =
562 roller.getUserManager().getWebsiteByHandle(handle);
563 ThemeResource resource =
564 roller.getFileManager().getFile(website, filePath);
565
566 log.debug("Exiting");
567 if (resource != null) return createAtomResourceEntry(website, resource);
568 }
569 }
570 throw new AtomNotFoundException("Cannot find specified entry/resource");
571 } catch (WebloggerException re) {
572 throw new AtomException("Getting entry");
573 }
574 }
575
576 /**
577 * Expects pathInfo of form /blog-name/resource/path/name
578 */
579 public AtomMediaResource getMediaResource(String[] pathInfo) throws AtomException {
/*
P/P * Method: AtomMediaResource getMediaResource(String[])
*
* Preconditions:
* log != null
* pathInfo != null
* pathInfo.length in 2..232-1
* init'ed(pathInfo[0])
* this.roller != null
* (soft) init'ed(pathInfo[...])
* (soft) init'ed(this.user)
*
* Presumptions:
* org.apache.roller.weblogger.business.FileManager:getFile(...)@595 != null
* org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@587 != null
* org.apache.roller.weblogger.business.Weblogger:getFileManager(...)@585 != null
* org.apache.roller.weblogger.business.Weblogger:getUserManager(...)@586 != null
*
* Postconditions:
* return_value == &new AtomMediaResource(getMediaResource#3)
* new AtomMediaResource(getMediaResource#3) num objects == 1
* init'ed(new AtomMediaResource(getMediaResource#3).contentLength)
* init'ed(new AtomMediaResource(getMediaResource#3).contentType)
* init'ed(new AtomMediaResource(getMediaResource#3).inputStream)
* new AtomMediaResource(getMediaResource#3).lastModified == &new Date(AtomMediaResource#1)
* new Date(AtomMediaResource#1) num objects == 1
*/
580 log.debug("Entering");
581 try {
582 // authenticated client posted a weblog entry
583 File tempFile = null;
584 String handle = pathInfo[0];
585 FileManager fmgr = roller.getFileManager();
586 UserManager umgr = roller.getUserManager();
587 Weblog website = umgr.getWebsiteByHandle(handle);
588 if (!canEdit(website)) {
+ 589 throw new AtomNotAuthorizedException("Not authorized to edit weblog: " + handle);
590 }
591 if (pathInfo.length > 1) {
592 try {
593 // Parse pathinfo to determine file path
594 String filePath = filePathFromPathInfo(pathInfo);
595 ThemeResource resource = fmgr.getFile(website, filePath);
596 return new AtomMediaResource(resource);
597 } catch (Exception e) {
598 throw new AtomException(
599 "Unexpected error during file upload", e);
600 }
601 }
602 throw new AtomException("Incorrect path information");
603
604 } catch (WebloggerException re) {
605 throw new AtomException("Posting media");
606 }
607 }
608
609 /**
610 * Update entry, URI like this /blog-name/entry/id
611 */
612 public Entry putEntry(String[] pathInfo, Entry entry) throws AtomException {
/*
P/P * Method: Entry putEntry(String[], Entry)
*
* Preconditions:
* entry != null
* log != null
* pathInfo != null
* pathInfo.length == 3
* init'ed(pathInfo[2])
* this.roller != null
* init'ed(throttle)
* (soft) init'ed(this.user)
*
* Presumptions:
* org.apache.roller.weblogger.business.WeblogManager:getWeblogEntry(...)@617 != null
* org.apache.roller.weblogger.business.Weblogger:getIndexManager(...)@635 != null
* org.apache.roller.weblogger.business.Weblogger:getWeblogManager(...)@617 != null
* org.apache.roller.weblogger.business.Weblogger:getWeblogManager(...)@627 != null
*
* Postconditions:
* return_value == &new Entry(createAtomEntry#1)
* new Entry(createAtomEntry#1) num objects == 1
*
* Test Vectors:
* throttle: {0}, {1}
* org.apache.roller.weblogger.pojos.WeblogEntry:isPublished(...)@634: {0}, {1}
*/
613 log.debug("Entering");
614 try {
615 if (pathInfo.length == 3) // URI is /blogname/entries/entryid
616 {
617 WeblogEntry rollerEntry =
618 roller.getWeblogManager().getWeblogEntry(pathInfo[2]);
619 if (rollerEntry == null) {
620 throw new AtomNotFoundException(
621 "Cannot find specified entry/resource");
622 }
+ 623 if (canEdit(rollerEntry)) {
624
625 if (throttle) oneSecondThrottle();
626
627 WeblogManager mgr = roller.getWeblogManager();
628 copyToRollerEntry(entry, rollerEntry);
629 rollerEntry.setUpdateTime(new Timestamp(new Date().getTime()));
630 mgr.saveWeblogEntry(rollerEntry);
631 roller.flush();
632
633 CacheManager.invalidate(rollerEntry.getWebsite());
634 if (rollerEntry.isPublished()) {
635 roller.getIndexManager().addEntryReIndexOperation(rollerEntry);
636 }
637 log.debug("Exiting");
638 return createAtomEntry(rollerEntry);
639 }
640 throw new AtomNotAuthorizedException("ERROR not authorized to update entry");
641 }
642 throw new AtomNotFoundException("Cannot find specified entry/resource");
643
644 } catch (WebloggerException re) {
645 throw new AtomException("Updating entry");
646 }
647 }
648
649 /**
650 * Delete entry, URI like this /blog-name/entry/id
651 */
652 public void deleteEntry(String[] pathInfo) throws AtomException {
/*
P/P * Method: void deleteEntry(String[])
*
* Preconditions:
* log != null
* pathInfo != null
* pathInfo[1] != null
* this.roller != null
* (soft) pathInfo.length in 3..232-1
* (soft) init'ed(pathInfo[0])
* (soft) init'ed(pathInfo[2])
* (soft) init'ed(pathInfo[...])
* (soft) init'ed(this.user)
*
* Presumptions:
* java.lang.String:equals(...)@671 == 1
* java.lang.String:length(...)@680 - java.lang.String:length(...)@680 in -232+1..231
* org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@673 != null
* org.apache.roller.weblogger.business.WeblogManager:getWeblogEntry(...)@658 != null
* org.apache.roller.weblogger.business.Weblogger:getFileManager(...)@681 != null
* ...
*
* Test Vectors:
* java.lang.String:equals(...)@656: {0}, {1}
*/
653 log.debug("Entering");
654 try {
655 if (pathInfo.length > 2) {
656 if (pathInfo[1].equals("entry")) // URI is /blogname/entry/entryid
657 {
658 WeblogEntry rollerEntry = roller.getWeblogManager().getWeblogEntry(pathInfo[2]);
659 if (rollerEntry == null) {
660 throw new AtomNotFoundException("cannot find specified entry/resource");
661 }
662 if (canEdit(rollerEntry)) {
663 WeblogManager mgr = roller.getWeblogManager();
664 CacheManager.invalidate(rollerEntry.getWebsite());
665 reindexEntry(rollerEntry);
666 mgr.removeWeblogEntry(rollerEntry);
667 log.debug("Deleted entry:" + rollerEntry.getAnchor());
668 roller.flush();
669 return;
670 }
671 } else if (pathInfo[1].equals("resource")) {
672 String handle = pathInfo[0];
673 Weblog website = roller.getUserManager().getWebsiteByHandle(handle);
674 if (website == null) {
675 throw new AtomNotFoundException("cannot find specified weblog");
676 }
+ 677 if (canEdit(website) && pathInfo.length > 1) {
678 try {
679 String path = filePathFromPathInfo(pathInfo);
+ 680 String fileName = path.substring(0, path.length() - ".media-link".length());
681 FileManager fmgr = roller.getFileManager();
682 fmgr.deleteFile(website, fileName);
683 log.debug("Deleted resource: " + fileName);
684 } catch (Exception e) {
685 String msg = "ERROR in atom.deleteResource";
686 log.error(msg,e);
687 throw new AtomException(msg);
688 }
689 return;
690 }
691 }
+ 692 throw new AtomNotAuthorizedException("ERROR not authorized to delete entry");
693 }
694 throw new AtomNotFoundException("cannot find specified entry/resource");
695
696 } catch (WebloggerException re) {
697 throw new AtomException("deleting entry");
698 }
699 }
700
701 //-------------------------------------------------------------------- resources
702
703 /**
704 * Create new resource in generic collection (a Weblogger blog has only one).
705 * TODO: can we avoid saving temporary file?
706 * TODO: do we need to handle mutli-part MIME uploads?
707 * TODO: use Jakarta Commons File-upload?
708 */
709 public Entry postMedia(String[] pathInfo,
710 String title, String slug, String contentType, InputStream is)
711 throws AtomException {
/*
P/P * Method: Entry postMedia(String[], String, String, String, InputStream)
*
* Preconditions:
* log != null
* pathInfo != null
* pathInfo.length in 2..232-1
* init'ed(pathInfo[0])
* this.roller != null
* (soft) contentType != null
* (soft) init'ed(pathInfo[...])
* (soft) init'ed(this.user)
*
* Presumptions:
* init'ed(java.io.File.separator)
* java.io.File:createTempFile(...)@728 != null
* org.apache.roller.weblogger.business.FileManager:getFile(...)@741 != null
* org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@719 != null
* org.apache.roller.weblogger.business.Weblogger:getFileManager(...)@717 != null
* ...
*
* Postconditions:
* return_value == &new Entry(createAtomResourceEntry#3)
* new Entry(createAtomResourceEntry#3) num objects == 1
*
* Test Vectors:
* java.lang.String:length(...)@736: {0}, {1..232-1}
*/
712 log.debug("Entering");
713 try {
714 // authenticated client posted a weblog entry
715 File tempFile = null;
716 String handle = pathInfo[0];
717 FileManager fmgr = roller.getFileManager();
718 UserManager umgr = roller.getUserManager();
719 Weblog website = umgr.getWebsiteByHandle(handle);
720 if (!canEdit(website)) {
+ 721 throw new AtomNotAuthorizedException("Not authorized to edit weblog: " + handle);
722 }
723 if (pathInfo.length > 1) {
724 // Save to temp file
725 String fileName = createFileName(website,
726 (slug != null) ? slug : Utilities.replaceNonAlphanumeric(title,' '), contentType);
727 try {
728 tempFile = File.createTempFile(fileName, "tmp");
729 FileOutputStream fos = new FileOutputStream(tempFile);
730 Utilities.copyInputToOutput(is, fos);
731 fos.close();
732
733 // Parse pathinfo to determine file path
734 String path = filePathFromPathInfo(pathInfo);
735
+ 736 if (path.length() > 0) path = path + File.separator;
737 FileInputStream fis = new FileInputStream(tempFile);
738 fmgr.saveFile(website, path + fileName, contentType, tempFile.length(), fis);
739 fis.close();
740
741 ThemeResource resource = fmgr.getFile(website, path + fileName);
742
743 log.debug("Exiting");
744 return createAtomResourceEntry(website, resource);
745
746 } catch (FileIOException fie) {
747 throw new AtomException(
748 "File upload disabled, over-quota or other error", fie);
749 } catch (Exception e) {
750 throw new AtomException(
751 "Unexpected error during file upload", e);
752 } finally {
753 if (tempFile != null) tempFile.delete();
754 }
755 }
756 throw new AtomException("Incorrect path information");
757
758 } catch (WebloggerException re) {
759 throw new AtomException("Posting media");
760 }
761 }
762
763 /**
764 * Creates a file name for a file based on a weblog, title string and a
765 * content-type.
766 *
767 * @param weblog Weblog for which file name is being created
768 * @param title Title to be used as basis for file name (or null)
769 * @param contentType Content type of file (must not be null)
770 *
771 * If a title is specified, the method will apply the same create-anchor
772 * logic we use for weblog entries to create a file name based on the title.
773 *
774 * If title is null, the base file name will be the weblog handle plus a
775 * YYYYMMDDHHSS timestamp.
776 *
777 * The extension will be formed by using the part of content type that
778 * comes after he slash.
779 *
780 * For example:
781 * weblog.handle = "daveblog"
782 * title = "Port Antonio"
783 * content-type = "image/jpg"
784 * Would result in port_antonio.jpg
785 *
786 * Another example:
787 * weblog.handle = "daveblog"
788 * title = null
789 * content-type = "image/jpg"
790 * Might result in daveblog-200608201034.jpg
791 */
792 private String createFileName(Weblog weblog, String slug, String contentType) {
793
/*
P/P * Method: String createFileName(Weblog, String, String)
* createFileName fails for all possible inputs
*
* Preconditions:
* (soft) contentType != null
* (soft) weblog != null
*
* Postconditions:
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* java.lang.StringBuilder:toString(...)._tainted == 0
* init'ed(java.util.StringTokenizer:nextToken(...)._tainted)
* return_value != null
*
* Test Vectors:
* slug: Addr_Set{null}, Inverse{null}
* java.lang.String:endsWith(...)@817: {1}, {0}
* java.lang.String:equals(...)@806: {1}, {0}
* java.util.StringTokenizer:hasMoreTokens(...)@811: {0}, {1}
*/
794 if (weblog == null) throw new IllegalArgumentException("weblog cannot be null");
795 if (contentType == null) throw new IllegalArgumentException("contentType cannot be null");
796
797 String fileName = null;
798
799 // Determine the extension based on the contentType. This is a hack.
800 // The info we need to map from contentType to file extension is in
801 // JRE/lib/content-type.properties, but Java Activation doesn't provide
802 // a way to do a reverse mapping or to get at the data.
803 String[] typeTokens = contentType.split("/");
+ 804 String ext = typeTokens[1];
805
806 if (slug != null && !slug.trim().equals("")) {
807 // We've got a title, so use it to build file name
808 StringTokenizer toker = new StringTokenizer(slug);
809 String tmp = null;
810 int count = 0;
811 while (toker.hasMoreTokens() && count < 5) {
812 String s = toker.nextToken();
813 s = s.toLowerCase();
814 tmp = (tmp == null) ? s : tmp + "_" + s;
815 count++;
816 }
+ 817 if (!tmp.endsWith("." + ext)) {
818 fileName = tmp + "." + ext;
819 } else {
820 fileName = tmp;
821 }
822 } else {
823 // No title or text, so instead we'll use the item's date
824 // in YYYYMMDD format to form the file name
825 SimpleDateFormat sdf = new SimpleDateFormat();
826 sdf.applyPattern("yyyyMMddHHSS");
827 fileName = weblog.getHandle()+"-"+sdf.format(new Date())+"."+ext;
828 }
829
830 return fileName;
831 }
832
833
834 /**
835 * Update resource specified by pathInfo using data from input stream.
836 * Expects pathInfo of form /blog-name/resource/path/name
837 */
838 public Entry putMedia(String[] pathInfo,
839 String contentType, InputStream is) throws AtomException {
840 try {
841 // authenticated client posted a weblog entry
/*
P/P * Method: Entry putMedia(String[], String, InputStream)
*
* Preconditions:
* pathInfo != null
* pathInfo.length in 2..232-1
* init'ed(pathInfo[0])
* this.roller != null
* (soft) log != null
* (soft) init'ed(pathInfo[...])
* (soft) init'ed(this.user)
*
* Presumptions:
* java.io.File:createTempFile(...)@853 != null
* java.util.UUID:randomUUID(...)@853 != null
* org.apache.roller.weblogger.business.FileManager:getFile(...)@862 != null
* org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@846 != null
* org.apache.roller.weblogger.business.Weblogger:getFileManager(...)@844 != null
* ...
*
* Postconditions:
* return_value == &new Entry(createAtomResourceEntry#3)
* new Entry(createAtomResourceEntry#3) num objects == 1
*/
842 File tempFile = null;
843 String handle = pathInfo[0];
844 FileManager fmgr = roller.getFileManager();
845 UserManager umgr = roller.getUserManager();
846 Weblog website = umgr.getWebsiteByHandle(handle);
847 if (!canEdit(website)) {
+ 848 throw new AtomNotAuthorizedException("Not authorized to edit weblog: " + handle);
849 }
850 if (pathInfo.length > 1) {
851 // Save to temp file
852 try {
853 tempFile = File.createTempFile(UUID.randomUUID().toString(), "tmp");
854 FileOutputStream fos = new FileOutputStream(tempFile);
855 Utilities.copyInputToOutput(is, fos);
856 fos.close();
857
858 // Parse pathinfo to determine file path
859 String path = filePathFromPathInfo(pathInfo);
860
861 // Attempt to load file, to ensure it exists
862 ThemeResource resource = fmgr.getFile(website, path);
863
864 FileInputStream fis = new FileInputStream(tempFile);
865 fmgr.saveFile(website, path, contentType, tempFile.length(), fis);
866 fis.close();
867
868 log.debug("Exiting");
869 return createAtomResourceEntry(website, resource);
870
871 } catch (FileIOException fie) {
872 throw new AtomException(
873 "File upload disabled, over-quota or other error", fie);
874 } catch (Exception e) {
875 throw new AtomException(
876 "Unexpected error during file upload", e);
877 } finally {
878 if (tempFile != null) tempFile.delete();
879 }
880 }
881 throw new AtomException("Incorrect path information");
882
883 } catch (WebloggerException re) {
884 throw new AtomException("Posting media");
885 }
886
887 }
888
889 //------------------------------------------------------------------ URI testers
890
891 /**
892 * True if URL is the introspection URI.
893 */
894 public boolean isIntrospectionURI(String[] pathInfo) {
/*
P/P * Method: bool isIntrospectionURI(String[])
*
* Preconditions:
* pathInfo != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* pathInfo.length: {1..+Inf}, {0}
*/
895 if (pathInfo.length==0) return true;
896 return false;
897 }
898
899 /**
900 * True if URL is a entry URI.
901 */
902 public boolean isEntryURI(String[] pathInfo) {
/*
P/P * Method: bool isEntryURI(String[])
*
* Preconditions:
* pathInfo != null
* (soft) pathInfo.length <= 232
* (soft) pathInfo[...] != null
* (soft) pathInfo[1] != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* pathInfo.length: {0..2}, {3..232}
* java.lang.String:endsWith(...)@904: {0}, {1}
* java.lang.String:equals(...)@903: {0}, {1}
* java.lang.String:equals(...)@904: {0}, {1}
*/
903 if (pathInfo.length > 2 && pathInfo[1].equals("entry")) return true;
904 if (pathInfo.length > 2 && pathInfo[1].equals("resource") && pathInfo[pathInfo.length-1].endsWith(".media-link")) return true;
905 return false;
906 }
907
908 /**
909 * True if URL is media edit URI. Media can be udpated, but not metadata.
910 */
911 public boolean isMediaEditURI(String[] pathInfo) {
/*
P/P * Method: bool isMediaEditURI(String[])
*
* Preconditions:
* pathInfo != null
* (soft) pathInfo[1] != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* pathInfo.length: {0,1}, {2..+Inf}
* java.lang.String:equals(...)@912: {0}, {1}
*/
912 if (pathInfo.length > 1 && pathInfo[1].equals("resource")) return true;
913 return false;
914 }
915
916 /**
917 * True if URL is a collection URI of any sort.
918 */
919 public boolean isCollectionURI(String[] pathInfo) {
/*
P/P * Method: bool isCollectionURI(String[])
*
* Preconditions:
* pathInfo != null
* (soft) pathInfo[1] != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* pathInfo.length: {0,1}, {2..+Inf}
* java.lang.String:equals(...)@920: {0}, {1}
* java.lang.String:equals(...)@921: {0}, {1}
* java.lang.String:equals(...)@922: {0}, {1}
*/
920 if (pathInfo.length > 1 && pathInfo[1].equals("entries")) return true;
921 if (pathInfo.length > 1 && pathInfo[1].equals("resources")) return true;
922 if (pathInfo.length > 1 && pathInfo[1].equals("categories")) return true;
923 return false;
924 }
925
926 //------------------------------------------------------------------ permissions
927
928 /**
929 * Return true if user is allowed to edit an entry.
930 */
931 private boolean canEdit(WeblogEntry entry) {
932 try {
/*
P/P * Method: bool canEdit(WeblogEntry)
*
* Preconditions:
* (soft) entry != null
* (soft) log != null
* (soft) init'ed(this.user)
*
* Postconditions:
* init'ed(return_value)
*/
933 return entry.hasWritePermissions(this.user);
934 } catch (Exception e) {
935 log.error("Checking website.canSave()");
936 }
937 return false;
938 }
939
940 /**
941 * Return true if user is allowed to create/edit weblog entries and file uploads in a website.
942 */
943 private boolean canEdit(Weblog website) {
944 try {
/*
P/P * Method: bool canEdit(Weblog)
*
* Preconditions:
* (soft) log != null
* (soft) init'ed(this.user)
* (soft) website != null
*
* Presumptions:
* init'ed(org.apache.roller.weblogger.pojos.WeblogPermission.AUTHOR)
*
* Postconditions:
* init'ed(return_value)
*/
945 return website.hasUserPermissions(this.user,WeblogPermission.AUTHOR);
946 } catch (Exception e) {
947 log.error("Checking website.hasUserPermissions()");
948 }
949 return false;
950 }
951
952 /**
953 * Return true if user is allowed to view an entry.
954 */
955 private boolean canView(WeblogEntry entry) {
/*
P/P * Method: bool canView(WeblogEntry)
*
* Preconditions:
* (soft) entry != null
* (soft) log != null
* (soft) init'ed(this.user)
*
* Postconditions:
* init'ed(return_value)
*/
956 return canEdit(entry);
957 }
958
959 /**
960 * Return true if user is allowed to view a website.
961 */
962 private boolean canView(Weblog website) {
/*
P/P * Method: bool canView(Weblog)
*
* Preconditions:
* (soft) log != null
* (soft) init'ed(this.user)
* (soft) website != null
*
* Postconditions:
* init'ed(return_value)
*/
963 return canEdit(website);
964 }
965
966 //-------------------------------------------------------------- authentication
967
968 /**
969 * Perform WSSE authentication based on information in request.
970 * Will not work if Weblogger password encryption is turned on.
971 */
972 protected String authenticateWSSE(HttpServletRequest request) {
/*
P/P * Method: String authenticateWSSE(HttpServletRequest)
*
* Preconditions:
* request != null
* (soft) log != null
* (soft) this.roller != null
*
* Presumptions:
* org.apache.roller.weblogger.business.UserManager:getUserByUserName(...)@1001 != null
* org.apache.roller.weblogger.business.Weblogger:getUserManager(...)@1001 != null
* org.apache.roller.weblogger.pojos.User:getPassword(...)@1002 != null
* org.apache.roller.weblogger.util.WSSEUtilities:generateDigest(...)@1002 != null
*
* Postconditions:
* return_value == null
*
* Test Vectors:
* java.lang.String:equals(...)@1006: {0}, {1}
* javax.servlet.http.HttpServletRequest:getHeader(...)@973: Inverse{null}, Addr_Set{null}
*/
973 String wsseHeader = request.getHeader("X-WSSE");
974 if (wsseHeader == null) return null;
975
976 String ret = null;
977 String userName = null;
978 String created = null;
979 String nonce = null;
980 String passwordDigest = null;
981 String[] tokens = wsseHeader.split(",");
+ 982 for (int i = 0; i < tokens.length; i++) {
+ 983 int index = tokens[i].indexOf('=');
984 if (index != -1) {
985 String key = tokens[i].substring(0, index).trim();
986 String value = tokens[i].substring(index + 1).trim();
987 value = value.replaceAll("\"", "");
988 if (key.startsWith("UsernameToken")) {
989 userName = value;
990 } else if (key.equalsIgnoreCase("nonce")) {
991 nonce = value;
992 } else if (key.equalsIgnoreCase("passworddigest")) {
993 passwordDigest = value;
994 } else if (key.equalsIgnoreCase("created")) {
995 created = value;
996 }
997 }
998 }
999 String digest = null;
1000 try {
1001 User user = roller.getUserManager().getUserByUserName(userName);
+ 1002 digest = WSSEUtilities.generateDigest(
1003 WSSEUtilities.base64Decode(nonce),
1004 created.getBytes("UTF-8"),
1005 user.getPassword().getBytes("UTF-8"));
1006 if (digest.equals(passwordDigest)) {
1007 ret = userName;
1008 }
1009 } catch (Exception e) {
1010 log.error("During wsseAuthenticataion: " + e.getMessage(), e);
1011 }
1012 return ret;
1013 }
1014
1015 /**
1016 * BASIC authentication.
1017 */
1018 public String authenticateBASIC(HttpServletRequest request) {
/*
P/P * Method: String authenticateBASIC(HttpServletRequest)
*
* Preconditions:
* (soft) log != null
* (soft) request != null
* (soft) this.roller != null
*
* Presumptions:
* java.lang.String:indexOf(...)@1031 <= 232-2
* org.apache.roller.weblogger.business.UserManager:getUserByUserName(...)@1034 != null
* org.apache.roller.weblogger.business.Weblogger:getUserManager(...)@1034 != null
* org.apache.roller.weblogger.pojos.User:getEnabled(...)@1035 != null
* org.apache.roller.weblogger.pojos.User:getPassword(...)@1045 != null
*
* Postconditions:
* java.lang.String:substring(...)._tainted == 0
* return_value in Addr_Set{null,&java.lang.String:substring(...)}
*
* Test Vectors:
* java.lang.Boolean:booleanValue(...)@1035: {0}, {1}
* java.lang.String:equalsIgnoreCase(...)@1028: {0}, {1}
* java.lang.String:equalsIgnoreCase(...)@1041: {0}, {1}
* java.lang.String:indexOf(...)@1031: {-1}, {-231..-2, 0..232-2}
* java.util.StringTokenizer:hasMoreTokens(...)@1026: {0}, {1}
* javax.servlet.http.HttpServletRequest:getHeader(...)@1023: Addr_Set{null}, Inverse{null}
*/
1019 boolean valid = false;
1020 String userID = null;
1021 String password = null;
1022 try {
1023 String authHeader = request.getHeader("Authorization");
1024 if (authHeader != null) {
1025 StringTokenizer st = new StringTokenizer(authHeader);
1026 if (st.hasMoreTokens()) {
1027 String basic = st.nextToken();
1028 if (basic.equalsIgnoreCase("Basic")) {
1029 String credentials = st.nextToken();
1030 String userPass = new String(Base64.decodeBase64(credentials.getBytes()));
1031 int p = userPass.indexOf(":");
1032 if (p != -1) {
1033 userID = userPass.substring(0, p);
1034 User user = roller.getUserManager().getUserByUserName(userID);
1035 boolean enabled = user.getEnabled().booleanValue();
1036 if (enabled) {
1037 // are passwords encrypted?
1038 String encrypted =
1039 WebloggerConfig.getProperty("passwds.encryption.enabled");
1040 password = userPass.substring(p+1);
1041 if ("true".equalsIgnoreCase(encrypted)) {
1042 password = Utilities.encodePassword(password,
1043 WebloggerConfig.getProperty("passwds.encryption.algorithm"));
1044 }
1045 valid = user.getPassword().equals(password);
1046 }
1047 }
1048 }
1049 }
1050 }
1051 } catch (Exception e) {
1052 log.debug(e);
1053 }
1054 if (valid) return userID;
1055 return null;
1056 }
1057
1058 //----------------------------------------------------------- internal utilities
1059
1060 /**
1061 * Create a Rome Atom entry based on a Weblogger entry.
1062 * Content is escaped.
1063 * Link is stored as rel=alternate link.
1064 */
1065 private Entry createAtomEntry(WeblogEntry entry) {
/*
P/P * Method: Entry createAtomEntry(WeblogEntry)
*
* Preconditions:
* entry != null
*
* Presumptions:
* java.util.Iterator:next(...)@1104 != null
* org.apache.roller.weblogger.pojos.WeblogCategory:getPath(...)@1099 != null
* org.apache.roller.weblogger.pojos.WeblogEntry:getCategory(...)@1099 != null
* org.apache.roller.weblogger.pojos.WeblogEntry:getCreator(...)@1089 != null
* org.apache.roller.weblogger.pojos.WeblogEntry:getTags(...)@1103 != null
* ...
*
* Postconditions:
* return_value == &new Entry(createAtomEntry#1)
* new Entry(createAtomEntry#1) num objects == 1
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@1103: {0}, {1}
* org.apache.commons.lang.StringUtils:isNotEmpty(...)@1082: {0}, {1}
*/
1066 Entry atomEntry = new Entry();
1067
1068 String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
1069 atomEntry.setId( absUrl + entry.getPermaLink());
1070 atomEntry.setTitle( entry.getTitle());
1071 atomEntry.setPublished( entry.getPubTime());
1072 atomEntry.setUpdated( entry.getUpdateTime());
1073
1074 Content content = new Content();
1075 content.setType(Content.HTML);
1076 content.setValue(entry.getText());
1077 List contents = new ArrayList();
1078 contents.add(content);
1079
1080 atomEntry.setContents(contents);
1081
1082 if (StringUtils.isNotEmpty(entry.getSummary())) {
1083 Content summary = new Content();
1084 summary.setType(Content.HTML);
1085 summary.setValue(entry.getSummary());
1086 atomEntry.setSummary(summary);
1087 }
1088
1089 User creator = entry.getCreator();
1090 Person author = new Person();
1091 author.setName( creator.getUserName());
1092 author.setEmail( creator.getEmailAddress());
1093 atomEntry.setAuthors( Collections.singletonList(author));
1094
1095 // Add Atom category for Weblogger category, using category scheme
1096 List categories = new ArrayList();
1097 Category atomCat = new Category();
1098 atomCat.setScheme(getWeblogCategoryScheme(entry.getWebsite()));
1099 atomCat.setTerm(entry.getCategory().getPath().substring(1));
1100 categories.add(atomCat);
1101
1102 // Add Atom categories for each Weblogger tag with null scheme
1103 for (Iterator tagit = entry.getTags().iterator(); tagit.hasNext();) {
1104 WeblogEntryTag tag = (WeblogEntryTag) tagit.next();
1105 Category newcat = new Category();
1106 newcat.setTerm(tag.getName());
1107 categories.add(newcat);
1108 }
1109 atomEntry.setCategories(categories);
1110
1111 Link altlink = new Link();
1112 altlink.setRel("alternate");
1113 altlink.setHref(absUrl + entry.getPermaLink());
1114 List altlinks = new ArrayList();
1115 altlinks.add(altlink);
1116 atomEntry.setAlternateLinks(altlinks);
1117
1118 Link editlink = new Link();
1119 editlink.setRel("edit");
1120 editlink.setHref(
1121 atomURL
1122 +"/"+entry.getWebsite().getHandle() + "/entry/" + entry.getId());
1123 List otherlinks = new ArrayList();
1124 otherlinks.add(editlink);
1125 atomEntry.setOtherLinks(otherlinks);
1126
1127 List modules = new ArrayList();
1128 AppModule app = new AppModuleImpl();
1129 app.setDraft(!WeblogEntry.PUBLISHED.equals(entry.getStatus()));
1130 app.setEdited(entry.getUpdateTime());
1131 modules.add(app);
1132 atomEntry.setModules(modules);
1133
1134 return atomEntry;
1135 }
1136
1137 private Entry createAtomResourceEntry(Weblog website, ThemeResource file) {
/*
P/P * Method: Entry createAtomResourceEntry(Weblog, ThemeResource)
*
* Preconditions:
* file != null
* website != null
*
* Presumptions:
* javax.activation.FileTypeMap:getDefaultFileTypeMap(...)@1150 != null
* org.apache.roller.weblogger.business.Weblogger:getUrlStrategy(...)@1147 != null
* org.apache.roller.weblogger.business.WebloggerFactory:getWeblogger(...)@1147 != null
* org.apache.roller.weblogger.pojos.ThemeResource:getPath(...)@1139 != null
*
* Postconditions:
* return_value == &new Entry(createAtomResourceEntry#3)
* new Entry(createAtomResourceEntry#3) num objects == 1
*
* Test Vectors:
* java.lang.String:startsWith(...)@1139: {0}, {1}
*/
+ 1138 String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
1139 String filePath =
1140 file.getPath().startsWith("/") ? file.getPath().substring(1) : file.getPath();
1141 String editURI =
1142 atomURL+"/"+website.getHandle()
1143 + "/resource/" + filePath + ".media-link";
1144 String editMediaURI =
1145 atomURL+"/"+ website.getHandle()
1146 + "/resource/" + filePath;
1147 URLStrategy urlStrategy = WebloggerFactory.getWeblogger().getUrlStrategy();
1148 String viewURI = urlStrategy.getWeblogResourceURL(website, filePath, true);
1149
1150 FileTypeMap map = FileTypeMap.getDefaultFileTypeMap();
1151 // TODO: figure out why PNG is missing from Java MIME types
1152 if (map instanceof MimetypesFileTypeMap) {
1153 try {
1154 ((MimetypesFileTypeMap)map).addMimeTypes("image/png png PNG");
1155 } catch (Exception ignored) {}
1156 }
1157 String contentType = map.getContentType(file.getName());
1158
1159 Entry entry = new Entry();
1160 entry.setId(editMediaURI);
1161 entry.setTitle(file.getName());
1162 entry.setUpdated(new Date(file.getLastModified()));
1163
1164 List otherlinks = new ArrayList();
1165 entry.setOtherLinks(otherlinks);
1166 Link editlink = new Link();
1167 editlink.setRel("edit");
1168 editlink.setHref(editURI);
1169 otherlinks.add(editlink);
1170 Link editMedialink = new Link();
1171 editMedialink.setRel("edit-media");
1172 editMedialink.setHref(editMediaURI);
1173 otherlinks.add(editMedialink);
1174
1175 Content content = new Content();
1176 content.setSrc(viewURI);
1177 content.setType(contentType);
1178 List contents = new ArrayList();
1179 contents.add(content);
1180 entry.setContents(contents);
1181
1182 List modules = new ArrayList();
1183 AppModule app = new AppModuleImpl();
1184 app.setDraft(false);
1185 app.setEdited(entry.getUpdated());
1186 modules.add(app);
1187 entry.setModules(modules);
1188
1189 return entry;
1190 }
1191
1192 /**
1193 * Copy fields from ROME entry to Weblogger entry.
1194 */
1195 private void copyToRollerEntry(Entry entry, WeblogEntry rollerEntry) throws WebloggerException {
1196
/*
P/P * Method: void copyToRollerEntry(Entry, WeblogEntry)
*
* Preconditions:
* entry != null
* rollerEntry != null
* (soft) this.roller != null
*
* Presumptions:
* com.sun.syndication.feed.atom.Category:getScheme(...)@1234 != null
* com.sun.syndication.feed.atom.Entry:getContents(...)@1207 != null
* com.sun.syndication.feed.atom.Entry:getContents(...)@1208 != null
* com.sun.syndication.feed.atom.Entry:getPublished(...)@1201 != null
* com.sun.syndication.feed.atom.Entry:getSummary(...)@1212 != null
* ...
*
* Test Vectors:
* com.sun.syndication.feed.atom.Category:getScheme(...)@1234: Addr_Set{null}, Inverse{null}
* com.sun.syndication.feed.atom.Category:getScheme(...)@1260: Inverse{null}, Addr_Set{null}
* com.sun.syndication.feed.atom.Category:getTerm(...)@1235: Addr_Set{null}, Inverse{null}
* com.sun.syndication.feed.atom.Entry:getCategories(...)@1229: Addr_Set{null}, Inverse{null}
* com.sun.syndication.feed.atom.Entry:getContents(...)@1207: Addr_Set{null}, Inverse{null}
* com.sun.syndication.feed.atom.Entry:getModule(...)@1217: Addr_Set{null}, Inverse{null}
* com.sun.syndication.feed.atom.Entry:getPublished(...)@1200: Addr_Set{null}, Inverse{null}
* com.sun.syndication.feed.atom.Entry:getSummary(...)@1211: Addr_Set{null}, Inverse{null}
* com.sun.syndication.feed.atom.Entry:getUpdated(...)@1203: Addr_Set{null}, Inverse{null}
* control.draft@1217: {0}, {1}
* ...
*/
1197 Timestamp current = new Timestamp(System.currentTimeMillis());
1198 Timestamp pubTime = current;
1199 Timestamp updateTime = current;
1200 if (entry.getPublished() != null) {
1201 pubTime = new Timestamp( entry.getPublished().getTime() );
1202 }
1203 if (entry.getUpdated() != null) {
1204 updateTime = new Timestamp( entry.getUpdated().getTime() );
1205 }
1206 rollerEntry.setTitle(entry.getTitle());
1207 if (entry.getContents() != null && entry.getContents().size() > 0) {
1208 Content content = (Content)entry.getContents().get(0);
1209 rollerEntry.setText(content.getValue());
1210 }
1211 if (entry.getSummary() != null) {
1212 rollerEntry.setSummary(entry.getSummary().getValue());
1213 }
1214 rollerEntry.setPubTime(pubTime);
1215 rollerEntry.setUpdateTime(updateTime);
1216
1217 AppModule control =
1218 (AppModule)entry.getModule(AppModule.URI);
1219 if (control!=null && control.getDraft()) {
1220 rollerEntry.setStatus(WeblogEntry.DRAFT);
1221 } else {
1222 rollerEntry.setStatus(WeblogEntry.PUBLISHED);
1223 }
1224
1225 // Process incoming categories:
1226 // Atom categories with weblog-level scheme are Weblogger categories.
1227 // Atom supports multiple cats, but Weblogger supports one/entry
1228 // so here we take accept the first category that exists.
1229 List categories = entry.getCategories();
1230 if (categories != null && categories.size() > 0) {
1231 for (int i=0; i<categories.size(); i++) {
1232 Category cat = (Category)categories.get(i);
1233
1234 if (cat.getScheme() != null && cat.getScheme().equals(getWeblogCategoryScheme(rollerEntry.getWebsite()))) {
1235 String catString = cat.getTerm();
1236 if (catString != null) {
1237 WeblogCategory rollerCat =
1238 roller.getWeblogManager().getWeblogCategoryByPath(
1239 rollerEntry.getWebsite(), catString);
1240 if (rollerCat != null) {
1241 // Found a valid category, so break out
1242 rollerEntry.setCategory(rollerCat);
1243 break;
1244 }
1245 }
1246 }
1247 }
1248 }
1249 if (rollerEntry.getCategory() == null) {
1250 // Didn't find a category? Fall back to the default Blogger API category.
1251 rollerEntry.setCategory(rollerEntry.getWebsite().getBloggerCategory());
1252 }
1253
1254 // Now process incoming categories that are tags:
1255 // Atom categories with no scheme are considered tags.
1256 String tags = "";
1257 if (categories != null && categories.size() > 0) {
1258 for (int i=0; i<categories.size(); i++) {
1259 Category cat = (Category)categories.get(i);
1260 if (cat.getScheme() == null) {
1261 tags = tags + " " + cat.getTerm();
1262 }
1263 }
1264 }
1265 rollerEntry.setTagsAsString(tags);
1266 }
1267
1268 private String getWeblogCategoryScheme(Weblog website) {
/*
P/P * Method: String getWeblogCategoryScheme(Weblog)
*
* Presumptions:
* org.apache.roller.weblogger.business.Weblogger:getUrlStrategy(...)@1269 != null
* org.apache.roller.weblogger.business.WebloggerFactory:getWeblogger(...)@1269 != null
*
* Postconditions:
* init'ed(return_value)
*/
1269 return WebloggerFactory.getWeblogger().getUrlStrategy().getWeblogURL(website, null, true);
1270 }
1271
1272
1273 private String filePathFromPathInfo(String[] pathInfo) {
/*
P/P * Method: String filePathFromPathInfo(String[])
*
* Preconditions:
* pathInfo != null
* pathInfo.length <= 232-1
* (soft) init'ed(pathInfo[...])
*
* Presumptions:
* init'ed(java.io.File.separator)
*
* Postconditions:
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* init'ed(return_value)
*
* Test Vectors:
* pathInfo.length: {2}, {3..232-1}
* java.lang.String:length(...)@1277: {0}, {1..232-1}
*/
1274 String path = null;
1275 if (pathInfo.length > 2) {
1276 for (int i = 2; i < pathInfo.length; i++) {
1277 if (path != null && path.length() > 0)
1278 path = path + File.separator + pathInfo[i];
1279 else
1280 path = pathInfo[i];
1281 }
1282 } if (pathInfo.length == 2) {
1283 path = "";
1284 }
1285 return path;
1286 }
1287
1288 private void reindexEntry(WeblogEntry entry) throws WebloggerException {
/*
P/P * Method: void reindexEntry(WeblogEntry)
*
* Preconditions:
* entry != null
* this.roller != null
*
* Presumptions:
* org.apache.roller.weblogger.business.Weblogger:getIndexManager(...)@1289 != null
*
* Test Vectors:
* org.apache.roller.weblogger.pojos.WeblogEntry:isPublished(...)@1295: {0}, {1}
*/
1289 IndexManager manager = roller.getIndexManager();
1290
1291 // TODO: figure out what's up here and at WeblogEntryFormAction line 696
1292 //manager.removeEntryIndexOperation(entry);
1293
1294 // if published, index the entry
1295 if (entry.isPublished()) {
1296 manager.addEntryReIndexOperation(entry);
1297 }
1298 }
1299
1300 private void oneSecondThrottle() {
1301 // Throttle one entry per second per weblog because time-
1302 // stamp in MySQL and other DBs has only 1 sec resolution
1303 try {
/*
P/P * Method: void oneSecondThrottle()
*/
1304 synchronized (getClass()) {
1305 Thread.sleep(1000);
1306 }
1307 } catch (Exception ignored) {}
1308 }
1309
1310 }
1311
1312
1313
SofCheck Inspector Build Version : 2.18479
| RollerAtomHandler.java |
2009-Jan-02 14:25:12 |
| RollerAtomHandler.class |
2009-Sep-04 03:12:46 |
| RollerAtomHandler$1.class |
2009-Sep-04 03:12:46 |