~ubuntu-core-dev/update-notifier/ubuntu

« back to all changes in this revision

Viewing changes to src/hooks.c

  • Committer: Julian Andres Klode
  • Date: 2019-04-02 13:18:28 UTC
  • mfrom: (957.1.5 esm)
  • Revision ID: juliank@ubuntu.com-20190402131828-n7brmzl4gwgvshoe
* Rewrite and extend motd messaging (LP: #1822340)
* Count ESM security updates as security updates

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
#include "hooks.h"
19
19
#include "rfc822.h"
20
20
#include "assert.h"
21
 
 
 
21
#include "trayappletui.h"
22
22
 
23
23
/* relative to the home dir */
24
24
#define HOOKS_SEEN_DEPRECATED ".update-notifier/hooks_seen"
41
41
   va_end(va);
42
42
}
43
43
 
44
 
void hooks_trayicon_update_tooltip (TrayApplet *un, int num_hooks)
45
 
{
46
 
   g_debug_hooks("update_tooltip: %p ", un);
47
 
   gchar *updates;
48
 
 
49
 
   updates = _("Information available");
50
 
 
51
 
   gtk_status_icon_set_tooltip(un->tray_icon, updates);
52
 
}
53
 
 
54
 
 
55
44
// compare a HookFile with a filename and find the HookFile that matches
56
 
gint compare_hook_func(gconstpointer a, gconstpointer b)
 
45
static gint
 
46
compare_hook_func(gconstpointer a, gconstpointer b)
57
47
{
58
 
   //g_debug_hooks("compare: %s %s\n",(char*)(((HookFileSeen*)a)->filename),(char*)b);
 
48
   //g_debug_hooks("compare: %s %s",(char*)(((HookFileSeen*)a)->filename),(char*)b);
59
49
   g_assert(a);
60
50
   g_assert(b);
61
51
 
63
53
}
64
54
 
65
55
// return the most recent mtime or ctime of the file
66
 
time_t hook_file_time(const gchar *filename)
 
56
static time_t
 
57
hook_file_time(const gchar *filename)
67
58
{
68
59
   struct stat buf;
69
60
   char *file = g_strdup_printf("%s/%s",HOOKS_DIR, filename);
70
61
   if(g_stat(file, &buf) <0) {
71
 
      g_warning("can't stat %s\n",file);
 
62
      g_warning("can't stat %s",file);
72
63
      g_free(file);
73
64
      return 0;
74
65
   }
80
71
   return mtime > ctime ? mtime : ctime;
81
72
}
82
73
 
83
 
gboolean hook_file_md5(const gchar *filename, guint8 *md5)
 
74
static gboolean
 
75
hook_file_md5(const gchar *filename, guint8 *md5)
84
76
{
85
77
   guchar buf[512];
86
78
   FILE *f;
91
83
   file = g_strdup_printf("%s/%s",HOOKS_DIR, filename);
92
84
   f = fopen(file,"r");
93
85
   if(f == NULL) {
94
 
      g_warning("can't read %s\n",file);
 
86
      g_warning("can't read %s",file);
95
87
      g_free(file);
96
88
      return FALSE;
97
89
   }
103
95
   
104
96
   n=DIGEST_SIZE;
105
97
   g_checksum_get_digest(checksum, md5, &n);
106
 
   //g_debug_hooks("md5: %s -> '%s' ", filename, md5);
 
98
   //g_debug_hooks("md5: %s -> '%s'", filename, md5);
107
99
 
108
100
   g_free(file);
109
101
   g_checksum_free(checksum);
113
105
/* mark a given hook file as seen 
114
106
  (actually implemented to write out all the information we have)
115
107
*/
116
 
gboolean hook_file_mark_as_seen(HookTrayAppletPrivate *priv, 
117
 
                                HookFile *hf)
 
108
static gboolean
 
109
hook_file_mark_as_seen(HookTrayAppletPrivate *priv, HookFile *hf)
118
110
{
119
111
   g_debug_hooks("mark_hook_file_as_seen: %s", hf->filename);
120
112
 
134
126
                                     HOOKS_SEEN);
135
127
   FILE *f = fopen(filename, "w");
136
128
   if(f==NULL) {
137
 
      g_warning("Something went wrong writing the users hook file");
 
129
      g_warning("Something went wrong writing the user's hook file");
138
130
      return FALSE;
139
131
   }
140
132
 
141
133
   // write out all the hooks that are seen 
142
134
   //
143
135
   // and any hooks that are not yet seen but have the same md5sum 
144
 
   // as the one just marked (avoids showing duplicated hooks effecivly). 
 
136
   // as the one just marked (avoids showing duplicated hooks effectively). 
145
137
   //
146
138
   // For hooks with the same md5sum use the mtime of the just displayed hook
147
139
   GList *elm = g_list_first(priv->hook_files);
165
157
}
166
158
 
