1
0
mirror of https://github.com/Rogiel/httpchannel synced 2025-12-05 23:22:51 +00:00

Implements several services and improves API

This commit is contained in:
2012-01-15 18:31:50 -02:00
parent f210afd16a
commit 23f80b50e6
117 changed files with 3741 additions and 1335 deletions

View File

@@ -1,10 +1,17 @@
<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"
<modelVersion>4.0.0</modelVersion> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <modelVersion>4.0.0</modelVersion>
<artifactId>httpchannel</artifactId> <parent>
<groupId>com.rogiel.httpchannel</groupId> <artifactId>httpchannel</artifactId>
<version>1.0.0</version> <groupId>com.rogiel.httpchannel</groupId>
<relativePath>..</relativePath> <version>1.0.0</version>
</parent> <relativePath>..</relativePath>
<artifactId>httpchannel-api</artifactId> </parent>
<artifactId>httpchannel-api</artifactId>
<name>HttpChannel/API</name>
<description>Module that defines the HttpChannel API. HttpChannels abstract complex download and upload steps into a simple and easy to use NIO Channel. NIO Channels can be wrapped into an InputStream or OutputStream and used in any way you may find possible to. Aside from that, Channels can be used natively in most next-gen libraries, meaning that you don't even need to wrap anything, just start writing or reading data to or from the channel wth a ByteBuffer.
Anyone using the library should try to rely on code from this module only and, only if necessary, on configuration classes that are implementation specific. Relying on any other resource or class is considered an error and should NOT be done.

View File

@@ -0,0 +1,33 @@
/**
*
*/
package com.rogiel.httpchannel.captcha;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public interface Captcha {
/**
* Sets the captcha answer
*
* @param answer
* the captcha answer
*/
void setAnswer(String answer);
/**
* Returns the captcha answer. <code>null</code> if the service was not able
* to resolve it automatically. In such case, {@link #setAnswer(String)}
* must be used to set the correct answer.
*
* @return the captcha answer
*/
String getAnswer();
/**
* @return <code>true</code> if, and only if, the service was able to
* automatically resolve the captcha result
*/
boolean wasAutomaticallyResolved();
}

View File

@@ -0,0 +1,19 @@
/**
*
*/
package com.rogiel.httpchannel.captcha;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface CaptchaResolver {
/**
* Tries to resolve the captcha
*
* @param captcha
* the captcha
* @return <code>true</code> if the captcha was resolve, <code>false</code>
* to abort
*/
boolean resolve(Captcha captcha);
}

View File

@@ -0,0 +1,11 @@
/**
*
*/
package com.rogiel.httpchannel.captcha;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public interface CaptchaService<C extends Captcha> {
}

View File

@@ -0,0 +1,22 @@
/**
*
*/
package com.rogiel.httpchannel.captcha;
import java.net.URL;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public interface ImageCaptcha extends Captcha {
/**
* @return the captcha identifier
*/
String getID();
/**
* @return the captcha image {@link URL}
*/
URL getImageURL();
}

View File

@@ -0,0 +1,31 @@
/**
*
*/
package com.rogiel.httpchannel.captcha;
import java.net.URL;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public interface ImageCaptchaService<C extends ImageCaptcha> extends
CaptchaService<C> {
/**
* Creates a new captcha from the given HTML content
*
* @param image
* the image {@link URL}
* @return a new captcha
*/
C create(String html);
/**
* Tries to automatically resolve the captcha
*
* @param captcha
* the captcha to be resolved
* @return <code>true</code> if the captcha was successfully resolved
*/
boolean resolve(C captcha);
}

View File

