v8.0 module release (#22975)
https://mattermost.atlassian.net/browse/MM-52079 ```release-note We upgrade the module version to 8.0. The new module path is github.com/mattermost-server/server/v8. ``` Co-authored-by: Doug Lauder <wiggin77@warpmail.net>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
831ea38f7e
Коммит
b200a07881
920
server/plugin/client_rpc.go
Обычный файл
920
server/plugin/client_rpc.go
Обычный файл
@@ -0,0 +1,920 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
//go:generate go run interface_generator/main.go
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/mattermost/mattermost-server/server/v8/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
var hookNameToId map[string]int = make(map[string]int)
|
||||
|
||||
type hooksRPCClient struct {
|
||||
client *rpc.Client
|
||||
log *mlog.Logger
|
||||
muxBroker *plugin.MuxBroker
|
||||
apiImpl API
|
||||
driver Driver
|
||||
implemented [TotalHooksID]bool
|
||||
doneWg sync.WaitGroup
|
||||
}
|
||||
|
||||
type hooksRPCServer struct {
|
||||
impl any
|
||||
muxBroker *plugin.MuxBroker
|
||||
apiRPCClient *apiRPCClient
|
||||
}
|
||||
|
||||
// Implements hashicorp/go-plugin/plugin.Plugin interface to connect the hooks of a plugin
|
||||
type hooksPlugin struct {
|
||||
hooks any
|
||||
apiImpl API
|
||||
driverImpl Driver
|
||||
log *mlog.Logger
|
||||
}
|
||||
|
||||
func (p *hooksPlugin) Server(b *plugin.MuxBroker) (any, error) {
|
||||
return &hooksRPCServer{impl: p.hooks, muxBroker: b}, nil
|
||||
}
|
||||
|
||||
func (p *hooksPlugin) Client(b *plugin.MuxBroker, client *rpc.Client) (any, error) {
|
||||
return &hooksRPCClient{client: client,
|
||||
log: p.log,
|
||||
muxBroker: b,
|
||||
apiImpl: p.apiImpl,
|
||||
driver: p.driverImpl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type apiRPCClient struct {
|
||||
client *rpc.Client
|
||||
muxBroker *plugin.MuxBroker
|
||||
}
|
||||
|
||||
type apiRPCServer struct {
|
||||
impl API
|
||||
muxBroker *plugin.MuxBroker
|
||||
}
|
||||
|
||||
// ErrorString is a fallback for sending unregistered implementations of the error interface across
|
||||
// rpc. For example, the errorString type from the github.com/pkg/errors package cannot be
|
||||
// registered since it is not exported, but this precludes common error handling paradigms.
|
||||
// ErrorString merely preserves the string description of the error, while satisfying the error
|
||||
// interface itself to allow other registered types (such as model.AppError) to be sent unmodified.
|
||||
type ErrorString struct {
|
||||
Code int // Code to map to various error variables
|
||||
Err string
|
||||
}
|
||||
|
||||
func (e ErrorString) Error() string {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func encodableError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if _, ok := err.(*model.AppError); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := err.(*pq.Error); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := err.(*mysql.MySQLError); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
ret := &ErrorString{
|
||||
Err: err.Error(),
|
||||
}
|
||||
|
||||
switch err {
|
||||
case io.EOF:
|
||||
ret.Code = 1
|
||||
case sql.ErrNoRows:
|
||||
ret.Code = 2
|
||||
case sql.ErrConnDone:
|
||||
ret.Code = 3
|
||||
case sql.ErrTxDone:
|
||||
ret.Code = 4
|
||||
case driver.ErrSkip:
|
||||
ret.Code = 5
|
||||
case driver.ErrBadConn:
|
||||
ret.Code = 6
|
||||
case driver.ErrRemoveArgument:
|
||||
ret.Code = 7
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func decodableError(err error) error {
|
||||
if encErr, ok := err.(*ErrorString); ok {
|
||||
switch encErr.Code {
|
||||
case 1:
|
||||
return io.EOF
|
||||
case 2:
|
||||
return sql.ErrNoRows
|
||||
case 3:
|
||||
return sql.ErrConnDone
|
||||
case 4:
|
||||
return sql.ErrTxDone
|
||||
case 5:
|
||||
return driver.ErrSkip
|
||||
case 6:
|
||||
return driver.ErrBadConn
|
||||
case 7:
|
||||
return driver.ErrRemoveArgument
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Registering some types used by MM for encoding/gob used by rpc
|
||||
func init() {
|
||||
gob.Register([]*model.SlackAttachment{})
|
||||
gob.Register([]any{})
|
||||
gob.Register(map[string]any{})
|
||||
gob.Register(&model.AppError{})
|
||||
gob.Register(&pq.Error{})
|
||||
gob.Register(&mysql.MySQLError{})
|
||||
gob.Register(&ErrorString{})
|
||||
gob.Register(&model.AutocompleteDynamicListArg{})
|
||||
gob.Register(&model.AutocompleteStaticListArg{})
|
||||
gob.Register(&model.AutocompleteTextArg{})
|
||||
gob.Register(&model.PreviewPost{})
|
||||
}
|
||||
|
||||
// These enforce compile time checks to make sure types implement the interface
|
||||
// If you are getting an error here, you probably need to run `make pluginapi` to
|
||||
// autogenerate RPC glue code
|
||||
var _ plugin.Plugin = &hooksPlugin{}
|
||||
var _ Hooks = &hooksRPCClient{}
|
||||
|
||||
//
|
||||
// Below are special cases for hooks or APIs that can not be auto generated
|
||||
//
|
||||
|
||||
func (g *hooksRPCClient) Implemented() (impl []string, err error) {
|
||||
err = g.client.Call("Plugin.Implemented", struct{}{}, &impl)
|
||||
for _, hookName := range impl {
|
||||
if hookId, ok := hookNameToId[hookName]; ok {
|
||||
g.implemented[hookId] = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Implemented replies with the names of the hooks that are implemented.
|
||||
func (s *hooksRPCServer) Implemented(args struct{}, reply *[]string) error {
|
||||
ifaceType := reflect.TypeOf((*Hooks)(nil)).Elem()
|
||||
implType := reflect.TypeOf(s.impl)
|
||||
selfType := reflect.TypeOf(s)
|
||||
var methods []string
|
||||
for i := 0; i < ifaceType.NumMethod(); i++ {
|
||||
method := ifaceType.Method(i)
|
||||
if m, ok := implType.MethodByName(method.Name); !ok {
|
||||
continue
|
||||
} else if m.Type.NumIn() != method.Type.NumIn()+1 {
|
||||
continue
|
||||
} else if m.Type.NumOut() != method.Type.NumOut() {
|
||||
continue
|
||||
} else {
|
||||
match := true
|
||||
for j := 0; j < method.Type.NumIn(); j++ {
|
||||
if m.Type.In(j+1) != method.Type.In(j) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
for j := 0; j < method.Type.NumOut(); j++ {
|
||||
if m.Type.Out(j) != method.Type.Out(j) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if _, ok := selfType.MethodByName(method.Name); !ok {
|
||||
continue
|
||||
}
|
||||
methods = append(methods, method.Name)
|
||||
}
|
||||
*reply = methods
|
||||
return encodableError(nil)
|
||||
}
|
||||
|
||||
type Z_OnActivateArgs struct {
|
||||
APIMuxId uint32
|
||||
DriverMuxId uint32
|
||||
}
|
||||
|
||||
type Z_OnActivateReturns struct {
|
||||
A error
|
||||
}
|
||||
|
||||
func (g *hooksRPCClient) OnActivate() error {
|
||||
muxId := g.muxBroker.NextId()
|
||||
g.doneWg.Add(1)
|
||||
go func() {
|
||||
defer g.doneWg.Done()
|
||||
g.muxBroker.AcceptAndServe(muxId, &apiRPCServer{
|
||||
impl: g.apiImpl,
|
||||
muxBroker: g.muxBroker,
|
||||
})
|
||||
}()
|
||||
|
||||
nextID := g.muxBroker.NextId()
|
||||
g.doneWg.Add(1)
|
||||
go func() {
|
||||
defer g.doneWg.Done()
|
||||
g.muxBroker.AcceptAndServe(nextID, &dbRPCServer{
|
||||
dbImpl: g.driver,
|
||||
})
|
||||
}()
|
||||
|
||||
_args := &Z_OnActivateArgs{
|
||||
APIMuxId: muxId,
|
||||
DriverMuxId: nextID,
|
||||
}
|
||||
_returns := &Z_OnActivateReturns{}
|
||||
|
||||
if err := g.client.Call("Plugin.OnActivate", _args, _returns); err != nil {
|
||||
g.log.Error("RPC call to OnActivate plugin failed.", mlog.Err(err))
|
||||
}
|
||||
return _returns.A
|
||||
}
|
||||
|
||||
func (s *hooksRPCServer) OnActivate(args *Z_OnActivateArgs, returns *Z_OnActivateReturns) error {
|
||||
connection, err := s.muxBroker.Dial(args.APIMuxId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn2, err := s.muxBroker.Dial(args.DriverMuxId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.apiRPCClient = &apiRPCClient{
|
||||
client: rpc.NewClient(connection),
|
||||
muxBroker: s.muxBroker,
|
||||
}
|
||||
|
||||
dbClient := &dbRPCClient{
|
||||
client: rpc.NewClient(conn2),
|
||||
}
|
||||
|
||||
if mmplugin, ok := s.impl.(interface {
|
||||
SetAPI(api API)
|
||||
SetDriver(driver Driver)
|
||||
}); ok {
|
||||
mmplugin.SetAPI(s.apiRPCClient)
|
||||
mmplugin.SetDriver(dbClient)
|
||||
}
|
||||
|
||||
if mmplugin, ok := s.impl.(interface {
|
||||
OnConfigurationChange() error
|
||||
}); ok {
|
||||
if err := mmplugin.OnConfigurationChange(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] call to OnConfigurationChange failed, error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Capture output of standard logger because go-plugin
|
||||
// redirects it.
|
||||
log.SetOutput(os.Stderr)
|
||||
|
||||
if hook, ok := s.impl.(interface {
|
||||
OnActivate() error
|
||||
}); ok {
|
||||
returns.A = encodableError(hook.OnActivate())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_LoadPluginConfigurationArgsArgs struct {
|
||||
}
|
||||
|
||||
type Z_LoadPluginConfigurationArgsReturns struct {
|
||||
A []byte
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) LoadPluginConfiguration(dest any) error {
|
||||
_args := &Z_LoadPluginConfigurationArgsArgs{}
|
||||
_returns := &Z_LoadPluginConfigurationArgsReturns{}
|
||||
if err := g.client.Call("Plugin.LoadPluginConfiguration", _args, _returns); err != nil {
|
||||
log.Printf("RPC call to LoadPluginConfiguration API failed: %s", err.Error())
|
||||
}
|
||||
if err := json.Unmarshal(_returns.A, dest); err != nil {
|
||||
log.Printf("LoadPluginConfiguration API failed to unmarshal: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) LoadPluginConfiguration(args *Z_LoadPluginConfigurationArgsArgs, returns *Z_LoadPluginConfigurationArgsReturns) error {
|
||||
var config any
|
||||
if hook, ok := s.impl.(interface {
|
||||
LoadPluginConfiguration(dest any) error
|
||||
}); ok {
|
||||
if err := hook.LoadPluginConfiguration(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
returns.A = b
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
hookNameToId["ServeHTTP"] = ServeHTTPID
|
||||
}
|
||||
|
||||
type Z_ServeHTTPArgs struct {
|
||||
ResponseWriterStream uint32
|
||||
Request *http.Request
|
||||
Context *Context
|
||||
RequestBodyStream uint32
|
||||
}
|
||||
|
||||
func (g *hooksRPCClient) ServeHTTP(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !g.implemented[ServeHTTPID] {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
serveHTTPStreamId := g.muxBroker.NextId()
|
||||
go func() {
|
||||
connection, err := g.muxBroker.Accept(serveHTTPStreamId)
|
||||
if err != nil {
|
||||
g.log.Error("Plugin failed to ServeHTTP, muxBroker couldn't accept connection", mlog.Uint32("serve_http_stream_id", serveHTTPStreamId), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
defer connection.Close()
|
||||
|
||||
rpcServer := rpc.NewServer()
|
||||
if err := rpcServer.RegisterName("Plugin", &httpResponseWriterRPCServer{w: w, log: g.log}); err != nil {
|
||||
g.log.Error("Plugin failed to ServeHTTP, couldn't register RPC name", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
rpcServer.ServeConn(connection)
|
||||
}()
|
||||
|
||||
requestBodyStreamId := uint32(0)
|
||||
if r.Body != nil {
|
||||
requestBodyStreamId = g.muxBroker.NextId()
|
||||
go func() {
|
||||
bodyConnection, err := g.muxBroker.Accept(requestBodyStreamId)
|
||||
if err != nil {
|
||||
g.log.Error("Plugin failed to ServeHTTP, muxBroker couldn't Accept request body connection", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
defer bodyConnection.Close()
|
||||
serveIOReader(r.Body, bodyConnection)
|
||||
}()
|
||||
}
|
||||
|
||||
forwardedRequest := &http.Request{
|
||||
Method: r.Method,
|
||||
URL: r.URL,
|
||||
Proto: r.Proto,
|
||||
ProtoMajor: r.ProtoMajor,
|
||||
ProtoMinor: r.ProtoMinor,
|
||||
Header: r.Header,
|
||||
Host: r.Host,
|
||||
RemoteAddr: r.RemoteAddr,
|
||||
RequestURI: r.RequestURI,
|
||||
}
|
||||
|
||||
if err := g.client.Call("Plugin.ServeHTTP", Z_ServeHTTPArgs{
|
||||
Context: c,
|
||||
ResponseWriterStream: serveHTTPStreamId,
|
||||
Request: forwardedRequest,
|
||||
RequestBodyStream: requestBodyStreamId,
|
||||
}, nil); err != nil {
|
||||
g.log.Error("Plugin failed to ServeHTTP, RPC call failed", mlog.Err(err))
|
||||
http.Error(w, "500 internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *hooksRPCServer) ServeHTTP(args *Z_ServeHTTPArgs, returns *struct{}) error {
|
||||
connection, err := s.muxBroker.Dial(args.ResponseWriterStream)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote response writer stream, error: %v", err.Error())
|
||||
return err
|
||||
}
|
||||
w := connectHTTPResponseWriter(connection)
|
||||
defer w.Close()
|
||||
|
||||
r := args.Request
|
||||
if args.RequestBodyStream != 0 {
|
||||
connection, err := s.muxBroker.Dial(args.RequestBodyStream)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote request body stream, error: %v", err.Error())
|
||||
return err
|
||||
}
|
||||
r.Body = connectIOReader(connection)
|
||||
} else {
|
||||
r.Body = io.NopCloser(&bytes.Buffer{})
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if hook, ok := s.impl.(interface {
|
||||
ServeHTTP(c *Context, w http.ResponseWriter, r *http.Request)
|
||||
}); ok {
|
||||
hook.ServeHTTP(args.Context, w, r)
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_PluginHTTPArgs struct {
|
||||
Request *http.Request
|
||||
RequestBody []byte
|
||||
}
|
||||
|
||||
type Z_PluginHTTPReturns struct {
|
||||
Response *http.Response
|
||||
ResponseBody []byte
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) PluginHTTP(request *http.Request) *http.Response {
|
||||
forwardedRequest := &http.Request{
|
||||
Method: request.Method,
|
||||
URL: request.URL,
|
||||
Proto: request.Proto,
|
||||
ProtoMajor: request.ProtoMajor,
|
||||
ProtoMinor: request.ProtoMinor,
|
||||
Header: request.Header,
|
||||
Host: request.Host,
|
||||
RemoteAddr: request.RemoteAddr,
|
||||
RequestURI: request.RequestURI,
|
||||
}
|
||||
|
||||
_args := &Z_PluginHTTPArgs{
|
||||
Request: forwardedRequest,
|
||||
}
|
||||
|
||||
if request.Body != nil {
|
||||
requestBody, err := io.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
log.Printf("RPC call to PluginHTTP API failed: %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
request.Body.Close()
|
||||
request.Body = nil
|
||||
|
||||
_args.RequestBody = requestBody
|
||||
}
|
||||
|
||||
_returns := &Z_PluginHTTPReturns{}
|
||||
if err := g.client.Call("Plugin.PluginHTTP", _args, _returns); err != nil {
|
||||
log.Printf("RPC call to PluginHTTP API failed: %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
_returns.Response.Body = io.NopCloser(bytes.NewBuffer(_returns.ResponseBody))
|
||||
|
||||
return _returns.Response
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) PluginHTTP(args *Z_PluginHTTPArgs, returns *Z_PluginHTTPReturns) error {
|
||||
args.Request.Body = io.NopCloser(bytes.NewBuffer(args.RequestBody))
|
||||
|
||||
if hook, ok := s.impl.(interface {
|
||||
PluginHTTP(request *http.Request) *http.Response
|
||||
}); ok {
|
||||
response := hook.PluginHTTP(args.Request)
|
||||
|
||||
responseBody, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return encodableError(fmt.Errorf("RPC call to PluginHTTP API failed: %s", err.Error()))
|
||||
}
|
||||
response.Body.Close()
|
||||
response.Body = nil
|
||||
|
||||
returns.Response = response
|
||||
returns.ResponseBody = responseBody
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("API PluginHTTP called but not implemented"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
hookNameToId["FileWillBeUploaded"] = FileWillBeUploadedID
|
||||
}
|
||||
|
||||
type Z_FileWillBeUploadedArgs struct {
|
||||
A *Context
|
||||
B *model.FileInfo
|
||||
UploadedFileStream uint32
|
||||
ReplacementFileStream uint32
|
||||
}
|
||||
|
||||
type Z_FileWillBeUploadedReturns struct {
|
||||
A *model.FileInfo
|
||||
B string
|
||||
}
|
||||
|
||||
func (g *hooksRPCClient) FileWillBeUploaded(c *Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
|
||||
if !g.implemented[FileWillBeUploadedID] {
|
||||
return info, ""
|
||||
}
|
||||
|
||||
uploadedFileStreamId := g.muxBroker.NextId()
|
||||
go func() {
|
||||
uploadedFileConnection, err := g.muxBroker.Accept(uploadedFileStreamId)
|
||||
if err != nil {
|
||||
g.log.Error("Plugin failed to serve upload file stream. MuxBroker could not Accept connection", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
defer uploadedFileConnection.Close()
|
||||
serveIOReader(file, uploadedFileConnection)
|
||||
}()
|
||||
|
||||
replacementDone := make(chan bool)
|
||||
replacementFileStreamId := g.muxBroker.NextId()
|
||||
go func() {
|
||||
defer close(replacementDone)
|
||||
|
||||
replacementFileConnection, err := g.muxBroker.Accept(replacementFileStreamId)
|
||||
if err != nil {
|
||||
g.log.Error("Plugin failed to serve replacement file stream. MuxBroker could not Accept connection", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
defer replacementFileConnection.Close()
|
||||
if _, err := io.Copy(output, replacementFileConnection); err != nil {
|
||||
g.log.Error("Error reading replacement file.", mlog.Err(err))
|
||||
}
|
||||
}()
|
||||
|
||||
_args := &Z_FileWillBeUploadedArgs{c, info, uploadedFileStreamId, replacementFileStreamId}
|
||||
_returns := &Z_FileWillBeUploadedReturns{A: _args.B}
|
||||
if err := g.client.Call("Plugin.FileWillBeUploaded", _args, _returns); err != nil {
|
||||
g.log.Error("RPC call FileWillBeUploaded to plugin failed.", mlog.Err(err))
|
||||
}
|
||||
|
||||
// Ensure the io.Copy from the replacementFileConnection above completes.
|
||||
<-replacementDone
|
||||
|
||||
return _returns.A, _returns.B
|
||||
}
|
||||
|
||||
func (s *hooksRPCServer) FileWillBeUploaded(args *Z_FileWillBeUploadedArgs, returns *Z_FileWillBeUploadedReturns) error {
|
||||
uploadFileConnection, err := s.muxBroker.Dial(args.UploadedFileStream)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote upload file stream, error: %v", err.Error())
|
||||
return err
|
||||
}
|
||||
defer uploadFileConnection.Close()
|
||||
fileReader := connectIOReader(uploadFileConnection)
|
||||
defer fileReader.Close()
|
||||
|
||||
replacementFileConnection, err := s.muxBroker.Dial(args.ReplacementFileStream)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote replacement file stream, error: %v", err.Error())
|
||||
return err
|
||||
}
|
||||
defer replacementFileConnection.Close()
|
||||
returnFileWriter := replacementFileConnection
|
||||
|
||||
if hook, ok := s.impl.(interface {
|
||||
FileWillBeUploaded(c *Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string)
|
||||
}); ok {
|
||||
returns.A, returns.B = hook.FileWillBeUploaded(args.A, args.B, fileReader, returnFileWriter)
|
||||
} else {
|
||||
return fmt.Errorf("hook FileWillBeUploaded called but not implemented")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MessageWillBePosted is in this file because of the difficulty of identifying which fields need special behaviour.
|
||||
// The special behaviour needed is decoding the returned post into the original one to avoid the unintentional removal
|
||||
// of fields by older plugins.
|
||||
func init() {
|
||||
hookNameToId["MessageWillBePosted"] = MessageWillBePostedID
|
||||
}
|
||||
|
||||
type Z_MessageWillBePostedArgs struct {
|
||||
A *Context
|
||||
B *model.Post
|
||||
}
|
||||
|
||||
type Z_MessageWillBePostedReturns struct {
|
||||
A *model.Post
|
||||
B string
|
||||
}
|
||||
|
||||
func (g *hooksRPCClient) MessageWillBePosted(c *Context, post *model.Post) (*model.Post, string) {
|
||||
_args := &Z_MessageWillBePostedArgs{c, post}
|
||||
_returns := &Z_MessageWillBePostedReturns{A: _args.B}
|
||||
if g.implemented[MessageWillBePostedID] {
|
||||
if err := g.client.Call("Plugin.MessageWillBePosted", _args, _returns); err != nil {
|
||||
g.log.Error("RPC call MessageWillBePosted to plugin failed.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
return _returns.A, _returns.B
|
||||
}
|
||||
|
||||
func (s *hooksRPCServer) MessageWillBePosted(args *Z_MessageWillBePostedArgs, returns *Z_MessageWillBePostedReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
MessageWillBePosted(c *Context, post *model.Post) (*model.Post, string)
|
||||
}); ok {
|
||||
returns.A, returns.B = hook.MessageWillBePosted(args.A, args.B)
|
||||
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("hook MessageWillBePosted called but not implemented"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MessageWillBeUpdated is in this file because of the difficulty of identifying which fields need special behaviour.
|
||||
// The special behaviour needed is decoding the returned post into the original one to avoid the unintentional removal
|
||||
// of fields by older plugins.
|
||||
func init() {
|
||||
hookNameToId["MessageWillBeUpdated"] = MessageWillBeUpdatedID
|
||||
}
|
||||
|
||||
type Z_MessageWillBeUpdatedArgs struct {
|
||||
A *Context
|
||||
B *model.Post
|
||||
C *model.Post
|
||||
}
|
||||
|
||||
type Z_MessageWillBeUpdatedReturns struct {
|
||||
A *model.Post
|
||||
B string
|
||||
}
|
||||
|
||||
func (g *hooksRPCClient) MessageWillBeUpdated(c *Context, newPost, oldPost *model.Post) (*model.Post, string) {
|
||||
_args := &Z_MessageWillBeUpdatedArgs{c, newPost, oldPost}
|
||||
_returns := &Z_MessageWillBeUpdatedReturns{A: _args.B}
|
||||
if g.implemented[MessageWillBeUpdatedID] {
|
||||
if err := g.client.Call("Plugin.MessageWillBeUpdated", _args, _returns); err != nil {
|
||||
g.log.Error("RPC call MessageWillBeUpdated to plugin failed.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
return _returns.A, _returns.B
|
||||
}
|
||||
|
||||
func (s *hooksRPCServer) MessageWillBeUpdated(args *Z_MessageWillBeUpdatedArgs, returns *Z_MessageWillBeUpdatedReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
MessageWillBeUpdated(c *Context, newPost, oldPost *model.Post) (*model.Post, string)
|
||||
}); ok {
|
||||
returns.A, returns.B = hook.MessageWillBeUpdated(args.A, args.B, args.C)
|
||||
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("hook MessageWillBeUpdated called but not implemented"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_LogDebugArgs struct {
|
||||
A string
|
||||
B []any
|
||||
}
|
||||
|
||||
type Z_LogDebugReturns struct {
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) LogDebug(msg string, keyValuePairs ...any) {
|
||||
stringifiedPairs := stringifyToObjects(keyValuePairs)
|
||||
_args := &Z_LogDebugArgs{msg, stringifiedPairs}
|
||||
_returns := &Z_LogDebugReturns{}
|
||||
if err := g.client.Call("Plugin.LogDebug", _args, _returns); err != nil {
|
||||
log.Printf("RPC call to LogDebug API failed: %s", err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) LogDebug(args *Z_LogDebugArgs, returns *Z_LogDebugReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
LogDebug(msg string, keyValuePairs ...any)
|
||||
}); ok {
|
||||
hook.LogDebug(args.A, args.B...)
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("API LogDebug called but not implemented"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_LogInfoArgs struct {
|
||||
A string
|
||||
B []any
|
||||
}
|
||||
|
||||
type Z_LogInfoReturns struct {
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) LogInfo(msg string, keyValuePairs ...any) {
|
||||
stringifiedPairs := stringifyToObjects(keyValuePairs)
|
||||
_args := &Z_LogInfoArgs{msg, stringifiedPairs}
|
||||
_returns := &Z_LogInfoReturns{}
|
||||
if err := g.client.Call("Plugin.LogInfo", _args, _returns); err != nil {
|
||||
log.Printf("RPC call to LogInfo API failed: %s", err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) LogInfo(args *Z_LogInfoArgs, returns *Z_LogInfoReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
LogInfo(msg string, keyValuePairs ...any)
|
||||
}); ok {
|
||||
hook.LogInfo(args.A, args.B...)
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("API LogInfo called but not implemented"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_LogWarnArgs struct {
|
||||
A string
|
||||
B []any
|
||||
}
|
||||
|
||||
type Z_LogWarnReturns struct {
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) LogWarn(msg string, keyValuePairs ...any) {
|
||||
stringifiedPairs := stringifyToObjects(keyValuePairs)
|
||||
_args := &Z_LogWarnArgs{msg, stringifiedPairs}
|
||||
_returns := &Z_LogWarnReturns{}
|
||||
if err := g.client.Call("Plugin.LogWarn", _args, _returns); err != nil {
|
||||
log.Printf("RPC call to LogWarn API failed: %s", err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) LogWarn(args *Z_LogWarnArgs, returns *Z_LogWarnReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
LogWarn(msg string, keyValuePairs ...any)
|
||||
}); ok {
|
||||
hook.LogWarn(args.A, args.B...)
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("API LogWarn called but not implemented"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_LogErrorArgs struct {
|
||||
A string
|
||||
B []any
|
||||
}
|
||||
|
||||
type Z_LogErrorReturns struct {
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) LogError(msg string, keyValuePairs ...any) {
|
||||
stringifiedPairs := stringifyToObjects(keyValuePairs)
|
||||
_args := &Z_LogErrorArgs{msg, stringifiedPairs}
|
||||
_returns := &Z_LogErrorReturns{}
|
||||
if err := g.client.Call("Plugin.LogError", _args, _returns); err != nil {
|
||||
log.Printf("RPC call to LogError API failed: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) LogError(args *Z_LogErrorArgs, returns *Z_LogErrorReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
LogError(msg string, keyValuePairs ...any)
|
||||
}); ok {
|
||||
hook.LogError(args.A, args.B...)
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("API LogError called but not implemented"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_InstallPluginArgs struct {
|
||||
PluginStreamID uint32
|
||||
B bool
|
||||
}
|
||||
|
||||
type Z_InstallPluginReturns struct {
|
||||
A *model.Manifest
|
||||
B *model.AppError
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) InstallPlugin(file io.Reader, replace bool) (*model.Manifest, *model.AppError) {
|
||||
pluginStreamID := g.muxBroker.NextId()
|
||||
|
||||
go func() {
|
||||
uploadPluginConnection, err := g.muxBroker.Accept(pluginStreamID)
|
||||
if err != nil {
|
||||
log.Print("Plugin failed to upload plugin. MuxBroker could not Accept connection", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
defer uploadPluginConnection.Close()
|
||||
serveIOReader(file, uploadPluginConnection)
|
||||
}()
|
||||
|
||||
_args := &Z_InstallPluginArgs{pluginStreamID, replace}
|
||||
_returns := &Z_InstallPluginReturns{}
|
||||
if err := g.client.Call("Plugin.InstallPlugin", _args, _returns); err != nil {
|
||||
log.Print("RPC call InstallPlugin to plugin failed.", mlog.Err(err))
|
||||
}
|
||||
|
||||
return _returns.A, _returns.B
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) InstallPlugin(args *Z_InstallPluginArgs, returns *Z_InstallPluginReturns) error {
|
||||
hook, ok := s.impl.(interface {
|
||||
InstallPlugin(file io.Reader, replace bool) (*model.Manifest, *model.AppError)
|
||||
})
|
||||
if !ok {
|
||||
return encodableError(fmt.Errorf("API InstallPlugin called but not implemented"))
|
||||
}
|
||||
|
||||
receivePluginConnection, err := s.muxBroker.Dial(args.PluginStreamID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote plugin stream, error: %v", err.Error())
|
||||
return err
|
||||
}
|
||||
pluginReader := connectIOReader(receivePluginConnection)
|
||||
defer pluginReader.Close()
|
||||
|
||||
returns.A, returns.B = hook.InstallPlugin(pluginReader, args.B)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_UploadDataArgs struct {
|
||||
A *model.UploadSession
|
||||
PluginStreamID uint32
|
||||
}
|
||||
|
||||
type Z_UploadDataReturns struct {
|
||||
A *model.FileInfo
|
||||
B error
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) UploadData(us *model.UploadSession, rd io.Reader) (*model.FileInfo, error) {
|
||||
pluginStreamID := g.muxBroker.NextId()
|
||||
|
||||
go func() {
|
||||
pluginConnection, err := g.muxBroker.Accept(pluginStreamID)
|
||||
if err != nil {
|
||||
log.Print("Failed to upload data. MuxBroker could not Accept connection", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
defer pluginConnection.Close()
|
||||
serveIOReader(rd, pluginConnection)
|
||||
}()
|
||||
|
||||
_args := &Z_UploadDataArgs{us, pluginStreamID}
|
||||
_returns := &Z_UploadDataReturns{}
|
||||
if err := g.client.Call("Plugin.UploadData", _args, _returns); err != nil {
|
||||
log.Print("RPC call UploadData to plugin failed.", mlog.Err(err))
|
||||
}
|
||||
|
||||
return _returns.A, _returns.B
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) UploadData(args *Z_UploadDataArgs, returns *Z_UploadDataReturns) error {
|
||||
hook, ok := s.impl.(interface {
|
||||
UploadData(us *model.UploadSession, rd io.Reader) (*model.FileInfo, error)
|
||||
})
|
||||
if !ok {
|
||||
return encodableError(fmt.Errorf("API UploadData called but not implemented"))
|
||||
}
|
||||
|
||||
receivePluginConnection, err := s.muxBroker.Dial(args.PluginStreamID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote plugin stream, error: %v", err.Error())
|
||||
return err
|
||||
}
|
||||
pluginReader := connectIOReader(receivePluginConnection)
|
||||
defer pluginReader.Close()
|
||||
|
||||
returns.A, returns.B = hook.UploadData(args.A, pluginReader)
|
||||
return nil
|
||||
}
|
||||
Ссылка в новой задаче
Block a user