167
159
/* mark a given HookFile as run */
168
 
gboolean mark_hook_file_as_run(HookTrayAppletPrivate *priv, HookFile *hf)
 
160
static gboolean
 
161
mark_hook_file_as_run(HookTrayAppletPrivate *priv, HookFile *hf)
169
162
{
170
163
   if(hf == NULL)
171
164
      return FALSE;
181
174
 * short_form: only return the languagecode if true, otherwise
182
175
 *             languagecode_countrycode
183
176
 */
184
 
char* get_lang_code(gboolean short_form)
 
177
static char *
 
178
get_lang_code(gboolean short_form)
185
179
{
186
180
   /* make own private copy */
187
181
   static gchar locale[51];
202
196
 * get a i18n field of the rfc822 header
203
197
 * Return Value: a pointer that must not be freed (part of the rfc822 struct)
204
198
 */
205
 
char *hook_file_lookup_i18n(struct rfc822_header *rfc822, char *field)
 
199
static char *
 
200
hook_file_lookup_i18n(struct rfc822_header *rfc822, char *field)
206
201
{
207
202
   gchar *s, *entry, *text;
208
203
 
218
213
   /* try $field-$languagecode_$countrycode.$codeset first */
219
214
   s = g_strdup_printf("%s-%s.%s", field, get_lang_code(FALSE), nl_langinfo(CODESET));
220
215
   entry = rfc822_header_lookup(rfc822, s);
221
 
   //g_debug_hooks("Looking for: %s ; found: %s\n",s,entry);
 
216
   //g_debug_hooks("Looking for: %s ; found: %s",s,entry);
222
217
   g_free(s);
223
218
   if(entry != NULL)
224
219
      return entry;
226
221
   /* try $field-$languagecode_$countrycode (and assume utf-8) */
227
222
   s = g_strdup_printf("%s-%s", field, get_lang_code(FALSE));
228
223
   entry = rfc822_header_lookup(rfc822, s);
229
 
   //g_debug_hooks("Looking for: %s ; found: %s\n",s,entry);
 
224
   //g_debug_hooks("Looking for: %s ; found: %s",s,entry);
230
225
   g_free(s);
231
226
   if(entry != NULL)
232
227
      return entry;
233
228
 
234
229
   /* try $field-$languagecode.$codeset next */
235
230
   s = g_strdup_printf("%s-%s.%s", field, get_lang_code(TRUE), nl_langinfo(CODESET));
236
 
   //g_debug_hooks("Looking for: %s ; found: %s\n",s,entry);
 
231
   //g_debug_hooks("Looking for: %s ; found: %s",s,entry);
237
232
   entry = rfc822_header_lookup(rfc822, s);
238
233
   g_free(s);
239
234
   if(entry != NULL)
241
236
 
242
237
   /* try $field-$languagecode (and assume utf-8 codeset) */
243
238
   s = g_strdup_printf("%s-%s", field, get_lang_code(TRUE));
244
 
   //g_debug_hooks("Looking for: %s ; found: %s\n",s,entry);
 
239
   //g_debug_hooks("Looking for: %s ; found: %s",s,entry);
245
240
   entry = rfc822_header_lookup(rfc822, s);
246
241
   g_free(s);
247
242
   if(entry != NULL)
252
247
   return entry;
253
248
}
254
249
 
255
 
char* hook_description_get_summary(struct rfc822_header *rfc822)
 
250
static char *
 
251
hook_description_get_summary(struct rfc822_header *rfc822)
256
252
{
257
253
   char *summary = hook_file_lookup_i18n(rfc822, "Name");
258
254
   return summary;
259
255
}
260
256
 
261
 
char* hook_description_get_description(struct rfc822_header *rfc822)
 
257
static char *
 
258
hook_description_get_description(struct rfc822_header *rfc822)
262
259
{
263
260
   char *description = hook_file_lookup_i18n(rfc822, "Description");
264
261
   return description;
267
264
/*
268
265
 * show the given hook file
269
266
 */
270
 
gboolean show_next_hook(TrayApplet *ta, GList *hooks)
 
267
static gboolean
 
268
show_next_hook(TrayApplet *ta, GList *hooks)
271
269
{
272
 
   //g_debug_hooks("show_next_hook()\n");
 
270
   //g_debug_hooks("show_next_hook()");
273
271
 
274
272
   HookTrayAppletPrivate *priv = (HookTrayAppletPrivate *)ta->user_data;
275
 
   GtkBuilder *builder = priv->gtk_builder;
276
 
 
277
 
   GtkWidget *title = GTK_WIDGET (gtk_builder_get_object(builder, "label_title"));
278
 
   assert(title);
279
 
   GtkWidget *text = GTK_WIDGET (gtk_builder_get_object(builder, "textview_hook"));
280
 
   assert(text);
281
 
   GtkWidget *button_next = GTK_WIDGET (gtk_builder_get_object(builder, "button_next"));
282
 
   assert(button_next);
283
 
   GtkWidget *button_run = GTK_WIDGET (gtk_builder_get_object(builder, "button_run"));
284
 
   assert(button_run);
285
273
 
286
274
   // init some vars
287
275
   HookFile *hf = NULL;
293
281
      hf = (HookFile*)elm->data;
294
282
      if(hf->seen == FALSE) {
295
283
         hook_file = hf->filename;
296
 
         //g_debug_hooks("next_hook is: %s\n",hook_file);
 
284
         //g_debug_hooks("next_hook is: %s",hook_file);
297
285
         break;
298
286
      }
299
287
   }
304
292
      return FALSE;
305
293
   }
306
294
 
 
295
   /* if there's another unseen hook, show the "next" button */
 
296
   gboolean show_next_button = FALSE;
 
297
   if(elm) {
 
298
      for(elm = g_list_next(elm); elm != NULL; elm = g_list_next(elm)) {
 
299
         if(((HookFile*)elm->data)->seen == FALSE) {
 
300
            show_next_button = TRUE;
 
301
            break;
 
302
         }
 
303
      }
 
304
   }
 
305
   if(show_next_button)
 
306
      gtk_widget_show(priv->button_next);
 
307
   else
 
308
      gtk_widget_hide(priv->button_next);
 
309
 
307
310
   char *filename = g_strdup_printf("%s%s",HOOKS_DIR,hook_file);
308
311
 
309
312
   /* setup the message */
310
 
   GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
 
313
   GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview_hook));
