Hi,

The attached patches show a dialog to accept the X509 certificate if
it's not yet trusted.
For example, if you haven't specified a certificate and it's the first
time you connect to a X509 enabled TigerVNC server.
Consequently, this also work for self-signed certificates.

It adds 2 files from readline (slightly modified) to resolve ~ paths as
a convenience for the default certificate loading (i.e. you can specify
path like ~/.vnc/.. this works for the revocation list too)

It let you accept and save this certificate (or refuse and disconnect),
and will reuse it by default (stored ~/.vnc/x509.certs) at the next
connection.

Also added a dialog box to accept mismatching hostnames between the
certificate presented and the address you connect to, and a few missing
TLS checks.

I will also patch the Windows client when I can, as I require the
feature to be there.

Patches apply to current SVN.

-- 
Guillaume Destuynder - m-privacy GmbH -

Am Köllnischen Park 1, 10179 Berlin
Tel: +49 30 24342334
Fax: +49 30 24342336
Web: http://www.m-privacy.de, http://oss.m-privacy.de
Handelsregister:
 Amtsgericht Charlottenburg HRB 84946
Geschäftsführer:
 Dipl.-Kfm. Holger Maczkowsky,
 Roman Maczkowsky
GnuPG-Key-ID: 0x3FB1D217
Index: unix/vncviewer/CConn.h
===================================================================
--- unix/vncviewer/CConn.h	(revision 4175)
+++ unix/vncviewer/CConn.h	(working copy)
@@ -26,6 +26,7 @@
 #include <rfb/CConnection.h>
 #include <rfb/Exception.h>
 #include <rfb/UserPasswdGetter.h>
+#include <rfb/CertificateDialog.h>
 #include <rdr/FdInStream.h>
 #include <list>
 
@@ -44,7 +45,7 @@
               public TXDeleteWindowCallback,
               public rdr::FdInStreamBlockCallback,
               public TXMenuCallback , public OptionsDialogCallback,
-              public TXEventHandler
+              public TXEventHandler, public rfb::CertificateDialog
 {
 public:
 
@@ -61,6 +62,10 @@
   // UserPasswdGetter methods
   virtual void getUserPasswd(char** user, char** password);
 
+  // CertificateDialog methods
+  virtual int getCertificateReply(char* cert);
+  virtual int saveCertificate(char* data);
+
   // TXMenuCallback methods
   void menuSelect(long id, TXMenu* m);
 
Index: unix/vncviewer/CConn.cxx
===================================================================
--- unix/vncviewer/CConn.cxx	(revision 4175)
+++ unix/vncviewer/CConn.cxx	(working copy)
@@ -21,6 +21,9 @@
 //
 
 #include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include "CConn.h"
 #include <rfb/CMsgWriter.h>
 #include <rfb/encodings.h>
@@ -75,7 +78,8 @@
   menuKeysym = XStringToKeysym(menuKeyStr.buf);
 
   setShared(shared);
-  CSecurity::upg = this; /* Security instance is created in CConnection costructor. */
+  CSecurity::upg = this; /* Security instance is created in CConnection constructor. */
+  CSecurityTLS::ctd = this;
 
   CharArray encStr(preferredEncoding.getData());
   int encNum = encodingNum(encStr.buf);
@@ -232,7 +236,46 @@
   *password = strDup(dlg.passwdEntry.getText());
 }
 
+// getCertificateReply() is called by the CSecurity object when it needs to get
+// a reply from the user (display/accept certificate dialog).
 
+int CConn::getCertificateReply(char* cert)
+{
+  TXMsgBox msgBox(dpy, cert, MB_YESNO, "VNC Viewer: Confirm certificate information");
+  if (msgBox.show())
+    return 0;
+  return 1;
+}
+
+int CConn::saveCertificate(char *data)
+{
+  char *homeDir = getenv("HOME");
+  if (!homeDir) {
+    vlog.error("Could not find environement variable $HOME");
+    return 1;
+  }
+
+  CharArray vncDir(strlen(homeDir)+6);
+  sprintf(vncDir.buf, "%s/.vnc", homeDir);
+  CharArray certFile(strlen(vncDir.buf)+12);
+  sprintf(certFile.buf, "%s/x509.certs", vncDir.buf);
+
+  int result = mkdir(vncDir.buf, 0755);
+  if (result == -1 && errno != EEXIST) {
+    vlog.error("Could not create .vnc directory: %s.", strerror(errno));
+    return 1;
+  }
+  FILE *f = fopen(certFile.buf, "w+");
+
+  if (!f) {
+    vlog.error("Couldnt open/create .vnc/x509.certs file: %s.", strerror(errno));
+    return 1;
+  }
+  fprintf(f, "%s", data); 
+  fclose(f);
+  return 0;
+}
+
 // CConnection callback methods
 
 // serverInit() is called when the serverInit message has been received.  At
Index: unix/vncviewer/Makefile.am
===================================================================
--- unix/vncviewer/Makefile.am	(revision 4175)
+++ unix/vncviewer/Makefile.am	(working copy)
@@ -16,6 +16,7 @@
 	$(top_builddir)/common/rfb/librfb.la \
 	$(top_builddir)/common/network/libnetwork.la \
 	$(top_builddir)/common/rdr/librdr.la \
+	$(top_builddir)/common/os/libos.la \
 	@X_PRE_LIBS@ @X_LIBS@ -lXext -lX11 @X_EXTRA_LIBS@ @GNUTLS_LIBS@ # @LIBINTL@
 
 if INCLUDED_ZLIB
Index: common/os/Makefile.am
===================================================================
--- common/os/Makefile.am	(revision 4175)
+++ common/os/Makefile.am	(working copy)
@@ -1,8 +1,8 @@
 noinst_LTLIBRARIES = libos.la
 
-HDRS = net.h print.h
+HDRS = net.h print.h tilde.h
 
-libos_la_SOURCES = $(HDRS) print.c net.c
+libos_la_SOURCES = $(HDRS) print.c net.c tilde.c
 
 libos_la_CPPFLAGS = -I$(top_srcdir)/common
 
Index: common/rfb/SecurityClient.cxx
===================================================================
--- common/rfb/SecurityClient.cxx	(revision 4175)
+++ common/rfb/SecurityClient.cxx	(working copy)
@@ -37,6 +37,7 @@
 using namespace rfb;
 
 UserPasswdGetter *CSecurity::upg = NULL;
+CertificateDialog *CSecurityTLS::ctd = NULL;
 
 StringParameter SecurityClient::secTypes
 ("SecurityTypes",
@@ -51,6 +52,7 @@
 CSecurity* SecurityClient::GetCSecurity(U32 secType)
 {
   assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */
+  assert (CSecurityTLS::ctd != NULL);
 
   if (!IsSupported(secType))
     goto bail;
Index: common/rfb/CSecurityTLS.h
===================================================================
--- common/rfb/CSecurityTLS.h	(revision 4175)
+++ common/rfb/CSecurityTLS.h	(working copy)
@@ -35,6 +35,7 @@
 #include <rfb/Security.h>
 #include <rdr/InStream.h>
 #include <rdr/OutStream.h>
+#include <rfb/CertificateDialog.h>
 #include <gnutls/gnutls.h>
 
 namespace rfb {
@@ -49,6 +50,7 @@
 
     static StringParameter x509ca;
     static StringParameter x509crl;
+    static CertificateDialog *ctd;
 
   protected:
     void shutdown();
Index: common/rfb/Makefile.am
===================================================================
--- common/rfb/Makefile.am	(revision 4175)
+++ common/rfb/Makefile.am	(working copy)
@@ -27,7 +27,7 @@
 	TransImageGetter.h transInitTempl.h transTempl.h TrueColourMap.h \
 	UpdateTracker.h UserPasswdGetter.h util.h VNCSConnectionST.h \
 	VNCServer.h VNCServerST.h zrleDecode.h ZRLEDecoder.h zrleEncode.h \
-	ZRLEEncoder.h
+	ZRLEEncoder.h CertificateDialog.h
 
 librfb_la_SOURCES = $(HDRS) Blacklist.cxx CConnection.cxx CMsgHandler.cxx \
 	CMsgReader.cxx CMsgReaderV3.cxx CMsgWriter.cxx CMsgWriterV3.cxx \
Index: common/rfb/CSecurityTLS.cxx
===================================================================
--- common/rfb/CSecurityTLS.cxx	(revision 4175)
+++ common/rfb/CSecurityTLS.cxx	(working copy)
@@ -2,6 +2,7 @@
  * Copyright (C) 2004 Red Hat Inc.
  * Copyright (C) 2005 Martin Koegler
  * Copyright (C) 2010 TigerVNC Team
+ * Copyright (C) 2010 m-privacy GmbH
  *    
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,6 +28,9 @@
 #error "This header should not be compiled without HAVE_GNUTLS defined"
 #endif
 
+#include <stdlib.h>
+#include <unistd.h>
+
 #include <rfb/CSecurityTLS.h>
 #include <rfb/SSecurityVeNCrypt.h> 
 #include <rfb/CConnection.h>
@@ -34,6 +38,7 @@
 #include <rfb/Exception.h>
 #include <rdr/TLSInStream.h>
 #include <rdr/TLSOutStream.h>
+#include <os/tilde.h>
 
 #include <gnutls/x509.h>
 
@@ -41,7 +46,7 @@
 
 using namespace rfb;
 
-StringParameter CSecurityTLS::x509ca("x509ca", "X509 CA certificate", "", ConfViewer);
+StringParameter CSecurityTLS::x509ca("x509ca", "X509 CA certificate", "~/.vnc/x509.certs", ConfViewer);
 StringParameter CSecurityTLS::x509crl("x509crl", "X509 CRL file", "", ConfViewer);
 
 static LogWriter vlog("TLS");
@@ -72,8 +77,12 @@
 CSecurityTLS::CSecurityTLS(bool _anon) : session(0), anon_cred(0),
 						 anon(_anon), fis(0), fos(0)
 {
-  cafile = x509ca.getData();
-  crlfile = x509crl.getData();
+  cafile = tilde_expand(x509ca.getData());
+  if (access(cafile, F_OK))
+	  cafile = "";
+  crlfile = tilde_expand(x509crl.getData());
+  if (access(crlfile, F_OK))
+	  crlfile = "";
 }
 
 void CSecurityTLS::shutdown()
@@ -206,6 +215,7 @@
   const gnutls_datum *cert_list;
   unsigned int cert_list_size = 0;
   unsigned int i;
+  gnutls_datum_t info;
 
   if (anon)
     return;
@@ -226,12 +236,18 @@
     throw AuthFailureException("certificate verification failed");
   }
 
-  if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
-    throw AuthFailureException("certificate issuer unknown");
+  if (status & GNUTLS_CERT_REVOKED) {
+    throw AuthFailureException("certificate has been revoked");
+  }
 
-  if (status & GNUTLS_CERT_INVALID)
-    throw AuthFailureException("certificate not trusted");
+  if (status & GNUTLS_CERT_EXPIRED) {
+    throw AuthFailureException("certificate has expired");
+  }
 
+  if (status & GNUTLS_CERT_NOT_ACTIVATED) {
+    throw AuthFailureException("certificate has not been activated");
+  }
+
   for (i = 0; i < cert_list_size; i++) {
     gnutls_x509_crt crt;
     gnutls_x509_crt_init(&crt);
@@ -239,12 +255,61 @@
     if (gnutls_x509_crt_import(crt, &cert_list[i],GNUTLS_X509_FMT_DER) < 0)
       throw AuthFailureException("Decoding of certificate failed");
 
+    if (gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info)) {
+        gnutls_free(info.data);
+	throw AuthFailureException("Could not find certificate to display");
+    }
+
     if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) {
-#if 0
-      throw AuthFailureException("Hostname mismatch"); /* Non-fatal for now... */
-#endif
+      char buf[255];
+      sprintf(buf, "Hostname (%s) does not match any certificate, do you want to continue?", client->getServerName());
+      vlog.debug("hostname mistmatch");
+      if ((CSecurityTLS::ctd)->getCertificateReply(buf)) {
+	 exit(1);
+      }
     }
+
+    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+      size_t out_size;
+      char *out_buf;
+      char *certinfo;
+      int len = 0;
+
+      vlog.debug("certificate issuer unknown");
+      vlog.debug("%s", info.data);
+
+      len = snprintf(NULL, 0, "This certificate has been signed by an unknown authority:\n\n%s\n\nDo you want to save it and continue?\n ", info.data);
+      if (len < 0)
+        AuthFailureException("certificate decoding error");
+
+      certinfo = (char*) malloc(len);
+      snprintf(certinfo, len, "This certificate has been signed by an unknown authority:\n\n%s\n\nDo you want to save it and continue? ", info.data);
+
+      for (int i=0;i<len-1;i++)
+        if (certinfo[i] == ',' && certinfo[i+1] == ' ')
+		certinfo[i] = '\n';
+
+      if ((CSecurityTLS::ctd)->getCertificateReply(certinfo)) {
+        free(certinfo);
+	exit(1);
+      }
+      free(certinfo);
+
+      if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, NULL, &out_size) == GNUTLS_E_SHORT_MEMORY_BUFFER)
+        AuthFailureException("out of memory");
+
+      //Save cert
+      out_buf = (char *) malloc(out_size);
+      if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, out_buf, &out_size) < 0)
+        AuthFailureException("certificate issuer unknown, and certificate export failed");
+      
+      if ((CSecurityTLS::ctd)->saveCertificate(out_buf))
+        AuthFailureException("certificate save failed");
+      free(out_buf);
+    } else if (status & GNUTLS_CERT_INVALID)
+        throw AuthFailureException("certificate not trusted");
     gnutls_x509_crt_deinit(crt);
+    gnutls_free(info.data);
   }
 }
 
--- /dev/null	2010-10-27 11:30:12.262000002 +0200
+++ common/os/tilde.c	2010-10-27 13:22:36.305000056 +0200
@@ -0,0 +1,402 @@
+/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
+
+/* Simplifications Copyright (C) 2010 m-privacy GmbH
+   Copyright (C) 1988-2009 Free Software Foundation, Inc.
+
+   This file is part of the GNU Readline Library (Readline), a library
+   for reading lines of text with interactive input and history editing.
+
+   Readline 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.
+
+   Readline 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 Readline.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "tilde.h"
+
+#if !defined (HAVE_GETPW_DECLS)
+#  if defined (HAVE_GETPWUID)
+extern struct passwd *getpwuid PARAMS((uid_t));
+#  endif
+#  if defined (HAVE_GETPWNAM)
+extern struct passwd *getpwnam PARAMS((const char *));
+#  endif
+#endif /* !HAVE_GETPW_DECLS */
+
+#if !defined (savestring)
+#define savestring(x) strcpy ((char *)malloc (1 + strlen (x)), (x))
+#endif /* !savestring */
+
+#if !defined (NULL)
+#  if defined (__STDC__)
+#    define NULL ((void *) 0)
+#  else
+#    define NULL 0x0
+#  endif /* !__STDC__ */
+#endif /* !NULL */
+
+/* The default value of tilde_additional_prefixes.  This is set to
+   whitespace preceding a tilde so that simple programs which do not
+   perform any word separation get desired behaviour. */
+static const char *default_prefixes[] =
+  { " ~", "\t~", (const char *)NULL };
+
+/* The default value of tilde_additional_suffixes.  This is set to
+   whitespace or newline so that simple programs which do not
+   perform any word separation get desired behaviour. */
+static const char *default_suffixes[] =
+  { " ", "\n", (const char *)NULL };
+
+/* If non-null, this contains the address of a function that the application
+   wants called before trying the standard tilde expansions.  The function
+   is called with the text sans tilde, and returns a malloc()'ed string
+   which is the expansion, or a NULL pointer if the expansion fails. */
+tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
+
+/* If non-null, this contains the address of a function to call if the
+   standard meaning for expanding a tilde fails.  The function is called
+   with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
+   which is the expansion, or a NULL pointer if there is no expansion. */
+tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
+
+/* When non-null, this is a NULL terminated array of strings which
+   are duplicates for a tilde prefix.  Bash uses this to expand
+   `=~' and `:~'. */
+char **tilde_additional_prefixes = (char **)default_prefixes;
+
+/* When non-null, this is a NULL terminated array of strings which match
+   the end of a username, instead of just "/".  Bash sets this to
+   `:' and `=~'. */
+char **tilde_additional_suffixes = (char **)default_suffixes;
+
+static int tilde_find_prefix PARAMS((const char *, int *));
+static int tilde_find_suffix PARAMS((const char *));
+static char *isolate_tilde_prefix PARAMS((const char *, int *));
+static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
+
+/* Find the start of a tilde expansion in STRING, and return the index of
+   the tilde which starts the expansion.  Place the length of the text
+   which identified this tilde starter in LEN, excluding the tilde itself. */
+static int
+tilde_find_prefix (string, len)
+     const char *string;
+     int *len;
+{
+  register int i, j, string_len;
+  register char **prefixes;
+
+  prefixes = tilde_additional_prefixes;
+
+  string_len = strlen (string);
+  *len = 0;
+
+  if (*string == '\0' || *string == '~')
+    return (0);
+
+  if (prefixes)
+    {
+      for (i = 0; i < string_len; i++)
+	{
+	  for (j = 0; prefixes[j]; j++)
+	    {
+	      if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
+		{
+		  *len = strlen (prefixes[j]) - 1;
+		  return (i + *len);
+		}
+	    }
+	}
+    }
+  return (string_len);
+}
+
+/* Find the end of a tilde expansion in STRING, and return the index of
+   the character which ends the tilde definition.  */
+static int
+tilde_find_suffix (string)
+     const char *string;
+{
+  register int i, j, string_len;
+  register char **suffixes;
+
+  suffixes = tilde_additional_suffixes;
+  string_len = strlen (string);
+
+  for (i = 0; i < string_len; i++)
+    {
+#if defined (__MSDOS__)
+      if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
+#else
+      if (string[i] == '/' /* || !string[i] */)
+#endif
+	break;
+
+      for (j = 0; suffixes && suffixes[j]; j++)
+	{
+	  if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
+	    return (i);
+	}
+    }
+  return (i);
+}
+
+/* Return a new string which is the result of tilde expanding STRING. */
+char *
+tilde_expand (string)
+     const char *string;
+{
+  char *result;
+  int result_size, result_index;
+
+  result_index = result_size = 0;
+  if (result = strchr (string, '~'))
+    result = (char *)malloc (result_size = (strlen (string) + 16));
+  else
+    result = (char *)malloc (result_size = (strlen (string) + 1));
+
+  /* Scan through STRING expanding tildes as we come to them. */
+  while (1)
+    {
+      register int start, end;
+      char *tilde_word, *expansion;
+      int len;
+
+      /* Make START point to the tilde which starts the expansion. */
+      start = tilde_find_prefix (string, &len);
+
+      /* Copy the skipped text into the result. */
+      if ((result_index + start + 1) > result_size)
+	result = (char *)realloc (result, 1 + (result_size += (start + 20)));
+
+      strncpy (result + result_index, string, start);
+      result_index += start;
+
+      /* Advance STRING to the starting tilde. */
+      string += start;
+
+      /* Make END be the index of one after the last character of the
+	 username. */
+      end = tilde_find_suffix (string);
+
+      /* If both START and END are zero, we are all done. */
+      if (!start && !end)
+	break;
+
+      /* Expand the entire tilde word, and copy it into RESULT. */
+      tilde_word = (char *)malloc (1 + end);
+      strncpy (tilde_word, string, end);
+      tilde_word[end] = '\0';
+      string += end;
+
+      expansion = tilde_expand_word (tilde_word);
+      free (tilde_word);
+
+      len = strlen (expansion);
+#ifdef __CYGWIN__
+      /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
+	 $HOME for `user' is /.  On cygwin, // denotes a network drive. */
+      if (len > 1 || *expansion != '/' || *string != '/')
+#endif
+	{
+	  if ((result_index + len + 1) > result_size)
+	    result = (char *)realloc (result, 1 + (result_size += (len + 20)));
+
+	  strcpy (result + result_index, expansion);
+	  result_index += len;
+	}
+      free (expansion);
+    }
+
+  result[result_index] = '\0';
+
+  return (result);
+}
+
+/* Take FNAME and return the tilde prefix we want expanded.  If LENP is
+   non-null, the index of the end of the prefix into FNAME is returned in
+   the location it points to. */
+static char *
+isolate_tilde_prefix (fname, lenp)
+     const char *fname;
+     int *lenp;
+{
+  char *ret;
+  int i;
+
+  ret = (char *)malloc (strlen (fname));
+#if defined (__MSDOS__)
+  for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
+#else
+  for (i = 1; fname[i] && fname[i] != '/'; i++)
+#endif
+    ret[i - 1] = fname[i];
+  ret[i - 1] = '\0';
+  if (lenp)
+    *lenp = i;
+  return ret;
+}
+
+#if 0
+/* Public function to scan a string (FNAME) beginning with a tilde and find
+   the portion of the string that should be passed to the tilde expansion
+   function.  Right now, it just calls tilde_find_suffix and allocates new
+   memory, but it can be expanded to do different things later. */
+char *
+tilde_find_word (fname, flags, lenp)
+     const char *fname;
+     int flags, *lenp;
+{
+  int x;
+  char *r;
+
+  x = tilde_find_suffix (fname);
+  if (x == 0)
+    {
+      r = savestring (fname);
+      if (lenp)
+	*lenp = 0;
+    }
+  else
+    {
+      r = (char *)malloc (1 + x);
+      strncpy (r, fname, x);
+      r[x] = '\0';
+      if (lenp)
+	*lenp = x;
+    }
+
+  return r;
+}
+#endif
+
+/* Return a string that is PREFIX concatenated with SUFFIX starting at
+   SUFFIND. */
+static char *
+glue_prefix_and_suffix (prefix, suffix, suffind)
+     char *prefix;
+     const char *suffix;
+     int suffind;
+{
+  char *ret;
+  int plen, slen;
+
+  plen = (prefix && *prefix) ? strlen (prefix) : 0;
+  slen = strlen (suffix + suffind);
+  ret = (char *)malloc (plen + slen + 1);
+  if (plen)
+    strcpy (ret, prefix);
+  strcpy (ret + plen, suffix + suffind);
+  return ret;
+}
+
+/* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
+   tilde.  If there is no expansion, call tilde_expansion_failure_hook.
+   This always returns a newly-allocated string, never static storage. */
+char *
+tilde_expand_word (filename)
+     const char *filename;
+{
+  char *dirname, *expansion, *username;
+  int user_len;
+  struct passwd *user_entry;
+  char *home_dir;
+  struct passwd *entry;
+
+  if (filename == 0)
+    return ((char *)NULL);
+
+  if (*filename != '~')
+    return (savestring (filename));
+
+  /* A leading `~/' or a bare `~' is *always* translated to the value of
+     $HOME or the home directory of the current user, regardless of any
+     preexpansion hook. */
+  if (filename[1] == '\0' || filename[1] == '/')
+    {
+      /* Prefix $HOME to the rest of the string. */
+      expansion = getenv ("HOME");
+
+      /* If there is no HOME variable, look up the directory in
+	 the password database. */
+      if (expansion == 0) {
+	      home_dir = (char *)NULL;
+#if defined (HAVE_GETPWUID)
+	      entry = getpwuid (getuid ());
+	      if (entry)
+		      home_dir = entry->pw_dir;
+#endif
+	      expansion = home_dir;
+      }
+
+      return (glue_prefix_and_suffix (expansion, filename, 1));
+    }
+
+  username = isolate_tilde_prefix (filename, &user_len);
+
+  if (tilde_expansion_preexpansion_hook)
+    {
+      expansion = (*tilde_expansion_preexpansion_hook) (username);
+      if (expansion)
+	{
+	  dirname = glue_prefix_and_suffix (expansion, filename, user_len);
+	  free (username);
+	  free (expansion);
+	  return (dirname);
+	}
+    }
+
+  /* No preexpansion hook, or the preexpansion hook failed.  Look in the
+     password database. */
+  dirname = (char *)NULL;
+#if defined (HAVE_GETPWNAM)
+  user_entry = getpwnam (username);
+#else
+  user_entry = 0;
+#endif
+  if (user_entry == 0)
+    {
+      /* If the calling program has a special syntax for expanding tildes,
+	 and we couldn't find a standard expansion, then let them try. */
+      if (tilde_expansion_failure_hook)
+	{
+	  expansion = (*tilde_expansion_failure_hook) (username);
+	  if (expansion)
+	    {
+	      dirname = glue_prefix_and_suffix (expansion, filename, user_len);
+	      free (expansion);
+	    }
+	}
+      /* If we don't have a failure hook, or if the failure hook did not
+	 expand the tilde, return a copy of what we were passed. */
+      if (dirname == 0)
+	dirname = savestring (filename);
+    }
+#if defined (HAVE_GETPWENT)
+  else
+    dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
+#endif
+
+  free (username);
+#if defined (HAVE_GETPWENT)
+  endpwent ();
+#endif
+  return (dirname);
+}
--- /dev/null	2010-10-27 11:30:12.262000002 +0200
+++ common/os/tilde.h	2010-10-27 13:14:58.015000023 +0200
@@ -0,0 +1,80 @@
+/* tilde.h: Externally available variables and function in libtilde.a. */
+
+/* Copyright (C) 1992-2009 Free Software Foundation, Inc.
+
+   This file contains the Readline Library (Readline), a set of
+   routines for providing Emacs style line input to programs that ask
+   for it.
+
+   Readline 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.
+
+   Readline 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 Readline.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if !defined (_TILDE_H_)
+#  define _TILDE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A function can be defined using prototypes and compile on both ANSI C
+   and traditional C compilers with something like this:
+	extern char *func PARAMS((char *, char *, int)); */
+
+#if !defined (PARAMS)
+#  if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus)
+#    define PARAMS(protos) protos
+#  else
+#    define PARAMS(protos) ()
+#  endif
+#endif
+
+typedef char *tilde_hook_func_t PARAMS((char *));
+
+/* If non-null, this contains the address of a function that the application
+   wants called before trying the standard tilde expansions.  The function
+   is called with the text sans tilde, and returns a malloc()'ed string
+   which is the expansion, or a NULL pointer if the expansion fails. */
+extern tilde_hook_func_t *tilde_expansion_preexpansion_hook;
+
+/* If non-null, this contains the address of a function to call if the
+   standard meaning for expanding a tilde fails.  The function is called
+   with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
+   which is the expansion, or a NULL pointer if there is no expansion. */
+extern tilde_hook_func_t *tilde_expansion_failure_hook;
+
+/* When non-null, this is a NULL terminated array of strings which
+   are duplicates for a tilde prefix.  Bash uses this to expand
+   `=~' and `:~'. */
+extern char **tilde_additional_prefixes;
+
+/* When non-null, this is a NULL terminated array of strings which match
+   the end of a username, instead of just "/".  Bash sets this to
+   `:' and `=~'. */
+extern char **tilde_additional_suffixes;
+
+/* Return a new string which is the result of tilde expanding STRING. */
+extern char *tilde_expand PARAMS((const char *));
+
+/* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
+   tilde.  If there is no expansion, call tilde_expansion_failure_hook. */
+extern char *tilde_expand_word PARAMS((const char *));
+
+/* Find the portion of the string beginning with ~ that should be expanded. */
+extern char *tilde_find_word PARAMS((const char *, int, int *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TILDE_H_ */
------------------------------------------------------------------------------
Nokia and AT&T present the 2010 Calling All Innovators-North America contest
Create new apps & games for the Nokia N8 for consumers in  U.S. and Canada
$10 million total in prizes - $4M cash, 500 devices, nearly $6M in marketing
Develop with Nokia Qt SDK, Web Runtime, or Java and Publish to Ovi Store 
http://p.sf.net/sfu/nokia-dev2dev
_______________________________________________
Tigervnc-devel mailing list
Tigervnc-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tigervnc-devel

Reply via email to