/* * Copyright (C) 2009 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.services.jcr.webdav.lnkproducer; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Created by The eXo Platform SAS. * @author Vitaly Guly - gavrikvetal@gmail.com * * @version $Id: $ */ public class LinkGenerator { /** * MICROSOFT *.LNK FILE HEADER. */ public static int[] linkHeader = { // 0h 1 dword Always 0000004Ch 'L' 0x4C, 0x00, 0x00, 0x00, // 4h 16 bytes GUID of shortcut files 0x01, 0x14/* 0x04 */, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, // 14h 1 dword Flags 0x81/* 0x00 */, 0x00, 0x00, 0x00, // 18h 1 dword File attributes 0x00, 0x00, 0x00, 0x00, // 1Ch 1 qword Time 1 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 24h 1 qword Time 2 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2Ch 1 qword Time 3 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 34h 1 dword File length 0x00, 0x00, 0x00, 0x00, // 38h 1 dword Icon number 0x00, 0x00, 0x00, 0x00, // 3Ch 1 dword ShowWnd value 0x01, 0x00, 0x00, 0x00, // 40h 1 dword Hot key 0x00, 0x00, 0x00, 0x00, // 44h 2 dwords Unknown, always zero 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /** * Host name. */ private String hostName; /** * Servlet path. */ private String servletPath; /** * Traget path. */ private String targetPath; /** * Constructor. * * @param hostName host name * @param servletPath servlet path * @param targetPath target path */ public LinkGenerator(String hostName, String servletPath, String targetPath) { this.hostName = hostName; this.servletPath = servletPath; this.targetPath = targetPath; } /** * Generates the content of link. * * @return link content * @throws IOException {@link IOException} */ public byte[] generateLinkContent() throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); // LINK HEADER for (int i = 0; i < linkHeader.length; i++) { byte curByteValue = (byte)linkHeader[i]; outStream.write(curByteValue); } // LINK BODY byte[] linkContent = getLinkContent(); writeInt(linkContent.length + 2, outStream); outStream.write(linkContent); // WRITE END LINK FILE for (int i = 0; i < 6; i++) { outStream.write(0); } return outStream.toByteArray(); } /** * Gets the content of the link. * * @return link content * @throws IOException {@link IOException} */ private byte[] getLinkContent() throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] firstItem = getFirstItem(); writeInt(firstItem.length + 2, outStream); writeBytes(firstItem, outStream); byte[] lastItem = getLastItem(); writeInt(lastItem.length + 2, outStream); writeBytes(lastItem, outStream); String[] pathes = servletPath.split("/"); String root = pathes[pathes.length - 1]; byte[] rootItem = getRootItem(root, servletPath); writeInt(rootItem.length + 2, outStream); writeBytes(rootItem, outStream); pathes = targetPath.split("/"); StringBuilder curHref = new StringBuilder(servletPath); for (int i = 0; i < pathes.length; i++) { if ("".equals(pathes[i])) { continue; } String curName = pathes[i]; curHref.append("/").append(curName); if (i < pathes.length - 1) { byte[] linkItem = getHreffedFolder(curName, curHref.toString()); writeInt(linkItem.length + 2, outStream); writeBytes(linkItem, outStream); } else { byte[] linkFile = getHreffedFile(curName, curHref.toString()); writeInt(linkFile.length + 2, outStream); writeBytes(linkFile, outStream); } } return outStream.toByteArray(); } /** * Returns the first item. * * @return first item * @throws IOException {@link IOException} */ private byte[] getFirstItem() throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); int[] firstItem = {0x1F, 0x50, 0xE0, 0x4F, 0xD0, 0x20, 0xEA, 0x3A, 0x69, 0x10, 0xA2, 0xD8, 0x08, 0x00, 0x2B, 0x30, 0x30, 0x9D,}; writeInts(firstItem, outStream); return outStream.toByteArray(); } /** * Returns the last item. * * @return last item * @throws IOException {@link IOException} */ private byte[] getLastItem() throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); int[] lastItem = {0x2E, 0x80, 0x00, 0xDF, 0xEA, 0xBD, 0x65, 0xC2, 0xD0, 0x11, 0xBC, 0xED, 0x00, 0xA0, 0xC9, 0x0A, 0xB5, 0x0F}; writeInts(lastItem, outStream); return outStream.toByteArray(); } /** * Returns the root item. * * @param rootName root item name. * @param servlet servlet name. * @return root item * @throws IOException {@link IOException} */ private byte[] getRootItem(String rootName, String servlet) throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] rootHead = getRootHeader(); writeBytes(rootHead, outStream); byte[] rootValue = getRootValue(rootName); writeInt(rootValue.length / 2, outStream); writeBytes(rootValue, outStream); outStream.write(0); outStream.write(0); writeSizedString(servlet, outStream); writeZeroQWord(outStream); return outStream.toByteArray(); } /** * Returns the root item. * * @return root item * @throws IOException {@link IOException} */ private byte[] getRootHeader() throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); int[] rootHeader = {0x4C, 0x50, 0x00, 0x01, 0x42, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00}; writeInts(rootHeader, outStream); return outStream.toByteArray(); } /** * Returns the root value. * * @param rootName root item name. * @return root item value * @throws IOException {@link IOException} */ private byte[] getRootValue(String rootName) throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); simpleWriteString(rootName, outStream); int[] rootVal = {0x20, 0x00, 0x3D, 0x04, 0x30, 0x04, 0x20, 0x00}; writeInts(rootVal, outStream); simpleWriteString(hostName, outStream); return outStream.toByteArray(); } private byte[] getHreffedFolder(String itemName, String itemHref) throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); int[] linkHrefHeader = {0x4C, 0x50, 0x00, 0x22, 0x42, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x40}; writeInts(linkHrefHeader, outStream); writeSizedString(itemName, outStream); writeSizedString(itemHref, outStream); writeZeroQWord(outStream); return outStream.toByteArray(); } private byte[] getHreffedFile(String itemName, String itemHref) throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); int[] attrs = {0x4C, 0x50, 0x00, 0x22, 0x42, 0x57, 0xF0, 0x43, 0x1C, 0x29, 0xB6, 0x5C, 0xC7, 0x01, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}; writeInts(attrs, outStream); writeSizedString(itemName, outStream); writeSizedString(itemHref, outStream); writeZeroQWord(outStream); return outStream.toByteArray(); } /** * Writes string into stream. * * @param outString string * @param outStream stream * @throws IOException {@link IOException} */ private void simpleWriteString(String outString, OutputStream outStream) throws IOException { for (int i = 0; i < outString.length(); i++) { int charCode = outString.charAt(i); outStream.write(charCode & 0xFF); outStream.write((charCode >> 8) & 0xFF); } } /** * Writes zero-string into stream. * * @param outString string * @param outStream stream * @throws IOException {@link IOException} */ private void writeZeroString(String outString, OutputStream outStream) throws IOException { simpleWriteString(outString, outStream); outStream.write(0); outStream.write(0); } private void writeSizedString(String outString, OutputStream outStream) throws IOException { int stringLength = outString.length(); writeInt(stringLength, outStream); writeZeroString(outString, outStream); } /** * Writes int into stream. * * @param intValue int value * @param outStream stream * @throws IOException {@link IOException} */ private void writeInt(int intValue, OutputStream outStream) throws IOException { outStream.write(intValue & 0xFF); outStream.write((intValue >> 8) & 0xFF); } /** * Writes int array into stream. * * @param bytes int array * @param outStream stream * @throws IOException {@link IOException} */ private void writeInts(int[] bytes, OutputStream outStream) throws IOException { for (int i = 0; i < bytes.length; i++) { byte curByte = (byte)bytes[i]; outStream.write(curByte); } } /** * Writes byte array into stream. * * @param bytes byte array * @param outStream stream * @throws IOException {@link IOException} */ private void writeBytes(byte[] bytes, OutputStream outStream) throws IOException { for (int i = 0; i < bytes.length; i++) { byte curByte = bytes[i]; outStream.write(curByte); } } private void writeZeroQWord(OutputStream outStream) throws IOException { int[] zeroQWord = {0x00, 0x00, 0x00, 0x00}; writeInts(zeroQWord, outStream); } }