311
314
   FILE *f = fopen(filename, "r");
312
315
   if(f == NULL) {
313
316
      g_warning("can't open %s", filename);
317
320
   struct rfc822_header *rfc822 = rfc822_parse_stanza(f);
318
321
 
319
322
   char *cmd = rfc822_header_lookup(rfc822, "Command");
320
 
   char *term = g_strstrip(rfc822_header_lookup(rfc822, "Terminal"));
321
 
   g_object_set_data(G_OBJECT(button_run),"cmd", g_strdup(cmd));
322
 
   g_object_set_data(G_OBJECT(button_run),"term", g_strdup(term));
323
 
   g_object_set_data(G_OBJECT(button_run),"hook_file", hf);
 
323
   char *term = rfc822_header_lookup(rfc822, "Terminal");
 
324
   if(term)
 
325
      term = g_strstrip(term);
 
326
   g_object_set_data(G_OBJECT(priv->button_run),"cmd", g_strdup(cmd));
 
327
   g_object_set_data(G_OBJECT(priv->button_run),"term", g_strdup(term));
 
328
   g_object_set_data(G_OBJECT(priv->button_run),"hook_file", hf);
324
329
   if(cmd != NULL) {
325
 
      gtk_widget_show(button_run);
 
330
      gtk_widget_show(priv->button_run);
326
331
   } else {
327
 
      gtk_widget_hide(button_run);
 
332
      gtk_widget_hide(priv->button_run);
328
333
   }
329
334
   char *title_str = hook_file_lookup_i18n(rfc822, "Title");
330
335
   if(title_str != NULL) {
331
336
      gchar *s = g_strdup_printf("<span weight=\"bold\" size=\"larger\">%s</span>", title_str);
332
 
      gtk_label_set_markup(GTK_LABEL(title), s);
 
337
      gtk_label_set_markup(GTK_LABEL(priv->label_title), s);
333
338
      g_free(s);
334
339
   }
335
340
   char *summary = hook_description_get_summary(rfc822);
352
357
   // set button name (if needed)
353
358
   char *b = hook_file_lookup_i18n(rfc822, "ButtonText");
354
359
   if(b)
355
 
      gtk_button_set_label(GTK_BUTTON (button_run), b);
 
360
      gtk_button_set_label(GTK_BUTTON (priv->button_run), b);
356
361
   else
357
 
      gtk_button_set_label(GTK_BUTTON (button_run), _("_Run this action now"));
 
362
      gtk_button_set_label(GTK_BUTTON (priv->button_run), _("_Run this action now"));
358
363
 
359
364
   // clean up
360
365
   fclose(f);
363
368
   rfc822_header_free_all(rfc822);
364
369
 
365
370
   /* mark the current hook file as seen */
366
 
   g_object_set_data(G_OBJECT(button_next), "HookFile", hf);
 
371
   g_object_set_data(G_OBJECT(priv->button_next), "HookFile", hf);
367
372
 
368
373
   return TRUE;
369
374
}
371
376
 
