diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedGraphicImageRenderer.java b/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedGraphicImageRenderer.java index eee862e..162d398 100644 --- a/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedGraphicImageRenderer.java +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedGraphicImageRenderer.java @@ -1,23 +1,24 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package be.rubus.web.jsf.primefaces; +import be.rubus.web.jsf.primefaces.util.StringEncrypter; import org.primefaces.component.graphicimage.GraphicImage; import org.primefaces.component.graphicimage.GraphicImageRenderer; import org.primefaces.model.StreamedContent; @@ -29,80 +30,97 @@ import javax.faces.component.UIParameter; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; /** */ public class AdvancedGraphicImageRenderer extends GraphicImageRenderer { - private boolean determineIfAdvancedRendering(GraphicImage image) { - boolean result = false; - - boolean isStreamedContent = image.getValue() instanceof StreamedContent; - Boolean advancedMarker = (Boolean) image.getAttributes().get(AdvancedRendererHandler.ADVANCED_RENDERING); - - if (isStreamedContent && advancedMarker == null) { - result = determineSpecificParents(image); - } - - if (!result && advancedMarker != null && advancedMarker) { - result = true; - } - - return result; - } - - private boolean determineSpecificParents(GraphicImage image) { - boolean result = false; - UIComponent current = image; - while (!result && !(current instanceof UIViewRoot)) { - result = current instanceof UIData; - if (!result) { - result = UIComponent.isCompositeComponent(current); - } - current = current.getParent(); - } - return result; - } - - @Override - protected String getImageSrc(FacesContext context, GraphicImage image) { - if (determineIfAdvancedRendering(image)) { - String src; - Object value = image.getValue(); - StreamedContent streamedContent = (StreamedContent) value; - - - Resource resource = context.getApplication().getResourceHandler() - .createResource("dynamiccontent", "advancedPrimefaces", streamedContent.getContentType()); - String resourcePath = resource.getRequestPath(); - String rid = createUniqueContentId(context); - StringBuilder builder = new StringBuilder(resourcePath); - GraphicImageManager graphicImageManager = GraphicImageUtil.retrieveManager(context); - graphicImageManager.registerImage(streamedContent, rid); - - builder.append("&").append(Constants.DYNAMIC_CONTENT_PARAM).append("=").append(rid); - - for (UIComponent kid : image.getChildren()) { - if (kid instanceof UIParameter) { - UIParameter param = (UIParameter) kid; - - builder.append("&").append(param.getName()).append("=").append(param.getValue()); - } - } - - src = builder.toString(); - - if (!image.isCache()) { - src += src.contains("?") ? "&" : "?"; - src += "primefaces_image=" + UUID.randomUUID().toString(); - } - - src = context.getExternalContext().encodeResourceURL(src); - return src; - - } else { - return super.getImageSrc(context, image); - } - } + private static final Logger LOG = Logger.getLogger(AdvancedGraphicImageRenderer.class.getName()); + private static final StringEncrypter ENCRYPTER = new StringEncrypter(AdvancedGraphicImageRenderer.class.getName()); + + private boolean determineIfAdvancedRendering(GraphicImage image) { + boolean result = false; + + boolean isStreamedContent = image.getValue() instanceof StreamedContent; + Boolean advancedMarker = (Boolean) image.getAttributes().get(AdvancedRendererHandler.ADVANCED_RENDERING); + + if (isStreamedContent && advancedMarker == null) { + result = determineSpecificParents(image); + } + + if (!result && advancedMarker != null && advancedMarker) { + result = true; + } + + return result; + } + + private boolean determineSpecificParents(GraphicImage image) { + boolean result = false; + UIComponent current = image; + while (!result && !(current instanceof UIViewRoot)) { + result = current instanceof UIData; + if (!result) { + result = UIComponent.isCompositeComponent(current); + } + current = current.getParent(); + } + return result; + } + + @Override + protected String getImageSrc(FacesContext context, GraphicImage image) { + if (determineIfAdvancedRendering(image)) { + String src; + Object value = image.getValue(); + StreamedContent streamedContent = (StreamedContent) value; + + + Resource resource = context.getApplication().getResourceHandler() + .createResource("dynamiccontent", "advancedPrimefaces", streamedContent.getContentType()); + String resourcePath = resource.getRequestPath(); + + String uniqueId = String.format("%1$s_%2$s", image.getClientId(context), image.getValueExpression("value").getExpressionString()); + String encrypted = ENCRYPTER.encrypt(uniqueId); + String rid = null; + try { + rid = URLEncoder.encode(encrypted, "UTF-8"); + } catch (UnsupportedEncodingException e) { + // Should never happen + LOG.log(Level.SEVERE, e.getMessage()); + } + + StringBuilder builder = new StringBuilder(resourcePath); + GraphicImageManager graphicImageManager = GraphicImageUtil.retrieveManager(context); + graphicImageManager.registerImage(streamedContent, rid); + + builder.append("&").append(Constants.DYNAMIC_CONTENT_PARAM).append("=").append(rid); + + for (UIComponent kid : image.getChildren()) { + if (kid instanceof UIParameter) { + UIParameter param = (UIParameter) kid; + + builder.append("&").append(param.getName()).append("=").append(param.getValue()); + } + } + + src = builder.toString(); + + if (!image.isCache()) { + src += src.contains("?") ? "&" : "?"; + src += "primefaces_image=" + UUID.randomUUID().toString(); + } + + src = context.getExternalContext().encodeResourceURL(src); + return src; + + } else { + return super.getImageSrc(context, image); + } + } } diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedRendererHandler.java b/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedRendererHandler.java index cd07aa4..16d5830 100644 --- a/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedRendererHandler.java +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedRendererHandler.java @@ -1,20 +1,20 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package be.rubus.web.jsf.primefaces; @@ -29,18 +29,18 @@ */ public class AdvancedRendererHandler extends TagHandler { - public static final String ADVANCED_RENDERING = "ADVANCED_RENDERING"; + public static final String ADVANCED_RENDERING = "ADVANCED_RENDERING"; - private Boolean needAdvancedRendering; + private Boolean needAdvancedRendering; - public AdvancedRendererHandler(TagConfig config) { - super(config); - TagAttribute advancedRendering = config.getTag().getAttributes().get("value"); - needAdvancedRendering = Boolean.valueOf(advancedRendering.getValue()); - } + public AdvancedRendererHandler(TagConfig config) { + super(config); + TagAttribute advancedRendering = config.getTag().getAttributes().get("value"); + needAdvancedRendering = Boolean.valueOf(advancedRendering.getValue()); + } - @Override - public void apply(FaceletContext ctx, UIComponent parent) throws IOException { - parent.getAttributes().put(ADVANCED_RENDERING, needAdvancedRendering); - } + @Override + public void apply(FaceletContext ctx, UIComponent parent) throws IOException { + parent.getAttributes().put(ADVANCED_RENDERING, needAdvancedRendering); + } } diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageManager.java b/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageManager.java index 703fb57..0ec6500 100644 --- a/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageManager.java +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageManager.java @@ -1,20 +1,20 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package be.rubus.web.jsf.primefaces; @@ -25,19 +25,15 @@ import javax.faces.bean.SessionScoped; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** */ @@ -45,74 +41,82 @@ @ManagedBean(name = "GraphicImageManager") public class GraphicImageManager implements HttpSessionBindingListener { - private Map storedContent = new HashMap(); + private static final Logger LOG = Logger.getLogger(GraphicImageManager.class.getName()); - @Override - public void valueBound(HttpSessionBindingEvent event) { - // No action required - } + private Map storedContent = new HashMap(); - @Override - public void valueUnbound(HttpSessionBindingEvent event) { - for (String tempFile : storedContent.values()) { - File f = new File(tempFile); - f.delete(); - } - } + @Override + public void valueBound(HttpSessionBindingEvent event) { + // No action required + } - public void registerImage(StreamedContent content, String uniqueId) { - try { - File tempFile = File.createTempFile(uniqueId, "primefaces"); + @Override + public void valueUnbound(HttpSessionBindingEvent event) { + for (String tempFile : storedContent.values()) { + File f = new File(tempFile); + f.delete(); + } + } - storedContent.put(uniqueId, tempFile.getAbsolutePath()); + public void registerImage(StreamedContent content, String uniqueId) { + try { + if (storedContent.containsKey(uniqueId)) { + return; + // Nothing to do, it is a ViewSCoped bean where we ask an AJAX update. + } - InputStream input = content.getStream(); - OutputStream output = new FileOutputStream(tempFile); - // get a channel from the stream - final ReadableByteChannel inputChannel = Channels.newChannel(input); - final WritableByteChannel outputChannel = Channels.newChannel(output); - // copy the channels - fastChannelCopy(inputChannel, outputChannel); - // closing the channels - inputChannel.close(); - outputChannel.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } + File tempFile = File.createTempFile(uniqueId, "primefaces"); + storedContent.put(uniqueId, tempFile.getAbsolutePath()); - public StreamedContent retrieveImage(String uniqueId) { - StreamedContent result = null; - String tempFile = storedContent.get(uniqueId); - if (tempFile != null) { - File f = new File(tempFile); - try { - result = new DefaultStreamedContent(new FileInputStream(f)); - } catch (FileNotFoundException e) { - // FIXME - e.printStackTrace(); - } - } - return result; - } + InputStream input = content.getStream(); + OutputStream output = new FileOutputStream(tempFile); + // get a channel from the stream + final ReadableByteChannel inputChannel = Channels.newChannel(input); + final WritableByteChannel outputChannel = Channels.newChannel(output); + // copy the channels + fastChannelCopy(inputChannel, outputChannel); + // closing the channels + inputChannel.close(); + outputChannel.close(); + } catch (IOException e) { + // Should not happen + LOG.log(Level.SEVERE, e.getMessage()); + } + } - private static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException { - final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024); - while (src.read(buffer) != -1) { - // prepare the buffer to be drained - buffer.flip(); - // write to the channel, may block - dest.write(buffer); - // If partial transfer, shift remainder down - // If buffer is empty, same as doing clear() - buffer.compact(); - } - // EOF will leave buffer in fill state - buffer.flip(); - // make sure the buffer is fully drained. - while (buffer.hasRemaining()) { - dest.write(buffer); - } - } + + public StreamedContent retrieveImage(String uniqueId) { + StreamedContent result = null; + String tempFile = storedContent.get(uniqueId); + if (tempFile != null) { + File f = new File(tempFile); + try { + result = new DefaultStreamedContent(new FileInputStream(f)); + } catch (FileNotFoundException e) { + // FIXME + LOG.log(Level.SEVERE, e.getMessage()); + } + } + return result; + } + + private static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException { + final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024); + while (src.read(buffer) != -1) { + // prepare the buffer to be drained + buffer.flip(); + // write to the channel, may block + dest.write(buffer); + // If partial transfer, shift remainder down + // If buffer is empty, same as doing clear() + buffer.compact(); + } + // EOF will leave buffer in fill state + buffer.flip(); + // make sure the buffer is fully drained. + while (buffer.hasRemaining()) { + dest.write(buffer); + } + } } diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageUtil.java b/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageUtil.java index 8d07fe2..e46a754 100644 --- a/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageUtil.java +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageUtil.java @@ -1,20 +1,20 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package be.rubus.web.jsf.primefaces; @@ -26,18 +26,18 @@ */ public class GraphicImageUtil { - public static GraphicImageManager retrieveManager(FacesContext context) { - ELContext eLContext = context.getELContext(); - ValueExpression ve = context.getApplication().getExpressionFactory() - .createValueExpression(context.getELContext(), buildElForGraphicImageManager(), GraphicImageManager.class); - return (GraphicImageManager) ve.getValue(eLContext); + public static GraphicImageManager retrieveManager(FacesContext context) { + ELContext eLContext = context.getELContext(); + ValueExpression ve = context.getApplication().getExpressionFactory() + .createValueExpression(context.getELContext(), buildElForGraphicImageManager(), GraphicImageManager.class); + return (GraphicImageManager) ve.getValue(eLContext); - } + } - private static String buildElForGraphicImageManager() { - StringBuilder result = new StringBuilder(); - result.append("#{").append(GraphicImageManager.class.getSimpleName()).append("}"); - return result.toString(); + private static String buildElForGraphicImageManager() { + StringBuilder result = new StringBuilder(); + result.append("#{").append(GraphicImageManager.class.getSimpleName()).append("}"); + return result.toString(); - } + } } diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/ImageResourceHandler.java b/core/src/main/java/be/rubus/web/jsf/primefaces/ImageResourceHandler.java index 6059785..dfc569d 100644 --- a/core/src/main/java/be/rubus/web/jsf/primefaces/ImageResourceHandler.java +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/ImageResourceHandler.java @@ -1,20 +1,20 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package be.rubus.web.jsf.primefaces; @@ -27,52 +27,56 @@ import javax.faces.context.FacesContext; import java.io.IOException; import java.io.InputStream; +import java.net.URLEncoder; import java.util.Map; /** */ public class ImageResourceHandler extends ResourceHandlerWrapper { - private ResourceHandler wrapped; + private ResourceHandler wrapped; - public ImageResourceHandler(ResourceHandler original) { - this.wrapped = original; - } + public ImageResourceHandler(ResourceHandler original) { + this.wrapped = original; + } - @Override - public ResourceHandler getWrapped() { - return wrapped; - } + @Override + public ResourceHandler getWrapped() { + return wrapped; + } - @Override - public void handleResourceRequest(FacesContext context) throws IOException { - Map params = context.getExternalContext().getRequestParameterMap(); - String library = params.get("ln"); - String dynamicContentId = params.get(Constants.DYNAMIC_CONTENT_PARAM); + @Override + public void handleResourceRequest(FacesContext context) throws IOException { + Map params = context.getExternalContext().getRequestParameterMap(); + String library = params.get("ln"); + String dynamicContentId = params.get(Constants.DYNAMIC_CONTENT_PARAM); - if (dynamicContentId != null && library != null && library.equals("advancedPrimefaces")) { - GraphicImageManager graphicImageManager = GraphicImageUtil.retrieveManager(context); - StreamedContent streamedContent = graphicImageManager.retrieveImage(dynamicContentId); + if (dynamicContentId != null && library != null && library.equals("advancedPrimefaces")) { + GraphicImageManager graphicImageManager = GraphicImageUtil.retrieveManager(context); + // Since we encoded it in be.rubus.web.jsf.primefaces.AdvancedGraphicImageRenderer.getImageSrc + // We have to encode it again since the parameter is encoded automatically but we expect the encoded as the fileName + String rid = URLEncoder.encode(dynamicContentId, "UTF-8"); + StreamedContent streamedContent = graphicImageManager.retrieveImage(rid); - ExternalContext externalContext = context.getExternalContext(); - externalContext.setResponseStatus(200); - externalContext.setResponseContentType(streamedContent.getContentType()); + ExternalContext externalContext = context.getExternalContext(); + externalContext.setResponseStatus(200); + externalContext.setResponseContentType(streamedContent.getContentType()); - byte[] buffer = new byte[2048]; + byte[] buffer = new byte[2048]; - int length; - InputStream inputStream = streamedContent.getStream(); - while ((length = (inputStream.read(buffer))) >= 0) { - externalContext.getResponseOutputStream().write(buffer, 0, length); - } + int length; + InputStream inputStream = streamedContent.getStream(); + while ((length = (inputStream.read(buffer))) >= 0) { + externalContext.getResponseOutputStream().write(buffer, 0, length); + } - externalContext.responseFlushBuffer(); - context.responseComplete(); + externalContext.responseFlushBuffer(); + context.responseComplete(); - } else { - getWrapped().handleResourceRequest(context); - } + } else { + getWrapped().handleResourceRequest(context); + } - } + } } diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/util/Base64.java b/core/src/main/java/be/rubus/web/jsf/primefaces/util/Base64.java new file mode 100644 index 0000000..ca406c1 --- /dev/null +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/util/Base64.java @@ -0,0 +1,634 @@ +package be.rubus.web.jsf.primefaces.util; + + +import java.util.Arrays; + +/** + * A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance + * with RFC 2045.

+ * On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster + * on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes) + * compared to sun.misc.Encoder()/Decoder().

+ *

+ * On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and + * about 50% faster for decoding large arrays. This implementation is about twice as fast on very small + * arrays (< 30 bytes). If source/destination is a String this + * version is about three times as fast due to the fact that the Commons Codec result has to be recoded + * to a String from byte[], which is very expensive.

+ *

+ * This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only + * allocates the resulting array. This produces less garbage and it is possible to handle arrays twice + * as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown + * whether Sun's sun.misc.Encoder()/Decoder() produce temporary arrays but since performance + * is quite low it probably does.

+ *

+ * The encoder produces the same output as the Sun one except that the Sun's encoder appends + * a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the + * length and is probably a side effect. Both are in conformance with RFC 2045 though.
+ * Commons codec seem to always att a trailing line separator.

+ *

+ * Note! + * The encode/decode method pairs (types) come in three versions with the exact same algorithm and + * thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different + * format types. The methods not used can simply be commented out.

+ *

+ * There is also a "fast" version of all decode methods that works the same way as the normal ones, but + * har a few demands on the decoded input. Normally though, these fast verions should be used if the source if + * the input is known and it hasn't bee tampered with.

+ *

+ * If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com. + *

+ * Licence (BSD): + * ============== + *

+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com) + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * Neither the name of the MiG InfoCom AB nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * @author Mikael Grev + * Date: 2004-aug-02 + * Time: 11:31:11 + * @version 2.2 + */ + +public class Base64 { + private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + private static final int[] IA = new int[256]; + + static { + Arrays.fill(IA, -1); + for (int i = 0, iS = CA.length; i < iS; i++) + IA[CA[i]] = i; + IA['='] = 0; + } + + // **************************************************************************************** + // * char[] version + // **************************************************************************************** + + /** + * Encodes a raw byte array into a BASE64 char[] representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static char[] encodeToChar(byte[] sArr, boolean lineSep) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) { + return new char[0]; + } + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + char[] dArr = new char[dLen]; + + // Encode even 24-bits + for (int s = 0, d = 0, cc = 0; s < eLen; ) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = CA[(i >>> 18) & 0x3f]; + dArr[d++] = CA[(i >>> 12) & 0x3f]; + dArr[d++] = CA[(i >>> 6) & 0x3f]; + dArr[d++] = CA[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = CA[i >> 12]; + dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** + * Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * + * @param sArr The source array. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(char[] sArr) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) { + return new byte[0]; + } + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[sArr[i]] < 0) { + sepCnt++; + } + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) { + return null; + } + + int pad = 0; + for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0; ) + if (sArr[i] == '=') { + pad++; + } + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[s++]]; + if (c >= 0) { + i |= c << (18 - j * 6); + } else { + j--; + } + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) { + dArr[d++] = (byte) i; + } + } + } + return dArr; + } + + /** + * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(char[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(char[] sArr) { + // Check special case + int sLen = sArr.length; + if (sLen == 0) { + return new byte[0]; + } + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[sArr[sIx]] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[sArr[eIx]] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + // **************************************************************************************** + // * byte[] version + // **************************************************************************************** + + /** + * Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) { + return encodeToByte(sArr, 0, sArr != null ? sArr.length : 0, lineSep); + } + + /** + * Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null an empty array will be returned. + * @param sOff The starting position in the bytes to convert. + * @param sLen The number of bytes to convert. If 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static byte[] encodeToByte(byte[] sArr, int sOff, int sLen, boolean lineSep) { + // Check special case + if (sArr == null || sLen == 0) { + return new byte[0]; + } + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + byte[] dArr = new byte[dLen]; + + // Encode even 24-bits + for (int s = sOff, d = 0, cc = 0; s < sOff + eLen; ) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[d++] = (byte) CA[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't an even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[sOff + eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sOff + sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = (byte) CA[i >> 12]; + dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** + * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(byte[] sArr) { + return decode(sArr, 0, sArr.length); + } + + /** + * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * + * @param sArr The source array. null will throw an exception. + * @param sOff The starting position in the source array. + * @param sLen The number of bytes to decode from the source array. Length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(byte[] sArr, int sOff, int sLen) { + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[sArr[sOff + i] & 0xff] < 0) { + sepCnt++; + } + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) { + return null; + } + + int pad = 0; + for (int i = sLen; i > 1 && IA[sArr[sOff + --i] & 0xff] <= 0; ) + if (sArr[sOff + i] == '=') { + pad++; + } + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[sOff + s++] & 0xff]; + if (c >= 0) { + i |= c << (18 - j * 6); + } else { + j--; + } + } + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) { + dArr[d++] = (byte) i; + } + } + } + + return dArr; + } + + + /** + * Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(byte[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(byte[] sArr) { + // Check special case + int sLen = sArr.length; + if (sLen == 0) { + return new byte[0]; + } + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + // **************************************************************************************** + // * String version + // **************************************************************************************** + + /** + * Encodes a raw byte array into a BASE64 String representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static String encodeToString(byte[] sArr, boolean lineSep) { + // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. + return new String(encodeToChar(sArr, lineSep)); + } + + /** + * Decodes a BASE64 encoded String. All illegal characters will be ignored and can handle both strings with + * and without line separators.
+ * Note! It can be up to about 2x the speed to call decode(str.toCharArray()) instead. That + * will create a temporary array though. This version will use str.charAt(i) to iterate the string. + * + * @param str The source string. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(String str) { + // Check special case + int sLen = str != null ? str.length() : 0; + if (sLen == 0) { + return new byte[0]; + } + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[str.charAt(i)] < 0) { + sepCnt++; + } + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) { + return null; + } + + // Count '=' at end + int pad = 0; + for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0; ) + if (str.charAt(i) == '=') { + pad++; + } + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[str.charAt(s++)]; + if (c >= 0) { + i |= c << (18 - j * 6); + } else { + j--; + } + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) { + dArr[d++] = (byte) i; + } + } + } + return dArr; + } + + /** + * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(String)}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param s The source string. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(String s) { + // Check special case + int sLen = s.length(); + if (sLen == 0) { + return new byte[0]; + } + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[s.charAt(sIx++)] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } +} + diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/util/StringEncrypter.java b/core/src/main/java/be/rubus/web/jsf/primefaces/util/StringEncrypter.java new file mode 100644 index 0000000..257b099 --- /dev/null +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/util/StringEncrypter.java @@ -0,0 +1,127 @@ +package be.rubus.web.jsf.primefaces.util; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.faces.FacesException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.KeySpec; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + */ +public class StringEncrypter { + + private static final Logger LOG = Logger.getLogger(StringEncrypter.class.getName()); + + private Cipher ecipher; + private Cipher dcipher; + + /** + * Constructor used to create this object. Responsible for setting and initializing this object's encrypter and decrypter Chipher instances given a Secret + * Key and algorithm. + * + * @param key Secret Key used to initialize both the encrypter and decrypter instances. + * @param algorithm Which algorithm to use for creating the encrypter and decrypter instances. + */ + public StringEncrypter(SecretKey key, String algorithm) { + try { + ecipher = Cipher.getInstance(algorithm); + dcipher = Cipher.getInstance(algorithm); + ecipher.init(Cipher.ENCRYPT_MODE, key); + dcipher.init(Cipher.DECRYPT_MODE, key); + } catch (Exception e) { + throw new FacesException("Could not initialize Cipher objects", e); + } + } + + /** + * Constructor used to create this object. Responsible for setting and initializing this object's encrypter and decrypter Chipher instances given a Pass + * Phrase and algorithm. + * + * @param passPhrase Pass Phrase used to initialize both the encrypter and decrypter instances. + */ + public StringEncrypter(String passPhrase) { + + // 8-bytes Salt + byte[] salt = { + (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, + (byte) 0x56, (byte) 0x34, (byte) 0xE3, (byte) 0x03 + }; + + // Iteration count + int iterationCount = 19; + + try { + + KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount); + SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec); + + ecipher = Cipher.getInstance("PBEWithMD5AndDES"); + dcipher = Cipher.getInstance("PBEWithMD5AndDES"); + + // Prepare the parameters to the cipthers + AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount); + + ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); + dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec); + + } catch (Exception e) { + throw new FacesException("Could not initialize Cipher objects", e); + } + } + + /** + * Takes a single String as an argument and returns an Encrypted version of that String. + * + * @param str String to be encrypted + * @return String Encrypted version of the provided String + */ + public String encrypt(String str) { + try { + // Encode the string into bytes using utf-8 + byte[] utf8 = str.getBytes("UTF8"); + + // Encrypt + byte[] enc = ecipher.doFinal(utf8); + + // Encode bytes to base64 to get a string + return Base64.encodeToString(enc, false); + + } catch (Exception e) { + LOG.log(Level.WARNING, "Could not encrypt string", e); + } + + return null; + } + + /** + * Takes a encrypted String as an argument, decrypts and returns the decrypted String. + * + * @param str Encrypted String to be decrypted + * @return String Decrypted version of the provided String + */ + public String decrypt(String str) { + + try { + + // Decode base64 to get bytes + byte[] dec = Base64.decode(str); + + // Decrypt + byte[] utf8 = dcipher.doFinal(dec); + + // Decode using utf-8 + return new String(utf8, "UTF8"); + + } catch (Exception e) { + LOG.log(Level.WARNING, "Could not decrypt string", e); + } + + return null; + } +} diff --git a/demo/src/main/java/be/rubus/web/jsf/view/Bean.java b/demo/src/main/java/be/rubus/web/jsf/view/Bean.java index 8a3c13d..22c3a91 100644 --- a/demo/src/main/java/be/rubus/web/jsf/view/Bean.java +++ b/demo/src/main/java/be/rubus/web/jsf/view/Bean.java @@ -24,6 +24,7 @@ import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; import javax.faces.bean.SessionScoped; +import javax.faces.bean.ViewScoped; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import java.io.InputStream; @@ -31,7 +32,7 @@ import java.util.List; @ManagedBean(name = "imageBean") -@RequestScoped +@ViewScoped public class Bean { private StreamedContent image; private List dataList; diff --git a/demo/src/main/webapp/index.xhtml b/demo/src/main/webapp/index.xhtml index 0215606..b601ab2 100644 --- a/demo/src/main/webapp/index.xhtml +++ b/demo/src/main/webapp/index.xhtml @@ -39,6 +39,9 @@ + + +