9 Commits

Author SHA1 Message Date
b472f63f17 Fix mistake in BUILD.txt 2020-10-15 01:01:30 +00:00
ad42cbe99b Add detailed instructions to build the plugin in BUILD.txt, known to build with from both the mtn and git mirrors 2020-10-14 22:57:18 +00:00
zzz
0c33a94f14 drop JSON license, no longer bundled 2020-08-23 14:31:41 +00:00
zzz
5b04a9cf40 0.6
Disable pack200
Update makeplugin.sh script
2020-06-08 12:50:11 +00:00
zzz
401271e911 0.5
Remove bundled libjson-simple which conflicts with i2p.jar
Switch to use libjson-simple 2.x API
Requires I2P 0.9.47
2020-05-31 13:46:34 +00:00
zzz
4025f49581 Fixes for Sonarr, v0.4 2017-03-26 16:57:08 +00:00
zzz
4577202ba4 0.3 2017-03-23 13:19:10 +00:00
zzz
f48d7fc412 Set downloadDir for magnets
Hide search button, doesn't work
Replace logo
2017-03-22 14:08:22 +00:00
zzz
2eea36dc3a Implement torrent-add 2017-03-22 00:38:08 +00:00
22 changed files with 705 additions and 1765 deletions

65
BUILD.txt Normal file
View File