372
377
void on_button_run_clicked(GtkWidget *self, gpointer *data)
373
378
{
374
 
   //g_debug_hooks("cb_button_run()\n");
 
379
   //g_debug_hooks("cb_button_run()");
375
380
   gchar *cmdline;
376
381
 
377
382
   TrayApplet *ta = (TrayApplet *)data;
385
390
   gchar *term = g_object_get_data(G_OBJECT(self), "term");
386
391
 
387
392
   if(cmd == NULL) {
388
 
      g_warning("cmd is NULL\n");
 
393
      g_warning("cmd is NULL");
389
394
      return;
390
395
   }
391
396
 
392
397
   if(term != NULL && !g_ascii_strncasecmp(term, "true",-1)) {
393
 
      cmdline = g_strdup_printf("gnome-terminal -e %s",cmd);
 
398
      cmdline = g_strdup_printf("x-terminal-emulator -e %s",cmd);
394
399
   } else 
395
400
      cmdline = g_strdup(cmd);
396
401
 
407
412
   HookFile *hf;
408
413
   hf = (HookFile*)g_object_get_data(G_OBJECT(self),"HookFile");
409
414
   if(hf == NULL) {
410
 
      g_warning("button_next called without HookFile\n");
 
415
      g_warning("button_next called without HookFile");
411
416
      return;
412
417
   }
413
418
   
414
419
   // mark as seen 
415
420
   hook_file_mark_as_seen(priv, hf);
416
 
   check_update_hooks(ta);
417
421
 
418
422
   if(priv->hook_files != NULL)
419
423
      show_next_hook(ta, priv->hook_files);
420
424
}
421
425
 
422
 
void show_hooks(TrayApplet *ta, gboolean focus_on_map)
423
 
{
 
426
static void
 
427
on_insert_text(GtkTextBuffer *buffer,
 
428
               GtkTextIter   *iter_end,
 
429
               gchar         *text,
 
430
               gint           arg3,
 
431
               gpointer       user_data)
 
432
{
 
433
   GtkTextIter iter, match_start, match_end, match_tmp;
 
434
 
 
435
   // get where we start
 
436
   gtk_text_buffer_get_iter_at_offset(buffer, &iter,
 
437
                                      gtk_text_iter_get_offset(iter_end) - g_utf8_strlen(text,-1));
 
438
   // search for http:// uris
 
439
   while(gtk_text_iter_forward_search(&iter, 
 
440
                                      "http://",  GTK_TEXT_SEARCH_VISIBLE_ONLY, 
 
441
                                      &match_start, &match_end, iter_end))  {
 
442
      match_tmp = match_end;
 
443
      // we found a uri, iterate it until the end is found
 
444
      while(gtk_text_iter_forward_char(&match_tmp)) {
 
445
         gchar *s = gtk_text_iter_get_text(&match_end,&match_tmp);
 
446
         if(strlen(s) < 1)
 
447
            break;
 
448
         char c = s[strlen(s)-1];
 
449
         if(c == ' ' || c == ')' || c == ']' || c == '\n' || c == '\t' || c == '>')
 
450
            break;
 
451
         match_end = match_tmp;
 
452
      }
 
453
      gchar *url = gtk_text_iter_get_text(&match_start, &match_end);
 
454
      //g_print("url: '%s'\n",url);
 
455
      GtkTextTag *tag;
 
456
      tag = gtk_text_buffer_create_tag(buffer, NULL,
 
457
                                       "foreground","blue",
 
458
                                       "underline", PANGO_UNDERLINE_SINGLE,
 
459
                                       NULL);
 
460
      g_object_set_data(G_OBJECT(tag), "url", url);
 
461
      gtk_text_buffer_apply_tag(buffer, tag, &match_start, &match_end);
 
462
      iter = match_end;
 
463
   }
 
464
}
 
465
 
 
466
static void
 
467
on_event_after(GtkWidget *widget,
 
468
               GdkEventButton  *event,
 
469
               gpointer   user_data)      
 
470
{
 
471
   if(event->type != GDK_BUTTON_RELEASE)
 
472
      return;
 
473
   if(event->button != 1)
 
474
      return;
 
475
   gint x,y;
 
476
   gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget),
 
477
                                         GTK_TEXT_WINDOW_WIDGET,
 
478
                                         event->x, event->y,
 
479
                                         &x, &y);
 
480
   GtkTextIter iter;
 
481
   gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y);
 
482
   GSList *tags = gtk_text_iter_get_tags(&iter);
 
483
   for( ; tags != NULL ; tags = tags->next) {
 
484
      gchar *url = g_object_get_data(G_OBJECT(tags->data), "url");
 
485
      if(url != NULL) {
 
486
         //g_print("click: '%s'\n",url);
 
487
         char *argv[] = { "/usr/bin/gnome-open", url, NULL };
 
488
         g_spawn_async(NULL, argv, NULL, 0, NULL, NULL, NULL, NULL);
 
489
         break;
 
490
      }
 
491
   }
 
492
}
 
493
 
 
494
static void
 
495
show_hooks(TrayApplet *ta, gboolean focus_on_map)
 
496
{
 
497
   HookTrayAppletPrivate *priv = (HookTrayAppletPrivate *)ta->user_data;
 
498
   GtkBuilder *builder;
 
499
   GError *error = NULL;
 
500
 
424
501
   g_debug_hooks("show_hooks()");
425
 
   HookTrayAppletPrivate* priv = (HookTrayAppletPrivate *)ta->user_data;
426
 
 
427
 
   GtkBuilder *builder = priv->gtk_builder;
428
 
   GtkWidget *dia = GTK_WIDGET (gtk_builder_get_object(builder, "dialog_hooks"));
429
 
   gtk_window_set_title(GTK_WINDOW(dia), _("Information available"));
430
 
   assert(dia);
431
 
   GtkWidget *button_run = GTK_WIDGET (gtk_builder_get_object(builder, "button_run"));
432
 
   assert(button_run);
433
 
   GtkWidget *button_next = GTK_WIDGET (gtk_builder_get_object(builder, "button_next"));
434
 
   assert(button_next);
435
 
 
436
 
   gtk_window_set_focus_on_map(GTK_WINDOW(dia), focus_on_map);
 
502
 
 
503
   builder = gtk_builder_new ();
 
504
   assert (builder);
 
505
 
 
506
   if (!gtk_builder_add_from_file (builder, UIDIR"hooks-dialog.ui", &error)) {
 
507
      g_warning ("Couldn't load builder file: %s", error->message);
 
508
      g_error_free (error);
 
509
   }
 
510
 
 
511
   gtk_builder_connect_signals (builder, (gpointer)ta);
 
512
 
 
513
   priv->dialog_hooks = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_hooks"));
 
514
   gtk_window_set_title (GTK_WINDOW (priv->dialog_hooks), _("Information available"));
 
515
   assert (priv->dialog_hooks);
 
516
   priv->label_title = GTK_WIDGET (gtk_builder_get_object (builder, "label_title"));
 
517
   assert (priv->label_title);
 
518
   priv->textview_hook = GTK_WIDGET (gtk_builder_get_object (builder, "textview_hook"));
 
519
   assert (priv->textview_hook);
 
520
   priv->button_run = GTK_WIDGET (gtk_builder_get_object (builder, "button_run"));
 
521
   assert (priv->button_run);
 
522
   priv->button_next = GTK_WIDGET (gtk_builder_get_object (builder, "button_next"));
 
523
   assert (priv->button_next);
 
524
 
 
525
   g_object_unref (G_OBJECT (builder));
 
526
 
 
527
   // create a bold tag
 
528
   GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->textview_hook));
 
529
   gtk_text_buffer_create_tag (buf, "bold_tag",
 
530
                               "scale", PANGO_SCALE_LARGE, 
 
531
                               "weight", PANGO_WEIGHT_BOLD,
 
532
                               NULL);  
 
533
   g_signal_connect_after (G_OBJECT (buf), "insert-text",
 
534
                           G_CALLBACK (on_insert_text), NULL);
 
535
   g_signal_connect_after (G_OBJECT (priv->textview_hook), "event-after",
 
536
                           G_CALLBACK (on_event_after), NULL);
 
537
 
 
538
   gtk_window_set_focus_on_map (GTK_WINDOW (priv->dialog_hooks), focus_on_map);
437
539
 
438
540
   // if show_next_hook() fails for some reason don't do anything
439
541
   if(!show_next_hook(ta, priv->hook_files))
440
 
      return;
 
542
      goto out;
441
543
 
442
 
   int res = gtk_dialog_run(GTK_DIALOG(dia));
 
544
   int res = gtk_dialog_run (GTK_DIALOG (priv->dialog_hooks));
