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