war on lfhs continues ... what a mess
[citadel.git] / citadel / netconfig.c
1 /*
2  * This module handles loading, saving, and parsing of room network configurations.
3  *
4  * Copyright (c) 2000-2021 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version 3.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  */
15
16 #include "sysdep.h"
17 #include <stdio.h>
18 #include <sys/types.h>
19 #include <dirent.h>
20
21 #ifdef HAVE_SYSCALL_H
22 # include <syscall.h>
23 #else 
24 # if HAVE_SYS_SYSCALL_H
25 #  include <sys/syscall.h>
26 # endif
27 #endif
28 #include <dirent.h>
29 #include <assert.h>
30
31 #include <libcitadel.h>
32
33 #include "ctdl_module.h"
34 #include "serv_extensions.h"
35 #include "config.h"
36
37 void vFreeRoomNetworkStruct(void *vOneRoomNetCfg);
38 void FreeRoomNetworkStructContent(OneRoomNetCfg *OneRNCfg);
39
40 HashList *CfgTypeHash = NULL;
41
42 /*-----------------------------------------------------------------------------*
43  *                       Per room network configs                              *
44  *-----------------------------------------------------------------------------*/
45
46
47 void RegisterRoomCfgType(const char* Name, long len, RoomNetCfg eCfg, CfgLineParser p, int uniq,  int nSegments, CfgLineSerializer s, CfgLineDeAllocator d)
48 {
49         CfgLineType *pCfg;
50
51         pCfg = (CfgLineType*) malloc(sizeof(CfgLineType));
52         pCfg->Parser = p;
53         pCfg->Serializer = s;
54         pCfg->DeAllocator = d;
55         pCfg->C = eCfg;
56         pCfg->Str.Key = Name;
57         pCfg->Str.len = len;
58         pCfg->IsSingleLine = uniq;
59         pCfg->nSegments = nSegments;
60         if (CfgTypeHash == NULL) {
61                 CfgTypeHash = NewHash(1, NULL);
62         }
63         Put(CfgTypeHash, Name, len, pCfg, NULL);
64 }
65
66
67 const CfgLineType *GetCfgTypeByStr(const char *Key, long len)
68 {
69         void *pv;
70         
71         if (GetHash(CfgTypeHash, Key, len, &pv) && (pv != NULL))
72         {
73                 return (const CfgLineType *) pv;
74         }
75         else
76         {
77                 return NULL;
78         }
79 }
80
81
82 const CfgLineType *GetCfgTypeByEnum(RoomNetCfg eCfg, HashPos *It)
83 {
84         const char *Key;
85         long len;
86         void *pv;
87         CfgLineType *pCfg;
88
89         RewindHashPos(CfgTypeHash, It, 1);
90         while (GetNextHashPos(CfgTypeHash, It, &len, &Key, &pv) && (pv != NULL))
91         {
92                 pCfg = (CfgLineType*) pv;
93                 if (pCfg->C == eCfg)
94                         return pCfg;
95         }
96         return NULL;
97 }
98
99
100 void ParseGeneric(const CfgLineType *ThisOne, StrBuf *Line, const char *LinePos, OneRoomNetCfg *OneRNCfg)
101 {
102         RoomNetCfgLine *nptr;
103         int i;
104
105         nptr = (RoomNetCfgLine *)
106                 malloc(sizeof(RoomNetCfgLine));
107         nptr->next = OneRNCfg->NetConfigs[ThisOne->C];
108         nptr->Value = malloc(sizeof(StrBuf*) * ThisOne->nSegments);
109         nptr->nValues = 0;
110         memset(nptr->Value, 0, sizeof(StrBuf*) * ThisOne->nSegments);
111         if (ThisOne->nSegments == 1)
112         {
113                 nptr->Value[0] = NewStrBufPlain(LinePos, StrLength(Line) - ( LinePos - ChrPtr(Line)) );
114                 nptr->nValues = 1;
115         }
116         else for (i = 0; i < ThisOne->nSegments; i++)
117         {
118                 nptr->nValues++;
119                 nptr->Value[i] = NewStrBufPlain(NULL, StrLength(Line) - ( LinePos - ChrPtr(Line)) );
120                 StrBufExtract_NextToken(nptr->Value[i], Line, &LinePos, '|');
121         }
122
123         OneRNCfg->NetConfigs[ThisOne->C] = nptr;
124 }
125
126
127 void SerializeGeneric(const CfgLineType *ThisOne, StrBuf *OutputBuffer, OneRoomNetCfg *OneRNCfg, RoomNetCfgLine *data)
128 {
129         int i;
130
131         StrBufAppendBufPlain(OutputBuffer, CKEY(ThisOne->Str), 0);
132         StrBufAppendBufPlain(OutputBuffer, HKEY("|"), 0);
133         for (i = 0; i < ThisOne->nSegments; i++)
134         {
135                 StrBufAppendBuf(OutputBuffer, data->Value[i], 0);
136                 if (i + 1 < ThisOne->nSegments)
137                         StrBufAppendBufPlain(OutputBuffer, HKEY("|"), 0);
138         }
139         StrBufAppendBufPlain(OutputBuffer, HKEY("\n"), 0);
140 }
141
142
143 void DeleteGenericCfgLine(const CfgLineType *ThisOne, RoomNetCfgLine **data)
144 {
145         int i;
146
147         if (*data == NULL)
148                 return;
149
150         for (i = 0; i < (*data)->nValues; i++)
151         {
152                 FreeStrBuf(&(*data)->Value[i]);
153         }
154         free ((*data)->Value);
155         free(*data);
156         *data = NULL;
157 }
158
159
160 RoomNetCfgLine *DuplicateOneGenericCfgLine(const RoomNetCfgLine *data)
161 {
162         int i;
163         RoomNetCfgLine *NewData;
164
165         NewData = (RoomNetCfgLine*)malloc(sizeof(RoomNetCfgLine));
166         memset(NewData, 0, sizeof(RoomNetCfgLine));
167         NewData->Value = (StrBuf **)malloc(sizeof(StrBuf*) * data->nValues);
168         memset(NewData->Value, 0, sizeof(StrBuf*) * data->nValues);
169
170         for (i = 0; i < data->nValues; i++)
171         {
172                 NewData->Value[i] = NewStrBufDup(data->Value[i]);
173         }
174         NewData->nValues = data->nValues;
175         return NewData;
176 }
177
178
179 /*
180  * Create a config key for a room's netconfig entry
181  */
182 void netcfg_keyname(char *keybuf, long roomnum)
183 {
184         if (!keybuf) return;
185         sprintf(keybuf, "c_netconfig_%010ld", roomnum);
186 }
187
188
189 /*
190  * Given a room number and a textual netconfig, convert to base64 and write to the configdb
191  */
192 void write_netconfig_to_configdb(long roomnum, const char *raw_netconfig)
193 {
194         char keyname[25];
195         char *enc;
196         int enc_len;
197         int len;
198
199         len = strlen(raw_netconfig);
200         netcfg_keyname(keyname, roomnum);
201         enc = malloc(len * 2);
202
203         if (enc) {
204                 enc_len = CtdlEncodeBase64(enc, raw_netconfig, len, 0);
205                 if ((enc_len > 1) && (enc[enc_len-2] == 13)) enc[enc_len-2] = 0;
206                 if ((enc_len > 0) && (enc[enc_len-1] == 10)) enc[enc_len-1] = 0;
207                 enc[enc_len] = 0;
208                 syslog(LOG_DEBUG, "netconfig: writing key '%s' (length=%d)", keyname, enc_len);
209                 CtdlSetConfigStr(keyname, enc);
210                 free(enc);
211         }
212 }
213
214
215 /*
216  * Given a room number, attempt to load the netconfig configdb entry for that room.
217  * If it returns NULL, there is no netconfig.
218  * Otherwise the caller owns the returned memory and is responsible for freeing it.
219  */
220 char *LoadRoomNetConfigFile(long roomnum)
221 {
222         char keyname[25];
223         char *encoded_netconfig = NULL;
224         char *decoded_netconfig = NULL;
225
226         netcfg_keyname(keyname, roomnum);
227         encoded_netconfig = CtdlGetConfigStr(keyname);
228         if (!encoded_netconfig) return NULL;
229
230         decoded_netconfig = malloc(strlen(encoded_netconfig));  // yeah, way bigger than it needs to be, but safe
231         CtdlDecodeBase64(decoded_netconfig, encoded_netconfig, strlen(encoded_netconfig));
232         return decoded_netconfig;
233 }
234
235
236 /*
237  * Deserialize a netconfig , allocate and return structured data
238  */
239 OneRoomNetCfg *ParseRoomNetConfigFile(char *serialized_data)
240 {
241         const char *Pos = NULL;
242         const CfgLineType *pCfg = NULL;
243         StrBuf *Line = NULL;
244         StrBuf *InStr = NULL;
245         StrBuf *Cfg = NULL;
246         OneRoomNetCfg *OneRNCfg = NULL;
247         int num_lines = 0;
248         int i = 0;
249
250         OneRNCfg = malloc(sizeof(OneRoomNetCfg));
251         memset(OneRNCfg, 0, sizeof(OneRoomNetCfg));
252
253         Line = NewStrBuf();
254         InStr = NewStrBuf();
255         Cfg = NewStrBufPlain(serialized_data, -1);
256         num_lines = num_tokens(ChrPtr(Cfg), '\n');
257
258         for (i=0; i<num_lines; ++i) {
259                 StrBufExtract_token(Line, Cfg, i, '\n');
260                 if (StrLength(Line) > 0) {
261                         Pos = NULL;
262                         StrBufExtract_NextToken(InStr, Line, &Pos, '|');
263         
264                         pCfg = GetCfgTypeByStr(SKEY(InStr));
265                         if (pCfg != NULL)
266                         {
267                                 pCfg->Parser(pCfg, Line, Pos, OneRNCfg);
268                         }
269                         else
270                         {
271                                 if (OneRNCfg->misc == NULL)
272                                 {
273                                         OneRNCfg->misc = NewStrBufDup(Line);
274                                 }
275                                 else
276                                 {
277                                         if (StrLength(OneRNCfg->misc) > 0) {
278                                                 StrBufAppendBufPlain(OneRNCfg->misc, HKEY("\n"), 0);
279                                         }
280                                         StrBufAppendBuf(OneRNCfg->misc, Line, 0);
281                                 }
282                         }
283                 }
284         }
285         FreeStrBuf(&InStr);
286         FreeStrBuf(&Line);
287         FreeStrBuf(&Cfg);
288         return OneRNCfg;
289 }
290
291
292 void SaveRoomNetConfigFile(OneRoomNetCfg *OneRNCfg, long roomnum)
293 {
294         RoomNetCfg eCfg;
295         StrBuf *Cfg = NULL;
296         StrBuf *OutBuffer = NULL;
297         HashPos *CfgIt;
298
299         Cfg = NewStrBuf();
300         OutBuffer = NewStrBuf();
301         CfgIt = GetNewHashPos(CfgTypeHash, 1);
302         for (eCfg = subpending; eCfg < maxRoomNetCfg; eCfg ++)
303         {
304                 const CfgLineType *pCfg;
305                 pCfg = GetCfgTypeByEnum(eCfg, CfgIt);
306                 if (pCfg)
307                 {
308                         if (pCfg->IsSingleLine)
309                         {
310                                 pCfg->Serializer(pCfg, OutBuffer, OneRNCfg, NULL);
311                         }
312                         else
313                         {
314                                 RoomNetCfgLine *pName = OneRNCfg->NetConfigs[pCfg->C];
315                                 while (pName != NULL)
316                                 {
317                                         pCfg->Serializer(pCfg, OutBuffer, OneRNCfg, pName);
318                                         pName = pName->next;
319                                 }
320                         }
321                 }
322
323         }
324         DeleteHashPos(&CfgIt);
325
326         if (OneRNCfg->misc != NULL) {
327                 StrBufAppendBuf(OutBuffer, OneRNCfg->misc, 0);
328         }
329
330         write_netconfig_to_configdb(roomnum, ChrPtr(OutBuffer));
331
332         FreeStrBuf(&OutBuffer);
333         FreeStrBuf(&Cfg);
334 }
335
336
337 void AddRoomCfgLine(OneRoomNetCfg *OneRNCfg, struct ctdlroom *qrbuf, RoomNetCfg LineType, RoomNetCfgLine *Line)
338 {
339         RoomNetCfgLine **pLine;
340
341         if (OneRNCfg == NULL)
342         {
343                 OneRNCfg = (OneRoomNetCfg*) malloc(sizeof(OneRoomNetCfg));
344                 memset(OneRNCfg, 0, sizeof(OneRoomNetCfg));
345         }
346         pLine = &OneRNCfg->NetConfigs[LineType];
347
348         while(*pLine != NULL) pLine = &((*pLine)->next);
349         *pLine = Line;
350 }
351
352
353 void FreeRoomNetworkStructContent(OneRoomNetCfg *OneRNCfg)
354 {
355         RoomNetCfg eCfg;
356         HashPos *CfgIt;
357
358         CfgIt = GetNewHashPos(CfgTypeHash, 1);
359         for (eCfg = subpending; eCfg < maxRoomNetCfg; eCfg ++)
360         {
361                 const CfgLineType *pCfg;
362                 RoomNetCfgLine *pNext, *pName;
363                 
364                 pCfg = GetCfgTypeByEnum(eCfg, CfgIt);
365                 pName= OneRNCfg->NetConfigs[eCfg];
366                 while (pName != NULL)
367                 {
368                         pNext = pName->next;
369                         if (pCfg != NULL)
370                         {
371                                 pCfg->DeAllocator(pCfg, &pName);
372                         }
373                         else
374                         {
375                                 DeleteGenericCfgLine(NULL, &pName);
376                         }
377                         pName = pNext;
378                 }
379         }
380         DeleteHashPos(&CfgIt);
381
382         FreeStrBuf(&OneRNCfg->Sender);
383         FreeStrBuf(&OneRNCfg->RoomInfo);
384         FreeStrBuf(&OneRNCfg->misc);
385         memset(OneRNCfg, 0, sizeof(OneRoomNetCfg));
386 }
387
388
389 void vFreeRoomNetworkStruct(void *vOneRoomNetCfg)
390 {
391         OneRoomNetCfg *OneRNCfg;
392         OneRNCfg = (OneRoomNetCfg*)vOneRoomNetCfg;
393         FreeRoomNetworkStructContent(OneRNCfg);
394         free(OneRNCfg);
395 }
396
397
398 void FreeRoomNetworkStruct(OneRoomNetCfg **pOneRNCfg)
399 {
400         vFreeRoomNetworkStruct(*pOneRNCfg);
401         *pOneRNCfg=NULL;
402 }
403
404
405 /*
406  * Fetch the netconfig entry for a room, parse it, and return the data.
407  * Caller owns the returned memory and MUST free it using FreeRoomNetworkStruct()
408  */
409 OneRoomNetCfg *CtdlGetNetCfgForRoom(long roomnum)
410 {
411         OneRoomNetCfg *OneRNCfg = NULL;
412         char *serialized_config = NULL;
413
414         serialized_config = LoadRoomNetConfigFile(roomnum);
415         if (!serialized_config) return NULL;
416
417         OneRNCfg = ParseRoomNetConfigFile(serialized_config);
418         free(serialized_config);
419         return OneRNCfg;
420 }
421
422
423 /*-----------------------------------------------------------------------------*
424  *              Per room network configs : exchange with client                *
425  *-----------------------------------------------------------------------------*/
426
427 void cmd_gnet(char *argbuf)
428 {
429         if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) {
430                 /* users can edit the netconfigs for their own mailbox rooms */
431         }
432         else if (CtdlAccessCheck(ac_room_aide)) return;
433         
434         cprintf("%d Network settings for room #%ld <%s>\n", LISTING_FOLLOWS, CC->room.QRnumber, CC->room.QRname);
435
436         char *c = LoadRoomNetConfigFile(CC->room.QRnumber);
437         if (c) {
438                 int len = strlen(c);
439                 client_write(c, len);                   // Can't use cprintf() here, it has a limit of 1024 bytes
440                 if (c[len] != '\n') {
441                         client_write(HKEY("\n"));
442                 }
443                 free(c);
444         }
445         cprintf("000\n");
446 }
447
448
449 void cmd_snet(char *argbuf)
450 {
451         struct CitContext *CCC = CC;
452         StrBuf *Line = NULL;
453         StrBuf *TheConfig = NULL;
454         int rc;
455
456         unbuffer_output();
457         Line = NewStrBuf();
458         TheConfig = NewStrBuf();
459         cprintf("%d send new netconfig now\n", SEND_LISTING);
460
461         while (rc = CtdlClientGetLine(Line), (rc >= 0))
462         {
463                 if ((rc == 3) && (strcmp(ChrPtr(Line), "000") == 0))
464                         break;
465
466                 StrBufAppendBuf(TheConfig, Line, 0);
467                 StrBufAppendBufPlain(TheConfig, HKEY("\n"), 0);
468         }
469         FreeStrBuf(&Line);
470
471         write_netconfig_to_configdb(CCC->room.QRnumber, ChrPtr(TheConfig));
472         FreeStrBuf(&TheConfig);
473 }
474
475
476 /*-----------------------------------------------------------------------------*
477  *                       Per node network configs                              *
478  *-----------------------------------------------------------------------------*/
479 void DeleteCtdlNodeConf(void *vNode)
480 {
481         CtdlNodeConf *Node = (CtdlNodeConf*) vNode;
482         FreeStrBuf(&Node->NodeName);
483         FreeStrBuf(&Node->Secret);
484         FreeStrBuf(&Node->Host);
485         FreeStrBuf(&Node->Port);
486         free(Node);
487 }
488
489
490 CtdlNodeConf *NewNode(StrBuf *SerializedNode)
491 {
492         const char *Pos = NULL;
493         CtdlNodeConf *Node;
494
495         /* we need at least 4 pipes and some other text so its invalid. */
496         if (StrLength(SerializedNode) < 8)
497                 return NULL;
498         Node = (CtdlNodeConf *) malloc(sizeof(CtdlNodeConf));
499
500         Node->DeleteMe = 0;
501
502         Node->NodeName=NewStrBuf();
503         StrBufExtract_NextToken(Node->NodeName, SerializedNode, &Pos, '|');
504
505         Node->Secret=NewStrBuf();
506         StrBufExtract_NextToken(Node->Secret, SerializedNode, &Pos, '|');
507
508         Node->Host=NewStrBuf();
509         StrBufExtract_NextToken(Node->Host, SerializedNode, &Pos, '|');
510
511         Node->Port=NewStrBuf();
512         StrBufExtract_NextToken(Node->Port, SerializedNode, &Pos, '|');
513         return Node;
514 }
515
516
517 /*
518  * Load or refresh the Citadel network (IGnet) configuration for this node.
519  */
520 HashList* CtdlLoadIgNetCfg(void)
521 {
522         const char *LinePos;
523         char       *Cfg;
524         StrBuf     *Buf;
525         StrBuf     *LineBuf;
526         HashList   *Hash;
527         CtdlNodeConf   *Node;
528
529         Cfg =  CtdlGetSysConfig(IGNETCFG);
530         if ((Cfg == NULL) || IsEmptyStr(Cfg)) {
531                 if (Cfg != NULL)
532                         free(Cfg);
533                 return NULL;
534         }
535
536         Hash = NewHash(1, NULL);
537         Buf = NewStrBufPlain(Cfg, -1);
538         free(Cfg);
539         LineBuf = NewStrBufPlain(NULL, StrLength(Buf));
540         LinePos = NULL;
541         do
542         {
543                 StrBufSipLine(LineBuf, Buf, &LinePos);
544                 if (StrLength(LineBuf) != 0) {
545                         Node = NewNode(LineBuf);
546                         if (Node != NULL) {
547                                 Put(Hash, SKEY(Node->NodeName), Node, DeleteCtdlNodeConf);
548                         }
549                 }
550         } while (LinePos != StrBufNOTNULL);
551         FreeStrBuf(&Buf);
552         FreeStrBuf(&LineBuf);
553         return Hash;
554 }
555
556
557 int is_recipient(OneRoomNetCfg *RNCfg, const char *Name)
558 {
559         const RoomNetCfg RecipientCfgs[] = {
560                 listrecp,
561                 digestrecp,
562                 participate,
563                 maxRoomNetCfg
564         };
565         int i;
566         RoomNetCfgLine *nptr;
567         size_t len;
568         
569         len = strlen(Name);
570         i = 0;
571         while (RecipientCfgs[i] != maxRoomNetCfg)
572         {
573                 nptr = RNCfg->NetConfigs[RecipientCfgs[i]];
574                 
575                 while (nptr != NULL)
576                 {
577                         if ((StrLength(nptr->Value[0]) == len) && 
578                             (!strcmp(Name, ChrPtr(nptr->Value[0]))))
579                         {
580                                 return 1;
581                         }
582                         nptr = nptr->next;
583                 }
584                 i++;
585         }
586         return 0;
587 }
588
589
590 int CtdlNetconfigCheckRoomaccess(char *errmsgbuf, size_t n, const char* RemoteIdentifier)
591 {
592         OneRoomNetCfg *RNCfg;
593         int found;
594
595         if (RemoteIdentifier == NULL)
596         {
597                 snprintf(errmsgbuf, n, "Need sender to permit access.");
598                 return (ERROR + USERNAME_REQUIRED);
599         }
600
601         begin_critical_section(S_NETCONFIGS);
602         RNCfg = CtdlGetNetCfgForRoom (CC->room.QRnumber);
603         if (RNCfg == NULL)
604         {
605                 end_critical_section(S_NETCONFIGS);
606                 snprintf(errmsgbuf, n,
607                          "This mailing list only accepts posts from subscribers.");
608                 return (ERROR + NO_SUCH_USER);
609         }
610         found = is_recipient (RNCfg, RemoteIdentifier);
611         FreeRoomNetworkStruct(&RNCfg);
612         end_critical_section(S_NETCONFIGS);
613
614         if (found) {
615                 return (0);
616         }
617         else {
618                 snprintf(errmsgbuf, n,
619                          "This mailing list only accepts posts from subscribers.");
620                 return (ERROR + NO_SUCH_USER);
621         }
622 }
623
624
625 /*-----------------------------------------------------------------------------*
626  *                 Network maps: evaluate other nodes                          *
627  *-----------------------------------------------------------------------------*/
628
629 void DeleteNetMap(void *vNetMap)
630 {
631         CtdlNetMap *TheNetMap = (CtdlNetMap*) vNetMap;
632         FreeStrBuf(&TheNetMap->NodeName);
633         FreeStrBuf(&TheNetMap->NextHop);
634         free(TheNetMap);
635 }
636
637
638 CtdlNetMap *NewNetMap(StrBuf *SerializedNetMap)
639 {
640         const char *Pos = NULL;
641         CtdlNetMap *NM;
642
643         /* we need at least 3 pipes and some other text so its invalid. */
644         if (StrLength(SerializedNetMap) < 6)
645                 return NULL;
646         NM = (CtdlNetMap *) malloc(sizeof(CtdlNetMap));
647
648         NM->NodeName=NewStrBuf();
649         StrBufExtract_NextToken(NM->NodeName, SerializedNetMap, &Pos, '|');
650
651         NM->lastcontact = StrBufExtractNext_long(SerializedNetMap, &Pos, '|');
652
653         NM->NextHop=NewStrBuf();
654         StrBufExtract_NextToken(NM->NextHop, SerializedNetMap, &Pos, '|');
655
656         return NM;
657 }
658
659
660 HashList* CtdlReadNetworkMap(void)
661 {
662         const char *LinePos;
663         char       *Cfg;
664         StrBuf     *Buf;
665         StrBuf     *LineBuf;
666         HashList   *Hash;
667         CtdlNetMap     *TheNetMap;
668
669         Hash = NewHash(1, NULL);
670         Cfg =  CtdlGetSysConfig(IGNETMAP);
671         if ((Cfg == NULL) || IsEmptyStr(Cfg)) {
672                 if (Cfg != NULL)
673                         free(Cfg);
674                 return Hash;
675         }
676
677         Buf = NewStrBufPlain(Cfg, -1);
678         free(Cfg);
679         LineBuf = NewStrBufPlain(NULL, StrLength(Buf));
680         LinePos = NULL;
681         while (StrBufSipLine(Buf, LineBuf, &LinePos))
682         {
683                 TheNetMap = NewNetMap(LineBuf);
684                 if (TheNetMap != NULL) { /* TODO: is the NodeName Uniq? */
685                         Put(Hash, SKEY(TheNetMap->NodeName), TheNetMap, DeleteNetMap);
686                 }
687         }
688         FreeStrBuf(&Buf);
689         FreeStrBuf(&LineBuf);
690         return Hash;
691 }
692
693
694 StrBuf *CtdlSerializeNetworkMap(HashList *Map)
695 {
696         void *vMap;
697         const char *key;
698         long len;
699         StrBuf *Ret = NewStrBuf();
700         HashPos *Pos = GetNewHashPos(Map, 0);
701
702         while (GetNextHashPos(Map, Pos, &len, &key, &vMap))
703         {
704                 CtdlNetMap *pMap = (CtdlNetMap*) vMap;
705                 StrBufAppendBuf(Ret, pMap->NodeName, 0);
706                 StrBufAppendBufPlain(Ret, HKEY("|"), 0);
707
708                 StrBufAppendPrintf(Ret, "%ld", pMap->lastcontact, 0);
709                 StrBufAppendBufPlain(Ret, HKEY("|"), 0);
710
711                 StrBufAppendBuf(Ret, pMap->NextHop, 0);
712                 StrBufAppendBufPlain(Ret, HKEY("\n"), 0);
713         }
714         DeleteHashPos(&Pos);
715         return Ret;
716 }
717
718
719 /*
720  * Convert any legacy configuration files in the "netconfigs" directory
721  */
722 void convert_legacy_netcfg_files(void)
723 {
724         DIR *dh = NULL;
725         struct dirent *dit = NULL;
726         char filename[PATH_MAX];
727         long roomnum;
728         FILE *fp;
729         long len;
730         char *v;
731
732         dh = opendir(ctdl_netcfg_dir);
733         if (!dh) return;
734
735         syslog(LOG_INFO, "netconfig: legacy netconfig files exist - converting them!");
736
737         while (dit = readdir(dh), dit != NULL)                  // yes, we use the non-reentrant version; we're not in threaded mode yet
738         {
739                 roomnum = atol(dit->d_name);
740                 if (roomnum > 0) {
741                         snprintf(filename, sizeof filename, "%s/%ld", ctdl_netcfg_dir, roomnum);
742                         fp = fopen(filename, "r");
743                         if (fp)
744                         {
745                                 fseek(fp, 0L, SEEK_END);
746                                 len = ftell(fp);
747                                 if (len > 0)
748                                 {
749                                         v = malloc(len);
750                                         if (v)
751                                         {
752                                                 rewind(fp);
753                                                 if (fread(v, len, 1, fp))
754                                                 {
755                                                         write_netconfig_to_configdb(roomnum, v);
756                                                         unlink(filename);
757                                                 }
758                                                 free(v);
759                                         }
760                                 }
761                                 else
762                                 {
763                                         unlink(filename);       // zero length netconfig, just delete it
764                                 }
765                                 fclose(fp);
766                         }
767                 }
768         }
769
770         closedir(dh);
771         rmdir(ctdl_netcfg_dir);
772 }
773
774
775 /*
776  * Module entry point
777  */
778 CTDL_MODULE_INIT(netconfig)
779 {
780         if (!threading)
781         {
782                 convert_legacy_netcfg_files();
783                 CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
784                 CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config");
785         }
786         return "netconfig";
787 }