]> Cypherpunks.ru repositories - nncp.git/blob - src/cfgdir.go
self section is optional in nncp-cfgdir
[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         return
422 }
423
424 func cfgDirLoad(src ...string) (v string, exists bool, err error) {
425         b, err := ioutil.ReadFile(filepath.Join(src...))
426         if err != nil {
427                 if os.IsNotExist(err) {
428                         return "", false, nil
429                 }
430                 return "", false, err
431         }
432         return strings.TrimSuffix(string(b), "\n"), true, nil
433 }
434
435 func cfgDirLoadMust(src ...string) (v string, err error) {
436         s, exists, err := cfgDirLoad(src...)
437         if err != nil {
438                 return "", err
439         }
440         if !exists {
441                 return "", fmt.Errorf("required \"%s\" does not exist", src[len(src)-1])
442         }
443         return s, nil
444 }
445
446 func cfgDirLoadOpt(src ...string) (v *string, err error) {
447         s, exists, err := cfgDirLoad(src...)
448         if err != nil {
449                 return nil, err
450         }
451         if !exists {
452                 return nil, nil
453         }
454         return &s, nil
455 }
456
457 func cfgDirLoadIntOpt(src ...string) (i64 *int64, err error) {
458         s, err := cfgDirLoadOpt(src...)
459         if err != nil {
460                 return nil, err
461         }
462         if s == nil {
463                 return nil, nil
464         }
465         i, err := strconv.ParseInt(*s, 10, 64)
466         if err != nil {
467                 return nil, err
468         }
469         return &i, nil
470 }
471
472 func cfgDirExists(src ...string) bool {
473         if _, err := os.Stat(filepath.Join(src...)); err == nil {
474                 return true
475         }
476         return false
477 }
478
479 func cfgDirReadFromTo(src ...string) (*FromToJSON, error) {
480         fromTo := FromToJSON{}
481
482         var err error
483         fromTo.From, err = cfgDirLoadMust(append(src, "from")...)
484         if err != nil {
485                 return nil, err
486         }
487
488         fromTo.To, err = cfgDirLoadMust(append(src, "to")...)
489         if err != nil {
490                 return nil, err
491         }
492
493         return &fromTo, nil
494 }
495
496 func DirToCfg(src string) (*CfgJSON, error) {
497         cfg := CfgJSON{}
498         var err error
499
500         cfg.Spool, err = cfgDirLoadMust(src, "spool")
501         if err != nil {
502                 return nil, err
503         }
504         cfg.Log, err = cfgDirLoadMust(src, "log")
505         if err != nil {
506                 return nil, err
507         }
508
509         if cfg.Umask, err = cfgDirLoadOpt(src, "umask"); err != nil {
510                 return nil, err
511         }
512         cfg.OmitPrgrs = cfgDirExists(src, "noprogress")
513         cfg.NoHdr = cfgDirExists(src, "nohdr")
514
515         sp, err := cfgDirLoadOpt(src, "mcd-listen")
516         if err != nil {
517                 return nil, err
518         }
519         if sp != nil {
520                 cfg.MCDRxIfis = strings.Split(*sp, "\n")
521         }
522
523         fis, err := ioutil.ReadDir(filepath.Join(src, "mcd-send"))
524         if err != nil && !os.IsNotExist(err) {
525                 return nil, err
526         }
527         if len(fis) > 0 {
528                 cfg.MCDTxIfis = make(map[string]int, len(fis))
529         }
530         for _, fi := range fis {
531                 n := fi.Name()
532                 if n[0] == '.' {
533                         continue
534                 }
535                 b, err := ioutil.ReadFile(filepath.Join(src, "mcd-send", fi.Name()))
536                 if err != nil {
537                         return nil, err
538                 }
539                 i, err := strconv.Atoi(strings.TrimSuffix(string(b), "\n"))
540                 if err != nil {
541                         return nil, err
542                 }
543                 cfg.MCDTxIfis[n] = i
544         }
545
546         notify := NotifyJSON{Exec: make(map[string]*FromToJSON)}
547         if cfgDirExists(src, "notify", "file") {
548                 if notify.File, err = cfgDirReadFromTo(src, "notify", "file"); err != nil {
549                         return nil, err
550                 }
551         }
552         if cfgDirExists(src, "notify", "freq") {
553                 if notify.Freq, err = cfgDirReadFromTo(src, "notify", "freq"); err != nil {
554                         return nil, err
555                 }
556         }
557         fis, err = ioutil.ReadDir(filepath.Join(src, "notify", "exec"))
558         if err != nil && !os.IsNotExist(err) {
559                 return nil, err
560         }
561         for _, fi := range fis {
562                 n := fi.Name()
563                 if n[0] == '.' || !fi.IsDir() {
564                         continue
565                 }
566                 if notify.Exec[fi.Name()], err = cfgDirReadFromTo(src, "notify", "exec", n); err != nil {
567                         return nil, err
568                 }
569         }
570         if notify.File != nil || notify.Freq != nil || len(notify.Exec) > 0 {
571                 cfg.Notify = &notify
572         }
573
574         if _, err = ioutil.ReadDir(filepath.Join(src, "self")); err == nil {
575                 self := NodeOurJSON{}
576                 if self.Id, err = cfgDirLoadMust(src, "self", "id"); err != nil {
577                         return nil, err
578                 }
579                 if self.ExchPub, err = cfgDirLoadMust(src, "self", "exchpub"); err != nil {
580                         return nil, err
581                 }
582                 if self.ExchPrv, err = cfgDirLoadMust(src, "self", "exchprv"); err != nil {
583                         return nil, err
584                 }
585                 if self.SignPub, err = cfgDirLoadMust(src, "self", "signpub"); err != nil {
586                         return nil, err
587                 }
588                 if self.SignPrv, err = cfgDirLoadMust(src, "self", "signprv"); err != nil {
589                         return nil, err
590                 }
591                 if self.NoisePub, err = cfgDirLoadMust(src, "self", "noisepub"); err != nil {
592                         return nil, err
593                 }
594                 if self.NoisePrv, err = cfgDirLoadMust(src, "self", "noiseprv"); err != nil {
595                         return nil, err
596                 }
597                 cfg.Self = &self
598         } else if !os.IsNotExist(err) {
599                 return nil, err
600         }
601
602         cfg.Neigh = make(map[string]NodeJSON)
603         fis, err = ioutil.ReadDir(filepath.Join(src, "neigh"))
604         if err != nil && !os.IsNotExist(err) {
605                 return nil, err
606         }
607         for _, fi := range fis {
608                 n := fi.Name()
609                 if n[0] == '.' {
610                         continue
611                 }
612                 node := NodeJSON{}
613                 if node.Id, err = cfgDirLoadMust(src, "neigh", n, "id"); err != nil {
614                         return nil, err
615                 }
616                 if node.ExchPub, err = cfgDirLoadMust(src, "neigh", n, "exchpub"); err != nil {
617                         return nil, err
618                 }
619                 if node.SignPub, err = cfgDirLoadMust(src, "neigh", n, "signpub"); err != nil {
620                         return nil, err
621                 }
622                 if node.NoisePub, err = cfgDirLoadOpt(src, "neigh", n, "noisepub"); err != nil {
623                         return nil, err
624                 }
625                 if node.Incoming, err = cfgDirLoadOpt(src, "neigh", n, "incoming"); err != nil {
626                         return nil, err
627                 }
628
629                 node.Exec = make(map[string][]string)
630                 fis2, err := ioutil.ReadDir(filepath.Join(src, "neigh", n, "exec"))
631                 if err != nil && !os.IsNotExist(err) {
632                         return nil, err
633                 }
634                 for _, fi2 := range fis2 {
635                         n2 := fi2.Name()
636                         if n2[0] == '.' {
637                                 continue
638                         }
639                         s, err := cfgDirLoadMust(src, "neigh", n, "exec", n2)
640                         if err != nil {
641                                 return nil, err
642                         }
643                         node.Exec[n2] = strings.Split(s, "\n")
644                 }
645
646                 if cfgDirExists(src, "neigh", n, "freq") {
647                         node.Freq = &NodeFreqJSON{}
648                         if node.Freq.Path, err = cfgDirLoadOpt(src, "neigh", n, "freq", "path"); err != nil {
649                                 return nil, err
650                         }
651
652                         i64, err := cfgDirLoadIntOpt(src, "neigh", n, "freq", "chunked")
653                         if err != nil {
654                                 return nil, err
655                         }
656                         if i64 != nil {
657                                 i := uint64(*i64)
658                                 node.Freq.Chunked = &i
659                         }
660
661                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "freq", "minsize")
662                         if err != nil {
663                                 return nil, err
664                         }
665                         if i64 != nil {
666                                 i := uint64(*i64)
667                                 node.Freq.MinSize = &i
668                         }
669
670                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "freq", "maxsize")
671                         if err != nil {
672                                 return nil, err
673                         }
674                         if i64 != nil {
675                                 i := uint64(*i64)
676                                 node.Freq.MaxSize = &i
677                         }
678                 }
679
680                 via, err := cfgDirLoadOpt(src, "neigh", n, "via")
681                 if err != nil {
682                         return nil, err
683                 }
684                 if via != nil {
685                         node.Via = strings.Split(*via, "\n")
686                 }
687
688                 node.Addrs = make(map[string]string)
689                 fis2, err = ioutil.ReadDir(filepath.Join(src, "neigh", n, "addrs"))
690                 if err != nil && !os.IsNotExist(err) {
691                         return nil, err
692                 }
693                 for _, fi2 := range fis2 {
694                         n2 := fi2.Name()
695                         if n2[0] == '.' {
696                                 continue
697                         }
698                         if node.Addrs[n2], err = cfgDirLoadMust(src, "neigh", n, "addrs", n2); err != nil {
699                                 return nil, err
700                         }
701                 }
702
703                 i64, err := cfgDirLoadIntOpt(src, "neigh", n, "rxrate")
704                 if err != nil {
705                         return nil, err
706                 }
707                 if i64 != nil {
708                         i := int(*i64)
709                         node.RxRate = &i
710                 }
711
712                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "txrate")
713                 if err != nil {
714                         return nil, err
715                 }
716                 if i64 != nil {
717                         i := int(*i64)
718                         node.TxRate = &i
719                 }
720
721                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "onlinedeadline")
722                 if err != nil {
723                         return nil, err
724                 }
725                 if i64 != nil {
726                         i := uint(*i64)
727                         node.OnlineDeadline = &i
728                 }
729
730                 i64, err = cfgDirLoadIntOpt(src, "neigh", n, "maxonlinetime")
731                 if err != nil {
732                         return nil, err
733                 }
734                 if i64 != nil {
735                         i := uint(*i64)
736                         node.MaxOnlineTime = &i
737                 }
738
739                 fis2, err = ioutil.ReadDir(filepath.Join(src, "neigh", n, "calls"))
740                 if err != nil && !os.IsNotExist(err) {
741                         return nil, err
742                 }
743                 callsIdx := make([]int, 0, len(fis2))
744                 for _, fi2 := range fis2 {
745                         n2 := fi2.Name()
746                         if !fi2.IsDir() {
747                                 continue
748                         }
749                         i, err := strconv.Atoi(n2)
750                         if err != nil {
751                                 continue
752                         }
753                         callsIdx = append(callsIdx, i)
754                 }
755                 sort.Ints(callsIdx)
756                 for _, i := range callsIdx {
757                         call := CallJSON{}
758                         is := strconv.Itoa(i)
759                         if call.Cron, err = cfgDirLoadMust(
760                                 src, "neigh", n, "calls", is, "cron",
761                         ); err != nil {
762                                 return nil, err
763                         }
764                         if call.Nice, err = cfgDirLoadOpt(
765                                 src, "neigh", n, "calls", is, "nice",
766                         ); err != nil {
767                                 return nil, err
768                         }
769                         if call.Xx, err = cfgDirLoadOpt(
770                                 src, "neigh", n, "calls", is, "xx",
771                         ); err != nil {
772                                 return nil, err
773                         }
774
775                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "rxrate")
776                         if err != nil {
777                                 return nil, err
778                         }
779                         if i64 != nil {
780                                 i := int(*i64)
781                                 call.RxRate = &i
782                         }
783
784                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "txrate")
785                         if err != nil {
786                                 return nil, err
787                         }
788                         if i64 != nil {
789                                 i := int(*i64)
790                                 call.TxRate = &i
791                         }
792
793                         if call.Addr, err = cfgDirLoadOpt(
794                                 src, "neigh", n, "calls", is, "addr",
795                         ); err != nil {
796                                 return nil, err
797                         }
798
799                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "onlinedeadline")
800                         if err != nil {
801                                 return nil, err
802                         }
803                         if i64 != nil {
804                                 i := uint(*i64)
805                                 call.OnlineDeadline = &i
806                         }
807
808                         i64, err = cfgDirLoadIntOpt(src, "neigh", n, "calls", is, "maxonlinetime")
809                         if err != nil {
810                                 return nil, err
811                         }
812                         if i64 != nil {
813                                 i := uint(*i64)
814                                 call.MaxOnlineTime = &i
815                         }
816
817                         if cfgDirExists(src, "neigh", n, "calls", is, "when-tx-exists") {
818                                 call.WhenTxExists = true
819                         }
820                         if cfgDirExists(src, "neigh", n, "calls", is, "nock") {
821                                 call.NoCK = true
822                         }
823                         if cfgDirExists(src, "neigh", n, "calls", is, "mcd-ignore") {
824                                 call.MCDIgnore = true
825                         }
826                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss") {
827                                 call.AutoToss = true
828                         }
829                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-doseen") {
830                                 call.AutoTossDoSeen = true
831                         }
832                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-nofile") {
833                                 call.AutoTossNoFile = true
834                         }
835                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-nofreq") {
836                                 call.AutoTossNoFreq = true
837                         }
838                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-noexec") {
839                                 call.AutoTossNoExec = true
840                         }
841                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-notrns") {
842                                 call.AutoTossNoTrns = true
843                         }
844                         if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-noarea") {
845                                 call.AutoTossNoArea = true
846                         }
847                         node.Calls = append(node.Calls, call)
848                 }
849                 cfg.Neigh[n] = node
850         }
851
852         cfg.Areas = make(map[string]AreaJSON)
853         fis, err = ioutil.ReadDir(filepath.Join(src, "areas"))
854         if err != nil && !os.IsNotExist(err) {
855                 return nil, err
856         }
857         for _, fi := range fis {
858                 n := fi.Name()
859                 if n[0] == '.' {
860                         continue
861                 }
862                 area := AreaJSON{}
863                 if area.Id, err = cfgDirLoadMust(src, "areas", n, "id"); err != nil {
864                         return nil, err
865                 }
866                 if area.Pub, err = cfgDirLoadOpt(src, "areas", n, "pub"); err != nil {
867                         return nil, err
868                 }
869                 if area.Prv, err = cfgDirLoadOpt(src, "areas", n, "prv"); err != nil {
870                         return nil, err
871                 }
872
873                 subs, err := cfgDirLoadOpt(src, "areas", n, "subs")
874                 if err != nil {
875                         return nil, err
876                 }
877                 if subs != nil {
878                         area.Subs = strings.Split(*subs, "\n")
879                 }
880
881                 area.Exec = make(map[string][]string)
882                 fis2, err := ioutil.ReadDir(filepath.Join(src, "areas", n, "exec"))
883                 if err != nil && !os.IsNotExist(err) {
884                         return nil, err
885                 }
886                 for _, fi2 := range fis2 {
887                         n2 := fi2.Name()
888                         if n2[0] == '.' {
889                                 continue
890                         }
891                         s, err := cfgDirLoadMust(src, "areas", n, "exec", n2)
892                         if err != nil {
893                                 return nil, err
894                         }
895                         area.Exec[n2] = strings.Split(s, "\n")
896                 }
897
898                 if area.Incoming, err = cfgDirLoadOpt(src, "areas", n, "incoming"); err != nil {
899                         return nil, err
900                 }
901
902                 if cfgDirExists(src, "areas", n, "allow-unknown") {
903                         area.AllowUnknown = true
904                 }
905                 cfg.Areas[n] = area
906         }
907
908         return &cfg, nil
909 }