gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet-go-plugins] branch master updated (52ce708 -> 49730e3)


From: gnunet
Subject: [gnunet-go-plugins] branch master updated (52ce708 -> 49730e3)
Date: Fri, 11 Nov 2022 11:59:22 +0100

This is an automated email from the git hooks/post-receive script.

bernd-fix pushed a change to branch master
in repository gnunet-go-plugins.

    from 52ce708  all: use Utility function from caller.
     new f1d132c  records: changed record layout (LSD0002).
     new 925c1f9  example-go: Utility handler added.
     new 49730e3  reclaimid: first version (no new/edit).

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 example-go/Makefile                    |   2 +-
 example-go/main.go                     |  24 ---
 example-go/utility.go                  | 110 ++++++++++++
 reclaimid/Makefile                     |   2 +-
 reclaimid/gui.htpl                     | 303 ++++++++++++++++++++++-----------
 example-c/plugin.h => reclaimid/jwt.go |  55 +++---
 reclaimid/main.go                      |   3 -
 reclaimid/plugin_test.go               |  10 ++
 reclaimid/records.go                   | 111 +++++-------
 reclaimid/utility.go                   | 106 ++++++++++++
 10 files changed, 501 insertions(+), 225 deletions(-)
 create mode 100644 example-go/utility.go
 copy example-c/plugin.h => reclaimid/jwt.go (52%)
 create mode 100644 reclaimid/utility.go

diff --git a/example-go/Makefile b/example-go/Makefile
index 4c96c72..79755fb 100644
--- a/example-go/Makefile
+++ b/example-go/Makefile
@@ -29,5 +29,5 @@ clean:
 test:
        go test -v ./...
 
-${GOPATH}/bin/plugin_example_go.so: main.go gui.htpl
+${GOPATH}/bin/plugin_example_go.so: main.go records.go utility.go gui.htpl
        go build -buildmode=plugin -trimpath -gcflags="-N -l" -o $@ ./...
diff --git a/example-go/main.go b/example-go/main.go
index 0b46c3d..953b73a 100644
--- a/example-go/main.go
+++ b/example-go/main.go
@@ -24,8 +24,6 @@ package main
 
 import (
        _ "embed"
-       "fmt"
-       "strconv"
 
        "github.com/bfix/gospel/data"
 )
@@ -95,9 +93,6 @@ func (p *CustomPlugin) Prefix(t uint32) string {
 // No need to customize code beond this point...
 //======================================================================
 
-// Utility function
-type Utility func(fcn string, args ...any) any
-
 // Plugin is the instance of a custom implementation accessed by the
 // gnunet-go framework
 var Plugin = NewCustomPlugin()
@@ -182,22 +177,3 @@ func (p *CustomPlugin) GetInstance(t uint32, buf []byte) 
(rec CustomRecord, err
        }
        return
 }
