github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/plugin/client_rpc.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 //go:generate go run interface_generator/main.go 5 6 package plugin 7 8 import ( 9 "bytes" 10 "encoding/gob" 11 "encoding/json" 12 "fmt" 13 "io" 14 "io/ioutil" 15 "log" 16 "net/http" 17 "net/rpc" 18 "os" 19 "reflect" 20 21 "github.com/dyatlov/go-opengraph/opengraph" 22 "github.com/hashicorp/go-plugin" 23 24 "github.com/mattermost/mattermost-server/v5/mlog" 25 "github.com/mattermost/mattermost-server/v5/model" 26 ) 27 28 var hookNameToId map[string]int = make(map[string]int) 29 30 type hooksRPCClient struct { 31 client *rpc.Client 32 log *mlog.Logger 33 muxBroker *plugin.MuxBroker 34 apiImpl API 35 implemented [TotalHooksId]bool 36 } 37 38 type hooksRPCServer struct { 39 impl interface{} 40 muxBroker *plugin.MuxBroker 41 apiRPCClient *apiRPCClient 42 } 43 44 // Implements hashicorp/go-plugin/plugin.Plugin interface to connect the hooks of a plugin 45 type hooksPlugin struct { 46 hooks interface{} 47 apiImpl API 48 log *mlog.Logger 49 } 50 51 func (p *hooksPlugin) Server(b *plugin.MuxBroker) (interface{}, error) { 52 return &hooksRPCServer{impl: p.hooks, muxBroker: b}, nil 53 } 54 55 func (p *hooksPlugin) Client(b *plugin.MuxBroker, client *rpc.Client) (interface{}, error) { 56 return &hooksRPCClient{client: client, log: p.log, muxBroker: b, apiImpl: p.apiImpl}, nil 57 } 58 59 type apiRPCClient struct { 60 client *rpc.Client 61 muxBroker *plugin.MuxBroker 62 } 63 64 type apiRPCServer struct { 65 impl API 66 muxBroker *plugin.MuxBroker 67 } 68 69 // ErrorString is a fallback for sending unregistered implementations of the error interface across 70 // rpc. For example, the errorString type from the github.com/pkg/errors package cannot be 71 // registered since it is not exported, but this precludes common error handling paradigms. 72 // ErrorString merely preserves the string description of the error, while satisfying the error 73 // interface itself to allow other registered types (such as model.AppError) to be sent unmodified. 74 type ErrorString struct { 75 Err string 76 } 77 78 func (e ErrorString) Error() string { 79 return e.Err 80 } 81 82 func encodableError(err error) error { 83 if err == nil { 84 return nil 85 } 86 if _, ok := err.(*model.AppError); ok { 87 return err 88 } 89 90 return &ErrorString{ 91 Err: err.Error(), 92 } 93 } 94 95 // Registering some types used by MM for encoding/gob used by rpc 96 func init() { 97 gob.Register([]*model.SlackAttachment{}) 98 gob.Register([]interface{}{}) 99 gob.Register(map[string]interface{}{}) 100 gob.Register(&model.AppError{}) 101 gob.Register(&ErrorString{}) 102 gob.Register(&opengraph.OpenGraph{}) 103 gob.Register(&model.AutocompleteDynamicListArg{}) 104 gob.Register(&model.AutocompleteStaticListArg{}) 105 gob.Register(&model.AutocompleteTextArg{}) 106 } 107 108 // These enforce compile time checks to make sure types implement the interface 109 // If you are getting an error here, you probably need to run `make pluginapi` to 110 // autogenerate RPC glue code 111 var _ plugin.Plugin = &hooksPlugin{} 112 var _ Hooks = &hooksRPCClient{} 113 114 // 115 // Below are special cases for hooks or APIs that can not be auto generated 116 // 117 118 func (g *hooksRPCClient) Implemented() (impl []string, err error) { 119 err = g.client.Call("Plugin.Implemented", struct{}{}, &impl) 120 for _, hookName := range impl { 121 if hookId, ok := hookNameToId[hookName]; ok { 122 g.implemented[hookId] = true 123 } 124 } 125 return 126 } 127 128 // Implemented replies with the names of the hooks that are implemented. 129 func (s *hooksRPCServer) Implemented(args struct{}, reply *[]string) error { 130 ifaceType := reflect.TypeOf((*Hooks)(nil)).Elem() 131 implType := reflect.TypeOf(s.impl) 132 selfType := reflect.TypeOf(s) 133 var methods []string 134 for i := 0; i < ifaceType.NumMethod(); i++ { 135 method := ifaceType.Method(i) 136 if m, ok := implType.MethodByName(method.Name); !ok { 137 continue 138 } else if m.Type.NumIn() != method.Type.NumIn()+1 { 139 continue 140 } else if m.Type.NumOut() != method.Type.NumOut() { 141 continue 142 } else { 143 match := true 144 for j := 0; j < method.Type.NumIn(); j++ { 145 if m.Type.In(j+1) != method.Type.In(j) { 146 match = false 147 break 148 } 149 } 150 for j := 0; j < method.Type.NumOut(); j++ { 151 if m.Type.Out(j) != method.Type.Out(j) { 152 match = false 153 break 154 } 155 } 156 if !match { 157 continue 158 } 159 } 160 if _, ok := selfType.MethodByName(method.Name); !ok { 161 continue 162 } 163 methods = append(methods, method.Name) 164 } 165 *reply = methods 166 return encodableError(nil) 167 } 168 169 type Z_OnActivateArgs struct { 170 APIMuxId uint32 171 } 172 173 type Z_OnActivateReturns struct { 174 A error 175 } 176 177 func (g *hooksRPCClient) OnActivate() error { 178 muxId := g.muxBroker.NextId() 179 go g.muxBroker.AcceptAndServe(muxId, &apiRPCServer{ 180 impl: g.apiImpl, 181 muxBroker: g.muxBroker, 182 }) 183 184 _args := &Z_OnActivateArgs{ 185 APIMuxId: muxId, 186 } 187 _returns := &Z_OnActivateReturns{} 188 189 if err := g.client.Call("Plugin.OnActivate", _args, _returns); err != nil { 190 g.log.Error("RPC call to OnActivate plugin failed.", mlog.Err(err)) 191 } 192 return _returns.A 193 } 194 195 func (s *hooksRPCServer) OnActivate(args *Z_OnActivateArgs, returns *Z_OnActivateReturns) error { 196 connection, err := s.muxBroker.Dial(args.APIMuxId) 197 if err != nil { 198 return err 199 } 200 201 s.apiRPCClient = &apiRPCClient{ 202 client: rpc.NewClient(connection), 203 muxBroker: s.muxBroker, 204 } 205 206 if mmplugin, ok := s.impl.(interface { 207 SetAPI(api API) 208 SetHelpers(helpers Helpers) 209 }); ok { 210 mmplugin.SetAPI(s.apiRPCClient) 211 mmplugin.SetHelpers(&HelpersImpl{API: s.apiRPCClient}) 212 } 213 214 if mmplugin, ok := s.impl.(interface { 215 OnConfigurationChange() error 216 }); ok { 217 if err := mmplugin.OnConfigurationChange(); err != nil { 218 fmt.Fprintf(os.Stderr, "[ERROR] call to OnConfigurationChange failed, error: %v", err.Error()) 219 } 220 } 221 222 // Capture output of standard logger because go-plugin 223 // redirects it. 224 log.SetOutput(os.Stderr) 225 226 if hook, ok := s.impl.(interface { 227 OnActivate() error 228 }); ok { 229 returns.A = encodableError(hook.OnActivate()) 230 } 231 return nil 232 } 233 234 type Z_LoadPluginConfigurationArgsArgs struct { 235 } 236 237 type Z_LoadPluginConfigurationArgsReturns struct { 238 A []byte 239 } 240 241 func (g *apiRPCClient) LoadPluginConfiguration(dest interface{}) error { 242 _args := &Z_LoadPluginConfigurationArgsArgs{} 243 _returns := &Z_LoadPluginConfigurationArgsReturns{} 244 if err := g.client.Call("Plugin.LoadPluginConfiguration", _args, _returns); err != nil { 245 log.Printf("RPC call to LoadPluginConfiguration API failed: %s", err.Error()) 246 } 247 if err := json.Unmarshal(_returns.A, dest); err != nil { 248 log.Printf("LoadPluginConfiguration API failed to unmarshal: %s", err.Error()) 249 } 250 return nil 251 } 252 253 func (s *apiRPCServer) LoadPluginConfiguration(args *Z_LoadPluginConfigurationArgsArgs, returns *Z_LoadPluginConfigurationArgsReturns) error { 254 var config interface{} 255 if hook, ok := s.impl.(interface { 256 LoadPluginConfiguration(dest interface{}) error 257 }); ok { 258 if err := hook.LoadPluginConfiguration(&config); err != nil { 259 return err 260 } 261 } 262 b, err := json.Marshal(config) 263 if err != nil { 264 return err 265 } 266 returns.A = b 267 return nil 268 } 269 270 func init() { 271 hookNameToId["ServeHTTP"] = ServeHTTPId 272 } 273 274 type Z_ServeHTTPArgs struct { 275 ResponseWriterStream uint32 276 Request *http.Request 277 Context *Context 278 RequestBodyStream uint32 279 } 280 281 func (g *hooksRPCClient) ServeHTTP(c *Context, w http.ResponseWriter, r *http.Request) { 282 if !g.implemented[ServeHTTPId] { 283 http.NotFound(w, r) 284 return 285 } 286 287 serveHTTPStreamId := g.muxBroker.NextId() 288 go func() { 289 connection, err := g.muxBroker.Accept(serveHTTPStreamId) 290 if err != nil { 291 g.log.Error("Plugin failed to ServeHTTP, muxBroker couldn't accept connection", mlog.Uint32("serve_http_stream_id", serveHTTPStreamId), mlog.Err(err)) 292 return 293 } 294 defer connection.Close() 295 296 rpcServer := rpc.NewServer() 297 if err := rpcServer.RegisterName("Plugin", &httpResponseWriterRPCServer{w: w, log: g.log}); err != nil { 298 g.log.Error("Plugin failed to ServeHTTP, couldn't register RPC name", mlog.Err(err)) 299 return 300 } 301 rpcServer.ServeConn(connection) 302 }() 303 304 requestBodyStreamId := uint32(0) 305 if r.Body != nil { 306 requestBodyStreamId = g.muxBroker.NextId() 307 go func() { 308 bodyConnection, err := g.muxBroker.Accept(requestBodyStreamId) 309 if err != nil { 310 g.log.Error("Plugin failed to ServeHTTP, muxBroker couldn't Accept request body connection", mlog.Err(err)) 311 return 312 } 313 defer bodyConnection.Close() 314 serveIOReader(r.Body, bodyConnection) 315 }() 316 } 317 318 forwardedRequest := &http.Request{ 319 Method: r.Method, 320 URL: r.URL, 321 Proto: r.Proto, 322 ProtoMajor: r.ProtoMajor, 323 ProtoMinor: r.ProtoMinor, 324 Header: r.Header, 325 Host: r.Host, 326 RemoteAddr: r.RemoteAddr, 327 RequestURI: r.RequestURI, 328 } 329 330 if err := g.client.Call("Plugin.ServeHTTP", Z_ServeHTTPArgs{ 331 Context: c, 332 ResponseWriterStream: serveHTTPStreamId, 333 Request: forwardedRequest, 334 RequestBodyStream: requestBodyStreamId, 335 }, nil); err != nil { 336 g.log.Error("Plugin failed to ServeHTTP, RPC call failed", mlog.Err(err)) 337 http.Error(w, "500 internal server error", http.StatusInternalServerError) 338 } 339 } 340 341 func (s *hooksRPCServer) ServeHTTP(args *Z_ServeHTTPArgs, returns *struct{}) error { 342 connection, err := s.muxBroker.Dial(args.ResponseWriterStream) 343 if err != nil { 344 fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote response writer stream, error: %v", err.Error()) 345 return err 346 } 347 w := connectHTTPResponseWriter(connection) 348 defer w.Close() 349 350 r := args.Request 351 if args.RequestBodyStream != 0 { 352 connection, err := s.muxBroker.Dial(args.RequestBodyStream) 353 if err != nil { 354 fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote request body stream, error: %v", err.Error()) 355 return err 356 } 357 r.Body = connectIOReader(connection) 358 } else { 359 r.Body = ioutil.NopCloser(&bytes.Buffer{}) 360 } 361 defer r.Body.Close() 362 363 if hook, ok := s.impl.(interface { 364 ServeHTTP(c *Context, w http.ResponseWriter, r *http.Request) 365 }); ok { 366 hook.ServeHTTP(args.Context, w, r) 367 } else { 368 http.NotFound(w, r) 369 } 370 371 return nil 372 } 373 374 type Z_PluginHTTPArgs struct { 375 Request *http.Request 376 RequestBody []byte 377 } 378 379 type Z_PluginHTTPReturns struct { 380 Response *http.Response 381 ResponseBody []byte 382 } 383 384 func (g *apiRPCClient) PluginHTTP(request *http.Request) *http.Response { 385 forwardedRequest := &http.Request{ 386 Method: request.Method, 387 URL: request.URL, 388 Proto: request.Proto, 389 ProtoMajor: request.ProtoMajor, 390 ProtoMinor: request.ProtoMinor, 391 Header: request.Header, 392 Host: request.Host, 393 RemoteAddr: request.RemoteAddr, 394 RequestURI: request.RequestURI, 395 } 396 397 requestBody, err := ioutil.ReadAll(request.Body) 398 if err != nil { 399 log.Printf("RPC call to PluginHTTP API failed: %s", err.Error()) 400 return nil 401 } 402 request.Body.Close() 403 request.Body = nil 404 405 _args := &Z_PluginHTTPArgs{ 406 Request: forwardedRequest, 407 RequestBody: requestBody, 408 } 409 410 _returns := &Z_PluginHTTPReturns{} 411 if err := g.client.Call("Plugin.PluginHTTP", _args, _returns); err != nil { 412 log.Printf("RPC call to PluginHTTP API failed: %s", err.Error()) 413 return nil 414 } 415 416 _returns.Response.Body = ioutil.NopCloser(bytes.NewBuffer(_returns.ResponseBody)) 417 418 return _returns.Response 419 } 420 421 func (s *apiRPCServer) PluginHTTP(args *Z_PluginHTTPArgs, returns *Z_PluginHTTPReturns) error { 422 args.Request.Body = ioutil.NopCloser(bytes.NewBuffer(args.RequestBody)) 423 424 if hook, ok := s.impl.(interface { 425 PluginHTTP(request *http.Request) *http.Response 426 }); ok { 427 response := hook.PluginHTTP(args.Request) 428 429 responseBody, err := ioutil.ReadAll(response.Body) 430 if err != nil { 431 return encodableError(fmt.Errorf("RPC call to PluginHTTP API failed: %s", err.Error())) 432 } 433 response.Body.Close() 434 response.Body = nil 435 436 returns.Response = response 437 returns.ResponseBody = responseBody 438 } else { 439 return encodableError(fmt.Errorf("API PluginHTTP called but not implemented.")) 440 } 441 return nil 442 } 443 444 func init() { 445 hookNameToId["FileWillBeUploaded"] = FileWillBeUploadedId 446 } 447 448 type Z_FileWillBeUploadedArgs struct { 449 A *Context 450 B *model.FileInfo 451 UploadedFileStream uint32 452 ReplacementFileStream uint32 453 } 454 455 type Z_FileWillBeUploadedReturns struct { 456 A *model.FileInfo 457 B string 458 } 459 460 func (g *hooksRPCClient) FileWillBeUploaded(c *Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) { 461 if !g.implemented[FileWillBeUploadedId] { 462 return info, "" 463 } 464 465 uploadedFileStreamId := g.muxBroker.NextId() 466 go func() { 467 uploadedFileConnection, err := g.muxBroker.Accept(uploadedFileStreamId) 468 if err != nil { 469 g.log.Error("Plugin failed to serve upload file stream. MuxBroker could not Accept connection", mlog.Err(err)) 470 return 471 } 472 defer uploadedFileConnection.Close() 473 serveIOReader(file, uploadedFileConnection) 474 }() 475 476 replacementDone := make(chan bool) 477 replacementFileStreamId := g.muxBroker.NextId() 478 go func() { 479 defer close(replacementDone) 480 481 replacementFileConnection, err := g.muxBroker.Accept(replacementFileStreamId) 482 if err != nil { 483 g.log.Error("Plugin failed to serve replacement file stream. MuxBroker could not Accept connection", mlog.Err(err)) 484 return 485 } 486 defer replacementFileConnection.Close() 487 if _, err := io.Copy(output, replacementFileConnection); err != nil { 488 g.log.Error("Error reading replacement file.", mlog.Err(err)) 489 } 490 }() 491 492 _args := &Z_FileWillBeUploadedArgs{c, info, uploadedFileStreamId, replacementFileStreamId} 493 _returns := &Z_FileWillBeUploadedReturns{A: _args.B} 494 if err := g.client.Call("Plugin.FileWillBeUploaded", _args, _returns); err != nil { 495 g.log.Error("RPC call FileWillBeUploaded to plugin failed.", mlog.Err(err)) 496 } 497 498 // Ensure the io.Copy from the replacementFileConnection above completes. 499 <-replacementDone 500 501 return _returns.A, _returns.B 502 } 503 504 func (s *hooksRPCServer) FileWillBeUploaded(args *Z_FileWillBeUploadedArgs, returns *Z_FileWillBeUploadedReturns) error { 505 uploadFileConnection, err := s.muxBroker.Dial(args.UploadedFileStream) 506 if err != nil { 507 fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote upload file stream, error: %v", err.Error()) 508 return err 509 } 510 defer uploadFileConnection.Close() 511 fileReader := connectIOReader(uploadFileConnection) 512 defer fileReader.Close() 513 514 replacementFileConnection, err := s.muxBroker.Dial(args.ReplacementFileStream) 515 if err != nil { 516 fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote replacement file stream, error: %v", err.Error()) 517 return err 518 } 519 defer replacementFileConnection.Close() 520 returnFileWriter := replacementFileConnection 521 522 if hook, ok := s.impl.(interface { 523 FileWillBeUploaded(c *Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) 524 }); ok { 525 returns.A, returns.B = hook.FileWillBeUploaded(args.A, args.B, fileReader, returnFileWriter) 526 } else { 527 return fmt.Errorf("Hook FileWillBeUploaded called but not implemented.") 528 } 529 return nil 530 } 531 532 // MessageWillBePosted is in this file because of the difficulty of identifying which fields need special behaviour. 533 // The special behaviour needed is decoding the returned post into the original one to avoid the unintentional removal 534 // of fields by older plugins. 535 func init() { 536 hookNameToId["MessageWillBePosted"] = MessageWillBePostedId 537 } 538 539 type Z_MessageWillBePostedArgs struct { 540 A *Context 541 B *model.Post 542 } 543 544 type Z_MessageWillBePostedReturns struct { 545 A *model.Post 546 B string 547 } 548 549 func (g *hooksRPCClient) MessageWillBePosted(c *Context, post *model.Post) (*model.Post, string) { 550 _args := &Z_MessageWillBePostedArgs{c, post} 551 _returns := &Z_MessageWillBePostedReturns{A: _args.B} 552 if g.implemented[MessageWillBePostedId] { 553 if err := g.client.Call("Plugin.MessageWillBePosted", _args, _returns); err != nil { 554 g.log.Error("RPC call MessageWillBePosted to plugin failed.", mlog.Err(err)) 555 } 556 } 557 return _returns.A, _returns.B 558 } 559 560 func (s *hooksRPCServer) MessageWillBePosted(args *Z_MessageWillBePostedArgs, returns *Z_MessageWillBePostedReturns) error { 561 if hook, ok := s.impl.(interface { 562 MessageWillBePosted(c *Context, post *model.Post) (*model.Post, string) 563 }); ok { 564 returns.A, returns.B = hook.MessageWillBePosted(args.A, args.B) 565 566 } else { 567 return encodableError(fmt.Errorf("Hook MessageWillBePosted called but not implemented.")) 568 } 569 return nil 570 } 571 572 // MessageWillBeUpdated is in this file because of the difficulty of identifying which fields need special behaviour. 573 // The special behaviour needed is decoding the returned post into the original one to avoid the unintentional removal 574 // of fields by older plugins. 575 func init() { 576 hookNameToId["MessageWillBeUpdated"] = MessageWillBeUpdatedId 577 } 578 579 type Z_MessageWillBeUpdatedArgs struct { 580 A *Context 581 B *model.Post 582 C *model.Post 583 } 584 585 type Z_MessageWillBeUpdatedReturns struct { 586 A *model.Post 587 B string 588 } 589 590 func (g *hooksRPCClient) MessageWillBeUpdated(c *Context, newPost, oldPost *model.Post) (*model.Post, string) { 591 _args := &Z_MessageWillBeUpdatedArgs{c, newPost, oldPost} 592 _returns := &Z_MessageWillBeUpdatedReturns{A: _args.B} 593 if g.implemented[MessageWillBeUpdatedId] { 594 if err := g.client.Call("Plugin.MessageWillBeUpdated", _args, _returns); err != nil { 595 g.log.Error("RPC call MessageWillBeUpdated to plugin failed.", mlog.Err(err)) 596 } 597 } 598 return _returns.A, _returns.B 599 } 600 601 func (s *hooksRPCServer) MessageWillBeUpdated(args *Z_MessageWillBeUpdatedArgs, returns *Z_MessageWillBeUpdatedReturns) error { 602 if hook, ok := s.impl.(interface { 603 MessageWillBeUpdated(c *Context, newPost, oldPost *model.Post) (*model.Post, string) 604 }); ok { 605 returns.A, returns.B = hook.MessageWillBeUpdated(args.A, args.B, args.C) 606 607 } else { 608 return encodableError(fmt.Errorf("Hook MessageWillBeUpdated called but not implemented.")) 609 } 610 return nil 611 } 612 613 type Z_LogDebugArgs struct { 614 A string 615 B []interface{} 616 } 617 618 type Z_LogDebugReturns struct { 619 } 620 621 func (g *apiRPCClient) LogDebug(msg string, keyValuePairs ...interface{}) { 622 stringifiedPairs := stringifyToObjects(keyValuePairs) 623 _args := &Z_LogDebugArgs{msg, stringifiedPairs} 624 _returns := &Z_LogDebugReturns{} 625 if err := g.client.Call("Plugin.LogDebug", _args, _returns); err != nil { 626 log.Printf("RPC call to LogDebug API failed: %s", err.Error()) 627 } 628 629 } 630 631 func (s *apiRPCServer) LogDebug(args *Z_LogDebugArgs, returns *Z_LogDebugReturns) error { 632 if hook, ok := s.impl.(interface { 633 LogDebug(msg string, keyValuePairs ...interface{}) 634 }); ok { 635 hook.LogDebug(args.A, args.B...) 636 } else { 637 return encodableError(fmt.Errorf("API LogDebug called but not implemented.")) 638 } 639 return nil 640 } 641 642 type Z_LogInfoArgs struct { 643 A string 644 B []interface{} 645 } 646 647 type Z_LogInfoReturns struct { 648 } 649 650 func (g *apiRPCClient) LogInfo(msg string, keyValuePairs ...interface{}) { 651 stringifiedPairs := stringifyToObjects(keyValuePairs) 652 _args := &Z_LogInfoArgs{msg, stringifiedPairs} 653 _returns := &Z_LogInfoReturns{} 654 if err := g.client.Call("Plugin.LogInfo", _args, _returns); err != nil { 655 log.Printf("RPC call to LogInfo API failed: %s", err.Error()) 656 } 657 658 } 659 660 func (s *apiRPCServer) LogInfo(args *Z_LogInfoArgs, returns *Z_LogInfoReturns) error { 661 if hook, ok := s.impl.(interface { 662 LogInfo(msg string, keyValuePairs ...interface{}) 663 }); ok { 664 hook.LogInfo(args.A, args.B...) 665 } else { 666 return encodableError(fmt.Errorf("API LogInfo called but not implemented.")) 667 } 668 return nil 669 } 670 671 type Z_LogWarnArgs struct { 672 A string 673 B []interface{} 674 } 675 676 type Z_LogWarnReturns struct { 677 } 678 679 func (g *apiRPCClient) LogWarn(msg string, keyValuePairs ...interface{}) { 680 stringifiedPairs := stringifyToObjects(keyValuePairs) 681 _args := &Z_LogWarnArgs{msg, stringifiedPairs} 682 _returns := &Z_LogWarnReturns{} 683 if err := g.client.Call("Plugin.LogWarn", _args, _returns); err != nil { 684 log.Printf("RPC call to LogWarn API failed: %s", err.Error()) 685 } 686 687 } 688 689 func (s *apiRPCServer) LogWarn(args *Z_LogWarnArgs, returns *Z_LogWarnReturns) error { 690 if hook, ok := s.impl.(interface { 691 LogWarn(msg string, keyValuePairs ...interface{}) 692 }); ok { 693 hook.LogWarn(args.A, args.B...) 694 } else { 695 return encodableError(fmt.Errorf("API LogWarn called but not implemented.")) 696 } 697 return nil 698 } 699 700 type Z_LogErrorArgs struct { 701 A string 702 B []interface{} 703 } 704 705 type Z_LogErrorReturns struct { 706 } 707 708 func (g *apiRPCClient) LogError(msg string, keyValuePairs ...interface{}) { 709 stringifiedPairs := stringifyToObjects(keyValuePairs) 710 _args := &Z_LogErrorArgs{msg, stringifiedPairs} 711 _returns := &Z_LogErrorReturns{} 712 if err := g.client.Call("Plugin.LogError", _args, _returns); err != nil { 713 log.Printf("RPC call to LogError API failed: %s", err.Error()) 714 } 715 } 716 717 func (s *apiRPCServer) LogError(args *Z_LogErrorArgs, returns *Z_LogErrorReturns) error { 718 if hook, ok := s.impl.(interface { 719 LogError(msg string, keyValuePairs ...interface{}) 720 }); ok { 721 hook.LogError(args.A, args.B...) 722 } else { 723 return encodableError(fmt.Errorf("API LogError called but not implemented.")) 724 } 725 return nil 726 } 727 728 type Z_InstallPluginArgs struct { 729 PluginStreamID uint32 730 B bool 731 } 732 733 type Z_InstallPluginReturns struct { 734 A *model.Manifest 735 B *model.AppError 736 } 737 738 func (g *apiRPCClient) InstallPlugin(file io.Reader, replace bool) (*model.Manifest, *model.AppError) { 739 pluginStreamID := g.muxBroker.NextId() 740 741 go func() { 742 uploadPluginConnection, err := g.muxBroker.Accept(pluginStreamID) 743 if err != nil { 744 log.Print("Plugin failed to upload plugin. MuxBroker could not Accept connection", mlog.Err(err)) 745 return 746 } 747 defer uploadPluginConnection.Close() 748 serveIOReader(file, uploadPluginConnection) 749 }() 750 751 _args := &Z_InstallPluginArgs{pluginStreamID, replace} 752 _returns := &Z_InstallPluginReturns{} 753 if err := g.client.Call("Plugin.InstallPlugin", _args, _returns); err != nil { 754 log.Print("RPC call InstallPlugin to plugin failed.", mlog.Err(err)) 755 } 756 757 return _returns.A, _returns.B 758 } 759 760 func (s *apiRPCServer) InstallPlugin(args *Z_InstallPluginArgs, returns *Z_InstallPluginReturns) error { 761 hook, ok := s.impl.(interface { 762 InstallPlugin(file io.Reader, replace bool) (*model.Manifest, *model.AppError) 763 }) 764 if !ok { 765 return encodableError(fmt.Errorf("API InstallPlugin called but not implemented.")) 766 } 767 768 receivePluginConnection, err := s.muxBroker.Dial(args.PluginStreamID) 769 if err != nil { 770 fmt.Fprintf(os.Stderr, "[ERROR] Can't connect to remote plugin stream, error: %v", err.Error()) 771 return err 772 } 773 pluginReader := connectIOReader(receivePluginConnection) 774 defer pluginReader.Close() 775 776 returns.A, returns.B = hook.InstallPlugin(pluginReader, args.B) 777 return nil 778 }