1 /* Copyright (C) 2016-2017 Shengyu Zhang <i@silverrainz.me>
2 *
3 * This file is part of Srain.
4 *
5 * Srain is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * @file app.c
21 * @brief Srain's application class implementation
22 * @author Shengyu Zhang <i@silverrainz.me>
23 * @version 0.06.2
24 * @date 2016-03-01
25 */
26
27 #include "core/core.h"
28 #include "sui/sui.h"
29 #include "config/reader.h"
30 #include "meta.h"
31 #include "log.h"
32 #include "i18n.h"
33 #include "path.h"
34 #include "utils.h"
35 #include "pattern_set.h"
36
37 #include "app_event.h"
38 #include "chat_command.h"
39
40 /* Only one SrnApplication instance in one application */
41 static SrnApplication *app_instance = NULL;
42
43 static void init_logger(SrnApplication *app);
44 static void finalize_logger(SrnApplication *app);
45
46 /*****************************************************************************
47 * Exported functions
48 *****************************************************************************/
49
srn_application_new(void)50 SrnApplication* srn_application_new(void){
51 char *path;
52 SrnRet ret;
53 SrnVersion *ver;
54 SrnConfigManager *cfg_mgr;
55 SrnApplication *app;
56 SrnApplicationConfig *cfg;
57
58 // Keep only one instance
59 g_return_val_if_fail(!app_instance, NULL);
60
61 ver = srn_version_new(PACKAGE_VERSION PACKAGE_BUILD);
62 ret = srn_version_parse(ver);
63 if (!RET_IS_OK(ret)){
64 ERR_FR("Failed to parse " PACKAGE_VERSION PACKAGE_BUILD
65 "as application version: %s", RET_MSG(ret));
66 return NULL;
67 }
68
69 // Init config
70 cfg_mgr = srn_config_manager_new(ver);
71 path = srn_get_system_config_file();
72 if (path){
73 ret = srn_config_manager_load_system_config(cfg_mgr, path);
74 g_free(path);
75 if (!RET_IS_OK(ret)){
76 sui_message_box(_("Error"), RET_MSG(ret));
77 }
78 }
79 path = srn_get_user_config_file();
80 if (path){
81 ret = srn_config_manager_load_user_config(cfg_mgr, path);
82 g_free(path);
83 if (!RET_IS_OK(ret)){
84 sui_message_box(_("Error"), RET_MSG(ret));
85 }
86 }
87 cfg = srn_application_config_new();
88 srn_config_manager_read_application_config(cfg_mgr, cfg);
89
90 app = g_malloc0(sizeof(SrnApplication));
91 app->ver = ver;
92 app->cfg = cfg;
93 app->cfg_mgr = cfg_mgr;
94
95 init_logger(app);
96 srn_application_init_ui_event(app);
97 srn_application_init_irc_event(app);
98
99 app->ui = sui_new_application(cfg->id ? cfg->id : PACKAGE_APPID,
100 app, &app->ui_app_events, cfg->ui);
101
102 app->pattern_set = srn_pattern_set_new();
103
104 app->cmd_ctx = srn_command_context_new();
105 srn_command_context_bind(app->cmd_ctx, cmd_bindings);
106
107 app_instance = app;
108
109 return app;
110 }
111
112 // SrnApplication* srn_application_get_instance(void){
srn_application_get_default(void)113 SrnApplication* srn_application_get_default(void){
114 return app_instance;
115 }
116
srn_application_quit(SrnApplication * app)117 void srn_application_quit(SrnApplication *app){
118 // TODO: cleanup
119 finalize_logger(app);
120 }
121
srn_application_run(SrnApplication * app,int argc,char * argv[])122 void srn_application_run(SrnApplication *app, int argc, char *argv[]){
123 sui_run_application(app->ui, argc, argv);
124 }
125
srn_application_set_config(SrnApplication * app,SrnApplicationConfig * cfg)126 void srn_application_set_config(SrnApplication *app, SrnApplicationConfig *cfg){
127 sui_application_set_config(app->ui, cfg->ui);
128 app->cfg = cfg;
129 }
130
srn_application_reload_config(SrnApplication * app)131 SrnRet srn_application_reload_config(SrnApplication *app){
132 char *path;
133 GList *lst;
134 SrnRet ret;
135 SrnLoggerConfig *logger_cfg;
136 SrnLoggerConfig *old_logger_cfg;
137 SrnApplicationConfig *cfg;
138 SrnApplicationConfig *old_cfg;
139 SrnConfigManager *cfg_mgr;
140
141 cfg_mgr = app->cfg_mgr;
142
143 /* Read newest user config: TODO: Read should not be done here */
144 path = srn_get_user_config_file();
145 if (!path){
146 return RET_ERR(_("User config not found"));
147 }
148 ret = srn_config_manager_load_user_config(cfg_mgr, path);
149 g_free(path);
150 if (!RET_IS_OK(ret)){
151 return ret;
152 }
153
154 /* Update log config */
155 logger_cfg = srn_logger_config_new();
156 old_logger_cfg = srn_logger_get_config(app->logger);
157 ret = srn_config_manager_read_log_config(cfg_mgr, logger_cfg);
158 if (!RET_IS_OK(ret)){
159 goto ERR_RELOAD_LOGGER;
160 }
161 ret = srn_logger_config_check(logger_cfg);
162 if (!RET_IS_OK(ret)){
163 goto ERR_RELOAD_LOGGER;
164 }
165 srn_logger_set_config(app->logger, logger_cfg);
166 srn_logger_config_free(old_logger_cfg);
167
168 /* Update application config */
169 old_cfg = app->cfg;
170 cfg = srn_application_config_new();
171 ret = srn_config_manager_read_application_config(cfg_mgr, cfg);
172 if (!RET_IS_OK(ret)){
173 goto ERR_RELOAD_APP;
174 }
175 ret = srn_application_config_check(cfg);
176 if (!RET_IS_OK(ret)){
177 goto ERR_RELOAD_APP;
178 }
179 srn_application_set_config(app, cfg);
180 srn_application_config_free(old_cfg);
181
182 /* Update server configs */
183 lst = app->srv_list;
184 while (lst) {
185 SrnServer *srv;
186 SrnServerConfig *srv_cfg;
187 SrnServerConfig *old_srv_cfg;
188
189 srv = lst->data;
190 old_srv_cfg = srv->cfg;
191 srv_cfg = srn_server_config_new();
192
193 ret = srn_config_manager_read_server_config(
194 app->cfg_mgr, srv_cfg, srv->name);
195 if (!RET_IS_OK(ret)){
196 goto ERR_RELOAD_SERVER;
197 }
198 ret = srn_server_config_check(srv_cfg);
199 if (!RET_IS_OK(ret)){
200 goto ERR_RELOAD_SERVER;
201 }
202 srn_server_config_free(old_srv_cfg);
203 srn_server_set_config(srv, srv_cfg);
204
205 ret = srn_server_reload_config(srv);
206 if (!RET_IS_OK(ret)){
207 goto ERR_RELOAD_SERVER;
208 }
209
210 lst = g_list_next(lst);
211 continue;
212
213 ERR_RELOAD_SERVER:
214 if (srv_cfg != srv->cfg){
215 srn_server_config_free(srv_cfg);
216 }
217 return ret;
218 }
219
220 return RET_OK(_("All config reloaded"));
221
222 ERR_RELOAD_LOGGER:
223 srn_logger_config_free(logger_cfg);
224 return RET_ERR(_("Failed to reload logger config: %1$s"),
225 RET_MSG(ret));
226
227 ERR_RELOAD_APP:
228 srn_application_config_free(cfg);
229 return RET_ERR(_("Failed to reload application config: %1$s"),
230 RET_MSG(ret));
231
232 }
233
srn_application_add_server(SrnApplication * app,const char * name)234 SrnRet srn_application_add_server(SrnApplication *app, const char *name){
235 SrnRet ret;
236 SrnServerConfig *srv_cfg;
237
238 srv_cfg = srn_server_config_new(name);
239 ret = srn_config_manager_read_server_config(app->cfg_mgr, srv_cfg, name);
240 if (!RET_IS_OK(ret)){
241 goto ERR;
242 }
243
244 ret = srn_application_add_server_with_config(app, name, srv_cfg);
245 if (!RET_IS_OK(ret)){
246 goto ERR;
247 }
248
249 return SRN_OK;
250
251 ERR:
252 srn_server_config_free(srv_cfg);
253 return ret;
254 }
255
srn_application_add_server_with_config(SrnApplication * app,const char * name,SrnServerConfig * srv_cfg)256 SrnRet srn_application_add_server_with_config(SrnApplication *app,
257 const char *name, SrnServerConfig *srv_cfg) {
258 GList *lst;
259 SrnRet ret;
260 SrnServer *srv;
261
262 lst = app->srv_list;
263 while (lst) {
264 srv = lst->data;
265 if (g_ascii_strcasecmp(srv->name, name) == 0){
266 return RET_ERR(_("Server \"%1$s\" already exists"), name);
267 }
268 lst = g_list_next(lst);
269 }
270
271 ret = srn_server_config_check(srv_cfg);
272 if (!RET_IS_OK(ret)){
273 return ret;
274 }
275
276 srv = srn_server_new(name, srv_cfg);
277 app->cur_srv = srv;
278 app->srv_list = g_list_append(app->srv_list, srv);
279
280 // Create server chat
281 ret = srn_server_add_chat(srv, srv->name);
282 if (!RET_IS_OK(ret)){
283 return ret;
284 }
285
286 /* Auto join chat */
287 // FIXME: This should be done in server.c?
288 for (GList *lst = srv->cfg->auto_join_chat_list;
289 lst;
290 lst = g_list_next(lst)){
291 const char *name;
292
293 name = lst->data;
294 ret = srn_server_add_chat(srv, name);
295 if (!RET_IS_OK(ret)){
296 ret = RET_ERR(_("Failed to add chat \"%1$s\": %2$s"),
297 name, RET_MSG(ret));
298 sui_message_box(_("Error"), RET_MSG(ret));
299 continue;
300 }
301 }
302
303 /* Run server autorun commands */
304 for (GList *lst = srv->cfg->auto_run_cmd_list; lst; lst = g_list_next(lst)){
305 const char *cmd;
306 SrnRet ret;
307 SrnChat *chat;
308
309 cmd = lst->data;
310 chat = srv->chat;
311 ret = srn_chat_run_command(chat, cmd);
312
313 // NOTE: The server and chat may be invlid after running command
314 if (!srn_server_is_valid(srv) || !srn_server_is_chat_valid(srv, chat)){
315 return ret;
316 }
317
318 if (RET_IS_OK(ret)){
319 if (ret != SRN_OK) { // Has OK message
320 srn_chat_add_misc_message_fmt(chat,
321 _("Autorun command: %1$s"), RET_MSG(ret));
322 }
323 } else {
324 srn_chat_add_error_message_fmt(chat,
325 _("Autorun command: %1$s"), RET_MSG(ret));
326 }
327 }
328
329 return SRN_OK;
330 }
331
srn_application_rm_server(SrnApplication * app,SrnServer * srv)332 SrnRet srn_application_rm_server(SrnApplication *app, SrnServer *srv) {
333 GList *lst;
334 SrnServerConfig *srv_cfg;
335
336 lst = g_list_find(app->srv_list, srv);
337 if (!lst){
338 return SRN_ERR;
339 }
340 if (app->cur_srv == srv) {
341 app->cur_srv = NULL;
342 }
343 app->srv_list = g_list_delete_link(app->srv_list, lst);
344
345 srv_cfg = srv->cfg;
346 srn_server_free(srv);
347 srn_server_config_free(srv_cfg);
348
349 return SRN_OK;
350 }
351
srn_application_get_server(SrnApplication * app,const char * name)352 SrnServer* srn_application_get_server(SrnApplication *app, const char *name){
353 GList *lst;
354
355 lst = app->srv_list;
356 while (lst) {
357 SrnServer *srv;
358
359 srv = lst->data;
360 if (g_ascii_strcasecmp(srv->name, name) == 0){
361 return srv;
362 }
363 lst = g_list_next(lst);
364 }
365
366 return NULL;
367 }
368
srn_application_get_server_by_addr(SrnApplication * app,SrnServerAddr * addr)369 SrnServer* srn_application_get_server_by_addr(SrnApplication *app,
370 SrnServerAddr *addr){
371 GList *lst;
372
373 lst = app->srv_list;
374 while (lst) {
375 GList *addr_lst;
376 SrnServer *srv;
377
378 srv = lst->data;
379 addr_lst = srv->cfg->addrs;
380 while (addr_lst){
381 if (srn_server_addr_equal(addr, addr_lst->data)){
382 return srv;
383 }
384 addr_lst = g_list_next(addr_lst);
385 }
386 lst = g_list_next(lst);
387 }
388
389 return NULL;
390 }
391
srn_application_is_server_valid(SrnApplication * app,SrnServer * srv)392 bool srn_application_is_server_valid(SrnApplication *app, SrnServer *srv) {
393 return g_list_find(app->srv_list, srv) != NULL;
394 }
395
srn_application_auto_connect_server(SrnApplication * app)396 void srn_application_auto_connect_server(SrnApplication *app) {
397 SrnRet ret;
398
399 for (GList *lst = app->cfg->auto_connect_srv_list;
400 lst;
401 lst = g_list_next(lst)){
402 const char *name;
403
404 name = lst->data;
405 ret = srn_application_add_server(app, name);
406 if (!RET_IS_OK(ret)){
407 ret = RET_ERR(_("Failed to add server \"%1$s\": %2$s"),
408 name, RET_MSG(ret));
409 sui_message_box(_("Error"), RET_MSG(ret));
410 continue;
411 }
412 srn_server_connect(srn_application_get_server(app, name));
413 }
414 }
415
416 /*****************************************************************************
417 * Static functions
418 *****************************************************************************/
419
init_logger(SrnApplication * app)420 static void init_logger(SrnApplication *app) {
421 SrnRet ret;
422
423 app->logger_cfg = srn_logger_config_new();
424 ret = srn_config_manager_read_log_config(app->cfg_mgr, app->logger_cfg);
425 if (!RET_IS_OK(ret)) {
426 // TODO
427 }
428 app->logger = srn_logger_new(app->logger_cfg);
429 srn_logger_set_default(app->logger);
430 }
431
finalize_logger(SrnApplication * app)432 static void finalize_logger(SrnApplication *app) {
433 srn_logger_free(app->logger);
434 srn_logger_config_free(app->logger_cfg);
435 }
436