@@ -16,37 +16,18 @@
*/ */
package com.rogiel.httpchannel.service; package com.rogiel.httpchannel.service;
import com.rogiel.httpchannel.service.config.ServiceConfiguration; import com.rogiel.httpchannel.captcha.Captcha;
import com.rogiel.httpchannel.captcha.CaptchaResolver;
import com.rogiel.httpchannel.service.exception.UnresolvedCaptchaException;
/** /**
* This is an abstract {@link Service} implementation. * This is an abstract {@link Service} implementation.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @version 1.0 * @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> public abstract class AbstractService implements Service {
implements Service { protected CaptchaResolver captchaResolver;
protected T configuration;
protected AbstractService(T configuration) {
this.configuration = configuration;
}
@Override
public T getServiceConfiguration() {
return configuration;
}
@Override
@SuppressWarnings("unchecked")
public void setServiceConfiguration(ServiceConfiguration configuration) {
this.configuration = (T) configuration;
}
@Override @Override
public Service clone() { public Service clone() {
@@ -56,4 +37,20 @@ public abstract class AbstractService<T extends ServiceConfiguration>
return null; return null;
} }
} }
@Override
public void setCaptchaResolver(CaptchaResolver captchaResolver) {
this.captchaResolver = captchaResolver;
}
protected boolean resolveCaptcha(Captcha captcha)
throws UnresolvedCaptchaException {
if (captchaResolver == null)
throw new UnresolvedCaptchaException();
if (!captchaResolver.resolve(captcha))
throw new UnresolvedCaptchaException();
if (captcha.getAnswer() == null)
throw new UnresolvedCaptchaException();
return true;
}
} }

View File

@@ -16,13 +16,29 @@
*/ */
package com.rogiel.httpchannel.service; package com.rogiel.httpchannel.service;
import com.rogiel.httpchannel.service.Authenticator.AuthenticatorConfiguration;
import com.rogiel.httpchannel.service.config.NullAuthenticatorConfiguration;
/** /**
* Implements an service capable of authenticating into an account. * Implements an service capable of authenticating into an account.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public interface AuthenticationService extends Service { public interface AuthenticationService<C extends AuthenticatorConfiguration>
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
* @param configuration
* the authenticator configuration
* @return an new {@link Authenticator} instance
*/
Authenticator<C> getAuthenticator(Credential credential, C configuration);
/** /**
* Creates {@link Authenticator} instance for this service. This instance is * Creates {@link Authenticator} instance for this service. This instance is
* attached to an {@link Credential} and to its parent {@link Service}. * attached to an {@link Credential} and to its parent {@link Service}.
@@ -31,7 +47,17 @@ public interface AuthenticationService extends Service {
* the credential * the credential
* @return an new {@link Authenticator} instance * @return an new {@link Authenticator} instance
*/ */
Authenticator getAuthenticator(Credential credential); Authenticator<C> getAuthenticator(Credential credential);
/**
* Creates a new configuration object. If a service does not support or
* require configuration, {@link NullAuthenticatorConfiguration} should be
* returned.
*
* @return a new configuration object or
* {@link NullAuthenticatorConfiguration}
*/
C newAuthenticatorConfiguration();
/** /**
* Return the matrix of capabilities for this {@link Authenticator}. * Return the matrix of capabilities for this {@link Authenticator}.

View File

@@ -18,15 +18,18 @@ package com.rogiel.httpchannel.service;
import java.io.IOException; import java.io.IOException;
import com.rogiel.httpchannel.captcha.CaptchaResolver;
import com.rogiel.httpchannel.service.Authenticator.AuthenticatorConfiguration;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException; import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.service.exception.UnresolvedCaptchaException;
/** /**
* This interfaces provides authentication for an service. * This interfaces provides authentication for an service.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public interface Authenticator { public interface Authenticator<C extends AuthenticatorConfiguration> {
/** /**
* Login into the {@link Service}. Once the authentication is done, it is * Login into the {@link Service}. Once the authentication is done, it is
* persistent for the entire service's operation.<br> * persistent for the entire service's operation.<br>
@@ -37,8 +40,13 @@ public interface Authenticator {
* if any IO error occur * if any IO error occur
* @throws AuthenticationInvalidCredentialException * @throws AuthenticationInvalidCredentialException
* if the credentials are not valid or cannot be used * if the credentials are not valid or cannot be used
* @throws UnresolvedCaptchaException
* if the service required captcha resolving but no
* {@link CaptchaResolver} was available or the resolver did not
* solve the challenge
*/ */
void login() throws IOException, AuthenticationInvalidCredentialException; void login() throws IOException, AuthenticationInvalidCredentialException,
UnresolvedCaptchaException;
/** /**
* Logout into the {@link Service}. The session is restored to an not * Logout into the {@link Service}. The session is restored to an not
@@ -48,4 +56,26 @@ public interface Authenticator {
* if any IO error occur * if any IO error occur
*/ */
void logout() throws IOException; void logout() throws IOException;
/**
* Returns this {@link Authenticator} configuration.
* <p>
* <b>IMPORTANT NOTE</b>: You should not modify any configuration within
* this configuration object once while the account is authenticated.
* Depending on the service, changing any setting could result in an state
* where the service cannot be logged out.
*
* @return this {@link Authenticator} configuration
*/
C getConfiguration();
/**
* This interface must be implemented in order to allow authentication
* configuration.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface AuthenticatorConfiguration {
}
} }

View File

@@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service;
/** /**
* Capability an certain {@link Authenticator} can have. * Capability an certain {@link Authenticator} can have.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public enum AuthenticatorCapability { public enum AuthenticatorCapability {

View File

@@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service;
/** /**
* This is an utility class to help manage Capabilities of all the services. * This is an utility class to help manage Capabilities of all the services.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @param <T> * @param <T>
* the capability enumeration * the capability enumeration
* @since 1.0 * @since 1.0

View File

@@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service;
/** /**
* Pair of username-password used for authenticating into services. * Pair of username-password used for authenticating into services.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public class Credential { public class Credential {

View File

@@ -16,6 +16,7 @@
*/ */
package com.rogiel.httpchannel.service; package com.rogiel.httpchannel.service;
import java.io.Closeable;
import java.nio.channels.Channel; import java.nio.channels.Channel;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
@@ -31,7 +32,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, Closeable {
/** /**
* @return the file size * @return the file size
*/ */

View File

@@ -20,7 +20,7 @@ package com.rogiel.httpchannel.service;
* This listener keeps an track on the progress on an {@link Downloader} * This listener keeps an track on the progress on an {@link Downloader}
* service. * service.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public interface DownloadListener { public interface DownloadListener {

View File

@@ -20,13 +20,17 @@ import java.net.URL;
import javax.tools.FileObject; import javax.tools.FileObject;
import com.rogiel.httpchannel.service.Downloader.DownloaderConfiguration;
import com.rogiel.httpchannel.service.config.NullDownloaderConfiguration;
/** /**
* Implements an service capable of downloading a file. * Implements an service capable of downloading a file.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public interface DownloadService extends Service { public interface DownloadService<C extends DownloaderConfiguration> extends
Service {
/** /**
* Creates a new instance of the {@link Downloader}. This instance will be * Creates a new instance of the {@link Downloader}. This instance will be
* attached to the {@link URL}, {@link FileObject} provided through the the * attached to the {@link URL}, {@link FileObject} provided through the the
@@ -34,14 +38,34 @@ public interface DownloadService extends Service {
* *
* @param url * @param url
* the url to be downloaded * the url to be downloaded
* @param file * @param configuration
* the destination file * the downloader configurationf
* @return an new instance of {@link Downloader} * @return an new instance of {@link Downloader}
*/ */
Downloader getDownloader(URL url); Downloader<C> getDownloader(URL url, C configuration);
/** /**
* Check if this {@link Service} can download from this URL. Implemtations * 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
* @return an new instance of {@link Downloader}
*/
Downloader<C> getDownloader(URL url);
/**
* Creates a new configuration object. If a service does not support or
* require configuration, {@link NullDownloaderConfiguration} should be
* returned.
*
* @return a new configuration object or {@link NullDownloaderConfiguration}
*/
C newDownloaderConfiguration();
/**
* Check if this {@link Service} can download from this URL. Implementations
* might or might not perform network activity. * might or might not perform network activity.
* <p> * <p>
* <b>Please note</b> that the value returned by this method may vary based * <b>Please note</b> that the value returned by this method may vary based

View File

@@ -18,18 +18,21 @@ package com.rogiel.httpchannel.service;
import java.io.IOException; import java.io.IOException;
import com.rogiel.httpchannel.captcha.CaptchaResolver;
import com.rogiel.httpchannel.service.Downloader.DownloaderConfiguration;
import com.rogiel.httpchannel.service.exception.DownloadLimitExceededException; import com.rogiel.httpchannel.service.exception.DownloadLimitExceededException;
import com.rogiel.httpchannel.service.exception.DownloadLinkNotFoundException; import com.rogiel.httpchannel.service.exception.DownloadLinkNotFoundException;
import com.rogiel.httpchannel.service.exception.DownloadNotAuthorizedException; import com.rogiel.httpchannel.service.exception.DownloadNotAuthorizedException;
import com.rogiel.httpchannel.service.exception.DownloadNotResumableException; import com.rogiel.httpchannel.service.exception.DownloadNotResumableException;
import com.rogiel.httpchannel.service.exception.UnresolvedCaptchaException;
/** /**
* This interfaces provides downloading for an service. * This interfaces provides downloading for an service.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public interface Downloader { public interface Downloader<C extends DownloaderConfiguration> {
/** /**
* Opens a new {@link DownloadChannel} that will be immediately ready to * Opens a new {@link DownloadChannel} that will be immediately ready to
* read data from the download stream. * read data from the download stream.
@@ -67,9 +70,105 @@ public interface Downloader {
* @throws DownloadNotResumableException * @throws DownloadNotResumableException
* if the download cannot be started at <tt>position</tt>. Will * if the download cannot be started at <tt>position</tt>. Will
* only be thrown if <tt>position > 0</tt>. * only be thrown if <tt>position > 0</tt>.
* @throws UnresolvedCaptchaException
* if the service required captcha resolving but no
* {@link CaptchaResolver} was available or the resolver did not
* solve the challenge
*/ */
DownloadChannel download(DownloadListener listener, long position) DownloadChannel openChannel(DownloadListener listener, long position)
throws IOException, DownloadLinkNotFoundException, throws IOException, DownloadLinkNotFoundException,
DownloadLimitExceededException, DownloadNotAuthorizedException, DownloadLimitExceededException, DownloadNotAuthorizedException,
DownloadNotResumableException; DownloadNotResumableException, UnresolvedCaptchaException;
/**
* Opens a new {@link DownloadChannel} with not listener. For more details,
* see {@link #openChannel(DownloadListener, long)}
*
* @param position
* the download start position. If seek is supported by service.
* If zero, download will start from the beginning.
* @return the {@link DownloadChannel} instance
* @throws IOException
* if any IO error occur
* @throws DownloadLinkNotFoundException
* if the direct download link cannot be found (the file could
* have been deleted)
* @throws DownloadLimitExceededException
* if the download limit has been exceed, most times thrown when
* downloading as a non-premium user
* @throws DownloadNotAuthorizedException
* if the user (or guest) account does not have necessary rights
* to download the file
* @throws DownloadNotResumableException
* if the download cannot be started at <tt>position</tt>. Will
* only be thrown if <tt>position > 0</tt>.
*/
DownloadChannel openChannel(long position) throws IOException,
DownloadLinkNotFoundException, DownloadLimitExceededException,
DownloadNotAuthorizedException, DownloadNotResumableException;
/**
* Opens a new {@link DownloadChannel} positioned at start. For more
* details, see {@link #openChannel(DownloadListener, long)}
*
* @param listener
* the listener to keep a track on the download progress
* @return the {@link DownloadChannel} instance
* @throws IOException
* if any IO error occur
* @throws DownloadLinkNotFoundException
* if the direct download link cannot be found (the file could
* have been deleted)
* @throws DownloadLimitExceededException
* if the download limit has been exceed, most times thrown when
* downloading as a non-premium user
* @throws DownloadNotAuthorizedException
* if the user (or guest) account does not have necessary rights
* to download the file
*/
DownloadChannel openChannel(DownloadListener listener) throws IOException,
DownloadLinkNotFoundException, DownloadLimitExceededException,
DownloadNotAuthorizedException;
/**
* Opens a new {@link DownloadChannel} with not listener and positioned at
* start. For more details, see {@link #openChannel(DownloadListener, long)}
*
* @return the {@link DownloadChannel} instance
* @throws IOException
* if any IO error occur
* @throws DownloadLinkNotFoundException
* if the direct download link cannot be found (the file could
* have been deleted)
* @throws DownloadLimitExceededException
* if the download limit has been exceed, most times thrown when
* downloading as a non-premium user
* @throws DownloadNotAuthorizedException
* if the user (or guest) account does not have necessary rights
* to download the file
*/
DownloadChannel openChannel() throws IOException,
DownloadLinkNotFoundException, DownloadLimitExceededException,
DownloadNotAuthorizedException;
/**
* Returns this {@link Downloader} configuration.
* <p>
* <b>IMPORTANT NOTE</b>: You should not modify any configuration within
* this configuration object once the download has started. Doing so, could
* result in a error.
*
* @return this {@link Downloader} configuration
*/
C getConfiguration();
/**
* This interface must be implemented in order to allow download
* configuration.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface DownloaderConfiguration {
}
} }

View File

@@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service;
/** /**
* Capability an certain {@link Downloader} can have. * Capability an certain {@link Downloader} can have.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public enum DownloaderCapability { public enum DownloaderCapability {
@@ -35,10 +35,21 @@ public enum DownloaderCapability {
* Can download files while authenticated with premium account * Can download files while authenticated with premium account
*/ */
PREMIUM_ACCOUNT_DOWNLOAD, PREMIUM_ACCOUNT_DOWNLOAD,
/** /**
* Resume interrupted downloads are possible and supported. * Can resume interrupted downloads even without authenticating
*/ */
RESUME, UNAUTHENTICATED_RESUME,
/**
* Can resume interrupted downloads but require to be logged with any
* account
*/
NON_PREMIUM_ACCOUNT_RESUME,
/**
* Can resume interrupted downloads but requires an premium account
*/
PREMIUM_ACCOUNT_RESUME,
/** /**
* Can check the status of the given link before starting download. * Can check the status of the given link before starting download.
*/ */

View File

@@ -16,7 +16,7 @@
*/ */
package com.rogiel.httpchannel.service; package com.rogiel.httpchannel.service;
import com.rogiel.httpchannel.service.config.ServiceConfiguration; import com.rogiel.httpchannel.captcha.CaptchaResolver;
/** /**
* Base interface for all the services. Whenever the operation suported by the * Base interface for all the services. Whenever the operation suported by the
@@ -27,7 +27,7 @@ import com.rogiel.httpchannel.service.config.ServiceConfiguration;
* </li> * </li>
* </ul> * </ul>
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public interface Service extends Cloneable { public interface Service extends Cloneable {
@@ -53,19 +53,13 @@ public interface Service extends Cloneable {
int getMinorVersion(); int getMinorVersion();
/** /**
* Returns this {@link ServiceConfiguration} instance * Sets this service captcha resolver. Resolvers are safe to be switched
* even after an transfer has begun.
* *
* @return the {@link ServiceConfiguration} instance * @param resolver
* the captcha resolver
*/ */
ServiceConfiguration getServiceConfiguration(); void setCaptchaResolver(CaptchaResolver resolver);
/**
* Sets this {@link ServiceConfiguration} instance
*
* @param configuration
* the {@link ServiceConfiguration} instance
*/
void setServiceConfiguration(ServiceConfiguration configuration);
/** /**
* @return a cloned version of this service * @return a cloned version of this service

View File

@@ -18,6 +18,8 @@ package com.rogiel.httpchannel.service;
import java.io.Serializable; import java.io.Serializable;
import com.rogiel.httpchannel.service.helper.Services;
/** /**
* An ID used to represent the given service * An ID used to represent the given service
* *
@@ -27,7 +29,7 @@ public class ServiceID implements Serializable, Cloneable {
/** /**
* This class serialization version UID * This class serialization version UID
*/ */
private static final long serialVersionUID = -1078456596792552200L; private static final long serialVersionUID = 1L;
/** /**
* The raw ID * The raw ID

View File

@@ -16,7 +16,9 @@
*/ */
package com.rogiel.httpchannel.service; package com.rogiel.httpchannel.service;
import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channel; import java.nio.channels.Channel;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
@@ -34,7 +36,7 @@ import com.rogiel.httpchannel.service.exception.UploadLinkNotFoundException;
* *
* @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, Closeable {
/** /**
* @return the file size * @return the file size
*/ */
@@ -52,7 +54,7 @@ public interface UploadChannel extends WritableByteChannel {
* *
* @return the download link for this upload * @return the download link for this upload
*/ */
String getDownloadLink(); URL getDownloadLink();
/** /**
* @throws UploadLinkNotFoundException * @throws UploadLinkNotFoundException

View File

@@ -16,13 +16,16 @@
*/ */
package com.rogiel.httpchannel.service; package com.rogiel.httpchannel.service;
import com.rogiel.httpchannel.service.Uploader.UploaderConfiguration;
import com.rogiel.httpchannel.service.config.NullUploaderConfiguration;
/** /**
* Implements an service capable of uploading a file. * Implements an service capable of uploading a file.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public interface UploadService extends Service { public interface UploadService<C extends UploaderConfiguration> extends Service {
/** /**
* Creates a new instance of {@link Uploader}. This instance is attached * Creates a new instance of {@link Uploader}. This instance is attached
* with the parent {@link Service} instance.<br> * with the parent {@link Service} instance.<br>
@@ -32,11 +35,34 @@ public interface UploadService extends Service {
* the name of the file to be uploaded * the name of the file to be uploaded
* @param filesize * @param filesize
* the size of the file to be uploaded. This must be exact. * the size of the file to be uploaded. This must be exact.
* @param description * @param configuration
* the description of the upload. If supported. * the uploader configuration
* @return the new {@link Uploader} instance * @return the new {@link Uploader} instance
*/ */
Uploader getUploader(String filename, long filesize, String description); Uploader<C> getUploader(String filename, long filesize, C configuration);
/**
* 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 filename
* the name of the file to be uploaded
* @param filesize
* the size of the file to be uploaded. This must be exact.
* @return the new {@link Uploader} instance
*/
Uploader<C> getUploader(String filename, long filesize);
/**
* Creates a new configuration object. If a service does not support or
* require configuration, {@link NullUploaderConfiguration} should be
* returned.
*
* @return a new configuration object or
* {@link NullUploaderConfiguration}
*/
C newUploaderConfiguration();
/** /**
* Get the maximum upload file size supported by this service. * Get the maximum upload file size supported by this service.

View File

@@ -18,13 +18,17 @@ package com.rogiel.httpchannel.service;
import java.io.IOException; import java.io.IOException;
import com.rogiel.httpchannel.captcha.CaptchaResolver;
import com.rogiel.httpchannel.service.Uploader.UploaderConfiguration;
import com.rogiel.httpchannel.service.exception.UnresolvedCaptchaException;
/** /**
* This interfaces provides uploading for an service. * This interfaces provides uploading for an service.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public interface Uploader { public interface Uploader<C extends UploaderConfiguration> {
/** /**
* Opens a new {@link UploadChannel} that will be immediately ready to * Opens a new {@link UploadChannel} that will be immediately ready to
* receive data to be sent to the upload stream. * receive data to be sent to the upload stream.
@@ -44,6 +48,52 @@ public interface Uploader {
* @return the {@link UploadChannel} instance * @return the {@link UploadChannel} instance
* @throws IOException * @throws IOException
* if any IO error occur * if any IO error occur
* @throws UnresolvedCaptchaException
* if the service required captcha resolving but no
* {@link CaptchaResolver} was available or the resolver did not
* solve the challenge
*/ */
UploadChannel upload() throws IOException; UploadChannel openChannel() throws IOException, UnresolvedCaptchaException;
/**
* Returns this {@link Uploader} configuration.
* <p>
* <b>IMPORTANT NOTE</b>: You should not modify any configuration within
* this configuration object once the upload has started. Doing so, could
* result in a error.
*
* @return this {@link Uploader} configuration
*/
C getConfiguration();
/**
* This interface must be implemented in order to allow upload
* configuration.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface UploaderConfiguration {
}
/**
* Defines an {@link UploaderConfiguration} that can allow <b>at least</b>
* an description field
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface DescriptionableUploaderConfiguration extends
UploaderConfiguration {
public static final String DEFAULT_DESCRIPTION = "Uploaded by httpchannel";
/**
* @return the upload description
*/
String description();
/**
* @param description
* the upload description
*/
DescriptionableUploaderConfiguration description(String description);
}
} }

View File

@@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service;
/** /**
* Capability an certain {@link Uploader} can have. * Capability an certain {@link Uploader} can have.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public enum UploaderCapability { public enum UploaderCapability {

View File

@@ -1,26 +0,0 @@
/*
* 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);
}

View File

@@ -1,47 +0,0 @@
/*
* 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;
}
}

View File

@@ -0,0 +1,17 @@
package com.rogiel.httpchannel.service.config;
import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Authenticator.AuthenticatorConfiguration;
/**
* An default {@link AuthenticatorConfiguration} implementation that is
* generally returned by
* {@link AuthenticationService#newAuthenticatorConfiguration()} when the
* service does not support or require any kind of configuration.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public final class NullAuthenticatorConfiguration implements
AuthenticatorConfiguration {
public static final NullAuthenticatorConfiguration SHARED_INSTANCE = new NullAuthenticatorConfiguration();
}

View File

@@ -0,0 +1,16 @@
package com.rogiel.httpchannel.service.config;
import com.rogiel.httpchannel.service.DownloadService;
import com.rogiel.httpchannel.service.Downloader.DownloaderConfiguration;
/**
* An default {@link DownloaderConfiguration} implementation that is generally
* returned by {@link DownloadService#newDownloaderConfiguration()} when the
* service does not support or require any kind of configuration.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public final class NullDownloaderConfiguration implements
DownloaderConfiguration {
public static final NullDownloaderConfiguration SHARED_INSTANCE = new NullDownloaderConfiguration();
}

View File

@@ -0,0 +1,15 @@
package com.rogiel.httpchannel.service.config;
import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.Uploader.UploaderConfiguration;
/**
* An default {@link UploaderConfiguration} implementation that is generally
* returned by {@link UploadService#newUploaderConfiguration()} when the service
* does not support or require any kind of configuration.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public final class NullUploaderConfiguration implements UploaderConfiguration {
public static final NullUploaderConfiguration SHARED_INSTANCE = new NullUploaderConfiguration();
}

View File

@@ -1,41 +0,0 @@
/*
* 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 com.rogiel.httpchannel.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 {
}

View File

@@ -1,113 +0,0 @@
/*
* 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 com.rogiel.httpchannel.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;
}
});
}
}

View File

@@ -1,51 +0,0 @@
/*
* 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 {
* &#064;ServiceConfigurationProperty(defaultValue = &quot;true&quot;)
* 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();
}

View File

@@ -16,7 +16,7 @@
*/ */
package com.rogiel.httpchannel.service.exception; package com.rogiel.httpchannel.service.exception;
import com.rogiel.httpchannel.service.captcha.CaptchaResolver; import com.rogiel.httpchannel.captcha.CaptchaResolver;
/** /**
* Exception thrown if the {@link CaptchaResolver} has returned an invalid * Exception thrown if the {@link CaptchaResolver} has returned an invalid
@@ -24,13 +24,13 @@ import com.rogiel.httpchannel.service.captcha.CaptchaResolver;
* *
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
public class DownloadInvalidCaptchaException extends DownloadServiceException { public class InvalidCaptchaException extends ChannelServiceException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Creates a new empty instance of this exception * Creates a new empty instance of this exception
*/ */
public DownloadInvalidCaptchaException() { public InvalidCaptchaException() {
super(); super();
} }
@@ -40,7 +40,7 @@ public class DownloadInvalidCaptchaException extends DownloadServiceException {
* @param cause * @param cause
* the root cause * the root cause
*/ */
public DownloadInvalidCaptchaException(String message, Throwable cause) { public InvalidCaptchaException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
@@ -48,7 +48,7 @@ public class DownloadInvalidCaptchaException extends DownloadServiceException {
* @param message * @param message
* the message * the message
*/ */
public DownloadInvalidCaptchaException(String message) { public InvalidCaptchaException(String message) {
super(message); super(message);
} }
@@ -56,7 +56,7 @@ public class DownloadInvalidCaptchaException extends DownloadServiceException {
* @param cause * @param cause
* the root cause * the root cause
*/ */
public DownloadInvalidCaptchaException(Throwable cause) { public InvalidCaptchaException(Throwable cause) {
super(cause); super(cause);
} }
} }

View File

@@ -14,42 +14,49 @@
* 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.util.transformer; package com.rogiel.httpchannel.service.exception;
import com.rogiel.httpchannel.captcha.CaptchaResolver;
/** /**
* @author Rogiel * Exception thrown if the {@link CaptchaResolver} has returned an invalid
* @since 1.0 * captcha
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
public class TransformationException extends Exception { public class UnresolvedCaptchaException extends ChannelServiceException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public TransformationException() { /**
* Creates a new empty instance of this exception
*/
public UnresolvedCaptchaException() {
super();
}
/**
* @param message
* the message
* @param cause
* the root cause
*/
public UnresolvedCaptchaException(String message, Throwable cause) {
super(message, cause);
} }
/** /**
* @param message * @param message
* the message * the message
*/ */
public TransformationException(String message) { public UnresolvedCaptchaException(String message) {
super(message); super(message);
} }
/** /**
* @param cause * @param cause
* the cause * the root cause
*/ */
public TransformationException(Throwable cause) { public UnresolvedCaptchaException(Throwable cause) {
super(cause); super(cause);
} }
/**
* @param message
* the message
* @param cause
* the cause
*/
public TransformationException(String message, Throwable cause) {
super(message, cause);
}
} }

View 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.helper;
import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Authenticator;
import com.rogiel.httpchannel.service.Authenticator.AuthenticatorConfiguration;
import com.rogiel.httpchannel.service.Credential;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class AuthenticationServices {
public static <S extends AuthenticationService<C>, C extends AuthenticatorConfiguration> Authenticator<C> authenticate(
S service, C configuration, String username, String password) {
return service.getAuthenticator(new Credential(username, password),
configuration);
}
public static <S extends AuthenticationService<C>, C extends AuthenticatorConfiguration> Authenticator<C> authenticate(
S service, String username, String password) {
return service.getAuthenticator(new Credential(username, password));
}
}

View File

@@ -14,19 +14,19 @@
* 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.captcha; package com.rogiel.httpchannel.service.helper;
import java.io.IOException;
import java.net.URL;
import com.rogiel.httpchannel.service.DownloadService;
/** /**
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/ */
public interface CaptchaResolver { public class DownloadServices {
/** public static boolean canDownload(DownloadService<?> service, URL url)
* Passes an captcha by parameter and waits for the response of the throws IOException {
* challenge. return service.matchURL(url);
* }
* @param captcha
* the captcha challenge
*/
String resolve(Captcha captcha);
} }

View File

@@ -14,12 +14,16 @@
* 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 com.rogiel.httpchannel.service.helper;
import java.net.URL; import java.net.URL;
import java.util.Iterator; import java.util.Iterator;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import com.rogiel.httpchannel.service.DownloadService;
import com.rogiel.httpchannel.service.Service;
import com.rogiel.httpchannel.service.ServiceID;
/** /**
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
@@ -43,12 +47,12 @@ public class Services {
* the URL * the URL
* @return the matched service * @return the matched service
*/ */
public static DownloadService matchURL(URL url) { public static DownloadService<?> matchURL(URL url) {
for (final Service service : iterate()) { for (final Service service : iterate()) {
if (!(service instanceof DownloadService)) if (!(service instanceof DownloadService))
continue; continue;
if (((DownloadService) service).matchURL(url)) if (((DownloadService<?>) service).matchURL(url))
return (DownloadService) service; return (DownloadService<?>) service;
} }
return null; return null;
} }

View File

@@ -14,37 +14,34 @@
* 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 com.rogiel.httpchannel.service.helper;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.Uploader;
import com.rogiel.httpchannel.service.Uploader.UploaderConfiguration;
/** /**
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
public class ServiceHelper { public class UploadServices {
private final Service service; public static <S extends UploadService<C>, C extends UploaderConfiguration> Uploader<C> upload(
S service, C configuration, Path path) throws IOException {
/** return service.getUploader(path.getFileName().toString(),
* @param service Files.size(path), configuration);
* the service
*/
public ServiceHelper(Service service) {
this.service = service;
} }
public UploadChannel upload(Path path, String description) public static <S extends UploadService<C>, C extends UploaderConfiguration> Uploader<C> upload(
throws IOException { S service, Path path) throws IOException {
return ((UploadService) service).getUploader( return service.getUploader(path.getFileName().toString(),
path.getFileName().toString(), Files.size(path), description) Files.size(path));
.upload();
} }
public UploadChannel upload(File file, String description) public static boolean canUpload(UploadService<?> service, Path path)
throws IOException { throws IOException {
return ((UploadService) service).getUploader(file.getName(), return service.getMaximumFilesize() >= Files.size(path);
file.length(), description).upload();
} }
} }

View File

@@ -1,25 +0,0 @@
/*
* 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.util.transformer;
/**
* @author rogiel
*
*/
public interface Transformer<O> {
O transform(String data) throws TransformationException;
}

View File

@@ -1,48 +0,0 @@
/*
* 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.util.transformer;
import java.net.URL;
import com.rogiel.httpchannel.util.transformer.impl.BooleanTransformer;
import com.rogiel.httpchannel.util.transformer.impl.IntegerTransformer;
import com.rogiel.httpchannel.util.transformer.impl.LongTransformer;
import com.rogiel.httpchannel.util.transformer.impl.StringTransformer;
import com.rogiel.httpchannel.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;
}
}

View File

@@ -1,30 +0,0 @@
/*
* 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.util.transformer.impl;
import com.rogiel.httpchannel.util.transformer.Transformer;
/**
* @author rogiel
*
*/
public class BooleanTransformer implements Transformer<Boolean> {
@Override
public Boolean transform(String data) {
return Boolean.parseBoolean(data);
}
}

View File

@@ -1,30 +0,0 @@
/*
* 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.util.transformer.impl;
import com.rogiel.httpchannel.util.transformer.Transformer;
/**
* @author rogiel
*
*/
public class IntegerTransformer implements Transformer<Integer> {
@Override
public Integer transform(String data) {
return Integer.parseInt(data);
}
}

View File

@@ -1,30 +0,0 @@
/*
* 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.util.transformer.impl;
import com.rogiel.httpchannel.util.transformer.Transformer;
/**
* @author rogiel
*
*/
public class LongTransformer implements Transformer<Long> {
@Override
public Long transform(String data) {
return Long.parseLong(data);
}
}

View File

@@ -1,30 +0,0 @@
/*
* 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.util.transformer.impl;
import com.rogiel.httpchannel.util.transformer.Transformer;
/**
* @author rogiel
*
*/
public class StringTransformer implements Transformer<String> {
@Override
public String transform(String data) {
return data;
}
}

View File

