1 /*
2 * forms.c
3 *
4 * Functions for various form dialogs.
5 *
6 * This file is part of the ckpass project.
7 *
8 * Copyright (C) 2009 Heath N. Caldwell <hncaldwell@gmail.com>
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25 #include <ncurses.h>
26 #include <form.h>
27 #include <string.h>
28 #include <kpass.h>
29 #include <time.h>
30
31 #include "forms.h"
32
password_form(char * buffer,int length)33 int password_form(char *buffer, int length) {
34 FIELD *fields[5];
35 FORM *form;
36 WINDOW *win;
37 int press;
38 int rows, cols;
39 int ret_val;
40 int i;
41 int max_x, max_y;
42
43 char *labels[] = {"Password:", "[OK]", "[CANCEL]"};
44
45 fields[0] = new_field(1, strlen(labels[0]), 0, 0, 0 , 0);
46 fields[1] = new_field(1, 16, 0, 1 + strlen(labels[0]), 0 , 0);
47 fields[2] = new_field(1, strlen(labels[1]), 2, 4, 0 , 0);
48 fields[3] = new_field(1, strlen(labels[2]), 2, 6 + strlen(labels[2]), 0 , 0);
49 fields[4] = 0;
50
51 field_opts_off(fields[0], O_ACTIVE);
52
53 set_field_back(fields[1], A_UNDERLINE);
54 field_opts_off(fields[1], O_PUBLIC);
55 field_opts_off(fields[1], O_STATIC);
56 field_opts_off(fields[1], O_AUTOSKIP);
57 set_max_field(fields[1], length);
58
59 field_opts_off(fields[2], O_EDIT);
60 field_opts_off(fields[3], O_EDIT);
61
62 form = new_form(fields);
63 form_opts_off(form, O_BS_OVERLOAD);
64 scale_form(form, &rows, &cols);
65
66 getmaxyx(stdscr, max_y, max_x);
67
68 win = newwin(rows + 4, cols + 4, (max_y - (rows + 4)) / 2, (max_x - (cols + 4)) / 2);
69 keypad(win, TRUE);
70 set_form_win(form, win);
71 set_form_sub(form, derwin(win, rows, cols, 2, 2));
72 box(win, 0, 0);
73
74 post_form(form);
75 curs_set(1);
76 wrefresh(win);
77
78 set_field_just(fields[0], JUSTIFY_LEFT);
79 set_field_buffer(fields[0], 0, labels[0]);
80 set_field_buffer(fields[2], 0, labels[1]);
81 set_field_buffer(fields[3], 0, labels[2]);
82
83 while(1) {
84 press = wgetch(win);
85
86 if(press == KEY_RESIZE) {
87 // This might be a mess to deal with.
88 continue;
89 } else if(press == '\t') {
90 if(current_field(form) != fields[1]) {
91 set_field_fore(current_field(form), A_NORMAL);
92 curs_set(1);
93 }
94 form_driver(form, REQ_NEXT_FIELD);
95 form_driver(form, REQ_END_LINE);
96 if(current_field(form) != fields[1]) {
97 set_field_fore(current_field(form), A_REVERSE);
98 curs_set(0);
99 }
100 } else if(press == KEY_LEFT) {
101 form_driver(form, REQ_PREV_CHAR);
102 } else if(press == KEY_RIGHT) {
103 form_driver(form, REQ_NEXT_CHAR);
104 } else if(press == KEY_BACKSPACE) {
105 form_driver(form, REQ_DEL_PREV);
106 } else if(press == '\n') {
107 if(current_field(form) == fields[3]) {
108 ret_val = 1;
109 break;
110 }
111
112 /* Force the field buffer to be written. */
113 if(current_field(form) == fields[1]) form_driver(form, REQ_NEXT_FIELD);
114
115 strncpy(buffer, field_buffer(fields[1], 0), length);
116
117 /* Trim trailing spaces (should probably be revisited). */
118 i = strlen(buffer) - 2;
119 for(; buffer[i] == ' ' && i > 0; i--) ;
120 buffer[i + 1] = '\0';
121
122 ret_val = 0;
123 break;
124 } else {
125 form_driver(form, press);
126 set_field_status(fields[1], true);
127 }
128 }
129
130 unpost_form(form);
131 free_form(form);
132
133 for(i = 0; fields[i]; i++) {
134 free_field(fields[i]);
135 }
136
137 delwin(win);
138
139 curs_set(0);
140 return ret_val;
141 }
142
open_database_form(char * buffer,int length)143 int open_database_form(char *buffer, int length) {
144 FIELD *fields[5];
145 FORM *form;
146 WINDOW *win;
147 int press;
148 int rows, cols;
149 int ret_val;
150 int i;
151 int max_x, max_y;
152
153 char *labels[] = {"Database File:", "[OK]", "[CANCEL]"};
154
155 fields[0] = new_field(1, strlen(labels[0]), 0, 0, 0 , 0);
156 fields[1] = new_field(1, 32, 0, 1 + strlen(labels[0]), 0 , 0);
157 fields[2] = new_field(1, strlen(labels[1]), 2, 16, 0 , 0);
158 fields[3] = new_field(1, strlen(labels[2]), 2, 18 + strlen(labels[2]), 0 , 0);
159 fields[4] = 0;
160
161 field_opts_off(fields[0], O_ACTIVE);
162
163 set_field_back(fields[1], A_UNDERLINE);
164 field_opts_off(fields[1], O_STATIC);
165 field_opts_off(fields[1], O_AUTOSKIP);
166 set_max_field(fields[1], length);
167
168 field_opts_off(fields[2], O_EDIT);
169 field_opts_off(fields[3], O_EDIT);
170
171 form = new_form(fields);
172 form_opts_off(form, O_BS_OVERLOAD);
173 scale_form(form, &rows, &cols);
174
175 getmaxyx(stdscr, max_y, max_x);
176
177 win = newwin(rows + 4, cols + 4, (max_y - (rows + 4)) / 2, (max_x - (cols + 4)) / 2);
178 keypad(win, TRUE);
179 set_form_win(form, win);
180 set_form_sub(form, derwin(win, rows, cols, 2, 2));
181 box(win, 0, 0);
182
183 post_form(form);
184 curs_set(1);
185 wrefresh(win);
186
187 set_field_just(fields[0], JUSTIFY_LEFT);
188 set_field_buffer(fields[0], 0, labels[0]);
189 set_field_buffer(fields[2], 0, labels[1]);
190 set_field_buffer(fields[3], 0, labels[2]);
191
192 while(1) {
193 press = wgetch(win);
194
195 if(press == KEY_RESIZE) {
196 // This might be a mess to deal with.
197 continue;
198 } else if(press == '\t') {
199 if(current_field(form) != fields[1]) {
200 set_field_fore(current_field(form), A_NORMAL);
201 curs_set(1);
202 }
203 form_driver(form, REQ_NEXT_FIELD);
204 form_driver(form, REQ_END_LINE);
205 if(current_field(form) != fields[1]) {
206 set_field_fore(current_field(form), A_REVERSE);
207 curs_set(0);
208 }
209 } else if(press == KEY_LEFT) {
210 form_driver(form, REQ_PREV_CHAR);
211 } else if(press == KEY_RIGHT) {
212 form_driver(form, REQ_NEXT_CHAR);
213 } else if(press == KEY_BACKSPACE) {
214 form_driver(form, REQ_DEL_PREV);
215 } else if(press == '\n') {
216 if(current_field(form) == fields[3]) {
217 ret_val = 1;
218 break;
219 }
220
221 /* Force the field buffer to be written. */
222 if(current_field(form) == fields[1]) form_driver(form, REQ_NEXT_FIELD);
223
224 strncpy(buffer, field_buffer(fields[1], 0), length);
225
226 /* Trim trailing spaces (should probably be revisited). */
227 i = strlen(buffer) - 2;
228 for(; buffer[i] == ' ' && i > 0; i--) ;
229 buffer[i + 1] = '\0';
230
231 ret_val = 0;
232 break;
233 } else {
234 form_driver(form, press);
235 set_field_status(fields[1], true);
236 }
237 }
238
239 unpost_form(form);
240 free_form(form);
241
242 for(i = 0; fields[i]; i++) {
243 free_field(fields[i]);
244 }
245
246 delwin(win);
247
248 curs_set(0);
249 return ret_val;
250 }
251
entry_form(struct kpass_db * db,struct kpass_entry * entry)252 int entry_form(struct kpass_db *db, struct kpass_entry *entry) {
253 enum {f_uuid, f_group, f_image, f_title, f_url, f_username, f_password,
254 f_desc, f_ctime, f_mtime, f_atime, f_etime, f_notes, f_last};
255
256 FIELD *fields[2*f_last + 3];
257 FORM *form;
258 WINDOW *win;
259 int press;
260 int rows, cols;
261 int ret_val;
262 int i, j;
263 int max_x, max_y;
264 int init_length = 40;
265 int max_length = 256;
266 char buffer[max_length];
267 struct tm tms;
268
269 char *labels[] = {"uuid:", "Group:", "Image:", "Title:", "URL:", "Username:", "Password:",
270 "Description:", "ctime:", "mtime:", "atime:", "etime:", "Notes:"};
271 char *buttons[] = {"[OK]", "[CANCEL]"};
272
273 for(i = 0; i < f_last; i++) {
274 fields[2*i] = new_field(1, strlen(labels[i]), i, 0, 0 , 0);
275 fields[2*i + 1] = new_field(1, init_length, i, 14, 0 , 0);
276
277 set_field_fore(fields[2*i], A_NORMAL);
278 field_opts_off(fields[2*i], O_ACTIVE);
279 set_field_just(fields[2*i], JUSTIFY_LEFT);
280 set_field_buffer(fields[2*i], 0, labels[i]);
281
282 set_field_fore(fields[2*i + 1], A_NORMAL);
283 set_field_back(fields[2*i + 1], A_UNDERLINE);
284 field_opts_off(fields[2*i + 1], O_STATIC);
285 field_opts_off(fields[2*i + 1], O_AUTOSKIP);
286 set_max_field(fields[2*i + 1], max_length);
287
288 switch(i) {
289 case f_uuid:
290 field_opts_off(fields[2*i + 1], O_ACTIVE);
291 set_field_back(fields[2*i + 1], A_NORMAL);
292
293 for(j=0; j<16; j++) {
294 sprintf(buffer + (j*2), "%02x", entry->uuid[j]);
295 }
296 buffer[j] = 0;
297 set_field_buffer(fields[2*i + 1], 0, buffer);
298 break;
299 case f_group:
300 field_opts_off(fields[2*i + 1], O_ACTIVE);
301 set_field_back(fields[2*i + 1], A_NORMAL);
302
303 for(j=0; j < db->groups_len && db->groups[j]->id != entry->group_id; j++);
304 set_field_buffer(fields[2*i + 1], 0, db->groups[j]->name);
305 break;
306 case f_image:
307 field_opts_off(fields[2*i + 1], O_ACTIVE);
308 set_field_back(fields[2*i + 1], A_NORMAL);
309
310 snprintf(buffer, max_length, "%d", entry->image_id);
311
312 set_field_buffer(fields[2*i + 1], 0, buffer);
313 break;
314 case f_title:
315 set_field_buffer(fields[2*i + 1], 0, entry->title);
316 break;
317 case f_url:
318 set_field_buffer(fields[2*i + 1], 0, entry->url);
319 break;
320 case f_username:
321 set_field_buffer(fields[2*i + 1], 0, entry->username);
322 break;
323 case f_password:
324 set_field_buffer(fields[2*i + 1], 0, entry->password);
325 break;
326 case f_desc:
327 set_field_buffer(fields[2*i + 1], 0, entry->desc);
328 break;
329 case f_ctime:
330 kpass_unpack_time(entry->ctime, &tms);
331 strftime(buffer, max_length, "%Y-%m-%d %H:%M:%S", &tms);
332 set_field_buffer(fields[2*i + 1], 0, buffer);
333 break;
334 case f_mtime:
335 kpass_unpack_time(entry->mtime, &tms);
336 strftime(buffer, max_length, "%Y-%m-%d %H:%M:%S", &tms);
337 set_field_buffer(fields[2*i + 1], 0, buffer);
338 break;
339 case f_atime:
340 kpass_unpack_time(entry->atime, &tms);
341 strftime(buffer, max_length, "%Y-%m-%d %H:%M:%S", &tms);
342 set_field_buffer(fields[2*i + 1], 0, buffer);
343 break;
344 case f_etime:
345 kpass_unpack_time(entry->etime, &tms);
346 strftime(buffer, max_length, "%Y-%m-%d %H:%M:%S", &tms);
347
348 if(!strcmp(buffer, "2999-12-28 23:59:59")) {
349 strcpy(buffer, "Never");
350 }
351
352 set_field_buffer(fields[2*i + 1], 0, buffer);
353 break;
354 case f_notes:
355 set_field_buffer(fields[2*i + 1], 0, entry->notes);
356 break;
357 }
358 }
359
360 fields[2*f_last] = new_field(1, strlen(buttons[0]), f_last + 1, 19, 0 , 0);
361 set_field_buffer(fields[2*i], 0, buttons[0]);
362 fields[2*f_last + 1] = new_field(1, strlen(buttons[1]), f_last + 1, 27, 0 , 0);
363 set_field_buffer(fields[2*i + 1], 0, buttons[1]);
364 fields[2*f_last + 2] = 0; /* terminating field */
365
366 field_opts_off(fields[2*f_last], O_EDIT);
367 field_opts_off(fields[2*f_last + 1], O_EDIT);
368
369 form = new_form(fields);
370 form_opts_off(form, O_BS_OVERLOAD);
371 scale_form(form, &rows, &cols);
372
373 getmaxyx(stdscr, max_y, max_x);
374
375 win = newwin(rows + 4, cols + 4, (max_y - (rows + 4)) / 2, (max_x - (cols + 4)) / 2);
376 keypad(win, TRUE);
377 set_form_win(form, win);
378 set_form_sub(form, derwin(win, rows, cols, 2, 2));
379 box(win, 0, 0);
380
381 post_form(form);
382 curs_set(1);
383 wrefresh(win);
384
385 while(1) {
386 press = wgetch(win);
387
388 if(press == KEY_RESIZE) {
389 // This might be a mess to deal with.
390 continue;
391 } else if(press == '\t' || press == KEY_DOWN || (press == '\n' && current_field(form) < fields[2*f_last])) {
392 if(current_field(form) == fields[2*f_last] || current_field(form) == fields[2*f_last + 1]) {
393 set_field_fore(current_field(form), A_NORMAL);
394 curs_set(1);
395 }
396 form_driver(form, REQ_NEXT_FIELD);
397 form_driver(form, REQ_END_LINE);
398 if(current_field(form) == fields[2*f_last] || current_field(form) == fields[2*f_last + 1]) {
399 set_field_fore(current_field(form), A_REVERSE);
400 curs_set(0);
401 }
402 } else if(press == KEY_UP) {
403 if(current_field(form) == fields[2*f_last] || current_field(form) == fields[2*f_last + 1]) {
404 set_field_fore(current_field(form), A_NORMAL);
405 curs_set(1);
406 }
407 form_driver(form, REQ_PREV_FIELD);
408 form_driver(form, REQ_END_LINE);
409 if(current_field(form) == fields[2*f_last] || current_field(form) == fields[2*f_last + 1]) {
410 set_field_fore(current_field(form), A_REVERSE);
411 curs_set(0);
412 }
413 } else if(press == KEY_LEFT) {
414 form_driver(form, REQ_PREV_CHAR);
415 } else if(press == KEY_RIGHT) {
416 form_driver(form, REQ_NEXT_CHAR);
417 } else if(press == KEY_BACKSPACE) {
418 form_driver(form, REQ_DEL_PREV);
419 } else if(press == '\n') {
420 if(current_field(form) == fields[2*f_last]) {
421 /* OK button. */
422
423 /* STORE THE STUFF */
424 ret_val = 0;
425 break;
426 } else if(current_field(form) == fields[2*f_last + 1]) {
427 /* Cancel button. */
428 ret_val = 1;
429 break;
430 }
431 } else {
432 form_driver(form, press);
433 }
434 }
435
436 unpost_form(form);
437 free_form(form);
438
439 for(i = 0; fields[i]; i++) {
440 free_field(fields[i]);
441 }
442
443 delwin(win);
444
445 curs_set(0);
446 return ret_val;
447 }
448
449