]> Cypherpunks.ru repositories - nncp.git/blob - src/cfgdir.go
afe7b40eadab34d16fd7f460aa176bb8181906af
[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 err = cfgDirMkdir(dst, "self"); err != nil {
172                 return
173         }
174         if err = cfgDirSave(cfg.Self.Id, dst, "self", "id"); err != nil {
175                 return
176         }
177         if err = cfgDirSave(cfg.Self.ExchPub, dst, "self", "exchpub"); err != nil {
178                 return
179         }
180         if err = cfgDirSave(cfg.Self.ExchPrv, dst, "self", "exchprv"); err != nil {
181                 return
182         }
183         if err = cfgDirSave(cfg.Self.SignPub, dst, "self", "signpub"); err != nil {
184                 return
185         }
186         if err = cfgDirSave(cfg.Self.SignPrv, dst, "self", "signprv"); err != nil {
187                 return
188         }
189         if err = cfgDirSave(cfg.Self.NoisePub, dst, "self", "noisepub"); err != nil {
190                 return
191         }
192         if err = cfgDirSave(cfg.Self.NoisePrv, dst, "self", "noiseprv"); err != nil {
193                 return
194         }
195
196         for name, n := range cfg.Neigh {
197                 if err = cfgDirMkdir(dst, "neigh", name); err != nil {
198                         return
199                 }
200                 if err = cfgDirSave(n.Id, dst, "neigh", name, "id"); err != nil {
201                         return
202                 }
203                 if err = cfgDirSave(n.ExchPub, dst, "neigh", name, "exchpub"); err != nil {
204                         return
205                 }
206                 if err = cfgDirSave(n.SignPub, dst, "neigh", name, "signpub"); err != nil {
207                         return
208                 }
209                 if err = cfgDirSave(n.NoisePub, dst, "neigh", name, "noisepub"); err != nil {
210                         return
211                 }
212                 if err = cfgDirSave(n.Incoming, dst, "neigh", name, "incoming"); err != nil {
213                         return
214                 }
215
216                 if len(n.Exec) > 0 {
217                         if err = cfgDirMkdir(dst, "neigh", name, "exec"); err != nil {
218                                 return
219                         }
220                         for k, v := range n.Exec {
221                                 if err = cfgDirSave(
222                                         strings.Join(v, "\n"),
223                                         dst, "neigh", name, "exec", k,
224                                 ); err != nil {
225                                         return
226                                 }
227                         }
228                 }
229
230                 if n.Freq != nil {
231                         if err = cfgDirMkdir(dst, "neigh", name, "freq"); err != nil {
232                                 return
233                         }
234                         if err = cfgDirSave(
235                                 n.Freq.Path,
236                                 dst, "neigh", name, "freq", "path",
237                         ); err != nil {
238                                 return
239                         }
240                         if err = cfgDirSave(
241                                 n.Freq.Chunked,
242                                 dst, "neigh", name, "freq", "chunked",
243                         ); err != nil {
244                                 return
245                         }
246                         if err = cfgDirSave(
247                                 n.Freq.MinSize,
248                                 dst, "neigh", name, "freq", "minsize",
249                         ); err != nil {
250                                 return
251                         }
252                         if err = cfgDirSave(
253                                 n.Freq.MaxSize,
254                                 dst, "neigh", name, "freq", "maxsize",
255                         ); err != nil {
256                                 return
257                         }
258                 }
259
260                 if len(n.Via) > 0 {
261                         if err = cfgDirSave(
262                                 strings.Join(n.Via, "\n"),
263                                 dst, "neigh", name, "via",
264                         ); err != nil {
265                                 return
266                         }
267                 }
268
269                 if len(n.Addrs) > 0 {
270                         if err = cfgDirMkdir(dst, "neigh", name, "addrs"); err != nil {
271                                 return
272                         }
273                         for k, v := range n.Addrs {
274                                 if err = cfgDirSave(v, dst, "neigh", name, "addrs", k); err != nil {
275                                         return
276                                 }
277                         }
278                 }
279
280                 if err = cfgDirSave(n.RxRate, dst, "neigh", name, "rxrate"); err != nil {
281                         return
282                 }
283                 if err = cfgDirSave(n.TxRate, dst, "neigh", name, "txrate"); err != nil {
284                         return
285                 }
286                 if err = cfgDirSave(n.OnlineDeadline, dst, "neigh", name, "onlinedeadline"); err != nil {
287                         return
288                 }
289                 if err = cfgDirSave(n.MaxOnlineTime, dst, "neigh", name, "maxonlinetime"); err != nil {
290                         return
291                 }
292
293                 for i, call := range n.Calls {
294                         is := strconv.Itoa(i)
295                         if err = cfgDirMkdir(dst, "neigh", name, "calls", is); err != nil {
296                                 return
297                         }
298                         if err = cfgDirSave(call.Cron, dst, "neigh", name, "calls", is, "cron"); err != nil {
299                                 return
300                         }
301                         if err = cfgDirSave(call.Nice, dst, "neigh", name, "calls", is, "nice"); err != nil {
302                                 return
303                         }
304                         if err = cfgDirSave(call.Xx, dst, "neigh", name, "calls", is, "xx"); err != nil {
305                                 return
306                         }
307                         if err = cfgDirSave(call.RxRate, dst, "neigh", name, "calls", is, "rxrate"); err != nil {
308                                 return
309                         }
310                         if err = cfgDirSave(call.TxRate, dst, "neigh", name, "calls", is, "txrate"); err != nil {
311                                 return
312                         }
313                         if err = cfgDirSave(call.Addr, dst, "neigh", name, "calls", is, "addr"); err != nil {
314                                 return
315                         }
316                         if err = cfgDirSave(call.OnlineDeadline, dst, "neigh", name, "calls", is, "onlinedeadline"); err != nil {
317                                 return
318                         }
319                         if err = cfgDirSave(call.MaxOnlineTime, dst, "neigh", name, "calls", is, "maxonlinetime"); err != nil {
320                                 return
321                         }
322                         if call.WhenTxExists {
323                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "when-tx-exists"); err != nil {
324                                         return
325                                 }
326                         }
327                         if call.NoCK {
328                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "nock"); err != nil {
329                                         return
330                                 }
331                         }
332                         if call.MCDIgnore {
333                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "mcd-ignore"); err != nil {
334                                         return
335                                 }
336                         }
337                         if call.AutoToss {
338                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss"); err != nil {
339                                         return
340                                 }
341                         }
342                         if call.AutoTossDoSeen {
343                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-doseen"); err != nil {
344                                         return
345                                 }
346                         }
347                         if call.AutoTossNoFile {
348                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-nofile"); err != nil {
349                                         return
350                                 }
351                         }
352                         if call.AutoTossNoFreq {
353                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-nofreq"); err != nil {
354                                         return
355                                 }
356                         }
357                         if call.AutoTossNoExec {
358                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-noexec"); err != nil {
359                                         return
360                                 }
361                         }
362                         if call.AutoTossNoTrns {
363                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-notrns"); err != nil {
364                                         return
365                                 }
366                         }
367                         if call.AutoTossNoArea {
368                                 if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-noarea"); err != nil {
369                                         return
370                                 }
371                         }
372                 }
373         }
374
375         for name, a := range cfg.Areas {
376                 if err = cfgDirMkdir(dst, "areas", name); err != nil {
377                         return
378                 }
379                 if err = cfgDirSave(a.Id, dst, "areas", name, "id"); err != nil {
380                         return
381                 }
382                 if err = cfgDirSave(a.Pub, dst, "areas", name, "pub"); err != nil {
383                         return
384                 }
385                 if err = cfgDirSave(a.Prv, dst, "areas", name, "prv"); err != nil {
386                         return
387                 }
388                 if err = cfgDirSave(a.Incoming, dst, "areas", name, "incoming"); err != nil {
389                         return
390                 }
391                 if a.AllowUnknown {
392                         if err = cfgDirTouch(dst, "areas", name, "allow-unknown"); err != nil {
393                                 return
394                         }
395                 }
396                 if len(a.Exec) > 0 {
397                         if err = cfgDirMkdir(dst, "areas", name, "exec"); err != nil {
398                                 return
399                         }
400                         for k, v := range a.Exec {
401                                 if err = cfgDirSave(
402                                         strings.Join(v, "\n"),
403                                         dst, "areas", name, "exec", k,
404                                 ); err != nil {
405                                         return
406                                 }
407                         }
408                 }
409                 if len(a.Subs) > 0 {
410                         if err = cfgDirSave(
411                                 strings.Join(a.Subs, "\n"),
412                                 dst, "areas", name, "subs",
413                         ); err != nil {
414                                 return
415                         }
416                 }
417         }
418
419         return
420 }
421
422 func cfgDirLoad(src ...string) (v string, exists bool, err error) {
423         b, err := ioutil.ReadFile(filepath.Join(src...))
424         if err != nil {
425                 if os.IsNotExist(err) {
426                         return "", false, nil
427                 }
428                 return "", false, err
429         }
430         return strings.TrimSuffix(string(b), "\n"), true, nil
431 }
432
433 func cfgDirLoadMust(src ...string) (v string, err error) {
434         s, exists, err := cfgDirLoad(src...)
435         if err != nil {
436                 return "", err
437         }
438         if !exists {
439                 return "", fmt.Errorf("required \"%s\" does not exist", src[len(src)-1])
440         }
441         return s, nil
442 }
443
444 func cfgDirLoadOpt(src ...string) (v *string, err error) {
445         s, exists, err := cfgDirLoad(src...)
446         if err != nil {
447                 return nil, err
448         }
449         if !exists {
450                 return nil, nil
451         }
452         return &s, nil
453 }
454
455 func cfgDirLoadIntOpt(src ...string) (i64 *int64, err error) {
456         s, err := cfgDirLoadOpt(src...)
457         if err != nil {
458                 return nil, err
459         }
460         if s == nil {
461                 return nil, nil
462         }
463         i, err := strconv.ParseInt(*s, 10, 64)
464         if err != nil {
465                 return nil, err
466         }
467         return &i, nil
468 }
469
470 func cfgDirExists(src ...string) bool {
471         if _, err := os.Stat(filepath.Join(src...)); err == nil {
472                 return true
473         }
474         return false
475 }
476
477 func cfgDirReadFromTo(src ...string) (*FromToJSON, error) {
478         fromTo := FromToJSON{}
479
480         var err error
481         fromTo.From, err = cfgDirLoadMust(append(src, "from")...)
482         if err != nil {
483                 return nil, err
484         }
485
486         fromTo.To, err = cfgDirLoadMust(append(src, "to")...)
487         if err != nil {
488                 return nil, err
489         }
490
491         return &fromTo, nil
492 }
493
494 func DirToCfg(src string) (*CfgJSON, error) {
495         cfg := CfgJSON{}
496         var err error
497
498         cfg.Spool, err = cfgDirLoadMust(src, "spool")
499         if err != nil {
500                 return nil, err
501         }
502         cfg.Log, err = cfgDirLoadMust(src, "log")
503         if err != nil {
504                 return nil, err
505         }
506
507         if cfg.Umask, err = cfgDirLoadOpt(src, "umask"); err != nil {
508                 return nil, err
509         }
510         cfg.OmitPrgrs = cfgDirExists(src, "noprogress")
511         cfg.NoHdr = cfgDirExists(src, "nohdr")
512
513         sp, err := cfgDirLoadOpt(src, "mcd-listen")
514         if err != nil {
515                 return nil, err
516         }
517         if sp != nil {
518                 cfg.MCDRxIfis = strings.Split(*sp, "\n")
519         }
520
521         fis, err := ioutil.ReadDir(filepath.Join(src, "mcd-send"))
522         if err != nil && !os.IsNotExist(err) {
523                 return nil, err
524         }
525         if len(fis) > 0 {
526                 cfg.MCDTxIfis = make(map[string]int, len(fis))
527         }
528         for _, fi := range fis {
529                 n := fi.Name()
530                 if n[0] == '.' {
531                         continue
532                 }
533                 b, err := ioutil.ReadFile(filepath.Join(src, "mcd-send", fi.Name()))
534                 if err != nil {
535                         return nil, err
536                 }
537                 i, err := strconv.Atoi(strings.TrimSuffix(string(b), "\n"))
538                 if err != nil {
539                         return nil, err
540                 }
541                 cfg.MCDTxIfis[n] = i
542         }
543
544         notify := NotifyJSON{Exec: make(map[string]*FromToJSON)}
545         if cfgDirExists(src, "notify", "file") {
546                 if notify.File, err = cfgDirReadFromTo(src, "notify", "file"); err != nil {
547                         return nil, err
548                 }
549         }
550         if cfgDirExists(src, "notify", "freq") {
551                 if notify.Freq, err = cfgDirReadFromTo(src, "notify", "freq"); err != nil {
552                         return nil, err
553                 }
554         }
555         fis, err = ioutil.ReadDir(filepath.Join(src, "notify", "exec"))
556         if err != nil && !os.IsNotExist(err) {
557                 return nil, err
558         }
559         for _, fi := range fis {
560                 n := fi.Name()
561                 if n[0] == '.' || !fi.IsDir() {
562                         continue
563                 }
564                 if notify.Exec[fi.Name()], err = cfgDirReadFromTo(src, "notify", "exec", n); err != nil {
565                         return nil, err
566                 }
567         }
568         if notify.File != nil || notify.Freq != nil || len(notify.Exec) > 0 {
569                 cfg.Notify = &notify
570         }
571
572         self := NodeOurJSON{}
573         if self.Id, err = cfgDirLoadMust(src, "self", "id"); err != nil {
574                 return nil, err
575         }
576         if self.ExchPub, err = cfgDirLoadMust(src, "self", "exchpub"); err != nil {
577                 return nil, err
578         }
579         if self.ExchPrv, err = cfgDirLoadMust(src, "self", "exchprv"); err != nil {
580                 return nil, err
581         }
582         if self.SignPub, err = cfgDirLoadMust(src, "self", "signpub"); err != nil {
583                 return nil, err
584         }
585         if self.SignPrv, err = cfgDirLoadMust(src, "self", "signprv"); err != nil {
586                 return nil, err
587         }
588         if self.NoisePub, err = cfgDirLoadMust(src, "self", "noisepub"); err != nil {
589                 return nil, err
590         }
591         if self.NoisePrv, err = cfgDirLoadMust(src, "self", "noiseprv"); err != nil {
592                 return nil, err
593         }
594         cfg.Self = &self
595
596         cfg.Neigh = make(map[string]NodeJSON)
597         fis, err = ioutil.ReadDir(filepath.Join(src, "neigh"))
598         if err != nil && !os.IsNotExist(err) {
599                 return nil, err
600         }
601         for _, fi := range fis {
602                 n := fi.Name()
603                 if n[0] == '.' {
604                         continue
605                 }
606                 node := NodeJSON{}
607                 if node.Id, err = cfgDirLoadMust(src, "neigh", n, "id"); err != nil {
608                         return nil, err
609                 }
610                 if node.ExchPub, err = cfgDirLoadMust(src, "neigh", n, "exchpub"); err != nil {
611                         return nil, err
612                 }
613                 if node.SignPub, err = cfgDirLoadMust(src, "neigh", n, "signpub"); err != nil {
614                         return nil, err
615                 }
616                 if node.NoisePub, err = cfgDirLoadOpt(src, "neigh", n, "noisepub"); err != nil {
617                         return nil, err
618                 }
619                 if node.Incoming, err = cfgDirLoadOpt(src, "neigh", n, "incoming"); err != nil {
620                         return nil, err
621                 }
622
623                 node.Exec = make(map[string][]string)
624                 fis2, err := ioutil.ReadDir(filepath.Join(src, "neigh", n, "exec"))
625                 if err != nil && !os.IsNotExist(err) {
626                         return nil, err
627                 }
628                 for _, fi2 := range fis2 {
629                         n2 := fi2.Name()
630                         if n2[0] == '.' {
631                                 continue
632                         }
633                         s, err := cfgDirLoadMust(src, "neigh", n, "exec", n2)
634                         if err != nil {
635                                 return nil, err
636                         }
637                         node.Exec[n2] = strings.Split(s, "\n")
638                 }
639
640                 if cfgDirExists(src, "neigh", n, "freq") {
641                         node.Freq = &NodeFreqJSON{}
642                         if node.Freq.Path, err = cfgDirLoadOpt(src, "neigh", n, "freq", "path"); err != nil {
643                                 return nil, err
644                         }
645
646                         i64, err := cfgDirLoadIntOpt(src, "neigh", n, "freq", "chunked")
647                         if err != nil {
648                                 return nil, err
649                         }
650                         if i64 != nil {
651                                 i := uint64(*i64)
652                                 node.Freq.Chunked = &i
653                         }
654
655                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "freq", "minsize")
656                         if err != nil {
657                                 return nil, err
658                         }
659                         if i64 != nil {
660                                 i := uint64(*i64)
661                                 node.Freq.MinSize = &i
662                         }
663
664                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "freq", "maxsize")
665                         if err != nil {
666                                 return nil, err
667                         }
668                         if i64 != nil {
669                                 i := uint64(*i64)
670                                 node.Freq.MaxSize = &i
671                         }
672                 }
673
674                 via, err := cfgDirLoadOpt(src, "neigh", n, "via")
675                 if err != nil {
676                         return nil, err
677                 }
678                 if via != nil {
679                         node.Via = strings.Split(*via, "\n")
680                 }
681
682                 node.Addrs = make(map[string]string)
683                 fis2, err = ioutil.ReadDir(filepath.Join(src, "neigh", n, "addrs"))
684                 if err != nil && !os.IsNotExist(err) {
685                         return nil, err
686                 }
687                 for _, fi2 := range fis2 {
688                         n2 := fi2.Name()
689                         if n2[0] == '.' {
690                                 continue
691                         }
692                         if node.Addrs[n2], err = cfgDirLoadMust(src, "neigh", n, "addrs", n2); err != nil {
693                                 return nil, err
694                         }
695                 }
696
697                 i64, err := cfgDirLoadIntOpt(src, "neigh", n, "rxrate")
698                 if err != nil {
699                         return nil, err
700                 }
701                 if i64 != nil {
702                         i := int(*i64)
703                         node.RxRate = &i
704                 }
705
706                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "txrate")
707                 if err != nil {
708                         return nil, err
709                 }
710                 if i64 != nil {
711                         i := int(*i64)
712                         node.TxRate = &i
713                 }
714
715                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "onlinedeadline")
716                 if err != nil {
717                         return nil, err
718                 }
719                 if i64 != nil {
720                         i := uint(*i64)
721                         node.OnlineDeadline = &i
722                 }
723
724                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "maxonlinetime")
725                 if err != nil {
726                         return nil, err
727                 }
728                 if i64 != nil {
729                         i := uint(*i64)
730                         node.MaxOnlineTime = &i
731                 }
732
733                 fis2, err = ioutil.ReadDir(filepath.Join(src, "neigh", n, "calls"))
734                 if err != nil && !os.IsNotExist(err) {
735                         return nil, err
736                 }
737                 callsIdx := make([]int, 0, len(fis2))
738                 for _, fi2 := range fis2 {
739                         n2 := fi2.Name()
740                         if !fi2.IsDir() {
741                                 continue
742                         }
743                         i, err := strconv.Atoi(n2)
744                         if err != nil {
745                                 continue
746                         }
747                         callsIdx = append(callsIdx, i)
748                 }
749                 sort.Ints(callsIdx)
750                 for _, i := range callsIdx {
751                         call := CallJSON{}
752                         is := strconv.Itoa(i)
753                         if call.Cron, err = cfgDirLoadMust(
754                                 src, "neigh", n, "calls", is, "cron",
755                         ); err != nil {
756                                 return nil, err
757                         }
758                         if call.Nice, err = cfgDirLoadOpt(
759                                 src, "neigh", n, "calls", is, "nice",
760                         ); err != nil {
761                                 return nil, err
762                         }
763                         if call.Xx, err = cfgDirLoadOpt(
764                                 src, "neigh", n, "calls", is, "xx",
765                         ); err != nil {
766                                 return nil, err
767                         }
768
769                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "rxrate")
770                         if err != nil {
771                                 return nil, err
772                         }
773                         if i64 != nil {
774                                 i := int(*i64)
775                                 call.RxRate = &i
776                         }
777
778                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "txrate")
779                         if err != nil {
780                                 return nil, err
781                         }
782                         if i64 != nil {
783                                 i := int(*i64)
784                                 call.TxRate = &i
785                         }
786
787                         if call.Addr, err = cfgDirLoadOpt(
788                                 src, "neigh", n, "calls", is, "addr",
789                         ); err != nil {
790                                 return nil, err
791                         }
792
793                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "onlinedeadline")
794                         if err != nil {
795                                 return nil, err
796                         }
797                         if i64 != nil {
798                                 i := uint(*i64)
799                                 call.OnlineDeadline = &i
800                         }
801
802                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "maxonlinetime")
803                         if err != nil {
804                                 return nil, err
805                         }
806                         if i64 != nil {
807                                 i := uint(*i64)
808                                 call.MaxOnlineTime = &i
809                         }
810
811                         if cfgDirExists(src, "neigh", n, "calls", is, "when-tx-exists") {
812                                 call.WhenTxExists = true
813                         }
814                         if cfgDirExists(src, "neigh", n, "calls", is, "nock") {
815                                 call.NoCK = true
816                         }
817                         if cfgDirExists(src, "neigh", n, "calls", is, "mcd-ignore") {
818                                 call.MCDIgnore = true
819                         }
820                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss") {
821                                 call.AutoToss = true
822                         }
823                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-doseen") {
824                                 call.AutoTossDoSeen = true
825                         }
826                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-nofile") {
827                                 call.AutoTossNoFile = true
828                         }
829                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-nofreq") {
830                                 call.AutoTossNoFreq = true
831                         }
832                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-noexec") {
833                                 call.AutoTossNoExec = true
834                         }
835                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-notrns") {
836                                 call.AutoTossNoTrns = true
837                         }
838                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-noarea") {
839                                 call.AutoTossNoArea = true
840                         }
841                         node.Calls = append(node.Calls, call)
842                 }
843                 cfg.Neigh[n] = node
844         }
845
846         cfg.Areas = make(map[string]AreaJSON)
847         fis, err = ioutil.ReadDir(filepath.Join(src, "areas"))
848         if err != nil && !os.IsNotExist(err) {
849                 return nil, err
850         }
851         for _, fi := range fis {
852                 n := fi.Name()
853                 if n[0] == '.' {
854                         continue
855                 }
856                 area := AreaJSON{}
857                 if area.Id, err = cfgDirLoadMust(src, "areas", n, "id"); err != nil {
858                         return nil, err
859                 }
860                 if area.Pub, err = cfgDirLoadOpt(src, "areas", n, "pub"); err != nil {
861                         return nil, err
862                 }
863                 if area.Prv, err = cfgDirLoadOpt(src, "areas", n, "prv"); err != nil {
864                         return nil, err
865                 }
866
867                 subs, err := cfgDirLoadOpt(src, "areas", n, "subs")
868                 if err != nil {
869                         return nil, err
870                 }
871                 if subs != nil {
872                         area.Subs = strings.Split(*subs, "\n")
873                 }
874
875                 area.Exec = make(map[string][]string)
876                 fis2, err := ioutil.ReadDir(filepath.Join(src, "areas", n, "exec"))
877                 if err != nil && !os.IsNotExist(err) {
878                         return nil, err
879                 }
880                 for _, fi2 := range fis2 {
881                         n2 := fi2.Name()
882                         if n2[0] == '.' {
883                                 continue
884                         }
885                         s, err := cfgDirLoadMust(src, "areas", n, "exec", n2)
886                         if err != nil {
887                                 return nil, err
888                         }
889                         area.Exec[n2] = strings.Split(s, "\n")
890                 }
891
892                 if area.Incoming, err = cfgDirLoadOpt(src, "areas", n, "incoming"); err != nil {
893                         return nil, err
894                 }
895
896                 if cfgDirExists(src, "areas", n, "allow-unknown") {
897                         area.AllowUnknown = true
898                 }
899                 cfg.Areas[n] = area
900         }
901
902         return &cfg, nil
903 }