@@ -1,39 +0,0 @@
/*
* 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.util.transformer.impl;
import java.net.MalformedURLException;
import java.net.URL;
import com.rogiel.httpchannel.util.transformer.TransformationException;
import com.rogiel.httpchannel.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);
}
}
}

View File

@@ -0,0 +1,24 @@
<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>
<parent>
<artifactId>httpchannel-capcha</artifactId>
<groupId>com.rogiel.httpchannel</groupId>
<version>1.0.0</version>
<relativePath>..</relativePath>
</parent>
<artifactId>httpchannel-captcha-recaptcha</artifactId>
<name>HttpChannel/CaptchaService/ReCaptcha</name>
<description>This module provides captcha resolving for Google ReCaptcha</description>
<dependencies>
<dependency>
<groupId>com.rogiel.httpchannel</groupId>
<artifactId>httpchannel-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.rogiel.httpchannel</groupId>
<artifactId>httpchannel-util</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,15 @@
package com.rogiel.httpchannel.captcha.impl;
import java.net.URL;
import com.rogiel.httpchannel.captcha.AbstractImageCaptcha;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class ReCaptcha extends AbstractImageCaptcha {
public ReCaptcha(URL url, String ID) {
super(url, ID);
}
}

View File

@@ -0,0 +1,44 @@
/**
*
*/
package com.rogiel.httpchannel.captcha.impl;
import java.io.IOException;
import java.net.URL;
import java.util.regex.Pattern;
import com.rogiel.httpchannel.captcha.AbstractImageCaptchaService;
import com.rogiel.httpchannel.util.PatternUtils;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class ReCaptchaService extends AbstractImageCaptchaService<ReCaptcha> {
// http://www.google.com/recaptcha/api/noscript?k=6LdRTL8SAAAAAE9UOdWZ4d0Ky-aeA7XfSqyWDM2m
private static final Pattern CAPTCHA_URL_PATTERN = Pattern
.compile("http://www\\.google\\.com/recaptcha/api/challenge\\?k=([0-9A-z|\\-]*)(&(.*))?");
private static final Pattern CAPTCHA_IMAGE_PATTERN = Pattern
.compile("challenge : '(.*)'");
private static final String BASE_URL = "http://www.google.com/recaptcha/api/image?c=";
@Override
public ReCaptcha create(HTMLPage page) throws IOException {
final String url = page.findScriptSrc(CAPTCHA_URL_PATTERN);
if (url == null)
return null;
final String captchaPage = get(url).asString();
final String id = PatternUtils.find(CAPTCHA_IMAGE_PATTERN, captchaPage,
1);
return new ReCaptcha(new URL(BASE_URL + id), id);
}
@Override
public boolean resolve(ReCaptcha captcha) {
// not supported!
return false;
}
}

View File

@@ -0,0 +1,16 @@
<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>
<parent>
<artifactId>httpchannel</artifactId>
<groupId>com.rogiel.httpchannel</groupId>
<version>1.0.0</version>
<relativePath>..</relativePath>
</parent>
<artifactId>httpchannel-capcha</artifactId>
<name>HttpChannel/CaptchaService</name>
<description>This module provides implementations for captcha resolving</description>
<packaging>pom</packaging>
<modules>
<module>httpchannel-captcha-recaptcha</module>
</modules>
</project>

View File

@@ -0,0 +1,31 @@
<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>
<parent>
<artifactId>httpchannel</artifactId>
<groupId>com.rogiel.httpchannel</groupId>
<version>1.0.0</version>
<relativePath>..</relativePath>
</parent>
<artifactId>httpchannel-channelcopy</artifactId>
<name>HttpChannel/ChannelCopy</name>
<description>The HttpChannel ChannelCopy module provides an easy method to copy data from a single download channel into one or more upload channels.</description>
<dependencies>
<dependency>
<groupId>com.rogiel.httpchannel</groupId>
<artifactId>httpchannel-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.rogiel.httpchannel.services</groupId>
<artifactId>httpchannel-service-megaupload</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.rogiel.httpchannel.services</groupId>
<artifactId>httpchannel-service-multiupload</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,206 @@
package com.rogiel.httpchannel.copy;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import com.rogiel.httpchannel.copy.exception.NoServiceFoundException;
import com.rogiel.httpchannel.service.DownloadChannel;
import com.rogiel.httpchannel.service.DownloadService;
import com.rogiel.httpchannel.service.UploadChannel;
import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.Uploader.UploaderConfiguration;
import com.rogiel.httpchannel.service.exception.DownloadLimitExceededException;
import com.rogiel.httpchannel.service.exception.DownloadLinkNotFoundException;
import com.rogiel.httpchannel.service.exception.DownloadNotAuthorizedException;
import com.rogiel.httpchannel.service.exception.UploadLinkNotFoundException;
import com.rogiel.httpchannel.service.helper.Services;
/**
* This class provides an utility that copies the entire content of a
* {@link ReadableByteChannel} (this can be an {@link DownloadChannel} or any
* {@link Files#newByteChannel(Path, java.nio.file.OpenOption...)} opened with
* {@link StandardOpenOption#READ READ}.
* <p>
* The input channel must be created or provided at construction time, but
* several output channels can be added through
* {@link #addOutput(UploadChannel)}, {@link #addOutput(UploadService)} or
* {@link #addOutput(UploadService, UploaderConfiguration)}.
* <p>
* Once all output channels were set, {@link #call()} must be called in order to
* start copying data. This class implements {@link Callable} and thus can be
* executed inside an {@link Executor}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ChannelCopy implements Callable<List<URL>> {
/**
* The input channel
*/
private final ReadableByteChannel downloadChannel;
/**
* The filename
*/
private final String filename;
/**
* The filesise
*/
private final long filesize;
/**
* The list of all channels to write data to
*/
private final List<UploadChannel> uploadChannels = new ArrayList<>();
/**
* Initializes with an {@link ReadableByteChannel}, filename and filesize
*
* @param channel
* the channel
* @param filename
* the file name
* @param filesize
* the file size
*/
public ChannelCopy(ReadableByteChannel channel, String filename,
long filesize) {
this.downloadChannel = channel;
this.filename = filename;
this.filesize = filesize;
}
/**
* Initializes with a {@link Path}. Will open a new channel.
*
* @param path
* @throws IOException
*/
public ChannelCopy(Path path) throws IOException {
this(Files.newByteChannel(path, StandardOpenOption.READ), path
.getFileName().toString(), Files.size(path));
}
/**
* Initializes with a {@link DownloadChannel}
*
* @param downloadChannel
* the download channel
*/
public ChannelCopy(DownloadChannel downloadChannel) {
this.downloadChannel = downloadChannel;
this.filename = downloadChannel.getFilename();
this.filesize = downloadChannel.getFilesize();
}
/**
* Initializes with an {@link URL}. First tries to open an
* {@link DownloadChannel}, if no service is found,
* {@link NoServiceFoundException} is thrown.
*
* @param url
* the source {@link URL}
* @throws DownloadLinkNotFoundException
* if the download link could not be found
* @throws DownloadLimitExceededException
* if the download limit has been exceeded
* @throws DownloadNotAuthorizedException
* if the download was not authorized by the service
* @throws NoServiceFoundException
* if no service could be found for the {@link URL}
* @throws IOException
* if any IO error occur
*/
public ChannelCopy(URL url) throws DownloadLinkNotFoundException,
DownloadLimitExceededException, DownloadNotAuthorizedException,
IOException {
final DownloadService<?> service = Services.matchURL(url);
if (service == null)
throw new NoServiceFoundException(url.toString());
final DownloadChannel downloadChannel = service.getDownloader(url)
.openChannel();
this.downloadChannel = downloadChannel;
this.filename = downloadChannel.getFilename();
this.filesize = downloadChannel.getFilesize();
}
/**
* Adds a new output channel in which data should be written
*
* @param channel
* the channel
*/
public void addOutput(UploadChannel channel) {
uploadChannels.add(channel);
}
/**
* Adds a new output in which data should be written. Creates a new
* {@link UploadChannel} based on the {@link UploadService}.
*
* @param service
* the upload service
* @throws IOException
* if any IO error occur
*/
public void addOutput(UploadService<?> service) throws IOException {
addOutput(service.getUploader(filename, filesize).openChannel());
}
/**
* Adds a new output in which data should be written. Creates a new
* {@link UploadChannel} based on the {@link UploadService}.
*
* @param service
* the upload service
* @param configuration
* the uploader configuration
* @throws IOException
* if any IO error occur
*/
public <S extends UploadService<C>, C extends UploaderConfiguration> void addOutput(
S service, C configuration) throws IOException {
addOutput(service.getUploader(filename, filesize, configuration)
.openChannel());
}
@Override
public List<URL> call() throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(16 * 1024);
try {
while (downloadChannel.read(buffer) >= 0) {
buffer.flip();
final int limit = buffer.limit();
final int position = buffer.position();
for (final UploadChannel channel : uploadChannels) {
channel.write(buffer);
buffer.limit(limit).position(position);
}
buffer.clear();
}
} finally {
downloadChannel.close();
for (final UploadChannel channel : uploadChannels) {
try {
channel.close();
} catch (UploadLinkNotFoundException e) {
}
}
}
final List<URL> urls = new ArrayList<>();
for (final UploadChannel channel : uploadChannels) {
urls.add(channel.getDownloadLink());
}
return urls;
}
}

View File

@@ -0,0 +1,42 @@
package com.rogiel.httpchannel.copy.exception;
import com.rogiel.httpchannel.service.exception.ChannelServiceException;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ChannelCopyException extends ChannelServiceException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance
*/
public ChannelCopyException() {
}
/**
* @param message
* the message
* @param cause
* the cause
*/
public ChannelCopyException(String message, Throwable cause) {
super(message, cause);
}
/**
* @param message
* the message
*/
public ChannelCopyException(String message) {
super(message);
}
/**
* @param cause
* the cause
*/
public ChannelCopyException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,40 @@
package com.rogiel.httpchannel.copy.exception;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class NoServiceFoundException extends ChannelCopyException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance
*/
public NoServiceFoundException() {
}
/**
* @param message
* the message
* @param cause
* the cause
*/
public NoServiceFoundException(String message, Throwable cause) {
super(message, cause);
}
/**
* @param message
* the message
*/
public NoServiceFoundException(String message) {
super(message);
}
/**
* @param cause
* the cause
*/
public NoServiceFoundException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,20 @@
package com.rogiel.httpchannel.wirecopy;
import java.io.IOException;
import java.nio.file.Paths;
import org.junit.Test;
import com.rogiel.httpchannel.copy.ChannelCopy;
import com.rogiel.httpchannel.service.impl.MegaUploadService;
import com.rogiel.httpchannel.service.impl.MultiUploadService;
public class ChannelCopyTest {
@Test
public void testWireCopy() throws IOException {
final ChannelCopy copy = new ChannelCopy(Paths.get("pom.xml"));
copy.addOutput(new MegaUploadService());
copy.addOutput(new MultiUploadService());
System.out.println(copy.call());
}
}

View File

@@ -0,0 +1,20 @@
<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>
<parent>
<artifactId>httpchannel-service</artifactId>
<groupId>com.rogiel.httpchannel</groupId>
<version>1.0.0</version>
<relativePath>..</relativePath>
</parent>
<artifactId>httpchannel-service-depositfiles</artifactId>
<groupId>com.rogiel.httpchannel.services</groupId>
<name>HttpChannel/Service/DepositFiles</name>
<description>Provides upload access to depositfiles.com</description>
<dependencies>
<dependency>
<groupId>com.rogiel.httpchannel</groupId>
<artifactId>httpchannel-captcha-recaptcha</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,212 @@
package com.rogiel.httpchannel.service.impl;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import com.rogiel.httpchannel.captcha.impl.ReCaptcha;
import com.rogiel.httpchannel.captcha.impl.ReCaptchaService;
import com.rogiel.httpchannel.service.AbstractAuthenticator;
import com.rogiel.httpchannel.service.AbstractHttpService;
import com.rogiel.httpchannel.service.AbstractUploader;
import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Authenticator;
import com.rogiel.httpchannel.service.AuthenticatorCapability;
import com.rogiel.httpchannel.service.CapabilityMatrix;
import com.rogiel.httpchannel.service.Credential;
import com.rogiel.httpchannel.service.Service;
import com.rogiel.httpchannel.service.ServiceID;
import com.rogiel.httpchannel.service.UploadChannel;
import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.Uploader;
import com.rogiel.httpchannel.service.UploaderCapability;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback;
import com.rogiel.httpchannel.service.config.NullAuthenticatorConfiguration;
import com.rogiel.httpchannel.service.config.NullUploaderConfiguration;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.service.exception.UnresolvedCaptchaException;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
/**
* This service handles uploads to UploadKing.com.
*
* @author <a href="http://www.rogiel.com/">Rogiel</a>
* @since 1.0
*/
public class DepositFilesService extends AbstractHttpService implements
Service, UploadService<NullUploaderConfiguration>,
AuthenticationService<NullAuthenticatorConfiguration> {
/**
* This service ID
*/
public static final ServiceID SERVICE_ID = ServiceID.create("depositfiles");
private static final Pattern UPLOAD_URL_PATTERN = Pattern
.compile("http://fileshare([0-9]*)\\.depositfiles\\.com/(.*)/\\?X-Progress-ID=(.*)");
private static final Pattern DOWNLOAD_URL_PATTERN = Pattern
.compile("http://(www\\.)?depositfiles\\.com/files/([0-9A-z]*)");
private static final Pattern VALID_LOGIN_REDIRECT = Pattern
.compile("window.location.href");
private final ReCaptchaService captchaService = new ReCaptchaService();
@Override
public ServiceID getID() {
return SERVICE_ID;
}
@Override
public int getMajorVersion() {
return 1;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public Uploader<NullUploaderConfiguration> getUploader(String filename,
long filesize, NullUploaderConfiguration configuration) {
return new UploadKingUploader(filename, filesize, configuration);
}
@Override
public Uploader<NullUploaderConfiguration> getUploader(String filename,
long filesize) {
return getUploader(filename, filesize, newUploaderConfiguration());
}
@Override
public NullUploaderConfiguration newUploaderConfiguration() {
return NullUploaderConfiguration.SHARED_INSTANCE;
}
@Override
public long getMaximumFilesize() {
return 2 * 1024 * 1024 * 1024;
}
@Override
public String[] getSupportedExtensions() {
return null;
}
@Override
public CapabilityMatrix<UploaderCapability> getUploadCapabilities() {
return new CapabilityMatrix<UploaderCapability>(
UploaderCapability.UNAUTHENTICATED_UPLOAD,
UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD,
UploaderCapability.PREMIUM_ACCOUNT_UPLOAD);
}
@Override
public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
Credential credential, NullAuthenticatorConfiguration configuration) {
return new UploadKingAuthenticator(credential, configuration);
}
@Override
public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
Credential credential) {
return getAuthenticator(credential, newAuthenticatorConfiguration());
}
@Override
public NullAuthenticatorConfiguration newAuthenticatorConfiguration() {
return NullAuthenticatorConfiguration.SHARED_INSTANCE;
}
@Override
public CapabilityMatrix<AuthenticatorCapability> getAuthenticationCapability() {
return new CapabilityMatrix<AuthenticatorCapability>();
}
protected class UploadKingUploader extends
AbstractUploader<NullUploaderConfiguration> implements
Uploader<NullUploaderConfiguration>,
LinkedUploadChannelCloseCallback {
private Future<HTMLPage> uploadFuture;
public UploadKingUploader(String filename, long filesize,
NullUploaderConfiguration configuration) {
super(filename, filesize, configuration);
}
@Override
public UploadChannel openChannel() throws IOException {
final HTMLPage page = get("http://www.depositfiles.com/").asPage();
final String url = page.findFormAction(UPLOAD_URL_PATTERN);
final String uploadID = page.getInputValue("UPLOAD_IDENTIFIER");
final String maxFileSize = page.getInputValue("MAX_FILE_SIZE");
final LinkedUploadChannel channel = createLinkedChannel(this);
uploadFuture = multipartPost(url).parameter("files", channel)
.parameter("go", true)
.parameter("UPLOAD_IDENTIFIER", uploadID)
.parameter("agree", true)
.parameter("MAX_FILE_SIZE", maxFileSize).asPageAsync();
return waitChannelLink(channel, uploadFuture);
}
@Override
public String finish() throws IOException {
try {
final String link = uploadFuture.get().findScript(
DOWNLOAD_URL_PATTERN, 0);
if (link == null)
return null;
return link;
} catch (InterruptedException e) {
return null;
} catch (ExecutionException e) {
throw (IOException) e.getCause();
}
}
}
protected class UploadKingAuthenticator extends
AbstractAuthenticator<NullAuthenticatorConfiguration> implements
Authenticator<NullAuthenticatorConfiguration> {
public UploadKingAuthenticator(Credential credential,
NullAuthenticatorConfiguration configuration) {
super(credential, configuration);
}
@Override
public void login() throws IOException {
HTMLPage page = post("http://depositfiles.com/login.php?return=%2F")
.parameter("go", true)
.parameter("login", credential.getUsername())
.parameter("password", credential.getPassword()).asPage();
final ReCaptcha captcha = captchaService.create(page);
if (captcha != null) {
if (!resolveCaptcha(captcha))
throw new UnresolvedCaptchaException();
page = post("http://depositfiles.com/login.php?return=%2F")
.parameter("go", true)
.parameter("login", credential.getUsername())
.parameter("password", credential.getPassword())
.parameter("recaptcha_challenge_field", captcha.getID())
.parameter("recaptcha_response_field",
captcha.getAnswer()).asPage();
}
if (!page.contains(VALID_LOGIN_REDIRECT))
throw new AuthenticationInvalidCredentialException();
return;
}
@Override
public void logout() throws IOException {
post("http://www.uploadking.com/login").parameter("do", "logout")
.request();
// TODO check logout status
}
}
}

View File

@@ -0,0 +1 @@
com.rogiel.httpchannel.service.impl.MultiUploadService

View File

@@ -0,0 +1,39 @@
package com.rogiel.httpchannel.service.impl;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import com.rogiel.httpchannel.service.UploaderCapability;
import com.rogiel.httpchannel.util.ChannelUtils;
public class DepositFilesServiceTest {
private DepositFilesService service;
@Before
public void setUp() throws Exception {
service = new DepositFilesService();
}
@Test
public void testNonLoguedInUploader() throws IOException {
assertTrue(
"This service does not have the capability UploadCapability.UNAUTHENTICATED_UPLOAD",
service.getUploadCapabilities().has(
UploaderCapability.UNAUTHENTICATED_UPLOAD));
final Path path = Paths.get("src/test/resources/upload-test-file.txt");
final URL url = ChannelUtils.upload(service, path);
Assert.assertNotNull(url);
System.out.println(url);
}
}

View File

@@ -0,0 +1,3 @@
This is a simple upload test file.
This is for testing purposes only.

View File

