github.com/pafomin-at-avito/mattermost-server@v5.11.1+incompatible/app/plugin.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/mattermost/mattermost-server/mlog"
    13  	"github.com/mattermost/mattermost-server/model"
    14  	"github.com/mattermost/mattermost-server/plugin"
    15  	"github.com/mattermost/mattermost-server/utils/fileutils"
    16  )
    17  
    18  // GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
    19  // initialized.
    20  //
    21  // To get the plugins environment when the plugins are disabled, manually acquire the plugins
    22  // lock instead.
    23  func (a *App) GetPluginsEnvironment() *plugin.Environment {
    24  	if !*a.Config().PluginSettings.Enable {
    25  		return nil
    26  	}
    27  
    28  	a.Srv.PluginsLock.RLock()
    29  	defer a.Srv.PluginsLock.RUnlock()
    30  
    31  	return a.Srv.PluginsEnvironment
    32  }
    33  
    34  func (a *App) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) {
    35  	a.Srv.PluginsLock.Lock()
    36  	defer a.Srv.PluginsLock.Unlock()
    37  
    38  	a.Srv.PluginsEnvironment = pluginsEnvironment
    39  }
    40  
    41  func (a *App) SyncPluginsActiveState() {
    42  	a.Srv.PluginsLock.RLock()
    43  	pluginsEnvironment := a.Srv.PluginsEnvironment
    44  	a.Srv.PluginsLock.RUnlock()
    45  
    46  	if pluginsEnvironment == nil {
    47  		return
    48  	}
    49  
    50  	config := a.Config().PluginSettings
    51  
    52  	if *config.Enable {
    53  		availablePlugins, err := pluginsEnvironment.Available()
    54  		if err != nil {
    55  			a.Log.Error("Unable to get available plugins", mlog.Err(err))
    56  			return
    57  		}
    58  
    59  		// Deactivate any plugins that have been disabled.
    60  		for _, plugin := range pluginsEnvironment.Active() {
    61  			// Determine if plugin is enabled
    62  			pluginId := plugin.Manifest.Id
    63  			pluginEnabled := false
    64  			if state, ok := config.PluginStates[pluginId]; ok {
    65  				pluginEnabled = state.Enable
    66  			}
    67  
    68  			// If it's not enabled we need to deactivate it
    69  			if !pluginEnabled {
    70  				deactivated := pluginsEnvironment.Deactivate(pluginId)
    71  				if deactivated && plugin.Manifest.HasClient() {
    72  					message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_DISABLED, "", "", "", nil)
    73  					message.Add("manifest", plugin.Manifest.ClientManifest())
    74  					a.Publish(message)
    75  				}
    76  			}
    77  		}
    78  
    79  		// Activate any plugins that have been enabled
    80  		for _, plugin := range availablePlugins {
    81  			if plugin.Manifest == nil {
    82  				plugin.WrapLogger(a.Log).Error("Plugin manifest could not be loaded", mlog.Err(plugin.ManifestError))
    83  				continue
    84  			}
    85  
    86  			// Determine if plugin is enabled
    87  			pluginId := plugin.Manifest.Id
    88  			pluginEnabled := false
    89  			if state, ok := config.PluginStates[pluginId]; ok {
    90  				pluginEnabled = state.Enable
    91  			}
    92  
    93  			// Activate plugin if enabled
    94  			if pluginEnabled {
    95  				updatedManifest, activated, err := pluginsEnvironment.Activate(pluginId)
    96  				if err != nil {
    97  					plugin.WrapLogger(a.Log).Error("Unable to activate plugin", mlog.Err(err))
    98  					continue
    99  				}
   100  
   101  				if activated && updatedManifest.HasClient() {
   102  					message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_ENABLED, "", "", "", nil)
   103  					message.Add("manifest", updatedManifest.ClientManifest())
   104  					a.Publish(message)
   105  				}
   106  			}
   107  		}
   108  	} else { // If plugins are disabled, shutdown plugins.
   109  		pluginsEnvironment.Shutdown()
   110  	}
   111  
   112  	if err := a.notifyPluginStatusesChanged(); err != nil {
   113  		mlog.Error("failed to notify plugin status changed", mlog.Err(err))
   114  	}
   115  }
   116  
   117  func (a *App) NewPluginAPI(manifest *model.Manifest) plugin.API {
   118  	return NewPluginAPI(a, manifest)
   119  }
   120  
   121  func (a *App) InitPlugins(pluginDir, webappPluginDir string) {
   122  	a.Srv.PluginsLock.RLock()
   123  	pluginsEnvironment := a.Srv.PluginsEnvironment
   124  	a.Srv.PluginsLock.RUnlock()
   125  	if pluginsEnvironment != nil || !*a.Config().PluginSettings.Enable {
   126  		a.SyncPluginsActiveState()
   127  		return
   128  	}
   129  
   130  	a.Log.Info("Starting up plugins")
   131  
   132  	if err := os.Mkdir(pluginDir, 0744); err != nil && !os.IsExist(err) {
   133  		mlog.Error("Failed to start up plugins", mlog.Err(err))
   134  		return
   135  	}
   136  
   137  	if err := os.Mkdir(webappPluginDir, 0744); err != nil && !os.IsExist(err) {
   138  		mlog.Error("Failed to start up plugins", mlog.Err(err))
   139  		return
   140  	}
   141  
   142  	env, err := plugin.NewEnvironment(a.NewPluginAPI, pluginDir, webappPluginDir, a.Log)
   143  	if err != nil {
   144  		mlog.Error("Failed to start up plugins", mlog.Err(err))
   145  		return
   146  	}
   147  	a.SetPluginsEnvironment(env)
   148  
   149  	prepackagedPluginsDir, found := fileutils.FindDir("prepackaged_plugins")
   150  	if found {
   151  		if err := filepath.Walk(prepackagedPluginsDir, func(walkPath string, info os.FileInfo, err error) error {
   152  			if !strings.HasSuffix(walkPath, ".tar.gz") {
   153  				return nil
   154  			}
   155  
   156  			if fileReader, err := os.Open(walkPath); err != nil {
   157  				mlog.Error("Failed to open prepackaged plugin", mlog.Err(err), mlog.String("path", walkPath))
   158  			} else if _, err := a.InstallPlugin(fileReader, true); err != nil {
   159  				mlog.Error("Failed to unpack prepackaged plugin", mlog.Err(err), mlog.String("path", walkPath))
   160  			}
   161  
   162  			return nil
   163  		}); err != nil {
   164  			mlog.Error("Failed to complete unpacking prepackaged plugins", mlog.Err(err))
   165  		}
   166  	}
   167  
   168  	// Sync plugin active state when config changes. Also notify plugins.
   169  	a.Srv.PluginsLock.Lock()
   170  	a.RemoveConfigListener(a.Srv.PluginConfigListenerId)
   171  	a.Srv.PluginConfigListenerId = a.AddConfigListener(func(*model.Config, *model.Config) {
   172  		a.SyncPluginsActiveState()
   173  		if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   174  			pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   175  				hooks.OnConfigurationChange()
   176  				return true
   177  			}, plugin.OnConfigurationChangeId)
   178  		}
   179  	})
   180  	a.Srv.PluginsLock.Unlock()
   181  
   182  	a.SyncPluginsActiveState()
   183  }
   184  
   185  func (a *App) ShutDownPlugins() {
   186  	a.Srv.PluginsLock.Lock()
   187  	pluginsEnvironment := a.Srv.PluginsEnvironment
   188  	defer a.Srv.PluginsLock.Unlock()
   189  	if pluginsEnvironment == nil {
   190  		return
   191  	}
   192  
   193  	mlog.Info("Shutting down plugins")
   194  
   195  	pluginsEnvironment.Shutdown()
   196  
   197  	a.RemoveConfigListener(a.Srv.PluginConfigListenerId)
   198  	a.Srv.PluginConfigListenerId = ""
   199  	a.Srv.PluginsEnvironment = nil
   200  }
   201  
   202  func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) {
   203  	pluginsEnvironment := a.GetPluginsEnvironment()
   204  	if pluginsEnvironment == nil {
   205  		return nil, model.NewAppError("GetActivePluginManifests", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   206  	}
   207  
   208  	plugins := pluginsEnvironment.Active()
   209  
   210  	manifests := make([]*model.Manifest, len(plugins))
   211  	for i, plugin := range plugins {
   212  		manifests[i] = plugin.Manifest
   213  	}
   214  
   215  	return manifests, nil
   216  }
   217  
   218  // EnablePlugin will set the config for an installed plugin to enabled, triggering asynchronous
   219  // activation if inactive anywhere in the cluster.
   220  func (a *App) EnablePlugin(id string) *model.AppError {
   221  	pluginsEnvironment := a.GetPluginsEnvironment()
   222  	if pluginsEnvironment == nil {
   223  		return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   224  	}
   225  
   226  	plugins, err := pluginsEnvironment.Available()
   227  	if err != nil {
   228  		return model.NewAppError("EnablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   229  	}
   230  
   231  	id = strings.ToLower(id)
   232  
   233  	var manifest *model.Manifest
   234  	for _, p := range plugins {
   235  		if p.Manifest.Id == id {
   236  			manifest = p.Manifest
   237  			break
   238  		}
   239  	}
   240  
   241  	if manifest == nil {
   242  		return model.NewAppError("EnablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusBadRequest)
   243  	}
   244  
   245  	a.UpdateConfig(func(cfg *model.Config) {
   246  		cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: true}
   247  	})
   248  
   249  	// This call will cause SyncPluginsActiveState to be called and the plugin to be activated
   250  	if err := a.SaveConfig(a.Config(), true); err != nil {
   251  		if err.Id == "ent.cluster.save_config.error" {
   252  			return model.NewAppError("EnablePlugin", "app.plugin.cluster.save_config.app_error", nil, "", http.StatusInternalServerError)
   253  		}
   254  		return model.NewAppError("EnablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   255  	}
   256  
   257  	return nil
   258  }
   259  
   260  // DisablePlugin will set the config for an installed plugin to disabled, triggering deactivation if active.
   261  func (a *App) DisablePlugin(id string) *model.AppError {
   262  	pluginsEnvironment := a.GetPluginsEnvironment()
   263  	if pluginsEnvironment == nil {
   264  		return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   265  	}
   266  
   267  	plugins, err := pluginsEnvironment.Available()
   268  	if err != nil {
   269  		return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   270  	}
   271  
   272  	id = strings.ToLower(id)
   273  
   274  	var manifest *model.Manifest
   275  	for _, p := range plugins {
   276  		if p.Manifest.Id == id {
   277  			manifest = p.Manifest
   278  			break
   279  		}
   280  	}
   281  
   282  	if manifest == nil {
   283  		return model.NewAppError("DisablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusBadRequest)
   284  	}
   285  
   286  	a.UpdateConfig(func(cfg *model.Config) {
   287  		cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: false}
   288  	})
   289  	a.UnregisterPluginCommands(id)
   290  
   291  	if err := a.SaveConfig(a.Config(), true); err != nil {
   292  		return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   293  	}
   294  
   295  	return nil
   296  }
   297  
   298  func (a *App) GetPlugins() (*model.PluginsResponse, *model.AppError) {
   299  	pluginsEnvironment := a.GetPluginsEnvironment()
   300  	if pluginsEnvironment == nil {
   301  		return nil, model.NewAppError("GetPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   302  	}
   303  
   304  	availablePlugins, err := pluginsEnvironment.Available()
   305  	if err != nil {
   306  		return nil, model.NewAppError("GetPlugins", "app.plugin.get_plugins.app_error", nil, err.Error(), http.StatusInternalServerError)
   307  	}
   308  	resp := &model.PluginsResponse{Active: []*model.PluginInfo{}, Inactive: []*model.PluginInfo{}}
   309  	for _, plugin := range availablePlugins {
   310  		if plugin.Manifest == nil {
   311  			continue
   312  		}
   313  
   314  		info := &model.PluginInfo{
   315  			Manifest: *plugin.Manifest,
   316  		}
   317  
   318  		if pluginsEnvironment.IsActive(plugin.Manifest.Id) {
   319  			resp.Active = append(resp.Active, info)
   320  		} else {
   321  			resp.Inactive = append(resp.Inactive, info)
   322  		}
   323  	}
   324  
   325  	return resp, nil
   326  }