Do not use ioutil as it is deprecated (#2625)

This commit is contained in:
Neil Alexander 2022-08-05 10:26:59 +01:00 committed by GitHub
parent 1b7f84250a
commit c8935fb53f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 52 additions and 60 deletions

View file

@ -18,7 +18,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
@ -34,7 +33,7 @@ import (
// If the final return value is non-nil, an error occurred and the cleanup function // If the final return value is non-nil, an error occurred and the cleanup function
// is nil. // is nil.
func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.UserLoginAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) { func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.UserLoginAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) {
reqBytes, err := ioutil.ReadAll(r) reqBytes, err := io.ReadAll(r)
if err != nil { if err != nil {
err := &util.JSONResponse{ err := &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,

View file

@ -16,7 +16,7 @@ package httputil
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io"
"net/http" "net/http"
"unicode/utf8" "unicode/utf8"
@ -29,9 +29,9 @@ import (
func UnmarshalJSONRequest(req *http.Request, iface interface{}) *util.JSONResponse { func UnmarshalJSONRequest(req *http.Request, iface interface{}) *util.JSONResponse {
// encoding/json allows invalid utf-8, matrix does not // encoding/json allows invalid utf-8, matrix does not
// https://matrix.org/docs/spec/client_server/r0.6.1#api-standards // https://matrix.org/docs/spec/client_server/r0.6.1#api-standards
body, err := ioutil.ReadAll(req.Body) body, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("ioutil.ReadAll failed") util.GetLogger(req.Context()).WithError(err).Error("io.ReadAll failed")
resp := jsonerror.InternalServerError() resp := jsonerror.InternalServerError()
return &resp return &resp
} }

View file

@ -17,7 +17,7 @@ package routing
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
@ -101,9 +101,9 @@ func SaveAccountData(
} }
} }
body, err := ioutil.ReadAll(req.Body) body, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("ioutil.ReadAll failed") util.GetLogger(req.Context()).WithError(err).Error("io.ReadAll failed")
return jsonerror.InternalServerError() return jsonerror.InternalServerError()
} }

View file

@ -1,7 +1,7 @@
package routing package routing
import ( import (
"io/ioutil" "io"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/auth" "github.com/matrix-org/dendrite/clientapi/auth"
@ -20,7 +20,7 @@ func Deactivate(
) util.JSONResponse { ) util.JSONResponse {
ctx := req.Context() ctx := req.Context()
defer req.Body.Close() // nolint:errcheck defer req.Body.Close() // nolint:errcheck
bodyBytes, err := ioutil.ReadAll(req.Body) bodyBytes, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,

View file

@ -15,7 +15,7 @@
package routing package routing
import ( import (
"io/ioutil" "io"
"net" "net"
"net/http" "net/http"
@ -175,7 +175,7 @@ func DeleteDeviceById(
}() }()
ctx := req.Context() ctx := req.Context()
defer req.Body.Close() // nolint:errcheck defer req.Body.Close() // nolint:errcheck
bodyBytes, err := ioutil.ReadAll(req.Body) bodyBytes, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,

View file

@ -19,7 +19,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"regexp" "regexp"
@ -371,7 +371,7 @@ func validateRecaptcha(
// Grab the body of the response from the captcha server // Grab the body of the response from the captcha server
var r recaptchaResponse var r recaptchaResponse
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusGatewayTimeout, Code: http.StatusGatewayTimeout,
@ -539,7 +539,7 @@ func Register(
cfg *config.ClientAPI, cfg *config.ClientAPI,
) util.JSONResponse { ) util.JSONResponse {
defer req.Body.Close() // nolint: errcheck defer req.Body.Close() // nolint: errcheck
reqBody, err := ioutil.ReadAll(req.Body) reqBody, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,

View file

@ -2,7 +2,7 @@ package routing
import ( import (
"bytes" "bytes"
"io/ioutil" "io"
"testing" "testing"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
@ -13,7 +13,7 @@ func TestSharedSecretRegister(t *testing.T) {
jsonStr := []byte(`{"admin":false,"mac":"f1ba8d37123866fd659b40de4bad9b0f8965c565","nonce":"759f047f312b99ff428b21d581256f8592b8976e58bc1b543972dc6147e529a79657605b52d7becd160ff5137f3de11975684319187e06901955f79e5a6c5a79","password":"wonderland","username":"alice"}`) jsonStr := []byte(`{"admin":false,"mac":"f1ba8d37123866fd659b40de4bad9b0f8965c565","nonce":"759f047f312b99ff428b21d581256f8592b8976e58bc1b543972dc6147e529a79657605b52d7becd160ff5137f3de11975684319187e06901955f79e5a6c5a79","password":"wonderland","username":"alice"}`)
sharedSecret := "dendritetest" sharedSecret := "dendritetest"
req, err := NewSharedSecretRegistrationRequest(ioutil.NopCloser(bytes.NewBuffer(jsonStr))) req, err := NewSharedSecretRegistrationRequest(io.NopCloser(bytes.NewBuffer(jsonStr)))
if err != nil { if err != nil {
t.Fatalf("failed to read request: %s", err) t.Fatalf("failed to read request: %s", err)
} }

View file

@ -19,7 +19,6 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"regexp" "regexp"
"strings" "strings"
@ -157,7 +156,7 @@ func main() {
func getPassword(password, pwdFile string, pwdStdin bool, r io.Reader) (string, error) { func getPassword(password, pwdFile string, pwdStdin bool, r io.Reader) (string, error) {
// read password from file // read password from file
if pwdFile != "" { if pwdFile != "" {
pw, err := ioutil.ReadFile(pwdFile) pw, err := os.ReadFile(pwdFile)
if err != nil { if err != nil {
return "", fmt.Errorf("Unable to read password from file: %v", err) return "", fmt.Errorf("Unable to read password from file: %v", err)
} }
@ -166,7 +165,7 @@ func getPassword(password, pwdFile string, pwdStdin bool, r io.Reader) (string,
// read password from stdin // read password from stdin
if pwdStdin { if pwdStdin {
data, err := ioutil.ReadAll(r) data, err := io.ReadAll(r)
if err != nil { if err != nil {
return "", fmt.Errorf("Unable to read password from stdin: %v", err) return "", fmt.Errorf("Unable to read password from stdin: %v", err)
} }

View file

@ -21,7 +21,6 @@ import (
"encoding/hex" "encoding/hex"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -76,11 +75,11 @@ func main() {
if pk, sk, err = ed25519.GenerateKey(nil); err != nil { if pk, sk, err = ed25519.GenerateKey(nil); err != nil {
panic(err) panic(err)
} }
if err = ioutil.WriteFile(keyfile, sk, 0644); err != nil { if err = os.WriteFile(keyfile, sk, 0644); err != nil {
panic(err) panic(err)
} }
} else if err == nil { } else if err == nil {
if sk, err = ioutil.ReadFile(keyfile); err != nil { if sk, err = os.ReadFile(keyfile); err != nil {
panic(err) panic(err)
} }
if len(sk) != ed25519.PrivateKeySize { if len(sk) != ed25519.PrivateKeySize {

View file

@ -20,7 +20,6 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"net" "net"
"os" "os"
@ -69,7 +68,7 @@ func Setup(instanceName, storageDirectory, peerURI string) (*Node, error) {
yggfile := fmt.Sprintf("%s/%s-yggdrasil.conf", storageDirectory, instanceName) yggfile := fmt.Sprintf("%s/%s-yggdrasil.conf", storageDirectory, instanceName)
if _, err := os.Stat(yggfile); !os.IsNotExist(err) { if _, err := os.Stat(yggfile); !os.IsNotExist(err) {
yggconf, e := ioutil.ReadFile(yggfile) yggconf, e := os.ReadFile(yggfile)
if e != nil { if e != nil {
panic(err) panic(err)
} }
@ -88,7 +87,7 @@ func Setup(instanceName, storageDirectory, peerURI string) (*Node, error) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
if e := ioutil.WriteFile(yggfile, j, 0600); e != nil { if e := os.WriteFile(yggfile, j, 0600); e != nil {
n.log.Printf("Couldn't write private key to file '%s': %s\n", yggfile, e) n.log.Printf("Couldn't write private key to file '%s': %s\n", yggfile, e)
} }

View file

@ -6,7 +6,7 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -128,7 +128,7 @@ func downloadArchive(cli *http.Client, tmpDir, archiveURL string, dockerfile []b
return nil, err return nil, err
} }
// add top level Dockerfile // add top level Dockerfile
err = ioutil.WriteFile(path.Join(tmpDir, "Dockerfile"), dockerfile, os.ModePerm) err = os.WriteFile(path.Join(tmpDir, "Dockerfile"), dockerfile, os.ModePerm)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to inject /Dockerfile: %w", err) return nil, fmt.Errorf("failed to inject /Dockerfile: %w", err)
} }
@ -150,7 +150,7 @@ func buildDendrite(httpClient *http.Client, dockerClient *client.Client, tmpDir,
if branchOrTagName == HEAD && *flagHead != "" { if branchOrTagName == HEAD && *flagHead != "" {
log.Printf("%s: Using %s as HEAD", branchOrTagName, *flagHead) log.Printf("%s: Using %s as HEAD", branchOrTagName, *flagHead)
// add top level Dockerfile // add top level Dockerfile
err = ioutil.WriteFile(path.Join(*flagHead, "Dockerfile"), []byte(Dockerfile), os.ModePerm) err = os.WriteFile(path.Join(*flagHead, "Dockerfile"), []byte(Dockerfile), os.ModePerm)
if err != nil { if err != nil {
return "", fmt.Errorf("custom HEAD: failed to inject /Dockerfile: %w", err) return "", fmt.Errorf("custom HEAD: failed to inject /Dockerfile: %w", err)
} }
@ -388,7 +388,7 @@ func runImage(dockerClient *client.Client, volumeName, version, imageID string)
}) })
// ignore errors when cannot get logs, it's just for debugging anyways // ignore errors when cannot get logs, it's just for debugging anyways
if err == nil { if err == nil {
logbody, err := ioutil.ReadAll(logs) logbody, err := io.ReadAll(logs)
if err == nil { if err == nil {
log.Printf("Container logs:\n\n%s\n\n", string(logbody)) log.Printf("Container logs:\n\n%s\n\n", string(logbody))
} }

View file

@ -9,7 +9,6 @@ import (
"encoding/pem" "encoding/pem"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"net/url" "net/url"
"os" "os"
@ -30,7 +29,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
data, err := ioutil.ReadFile(*requestKey) data, err := os.ReadFile(*requestKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -6,7 +6,7 @@ import (
"crypto/ed25519" "crypto/ed25519"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"os" "os"
"testing" "testing"
@ -66,7 +66,7 @@ func TestMain(m *testing.M) {
s.cache = caching.NewRistrettoCache(8*1024*1024, time.Hour, false) s.cache = caching.NewRistrettoCache(8*1024*1024, time.Hour, false)
// Create a temporary directory for JetStream. // Create a temporary directory for JetStream.
d, err := ioutil.TempDir("./", "jetstream*") d, err := os.MkdirTemp("./", "jetstream*")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -136,7 +136,7 @@ func (m *MockRoundTripper) RoundTrip(req *http.Request) (res *http.Response, err
// And respond. // And respond.
res = &http.Response{ res = &http.Response{
StatusCode: 200, StatusCode: 200,
Body: ioutil.NopCloser(bytes.NewReader(body)), Body: io.NopCloser(bytes.NewReader(body)),
} }
return return
} }

View file

@ -18,7 +18,7 @@
package internal package internal
import ( import (
"io/ioutil" "io"
"log/syslog" "log/syslog"
"github.com/MFAshby/stdemuxerhook" "github.com/MFAshby/stdemuxerhook"
@ -63,7 +63,7 @@ func SetupHookLogging(hooks []config.LogrusHook, componentName string) {
setupStdLogHook(logrus.InfoLevel) setupStdLogHook(logrus.InfoLevel)
} }
// Hooks are now configured for stdout/err, so throw away the default logger output // Hooks are now configured for stdout/err, so throw away the default logger output
logrus.SetOutput(ioutil.Discard) logrus.SetOutput(io.Discard)
} }
func checkSyslogHookParams(params map[string]interface{}) { func checkSyslogHookParams(params map[string]interface{}) {

View file

@ -18,7 +18,7 @@ import (
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
@ -203,7 +203,7 @@ func TestUpdateNoPrevID(t *testing.T) {
} }
return &http.Response{ return &http.Response{
StatusCode: 200, StatusCode: 200,
Body: ioutil.NopCloser(strings.NewReader(` Body: io.NopCloser(strings.NewReader(`
{ {
"user_id": "` + remoteUserID + `", "user_id": "` + remoteUserID + `",
"stream_id": 5, "stream_id": 5,
@ -318,7 +318,7 @@ func TestDebounce(t *testing.T) {
// now send the response over federation // now send the response over federation
fedCh <- &http.Response{ fedCh <- &http.Response{
StatusCode: 200, StatusCode: 200,
Body: ioutil.NopCloser(strings.NewReader(` Body: io.NopCloser(strings.NewReader(`
{ {
"user_id": "` + userID + `", "user_id": "` + userID + `",
"stream_id": 5, "stream_id": 5,

View file

@ -21,7 +21,6 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -180,7 +179,7 @@ func createTempDir(baseDirectory config.Path) (types.Path, error) {
if err := os.MkdirAll(baseTmpDir, 0770); err != nil { if err := os.MkdirAll(baseTmpDir, 0770); err != nil {
return "", fmt.Errorf("failed to create base temp dir: %w", err) return "", fmt.Errorf("failed to create base temp dir: %w", err)
} }
tmpDir, err := ioutil.TempDir(baseTmpDir, "") tmpDir, err := os.MkdirTemp(baseTmpDir, "")
if err != nil { if err != nil {
return "", fmt.Errorf("failed to create temp dir: %w", err) return "", fmt.Errorf("failed to create temp dir: %w", err)
} }

View file

@ -19,7 +19,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"mime" "mime"
"net/http" "net/http"
"net/url" "net/url"
@ -695,7 +694,7 @@ func (r *downloadRequest) GetContentLengthAndReader(contentLengthHeader string,
// We successfully parsed the Content-Length, so we'll return a limited // We successfully parsed the Content-Length, so we'll return a limited
// reader that restricts us to reading only up to this size. // reader that restricts us to reading only up to this size.
reader = ioutil.NopCloser(io.LimitReader(*body, parsedLength)) reader = io.NopCloser(io.LimitReader(*body, parsedLength))
contentLength = parsedLength contentLength = parsedLength
} else { } else {
// Content-Length header is missing. If we have a maximum file size // Content-Length header is missing. If we have a maximum file size
@ -704,7 +703,7 @@ func (r *downloadRequest) GetContentLengthAndReader(contentLengthHeader string,
// ultimately it will get rewritten later when the temp file is written // ultimately it will get rewritten later when the temp file is written
// to disk. // to disk.
if maxFileSizeBytes > 0 { if maxFileSizeBytes > 0 {
reader = ioutil.NopCloser(io.LimitReader(*body, int64(maxFileSizeBytes))) reader = io.NopCloser(io.LimitReader(*body, int64(maxFileSizeBytes)))
} }
contentLength = 0 contentLength = 0
} }

View file

@ -19,8 +19,8 @@ import (
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/url" "net/url"
"os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
@ -191,7 +191,7 @@ type ConfigErrors []string
// Load a yaml config file for a server run as multiple processes or as a monolith. // Load a yaml config file for a server run as multiple processes or as a monolith.
// Checks the config to ensure that it is valid. // Checks the config to ensure that it is valid.
func Load(configPath string, monolith bool) (*Dendrite, error) { func Load(configPath string, monolith bool) (*Dendrite, error) {
configData, err := ioutil.ReadFile(configPath) configData, err := os.ReadFile(configPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -199,9 +199,9 @@ func Load(configPath string, monolith bool) (*Dendrite, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Pass the current working directory and ioutil.ReadFile so that they can // Pass the current working directory and os.ReadFile so that they can
// be mocked in the tests // be mocked in the tests
return loadConfig(basePath, configData, ioutil.ReadFile, monolith) return loadConfig(basePath, configData, os.ReadFile, monolith)
} }
func loadConfig( func loadConfig(
@ -530,7 +530,7 @@ func (config *Dendrite) KeyServerURL() string {
// SetupTracing configures the opentracing using the supplied configuration. // SetupTracing configures the opentracing using the supplied configuration.
func (config *Dendrite) SetupTracing(serviceName string) (closer io.Closer, err error) { func (config *Dendrite) SetupTracing(serviceName string) (closer io.Closer, err error) {
if !config.Tracing.Enabled { if !config.Tracing.Enabled {
return ioutil.NopCloser(bytes.NewReader([]byte{})), nil return io.NopCloser(bytes.NewReader([]byte{})), nil
} }
return config.Tracing.Jaeger.InitGlobalTracer( return config.Tracing.Jaeger.InitGlobalTracer(
serviceName, serviceName,

View file

@ -16,7 +16,7 @@ package config
import ( import (
"fmt" "fmt"
"io/ioutil" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
@ -181,7 +181,7 @@ func loadAppServices(config *AppServiceAPI, derived *Derived) error {
} }
// Read the application service's config file // Read the application service's config file
configData, err := ioutil.ReadFile(absPath) configData, err := os.ReadFile(absPath)
if err != nil { if err != nil {
return err return err
} }

View file

@ -7,7 +7,7 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"io/ioutil" "io"
"net/http" "net/http"
"sort" "sort"
"strings" "strings"
@ -428,12 +428,12 @@ func postRelationships(t *testing.T, expectCode int, accessToken string, req *ms
t.Fatalf("failed to do request: %s", err) t.Fatalf("failed to do request: %s", err)
} }
if res.StatusCode != expectCode { if res.StatusCode != expectCode {
body, _ := ioutil.ReadAll(res.Body) body, _ := io.ReadAll(res.Body)
t.Fatalf("wrong response code, got %d want %d - body: %s", res.StatusCode, expectCode, string(body)) t.Fatalf("wrong response code, got %d want %d - body: %s", res.StatusCode, expectCode, string(body))
} }
if res.StatusCode == 200 { if res.StatusCode == 200 {
var result msc2836.EventRelationshipResponse var result msc2836.EventRelationshipResponse
body, err := ioutil.ReadAll(res.Body) body, err := io.ReadAll(res.Body)
if err != nil { if err != nil {
t.Fatalf("response 200 OK but failed to read response body: %s", err) t.Fatalf("response 200 OK but failed to read response body: %s", err)
} }

View file

@ -16,7 +16,7 @@ package routing
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io"
"net/http" "net/http"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
@ -88,7 +88,7 @@ func PutFilter(
var filter gomatrixserverlib.Filter var filter gomatrixserverlib.Filter
defer req.Body.Close() // nolint:errcheck defer req.Body.Close() // nolint:errcheck
body, err := ioutil.ReadAll(req.Body) body, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,

View file

@ -22,7 +22,6 @@ import (
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"math/big" "math/big"
"os" "os"
"strings" "strings"
@ -144,7 +143,7 @@ func NewTLSKeyWithAuthority(serverName, tlsKeyPath, tlsCertPath, authorityKeyPat
} }
// load the authority key // load the authority key
dat, err := ioutil.ReadFile(authorityKeyPath) dat, err := os.ReadFile(authorityKeyPath)
if err != nil { if err != nil {
return err return err
} }
@ -158,7 +157,7 @@ func NewTLSKeyWithAuthority(serverName, tlsKeyPath, tlsCertPath, authorityKeyPat
} }
// load the authority certificate // load the authority certificate
dat, err = ioutil.ReadFile(authorityCertPath) dat, err = os.ReadFile(authorityCertPath)
if err != nil { if err != nil {
return err return err
} }