@@ -7,4 +7,7 @@
<relativePath>..</relativePath> <relativePath>..</relativePath>
</parent> </parent>
<artifactId>httpchannel-service-hotfile</artifactId> <artifactId>httpchannel-service-hotfile</artifactId>
<groupId>com.rogiel.httpchannel.services</groupId>
<name>HttpChannel/Service/HotFile</name>
<description>Provides download and upload access to hotfile.com</description>
</project> </project>

View File

@@ -25,12 +25,12 @@ import java.util.regex.Pattern;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ClientProtocolException;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.htmlparser.Tag; import org.htmlparser.Tag;
import com.rogiel.httpchannel.service.AbstractDownloader; import com.rogiel.httpchannel.service.AbstractAuthenticator;
import com.rogiel.httpchannel.service.AbstractHttpDownloader;
import com.rogiel.httpchannel.service.AbstractHttpService; import com.rogiel.httpchannel.service.AbstractHttpService;
import com.rogiel.httpchannel.service.AbstractUploader;
import com.rogiel.httpchannel.service.AuthenticationService; import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Authenticator; import com.rogiel.httpchannel.service.Authenticator;
import com.rogiel.httpchannel.service.AuthenticatorCapability; import com.rogiel.httpchannel.service.AuthenticatorCapability;
@@ -50,23 +50,22 @@ import com.rogiel.httpchannel.service.UploaderCapability;
import com.rogiel.httpchannel.service.channel.InputStreamDownloadChannel; import com.rogiel.httpchannel.service.channel.InputStreamDownloadChannel;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel; import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback; import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannelContentBody; import com.rogiel.httpchannel.service.config.NullAuthenticatorConfiguration;
import com.rogiel.httpchannel.service.config.ServiceConfiguration; import com.rogiel.httpchannel.service.config.NullDownloaderConfiguration;
import com.rogiel.httpchannel.service.config.ServiceConfigurationHelper; import com.rogiel.httpchannel.service.config.NullUploaderConfiguration;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException; import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.service.impl.HotFileService.HotFileServiceConfiguration;
import com.rogiel.httpchannel.util.ThreadUtils;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage; import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
/** /**
* This service handles login, upload and download to HotFile.com. * This service handles login, upload and download to HotFile.com.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public class HotFileService extends public class HotFileService extends AbstractHttpService implements Service,
AbstractHttpService<HotFileServiceConfiguration> implements Service, UploadService<NullUploaderConfiguration>,
UploadService, DownloadService, AuthenticationService { DownloadService<NullDownloaderConfiguration>,
AuthenticationService<NullAuthenticatorConfiguration> {
/** /**
* This service ID * This service ID
*/ */
@@ -85,11 +84,6 @@ public class HotFileService extends
private static final Pattern DOWNLOAD_URL_PATTERN = Pattern private static final Pattern DOWNLOAD_URL_PATTERN = Pattern
.compile("http://hotfile\\.com/dl/([0-9]*)/([A-Za-z0-9]*)/(.*)"); .compile("http://hotfile\\.com/dl/([0-9]*)/([A-Za-z0-9]*)/(.*)");
public HotFileService() {
super(ServiceConfigurationHelper
.defaultConfiguration(HotFileServiceConfiguration.class));
}
@Override @Override
public ServiceID getID() { public ServiceID getID() {
return SERVICE_ID; return SERVICE_ID;
@@ -106,9 +100,20 @@ public class HotFileService extends
} }
@Override @Override
public Uploader getUploader(String filename, long filesize, public Uploader<NullUploaderConfiguration> getUploader(String filename,
String description) { long filesize, NullUploaderConfiguration configuration) {
return new HotFileUploader(filename, filesize); return new HotFileUploader(filename, filesize, configuration);
}
@Override
public Uploader<NullUploaderConfiguration> getUploader(String filename,
long filesize) {
return getUploader(filename, filesize, newUploaderConfiguration());
}
@Override
public NullUploaderConfiguration newUploaderConfiguration() {
return NullUploaderConfiguration.SHARED_INSTANCE;
} }
@Override @Override
@@ -130,8 +135,19 @@ public class HotFileService extends
} }
@Override @Override
public Downloader getDownloader(URL url) { public Downloader<NullDownloaderConfiguration> getDownloader(URL url,
return new HotFileDownloader(url); NullDownloaderConfiguration configuration) {
return new HotFileDownloader(url, configuration);
}
@Override
public Downloader<NullDownloaderConfiguration> getDownloader(URL url) {
return getDownloader(url, newDownloaderConfiguration());
}
@Override
public NullDownloaderConfiguration newDownloaderConfiguration() {
return NullDownloaderConfiguration.SHARED_INSTANCE;
} }
@Override @Override
@@ -146,8 +162,20 @@ public class HotFileService extends
} }
@Override @Override
public Authenticator getAuthenticator(Credential credential) { public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
return new HotFileAuthenticator(credential); Credential credential, NullAuthenticatorConfiguration configuration) {
return new HotFileAuthenticator(credential, configuration);
}
@Override
public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
Credential credential) {
return getAuthenticator(credential, newAuthenticatorConfiguration());
}
@Override
public NullAuthenticatorConfiguration newAuthenticatorConfiguration() {
return NullAuthenticatorConfiguration.SHARED_INSTANCE;
} }
@Override @Override
@@ -155,36 +183,27 @@ public class HotFileService extends
return new CapabilityMatrix<AuthenticatorCapability>(); return new CapabilityMatrix<AuthenticatorCapability>();
} }
protected class HotFileUploader implements Uploader, protected class HotFileUploader extends
AbstractUploader<NullUploaderConfiguration> implements
Uploader<NullUploaderConfiguration>,
LinkedUploadChannelCloseCallback { LinkedUploadChannelCloseCallback {
private final String filename;
private final long filesize;
private Future<HTMLPage> uploadFuture; private Future<HTMLPage> uploadFuture;
public HotFileUploader(String filename, long filesize) { public HotFileUploader(String filename, long filesize,
super(); NullUploaderConfiguration configuration) {
this.filename = filename; super(filename, filesize, configuration);
this.filesize = filesize;
} }
@Override @Override
public UploadChannel upload() throws IOException { public UploadChannel openChannel() throws IOException {
final HTMLPage page = getAsPage("http://www.hotfile.com/"); final HTMLPage page = get("http://www.hotfile.com/").asPage();
final String action = page.getFormAction(UPLOAD_URL_PATTERN); final String action = page.findFormAction(UPLOAD_URL_PATTERN);
final LinkedUploadChannel channel = new LinkedUploadChannel(this, final LinkedUploadChannel channel = createLinkedChannel(this);
filesize, filename);
final MultipartEntity entity = new MultipartEntity();
entity.addPart("uploads[]", new LinkedUploadChannelContentBody( uploadFuture = multipartPost(action)
channel)); .parameter("uploads[]", channel).asPageAsync();
return waitChannelLink(channel, uploadFuture);
uploadFuture = postAsPageAsync(action, entity);
while (!channel.isLinked() && !uploadFuture.isDone()) {
ThreadUtils.sleep(100);
}
return channel;
} }
@Override @Override
@@ -199,17 +218,17 @@ public class HotFileService extends
} }
} }
protected class HotFileDownloader extends AbstractDownloader { protected class HotFileDownloader extends
private final URL url; AbstractHttpDownloader<NullDownloaderConfiguration> {
public HotFileDownloader(URL url,
public HotFileDownloader(URL url) { NullDownloaderConfiguration configuration) {
this.url = url; super(url, configuration);
} }
@Override @Override
public DownloadChannel download(DownloadListener listener, long position) public DownloadChannel openChannel(DownloadListener listener,
throws IOException { long position) throws IOException {
final HTMLPage page = getAsPage(url.toString()); final HTMLPage page = get(url).asPage();
// // try to find timer // // try to find timer
// final String stringTimer = PatternUtils.find(DOWNLOAD_TIMER, // final String stringTimer = PatternUtils.find(DOWNLOAD_TIMER,
@@ -224,11 +243,12 @@ public class HotFileService extends
// } // }
final String downloadUrl = page final String downloadUrl = page
.getLink(DOWNLOAD_DIRECT_LINK_PATTERN); .findLink(DOWNLOAD_DIRECT_LINK_PATTERN);
// final String tmHash = PatternUtils.find(DOWNLOAD_TMHASH_PATTERN, // final String tmHash = PatternUtils.find(DOWNLOAD_TMHASH_PATTERN,
// content);F // content);F
if (downloadUrl != null && downloadUrl.length() > 0) { if (downloadUrl != null && downloadUrl.length() > 0) {
final HttpResponse downloadResponse = get(downloadUrl); final HttpResponse downloadResponse = get(downloadUrl)
.request();
final String filename = FilenameUtils.getName(downloadUrl); final String filename = FilenameUtils.getName(downloadUrl);
long contentLength = getContentLength(downloadResponse); long contentLength = getContentLength(downloadResponse);
@@ -284,23 +304,21 @@ public class HotFileService extends
} }
} }
protected class HotFileAuthenticator implements Authenticator { protected class HotFileAuthenticator extends
private final Credential credential; AbstractAuthenticator<NullAuthenticatorConfiguration> implements
Authenticator<NullAuthenticatorConfiguration> {
public HotFileAuthenticator(Credential credential) { public HotFileAuthenticator(Credential credential,
this.credential = credential; NullAuthenticatorConfiguration configuration) {
super(credential, configuration);
} }
@Override @Override
public void login() throws ClientProtocolException, IOException { public void login() throws ClientProtocolException, IOException {
final MultipartEntity entity = new MultipartEntity(); HTMLPage page = post("http://www.hotfile.com/login.php")
.parameter("returnto", "/index.php")
.parameter("user", credential.getUsername())
.parameter("pass", credential.getPassword()).asPage();
entity.addPart("returnto", new StringBody("/index.php"));
entity.addPart("user", new StringBody(credential.getUsername()));
entity.addPart("pass", new StringBody(credential.getPassword()));
HTMLPage page = postAsPage("http://www.hotfile.com/login.php",
entity);
final Tag accountTag = page.getTagByID("account"); final Tag accountTag = page.getTagByID("account");
if (accountTag == null) if (accountTag == null)
throw new AuthenticationInvalidCredentialException(); throw new AuthenticationInvalidCredentialException();
@@ -308,18 +326,12 @@ public class HotFileService extends
@Override @Override
public void logout() throws IOException { public void logout() throws IOException {
final MultipartEntity entity = new MultipartEntity(); post("http://www.megaupload.com/?c=account").parameter("logout",
entity.addPart("logout", new StringBody("1")); true).request();
postAsString("http://www.megaupload.com/?c=account", entity);
// TODO check logout status // TODO check logout status
} }
} }
public static interface HotFileServiceConfiguration extends
ServiceConfiguration {
}
@Override @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + " " + getMajorVersion() + "." return this.getClass().getSimpleName() + " " + getMajorVersion() + "."

View File

@@ -20,7 +20,7 @@ import junit.framework.Assert;
import org.junit.Test; import org.junit.Test;
import com.rogiel.httpchannel.service.Services; import com.rogiel.httpchannel.service.helper.Services;
/** /**
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>

View File

@@ -22,8 +22,6 @@ import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.SeekableByteChannel; import java.nio.channels.SeekableByteChannel;
@@ -38,23 +36,19 @@ import org.apache.commons.io.IOUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Credential; import com.rogiel.httpchannel.service.Credential;
import com.rogiel.httpchannel.service.DownloadChannel; import com.rogiel.httpchannel.service.DownloadChannel;
import com.rogiel.httpchannel.service.DownloadService; import com.rogiel.httpchannel.service.DownloadService;
import com.rogiel.httpchannel.service.Service;
import com.rogiel.httpchannel.service.ServiceHelper;
import com.rogiel.httpchannel.service.ServiceID; import com.rogiel.httpchannel.service.ServiceID;
import com.rogiel.httpchannel.service.Services;
import com.rogiel.httpchannel.service.UploadChannel; import com.rogiel.httpchannel.service.UploadChannel;
import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.UploaderCapability; import com.rogiel.httpchannel.service.UploaderCapability;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException; import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.service.helper.Services;
import com.rogiel.httpchannel.service.helper.UploadServices;
import com.rogiel.httpchannel.util.ChannelUtils; import com.rogiel.httpchannel.util.ChannelUtils;
public class HotFileServiceTest { public class HotFileServiceTest {
private Service service; private HotFileService service;
private ServiceHelper helper;
/** /**
* See <b>src/test/resources/config/hotfile.properties</b> * See <b>src/test/resources/config/hotfile.properties</b>
@@ -76,7 +70,6 @@ public class HotFileServiceTest {
public void setUp() throws Exception { public void setUp() throws Exception {
// MegaUploadServiceConfiguration.class; // MegaUploadServiceConfiguration.class;
service = new HotFileService(); service = new HotFileService();
helper = new ServiceHelper(service);
final Properties properties = new Properties(); final Properties properties = new Properties();
properties.load(new FileInputStream( properties.load(new FileInputStream(
@@ -87,33 +80,31 @@ public class HotFileServiceTest {
@Test @Test
public void testServiceId() { public void testServiceId() {
System.out.println("Service: " + service.toString());
assertEquals(ServiceID.create("hotfile"), service.getID()); assertEquals(ServiceID.create("hotfile"), service.getID());
} }
@Test @Test
public void testValidAuthenticator() throws IOException { public void testValidAuthenticator() throws IOException {
((AuthenticationService) service).getAuthenticator( service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD))
new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); .login();
} }
@Test(expected = AuthenticationInvalidCredentialException.class) @Test(expected = AuthenticationInvalidCredentialException.class)
public void testInvalidAuthenticator() throws IOException { public void testInvalidAuthenticator() throws IOException {
((AuthenticationService) service).getAuthenticator( service.getAuthenticator(
new Credential(INVALID_USERNAME, INVALID_PASSWORD)).login(); new Credential(INVALID_USERNAME, INVALID_PASSWORD)).login();
} }
@Test @Test
public void testNonLoguedInUploader() throws IOException, public void testNonLoguedInUploader() throws IOException {
URISyntaxException {
assertTrue( assertTrue(
"This service does not have the capability UploadCapability.FREE_UPLOAD", "This service does not have the capability UploadCapability.FREE_UPLOAD",
((UploadService) service).getUploadCapabilities().has( service.getUploadCapabilities().has(
UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD)); UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD));
final Path path = Paths.get("src/test/resources/upload-test-file.txt"); final Path path = Paths.get("src/test/resources/upload-test-file.txt");
final UploadChannel channel = helper.upload(path, final UploadChannel channel = UploadServices.upload(service, path)
"httpchannel test upload"); .openChannel();
final SeekableByteChannel inChannel = Files.newByteChannel(path); final SeekableByteChannel inChannel = Files.newByteChannel(path);
try { try {
@@ -131,15 +122,15 @@ public class HotFileServiceTest {
public void testLoguedInUploader() throws IOException { public void testLoguedInUploader() throws IOException {
assertTrue( assertTrue(
"This service does not have the capability UploadCapability.PREMIUM_UPLOAD", "This service does not have the capability UploadCapability.PREMIUM_UPLOAD",
((UploadService) service).getUploadCapabilities().has( service.getUploadCapabilities().has(
UploaderCapability.PREMIUM_ACCOUNT_UPLOAD)); UploaderCapability.PREMIUM_ACCOUNT_UPLOAD));
((AuthenticationService) service).getAuthenticator( service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD))
new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); .login();
final Path path = Paths.get("src/test/resources/upload-test-file.txt"); final Path path = Paths.get("src/test/resources/upload-test-file.txt");
final UploadChannel channel = helper.upload(path, final UploadChannel channel = UploadServices.upload(service, path)
"httpchannel test upload"); .openChannel();
final SeekableByteChannel inChannel = Files.newByteChannel(path); final SeekableByteChannel inChannel = Files.newByteChannel(path);
try { try {
@@ -154,30 +145,29 @@ public class HotFileServiceTest {
} }
@Test @Test
public void testDownloader() throws IOException, MalformedURLException { public void testDownloader() throws IOException {
final URL downloadUrl = new URL( final URL downloadUrl = new URL(
"http://hotfile.com/dl/129251605/9b4faf2/simulado_2010_1_res_all.zip.htm"); "http://hotfile.com/dl/129251605/9b4faf2/simulado_2010_1_res_all.zip.htm");
final DownloadService service = Services.matchURL(downloadUrl); final DownloadService<?> service = Services.matchURL(downloadUrl);
final DownloadChannel channel = service.getDownloader(downloadUrl) final DownloadChannel channel = service.getDownloader(downloadUrl)
.download(null, 0); .openChannel(null, 0);
final ByteArrayOutputStream bout = new ByteArrayOutputStream(); final ByteArrayOutputStream bout = new ByteArrayOutputStream();
IOUtils.copy(Channels.newInputStream(channel), bout); IOUtils.copy(Channels.newInputStream(channel), bout);
System.out.println(bout.size()); System.out.println(bout.size());
} }
@Test @Test
public void testLoggedInDownloader() throws IOException, public void testLoggedInDownloader() throws IOException {
MalformedURLException { service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD))
((AuthenticationService) service).getAuthenticator( .login();
new Credential(VALID_USERNAME, VALID_PASSWORD)).login();
final DownloadChannel channel = ((DownloadService) service) final DownloadChannel channel = service
.getDownloader( .getDownloader(
new URL( new URL(
"http://hotfile.com/dl/129251605/9b4faf2/simulado_2010_1_res_all.zip.html")) "http://hotfile.com/dl/129251605/9b4faf2/simulado_2010_1_res_all.zip.html"))
.download(null, 0); .openChannel(null, 0);
final ByteArrayOutputStream bout = new ByteArrayOutputStream(); final ByteArrayOutputStream bout = new ByteArrayOutputStream();
IOUtils.copy(Channels.newInputStream(channel), bout); IOUtils.copy(Channels.newInputStream(channel), bout);

View File

@@ -1,41 +0,0 @@
/*
* 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 org.junit.Assert;
import org.junit.Test;
import com.rogiel.httpchannel.service.Service;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class ServiceCloningTest {
@Test
public void testDiscovery() {
Service original = HotFileService.SERVICE_ID.getService();
Service service = HotFileService.SERVICE_ID.getService().clone();
// set configuration to anything else
service.setServiceConfiguration(null);
Assert.assertNotSame(service, original);
Assert.assertNotSame(service.getServiceConfiguration(),
original.getServiceConfiguration());
}
}

View File

@@ -7,4 +7,7 @@
<relativePath>..</relativePath> <relativePath>..</relativePath>
</parent> </parent>
<artifactId>httpchannel-service-megaupload</artifactId> <artifactId>httpchannel-service-megaupload</artifactId>
<groupId>com.rogiel.httpchannel.services</groupId>
<name>HttpChannel/Service/MegaUpload</name>
<description>Provides upload and download access to megaupload.com</description>
</project> </project>

View File

@@ -0,0 +1,16 @@
package com.rogiel.httpchannel.service.impl;
import com.rogiel.httpchannel.service.Downloader.DownloaderConfiguration;
public class MegaUploadDownloaderConfiguration implements
DownloaderConfiguration {
private boolean respectWaitTime = true;
public boolean getRespectWaitTime() {
return respectWaitTime;
}
public void setRespectWaitTime(boolean respectWaitTime) {
this.respectWaitTime = respectWaitTime;
}
}

View File

@@ -18,8 +18,6 @@ package com.rogiel.httpchannel.service.impl;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -27,14 +25,11 @@ import java.util.regex.Pattern;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.message.BasicNameValuePair;
import com.rogiel.httpchannel.service.AbstractDownloader; import com.rogiel.httpchannel.service.AbstractAuthenticator;
import com.rogiel.httpchannel.service.AbstractHttpDownloader;
import com.rogiel.httpchannel.service.AbstractHttpService; import com.rogiel.httpchannel.service.AbstractHttpService;
import com.rogiel.httpchannel.service.AbstractUploader;
import com.rogiel.httpchannel.service.AuthenticationService; import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Authenticator; import com.rogiel.httpchannel.service.Authenticator;
import com.rogiel.httpchannel.service.AuthenticatorCapability; import com.rogiel.httpchannel.service.AuthenticatorCapability;
@@ -51,32 +46,26 @@ import com.rogiel.httpchannel.service.UploadChannel;
import com.rogiel.httpchannel.service.UploadService; import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.Uploader; import com.rogiel.httpchannel.service.Uploader;
import com.rogiel.httpchannel.service.UploaderCapability; 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;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback; import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannelContentBody; import com.rogiel.httpchannel.service.config.NullAuthenticatorConfiguration;
import com.rogiel.httpchannel.service.config.ServiceConfiguration;
import com.rogiel.httpchannel.service.config.ServiceConfigurationHelper;
import com.rogiel.httpchannel.service.config.ServiceConfigurationProperty;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException; import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.service.exception.DownloadLimitExceededException; import com.rogiel.httpchannel.service.exception.DownloadLimitExceededException;
import com.rogiel.httpchannel.service.exception.DownloadLinkNotFoundException; import com.rogiel.httpchannel.service.exception.DownloadLinkNotFoundException;
import com.rogiel.httpchannel.service.exception.UploadLinkNotFoundException;
import com.rogiel.httpchannel.service.impl.MegaUploadService.MegaUploadServiceConfiguration;
import com.rogiel.httpchannel.util.HttpClientUtils; import com.rogiel.httpchannel.util.HttpClientUtils;
import com.rogiel.httpchannel.util.PatternUtils; import com.rogiel.httpchannel.util.PatternUtils;
import com.rogiel.httpchannel.util.ThreadUtils;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage; import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
/** /**
* This service handles login, upload and download to MegaUpload.com. * This service handles login, upload and download to MegaUpload.com.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public class MegaUploadService extends public class MegaUploadService extends AbstractHttpService implements Service,
AbstractHttpService<MegaUploadServiceConfiguration> implements Service, UploadService<MegaUploadUploaderConfiguration>,
UploadService, DownloadService, AuthenticationService { DownloadService<MegaUploadDownloaderConfiguration>,
AuthenticationService<NullAuthenticatorConfiguration> {
/** /**
* This service ID * This service ID
*/ */
@@ -98,11 +87,6 @@ public class MegaUploadService extends
private static final Pattern LOGIN_USERNAME_PATTERN = Pattern private static final Pattern LOGIN_USERNAME_PATTERN = Pattern
.compile("flashvars\\.username = \"(.*)\";"); .compile("flashvars\\.username = \"(.*)\";");
public MegaUploadService() {
super(ServiceConfigurationHelper
.defaultConfiguration(MegaUploadServiceConfiguration.class));
}
@Override @Override
public ServiceID getID() { public ServiceID getID() {
return SERVICE_ID; return SERVICE_ID;
@@ -119,9 +103,21 @@ public class MegaUploadService extends
} }
@Override @Override
public Uploader getUploader(String filename, long filesize, public Uploader<MegaUploadUploaderConfiguration> getUploader(
String description) { String filename, long filesize,
return new MegaUploadUploader(filename, filesize, description); MegaUploadUploaderConfiguration configuration) {
return new MegaUploadUploader(filename, filesize, configuration);
}
@Override
public Uploader<MegaUploadUploaderConfiguration> getUploader(
String filename, long filesize) {
return getUploader(filename, filesize, newUploaderConfiguration());
}
@Override
public MegaUploadUploaderConfiguration newUploaderConfiguration() {
return new MegaUploadUploaderConfiguration();
} }
@Override @Override
@@ -143,8 +139,19 @@ public class MegaUploadService extends
} }
@Override @Override
public Downloader getDownloader(URL url) { public Downloader<MegaUploadDownloaderConfiguration> getDownloader(URL url,
return new MegaUploadDownloader(url); MegaUploadDownloaderConfiguration configuration) {
return new MegaUploadDownloader(url, configuration);
}
@Override
public Downloader<MegaUploadDownloaderConfiguration> getDownloader(URL url) {
return getDownloader(url, newDownloaderConfiguration());
}
@Override
public MegaUploadDownloaderConfiguration newDownloaderConfiguration() {
return new MegaUploadDownloaderConfiguration();
} }
@Override @Override
@@ -156,13 +163,28 @@ public class MegaUploadService extends
public CapabilityMatrix<DownloaderCapability> getDownloadCapabilities() { public CapabilityMatrix<DownloaderCapability> getDownloadCapabilities() {
return new CapabilityMatrix<DownloaderCapability>( return new CapabilityMatrix<DownloaderCapability>(
DownloaderCapability.UNAUTHENTICATED_DOWNLOAD, DownloaderCapability.UNAUTHENTICATED_DOWNLOAD,
DownloaderCapability.UNAUTHENTICATED_RESUME,
DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD, DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD,
DownloaderCapability.PREMIUM_ACCOUNT_DOWNLOAD); DownloaderCapability.NON_PREMIUM_ACCOUNT_RESUME,
DownloaderCapability.PREMIUM_ACCOUNT_DOWNLOAD,
DownloaderCapability.PREMIUM_ACCOUNT_RESUME);
} }
@Override @Override
public Authenticator getAuthenticator(Credential credential) { public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
return new MegaUploadAuthenticator(credential); Credential credential, NullAuthenticatorConfiguration configuration) {
return new MegaUploadAuthenticator(credential, configuration);
}
@Override
public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
Credential credential) {
return getAuthenticator(credential, newAuthenticatorConfiguration());
}
@Override
public NullAuthenticatorConfiguration newAuthenticatorConfiguration() {
return NullAuthenticatorConfiguration.SHARED_INSTANCE;
} }
@Override @Override
@@ -170,50 +192,35 @@ public class MegaUploadService extends
return new CapabilityMatrix<AuthenticatorCapability>(); return new CapabilityMatrix<AuthenticatorCapability>();
} }
protected class MegaUploadUploader implements Uploader, protected class MegaUploadUploader extends
AbstractUploader<MegaUploadUploaderConfiguration> implements
Uploader<MegaUploadUploaderConfiguration>,
LinkedUploadChannelCloseCallback { LinkedUploadChannelCloseCallback {
private final String filename;
private final long filesize;
private final String description;
private Future<String> uploadFuture; private Future<String> uploadFuture;
public MegaUploadUploader(String filename, long filesize, public MegaUploadUploader(String filename, long filesize,
String description) { MegaUploadUploaderConfiguration configuration) {
this.filename = filename; super(filename, filesize, configuration);
this.filesize = filesize;
this.description = (description != null ? description
: configuration.getDefaultUploadDescription());
} }
@Override @Override
public UploadChannel upload() throws IOException { public UploadChannel openChannel() throws IOException {
final HTMLPage page = getAsPage("http://www.megaupload.com/multiupload/"); final HTMLPage page = get("http://www.megaupload.com/multiupload/")
final String url = page.getFormAction(UPLOAD_URL_PATTERN); .asPage();
final String url = page.findFormAction(UPLOAD_URL_PATTERN);
final LinkedUploadChannel channel = new LinkedUploadChannel(this, final LinkedUploadChannel channel = createLinkedChannel(this);
filesize, filename); uploadFuture = multipartPost(url)
final MultipartEntity entity = new MultipartEntity(); .parameter("multimessage_0", configuration.description())
.parameter("multifile_0", channel).asStringAsync();
entity.addPart("multifile_0", new LinkedUploadChannelContentBody( return waitChannelLink(channel, uploadFuture);
channel));
entity.addPart("multimessage_0", new StringBody(description));
uploadFuture = postAsStringAsync(url, entity);
while (!channel.isLinked() && !uploadFuture.isDone()) {
ThreadUtils.sleep(100);
}
return channel;
} }
@Override @Override
public String finish() throws IOException { public String finish() throws IOException {
try { try {
String link = PatternUtils.find(DOWNLOAD_URL_PATTERN, return PatternUtils.find(DOWNLOAD_URL_PATTERN,
uploadFuture.get()); uploadFuture.get());
if (link == null)
throw new UploadLinkNotFoundException();
return link;
} catch (InterruptedException e) { } catch (InterruptedException e) {
return null; return null;
} catch (ExecutionException e) { } catch (ExecutionException e) {
@@ -222,17 +229,18 @@ public class MegaUploadService extends
} }
} }
protected class MegaUploadDownloader extends AbstractDownloader { protected class MegaUploadDownloader extends
private final URL url; AbstractHttpDownloader<MegaUploadDownloaderConfiguration> implements
Downloader<MegaUploadDownloaderConfiguration> {
public MegaUploadDownloader(URL url) { public MegaUploadDownloader(URL url,
this.url = url; MegaUploadDownloaderConfiguration configuration) {
super(url, configuration);
} }
@Override @Override
public DownloadChannel download(DownloadListener listener, long position) public DownloadChannel openChannel(DownloadListener listener,
throws IOException { long position) throws IOException {
HttpResponse response = get(url.toString()); HttpResponse response = get(url).request();
// disable direct downloads, we don't support them! // disable direct downloads, we don't support them!
if (response.getEntity().getContentType().getValue() if (response.getEntity().getContentType().getValue()
@@ -240,30 +248,28 @@ public class MegaUploadService extends
// close connection // close connection
response.getEntity().getContent().close(); response.getEntity().getContent().close();
final List<NameValuePair> pairs = new ArrayList<NameValuePair>();
pairs.add(new BasicNameValuePair("do", "directdownloads"));
pairs.add(new BasicNameValuePair("accountupdate", "1"));
pairs.add(new BasicNameValuePair("set_ddl", "0"));
// execute update // execute update
postAsString("http://www.megaupload.com/?c=account", post("http://www.megaupload.com/?c=account")
new UrlEncodedFormEntity(pairs)); .parameter("do", "directdownloads")
.parameter("accountupdate", "1")
.parameter("set_ddl", "0").request();
// execute and re-request download // execute and re-request download
response = get(url.toString()); response = get(url).request();
} }
final HTMLPage page = HttpClientUtils.toPage(response); final HTMLPage page = HttpClientUtils.toPage(response);
// try to find timer // try to find timer
int timer = page.findIntegerInScript(DOWNLOAD_TIMER, 1); int timer = page.findScriptAsInt(DOWNLOAD_TIMER, 1);
if (timer > 0 && configuration.respectWaitTime()) { if (timer > 0 && configuration.getRespectWaitTime()) {
timer(listener, timer * 1000); timer(listener, timer * 1000);
} }
final String downloadUrl = page final String downloadUrl = page
.getLink(DOWNLOAD_DIRECT_LINK_PATTERN); .findLink(DOWNLOAD_DIRECT_LINK_PATTERN);
if (downloadUrl != null && downloadUrl.length() > 0) { if (downloadUrl != null && downloadUrl.length() > 0) {
final HttpResponse downloadResponse = get(downloadUrl, position); final HttpResponse downloadResponse = get(downloadUrl)
.position(position).request();
if (downloadResponse.getStatusLine().getStatusCode() == HttpStatus.SC_FORBIDDEN if (downloadResponse.getStatusLine().getStatusCode() == HttpStatus.SC_FORBIDDEN
|| downloadResponse.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) { || downloadResponse.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) {
downloadResponse.getEntity().getContent().close(); downloadResponse.getEntity().getContent().close();
@@ -274,7 +280,7 @@ public class MegaUploadService extends
final String filename = FilenameUtils.getName(downloadUrl); final String filename = FilenameUtils.getName(downloadUrl);
final long contentLength = getContentLength(downloadResponse); final long contentLength = getContentLength(downloadResponse);
return new InputStreamDownloadChannel(downloadResponse return createInputStreamChannel(downloadResponse
.getEntity().getContent(), contentLength, filename); .getEntity().getContent(), contentLength, filename);
} }
} else { } else {
@@ -283,51 +289,34 @@ public class MegaUploadService extends
} }
} }
protected class MegaUploadAuthenticator implements Authenticator { protected class MegaUploadAuthenticator extends
private final Credential credential; AbstractAuthenticator<NullAuthenticatorConfiguration> implements
Authenticator<NullAuthenticatorConfiguration> {
public MegaUploadAuthenticator(Credential credential) { public MegaUploadAuthenticator(Credential credential,
this.credential = credential; NullAuthenticatorConfiguration configuration) {
super(credential, configuration);
} }
@Override @Override
public void login() throws IOException { public void login() throws IOException {
final MultipartEntity entity = new MultipartEntity(); final HTMLPage page = post("http://www.megaupload.com/?c=login")
.parameter("login", true)
entity.addPart("login", new StringBody("1")); .parameter("username", credential.getUsername())
entity.addPart("username", new StringBody(credential.getUsername())); .parameter("", credential.getPassword()).asPage();
entity.addPart("password", new StringBody(credential.getPassword()));
final HTMLPage page = postAsPage(
"http://www.megaupload.com/?c=login", entity);
String username = page.findInScript(LOGIN_USERNAME_PATTERN, 1);
String username = page.findScript(LOGIN_USERNAME_PATTERN, 1);
if (username == null) if (username == null)
throw new AuthenticationInvalidCredentialException(); throw new AuthenticationInvalidCredentialException();
} }
@Override @Override
public void logout() throws IOException { public void logout() throws IOException {
final MultipartEntity entity = new MultipartEntity(); post("http://www.megaupload.com/?c=account").parameter("logout",
entity.addPart("logout", new StringBody("1")); true).request();
postAsString("http://www.megaupload.com/?c=account", entity);
// TODO check logout status // TODO check logout status
} }
} }
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 @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + " " + getMajorVersion() + "." return this.getClass().getSimpleName() + " " + getMajorVersion() + "."

View File

@@ -0,0 +1,29 @@
package com.rogiel.httpchannel.service.impl;
import com.rogiel.httpchannel.service.Uploader.DescriptionableUploaderConfiguration;
import com.rogiel.httpchannel.service.Uploader.UploaderConfiguration;
import com.rogiel.httpchannel.service.impl.MegaUploadService.MegaUploadUploader;
/**
* Describes an configuration for an {@link MegaUploadUploader}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class MegaUploadUploaderConfiguration implements UploaderConfiguration,
DescriptionableUploaderConfiguration {
/**
* The upload description
*/
private String description = DescriptionableUploaderConfiguration.DEFAULT_DESCRIPTION;
@Override
public String description() {
return description;
}
@Override
public MegaUploadUploaderConfiguration description(String description) {
this.description = description;
return this;
}
}

View File

@@ -20,7 +20,7 @@ import junit.framework.Assert;
import org.junit.Test; import org.junit.Test;
import com.rogiel.httpchannel.service.Services; import com.rogiel.httpchannel.service.helper.Services;
/** /**
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>

View File

@@ -20,10 +20,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.SeekableByteChannel; import java.nio.channels.SeekableByteChannel;
@@ -38,25 +36,18 @@ import org.apache.commons.io.IOUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Credential; import com.rogiel.httpchannel.service.Credential;
import com.rogiel.httpchannel.service.DownloadChannel; import com.rogiel.httpchannel.service.DownloadChannel;
import com.rogiel.httpchannel.service.DownloadListener; import com.rogiel.httpchannel.service.DownloadListener;
import com.rogiel.httpchannel.service.DownloadService;
import com.rogiel.httpchannel.service.Service;
import com.rogiel.httpchannel.service.ServiceHelper;
import com.rogiel.httpchannel.service.ServiceID; import com.rogiel.httpchannel.service.ServiceID;
import com.rogiel.httpchannel.service.UploadChannel; import com.rogiel.httpchannel.service.UploadChannel;
import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.UploaderCapability; import com.rogiel.httpchannel.service.UploaderCapability;
import com.rogiel.httpchannel.service.config.ServiceConfigurationHelper;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException; import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.service.impl.MegaUploadService.MegaUploadServiceConfiguration; import com.rogiel.httpchannel.service.helper.UploadServices;
import com.rogiel.httpchannel.util.ChannelUtils; import com.rogiel.httpchannel.util.ChannelUtils;
public class MegaUploadServiceTest { public class MegaUploadServiceTest {
private Service service; private MegaUploadService service;
private ServiceHelper helper;
/** /**
* See <b>src/test/resources/config/megaupload.properties</b> * See <b>src/test/resources/config/megaupload.properties</b>
@@ -78,7 +69,6 @@ public class MegaUploadServiceTest {
public void setUp() throws Exception { public void setUp() throws Exception {
// MegaUploadServiceConfiguration.class; // MegaUploadServiceConfiguration.class;
service = new MegaUploadService(); service = new MegaUploadService();
helper = new ServiceHelper(service);
final Properties properties = new Properties(); final Properties properties = new Properties();
properties.load(new FileInputStream( properties.load(new FileInputStream(
@@ -89,19 +79,18 @@ public class MegaUploadServiceTest {
@Test @Test
public void testServiceId() { public void testServiceId() {
System.out.println("Service: " + service.toString());
assertEquals(ServiceID.create("megaupload"), service.getID()); assertEquals(ServiceID.create("megaupload"), service.getID());
} }
@Test @Test
public void testValidAuthenticator() throws IOException { public void testValidAuthenticator() throws IOException {
((AuthenticationService) service).getAuthenticator( service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD))
new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); .login();
} }
@Test(expected = AuthenticationInvalidCredentialException.class) @Test(expected = AuthenticationInvalidCredentialException.class)
public void testInvalidAuthenticator() throws IOException { public void testInvalidAuthenticator() throws IOException {
((AuthenticationService) service).getAuthenticator( service.getAuthenticator(
new Credential(INVALID_USERNAME, INVALID_PASSWORD)).login(); new Credential(INVALID_USERNAME, INVALID_PASSWORD)).login();
} }
@@ -109,11 +98,11 @@ public class MegaUploadServiceTest {
public void testNonLoguedInUploader() throws IOException { public void testNonLoguedInUploader() throws IOException {
assertTrue( assertTrue(
"This service does not have the capability UploadCapability.FREE_UPLOAD", "This service does not have the capability UploadCapability.FREE_UPLOAD",
((UploadService) service).getUploadCapabilities().has( service.getUploadCapabilities().has(
UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD)); UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD));
final Path path = Paths.get("src/test/resources/upload-test-file.txt"); final Path path = Paths.get("src/test/resources/upload-test-file.txt");
final UploadChannel channel = helper.upload(path, final UploadChannel channel = UploadServices.upload(service, path)
"httpchannel test upload"); .openChannel();
final SeekableByteChannel inChannel = Files.newByteChannel(path); final SeekableByteChannel inChannel = Files.newByteChannel(path);
try { try {
@@ -131,15 +120,15 @@ public class MegaUploadServiceTest {
public void testLoguedInUploader() throws IOException { public void testLoguedInUploader() throws IOException {
assertTrue( assertTrue(
"This service does not have the capability UploadCapability.PREMIUM_UPLOAD", "This service does not have the capability UploadCapability.PREMIUM_UPLOAD",
((UploadService) service).getUploadCapabilities().has( service.getUploadCapabilities().has(
UploaderCapability.PREMIUM_ACCOUNT_UPLOAD)); UploaderCapability.PREMIUM_ACCOUNT_UPLOAD));
((AuthenticationService) service).getAuthenticator( service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD))
new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); .login();
final Path path = Paths.get("src/test/resources/upload-test-file.txt"); final Path path = Paths.get("src/test/resources/upload-test-file.txt");
final UploadChannel channel = helper.upload(path, final UploadChannel channel = UploadServices.upload(service, path)
"httpchannel test upload"); .openChannel();
final SeekableByteChannel inChannel = Files.newByteChannel(path); final SeekableByteChannel inChannel = Files.newByteChannel(path);
try { try {
@@ -154,10 +143,10 @@ public class MegaUploadServiceTest {
} }
@Test @Test
public void testFreeDownloader() throws IOException, MalformedURLException { public void testFreeDownloader() throws IOException {
final DownloadChannel channel = ((DownloadService) service) final DownloadChannel channel = service.getDownloader(
.getDownloader(new URL("http://www.megaupload.com/?d=CVQKJ1KM")) new URL("http://www.megaupload.com/?d=CVQKJ1KM")).openChannel(
.download(new DownloadListener() { new DownloadListener() {
@Override @Override
public boolean timer(long time) { public boolean timer(long time) {
System.out.println("Waiting " + time); System.out.println("Waiting " + time);
@@ -172,14 +161,13 @@ public class MegaUploadServiceTest {
} }
@Test @Test
public void testPremiumDownloader() throws IOException, public void testPremiumDownloader() throws IOException {
MalformedURLException { service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD))
((AuthenticationService) service).getAuthenticator( .login();
new Credential(VALID_USERNAME, VALID_PASSWORD)).login();
final DownloadChannel channel = ((DownloadService) service) final DownloadChannel channel = service.getDownloader(
.getDownloader(new URL("http://www.megaupload.com/?d=CVQKJ1KM")) new URL("http://www.megaupload.com/?d=CVQKJ1KM")).openChannel(
.download(new DownloadListener() { new DownloadListener() {
@Override @Override
public boolean timer(long time) { public boolean timer(long time) {
System.out.println("Waiting " + time); System.out.println("Waiting " + time);
@@ -192,17 +180,18 @@ public class MegaUploadServiceTest {
} }
@Test @Test
public void testNoWaitDownloader() throws IOException, public void testNoWaitDownloader() throws IOException {
MalformedURLException {
service = new MegaUploadService(); service = new MegaUploadService();
service.setServiceConfiguration(ServiceConfigurationHelper.file( // service.setServiceConfiguration(ServiceConfigurationHelper.file(
MegaUploadServiceConfiguration.class, new File( // MegaUploadServiceConfiguration.class, new File(
"src/test/resources/megaupload-nowait.properties"))); // "src/test/resources/megaupload-nowait.properties")));
final MegaUploadDownloaderConfiguration config = new MegaUploadDownloaderConfiguration();
config.setRespectWaitTime(false);
@SuppressWarnings("unused") @SuppressWarnings({ "unused" })
final DownloadChannel channel = ((DownloadService) service) final DownloadChannel channel = service.getDownloader(
.getDownloader(new URL("http://www.megaupload.com/?d=CVQKJ1KM")) new URL("http://www.megaupload.com/?d=CVQKJ1KM"), config)
.download(new DownloadListener() { .openChannel(new DownloadListener() {
@Override @Override
public boolean timer(long time) { public boolean timer(long time) {
System.out.println("Waiting " + time); System.out.println("Waiting " + time);

View File

@@ -0,0 +1,21 @@
<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>
<parent>
<artifactId>httpchannel-service</artifactId>
<groupId>com.rogiel.httpchannel</groupId>
<version>1.0.0</version>
<relativePath>..</relativePath>
</parent>
<artifactId>httpchannel-service-multiupload</artifactId>
<groupId>com.rogiel.httpchannel.services</groupId>
<name>HttpChannel/Service/MultiUpload</name>
<description>Provides upload access to multiupload.com</description>
<dependencies>
<dependency>
<groupId>com.rogiel.httpchannel</groupId>
<artifactId>httpchannel-tests</artifactId>
<version>1.0.0</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,265 @@
package com.rogiel.httpchannel.service.impl;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import com.rogiel.httpchannel.http.PostMultipartRequest;
import com.rogiel.httpchannel.service.AbstractAuthenticator;
import com.rogiel.httpchannel.service.AbstractHttpDownloader;
import com.rogiel.httpchannel.service.AbstractHttpService;
import com.rogiel.httpchannel.service.AbstractUploader;
import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Authenticator;
import com.rogiel.httpchannel.service.AuthenticatorCapability;
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.Downloader;
import com.rogiel.httpchannel.service.DownloaderCapability;
import com.rogiel.httpchannel.service.Service;
import com.rogiel.httpchannel.service.ServiceID;
import com.rogiel.httpchannel.service.UploadChannel;
import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.Uploader;
import com.rogiel.httpchannel.service.UploaderCapability;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback;
import com.rogiel.httpchannel.service.config.NullAuthenticatorConfiguration;
import com.rogiel.httpchannel.service.config.NullDownloaderConfiguration;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.service.exception.DownloadLimitExceededException;
import com.rogiel.httpchannel.service.exception.DownloadLinkNotFoundException;
import com.rogiel.httpchannel.service.exception.DownloadNotAuthorizedException;
import com.rogiel.httpchannel.service.exception.DownloadNotResumableException;
import com.rogiel.httpchannel.service.impl.MultiUploadUploaderConfiguration.MultiUploadMirrorService;
import com.rogiel.httpchannel.util.PatternUtils;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
/**
* This service handles uploads to MultiUpload.com.
*
* @author <a href="http://www.rogiel.com/">Rogiel</a>
* @since 1.0
*/
public class MultiUploadService extends AbstractHttpService implements Service,
UploadService<MultiUploadUploaderConfiguration>,
DownloadService<NullDownloaderConfiguration>,
AuthenticationService<NullAuthenticatorConfiguration> {
/**
* This service ID
*/
public static final ServiceID SERVICE_ID = ServiceID.create("multiupload");
// http://www52.multiupload.com/upload/?UPLOAD_IDENTIFIER=73132658610746
private static final Pattern UPLOAD_URL_PATTERN = Pattern
.compile("http://www([0-9]*)\\.multiupload\\.com/upload/\\?UPLOAD_IDENTIFIER=[0-9]*");
private static final Pattern DOWNLOAD_ID_PATTERN = Pattern
.compile("\"downloadid\":\"([0-9a-zA-Z]*)\"");
private static final Pattern DOWNLOAD_LINK_PATTERN = Pattern
.compile("http://(www\\.)?multiupload\\.com/([0-9a-zA-Z]*)");
private static final Pattern DIRECT_DOWNLOAD_LINK_PATTERN = Pattern
.compile("http://www[0-9]*\\.multiupload\\.com(:[0-9]*)?/files/([0-9a-zA-Z]*)/(.*)");
@Override
public ServiceID getID() {
return SERVICE_ID;
}
@Override
public int getMajorVersion() {
return 1;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public Uploader<MultiUploadUploaderConfiguration> getUploader(
String filename, long filesize,
MultiUploadUploaderConfiguration configuration) {
if (configuration == null)
configuration = new MultiUploadUploaderConfiguration();
return new MultiUploadUploader(filename, filesize, configuration);
}
@Override
public Uploader<MultiUploadUploaderConfiguration> getUploader(
String filename, long filesize) {
return getUploader(filename, filesize, newUploaderConfiguration());
}
@Override
public MultiUploadUploaderConfiguration newUploaderConfiguration() {
return new MultiUploadUploaderConfiguration();
}
@Override
public long getMaximumFilesize() {
return 1 * 1024 * 1024 * 1024;
}
@Override
public String[] getSupportedExtensions() {
return null;
}
@Override
public CapabilityMatrix<UploaderCapability> getUploadCapabilities() {
return new CapabilityMatrix<UploaderCapability>(
UploaderCapability.UNAUTHENTICATED_UPLOAD,
UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD,
UploaderCapability.PREMIUM_ACCOUNT_UPLOAD);
}
@Override
public Downloader<NullDownloaderConfiguration> getDownloader(URL url,
NullDownloaderConfiguration configuration) {
return new MultiUploaderDownloader(url, configuration);
}
@Override
public Downloader<NullDownloaderConfiguration> getDownloader(URL url) {
return getDownloader(url, newDownloaderConfiguration());
}
@Override
public NullDownloaderConfiguration newDownloaderConfiguration() {
return NullDownloaderConfiguration.SHARED_INSTANCE;
}
@Override
public boolean matchURL(URL url) {
return DOWNLOAD_LINK_PATTERN.matcher(url.toString()).matches();
}
@Override
public CapabilityMatrix<DownloaderCapability> getDownloadCapabilities() {
return new CapabilityMatrix<>(
DownloaderCapability.UNAUTHENTICATED_DOWNLOAD,
DownloaderCapability.UNAUTHENTICATED_RESUME,
DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD,
DownloaderCapability.NON_PREMIUM_ACCOUNT_RESUME);
}
@Override
public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
Credential credential, NullAuthenticatorConfiguration configuration) {
return new MultiUploadAuthenticator(credential, configuration);
}
@Override
public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
Credential credential) {
return getAuthenticator(credential, newAuthenticatorConfiguration());
}
@Override
public NullAuthenticatorConfiguration newAuthenticatorConfiguration() {
return NullAuthenticatorConfiguration.SHARED_INSTANCE;
}
@Override
public CapabilityMatrix<AuthenticatorCapability> getAuthenticationCapability() {
return new CapabilityMatrix<AuthenticatorCapability>();
}
protected class MultiUploadUploader extends
AbstractUploader<MultiUploadUploaderConfiguration> implements
Uploader<MultiUploadUploaderConfiguration>,
LinkedUploadChannelCloseCallback {
private Future<String> uploadFuture;
public MultiUploadUploader(String filename, long filesize,
MultiUploadUploaderConfiguration configuration) {
super(filename, filesize, configuration);
}
@Override
public UploadChannel openChannel() throws IOException {
final String url = get("http://www.multiupload.com/").asPage()
.findFormAction(UPLOAD_URL_PATTERN);
final LinkedUploadChannel channel = createLinkedChannel(this);
PostMultipartRequest request = multipartPost(url).parameter(
"description_0", configuration.description()).parameter(
"file_0", channel);
for (final MultiUploadMirrorService mirror : configuration
.uploadServices()) {
request.parameter("service_" + mirror.id, 1);
}
uploadFuture = request.asStringAsync();
return waitChannelLink(channel, uploadFuture);
}
@Override
public String finish() throws IOException {
try {
final String linkId = PatternUtils.find(DOWNLOAD_ID_PATTERN,
uploadFuture.get(), 1);
if (linkId == null)
return null;
return new StringBuilder("http://www.multiupload.com/").append(
linkId).toString();
} catch (InterruptedException e) {
return null;
} catch (ExecutionException e) {
throw (IOException) e.getCause();
}
}
}
protected class MultiUploaderDownloader extends
AbstractHttpDownloader<NullDownloaderConfiguration> implements
Downloader<NullDownloaderConfiguration> {
protected MultiUploaderDownloader(URL url,
NullDownloaderConfiguration configuration) {
super(url, configuration);
}
@Override
public DownloadChannel openChannel(DownloadListener listener,
long position) throws IOException,
DownloadLinkNotFoundException, DownloadLimitExceededException,
DownloadNotAuthorizedException, DownloadNotResumableException {
final HTMLPage page = get(url).asPage();
final String link = page.findLink(DIRECT_DOWNLOAD_LINK_PATTERN);
if (link == null)
throw new DownloadLinkNotFoundException();
return download(get(link).position(position));
}
}
protected class MultiUploadAuthenticator extends
AbstractAuthenticator<NullAuthenticatorConfiguration> implements
Authenticator<NullAuthenticatorConfiguration> {
public MultiUploadAuthenticator(Credential credential,
NullAuthenticatorConfiguration configuration) {
super(credential, configuration);
}
@Override
public void login() throws IOException {
final HTMLPage page = post("http://www.multiupload.com/login")
.parameter("username", credential.getUsername())
.parameter("password", credential.getPassword()).asPage();
if (!page.containsIgnoreCase(credential.getUsername()))
throw new AuthenticationInvalidCredentialException();
}
@Override
public void logout() throws IOException {
post("http://www.multiupload.com/login").parameter("do", "logout")
.request();
// TODO check logout status
}
}
}

View File

@@ -0,0 +1,110 @@
package com.rogiel.httpchannel.service.impl;
import java.util.EnumSet;
import com.rogiel.httpchannel.service.Uploader.DescriptionableUploaderConfiguration;
import com.rogiel.httpchannel.service.Uploader.UploaderConfiguration;
import com.rogiel.httpchannel.service.impl.MultiUploadService.MultiUploadUploader;
/**
* Describes an configuration for an {@link MultiUploadUploader}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class MultiUploadUploaderConfiguration implements UploaderConfiguration,
DescriptionableUploaderConfiguration {
/**
* The upload description
*/
private String description = DescriptionableUploaderConfiguration.DEFAULT_DESCRIPTION;
/**
* The services in which Multiupload should mirror the uploaded file
*/
private EnumSet<MultiUploadMirrorService> uploadServices = EnumSet
.allOf(MultiUploadMirrorService.class);
/**
* An enumeration containing all supported services for Multiupload
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public enum MultiUploadMirrorService {
MEGAUPLOAD(1), UPLOADKING(16), DEPOSIT_FILES(7), HOTFILE(9), UPLOAD_HERE(
17), ZSHARE(6), FILE_SONIC(15), FILE_SERVE(18), WUPLOAD(19);
/**
* The internal multiupload id
*/
public final int id;
private MultiUploadMirrorService(int id) {
this.id = id;
}
}
@Override
public String description() {
return description;
}
@Override
public MultiUploadUploaderConfiguration description(String description) {
this.description = description;
return this;
}
/**
* Adds this service as an desired mirror
*
* @param service
* the service
*/
public MultiUploadUploaderConfiguration uploadService(
MultiUploadMirrorService... services) {
for (final MultiUploadMirrorService service : services) {
uploadServices.add(service);
}
return this;
}
/**
* Checks if the service is on the desired mirror list
*
* @param service
* the service
* @return <code>true</code> if the service is on the list
*/
public boolean containsUploadService(MultiUploadMirrorService service) {
return uploadServices.contains(service);
}
/**
* Removes this service from the mirror list
*
* @param service
* the service
*/
public MultiUploadUploaderConfiguration removeUploadService(
MultiUploadMirrorService service) {
uploadServices.remove(service);
return this;
}
/**
* Removes all services from the mirror list
*
* @return
*/
public MultiUploadUploaderConfiguration clearUploadServices() {
uploadServices.clear();
return this;
}
/**
* @return the list of services of which MultiUpload should try to make
* mirrors
*/
public EnumSet<MultiUploadMirrorService> uploadServices() {
return uploadServices;
}
}

