mirror of
https://github.com/Rogiel/httpchannel
synced 2025-12-05 23:22:51 +00:00
Implemented streaming http uploads and downloads for MegaUpload
This commit is contained in:
33
pom.xml
33
pom.xml
@@ -1,8 +1,39 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.rogiel.seedbox</groupId>
|
<groupId>com.rogiel.seedbox</groupId>
|
||||||
<artifactId>seedbox-httpchannel</artifactId>
|
<artifactId>seedbox-httpchannel</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
<name>Seedbox - HTTP Channel library</name>
|
<name>Seedbox - HTTP Channel library</name>
|
||||||
<description>Library capable of downloading and uploading files from free servers using channels.</description>
|
<description>Library capable of downloading and uploading files from free servers using channels.</description>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.9</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>4.1.2</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpmime</artifactId>
|
||||||
|
<version>4.1.2</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>1.3.2</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.DownloadListener.TimerWaitReason;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.ThreadUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author rogiel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class AbstractDownloader implements Downloader {
|
||||||
|
protected void timer(DownloadListener listener, long timer) {
|
||||||
|
listener.timer(timer, TimerWaitReason.DOWNLOAD_TIMER);
|
||||||
|
ThreadUtils.sleep(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean cooldown(DownloadListener listener, long cooldown)
|
||||||
|
throws IOException {
|
||||||
|
if (listener.timer(cooldown, TimerWaitReason.COOLDOWN)) {
|
||||||
|
ThreadUtils.sleep(cooldown);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new IOException("Timer " + TimerWaitReason.COOLDOWN
|
||||||
|
+ " aborted due to listener request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.config.ServiceConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base service for HTTP enabled services.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public abstract class AbstractHttpService<T extends ServiceConfiguration>
|
||||||
|
extends AbstractService<T> implements Service {
|
||||||
|
/**
|
||||||
|
* The {@link HttpClient} instance for this service
|
||||||
|
*/
|
||||||
|
protected HttpClient client = new DefaultHttpClient();
|
||||||
|
|
||||||
|
protected AbstractHttpService(T configuration) {
|
||||||
|
super(configuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.config.ServiceConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an abstract {@link Service} implementation.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @version 1.0
|
||||||
|
* @param <T>
|
||||||
|
* The {@link ServiceConfiguration} <b>interface</b> type used by the
|
||||||
|
* {@link Service}. Note that this <b>must</b> be an interface!s
|
||||||
|
* @see ServiceConfiguration ServiceConfiguration for details on the configuration interface.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractService<T extends ServiceConfiguration>
|
||||||
|
implements Service {
|
||||||
|
protected final T configuration;
|
||||||
|
|
||||||
|
protected AbstractService(T configuration) {
|
||||||
|
this.configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getServiceConfiguration() {
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements an service capable of authenticating into an account.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public interface AuthenticationService extends Service {
|
||||||
|
/**
|
||||||
|
* Creates {@link Authenticator} instance for this service. This instance is
|
||||||
|
* attached to an {@link Credential} and to its parent {@link Service}.
|
||||||
|
*
|
||||||
|
* @param credential
|
||||||
|
* the credential
|
||||||
|
* @return an new {@link Authenticator} instance
|
||||||
|
*/
|
||||||
|
Authenticator getAuthenticator(Credential credential);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the matrix of capabilities for this {@link Authenticator}.
|
||||||
|
*
|
||||||
|
* @return {@link CapabilityMatrix} with all capabilities of this
|
||||||
|
* {@link Authenticator}.
|
||||||
|
* @see AuthenticatorCapability
|
||||||
|
*/
|
||||||
|
CapabilityMatrix<AuthenticatorCapability> getAuthenticationCapability();
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interfaces provides authentication for an service.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public interface Authenticator {
|
||||||
|
/**
|
||||||
|
* Login into the {@link Service}. Once the authentication is done, it is
|
||||||
|
* persistent for the entire service's operation.<br>
|
||||||
|
* <b>Note</b>: If you want to logout the user, see
|
||||||
|
* {@link Authenticator#logout()}
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
* the listener do keep a track on the authentication progress
|
||||||
|
*/
|
||||||
|
void login(AuthenticatorListener listener) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout into the {@link Service}. The session is restored to an not
|
||||||
|
* logged-in state.
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
* the listener do keep a track on the logout progress
|
||||||
|
*/
|
||||||
|
void logout(AuthenticatorListener listener) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capability an certain {@link Authenticator} can have.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public enum AuthenticatorCapability {
|
||||||
|
/**
|
||||||
|
* Mark an {@link Authenticator} capable of fetching account information.
|
||||||
|
*/
|
||||||
|
FETCH_ACCOUNT_INFORMATION;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener keeps an track on the Authentication process.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public interface AuthenticatorListener {
|
||||||
|
/**
|
||||||
|
* The username and password informed was not valid.
|
||||||
|
*
|
||||||
|
* @param credential
|
||||||
|
* the authenticating credential
|
||||||
|
*/
|
||||||
|
void invalidCredentials(Credential credential);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The username and password informed was valid. User is authenticated.
|
||||||
|
*
|
||||||
|
* @param credential
|
||||||
|
* the authenticating credential
|
||||||
|
*/
|
||||||
|
void loginSuccessful(Credential credential);
|
||||||
|
|
||||||
|
void logout(Credential credential);
|
||||||
|
|
||||||
|
void exception(IOException e) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an utility class to help manage Capabilities of all the services.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @param <T>
|
||||||
|
* the capability enumeration
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class CapabilityMatrix<T> {
|
||||||
|
/**
|
||||||
|
* The list of all supported capabilities
|
||||||
|
*/
|
||||||
|
private final T[] matrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new matrix of capabilities
|
||||||
|
*
|
||||||
|
* @param matrix
|
||||||
|
* all the capabilities this service support
|
||||||
|
*/
|
||||||
|
public CapabilityMatrix(T... matrix) {
|
||||||
|
this.matrix = matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether an certatin capability is in the matrix or not.
|
||||||
|
*
|
||||||
|
* @param capability
|
||||||
|
* the capability being searched in the matrix
|
||||||
|
* @return true if existent, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean has(T capability) {
|
||||||
|
for (final T capScan : matrix) {
|
||||||
|
if (capScan == capability) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/main/java/com/rogiel/httpchannel/service/Credential.java
Normal file
59
src/main/java/com/rogiel/httpchannel/service/Credential.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pair of username-password used for authenticating into services.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class Credential {
|
||||||
|
private final String username;
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new pair of username-password credential
|
||||||
|
*
|
||||||
|
* @param username
|
||||||
|
* the username
|
||||||
|
* @param password
|
||||||
|
* the password
|
||||||
|
*/
|
||||||
|
public Credential(String username, String password) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the username
|
||||||
|
*
|
||||||
|
* @return the username
|
||||||
|
*/
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the password
|
||||||
|
*
|
||||||
|
* @return the password
|
||||||
|
*/
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rogiel.httpchannel;
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
|
||||||
@@ -22,5 +22,7 @@ import java.nio.channels.ReadableByteChannel;
|
|||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
*/
|
*/
|
||||||
public interface DownloadChannel extends ReadableByteChannel {
|
public interface DownloadChannel extends ReadableByteChannel {
|
||||||
|
long getLength();
|
||||||
|
|
||||||
|
String getFilename();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.captcha.Captcha;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener keeps an track on the progress on an {@link Downloader}
|
||||||
|
* service.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public interface DownloadListener {
|
||||||
|
/**
|
||||||
|
* Inform that the downloader will be waiting for an certain amount of time
|
||||||
|
* due to an timer in the download site.
|
||||||
|
*
|
||||||
|
* @param time
|
||||||
|
* the time in ms in which the service will be be waiting.
|
||||||
|
* @param reason
|
||||||
|
* the reason why this timer is running
|
||||||
|
* @return true if desires to wait, false otherwise
|
||||||
|
*/
|
||||||
|
boolean timer(long time, TimerWaitReason reason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The reason why an certain timer is being ran.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
*/
|
||||||
|
public enum TimerWaitReason {
|
||||||
|
/**
|
||||||
|
* Normal download timer. An annoyance.
|
||||||
|
*/
|
||||||
|
DOWNLOAD_TIMER,
|
||||||
|
/**
|
||||||
|
* This IP has already download up to the limit, waiting for releasing
|
||||||
|
* of the block.
|
||||||
|
*/
|
||||||
|
COOLDOWN,
|
||||||
|
/**
|
||||||
|
* This is an unknown wait time.
|
||||||
|
*/
|
||||||
|
UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passes an captcha by parameter and waits for the response of the
|
||||||
|
* challenge.
|
||||||
|
*
|
||||||
|
* @param captcha
|
||||||
|
* the captcha challenge
|
||||||
|
*/
|
||||||
|
String captcha(Captcha captcha);
|
||||||
|
}
|
||||||
@@ -16,42 +16,46 @@
|
|||||||
*/
|
*/
|
||||||
package com.rogiel.httpchannel.service;
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.net.URL;
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.ReadableByteChannel;
|
|
||||||
|
|
||||||
import com.rogiel.httpchannel.DownloadChannel;
|
import javax.tools.FileObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* Implements an service capable of downloading a file.
|
||||||
*
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public interface DownloadService {
|
public interface DownloadService extends Service {
|
||||||
DownloadChannel download(URI uri, CaptchaResolver captchaResolver);
|
/**
|
||||||
|
* Creates a new instance of the {@link Downloader}. This instance will be
|
||||||
|
* attached to the {@link URL}, {@link FileObject} provided through the the
|
||||||
|
* arguments and the parent {@link Service} instance.
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* the url to be downloaded
|
||||||
|
* @param file
|
||||||
|
* the destination file
|
||||||
|
* @return an new instance of {@link Downloader}
|
||||||
|
*/
|
||||||
|
Downloader getDownloader(URL url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple delegating implementation for {@link DownloadChannel}.
|
* Check if this {@link Service} can download from this URL. Implemtations
|
||||||
|
* might or might not perform network activity.
|
||||||
*
|
*
|
||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* @param url
|
||||||
|
* the url to be tested.
|
||||||
|
* @return true if supported, false otherwise.
|
||||||
*/
|
*/
|
||||||
public abstract class SimpleDownloadChannel implements DownloadChannel {
|
boolean matchURL(URL url);
|
||||||
protected final ReadableByteChannel channel;
|
|
||||||
|
|
||||||
public SimpleDownloadChannel(ReadableByteChannel channel) {
|
/**
|
||||||
this.channel = channel;
|
* Return the matrix of capabilities for this {@link Downloader}.
|
||||||
}
|
*
|
||||||
|
* @return {@link CapabilityMatrix} with all capabilities of this
|
||||||
public int read(ByteBuffer dst) throws IOException {
|
* {@link Downloader}.
|
||||||
return channel.read(dst);
|
* @see DownloaderCapability
|
||||||
}
|
*/
|
||||||
|
CapabilityMatrix<DownloaderCapability> getDownloadCapabilities();
|
||||||
public boolean isOpen() {
|
|
||||||
return channel.isOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws IOException {
|
|
||||||
channel.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
35
src/main/java/com/rogiel/httpchannel/service/Downloader.java
Normal file
35
src/main/java/com/rogiel/httpchannel/service/Downloader.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interfaces provides downloading for an service.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public interface Downloader {
|
||||||
|
/**
|
||||||
|
* Starts the download process.
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
* the listener to keep a track on the download progress
|
||||||
|
*/
|
||||||
|
DownloadChannel download(DownloadListener listener) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capability an certain {@link Downloader} can have.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public enum DownloaderCapability {
|
||||||
|
/**
|
||||||
|
* Can download files while not authenticated
|
||||||
|
*/
|
||||||
|
UNAUTHENTICATED_DOWNLOAD,
|
||||||
|
/**
|
||||||
|
* Can download files while authenticated with non-premium account
|
||||||
|
*/
|
||||||
|
NON_PREMIUM_ACCOUNT_DOWNLOAD,
|
||||||
|
/**
|
||||||
|
* Can download files while authenticated with premium account
|
||||||
|
*/
|
||||||
|
PREMIUM_ACCOUNT_DOWNLOAD,
|
||||||
|
/**
|
||||||
|
* Resume interrupted downloads are possible and supported.
|
||||||
|
*/
|
||||||
|
RESUME,
|
||||||
|
/**
|
||||||
|
* Can check the status of the given link before starting download.
|
||||||
|
*/
|
||||||
|
STATUS_CHECK;
|
||||||
|
}
|
||||||
@@ -16,10 +16,46 @@
|
|||||||
*/
|
*/
|
||||||
package com.rogiel.httpchannel.service;
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.config.ServiceConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* Base interface for all the services. Whenever the operation suported by the
|
||||||
|
* {@link Service} it must implement this interface. Most of implementations
|
||||||
|
* will benefit from abstract instances of this interface:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link AbstractHttpService}: provides an basic support for HTTP services.
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public interface Service {
|
public interface Service {
|
||||||
|
/**
|
||||||
|
* Get the ServiceID.
|
||||||
|
*
|
||||||
|
* @return the id of the service
|
||||||
|
*/
|
||||||
|
String getId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Major version of this service
|
||||||
|
*
|
||||||
|
* @return the major version
|
||||||
|
*/
|
||||||
|
int getMajorVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the minor version of this service
|
||||||
|
*
|
||||||
|
* @return the minor version
|
||||||
|
*/
|
||||||
|
int getMinorVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this {@link ServiceConfiguration} instance
|
||||||
|
*
|
||||||
|
* @return the {@link ServiceConfiguration} instance
|
||||||
|
*/
|
||||||
|
ServiceConfiguration getServiceConfiguration();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base exception for any {@link Service}.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class ServiceException extends IOException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public ServiceException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException(Throwable t) {
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException(String message, Throwable t) {
|
||||||
|
super(message, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,14 +14,17 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rogiel.httpchannel;
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
*/
|
*/
|
||||||
public interface UploadChannel extends WritableByteChannel {
|
public interface UploadChannel extends WritableByteChannel {
|
||||||
URI getLink();
|
long getLength();
|
||||||
|
|
||||||
|
String getFilename();
|
||||||
|
|
||||||
|
String getDownloadLink();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener keeps an track on the progress on an {@link Uploader} service.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public interface UploadListener {
|
||||||
|
long getFilesize();
|
||||||
|
|
||||||
|
String getFilename();
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.ThreadUtils;
|
||||||
|
|
||||||
|
import org.apache.http.entity.mime.content.AbstractContentBody;
|
||||||
|
import org.apache.http.entity.mime.content.ContentBody;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ContentBody} used to upload files in {@link Uploader} implementations.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class UploadListenerContentBody extends AbstractContentBody {
|
||||||
|
private final LinkedUploadChannel channel;
|
||||||
|
|
||||||
|
public UploadListenerContentBody(LinkedUploadChannel channel) {
|
||||||
|
super("application/octet-stream");
|
||||||
|
this.channel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return channel.getFilename();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream out) throws IOException {
|
||||||
|
final WritableByteChannel outputChannel = Channels.newChannel(out);
|
||||||
|
channel.linkChannel(outputChannel);
|
||||||
|
|
||||||
|
while (channel.isOpen() && outputChannel.isOpen()) {
|
||||||
|
ThreadUtils.sleep(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCharset() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getContentLength() {
|
||||||
|
return channel.getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTransferEncoding() {
|
||||||
|
return "binary";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,38 +16,39 @@
|
|||||||
*/
|
*/
|
||||||
package com.rogiel.httpchannel.service;
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
|
||||||
|
|
||||||
import com.rogiel.httpchannel.UploadChannel;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* Implements an service capable of uploading a file.
|
||||||
*/
|
|
||||||
public interface UploadService {
|
|
||||||
UploadChannel upload(String filename, long filesize);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple delegating implementation for {@link UploadChannel}.
|
|
||||||
*
|
*
|
||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public abstract class SimpleUploadChannel implements UploadChannel {
|
public interface UploadService extends Service {
|
||||||
protected final WritableByteChannel channel;
|
/**
|
||||||
|
* Creates a new instance of {@link Uploader}. This instance is attached
|
||||||
|
* with the parent {@link Service} instance.<br>
|
||||||
|
* <b>Note</b>: not all services might support <tt>description</tt>
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* the file to be uploaded
|
||||||
|
* @param description
|
||||||
|
* the description of the upload. If supported.
|
||||||
|
* @return the new {@link Uploader} instance
|
||||||
|
*/
|
||||||
|
Uploader getUploader(String description);
|
||||||
|
|
||||||
public SimpleUploadChannel(WritableByteChannel channel) {
|
/**
|
||||||
this.channel = channel;
|
* Get the maximum upload file size supported by this service.
|
||||||
}
|
*
|
||||||
|
* @return the maximum filesize supported
|
||||||
|
*/
|
||||||
|
long getMaximumFilesize();
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public int write(ByteBuffer src) throws IOException {
|
* Return the matrix of capabilities for this {@link Uploader}.
|
||||||
return channel.write(src);
|
*
|
||||||
}
|
* @return {@link CapabilityMatrix} with all capabilities of this
|
||||||
|
* {@link Uploader}.
|
||||||
@Override
|
* @see UploaderCapability
|
||||||
public boolean isOpen() {
|
*/
|
||||||
return channel.isOpen();
|
CapabilityMatrix<UploaderCapability> getUploadCapabilities();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/main/java/com/rogiel/httpchannel/service/Uploader.java
Normal file
38
src/main/java/com/rogiel/httpchannel/service/Uploader.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interfaces provides uploading for an service.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public interface Uploader {
|
||||||
|
/**
|
||||||
|
* Starts the upload process on this service.
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
* the listener do keep an track on the upload process
|
||||||
|
* @throws UploadServiceException
|
||||||
|
* thrown if something went wrong
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
UploadChannel upload(UploadListener listener) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capability an certain {@link Uploader} can have.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public enum UploaderCapability {
|
||||||
|
/**
|
||||||
|
* Can upload while not authenticated into any account
|
||||||
|
*/
|
||||||
|
UNAUTHENTICATED_UPLOAD,
|
||||||
|
/**
|
||||||
|
* Can upload while authenticated with a non-premium account
|
||||||
|
*/
|
||||||
|
NON_PREMIUM_ACCOUNT_UPLOAD,
|
||||||
|
/**
|
||||||
|
* Can upload while authenticated with a premium account
|
||||||
|
*/
|
||||||
|
PREMIUM_ACCOUNT_UPLOAD;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service.captcha;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public interface Captcha {
|
||||||
|
String getAnswer();
|
||||||
|
void setAnswer(String answer);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service.captcha;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class ImageCaptcha implements Captcha {
|
||||||
|
private URL url;
|
||||||
|
|
||||||
|
private String answer;
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(URL url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAnswer() {
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAnswer(String answer) {
|
||||||
|
this.answer = answer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service.channel;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.DownloadChannel;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class InputStreamDownloadChannel implements DownloadChannel {
|
||||||
|
private final ReadableByteChannel channel;
|
||||||
|
|
||||||
|
private final long length;
|
||||||
|
private final String filename;
|
||||||
|
|
||||||
|
public InputStreamDownloadChannel(InputStream in, final long length,
|
||||||
|
final String filename) {
|
||||||
|
this.channel = Channels.newChannel(in);
|
||||||
|
this.length = length;
|
||||||
|
this.filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(ByteBuffer dst) throws IOException {
|
||||||
|
return channel.read(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpen() {
|
||||||
|
return channel.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
channel.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service.channel;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.UploadChannel;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LinkedUploadChannel implements UploadChannel {
|
||||||
|
private WritableByteChannel channel;
|
||||||
|
private final LinkedUploadChannelCloseCallback closeCallback;
|
||||||
|
|
||||||
|
private final long length;
|
||||||
|
private final String filename;
|
||||||
|
private String downloadLink;
|
||||||
|
|
||||||
|
private boolean open = true;
|
||||||
|
|
||||||
|
public LinkedUploadChannel(LinkedUploadChannelCloseCallback closeCallback,
|
||||||
|
long filesize, String filename) {
|
||||||
|
this.closeCallback = closeCallback;
|
||||||
|
this.filename = filename;
|
||||||
|
this.length = filesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int write(ByteBuffer src) throws IOException {
|
||||||
|
if (channel == null)
|
||||||
|
throw new IOException("Channel is not linked yet");
|
||||||
|
return channel.write(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpen() {
|
||||||
|
return (channel != null ? channel.isOpen() : true) && open;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
open = false;
|
||||||
|
downloadLink = closeCallback.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface LinkedUploadChannelCloseCallback {
|
||||||
|
String finish() throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDownloadLink() {
|
||||||
|
return downloadLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void linkChannel(WritableByteChannel channel) throws IOException {
|
||||||
|
if(this.channel != null)
|
||||||
|
throw new IOException("This channel is already linked.");
|
||||||
|
this.channel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLinked() {
|
||||||
|
return channel != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.transformer.Transformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an flag interface to indicate that an certain Interface is the
|
||||||
|
* configuration for the service.<br>
|
||||||
|
* <br>
|
||||||
|
* Every service must create an <tt>interface</tt> with the configuration
|
||||||
|
* methods, additionally an Annotation informing the default value.
|
||||||
|
* ServiceConfiguration implementations might use reflection ({@link Proxy}),
|
||||||
|
* hard-coding or any other way for fetching the data.<br>
|
||||||
|
* <br>
|
||||||
|
* String data stored in the annotation is converted to Java Types using the
|
||||||
|
* {@link Transformer} class.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @version 1.0
|
||||||
|
* @see Transformer
|
||||||
|
*/
|
||||||
|
public interface ServiceConfiguration {
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service.config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.transformer.TransformerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for {@link ServiceConfiguration} system.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class ServiceConfigurationHelper {
|
||||||
|
/**
|
||||||
|
* Creates a Proxy Class that returns all the default values of
|
||||||
|
* configuration interfaces. The values are mapped by
|
||||||
|
* {@link ServiceConfigurationProperty} annotation.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the interface extending {@link ServiceConfiguration}. Service
|
||||||
|
* specific.
|
||||||
|
* @param type
|
||||||
|
* the type Class representing T.
|
||||||
|
* @return the proxied {@link ServiceConfiguration} instance.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends ServiceConfiguration> T defaultConfiguration(
|
||||||
|
Class<T> type) {
|
||||||
|
return (T) Proxy.newProxyInstance(
|
||||||
|
ServiceConfiguration.class.getClassLoader(),
|
||||||
|
new Class<?>[] { type }, new InvocationHandler() {
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object object, Method method,
|
||||||
|
Object[] arguments) throws Throwable {
|
||||||
|
final ServiceConfigurationProperty property = method
|
||||||
|
.getAnnotation(ServiceConfigurationProperty.class);
|
||||||
|
if (property != null)
|
||||||
|
return TransformerFactory.getTransformer(
|
||||||
|
method.getReturnType()).transform(
|
||||||
|
property.defaultValue());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Proxy Class that returns all the default values of
|
||||||
|
* configuration interfaces. The values are mapped by
|
||||||
|
* {@link ServiceConfigurationProperty} annotation.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the interface extending {@link ServiceConfiguration}. Service
|
||||||
|
* specific.
|
||||||
|
* @param type
|
||||||
|
* the type Class representing T.
|
||||||
|
* @return the proxied {@link ServiceConfiguration} instance.
|
||||||
|
* @throws IOException
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends ServiceConfiguration> T file(Class<T> type,
|
||||||
|
File file) throws FileNotFoundException, IOException {
|
||||||
|
final Properties properties = new Properties();
|
||||||
|
properties.load(new FileInputStream(file));
|
||||||
|
|
||||||
|
return (T) Proxy.newProxyInstance(
|
||||||
|
ServiceConfiguration.class.getClassLoader(),
|
||||||
|
new Class<?>[] { type }, new InvocationHandler() {
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object object, Method method,
|
||||||
|
Object[] arguments) throws Throwable {
|
||||||
|
final ServiceConfigurationProperty property = method
|
||||||
|
.getAnnotation(ServiceConfigurationProperty.class);
|
||||||
|
if (property != null)
|
||||||
|
return TransformerFactory.getTransformer(
|
||||||
|
method.getReturnType()).transform(
|
||||||
|
get(property));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String get(ServiceConfigurationProperty property) {
|
||||||
|
String value = properties.getProperty(property.key());
|
||||||
|
if (value == null)
|
||||||
|
value = property.defaultValue();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service.config;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation that defines the default value for an {@link ServiceConfiguration}
|
||||||
|
* method.<br>
|
||||||
|
* <br>
|
||||||
|
* <h1>Usage example</h1>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public interface DummyServiceConfiguration extends ServiceConfiguration {
|
||||||
|
* @ServiceConfigurationProperty(defaultValue = "true")
|
||||||
|
* boolean retryAllowed();
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The default implementation created by
|
||||||
|
* {@link ServiceConfigurationHelper#defaultConfiguration()} will always return
|
||||||
|
* the <tt>defaultValue</tt>.
|
||||||
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface ServiceConfigurationProperty {
|
||||||
|
String key();
|
||||||
|
String defaultValue();
|
||||||
|
}
|
||||||
@@ -17,35 +17,318 @@
|
|||||||
package com.rogiel.httpchannel.service.impl;
|
package com.rogiel.httpchannel.service.impl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URL;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import com.rogiel.httpchannel.DownloadChannel;
|
import net.sf.f2s.util.HttpClientUtils;
|
||||||
import com.rogiel.httpchannel.UploadChannel;
|
import net.sf.f2s.util.PatternUtils;
|
||||||
import com.rogiel.httpchannel.service.CaptchaResolver;
|
import net.sf.f2s.util.ThreadUtils;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.mime.MultipartEntity;
|
||||||
|
import org.apache.http.entity.mime.content.StringBody;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.AbstractDownloader;
|
||||||
|
import com.rogiel.httpchannel.service.AbstractHttpService;
|
||||||
|
import com.rogiel.httpchannel.service.AuthenticationService;
|
||||||
|
import com.rogiel.httpchannel.service.Authenticator;
|
||||||
|
import com.rogiel.httpchannel.service.AuthenticatorCapability;
|
||||||
|
import com.rogiel.httpchannel.service.AuthenticatorListener;
|
||||||
|
import com.rogiel.httpchannel.service.CapabilityMatrix;
|
||||||
|
import com.rogiel.httpchannel.service.Credential;
|
||||||
|
import com.rogiel.httpchannel.service.DownloadChannel;
|
||||||
|
import com.rogiel.httpchannel.service.DownloadListener;
|
||||||
import com.rogiel.httpchannel.service.DownloadService;
|
import com.rogiel.httpchannel.service.DownloadService;
|
||||||
|
import com.rogiel.httpchannel.service.Downloader;
|
||||||
|
import com.rogiel.httpchannel.service.DownloaderCapability;
|
||||||
|
import com.rogiel.httpchannel.service.Service;
|
||||||
|
import com.rogiel.httpchannel.service.UploadChannel;
|
||||||
|
import com.rogiel.httpchannel.service.UploadListener;
|
||||||
|
import com.rogiel.httpchannel.service.UploadListenerContentBody;
|
||||||
import com.rogiel.httpchannel.service.UploadService;
|
import com.rogiel.httpchannel.service.UploadService;
|
||||||
|
import com.rogiel.httpchannel.service.Uploader;
|
||||||
|
import com.rogiel.httpchannel.service.UploaderCapability;
|
||||||
|
import com.rogiel.httpchannel.service.channel.InputStreamDownloadChannel;
|
||||||
|
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
|
||||||
|
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback;
|
||||||
|
import com.rogiel.httpchannel.service.config.ServiceConfiguration;
|
||||||
|
import com.rogiel.httpchannel.service.config.ServiceConfigurationProperty;
|
||||||
|
import com.rogiel.httpchannel.service.impl.MegaUploadService.MegaUploadServiceConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* This service handles login, upload and download to MegaUpload.com.
|
||||||
*
|
*
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public class MegaUploadService implements DownloadService, UploadService {
|
public class MegaUploadService extends
|
||||||
public DownloadChannel download(URI uri, CaptchaResolver captchaResolver) {
|
AbstractHttpService<MegaUploadServiceConfiguration> implements Service,
|
||||||
return new SimpleDownloadChannel(null) {
|
UploadService, DownloadService, AuthenticationService {
|
||||||
};
|
private static final Pattern UPLOAD_URL_PATTERN = Pattern
|
||||||
}
|
.compile("http://www([0-9]*)\\.megaupload\\.com/upload_done\\.php\\?UPLOAD_IDENTIFIER=[0-9]*");
|
||||||
|
|
||||||
public UploadChannel upload(String filename, long filesize) {
|
private static final Pattern DOWNLOAD_DIRECT_LINK_PATTERN = Pattern
|
||||||
return new SimpleUploadChannel(null) {
|
.compile("http://www([0-9]*)\\.megaupload\\.com/files/([A-Za-z0-9]*)/([^\"]*)");
|
||||||
@Override
|
private static final Pattern DOWNLOAD_TIMER = Pattern
|
||||||
public void close() throws IOException {
|
.compile("count=([0-9]*);");
|
||||||
|
// private static final Pattern DOWNLOAD_FILESIZE = Pattern
|
||||||
|
// .compile("[0-9]*(\\.[0-9]*)? (K|M|G)B");
|
||||||
|
|
||||||
|
private static final Pattern DOWNLOAD_URL_PATTERN = Pattern
|
||||||
|
.compile("http://www\\.megaupload\\.com/\\?d=([A-Za-z0-9]*)");
|
||||||
|
|
||||||
|
public MegaUploadService(final MegaUploadServiceConfiguration configuration) {
|
||||||
|
super(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getLink() {
|
public String getId() {
|
||||||
|
return "megaupload";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMajorVersion() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinorVersion() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uploader getUploader(String description) {
|
||||||
|
return new MegaUploadUploader(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMaximumFilesize() {
|
||||||
|
return 1 * 1024 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CapabilityMatrix<UploaderCapability> getUploadCapabilities() {
|
||||||
|
return new CapabilityMatrix<UploaderCapability>(
|
||||||
|
UploaderCapability.UNAUTHENTICATED_UPLOAD,
|
||||||
|
UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD,
|
||||||
|
UploaderCapability.PREMIUM_ACCOUNT_UPLOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Downloader getDownloader(URL url) {
|
||||||
|
return new MegaUploadDownloader(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matchURL(URL url) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CapabilityMatrix<DownloaderCapability> getDownloadCapabilities() {
|
||||||
|
return new CapabilityMatrix<DownloaderCapability>(
|
||||||
|
DownloaderCapability.UNAUTHENTICATED_DOWNLOAD,
|
||||||
|
DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD,
|
||||||
|
DownloaderCapability.PREMIUM_ACCOUNT_DOWNLOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authenticator getAuthenticator(Credential credential) {
|
||||||
|
return new MegaUploadAuthenticator(credential);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CapabilityMatrix<AuthenticatorCapability> getAuthenticationCapability() {
|
||||||
|
return new CapabilityMatrix<AuthenticatorCapability>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class MegaUploadUploader implements Uploader,
|
||||||
|
LinkedUploadChannelCloseCallback {
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private Future<String> uploadFuture;
|
||||||
|
|
||||||
|
public MegaUploadUploader(String description) {
|
||||||
|
this.description = (description != null ? description
|
||||||
|
: configuration.getDefaultUploadDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadChannel upload(UploadListener listener) throws IOException {
|
||||||
|
final String body = HttpClientUtils.get(client,
|
||||||
|
"http://www.megaupload.com/multiupload/");
|
||||||
|
final String url = PatternUtils.find(UPLOAD_URL_PATTERN, body);
|
||||||
|
|
||||||
|
final HttpPost upload = new HttpPost(url);
|
||||||
|
final MultipartEntity entity = new MultipartEntity();
|
||||||
|
upload.setEntity(entity);
|
||||||
|
|
||||||
|
final LinkedUploadChannel channel = new LinkedUploadChannel(this,
|
||||||
|
listener.getFilesize(), listener.getFilename());
|
||||||
|
|
||||||
|
entity.addPart("multifile_0",
|
||||||
|
new UploadListenerContentBody(channel));
|
||||||
|
entity.addPart("multimessage_0", new StringBody(description));
|
||||||
|
|
||||||
|
uploadFuture = HttpClientUtils.executeAsync(client, upload);
|
||||||
|
while (!channel.isLinked() && !uploadFuture.isDone()) {
|
||||||
|
ThreadUtils.sleep(100);
|
||||||
|
}
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String finish() throws IOException {
|
||||||
|
try {
|
||||||
|
return PatternUtils.find(DOWNLOAD_URL_PATTERN,
|
||||||
|
uploadFuture.get());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
} catch (ExecutionException e) {
|
||||||
};
|
throw (IOException) e.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class MegaUploadDownloader extends AbstractDownloader {
|
||||||
|
private final URL url;
|
||||||
|
|
||||||
|
public MegaUploadDownloader(URL url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DownloadChannel download(DownloadListener listener)
|
||||||
|
throws IOException {
|
||||||
|
final HttpGet request = new HttpGet(url.toString());
|
||||||
|
final HttpResponse response = client.execute(request);
|
||||||
|
final String content = IOUtils.toString(response.getEntity()
|
||||||
|
.getContent());
|
||||||
|
|
||||||
|
// try to find timer
|
||||||
|
final String stringTimer = PatternUtils.find(DOWNLOAD_TIMER,
|
||||||
|
content, 1);
|
||||||
|
int timer = 0;
|
||||||
|
if (stringTimer != null && stringTimer.length() > 0) {
|
||||||
|
timer = Integer.parseInt(stringTimer);
|
||||||
|
}
|
||||||
|
if (timer > 0 && configuration.respectWaitTime()) {
|
||||||
|
timer(listener, timer * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String downloadUrl = PatternUtils.find(
|
||||||
|
DOWNLOAD_DIRECT_LINK_PATTERN, content, 0);
|
||||||
|
if (downloadUrl != null && downloadUrl.length() > 0) {
|
||||||
|
final HttpGet downloadRequest = new HttpGet(downloadUrl);
|
||||||
|
final HttpResponse downloadResponse = client
|
||||||
|
.execute(downloadRequest);
|
||||||
|
if (downloadResponse.getStatusLine().getStatusCode() == HttpStatus.SC_FORBIDDEN
|
||||||
|
|| downloadResponse.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) {
|
||||||
|
downloadResponse.getEntity().getContent().close();
|
||||||
|
if (cooldown(listener, 60 * 1000))
|
||||||
|
return download(listener); // retry download
|
||||||
|
} else {
|
||||||
|
final String filename = FilenameUtils.getName(downloadUrl);
|
||||||
|
// listener.fileName(filename);
|
||||||
|
|
||||||
|
final Header contentLengthHeader = downloadResponse
|
||||||
|
.getFirstHeader("Content-Length");
|
||||||
|
long contentLength = -1;
|
||||||
|
if (contentLengthHeader != null) {
|
||||||
|
contentLength = Long.valueOf(contentLengthHeader
|
||||||
|
.getValue());
|
||||||
|
// listener.fileSize(contentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InputStreamDownloadChannel(downloadResponse
|
||||||
|
.getEntity().getContent(), contentLength, filename);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IOException("Download link not found");
|
||||||
|
}
|
||||||
|
throw new IOException("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class MegaUploadAuthenticator implements Authenticator {
|
||||||
|
private final Credential credential;
|
||||||
|
|
||||||
|
public MegaUploadAuthenticator(Credential credential) {
|
||||||
|
this.credential = credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void login(AuthenticatorListener listener) {
|
||||||
|
try {
|
||||||
|
final HttpPost login = new HttpPost(
|
||||||
|
"http://www.megaupload.com/?c=login");
|
||||||
|
final MultipartEntity entity = new MultipartEntity();
|
||||||
|
login.setEntity(entity);
|
||||||
|
|
||||||
|
entity.addPart("login", new StringBody("1"));
|
||||||
|
entity.addPart("username",
|
||||||
|
new StringBody(credential.getUsername()));
|
||||||
|
entity.addPart("password",
|
||||||
|
new StringBody(credential.getPassword()));
|
||||||
|
|
||||||
|
final String response = HttpClientUtils.execute(client, login);
|
||||||
|
if (response.contains("Username and password do "
|
||||||
|
+ "not match. Please try again!")) {
|
||||||
|
listener.invalidCredentials(credential);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
listener.loginSuccessful(credential);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// throw new NestedLoginServiceException(e);
|
||||||
|
// TODO throw an exception here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logout(AuthenticatorListener listener) {
|
||||||
|
try {
|
||||||
|
final HttpPost logout = new HttpPost(
|
||||||
|
"http://www.megaupload.com/?c=account");
|
||||||
|
final MultipartEntity entity = new MultipartEntity();
|
||||||
|
logout.setEntity(entity);
|
||||||
|
|
||||||
|
entity.addPart("logout", new StringBody("1"));
|
||||||
|
HttpClientUtils.execute(client, logout);
|
||||||
|
|
||||||
|
// TODO check logout status
|
||||||
|
|
||||||
|
listener.logout(credential);
|
||||||
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
// throw new NestedLoginServiceException(e);
|
||||||
|
// TODO throw an exception here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface MegaUploadServiceConfiguration extends
|
||||||
|
ServiceConfiguration {
|
||||||
|
@ServiceConfigurationProperty(key = "megaupload.wait", defaultValue = "true")
|
||||||
|
boolean respectWaitTime();
|
||||||
|
|
||||||
|
@ServiceConfigurationProperty(key = "megaupload.port", defaultValue = "80")
|
||||||
|
int getPreferedDownloadPort();
|
||||||
|
|
||||||
|
@ServiceConfigurationProperty(key = "megaupload.description", defaultValue = "Uploaded by seedbox-httpchannel")
|
||||||
|
String getDefaultUploadDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.getClass().getSimpleName() + " " + getMajorVersion() + "."
|
||||||
|
+ getMinorVersion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
src/main/java/net/sf/f2s/util/HttpClientUtils.java
Normal file
63
src/main/java/net/sf/f2s/util/HttpClientUtils.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
|
||||||
|
public class HttpClientUtils {
|
||||||
|
private static final ExecutorService threadPool = Executors
|
||||||
|
.newCachedThreadPool();
|
||||||
|
|
||||||
|
public static String get(HttpClient client, String url) throws IOException {
|
||||||
|
return execute(client, new HttpGet(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String execute(HttpClient client, HttpUriRequest request)
|
||||||
|
throws IOException {
|
||||||
|
return toString(client.execute(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Future<String> executeAsync(final HttpClient client,
|
||||||
|
final HttpUriRequest request) throws IOException {
|
||||||
|
return threadPool.submit(new Callable<String>() {
|
||||||
|
@Override
|
||||||
|
public String call() throws Exception {
|
||||||
|
return HttpClientUtils.toString(client.execute(request));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(HttpResponse response) throws IOException {
|
||||||
|
final InputStream in = response.getEntity().getContent();
|
||||||
|
try {
|
||||||
|
return IOUtils.toString(in);
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/main/java/net/sf/f2s/util/PatternUtils.java
Normal file
46
src/main/java/net/sf/f2s/util/PatternUtils.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class PatternUtils {
|
||||||
|
public static String find(Pattern pattern, String text) {
|
||||||
|
return find(pattern, text, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String find(Pattern pattern, String text, int n) {
|
||||||
|
final Matcher matcher = pattern.matcher(text);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(n);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String match(Pattern pattern, String text) {
|
||||||
|
return match(pattern, text, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String match(Pattern pattern, String text, int n) {
|
||||||
|
final Matcher matcher = pattern.matcher(text);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
return matcher.group(n);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/main/java/net/sf/f2s/util/ThreadUtils.java
Normal file
30
src/main/java/net/sf/f2s/util/ThreadUtils.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class ThreadUtils {
|
||||||
|
public static void sleep(long time) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(time);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util.transformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class TransformationException extends Exception {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public TransformationException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param message
|
||||||
|
* the message
|
||||||
|
*/
|
||||||
|
public TransformationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cause
|
||||||
|
* the cause
|
||||||
|
*/
|
||||||
|
public TransformationException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param message
|
||||||
|
* the message
|
||||||
|
* @param cause
|
||||||
|
* the cause
|
||||||
|
*/
|
||||||
|
public TransformationException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,12 +14,12 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rogiel.httpchannel.service;
|
package net.sf.f2s.util.transformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* @author rogiel
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface CaptchaResolver {
|
public interface Transformer<O> {
|
||||||
|
O transform(String data) throws TransformationException;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util.transformer;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.transformer.impl.BooleanTransformer;
|
||||||
|
import net.sf.f2s.util.transformer.impl.IntegerTransformer;
|
||||||
|
import net.sf.f2s.util.transformer.impl.LongTransformer;
|
||||||
|
import net.sf.f2s.util.transformer.impl.StringTransformer;
|
||||||
|
import net.sf.f2s.util.transformer.impl.URLTransformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Rogiel
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class TransformerFactory {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> Transformer<T> getTransformer(Class<T> type) {
|
||||||
|
if (String.class.isAssignableFrom(type)) {
|
||||||
|
return (Transformer<T>) new StringTransformer();
|
||||||
|
} else if (Boolean.class.isAssignableFrom(type) || type == Boolean.TYPE) {
|
||||||
|
return (Transformer<T>) new BooleanTransformer();
|
||||||
|
} else if (Integer.class.isAssignableFrom(type) || type == Integer.TYPE) {
|
||||||
|
return (Transformer<T>) new IntegerTransformer();
|
||||||
|
} else if (Long.class.isAssignableFrom(type) || type == Long.TYPE) {
|
||||||
|
return (Transformer<T>) new LongTransformer();
|
||||||
|
} else if (URL.class.isAssignableFrom(type)) {
|
||||||
|
return (Transformer<T>) new URLTransformer();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util.transformer.impl;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.transformer.Transformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author rogiel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BooleanTransformer implements Transformer<Boolean> {
|
||||||
|
@Override
|
||||||
|
public Boolean transform(String data) {
|
||||||
|
return Boolean.parseBoolean(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util.transformer.impl;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.transformer.Transformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author rogiel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class IntegerTransformer implements Transformer<Integer> {
|
||||||
|
@Override
|
||||||
|
public Integer transform(String data) {
|
||||||
|
return Integer.parseInt(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util.transformer.impl;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.transformer.Transformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author rogiel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LongTransformer implements Transformer<Long> {
|
||||||
|
@Override
|
||||||
|
public Long transform(String data) {
|
||||||
|
return Long.parseLong(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util.transformer.impl;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.transformer.Transformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author rogiel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class StringTransformer implements Transformer<String> {
|
||||||
|
@Override
|
||||||
|
public String transform(String data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.sf.f2s.util.transformer.impl;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import net.sf.f2s.util.transformer.TransformationException;
|
||||||
|
import net.sf.f2s.util.transformer.Transformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author rogiel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class URLTransformer implements Transformer<URL> {
|
||||||
|
@Override
|
||||||
|
public URL transform(String data) throws TransformationException {
|
||||||
|
try {
|
||||||
|
return new URL(data);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new TransformationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of seedbox <github.com/seedbox>.
|
||||||
|
*
|
||||||
|
* seedbox is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* seedbox is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with seedbox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rogiel.httpchannel.service.impl;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.rogiel.httpchannel.service.AuthenticationService;
|
||||||
|
import com.rogiel.httpchannel.service.AuthenticatorListener;
|
||||||
|
import com.rogiel.httpchannel.service.Credential;
|
||||||
|
import com.rogiel.httpchannel.service.DownloadChannel;
|
||||||
|
import com.rogiel.httpchannel.service.DownloadListener;
|
||||||
|
import com.rogiel.httpchannel.service.DownloadService;
|
||||||
|
import com.rogiel.httpchannel.service.Service;
|
||||||
|
import com.rogiel.httpchannel.service.UploadChannel;
|
||||||
|
import com.rogiel.httpchannel.service.UploadListener;
|
||||||
|
import com.rogiel.httpchannel.service.UploadService;
|
||||||
|
import com.rogiel.httpchannel.service.UploaderCapability;
|
||||||
|
import com.rogiel.httpchannel.service.captcha.Captcha;
|
||||||
|
import com.rogiel.httpchannel.service.config.ServiceConfigurationHelper;
|
||||||
|
import com.rogiel.httpchannel.service.impl.MegaUploadService.MegaUploadServiceConfiguration;
|
||||||
|
|
||||||
|
public class MegaUploadServiceTest {
|
||||||
|
private Service service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See <b>src/test/resources/config/megaupload.properties</b>
|
||||||
|
* <p>
|
||||||
|
* Key: username
|
||||||
|
*/
|
||||||
|
private String VALID_USERNAME;
|
||||||
|
/**
|
||||||
|
* See <b>src/test/resources/config/megaupload.properties</b>
|
||||||
|
* <p>
|
||||||
|
* Key: password
|
||||||
|
*/
|
||||||
|
private String VALID_PASSWORD;
|
||||||
|
|
||||||
|
private static final String INVALID_USERNAME = "invalid";
|
||||||
|
private static final String INVALID_PASSWORD = "abc";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
// MegaUploadServiceConfiguration.class;
|
||||||
|
service = new MegaUploadService(
|
||||||
|
ServiceConfigurationHelper
|
||||||
|
.defaultConfiguration(MegaUploadServiceConfiguration.class));
|
||||||
|
|
||||||
|
final Properties properties = new Properties();
|
||||||
|
properties.load(new FileInputStream(
|
||||||
|
"src/test/resources/config/megaupload.properties"));
|
||||||
|
VALID_USERNAME = properties.getProperty("username");
|
||||||
|
VALID_PASSWORD = properties.getProperty("password");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testServiceId() {
|
||||||
|
System.out.println("Service: "+service.toString());
|
||||||
|
assertEquals("megaupload", service.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidAuthenticator() throws IOException {
|
||||||
|
((AuthenticationService) service).getAuthenticator(
|
||||||
|
new Credential(VALID_USERNAME, VALID_PASSWORD)).login(
|
||||||
|
new AuthenticatorListener() {
|
||||||
|
@Override
|
||||||
|
public void invalidCredentials(Credential credential) {
|
||||||
|
fail("This login attemp should've been successful.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loginSuccessful(Credential credential) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logout(Credential credential) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exception(IOException e) throws IOException {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidAuthenticator() throws IOException {
|
||||||
|
((AuthenticationService) service).getAuthenticator(
|
||||||
|
new Credential(INVALID_USERNAME, INVALID_PASSWORD)).login(
|
||||||
|
new AuthenticatorListener() {
|
||||||
|
@Override
|
||||||
|
public void invalidCredentials(Credential credential) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loginSuccessful(Credential credential) {
|
||||||
|
fail("This login attemp should't have been successful.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logout(Credential credential) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exception(IOException e) throws IOException {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNonLoguedInUploader() throws IOException {
|
||||||
|
assertTrue(
|
||||||
|
"This service does not have the capability UploadCapability.FREE_UPLOAD",
|
||||||
|
((UploadService) service).getUploadCapabilities().has(
|
||||||
|
UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD));
|
||||||
|
final UploadChannel channel = ((UploadService) service).getUploader(
|
||||||
|
"Upload by httpchannel").upload(new UploadListener() {
|
||||||
|
@Override
|
||||||
|
public long getFilesize() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return "test.bin";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
final ByteBuffer buffer = ByteBuffer.allocate(10);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
|
||||||
|
buffer.flip();
|
||||||
|
|
||||||
|
channel.write(buffer);
|
||||||
|
channel.close();
|
||||||
|
Assert.assertNotNull(channel.getDownloadLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoguedInUploader() throws IOException {
|
||||||
|
assertTrue(
|
||||||
|
"This service does not have the capability UploadCapability.PREMIUM_UPLOAD",
|
||||||
|
((UploadService) service).getUploadCapabilities().has(
|
||||||
|
UploaderCapability.PREMIUM_ACCOUNT_UPLOAD));
|
||||||
|
|
||||||
|
((AuthenticationService) service).getAuthenticator(
|
||||||
|
new Credential(VALID_USERNAME, VALID_PASSWORD)).login(
|
||||||
|
new AuthenticatorListener() {
|
||||||
|
@Override
|
||||||
|
public void invalidCredentials(Credential credential) {
|
||||||
|
fail("Invalid credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loginSuccessful(Credential credential) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logout(Credential credential) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exception(IOException e) throws IOException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final UploadChannel channel = ((UploadService) service).getUploader(
|
||||||
|
"Upload by httpchannel").upload(new UploadListener() {
|
||||||
|
@Override
|
||||||
|
public long getFilesize() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return "test.bin";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
final ByteBuffer buffer = ByteBuffer.allocate(10);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
buffer.put((byte) 0x00);
|
||||||
|
|
||||||
|
buffer.flip();
|
||||||
|
|
||||||
|
channel.write(buffer);
|
||||||
|
channel.close();
|
||||||
|
Assert.assertNotNull(channel.getDownloadLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDownloader() throws IOException, MalformedURLException {
|
||||||
|
final DownloadChannel channel = ((DownloadService) service)
|
||||||
|
.getDownloader(new URL("http://www.megaupload.com/?d=CVQKJ1KM"))
|
||||||
|
.download(new DownloadListener() {
|
||||||
|
@Override
|
||||||
|
public boolean timer(long time, TimerWaitReason reason) {
|
||||||
|
System.out.println("Waiting " + time + " in " + reason);
|
||||||
|
// if (reason == TimerWaitReason.DOWNLOAD_TIMER)
|
||||||
|
// return true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String captcha(Captcha captcha) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||||
|
IOUtils.copy(Channels.newInputStream(channel), bout);
|
||||||
|
System.out.println(bout.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoWaitDownloader() throws IOException,
|
||||||
|
MalformedURLException {
|
||||||
|
service = new MegaUploadService(ServiceConfigurationHelper.file(
|
||||||
|
MegaUploadServiceConfiguration.class, new File(
|
||||||
|
"src/test/resources/megaupload-nowait.properties")));
|
||||||
|
|
||||||
|
final DownloadChannel channel = ((DownloadService) service)
|
||||||
|
.getDownloader(new URL("http://www.megaupload.com/?d=CVQKJ1KM"))
|
||||||
|
.download(new DownloadListener() {
|
||||||
|
@Override
|
||||||
|
public boolean timer(long time, TimerWaitReason reason) {
|
||||||
|
System.out.println("Waiting " + time + " in " + reason);
|
||||||
|
if (reason == TimerWaitReason.DOWNLOAD_TIMER)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String captcha(Captcha captcha) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user