Page MenuHome GnuPG

No OneTemporary

This document is not UTF8. It was detected as Shift JIS and converted to UTF8 for display.
diff --git a/assuan/assuan-handler.c b/assuan/assuan-handler.c
index 947ab9d..aadad21 100644
--- a/assuan/assuan-handler.c
+++ b/assuan/assuan-handler.c
@@ -1,662 +1,672 @@
-/* assuan-handler.c - dispatch commands
+/* assuan-handler.c - dispatch commands
* Copyright (C) 2001 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG 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 2 of the License, or
* (at your option) any later version.
*
* GnuPG 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "assuan-defs.h"
#define spacep(p) (*(p) == ' ' || *(p) == '\t')
#define digitp(a) ((a) >= '0' && (a) <= '9')
static int
dummy_handler (ASSUAN_CONTEXT ctx, char *line)
{
+ (void)line;
return set_error (ctx, Server_Fault, "no handler registered");
}
static int
std_handler_nop (ASSUAN_CONTEXT ctx, char *line)
{
+ (void)ctx;
+ (void)line;
return 0; /* okay */
}
-
+
static int
std_handler_cancel (ASSUAN_CONTEXT ctx, char *line)
{
+ (void)line;
if (ctx->cancel_notify_fnc)
ctx->cancel_notify_fnc (ctx);
- return set_error (ctx, Not_Implemented, NULL);
+ return set_error (ctx, Not_Implemented, NULL);
}
static int
std_handler_option (ASSUAN_CONTEXT ctx, char *line)
{
char *key, *value, *p;
for (key=line; spacep (key); key++)
;
if (!*key)
return set_error (ctx, Syntax_Error, "argument required");
if (*key == '=')
return set_error (ctx, Syntax_Error, "no option name given");
for (value=key; *value && !spacep (value) && *value != '='; value++)
;
if (*value)
{
if (spacep (value))
*value++ = 0; /* terminate key */
for (; spacep (value); value++)
;
if (*value == '=')
{
*value++ = 0; /* terminate key */
for (; spacep (value); value++)
;
if (!*value)
return set_error (ctx, Syntax_Error, "option argument expected");
}
if (*value)
{
for (p = value + strlen(value) - 1; p > value && spacep (p); p--)
;
if (p > value)
*++p = 0; /* strip trailing spaces */
}
}
if (*key == '-' && key[1] == '-' && key[2])
key += 2; /* the double dashes are optional */
if (*key == '-')
return set_error (ctx, Syntax_Error,
"option should not begin with one dash");
if (ctx->option_handler_fnc)
return ctx->option_handler_fnc (ctx, key, value);
return 0;
}
-
+
static int
std_handler_bye (ASSUAN_CONTEXT ctx, char *line)
{
+ (void)line;
if (ctx->bye_notify_fnc)
ctx->bye_notify_fnc (ctx);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return -1; /* pretty simple :-) */
}
-
+
static int
std_handler_auth (ASSUAN_CONTEXT ctx, char *line)
{
- return set_error (ctx, Not_Implemented, NULL);
+ (void)line;
+ return set_error (ctx, Not_Implemented, NULL);
}
-
+
static int
std_handler_reset (ASSUAN_CONTEXT ctx, char *line)
{
+ (void)line;
if (ctx->reset_notify_fnc)
ctx->reset_notify_fnc (ctx);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return 0;
}
-
+
static int
std_handler_end (ASSUAN_CONTEXT ctx, char *line)
{
- return set_error (ctx, Not_Implemented, NULL);
+ (void)line;
+ return set_error (ctx, Not_Implemented, NULL);
}
static int
parse_cmd_input_output (ASSUAN_CONTEXT ctx, char *line, int *rfd)
{
char *endp;
if (strncmp (line, "FD=", 3))
return set_error (ctx, Syntax_Error, "FD=<n> expected");
line += 3;
if (!digitp (*line))
return set_error (ctx, Syntax_Error, "number required");
*rfd = strtoul (line, &endp, 10);
/* remove that argument so that a notify handler won't see it */
memset (line, ' ', endp? (endp-line):strlen(line));
if (*rfd == ctx->inbound.fd)
return set_error (ctx, Parameter_Conflict, "fd same as inbound fd");
if (*rfd == ctx->outbound.fd)
return set_error (ctx, Parameter_Conflict, "fd same as outbound fd");
return 0;
}
/* Format is INPUT FD=<n> */
static int
std_handler_input (ASSUAN_CONTEXT ctx, char *line)
{
int rc, fd;
rc = parse_cmd_input_output (ctx, line, &fd);
if (rc)
return rc;
ctx->input_fd = fd;
if (ctx->input_notify_fnc)
ctx->input_notify_fnc (ctx, line);
return 0;
}
/* Format is OUTPUT FD=<n> */
static int
std_handler_output (ASSUAN_CONTEXT ctx, char *line)
{
int rc, fd;
rc = parse_cmd_input_output (ctx, line, &fd);
if (rc)
return rc;
ctx->output_fd = fd;
if (ctx->output_notify_fnc)
ctx->output_notify_fnc (ctx, line);
return 0;
}
-
+
/* This is a table with the standard commands and handler for them.
The table is used to initialize a new context and assuciate strings
and handlers with cmd_ids */
static struct {
const char *name;
int cmd_id;
int (*handler)(ASSUAN_CONTEXT, char *line);
int always; /* always initialize this command */
} std_cmd_table[] = {
{ "NOP", ASSUAN_CMD_NOP, std_handler_nop, 1 },
{ "CANCEL", ASSUAN_CMD_CANCEL, std_handler_cancel, 1 },
{ "OPTION", ASSUAN_CMD_OPTION, std_handler_option, 1 },
{ "BYE", ASSUAN_CMD_BYE, std_handler_bye, 1 },
{ "AUTH", ASSUAN_CMD_AUTH, std_handler_auth, 1 },
{ "RESET", ASSUAN_CMD_RESET, std_handler_reset, 1 },
{ "END", ASSUAN_CMD_END, std_handler_end, 1 },
{ "INPUT", ASSUAN_CMD_INPUT, std_handler_input },
{ "OUTPUT", ASSUAN_CMD_OUTPUT, std_handler_output },
{ "OPTION", ASSUAN_CMD_OPTION, std_handler_option, 1 },
{ NULL }
};
/**
* assuan_register_command:
* @ctx: the server context
* @cmd_id: An ID value for the command
* @cmd_name: A string with the command name
* @handler: The handler function to be called
- *
+ *
* Register a handler to be used for a given command.
- *
+ *
* The @cmd_name must be %NULL or an empty string for all @cmd_ids
* below %ASSUAN_CMD_USER because predefined values are used.
- *
- * Return value:
+ *
+ * Return value:
**/
int
assuan_register_command (ASSUAN_CONTEXT ctx,
int cmd_id, const char *cmd_name,
int (*handler)(ASSUAN_CONTEXT, char *))
{
int i;
if (cmd_name && !*cmd_name)
cmd_name = NULL;
if (cmd_id < ASSUAN_CMD_USER)
- {
+ {
if (cmd_name)
return ASSUAN_Invalid_Value; /* must be NULL for these values*/
for (i=0; std_cmd_table[i].name; i++)
{
if (std_cmd_table[i].cmd_id == cmd_id)
{
cmd_name = std_cmd_table[i].name;
if (!handler)
handler = std_cmd_table[i].handler;
break;
}
}
if (!std_cmd_table[i].name)
return ASSUAN_Invalid_Value; /* not a pre-registered one */
}
-
+
if (!handler)
handler = dummy_handler;
if (!cmd_name)
return ASSUAN_Invalid_Value;
/* fprintf (stderr, "DBG-assuan: registering %d as `%s'\n", cmd_id, cmd_name); */
if (!ctx->cmdtbl)
{
ctx->cmdtbl_size = 50;
ctx->cmdtbl = xtrycalloc ( ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
if (!ctx->cmdtbl)
return ASSUAN_Out_Of_Core;
ctx->cmdtbl_used = 0;
}
else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
{
struct cmdtbl_s *x;
x = xtryrealloc ( ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
if (!x)
return ASSUAN_Out_Of_Core;
ctx->cmdtbl = x;
ctx->cmdtbl_size += 50;
}
ctx->cmdtbl[ctx->cmdtbl_used].name = cmd_name;
ctx->cmdtbl[ctx->cmdtbl_used].cmd_id = cmd_id;
ctx->cmdtbl[ctx->cmdtbl_used].handler = handler;
ctx->cmdtbl_used++;
return 0;
}
int
assuan_register_bye_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
{
if (!ctx)
return ASSUAN_Invalid_Value;
ctx->bye_notify_fnc = fnc;
return 0;
}
int
assuan_register_reset_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
{
if (!ctx)
return ASSUAN_Invalid_Value;
ctx->reset_notify_fnc = fnc;
return 0;
}
int
assuan_register_cancel_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
{
if (!ctx)
return ASSUAN_Invalid_Value;
ctx->cancel_notify_fnc = fnc;
return 0;
}
int
assuan_register_option_handler (ASSUAN_CONTEXT ctx,
int (*fnc)(ASSUAN_CONTEXT,
const char*, const char*))
{
if (!ctx)
return ASSUAN_Invalid_Value;
ctx->option_handler_fnc = fnc;
return 0;
}
int
assuan_register_input_notify (ASSUAN_CONTEXT ctx,
void (*fnc)(ASSUAN_CONTEXT, const char *))
{
if (!ctx)
return ASSUAN_Invalid_Value;
ctx->input_notify_fnc = fnc;
return 0;
}
int
assuan_register_output_notify (ASSUAN_CONTEXT ctx,
void (*fnc)(ASSUAN_CONTEXT, const char *))
{
if (!ctx)
return ASSUAN_Invalid_Value;
ctx->output_notify_fnc = fnc;
return 0;
}
/* Helper to register the standards commands */
int
_assuan_register_std_commands (ASSUAN_CONTEXT ctx)
{
int i, rc;
for (i=0; std_cmd_table[i].name; i++)
{
if (std_cmd_table[i].always)
{
rc = assuan_register_command (ctx, std_cmd_table[i].cmd_id,
NULL, NULL);
if (rc)
return rc;
}
- }
+ }
return 0;
}
/* Process the special data lines. The "D " has already been removed
from the line. As all handlers this function may modify the line. */
static int
handle_data_line (ASSUAN_CONTEXT ctx, char *line, int linelen)
{
+ (void)line;
+ (void)linelen;
return set_error (ctx, Not_Implemented, NULL);
}
/* like ascii_strcasecmp but assume that B is already uppercase */
static int
my_strcasecmp (const char *a, const char *b)
{
if (a == b)
return 0;
for (; *a && *b; a++, b++)
{
if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b)
break;
}
return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b);
}
/* Parse the line, break out the command, find it in the command
table, remove leading and white spaces from the arguments, all the
handler with the argument line and return the error */
-static int
+static int
dispatch_command (ASSUAN_CONTEXT ctx, char *line, int linelen)
{
char *p;
const char *s;
int shift, i;
if (*line == 'D' && line[1] == ' ') /* divert to special handler */
return handle_data_line (ctx, line+2, linelen-2);
for (p=line; *p && *p != ' ' && *p != '\t'; p++)
;
if (p==line)
- return set_error (ctx, Syntax_Error, "leading white-space");
- if (*p)
+ return set_error (ctx, Syntax_Error, "leading white-space");
+ if (*p)
{ /* Skip over leading WS after the keyword */
*p++ = 0;
while ( *p == ' ' || *p == '\t')
p++;
}
shift = p - line;
for (i=0; (s=ctx->cmdtbl[i].name); i++)
{
if (!strcmp (line, s))
break;
}
if (!s)
{ /* and try case insensitive */
for (i=0; (s=ctx->cmdtbl[i].name); i++)
{
if (!my_strcasecmp (line, s))
break;
}
}
if (!s)
return set_error (ctx, Unknown_Command, NULL);
line += shift;
linelen -= shift;
/* fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */
return ctx->cmdtbl[i].handler (ctx, line);
}
static int
process_request (ASSUAN_CONTEXT ctx)
{
int rc;
if (ctx->in_inquire)
return ASSUAN_Nested_Commands;
rc = _assuan_read_line (ctx);
if (rc)
return rc;
if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
return 0; /* comment line - ignore */
ctx->outbound.data.error = 0;
ctx->outbound.data.linelen = 0;
/* dispatch command and return reply */
rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
/* check from data write errors */
if (ctx->outbound.data.fp)
{ /* Flush the data lines */
fclose (ctx->outbound.data.fp);
ctx->outbound.data.fp = NULL;
if (!rc && ctx->outbound.data.error)
rc = ctx->outbound.data.error;
}
else /* flush any data send w/o using the data fp */
{
assuan_send_data (ctx, NULL, 0);
if (!rc && ctx->outbound.data.error)
rc = ctx->outbound.data.error;
}
/* Error handling */
if (!rc)
{
rc = assuan_write_line (ctx, ctx->okay_line? ctx->okay_line : "OK");
}
else if (rc == -1)
- { /* No error checking because the peer may have already disconnect */
+ { /* No error checking because the peer may have already disconnect */
assuan_write_line (ctx, "OK closing connection");
ctx->finish_handler (ctx);
}
- else
+ else
{
char errline[256];
if (rc < 100)
sprintf (errline, "ERR %d server fault (%.50s)",
ASSUAN_Server_Fault, assuan_strerror (rc));
else
{
const char *text = ctx->err_no == rc? ctx->err_str:NULL;
sprintf (errline, "ERR %d %.50s%s%.100s",
rc, assuan_strerror (rc), text? " - ":"", text?text:"");
}
rc = assuan_write_line (ctx, errline);
}
ctx->confidential = 0;
if (ctx->okay_line)
{
xfree (ctx->okay_line);
ctx->okay_line = NULL;
}
return rc;
}
/**
* assuan_process:
* @ctx: assuan context
- *
+ *
* This fucntion is used to handle the assuan protocol after a
* connection has been established using assuan_accept(). This is the
* main protocol handler.
- *
+ *
* Return value: 0 on success or an error code if the assuan operation
* failed. Note, that no error is returned for operational errors.
**/
int
assuan_process (ASSUAN_CONTEXT ctx)
{
int rc;
do {
rc = process_request (ctx);
} while (!rc);
if (rc == -1)
rc = 0;
return rc;
}
/**
* assuan_process_next:
* @ctx: Assuan context
- *
+ *
* Same as assuan_process() but the user has to provide the outer
* loop. He should loop as long as the return code is zero and stop
* otherwise; -1 is regular end.
- *
+ *
* See also: assuan_get_active_fds()
* Return value: -1 for end of server, 0 on success or an error code
**/
-int
+int
assuan_process_next (ASSUAN_CONTEXT ctx)
{
return process_request (ctx);
}
/**
* assuan_get_active_fds:
* @ctx: Assuan context
* @what: 0 for read fds, 1 for write fds
* @fdarray: Caller supplied array to store the FDs
* @fdarraysize: size of that array
- *
+ *
* Return all active filedescriptors for the given context. This
* function can be used to select on the fds and call
* assuan_process_next() if there is an active one. The first fd in
* the array is the one used for the command connection.
*
* Note, that write FDs are not yet supported.
- *
+ *
* Return value: number of FDs active and put into @fdarray or -1 on
* error which is most likely a too small fdarray.
**/
-int
+int
assuan_get_active_fds (ASSUAN_CONTEXT ctx, int what,
int *fdarray, int fdarraysize)
{
int n = 0;
if (!ctx || fdarraysize < 2 || what < 0 || what > 1)
return -1;
if (!what)
{
if (ctx->inbound.fd != -1)
fdarray[n++] = ctx->inbound.fd;
}
else
{
if (ctx->outbound.fd != -1)
fdarray[n++] = ctx->outbound.fd;
if (ctx->outbound.data.fp)
fdarray[n++] = fileno (ctx->outbound.data.fp);
}
return n;
}
/* Set the text used for the next OK reponse. This string is
automatically reset to NULL after the next command. */
AssuanError
assuan_set_okay_line (ASSUAN_CONTEXT ctx, const char *line)
{
if (!ctx)
return ASSUAN_Invalid_Value;
if (!line)
{
xfree (ctx->okay_line);
ctx->okay_line = NULL;
}
else
{
/* FIXME: we need to use gcry_is_secure() to test whether
we should allocate the entire line in secure memory */
char *buf = xtrymalloc (3+strlen(line)+1);
if (!buf)
return ASSUAN_Out_Of_Core;
strcpy (buf, "OK ");
strcpy (buf+3, line);
xfree (ctx->okay_line);
ctx->okay_line = buf;
}
return 0;
}
void
assuan_write_status (ASSUAN_CONTEXT ctx, const char *keyword, const char *text)
{
char buffer[256];
char *helpbuf;
size_t n;
if ( !ctx || !keyword)
return;
if (!text)
text = "";
n = 2 + strlen (keyword) + 1 + strlen (text) + 1;
if (n < sizeof (buffer))
{
strcpy (buffer, "S ");
strcat (buffer, keyword);
if (*text)
{
strcat (buffer, " ");
strcat (buffer, text);
}
assuan_write_line (ctx, buffer);
}
else if ( (helpbuf = xtrymalloc (n)) )
{
strcpy (helpbuf, "S ");
strcat (helpbuf, keyword);
if (*text)
{
strcat (helpbuf, " ");
strcat (helpbuf, text);
}
assuan_write_line (ctx, helpbuf);
xfree (helpbuf);
}
}
diff --git a/assuan/assuan-pipe-server.c b/assuan/assuan-pipe-server.c
index 07373e1..c0d464f 100644
--- a/assuan/assuan-pipe-server.c
+++ b/assuan/assuan-pipe-server.c
@@ -1,124 +1,127 @@
-/* assuan-pipe-server.c - Assuan server working over a pipe
+/* assuan-pipe-server.c - Assuan server working over a pipe
* Copyright (C) 2001 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG 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 2 of the License, or
* (at your option) any later version.
*
* GnuPG 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include "assuan-defs.h"
static void
deinit_pipe_server (ASSUAN_CONTEXT ctx)
{
+ (void)ctx;
/* nothing to do for this simple server */
}
static int
accept_connection (ASSUAN_CONTEXT ctx)
{
+ (void)ctx;
/* This is a NOP for a pipe server */
return 0;
}
static int
finish_connection (ASSUAN_CONTEXT ctx)
{
+ (void)ctx;
/* This is a NOP for a pipe server */
return 0;
}
/* Create a new context. Note that the handlers are set up for a pipe
server/client - this way we don't need extra dummy functions */
int
_assuan_new_context (ASSUAN_CONTEXT *r_ctx)
{
ASSUAN_CONTEXT ctx;
int rc;
*r_ctx = NULL;
ctx = xtrycalloc (1, sizeof *ctx);
if (!ctx)
return ASSUAN_Out_Of_Core;
ctx->input_fd = -1;
ctx->output_fd = -1;
ctx->inbound.fd = -1;
ctx->outbound.fd = -1;
ctx->listen_fd = -1;
ctx->client_pid = (pid_t)-1;
/* use the pipe server handler as a default */
ctx->deinit_handler = deinit_pipe_server;
ctx->accept_handler = accept_connection;
ctx->finish_handler = finish_connection;
rc = _assuan_register_std_commands (ctx);
if (rc)
xfree (ctx);
else
*r_ctx = ctx;
return rc;
}
int
assuan_init_pipe_server (ASSUAN_CONTEXT *r_ctx, int filedes[2])
{
int rc;
rc = _assuan_new_context (r_ctx);
if (!rc)
{
ASSUAN_CONTEXT ctx = *r_ctx;
ctx->is_server = 1;
ctx->inbound.fd = filedes[0];
ctx->outbound.fd = filedes[1];
ctx->pipe_mode = 1;
}
return rc;
}
void
_assuan_release_context (ASSUAN_CONTEXT ctx)
{
if (ctx)
{
xfree (ctx->hello_line);
xfree (ctx->okay_line);
xfree (ctx);
}
}
void
assuan_deinit_server (ASSUAN_CONTEXT ctx)
{
if (ctx)
{
/* We use this function pointer to avoid linking other server
when not needed but still allow for a generic deinit function */
ctx->deinit_handler (ctx);
ctx->deinit_handler = NULL;
_assuan_release_context (ctx);
}
}
diff --git a/configure.ac b/configure.ac
index 88bc00b..c453e45 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,600 +1,600 @@
# configure.ac
# Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at>
# Copyright (C) 2001, 2002, 2003, 2004, 2007, 2015 g10 Code GmbH
#
# This file is part of PINENTRY.
#
# PINENTRY 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 2 of the License, or
# (at your option) any later version.
#
# PINENTRY 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
# (Process this file with autoconf to produce a configure script.)
AC_PREREQ(2.69)
min_automake_version="1.14"
# To build a release you need to create a tag with the version number
# (git tag -s pinentry-n.m.k) and run "./autogen.sh --force". Please
# bump the version number immediately after the release, do another
# commit, and a push so that the git magic is able to work.
m4_define(mym4_version, [0.9.4])
# Below is m4 magic to extract and compute the git revision number,
# the decimalized short revision number, a beta version string and a
# flag indicating a development version (mym4_isgit). Note that the
# m4 processing is done by autoconf and not during the configure run.
m4_define([mym4_revision], m4_esyscmd([git branch -v 2>/dev/null \
| awk '/^\* / {printf "%s",$3}']))
m4_define([mym4_revision_dec],
m4_esyscmd_s([echo $((0x$(echo ]mym4_revision[|head -c 4)))]))
m4_define([mym4_betastring],
m4_esyscmd_s([git describe --match 'pinentry-[0-9].[0-9].*[0-9]' \
--long| awk -F- '$3!=0{print"-beta"$3}']))
m4_define([mym4_isgit],m4_if(mym4_betastring,[],[no],[yes]))
m4_define([mym4_full_version],[mym4_version[]mym4_betastring])
AC_INIT([pinentry],[mym4_full_version], [http://bugs.gnupg.org])
AC_CONFIG_AUX_DIR([build-aux])
AM_CONFIG_HEADER(config.h)
AC_CONFIG_SRCDIR(pinentry/pinentry.h)
AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip])
AC_GNU_SOURCE
AM_MAINTAINER_MODE
AC_CANONICAL_HOST
AH_TOP([
#ifndef PINENTRY_CONFIG_H_INCLUDED
#define PINENTRY_CONFIG_H_INCLUDED
/* Enable gpg-error's strerror macro under W32CE. */
#define GPG_ERR_ENABLE_ERRNO_MACROS 1
])
AH_BOTTOM([
#endif /*PINENTRY_CONFIG_H_INCLUDED*/
])
dnl Checks for programs.
AC_PROG_MAKE_SET
AM_SANITY_CHECK
missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AC_PROG_CC
AC_PROG_CPP
AC_PROG_INSTALL
AC_PROG_RANLIB
# We need to check for cplusplus here because we may not do the test
# for Qt and autoconf does does not allow that.
AC_PROG_CXX
AC_PROG_LN_S
AC_CHECK_TOOL(WINDRES, windres, :)
AC_CHECK_PROGS(GITLOG_TO_CHANGELOG, gitlog-to-changelog,
[build-aux/gitlog-to-changelog])
have_dosish_system=no
have_w32_system=no
have_w32ce_system=no
case "${host}" in
*-mingw32*)
AC_DEFINE(USE_ONLY_8DOT3,1,
[Set this to limit filenames to the 8.3 format])
have_dosish_system=yes
have_w32_system=yes
case "${host}" in
*-mingw32ce*)
have_w32ce_system=yes
;;
*)
AC_DEFINE(HAVE_DRIVE_LETTERS,1,
[Defined if the OS supports drive letters.])
;;
esac
;;
i?86-emx-os2 | i?86-*-os2*emx )
# OS/2 with the EMX environment
AC_DEFINE(HAVE_DRIVE_LETTERS)
have_dosish_system=yes
;;
i?86-*-msdosdjgpp*)
# DOS with the DJGPP environment
AC_DEFINE(HAVE_DRIVE_LETTERS)
have_dosish_system=yes
;;
esac
if test "$have_dosish_system" = yes; then
AC_DEFINE(HAVE_DOSISH_SYSTEM,1,
[Defined if we run on some of the PCDOS like systems
(DOS, Windoze. OS/2) with special properties like
no file modes])
fi
AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes)
if test "$have_w32_system" = yes; then
AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system])
if test "$have_w32ce_system" = yes; then
AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE])
fi
fi
AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes)
AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes)
dnl Checks for compiler features.
if test "$GCC" = yes; then
# Check whether gcc does not emit a diagnositc for unknown -Wno-*
# options. This is the case for gcc >= 4.6
AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 )
#kickerror
#endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no])
AC_MSG_RESULT($_gcc_silent_wno)
if test "$USE_MAINTAINER_MODE" = "yes"; then
CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes"
CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security"
if test x"$_gcc_silent_wno" = xyes ; then
_gcc_warn=yes
else
AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wno-missing-field-initializers"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
[_gcc_warn=yes],[_gcc_warn=no])
AC_MSG_RESULT($_gcc_warn)
CFLAGS=$_gcc_cflags_save;
fi
if test x"$_gcc_warn" = xyes ; then
- CFLAGS="$CFLAGS -W -Wno-missing-field-initializers"
+ CFLAGS="$CFLAGS -W -Wno-sign-compare -Wno-missing-field-initializers"
fi
AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wdeclaration-after-statement"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_warn=yes,_gcc_warn=no)
AC_MSG_RESULT($_gcc_warn)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_warn" = xyes ; then
CFLAGS="$CFLAGS -Wdeclaration-after-statement"
fi
else
# Not in maintainer mode: Use standard warnings.
CFLAGS="$CFLAGS -Wall"
fi
CPPFLAGS="$CPPFLAGS -Wall"
if test x"$_gcc_silent_wno" = xyes ; then
_gcc_warn=yes
else
AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wno-pointer-sign"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],[_gcc_warn=yes],[_gcc_warn=no])
AC_MSG_RESULT($_gcc_warn)
CFLAGS=$_gcc_cflags_save;
fi
if test x"$_gcc_warn" = xyes ; then
CFLAGS="$CFLAGS -Wno-pointer-sign"
fi
AC_MSG_CHECKING([if gcc supports -Wpointer-arith])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wpointer-arith"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_warn=yes,_gcc_warn=no)
AC_MSG_RESULT($_gcc_warn)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_warn" = xyes ; then
CFLAGS="$CFLAGS -Wpointer-arith"
fi
fi
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(string.h unistd.h langinfo.h termio.h locale.h utime.h wchar.h)
dnl Checks for library functions.
AC_CHECK_FUNCS(seteuid stpcpy mmap)
GNUPG_CHECK_MLOCK
# Common libraries and cflags.
COMMON_CFLAGS=
COMMON_LIBS=
AC_SUBST(COMMON_CFLAGS)
AC_SUBST(COMMON_LIBS)
dnl Checks for libassuan.
dnl -> None required because we use a stripped down version of libassuan.
dnl Checks for libsecmem.
GNUPG_CHECK_TYPEDEF(byte, HAVE_BYTE_TYPEDEF)
GNUPG_CHECK_TYPEDEF(ulong, HAVE_ULONG_TYPEDEF)
dnl Check for libcap
AC_ARG_WITH([libcap], AC_HELP_STRING([--without-libcap],
[Disable support for capabilities library]))
if test "x$with_libcap" != "xno"; then
AC_PATH_PROG(SETCAP, setcap, :, "$PATH:/sbin:/usr/sbin")
AC_CHECK_LIB(cap, cap_set_proc, [
AC_DEFINE(USE_CAPABILITIES,1,[The capabilities support library is installed])
LIBCAP=-lcap
])
fi
AC_SUBST(LIBCAP)
dnl
dnl Check for curses pinentry program.
dnl
AC_ARG_ENABLE(pinentry-curses,
AC_HELP_STRING([--enable-pinentry-curses], [build curses pinentry]),
pinentry_curses=$enableval, pinentry_curses=maybe)
AC_ARG_ENABLE(fallback-curses,
AC_HELP_STRING([--enable-fallback-curses], [include curses fallback]),
fallback_curses=$enableval, fallback_curses=maybe)
dnl
dnl Checks for curses libraries. Deal correctly with $pinentry_curses = maybe
dnl and $fallback_curses = maybe.
dnl
if test "$pinentry_curses" != "no" -o "$fallback_curses" != "no"; then
IU_LIB_CURSES
fi
if test "$LIBCURSES"; then
if test "$pinentry_curses" != "no"; then
pinentry_curses=yes
fi
if test "$fallback_curses" != "no"; then
fallback_curses=yes
AC_DEFINE(FALLBACK_CURSES, 1,
[The GUI pinentries should fall back to curses if X is not available.])
fi
else
if test "$pinentry_curses" = "yes" -o "$fallback_curses" = "yes"; then
AC_MSG_ERROR([[
***
*** The curses library is required. The latest version of
*** ncurses is always available from ftp://ftp.gnu.org/gnu/ncurses/.
***]])
fi
pinentry_curses=no
fallback_curses=no
fi
AM_CONDITIONAL(BUILD_LIBPINENTRY_CURSES,
test "$pinentry_curses" = "yes" -o "$fallback_curses" = "yes")
AM_CONDITIONAL(BUILD_PINENTRY_CURSES, test "$pinentry_curses" = "yes")
AM_CONDITIONAL(FALLBACK_CURSES, test "$fallback_curses" = "yes")
if test "$pinentry_curses" = "yes"; then
AC_DEFINE(PINENTRY_CURSES, 1,
[The Curses version of Pinentry is to be build])
fi
dnl
dnl Check for tty pinentry program.
dnl
AC_ARG_ENABLE(pinentry-tty,
AC_HELP_STRING([--enable-pinentry-tty], [build tty pinentry]),
pinentry_tty=$enableval, pinentry_tty=maybe)
AM_CONDITIONAL(BUILD_PINENTRY_TTY, test "$pinentry_tty" = "yes")
if test "$pinentry_tty" = "yes"; then
AC_DEFINE(PINENTRY_TTY, 1,
[The TTY version of Pinentry is to be build])
fi
dnl
dnl Additional checks pinentry Curses.
dnl
if test "$pinentry_curses" = "yes" \
-o "$fallback_curses" = "yes" ; then
AM_ICONV
if test "$am_cv_func_iconv" != "yes"; then
AC_MSG_ERROR([[
***
*** The iconv function is required. You can specify its location
*** using the --with-libiconv-prefix=PREFIX option to configure.
***]])
fi
fi
dnl
dnl Check for GTK+-2 / GNOME3 pinentry programs.
dnl
AC_ARG_ENABLE(pinentry-gtk2,
AC_HELP_STRING([--enable-pinentry-gtk2], [build GTK+-2 pinentry]),
pinentry_gtk_2=$enableval, pinentry_gtk_2=maybe)
AC_ARG_ENABLE(pinentry-gnome3,
AC_HELP_STRING([--enable-pinentry-gnome3], [build GNOME 3 pinentry]),
pinentry_gnome_3=$enableval, pinentry_gnome_3=maybe)
dnl check for pkg-config
if test "$pinentry_gtk_2" != "no" -o "$pinentry_gnome_3" != "no"; then
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
if test x"${PKG_CONFIG}" = xno ; then
pinentry_gtk_2=no
pinentry_gnome_3=no
fi
fi
dnl check if the module gtk+-2.0 exists
if test "$pinentry_gtk_2" != "no" -o "$pinentry_gnome_3" != "no"; then
AC_MSG_CHECKING([for gtk+-2])
"${PKG_CONFIG}" --exists gtk+-2.0
if test $? -ne 0 ; then
AC_MSG_RESULT([no])
AC_MSG_WARN([pkg-config could not find the module gtk+-2.0])
pinentry_gtk_2=no
pinentry_gnome_3=no
else
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([gtk+-2 version >= 2.4.0])
modvers=`"${PKG_CONFIG}" --modversion gtk+-2.0`
AC_MSG_RESULT([$modvers])
"${PKG_CONFIG}" --atleast-version=2.4.0 gtk+-2.0
if test $? -ne 0 ; then
AC_MSG_WARN([building GTK+-2 pinentry disabled])
pinentry_gtk_2=no
pinentry_gnome_3=no
else
GTK2CFLAGS=`"${PKG_CONFIG}" --cflags gtk+-2.0`
GTK2LIBS=`"${PKG_CONFIG}" --libs gtk+-2.0`
AC_SUBST(GTK2CFLAGS)
AC_SUBST(GTK2LIBS)
if test "$pinentry_gtk_2" != "no"
then
pinentry_gtk_2=yes
fi
if test "$pinentry_gnome_3" != "no"
then
pinentry_gnome_3=yes
fi
fi
fi
fi
AM_CONDITIONAL(BUILD_PINENTRY_GTK_2, test "$pinentry_gtk_2" = "yes")
if test "$pinentry_gnome_3" != "no"; then
AC_MSG_CHECKING([for gcr])
"${PKG_CONFIG}" --exists gcr-3,gcr-base-3
if test $? -ne 0 ; then
AC_MSG_RESULT([no])
AC_MSG_WARN([pkg-config could not find the module gcr-3,gcr-base-3])
pinentry_gnome_3=no
else
AC_MSG_RESULT([yes])
GNOME3CFLAGS=`"${PKG_CONFIG}" --cflags gcr-3,gcr-base-3`
GNOME3LIBS=`"${PKG_CONFIG}" --libs gcr-3,gcr-base-3`
AC_SUBST(GNOME3CFLAGS)
AC_SUBST(GNOME3LIBS)
AC_DEFINE(GCR_API_SUBJECT_TO_CHANGE, 1, [Nod nod])
pinentry_gnome_3=yes
fi
fi
AM_CONDITIONAL(BUILD_PINENTRY_GNOME_3, test "$pinentry_gnome_3" = "yes")
dnl
dnl Check for libsecret.
dnl
AC_ARG_ENABLE(libsecret,
AC_HELP_STRING([--enable-libsecret],
[optionally cache passphrases using libsecret]),
libsecret=$enableval, libsecret=maybe)
dnl check for pkg-config
if test "$libsecret" != "no"; then
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
if test x"${PKG_CONFIG}" = xno ; then
libsecret=no
fi
fi
dnl check if the module libsecret exists
if test "$libsecret" != "no"; then
AC_MSG_CHECKING([for libsecret])
"${PKG_CONFIG}" --exists 'libsecret-1'
if test $? -ne 0 ; then
AC_MSG_RESULT([no])
AC_MSG_WARN([pkg-config could not find the modules libsecret-1])
libsecret=no
else
AC_MSG_RESULT([yes])
LIBSECRET_CFLAGS=`"${PKG_CONFIG}" --cflags 'libsecret-1'`
LIBSECRET_LIBS=`"${PKG_CONFIG}" --libs 'libsecret-1'`
libsecret=yes
fi
fi
AM_CONDITIONAL(BUILD_WITH_LIBSECRET, test "$libsecret" = "yes")
if test "$libsecret" = "yes"; then
AC_DEFINE(HAVE_LIBSECRET, 1,
[The pinentries should optionally cache the passphrase using libsecret.])
COMMON_CFLAGS="$COMMON_CFLAGS $LIBSECRET_CFLAGS"
COMMON_LIBS="$COMMON_LIBS $LIBSECRET_LIBS"
fi
dnl
dnl Check for Qt4 pinentry program.
dnl
AC_ARG_ENABLE(pinentry-qt4,
AC_HELP_STRING([--enable-pinentry-qt4], [build Qt4 pinentry]),
pinentry_qt4=$enableval, pinentry_qt4=maybe)
dnl
dnl Checks for Qt4 libraries. Deal correctly with $pinentry_qt4 = maybe.
dnl
if test "$pinentry_qt4" != "no"; then
PKG_CHECK_MODULES(QT4_CORE, QtCore,,
if test "$pinentry_qt4" = "yes"; then
AC_MSG_ERROR([[
***
*** Qt4 Core is required.
***]])
else
pinentry_qt4=no
fi)
fi
if test "$pinentry_qt4" != "no"; then
QT_PATH_MOC
if test "$have_moc" != "yes"; then
if test "$pinentry_qt4" = "yes"; then
AC_MSG_ERROR([[
***
*** Qt moc is required.
***]])
else
pinentry_qt4=no
fi
fi
PKG_CHECK_MODULES(QT4_GUI, QtGui,,
if test "$pinentry_qt4" = "yes"; then
AC_MSG_ERROR([[
***
*** Qt4 Gui is required.
***]])
else
pinentry_qt4=no
fi)
fi
dnl If we have come so far, Qt4 pinentry can be build.
if test "$pinentry_qt4" != "no"; then
pinentry_qt4=yes
fi
AM_CONDITIONAL(BUILD_PINENTRY_QT4, test "$pinentry_qt4" = "yes")
if test "$pinentry_qt4" = "yes"; then
AC_DEFINE(PINENTRY_QT4, 1, [The Qt4 version of Pinentry is to be build])
fi
dnl
dnl Option to add insecure clipboard support to pinentry-qt4
dnl
AC_ARG_ENABLE(pinentry-qt4-clipboard,
AC_HELP_STRING([--enable-pinentry-qt4-clipboard], [Enable clipboard support in
pinentry-qt4]), pinentry_qt4_clipboard=$enableval)
if test "$pinentry_qt4_clipboard" = "yes" -a "$pinentry_qt4" = "yes"; then
AC_DEFINE(PINENTRY_QT4_CLIPBOARD, 1, [Pinentry-qt4 should have clipboard support])
pinentry_qt4_clip_msg="(with clipboard support)"
fi
dnl if test "$pinentry_qt4" = "yes"; then
dnl Additional checks for Qt4 pinentry.
dnl End of additional checks for Qt4 pinentry.
dnl fi
#
# Check whether we should build the W32 pinentry. This is actually
# the simplest check as we do this only for that platform.
#
pinentry_w32=no
test $have_w32_system = yes && pinentry_w32=yes
AM_CONDITIONAL(BUILD_PINENTRY_W32, test "$pinentry_w32" = "yes")
# Figure out the default pinentry. We are very conservative here.
# Please change the order only after verifying that the preferred
# pinentry really is better (more feature-complete and more secure).
if test "$pinentry_gtk_2" = "yes"; then
PINENTRY_DEFAULT=pinentry-gtk-2
else
if test "$pinentry_qt4" = "yes"; then
PINENTRY_DEFAULT=pinentry-qt4
else
if test "$pinentry_gnome_3" = "yes"; then
PINENTRY_DEFAULT=pinentry-gnome3
else
if test "$pinentry_curses" = "yes"; then
PINENTRY_DEFAULT=pinentry-curses
else
if test "$pinentry_tty" = "yes"; then
PINENTRY_DEFAULT=pinentry-tty
else
if test "$pinentry_w32" = "yes"; then
PINENTRY_DEFAULT=pinentry-w32
else
AC_MSG_ERROR([[No pinentry enabled.]])
fi
fi
fi
fi
fi
fi
AC_SUBST(PINENTRY_DEFAULT)
AC_CONFIG_FILES([
assuan/Makefile
secmem/Makefile
pinentry/Makefile
curses/Makefile
tty/Makefile
gtk+-2/Makefile
gnome3/Makefile
qt4/Makefile
w32/Makefile
doc/Makefile
Makefile
])
AC_OUTPUT
AC_MSG_NOTICE([
Pinentry v${VERSION} has been configured as follows:
Revision: mym4_revision (mym4_revision_dec)
Platform: $host
Curses Pinentry ..: $pinentry_curses
TTY Pinentry .....: $pinentry_tty
GTK+-2 Pinentry ..: $pinentry_gtk_2
GNOME 3 Pinentry .: $pinentry_gnome_3
Qt4 Pinentry .....: $pinentry_qt4 $pinentry_qt4_clip_msg
W32 Pinentry .....: $pinentry_w32
Fallback to Curses: $fallback_curses
libsecret ........: $libsecret
Default Pinentry .: $PINENTRY_DEFAULT
])
diff --git a/gtk+-2/gtksecentry.c b/gtk+-2/gtksecentry.c
index 10f41c3..112d7c2 100644
--- a/gtk+-2/gtksecentry.c
+++ b/gtk+-2/gtksecentry.c
@@ -1,3530 +1,3562 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
* Copyright (C) 2004 Albrecht Dre゚
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* Heavily stripped down for use in pinentry-gtk-2 by Albrecht Dre゚
* <albrecht.dress@arcor.de> Feb. 2004:
*
* The entry is now always invisible, uses secure memory methods to
* allocate the text memory, and all potentially dangerous methods
* (copy, popup, etc.) have been removed.
*/
#include <stdlib.h>
#include <string.h>
#include <gdk/gdkkeysyms.h>
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7 )
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstrict-prototypes"
#endif
#include <gtk/gtk.h>
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7 )
# pragma GCC diagnostic pop
#endif
#include "gtksecentry.h"
#include "memory.h"
#define MIN_SECURE_ENTRY_WIDTH 150
#define DRAW_TIMEOUT 20
#define INNER_BORDER 2
/* Initial size of buffer, in bytes */
#define MIN_SIZE 16
/* Maximum size of text buffer, in bytes */
#define MAX_SIZE G_MAXUSHORT
enum {
ACTIVATE,
MOVE_CURSOR,
INSERT_AT_CURSOR,
DELETE_FROM_CURSOR,
PASTE_CLIPBOARD,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_CURSOR_POSITION,
PROP_SELECTION_BOUND,
PROP_MAX_LENGTH,
PROP_HAS_FRAME,
PROP_INVISIBLE_CHAR,
PROP_ACTIVATES_DEFAULT,
PROP_WIDTH_CHARS,
PROP_SCROLL_OFFSET,
PROP_TEXT,
PROP_EDITING_CANCELED
};
static guint signals[LAST_SIGNAL] = { 0 };
/* GObject, GtkObject methods
*/
static void gtk_secure_entry_class_init(GtkSecureEntryClass * klass);
static void gtk_secure_entry_editable_init(GtkEditableClass * iface);
static void gtk_secure_entry_cell_editable_init(GtkCellEditableIface *
iface);
static void gtk_secure_entry_init(GtkSecureEntry * entry);
static void gtk_secure_entry_set_property(GObject * object,
guint prop_id,
const GValue * value,
GParamSpec * pspec);
static void gtk_secure_entry_get_property(GObject * object,
guint prop_id,
GValue * value,
GParamSpec * pspec);
static void gtk_secure_entry_finalize(GObject * object);
/* GtkWidget methods
*/
static void gtk_secure_entry_realize(GtkWidget * widget);
static void gtk_secure_entry_unrealize(GtkWidget * widget);
static void gtk_secure_entry_size_request(GtkWidget * widget,
GtkRequisition * requisition);
static void gtk_secure_entry_size_allocate(GtkWidget * widget,
GtkAllocation * allocation);
static void gtk_secure_entry_draw_frame(GtkWidget * widget);
static gint gtk_secure_entry_expose(GtkWidget * widget,
GdkEventExpose * event);
static gint gtk_secure_entry_button_press(GtkWidget * widget,
GdkEventButton * event);
static gint gtk_secure_entry_button_release(GtkWidget * widget,
GdkEventButton * event);
static gint gtk_secure_entry_motion_notify(GtkWidget * widget,
GdkEventMotion * event);
static gint gtk_secure_entry_key_press(GtkWidget * widget,
GdkEventKey * event);
static gint gtk_secure_entry_key_release(GtkWidget * widget,
GdkEventKey * event);
static gint gtk_secure_entry_focus_in(GtkWidget * widget,
GdkEventFocus * event);
static gint gtk_secure_entry_focus_out(GtkWidget * widget,
GdkEventFocus * event);
static void gtk_secure_entry_grab_focus(GtkWidget * widget);
static void gtk_secure_entry_style_set(GtkWidget * widget,
GtkStyle * previous_style);
static void gtk_secure_entry_direction_changed(GtkWidget * widget,
GtkTextDirection
previous_dir);
static void gtk_secure_entry_state_changed(GtkWidget * widget,
GtkStateType previous_state);
static void gtk_secure_entry_screen_changed(GtkWidget * widget,
GdkScreen * old_screen);
/* GtkEditable method implementations
*/
static void gtk_secure_entry_insert_text(GtkEditable * editable,
const gchar * new_text,
gint new_text_length,
gint * position);
static void gtk_secure_entry_delete_text(GtkEditable * editable,
gint start_pos, gint end_pos);
static void gtk_secure_entry_real_set_position(GtkEditable * editable,
gint position);
static gint gtk_secure_entry_get_position(GtkEditable * editable);
static void gtk_secure_entry_set_selection_bounds(GtkEditable * editable,
gint start, gint end);
static gboolean gtk_secure_entry_get_selection_bounds(GtkEditable *
editable,
gint * start,
gint * end);
/* GtkCellEditable method implementations
*/
static void gtk_secure_entry_start_editing(GtkCellEditable * cell_editable,
GdkEvent * event);
/* Default signal handlers
*/
static void gtk_secure_entry_real_insert_text(GtkEditable * editable,
const gchar * new_text,
gint new_text_length,
gint * position);
static void gtk_secure_entry_real_delete_text(GtkEditable * editable,
gint start_pos,
gint end_pos);
static void gtk_secure_entry_move_cursor(GtkSecureEntry * entry,
GtkMovementStep step,
gint count,
gboolean extend_selection);
static void gtk_secure_entry_insert_at_cursor(GtkSecureEntry * entry,
const gchar * str);
static void gtk_secure_entry_delete_from_cursor(GtkSecureEntry * entry,
GtkDeleteType type,
gint count);
static void gtk_secure_entry_real_activate(GtkSecureEntry * entry);
static void gtk_secure_entry_keymap_direction_changed(GdkKeymap * keymap,
GtkSecureEntry *
entry);
/* IM Context Callbacks
*/
static void gtk_secure_entry_commit_cb(GtkIMContext * context,
const gchar * str,
GtkSecureEntry * entry);
static void gtk_secure_entry_preedit_changed_cb(GtkIMContext * context,
GtkSecureEntry * entry);
static gboolean gtk_secure_entry_retrieve_surrounding_cb(GtkIMContext *
context,
GtkSecureEntry *
entry);
static gboolean gtk_secure_entry_delete_surrounding_cb(GtkIMContext *
context,
gint offset,
gint n_chars,
GtkSecureEntry *
entry);
/* Internal routines
*/
static void begin_change (GtkSecureEntry *entry);
static void end_change (GtkSecureEntry *entry);
static void emit_changed (GtkSecureEntry *entry);
static void gtk_secure_entry_paste (GtkSecureEntry *entry, GdkAtom selection);
static void gtk_secure_entry_paste_clipboard (GtkSecureEntry *entry);
static void gtk_secure_entry_enter_text(GtkSecureEntry * entry,
const gchar * str);
static void gtk_secure_entry_set_positions(GtkSecureEntry * entry,
gint current_pos,
gint selection_bound);
static void gtk_secure_entry_draw_text(GtkSecureEntry * entry);
static void gtk_secure_entry_draw_cursor(GtkSecureEntry * entry);
static PangoLayout *gtk_secure_entry_ensure_layout(GtkSecureEntry * entry,
gboolean
include_preedit);
static void gtk_secure_entry_reset_layout(GtkSecureEntry * entry);
static void gtk_secure_entry_queue_draw(GtkSecureEntry * entry);
static void gtk_secure_entry_reset_im_context(GtkSecureEntry * entry);
static void gtk_secure_entry_recompute(GtkSecureEntry * entry);
static gint gtk_secure_entry_find_position(GtkSecureEntry * entry, gint x);
static void gtk_secure_entry_get_cursor_locations(GtkSecureEntry * entry,
gint * strong_x,
gint * weak_x);
static void gtk_secure_entry_adjust_scroll(GtkSecureEntry * entry);
static gint gtk_secure_entry_move_visually(GtkSecureEntry * editable,
gint start, gint count);
static gint gtk_secure_entry_move_logically(GtkSecureEntry * entry,
gint start, gint count);
static gboolean gtk_secure_entry_mnemonic_activate(GtkWidget * widget,
gboolean group_cycling);
static void gtk_secure_entry_state_changed(GtkWidget * widget,
GtkStateType previous_state);
static void gtk_secure_entry_check_cursor_blink(GtkSecureEntry * entry);
static void gtk_secure_entry_pend_cursor_blink(GtkSecureEntry * entry);
static void get_text_area_size(GtkSecureEntry * entry,
gint * x,
gint * y, gint * width, gint * height);
static void get_widget_window_size(GtkSecureEntry * entry,
gint * x,
gint * y, gint * width, gint * height);
#define _gtk_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
#define _gtk_marshal_VOID__STRING g_cclosure_marshal_VOID__STRING
static void _gtk_marshal_VOID__ENUM_INT_BOOLEAN(GClosure * closure,
GValue * return_value,
guint n_param_values,
const GValue *
param_values,
gpointer invocation_hint,
gpointer marshal_data);
static void _gtk_marshal_VOID__ENUM_INT(GClosure * closure,
GValue * return_value,
guint n_param_values,
const GValue * param_values,
gpointer invocation_hint,
gpointer marshal_data);
static GtkWidgetClass *parent_class = NULL;
gboolean g_use_secure_mem = FALSE;
# define g_sec_new(type, count) \
((type *) g_sec_malloc ((unsigned) sizeof (type) * (count)))
#define WITH_SECURE_MEM(EXP) do { \
gboolean tmp = g_use_secure_mem; \
g_use_secure_mem = TRUE; \
EXP; \
g_use_secure_mem = tmp; \
} while(0)
gpointer
secentry_malloc (GMALLOC_SIZE size)
{
gpointer p;
if (size == 0)
return NULL;
if (g_use_secure_mem)
p = (gpointer) secmem_malloc(size);
else
p = (gpointer) malloc(size);
if (!p)
g_error("could not allocate %lu bytes", (unsigned long)size);
return p;
}
gpointer
secentry_realloc (gpointer mem, GMALLOC_SIZE size)
{
gpointer p;
if (size == 0) {
g_free(mem);
return NULL;
}
if (!mem) {
if (g_use_secure_mem)
p = (gpointer) secmem_malloc(size);
else
p = (gpointer) malloc(size);
} else {
if (g_use_secure_mem) {
g_assert(m_is_secure(mem));
p = (gpointer) secmem_realloc(mem, size);
} else
p = (gpointer) realloc(mem, size);
}
if (!p)
g_error("could not reallocate %lu bytes", (gulong) size);
return p;
}
void
secentry_free(gpointer mem)
{
if (mem) {
if (m_is_secure(mem))
secmem_free(mem);
else
free(mem);
}
}
GType
gtk_secure_entry_get_type(void)
{
static GType entry_type = 0;
if (!entry_type) {
static const GTypeInfo entry_info = {
sizeof(GtkSecureEntryClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) gtk_secure_entry_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof(GtkSecureEntry),
0, /* n_preallocs */
(GInstanceInitFunc) gtk_secure_entry_init,
};
static const GInterfaceInfo editable_info = {
(GInterfaceInitFunc) gtk_secure_entry_editable_init, /* interface_init */
NULL, /* interface_finalize */
NULL /* interface_data */
};
static const GInterfaceInfo cell_editable_info = {
(GInterfaceInitFunc) gtk_secure_entry_cell_editable_init, /* interface_init */
NULL, /* interface_finalize */
NULL /* interface_data */
};
entry_type =
g_type_register_static(GTK_TYPE_WIDGET, "GtkSecureEntry",
&entry_info, 0);
g_type_add_interface_static(entry_type,
GTK_TYPE_EDITABLE, &editable_info);
g_type_add_interface_static(entry_type,
GTK_TYPE_CELL_EDITABLE,
&cell_editable_info);
}
return entry_type;
}
static void
add_move_binding(GtkBindingSet * binding_set,
guint keyval,
guint modmask, GtkMovementStep step, gint count)
{
g_return_if_fail((modmask & GDK_SHIFT_MASK) == 0);
gtk_binding_entry_add_signal(binding_set, keyval, modmask,
"move_cursor", 3,
G_TYPE_ENUM, step,
G_TYPE_INT, count, G_TYPE_BOOLEAN, FALSE);
/* Selection-extending version */
gtk_binding_entry_add_signal(binding_set, keyval,
modmask | GDK_SHIFT_MASK, "move_cursor",
3, G_TYPE_ENUM, step, G_TYPE_INT, count,
G_TYPE_BOOLEAN, TRUE);
}
static void
gtk_secure_entry_class_init(GtkSecureEntryClass * class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
GtkWidgetClass *widget_class;
GtkBindingSet *binding_set;
widget_class = (GtkWidgetClass *) class;
parent_class = g_type_class_peek_parent(class);
gobject_class->finalize = gtk_secure_entry_finalize;
gobject_class->set_property = gtk_secure_entry_set_property;
gobject_class->get_property = gtk_secure_entry_get_property;
widget_class->realize = gtk_secure_entry_realize;
widget_class->unrealize = gtk_secure_entry_unrealize;
widget_class->size_request = gtk_secure_entry_size_request;
widget_class->size_allocate = gtk_secure_entry_size_allocate;
widget_class->expose_event = gtk_secure_entry_expose;
widget_class->button_press_event = gtk_secure_entry_button_press;
widget_class->button_release_event = gtk_secure_entry_button_release;
widget_class->motion_notify_event = gtk_secure_entry_motion_notify;
widget_class->key_press_event = gtk_secure_entry_key_press;
widget_class->key_release_event = gtk_secure_entry_key_release;
widget_class->focus_in_event = gtk_secure_entry_focus_in;
widget_class->focus_out_event = gtk_secure_entry_focus_out;
widget_class->grab_focus = gtk_secure_entry_grab_focus;
widget_class->style_set = gtk_secure_entry_style_set;
widget_class->direction_changed = gtk_secure_entry_direction_changed;
widget_class->state_changed = gtk_secure_entry_state_changed;
widget_class->screen_changed = gtk_secure_entry_screen_changed;
widget_class->mnemonic_activate = gtk_secure_entry_mnemonic_activate;
class->move_cursor = gtk_secure_entry_move_cursor;
class->insert_at_cursor = gtk_secure_entry_insert_at_cursor;
class->delete_from_cursor = gtk_secure_entry_delete_from_cursor;
class->paste_clipboard = gtk_secure_entry_paste_clipboard;
class->activate = gtk_secure_entry_real_activate;
g_object_class_override_property (gobject_class,
PROP_EDITING_CANCELED,
"editing-canceled");
g_object_class_install_property(gobject_class,
PROP_CURSOR_POSITION,
g_param_spec_int("cursor_position",
"Cursor Position",
"The current position of the insertion cursor in chars",
0, MAX_SIZE, 0,
G_PARAM_READABLE));
g_object_class_install_property(gobject_class,
PROP_SELECTION_BOUND,
g_param_spec_int("selection_bound",
"Selection Bound",
"The position of the opposite end of the selection from the cursor in chars",
0, MAX_SIZE, 0,
G_PARAM_READABLE));
g_object_class_install_property(gobject_class,
PROP_MAX_LENGTH,
g_param_spec_int("max_length",
"Maximum length",
"Maximum number of characters for this entry. Zero if no maximum",
0, MAX_SIZE, 0,
G_PARAM_READABLE |
G_PARAM_WRITABLE));
g_object_class_install_property(gobject_class,
PROP_HAS_FRAME,
g_param_spec_boolean("has_frame",
"Has Frame",
"FALSE removes outside bevel from entry",
TRUE,
G_PARAM_READABLE |
G_PARAM_WRITABLE));
g_object_class_install_property(gobject_class,
PROP_INVISIBLE_CHAR,
g_param_spec_unichar("invisible_char",
"Invisible character",
"The character to use when masking entry contents (in \"password mode\")",
'*',
G_PARAM_READABLE |
G_PARAM_WRITABLE));
g_object_class_install_property(gobject_class,
PROP_ACTIVATES_DEFAULT,
g_param_spec_boolean
("activates_default",
"Activates default",
"Whether to activate the default widget (such as the default button in a dialog) when Enter is pressed",
FALSE,
G_PARAM_READABLE | G_PARAM_WRITABLE));
g_object_class_install_property(gobject_class, PROP_WIDTH_CHARS,
g_param_spec_int("width_chars",
"Width in chars",
"Number of characters to leave space for in the entry",
-1, G_MAXINT, -1,
G_PARAM_READABLE |
G_PARAM_WRITABLE));
g_object_class_install_property(gobject_class,
PROP_SCROLL_OFFSET,
g_param_spec_int("scroll_offset",
"Scroll offset",
"Number of pixels of the entry scrolled off the screen to the left",
0, G_MAXINT, 0,
G_PARAM_READABLE));
g_object_class_install_property(gobject_class,
PROP_TEXT,
g_param_spec_string("text",
"Text",
"The contents of the entry",
"",
G_PARAM_READABLE |
G_PARAM_WRITABLE));
/* Action signals */
signals[ACTIVATE] =
g_signal_new("activate",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET(GtkSecureEntryClass, activate),
NULL, NULL, _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
widget_class->activate_signal = signals[ACTIVATE];
signals[MOVE_CURSOR] =
g_signal_new("move_cursor",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET(GtkSecureEntryClass, move_cursor),
NULL, NULL,
_gtk_marshal_VOID__ENUM_INT_BOOLEAN,
G_TYPE_NONE, 3,
GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT, G_TYPE_BOOLEAN);
signals[INSERT_AT_CURSOR] =
g_signal_new("insert_at_cursor",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET(GtkSecureEntryClass,
insert_at_cursor), NULL, NULL,
_gtk_marshal_VOID__STRING, G_TYPE_NONE, 1,
G_TYPE_STRING);
signals[DELETE_FROM_CURSOR] =
g_signal_new("delete_from_cursor",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET(GtkSecureEntryClass,
delete_from_cursor), NULL, NULL,
_gtk_marshal_VOID__ENUM_INT, G_TYPE_NONE, 2,
GTK_TYPE_DELETE_TYPE, G_TYPE_INT);
signals[PASTE_CLIPBOARD] =
g_signal_new ("paste-clipboard",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkEntryClass, paste_clipboard),
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/*
* Key bindings
*/
binding_set = gtk_binding_set_by_class(class);
/* Moving the insertion point */
add_move_binding(binding_set, GDK_Right, 0,
GTK_MOVEMENT_VISUAL_POSITIONS, 1);
add_move_binding(binding_set, GDK_Left, 0,
GTK_MOVEMENT_VISUAL_POSITIONS, -1);
add_move_binding(binding_set, GDK_KP_Right, 0,
GTK_MOVEMENT_VISUAL_POSITIONS, 1);
add_move_binding(binding_set, GDK_KP_Left, 0,
GTK_MOVEMENT_VISUAL_POSITIONS, -1);
add_move_binding(binding_set, GDK_Right, GDK_CONTROL_MASK,
GTK_MOVEMENT_WORDS, 1);
add_move_binding(binding_set, GDK_Left, GDK_CONTROL_MASK,
GTK_MOVEMENT_WORDS, -1);
add_move_binding(binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
GTK_MOVEMENT_WORDS, 1);
add_move_binding(binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
GTK_MOVEMENT_WORDS, -1);
add_move_binding(binding_set, GDK_Home, 0,
GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
add_move_binding(binding_set, GDK_End, 0,
GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
add_move_binding(binding_set, GDK_KP_Home, 0,
GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
add_move_binding(binding_set, GDK_KP_End, 0,
GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
add_move_binding(binding_set, GDK_Home, GDK_CONTROL_MASK,
GTK_MOVEMENT_BUFFER_ENDS, -1);
add_move_binding(binding_set, GDK_End, GDK_CONTROL_MASK,
GTK_MOVEMENT_BUFFER_ENDS, 1);
add_move_binding(binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
GTK_MOVEMENT_BUFFER_ENDS, -1);
add_move_binding(binding_set, GDK_KP_End, GDK_CONTROL_MASK,
GTK_MOVEMENT_BUFFER_ENDS, 1);
/* Select all
*/
gtk_binding_entry_add_signal(binding_set, GDK_a, GDK_CONTROL_MASK,
"move_cursor", 3,
GTK_TYPE_MOVEMENT_STEP,
GTK_MOVEMENT_BUFFER_ENDS, G_TYPE_INT, -1,
G_TYPE_BOOLEAN, FALSE);
gtk_binding_entry_add_signal(binding_set, GDK_a, GDK_CONTROL_MASK,
"move_cursor", 3, GTK_TYPE_MOVEMENT_STEP,
GTK_MOVEMENT_BUFFER_ENDS, G_TYPE_INT, 1,
G_TYPE_BOOLEAN, TRUE);
/* Activate
*/
gtk_binding_entry_add_signal(binding_set, GDK_Return, 0,
"activate", 0);
gtk_binding_entry_add_signal(binding_set, GDK_KP_Enter, 0,
"activate", 0);
/* Deleting text */
gtk_binding_entry_add_signal(binding_set, GDK_Delete, 0,
"delete_from_cursor", 2,
G_TYPE_ENUM, GTK_DELETE_CHARS,
G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_KP_Delete, 0,
"delete_from_cursor", 2,
G_TYPE_ENUM, GTK_DELETE_CHARS,
G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_BackSpace, 0,
"delete_from_cursor", 2,
G_TYPE_ENUM, GTK_DELETE_CHARS,
G_TYPE_INT, -1);
/* Make this do the same as Backspace, to help with mis-typing */
gtk_binding_entry_add_signal(binding_set, GDK_BackSpace,
GDK_SHIFT_MASK, "delete_from_cursor", 2,
G_TYPE_ENUM, GTK_DELETE_CHARS, G_TYPE_INT,
-1);
gtk_binding_entry_add_signal(binding_set, GDK_Delete, GDK_CONTROL_MASK,
"delete_from_cursor", 2,
G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_KP_Delete,
GDK_CONTROL_MASK, "delete_from_cursor", 2,
G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_BackSpace,
GDK_CONTROL_MASK, "delete_from_cursor", 2,
G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
G_TYPE_INT, -1);
/* Clipboard - only pasting of course. */
/* Ist does not work right now. Depending on the GTK version we
see segv due to "property added after class was intialized". */
/* gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK, */
/* "paste-clipboard", 0); */
/* gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_SHIFT_MASK, */
/* "paste-clipboard", 0); */
}
static void
gtk_secure_entry_editable_init(GtkEditableClass * iface)
{
iface->do_insert_text = gtk_secure_entry_insert_text;
iface->do_delete_text = gtk_secure_entry_delete_text;
iface->insert_text = gtk_secure_entry_real_insert_text;
iface->delete_text = gtk_secure_entry_real_delete_text;
iface->set_selection_bounds = gtk_secure_entry_set_selection_bounds;
iface->get_selection_bounds = gtk_secure_entry_get_selection_bounds;
iface->set_position = gtk_secure_entry_real_set_position;
iface->get_position = gtk_secure_entry_get_position;
}
static void
gtk_secure_entry_cell_editable_init(GtkCellEditableIface * iface)
{
iface->start_editing = gtk_secure_entry_start_editing;
}
static void
gtk_secure_entry_set_property(GObject * object,
guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
switch (prop_id) {
case PROP_MAX_LENGTH:
gtk_secure_entry_set_max_length(entry, g_value_get_int(value));
break;
case PROP_HAS_FRAME:
gtk_secure_entry_set_has_frame(entry, g_value_get_boolean(value));
break;
case PROP_INVISIBLE_CHAR:
gtk_secure_entry_set_invisible_char(entry,
g_value_get_uint(value));
break;
case PROP_ACTIVATES_DEFAULT:
gtk_secure_entry_set_activates_default(entry,
g_value_get_boolean(value));
break;
case PROP_WIDTH_CHARS:
gtk_secure_entry_set_width_chars(entry, g_value_get_int(value));
break;
case PROP_TEXT:
gtk_secure_entry_set_text(entry, g_value_get_string(value));
break;
case PROP_EDITING_CANCELED:
/* We may want to implement the GtkCelllEditable methods.
However it is only used by GtkTreeView and we don't use that
here. We provide this property only to get rid of a Gtk+
runtime warning. */
entry->editing_canceled = g_value_get_boolean (value);
break;
case PROP_SCROLL_OFFSET:
case PROP_CURSOR_POSITION:
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
gtk_secure_entry_get_property(GObject * object,
guint prop_id,
GValue * value, GParamSpec * pspec)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
switch (prop_id) {
case PROP_CURSOR_POSITION:
g_value_set_int(value, entry->current_pos);
break;
case PROP_SELECTION_BOUND:
g_value_set_int(value, entry->selection_bound);
break;
case PROP_MAX_LENGTH:
g_value_set_int(value, entry->text_max_length);
break;
case PROP_HAS_FRAME:
g_value_set_boolean(value, entry->has_frame);
break;
case PROP_INVISIBLE_CHAR:
g_value_set_uint(value, entry->invisible_char);
break;
case PROP_ACTIVATES_DEFAULT:
g_value_set_boolean(value, entry->activates_default);
break;
case PROP_WIDTH_CHARS:
g_value_set_int(value, entry->width_chars);
break;
case PROP_SCROLL_OFFSET:
g_value_set_int(value, entry->scroll_offset);
break;
case PROP_TEXT:
g_value_set_string(value, gtk_secure_entry_get_text(entry));
break;
case PROP_EDITING_CANCELED:
g_value_set_boolean (value, entry->editing_canceled);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
gtk_secure_entry_init(GtkSecureEntry * entry)
{
GTK_WIDGET_SET_FLAGS(entry, GTK_CAN_FOCUS);
entry->text_size = MIN_SIZE;
WITH_SECURE_MEM(entry->text = g_malloc(entry->text_size));
entry->text[0] = '\0';
entry->invisible_char = '*';
entry->width_chars = -1;
entry->is_cell_renderer = FALSE;
entry->editing_canceled = FALSE;
entry->has_frame = TRUE;
/* This object is completely private. No external entity can gain a reference
* to it; so we create it here and destroy it in finalize().
*/
entry->im_context = gtk_im_multicontext_new();
g_signal_connect(entry->im_context, "commit",
G_CALLBACK(gtk_secure_entry_commit_cb), entry);
g_signal_connect(entry->im_context, "preedit_changed",
G_CALLBACK(gtk_secure_entry_preedit_changed_cb),
entry);
g_signal_connect(entry->im_context, "retrieve_surrounding",
G_CALLBACK(gtk_secure_entry_retrieve_surrounding_cb),
entry);
g_signal_connect(entry->im_context, "delete_surrounding",
G_CALLBACK(gtk_secure_entry_delete_surrounding_cb),
entry);
}
static void
gtk_secure_entry_finalize(GObject * object)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
if (entry->cached_layout)
g_object_unref(entry->cached_layout);
g_object_unref(entry->im_context);
if (entry->blink_timeout)
g_source_remove(entry->blink_timeout);
if (entry->recompute_idle)
g_source_remove(entry->recompute_idle);
entry->text_size = 0;
if (entry->text)
WITH_SECURE_MEM(g_free(entry->text));
entry->text = NULL;
G_OBJECT_CLASS(parent_class)->finalize(object);
}
static void
gtk_secure_entry_realize(GtkWidget * widget)
{
GtkSecureEntry *entry;
GdkWindowAttr attributes;
gint attributes_mask;
GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
entry = GTK_SECURE_ENTRY(widget);
attributes.window_type = GDK_WINDOW_CHILD;
get_widget_window_size(entry, &attributes.x, &attributes.y,
&attributes.width, &attributes.height);
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual(widget);
attributes.colormap = gtk_widget_get_colormap(widget);
attributes.event_mask = gtk_widget_get_events(widget);
attributes.event_mask |= (GDK_EXPOSURE_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON3_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK |
GDK_POINTER_MOTION_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK);
attributes_mask =
GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window =
gdk_window_new(gtk_widget_get_parent_window(widget), &attributes,
attributes_mask);
gdk_window_set_user_data(widget->window, entry);
get_text_area_size(entry, &attributes.x, &attributes.y,
&attributes.width, &attributes.height);
attributes.cursor =
gdk_cursor_new_for_display(gtk_widget_get_display(widget),
GDK_XTERM);
attributes_mask |= GDK_WA_CURSOR;
entry->text_area =
gdk_window_new(widget->window, &attributes, attributes_mask);
gdk_window_set_user_data(entry->text_area, entry);
gdk_cursor_unref(attributes.cursor);
widget->style = gtk_style_attach(widget->style, widget->window);
gdk_window_set_background(widget->window,
&widget->style->
base[GTK_WIDGET_STATE(widget)]);
gdk_window_set_background(entry->text_area,
&widget->style->
base[GTK_WIDGET_STATE(widget)]);
gdk_window_show(entry->text_area);
gtk_im_context_set_client_window(entry->im_context, entry->text_area);
gtk_secure_entry_adjust_scroll(entry);
}
static void
gtk_secure_entry_unrealize(GtkWidget * widget)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
gtk_secure_entry_reset_layout(entry);
gtk_im_context_set_client_window(entry->im_context, NULL);
if (entry->text_area) {
gdk_window_set_user_data(entry->text_area, NULL);
gdk_window_destroy(entry->text_area);
entry->text_area = NULL;
}
if (GTK_WIDGET_CLASS(parent_class)->unrealize)
(*GTK_WIDGET_CLASS(parent_class)->unrealize) (widget);
}
static void
get_borders(GtkSecureEntry * entry, gint * xborder, gint * yborder)
{
GtkWidget *widget = GTK_WIDGET(entry);
gint focus_width;
gboolean interior_focus;
gtk_widget_style_get(widget,
"interior-focus", &interior_focus,
"focus-line-width", &focus_width, NULL);
if (entry->has_frame) {
*xborder = widget->style->xthickness;
*yborder = widget->style->ythickness;
} else {
*xborder = 0;
*yborder = 0;
}
if (!interior_focus) {
*xborder += focus_width;
*yborder += focus_width;
}
}
static void
gtk_secure_entry_size_request(GtkWidget * widget,
GtkRequisition * requisition)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
PangoFontMetrics *metrics;
gint xborder, yborder;
PangoContext *context;
context = gtk_widget_get_pango_context(widget);
metrics = pango_context_get_metrics(context,
widget->style->font_desc,
pango_context_get_language
(context));
entry->ascent = pango_font_metrics_get_ascent(metrics);
entry->descent = pango_font_metrics_get_descent(metrics);
get_borders(entry, &xborder, &yborder);
xborder += INNER_BORDER;
yborder += INNER_BORDER;
if (entry->width_chars < 0)
requisition->width = MIN_SECURE_ENTRY_WIDTH + xborder * 2;
else {
gint char_width =
pango_font_metrics_get_approximate_char_width(metrics);
gint digit_width =
pango_font_metrics_get_approximate_digit_width(metrics);
gint char_pixels =
(MAX(char_width, digit_width) + PANGO_SCALE - 1) / PANGO_SCALE;
requisition->width =
char_pixels * entry->width_chars + xborder * 2;
}
requisition->height =
PANGO_PIXELS(entry->ascent + entry->descent) + yborder * 2;
pango_font_metrics_unref(metrics);
}
static void
get_text_area_size(GtkSecureEntry * entry,
gint * x, gint * y, gint * width, gint * height)
{
gint xborder, yborder;
GtkRequisition requisition;
GtkWidget *widget = GTK_WIDGET(entry);
gtk_widget_get_child_requisition(widget, &requisition);
get_borders(entry, &xborder, &yborder);
if (x)
*x = xborder;
if (y)
*y = yborder;
if (width)
*width = GTK_WIDGET(entry)->allocation.width - xborder * 2;
if (height)
*height = requisition.height - yborder * 2;
}
static void
get_widget_window_size(GtkSecureEntry * entry,
gint * x, gint * y, gint * width, gint * height)
{
GtkRequisition requisition;
GtkWidget *widget = GTK_WIDGET(entry);
gtk_widget_get_child_requisition(widget, &requisition);
if (x)
*x = widget->allocation.x;
if (y) {
if (entry->is_cell_renderer)
*y = widget->allocation.y;
else
*y = widget->allocation.y + (widget->allocation.height -
requisition.height) / 2;
}
if (width)
*width = widget->allocation.width;
if (height) {
if (entry->is_cell_renderer)
*height = widget->allocation.height;
else
*height = requisition.height;
}
}
static void
gtk_secure_entry_size_allocate(GtkWidget * widget,
GtkAllocation * allocation)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
widget->allocation = *allocation;
if (GTK_WIDGET_REALIZED(widget)) {
/* We call gtk_widget_get_child_requisition, since we want (for
* backwards compatibility reasons) the realization here to
* be affected by the usize of the entry, if set
*/
gint x, y, width, height;
get_widget_window_size(entry, &x, &y, &width, &height);
gdk_window_move_resize(widget->window, x, y, width, height);
get_text_area_size(entry, &x, &y, &width, &height);
gdk_window_move_resize(entry->text_area, x, y, width, height);
gtk_secure_entry_recompute(entry);
}
}
static void
gtk_secure_entry_draw_frame(GtkWidget * widget)
{
gint x = 0, y = 0;
gint width, height;
gboolean interior_focus;
gint focus_width;
gtk_widget_style_get(widget,
"interior-focus", &interior_focus,
"focus-line-width", &focus_width, NULL);
gdk_drawable_get_size(widget->window, &width, &height);
if (GTK_WIDGET_HAS_FOCUS(widget) && !interior_focus) {
x += focus_width;
y += focus_width;
width -= 2 * focus_width;
height -= 2 * focus_width;
}
gtk_paint_shadow(widget->style, widget->window,
GTK_STATE_NORMAL, GTK_SHADOW_IN,
NULL, widget, "entry", x, y, width, height);
if (GTK_WIDGET_HAS_FOCUS(widget) && !interior_focus) {
x -= focus_width;
y -= focus_width;
width += 2 * focus_width;
height += 2 * focus_width;
gtk_paint_focus(widget->style, widget->window,
GTK_WIDGET_STATE(widget), NULL, widget, "entry", 0,
0, width, height);
}
}
static gint
gtk_secure_entry_expose(GtkWidget * widget, GdkEventExpose * event)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
if (widget->window == event->window)
gtk_secure_entry_draw_frame(widget);
else if (entry->text_area == event->window) {
gint area_width, area_height;
get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
gtk_paint_flat_box(widget->style, entry->text_area,
GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
NULL, widget, "entry_bg",
0, 0, area_width, area_height);
if ((entry->invisible_char != 0) &&
GTK_WIDGET_HAS_FOCUS(widget) &&
entry->selection_bound == entry->current_pos
&& entry->cursor_visible)
gtk_secure_entry_draw_cursor(GTK_SECURE_ENTRY(widget));
gtk_secure_entry_draw_text(GTK_SECURE_ENTRY(widget));
}
return FALSE;
}
static gint
gtk_secure_entry_button_press(GtkWidget * widget, GdkEventButton * event)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
gint tmp_pos;
if (event->window != entry->text_area ||
(entry->button && event->button != entry->button))
return FALSE;
entry->button = event->button;
if (!GTK_WIDGET_HAS_FOCUS(widget)) {
entry->in_click = TRUE;
gtk_widget_grab_focus(widget);
entry->in_click = FALSE;
}
tmp_pos =
gtk_secure_entry_find_position(entry,
event->x + entry->scroll_offset);
if (event->button == 1) {
switch (event->type) {
case GDK_BUTTON_PRESS:
gtk_secure_entry_set_positions(entry, tmp_pos, tmp_pos);
break;
default:
break;
}
return TRUE;
}
else if (event->button == 2) {
switch (event->type) {
case GDK_BUTTON_PRESS:
entry->insert_pos = tmp_pos;
gtk_secure_entry_paste (entry, GDK_SELECTION_PRIMARY);
return TRUE;
default:
break;
}
}
return FALSE;
}
static gint
gtk_secure_entry_button_release(GtkWidget * widget, GdkEventButton * event)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
if (event->window != entry->text_area
|| entry->button != event->button)
return FALSE;
entry->button = 0;
return TRUE;
}
static gint
gtk_secure_entry_motion_notify(GtkWidget * widget, GdkEventMotion * event)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
gint tmp_pos;
if (entry->mouse_cursor_obscured) {
GdkCursor *cursor;
cursor =
gdk_cursor_new_for_display(gtk_widget_get_display(widget),
GDK_XTERM);
gdk_window_set_cursor(entry->text_area, cursor);
gdk_cursor_unref(cursor);
entry->mouse_cursor_obscured = FALSE;
}
if (event->window != entry->text_area || entry->button != 1)
return FALSE;
if (event->is_hint || (entry->text_area != event->window))
gdk_window_get_pointer(entry->text_area, NULL, NULL, NULL);
{
gint height;
gdk_drawable_get_size(entry->text_area, NULL, &height);
if (event->y < 0)
tmp_pos = 0;
else if (event->y >= height)
tmp_pos = entry->text_length;
else
tmp_pos =
gtk_secure_entry_find_position(entry,
event->x +
entry->scroll_offset);
gtk_secure_entry_set_positions(entry, tmp_pos, -1);
}
return TRUE;
}
static void
set_invisible_cursor(GdkWindow * window)
{
GdkBitmap *empty_bitmap;
GdkCursor *cursor;
GdkColor useless;
char invisible_cursor_bits[] = { 0x0 };
useless.red = useless.green = useless.blue = 0;
useless.pixel = 0;
empty_bitmap = gdk_bitmap_create_from_data(window,
invisible_cursor_bits, 1,
1);
cursor = gdk_cursor_new_from_pixmap(empty_bitmap,
empty_bitmap,
&useless, &useless, 0, 0);
gdk_window_set_cursor(window, cursor);
gdk_cursor_unref(cursor);
g_object_unref(empty_bitmap);
}
static void
gtk_secure_entry_obscure_mouse_cursor(GtkSecureEntry * entry)
{
if (entry->mouse_cursor_obscured)
return;
set_invisible_cursor(entry->text_area);
entry->mouse_cursor_obscured = TRUE;
}
static gint
gtk_secure_entry_key_press(GtkWidget * widget, GdkEventKey * event)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
gtk_secure_entry_pend_cursor_blink(entry);
if (gtk_im_context_filter_keypress(entry->im_context, event)) {
gtk_secure_entry_obscure_mouse_cursor(entry);
entry->need_im_reset = TRUE;
return TRUE;
}
if (GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event))
/* Activate key bindings
*/
return TRUE;
return FALSE;
}
static gint
gtk_secure_entry_key_release(GtkWidget * widget, GdkEventKey * event)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
if (gtk_im_context_filter_keypress(entry->im_context, event)) {
entry->need_im_reset = TRUE;
return TRUE;
}
return GTK_WIDGET_CLASS(parent_class)->key_release_event(widget,
event);
}
static gint
gtk_secure_entry_focus_in(GtkWidget * widget, GdkEventFocus * event)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+ (void)event;
+
gtk_widget_queue_draw(widget);
entry->need_im_reset = TRUE;
gtk_im_context_focus_in(entry->im_context);
g_signal_connect(gdk_keymap_get_for_display
(gtk_widget_get_display(widget)), "direction_changed",
G_CALLBACK(gtk_secure_entry_keymap_direction_changed),
entry);
gtk_secure_entry_check_cursor_blink(entry);
return FALSE;
}
static gint
gtk_secure_entry_focus_out(GtkWidget * widget, GdkEventFocus * event)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+ (void)event;
+
gtk_widget_queue_draw(widget);
entry->need_im_reset = TRUE;
gtk_im_context_focus_out(entry->im_context);
gtk_secure_entry_check_cursor_blink(entry);
g_signal_handlers_disconnect_by_func(gdk_keymap_get_for_display
(gtk_widget_get_display(widget)),
gtk_secure_entry_keymap_direction_changed,
entry);
return FALSE;
}
static void
gtk_secure_entry_grab_focus(GtkWidget * widget)
{
GtkWidget *tmp;
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
gboolean select_on_focus;
GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_DEFAULT);
GTK_WIDGET_CLASS(parent_class)->grab_focus(widget);
/* read current select on focus setting from GtkEntry */
tmp = gtk_entry_new ();
g_object_get(gtk_widget_get_settings(tmp),
"gtk-entry-select-on-focus", &select_on_focus, NULL);
gtk_widget_destroy (tmp);
if (select_on_focus && !entry->in_click)
gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
}
static void
gtk_secure_entry_direction_changed(GtkWidget * widget,
GtkTextDirection previous_dir)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
gtk_secure_entry_recompute(entry);
GTK_WIDGET_CLASS(parent_class)->direction_changed(widget,
previous_dir);
}
static void
gtk_secure_entry_state_changed(GtkWidget * widget,
GtkStateType previous_state)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+ (void)previous_state;
+
if (GTK_WIDGET_REALIZED(widget)) {
gdk_window_set_background(widget->window,
&widget->style->
base[GTK_WIDGET_STATE(widget)]);
gdk_window_set_background(entry->text_area,
&widget->style->
base[GTK_WIDGET_STATE(widget)]);
}
if (!GTK_WIDGET_IS_SENSITIVE(widget)) {
/* Clear any selection */
gtk_editable_select_region(GTK_EDITABLE(entry), entry->current_pos,
entry->current_pos);
}
gtk_widget_queue_draw(widget);
}
static void
gtk_secure_entry_screen_changed(GtkWidget * widget, GdkScreen * old_screen)
{
+ (void)old_screen;
+
gtk_secure_entry_recompute(GTK_SECURE_ENTRY(widget));
}
/* GtkEditable method implementations
*/
static void
gtk_secure_entry_insert_text(GtkEditable * editable,
const gchar * new_text,
gint new_text_length, gint * position)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
gchar *text;
if (*position < 0 || *position > entry->text_length)
*position = entry->text_length;
g_object_ref(editable);
WITH_SECURE_MEM(text = g_new(gchar, new_text_length + 1));
text[new_text_length] = '\0';
strncpy(text, new_text, new_text_length);
g_signal_emit_by_name(editable, "insert_text", text, new_text_length,
position);
WITH_SECURE_MEM(g_free(text));
g_object_unref(editable);
}
static void
gtk_secure_entry_delete_text(GtkEditable * editable,
gint start_pos, gint end_pos)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
if (end_pos < 0 || end_pos > entry->text_length)
end_pos = entry->text_length;
if (start_pos < 0)
start_pos = 0;
if (start_pos > end_pos)
start_pos = end_pos;
g_object_ref(editable);
g_signal_emit_by_name(editable, "delete_text", start_pos, end_pos);
g_object_unref(editable);
}
static void
gtk_secure_entry_set_position_internal(GtkSecureEntry * entry,
gint position, gboolean reset_im)
{
if (position < 0 || position > entry->text_length)
position = entry->text_length;
if (position != entry->current_pos ||
position != entry->selection_bound) {
if (reset_im)
gtk_secure_entry_reset_im_context(entry);
gtk_secure_entry_set_positions(entry, position, position);
}
}
static void
gtk_secure_entry_real_set_position(GtkEditable * editable, gint position)
{
gtk_secure_entry_set_position_internal(GTK_SECURE_ENTRY(editable),
position, TRUE);
}
static gint
gtk_secure_entry_get_position(GtkEditable * editable)
{
return GTK_SECURE_ENTRY(editable)->current_pos;
}
static void
gtk_secure_entry_set_selection_bounds(GtkEditable * editable,
gint start, gint end)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
if (start < 0)
start = entry->text_length;
if (end < 0)
end = entry->text_length;
gtk_secure_entry_reset_im_context(entry);
gtk_secure_entry_set_positions(entry,
MIN(end, entry->text_length),
MIN(start, entry->text_length));
}
static gboolean
gtk_secure_entry_get_selection_bounds(GtkEditable * editable,
gint * start, gint * end)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
*start = entry->selection_bound;
*end = entry->current_pos;
return (entry->selection_bound != entry->current_pos);
}
static void
gtk_secure_entry_style_set(GtkWidget * widget, GtkStyle * previous_style)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
gtk_secure_entry_recompute(entry);
if (previous_style && GTK_WIDGET_REALIZED(widget)) {
gdk_window_set_background(widget->window,
&widget->style->
base[GTK_WIDGET_STATE(widget)]);
gdk_window_set_background(entry->text_area,
&widget->style->
base[GTK_WIDGET_STATE(widget)]);
}
}
/* GtkCellEditable method implementations
*/
static void
gtk_cell_editable_secure_entry_activated(GtkSecureEntry * entry, gpointer data)
{
+ (void)data;
+
gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
}
static gboolean
gtk_cell_editable_key_press_event(GtkSecureEntry * entry,
GdkEventKey * key_event, gpointer data)
{
+ (void)data;
+
if (key_event->keyval == GDK_Escape) {
entry->editing_canceled = TRUE;
gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
return TRUE;
}
/* override focus */
if (key_event->keyval == GDK_Up || key_event->keyval == GDK_Down) {
gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
return TRUE;
}
return FALSE;
}
static void
gtk_secure_entry_start_editing(GtkCellEditable * cell_editable,
GdkEvent * event)
{
+ (void)event;
+
GTK_SECURE_ENTRY(cell_editable)->is_cell_renderer = TRUE;
g_signal_connect(cell_editable, "activate",
G_CALLBACK(gtk_cell_editable_secure_entry_activated), NULL);
g_signal_connect(cell_editable, "key_press_event",
G_CALLBACK(gtk_cell_editable_key_press_event), NULL);
}
/* Default signal handlers
*/
static void
gtk_secure_entry_real_insert_text(GtkEditable * editable,
const gchar * new_text,
gint new_text_length, gint * position)
{
gint _index;
gint n_chars;
GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
if (new_text_length < 0)
new_text_length = strlen(new_text);
n_chars = g_utf8_strlen(new_text, new_text_length);
if (entry->text_max_length > 0
&& n_chars + entry->text_length > entry->text_max_length) {
gdk_display_beep(gtk_widget_get_display(GTK_WIDGET(entry)));
n_chars = entry->text_max_length - entry->text_length;
new_text_length =
g_utf8_offset_to_pointer(new_text, n_chars) - new_text;
}
if (new_text_length + entry->n_bytes + 1 > entry->text_size) {
while (new_text_length + entry->n_bytes + 1 > entry->text_size) {
if (entry->text_size == 0)
entry->text_size = MIN_SIZE;
else {
if (2 * (guint) entry->text_size < MAX_SIZE &&
2 * (guint) entry->text_size > entry->text_size)
entry->text_size *= 2;
else {
entry->text_size = MAX_SIZE;
if (new_text_length >
(gint) entry->text_size - (gint) entry->n_bytes -
1) {
new_text_length =
(gint) entry->text_size -
(gint) entry->n_bytes - 1;
new_text_length =
g_utf8_find_prev_char(new_text,
new_text +
new_text_length + 1) -
new_text;
n_chars = g_utf8_strlen(new_text, new_text_length);
}
break;
}
}
}
WITH_SECURE_MEM(entry->text =
g_realloc(entry->text, entry->text_size));
}
_index = g_utf8_offset_to_pointer(entry->text, *position) - entry->text;
g_memmove(entry->text + _index + new_text_length, entry->text + _index,
entry->n_bytes - _index);
memcpy(entry->text + _index, new_text, new_text_length);
entry->n_bytes += new_text_length;
entry->text_length += n_chars;
/* NUL terminate for safety and convenience */
entry->text[entry->n_bytes] = '\0';
if (entry->current_pos > *position)
entry->current_pos += n_chars;
if (entry->selection_bound > *position)
entry->selection_bound += n_chars;
*position += n_chars;
gtk_secure_entry_recompute(entry);
emit_changed (entry);
g_object_notify(G_OBJECT(editable), "text");
}
static void
gtk_secure_entry_real_delete_text(GtkEditable * editable,
gint start_pos, gint end_pos)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
if (start_pos < 0)
start_pos = 0;
if (end_pos < 0 || end_pos > entry->text_length)
end_pos = entry->text_length;
if (start_pos < end_pos) {
gint start_index =
g_utf8_offset_to_pointer(entry->text, start_pos) - entry->text;
gint end_index =
g_utf8_offset_to_pointer(entry->text, end_pos) - entry->text;
gint current_pos;
gint selection_bound;
g_memmove(entry->text + start_index, entry->text + end_index,
entry->n_bytes + 1 - end_index);
entry->text_length -= (end_pos - start_pos);
entry->n_bytes -= (end_index - start_index);
current_pos = entry->current_pos;
if (current_pos > start_pos)
current_pos -= MIN(current_pos, end_pos) - start_pos;
selection_bound = entry->selection_bound;
if (selection_bound > start_pos)
selection_bound -= MIN(selection_bound, end_pos) - start_pos;
gtk_secure_entry_set_positions(entry, current_pos,
selection_bound);
gtk_secure_entry_recompute(entry);
emit_changed (entry);
g_object_notify(G_OBJECT(editable), "text");
}
}
/* Compute the X position for an offset that corresponds to the "more important
* cursor position for that offset. We use this when trying to guess to which
* end of the selection we should go to when the user hits the left or
* right arrow key.
*/
static gint
get_better_cursor_x(GtkSecureEntry * entry, gint offset)
{
GdkKeymap *keymap =
gdk_keymap_get_for_display(gtk_widget_get_display
(GTK_WIDGET(entry)));
PangoDirection keymap_direction = gdk_keymap_get_direction(keymap);
gboolean split_cursor;
PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
const gchar *text = pango_layout_get_text(layout);
gint _index = g_utf8_offset_to_pointer(text, offset) - text;
PangoRectangle strong_pos, weak_pos;
g_object_get(gtk_widget_get_settings(GTK_WIDGET(entry)),
"gtk-split-cursor", &split_cursor, NULL);
pango_layout_get_cursor_pos(layout, _index, &strong_pos, &weak_pos);
if (split_cursor)
return strong_pos.x / PANGO_SCALE;
else
return (keymap_direction ==
entry->resolved_dir) ? strong_pos.x /
PANGO_SCALE : weak_pos.x / PANGO_SCALE;
}
static void
gtk_secure_entry_move_cursor(GtkSecureEntry * entry,
GtkMovementStep step,
gint count, gboolean extend_selection)
{
gint new_pos = entry->current_pos;
gtk_secure_entry_reset_im_context(entry);
if (entry->current_pos != entry->selection_bound && !extend_selection) {
/* If we have a current selection and aren't extending it, move to the
* start/or end of the selection as appropriate
*/
switch (step) {
case GTK_MOVEMENT_VISUAL_POSITIONS:
{
gint current_x =
get_better_cursor_x(entry, entry->current_pos);
gint bound_x =
get_better_cursor_x(entry, entry->selection_bound);
if (count < 0)
new_pos =
current_x <
bound_x ? entry->current_pos : entry->
selection_bound;
else
new_pos =
current_x >
bound_x ? entry->current_pos : entry->
selection_bound;
break;
}
case GTK_MOVEMENT_LOGICAL_POSITIONS:
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
case GTK_MOVEMENT_PARAGRAPH_ENDS:
case GTK_MOVEMENT_BUFFER_ENDS:
new_pos = count < 0 ? 0 : entry->text_length;
break;
case GTK_MOVEMENT_WORDS:
case GTK_MOVEMENT_DISPLAY_LINES:
case GTK_MOVEMENT_PARAGRAPHS:
case GTK_MOVEMENT_PAGES:
case GTK_MOVEMENT_HORIZONTAL_PAGES:
break;
}
} else {
switch (step) {
case GTK_MOVEMENT_LOGICAL_POSITIONS:
new_pos =
gtk_secure_entry_move_logically(entry, new_pos, count);
break;
case GTK_MOVEMENT_VISUAL_POSITIONS:
new_pos =
gtk_secure_entry_move_visually(entry, new_pos, count);
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
case GTK_MOVEMENT_PARAGRAPH_ENDS:
case GTK_MOVEMENT_BUFFER_ENDS:
new_pos = count < 0 ? 0 : entry->text_length;
break;
case GTK_MOVEMENT_WORDS:
case GTK_MOVEMENT_DISPLAY_LINES:
case GTK_MOVEMENT_PARAGRAPHS:
case GTK_MOVEMENT_PAGES:
case GTK_MOVEMENT_HORIZONTAL_PAGES:
break;
}
}
if (extend_selection)
gtk_editable_select_region(GTK_EDITABLE(entry),
entry->selection_bound, new_pos);
else
gtk_editable_set_position(GTK_EDITABLE(entry), new_pos);
gtk_secure_entry_pend_cursor_blink(entry);
}
static void
gtk_secure_entry_insert_at_cursor(GtkSecureEntry * entry,
const gchar * str)
{
GtkEditable *editable = GTK_EDITABLE(entry);
gint pos = entry->current_pos;
gtk_secure_entry_reset_im_context(entry);
gtk_editable_insert_text(editable, str, -1, &pos);
gtk_editable_set_position(editable, pos);
}
static void
gtk_secure_entry_delete_from_cursor(GtkSecureEntry * entry,
GtkDeleteType type, gint count)
{
GtkEditable *editable = GTK_EDITABLE(entry);
gint start_pos = entry->current_pos;
gint end_pos = entry->current_pos;
gtk_secure_entry_reset_im_context(entry);
if (entry->selection_bound != entry->current_pos) {
gtk_editable_delete_selection(editable);
return;
}
switch (type) {
case GTK_DELETE_CHARS:
end_pos =
gtk_secure_entry_move_logically(entry, entry->current_pos,
count);
gtk_editable_delete_text(editable, MIN(start_pos, end_pos),
MAX(start_pos, end_pos));
break;
case GTK_DELETE_DISPLAY_LINE_ENDS:
case GTK_DELETE_PARAGRAPH_ENDS:
if (count < 0)
gtk_editable_delete_text(editable, 0, entry->current_pos);
else
gtk_editable_delete_text(editable, entry->current_pos, -1);
break;
case GTK_DELETE_DISPLAY_LINES:
case GTK_DELETE_PARAGRAPHS:
gtk_editable_delete_text(editable, 0, -1);
break;
default:
break;
}
gtk_secure_entry_pend_cursor_blink(entry);
}
static void
begin_change (GtkSecureEntry *entry)
{
entry->change_count++;
}
static void
end_change (GtkSecureEntry *entry)
{
GtkEditable *editable = GTK_EDITABLE (entry);
g_return_if_fail (entry->change_count > 0);
entry->change_count--;
if (entry->change_count == 0)
{
if (entry->real_changed)
{
g_signal_emit_by_name (editable, "changed");
entry->real_changed = FALSE;
}
}
}
static void
emit_changed (GtkSecureEntry *entry)
{
GtkEditable *editable = GTK_EDITABLE (entry);
if (entry->change_count == 0)
g_signal_emit_by_name (editable, "changed");
else
entry->real_changed = TRUE;
}
static void
paste_received (GtkClipboard *clipboard,
const gchar *text,
gpointer data)
{
GtkSecureEntry *entry = GTK_SECURE_ENTRY (data);
GtkEditable *editable = GTK_EDITABLE (entry);
+ (void)clipboard;
+
if (entry->button == 2)
{
gint pos, start, end;
pos = entry->insert_pos;
gtk_editable_get_selection_bounds (editable, &start, &end);
if (!((start <= pos && pos <= end) || (end <= pos && pos <= start)))
gtk_editable_select_region (editable, pos, pos);
}
if (text)
{
gint pos, start, end;
gint length = -1;
begin_change (entry);
g_object_freeze_notify (G_OBJECT (entry));
if (gtk_editable_get_selection_bounds (editable, &start, &end))
gtk_editable_delete_text (editable, start, end);
pos = entry->current_pos;
gtk_editable_insert_text (editable, text, length, &pos);
gtk_editable_set_position (editable, pos);
g_object_thaw_notify (G_OBJECT (entry));
end_change (entry);
}
g_object_unref (entry);
}
static void
gtk_secure_entry_paste (GtkSecureEntry *entry, GdkAtom selection)
{
g_object_ref (entry);
gtk_clipboard_request_text (gtk_widget_get_clipboard (GTK_WIDGET (entry),
selection),
paste_received, entry);
}
static void
gtk_secure_entry_paste_clipboard (GtkSecureEntry *entry)
{
gtk_secure_entry_paste (entry, GDK_SELECTION_CLIPBOARD);
}
/* static void */
/* gtk_secure_entry_delete_cb(GtkSecureEntry * entry) */
/* { */
/* GtkEditable *editable = GTK_EDITABLE(entry); */
/* gint start, end; */
/* if (gtk_editable_get_selection_bounds(editable, &start, &end)) */
/* gtk_editable_delete_text(editable, start, end); */
/* } */
/* static void */
/* gtk_secure_entry_toggle_overwrite(GtkSecureEntry * entry) */
/* { */
/* entry->overwrite_mode = !entry->overwrite_mode; */
/* } */
static void
gtk_secure_entry_real_activate(GtkSecureEntry * entry)
{
GtkWindow *window;
GtkWidget *toplevel;
GtkWidget *widget;
widget = GTK_WIDGET(entry);
if (entry->activates_default) {
toplevel = gtk_widget_get_toplevel(widget);
if (GTK_IS_WINDOW(toplevel)) {
window = GTK_WINDOW(toplevel);
if (window &&
widget != window->default_widget &&
!(widget == window->focus_widget &&
(!window->default_widget
|| !GTK_WIDGET_SENSITIVE(window->default_widget))))
gtk_window_activate_default(window);
}
}
}
static void
gtk_secure_entry_keymap_direction_changed(GdkKeymap * keymap,
GtkSecureEntry * entry)
{
- gtk_secure_entry_recompute(entry);
+ (void)keymap;
+
+ gtk_secure_entry_recompute(entry);
}
/* IM Context Callbacks
*/
static void
gtk_secure_entry_commit_cb(GtkIMContext * context,
const gchar * str, GtkSecureEntry * entry)
{
- gtk_secure_entry_enter_text(entry, str);
+ (void)context;
+
+ gtk_secure_entry_enter_text(entry, str);
}
static void
gtk_secure_entry_preedit_changed_cb(GtkIMContext * context,
GtkSecureEntry * entry)
{
gchar *preedit_string;
gint cursor_pos;
+ (void)context;
+
gtk_im_context_get_preedit_string(entry->im_context,
&preedit_string, NULL, &cursor_pos);
entry->preedit_length = strlen(preedit_string);
cursor_pos = CLAMP(cursor_pos, 0, g_utf8_strlen(preedit_string, -1));
entry->preedit_cursor = cursor_pos;
g_free(preedit_string);
gtk_secure_entry_recompute(entry);
}
static gboolean
gtk_secure_entry_retrieve_surrounding_cb(GtkIMContext * context,
GtkSecureEntry * entry)
{
gtk_im_context_set_surrounding(context,
entry->text,
entry->n_bytes,
g_utf8_offset_to_pointer(entry->text,
entry->
current_pos) -
entry->text);
return TRUE;
}
static gboolean
gtk_secure_entry_delete_surrounding_cb(GtkIMContext * slave,
gint offset,
gint n_chars,
GtkSecureEntry * entry)
{
+ (void)slave;
+
gtk_editable_delete_text(GTK_EDITABLE(entry),
entry->current_pos + offset,
entry->current_pos + offset + n_chars);
return TRUE;
}
/* Internal functions
*/
/* Used for im_commit_cb and inserting Unicode chars */
static void
gtk_secure_entry_enter_text(GtkSecureEntry * entry, const gchar * str)
{
GtkEditable *editable = GTK_EDITABLE(entry);
gint tmp_pos;
if (gtk_editable_get_selection_bounds(editable, NULL, NULL))
gtk_editable_delete_selection(editable);
else {
if (entry->overwrite_mode)
gtk_secure_entry_delete_from_cursor(entry, GTK_DELETE_CHARS,
1);
}
tmp_pos = entry->current_pos;
gtk_editable_insert_text(editable, str, strlen(str), &tmp_pos);
gtk_secure_entry_set_position_internal(entry, tmp_pos, FALSE);
}
/* All changes to entry->current_pos and entry->selection_bound
* should go through this function.
*/
static void
gtk_secure_entry_set_positions(GtkSecureEntry * entry,
gint current_pos, gint selection_bound)
{
gboolean changed = FALSE;
g_object_freeze_notify(G_OBJECT(entry));
if (current_pos != -1 && entry->current_pos != current_pos) {
entry->current_pos = current_pos;
changed = TRUE;
g_object_notify(G_OBJECT(entry), "cursor_position");
}
if (selection_bound != -1 && entry->selection_bound != selection_bound) {
entry->selection_bound = selection_bound;
changed = TRUE;
g_object_notify(G_OBJECT(entry), "selection_bound");
}
g_object_thaw_notify(G_OBJECT(entry));
if (changed)
gtk_secure_entry_recompute(entry);
}
static void
gtk_secure_entry_reset_layout(GtkSecureEntry * entry)
{
if (entry->cached_layout) {
g_object_unref(entry->cached_layout);
entry->cached_layout = NULL;
}
}
static void
update_im_cursor_location(GtkSecureEntry * entry)
{
GdkRectangle area;
gint strong_x;
gint strong_xoffset;
gint area_width, area_height;
gtk_secure_entry_get_cursor_locations(entry, &strong_x, NULL);
get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
strong_xoffset = strong_x - entry->scroll_offset;
if (strong_xoffset < 0) {
strong_xoffset = 0;
} else if (strong_xoffset > area_width) {
strong_xoffset = area_width;
}
area.x = strong_xoffset;
area.y = 0;
area.width = 0;
area.height = area_height;
gtk_im_context_set_cursor_location(entry->im_context, &area);
}
static gboolean
recompute_idle_func(gpointer data)
{
GtkSecureEntry *entry;
GDK_THREADS_ENTER();
entry = GTK_SECURE_ENTRY(data);
entry->recompute_idle = 0;
if (gtk_widget_has_screen(GTK_WIDGET(entry))) {
gtk_secure_entry_adjust_scroll(entry);
gtk_secure_entry_queue_draw(entry);
update_im_cursor_location(entry);
}
GDK_THREADS_LEAVE();
return FALSE;
}
static void
gtk_secure_entry_recompute(GtkSecureEntry * entry)
{
gtk_secure_entry_reset_layout(entry);
gtk_secure_entry_check_cursor_blink(entry);
if (!entry->recompute_idle) {
entry->recompute_idle = g_idle_add_full(G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
recompute_idle_func, entry,
NULL);
}
}
static void
append_char(GString * str, gunichar ch, gint count)
{
gint i;
gint char_len;
gchar buf[7];
char_len = g_unichar_to_utf8(ch, buf);
i = 0;
while (i < count) {
g_string_append_len(str, buf, char_len);
++i;
}
}
static PangoLayout *
gtk_secure_entry_create_layout(GtkSecureEntry * entry,
gboolean include_preedit)
{
GtkWidget *widget = GTK_WIDGET(entry);
PangoLayout *layout = gtk_widget_create_pango_layout(widget, NULL);
PangoAttrList *tmp_attrs = pango_attr_list_new();
gchar *preedit_string = NULL;
gint preedit_length = 0;
PangoAttrList *preedit_attrs = NULL;
pango_layout_set_single_paragraph_mode(layout, TRUE);
if (include_preedit) {
gtk_im_context_get_preedit_string(entry->im_context,
&preedit_string, &preedit_attrs,
NULL);
preedit_length = entry->preedit_length;
}
if (preedit_length) {
GString *tmp_string = g_string_new(NULL);
gint cursor_index = g_utf8_offset_to_pointer(entry->text,
entry->current_pos) -
entry->text;
gint ch_len;
gint preedit_len_chars;
gunichar invisible_char;
ch_len = g_utf8_strlen(entry->text, entry->n_bytes);
preedit_len_chars = g_utf8_strlen(preedit_string, -1);
ch_len += preedit_len_chars;
if (entry->invisible_char != 0)
invisible_char = entry->invisible_char;
else
invisible_char = ' '; /* just pick a char */
append_char(tmp_string, invisible_char, ch_len);
/* Fix cursor index to point to invisible char corresponding
* to the preedit, fix preedit_length to be the length of
* the invisible chars representing the preedit
*/
cursor_index =
g_utf8_offset_to_pointer(tmp_string->str,
entry->current_pos) -
tmp_string->str;
preedit_length =
preedit_len_chars * g_unichar_to_utf8(invisible_char,
NULL);
pango_layout_set_text(layout, tmp_string->str, tmp_string->len);
pango_attr_list_splice(tmp_attrs, preedit_attrs,
cursor_index, preedit_length);
g_string_free(tmp_string, TRUE);
} else {
PangoDirection pango_dir;
pango_dir = pango_find_base_dir(entry->text, entry->n_bytes);
if (pango_dir == PANGO_DIRECTION_NEUTRAL) {
if (GTK_WIDGET_HAS_FOCUS(widget)) {
GdkDisplay *display = gtk_widget_get_display(widget);
GdkKeymap *keymap = gdk_keymap_get_for_display(display);
pango_dir = gdk_keymap_get_direction(keymap);
} else {
if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_LTR)
pango_dir = PANGO_DIRECTION_LTR;
else
pango_dir = PANGO_DIRECTION_RTL;
}
}
pango_context_set_base_dir(gtk_widget_get_pango_context(widget),
pango_dir);
pango_layout_set_alignment(layout, pango_dir);
entry->resolved_dir = pango_dir;
{
GString *str = g_string_new(NULL);
gunichar invisible_char;
if (entry->invisible_char != 0)
invisible_char = entry->invisible_char;
else
invisible_char = ' '; /* just pick a char */
append_char(str, invisible_char, entry->text_length);
pango_layout_set_text(layout, str->str, str->len);
g_string_free(str, TRUE);
}
}
pango_layout_set_attributes(layout, tmp_attrs);
if (preedit_string)
g_free(preedit_string);
if (preedit_attrs)
pango_attr_list_unref(preedit_attrs);
pango_attr_list_unref(tmp_attrs);
return layout;
}
static PangoLayout *
gtk_secure_entry_ensure_layout(GtkSecureEntry * entry,
gboolean include_preedit)
{
if (entry->preedit_length > 0 &&
!include_preedit != !entry->cache_includes_preedit)
gtk_secure_entry_reset_layout(entry);
if (!entry->cached_layout) {
entry->cached_layout =
gtk_secure_entry_create_layout(entry, include_preedit);
entry->cache_includes_preedit = include_preedit;
}
return entry->cached_layout;
}
static void
get_layout_position(GtkSecureEntry * entry, gint * x, gint * y)
{
PangoLayout *layout;
PangoRectangle logical_rect;
gint area_width, area_height;
gint y_pos;
PangoLayoutLine *line;
layout = gtk_secure_entry_ensure_layout(entry, TRUE);
get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
line = pango_layout_get_lines(layout)->data;
pango_layout_line_get_extents(line, NULL, &logical_rect);
/* Align primarily for locale's ascent/descent */
y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
entry->ascent + logical_rect.y);
/* Now see if we need to adjust to fit in actual drawn string */
if (logical_rect.height > area_height)
y_pos = (area_height - logical_rect.height) / 2;
else if (y_pos < 0)
y_pos = 0;
else if (y_pos + logical_rect.height > area_height)
y_pos = area_height - logical_rect.height;
y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
if (x)
*x = INNER_BORDER - entry->scroll_offset;
if (y)
*y = y_pos;
}
static void
gtk_secure_entry_draw_text(GtkSecureEntry * entry)
{
GtkWidget *widget;
PangoLayoutLine *line;
if (entry->invisible_char == 0)
return;
if (GTK_WIDGET_DRAWABLE(entry)) {
PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
gint x, y;
gint start_pos, end_pos;
widget = GTK_WIDGET(entry);
get_layout_position(entry, &x, &y);
gdk_draw_layout(entry->text_area,
widget->style->text_gc[widget->state], x, y,
layout);
if (gtk_editable_get_selection_bounds
(GTK_EDITABLE(entry), &start_pos, &end_pos)) {
gint *ranges;
gint n_ranges, i;
PangoRectangle logical_rect;
const gchar *text = pango_layout_get_text(layout);
gint start_index =
g_utf8_offset_to_pointer(text, start_pos) - text;
gint end_index =
g_utf8_offset_to_pointer(text, end_pos) - text;
GdkRegion *clip_region = gdk_region_new();
GdkGC *text_gc;
GdkGC *selection_gc;
line = pango_layout_get_lines(layout)->data;
pango_layout_line_get_x_ranges(line, start_index, end_index,
&ranges, &n_ranges);
pango_layout_get_extents(layout, NULL, &logical_rect);
if (GTK_WIDGET_HAS_FOCUS(entry)) {
selection_gc = widget->style->base_gc[GTK_STATE_SELECTED];
text_gc = widget->style->text_gc[GTK_STATE_SELECTED];
} else {
selection_gc = widget->style->base_gc[GTK_STATE_ACTIVE];
text_gc = widget->style->text_gc[GTK_STATE_ACTIVE];
}
for (i = 0; i < n_ranges; i++) {
GdkRectangle rect;
rect.x =
INNER_BORDER - entry->scroll_offset +
ranges[2 * i] / PANGO_SCALE;
rect.y = y;
rect.width =
(ranges[2 * i + 1] - ranges[2 * i]) / PANGO_SCALE;
rect.height = logical_rect.height / PANGO_SCALE;
gdk_draw_rectangle(entry->text_area, selection_gc, TRUE,
rect.x, rect.y, rect.width,
rect.height);
gdk_region_union_with_rect(clip_region, &rect);
}
gdk_gc_set_clip_region(text_gc, clip_region);
gdk_draw_layout(entry->text_area, text_gc, x, y, layout);
gdk_gc_set_clip_region(text_gc, NULL);
gdk_region_destroy(clip_region);
g_free(ranges);
}
}
}
static void
draw_insertion_cursor(GtkSecureEntry * entry,
GdkRectangle * cursor_location,
gboolean is_primary,
PangoDirection direction, gboolean draw_arrow)
{
GtkWidget *widget = GTK_WIDGET(entry);
GtkTextDirection text_dir;
if (direction == PANGO_DIRECTION_LTR)
text_dir = GTK_TEXT_DIR_LTR;
else
text_dir = GTK_TEXT_DIR_RTL;
gtk_draw_insertion_cursor(widget, entry->text_area, NULL,
cursor_location,
is_primary, text_dir, draw_arrow);
}
static void
gtk_secure_entry_draw_cursor(GtkSecureEntry * entry)
{
GdkKeymap *keymap =
gdk_keymap_get_for_display(gtk_widget_get_display
(GTK_WIDGET(entry)));
PangoDirection keymap_direction = gdk_keymap_get_direction(keymap);
if (GTK_WIDGET_DRAWABLE(entry)) {
GtkWidget *widget = GTK_WIDGET(entry);
GdkRectangle cursor_location;
gboolean split_cursor;
gint xoffset = INNER_BORDER - entry->scroll_offset;
gint strong_x, weak_x;
gint text_area_height;
PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
gint x1 = 0;
gint x2 = 0;
gdk_drawable_get_size(entry->text_area, NULL, &text_area_height);
gtk_secure_entry_get_cursor_locations(entry, &strong_x, &weak_x);
g_object_get(gtk_widget_get_settings(widget),
"gtk-split-cursor", &split_cursor, NULL);
dir1 = entry->resolved_dir;
if (split_cursor) {
x1 = strong_x;
if (weak_x != strong_x) {
dir2 =
(entry->resolved_dir ==
PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL :
PANGO_DIRECTION_LTR;
x2 = weak_x;
}
} else {
if (keymap_direction == entry->resolved_dir)
x1 = strong_x;
else
x1 = weak_x;
}
cursor_location.x = xoffset + x1;
cursor_location.y = INNER_BORDER;
cursor_location.width = 0;
cursor_location.height = text_area_height - 2 * INNER_BORDER;
draw_insertion_cursor(entry,
&cursor_location, TRUE, dir1,
dir2 != PANGO_DIRECTION_NEUTRAL);
if (dir2 != PANGO_DIRECTION_NEUTRAL) {
cursor_location.x = xoffset + x2;
draw_insertion_cursor(entry,
&cursor_location, FALSE, dir2, TRUE);
}
}
}
static void
gtk_secure_entry_queue_draw(GtkSecureEntry * entry)
{
if (GTK_WIDGET_REALIZED(entry))
gdk_window_invalidate_rect(entry->text_area, NULL, FALSE);
}
static void
gtk_secure_entry_reset_im_context(GtkSecureEntry * entry)
{
if (entry->need_im_reset) {
entry->need_im_reset = 0;
gtk_im_context_reset(entry->im_context);
}
}
static gint
gtk_secure_entry_find_position(GtkSecureEntry * entry, gint x)
{
PangoLayout *layout;
PangoLayoutLine *line;
gint _index;
gint pos;
gboolean trailing;
const gchar *text;
gint cursor_index;
layout = gtk_secure_entry_ensure_layout(entry, TRUE);
text = pango_layout_get_text(layout);
cursor_index =
g_utf8_offset_to_pointer(text, entry->current_pos) - text;
line = pango_layout_get_lines(layout)->data;
pango_layout_line_x_to_index(line, x * PANGO_SCALE, &_index, &trailing);
if (_index >= cursor_index && entry->preedit_length) {
if (_index >= cursor_index + entry->preedit_length)
_index -= entry->preedit_length;
else {
_index = cursor_index;
trailing = 0;
}
}
pos = g_utf8_pointer_to_offset(text, text + _index);
pos += trailing;
return pos;
}
static void
gtk_secure_entry_get_cursor_locations(GtkSecureEntry * entry,
gint * strong_x, gint * weak_x)
{
if (!entry->invisible_char) {
if (strong_x)
*strong_x = 0;
if (weak_x)
*weak_x = 0;
} else {
PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, TRUE);
const gchar *text = pango_layout_get_text(layout);
PangoRectangle strong_pos, weak_pos;
gint _index;
_index =
g_utf8_offset_to_pointer(text,
entry->current_pos +
entry->preedit_cursor) - text;
pango_layout_get_cursor_pos(layout, _index, &strong_pos, &weak_pos);
if (strong_x)
*strong_x = strong_pos.x / PANGO_SCALE;
if (weak_x)
*weak_x = weak_pos.x / PANGO_SCALE;
}
}
static void
gtk_secure_entry_adjust_scroll(GtkSecureEntry * entry)
{
gint min_offset, max_offset;
gint text_area_width, text_width;
gint strong_x, weak_x;
gint strong_xoffset, weak_xoffset;
PangoLayout *layout;
PangoLayoutLine *line;
PangoRectangle logical_rect;
if (!GTK_WIDGET_REALIZED(entry))
return;
gdk_drawable_get_size(entry->text_area, &text_area_width, NULL);
text_area_width -= 2 * INNER_BORDER;
layout = gtk_secure_entry_ensure_layout(entry, TRUE);
line = pango_layout_get_lines(layout)->data;
pango_layout_line_get_extents(line, NULL, &logical_rect);
/* Display as much text as we can */
text_width = PANGO_PIXELS(logical_rect.width);
if (text_width > text_area_width) {
min_offset = 0;
max_offset = text_width - text_area_width;
} else {
min_offset = 0;
max_offset = min_offset;
}
entry->scroll_offset =
CLAMP(entry->scroll_offset, min_offset, max_offset);
/* And make sure cursors are on screen. Note that the cursor is
* actually drawn one pixel into the INNER_BORDER space on
* the right, when the scroll is at the utmost right. This
* looks better to to me than confining the cursor inside the
* border entirely, though it means that the cursor gets one
* pixel closer to the the edge of the widget on the right than
* on the left. This might need changing if one changed
* INNER_BORDER from 2 to 1, as one would do on a
* small-screen-real-estate display.
*
* We always make sure that the strong cursor is on screen, and
* put the weak cursor on screen if possible.
*/
gtk_secure_entry_get_cursor_locations(entry, &strong_x, &weak_x);
strong_xoffset = strong_x - entry->scroll_offset;
if (strong_xoffset < 0) {
entry->scroll_offset += strong_xoffset;
strong_xoffset = 0;
} else if (strong_xoffset > text_area_width) {
entry->scroll_offset += strong_xoffset - text_area_width;
strong_xoffset = text_area_width;
}
weak_xoffset = weak_x - entry->scroll_offset;
if (weak_xoffset < 0
&& strong_xoffset - weak_xoffset <= text_area_width) {
entry->scroll_offset += weak_xoffset;
} else if (weak_xoffset > text_area_width &&
strong_xoffset - (weak_xoffset - text_area_width) >= 0) {
entry->scroll_offset += weak_xoffset - text_area_width;
}
g_object_notify(G_OBJECT(entry), "scroll_offset");
}
static gint
gtk_secure_entry_move_visually(GtkSecureEntry * entry,
gint start, gint count)
{
gint _index;
PangoLayout *layout = gtk_secure_entry_ensure_layout(entry, FALSE);
const gchar *text;
text = pango_layout_get_text(layout);
_index = g_utf8_offset_to_pointer(text, start) - text;
while (count != 0) {
int new_index, new_trailing;
gboolean split_cursor;
gboolean strong;
g_object_get(gtk_widget_get_settings(GTK_WIDGET(entry)),
"gtk-split-cursor", &split_cursor, NULL);
if (split_cursor)
strong = TRUE;
else {
GdkKeymap *keymap =
gdk_keymap_get_for_display(gtk_widget_get_display
(GTK_WIDGET(entry)));
PangoDirection keymap_direction =
gdk_keymap_get_direction(keymap);
strong = keymap_direction == entry->resolved_dir;
}
if (count > 0) {
pango_layout_move_cursor_visually(layout, strong, _index, 0, 1,
&new_index, &new_trailing);
count--;
} else {
pango_layout_move_cursor_visually(layout, strong, _index, 0, -1,
&new_index, &new_trailing);
count++;
}
if (new_index < 0 || new_index == G_MAXINT)
break;
_index = new_index;
while (new_trailing--)
_index = g_utf8_next_char(text + new_index) - text;
}
return g_utf8_pointer_to_offset(text, text + _index);
}
static gint
gtk_secure_entry_move_logically(GtkSecureEntry * entry,
gint start, gint count)
{
gint new_pos = start;
/* Prevent any leak of information */
new_pos = CLAMP(start + count, 0, entry->text_length);
return new_pos;
}
/* Public API
*/
GtkWidget *
gtk_secure_entry_new(void)
{
return g_object_new(GTK_TYPE_SECURE_ENTRY, NULL);
}
/**
* gtk_secure_entry_new_with_max_length:
* @max: the maximum length of the entry, or 0 for no maximum.
* (other than the maximum length of entries.) The value passed in will
* be clamped to the range 0-65536.
*
* Creates a new #GtkSecureEntry widget with the given maximum length.
*
* Note: the existence of this function is inconsistent
* with the rest of the GTK+ API. The normal setup would
* be to just require the user to make an extra call
* to gtk_secure_entry_set_max_length() instead. It is not
* expected that this function will be removed, but
* it would be better practice not to use it.
*
* Return value: a new #GtkSecureEntry.
**/
GtkWidget *
gtk_secure_entry_new_with_max_length(gint max)
{
GtkSecureEntry *entry;
max = CLAMP(max, 0, MAX_SIZE);
entry = g_object_new(GTK_TYPE_SECURE_ENTRY, NULL);
entry->text_max_length = max;
return GTK_WIDGET(entry);
}
void
gtk_secure_entry_set_text(GtkSecureEntry * entry, const gchar * text)
{
gint tmp_pos;
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
g_return_if_fail(text != NULL);
/* Actually setting the text will affect the cursor and selection;
* if the contents don't actually change, this will look odd to the user.
*/
if (strcmp(entry->text, text) == 0)
return;
gtk_editable_delete_text(GTK_EDITABLE(entry), 0, -1);
tmp_pos = 0;
gtk_editable_insert_text(GTK_EDITABLE(entry), text, strlen(text),
&tmp_pos);
}
void
gtk_secure_entry_append_text(GtkSecureEntry * entry, const gchar * text)
{
gint tmp_pos;
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
g_return_if_fail(text != NULL);
tmp_pos = entry->text_length;
gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &tmp_pos);
}
void
gtk_secure_entry_prepend_text(GtkSecureEntry * entry, const gchar * text)
{
gint tmp_pos;
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
g_return_if_fail(text != NULL);
tmp_pos = 0;
gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &tmp_pos);
}
void
gtk_secure_entry_set_position(GtkSecureEntry * entry, gint position)
{
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
gtk_editable_set_position(GTK_EDITABLE(entry), position);
}
/**
* gtk_secure_entry_set_invisible_char:
* @entry: a #GtkSecureEntry
* @ch: a Unicode character
*
* Sets the character to use in place of the actual text when
* gtk_secure_entry_set_visibility() has been called to set text visibility
* to %FALSE. i.e. this is the character used in "password mode" to
* show the user how many characters have been typed. The default
* invisible char is an asterisk ('*'). If you set the invisible char
* to 0, then the user will get no feedback at all; there will be
* no text on the screen as they type.
*
**/
void
gtk_secure_entry_set_invisible_char(GtkSecureEntry * entry, gunichar ch)
{
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
if (ch == entry->invisible_char)
return;
entry->invisible_char = ch;
g_object_notify(G_OBJECT(entry), "invisible_char");
gtk_secure_entry_recompute(entry);
}
/**
* gtk_secure_entry_get_invisible_char:
* @entry: a #GtkSecureEntry
*
* Retrieves the character displayed in place of the real characters
* for entries with visisbility set to false. See gtk_secure_entry_set_invisible_char().
*
* Return value: the current invisible char, or 0, if the entry does not
* show invisible text at all.
**/
gunichar
gtk_secure_entry_get_invisible_char(GtkSecureEntry * entry)
{
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
return entry->invisible_char;
}
/**
* gtk_secure_entry_get_text:
* @entry: a #GtkSecureEntry
*
* Retrieves the contents of the entry widget.
* See also gtk_editable_get_chars().
*
* Return value: a pointer to the contents of the widget as a
* string. This string points to internally allocated
* storage in the widget and must not be freed, modified or
* stored.
**/
G_CONST_RETURN gchar *
gtk_secure_entry_get_text(GtkSecureEntry * entry)
{
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), NULL);
return entry->text;
}
void
gtk_secure_entry_select_region(GtkSecureEntry * entry,
gint start, gint end)
{
gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
}
/**
* gtk_secure_entry_set_max_length:
* @entry: a #GtkSecureEntry.
* @max: the maximum length of the entry, or 0 for no maximum.
* (other than the maximum length of entries.) The value passed in will
* be clamped to the range 0-65536.
*
* Sets the maximum allowed length of the contents of the widget. If
* the current contents are longer than the given length, then they
* will be truncated to fit.
**/
void
gtk_secure_entry_set_max_length(GtkSecureEntry * entry, gint max)
{
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
max = CLAMP(max, 0, MAX_SIZE);
if (max > 0 && entry->text_length > max)
gtk_editable_delete_text(GTK_EDITABLE(entry), max, -1);
entry->text_max_length = max;
g_object_notify(G_OBJECT(entry), "max_length");
}
/**
* gtk_secure_entry_get_max_length:
* @entry: a #GtkSecureEntry
*
* Retrieves the maximum allowed length of the text in
* @entry. See gtk_secure_entry_set_max_length().
*
* Return value: the maximum allowed number of characters
* in #GtkSecureEntry, or 0 if there is no maximum.
**/
gint
gtk_secure_entry_get_max_length(GtkSecureEntry * entry)
{
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
return entry->text_max_length;
}
/**
* gtk_secure_entry_set_activates_default:
* @entry: a #GtkSecureEntry
* @setting: %TRUE to activate window's default widget on Enter keypress
*
* If @setting is %TRUE, pressing Enter in the @entry will activate the default
* widget for the window containing the entry. This usually means that
* the dialog box containing the entry will be closed, since the default
* widget is usually one of the dialog buttons.
*
* (For experts: if @setting is %TRUE, the entry calls
* gtk_window_activate_default() on the window containing the entry, in
* the default handler for the "activate" signal.)
*
**/
void
gtk_secure_entry_set_activates_default(GtkSecureEntry * entry,
gboolean setting)
{
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
setting = setting != FALSE;
if (setting != entry->activates_default) {
entry->activates_default = setting;
g_object_notify(G_OBJECT(entry), "activates_default");
}
}
/**
* gtk_secure_entry_get_activates_default:
* @entry: a #GtkSecureEntry
*
* Retrieves the value set by gtk_secure_entry_set_activates_default().
*
* Return value: %TRUE if the entry will activate the default widget
**/
gboolean
gtk_secure_entry_get_activates_default(GtkSecureEntry * entry)
{
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), FALSE);
return entry->activates_default;
}
/**
* gtk_secure_entry_set_width_chars:
* @entry: a #GtkSecureEntry
* @n_chars: width in chars
*
* Changes the size request of the entry to be about the right size
* for @n_chars characters. Note that it changes the size
* <emphasis>request</emphasis>, the size can still be affected by
* how you pack the widget into containers. If @n_chars is -1, the
* size reverts to the default entry size.
*
**/
void
gtk_secure_entry_set_width_chars(GtkSecureEntry * entry, gint n_chars)
{
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
if (entry->width_chars != n_chars) {
entry->width_chars = n_chars;
g_object_notify(G_OBJECT(entry), "width_chars");
gtk_widget_queue_resize(GTK_WIDGET(entry));
}
}
/**
* gtk_secure_entry_get_width_chars:
* @entry: a #GtkSecureEntry
*
* Gets the value set by gtk_secure_entry_set_width_chars().
*
* Return value: number of chars to request space for, or negative if unset
**/
gint
gtk_secure_entry_get_width_chars(GtkSecureEntry * entry)
{
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
return entry->width_chars;
}
/**
* gtk_secure_entry_set_has_frame:
* @entry: a #GtkSecureEntry
* @setting: new value
*
* Sets whether the entry has a beveled frame around it.
**/
void
gtk_secure_entry_set_has_frame(GtkSecureEntry * entry, gboolean setting)
{
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
setting = (setting != FALSE);
if (entry->has_frame == setting)
return;
gtk_widget_queue_resize(GTK_WIDGET(entry));
entry->has_frame = setting;
g_object_notify(G_OBJECT(entry), "has_frame");
}
/**
* gtk_secure_entry_get_has_frame:
* @entry: a #GtkSecureEntry
*
* Gets the value set by gtk_secure_entry_set_has_frame().
*
* Return value: whether the entry has a beveled frame
**/
gboolean
gtk_secure_entry_get_has_frame(GtkSecureEntry * entry)
{
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), FALSE);
return entry->has_frame;
}
/**
* gtk_secure_entry_get_layout:
* @entry: a #GtkSecureEntry
*
* Gets the #PangoLayout used to display the entry.
* The layout is useful to e.g. convert text positions to
* pixel positions, in combination with gtk_secure_entry_get_layout_offsets().
* The returned layout is owned by the entry so need not be
* freed by the caller.
*
* Keep in mind that the layout text may contain a preedit string, so
* gtk_secure_entry_layout_index_to_text_index() and
* gtk_secure_entry_text_index_to_layout_index() are needed to convert byte
* indices in the layout to byte indices in the entry contents.
*
* Return value: the #PangoLayout for this entry
**/
PangoLayout *
gtk_secure_entry_get_layout(GtkSecureEntry * entry)
{
PangoLayout *layout;
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), NULL);
layout = gtk_secure_entry_ensure_layout(entry, TRUE);
return layout;
}
/**
* gtk_secure_entry_layout_index_to_text_index:
* @entry: a #GtkSecureEntry
* @layout_index: byte index into the entry layout text
*
* Converts from a position in the entry contents (returned
* by gtk_secure_entry_get_text()) to a position in the
* entry's #PangoLayout (returned by gtk_secure_entry_get_layout(),
* with text retrieved via pango_layout_get_text()).
*
* Return value: byte index into the entry contents
**/
gint
gtk_secure_entry_layout_index_to_text_index(GtkSecureEntry * entry,
gint layout_index)
{
PangoLayout *layout;
const gchar *text;
gint cursor_index;
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
layout = gtk_secure_entry_ensure_layout(entry, TRUE);
text = pango_layout_get_text(layout);
cursor_index =
g_utf8_offset_to_pointer(text, entry->current_pos) - text;
if (layout_index >= cursor_index && entry->preedit_length) {
if (layout_index >= cursor_index + entry->preedit_length)
layout_index -= entry->preedit_length;
else
layout_index = cursor_index;
}
return layout_index;
}
/**
* gtk_secure_entry_text_index_to_layout_index:
* @entry: a #GtkSecureEntry
* @text_index: byte index into the entry contents
*
* Converts from a position in the entry's #PangoLayout(returned by
* gtk_secure_entry_get_layout()) to a position in the entry contents
* (returned by gtk_secure_entry_get_text()).
*
* Return value: byte index into the entry layout text
**/
gint
gtk_secure_entry_text_index_to_layout_index(GtkSecureEntry * entry,
gint text_index)
{
PangoLayout *layout;
const gchar *text;
gint cursor_index;
g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
layout = gtk_secure_entry_ensure_layout(entry, TRUE);
text = pango_layout_get_text(layout);
cursor_index =
g_utf8_offset_to_pointer(text, entry->current_pos) - text;
if (text_index > cursor_index)
text_index += entry->preedit_length;
return text_index;
}
/**
* gtk_secure_entry_get_layout_offsets:
* @entry: a #GtkSecureEntry
* @x: location to store X offset of layout, or %NULL
* @y: location to store Y offset of layout, or %NULL
*
*
* Obtains the position of the #PangoLayout used to render text
* in the entry, in widget coordinates. Useful if you want to line
* up the text in an entry with some other text, e.g. when using the
* entry to implement editable cells in a sheet widget.
*
* Also useful to convert mouse events into coordinates inside the
* #PangoLayout, e.g. to take some action if some part of the entry text
* is clicked.
*
* Note that as the user scrolls around in the entry the offsets will
* change; you'll need to connect to the "notify::scroll_offset"
* signal to track this. Remember when using the #PangoLayout
* functions you need to convert to and from pixels using
* PANGO_PIXELS() or #PANGO_SCALE.
*
* Keep in mind that the layout text may contain a preedit string, so
* gtk_secure_entry_layout_index_to_text_index() and
* gtk_secure_entry_text_index_to_layout_index() are needed to convert byte
* indices in the layout to byte indices in the entry contents.
*
**/
void
gtk_secure_entry_get_layout_offsets(GtkSecureEntry * entry,
gint * x, gint * y)
{
gint text_area_x, text_area_y;
g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
/* this gets coords relative to text area */
get_layout_position(entry, x, y);
/* convert to widget coords */
get_text_area_size(entry, &text_area_x, &text_area_y, NULL, NULL);
if (x)
*x += text_area_x;
if (y)
*y += text_area_y;
}
/* Quick hack of a popup menu
*/
/* static void */
/* activate_cb(GtkWidget * menuitem, GtkSecureEntry * entry) */
/* { */
/* const gchar *asignal = */
/* g_object_get_data(G_OBJECT(menuitem), "gtk-signal"); */
/* g_signal_emit_by_name(entry, asignal); */
/* } */
static gboolean
gtk_secure_entry_mnemonic_activate(GtkWidget * widget,
gboolean group_cycling)
{
+ (void)group_cycling;
+
gtk_widget_grab_focus(widget);
return TRUE;
}
/* static void */
/* unichar_chosen_func(const char *text, gpointer data) */
/* { */
/* GtkSecureEntry *entry = GTK_SECURE_ENTRY(data); */
/* gtk_secure_entry_enter_text(entry, text); */
/* } */
/* We display the cursor when
*
* - the selection is empty, AND
* - the widget has focus
*/
#define CURSOR_ON_MULTIPLIER 0.66
#define CURSOR_OFF_MULTIPLIER 0.34
#define CURSOR_PEND_MULTIPLIER 1.0
static gboolean
cursor_blinks(GtkSecureEntry * entry)
{
GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(entry));
gboolean blink;
if (GTK_WIDGET_HAS_FOCUS(entry) &&
entry->selection_bound == entry->current_pos) {
g_object_get(settings, "gtk-cursor-blink", &blink, NULL);
return blink;
} else
return FALSE;
}
static gint
get_cursor_time(GtkSecureEntry * entry)
{
GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(entry));
gint blinktime;
g_object_get(settings, "gtk-cursor-blink-time", &blinktime, NULL);
return blinktime;
}
static void
show_cursor(GtkSecureEntry * entry)
{
if (!entry->cursor_visible) {
entry->cursor_visible = TRUE;
if (GTK_WIDGET_HAS_FOCUS(entry)
&& entry->selection_bound == entry->current_pos)
gtk_widget_queue_draw(GTK_WIDGET(entry));
}
}
static void
hide_cursor(GtkSecureEntry * entry)
{
if (entry->cursor_visible) {
entry->cursor_visible = FALSE;
if (GTK_WIDGET_HAS_FOCUS(entry)
&& entry->selection_bound == entry->current_pos)
gtk_widget_queue_draw(GTK_WIDGET(entry));
}
}
/*
* Blink!
*/
static gint
blink_cb(gpointer data)
{
GtkSecureEntry *entry;
GDK_THREADS_ENTER();
entry = GTK_SECURE_ENTRY(data);
if (!GTK_WIDGET_HAS_FOCUS(entry)) {
g_warning
("GtkSecureEntry - did not receive focus-out-event. If you\n"
"connect a handler to this signal, it must return\n"
"FALSE so the entry gets the event as well");
}
g_assert(GTK_WIDGET_HAS_FOCUS(entry));
g_assert(entry->selection_bound == entry->current_pos);
if (entry->cursor_visible) {
hide_cursor(entry);
entry->blink_timeout =
g_timeout_add(get_cursor_time(entry) * CURSOR_OFF_MULTIPLIER,
blink_cb, entry);
} else {
show_cursor(entry);
entry->blink_timeout =
g_timeout_add(get_cursor_time(entry) * CURSOR_ON_MULTIPLIER,
blink_cb, entry);
}
GDK_THREADS_LEAVE();
/* Remove ourselves */
return FALSE;
}
static void
gtk_secure_entry_check_cursor_blink(GtkSecureEntry * entry)
{
if (cursor_blinks(entry)) {
if (!entry->blink_timeout) {
entry->blink_timeout =
g_timeout_add(get_cursor_time(entry) *
CURSOR_ON_MULTIPLIER, blink_cb, entry);
show_cursor(entry);
}
} else {
if (entry->blink_timeout) {
g_source_remove(entry->blink_timeout);
entry->blink_timeout = 0;
}
entry->cursor_visible = TRUE;
}
}
static void
gtk_secure_entry_pend_cursor_blink(GtkSecureEntry * entry)
{
if (cursor_blinks(entry)) {
if (entry->blink_timeout != 0)
g_source_remove(entry->blink_timeout);
entry->blink_timeout =
g_timeout_add(get_cursor_time(entry) * CURSOR_PEND_MULTIPLIER,
blink_cb, entry);
show_cursor(entry);
}
}
static inline gboolean
keyval_is_cursor_move(guint keyval)
{
if (keyval == GDK_Up || keyval == GDK_KP_Up)
return TRUE;
if (keyval == GDK_Down || keyval == GDK_KP_Down)
return TRUE;
if (keyval == GDK_Page_Up)
return TRUE;
if (keyval == GDK_Page_Down)
return TRUE;
return FALSE;
}
/* stolen from gtkmarshalers.c */
#define g_marshal_value_peek_enum(v) (v)->data[0].v_int
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
/* VOID:ENUM,INT,BOOLEAN (gtkmarshalers.list:64) */
static void
_gtk_marshal_VOID__ENUM_INT_BOOLEAN(GClosure * closure,
GValue * return_value,
guint n_param_values,
const GValue * param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
typedef void (*GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (gpointer data1,
gint arg_1,
gint arg_2,
gboolean arg_3,
gpointer data2);
register GMarshalFunc_VOID__ENUM_INT_BOOLEAN callback;
register GCClosure *cc = (GCClosure *) closure;
register gpointer data1, data2;
+ (void)return_value;
+ (void)invocation_hint;
+
g_return_if_fail(n_param_values == 4);
if (G_CCLOSURE_SWAP_DATA(closure)) {
data1 = closure->data;
data2 = g_value_peek_pointer(param_values + 0);
} else {
data1 = g_value_peek_pointer(param_values + 0);
data2 = closure->data;
}
callback =
(GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (marshal_data ? marshal_data
: cc->callback);
callback(data1,
g_marshal_value_peek_enum(param_values + 1),
g_marshal_value_peek_int(param_values + 2),
g_marshal_value_peek_boolean(param_values + 3), data2);
}
static void
_gtk_marshal_VOID__ENUM_INT(GClosure * closure,
GValue * return_value,
guint n_param_values,
const GValue * param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1,
gint arg_1,
gint arg_2,
gpointer data2);
register GMarshalFunc_VOID__ENUM_INT callback;
register GCClosure *cc = (GCClosure *) closure;
register gpointer data1, data2;
+ (void)return_value;
+ (void)invocation_hint;
+
g_return_if_fail(n_param_values == 3);
if (G_CCLOSURE_SWAP_DATA(closure)) {
data1 = closure->data;
data2 = g_value_peek_pointer(param_values + 0);
} else {
data1 = g_value_peek_pointer(param_values + 0);
data2 = closure->data;
}
callback =
(GMarshalFunc_VOID__ENUM_INT) (marshal_data ? marshal_data : cc->
callback);
callback(data1,
g_marshal_value_peek_enum(param_values + 1),
g_marshal_value_peek_int(param_values + 2), data2);
}
diff --git a/gtk+-2/pinentry-gtk-2.c b/gtk+-2/pinentry-gtk-2.c
index 1a88e5a..1666698 100644
--- a/gtk+-2/pinentry-gtk-2.c
+++ b/gtk+-2/pinentry-gtk-2.c
@@ -1,737 +1,760 @@
/* pinentry-gtk-2.c
Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at>
Copyright (C) 2001, 2002, 2007, 2015 g10 Code GmbH
Copyright (C) 2004 by Albrecht Dre゚ <albrecht.dress@arcor.de>
pinentry-gtk-2 is a pinentry application for the Gtk+-2 widget set.
It tries to follow the Gnome Human Interface Guide as close as
possible.
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gdk/gdkkeysyms.h>
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7 )
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstrict-prototypes"
#endif
#include <gtk/gtk.h>
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7 )
# pragma GCC diagnostic pop
#endif
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "getopt.h"
#endif /* HAVE_GETOPT_H */
#include "gtksecentry.h"
#include "pinentry.h"
#ifdef FALLBACK_CURSES
#include "pinentry-curses.h"
#endif
#define PGMNAME "pinentry-gtk2"
#ifndef VERSION
# define VERSION
#endif
static pinentry_t pinentry;
static int grab_failed;
static int passphrase_ok;
typedef enum { CONFIRM_CANCEL, CONFIRM_OK, CONFIRM_NOTOK } confirm_value_t;
static confirm_value_t confirm_value;
static GtkWidget *entry;
static GtkWidget *repeat_entry;
static GtkWidget *error_label;
static GtkWidget *qualitybar;
static GtkTooltips *tooltips;
static gboolean got_input;
static guint timeout_source;
/* Gnome hig small and large space in pixels. */
#define HIG_SMALL 6
#define HIG_LARGE 12
/* The text shown in the quality bar when no text is shown. This is not
* the empty string, because with an empty string the height of
* the quality bar is less than with a non-empty string. This results
* in ugly layout changes when the text changes from non-empty to empty
* and vice versa. */
#define QUALITYBAR_EMPTY_TEXT " "
/* Constrain size of the window the window should not shrink beyond
the requisition, and should not grow vertically. */
static void
constrain_size (GtkWidget *win, GtkRequisition *req, gpointer data)
{
static gint width, height;
GdkGeometry geo;
+ (void)data;
+
if (req->width == width && req->height == height)
return;
width = req->width;
height = req->height;
geo.min_width = width;
/* This limit is arbitrary, but INT_MAX breaks other things */
geo.max_width = 10000;
geo.min_height = geo.max_height = height;
gtk_window_set_geometry_hints (GTK_WINDOW (win), NULL, &geo,
GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
}
/* Realize the window as transient if we grab the keyboard. This
makes the window a modal dialog to the root window, which helps the
window manager. See the following quote from:
http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2512420
Implementing enhanced support for application transient windows
If the WM_TRANSIENT_FOR property is set to None or Root window, the
window should be treated as a transient for all other windows in
the same group. It has been noted that this is a slight ICCCM
violation, but as this behavior is pretty standard for many
toolkits and window managers, and is extremely unlikely to break
anything, it seems reasonable to document it as standard. */
static void
make_transient (GtkWidget *win, GdkEvent *event, gpointer data)
{
GdkScreen *screen;
GdkWindow *root;
+ (void)event;
+ (void)data;
+
if (! pinentry->grab)
return;
/* Make window transient for the root window. */
screen = gdk_screen_get_default ();
root = gdk_screen_get_root_window (screen);
gdk_window_set_transient_for (win->window, root);
}
/* Grab the keyboard for maximum security */
static int
grab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data)
{
+ (void)data;
+
if (! pinentry->grab)
return FALSE;
if (gdk_keyboard_grab (win->window, FALSE, gdk_event_get_time (event)))
{
g_critical ("could not grab keyboard");
grab_failed = 1;
gtk_main_quit ();
}
return FALSE;
}
/* Remove grab. */
static int
ungrab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data)
{
+ (void)data;
+
gdk_keyboard_ungrab (gdk_event_get_time (event));
/* Unmake window transient for the root window. */
/* gdk_window_set_transient_for cannot be used with parent = NULL to
unset transient hint (unlike gtk_ version which can). Replacement
code is taken from gtk_window_transient_parent_unrealized. */
gdk_property_delete (win->window,
gdk_atom_intern_static_string ("WM_TRANSIENT_FOR"));
return FALSE;
}
static int
delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
+ (void)widget;
+ (void)event;
+ (void)data;
+
pinentry->close_button = 1;
gtk_main_quit ();
return TRUE;
}
static void
button_clicked (GtkWidget *widget, gpointer data)
{
+ (void)widget;
+
if (data)
{
const char *s, *s2;
/* Okay button or enter used in text field. */
s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (entry));
if (!s)
s = "";
if (pinentry->repeat_passphrase && repeat_entry)
{
s2 = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (repeat_entry));
if (!s2)
s2 = "";
if (strcmp (s, s2))
{
gtk_label_set_text (GTK_LABEL (error_label),
pinentry->repeat_error_string?
pinentry->repeat_error_string:
"not correctly repeated");
gtk_widget_grab_focus (entry);
return; /* again */
}
pinentry->repeat_okay = 1;
}
passphrase_ok = 1;
pinentry_setbufferlen (pinentry, strlen (s) + 1);
if (pinentry->pin)
strcpy (pinentry->pin, s);
}
gtk_main_quit ();
}
static void
enter_callback (GtkWidget *widget, GtkWidget *anentry)
{
+ (void)anentry;
+
button_clicked (widget, (gpointer) CONFIRM_OK);
}
static void
confirm_button_clicked (GtkWidget *widget, gpointer data)
{
+ (void)widget;
+
confirm_value = (confirm_value_t) data;
gtk_main_quit ();
}
static void
cancel_callback (GtkAccelGroup *acc, GObject *accelerable,
guint keyval, GdkModifierType modifier, gpointer data)
{
int confirm_mode = !!data;
+ (void)acc;
+ (void)keyval;
+ (void)modifier;
+
if (confirm_mode)
confirm_button_clicked (GTK_WIDGET (accelerable),
(gpointer)CONFIRM_CANCEL);
else
button_clicked (GTK_WIDGET (accelerable),
(gpointer)CONFIRM_CANCEL);
}
static gchar *
pinentry_utf8_validate (gchar *text)
{
gchar *result;
if (!text)
return NULL;
if (g_utf8_validate (text, -1, NULL))
return g_strdup (text);
/* Failure: Assume that it was encoded in the current locale and
convert it to utf-8. */
result = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
if (!result)
{
gchar *p;
result = p = g_strdup (text);
while (!g_utf8_validate (p, -1, (const gchar **) &p))
*p = '?';
}
return result;
}
/* Handler called for "changed". We use it to update the quality
indicator. */
static void
changed_text_handler (GtkWidget *widget)
{
char textbuf[50];
const char *s;
int length;
int percent;
GdkColor color = { 0, 0, 0, 0};
got_input = TRUE;
if (pinentry->repeat_passphrase && repeat_entry)
{
gtk_secure_entry_set_text (GTK_SECURE_ENTRY (repeat_entry), "");
gtk_label_set_text (GTK_LABEL (error_label), "");
}
if (!qualitybar || !pinentry->quality_bar)
return;
s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (widget));
if (!s)
s = "";
length = strlen (s);
percent = length? pinentry_inq_quality (pinentry, s, length) : 0;
if (!length)
{
strcpy(textbuf, QUALITYBAR_EMPTY_TEXT);
color.red = 0xffff;
}
else if (percent < 0)
{
snprintf (textbuf, sizeof textbuf, "(%d%%)", -percent);
color.red = 0xffff;
percent = -percent;
}
else
{
snprintf (textbuf, sizeof textbuf, "%d%%", percent);
color.green = 0xffff;
}
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar),
(double)percent/100.0);
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar), textbuf);
gtk_widget_modify_bg (qualitybar, GTK_STATE_PRELIGHT, &color);
}
#ifdef HAVE_LIBSECRET
static void
may_save_passphrase_toggled (GtkWidget *widget, gpointer data)
{
GtkToggleButton *button = GTK_TOGGLE_BUTTON (widget);
pinentry_t ctx = (pinentry_t) data;
ctx->may_cache_password = gtk_toggle_button_get_active (button);
}
#endif
static gboolean
timeout_cb (gpointer data)
{
(void)data;
if (!got_input)
gtk_main_quit ();
/* Don't run again. */
timeout_source = 0;
return FALSE;
}
static GtkWidget *
create_window (pinentry_t ctx, int confirm_mode)
{
GtkWidget *w;
GtkWidget *win, *box;
GtkWidget *wvbox, *chbox, *bbox;
GtkAccelGroup *acc;
GClosure *acc_cl;
gchar *msg;
tooltips = gtk_tooltips_new ();
/* FIXME: check the grabbing code against the one we used with the
old gpg-agent */
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
acc = gtk_accel_group_new ();
g_signal_connect (G_OBJECT (win), "delete_event",
G_CALLBACK (delete_event), NULL);
#if 0
g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (gtk_main_quit),
NULL);
#endif
g_signal_connect (G_OBJECT (win), "size-request",
G_CALLBACK (constrain_size), NULL);
if (!confirm_mode)
{
if (pinentry->grab)
g_signal_connect (G_OBJECT (win),
"realize", G_CALLBACK (make_transient), NULL);
/* We need to grab the keyboard when its visible! not when its
mapped (there is a difference) */
g_object_set (G_OBJECT(win), "events",
GDK_VISIBILITY_NOTIFY_MASK | GDK_STRUCTURE_MASK, NULL);
g_signal_connect (G_OBJECT (win),
pinentry->grab
? "visibility-notify-event"
: "focus-in-event",
G_CALLBACK (grab_keyboard), NULL);
g_signal_connect (G_OBJECT (win),
pinentry->grab ? "unmap-event" : "focus-out-event",
G_CALLBACK (ungrab_keyboard), NULL);
}
gtk_window_add_accel_group (GTK_WINDOW (win), acc);
wvbox = gtk_vbox_new (FALSE, HIG_LARGE * 2);
gtk_container_add (GTK_CONTAINER (win), wvbox);
gtk_container_set_border_width (GTK_CONTAINER (wvbox), HIG_LARGE);
chbox = gtk_hbox_new (FALSE, HIG_LARGE);
gtk_box_pack_start (GTK_BOX (wvbox), chbox, FALSE, FALSE, 0);
w = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
GTK_ICON_SIZE_DIALOG);
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0);
gtk_box_pack_start (GTK_BOX (chbox), w, FALSE, FALSE, 0);
box = gtk_vbox_new (FALSE, HIG_SMALL);
gtk_box_pack_start (GTK_BOX (chbox), box, TRUE, TRUE, 0);
if (pinentry->title)
{
msg = pinentry_utf8_validate (pinentry->title);
gtk_window_set_title (GTK_WINDOW(win), msg);
}
if (pinentry->description)
{
msg = pinentry_utf8_validate (pinentry->description);
w = gtk_label_new (msg);
g_free (msg);
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
}
if (!confirm_mode && (pinentry->error || pinentry->repeat_passphrase))
{
/* With the repeat passphrase option we need to create the label
in any case so that it may later be updated by the error
message. */
GdkColor color = { 0, 0xffff, 0, 0 };
if (pinentry->error)
msg = pinentry_utf8_validate (pinentry->error);
else
msg = "";
error_label = gtk_label_new (msg);
if (pinentry->error)
g_free (msg);
gtk_misc_set_alignment (GTK_MISC (error_label), 0.0, 0.5);
gtk_label_set_line_wrap (GTK_LABEL (error_label), TRUE);
gtk_box_pack_start (GTK_BOX (box), error_label, TRUE, FALSE, 0);
gtk_widget_modify_fg (error_label, GTK_STATE_NORMAL, &color);
}
qualitybar = NULL;
if (!confirm_mode)
{
int nrow;
GtkWidget* table;
nrow = 1;
if (pinentry->quality_bar)
nrow++;
if (pinentry->repeat_passphrase)
nrow++;
table = gtk_table_new (nrow, 2, FALSE);
nrow = 0;
gtk_box_pack_start (GTK_BOX (box), table, FALSE, FALSE, 0);
if (pinentry->prompt)
{
msg = pinentry_utf8_validate (pinentry->prompt);
w = gtk_label_new_with_mnemonic (msg);
g_free (msg);
gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1,
GTK_FILL, GTK_FILL, 4, 0);
}
entry = gtk_secure_entry_new ();
gtk_widget_set_size_request (entry, 200, -1);
g_signal_connect (G_OBJECT (entry), "activate",
G_CALLBACK (enter_callback), entry);
g_signal_connect (G_OBJECT (entry), "changed",
G_CALLBACK (changed_text_handler), entry);
gtk_table_attach (GTK_TABLE (table), entry, 1, 2, nrow, nrow+1,
GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
gtk_widget_grab_focus (entry);
gtk_widget_show (entry);
nrow++;
if (pinentry->quality_bar)
{
msg = pinentry_utf8_validate (pinentry->quality_bar);
w = gtk_label_new (msg);
g_free (msg);
gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1,
GTK_FILL, GTK_FILL, 4, 0);
qualitybar = gtk_progress_bar_new();
gtk_widget_add_events (qualitybar,
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar),
QUALITYBAR_EMPTY_TEXT);
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar), 0.0);
if (pinentry->quality_bar_tt)
gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips), qualitybar,
pinentry->quality_bar_tt, "");
gtk_table_attach (GTK_TABLE (table), qualitybar, 1, 2, nrow, nrow+1,
GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
nrow++;
}
if (pinentry->repeat_passphrase)
{
msg = pinentry_utf8_validate (pinentry->repeat_passphrase);
w = gtk_label_new (msg);
g_free (msg);
gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1,
GTK_FILL, GTK_FILL, 4, 0);
repeat_entry = gtk_secure_entry_new ();
gtk_widget_set_size_request (repeat_entry, 200, -1);
g_signal_connect (G_OBJECT (entry), "activate",
G_CALLBACK (enter_callback), repeat_entry);
gtk_table_attach (GTK_TABLE (table), repeat_entry, 1, 2, nrow, nrow+1,
GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
gtk_widget_grab_focus (entry);
gtk_widget_show (entry);
nrow++;
}
}
bbox = gtk_hbutton_box_new ();
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
gtk_box_set_spacing (GTK_BOX (bbox), 6);
gtk_box_pack_start (GTK_BOX (wvbox), bbox, TRUE, FALSE, 0);
#ifdef HAVE_LIBSECRET
if (ctx->allow_external_password_cache && ctx->keyinfo)
/* Only show this if we can cache passwords and we have a stable
key identifier. */
{
if (pinentry->default_pwmngr)
{
msg = pinentry_utf8_validate (pinentry->default_pwmngr);
w = gtk_check_button_new_with_mnemonic (msg);
g_free (msg);
}
else
w = gtk_check_button_new_with_label ("Save passphrase using libsecret");
/* Make sure it is off by default. */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE);
gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
gtk_widget_show (w);
g_signal_connect (G_OBJECT (w), "toggled",
G_CALLBACK (may_save_passphrase_toggled),
(gpointer) ctx);
}
#endif
if (!pinentry->one_button)
{
if (pinentry->cancel)
{
msg = pinentry_utf8_validate (pinentry->cancel);
w = gtk_button_new_with_mnemonic (msg);
g_free (msg);
}
else if (pinentry->default_cancel)
{
GtkWidget *image;
msg = pinentry_utf8_validate (pinentry->default_cancel);
w = gtk_button_new_with_mnemonic (msg);
g_free (msg);
image = gtk_image_new_from_stock (GTK_STOCK_CANCEL,
GTK_ICON_SIZE_BUTTON);
if (image)
gtk_button_set_image (GTK_BUTTON (w), image);
}
else
w = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
gtk_container_add (GTK_CONTAINER (bbox), w);
g_signal_connect (G_OBJECT (w), "clicked",
G_CALLBACK (confirm_mode ? confirm_button_clicked
: button_clicked),
(gpointer) CONFIRM_CANCEL);
acc_cl = g_cclosure_new (G_CALLBACK (cancel_callback),
(confirm_mode? "":NULL), NULL);
gtk_accel_group_connect (acc, GDK_KEY_Escape, 0, 0, acc_cl);
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
}
if (confirm_mode && !pinentry->one_button && pinentry->notok)
{
msg = pinentry_utf8_validate (pinentry->notok);
w = gtk_button_new_with_mnemonic (msg);
g_free (msg);
gtk_container_add (GTK_CONTAINER (bbox), w);
g_signal_connect (G_OBJECT (w), "clicked",
G_CALLBACK (confirm_button_clicked),
(gpointer) CONFIRM_NOTOK);
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
}
if (pinentry->ok)
{
msg = pinentry_utf8_validate (pinentry->ok);
w = gtk_button_new_with_mnemonic (msg);
g_free (msg);
}
else if (pinentry->default_ok)
{
GtkWidget *image;
msg = pinentry_utf8_validate (pinentry->default_ok);
w = gtk_button_new_with_mnemonic (msg);
g_free (msg);
image = gtk_image_new_from_stock (GTK_STOCK_OK,
GTK_ICON_SIZE_BUTTON);
if (image)
gtk_button_set_image (GTK_BUTTON (w), image);
}
else
w = gtk_button_new_from_stock (GTK_STOCK_OK);
gtk_container_add (GTK_CONTAINER(bbox), w);
if (!confirm_mode)
{
g_signal_connect (G_OBJECT (w), "clicked",
G_CALLBACK (button_clicked), "ok");
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
gtk_widget_grab_default (w);
g_signal_connect_object (G_OBJECT (entry), "focus_in_event",
G_CALLBACK (gtk_widget_grab_default),
G_OBJECT (w), 0);
}
else
{
g_signal_connect (G_OBJECT (w), "clicked",
G_CALLBACK(confirm_button_clicked),
(gpointer) CONFIRM_OK);
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
}
gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);
gtk_window_set_keep_above (GTK_WINDOW (win), TRUE);
gtk_widget_show_all (win);
gtk_window_present (GTK_WINDOW (win)); /* Make sure it has the focus. */
if (pinentry->timeout > 0)
timeout_source = g_timeout_add (pinentry->timeout*1000, timeout_cb, NULL);
return win;
}
static int
gtk_cmd_handler (pinentry_t pe)
{
GtkWidget *w;
int want_pass = !!pe->pin;
got_input = FALSE;
pinentry = pe;
confirm_value = CONFIRM_CANCEL;
passphrase_ok = 0;
w = create_window (pe, want_pass ? 0 : 1);
gtk_main ();
gtk_widget_destroy (w);
while (gtk_events_pending ())
gtk_main_iteration ();
if (timeout_source)
/* There is a timer running. Cancel it. */
{
g_source_remove (timeout_source);
timeout_source = 0;
}
if (confirm_value == CONFIRM_CANCEL || grab_failed)
pe->canceled = 1;
pinentry = NULL;
if (want_pass)
{
if (passphrase_ok && pe->pin)
return strlen (pe->pin);
else
return -1;
}
else
return (confirm_value == CONFIRM_OK) ? 1 : 0;
}
pinentry_cmd_handler_t pinentry_cmd_handler = gtk_cmd_handler;
int
main (int argc, char *argv[])
{
static GMemVTable secure_mem =
{
secentry_malloc,
secentry_realloc,
secentry_free,
NULL,
NULL,
NULL
};
g_mem_set_vtable (&secure_mem);
pinentry_init (PGMNAME);
#ifdef FALLBACK_CURSES
if (pinentry_have_display (argc, argv))
{
if (! gtk_init_check (&argc, &argv))
pinentry_cmd_handler = curses_cmd_handler;
}
else
pinentry_cmd_handler = curses_cmd_handler;
#else
gtk_init (&argc, &argv);
#endif
pinentry_parse_opts (argc, argv);
if (pinentry_loop ())
return 1;
return 0;
}
diff --git a/pinentry/pinentry.c b/pinentry/pinentry.c
index 9a6a090..a0737ad 100644
--- a/pinentry/pinentry.c
+++ b/pinentry/pinentry.c
@@ -1,1447 +1,1485 @@
/* pinentry.c - The PIN entry support library
Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015 g10 Code GmbH
This file is part of PINENTRY.
PINENTRY 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 2 of the License, or
(at your option) any later version.
PINENTRY 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 this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef HAVE_W32CE_SYSTEM
# include <errno.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#ifndef HAVE_W32CE_SYSTEM
# include <locale.h>
#endif
#ifdef HAVE_LANGINFO_H
#include <langinfo.h>
#endif
#include <limits.h>
#ifdef HAVE_W32CE_SYSTEM
# include <windows.h>
#endif
#if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK
#include <iconv.h>
#endif
#include "assuan.h"
#include "memory.h"
#include "secmem-util.h"
#include "argparse.h"
#include "pinentry.h"
#include "password-cache.h"
#ifdef HAVE_W32CE_SYSTEM
#define getpid() GetCurrentProcessId ()
#endif
/* Keep the name of our program here. */
static char this_pgmname[50];
struct pinentry pinentry;
static void
pinentry_reset (int use_defaults)
{
/* GPG Agent sets these options once when it starts the pinentry.
Don't reset them. */
int grab = pinentry.grab;
char *ttyname = pinentry.ttyname;
char *ttytype = pinentry.ttytype;
char *lc_ctype = pinentry.lc_ctype;
char *lc_messages = pinentry.lc_messages;
int allow_external_password_cache = pinentry.allow_external_password_cache;
char *default_ok = pinentry.default_ok;
char *default_cancel = pinentry.default_cancel;
char *default_prompt = pinentry.default_prompt;
char *default_pwmngr = pinentry.default_pwmngr;
char *touch_file = pinentry.touch_file;
/* These options are set from the command line. Don't reset
them. */
int debug = pinentry.debug;
char *display = pinentry.display;
int parent_wid = pinentry.parent_wid;
pinentry_color_t color_fg = pinentry.color_fg;
int color_fg_bright = pinentry.color_fg_bright;
pinentry_color_t color_bg = pinentry.color_bg;
pinentry_color_t color_so = pinentry.color_so;
int color_so_bright = pinentry.color_so_bright;
int timout = pinentry.timeout;
/* Free any allocated memory. */
if (use_defaults)
{
free (pinentry.ttyname);
free (pinentry.ttytype);
free (pinentry.lc_ctype);
free (pinentry.lc_messages);
free (pinentry.default_ok);
free (pinentry.default_cancel);
free (pinentry.default_prompt);
free (pinentry.default_pwmngr);
free (pinentry.touch_file);
free (pinentry.display);
}
free (pinentry.title);
free (pinentry.description);
free (pinentry.error);
free (pinentry.prompt);
free (pinentry.ok);
free (pinentry.notok);
free (pinentry.cancel);
secmem_free (pinentry.pin);
free (pinentry.repeat_passphrase);
free (pinentry.repeat_error_string);
free (pinentry.quality_bar);
free (pinentry.quality_bar_tt);
free (pinentry.keyinfo);
/* Reset the pinentry structure. */
memset (&pinentry, 0, sizeof (pinentry));
if (use_defaults)
{
/* Pinentry timeout in seconds. */
pinentry.timeout = 60;
/* Global grab. */
pinentry.grab = 1;
pinentry.color_fg = PINENTRY_COLOR_DEFAULT;
pinentry.color_fg_bright = 0;
pinentry.color_bg = PINENTRY_COLOR_DEFAULT;
pinentry.color_so = PINENTRY_COLOR_DEFAULT;
pinentry.color_so_bright = 0;
}
else
/* Restore the options. */
{
pinentry.grab = grab;
pinentry.ttyname = ttyname;
pinentry.ttytype = ttytype;
pinentry.lc_ctype = lc_ctype;
pinentry.lc_messages = lc_messages;
pinentry.allow_external_password_cache = allow_external_password_cache;
pinentry.default_ok = default_ok;
pinentry.default_cancel = default_cancel;
pinentry.default_prompt = default_prompt;
pinentry.default_pwmngr = default_pwmngr;
pinentry.touch_file = touch_file;
pinentry.debug = debug;
pinentry.display = display;
pinentry.parent_wid = parent_wid;
pinentry.color_fg = color_fg;
pinentry.color_fg_bright = color_fg_bright;
pinentry.color_bg = color_bg;
pinentry.color_so = color_so;
pinentry.color_so_bright = color_so_bright;
pinentry.timeout = timout;
}
}
static void
pinentry_assuan_reset_handler (ASSUAN_CONTEXT ctx)
{
+ (void)ctx;
pinentry_reset (0);
}
+
+
static int lc_ctype_unknown_warning = 0;
#if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK
char *
pinentry_utf8_to_local (const char *lc_ctype, const char *text)
{
iconv_t cd;
const char *input = text;
size_t input_len = strlen (text) + 1;
char *output;
size_t output_len;
char *output_buf;
size_t processed;
char *old_ctype;
char *target_encoding;
/* If no locale setting could be determined, simply copy the
string. */
if (!lc_ctype)
{
if (! lc_ctype_unknown_warning)
{
fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
this_pgmname);
lc_ctype_unknown_warning = 1;
}
return strdup (text);
}
old_ctype = strdup (setlocale (LC_CTYPE, NULL));
if (!old_ctype)
return NULL;
setlocale (LC_CTYPE, lc_ctype);
target_encoding = nl_langinfo (CODESET);
if (!target_encoding)
target_encoding = "?";
setlocale (LC_CTYPE, old_ctype);
free (old_ctype);
/* This is overkill, but simplifies the iconv invocation greatly. */
output_len = input_len * MB_LEN_MAX;
output_buf = output = malloc (output_len);
if (!output)
return NULL;
cd = iconv_open (target_encoding, "UTF-8");
if (cd == (iconv_t) -1)
{
fprintf (stderr, "%s: can't convert from UTF-8 to %s: %s\n",
this_pgmname, target_encoding, strerror (errno));
free (output_buf);
return NULL;
}
processed = iconv (cd, (ICONV_CONST char **)&input, &input_len,
&output, &output_len);
iconv_close (cd);
if (processed == (size_t) -1 || input_len)
{
fprintf (stderr, "%s: error converting from UTF-8 to %s: %s\n",
this_pgmname, target_encoding, strerror (errno));
free (output_buf);
return NULL;
}
return output_buf;
}
/* Convert TEXT which is encoded according to LC_CTYPE to UTF-8. With
SECURE set to true, use secure memory for the returned buffer.
Return NULL on error. */
char *
pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure)
{
char *old_ctype;
char *source_encoding;
iconv_t cd;
const char *input = text;
size_t input_len = strlen (text) + 1;
char *output;
size_t output_len;
char *output_buf;
size_t processed;
/* If no locale setting could be determined, simply copy the
string. */
if (!lc_ctype)
{
if (! lc_ctype_unknown_warning)
{
fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
this_pgmname);
lc_ctype_unknown_warning = 1;
}
output_buf = secure? secmem_malloc (input_len) : malloc (input_len);
if (output_buf)
strcpy (output_buf, input);
return output_buf;
}
old_ctype = strdup (setlocale (LC_CTYPE, NULL));
if (!old_ctype)
return NULL;
setlocale (LC_CTYPE, lc_ctype);
source_encoding = nl_langinfo (CODESET);
setlocale (LC_CTYPE, old_ctype);
free (old_ctype);
/* This is overkill, but simplifies the iconv invocation greatly. */
output_len = input_len * MB_LEN_MAX;
output_buf = output = secure? secmem_malloc (output_len):malloc (output_len);
if (!output)
return NULL;
cd = iconv_open ("UTF-8", source_encoding);
if (cd == (iconv_t) -1)
{
fprintf (stderr, "%s: can't convert from %s to UTF-8: %s\n",
this_pgmname, source_encoding? source_encoding : "?",
strerror (errno));
if (secure)
secmem_free (output_buf);
else
free (output_buf);
return NULL;
}
processed = iconv (cd, (ICONV_CONST char **)&input, &input_len,
&output, &output_len);
iconv_close (cd);
if (processed == (size_t) -1 || input_len)
{
fprintf (stderr, "%s: error converting from %s to UTF-8: %s\n",
this_pgmname, source_encoding? source_encoding : "?",
strerror (errno));
if (secure)
secmem_free (output_buf);
else
free (output_buf);
return NULL;
}
return output_buf;
}
#endif
/* Copy TEXT or TEXTLEN to BUFFER and escape as required. Return a
pointer to the end of the new buffer. Note that BUFFER must be
large enough to keep the entire text; allocataing it 3 times of
TEXTLEN is sufficient. */
static char *
copy_and_escape (char *buffer, const void *text, size_t textlen)
{
int i;
const unsigned char *s = (unsigned char *)text;
char *p = buffer;
for (i=0; i < textlen; i++)
{
if (s[i] < ' ' || s[i] == '+')
{
snprintf (p, 4, "%%%02X", s[i]);
p += 3;
}
else if (s[i] == ' ')
*p++ = '+';
else
*p++ = s[i];
}
return p;
}
/* Run a quality inquiry for PASSPHRASE of LENGTH. (We need LENGTH
because not all backends might be able to return a proper
C-string.). Returns: A value between -100 and 100 to give an
estimate of the passphrase's quality. Negative values are use if
the caller won't even accept that passphrase. Note that we expect
just one data line which should not be escaped in any represent a
numeric signed decimal value. Extra data is currently ignored but
should not be send at all. */
int
pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length)
{
ASSUAN_CONTEXT ctx = pin->ctx_assuan;
const char prefix[] = "INQUIRE QUALITY ";
char *command;
char *line;
size_t linelen;
int gotvalue = 0;
int value = 0;
int rc;
if (!ctx)
return 0; /* Can't run the callback. */
if (length > 300)
length = 300; /* Limit so that it definitely fits into an Assuan
line. */
command = secmem_malloc (strlen (prefix) + 3*length + 1);
if (!command)
return 0;
strcpy (command, prefix);
copy_and_escape (command + strlen(command), passphrase, length);
rc = assuan_write_line (ctx, command);
secmem_free (command);
if (rc)
{
fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
return 0;
}
for (;;)
{
do
{
rc = assuan_read_line (ctx, &line, &linelen);
if (rc)
{
fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
return 0;
}
}
while (*line == '#' || !linelen);
if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
&& (!line[3] || line[3] == ' '))
break; /* END command received*/
if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
&& (!line[3] || line[3] == ' '))
break; /* CAN command received*/
if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
&& (!line[3] || line[3] == ' '))
break; /* ERR command received*/
if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
continue;
gotvalue = 1;
value = atoi (line+2);
}
if (value < -100)
value = -100;
else if (value > 100)
value = 100;
return value;
}
/* Try to make room for at least LEN bytes in the pinentry. Returns
new buffer on success and 0 on failure or when the old buffer is
sufficient. */
char *
pinentry_setbufferlen (pinentry_t pin, int len)
{
char *newp;
if (pin->pin_len)
assert (pin->pin);
else
assert (!pin->pin);
if (len < 2048)
len = 2048;
if (len <= pin->pin_len)
return NULL;
newp = secmem_realloc (pin->pin, len);
if (newp)
{
pin->pin = newp;
pin->pin_len = len;
}
else
{
secmem_free (pin->pin);
pin->pin = 0;
pin->pin_len = 0;
}
return newp;
}
static void
pinentry_setbuffer_clear (pinentry_t pin)
{
if (! pin->pin)
{
assert (pin->pin_len == 0);
return;
}
assert (pin->pin_len > 0);
secmem_free (pin->pin);
pin->pin = NULL;
pin->pin_len = 0;
}
static void
pinentry_setbuffer_init (pinentry_t pin)
{
pinentry_setbuffer_clear (pin);
pinentry_setbufferlen (pin, 0);
}
/* passphrase better be alloced with secmem_alloc. */
void
pinentry_setbuffer_use (pinentry_t pin, char *passphrase, int len)
{
if (! passphrase)
{
assert (len == 0);
pinentry_setbuffer_clear (pin);
return;
}
if (passphrase && len == 0)
len = strlen (passphrase) + 1;
if (pin->pin)
secmem_free (pin->pin);
pin->pin = passphrase;
pin->pin_len = len;
}
/* Initialize the secure memory subsystem, drop privileges and return.
Must be called early. */
void
pinentry_init (const char *pgmname)
{
/* Store away our name. */
if (strlen (pgmname) > sizeof this_pgmname - 2)
abort ();
strcpy (this_pgmname, pgmname);
/* Initialize secure memory. 1 is too small, so the default size
will be used. */
secmem_init (1);
secmem_set_flags (SECMEM_WARN);
drop_privs ();
if (atexit (secmem_term))
- /* FIXME: Could not register at-exit function, bail out. */
- ;
+ {
+ /* FIXME: Could not register at-exit function, bail out. */
+ }
assuan_set_malloc_hooks (secmem_malloc, secmem_realloc, secmem_free);
}
/* Simple test to check whether DISPLAY is set or the option --display
was given. Used to decide whether the GUI or curses should be
initialized. */
int
pinentry_have_display (int argc, char **argv)
{
#ifndef HAVE_W32CE_SYSTEM
const char *s;
s = getenv ("DISPLAY");
if (s && *s)
return 1;
#endif
for (; argc; argc--, argv++)
if (!strcmp (*argv, "--display") || !strncmp (*argv, "--display=", 10))
return 1;
return 0;
}
/* Print usage information and and provide strings for help. */
static const char *
my_strusage( int level )
{
const char *p;
switch (level)
{
case 11: p = this_pgmname; break;
case 12: p = "pinentry"; break;
case 13: p = PACKAGE_VERSION; break;
case 14: p = "Copyright (C) 2015 g10 Code GmbH"; break;
case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
case 1:
case 40:
{
static char *str;
if (!str)
{
size_t n = 50 + strlen (this_pgmname);
str = malloc (n);
if (str)
snprintf (str, n, "Usage: %s [options] (-h for help)",
this_pgmname);
}
p = str;
}
break;
case 41:
p = "Ask securely for a secret and print it to stdout.";
break;
case 42:
p = "1"; /* Flag print 40 as part of 41. */
break;
default: p = NULL; break;
}
return p;
}
char *
parse_color (char *arg, pinentry_color_t *color_p, int *bright_p)
{
static struct
{
const char *name;
pinentry_color_t color;
} colors[] = { { "none", PINENTRY_COLOR_NONE },
{ "default", PINENTRY_COLOR_DEFAULT },
{ "black", PINENTRY_COLOR_BLACK },
{ "red", PINENTRY_COLOR_RED },
{ "green", PINENTRY_COLOR_GREEN },
{ "yellow", PINENTRY_COLOR_YELLOW },
{ "blue", PINENTRY_COLOR_BLUE },
{ "magenta", PINENTRY_COLOR_MAGENTA },
{ "cyan", PINENTRY_COLOR_CYAN },
{ "white", PINENTRY_COLOR_WHITE } };
int i;
char *new_arg;
pinentry_color_t color = PINENTRY_COLOR_DEFAULT;
if (!arg)
return NULL;
new_arg = strchr (arg, ',');
if (new_arg)
new_arg++;
if (bright_p)
{
const char *bname[] = { "bright-", "bright", "bold-", "bold" };
*bright_p = 0;
for (i = 0; i < sizeof (bname) / sizeof (bname[0]); i++)
if (!strncasecmp (arg, bname[i], strlen (bname[i])))
{
*bright_p = 1;
arg += strlen (bname[i]);
}
}
for (i = 0; i < sizeof (colors) / sizeof (colors[0]); i++)
if (!strncasecmp (arg, colors[i].name, strlen (colors[i].name)))
color = colors[i].color;
*color_p = color;
return new_arg;
}
/* Parse the command line options. May exit the program if only help
or version output is requested. */
void
pinentry_parse_opts (int argc, char *argv[])
{
static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_n('d', "debug", "Turn on debugging output"),
ARGPARSE_s_s('D', "display", "|DISPLAY|Set the X display"),
ARGPARSE_s_s('T', "ttyname", "|FILE|Set the tty terminal node name"),
ARGPARSE_s_s('N', "ttytype", "|NAME|Set the tty terminal type"),
ARGPARSE_s_s('C', "lc-ctype", "|STRING|Set the tty LC_CTYPE value"),
ARGPARSE_s_s('M', "lc-messages", "|STRING|Set the tty LC_MESSAGES value"),
ARGPARSE_s_i('o', "timeout",
"|SECS|Timeout waiting for input after this many seconds"),
ARGPARSE_s_n('g', "no-global-grab",
"Grab keyboard only while window is focused"),
ARGPARSE_s_u('W', "parent-wid", "Parent window ID (for positioning)"),
ARGPARSE_s_s('c', "colors", "|STRING|Set custom colors for ncurses"),
ARGPARSE_end()
};
ARGPARSE_ARGS pargs = { &argc, &argv, 0 };
set_strusage (my_strusage);
pinentry_reset (1);
while (arg_parse (&pargs, opts))
{
switch (pargs.r_opt)
{
case 'd':
pinentry.debug = 1;
break;
case 'g':
pinentry.grab = 0;
break;
case 'D':
/* Note, this is currently not used because the GUI engine
has already been initialized when parsing these options. */
pinentry.display = strdup (pargs.r.ret_str);
if (!pinentry.display)
{
#ifndef HAVE_W32CE_SYSTEM
fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
#endif
exit (EXIT_FAILURE);
}
break;
case 'T':
pinentry.ttyname = strdup (pargs.r.ret_str);
if (!pinentry.ttyname)
{
#ifndef HAVE_W32CE_SYSTEM
fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
#endif
exit (EXIT_FAILURE);
}
break;
case 'N':
pinentry.ttytype = strdup (pargs.r.ret_str);
if (!pinentry.ttytype)
{
#ifndef HAVE_W32CE_SYSTEM
fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
#endif
exit (EXIT_FAILURE);
}
break;
case 'C':
pinentry.lc_ctype = strdup (pargs.r.ret_str);
if (!pinentry.lc_ctype)
{
#ifndef HAVE_W32CE_SYSTEM
fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
#endif
exit (EXIT_FAILURE);
}
break;
case 'M':
pinentry.lc_messages = strdup (pargs.r.ret_str);
if (!pinentry.lc_messages)
{
#ifndef HAVE_W32CE_SYSTEM
fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
#endif
exit (EXIT_FAILURE);
}
break;
case 'W':
pinentry.parent_wid = pargs.r.ret_ulong;
break;
case 'c':
{
char *tmpstr = pargs.r.ret_str;
tmpstr = parse_color (tmpstr, &pinentry.color_fg,
&pinentry.color_fg_bright);
tmpstr = parse_color (tmpstr, &pinentry.color_bg, NULL);
tmpstr = parse_color (tmpstr, &pinentry.color_so,
&pinentry.color_so_bright);
}
break;
case 'o':
pinentry.timeout = pargs.r.ret_int;
break;
default:
pargs.err = ARGPARSE_PRINT_WARNING;
break;
}
}
}
static int
option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
{
+ (void)ctx;
+
if (!strcmp (key, "no-grab") && !*value)
pinentry.grab = 0;
else if (!strcmp (key, "grab") && !*value)
pinentry.grab = 1;
else if (!strcmp (key, "debug-wait"))
{
#ifndef HAVE_W32CE_SYSTEM
fprintf (stderr, "%s: waiting for debugger - my pid is %u ...\n",
this_pgmname, (unsigned int) getpid());
sleep (*value?atoi (value):5);
fprintf (stderr, "%s: ... okay\n", this_pgmname);
#endif
}
else if (!strcmp (key, "display"))
{
if (pinentry.display)
free (pinentry.display);
pinentry.display = strdup (value);
if (!pinentry.display)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "ttyname"))
{
if (pinentry.ttyname)
free (pinentry.ttyname);
pinentry.ttyname = strdup (value);
if (!pinentry.ttyname)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "ttytype"))
{
if (pinentry.ttytype)
free (pinentry.ttytype);
pinentry.ttytype = strdup (value);
if (!pinentry.ttytype)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "lc-ctype"))
{
if (pinentry.lc_ctype)
free (pinentry.lc_ctype);
pinentry.lc_ctype = strdup (value);
if (!pinentry.lc_ctype)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "lc-messages"))
{
if (pinentry.lc_messages)
free (pinentry.lc_messages);
pinentry.lc_messages = strdup (value);
if (!pinentry.lc_messages)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "parent-wid"))
{
pinentry.parent_wid = atoi (value);
/* FIXME: Use strtol and add some error handling. */
}
else if (!strcmp (key, "touch-file"))
{
if (pinentry.touch_file)
free (pinentry.touch_file);
pinentry.touch_file = strdup (value);
if (!pinentry.touch_file)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "default-ok"))
{
pinentry.default_ok = strdup (value);
if (!pinentry.default_ok)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "default-cancel"))
{
pinentry.default_cancel = strdup (value);
if (!pinentry.default_cancel)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "default-prompt"))
{
pinentry.default_prompt = strdup (value);
if (!pinentry.default_prompt)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "default-pwmngr"))
{
pinentry.default_pwmngr = strdup (value);
if (!pinentry.default_pwmngr)
return ASSUAN_Out_Of_Core;
}
else if (!strcmp (key, "allow-external-password-cache") && !*value)
{
pinentry.allow_external_password_cache = 1;
pinentry.tried_password_cache = 0;
}
else
return ASSUAN_Invalid_Option;
return 0;
}
/* Note, that it is sufficient to allocate the target string D as
long as the source string S, i.e.: strlen(s)+1; */
static void
strcpy_escaped (char *d, const char *s)
{
while (*s)
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d++ = xtoi_2 ( s);
s += 2;
}
else
*d++ = *s++;
}
*d = 0;
}
static int
cmd_setdesc (ASSUAN_CONTEXT ctx, char *line)
{
char *newd;
- newd = malloc (strlen (line) + 1);
+ (void)ctx;
+
+ newd = malloc (strlen (line) + 1);
if (!newd)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newd, line);
if (pinentry.description)
free (pinentry.description);
pinentry.description = newd;
return 0;
}
static int
cmd_setprompt (ASSUAN_CONTEXT ctx, char *line)
{
char *newp;
- newp = malloc (strlen (line) + 1);
+ (void)ctx;
+
+ newp = malloc (strlen (line) + 1);
if (!newp)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newp, line);
if (pinentry.prompt)
free (pinentry.prompt);
pinentry.prompt = newp;
return 0;
}
/* The data provided at LINE may be used by pinentry implementations
to identify a key for caching strategies of its own. The empty
string and --clear mean that the key does not have a stable
identifier. */
static int
cmd_setkeyinfo (ASSUAN_CONTEXT ctx, char *line)
{
+ (void)ctx;
+
if (pinentry.keyinfo)
free (pinentry.keyinfo);
if (*line && strcmp(line, "--clear") != 0)
pinentry.keyinfo = strdup (line);
else
pinentry.keyinfo = NULL;
return 0;
}
static int
cmd_setrepeat (ASSUAN_CONTEXT ctx, char *line)
{
char *p;
+ (void)ctx;
+
p = malloc (strlen (line) + 1);
if (!p)
return ASSUAN_Out_Of_Core;
strcpy_escaped (p, line);
free (pinentry.repeat_passphrase);
pinentry.repeat_passphrase = p;
return 0;
}
static int
cmd_setrepeaterror (ASSUAN_CONTEXT ctx, char *line)
{
char *p;
+ (void)ctx;
+
p = malloc (strlen (line) + 1);
if (!p)
return ASSUAN_Out_Of_Core;
strcpy_escaped (p, line);
free (pinentry.repeat_error_string);
pinentry.repeat_error_string = p;
return 0;
}
static int
cmd_seterror (ASSUAN_CONTEXT ctx, char *line)
{
char *newe;
- newe = malloc (strlen (line) + 1);
+ (void)ctx;
+
+ newe = malloc (strlen (line) + 1);
if (!newe)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newe, line);
if (pinentry.error)
free (pinentry.error);
pinentry.error = newe;
return 0;
}
static int
cmd_setok (ASSUAN_CONTEXT ctx, char *line)
{
char *newo;
- newo = malloc (strlen (line) + 1);
+ (void)ctx;
+
+ newo = malloc (strlen (line) + 1);
if (!newo)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newo, line);
if (pinentry.ok)
free (pinentry.ok);
pinentry.ok = newo;
return 0;
}
static int
cmd_setnotok (ASSUAN_CONTEXT ctx, char *line)
{
char *newo;
- newo = malloc (strlen (line) + 1);
+ (void)ctx;
+
+ newo = malloc (strlen (line) + 1);
if (!newo)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newo, line);
if (pinentry.notok)
free (pinentry.notok);
pinentry.notok = newo;
return 0;
}
static int
cmd_setcancel (ASSUAN_CONTEXT ctx, char *line)
{
char *newc;
- newc = malloc (strlen (line) + 1);
+ (void)ctx;
+
+ newc = malloc (strlen (line) + 1);
if (!newc)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newc, line);
if (pinentry.cancel)
free (pinentry.cancel);
pinentry.cancel = newc;
return 0;
}
static int
cmd_settimeout (ASSUAN_CONTEXT ctx, char *line)
{
- if (line && *line)
- pinentry.timeout = atoi(line);
+ (void)ctx;
- return 0;
+ if (line && *line)
+ pinentry.timeout = atoi (line);
+
+ return 0;
}
static int
cmd_settitle (ASSUAN_CONTEXT ctx, char *line)
{
char *newt;
- newt = malloc (strlen (line) + 1);
+ (void)ctx;
+
+ newt = malloc (strlen (line) + 1);
if (!newt)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newt, line);
if (pinentry.title)
free (pinentry.title);
pinentry.title = newt;
return 0;
}
static int
cmd_setqualitybar (ASSUAN_CONTEXT ctx, char *line)
{
char *newval;
+ (void)ctx;
+
if (!*line)
line = "Quality:";
newval = malloc (strlen (line) + 1);
if (!newval)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newval, line);
if (pinentry.quality_bar)
free (pinentry.quality_bar);
pinentry.quality_bar = newval;
return 0;
}
/* Set the tooltip to be used for a quality bar. */
static int
cmd_setqualitybar_tt (ASSUAN_CONTEXT ctx, char *line)
{
char *newval;
+ (void)ctx;
+
if (*line)
{
newval = malloc (strlen (line) + 1);
if (!newval)
return ASSUAN_Out_Of_Core;
strcpy_escaped (newval, line);
}
else
newval = NULL;
if (pinentry.quality_bar_tt)
free (pinentry.quality_bar_tt);
pinentry.quality_bar_tt = newval;
return 0;
}
static int
cmd_getpin (ASSUAN_CONTEXT ctx, char *line)
{
int result;
int set_prompt = 0;
int just_read_password_from_cache = 0;
+ (void)line;
+
pinentry_setbuffer_init (&pinentry);
if (!pinentry.pin)
return ASSUAN_Out_Of_Core;
/* Try reading from the password cache. */
if (/* If repeat passphrase is set, then we don't want to read from
the cache. */
! pinentry.repeat_passphrase
/* Are we allowed to read from the cache? */
&& pinentry.allow_external_password_cache
&& pinentry.keyinfo
/* Only read from the cache if we haven't already tried it. */
&& ! pinentry.tried_password_cache
/* If the last read resulted in an error, then don't read from
the cache. */
&& ! pinentry.error)
{
char *password;
pinentry.tried_password_cache = 1;
password = password_cache_lookup (pinentry.keyinfo);
if (password)
/* There is a cached password. Try it. */
{
int len = strlen(password) + 1;
if (len > pinentry.pin_len)
len = pinentry.pin_len;
memcpy (pinentry.pin, password, len);
pinentry.pin[len] = '\0';
secmem_free (password);
pinentry.pin_from_cache = 1;
assuan_write_status (ctx, "PASSWORD_FROM_CACHE", "");
/* Result is the length of the password not including the
NUL terminator. */
result = len - 1;
just_read_password_from_cache = 1;
goto out;
}
}
/* The password was not cached (or we are not allowed to / cannot
use the cache). Prompt the user. */
pinentry.pin_from_cache = 0;
if (!pinentry.prompt)
{
pinentry.prompt = pinentry.default_prompt?pinentry.default_prompt:"PIN:";
set_prompt = 1;
}
pinentry.locale_err = 0;
pinentry.specific_err = 0;
pinentry.close_button = 0;
pinentry.repeat_okay = 0;
pinentry.one_button = 0;
pinentry.ctx_assuan = ctx;
result = (*pinentry_cmd_handler) (&pinentry);
pinentry.ctx_assuan = NULL;
if (pinentry.error)
{
free (pinentry.error);
pinentry.error = NULL;
}
if (pinentry.repeat_passphrase)
{
free (pinentry.repeat_passphrase);
pinentry.repeat_passphrase = NULL;
}
if (set_prompt)
pinentry.prompt = NULL;
pinentry.quality_bar = 0; /* Reset it after the command. */
if (pinentry.close_button)
assuan_write_status (ctx, "BUTTON_INFO", "close");
if (result < 0)
{
pinentry_setbuffer_clear (&pinentry);
if (pinentry.specific_err)
return pinentry.specific_err;
return pinentry.locale_err? ASSUAN_Locale_Problem: ASSUAN_Canceled;
}
out:
if (result)
{
if (pinentry.repeat_okay)
assuan_write_status (ctx, "PIN_REPEATED", "");
result = assuan_send_data (ctx, pinentry.pin, strlen(pinentry.pin));
if (!result)
result = assuan_send_data (ctx, NULL, 0);
if (/* GPG Agent says it's okay. */
pinentry.allow_external_password_cache && pinentry.keyinfo
/* We didn't just read it from the cache. */
&& ! just_read_password_from_cache
/* And the user said it's okay. */
&& pinentry.may_cache_password)
/* Cache the password. */
password_cache_save (pinentry.keyinfo, pinentry.pin);
}
pinentry_setbuffer_clear (&pinentry);
return result;
}
/* Note that the option --one-button is a hack to allow the use of old
pinentries while the caller is ignoring the result. Given that
options have never been used or flagged as an error the new option
is an easy way to enable the messsage mode while not requiring to
update pinentry or to have the caller test for the message
command. New applications which are free to require an updated
pinentry should use MESSAGE instead. */
static int
cmd_confirm (ASSUAN_CONTEXT ctx, char *line)
{
int result;
pinentry.one_button = !!strstr (line, "--one-button");
pinentry.quality_bar = 0;
pinentry.close_button = 0;
pinentry.locale_err = 0;
pinentry.specific_err = 0;
pinentry.canceled = 0;
pinentry_setbuffer_clear (&pinentry);
result = (*pinentry_cmd_handler) (&pinentry);
if (pinentry.error)
{
free (pinentry.error);
pinentry.error = NULL;
}
if (pinentry.close_button)
assuan_write_status (ctx, "BUTTON_INFO", "close");
if (result)
return 0;
if (pinentry.specific_err)
return pinentry.specific_err;
if (pinentry.locale_err)
return ASSUAN_Locale_Problem;
if (pinentry.one_button)
return 0;
if (pinentry.canceled)
return ASSUAN_Canceled;
return ASSUAN_Not_Confirmed;
}
static int
cmd_message (ASSUAN_CONTEXT ctx, char *line)
{
+ (void)line;
+
return cmd_confirm (ctx, "--one-button");
}
/* GETINFO <what>
Multipurpose function to return a variety of information.
Supported values for WHAT are:
version - Return the version of the program.
pid - Return the process id of the server.
*/
static int
cmd_getinfo (assuan_context_t ctx, char *line)
{
int rc;
if (!strcmp (line, "version"))
{
const char *s = VERSION;
rc = assuan_send_data (ctx, s, strlen (s));
}
else if (!strcmp (line, "pid"))
{
char numbuf[50];
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
}
else
rc = ASSUAN_Parameter_Error;
return rc;
}
/* CLEARPASSPHRASE <cacheid>
Clear the cache passphrase associated with the key identified by
cacheid.
*/
static int
cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line)
{
+ (void)ctx;
+
if (! line)
return ASSUAN_Invalid_Value;
/* Remove leading and trailing white space. */
while (*line == ' ')
line ++;
while (line[strlen (line) - 1] == ' ')
line[strlen (line) - 1] = 0;
switch (password_cache_clear (line))
{
case 1: return 0;
case 0: return ASSUAN_Invalid_Value;
default: return ASSUAN_General_Error;
}
}
/* Tell the assuan library about our commands. */
static int
register_commands (ASSUAN_CONTEXT ctx)
{
static struct
{
const char *name;
int cmd_id;
int (*handler) (ASSUAN_CONTEXT, char *line);
} table[] =
{
{ "SETDESC", 0, cmd_setdesc },
{ "SETPROMPT", 0, cmd_setprompt },
{ "SETKEYINFO", 0, cmd_setkeyinfo },
{ "SETREPEAT", 0, cmd_setrepeat },
{ "SETREPEATERROR",0, cmd_setrepeaterror },
{ "SETERROR", 0, cmd_seterror },
{ "SETOK", 0, cmd_setok },
{ "SETNOTOK", 0, cmd_setnotok },
{ "SETCANCEL", 0, cmd_setcancel },
{ "GETPIN", 0, cmd_getpin },
{ "CONFIRM", 0, cmd_confirm },
{ "MESSAGE", 0, cmd_message },
{ "SETQUALITYBAR", 0, cmd_setqualitybar },
{ "SETQUALITYBAR_TT", 0, cmd_setqualitybar_tt },
{ "GETINFO", 0, cmd_getinfo },
{ "SETTITLE", 0, cmd_settitle },
{ "SETTIMEOUT", 0, cmd_settimeout },
{ "CLEARPASSPHRASE", 0, cmd_clear_passphrase },
{ NULL }
};
int i, j, rc;
for (i = j = 0; table[i].name; i++)
{
rc = assuan_register_command (ctx,
table[i].cmd_id ? table[i].cmd_id
: (ASSUAN_CMD_USER + j++),
table[i].name, table[i].handler);
if (rc)
return rc;
}
return 0;
}
int
pinentry_loop2 (int infd, int outfd)
{
int rc;
int filedes[2];
ASSUAN_CONTEXT ctx;
/* Extra check to make sure we have dropped privs. */
#ifndef HAVE_DOSISH_SYSTEM
if (getuid() != geteuid())
abort ();
#endif
/* For now we use a simple pipe based server so that we can work
from scripts. We will later add options to run as a daemon and
wait for requests on a Unix domain socket. */
filedes[0] = infd;
filedes[1] = outfd;
rc = assuan_init_pipe_server (&ctx, filedes);
if (rc)
{
fprintf (stderr, "%s: failed to initialize the server: %s\n",
this_pgmname, assuan_strerror(rc));
return -1;
}
rc = register_commands (ctx);
if (rc)
{
fprintf (stderr, "%s: failed to the register commands with Assuan: %s\n",
this_pgmname, assuan_strerror(rc));
return -1;
}
assuan_register_option_handler (ctx, option_handler);
#if 0
assuan_set_log_stream (ctx, stderr);
#endif
assuan_register_reset_notify (ctx, pinentry_assuan_reset_handler);
for (;;)
{
rc = assuan_accept (ctx);
if (rc == -1)
break;
else if (rc)
{
fprintf (stderr, "%s: Assuan accept problem: %s\n",
this_pgmname, assuan_strerror (rc));
break;
}
rc = assuan_process (ctx);
if (rc)
{
fprintf (stderr, "%s: Assuan processing failed: %s\n",
this_pgmname, assuan_strerror (rc));
continue;
}
}
assuan_deinit_server (ctx);
return 0;
}
/* Start the pinentry event loop. The program will start to process
Assuan commands until it is finished or an error occurs. If an
error occurs, -1 is returned. Otherwise, 0 is returned. */
int
pinentry_loop (void)
{
return pinentry_loop2 (STDIN_FILENO, STDOUT_FILENO);
}
diff --git a/qt4/secstring.cpp b/qt4/secstring.cpp
index c8b9276..42207be 100644
--- a/qt4/secstring.cpp
+++ b/qt4/secstring.cpp
@@ -1,104 +1,104 @@
-/*
+/*
secstring.h - typedefs and conversion for secmem-backed std::strings.
Copyright (C) 2008 Klar舁vdalens Datakonsult AB (KDAB)
Written by Marc Mutz <marc@kdab.com>.
-
+
This program 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 2 of the
License, or (at your option) any later version.
-
+
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "secstring.h"
// the following code has been ported from QByteArray to secstring.
// The licence of the original code:
/****************************************************************************
**
** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License versions 2.0 or 3.0 as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information
** to ensure GNU General Public Licensing requirements will be met:
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
** exception, Nokia gives you certain additional rights. These rights
** are described in the Nokia Qt GPL Exception version 1.3, included in
** the file GPL_EXCEPTION.txt in this package.
**
** Qt for Windows(R) Licensees
** As a special exception, Nokia, as the sole copyright holder for Qt
** Designer, grants users of the Qt/Eclipse Integration plug-in the
** right for the Qt/Eclipse Integration to link to functionality
** provided by Qt Designer and its related libraries.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
****************************************************************************/
secstring toUtf8( const secqstring & str )
{
secstring ba;
if (const int l = str.size()) {
int rlen = l*3+1;
ba.reserve(rlen);
- uchar *cursor = (uchar*)ba.data();
+ /*uchar *cursor = (uchar*)ba.data();*/
const QChar *ch = str.data();
for (int i=0; i < l; i++) {
uint u = ch->unicode();
if (u < 0x80) {
ba.push_back( (uchar)u );
} else {
if (u < 0x0800) {
ba.push_back( 0xc0 | ((uchar) (u >> 6)) );
} else {
if (ch->isHighSurrogate() && i < l-1) {
ushort low = ch[1].unicode();
if (ch[1].isLowSurrogate()) {
++ch;
++i;
u = QChar::surrogateToUcs4(u,low);
}
}
if (u > 0xffff) {
ba.push_back( 0xf0 | ((uchar) (u >> 18)) );
ba.push_back( 0x80 | (((uchar) (u >> 12)) & 0x3f) );
} else {
ba.push_back( 0xe0 | ((uchar) (u >> 12)) );
}
ba.push_back( 0x80 | (((uchar) (u >> 6)) & 0x3f) );
}
ba.push_back( 0x80 | ((uchar) (u&0x3f)) );
}
++ch;
}
}
return ba;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:41 PM (17 h, 22 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
04/37/f133fbf08dafdf9f732b791c1a02

Event Timeline