-
-// Numbers convertable from parameter value (string)
-type Number interface {
-       uint16 | uint32 | int | int16 | int32 | float32 | float64
-}
-
-// convert parameter value (string) to given number type
-func asInt[T Number](params map[string]string, key string) (val T, err error) {
-       vs, ok := params[key]
-       if !ok {
-               err = fmt.Errorf("%s missing", key)
-               return
-       }
-       var v int
-       if v, err = strconv.Atoi(vs); err != nil {
-               return
-       }
-       return T(v), nil
-}
diff --git a/example-go/utility.go b/example-go/utility.go
new file mode 100644
index 0000000..091e4e8
--- /dev/null
+++ b/example-go/utility.go
@@ -0,0 +1,110 @@
+// This file is part of gnunet-go-plugins, a collection of plugins for
+// the GNUnet-implementation in Golang (gnunet.go). Plugins are used to
+// implement type-specific processing of resource records (e.g. GUI).
+// Copyright (C) 2022 by the authors:
+//
+// * Bernd Fix <brf@hoi-polloi.org>  >Y<
+//
+// gnunet-go-plugins is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// gnunet-go-plugons is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package main
+
+import (
+       "encoding/base32"
+       "fmt"
+       "strconv"
+       "strings"
+)
+
+// Utility function for exported ZoneMaster utility helpers
+type Utility func(fcn string, args ...any) any
+
+// GNSType returns the name of a GNSType
+func (u Utility) GNSType(t uint32) string {
+       rtype, ok := u("gns_type_name", t).(string)
+       if !ok {
+               return fmt.Sprintf("GNSType(%d)", t)
+       }
+       return rtype
+}
+
+// GNSFlags returns the list of GNS flags by name
+func (u Utility) GNSFlags(f uint32) string {
+       flags, ok := u("gns_flags", f).([]string)
+       if !ok {
+               return fmt.Sprintf("GNSFlags(%d)", f)
+       }
+       if len(flags) == 0 {
+               return "None"
+       }
+       return strings.Join(flags, ",")
+}
+
+// Base32GNS encoding character set
+var cf32Enc = base32.NewEncoding("0123456789ABCDEFGHJKMNPQRSTVWXYZ")
+
+// Return a byte array encoded with Base32GNS
+//
+//nolint:deadcode // might be used, might be not...
+func encodeBase32GNS(buf []byte) string {
+       return strings.TrimRight(cf32Enc.EncodeToString(buf), "=")
+}
+
+// Convert string to byte array using Base32GNS
+//
+//nolint:deadcode // might be used, might be not...
+func decodeBase32GNS(s string) ([]byte, error) {
+       s = strings.ToUpper(s)
+       s = strings.NewReplacer("O", "0", "I", "1", "L", "1", "U", 
"V").Replace(s)
+       return cf32Enc.DecodeString(s)
+}
+
+// Numbers convertable from parameter value (string)
+type Number interface {
+       uint16 | uint32 | int | int16 | int32 | float32 | float64
+}
+
+// convert parameter value (string) to given number type
+func asInt[T Number](params map[string]string, key string) (val T, err error) {
+       vs, ok := params[key]
+       if !ok {
+               err = fmt.Errorf("%s missing", key)
+               return
+       }
+       var v int
+       if v, err = strconv.Atoi(vs); err != nil {
+               return
+       }
+       return T(v), nil
+}
+
+// Split a string into lines of given length
+//
+//nolint:deadcode // might be unused
+func split(s, prefix string, chunk int) (out string) {
+       for len(s) > chunk {
+               if len(out) > 0 {
+                       out += ",<br>"
+               }
+               out += prefix + s[:chunk]
+               s = s[chunk:]
+       }
+       if len(out) > 0 {
+               out += ",<br>"
+       }
+       out += prefix + s
+       return
+}
diff --git a/reclaimid/Makefile b/reclaimid/Makefile
index 0f52007..2c471c6 100644
--- a/reclaimid/Makefile
+++ b/reclaimid/Makefile
@@ -29,5 +29,5 @@ clean:
 test:
        go test -v ./...
 
-${GOPATH}/bin/plugin_reclaimid.so: main.go records.go gui.htpl
+${GOPATH}/bin/plugin_reclaimid.so: main.go records.go utility.go jwt.go 
gui.htpl
        go build -buildmode=plugin -trimpath -gcflags="-N -l" -o $@ ./...
diff --git a/reclaimid/gui.htpl b/reclaimid/gui.htpl
index 8317a72..b576279 100644
--- a/reclaimid/gui.htpl
+++ b/reclaimid/gui.htpl
@@ -1,111 +1,212 @@
-{{define "new_myrecords"}}
-    <!-- DEBUG: show template parameters -->
+{{define "new_reclaimid"}}
+    <div>{{template "logo"}}</div>
     <div>
