KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > roller > webservices > atomprotocol > RollerAtomHandler


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.webservices.atomprotocol;
19 import java.io.File JavaDoc;
20 import java.io.FileInputStream JavaDoc;
21 import java.io.FileOutputStream JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.sql.Timestamp JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Date JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.StringTokenizer JavaDoc;
29 import java.util.Collections JavaDoc;
30 import javax.activation.MimetypesFileTypeMap JavaDoc;
31
32 import javax.servlet.http.HttpServletRequest JavaDoc;
33
34 import org.apache.commons.codec.binary.Base64;
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.roller.model.FileManager;
38 import org.apache.roller.model.Roller;
39 import org.apache.roller.model.RollerFactory;
40 import org.apache.roller.pojos.UserData;
41 import org.apache.roller.pojos.PermissionsData;
42 import org.apache.roller.pojos.WeblogCategoryData;
43 import org.apache.roller.pojos.WeblogEntryData;
44 import org.apache.roller.pojos.WebsiteData;
45 import org.apache.roller.ui.core.RollerContext;
46 import org.apache.roller.util.RollerMessages;
47 import org.apache.roller.util.Utilities;
48 import org.apache.roller.util.WSSEUtilities;
49
50 import com.sun.syndication.feed.atom.Content;
51 import com.sun.syndication.feed.atom.Category;
52 import com.sun.syndication.feed.atom.Entry;
53 import com.sun.syndication.feed.atom.Feed;
54 import com.sun.syndication.feed.atom.Link;
55 import com.sun.syndication.feed.atom.Person;
56 import java.io.IOException JavaDoc;
57 import java.text.SimpleDateFormat JavaDoc;
58 import java.util.Comparator JavaDoc;
59 import java.util.Map JavaDoc;
60 import java.util.SortedSet JavaDoc;
61 import java.util.TreeSet JavaDoc;
62 import javax.activation.FileTypeMap JavaDoc;
63 import org.apache.commons.lang.StringUtils;
64 import org.apache.roller.RollerException;
65 import org.apache.roller.config.RollerConfig;
66 import org.apache.roller.config.RollerRuntimeConfig;
67 import org.apache.roller.model.WeblogManager;
68 import org.apache.roller.pojos.RollerPropertyData;
69 import org.apache.roller.util.URLUtilities;
70 import org.apache.roller.util.cache.CacheManager;
71
72 /**
73  * Roller's Atom Protocol implementation.
74  * <pre>
75  * Each Roller workspace has two collections, one that accepts entries and
76  * that accepts everything. The entries collection represents the weblog
77  * entries in a single weblog and the everything collection represents that
78  * weblog's uploaded-files.
79  *
80  * Here are the APP URIs suppored by Roller:
81  *
82  * /roller-services/app
83  * Introspection doc
84  *
85  * /roller-services/app/<weblog-handle>/entries
86  * Entry collection for a blog
87  *
88  * /roller-services/app/<weblog-handle>/entries/<offset>
89  * Entry collection for a blog, with offset
90  *
91  * /roller-services/app/<weblog-handle>/entry/<id>
92  * Individual entry (i.e. edit URI)
93  *
94  * /roller-services/app/<weblog-handle>/resources
95  * Resource (i.e. file-uploads) collection for a blog
96  *
97  * /roller-services/app/<weblog-handle>/resources/<offset>
98  * Resource collection for a blog, with offset
99  *
100  * /roller-services/app/<weblog-handle>/resource/*.media-link<name>
101  * Individual resource metadata (i.e. edit URI)
102  *
103  * /roller-services/app/<weblog-handle>/resource/<name>
104  * Individual resource data (i.e. media-edit URI)
105  *
106  * </pre>
107  *
108  * @author David M Johnson
109  */

