"Fossies" - the Fresh Open Source Software Archive

Member "jpilot-2_0_1/KeyRing/keyring.c" (3 Apr 2021, 101505 Bytes) of package /linux/privat/jpilot-2_0_1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*******************************************************************************
    2  * keyring.c
    3  *
    4  * This is a plugin for J-Pilot for the KeyRing Palm program.
    5  * It keeps records and uses DES3 encryption.
    6  *
    7  * Copyright (C) 2001-2014 by Judd Montgomery
    8  *
    9  * This program is free software; you can redistribute it and/or modify
   10  * it under the terms of the GNU General Public License as published by
   11  * the Free Software Foundation; version 2 of the License.
   12  *
   13  * This program is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, write to the Free Software
   20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   21  ******************************************************************************/
   22 
   23 /********************************* Includes ***********************************/
   24 #include <stdlib.h>
   25 #include <stdio.h>
   26 #include <string.h>
   27 #include <time.h>
   28 #include <unistd.h>
   29 #include <errno.h>
   30 #include <sys/stat.h>
   31 #include <gtk/gtk.h>
   32 
   33 #include "config.h"
   34 
   35 #ifdef HAVE_LIBGCRYPT
   36 
   37 #  include <gcrypt.h>
   38 
   39 #else
   40 /* OpenSSL header files */
   41 #  include <openssl/md5.h>
   42 #  include <openssl/des.h>
   43 
   44 #endif
   45 
   46 /* Pilot-link header files */
   47 #include <pi-appinfo.h>
   48 #include <pi-dlp.h>
   49 #include <pi-file.h>
   50 
   51 /* Jpilot header files */
   52 #include "libplugin.h"
   53 #include "utils.h"
   54 #include "i18n.h"
   55 #include "prefs.h"
   56 #include "stock_buttons.h"
   57 #include "export.h"
   58 
   59 /********************************* Constants **********************************/
   60 #define KEYRING_CAT1 1
   61 #define KEYRING_CAT2 2
   62 #define NUM_KEYRING_CAT_ITEMS 16
   63 
   64 #define PASSWD_ENTER       0
   65 #define PASSWD_ENTER_RETRY 1
   66 #define PASSWD_ENTER_NEW   2
   67 
   68 #define PASSWD_LEN 100
   69 
   70 #define CONNECT_SIGNALS    400
   71 #define DISCONNECT_SIGNALS 401
   72 #define PASSWD_FLAG 1
   73 
   74 #define KEYR_CHGD_COLUMN 0
   75 #define KEYR_NAME_COLUMN 1
   76 #define KEYR_ACCT_COLUMN 2
   77 
   78 /* re-ask password PLUGIN_MAX_INACTIVE_TIME seconds after 
   79  * deselecting the plugin */
   80 #define PLUGIN_MAX_INACTIVE_TIME 10
   81 
   82 /* for password hashes */
   83 #define SALT_SIZE 4
   84 #define MESSAGE_BUF_SIZE 64
   85 #define MD5_HASH_SIZE 16
   86 
   87 #define MIN_KR_PASS (20)   /* Minimum auto-generated passwd length */
   88 #define MAX_KR_PASS (25)   /* Maximum auto-generated passwd length */
   89 
   90 enum {
   91     KEYRING_CHANGED_COLUMN_ENUM,
   92     KEYRING_NAME_COLUMN_ENUM,
   93     KEYRING_ACCOUNT_COLUMN_ENUM,
   94     KEYRING_DATA_COLUMN_ENUM,
   95     KEYRING_BACKGROUND_COLOR_ENUM,
   96     KEYRING_BACKGROUND_COLOR_ENABLED_ENUM,
   97     KEYRING_FOREGROUND_COLOR_ENUM,
   98     KEYRINGS_FOREGROUND_COLOR_ENABLED_ENUM,
   99     KEYRING_NUM_COLS
  100 };
  101 struct KeyRing {
  102     char *name;     /* Unencrypted */
  103     char *account;  /* Encrypted */
  104     char *password; /* Encrypted */
  105     char *note;     /* Encrypted */
  106     struct tm last_changed; /* Encrypted */
  107 };
  108 /* My wrapper to the KeyRing structure so that I can put a few more 
  109  * fields in with it.  */
  110 struct MyKeyRing {
  111     PCRecType rt;
  112     unsigned int unique_id;
  113     unsigned char attrib;
  114     struct KeyRing kr;
  115     struct MyKeyRing *next;
  116 };
  117 
  118 /******************************* Global vars **********************************/
  119 /* This is the category that is currently being displayed */
  120 static struct CategoryAppInfo keyr_app_info;
  121 static int keyr_category = CATEGORY_ALL;
  122 
  123 static GtkWidget *treeView;
  124 static GtkListStore *listStore;
  125 static GtkWidget *entry_name;
  126 static GtkWidget *entry_account;
  127 static GtkWidget *entry_password;
  128 static GtkWidget *keyr_note;
  129 static GObject *keyr_note_buffer;
  130 static GtkWidget *category_menu1;
  131 static GtkWidget *category_menu2;
  132 static struct sorted_cats sort_l[NUM_KEYRING_CAT_ITEMS];
  133 static GtkWidget *pane = NULL;
  134 static GtkWidget *scrolled_window;
  135 static GtkWidget *new_record_button;
  136 static GtkWidget *apply_record_button;
  137 static GtkWidget *add_record_button;
  138 static GtkWidget *delete_record_button;
  139 static GtkWidget *undelete_record_button;
  140 static GtkWidget *copy_record_button;
  141 static GtkWidget *cancel_record_button;
  142 static GtkWidget *date_button;
  143 static struct tm glob_date;
  144 #ifndef ENABLE_STOCK_BUTTONS
  145 static GtkAccelGroup *accel_group;
  146 #endif
  147 static int record_changed;
  148 static int column_selected;
  149 static int row_selected;
  150 
  151 #ifdef HAVE_LIBGCRYPT
  152 static unsigned char key[24];
  153 #else
  154 #ifdef HEADER_NEW_DES_H
  155 static DES_cblock current_key1;
  156 static DES_cblock current_key2;
  157 static DES_key_schedule s1, s2;
  158 #else
  159 static des_cblock current_key1;
  160 static des_cblock current_key2;
  161 static des_key_schedule s1, s2;
  162 #endif
  163 #endif
  164 
  165 static time_t plugin_last_time = 0;
  166 static gboolean plugin_active = FALSE;
  167 
  168 static struct MyKeyRing *glob_keyring_list = NULL;
  169 static struct MyKeyRing *export_keyring_list = NULL;
  170 
  171 /****************************** Prototypes ************************************/
  172 
  173 void keyr_update_liststore(GtkListStore *pListStore, struct MyKeyRing **keyring_list,
  174                            int category, int main);
  175 
  176 static void connect_changed_signals(int con_or_dis);
  177 
  178 static int keyring_find(int unique_id);
  179 
  180 static void update_date_button(GtkWidget *button, struct tm *t);
  181 
  182 static gboolean handleKeyringRowSelection(GtkTreeSelection *selection,
  183                                           GtkTreeModel *model,
  184                                           GtkTreePath *path,
  185                                           gboolean path_currently_selected,
  186                                           gpointer userdata);
  187 
  188 void deleteKeyRing(struct MyKeyRing *mkr, gpointer data);
  189 
  190 void undeleteKeyRing(struct MyKeyRing *mkr, gpointer data);
  191 
  192 void addKeyRing(struct MyKeyRing *mkr, gpointer data);
  193 
  194 static GtkWidget *cb_keyr_export_init_treeView();
  195 
  196 /****************************** Main Code *************************************/
  197 
  198 /* Routine to get category app info from raw buffer. 
  199  * KeyRing is broken and uses a non-standard length CategoryAppInfo.
  200  * The KeyRing structure is 276 bytes whereas pilot-link uses 278.
  201  * Code below is taken from unpack_CategoryAppInfo in pilot-link but modified
  202  * for the shortened structure. */
  203 static int keyr_plugin_unpack_cai_from_ai(struct CategoryAppInfo *cai,
  204                                           unsigned char *record,
  205                                           int len) {
  206     int i, rec;
  207 
  208     jp_logf(JP_LOG_DEBUG, "unpack_keyring_cai_from_ai\n");
  209 
  210     if (len < 2 + 16 * 16 + 16 + 2)
  211         return EXIT_FAILURE;
  212     rec = get_short(record);
  213     for (i = 0; i < 16; i++) {
  214         if (rec & (1 << i))
  215             cai->renamed[i] = 1;
  216         else
  217             cai->renamed[i] = 0;
  218     }
  219     record += 2;
  220     for (i = 0; i < 16; i++) {
  221         memcpy(cai->name[i], record, 16);
  222         record += 16;
  223     }
  224     memcpy(cai->ID, record, 16);
  225     record += 16;
  226     cai->lastUniqueID = get_byte(record);
  227 
  228     return EXIT_SUCCESS;
  229 }
  230 
  231 int plugin_unpack_cai_from_ai(struct CategoryAppInfo *cai,
  232                               unsigned char *record,
  233                               int len) {
  234     return keyr_plugin_unpack_cai_from_ai(cai, record, len);
  235 }
  236 
  237 /* Routine to pack CategoryAppInfo struct into non-standard size buffer */
  238 int plugin_pack_cai_into_ai(struct CategoryAppInfo *cai,
  239                             unsigned char *record,
  240                             int len) {
  241     int i, rec;
  242 
  243     if (!record) {
  244         return EXIT_SUCCESS;
  245     }
  246     if (len < (2 + 16 * 16 + 16 + 2))
  247         return EXIT_FAILURE;   /* not enough room */
  248     rec = 0;
  249     for (i = 0; i < 16; i++) {
  250         if (cai->renamed[i])
  251             rec |= (1 << i);
  252     }
  253     set_short(record, rec);
  254     record += 2;
  255     for (i = 0; i < 16; i++) {
  256         memcpy(record, cai->name[i], 16);
  257         record += 16;
  258     }
  259     memcpy(record, cai->ID, 16);
  260     record += 16;
  261     set_byte(record, cai->lastUniqueID);
  262     record++;
  263     set_byte(record, 0);      /* gapfill */
  264 
  265     return EXIT_SUCCESS;
  266 }
  267 
  268 static int pack_KeyRing(struct KeyRing *kr,
  269                         unsigned char *buf,
  270                         int buf_size,
  271                         int *wrote_size) {
  272     int n;
  273     int i;
  274     char empty[] = "";
  275     char last_changed[2];
  276     unsigned short packed_date;
  277 #ifdef HAVE_LIBGCRYPT
  278     gcry_error_t err;
  279     gcry_cipher_hd_t hd;
  280 #endif
  281 
  282     jp_logf(JP_LOG_DEBUG, "KeyRing: pack_KeyRing()\n");
  283 
  284     packed_date = (((kr->last_changed.tm_year - 4) << 9) & 0xFE00) |
  285                   (((kr->last_changed.tm_mon + 1) << 5) & 0x01E0) |
  286                   (kr->last_changed.tm_mday & 0x001F);
  287     set_short(last_changed, packed_date);
  288 
  289     *wrote_size = 0;
  290 
  291     if (!(kr->name)) kr->name = empty;
  292     if (!(kr->account)) kr->account = empty;
  293     if (!(kr->password)) kr->password = empty;
  294     if (!(kr->note)) kr->note = empty;
  295 
  296     /* 2 is for the lastChanged date */
  297     /* 3 chars accounts for NULL string terminators */
  298     n = strlen(kr->account) + strlen(kr->password) + strlen(kr->note) + 2 + 3;
  299     /* The encrypted portion must be a multiple of 8 */
  300     if ((n % 8)) {
  301         n = n + (8 - (n % 8));
  302     }
  303     /* Now we can add in the unencrypted part */
  304     n = n + strlen(kr->name) + 1;
  305     jp_logf(JP_LOG_DEBUG, "pack n=%d\n", n);
  306 
  307     if (n + 2 > buf_size) {
  308         jp_logf(JP_LOG_WARN, _("KeyRing: pack_KeyRing(): buf_size too small\n"));
  309         return EXIT_FAILURE;
  310     }
  311 
  312     memset(buf, 0, n + 1);
  313     *wrote_size = n;
  314     strcpy((char *) buf, kr->name);
  315     i = strlen(kr->name) + 1;
  316     strcpy((char *) &buf[i], kr->account);
  317     i += strlen(kr->account) + 1;
  318     strcpy((char *) &buf[i], kr->password);
  319     i += strlen(kr->password) + 1;
  320     strcpy((char *) &buf[i], kr->note);
  321     i += strlen(kr->note) + 1;
  322     strncpy((char *) &buf[i], last_changed, 2);
  323 #ifdef HAVE_LIBGCRYPT
  324     err = gcry_cipher_open(&hd, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0);
  325     if (err)
  326         jp_logf(JP_LOG_DEBUG, "gcry_cipher_open: %s\n", gpg_strerror(err));
  327 
  328     err = gcry_cipher_setkey(hd, key, sizeof(key));
  329     if (err)
  330         jp_logf(JP_LOG_DEBUG, "gcry_cipher_setkey: %s\n", gpg_strerror(err));
  331 
  332     for (i = strlen(kr->name) + 1; i < n; i += 8) {
  333         char tmp[8];
  334         err = gcry_cipher_encrypt(hd, tmp, 8, &buf[i], 8);
  335         if (err)
  336             jp_logf(JP_LOG_DEBUG, "gcry_cipher_encrypt: %s\n", gpg_strerror(err));
  337         memcpy(&buf[i], tmp, 8);
  338     }
  339 
  340     gcry_cipher_close(hd);
  341 #else
  342     for (i=strlen(kr->name)+1; i<n; i=i+8) {
  343 #ifdef HEADER_NEW_DES_H
  344        DES_ecb3_encrypt((DES_cblock *)&buf[i], (DES_cblock *)&buf[i],
  345                         &s1, &s2, &s1, DES_ENCRYPT);
  346 #else
  347        des_ecb3_encrypt((const_des_cblock *)&buf[i], (des_cblock *)(&buf[i]),
  348                         s1, s2, s1, DES_ENCRYPT);
  349 #endif
  350     }
  351 #endif
  352 
  353 #ifdef JPILOT_DEBUG
  354     for (i=0;i<n; i++) {
  355        printf("%02x ", (unsigned char)buf[i]);
  356     }
  357     printf("\n");
  358 #endif
  359 
  360     return n;
  361 }
  362 
  363 static int unpack_KeyRing(struct KeyRing *kr,
  364                           unsigned char *buf,
  365                           int buf_size) {
  366     int i, j;
  367     int n;
  368     int rem;
  369     unsigned char *clear_text;
  370     unsigned char *P;
  371     unsigned char *Pstr[4];
  372     const char *safety[] = {"", "", "", ""};
  373     unsigned short packed_date;
  374 #ifdef HAVE_LIBGCRYPT
  375     gcry_error_t err;
  376     gcry_cipher_hd_t hd;
  377 #endif
  378 
  379     jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing\n");
  380     if (!memchr(buf, '\0', buf_size)) {
  381         jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): No null terminator found in buf\n");
  382         return 0;
  383     }
  384     n = strlen((char *) buf) + 1;
  385 
  386     rem = buf_size - n;
  387     if (rem > 0xFFFF) {
  388         /* This can be caused by a bug in libplugin.c from jpilot 0.99.1
  389          * and before.  It occurs on the last record */
  390         jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): buffer too big n=%d, buf_size=%d\n", n, buf_size);
  391         jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): truncating\n");
  392         rem = 0xFFFF - n;
  393         rem = rem - (rem % 8);
  394     }
  395     clear_text = malloc(rem + 8); /* Allow for some safety NULLs */
  396     memset(clear_text, 0, rem + 8);
  397 
  398     jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): rem (should be multiple of 8)=%d\n", rem);
  399     jp_logf(JP_LOG_DEBUG, "KeyRing: unpack_KeyRing(): rem%%8=%d\n", rem % 8);
  400 
  401     P = &buf[n];
  402 #ifdef HAVE_LIBGCRYPT
  403     err = gcry_cipher_open(&hd, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0);
  404     if (err)
  405         jp_logf(JP_LOG_DEBUG, "gcry_cipher_open: %s\n", gpg_strerror(err));
  406 
  407     err = gcry_cipher_setkey(hd, key, sizeof(key));
  408     if (err)
  409         jp_logf(JP_LOG_DEBUG, "gcry_cipher_setkey: %s\n", gpg_strerror(err));
  410 
  411     err = gcry_cipher_decrypt(hd, clear_text, rem, P, rem);
  412     if (err)
  413         jp_logf(JP_LOG_DEBUG, "gcry_cipher_decrypt: %s\n", gpg_strerror(err));
  414 
  415     gcry_cipher_close(hd);
  416 #else
  417     for (i=0; i<rem; i+=8) {
  418 #ifdef HEADER_NEW_DES_H
  419        DES_ecb3_encrypt((DES_cblock *)&P[i], (DES_cblock *)(clear_text+i),
  420                         &s1, &s2, &s1, DES_DECRYPT);
  421 #else
  422        des_ecb3_encrypt((const_des_cblock *)&P[i], (des_cblock *)(clear_text+i),
  423                         s1, s2, s1, DES_DECRYPT);
  424 #endif
  425     }
  426 #endif
  427 
  428     Pstr[0] = clear_text;
  429     Pstr[1] = (unsigned char *) safety[1];
  430     Pstr[2] = (unsigned char *) safety[2];
  431     Pstr[3] = (unsigned char *) safety[3];
  432 
  433     for (i = 0, j = 1; (i < rem) && (j < 4); i++) {
  434         if (!clear_text[i]) {
  435             Pstr[j] = &clear_text[i + 1];
  436             j++;
  437         }
  438     }
  439     /*
  440     kr->name=strdup((char *)buf);
  441     kr->account=strdup((char *)Pstr[0]);
  442     kr->password=strdup((char *)Pstr[1]);
  443     kr->note=strdup((char *)Pstr[2]);
  444     */
  445 
  446     kr->name = jp_charset_p2newj((char *) buf, -1);
  447     kr->account = jp_charset_p2newj((char *) Pstr[0], -1);
  448     kr->password = jp_charset_p2newj((char *) Pstr[1], -1);
  449     kr->note = jp_charset_p2newj((char *) Pstr[2], -1);
  450 
  451     packed_date = get_short(Pstr[3]);
  452     kr->last_changed.tm_year = ((packed_date & 0xFE00) >> 9) + 4;
  453     kr->last_changed.tm_mon = ((packed_date & 0x01E0) >> 5) - 1;
  454     kr->last_changed.tm_mday = (packed_date & 0x001F);
  455     kr->last_changed.tm_hour = 0;
  456     kr->last_changed.tm_min = 0;
  457     kr->last_changed.tm_sec = 0;
  458     kr->last_changed.tm_isdst = -1;
  459 
  460     if (0 == packed_date) {
  461         kr->last_changed.tm_year = 0;
  462         kr->last_changed.tm_mon = 0;
  463         kr->last_changed.tm_mday = 0;
  464     }
  465 
  466 #ifdef DEBUG
  467     printf("name  [%s]\n", buf);
  468     printf("Pstr0 [%s]\n", Pstr[0]);
  469     printf("Pstr1 [%s]\n", Pstr[1]);
  470     printf("Pstr2 [%s]\n", Pstr[2]);
  471     printf("last_changed %d-%d-%d\n",
  472             kr->last_changed.tm_year,
  473             kr->last_changed.tm_mon,
  474             kr->last_changed.tm_mday);
  475 #endif
  476 
  477     free(clear_text);
  478 
  479     return 1;
  480 }
  481 
  482 static int get_keyr_cat_info(struct CategoryAppInfo *cai) {
  483     unsigned char *buf;
  484     int buf_size;
  485 
  486     memset(cai, 0, sizeof(struct CategoryAppInfo));
  487     jp_get_app_info("Keys-Gtkr", &buf, &buf_size);
  488     keyr_plugin_unpack_cai_from_ai(cai, buf, buf_size);
  489     free(buf);
  490 
  491     return EXIT_SUCCESS;
  492 }
  493 
  494 /*
  495  * Return EXIT_FAILURE if password isn't good.
  496  * Return EXIT_SUCCESS if good and global and also sets s1, and s2 set
  497  */
  498 static int set_password_hash(unsigned char *buf, int buf_size, char *passwd) {
  499     unsigned char buffer[MESSAGE_BUF_SIZE];
  500     unsigned char md[MD5_HASH_SIZE];
  501 
  502     if (buf_size < MD5_HASH_SIZE) {
  503         return EXIT_FAILURE;
  504     }
  505     /* Must wipe passwd out of memory after using it */
  506     memset(buffer, 0, MESSAGE_BUF_SIZE);
  507     memcpy(buffer, buf, SALT_SIZE);
  508     strncpy((char *) (buffer + SALT_SIZE), passwd, MESSAGE_BUF_SIZE - SALT_SIZE - 1);
  509 #ifdef HAVE_LIBGCRYPT
  510     gcry_md_hash_buffer(GCRY_MD_MD5, md, buffer, MESSAGE_BUF_SIZE);
  511 #else
  512     MD5(buffer, MESSAGE_BUF_SIZE, md);
  513 #endif
  514 
  515     /* wipe out password traces */
  516     memset(buffer, 0, MESSAGE_BUF_SIZE);
  517 
  518     if (memcmp(md, buf + SALT_SIZE, MD5_HASH_SIZE)) {
  519         return EXIT_FAILURE;
  520     }
  521 
  522 #ifdef HAVE_LIBGCRYPT
  523     gcry_md_hash_buffer(GCRY_MD_MD5, md, passwd, strlen(passwd));
  524     memcpy(key, md, 16);    /* k1 and k2 */
  525     memcpy(key + 16, md, 8);  /* k1 again */
  526 #else
  527     MD5((unsigned char *)passwd, strlen(passwd), md);
  528     memcpy(current_key1, md, 8);
  529     memcpy(current_key2, md+8, 8);
  530 #ifdef HEADER_NEW_DES_H
  531     DES_set_key(&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