1 /*
2  * PROJECT:    ReactOS NetSh
3  * LICENSE:    GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:    Network Shell context management functions
5  * COPYRIGHT:  Copyright 2023 Eric Kohl <eric.kohl@reactos.org>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include "precomp.h"
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS ********************************************************************/
16 
17 PCONTEXT_ENTRY pRootContext = NULL;
18 PCONTEXT_ENTRY pCurrentContext = NULL;
19 
20 /* FUNCTIONS ******************************************************************/
21 
22 PCONTEXT_ENTRY
23 AddContext(
24     PCONTEXT_ENTRY pParentContext,
25     PWSTR pszName,
26     GUID *pGuid)
27 {
28     PCONTEXT_ENTRY pEntry;
29 
30     if (pParentContext != NULL && pszName == NULL)
31         return NULL;
32 
33     /* Allocate the entry */
34     pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CONTEXT_ENTRY));
35     if (pEntry == NULL)
36         return NULL;
37 
38     /* Allocate the name buffer */
39     if (pszName != NULL)
40     {
41         pEntry->pszContextName = HeapAlloc(GetProcessHeap(),
42                                            HEAP_ZERO_MEMORY,
43                                            (wcslen(pszName) + 1) * sizeof(WCHAR));
44         if (pEntry->pszContextName == NULL)
45         {
46             HeapFree(GetProcessHeap(), 0, pEntry);
47             return NULL;
48         }
49 
50         /* Fill the entry */
51         wcscpy(pEntry->pszContextName, pszName);
52     }
53 
54     pEntry->pParentContext = pParentContext;
55     if (pGuid != NULL)
56         CopyMemory(&pEntry->Guid, pGuid, sizeof(pEntry->Guid));
57 
58     /* Insert it */
59     if (pParentContext != NULL)
60     {
61         if (pParentContext->pSubContextHead == NULL && pParentContext->pSubContextTail == NULL)
62         {
63             pParentContext->pSubContextHead = pEntry;
64             pParentContext->pSubContextTail = pEntry;
65         }
66         else
67         {
68             pEntry->pPrev = pParentContext->pSubContextTail;
69             pParentContext->pSubContextTail->pNext = pEntry;
70             pParentContext->pSubContextTail = pEntry;
71         }
72     }
73 
74     return pEntry;
75 }
76 
77 
78 PCOMMAND_ENTRY
79 AddContextCommand(
80     PCONTEXT_ENTRY pContext,
81     LPCWSTR pwszCmdToken,
82     PFN_HANDLE_CMD pfnCmdHandler,
83     DWORD dwShortCmdHelpToken,
84     DWORD dwCmdHlpToken,
85     DWORD dwFlags)
86 {
87     PCOMMAND_ENTRY pEntry;
88 
89     if (pfnCmdHandler == NULL)
90         return NULL;
91 
92     /* Allocate the entry */
93     pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(COMMAND_ENTRY));
94     if (pEntry == NULL)
95         return NULL;
96 
97     pEntry->pwszCmdToken = HeapAlloc(GetProcessHeap(),
98                                      HEAP_ZERO_MEMORY,
99                                      (wcslen(pwszCmdToken) + 1) * sizeof(WCHAR));
100     if (pEntry->pwszCmdToken == NULL)
101     {
102         HeapFree(GetProcessHeap(), 0, pEntry);
103         return NULL;
104     }
105 
106     wcscpy((LPWSTR)pEntry->pwszCmdToken, pwszCmdToken);
107 
108     pEntry->pfnCmdHandler = pfnCmdHandler;
109     pEntry->dwShortCmdHelpToken = dwShortCmdHelpToken;
110     pEntry->dwCmdHlpToken = dwCmdHlpToken;
111     pEntry->dwFlags = dwFlags;
112 
113     if (pContext->pCommandListHead == NULL && pContext->pCommandListTail == NULL)
114     {
115         pContext->pCommandListHead = pEntry;
116         pContext->pCommandListTail = pEntry;
117     }
118     else
119     {
120         pEntry->pPrev = pContext->pCommandListTail;
121         pContext->pCommandListTail->pNext = pEntry;
122         pContext->pCommandListTail = pEntry;
123     }
124 
125     return pEntry;
126 }
127 
128 
129 PCOMMAND_GROUP
130 AddCommandGroup(
131     PCONTEXT_ENTRY pContext,
132     LPCWSTR pwszCmdGroupToken,
133     DWORD dwShortCmdHelpToken,
134     DWORD dwFlags)
135 {
136     PCOMMAND_GROUP pEntry;
137 
138     /* Allocate the entry */
139     pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(COMMAND_GROUP));
140     if (pEntry == NULL)
141         return NULL;
142 
143     pEntry->pwszCmdGroupToken = HeapAlloc(GetProcessHeap(),
144                                           HEAP_ZERO_MEMORY,
145                                           (wcslen(pwszCmdGroupToken) + 1) * sizeof(WCHAR));
146     if (pEntry->pwszCmdGroupToken == NULL)
147     {
148         HeapFree(GetProcessHeap(), 0, pEntry);
149         return NULL;
150     }
151 
152     wcscpy((LPWSTR)pEntry->pwszCmdGroupToken, pwszCmdGroupToken);
153     pEntry->dwShortCmdHelpToken = dwShortCmdHelpToken;
154     pEntry->dwFlags = dwFlags;
155 
156     if (pContext->pGroupListHead == NULL && pContext->pGroupListTail == NULL)
157     {
158         pContext->pGroupListHead = pEntry;
159         pContext->pGroupListTail = pEntry;
160     }
161     else
162     {
163         pEntry->pPrev = pContext->pGroupListTail;
164         pContext->pGroupListTail->pNext = pEntry;
165         pContext->pGroupListTail = pEntry;
166     }
167 
168     return pEntry;
169 }
170 
171 
172 PCOMMAND_ENTRY
173 AddGroupCommand(
174     PCOMMAND_GROUP pGroup,
175     LPCWSTR pwszCmdToken,
176     PFN_HANDLE_CMD pfnCmdHandler,
177     DWORD dwShortCmdHelpToken,
178     DWORD dwCmdHlpToken,
179     DWORD dwFlags)
180 {
181     PCOMMAND_ENTRY pEntry;
182 
183     if (pfnCmdHandler == NULL)
184         return NULL;
185 
186     /* Allocate the entry */
187     pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(COMMAND_ENTRY));
188     if (pEntry == NULL)
189         return NULL;
190 
191     pEntry->pwszCmdToken = HeapAlloc(GetProcessHeap(),
192                                      HEAP_ZERO_MEMORY,
193                                      (wcslen(pwszCmdToken) + 1) * sizeof(WCHAR));
194     if (pEntry->pwszCmdToken == NULL)
195     {
196         HeapFree(GetProcessHeap(), 0, pEntry);
197         return NULL;
198     }
199 
200     wcscpy((LPWSTR)pEntry->pwszCmdToken, pwszCmdToken);
201 
202     pEntry->pfnCmdHandler = pfnCmdHandler;
203     pEntry->dwShortCmdHelpToken = dwShortCmdHelpToken;
204     pEntry->dwCmdHlpToken = dwCmdHlpToken;
205     pEntry->dwFlags = dwFlags;
206 
207     if (pGroup->pCommandListHead == NULL && pGroup->pCommandListTail == NULL)
208     {
209         pGroup->pCommandListHead = pEntry;
210         pGroup->pCommandListTail = pEntry;
211     }
212     else
213     {
214         pEntry->pPrev = pGroup->pCommandListTail;
215         pGroup->pCommandListTail->pNext = pEntry;
216         pGroup->pCommandListTail = pEntry;
217     }
218 
219     return pEntry;
220 }
221 
222 
223 VOID
224 DeleteContext(
225     PWSTR pszName)
226 {
227     /* Delete all commands */
228     /* Delete the context */
229 }
230 
231 
232 DWORD
233 WINAPI
234 UpCommand(
235     LPCWSTR pwszMachine,
236     LPWSTR *argv,
237     DWORD dwCurrentIndex,
238     DWORD dwArgCount,
239     DWORD dwFlags,
240     LPCVOID pvData,
241     BOOL *pbDone)
242 {
243     if (pCurrentContext != pRootContext)
244         pCurrentContext = pCurrentContext->pParentContext;
245 
246     return 0;
247 }
248 
249 
250 DWORD
251 WINAPI
252 ExitCommand(
253     LPCWSTR pwszMachine,
254     LPWSTR *argv,
255     DWORD dwCurrentIndex,
256     DWORD dwArgCount,
257     DWORD dwFlags,
258     LPCVOID pvData,
259     BOOL *pbDone)
260 {
261     *pbDone = TRUE;
262     return 0;
263 }
264 
265 
266 DWORD
267 WINAPI
268 RemCommand(
269     LPCWSTR pwszMachine,
270     LPWSTR *argv,
271     DWORD dwCurrentIndex,
272     DWORD dwArgCount,
273     DWORD dwFlags,
274     LPCVOID pvData,
275     BOOL *pbDone)
276 {
277     return 0;
278 }
279 
280 
281 BOOL
282 CreateRootContext(VOID)
283 {
284     PCOMMAND_GROUP pGroup;
285 
286     pRootContext = AddContext(NULL, NULL, NULL);
287     DPRINT1("pRootContext: %p\n", pRootContext);
288     if (pRootContext == NULL)
289         return FALSE;
290 
291     pRootContext->hModule = GetModuleHandle(NULL);
292 
293     AddContextCommand(pRootContext, L"..",   UpCommand, IDS_HLP_UP, IDS_HLP_UP_EX, 0);
294     AddContextCommand(pRootContext, L"?",    HelpCommand, IDS_HLP_HELP, IDS_HLP_HELP_EX, 0);
295     AddContextCommand(pRootContext, L"bye",  ExitCommand, IDS_HLP_EXIT, IDS_HLP_EXIT_EX, 0);
296     AddContextCommand(pRootContext, L"exit", ExitCommand, IDS_HLP_EXIT, IDS_HLP_EXIT_EX, 0);
297     AddContextCommand(pRootContext, L"help", HelpCommand, IDS_HLP_HELP, IDS_HLP_HELP_EX, 0);
298     AddContextCommand(pRootContext, L"quit", ExitCommand, IDS_HLP_EXIT, IDS_HLP_EXIT_EX, 0);
299 
300     pGroup = AddCommandGroup(pRootContext, L"add", IDS_HLP_GROUP_ADD, 0);
301     if (pGroup)
302     {
303         AddGroupCommand(pGroup, L"helper", AddHelperCommand, IDS_HLP_ADD_HELPER, IDS_HLP_ADD_HELPER_EX, 0);
304     }
305 
306     pGroup = AddCommandGroup(pRootContext, L"delete", IDS_HLP_GROUP_DELETE, 0);
307     if (pGroup)
308     {
309         AddGroupCommand(pGroup, L"helper", DeleteHelperCommand, IDS_HLP_DEL_HELPER, IDS_HLP_DEL_HELPER_EX, 0);
310     }
311 
312     pGroup = AddCommandGroup(pRootContext, L"show", IDS_HLP_GROUP_SHOW, 0);
313     if (pGroup)
314     {
315         AddGroupCommand(pGroup, L"helper", ShowHelperCommand, IDS_HLP_SHOW_HELPER, IDS_HLP_SHOW_HELPER_EX, 0);
316     }
317 
318     pCurrentContext = pRootContext;
319 
320     return TRUE;
321 }
322 
323 
324 DWORD
325 WINAPI
326 RegisterContext(
327     _In_ const NS_CONTEXT_ATTRIBUTES *pChildContext)
328 {
329     PCONTEXT_ENTRY pContext;
330     DWORD i;
331 
332     DPRINT1("RegisterContext(%p)\n", pChildContext);
333     if (pChildContext == NULL)
334     {
335         DPRINT1("Invalid child context!\n");
336         return ERROR_INVALID_PARAMETER;
337     }
338 
339     if ((pChildContext->pwszContext == NULL) ||
340         (wcslen(pChildContext->pwszContext) == 0) ||
341         (wcschr(pChildContext->pwszContext, L' ') != 0) ||
342         (wcschr(pChildContext->pwszContext, L'=') != 0))
343     {
344         DPRINT1("Invalid context name!\n");
345         return ERROR_INVALID_PARAMETER;
346     }
347 
348     DPRINT1("Name: %S\n", pChildContext->pwszContext);
349 
350     pContext = AddContext(pRootContext, pChildContext->pwszContext, (GUID*)&pChildContext->guidHelper);
351     if (pContext != NULL)
352     {
353         for (i = 0; i < pChildContext->ulNumTopCmds; i++)
354         {
355             AddContextCommand(pContext,
356                 pChildContext->pTopCmds[i].pwszCmdToken,
357                 pChildContext->pTopCmds[i].pfnCmdHandler,
358                 pChildContext->pTopCmds[i].dwShortCmdHelpToken,
359                 pChildContext->pTopCmds[i].dwCmdHlpToken,
360                 pChildContext->pTopCmds[i].dwFlags);
361         }
362 
363         /* Add command groups */
364         for (i = 0; i < pChildContext->ulNumGroups; i++)
365         {
366             AddCommandGroup(pContext,
367                 pChildContext->pCmdGroups[i].pwszCmdGroupToken,
368                 pChildContext->pCmdGroups[i].dwShortCmdHelpToken,
369                 pChildContext->pCmdGroups[i].dwFlags);
370         }
371     }
372 
373     return ERROR_SUCCESS;
374 }
375