View File

@@ -0,0 +1 @@
com.rogiel.httpchannel.service.impl.MultiUploadService

View File

@@ -0,0 +1,32 @@
package com.rogiel.httpchannel.service.impl;
import java.net.MalformedURLException;
import java.net.URL;
import com.rogiel.httpchannel.service.AbstractServiceTest;
import com.rogiel.httpchannel.service.Credential;
import com.rogiel.httpchannel.service.Service;
public class MultiUploadServiceTest extends AbstractServiceTest {
@Override
protected Service createService() {
return new MultiUploadService();
}
@Override
protected URL createDownloadURL() throws MalformedURLException {
return new URL("http://www.multiupload.com/QPDUXJDZZY");
}
@Override
protected Credential createValidCredential() {
return null;
}
@Override
protected Credential createInvalidCredential() {
return new Credential("invalid-"
+ Double.toString(Math.random() * 1000), Double.toString(Math
.random() * Integer.MAX_VALUE));
}
}

View File

@@ -0,0 +1,3 @@
This is a simple upload test file.
This is for testing purposes only.

View File

@@ -0,0 +1,13 @@
<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>
<parent>
<artifactId>httpchannel-service</artifactId>
<groupId>com.rogiel.httpchannel</groupId>
<version>1.0.0</version>
<relativePath>..</relativePath>
</parent>
<artifactId>httpchannel-service-uploadking</artifactId>
<groupId>com.rogiel.httpchannel.services</groupId>
<name>HttpChannel/Service/UploadKing</name>
<description>Provides upload access to uploadking.com</description>
</project>

