From cb53098f59c978de6882b2c26bda4fa863d189f7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 14 Apr 2005 10:53:33 +0000 Subject: [PATCH] Added support for the Textamerica API. Added support fot posts titles via the MetaWeblog API. LifeBlogger is now using Apache's XML-RPC library. --- LifeBlogger.iml | 9 + LifeBlogger.ipr | 2 +- LifeBlogger.jnlp | 1 + build.properties | 2 +- lib/xmlrpc-1.1.jar | Bin 0 -> 57460 bytes licenses/{commons.txt => apache.txt} | 0 licenses/base64.txt | 8 - src/net/thauvin/lifeblogger/Base64.java | 1451 ----------------- src/net/thauvin/lifeblogger/LifeBlogger.java | 289 ++-- src/net/thauvin/lifeblogger/LifeFTP.java | 2 +- .../thauvin/lifeblogger/LifeMediaObject.java | 162 +- src/net/thauvin/lifeblogger/LifePost.java | 162 +- src/net/thauvin/lifeblogger/LifeRPC.java | 256 +++ .../thauvin/lifeblogger/LifeRPCResponse.java | 174 -- src/net/thauvin/lifeblogger/ReleaseInfo.java | 18 +- src/net/thauvin/lifeblogger/main.xml | 1 + src/net/thauvin/lifeblogger/post.xml | 5 +- www/index.html | 4 +- 18 files changed, 505 insertions(+), 2041 deletions(-) create mode 100644 lib/xmlrpc-1.1.jar rename licenses/{commons.txt => apache.txt} (100%) delete mode 100644 licenses/base64.txt delete mode 100644 src/net/thauvin/lifeblogger/Base64.java create mode 100644 src/net/thauvin/lifeblogger/LifeRPC.java delete mode 100644 src/net/thauvin/lifeblogger/LifeRPCResponse.java diff --git a/LifeBlogger.iml b/LifeBlogger.iml index ce87db1..4054451 100644 --- a/LifeBlogger.iml +++ b/LifeBlogger.iml @@ -37,6 +37,15 @@ + + + + + + + + + diff --git a/LifeBlogger.ipr b/LifeBlogger.ipr index c5c0ee3..f97a3bd 100644 --- a/LifeBlogger.ipr +++ b/LifeBlogger.ipr @@ -11,7 +11,7 @@ - + diff --git a/LifeBlogger.jnlp b/LifeBlogger.jnlp index 9dbfdc8..c0716b7 100644 --- a/LifeBlogger.jnlp +++ b/LifeBlogger.jnlp @@ -19,6 +19,7 @@ + diff --git a/build.properties b/build.properties index bca7f35..69655f9 100644 --- a/build.properties +++ b/build.properties @@ -1,6 +1,6 @@ # Project proj.name=LifeBlogger -proj.version=0.1.2 +proj.version=0.2 proj.package=net.thauvin.lifeblogger proj.run=${proj.package}.LifeBlogger diff --git a/lib/xmlrpc-1.1.jar b/lib/xmlrpc-1.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..b360b037cafaa2785fb200472437c96e65522f1e GIT binary patch literal 57460 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 literal 0 HcmV?d00001 diff --git a/licenses/commons.txt b/licenses/apache.txt similarity index 100% rename from licenses/commons.txt rename to licenses/apache.txt diff --git a/licenses/base64.txt b/licenses/base64.txt deleted file mode 100644 index 5640118..0000000 --- a/licenses/base64.txt +++ /dev/null @@ -1,8 +0,0 @@ -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. - -Robert Harder -rob@iharder.net \ No newline at end of file diff --git a/src/net/thauvin/lifeblogger/Base64.java b/src/net/thauvin/lifeblogger/Base64.java deleted file mode 100644 index 59835da..0000000 --- a/src/net/thauvin/lifeblogger/Base64.java +++ /dev/null @@ -1,1451 +0,0 @@ -package net.thauvin.lifeblogger; - -/** - * Encodes and decodes to and from Base64 notation. - * - *

- * Change Log: - *

- *
    - *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added - * some convenience methods for reading and writing to and from files.
  • - *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems - * with other encodings (like EBCDIC).
  • - *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the - * encoded data was a single byte.
  • - *
  • v2.0 - I got rid of methods that used booleans to set options. - * Now everything is more consolidated and cleaner. The code now detects - * when data that's being decoded is gzip-compressed and will decompress it - * automatically. Generally things are cleaner. You'll probably have to - * change some method calls that you were making to support the new - * options format (ints that you "OR" together).
  • - *
  • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
  • - *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. - * This helps when using GZIP streams. - * Added the ability to GZip-compress objects before encoding them.
  • - *
  • v1.4 - Added helper methods to read/write files.
  • - *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • - *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream - * where last buffer being read, if not completely full, was not returned.
  • - *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • - *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • - *
- * - *