443
545
   if(res == GTK_RESPONSE_CLOSE) {
444
546
      // mark the currently current hookfile as seen
445
547
      HookFile *hf;
446
 
      hf = (HookFile*)g_object_get_data(G_OBJECT(button_next),"HookFile");
 
548
      hf = (HookFile*)g_object_get_data (G_OBJECT (priv->button_next), "HookFile");
447
549
      if(hf == NULL) {
448
 
         g_warning("show_hooks called without HookFile\n");
449
 
         return;
 
550
         g_warning("show_hooks called without HookFile");
 
551
         goto out;
450
552
      }
451
553
   
452
554
      // mark as seen 
454
556
      check_update_hooks(ta);
455
557
   }
456
558
 
457
 
   gtk_widget_hide(dia);
 
559
out:
 
560
   g_clear_pointer (&priv->dialog_hooks, gtk_widget_destroy);
 
561
   priv->label_title = NULL;
 
562
   priv->textview_hook = NULL;
 
563
   priv->button_run = NULL;
 
564
   priv->button_next = NULL;
458
565
}
459
566
 
460
567
static gboolean
465
572
 
466
573
   if(priv->active_notification != NULL) {
467
574
      notify_notification_close(priv->active_notification, NULL);
468
 
      priv->active_notification = NULL;
 
575
      g_clear_object(&priv->active_notification);
469
576
   }
470
577
   
471
 
   //g_debug_hooks("left click on hook applet\n");
 
578
   //g_debug_hooks("left click on hook applet");
472
579
   show_hooks(ta, TRUE);
473
580
   return TRUE;
474
581
}
475
582
 
476
 
gboolean is_hook_relevant(const gchar *hook_file)
 
583
static gboolean
 
584
is_hook_relevant(const gchar *hook_file)
477
585
{
478
586
   g_debug_hooks("is_hook_relevant(): %s", hook_file);
479
587
   gboolean res = TRUE;
543
651
   TrayApplet *ta = (TrayApplet*)data;
544
652
   HookTrayAppletPrivate *priv = (HookTrayAppletPrivate*)ta->user_data;
545
653
 
546
 
   if(!gtk_status_icon_get_visible(ta->tray_icon))
547
 
      return FALSE;
548
 
 
549
 
   GtkWidget *d = GTK_WIDGET (gtk_builder_get_object(priv->gtk_builder,"dialog_hooks"));
550
 
   if((d && gtk_widget_get_visible(d)) || priv->active_notification != NULL)
551
 
      return FALSE;
552
 
 
553
 
   GdkRectangle area;
554
 
   gtk_status_icon_get_geometry(ta->tray_icon, NULL, &area, NULL);
555
 
   // no usefull coordiante yet, do another timeout
556
 
   if(area.x <= 0 || area.y <= 0 || area.width <= 0 || area.height <= 0)
557
 
      return TRUE;
 
654
   if(!tray_applet_ui_get_visible(ta))
 
655
      return FALSE;
 
656
 
 
657
   if((priv->dialog_hooks && gtk_widget_get_visible(priv->dialog_hooks)) ||
 
658
      priv->active_notification != NULL)
 
659
      return FALSE;
558
660
 
559
661
   NotifyNotification *n;
560
662
   GdkPixbuf* pix;
561
 
   n = notify_notification_new_with_status_icon(
 
663
   n = notify_notification_new(
562
664
                               _("Information available"),
563
665
                               _("Click on the notification icon"
564
666
                                 " to show the available information.\n"),
565
 
                               NULL, 
566
 
                               ta->tray_icon);
 
667
                               NULL);
567
668
   
568
669
   pix = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), 
569
670
                                  GTK_STOCK_DIALOG_INFO, 48,0,NULL);
571
672
   g_object_unref(pix);
572
673
   notify_notification_set_timeout (n, 60*1000);
573
674
   notify_notification_show(n, NULL);
 
675
   if (priv->active_notification)
 
676
      g_object_unref(priv->active_notification);
574
677
   priv->active_notification = n;
575
678
 
576
679
   return FALSE;
588
691
 
589
692
   dir=g_dir_open(HOOKS_DIR, 0, NULL);
590
693
   if(dir == NULL) {
591
 
      g_warning("can't read %s directory\n",HOOKS_DIR);
 
694
      g_warning("can't read %s directory",HOOKS_DIR);
592
695
      return FALSE;
593
696
   }
594
697
 
595
698
   g_debug_hooks("reading '%s' dir", HOOKS_DIR);
596
699
   int unseen_count = 0;
