1
2 /*
3 * Osmo - a handy personal organizer
4 *
5 * Copyright (C) 2007 Tomasz Maka <pasp@users.sourceforge.net>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "gui.h"
23 #include "calendar.h"
24 #include "calendar_utils.h"
25 #include "utils.h"
26 #include "utils_date.h"
27 #include "utils_gui.h"
28 #include "i18n.h"
29 #include "check_events.h"
30 #include "options_prefs.h"
31 #include "contacts.h"
32
33 #ifdef BACKUP_SUPPORT
34 #include "backup.h"
35 #endif /* BACKUP_SUPPORT */
36
37 #define RUN_FLAG_FILE "lock"
38 #define OLD_CONFIG_DIRNAME ".osmo"
39 #ifdef HAVE_LIBWEBKIT
40 #define MAX_STYLESHEET_SIZE 1024*1024
41 #endif /* HAVE_LIBWEBKIT */
42
43 static gboolean migrate_to_xdg_dirs(gchar *user_config, GUI *appGUI);
44 static gboolean extract_command_line_args(GOptionEntry *options, gint *argc, gchar ***argv, GUI *appGUI);
45 #ifdef HAVE_LIBWEBKIT
46 static gchar* load_stylesheet(const gchar* file);
47 #endif /* HAVE_LIBWEBKIT */
48 /*------------------------------------------------------------------------------*/
49
main(int argc,char ** argv)50 int main(int argc, char **argv) {
51
52 GUI *appGUI = NULL;
53 CALENDAR *cal = NULL;
54
55 #ifdef TASKS_ENABLED
56 TASKS *tsk = NULL;
57 #endif /* TASKS_ENABLED */
58
59 #ifdef CONTACTS_ENABLED
60 CONTACTS *cnt = NULL;
61 #endif /* CONTACTS_ENABLED */
62
63 #ifdef NOTES_ENABLED
64 NOTES *nte = NULL;
65 #endif /* NOTES_ENABLED */
66
67 OPTIONS *opt = NULL;
68 GtkWidget *info_dialog;
69 gchar tmpbuf[BUFFER_SIZE];
70 gint response = -1;
71 #ifndef WIN32
72 struct flock *s_lock = NULL;
73 #endif /* WIN32 */
74 int fhandle = 0;
75 gchar* output_codeset = NULL;
76
77 gboolean cmd_calendar = FALSE;
78 gboolean cmd_check_events = FALSE;
79 gint cmd_check_ndays_events = 0;
80 gchar *cmd_cfg_path = NULL;
81 #ifdef CONTACTS_ENABLED
82 gchar *cmd_mutt_query = NULL;
83 #endif /* CONTACTS_ENABLED */
84 #ifdef HAVE_LIBWEBKIT
85 gchar *cmd_stylesheet_file = NULL;
86 #endif /* HAVE_LIBWEBKIT */
87 GOptionEntry cmd_options[] = {
88 { "calendar", 'c', 0, G_OPTION_ARG_NONE, &cmd_calendar, N_("Show small calendar window"), NULL },
89 { "check", 'e', 0, G_OPTION_ARG_NONE, &cmd_check_events, N_("Check for events since last run"), NULL },
90 { "days", 'd', 0, G_OPTION_ARG_INT, &cmd_check_ndays_events, N_("Number of days to check forward for events (default: 0)"), NULL },
91 { "config", 's', 0, G_OPTION_ARG_STRING, &cmd_cfg_path, N_("Set absolute path for settings and data files"), "STRING" },
92 #ifdef CONTACTS_ENABLED
93 { "mutt-query", 'q', 0, G_OPTION_ARG_STRING, &cmd_mutt_query, N_("Match contacts with given string"), "STRING" },
94 #endif /* CONTACTS_ENABLED */
95 #ifdef HAVE_LIBWEBKIT
96 { "html-stylesheet", 0, 0, G_OPTION_ARG_STRING, &cmd_stylesheet_file, N_("HTML renderer stylesheet file"), "STRING" },
97 #endif /* HAVE_LIBWEBKIT */
98 { NULL }
99 };
100
101 appGUI = g_new0 (GUI, 1);
102 g_return_val_if_fail (appGUI != NULL, -1);
103
104 cal = g_new0 (CALENDAR, 1);
105 g_return_val_if_fail (cal != NULL, -1);
106
107 #ifdef TASKS_ENABLED
108 tsk = g_new0 (TASKS, 1);
109 g_return_val_if_fail (tsk != NULL, -1);
110 #endif /* TASKS_ENABLED */
111
112 #ifdef CONTACTS_ENABLED
113 cnt = g_new0 (CONTACTS, 1);
114 g_return_val_if_fail (cnt != NULL, -1);
115 #endif /* CONTACTS_ENABLED */
116
117 #ifdef NOTES_ENABLED
118 nte = g_new0 (NOTES, 1);
119 g_return_val_if_fail (nte != NULL, -1);
120 #endif /* NOTES_ENABLED */
121
122 opt = g_new0 (OPTIONS, 1);
123 g_return_val_if_fail (opt != NULL, -1);
124
125 /* register modules */
126 appGUI->cal = cal;
127
128 #ifdef TASKS_ENABLED
129 appGUI->tsk = tsk;
130 #endif /* TASKS_ENABLED */
131
132 #ifdef CONTACTS_ENABLED
133 appGUI->cnt = cnt;
134 #endif /* CONTACTS_ENABLED */
135
136 #ifdef NOTES_ENABLED
137 appGUI->nte = nte;
138 #endif /* NOTES_ENABLED */
139
140 appGUI->opt = opt;
141
142 appGUI->run_date = utl_date_get_current_julian ();
143 appGUI->run_time = utl_time_get_current_seconds ();
144 appGUI->key_counter = 0;
145
146 /* default values */
147 appGUI->hovering_over_link = FALSE;
148 appGUI->hand_cursor = NULL;
149 appGUI->regular_cursor = NULL;
150 appGUI->gui_url_tag = NULL;
151 appGUI->trayicon_popup_menu = NULL;
152
153 appGUI->calendar_only = FALSE;
154 appGUI->cal->datecal_bio = FALSE;
155 appGUI->check_events = FALSE;
156 appGUI->save_status = 0;
157 appGUI->print_font_size = 10;
158
159 g_sprintf (appGUI->version, "%02d%02d%02d", atoi(VERSION_MAJOR), atoi(VERSION_MINOR), atoi(VERSION_MICRO));
160
161 appGUI->cal->last_selected_year = -1;
162 appGUI->cal->date = utl_date_new_current ();
163 appGUI->about_links_list = NULL;
164
165 #ifdef TASKS_ENABLED
166 appGUI->tsk->next_id = 1;
167 appGUI->tsk->tasks_list_store = NULL;
168 appGUI->tsk->notifications = NULL;
169 appGUI->tsk->notifications_enable = FALSE;
170 #endif /* TASKS_ENABLED */
171
172 #ifdef CONTACTS_ENABLED
173 appGUI->cnt->export_button = NULL;
174 appGUI->cnt->output_file_entry = NULL;
175 appGUI->cnt->write_flag = TRUE;
176 #endif /* CONTACTS_ENABLED */
177
178 if (!(output_codeset = bind_textdomain_codeset(PACKAGE, NULL))) {
179 output_codeset = g_strdup("UTF-8");
180 } else {
181 output_codeset = g_strdup(output_codeset);
182 }
183
184 setlocale (LC_ALL, "");
185 bindtextdomain (PACKAGE, LOCALEDIR);
186
187 if (bind_textdomain_codeset(PACKAGE, output_codeset) == NULL) {
188 fprintf(stderr, "Error setting gettext output codeset to %s\n", output_codeset);
189 g_free (output_codeset);
190 goto finish;
191 }
192
193 textdomain (PACKAGE);
194 g_free (output_codeset);
195
196 if (extract_command_line_args(cmd_options, &argc, &argv, appGUI) == FALSE) {
197 goto finish;
198 }
199
200 appGUI->calendar_only = cmd_calendar;
201 appGUI->check_events = cmd_check_events;
202 appGUI->check_ndays_events = cmd_check_ndays_events;
203 if(cmd_cfg_path) {
204 fprintf(stderr, "%s\n", _("The user config path option is deprecated. Use XDG environment variables instead."));
205 }
206 if(migrate_to_xdg_dirs(cmd_cfg_path, appGUI) == FALSE) {
207 goto finish;
208 }
209 if (prefs_get_config_filename (CONFIG_FILENAME, appGUI) == NULL) {
210 fprintf(stderr, "%s\n", _("ERROR: Cannot create config files"));
211 goto finish;
212 }
213
214 #if defined(BACKUP_SUPPORT) && defined(HAVE_LIBGRINGOTTS)
215 backup_restore_run (appGUI);
216 #endif /* BACKUP_SUPPORT & HAVE_LIBGRINGOTTS */
217
218 #ifndef WIN32
219 s_lock = g_new0 (struct flock, 1);
220 s_lock->l_type = F_WRLCK;
221 s_lock->l_whence = SEEK_SET;
222 s_lock->l_start = 0;
223 s_lock->l_len = 0;
224 #endif /* WIN32 */
225
226 prefs_read_config (appGUI);
227
228 gtk_init (&argc, &argv);
229
230 #ifdef CONTACTS_ENABLED
231 if (cmd_mutt_query)
232 {
233 query (appGUI, cmd_mutt_query);
234 goto finish;
235 }
236 #endif /* CONTACTS_ENABLED */
237
238 if (appGUI->calendar_only == FALSE) {
239
240 close(creat(prefs_get_runtime_filename (RUN_FLAG_FILE, appGUI), S_IRUSR | S_IWUSR)); /* create lock file */
241
242 fhandle = open(prefs_get_runtime_filename (RUN_FLAG_FILE, appGUI), O_RDWR);
243 #ifndef WIN32
244 if (fhandle) {
245 if (fcntl(fhandle, F_SETLK, s_lock) == -1) {
246 close (fhandle);
247
248 g_snprintf (tmpbuf, BUFFER_SIZE, "%s %s\n\n%s",
249 _("Another copy of OSMO is already running."),
250 _("Simultaneously use two or more copies of OSMO can be a cause of data loss."),
251 _("Do you really want to continue?"));
252
253 info_dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW_TOPLEVEL, GTK_DIALOG_MODAL,
254 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, tmpbuf, NULL);
255
256 gtk_window_set_type_hint (GTK_WINDOW (info_dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
257 gtk_window_set_skip_pager_hint(GTK_WINDOW(info_dialog), TRUE);
258 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(info_dialog), TRUE);
259 gtk_window_set_keep_above(GTK_WINDOW(info_dialog), TRUE);
260 gtk_window_set_title(GTK_WINDOW(info_dialog), _("Warning"));
261 gtk_window_set_position(GTK_WINDOW(info_dialog), GTK_WIN_POS_CENTER);
262 gtk_widget_show (info_dialog);
263
264 response = gtk_dialog_run(GTK_DIALOG(info_dialog));
265 gtk_widget_destroy(info_dialog);
266
267 if (response == GTK_RESPONSE_NO || response == GTK_RESPONSE_DELETE_EVENT) {
268 goto finish;
269 }
270 }
271 }
272 #endif /* WIN32 */
273 }
274
275
276 #ifdef HAVE_LIBNOTIFY
277 #ifdef TASKS_ENABLED
278 notify_init("Osmo notification");
279 #endif /* TASKS_ENABLED */
280 #endif /* HAVE_LIBNOTIFY */
281
282 if (appGUI->calendar_only != TRUE) {
283 /* setup timer at 1000ms (1s) interval */
284 appGUI->timer = g_timeout_add (1000, time_handler, appGUI);
285 }
286
287 config.run_counter++;
288
289 #ifndef REV
290 g_set_application_name ("Osmo " VERSION_MAJOR "." VERSION_MINOR "." VERSION_MICRO);
291 #else
292 g_set_application_name ("Osmo (development)");
293 #endif
294
295 #ifdef HAVE_LIBWEBKIT
296 if(cmd_stylesheet_file) {
297 appGUI->stylesheet = load_stylesheet(cmd_stylesheet_file);
298 }
299 #endif /* HAVE_LIBWEBKIT */
300
301 gtk_window_set_default_icon_name ("osmo");
302
303 if (gui_create_window (appGUI) == TRUE) {
304 gtk_main ();
305 }
306
307 if (appGUI->calendar_only == FALSE) {
308 config.lastrun_date = utl_date_get_current_julian ();
309 config.lastrun_time = utl_time_get_current_seconds ();
310 }
311
312 if (appGUI->check_events == FALSE) {
313 prefs_write_config (appGUI);
314 }
315
316 if (fhandle && appGUI->calendar_only == FALSE) {
317 #ifndef WIN32
318 s_lock->l_type = F_UNLCK;
319 fcntl(fhandle, F_SETLK, s_lock);
320 #endif /* WIN32 */
321 close (fhandle);
322 }
323
324 finish:
325 g_date_free (appGUI->cal->date);
326 utl_gui_url_remove_links (&appGUI->about_links_list, NULL);
327 if (appGUI->hand_cursor != NULL) {
328 g_object_unref (appGUI->hand_cursor);
329 g_object_unref (appGUI->regular_cursor);
330 }
331 g_free (opt);
332
333 #ifdef NOTES_ENABLED
334 g_free (nte);
335 #endif /* NOTES_ENABLED */
336
337 #ifdef CONTACTS_ENABLED
338 g_free (cnt);
339 #endif /* CONTACTS_ENABLED */
340
341 #ifdef TASKS_ENABLED
342 g_free (tsk);
343 #endif /* TASKS_ENABLED */
344
345 #ifdef HAVE_LIBWEBKIT
346 g_free(appGUI->stylesheet);
347 #endif /* HAVE_LIBWEBKIT */
348
349 g_free (cal);
350 g_free (appGUI);
351 g_free(cmd_cfg_path);
352 #ifdef CONTACTS_ENABLED
353 g_free(cmd_mutt_query);
354 #endif /* CONTACTS_ENABLED */
355 #ifdef HAVE_LIBWEBKIT
356 g_free(cmd_stylesheet_file);
357 #endif /* HAVE_LIBWEBKIT */
358 #ifndef WIN32
359 g_free (s_lock);
360 #endif /* WIN32 */
361 return 0;
362 }
363
364 /*------------------------------------------------------------------------------*/
365 /* This is a temporary migration from the old Osmo .osmo directory to */
366 /* XDG directories. DELETE IT after the migration support is no longer needed. */
367 /*------------------------------------------------------------------------------*/
368
369 gchar*
get_old_config_dir(gchar * user_config,GUI * appGUI)370 get_old_config_dir (gchar *user_config, GUI *appGUI) {
371
372 static gchar dirname[PATH_MAX];
373
374 if (user_config == NULL) {
375 #if defined(CONFIG_PATH) && defined(CONFIG_DIR)
376 g_snprintf (dirname, PATH_MAX, "%s%c%s", CONFIG_PATH, G_DIR_SEPARATOR, CONFIG_DIR);
377 #elif defined(CONFIG_DIR)
378 g_snprintf (dirname, PATH_MAX, "%s%c%s", g_get_home_dir(), G_DIR_SEPARATOR, CONFIG_DIR);
379 #elif defined(CONFIG_PATH)
380 g_snprintf (dirname, PATH_MAX, "%s%c%s", CONFIG_PATH, G_DIR_SEPARATOR, OLD_CONFIG_DIRNAME);
381 #else
382 g_snprintf (dirname, PATH_MAX, "%s%c%s", g_get_home_dir(), G_DIR_SEPARATOR, OLD_CONFIG_DIRNAME);
383 #endif
384 } else {
385 g_strlcpy (dirname, user_config, PATH_MAX);
386 }
387
388 return dirname;
389 }
390
391 static gboolean
migrate_to_xdg_dirs(gchar * user_config,GUI * appGUI)392 migrate_to_xdg_dirs(gchar *user_config, GUI *appGUI) {
393 gchar *config = prefs_get_config_filename(CONFIG_FILENAME, appGUI);
394 if (!g_file_test(config, G_FILE_TEST_EXISTS)) {
395 gchar *old_dirname = get_old_config_dir(user_config, appGUI);
396 if (g_file_test(old_dirname, G_FILE_TEST_EXISTS)) {
397 fprintf(stderr, _("The old Osmo config directory %s is found. "
398 "The config files and data will be copied to XDG compatible directories. "
399 "Delete the old config directory after a successful migration.\n"),
400 old_dirname);
401 return prefs_restore(old_dirname, appGUI);
402 }
403 }
404 return TRUE;
405 }
406 /*------------------------------------------------------------------------------*/
407 static gboolean
extract_command_line_args(GOptionEntry * options,gint * argc,gchar *** argv,GUI * appGUI)408 extract_command_line_args(GOptionEntry *options, gint *argc, gchar ***argv, GUI *appGUI) {
409 GOptionContext *cmd_context;
410 gchar *program_summary;
411 GError *err = NULL;
412
413 program_summary = g_strdup_printf(N_("OSMO v%s (handy personal organizer)\n"
414 "Copyright (c) 2007-2017 Tomasz Maka <pasp@users.sourceforge.net>\n\n"
415 "Configuration directory = %s\n"
416 "Data directory = %s"),
417 VERSION, prefs_get_config_filename("", appGUI), prefs_get_data_filename("", appGUI));
418
419 cmd_context = g_option_context_new(NULL);
420 g_option_context_set_translation_domain(cmd_context, TRANSLATION_DOMAIN);
421 g_option_context_set_summary(cmd_context, program_summary);
422 g_free(program_summary);
423 g_option_context_add_main_entries(cmd_context, options, TRANSLATION_DOMAIN);
424 if (g_option_context_parse(cmd_context, argc, argv, &err) == FALSE) {
425 fprintf(stderr, _("Failed to parse the command line arguments %s.\n"), err->message);
426 g_option_context_free(cmd_context);
427 g_error_free (err);
428 return FALSE;
429 }
430 g_option_context_free(cmd_context);
431 return TRUE;
432 }
433 /*------------------------------------------------------------------------------*/
434 #ifdef HAVE_LIBWEBKIT
435 static gchar*
load_stylesheet(const gchar * file)436 load_stylesheet(const gchar* file) {
437 GStatBuf st;
438 gchar *content;
439
440 g_return_val_if_fail(g_stat(file, &st) == 0, NULL);
441 if (st.st_size > MAX_STYLESHEET_SIZE) {
442 fprintf(stderr, _("Stylesheet file is too large. Max size is %d.\n"), MAX_STYLESHEET_SIZE);
443 return NULL;
444 }
445 g_return_val_if_fail(g_file_get_contents(file, &content, NULL, NULL), NULL);
446 return content;
447 }
448 /*------------------------------------------------------------------------------*/
449 #endif /* HAVE_LIBWEBKIT */
450