- define additional shell paths for CSIDL_... constants
[wine] / dlls / netapi32 / nbnamecache.c
1 /* Copyright (c) 2003 Juan Lang
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  *
17  * This implementation uses a linked list, because I don't have a decent
18  * hash table implementation handy.  This is somewhat inefficient, but it's
19  * rather more efficient than not having a name cache at all.
20  */
21
22 #include "wine/debug.h"
23 #include "nbnamecache.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
26
27 typedef struct _NBNameCacheNode
28 {
29     DWORD expireTime;
30     NBNameCacheEntry *entry;
31     struct _NBNameCacheNode *next;
32 } NBNameCacheNode;
33
34 struct NBNameCache
35 {
36     HANDLE heap;
37     CRITICAL_SECTION cs;
38     DWORD entryExpireTimeMS;
39     NBNameCacheNode *head;
40 };
41
42 /* Unlinks the node pointed to by *prev, and frees any associated memory.
43  * If that node's next pointed to another node, *prev now points to it.
44  * Assumes the caller owns cache's lock.
45  */
46 static void NBNameCacheUnlinkNode(struct NBNameCache *cache,
47  NBNameCacheNode **prev)
48 {
49     if (cache && prev && *prev)
50     {
51         NBNameCacheNode *next = (*prev)->next;
52
53         if ((*prev)->entry)
54             HeapFree(cache->heap, 0, (*prev)->entry);
55         HeapFree(cache->heap, 0, *prev);
56         *prev = next;
57     }
58 }
59
60 /* Walks the list beginning with cache->head looking for the node with name
61  * name.  If the node is found, returns a pointer to the next pointer of the
62  * node _prior_ to the found node (or head if head points to it).  Thus, if the
63  * node's all you want, dereference the return value twice.  If you want to
64  * modify the list, modify the referent of the return value.
65  * While it's at it, deletes nodes whose time has expired (except the node
66  * you're looking for, of course).
67  * Returns NULL if the node isn't found.
68  * Assumes the caller owns cache's lock.
69  */
70 static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache,
71  const char name[NCBNAMSZ])
72 {
73     NBNameCacheNode **ret = NULL;
74
75     if (cache && cache->head)
76     {
77         NBNameCacheNode **ptr;
78
79         ptr = &cache->head;
80         while (ptr && *ptr && (*ptr)->entry)
81         {
82             if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1))
83                 ret = ptr;
84             else
85             {
86                 if (GetTickCount() > (*ptr)->expireTime)
87                     NBNameCacheUnlinkNode(cache, ptr);
88             }
89             if (*ptr)
90                 ptr = &(*ptr)->next;
91         }
92     }
93     return ret;
94 }
95
96 struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS)
97 {
98     struct NBNameCache *cache;
99     
100     
101     if (!heap)
102         heap = GetProcessHeap();
103     cache = (struct NBNameCache *)HeapAlloc(heap, 0,
104      sizeof(struct NBNameCache));
105     if (cache)
106     {
107         cache->heap = heap;
108         InitializeCriticalSection(&cache->cs);
109         cache->entryExpireTimeMS = entryExpireTimeMS;
110         cache->head = NULL;
111     }
112     return cache;
113 }
114
115 BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry)
116 {
117     BOOL ret;
118
119     if (cache && entry)
120     {
121         NBNameCacheNode **node;
122
123         EnterCriticalSection(&cache->cs);
124         node = NBNameCacheWalk(cache, entry->name);
125         if (node)
126         {
127             (*node)->expireTime = GetTickCount() +
128              cache->entryExpireTimeMS;
129             HeapFree(cache->heap, 0, (*node)->entry);
130             (*node)->entry = entry;
131             ret = TRUE;
132         }
133         else
134         {
135             NBNameCacheNode *newNode = (NBNameCacheNode *)HeapAlloc(
136              cache->heap, 0, sizeof(NBNameCacheNode));
137             if (newNode)
138             {
139                 newNode->expireTime = GetTickCount() +
140                  cache->entryExpireTimeMS;
141                 newNode->entry = entry;
142                 newNode->next = cache->head;
143                 cache->head = newNode;
144                 ret = TRUE;
145             }
146             else
147                 ret = FALSE;
148         }
149         LeaveCriticalSection(&cache->cs);
150     }
151     else
152         ret = FALSE;
153     return ret;
154 }
155
156 const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
157  const UCHAR name[NCBNAMSZ])
158 {
159     const NBNameCacheEntry *ret;
160     UCHAR printName[NCBNAMSZ];
161
162     memcpy(printName, name, NCBNAMSZ - 1);
163     printName[NCBNAMSZ - 1] = '\0';
164     if (cache)
165     {
166         NBNameCacheNode **node;
167
168         EnterCriticalSection(&cache->cs);
169         node = NBNameCacheWalk(cache, name);
170         if (node)
171             ret = (*node)->entry;
172         else
173             ret = NULL;
174         LeaveCriticalSection(&cache->cs);
175     }
176     else
177         ret = NULL;
178     return ret;
179 }
180
181 BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
182  const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ])
183 {
184     BOOL ret;
185
186     if (cache)
187     {
188         NBNameCacheNode **node;
189
190         EnterCriticalSection(&cache->cs);
191         node = NBNameCacheWalk(cache, name);
192         if (node && *node && (*node)->entry)
193         {
194             memcpy((*node)->entry->nbname, nbname, NCBNAMSZ);
195             ret = TRUE;
196         }
197         else
198             ret = FALSE;
199         LeaveCriticalSection(&cache->cs);
200     }
201     else
202         ret = FALSE;
203     return ret;
204 }
205
206 void NBNameCacheDestroy(struct NBNameCache *cache)
207 {
208     if (cache)
209     {
210         DeleteCriticalSection(&cache->cs);
211         while (cache->head)
212             NBNameCacheUnlinkNode(cache, &cache->head);
213         HeapFree(cache->heap, 0, cache);
214     }
215 }