597
700
   while((hook_file=g_dir_read_name(dir)) != NULL) {
598
 
      g_debug_hooks("investigating file '%s' ", hook_file); 
 
701
      g_debug_hooks("investigating file '%s'", hook_file); 
599
702
 
600
703
      // check if the hook still applies (for e.g. DontShowAfterReboot)
601
704
      if(!is_hook_relevant(hook_file)) {
655
758
               (e->seen == FALSE) &&
656
759
               (memcmp(hf->md5,e->md5,DIGEST_SIZE)==0) )
657
760
              {
658
 
                 g_debug_hooks("%s (%s) was seen also in %s (%s)\n",
 
761
                 g_debug_hooks("%s (%s) was seen also in %s (%s)",
659
762
                         hf->filename, hf->md5, e->filename, e->md5);
660
763
                 md5match = TRUE;
661
764
              }
662
765
            x = g_list_next(x);
663
766
         }
664
767
         if(!md5match) {
665
 
            g_debug_hooks("%s increases unseen_count\n",hf->filename);
 
768
            g_debug_hooks("%s increases unseen_count",hf->filename);
666
769
            unseen_count++;
667
770
         }
668
771
      } else 
672
775
 
673
776
   //g_debug_hooks("hooks: %i (new: %i)", g_list_length(hook_files), unseen_count);
674
777
 
675
 
   GtkBuilder *builder = priv->gtk_builder;
676
 
   GtkWidget *button_next = GTK_WIDGET (gtk_builder_get_object(builder, "button_next"));
677
 
   assert(button_next);
678
 
 
679
 
   // check if we do a notification or a dialog
680
 
   GConfClient *gconf = gconf_client_get_default();
681
 
   if(gconf_client_get_bool(gconf, GCONF_KEY_AUTO_LAUNCH, NULL)) {
 
778
   if (unseen_count > 0) {
 
779
      // we only do a dialog
682
780
      g_debug_hooks("showing hooks with focus on map == FALSE");
683
 
      if (unseen_count < 2)
684
 
         gtk_widget_hide(button_next);
685
 
      else
686
 
         gtk_widget_show(button_next);
687
781
      show_hooks(ta, FALSE);
688
782
      return TRUE;
689
 
   }
690
 
 
691
 
   // no dialog, do the notification
692
 
   if((unseen_count > 0) && !gtk_status_icon_get_visible (ta->tray_icon))
693
 
      g_timeout_add(5000, show_notification, ta);
694
 
 
695
 
   switch(unseen_count) {
696
 
   case 0:
697
 
      gtk_status_icon_set_visible (ta->tray_icon, FALSE);
698
 
      gtk_widget_hide(button_next);
699
 
      break;
700
 
   case 1:
701
 
      gtk_status_icon_set_visible (ta->tray_icon, TRUE);
702
 
      gtk_widget_hide(button_next);
703
 
      break;
704
 
   default:
705
 
      gtk_status_icon_set_visible (ta->tray_icon, TRUE);
706
 
      gtk_widget_show(button_next);
707
 
      break;
708
 
   }
709
 
   hooks_trayicon_update_tooltip (ta, unseen_count);
 
783
   } else
 
784
      tray_applet_ui_destroy (ta);
710
785
 
711
786
   return TRUE;
712
787
}
713
788
 
714
 
void hook_read_seen_file(HookTrayAppletPrivate *priv, const char* filename)
 
789
static void
 
790
hook_read_seen_file(HookTrayAppletPrivate *priv, const char* filename)
715
791
{
716
792
   HookFile *t;
717
793
   char buf[512];
743
819
         HookFile *exisiting = (HookFile *)elm->data;
744
820
         // and it's more current
745
821
         if(exisiting->mtime > time) {
746
 
            g_debug_hooks("existing is newer, ignoring the read one\n");
 
822
            g_debug_hooks("existing is newer, ignoring the read one");
747
823
            continue;
748
824
         }
749
825
 
772
848
   fclose(f);
773
849
}
774
850
 
775
 
gboolean 
 
851
static gboolean 
776
852
init_already_seen_hooks(TrayApplet *ta)
777
853
{
778
854
   g_debug_hooks("init_already_seen_hooks");
799
875
   // read user hook file
800
876
   hook_read_seen_file(priv,filename);
801
877
   g_free(filename);
 
878
   g_free(old_filename);
802
879
 
803
880
   return TRUE;
804
881
}
805
882
 
806
 
static void
807
 
on_insert_text(GtkTextBuffer *buffer,
808
 
               GtkTextIter   *iter_end,
809
 
               gchar         *text,
810
 
               gint           arg3,
811
 
               gpointer       user_data)
812
 