-        <h3>Template data:</h3>
-       <p>Ref = {{.Ref}}</p>
-           <p>Action = {{.Action}}</p>
-           <p>Button = {{.Button}}</p>
-        <p>Names: {{range $i,$v := .Names}}'{{$v}}',{{end}}</p>
-        <p>RRSpecs:</p>
-        <ul>
-            {{range $i,$s := .RRspecs}}
-            <li>Type = {{$s.Type}}, Flags = {{$s.Flags}}</li>
-            {{end}}
-        </ul>
-        <p>Params:</p>
-        <ul>
-        {{range $k,$v := .Params}}
-            <li>'{{$k}}' = '{{$v}}'</li>
-        {{end}}
-        </ul>
-    </div>
-
-    {{$data := .}}
-    <div>
-        <h3>Creating a new custom resource record for label "{{index .Params 
"label"}}":</h3>
-        <div class="tabset">
-            {{range $i, $type := .RRspecs}}
-            <input type="radio" name="tabset" id="tab{{$i}}" 
aria-controls="tab{{$i}}" {{if eq $i 0}}checked{{end}}>
-            <label for="tab{{$i}}">{{rrtype $type.Type}}</label>
-            {{end}}
-            <div class="tab-panels">
-                {{range $i, $spec := .RRspecs}}
-                <section id="tab{{$i}}" class="tab-panel">
-                    {{$t := rrtype $spec.Type}}
-                    {{$pf := setspecs $data.Params $spec}}
-                    {{if eq $t "MYRECTYPE1"}}{{template "MYRECTYPE1" 
$data}}{{end}}
-                    {{if eq $t "MYRECTYPE2"}}{{template "MYRECTYPE2" 
$data}}{{end}}
-                </section>
-                {{end}}
-            </div>
-        </div>
+        <h2>Creating new records is not allowed</h2>
+        <p>
+            New records are created by using the appropriate tools provided by 
the
+            <a href="https://reclaimid.gitlab.io/"; target="_blank">re:claimID 
project</a>.
+        </p>
         <a href="/"><button>Leave</button></a>
     </div>
 {{end}}
 
-{{define "edit_myrecords"}}
-    <!-- DEBUG: show template parameters -->
+{{define "edit_reclaimid"}}
+    <div>{{template "logo"}}</div>
     <div>
-        <h3>Template data:</h3>
-       <p>Ref = {{.Ref}}</p>
-           <p>Action = {{.Action}}</p>
-           <p>Button = {{.Button}}</p>
-        <p>Names: {{range $i,$v := .Names}}'{{$v}}',{{end}}</p>
-        <ul>
-        {{range $k,$v := .Params}}
-            <li>'{{$k}}' = '{{$v}}'</li>
-        {{end}}
-        </ul>
-    </div>
-
-    <div>
-        <h3>Edit a resource record for label {{index .Params "label"}}:</h3>
-        <p><small>(Created: {{index .Params "created"}}, Last edited: {{index 
.Params "modified"}})</small></p>
-        {{$t := rritype (index .Params "type")}}
-        {{if eq $t "MYRECTYPE1"}}{{template "MYRECTYPE1" .}}{{end}}
-        {{if eq $t "MYRECTYPE2"}}{{template "MYRECTYPE2" .}}{{end}}
+        <h2>Editing records is not allowed</h2>
+        <p>
+            Existing records are managed by using the appropriate tools 
provided
+            by the <a href="https://reclaimid.gitlab.io/"; 
target="_blank">re:claimID
+            project</a>.
+        </p>
+        <a href="/"><button>Leave</button></a>
     </div>
-    <a href="/"><button>Leave</button></a>
-{{end}}
-
-{{define "MYRECTYPE1"}}
-    <h3>RECTYPE1</h3>
-    <form action="/action/{{.Action}}/rr/{{.Ref}}" {{if eq .Action 
"upd"}}method="post"{{end}}>
-        <input type="hidden" name="type" value="{{index .Params "type"}}">
-        <table>
-            <tr>
-                <td align="right"><b>Attribute1:</b></td>
-                <td>
-                    <input type="text" name="myrec1_text"
-                        maxlength="63" size="63"
-                        autofocus required
-                        value="{{index .Params "myrec1_text"}}"
-                    >
-                </td>
-            </tr>
-            {{template "RRCommon" .}}
-            <tr><td/><td><button id="submit">{{.Button}} 
record</button></td></tr>
-        </table>
-    </form>
 {{end}}
 
