diff --git a/evolution-data-server-1.9.1-kill-ememory.patch b/evolution-data-server-1.9.1-kill-ememory.patch deleted file mode 100644 index aa6f216..0000000 --- a/evolution-data-server-1.9.1-kill-ememory.patch +++ /dev/null @@ -1,528 +0,0 @@ ---- evolution-data-server-1.11.3/camel/camel-text-index.c.kill-ememory 2007-06-01 03:06:38.000000000 -0400 -+++ evolution-data-server-1.11.3/camel/camel-text-index.c 2007-06-04 12:28:31.000000000 -0400 -@@ -70,7 +70,7 @@ typedef struct _CamelTextIndexNamePrivat - struct _CamelTextIndexNamePrivate { - GString *buffer; - camel_key_t nameid; -- EMemPool *pool; -+ GStringChunk *string_chunk; - }; - - CamelTextIndexName *camel_text_index_name_new(CamelTextIndex *idx, const char *name, camel_key_t nameid); -@@ -1374,7 +1374,7 @@ text_index_name_add_word(CamelIndexName - struct _CamelTextIndexNamePrivate *p = ((CamelTextIndexName *)idn)->priv; - - if (g_hash_table_lookup(idn->words, word) == NULL) { -- char *w = e_mempool_strdup(p->pool, word); -+ char *w = g_string_chunk_insert(p->string_chunk, word); - - g_hash_table_insert(idn->words, w, w); - } -@@ -1502,7 +1502,7 @@ camel_text_index_name_init(CamelTextInde - - p = idn->priv = g_malloc0(sizeof(*idn->priv)); - p->buffer = g_string_new(""); -- p->pool = e_mempool_new(256, 128, E_MEMPOOL_ALIGN_BYTE); -+ p->string_chunk = g_string_chunk_new(256); - } - - static void -@@ -1513,7 +1513,7 @@ camel_text_index_name_finalise(CamelText - g_hash_table_destroy(idn->parent.words); - - g_string_free(p->buffer, TRUE); -- e_mempool_destroy(p->pool); -+ g_string_chunk_free(p->string_chunk); - - g_free(p); - } -@@ -1545,7 +1545,7 @@ camel_text_index_name_new(CamelTextIndex - - cin->index = (CamelIndex *)idx; - camel_object_ref((CamelObject *)idx); -- cin->name = e_mempool_strdup(p->pool, name); -+ cin->name = g_string_chunk_insert(p->string_chunk, name); - p->nameid = nameid; - - return idn; ---- evolution-data-server-1.11.3/camel/camel-mime-parser.c.kill-ememory 2007-05-09 00:09:34.000000000 -0400 -+++ evolution-data-server-1.11.3/camel/camel-mime-parser.c 2007-06-04 12:28:31.000000000 -0400 -@@ -120,7 +120,7 @@ struct _header_scan_stack { - camel_mime_parser_state_t savestate; /* state at invocation of this part */ - - #ifdef MEMPOOL -- EMemPool *pool; /* memory pool to keep track of headers/etc at this level */ -+ GStringChunk *string_chunk; /* string chunk to keep track of headers/etc at this level */ - #endif - struct _camel_header_raw *headers; /* headers for this part */ - -@@ -999,7 +999,8 @@ folder_pull_part(struct _header_scan_sta - s->parts = h->parent; - g_free(h->boundary); - #ifdef MEMPOOL -- e_mempool_destroy(h->pool); -+ if (h->string_chunk != NULL) -+ g_string_chunk_free(h->string_chunk); - #else - camel_header_raw_clear(&h->headers); - #endif -@@ -1107,20 +1108,16 @@ header_append_mempool(struct _header_sca - content = strchr(header, ':'); - if (content) { - register int len; -- n = e_mempool_alloc(h->pool, sizeof(*n)); -+ n = g_malloc(sizeof(*n)); - n->next = NULL; - - len = content-header; -- n->name = e_mempool_alloc(h->pool, len+1); -- memcpy(n->name, header, len); -- n->name[len] = 0; -+ n->name = g_string_chunk_insert_len(h->string_chunk, header, len); - - content++; - - len = s->outptr - content; -- n->value = e_mempool_alloc(h->pool, len+1); -- memcpy(n->value, content, len); -- n->value[len] = 0; -+ n->value = g_string_chunk_insert_len(h->string_chunk, content, len); - - n->offset = offset; - -@@ -1178,7 +1175,7 @@ folder_scan_header(struct _header_scan_s - - h = g_malloc0(sizeof(*h)); - #ifdef MEMPOOL -- h->pool = e_mempool_new(8192, 4096, E_MEMPOOL_ALIGN_STRUCT); -+ h->string_chunk = g_string_chunk_new(8192); - #endif - - if (s->parts) ---- evolution-data-server-1.11.3/camel/camel-folder.c.kill-ememory 2007-06-04 01:40:57.000000000 -0400 -+++ evolution-data-server-1.11.3/camel/camel-folder.c 2007-06-04 12:30:21.000000000 -0400 -@@ -1755,7 +1755,7 @@ struct _CamelFolderChangeInfoPrivate { - GHashTable *uid_stored; /* what we have stored, which array they're in */ - GHashTable *uid_source; /* used to create unique lists */ - GPtrArray *uid_filter; /* uids to be filtered */ -- struct _EMemPool *uid_pool; /* pool used to store copies of uid strings */ -+ GStringChunk *uid_string_chunk; /* used to store copies of uid strings */ - }; - - -@@ -1934,7 +1934,7 @@ camel_folder_change_info_new(void) - info->priv->uid_stored = g_hash_table_new(g_str_hash, g_str_equal); - info->priv->uid_source = NULL; - info->priv->uid_filter = g_ptr_array_new(); -- info->priv->uid_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE); -+ info->priv->uid_string_chunk = g_string_chunk_new(512); - - return info; - } -@@ -1960,7 +1960,7 @@ camel_folder_change_info_add_source(Came - p->uid_source = g_hash_table_new(g_str_hash, g_str_equal); - - if (g_hash_table_lookup(p->uid_source, uid) == NULL) -- g_hash_table_insert(p->uid_source, e_mempool_strdup(p->uid_pool, uid), GINT_TO_POINTER (1)); -+ g_hash_table_insert(p->uid_source, g_string_chunk_insert(p->uid_string_chunk, uid), GINT_TO_POINTER (1)); - } - - -@@ -1989,7 +1989,7 @@ camel_folder_change_info_add_source_list - char *uid = list->pdata[i]; - - if (g_hash_table_lookup(p->uid_source, uid) == NULL) -- g_hash_table_insert(p->uid_source, e_mempool_strdup(p->uid_pool, uid), GINT_TO_POINTER (1)); -+ g_hash_table_insert(p->uid_source, g_string_chunk_insert(p->uid_string_chunk, uid), GINT_TO_POINTER (1)); - } - } - -@@ -2101,7 +2101,7 @@ change_info_recent_uid(CamelFolderChange - - /* always add to recent, but dont let anyone else know */ - if (!g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) { -- olduid = e_mempool_strdup(p->uid_pool, uid); -+ olduid = g_string_chunk_insert(p->uid_string_chunk, uid); - } - g_ptr_array_add(info->uid_recent, olduid); - } -@@ -2117,7 +2117,7 @@ change_info_filter_uid(CamelFolderChange - - /* always add to filter, but dont let anyone else know */ - if (!g_hash_table_lookup_extended(p->uid_stored, uid, (void **)&olduid, (void **)&olduids)) { -- olduid = e_mempool_strdup(p->uid_pool, uid); -+ olduid = g_string_chunk_insert(p->uid_string_chunk, uid); - } - g_ptr_array_add(p->uid_filter, olduid); - } -@@ -2182,7 +2182,7 @@ camel_folder_change_info_add_uid(CamelFo - return; - } - -- olduid = e_mempool_strdup(p->uid_pool, uid); -+ olduid = g_string_chunk_insert(p->uid_string_chunk, uid); - g_ptr_array_add(info->uid_added, olduid); - g_hash_table_insert(p->uid_stored, olduid, info->uid_added); - } -@@ -2216,7 +2216,7 @@ camel_folder_change_info_remove_uid(Came - return; - } - -- olduid = e_mempool_strdup(p->uid_pool, uid); -+ olduid = g_string_chunk_insert(p->uid_string_chunk, uid); - g_ptr_array_add(info->uid_removed, olduid); - g_hash_table_insert(p->uid_stored, olduid, info->uid_removed); - } -@@ -2245,7 +2245,7 @@ camel_folder_change_info_change_uid(Came - return; - } - -- olduid = e_mempool_strdup(p->uid_pool, uid); -+ olduid = g_string_chunk_insert(p->uid_string_chunk, uid); - g_ptr_array_add(info->uid_changed, olduid); - g_hash_table_insert(p->uid_stored, olduid, info->uid_changed); - } -@@ -2314,7 +2314,8 @@ camel_folder_change_info_clear(CamelFold - g_hash_table_destroy(p->uid_stored); - p->uid_stored = g_hash_table_new(g_str_hash, g_str_equal); - g_ptr_array_set_size(p->uid_filter, 0); -- e_mempool_flush(p->uid_pool, TRUE); -+ g_string_chunk_free(p->uid_string_chunk); -+ p->uid_string_chunk = g_string_chunk_new(512); - } - - -@@ -2338,7 +2339,7 @@ camel_folder_change_info_free(CamelFolde - - g_hash_table_destroy(p->uid_stored); - g_ptr_array_free(p->uid_filter, TRUE); -- e_mempool_destroy(p->uid_pool); -+ g_string_chunk_free(p->uid_string_chunk); - g_free(p); - - g_ptr_array_free(info->uid_added, TRUE); ---- evolution-data-server-1.11.3/camel/camel-folder-summary.c.kill-ememory 2007-06-01 03:06:38.000000000 -0400 -+++ evolution-data-server-1.11.3/camel/camel-folder-summary.c 2007-06-04 12:28:31.000000000 -0400 -@@ -134,10 +134,6 @@ camel_folder_summary_init (CamelFolderSu - s->message_info_chunks = NULL; - s->content_info_chunks = NULL; - --#if defined (DOESTRV) || defined (DOEPOOLV) -- s->message_info_strings = CAMEL_MESSAGE_INFO_LAST; --#endif -- - s->version = CAMEL_FOLDER_SUMMARY_VERSION; - s->flags = 0; - s->time = 0; -@@ -875,13 +871,6 @@ camel_folder_summary_add(CamelFolderSumm - - CAMEL_SUMMARY_LOCK(s, summary_lock); - --/* unnecessary for pooled vectors */ --#ifdef DOESTRV -- /* this is vitally important, and also if this is ever modified, then -- the hash table needs to be resynced */ -- info->strings = e_strv_pack(info->strings); --#endif -- - g_ptr_array_add(s->messages, info); - g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info); - s->flags |= CAMEL_SUMMARY_DIRTY; ---- evolution-data-server-1.11.3/camel/providers/local/camel-maildir-summary.c.kill-ememory 2007-05-09 00:09:30.000000000 -0400 -+++ evolution-data-server-1.11.3/camel/providers/local/camel-maildir-summary.c 2007-06-04 12:28:31.000000000 -0400 -@@ -129,10 +129,6 @@ camel_maildir_summary_init (CamelMaildir - s->message_info_size = sizeof(CamelMaildirMessageInfo); - s->content_info_size = sizeof(CamelMaildirMessageContentInfo); - --#if defined (DOEPOOLV) || defined (DOESTRV) -- s->message_info_strings = CAMEL_MAILDIR_INFO_LAST; --#endif -- - if (gethostname(hostname, 256) == 0) { - o->priv->hostname = g_strdup(hostname); - } else { -@@ -329,11 +325,9 @@ static CamelMessageInfo *message_info_ne - - static void message_info_free(CamelFolderSummary *s, CamelMessageInfo *mi) - { --#if !defined (DOEPOOLV) && !defined (DOESTRV) - CamelMaildirMessageInfo *mdi = (CamelMaildirMessageInfo *)mi; - - g_free(mdi->filename); --#endif - ((CamelFolderSummaryClass *) parent_class)->message_info_free(s, mi); - } - -@@ -412,7 +406,7 @@ static int maildir_summary_load(CamelLoc - struct dirent *d; - CamelMaildirSummary *mds = (CamelMaildirSummary *)cls; - char *uid; -- EMemPool *pool; -+ GStringChunk *string_chunk; - int ret; - - cur = g_strdup_printf("%s/cur", cls->folder_path); -@@ -429,7 +423,7 @@ static int maildir_summary_load(CamelLoc - } - - mds->priv->load_map = g_hash_table_new(g_str_hash, g_str_equal); -- pool = e_mempool_new(1024, 512, E_MEMPOOL_ALIGN_BYTE); -+ string_chunk = g_string_chunk_new(1024); - - while ( (d = readdir(dir)) ) { - if (d->d_name[0] == '.') -@@ -438,13 +432,13 @@ static int maildir_summary_load(CamelLoc - /* map the filename -> uid */ - uid = strchr(d->d_name, ':'); - if (uid) { -- int len = uid-d->d_name; -- uid = e_mempool_alloc(pool, len+1); -- memcpy(uid, d->d_name, len); -- uid[len] = 0; -- g_hash_table_insert(mds->priv->load_map, uid, e_mempool_strdup(pool, d->d_name)); -+ char *cp = uid; -+ *cp = '\0'; -+ uid = g_string_chunk_insert(string_chunk, uid); -+ *cp = ':'; -+ g_hash_table_insert(mds->priv->load_map, uid, g_string_chunk_insert(string_chunk, d->d_name)); - } else { -- uid = e_mempool_strdup(pool, d->d_name); -+ uid = g_string_chunk_insert(string_chunk, d->d_name); - g_hash_table_insert(mds->priv->load_map, uid, uid); - } - } -@@ -455,7 +449,7 @@ static int maildir_summary_load(CamelLoc - - g_hash_table_destroy(mds->priv->load_map); - mds->priv->load_map = NULL; -- e_mempool_destroy(pool); -+ g_string_chunk_free(string_chunk); - - return ret; - } -@@ -621,23 +615,8 @@ maildir_summary_check(CamelLocalSummary - filename = camel_maildir_info_filename(mdi); - /* TODO: only store the extension in the mdi->filename struct, not the whole lot */ - if (filename == NULL || strcmp(filename, d->d_name) != 0) { --#ifdef DOESTRV --#warning "cannot modify the estrv after its been setup, for mt-safe code" -- CAMEL_SUMMARY_LOCK(s, summary_lock); -- /* need to update the summary hash ref */ -- g_hash_table_remove(s->messages_uid, camel_message_info_uid(info)); -- info->strings = e_strv_set_ref(info->strings, CAMEL_MAILDIR_INFO_FILENAME, d->d_name); -- info->strings = e_strv_pack(info->strings); -- g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info); -- CAMEL_SUMMARY_UNLOCK(s, summary_lock); --#else --# ifdef DOEPOOLV -- info->strings = e_poolv_set(info->strings, CAMEL_MAILDIR_INFO_FILENAME, d->d_name, FALSE); --# else - g_free(mdi->filename); - mdi->filename = g_strdup(d->d_name); --# endif --#endif - } - camel_message_info_free(info); - } -@@ -727,9 +706,6 @@ maildir_summary_sync(CamelLocalSummary * - int count, i; - CamelMessageInfo *info; - CamelMaildirMessageInfo *mdi; --#ifdef DOESTRV -- CamelFolderSummary *s = (CamelFolderSummary *)cls; --#endif - char *name; - struct stat st; - -@@ -775,26 +751,8 @@ maildir_summary_sync(CamelLocalSummary * - /* we'll assume it didn't work, but dont change anything else */ - g_free(newname); - } else { -- /* TODO: If this is made mt-safe, then this code could be a problem, since -- the estrv is being modified. -- Sigh, this may mean the maildir name has to be cached another way */ --#ifdef DOESTRV --#warning "cannot modify the estrv after its been setup, for mt-safe code" -- CAMEL_SUMMARY_LOCK(s, summary_lock); -- /* need to update the summary hash ref */ -- g_hash_table_remove(s->messages_uid, camel_message_info_uid(info)); -- info->strings = e_strv_set_ref_free(info->strings, CAMEL_MAILDIR_INFO_FILENAME, newname); -- info->strings = e_strv_pack(info->strings); -- g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info); -- CAMEL_SUMMARY_UNLOCK(s, summary_lock); --#else --# ifdef DOEPOOLV -- info->strings = e_poolv_set(info->strings, CAMEL_MAILDIR_INFO_FILENAME, newname, TRUE); --# else - g_free(mdi->filename); - mdi->filename = newname; --# endif --#endif - } - g_free(name); - g_free(dest); ---- evolution-data-server-1.11.3/camel/camel-folder-search.c.kill-ememory 2007-05-09 00:09:33.000000000 -0400 -+++ evolution-data-server-1.11.3/camel/camel-folder-search.c 2007-06-04 12:28:31.000000000 -0400 -@@ -53,7 +53,7 @@ - #define r(x) - - struct _CamelFolderSearchPrivate { -- GHashTable *mempool_hash; -+ GHashTable *string_chunk_hash; - CamelException *ex; - - CamelFolderThread *threads; -@@ -123,23 +123,23 @@ camel_folder_search_init (CamelFolderSea - - obj->sexp = e_sexp_new(); - -- /* use a hash of mempools to associate the returned uid lists with -- the backing mempool. yes pretty weird, but i didn't want to change -- the api just yet */ -+ /* use a hash of string chunks to associate the returned uid lists -+ * with the backing string chunk. yes pretty weird, but i didn't -+ * want to change the api just yet */ - -- p->mempool_hash = g_hash_table_new(0, 0); -+ p->string_chunk_hash = g_hash_table_new(0, 0); - } - - static void --free_mempool(void *key, void *value, void *data) -+free_string_chunk (void *key, void *value, void *data) - { - GPtrArray *uids = key; -- EMemPool *pool = value; -+ GStringChunk *string_chunk = value; - - g_warning("Search closed with outstanding result unfreed: %p", uids); - - g_ptr_array_free(uids, TRUE); -- e_mempool_destroy(pool); -+ g_string_chunk_free(string_chunk); - } - - static void -@@ -154,8 +154,8 @@ camel_folder_search_finalize (CamelObjec - g_hash_table_destroy(search->summary_hash); - - g_free(search->last_search); -- g_hash_table_foreach(p->mempool_hash, free_mempool, obj); -- g_hash_table_destroy(p->mempool_hash); -+ g_hash_table_foreach(p->string_chunk_hash, free_string_chunk, obj); -+ g_hash_table_destroy(p->string_chunk_hash); - g_free(p); - } - -@@ -345,7 +345,7 @@ camel_folder_search_execute_expression(C - GPtrArray *matches; - int i; - GHashTable *results; -- EMemPool *pool; -+ GStringChunk *string_chunk; - struct _CamelFolderSearchPrivate *p = _PRIVATE(search); - - p->ex = ex; -@@ -374,11 +374,7 @@ camel_folder_search_execute_expression(C - /* now create a folder summary to return?? */ - if (r->type == ESEXP_RES_ARRAY_PTR) { - d(printf("got result ...\n")); -- /* we use a mempool to store the strings, packed in tight as possible, and freed together */ -- /* because the strings are often short (like <8 bytes long), we would be wasting appx 50% -- of memory just storing the size tag that malloc assigns us and alignment padding, so this -- gets around that (and is faster to allocate and free as a bonus) */ -- pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE); -+ string_chunk = g_string_chunk_new(512); - if (search->summary) { - /* reorder result in summary order */ - results = g_hash_table_new(g_str_hash, g_str_equal); -@@ -390,20 +386,21 @@ camel_folder_search_execute_expression(C - CamelMessageInfo *info = g_ptr_array_index(search->summary, i); - char *uid = (char *)camel_message_info_uid(info); - if (g_hash_table_lookup(results, uid)) { -- g_ptr_array_add(matches, e_mempool_strdup(pool, uid)); -+ g_ptr_array_add(matches, g_string_chunk_insert(string_chunk, uid)); - } - } - g_hash_table_destroy(results); - } else { - for (i=0;ivalue.ptrarray->len;i++) { - d(printf("adding match: %s\n", (char *)g_ptr_array_index(r->value.ptrarray, i))); -- g_ptr_array_add(matches, e_mempool_strdup(pool, g_ptr_array_index(r->value.ptrarray, i))); -+ g_ptr_array_add(matches, g_string_chunk_insert(string_chunk, g_ptr_array_index(r->value.ptrarray, i))); - } - } -- /* instead of putting the mempool_hash in the structure, we keep the api clean by -- putting a reference to it in a hashtable. Lets us do some debugging and catch -- unfree'd results as well. */ -- g_hash_table_insert(p->mempool_hash, matches, pool); -+ /* instead of putting the string_chunk_hash in the structure, -+ * we keep the api clean by putting a reference to it in a -+ * hashtable. Lets us do some debugging and catch unfree'd -+ * results as well. */ -+ g_hash_table_insert(p->string_chunk_hash, matches, string_chunk); - } else { - g_warning("Search returned an invalid result type"); - } -@@ -444,7 +441,7 @@ camel_folder_search_search(CamelFolderSe - GPtrArray *matches = NULL, *summary_set; - int i; - GHashTable *results; -- EMemPool *pool; -+ GStringChunk *string_chunk; - struct _CamelFolderSearchPrivate *p = _PRIVATE(search); - - g_assert(search->folder); -@@ -496,12 +493,7 @@ camel_folder_search_search(CamelFolderSe - /* now create a folder summary to return?? */ - if (r->type == ESEXP_RES_ARRAY_PTR) { - d(printf("got result ...\n")); -- -- /* we use a mempool to store the strings, packed in tight as possible, and freed together */ -- /* because the strings are often short (like <8 bytes long), we would be wasting appx 50% -- of memory just storing the size tag that malloc assigns us and alignment padding, so this -- gets around that (and is faster to allocate and free as a bonus) */ -- pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE); -+ string_chunk = g_string_chunk_new(512); - /* reorder result in summary order */ - results = g_hash_table_new(g_str_hash, g_str_equal); - for (i=0;ivalue.ptrarray->len;i++) { -@@ -513,14 +505,15 @@ camel_folder_search_search(CamelFolderSe - CamelMessageInfo *info = g_ptr_array_index(summary_set, i); - char *uid = (char *)camel_message_info_uid(info); - if (g_hash_table_lookup(results, uid)) -- g_ptr_array_add(matches, e_mempool_strdup(pool, uid)); -+ g_ptr_array_add(matches, g_string_chunk_insert(string_chunk, uid)); - } - g_hash_table_destroy(results); - -- /* instead of putting the mempool_hash in the structure, we keep the api clean by -- putting a reference to it in a hashtable. Lets us do some debugging and catch -- unfree'd results as well. */ -- g_hash_table_insert(p->mempool_hash, matches, pool); -+ /* instead of putting the string_chunk_hash in the structure, -+ * we keep the api clean by putting a reference to it in a -+ * hashtable. Lets us do some debugging and catch unfree'd -+ * results as well. */ -+ g_hash_table_insert(p->string_chunk_hash, matches, string_chunk); - } else { - g_warning("Search returned an invalid result type"); - } -@@ -553,12 +546,12 @@ void camel_folder_search_free_result(Cam - { - int i; - struct _CamelFolderSearchPrivate *p = _PRIVATE(search); -- EMemPool *pool; -+ GStringChunk *string_chunk; - -- pool = g_hash_table_lookup(p->mempool_hash, result); -- if (pool) { -- e_mempool_destroy(pool); -- g_hash_table_remove(p->mempool_hash, result); -+ string_chunk = g_hash_table_lookup(p->string_chunk_hash, result); -+ if (string_chunk) { -+ g_string_chunk_free(string_chunk); -+ g_hash_table_remove(p->string_chunk_hash, result); - } else { - for (i=0;ilen;i++) - g_free(g_ptr_array_index(result, i)); diff --git a/evolution-data-server-1.9.92-e-passwords.patch b/evolution-data-server-1.9.92-e-passwords.patch deleted file mode 100644 index 12aadea..0000000 --- a/evolution-data-server-1.9.92-e-passwords.patch +++ /dev/null @@ -1,1877 +0,0 @@ ---- evolution-data-server-1.11.92/libedataserverui/e-passwords.c.e-passwords 2007-09-02 14:54:22.000000000 -0400 -+++ evolution-data-server-1.11.92/libedataserverui/e-passwords.c 2007-09-03 23:13:10.000000000 -0400 -@@ -23,18 +23,29 @@ - */ - - /* -- * This looks a lot more complicated than it is, and than you'd think -- * it would need to be. There is however, method to the madness. -+ * The threading model was changed in version 1.11. Here's the details. - * -- * The code most cope with being called from any thread at any time, -- * recursively from the main thread, and then serialising every -- * request so that sane and correct values are always returned, and -- * duplicate requests are never made. -+ * We now use a dedicated "dispatcher" thread to process password messages. -+ * The callback functions do not use synchronization guards when accessing -+ * the password store, so those functions must only be called from the -+ * dispatcher thread. - * -- * To this end, every call is marshalled and queued and a dispatch -- * method invoked until that request is satisfied. If mainloop -- * recursion occurs, then the sub-call will necessarily return out of -- * order, but will not be processed out of order. -+ * Message processing behind a visible password dialog is the trickiest -+ * part to get right. We want to, at all costs, avoid repeatedly prompting -+ * the user for the same password. To that end, when the password dialog -+ * for some key 'X' is visible, the dispatcher thread is blocked and -+ * subsequent messages for key 'X' are expedited by redirecting them to a -+ * special "express queue" [1]. -+ * -+ * Once the password dialog is closed, all messages in the express queue -+ * are either (depending on the user's response) cancelled or loaded with -+ * the new password and returned to the sender. The express queue is then -+ * closed so that no new messages can enter, and the dispatcher thread -+ * resumes its normal processing. -+ * -+ * [1] Yes, it's a stupid name, but it's the best I could think of. It's -+ * somewhat analogous to an express lane on a freeway because messages -+ * get through faster. - */ - - #ifdef HAVE_CONFIG_H -@@ -42,159 +53,381 @@ - #endif - - #include --#include -+#include - #include --#include --#include --#include --#include --#include - - #include "e-passwords.h" --#include "libedataserver/e-msgport.h" -+#include "libedataserver/e-flag.h" - #include "libedataserver/e-url.h" - - #ifdef WITH_GNOME_KEYRING - #include - #endif - --#ifndef ENABLE_THREADS --#define ENABLE_THREADS (1) --#endif -- --#ifdef ENABLE_THREADS --#include -- --static pthread_t main_thread; --static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; --#define LOCK() pthread_mutex_lock(&lock) --#define UNLOCK() pthread_mutex_unlock(&lock) --#else --#define LOCK() --#define UNLOCK() --#endif -+#define d(x) - --#define d(x) x -+typedef struct _EPassMsg EPassMsg; -+typedef struct _EPassDialogData EPassDialogData; -+typedef void (*EPassCallback) (EPassMsg *msg); - - struct _EPassMsg { -- EMsg msg; - -- void (*dispatch)(struct _EPassMsg *); -+ /* The 'expedite' flag indicates that the message -+ * is eligible for entry into the express queue. */ - -- /* input */ -- struct _GtkWindow *parent; -- const char *component; -- const char *key; -- const char *title; -- const char *prompt; -- const char *oldpass; -- guint32 flags; -+ /* dispatching */ -+ EPassCallback callback; -+ gboolean expedite; -+ EFlag *done; -+ -+ /* input/output */ -+ const gchar *component; -+ const gchar *key; -+ gchar *password; -+ gpointer data; -+}; - -- /* output */ -- gboolean *remember; -- char *password; -+struct _EPassDialogData { - -- /* work variables */ -- GtkWidget *entry; -- GtkWidget *check; -- guint ismain:1; -- guint noreply:1; /* supress replies; when calling -- * dispatch functions from others */ -+ /* settings */ -+ GtkWindow *parent; -+ const gchar *title; -+ const gchar *prompt; -+ gint flags; -+ -+ /* input/output */ -+ gboolean remember; -+ gint response; -+ -+ /* synchronization */ -+ EFlag *done; - }; - --typedef struct _EPassMsg EPassMsg; -+/* Forward Declarations */ -+static void ep_msg_dispatch (EPassMsg *msg); - --static GHashTable *passwords = NULL; --static GtkDialog *password_dialog = NULL; --static EDList request_list = E_DLIST_INITIALISER(request_list); --static int idle_id; --static int ep_online_state = TRUE; -- --static char *decode_base64 (char *base64); --static int base64_encode_close(unsigned char *in, int inlen, gboolean break_lines, unsigned char *out, int *state, int *save); --static int base64_encode_step(unsigned char *in, int len, gboolean break_lines, unsigned char *out, int *state, int *save); -+static GThreadPool *dispatcher = NULL; -+static GHashTable *password_cache = NULL; -+static gboolean ep_online_state = TRUE; - --static gboolean --ep_idle_dispatch(void *data) -+/* The queue's lock also guards the key. */ -+static const gchar *express_key = NULL; -+static GAsyncQueue *express_queue = NULL; -+ -+#define KEY_FILE_GROUP_PREFIX "Passwords-" -+static GKeyFile *key_file = NULL; -+ -+static gchar * -+ep_key_file_get_filename (void) - { -- EPassMsg *msg; -+ /* XXX It would be nice to someday move this data elsewhere, or else -+ * fully migrate to GNOME Keyring or whatever software supercedes it. -+ * Evolution is one of the few remaining GNOME-2 applications that -+ * still uses the deprecated ~/.gnome2-private directory. */ - -- /* As soon as a password window is up we stop; it will -- re-invoke us when it has been closed down */ -- LOCK(); -- while (password_dialog == NULL && (msg = (EPassMsg *)e_dlist_remhead(&request_list))) { -- UNLOCK(); -+ return g_build_filename (g_get_home_dir (), -+ ".gnome2_private", "Evolution", NULL); -+} - -- msg->dispatch(msg); -+static gchar * -+ep_key_file_get_group (const gchar *component) -+{ -+ return g_strconcat (KEY_FILE_GROUP_PREFIX, component, NULL); -+} - -- LOCK(); -+static gchar * -+ep_key_file_normalize_key (const gchar *key) -+{ -+ /* XXX Previous code converted all slashes and equal signs in the -+ * key to underscores for use with "gnome-config" functions. While -+ * it may not be necessary to convert slashes for use with GKeyFile, -+ * and an equal sign was most likely never encountered in a key, we -+ * continue to do the same for backward-compatibility. */ -+ -+ gchar *normalized_key, *cp; -+ -+ normalized_key = g_strdup (key); -+ for (cp = normalized_key; *cp != '\0'; cp++) -+ if (*cp == '/' || *cp == '=') -+ *cp = '_'; -+ -+ return normalized_key; -+} -+ -+static void -+ep_key_file_load (void) -+{ -+ gchar *filename; -+ GError *error = NULL; -+ -+ filename = ep_key_file_get_filename (); -+ -+ if (!g_file_test (filename, G_FILE_TEST_EXISTS)) -+ goto exit; -+ -+ g_key_file_load_from_file (key_file, filename, -+ G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, -+ &error); -+ -+ if (error != NULL) { -+ g_warning ("%s", error->message); -+ g_error_free (error); - } - -- idle_id = 0; -- UNLOCK(); -+exit: -+ g_free (filename); -+} - -- return FALSE; -+static void -+ep_key_file_save (void) -+{ -+ gchar *contents; -+ gchar *filename; -+ gsize length; -+ GError *error = NULL; -+ -+ filename = ep_key_file_get_filename (); -+ contents = g_key_file_to_data (key_file, &length, NULL); -+ -+ g_file_set_contents (filename, contents, length, &error); -+ -+ if (error != NULL) { -+ g_warning ("%s", error->message); -+ g_error_free (error); -+ } -+ -+ g_free (contents); -+ g_free (filename); - } - --static EPassMsg * --ep_msg_new(void (*dispatch)(EPassMsg *)) -+static gchar * -+ep_password_encode (const gchar *password) - { -- EPassMsg *msg; -+ /* XXX The previous Base64 encoding function did not encode the -+ * password's trailing nul byte. This makes decoding the Base64 -+ * string into a nul-terminated password more difficult, but we -+ * continue to do it this way for backward-compatibility. */ -+ -+ gsize length = strlen (password); -+ return g_base64_encode ((const guchar *) password, length); -+} -+ -+static gchar * -+ep_password_decode (const gchar *encoded_password) -+{ -+ /* XXX The previous Base64 encoding function did not encode the -+ * password's trailing nul byte, so we have to append a nul byte -+ * to the decoded data to make it a nul-terminated string. */ -+ -+ gchar *password; -+ gsize length; - -- e_passwords_init(); -+ password = (gchar *) g_base64_decode (encoded_password, &length); -+ password = g_realloc (password, length + 1); -+ password[length] = '\0'; - -- msg = g_malloc0(sizeof(*msg)); -- msg->dispatch = dispatch; -- msg->msg.reply_port = e_msgport_new(); --#ifdef ENABLE_THREADS -- msg->ismain = pthread_equal(pthread_self(), main_thread); -+ return password; -+} -+ -+static gpointer -+ep_init (gpointer data) -+{ -+ /* Initialize the data structures with unbounded lifetimes. */ -+ -+ dispatcher = g_thread_pool_new ( -+ (GFunc) ep_msg_dispatch, NULL, 1, FALSE, NULL); -+ -+ password_cache = g_hash_table_new_full ( -+ g_str_hash, g_str_equal, g_free, g_free); -+ -+ express_queue = g_async_queue_new (); -+ -+#ifdef WITH_GNOME_KEYRING -+ if (!gnome_keyring_is_available ()) { -+ key_file = g_key_file_new (); -+ ep_key_file_load (); -+ } - #else -- msg->ismain = TRUE; -+ key_file = g_key_file_new (); -+ ep_key_file_load (); - #endif -+ -+ return NULL; -+} -+ -+static EPassMsg * -+ep_msg_new (EPassCallback callback, gboolean expedite) -+{ -+ static GOnce once = G_ONCE_INIT; -+ EPassMsg *msg; -+ -+ g_once (&once, ep_init, NULL); -+ -+ msg = g_slice_new0 (EPassMsg); -+ msg->callback = callback; -+ msg->expedite = expedite; -+ msg->done = e_flag_new (); -+ - return msg; - } - - static void --ep_msg_free(EPassMsg *msg) -+ep_msg_dispatch (EPassMsg *msg) -+{ -+ msg->callback (msg); -+ e_flag_set (msg->done); -+} -+ -+static gboolean -+ep_msg_expedite (EPassMsg *msg) -+{ -+ gboolean success = FALSE; -+ -+ g_async_queue_lock (express_queue); -+ if (express_key != NULL && strcmp (msg->key, express_key) == 0) { -+ g_async_queue_push_unlocked (express_queue, msg); -+ success = TRUE; -+ } -+ g_async_queue_unlock (express_queue); -+ -+ return success; -+} -+ -+static void -+ep_msg_send (EPassMsg *msg) -+{ -+ GMainContext *context; -+ -+ context = g_main_context_default (); -+ -+ /* Expedite flag requires a key. */ -+ g_assert (!msg->expedite || msg->key != NULL); -+ -+ if (!(msg->expedite && ep_msg_expedite (msg))) -+ g_thread_pool_push (dispatcher, msg, NULL); -+ -+ /* Don't block the main loop. */ -+ if (g_main_context_is_owner (context)) -+ while (!e_flag_is_set (msg->done)) -+ g_main_context_iteration (context, TRUE); -+ else -+ e_flag_wait (msg->done); -+} -+ -+static void -+ep_msg_free (EPassMsg *msg) - { -- e_msgport_destroy(msg->msg.reply_port); -- g_free(msg->password); -- g_free(msg); -+ e_flag_free (msg->done); -+ g_slice_free (EPassMsg, msg); -+} -+ -+static EPassDialogData * -+ep_dialog_data_new (GtkWindow *parent, const gchar *title, -+ const gchar *prompt, gboolean remember, gint flags) -+{ -+ EPassDialogData *data; -+ -+ data = g_slice_new0 (EPassDialogData); -+ data->done = e_flag_new (); -+ -+ data->parent = parent; -+ data->title = title; -+ data->prompt = prompt; -+ data->remember = remember; -+ data->flags = flags; -+ -+ return data; - } - - static void --ep_msg_send(EPassMsg *msg) -+ep_dialog_data_free (EPassDialogData *data) -+{ -+ e_flag_free (data->done); -+ g_slice_free (EPassDialogData, data); -+} -+ -+static gboolean -+ep_dialog_run (EPassMsg *msg) - { -- int needidle = 0; -+ /* This function must run in the main thread. */ - -- LOCK(); -- e_dlist_addtail(&request_list, (EDListNode *)&msg->msg); -- if (!idle_id) { -- if (!msg->ismain) -- idle_id = g_idle_add(ep_idle_dispatch, NULL); -+ EPassDialogData *data = msg->data; -+ GtkBox *box; -+ GtkWidget *dialog; -+ GtkWidget *entry; -+ GtkWidget *check_button = NULL; -+ gboolean visible; -+ gint remember; -+ -+ visible = !(data->flags & E_PASSWORDS_SECRET); -+ remember = data->flags & E_PASSWORDS_REMEMBER_MASK; -+ -+ /* Create a standard message dialog. */ -+ dialog = gtk_message_dialog_new ( -+ data->parent, 0, GTK_MESSAGE_QUESTION, -+ GTK_BUTTONS_OK_CANCEL, "%s", data->prompt); -+ gtk_window_set_title (GTK_WINDOW (dialog), data->title); -+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); -+ -+ box = GTK_BOX (GTK_DIALOG (dialog)->vbox); -+ -+ /* Add a GtkEntry for the password. */ -+ entry = gtk_entry_new (); -+ gtk_box_pack_start (box, entry, TRUE, FALSE, 3); -+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); -+ gtk_entry_set_visibility (GTK_ENTRY (entry), visible); -+ if (msg->password != NULL) { -+ gtk_entry_set_text (GTK_ENTRY (entry), msg->password); -+ g_free (msg->password); -+ msg->password = NULL; -+ } -+ atk_object_set_description ( -+ gtk_widget_get_accessible (entry), data->prompt); -+ gtk_widget_grab_focus (entry); -+ gtk_widget_show (entry); -+ -+ /* Add a GtkCheckButton for the "remember" option. */ -+ if (remember != E_PASSWORDS_REMEMBER_NEVER) { -+ if (data->flags & E_PASSWORDS_PASSPHRASE) -+ check_button = gtk_check_button_new_with_mnemonic ( -+ (remember == E_PASSWORDS_REMEMBER_FOREVER) ? -+ _("_Remember this passphrase") : -+ _("_Remember this passphrase for the " -+ "remainder of this session")); - else -- needidle = 1; -- } -- UNLOCK(); -+ check_button = gtk_check_button_new_with_mnemonic ( -+ (remember == E_PASSWORDS_REMEMBER_FOREVER) ? -+ _("_Remember this password") : -+ _("_Remember this password for the " -+ "remainder of this session")); -+ gtk_box_pack_start (box, check_button, TRUE, FALSE, 3); -+ gtk_toggle_button_set_active ( -+ GTK_TOGGLE_BUTTON (check_button), data->remember); -+ if (data->flags & E_PASSWORDS_DISABLE_REMEMBER) -+ gtk_widget_set_sensitive (check_button, FALSE); -+ gtk_widget_show (check_button); -+ } -+ -+ /* Run the dialog and collect the results. */ -+ data->response = gtk_dialog_run (GTK_DIALOG (dialog)); -+ msg->password = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); -+ if (check_button != NULL) -+ data->remember = gtk_toggle_button_get_active ( -+ GTK_TOGGLE_BUTTON (check_button)); -+ else -+ data->remember = FALSE; - -- if (msg->ismain) { -- EPassMsg *m; -+ gtk_widget_destroy (dialog); -+ e_flag_set (data->done); - -- if (needidle) -- ep_idle_dispatch(NULL); -- while ((m = (EPassMsg *)e_msgport_get(msg->msg.reply_port)) == NULL) -- g_main_context_iteration(NULL, TRUE); -- g_assert(m == msg); -- } else { -- EMsg *reply_msg = e_msgport_wait(msg->msg.reply_port); -- g_assert(reply_msg == &msg->msg); -- } -+ return FALSE; - } - --/* the functions that actually do the work */ -+/* The rest of these static functions must run in the dispatcher thread. */ -+ - #ifdef WITH_GNOME_KEYRING - static void --ep_clear_passwords_keyring(EPassMsg *msg) -+ep_clear_passwords_keyring (EPassMsg *msg) - { - GnomeKeyringAttributeList *attributes; - GnomeKeyringResult result; -@@ -203,11 +436,8 @@ - - result = gnome_keyring_get_default_keyring_sync (&default_keyring); - if (!default_keyring) { -- if (gnome_keyring_create_sync ("default", NULL) != GNOME_KEYRING_RESULT_OK) { -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -- return; -- } -+ if (gnome_keyring_create_sync ("default", NULL) != GNOME_KEYRING_RESULT_OK) -+ return; - default_keyring = g_strdup ("default"); - } - -@@ -232,38 +462,44 @@ - } - - g_free (default_keyring); -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); - } - #endif -+ - static void --ep_clear_passwords_file(EPassMsg *msg) -+ep_clear_passwords_keyfile (EPassMsg *msg) - { -- char *path; -+ gchar *group; -+ GError *error = NULL; - -- path = g_strdup_printf ("/Evolution/Passwords-%s", msg->component); -+ group = ep_key_file_get_group (msg->component); - -- gnome_config_private_clean_section (path); -- gnome_config_private_sync_file ("/Evolution"); -- -- g_free (path); -+ g_key_file_remove_group (key_file, group, &error); -+ if (error == NULL) -+ ep_key_file_save (); -+ else { -+ g_warning ("%s", error->message); -+ g_error_free (error); -+ } - -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -+ g_free (group); - } - --static gboolean --free_entry (gpointer key, gpointer value, gpointer user_data) -+static void -+ep_clear_passwords (EPassMsg *msg) - { -- g_free (key); -- memset (value, 0, strlen (value)); -- g_free (value); -- return TRUE; -+#ifdef WITH_GNOME_KEYRING -+ if (gnome_keyring_is_available ()) -+ ep_clear_passwords_keyring (msg); -+ else -+ ep_clear_passwords_keyfile (msg); -+#else -+ ep_clear_passwords_keyfile (msg); -+#endif - } - - #ifdef WITH_GNOME_KEYRING - static void --ep_forget_passwords_keyring(EPassMsg *msg) -+ep_forget_passwords_keyring (EPassMsg *msg) - { - GnomeKeyringAttributeList *attributes; - GnomeKeyringResult result; -@@ -272,11 +508,8 @@ - - result = gnome_keyring_get_default_keyring_sync (&default_keyring); - if (!default_keyring) { -- if (gnome_keyring_create_sync ("default", NULL) != GNOME_KEYRING_RESULT_OK) { -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -- return; -- } -+ if (gnome_keyring_create_sync ("default", NULL) != GNOME_KEYRING_RESULT_OK) -+ return; - default_keyring = g_strdup ("default"); - } - d(g_print("Get Default %d\n", result)); -@@ -301,68 +534,53 @@ - g_free (default_keyring); - - /* free up the session passwords */ -- g_hash_table_foreach_remove (passwords, free_entry, NULL); -- -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -+ g_hash_table_remove_all (password_cache); - } - #endif - - static void --ep_forget_passwords_file(EPassMsg *msg) -+ep_forget_passwords_keyfile (EPassMsg *msg) - { -- void *it; -- char *key; -+ gchar **groups; -+ gsize length, ii; - -- it = gnome_config_private_init_iterator_sections("/Evolution"); -- while ( (it = gnome_config_iterator_next(it, &key, NULL)) ) { -- if (0 == strncmp(key, "Passwords-", 10)) { -- char *section = g_strdup_printf("/Evolution/%s", key); -+ g_hash_table_remove_all (password_cache); - -- gnome_config_private_clean_section (section); -- g_free(section); -- } -- g_free(key); -+ groups = g_key_file_get_groups (key_file, &length); -+ for (ii = 0; ii < length; ii++) { -+ if (!g_str_has_prefix (groups[ii], KEY_FILE_GROUP_PREFIX)) -+ continue; -+ g_key_file_remove_group (key_file, groups[ii], NULL); - } -- -- gnome_config_private_sync_file ("/Evolution"); -- -- /* free up the session passwords */ -- g_hash_table_foreach_remove (passwords, free_entry, NULL); -- -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -+ ep_key_file_save (); -+ g_strfreev (groups); - } - --static char * --password_path (const char *component_name, const char *key) -+static void -+ep_forget_passwords (EPassMsg *msg) - { -- char *keycopy, *path; -- int i; -- keycopy = g_strdup (key); -- -- for (i = 0; i < strlen (keycopy); i ++) -- if (keycopy[i] == '/' || keycopy[i] =='=') -- keycopy[i] = '_'; -- -- path = g_strdup_printf ("/Evolution/Passwords-%s/%s", component_name, keycopy); -- -- g_free (keycopy); -- -- return path; -+#ifdef WITH_GNOME_KEYRING -+ if (gnome_keyring_is_available ()) -+ ep_forget_passwords_keyring (msg); -+ else -+ ep_forget_passwords_keyfile (msg); -+#else -+ ep_forget_passwords_keyfile (msg); -+#endif - } - - #ifdef WITH_GNOME_KEYRING - static void --ep_remember_password_keyring(EPassMsg *msg) -+ep_remember_password_keyring (EPassMsg *msg) - { -- gpointer okey, value; -+ gpointer value; - -- if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) { -+ value = g_hash_table_lookup (password_cache, msg->key); -+ if (value != NULL) { - /* add it to the on-disk cache of passwords */ - GnomeKeyringAttributeList *attributes; - GnomeKeyringResult result; -- EUri *uri = e_uri_new (okey); -+ EUri *uri = e_uri_new (msg->key); - guint32 item_id; - - if (!strcmp (uri->protocol, "ldap") && !uri->user) { -@@ -394,48 +612,48 @@ - gnome_keyring_attribute_list_free (attributes); - - /* now remove it from our session hash */ -- g_hash_table_remove (passwords, msg->key); -- g_free (okey); -- g_free (value); -+ g_hash_table_remove (password_cache, msg->key); - - e_uri_free (uri); - } -- -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); - } - #endif - - static void --ep_remember_password_file(EPassMsg *msg) -+ep_remember_password_keyfile (EPassMsg *msg) - { -- gpointer okey, value; -- char *path, *pass64; -- int len, state, save; -+ gchar *group, *key, *password; - -- if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) { -- /* add it to the on-disk cache of passwords */ -- path = password_path (msg->component, okey); -+ password = g_hash_table_lookup (password_cache, msg->key); -+ if (password == NULL) { -+ g_warning ("Password for key \"%s\" not found", msg->key); -+ return; -+ } - -- len = strlen (value); -- pass64 = g_malloc0 ((len + 2) * 4 / 3 + 1); -- state = save = 0; -- base64_encode_close (value, len, FALSE, (guchar *)pass64, &state, &save); -- -- gnome_config_private_set_string (path, pass64); -- g_free (path); -- g_free (pass64); -+ group = ep_key_file_get_group (msg->component); -+ key = ep_key_file_normalize_key (msg->key); -+ password = ep_password_encode (password); - -- /* now remove it from our session hash */ -- g_hash_table_remove (passwords, msg->key); -- g_free (okey); -- g_free (value); -+ g_hash_table_remove (password_cache, msg->key); -+ g_key_file_set_string (key_file, group, key, password); -+ ep_key_file_save (); - -- gnome_config_private_sync_file ("/Evolution"); -- } -+ g_free (group); -+ g_free (key); -+ g_free (password); -+} - -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -+static void -+ep_remember_password (EPassMsg *msg) -+{ -+#ifdef WITH_GNOME_KEYRING -+ if (gnome_keyring_is_available ()) -+ ep_remember_password_keyring (msg); -+ else -+ ep_remember_password_keyfile (msg); -+#else -+ ep_remember_password_keyfile (msg); -+#endif - } - - #ifdef WITH_GNOME_KEYRING -@@ -446,7 +664,6 @@ - GnomeKeyringResult result; - GList *matches = NULL, *tmp; - char *default_keyring = NULL; -- gpointer okey, value; - EUri *uri = e_uri_new (msg->key); - - if (!strcmp (uri->protocol, "ldap") && !uri->user) { -@@ -460,12 +677,7 @@ - uri->user = keycopy; - } - -- if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) { -- g_hash_table_remove (passwords, msg->key); -- memset (value, 0, strlen (value)); -- g_free (okey); -- g_free (value); -- } -+ g_hash_table_remove (password_cache, msg->key); - - if (!uri->host && !uri->user) - /* No need to remove from keyring for pass phrases */ -@@ -524,36 +736,45 @@ - g_free (default_keyring); - - exit: -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -- - e_uri_free(uri); - } - #endif - - static void --ep_forget_password_file (EPassMsg *msg) -+ep_forget_password_keyfile (EPassMsg *msg) - { -- gpointer okey, value; -- char *path; -+ gchar *group, *key; -+ GError *error = NULL; - -- if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) { -- g_hash_table_remove (passwords, msg->key); -- memset (value, 0, strlen (value)); -- g_free (okey); -- g_free (value); -- } -- -- /* clear it in the on disk db */ -- path = password_path (msg->component, msg->key); -- gnome_config_private_clean_key (path); -- gnome_config_private_sync_file ("/Evolution"); -- g_free (path); -- -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -+ g_hash_table_remove (password_cache, msg->key); -+ -+ group = ep_key_file_get_group (msg->component); -+ key = ep_key_file_normalize_key (msg->key); -+ -+ g_key_file_remove_key (key_file, group, key, &error); -+ if (error == NULL) -+ ep_key_file_save (); -+ else { -+ g_warning ("%s", error->message); -+ g_error_free (error); -+ } -+ -+ g_free (group); -+ g_free (key); - } - -+static void -+ep_forget_password (EPassMsg *msg) -+{ -+#ifdef WITH_GNOME_KEYRING -+ if (gnome_keyring_is_available ()) -+ ep_forget_password_keyring (msg); -+ else -+ ep_forget_password_keyfile (msg); -+#else -+ ep_forget_password_keyfile (msg); -+#endif -+} - - #ifdef WITH_GNOME_KEYRING - static void -@@ -562,11 +783,11 @@ - char *passwd; - GnomeKeyringAttributeList *attributes; - GnomeKeyringResult result; -- GList *matches = NULL, *tmp; -+ GList *matches = NULL, *tmp; - -- passwd = g_hash_table_lookup (passwords, msg->key); -+ passwd = g_hash_table_lookup (password_cache, msg->key); - if (passwd) { -- msg->password = g_strdup(passwd); -+ msg->password = g_strdup (passwd); - } else { - EUri *uri = e_uri_new (msg->key); - -@@ -630,288 +851,154 @@ - - e_uri_free (uri); - } -- -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); - } - #endif - - static void --ep_get_password_file (EPassMsg *msg) -+ep_get_password_keyfile (EPassMsg *msg) - { -- char *path, *passwd; -- char *encoded = NULL; -+ gchar *group, *key, *password; -+ GError *error = NULL; - -- passwd = g_hash_table_lookup (passwords, msg->key); -- if (passwd) { -- msg->password = g_strdup(passwd); -+ password = g_hash_table_lookup (password_cache, msg->key); -+ if (password != NULL) { -+ msg->password = g_strdup (password); -+ return; -+ } -+ -+ group = ep_key_file_get_group (msg->component); -+ key = ep_key_file_normalize_key (msg->key); -+ -+ password = g_key_file_get_string (key_file, group, key, &error); -+ if (password != NULL) { -+ msg->password = ep_password_decode (password); -+ g_free (password); - } else { -- /* not part of the session hash, look it up in the on disk db */ -- path = password_path (msg->component, msg->key); -- encoded = gnome_config_private_get_string_with_default (path, NULL); -- g_free (path); -- if (encoded) { -- msg->password = decode_base64 (encoded); -- g_free (encoded); -- } -+ g_warning ("%s", error->message); -+ g_error_free (error); - } - -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -+ g_free (group); -+ g_free (key); - } - - static void --ep_add_password (EPassMsg *msg) -+ep_get_password (EPassMsg *msg) - { -- gpointer okey, value; -- -- if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) { -- g_hash_table_remove (passwords, msg->key); -- g_free (okey); -- g_free (value); -- } -+#ifdef WITH_GNOME_KEYRING -+ if (gnome_keyring_is_available ()) -+ ep_get_password_keyring (msg); -+ else -+ ep_get_password_keyfile (msg); -+#else -+ ep_get_password_keyfile (msg); -+#endif -+} - -- g_hash_table_insert (passwords, g_strdup (msg->key), g_strdup (msg->oldpass)); -+static void -+ep_add_password (EPassMsg *msg) -+{ -+ gchar *key = g_strdup (msg->key); -+ gchar *password = g_strdup (msg->password); - -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -+ g_hash_table_insert (password_cache, key, password); - } - --static void ep_ask_password(EPassMsg *msg); -- - static void --pass_response(GtkDialog *dialog, int response, void *data) -+ep_ask_password (EPassMsg *msg) - { -- EPassMsg *msg = data; -- int type = msg->flags & E_PASSWORDS_REMEMBER_MASK; -- EDList pending = E_DLIST_INITIALISER(pending); -- EPassMsg *mw, *mn; -+ EPassDialogData *data = msg->data; -+ gchar *password; -+ gint remember; - -- if (response == GTK_RESPONSE_OK) { -- msg->password = g_strdup(gtk_entry_get_text((GtkEntry *)msg->entry)); -+ /* Open the express queue for this key. This allows us to catch -+ * incoming operations on this key while waiting for the user to -+ * provide a password. Once we have the password, immediately -+ * process any messages in the express queue. */ -+ g_async_queue_lock (express_queue); -+ express_key = msg->key; -+ g_async_queue_unlock (express_queue); - -- if (type != E_PASSWORDS_REMEMBER_NEVER) { -- int noreply = msg->noreply; -+ if (data->flags & E_PASSWORDS_REPROMPT) -+ ep_get_password (msg); - -- *msg->remember = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (msg->check)); -+ /* Run the password dialog in the main thread. */ -+ g_idle_add ((GSourceFunc) ep_dialog_run, msg); - -- msg->noreply = 1; -- -- if (*msg->remember || type == E_PASSWORDS_REMEMBER_FOREVER) { -- msg->oldpass = msg->password; -- ep_add_password(msg); -- } --#ifdef WITH_GNOME_KEYRING -- if (*msg->remember && type == E_PASSWORDS_REMEMBER_FOREVER) { -- if (gnome_keyring_is_available()) -- ep_remember_password_keyring(msg); -- else -- ep_remember_password_file(msg); -- } --#else -- if (*msg->remember && type == E_PASSWORDS_REMEMBER_FOREVER) -- ep_remember_password_file(msg); --#endif -+ /* Wait for the user to respond. */ -+ e_flag_wait (data->done); - -- msg->noreply = noreply; -- } -+ /* If the user cancelled the dialog then we need to dispatch -+ * any messages that we trapped in the express queue. */ -+ if (data->response != GTK_RESPONSE_OK) { -+ g_async_queue_lock (express_queue); -+ express_key = NULL; -+ while ((msg = g_async_queue_try_pop_unlocked (express_queue))) -+ ep_msg_dispatch (msg); -+ g_async_queue_unlock (express_queue); -+ return; - } - -- gtk_widget_destroy((GtkWidget *)dialog); -- password_dialog = NULL; -+ remember = data->flags & E_PASSWORDS_REMEMBER_MASK; - -- /* ok, here things get interesting, we suck up any pending -- * operations on this specific password, and return the same -- * result or ignore other operations */ -- -- LOCK(); -- mw = (EPassMsg *)request_list.head; -- mn = (EPassMsg *)mw->msg.ln.next; -- while (mn) { --#ifdef WITH_GNOME_KEYRING -- if ((mw->dispatch == (gnome_keyring_is_available() ? ep_forget_password_keyring : ep_forget_password_file) --#else -- if ((mw->dispatch == ep_forget_password_file --#endif --#ifdef WITH_GNOME_KEYRING -- || mw->dispatch == (gnome_keyring_is_available() ? ep_get_password_keyring : ep_get_password_file) --#else -- || mw->dispatch == ep_get_password_file --#endif -- || mw->dispatch == ep_ask_password) -- && (strcmp(mw->component, msg->component) == 0 -- && strcmp(mw->key, msg->key) == 0)) { -- e_dlist_remove((EDListNode *)mw); -- mw->password = g_strdup(msg->password); -- e_msgport_reply(&mw->msg); -- } -- mw = mn; -- mn = (EPassMsg *)mn->msg.ln.next; -- } -- UNLOCK(); -+ if (remember == E_PASSWORDS_REMEMBER_NEVER) -+ goto cleanup; - -- if (!msg->noreply) -- e_msgport_reply(&msg->msg); -+ if (data->remember || remember == E_PASSWORDS_REMEMBER_FOREVER) -+ ep_add_password (msg); - -- ep_idle_dispatch(NULL); --} -+ if (data->remember && remember == E_PASSWORDS_REMEMBER_FOREVER) -+ ep_remember_password (msg); - --static void --ep_ask_password(EPassMsg *msg) --{ -- GtkWidget *vbox; -- int type = msg->flags & E_PASSWORDS_REMEMBER_MASK; -- guint noreply = msg->noreply; -- AtkObject *a11y; -- -- msg->noreply = 1; -- -- /*password_dialog = (GtkDialog *)e_error_new(msg->parent, "mail:ask-session-password", msg->prompt, NULL);*/ -- password_dialog = (GtkDialog *)gtk_message_dialog_new (msg->parent, -- 0, -- GTK_MESSAGE_QUESTION, -- GTK_BUTTONS_OK_CANCEL, -- "%s", msg->prompt); -- gtk_window_set_title(GTK_WINDOW(password_dialog), msg->title); -- -- gtk_widget_ensure_style (GTK_WIDGET (password_dialog)); -- gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (password_dialog)->vbox), 0); -- gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (password_dialog)->action_area), 12); -- -- gtk_dialog_set_default_response(password_dialog, GTK_RESPONSE_OK); -+cleanup: - -- vbox = gtk_vbox_new (FALSE, 12); -- gtk_widget_show (vbox); -- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (password_dialog)->vbox), vbox, TRUE, FALSE, 0); -- gtk_container_set_border_width((GtkContainer *)vbox, 12); -- -- msg->entry = gtk_entry_new (); -+ password = g_strdup (msg->password); - -- a11y = gtk_widget_get_accessible (msg->entry); -- atk_object_set_description (a11y, msg->prompt); -- gtk_entry_set_visibility ((GtkEntry *)msg->entry, !(msg->flags & E_PASSWORDS_SECRET)); -- gtk_entry_set_activates_default((GtkEntry *)msg->entry, TRUE); -- gtk_box_pack_start (GTK_BOX (vbox), msg->entry, TRUE, FALSE, 3); -- gtk_widget_show (msg->entry); -- gtk_widget_grab_focus (msg->entry); -- -- if ((msg->flags & E_PASSWORDS_REPROMPT)) { --#ifdef WITH_GNOME_KEYRING -- if (gnome_keyring_is_available()) -- ep_get_password_keyring(msg); -- else -- ep_get_password_file(msg); --#else -- ep_get_password_file(msg); --#endif -- if (msg->password) { -- gtk_entry_set_text ((GtkEntry *) msg->entry, msg->password); -- g_free (msg->password); -- msg->password = NULL; -- } -+ /* Close the express queue and process any messages there. */ -+ g_async_queue_lock (express_queue); -+ express_key = NULL; -+ while ((msg = g_async_queue_try_pop_unlocked (express_queue))) { -+ msg->password = g_strdup (password); -+ e_flag_set (msg->done); - } -+ g_async_queue_unlock (express_queue); - -- /* static password, shouldn't be remembered between sessions, -- but will be remembered within the session beyond our control */ -- if (type != E_PASSWORDS_REMEMBER_NEVER) { -- if (msg->flags & E_PASSWORDS_PASSPHRASE) { -- msg->check = gtk_check_button_new_with_mnemonic(type == E_PASSWORDS_REMEMBER_FOREVER -- ? _("_Remember this passphrase") -- : _("_Remember this passphrase for the remainder of this session")); -- } else { -- msg->check = gtk_check_button_new_with_mnemonic(type == E_PASSWORDS_REMEMBER_FOREVER -- ? _("_Remember this password") -- : _("_Remember this password for the remainder of this session")); -- -- } -- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (msg->check), *msg->remember); -- gtk_box_pack_start (GTK_BOX (vbox), msg->check, TRUE, FALSE, 3); -- if ((msg->flags & E_PASSWORDS_DISABLE_REMEMBER)) -- gtk_widget_set_sensitive(msg->check, FALSE); -- gtk_widget_show (msg->check); -- } -- -- msg->noreply = noreply; -- -- g_signal_connect(password_dialog, "response", G_CALLBACK (pass_response), msg); -- -- if (msg->parent) -- gtk_dialog_run (GTK_DIALOG (password_dialog)); -- else -- gtk_widget_show((GtkWidget *)password_dialog); -+ g_free (password); - } - -- - /** - * e_passwords_init: - * -- * Initializes the e_passwords routines. Must be called before any other -- * e_passwords_* function. -+ * This function is here for backward-compatibility. It does nothing. - **/ - void - e_passwords_init (void) - { -- LOCK(); -- -- if (!passwords) { -- /* create the per-session hash table */ -- passwords = g_hash_table_new (g_str_hash, g_str_equal); --#ifdef ENABLE_THREADS -- main_thread = pthread_self(); --#endif -- } -- -- UNLOCK(); - } - - /** -- * e_passwords_cancel: -- * -- * Cancel any outstanding password operations and close any dialogues -- * currently being shown. -+ * e_passwords_shutdown: -+ * -+ * This function is here for backward-compatibility. It does nothing. - **/ - void --e_passwords_cancel(void) -+e_passwords_shutdown (void) - { -- EPassMsg *msg; -- -- LOCK(); -- while ((msg = (EPassMsg *)e_dlist_remhead(&request_list))) -- e_msgport_reply(&msg->msg); -- UNLOCK(); -- -- if (password_dialog) -- gtk_dialog_response(password_dialog,GTK_RESPONSE_CANCEL); - } - - /** -- * e_passwords_shutdown: -- * -- * Cleanup routine to call before exiting. -+ * e_passwords_cancel: -+ * -+ * This function is here for backward-compatibility. It does nothing. - **/ - void --e_passwords_shutdown (void) -+e_passwords_cancel(void) - { --#ifdef WITH_GNOME_KEYRING -- /* shouldn't need this really - everything is synchronous */ -- if (!gnome_keyring_is_available()) -- gnome_config_private_sync_file ("/Evolution"); --#else -- gnome_config_private_sync_file ("/Evolution"); --#endif -- e_passwords_cancel(); -- -- if (passwords) { -- /* and destroy our per session hash */ -- g_hash_table_foreach_remove (passwords, free_entry, NULL); -- g_hash_table_destroy (passwords); -- passwords = NULL; -- } - } - - /** - * e_passwords_set_online: -- * @state: -+ * @online: - * - * Set the offline-state of the application. This is a work-around - * for having the backends fully offline aware, and returns a -@@ -920,10 +1007,11 @@ - * FIXME: This is not a permanent api, review post 2.0. - **/ - void --e_passwords_set_online(int state) -+e_passwords_set_online (gboolean online) - { -- ep_online_state = state; -- /* TODO: we could check that a request is open and close it, or maybe who cares */ -+ /* TODO We could check that a request is open and close it, -+ * or maybe who cares */ -+ ep_online_state = online; - } - - /** -@@ -934,151 +1022,138 @@ - void - e_passwords_forget_passwords (void) - { --#ifdef WITH_GNOME_KEYRING -- EPassMsg *msg = ep_msg_new(gnome_keyring_is_available() ? ep_forget_passwords_keyring : ep_forget_passwords_file); --#else -- EPassMsg *msg = ep_msg_new(ep_forget_passwords_file); --#endif -- -- ep_msg_send(msg); -- ep_msg_free(msg); -+ EPassMsg *msg; -+ -+ msg = ep_msg_new (ep_forget_passwords, FALSE); -+ -+ ep_msg_send (msg); -+ ep_msg_free (msg); - } - - /** - * e_passwords_clear_passwords: -+ * @component: component name of the caller - * - * Forgets all disk cached passwords for the component. - **/ - void --e_passwords_clear_passwords (const char *component_name) -+e_passwords_clear_passwords (const gchar *component) - { --#ifdef WITH_GNOME_KEYRING -- EPassMsg *msg = ep_msg_new(gnome_keyring_is_available() ? ep_clear_passwords_keyring : ep_clear_passwords_file); --#else -- EPassMsg *msg = ep_msg_new(ep_clear_passwords_file); --#endif -+ EPassMsg *msg; - -- msg->component = component_name; -- ep_msg_send(msg); -- ep_msg_free(msg); -+ g_return_if_fail (component != NULL); -+ -+ msg = ep_msg_new (ep_clear_passwords, FALSE); -+ msg->component = component; -+ -+ ep_msg_send (msg); -+ ep_msg_free (msg); - } - - /** - * e_passwords_remember_password: -+ * @component: component name of the caller - * @key: the key - * - * Saves the password associated with @key to disk. - **/ - void --e_passwords_remember_password (const char *component_name, const char *key) -+e_passwords_remember_password (const gchar *component, const gchar *key) - { - EPassMsg *msg; - -- g_return_if_fail(component_name != NULL); -- g_return_if_fail(key != NULL); -+ g_return_if_fail (component != NULL); -+ g_return_if_fail (key != NULL); - --#ifdef WITH_GNOME_KEYRING -- msg = ep_msg_new(gnome_keyring_is_available() ? ep_remember_password_keyring : ep_remember_password_file); --#else -- msg = ep_msg_new(ep_remember_password_file); --#endif -- msg->component = component_name; -+ msg = ep_msg_new (ep_remember_password, FALSE); -+ msg->component = component; - msg->key = key; - -- ep_msg_send(msg); -- ep_msg_free(msg); -+ ep_msg_send (msg); -+ ep_msg_free (msg); - } - - /** - * e_passwords_forget_password: -+ * @component: component name of the caller - * @key: the key - * - * Forgets the password associated with @key, in memory and on disk. - **/ - void --e_passwords_forget_password (const char *component_name, const char *key) -+e_passwords_forget_password (const gchar *component, const gchar *key) - { - EPassMsg *msg; - -- g_return_if_fail(component_name != NULL); -- g_return_if_fail(key != NULL); -+ g_return_if_fail (component != NULL); -+ g_return_if_fail (key != NULL); - --#ifdef WITH_GNOME_KEYRING -- msg = ep_msg_new(gnome_keyring_is_available() ? ep_forget_password_keyring : ep_forget_password_file); --#else -- msg = ep_msg_new(ep_forget_password_file); --#endif -- msg->component = component_name; -+ msg = ep_msg_new (ep_forget_password, TRUE); -+ msg->component = component; - msg->key = key; - -- ep_msg_send(msg); -- ep_msg_free(msg); -+ ep_msg_send (msg); -+ ep_msg_free (msg); - } - - /** - * e_passwords_get_password: -+ * @component: component name of the caller - * @key: the key - * - * Return value: the password associated with @key, or %NULL. Caller - * must free the returned password. - **/ - char * --e_passwords_get_password (const char *component_name, const char *key) -+e_passwords_get_password (const gchar *component, const gchar *key) - { - EPassMsg *msg; -- char *passwd; -- -- g_return_val_if_fail(component_name != NULL, NULL); -- g_return_val_if_fail(key != NULL, NULL); -+ char *password; - --#ifdef WITH_GNOME_KEYRING -- msg = ep_msg_new(gnome_keyring_is_available() ? ep_get_password_keyring : ep_get_password_file); --#else -- msg = ep_msg_new(ep_get_password_file); --#endif -+ g_return_val_if_fail (component != NULL, NULL); -+ g_return_val_if_fail (key != NULL, NULL); - -- msg->component = component_name; -+ msg = ep_msg_new (ep_get_password, TRUE); -+ msg->component = component; - msg->key = key; - -- ep_msg_send(msg); -+ ep_msg_send (msg); -+ password = msg->password; -+ ep_msg_free (msg); - -- passwd = msg->password; -- msg->password = NULL; -- ep_msg_free(msg); -- -- return passwd; -+ return password; - } - - /** - * e_passwords_add_password: - * @key: a key -- * @passwd: the password for @key -+ * @password: the password for @key - * - * This stores the @key/@passwd pair in the current session's password - * hash. - **/ - void --e_passwords_add_password (const char *key, const char *passwd) -+e_passwords_add_password (const gchar *key, const gchar *password) - { - EPassMsg *msg; - -- g_return_if_fail(key != NULL); -- g_return_if_fail(passwd != NULL); -+ g_return_if_fail (key != NULL); -+ g_return_if_fail (password != NULL); - -- msg = ep_msg_new(ep_add_password); -+ msg = ep_msg_new (ep_add_password, FALSE); - msg->key = key; -- msg->oldpass = passwd; -+ msg->password = g_strdup (password); - -- ep_msg_send(msg); -- ep_msg_free(msg); -+ ep_msg_send (msg); -+ g_free (msg->password); -+ ep_msg_free (msg); - } - - /** - * e_passwords_ask_password: - * @title: title for the password dialog -- * @component_name: the name of the component for which we're storing -- * the password (e.g. Mail, Addressbook, etc.) -- * @key: key to store the password under -+ * @component: component name of the caller -+ * @key: key to store the password under, or NULL - * @prompt: prompt string - * @type: whether or not to offer to remember the password, - * and for how long. -@@ -1093,245 +1168,44 @@ - * return value is non-%NULL and @remember_type is not - * E_PASSWORDS_DO_NOT_REMEMBER. - **/ --char * --e_passwords_ask_password (const char *title, const char *component_name, -- const char *key, -- const char *prompt, -+gchar * -+e_passwords_ask_password (const gchar *title, -+ const gchar *component, -+ const gchar *key, -+ const gchar *prompt, - EPasswordsRememberType type, -- gboolean *remember, -+ gboolean *p_remember, - GtkWindow *parent) - { -- char *passwd; - EPassMsg *msg; -- -- g_return_val_if_fail (component_name != NULL, NULL); -- g_return_val_if_fail (key != NULL, NULL); -+ EPassDialogData *data; -+ gboolean remember; -+ gchar *password; -+ -+ g_return_val_if_fail (title != NULL, NULL); -+ g_return_val_if_fail (component != NULL, NULL); -+ g_return_val_if_fail (prompt != NULL, NULL); - - if ((type & E_PASSWORDS_ONLINE) && !ep_online_state) - return NULL; - -- msg = ep_msg_new (ep_ask_password); -- msg->title = title; -- msg->component = component_name; -- msg->key = key; -- msg->prompt = prompt; -- msg->flags = type; -- msg->remember = remember; -- msg->parent = parent; -- -- ep_msg_send(msg); -- passwd = msg->password; -- msg->password = NULL; -- ep_msg_free(msg); -- -- return passwd; --} -- -- -- --static char *base64_alphabet = --"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -- --static unsigned char camel_mime_base64_rank[256] = { -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, -- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, -- 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, -- 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, -- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, --}; -- --/* call this when finished encoding everything, to -- flush off the last little bit */ --static int --base64_encode_close(unsigned char *in, int inlen, gboolean break_lines, unsigned char *out, int *state, int *save) --{ -- int c1, c2; -- unsigned char *outptr = out; -- -- if (inlen>0) -- outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save); -- -- c1 = ((unsigned char *)save)[1]; -- c2 = ((unsigned char *)save)[2]; -- -- switch (((char *)save)[0]) { -- case 2: -- outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ]; -- g_assert(outptr[2] != 0); -- goto skip; -- case 1: -- outptr[2] = '='; -- skip: -- outptr[0] = base64_alphabet[ c1 >> 2 ]; -- outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )]; -- outptr[3] = '='; -- outptr += 4; -- break; -- } -- if (break_lines) -- *outptr++ = '\n'; -- -- *save = 0; -- *state = 0; -- -- return outptr-out; --} -- --/* -- performs an 'encode step', only encodes blocks of 3 characters to the -- output at a time, saves left-over state in state and save (initialise to -- 0 on first invocation). --*/ --static int --base64_encode_step(unsigned char *in, int len, gboolean break_lines, unsigned char *out, int *state, int *save) --{ -- register unsigned char *inptr, *outptr; -- -- if (len<=0) -- return 0; -- -- inptr = in; -- outptr = out; -- -- if (len + ((char *)save)[0] > 2) { -- unsigned char *inend = in+len-2; -- register int c1, c2, c3; -- register int already; -- -- already = *state; -- -- switch (((char *)save)[0]) { -- case 1: c1 = ((unsigned char *)save)[1]; goto skip1; -- case 2: c1 = ((unsigned char *)save)[1]; -- c2 = ((unsigned char *)save)[2]; goto skip2; -- } -- -- /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */ -- while (inptr < inend) { -- c1 = *inptr++; -- skip1: -- c2 = *inptr++; -- skip2: -- c3 = *inptr++; -- *outptr++ = base64_alphabet[ c1 >> 2 ]; -- *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ]; -- *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ]; -- *outptr++ = base64_alphabet[ c3 & 0x3f ]; -- /* this is a bit ugly ... */ -- if (break_lines && (++already)>=19) { -- *outptr++='\n'; -- already = 0; -- } -- } -- -- ((char *)save)[0] = 0; -- len = 2-(inptr-inend); -- *state = already; -- } -- -- if (len>0) { -- register char *saveout; -- -- /* points to the slot for the next char to save */ -- saveout = & (((char *)save)[1]) + ((char *)save)[0]; -- -- /* len can only be 0 1 or 2 */ -- switch(len) { -- case 2: *saveout++ = *inptr++; -- case 1: *saveout++ = *inptr++; -- } -- ((char *)save)[0]+=len; -- } -+ remember = (p_remember != NULL) ? *p_remember : FALSE; -+ data = ep_dialog_data_new (parent, title, prompt, remember, type); - -- return outptr-out; --} -+ msg = ep_msg_new (ep_ask_password, TRUE); -+ msg->component = component; -+ msg->key = (key != NULL) ? key : ""; -+ msg->data = data; - -+ ep_msg_send (msg); -+ password = msg->password; -+ remember = data->remember; -+ ep_msg_free (msg); - --/** -- * base64_decode_step: decode a chunk of base64 encoded data -- * @in: input stream -- * @len: max length of data to decode -- * @out: output stream -- * @state: holds the number of bits that are stored in @save -- * @save: leftover bits that have not yet been decoded -- * -- * Decodes a chunk of base64 encoded data -- **/ --static int --base64_decode_step(unsigned char *in, int len, unsigned char *out, int *state, unsigned int *save) --{ -- register unsigned char *inptr, *outptr; -- unsigned char *inend, c; -- register unsigned int v; -- int i; -- -- inend = in+len; -- outptr = out; -- -- /* convert 4 base64 bytes to 3 normal bytes */ -- v=*save; -- i=*state; -- inptr = in; -- while (inptr>16; -- *outptr++ = v>>8; -- *outptr++ = v; -- i=0; -- } -- } -- } -+ ep_dialog_data_free (data); - -- *save = v; -- *state = i; -+ if (p_remember != NULL) -+ *p_remember = remember; - -- /* quick scan back for '=' on the end somewhere */ -- /* fortunately we can drop 1 output char for each trailing = (upto 2) */ -- i=2; -- while (inptr>in && i) { -- inptr--; -- if (camel_mime_base64_rank[*inptr] != 0xff) { -- if (*inptr == '=') -- outptr--; -- i--; -- } -- } -- -- /* if i!= 0 then there is a truncation error! */ -- return outptr-out; --} -- --static char * --decode_base64 (char *base64) --{ -- guchar *plain; -- char *pad = "=="; -- int len, out, state; -- unsigned int save; -- -- len = strlen (base64); -- plain = g_malloc0 (len); -- state = save = 0; -- out = base64_decode_step ((guchar *)base64, len, plain, &state, &save); -- if (len % 4) { -- base64_decode_step ((guchar *)pad, 4 - len % 4, plain + out, -- &state, &save); -- } -- -- return (char *)plain; -+ return password; - } ---- evolution-data-server-1.11.92/libedataserverui/e-passwords.h.e-passwords 2007-09-02 14:54:22.000000000 -0400 -+++ evolution-data-server-1.11.92/libedataserverui/e-passwords.h 2007-09-03 23:13:10.000000000 -0400 -@@ -28,45 +28,65 @@ - - G_BEGIN_DECLS - --/* -- initialization is now implicit when you call any of the functions -- below, although this is only correct if the functions are called -- from the main thread. -- -- e_passwords_shutdown should be called at exit time to synch the -- password on-disk storage, and to free up in-memory storage. */ --void e_passwords_init (void); -- --void e_passwords_shutdown (void); --void e_passwords_cancel(void); --void e_passwords_set_online(int state); --void e_passwords_remember_password (const char *component, const char *key); --void e_passwords_add_password (const char *key, const char *passwd); --char *e_passwords_get_password (const char *component, const char *key); --void e_passwords_forget_password (const char *component, const char *key); --void e_passwords_forget_passwords (void); --void e_passwords_clear_passwords (const char *component); -- -+/** -+ * EPasswordsRememberType: -+ * @E_PASSWORDS_REMEMBER_NEVER: -+ * Do not remember the password. -+ * @E_PASSWORDS_REMEMBER_SESSION: -+ * Remember the password for this session only. -+ * @E_PASSWORDS_REMEMBER_FOREVER: -+ * Remember the password across multiple sessions. -+ * @E_PASSWORDS_REMEMBER_MASK: -+ * Mask value separates the mutually-exclusive values -+ * of the enumeration from the flags. -+ * @E_PASSWORDS_SECRET: -+ * Do not show the password as it's being typed. -+ * @E_PASSWORDS_REPROMPT: -+ * Populate the dialog with the current password (if one exists). -+ * @E_PASSWORDS_ONLINE: -+ * Only prompt the user for a password if we're online. -+ * @E_PASSWORDS_DISABLE_REMEMBER: -+ * Disable the "remember password" option on the dialog. -+ * @E_PASSWORDS_PASSPHRASE: -+ * Ask for a passphrase instead of a password. -+ **/ - typedef enum { - E_PASSWORDS_REMEMBER_NEVER, - E_PASSWORDS_REMEMBER_SESSION, - E_PASSWORDS_REMEMBER_FOREVER, - E_PASSWORDS_REMEMBER_MASK = 0xf, - -- /* option bits */ -- E_PASSWORDS_SECRET = 1<<8, -- E_PASSWORDS_REPROMPT = 1<<9, -- E_PASSWORDS_ONLINE = 1<<10, /* only ask if we're online */ -- E_PASSWORDS_DISABLE_REMEMBER = 1<<11, /* disable the 'remember password' checkbox */ -- E_PASSWORDS_PASSPHRASE = 1<<12, /* We are asking a passphrase */ -+ /* flags */ -+ E_PASSWORDS_SECRET = 1 << 8, -+ E_PASSWORDS_REPROMPT = 1 << 9, -+ E_PASSWORDS_ONLINE = 1 << 10, -+ E_PASSWORDS_DISABLE_REMEMBER = 1 << 11, -+ E_PASSWORDS_PASSPHRASE = 1 << 12, - } EPasswordsRememberType; - --char * e_passwords_ask_password (const char *title, -- const char*component_name, const char *key, -- const char *prompt, -- EPasswordsRememberType remember_type, -- gboolean *remember, -- GtkWindow *parent); -+#ifndef EDS_DISABLE_DEPRECATED -+void e_passwords_init (void); -+void e_passwords_shutdown (void); -+void e_passwords_cancel (void); -+#endif -+void e_passwords_set_online (gboolean online); -+void e_passwords_remember_password (const gchar *component, -+ const gchar *key); -+void e_passwords_add_password (const gchar *key, -+ const gchar *password); -+gchar * e_passwords_get_password (const gchar *component, -+ const gchar *key); -+void e_passwords_forget_password (const gchar *component, -+ const gchar *key); -+void e_passwords_forget_passwords (void); -+void e_passwords_clear_passwords (const gchar *component); -+gchar * e_passwords_ask_password (const gchar *title, -+ const gchar *component, -+ const gchar *key, -+ const gchar *prompt, -+ EPasswordsRememberType remember_type, -+ gboolean *remember, -+ GtkWindow *parent); - - G_END_DECLS - ---- evolution-data-server-1.11.92/libedataserver/e-msgport.h.e-passwords 2007-07-05 02:01:55.000000000 -0400 -+++ evolution-data-server-1.11.92/libedataserver/e-msgport.h 2007-09-03 23:13:10.000000000 -0400 -@@ -54,7 +54,7 @@ - - /* header for any message */ - typedef struct _EMsg { -- EDListNode ln; -+ EDListNode ln; /* deprecated */ - EMsgPort *reply_port; - gint flags; - } EMsg; diff --git a/evolution-data-server-2.21.2-base64.patch b/evolution-data-server-2.21.2-base64.patch deleted file mode 100644 index 9d793d5..0000000 --- a/evolution-data-server-2.21.2-base64.patch +++ /dev/null @@ -1,147 +0,0 @@ -diff -up evolution-data-server-2.21.2/libedataserverui/e-passwords.c.base64 evolution-data-server-2.21.2/libedataserverui/e-passwords.c ---- evolution-data-server-2.21.2/libedataserverui/e-passwords.c.base64 2007-11-08 19:59:38.000000000 -0500 -+++ evolution-data-server-2.21.2/libedataserverui/e-passwords.c 2007-11-15 13:39:13.000000000 -0500 -@@ -214,7 +214,7 @@ ep_password_decode (const gchar *encoded - * to the decoded data to make it a nul-terminated string. */ - - gchar *password; -- gsize length; -+ gsize length = 0; - - password = (gchar *) g_base64_decode (encoded_password, &length); - password = g_realloc (password, length + 1); -diff -up evolution-data-server-2.21.2/servers/exchange/lib/e2k-autoconfig.c.base64 evolution-data-server-2.21.2/servers/exchange/lib/e2k-autoconfig.c ---- evolution-data-server-2.21.2/servers/exchange/lib/e2k-autoconfig.c.base64 2007-10-23 05:57:29.000000000 -0400 -+++ evolution-data-server-2.21.2/servers/exchange/lib/e2k-autoconfig.c 2007-11-15 13:39:13.000000000 -0500 -@@ -306,8 +306,6 @@ get_ctx_auth_handler (SoupMessage *msg, - E2kAutoconfig *ac = user_data; - const GSList *headers; - const char *challenge_hdr; -- guchar *challenge; -- gsize length; - - ac->saw_ntlm = ac->saw_basic = FALSE; - headers = soup_message_get_header_list (msg->response_headers, -@@ -322,6 +320,9 @@ get_ctx_auth_handler (SoupMessage *msg, - - if (!strncmp (challenge_hdr, "NTLM ", 5) && - (!ac->w2k_domain || !ac->nt_domain)) { -+ guchar *challenge; -+ gsize length = 0; -+ - challenge = g_base64_decode (challenge_hdr + 5, &length); - if (!ac->nt_domain) - ac->nt_domain_defaulted = TRUE; -diff -up evolution-data-server-2.21.2/servers/exchange/lib/e2k-result.c.base64 evolution-data-server-2.21.2/servers/exchange/lib/e2k-result.c ---- evolution-data-server-2.21.2/servers/exchange/lib/e2k-result.c.base64 2007-10-23 05:57:29.000000000 -0400 -+++ evolution-data-server-2.21.2/servers/exchange/lib/e2k-result.c 2007-11-15 13:39:13.000000000 -0500 -@@ -45,7 +45,7 @@ prop_get_binary_array (E2kResult *result - byte_array = g_byte_array_new (); - if (node->xmlChildrenNode && node->xmlChildrenNode->content) { - guchar *data; -- gsize length; -+ gsize length = 0; - - data = g_base64_decode ( - node->xmlChildrenNode->content, &length); -@@ -84,7 +84,7 @@ prop_get_binary (E2kResult *result, cons - byte_array = g_byte_array_new (); - if (node->xmlChildrenNode && node->xmlChildrenNode->content) { - guchar *data; -- gsize length; -+ gsize length = 0; - - data = g_base64_decode ( - node->xmlChildrenNode->content, &length); -diff -up evolution-data-server-2.21.2/addressbook/tests/ebook/test-photo.c.base64 evolution-data-server-2.21.2/addressbook/tests/ebook/test-photo.c ---- evolution-data-server-2.21.2/addressbook/tests/ebook/test-photo.c.base64 2007-10-23 05:57:35.000000000 -0400 -+++ evolution-data-server-2.21.2/addressbook/tests/ebook/test-photo.c 2007-11-15 13:39:13.000000000 -0500 -@@ -28,15 +28,19 @@ main (int argc, char **argv) - { - EContact *contact; - EContactPhoto *photo, *new_photo; -+ guchar *data; -+ gsize length = 0; - - g_type_init (); - - contact = e_contact_new (); -+ data = g_base64_decode (photo_data, &length); - - photo = g_new (EContactPhoto, 1); - photo->type = E_CONTACT_PHOTO_TYPE_INLINED; - photo->data.inlined.mime_type = NULL; -- photo->data.inlined.data = g_base64_decode (photo_data, &photo->data.inlined.length); -+ photo->data.inlined.data = data; -+ photo->data.inlined.length = length; - - /* set the photo */ - e_contact_set (contact, E_CONTACT_PHOTO, photo); -diff -up evolution-data-server-2.21.2/addressbook/libebook/e-vcard.c.base64 evolution-data-server-2.21.2/addressbook/libebook/e-vcard.c ---- evolution-data-server-2.21.2/addressbook/libebook/e-vcard.c.base64 2007-10-23 05:57:37.000000000 -0400 -+++ evolution-data-server-2.21.2/addressbook/libebook/e-vcard.c 2007-11-15 13:39:13.000000000 -0500 -@@ -1780,7 +1780,7 @@ e_vcard_attribute_get_values_decoded (EV - case EVC_ENCODING_BASE64: - for (l = attr->values; l; l = l->next) { - guchar *decoded; -- gsize len; -+ gsize len = 0; - - decoded = g_base64_decode (l->data, &len); - attr->decoded_values = g_list_prepend (attr->decoded_values, g_string_new_len (decoded, len)); -diff -up evolution-data-server-2.21.2/camel/camel-sasl.c.base64 evolution-data-server-2.21.2/camel/camel-sasl.c ---- evolution-data-server-2.21.2/camel/camel-sasl.c.base64 2007-10-23 05:57:18.000000000 -0400 -+++ evolution-data-server-2.21.2/camel/camel-sasl.c 2007-11-15 13:39:30.000000000 -0500 -@@ -134,9 +134,9 @@ camel_sasl_challenge_base64 (CamelSasl * - - g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL); - -- if (token) { -+ if (token && *token) { - guchar *data; -- gsize length; -+ gsize length = 0; - - data = g_base64_decode (token, &length); - token_binary = g_byte_array_new (); -diff -up evolution-data-server-2.21.2/camel/camel-mime-utils.c.base64 evolution-data-server-2.21.2/camel/camel-mime-utils.c ---- evolution-data-server-2.21.2/camel/camel-mime-utils.c.base64 2007-11-09 13:13:12.000000000 -0500 -+++ evolution-data-server-2.21.2/camel/camel-mime-utils.c 2007-11-15 13:39:13.000000000 -0500 -@@ -192,7 +192,7 @@ size_t - camel_base64_decode_simple (char *data, size_t len) - { - guchar *out_data; -- gsize out_len; -+ gsize out_len = 0; - - g_return_val_if_fail (data != NULL, 0); - g_return_val_if_fail (strlen (data) > 1, 0); -diff -up evolution-data-server-2.21.2/camel/providers/groupwise/camel-groupwise-folder.c.base64 evolution-data-server-2.21.2/camel/providers/groupwise/camel-groupwise-folder.c ---- evolution-data-server-2.21.2/camel/providers/groupwise/camel-groupwise-folder.c.base64 2007-10-23 05:56:59.000000000 -0400 -+++ evolution-data-server-2.21.2/camel/providers/groupwise/camel-groupwise-folder.c 2007-11-15 13:39:13.000000000 -0500 -@@ -1684,7 +1684,7 @@ groupwise_folder_item_to_msg( CamelFolde - EGwItemAttachment *attach = (EGwItemAttachment *)al->data; - if (!g_ascii_strcasecmp (attach->name, "Mime.822")) { - if (attach->size > MAX_ATTACHMENT_SIZE) { -- int len_iter = 0, t_len , offset = 0, t_offset = 0; -+ int t_len , offset = 0, t_offset = 0; - char *t_attach = NULL; - GString *gstr = g_string_new (NULL); - -@@ -1694,6 +1694,7 @@ groupwise_folder_item_to_msg( CamelFolde - attach->id, t_offset, MAX_ATTACHMENT_SIZE, - (const char **)&t_attach, &t_len, &offset); - if (status == E_GW_CONNECTION_STATUS_OK) { -+ gsize len_iter = 0; - char *temp = NULL; - - temp = g_base64_decode(t_attach, &len_iter); -@@ -1831,7 +1832,7 @@ groupwise_folder_item_to_msg( CamelFolde - attach->id, t_offset, MAX_ATTACHMENT_SIZE, - (const char **)&t_attach, &t_len, &offset); - if (status == E_GW_CONNECTION_STATUS_OK) { -- int len_iter = 0; -+ gsize len_iter = 0; - char *temp = NULL; - - temp = g_base64_decode(t_attach, &len_iter); diff --git a/evolution-data-server-2.21.5-password-dialog.patch b/evolution-data-server-2.21.5-password-dialog.patch new file mode 100644 index 0000000..0d603cd --- /dev/null +++ b/evolution-data-server-2.21.5-password-dialog.patch @@ -0,0 +1,19 @@ +diff -up evolution-data-server-2.21.5/libedataserverui/e-passwords.c.password-dialog evolution-data-server-2.21.5/libedataserverui/e-passwords.c +--- evolution-data-server-2.21.5/libedataserverui/e-passwords.c.password-dialog 2008-01-16 12:23:51.000000000 -0500 ++++ evolution-data-server-2.21.5/libedataserverui/e-passwords.c 2008-01-16 12:24:18.000000000 -0500 +@@ -873,12 +873,12 @@ ep_ask_password (EPassMsg *msg) + + widget = gtk_dialog_new_with_buttons ( + msg->title, msg->parent, 0, +- GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, +- GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, ++ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, ++ GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_has_separator (GTK_DIALOG (widget), FALSE); + gtk_dialog_set_default_response ( +- GTK_DIALOG (widget), GTK_RESPONSE_ACCEPT); ++ GTK_DIALOG (widget), GTK_RESPONSE_OK); + gtk_window_set_resizable (GTK_WINDOW (widget), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (widget), 12); + password_dialog = GTK_DIALOG (widget); diff --git a/evolution-data-server.spec b/evolution-data-server.spec index 4e630c0..341923d 100644 --- a/evolution-data-server.spec +++ b/evolution-data-server.spec @@ -27,7 +27,7 @@ Name: evolution-data-server Version: 2.21.5 -Release: 1%{?dist} +Release: 2%{?dist} License: LGPL Group: System Environment/Libraries Summary: Backend data server for Evolution @@ -47,23 +47,16 @@ Patch11: evolution-data-server-1.8.0-no-gnome-common.patch Patch12: evolution-data-server-1.7.91-maybe-fix-crash.patch # RH bug #215702 / GNOME bug #487988 -Patch14: evolution-data-server-1.8.0-fix-ldap-query.patch - -# GNOME bug #363695 -Patch15: evolution-data-server-1.9.1-kill-ememory.patch - -# GNOME bug #376991 -# XXX Disabled due to outstanding issues. -#Patch16: evolution-data-server-1.9.92-e-passwords.patch +Patch13: evolution-data-server-1.8.0-fix-ldap-query.patch # GNOME bug #373146 -Patch18: evolution-data-server-1.10.1-camel-folder-summary-crash.patch +Patch14: evolution-data-server-1.10.1-camel-folder-summary-crash.patch # RH bug #243296 -Patch19: evolution-data-server-1.11.5-fix-64bit-acinclude.patch +Patch15: evolution-data-server-1.11.5-fix-64bit-acinclude.patch -# RH bug #384741 / GNOME bug #474000 -Patch20: evolution-data-server-2.21.2-base64.patch +# GNOME bug #509644 +Patch16: evolution-data-server-2.21.5-password-dialog.patch ### Build Dependencies ### @@ -140,12 +133,10 @@ This package contains developer documentation for %{name}. %patch10 -p1 -b .ldaphack %patch11 -p1 -b .no-gnome-common %patch12 -p1 -b .maybe-fix-crash -%patch14 -p1 -b .fix-ldap-query -#%patch15 -p1 -b .kill-ememory -##%patch16 -p1 -b .e-passwords -#%patch18 -p1 -b .camel-folder-summary-crash -#%patch19 -p1 -b .fix-64bit-acinclude -#%patch20 -p1 -b .base64 +%patch13 -p1 -b .fix-ldap-query +%patch14 -p1 -b .camel-folder-summary-crash +%patch15 -p1 -b .fix-64bit-acinclude +%patch16 -p1 -b .password-dialog mkdir -p krb5-fakeprefix/include mkdir -p krb5-fakeprefix/lib @@ -388,6 +379,12 @@ rm -rf $RPM_BUILD_ROOT %{_datadir}/gtk-doc/html/libedataserverui %changelog +* Wed Jan 16 2008 Matthew Barnes - 2.21.5-2.fc9 +- Add patch for GNOME bug #509644 (password dialog breakage). +- Remove patch for RH bug #384741 (fixed upstream). +- Remove patch for GNOME bug #363695 (obsolete). +- Remove patch for GNOME bug #376991 (obsolete). + * Mon Jan 14 2008 Matthew Barnes - 2.21.5-1.fc9 - Update to 2.21.5