Blame


1 07ff3c84 2023-12-17 benni /* See LICENSE file for copyright and license details. */
2 07ff3c84 2023-12-17 benni #include <ctype.h>
3 07ff3c84 2023-12-17 benni #include <libconfig.h>
4 07ff3c84 2023-12-17 benni #include <locale.h>
5 07ff3c84 2023-12-17 benni #include <signal.h>
6 07ff3c84 2023-12-17 benni #include <stdio.h>
7 07ff3c84 2023-12-17 benni #include <stdlib.h>
8 07ff3c84 2023-12-17 benni #include <string.h>
9 07ff3c84 2023-12-17 benni #include <strings.h>
10 07ff3c84 2023-12-17 benni #include <pwd.h>
11 07ff3c84 2023-12-17 benni #include <time.h>
12 07ff3c84 2023-12-17 benni #include <unistd.h>
13 07ff3c84 2023-12-17 benni
14 07ff3c84 2023-12-17 benni #include <X11/Xlib.h>
15 07ff3c84 2023-12-17 benni #include <X11/Xatom.h>
16 07ff3c84 2023-12-17 benni #include <X11/Xutil.h>
17 07ff3c84 2023-12-17 benni #ifdef XINERAMA
18 07ff3c84 2023-12-17 benni #include <X11/extensions/Xinerama.h>
19 07ff3c84 2023-12-17 benni #endif
20 07ff3c84 2023-12-17 benni #include <X11/Xft/Xft.h>
21 07ff3c84 2023-12-17 benni
22 07ff3c84 2023-12-17 benni #include "drw.h"
23 07ff3c84 2023-12-17 benni #include "util.h"
24 07ff3c84 2023-12-17 benni
25 07ff3c84 2023-12-17 benni #include "pinentry/pinentry.h"
26 07ff3c84 2023-12-17 benni #include "pinentry/memory.h"
27 07ff3c84 2023-12-17 benni
28 07ff3c84 2023-12-17 benni #define INTERSECT(x, y, w, h, r) \
29 07ff3c84 2023-12-17 benni (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \
30 07ff3c84 2023-12-17 benni && MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
31 07ff3c84 2023-12-17 benni #define LENGTH(X) (sizeof(X) / sizeof(X[0]))
32 07ff3c84 2023-12-17 benni #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
33 07ff3c84 2023-12-17 benni #define MINDESCLEN 8
34 07ff3c84 2023-12-17 benni
35 07ff3c84 2023-12-17 benni
36 07ff3c84 2023-12-17 benni enum { SchemePrompt, SchemeNormal, SchemeSelect, SchemeDesc, SchemeLast };
37 07ff3c84 2023-12-17 benni enum { WinPin, WinConfirm };
38 07ff3c84 2023-12-17 benni enum { Ok, NotOk, Cancel };
39 07ff3c84 2023-12-17 benni enum { Nothing, Yes, No };
40 07ff3c84 2023-12-17 benni
41 07ff3c84 2023-12-17 benni static int bh, mw, mh;
42 07ff3c84 2023-12-17 benni static int sel;
43 07ff3c84 2023-12-17 benni static int promptw, pdescw;
44 07ff3c84 2023-12-17 benni /* Sum of left and right padding */
45 07ff3c84 2023-12-17 benni static int lrpad;
46 07ff3c84 2023-12-17 benni static size_t cursor;
47 07ff3c84 2023-12-17 benni static int screen;
48 07ff3c84 2023-12-17 benni
49 07ff3c84 2023-12-17 benni static char* pin;
50 07ff3c84 2023-12-17 benni static int pin_len;
51 07ff3c84 2023-12-17 benni static char* pin_repeat;
52 07ff3c84 2023-12-17 benni static int pin_repeat_len;
53 07ff3c84 2023-12-17 benni static int repeat;
54 07ff3c84 2023-12-17 benni
55 07ff3c84 2023-12-17 benni static Atom clip, utf8;
56 07ff3c84 2023-12-17 benni static Display *dpy;
57 07ff3c84 2023-12-17 benni static Window root, parentwin, win;
58 07ff3c84 2023-12-17 benni static XIC xic;
59 07ff3c84 2023-12-17 benni
60 07ff3c84 2023-12-17 benni static Drw *drw;
61 07ff3c84 2023-12-17 benni static Clr *scheme[SchemeLast];
62 07ff3c84 2023-12-17 benni
63 07ff3c84 2023-12-17 benni static int timed_out;
64 07ff3c84 2023-12-17 benni static int winmode;
65 07ff3c84 2023-12-17 benni pinentry_t pinentry_info;
66 07ff3c84 2023-12-17 benni
67 07ff3c84 2023-12-17 benni #include "config.h"
68 07ff3c84 2023-12-17 benni
69 07ff3c84 2023-12-17 benni static int
70 07ff3c84 2023-12-17 benni drawitem(const char* text, Bool sel, int x, int y, int w) {
71 07ff3c84 2023-12-17 benni unsigned int i = (sel) ? SchemeSelect : SchemeNormal;
72 07ff3c84 2023-12-17 benni
73 07ff3c84 2023-12-17 benni drw_setscheme(drw, scheme[i]);
74 07ff3c84 2023-12-17 benni
75 07ff3c84 2023-12-17 benni return drw_text(drw, x, y, w, bh, lrpad / 2, text, 0);
76 07ff3c84 2023-12-17 benni }
77 07ff3c84 2023-12-17 benni
78 07ff3c84 2023-12-17 benni static void
79 07ff3c84 2023-12-17 benni grabfocus(void) {
80 07ff3c84 2023-12-17 benni Window focuswin;
81 07ff3c84 2023-12-17 benni int i, revertwin;
82 07ff3c84 2023-12-17 benni
83 07ff3c84 2023-12-17 benni for (i = 0; i < 100; ++i) {
84 07ff3c84 2023-12-17 benni XGetInputFocus(dpy, &focuswin, &revertwin);
85 07ff3c84 2023-12-17 benni if (focuswin == win) {
86 07ff3c84 2023-12-17 benni return;
87 07ff3c84 2023-12-17 benni }
88 07ff3c84 2023-12-17 benni XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
89 07ff3c84 2023-12-17 benni usleep(1000);
90 07ff3c84 2023-12-17 benni }
91 07ff3c84 2023-12-17 benni
92 07ff3c84 2023-12-17 benni die("cannot grab focus");
93 07ff3c84 2023-12-17 benni }
94 07ff3c84 2023-12-17 benni
95 07ff3c84 2023-12-17 benni static void
96 07ff3c84 2023-12-17 benni grabkeyboard(void) {
97 07ff3c84 2023-12-17 benni int i;
98 07ff3c84 2023-12-17 benni
99 07ff3c84 2023-12-17 benni if (embedded) {
100 07ff3c84 2023-12-17 benni return;
101 07ff3c84 2023-12-17 benni }
102 07ff3c84 2023-12-17 benni
103 07ff3c84 2023-12-17 benni /* Try to grab keyboard,
104 07ff3c84 2023-12-17 benni * we may have to wait for another process to ungrab */
105 07ff3c84 2023-12-17 benni for (i = 0; i < 1000; i++) {
106 07ff3c84 2023-12-17 benni if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
107 07ff3c84 2023-12-17 benni GrabModeAsync, CurrentTime) == GrabSuccess) {
108 07ff3c84 2023-12-17 benni return;
109 07ff3c84 2023-12-17 benni }
110 07ff3c84 2023-12-17 benni usleep(1000);
111 07ff3c84 2023-12-17 benni }
112 07ff3c84 2023-12-17 benni
113 07ff3c84 2023-12-17 benni die("cannot grab keyboard");
114 07ff3c84 2023-12-17 benni }
115 07ff3c84 2023-12-17 benni
116 07ff3c84 2023-12-17 benni static size_t
117 07ff3c84 2023-12-17 benni nextrune(int cursor, int inc) {
118 07ff3c84 2023-12-17 benni ssize_t n;
119 07ff3c84 2023-12-17 benni
120 07ff3c84 2023-12-17 benni /* Return location of next utf8 rune in the given direction (+1 or -1) */
121 07ff3c84 2023-12-17 benni for (n = cursor + inc;
122 07ff3c84 2023-12-17 benni n + inc >= 0 && (pin[n] & 0xc0) == 0x80;
123 07ff3c84 2023-12-17 benni n += inc);
124 07ff3c84 2023-12-17 benni
125 07ff3c84 2023-12-17 benni return n;
126 07ff3c84 2023-12-17 benni }
127 07ff3c84 2023-12-17 benni
128 07ff3c84 2023-12-17 benni static void
129 07ff3c84 2023-12-17 benni setup_pin(char* pin_ptr, int len, int reset) {
130 07ff3c84 2023-12-17 benni pin = pin_ptr;
131 07ff3c84 2023-12-17 benni pin_len = len;
132 07ff3c84 2023-12-17 benni
133 07ff3c84 2023-12-17 benni if (reset) {
134 07ff3c84 2023-12-17 benni promptw = (prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
135 07ff3c84 2023-12-17 benni cursor = 0;
136 07ff3c84 2023-12-17 benni
137 07ff3c84 2023-12-17 benni if (pin) {
138 07ff3c84 2023-12-17 benni pin[0] = '\0';
139 07ff3c84 2023-12-17 benni }
140 07ff3c84 2023-12-17 benni }
141 07ff3c84 2023-12-17 benni }
142 07ff3c84 2023-12-17 benni
143 07ff3c84 2023-12-17 benni static void
144 07ff3c84 2023-12-17 benni insert(const char *str, ssize_t n) {
145 07ff3c84 2023-12-17 benni size_t len = strlen(pin);
146 07ff3c84 2023-12-17 benni
147 07ff3c84 2023-12-17 benni // FIXME: Pinentry crashes when increasing the pin buffer the second time.
148 07ff3c84 2023-12-17 benni // Other pinentry programs has a limited passwort field length.
149 07ff3c84 2023-12-17 benni if (len + n > pin_len - 1) {
150 07ff3c84 2023-12-17 benni if (repeat) {
151 07ff3c84 2023-12-17 benni pin_repeat_len = 2 * pin_repeat_len;
152 07ff3c84 2023-12-17 benni pin_repeat = secmem_realloc(pin_repeat, pin_repeat_len);
153 07ff3c84 2023-12-17 benni setup_pin(pin_repeat, pin_repeat_len, 0);
154 07ff3c84 2023-12-17 benni if (!pin_repeat) {
155 07ff3c84 2023-12-17 benni pin_len = 0;
156 07ff3c84 2023-12-17 benni }
157 07ff3c84 2023-12-17 benni } else {
158 07ff3c84 2023-12-17 benni if (!pinentry_setbufferlen(pinentry_info, 2 * pinentry_info->pin_len)) {
159 07ff3c84 2023-12-17 benni pin_len = 0;
160 07ff3c84 2023-12-17 benni } else {
161 07ff3c84 2023-12-17 benni setup_pin(pinentry_info->pin, pinentry_info->pin_len, 0);
162 07ff3c84 2023-12-17 benni }
163 07ff3c84 2023-12-17 benni }
164 07ff3c84 2023-12-17 benni if (pin_len == 0) {
165 07ff3c84 2023-12-17 benni printf("Error: Couldn't allocate secure memory\n");
166 07ff3c84 2023-12-17 benni return;
167 07ff3c84 2023-12-17 benni }
168 07ff3c84 2023-12-17 benni }
169 07ff3c84 2023-12-17 benni
170 07ff3c84 2023-12-17 benni /* Move existing text out of the way, insert new text, and update cursor */
171 07ff3c84 2023-12-17 benni memmove(&pin[cursor + n], &pin[cursor], pin_len - cursor - MAX(n, 0));
172 07ff3c84 2023-12-17 benni
173 07ff3c84 2023-12-17 benni if (n > 0) {
174 07ff3c84 2023-12-17 benni memcpy(&pin[cursor], str, n);
175 07ff3c84 2023-12-17 benni }
176 07ff3c84 2023-12-17 benni
177 07ff3c84 2023-12-17 benni cursor += n;
178 07ff3c84 2023-12-17 benni pin[len + n] = '\0';
179 07ff3c84 2023-12-17 benni }
180 07ff3c84 2023-12-17 benni
181 07ff3c84 2023-12-17 benni static void
182 07ff3c84 2023-12-17 benni drawwin(void) {
183 07ff3c84 2023-12-17 benni unsigned int curpos;
184 07ff3c84 2023-12-17 benni int x = 0, fh = drw->fonts->h, pb, pbw = 0, i;
185 07ff3c84 2023-12-17 benni size_t asterlen = strlen(asterisk);
186 07ff3c84 2023-12-17 benni size_t pdesclen;
187 07ff3c84 2023-12-17 benni int leftinput;
188 07ff3c84 2023-12-17 benni char* censort;
189 07ff3c84 2023-12-17 benni
190 07ff3c84 2023-12-17 benni char* pprompt = (repeat) ? pinentry_info->repeat_passphrase : pinentry_info->prompt;
191 07ff3c84 2023-12-17 benni int ppromptw = (pprompt) ? TEXTW(pprompt) : 0;
192 07ff3c84 2023-12-17 benni
193 07ff3c84 2023-12-17 benni unsigned int censortl = minpwlen * TEXTW(asterisk) / strlen(asterisk);
194 07ff3c84 2023-12-17 benni unsigned int confirml = TEXTW(" YesNo ") + 3 * lrpad;
195 07ff3c84 2023-12-17 benni
196 07ff3c84 2023-12-17 benni drw_setscheme(drw, scheme[SchemeNormal]);
197 07ff3c84 2023-12-17 benni drw_rect(drw, 0, 0, mw, mh, 1, 1);
198 07ff3c84 2023-12-17 benni
199 07ff3c84 2023-12-17 benni if (prompt) {
200 07ff3c84 2023-12-17 benni drw_setscheme(drw, scheme[SchemePrompt]);
201 07ff3c84 2023-12-17 benni x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
202 07ff3c84 2023-12-17 benni }
203 07ff3c84 2023-12-17 benni
204 07ff3c84 2023-12-17 benni if (pprompt) {
205 07ff3c84 2023-12-17 benni drw_setscheme(drw, scheme[SchemePrompt]);
206 07ff3c84 2023-12-17 benni drw_text(drw, x, 0, ppromptw, bh, lrpad / 2, pprompt, 0);
207 07ff3c84 2023-12-17 benni x += ppromptw;
208 07ff3c84 2023-12-17 benni }
209 07ff3c84 2023-12-17 benni
210 07ff3c84 2023-12-17 benni if (pinentry_info->description) {
211 07ff3c84 2023-12-17 benni pb = mw - x;
212 07ff3c84 2023-12-17 benni pdesclen = strlen(pinentry_info->description);
213 07ff3c84 2023-12-17 benni
214 07ff3c84 2023-12-17 benni if (pb > 0) {
215 07ff3c84 2023-12-17 benni pb -= (winmode == WinPin) ? censortl : confirml;
216 07ff3c84 2023-12-17 benni pbw = MINDESCLEN * pdescw / pdesclen;
217 07ff3c84 2023-12-17 benni pbw = MIN(pbw, pdescw);
218 07ff3c84 2023-12-17 benni
219 07ff3c84 2023-12-17 benni if (pb >= pbw) {
220 07ff3c84 2023-12-17 benni pbw = MAX(pbw, pdescw);
221 07ff3c84 2023-12-17 benni pbw = MIN(pbw, pb);
222 07ff3c84 2023-12-17 benni pb = mw - pbw;
223 07ff3c84 2023-12-17 benni
224 07ff3c84 2023-12-17 benni for (i = 0; i < pdesclen; i++) {
225 07ff3c84 2023-12-17 benni if (pinentry_info->description[i] == '\n') {
226 07ff3c84 2023-12-17 benni pinentry_info->description[i] = ' ';
227 07ff3c84 2023-12-17 benni }
228 07ff3c84 2023-12-17 benni }
229 07ff3c84 2023-12-17 benni
230 07ff3c84 2023-12-17 benni drw_setscheme(drw, scheme[SchemeDesc]);
231 07ff3c84 2023-12-17 benni drw_text(drw, pb, 0, pbw, bh, lrpad / 2, pinentry_info->description,
232 07ff3c84 2023-12-17 benni 0);
233 07ff3c84 2023-12-17 benni } else {
234 07ff3c84 2023-12-17 benni pbw = 0;
235 07ff3c84 2023-12-17 benni }
236 07ff3c84 2023-12-17 benni }
237 07ff3c84 2023-12-17 benni }
238 07ff3c84 2023-12-17 benni
239 07ff3c84 2023-12-17 benni /* Draw input field */
240 07ff3c84 2023-12-17 benni drw_setscheme(drw, scheme[SchemeNormal]);
241 07ff3c84 2023-12-17 benni
242 07ff3c84 2023-12-17 benni if (winmode == WinPin) {
243 07ff3c84 2023-12-17 benni censort = ecalloc(1, asterlen * pin_len);
244 07ff3c84 2023-12-17 benni
245 07ff3c84 2023-12-17 benni for (i = 0; i < asterlen * strlen(pin); i += asterlen) {
246 07ff3c84 2023-12-17 benni memcpy(&censort[i], asterisk, asterlen);
247 07ff3c84 2023-12-17 benni }
248 07ff3c84 2023-12-17 benni
249 07ff3c84 2023-12-17 benni censort[i+1] = '\n';
250 07ff3c84 2023-12-17 benni leftinput = mw - x - pbw;
251 07ff3c84 2023-12-17 benni drw_text(drw, x, 0, leftinput, bh, lrpad / 2, censort, 0);
252 07ff3c84 2023-12-17 benni drw_font_getexts(drw->fonts, censort, cursor * asterlen, &curpos, NULL);
253 07ff3c84 2023-12-17 benni
254 07ff3c84 2023-12-17 benni if ((curpos += lrpad / 2 - 1) < leftinput) {
255 07ff3c84 2023-12-17 benni drw_setscheme(drw, scheme[SchemeNormal]);
256 07ff3c84 2023-12-17 benni drw_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0);
257 07ff3c84 2023-12-17 benni }
258 07ff3c84 2023-12-17 benni
259 07ff3c84 2023-12-17 benni free(censort);
260 07ff3c84 2023-12-17 benni } else {
261 07ff3c84 2023-12-17 benni x += TEXTW(" ");
262 07ff3c84 2023-12-17 benni x = drawitem("No", (sel == No), x, 0, TEXTW("No"));
263 07ff3c84 2023-12-17 benni x = drawitem("Yes", (sel == Yes), x, 0, TEXTW("Yes"));
264 07ff3c84 2023-12-17 benni }
265 07ff3c84 2023-12-17 benni
266 07ff3c84 2023-12-17 benni drw_map(drw, win, 0, 0, mw, mh);
267 07ff3c84 2023-12-17 benni }
268 07ff3c84 2023-12-17 benni
269 07ff3c84 2023-12-17 benni static void
270 07ff3c84 2023-12-17 benni setup(void) {
271 07ff3c84 2023-12-17 benni int x, y, i = 0;
272 07ff3c84 2023-12-17 benni unsigned int du;
273 07ff3c84 2023-12-17 benni XSetWindowAttributes swa;
274 07ff3c84 2023-12-17 benni XIM xim;
275 07ff3c84 2023-12-17 benni Window w, dw, *dws;
276 07ff3c84 2023-12-17 benni XWindowAttributes wa;
277 07ff3c84 2023-12-17 benni #ifdef XINERAMA
278 07ff3c84 2023-12-17 benni XineramaScreenInfo *info;
279 07ff3c84 2023-12-17 benni Window pw;
280 07ff3c84 2023-12-17 benni int a, j, di, n, area = 0;
281 07ff3c84 2023-12-17 benni #endif
282 07ff3c84 2023-12-17 benni
283 07ff3c84 2023-12-17 benni /* Init appearance */
284 07ff3c84 2023-12-17 benni scheme[SchemePrompt] = drw_scm_create(drw, colors[SchemePrompt], 2);
285 07ff3c84 2023-12-17 benni scheme[SchemeNormal] = drw_scm_create(drw, colors[SchemeNormal], 2);
286 07ff3c84 2023-12-17 benni scheme[SchemeSelect] = drw_scm_create(drw, colors[SchemeSelect], 2);
287 07ff3c84 2023-12-17 benni scheme[SchemeDesc] = drw_scm_create(drw, colors[SchemeDesc], 2);
288 07ff3c84 2023-12-17 benni
289 07ff3c84 2023-12-17 benni clip = XInternAtom(dpy, "CLIPBOARD", False);
290 07ff3c84 2023-12-17 benni utf8 = XInternAtom(dpy, "UTF8_STRING", False);
291 07ff3c84 2023-12-17 benni
292 07ff3c84 2023-12-17 benni /* Calculate menu geometry */
293 07ff3c84 2023-12-17 benni bh = drw->fonts->h + 2;
294 07ff3c84 2023-12-17 benni bh = MAX(bh, lineheight);
295 07ff3c84 2023-12-17 benni mh = bh;
296 07ff3c84 2023-12-17 benni #ifdef XINERAMA
297 07ff3c84 2023-12-17 benni info = XineramaQueryScreens(dpy, &n);
298 07ff3c84 2023-12-17 benni
299 07ff3c84 2023-12-17 benni if (parentwin == root && info) {
300 07ff3c84 2023-12-17 benni XGetInputFocus(dpy, &w, &di);
301 07ff3c84 2023-12-17 benni if (mon >= 0 && mon < n) {
302 07ff3c84 2023-12-17 benni i = mon;
303 07ff3c84 2023-12-17 benni } else if (w != root && w != PointerRoot && w != None) {
304 07ff3c84 2023-12-17 benni /* Find top-level window containing current input focus */
305 07ff3c84 2023-12-17 benni do {
306 07ff3c84 2023-12-17 benni if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) {
307 07ff3c84 2023-12-17 benni XFree(dws);
308 07ff3c84 2023-12-17 benni }
309 07ff3c84 2023-12-17 benni } while (w != root && w != pw);
310 07ff3c84 2023-12-17 benni /* Find xinerama screen with which the window intersects most */
311 07ff3c84 2023-12-17 benni if (XGetWindowAttributes(dpy, pw, &wa)) {
312 07ff3c84 2023-12-17 benni for (j = 0; j < n; j++) {
313 07ff3c84 2023-12-17 benni a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j]);
314 07ff3c84 2023-12-17 benni if (a > area) {
315 07ff3c84 2023-12-17 benni area = a;
316 07ff3c84 2023-12-17 benni i = j;
317 07ff3c84 2023-12-17 benni }
318 07ff3c84 2023-12-17 benni }
319 07ff3c84 2023-12-17 benni }
320 07ff3c84 2023-12-17 benni }
321 07ff3c84 2023-12-17 benni /* No focused window is on screen, so use pointer location instead */
322 07ff3c84 2023-12-17 benni if (mon < 0 && !area
323 07ff3c84 2023-12-17 benni && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) {
324 07ff3c84 2023-12-17 benni for (i = 0; i < n; i++) {
325 07ff3c84 2023-12-17 benni if (INTERSECT(x, y, 1, 1, info[i])) {
326 07ff3c84 2023-12-17 benni break;
327 07ff3c84 2023-12-17 benni }
328 07ff3c84 2023-12-17 benni }
329 07ff3c84 2023-12-17 benni }
330 07ff3c84 2023-12-17 benni
331 07ff3c84 2023-12-17 benni x = info[i].x_org;
332 07ff3c84 2023-12-17 benni y = info[i].y_org + (bottom ? info[i].height - mh : 0);
333 07ff3c84 2023-12-17 benni mw = info[i].width;
334 07ff3c84 2023-12-17 benni XFree(info);
335 07ff3c84 2023-12-17 benni } else
336 07ff3c84 2023-12-17 benni #endif
337 07ff3c84 2023-12-17 benni {
338 07ff3c84 2023-12-17 benni if (!XGetWindowAttributes(dpy, parentwin, &wa)) {
339 07ff3c84 2023-12-17 benni die("could not get embedding window attributes: 0x%lx", parentwin);
340 07ff3c84 2023-12-17 benni }
341 07ff3c84 2023-12-17 benni x = 0;
342 07ff3c84 2023-12-17 benni y = bottom ? wa.height - mh : 0;
343 07ff3c84 2023-12-17 benni mw = wa.width;
344 07ff3c84 2023-12-17 benni }
345 07ff3c84 2023-12-17 benni
346 07ff3c84 2023-12-17 benni pdescw = (pinentry_info->description) ? TEXTW(pinentry_info->description) : 0;
347 07ff3c84 2023-12-17 benni
348 07ff3c84 2023-12-17 benni /* Create menu window */
349 07ff3c84 2023-12-17 benni swa.override_redirect = True;
350 07ff3c84 2023-12-17 benni swa.background_pixel = scheme[SchemePrompt][ColBg].pixel;
351 07ff3c84 2023-12-17 benni swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
352 07ff3c84 2023-12-17 benni win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
353 07ff3c84 2023-12-17 benni CopyFromParent, CopyFromParent, CopyFromParent,
354 07ff3c84 2023-12-17 benni CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
355 07ff3c84 2023-12-17 benni
356 07ff3c84 2023-12-17 benni /* Open input methods */
357 07ff3c84 2023-12-17 benni xim = XOpenIM(dpy, NULL, NULL, NULL);
358 07ff3c84 2023-12-17 benni xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
359 07ff3c84 2023-12-17 benni XNClientWindow, win, XNFocusWindow, win, NULL);
360 07ff3c84 2023-12-17 benni XMapRaised(dpy, win);
361 07ff3c84 2023-12-17 benni
362 07ff3c84 2023-12-17 benni if (embedded) {
363 07ff3c84 2023-12-17 benni XSelectInput(dpy, parentwin, FocusChangeMask);
364 07ff3c84 2023-12-17 benni
365 07ff3c84 2023-12-17 benni if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
366 07ff3c84 2023-12-17 benni for (i = 0; i < du && dws[i] != win; ++i) {
367 07ff3c84 2023-12-17 benni XSelectInput(dpy, dws[i], FocusChangeMask);
368 07ff3c84 2023-12-17 benni }
369 07ff3c84 2023-12-17 benni
370 07ff3c84 2023-12-17 benni XFree(dws);
371 07ff3c84 2023-12-17 benni }
372 07ff3c84 2023-12-17 benni grabfocus();
373 07ff3c84 2023-12-17 benni }
374 07ff3c84 2023-12-17 benni
375 07ff3c84 2023-12-17 benni drw_resize(drw, mw, mh);
376 07ff3c84 2023-12-17 benni }
377 07ff3c84 2023-12-17 benni
378 07ff3c84 2023-12-17 benni static void
379 07ff3c84 2023-12-17 benni cleanup(void) {
380 07ff3c84 2023-12-17 benni XUngrabKey(dpy, AnyKey, AnyModifier, root);
381 07ff3c84 2023-12-17 benni free(scheme[SchemeDesc]);
382 07ff3c84 2023-12-17 benni free(scheme[SchemeSelect]);
383 07ff3c84 2023-12-17 benni free(scheme[SchemeNormal]);
384 07ff3c84 2023-12-17 benni free(scheme[SchemePrompt]);
385 07ff3c84 2023-12-17 benni drw_free(drw);
386 07ff3c84 2023-12-17 benni XSync(dpy, False);
387 07ff3c84 2023-12-17 benni XCloseDisplay(dpy);
388 07ff3c84 2023-12-17 benni }
389 07ff3c84 2023-12-17 benni
390 07ff3c84 2023-12-17 benni static int
391 07ff3c84 2023-12-17 benni keypress_confirm(XKeyEvent *ev, KeySym ksym) {
392 07ff3c84 2023-12-17 benni if (ev->state & ControlMask) {
393 07ff3c84 2023-12-17 benni switch(ksym) {
394 07ff3c84 2023-12-17 benni case XK_c:
395 07ff3c84 2023-12-17 benni pinentry_info->canceled = 1;
396 07ff3c84 2023-12-17 benni sel = No;
397 07ff3c84 2023-12-17 benni return 1;
398 07ff3c84 2023-12-17 benni default:
399 07ff3c84 2023-12-17 benni return 1;
400 07ff3c84 2023-12-17 benni }
401 07ff3c84 2023-12-17 benni }
402 07ff3c84 2023-12-17 benni
403 07ff3c84 2023-12-17 benni switch(ksym) {
404 07ff3c84 2023-12-17 benni case XK_KP_Enter:
405 07ff3c84 2023-12-17 benni case XK_Return:
406 07ff3c84 2023-12-17 benni if (sel != Nothing) {
407 07ff3c84 2023-12-17 benni return 1;
408 07ff3c84 2023-12-17 benni }
409 07ff3c84 2023-12-17 benni break;
410 07ff3c84 2023-12-17 benni case XK_y:
411 07ff3c84 2023-12-17 benni case XK_Y:
412 07ff3c84 2023-12-17 benni sel = Yes;
413 07ff3c84 2023-12-17 benni return 1;
414 07ff3c84 2023-12-17 benni case XK_n:
415 07ff3c84 2023-12-17 benni case XK_N:
416 07ff3c84 2023-12-17 benni sel = No;
417 07ff3c84 2023-12-17 benni return 1;
418 07ff3c84 2023-12-17 benni case XK_g:
419 07ff3c84 2023-12-17 benni case XK_G:
420 07ff3c84 2023-12-17 benni case XK_Escape:
421 07ff3c84 2023-12-17 benni pinentry_info->canceled = 1;
422 07ff3c84 2023-12-17 benni sel = No;
423 07ff3c84 2023-12-17 benni return 1;
424 07ff3c84 2023-12-17 benni case XK_h:
425 07ff3c84 2023-12-17 benni case XK_j:
426 07ff3c84 2023-12-17 benni case XK_Home:
427 07ff3c84 2023-12-17 benni case XK_Left:
428 07ff3c84 2023-12-17 benni case XK_Prior:
429 07ff3c84 2023-12-17 benni case XK_Up:
430 07ff3c84 2023-12-17 benni sel = No;
431 07ff3c84 2023-12-17 benni break;
432 07ff3c84 2023-12-17 benni case XK_k:
433 07ff3c84 2023-12-17 benni case XK_l:
434 07ff3c84 2023-12-17 benni case XK_Down:
435 07ff3c84 2023-12-17 benni case XK_End:
436 07ff3c84 2023-12-17 benni case XK_Next:
437 07ff3c84 2023-12-17 benni case XK_Right:
438 07ff3c84 2023-12-17 benni sel = Yes;
439 07ff3c84 2023-12-17 benni break;
440 07ff3c84 2023-12-17 benni }
441 07ff3c84 2023-12-17 benni
442 07ff3c84 2023-12-17 benni return 0;
443 07ff3c84 2023-12-17 benni }
444 07ff3c84 2023-12-17 benni
445 07ff3c84 2023-12-17 benni static int
446 07ff3c84 2023-12-17 benni keypress_pin(XKeyEvent *ev, KeySym ksym, char* buf, int len) {
447 07ff3c84 2023-12-17 benni int old;
448 07ff3c84 2023-12-17 benni
449 07ff3c84 2023-12-17 benni if (ev->state & ControlMask) {
450 07ff3c84 2023-12-17 benni switch(ksym) {
451 07ff3c84 2023-12-17 benni case XK_a: ksym = XK_Home; break;
452 07ff3c84 2023-12-17 benni case XK_b: ksym = XK_Left; break;
453 07ff3c84 2023-12-17 benni case XK_c: ksym = XK_Escape; break;
454 07ff3c84 2023-12-17 benni case XK_d: ksym = XK_Delete; break;
455 07ff3c84 2023-12-17 benni case XK_e: ksym = XK_End; break;
456 07ff3c84 2023-12-17 benni case XK_f: ksym = XK_Right; break;
457 07ff3c84 2023-12-17 benni case XK_g: ksym = XK_Escape; break;
458 07ff3c84 2023-12-17 benni case XK_h: ksym = XK_BackSpace; break;
459 07ff3c84 2023-12-17 benni case XK_k:
460 07ff3c84 2023-12-17 benni old = cursor;
461 07ff3c84 2023-12-17 benni cursor = strlen(pin);
462 07ff3c84 2023-12-17 benni insert(NULL, old - cursor);
463 07ff3c84 2023-12-17 benni break;
464 07ff3c84 2023-12-17 benni case XK_u:
465 07ff3c84 2023-12-17 benni insert(NULL, -cursor);
466 07ff3c84 2023-12-17 benni break;
467 07ff3c84 2023-12-17 benni case XK_v:
468 07ff3c84 2023-12-17 benni XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
469 07ff3c84 2023-12-17 benni utf8, utf8, win, CurrentTime);
470 07ff3c84 2023-12-17 benni return 0;
471 07ff3c84 2023-12-17 benni case XK_Return:
472 07ff3c84 2023-12-17 benni case XK_KP_Enter:
473 07ff3c84 2023-12-17 benni break;
474 07ff3c84 2023-12-17 benni case XK_bracketleft:
475 07ff3c84 2023-12-17 benni pinentry_info->canceled = 1;
476 07ff3c84 2023-12-17 benni return 1;
477 07ff3c84 2023-12-17 benni default:
478 07ff3c84 2023-12-17 benni return 1;
479 07ff3c84 2023-12-17 benni }
480 07ff3c84 2023-12-17 benni }
481 07ff3c84 2023-12-17 benni
482 07ff3c84 2023-12-17 benni switch(ksym) {
483 07ff3c84 2023-12-17 benni case XK_Delete:
484 07ff3c84 2023-12-17 benni if (pin[cursor] == '\0') {
485 07ff3c84 2023-12-17 benni return 0;
486 07ff3c84 2023-12-17 benni }
487 07ff3c84 2023-12-17 benni cursor = nextrune(cursor, +1);
488 07ff3c84 2023-12-17 benni /* Fallthrough */
489 07ff3c84 2023-12-17 benni case XK_BackSpace:
490 07ff3c84 2023-12-17 benni if (cursor == 0) {
491 07ff3c84 2023-12-17 benni return 0;
492 07ff3c84 2023-12-17 benni }
493 07ff3c84 2023-12-17 benni insert(NULL, nextrune(cursor, -1) - cursor);
494 07ff3c84 2023-12-17 benni break;
495 07ff3c84 2023-12-17 benni case XK_Escape:
496 07ff3c84 2023-12-17 benni pinentry_info->canceled = 1;
497 07ff3c84 2023-12-17 benni return 1;
498 07ff3c84 2023-12-17 benni case XK_Left:
499 07ff3c84 2023-12-17 benni if (cursor > 0) {
500 07ff3c84 2023-12-17 benni cursor = nextrune(cursor, -1);
501 07ff3c84 2023-12-17 benni }
502 07ff3c84 2023-12-17 benni break;
503 07ff3c84 2023-12-17 benni case XK_Right:
504 07ff3c84 2023-12-17 benni if (pin[cursor] != '\0') {
505 07ff3c84 2023-12-17 benni cursor = nextrune(cursor, +1);
506 07ff3c84 2023-12-17 benni }
507 07ff3c84 2023-12-17 benni break;
508 07ff3c84 2023-12-17 benni case XK_Home:
509 07ff3c84 2023-12-17 benni cursor = 0;
510 07ff3c84 2023-12-17 benni break;
511 07ff3c84 2023-12-17 benni case XK_End:
512 07ff3c84 2023-12-17 benni cursor = strlen(pin);
513 07ff3c84 2023-12-17 benni break;
514 07ff3c84 2023-12-17 benni case XK_Return:
515 07ff3c84 2023-12-17 benni case XK_KP_Enter:
516 07ff3c84 2023-12-17 benni return 1;
517 07ff3c84 2023-12-17 benni break;
518 07ff3c84 2023-12-17 benni default:
519 07ff3c84 2023-12-17 benni if (!iscntrl(*buf)) {
520 07ff3c84 2023-12-17 benni insert(buf, len);
521 07ff3c84 2023-12-17 benni }
522 07ff3c84 2023-12-17 benni }
523 07ff3c84 2023-12-17 benni
524 07ff3c84 2023-12-17 benni return 0;
525 07ff3c84 2023-12-17 benni }
526 07ff3c84 2023-12-17 benni
527 07ff3c84 2023-12-17 benni static int
528 07ff3c84 2023-12-17 benni keypress(XKeyEvent *ev) {
529 07ff3c84 2023-12-17 benni char buf[32];
530 07ff3c84 2023-12-17 benni int len;
531 07ff3c84 2023-12-17 benni int ret = 1;
532 07ff3c84 2023-12-17 benni
533 07ff3c84 2023-12-17 benni KeySym ksym = NoSymbol;
534 07ff3c84 2023-12-17 benni Status status;
535 07ff3c84 2023-12-17 benni len = XmbLookupString(xic, ev, buf, sizeof(buf), &ksym, &status);
536 07ff3c84 2023-12-17 benni
537 07ff3c84 2023-12-17 benni if (status != XBufferOverflow) {
538 07ff3c84 2023-12-17 benni if (winmode == WinConfirm) {
539 07ff3c84 2023-12-17 benni ret = keypress_confirm(ev, ksym);
540 07ff3c84 2023-12-17 benni } else {
541 07ff3c84 2023-12-17 benni ret = keypress_pin(ev, ksym, buf, len);
542 07ff3c84 2023-12-17 benni }
543 07ff3c84 2023-12-17 benni
544 07ff3c84 2023-12-17 benni if (ret == 0) {
545 07ff3c84 2023-12-17 benni drawwin();
546 07ff3c84 2023-12-17 benni }
547 07ff3c84 2023-12-17 benni }
548 07ff3c84 2023-12-17 benni
549 07ff3c84 2023-12-17 benni return ret;
550 07ff3c84 2023-12-17 benni }
551 07ff3c84 2023-12-17 benni
552 07ff3c84 2023-12-17 benni static void
553 07ff3c84 2023-12-17 benni paste(void) {
554 07ff3c84 2023-12-17 benni char *p, *q;
555 07ff3c84 2023-12-17 benni int di;
556 07ff3c84 2023-12-17 benni unsigned long dl;
557 07ff3c84 2023-12-17 benni Atom da;
558 07ff3c84 2023-12-17 benni
559 07ff3c84 2023-12-17 benni /* We have been given the current selection, now insert it into input */
560 07ff3c84 2023-12-17 benni XGetWindowProperty(dpy, win, utf8, 0, pin_len / 4, False, utf8, &da, &di,
561 07ff3c84 2023-12-17 benni &dl, &dl, (unsigned char **)&p);
562 07ff3c84 2023-12-17 benni insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t) strlen(p));
563 07ff3c84 2023-12-17 benni XFree(p);
564 07ff3c84 2023-12-17 benni drawwin();
565 07ff3c84 2023-12-17 benni }
566 07ff3c84 2023-12-17 benni
567 07ff3c84 2023-12-17 benni void
568 07ff3c84 2023-12-17 benni run(void) {
569 07ff3c84 2023-12-17 benni XEvent ev;
570 07ff3c84 2023-12-17 benni
571 07ff3c84 2023-12-17 benni drawwin();
572 07ff3c84 2023-12-17 benni
573 07ff3c84 2023-12-17 benni while (!XNextEvent(dpy, &ev)) {
574 07ff3c84 2023-12-17 benni if (XFilterEvent(&ev, win)) {
575 07ff3c84 2023-12-17 benni continue;
576 07ff3c84 2023-12-17 benni }
577 07ff3c84 2023-12-17 benni switch(ev.type) {
578 07ff3c84 2023-12-17 benni case Expose:
579 07ff3c84 2023-12-17 benni if (ev.xexpose.count == 0) {
580 07ff3c84 2023-12-17 benni drw_map(drw, win, 0, 0, mw, mh);
581 07ff3c84 2023-12-17 benni }
582 07ff3c84 2023-12-17 benni break;
583 07ff3c84 2023-12-17 benni case KeyPress:
584 07ff3c84 2023-12-17 benni if (keypress(&ev.xkey)) {
585 07ff3c84 2023-12-17 benni return;
586 07ff3c84 2023-12-17 benni }
587 07ff3c84 2023-12-17 benni break;
588 07ff3c84 2023-12-17 benni case SelectionNotify:
589 07ff3c84 2023-12-17 benni if (ev.xselection.property == utf8) {
590 07ff3c84 2023-12-17 benni paste();
591 07ff3c84 2023-12-17 benni }
592 07ff3c84 2023-12-17 benni break;
593 07ff3c84 2023-12-17 benni case VisibilityNotify:
594 07ff3c84 2023-12-17 benni if (ev.xvisibility.state != VisibilityUnobscured) {
595 07ff3c84 2023-12-17 benni XRaiseWindow(dpy, win);
596 07ff3c84 2023-12-17 benni }
597 07ff3c84 2023-12-17 benni break;
598 07ff3c84 2023-12-17 benni }
599 07ff3c84 2023-12-17 benni }
600 07ff3c84 2023-12-17 benni }
601 07ff3c84 2023-12-17 benni
602 07ff3c84 2023-12-17 benni static void
603 07ff3c84 2023-12-17 benni catchsig(int sig) {
604 07ff3c84 2023-12-17 benni if (sig == SIGALRM) {
605 07ff3c84 2023-12-17 benni timed_out = 1;
606 07ff3c84 2023-12-17 benni }
607 07ff3c84 2023-12-17 benni }
608 07ff3c84 2023-12-17 benni
609 07ff3c84 2023-12-17 benni static void
610 07ff3c84 2023-12-17 benni password(void) {
611 07ff3c84 2023-12-17 benni winmode = WinPin;
612 07ff3c84 2023-12-17 benni repeat = 0;
613 07ff3c84 2023-12-17 benni setup_pin(pinentry_info->pin, pinentry_info->pin_len, 1);
614 07ff3c84 2023-12-17 benni run();
615 07ff3c84 2023-12-17 benni
616 07ff3c84 2023-12-17 benni if (!pinentry_info->canceled && pinentry_info->repeat_passphrase) {
617 07ff3c84 2023-12-17 benni repeat = 1;
618 07ff3c84 2023-12-17 benni pin_repeat_len = pinentry_info->pin_len;
619 07ff3c84 2023-12-17 benni pin_repeat = secmem_malloc(pinentry_info->pin_len);
620 07ff3c84 2023-12-17 benni setup_pin(pin_repeat, pin_repeat_len, 1);
621 07ff3c84 2023-12-17 benni run();
622 07ff3c84 2023-12-17 benni
623 07ff3c84 2023-12-17 benni pinentry_info->repeat_okay = (strcmp(pinentry_info->pin, pin_repeat) == 0)? 1 : 0;
624 07ff3c84 2023-12-17 benni secmem_free(pin_repeat);
625 07ff3c84 2023-12-17 benni
626 07ff3c84 2023-12-17 benni if (!pinentry_info->repeat_okay) {
627 07ff3c84 2023-12-17 benni pinentry_info->result = -1;
628 07ff3c84 2023-12-17 benni return;
629 07ff3c84 2023-12-17 benni }
630 07ff3c84 2023-12-17 benni }
631 07ff3c84 2023-12-17 benni
632 07ff3c84 2023-12-17 benni if (pinentry_info->canceled) {
633 07ff3c84 2023-12-17 benni pinentry_info->result = -1;
634 07ff3c84 2023-12-17 benni return;
635 07ff3c84 2023-12-17 benni }
636 07ff3c84 2023-12-17 benni
637 07ff3c84 2023-12-17 benni pinentry_info->result = strlen(pinentry_info->pin);
638 07ff3c84 2023-12-17 benni }
639 07ff3c84 2023-12-17 benni
640 07ff3c84 2023-12-17 benni static void
641 07ff3c84 2023-12-17 benni confirm(void) {
642 07ff3c84 2023-12-17 benni winmode = WinConfirm;
643 07ff3c84 2023-12-17 benni sel = Nothing;
644 07ff3c84 2023-12-17 benni run();
645 07ff3c84 2023-12-17 benni pinentry_info->result = sel != No;
646 07ff3c84 2023-12-17 benni }
647 07ff3c84 2023-12-17 benni
648 07ff3c84 2023-12-17 benni static int
649 07ff3c84 2023-12-17 benni cmdhandler(pinentry_t received_pinentry) {
650 07ff3c84 2023-12-17 benni struct sigaction sa;
651 07ff3c84 2023-12-17 benni XWindowAttributes wa;
652 07ff3c84 2023-12-17 benni
653 07ff3c84 2023-12-17 benni pinentry_info = received_pinentry;
654 07ff3c84 2023-12-17 benni
655 07ff3c84 2023-12-17 benni if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) {
656 07ff3c84 2023-12-17 benni fputs("warning: no locale support\n", stderr);
657 07ff3c84 2023-12-17 benni }
658 07ff3c84 2023-12-17 benni if (!(dpy = XOpenDisplay(pinentry_info->display))) {
659 07ff3c84 2023-12-17 benni die("cannot open display");
660 07ff3c84 2023-12-17 benni }
661 07ff3c84 2023-12-17 benni screen = DefaultScreen(dpy);
662 07ff3c84 2023-12-17 benni root = RootWindow(dpy, screen);
663 07ff3c84 2023-12-17 benni embedded = (pinentry_info->parent_wid) ? embedded : 0;
664 07ff3c84 2023-12-17 benni parentwin = (embedded) ? pinentry_info->parent_wid : root;
665 07ff3c84 2023-12-17 benni if (!XGetWindowAttributes(dpy, parentwin, &wa)) {
666 07ff3c84 2023-12-17 benni die("could not get embedding window attributes: 0x%lx", parentwin);
667 07ff3c84 2023-12-17 benni }
668 07ff3c84 2023-12-17 benni drw = drw_create(dpy, screen, root, wa.width, wa.height);
669 07ff3c84 2023-12-17 benni if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) {
670 07ff3c84 2023-12-17 benni die("no fonts could be loaded.");
671 07ff3c84 2023-12-17 benni }
672 07ff3c84 2023-12-17 benni lrpad = drw->fonts->h;
673 07ff3c84 2023-12-17 benni drw_setscheme(drw, scheme[SchemePrompt]);
674 07ff3c84 2023-12-17 benni
675 07ff3c84 2023-12-17 benni if (pinentry_info->timeout) {
676 07ff3c84 2023-12-17 benni memset(&sa, 0, sizeof(sa));
677 07ff3c84 2023-12-17 benni sa.sa_handler = catchsig;
678 07ff3c84 2023-12-17 benni sigaction(SIGALRM, &sa, NULL);
679 07ff3c84 2023-12-17 benni alarm(pinentry_info->timeout);
680 07ff3c84 2023-12-17 benni }
681 07ff3c84 2023-12-17 benni
682 07ff3c84 2023-12-17 benni grabkeyboard();
683 07ff3c84 2023-12-17 benni setup();
684 07ff3c84 2023-12-17 benni
685 07ff3c84 2023-12-17 benni if (pinentry_info->pin) {
686 07ff3c84 2023-12-17 benni do {
687 07ff3c84 2023-12-17 benni password();
688 07ff3c84 2023-12-17 benni } while (!pinentry_info->canceled && pinentry_info->repeat_passphrase
689 07ff3c84 2023-12-17 benni && !pinentry_info->repeat_okay);
690 07ff3c84 2023-12-17 benni } else {
691 07ff3c84 2023-12-17 benni confirm();
692 07ff3c84 2023-12-17 benni }
693 07ff3c84 2023-12-17 benni
694 07ff3c84 2023-12-17 benni cleanup();
695 07ff3c84 2023-12-17 benni
696 07ff3c84 2023-12-17 benni return pinentry_info->result;
697 07ff3c84 2023-12-17 benni }
698 07ff3c84 2023-12-17 benni
699 07ff3c84 2023-12-17 benni pinentry_cmd_handler_t pinentry_cmd_handler = cmdhandler;
700 07ff3c84 2023-12-17 benni
701 07ff3c84 2023-12-17 benni int
702 07ff3c84 2023-12-17 benni main(int argc, char *argv[]) {
703 07ff3c84 2023-12-17 benni pinentry_init("pinentry-dmenu");
704 07ff3c84 2023-12-17 benni pinentry_parse_opts(argc, argv);
705 af60b04a 2023-12-17 benni return pinentry_loop();
706 07ff3c84 2023-12-17 benni }