@ -0,0 +1,65 @@
Setting up a build environment for I2PSnark-RPC
===============================================
These build instructions seem to work with both git and mtn.
1. You can build I2PSnark-RPC using either the packages that come with
an I2P installation or with packages from the I2P code.
If you have an existing I2P installation and want to use it, you should set
the I2P environment variable to point to your I2P Installation. If you
installed I2P via the .jar, this will be $HOME/i2p. On Apple, the I2P
libraries are found at `/Applications/i2p`. If you have installed I2P using
a Debian package(i.e. `sudo apt-get install i2p i2p-router`) then you have
libraries available at /usr/lib/i2p.
If you have the I2P source code in ../i2p relative to the parent directory,
and have run `ant pkg`, then the libraries will automatically be detected.
./i2p/pkg-tmp
./i2p.plugins.i2psnark-rpc
2. Copy plugin.config from the scripts directory into the plugin directory.
cp scripts/plugin.config plugin/plugin.config
3. If you don't have plugin signing keys yet, the build script will generate them.
However, if you prefer to do this in advance, you can.
To do this with I2P installed in the $HOME directory, you can run the following
script:
java -cp "~/i2p/lib/i2p.jar:/usr/share/java/gnu-getopt.jar" \
net.i2p.crypto.SU3File keygen -t RSA_SHA512_4096 \
"~/.i2p-plugin-keys/user-su3-public-signing.crt" \
"~/.i2p-plugin-keys/user-su3-keystore.ks" \
"user@mail.i2p"
There is a helper script to make this process easier at
[i2pgit.org](https://i2pgit.org/idk/generate-plugin-signing-keys)
[git.idk.i2p](https://i2pgit.org/idk/generate-plugin-signing-keys). If you
have never generated plugin signing keys before, you can run the command:
./i2pk -p $HOME/.i2p-plugin-keys -n 'user' -s 'user@mail.i2p' generate_keys
4. Edit the plugin/plugin.config file. You will need to change the signer field to
match your keys, and add a version line to the file.
name=i2psnark-rpc
-signer=zzz-plugin@mail.i2p
+signer=user@mail.i2p
consoleLinkName=I2PSnark-Remote
consoleLinkURL=/transmission/web/
description=RPC and Web UI for i2psnark
min-java-version=1.7
min-jetty-version=9
min-i2p-version=0.9.47
+version=0.1.2-test
5. You should be ready to build the plugin. Run the script scripts/makeplugin.sh
from the project root.
./scripts/makeplugin.sh
You will be prompted to generate your keystore password. Once entered, the
i2psnark-rpc plugin packages will be generated.

View File

@ -1,3 +1,21 @@
* 2020-06-08 0.6
- Disable pack200
* 2020-05-31 0.5
- Remove bundled libjson-simple which conflicts with i2p.jar
- Switch to use libjson-simple 2.x API in i2p.jar
- Requires I2P 0.9.47
* 2017-03-26 0.4
- Fixes for Sonarr
* 2017-03-23 0.3
- Implement torrent-add
- Fix stats for magnets and downloads
- Replace logo
- Hide search button, doesn't work
- Rename console link
* 2017-03-21 0.2
- Implement percentDone
- Implement startDate, requires 0.9.29-9

View File

@ -11,7 +11,7 @@
<target name="plugin" depends="war">
<!-- get version number -->
<buildnumber file="scripts/build.number" />
<property name="release.number" value="0.2" />
<property name="release.number" value="0.6" />
<!-- we don't bother with an update plugin, everything is in a single war -->
<copy file="LICENSE.txt" todir="plugin/" overwrite="true" />
@ -21,8 +21,8 @@
<arg value="version=${release.number}-b${build.number}" />
</exec>
<exec executable="pack200" failonerror="true">
<arg value="-g" />
<arg value="plugin/console/webapps/transmission.war.pack" />
<arg value="-r" />
<arg value="plugin/console/webapps/transmission.war" />
<arg value="src/build/transmission.war.jar" />
</exec>
<input message="Enter su3 signing key password:" addproperty="release.password.su3" />
@ -47,7 +47,7 @@
<target name="clean" >
<ant dir="src" target="clean" />
<delete file="plugin/plugin.config" />
<delete file="plugin/console/webapps/transmission.war.pack" />
<delete file="plugin/console/webapps/transmission.war" />
<delete file="plugin/LICENSE.txt" />
<delete file="plugin/README.txt" />
<delete file="plugin.zip" />

View File

@ -1,157 +0,0 @@
**** LICENSING NOTE FROM PARG ****
The version included within Vuze in LGPL version 2.1 and is an old version of the software available here:
https://code.google.com/p/json-simple/downloads/detail?name=json_simple.zip&can=2&q=
(archived here: https://web.archive.org/web/20140328054522/https://json-simple.googlecode.com/files/json_simple.zip )
The license was subsequently updated to Apache but the Vuze copy remains LGPL.
***********************************
Simple Java toolkit for JSON (JSON.simple)
==========================================
1.Why the Simple Java toolkit (also named as JSON.simple) for JSON?
When I use JSON as the data exchange format between the AJAX client and JSP
for the first time, what worry me mostly is how to encode Java strings and
numbers correctly in the server side so the AJAX client will receive a well
formed JSON data. When I looked into the 'JSON in Java' directory in JSON
website,I found that wrappers to JSONObject and JSONArray can be simpler,
due to the simplicity of JSON itself. So I wrote the JSON.simple package.
2.Is it simple,really?
I think so. Take an example:
import org.json.simple.JSONObject;
JSONObject obj=new JSONObject();
obj.put("name","foo");
obj.put("num",new Integer(100));
obj.put("balance",new Double(1000.21));
obj.put("is_vip",new Boolean(true));
obj.put("nickname",null);
System.out.print(obj);
Result:
{"nickname":null,"num":100,"balance":1000.21,"is_vip":true,"name":"foo"}
The JSONObject.toString() will escape controls and specials correctly.
3.How to use JSON.simple in JSP?
Take an example in JSP:
<%@page contentType="text/html; charset=UTF-8"%>
<%@page import="org.json.simple.JSONObject"%>
<%
JSONObject obj=new JSONObject();
obj.put("name","foo");
obj.put("num",new Integer(100));
obj.put("balance",new Double(1000.21));
obj.put("is_vip",new Boolean(true));
obj.put("nickname",null);
out.print(obj);
out.flush();
%>
So the AJAX client will get the responseText.
4.Some details about JSONObject?
JSONObject inherits java.util.HashMap,so it don't have to worry about the
mapping things between keys and values. Feel free to use the Map methods
like get(), put(), and remove() and others. JSONObject.toString() will
combine key value pairs to get the JSON data string. Values will be escaped
into JSON quote string format if it's an instance of java.lang.String. Other
type of instance like java.lang.Number,java.lang.Boolean,null,JSONObject and
JSONArray will NOT escape, just take their java.lang.String.valueOf() result.
null value will be the JSON 'null' in the result.
It's still correct if you put an instance of JSONObject or JSONArray into an
instance of JSONObject or JSONArray. Take the example about:
JSONObject obj2=new JSONObject();
obj2.put("phone","123456");
obj2.put("zip","7890");
obj.put("contact",obj2);
System.out.print(obj);
Result:
{"nickname":null,"num":100,"contact":{"phone":"123456","zip":"7890"},"balance":1000.21,"is_vip":true,"name":"foo"}
The method JSONObject.escape() is used to escape Java string into JSON quote
string. Controls and specials will be escaped correctly into \b,\f,\r,\n,\t,
\",\\,\/,\uhhhh.
5.Some detail about JSONArray?
org.json.simple.JSONArray inherits java.util.ArrayList. Feel free to use the
List methods like get(),add(),remove(),iterator() and so on. The rules of
JSONArray.toString() is similar to JSONObject.toString(). Here's the example:
import org.json.simple.JSONArray;
JSONArray array=new JSONArray();
array.add("hello");
array.add(new Integer(123));
array.add(new Boolean(false));
array.add(null);
array.add(new Double(123.45));
array.add(obj2);//see above
System.out.print(array);
Result:
["hello",123,false,null,123.45,{"phone":"123456","zip":"7890"}]
6.What is JSONValue for?
org.json.simple.JSONValue is use to parse JSON data into Java Object.
In JSON, the topmost entity is JSON value, not the JSON object. But
it's not necessary to wrap JSON string,boolean,number and null again,
for the Java has already had the according classes: java.lang.String,
java.lang.Boolean,java.lang.Number and null. The mapping is:
JSON Java
------------------------------------------------
string <=> java.lang.String
number <=> java.lang.Number
true|false <=> java.lang.Boolean
null <=> null
array <=> org.json.simple.JSONArray
object <=> org.json.simple.JSONObject
------------------------------------------------
JSONValue has only one kind of method, JSONValue.parse(), which receives
a java.io.Reader or java.lang.String. Return type of JSONValue.parse()
is according to the mapping above. If the input is incorrect in syntax or
there's exceptions during the parsing, I choose to return null, ignoring
the exception: I have no idea if it's a serious implementaion, but I think
it's convenient to the user.
Here's the example:
String s="[0,{\"1\":{\"2\":{\"3\":{\"4\":[5,{\"6\":7}]}}}}]";
Object obj=JSONValue.parse(s);
JSONArray array=(JSONArray)obj;
System.out.println(array.get(1));
JSONObject obj2=(JSONObject)array.get(1);
System.out.println(obj2.get("1"));
Result:
{"1":{"2":{"3":{"4":[5,{"6":7}]}}}}
{"2":{"3":{"4":[5,{"6":7}]}}}
7.About the author.
I'm a Java EE developer on Linux.
I'm working on web systems and information retrieval systems.
I also develop 3D games and Flash games.
You can contact me through:
Fang Yidong<fangyidong@yahoo.com.cn>
Fang Yidong<fangyidng@gmail.com>

View File

@ -7,6 +7,17 @@
# zzz 2010-02
# zzz 2014-08 added support for su3 files
#
if [ -z "$I2P" -a -d "$PWD/../i2p/pkg-temp" ]; then
export I2P=$PWD/../i2p/pkg-temp
fi
if [ ! -d "$I2P" ]; then
echo "Can't locate your I2P installation. Please add a environment variable named I2P with the path to the folder as value"
echo "On OSX this solved with running: export I2P=/Applications/i2p if default install directory is used."
exit 1
fi
CPATH=$I2P/lib/i2p.jar:/usr/share/java/gnu-getopt.jar
PUBKEYDIR=$HOME/.i2p-plugin-keys
PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key
@ -22,53 +33,53 @@ PLUGINDIR=${1:-plugin}
PC=plugin.config
PCT=${PC}.tmp
if [ ! -d $PLUGINDIR ]
if [ ! -d "$PLUGINDIR" ]
then
echo "You must have a $PLUGINDIR directory"
exit 1
fi
if [ ! -f $PLUGINDIR/$PC ]
if [ ! -f "$PLUGINDIR/$PC" ]
then
echo "You must have a $PLUGINDIR/$PC file"
exit 1
fi
SIGNER=`grep '^signer=' $PLUGINDIR/$PC`
SIGNER=`grep '^signer=' "$PLUGINDIR/$PC"`
if [ "$?" -ne "0" ]
then
echo "You must have a signer name in $PC"
echo 'For example name=foo'
echo 'For example signer=foo'
exit 1
fi
SIGNER=`echo $SIGNER | cut -f 2 -d '='`
if [ ! -f $PRIVKEYFILE ]
if [ ! -f "$PRIVKEYFILE" ]
then
echo "Creating new XPI2P DSA keys"
mkdir -p $PUBKEYDIR || exit 1
java -cp $CPATH net.i2p.crypto.TrustedUpdate keygen $PUBKEYFILE $PRIVKEYFILE || exit 1
java -cp $CPATH net.i2p.data.Base64 encode $PUBKEYFILE $B64KEYFILE || exit 1
mkdir -p "$PUBKEYDIR" || exit 1
java -cp "$CPATH" net.i2p.crypto.TrustedUpdate keygen "$PUBKEYFILE" "$PRIVKEYFILE" || exit 1
java -cp "$CPATH" net.i2p.data.Base64 encode "$PUBKEYFILE" "$B64KEYFILE" || exit 1
rm -rf logs/
chmod 444 $PUBKEYFILE $B64KEYFILE
chmod 400 $PRIVKEYFILE
chmod 444 "$PUBKEYFILE" "$B64KEYFILE"
chmod 400 "$PRIVKEYFILE"
echo "Created new XPI2P keys: $PUBKEYFILE $PRIVKEYFILE"
fi
if [ ! -f $PRIVKEYSTORE ]
if [ ! -f "$PRIVKEYSTORE" ]
then
echo "Creating new SU3 $KEYTYPE keys for $SIGNER"
java -cp $CPATH net.i2p.crypto.SU3File keygen -t $KEYTYPE $PUBKEYSTORE $PRIVKEYSTORE $SIGNER || exit 1
java -cp "$CPATH" net.i2p.crypto.SU3File keygen -t $KEYTYPE "$PUBKEYSTORE" "$PRIVKEYSTORE" $SIGNER || exit 1
echo '*** Save your password in a safe place!!! ***'
rm -rf logs/
# copy to the router dir so verify will work
CDIR=$I2P/certificates/plugin
mkdir -p $CDIR || exit 1
mkdir -p "$CDIR" || exit 1
CFILE=$CDIR/`echo $SIGNER | sed s/@/_at_/`.crt
cp $PUBKEYSTORE $CFILE
chmod 444 $PUBKEYSTORE
chmod 400 $PRIVKEYSTORE
chmod 644 $CFILE
cp "$PUBKEYSTORE" "$CFILE"
chmod 444 "$PUBKEYSTORE"
chmod 400 "$PRIVKEYSTORE"
chmod 644 "$CFILE"
echo "Created new SU3 keys: $PUBKEYSTORE $PRIVKEYSTORE"
echo "Copied public key to $CFILE for testing"
fi
@ -76,7 +87,7 @@ fi
rm -f plugin.zip
OPWD=$PWD
cd $PLUGINDIR
cd "$PLUGINDIR"
grep -q '^name=' $PC
if [ "$?" -ne "0" ]
@ -102,12 +113,12 @@ mv $PCT $PC || exit 1
# add our Base64 key
grep -v '^key=' $PC > $PCT
B64KEY=`cat $B64KEYFILE`
B64KEY=`cat "$B64KEYFILE"`
echo "key=$B64KEY" >> $PCT || exit 1
mv $PCT $PC || exit 1
# zip it
zip -r $OPWD/plugin.zip * || exit 1
zip -r "$OPWD/plugin.zip" * || exit 1
# get the version and use it for the sud header
VERSION=`grep '^version=' $PC | cut -f 2 -d '='`
@ -115,24 +126,24 @@ VERSION=`grep '^version=' $PC | cut -f 2 -d '='`
NAME=`grep '^name=' $PC | cut -f 2 -d '='`
XPI2P=${NAME}.xpi2p
SU3=${NAME}.su3
cd $OPWD
cd "$OPWD"
# sign it
echo 'Signing. ...'
java -cp $CPATH net.i2p.crypto.TrustedUpdate sign plugin.zip $XPI2P $PRIVKEYFILE $VERSION || exit 1
java -cp $CPATH net.i2p.crypto.SU3File sign -c PLUGIN -t $KEYTYPE plugin.zip $SU3 $PRIVKEYSTORE $VERSION $SIGNER || exit 1
java -cp "$CPATH" net.i2p.crypto.TrustedUpdate sign plugin.zip "$XPI2P" "$PRIVKEYFILE" "$VERSION" || exit 1
java -cp "$CPATH" net.i2p.crypto.SU3File sign -c PLUGIN -t $KEYTYPE plugin.zip "$SU3" "$PRIVKEYSTORE" "$VERSION" "$SIGNER" || exit 1
rm -f plugin.zip
# verify
echo 'Verifying. ...'
java -cp $CPATH net.i2p.crypto.TrustedUpdate showversion $XPI2P || exit 1
java -cp $CPATH -Drouter.trustedUpdateKeys=$B64KEY net.i2p.crypto.TrustedUpdate verifysig $XPI2P || exit 1
java -cp $CPATH net.i2p.crypto.SU3File showversion $SU3 || exit 1
java -cp $CPATH net.i2p.crypto.SU3File verifysig -k $PUBKEYSTORE $SU3 || exit 1
java -cp "$CPATH" net.i2p.crypto.TrustedUpdate showversion "$XPI2P" || exit 1
java -cp "$CPATH" -Drouter.trustedUpdateKeys=$B64KEY net.i2p.crypto.TrustedUpdate verifysig "$XPI2P" || exit 1
java -cp "$CPATH" net.i2p.crypto.SU3File showversion "$SU3" || exit 1
java -cp "$CPATH" net.i2p.crypto.SU3File verifysig -k "$PUBKEYSTORE" "$SU3" || exit 1
rm -rf logs/
echo 'Plugin files created: '
wc -c $XPI2P
wc -c $SU3
wc -c "$XPI2P"
wc -c "$SU3"
exit 0

View File

@ -1,6 +1,6 @@
name=i2psnark-rpc
signer=zzz-plugin@mail.i2p
consoleLinkName=Transmission
consoleLinkName=I2PSnark-Remote
consoleLinkURL=/transmission/web/
description=RPC and Web UI for i2psnark
author=zzz@mail.i2p
@ -9,4 +9,4 @@ websiteURL=http://zzz.i2p/forums/16
license=GPLv2
min-java-version=1.7
min-jetty-version=9
min-i2p-version=0.9.29
min-i2p-version=0.9.47

View File

@ -1,205 +0,0 @@
/*
* $Id: ItemList.java,v 1.2 2009-03-15 22:12:18 parg Exp $
* Created on 2006-3-24
*/
package org.json.simple;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/**
* <20><><EFBFBD><EFBFBD><EFBFBD>÷ָ<C3B7><D6B8><EFBFBD><EFBFBD>ֿ<EFBFBD><D6BF><EFBFBD>һ<EFBFBD><D2BB>item.<2E>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>item.ÿ<><C3BF>item<65><6D><EFBFBD>߲<EFBFBD><DFB2><EFBFBD><EFBFBD>ǿհ׷<D5B0>.
* <20><><EFBFBD>
* |a:b:c| => |a|,|b|,|c|
* |:| => ||,||
* |a:| => |a|,||
* @author FangYidong<fangyidong@yahoo.com.cn>
*/
public class ItemList {
private final static String sp=",";
List<String> items=new ArrayList<String>();
public ItemList(){}
/**
*
* @param s <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*/
public ItemList(String s){
this.split(s,sp,items);
}
/**
*
* @param s <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* @param sp <20>ָ<EFBFBD><D6B8><EFBFBD>
*/
//public ItemList(String s,String sp){
// this.sp=s;
// this.split(s,sp,items);
//}
/**
*
* @param s
* @param sp
* @param isMultiToken sp<73>Ƿ<EFBFBD>Ϊ<EFBFBD><CEAA>ָ<EFBFBD><D6B8><EFBFBD>
*/
public ItemList(String s,String sp,boolean isMultiToken){
split(s,sp,items,isMultiToken);
}
public List<String> getItems(){
return this.items;
}
public String[] getArray(){
return (String[])this.items.toArray(new String[items.size()]);
}
public void split(String s,String sp,List<String> append,boolean isMultiToken){
if(s==null || sp==null)
return;
if(isMultiToken){
StringTokenizer tokens=new StringTokenizer(s,sp);
while(tokens.hasMoreTokens()){
append.add(tokens.nextToken().trim());
}
}
else{
this.split(s,sp,append);
}
}
public void split(String s,String sp,List<String> append){
if(s==null || sp==null)
return;
int pos=0;
int prevPos=0;
do{
prevPos=pos;
pos=s.indexOf(sp,pos);
if(pos==-1)
break;
append.add(s.substring(prevPos,pos).trim());
pos+=sp.length();
}while(pos!=-1);
append.add(s.substring(prevPos).trim());
}
/**
* <20><><EFBFBD>÷ָ<C3B7><D6B8><EFBFBD>.
* @param sp <20>ָ<EFBFBD><D6B8><EFBFBD>
*/
//public void setSP(String sp){
// this.sp=sp;
//}
/**
* <20><><EFBFBD><EFBFBD><EBB5A5>item.
* @param i <20><><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><CEBB>(֮ǰ)
* @param item
*/
public void add(int i,String item){
if(item==null)
return;
items.add(i,item.trim());
}
/**
* <20><><EFBFBD><EFBFBD><EBB5A5>item.
* @param item
*/
public void add(String item){
if(item==null)
return;
items.add(item.trim());
}
/**
* <20><>һ<EFBFBD><D2BB>item.
* @param list <20><><EFBFBD><EFBFBD><EFBFBD>list
*/
public void addAll(ItemList list){
items.addAll(list.items);
}
/**
* <20><>һ<EFBFBD><D2BB>item.
* @param s <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*/
public void addAll(String s){
this.split(s,sp,items);
}
/**
* <20><>һ<EFBFBD><D2BB>item.
* @param s <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* @param sp <20>ָ<EFBFBD><D6B8><EFBFBD>
*/
public void addAll(String s,String sp){
this.split(s,sp,items);
}
public void addAll(String s,String sp,boolean isMultiToken){
this.split(s,sp,items,isMultiToken);
}
/**
* <20><>õ<EFBFBD>i<EFBFBD><69>item. 0-based.
* @param i
* @return
*/
public String get(int i){
return (String)items.get(i);
}
/**
* <20><><EFBFBD>item<65><6D>.
* @return
*/
public int size(){
return items.size();
}
/**
* <20>÷ָ<C3B7><D6B8><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8>ı<EFBFBD>ʾ.
*/
public String toString(){
return toString(sp);
}
/**
* <20>÷ָ<C3B7><D6B8><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8>ı<EFBFBD>ʾ.
* @param sp <20><><EFBFBD><EFBFBD>ø÷ָ<C3B7><D6B8><EFBFBD><EFBFBD>ָ<EFBFBD>.
* @return
*/
public String toString(String sp){
StringBuilder sb=new StringBuilder();
for(int i=0;i<items.size();i++){
if(i==0)
sb.append(items.get(i));
else{
sb.append(sp);
sb.append(items.get(i));
}
}
return sb.toString();
}
/**
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>item.
*/
public void clear(){
items.clear();
}
/**
* <20><>λ.<2E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD><DDA3><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><C4AC>ֵ.
*/
public void reset(){
//sp=",";
items.clear();
}
}

View File

@ -1,72 +0,0 @@
/*
* $Id: JSONArray.java,v 1.1 2007-06-05 00:43:56 tuxpaper Exp $
* Created on 2006-4-10
*/
package org.json.simple;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @author FangYidong<fangyidong@yahoo.com.cn>
*/
public class JSONArray extends ArrayList<Object> {
public JSONArray() {
super();
}
public JSONArray(Collection<Object> arg0) {
super(arg0);
}
public JSONArray(int initialCapacity) {
super(initialCapacity);
}
public String toString(){
ItemList list=new ItemList();
Iterator<Object> iter=iterator();
while(iter.hasNext()){
Object value=iter.next();
if(value instanceof String){
list.add("\""+JSONObject.escape((String)value)+"\"");
}
else
list.add(String.valueOf(value));
}
return "["+list.toString()+"]";
}
public void toString( StringBuilder sb ){
sb.append( "[" );
Iterator<Object> iter=iterator();
boolean first = true;
while(iter.hasNext()){
if ( first ){
first = false;
}else{
sb.append( "," );
}
Object value=iter.next();
if(value instanceof String){
sb.append( "\"" );
JSONObject.escape(sb, (String)value);
sb.append( "\"");
}else if ( value instanceof JSONObject ){
((JSONObject)value).toString( sb );
}else if ( value instanceof JSONArray ){
((JSONArray)value).toString( sb );
}else{
sb.append(String.valueOf(value));
}
}
sb.append( "]" );
}
}

View File

@ -1,207 +0,0 @@
/*
* $Id: JSONObject.java,v 1.2 2008-08-07 01:18:54 parg Exp $
* Created on 2006-4-10
*/
package org.json.simple;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author FangYidong<fangyidong@yahoo.com.cn>
*/
public class JSONObject extends HashMap<String,Object>{
public JSONObject() {
super();
}
public JSONObject(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public JSONObject(int initialCapacity) {
super(initialCapacity);
}
public JSONObject(Map<String,Object> arg0) {
super(arg0);
}
public String toString(){
ItemList list=new ItemList();
Iterator<Map.Entry<String, Object>> iter=entrySet().iterator();
while(iter.hasNext()){
Map.Entry<String, Object> entry=iter.next();
list.add(toString(entry.getKey().toString(),entry.getValue()));
}
return "{"+list.toString()+"}";
}
public void toString( StringBuilder sb ){
sb.append( "{" );
Iterator iter=entrySet().iterator();
boolean first = true;
while(iter.hasNext()){
if ( first ){
first = false;
}else{
sb.append( "," );
}
Map.Entry entry=(Map.Entry)iter.next();
toString(sb, entry.getKey().toString(),entry.getValue());
}
sb.append( "}" );
}
public static String toString(String key,Object value){
StringBuilder sb=new StringBuilder();
sb.append("\"");
sb.append(escape(key));
sb.append("\":");
if(value==null){
sb.append("null");
return sb.toString();
}
if(value instanceof String){
sb.append("\"");
sb.append(escape((String)value));
sb.append("\"");
}
else
sb.append(value);
return sb.toString();
}
public static void toString(StringBuilder sb, String key,Object value){
sb.append("\"");
escape(sb,key);
sb.append("\":");
if(value==null){
sb.append("null");
return;
}
if(value instanceof String){
sb.append("\"");
escape(sb,(String)value);
sb.append("\"");
}else if ( value instanceof JSONObject ){
((JSONObject)value).toString( sb );
}else if ( value instanceof JSONArray ){
((JSONArray)value).toString( sb );
}else{
sb.append(String.valueOf( value ));
}
}
/**
* " => \" , \ => \\
* @param s
* @return
*/
public static String escape(String s){
if(s==null)
return null;
StringBuilder sb=new StringBuilder();
for(int i=0;i<s.length();i++){
char ch=s.charAt(i);
switch(ch){
case '"':
sb.append("\\\"");
break;
case '\\':
sb.append("\\\\");
break;
case '\b':
sb.append("\\b");
break;
case '\f':
sb.append("\\f");
break;
case '\n':
sb.append("\\n");
break;
case '\r':
sb.append("\\r");
break;
case '\t':
sb.append("\\t");
break;
case '/':
sb.append("\\/");
break;
default:
if(ch>='\u0000' && ch<='\u001F'){
String ss=Integer.toHexString(ch);
sb.append("\\u");
for(int k=0;k<4-ss.length();k++){
sb.append('0');
}
sb.append(ss.toUpperCase());
}
else{
sb.append(ch);
}
}
}//for
return sb.toString();
}
public static void escape(StringBuilder sb, String s){
if(s==null){
sb.append((String)null);
}else{
for(int i=0;i<s.length();i++){
char ch=s.charAt(i);
switch(ch){
case '"':
sb.append("\\\"");
break;
case '\\':
sb.append("\\\\");
break;
case '\b':
sb.append("\\b");
break;
case '\f':
sb.append("\\f");
break;
case '\n':
sb.append("\\n");
break;
case '\r':
sb.append("\\r");
break;
case '\t':
sb.append("\\t");
break;
case '/':
sb.append("\\/");
break;
default:
if(ch>='\u0000' && ch<='\u001F'){
String ss=Integer.toHexString(ch);
sb.append("\\u");
for(int k=0;k<4-ss.length();k++){
sb.append('0');
}
sb.append(ss.toUpperCase());
}
else{
sb.append(ch);
}
}
}//for
}
}
}

View File

@ -1,46 +0,0 @@
/*
* $Id: JSONValue.java,v 1.1 2007-06-05 00:43:56 tuxpaper Exp $
* Created on 2006-4-15
*/
package org.json.simple;
import java.io.Reader;
import java.io.StringReader;
import java.util.Map;
import org.json.simple.parser.JSONParser;
import org.klomp.snark.rpc.JSONUtils;
/**
* @author FangYidong<fangyidong@yahoo.com.cn>
*/
public class JSONValue {
/**
* parse into java object from input source.
* @param in
* @return instance of : JSONObject,JSONArray,String,Boolean,Long,Double or null
*/
public static Object parse(Reader in){
try{
JSONParser parser=new JSONParser();
return parser.parse(in);
}
catch(Exception e){
return null;
}
}
public static Object parse(String s){
StringReader in=new StringReader(s);
return parse(in);
}
public static String toJSONString(Object value) {
if (value instanceof Map) {
return JSONUtils.encodeToJSON((Map) value);
}
return "";
}
}

View File

@ -1,190 +0,0 @@
/*
* $Id: JSONParser.java,v 1.2 2008-08-07 01:18:55 parg Exp $
* Created on 2006-4-15
*/
package org.json.simple.parser;
import java.io.Reader;
import java.util.Stack;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
/**
* @author FangYidong<fangyidong@yahoo.com.cn>
*/
public class JSONParser {
public static final int S_INIT=0;
public static final int S_IN_FINISHED_VALUE=1;//string,number,boolean,null,object,array
public static final int S_IN_OBJECT=2;
public static final int S_IN_ARRAY=3;
public static final int S_PASSED_PAIR_KEY=4;
public static final int S_IN_ERROR=-1;
private int peekStatus(Stack statusStack){
if(statusStack.size()==0)
return -1;
Integer status=(Integer)statusStack.peek();
return status.intValue();
}
public Object parse(Reader in) throws Exception{
Stack statusStack=new Stack();
Stack valueStack=new Stack();
Yylex lexer=new Yylex(in);
Yytoken token=null;
int status=S_INIT;
try{
do{
token=lexer.yylex();
if(token==null)
token=new Yytoken(Yytoken.TYPE_EOF,null);
switch(status){
case S_INIT:
switch(token.type){
case Yytoken.TYPE_VALUE:
status=S_IN_FINISHED_VALUE;
statusStack.push(new Integer(status));
valueStack.push(token.value);
break;
case Yytoken.TYPE_LEFT_BRACE:
status=S_IN_OBJECT;
statusStack.push(new Integer(status));
valueStack.push(new JSONObject());
break;
case Yytoken.TYPE_LEFT_SQUARE:
status=S_IN_ARRAY;
statusStack.push(new Integer(status));
valueStack.push(new JSONArray());
break;
default:
status=S_IN_ERROR;
}//inner switch
break;
case S_IN_FINISHED_VALUE:
if(token.type==Yytoken.TYPE_EOF)
return valueStack.pop();
else
return null;
case S_IN_OBJECT:
switch(token.type){
case Yytoken.TYPE_COMMA:
break;
case Yytoken.TYPE_VALUE:
if(token.value instanceof String){
String key=(String)token.value;
valueStack.push(key);
status=S_PASSED_PAIR_KEY;
statusStack.push(new Integer(status));
}
else{
status=S_IN_ERROR;
}
break;
case Yytoken.TYPE_RIGHT_BRACE:
if(valueStack.size()>1){
statusStack.pop();
JSONObject map = (JSONObject)valueStack.pop();
status=peekStatus(statusStack);
}
else{
status=S_IN_FINISHED_VALUE;
}
break;
default:
status=S_IN_ERROR;
break;
}//inner switch
break;
case S_PASSED_PAIR_KEY:
switch(token.type){
case Yytoken.TYPE_COLON:
break;
case Yytoken.TYPE_VALUE:
statusStack.pop();
String key=(String)valueStack.pop();
JSONObject parent=(JSONObject)valueStack.peek();
parent.put(key,token.value);
status=peekStatus(statusStack);
break;
case Yytoken.TYPE_LEFT_SQUARE:
statusStack.pop();
key=(String)valueStack.pop();
parent=(JSONObject)valueStack.peek();
JSONArray newArray=new JSONArray();
parent.put(key,newArray);
status=S_IN_ARRAY;
statusStack.push(new Integer(status));
valueStack.push(newArray);
break;
case Yytoken.TYPE_LEFT_BRACE:
statusStack.pop();
key=(String)valueStack.pop();
parent=(JSONObject)valueStack.peek();
JSONObject newObject=new JSONObject();
parent.put(key,newObject);
status=S_IN_OBJECT;
statusStack.push(new Integer(status));
valueStack.push(newObject);
break;
default:
status=S_IN_ERROR;
}
break;
case S_IN_ARRAY:
switch(token.type){
case Yytoken.TYPE_COMMA:
break;
case Yytoken.TYPE_VALUE:
JSONArray val=(JSONArray)valueStack.peek();
val.add(token.value);
break;
case Yytoken.TYPE_RIGHT_SQUARE:
if(valueStack.size()>1){
statusStack.pop();
valueStack.pop();
status=peekStatus(statusStack);
}
else{
status=S_IN_FINISHED_VALUE;
}
break;
case Yytoken.TYPE_LEFT_BRACE:
val=(JSONArray)valueStack.peek();
JSONObject newObject=new JSONObject();
val.add(newObject);
status=S_IN_OBJECT;
statusStack.push(new Integer(status));
valueStack.push(newObject);
break;
case Yytoken.TYPE_LEFT_SQUARE:
val=(JSONArray)valueStack.peek();
JSONArray newArray=new JSONArray();
val.add(newArray);
status=S_IN_ARRAY;
statusStack.push(new Integer(status));
valueStack.push(newArray);
break;
default:
status=S_IN_ERROR;
}//inner switch
break;
case S_IN_ERROR:
return null;
}//switch
if(status==S_IN_ERROR)
return null;
}while(token.type!=Yytoken.TYPE_EOF);
}
catch(Exception e){
throw e;
}
return null;
}
}

View File

@ -1,428 +0,0 @@
package org.json.simple.parser;
class Yylex {
private final static int YY_BUFFER_SIZE = 512;
private final static int YY_F = -1;
private final static int YY_NO_STATE = -1;
private final static int YY_NOT_ACCEPT = 0;
//private final static int YY_START = 1;
private final static int YY_END = 2;
private final static int YY_NO_ANCHOR = 4;
private final static int YY_BOL = 65536;
private final static int YY_EOF = 65537;
private StringBuffer sb=new StringBuffer();
private java.io.BufferedReader yy_reader;
private int yy_buffer_index;
private int yy_buffer_read;
private int yy_buffer_start;
private int yy_buffer_end;
private char yy_buffer[];
private boolean yy_at_bol;
private int yy_lexical_state;
Yylex (java.io.Reader reader) {
this ();
if (null == reader) {
throw (new Error("Error: Bad input stream initializer."));
}
yy_reader = new java.io.BufferedReader(reader);
}
Yylex (java.io.InputStream instream) {
this ();
if (null == instream) {
throw (new Error("Error: Bad input stream initializer."));
}
yy_reader = new java.io.BufferedReader(new java.io.InputStreamReader(instream));
}
private Yylex () {
yy_buffer = new char[YY_BUFFER_SIZE];
yy_buffer_read = 0;
yy_buffer_index = 0;
yy_buffer_start = 0;
yy_buffer_end = 0;
yy_at_bol = true;
yy_lexical_state = YYINITIAL;
}
//private boolean yy_eof_done = false;
private static final int YYINITIAL = 0;
private static final int STRING_BEGIN = 1;
private static final int yy_state_dtrans[] = {
0,
39
};
private void yybegin (int state) {
yy_lexical_state = state;
}
private int yy_advance ()
throws java.io.IOException {
int next_read;
int i;
int j;
if (yy_buffer_index < yy_buffer_read) {
return yy_buffer[yy_buffer_index++];
}
if (0 != yy_buffer_start) {
i = yy_buffer_start;
j = 0;
while (i < yy_buffer_read) {
yy_buffer[j] = yy_buffer[i];
++i;
++j;
}
yy_buffer_end = yy_buffer_end - yy_buffer_start;
yy_buffer_start = 0;
yy_buffer_read = j;
yy_buffer_index = j;
next_read = yy_reader.read(yy_buffer,
yy_buffer_read,
yy_buffer.length - yy_buffer_read);
if (-1 == next_read) {
return YY_EOF;
}
yy_buffer_read = yy_buffer_read + next_read;
}
while (yy_buffer_index >= yy_buffer_read) {
if (yy_buffer_index >= yy_buffer.length) {
yy_buffer = yy_double(yy_buffer);
}
next_read = yy_reader.read(yy_buffer,
yy_buffer_read,
yy_buffer.length - yy_buffer_read);
if (-1 == next_read) {
return YY_EOF;
}
yy_buffer_read = yy_buffer_read + next_read;
}
return yy_buffer[yy_buffer_index++];
}
private void yy_move_end () {
if (yy_buffer_end > yy_buffer_start &&
'\n' == yy_buffer[yy_buffer_end-1])
yy_buffer_end--;
if (yy_buffer_end > yy_buffer_start &&
'\r' == yy_buffer[yy_buffer_end-1])
yy_buffer_end--;
}
//private boolean yy_last_was_cr=false;
private void yy_mark_start () {
yy_buffer_start = yy_buffer_index;
}
private void yy_mark_end () {
yy_buffer_end = yy_buffer_index;
}
private void yy_to_mark () {
yy_buffer_index = yy_buffer_end;
yy_at_bol = (yy_buffer_end > yy_buffer_start) &&
('\r' == yy_buffer[yy_buffer_end-1] ||
'\n' == yy_buffer[yy_buffer_end-1] ||
2028/*LS*/ == yy_buffer[yy_buffer_end-1] ||
2029/*PS*/ == yy_buffer[yy_buffer_end-1]);
}
private java.lang.String yytext () {
return (new java.lang.String(yy_buffer,
yy_buffer_start,
yy_buffer_end - yy_buffer_start));
}
//private int yylength () {
// return yy_buffer_end - yy_buffer_start;
//}
private char[] yy_double (char buf[]) {
int i;
char newbuf[];
newbuf = new char[2*buf.length];
for (i = 0; i < buf.length; ++i) {
newbuf[i] = buf[i];
}
return newbuf;
}
private static final int YY_E_INTERNAL = 0;
//private final int YY_E_MATCH = 1;
private java.lang.String yy_error_string[] = {
"Error: Internal error.\n",
"Error: Unmatched input.\n"
};
private void yy_error (int code,boolean fatal) {
java.lang.System.out.print(yy_error_string[code]);
java.lang.System.out.flush();
if (fatal) {
throw new Error("Fatal Error.\n");
}
}
private static int[][] unpackFromString(int size1, int size2, String st) {
int colonIndex = -1;
String lengthString;
int sequenceLength = 0;
int sequenceInteger = 0;
int commaIndex;
String workString;
int res[][] = new int[size1][size2];
for (int i= 0; i < size1; i++) {
for (int j= 0; j < size2; j++) {
if (sequenceLength != 0) {
res[i][j] = sequenceInteger;
sequenceLength--;
continue;
}
commaIndex = st.indexOf(',');
workString = (commaIndex==-1) ? st :
st.substring(0, commaIndex);
st = st.substring(commaIndex+1);
colonIndex = workString.indexOf(':');
if (colonIndex == -1) {
res[i][j]=Integer.parseInt(workString);
continue;
}
lengthString =
workString.substring(colonIndex+1);
sequenceLength=Integer.parseInt(lengthString);
workString=workString.substring(0,colonIndex);
sequenceInteger=Integer.parseInt(workString);
res[i][j] = sequenceInteger;
sequenceLength--;
}
}
return res;
}
private static final int yy_acpt[] = {
/* 0 */ YY_NOT_ACCEPT,
/* 1 */ YY_NO_ANCHOR,
/* 2 */ YY_NO_ANCHOR,
/* 3 */ YY_NO_ANCHOR,
/* 4 */ YY_NO_ANCHOR,
/* 5 */ YY_NO_ANCHOR,
/* 6 */ YY_NO_ANCHOR,
/* 7 */ YY_NO_ANCHOR,
/* 8 */ YY_NO_ANCHOR,
/* 9 */ YY_NO_ANCHOR,
/* 10 */ YY_NO_ANCHOR,
/* 11 */ YY_NO_ANCHOR,
/* 12 */ YY_NO_ANCHOR,
/* 13 */ YY_NO_ANCHOR,
/* 14 */ YY_NO_ANCHOR,
/* 15 */ YY_NO_ANCHOR,
/* 16 */ YY_NO_ANCHOR,
/* 17 */ YY_NO_ANCHOR,
/* 18 */ YY_NO_ANCHOR,
/* 19 */ YY_NO_ANCHOR,
/* 20 */ YY_NO_ANCHOR,
/* 21 */ YY_NO_ANCHOR,
/* 22 */ YY_NO_ANCHOR,
/* 23 */ YY_NO_ANCHOR,
/* 24 */ YY_NO_ANCHOR,
/* 25 */ YY_NOT_ACCEPT,
/* 26 */ YY_NO_ANCHOR,
/* 27 */ YY_NO_ANCHOR,
/* 28 */ YY_NOT_ACCEPT,
/* 29 */ YY_NOT_ACCEPT,
/* 30 */ YY_NOT_ACCEPT,
/* 31 */ YY_NOT_ACCEPT,
/* 32 */ YY_NOT_ACCEPT,
/* 33 */ YY_NOT_ACCEPT,
/* 34 */ YY_NOT_ACCEPT,
/* 35 */ YY_NOT_ACCEPT,
/* 36 */ YY_NOT_ACCEPT,
/* 37 */ YY_NOT_ACCEPT,
/* 38 */ YY_NOT_ACCEPT,
/* 39 */ YY_NOT_ACCEPT,
/* 40 */ YY_NOT_ACCEPT,
/* 41 */ YY_NOT_ACCEPT,
/* 42 */ YY_NOT_ACCEPT,
/* 43 */ YY_NOT_ACCEPT,
/* 44 */ YY_NOT_ACCEPT
};
private static final int yy_cmap[] = unpackFromString(1,65538,
"11:8,27:2,28,11,27,28,11:18,27,11,2,11:8,16,25,12,14,3,13:10,26,11:6,10:4,1" +
"5,10,11:20,23,1,24,11:3,18,4,10:2,17,5,11:5,19,11,6,11:3,7,20,8,9,11:5,21,1" +
"1,22,11:65410,0:2")[0];
private static final int yy_rmap[] = unpackFromString(1,45,
"0,1:2,2,1:7,3,1:2,4,1:10,5,6,1,7,8,9,10,11,12,13,14,15,16,6,17,18,19,20,21," +
"22")[0];
private static final int yy_nxt[][] = unpackFromString(23,29,
"1,-1,2,-1:2,25,28,-1,29,-1:3,30,3,-1:7,4,5,6,7,8,9,10:2,-1:42,3,33,34,-1,34" +
",-1:24,11,-1,34,-1,34,-1:12,16,17,18,19,20,21,22,23,40,-1:37,31,-1:23,26,-1" +
":24,42,-1:26,32,-1:34,3,-1:34,35,-1:18,37,-1:32,11,-1:27,38,26,-1:2,38,-1:3" +
"2,37,-1:27,12,-1:26,13,-1:11,1,14,15,27:25,-1:5,44:2,-1:4,44,-1:2,44,-1,44," +
"-1,44:2,-1:14,24:2,-1:4,24,-1:2,24,-1,24,-1,24:2,-1:29,36,-1:13,41:2,-1:4,4" +
"1,-1:2,41,-1,41,-1,41:2,-1:14,43:2,-1:4,43,-1:2,43,-1,43,-1,43:2,-1:10");
public Yytoken yylex ()
throws java.io.IOException {
int yy_lookahead;
int yy_anchor = YY_NO_ANCHOR;
int yy_state = yy_state_dtrans[yy_lexical_state];
int yy_next_state = YY_NO_STATE;
int yy_last_accept_state = YY_NO_STATE;
boolean yy_initial = true;
int yy_this_accept;
yy_mark_start();
yy_this_accept = yy_acpt[yy_state];
if (YY_NOT_ACCEPT != yy_this_accept) {
yy_last_accept_state = yy_state;
yy_mark_end();
}
while (true) {
if (yy_initial && yy_at_bol) yy_lookahead = YY_BOL;
else yy_lookahead = yy_advance();
yy_next_state = YY_F;
yy_next_state = yy_nxt[yy_rmap[yy_state]][yy_cmap[yy_lookahead]];
if (YY_EOF == yy_lookahead && true == yy_initial) {
return null;
}
if (YY_F != yy_next_state) {
yy_state = yy_next_state;
yy_initial = false;
yy_this_accept = yy_acpt[yy_state];
if (YY_NOT_ACCEPT != yy_this_accept) {
yy_last_accept_state = yy_state;
yy_mark_end();
}
}
else {
if (YY_NO_STATE == yy_last_accept_state) {
throw (new Error("Lexical Error: Unmatched Input."));
}
else {
yy_anchor = yy_acpt[yy_last_accept_state];
if (0 != (YY_END & yy_anchor)) {
yy_move_end();
}
yy_to_mark();
switch (yy_last_accept_state) {
case 1:
case -2:
break;
case 2:
{ sb.delete(0,sb.length());yybegin(STRING_BEGIN);}
case -3:
break;
case 3:
{ Long val=Long.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE,val);}
case -4:
break;
case 4:
{ return new Yytoken(Yytoken.TYPE_LEFT_BRACE,null);}
case -5:
break;
case 5:
{ return new Yytoken(Yytoken.TYPE_RIGHT_BRACE,null);}
case -6:
break;
case 6:
{ return new Yytoken(Yytoken.TYPE_LEFT_SQUARE,null);}
case -7:
break;
case 7:
{ return new Yytoken(Yytoken.TYPE_RIGHT_SQUARE,null);}
case -8:
break;
case 8:
{ return new Yytoken(Yytoken.TYPE_COMMA,null);}
case -9:
break;
case 9:
{ return new Yytoken(Yytoken.TYPE_COLON,null);}
case -10:
break;
case 10:
{}
case -11:
break;
case 11:
{ Double val=Double.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE,val);}
case -12:
break;
case 12:
{ return new Yytoken(Yytoken.TYPE_VALUE,null);}
case -13:
break;
case 13:
{ Boolean val=Boolean.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE,val);}
case -14:
break;
case 14:
{ sb.append(yytext());}
case -15:
break;
case 15:
{ yybegin(YYINITIAL);return new Yytoken(Yytoken.TYPE_VALUE,sb.toString());}
case -16:
break;
case 16:
{sb.append('\\');}
case -17:
break;
case 17:
{sb.append('"');}
case -18:
break;
case 18:
{sb.append('/');}
case -19:
break;
case 19:
{sb.append('\b');}
case -20:
break;
case 20:
{sb.append('\f');}
case -21:
break;
case 21:
{sb.append('\n');}
case -22:
break;
case 22:
{sb.append('\r');}
case -23:
break;
case 23:
{sb.append('\t');}
case -24:
break;
case 24:
{ int ch=Integer.parseInt(yytext().substring(2),16);
sb.append((char)ch);
}
case -25:
break;
case 26:
{ Double val=Double.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE,val);}
case -26:
break;
case 27:
{ sb.append(yytext());}
case -27:
break;
default:
yy_error(YY_E_INTERNAL,false);
case -1:
}
yy_initial = true;
yy_state = yy_state_dtrans[yy_lexical_state];
yy_next_state = YY_NO_STATE;
yy_last_accept_state = YY_NO_STATE;
yy_mark_start();
yy_this_accept = yy_acpt[yy_state];
if (YY_NOT_ACCEPT != yy_this_accept) {
yy_last_accept_state = yy_state;
yy_mark_end();
}
}
}
}
}
}

