]> Cypherpunks.ru repositories - nncp.git/blob - src/cfgdir.go
Yggdrasil overlay transport
[nncp.git] / src / cfgdir.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2022 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
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 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package nncp
19
20 import (
21         "fmt"
22         "io/ioutil"
23         "os"
24         "path/filepath"
25         "sort"
26         "strconv"
27         "strings"
28 )
29
30 func cfgDirMkdir(dst ...string) error {
31         return os.MkdirAll(filepath.Join(dst...), os.FileMode(0777))
32 }
33
34 func cfgDirSave(v interface{}, dst ...string) error {
35         var r string
36         switch v := v.(type) {
37         case *string:
38                 if v == nil {
39                         return nil
40                 }
41                 r = *v
42         case string:
43                 r = v
44         case *int:
45                 if v == nil {
46                         return nil
47                 }
48                 r = strconv.Itoa(*v)
49         case *uint:
50                 if v == nil {
51                         return nil
52                 }
53                 r = strconv.Itoa(int(*v))
54         case *uint64:
55                 if v == nil {
56                         return nil
57                 }
58                 r = strconv.FormatUint(*v, 10)
59         case int:
60                 r = strconv.Itoa(v)
61         default:
62                 panic("unsupported value type")
63         }
64         mode := os.FileMode(0666)
65         if strings.HasSuffix(dst[len(dst)-1], "prv") {
66                 mode = os.FileMode(0600)
67         }
68         return ioutil.WriteFile(filepath.Join(dst...), []byte(r+"\n"), mode)
69 }
70
71 func cfgDirTouch(dst ...string) error {
72         if fd, err := os.Create(filepath.Join(dst...)); err == nil {
73                 fd.Close()
74         } else {
75                 return err
76         }
77         return nil
78 }
79
80 func CfgToDir(dst string, cfg *CfgJSON) (err error) {
81         if err = cfgDirMkdir(dst); err != nil {
82                 return
83         }
84         if err = cfgDirSave(cfg.Spool, dst, "spool"); err != nil {
85                 return
86         }
87         if err = cfgDirSave(cfg.Log, dst, "log"); err != nil {
88                 return
89         }
90         if err = cfgDirSave(cfg.Umask, dst, "umask"); err != nil {
91                 return
92         }
93         if cfg.OmitPrgrs {
94                 if err = cfgDirTouch(dst, "noprogress"); err != nil {
95                         return
96                 }
97         }
98         if cfg.NoHdr {
99                 if err = cfgDirTouch(dst, "nohdr"); err != nil {
100                         return
101                 }
102         }
103
104         if len(cfg.MCDRxIfis) > 0 {
105                 if err = cfgDirSave(
106                         strings.Join(cfg.MCDRxIfis, "\n"),
107                         dst, "mcd-listen",
108                 ); err != nil {
109                         return
110                 }
111         }
112         if len(cfg.MCDTxIfis) > 0 {
113                 if err = cfgDirMkdir(dst, "mcd-send"); err != nil {
114                         return
115                 }
116                 for ifi, t := range cfg.MCDTxIfis {
117                         if err = cfgDirSave(t, dst, "mcd-send", ifi); err != nil {
118                                 return
119                         }
120                 }
121         }
122
123         if cfg.Notify != nil {
124                 if cfg.Notify.File != nil {
125                         if err = cfgDirMkdir(dst, "notify", "file"); err != nil {
126                                 return
127                         }
128                         if err = cfgDirSave(
129                                 cfg.Notify.File.From,
130                                 dst, "notify", "file", "from",
131                         ); err != nil {
132                                 return
133                         }
134                         if err = cfgDirSave(
135                                 cfg.Notify.File.To,
136                                 dst, "notify", "file", "to",
137                         ); err != nil {
138                                 return
139                         }
140                 }
141                 if cfg.Notify.Freq != nil {
142                         if err = cfgDirMkdir(dst, "notify", "freq"); err != nil {
143                                 return
144                         }
145                         if err = cfgDirSave(
146                                 cfg.Notify.Freq.From,
147                                 dst, "notify", "freq", "from",
148                         ); err != nil {
149                                 return
150                         }
151                         if err = cfgDirSave(
152                                 cfg.Notify.Freq.To,
153                                 dst, "notify", "freq", "to",
154                         ); err != nil {
155                                 return
156                         }
157                 }
158                 for k, v := range cfg.Notify.Exec {
159                         if err = cfgDirMkdir(dst, "notify", "exec", k); err != nil {
160                                 return
161                         }
162                         if err = cfgDirSave(v.From, dst, "notify", "exec", k, "from"); err != nil {
163                                 return
164                         }
165                         if err = cfgDirSave(v.To, dst, "notify", "exec", k, "to"); err != nil {
166                                 return
167                         }
168                 }
169         }
170
171         if cfg.Self != nil {
172                 if err = cfgDirMkdir(dst, "self"); err != nil {
173                         return
174                 }
175                 if err = cfgDirSave(cfg.Self.Id, dst, "self", "id"); err != nil {
176                         return
177                 }
178                 if err = cfgDirSave(cfg.Self.ExchPub, dst, "self", "exchpub"); err != nil {
179                         return
180                 }
181                 if err = cfgDirSave(cfg.Self.ExchPrv, dst, "self", "exchprv"); err != nil {
182                         return
183                 }
184                 if err = cfgDirSave(cfg.Self.SignPub, dst, "self", "signpub"); err != nil {
185                         return
186                 }
187                 if err = cfgDirSave(cfg.Self.SignPrv, dst, "self", "signprv"); err != nil {
188                         return
189                 }
190                 if err = cfgDirSave(cfg.Self.NoisePub, dst, "self", "noisepub"); err != nil {
191                         return
192                 }
193                 if err = cfgDirSave(cfg.Self.NoisePrv, dst, "self", "noiseprv"); err != nil {
194                         return
195                 }
196         }
197
198         for name, n := range cfg.Neigh {
199                 if err = cfgDirMkdir(dst, "neigh", name); err != nil {
200                         return
201                 }
202                 if err = cfgDirSave(n.Id, dst, "neigh", name, "id"); err != nil {
203                         return
204                 }
205                 if err = cfgDirSave(n.ExchPub, dst, "neigh", name, "exchpub"); err != nil {
206                         return
207                 }
208                 if err = cfgDirSave(n.SignPub, dst, "neigh", name, "signpub"); err != nil {
209                         return
210                 }
211                 if err = cfgDirSave(n.NoisePub, dst, "neigh", name, "noisepub"); err != nil {
212                         return
213                 }
214                 if err = cfgDirSave(n.Incoming, dst, "neigh", name, "incoming"); err != nil {
215                         return
216                 }
217
218                 if len(n.Exec) > 0 {
219                         if err = cfgDirMkdir(dst, "neigh", name, "exec"); err != nil {
220                                 return
221                         }
222                         for k, v := range n.Exec {
223                                 if err = cfgDirSave(
224                                         strings.Join(v, "\n"),
225                                         dst, "neigh", name, "exec", k,
226                                 ); err != nil {
227                                         return
228                                 }
229                         }
230                 }
231
232                 if n.Freq != nil {
233                         if err = cfgDirMkdir(dst, "neigh", name, "freq"); err != nil {
234                                 return
235                         }
236                         if err = cfgDirSave(
237                                 n.Freq.Path,
238                                 dst, "neigh", name, "freq", "path",
239                         ); err != nil {
240                                 return
241                         }
242                         if err = cfgDirSave(
243                                 n.Freq.Chunked,
244                                 dst, "neigh", name, "freq", "chunked",
245                         ); err != nil {
246                                 return
247                         }
248                         if err = cfgDirSave(
249                                 n.Freq.MinSize,
250                                 dst, "neigh", name, "freq", "minsize",
251                         ); err != nil {
252                                 return
253                         }
254                         if err = cfgDirSave(
255                                 n.Freq.MaxSize,
256                                 dst, "neigh", name, "freq", "maxsize",
257                         ); err != nil {
258                                 return
259                         }
260                 }
261
262                 if len(n.Via) > 0 {
263                         if err = cfgDirSave(
264                                 strings.Join(n.Via, "\n"),
265                                 dst, "neigh", name, "via",
266                         ); err != nil {
267                                 return
268                         }
269                 }
270
271                 if len(n.Addrs) > 0 {
272                         if err = cfgDirMkdir(dst, "neigh", name, "addrs"); err != nil {
273                                 return
274                         }
275                         for k, v := range n.Addrs {
276                                 if err = cfgDirSave(v, dst, "neigh", name, "addrs", k); err != nil {
277                                         return
278                                 }
279                         }
280                 }
281
282                 if err = cfgDirSave(n.RxRate, dst, "neigh", name, "rxrate"); err != nil {
283                         return
284                 }
285                 if err = cfgDirSave(n.TxRate, dst, "neigh", name, "txrate"); err != nil {
286                         return
287                 }
288                 if err = cfgDirSave(n.OnlineDeadline, dst, "neigh", name, "onlinedeadline"); err != nil {
289                         return
290                 }
291                 if err = cfgDirSave(n.MaxOnlineTime, dst, "neigh", name, "maxonlinetime"); err != nil {
292                         return
293                 }
294
295                 for i, call := range n.Calls {
296                         is := strconv.Itoa(i)
297                         if err = cfgDirMkdir(dst, "neigh", name, "calls", is); err != nil {
298                                 return
299                         }
300                         if err = cfgDirSave(call.Cron, dst, "neigh", name, "calls", is, "cron"); err != nil {
301                                 return
302                         }
303                         if err = cfgDirSave(call.Nice, dst, "neigh", name, "calls", is, "nice"); err != nil {
304                                 return
305                         }
306                         if err = cfgDirSave(call.Xx, dst, "neigh", name, "calls", is, "xx"); err != nil {
307                                 return
308                         }
309                         if err = cfgDirSave(call.RxRate, dst, "neigh", name, "calls", is, "rxrate"); err != nil {
310                                 return
311                         }
312                         if err = cfgDirSave(call.TxRate, dst, "neigh", name, "calls", is, "txrate"); err != nil {
313                                 return
314                         }
315                         if err = cfgDirSave(call.Addr, dst, "neigh", name, "calls", is, "addr"); err != nil {
316                                 return
317                         }
318                         if err = cfgDirSave(call.OnlineDeadline, dst, "neigh", name, "calls", is, "onlinedeadline"); err != nil {
319                                 return
320                         }
321                         if err = cfgDirSave(call.MaxOnlineTime, dst, "neigh", name, "calls", is, "maxonlinetime"); err != nil {
322                                 return
323                         }
324                         if call.WhenTxExists {
325                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "when-tx-exists"); err != nil {
326                                         return
327                                 }
328                         }
329                         if call.NoCK {
330                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "nock"); err != nil {
331                                         return
332                                 }
333                         }
334                         if call.MCDIgnore {
335                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "mcd-ignore"); err != nil {
336                                         return
337                                 }
338                         }
339                         if call.AutoToss {
340                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss"); err != nil {
341                                         return
342                                 }
343                         }
344                         if call.AutoTossDoSeen {
345                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-doseen"); err != nil {
346                                         return
347                                 }
348                         }
349                         if call.AutoTossNoFile {
350                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-nofile"); err != nil {
351                                         return
352                                 }
353                         }
354                         if call.AutoTossNoFreq {
355                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-nofreq"); err != nil {
356                                         return
357                                 }
358                         }
359                         if call.AutoTossNoExec {
360                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-noexec"); err != nil {
361                                         return
362                                 }
363                         }
364                         if call.AutoTossNoTrns {
365                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-notrns"); err != nil {
366                                         return
367                                 }
368                         }
369                         if call.AutoTossNoArea {
370                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-noarea"); err != nil {
371                                         return
372                                 }
373                         }
374                 }
375         }
376
377         for name, a := range cfg.Areas {
378                 if err = cfgDirMkdir(dst, "areas", name); err != nil {
379                         return
380                 }
381                 if err = cfgDirSave(a.Id, dst, "areas", name, "id"); err != nil {
382                         return
383                 }
384                 if err = cfgDirSave(a.Pub, dst, "areas", name, "pub"); err != nil {
385                         return
386                 }
387                 if err = cfgDirSave(a.Prv, dst, "areas", name, "prv"); err != nil {
388                         return
389                 }
390                 if err = cfgDirSave(a.Incoming, dst, "areas", name, "incoming"); err != nil {
391                         return
392                 }
393                 if a.AllowUnknown {
394                         if err = cfgDirTouch(dst, "areas", name, "allow-unknown"); err != nil {
395                                 return
396                         }
397                 }
398                 if len(a.Exec) > 0 {
399                         if err = cfgDirMkdir(dst, "areas", name, "exec"); err != nil {
400                                 return
401                         }
402                         for k, v := range a.Exec {
403                                 if err = cfgDirSave(
404                                         strings.Join(v, "\n"),
405                                         dst, "areas", name, "exec", k,
406                                 ); err != nil {
407                                         return
408                                 }
409                         }
410                 }
411                 if len(a.Subs) > 0 {
412                         if err = cfgDirSave(
413                                 strings.Join(a.Subs, "\n"),
414                                 dst, "areas", name, "subs",
415                         ); err != nil {
416                                 return
417                         }
418                 }
419         }
420
421         if len(cfg.YggdrasilAliases) > 0 {
422                 if err = cfgDirMkdir(dst, "yggdrasil-aliases"); err != nil {
423                         return
424                 }
425                 for alias, v := range cfg.YggdrasilAliases {
426                         if err = cfgDirSave(v, dst, "yggdrasil-aliases", alias); err != nil {
427                                 return
428                         }
429                 }
430         }
431
432         return
433 }
434
435 func cfgDirLoad(src ...string) (v string, exists bool, err error) {
436         b, err := ioutil.ReadFile(filepath.Join(src...))
437         if err != nil {
438                 if os.IsNotExist(err) {
439                         return "", false, nil
440                 }
441                 return "", false, err
442         }
443         return strings.TrimSuffix(string(b), "\n"), true, nil
444 }
445
446 func cfgDirLoadMust(src ...string) (v string, err error) {
447         s, exists, err := cfgDirLoad(src...)
448         if err != nil {
449                 return "", err
450         }
451         if !exists {
452                 return "", fmt.Errorf("required \"%s\" does not exist", src[len(src)-1])
453         }
454         return s, nil
455 }
456
457 func cfgDirLoadOpt(src ...string) (v *string, err error) {
458         s, exists, err := cfgDirLoad(src...)
459         if err != nil {
460                 return nil, err
461         }
462         if !exists {
463                 return nil, nil
464         }
465         return &s, nil
466 }
467
468 func cfgDirLoadIntOpt(src ...string) (i64 *int64, err error) {
469         s, err := cfgDirLoadOpt(src...)
470         if err != nil {
471                 return nil, err
472         }
473         if s == nil {
474                 return nil, nil
475         }
476         i, err := strconv.ParseInt(*s, 10, 64)
477         if err != nil {
478                 return nil, err
479         }
480         return &i, nil
481 }
482
483 func cfgDirExists(src ...string) bool {
484         if _, err := os.Stat(filepath.Join(src...)); err == nil {
485                 return true
486         }
487         return false
488 }
489
490 func cfgDirReadFromTo(src ...string) (*FromToJSON, error) {
491         fromTo := FromToJSON{}
492
493         var err error
494         fromTo.From, err = cfgDirLoadMust(append(src, "from")...)
495         if err != nil {
496                 return nil, err
497         }
498
499         fromTo.To, err = cfgDirLoadMust(append(src, "to")...)
500         if err != nil {
501                 return nil, err
502         }
503
504         return &fromTo, nil
505 }
506
507 func DirToCfg(src string) (*CfgJSON, error) {
508         cfg := CfgJSON{}
509         var err error
510
511         cfg.Spool, err = cfgDirLoadMust(src, "spool")
512         if err != nil {
513                 return nil, err
514         }
515         cfg.Log, err = cfgDirLoadMust(src, "log")
516         if err != nil {
517                 return nil, err
518         }
519
520         if cfg.Umask, err = cfgDirLoadOpt(src, "umask"); err != nil {
521                 return nil, err
522         }
523         cfg.OmitPrgrs = cfgDirExists(src, "noprogress")
524         cfg.NoHdr = cfgDirExists(src, "nohdr")
525
526         sp, err := cfgDirLoadOpt(src, "mcd-listen")
527         if err != nil {
528                 return nil, err
529         }
530         if sp != nil {
531                 cfg.MCDRxIfis = strings.Split(*sp, "\n")
532         }
533
534         fis, err := ioutil.ReadDir(filepath.Join(src, "mcd-send"))
535         if err != nil && !os.IsNotExist(err) {
536                 return nil, err
537         }
538         if len(fis) > 0 {
539                 cfg.MCDTxIfis = make(map[string]int, len(fis))
540         }
541         for _, fi := range fis {
542                 n := fi.Name()
543                 if n[0] == '.' {
544                         continue
545                 }
546                 b, err := ioutil.ReadFile(filepath.Join(src, "mcd-send", fi.Name()))
547                 if err != nil {
548                         return nil, err
549                 }
550                 i, err := strconv.Atoi(strings.TrimSuffix(string(b), "\n"))
551                 if err != nil {
552                         return nil, err
553                 }
554                 cfg.MCDTxIfis[n] = i
555         }
556
557         notify := NotifyJSON{Exec: make(map[string]*FromToJSON)}
558         if cfgDirExists(src, "notify", "file") {
559                 if notify.File, err = cfgDirReadFromTo(src, "notify", "file"); err != nil {
560                         return nil, err
561                 }
562         }
563         if cfgDirExists(src, "notify", "freq") {
564                 if notify.Freq, err = cfgDirReadFromTo(src, "notify", "freq"); err != nil {
565                         return nil, err
566                 }
567         }
568         fis, err = ioutil.ReadDir(filepath.Join(src, "notify", "exec"))
569         if err != nil && !os.IsNotExist(err) {
570                 return nil, err
571         }
572         for _, fi := range fis {
573                 n := fi.Name()
574                 if n[0] == '.' || !fi.IsDir() {
575                         continue
576                 }
577                 if notify.Exec[fi.Name()], err = cfgDirReadFromTo(src, "notify", "exec", n); err != nil {
578                         return nil, err
579                 }
580         }
581         if notify.File != nil || notify.Freq != nil || len(notify.Exec) > 0 {
582                 cfg.Notify = &notify
583         }
584
585         if _, err = ioutil.ReadDir(filepath.Join(src, "self")); err == nil {
586                 self := NodeOurJSON{}
587                 if self.Id, err = cfgDirLoadMust(src, "self", "id"); err != nil {
588                         return nil, err
589                 }
590                 if self.ExchPub, err = cfgDirLoadMust(src, "self", "exchpub"); err != nil {
591                         return nil, err
592                 }
593                 if self.ExchPrv, err = cfgDirLoadMust(src, "self", "exchprv"); err != nil {
594                         return nil, err
595                 }
596                 if self.SignPub, err = cfgDirLoadMust(src, "self", "signpub"); err != nil {
597                         return nil, err
598                 }
599                 if self.SignPrv, err = cfgDirLoadMust(src, "self", "signprv"); err != nil {
600                         return nil, err
601                 }
602                 if self.NoisePub, err = cfgDirLoadMust(src, "self", "noisepub"); err != nil {
603                         return nil, err
604                 }
605                 if self.NoisePrv, err = cfgDirLoadMust(src, "self", "noiseprv"); err != nil {
606                         return nil, err
607                 }
608                 cfg.Self = &self
609         } else if !os.IsNotExist(err) {
610                 return nil, err
611         }
612
613         cfg.Neigh = make(map[string]NodeJSON)
614         fis, err = ioutil.ReadDir(filepath.Join(src, "neigh"))
615         if err != nil && !os.IsNotExist(err) {
616                 return nil, err
617         }
618         for _, fi := range fis {
619                 n := fi.Name()
620                 if n[0] == '.' {
621                         continue
622                 }
623                 node := NodeJSON{}
624                 if node.Id, err = cfgDirLoadMust(src, "neigh", n, "id"); err != nil {
625                         return nil, err
626                 }
627                 if node.ExchPub, err = cfgDirLoadMust(src, "neigh", n, "exchpub"); err != nil {
628                         return nil, err
629                 }
630                 if node.SignPub, err = cfgDirLoadMust(src, "neigh", n, "signpub"); err != nil {
631                         return nil, err
632                 }
633                 if node.NoisePub, err = cfgDirLoadOpt(src, "neigh", n, "noisepub"); err != nil {
634                         return nil, err
635                 }
636                 if node.Incoming, err = cfgDirLoadOpt(src, "neigh", n, "incoming"); err != nil {
637                         return nil, err
638                 }
639
640                 node.Exec = make(map[string][]string)
641                 fis2, err := ioutil.ReadDir(filepath.Join(src, "neigh", n, "exec"))
642                 if err != nil && !os.IsNotExist(err) {
643                         return nil, err
644                 }
645                 for _, fi2 := range fis2 {
646                         n2 := fi2.Name()
647                         if n2[0] == '.' {
648                                 continue
649                         }
650                         s, err := cfgDirLoadMust(src, "neigh", n, "exec", n2)
651                         if err != nil {
652                                 return nil, err
653                         }
654                         node.Exec[n2] = strings.Split(s, "\n")
655                 }
656
657                 if cfgDirExists(src, "neigh", n, "freq") {
658                         node.Freq = &NodeFreqJSON{}
659                         if node.Freq.Path, err = cfgDirLoadOpt(src, "neigh", n, "freq", "path"); err != nil {
660                                 return nil, err
661                         }
662
663                         i64, err := cfgDirLoadIntOpt(src, "neigh", n, "freq", "chunked")
664                         if err != nil {
665                                 return nil, err
666                         }
667                         if i64 != nil {
668                                 i := uint64(*i64)
669                                 node.Freq.Chunked = &i
670                         }
671
672                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "freq", "minsize")
673                         if err != nil {
674                                 return nil, err
675                         }
676                         if i64 != nil {
677                                 i := uint64(*i64)
678                                 node.Freq.MinSize = &i
679                         }
680
681                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "freq", "maxsize")
682                         if err != nil {
683                                 return nil, err
684                         }
685                         if i64 != nil {
686                                 i := uint64(*i64)
687                                 node.Freq.MaxSize = &i
688                         }
689                 }
690
691                 via, err := cfgDirLoadOpt(src, "neigh", n, "via")
692                 if err != nil {
693                         return nil, err
694                 }
695                 if via != nil {
696                         node.Via = strings.Split(*via, "\n")
697                 }
698
699                 node.Addrs = make(map[string]string)
700                 fis2, err = ioutil.ReadDir(filepath.Join(src, "neigh", n, "addrs"))
701                 if err != nil && !os.IsNotExist(err) {
702                         return nil, err
703                 }
704                 for _, fi2 := range fis2 {
705                         n2 := fi2.Name()
706                         if n2[0] == '.' {
707                                 continue
708                         }
709                         if node.Addrs[n2], err = cfgDirLoadMust(src, "neigh", n, "addrs", n2); err != nil {
710                                 return nil, err
711                         }
712                 }
713
714                 i64, err := cfgDirLoadIntOpt(src, "neigh", n, "rxrate")
715                 if err != nil {
716                         return nil, err
717                 }
718                 if i64 != nil {
719                         i := int(*i64)
720                         node.RxRate = &i
721                 }
722
723                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "txrate")
724                 if err != nil {
725                         return nil, err
726                 }
727                 if i64 != nil {
728                         i := int(*i64)
729                         node.TxRate = &i
730                 }
731
732                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "onlinedeadline")
733                 if err != nil {
734                         return nil, err
735                 }
736                 if i64 != nil {
737                         i := uint(*i64)
738                         node.OnlineDeadline = &i
739                 }
740
741                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "maxonlinetime")
742                 if err != nil {
743                         return nil, err
744                 }
745                 if i64 != nil {
746                         i := uint(*i64)
747                         node.MaxOnlineTime = &i
748                 }
749
750                 fis2, err = ioutil.ReadDir(filepath.Join(src, "neigh", n, "calls"))
751                 if err != nil && !os.IsNotExist(err) {
752                         return nil, err
753                 }
754                 callsIdx := make([]int, 0, len(fis2))
755                 for _, fi2 := range fis2 {
756                         n2 := fi2.Name()
757                         if !fi2.IsDir() {
758                                 continue
759                         }
760                         i, err := strconv.Atoi(n2)
761                         if err != nil {
762                                 continue
763                         }
764                         callsIdx = append(callsIdx, i)
765                 }
766                 sort.Ints(callsIdx)
767                 for _, i := range callsIdx {
768                         call := CallJSON{}
769                         is := strconv.Itoa(i)
770                         if call.Cron, err = cfgDirLoadMust(
771                                 src, "neigh", n, "calls", is, "cron",
772                         ); err != nil {
773                                 return nil, err
774                         }
775                         if call.Nice, err = cfgDirLoadOpt(
776                                 src, "neigh", n, "calls", is, "nice",
777                         ); err != nil {
778                                 return nil, err
779                         }
780                         if call.Xx, err = cfgDirLoadOpt(
781                                 src, "neigh", n, "calls", is, "xx",
782                         ); err != nil {
783                                 return nil, err
784                         }
785
786                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "rxrate")
787                         if err != nil {
788                                 return nil, err
789                         }
790                         if i64 != nil {
791                                 i := int(*i64)
792                                 call.RxRate = &i
793                         }
794
795                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "txrate")
796                         if err != nil {
797                                 return nil, err
798                         }
799                         if i64 != nil {
800                                 i := int(*i64)
801                                 call.TxRate = &i
802                         }
803
804                         if call.Addr, err = cfgDirLoadOpt(
805                                 src, "neigh", n, "calls", is, "addr",
806                         ); err != nil {
807                                 return nil, err
808                         }
809
810                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "onlinedeadline")
811                         if err != nil {
812                                 return nil, err
813                         }
814                         if i64 != nil {
815                                 i := uint(*i64)
816                                 call.OnlineDeadline = &i
817                         }
818
819                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "maxonlinetime")
820                         if err != nil {
821                                 return nil, err
822                         }
823                         if i64 != nil {
824                                 i := uint(*i64)
825                                 call.MaxOnlineTime = &i
826                         }
827
828                         if cfgDirExists(src, "neigh", n, "calls", is, "when-tx-exists") {
829                                 call.WhenTxExists = true
830                         }
831                         if cfgDirExists(src, "neigh", n, "calls", is, "nock") {
832                                 call.NoCK = true
833                         }
834                         if cfgDirExists(src, "neigh", n, "calls", is, "mcd-ignore") {
835                                 call.MCDIgnore = true
836                         }
837                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss") {
838                                 call.AutoToss = true
839                         }
840                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-doseen") {
841                                 call.AutoTossDoSeen = true
842                         }
843                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-nofile") {
844                                 call.AutoTossNoFile = true
845                         }
846                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-nofreq") {
847                                 call.AutoTossNoFreq = true
848                         }
849                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-noexec") {
850                                 call.AutoTossNoExec = true
851                         }
852                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-notrns") {
853                                 call.AutoTossNoTrns = true
854                         }
855                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-noarea") {
856                                 call.AutoTossNoArea = true
857                         }
858                         node.Calls = append(node.Calls, call)
859                 }
860                 cfg.Neigh[n] = node
861         }
862
863         cfg.Areas = make(map[string]AreaJSON)
864         fis, err = ioutil.ReadDir(filepath.Join(src, "areas"))
865         if err != nil && !os.IsNotExist(err) {
866                 return nil, err
867         }
868         for _, fi := range fis {
869                 n := fi.Name()
870                 if n[0] == '.' {
871                         continue
872                 }
873                 area := AreaJSON{}
874                 if area.Id, err = cfgDirLoadMust(src, "areas", n, "id"); err != nil {
875                         return nil, err
876                 }
877                 if area.Pub, err = cfgDirLoadOpt(src, "areas", n, "pub"); err != nil {
878                         return nil, err
879                 }
880                 if area.Prv, err = cfgDirLoadOpt(src, "areas", n, "prv"); err != nil {
881                         return nil, err
882                 }
883
884                 subs, err := cfgDirLoadOpt(src, "areas", n, "subs")
885                 if err != nil {
886                         return nil, err
887                 }
888                 if subs != nil {
889                         area.Subs = strings.Split(*subs, "\n")
890                 }
891
892                 area.Exec = make(map[string][]string)
893                 fis2, err := ioutil.ReadDir(filepath.Join(src, "areas", n, "exec"))
894                 if err != nil && !os.IsNotExist(err) {
895                         return nil, err
896                 }
897                 for _, fi2 := range fis2 {
898                         n2 := fi2.Name()
899                         if n2[0] == '.' {
900                                 continue
901                         }
902                         s, err := cfgDirLoadMust(src, "areas", n, "exec", n2)
903                         if err != nil {
904                                 return nil, err
905                         }
906                         area.Exec[n2] = strings.Split(s, "\n")
907                 }
908
909                 if area.Incoming, err = cfgDirLoadOpt(src, "areas", n, "incoming"); err != nil {
910                         return nil, err
911                 }
912
913                 if cfgDirExists(src, "areas", n, "allow-unknown") {
914                         area.AllowUnknown = true
915                 }
916                 cfg.Areas[n] = area
917         }
918
919         fis, err = ioutil.ReadDir(filepath.Join(src, "yggdrasil-aliases"))
920         if err != nil && !os.IsNotExist(err) {
921                 return nil, err
922         }
923         if len(fis) > 0 {
924                 cfg.YggdrasilAliases = make(map[string]string, len(fis))
925         }
926         for _, fi := range fis {
927                 n := fi.Name()
928                 if n[0] == '.' {
929                         continue
930                 }
931                 b, err := ioutil.ReadFile(filepath.Join(src, "yggdrasil-aliases", fi.Name()))
932                 if err != nil {
933                         return nil, err
934                 }
935                 cfg.YggdrasilAliases[n] = strings.TrimSuffix(string(b), "\n")
936         }
937
938         return &cfg, nil
939 }