Added support for the Textamerica API.
Added support fot posts titles via the MetaWeblog API. LifeBlogger is now using Apache's XML-RPC library.
This commit is contained in:
parent
3134321d29
commit
cb53098f59
18 changed files with 505 additions and 2041 deletions
|
@ -37,6 +37,15 @@
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
</library>
|
</library>
|
||||||
</orderEntry>
|
</orderEntry>
|
||||||
|
<orderEntry type="module-library">
|
||||||
|
<library>
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MODULE_DIR$/lib/xmlrpc-1.1.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</orderEntry>
|
||||||
<orderEntryProperties />
|
<orderEntryProperties />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</buildFile>
|
</buildFile>
|
||||||
<buildFile url="file://$PROJECT_DIR$/webstart.xml">
|
<buildFile url="file://$PROJECT_DIR$/webstart.xml">
|
||||||
<additionalClassPath />
|
<additionalClassPath />
|
||||||
<antReference name="Local Ant" />
|
<antReference name="Apache Ant version 1.6.2" />
|
||||||
<customJdkName value="" />
|
<customJdkName value="" />
|
||||||
<maximumHeapSize value="128" />
|
<maximumHeapSize value="128" />
|
||||||
<properties />
|
<properties />
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<jar href="commons-net-1.3.0.jar"/>
|
<jar href="commons-net-1.3.0.jar"/>
|
||||||
<jar href="sqlite.jar"/>
|
<jar href="sqlite.jar"/>
|
||||||
<jar href="thinlet.jar"/>
|
<jar href="thinlet.jar"/>
|
||||||
|
<jar href="xmlrpc-1.1.jar"/>
|
||||||
<nativelib href="sqlite_jni.dll"/>
|
<nativelib href="sqlite_jni.dll"/>
|
||||||
</resources>
|
</resources>
|
||||||
<application-desc main-class="net.thauvin.lifeblogger.LifeBlogger"/>
|
<application-desc main-class="net.thauvin.lifeblogger.LifeBlogger"/>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Project
|
# Project
|
||||||
proj.name=LifeBlogger
|
proj.name=LifeBlogger
|
||||||
proj.version=0.1.2
|
proj.version=0.2
|
||||||
proj.package=net.thauvin.lifeblogger
|
proj.package=net.thauvin.lifeblogger
|
||||||
proj.run=${proj.package}.LifeBlogger
|
proj.run=${proj.package}.LifeBlogger
|
||||||
|
|
||||||
|
|
BIN
lib/xmlrpc-1.1.jar
Normal file
BIN
lib/xmlrpc-1.1.jar
Normal file
Binary file not shown.
|
@ -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 <a href="http://iharder.net/xmlizable">http://iharder.net/base64</a>
|
|
||||||
periodically to check for updates or to contribute improvements.
|
|
||||||
|
|
||||||
Robert Harder
|
|
||||||
rob@iharder.net
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* @(#)LifeBlogger.java
|
* @(#)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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -55,29 +55,30 @@ import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.Base64;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <code>LifeBlogger</code> class uploads/posts Lifeblog's favorite data to a blog.
|
* The <code>LifeBlogger</code> class uploads/posts Lifeblog's favorite data to a blog.
|
||||||
*
|
*
|
||||||
* @author <a href="http://www.thauvin.net/erik/">Erik C. Thauvin</a>
|
* @author <a href="http://www.thauvin.net/erik/">Erik C. Thauvin</a>
|
||||||
* @version $Revision$, $Date$
|
* @version $Revision$, $Date$
|
||||||
*
|
* @created April 13, 2005
|
||||||
* @created Jul 19, 2004
|
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public class LifeBlogger extends AntiAliasedThinlet
|
public class LifeBlogger extends AntiAliasedThinlet
|
||||||
{
|
{
|
||||||
private static final String DRIVER = "SQLite.JDBCDriver";
|
private static final String DRIVER = "SQLite.JDBCDriver";
|
||||||
private static final String PREFS =
|
private static final String PREFS = System.getProperty("user.home") + File.separator + ReleaseInfo.getProject() +
|
||||||
System.getProperty("user.home") + File.separator + ReleaseInfo.getProject() + ".properties";
|
".properties";
|
||||||
private static final String JDBC_PREFIX = "jdbc:sqlite:/";
|
private static final String JDBC_PREFIX = "jdbc:sqlite:/";
|
||||||
private static final String DATABASE = "\\DataBase\\NokiaLifeblogDataBase.db";
|
private static final String DATABASE = "\\DataBase\\NokiaLifeblogDataBase.db";
|
||||||
private static final String DEFAULT_ACTION = "mw";
|
private static final String DEFAULT_ACTION = "mw";
|
||||||
private static final String MIME_JPG = "image/jpeg";
|
private static final String MIME_JPG = "image/jpeg";
|
||||||
private static final String MIME_3GP = "video/3gpp";
|
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 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.
|
* 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.
|
* Displays the about dialog.
|
||||||
*/
|
*/
|
||||||
|
@ -342,6 +331,10 @@ public class LifeBlogger extends AntiAliasedThinlet
|
||||||
{
|
{
|
||||||
ftpDialog(info[1]);
|
ftpDialog(info[1]);
|
||||||
}
|
}
|
||||||
|
else if ("ta".equals(_action))
|
||||||
|
{
|
||||||
|
taDialog(info[1], info[2]);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mwDialog(info[1], info[2]);
|
mwDialog(info[1], info[2]);
|
||||||
|
@ -401,15 +394,13 @@ public class LifeBlogger extends AntiAliasedThinlet
|
||||||
{
|
{
|
||||||
_prefs.put("host", host);
|
_prefs.put("host", host);
|
||||||
_prefs.put("login", login);
|
_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);
|
_prefs.put("path", path);
|
||||||
|
|
||||||
savePrefs();
|
savePrefs();
|
||||||
|
|
||||||
closeDialog(dialog);
|
closeDialog(dialog);
|
||||||
|
|
||||||
final LifeFTP ftp =
|
final LifeFTP ftp = new LifeFTP(this, host, login, password, path, filename,
|
||||||
new LifeFTP(this, host, login, password, path, filename,
|
|
||||||
new File(getString(find(ftpPanel, "file"), "text")));
|
new File(getString(find(ftpPanel, "file"), "text")));
|
||||||
ftp.start();
|
ftp.start();
|
||||||
}
|
}
|
||||||
|
@ -456,14 +447,14 @@ public class LifeBlogger extends AntiAliasedThinlet
|
||||||
{
|
{
|
||||||
_prefs.put("mw-host", host);
|
_prefs.put("mw-host", host);
|
||||||
_prefs.put("mw-login", login);
|
_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);
|
_prefs.put("mw-id", blogID);
|
||||||
|
|
||||||
if (_prefs.getProperty("blog-host") == null)
|
if (_prefs.getProperty("blog-host") == null)
|
||||||
{
|
{
|
||||||
_prefs.put("blog-host", host);
|
_prefs.put("blog-host", host);
|
||||||
_prefs.put("blog-login", login);
|
_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);
|
_prefs.put("blog-id", blogID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,35 +462,22 @@ public class LifeBlogger extends AntiAliasedThinlet
|
||||||
|
|
||||||
closeDialog(dialog);
|
closeDialog(dialog);
|
||||||
|
|
||||||
final LifeMediaObject mw =
|
final LifeMediaObject mw = new LifeMediaObject(this, host, blogID, login, password, filename,
|
||||||
new LifeMediaObject(this, host, blogID, login, password, filename,
|
|
||||||
String.valueOf(getProperty(find(mwPanel, "file"), "mtype")),
|
String.valueOf(getProperty(find(mwPanel, "file"), "mtype")),
|
||||||
new File(getString(find(mwPanel, "file"), "text")));
|
new File(getString(find(mwPanel, "file"), "text")));
|
||||||
mw.start();
|
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.
|
* Displays the post to blog dialog.
|
||||||
*
|
*
|
||||||
* @param url The URL pointing to the location of the media object.
|
* @param url The URL pointing to the location of the media object.
|
||||||
* @param filename DOCUMENT ME!
|
* @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
|
try
|
||||||
{
|
{
|
||||||
|
@ -507,10 +485,25 @@ public class LifeBlogger extends AntiAliasedThinlet
|
||||||
setString(find(post, "host"), "text", _prefs.getProperty("blog-host", ""));
|
setString(find(post, "host"), "text", _prefs.getProperty("blog-host", ""));
|
||||||
setString(find(post, "blogid"), "text", _prefs.getProperty("blog-id", ""));
|
setString(find(post, "blogid"), "text", _prefs.getProperty("blog-id", ""));
|
||||||
setString(find(post, "login"), "text", _prefs.getProperty("blog-login", ""));
|
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",
|
setString(find(post, "entry"), "text",
|
||||||
"<img src=\"" + url + "\" alt=\"" + filename +
|
"<img src=\"" + url + "\" alt=\"" + filename +
|
||||||
"\">\r<p>via <a href=\"http://www.thauvin.net/erik/lifeblogger/\">LifeBlogger</a></p>");
|
"\">\r<p>via <a href=\"http://www.thauvin.net/erik/lifeblogger/\">LifeBlogger</a></p>");
|
||||||
|
|
||||||
|
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);
|
add(post);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -594,10 +587,78 @@ public class LifeBlogger extends AntiAliasedThinlet
|
||||||
*
|
*
|
||||||
* @throws IOException If an error occurs while performing the action.
|
* @throws IOException If an error occurs while performing the action.
|
||||||
*/
|
*/
|
||||||
public final void publish(Object dialog, Object blogPanel)
|
public void publish(Object dialog, Object blogPanel)
|
||||||
throws IOException
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -737,7 +798,7 @@ public class LifeBlogger extends AntiAliasedThinlet
|
||||||
setString(find(ftp, "host"), "text", _prefs.getProperty("host", ""));
|
setString(find(ftp, "host"), "text", _prefs.getProperty("host", ""));
|
||||||
setString(find(ftp, "login"), "text", _prefs.getProperty("login", "anonymous"));
|
setString(find(ftp, "login"), "text", _prefs.getProperty("login", "anonymous"));
|
||||||
setString(find(ftp, "path"), "text", _prefs.getProperty("path", ""));
|
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);
|
add(ftp);
|
||||||
requestFocus(find(ftp, "host"));
|
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, "filename"), "text", file.substring(file.lastIndexOf('\\') + 1));
|
||||||
setString(find(mw, "host"), "text", _prefs.getProperty("mw-host", ""));
|
setString(find(mw, "host"), "text", _prefs.getProperty("mw-host", ""));
|
||||||
setString(find(mw, "login"), "text", _prefs.getProperty("mw-login", ""));
|
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", ""));
|
setString(find(mw, "blogid"), "text", _prefs.getProperty("mw-id", ""));
|
||||||
add(mw);
|
add(mw);
|
||||||
requestFocus(find(mw, "host"));
|
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 <code>true</code> to publish the post, <code>false</code> 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.
|
// Saves the properties.
|
||||||
private void savePrefs()
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ public class LifeFTP extends LifeBlog
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
getThinlet().closeDialog(getDialog());
|
getThinlet().closeDialog(getDialog());
|
||||||
getThinlet().postDialog(getPath() + (getPath().endsWith("/") ? "" : "/") + getFilename(), getFilename());
|
getThinlet().postDialog(getPath() + (getPath().endsWith("/") ? "" : "/") + getFilename(), getFilename(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ftp.logout();
|
ftp.logout();
|
||||||
|
|
|
@ -47,7 +47,6 @@ import java.net.URLConnection;
|
||||||
*
|
*
|
||||||
* @author <a href="http://www.thauvin.net/erik/">Erik C. Thauvin</a>
|
* @author <a href="http://www.thauvin.net/erik/">Erik C. Thauvin</a>
|
||||||
* @version $Revision$, $Date$
|
* @version $Revision$, $Date$
|
||||||
*
|
|
||||||
* @created Jul 21, 2004
|
* @created Jul 21, 2004
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
|
@ -60,10 +59,10 @@ public class LifeMediaObject extends LifeBlog
|
||||||
* Creates a new LifeMediaObject object.
|
* Creates a new LifeMediaObject object.
|
||||||
*
|
*
|
||||||
* @param thinlet The Thinlet instance.
|
* @param thinlet The Thinlet instance.
|
||||||
* @param url The MetaWeblog XML-RPC URL.
|
* @param url The XML-RPC URL endpoint.
|
||||||
* @param blogID The blog ID.
|
* @param blogID The blog ID.
|
||||||
* @param login The MetaWeblog login username.
|
* @param login The login username.
|
||||||
* @param password The MetaWeblog login password.
|
* @param password The login password.
|
||||||
* @param filename The name of the object to store.
|
* @param filename The name of the object to store.
|
||||||
* @param mimeType The object's MIME type.
|
* @param mimeType The object's MIME type.
|
||||||
* @param file The file to store.
|
* @param file The file to store.
|
||||||
|
@ -86,148 +85,19 @@ public class LifeMediaObject extends LifeBlog
|
||||||
* @see Thread#run()
|
* @see Thread#run()
|
||||||
*/
|
*/
|
||||||
public final void run()
|
public final void run()
|
||||||
{
|
|
||||||
FileInputStream fis = null;
|
|
||||||
FileOutputStream fos = null;
|
|
||||||
BufferedOutputStream bos = null;
|
|
||||||
Base64.OutputStream out = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
getThinlet().add(getDialog());
|
getThinlet().add(getDialog());
|
||||||
|
|
||||||
final URL url = new URL(getHost());
|
try
|
||||||
|
|
||||||
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("<?xml version=\"1.0\"?><methodCall><methodName>metaWeblog.newMediaObject</methodName><params><param><value><string>").append(_blogID)
|
|
||||||
.append("</string></value></param><param><value><string>")
|
|
||||||
.append(getLogin())
|
|
||||||
.append("</string></value></param><param><value><string>")
|
|
||||||
.append(getPassword())
|
|
||||||
.append("</string></value></param><param><value><struct><member><name>bits</name><value><base64>");
|
|
||||||
|
|
||||||
final StringBuffer end =
|
|
||||||
new StringBuffer("</base64></value></member><member><name>name</name><value><string>").append(getFilename())
|
|
||||||
.append("</string></value></member><member><name>type</name><value><string>")
|
|
||||||
.append(_mimeType)
|
|
||||||
.append("</string></value></member></struct></value></param></params></methodCall>");
|
|
||||||
|
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
|
final String url = LifeRPC.metaWeblogNewMediaObject(getHost(), _blogID, getLogin(), getPassword(),
|
||||||
|
getFilename(), _mimeType, getFile());
|
||||||
getThinlet().closeDialog(getDialog());
|
getThinlet().closeDialog(getDialog());
|
||||||
getThinlet().postDialog(xmlrpc.getResponse(), getFilename());
|
getThinlet().postDialog(url, getFilename(), true);
|
||||||
}
|
}
|
||||||
else
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
alert(xmlrpc.getResponse());
|
alert(e.getMessage());
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
getThinlet().closeDialog(getDialog());
|
|
||||||
getThinlet().handleException(e);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,27 +36,24 @@
|
||||||
*/
|
*/
|
||||||
package net.thauvin.lifeblogger;
|
package net.thauvin.lifeblogger;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <code>LifePost</code> class posts a new blog entry using the blogger.newPost() XML-RPC method.
|
* The <code>LifePost</code> class posts a new blog entry using the blogger.newPost() XML-RPC method.
|
||||||
*
|
*
|
||||||
* @author <a href="http://www.thauvin.net/erik/">Erik C. Thauvin</a>
|
* @author <a href="http://www.thauvin.net/erik/">Erik C. Thauvin</a>
|
||||||
* @version $Revision$, $Date$
|
* @version $Revision$, $Date$
|
||||||
*
|
|
||||||
* @created Jul 21, 2004
|
* @created Jul 21, 2004
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public class LifePost extends LifeAction
|
public class LifePost extends LifeAction
|
||||||
{
|
{
|
||||||
private final String _blogEntry;
|
|
||||||
private final String _blogID;
|
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.
|
* Creates a new LifePost object.
|
||||||
|
@ -66,20 +63,24 @@ public class LifePost extends LifeAction
|
||||||
* @param blogID The blog ID.
|
* @param blogID The blog ID.
|
||||||
* @param login The MetaWeblog login username.
|
* @param login The MetaWeblog login username.
|
||||||
* @param password The MetaWeblog login password.
|
* @param password The MetaWeblog login password.
|
||||||
* @param blogEntry The blog entry.
|
* @param title The post's title, if any.
|
||||||
* @param publish DOCUMENT ME!
|
* @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.
|
* @throws IOException If an error occurs while creating the object.
|
||||||
*/
|
*/
|
||||||
public LifePost(LifeBlogger thinlet, String url, String blogID, String login, String password, String blogEntry,
|
public LifePost(LifeBlogger thinlet, String url, String blogID, String login, String password, String title,
|
||||||
boolean publish)
|
String description, String file, String fileType)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
super(thinlet, url, login, password);
|
super(thinlet, url, login, password);
|
||||||
|
|
||||||
_blogEntry = blogEntry;
|
_entry = description;
|
||||||
_blogID = blogID;
|
_blogID = blogID;
|
||||||
_publish = publish;
|
_title = title;
|
||||||
|
_file = file;
|
||||||
|
_fileType = fileType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,123 +89,40 @@ public class LifePost extends LifeAction
|
||||||
* @see Thread#run()
|
* @see Thread#run()
|
||||||
*/
|
*/
|
||||||
public final void run()
|
public final void run()
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
getThinlet().add(getDialog());
|
getThinlet().add(getDialog());
|
||||||
|
|
||||||
final URL url = new URL(getHost());
|
|
||||||
|
|
||||||
if (!"http".equalsIgnoreCase(url.getProtocol()))
|
try
|
||||||
{
|
{
|
||||||
throw new IOException("Unsupported URL protocol: " + url.getProtocol());
|
final String response;
|
||||||
|
|
||||||
|
if (_file.length() != 0)
|
||||||
|
{
|
||||||
|
response = LifeRPC.taEntryUpdate(getHost(), _blogID, getLogin(), getPassword(), _title, _entry, _file,
|
||||||
|
_fileType);
|
||||||
|
}
|
||||||
|
else if (_title != null)
|
||||||
|
{
|
||||||
|
response = LifeRPC.metaWeblogNewPost(getHost(), _blogID, getLogin(), getPassword(), _title, _entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response = LifeRPC.bloggerNewPost(getHost(), _blogID, getLogin(), getPassword(), _entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
final StringBuffer request =
|
|
||||||
new StringBuffer("<?xml version=\"1.0\"?><methodCall><methodName>blogger.newPost</methodName><params><param><value><string>0a6afffffffaffffffb8ffffff8569474cffffffc778500c03ffffffecffffff876116565a27283bffffffda56</string></value></param><param><value><string>").append(_blogID)
|
|
||||||
.append("</string></value></param><param><value><string>")
|
|
||||||
.append(getLogin())
|
|
||||||
.append("</string></value></param><param><value><string>")
|
|
||||||
.append(getPassword())
|
|
||||||
.append("</string></value></param><param><value><string>")
|
|
||||||
.append(textToXML(_blogEntry))
|
|
||||||
.append("</string></value></param><param><value><boolean>")
|
|
||||||
.append(String.valueOf(_publish))
|
|
||||||
.append("</boolean></value></param></params></methodCall>");
|
|
||||||
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())
|
|
||||||
{
|
|
||||||
//getThinlet().closeDialog(getDialog());
|
|
||||||
getThinlet().setIcon(getThinlet().find(getDialog(), "iconlbl"), "icon",
|
getThinlet().setIcon(getThinlet().find(getDialog(), "iconlbl"), "icon",
|
||||||
getThinlet().getIcon("/icon/info.gif"));
|
getThinlet().getIcon("/icon/info.gif"));
|
||||||
getThinlet().setString(getThinlet().find(getDialog(), "message"), "text",
|
getThinlet().setString(getThinlet().find(getDialog(), "message"), "text",
|
||||||
"Post successful. (ID " + xmlrpc.getResponse() + ')');
|
"Post successful. (ID " + response + ')');
|
||||||
getThinlet().setBoolean(getThinlet().find(getDialog(), "closebtn"), "enabled", true);
|
getThinlet().setBoolean(getThinlet().find(getDialog(), "closebtn"), "enabled", true);
|
||||||
}
|
}
|
||||||
else
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
alert(xmlrpc.getResponse());
|
alert(e.getMessage());
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
getThinlet().closeDialog(getDialog());
|
|
||||||
getThinlet().handleException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
256
src/net/thauvin/lifeblogger/LifeRPC.java
Normal file
256
src/net/thauvin/lifeblogger/LifeRPC.java
Normal file
|
@ -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 <code>LifeRPC</code> 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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <code>LifeRPCResponse</code> class uses the Thinlet DOM parser to process a XML-RCP response.
|
|
||||||
*
|
|
||||||
* @author <a href="http://www.thauvin.net/erik/">Erik C. Thauvin</a>
|
|
||||||
* @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 <code>true</code> is the response is valid, <code>false</code> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Created by JReleaseInfo AntTask from Open Source Competence Group */
|
/* 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;
|
package net.thauvin.lifeblogger;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -12,28 +12,28 @@ import java.util.Date;
|
||||||
public class ReleaseInfo {
|
public class ReleaseInfo {
|
||||||
|
|
||||||
|
|
||||||
/** buildDate (set during build process to 1107679696812L). */
|
/** buildDate (set during build process to 1113474439925L). */
|
||||||
private static Date buildDate = new Date(1107679696812L);
|
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
|
* @return Date buildDate
|
||||||
*/
|
*/
|
||||||
public static final Date getBuildDate() { return 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
|
* @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"). */
|
/** version (set during build process to "0.2"). */
|
||||||
private static String version = new String("0.1.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
|
* @return String version
|
||||||
*/
|
*/
|
||||||
public static final String getVersion() { return version; }
|
public static final String getVersion() { return version; }
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<menu text="Blog" mnemonic="0">
|
<menu text="Blog" mnemonic="0">
|
||||||
<checkboxmenuitem name="ftp" text="Via FTP" group="via" mnemonic="4" action="setAction(this.name)"/>
|
<checkboxmenuitem name="ftp" text="Via FTP" group="via" mnemonic="4" action="setAction(this.name)"/>
|
||||||
<checkboxmenuitem name="mw" text="Via MetaWeblog" group="via" mnemonic="4" action="setAction(this.name)"/>
|
<checkboxmenuitem name="mw" text="Via MetaWeblog" group="via" mnemonic="4" action="setAction(this.name)"/>
|
||||||
|
<checkboxmenuitem name="ta" text="Via Textamerica" group="via" mnemonic="4" action="setAction(this.name)"/>
|
||||||
</menu>
|
</menu>
|
||||||
<menu text="Help" mnemonic="0">
|
<menu text="Help" mnemonic="0">
|
||||||
<menuitem text="About" action="about()" icon="/icon/about.gif" mnemonic="0"/>
|
<menuitem text="About" action="about()" icon="/icon/about.gif" mnemonic="0"/>
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<dialog name="dialog" icon="/icon/icon.gif" text="Post to Your Blog (Blogger API)" modal="false" columns="1" gap="4" top="4" left="4" bottom="4" right="4">
|
<dialog name="dialog" icon="/icon/icon.gif" text="Post to Your Blog" modal="false" columns="1" gap="4" top="4" left="4" bottom="4" right="4">
|
||||||
<panel name="blog" columns="2" halign="right" gap="4" weightx="1">
|
<panel name="blog" columns="2" halign="right" gap="4" weightx="1">
|
||||||
<label text="RPC URL:"/><textfield name="host" start="0" end="1000"/>
|
<label text="RPC URL:"/><textfield name="host" start="0" end="1000"/>
|
||||||
<label text="Blog ID:"/><textfield name="blogid"/>
|
<label text="Blog ID:"/><textfield name="blogid"/>
|
||||||
<label text="Login:"/><textfield name="login"/>
|
<label text="Login:"/><textfield name="login"/>
|
||||||
<label text="Password:"/><passwordfield name="password"/>
|
<label text="Password:"/><passwordfield name="password"/>
|
||||||
|
<label text="Title: " name="titleFld" visible="false"/><textfield name="title" visible="false"/>
|
||||||
<label valign="top" text="Post:"/>
|
<label valign="top" text="Post:"/>
|
||||||
<textarea name="entry" width="300" height="150" weightx="1" weighty="1"/>
|
<textarea name="entry" width="300" height="150" weightx="1" weighty="1"/>
|
||||||
|
<textfield name="file" visible="false" editable="false"/>
|
||||||
</panel>
|
</panel>
|
||||||
<panel columns="2" halign="right" gap="4">
|
<panel columns="2" halign="right" gap="4">
|
||||||
<button name="cancelbtn" text="Cancel" icon="/icon/exit.gif" action="closeDialog(dialog)"/>
|
<button name="cancelbtn" text="Cancel" icon="/icon/exit.gif" action="closeDialog(dialog)"/>
|
||||||
<!-- <button name="sendbtn" text="Post" icon="/icon/go.gif" action="post(dialog, blog)"/> -->
|
|
||||||
<button name="sendbtn" text="Post" icon="/icon/go.gif" action="publish(dialog, blog)"/>
|
<button name="sendbtn" text="Post" icon="/icon/go.gif" action="publish(dialog, blog)"/>
|
||||||
</panel>
|
</panel>
|
||||||
</dialog>
|
</dialog>
|
|
@ -68,7 +68,7 @@ End If
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td valign="top" width="219"><p>LifeBlogger allows you to post your <a href="http://www.nokia.com/lifeblog/">Nokia Lifeblog</a> favorites to your blog.</p>
|
<td valign="top" width="219"><p>LifeBlogger allows you to post your <a href="http://www.nokia.com/lifeblog/">Nokia Lifeblog</a> favorites to your blog.</p>
|
||||||
<p>Your images, text notes and videos can now be effortlessly posted to your blog using FTP <span class="Grey10px">(<a href="http://www.blogger.com/">Blogger</a>, etc.)</span> or via the MetaWeblog API <span class="Grey10px">(<a href="http://www.movabletype.org/">Movable Type</a>, <a href="http://radio.userland.com/">Radio UserLand</a>, etc.)</span> <a href="http://www.russellbeattie.com/notebook/1007954.html" class="Grey10px">[More...]</a></p>
|
<p>Your images, text notes and videos can now be effortlessly posted to your blog using FTP <span class="Grey10px">(<a href="http://www.blogger.com/">Blogger</a>, etc.)</span> or via the MetaWeblog API <span class="Grey10px">(<a href="http://www.movabletype.org/">Movable Type</a>, <a href="http://radio.userland.com/">Radio UserLand</a>, etc.)</span>, or via the <a href="http://www.textamerica.com/">Textamercia</a> API. <a href="http://www.russellbeattie.com/notebook/1007954.html" class="Grey10px">[More...]</a></p>
|
||||||
<p>LifeBlogger makes extensive use of various <strong>open source libraries</strong>, including:</p>
|
<p>LifeBlogger makes extensive use of various <strong>open source libraries</strong>, including:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="http://www.thinlet.com/">Thinlet</a></li>
|
<li><a href="http://www.thinlet.com/">Thinlet</a></li>
|
||||||
|
@ -76,7 +76,7 @@ End If
|
||||||
<li><a href="http://www.roxes.com/produkte/rat.html">Roxes Ant Tasks</a></li>
|
<li><a href="http://www.roxes.com/produkte/rat.html">Roxes Ant Tasks</a></li>
|
||||||
<li><a href="http://jakarta.apache.org/commons/net/">Commons Net</a></li>
|
<li><a href="http://jakarta.apache.org/commons/net/">Commons Net</a></li>
|
||||||
<li><a href="http://www.ch-werner.de/javasqlite/">JavaSQLite</a></li>
|
<li><a href="http://www.ch-werner.de/javasqlite/">JavaSQLite</a></li>
|
||||||
<li><a href="http://iharder.sourceforge.net/base64/">Base64</a></li>
|
<li><a href="http://ws.apache.org/xmlrpc/">Apache XML-RPC</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><br>
|
<p><br>
|
||||||
</p></td>
|
</p></td>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue