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