vnc: Add some command line options and enable auth

 * Parse ipv4/v6 address and port
 * Allow setting or clearing of the server password
diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp
index 084bfa2..a637c47 100644
--- a/src/VNCFlinger.cpp
+++ b/src/VNCFlinger.cpp
@@ -31,15 +31,67 @@
 
 using namespace android;
 
+VNCFlinger::VNCFlinger() {
+    mOrientation = -1;
+    mMainDpy = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+    updateDisplayProjection();
+
+    createVNCServer();
+
+    String8 v4("127.0.0.1");
+    String8 v6("::1");
+
+    setListenAddress(v4, false);
+    setListenAddress(v6, true);
+}
+
+status_t VNCFlinger::setListenAddress(String8& address, bool v6) {
+    if (v6) {
+        mVNCScreen->listen6Interface = const_cast<char *>(address.string());
+        return NO_ERROR;
+    }
+    if (!rfbStringToAddr(const_cast<char *>(address.string()), &(mVNCScreen->listenInterface))) {
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+status_t VNCFlinger::setPort(unsigned int port) {
+    if (port > 65535) {
+        port = 0;
+    }
+    if (port == 0) {
+        mVNCScreen->autoPort = 1;
+    } else {
+        mVNCScreen->autoPort = 0;
+        mVNCScreen->port = port;
+        mVNCScreen->ipv6port = port;
+    }
+    return NO_ERROR;
+}
+
+status_t VNCFlinger::clearPassword() {
+    std::remove(VNC_AUTH_FILE);
+    ALOGW("Password authentication disabled");
+    return OK;
+}
+
+status_t VNCFlinger::setPassword(String8& passwd) {
+    String8 path(VNC_AUTH_FILE);
+    if (rfbEncryptAndStorePasswd(const_cast<char *>(passwd.string()),
+                                 const_cast<char *>(path.string())) != 0) {
+        ALOGE("Failed to set password");
+        return BAD_VALUE;
+    }
+    ALOGI("Password has been set");
+    return OK;
+}
+
 status_t VNCFlinger::start() {
     sp<ProcessState> self = ProcessState::self();
     self->startThreadPool();
 
-    mMainDpy = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
-
-    updateDisplayProjection();
-
-    status_t err = createVNCServer();
+    status_t err = startVNCServer();
     if (err != NO_ERROR) {
         ALOGE("Failed to start VNCFlinger: err=%d", err);
         return err;
@@ -61,6 +113,8 @@
 
     ALOGV("Shutting down");
 
+    rfbShutdownServer(mVNCScreen, true);
+
     destroyVirtualDisplayLocked();
     mClientCount = 0;
     mRunning = false;
@@ -154,7 +208,7 @@
     rfbErr = VNCFlinger::rfbLogger;
 
     // 32-bit color
-    mVNCScreen = rfbGetScreen(&mArgc, mArgv, mWidth, mHeight, 8, 3, 4);
+    mVNCScreen = rfbGetScreen(0, NULL, mWidth, mHeight, 8, 3, 4);
     if (mVNCScreen == NULL) {
         ALOGE("Unable to create VNCScreen");
         return NO_INIT;
@@ -168,7 +222,7 @@
     mVNCScreen->desktopName = "VNCFlinger";
     mVNCScreen->alwaysShared = TRUE;
     mVNCScreen->httpDir = NULL;
-    mVNCScreen->port = VNC_PORT;
+    mVNCScreen->authPasswdData = (void *)VNC_AUTH_FILE;
     mVNCScreen->newClientHook = (rfbNewClientHookPtr)VNCFlinger::onNewClient;
     mVNCScreen->kbdAddEvent = InputDevice::onKeyEvent;
     mVNCScreen->ptrAddEvent = InputDevice::onPointerEvent;
@@ -179,12 +233,17 @@
     mVNCScreen->handleEventsEagerly = true;
     mVNCScreen->deferUpdateTime = 1;
     mVNCScreen->screenData = this;
+
+    return err;
+}
+
+status_t VNCFlinger::startVNCServer() {
     rfbInitServer(mVNCScreen);
 
     /* Mark as dirty since we haven't sent any updates at all yet. */
     rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
 
-    return err;
+    return NO_ERROR;
 }
 
 size_t VNCFlinger::addClient() {
diff --git a/src/VNCFlinger.h b/src/VNCFlinger.h
index fe4a34b..7433f0c 100644
--- a/src/VNCFlinger.h
+++ b/src/VNCFlinger.h
@@ -20,18 +20,18 @@
 
 #include <gui/CpuConsumer.h>
 #include <ui/DisplayInfo.h>
+#include <utils/String8.h>
 
 #include <rfb/rfb.h>
 
-#define VNC_PORT 5901
+#define VNC_AUTH_FILE "/data/system/vncauth"
 #define NUM_BUFS 1
 
 namespace android {
 
-class VNCFlinger {
+class VNCFlinger : public RefBase {
   public:
-    VNCFlinger(int argc, char** argv) : mArgc(argc), mArgv(argv), mOrientation(-1) {
-    }
+    VNCFlinger();
 
     virtual ~VNCFlinger() {
     }
@@ -42,6 +42,12 @@
     virtual size_t addClient();
     virtual size_t removeClient();
 
+    virtual status_t setListenAddress(String8& address, bool v6);
+    virtual status_t setPort(unsigned int port);
+
+    virtual status_t clearPassword();
+    virtual status_t setPassword(String8& passwd);
+
   private:
     class FrameListener : public CpuConsumer::FrameAvailableListener {
       public:
@@ -60,7 +66,9 @@
 
     virtual status_t createVirtualDisplay();
     virtual status_t destroyVirtualDisplayLocked();
+
     virtual status_t createVNCServer();
+    virtual status_t startVNCServer();
 
     virtual void processFrame();
 
@@ -75,9 +83,6 @@
     static void onFrameDone(rfbClientPtr cl, int result);
     static void rfbLogger(const char* format, ...);
 
-    int mArgc;
-    char** mArgv;
-
     bool mRunning;
     bool mFrameAvailable;
     bool mRotate;
diff --git a/src/main.cpp b/src/main.cpp
index dc34921..8ef24fb 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -14,23 +14,118 @@
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
+#include <getopt.h>
 
 #include <csignal>
+#include <iostream>
 
 #include "VNCFlinger.h"
 
 using namespace android;
 
-static VNCFlinger* VNC;
+static sp<VNCFlinger> gVNC;
+
+static const char* const shortOpts = "4:6:p:s:vh";
+static const option longOpts[] = {
+    {"listen", 1, nullptr, '4'},
+    {"listen6", 1, nullptr, '6'},
+    {"port", 1, nullptr, 'p'},
+    {"password", 1, nullptr, 's'},
+    {"version", 0, nullptr, 'v'},
+    {"help", 0, nullptr, 'h'},
+};
 
 static void onSignal(int /* signal */) {
-    VNC->stop();
+    gVNC->stop();
+}
+
+static void printVersion() {
+    std::cout << "VNCFlinger-" << VNCFLINGER_VERSION << std::endl;
+    exit(0);
+}
+
+static void printHelp() {
+    std::cout << "Usage: vncflinger [OPTIONS]\n\n"
+              << "  -4 <addr>        IPv4 address to listen (default: localhost)\n"
+              << "  -6 <addr>        IPv6 address to listen (default: localhost)\n"
+              << "  -p <num>         Port to listen on (default: 5900)\n"
+              << "  -s <pass>        Store server password\n"
+              << "  -c               Clear server password\n"
+              << "  -v               Show server version\n"
+              << "  -h               Show help\n\n";
+    exit(1);
+}
+
+static void parseArgs(int argc, char** argv) {
+    String8 arg;
+
+    while (true) {
+        const auto opt = getopt_long(argc, argv, shortOpts, longOpts, nullptr);
+
+        if (opt < 0) {
+            break;
+        }
+
+        switch (opt) {
+            case 's':
+                arg = optarg;
+                if (gVNC->setPassword(arg) != OK) {
+                    std::cerr << "Failed to set password\n";
+                    exit(1);
+                }
+                exit(0);
+
+            case 'c':
+                if (gVNC->clearPassword() != OK) {
+                    std::cerr << "Failed to clear password\n";
+                    exit(1);
+                }
+                exit(0);
+
+            case '4':
+                arg = optarg;
+                if (gVNC->setListenAddress(arg, false) != OK) {
+                    std::cerr << "Failed to set IPv4 address\n";
+                    exit(1);
+                }
+                break;
+
+            case '6':
+                arg = optarg;
+                if (gVNC->setListenAddress(arg, true) != OK) {
+                    std::cerr << "Failed to set IPv6 address\n";
+                    exit(1);
+                }
+                break;
+
+            case 'p':
+                std::cerr << "port=" << optarg << std::endl;
+                if (gVNC->setPort(std::stoi(optarg)) != OK) {
+                    std::cerr << "Failed to set port\n";
+                    exit(1);
+                }
+                break;
+
+            case 'v':
+                printVersion();
+                break;
+
+            case 'h':
+            case '?':
+            default:
+                printHelp();
+                break;
+        }
+    }
 }
 
 int main(int argc, char** argv) {
     std::signal(SIGINT, onSignal);
     std::signal(SIGHUP, onSignal);
 
-    VNC = new VNCFlinger(argc, argv);
-    VNC->start();
+    gVNC = new VNCFlinger();
+
+    parseArgs(argc, argv);
+
+    gVNC->start();
 }