View File

@@ -0,0 +1,191 @@
package com.rogiel.httpchannel.service.impl;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import com.rogiel.httpchannel.service.AbstractAuthenticator;
import com.rogiel.httpchannel.service.AbstractHttpService;
import com.rogiel.httpchannel.service.AbstractUploader;
import com.rogiel.httpchannel.service.AuthenticationService;
import com.rogiel.httpchannel.service.Authenticator;
import com.rogiel.httpchannel.service.AuthenticatorCapability;
import com.rogiel.httpchannel.service.CapabilityMatrix;
import com.rogiel.httpchannel.service.Credential;
import com.rogiel.httpchannel.service.Service;
import com.rogiel.httpchannel.service.ServiceID;
import com.rogiel.httpchannel.service.UploadChannel;
import com.rogiel.httpchannel.service.UploadService;
import com.rogiel.httpchannel.service.Uploader;
import com.rogiel.httpchannel.service.UploaderCapability;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback;
import com.rogiel.httpchannel.service.config.NullAuthenticatorConfiguration;
import com.rogiel.httpchannel.service.config.NullUploaderConfiguration;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.util.PatternUtils;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
/**
* This service handles uploads to UploadKing.com.
*
* @author <a href="http://www.rogiel.com/">Rogiel</a>
* @since 1.0
*/
public class UploadKingService extends AbstractHttpService implements Service,
UploadService<NullUploaderConfiguration>,
AuthenticationService<NullAuthenticatorConfiguration> {
/**
* This service ID
*/
public static final ServiceID SERVICE_ID = ServiceID.create("uploadking");
private static final Pattern UPLOAD_URL_PATTERN = Pattern
.compile("http://www([0-9]*)\\.uploadking\\.com/upload/\\?UPLOAD_IDENTIFIER=[0-9]*");
private static final Pattern DOWNLOAD_ID_PATTERN = Pattern
.compile("\"downloadid\":\"([0-9a-zA-Z]*)\"");
private static final String INVALID_LOGIN_STRING = "Incorrect username and/or password. Please try again!";
@Override
public ServiceID getID() {
return SERVICE_ID;
}
@Override
public int getMajorVersion() {
return 1;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public Uploader<NullUploaderConfiguration> getUploader(String filename,
long filesize, NullUploaderConfiguration configuration) {
return new UploadKingUploader(filename, filesize, configuration);
}
@Override
public Uploader<NullUploaderConfiguration> getUploader(String filename,
long filesize) {
return getUploader(filename, filesize, newUploaderConfiguration());
}
@Override
public NullUploaderConfiguration newUploaderConfiguration() {
return NullUploaderConfiguration.SHARED_INSTANCE;
}
@Override
public long getMaximumFilesize() {
return 1 * 1024 * 1024 * 1024;
}
@Override
public String[] getSupportedExtensions() {
return null;
}
@Override
public CapabilityMatrix<UploaderCapability> getUploadCapabilities() {
return new CapabilityMatrix<UploaderCapability>(
UploaderCapability.UNAUTHENTICATED_UPLOAD,
UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD,
UploaderCapability.PREMIUM_ACCOUNT_UPLOAD);
}
@Override
public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
Credential credential, NullAuthenticatorConfiguration configuration) {
return new UploadKingAuthenticator(credential, configuration);
}
@Override
public Authenticator<NullAuthenticatorConfiguration> getAuthenticator(
Credential credential) {
return getAuthenticator(credential, newAuthenticatorConfiguration());
}
@Override
public NullAuthenticatorConfiguration newAuthenticatorConfiguration() {
return NullAuthenticatorConfiguration.SHARED_INSTANCE;
}
@Override
public CapabilityMatrix<AuthenticatorCapability> getAuthenticationCapability() {
return new CapabilityMatrix<AuthenticatorCapability>();
}
protected class UploadKingUploader extends
AbstractUploader<NullUploaderConfiguration> implements
Uploader<NullUploaderConfiguration>,
LinkedUploadChannelCloseCallback {
private Future<String> uploadFuture;
public UploadKingUploader(String filename, long filesize,
NullUploaderConfiguration configuration) {
super(filename, filesize, configuration);
}
@Override
public UploadChannel openChannel() throws IOException {
final HTMLPage page = get("http://www.uploadking.com/").asPage();
final String userCookie = page.getInputValueById("usercookie");
final String url = page.findFormAction(UPLOAD_URL_PATTERN);
final String uploadID = page.getInputValue("UPLOAD_IDENTIFIER");
final LinkedUploadChannel channel = createLinkedChannel(this);
uploadFuture = multipartPost(url).parameter("file_0", channel)
.parameter("u", userCookie)
.parameter("UPLOAD_IDENTIFIER", uploadID).asStringAsync();
return waitChannelLink(channel, uploadFuture);
}
@Override
public String finish() throws IOException {
try {
final String linkId = PatternUtils.find(DOWNLOAD_ID_PATTERN,
uploadFuture.get(), 1);
if (linkId == null)
return null;
return new StringBuilder("http://www.uploadking.com/").append(
linkId).toString();
} catch (InterruptedException e) {
return null;
} catch (ExecutionException e) {
throw (IOException) e.getCause();
}
}
}
protected class UploadKingAuthenticator extends
AbstractAuthenticator<NullAuthenticatorConfiguration> implements
Authenticator<NullAuthenticatorConfiguration> {
public UploadKingAuthenticator(Credential credential,
NullAuthenticatorConfiguration configuration) {
super(credential, configuration);
}
@Override
public void login() throws IOException {
final HTMLPage page = post("http://www.uploadking.com/login")
.parameter("do", "login")
.parameter("username", credential.getUsername())
.parameter("password", credential.getPassword()).asPage();
if (page.contains(INVALID_LOGIN_STRING))
throw new AuthenticationInvalidCredentialException();
}
@Override
public void logout() throws IOException {
post("http://www.uploadking.com/login").parameter("do", "logout")
.request();
// TODO check logout status
}
}
}

