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