110 public class RollerAtomHandler implements AtomHandler {
111     private HttpServletRequest JavaDoc mRequest;
112     private Roller mRoller;
113     private RollerContext mRollerContext;
114     private UserData user;
115     private int mMaxEntries = 20;
116     //private MessageDigest md5Helper = null;
117
//private MD5Encoder md5Encoder = new MD5Encoder();
118

119     private static Log mLogger =
120             LogFactory.getFactory().getInstance(RollerAtomHandler.class);
121     
122     //---------------------------------------------------------------- construction
123

124     /**
125      * Create Atom handler for a request and attempt to authenticate user.
126      * If user is authenticated, then getAuthenticatedUsername() will return
127      * then user's name, otherwise it will return null.
128      */

129     public RollerAtomHandler(HttpServletRequest JavaDoc request) {
130         mRequest = request;
131         mRoller = RollerFactory.getRoller();
132         mRollerContext = RollerContext.getRollerContext();
133         
134         // TODO: decide what to do about authentication, is WSSE going to fly?
135
//String userName = authenticateWSSE(request);
136
String JavaDoc userName = authenticateBASIC(request);
137         if (userName != null) {
138             try {
139                 this.user = mRoller.getUserManager().getUserByUserName(userName);
140             } catch (Exception JavaDoc neverHappen) {
141                 mLogger.debug("ERROR: getting user", neverHappen);
142             }
143         }
144     }
145     
146     /**
147      * Return weblogHandle of authenticated user or null if there is none.
148      */

149     public String JavaDoc getAuthenticatedUsername() {
150         String JavaDoc ret = null;
151         if (this.user != null) {
152             ret = user.getUserName();
153         }
154         return ret;
155     }
156     
157     //---------------------------------------------------------------- introspection
158

159     /**
160      * Return Atom service document for site, getting blog-name from pathInfo.
161      * The workspace will contain collections for entries, categories and resources.
162      */

163     public AtomService getIntrospection() throws AtomException {
164         AtomService service = new AtomService();
165         List JavaDoc perms = null;
166         try {
167             perms = mRoller.getUserManager().getAllPermissions(user);
168
169         } catch (RollerException re) {
170             throw new AtomException("ERROR: getting user's weblogs", re);
171         }
172         String JavaDoc accept = null;
173         try {
174             accept = getAcceptedContentTypeRange();
175         } catch (RollerException re) {
176             throw new AtomException("ERROR: getting site's accept range", re);
177         }
178         if (perms != null) {
179             for (Iterator JavaDoc iter=perms.iterator(); iter.hasNext();) {
180                 PermissionsData perm = (PermissionsData)iter.next();
181                 String JavaDoc handle = perm.getWebsite().getHandle();
182                 AtomService.Workspace workspace = new AtomService.Workspace();
183                 workspace.setTitle(Utilities.removeHTML(perm.getWebsite().getName()));
184                 service.addWorkspace(workspace);
185                 
186                 AtomService.Collection entryCol = new AtomService.Collection();
187                 entryCol.setTitle("Weblog Entries");
188                 entryCol.setAccept("entry");
189                 entryCol.setHref(URLUtilities.getAtomProtocolURL(true)+"/"+handle+"/entries");
190                 try {
191                     AtomService.Categories cats = new AtomService.Categories();
192                     cats.setFixed(true);
193                     cats.setScheme(URLUtilities.getWeblogURL(perm.getWebsite(), null, true));
194                     List JavaDoc rollerCats = mRoller.getWeblogManager().getWeblogCategories(perm.getWebsite(), false);
195                     for (Iterator JavaDoc it = rollerCats.iterator(); it.hasNext();) {
196                         WeblogCategoryData rollerCat = (WeblogCategoryData)it.next();
197                         AtomService.Category cat = new AtomService.Category();
198                         cat.setTerm(rollerCat.getPath());
199                         cat.setLabel(rollerCat.getName());
200                         cats.addCategory(cat);
201                     }
202                     entryCol.addCategories(cats);
203                 } catch (Exception JavaDoc e) {
204                     throw new AtomException("ERROR fetching weblog categories");
205                 }
206                 workspace.addCollection(entryCol);
207                                 
208                 AtomService.Collection uploadCol = new AtomService.Collection();
209                 uploadCol.setTitle("Media Files");
210                 uploadCol.setAccept(accept);
211                 uploadCol.setHref(URLUtilities.getAtomProtocolURL(true)+"/"+handle+"/resources");
212                 workspace.addCollection(uploadCol);
213             }
214         }
215         return service;
216     }
217     
218     /**
219      * Build accept range by taking things that appear to be content-type rules
220      * from site's file-upload allowed extensions.
221      */

222     private String JavaDoc getAcceptedContentTypeRange() throws RollerException {
223         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
224         Roller roller = RollerFactory.getRoller();
225         Map JavaDoc config = roller.getPropertiesManager().getProperties();
226         String JavaDoc allows = ((RollerPropertyData)config.get("uploads.types.allowed")).getValue();
227         String JavaDoc[] rules = StringUtils.split(StringUtils.deleteWhitespace(allows), ",");
228         for (int i=0; i<rules.length; i++) {
229             if (rules[i].indexOf("/") == -1) continue;
230             if (sb.length() != 0) {
231                 sb.append(",");
232             }
233             sb.append(rules[i]);
234         }
235         return sb.toString();
236     }
237     
238     //----------------------------------------------------------------- collections
239

240     /**
241      * Return collection specified by pathinfo.
242      * <pre>
243      * Supports these URI forms:
244      * /<blog-name>/entries
245      * /<blog-name>/entries/offset
246      * /<blog-name>/resources
247      * /<blog-name>/resources/offset
248      * </pre>
249      */

250     public Feed getCollection(String JavaDoc[] pathInfo) throws AtomException {
251         int start = 0;
252         if (pathInfo.length > 2) {
253             try {
254                 String JavaDoc s = pathInfo[2].trim();
255                 start = Integer.parseInt(s);
256             } catch (Throwable JavaDoc t) {
257                 mLogger.warn("Unparsable range: " + pathInfo[2]);
258             }
259         }
260         if (pathInfo.length > 0 && pathInfo[1].equals("entries")) {
261             return getCollectionOfEntries(pathInfo, start, mMaxEntries);
262         } else if (pathInfo.length > 0 && pathInfo[1].equals("resources")) {
263             return getCollectionOfResources(pathInfo, start, mMaxEntries);
264         }
265         throw new AtomNotFoundException("ERROR: cannot find collection specified");
266     }
267     
268     /**
269      * Helper method that returns collection of entries, called by getCollection().
270      */

271     public Feed getCollectionOfEntries(
272             String JavaDoc[] pathInfo, int start, int max) throws AtomException {
273         try {
274             String JavaDoc handle = pathInfo[0];
275             String JavaDoc absUrl = RollerRuntimeConfig.getAbsoluteContextURL();
276             WebsiteData website =
277                 mRoller.getUserManager().getWebsiteByHandle(handle);
278             if (website == null) {
279                 throw new AtomNotFoundException("ERROR: cannot find specified weblog");
280             }
281             List JavaDoc entries = null;
282             if (canView(website)) {
283                 entries = mRoller.getWeblogManager().getWeblogEntries(
284                         website, // website
285
null, // user
286
null, // startDate
287
null, // endDate
288
null, // catName
289
null, // status
290
"updateTime", // sortby
291
null, // locale
292
start, // offset (for range paging)
293
max + 1); // maxEntries
294
Feed feed = new Feed();
295                 feed.setId(URLUtilities.getAtomProtocolURL(true)
296                     +"/"+website.getHandle() + "/entries/" + start);
297                 feed.setTitle(website.getName());
298                 
299                 Link link = new Link();
300                 link.setHref(absUrl + "/" + website.getHandle());
301                 link.setRel("alternate");
302                 link.setType("text/html");
303                 feed.setAlternateLinks(Collections.singletonList(link));
304                 
305                 List JavaDoc atomEntries = new ArrayList JavaDoc();
306                 int count = 0;
307                 for (Iterator JavaDoc iter = entries.iterator(); iter.hasNext() && count < mMaxEntries; count++) {
308                     WeblogEntryData rollerEntry = (WeblogEntryData)iter.next();
309                     Entry entry = createAtomEntry(rollerEntry);
310                     atomEntries.add(entry);
311                     if (count == 0) {
312                         // first entry is most recent
313
feed.setUpdated(entry.getUpdated());
314                     }
315                 }
316                 List JavaDoc links = new ArrayList JavaDoc();
317                 if (entries.size() > max) { // add next link
318
int nextOffset = start + max;
319                     String JavaDoc url = URLUtilities.getAtomProtocolURL(true)+"/"
320                             + website.getHandle() + "/entries/" + nextOffset;
321                     Link nextLink = new Link();
322                     nextLink.setRel("next");
323                     nextLink.setHref(url);
324                     links.add(nextLink);
325                 }
326                 if (start > 0) { // add previous link
327
int prevOffset = start > max ? start - max : 0;
328                     String JavaDoc url = URLUtilities.getAtomProtocolURL(true)+"/"
329                             +website.getHandle() + "/entries/" + prevOffset;
330                     Link prevLink = new Link();
331                     prevLink.setRel("previous");
332                     prevLink.setHref(url);
333                     links.add(prevLink);
334                 }
335                 if (links.size() > 0) feed.setOtherLinks(links);
336                 // Use collection URI as id
337
feed.setEntries(atomEntries);
338                 return feed;
339             }
340             throw new AtomNotAuthorizedException("ERROR: not authorized to access website");
341         
342         } catch (RollerException re) {
343             throw new AtomException("ERROR: getting entry collection");
344         }
345     }
346     
347     /**
348      * Helper method that returns collection of resources, called by getCollection().
349      */

350     public Feed getCollectionOfResources(
351             String JavaDoc[] pathInfo, int start, int max) throws AtomException {
352         try {
353             String JavaDoc handle = pathInfo[0];
354             String JavaDoc absUrl = RollerRuntimeConfig.getAbsoluteContextURL();
355             WebsiteData website =
356                 mRoller.getUserManager().getWebsiteByHandle(handle);
357             if (website == null) {
358                 throw new AtomNotFoundException(
359                     "ERROR: cannot find specified weblog");
360             }
361             FileManager fmgr = mRoller.getFileManager();
362             File JavaDoc[] files = fmgr.getFiles(website.getHandle());
363                         
364             if (canView(website)) {
365                 Feed feed = new Feed();
366                 feed.setId(URLUtilities.getAtomProtocolURL(true)
367                     +"/"+website.getHandle() + "/entries/" + start);
368                 feed.setTitle(website.getName());
369                 
370                 Link link = new Link();
371                 link.setHref(absUrl + "/" + website.getHandle());
372                 link.setRel("alternate");
373                 link.setType("text/html");
374                 feed.setAlternateLinks(Collections.singletonList(link));
375                 
376                 SortedSet JavaDoc sortedSet = new TreeSet JavaDoc(new Comparator JavaDoc() {
377                     public int compare(Object JavaDoc o1, Object JavaDoc o2) {
378                         File JavaDoc f1 = (File JavaDoc)o1;
379                         File JavaDoc f2 = (File JavaDoc)o2;
380                         if (f1.lastModified() < f2.lastModified()) return 1;
381                         else if (f1.lastModified() == f2.lastModified()) return 0;
382                         else return -1;
383                     }
384                     public boolean equals(Object JavaDoc obj) {
385                         return false;
386                     }
387                 });
388                 List JavaDoc atomEntries = new ArrayList JavaDoc();
389                 if (files != null && start < files.length) {
390                     for (int i=0; i<files.length; i++) {
391                         sortedSet.add(files[i]);
392                     }
393                 }
394                 int count = 0;
395                 File JavaDoc[] sortedArray = (File JavaDoc[])sortedSet.toArray(new File JavaDoc[sortedSet.size()]);
396                 for (int i=start; i<(start + max) && i<(sortedArray.length); i++) {
397                     Entry entry = createAtomResourceEntry(website, sortedArray[i]);
398                     atomEntries.add(entry);
399                     if (count == 0) {
400                         // first entry is most recent
401
feed.setUpdated(entry.getUpdated());
402                     }
403                     count++;
404                 }
405                 if (start + count < files.length) { // add next link
406
int nextOffset = start + max;
407                     String JavaDoc url = URLUtilities.getAtomProtocolURL(true)
408                         +"/"+ website.getHandle() + "/resources/" + nextOffset;
409                     Link nextLink = new Link();
410                     nextLink.setRel("next");
411                     nextLink.setHref(url);
412                     List JavaDoc next = new ArrayList JavaDoc();
413                     next.add(nextLink);
414                     feed.setOtherLinks(next);
415                 }
416                 if (start > 0) { // add previous link
417
int prevOffset = start > max ? start - max : 0;
418                     String JavaDoc url = URLUtilities.getAtomProtocolURL(true)
419                         +"/"+website.getHandle() + "/resources/" + prevOffset;
420                     Link prevLink = new Link();
421                     prevLink.setRel("previous");
422                     prevLink.setHref(url);
423                     List JavaDoc prev = new ArrayList JavaDoc();
424                     prev.add(prevLink);
425                     feed.setOtherLinks(prev);
426                 }
427                 feed.setEntries(atomEntries);
428                 return feed;
429             }
430             throw new AtomNotAuthorizedException(
431                 "ERROR: not authorized to access website");
432        
433         } catch (RollerException re) {
434             throw new AtomException("ERROR: getting resource collection");
435         }
436     }
437     
438     //--------------------------------------------------------------------- entries
439

440     /**
441      * Create entry in the entry collection (a Roller blog has only one).
442      */

443     public Entry postEntry(String JavaDoc[] pathInfo, Entry entry) throws AtomException {
444         try {
445             // authenticated client posted a weblog entry
446
String JavaDoc handle = pathInfo[0];
447             WebsiteData website =
448                 mRoller.getUserManager().getWebsiteByHandle(handle);
449             if (website == null) {
450                 throw new AtomNotFoundException("ERROR: cannot find specified weblog");
451             }
452             if (canEdit(website)) {
453                 // Save it and commit it
454
WeblogManager mgr = mRoller.getWeblogManager();
455                 WeblogEntryData rollerEntry = createRollerEntry(website, entry);
456                 rollerEntry.setCreator(this.user);
457                 mgr.saveWeblogEntry(rollerEntry);
458                 mRoller.flush();
459
460                 // Throttle one entry per second
461
// (MySQL timestamp has 1 sec resolution, damnit)
462
try { Thread.sleep(1000); } catch (Exception JavaDoc ignored) {}
463
464                 CacheManager.invalidate(website);
465                 if (rollerEntry.isPublished()) {
466                     mRoller.getIndexManager().addEntryReIndexOperation(rollerEntry);
467                 }
468                 return createAtomEntry(rollerEntry);
469             }
470             throw new AtomNotAuthorizedException(
471                 "ERROR: not authorized to access website");
472             
473         } catch (RollerException re) {
474             throw new AtomException("ERROR: posting entry");
475         }
476     }
477     
478     /**
479      * Retrieve entry, URI like this /blog-name/entry/id
480      */

481     public Entry getEntry(String JavaDoc[] pathInfo) throws AtomException {
482         try {
483             if (pathInfo.length == 3) // URI is /blogname/entries/entryid
484
{
485                 if (pathInfo[1].equals("entry")) {
486                     WeblogEntryData entry =
487                         mRoller.getWeblogManager().getWeblogEntry(pathInfo[2]);
488                     if (entry == null) {
489                         throw new AtomNotFoundException(
490                             "ERROR: cannot find specified entry/resource");
491                     }
492                     if (!canView(entry)) {
493                         throw new AtomNotAuthorizedException(
494                             "ERROR: not authorized to view entry");
495                     } else {
496                         return createAtomEntry(entry);
497                     }
498                 } else if (pathInfo[1].equals("resource") && pathInfo[2].endsWith(".media-link")) {
499                     String JavaDoc fileName =
500                         pathInfo[2].substring(0, pathInfo[2].length() - ".media-link".length());
501                     String JavaDoc handle = pathInfo[0];
502                     WebsiteData website =
503                         mRoller.getUserManager().getWebsiteByHandle(handle);
504                     String JavaDoc uploadPath =
505                         RollerFactory.getRoller().getFileManager().getUploadUrl();
506                     File JavaDoc resource =
507                         new File JavaDoc(uploadPath + File.separator + fileName);
508                     return createAtomResourceEntry(website, resource);
509                 }
510             }
511             throw new AtomNotFoundException(
512                 "ERROR: cannot find specified entry/resource");
513         } catch (RollerException re) {
514             throw new AtomException("ERROR: getting entry");
515         }
516     }
517     
518     /**
519      * Update entry, URI like this /blog-name/entry/id
520      */

521     public Entry putEntry(String JavaDoc[] pathInfo, Entry entry) throws AtomException {
522         try {
523             if (pathInfo.length == 3) // URI is /blogname/entries/entryid
524
{
525                 WeblogEntryData rollerEntry =
526                     mRoller.getWeblogManager().getWeblogEntry(pathInfo[2]);
527                 if (rollerEntry == null) {
528                     throw new AtomNotFoundException(
529                         "ERROR: cannot find specified entry/resource");
530                 }
531                 if (canEdit(rollerEntry)) {
532                     WeblogManager mgr = mRoller.getWeblogManager();
533
534                     WeblogEntryData rawUpdate = createRollerEntry(rollerEntry.getWebsite(), entry);
535                     rollerEntry.setPubTime(rawUpdate.getPubTime());
536                     rollerEntry.setUpdateTime(rawUpdate.getUpdateTime());
537                     rollerEntry.setText(rawUpdate.getText());
538                     rollerEntry.setStatus(rawUpdate.getStatus());
539                     rollerEntry.setCategory(rawUpdate.getCategory());
540                     rollerEntry.setTitle(rawUpdate.getTitle());
541
542                     mgr.saveWeblogEntry(rollerEntry);
543                     mRoller.flush();
544
545                     CacheManager.invalidate(rollerEntry.getWebsite());
546                     if (rollerEntry.isPublished()) {
547                         mRoller.getIndexManager().addEntryReIndexOperation(rollerEntry);
548                     }
549                     return createAtomEntry(rollerEntry);
550                 }
551                 throw new AtomNotAuthorizedException("ERROR not authorized to update entry");
552             }
553             throw new AtomNotFoundException("ERROR: cannot find specified entry/resource");
554             
555         } catch (RollerException re) {
556             throw new AtomException("ERROR: updating entry");
557         }
558     }
559     
560     /**
561      * Delete entry, URI like this /blog-name/entry/id
562      */

563     public void deleteEntry(String JavaDoc[] pathInfo) throws AtomException {
564         try {
565             if (pathInfo.length == 3) // URI is /blogname/entry/entryid
566
{
567                 if (pathInfo[1].equals("entry")) {
568                     WeblogEntryData rollerEntry = mRoller.getWeblogManager().getWeblogEntry(pathInfo[2]);
569                     if (rollerEntry == null) {
570                         throw new AtomNotFoundException("ERROR: cannot find specified entry/resource");
571                     }
572                     if (canEdit(rollerEntry)) {
573                         WeblogManager mgr = mRoller.getWeblogManager();
574                         mgr.removeWeblogEntry(rollerEntry);
575                         mRoller.flush();
576                         CacheManager.invalidate(rollerEntry.getWebsite());
577                         mRoller.getIndexManager().removeEntryIndexOperation(rollerEntry);
578                         return;
579                     }
580                 } else if (pathInfo[1].equals("resource")) {
581                     String JavaDoc handle = pathInfo[0];
582                     WebsiteData website = mRoller.getUserManager().getWebsiteByHandle(handle);
583                     if (website == null) {
584                         throw new AtomNotFoundException("ERROR: cannot find specified weblog");
585                     }
586                     if (canEdit(website) && pathInfo.length > 1) {
587                         try {
588                             String JavaDoc fileName = pathInfo[2];
589                             if (pathInfo[2].endsWith(".media-link")) {
590                                 fileName = fileName.substring(0, pathInfo[2].length() - ".media-link".length());
591                             }
592                             FileManager fmgr = mRoller.getFileManager();
593                             fmgr.deleteFile(website.getHandle(), fileName);
594                         } catch (Exception JavaDoc e) {
595                             String JavaDoc msg = "ERROR in atom.deleteResource";
596                             mLogger.error(msg,e);
597                             throw new AtomException(msg);
598                         }
599                         return;
600                     }
601                 }
602                 throw new AtomNotAuthorizedException("ERROR not authorized to delete entry");
603             }
604             throw new AtomNotFoundException("ERROR: cannot find specified entry/resource");
605             
606         } catch (RollerException re) {
607             throw new AtomException("ERROR: deleting entry");
608         }
609     }
610     
611     //-------------------------------------------------------------------- resources
612

613     /**
614      * Create new resource in generic collection (a Roller blog has only one).
615      * TODO: can we avoid saving temporary file?
616      * TODO: do we need to handle mutli-part MIME uploads?
617      * TODO: use Jakarta Commons File-upload?
618      */

619     public Entry postMedia(String JavaDoc[] pathInfo,
620             String JavaDoc title, String JavaDoc slug, String JavaDoc contentType, InputStream JavaDoc is)
621             throws AtomException {
622         try {
623             // authenticated client posted a weblog entry
624
File JavaDoc tempFile = null;
625             RollerMessages msgs = new RollerMessages();
626             String JavaDoc handle = pathInfo[0];
627             WebsiteData website =
628                 mRoller.getUserManager().getWebsiteByHandle(handle);
629             if (canEdit(website) && pathInfo.length > 1) {
630                 // save to temp file
631
String JavaDoc fileName = createFileName(website, (slug != null) ? slug : title, contentType);
632                 try {
633                     FileManager fmgr = mRoller.getFileManager();
634                     tempFile = File.createTempFile(fileName, "tmp");
635                     FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc(tempFile);
636                     Utilities.copyInputToOutput(is, fos);
637                     fos.close();
638
639                     // If save is allowed by Roller system-wide policies
640
if (fmgr.canSave(website.getHandle(), fileName, contentType, tempFile.length(), msgs)) {
641                         // Then save the file
642
FileInputStream JavaDoc fis = new FileInputStream JavaDoc(tempFile);
643                         fmgr.saveFile(website.getHandle(), fileName, contentType, tempFile.length(), fis);
644                         fis.close();
645
646                         File JavaDoc resource = new File JavaDoc(fmgr.getUploadDir() + File.separator + fileName);
647                         return createAtomResourceEntry(website, resource);
648                     }
649
650                 } catch (IOException JavaDoc e) {
651                     String JavaDoc msg = "ERROR reading posted file";
652                     mLogger.error(msg,e);
653                     throw new AtomException(msg, e);
654                 } finally {
655                     if (tempFile != null) tempFile.delete();
656                 }
657             }
658             // TODO: AtomUnsupportedMediaType and AtomRequestEntityTooLarge needed?
659
throw new AtomException("File upload denied because:" + msgs.toString());
660         
661         } catch (RollerException re) {
662             throw new AtomException("ERROR: posting media");
663         }
664     }
665     
666     /**
667      * Creates a file name for a file based on a weblog, title string and a
668      * content-type.
669      *
670      * @param weblog Weblog for which file name is being created
671      * @param title Title to be used as basis for file name (or null)
672      * @param contentType Content type of file (must not be null)
673      *
674      * If a title is specified, the method will apply the same create-anchor
675      * logic we use for weblog entries to create a file name based on the title.
676      *
677      * If title is null, the base file name will be the weblog handle plus a
678      * YYYYMMDDHHSS timestamp.
679      *
680      * The extension will be formed by using the part of content type that
681      * comes after he slash.
682      *
683      * For example:
684      * weblog.handle = "daveblog"
685      * title = "Port Antonio"
686      * content-type = "image/jpg"
687      * Would result in port_antonio.jpg
688      *
689      * Another example:
690      * weblog.handle = "daveblog"
691      * title = null
692      * content-type = "image/jpg"
693      * Might result in daveblog-200608201034.jpg
694      */

695     private String JavaDoc createFileName(WebsiteData weblog, String JavaDoc title, String JavaDoc contentType) {
696         
697         if (weblog == null) throw new IllegalArgumentException JavaDoc("weblog cannot be null");
698         if (contentType == null) throw new IllegalArgumentException JavaDoc("contentType cannot be null");
699         
700         String JavaDoc fileName = null;
701         
702         // Determine the extension based on the contentType. This is a hack.
703
// The info we need to map from contentType to file extension is in
704
// JRE/lib/content-type.properties, but Java Activation doesn't provide
705
// a way to do a reverse mapping or to get at the data.
706
String JavaDoc[] typeTokens = contentType.split("/");
707         String JavaDoc ext = typeTokens[1];
708         
709         if (title != null && !title.trim().equals("")) {
710             // We've got a title, so use it to build file name
711
String JavaDoc base = Utilities.replaceNonAlphanumeric(title, ' ');
712             StringTokenizer JavaDoc toker = new StringTokenizer JavaDoc(base);
713             String JavaDoc tmp = null;
714             int count = 0;
715             while (toker.hasMoreTokens() && count < 5) {
716                 String JavaDoc s = toker.nextToken();
717                 s = s.toLowerCase();
718                 tmp = (tmp == null) ? s : tmp + "_" + s;
719                 count++;
720             }
721             fileName = tmp + "." + ext;
722             
723         } else {
724             // No title or text, so instead we'll use the item's date
725
// in YYYYMMDD format to form the file name
726
SimpleDateFormat JavaDoc sdf = new SimpleDateFormat JavaDoc();
727             sdf.applyPattern("yyyyMMddHHSS");
728             fileName = weblog.getHandle()+"-"+sdf.format(new Date JavaDoc())+"."+ext;
729         }
730         
731         return fileName;
732     }
733     
734     
735     /**
736      * Update resource specified by pathInfo using data from input stream.
737      * Expects pathInfo of form /blog-name/resource/name
738      */

739     public Entry putMedia(String JavaDoc[] pathInfo,
740             String JavaDoc contentType, InputStream JavaDoc is) throws AtomException {
741         if (pathInfo.length > 2) {
742             String JavaDoc name = pathInfo[2];
743             return postMedia(pathInfo, name, name, contentType, is);
744         }
745         throw new AtomException("ERROR: bad pathInfo");
746     }
747             
748     //------------------------------------------------------------------ URI testers
749

750     /**
751      * True if URL is the introspection URI.
752      */

753     public boolean isIntrospectionURI(String JavaDoc[] pathInfo) {
754         if (pathInfo.length==0) return true;
755         return false;
756     }
757     
758     /**
759      * True if URL is a entry URI.
760      */

761     public boolean isEntryURI(String JavaDoc[] pathInfo) {
762         if (pathInfo.length > 2 && pathInfo[1].equals("entry")) return true;
763         if (pathInfo.length > 2 && pathInfo[1].equals("resource")) return true;
764         return false;
765     }
766         
767     /**
768      * True if URL is media edit URI. Media can be udpated, but not metadata.
769      */

770     public boolean isMediaEditURI(String JavaDoc[] pathInfo) {
771         if (pathInfo.length > 1 && pathInfo[1].equals("resource")) return true;
772         return false;
773     }
774     
775     /**
776      * True if URL is a category URI.
777      */

778     public boolean isCategoryURI(String JavaDoc[] pathInfo) {
779         if (pathInfo.length > 1 && pathInfo[1].equals("category")) return true;
780         return false;
781     }
782     
783     /**
784      * True if URL is a collection URI of any sort.
785      */

786     public boolean isCollectionURI(String JavaDoc[] pathInfo) {
787         if (pathInfo.length > 1 && pathInfo[1].equals("entries")) return true;
788         if (pathInfo.length > 1 && pathInfo[1].equals("resources")) return true;
789         if (pathInfo.length > 1 && pathInfo[1].equals("categories")) return true;
790         return false;
791     }
792     
793     //------------------------------------------------------------------ permissions
794

795     /**
796      * Return true if user is allowed to edit an entry.
797      */

798     private boolean canEdit(WeblogEntryData entry) {
799         try {
800             return entry.hasWritePermissions(this.user);
801         } catch (Exception JavaDoc e) {
802             mLogger.error("ERROR: checking website.canSave()");
803         }
804         return false;
805     }
806     
807     /**
808      * Return true if user is allowed to create/edit weblog entries and file uploads in a website.
809      */

810     private boolean canEdit(WebsiteData website) {
811         try {
812             return website.hasUserPermissions(this.user, PermissionsData.AUTHOR);
813         } catch (Exception JavaDoc e) {
814             mLogger.error("ERROR: checking website.hasUserPermissions()");
815         }
816         return false;
817     }
818     
819     /**
820      * Return true if user is allowed to view an entry.
821      */

822     private boolean canView(WeblogEntryData entry) {
823         return canEdit(entry);
824     }
825     
826     /**
827      * Return true if user is allowed to view a website.
828      */

829     private boolean canView(WebsiteData website) {
830         return canEdit(website);
831     }
832     
833     //-------------------------------------------------------------- authentication
834

835     /**
836      * Perform WSSE authentication based on information in request.
837      * Will not work if Roller password encryption is turned on.
838      */

839     protected String JavaDoc authenticateWSSE(HttpServletRequest JavaDoc request) {
840         String JavaDoc wsseHeader = request.getHeader("X-WSSE");
841         if (wsseHeader == null) return null;
842         
843         String JavaDoc ret = null;
844         String JavaDoc userName = null;
845         String JavaDoc created = null;
846         String JavaDoc nonce = null;
847         String JavaDoc passwordDigest = null;
848         String JavaDoc[] tokens = wsseHeader.split(",");
849         for (int i = 0; i < tokens.length; i++) {
850             int index = tokens[i].indexOf('=');
851             if (index != -1) {
852                 String JavaDoc key = tokens[i].substring(0, index).trim();
853                 String JavaDoc value = tokens[i].substring(index + 1).trim();
854                 value = value.replaceAll("\"", "");
855                 if (key.startsWith("UsernameToken")) {
856                     userName = value;
857                 } else if (key.equalsIgnoreCase("nonce")) {
858                     nonce = value;
859                 } else if (key.equalsIgnoreCase("passworddigest")) {
860                     passwordDigest = value;
861                 } else if (key.equalsIgnoreCase("created")) {
862                     created = value;
863                 }
864             }
865         }
866         String JavaDoc digest = null;
867         try {
868             UserData user = mRoller.getUserManager().getUserByUserName(userName);
869             digest = WSSEUtilities.generateDigest(
870                     WSSEUtilities.base64Decode(nonce),
871                     created.getBytes("UTF-8"),
872                     user.getPassword().getBytes("UTF-8"));
873             if (digest.equals(passwordDigest)) {
874                 ret = userName;
875             }
876         } catch (Exception JavaDoc e) {
877             mLogger.error("ERROR in wsseAuthenticataion: " + e.getMessage(), e);
878         }
879         return ret;
880     }
881     
882     /**
883      * BASIC authentication.
884      */

885     public String JavaDoc authenticateBASIC(HttpServletRequest JavaDoc request) {
886         boolean valid = false;
887         String JavaDoc userID = null;
888         String JavaDoc password = null;
889         try {
890             String JavaDoc authHeader = request.getHeader("Authorization");
891             if (authHeader != null) {
892                 StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(authHeader);
893                 if (st.hasMoreTokens()) {
894                     String JavaDoc basic = st.nextToken();
895                     if (basic.equalsIgnoreCase("Basic")) {
896                         String JavaDoc credentials = st.nextToken();
897                         String JavaDoc userPass = new String JavaDoc(Base64.decodeBase64(credentials.getBytes()));
898                         int p = userPass.indexOf(":");
899                         if (p != -1) {
900                             userID = userPass.substring(0, p);
901                             UserData user = mRoller.getUserManager().getUserByUserName(userID);
902                             boolean enabled = user.getEnabled().booleanValue();
903                             if (enabled) {
904                                 // are passwords encrypted?
905
RollerContext rollerContext =
906                                         RollerContext.getRollerContext();
907                                 String JavaDoc encrypted =
908                                         RollerConfig.getProperty("passwds.encryption.enabled");
909                                 password = userPass.substring(p+1);
910                                 if ("true".equalsIgnoreCase(encrypted)) {
911                                     password = Utilities.encodePassword(password,
912                                             RollerConfig.getProperty("passwds.encryption.algorithm"));
913                                 }
914                                 valid = user.getPassword().equals(password);
915                             }
916                         }
917                     }
918                 }
919             }
920         } catch (Exception JavaDoc e) {
921             mLogger.debug(e);
922         }
923         if (valid) return userID;
924         return null;
925     }
926     
927     //----------------------------------------------------------- internal utilities
928

929     /**
930      * Create a Rome Atom entry based on a Roller entry.
931      * Content is escaped.
932      * Link is stored as rel=alternate link.
933      */

934     private Entry createAtomEntry(WeblogEntryData entry) {
935         Entry atomEntry = new Entry();
936         Content content = new Content();
937         content.setType(Content.HTML);
938         content.setValue(entry.getText());
939         List JavaDoc contents = new ArrayList JavaDoc();
940         contents.add(content);
941         
942         String JavaDoc absUrl = RollerRuntimeConfig.getAbsoluteContextURL();
943         atomEntry.setId( absUrl + entry.getPermaLink());
944         atomEntry.setTitle( entry.getTitle());
945         atomEntry.setContents( contents);
946         atomEntry.setPublished( entry.getPubTime());
947         atomEntry.setUpdated( entry.getUpdateTime());
948         
949         UserData creator = entry.getCreator();
950         Person author = new Person();
951         author.setName( creator.getUserName());
952         author.setEmail( creator.getEmailAddress());
953         atomEntry.setAuthors( Collections.singletonList(author));
954         
955         List JavaDoc categories = new ArrayList JavaDoc();
956         Category atomCat = new Category();
957         atomCat.setTerm(entry.getCategory().getPath());
958         categories.add(atomCat);
959         atomEntry.setCategories(categories);
960         
961         Link altlink = new Link();
962         altlink.setRel("alternate");
963         altlink.setHref(absUrl + entry.getPermaLink());
964         List JavaDoc altlinks = new ArrayList JavaDoc();
965         altlinks.add(altlink);
966         atomEntry.setAlternateLinks(altlinks);
967         
968         Link editlink = new Link();
969         editlink.setRel("edit");
970         editlink.setHref(
971                 URLUtilities.getAtomProtocolURL(true)
972                 +"/"+entry.getWebsite().getHandle() + "/entry/" + entry.getId());
973         List JavaDoc otherlinks = new ArrayList JavaDoc();
974         otherlinks.add(editlink);
975         atomEntry.setOtherLinks(otherlinks);
976         
977         List JavaDoc modules = new ArrayList JavaDoc();
978         PubControlModule pubControl = new PubControlModuleImpl();
979         pubControl.setDraft(
980                 !WeblogEntryData.PUBLISHED.equals(entry.getStatus()));
981         modules.add(pubControl);
982         atomEntry.setModules(modules);
983         
984         return atomEntry;
985     }
986     
987     private Entry createAtomResourceEntry(WebsiteData website, File JavaDoc file) {
988         String JavaDoc absUrl = RollerRuntimeConfig.getAbsoluteContextURL();
989         String JavaDoc editURI =
990                 URLUtilities.getAtomProtocolURL(true)+"/"+website.getHandle()
991                 + "/resource/" + file.getName() + ".media-link";
992         String JavaDoc editMediaURI =
993                 URLUtilities.getAtomProtocolURL(true)+"/"+ website.getHandle()
994                 + "/resource/" + file.getName();
995         String JavaDoc viewURI = absUrl
996                 + "/resources/" + website.getHandle()
997                 + "/" + file.getName();
998         
999         FileTypeMap JavaDoc map = FileTypeMap.getDefaultFileTypeMap();
1000        // TODO: figure out why PNG is missing from Java MIME types
1001
if (map instanceof MimetypesFileTypeMap JavaDoc) {
1002            try {
1003                ((MimetypesFileTypeMap JavaDoc)map).addMimeTypes("image/png png PNG");
1004            } catch (Exception JavaDoc ignored) {}
1005        }
1006        String JavaDoc contentType = map.getContentType(file);
1007        
1008        Entry entry = new Entry();
1009        entry.setId(editMediaURI);
1010        entry.setTitle(file.getName());
1011        entry.setUpdated(new Date JavaDoc(file.lastModified()));
1012        
1013        List JavaDoc otherlinks = new ArrayList JavaDoc();
1014        entry.setOtherLinks(otherlinks);
1015        Link editlink = new Link();
1016            editlink.setRel("edit");
1017            editlink.setHref(editURI);
1018            otherlinks.add(editlink);
1019        Link editMedialink = new Link();
1020            editMedialink.setRel("edit-media");
1021            editMedialink.setHref(editMediaURI);
1022            otherlinks.add(editMedialink);
1023        
1024        Content content = new Content();
1025        content.setSrc(viewURI);
1026        content.setType(contentType);
1027        List JavaDoc contents = new ArrayList JavaDoc();
1028        contents.add(content);
1029        entry.setContents(contents);
1030        
1031        return entry;
1032    }
1033    
1034    /**
1035     * Create a Roller weblog entry based on a Rome Atom entry object
1036     */

1037    private WeblogEntryData createRollerEntry(WebsiteData website, Entry entry)
1038    throws RollerException {
1039        
1040        Timestamp JavaDoc current = new Timestamp JavaDoc(System.currentTimeMillis());
1041        Timestamp JavaDoc pubTime = current;
1042        Timestamp JavaDoc updateTime = current;
1043        if (entry.getPublished() != null) {
1044            pubTime = new Timestamp JavaDoc( entry.getPublished().getTime() );
1045        }
1046        if (entry.getUpdated() != null) {
1047            updateTime = new Timestamp JavaDoc( entry.getUpdated().getTime() );
1048        }
1049        WeblogEntryData rollerEntry = new WeblogEntryData();
1050        rollerEntry.setTitle(entry.getTitle());
1051        if (entry.getContents() != null && entry.getContents().size() > 0) {
1052            Content content = (Content)entry.getContents().get(0);
1053            rollerEntry.setText(content.getValue());
1054        }
1055        rollerEntry.setPubTime(pubTime);
1056        rollerEntry.setUpdateTime(updateTime);
1057        rollerEntry.setWebsite(website);
1058        
1059        PubControlModule control =
1060                (PubControlModule)entry.getModule("http://purl.org/atom/app#");
1061        if (control!=null && control.getDraft()) {
1062            rollerEntry.setStatus(WeblogEntryData.DRAFT);
1063        } else {
1064            rollerEntry.setStatus(WeblogEntryData.PUBLISHED);
1065        }
1066        
1067        // Atom supports multiple cats, Roller supports one/entry
1068
// so here we take accept the first category that exists
1069
List JavaDoc categories = entry.getCategories();
1070        if (categories != null && categories.size() > 0) {
1071            for (int i=0; i<categories.size(); i++) {
1072                Category cat = (Category)categories.get(i);
1073                // Caller has no way of knowing our categories, so be lenient here
1074
String JavaDoc catString = cat.getTerm() != null ? cat.getTerm() : cat.getLabel();
1075                if (catString != null) {
1076                    WeblogCategoryData rollerCat =
1077                            mRoller.getWeblogManager().getWeblogCategoryByPath(
1078                            website, catString);
1079                    if (rollerCat != null) {
1080                        // Found a valid category, so break out
1081
rollerEntry.setCategory(rollerCat);
1082                        break;
1083                    }
1084                }
1085            }
1086        }
1087        if (rollerEntry.getCategory() == null) {
1088            // no category? fall back to the default Blogger API category
1089
rollerEntry.setCategory(website.getBloggerCategory());
1090        }
1091        return rollerEntry;
1092    }
1093    
1094}
1095
Popular Tags