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