"Fossies" - the Fresh Open Source Software Archive 
Member "jpilot-2_0_1/KeyRing/keyring.c" (3 Apr 2021, 101505 Bytes) of package /linux/privat/jpilot-2_0_1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /*******************************************************************************
2 * keyring.c
3 *
4 * This is a plugin for J-Pilot for the KeyRing Palm program.
5 * It keeps records and uses DES3 encryption.
6 *
7 * Copyright (C) 2001-2014 by Judd Montgomery
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 ******************************************************************************/
22
23 /********************************* Includes ***********************************/
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <sys/stat.h>
31 #include <gtk/gtk.h>
32
33 #include "config.h"
34
35 #ifdef HAVE_LIBGCRYPT
36
37 # include <gcrypt.h>
38
39 #else
40 /* OpenSSL header files */
41 # include <openssl/md5.h>
42 # include <openssl/des.h>
43
44 #endif
45
46 /* Pilot-link header files */
47 #include <pi-appinfo.h>
48 #include <pi-dlp.h>
49 #include <pi-file.h>
50
51 /* Jpilot header files */
52 #include "libplugin.h"
53 #include "utils.h"
54 #include "i18n.h"
55 #include "prefs.h"
56 #include "stock_buttons.h"
57 #include "export.h"
58
59 /********************************* Constants **********************************/
60 #define KEYRING_CAT1 1
61 #define KEYRING_CAT2 2
62 #define NUM_KEYRING_CAT_ITEMS 16
63
64 #define PASSWD_ENTER 0
65 #define PASSWD_ENTER_RETRY 1
66 #define PASSWD_ENTER_NEW 2
67
68 #define PASSWD_LEN 100
69
70 #define CONNECT_SIGNALS 400
71 #define DISCONNECT_SIGNALS 401
72 #define PASSWD_FLAG 1
73
74 #define KEYR_CHGD_COLUMN 0
75 #define KEYR_NAME_COLUMN 1
76 #define KEYR_ACCT_COLUMN 2
77
78 /* re-ask password PLUGIN_MAX_INACTIVE_TIME seconds after
79 * deselecting the plugin */
80 #define PLUGIN_MAX_INACTIVE_TIME 10
81
82 /* for password hashes */
83 #define SALT_SIZE 4
84 #define MESSAGE_BUF_SIZE 64
85 #define MD5_HASH_SIZE 16
86
87 #define MIN_KR_PASS (20) /* Minimum auto-generated passwd length */
88 #define MAX_KR_PASS (25) /* Maximum auto-generated passwd length */
89
90 enum {
91 KEYRING_CHANGED_COLUMN_ENUM,
92 KEYRING_NAME_COLUMN_ENUM,
93 KEYRING_ACCOUNT_COLUMN_ENUM,
94 KEYRING_DATA_COLUMN_ENUM,
95 KEYRING_BACKGROUND_COLOR_ENUM,
96 KEYRING_BACKGROUND_COLOR_ENABLED_ENUM,
97 KEYRING_FOREGROUND_COLOR_ENUM,
98 KEYRINGS_FOREGROUND_COLOR_ENABLED_ENUM,
99 KEYRING_NUM_COLS
100 };
101 struct KeyRing {
102 char *name; /* Unencrypted */
103 char *account; /* Encrypted */
104 char *password; /* Encrypted */
105 char *note; /* Encrypted */
106 struct tm last_changed; /* Encrypted */
107 };
108 /* My wrapper to the KeyRing structure so that I can put a few more
109 * fields in with it. */
110 struct MyKeyRing {
111 PCRecType rt;
112 unsigned int unique_id;
113 unsigned char attrib;
114 struct KeyRing kr;
115 struct MyKeyRing *next;
116 };
117
118 /******************************* Global vars **********************************/
119 /* This is the category that is currently being displayed */
120 static struct CategoryAppInfo keyr_app_info;
121 static int keyr_category = CATEGORY_ALL;
122
123 static GtkWidget *treeView;
124 static GtkListStore *listStore;
125 static GtkWidget *entry_name;
126 static GtkWidget *entry_account;
127 static GtkWidget *entry_password;
128 static GtkWidget *keyr_note;
129 static GObject *keyr_note_buffer;
130 static GtkWidget *category_menu1;
131 static GtkWidget *category_menu2;
132 static struct sorted_cats sort_l[NUM_KEYRING_CAT_ITEMS];
133 static GtkWidget *pane = NULL;
134 static GtkWidget *scrolled_window;
135 static GtkWidget *new_record_button;
136 static GtkWidget *apply_record_button;
137 static GtkWidget *add_record_button;
138 static GtkWidget *delete_record_button;
139 static GtkWidget *undelete_record_button;
140 static GtkWidget *copy_record_button;
141 static GtkWidget *cancel_record_button;
142 static GtkWidget *date_button;
143 static struct tm glob_date;
144 #ifndef ENABLE_STOCK_BUTTONS
145 static GtkAccelGroup *accel_group;
146 #endif
147 static int record_changed;
148 static int column_selected;
149 static int row_selected;
150
151 #ifdef HAVE_LIBGCRYPT
152 static unsigned char key[24];
153 #else
154 #ifdef HEADER_NEW_DES_H
155 static DES_cblock current_key1;
156 static DES_cblock current_key2;
157 static DES_key_schedule s1, s2;
158 #else
159 static des_cblock current_key1;
160 static des_cblock current_key2;
161 static des_key_schedule s1, s2;
162 #endif
163 #endif
164
165 static time_t plugin_last_time = 0;
166 static gboolean plugin_active = FALSE;
167
168 static struct MyKeyRing *glob_keyring_list = NULL;
169 static struct MyKeyRing *export_keyring_list = NULL;
170
171 /****************************** Prototypes ************************************/
172
173 void keyr_update_liststore(GtkListStore *pListStore, struct MyKeyRing **keyring_list,
174 int category, int main);
175
176 static void connect_changed_signals(int con_or_dis);
177
178 static int keyring_find(int unique_id);
179
180 static void update_date_button(GtkWidget *button, struct tm *t);
181
182 static gboolean handleKeyringRowSelection(GtkTreeSelection *selection,
183 GtkTreeModel *model,
184 GtkTreePath *path,
185 gboolean path_currently_selected,
186 gpointer userdata);
187
188 void deleteKeyRing(struct MyKeyRing *mkr, gpointer data);
189
190 void undeleteKeyRing(struct MyKeyRing *mkr, gpointer data);
191
192 void addKeyRing(struct MyKeyRing *mkr, gpointer data);
193
194 static GtkWidget *cb_keyr_export_init_treeView();
195
196 /****************************** Main Code *************************************/
197
198 /* Routine to get category app info from raw buffer.
199 * KeyRing is broken and uses a non-standard length CategoryAppInfo.
200 * The KeyRing structure is 276 bytes whereas pilot-link uses 278.
201 * Code below is taken from unpack_CategoryAppInfo in pilot-link but modified
202 * for the shortened structure. */
203 static int keyr_plugin_unpack_cai_from_ai(struct CategoryAppInfo *cai,
204 unsigned char *record,
205 int len) {
206 int i, rec;
207
208 jp_logf(JP_LOG_DEBUG, "unpack_keyring_cai_from_ai\n");
209
210 if (len < 2 + 16 * 16 + 16 + 2)
211 return EXIT_FAILURE;
212 rec = get_short(record);
213 for (i = 0; i < 16; i++) {
214 if (rec & (1 << i))
215 cai->renamed[i] = 1;
216 else
217 cai->renamed[i] = 0;
218 }
219 record += 2;
220 for (i = 0; i < 16; i++) {
221 memcpy(cai->name[i], record, 16);
222 record += 16;
223 }
224 memcpy(cai->ID, record, 16);
225 record += 16;
226 cai->lastUniqueID = get_byte(record);
227
228 return EXIT_SUCCESS;
229 }
230
231 int plugin_unpack_cai_from_ai(struct CategoryAppInfo *cai,
232 unsigned char *record,
233 int len) {
234 return keyr_plugin_unpack_cai_from_ai(cai, record, len);
235 }
236
237 /* Routine to pack CategoryAppInfo struct into non-standard size buffer */
238 int plugin_pack_cai_into_ai(struct CategoryAppInfo *cai,
239 unsigned char *record,
240 int len) {
241 int i, rec;
242
243 if (!record) {
244 return EXIT_SUCCESS;
245 }
246 if (len < (2 + 16 * 16 + 16 + 2))
247 return EXIT_FAILURE; /* not enough room */
248 rec = 0;
249 for (i = 0; i < 16; i++) {
250 if (cai->renamed[i])
251 rec |= (1 << i);
252 }
253 set_short(record, rec);
254 record += 2;
255 for (i = 0; i < 16; i++) {
256 memcpy(record, cai->name[i], 16);
257 record += 16;
258 }
259 memcpy(record, cai->ID, 16);
260 record += 16;
261 set_byte(record, cai->lastUniqueID);
262 record++;
263 set_byte(record, 0); /* gapfill */
264
265 return EXIT_SUCCESS;
266 }
267
268 static int pack_KeyRing(struct KeyRing *kr,
269 unsigned char *buf,
270 int buf_size,
271 int *wrote_size) {
272 int n;
273 int i;
274 char empty[] = "";
275 char last_changed[2];
276 unsigned short packed_date;
277 #ifdef HAVE_LIBGCRYPT
278 gcry_error_t err;
279 gcry_cipher_hd_t hd;
280 #endif
281
282 jp_logf(JP_LOG_DEBUG, "KeyRing: pack_KeyRing()\n");
283
284 packed_date = (((kr->last_changed.tm_year - 4) << 9) & 0xFE00) |
285 (((kr->last_changed.tm_mon + 1) << 5) & 0x01E0) |
286 (kr->last_changed.tm_mday & 0x001F);
287 set_short(last_changed, packed_date);
288
289 *wrote_size = 0;
290
291 if (!(kr->name)) kr->name = empty;
292 if (!(kr->account)) kr->account = empty;
293 if (!(kr->password)) kr->password = empty;
294 if (!(kr->note)) kr->note = empty;
295
296 /* 2 is for the lastChanged date */
297 /* 3 chars accounts for NULL string terminators */
298 n = strlen(kr->account) + strlen(kr->password) + strlen(kr->note) + 2 + 3;
299 /* The encrypted portion must be a multiple of 8 */
300 if ((n % 8)) {
301 n = n + (8 - (n % 8));
302 }
303 /* Now we can add in the unencrypted part */
304 n = n + strlen(kr->name) + 1;
305 jp_logf(JP_LOG_DEBUG, "pack n=%d\n", n);
306
307 if (n + 2 > buf_size) {
308 jp_logf(JP_LOG_WARN, _("KeyRing: pack_KeyRing(): buf_size too small\n"));
309 return EXIT_FAILURE;
310 }
311
312 memset(buf, 0, n + 1);
313 *wrote_size = n;
314 strcpy((char *) buf, kr->name);
315 i = strlen(kr->name) + 1;
316 strcpy((char *) &buf[i], kr->account);
317 i += strlen(kr->account) + 1;
318 strcpy((char *) &buf[i], kr->password);
319 i += strlen(kr->password) + 1;
320 strcpy((char *) &buf[i], kr->note);
321 i += strlen(kr->note) + 1;
322 strncpy((char *) &buf[i], last_changed, 2);
323 #ifdef HAVE_LIBGCRYPT
324 err = gcry_cipher_open(&hd, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0);
325 if (err)
326 jp_logf(JP_LOG_DEBUG, "gcry_cipher_open: %s\n", gpg_strerror(err));
327
328 err = gcry_cipher_setkey(hd, key, sizeof(key));
329 if (err)
330 jp_logf(JP_LOG_DEBUG, "gcry_cipher_setkey: %s\n", gpg_strerror(err));
331
332 for (i = strlen(kr->name) + 1; i < n; i += 8) {
333 char tmp[8];
334 err = gcry_cipher_encrypt(hd, tmp, 8, &buf[i], 8);
335 if (err)
336 jp_logf(JP_LOG_DEBUG, "gcry_cipher_encrypt: %s\n", gpg_strerror(err));
337 memcpy(&buf[i], tmp, 8);
338 }
339
340 gcry_cipher_close(hd);
341 #else
342 for (i=strlen(kr->name)+1; i<n; i=i+8) {
343 #ifdef HEADER_NEW_DES_H
344 DES_ecb3_encrypt((DES_cblock *)&buf[i], (DES_cblock *)&buf[i],
345 &s1, &s2, &s1, DES_ENCRYPT);
346 #else
347 des_ecb3_encrypt((const_des_cblock *)&buf[i], (des_cblock *)(&buf[i]),
348 s1, s2, s1, DES_ENCRYPT);
349 #endif
350 }
351 #endif
352
353 #ifdef JPILOT_DEBUG
354 for (i=0;i<n; i++) {
355 printf("%02x ", (unsigned char)buf[i]);
356 }
357 printf("\n");
358 #endif
359
360 return n;
361 }
362
363 static int unpack_KeyRing(struct KeyRing *kr,
364 unsigned char *buf,
365 int buf_size) {
366 int i, j;
367 int n;
368 int rem;
369 unsigned char *clear_text;
370 unsigned char *P;
371 unsigned char *Pstr[4];
372 const char *safety[] = {"", "", "", ""};
373 unsigned short packed_date;
374 #ifdef HAVE_LIBGCRYPT
375 gcry_error_t err;
376 gcry_cipher_hd_t hd;
377 #endif
378
379 jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing\n");
380 if (!memchr(buf, '\0', buf_size)) {
381 jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): No null terminator found in buf\n");
382 return 0;
383 }
384 n = strlen((char *) buf) + 1;
385
386 rem = buf_size - n;
387 if (rem > 0xFFFF) {
388 /* This can be caused by a bug in libplugin.c from jpilot 0.99.1
389 * and before. It occurs on the last record */
390 jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): buffer too big n=%d, buf_size=%d\n", n, buf_size);
391 jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): truncating\n");
392 rem = 0xFFFF - n;
393 rem = rem - (rem % 8);
394 }
395 clear_text = malloc(rem + 8); /* Allow for some safety NULLs */
396 memset(clear_text, 0, rem + 8);
397
398 jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): rem (should be multiple of 8)=%d\n", rem);
399 jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): rem%%8=%d\n", rem % 8);
400
401 P = &buf[n];
402 #ifdef HAVE_LIBGCRYPT
403 err = gcry_cipher_open(&hd, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0);
404 if (err)
405 jp_logf(JP_LOG_DEBUG, "gcry_cipher_open: %s\n", gpg_strerror(err));
406
407 err = gcry_cipher_setkey(hd, key, sizeof(key));
408 if (err)
409 jp_logf(JP_LOG_DEBUG, "gcry_cipher_setkey: %s\n", gpg_strerror(err));
410
411 err = gcry_cipher_decrypt(hd, clear_text, rem, P, rem);
412 if (err)
413 jp_logf(JP_LOG_DEBUG, "gcry_cipher_decrypt: %s\n", gpg_strerror(err));
414
415 gcry_cipher_close(hd);
416 #else
417 for (i=0; i<rem; i+=8) {
418 #ifdef HEADER_NEW_DES_H
419 DES_ecb3_encrypt((DES_cblock *)&P[i], (DES_cblock *)(clear_text+i),
420 &s1, &s2, &s1, DES_DECRYPT);
421 #else
422 des_ecb3_encrypt((const_des_cblock *)&P[i], (des_cblock *)(clear_text+i),
423 s1, s2, s1, DES_DECRYPT);
424 #endif
425 }
426 #endif
427
428 Pstr[0] = clear_text;
429 Pstr[1] = (unsigned char *) safety[1];
430 Pstr[2] = (unsigned char *) safety[2];
431 Pstr[3] = (unsigned char *) safety[3];
432
433 for (i = 0, j = 1; (i < rem) && (j < 4); i++) {
434 if (!clear_text[i]) {
435 Pstr[j] = &clear_text[i + 1];
436 j++;
437 }
438 }
439 /*
440 kr->name=strdup((char *)buf);
441 kr->account=strdup((char *)Pstr[0]);
442 kr->password=strdup((char *)Pstr[1]);
443 kr->note=strdup((char *)Pstr[2]);
444 */
445
446 kr->name = jp_charset_p2newj((char *) buf, -1);
447 kr->account = jp_charset_p2newj((char *) Pstr[0], -1);
448 kr->password = jp_charset_p2newj((char *) Pstr[1], -1);
449 kr->note = jp_charset_p2newj((char *) Pstr[2], -1);
450
451 packed_date = get_short(Pstr[3]);
452 kr->last_changed.tm_year = ((packed_date & 0xFE00) >> 9) + 4;
453 kr->last_changed.tm_mon = ((packed_date & 0x01E0) >> 5) - 1;
454 kr->last_changed.tm_mday = (packed_date & 0x001F);
455 kr->last_changed.tm_hour = 0;
456 kr->last_changed.tm_min = 0;
457 kr->last_changed.tm_sec = 0;
458 kr->last_changed.tm_isdst = -1;
459
460 if (0 == packed_date) {
461 kr->last_changed.tm_year = 0;
462 kr->last_changed.tm_mon = 0;
463 kr->last_changed.tm_mday = 0;
464 }
465
466 #ifdef DEBUG
467 printf("name [%s]\n", buf);
468 printf("Pstr0 [%s]\n", Pstr[0]);
469 printf("Pstr1 [%s]\n", Pstr[1]);
470 printf("Pstr2 [%s]\n", Pstr[2]);
471 printf("last_changed %d-%d-%d\n",
472 kr->last_changed.tm_year,
473 kr->last_changed.tm_mon,
474 kr->last_changed.tm_mday);
475 #endif
476
477 free(clear_text);
478
479 return 1;
480 }
481
482 static int get_keyr_cat_info(struct CategoryAppInfo *cai) {
483 unsigned char *buf;
484 int buf_size;
485
486 memset(cai, 0, sizeof(struct CategoryAppInfo));
487 jp_get_app_info("Keys-Gtkr", &buf, &buf_size);
488 keyr_plugin_unpack_cai_from_ai(cai, buf, buf_size);
489 free(buf);
490
491 return EXIT_SUCCESS;
492 }
493
494 /*
495 * Return EXIT_FAILURE if password isn't good.
496 * Return EXIT_SUCCESS if good and global and also sets s1, and s2 set
497 */
498 static int set_password_hash(unsigned char *buf, int buf_size, char *passwd) {
499 unsigned char buffer[MESSAGE_BUF_SIZE];
500 unsigned char md[MD5_HASH_SIZE];
501
502 if (buf_size < MD5_HASH_SIZE) {
503 return EXIT_FAILURE;
504 }
505 /* Must wipe passwd out of memory after using it */
506 memset(buffer, 0, MESSAGE_BUF_SIZE);
507 memcpy(buffer, buf, SALT_SIZE);
508 strncpy((char *) (buffer + SALT_SIZE), passwd, MESSAGE_BUF_SIZE - SALT_SIZE - 1);
509 #ifdef HAVE_LIBGCRYPT
510 gcry_md_hash_buffer(GCRY_MD_MD5, md, buffer, MESSAGE_BUF_SIZE);
511 #else
512 MD5(buffer, MESSAGE_BUF_SIZE, md);
513 #endif
514
515 /* wipe out password traces */
516 memset(buffer, 0, MESSAGE_BUF_SIZE);
517
518 if (memcmp(md, buf + SALT_SIZE, MD5_HASH_SIZE)) {
519 return EXIT_FAILURE;
520 }
521
522 #ifdef HAVE_LIBGCRYPT
523 gcry_md_hash_buffer(GCRY_MD_MD5, md, passwd, strlen(passwd));
524 memcpy(key, md, 16); /* k1 and k2 */
525 memcpy(key + 16, md, 8); /* k1 again */
526 #else
527 MD5((unsigned char *)passwd, strlen(passwd), md);
528 memcpy(current_key1, md, 8);
529 memcpy(current_key2, md+8, 8);
530 #ifdef HEADER_NEW_DES_H
531 DES_set_key(¤t_key1, &s1);
532 DES_set_key(¤t_key2, &s2);
533 #else
534 des_set_key(¤t_key1, s1);
535 des_set_key(¤t_key2, s2);
536 #endif
537 #endif
538
539 return EXIT_SUCCESS;
540 }
541
542 /* Start password change code */
543
544 /*
545 * Code for this is written, just need to add another jpilot API for
546 * cancelling a sync if the passwords don't match.
547 */
548
549 /* End password change code */
550
551 /* Utility function to read keyring data file and filter out unwanted records
552 *
553 * Returns the number of records read */
554 static int get_keyring(struct MyKeyRing **mkr_list, int category) {
555 GList *records = NULL;
556 GList *temp_list;
557 buf_rec *br;
558 struct MyKeyRing *mkr;
559 int rec_count;
560 long keep_modified, keep_deleted;
561
562 jp_logf(JP_LOG_DEBUG, "get_keyring()\n");
563
564 *mkr_list = NULL;
565 rec_count = 0;
566
567 /* Read raw database of records */
568 if (jp_read_DB_files("Keys-Gtkr", &records) == -1)
569 return 0;
570
571 /* Get preferences used for filtering */
572 get_pref(PREF_SHOW_MODIFIED, &keep_modified, NULL);
573 get_pref(PREF_SHOW_DELETED, &keep_deleted, NULL);
574
575 /* Sort through list of records masking out unwanted ones */
576 for (temp_list = records; temp_list; temp_list = temp_list->next) {
577 if (temp_list->data) {
578 br = temp_list->data;
579 } else {
580 continue;
581 }
582 if (!br->buf) {
583 continue;
584 }
585 /* record 0 is the hash-key record */
586 if (br->attrib & dlpRecAttrSecret) {
587 continue;
588 }
589
590 /* Filter out deleted or deleted/modified records */
591 if (((br->rt == DELETED_PALM_REC) && (!keep_deleted)) ||
592 ((br->rt == DELETED_PC_REC) && (!keep_deleted)) ||
593 ((br->rt == MODIFIED_PALM_REC) && (!keep_modified))) {
594 continue;
595 }
596
597 /* Filter by category */
598 if (((br->attrib & 0x0F) != category) && category != CATEGORY_ALL) {
599 continue;
600 }
601
602 mkr = malloc(sizeof(struct MyKeyRing));
603 mkr->next = NULL;
604 mkr->attrib = br->attrib;
605 mkr->unique_id = br->unique_id;
606 mkr->rt = br->rt;
607
608 if (unpack_KeyRing(&(mkr->kr), br->buf, br->size) <= 0) {
609 free(mkr);
610 continue;
611 }
612
613 /* prepend to list */
614 mkr->next = *mkr_list;
615 *mkr_list = mkr;
616
617 rec_count++;
618 }
619
620 jp_free_DB_records(&records);
621
622 jp_logf(JP_LOG_DEBUG, "Leaving get_keyring()\n");
623
624 return rec_count;
625 }
626
627 static void set_new_button_to(int new_state) {
628 jp_logf(JP_LOG_DEBUG, "set_new_button_to new %d old %d\n", new_state, record_changed);
629
630 if (record_changed == new_state) {
631 return;
632 }
633
634 switch (new_state) {
635 case MODIFY_FLAG:
636 gtk_widget_show(cancel_record_button);
637 gtk_widget_show(copy_record_button);
638 gtk_widget_show(apply_record_button);
639
640 gtk_widget_hide(add_record_button);
641 gtk_widget_hide(delete_record_button);
642 gtk_widget_hide(new_record_button);
643 gtk_widget_hide(undelete_record_button);
644
645 break;
646 case NEW_FLAG:
647 gtk_widget_show(cancel_record_button);
648 gtk_widget_show(add_record_button);
649
650 gtk_widget_hide(apply_record_button);
651 gtk_widget_hide(copy_record_button);
652 gtk_widget_hide(delete_record_button);
653 gtk_widget_hide(new_record_button);
654 gtk_widget_hide(undelete_record_button);
655
656 break;
657 case CLEAR_FLAG:
658 gtk_widget_show(delete_record_button);
659 gtk_widget_show(copy_record_button);
660 gtk_widget_show(new_record_button);
661
662 gtk_widget_hide(add_record_button);
663 gtk_widget_hide(apply_record_button);
664 gtk_widget_hide(cancel_record_button);
665 gtk_widget_hide(undelete_record_button);
666
667 break;
668 case UNDELETE_FLAG:
669 gtk_widget_show(undelete_record_button);
670 gtk_widget_show(copy_record_button);
671 gtk_widget_show(new_record_button);
672
673 gtk_widget_hide(add_record_button);
674 gtk_widget_hide(apply_record_button);
675 gtk_widget_hide(cancel_record_button);
676 gtk_widget_hide(delete_record_button);
677 break;
678
679 default:
680 return;
681 }
682
683 record_changed = new_state;
684 }
685
686 /* Find position of category in sorted category array
687 * via its assigned category number */
688 static int find_sort_cat_pos(int cat) {
689 int i;
690
691 for (i = 0; i < NUM_KEYRING_CAT_ITEMS; i++) {
692 if (sort_l[i].cat_num == cat) {
693 return i;
694 }
695 }
696
697 return -1;
698 }
699
700 /* Find a category's position in the category menu.
701 * This is equal to the category number except for the Unfiled category.
702 * The Unfiled category is always in the last position which changes as
703 * the number of categories changes */
704 static int find_menu_cat_pos(int cat) {
705 int i;
706
707 if (cat != NUM_KEYRING_CAT_ITEMS - 1) {
708 return cat;
709 } else { /* Unfiled category */
710 /* Count how many category entries are filled */
711 for (i = 0; i < NUM_KEYRING_CAT_ITEMS; i++) {
712 if (!sort_l[i].Pcat[0]) {
713 return i;
714 }
715 }
716 return 0;
717 }
718 }
719
720
721 static gint GtkTreeModelKeyrCompareDates(GtkTreeModel *model,
722 GtkTreeIter *left,
723 GtkTreeIter *right,
724 gpointer columnId) {
725
726
727 struct MyKeyRing *mkr1, *mkr2;
728
729 struct KeyRing *keyr1, *keyr2;
730 time_t time1, time2;
731 gtk_tree_model_get(GTK_TREE_MODEL(model), left, KEYRING_DATA_COLUMN_ENUM, &mkr1, -1);
732 gtk_tree_model_get(GTK_TREE_MODEL(model), right, KEYRING_DATA_COLUMN_ENUM, &mkr2, -1);
733 keyr1 = &(mkr1->kr);
734 keyr2 = &(mkr2->kr);
735
736 time1 = mktime(&(keyr1->last_changed));
737 time2 = mktime(&(keyr2->last_changed));
738
739 return (time1 - time2);
740 }
741
742 /* Function is used to sort list case insensitively
743 * not sure if this is really needed as the default sort seems to do the same thing
744 */
745 static gint GtkTreeModelKeyrCompareNocase(GtkTreeModel *model,
746 GtkTreeIter *left,
747 GtkTreeIter *right,
748 gpointer columnId) {
749
750
751 gchar *str1, *str2;
752 gtk_tree_model_get(GTK_TREE_MODEL(model), left, KEYRING_NAME_COLUMN_ENUM, &str1, -1);
753 gtk_tree_model_get(GTK_TREE_MODEL(model), right, KEYRING_NAME_COLUMN_ENUM, &str2, -1);
754 return g_ascii_strcasecmp(str1, str2);
755 }
756
757 static void cb_record_changed(GtkWidget *widget, gpointer data) {
758 int flag;
759 struct tm *now;
760 time_t ltime;
761
762 jp_logf(JP_LOG_DEBUG, "cb_record_changed\n");
763
764 flag = GPOINTER_TO_INT(data);
765
766 if (record_changed == CLEAR_FLAG) {
767 connect_changed_signals(DISCONNECT_SIGNALS);
768 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL) > 0) {
769 set_new_button_to(MODIFY_FLAG);
770 /* Update the lastChanged field when password is modified */
771 if (flag == PASSWD_FLAG) {
772 time(<ime);
773 now = localtime(<ime);
774 memcpy(&glob_date, now, sizeof(struct tm));
775 update_date_button(date_button, &glob_date);
776 }
777 } else {
778 set_new_button_to(NEW_FLAG);
779 }
780 } else if (record_changed == UNDELETE_FLAG) {
781 jp_logf(JP_LOG_INFO | JP_LOG_GUI,
782 _("This record is deleted.\n"
783 "Undelete it or copy it to make changes.\n"));
784 }
785 }
786
787 static void connect_changed_signals(int con_or_dis) {
788 static int connected = 0;
789
790 /* CONNECT */
791 if ((con_or_dis == CONNECT_SIGNALS) && (!connected)) {
792 jp_logf(JP_LOG_DEBUG, "KeyRing: connect_changed_signals\n");
793 connected = 1;
794
795 if(category_menu2){
796 g_signal_connect(G_OBJECT(category_menu2),"changed",G_CALLBACK(cb_record_changed),NULL);
797 }
798
799 g_signal_connect(G_OBJECT(entry_name), "changed",
800 G_CALLBACK(cb_record_changed), NULL);
801 g_signal_connect(G_OBJECT(entry_account), "changed",
802 G_CALLBACK(cb_record_changed), NULL);
803 g_signal_connect(G_OBJECT(entry_password), "changed",
804 G_CALLBACK(cb_record_changed),
805 GINT_TO_POINTER(PASSWD_FLAG));
806 g_signal_connect(G_OBJECT(date_button), "pressed",
807 G_CALLBACK(cb_record_changed), NULL);
808 g_signal_connect(keyr_note_buffer, "changed",
809 G_CALLBACK(cb_record_changed), NULL);
810 }
811
812 /* DISCONNECT */
813 if ((con_or_dis == DISCONNECT_SIGNALS) && (connected)) {
814 jp_logf(JP_LOG_DEBUG, "KeyRing: disconnect_changed_signals\n");
815 connected = 0;
816
817 if(category_menu2) {
818 g_signal_handlers_disconnect_by_func(G_OBJECT(category_menu2), G_CALLBACK(cb_record_changed), NULL);
819 }
820
821 g_signal_handlers_disconnect_by_func(G_OBJECT(entry_name),
822 G_CALLBACK(cb_record_changed), NULL);
823 g_signal_handlers_disconnect_by_func(G_OBJECT(entry_account),
824 G_CALLBACK(cb_record_changed), NULL);
825 g_signal_handlers_disconnect_by_func(G_OBJECT(entry_password),
826 G_CALLBACK(cb_record_changed),
827 GINT_TO_POINTER(PASSWD_FLAG));
828 g_signal_handlers_disconnect_by_func(G_OBJECT(date_button),
829 G_CALLBACK(cb_record_changed), NULL);
830 g_signal_handlers_disconnect_by_func(keyr_note_buffer,
831 G_CALLBACK(cb_record_changed), NULL);
832 }
833 }
834
835 static void free_mykeyring_list(struct MyKeyRing **PPmkr) {
836 struct MyKeyRing *mkr, *next_mkr;
837
838 jp_logf(JP_LOG_DEBUG, "KeyRing: free_mykeyring_list\n");
839 for (mkr = *PPmkr; mkr; mkr = next_mkr) {
840 if (mkr->kr.name) free(mkr->kr.name);
841 if (mkr->kr.account) free(mkr->kr.account);
842 if (mkr->kr.password) free(mkr->kr.password);
843 if (mkr->kr.note) free(mkr->kr.note);
844 next_mkr = mkr->next;
845 free(mkr);
846 }
847 *PPmkr = NULL;
848 }
849
850 gboolean deleteKeyRingRecord(GtkTreeModel *model,
851 GtkTreePath *path,
852 GtkTreeIter *iter,
853 gpointer data) {
854 int *i = gtk_tree_path_get_indices(path);
855 if (i[0] == row_selected) {
856 struct MyKeyRing *mkr = NULL;
857 gtk_tree_model_get(model, iter, KEYRING_DATA_COLUMN_ENUM, &mkr, -1);
858 deleteKeyRing(mkr, data);
859 return TRUE;
860 }
861
862 return FALSE;
863
864
865 }
866
867 gboolean undeleteKeyRingRecord(GtkTreeModel *model,
868 GtkTreePath *path,
869 GtkTreeIter *iter,
870 gpointer data) {
871 int *i = gtk_tree_path_get_indices(path);
872 if (i[0] == row_selected) {
873 struct MyKeyRing *mkr = NULL;
874 gtk_tree_model_get(model, iter, KEYRING_DATA_COLUMN_ENUM, &mkr, -1);
875 undeleteKeyRing(mkr, data);
876 return TRUE;
877 }
878
879 return FALSE;
880
881
882 }
883
884 void undeleteKeyRing(struct MyKeyRing *mkr, gpointer data) {
885
886 buf_rec br;
887 char buf[0xFFFF];
888 int new_size;
889 int flag;
890 if (mkr == NULL) {
891 return;
892 }
893
894 jp_logf(JP_LOG_DEBUG, "mkr->unique_id = %d\n", mkr->unique_id);
895 jp_logf(JP_LOG_DEBUG, "mkr->rt = %d\n", mkr->rt);
896
897 pack_KeyRing(&(mkr->kr), (unsigned char *) buf, 0xFFFF, &new_size);
898
899 br.rt = mkr->rt;
900 br.unique_id = mkr->unique_id;
901 br.attrib = mkr->attrib;
902 br.buf = buf;
903 br.size = new_size;
904
905 flag = GPOINTER_TO_INT(data);
906
907 if (flag == UNDELETE_FLAG) {
908 if (mkr->rt == DELETED_PALM_REC ||
909 mkr->rt == DELETED_PC_REC) {
910 jp_undelete_record("Keys-Gtkr", &br, flag);
911 }
912 /* Possible later addition of undelete for modified records
913 else if (mmemo->rt == MODIFIED_PALM_REC)
914 {
915 cb_add_new_record(widget, GINT_TO_POINTER(COPY_FLAG));
916 }
917 */
918 }
919
920 keyr_update_liststore(listStore, &glob_keyring_list, keyr_category, TRUE);
921 }
922
923 void deleteKeyRing(struct MyKeyRing *mkr, gpointer data) {
924 struct KeyRing kr;
925 int new_size;
926 char buf[0xFFFF];
927 buf_rec br;
928 int flag;
929
930 jp_logf(JP_LOG_DEBUG, "KeyRing: cb_delete_keyring\n");
931
932 if (!mkr) {
933 return;
934 }
935
936 /* The record that we want to delete should be written to the pc file
937 * so that it can be deleted at sync time. We need the original record
938 * so that if it has changed on the pilot we can warn the user that
939 * the record has changed on the pilot. */
940 kr = mkr->kr;
941
942 kr.name = strdup(kr.name);
943 jp_charset_j2p(kr.name, strlen(kr.name) + 1);
944
945 kr.account = strdup(kr.account);
946 jp_charset_j2p(kr.account, strlen(kr.account) + 1);
947
948 kr.password = strdup(kr.password);
949 jp_charset_j2p(kr.password, strlen(kr.password) + 1);
950
951 kr.note = strdup(kr.note);
952 jp_charset_j2p(kr.note, strlen(kr.note) + 1);
953
954 pack_KeyRing(&kr, (unsigned char *) buf, 0xFFFF, &new_size);
955
956 free(kr.name);
957 free(kr.account);
958 free(kr.password);
959 free(kr.note);
960
961 br.rt = mkr->rt;
962 br.unique_id = mkr->unique_id;
963 br.attrib = mkr->attrib;
964 br.buf = buf;
965 br.size = new_size;
966
967 flag = GPOINTER_TO_INT(data);
968 if ((flag == MODIFY_FLAG) || (flag == DELETE_FLAG)) {
969 jp_delete_record("Keys-Gtkr", &br, flag);
970 if (flag == DELETE_FLAG) {
971 /* when we redraw we want to go to the line above the deleted one */
972 if (row_selected > 0) {
973 row_selected--;
974 }
975 }
976 }
977
978 if (flag == DELETE_FLAG) {
979 keyr_update_liststore(listStore, &glob_keyring_list, keyr_category, TRUE);
980 }
981 }
982
983 /* This function gets called when the "delete" button is pressed */
984 static void cb_delete_keyring(GtkWidget *widget, gpointer data) {
985 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), deleteKeyRingRecord, data);
986 }
987
988 static void cb_undelete_keyring(GtkWidget *widget, gpointer data) {
989 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), undeleteKeyRingRecord, data);
990
991 }
992
993 static void cb_cancel(GtkWidget *widget, gpointer data) {
994 set_new_button_to(CLEAR_FLAG);
995 keyr_update_liststore(listStore, &glob_keyring_list, keyr_category, TRUE);
996 }
997
998 static void update_date_button(GtkWidget *button, struct tm *t) {
999 const char *short_date;
1000 char str[255];
1001
1002 get_pref(PREF_SHORTDATE, NULL, &short_date);
1003 strftime(str, sizeof(str), short_date, t);
1004
1005 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(button))), str);
1006 }
1007
1008 /*
1009 * This is called when the "New" button is pressed.
1010 * It clears out all the detail fields on the right-hand side.
1011 */
1012 static int keyr_clear_details(void) {
1013 struct tm *now;
1014 time_t ltime;
1015 int new_cat;
1016 int sorted_position;
1017
1018 jp_logf(JP_LOG_DEBUG, "KeyRing: cb_clear\n");
1019
1020 connect_changed_signals(DISCONNECT_SIGNALS);
1021 GtkTreeSelection *treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
1022
1023 gtk_tree_selection_set_select_function(treeSelection, NULL, NULL, NULL);
1024
1025 gtk_list_store_clear(listStore);
1026 gtk_tree_selection_set_select_function(treeSelection, handleKeyringRowSelection, NULL, NULL);
1027 /* Put the current time in the lastChanged part of the record */
1028 time(<ime);
1029 now = localtime(<ime);
1030 memcpy(&glob_date, now, sizeof(struct tm));
1031 update_date_button(date_button, &glob_date);
1032
1033 gtk_entry_set_text(GTK_ENTRY(entry_name), "");
1034 gtk_entry_set_text(GTK_ENTRY(entry_account), "");
1035 gtk_entry_set_text(GTK_ENTRY(entry_password), "");
1036 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(keyr_note_buffer), "", -1);
1037 if (keyr_category == CATEGORY_ALL) {
1038 new_cat = 0;
1039 } else {
1040 new_cat = keyr_category;
1041 }
1042 sorted_position = find_sort_cat_pos(new_cat);
1043 if (sorted_position < 0) {
1044 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1045 } else {
1046 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu2),find_menu_cat_pos(sorted_position));
1047 }
1048
1049 set_new_button_to(CLEAR_FLAG);
1050 connect_changed_signals(CONNECT_SIGNALS);
1051
1052 return EXIT_SUCCESS;
1053 }
1054
1055 gboolean addKeyRingRecord(GtkTreeModel *model,
1056 GtkTreePath *path,
1057 GtkTreeIter *iter,
1058 gpointer data) {
1059 int *i = gtk_tree_path_get_indices(path);
1060 if (i[0] == row_selected) {
1061 struct MyKeyRing *mkr = NULL;
1062 gtk_tree_model_get(model, iter, KEYRING_DATA_COLUMN_ENUM, &mkr, -1);
1063 addKeyRing(mkr, data);
1064 return TRUE;
1065 }
1066
1067 return FALSE;
1068
1069
1070 }
1071
1072 void addKeyRing(struct MyKeyRing *mkr, gpointer data) {
1073 struct KeyRing kr;
1074 buf_rec br;
1075 unsigned char buf[0x10000];
1076 int new_size;
1077 int flag;
1078
1079 GtkTextIter start_iter;
1080 GtkTextIter end_iter;
1081 unsigned int unique_id;
1082
1083
1084 unique_id = 0;
1085
1086 jp_logf(JP_LOG_DEBUG, "KeyRing: cb_add_new_record\n");
1087
1088 flag = GPOINTER_TO_INT(data);
1089
1090 if (flag == CLEAR_FLAG) {
1091 keyr_clear_details();
1092 connect_changed_signals(DISCONNECT_SIGNALS);
1093 set_new_button_to(NEW_FLAG);
1094 gtk_widget_grab_focus(GTK_WIDGET(entry_name));
1095 return;
1096 }
1097 if ((flag != NEW_FLAG) && (flag != MODIFY_FLAG) && (flag != COPY_FLAG)) {
1098 return;
1099 }
1100
1101 kr.name = (char *) gtk_entry_get_text(GTK_ENTRY(entry_name));
1102 kr.account = (char *) gtk_entry_get_text(GTK_ENTRY(entry_account));
1103 kr.password = (char *) gtk_entry_get_text(GTK_ENTRY(entry_password));
1104
1105 /* Put the glob_date in the lastChanged part of the record */
1106 memcpy(&(kr.last_changed), &glob_date, sizeof(struct tm));
1107
1108 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(keyr_note_buffer), &start_iter, &end_iter);
1109 kr.note = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(keyr_note_buffer), &start_iter, &end_iter, TRUE);
1110
1111 kr.name = strdup(kr.name);
1112 jp_charset_j2p(kr.name, strlen(kr.name) + 1);
1113
1114 kr.account = strdup(kr.account);
1115 jp_charset_j2p(kr.account, strlen(kr.account) + 1);
1116
1117 kr.password = strdup(kr.password);
1118 jp_charset_j2p(kr.password, strlen(kr.password) + 1);
1119
1120 jp_charset_j2p(kr.note, strlen(kr.note) + 1);
1121
1122 pack_KeyRing(&kr, buf, 0xFFFF, &new_size);
1123
1124 /* free allocated memory now that kr structure is packed into buf */
1125 if (kr.name) free(kr.name);
1126 if (kr.account) free(kr.account);
1127 if (kr.password) free(kr.password);
1128 if (kr.note) free(kr.note);
1129
1130 /* Any attributes go here. Usually just the category */
1131 /* grab category from menu */
1132 if (GTK_IS_WIDGET(category_menu2)) {
1133 br.attrib = get_selected_category_from_combo_box(GTK_COMBO_BOX(category_menu2));
1134 }
1135 jp_logf(JP_LOG_DEBUG, "category is %d\n", br.attrib);
1136
1137 br.buf = buf;
1138 br.size = new_size;
1139
1140 set_new_button_to(CLEAR_FLAG);
1141
1142 /* Keep unique ID intact */
1143 if (flag == MODIFY_FLAG) {
1144 if (!mkr) {
1145 return;
1146 }
1147 unique_id = mkr->unique_id;
1148
1149 if ((mkr->rt == DELETED_PALM_REC) ||
1150 (mkr->rt == DELETED_PC_REC) ||
1151 (mkr->rt == MODIFIED_PALM_REC)) {
1152 jp_logf(JP_LOG_INFO, _("You can't modify a record that is deleted\n"));
1153 return;
1154 }
1155 }
1156
1157 /* Keep unique ID intact */
1158 if (flag == MODIFY_FLAG) {
1159 cb_delete_keyring(NULL, data);
1160 if ((mkr->rt == PALM_REC) || (mkr->rt == REPLACEMENT_PALM_REC)) {
1161 br.unique_id = unique_id;
1162 br.rt = REPLACEMENT_PALM_REC;
1163 } else {
1164 br.unique_id = 0;
1165 br.rt = NEW_PC_REC;
1166 }
1167 } else {
1168 br.unique_id = 0;
1169 br.rt = NEW_PC_REC;
1170 }
1171
1172 /* Write out the record. It goes to the .pc3 file until it gets synced */
1173 jp_pc_write("Keys-Gtkr", &br);
1174
1175 keyr_update_liststore(listStore, &glob_keyring_list, keyr_category, TRUE);
1176
1177 keyring_find(br.unique_id);
1178
1179
1180 }
1181
1182 /*
1183 * This function is called when the user presses the "Add" button.
1184 * We collect all of the data from the GUI and pack it into a keyring
1185 * record and then write it out.
1186 kr->name=strdup((char *)buf);
1187 kr->account=strdup((char *)Pstr[0]);
1188 kr->password=strdup((char *)Pstr[1]);
1189 kr->note=strdup((char *)Pstr[2]);
1190 */
1191 static void cb_add_new_record(GtkWidget *widget, gpointer data) {
1192
1193 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL) != 0) {
1194 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), addKeyRingRecord, data);
1195 } else {
1196 //no records exist in category yet.
1197 addKeyRing(NULL, data);
1198 }
1199
1200
1201 }
1202
1203 static void cb_date_button(GtkWidget *widget, gpointer data) {
1204 long fdow;
1205 int ret;
1206 struct tm temp_glob_date = glob_date;
1207
1208 get_pref(PREF_FDOW, &fdow, NULL);
1209
1210 /* date is not set */
1211 if (glob_date.tm_mon < 0) {
1212 /* use today date */
1213 time_t t = time(NULL);
1214 glob_date = *localtime(&t);
1215 }
1216
1217 ret = jp_cal_dialog(GTK_WINDOW(gtk_widget_get_toplevel(widget)), "", fdow,
1218 &(glob_date.tm_mon),
1219 &(glob_date.tm_mday),
1220 &(glob_date.tm_year));
1221 if (ret == CAL_DONE)
1222 update_date_button(date_button, &glob_date);
1223 else
1224 glob_date = temp_glob_date;
1225 }
1226
1227 /* First pass at password generating code */
1228 static void cb_gen_password(GtkWidget *widget, gpointer data) {
1229 GtkWidget *entry;
1230 int i,
1231 length,
1232 alpha_size,
1233 numer_size;
1234 char alpha[] = "abcdfghjklmnpqrstvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1235 char numer[] = "1234567890";
1236 char passwd[MAX_KR_PASS + 1];
1237
1238 jp_logf(JP_LOG_DEBUG, "KeyRing: cb_gen_password\n");
1239
1240 entry = data;
1241
1242 srand(time(NULL) * getpid());
1243 alpha_size = strlen(alpha);
1244 numer_size = strlen(numer);
1245
1246 length = rand() % (MAX_KR_PASS - MIN_KR_PASS) + MIN_KR_PASS;
1247
1248 for (i = 0; i < length; i++) {
1249 if ((i % 2) == 0) {
1250 passwd[i] = alpha[rand() % alpha_size];
1251 } else {
1252 passwd[i] = numer[rand() % numer_size];
1253 }
1254 }
1255
1256 passwd[length] = '\0';
1257
1258 gtk_entry_set_text(GTK_ENTRY(entry), passwd);
1259
1260 return;
1261 }
1262
1263 /*
1264 * This function just adds the record to the treeView on the left side of
1265 * the screen.
1266 */
1267 static int display_record(struct MyKeyRing *mkr, int row, GtkTreeIter *iter) {
1268 char temp[8];
1269 char *nameTxt;
1270 char *accountTxt;
1271 const char *svalue;
1272 char changedTxt[50];
1273
1274 jp_logf(JP_LOG_DEBUG, "KeyRing: display_record\n");
1275
1276 /* Highlight row background depending on status */
1277 GdkRGBA bgColor;
1278 gboolean showBgColor;
1279 switch (mkr->rt) {
1280 case NEW_PC_REC:
1281 case REPLACEMENT_PALM_REC:
1282 bgColor = get_color(LIST_NEW_RED, LIST_NEW_GREEN, LIST_NEW_BLUE);
1283 showBgColor = TRUE;
1284 break;
1285 case DELETED_PALM_REC:
1286 case DELETED_PC_REC:
1287 bgColor = get_color(LIST_DEL_RED, LIST_DEL_GREEN, LIST_DEL_BLUE);
1288 showBgColor = TRUE;
1289 break;
1290 case MODIFIED_PALM_REC:
1291 bgColor = get_color(LIST_MOD_RED, LIST_MOD_GREEN, LIST_MOD_BLUE);
1292 showBgColor = TRUE;
1293 break;
1294 default:
1295 showBgColor = FALSE;
1296 }
1297
1298 if (mkr->kr.last_changed.tm_year == 0) {
1299 sprintf(changedTxt, _("No date"));
1300 } else {
1301 get_pref(PREF_SHORTDATE, NULL, &svalue);
1302 strftime(changedTxt, sizeof(changedTxt), svalue, &(mkr->kr.last_changed));
1303 }
1304
1305 if ((!(mkr->kr.name)) || (mkr->kr.name[0] == '\0')) {
1306 sprintf(temp, "#%03d", row);
1307 nameTxt = temp;
1308 } else {
1309 nameTxt = mkr->kr.name;
1310 }
1311
1312 if ((!(mkr->kr.account)) || (mkr->kr.account[0] == '\0')) {
1313 accountTxt = "";
1314 } else {
1315 accountTxt = mkr->kr.account;
1316 }
1317 gtk_list_store_append(listStore, iter);
1318 gtk_list_store_set(listStore, iter,
1319 KEYRING_CHANGED_COLUMN_ENUM, changedTxt,
1320 KEYRING_NAME_COLUMN_ENUM, nameTxt,
1321 KEYRING_ACCOUNT_COLUMN_ENUM, accountTxt,
1322 KEYRING_DATA_COLUMN_ENUM, mkr,
1323 KEYRING_BACKGROUND_COLOR_ENABLED_ENUM, showBgColor,
1324 KEYRING_BACKGROUND_COLOR_ENUM, showBgColor ? &bgColor : NULL, -1);
1325
1326
1327 return EXIT_SUCCESS;
1328 }
1329
1330 static int
1331 display_record_export(GtkListStore *pListStore, struct MyKeyRing *mkr, int row, GtkTreeIter *iter) {
1332 char temp[8];
1333 char *nameTxt;
1334
1335 jp_logf(JP_LOG_DEBUG, "KeyRing: display_record_export\n");
1336
1337 if ((!(mkr->kr.name)) || (mkr->kr.name[0] == '\0')) {
1338 sprintf(temp, "#%03d", row);
1339 nameTxt = temp;
1340 } else {
1341 nameTxt = mkr->kr.name;
1342 }
1343 //KEYRING_CHANGED_COLUMN_ENUM
1344 gtk_list_store_append(pListStore, iter);
1345 gtk_list_store_set(pListStore, iter,
1346 KEYRING_CHANGED_COLUMN_ENUM, nameTxt,
1347 KEYRING_DATA_COLUMN_ENUM, mkr, -1);
1348 return EXIT_SUCCESS;
1349 }
1350
1351 void keyr_update_liststore(GtkListStore *pListStore, struct MyKeyRing **keyring_list,
1352 int category, int main) {
1353 GtkTreeIter iter;
1354 int entries_shown;
1355 struct MyKeyRing *temp_list;
1356
1357 jp_logf(JP_LOG_DEBUG, "KeyRing: keyr_update_liststore\n");
1358
1359 free_mykeyring_list(keyring_list);
1360
1361 /* This function takes care of reading the database for us */
1362 get_keyring(keyring_list, category);
1363
1364 if (main) {
1365 keyr_clear_details();
1366 }
1367
1368
1369
1370 entries_shown = 0;
1371
1372 for (temp_list = *keyring_list; temp_list; temp_list = temp_list->next) {
1373
1374 if (main) {
1375 display_record(temp_list, entries_shown, &iter);
1376 } else {
1377 display_record_export(pListStore, temp_list, entries_shown, &iter);
1378 }
1379 entries_shown++;
1380 }
1381 jp_logf(JP_LOG_DEBUG, "KeyRing: leave keyr_update_liststore\n");
1382 }
1383
1384 static gboolean handleKeyringRowSelection(GtkTreeSelection *selection,
1385 GtkTreeModel *model,
1386 GtkTreePath *path,
1387 gboolean path_currently_selected,
1388 gpointer userdata) {
1389 GtkTreeIter iter;
1390 struct MyKeyRing *mkr;
1391 int index, sorted_position;
1392 int b;
1393 unsigned int unique_id = 0;
1394
1395 jp_logf(JP_LOG_DEBUG, "KeyRing: handleKeyringRowSelection\n");
1396 if ((gtk_tree_model_get_iter(model, &iter, path)) && (!path_currently_selected)) {
1397 int *i = gtk_tree_path_get_indices(path);
1398 row_selected = i[0];
1399 gtk_tree_model_get(model, &iter, KEYRING_DATA_COLUMN_ENUM, &mkr, -1);
1400 if ((record_changed == MODIFY_FLAG) || (record_changed == NEW_FLAG)) {
1401
1402
1403 if (mkr != NULL) {
1404 unique_id = mkr->unique_id;
1405 }
1406
1407 // We need to turn this "scroll with mouse held down" thing off
1408 button_set_for_motion(0);
1409
1410 b = dialog_save_changed_record_with_cancel(pane, record_changed);
1411 if (b == DIALOG_SAID_1) { /* Cancel */
1412 return TRUE;
1413 }
1414 if (b == DIALOG_SAID_3) { /* Save */
1415 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1416 }
1417
1418 set_new_button_to(CLEAR_FLAG);
1419
1420 if (unique_id) {
1421 keyring_find(unique_id);
1422 }
1423
1424 return TRUE;
1425 }
1426
1427
1428 if (mkr == NULL) {
1429 return TRUE;
1430 }
1431
1432 if (mkr->rt == DELETED_PALM_REC ||
1433 (mkr->rt == DELETED_PC_REC))
1434 /* Possible later addition of undelete code for modified deleted records
1435 || mkr->rt == MODIFIED_PALM_REC
1436 */
1437 {
1438 set_new_button_to(UNDELETE_FLAG);
1439 } else {
1440 set_new_button_to(CLEAR_FLAG);
1441 }
1442
1443 connect_changed_signals(DISCONNECT_SIGNALS);
1444
1445 index = mkr->attrib & 0x0F;
1446 sorted_position = find_sort_cat_pos(index);
1447 int pos = findSortedPostion(sorted_position, GTK_COMBO_BOX(category_menu2));
1448 if (pos != sorted_position && index != 0) {
1449 /* Illegal category */
1450 jp_logf(JP_LOG_DEBUG, "Category is not legal\n");
1451 sorted_position = 0;
1452 }
1453
1454 if (sorted_position < 0) {
1455 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1456 } else {
1457 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu2),find_menu_cat_pos(sorted_position));
1458 }
1459
1460 if (mkr->kr.name) {
1461 gtk_entry_set_text(GTK_ENTRY(entry_name), mkr->kr.name);
1462 } else {
1463 gtk_entry_set_text(GTK_ENTRY(entry_name), "");
1464 }
1465
1466 if (mkr->kr.account) {
1467 gtk_entry_set_text(GTK_ENTRY(entry_account), mkr->kr.account);
1468 } else {
1469 gtk_entry_set_text(GTK_ENTRY(entry_account), "");
1470 }
1471
1472 if (mkr->kr.password) {
1473 gtk_entry_set_text(GTK_ENTRY(entry_password), mkr->kr.password);
1474 } else {
1475 gtk_entry_set_text(GTK_ENTRY(entry_password), "");
1476 }
1477
1478 memcpy(&glob_date, &(mkr->kr.last_changed), sizeof(struct tm));
1479 update_date_button(date_button, &(mkr->kr.last_changed));
1480
1481 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(keyr_note_buffer), "", -1);
1482 if (mkr->kr.note) {
1483 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(keyr_note_buffer), mkr->kr.note, -1);
1484 }
1485
1486 connect_changed_signals(CONNECT_SIGNALS);
1487 }
1488 jp_logf(JP_LOG_DEBUG, "KeyRing: leaving handleKeyringRowSelection\n");
1489 return TRUE;
1490 }
1491
1492 static void cb_category(GtkComboBox *item, int selection) {
1493 int b;
1494
1495 jp_logf(JP_LOG_DEBUG, "KeyRing: cb_category\n");
1496 if (!item) return;
1497 if (gtk_combo_box_get_active(GTK_COMBO_BOX(item)) < 0) {
1498 return;
1499 }
1500 int selectedItem = get_selected_category_from_combo_box(item);
1501 if (selectedItem == -1) {
1502 return;
1503 }
1504
1505 if (keyr_category == selectedItem) { return; }
1506
1507 b = dialog_save_changed_record_with_cancel(pane, record_changed);
1508 if (b == DIALOG_SAID_1) { /* Cancel */
1509 int index, index2;
1510
1511 if (keyr_category == CATEGORY_ALL) {
1512 index = 0;
1513 index2 = 0;
1514 } else {
1515 index = find_sort_cat_pos(keyr_category);
1516 index2 = find_menu_cat_pos(index) + 1;
1517 index += 1;
1518 }
1519
1520 if (index < 0) {
1521 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1522 } else {
1523 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), index2);
1524 }
1525
1526 return;
1527 }
1528 if (b == DIALOG_SAID_3) { /* Save */
1529 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1530 }
1531
1532 keyr_category = selectedItem;
1533 row_selected = 0;
1534 keyr_update_liststore(listStore, &glob_keyring_list, keyr_category, TRUE);
1535
1536 }
1537
1538 /***** PASSWORD GUI *****/
1539
1540 /*
1541 * Start of Dialog window code
1542 */
1543 struct dialog_data {
1544 GtkWidget *entry;
1545 int button_hit;
1546 char text[PASSWD_LEN + 2];
1547 };
1548
1549 static void cb_dialog_button(GtkWidget *widget, gpointer data) {
1550 struct dialog_data *Pdata;
1551 GtkWidget *w;
1552
1553 /* Find the main window from some widget */
1554 w = GTK_WIDGET(gtk_widget_get_toplevel(widget));
1555
1556 if (GTK_IS_WINDOW(w)) {
1557 Pdata = g_object_get_data(G_OBJECT(w), "dialog_data");
1558 if (Pdata) {
1559 Pdata->button_hit = GPOINTER_TO_INT(data);
1560 }
1561 gtk_widget_destroy(GTK_WIDGET(w));
1562 }
1563 }
1564
1565 static gboolean cb_destroy_dialog(GtkWidget *widget) {
1566 struct dialog_data *Pdata;
1567 const char *entry;
1568
1569 Pdata = g_object_get_data(G_OBJECT(widget), "dialog_data");
1570 if (!Pdata) {
1571 return TRUE;
1572 }
1573 entry = gtk_entry_get_text(GTK_ENTRY(Pdata->entry));
1574
1575 if (entry) {
1576 strncpy(Pdata->text, entry, PASSWD_LEN);
1577 Pdata->text[PASSWD_LEN] = '\0';
1578 /* Clear entry field */
1579 gtk_entry_set_text(GTK_ENTRY(Pdata->entry), "");
1580 }
1581
1582 gtk_main_quit();
1583
1584 return TRUE;
1585 }
1586
1587 /*
1588 * returns 2 if OK was pressed, 1 if cancel was hit
1589 */
1590 static int dialog_password(GtkWindow *main_window,
1591 char *ascii_password,
1592 int reason) {
1593 GtkWidget *button, *label;
1594 GtkWidget *hbox1, *vbox1;
1595 GtkWidget *dialog;
1596 GtkWidget *entry;
1597 struct dialog_data Pdata;
1598 int ret;
1599
1600 if (!ascii_password) {
1601 return EXIT_FAILURE;
1602 }
1603 ascii_password[0] = '\0';
1604 ret = 2;
1605
1606 dialog = gtk_widget_new(GTK_TYPE_WINDOW,
1607 "type", GTK_WINDOW_TOPLEVEL,
1608 "title", "KeyRing",
1609 NULL);
1610
1611 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
1612
1613 g_signal_connect(G_OBJECT(dialog), "destroy",
1614 G_CALLBACK(cb_destroy_dialog), dialog);
1615
1616 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1617
1618 if (main_window) {
1619 if (GTK_IS_WINDOW(main_window)) {
1620 gtk_window_set_transient_for(GTK_WINDOW(dialog),
1621 GTK_WINDOW(main_window));
1622 }
1623 }
1624
1625 hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
1626 gtk_container_add(GTK_CONTAINER(dialog), hbox1);
1627 gtk_box_pack_start(GTK_BOX(hbox1), gtk_image_new_from_icon_name("dialog-password", GTK_ICON_SIZE_DIALOG),
1628 FALSE, FALSE, 2);
1629
1630 vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
1631
1632 gtk_container_set_border_width(GTK_CONTAINER(vbox1), 5);
1633
1634 gtk_container_add(GTK_CONTAINER(hbox1), vbox1);
1635
1636 hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
1637 gtk_container_set_border_width(GTK_CONTAINER(hbox1), 5);
1638 gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 2);
1639
1640 /* Label */
1641 if (reason == PASSWD_ENTER_RETRY) {
1642 label = gtk_label_new(_("Incorrect, Reenter KeyRing Password"));
1643 } else if (reason == PASSWD_ENTER_NEW) {
1644 label = gtk_label_new(_("Enter a NEW KeyRing Password"));
1645 } else {
1646 label = gtk_label_new(_("Enter KeyRing Password"));
1647 }
1648 gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 2);
1649
1650 entry = gtk_entry_new();
1651 gtk_entry_set_max_length(GTK_ENTRY(entry), 32);
1652 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
1653 g_signal_connect(G_OBJECT(entry), "activate",
1654 G_CALLBACK(cb_dialog_button),
1655 GINT_TO_POINTER(DIALOG_SAID_2));
1656 gtk_box_pack_start(GTK_BOX(hbox1), entry, TRUE, TRUE, 1);
1657
1658 /* Button Box */
1659 hbox1 = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
1660 gtk_button_box_set_layout(GTK_BUTTON_BOX (hbox1), GTK_BUTTONBOX_END);
1661 gtk_box_set_spacing(GTK_BOX(hbox1), 6);
1662 gtk_container_set_border_width(GTK_CONTAINER(hbox1), 5);
1663 gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 2);
1664
1665 /* Buttons */
1666 button = gtk_button_new_with_label("Cancel");
1667 g_signal_connect(G_OBJECT(button), "clicked",
1668 G_CALLBACK(cb_dialog_button),
1669 GINT_TO_POINTER(DIALOG_SAID_1));
1670 gtk_box_pack_start(GTK_BOX(hbox1), button, FALSE, FALSE, 1);
1671
1672 button = gtk_button_new_with_label("OK");
1673 g_signal_connect(G_OBJECT(button), "clicked",
1674 G_CALLBACK(cb_dialog_button),
1675 GINT_TO_POINTER(DIALOG_SAID_2));
1676 gtk_box_pack_start(GTK_BOX(hbox1), button, FALSE, FALSE, 1);
1677
1678 /* Set the default button pressed to CANCEL */
1679 Pdata.button_hit = DIALOG_SAID_1;
1680 Pdata.entry = entry;
1681 Pdata.text[0] = '\0';
1682
1683 g_object_set_data(G_OBJECT(dialog), "dialog_data", &Pdata);
1684 gtk_widget_grab_focus(GTK_WIDGET(entry));
1685
1686 gtk_widget_show_all(dialog);
1687
1688 gtk_main();
1689
1690 if (Pdata.button_hit == DIALOG_SAID_1) {
1691 ret = 1;
1692 }
1693 if (Pdata.button_hit == DIALOG_SAID_2) {
1694 ret = 2;
1695 }
1696 strncpy(ascii_password, Pdata.text, PASSWD_LEN);
1697 memset(Pdata.text, 0, PASSWD_LEN);
1698
1699 return ret;
1700 }
1701
1702 /***** End Password GUI *****/
1703
1704 static int check_for_db(void) {
1705 char file[] = "Keys-Gtkr.pdb";
1706 char full_name[1024];
1707 struct stat buf;
1708
1709 jp_get_home_file_name(file, full_name, sizeof(full_name));
1710
1711 if (stat(full_name, &buf)) {
1712 jp_logf(JP_LOG_FATAL, _("KeyRing: file %s not found.\n"), full_name);
1713 jp_logf(JP_LOG_FATAL, _("KeyRing: Try Syncing.\n"), full_name);
1714 return EXIT_FAILURE;
1715 }
1716
1717 return EXIT_SUCCESS;
1718 }
1719
1720 /*
1721 * returns EXIT_SUCCESS on password correct,
1722 * EXIT_FAILURE on password incorrect,
1723 * <0 on error
1724 */
1725 static int verify_pasword(char *ascii_password) {
1726 GList *records;
1727 GList *temp_list;
1728 buf_rec *br;
1729 int password_not_correct;
1730
1731 jp_logf(JP_LOG_DEBUG, "KeyRing: verify_pasword\n");
1732
1733 if (check_for_db()) {
1734 return EXIT_FAILURE;
1735 }
1736
1737 /* This function takes care of reading the Database for us */
1738 records = NULL;
1739 if (jp_read_DB_files("Keys-Gtkr", &records) == -1)
1740 return EXIT_SUCCESS;
1741
1742 password_not_correct = 1;
1743 /* Find special record marked as password */
1744 for (temp_list = records; temp_list; temp_list = temp_list->next) {
1745 if (temp_list->data) {
1746 br = temp_list->data;
1747 } else {
1748 continue;
1749 }
1750 if (!br->buf) {
1751 continue;
1752 }
1753
1754 if ((br->rt == DELETED_PALM_REC) || (br->rt == MODIFIED_PALM_REC)) {
1755 continue;
1756 }
1757
1758 /* This record should be record 0 and is the hash-key record */
1759 if (br->attrib & dlpRecAttrSecret) {
1760 password_not_correct =
1761 set_password_hash(br->buf, br->size, ascii_password);
1762 break;
1763 }
1764 }
1765
1766 jp_free_DB_records(&records);
1767
1768 if (password_not_correct)
1769 return EXIT_FAILURE;
1770 else
1771 return EXIT_SUCCESS;
1772 }
1773
1774 #define PLUGIN_MAJOR 1
1775 #define PLUGIN_MINOR 1
1776
1777 /* This is a mandatory plugin function. */
1778 void plugin_version(int *major_version, int *minor_version) {
1779 *major_version = PLUGIN_MAJOR;
1780 *minor_version = PLUGIN_MINOR;
1781 }
1782
1783 static int static_plugin_get_name(char *name, int len) {
1784 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin_get_name\n");
1785 snprintf(name, len, "KeyRing %d.%d", PLUGIN_MAJOR, PLUGIN_MINOR);
1786 return EXIT_SUCCESS;
1787 }
1788
1789 /* This is a mandatory plugin function. */
1790 int plugin_get_name(char *name, int len) {
1791 return static_plugin_get_name(name, len);
1792 }
1793
1794 /*
1795 * This is an optional plugin function.
1796 * This is the name that will show up in the plugins menu in J-Pilot.
1797 */
1798 int plugin_get_menu_name(char *name, int len) {
1799 strncpy(name, _("KeyRing"), len);
1800 return EXIT_SUCCESS;
1801 }
1802
1803 /*
1804 * This is an optional plugin function.
1805 * This is the name that will show up in the plugins help menu in J-Pilot.
1806 * If this function is used then plugin_help must be also.
1807 */
1808 int plugin_get_help_name(char *name, int len) {
1809 g_snprintf(name, len, _("About %s"), _("KeyRing"));
1810 return EXIT_SUCCESS;
1811 }
1812
1813 /*
1814 * This is an optional plugin function.
1815 * This is the palm database that will automatically be synced.
1816 */
1817 int plugin_get_db_name(char *name, int len) {
1818 strncpy(name, "Keys-Gtkr", len);
1819 return EXIT_SUCCESS;
1820 }
1821
1822 /*
1823 * This is a plugin callback function which provides information
1824 * to the user about the plugin.
1825 */
1826 int plugin_help(char **text, int *width, int *height) {
1827 /* We could also pass back *text=NULL
1828 * and implement whatever we wanted to here.
1829 */
1830 char plugin_name[200];
1831
1832 static_plugin_get_name(plugin_name, sizeof(plugin_name));
1833 *text = g_strdup_printf(
1834 /*-------------------------------------------*/
1835 _("%s\n"
1836 "\n"
1837 "KeyRing plugin for J-Pilot was written by\n"
1838 "Judd Montgomery (c) 2001.\n"
1839 "judd@jpilot.org, http://jpilot.org\n"
1840 "\n"
1841 "KeyRing is a free PalmOS program for storing\n"
1842 "passwords and other information in encrypted form\n"
1843 "http://gnukeyring.sourceforge.net"
1844 ),
1845 plugin_name
1846 );
1847 *height = 0;
1848 *width = 0;
1849
1850 return EXIT_SUCCESS;
1851 }
1852
1853 /*
1854 * This is a plugin callback function that is executed when J-Pilot starts up.
1855 * base_dir is where J-Pilot is compiled to be installed at (e.g. /usr/local/)
1856 */
1857 int plugin_startup(jp_startup_info *info) {
1858 jp_init();
1859
1860 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin_startup\n");
1861 if (info) {
1862 if (info->base_dir) {
1863 jp_logf(JP_LOG_DEBUG, "KeyRing: base_dir = [%s]\n", info->base_dir);
1864 }
1865 }
1866 return EXIT_SUCCESS;
1867 }
1868
1869 /*
1870 * This is a plugin callback function that is executed before a sync occurs.
1871 * Any sync preperation can be done here.
1872 */
1873 int plugin_pre_sync(void) {
1874 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin_pre_sync\n");
1875 return EXIT_SUCCESS;
1876 }
1877
1878 /*
1879 * This is a plugin callback function that is executed during a sync.
1880 * Notice that I don't need to sync the KeyRing application. Since I used
1881 * the plugin_get_db_name call to tell J-Pilot what to sync for me. It will
1882 * be done automatically.
1883 */
1884 int plugin_sync(int sd) {
1885 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin_sync\n");
1886 return EXIT_SUCCESS;
1887 }
1888
1889 /*
1890 * This is a plugin callback function called after a sync.
1891 */
1892 int plugin_post_sync(void) {
1893 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin_post_sync\n");
1894 return EXIT_SUCCESS;
1895 }
1896
1897 static int add_search_result(const char *line,
1898 int unique_id,
1899 struct search_result **sr) {
1900 struct search_result *new_sr;
1901
1902 jp_logf(JP_LOG_DEBUG, "KeyRing: add_search_result for [%s]\n", line);
1903
1904 new_sr = malloc(sizeof(struct search_result));
1905 if (!new_sr) {
1906 return EXIT_FAILURE;
1907 }
1908 new_sr->unique_id = unique_id;
1909 new_sr->line = strdup(line);
1910 new_sr->next = *sr;
1911 *sr = new_sr;
1912
1913 return EXIT_SUCCESS;
1914 }
1915
1916 /*
1917 * This function is called when the user does a search. It should return
1918 * records which match the search string.
1919 */
1920 int plugin_search(const char *search_string, int case_sense,
1921 struct search_result **sr) {
1922 struct MyKeyRing *mkr_list;
1923 struct MyKeyRing *temp_list;
1924 struct MyKeyRing mkr;
1925 int num, count;
1926 char *line;
1927
1928 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin_search\n");
1929
1930 *sr = NULL;
1931 mkr_list = NULL;
1932
1933 if (!plugin_active) {
1934 return 0;
1935 }
1936
1937 /* This function takes care of reading the Database for us */
1938 num = get_keyring(&mkr_list, CATEGORY_ALL);
1939 if (-1 == num)
1940 return 0;
1941
1942 count = 0;
1943
1944 /* Search through returned records */
1945 for (temp_list = mkr_list; temp_list; temp_list = temp_list->next) {
1946 mkr = *temp_list;
1947 line = NULL;
1948
1949 /* find in record name */
1950 if (jp_strstr(mkr.kr.name, search_string, case_sense))
1951 line = mkr.kr.name;
1952
1953 /* find in record account */
1954 if (jp_strstr(mkr.kr.account, search_string, case_sense))
1955 line = mkr.kr.account;
1956
1957 /* find in record password */
1958 if (jp_strstr(mkr.kr.password, search_string, case_sense))
1959 line = mkr.kr.password;
1960
1961 /* find in record note */
1962 if (jp_strstr(mkr.kr.note, search_string, case_sense))
1963 line = mkr.kr.note;
1964
1965 if (line) {
1966 /* Add it to our result list */
1967 jp_logf(JP_LOG_DEBUG, "KeyRing: calling add_search_result\n");
1968 add_search_result(line, mkr.unique_id, sr);
1969 jp_logf(JP_LOG_DEBUG, "KeyRing: back from add_search_result\n");
1970 count++;
1971 }
1972 }
1973
1974 free_mykeyring_list(&mkr_list);
1975
1976 return count;
1977 }
1978
1979 gboolean
1980 findKeyRingRecord(GtkTreeModel *model,
1981 GtkTreePath *path,
1982 GtkTreeIter *iter,
1983 gpointer data) {
1984 int uniqueId = GPOINTER_TO_INT(data);
1985 if (uniqueId) {
1986 struct MyKeyRing *mkr = NULL;
1987
1988 gtk_tree_model_get(model, iter, KEYRING_DATA_COLUMN_ENUM, &mkr, -1);
1989 if (mkr->unique_id == uniqueId) {
1990 GtkTreeSelection *selection = NULL;
1991 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
1992 gtk_tree_selection_select_path(selection, path);
1993 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeView), path, KEYRING_CHANGED_COLUMN_ENUM, FALSE, 1.0, 0.0);
1994 return TRUE;
1995 }
1996 }
1997 return FALSE;
1998 }
1999
2000 static int keyring_find(int unique_id) {
2001 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), findKeyRingRecord, GINT_TO_POINTER(unique_id));
2002 return EXIT_SUCCESS;
2003 }
2004
2005 static void cb_keyr_update_listStore(GtkWidget *treeView, int category) {
2006 keyr_update_liststore(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeView))), &export_keyring_list,
2007 category, FALSE);
2008 }
2009
2010 static void cb_keyr_export_done(GtkWidget *widget, const char *filename) {
2011 free_mykeyring_list(&export_keyring_list);
2012
2013 set_pref(PREF_KEYR_EXPORT_FILENAME, 0, filename, TRUE);
2014 }
2015
2016 static void cb_keyr_export_ok(GtkWidget *export_window, GtkWidget *treeView,
2017 int type, const char *filename) {
2018 struct MyKeyRing *mkr;
2019 GList *list, *temp_list;
2020 FILE *out;
2021 struct stat statb;
2022 int i, r;
2023 const char *short_date;
2024 time_t ltime;
2025 struct tm *now;
2026 char *button_text[] = {N_("OK")};
2027 char *button_overwrite_text[] = {N_("No"), N_("Yes")};
2028 char *button_keepassx_text[] = {N_("Cancel"), N_("Overwrite"), N_("Append")};
2029 enum {
2030 NA = 0, cancel = DIALOG_SAID_1, overwrite = DIALOG_SAID_2, append = DIALOG_SAID_3
2031 } keepassx_answer = NA;
2032 char text[1024];
2033 char str1[256], str2[256];
2034 char date_string[1024];
2035 char pref_time[40];
2036 char csv_text[65550];
2037 long char_set;
2038 char *utf;
2039 int cat;
2040
2041 /* Open file for export, including corner cases where file exists or
2042 * can't be opened */
2043 if (!stat(filename, &statb)) {
2044 if (S_ISDIR(statb.st_mode)) {
2045 g_snprintf(text, sizeof(text), _("%s is a directory"), filename);
2046 dialog_generic(GTK_WINDOW(export_window),
2047 _("Error Opening File"),
2048 DIALOG_ERROR, text, 1, button_text);
2049 return;
2050 }
2051 if (type == EXPORT_TYPE_KEEPASSX) {
2052 g_snprintf(text, sizeof(text), _("KeePassX XML File exists, Do you want to"));
2053 keepassx_answer = dialog_generic(GTK_WINDOW(export_window),
2054 _("Overwrite File?"),
2055 DIALOG_ERROR, text, 3, button_keepassx_text);
2056 if (keepassx_answer == cancel) {
2057 return;
2058 }
2059 } else {
2060 g_snprintf(text, sizeof(text), _("Do you want to overwrite file %s?"), filename);
2061 r = dialog_generic(GTK_WINDOW(export_window),
2062 _("Overwrite File?"),
2063 DIALOG_ERROR, text, 2, button_overwrite_text);
2064 if (r != DIALOG_SAID_2) {
2065 return;
2066 }
2067 }
2068 }
2069
2070 if ((keepassx_answer == append)) {
2071 out = fopen(filename, "r+");
2072 } else {
2073 out = fopen(filename, "w");
2074 }
2075 if (!out) {
2076 g_snprintf(text, sizeof(text), _("Error opening file: %s"), filename);
2077 dialog_generic(GTK_WINDOW(export_window),
2078 _("Error Opening File"),
2079 DIALOG_ERROR, text, 1, button_text);
2080 return;
2081 }
2082
2083 /* Write a header for TEXT file */
2084 if (type == EXPORT_TYPE_TEXT) {
2085 get_pref(PREF_SHORTDATE, NULL, &short_date);
2086 get_pref_time_no_secs(pref_time);
2087 time(<ime);
2088 now = localtime(<ime);
2089 strftime(str1, sizeof(str1), short_date, now);
2090 strftime(str2, sizeof(str2), pref_time, now);
2091 g_snprintf(date_string, sizeof(date_string), "%s %s", str1, str2);
2092 fprintf(out, _("Keys exported from %s %s on %s\n\n"),
2093 PN, VERSION, date_string);
2094 }
2095
2096 /* Write a header to the CSV file */
2097 if (type == EXPORT_TYPE_CSV) {
2098 fprintf(out, "\"Category\",\"Name\",\"Account\",\"Password\",\"Note\"\n");
2099 }
2100
2101 /* Write a header to the B-Folders CSV file */
2102 if (type == EXPORT_TYPE_BFOLDERS) {
2103 fprintf(out, "Login passwords:\n");
2104 fprintf(out, "Title,Location,Usename,Password, "
2105 "\"Custom Label 1\",\"Custom Value 1\",\"Custom Label 2\",\"Custom Value 2\","
2106 "\"Custom Label 3\",\"Custom Value 3\",\"Custom Label 4\",\"Custom Value 4\","
2107 "\"Custom Label 5\",\"Custom Value 5\", Note,Folder\n");
2108 }
2109
2110 if (type == EXPORT_TYPE_KEEPASSX) {
2111 if (keepassx_answer != append) {
2112 /* Write a database header to the KeePassX XML file */
2113 /* If we append to an XML file we don't need another header */
2114 fprintf(out, "<!DOCTYPE KEEPASSX_DATABASE>\n");
2115 fprintf(out, "<database>\n");
2116 } else {
2117 /* We'll need to remove the last part of the XML file */
2118 fseek(out, -12L, SEEK_END);
2119 fread(text, 11, 1, out);
2120 text[11] = '\0';
2121 if (strncmp(text, "</database>", 11)) {
2122 jp_logf(JP_LOG_WARN, _("This doesn't look like a KeePassX XML file\n"));
2123 fseek(out, 0L, SEEK_END);
2124 } else {
2125 fseek(out, -12L, SEEK_END);
2126 }
2127 }
2128 /* Write a group header to the KeePassX XML file */
2129 fprintf(out, " <group>\n");
2130 fprintf(out, " <title>Keyring</title>\n");
2131 fprintf(out, " <icon>0</icon>\n");
2132 }
2133
2134 get_pref(PREF_CHAR_SET, &char_set, NULL);
2135 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
2136 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView));
2137 list = gtk_tree_selection_get_selected_rows(selection, &model);
2138
2139 for (i = 0, temp_list = list; temp_list; temp_list = temp_list->next, i++) {
2140 GtkTreePath *path = temp_list->data;
2141 GtkTreeIter iter;
2142 if (gtk_tree_model_get_iter(model, &iter, path)) {
2143 gtk_tree_model_get(model, &iter, KEYRING_DATA_COLUMN_ENUM, &mkr, -1);
2144 if (!mkr) {
2145 continue;
2146 jp_logf(JP_LOG_WARN, _("Can't export key %d\n"), (long) temp_list->data + 1);
2147 }
2148 switch (type) {
2149 case EXPORT_TYPE_CSV:
2150 utf = charset_p2newj(keyr_app_info.name[mkr->attrib & 0x0F], 16, char_set);
2151 fprintf(out, "\"%s\",", utf);
2152 g_free(utf);
2153 str_to_csv_str(csv_text, mkr->kr.name);
2154 fprintf(out, "\"%s\",", csv_text);
2155 str_to_csv_str(csv_text, mkr->kr.account);
2156 fprintf(out, "\"%s\",", csv_text);
2157 str_to_csv_str(csv_text, mkr->kr.password);
2158 fprintf(out, "\"%s\",", csv_text);
2159 str_to_csv_str(csv_text, mkr->kr.note);
2160 fprintf(out, "\"%s\"\n", csv_text);
2161 break;
2162
2163 case EXPORT_TYPE_BFOLDERS:
2164 str_to_csv_str(csv_text, mkr->kr.name);
2165 fprintf(out, "\"%s\",", csv_text);
2166
2167 fprintf(out, "\"\",");
2168
2169 str_to_csv_str(csv_text, mkr->kr.account);
2170 fprintf(out, "\"%s\",", csv_text);
2171 str_to_csv_str(csv_text, mkr->kr.password);
2172 fprintf(out, "\"%s\",", csv_text);
2173
2174 fprintf(out, "\"\",\"\",\"\",\"\","
2175 "\"\",\"\",\"\",\"\","
2176 "\"\",\"\",");
2177
2178 str_to_csv_str(csv_text, mkr->kr.note);
2179 fprintf(out, "\"%s\",", csv_text);
2180
2181 fprintf(out, "\"KeyRing > ");
2182
2183 utf = charset_p2newj(keyr_app_info.name[mkr->attrib & 0x0F], 16, char_set);
2184 fprintf(out, "%s\"\n", utf);
2185 g_free(utf);
2186
2187 break;
2188
2189 case EXPORT_TYPE_TEXT:
2190 fprintf(out, "#%d\n", i + 1);
2191 fprintf(out, "Name: %s\n", mkr->kr.name);
2192 fprintf(out, "Account: %s\n", mkr->kr.account);
2193 fprintf(out, "Password: %s\n", mkr->kr.password);
2194 fprintf(out, "Note: %s\n", mkr->kr.note);
2195 break;
2196
2197 case EXPORT_TYPE_KEEPASSX:
2198 break;
2199
2200 default:
2201 jp_logf(JP_LOG_WARN, _("Unknown export type\n"));
2202 }
2203 }
2204 }
2205
2206 /* I'm writing a second loop for the KeePassX XML file because I want to
2207 * put each category into a folder and we need to write the tag for a folder
2208 * and then find each record in that category/folder
2209 */
2210 if (type == EXPORT_TYPE_KEEPASSX) {
2211 for (cat = 0; cat < 16; cat++) {
2212 if (keyr_app_info.name[cat][0] == '\0') {
2213 continue;
2214 }
2215 /* Write a folder XML tag */
2216 utf = charset_p2newj(keyr_app_info.name[cat], 16, char_set);
2217 fprintf(out, " <group>\n");
2218 fprintf(out, " <title>%s</title>\n", utf);
2219 fprintf(out, " <icon>13</icon>\n");
2220 g_free(utf);
2221
2222 for (i = 0, temp_list = list; temp_list; temp_list = temp_list->next, i++) {
2223 GtkTreePath *path = temp_list->data;
2224 GtkTreeIter iter;
2225 if (gtk_tree_model_get_iter(model, &iter, path)) {
2226 gtk_tree_model_get(model, &iter, KEYRING_DATA_COLUMN_ENUM, &mkr, -1);
2227 if (!mkr) {
2228 continue;
2229 jp_logf(JP_LOG_WARN, _("Can't export key %d\n"), (long) temp_list->data + 1);
2230 }
2231 if ((mkr->attrib & 0x0F) != cat) {
2232 continue;
2233 }
2234 fprintf(out, " <entry>\n");
2235 str_to_keepass_str(csv_text, mkr->kr.name);
2236 fprintf(out, " <title>%s</title>\n", csv_text);
2237 str_to_keepass_str(csv_text, mkr->kr.account);
2238 fprintf(out, " <username>%s</username>\n", csv_text);
2239 str_to_keepass_str(csv_text, mkr->kr.password);
2240 fprintf(out, " <password>%s</password>\n", csv_text);
2241 /* No keyring field for url */
2242 str_to_keepass_str(csv_text, mkr->kr.note);
2243 fprintf(out, " <comment>%s</comment>\n", csv_text);
2244 fprintf(out, " <icon>0</icon>\n");
2245 /* No keyring field for creation */
2246 /* No keyring field for lastaccess */
2247 /* lastmod */
2248 strftime(str1, sizeof(str1), "%Y-%m-%dT%H:%M:%S", &(mkr->kr.last_changed));
2249 fprintf(out, " <lastmod>%s</lastmod>\n", str1);
2250 /* No keyring field for expire */
2251 fprintf(out, " <expire>Never</expire>\n");
2252 fprintf(out, " </entry>\n");
2253 }
2254 fprintf(out, " </group>\n");
2255 }
2256 }
2257
2258 /* Write a footer to the KeePassX XML file */
2259 if (type == EXPORT_TYPE_KEEPASSX) {
2260 fprintf(out, " </group>\n");
2261 fprintf(out, "</database>\n");
2262 }
2263 }
2264
2265 if (out) {
2266 fclose(out);
2267 }
2268 }
2269
2270 /*
2271 * This is a plugin callback function to export records.
2272 */
2273 int plugin_export(GtkWidget *window) {
2274 int w, h, x, y;
2275 char *type_text[] = {N_("Text"), N_("CSV"), N_("B-Folders CSV"), N_("KeePassX XML"), NULL};
2276 int type_int[] = {EXPORT_TYPE_TEXT, EXPORT_TYPE_CSV, EXPORT_TYPE_BFOLDERS, EXPORT_TYPE_KEEPASSX};
2277
2278 w = gdk_window_get_width(gtk_widget_get_window(window));
2279 h = gdk_window_get_height(gtk_widget_get_window(window));
2280 gdk_window_get_root_origin(gtk_widget_get_window(window), &x, &y);
2281
2282 w = gtk_paned_get_position(GTK_PANED(pane));
2283 x += 40;
2284
2285 export_gui(window,
2286 w, h, x, y, 1, sort_l,
2287 PREF_KEYR_EXPORT_FILENAME,
2288 type_text,
2289 type_int,
2290 cb_keyr_export_init_treeView,
2291 cb_keyr_update_listStore,
2292 cb_keyr_export_done,
2293 cb_keyr_export_ok
2294 );
2295
2296 return EXIT_SUCCESS;
2297 }
2298
2299 static GtkWidget *cb_keyr_export_init_treeView() {
2300 GtkListStore *listStore = gtk_list_store_new(KEYRING_NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
2301 G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_BOOLEAN, G_TYPE_STRING,
2302 G_TYPE_BOOLEAN);
2303 GtkTreeModel *model = GTK_TREE_MODEL(listStore);
2304 GtkWidget *keyr_treeView = gtk_tree_view_new_with_model(model);
2305
2306 GtkCellRenderer *changedRenderer = gtk_cell_renderer_text_new();
2307 GtkTreeViewColumn *changedColumn = gtk_tree_view_column_new_with_attributes("Changed",
2308 changedRenderer,
2309 "text", KEYRING_CHANGED_COLUMN_ENUM,
2310 "cell-background-rgba",
2311 KEYRING_BACKGROUND_COLOR_ENUM,
2312 "cell-background-set",
2313 KEYRING_BACKGROUND_COLOR_ENABLED_ENUM,
2314 NULL);
2315 gtk_tree_view_column_set_sort_column_id(changedColumn, KEYRING_CHANGED_COLUMN_ENUM);
2316 // gtk_cell_renderer_set_fixed_size(changedRenderer, -1, 1);
2317
2318 GtkCellRenderer *nameRenderer = gtk_cell_renderer_text_new();
2319 GtkTreeViewColumn *nameColumn = gtk_tree_view_column_new_with_attributes("Name",
2320 nameRenderer,
2321 "text", KEYRING_NAME_COLUMN_ENUM,
2322 "cell-background-rgba",
2323 KEYRING_BACKGROUND_COLOR_ENUM,
2324 "cell-background-set",
2325 KEYRING_BACKGROUND_COLOR_ENABLED_ENUM,
2326 NULL);
2327 gtk_tree_view_column_set_sort_column_id(nameColumn, KEYRING_NAME_COLUMN_ENUM);
2328 // gtk_cell_renderer_set_fixed_size(nameRenderer, -1, 1);
2329
2330 GtkCellRenderer *accountRenderer = gtk_cell_renderer_text_new();
2331 GtkTreeViewColumn *accountColumn = gtk_tree_view_column_new_with_attributes("Account",
2332 accountRenderer,
2333 "text", KEYRING_ACCOUNT_COLUMN_ENUM,
2334 "cell-background-rgba",
2335 KEYRING_BACKGROUND_COLOR_ENUM,
2336 "cell-background-set",
2337 KEYRING_BACKGROUND_COLOR_ENABLED_ENUM,
2338 NULL);
2339 gtk_tree_view_column_set_sort_column_id(accountColumn, KEYRING_ACCOUNT_COLUMN_ENUM);
2340 // gtk_cell_renderer_set_fixed_size(accountRenderer, -1, 1);
2341
2342 gtk_tree_view_insert_column(GTK_TREE_VIEW(keyr_treeView), changedColumn, KEYRING_CHANGED_COLUMN_ENUM);
2343 gtk_tree_view_insert_column(GTK_TREE_VIEW(keyr_treeView), nameColumn, KEYRING_NAME_COLUMN_ENUM);
2344 gtk_tree_view_insert_column(GTK_TREE_VIEW(keyr_treeView), accountColumn, KEYRING_ACCOUNT_COLUMN_ENUM);
2345
2346 gtk_tree_view_column_set_sizing(changedColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2347 gtk_tree_view_column_set_sizing(nameColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2348 gtk_tree_view_column_set_sizing(accountColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2349
2350 return GTK_WIDGET(keyr_treeView);
2351 }
2352
2353 /*
2354 * This is a plugin callback function called during Jpilot program exit.
2355 */
2356 int plugin_exit_cleanup(void) {
2357 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin_exit_cleanup\n");
2358 return EXIT_SUCCESS;
2359 }
2360
2361 /*
2362 * This is a plugin callback function called when the plugin is terminated
2363 * such as by switching to another application(ToDo, Memo, etc.)
2364 */
2365 int plugin_gui_cleanup(void) {
2366 int b;
2367
2368 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin_gui_cleanup\n");
2369
2370 b = dialog_save_changed_record(GTK_WIDGET(treeView), record_changed);
2371 if (b == DIALOG_SAID_2) {
2372 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
2373 }
2374
2375 connect_changed_signals(DISCONNECT_SIGNALS);
2376
2377 free_mykeyring_list(&glob_keyring_list);
2378
2379 /* if the password was correct */
2380 if (plugin_last_time && (TRUE == plugin_active)) {
2381 plugin_last_time = time(NULL);
2382 }
2383 plugin_active = FALSE;
2384
2385 /* the pane may not exist if the wrong password is entered and
2386 * the GUI was not built */
2387 if (pane) {
2388 /* Remove the accelerators */
2389 #ifndef ENABLE_STOCK_BUTTONS
2390 gtk_window_remove_accel_group(GTK_WINDOW(gtk_widget_get_toplevel(pane)), accel_group);
2391 #endif
2392
2393 /* Record the position of the window pane to restore later */
2394 set_pref(PREF_KEYRING_PANE, gtk_paned_get_position(GTK_PANED(pane)), NULL, TRUE);
2395
2396 pane = NULL;
2397
2398 gtk_list_store_clear(listStore);
2399 }
2400
2401 return EXIT_SUCCESS;
2402 }
2403
2404 static void column_clicked_cb(GtkTreeViewColumn *column) {
2405 column_selected = gtk_tree_view_column_get_sort_column_id(column);
2406
2407 }
2408
2409 /*
2410 * This function is called by J-Pilot when the user selects this plugin
2411 * from the plugin menu, or from the search window when a search result
2412 * record is chosen. In the latter case, unique ID will be set. This
2413 * application should go directly to that record in the case.
2414 */
2415 int plugin_gui(GtkWidget *vbox, GtkWidget *hbox, unsigned int unique_id) {
2416 GtkWidget *vbox1, *vbox2;
2417 GtkWidget *hbox_temp;
2418 GtkWidget *button;
2419 GtkWidget *label;
2420 //gtk3 GtkWidget *table;
2421 GtkWidget *grid;
2422 GtkWindow *w;
2423 GtkWidget *separator;
2424 long ivalue;
2425 char ascii_password[PASSWD_LEN];
2426 int r;
2427 int password_not_correct;
2428 int retry;
2429 int cycle_category = FALSE;
2430 long char_set;
2431 long show_tooltips;
2432 char *cat_name;
2433 int new_cat;
2434 int index, index2;
2435 int i;
2436 #ifdef HAVE_LIBGCRYPT
2437 static int gcrypt_init = 0;
2438 #endif
2439
2440 jp_logf(JP_LOG_DEBUG, "KeyRing: plugin gui started, unique_id=%d\n", unique_id);
2441
2442 if (check_for_db()) {
2443 return EXIT_FAILURE;
2444 }
2445
2446 #ifdef HAVE_LIBGCRYPT
2447 if (!gcrypt_init) {
2448 gcrypt_init = 1;
2449
2450 /* Version check should be the very first call because it
2451 makes sure that important subsystems are intialized. */
2452 if (!gcry_check_version(GCRYPT_VERSION)) {
2453 fputs("libgcrypt version mismatch\n", stderr);
2454 return EXIT_FAILURE;
2455 }
2456
2457 /* We don't want to see any warnings, e.g. because we have not yet
2458 parsed program options which might be used to suppress such
2459 warnings. */
2460 gcry_control(GCRYCTL_SUSPEND_SECMEM_WARN);
2461
2462 /* ... If required, other initialization goes here. Note that the
2463 process might still be running with increased privileges and that
2464 the secure memory has not been intialized. */
2465
2466 /* Allocate a pool of 16k secure memory. This make the secure memory
2467 available and also drops privileges where needed. */
2468 gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0);
2469
2470 /* It is now okay to let Libgcrypt complain when there was/is
2471 a problem with the secure memory. */
2472 gcry_control(GCRYCTL_RESUME_SECMEM_WARN);
2473
2474 /* ... If required, other initialization goes here. */
2475
2476 /* Tell Libgcrypt that initialization has completed. */
2477 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
2478 }
2479 #endif
2480
2481 /* Find the main window from some widget */
2482 w = GTK_WINDOW(gtk_widget_get_toplevel(hbox));
2483
2484 #if 0
2485 /* Change Password button */
2486 button = gtk_button_new_with_label(_("Change\nKeyRing\nPassword"));
2487 g_signal_connect(G_OBJECT(button), "clicked",
2488 G_CALLBACK(cb_change_password), NULL);
2489 gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0);
2490 #endif
2491
2492 if (difftime(time(NULL), plugin_last_time) > PLUGIN_MAX_INACTIVE_TIME) {
2493 /* reset last time we entered */
2494 plugin_last_time = 0;
2495
2496 password_not_correct = TRUE;
2497 retry = PASSWD_ENTER;
2498 while (password_not_correct) {
2499 r = dialog_password(w, ascii_password, retry);
2500 retry = PASSWD_ENTER_RETRY;
2501 if (r != 2) {
2502 memset(ascii_password, 0, PASSWD_LEN - 1);
2503 return 0;
2504 }
2505 password_not_correct = (verify_pasword(ascii_password) > 0);
2506 }
2507 memset(ascii_password, 0, PASSWD_LEN - 1);
2508 } else {
2509 cycle_category = TRUE;
2510 }
2511
2512 /* called to display the result of a search */
2513 if (unique_id) {
2514 cycle_category = FALSE;
2515 }
2516
2517 /* plugin entered with correct password */
2518 plugin_last_time = time(NULL);
2519 plugin_active = TRUE;
2520
2521 /************************************************************/
2522 /* Build GUI */
2523 record_changed = CLEAR_FLAG;
2524 row_selected = 0;
2525
2526 /* Do some initialization */
2527
2528 get_keyr_cat_info(&keyr_app_info);
2529 get_pref(PREF_CHAR_SET, &char_set, NULL);
2530
2531 for (i = 1; i < NUM_KEYRING_CAT_ITEMS; i++) {
2532 cat_name = charset_p2newj(keyr_app_info.name[i], 31, char_set);
2533 strcpy(sort_l[i - 1].Pcat, cat_name);
2534 free(cat_name);
2535 sort_l[i - 1].cat_num = i;
2536 }
2537 /* put reserved 'Unfiled' category at end of list */
2538 cat_name = charset_p2newj(keyr_app_info.name[0], 31, char_set);
2539 strcpy(sort_l[NUM_KEYRING_CAT_ITEMS - 1].Pcat, cat_name);
2540 free(cat_name);
2541 sort_l[NUM_KEYRING_CAT_ITEMS - 1].cat_num = 0;
2542
2543 qsort(sort_l, NUM_KEYRING_CAT_ITEMS - 1, sizeof(struct sorted_cats), cat_compare);
2544
2545 #ifdef JPILOT_DEBUG
2546 for (i=0; i<NUM_KEYRING_CAT_ITEMS; i++) {
2547 printf("cat %d [%s]\n", sort_l[i].cat_num, sort_l[i].Pcat);
2548 }
2549 #endif
2550
2551 if (keyr_category > NUM_KEYRING_CAT_ITEMS) {
2552 keyr_category = CATEGORY_ALL;
2553 }
2554
2555 /* Make accelerators for some buttons window */
2556 #ifndef ENABLE_STOCK_BUTTONS
2557 accel_group = gtk_accel_group_new();
2558 gtk_window_add_accel_group(GTK_WINDOW(gtk_widget_get_toplevel(vbox)), accel_group);
2559 #endif
2560 get_pref(PREF_SHOW_TOOLTIPS, &show_tooltips, NULL);
2561
2562 pane = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
2563 get_pref(PREF_KEYRING_PANE, &ivalue, NULL);
2564 gtk_paned_set_position(GTK_PANED(pane), ivalue);
2565
2566 gtk_box_pack_start(GTK_BOX(hbox), pane, TRUE, TRUE, 5);
2567
2568 /* left and right main boxes */
2569 vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2570 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2571 gtk_paned_pack1(GTK_PANED(pane), vbox1, TRUE, FALSE);
2572 gtk_paned_pack2(GTK_PANED(pane), vbox2, TRUE, FALSE);
2573
2574 gtk_widget_set_size_request(GTK_WIDGET(vbox1), 0, 230);
2575 gtk_widget_set_size_request(GTK_WIDGET(vbox2), 0, 230);
2576
2577 /**********************************************************************/
2578 /* Left half of screen */
2579 /**********************************************************************/
2580 /* Left-side Category menu */
2581 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2582 gtk_box_pack_start(GTK_BOX(vbox1), hbox_temp, FALSE, FALSE, 0);
2583
2584 make_category_menu(&category_menu1,
2585 sort_l, cb_category, TRUE, FALSE);
2586
2587 gtk_box_pack_start(GTK_BOX(hbox_temp), category_menu1, TRUE, TRUE, 0);
2588
2589 /* Scrolled window */
2590 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2591 gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 0);
2592 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2593 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2594 gtk_box_pack_start(GTK_BOX(vbox1), scrolled_window, TRUE, TRUE, 0);
2595
2596 /* listStore */
2597 listStore = gtk_list_store_new(KEYRING_NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
2598 G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN);
2599 GtkTreeModel *model = GTK_TREE_MODEL(listStore);
2600 treeView = gtk_tree_view_new_with_model(model);
2601 GtkCellRenderer *changedRenderer = gtk_cell_renderer_text_new();
2602 GtkTreeViewColumn *changedColumn = gtk_tree_view_column_new_with_attributes(_("Changed"),
2603 changedRenderer,
2604 "text", KEYRING_CHANGED_COLUMN_ENUM,
2605 "cell-background-rgba",
2606 KEYRING_BACKGROUND_COLOR_ENUM,
2607 "cell-background-set",
2608 KEYRING_BACKGROUND_COLOR_ENABLED_ENUM,
2609 NULL);
2610 gtk_tree_view_column_set_sort_column_id(changedColumn, KEYRING_CHANGED_COLUMN_ENUM);
2611 GtkCellRenderer *nameRenderer = gtk_cell_renderer_text_new();
2612 GtkTreeViewColumn *nameColumn = gtk_tree_view_column_new_with_attributes(_("Name"),
2613 nameRenderer,
2614 "text", KEYRING_NAME_COLUMN_ENUM,
2615 "cell-background-rgba",
2616 KEYRING_BACKGROUND_COLOR_ENUM,
2617 "cell-background-set",
2618 KEYRING_BACKGROUND_COLOR_ENABLED_ENUM,
2619 NULL);
2620 gtk_tree_view_column_set_sort_column_id(nameColumn, KEYRING_NAME_COLUMN_ENUM);
2621 GtkCellRenderer *accountRenderer = gtk_cell_renderer_text_new();
2622 GtkTreeViewColumn *accountColumn = gtk_tree_view_column_new_with_attributes(_("Account"),
2623 accountRenderer,
2624 "text", KEYRING_ACCOUNT_COLUMN_ENUM,
2625 "cell-background-rgba",
2626 KEYRING_BACKGROUND_COLOR_ENUM,
2627 "cell-background-set",
2628 KEYRING_BACKGROUND_COLOR_ENABLED_ENUM,
2629 NULL);
2630 gtk_tree_view_column_set_sort_column_id(accountColumn, KEYRING_ACCOUNT_COLUMN_ENUM);
2631 gtk_tree_view_insert_column(GTK_TREE_VIEW(treeView), changedColumn, KEYRING_CHANGED_COLUMN_ENUM);
2632 gtk_tree_view_insert_column(GTK_TREE_VIEW(treeView), nameColumn, KEYRING_NAME_COLUMN_ENUM);
2633 gtk_tree_view_insert_column(GTK_TREE_VIEW(treeView), accountColumn, KEYRING_ACCOUNT_COLUMN_ENUM);
2634 gtk_tree_view_column_set_clickable(changedColumn, gtk_true());
2635 gtk_tree_view_column_set_clickable(nameColumn, gtk_true());
2636 gtk_tree_view_column_set_clickable(accountColumn, gtk_true());
2637 gtk_tree_view_column_set_sizing(changedColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2638 gtk_tree_view_column_set_fixed_width(nameColumn, 150);
2639 gtk_tree_view_column_set_sizing(accountColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2640 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView)),
2641 GTK_SELECTION_BROWSE);
2642 GtkTreeSortable *sortable = GTK_TREE_SORTABLE(listStore);
2643 gtk_tree_sortable_set_sort_func(sortable, KEYRING_CHANGED_COLUMN_ENUM, GtkTreeModelKeyrCompareDates,
2644 GINT_TO_POINTER(KEYRING_CHANGED_COLUMN_ENUM), NULL);
2645 gtk_tree_sortable_set_sort_func(sortable, KEYRING_NAME_COLUMN_ENUM, GtkTreeModelKeyrCompareNocase,
2646 GINT_TO_POINTER(KEYRING_NAME_COLUMN_ENUM), NULL);
2647 for (int x = 0; x < KEYRING_NUM_COLS - 5; x++) {
2648 gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), x), gtk_false());
2649 }
2650 gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), column_selected),
2651 gtk_true());
2652 gtk_widget_set_events(GTK_WIDGET(treeView), GDK_BUTTON1_MOTION_MASK);
2653 g_signal_connect (G_OBJECT(treeView), "motion_notify_event",
2654 G_CALLBACK(motion_notify_event), NULL);
2655 g_signal_connect (G_OBJECT(treeView), "button-press-event",
2656 G_CALLBACK(button_pressed_for_motion), NULL);
2657 g_signal_connect (G_OBJECT(treeView), "button-release-event",
2658 G_CALLBACK(button_released_for_motion), NULL);
2659 g_signal_connect (changedColumn, "clicked", G_CALLBACK(column_clicked_cb), NULL);
2660 g_signal_connect (nameColumn, "clicked", G_CALLBACK(column_clicked_cb), NULL);
2661 g_signal_connect (accountColumn, "clicked", G_CALLBACK(column_clicked_cb), NULL);
2662
2663 GtkTreeSelection *treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
2664
2665 gtk_tree_selection_set_select_function(treeSelection, handleKeyringRowSelection, NULL, NULL);
2666 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolled_window),
2667 GTK_POLICY_NEVER,
2668 GTK_POLICY_AUTOMATIC);
2669 gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(treeView));
2670
2671 /**********************************************************************/
2672 /* Right half of screen */
2673 /**********************************************************************/
2674 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
2675 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
2676
2677 /* Cancel button */
2678 CREATE_BUTTON(cancel_record_button, _("Cancel"), CANCEL, _("Cancel the modifications"), GDK_KEY_Escape, 0, "ESC")
2679 g_signal_connect(G_OBJECT(cancel_record_button), "clicked",
2680 G_CALLBACK(cb_cancel), NULL);
2681
2682 /* Delete button */
2683 CREATE_BUTTON(delete_record_button, _("Delete"), DELETE, _("Delete the selected record"), GDK_d, GDK_CONTROL_MASK,
2684 "Ctrl+D");
2685 g_signal_connect(G_OBJECT(delete_record_button), "clicked",
2686 G_CALLBACK(cb_delete_keyring),
2687 GINT_TO_POINTER(DELETE_FLAG));
2688
2689 /* Undelete button */
2690 CREATE_BUTTON(undelete_record_button, _("Undelete"), UNDELETE, _("Undelete the selected record"), 0, 0, "")
2691 g_signal_connect(G_OBJECT(undelete_record_button), "clicked",
2692 G_CALLBACK(cb_undelete_keyring),
2693 GINT_TO_POINTER(UNDELETE_FLAG));
2694
2695 /* Copy button */
2696 CREATE_BUTTON(copy_record_button, _("Copy"), COPY, _("Copy the selected record"), GDK_c,
2697 GDK_CONTROL_MASK | GDK_SHIFT_MASK, "Ctrl+Shift+C")
2698 g_signal_connect(G_OBJECT(copy_record_button), "clicked",
2699 G_CALLBACK(cb_add_new_record),
2700 GINT_TO_POINTER(COPY_FLAG));
2701
2702 /* New Record button */
2703 CREATE_BUTTON(new_record_button, _("New Record"), NEW, _("Add a new record"), GDK_n, GDK_CONTROL_MASK, "Ctrl+N")
2704 g_signal_connect(G_OBJECT(new_record_button), "clicked",
2705 G_CALLBACK(cb_add_new_record),
2706 GINT_TO_POINTER(CLEAR_FLAG));
2707
2708 /* Add Record button */
2709 CREATE_BUTTON(add_record_button, _("Add Record"), ADD, _("Add the new record"), GDK_KEY_Return, GDK_CONTROL_MASK,
2710 "Ctrl+Enter")
2711 g_signal_connect(G_OBJECT(add_record_button), "clicked",
2712 G_CALLBACK(cb_add_new_record),
2713 GINT_TO_POINTER(NEW_FLAG));
2714 #ifndef ENABLE_STOCK_BUTTONS
2715 gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(gtk_bin_get_child(GTK_BIN(add_record_button)))), "label_high");
2716 #endif
2717
2718 /* Apply Changes button */
2719 CREATE_BUTTON(apply_record_button, _("Apply Changes"), APPLY, _("Commit the modifications"), GDK_KEY_Return,
2720 GDK_CONTROL_MASK, "Ctrl+Enter")
2721 g_signal_connect(G_OBJECT(apply_record_button), "clicked",
2722 G_CALLBACK(cb_add_new_record),
2723 GINT_TO_POINTER(MODIFY_FLAG));
2724 #ifndef ENABLE_STOCK_BUTTONS
2725 gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(gtk_bin_get_child(GTK_BIN(apply_record_button)))), "label_high");
2726 #endif
2727
2728 /* Separator */
2729 separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2730 gtk_box_pack_start(GTK_BOX(vbox2), separator, FALSE, FALSE, 5);
2731
2732 /* Grid */
2733 grid = gtk_grid_new();
2734 gtk_grid_set_column_homogeneous(GTK_GRID(grid), FALSE);
2735 gtk_box_pack_start(GTK_BOX(vbox2), grid, TRUE, TRUE, 5);
2736
2737
2738 /* Category menu */
2739 label = gtk_label_new(_("Category: "));
2740 gtk_widget_set_hexpand(GTK_WIDGET(label), FALSE);
2741 gtk_widget_set_vexpand(GTK_WIDGET(label), FALSE);
2742 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_END);
2743 gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2744 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(label), 0, 0, 1, 1);
2745 make_category_menu(&category_menu2,
2746 sort_l, NULL, FALSE, FALSE);
2747 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(category_menu2), 1, 0, 2, 1);
2748 gtk_widget_set_halign(GTK_WIDGET(category_menu2), GTK_ALIGN_FILL);
2749 gtk_widget_set_valign(GTK_WIDGET(category_menu2), GTK_ALIGN_CENTER);
2750 gtk_widget_set_hexpand(GTK_WIDGET(category_menu2), TRUE);
2751
2752 /* Name entry */
2753 label = gtk_label_new(_("name: "));
2754 gtk_widget_set_hexpand(GTK_WIDGET(label), FALSE);
2755 gtk_widget_set_vexpand(GTK_WIDGET(label), FALSE);
2756 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_END);
2757 gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2758 entry_name = gtk_entry_new();
2759 entry_set_multiline_truncate(GTK_ENTRY(entry_name), TRUE);
2760 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(label), 0, 1, 1, 1);
2761 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(entry_name), 1, 1, 2, 1);
2762 gtk_widget_set_halign(GTK_WIDGET(entry_name), GTK_ALIGN_FILL);
2763 gtk_widget_set_valign(GTK_WIDGET(entry_name), GTK_ALIGN_CENTER);
2764 gtk_widget_set_hexpand(GTK_WIDGET(entry_name), TRUE);
2765
2766 /* Account entry */
2767 label = gtk_label_new(_("account: "));
2768 gtk_widget_set_hexpand(GTK_WIDGET(label), FALSE);
2769 gtk_widget_set_vexpand(GTK_WIDGET(label), FALSE);
2770 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_END);
2771 gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2772 //gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_SHRINK);
2773 //gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_SHRINK);
2774 entry_account = gtk_entry_new();
2775 entry_set_multiline_truncate(GTK_ENTRY(entry_account), TRUE);
2776 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(label), 0, 2, 1, 1);
2777 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(entry_account), 1, 2, 2, 1);
2778 gtk_widget_set_halign(GTK_WIDGET(entry_account), GTK_ALIGN_FILL);
2779 gtk_widget_set_valign(GTK_WIDGET(entry_account), GTK_ALIGN_FILL);
2780 gtk_widget_set_hexpand(GTK_WIDGET(entry_account), TRUE);
2781
2782 /* Password entry */
2783 label = gtk_label_new(_("password: "));
2784 gtk_widget_set_hexpand(GTK_WIDGET(label), FALSE);
2785 gtk_widget_set_vexpand(GTK_WIDGET(label), FALSE);
2786 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_END);
2787 gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2788 entry_password = gtk_entry_new();
2789 gtk_widget_set_halign(GTK_WIDGET(entry_password), GTK_ALIGN_FILL);
2790 gtk_widget_set_valign(GTK_WIDGET(entry_password), GTK_ALIGN_CENTER);
2791 gtk_widget_set_hexpand(GTK_WIDGET(entry_password), TRUE);
2792
2793 /* Generate Password button (creates random password) */
2794 button = gtk_button_new_with_label(_("Generate Password"));
2795 g_signal_connect(G_OBJECT(button), "clicked",
2796 G_CALLBACK(cb_gen_password), entry_password);
2797
2798 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(label), 0, 3, 1, 1);
2799 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(entry_password), 1, 3, 1, 1);
2800 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(button), 2, 3, 1, 1);
2801
2802
2803 /* Last Changed entry */
2804 label = gtk_label_new(_("last changed: "));
2805 gtk_widget_set_hexpand(GTK_WIDGET(label), FALSE);
2806 gtk_widget_set_vexpand(GTK_WIDGET(label), FALSE);
2807 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_END);
2808 gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2809
2810 date_button = gtk_button_new_with_label("");
2811 g_signal_connect(G_OBJECT(date_button), "clicked",
2812 G_CALLBACK(cb_date_button), date_button);
2813 gtk_widget_set_halign(GTK_WIDGET(date_button), GTK_ALIGN_FILL);
2814 gtk_widget_set_valign(GTK_WIDGET(date_button), GTK_ALIGN_CENTER);
2815 gtk_widget_set_hexpand(GTK_WIDGET(date_button), TRUE);
2816
2817 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(label), 0, 4, 1, 1);
2818 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(date_button), 1, 4, 2, 1);
2819
2820 /* Note textbox */
2821 label = gtk_label_new(_("Note"));
2822 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2823 gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2824 gtk_widget_set_hexpand(GTK_WIDGET(label), FALSE);
2825 gtk_widget_set_vexpand(GTK_WIDGET(label), FALSE);
2826 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2827 gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER);
2828 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(label), 0, 5, 3, 1);
2829
2830
2831 keyr_note = gtk_text_view_new();
2832 keyr_note_buffer = G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyr_note)));
2833 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyr_note), TRUE);
2834 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyr_note), GTK_WRAP_WORD);
2835
2836 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2837 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2838 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2839 gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 1);
2840 gtk_container_add(GTK_CONTAINER(scrolled_window), keyr_note);
2841 gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(scrolled_window), 0, 6, 3, 1);
2842
2843 // Make the scrolled_window take up extra space
2844 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_FILL);
2845 gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_FILL);
2846 gtk_widget_set_hexpand(GTK_WIDGET(scrolled_window), TRUE);
2847 gtk_widget_set_vexpand(GTK_WIDGET(scrolled_window), TRUE);
2848
2849 /**********************************************************************/
2850
2851 gtk_widget_show_all(hbox);
2852 gtk_widget_show_all(vbox);
2853
2854 gtk_widget_hide(add_record_button);
2855 gtk_widget_hide(apply_record_button);
2856 gtk_widget_hide(undelete_record_button);
2857 gtk_widget_hide(cancel_record_button);
2858
2859 if (cycle_category) {
2860 /* First cycle keyr_category var */
2861 if (keyr_category == CATEGORY_ALL) {
2862 new_cat = -1;
2863 } else {
2864 new_cat = find_sort_cat_pos(keyr_category);
2865 }
2866 for (i = 0; i < NUM_KEYRING_CAT_ITEMS; i++) {
2867 new_cat++;
2868 if (new_cat >= NUM_KEYRING_CAT_ITEMS) {
2869 keyr_category = CATEGORY_ALL;
2870 break;
2871 }
2872 if ((sort_l[new_cat].Pcat) && (sort_l[new_cat].Pcat[0])) {
2873 keyr_category = sort_l[new_cat].cat_num;
2874 break;
2875 }
2876 }
2877 /* Then update menu with new keyr_category */
2878 if (keyr_category == CATEGORY_ALL) {
2879 index = 0;
2880 index2 = 0;
2881 } else {
2882 index = find_sort_cat_pos(keyr_category);
2883 index2 = find_menu_cat_pos(index) + 1;
2884 index += 1;
2885 }
2886 if (index < 0) {
2887 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
2888 } else {
2889 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), index2);
2890 }
2891 } else {
2892 keyr_category = CATEGORY_ALL;
2893 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), 0);
2894 }
2895 keyr_update_liststore(listStore, &glob_keyring_list, keyr_category, TRUE);
2896
2897 if (unique_id) {
2898 keyring_find(unique_id);
2899 }
2900
2901 return EXIT_SUCCESS;
2902 }
2903