{
813
 
   GtkTextIter iter, match_start, match_end, match_tmp;
814
 
 
815
 
   // get where we start
816
 
   gtk_text_buffer_get_iter_at_offset(buffer, &iter,
817
 
                                      gtk_text_iter_get_offset(iter_end) - g_utf8_strlen(text,-1));
818
 
   // search for http:// uris
819
 
   while(gtk_text_iter_forward_search(&iter, 
820
 
                                      "http://",  GTK_TEXT_SEARCH_VISIBLE_ONLY, 
821
 
                                      &match_start, &match_end, iter_end))  {
822
 
      match_tmp = match_end;
823
 
      // we found a uri, iterate it until the end is found
824
 
      while(gtk_text_iter_forward_char(&match_tmp)) {
825
 
         gchar *s = gtk_text_iter_get_text(&match_end,&match_tmp);
826
 
         if(strlen(s) < 1)
827
 
            break;
828
 
         char c = s[strlen(s)-1];
829
 
         if(c == ' ' || c == ')' || c == ']' || c == '\n' || c == '\t' || c == '>')
830
 
            break;
831
 
         match_end = match_tmp;
832
 
      }
833
 
      gchar *url = gtk_text_iter_get_text(&match_start, &match_end);
834
 
      //g_print("url: '%s'\n",url);
835
 
      GtkTextTag *tag;
836
 
      tag = gtk_text_buffer_create_tag(buffer, NULL,
837
 
                                       "foreground","blue",
838
 
                                       "underline", PANGO_UNDERLINE_SINGLE,
839
 
                                       NULL);
840
 
      g_object_set_data(G_OBJECT(tag), "url", url);
841
 
      gtk_text_buffer_apply_tag(buffer, tag, &match_start, &match_end);
842
 
      iter = match_end;
843
 
   }
844
 
}
845
 
 
846
 
static void
847
 
on_event_after(GtkWidget *widget,
848
 
               GdkEventButton  *event,
849
 
               gpointer   user_data)      
850
 
{
851
 
   if(event->type != GDK_BUTTON_RELEASE)
852
 
      return;
853
 
   if(event->button != 1)
854
 
      return;
855
 
   gint x,y;
856
 
   gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget),
857
 
                                         GTK_TEXT_WINDOW_WIDGET,
858
 
                                         event->x, event->y,
859
 
                                         &x, &y);
860
 
   GtkTextIter iter;
861
 
   gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y);
862
 
   GSList *tags = gtk_text_iter_get_tags(&iter);
863
 
   for( ; tags != NULL ; tags = tags->next) {
864
 
      gchar *url = g_object_get_data(G_OBJECT(tags->data), "url");
865
 
      if(url != NULL) {
866
 
         //g_print("click: '%s'\n",url);
867
 
         char *argv[] = { "/usr/bin/gnome-open", url, NULL };
868
 
         g_spawn_async(NULL, argv, NULL, 0, NULL, NULL, NULL, NULL);
869
 
         break;
870
 
      }
871
 
   }
872
 
}
873
 
 
874
883
void hook_tray_icon_init(TrayApplet *ta)
875
884
{
876
 
   GError* error = NULL;
877
 
   GtkBuilder* builder = gtk_builder_new ();
878
 
 
879
885
   HookTrayAppletPrivate *priv = g_new0(HookTrayAppletPrivate, 1);
880
886
   ta->user_data = priv;
881
887
 
882
 
   if (!gtk_builder_add_from_file (builder, UIDIR"hooks-dialog.ui", &error)) {
883
 
      g_warning ("Couldn't load builder file: %s", error->message);
884
 
      g_error_free (error);
885
 
   }
886
 
 
887
 
   assert(builder);
888
 
   priv->gtk_builder = builder;
889
 
   gtk_builder_connect_signals (builder, (gpointer)ta);
890
 
 
891
 
   /* show dialog on click */
892
 
   g_signal_connect (G_OBJECT(ta->tray_icon),
893
 
                     "activate",
894
 
                     G_CALLBACK (button_release_cb),
895
 
                     ta);
896
 
 
897
 
   // create a bold tag
898
 
   GtkWidget *text = GTK_WIDGET (gtk_builder_get_object(builder, "textview_hook"));
899
 
   GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
900
 
   gtk_text_buffer_create_tag (buf, "bold_tag",
901
 
                               "scale", PANGO_SCALE_LARGE, 
902
 
                               "weight", PANGO_WEIGHT_BOLD,
903
 
                               NULL);  
904
 
   g_signal_connect_after(G_OBJECT(buf), "insert-text", 
905
 
                          G_CALLBACK(on_insert_text), NULL);
906
 
   g_signal_connect_after(G_OBJECT(text), "event-after", 
907
 
                          G_CALLBACK(on_event_after), NULL);
908
 
 
909
 
 
910
888
   /* read already seen hooks */
911
889
   init_already_seen_hooks(ta);
912
890