View File

@ -1,31 +0,0 @@
/*
* $Id: Yytoken.java,v 1.1 2007-06-05 00:43:56 tuxpaper Exp $
* Created on 2006-4-15
*/
package org.json.simple.parser;
/**
* @author FangYidong<fangyidong@yahoo.com.cn>
*/
public class Yytoken {
public static final int TYPE_VALUE=0;//JSON primitive value: string,number,boolean,null
public static final int TYPE_LEFT_BRACE=1;
public static final int TYPE_RIGHT_BRACE=2;
public static final int TYPE_LEFT_SQUARE=3;
public static final int TYPE_RIGHT_SQUARE=4;
public static final int TYPE_COMMA=5;
public static final int TYPE_COLON=6;
public static final int TYPE_EOF=-1;//end of file
public int type=0;
public Object value=null;
public Yytoken(int type,Object value){
this.type=type;
this.value=value;
}
public String toString(){
return String.valueOf(type+"=>|"+value+"|");
}
}

View File

@ -0,0 +1,357 @@
package org.klomp.snark.rpc;
/*
* Released into the public domain
* with no warranty of any kind, either expressed or implied.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocketEepGet;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.crypto.SHA1;
import net.i2p.data.DataHelper;
import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SecureFile;
import org.klomp.snark.I2PSnarkUtil;
import org.klomp.snark.MetaInfo;
import org.klomp.snark.Snark;
import org.klomp.snark.SnarkManager;
import org.klomp.snark.Storage;
/**
* A cancellable torrent file downloader.
* We extend Snark so its status may be easily listed in the
* web table without adding a lot of code there.
*
* Upon successful download, this Snark will be deleted and
* a "real" Snark created.
*
* The methods return values similar to a Snark in magnet mode.
* A fake info hash, which is the SHA1 of the URL, is returned
* to prevent duplicates.
*
* This Snark may be stopped and restarted, although a partially
* downloaded file is discarded.
*
* @since 0.9.1 Moved from I2PSnarkUtil
*/
public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnable {
private final I2PAppContext _ctx;
private final Log _log;
private final SnarkManager _mgr;
private final String _url;
private final byte[] _fakeHash;
private final String _name;
private final File _dataDir;
private volatile long _remaining = -1;
private volatile long _total = -1;
private volatile long _transferred;
private volatile boolean _isRunning;
private volatile boolean _active;
private volatile long _started;
private String _failCause;
private Thread _thread;
private EepGet _eepGet;
private static final int RETRIES = 3;
/**
* Caller should call _mgr.addDownloader(this), which
* will start things off.
*
* @param dataDir null to default to snark data directory
*/
public FetchAndAdd(I2PAppContext ctx, SnarkManager mgr, String url, File dataDir) {
// magnet constructor
super(mgr.util(), "Torrent download",
null, null, null, null, null, false, null);
_ctx = ctx;
_log = ctx.logManager().getLog(FetchAndAdd.class);
_mgr = mgr;
_url = url;
_name = _t("Download torrent file from {0}", url);
_dataDir = dataDir;
byte[] fake = null;
try {
fake = SHA1.getInstance().digest(url.getBytes("ISO-8859-1"));
} catch (IOException ioe) {}
_fakeHash = fake;
}
/**
* Set off by startTorrent()
*/
public void run() {
File file = get();
if (!_isRunning) // stopped?
return;
_isRunning = false;
if (file != null && file.exists() && file.length() > 0) {
// remove this in snarks
_mgr.deleteMagnet(this);
add(file);
} else {
_mgr.addMessageNoEscape(_t("Torrent was not retrieved from {0}", urlify(_url)) +
((_failCause != null) ? (": " + DataHelper.stripHTML(_failCause)) : ""));
}
if (file != null)
file.delete();
}
/**
* Copied from I2PSnarkUtil so we may add ourselves as a status listener
* @return null on failure
*/
private File get() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetching [" + _url + "]");
File out = null;
try {
out = SecureFile.createTempFile("torrentFile", null, _mgr.util().getTempDir());
} catch (IOException ioe) {
_log.error("temp file error", ioe);
_mgr.addMessage("Temp file error: " + ioe);
if (out != null)
out.delete();
return null;
}
out.deleteOnExit();
if (!_mgr.util().connected()) {
_mgr.addMessage(_t("Opening the I2P tunnel"));
if (!_mgr.util().connect())
return null;
}
I2PSocketManager manager = _mgr.util().getSocketManager();
if (manager == null)
return null;
_eepGet = new I2PSocketEepGet(_ctx, manager, RETRIES, out.getAbsolutePath(), _url);
_eepGet.addStatusListener(this);
_eepGet.addHeader("User-Agent", I2PSnarkUtil.EEPGET_USER_AGENT);
if (_eepGet.fetch()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetch successful [" + _url + "]: size=" + out.length());
return out;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetch failed [" + _url + ']');
out.delete();
return null;
}
}
/**
* Tell SnarkManager to copy the torrent file over and add it to the Snarks list.
* This Snark may then be deleted.
*/
private void add(File file) {
FileInputStream in = null;
try {
in = new FileInputStream(file);
byte[] fileInfoHash = new byte[20];
String name = MetaInfo.getNameAndInfoHash(in, fileInfoHash);
try { in.close(); } catch (IOException ioe) {}
Snark snark = _mgr.getTorrentByInfoHash(fileInfoHash);
if (snark != null) {
_mgr.addMessage(_t("Torrent with this info hash is already running: {0}", snark.getBaseName()));
return;
}
String originalName = Storage.filterName(name);
name = originalName + ".torrent";
File torrentFile = new File(_mgr.getDataDir(), name);
String canonical = torrentFile.getCanonicalPath();
if (torrentFile.exists()) {
if (_mgr.getTorrent(canonical) != null)
_mgr.addMessage(_t("Torrent already running: {0}", name));
else
_mgr.addMessage(_t("Torrent already in the queue: {0}", name));
} else {
// This may take a LONG time to create the storage.
_mgr.copyAndAddTorrent(file, canonical, _dataDir);
snark = _mgr.getTorrentByBaseName(originalName);
if (snark != null)
snark.startTorrent();
else
throw new IOException("Unknown error - check logs");
}
} catch (IOException ioe) {
_mgr.addMessageNoEscape(_t("Torrent at {0} was not valid", urlify(_url)) + ": " + DataHelper.stripHTML(ioe.getMessage()));
} catch (OutOfMemoryError oom) {
_mgr.addMessageNoEscape(_t("ERROR - Out of memory, cannot create torrent from {0}", urlify(_url)) + ": " + DataHelper.stripHTML(oom.getMessage()));
} finally {
try { if (in != null) in.close(); } catch (IOException ioe) {}
}
}
// Snark overrides so all the buttons and stats on the web page work
@Override
public synchronized void startTorrent() {
if (_isRunning)
return;
// reset counters in case starting a second time
_remaining = -1;
// leave the total if we knew it before
//_total = -1;
_transferred = 0;
_failCause = null;
_started = _util.getContext().clock().now();
_isRunning = true;
_active = false;
_thread = new I2PAppThread(this, "Torrent File EepGet", true);
_thread.start();
}
@Override
public synchronized void stopTorrent() {
if (_thread != null && _isRunning) {
if (_eepGet != null)
_eepGet.stopFetching();
_thread.interrupt();
}
_isRunning = false;
_active = false;
}
@Override
public boolean isStopped() {
return !_isRunning;
}
@Override
public String getName() {
return _name;
}
@Override
public String getBaseName() {
return _name;
}
@Override
public byte[] getInfoHash() {
return _fakeHash;
}
/**
* @return torrent file size or -1
*/
@Override
public long getTotalLength() {
return _total;
}
/**
* @return -1 when done so the web will list us as "complete" instead of "seeding"
*/
@Override
public long getRemainingLength() {
long rv = _remaining;
return rv > 0 ? rv : -1;
}
/**
* @return torrent file bytes remaining or -1
*/
@Override
public long getNeededLength() {
return _remaining;
}
@Override
public long getDownloadRate() {
if (_isRunning && _active) {
long time = _ctx.clock().now() - _started;
if (time > 1000) {
long rv = (_transferred * 1000) / time;
if (rv >= 100)
return rv;
}
}
return 0;
}
@Override
public long getDownloaded() {
return _total - _remaining;
}
@Override
public int getPeerCount() {
return (_isRunning && _active && _transferred > 0) ? 1 : 0;
}
@Override
public int getTrackerSeenPeers() {
return (_transferred > 0) ? 1 : 0;
}
// End Snark overrides
// EepGet status listeners to maintain the state for the web page
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
if (bytesRemaining >= 0) {
_remaining = bytesRemaining;
}
_transferred = bytesTransferred;
if (cause != null)
_failCause = cause.toString();
_active = false;
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
if (bytesRemaining >= 0) {
_remaining = bytesRemaining;
_total = bytesRemaining + currentWrite + alreadyTransferred;
}
_transferred = bytesTransferred;
_active = true;
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
if (bytesRemaining >= 0) {
_remaining = bytesRemaining;
_total = bytesRemaining + alreadyTransferred;
}
_transferred = bytesTransferred;
_active = false;
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
if (bytesRemaining >= 0) {
_remaining = bytesRemaining;
}
_transferred = bytesTransferred;
_active = false;
}
public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
// End of EepGet status listeners
private String _t(String s) {
return _mgr.util().getString(s);
}
private String _t(String s, String o) {
return _mgr.util().getString(s, o);
}
private static String urlify(String s) {
return UIUtil.urlify(s);
}
}

