"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. For more information about "keyring.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.8.2_vs_2_0_1.

    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(&current_key1, &s1);
  532     DES_set_key(&current_key2, &s2);
  533 #else
  534     des_set_key(&current_key1, s1);
  535     des_set_key(&current_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(&ltime);
  773                 now = localtime(&ltime);
  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(&ltime);
 1029     now = localtime(&ltime);
 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(&ltime);
 2088         now = localtime(&ltime);
 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