- * 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.1 - */ -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 = "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 */ - }; - - // I think I end up not using the BAD_ENCODING indicator. - //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 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 - * @param 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 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 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 != null && bytes.length >= 4 ) - { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( 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 - - - - /** - * Convenience method for encoding data to a file. - * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean encodeToFile( byte[] dataToEncode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.ENCODE ); - bos.write( dataToEncode ); - success = true; - } // end try - catch( java.io.IOException e ) - { - - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean decodeToFile( String dataToDecode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.DECODE ); - bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); - success = true; - } // end try - catch( java.io.IOException e ) - { - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end decodeToFile - - - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - * @param filename Filename for reading encoded data - * @return decoded byte array or null if unsuccessful - * - * @since 2.1 - */ - public static byte[] decodeFromFile( String filename ) - { - byte[] decodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if( file.length() > Integer.MAX_VALUE ) - { - System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); - return null; - } // end if: file too big for int index - buffer = new byte[ (int)file.length() ]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.DECODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - decodedData = new byte[ length ]; - System.arraycopy( buffer, 0, decodedData, 0, length ); - - } // end try - catch( java.io.IOException e ) - { - System.err.println( "Error decoding from file " + filename ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return decodedData; - } // end decodeFromFile - - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - * @param filename Filename for reading binary data - * @return base64-encoded string or null if unsuccessful - * - * @since 2.1 - */ - public static String encodeFromFile( String filename ) - { - String encodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = new byte[ (int)(file.length() * 1.4) ]; - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.ENCODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - - } // end try - catch( java.io.IOException e ) - { - System.err.println( "Error encoding from file " + filename ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return encodedData; - } // end encodeFromFile - - - - - /* ******** 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 - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream - { - 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 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 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.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & 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 - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream - { - 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 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 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.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(int)} 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/LifeBlogger.java b/src/net/thauvin/lifeblogger/LifeBlogger.java index 6a31ae8..1a93bc0 100644 --- a/src/net/thauvin/lifeblogger/LifeBlogger.java +++ b/src/net/thauvin/lifeblogger/LifeBlogger.java @@ -1,7 +1,7 @@ /* * @(#)LifeBlogger.java * - * Copyright (c) 2004, Erik C. Thauvin (http://www.thauvin.net/erik/) + * Copyright (c) 2005, Erik C. Thauvin (http://www.thauvin.net/erik/) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,29 +55,30 @@ import javax.imageio.ImageIO; import javax.swing.*; +import org.apache.xmlrpc.Base64; + /** * The LifeBlogger class uploads/posts Lifeblog's favorite data to a blog. * - * @author Erik C. Thauvin + * @author Erik C. Thauvin * @version $Revision$, $Date$ - * - * @created Jul 19, 2004 - * @since 1.0 + * @created April 13, 2005 + * @since 1.0 */ public class LifeBlogger extends AntiAliasedThinlet { 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 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 static final String DEFAULT_ACTION = "mw"; private static final String MIME_JPG = "image/jpeg"; private static final String MIME_3GP = "video/3gpp"; - private final Properties _prefs = new Properties(); - private File _homeDir = new File(System.getProperty("user.home") + "\\My Documents\\NokiaLifeblogData"); private String _action; + private File _homeDir = new File(System.getProperty("user.home") + "\\My Documents\\NokiaLifeblogData"); + private final Properties _prefs = new Properties(); /** * Creates a new LifeBlogger object. @@ -163,18 +164,6 @@ public class LifeBlogger extends AntiAliasedThinlet } } - /** - * 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. */ @@ -200,9 +189,9 @@ public class LifeBlogger extends AntiAliasedThinlet /** * 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. + * @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. */ @@ -320,7 +309,7 @@ public class LifeBlogger extends AntiAliasedThinlet /** * Performs the blog action. * - * @param table The table containing the selected item to perform the action on. + * @param table The table containing the selected item to perform the action on. * * @throws Exception If an error occurs while performing the action. */ @@ -342,6 +331,10 @@ public class LifeBlogger extends AntiAliasedThinlet { ftpDialog(info[1]); } + else if ("ta".equals(_action)) + { + taDialog(info[1], info[2]); + } else { mwDialog(info[1], info[2]); @@ -371,8 +364,8 @@ public class LifeBlogger extends AntiAliasedThinlet /** * Preforms the FTP action. * - * @param dialog The FTP dialog, - * @param ftpPanel The panel contaning the FTP data. + * @param dialog The FTP dialog, + * @param ftpPanel The panel contaning the FTP data. * * @throws IOException If an error occurs while performing the action. */ @@ -401,16 +394,14 @@ public class LifeBlogger extends AntiAliasedThinlet { _prefs.put("host", host); _prefs.put("login", login); - _prefs.put("password", Base64.encodeBytes(password.getBytes(), Base64.DONT_BREAK_LINES)); + _prefs.put("password", new String(Base64.encode(password.getBytes()))); _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"))); + final LifeFTP ftp = new LifeFTP(this, host, login, password, path, filename, + new File(getString(find(ftpPanel, "file"), "text"))); ftp.start(); } } @@ -418,8 +409,8 @@ public class LifeBlogger extends AntiAliasedThinlet /** * Preforms the MetaWeblog action. * - * @param dialog The MetaWeblog dialog, - * @param mwPanel The panel contaning the MetaWeblog data. + * @param dialog The MetaWeblog dialog, + * @param mwPanel The panel contaning the MetaWeblog data. * * @throws IOException If an error occurs while performing the action. */ @@ -456,14 +447,14 @@ public class LifeBlogger extends AntiAliasedThinlet { _prefs.put("mw-host", host); _prefs.put("mw-login", login); - _prefs.put("mw-password", Base64.encodeBytes(password.getBytes(), Base64.DONT_BREAK_LINES)); + _prefs.put("mw-password", new String(Base64.encode(password.getBytes()))); _prefs.put("mw-id", blogID); if (_prefs.getProperty("blog-host") == null) { _prefs.put("blog-host", host); _prefs.put("blog-login", login); - _prefs.put("blog-password", Base64.encodeBytes(password.getBytes(), Base64.DONT_BREAK_LINES)); + _prefs.put("blog-password", new String(Base64.encode(password.getBytes()))); _prefs.put("blog-id", blogID); } @@ -471,35 +462,22 @@ public class LifeBlogger extends AntiAliasedThinlet 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"))); + 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(); } } - /** - * Preforms the post to blog action. - * - * @param dialog The post dialog, - * @param blogPanel The panel contaning the post data. - * - * @throws IOException If an error occurs while performing the action. - */ - public final void post(Object dialog, Object blogPanel) - throws IOException - { - post(dialog, blogPanel, false); - } /** * Displays the post to blog dialog. * - * @param url The URL pointing to the location of the media object. - * @param filename DOCUMENT ME! + * @param url The URL pointing to the location of the media object. + * @param filename The file name. + * @param metaWeblog The metaWeblog flag. */ - public final void postDialog(String url, String filename) + public final void postDialog(String url, String filename, boolean metaWeblog) { try { @@ -507,10 +485,25 @@ public class LifeBlogger extends AntiAliasedThinlet setString(find(post, "host"), "text", _prefs.getProperty("blog-host", "")); setString(find(post, "blogid"), "text", _prefs.getProperty("blog-id", "")); setString(find(post, "login"), "text", _prefs.getProperty("blog-login", "")); - setString(find(post, "password"), "text", new String(Base64.decode(_prefs.getProperty("blog-password", "")))); + setString(find(post, "password"), "text", + new String(Base64.decode(_prefs.getProperty("blog-password", "").getBytes()))); setString(find(post, "entry"), "text", "\""\r

via LifeBlogger

"); + + if (metaWeblog) + { + setBoolean(find(post, "title"), "visible", true); + setBoolean(find(post, "titleFld"), "visible", true); + setString(post, "text", getString(post, "text") + " (MetaWeblog API)"); + } + else + { + setBoolean(find(post, "title"), "visible", false); + setBoolean(find(post, "titleFld"), "visible", false); + setString(post, "text", getString(post, "text") + " (Blogger API)"); + } + add(post); } catch (Exception e) @@ -522,7 +515,7 @@ public class LifeBlogger extends AntiAliasedThinlet /** * Previews a JPEG image. * - * @param table The data table. + * @param table The data table. * * @throws Exception If an error occurs while previewing the image. */ @@ -540,7 +533,7 @@ public class LifeBlogger extends AntiAliasedThinlet if ((info[1] != null) && MIME_JPG.equals(info[2])) { - // Retrieve the jpg image + // Retrieve the jpg image final BufferedImage in = ImageIO.read(new File(info[1])); final int maxDim = 200; @@ -589,21 +582,89 @@ public class LifeBlogger extends AntiAliasedThinlet /** * Preforms the publish to blog action. * - * @param dialog The post dialog, - * @param blogPanel The panel contaning the post data. + * @param dialog The post dialog, + * @param blogPanel The panel contaning the post data. * * @throws IOException If an error occurs while performing the action. */ - public final void publish(Object dialog, Object blogPanel) - throws IOException + public void publish(Object dialog, Object blogPanel) + throws IOException { - post(dialog, blogPanel, true); + final String host = getString(find(blogPanel, "host"), "text"); + final String blogID = getString(find(blogPanel, "blogid"), "text"); + final String login = getString(find(blogPanel, "login"), "text"); + final String password = getString(find(blogPanel, "password"), "text"); + final String entry = getString(find(blogPanel, "entry"), "text"); + + final Object fileFld = find(blogPanel, "file"); + final String file = getString(fileFld, "text"); + final String fileType = (String) getProperty(fileFld, "mime"); + + final String title; + final Object titleFld = find(blogPanel, "title"); + + if (getBoolean(titleFld, "visible")) + { + title = getString(titleFld, "text"); + } + else + { + title = null; + } + + 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 (entry.length() <= 0) + { + alert("Please specify a post entry."); + } + else if (blogID.length() <= 0) + { + alert("Please specify a blog ID."); + } + else + { + _prefs.put("blog-host", host); + _prefs.put("blog-login", login); + _prefs.put("blog-password", new String(Base64.encode(password.getBytes()))); + _prefs.put("blog-id", blogID); + + savePrefs(); + + closeDialog(dialog); + + final LifePost post = new LifePost(this, host, blogID, login, password, title, + getString(find(blogPanel, "entry"), "text"), file, fileType); + post.start(); + } + } + + /** + * Sets the blog action. + * + * @param action The action + */ + public final void setAction(String action) + { + _action = action; + _prefs.put("via", _action); + savePrefs(); } /** * Toggles the given button based on the specified table selection. * - * @param table The table. + * @param table The table. * @param button The button. */ public final void toggleButton(Object table, Object button) @@ -614,8 +675,8 @@ public class LifeBlogger extends AntiAliasedThinlet /** * Updates the table data. * - * @param thinlet The Thinlet object. - * @param table The table to update. + * @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) @@ -737,7 +798,7 @@ public class LifeBlogger extends AntiAliasedThinlet setString(find(ftp, "host"), "text", _prefs.getProperty("host", "")); setString(find(ftp, "login"), "text", _prefs.getProperty("login", "anonymous")); setString(find(ftp, "path"), "text", _prefs.getProperty("path", "")); - setString(find(ftp, "password"), "text", new String(Base64.decode(_prefs.getProperty("password", "")))); + setString(find(ftp, "password"), "text", new String(Base64.decode(_prefs.getProperty("password", "").getBytes()))); add(ftp); requestFocus(find(ftp, "host")); } @@ -758,7 +819,7 @@ public class LifeBlogger extends AntiAliasedThinlet 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", "")); - setString(find(mw, "password"), "text", new String(Base64.decode(_prefs.getProperty("mw-password", "")))); + setString(find(mw, "password"), "text", new String(Base64.decode(_prefs.getProperty("mw-password", "").getBytes()))); setString(find(mw, "blogid"), "text", _prefs.getProperty("mw-id", "")); add(mw); requestFocus(find(mw, "host")); @@ -769,61 +830,6 @@ public class LifeBlogger extends AntiAliasedThinlet } } - /** - * Preforms the post/publish to blog action. - * - * @param dialog The post dialog, - * @param blogPanel The panel contaning the post data. - * @param publish Set to true to publish the post, false otherwise. - * - * @throws IOException If an error occurs while performing the action. - */ - private void post(Object dialog, Object blogPanel, boolean publish) - throws IOException - { - final String host = getString(find(blogPanel, "host"), "text"); - final String blogID = getString(find(blogPanel, "blogid"), "text"); - final String login = getString(find(blogPanel, "login"), "text"); - final String password = getString(find(blogPanel, "password"), "text"); - final String entry = getString(find(blogPanel, "entry"), "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 (entry.length() <= 0) - { - alert("Please specify a post entry."); - } - else if (blogID.length() <= 0) - { - alert("Please specify a blog ID."); - } - else - { - _prefs.put("blog-host", host); - _prefs.put("blog-login", login); - _prefs.put("blog-password", Base64.encodeBytes(password.getBytes(), Base64.DONT_BREAK_LINES)); - _prefs.put("blog-id", blogID); - - savePrefs(); - - closeDialog(dialog); - - final LifePost post = - new LifePost(this, host, blogID, login, password, getString(find(blogPanel, "entry"), "text"), publish); - post.start(); - } - } - // Saves the properties. private void savePrefs() { @@ -854,4 +860,39 @@ public class LifeBlogger extends AntiAliasedThinlet } } } + + // Display the Textamerica dialog + private void taDialog(String file, String mimeType) + { + if ((!mimeType.equals(MIME_JPG)) && (!mimeType.equals(MIME_3GP))) + { + alert("This media type is not supported by Textamerica."); + + return; + } + + try + { + final Object post = parse("post.xml"); + setString(find(post, "host"), "text", _prefs.getProperty("blog-host", "http://xml.api.textamerica.com/")); + setString(find(post, "blogid"), "text", _prefs.getProperty("blog-id", "")); + setString(find(post, "login"), "text", _prefs.getProperty("blog-login", "")); + setString(find(post, "password"), "text", + new String(Base64.decode(_prefs.getProperty("blog-password", "").getBytes()))); + + final Object fileFld = find(post, "file"); + setString(fileFld, "text", file); + putProperty(fileFld, "mime", ((mimeType == MIME_JPG) ? "JPG" : "3GP")); + + setBoolean(find(post, "title"), "visible", true); + setBoolean(find(post, "titleFld"), "visible", true); + setString(post, "text", getString(post, "text") + " (Textamerica API)"); + + add(post); + } + catch (Exception e) + { + e.printStackTrace(); + } + } } diff --git a/src/net/thauvin/lifeblogger/LifeFTP.java b/src/net/thauvin/lifeblogger/LifeFTP.java index f1a54c4..e2a6f74 100644 --- a/src/net/thauvin/lifeblogger/LifeFTP.java +++ b/src/net/thauvin/lifeblogger/LifeFTP.java @@ -136,7 +136,7 @@ public class LifeFTP extends LifeBlog else { getThinlet().closeDialog(getDialog()); - getThinlet().postDialog(getPath() + (getPath().endsWith("/") ? "" : "/") + getFilename(), getFilename()); + getThinlet().postDialog(getPath() + (getPath().endsWith("/") ? "" : "/") + getFilename(), getFilename(), false); } ftp.logout(); diff --git a/src/net/thauvin/lifeblogger/LifeMediaObject.java b/src/net/thauvin/lifeblogger/LifeMediaObject.java index 721ecae..548c40d 100644 --- a/src/net/thauvin/lifeblogger/LifeMediaObject.java +++ b/src/net/thauvin/lifeblogger/LifeMediaObject.java @@ -45,11 +45,10 @@ import java.net.URLConnection; /** * The LifeMediaObject class posts a new media object via the metaWeblog.newMediaObject XML-RPC method. * - * @author Erik C. Thauvin + * @author Erik C. Thauvin * @version $Revision$, $Date$ - * * @created Jul 21, 2004 - * @since 1.0 + * @since 1.0 */ public class LifeMediaObject extends LifeBlog { @@ -59,14 +58,14 @@ public class LifeMediaObject extends LifeBlog /** * 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. + * @param thinlet The Thinlet instance. + * @param url The XML-RPC URL endpoint. + * @param blogID The blog ID. + * @param login The login username. + * @param password The 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. */ @@ -87,147 +86,18 @@ public class LifeMediaObject extends LifeBlog */ public final void run() { - FileInputStream fis = null; - FileOutputStream fos = null; - BufferedOutputStream bos = null; - Base64.OutputStream out = null; + getThinlet().add(getDialog()); try { - getThinlet().add(getDialog()); - - final URL url = new URL(getHost()); - - if (!"http".equalsIgnoreCase(url.getProtocol())) - { - throw new IOException("Unsupported URL protocol: " + url.getProtocol()); - } - - final File tmpFile = File.createTempFile(ReleaseInfo.getProject(), ".b64"); - tmpFile.deleteOnExit(); - - fis = new FileInputStream(getFile()); - fos = new FileOutputStream(tmpFile); - bos = new BufferedOutputStream(fos); - out = new Base64.OutputStream(bos, Base64.ENCODE | Base64.DONT_BREAK_LINES); - - final byte[] buf = new byte[1024]; - int len; - - while ((len = fis.read(buf)) > 0) - { - out.write(buf, 0, len); - } - - fis.close(); - - final StringBuffer start = - new StringBuffer("metaWeblog.newMediaObject").append(_blogID) - .append("") - .append(getLogin()) - .append("") - .append(getPassword()) - .append("bits"); - - 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() + tmpFile.length() + end.length())); - urlConn.setRequestProperty("Content-Type", "text/xml"); - - final DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream()); - dos.write(start.toString().getBytes()); - dos.flush(); - - fis = new FileInputStream(tmpFile); - - while ((len = fis.read(buf)) > 0) - { - dos.write(buf, 0, len); - dos.flush(); - } - - fis.close(); - - dos.write(end.toString().getBytes()); - dos.flush(); - - dos.close(); - - final LifeRPCResponse xmlrpc = new LifeRPCResponse(urlConn.getInputStream()); - - if (xmlrpc.isValidResponse()) - { - getThinlet().closeDialog(getDialog()); - getThinlet().postDialog(xmlrpc.getResponse(), getFilename()); - } - else - { - alert(xmlrpc.getResponse()); - } - } - catch (IOException e) - { + final String url = LifeRPC.metaWeblogNewMediaObject(getHost(), _blogID, getLogin(), getPassword(), + getFilename(), _mimeType, getFile()); getThinlet().closeDialog(getDialog()); - getThinlet().handleException(e); + getThinlet().postDialog(url, getFilename(), true); } - finally + catch (Exception e) { - if (fis != null) - { - try - { - fis.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - - if (bos != null) - { - try - { - bos.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - - if (fos != null) - { - try - { - fos.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - - if (out != null) - { - try - { - out.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } + alert(e.getMessage()); } } } diff --git a/src/net/thauvin/lifeblogger/LifePost.java b/src/net/thauvin/lifeblogger/LifePost.java index 3fa228e..81d0bec 100644 --- a/src/net/thauvin/lifeblogger/LifePost.java +++ b/src/net/thauvin/lifeblogger/LifePost.java @@ -36,50 +36,51 @@ */ package net.thauvin.lifeblogger; -import java.io.DataOutputStream; import java.io.IOException; -import java.net.URL; -import java.net.URLConnection; - /** * The LifePost class posts a new blog entry using the blogger.newPost() XML-RPC method. * - * @author Erik C. Thauvin + * @author Erik C. Thauvin * @version $Revision$, $Date$ - * * @created Jul 21, 2004 - * @since 1.0 + * @since 1.0 */ public class LifePost extends LifeAction { - private final String _blogEntry; private final String _blogID; - private final boolean _publish; + private final String _entry; + private final String _file; + private final String _fileType; + private final String _title; /** * Creates a new LifePost 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 blogEntry The blog entry. - * @param publish DOCUMENT ME! + * @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 title The post's title, if any. + * @param description The blog description's text/description. + * @param file The file location, if any. + * @param fileType The file type, if any. * * @throws IOException If an error occurs while creating the object. */ - public LifePost(LifeBlogger thinlet, String url, String blogID, String login, String password, String blogEntry, - boolean publish) + public LifePost(LifeBlogger thinlet, String url, String blogID, String login, String password, String title, + String description, String file, String fileType) throws IOException { super(thinlet, url, login, password); - _blogEntry = blogEntry; + _entry = description; _blogID = blogID; - _publish = publish; + _title = title; + _file = file; + _fileType = fileType; } /** @@ -89,122 +90,39 @@ public class LifePost extends LifeAction */ public final void run() { + getThinlet().add(getDialog()); + + try { - getThinlet().add(getDialog()); + final String response; - final URL url = new URL(getHost()); - - if (!"http".equalsIgnoreCase(url.getProtocol())) + if (_file.length() != 0) { - throw new IOException("Unsupported URL protocol: " + url.getProtocol()); + response = LifeRPC.taEntryUpdate(getHost(), _blogID, getLogin(), getPassword(), _title, _entry, _file, + _fileType); } - - final StringBuffer request = - new StringBuffer("blogger.newPost0a6afffffffaffffffb8ffffff8569474cffffffc778500c03ffffffecffffff876116565a27283bffffffda56").append(_blogID) - .append("") - .append(getLogin()) - .append("") - .append(getPassword()) - .append("") - .append(textToXML(_blogEntry)) - .append("") - .append(String.valueOf(_publish)) - .append(""); - final URLConnection urlConn = url.openConnection(); - urlConn.setDoInput(true); - urlConn.setDoOutput(true); - urlConn.setUseCaches(false); - urlConn.setRequestProperty("Content-Length", String.valueOf(request.length())); - urlConn.setRequestProperty("Content-Type", "text/xml"); - - final DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream()); - dos.write(request.toString().getBytes()); - dos.flush(); - dos.close(); - - //System.out.println(request); - final LifeRPCResponse xmlrpc = new LifeRPCResponse(urlConn.getInputStream()); - - if (xmlrpc.isValidResponse()) + else if (_title != null) { - //getThinlet().closeDialog(getDialog()); - getThinlet().setIcon(getThinlet().find(getDialog(), "iconlbl"), "icon", - getThinlet().getIcon("/icon/info.gif")); - getThinlet().setString(getThinlet().find(getDialog(), "message"), "text", - "Post successful. (ID " + xmlrpc.getResponse() + ')'); - getThinlet().setBoolean(getThinlet().find(getDialog(), "closebtn"), "enabled", true); + response = LifeRPC.metaWeblogNewPost(getHost(), _blogID, getLogin(), getPassword(), _title, _entry); } else { - alert(xmlrpc.getResponse()); + response = LifeRPC.bloggerNewPost(getHost(), _blogID, getLogin(), getPassword(), _entry); } + + getThinlet().setIcon(getThinlet().find(getDialog(), "iconlbl"), "icon", + getThinlet().getIcon("/icon/info.gif")); + getThinlet().setString(getThinlet().find(getDialog(), "message"), "text", + "Post successful. (ID " + response + ')'); + getThinlet().setBoolean(getThinlet().find(getDialog(), "closebtn"), "enabled", true); } - catch (IOException e) + catch (Exception e) { - getThinlet().closeDialog(getDialog()); - getThinlet().handleException(e); + alert(e.getMessage()); } + } - /** - * Converts a character to XML entity. - * - * @param ch The character to convert. - * - * @return The converted string. - */ - private String charToXML(char ch) - { - final int c; - // Convert left bracket - if (ch == '<') - { - return ("<"); - } - - // Convert ampersand - else if (ch == '&') - { - return ("&"); - } - - // High/Low-ASCII character - else if ((ch >= 128) || (ch < 32)) - { - c = (int) ch; - - return ("&#" + c + ';'); - } - - // No conversion - else - { - // Return character as string - return (String.valueOf(ch)); - } - } - - /** - * Converts a text string to XML entities. - * - * @param text The string to convert. - * - * @return The converted string. - */ - private String textToXML(String text) - { - final StringBuffer html = new StringBuffer(); - - // Loop thru each characters of the text - for (int i = 0; i < text.length(); i++) - { - // Convert character to XML - html.append(charToXML(text.charAt(i))); - } - - // Return XML string - return html.toString(); - } } diff --git a/src/net/thauvin/lifeblogger/LifeRPC.java b/src/net/thauvin/lifeblogger/LifeRPC.java new file mode 100644 index 0000000..c699d59 --- /dev/null +++ b/src/net/thauvin/lifeblogger/LifeRPC.java @@ -0,0 +1,256 @@ +/* + * @(#)LifeRPC.java + * + * Copyright (C) 2005 by Erik C. Thauvin (erik@thauvin.net) + * 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.xmlrpc.Base64; +import org.apache.xmlrpc.XmlRpcClient; + +import java.io.*; + +import java.util.Hashtable; +import java.util.Vector; + + +/** + * The LifeRPC class implements the blogger.newPost, metaWeblog.newPost, metaWeblog.newMediaObject and + * ta.Entry.Update XML-RPC methods. + * + * @author Erik C. Thauvin + * @version $Revision$, $Date$ + * @created Apr 13, 2005 + * @since 1.0 + */ +public class LifeRPC +{ + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException if the constructor is called. + */ + private LifeRPC() + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Illegal constructor call."); + } + + /** + * Implements the blogger.newPost XML-RPC call. + * + * @param url The XML-RPC URL endpoint. + * @param blogID The blog ID. + * @param login The login username. + * @param password The login password. + * @param description The blog description's text/description. + * + * @return The post ID. + * + * @throws Exception If an error occurred while posting. + */ + public static String bloggerNewPost(String url, String blogID, String login, String password, String description) + throws Exception + { + final XmlRpcClient xmlrpc = new XmlRpcClient(url); + final Vector params = new Vector(0); + + // Set the API key + params.add("0a6afffffffaffffffb8ffffff8569474cffffffc778500c03ffffffecffffff876116565a27283bffffffda56"); + + params.add(blogID); + params.add(login); + params.add(password); + params.add(description); + + // Set the publish flag + params.add(Boolean.valueOf(true)); + + return ((String) xmlrpc.execute("blogger.newPost", params)); + + } + + /** + * Implements blogger.newPost XML-RPC call. + * + * @param url The XML-RPC URL endpoint. + * @param blogID The blog ID. + * @param login The login username. + * @param password The login password. + * @param filename The name of the object to store. + * @param mimeType The object's MIME type. + * @param file The file to store. + * + * @return The URL of the new media object. + * + * @throws Exception If an error occurred while upload the new media object. + */ + public static String metaWeblogNewMediaObject(String url, String blogID, String login, String password, + String filename, String mimeType, File file) + throws Exception + { + final XmlRpcClient xmlrpc = new XmlRpcClient(url); + final Vector params = new Vector(0); + + final InputStream is = new FileInputStream(file); + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024); + final BufferedInputStream bufferedInputStream = new BufferedInputStream(is); + + final byte[] bytes = new byte[1024]; + int len = 0; + + while ((len = bufferedInputStream.read(bytes)) > 0) + { + byteArrayOutputStream.write(bytes, 0, len); + } + + is.close(); + bufferedInputStream.close(); + + params.add(blogID); + params.add(login); + params.add(password); + + final Hashtable media = new Hashtable(0); + + media.put("name", filename); + media.put("type", mimeType); + media.put("bits", byteArrayOutputStream.toByteArray()); + + params.add(media); + + final Hashtable response = (Hashtable) xmlrpc.execute("metaWeblog.newMediaObject", params); + + return ((String) response.get("url")); + } + + /** + * Implements the metaWeblog.newPost XML-RPC call. + * + * @param url The XML-RPC URL endpoint. + * @param blogID The blog ID. + * @param login The login username. + * @param password The login password. + * @param title The blog description's title. + * @param description The blog description's text/description. + * + * @return The post ID. + * + * @throws Exception If an error occurred while posting. + */ + public static String metaWeblogNewPost(String url, String blogID, String login, String password, String title, + String description) + throws Exception + { + final XmlRpcClient xmlrpc = new XmlRpcClient(url); + final Vector params = new Vector(0); + + params.add(blogID); + params.add(login); + params.add(password); + + final Hashtable blogEntry = new Hashtable(0); + blogEntry.put("title", title); + blogEntry.put("description", description); + params.add(blogEntry); + + // Set the publish flag + params.add(Boolean.valueOf(true)); + + return ((String) xmlrpc.execute("metaWeblog.newPost", params)); + + } + + /** + * Implements the metaWeblog.newPost XML-RPC call. + * + * @param url The XML-RPC URL endpoint. + * @param blogID The blog ID. + * @param login The login username. + * @param password The login password. + * @param title The blog description's title. + * @param description The blog description's text/description. + * @param file The file location. + * @param fileType The file type. + * + * @return The post ID. + * + * @throws Exception If an error occurred while posting. + */ + public static String taEntryUpdate(String url, String blogID, String login, String password, String title, + String description, String file, String fileType) + throws Exception + { + final XmlRpcClient xmlrpc = new XmlRpcClient(url); + final Vector params = new Vector(0); + + final InputStream is = new FileInputStream(file); + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024); + final BufferedInputStream bufferedInputStream = new BufferedInputStream(is); + + final byte[] bytes = new byte[1024]; + int len = 0; + + while ((len = bufferedInputStream.read(bytes)) > 0) + { + byteArrayOutputStream.write(bytes, 0, len); + } + + is.close(); + bufferedInputStream.close(); + + final byte[] encodedBytes = Base64.encode(byteArrayOutputStream.toByteArray()); + + // Set the API key + params.add("TA113805"); + + params.add(login); + params.add(password); + params.add(blogID); + + // Set the entry ID + params.add("0"); + + params.add(title); + params.add(description); + + // Set the category ID + params.add("0"); + + params.add(new String(encodedBytes)); + params.add(fileType); + + return ((String) xmlrpc.execute("ta.entry.Update", params)); + } +} diff --git a/src/net/thauvin/lifeblogger/LifeRPCResponse.java b/src/net/thauvin/lifeblogger/LifeRPCResponse.java deleted file mode 100644 index 6a252e0..0000000 --- a/src/net/thauvin/lifeblogger/LifeRPCResponse.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * @(#)LifeRPCResponse.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.Thinlet; - -import java.io.IOException; -import java.io.InputStream; - - -/** - * The LifeRPCResponse class uses the Thinlet DOM parser to process a XML-RCP response. - * - * @author Erik C. Thauvin - * @version $Revision$, $Date$ - * - * @created Jul 21, 2004 - * @since 1.0 - */ -public class LifeRPCResponse extends Thinlet -{ - private final InputStream _inputStream; - private String _response; - - /** - * Creates a new LifeRPCResponse object. - * - * @param inputStream The input stream. - */ - public LifeRPCResponse(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); - - if (struct == null) - { - final Object string = getDOMNode(value, "string", 0); - - if (string == null) - { - _response = getDOMText(value); - } - else - { - _response = getDOMText(string); - } - } - else - { - final Object member = getDOMNode(struct, "member", 0); - final Object url = getDOMNode(member, "value", 0); - final Object string = getDOMNode(url, "string", 0); - - if (string == null) - { - _response = getDOMText(url); - } - else - { - _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); - Object member = getDOMNode(struct, "member", 0); - - if (getDOMCount(struct, "member") > 1) - { - member = getDOMNode(struct, "member", 1); - } - - final Object error = getDOMNode(member, "value", 0); - final Object string = getDOMNode(error, "string", 0); - - if (string != null) - { - _response = getDOMText(string); - } - else - { - throw new IOException("Could not parse the XML-RPC error response."); - } - - return false; - } - } - catch (IOException e) - { - throw e; - } - finally - { - try - { - _inputStream.close(); - } - catch (IOException ignore) - { - ; // Do nothing - } - } - } -} diff --git a/src/net/thauvin/lifeblogger/ReleaseInfo.java b/src/net/thauvin/lifeblogger/ReleaseInfo.java index 040811a..ee5c511 100644 --- a/src/net/thauvin/lifeblogger/ReleaseInfo.java +++ b/src/net/thauvin/lifeblogger/ReleaseInfo.java @@ -1,5 +1,5 @@ /* Created by JReleaseInfo AntTask from Open Source Competence Group */ -/* Creation date Sun Feb 06 00:48:16 PST 2005 */ +/* Creation date Thu Apr 14 03:27:19 PDT 2005 */ package net.thauvin.lifeblogger; import java.util.Date; @@ -12,28 +12,28 @@ import java.util.Date; public class ReleaseInfo { - /** buildDate (set during build process to 1107679696812L). */ - private static Date buildDate = new Date(1107679696812L); + /** buildDate (set during build process to 1113474439925L). */ + private static Date buildDate = new Date(1113474439925L); /** - * Get buildDate (set during build process to Sun Feb 06 00:48:16 PST 2005). + * Get buildDate (set during build process to Thu Apr 14 03:27:19 PDT 2005). * @return Date buildDate */ public static final Date getBuildDate() { return buildDate; } /** - * Get buildNumber (set during build process to 5). + * Get buildNumber (set during build process to 1). * @return int buildNumber */ - public static final int getBuildNumber() { return 5; } + public static final int getBuildNumber() { return 1; } - /** version (set during build process to "0.1.2"). */ - private static String version = new String("0.1.2"); + /** version (set during build process to "0.2"). */ + private static String version = new String("0.2"); /** - * Get version (set during build process to "0.1.2"). + * Get version (set during build process to "0.2"). * @return String version */ public static final String getVersion() { return version; } diff --git a/src/net/thauvin/lifeblogger/main.xml b/src/net/thauvin/lifeblogger/main.xml index 9a7aac7..dc209a6 100644 --- a/src/net/thauvin/lifeblogger/main.xml +++ b/src/net/thauvin/lifeblogger/main.xml @@ -7,6 +7,7 @@ + diff --git a/src/net/thauvin/lifeblogger/post.xml b/src/net/thauvin/lifeblogger/post.xml index 42c3da8..6454f17 100644 --- a/src/net/thauvin/lifeblogger/post.xml +++ b/src/net/thauvin/lifeblogger/post.xml @@ -1,16 +1,17 @@ - +