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