1 /*
2  *  PWMan - password management application
3  *
4  *  Copyright (C) 2002  Ivan Kelly <ivan@ivankelly.net>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #include <pwman.h>
22 #include <ui.h>
23 #include <help.h>
24 #include <time.h>
25 #include <stdlib.h>
26 
27 char * statusline_ask_str(char *, char*, int);
28 Pw *get_highlighted_item();
29 
30 int should_resize = FALSE;
31 int can_resize = FALSE;
32 
33 
34 WINDOW *top = NULL, *bottom = NULL;
35 extern int curitem, lines;
36 extern WINDOW *list;
37 
38 void
ui_draw_top()39 ui_draw_top()
40 {
41 	werase(top);
42 	mvwhline(top, 1, 0, ACS_HLINE, COLS);
43 	if( !options->readonly) {
44 		mvwprintw(top, 0, 0, "%s | %s", PACKAGE " " VERSION,
45 			  MAIN_HELPLINE);
46 	} else {
47 		mvwprintw(top, 0, 0, "%s | %s | %s", PACKAGE " " VERSION,
48 			  READONLY_MSG, MAIN_HELPLINE);
49 	}
50 
51 	wrefresh(top);
52 }
53 
54 void
ui_draw_bottom()55 ui_draw_bottom()
56 {
57 	werase(bottom);
58 	mvwhline(bottom, 0, 0, ACS_HLINE, COLS);
59 	mvwhline(bottom, 2, 0, ACS_HLINE, COLS);
60 
61 	wrefresh(bottom);
62 }
63 
64 void
ui_refresh_windows()65 ui_refresh_windows()
66 {
67 	ui_draw_top();
68 	ui_draw_bottom();
69 	uilist_refresh();
70 
71 	refresh();
72 }
73 
74 void
ui_resize_windows()75 ui_resize_windows()
76 {
77 	wresize(top, 2, COLS);
78 }
79 
80 void
ui_init_windows()81 ui_init_windows()
82 {
83 	top = newwin(2, COLS,0,0);
84 	bottom = newwin(3, COLS, LINES - 3, 0);
85 
86 	uilist_init();
87 }
88 
89 void
ui_free_windows()90 ui_free_windows()
91 {
92 	uilist_free();
93 
94 	erase();
95 	delwin(top);
96 	delwin(bottom);
97 }
98 
99 static void
ui_too_small_warning()100 ui_too_small_warning()
101 {
102 	clear();
103 	attron(A_BOLD);
104 	mvprintw(((LINES-2)/2),0,"Your window is too small\n\n"
105 			"Minimum size is %dx%d\n\n"
106 			"Please resize and press any key", MIN_LINES, MIN_COLS);
107 	attroff(A_BOLD);
108 	getch();
109 }
110 
111 #ifdef SIGWINCH
112 static void
ui_resize()113 ui_resize()
114 {
115 	struct winsize winsz;
116 	ioctl (0, TIOCGWINSZ, &winsz);
117 
118 	resizeterm(winsz.ws_row, winsz.ws_col);
119 	if((winsz.ws_col < MIN_COLS) || (winsz.ws_row < MIN_LINES)) {
120 
121 		/*
122 		 * if window is too small notify user
123 		 * until he changes it
124 		 */
125 		do {
126 			ui_too_small_warning();
127 			ioctl (0, TIOCGWINSZ, &winsz);
128 			resizeterm(winsz.ws_row, winsz.ws_col);
129 		} while ((winsz.ws_col < MIN_COLS) || (winsz.ws_row < MIN_LINES));
130 
131 	} else {
132 		should_resize = FALSE;
133 		/*	resize_windows();
134 		resize_list();*/
135 		ui_free_windows();
136 		ui_init_windows();
137 		ui_refresh_windows();
138 	}
139 }
140 #endif
141 
142 static void
ui_win_changed(int i)143 ui_win_changed(int i)
144 {
145 	if( can_resize ){
146 		ui_resize();
147 		ui_refresh_windows(); /* dunno why i need this but it wont work without it */
148 	} else {
149 		should_resize = TRUE;
150 	}
151 }
152 
153 int
ui_init()154 ui_init()
155 {
156 	initscr();
157 	cbreak();
158 	noecho();
159 	nonl();
160 
161 	intrflush(stdscr, FALSE);
162 	keypad(stdscr, TRUE);
163 
164 	if((LINES < MIN_LINES) || (COLS < MIN_COLS)){
165 		clear(); refresh(); endwin();
166 		fprintf(stderr, "Your terminal is to small\n");
167 		fprintf(stderr, "Min size is %dx%d\n", MIN_COLS, MIN_LINES);
168 
169 		return 1;
170 	}
171 
172 #ifdef SIGWINCH
173 	signal(SIGWINCH, ui_win_changed);
174 #endif
175 
176 	ui_init_windows();
177 	ui_refresh_windows();
178 	return 0;
179 }
180 
181 void
ui_display_help()182 ui_display_help()
183 {
184 	int i;
185 	WINDOW *helpwin;
186 
187 	helpwin = newwin(LINES - 5, COLS - 6, 3, 3);
188 	uilist_clear();
189 
190 	for(i = 0; help[i] != NULL; i++){
191 		waddstr(helpwin, help[i]);
192 		if( !((i+1) % (LINES - 9)) || (help[i+1] == NULL) ){
193 	/*		refresh();*/
194 			wrefresh(helpwin);
195 			ui_statusline_msg("Press any key to continue...");
196 			getch();
197 			wclear(helpwin);
198 		}
199 	}
200 	uilist_refresh();
201 	ui_statusline_clear();
202 	delwin(helpwin);
203 }
204 
205 int
ui_run()206 ui_run()
207 {
208 	Pw *current_item;
209 	int ch;
210 	int i = 0;
211 	int load_worked = 0;
212 #ifdef DEBUG
213 	int debug_i = 0;
214 #endif
215 	char msg[80];
216 
217 	time_base = time(NULL);
218 
219 	while(1){
220 		can_resize = TRUE;
221 		if( should_resize ){
222 			ui_resize();
223 		}
224 		ch = getch();
225 		ui_statusline_clear();
226 		can_resize = FALSE;
227 
228 		if((time_base < (time(NULL) - (options->passphrase_timeout*60)))
229 				&& options->passphrase_timeout != 0 && tolower(ch) != 'q'){
230 			pwlist_write_file();
231 			pwlist_free_all();
232 
233 			ui_statusline_msg("Passphrase has timed out and you must enter it again.");
234 			getch();
235 
236 			load_worked = pwlist_read_file();
237 			if(load_worked != 0) {
238 				ui_statusline_msg("Error - unable to re-load the password file!");
239 				break;
240 			}
241 
242 			time_base = time(NULL);
243 		}
244 
245 		switch(ch){
246 			case 'Q':
247 			case 'q':
248 				if(search_results != NULL) {
249 					search_remove();
250 				} else {
251 					if(action_list_at_top_level()){
252 						return 0;
253 					}
254 				}
255 				break;
256 			case '?':
257 				ui_display_help();
258 				break;
259 			case KEY_PPAGE:
260 				uilist_page_up();
261 				break;
262 			case KEY_NPAGE:
263 				uilist_page_down();
264 				break;
265 			case KEY_UP:
266 			case 'k':
267 				uilist_up();
268 				break;
269 			case KEY_DOWN:
270 			case 'j':
271 				uilist_down();
272 				break;
273 			case 'A':
274 				if (!options->readonly){
275 					action_list_add_sublist();
276 				} else {
277 					statusline_readonly();
278 				}
279 				break;
280 			case 'U':
281 				action_list_up_one_level();
282 				break;
283 			case 'r':
284 				if (!options->readonly){
285 					action_list_rename();
286 					pwlist_write_file();
287 				} else {
288 					statusline_readonly();
289 				}
290 				break;
291 			case 'a':
292 				if (!options->readonly){
293 					action_list_add_pw();
294 					pwlist_write_file();
295 				} else {
296 					statusline_readonly();
297 				}
298 				break;
299 			case 'e':
300 			case ' ':
301 			case 13: /* return/enter key */
302 				action_list_select_item();
303 				/*current_item = get_current_item();
304 				if(current_item){
305 					edit_pw(current_item);
306 				}*/
307 				break;
308 			case 'd':
309 			case 0x14A: /* DEL key */
310 				if (!options->readonly){
311 					action_list_delete_item();
312 				} else {
313 					statusline_readonly();
314 				}
315 				break;
316 			case 'm':
317 				if (!options->readonly){
318 					action_list_move_item();
319 				} else {
320 					statusline_readonly();
321 				}
322 				break;
323 			case 'M':
324 				if (!options->readonly){
325 					action_list_move_item_up_level();
326 				} else {
327 					statusline_readonly();
328 				}
329 				break;
330 			case 'h':
331 				hide_cursor();
332 				break;
333 				case 's':
334 				show_cursor();
335 				break;
336 			case 'o':
337 				action_edit_options();
338 				break;
339 			case 0x17: /* control-w */
340 				if (!options->readonly){
341 					pwlist_write_file();
342 				} else {
343 					statusline_readonly();
344 				}
345 				break;
346 			case 0x12: /* control-r */
347 				action_list_read_file();
348 				break;
349 			case 0x07: /* control-g */
350 				pwgen_indep();
351 				break;
352 			case 0x06: /* control-f */
353 				gnupg_forget_passphrase();
354 				break;
355 			case 0x0C: /* control-l */
356 				ui_refresh_windows();
357 				break;
358 			case '/':
359 			case 'F':
360 				search_get();
361 				break;
362 			case 'f':
363 				filter_get();
364 				break;
365 			case 'E':
366 				action_list_export();
367 				break;
368 			case 'I':
369 				if (!options->readonly){
370 					pwlist_import_passwd();
371 					uilist_refresh();
372 				} else {
373 					statusline_readonly();
374 				}
375 				break;
376 			case 'L':
377 				action_list_locate();
378 				break;
379 			case 'l':
380 				action_list_launch();
381 				break;
382 			case 0x0B: /* control-k (up) */
383 			case '[':
384 				action_list_move_item_up();
385 				break;
386 			case 0x0A: /* control-j (down) */
387 			case ']':
388 				action_list_move_item_down();
389 				break;
390 #ifdef DEBUG
391 			case '$':
392 				debug_i++;
393 				snprintf(msg, 80, "Name %d", debug_i);
394 
395 				pwlist_add(current_pw_sublist, msg, "myhost", "myuser", "mypasswd", "mylaucnh");
396 				uilist_refresh();
397 				break;
398 #endif
399 			default:
400 				break;
401 		}
402 	}
403 	return 0;
404 }
405 
406 int
ui_end()407 ui_end()
408 {
409 	ui_free_windows();
410 	clear();
411 	refresh();
412 	endwin();
413 	echo();
414 	return 0;
415 }
416 
417 int
ui_statusline_msg(char * msg)418 ui_statusline_msg(char * msg)
419 {
420 	ui_statusline_clear();
421 	mvwaddstr(bottom, 1, 0, msg);
422 	refresh();
423 	wrefresh(bottom);
424 	return 0;
425 }
426 
427 int
ui_statusline_clear()428 ui_statusline_clear()
429 {
430 	wmove(bottom, 1, 0);
431 	wclrtoeol(bottom);
432 	wrefresh(bottom);
433 	refresh();
434 	return 0;
435 }
436 
437 void
ui_statusline_ask_num(char * msg,int * i)438 ui_statusline_ask_num(char *msg, int *i)
439 {
440 	int x = strlen(msg) + 5;
441 	char input[STRING_SHORT];
442 
443 	ui_statusline_clear();
444 	ui_statusline_msg(msg);
445 
446 	echo();
447 	show_cursor();
448 
449 	mvwgetnstr(bottom, 1, x, input, STRING_SHORT);
450 	*i = atoi(input);
451 
452 	noecho();
453 	hide_cursor();
454 
455 	ui_statusline_clear();
456 }
457 
458 void
ui_statusline_ask_bool(char * base_msg,int * i)459 ui_statusline_ask_bool(char *base_msg, int *i)
460 {
461    char v;
462    char* bool_prompt = " (Y)es (N)o";
463    int base_len = strlen(base_msg);
464 
465    // Build the new message, with the boolean prompts in it
466    char msg[base_len+strlen(bool_prompt)];
467    strcpy(msg, base_msg);
468    if (base_msg[base_len-2] == ':' && base_msg[base_len-1] == '\t') {
469       // Special case - put it before the :\t
470       strcpy(msg+base_len-2, bool_prompt);
471       strcpy(msg+base_len+strlen(bool_prompt)-2, ":\t");
472    } else {
473       // Append the YN bit after the main message
474       strcpy(msg+base_len, bool_prompt);
475    }
476 
477    ui_statusline_ask_char(msg, &v, "YyNn");
478    if (v == 'Y' || v == 'y') {
479       *i = 1;
480    } else {
481       *i = 0;
482    }
483 }
484 
485 void
ui_statusline_ask_char(char * msg,char * c,char * valid)486 ui_statusline_ask_char(char *msg, char *c, char* valid)
487 {
488 	int x = strlen(msg) + 5;
489 	char input[STRING_SHORT];
490 
491 	*c = 0;
492 	do {
493 		ui_statusline_clear();
494 		if(*c != 0){
495 			ui_statusline_msg("Bad choice, press any key to try again");
496 			getch();
497 			ui_statusline_clear();
498 		}
499 		ui_statusline_msg(msg);
500 
501 		echo();
502 		show_cursor();
503 
504 		*c = mvwgetch(bottom, 1, x);
505 
506 		noecho();
507 		hide_cursor();
508 
509 	} while ( !strchr(valid, *c) );
510 
511 	ui_statusline_clear();
512 }
513 
514 char *
ui_statusline_ask_str(char * msg,char * input,int len)515 ui_statusline_ask_str(char *msg, char *input, int len)
516 {
517 	char *tmp;
518 	char *tmp2;
519 	char *tmp3;
520 	int x = strlen(msg) + 5;
521 
522 	if(input == NULL){
523 		input = malloc(len);
524 	}
525 	ui_statusline_clear();
526 	ui_statusline_msg(msg);
527 
528 	echo();
529 	show_cursor();
530 	mvwgetnstr(bottom, 1, x, input, len);
531 	noecho();
532 	hide_cursor();
533 
534 	ui_statusline_clear();
535 
536 	// Tabs don't play nicely with ncurses or xml
537 	// So, swap any for (a single) space
538 	tmp = input;
539 	while(*tmp != 0) {
540 		if(*tmp == 9) *tmp = ' ';
541 		tmp++;
542 	}
543 
544 	// In some cases (eg when inside screen), the backspace
545 	// comes through to us. Handle it here if needed
546 	tmp = input;
547 	while(*tmp != 0) {
548 		if(*tmp == 8) {
549          // tmp2 is where to copy to, tmp3 is where to copy from
550          tmp3 = tmp + 1;
551          if(tmp == input) {
552             tmp2 = tmp;
553          } else {
554             tmp2 = tmp - 1;
555          }
556 
557          // When we're done, start from the character
558          //  we copied in to
559          tmp = tmp2;
560 
561          // Process forward
562          while(*tmp3 != 0) {
563             *tmp2 = *tmp3;
564             tmp2++;
565             tmp3++;
566          }
567          *tmp2 = 0;
568       } else {
569    		tmp++;
570       }
571 	}
572 
573 	// All done
574 	return input;
575 }
576 
577 char *
ui_statusline_ask_str_with_autogen(char * msg,char * input,int len,char * (* autogen)(char *),int ch)578 ui_statusline_ask_str_with_autogen(char *msg, char *input, int len, char *(*autogen)(char *), int ch)
579 {
580 	int i = 0;
581 	int c;
582 	char *text[2], *s;
583 	int x;
584 
585 	if(input == NULL){
586 		input = malloc(len);
587 	}
588 	text[0] = malloc(STRING_MEDIUM);
589 	text[1] = malloc(STRING_SHORT);
590 
591 	strncpy(text[1], msg, STRING_SHORT);
592 	if(s = strrchr(text[1], ':')){
593 		*s = 0;
594 	}
595 	snprintf(text[0], STRING_MEDIUM, "%s(%c for autogen):\t", text[1],ch);
596 	x = strlen(text[0]) + 5;
597 
598 	ui_statusline_clear();
599 	ui_statusline_msg(text[0]);
600 
601 	show_cursor();
602 	noecho();
603 
604 	wmove(bottom, 1, x);
605 
606 	while(i < len){
607 		c = wgetch(bottom);
608 		if(c == 0x7f){
609 			if(i){
610 				i--;
611 				mvwaddch(bottom, 1, x+i, ' ');
612 				wmove(bottom, 1, x+i);
613 			}
614 		} else if(c == 0xd){
615 			input[i] = 0;
616 			break;
617 		} else if(c == ch){
618 			input = autogen(input);
619 			break;
620 		} else {
621 			input[i] = c;
622 			mvwaddch(bottom, 1, x + i, c);
623 			i++;
624 		}
625 	}
626 
627 	hide_cursor();
628 
629 	ui_statusline_clear();
630 
631 	free(text[0]);
632 	free(text[1]);
633 
634 	return input;
635 }
636 
637 char *
ui_statusline_ask_passwd(char * msg,char * input,int len,int cancel)638 ui_statusline_ask_passwd(char *msg, char *input, int len, int cancel)
639 {
640 	int i = 0;
641 	int c;
642 	int x = strlen(msg) + 5;
643 
644 	if(!input){
645 		input = malloc(len);
646 	}
647 	ui_statusline_clear();
648 	ui_statusline_msg(msg);
649 
650 	show_cursor();
651 	noecho();
652 
653 	wmove(bottom, 1, x);
654 
655 	while(i < len){
656 		c = wgetch(bottom);
657 		if(c == 0x7f){ /* 0x7f = delete */
658 			if(i){
659 				i--;
660 				mvwaddch(bottom, 1, x+i, ' ');
661 				wmove(bottom, 1, x+i);
662 			}
663 		} else if(c == 0xd){ /* 0xd == enter/return */
664 			input[i] = 0;
665 			break;
666 		} else if(c == cancel){
667 			free(input);
668 			input = NULL;
669 
670 			return input;
671 		} else {
672 			input[i] = c;
673 			mvwaddch(bottom, 1, x + i, '*');
674 			i++;
675 		}
676 	}
677 
678 	hide_cursor();
679 
680 	ui_statusline_clear();
681 
682 	return input;
683 }
684 
685 int
ui_statusline_yes_no(char * msg,int def)686 ui_statusline_yes_no(char *msg, int def)
687 {
688 	int ret = -1, len;
689 	char *msg2;
690 	int ch;
691 
692 	len = strlen(msg) + 10;
693 	msg2 = malloc(len);
694 
695 	snprintf(msg2, len,  "%s%s", msg, def ? " (Y/n)?" : " (y/N)?", NULL);
696 
697 	while(ret == -1){
698 		ui_statusline_msg(msg2);
699 
700 		ch = getch();
701 		switch( ch ){
702 			case 'n':
703 			case 'N':
704 				ret = FALSE;
705 				break;
706 			case 'y':
707 			case 'Y':
708 				ret = TRUE;
709 				break;
710 			case 13:
711 				ret = def;
712 				break;
713 			default:
714 				ui_statusline_msg("Bad option, try again.");
715 				getch();
716 				break;
717 		}
718 	}
719 
720 	free(msg2);
721 	ui_statusline_clear();
722 
723 	return ret;
724 }
725 
726 void
statusline_readonly()727 statusline_readonly()
728 {
729 	ui_statusline_msg("Password file is opened readonly");
730 }
731