View File

@@ -0,0 +1 @@
com.rogiel.httpchannel.service.impl.MultiUploadService

View File

@@ -0,0 +1,39 @@
package com.rogiel.httpchannel.service.impl;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import com.rogiel.httpchannel.service.UploaderCapability;
import com.rogiel.httpchannel.util.ChannelUtils;
public class UploadKingServiceTest {
private UploadKingService service;
@Before
public void setUp() throws Exception {
service = new UploadKingService();
}
@Test
public void testNonLoguedInUploader() throws IOException {
assertTrue(
"This service does not have the capability UploadCapability.UNAUTHENTICATED_UPLOAD",
service.getUploadCapabilities().has(
UploaderCapability.UNAUTHENTICATED_UPLOAD));
final Path path = Paths.get("src/test/resources/upload-test-file.txt");
final URL url = ChannelUtils.upload(service, path);
Assert.assertNotNull(url);
System.out.println(url);
}
}

View File

@@ -0,0 +1,3 @@
This is a simple upload test file.
This is for testing purposes only.

View File

@@ -13,6 +13,7 @@
<modules> <modules>
<module>httpchannel-service-megaupload</module> <module>httpchannel-service-megaupload</module>
<module>httpchannel-service-hotfile</module> <module>httpchannel-service-hotfile</module>
<module>httpchannel-service-multiupload</module>
</modules> </modules>
<dependencies> <dependencies>
@@ -27,4 +28,6 @@
<version>1.0.0</version> <version>1.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
<name>HttpChannel/Service</name>
<description>Parent module that all service implementations should inherit</description>
</project> </project>

24
httpchannel-tests/pom.xml Normal file
View File

@@ -0,0 +1,24 @@
<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>
<parent>
<artifactId>httpchannel</artifactId>
<groupId>com.rogiel.httpchannel</groupId>
<version>1.0.0</version>
<relativePath>..</relativePath>
</parent>
<artifactId>httpchannel-tests</artifactId>
<name>HttpChannel/Tests</name>
<description>This module contains several test utilities</description>
<dependencies>
<dependency>
<groupId>com.rogiel.httpchannel</groupId>
<artifactId>httpchannel-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.rogiel.httpchannel</groupId>
<artifactId>httpchannel-util</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,237 @@
/*
* 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 static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import com.rogiel.httpchannel.service.exception.AuthenticationInvalidCredentialException;
import com.rogiel.httpchannel.util.ChannelUtils;
import com.rogiel.httpchannel.util.ChecksumUtils;
public abstract class AbstractServiceTest {
public static final Path TEST_UPLOAD_FILE = Paths
.get("src/main/resources/upload-test-file.txt");
public static final byte[] EXPECTED_FULL_CHECKSUM = new byte[] { 27, -93,
-76, 6, 123, -31, -9, 1, -100, 103, 123, -108, -22, -3, 121, -54,
-127, 27, 43, -8 };
public static final byte[] EXPECTED_RESUME_CHECKSUM = new byte[] { 39, -29,
-107, -76, -69, -122, -20, 78, -27, -60, 95, -23, 70, -127, -17,
101, -39, -87, -2, -67 };
private Service service;
private URL downloadURL;
private Credential validCredential;
private Credential invalidCredential;
@Before
public void setUp() throws Exception {
this.service = createService();
downloadURL = createDownloadURL();
validCredential = createValidCredential();
invalidCredential = createInvalidCredential();
}
protected abstract Service createService();
protected Credential createValidCredential() {
return null;
}
protected Credential createInvalidCredential() {
return null;
}
protected URL createDownloadURL() throws MalformedURLException {
return null;
}
@Test
public void testUnauthenticatedUpload() throws IOException {
if (!(service instanceof UploadService))
return;
assertTrue(
"This service does not have the capability UploadCapability.UNAUTHENTICATED_UPLOAD",
((UploadService<?>) service).getUploadCapabilities().has(
UploaderCapability.UNAUTHENTICATED_UPLOAD));
final URL url = ChannelUtils.upload((UploadService<?>) service,
TEST_UPLOAD_FILE);
Assert.assertNotNull(url);
System.out.println("Uploaded file to " + url);
}
@Test
public void testAuthenticatedUpload() throws IOException {
if (validCredential == null)
return;
if (!(service instanceof UploadService))
return;
if (!(service instanceof AuthenticationService))
fail("The servide does not support authentication!");
assertTrue(
"This service does not have the capability UploadCapability.NON_PREMIUM_ACCOUNT_UPLOAD",
((UploadService<?>) service).getUploadCapabilities().has(
UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD));
((AuthenticationService<?>) service).getAuthenticator(validCredential)
.login();
final URL url = ChannelUtils.upload((UploadService<?>) service,
TEST_UPLOAD_FILE);
Assert.assertNotNull(url);
System.out.println("Uploaded file to " + url);
}
@Test
public void testUnauthenticatedDownload() throws IOException,
NoSuchAlgorithmException {
if (!(service instanceof DownloadService))
return;
assertTrue(
"This service does not have the capability DownloaderCapability.UNAUTHENTICATED_DOWNLOAD",
((DownloadService<?>) service).getDownloadCapabilities().has(
DownloaderCapability.UNAUTHENTICATED_DOWNLOAD));
final byte[] data = ChannelUtils
.toByteArray(((DownloadService<?>) service).getDownloader(
downloadURL).openChannel());
ChecksumUtils.assertChecksum(
"Downloaded data checksum did not matched", "SHA1", data,
EXPECTED_FULL_CHECKSUM);
}
@Test
public void testUnauthenticatedDownloadResume() throws IOException,
NoSuchAlgorithmException {
if (!(service instanceof DownloadService))
return;
if (!((DownloadService<?>) service).getDownloadCapabilities().has(
DownloaderCapability.UNAUTHENTICATED_RESUME))
return;
assertTrue(
"This service does not have the capability DownloaderCapability.UNAUTHENTICATED_DOWNLOAD",
((DownloadService<?>) service).getDownloadCapabilities().has(
DownloaderCapability.UNAUTHENTICATED_DOWNLOAD));
final byte[] data = ChannelUtils
.toByteArray(((DownloadService<?>) service).getDownloader(
downloadURL).openChannel(50));
ChecksumUtils.assertChecksum(
"Downloaded data checksum did not matched", "SHA1", data,
EXPECTED_RESUME_CHECKSUM);
}
@Test
public void testAuthenticatedDownload() throws IOException,
NoSuchAlgorithmException {
if (validCredential == null)
return;
if (!(service instanceof DownloadService))
return;
if (!(service instanceof AuthenticationService))
fail("The service does not support authentication!");
assertTrue(
"This service does not have the capability DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD",
((DownloadService<?>) service).getDownloadCapabilities().has(
DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD));
((AuthenticationService<?>) service).getAuthenticator(validCredential)
.login();
final byte[] data = ChannelUtils
.toByteArray(((DownloadService<?>) service).getDownloader(
downloadURL).openChannel());
ChecksumUtils.assertChecksum(
"Downloaded data checksum did not matched", "SHA1", data,
EXPECTED_FULL_CHECKSUM);
}
@Test
public void testAuthenticatedDownloadResume() throws IOException,
NoSuchAlgorithmException {
if (validCredential == null)
return;
if (!(service instanceof DownloadService))
return;
if (!(service instanceof AuthenticationService))
fail("The service does not support authentication!");
if (!((DownloadService<?>) service).getDownloadCapabilities().has(
DownloaderCapability.NON_PREMIUM_ACCOUNT_RESUME))
return;
assertTrue(
"This service does not have the capability DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD",
((DownloadService<?>) service).getDownloadCapabilities().has(
DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD));
((AuthenticationService<?>) service).getAuthenticator(validCredential)
.login();
final byte[] data = ChannelUtils
.toByteArray(((DownloadService<?>) service).getDownloader(
downloadURL).openChannel(50));
ChecksumUtils.assertChecksum(
"Downloaded data checksum did not matched", "SHA1", data,
EXPECTED_RESUME_CHECKSUM);
}
@Test
public void testAuthenticatorWithValidCredential() throws IOException {
if (validCredential == null)
return;
if (!(service instanceof AuthenticationService))
return;
((AuthenticationService<?>) service).getAuthenticator(validCredential)
.login();
}
@Test
public void testAuthenticatorWithInvalidCredential() throws IOException {
if (invalidCredential == null)
return;
if (!(service instanceof AuthenticationService))
return;
try {
((AuthenticationService<?>) service).getAuthenticator(
invalidCredential).login();
} catch (AuthenticationInvalidCredentialException e) {
return;
}
fail("This login attept should have failed!");
}
}

View File

@@ -0,0 +1,15 @@
package com.rogiel.httpchannel.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.junit.Assert;
public class ChecksumUtils {
public static void assertChecksum(String message, String algorithm,
byte[] data, byte[] expected) throws NoSuchAlgorithmException {
final MessageDigest md = MessageDigest.getInstance(algorithm);
final byte[] actual = md.digest(data);
Assert.assertArrayEquals(message, expected, actual);
}
}

View File

@@ -0,0 +1,3 @@
This is a simple upload test file.
This is for testing purposes only.

View File

@@ -59,8 +59,8 @@
<groupId>org.htmlparser</groupId> <groupId>org.htmlparser</groupId>
<artifactId>htmlparser</artifactId> <artifactId>htmlparser</artifactId>
<version>2.1</version> <version>2.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<name>HttpChannel/Service/Utilities</name>
<description>Module providing several utilities to service implementations. Though this module is not required to implement services, it contains several shortcuts that can help implementing services.</description>
</project> </project>

View File

@@ -0,0 +1,47 @@
/**
*
*/
package com.rogiel.httpchannel.captcha;
import java.net.URL;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public abstract class AbstractImageCaptcha implements ImageCaptcha {
private final URL imageURL;
private final String ID;
private String answer;
private boolean automaticallyResolved;
public AbstractImageCaptcha(URL imageURL, String ID) {
this.imageURL = imageURL;
this.ID = ID;
}
@Override
public void setAnswer(String answer) {
this.answer = answer;
}
@Override
public String getAnswer() {
return answer;
}
@Override
public boolean wasAutomaticallyResolved() {
return automaticallyResolved;
}
@Override
public URL getImageURL() {
return imageURL;
}
public String getID() {
return ID;
}
}

View File