View File

@ -20,9 +20,9 @@ package org.klomp.snark.rpc;
import java.io.UnsupportedEncodingException;
import java.util.*;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.JsonArray;
import org.json.simple.JsonObject;
import org.json.simple.Jsoner;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
@ -47,7 +47,7 @@ public class JSONUtils
*/
public static Map decodeJSON(String json) {
try {
Object object = JSONValue.parse(json);
Object object = Jsoner.deserialize(json);
if (object instanceof Map) {
return (Map) object;
}
@ -62,7 +62,7 @@ public class JSONUtils
}
/**
* encodes a map into a JSONObject.
* encodes a map into a JsonObject.
* <P>
* It's recommended that you use {@link #encodeToJSON(Map)} instead
*
@ -71,8 +71,8 @@ public class JSONUtils
*
* @since 3.0.1.5
*/
public static JSONObject encodeToJSONObject(Map map) {
JSONObject newMap = new JSONObject((int)(map.size()*1.5));
public static JsonObject encodeToJSONObject(Map map) {
JsonObject newMap = new JsonObject();
for (Map.Entry<String, Object> entry: ((Map<String,Object>)map).entrySet()){
String key = entry.getKey();
@ -99,14 +99,12 @@ public class JSONUtils
* @since 3.0.1.5
*/
public static String encodeToJSON(Map map) {
JSONObject jobj = encodeToJSONObject(map);
StringBuilder sb = new StringBuilder(8192);
jobj.toString( sb );
return( sb.toString());
JsonObject jobj = encodeToJSONObject(map);
return jobj.toJson();
}
public static String encodeToJSON(Collection list) {
return encodeToJSONArray(list).toString();
return encodeToJSONArray(list).toJson();
}
private static Object coerce(Object value) {
@ -153,8 +151,8 @@ public class JSONUtils
*
* @since 3.0.1.5
*/
private static JSONArray encodeToJSONArray(Collection list) {
JSONArray newList = new JSONArray(list.size());
private static JsonArray encodeToJSONArray(Collection list) {
JsonArray newList = new JsonArray();
for ( Object value: list ){
newList.add(coerce(value));
}

View File

@ -24,6 +24,13 @@ public class RPCServlet extends HttpServlet {
private SnarkManager _manager;
private XMWebUIPlugin _plugin;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Sonarr does a GET to test,
// pass it through so it will get the 409 response
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
XMWebUIPlugin plugin;

View File

@ -1,5 +1,10 @@
package org.klomp.snark.rpc;
import java.io.File;
import java.io.IOException;
import net.i2p.data.DataHelper;
import org.klomp.snark.PeerID;
/**
@ -84,4 +89,69 @@ class UIUtil {
}
return buf.toString();
}
/**
* Is "a" equal to "b",
* or is "a" a directory and a parent of file or directory "b",
* canonically speaking?
*
* @since 0.9.15
*/
public static boolean isParentOf(File a, File b) {
try {
a = a.getCanonicalFile();
b = b.getCanonicalFile();
} catch (IOException ioe) {
return false;
}
if (a.equals(b))
return true;
if (!a.isDirectory())
return false;
// easy case
if (!b.getPath().startsWith(a.getPath()))
return false;
// dir by dir
while (!a.equals(b)) {
b = b.getParentFile();
if (b == null)
return false;
}
return true;
}
/**
* This is for a full URL. For a path only, use encodePath().
* @since 0.7.14
*/
static String urlify(String s) {
return urlify(s, 100);
}
/**
* This is for a full URL. For a path only, use encodePath().
* @since 0.9
*/
private static String urlify(String s, int max) {
StringBuilder buf = new StringBuilder(256);
// browsers seem to work without doing this but let's be strict
String link = urlEncode(s);
String display;
if (s.length() <= max)
display = DataHelper.escapeHTML(link);
else
display = DataHelper.escapeHTML(s.substring(0, max)) + "&hellip;";
buf.append("<a href=\"").append(link).append("\">").append(display).append("</a>");
return buf.toString();
}
/**
* This is for a full URL. For a path only, use encodePath().
* @since 0.8.13
*/
private static String urlEncode(String s) {
return s.replace(";", "%3B").replace("&", "&amp;").replace(" ", "%20")
.replace("<", "%3C").replace(">", "%3E")
.replace("[", "%5B").replace("]", "%5D");
}
}

View File

@ -19,6 +19,7 @@
*/
package org.klomp.snark.rpc;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -31,6 +32,7 @@ import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Collection;
import java.util.Collections;
@ -40,6 +42,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@ -61,11 +64,13 @@ import net.i2p.data.Base64;
import net.i2p.data.Destination;
import net.i2p.servlet.RequestWrapper;
import net.i2p.util.Log;
import net.i2p.util.SecureFile;
import org.gudy.azureus2.plugins.download.DownloadException;
import org.klomp.snark.BitField;
import org.klomp.snark.I2PSnarkUtil;
import org.klomp.snark.MagnetURI;
import org.klomp.snark.MetaInfo;
import org.klomp.snark.Peer;
import org.klomp.snark.PeerID;
@ -75,8 +80,7 @@ import org.klomp.snark.Storage;
import org.klomp.snark.TrackerClient;
import org.klomp.snark.bencode.BEncoder;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.JsonObject;
@SuppressWarnings({
"unchecked",
@ -420,7 +424,7 @@ XMWebUIPlugin {
generateSupport(HttpServletRequest request, HttpServletResponse response) throws IOException {
boolean logit = trace_param;
if (logit) {
log("-> " + request.getServletPath());
log("-> " + request.getMethod() + ' ' + request.getServletPath());
String qs = request.getQueryString();
if (qs != null)
log( "-> query: " + qs);
@ -435,10 +439,6 @@ XMWebUIPlugin {
response.setStatus( 415 );
return true;
}
if (!request.getMethod().equals("POST")) {
response.setStatus( 405 );
return true;
}
try {
String session_id = getSessionID( request );
// Set cookie just in case client is looking for one..
@ -453,6 +453,12 @@ XMWebUIPlugin {
response.getOutputStream().write("You_didn_t_set_the_X-Transmission-Session-Id".getBytes());
return true;
}
if (!request.getMethod().equals("POST")) {
// Sonarr does a GET for testing and to get the 409, shouldn't go past here
response.setContentType("text/plain; charset=UTF-8");
response.setStatus(200);
return true;
}
String session_id_plus = session_id;
// XXX getHeaders() keys are lowercase.. this line always null?
String tid = request.getHeader( "X-XMRPC-Tunnel-ID" );
@ -833,13 +839,11 @@ XMWebUIPlugin {
method_Session_Get(args, result);
} else if ( method.equals( "session-stats" )) {
method_Session_Stats(args, result);
/****
} else if ( method.equals( "torrent-add" )) {
String agent = MapUtils.getMapString(request.getHeaders(), "User-Agent", "");
boolean xmlEscape = agent.startsWith("Mozilla/");
String agent = request.getHeader("User-Agent");
boolean xmlEscape = agent != null && agent.startsWith("Mozilla/");
method_Torrent_Add(args, result, xmlEscape);
// this is handled within the torrent-add method: save_core_state = true;
****/
} else if ( method.equals( "torrent-start-all" )) {
checkUpdatePermissions();
_manager.startAllTorrents();
@ -1184,7 +1188,7 @@ XMWebUIPlugin {
List<Map> l_engines = new ArrayList<Map>();
result.put("engines", l_engines);
for (Engine engine : engines) {
JSONObject map = new JSONObject();
JsonObject map = new JsonObject();
l_engines.add(map);
map.put("name", engine.getName());
map.put("id", engine.getUID());
@ -1922,7 +1926,8 @@ XMWebUIPlugin {
result.put( "rpc-version-minimum", Long.valueOf(6)); // number the minimum RPC API version supported
result.put( "seedRatioLimit", Double.valueOf(100.0) ); // double the default seed ratio for torrents to use
result.put( "seedRatioLimited", Boolean.FALSE ); // boolean true if seedRatioLimit is honored by default
result.put( "version", CoreVersion.VERSION); // string
result.put( "version", "2.80"); // This must match the RPC API from the spec to make Sonarr happy
result.put( "i2p-version", CoreVersion.VERSION); // unused
result.put( "az-rpc-version", VUZE_RPC_VERSION);
result.put( "az-version", az_version ); // string
result.put( "az-mode", az_mode ); // string
@ -2915,7 +2920,6 @@ XMWebUIPlugin {
form of one of 3.3's tr_info objects with the
fields for id, name, and hashString.
*/
/****
private void
method_Torrent_Add(
final Map args,
@ -2927,46 +2931,41 @@ XMWebUIPlugin {
String metainfoString = (String) args.get("metainfo");
byte[] metainfoBytes = null;
if ( metainfoString != null ) {
metainfoBytes = Base64.decode( metainfoString.replaceAll("[\r\n]+", "") );
//metainfoBytes = Base64.decode( metainfoString );
VuzeFileHandler vfh = VuzeFileHandler.getSingleton();
if ( vfh != null ) {
VuzeFile vf = vfh.loadVuzeFile( metainfoBytes );
if ( vf != null ) {
VuzeFileComponent[] comps = vf.getComponents();
for ( VuzeFileComponent comp: comps ) {
if ( comp.getType() != VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ) {
throw( new TextualException( "Unsupported Vuze File component type: " + comp.getTypeName()));
}
}
vfh.handleFiles( new VuzeFile[]{ vf }, VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE );
String added_templates = "";
for ( VuzeFileComponent comp: comps ) {
if ( comp.isProcessed()) {
Engine e = (Engine)comp.getData( Engine.VUZE_FILE_COMPONENT_ENGINE_KEY );
if ( e != null ) {
added_templates += (added_templates==""?"":", ") + e.getName();
}
}
}
if ( added_templates.length() == 0 ) {
throw( new TextualException( "No search template(s) added" ));
} else {
throw( new TextualException( "Installed search template(s): " + added_templates ));
}
}
}
metainfoBytes = Base64.decode( metainfoString.replaceAll("[\r\n]+", ""), true );
if (metainfoBytes == null)
throw new TextualException("bad metainfo base64");
}
Snark torrent = null;
MetaInfo torrent = null;
Snark download = null;
String url = (String) args.get("filename");
final boolean add_stopped = getBoolean(args.get("paused"));
String download_dir = (String) args.get("download-dir");
final File file_Download_dir = download_dir == null ? null : new File(download_dir);
final File file_Download_dir = download_dir == null ? null : new SecureFile(download_dir);
if (file_Download_dir != null) {
// This code is copied from I2PSnarkServlet
if (!file_Download_dir.isAbsolute()) {
throw new TextualException(_t("Data directory must be an absolute path") + ": " + file_Download_dir);
}
if (!file_Download_dir.isDirectory() && !file_Download_dir.mkdirs()) {
throw new TextualException(_t("Data directory cannot be created") + ": " + file_Download_dir);
}
Collection<Snark> snarks = _manager.getTorrents();
for (Snark s : snarks) {
Storage storage = s.getStorage();
if (storage == null)
continue;
File sbase = storage.getBase();
if (UIUtil.isParentOf(sbase, file_Download_dir)) {
throw new TextualException(_t("Cannot add torrent {0} inside another torrent: {1}",
file_Download_dir.getAbsolutePath(), sbase));
}
}
}
// peer-limit not used
//getNumber(args.get("peer-limit"), 0);
// bandwidthPriority not used
//getNumber(args.get("bandwidthPriority"), TR_PRI_NORMAL);
/****
final DownloadWillBeAddedListener add_listener =
new DownloadWillBeAddedListener() {
public void initialised(Snark download) {
@ -3012,6 +3011,7 @@ XMWebUIPlugin {
DiskManagerFileInfo.PRIORITY_LOW);
}
}
// don't need priority-normal if they are normal by default.
// handle initial categories/tags
try {
@ -3038,164 +3038,52 @@ XMWebUIPlugin {
}
}
};
TorrentManager torrentManager = plugin_interface.getTorrentManager();
****/
boolean duplicate = false;
if ( metainfoBytes != null ) {
try {
torrent = torrentManager.createFromBEncodedData( metainfoBytes);
SnarkManager dm = _manager;
download = dm.getDownload( torrent );
duplicate = download != null;
} catch (Throwable e) {
e.printStackTrace();
//System.err.println("decode of " + new String(Base64.encode(metainfoBytes), "UTF8"));
throw (new IOException("torrent download failed: "
+ Debug.getNestedExceptionMessage(e)));
torrent = new MetaInfo(new ByteArrayInputStream(metainfoBytes));
download = _manager.getTorrentByInfoHash(torrent.getInfoHash());
duplicate = download != null;
if ( download == null ) {
boolean success = _manager.addTorrent( torrent, null, null, file_Download_dir, add_stopped );
if (success)
download = _manager.getTorrentByInfoHash(torrent.getInfoHash());
}
} else if (url == null) {
throw (new IOException("url missing"));
} else {
url = url.trim().replaceAll(" ", "%20");
// hack due to core bug - have to add a bogus arg onto magnet uris else they fail to parse
String lc_url = url.toLowerCase( Locale.US );
if ( lc_url.startsWith("magnet:")) {
url += "&dummy_param=1";
} else if (!lc_url.startsWith("http")) {
url = UrlUtils.parseTextForURL(url, true, true);
}
byte[] hashFromMagnetURI = getHashFromMagnetURI(url);
if (hashFromMagnetURI != null) {
org.gudy.azureus2.plugins.download.SnarkManager dm = _manager;
download = dm.getDownload(hashFromMagnetURI);
download = _manager.getTorrentByInfoHash(hashFromMagnetURI);
duplicate = download != null;
}
if (download == null) {
URL torrent_url;
try {
torrent_url = new URL(url);
} catch (MalformedURLException mue) {
throw new TextualException("The torrent URI was not valid");
}
try {
final TorrentDownloader dl = torrentManager.getURLDownloader(torrent_url, null, null);
Object cookies = args.get("cookies");
if ( cookies != null ) {
dl.setRequestProperty("URL_Cookie", cookies);
}
boolean is_magnet = torrent_url.getProtocol().equalsIgnoreCase( "magnet" );
if ( is_magnet ) {
TimerEvent magnet_event = null;
final Object[] f_result = { null };
try {
final AESemaphore sem = new AESemaphore( "magnetsem" );
final URL f_torrent_url = torrent_url;
final String f_name = (String) args.get("name");
magnet_event = SimpleTimer.addEvent(
"magnetcheck",
_context.clock().getOffsetTime( 10*1000 ),
new TimerEventPerformer()
{
public void
perform(
TimerEvent event )
{
synchronized( f_result ) {
if ( f_result[0] != null ) {
return;
}
MagnetSnark magnet_download = new MagnetDownload( f_torrent_url, f_name );
byte[] hash = magnet_download.getInfoHash();
synchronized( magnet_downloads ) {
boolean duplicate = false;
Iterator<MagnetDownload> it = magnet_downloads.iterator();
while( it.hasNext()) {
MagnetSnark md = it.next();
if ( hash.length > 0 && Arrays.equals( hash, md.getInfoHash())) {
if ( md.getError() == null ) {
duplicate = true;
magnet_download = md;
break;
} else {
it.remove();
addRecentlyRemoved( md );
}
}
}
if ( !duplicate ) {
magnet_downloads.add( magnet_download );
}
}
f_result[0] = magnet_download;
}
sem.release();
}
});
new AEThread2( "magnetasync" )
{
public void
run()
{
try {
MetaInfo torrent = dl.download("UTF-8");
synchronized( f_result ) {
if ( f_result[0] == null ) {
f_result[0] = torrent;
} else {
MagnetSnark md = (MagnetDownload)f_result[0];
boolean already_removed;
synchronized( magnet_downloads ) {
already_removed = !magnet_downloads.remove( md );
}
if ( !already_removed ) {
addRecentlyRemoved( md );
addTorrent( torrent, file_Download_dir, add_stopped, add_listener );
}
}
}
} catch( Throwable e ) {
synchronized( f_result ) {
if ( f_result[0] == null ) {
f_result[0] = e;
} else {
MagnetSnark md = (MagnetDownload)f_result[0];
md.setError( e );
}
}
} finally {
sem.release();
}
}
}.start();
sem.reserve();
Object res;
synchronized( f_result ) {
res = f_result[0];
}
if ( res instanceof Snark ) {
torrent = (Torrent)res;
} else if ( res instanceof Throwable ) {
throw((Throwable)res);
} else {
download = (MagnetDownload)res;
torrent = null;
}
} finally {
if ( magnet_event != null ) {
magnet_event.cancel();
}
}
// This code is copied from I2PSnarkServlet
if (url.startsWith("http://")) {
download = new FetchAndAdd(_context, _manager, url, file_Download_dir);
_manager.addDownloader(download);
} else {
torrent = dl.download("UTF-8");
if (url.startsWith(MagnetURI.MAGNET) || url.startsWith(MagnetURI.MAGGOT)) {
addMagnet(url, file_Download_dir);
} else if (url.length() == 40 && url.replaceAll("[a-fA-F0-9]", "").length() == 0) {
// hex
url = url.toUpperCase(Locale.US);
addMagnet(MagnetURI.MAGNET_FULL + url, file_Download_dir);
} else if (url.length() == 32 && url.replaceAll("[a-zA-Z2-7]", "").length() == 0) {
// b32
url = url.toUpperCase(Locale.US);
addMagnet(MagnetURI.MAGNET_FULL + url, file_Download_dir);
} else {
throw new TextualException("The torrent URI was not valid");
}
download = _manager.getTorrentByInfoHash(hashFromMagnetURI);
if (download == null)
throw new TextualException("Magnet add failed"); //shouldn't happen
}
} catch( Throwable e ) {
e.printStackTrace();
throw( new IOException( Debug.getNestedExceptionMessage( e )));
}
}
}
if ( download == null ) {
download = addTorrent( torrent, file_Download_dir, add_stopped, add_listener );
}
// download must be non-null here, either the new torrent or the old duplicate
Map<String, Object> torrent_details = new HashMap<String, Object>();
torrent_details.put("id", new Long(getID(download, true)));
torrent_details.put("name", xmlEscape ? escapeXML(download.getName()) : download.getName());
@ -3204,15 +3092,32 @@ XMWebUIPlugin {
result.put(duplicate ? "torrent-duplicate" : "torrent-added", torrent_details);
}
private static byte[] getHashFromMagnetURI(String magnetURI) {
Pattern patXT = Pattern.compile("xt=urn:(?:btih|sha1):([^&]+)");
Matcher matcher = patXT.matcher(magnetURI);
if (matcher.find()) {
return UrlUtils.decodeSHA1Hash(matcher.group(1));
private byte[] getHashFromMagnetURI(String magnetURI) {
try {
MagnetURI magnet = new MagnetURI(_util, magnetURI);
return magnet.getInfoHash();
} catch (IllegalArgumentException iae) {
return null;
}
}
/**
* Copied from I2PSnarkServlet
* @param url in base32 or hex
* @param dataDir null to default to snark data directory
* @since 0.8.4
*/
private void addMagnet(String url, File dataDir) {
try {
MagnetURI magnet = new MagnetURI(_util, url);
String name = magnet.getName();
byte[] ih = magnet.getInfoHash();
String trackerURL = magnet.getTrackerURL();
_manager.addMagnet(name, ih, trackerURL, true, dataDir);
} catch (IllegalArgumentException iae) {
throw new TextualException(_t("Invalid magnet URL {0}", url));
}
return null;
}
****/
private Map
method_Torrent_Get(
@ -3417,6 +3322,9 @@ XMWebUIPlugin {
// downloadDir | string | tr_torrent
if (storage != null) {
value = storage.getBase().getAbsolutePath();
} else {
//value = "TBD";
value = _manager.getDataDir().getAbsolutePath();
}
} else if (field.equals("downloadedEver")) {
// RPC v0
@ -3637,10 +3545,12 @@ XMWebUIPlugin {
long needed = download.getNeededLength();
if (needed < 0)
needed = download.getRemainingLength();
if (needed < 0)
needed = 1; // TODO
long whenDone = download.getTotalLength() - download.getSkippedLength();
value = 1.0f - (needed / (float) whenDone);
if (needed < 0) {
value = 0.0f;
} else {
long whenDone = download.getTotalLength() - download.getSkippedLength();
value = 1.0f - (needed / (float) whenDone);
}
} else if (field.equals("pieces")) {
// RPC v5
value = torrentGet_pieces(download);
@ -3712,7 +3622,7 @@ XMWebUIPlugin {
* if only some of the torrent's files are wanted.
* [0...tr_info.totalSize]
**/
value = download.getTotalLength() - download.getSkippedLength();
value = Math.max(download.getTotalLength() - download.getSkippedLength(), 1L);
} else if (field.equals("startDate")) {
// When the torrent was last started.
try {
@ -3749,7 +3659,7 @@ XMWebUIPlugin {
boolean hack = agent != null && agent.contains("httpok"); // Torrnado
value = torrentGet_trackers(download, hack);
} else if (field.equals("totalSize")) {
value = download.getTotalLength();
value = Math.max(download.getTotalLength(), 1L);
} else if (field.equals("torrentFile")) {
// torrentFile | string | tr_info
// Path to torrent
@ -5437,176 +5347,6 @@ XMWebUIPlugin {
}
}
/****
private class
MagnetDownload
implements Snark
{
private URL magnet_url;
private String name;
private byte[] hash;
private long create_time;
private Map<TorrentAttribute,Long> attributes = new HashMap<TorrentAttribute, Long>();
private String temp_dir = _util.getTempDir().getAbsolutePath();
private Throwable error;
private
MagnetDownload(
URL _magnet,
String friendlyName )
{
create_time = _context.clock().now();
magnet_url = _magnet;
String str = magnet_url.toExternalForm();
int pos = str.indexOf( '?' );
if ( pos != -1 ) {
str = str.substring( pos+1 );
}
String[] args = str.split( "&" );
Map<String,String> arg_map = new HashMap<String,String>();
for ( String arg: args ) {
String[] bits = arg.split( "=" );
if ( bits.length == 2 ) {
try {
String lhs = bits[0].trim().toLowerCase( Locale.US );
String rhs = URLDecoder.decode( bits[1].trim(), "UTF-8");
if ( lhs.equals( "xt" )) {
if ( rhs.toLowerCase( Locale.US ).startsWith( "urn:btih:" )) {
arg_map.put( lhs, rhs );
} else {
String existing = arg_map.get( "xt" );
if ( existing == null ||
( !existing.toLowerCase( Locale.US ).startsWith( "urn:btih:" ) && rhs.startsWith( "urn:sha1:" ))) {
arg_map.put( lhs, rhs );
}
}
} else {
arg_map.put( lhs, rhs );
}
} catch( Throwable e ) {
}
}
}
hash = new byte[0];
String hash_str = arg_map.get( "xt" );
if ( hash_str != null ) {
hash_str = hash_str.toLowerCase( Locale.US );
if ( hash_str.startsWith( "urn:btih:" ) || hash_str.startsWith( "urn:sha1" )) {
hash = UrlUtils.decodeSHA1Hash( hash_str.substring( 9 ));
}
}
name = arg_map.get( "dn" );
if ( name == null ) {
if ( friendlyName != null ) {
name = friendlyName;
} else if ( hash == null ) {
name = magnet_url.toExternalForm();
} else {
name = Base32.encode( hash );
}
}
name = "Magnet download for '" + name + "'";
getID( this, true );
}
private long
getCreateTime()
{
return( create_time );
}
private URL
getMagnetURL()
{
return( magnet_url );
}
public boolean
isStub()
{
return( true );
}
public Snark
destubbify()
throws DownloadException
{
throw( new DownloadException( "Not supported" ));
}
public String
getName()
{
return( name );
}
public byte[]
getInfoHash()
{
return( hash );
}
public MetaInfo
getTorrent()
{
return( null );
}
public long
getTorrentSize()
{
return( 16*1024 ); // dont know the size
}
public String
getSavePath()
{
return( temp_dir );
}
private void
setError(
Throwable e )
{
error = e;
}
private Throwable
getError()
{
return( error );
}
public SnarkFile[]
getStubFiles()
{
return( new SnarkFile[0]);
}
public long
getLongAttribute(
TorrentAttribute attribute )
{
Long l = attributes.get( attribute );
return( l==null?0:l );
}
public void
setLongAttribute(
TorrentAttribute attribute,
long value)
{
attributes.put( attribute, value );
}
public void
remove()
throws DownloadException
{
}
}
****/
/**
* Copied from Vuze UrlUtils.java
*/
@ -5656,6 +5396,16 @@ XMWebUIPlugin {
return _util.getString(s);
}
/** translate */
private String _t(String s, Object o) {
return _util.getString(s, o);
}
/** translate */
private String _t(String s, Object o, Object o2) {
return _util.getString(s, o, o2);
}
protected void log(String str) {
_log.debug(str);
}

View File

@ -1,6 +1,6 @@
<html>
<body>
<h2>i2psnark-rpc Plugin Version 0.2</h2>
<h2>i2psnark-rpc Plugin Version 0.4</h2>
<p>
This is a Transmission- and Vuze- compatible RPC server for i2psnark,
with the Vuze-modified Transmission web UI.
@ -16,6 +16,11 @@ specify port 7657. For example, to list the torrents:
transmission-remote 7657 -l
</pre>
<p>
For <a href="https://sonarr.tv/">Sonarr</a>:
Settings -&gt; Download Client -&gt click "+" -&gt; click "Transmission" -&gt; change port to 7657 and click "Save"
<p>
Most basic features are supported. Several advanced features are not supported.
Some may be added in a future release.

View File

@ -35,13 +35,6 @@ Copyright (c) Transmission authors and contributors
<br>
See <a href="licenses/Transmission.txt">Transmission.txt</a>
<p>
JSON:
<br>
LGPLv2.1
<br>
See <a href="licenses/JSON.txt">JSON.txt</a>
<p>
Transmission/Vuze web UI:
<br>

View File

@ -49,7 +49,7 @@
<script type="text/javascript" src="./javascript/notifications.js"></script>
<script type="text/javascript" src="./javascript/easyXDM-2.4.18.4.min.js"></script>
<script type="text/javascript" src="./javascript/vuze.js"></script>
<title>Vuze Remote</title>
<title>I2PSnark Remote</title>
</head>
<body id="transmission_body">
@ -62,7 +62,9 @@
<div id="toolbar-remove" title="Remove Selected Torrents"></div>
<div id="toolbar-start-all" title="Start All Torrents"></div>
<div id="toolbar-pause-all" title="Pause All Torrents"></div>
<!--
<div id="toolbar-search" title="Search"></div>
-->
<div id="toolbar-inspector" title="Toggle Inspector"></div>
</div>
@ -92,7 +94,7 @@
</div>
<div id="torrent_logo">
<img src="style/transmission/images/graphics/vuze_remote_logo.png" alt="Vuze Remote">
<img src="style/i2p/images/graphics/i2plogo.png" alt="I2PSnark Remote">
</div>
<div id="statusbar">

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB