crypt32: Allow updating data messages with no content.
[wine] / dlls / crypt32 / context.c
1 /*
2  * Copyright 2006 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 #include <assert.h>
19 #include <stdarg.h>
20 #include "windef.h"
21 #include "winbase.h"
22 #include "wincrypt.h"
23 #include "wine/debug.h"
24 #include "wine/list.h"
25 #include "crypt32_private.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(context);
28
29 typedef enum _ContextType {
30     ContextTypeData,
31     ContextTypeLink,
32 } ContextType;
33
34 typedef struct _BASE_CONTEXT
35 {
36     LONG        ref;
37     ContextType type;
38 } BASE_CONTEXT, *PBASE_CONTEXT;
39
40 typedef struct _DATA_CONTEXT
41 {
42     LONG                   ref;
43     ContextType            type; /* always ContextTypeData */
44     PCONTEXT_PROPERTY_LIST properties;
45 } DATA_CONTEXT, *PDATA_CONTEXT;
46
47 typedef struct _LINK_CONTEXT
48 {
49     LONG          ref;
50     ContextType   type; /* always ContextTypeLink */
51     PBASE_CONTEXT linked;
52 } LINK_CONTEXT, *PLINK_CONTEXT;
53
54 #define CONTEXT_FROM_BASE_CONTEXT(p, s) ((LPBYTE)(p) - (s))
55 #define BASE_CONTEXT_FROM_CONTEXT(p, s) (PBASE_CONTEXT)((LPBYTE)(p) + (s))
56
57 void *Context_CreateDataContext(size_t contextSize)
58 {
59     void *ret = CryptMemAlloc(contextSize + sizeof(DATA_CONTEXT));
60
61     if (ret)
62     {
63         PDATA_CONTEXT context = (PDATA_CONTEXT)((LPBYTE)ret + contextSize);
64
65         context->ref = 1;
66         context->type = ContextTypeData;
67         context->properties = ContextPropertyList_Create();
68         if (!context->properties)
69         {
70             CryptMemFree(ret);
71             ret = NULL;
72         }
73     }
74     TRACE("returning %p\n", ret);
75     return ret;
76 }
77
78 void *Context_CreateLinkContext(unsigned int contextSize, void *linked, unsigned int extra,
79  BOOL addRef)
80 {
81     void *context = CryptMemAlloc(contextSize + sizeof(LINK_CONTEXT) + extra);
82
83     TRACE("(%d, %p, %d)\n", contextSize, linked, extra);
84
85     if (context)
86     {
87         PLINK_CONTEXT linkContext = (PLINK_CONTEXT)BASE_CONTEXT_FROM_CONTEXT(
88          context, contextSize);
89         PBASE_CONTEXT linkedBase = BASE_CONTEXT_FROM_CONTEXT(linked,
90          contextSize);
91
92         memcpy(context, linked, contextSize);
93         linkContext->ref = 1;
94         linkContext->type = ContextTypeLink;
95         linkContext->linked = linkedBase;
96         if (addRef)
97             Context_AddRef(linked, contextSize);
98         TRACE("%p's ref count is %d\n", context, linkContext->ref);
99     }
100     TRACE("returning %p\n", context);
101     return context;
102 }
103
104 void Context_AddRef(void *context, size_t contextSize)
105 {
106     PBASE_CONTEXT baseContext = BASE_CONTEXT_FROM_CONTEXT(context, contextSize);
107
108     InterlockedIncrement(&baseContext->ref);
109     TRACE("%p's ref count is %d\n", context, baseContext->ref);
110     if (baseContext->type == ContextTypeLink)
111     {
112         void *linkedContext = Context_GetLinkedContext(context, contextSize);
113         PBASE_CONTEXT linkedBase = BASE_CONTEXT_FROM_CONTEXT(linkedContext,
114          contextSize);
115
116         /* Add-ref the linked contexts too */
117         while (linkedContext && linkedBase->type == ContextTypeLink)
118         {
119             InterlockedIncrement(&linkedBase->ref);
120             TRACE("%p's ref count is %d\n", linkedContext, linkedBase->ref);
121             linkedContext = Context_GetLinkedContext(linkedContext,
122              contextSize);
123             if (linkedContext)
124                 linkedBase = BASE_CONTEXT_FROM_CONTEXT(linkedContext,
125                  contextSize);
126             else
127                 linkedBase = NULL;
128         }
129         if (linkedContext)
130         {
131             /* It's not a link context, so it wasn't add-ref'ed in the while
132              * loop, so add-ref it here.
133              */
134             linkedBase = BASE_CONTEXT_FROM_CONTEXT(linkedContext,
135              contextSize);
136             InterlockedIncrement(&linkedBase->ref);
137             TRACE("%p's ref count is %d\n", linkedContext, linkedBase->ref);
138         }
139     }
140 }
141
142 void *Context_GetExtra(const void *context, size_t contextSize)
143 {
144     PBASE_CONTEXT baseContext = BASE_CONTEXT_FROM_CONTEXT(context, contextSize);
145
146     assert(baseContext->type == ContextTypeLink);
147     return (LPBYTE)baseContext + sizeof(LINK_CONTEXT);
148 }
149
150 void *Context_GetLinkedContext(void *context, size_t contextSize)
151 {
152     PBASE_CONTEXT baseContext = BASE_CONTEXT_FROM_CONTEXT(context, contextSize);
153
154     assert(baseContext->type == ContextTypeLink);
155     return CONTEXT_FROM_BASE_CONTEXT(((PLINK_CONTEXT)baseContext)->linked,
156      contextSize);
157 }
158
159 PCONTEXT_PROPERTY_LIST Context_GetProperties(const void *context, size_t contextSize)
160 {
161     PBASE_CONTEXT ptr = BASE_CONTEXT_FROM_CONTEXT(context, contextSize);
162
163     while (ptr && ptr->type == ContextTypeLink)
164         ptr = ((PLINK_CONTEXT)ptr)->linked;
165     return (ptr && ptr->type == ContextTypeData) ?
166      ((PDATA_CONTEXT)ptr)->properties : NULL;
167 }
168
169 BOOL Context_Release(void *context, size_t contextSize,
170  ContextFreeFunc dataContextFree)
171 {
172     PBASE_CONTEXT base = BASE_CONTEXT_FROM_CONTEXT(context, contextSize);
173     BOOL ret = TRUE;
174
175     if (base->ref <= 0)
176     {
177         ERR("%p's ref count is %d\n", context, base->ref);
178         return FALSE;
179     }
180     if (base->type == ContextTypeLink)
181     {
182         /* The linked context is of the same type as this, so release
183          * it as well, using the same offset and data free function.
184          */
185         ret = Context_Release(CONTEXT_FROM_BASE_CONTEXT(
186          ((PLINK_CONTEXT)base)->linked, contextSize), contextSize,
187          dataContextFree);
188     }
189     if (InterlockedDecrement(&base->ref) == 0)
190     {
191         TRACE("freeing %p\n", context);
192         if (base->type == ContextTypeData)
193         {
194             ContextPropertyList_Free(((PDATA_CONTEXT)base)->properties);
195             dataContextFree(context);
196         }
197         CryptMemFree(context);
198     }
199     else
200         TRACE("%p's ref count is %d\n", context, base->ref);
201     return ret;
202 }
203
204 void Context_CopyProperties(const void *to, const void *from,
205  size_t contextSize)
206 {
207     PCONTEXT_PROPERTY_LIST toProperties, fromProperties;
208
209     toProperties = Context_GetProperties(to, contextSize);
210     fromProperties = Context_GetProperties(from, contextSize);
211     assert(toProperties && fromProperties);
212     ContextPropertyList_Copy(toProperties, fromProperties);
213 }
214
215 struct ContextList
216 {
217     PCWINE_CONTEXT_INTERFACE contextInterface;
218     size_t contextSize;
219     CRITICAL_SECTION cs;
220     struct list contexts;
221 };
222
223 struct ContextList *ContextList_Create(
224  PCWINE_CONTEXT_INTERFACE contextInterface, size_t contextSize)
225 {
226     struct ContextList *list = CryptMemAlloc(sizeof(struct ContextList));
227
228     if (list)
229     {
230         list->contextInterface = contextInterface;
231         list->contextSize = contextSize;
232         InitializeCriticalSection(&list->cs);
233         list->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ContextList.cs");
234         list_init(&list->contexts);
235     }
236     return list;
237 }
238
239 static inline struct list *ContextList_ContextToEntry(const struct ContextList *list,
240  const void *context)
241 {
242     struct list *ret;
243
244     if (context)
245         ret = Context_GetExtra(context, list->contextSize);
246     else
247         ret = NULL;
248     return ret;
249 }
250
251 static inline void *ContextList_EntryToContext(const struct ContextList *list,
252  struct list *entry)
253 {
254     return (LPBYTE)entry - sizeof(LINK_CONTEXT) - list->contextSize;
255 }
256
257 void *ContextList_Add(struct ContextList *list, void *toLink, void *toReplace)
258 {
259     void *context;
260
261     TRACE("(%p, %p, %p)\n", list, toLink, toReplace);
262
263     context = Context_CreateLinkContext(list->contextSize, toLink,
264      sizeof(struct list), TRUE);
265     if (context)
266     {
267         struct list *entry = ContextList_ContextToEntry(list, context);
268
269         TRACE("adding %p\n", context);
270         EnterCriticalSection(&list->cs);
271         if (toReplace)
272         {
273             struct list *existing = ContextList_ContextToEntry(list, toReplace);
274
275             entry->prev = existing->prev;
276             entry->next = existing->next;
277             entry->prev->next = entry;
278             entry->next->prev = entry;
279             existing->prev = existing->next = existing;
280             list->contextInterface->free(toReplace);
281         }
282         else
283             list_add_head(&list->contexts, entry);
284         LeaveCriticalSection(&list->cs);
285     }
286     return context;
287 }
288
289 void *ContextList_Enum(struct ContextList *list, void *pPrev)
290 {
291     struct list *listNext;
292     void *ret;
293
294     EnterCriticalSection(&list->cs);
295     if (pPrev)
296     {
297         struct list *prevEntry = ContextList_ContextToEntry(list, pPrev);
298
299         listNext = list_next(&list->contexts, prevEntry);
300         list->contextInterface->free(pPrev);
301     }
302     else
303         listNext = list_next(&list->contexts, &list->contexts);
304     LeaveCriticalSection(&list->cs);
305
306     if (listNext)
307     {
308         ret = ContextList_EntryToContext(list, listNext);
309         list->contextInterface->duplicate(ret);
310     }
311     else
312         ret = NULL;
313     return ret;
314 }
315
316 BOOL ContextList_Remove(struct ContextList *list, void *context)
317 {
318     struct list *entry = ContextList_ContextToEntry(list, context);
319     BOOL inList = FALSE;
320
321     EnterCriticalSection(&list->cs);
322     if (!list_empty(entry))
323     {
324         list_remove(entry);
325         inList = TRUE;
326     }
327     LeaveCriticalSection(&list->cs);
328     if (inList)
329         list_init(entry);
330     return inList;
331 }
332
333 static void ContextList_Empty(struct ContextList *list)
334 {
335     struct list *entry, *next;
336
337     EnterCriticalSection(&list->cs);
338     LIST_FOR_EACH_SAFE(entry, next, &list->contexts)
339     {
340         const void *context = ContextList_EntryToContext(list, entry);
341
342         TRACE("removing %p\n", context);
343         list_remove(entry);
344         list->contextInterface->free(context);
345     }
346     LeaveCriticalSection(&list->cs);
347 }
348
349 void ContextList_Free(struct ContextList *list)
350 {
351     ContextList_Empty(list);
352     list->cs.DebugInfo->Spare[0] = 0;
353     DeleteCriticalSection(&list->cs);
354     CryptMemFree(list);
355 }