@@ -0,0 +1,54 @@
/**
*
*/
package com.rogiel.httpchannel.captcha;
import java.io.IOException;
import java.net.URL;
import com.rogiel.httpchannel.http.GetRequest;
import com.rogiel.httpchannel.http.HttpContext;
import com.rogiel.httpchannel.http.PostMultipartRequest;
import com.rogiel.httpchannel.http.PostRequest;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public abstract class AbstractImageCaptchaService<C extends AbstractImageCaptcha>
implements ImageCaptchaService<C> {
protected final HttpContext http = new HttpContext();
@Override
public final C create(String html) {
try {
return create(HTMLPage.parse(html));
} catch (IOException e) {
return null;
}
}
public abstract C create(HTMLPage page) throws IOException;
@Override
public boolean resolve(C captcha) {
return false;
}
public GetRequest get(String url) {
return http.get(url);
}
public GetRequest get(URL url) {
return http.get(url);
}
public PostRequest post(String url) {
return http.post(url);
}
public PostMultipartRequest multipartPost(String url) {
return http.multipartPost(url);
}
}

View File

@@ -0,0 +1,30 @@
/**
*
*/
package com.rogiel.httpchannel.http;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
public class GetRequest extends Request {
private long position = 0;
public GetRequest(HttpContext ctx, String url) {
super(ctx, url);
}
@Override
public HttpResponse request() throws IOException {
final HttpGet get = new HttpGet(url);
if (position > 0)
get.addHeader("Range", "bytes=" + position + "-");
return ctx.client.execute(get);
}
public GetRequest position(long position) {
this.position = position;
return this;
}
}

View File

@@ -0,0 +1,40 @@
/**
*
*/
package com.rogiel.httpchannel.http;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class HttpContext {
protected final ExecutorService threadPool = Executors
.newCachedThreadPool();
/**
* The {@link HttpClient} instance for this service
*/
protected DefaultHttpClient client = new DefaultHttpClient();
public GetRequest get(String url) {
return new GetRequest(this, url);
}
public GetRequest get(URL url) {
return get(url.toString());
}
public PostRequest post(String url) {
return new PostRequest(this, url);
}
public PostMultipartRequest multipartPost(String url) {
return new PostMultipartRequest(this, url);
}
}

View File

@@ -0,0 +1,48 @@
/**
*
*/
package com.rogiel.httpchannel.http;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.StringBody;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannelContentBody;
public class PostMultipartRequest extends PostRequest {
private final MultipartEntity entity;
public PostMultipartRequest(HttpContext ctx, String url) {
super(ctx, url);
this.entity = new MultipartEntity();
}
@Override
public HttpResponse request() throws IOException {
final HttpPost post = new HttpPost(url);
post.setEntity(entity);
return ctx.client.execute(post);
}
public PostMultipartRequest parameter(String name, ContentBody body) {
entity.addPart(name, body);
return this;
}
@Override
public PostMultipartRequest parameter(String name, String value)
throws UnsupportedEncodingException {
return parameter(name, new StringBody(value));
}
public PostMultipartRequest parameter(String name,
LinkedUploadChannel channel) {
return parameter(name, new LinkedUploadChannelContentBody(channel));
}
}

View File

@@ -0,0 +1,46 @@
/**
*
*/
package com.rogiel.httpchannel.http;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
public class PostRequest extends Request {
protected final List<NameValuePair> params = new ArrayList<NameValuePair>();
public PostRequest(HttpContext ctx, String url) {
super(ctx, url);
}
@Override
public HttpResponse request() throws IOException {
final HttpPost post = new HttpPost(url);
post.setEntity(new UrlEncodedFormEntity(params));
return ctx.client.execute(post);
}
public PostRequest parameter(String name, String value)
throws UnsupportedEncodingException {
params.add(new BasicNameValuePair(name, value));
return this;
}
public PostRequest parameter(String name, int value)
throws UnsupportedEncodingException {
return parameter(name, Integer.toString(value));
}
public PostRequest parameter(String name, boolean value)
throws UnsupportedEncodingException {
return parameter(name, (value ? "1" : "0"));
}
}

View File

@@ -0,0 +1,65 @@
/**
*
*/
package com.rogiel.httpchannel.http;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import com.rogiel.httpchannel.util.HttpClientUtils;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
public abstract class Request {
protected final HttpContext ctx;
protected final String url;
public Request(HttpContext ctx, String url) {
this.ctx = ctx;
this.url = url;
}
public abstract HttpResponse request() throws IOException;
public Future<HttpResponse> requestAsync() throws IOException {
return ctx.threadPool.submit(new Callable<HttpResponse>() {
@Override
public HttpResponse call() throws Exception {
return request();
}
});
}
public String asString() throws ClientProtocolException, IOException {
return HttpClientUtils.toString(request());
}
public Future<String> asStringAsync() throws IOException {
return ctx.threadPool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return asString();
}
});
}
public HTMLPage asPage() throws ClientProtocolException, IOException {
return HTMLPage.parse(asString());
}
public Future<HTMLPage> asPageAsync() throws IOException {
return ctx.threadPool.submit(new Callable<HTMLPage>() {
@Override
public HTMLPage call() throws Exception {
return asPage();
}
});
}
public String getURL() {
return url;
}
}

View File

@@ -0,0 +1,39 @@
package com.rogiel.httpchannel.service;
import com.rogiel.httpchannel.service.Authenticator.AuthenticatorConfiguration;
/**
* An abstract {@link Authenticator} that implements most of the general-purpose
* methods
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <C>
* the {@link Authenticator} configuration object type
*/
public abstract class AbstractAuthenticator<C extends AuthenticatorConfiguration>
implements Authenticator<C> {
protected final Credential credential;
/**
* The {@link Authenticator} configuration
*/
protected final C configuration;
/**
* Creates a new instance
*
* @param credential
* the authentication credential
* @param configuration
* the configuration object
*/
protected AbstractAuthenticator(Credential credential, C configuration) {
this.credential = credential;
this.configuration = configuration;
}
@Override
public C getConfiguration() {
return configuration;
}
}

View File

@@ -1,52 +1,79 @@
/*
* 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; package com.rogiel.httpchannel.service;
import org.apache.http.Header; import java.io.IOException;
import org.apache.http.HttpResponse; import java.io.InputStream;
import java.net.URL;
import com.rogiel.httpchannel.util.ThreadUtils; import com.rogiel.httpchannel.service.Downloader.DownloaderConfiguration;
import com.rogiel.httpchannel.service.channel.InputStreamDownloadChannel;
import com.rogiel.httpchannel.service.exception.DownloadLimitExceededException;
import com.rogiel.httpchannel.service.exception.DownloadLinkNotFoundException;
import com.rogiel.httpchannel.service.exception.DownloadNotAuthorizedException;
import com.rogiel.httpchannel.service.exception.DownloadNotResumableException;
/** /**
* @author rogiel * An abstract {@link Downloader} that implements most of the general-purpose
* methods
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <C>
* the {@link Downloader} configuration object type
*/ */
public abstract class AbstractDownloader implements Downloader { public abstract class AbstractDownloader<C extends DownloaderConfiguration>
protected int parseTimer(String stringTimer) { implements Downloader<C> {
int timer = 0; /**
if (stringTimer != null && stringTimer.length() > 0) { * The download URL
timer = Integer.parseInt(stringTimer); */
} protected final URL url;
return timer;
/**
* The {@link Downloader} configuration
*/
protected final C configuration;
/**
* Creates a new instance
*
* @param url
* the download url
* @param configuration
* the configuration object
*/
protected AbstractDownloader(URL url, C configuration) {
this.url = url;
this.configuration = configuration;
} }
protected long getContentLength(HttpResponse response) { @Override
final Header contentLengthHeader = response public DownloadChannel openChannel(long position) throws IOException,
.getFirstHeader("Content-Length"); DownloadLinkNotFoundException, DownloadLimitExceededException,
long contentLength = -1; DownloadNotAuthorizedException, DownloadNotResumableException {
if (contentLengthHeader != null) { return openChannel(null, position);
contentLength = Long.valueOf(contentLengthHeader.getValue());
}
return contentLength;
} }
protected void timer(DownloadListener listener, long timer) { @Override
if (listener != null) { public DownloadChannel openChannel(DownloadListener listener)
listener.timer(timer); throws IOException, DownloadLinkNotFoundException,
} DownloadLimitExceededException, DownloadNotAuthorizedException,
ThreadUtils.sleep(timer); DownloadNotResumableException {
return openChannel(listener, 0);
}
@Override
public DownloadChannel openChannel() throws IOException,
DownloadLinkNotFoundException, DownloadLimitExceededException,
DownloadNotAuthorizedException, DownloadNotResumableException {
return openChannel(null, 0);
}
protected InputStreamDownloadChannel createInputStreamChannel(
InputStream in, long length, String filename) {
return new InputStreamDownloadChannel(in, length, filename);
}
@Override
public C getConfiguration() {
return configuration;
} }
} }

View File

@@ -0,0 +1,66 @@
/*
* 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.net.URL;
import org.apache.commons.io.FilenameUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import com.rogiel.httpchannel.http.Request;
import com.rogiel.httpchannel.service.Downloader.DownloaderConfiguration;
import com.rogiel.httpchannel.service.channel.InputStreamDownloadChannel;
import com.rogiel.httpchannel.util.ThreadUtils;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class AbstractHttpDownloader<C extends DownloaderConfiguration>
extends AbstractDownloader<C> implements Downloader<C> {
protected AbstractHttpDownloader(URL url, C configuration) {
super(url, configuration);
}
protected long getContentLength(HttpResponse response) {
final Header contentLengthHeader = response
.getFirstHeader("Content-Length");
long contentLength = -1;
if (contentLengthHeader != null) {
contentLength = Long.valueOf(contentLengthHeader.getValue());
}
return contentLength;
}
protected void timer(DownloadListener listener, long timer) {
if (listener != null) {
if (!listener.timer(timer))
return;
}
ThreadUtils.sleep(timer);
}
protected InputStreamDownloadChannel download(Request request)
throws IOException {
final HttpResponse response = request.request();
final String filename = FilenameUtils.getName(request.getURL());
final long contentLength = getContentLength(response);
return createInputStreamChannel(response.getEntity().getContent(),
contentLength, filename);
}
}

View File

@@ -16,151 +16,47 @@
*/ */
package com.rogiel.httpchannel.service; package com.rogiel.httpchannel.service;
import java.io.IOException; import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.apache.http.HttpEntity; import com.rogiel.httpchannel.http.GetRequest;
import org.apache.http.HttpResponse; import com.rogiel.httpchannel.http.HttpContext;
import org.apache.http.client.ClientProtocolException; import com.rogiel.httpchannel.http.PostMultipartRequest;
import org.apache.http.client.HttpClient; import com.rogiel.httpchannel.http.PostRequest;
import org.apache.http.client.methods.HttpGet; import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
import org.apache.http.client.methods.HttpPost; import com.rogiel.httpchannel.util.ThreadUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import com.rogiel.httpchannel.service.captcha.CaptchaResolver;
import com.rogiel.httpchannel.service.config.ServiceConfiguration;
import com.rogiel.httpchannel.util.AlwaysRedirectStrategy;
import com.rogiel.httpchannel.util.HttpClientUtils;
import com.rogiel.httpchannel.util.htmlparser.HTMLPage;
/** /**
* Abstract base service for HTTP enabled services. * Abstract base service for HTTP enabled services.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public abstract class AbstractHttpService<T extends ServiceConfiguration> public abstract class AbstractHttpService extends AbstractService implements
extends AbstractService<T> implements Service { Service {
private static final ExecutorService threadPool = Executors protected final HttpContext http = new HttpContext();
.newCachedThreadPool();
/** protected LinkedUploadChannel waitChannelLink(LinkedUploadChannel channel,
* The {@link HttpClient} instance for this service Future<?> future) {
*/ while (!channel.isLinked() && !future.isDone()) {
protected DefaultHttpClient client = new DefaultHttpClient(); ThreadUtils.sleep(100);
}
/** return channel;
* The captcha resolver
*/
protected CaptchaResolver captchaResolver;
protected AbstractHttpService(T configuration) {
super(configuration);
client.setRedirectStrategy(new AlwaysRedirectStrategy());
// client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS,
// true);
// client.getParams().setIntParameter(ClientPNames.MAX_REDIRECTS, 10);
// client.setRedirectStrategy(new DefaultRedirectStrategy());
} }
protected HttpResponse get(String url) throws ClientProtocolException, public GetRequest get(String url) {
IOException { return http.get(url);
final HttpGet request = new HttpGet(url);
return client.execute(request);
} }
protected HttpResponse get(String url, long rangeStart) public GetRequest get(URL url) {
throws ClientProtocolException, IOException { return http.get(url);
final HttpGet request = new HttpGet(url);
if (rangeStart >= 0)
request.addHeader("Range", "bytes=" + rangeStart + "-");
return client.execute(request);
} }
protected String getAsString(String url) throws ClientProtocolException, public PostRequest post(String url) {
IOException { return http.post(url);
return HttpClientUtils.toString(get(url));
} }
protected HTMLPage getAsPage(String url) throws ClientProtocolException, public PostMultipartRequest multipartPost(String url) {
IOException { return http.multipartPost(url);
return HTMLPage.parse(getAsString(url));
}
public Future<HttpResponse> getAsync(final String url) throws IOException {
return threadPool.submit(new Callable<HttpResponse>() {
@Override
public HttpResponse call() throws Exception {
return get(url);
}
});
}
public Future<String> getAsStringAsync(final String url) throws IOException {
return threadPool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return getAsString(url);
}
});
}
public Future<HTMLPage> getAsPageAsync(final String url) throws IOException {
return threadPool.submit(new Callable<HTMLPage>() {
@Override
public HTMLPage call() throws Exception {
return getAsPage(url);
}
});
}
protected HttpResponse post(String url, HttpEntity entity)
throws ClientProtocolException, IOException {
final HttpPost request = new HttpPost(url);
request.setEntity(entity);
return client.execute(request);
}
protected String postAsString(String url, HttpEntity entity)
throws ClientProtocolException, IOException {
return HttpClientUtils.toString(post(url, entity));
}
protected HTMLPage postAsPage(String url, HttpEntity entity)
throws ClientProtocolException, IOException {
return HTMLPage.parse(postAsString(url, entity));
}
protected Future<HttpResponse> postAsync(final String url,
final HttpEntity entity) throws IOException {
return threadPool.submit(new Callable<HttpResponse>() {
@Override
public HttpResponse call() throws Exception {
return post(url, entity);
}
});
}
protected Future<String> postAsStringAsync(final String url,
final HttpEntity entity) throws IOException {
return threadPool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return postAsString(url, entity);
}
});
}
protected Future<HTMLPage> postAsPageAsync(final String url,
final HttpEntity entity) throws IOException {
return threadPool.submit(new Callable<HTMLPage>() {
@Override
public HTMLPage call() throws Exception {
return postAsPage(url, entity);
}
});
} }
} }

View File

@@ -0,0 +1,58 @@
package com.rogiel.httpchannel.service;
import com.rogiel.httpchannel.service.Uploader.UploaderConfiguration;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel;
import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback;
/**
* An abstract {@link Uploader} that implements most of the general-purpose
* methods
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <C>
* the {@link Uploader} configuration object type
*/
public abstract class AbstractUploader<C extends UploaderConfiguration>
implements Uploader<C> {
protected final String filename;
protected final long filesize;
/**
* The {@link Uploader} configuration
*/
protected final C configuration;
/**
* Creates a new instance
*
* @param filename
* the file name
* @param filesize
* the file size
* @param configuration
* the configuration object
*/
public AbstractUploader(String filename, long filesize, C configuration) {
this.filename = filename;
this.filesize = filesize;
this.configuration = configuration;
}
/**
* Creates a new linked channel
*
* @param closeCallback
* the close callback
* @return a newly created {@link LinkedUploadChannel}
*/
protected LinkedUploadChannel createLinkedChannel(
LinkedUploadChannelCloseCallback closeCallback) {
return new LinkedUploadChannel(closeCallback, filesize, filename);
}
@Override
public C getConfiguration() {
return configuration;
}
}

View File

@@ -17,11 +17,12 @@
package com.rogiel.httpchannel.service.channel; package com.rogiel.httpchannel.service.channel;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import com.rogiel.httpchannel.service.UploadChannel; import com.rogiel.httpchannel.service.UploadChannel;
import com.rogiel.httpchannel.service.exception.UploadLinkNotFoundException;
/** /**
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
@@ -33,7 +34,7 @@ public class LinkedUploadChannel implements UploadChannel {
private final long length; private final long length;
private final String filename; private final String filename;
private String downloadLink; private URL downloadLink;
private boolean open = true; private boolean open = true;
@@ -59,7 +60,10 @@ public class LinkedUploadChannel implements UploadChannel {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
open = false; open = false;
downloadLink = closeCallback.finish(); final String downloadLink = closeCallback.finish();
if (downloadLink == null)
throw new UploadLinkNotFoundException();
this.downloadLink = new URL(downloadLink);
} }
public interface LinkedUploadChannelCloseCallback { public interface LinkedUploadChannelCloseCallback {
@@ -77,12 +81,12 @@ public class LinkedUploadChannel implements UploadChannel {
} }
@Override @Override
public String getDownloadLink() { public URL getDownloadLink() {
return downloadLink; return downloadLink;
} }
public void linkChannel(WritableByteChannel channel) throws IOException { public void linkChannel(WritableByteChannel channel) throws IOException {
if(this.channel != null) if (this.channel != null)
throw new IOException("This channel is already linked."); throw new IOException("This channel is already linked.");
this.channel = channel; this.channel = channel;
} }

View File

@@ -29,7 +29,7 @@ import com.rogiel.httpchannel.service.Uploader;
/** /**
* {@link ContentBody} used to upload files in {@link Uploader} implementations. * {@link ContentBody} used to upload files in {@link Uploader} implementations.
* *
* @author Rogiel * @author <a href="http://www.rogiel.com">Rogiel</a>
* @since 1.0 * @since 1.0
*/ */
public class LinkedUploadChannelContentBody extends AbstractContentBody { public class LinkedUploadChannelContentBody extends AbstractContentBody {

Some files were not shown because too many files have changed in this diff Show More