-{{define "MYRECTYPE2"}}
-    <h3>RECTYPE2</h3>
-    <form action="/action/{{.Action}}/rr/{{.Ref}}" {{if eq .Action 
"upd"}}method="post"{{end}}>
-        <input type="hidden" name="type" value="{{index .Params "type"}}">
-        <table>
-            <tr>
-                <td align="right"><b>Attribute1:</b></td>
-                <td>
-                    <input type="text" name="myrec2_text"
-                        maxlength="63" size="63"
-                        autofocus required
-                        value="{{index .Params "myrec2_text"}}"
-                    >
-                </td>
-            </tr>
-            {{template "RRCommon" .}}
-            <tr><td/><td><button id="submit">{{.Button}} 
record</button></td></tr>
-        </table>
-    </form>
+{{define "logo"}}
+<img src="data:image/png;base64,
+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
+IHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+
+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5
+bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBN
+TT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRw
+Oi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpk
+Yz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpleGlmPSJodHRw
+Oi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOkdJTVA9Imh0dHA6Ly93d3cuZ2lt
+cC5vcmcveG1wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAv
+IgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICB4bXBNTTpE
+b2N1bWVudElEPSJnaW1wOmRvY2lkOmdpbXA6YjQ1YjY3OTctMGQ3ZS00NjQ3LWEzNjUtZWMwYmZj
+MTJiNDcxIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjQ3YmQwMTBkLTE2NDYtNDEzNi1i
+OTU3LTAwNDkwYjAxNDNlMyIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmMz
+MTVkODI5LWRkNzUtNGI2OS1hNTgxLTI3YzZhNTA4NDk2NCIKICAgZGM6Rm9ybWF0PSJpbWFnZS9w
+bmciCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjgzMCIK
+ICAgZXhpZjpQaXhlbFlEaW1lbnNpb249IjE0MyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQ
+bGF0Zm9ybT0iTGludXgiCiAgIEdJTVA6VGltZVN0YW1wPSIxNjY4MTE0MTYzMTQyMDQ4IgogICBH
+SU1QOlZlcnNpb249IjIuMTAuMzIiCiAgIHRpZmY6Q29tcHJlc3Npb249IjEiCiAgIHRpZmY6T3Jp
+ZW50YXRpb249IjEiCiAgIHRpZmY6UGhvdG9tZXRyaWNJbnRlcnByZXRhdGlvbj0iMiIKICAgdGlm
+ZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiCiAgIHht
+cDpNZXRhZGF0YURhdGU9IjIwMjI6MTE6MTBUMjI6MDI6NDMrMDE6MDAiCiAgIHhtcDpNb2RpZnlE
+YXRlPSIyMDIyOjExOjEwVDIyOjAyOjQzKzAxOjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8
+cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0
+RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6OTc0ZWY2MjQt
+ZDU5OS00MDc3LTg0ZmEtNmQ0NmEyOTNkMjM2IgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJH
+aW1wIDIuMTAgKExpbnV4KSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0xMS0xMFQyMjowMjo0Mysw
+MTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3Jp
+cHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAK
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz7hZgNtAAAABmJL
+R0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5gsKFQIrkMN5EwAAE2pJ
+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"/>
 {{end}}
diff --git a/example-c/plugin.h b/reclaimid/jwt.go
similarity index 52%
copy from example-c/plugin.h
copy to reclaimid/jwt.go
index f73e43b..411e673 100644
--- a/example-c/plugin.h
+++ b/reclaimid/jwt.go
@@ -20,25 +20,36 @@
 //
 // SPDX-License-Identifier: AGPL3.0-or-later
 
-#ifndef _PLUGIN_H
-#define _PLUGIN_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <arpa/inet.h>
-
-int can_handle(uint32_t*);
-
-int compatible(const char* label, uint32_t* in, uint32_t* out);
-
-const char* prefix(uint32_t rr);
-
-char* value(uint32_t rrType, char* rrData);
-
-int to_map(uint32_t rrType, char* rrData, char** params);
-
-int from_map(uint32_t rrType, char* params, char** rrData);
-
-#endif
+package main
+
+import (
+       "encoding/base64"
+       "fmt"
+       "strings"
+)
+
+//----------------------------------------------------------------------
+// JSON Web Token (JWT) handling
+//----------------------------------------------------------------------
+
+func decodeJWT(in string) string {
+       parts := strings.Split(in, ".")
+       if len(parts) != 3 {
+               return "(invalid:parts)"
+       }
+       var s1, s2, s3 []byte
+       var err error
+       if s1, err = base64.StdEncoding.DecodeString(parts[0]); err != nil {
+               return "(invalid:header)"
+       }
+       jwt := fmt.Sprintf("<br>&nbsp;&nbsp;Header: %s", string(s1))
+       if s2, err = base64.StdEncoding.DecodeString(parts[1]); err != nil {
+               return "(invalid:payload)"
+       }
+       jwt += fmt.Sprintf("<br>&nbsp;&nbsp;Payload: %s<br>", string(s2))
+
+       if s3, err = base64.StdEncoding.DecodeString(parts[2]); err != nil {
+               return "(invalid:signature)"
+       }
+       return jwt + fmt.Sprintf("<br>&nbsp;&nbsp;Signature: %s<br>", 
split(string(s3), "&nbsp;&nbsp;", 64))
+}
diff --git a/reclaimid/main.go b/reclaimid/main.go
index 34db340..fb4f178 100644
--- a/reclaimid/main.go
+++ b/reclaimid/main.go
@@ -93,9 +93,6 @@ func (p *CustomPlugin) Prefix(t uint32) string {
 // No need to customize code beond this point...
 //======================================================================
 
-// Utility function
-type Utility func(fcn string, args ...any) any
-
 // Plugin is the instance of a custom implementation accessed by the
 // gnunet-go framework
 var Plugin = NewCustomPlugin()
diff --git a/reclaimid/plugin_test.go b/reclaimid/plugin_test.go
index 2a4f2fa..3e28a4a 100644
--- a/reclaimid/plugin_test.go
+++ b/reclaimid/plugin_test.go
@@ -23,6 +23,7 @@
 package main
 
 import (
+       "encoding/hex"
        "testing"
 )
 
@@ -117,3 +118,12 @@ func TestPluginFromMap(t *testing.T) {
                }
        */
 }
+
+func TestPluginAttributeEncoding(t *testing.T) {
+       s := 
"0000008000001T5BYEPR4CBE0T83PG94YV19T571YSBZT9VWMGNZ56FW3KV5V5BZ000000000000000000000000000000000000000000000000000000000M00000KCNPP2TBCC9S6CG38DXMJTW3FDHP6YT9EDXS6E00"
+       buf, err := decodeBase32GNS(s)
+       if err != nil {
+               t.Error(err)
+       }
+       t.Log(hex.EncodeToString(buf))
+}
diff --git a/reclaimid/records.go b/reclaimid/records.go
index f08f04d..1f88a53 100644
--- a/reclaimid/records.go
+++ b/reclaimid/records.go
@@ -24,10 +24,7 @@ package main
 
 import (
        "bytes"
-       "encoding/base32"
-       "encoding/hex"
        "fmt"
-       "strconv"
        "strings"
 
        "github.com/bfix/gospel/data"
@@ -69,37 +66,26 @@ func GetInstance(t uint32, buf []byte) (rec ReclaimRecord, 
err error) {
 
 // ReclaimAttribute record
 type ReclaimAttribute struct {
-       Type       uint32 `order:"big"`    // type
-       Flags      uint32 `order:"big"`    // claim
-       ID         []byte `size:"32"`      // Reclaim identifier
-       Credential []byte `size:"32"`      // Reclaim identifier
-       NameLen    uint16 `order:"big"`    // length of name
-       Res1       uint16 `order:"big"`    // reserved
-       DataLen    uint16 `order:"big"`    // length of data
-       Res2       uint16 `order:"big"`    // reserved
-       Name       []byte `size:"NameLen"` // name
-       Data       []byte `size:"DataLen"` // binary data
+       Type        uint32 `order:"big"`    // type
+       Flags       uint32 `order:"big"`    // flags
+       ID          []byte `size:"32"`      // Reclaim identifier
+       Attestation []byte `size:"32"`      // Attestation
+       NameLen     uint32 `order:"big"`    // length of name
+       DataLen     uint32 `order:"big"`    // length of data
+       Name        []byte `size:"NameLen"` // name
+       Data        []byte `size:"DataLen"` // binary data
 }
 
 // Value returns a human-readable representation of the record
 func (rec *ReclaimAttribute) Value(inv Utility) (string, error) {
        wrt := new(bytes.Buffer)
        wrt.WriteString(fmt.Sprintf("ID=%s,<br>", encodeBase32GNS(rec.ID)))
-       cred := encodeBase32GNS(rec.Credential)
+       cred := encodeBase32GNS(rec.Attestation)
        if len(strings.ReplaceAll(cred, "0", "")) > 0 {
                wrt.WriteString(fmt.Sprintf("Credential=%s,<br>", cred))
        }
-       var ok bool
-       var rtype string
-       if inv != nil {
-               rtype, ok = inv("gns_type_name", rec.Type).(string)
-       }
-       if ok {
-               wrt.WriteString(fmt.Sprintf("Type=%s,<br>", rtype))
-       } else {
-               wrt.WriteString(fmt.Sprintf("Type=%d,<br>", rec.Type))
-       }
-       wrt.WriteString(fmt.Sprintf("Flags=%d,<br>", rec.Flags))
+       wrt.WriteString(fmt.Sprintf("Type=%s,<br>", reclaimType(rec.Type)))
+       wrt.WriteString(fmt.Sprintf("Flags=%s,<br>", reclaimFlags(rec.Flags)))
        name := string(rec.Name)
        val := string(rec.Data)[:rec.DataLen-1]
        wrt.WriteString(fmt.Sprintf("[%s=%s]", name, val))
@@ -110,7 +96,7 @@ func (rec *ReclaimAttribute) Value(inv Utility) (string, 
error) {
 func (rec *ReclaimAttribute) ToMap(inv Utility) (params map[string]string, err 
error) {
        params = make(map[string]string)
        params["reclaim_attribute_id"] = encodeBase32GNS(rec.ID)
-       params["reclaim_attribute_credential"] = encodeBase32GNS(rec.Credential)
+       params["reclaim_attribute_credential"] = 
encodeBase32GNS(rec.Attestation)
        params["reclaim_attribute_type"] = fmt.Sprintf("%d", rec.Type)
        params["reclaim_attribute_flags"] = fmt.Sprintf("%d", rec.Flags)
        params["reclaim_attribute_key"] = string(rec.Name)
@@ -144,11 +130,9 @@ type ReclaimCredential struct {
 func (rec *ReclaimCredential) Value(inv Utility) (string, error) {
        wrt := new(bytes.Buffer)
        wrt.WriteString(fmt.Sprintf("ID=%s,<br>", encodeBase32GNS(rec.ID)))
-       wrt.WriteString(fmt.Sprintf("Type=%d,<br>", rec.Type))
-       wrt.WriteString(fmt.Sprintf("Flags=%d,<br>", rec.Flags))
-       name := string(rec.Name)
-       val := hex.EncodeToString(rec.Data)
-       wrt.WriteString(fmt.Sprintf("[%s=%s]", name, split(val, 64)))
+       wrt.WriteString(fmt.Sprintf("Type=%s,<br>", reclaimType(rec.Type)))
+       wrt.WriteString(fmt.Sprintf("Flags=%s,<br>", reclaimFlags(rec.Flags)))
+       wrt.WriteString(fmt.Sprintf("[%s=%s]", string(rec.Name), 
decodeJWT(string(rec.Data))))
        return wrt.String(), nil
 }
 
@@ -159,7 +143,7 @@ func (rec *ReclaimCredential) ToMap(inv Utility) (params 
map[string]string, err
        params["reclaim_credential_type"] = fmt.Sprintf("%d", rec.Type)
        params["reclaim_credential_flags"] = fmt.Sprintf("%d", rec.Flags)
        params["reclaim_credential_key"] = string(rec.Name)
-       params["reclaim_credential_value"] = hex.EncodeToString(rec.Data)
+       params["reclaim_credential_value"] = string(rec.Data)
        return
 }
 
@@ -169,52 +153,33 @@ func (rec *ReclaimCredential) FromMap(inv Utility, params 
map[string]string) err
 }
 
 //----------------------------------------------------------------------
-// Helpers
+// Reclaim types and flags
 //----------------------------------------------------------------------
 
-// Split a string into lines of given length
-func split(s string, chunk int) string {
-       out := ""
-       for len(s) > chunk {
-               if len(out) > 0 {
-                       out += ",<br>"
-               }
-               out += s[:chunk]
-               s = s[chunk:]
-       }
-       return out
-}
-
-// Base32GNS encoding character set
-var cf32Enc = base32.NewEncoding("0123456789ABCDEFGHJKMNPQRSTVWXYZ")
-
-// Return a byte array encoded with Base32GNS
-func encodeBase32GNS(buf []byte) string {
-       return strings.TrimRight(cf32Enc.EncodeToString(buf), "=")
-}
-
-// Convert string to byte array using Base32GNS
-func decodeBase32GNS(s string) ([]byte, error) {
-       s = strings.ToUpper(s)
-       s = strings.NewReplacer("O", "0", "I", "1", "L", "1", "U", 
"V").Replace(s)
-       return cf32Enc.DecodeString(s)
-}
+// Reclaim types and flags
+//
+//nolint:stylecheck // my style...
+const (
+       RECLAIM_TYPE_STRING = 1
+)
 
-// Numbers convertable from parameter value (string)
-type Number interface {
-       uint16 | uint32 | int | int16 | int32 | float32 | float64
+// get the reclaim type name
+func reclaimType(t uint32) string {
+       switch t {
+       case RECLAIM_TYPE_STRING:
+               return "STRING"
+       }
+       return "UNKNOWN"
 }
 
-// convert parameter value (string) to given number type
-func asInt[T Number](params map[string]string, key string) (val T, err error) {
-       vs, ok := params[key]
-       if !ok {
-               err = fmt.Errorf("%s missing", key)
-               return
+// get reclaim flags as names
+func reclaimFlags(f uint32) string {
+       if f == 0 {
+               return "None"
        }
-       var v int
-       if v, err = strconv.Atoi(vs); err != nil {
-               return
+       s := fmt.Sprintf("%b", f)
+       for len(s) < 32 {
+               s = "0" + s
        }
-       return T(v), nil
+       return strings.TrimRight(s, "0")
 }
diff --git a/reclaimid/utility.go b/reclaimid/utility.go
new file mode 100644
index 0000000..b91bdf7
--- /dev/null
+++ b/reclaimid/utility.go
@@ -0,0 +1,106 @@
+// This file is part of gnunet-go-plugins, a collection of plugins for
+// the GNUnet-implementation in Golang (gnunet.go). Plugins are used to
+// implement type-specific processing of resource records (e.g. GUI).
+// Copyright (C) 2022 by the authors:
+//
+// * Bernd Fix <brf@hoi-polloi.org>  >Y<
+//
+// gnunet-go-plugins is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// gnunet-go-plugons is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package main
+
+import (
+       "encoding/base32"
+       "fmt"
+       "strconv"
+       "strings"
+)
+
+// Utility function for exported ZoneMaster utility helpers
+type Utility func(fcn string, args ...any) any
+
+// GNSType returns the name of a GNSType
+func (u Utility) GNSType(t uint32) string {
+       rtype, ok := u("gns_type_name", t).(string)
+       if !ok {
+               return fmt.Sprintf("GNSType(%d)", t)
+       }
+       return rtype
+}
+
+// GNSFlags returns the list of GNS flags by name
+func (u Utility) GNSFlags(f uint32) string {
+       flags, ok := u("gns_flags", f).([]string)
+       if !ok {
+               return fmt.Sprintf("GNSFlags(%d)", f)
+       }
+       if len(flags) == 0 {
+               return "None"
+       }
+       return strings.Join(flags, ",")
+}
+
+// Base32GNS encoding character set
+var cf32Enc = base32.NewEncoding("0123456789ABCDEFGHJKMNPQRSTVWXYZ")
+
+// Return a byte array encoded with Base32GNS
+func encodeBase32GNS(buf []byte) string {
+       return strings.TrimRight(cf32Enc.EncodeToString(buf), "=")
+}
+
+// Convert string to byte array using Base32GNS
+func decodeBase32GNS(s string) ([]byte, error) {
+       s = strings.ToUpper(s)
+       s = strings.NewReplacer("O", "0", "I", "1", "L", "1", "U", 
"V").Replace(s)
+       return cf32Enc.DecodeString(s)
+}
+
+// Numbers convertable from parameter value (string)
+type Number interface {
+       uint16 | uint32 | int | int16 | int32 | float32 | float64
+}
+
+// convert parameter value (string) to given number type
+//
+//nolint:deadcode // might be used, might be not...
+func asInt[T Number](params map[string]string, key string) (val T, err error) {
+       vs, ok := params[key]
+       if !ok {
+               err = fmt.Errorf("%s missing", key)
+               return
+       }
+       var v int
+       if v, err = strconv.Atoi(vs); err != nil {
+               return
+       }
+       return T(v), nil
+}
+
+// Split a string into lines of given length
+func split(s, prefix string, chunk int) (out string) {
+       for len(s) > chunk {
+               if len(out) > 0 {
+                       out += ",<br>"
+               }
+               out += prefix + s[:chunk]
+               s = s[chunk:]
+       }
+       if len(out) > 0 {
+               out += ",<br>"
+       }
+       out += prefix + s
+       return
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]