From 23f80b50e622bde63a37a0c3f637fd7f924d9a1c Mon Sep 17 00:00:00 2001 From: Rogiel Date: Sun, 15 Jan 2012 18:31:50 -0200 Subject: [PATCH] Implements several services and improves API --- httpchannel-api/pom.xml | 25 +- .../rogiel/httpchannel/captcha/Captcha.java | 33 ++ .../httpchannel/captcha/CaptchaResolver.java | 19 + .../httpchannel/captcha/CaptchaService.java | 11 + .../httpchannel/captcha/ImageCaptcha.java | 22 ++ .../captcha/ImageCaptchaService.java | 31 ++ .../httpchannel/service/AbstractService.java | 47 ++- .../service/AuthenticationService.java | 32 +- .../httpchannel/service/Authenticator.java | 36 +- .../service/AuthenticatorCapability.java | 2 +- .../httpchannel/service/CapabilityMatrix.java | 2 +- .../httpchannel/service/Credential.java | 2 +- .../httpchannel/service/DownloadChannel.java | 3 +- .../httpchannel/service/DownloadListener.java | 2 +- .../httpchannel/service/DownloadService.java | 36 +- .../httpchannel/service/Downloader.java | 107 +++++- .../service/DownloaderCapability.java | 17 +- .../rogiel/httpchannel/service/Service.java | 20 +- .../rogiel/httpchannel/service/ServiceID.java | 4 +- .../httpchannel/service/UploadChannel.java | 6 +- .../httpchannel/service/UploadService.java | 36 +- .../rogiel/httpchannel/service/Uploader.java | 56 ++- .../service/UploaderCapability.java | 2 +- .../httpchannel/service/captcha/Captcha.java | 26 -- .../service/captcha/ImageCaptcha.java | 47 --- .../NullAuthenticatorConfiguration.java | 17 + .../config/NullDownloaderConfiguration.java | 16 + .../config/NullUploaderConfiguration.java | 15 + .../service/config/ServiceConfiguration.java | 41 -- .../config/ServiceConfigurationHelper.java | 113 ------ .../config/ServiceConfigurationProperty.java | 51 --- ...tion.java => InvalidCaptchaException.java} | 12 +- .../UnresolvedCaptchaException.java} | 45 ++- .../helper/AuthenticationServices.java | 38 ++ .../DownloadServices.java} | 22 +- .../service/{ => helper}/Services.java | 12 +- .../UploadServices.java} | 35 +- .../util/transformer/Transformer.java | 25 -- .../util/transformer/TransformerFactory.java | 48 --- .../transformer/impl/BooleanTransformer.java | 30 -- .../transformer/impl/IntegerTransformer.java | 30 -- .../transformer/impl/LongTransformer.java | 30 -- .../transformer/impl/StringTransformer.java | 30 -- .../util/transformer/impl/URLTransformer.java | 39 -- .../httpchannel-captcha-recaptcha/pom.xml | 24 ++ .../httpchannel/captcha/impl/ReCaptcha.java | 15 + .../captcha/impl/ReCaptchaService.java | 44 +++ httpchannel-capcha/pom.xml | 16 + httpchannel-channelcopy/pom.xml | 31 ++ .../rogiel/httpchannel/copy/ChannelCopy.java | 206 ++++++++++ .../copy/exception/ChannelCopyException.java | 42 ++ .../exception/NoServiceFoundException.java | 40 ++ .../httpchannel/wirecopy/ChannelCopyTest.java | 20 + .../httpchannel-service-depositfiles/pom.xml | 20 + .../service/impl/DepositFilesService.java | 212 +++++++++++ .../com.rogiel.httpchannel.service.Service | 1 + .../service/impl/DepositFilesServiceTest.java | 39 ++ .../src/test/resources/upload-test-file.txt | 3 + .../httpchannel-service-hotfile/pom.xml | 3 + .../service/impl/HotFileService.java | 166 ++++---- .../service/impl/DiscoveryTest.java | 2 +- .../service/impl/HotFileServiceTest.java | 56 ++- .../service/impl/ServiceCloningTest.java | 41 -- .../httpchannel-service-megaupload/pom.xml | 3 + .../MegaUploadDownloaderConfiguration.java | 16 + .../service/impl/MegaUploadService.java | 219 +++++------ .../impl/MegaUploadUploaderConfiguration.java | 29 ++ .../service/impl/DiscoveryTest.java | 2 +- .../service/impl/MegaUploadServiceTest.java | 77 ++-- .../httpchannel-service-multiupload/pom.xml | 21 + .../service/impl/MultiUploadService.java | 265 +++++++++++++ .../MultiUploadUploaderConfiguration.java | 110 ++++++ .../com.rogiel.httpchannel.service.Service | 1 + .../service/impl/MultiUploadServiceTest.java | 32 ++ .../src/test/resources/upload-test-file.txt | 3 + .../httpchannel-service-uploadking/pom.xml | 13 + .../service/impl/UploadKingService.java | 191 ++++++++++ .../com.rogiel.httpchannel.service.Service | 1 + .../service/impl/UploadKingServiceTest.java | 39 ++ .../src/test/resources/upload-test-file.txt | 3 + httpchannel-service/pom.xml | 3 + httpchannel-tests/pom.xml | 24 ++ .../service/AbstractServiceTest.java | 237 ++++++++++++ .../httpchannel/util/ChecksumUtils.java | 15 + .../src/main/resources/upload-test-file.txt | 3 + httpchannel-util/pom.xml | 4 +- .../captcha/AbstractImageCaptcha.java | 47 +++ .../captcha/AbstractImageCaptchaService.java | 54 +++ .../rogiel/httpchannel/http/GetRequest.java | 30 ++ .../rogiel/httpchannel/http/HttpContext.java | 40 ++ .../http/PostMultipartRequest.java | 48 +++ .../rogiel/httpchannel/http/PostRequest.java | 46 +++ .../com/rogiel/httpchannel/http/Request.java | 65 ++++ .../service/AbstractAuthenticator.java | 39 ++ .../service/AbstractDownloader.java | 131 ++++--- .../service/AbstractHttpDownloader.java | 66 ++++ .../service/AbstractHttpService.java | 154 ++------ .../httpchannel/service/AbstractUploader.java | 58 +++ .../service/channel/LinkedUploadChannel.java | 14 +- .../LinkedUploadChannelContentBody.java | 2 +- .../rogiel/httpchannel/util/ChannelUtils.java | 31 +- .../rogiel/httpchannel/util/ThreadUtils.java | 2 +- .../util/htmlparser/ContainsFilter.java | 20 + .../htmlparser/ContainsInLowerCaseFilter.java | 20 + .../htmlparser/FormActionPatternFilter.java | 24 ++ .../util/htmlparser/FramePatternFilter.java | 26 ++ .../httpchannel/util/htmlparser/HTMLPage.java | 358 +++++++++--------- .../httpchannel/util/htmlparser/IDFilter.java | 26 ++ .../util/htmlparser/ImagePatternFilter.java | 24 ++ .../util/htmlparser/InputIDFilter.java | 26 ++ .../util/htmlparser/InputNameFilter.java | 26 ++ .../htmlparser/InputValuePatternFilter.java | 28 ++ .../util/htmlparser/LinkPatternFilter.java | 24 ++ .../util/htmlparser/NameFilter.java | 26 ++ .../util/htmlparser/ScriptContainsFilter.java | 24 ++ .../util/htmlparser/ScriptSrcFilter.java | 26 ++ pom.xml | 6 +- 117 files changed, 3741 insertions(+), 1335 deletions(-) create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/Captcha.java create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/CaptchaResolver.java create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/CaptchaService.java create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/ImageCaptcha.java create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/ImageCaptchaService.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/Captcha.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/ImageCaptcha.java create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullAuthenticatorConfiguration.java create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullDownloaderConfiguration.java create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullUploaderConfiguration.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfiguration.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfigurationHelper.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfigurationProperty.java rename httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/{DownloadInvalidCaptchaException.java => InvalidCaptchaException.java} (77%) rename httpchannel-api/src/main/java/com/rogiel/httpchannel/{util/transformer/TransformationException.java => service/exception/UnresolvedCaptchaException.java} (59%) create mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/AuthenticationServices.java rename httpchannel-api/src/main/java/com/rogiel/httpchannel/service/{captcha/CaptchaResolver.java => helper/DownloadServices.java} (70%) rename httpchannel-api/src/main/java/com/rogiel/httpchannel/service/{ => helper}/Services.java (88%) rename httpchannel-api/src/main/java/com/rogiel/httpchannel/service/{ServiceHelper.java => helper/UploadServices.java} (50%) delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/Transformer.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/TransformerFactory.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/BooleanTransformer.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/IntegerTransformer.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/LongTransformer.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/StringTransformer.java delete mode 100644 httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/URLTransformer.java create mode 100644 httpchannel-capcha/httpchannel-captcha-recaptcha/pom.xml create mode 100644 httpchannel-capcha/httpchannel-captcha-recaptcha/src/main/java/com/rogiel/httpchannel/captcha/impl/ReCaptcha.java create mode 100644 httpchannel-capcha/httpchannel-captcha-recaptcha/src/main/java/com/rogiel/httpchannel/captcha/impl/ReCaptchaService.java create mode 100644 httpchannel-capcha/pom.xml create mode 100644 httpchannel-channelcopy/pom.xml create mode 100644 httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/ChannelCopy.java create mode 100644 httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/exception/ChannelCopyException.java create mode 100644 httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/exception/NoServiceFoundException.java create mode 100644 httpchannel-channelcopy/src/test/java/com/rogiel/httpchannel/wirecopy/ChannelCopyTest.java create mode 100644 httpchannel-service/httpchannel-service-depositfiles/pom.xml create mode 100644 httpchannel-service/httpchannel-service-depositfiles/src/main/java/com/rogiel/httpchannel/service/impl/DepositFilesService.java create mode 100644 httpchannel-service/httpchannel-service-depositfiles/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service create mode 100644 httpchannel-service/httpchannel-service-depositfiles/src/test/java/com/rogiel/httpchannel/service/impl/DepositFilesServiceTest.java create mode 100644 httpchannel-service/httpchannel-service-depositfiles/src/test/resources/upload-test-file.txt delete mode 100644 httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/ServiceCloningTest.java create mode 100644 httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadDownloaderConfiguration.java create mode 100644 httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadUploaderConfiguration.java create mode 100644 httpchannel-service/httpchannel-service-multiupload/pom.xml create mode 100644 httpchannel-service/httpchannel-service-multiupload/src/main/java/com/rogiel/httpchannel/service/impl/MultiUploadService.java create mode 100644 httpchannel-service/httpchannel-service-multiupload/src/main/java/com/rogiel/httpchannel/service/impl/MultiUploadUploaderConfiguration.java create mode 100644 httpchannel-service/httpchannel-service-multiupload/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service create mode 100644 httpchannel-service/httpchannel-service-multiupload/src/test/java/com/rogiel/httpchannel/service/impl/MultiUploadServiceTest.java create mode 100644 httpchannel-service/httpchannel-service-multiupload/src/test/resources/upload-test-file.txt create mode 100644 httpchannel-service/httpchannel-service-uploadking/pom.xml create mode 100644 httpchannel-service/httpchannel-service-uploadking/src/main/java/com/rogiel/httpchannel/service/impl/UploadKingService.java create mode 100644 httpchannel-service/httpchannel-service-uploadking/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service create mode 100644 httpchannel-service/httpchannel-service-uploadking/src/test/java/com/rogiel/httpchannel/service/impl/UploadKingServiceTest.java create mode 100644 httpchannel-service/httpchannel-service-uploadking/src/test/resources/upload-test-file.txt create mode 100644 httpchannel-tests/pom.xml create mode 100644 httpchannel-tests/src/main/java/com/rogiel/httpchannel/service/AbstractServiceTest.java create mode 100644 httpchannel-tests/src/main/java/com/rogiel/httpchannel/util/ChecksumUtils.java create mode 100644 httpchannel-tests/src/main/resources/upload-test-file.txt create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/captcha/AbstractImageCaptcha.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/captcha/AbstractImageCaptchaService.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/http/GetRequest.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/http/HttpContext.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/http/PostMultipartRequest.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/http/PostRequest.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/http/Request.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractAuthenticator.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractHttpDownloader.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractUploader.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ContainsFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ContainsInLowerCaseFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/FormActionPatternFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/FramePatternFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/IDFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ImagePatternFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputIDFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputNameFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputValuePatternFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/LinkPatternFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/NameFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ScriptContainsFilter.java create mode 100644 httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ScriptSrcFilter.java diff --git a/httpchannel-api/pom.xml b/httpchannel-api/pom.xml index 2a711c9..1851b80 100644 --- a/httpchannel-api/pom.xml +++ b/httpchannel-api/pom.xml @@ -1,10 +1,17 @@ - - 4.0.0 - - httpchannel - com.rogiel.httpchannel - 1.0.0 - .. - - httpchannel-api + + 4.0.0 + + httpchannel + com.rogiel.httpchannel + 1.0.0 + .. + + httpchannel-api + HttpChannel/API + 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. + +One of the most interesting usages of channels for uploads and download is that you can easily copy data straight from one channel to the other, with less than 10 lines of code! Also, channels allows the implementation of a "tee" mechanism, in which data redden from a single channel can be copied to several other channels on the fly! \ No newline at end of file diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/Captcha.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/Captcha.java new file mode 100644 index 0000000..2ce6aa4 --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/Captcha.java @@ -0,0 +1,33 @@ +/** + * + */ +package com.rogiel.httpchannel.captcha; + +/** + * @author Rogiel + * + */ +public interface Captcha { + /** + * Sets the captcha answer + * + * @param answer + * the captcha answer + */ + void setAnswer(String answer); + + /** + * Returns the captcha answer. null 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 true if, and only if, the service was able to + * automatically resolve the captcha result + */ + boolean wasAutomaticallyResolved(); +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/CaptchaResolver.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/CaptchaResolver.java new file mode 100644 index 0000000..60141e4 --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/CaptchaResolver.java @@ -0,0 +1,19 @@ +/** + * + */ +package com.rogiel.httpchannel.captcha; + +/** + * @author Rogiel + */ +public interface CaptchaResolver { + /** + * Tries to resolve the captcha + * + * @param captcha + * the captcha + * @return true if the captcha was resolve, false + * to abort + */ + boolean resolve(Captcha captcha); +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/CaptchaService.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/CaptchaService.java new file mode 100644 index 0000000..6ec0439 --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/CaptchaService.java @@ -0,0 +1,11 @@ +/** + * + */ +package com.rogiel.httpchannel.captcha; + +/** + * @author Rogiel + * + */ +public interface CaptchaService { +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/ImageCaptcha.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/ImageCaptcha.java new file mode 100644 index 0000000..6094cdc --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/ImageCaptcha.java @@ -0,0 +1,22 @@ +/** + * + */ +package com.rogiel.httpchannel.captcha; + +import java.net.URL; + +/** + * @author Rogiel + * + */ +public interface ImageCaptcha extends Captcha { + /** + * @return the captcha identifier + */ + String getID(); + + /** + * @return the captcha image {@link URL} + */ + URL getImageURL(); +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/ImageCaptchaService.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/ImageCaptchaService.java new file mode 100644 index 0000000..966860b --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/captcha/ImageCaptchaService.java @@ -0,0 +1,31 @@ +/** + * + */ +package com.rogiel.httpchannel.captcha; + +import java.net.URL; + +/** + * @author Rogiel + * + */ +public interface ImageCaptchaService extends + CaptchaService { + /** + * 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 true if the captcha was successfully resolved + */ + boolean resolve(C captcha); +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AbstractService.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AbstractService.java index e66d10f..d70838b 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AbstractService.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AbstractService.java @@ -16,37 +16,18 @@ */ 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. * - * @author Rogiel + * @author Rogiel * @version 1.0 - * @param - * The {@link ServiceConfiguration} interface type used by the - * {@link Service}. Note that this must be an interface!s - * @see ServiceConfiguration ServiceConfiguration for details on the - * configuration interface. */ -public abstract class AbstractService - implements Service { - 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; - } +public abstract class AbstractService implements Service { + protected CaptchaResolver captchaResolver; @Override public Service clone() { @@ -56,4 +37,20 @@ public abstract class AbstractService 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; + } } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AuthenticationService.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AuthenticationService.java index 4204a14..459ab94 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AuthenticationService.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AuthenticationService.java @@ -16,13 +16,29 @@ */ 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. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public interface AuthenticationService extends Service { +public interface AuthenticationService + extends Service { + /** + * Creates {@link Authenticator} instance for this service. This instance is + * attached to an {@link Credential} and to its parent {@link Service}. + * + * @param credential + * the credential + * @param configuration + * the authenticator configuration + * @return an new {@link Authenticator} instance + */ + Authenticator getAuthenticator(Credential credential, C configuration); + /** * Creates {@link Authenticator} instance for this service. This instance is * attached to an {@link Credential} and to its parent {@link Service}. @@ -31,7 +47,17 @@ public interface AuthenticationService extends Service { * the credential * @return an new {@link Authenticator} instance */ - Authenticator getAuthenticator(Credential credential); + Authenticator 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}. diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Authenticator.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Authenticator.java index b2284ce..4bf92c2 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Authenticator.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Authenticator.java @@ -18,15 +18,18 @@ package com.rogiel.httpchannel.service; 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.UnresolvedCaptchaException; /** * This interfaces provides authentication for an service. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public interface Authenticator { +public interface Authenticator { /** * Login into the {@link Service}. Once the authentication is done, it is * persistent for the entire service's operation.
@@ -37,8 +40,13 @@ public interface Authenticator { * if any IO error occur * @throws AuthenticationInvalidCredentialException * 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 @@ -48,4 +56,26 @@ public interface Authenticator { * if any IO error occur */ void logout() throws IOException; + + /** + * Returns this {@link Authenticator} configuration. + *

+ * IMPORTANT NOTE: 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 Rogiel + */ + public interface AuthenticatorConfiguration { + + } } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AuthenticatorCapability.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AuthenticatorCapability.java index d9ae9c6..213a230 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AuthenticatorCapability.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/AuthenticatorCapability.java @@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service; /** * Capability an certain {@link Authenticator} can have. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ public enum AuthenticatorCapability { diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/CapabilityMatrix.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/CapabilityMatrix.java index bbc1aa0..d31f5ff 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/CapabilityMatrix.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/CapabilityMatrix.java @@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service; /** * This is an utility class to help manage Capabilities of all the services. * - * @author Rogiel + * @author Rogiel * @param * the capability enumeration * @since 1.0 diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Credential.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Credential.java index e91661e..54b97dd 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Credential.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Credential.java @@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service; /** * Pair of username-password used for authenticating into services. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ public class Credential { diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadChannel.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadChannel.java index a32229d..5eda04a 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadChannel.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadChannel.java @@ -16,6 +16,7 @@ */ package com.rogiel.httpchannel.service; +import java.io.Closeable; import java.nio.channels.Channel; import java.nio.channels.ReadableByteChannel; @@ -31,7 +32,7 @@ import java.nio.channels.ReadableByteChannel; * * @author Rogiel */ -public interface DownloadChannel extends ReadableByteChannel { +public interface DownloadChannel extends ReadableByteChannel, Closeable { /** * @return the file size */ diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadListener.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadListener.java index c799e10..ec45b73 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadListener.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadListener.java @@ -20,7 +20,7 @@ package com.rogiel.httpchannel.service; * This listener keeps an track on the progress on an {@link Downloader} * service. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ public interface DownloadListener { diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadService.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadService.java index f96826e..bad72e1 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadService.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloadService.java @@ -20,13 +20,17 @@ import java.net.URL; 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. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public interface DownloadService extends Service { +public interface DownloadService extends + Service { /** * Creates a new instance of the {@link Downloader}. This instance will be * attached to the {@link URL}, {@link FileObject} provided through the the @@ -34,14 +38,34 @@ public interface DownloadService extends Service { * * @param url * the url to be downloaded - * @param file - * the destination file + * @param configuration + * the downloader configurationf * @return an new instance of {@link Downloader} */ - Downloader getDownloader(URL url); + Downloader 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 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. *

* Please note that the value returned by this method may vary based diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Downloader.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Downloader.java index 1358a94..b701c41 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Downloader.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Downloader.java @@ -18,18 +18,21 @@ package com.rogiel.httpchannel.service; 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.DownloadLinkNotFoundException; import com.rogiel.httpchannel.service.exception.DownloadNotAuthorizedException; import com.rogiel.httpchannel.service.exception.DownloadNotResumableException; +import com.rogiel.httpchannel.service.exception.UnresolvedCaptchaException; /** * This interfaces provides downloading for an service. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public interface Downloader { +public interface Downloader { /** * Opens a new {@link DownloadChannel} that will be immediately ready to * read data from the download stream. @@ -67,9 +70,105 @@ public interface Downloader { * @throws DownloadNotResumableException * if the download cannot be started at position. Will * only be thrown if position > 0. + * @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, 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 position. Will + * only be thrown if position > 0. + */ + 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. + *

+ * IMPORTANT NOTE: 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 Rogiel + */ + public interface DownloaderConfiguration { + + } } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloaderCapability.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloaderCapability.java index c7e45a2..151ce6e 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloaderCapability.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/DownloaderCapability.java @@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service; /** * Capability an certain {@link Downloader} can have. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ public enum DownloaderCapability { @@ -35,10 +35,21 @@ public enum DownloaderCapability { * Can download files while authenticated with premium account */ 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. */ diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Service.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Service.java index 4be10d1..ba652ed 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Service.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Service.java @@ -16,7 +16,7 @@ */ 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 @@ -27,7 +27,7 @@ import com.rogiel.httpchannel.service.config.ServiceConfiguration; * * * - * @author Rogiel + * @author Rogiel * @since 1.0 */ public interface Service extends Cloneable { @@ -53,19 +53,13 @@ public interface Service extends Cloneable { 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(); - - /** - * Sets this {@link ServiceConfiguration} instance - * - * @param configuration - * the {@link ServiceConfiguration} instance - */ - void setServiceConfiguration(ServiceConfiguration configuration); + void setCaptchaResolver(CaptchaResolver resolver); /** * @return a cloned version of this service diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/ServiceID.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/ServiceID.java index 6a5ea29..499e8e0 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/ServiceID.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/ServiceID.java @@ -18,6 +18,8 @@ package com.rogiel.httpchannel.service; import java.io.Serializable; +import com.rogiel.httpchannel.service.helper.Services; + /** * An ID used to represent the given service * @@ -27,7 +29,7 @@ public class ServiceID implements Serializable, Cloneable { /** * This class serialization version UID */ - private static final long serialVersionUID = -1078456596792552200L; + private static final long serialVersionUID = 1L; /** * The raw ID diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploadChannel.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploadChannel.java index 71b1873..f2ed303 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploadChannel.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploadChannel.java @@ -16,7 +16,9 @@ */ package com.rogiel.httpchannel.service; +import java.io.Closeable; import java.io.IOException; +import java.net.URL; import java.nio.channels.Channel; import java.nio.channels.WritableByteChannel; @@ -34,7 +36,7 @@ import com.rogiel.httpchannel.service.exception.UploadLinkNotFoundException; * * @author Rogiel */ -public interface UploadChannel extends WritableByteChannel { +public interface UploadChannel extends WritableByteChannel, Closeable { /** * @return the file size */ @@ -52,7 +54,7 @@ public interface UploadChannel extends WritableByteChannel { * * @return the download link for this upload */ - String getDownloadLink(); + URL getDownloadLink(); /** * @throws UploadLinkNotFoundException diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploadService.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploadService.java index 8300d3b..9307390 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploadService.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploadService.java @@ -16,13 +16,16 @@ */ 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. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public interface UploadService extends Service { +public interface UploadService extends Service { /** * Creates a new instance of {@link Uploader}. This instance is attached * with the parent {@link Service} instance.
@@ -32,11 +35,34 @@ public interface UploadService extends Service { * the name of the file to be uploaded * @param filesize * the size of the file to be uploaded. This must be exact. - * @param description - * the description of the upload. If supported. + * @param configuration + * the uploader configuration * @return the new {@link Uploader} instance */ - Uploader getUploader(String filename, long filesize, String description); + Uploader getUploader(String filename, long filesize, C configuration); + + /** + * Creates a new instance of {@link Uploader}. This instance is attached + * with the parent {@link Service} instance.
+ * Note: not all services might support description + * + * @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 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. diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Uploader.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Uploader.java index cc2ad9c..074c62e 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Uploader.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Uploader.java @@ -18,13 +18,17 @@ package com.rogiel.httpchannel.service; 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. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public interface Uploader { +public interface Uploader { /** * Opens a new {@link UploadChannel} that will be immediately ready to * receive data to be sent to the upload stream. @@ -44,6 +48,52 @@ public interface Uploader { * @return the {@link UploadChannel} instance * @throws IOException * 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. + *

+ * IMPORTANT NOTE: 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 Rogiel + */ + public interface UploaderConfiguration { + } + + /** + * Defines an {@link UploaderConfiguration} that can allow at least + * an description field + * + * @author Rogiel + */ + 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); + } } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploaderCapability.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploaderCapability.java index a96c57b..010e367 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploaderCapability.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/UploaderCapability.java @@ -19,7 +19,7 @@ package com.rogiel.httpchannel.service; /** * Capability an certain {@link Uploader} can have. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ public enum UploaderCapability { diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/Captcha.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/Captcha.java deleted file mode 100644 index 9778c67..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/Captcha.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * This file is part of 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 . - */ -package com.rogiel.httpchannel.service.captcha; - -/** - * @author Rogiel - * @since 1.0 - */ -public interface Captcha { - String getAnswer(); - void setAnswer(String answer); -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/ImageCaptcha.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/ImageCaptcha.java deleted file mode 100644 index 832d8ae..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/ImageCaptcha.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of 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 . - */ -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; - } -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullAuthenticatorConfiguration.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullAuthenticatorConfiguration.java new file mode 100644 index 0000000..f2832e4 --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullAuthenticatorConfiguration.java @@ -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 Rogiel + */ +public final class NullAuthenticatorConfiguration implements + AuthenticatorConfiguration { + public static final NullAuthenticatorConfiguration SHARED_INSTANCE = new NullAuthenticatorConfiguration(); +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullDownloaderConfiguration.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullDownloaderConfiguration.java new file mode 100644 index 0000000..4a0ae36 --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullDownloaderConfiguration.java @@ -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 Rogiel + */ +public final class NullDownloaderConfiguration implements + DownloaderConfiguration { + public static final NullDownloaderConfiguration SHARED_INSTANCE = new NullDownloaderConfiguration(); +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullUploaderConfiguration.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullUploaderConfiguration.java new file mode 100644 index 0000000..105ced4 --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/NullUploaderConfiguration.java @@ -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 Rogiel + */ +public final class NullUploaderConfiguration implements UploaderConfiguration { + public static final NullUploaderConfiguration SHARED_INSTANCE = new NullUploaderConfiguration(); +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfiguration.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfiguration.java deleted file mode 100644 index 399831a..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfiguration.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of 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 . - */ -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.
- *
- * Every service must create an interface 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.
- *
- * 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 { -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfigurationHelper.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfigurationHelper.java deleted file mode 100644 index ed92903..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfigurationHelper.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of 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 . - */ -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 - * 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 defaultConfiguration( - Class 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 - * 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 file(Class 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; - } - }); - } -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfigurationProperty.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfigurationProperty.java deleted file mode 100644 index b5d00f5..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/config/ServiceConfigurationProperty.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of 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 . - */ -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.
- *
- *

Usage example

- * - *
- * public interface DummyServiceConfiguration extends ServiceConfiguration {
- * 	@ServiceConfigurationProperty(defaultValue = "true")
- * 	boolean retryAllowed();
- * }
- * 
- * - * The default implementation created by - * {@link ServiceConfigurationHelper#defaultConfiguration()} will always return - * the defaultValue. - * - * @author Rogiel - * @version 1.0 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface ServiceConfigurationProperty { - String key(); - String defaultValue(); -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/DownloadInvalidCaptchaException.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/InvalidCaptchaException.java similarity index 77% rename from httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/DownloadInvalidCaptchaException.java rename to httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/InvalidCaptchaException.java index d1e3ca8..c3b227e 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/DownloadInvalidCaptchaException.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/InvalidCaptchaException.java @@ -16,7 +16,7 @@ */ 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 @@ -24,13 +24,13 @@ import com.rogiel.httpchannel.service.captcha.CaptchaResolver; * * @author Rogiel */ -public class DownloadInvalidCaptchaException extends DownloadServiceException { +public class InvalidCaptchaException extends ChannelServiceException { private static final long serialVersionUID = 1L; /** * Creates a new empty instance of this exception */ - public DownloadInvalidCaptchaException() { + public InvalidCaptchaException() { super(); } @@ -40,7 +40,7 @@ public class DownloadInvalidCaptchaException extends DownloadServiceException { * @param cause * the root cause */ - public DownloadInvalidCaptchaException(String message, Throwable cause) { + public InvalidCaptchaException(String message, Throwable cause) { super(message, cause); } @@ -48,7 +48,7 @@ public class DownloadInvalidCaptchaException extends DownloadServiceException { * @param message * the message */ - public DownloadInvalidCaptchaException(String message) { + public InvalidCaptchaException(String message) { super(message); } @@ -56,7 +56,7 @@ public class DownloadInvalidCaptchaException extends DownloadServiceException { * @param cause * the root cause */ - public DownloadInvalidCaptchaException(Throwable cause) { + public InvalidCaptchaException(Throwable cause) { super(cause); } } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/TransformationException.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/UnresolvedCaptchaException.java similarity index 59% rename from httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/TransformationException.java rename to httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/UnresolvedCaptchaException.java index 775b7c1..da86cf0 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/TransformationException.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/exception/UnresolvedCaptchaException.java @@ -14,42 +14,49 @@ * You should have received a copy of the GNU General Public License * along with seedbox. If not, see . */ -package com.rogiel.httpchannel.util.transformer; +package com.rogiel.httpchannel.service.exception; + +import com.rogiel.httpchannel.captcha.CaptchaResolver; /** - * @author Rogiel - * @since 1.0 + * Exception thrown if the {@link CaptchaResolver} has returned an invalid + * captcha + * + * @author Rogiel */ -public class TransformationException extends Exception { +public class UnresolvedCaptchaException extends ChannelServiceException { 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 * the message */ - public TransformationException(String message) { + public UnresolvedCaptchaException(String message) { super(message); } /** * @param cause - * the cause + * the root cause */ - public TransformationException(Throwable cause) { + public UnresolvedCaptchaException(Throwable cause) { super(cause); } - - /** - * @param message - * the message - * @param cause - * the cause - */ - public TransformationException(String message, Throwable cause) { - super(message, cause); - } - } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/AuthenticationServices.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/AuthenticationServices.java new file mode 100644 index 0000000..a059d70 --- /dev/null +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/AuthenticationServices.java @@ -0,0 +1,38 @@ +/* + * This file is part of 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 . + */ +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 Rogiel + */ +public class AuthenticationServices { + public static , C extends AuthenticatorConfiguration> Authenticator authenticate( + S service, C configuration, String username, String password) { + return service.getAuthenticator(new Credential(username, password), + configuration); + } + + public static , C extends AuthenticatorConfiguration> Authenticator authenticate( + S service, String username, String password) { + return service.getAuthenticator(new Credential(username, password)); + } +} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/CaptchaResolver.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/DownloadServices.java similarity index 70% rename from httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/CaptchaResolver.java rename to httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/DownloadServices.java index d19dee0..ae14334 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/captcha/CaptchaResolver.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/DownloadServices.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU General Public License * along with seedbox. If not, see . */ -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 Rogiel - * */ -public interface CaptchaResolver { - /** - * Passes an captcha by parameter and waits for the response of the - * challenge. - * - * @param captcha - * the captcha challenge - */ - String resolve(Captcha captcha); +public class DownloadServices { + public static boolean canDownload(DownloadService service, URL url) + throws IOException { + return service.matchURL(url); + } } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Services.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/Services.java similarity index 88% rename from httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Services.java rename to httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/Services.java index 1037385..9918923 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/Services.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/Services.java @@ -14,12 +14,16 @@ * You should have received a copy of the GNU General Public License * along with seedbox. If not, see . */ -package com.rogiel.httpchannel.service; +package com.rogiel.httpchannel.service.helper; import java.net.URL; import java.util.Iterator; import java.util.ServiceLoader; +import com.rogiel.httpchannel.service.DownloadService; +import com.rogiel.httpchannel.service.Service; +import com.rogiel.httpchannel.service.ServiceID; + /** * @author Rogiel */ @@ -43,12 +47,12 @@ public class Services { * the URL * @return the matched service */ - public static DownloadService matchURL(URL url) { + public static DownloadService matchURL(URL url) { for (final Service service : iterate()) { if (!(service instanceof DownloadService)) continue; - if (((DownloadService) service).matchURL(url)) - return (DownloadService) service; + if (((DownloadService) service).matchURL(url)) + return (DownloadService) service; } return null; } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/ServiceHelper.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/UploadServices.java similarity index 50% rename from httpchannel-api/src/main/java/com/rogiel/httpchannel/service/ServiceHelper.java rename to httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/UploadServices.java index b2265f7..977dc06 100644 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/ServiceHelper.java +++ b/httpchannel-api/src/main/java/com/rogiel/httpchannel/service/helper/UploadServices.java @@ -14,37 +14,34 @@ * You should have received a copy of the GNU General Public License * along with seedbox. If not, see . */ -package com.rogiel.httpchannel.service; +package com.rogiel.httpchannel.service.helper; -import java.io.File; import java.io.IOException; import java.nio.file.Files; 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 Rogiel */ -public class ServiceHelper { - private final Service service; - - /** - * @param service - * the service - */ - public ServiceHelper(Service service) { - this.service = service; +public class UploadServices { + public static , C extends UploaderConfiguration> Uploader upload( + S service, C configuration, Path path) throws IOException { + return service.getUploader(path.getFileName().toString(), + Files.size(path), configuration); } - public UploadChannel upload(Path path, String description) - throws IOException { - return ((UploadService) service).getUploader( - path.getFileName().toString(), Files.size(path), description) - .upload(); + public static , C extends UploaderConfiguration> Uploader upload( + S service, Path path) throws IOException { + return service.getUploader(path.getFileName().toString(), + Files.size(path)); } - public UploadChannel upload(File file, String description) + public static boolean canUpload(UploadService service, Path path) throws IOException { - return ((UploadService) service).getUploader(file.getName(), - file.length(), description).upload(); + return service.getMaximumFilesize() >= Files.size(path); } } diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/Transformer.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/Transformer.java deleted file mode 100644 index 4596845..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/Transformer.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is part of 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 . - */ -package com.rogiel.httpchannel.util.transformer; - -/** - * @author rogiel - * - */ -public interface Transformer { - O transform(String data) throws TransformationException; -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/TransformerFactory.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/TransformerFactory.java deleted file mode 100644 index a7c3221..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/TransformerFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of 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 . - */ -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 Transformer getTransformer(Class type) { - if (String.class.isAssignableFrom(type)) { - return (Transformer) new StringTransformer(); - } else if (Boolean.class.isAssignableFrom(type) || type == Boolean.TYPE) { - return (Transformer) new BooleanTransformer(); - } else if (Integer.class.isAssignableFrom(type) || type == Integer.TYPE) { - return (Transformer) new IntegerTransformer(); - } else if (Long.class.isAssignableFrom(type) || type == Long.TYPE) { - return (Transformer) new LongTransformer(); - } else if (URL.class.isAssignableFrom(type)) { - return (Transformer) new URLTransformer(); - } - return null; - } -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/BooleanTransformer.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/BooleanTransformer.java deleted file mode 100644 index 4951e92..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/BooleanTransformer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of 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 . - */ -package com.rogiel.httpchannel.util.transformer.impl; - -import com.rogiel.httpchannel.util.transformer.Transformer; - -/** - * @author rogiel - * - */ -public class BooleanTransformer implements Transformer { - @Override - public Boolean transform(String data) { - return Boolean.parseBoolean(data); - } -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/IntegerTransformer.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/IntegerTransformer.java deleted file mode 100644 index 0356298..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/IntegerTransformer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of 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 . - */ -package com.rogiel.httpchannel.util.transformer.impl; - -import com.rogiel.httpchannel.util.transformer.Transformer; - -/** - * @author rogiel - * - */ -public class IntegerTransformer implements Transformer { - @Override - public Integer transform(String data) { - return Integer.parseInt(data); - } -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/LongTransformer.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/LongTransformer.java deleted file mode 100644 index 3b9a64a..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/LongTransformer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of 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 . - */ -package com.rogiel.httpchannel.util.transformer.impl; - -import com.rogiel.httpchannel.util.transformer.Transformer; - -/** - * @author rogiel - * - */ -public class LongTransformer implements Transformer { - @Override - public Long transform(String data) { - return Long.parseLong(data); - } -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/StringTransformer.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/StringTransformer.java deleted file mode 100644 index 99221bf..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/StringTransformer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of 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 . - */ -package com.rogiel.httpchannel.util.transformer.impl; - -import com.rogiel.httpchannel.util.transformer.Transformer; - -/** - * @author rogiel - * - */ -public class StringTransformer implements Transformer { - @Override - public String transform(String data) { - return data; - } -} diff --git a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/URLTransformer.java b/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/URLTransformer.java deleted file mode 100644 index eb4046e..0000000 --- a/httpchannel-api/src/main/java/com/rogiel/httpchannel/util/transformer/impl/URLTransformer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of 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 . - */ -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 { - @Override - public URL transform(String data) throws TransformationException { - try { - return new URL(data); - } catch (MalformedURLException e) { - throw new TransformationException(e); - } - } -} diff --git a/httpchannel-capcha/httpchannel-captcha-recaptcha/pom.xml b/httpchannel-capcha/httpchannel-captcha-recaptcha/pom.xml new file mode 100644 index 0000000..9e4a4ac --- /dev/null +++ b/httpchannel-capcha/httpchannel-captcha-recaptcha/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + httpchannel-capcha + com.rogiel.httpchannel + 1.0.0 + .. + + httpchannel-captcha-recaptcha + HttpChannel/CaptchaService/ReCaptcha + This module provides captcha resolving for Google ReCaptcha + + + com.rogiel.httpchannel + httpchannel-api + 1.0.0 + + + com.rogiel.httpchannel + httpchannel-util + 1.0.0 + + + \ No newline at end of file diff --git a/httpchannel-capcha/httpchannel-captcha-recaptcha/src/main/java/com/rogiel/httpchannel/captcha/impl/ReCaptcha.java b/httpchannel-capcha/httpchannel-captcha-recaptcha/src/main/java/com/rogiel/httpchannel/captcha/impl/ReCaptcha.java new file mode 100644 index 0000000..6d3f277 --- /dev/null +++ b/httpchannel-capcha/httpchannel-captcha-recaptcha/src/main/java/com/rogiel/httpchannel/captcha/impl/ReCaptcha.java @@ -0,0 +1,15 @@ +package com.rogiel.httpchannel.captcha.impl; + +import java.net.URL; + +import com.rogiel.httpchannel.captcha.AbstractImageCaptcha; + +/** + * @author Rogiel + * + */ +public class ReCaptcha extends AbstractImageCaptcha { + public ReCaptcha(URL url, String ID) { + super(url, ID); + } +} diff --git a/httpchannel-capcha/httpchannel-captcha-recaptcha/src/main/java/com/rogiel/httpchannel/captcha/impl/ReCaptchaService.java b/httpchannel-capcha/httpchannel-captcha-recaptcha/src/main/java/com/rogiel/httpchannel/captcha/impl/ReCaptchaService.java new file mode 100644 index 0000000..a2ca273 --- /dev/null +++ b/httpchannel-capcha/httpchannel-captcha-recaptcha/src/main/java/com/rogiel/httpchannel/captcha/impl/ReCaptchaService.java @@ -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 Rogiel + * + */ +public class ReCaptchaService extends AbstractImageCaptchaService { + // 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; + } +} diff --git a/httpchannel-capcha/pom.xml b/httpchannel-capcha/pom.xml new file mode 100644 index 0000000..21a669d --- /dev/null +++ b/httpchannel-capcha/pom.xml @@ -0,0 +1,16 @@ + + 4.0.0 + + httpchannel + com.rogiel.httpchannel + 1.0.0 + .. + + httpchannel-capcha + HttpChannel/CaptchaService + This module provides implementations for captcha resolving + pom + + httpchannel-captcha-recaptcha + + \ No newline at end of file diff --git a/httpchannel-channelcopy/pom.xml b/httpchannel-channelcopy/pom.xml new file mode 100644 index 0000000..3ca84d7 --- /dev/null +++ b/httpchannel-channelcopy/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + httpchannel + com.rogiel.httpchannel + 1.0.0 + .. + + httpchannel-channelcopy + HttpChannel/ChannelCopy + The HttpChannel ChannelCopy module provides an easy method to copy data from a single download channel into one or more upload channels. + + + com.rogiel.httpchannel + httpchannel-api + 1.0.0 + + + com.rogiel.httpchannel.services + httpchannel-service-megaupload + 1.0.0 + test + + + com.rogiel.httpchannel.services + httpchannel-service-multiupload + 1.0.0 + + + \ No newline at end of file diff --git a/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/ChannelCopy.java b/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/ChannelCopy.java new file mode 100644 index 0000000..bc8ca13 --- /dev/null +++ b/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/ChannelCopy.java @@ -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}. + *

+ * 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)}. + *

+ * 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 Rogiel + */ +public class ChannelCopy implements Callable> { + /** + * 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 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 , C extends UploaderConfiguration> void addOutput( + S service, C configuration) throws IOException { + addOutput(service.getUploader(filename, filesize, configuration) + .openChannel()); + } + + @Override + public List 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 urls = new ArrayList<>(); + for (final UploadChannel channel : uploadChannels) { + urls.add(channel.getDownloadLink()); + } + + return urls; + } +} diff --git a/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/exception/ChannelCopyException.java b/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/exception/ChannelCopyException.java new file mode 100644 index 0000000..834a885 --- /dev/null +++ b/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/exception/ChannelCopyException.java @@ -0,0 +1,42 @@ +package com.rogiel.httpchannel.copy.exception; + +import com.rogiel.httpchannel.service.exception.ChannelServiceException; + +/** + * @author Rogiel + */ +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); + } +} diff --git a/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/exception/NoServiceFoundException.java b/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/exception/NoServiceFoundException.java new file mode 100644 index 0000000..b3157ee --- /dev/null +++ b/httpchannel-channelcopy/src/main/java/com/rogiel/httpchannel/copy/exception/NoServiceFoundException.java @@ -0,0 +1,40 @@ +package com.rogiel.httpchannel.copy.exception; + +/** + * @author Rogiel + */ +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); + } +} diff --git a/httpchannel-channelcopy/src/test/java/com/rogiel/httpchannel/wirecopy/ChannelCopyTest.java b/httpchannel-channelcopy/src/test/java/com/rogiel/httpchannel/wirecopy/ChannelCopyTest.java new file mode 100644 index 0000000..dcdc611 --- /dev/null +++ b/httpchannel-channelcopy/src/test/java/com/rogiel/httpchannel/wirecopy/ChannelCopyTest.java @@ -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()); + } +} diff --git a/httpchannel-service/httpchannel-service-depositfiles/pom.xml b/httpchannel-service/httpchannel-service-depositfiles/pom.xml new file mode 100644 index 0000000..ba72efa --- /dev/null +++ b/httpchannel-service/httpchannel-service-depositfiles/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + httpchannel-service + com.rogiel.httpchannel + 1.0.0 + .. + + httpchannel-service-depositfiles + com.rogiel.httpchannel.services + HttpChannel/Service/DepositFiles + Provides upload access to depositfiles.com + + + com.rogiel.httpchannel + httpchannel-captcha-recaptcha + 1.0.0 + + + \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-depositfiles/src/main/java/com/rogiel/httpchannel/service/impl/DepositFilesService.java b/httpchannel-service/httpchannel-service-depositfiles/src/main/java/com/rogiel/httpchannel/service/impl/DepositFilesService.java new file mode 100644 index 0000000..735c196 --- /dev/null +++ b/httpchannel-service/httpchannel-service-depositfiles/src/main/java/com/rogiel/httpchannel/service/impl/DepositFilesService.java @@ -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 Rogiel + * @since 1.0 + */ +public class DepositFilesService extends AbstractHttpService implements + Service, UploadService, + AuthenticationService { + /** + * 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 getUploader(String filename, + long filesize, NullUploaderConfiguration configuration) { + return new UploadKingUploader(filename, filesize, configuration); + } + + @Override + public Uploader 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 getUploadCapabilities() { + return new CapabilityMatrix( + UploaderCapability.UNAUTHENTICATED_UPLOAD, + UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD, + UploaderCapability.PREMIUM_ACCOUNT_UPLOAD); + } + + @Override + public Authenticator getAuthenticator( + Credential credential, NullAuthenticatorConfiguration configuration) { + return new UploadKingAuthenticator(credential, configuration); + } + + @Override + public Authenticator getAuthenticator( + Credential credential) { + return getAuthenticator(credential, newAuthenticatorConfiguration()); + } + + @Override + public NullAuthenticatorConfiguration newAuthenticatorConfiguration() { + return NullAuthenticatorConfiguration.SHARED_INSTANCE; + } + + @Override + public CapabilityMatrix getAuthenticationCapability() { + return new CapabilityMatrix(); + } + + protected class UploadKingUploader extends + AbstractUploader implements + Uploader, + LinkedUploadChannelCloseCallback { + private Future 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 implements + Authenticator { + 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 + } + } +} diff --git a/httpchannel-service/httpchannel-service-depositfiles/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service b/httpchannel-service/httpchannel-service-depositfiles/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service new file mode 100644 index 0000000..4013beb --- /dev/null +++ b/httpchannel-service/httpchannel-service-depositfiles/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service @@ -0,0 +1 @@ +com.rogiel.httpchannel.service.impl.MultiUploadService \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-depositfiles/src/test/java/com/rogiel/httpchannel/service/impl/DepositFilesServiceTest.java b/httpchannel-service/httpchannel-service-depositfiles/src/test/java/com/rogiel/httpchannel/service/impl/DepositFilesServiceTest.java new file mode 100644 index 0000000..9eb7353 --- /dev/null +++ b/httpchannel-service/httpchannel-service-depositfiles/src/test/java/com/rogiel/httpchannel/service/impl/DepositFilesServiceTest.java @@ -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); + } +} diff --git a/httpchannel-service/httpchannel-service-depositfiles/src/test/resources/upload-test-file.txt b/httpchannel-service/httpchannel-service-depositfiles/src/test/resources/upload-test-file.txt new file mode 100644 index 0000000..3cfccc8 --- /dev/null +++ b/httpchannel-service/httpchannel-service-depositfiles/src/test/resources/upload-test-file.txt @@ -0,0 +1,3 @@ +This is a simple upload test file. + +This is for testing purposes only. \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-hotfile/pom.xml b/httpchannel-service/httpchannel-service-hotfile/pom.xml index 393ab43..f799833 100644 --- a/httpchannel-service/httpchannel-service-hotfile/pom.xml +++ b/httpchannel-service/httpchannel-service-hotfile/pom.xml @@ -7,4 +7,7 @@ .. httpchannel-service-hotfile + com.rogiel.httpchannel.services + HttpChannel/Service/HotFile + Provides download and upload access to hotfile.com \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-hotfile/src/main/java/com/rogiel/httpchannel/service/impl/HotFileService.java b/httpchannel-service/httpchannel-service-hotfile/src/main/java/com/rogiel/httpchannel/service/impl/HotFileService.java index ddb3379..34d3a20 100644 --- a/httpchannel-service/httpchannel-service-hotfile/src/main/java/com/rogiel/httpchannel/service/impl/HotFileService.java +++ b/httpchannel-service/httpchannel-service-hotfile/src/main/java/com/rogiel/httpchannel/service/impl/HotFileService.java @@ -25,12 +25,12 @@ import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import org.apache.http.HttpResponse; 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 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.AbstractUploader; import com.rogiel.httpchannel.service.AuthenticationService; import com.rogiel.httpchannel.service.Authenticator; import com.rogiel.httpchannel.service.AuthenticatorCapability; @@ -50,28 +50,27 @@ import com.rogiel.httpchannel.service.UploaderCapability; import com.rogiel.httpchannel.service.channel.InputStreamDownloadChannel; import com.rogiel.httpchannel.service.channel.LinkedUploadChannel; import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback; -import com.rogiel.httpchannel.service.channel.LinkedUploadChannelContentBody; -import com.rogiel.httpchannel.service.config.ServiceConfiguration; -import com.rogiel.httpchannel.service.config.ServiceConfigurationHelper; +import com.rogiel.httpchannel.service.config.NullAuthenticatorConfiguration; +import com.rogiel.httpchannel.service.config.NullDownloaderConfiguration; +import com.rogiel.httpchannel.service.config.NullUploaderConfiguration; 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; /** * This service handles login, upload and download to HotFile.com. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public class HotFileService extends - AbstractHttpService implements Service, - UploadService, DownloadService, AuthenticationService { +public class HotFileService extends AbstractHttpService implements Service, + UploadService, + DownloadService, + AuthenticationService { /** * This service ID */ public static final ServiceID SERVICE_ID = ServiceID.create("hotfile"); - + private static final Pattern UPLOAD_URL_PATTERN = Pattern .compile("http://u[0-9]*\\.hotfile\\.com/upload\\.cgi\\?[0-9]*"); @@ -85,11 +84,6 @@ public class HotFileService extends private static final Pattern DOWNLOAD_URL_PATTERN = Pattern .compile("http://hotfile\\.com/dl/([0-9]*)/([A-Za-z0-9]*)/(.*)"); - public HotFileService() { - super(ServiceConfigurationHelper - .defaultConfiguration(HotFileServiceConfiguration.class)); - } - @Override public ServiceID getID() { return SERVICE_ID; @@ -106,9 +100,20 @@ public class HotFileService extends } @Override - public Uploader getUploader(String filename, long filesize, - String description) { - return new HotFileUploader(filename, filesize); + public Uploader getUploader(String filename, + long filesize, NullUploaderConfiguration configuration) { + return new HotFileUploader(filename, filesize, configuration); + } + + @Override + public Uploader getUploader(String filename, + long filesize) { + return getUploader(filename, filesize, newUploaderConfiguration()); + } + + @Override + public NullUploaderConfiguration newUploaderConfiguration() { + return NullUploaderConfiguration.SHARED_INSTANCE; } @Override @@ -130,8 +135,19 @@ public class HotFileService extends } @Override - public Downloader getDownloader(URL url) { - return new HotFileDownloader(url); + public Downloader getDownloader(URL url, + NullDownloaderConfiguration configuration) { + return new HotFileDownloader(url, configuration); + } + + @Override + public Downloader getDownloader(URL url) { + return getDownloader(url, newDownloaderConfiguration()); + } + + @Override + public NullDownloaderConfiguration newDownloaderConfiguration() { + return NullDownloaderConfiguration.SHARED_INSTANCE; } @Override @@ -146,8 +162,20 @@ public class HotFileService extends } @Override - public Authenticator getAuthenticator(Credential credential) { - return new HotFileAuthenticator(credential); + public Authenticator getAuthenticator( + Credential credential, NullAuthenticatorConfiguration configuration) { + return new HotFileAuthenticator(credential, configuration); + } + + @Override + public Authenticator getAuthenticator( + Credential credential) { + return getAuthenticator(credential, newAuthenticatorConfiguration()); + } + + @Override + public NullAuthenticatorConfiguration newAuthenticatorConfiguration() { + return NullAuthenticatorConfiguration.SHARED_INSTANCE; } @Override @@ -155,36 +183,27 @@ public class HotFileService extends return new CapabilityMatrix(); } - protected class HotFileUploader implements Uploader, + protected class HotFileUploader extends + AbstractUploader implements + Uploader, LinkedUploadChannelCloseCallback { - private final String filename; - private final long filesize; - private Future uploadFuture; - public HotFileUploader(String filename, long filesize) { - super(); - this.filename = filename; - this.filesize = filesize; + public HotFileUploader(String filename, long filesize, + NullUploaderConfiguration configuration) { + super(filename, filesize, configuration); } @Override - public UploadChannel upload() throws IOException { - final HTMLPage page = getAsPage("http://www.hotfile.com/"); - final String action = page.getFormAction(UPLOAD_URL_PATTERN); + public UploadChannel openChannel() throws IOException { + final HTMLPage page = get("http://www.hotfile.com/").asPage(); + final String action = page.findFormAction(UPLOAD_URL_PATTERN); - final LinkedUploadChannel channel = new LinkedUploadChannel(this, - filesize, filename); - final MultipartEntity entity = new MultipartEntity(); + final LinkedUploadChannel channel = createLinkedChannel(this); - entity.addPart("uploads[]", new LinkedUploadChannelContentBody( - channel)); - - uploadFuture = postAsPageAsync(action, entity); - while (!channel.isLinked() && !uploadFuture.isDone()) { - ThreadUtils.sleep(100); - } - return channel; + uploadFuture = multipartPost(action) + .parameter("uploads[]", channel).asPageAsync(); + return waitChannelLink(channel, uploadFuture); } @Override @@ -199,17 +218,17 @@ public class HotFileService extends } } - protected class HotFileDownloader extends AbstractDownloader { - private final URL url; - - public HotFileDownloader(URL url) { - this.url = url; + protected class HotFileDownloader extends + AbstractHttpDownloader { + public HotFileDownloader(URL url, + NullDownloaderConfiguration configuration) { + super(url, configuration); } @Override - public DownloadChannel download(DownloadListener listener, long position) - throws IOException { - final HTMLPage page = getAsPage(url.toString()); + public DownloadChannel openChannel(DownloadListener listener, + long position) throws IOException { + final HTMLPage page = get(url).asPage(); // // try to find timer // final String stringTimer = PatternUtils.find(DOWNLOAD_TIMER, @@ -224,11 +243,12 @@ public class HotFileService extends // } final String downloadUrl = page - .getLink(DOWNLOAD_DIRECT_LINK_PATTERN); + .findLink(DOWNLOAD_DIRECT_LINK_PATTERN); // final String tmHash = PatternUtils.find(DOWNLOAD_TMHASH_PATTERN, // content);F if (downloadUrl != null && downloadUrl.length() > 0) { - final HttpResponse downloadResponse = get(downloadUrl); + final HttpResponse downloadResponse = get(downloadUrl) + .request(); final String filename = FilenameUtils.getName(downloadUrl); long contentLength = getContentLength(downloadResponse); @@ -284,23 +304,21 @@ public class HotFileService extends } } - protected class HotFileAuthenticator implements Authenticator { - private final Credential credential; - - public HotFileAuthenticator(Credential credential) { - this.credential = credential; + protected class HotFileAuthenticator extends + AbstractAuthenticator implements + Authenticator { + public HotFileAuthenticator(Credential credential, + NullAuthenticatorConfiguration configuration) { + super(credential, configuration); } @Override 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"); if (accountTag == null) throw new AuthenticationInvalidCredentialException(); @@ -308,18 +326,12 @@ public class HotFileService extends @Override public void logout() throws IOException { - final MultipartEntity entity = new MultipartEntity(); - entity.addPart("logout", new StringBody("1")); - - postAsString("http://www.megaupload.com/?c=account", entity); + post("http://www.megaupload.com/?c=account").parameter("logout", + true).request(); // TODO check logout status } } - public static interface HotFileServiceConfiguration extends - ServiceConfiguration { - } - @Override public String toString() { return this.getClass().getSimpleName() + " " + getMajorVersion() + "." diff --git a/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/DiscoveryTest.java b/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/DiscoveryTest.java index 5034e69..6ca40f7 100644 --- a/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/DiscoveryTest.java +++ b/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/DiscoveryTest.java @@ -20,7 +20,7 @@ import junit.framework.Assert; import org.junit.Test; -import com.rogiel.httpchannel.service.Services; +import com.rogiel.httpchannel.service.helper.Services; /** * @author Rogiel diff --git a/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/HotFileServiceTest.java b/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/HotFileServiceTest.java index afefead..2ff56ad 100644 --- a/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/HotFileServiceTest.java +++ b/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/HotFileServiceTest.java @@ -22,8 +22,6 @@ import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.SeekableByteChannel; @@ -38,23 +36,19 @@ import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Test; -import com.rogiel.httpchannel.service.AuthenticationService; import com.rogiel.httpchannel.service.Credential; import com.rogiel.httpchannel.service.DownloadChannel; 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.Services; import com.rogiel.httpchannel.service.UploadChannel; -import com.rogiel.httpchannel.service.UploadService; import com.rogiel.httpchannel.service.UploaderCapability; 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; public class HotFileServiceTest { - private Service service; - private ServiceHelper helper; + private HotFileService service; /** * See src/test/resources/config/hotfile.properties @@ -76,7 +70,6 @@ public class HotFileServiceTest { public void setUp() throws Exception { // MegaUploadServiceConfiguration.class; service = new HotFileService(); - helper = new ServiceHelper(service); final Properties properties = new Properties(); properties.load(new FileInputStream( @@ -87,33 +80,31 @@ public class HotFileServiceTest { @Test public void testServiceId() { - System.out.println("Service: " + service.toString()); assertEquals(ServiceID.create("hotfile"), service.getID()); } @Test public void testValidAuthenticator() throws IOException { - ((AuthenticationService) service).getAuthenticator( - new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); + service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD)) + .login(); } @Test(expected = AuthenticationInvalidCredentialException.class) public void testInvalidAuthenticator() throws IOException { - ((AuthenticationService) service).getAuthenticator( + service.getAuthenticator( new Credential(INVALID_USERNAME, INVALID_PASSWORD)).login(); } @Test - public void testNonLoguedInUploader() throws IOException, - URISyntaxException { + public void testNonLoguedInUploader() throws IOException { assertTrue( "This service does not have the capability UploadCapability.FREE_UPLOAD", - ((UploadService) service).getUploadCapabilities().has( + service.getUploadCapabilities().has( UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD)); final Path path = Paths.get("src/test/resources/upload-test-file.txt"); - final UploadChannel channel = helper.upload(path, - "httpchannel test upload"); + final UploadChannel channel = UploadServices.upload(service, path) + .openChannel(); final SeekableByteChannel inChannel = Files.newByteChannel(path); try { @@ -131,15 +122,15 @@ public class HotFileServiceTest { public void testLoguedInUploader() throws IOException { assertTrue( "This service does not have the capability UploadCapability.PREMIUM_UPLOAD", - ((UploadService) service).getUploadCapabilities().has( + service.getUploadCapabilities().has( UploaderCapability.PREMIUM_ACCOUNT_UPLOAD)); - ((AuthenticationService) service).getAuthenticator( - new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); + service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD)) + .login(); final Path path = Paths.get("src/test/resources/upload-test-file.txt"); - final UploadChannel channel = helper.upload(path, - "httpchannel test upload"); + final UploadChannel channel = UploadServices.upload(service, path) + .openChannel(); final SeekableByteChannel inChannel = Files.newByteChannel(path); try { @@ -154,30 +145,29 @@ public class HotFileServiceTest { } @Test - public void testDownloader() throws IOException, MalformedURLException { + public void testDownloader() throws IOException { final URL downloadUrl = new URL( "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) - .download(null, 0); + .openChannel(null, 0); final ByteArrayOutputStream bout = new ByteArrayOutputStream(); IOUtils.copy(Channels.newInputStream(channel), bout); System.out.println(bout.size()); } @Test - public void testLoggedInDownloader() throws IOException, - MalformedURLException { - ((AuthenticationService) service).getAuthenticator( - new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); + public void testLoggedInDownloader() throws IOException { + service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD)) + .login(); - final DownloadChannel channel = ((DownloadService) service) + final DownloadChannel channel = service .getDownloader( new URL( "http://hotfile.com/dl/129251605/9b4faf2/simulado_2010_1_res_all.zip.html")) - .download(null, 0); + .openChannel(null, 0); final ByteArrayOutputStream bout = new ByteArrayOutputStream(); IOUtils.copy(Channels.newInputStream(channel), bout); diff --git a/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/ServiceCloningTest.java b/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/ServiceCloningTest.java deleted file mode 100644 index 67f9468..0000000 --- a/httpchannel-service/httpchannel-service-hotfile/src/test/java/com/rogiel/httpchannel/service/impl/ServiceCloningTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of 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 . - */ -package com.rogiel.httpchannel.service.impl; - -import org.junit.Assert; -import org.junit.Test; - -import com.rogiel.httpchannel.service.Service; - -/** - * @author Rogiel - * - */ -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()); - } -} diff --git a/httpchannel-service/httpchannel-service-megaupload/pom.xml b/httpchannel-service/httpchannel-service-megaupload/pom.xml index 1b29388..cd94c3c 100644 --- a/httpchannel-service/httpchannel-service-megaupload/pom.xml +++ b/httpchannel-service/httpchannel-service-megaupload/pom.xml @@ -7,4 +7,7 @@ .. httpchannel-service-megaupload + com.rogiel.httpchannel.services + HttpChannel/Service/MegaUpload + Provides upload and download access to megaupload.com \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadDownloaderConfiguration.java b/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadDownloaderConfiguration.java new file mode 100644 index 0000000..dbf4313 --- /dev/null +++ b/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadDownloaderConfiguration.java @@ -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; + } +} diff --git a/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadService.java b/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadService.java index ada154d..8026d0c 100644 --- a/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadService.java +++ b/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadService.java @@ -18,8 +18,6 @@ package com.rogiel.httpchannel.service.impl; import java.io.IOException; import java.net.URL; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.regex.Pattern; @@ -27,14 +25,11 @@ import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import org.apache.http.HttpResponse; 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.AbstractUploader; import com.rogiel.httpchannel.service.AuthenticationService; import com.rogiel.httpchannel.service.Authenticator; import com.rogiel.httpchannel.service.AuthenticatorCapability; @@ -51,37 +46,31 @@ 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.InputStreamDownloadChannel; import com.rogiel.httpchannel.service.channel.LinkedUploadChannel; import com.rogiel.httpchannel.service.channel.LinkedUploadChannel.LinkedUploadChannelCloseCallback; -import com.rogiel.httpchannel.service.channel.LinkedUploadChannelContentBody; -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.config.NullAuthenticatorConfiguration; 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.UploadLinkNotFoundException; -import com.rogiel.httpchannel.service.impl.MegaUploadService.MegaUploadServiceConfiguration; import com.rogiel.httpchannel.util.HttpClientUtils; import com.rogiel.httpchannel.util.PatternUtils; -import com.rogiel.httpchannel.util.ThreadUtils; import com.rogiel.httpchannel.util.htmlparser.HTMLPage; /** * This service handles login, upload and download to MegaUpload.com. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public class MegaUploadService extends - AbstractHttpService implements Service, - UploadService, DownloadService, AuthenticationService { +public class MegaUploadService extends AbstractHttpService implements Service, + UploadService, + DownloadService, + AuthenticationService { /** * This service ID */ public static final ServiceID SERVICE_ID = ServiceID.create("megaupload"); - + private static final Pattern UPLOAD_URL_PATTERN = Pattern .compile("http://www([0-9]*)\\.megaupload\\.com/upload_done\\.php\\?UPLOAD_IDENTIFIER=[0-9]*"); @@ -98,11 +87,6 @@ public class MegaUploadService extends private static final Pattern LOGIN_USERNAME_PATTERN = Pattern .compile("flashvars\\.username = \"(.*)\";"); - public MegaUploadService() { - super(ServiceConfigurationHelper - .defaultConfiguration(MegaUploadServiceConfiguration.class)); - } - @Override public ServiceID getID() { return SERVICE_ID; @@ -119,9 +103,21 @@ public class MegaUploadService extends } @Override - public Uploader getUploader(String filename, long filesize, - String description) { - return new MegaUploadUploader(filename, filesize, description); + public Uploader getUploader( + String filename, long filesize, + MegaUploadUploaderConfiguration configuration) { + return new MegaUploadUploader(filename, filesize, configuration); + } + + @Override + public Uploader getUploader( + String filename, long filesize) { + return getUploader(filename, filesize, newUploaderConfiguration()); + } + + @Override + public MegaUploadUploaderConfiguration newUploaderConfiguration() { + return new MegaUploadUploaderConfiguration(); } @Override @@ -143,8 +139,19 @@ public class MegaUploadService extends } @Override - public Downloader getDownloader(URL url) { - return new MegaUploadDownloader(url); + public Downloader getDownloader(URL url, + MegaUploadDownloaderConfiguration configuration) { + return new MegaUploadDownloader(url, configuration); + } + + @Override + public Downloader getDownloader(URL url) { + return getDownloader(url, newDownloaderConfiguration()); + } + + @Override + public MegaUploadDownloaderConfiguration newDownloaderConfiguration() { + return new MegaUploadDownloaderConfiguration(); } @Override @@ -156,13 +163,28 @@ public class MegaUploadService extends public CapabilityMatrix getDownloadCapabilities() { return new CapabilityMatrix( DownloaderCapability.UNAUTHENTICATED_DOWNLOAD, + DownloaderCapability.UNAUTHENTICATED_RESUME, DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD, - DownloaderCapability.PREMIUM_ACCOUNT_DOWNLOAD); + DownloaderCapability.NON_PREMIUM_ACCOUNT_RESUME, + DownloaderCapability.PREMIUM_ACCOUNT_DOWNLOAD, + DownloaderCapability.PREMIUM_ACCOUNT_RESUME); } @Override - public Authenticator getAuthenticator(Credential credential) { - return new MegaUploadAuthenticator(credential); + public Authenticator getAuthenticator( + Credential credential, NullAuthenticatorConfiguration configuration) { + return new MegaUploadAuthenticator(credential, configuration); + } + + @Override + public Authenticator getAuthenticator( + Credential credential) { + return getAuthenticator(credential, newAuthenticatorConfiguration()); + } + + @Override + public NullAuthenticatorConfiguration newAuthenticatorConfiguration() { + return NullAuthenticatorConfiguration.SHARED_INSTANCE; } @Override @@ -170,50 +192,35 @@ public class MegaUploadService extends return new CapabilityMatrix(); } - protected class MegaUploadUploader implements Uploader, + protected class MegaUploadUploader extends + AbstractUploader implements + Uploader, LinkedUploadChannelCloseCallback { - private final String filename; - private final long filesize; - private final String description; - private Future uploadFuture; public MegaUploadUploader(String filename, long filesize, - String description) { - this.filename = filename; - this.filesize = filesize; - this.description = (description != null ? description - : configuration.getDefaultUploadDescription()); + MegaUploadUploaderConfiguration configuration) { + super(filename, filesize, configuration); } @Override - public UploadChannel upload() throws IOException { - final HTMLPage page = getAsPage("http://www.megaupload.com/multiupload/"); - final String url = page.getFormAction(UPLOAD_URL_PATTERN); + public UploadChannel openChannel() throws IOException { + final HTMLPage page = get("http://www.megaupload.com/multiupload/") + .asPage(); + final String url = page.findFormAction(UPLOAD_URL_PATTERN); - final LinkedUploadChannel channel = new LinkedUploadChannel(this, - filesize, filename); - final MultipartEntity entity = new MultipartEntity(); - - entity.addPart("multifile_0", new LinkedUploadChannelContentBody( - channel)); - entity.addPart("multimessage_0", new StringBody(description)); - - uploadFuture = postAsStringAsync(url, entity); - while (!channel.isLinked() && !uploadFuture.isDone()) { - ThreadUtils.sleep(100); - } - return channel; + final LinkedUploadChannel channel = createLinkedChannel(this); + uploadFuture = multipartPost(url) + .parameter("multimessage_0", configuration.description()) + .parameter("multifile_0", channel).asStringAsync(); + return waitChannelLink(channel, uploadFuture); } @Override public String finish() throws IOException { try { - String link = PatternUtils.find(DOWNLOAD_URL_PATTERN, + return PatternUtils.find(DOWNLOAD_URL_PATTERN, uploadFuture.get()); - if (link == null) - throw new UploadLinkNotFoundException(); - return link; } catch (InterruptedException e) { return null; } catch (ExecutionException e) { @@ -222,17 +229,18 @@ public class MegaUploadService extends } } - protected class MegaUploadDownloader extends AbstractDownloader { - private final URL url; - - public MegaUploadDownloader(URL url) { - this.url = url; + protected class MegaUploadDownloader extends + AbstractHttpDownloader implements + Downloader { + public MegaUploadDownloader(URL url, + MegaUploadDownloaderConfiguration configuration) { + super(url, configuration); } @Override - public DownloadChannel download(DownloadListener listener, long position) - throws IOException { - HttpResponse response = get(url.toString()); + public DownloadChannel openChannel(DownloadListener listener, + long position) throws IOException { + HttpResponse response = get(url).request(); // disable direct downloads, we don't support them! if (response.getEntity().getContentType().getValue() @@ -240,30 +248,28 @@ public class MegaUploadService extends // close connection response.getEntity().getContent().close(); - final List pairs = new ArrayList(); - pairs.add(new BasicNameValuePair("do", "directdownloads")); - pairs.add(new BasicNameValuePair("accountupdate", "1")); - pairs.add(new BasicNameValuePair("set_ddl", "0")); - // execute update - postAsString("http://www.megaupload.com/?c=account", - new UrlEncodedFormEntity(pairs)); + post("http://www.megaupload.com/?c=account") + .parameter("do", "directdownloads") + .parameter("accountupdate", "1") + .parameter("set_ddl", "0").request(); // execute and re-request download - response = get(url.toString()); + response = get(url).request(); } final HTMLPage page = HttpClientUtils.toPage(response); // try to find timer - int timer = page.findIntegerInScript(DOWNLOAD_TIMER, 1); - if (timer > 0 && configuration.respectWaitTime()) { + int timer = page.findScriptAsInt(DOWNLOAD_TIMER, 1); + if (timer > 0 && configuration.getRespectWaitTime()) { timer(listener, timer * 1000); } final String downloadUrl = page - .getLink(DOWNLOAD_DIRECT_LINK_PATTERN); + .findLink(DOWNLOAD_DIRECT_LINK_PATTERN); 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 || downloadResponse.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) { downloadResponse.getEntity().getContent().close(); @@ -274,7 +280,7 @@ public class MegaUploadService extends final String filename = FilenameUtils.getName(downloadUrl); final long contentLength = getContentLength(downloadResponse); - return new InputStreamDownloadChannel(downloadResponse + return createInputStreamChannel(downloadResponse .getEntity().getContent(), contentLength, filename); } } else { @@ -283,51 +289,34 @@ public class MegaUploadService extends } } - protected class MegaUploadAuthenticator implements Authenticator { - private final Credential credential; - - public MegaUploadAuthenticator(Credential credential) { - this.credential = credential; + protected class MegaUploadAuthenticator extends + AbstractAuthenticator implements + Authenticator { + public MegaUploadAuthenticator(Credential credential, + NullAuthenticatorConfiguration configuration) { + super(credential, configuration); } @Override public void login() throws IOException { - final MultipartEntity entity = new MultipartEntity(); - - entity.addPart("login", new StringBody("1")); - entity.addPart("username", new StringBody(credential.getUsername())); - 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); + final HTMLPage page = post("http://www.megaupload.com/?c=login") + .parameter("login", true) + .parameter("username", credential.getUsername()) + .parameter("", credential.getPassword()).asPage(); + String username = page.findScript(LOGIN_USERNAME_PATTERN, 1); if (username == null) throw new AuthenticationInvalidCredentialException(); } @Override public void logout() throws IOException { - final MultipartEntity entity = new MultipartEntity(); - entity.addPart("logout", new StringBody("1")); - - postAsString("http://www.megaupload.com/?c=account", entity); + post("http://www.megaupload.com/?c=account").parameter("logout", + true).request(); // 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 public String toString() { return this.getClass().getSimpleName() + " " + getMajorVersion() + "." diff --git a/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadUploaderConfiguration.java b/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadUploaderConfiguration.java new file mode 100644 index 0000000..3e2576a --- /dev/null +++ b/httpchannel-service/httpchannel-service-megaupload/src/main/java/com/rogiel/httpchannel/service/impl/MegaUploadUploaderConfiguration.java @@ -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 Rogiel + */ +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; + } +} diff --git a/httpchannel-service/httpchannel-service-megaupload/src/test/java/com/rogiel/httpchannel/service/impl/DiscoveryTest.java b/httpchannel-service/httpchannel-service-megaupload/src/test/java/com/rogiel/httpchannel/service/impl/DiscoveryTest.java index f2cd971..d4534ce 100644 --- a/httpchannel-service/httpchannel-service-megaupload/src/test/java/com/rogiel/httpchannel/service/impl/DiscoveryTest.java +++ b/httpchannel-service/httpchannel-service-megaupload/src/test/java/com/rogiel/httpchannel/service/impl/DiscoveryTest.java @@ -20,7 +20,7 @@ import junit.framework.Assert; import org.junit.Test; -import com.rogiel.httpchannel.service.Services; +import com.rogiel.httpchannel.service.helper.Services; /** * @author Rogiel diff --git a/httpchannel-service/httpchannel-service-megaupload/src/test/java/com/rogiel/httpchannel/service/impl/MegaUploadServiceTest.java b/httpchannel-service/httpchannel-service-megaupload/src/test/java/com/rogiel/httpchannel/service/impl/MegaUploadServiceTest.java index c3695d1..57ab707 100644 --- a/httpchannel-service/httpchannel-service-megaupload/src/test/java/com/rogiel/httpchannel/service/impl/MegaUploadServiceTest.java +++ b/httpchannel-service/httpchannel-service-megaupload/src/test/java/com/rogiel/httpchannel/service/impl/MegaUploadServiceTest.java @@ -20,10 +20,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.SeekableByteChannel; @@ -38,25 +36,18 @@ import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Test; -import com.rogiel.httpchannel.service.AuthenticationService; import com.rogiel.httpchannel.service.Credential; import com.rogiel.httpchannel.service.DownloadChannel; import com.rogiel.httpchannel.service.DownloadListener; -import com.rogiel.httpchannel.service.DownloadService; -import com.rogiel.httpchannel.service.Service; -import com.rogiel.httpchannel.service.ServiceHelper; import com.rogiel.httpchannel.service.ServiceID; import com.rogiel.httpchannel.service.UploadChannel; -import com.rogiel.httpchannel.service.UploadService; 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.impl.MegaUploadService.MegaUploadServiceConfiguration; +import com.rogiel.httpchannel.service.helper.UploadServices; import com.rogiel.httpchannel.util.ChannelUtils; public class MegaUploadServiceTest { - private Service service; - private ServiceHelper helper; + private MegaUploadService service; /** * See src/test/resources/config/megaupload.properties @@ -78,7 +69,6 @@ public class MegaUploadServiceTest { public void setUp() throws Exception { // MegaUploadServiceConfiguration.class; service = new MegaUploadService(); - helper = new ServiceHelper(service); final Properties properties = new Properties(); properties.load(new FileInputStream( @@ -89,19 +79,18 @@ public class MegaUploadServiceTest { @Test public void testServiceId() { - System.out.println("Service: " + service.toString()); assertEquals(ServiceID.create("megaupload"), service.getID()); } @Test public void testValidAuthenticator() throws IOException { - ((AuthenticationService) service).getAuthenticator( - new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); + service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD)) + .login(); } @Test(expected = AuthenticationInvalidCredentialException.class) public void testInvalidAuthenticator() throws IOException { - ((AuthenticationService) service).getAuthenticator( + service.getAuthenticator( new Credential(INVALID_USERNAME, INVALID_PASSWORD)).login(); } @@ -109,11 +98,11 @@ public class MegaUploadServiceTest { public void testNonLoguedInUploader() throws IOException { assertTrue( "This service does not have the capability UploadCapability.FREE_UPLOAD", - ((UploadService) service).getUploadCapabilities().has( + service.getUploadCapabilities().has( UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD)); final Path path = Paths.get("src/test/resources/upload-test-file.txt"); - final UploadChannel channel = helper.upload(path, - "httpchannel test upload"); + final UploadChannel channel = UploadServices.upload(service, path) + .openChannel(); final SeekableByteChannel inChannel = Files.newByteChannel(path); try { @@ -131,15 +120,15 @@ public class MegaUploadServiceTest { public void testLoguedInUploader() throws IOException { assertTrue( "This service does not have the capability UploadCapability.PREMIUM_UPLOAD", - ((UploadService) service).getUploadCapabilities().has( + service.getUploadCapabilities().has( UploaderCapability.PREMIUM_ACCOUNT_UPLOAD)); - ((AuthenticationService) service).getAuthenticator( - new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); + service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD)) + .login(); final Path path = Paths.get("src/test/resources/upload-test-file.txt"); - final UploadChannel channel = helper.upload(path, - "httpchannel test upload"); + final UploadChannel channel = UploadServices.upload(service, path) + .openChannel(); final SeekableByteChannel inChannel = Files.newByteChannel(path); try { @@ -154,10 +143,10 @@ public class MegaUploadServiceTest { } @Test - public void testFreeDownloader() throws IOException, MalformedURLException { - final DownloadChannel channel = ((DownloadService) service) - .getDownloader(new URL("http://www.megaupload.com/?d=CVQKJ1KM")) - .download(new DownloadListener() { + public void testFreeDownloader() throws IOException { + final DownloadChannel channel = service.getDownloader( + new URL("http://www.megaupload.com/?d=CVQKJ1KM")).openChannel( + new DownloadListener() { @Override public boolean timer(long time) { System.out.println("Waiting " + time); @@ -172,14 +161,13 @@ public class MegaUploadServiceTest { } @Test - public void testPremiumDownloader() throws IOException, - MalformedURLException { - ((AuthenticationService) service).getAuthenticator( - new Credential(VALID_USERNAME, VALID_PASSWORD)).login(); + public void testPremiumDownloader() throws IOException { + service.getAuthenticator(new Credential(VALID_USERNAME, VALID_PASSWORD)) + .login(); - final DownloadChannel channel = ((DownloadService) service) - .getDownloader(new URL("http://www.megaupload.com/?d=CVQKJ1KM")) - .download(new DownloadListener() { + final DownloadChannel channel = service.getDownloader( + new URL("http://www.megaupload.com/?d=CVQKJ1KM")).openChannel( + new DownloadListener() { @Override public boolean timer(long time) { System.out.println("Waiting " + time); @@ -192,17 +180,18 @@ public class MegaUploadServiceTest { } @Test - public void testNoWaitDownloader() throws IOException, - MalformedURLException { + public void testNoWaitDownloader() throws IOException { service = new MegaUploadService(); - service.setServiceConfiguration(ServiceConfigurationHelper.file( - MegaUploadServiceConfiguration.class, new File( - "src/test/resources/megaupload-nowait.properties"))); + // service.setServiceConfiguration(ServiceConfigurationHelper.file( + // MegaUploadServiceConfiguration.class, new File( + // "src/test/resources/megaupload-nowait.properties"))); + final MegaUploadDownloaderConfiguration config = new MegaUploadDownloaderConfiguration(); + config.setRespectWaitTime(false); - @SuppressWarnings("unused") - final DownloadChannel channel = ((DownloadService) service) - .getDownloader(new URL("http://www.megaupload.com/?d=CVQKJ1KM")) - .download(new DownloadListener() { + @SuppressWarnings({ "unused" }) + final DownloadChannel channel = service.getDownloader( + new URL("http://www.megaupload.com/?d=CVQKJ1KM"), config) + .openChannel(new DownloadListener() { @Override public boolean timer(long time) { System.out.println("Waiting " + time); diff --git a/httpchannel-service/httpchannel-service-multiupload/pom.xml b/httpchannel-service/httpchannel-service-multiupload/pom.xml new file mode 100644 index 0000000..07db14a --- /dev/null +++ b/httpchannel-service/httpchannel-service-multiupload/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + httpchannel-service + com.rogiel.httpchannel + 1.0.0 + .. + + httpchannel-service-multiupload + com.rogiel.httpchannel.services + HttpChannel/Service/MultiUpload + Provides upload access to multiupload.com + + + com.rogiel.httpchannel + httpchannel-tests + 1.0.0 + runtime + + + \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-multiupload/src/main/java/com/rogiel/httpchannel/service/impl/MultiUploadService.java b/httpchannel-service/httpchannel-service-multiupload/src/main/java/com/rogiel/httpchannel/service/impl/MultiUploadService.java new file mode 100644 index 0000000..31e8853 --- /dev/null +++ b/httpchannel-service/httpchannel-service-multiupload/src/main/java/com/rogiel/httpchannel/service/impl/MultiUploadService.java @@ -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 Rogiel + * @since 1.0 + */ +public class MultiUploadService extends AbstractHttpService implements Service, + UploadService, + DownloadService, + AuthenticationService { + /** + * 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 getUploader( + String filename, long filesize, + MultiUploadUploaderConfiguration configuration) { + if (configuration == null) + configuration = new MultiUploadUploaderConfiguration(); + return new MultiUploadUploader(filename, filesize, configuration); + } + + @Override + public Uploader 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 getUploadCapabilities() { + return new CapabilityMatrix( + UploaderCapability.UNAUTHENTICATED_UPLOAD, + UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD, + UploaderCapability.PREMIUM_ACCOUNT_UPLOAD); + } + + @Override + public Downloader getDownloader(URL url, + NullDownloaderConfiguration configuration) { + return new MultiUploaderDownloader(url, configuration); + } + + @Override + public Downloader 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 getDownloadCapabilities() { + return new CapabilityMatrix<>( + DownloaderCapability.UNAUTHENTICATED_DOWNLOAD, + DownloaderCapability.UNAUTHENTICATED_RESUME, + DownloaderCapability.NON_PREMIUM_ACCOUNT_DOWNLOAD, + DownloaderCapability.NON_PREMIUM_ACCOUNT_RESUME); + } + + @Override + public Authenticator getAuthenticator( + Credential credential, NullAuthenticatorConfiguration configuration) { + return new MultiUploadAuthenticator(credential, configuration); + } + + @Override + public Authenticator getAuthenticator( + Credential credential) { + return getAuthenticator(credential, newAuthenticatorConfiguration()); + } + + @Override + public NullAuthenticatorConfiguration newAuthenticatorConfiguration() { + return NullAuthenticatorConfiguration.SHARED_INSTANCE; + } + + @Override + public CapabilityMatrix getAuthenticationCapability() { + return new CapabilityMatrix(); + } + + protected class MultiUploadUploader extends + AbstractUploader implements + Uploader, + LinkedUploadChannelCloseCallback { + private Future 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 implements + Downloader { + 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 implements + Authenticator { + 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 + } + } +} diff --git a/httpchannel-service/httpchannel-service-multiupload/src/main/java/com/rogiel/httpchannel/service/impl/MultiUploadUploaderConfiguration.java b/httpchannel-service/httpchannel-service-multiupload/src/main/java/com/rogiel/httpchannel/service/impl/MultiUploadUploaderConfiguration.java new file mode 100644 index 0000000..17437b3 --- /dev/null +++ b/httpchannel-service/httpchannel-service-multiupload/src/main/java/com/rogiel/httpchannel/service/impl/MultiUploadUploaderConfiguration.java @@ -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 Rogiel + */ +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 uploadServices = EnumSet + .allOf(MultiUploadMirrorService.class); + + /** + * An enumeration containing all supported services for Multiupload + * + * @author Rogiel + */ + 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 true 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 uploadServices() { + return uploadServices; + } +} diff --git a/httpchannel-service/httpchannel-service-multiupload/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service b/httpchannel-service/httpchannel-service-multiupload/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service new file mode 100644 index 0000000..4013beb --- /dev/null +++ b/httpchannel-service/httpchannel-service-multiupload/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service @@ -0,0 +1 @@ +com.rogiel.httpchannel.service.impl.MultiUploadService \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-multiupload/src/test/java/com/rogiel/httpchannel/service/impl/MultiUploadServiceTest.java b/httpchannel-service/httpchannel-service-multiupload/src/test/java/com/rogiel/httpchannel/service/impl/MultiUploadServiceTest.java new file mode 100644 index 0000000..303392c --- /dev/null +++ b/httpchannel-service/httpchannel-service-multiupload/src/test/java/com/rogiel/httpchannel/service/impl/MultiUploadServiceTest.java @@ -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)); + } +} diff --git a/httpchannel-service/httpchannel-service-multiupload/src/test/resources/upload-test-file.txt b/httpchannel-service/httpchannel-service-multiupload/src/test/resources/upload-test-file.txt new file mode 100644 index 0000000..3cfccc8 --- /dev/null +++ b/httpchannel-service/httpchannel-service-multiupload/src/test/resources/upload-test-file.txt @@ -0,0 +1,3 @@ +This is a simple upload test file. + +This is for testing purposes only. \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-uploadking/pom.xml b/httpchannel-service/httpchannel-service-uploadking/pom.xml new file mode 100644 index 0000000..ba4ed0a --- /dev/null +++ b/httpchannel-service/httpchannel-service-uploadking/pom.xml @@ -0,0 +1,13 @@ + + 4.0.0 + + httpchannel-service + com.rogiel.httpchannel + 1.0.0 + .. + + httpchannel-service-uploadking + com.rogiel.httpchannel.services + HttpChannel/Service/UploadKing + Provides upload access to uploadking.com + \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-uploadking/src/main/java/com/rogiel/httpchannel/service/impl/UploadKingService.java b/httpchannel-service/httpchannel-service-uploadking/src/main/java/com/rogiel/httpchannel/service/impl/UploadKingService.java new file mode 100644 index 0000000..74888a7 --- /dev/null +++ b/httpchannel-service/httpchannel-service-uploadking/src/main/java/com/rogiel/httpchannel/service/impl/UploadKingService.java @@ -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 Rogiel + * @since 1.0 + */ +public class UploadKingService extends AbstractHttpService implements Service, + UploadService, + AuthenticationService { + /** + * 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 getUploader(String filename, + long filesize, NullUploaderConfiguration configuration) { + return new UploadKingUploader(filename, filesize, configuration); + } + + @Override + public Uploader 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 getUploadCapabilities() { + return new CapabilityMatrix( + UploaderCapability.UNAUTHENTICATED_UPLOAD, + UploaderCapability.NON_PREMIUM_ACCOUNT_UPLOAD, + UploaderCapability.PREMIUM_ACCOUNT_UPLOAD); + } + + @Override + public Authenticator getAuthenticator( + Credential credential, NullAuthenticatorConfiguration configuration) { + return new UploadKingAuthenticator(credential, configuration); + } + + @Override + public Authenticator getAuthenticator( + Credential credential) { + return getAuthenticator(credential, newAuthenticatorConfiguration()); + } + + @Override + public NullAuthenticatorConfiguration newAuthenticatorConfiguration() { + return NullAuthenticatorConfiguration.SHARED_INSTANCE; + } + + @Override + public CapabilityMatrix getAuthenticationCapability() { + return new CapabilityMatrix(); + } + + protected class UploadKingUploader extends + AbstractUploader implements + Uploader, + LinkedUploadChannelCloseCallback { + private Future 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 implements + Authenticator { + 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 + } + } +} diff --git a/httpchannel-service/httpchannel-service-uploadking/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service b/httpchannel-service/httpchannel-service-uploadking/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service new file mode 100644 index 0000000..4013beb --- /dev/null +++ b/httpchannel-service/httpchannel-service-uploadking/src/main/resources/META-INF/services/com.rogiel.httpchannel.service.Service @@ -0,0 +1 @@ +com.rogiel.httpchannel.service.impl.MultiUploadService \ No newline at end of file diff --git a/httpchannel-service/httpchannel-service-uploadking/src/test/java/com/rogiel/httpchannel/service/impl/UploadKingServiceTest.java b/httpchannel-service/httpchannel-service-uploadking/src/test/java/com/rogiel/httpchannel/service/impl/UploadKingServiceTest.java new file mode 100644 index 0000000..99dafd8 --- /dev/null +++ b/httpchannel-service/httpchannel-service-uploadking/src/test/java/com/rogiel/httpchannel/service/impl/UploadKingServiceTest.java @@ -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); + } +} diff --git a/httpchannel-service/httpchannel-service-uploadking/src/test/resources/upload-test-file.txt b/httpchannel-service/httpchannel-service-uploadking/src/test/resources/upload-test-file.txt new file mode 100644 index 0000000..3cfccc8 --- /dev/null +++ b/httpchannel-service/httpchannel-service-uploadking/src/test/resources/upload-test-file.txt @@ -0,0 +1,3 @@ +This is a simple upload test file. + +This is for testing purposes only. \ No newline at end of file diff --git a/httpchannel-service/pom.xml b/httpchannel-service/pom.xml index 87757d8..c920672 100644 --- a/httpchannel-service/pom.xml +++ b/httpchannel-service/pom.xml @@ -13,6 +13,7 @@ httpchannel-service-megaupload httpchannel-service-hotfile + httpchannel-service-multiupload @@ -27,4 +28,6 @@ 1.0.0 + HttpChannel/Service + Parent module that all service implementations should inherit \ No newline at end of file diff --git a/httpchannel-tests/pom.xml b/httpchannel-tests/pom.xml new file mode 100644 index 0000000..7cf6180 --- /dev/null +++ b/httpchannel-tests/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + httpchannel + com.rogiel.httpchannel + 1.0.0 + .. + + httpchannel-tests + HttpChannel/Tests + This module contains several test utilities + + + com.rogiel.httpchannel + httpchannel-api + 1.0.0 + + + com.rogiel.httpchannel + httpchannel-util + 1.0.0 + + + \ No newline at end of file diff --git a/httpchannel-tests/src/main/java/com/rogiel/httpchannel/service/AbstractServiceTest.java b/httpchannel-tests/src/main/java/com/rogiel/httpchannel/service/AbstractServiceTest.java new file mode 100644 index 0000000..c1d587f --- /dev/null +++ b/httpchannel-tests/src/main/java/com/rogiel/httpchannel/service/AbstractServiceTest.java @@ -0,0 +1,237 @@ +/* + * This file is part of 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 . + */ +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!"); + } +} diff --git a/httpchannel-tests/src/main/java/com/rogiel/httpchannel/util/ChecksumUtils.java b/httpchannel-tests/src/main/java/com/rogiel/httpchannel/util/ChecksumUtils.java new file mode 100644 index 0000000..754ad74 --- /dev/null +++ b/httpchannel-tests/src/main/java/com/rogiel/httpchannel/util/ChecksumUtils.java @@ -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); + } +} diff --git a/httpchannel-tests/src/main/resources/upload-test-file.txt b/httpchannel-tests/src/main/resources/upload-test-file.txt new file mode 100644 index 0000000..3cfccc8 --- /dev/null +++ b/httpchannel-tests/src/main/resources/upload-test-file.txt @@ -0,0 +1,3 @@ +This is a simple upload test file. + +This is for testing purposes only. \ No newline at end of file diff --git a/httpchannel-util/pom.xml b/httpchannel-util/pom.xml index b271131..4a2f3e1 100644 --- a/httpchannel-util/pom.xml +++ b/httpchannel-util/pom.xml @@ -59,8 +59,8 @@ org.htmlparser htmlparser 2.1 - jar - compile + HttpChannel/Service/Utilities + 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. \ No newline at end of file diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/captcha/AbstractImageCaptcha.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/captcha/AbstractImageCaptcha.java new file mode 100644 index 0000000..2429ef9 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/captcha/AbstractImageCaptcha.java @@ -0,0 +1,47 @@ +/** + * + */ +package com.rogiel.httpchannel.captcha; + +import java.net.URL; + +/** + * @author Rogiel + * + */ +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; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/captcha/AbstractImageCaptchaService.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/captcha/AbstractImageCaptchaService.java new file mode 100644 index 0000000..ca0ff58 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/captcha/AbstractImageCaptchaService.java @@ -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 Rogiel + * + */ +public abstract class AbstractImageCaptchaService + implements ImageCaptchaService { + 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); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/GetRequest.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/GetRequest.java new file mode 100644 index 0000000..b8123c8 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/GetRequest.java @@ -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; + } +} \ No newline at end of file diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/HttpContext.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/HttpContext.java new file mode 100644 index 0000000..3b6a799 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/HttpContext.java @@ -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 Rogiel + * + */ +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); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/PostMultipartRequest.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/PostMultipartRequest.java new file mode 100644 index 0000000..970e60b --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/PostMultipartRequest.java @@ -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)); + } +} \ No newline at end of file diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/PostRequest.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/PostRequest.java new file mode 100644 index 0000000..4039473 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/PostRequest.java @@ -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 params = new ArrayList(); + + 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")); + } +} \ No newline at end of file diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/Request.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/Request.java new file mode 100644 index 0000000..948a452 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/http/Request.java @@ -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 requestAsync() throws IOException { + return ctx.threadPool.submit(new Callable() { + @Override + public HttpResponse call() throws Exception { + return request(); + } + }); + } + + public String asString() throws ClientProtocolException, IOException { + return HttpClientUtils.toString(request()); + } + + public Future asStringAsync() throws IOException { + return ctx.threadPool.submit(new Callable() { + @Override + public String call() throws Exception { + return asString(); + } + }); + } + + public HTMLPage asPage() throws ClientProtocolException, IOException { + return HTMLPage.parse(asString()); + } + + public Future asPageAsync() throws IOException { + return ctx.threadPool.submit(new Callable() { + @Override + public HTMLPage call() throws Exception { + return asPage(); + } + }); + } + + public String getURL() { + return url; + } +} \ No newline at end of file diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractAuthenticator.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractAuthenticator.java new file mode 100644 index 0000000..9434459 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractAuthenticator.java @@ -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 Rogiel + * + * @param + * the {@link Authenticator} configuration object type + */ +public abstract class AbstractAuthenticator + implements Authenticator { + 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; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractDownloader.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractDownloader.java index f61b226..af31858 100644 --- a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractDownloader.java +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractDownloader.java @@ -1,52 +1,79 @@ -/* - * This file is part of 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 . - */ -package com.rogiel.httpchannel.service; - -import org.apache.http.Header; -import org.apache.http.HttpResponse; - -import com.rogiel.httpchannel.util.ThreadUtils; - -/** - * @author rogiel - */ -public abstract class AbstractDownloader implements Downloader { - protected int parseTimer(String stringTimer) { - int timer = 0; - if (stringTimer != null && stringTimer.length() > 0) { - timer = Integer.parseInt(stringTimer); - } - return timer; - } - - 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) { - listener.timer(timer); - } - ThreadUtils.sleep(timer); - } -} +package com.rogiel.httpchannel.service; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +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; + +/** + * An abstract {@link Downloader} that implements most of the general-purpose + * methods + * + * @author Rogiel + * + * @param + * the {@link Downloader} configuration object type + */ +public abstract class AbstractDownloader + implements Downloader { + /** + * The download URL + */ + protected final URL url; + + /** + * 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; + } + + @Override + public DownloadChannel openChannel(long position) throws IOException, + DownloadLinkNotFoundException, DownloadLimitExceededException, + DownloadNotAuthorizedException, DownloadNotResumableException { + return openChannel(null, position); + } + + @Override + public DownloadChannel openChannel(DownloadListener listener) + throws IOException, DownloadLinkNotFoundException, + DownloadLimitExceededException, DownloadNotAuthorizedException, + 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; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractHttpDownloader.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractHttpDownloader.java new file mode 100644 index 0000000..37db7c5 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractHttpDownloader.java @@ -0,0 +1,66 @@ +/* + * This file is part of 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 . + */ +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 Rogiel + */ +public abstract class AbstractHttpDownloader + extends AbstractDownloader implements Downloader { + 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); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractHttpService.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractHttpService.java index 36a494f..0f3feda 100644 --- a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractHttpService.java +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractHttpService.java @@ -16,151 +16,47 @@ */ package com.rogiel.httpchannel.service; -import java.io.IOException; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.net.URL; import java.util.concurrent.Future; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -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; +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.service.channel.LinkedUploadChannel; +import com.rogiel.httpchannel.util.ThreadUtils; /** * Abstract base service for HTTP enabled services. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ -public abstract class AbstractHttpService - extends AbstractService implements Service { - private static final ExecutorService threadPool = Executors - .newCachedThreadPool(); +public abstract class AbstractHttpService extends AbstractService implements + Service { + protected final HttpContext http = new HttpContext(); - /** - * The {@link HttpClient} instance for this service - */ - protected DefaultHttpClient client = new DefaultHttpClient(); - - /** - * 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 LinkedUploadChannel waitChannelLink(LinkedUploadChannel channel, + Future future) { + while (!channel.isLinked() && !future.isDone()) { + ThreadUtils.sleep(100); + } + return channel; } - protected HttpResponse get(String url) throws ClientProtocolException, - IOException { - final HttpGet request = new HttpGet(url); - return client.execute(request); + public GetRequest get(String url) { + return http.get(url); } - protected HttpResponse get(String url, long rangeStart) - throws ClientProtocolException, IOException { - final HttpGet request = new HttpGet(url); - if (rangeStart >= 0) - request.addHeader("Range", "bytes=" + rangeStart + "-"); - return client.execute(request); + public GetRequest get(URL url) { + return http.get(url); } - protected String getAsString(String url) throws ClientProtocolException, - IOException { - return HttpClientUtils.toString(get(url)); + public PostRequest post(String url) { + return http.post(url); } - protected HTMLPage getAsPage(String url) throws ClientProtocolException, - IOException { - return HTMLPage.parse(getAsString(url)); - } - - public Future getAsync(final String url) throws IOException { - return threadPool.submit(new Callable() { - @Override - public HttpResponse call() throws Exception { - return get(url); - } - }); - } - - public Future getAsStringAsync(final String url) throws IOException { - return threadPool.submit(new Callable() { - @Override - public String call() throws Exception { - return getAsString(url); - } - }); - } - - public Future getAsPageAsync(final String url) throws IOException { - return threadPool.submit(new Callable() { - @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 postAsync(final String url, - final HttpEntity entity) throws IOException { - return threadPool.submit(new Callable() { - @Override - public HttpResponse call() throws Exception { - return post(url, entity); - } - }); - } - - protected Future postAsStringAsync(final String url, - final HttpEntity entity) throws IOException { - return threadPool.submit(new Callable() { - @Override - public String call() throws Exception { - return postAsString(url, entity); - } - }); - } - - protected Future postAsPageAsync(final String url, - final HttpEntity entity) throws IOException { - return threadPool.submit(new Callable() { - @Override - public HTMLPage call() throws Exception { - return postAsPage(url, entity); - } - }); + public PostMultipartRequest multipartPost(String url) { + return http.multipartPost(url); } } diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractUploader.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractUploader.java new file mode 100644 index 0000000..d38f35d --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/AbstractUploader.java @@ -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 Rogiel + * + * @param + * the {@link Uploader} configuration object type + */ +public abstract class AbstractUploader + implements Uploader { + 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; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/channel/LinkedUploadChannel.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/channel/LinkedUploadChannel.java index 6d8de0c..abe22ef 100644 --- a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/channel/LinkedUploadChannel.java +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/channel/LinkedUploadChannel.java @@ -17,11 +17,12 @@ package com.rogiel.httpchannel.service.channel; import java.io.IOException; +import java.net.URL; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import com.rogiel.httpchannel.service.UploadChannel; - +import com.rogiel.httpchannel.service.exception.UploadLinkNotFoundException; /** * @author Rogiel @@ -33,7 +34,7 @@ public class LinkedUploadChannel implements UploadChannel { private final long length; private final String filename; - private String downloadLink; + private URL downloadLink; private boolean open = true; @@ -59,7 +60,10 @@ public class LinkedUploadChannel implements UploadChannel { @Override public void close() throws IOException { open = false; - downloadLink = closeCallback.finish(); + final String downloadLink = closeCallback.finish(); + if (downloadLink == null) + throw new UploadLinkNotFoundException(); + this.downloadLink = new URL(downloadLink); } public interface LinkedUploadChannelCloseCallback { @@ -77,12 +81,12 @@ public class LinkedUploadChannel implements UploadChannel { } @Override - public String getDownloadLink() { + public URL getDownloadLink() { return downloadLink; } public void linkChannel(WritableByteChannel channel) throws IOException { - if(this.channel != null) + if (this.channel != null) throw new IOException("This channel is already linked."); this.channel = channel; } diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/channel/LinkedUploadChannelContentBody.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/channel/LinkedUploadChannelContentBody.java index 433896a..c61f0fc 100644 --- a/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/channel/LinkedUploadChannelContentBody.java +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/service/channel/LinkedUploadChannelContentBody.java @@ -29,7 +29,7 @@ import com.rogiel.httpchannel.service.Uploader; /** * {@link ContentBody} used to upload files in {@link Uploader} implementations. * - * @author Rogiel + * @author Rogiel * @since 1.0 */ public class LinkedUploadChannelContentBody extends AbstractContentBody { diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/ChannelUtils.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/ChannelUtils.java index 0993675..f7cef4e 100644 --- a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/ChannelUtils.java +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/ChannelUtils.java @@ -16,14 +16,23 @@ */ package com.rogiel.httpchannel.util; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.net.URL; import java.nio.ByteBuffer; +import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +import com.rogiel.httpchannel.service.UploadChannel; +import com.rogiel.httpchannel.service.UploadService; +import com.rogiel.httpchannel.service.helper.UploadServices; /** * @author Rogiel - * */ public class ChannelUtils { public static void copy(ReadableByteChannel in, WritableByteChannel out) @@ -48,4 +57,24 @@ public class ChannelUtils { buffer.compact(); } } + + public static byte[] toByteArray(ReadableByteChannel channel) + throws IOException { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + copy(channel, Channels.newChannel(out)); + return out.toByteArray(); + } + + public static URL upload(UploadService service, Path path) + throws IOException { + final UploadChannel uploadChannel = UploadServices + .upload(service, path).openChannel(); + try { + copy(Files.newByteChannel(path, StandardOpenOption.READ), + uploadChannel); + } finally { + uploadChannel.close(); + } + return uploadChannel.getDownloadLink(); + } } diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/ThreadUtils.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/ThreadUtils.java index dbb0ba8..3844485 100644 --- a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/ThreadUtils.java +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/ThreadUtils.java @@ -18,7 +18,7 @@ package com.rogiel.httpchannel.util; /** - * @author Rogiel + * @author Rogiel * @since 1.0 */ public class ThreadUtils { diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ContainsFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ContainsFilter.java new file mode 100644 index 0000000..398834b --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ContainsFilter.java @@ -0,0 +1,20 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; + +public class ContainsFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern content; + + public ContainsFilter(Pattern content) { + this.content = content; + } + + @Override + public boolean accept(Node node) { + return content.matcher(node.getText()).find(); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ContainsInLowerCaseFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ContainsInLowerCaseFilter.java new file mode 100644 index 0000000..400e272 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ContainsInLowerCaseFilter.java @@ -0,0 +1,20 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; + +public class ContainsInLowerCaseFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern content; + + public ContainsInLowerCaseFilter(Pattern content) { + this.content = content; + } + + @Override + public boolean accept(Node node) { + return content.matcher(node.getText().toLowerCase()).find(); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/FormActionPatternFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/FormActionPatternFilter.java new file mode 100644 index 0000000..d622064 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/FormActionPatternFilter.java @@ -0,0 +1,24 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.tags.FormTag; + +public class FormActionPatternFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern pattern; + + public FormActionPatternFilter(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof FormTag)) + return false; + final FormTag form = (FormTag) node; + return pattern.matcher(form.getFormLocation()).matches(); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/FramePatternFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/FramePatternFilter.java new file mode 100644 index 0000000..90a9709 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/FramePatternFilter.java @@ -0,0 +1,26 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.nodes.TagNode; + +public class FramePatternFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern pattern; + + public FramePatternFilter(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof TagNode)) + return false; + final TagNode frame = (TagNode) node; + if (frame.getAttribute("src") == null) + return false; + return pattern.matcher(frame.getAttribute("src")).matches(); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/HTMLPage.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/HTMLPage.java index a8fe811..e4361ba 100644 --- a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/HTMLPage.java +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/HTMLPage.java @@ -16,6 +16,9 @@ */ package com.rogiel.httpchannel.util.htmlparser; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -23,7 +26,10 @@ import org.htmlparser.Node; import org.htmlparser.NodeFilter; import org.htmlparser.Parser; import org.htmlparser.Tag; +import org.htmlparser.filters.AndFilter; +import org.htmlparser.nodes.TagNode; import org.htmlparser.tags.FormTag; +import org.htmlparser.tags.ImageTag; import org.htmlparser.tags.InputTag; import org.htmlparser.tags.LinkTag; import org.htmlparser.tags.ScriptTag; @@ -35,214 +41,208 @@ import org.htmlparser.util.ParserException; * @author Rogiel */ public class HTMLPage { - private final Parser parser; + private final NodeList nodes; - private HTMLPage(Parser parser) { - this.parser = parser; + private HTMLPage(Parser parser) throws ParserException { + this.nodes = parser.parse(null); } - public String getLink(final Pattern pattern) { - NodeList nodes; + private List filter(final Class nodeType, + NodeFilter... filters) { + final NodeFilter filter; + if (filters.length == 1) + filter = filters[0]; + else + filter = new AndFilter(filters); try { - nodes = parser.extractAllNodesThatMatch(new NodeFilter() { - private static final long serialVersionUID = 1L; - - @Override - public boolean accept(Node node) { - if (!(node instanceof LinkTag)) - return false; - final LinkTag link = (LinkTag) node; - return pattern.matcher(link.getLink()).matches(); - } - }); + return list(nodes.extractAllNodesThatMatch(filter, true)); } catch (ParserException e) { - return null; + return Collections.emptyList(); } - if (nodes.size() >= 1) - return ((LinkTag) nodes.elements().nextNode()).getLink(); - return null; } - public String getFormAction(final Pattern pattern) { - NodeList nodes; - try { - nodes = parser.extractAllNodesThatMatch(new NodeFilter() { - private static final long serialVersionUID = 1L; - - @Override - public boolean accept(Node node) { - if (!(node instanceof FormTag)) - return false; - final FormTag form = (FormTag) node; - return pattern.matcher(form.getFormLocation()).matches(); - } - }); - } catch (ParserException e) { - return null; + @SuppressWarnings("unchecked") + private List list(final NodeList list) + throws ParserException { + final List filtered = new ArrayList<>(); + final NodeIterator iterator = list.elements(); + while (iterator.hasMoreNodes()) { + filtered.add((T) iterator.nextNode()); } - if (nodes.size() >= 1) - return ((FormTag) nodes.elements().nextNode()).getFormLocation(); - return null; + return filtered; } - public String getInputValue(final String inputName) { - NodeList nodes; - try { - nodes = parser.extractAllNodesThatMatch(new NodeFilter() { - private static final long serialVersionUID = 1L; - - @Override - public boolean accept(Node node) { - if (!(node instanceof InputTag)) - return false; - final InputTag input = (InputTag) node; - if (!input.getAttribute("name").equals(inputName)) - return false; - return true; - } - }); - } catch (ParserException e) { - return null; - } - if (nodes.size() >= 1) - return ((InputTag) nodes.elements().nextNode()) - .getAttribute("value"); - return null; - } - - public int getIntegerInputValue(final String inputName) { - return Integer.parseInt(getInputValue(inputName)); - } - - public String getInputValue(final Pattern pattern) { - NodeList nodes; - try { - nodes = parser.extractAllNodesThatMatch(new NodeFilter() { - private static final long serialVersionUID = 1L; - - @Override - public boolean accept(Node node) { - if (!(node instanceof InputTag)) - return false; - final InputTag input = (InputTag) node; - if (input.getAttribute("value") == null) - return false; - if (!pattern.matcher(input.getAttribute("value")).matches()) - return false; - return true; - } - }); - } catch (ParserException e) { - return null; - } - if (nodes.size() >= 1) - return ((InputTag) nodes.elements().nextNode()) - .getAttribute("value"); - return null; - } - - public Tag getTagByID(final String id) { - NodeList nodes; - try { - nodes = parser.extractAllNodesThatMatch(new NodeFilter() { - private static final long serialVersionUID = 1L; - - @Override - public boolean accept(Node node) { - if (!(node instanceof Tag)) - return false; - if (((Tag) node).getAttribute("id") == null) - return false; - return ((Tag) node).getAttribute("id").equals(id); - } - }); - } catch (ParserException e) { - return null; - } - if (nodes.size() >= 1) - return ((Tag) nodes.elements().nextNode()); - return null; - } - - public Tag getTagByName(final String name) { - NodeList nodes; - try { - nodes = parser.extractAllNodesThatMatch(new NodeFilter() { - private static final long serialVersionUID = 1L; - - @Override - public boolean accept(Node node) { - if (!(node instanceof Tag)) - return false; - return ((Tag) node).getAttribute("name").equals(name); - } - }); - } catch (ParserException e) { - return null; - } - if (nodes.size() >= 1) - return ((Tag) nodes.elements().nextNode()); - return null; + public boolean contains(final Pattern pattern) { + return !filter(Node.class, new ContainsFilter(pattern)).isEmpty(); } public boolean contains(final String text) { - try { - for (NodeIterator e = parser.elements(); e.hasMoreNodes();) { - if (e.nextNode().toPlainTextString().contains(text)) - return true; - } - } catch (ParserException e) { - return false; - } - return false; + return contains(Pattern.compile(Pattern.quote(text))); } - public String findInScript(final Pattern pattern, int n) { - NodeList nodes; - try { - nodes = parser.extractAllNodesThatMatch(new NodeFilter() { - private static final long serialVersionUID = 1L; + public boolean containsIgnoreCase(final String text) { + return !filter( + Node.class, + new ContainsInLowerCaseFilter(Pattern.compile(Pattern + .quote(text.toLowerCase())))).isEmpty(); + } - @Override - public boolean accept(Node node) { - if (!(node instanceof ScriptTag)) - return false; - final ScriptTag script = (ScriptTag) node; - return pattern.matcher(script.getScriptCode()).find(); - } - }); - } catch (ParserException e) { - return null; - } - if (nodes.size() >= 1) { - final ScriptTag script = (ScriptTag) nodes.elements().nextNode(); - final Matcher matcher = pattern.matcher(script.getScriptCode()); + public String find(final Pattern pattern, int n) { + for (final Node tag : filter(Tag.class, new ContainsFilter(pattern))) { + final Matcher matcher = pattern.matcher(tag.getText()); if (matcher.find()) return matcher.group(n); } return null; } - public int findIntegerInScript(final Pattern pattern, int n) { - String found = findInScript(pattern, n); - if(found == null) + public int findAsInt(final Pattern pattern, int n) { + String found = find(pattern, n); + if (found == null) return 0; - return Integer.parseInt(findInScript(pattern, n)); + return Integer.parseInt(findScript(pattern, n)); + } + + /** + * Tries to find a link that has an URL following the given pattern + * + * @param pattern + * the pattern + * @return the link content, if found. null otherwise + */ + public String findLink(final Pattern pattern) { + for (final LinkTag tag : filter(LinkTag.class, new LinkPatternFilter( + pattern))) { + return tag.getLink(); + } + return null; + } + + /** + * Tries to find a frame that has an URL following the given pattern + * + * @param pattern + * the pattern + * @return the iframe url, if found. null otherwise + */ + public String findFrame(final Pattern pattern) { + for (final TagNode tag : filter(TagNode.class, new FramePatternFilter( + pattern))) { + return tag.getAttribute("src"); + } + return null; + } + + /** + * Tries to find a image that has an URL following the given pattern + * + * @param pattern + * the pattern + * @return the iframe url, if found. null otherwise + */ + public String findImage(final Pattern pattern) { + for (final ImageTag tag : filter(ImageTag.class, + new ImagePatternFilter(pattern))) { + return tag.getImageURL(); + } + return null; + } + + /** + * Tries to find a form which has an location that respects the given + * pattern + * + * @param pattern + * the pattern + * @return the URL found, if any. null otherwise + */ + public String findFormAction(final Pattern pattern) { + for (final FormTag tag : filter(FormTag.class, + new FormActionPatternFilter(pattern))) { + return tag.getFormLocation(); + } + return null; + } + + private String inputValue(List tags) { + for (final InputTag tag : tags) { + return tag.getAttribute("value"); + } + return null; + } + + public String getInputValue(final String inputName) { + return inputValue(filter(InputTag.class, new InputNameFilter(inputName))); + } + + public int getInputValueAsInt(final String inputName) { + return Integer.parseInt(getInputValue(inputName)); + } + + public String getInputValueById(final String id) { + return inputValue(filter(InputTag.class, new InputIDFilter(id))); + } + + public String getInputValue(final Pattern pattern) { + return inputValue(filter(InputTag.class, new InputValuePatternFilter( + pattern))); + } + + public Tag getTagByID(final String id) { + for (final Tag tag : filter(Tag.class, new IDFilter(id))) { + return tag; + } + return null; + } + + public Tag getTagByName(final String name) { + for (final Tag tag : filter(Tag.class, new NameFilter(name))) { + return tag; + } + return null; + } + + public String findScript(final Pattern pattern, int n) { + for (final ScriptTag tag : filter(ScriptTag.class, + new ScriptContainsFilter(pattern))) { + final Matcher matcher = pattern.matcher(tag.getScriptCode()); + if (matcher.find()) + return matcher.group(n); + } + return null; + } + + public String findScriptSrc(final Pattern pattern) { + for (final ScriptTag tag : filter(ScriptTag.class, new ScriptSrcFilter( + pattern))) { + final Matcher matcher = pattern.matcher(tag.getAttribute("src")); + if (matcher.matches()) + return matcher.group(); + } + return null; + } + + public int findScriptAsInt(final Pattern pattern, int n) { + String found = findScript(pattern, n); + if (found == null) + return 0; + return Integer.parseInt(found); } public String toString() { - final StringBuilder builder = new StringBuilder(); - try { - for (NodeIterator i = parser.elements(); i.hasMoreNodes();) { - builder.append(i.nextNode().toHtml(true)); - } - } catch (ParserException e) { - return null; - } - return builder.toString(); + // try { + // return parser.parse(null).toHtml(false); + // } catch (ParserException e1) { + // return null; + // } + return nodes.toHtml(false); } public static HTMLPage parse(String html) { - return new HTMLPage(Parser.createParser(html, null)); + try { + return new HTMLPage(Parser.createParser(html, null)); + } catch (ParserException e) { + return null; + } } } diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/IDFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/IDFilter.java new file mode 100644 index 0000000..42a69c5 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/IDFilter.java @@ -0,0 +1,26 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.Tag; + +public class IDFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final String id; + + public IDFilter(String id) { + this.id = id; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof Tag)) + return false; + final Tag tag = (Tag) node; + if (tag.getAttribute("id") == null) + return false; + if (!tag.getAttribute("id").equals(id)) + return false; + return true; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ImagePatternFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ImagePatternFilter.java new file mode 100644 index 0000000..5767ad2 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ImagePatternFilter.java @@ -0,0 +1,24 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.tags.ImageTag; + +public class ImagePatternFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern pattern; + + public ImagePatternFilter(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof ImageTag)) + return false; + final ImageTag frame = (ImageTag) node; + return pattern.matcher(frame.getImageURL()).matches(); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputIDFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputIDFilter.java new file mode 100644 index 0000000..e6d9526 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputIDFilter.java @@ -0,0 +1,26 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.tags.InputTag; + +public class InputIDFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final String id; + + public InputIDFilter(String id) { + this.id = id; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof InputTag)) + return false; + final InputTag input = (InputTag) node; + if (input.getAttribute("id") == null) + return false; + if (!input.getAttribute("id").equals(id)) + return false; + return true; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputNameFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputNameFilter.java new file mode 100644 index 0000000..709358d --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputNameFilter.java @@ -0,0 +1,26 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.tags.InputTag; + +public class InputNameFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final String name; + + public InputNameFilter(String name) { + this.name = name; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof InputTag)) + return false; + final InputTag input = (InputTag) node; + if(input.getAttribute("name") == null) + return false; + if (!input.getAttribute("name").equals(name)) + return false; + return true; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputValuePatternFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputValuePatternFilter.java new file mode 100644 index 0000000..c84dd80 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/InputValuePatternFilter.java @@ -0,0 +1,28 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.tags.InputTag; + +public class InputValuePatternFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern pattern; + + public InputValuePatternFilter(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof InputTag)) + return false; + final InputTag input = (InputTag) node; + if (input.getAttribute("value") == null) + return false; + if (!pattern.matcher(input.getAttribute("value")).matches()) + return false; + return true; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/LinkPatternFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/LinkPatternFilter.java new file mode 100644 index 0000000..b51addf --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/LinkPatternFilter.java @@ -0,0 +1,24 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.tags.LinkTag; + +public class LinkPatternFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern pattern; + + public LinkPatternFilter(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof LinkTag)) + return false; + final LinkTag link = (LinkTag) node; + return pattern.matcher(link.getLink()).matches(); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/NameFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/NameFilter.java new file mode 100644 index 0000000..62206ff --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/NameFilter.java @@ -0,0 +1,26 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.Tag; + +public class NameFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final String name; + + public NameFilter(String name) { + this.name = name; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof Tag)) + return false; + final Tag tag = (Tag) node; + if (tag.getAttribute("name") == null) + return false; + if (!tag.getAttribute("name").equals(name)) + return false; + return true; + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ScriptContainsFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ScriptContainsFilter.java new file mode 100644 index 0000000..1db6320 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ScriptContainsFilter.java @@ -0,0 +1,24 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.tags.ScriptTag; + +public class ScriptContainsFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern pattern; + + public ScriptContainsFilter(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof ScriptTag)) + return false; + final ScriptTag script = (ScriptTag) node; + return pattern.matcher(script.getScriptCode()).find(); + } +} diff --git a/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ScriptSrcFilter.java b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ScriptSrcFilter.java new file mode 100644 index 0000000..378c980 --- /dev/null +++ b/httpchannel-util/src/main/java/com/rogiel/httpchannel/util/htmlparser/ScriptSrcFilter.java @@ -0,0 +1,26 @@ +package com.rogiel.httpchannel.util.htmlparser; + +import java.util.regex.Pattern; + +import org.htmlparser.Node; +import org.htmlparser.NodeFilter; +import org.htmlparser.tags.ScriptTag; + +public class ScriptSrcFilter implements NodeFilter { + private static final long serialVersionUID = 1L; + private final Pattern pattern; + + public ScriptSrcFilter(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean accept(Node node) { + if (!(node instanceof ScriptTag)) + return false; + final ScriptTag script = (ScriptTag) node; + if (script.getAttribute("src") == null) + return false; + return pattern.matcher(script.getAttribute("src")).matches(); + } +} diff --git a/pom.xml b/pom.xml index 52b7c4d..a1a6fe5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,16 @@ 1.0.0 pom - Seedbox - HTTP Channel library + HttpChannel Library capable of downloading and uploading files from free servers using channels. httpchannel-api httpchannel-service httpchannel-util + httpchannel-channelcopy + httpchannel-tests + httpchannel-capcha @@ -87,4 +90,5 @@ test + http://httpchannel.rogiel.com/ \ No newline at end of file