commit 7e410fa9877045d09bf90f4b692970f05915a52b Author: Erik C. Thauvin Date: Thu Jul 22 11:26:01 2004 +0000 Initial import. diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..f1fae07 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,11 @@ +build/** +dist/** +dist-signed/** +DevSuite/** +build +dist +dist-signed +DevSuite +buildnum.properties +keystore.properties +ftp.properties \ No newline at end of file diff --git a/LifeBlogger.iml b/LifeBlogger.iml new file mode 100644 index 0000000..239c226 --- /dev/null +++ b/LifeBlogger.iml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LifeBlogger.ipr b/LifeBlogger.ipr new file mode 100644 index 0000000..09008d2 --- /dev/null +++ b/LifeBlogger.ipr @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LifeBlogger.iws b/LifeBlogger.iws new file mode 100644 index 0000000..908ab0e --- /dev/null +++ b/LifeBlogger.iws @@ -0,0 +1,566 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LifeBlogger.jnlp b/LifeBlogger.jnlp new file mode 100644 index 0000000..749f331 --- /dev/null +++ b/LifeBlogger.jnlp @@ -0,0 +1,25 @@ + + + + LifeBlogger + Erik C. Thauvin + + Posts your Lifeblog favorites to your blog + Posts your Lifeblog favorites to your blog + + + + + + + + + + + + + + + + + diff --git a/ant/jreleaseinfo-1.2.0.jar b/ant/jreleaseinfo-1.2.0.jar new file mode 100644 index 0000000..4ff0018 Binary files /dev/null and b/ant/jreleaseinfo-1.2.0.jar differ diff --git a/ant/roxes-ant-tasks-1.2-2004-01-30.jar b/ant/roxes-ant-tasks-1.2-2004-01-30.jar new file mode 100644 index 0000000..8b09fa6 Binary files /dev/null and b/ant/roxes-ant-tasks-1.2-2004-01-30.jar differ diff --git a/build.properties b/build.properties new file mode 100644 index 0000000..679ea00 --- /dev/null +++ b/build.properties @@ -0,0 +1,17 @@ +# Project +proj.name=LifeBlogger +proj.version=0.1.0 +proj.release=false +proj.package=net.thauvin.lifeblogger +proj.run=${proj.package}.LifeBlogger + +# Locations +path.build=build +path.src=src +path.dist=dist +path.lib=lib +path.libexec=libexec +path.ant=ant + +# Jar +jar.name=${proj.name} \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..ad87813 --- /dev/null +++ b/build.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/icon64.gif b/images/icon64.gif new file mode 100644 index 0000000..52bdf4f Binary files /dev/null and b/images/icon64.gif differ diff --git a/images/logo.gif b/images/logo.gif new file mode 100644 index 0000000..d06e456 Binary files /dev/null and b/images/logo.gif differ diff --git a/lib/commons-net-1.2.2.jar b/lib/commons-net-1.2.2.jar new file mode 100644 index 0000000..f633197 Binary files /dev/null and b/lib/commons-net-1.2.2.jar differ diff --git a/lib/sqlite.jar b/lib/sqlite.jar new file mode 100644 index 0000000..8f96c0a Binary files /dev/null and b/lib/sqlite.jar differ diff --git a/lib/thinlet.jar b/lib/thinlet.jar new file mode 100644 index 0000000..6e56529 Binary files /dev/null and b/lib/thinlet.jar differ diff --git a/libexec/sqlite_jni.dll b/libexec/sqlite_jni.dll new file mode 100644 index 0000000..d6a6bfe Binary files /dev/null and b/libexec/sqlite_jni.dll differ diff --git a/src/icon/error.gif b/src/icon/error.gif new file mode 100644 index 0000000..838362d Binary files /dev/null and b/src/icon/error.gif differ diff --git a/src/icon/exit.gif b/src/icon/exit.gif new file mode 100644 index 0000000..624d6ec Binary files /dev/null and b/src/icon/exit.gif differ diff --git a/src/icon/go.gif b/src/icon/go.gif new file mode 100644 index 0000000..2088548 Binary files /dev/null and b/src/icon/go.gif differ diff --git a/src/icon/help.gif b/src/icon/help.gif new file mode 100644 index 0000000..a046f46 Binary files /dev/null and b/src/icon/help.gif differ diff --git a/src/icon/icon.gif b/src/icon/icon.gif new file mode 100644 index 0000000..58e3bdd Binary files /dev/null and b/src/icon/icon.gif differ diff --git a/src/icon/image.gif b/src/icon/image.gif new file mode 100644 index 0000000..0372771 Binary files /dev/null and b/src/icon/image.gif differ diff --git a/src/icon/info.gif b/src/icon/info.gif new file mode 100644 index 0000000..c2a47b6 Binary files /dev/null and b/src/icon/info.gif differ diff --git a/src/icon/logo.gif b/src/icon/logo.gif new file mode 100644 index 0000000..d06e456 Binary files /dev/null and b/src/icon/logo.gif differ diff --git a/src/icon/movie.gif b/src/icon/movie.gif new file mode 100644 index 0000000..824ef6b Binary files /dev/null and b/src/icon/movie.gif differ diff --git a/src/icon/refresh.gif b/src/icon/refresh.gif new file mode 100644 index 0000000..570c61c Binary files /dev/null and b/src/icon/refresh.gif differ diff --git a/src/icon/text.gif b/src/icon/text.gif new file mode 100644 index 0000000..7ec3fde Binary files /dev/null and b/src/icon/text.gif differ diff --git a/src/icon/thinlet.gif b/src/icon/thinlet.gif new file mode 100644 index 0000000..4d9a3b8 Binary files /dev/null and b/src/icon/thinlet.gif differ diff --git a/src/icon/wait.gif b/src/icon/wait.gif new file mode 100644 index 0000000..c91e58e Binary files /dev/null and b/src/icon/wait.gif differ diff --git a/src/net/thauvin/lifeblogger/Base64.java b/src/net/thauvin/lifeblogger/Base64.java new file mode 100644 index 0000000..e7f9c3d --- /dev/null +++ b/src/net/thauvin/lifeblogger/Base64.java @@ -0,0 +1,1343 @@ +package net.thauvin.lifeblogger; + +/** + * Encodes and decodes to and from Base64 notation. + * + *

+ * Change Log: + *

+ * + * + *

+ * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit http://iharder.net/base64 + * periodically to check for updates or to contribute improvements. + *

+ * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.0 + */ +public class Base64 +{ + +/* ******** P U B L I C F I E L D S ******** */ + + + /** No options specified. Value is zero. */ + public final static int NO_OPTIONS = 0; + + /** Specify encoding. */ + public final static int ENCODE = 1; + + + /** Specify decoding. */ + public final static int DECODE = 0; + + + /** Specify that data should be gzip-compressed. */ + public final static int GZIP = 2; + + + /** Don't break lines when encoding (violates strict Base64 specification) */ + public final static int DONT_BREAK_LINES = 8; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private final static int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private final static byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private final static byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private final static String PREFERRED_ENCODING = "UTF-8"; + + + /** The 64 valid Base64 values. */ + private final static byte[] ALPHABET; + private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */ + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + /** Determine which ALPHABET to use. */ + static + { + byte[] __bytes; + try + { + __bytes = new String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").getBytes( PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException use) + { + __bytes = _NATIVE_ALPHABET; // Fall back to native encoding + } // end catch + ALPHABET = __bytes; + } // end static + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private final static byte[] DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + private final static byte BAD_ENCODING = -9; // Indicates error in encoding + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + + /** Defeats instantiation. */ + private Base64(){} + + + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * + * @param threeBytes the array to convert + * @return four byte array in Base64 notation. + * @since 1.3 + */ + private static byte[] encode3to4( byte[] threeBytes ) + { + return encode3to4( threeBytes, 3 ); + } // end encodeToBytes + + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.3 + */ + private static byte[] encode3to4( byte[] threeBytes, int numSigBytes ) + { + byte[] dest = new byte[4]; + encode3to4( threeBytes, 0, numSigBytes, dest, 0 ); + return dest; + } + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes ) + { + encode3to4( threeBytes, 0, numSigBytes, b4, 0 ); + return b4; + } // end encode3to4 + + + /** + * Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset ) + { + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeObject( myObj, Base64.GZIP ) or + *

+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @options Specified options + * @return The Base64-encoded object + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + { + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.io.ObjectOutputStream oos = null; + java.util.zip.GZIPOutputStream gzos = null; + + // Isolate options + int gzip = (options & GZIP); + int dontBreakLines = (options & DONT_BREAK_LINES); + + try + { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); + + // GZip? + if( gzip == GZIP ) + { + gzos = new java.util.zip.GZIPOutputStream( b64os ); + oos = new java.io.ObjectOutputStream( gzos ); + } // end if: gzip + else + oos = new java.io.ObjectOutputStream( b64os ); + + oos.writeObject( serializableObject ); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + return null; + } // end catch + finally + { + try{ oos.close(); } catch( Exception e ){} + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) + { + return encodeBytes( source, 0, source.length, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param options Specified options + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) + { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) + { + return encodeBytes( source, off, len, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param breakLines Break lines at 80 characters or less. + * @param options Specified options + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int off, int len, int options ) + { + // Isolate options + int dontBreakLines = ( options & DONT_BREAK_LINES ); + int gzip = ( options & GZIP ); + + // Compress? + if( gzip == GZIP ) + { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + + try + { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + return null; + } // end catch + finally + { + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else + { + // Convert option to boolean in way that code likes it. + boolean breakLines = dontBreakLines == 0; + + int len43 = len * 4 / 3; + byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) + { + encode3to4( source, d+off, 3, outBuff, e ); + + lineLength += 4; + if( breakLines && lineLength == MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) + { + encode3to4( source, d+off, len - d, outBuff, e ); + e += 4; + } // end if: some padding needed + + + // Return value according to relevant encoding. + try + { + return new String( outBuff, 0, e, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( outBuff, 0, e ); + } // end catch + + } // end else: don't compress + + } // end encodeBytes + + + + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes the first four bytes of array fourBytes + * and returns an array up to three bytes long with the + * decoded values. + * + * @param fourBytes the array with Base64 content + * @return array with decoded values + * @since 1.3 + */ + private static byte[] decode4to3( byte[] fourBytes ) + { + byte[] outBuff1 = new byte[3]; + int count = decode4to3( fourBytes, 0, outBuff1, 0 ); + byte[] outBuff2 = new byte[ count ]; + + for( int i = 0; i < count; i++ ) + outBuff2[i] = outBuff1[i]; + + return outBuff2; + } + + + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the number of decoded bytes converted + * @since 1.3 + */ + private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) + { + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else + { + try{ + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + }catch( Exception e){ + System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); + System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); + System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); + System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); + return -1; + } //e nd catch + } + } // end decodeToBytes + + + + + /** + * Very low-level access to decoding ASCII characters in + * the form of a byte array. Does not support automatically + * gunzipping or any other "fancy" features. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @return decoded data + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len ) + { + int len34 = len * 3 / 4; + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; + + byte[] b4 = new byte[4]; + int b4Posn = 0; + int i = 0; + byte sbiCrop = 0; + byte sbiDecode = 0; + for( i = off; i < off+len; i++ ) + { + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; + + if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better + { + if( sbiDecode >= EQUALS_SIGN_ENC ) + { + b4[ b4Posn++ ] = sbiCrop; + if( b4Posn > 3 ) + { + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) + break; + } // end if: quartet built + + } // end if: equals sign or better + + } // end if: white space, equals sign or better + else + { + System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); + return null; + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s ) + { + byte[] bytes; + try + { + bytes = s.getBytes( PREFERRED_ENCODING ); + } // end try + catch( java.io.UnsupportedEncodingException uee ) + { + bytes = s.getBytes(); + } // end catch + // + + // Decode + bytes = decode( bytes, 0, bytes.length ); + + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + if( bytes.length >= 2 ) + { + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( + bytes != null && // In case decoding returned null + bytes.length >= 4 && // Don't want to get ArrayIndexOutOfBounds exception + java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) + { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try + { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) + { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) + { + // Just return originally-decoded bytes + } // end catch + finally + { + try{ baos.close(); } catch( Exception e ){} + try{ gzis.close(); } catch( Exception e ){} + try{ bais.close(); } catch( Exception e ){} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @since 1.5 + */ + public static Object decodeToObject( String encodedObject ) + { + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try + { + bais = new java.io.ByteArrayInputStream( objBytes ); + ois = new java.io.ObjectInputStream( bais ); + + obj = ois.readObject(); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + obj = null; + } // end catch + catch( java.lang.ClassNotFoundException e ) + { + e.printStackTrace(); + obj = null; + } // end catch + finally + { + try{ bais.close(); } catch( Exception e ){} + try{ ois.close(); } catch( Exception e ){} + } // end finally + + return obj; + } // end decodeObject + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link Base64#InputStream} will read data from another + * {@link java.io.InputStream}, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @see java.io.FilterInputStream + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream + { + private int options; // Options specified + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + + + /** + * Constructs a {@link Base64#InputStream} in DECODE mode. + * + * @param in the {@link java.io.InputStream} from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) + { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link Base64#InputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the {@link java.io.InputStream} from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) + { + super( in ); + this.options = options; + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.breakLines = breakLines; + this.encode = encode; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + public int read() throws java.io.IOException + { + // Do we need to get data? + if( position < 0 ) + { + if( encode ) + { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) + { + try + { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) + { + b3[i] = (byte)b; + numBinaryBytes++; + } // end if: not end of stream + + } // end try: read + catch( java.io.IOException e ) + { + // Only a problem if we got no data at all. + if( i == 0 ) + throw e; + + } // end catch + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) + { + encode3to4( b3, 0, numBinaryBytes, buffer, 0 ); + position = 0; + numSigBytes = 4; + } // end if: got data + else + { + return -1; + } // end else + } // end if: encoding + + // Else decoding + else + { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) + { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) + break; // Reads a -1 if end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) + { + numSigBytes = decode4to3( b4, 0, buffer, 0 ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else + { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) + { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ) + return -1; + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) + { + lineLength = 0; + return '\n'; + } // end if + else + { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) + position = -1; + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else + { + // When JDK1.4 is more accepted, use an assertion here. + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + public int read( byte[] dest, int off, int len ) throws java.io.IOException + { + int i; + int b; + for( i = 0; i < len; i++ ) + { + b = read(); + + //if( b < 0 && i == 0 ) + // return -1; + + if( b >= 0 ) + dest[off + i] = (byte)b; + else if( i == 0 ) + return -1; + else + break; // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link Base64#OutputStream} will write data to another + * {@link java.io.OutputStream}, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @see java.io.FilterOutputStream + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream + { + private int options; + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + + /** + * Constructs a {@link Base64#OutputStream} in ENCODE mode. + * + * @param out the {@link java.io.OutputStream} to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) + { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link Base64#OutputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the {@link java.io.OutputStream} to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) + { + super( out ); + this.options = options; + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + public void write(int theByte) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to encode. + { + out.write( encode3to4( b4, buffer, bufferLength ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else + { + // Meaningful Base64 character? + if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to output. + { + int len = Base64.decode4to3( buffer, 0, b4, 0 ); + out.write( b4, 0, len ); + //out.write( Base64.decode4to3( buffer ) ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) + { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + public void write( byte[] theBytes, int off, int len ) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) + { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + */ + public void flushBase64() throws java.io.IOException + { + if( position > 0 ) + { + if( encode ) + { + out.write( encode3to4( b4, buffer, position ) ); + position = 0; + } // end if: encoding + else + { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + public void close() throws java.io.IOException + { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException + { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() + { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + +} // end class Base64 diff --git a/src/net/thauvin/lifeblogger/LifeBlog.java b/src/net/thauvin/lifeblogger/LifeBlog.java new file mode 100644 index 0000000..9f0ebc7 --- /dev/null +++ b/src/net/thauvin/lifeblogger/LifeBlog.java @@ -0,0 +1,224 @@ +/* + * @(#)LifeBlog.java + * + * Copyright (c) 2004, Erik C. Thauvin (http://www.thauvin.net/erik/) + * 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 authors 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. + * + * $Id$ + * + */ +package net.thauvin.lifeblogger; + +import java.awt.*; + +import java.io.File; +import java.io.IOException; + + +/** + * The LifeBlog abstract class provides the base functionality used by all blog ({@link LifeFTP}, {@link + * LifeMediaObject}, etc.) actions. + * + * @author Erik C. Thauvin + * @version $Revision$, $Date$ + * + * @created Jul 20, 2004 + * @since 1.0 + */ +public abstract class LifeBlog extends Thread +{ + /** + * The file to upload/store. + */ + private final File _file; + + /** + * The Thinlet instance. + */ + private final LifeBlogger _thinlet; + + /** + * The Transfer dialog. + */ + private final Object _dialog; + + /** + * The file name. + */ + private final String _filename; + + /** + * The host name. + */ + private final String _host; + + /** + * The login name. + */ + private final String _login; + + /** + * The password. + */ + private final String _password; + + /** + * The path/location. + */ + private final String _path; + + /** + * Creates a new LifeBlog object. + * + * @param thinlet The Thinlet instance. + * @param host The host. + * @param login The login name. + * @param password The password. + * @param path The path/location. + * @param filename The name of the file to upload/store. + * @param file The file to upload. + * + * @throws IOException If an error occurs while creating the object. + */ + protected LifeBlog(LifeBlogger thinlet, String host, String login, String password, String path, String filename, + File file) + throws IOException + { + _thinlet = thinlet; + _dialog = getThinlet().parse("transfer.xml"); + _host = host; + _login = login; + _password = password; + _path = path; + _filename = filename; + _file = file; + } + + /** + * Performs the action. + * + * @see Thread#run() + */ + public abstract void run(); + + /** + * Returns the Transfer dialog. + * + * @return The dialog. + */ + protected final Object getDialog() + { + return _dialog; + } + + /** + * Returns the file. + * + * @return The file. + */ + protected final File getFile() + { + return _file; + } + + /** + * Returns the file name. + * + * @return The file name. + */ + protected final String getFilename() + { + return _filename; + } + + /** + * Returns the host name. + * + * @return The host. + */ + protected final String getHost() + { + return _host; + } + + /** + * Returns the login name. + * + * @return The login. + */ + protected final String getLogin() + { + return _login; + } + + /** + * Returns the password. + * + * @return The password. + */ + protected final String getPassword() + { + return _password; + } + + /** + * Returns the path/location. + * + * @return The path. + */ + protected final String getPath() + { + return _path; + } + + /** + * Returns the Thinlet instance. + * + * @return The Thinlet. + */ + protected final LifeBlogger getThinlet() + { + return _thinlet; + } + + /** + * Displays an alert message. + * + * @param message The message to display. + */ + protected final void alert(String message) + { + Toolkit.getDefaultToolkit().beep(); + + getThinlet().setIcon(getThinlet().find(getDialog(), "iconlbl"), "icon", getThinlet().getIcon("/icon/error.gif")); + getThinlet().setString(getThinlet().find(getDialog(), "message"), "text", message); + getThinlet().setBoolean(getThinlet().find(getDialog(), "closebtn"), "enabled", true); + } +} diff --git a/src/net/thauvin/lifeblogger/LifeBlogger.java b/src/net/thauvin/lifeblogger/LifeBlogger.java new file mode 100644 index 0000000..7dd3913 --- /dev/null +++ b/src/net/thauvin/lifeblogger/LifeBlogger.java @@ -0,0 +1,598 @@ +/* + * @(#)LifeBlogger.java + * + * Copyright (c) 2004, Erik C. Thauvin (http://www.thauvin.net/erik/) + * 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 authors 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. + * + * $Id$ + * + */ +package net.thauvin.lifeblogger; + +import thinlet.FrameLauncher; +import thinlet.Thinlet; + +import java.awt.*; + +import java.io.*; + +import java.net.URL; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; + +import java.text.SimpleDateFormat; + +import java.util.Properties; + +import javax.swing.*; + + +/** + * The LifeBlogger class uploads/posts Lifeblog's favorite data to a blog. + * + * @author Erik C. Thauvin + * @version $Revision$, $Date$ + * + * @created Jul 19, 2004 + * @since 1.0 + */ +public class LifeBlogger extends Thinlet +{ + private static final String DRIVER = "SQLite.JDBCDriver"; + private static final String PREFS = + System.getProperty("user.home") + File.separator + ReleaseInfo.getProject() + ".properties"; + private static final String JDBC_PREFIX = "jdbc:sqlite:/"; + private static final String DATABASE = "\\DataBase\\NokiaLifeblogDataBase.db"; + private final Properties _prefs = new Properties(); + private File _homeDir = new File(System.getProperty("user.home") + "\\My Documents\\NokiaLifeblogData"); + private String _action; + + /** + * Creates a new LifeBlogger object. + * + * @throws IOException DOCUMENT ME! + */ + private LifeBlogger() + throws IOException + { + setFont(new Font("SansSerif", Font.PLAIN, 11)); + + FileInputStream fis = null; + + try + { + fis = new FileInputStream(PREFS); + _prefs.load(fis); + } + catch (IOException e) + { + e.printStackTrace(System.err); + } + finally + { + if (fis != null) + { + try + { + fis.close(); + } + catch (IOException ignore) + { + ; // Do nothing + } + } + } + + _homeDir = new File(_prefs.getProperty("home", _homeDir.getAbsolutePath())); + _action = _prefs.getProperty("via", "ftp"); + + try + { + Class.forName(DRIVER); + } + catch (Exception e) + { + e.printStackTrace(); + } + + final Object main = parse("main.xml"); + setBoolean(find(main, _action), "selected", true); + add(main); + } + + /** + * The main program. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + new FrameLauncher("LifeBlogger", + (new ImageIcon(LifeBlogger.class.getResource("/icon/icon.gif"))).getImage(), + new LifeBlogger(), 400, 400, true); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * Sets the blog action. + * + * @param action The action + */ + public final void setAction(String action) + { + _action = action; + _prefs.put("via", _action); + savePrefs(); + } + + /** + * Displays the about dialog. + */ + public final void about() + { + try + { + final Object about = parse("about.xml"); + setString(about, "text", "About " + ReleaseInfo.getProject()); + setString(find(about, "version"), "text", + "Version " + ReleaseInfo.getVersion() + " - Build " + ReleaseInfo.getBuildNumber()); + + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + setString(find(about, "date"), "text", sdf.format(ReleaseInfo.getBuildDate())); + add(about); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * Populates the table rows. + * + * @param thinlet The Thinlet object. + * @param table The table to populate. + * @param buttonsPanel The panel containing the buttons/label to update. + * + * @throws Exception If an error occurs while populate the table. + */ + public final void addTableRows(Thinlet thinlet, Object table, Object buttonsPanel) + throws Exception + { + if (!_homeDir.exists()) + { + final JFileChooser fc = new JFileChooser(); + fc.setDialogTitle("Select Nokia LifeBlog document directory:"); + fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + fc.setMultiSelectionEnabled(false); + fc.setAcceptAllFileFilterUsed(false); + + final int res = fc.showOpenDialog(this); + + if (res == JFileChooser.APPROVE_OPTION) + { + _homeDir = fc.getSelectedFile(); + _prefs.put("home", _homeDir.getAbsolutePath()); + + savePrefs(); + } + } + + final Connection con = DriverManager.getConnection(JDBC_PREFIX + _homeDir.getAbsolutePath() + DATABASE); + + final Statement st = con.createStatement(); + final ResultSet rs = + st.executeQuery("SELECT * FROM HooverObject WHERE MobileFavourite = 'true' ORDER BY TimeStamp DESC"); + + Object row; + Object cell; + String ts; + String name; + + boolean first = true; + int found = 0; + + while (rs.next()) + { + ts = rs.getString("TimeStamp"); + name = rs.getString("name"); + + row = Thinlet.create("row"); + + cell = Thinlet.create("cell"); + thinlet.setString(cell, "text", name); + + if (name.toLowerCase().endsWith("jpg")) + { + thinlet.setIcon(cell, "icon", getIcon("/icon/image.gif")); + } + else if (name.toLowerCase().endsWith("3gp")) + { + thinlet.setIcon(cell, "icon", getIcon("/icon/movie.gif")); + } + else + { + thinlet.setIcon(cell, "icon", getIcon("/icon/text.gif")); + } + + thinlet.putProperty(cell, "oid", rs.getString("HooverObjectID")); + thinlet.add(row, cell); + + cell = Thinlet.create("cell"); + thinlet.setString(cell, "text", ts.substring(0, ts.lastIndexOf(':'))); + thinlet.add(row, cell); + + if (first) + { + thinlet.setBoolean(row, "selected", true); + first = false; + } + + thinlet.add(table, row); + + found++; + } + + thinlet.setString(find(buttonsPanel, "favslbl"), "text", "Favorites: " + found); + + st.close(); + rs.close(); + con.close(); + + toggleButton(table, find(buttonsPanel, "blogbtn")); + } + + /** + * Performs the blog action. + * + * @param table The table containing the selected item to perform the action on. + * + * @throws Exception If an error occurs while performing the action. + */ + public final void blog(Object table) + throws Exception + { + final int selected = getSelectedIndex(table); + + if (selected != -1) + { + final Object row = getItem(table, selected); + final String name = String.valueOf(getProperty(getItem(row, 0), "oid")); + + final Connection con = DriverManager.getConnection(JDBC_PREFIX + _homeDir.getAbsolutePath() + DATABASE); + + final Statement st = con.createStatement(); + final ResultSet rs = st.executeQuery("SELECT * FROM BinaryItem WHERE HooverObjectID = " + name); + + if (rs.next()) + { + if ("ftp".equals(_action)) + { + ftpDialog(_homeDir.getAbsolutePath() + "\\DataStore" + rs.getString("Pathname") + + rs.getString("Filename")); + } + else + { + mwDialog(_homeDir.getAbsolutePath() + "\\DataStore" + rs.getString("Pathname") + + rs.getString("Filename"), rs.getString("ObjectMimeType")); + } + } + + st.close(); + rs.close(); + con.close(); + } + } + + /** + * Closes the given dialog. + * + * @param dialog The dialog to close. + */ + public final void closeDialog(Object dialog) + { + remove(dialog); + } + + /** + * Extis the main program. + */ + public final void exit() + { + System.exit(0); + } + + /** + * Preforms the FTP action. + * + * @param dialog The FTP dialog, + * @param ftpPanel The panel contaning the FTP data. + * + * @throws IOException If an error occurs while performing the action. + */ + public final void ftp(Object dialog, Object ftpPanel) + throws IOException + { + final String host = getString(find(ftpPanel, "host"), "text"); + final String login = getString(find(ftpPanel, "login"), "text"); + final String password = getString(find(ftpPanel, "password"), "text"); + final String path = getString(find(ftpPanel, "path"), "text"); + final String filename = getString(find(ftpPanel, "filename"), "text"); + + if (host.length() <= 0) + { + alert("Please specify a host name."); + } + else if (login.length() <= 0) + { + alert("Please specify a login name."); + } + else if (filename.length() <= 0) + { + alert("Please specify a file name."); + } + else + { + _prefs.put("host", host); + _prefs.put("login", login); + _prefs.put("password", password); + _prefs.put("path", path); + + savePrefs(); + + closeDialog(dialog); + + final LifeFTP ftp = + new LifeFTP(this, host, login, password, path, filename, + new File(getString(find(ftpPanel, "file"), "text"))); + ftp.start(); + } + } + + /** + * Preforms the MetaWeblog action. + * + * @param dialog The MetaWeblog dialog, + * @param mwPanel The panel contaning the MetaWeblog data. + * + * @throws IOException If an error occurs while performing the action. + */ + public final void metaWeblog(Object dialog, Object mwPanel) + throws IOException + { + final String host = getString(find(mwPanel, "host"), "text"); + final String login = getString(find(mwPanel, "login"), "text"); + final String password = getString(find(mwPanel, "password"), "text"); + final String filename = getString(find(mwPanel, "filename"), "text"); + final String blogID = getString(find(mwPanel, "blogid"), "text"); + + if (host.length() <= 0) + { + alert("Please specify a XML-RPC URL."); + } + else if (login.length() <= 0) + { + alert("Please specify a login name."); + } + else if (password.length() <= 0) + { + alert("Please specify a password."); + } + else if (filename.length() <= 0) + { + alert("Please specify a file name."); + } + else if (blogID.length() <= 0) + { + alert("Please specify a blog ID."); + } + else + { + _prefs.put("mw-host", host); + _prefs.put("mw-login", login); + _prefs.put("mw-password", password); + _prefs.put("mw-id", blogID); + + savePrefs(); + + closeDialog(dialog); + + final LifeMediaObject mw = + new LifeMediaObject(this, host, blogID, login, password, filename, + String.valueOf(getProperty(find(mwPanel, "file"), "mtype")), + new File(getString(find(mwPanel, "file"), "text"))); + mw.start(); + } + } + + /** + * Toggles the given button based on the specified table selection. + * + * @param table The table. + * @param button The button. + */ + public final void toggleButton(Object table, Object button) + { + setBoolean(button, "enabled", getSelectedIndex(table) != -1); + } + + /** + * Updates the table data. + * + * @param thinlet The Thinlet object. + * @param table The table to update. + * @param buttonsPanel The panel containing the buttons/label to update. + */ + public final void updateTable(Thinlet thinlet, Object table, Object buttonsPanel) + { + thinlet.removeAll(table); + + try + { + addTableRows(thinlet, table, buttonsPanel); + } + catch (Exception e) + { + showException(e); + } + } + + /** + * Displays an exception stacktrace. + * + * @param thr The exception. + */ + protected final void showException(Throwable thr) + { + final StringWriter writer = new StringWriter(); + thr.printStackTrace(new PrintWriter(writer)); + + final String trace = writer.toString().replace('\r', ' ').replace('\t', ' '); + String thrclass = thr.getClass().getName(); + thrclass = thrclass.substring(thrclass.lastIndexOf('.') + 1); + + try + { + final Object dialog = parse("exception.xml"); + setString(dialog, "text", thrclass); + setString(find(dialog, "message"), "text", thr.getMessage()); + setString(find(dialog, "stacktrace"), "text", trace); + + Toolkit.getDefaultToolkit().beep(); + add(dialog); + requestFocus(find(dialog, "closebtn")); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + // Displays an alert. + private void alert(String message) + { + Toolkit.getDefaultToolkit().beep(); + + try + { + final Object alert = parse("alert.xml"); + setString(find(alert, "message"), "text", message); + + add(alert); + requestFocus(find(alert, "closebtn")); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + // Display the FTP dialog. + private void ftpDialog(String file) + { + try + { + final Object ftp = parse("ftp.xml"); + setString(find(ftp, "file"), "text", file); + setString(find(ftp, "filename"), "text", file.substring(file.lastIndexOf('\\') + 1)); + setString(find(ftp, "host"), "text", _prefs.getProperty("host", "")); + setString(find(ftp, "login"), "text", _prefs.getProperty("login", "anonymous")); + setString(find(ftp, "password"), "text", _prefs.getProperty("password", "")); + add(ftp); + requestFocus(find(ftp, "host")); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + // Display the MetaWeblog dialog. + private void mwDialog(String file, String mimeType) + { + try + { + final Object mw = parse("metaweblog.xml"); + setString(find(mw, "file"), "text", file); + putProperty(find(mw, "file"), "mtype", mimeType); + setString(find(mw, "filename"), "text", file.substring(file.lastIndexOf('\\') + 1)); + setString(find(mw, "host"), "text", _prefs.getProperty("mw-host", "")); + setString(find(mw, "login"), "text", _prefs.getProperty("mw-login", "anonymous")); + setString(find(mw, "password"), "text", _prefs.getProperty("mw-password", "")); + setString(find(mw, "blogid"), "text", _prefs.getProperty("mw-id", "")); + add(mw); + requestFocus(find(mw, "host")); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + // Saves the properties. + private void savePrefs() + { + FileOutputStream out = null; + + try + { + out = new FileOutputStream(PREFS); + _prefs.store(out, ReleaseInfo.getProject() + ' ' + ReleaseInfo.getVersion()); + out.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + finally + { + if (out != null) + { + try + { + out.close(); + } + catch (IOException ignore) + { + ; // Do nothing + } + } + } + } +} diff --git a/src/net/thauvin/lifeblogger/LifeFTP.java b/src/net/thauvin/lifeblogger/LifeFTP.java new file mode 100644 index 0000000..9b026ff --- /dev/null +++ b/src/net/thauvin/lifeblogger/LifeFTP.java @@ -0,0 +1,145 @@ +/* + * @(#)LifeFTP.java + * + * Copyright (c) 2004, Erik C. Thauvin (http://www.thauvin.net/erik/) + * 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 authors 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. + * + * $Id$ + * + */ +package net.thauvin.lifeblogger; + +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPReply; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + + +/** + * The LifeFTP class stores/uploads a file to a FTP server. + * + * @author Erik C. Thauvin + * @version $Revision$, $Date$ + * + * @created Jul 20, 2004 + * @since 1.0 + */ +public class LifeFTP extends LifeBlog +{ + /** + * Creates a new LifeFTP object. + * + * @param thinlet The Thinlet object. + * @param host The FTP host. + * @param login The FTP login username. + * @param password The FTP login password. + * @param path The FTP path to upload to. + * @param filename The name of the file to upload. + * @param file The file to upload. + * + * @throws IOException If an error occurs while creating the object. + */ + public LifeFTP(LifeBlogger thinlet, String host, String login, String password, String path, String filename, + File file) + throws IOException + { + super(thinlet, host, login, password, path, filename, file); + } + + /** + * Performs the FTP transfer. + * + * @see Thread#run() + */ + public final void run() + { + boolean success = false; + + try + { + getThinlet().add(getDialog()); + + final FTPClient ftp = new FTPClient(); + ftp.connect(getHost()); + ftp.login(getLogin(), getPassword()); + + success = FTPReply.isPositiveCompletion(ftp.getReplyCode()); + + if (!success) + { + alert("Invalid login and/or password."); + } + else + { + if (getPath().length() > 0) + { + ftp.changeWorkingDirectory(getPath()); + + success = FTPReply.isPositiveCompletion(ftp.getReplyCode()); + + if (!success) + { + alert("Could not access specified path."); + } + } + + if (success) + { + final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(getFile())); + + ftp.storeFile(getFilename(), bis); + + bis.close(); + + success = FTPReply.isPositiveCompletion(ftp.getReplyCode()); + + if (!success) + { + alert("An error occurred: " + ftp.getReplyString()); + } + else + { + getThinlet().closeDialog(getDialog()); + } + } + } + + ftp.disconnect(); + } + catch (IOException e) + { + getThinlet().closeDialog(getDialog()); + getThinlet().showException(e); + } + } +} diff --git a/src/net/thauvin/lifeblogger/LifeMediaObjResponse.java b/src/net/thauvin/lifeblogger/LifeMediaObjResponse.java new file mode 100644 index 0000000..46c5bf1 --- /dev/null +++ b/src/net/thauvin/lifeblogger/LifeMediaObjResponse.java @@ -0,0 +1,135 @@ +/* + * @(#)LifeMediaObjResponse.java + * + * 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 authors 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. + * + * $Id$ + * + */ +package net.thauvin.lifeblogger; + +import thinlet.Thinlet; + +import java.io.IOException; +import java.io.InputStream; + + +/** + * The LifeMediaObjResponse class uses the Thinlet DOM parser to process the metaWeblog.newMediaObject + * XML-RPC reponse. + * + * @author Erik C. Thauvin + * @version $Revision$, $Date$ + * + * @created Jul 21, 2004 + * @since 1.0 + */ +public class LifeMediaObjResponse extends Thinlet +{ + private final InputStream _inputStream; + private String _response; + + /** + * Creates a new LifeMediaObjResponse object. + * + * @param inputStream The input stream. + */ + public LifeMediaObjResponse(InputStream inputStream) + { + _inputStream = inputStream; + } + + /** + * Returns the XML-RPC response/fault string. + * + * @return The response string. + */ + public final String getResponse() + { + return _response; + } + + /** + * Parses and validates the XML-RPC response. + * + * @return true is the response is valid, false if it is a fault. + * + * @throws IOException If an error occurs while processing the response. + */ + public final boolean isValidResponse() + throws IOException + { + try + { + final Object dom = parseDOM(_inputStream); + final Object params = getDOMNode(dom, "params", 0); + + if (params != null) + { + final Object param = getDOMNode(params, "param", 0); + final Object value = getDOMNode(param, "value", 0); + final Object struct = getDOMNode(value, "struct", 0); + final Object member = getDOMNode(struct, "member", 0); + final Object url = getDOMNode(member, "value", 0); + final Object string = getDOMNode(url, "string", 0); + + _response = getDOMText(string); + + return true; + } + else + { + final Object fault = getDOMNode(dom, "fault", 0); + final Object value = getDOMNode(fault, "value", 0); + final Object struct = getDOMNode(value, "struct", 0); + final Object member = getDOMNode(struct, "member", 0); + final Object error = getDOMNode(member, "value", 0); + final Object string = getDOMNode(error, "string", 0); + + _response = getDOMText(string); + + return false; + } + } + catch (IOException e) + { + throw e; + } + finally + { + try + { + _inputStream.close(); + } + catch (IOException ignore) + { + ; // Do nothing + } + } + } +} diff --git a/src/net/thauvin/lifeblogger/LifeMediaObject.java b/src/net/thauvin/lifeblogger/LifeMediaObject.java new file mode 100644 index 0000000..b3b8292 --- /dev/null +++ b/src/net/thauvin/lifeblogger/LifeMediaObject.java @@ -0,0 +1,216 @@ +/* + * @(#)LifeMediaObject.java + * + * Copyright (c) 2004, Erik C. Thauvin (http://www.thauvin.net/erik/) + * 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 authors 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. + * + * $Id$ + * + */ +package net.thauvin.lifeblogger; + +import java.awt.*; +import java.awt.datatransfer.StringSelection; + +import java.io.*; + +import java.net.URL; +import java.net.URLConnection; + + +/** + * The LifeMediaObject class posts a new media object via the metaWeblog.newMediaObject XML-RPC call. + * + * @author Erik C. Thauvin + * @version $Revision$, $Date$ + * + * @created Jul 21, 2004 + * @since 1.0 + */ +public class LifeMediaObject extends LifeBlog +{ + private final String _blogID; + private final String _mimeType; + + /** + * Creates a new LifeMediaObject object. + * + * @param thinlet The Thinlet instance. + * @param url The MetaWeblog XML-RPC URL. + * @param blogID The blog ID. + * @param login The MetaWeblog login username. + * @param password The MetaWeblog login password. + * @param filename The name of the object to store. + * @param mimeType The object's MIME type. + * @param file The file to store. + * + * @throws IOException If an error occurs while creating the object. + */ + public LifeMediaObject(LifeBlogger thinlet, String url, String blogID, String login, String password, + String filename, String mimeType, File file) + throws IOException + { + super(thinlet, url, login, password, "", filename, file); + + _mimeType = mimeType; + _blogID = blogID; + } + + /** + * Performs the action. + * + * @see Thread#run() + */ + public final void run() + { + FileInputStream fis = null; + final BufferedReader input = null; + + try + { + getThinlet().add(getDialog()); + + final URL url = new URL(getHost()); + + if (!"http".equalsIgnoreCase(url.getProtocol())) + { + throw new IOException("Unsupported URL protocol: " + url.getProtocol()); + } + + // The following is a little hackish. + // A better way would be to generate the request to a temporary file. + final long len = getFile().length(); + + if (len > Integer.MAX_VALUE) + { + throw new IOException("Sorry. The file is too large."); + } + + fis = new FileInputStream(getFile()); + + final byte[] bytes = new byte[(int) getFile().length()]; + + int offset = 0; + int numRead = 0; + + while ((offset < bytes.length) && ((numRead = fis.read(bytes, offset, bytes.length - offset)) >= 0)) + { + offset += numRead; + } + + if (offset < bytes.length) + { + throw new IOException("Could not completely read file: " + getFile().getName()); + } + + final StringBuffer start = + new StringBuffer("metaWeblog.newMediaObject").append(_blogID) + .append("") + .append(getLogin()) + .append("") + .append(getPassword()) + .append("bits"); + final String bits = Base64.encodeBytes(bytes); + + final StringBuffer end = + new StringBuffer("name").append(getFilename()) + .append("type") + .append(_mimeType) + .append(""); + + final URLConnection urlConn = url.openConnection(); + urlConn.setDoInput(true); + urlConn.setDoOutput(true); + urlConn.setUseCaches(false); + urlConn.setRequestProperty("Content-Length", String.valueOf(start.length() + bits.length() + end.length())); + urlConn.setRequestProperty("Content-Type", "text/xml"); + + final DataOutputStream output = new DataOutputStream(urlConn.getOutputStream()); + output.write(start.toString().getBytes()); + output.flush(); + output.write(bits.getBytes()); + output.flush(); + output.write(end.toString().getBytes()); + output.flush(); + + output.close(); + + final LifeMediaObjResponse xmlrpc = new LifeMediaObjResponse(urlConn.getInputStream()); + + if (xmlrpc.isValidResponse()) + { + getThinlet().setIcon(getThinlet().find(getDialog(), "iconlbl"), "icon", + getThinlet().getIcon("/icon/info.gif")); + getThinlet().setString(getThinlet().find(getDialog(), "message"), "text", + "The file can now be accessed at:\n\n" + xmlrpc.getResponse() + + "\n\nwhich has been copied to the clipboard."); + getThinlet().setBoolean(getThinlet().find(getDialog(), "closebtn"), "enabled", true); + + final StringSelection ss = new StringSelection(xmlrpc.getResponse()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, ss); + } + else + { + alert(xmlrpc.getResponse()); + } + } + catch (IOException e) + { + getThinlet().closeDialog(getDialog()); + getThinlet().showException(e); + } + finally + { + if (input != null) + { + try + { + input.close(); + } + catch (IOException e) + { + ; // Do nothing + } + } + + if (fis != null) + { + try + { + fis.close(); + } + catch (IOException ignore) + { + ; // Do nothing + } + } + } + } +} diff --git a/src/net/thauvin/lifeblogger/ReleaseInfo.java b/src/net/thauvin/lifeblogger/ReleaseInfo.java new file mode 100644 index 0000000..f3ac36f --- /dev/null +++ b/src/net/thauvin/lifeblogger/ReleaseInfo.java @@ -0,0 +1,51 @@ +/* Created by JReleaseInfo AntTask from Open Source Competence Group */ +/* Creation date Thu Jul 22 04:13:17 PDT 2004 */ +package net.thauvin.lifeblogger; + +import java.util.Date; + +/** + * This class provides information gathered from the build environment. + * + * @author JReleaseInfo AntTask + */ +public class ReleaseInfo { + + + /** buildDate (set during build process to 1090494797296L). */ + private static Date buildDate = new Date(1090494797296L); + + /** + * Get buildDate (set during build process to Thu Jul 22 04:13:17 PDT 2004). + * @return Date buildDate + */ + public static final Date getBuildDate() { return buildDate; } + + + /** + * Get buildNumber (set during build process to 77). + * @return int buildNumber + */ + public static final int getBuildNumber() { return 77; } + + + /** version (set during build process to "0.1.0"). */ + private static String version = new String("0.1.0"); + + /** + * Get version (set during build process to "0.1.0"). + * @return String version + */ + public static final String getVersion() { return version; } + + + /** project (set during build process to "LifeBlogger"). */ + private static String project = new String("LifeBlogger"); + + /** + * Get project (set during build process to "LifeBlogger"). + * @return String project + */ + public static final String getProject() { return project; } + +} diff --git a/src/net/thauvin/lifeblogger/about.xml b/src/net/thauvin/lifeblogger/about.xml new file mode 100644 index 0000000..ef0f959 --- /dev/null +++ b/src/net/thauvin/lifeblogger/about.xml @@ -0,0 +1,28 @@ + +

+ \ No newline at end of file diff --git a/src/net/thauvin/lifeblogger/alert.xml b/src/net/thauvin/lifeblogger/alert.xml new file mode 100644 index 0000000..34492f4 --- /dev/null +++ b/src/net/thauvin/lifeblogger/alert.xml @@ -0,0 +1,6 @@ + + +