From 3cc7911fedcbea2337237a04658495b369c73d35 Mon Sep 17 00:00:00 2001 From: bju Date: Sun, 24 Mar 2013 04:14:45 +0100 Subject: the answer traffic is now compressed if enabled on the server using zip or gzip --- vdr-vdrmanager/Makefile | 2 +- vdr-vdrmanager/compressor.cpp | 164 ++++ vdr-vdrmanager/compressor.h | 32 + vdr-vdrmanager/handler.cpp | 16 +- vdr-vdrmanager/helpers.cpp | 80 -- vdr-vdrmanager/helpers.h | 2 - vdr-vdrmanager/sock.cpp | 136 ++- vdr-vdrmanager/sock.h | 15 +- vdr-vdrmanager/vdrmanager.cpp | 28 +- vdr-vdrmanager/vdrmanagerthread.cpp | 5 +- vdr-vdrmanager/vdrmanagerthread.h | 3 +- .../vdrmanager/utils/svdrp/SvdrpClient.java | 928 +++++++++++---------- 12 files changed, 835 insertions(+), 576 deletions(-) create mode 100644 vdr-vdrmanager/compressor.cpp create mode 100644 vdr-vdrmanager/compressor.h diff --git a/vdr-vdrmanager/Makefile b/vdr-vdrmanager/Makefile index 4fae6b6..c43fa6a 100644 --- a/vdr-vdrmanager/Makefile +++ b/vdr-vdrmanager/Makefile @@ -47,7 +47,7 @@ SOFILE = libvdr-$(PLUGIN).so ### The object files (add further files here): -OBJS = $(PLUGIN).o sock.o vdrmanagerthread.o select.o handler.o helpers.o +OBJS = $(PLUGIN).o sock.o vdrmanagerthread.o select.o handler.o helpers.o compressor.o ### The main target: diff --git a/vdr-vdrmanager/compressor.cpp b/vdr-vdrmanager/compressor.cpp new file mode 100644 index 0000000..6a479f3 --- /dev/null +++ b/vdr-vdrmanager/compressor.cpp @@ -0,0 +1,164 @@ +/* + * compressor.cpp + * + * Created on: 23.03.2013 + * Author: bju + */ + +#include "compressor.h" + +#include +#include +#include +#include + +#define CHUNK 16384 + +cCompressor::cCompressor() { + size = 0; + data = NULL; +} + +cCompressor::~cCompressor() { + +} + +bool cCompressor::CompressGzip(string text) { + + int in_fd[2]; + if (pipe(in_fd) < 0) { + return false; + } + + int out_fd[2]; + if (pipe(out_fd) < 0) { + return false; + } + + int pid = fork(); + if (pid < 0) { + return false; + } + + if (pid == 0) { + // child + close(in_fd[1]); + close(out_fd[0]); + dup2(in_fd[0], 0); + dup2(out_fd[1], 1); + + execlp("gzip", "gzip", "-c", "-9", NULL); + + exit(-1); + + } else { + // parent + close(in_fd[0]); + close(out_fd[1]); + + write(in_fd[1], text.c_str(), text.length()); + close(in_fd[1]); + + char buf[32*1024]; + + for(;;) { + int count = read(out_fd[0], buf, sizeof(buf)); + if (count < 0) { + close(out_fd[0]); + return false; + } + if (count == 0) + break; + + char * newdata = (char *)malloc(size + count); + if (data != NULL) { + memcpy(newdata, data, size); + } + memcpy(newdata + size, buf, count); + if (data != NULL) { + free(data); + } + data = newdata; + size += count; + } + + close(out_fd[0]); + } + + return true; +} + +bool cCompressor::CompressZlib(string text) { + + int ret, flush; + unsigned have; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + int level = 9; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + ret = deflateInit(&strm, level); + if (ret != Z_OK) + return false; + + string input = text; + uInt len; + + do { + len = input.length(); + if (len > CHUNK) { + len = CHUNK; + } + flush = len > 0 ? Z_NO_FLUSH : Z_FINISH; + + strm.avail_in = len; + strm.next_in = (unsigned char *)input.c_str(); + + do { + strm.avail_out = CHUNK; + strm.next_out = out; + + ret = deflate(&strm, flush); + if (ret < 0) { + if (data != NULL) { + free(data); + } + return false; + } + + have = CHUNK - strm.avail_out; + + char * newdata = (char *)malloc(size + have); + if (data != NULL) { + memcpy(newdata, data, size); + } + memcpy(newdata + size, out, have); + if (data != NULL) { + free(data); + } + data = newdata; + size += have; + + } while (strm.avail_out == 0); + + input = input.substr(len); + + } while (flush != Z_FINISH); + + // clean up and return + (void)deflateEnd(&strm); + + return true; +} + +char * cCompressor::GetData() { + return data; +} + +size_t cCompressor::getDataSize() { + return size; +} + diff --git a/vdr-vdrmanager/compressor.h b/vdr-vdrmanager/compressor.h new file mode 100644 index 0000000..fbcc14f --- /dev/null +++ b/vdr-vdrmanager/compressor.h @@ -0,0 +1,32 @@ +/* + * compressor.h + * + * Created on: 23.03.2013 + * Author: bju + */ + +#ifndef COMPRESSOR_H_ +#define COMPRESSOR_H_ + +#include + +#define COMPRESSION_NONE 0 +#define COMPRESSION_ZLIB 1 +#define COMPRESSION_GZIP 2 + +using namespace std; + +class cCompressor { +private: + char * data; + size_t size; +public: + cCompressor(); + virtual ~cCompressor(); + bool CompressGzip(string text); + bool CompressZlib(string text); + char * GetData(); + size_t getDataSize(); +}; +#endif /* COMPRESSOR_H_ */ + diff --git a/vdr-vdrmanager/handler.cpp b/vdr-vdrmanager/handler.cpp index 43b500a..ea15296 100644 --- a/vdr-vdrmanager/handler.cpp +++ b/vdr-vdrmanager/handler.cpp @@ -17,6 +17,8 @@ bool cHandler::HandleNewClient(cVdrmanagerClientSocket * sock) bool cHandler::HandleClientRequest(cVdrmanagerClientSocket * sock) { + bool closeSocket = true; + while(sock->Read()) { // get lines @@ -48,6 +50,12 @@ bool cHandler::HandleClientRequest(cVdrmanagerClientSocket * sock) sock->SetLoggedIn(); sock->PutLine("!OK\r\n"); } + closeSocket = false; + } + else if (cmd == "COMPRESS") + { + sock->ActivateCompression(); + closeSocket = false; } else if (cmd == "TIMERS") { @@ -97,9 +105,15 @@ bool cHandler::HandleClientRequest(cVdrmanagerClientSocket * sock) else if (cmd == "QUIT") { // close socket - sock->PutLine(string("Good bye! :-)\n")); + sock->PutLine(string("Good bye! :-)\r\n")); sock->Disconnect(); } + + if (closeSocket) { + sock->Disconnect(); + } + + sock->Flush(); } } diff --git a/vdr-vdrmanager/helpers.cpp b/vdr-vdrmanager/helpers.cpp index 80721f3..b83eba0 100644 --- a/vdr-vdrmanager/helpers.cpp +++ b/vdr-vdrmanager/helpers.cpp @@ -1066,86 +1066,6 @@ int cHelpers::RecordingLengthInSeconds(cRecording* recording) { return Duration(recording) * 60; } -/** Compress a STL string using zlib with given compression level and return - * the binary data. */ -string cHelpers::compress_string(const string& str, int compressionlevel) { - z_stream zs; // z_stream is zlib's control structure - memset(&zs, 0, sizeof(zs)); - - if (deflateInit(&zs, compressionlevel) != Z_OK) - throw(runtime_error("deflateInit failed while compressing.")); - - zs.next_in = (Bytef*) str.data(); - zs.avail_in = str.size(); // set the z_stream's input - - int ret; - char outbuffer[32768]; - string outstring; - -// retrieve the compressed bytes blockwise - do { - zs.next_out = reinterpret_cast(outbuffer); - zs.avail_out = sizeof(outbuffer); - - ret = deflate(&zs, Z_FINISH); - - if (outstring.size() < zs.total_out) { - // append the block to the output string - outstring.append(outbuffer, zs.total_out - outstring.size()); - } - } while (ret == Z_OK); - - deflateEnd(&zs); - - if (ret != Z_STREAM_END) { // an error occurred that was not EOF - ostringstream oss; - oss << "Exception during zlib compression: (" << ret << ") " << zs.msg; - throw(runtime_error(oss.str())); - } - - return outstring; -} - -/** Decompress an STL string using zlib and return the original data. */ -string cHelpers::decompress_string(const string& str) { - z_stream zs; // z_stream is zlib's control structure - memset(&zs, 0, sizeof(zs)); - - if (inflateInit(&zs) != Z_OK) - throw(runtime_error("inflateInit failed while decompressing.")); - - zs.next_in = (Bytef*) str.data(); - zs.avail_in = str.size(); - - int ret; - char outbuffer[32768]; - string outstring; - -// get the decompressed bytes blockwise using repeated calls to inflate - do { - zs.next_out = reinterpret_cast(outbuffer); - zs.avail_out = sizeof(outbuffer); - - ret = inflate(&zs, 0); - - if (outstring.size() < zs.total_out) { - outstring.append(outbuffer, zs.total_out - outstring.size()); - } - - } while (ret == Z_OK); - - inflateEnd(&zs); - - if (ret != Z_STREAM_END) { // an error occurred that was not EOF - ostringstream oss; - oss << "Exception during zlib decompression: (" << ret << ") " - << zs.msg; - throw(runtime_error(oss.str())); - } - - return outstring; -} - //These three methodes were stolen from vdr-restfulapi project. Thanks! std::queue cHelpers::ConvertToBinary(int v) { int b; diff --git a/vdr-vdrmanager/helpers.h b/vdr-vdrmanager/helpers.h index b9ebee5..18576be 100644 --- a/vdr-vdrmanager/helpers.h +++ b/vdr-vdrmanager/helpers.h @@ -31,8 +31,6 @@ public: static string ToUpper(string text); static string ToLower(string text); static string Trim(string text); - static string decompress_string(const string& str); - static string compress_string(const string& str, int compressionlevel = Z_BEST_COMPRESSION); static long Duration(cRecording* recording); private: static string SafeCall(string (*)()); diff --git a/vdr-vdrmanager/sock.cpp b/vdr-vdrmanager/sock.cpp index 1d17ee2..2102c36 100644 --- a/vdr-vdrmanager/sock.cpp +++ b/vdr-vdrmanager/sock.cpp @@ -5,6 +5,7 @@ #include #include "sock.h" #include "helpers.h" +#include "compressor.h" static int clientno = 0; @@ -60,10 +61,12 @@ cVdrmanagerServerSocket::cVdrmanagerServerSocket() : cVdrmanagerServerSocket::~cVdrmanagerServerSocket() { } -bool cVdrmanagerServerSocket::Create(int port, const char * password, bool forceCheckSvrp) { - // save password - this->password = password; +bool cVdrmanagerServerSocket::Create(int port, const char * password, bool forceCheckSvrp, int compressionMode) { + + this->password = password; this->forceCheckSvdrp = forceCheckSvrp; + this->compressionMode = compressionMode; + // create socket sock = socket(PF_INET, SOCK_STREAM, 0); if (sock < 0) { @@ -111,7 +114,7 @@ cVdrmanagerClientSocket * cVdrmanagerServerSocket::Accept() { int newsock = accept(sock, (struct sockaddr *) &clientname, &size); if (newsock > 0) { // create client socket - newsocket = new cVdrmanagerClientSocket(password); + newsocket = new cVdrmanagerClientSocket(password, compressionMode); if (!newsocket->Attach(newsock)) { delete newsocket; return NULL; @@ -138,12 +141,20 @@ cVdrmanagerClientSocket * cVdrmanagerServerSocket::Accept() { /* * cVdrmonClientSocket */ -cVdrmanagerClientSocket::cVdrmanagerClientSocket(const char * password) { - readbuf = writebuf = ""; +cVdrmanagerClientSocket::cVdrmanagerClientSocket(const char * password, int compressionMode) { + readbuf = ""; + writebuf = ""; + sendbuf = NULL; + sendsize = 0; + sendoffset = 0; disconnected = false; + initDisconnect = false; client = ++clientno; this->password = password; + this->compressionMode = compressionMode; login = false; + compression = false; + initCompression = false; } cVdrmanagerClientSocket::~cVdrmanagerClientSocket() { @@ -212,42 +223,74 @@ bool cVdrmanagerClientSocket::Disconnected() { } void cVdrmanagerClientSocket::Disconnect() { - disconnected = true; + initDisconnect = true; } bool cVdrmanagerClientSocket::PutLine(string line) { - //TODO http://projects.vdr-developer.org/issues/790 - //string line2 = cHelpers::compress_string(line); - //unsigned long l = line.size(); - //unsigned long l2 = line2.size(); - //if(l2 == 0){ - //l2 = 1; - //} - //dsyslog("[vdrmanager] PutLine, line size is %lu, with zlib it would be %lu (factor %lu)", l, l2, l/l2); - // add line to write buffer - writebuf += line; - - // data present? - if (writebuf.length() > 0) { + + // fill writebuf + if (line.length() > 0) { + writebuf += line; + return true; + } + + // initialize sendbuf if needed + if (sendbuf == NULL) { + if (!compression) { + sendbuf = (char *)malloc(writebuf.length()+1); + strcpy(sendbuf, writebuf.c_str()); + sendsize = writebuf.length(); + } else { + Compress(); + } + sendoffset = 0; + writebuf.clear(); + } + + // send data + if (sendsize > 0) { + // write so many bytes as possible - int rc = write(sock, writebuf.c_str(), writebuf.length()); + int rc = write(sock, sendbuf + sendoffset, sendsize); if (rc < 0 && errno != EAGAIN) { LOG_ERROR; + + if (sendbuf != NULL) { + free(sendbuf); + sendbuf = NULL; + } + return false; } + sendsize -= rc; + sendoffset += rc; + } + + if (sendsize == 0) { - // move the remainder - if (rc > 0) - writebuf = writebuf.substr(rc, writebuf.length() - rc); + if (sendbuf != NULL) { + free(sendbuf); + sendbuf = NULL; + } + + if (initCompression) { + isyslog("Compression is activated now"); + initCompression = false; + compression = true; + } + + if (initDisconnect) { + initDisconnect = false; + disconnected = true; + } } return true; } bool cVdrmanagerClientSocket::Flush() { - string empty = ""; - return PutLine(empty); + return PutLine(""); } bool cVdrmanagerClientSocket::Attach(int fd) { @@ -260,7 +303,7 @@ int cVdrmanagerClientSocket::GetClientId() { } bool cVdrmanagerClientSocket::WritePending() { - return writebuf.length() > 0; + return sendoffset < sendsize; } bool cVdrmanagerClientSocket::IsLoggedIn() { @@ -270,3 +313,42 @@ bool cVdrmanagerClientSocket::IsLoggedIn() { void cVdrmanagerClientSocket::SetLoggedIn() { login = true; } + +void cVdrmanagerClientSocket::ActivateCompression() { + + string mode = "NONE"; + switch (compressionMode) { + case COMPRESSION_GZIP: + mode = "GZIP"; + initCompression = true; + break; + case COMPRESSION_ZLIB: + mode = "ZLIB"; + initCompression = true; + break; + default: + mode = "NONE"; + break; + } + + PutLine("!OK " + mode + "\r\n"); +} + +void cVdrmanagerClientSocket::Compress() { + cCompressor compressor = cCompressor(); + + switch (compressionMode) { + case COMPRESSION_GZIP: + compressor.CompressGzip(writebuf); + break; + case COMPRESSION_ZLIB: + compressor.CompressZlib(writebuf); + break; + } + + sendbuf = compressor.GetData(); + sendsize = compressor.getDataSize(); + + double ratio = 1.0 * writebuf.length() / sendsize; + isyslog("Compression stats: raw %ld, compressed %ld, ratio %f:1", writebuf.length(), sendsize, ratio); +} diff --git a/vdr-vdrmanager/sock.h b/vdr-vdrmanager/sock.h index 0234afc..25a7b83 100644 --- a/vdr-vdrmanager/sock.h +++ b/vdr-vdrmanager/sock.h @@ -5,6 +5,7 @@ #ifndef _VDRMON_SOCK #define _VDRMON_SOCK +#include #include #include #include @@ -17,6 +18,7 @@ protected: int sock; const char * password; bool forceCheckSvdrp; + int compressionMode; protected: cVdrmanagerSocket(); bool IsPasswordSet(); @@ -33,11 +35,18 @@ class cVdrmanagerClientSocket : public cVdrmanagerSocket private: string readbuf; string writebuf; + char * sendbuf; + size_t sendsize; + size_t sendoffset; bool disconnected; + bool initDisconnect; int client; bool login; + bool compression; + bool initCompression; + int compressionMode; public: - cVdrmanagerClientSocket(const char * password); + cVdrmanagerClientSocket(const char * password, int compressionMode); virtual ~cVdrmanagerClientSocket(); bool Attach(int fd); bool IsLineComplete(); @@ -51,6 +60,8 @@ public: bool WritePending(); bool IsLoggedIn(); void SetLoggedIn(); + void ActivateCompression(); + void Compress(); }; class cVdrmanagerServerSocket : public cVdrmanagerSocket @@ -58,7 +69,7 @@ class cVdrmanagerServerSocket : public cVdrmanagerSocket public: cVdrmanagerServerSocket(); virtual ~cVdrmanagerServerSocket(); - bool Create(int port, const char * password, bool forceCheckSvdrp); + bool Create(int port, const char * password, bool forceCheckSvdrp, int compressionMode); cVdrmanagerClientSocket * Accept(); }; diff --git a/vdr-vdrmanager/vdrmanager.cpp b/vdr-vdrmanager/vdrmanager.cpp index 2b81464..4d00f41 100644 --- a/vdr-vdrmanager/vdrmanager.cpp +++ b/vdr-vdrmanager/vdrmanager.cpp @@ -12,6 +12,7 @@ #include #include #include "vdrmanagerthread.h" +#include "compressor.h" #define VDRMANAGER_PORT 6420 @@ -26,6 +27,7 @@ private: const char * password; bool forceCheckSvdrp; bool forceDelete; + int compressionMode; protected: public: cVdrManager(void); @@ -73,12 +75,17 @@ cMenuSetupPage * cVdrManager::SetupMenu(void) { } const char * cVdrManager::CommandLineHelp(void) { - return " -p port port number to listen to\n -P password password (none if not given). No password forces check against svdrphosts.conf.\n -s force check against svdrphosts.conf, even if a password was given\n -f force delete of a timer or a recording even if they are active\n"; + return + " -p port port number to listen to\n" + " -P password password (none if not given). No password forces check against svdrphosts.conf.\n" + " -s force check against svdrphosts.conf, even if a password was given\n" + " -f force delete of a timer or a recording even if they are active\n" + " -c compression selects the compression mode to use (zlib, gzip or none)"; } bool cVdrManager::ProcessArgs(int argc, char *argv[]) { int c; - while ((c = getopt(argc, argv, "p:P:s:f")) != -1) + while ((c = getopt(argc, argv, "c:p:P:sf")) != -1) switch (c) { case 'p': port = atoi(optarg); @@ -92,13 +99,16 @@ bool cVdrManager::ProcessArgs(int argc, char *argv[]) { case 'f': forceDelete = true; break; + case 'c': + if (optarg[0] == 'g') { + compressionMode = COMPRESSION_GZIP; + } else if (optarg[0] == 'z') { + compressionMode = COMPRESSION_ZLIB; + } else { + compressionMode = COMPRESSION_NONE; + } + break; case '?': - if (optopt == 'c') { - fprintf(stderr, "Option -%c requires an argument.\n", optopt); - } else if (isprint(optopt)) - fprintf(stderr, "Unknown option `-%c'.\n", optopt); - else - fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); return false; default: return false; @@ -115,7 +125,7 @@ bool cVdrManager::Initialize(void) { // Initialize any background activities the plugin shall perform. // Start any background activities the plugin shall perform. - Thread = new cVdrManagerThread(port, password, forceCheckSvdrp); + Thread = new cVdrManagerThread(port, password, forceCheckSvdrp, compressionMode); return Thread != NULL; } diff --git a/vdr-vdrmanager/vdrmanagerthread.cpp b/vdr-vdrmanager/vdrmanagerthread.cpp index 2a4ca64..51301e5 100644 --- a/vdr-vdrmanager/vdrmanagerthread.cpp +++ b/vdr-vdrmanager/vdrmanagerthread.cpp @@ -8,12 +8,13 @@ #include "select.h" #include "helpers.h" -cVdrManagerThread::cVdrManagerThread(int port, const char * password, bool forceCheckSvdrp) +cVdrManagerThread::cVdrManagerThread(int port, const char * password, bool forceCheckSvdrp, int compressionMode) { select = NULL; this -> port = port; this -> password = password; this -> forceCheckSvdrp = forceCheckSvdrp; + this -> compressionMode = compressionMode; } cVdrManagerThread::~cVdrManagerThread() @@ -43,7 +44,7 @@ bool cVdrManagerThread::Init() // create server socket cVdrmanagerServerSocket * sock = new cVdrmanagerServerSocket(); - if (sock == NULL || !sock->Create(port, password, forceCheckSvdrp)) + if (sock == NULL || !sock->Create(port, password, forceCheckSvdrp, compressionMode)) return false; // register server socket diff --git a/vdr-vdrmanager/vdrmanagerthread.h b/vdr-vdrmanager/vdrmanagerthread.h index 83f5f31..0dd3646 100644 --- a/vdr-vdrmanager/vdrmanagerthread.h +++ b/vdr-vdrmanager/vdrmanagerthread.h @@ -22,8 +22,9 @@ private: int port; const char * password; bool forceCheckSvdrp; + int compressionMode; public: - cVdrManagerThread(int port, const char * password, bool forceCheckSvdrp); + cVdrManagerThread(int port, const char * password, bool forceCheckSvdrp, int compressionMode); virtual void Action(void); void Shutdown(); private: diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/utils/svdrp/SvdrpClient.java b/vdrmanager/src/de/bjusystems/vdrmanager/utils/svdrp/SvdrpClient.java index f6307f5..5c33981 100644 --- a/vdrmanager/src/de/bjusystems/vdrmanager/utils/svdrp/SvdrpClient.java +++ b/vdrmanager/src/de/bjusystems/vdrmanager/utils/svdrp/SvdrpClient.java @@ -12,8 +12,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; -import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import android.util.Log; @@ -28,455 +29,480 @@ import de.bjusystems.vdrmanager.data.Preferences; */ public abstract class SvdrpClient { - private final String TAG = getClass().getName(); - - /** Socket for connection to SVDRP */ - private Socket socket; - /** Output stream for sending commands */ - private OutputStream outputStream; - /** Input stream for reading answer lines */ - private InputStream inputStream; - /** flag for stopping the current request */ - private boolean abort; - /** listener */ - private final List svdrpListeners = new ArrayList(); - - private final List> svdrpResultListeners = new ArrayList>(); - - private final List svdrpExceptionListeners = new ArrayList(); - - private final List> svdrpFinishedListeners = new ArrayList>(); - - /** list of results */ - // private final List results = new ArrayList(); - /** should the listener be informed about each received result */ - // private boolean resultInfoEnabled = false; - /** - * @return true if the client has result - */ - // public boolean hasResults(){ - // return results.isEmpty() == false; - // } - - private Timer watchDog = new Timer(); - - // private NativeDES crypt = new NativeDES(); - - public boolean isConnected() { - if (socket == null) { - return false; - } - return socket.isConnected(); - } - - /** - * Parse received answer line - * - * @param line - * line - * @return received data object or null if not completed yet - */ - protected abstract Result parseAnswer(String line); - - public abstract int getProgressTextId(); - - public abstract void run(); - - /** - * Constructor - * - * @param prefs - * Preferences - */ - protected SvdrpClient() { - // results.clear(); - } - - /** - * Remove all listeners - */ - public void clearListener() { - svdrpExceptionListeners.clear(); - svdrpListeners.clear(); - svdrpResultListeners.clear(); - } - - /** - * Adds the listener to the list of listeners - * - * @param listener - * listener - */ - public void addSvdrpListener(final SvdrpListener listener) { - svdrpListeners.add(listener); - } - - /** - * Adds the listener to the list of listeners - * - * @param listener - * listener - */ - public void addSvdrpResultListener( - final SvdrpResultListener listener) { - svdrpResultListeners.add(listener); - } - - /** - * Adds the listener to the list of listeners - * - * @param listener - * listener - */ - public void addSvdrpFinishedListener( - final SvdrpFinishedListener listener) { - svdrpFinishedListeners.add(listener); - } - - /** - * Adds the listener to the list of listeners - * - * @param listener - * listener - */ - public void addSvdrpExceptionListener(final SvdrpExceptionListener listener) { - svdrpExceptionListeners.add(listener); - } - - /** - * Removes the listener from the list of listeners - * - * @param listener - * listener - */ - public void removeSvdrpListener(final SvdrpListener listener) { - svdrpListeners.remove(listener); - } - - public void removeSvdrpResultListener( - final SvdrpResultListener listener) { - svdrpResultListeners.remove(listener); - } - - public void removeSvdrpExceptionListener( - final SvdrpExceptionListener listener) { - svdrpExceptionListeners.remove(listener); - } - - /** - * Cancel the current request - */ - public void abort() { - abort = true; - try { - if (isConnected()) { - socket.shutdownInput(); - socket.shutdownOutput(); - socket.close(); - } - } catch (Exception ex) { - Log.w(TAG, ex); - } - - } - - // /** - // * Gets the list of results - // * - // * @return results - // */ - // public List getResults() { - // return results; - // } - - /** - * Connect to SVDRP - * - * @param host - * host - * @param port - * port - * @param ssl - * use SSL - * @throws IOException - * on errors - */ - protected boolean connect() throws IOException { - - final Preferences prefs = Preferences.get(); - try { - // connect - informListener(SvdrpEvent.CONNECTING); - - if (false && Preferences.get().isSecure()) { - - SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory - .getDefault(); // Erzeugt eine SSLSocketFactory mit - // Standartkonfiguration - socket = (SSLSocket) factory.createSocket(); - } else { - socket = new Socket(); - } - - socket.connect( - new InetSocketAddress(prefs.getSvdrpHost(), prefs - .getSvdrpPort()), - prefs.getConnectionTimeout() * 1000);// 8 secs for connect - if (abort) { - informListener(SvdrpEvent.ABORTED); - } - // - socket.setSoTimeout(prefs.getReadTimeout() * 1000);// 15 sec for - // each read - final long delay = C.ONE_MINUTE_IN_MILLIS * prefs.getTimeout() * 60; // in - // 3 - // minutes - // we - // abort - // the - // communication - watchDog.schedule(new TimerTask() { - @Override - public void run() { - Log.w(TAG, "Aborted after " + delay + " ms"); - abort = true; - } - }, delay); - informListener(SvdrpEvent.CONNECTED); - } catch (final SocketTimeoutException sote) { - Log.w(TAG, sote); - if (abort) { - informListener(SvdrpEvent.ABORTED); - } else { - informListener(SvdrpEvent.CONNECTION_TIMEOUT); - } - return false; - } catch (final Exception e) { - - Log.w(TAG, e); - if (abort) { - informListener(SvdrpEvent.ABORTED); - } else { - informListener(SvdrpEvent.CONNECT_ERROR); - } - return false; - } - - // create streams - outputStream = socket.getOutputStream(); - inputStream = socket.getInputStream(); - // TODO http://projects.vdr-developer.org/issues/790 - // inputStream = new InflaterInputStream(socket.getInputStream()) - - // password needed? - informListener(SvdrpEvent.LOGIN); - writeLine("passwd " + prefs.getPassword()); - if (!readLine().startsWith("!OK")) { - informListener(SvdrpEvent.LOGIN_ERROR); - disconnect(); - return false; - } else { - informListener(SvdrpEvent.LOGGED_IN); - } - return true; - } - - /** - * Disconnect from SVDRP if connected - * - * @throws IOException - * on errors - */ - protected void disconnect() throws IOException { - informListener(SvdrpEvent.DISCONNECTING); - if (socket != null && socket.isConnected()) { - socket.close(); - socket = null; - } - informListener(SvdrpEvent.DISCONNECTED); - } - - /** - * Sends one line to SVDRP - * - * @param line - * line of text - * @throws IOException - * on errors - */ - protected void writeLine(final String line) throws IOException { - - String command = line + "\r\n"; - // if (false && Preferences.get().isSecure()) { - // command = crypt.encrypt(command, Preferences.get().getPassword()); - // } - final byte[] bytes = command.getBytes("utf-8"); - outputStream.write(bytes); - outputStream.flush(); - } - - /** - * Reads one line from SVDRP - * - * @return line read - * @throws IOException - * on errors - */ - protected String readLine() throws IOException { - - // handle not gzipped input - final ByteArrayOutputStream lineBytes = new ByteArrayOutputStream(); - - for (;;) { - - // read next char - final int d = inputStream.read(); - if (d < 0) { - break; - } - final char c = (char) d; - - // skip '\r' - if (c == '\r') { - continue; - } - - // with '\n' the line is completed - if (c == '\n') { - break; - } - - // remember char - lineBytes.write(c); - } - - String line = null; - try { - line = lineBytes.toString(Preferences.get().getEncoding()); - } catch (UnsupportedEncodingException usex) { - Log.w(TAG, usex); - line = lineBytes.toString(); - } - // if (false && Preferences.get().isSecure()) { - // line = crypt.decrypt(line, Preferences.get().getPassword()); - // } - return line; - } - - public void runCommand(final String command) { - - try { - - // reset cancel flag - abort = false; - - // clear results - // results.clear(); - - // connect - final boolean connected = connect(); - if (!connected) { - return; - } - - // send command - informListener(SvdrpEvent.COMMAND_SENDING); - writeLine(command); - informListener(SvdrpEvent.COMMAND_SENT); - Log.i(TAG, SvdrpEvent.COMMAND_SENT + ":" + command); - - // read first line - String line = readLine(); - if (!line.startsWith("START")) { - Log.w(TAG, line); - throw new IOException("Answer not wellformed: " + line); - } - - // read answer lines - for (; !abort;) { - - // get next line - line = readLine(); - if (line.length() == 0) { - break; - } - - // last line? - if (line.startsWith("END")) { - break; - } - - // error? - if (line.startsWith("!ERROR")) { - Log.w(TAG, line); - String msg; - if (line.startsWith("!ERROR:")) { - msg = line.substring(7); - } else { - msg = line; - } - disconnect(); - informListener(SvdrpEvent.ERROR, new SvdrpException(msg)); - break; - } - - // delegate analysis - Result result = null; - try { - result = parseAnswer(line); - - } catch (Exception ex) { - Log.w(TAG, ex); - disconnect(); - Log.w(TAG, "line: " + line); - informListener(SvdrpEvent.ERROR, ex); - return; - } - if (result != null) { - informListener(result); - // results.add(result); - // if (resultInfoEnabled) { - - // } - } - - } - - // disconnect - disconnect(); - - if (abort) { - informListener(SvdrpEvent.ABORTED); - } else { - informListener(SvdrpEvent.FINISHED_SUCCESS); - } - - } catch (final Exception e) { - Log.w(TAG, e); - informListener(SvdrpEvent.FINISHED_ABNORMALY, e); - } - } - - // public void setResultInfoEnabled(final boolean resultInfoEnabled) { - // this.resultInfoEnabled = resultInfoEnabled; - // } - - protected void informListener(final SvdrpEvent event, final Throwable e) { - for (final SvdrpExceptionListener listener : svdrpExceptionListeners) { - listener.svdrpEvent(event, e); - } - } - - protected void informListener(final SvdrpEvent event) { - for (final SvdrpListener listener : svdrpListeners) { - listener.svdrpEvent(event); - } - } - - protected void informListener(final Result result) { - for (final SvdrpResultListener listener : svdrpResultListeners) { - listener.svdrpEvent(result); - } - } + private final String TAG = getClass().getName(); + + /** Socket for connection to SVDRP */ + private Socket socket; + /** Output stream for sending commands */ + private OutputStream outputStream; + /** Input stream for reading answer lines */ + private InputStream inputStream; + /** flag for stopping the current request */ + private boolean abort; + /** listener */ + private final List svdrpListeners = new ArrayList(); + + private final List> svdrpResultListeners = new ArrayList>(); + + private final List svdrpExceptionListeners = new ArrayList(); + + private final List> svdrpFinishedListeners = new ArrayList>(); + + /** list of results */ + // private final List results = new ArrayList(); + /** should the listener be informed about each received result */ + // private boolean resultInfoEnabled = false; + /** + * @return true if the client has result + */ + // public boolean hasResults(){ + // return results.isEmpty() == false; + // } + + private final Timer watchDog = new Timer(); + + // private NativeDES crypt = new NativeDES(); + + public boolean isConnected() { + if (socket == null) { + return false; + } + return socket.isConnected(); + } + + /** + * Parse received answer line + * + * @param line + * line + * @return received data object or null if not completed yet + */ + protected abstract Result parseAnswer(String line); + + public abstract int getProgressTextId(); + + public abstract void run(); + + /** + * Constructor + * + * @param prefs + * Preferences + */ + protected SvdrpClient() { + // results.clear(); + } + + /** + * Remove all listeners + */ + public void clearListener() { + svdrpExceptionListeners.clear(); + svdrpListeners.clear(); + svdrpResultListeners.clear(); + } + + /** + * Adds the listener to the list of listeners + * + * @param listener + * listener + */ + public void addSvdrpListener(final SvdrpListener listener) { + svdrpListeners.add(listener); + } + + /** + * Adds the listener to the list of listeners + * + * @param listener + * listener + */ + public void addSvdrpResultListener( + final SvdrpResultListener listener) { + svdrpResultListeners.add(listener); + } + + /** + * Adds the listener to the list of listeners + * + * @param listener + * listener + */ + public void addSvdrpFinishedListener( + final SvdrpFinishedListener listener) { + svdrpFinishedListeners.add(listener); + } + + /** + * Adds the listener to the list of listeners + * + * @param listener + * listener + */ + public void addSvdrpExceptionListener(final SvdrpExceptionListener listener) { + svdrpExceptionListeners.add(listener); + } + + /** + * Removes the listener from the list of listeners + * + * @param listener + * listener + */ + public void removeSvdrpListener(final SvdrpListener listener) { + svdrpListeners.remove(listener); + } + + public void removeSvdrpResultListener( + final SvdrpResultListener listener) { + svdrpResultListeners.remove(listener); + } + + public void removeSvdrpExceptionListener( + final SvdrpExceptionListener listener) { + svdrpExceptionListeners.remove(listener); + } + + /** + * Cancel the current request + */ + public void abort() { + abort = true; + try { + if (isConnected()) { + socket.shutdownInput(); + socket.shutdownOutput(); + socket.close(); + } + } catch (final Exception ex) { + Log.w(TAG, ex); + } + + } + + // /** + // * Gets the list of results + // * + // * @return results + // */ + // public List getResults() { + // return results; + // } + + /** + * Connect to SVDRP + * + * @param host + * host + * @param port + * port + * @param ssl + * use SSL + * @throws IOException + * on errors + */ + protected boolean connect() throws IOException { + + final Preferences prefs = Preferences.get(); + try { + // connect + informListener(SvdrpEvent.CONNECTING); + + if (false && Preferences.get().isSecure()) { + + final SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory + .getDefault(); // Erzeugt eine SSLSocketFactory mit + // Standartkonfiguration + socket = factory.createSocket(); + } else { + socket = new Socket(); + } + + socket.connect( + new InetSocketAddress(prefs.getSvdrpHost(), prefs + .getSvdrpPort()), + prefs.getConnectionTimeout() * 1000);// 8 secs for connect + if (abort) { + informListener(SvdrpEvent.ABORTED); + } + // + socket.setSoTimeout(prefs.getReadTimeout() * 1000);// 15 sec for + // each read + final long delay = C.ONE_MINUTE_IN_MILLIS * prefs.getTimeout() * 60; // in + // 3 + // minutes + // we + // abort + // the + // communication + watchDog.schedule(new TimerTask() { + @Override + public void run() { + Log.w(TAG, "Aborted after " + delay + " ms"); + abort = true; + } + }, delay); + informListener(SvdrpEvent.CONNECTED); + } catch (final SocketTimeoutException sote) { + Log.w(TAG, sote); + if (abort) { + informListener(SvdrpEvent.ABORTED); + } else { + informListener(SvdrpEvent.CONNECTION_TIMEOUT); + } + return false; + } catch (final Exception e) { + + Log.w(TAG, e); + if (abort) { + informListener(SvdrpEvent.ABORTED); + } else { + informListener(SvdrpEvent.CONNECT_ERROR); + } + return false; + } + + // create streams + outputStream = socket.getOutputStream(); + inputStream = socket.getInputStream(); + // TODO http://projects.vdr-developer.org/issues/790 + // inputStream = new InflaterInputStream(socket.getInputStream()) + + // password needed? + informListener(SvdrpEvent.LOGIN); + writeLine("passwd " + prefs.getPassword()); + if (!readLine().startsWith("!OK")) { + informListener(SvdrpEvent.LOGIN_ERROR); + disconnect(); + return false; + } else { + informListener(SvdrpEvent.LOGGED_IN); + } + return true; + } + + /** + * Disconnect from SVDRP if connected + * + * @throws IOException + * on errors + */ + protected void disconnect() throws IOException { + informListener(SvdrpEvent.DISCONNECTING); + if (socket != null && socket.isConnected()) { + socket.close(); + socket = null; + } + informListener(SvdrpEvent.DISCONNECTED); + } + + /** + * Sends one line to SVDRP + * + * @param line + * line of text + * @throws IOException + * on errors + */ + protected void writeLine(final String line) throws IOException { + + final String command = line + "\r\n"; + // if (false && Preferences.get().isSecure()) { + // command = crypt.encrypt(command, Preferences.get().getPassword()); + // } + final byte[] bytes = command.getBytes("utf-8"); + outputStream.write(bytes); + outputStream.flush(); + } + + /** + * Reads one line from SVDRP + * + * @return line read + * @throws IOException + * on errors + */ + protected String readLine() throws IOException { + + // handle not gzipped input + final ByteArrayOutputStream lineBytes = new ByteArrayOutputStream(); + + for (;;) { + + // read next char + final int d = inputStream.read(); + if (d < 0) { + break; + } + final char c = (char) d; + + // skip '\r' + if (c == '\r') { + continue; + } + + // with '\n' the line is completed + if (c == '\n') { + break; + } + + // remember char + lineBytes.write(c); + } + + String line = null; + try { + line = lineBytes.toString(Preferences.get().getEncoding()); + } catch (final UnsupportedEncodingException usex) { + Log.w(TAG, usex); + line = lineBytes.toString(); + } + // if (false && Preferences.get().isSecure()) { + // line = crypt.decrypt(line, Preferences.get().getPassword()); + // } + return line; + } + + public void runCommand(final String command) { + + try { + + // reset cancel flag + abort = false; + + // clear results + // results.clear(); + + // connect + final boolean connected = connect(); + if (!connected) { + return; + } + + // activate compression + Log.i(TAG, "Activate compression"); + writeLine("compress"); + + // send command + informListener(SvdrpEvent.COMMAND_SENDING); + writeLine(command); + informListener(SvdrpEvent.COMMAND_SENT); + Log.i(TAG, SvdrpEvent.COMMAND_SENT + ":" + command); + + // get the answer for the compress command or + // the first line of the answer for the command + String line = readLine(); + if (line.startsWith("!OK")) { + final String[] words = line.split(" "); + if (words.length > 1) { + final String mode = words[1].toUpperCase(); + if (mode.equals("ZLIB")) { + Log.i(TAG, "ZLIB compression activated"); + inputStream = new InflaterInputStream(inputStream); + } else if (mode.equals("GZIP")) { + Log.i(TAG, "GZIP compression activated"); + inputStream = new GZIPInputStream(inputStream); + } else { + Log.i(TAG, "NO compression activated"); + } + } + line = readLine(); + } else { + Log.i(TAG, "NO compression activated"); + } + + // correct answer? + if (!line.startsWith("START")) { + Log.w(TAG, line); + throw new IOException("Answer not wellformed: " + line); + } + + // read answer lines + for (; !abort;) { + + // get next line + line = readLine(); + if (line.length() == 0) { + break; + } + + // last line? + if (line.startsWith("END")) { + break; + } + + // error? + if (line.startsWith("!ERROR")) { + Log.w(TAG, line); + String msg; + if (line.startsWith("!ERROR:")) { + msg = line.substring(7); + } else { + msg = line; + } + disconnect(); + informListener(SvdrpEvent.ERROR, new SvdrpException(msg)); + break; + } + + // delegate analysis + Result result = null; + try { + result = parseAnswer(line); + + } catch (final Exception ex) { + Log.w(TAG, ex); + disconnect(); + Log.w(TAG, "line: " + line); + informListener(SvdrpEvent.ERROR, ex); + return; + } + if (result != null) { + informListener(result); + // results.add(result); + // if (resultInfoEnabled) { + + // } + } + + } + + // disconnect + disconnect(); + + if (abort) { + informListener(SvdrpEvent.ABORTED); + } else { + informListener(SvdrpEvent.FINISHED_SUCCESS); + } + + } catch (final Exception e) { + Log.w(TAG, e); + informListener(SvdrpEvent.FINISHED_ABNORMALY, e); + } + } + + // public void setResultInfoEnabled(final boolean resultInfoEnabled) { + // this.resultInfoEnabled = resultInfoEnabled; + // } + + protected void informListener(final SvdrpEvent event, final Throwable e) { + for (final SvdrpExceptionListener listener : svdrpExceptionListeners) { + listener.svdrpEvent(event, e); + } + } + + protected void informListener(final SvdrpEvent event) { + for (final SvdrpListener listener : svdrpListeners) { + listener.svdrpEvent(event); + } + } + + protected void informListener(final Result result) { + for (final SvdrpResultListener listener : svdrpResultListeners) { + listener.svdrpEvent(result); + } + } } -- cgit v1.2.3