7
6
#include <glib/gstdio.h>
8
#include <glade/glade.h>
12
#include "update-notifier.h"
18
9
/* relative to the home dir */
19
10
#define HOOKS_SEEN ".update-notifier/hooks_seen"
21
/* used by e.g. the installer to mark stuff that's already done */
22
#define GLOBAL_HOOKS_SEEN "/etc/update-notifier/hooks_seen"
28
static void debug_log_handler (const gchar *log_domain,
29
GLogLevelFlags log_level,
34
g_log_default_handler (log_domain, log_level, message, user_data);
39
12
void hooks_trayicon_update_tooltip (TrayApplet *un, int num_hooks)
41
g_debug("update_tooltip: %x ", un);
14
//g_print("update_tooltip: %x \n", un);
43
16
gchar *explanation;
45
updates = g_strdup_printf(ngettext("There is %i item of post-update information available!", "There are %i items of post-update information available!", num_hooks), num_hooks);
46
explanation = ngettext("Press this icon to show the information.", "Press this icon to show the information.", num_hooks);
18
updates = g_strdup_printf(_("There are %i post-update informations available!"),
21
explanation = _("Press this icon to show the information.");
48
23
gtk_tooltips_set_tip(GTK_TOOLTIPS(un->tooltip),
49
24
GTK_WIDGET (un->eventbox),
56
// compare a HookFile with a filename and find the HookFile that matches
57
30
gint compare_hook_func(gconstpointer a, gconstpointer b)
59
//g_debug("compare: %s %s\n",(char*)(((HookFileSeen*)a)->filename),(char*)b);
63
return strcmp(((HookFile*)a)->filename, b);
66
// return the most recent mtime or ctime of the file
67
time_t hook_file_time(const gchar *filename)
32
//g_print("compare: %s %s\n",(char*)(((HookFileSeen*)a)->filename),(char*)b);
33
return strcmp(((HookFileSeen*)a)->filename, b);
36
gboolean hook_file_is_new(HookDialog *h, const gchar *filename)
38
if(g_list_find_custom(h->hook_files_seen,
39
filename, compare_hook_func) == NULL)
45
gboolean hook_file_mark_as_seen(HookDialog *hook_dialog,
46
const gchar *hook_file)
48
//g_print("mark_hook_file_as_seen: %s\n",hook_file);
50
gchar *filename = g_strdup_printf("%s/"HOOKS_SEEN,g_get_home_dir());
51
FILE *f = fopen(filename, "a");
70
char *file = g_strdup_printf("%s/%s",HOOKS_DIR, filename);
71
if(g_stat(file, &buf) <0) {
72
g_error("can't stat %s\n",file);
78
time_t mtime = buf.st_mtime;
79
time_t ctime = buf.st_ctime;
81
return mtime > ctime ? mtime : ctime;
84
gboolean hook_file_md5(const gchar *filename, char *md5)
92
file = g_strdup_printf("%s/%s",HOOKS_DIR, filename);
95
g_warning("can't read %s\n",file);
101
n = fread(buf,1, sizeof(buf), f);
102
md5_update(&md5ctx, buf,n);
104
md5_finish(&md5ctx, md5);
110
/* mark a given hook file as seen
111
(actually implemented to write out all the information we have)
113
gboolean hook_file_mark_as_seen(HookDialog *hook_dialog,
116
g_debug("mark_hook_file_as_seen: %s", hf->filename);
120
hook_file_md5(hf->filename,&md5);
125
// update the time (extra paranoia, shouldn't be needed)
126
hf->mtime = hook_file_time(hf->filename);
128
// write out the list of known files
129
gchar *filename = g_strdup_printf("%s/%s",g_get_home_dir(),HOOKS_SEEN);
130
FILE *f = fopen(filename, "w");
132
g_warning("Something went wrong writing the users hook file");
136
// write out all the hooks that are seen
138
// and any hooks that are not yet seen but have the same md5sum
139
// as the one just marked (avoids showing duplicated hooks effecivly).
141
// For hooks with the same md5sum use the mtime of the just displayed hook
142
GList *elm = g_list_first(hook_dialog->hook_files);
143
for(;elm != NULL; elm = g_list_next(elm)) {
144
HookFile *e = (HookFile*)elm->data;
145
g_debug("looking at: %s (%s)",e->filename, e->md5);
146
if(e->seen == TRUE) {
147
g_debug("e->seen: %s %i %x", e->filename,e->mtime, (int)(e->cmd_run));
148
fprintf(f,"%s %i %x\n", e->filename, e->mtime, (int)(e->cmd_run));
149
} else if(memcmp(e->md5,md5,16) == 0) {
151
fprintf(f,"%s %i %x\n", e->filename,hf->mtime, (int)(e->cmd_run));
152
g_debug("same md5: %s %i %x",e->filename,hf->mtime,(int)(e->cmd_run));
56
gchar *file = g_strdup_printf("%s%s",HOOKS_DIR,hook_file);
59
HookFileSeen *t = g_new0(HookFileSeen,1);
60
t->filename = g_strdup(hook_file);
61
t->mtime = buf.st_mtime;
63
fprintf(f,"%s %i %x\n", t->filename, t->mtime, (int)(t->cmd_run));
68
hook_dialog->hook_files_seen= g_list_append(hook_dialog->hook_files_seen,t);
162
/* mark a given HookFile as run */
163
gboolean mark_hook_file_as_run(HookDialog *hook_dialog, HookFile *hf)
73
gboolean mark_hook_file_as_run(HookDialog *hook_dialog, const gchar *filename)
168
g_debug("mark_hook_file_as_run: %s\n",hf->filename);
76
//g_print("mark_hook_file_as_run: %s\n",filename);
204
110
/* try $field-$languagecode_$countrycode first */
205
111
s = g_strdup_printf("%s-%s", field, get_lang_code(FALSE));
206
112
entry = rfc822_header_lookup(rfc822, s);
207
//g_debug("Looking for: %s ; found: %s\n",s,entry);
113
//g_print("Looking for: %s ; found: %s\n",s,entry);
209
115
if(entry != NULL)
212
118
/* try $field-$languagecode next */
213
119
s = g_strdup_printf("%s-%s", field, get_lang_code(TRUE));
214
//g_debug("Looking for: %s ; found: %s\n",s,entry);
120
//g_print("Looking for: %s ; found: %s\n",s,entry);
215
121
entry = rfc822_header_lookup(rfc822, s);
217
123
if(entry != NULL)
220
/* now try translating it with gettext */
221
entry = rfc822_header_lookup(rfc822, field);
225
char* hook_description_get_summary(struct rfc822_header *rfc822)
227
char *description = hook_file_lookup_i18n(rfc822, "Name");
229
char *summary = g_strdup(description);
230
char *p = strchr(summary,'\n');
236
char* hook_description_get_description(struct rfc822_header *rfc822)
238
char *description = hook_file_lookup_i18n(rfc822, "Description");
241
// look for the first \n, that's the summary
242
newd = p = g_strdup(description);
247
while( (p=strchr(p,'\n')) != NULL)
249
// strip leading/trailing whitespace
126
/* if everything else fails ... return untranslated */
127
return rfc822_header_lookup(rfc822, field);
257
131
* show the given hook file
259
gboolean show_next_hook(TrayApplet *ta, GList *hooks)
133
void show_one_hook(TrayApplet *ta, gchar *hook_file)
261
//g_debug("show_next_hook()\n");
263
135
HookDialog *hook_dialog = (HookDialog *)ta->user_data;
264
136
GladeXML *xml = hook_dialog->glade_xml;
307
158
char *term = g_strstrip(rfc822_header_lookup(rfc822, "Terminal"));
308
159
g_object_set_data(G_OBJECT(button_run),"cmd", g_strdup(cmd));
309
160
g_object_set_data(G_OBJECT(button_run),"term", g_strdup(term));
310
g_object_set_data(G_OBJECT(button_run),"hook_file", hf);
161
g_object_set_data(G_OBJECT(button_run),"hook_file", g_strdup(hook_file));
311
162
if(cmd != NULL) {
312
163
gtk_widget_show(button_run);
314
165
gtk_widget_hide(button_run);
317
char *summary = hook_description_get_summary(rfc822);
318
char *descr = hook_description_get_description(rfc822);
319
char *s = g_strdup_printf("%s\n\n%s\n",gettext(summary), gettext(descr));
168
char *name = hook_file_lookup_i18n(rfc822, "Name");
169
char *description = hook_file_lookup_i18n(rfc822, "Description");
170
char *s = g_strdup_printf("%s\n\n%s\n",name, description);
320
171
gtk_text_buffer_set_text(buf, s, -1);
322
// set the name to bold
323
GtkTextIter start, end;
324
gtk_text_buffer_get_iter_at_offset(buf, &start, 0);
325
gtk_text_buffer_get_iter_at_offset(buf, &end, g_utf8_strlen(gettext(summary),-1));
326
gtk_text_buffer_apply_tag_by_name(buf, "bold_tag", &start, &end);
330
175
g_free(filename);
334
176
rfc822_header_free_all(rfc822);
336
178
/* mark the current hook file as seen */
337
g_object_set_data(G_OBJECT(button_next), "HookFile", hf);
179
hook_file_mark_as_seen(hook_dialog, hook_file);
180
check_update_hooks(ta);
343
184
void cb_button_run(GtkWidget *self, void *data)
345
//g_debug("cb_button_run()\n");
186
//g_print("cb_button_run()\n");
348
189
TrayApplet *ta = (TrayApplet *)data;
349
190
HookDialog *hook_dialog = (HookDialog *)ta->user_data;
351
192
/* mark the current hook file as run */
352
HookFile *hf = g_object_get_data(G_OBJECT(self), "hook_file");
353
mark_hook_file_as_run(hook_dialog, hf);
193
gchar *hook_file = g_object_get_data(G_OBJECT(self), "hook_file");
194
mark_hook_file_as_run(hook_dialog, (const char*)hook_file);
355
196
gchar *cmd = g_object_get_data(G_OBJECT(self), "cmd");
356
197
gchar *term = g_object_get_data(G_OBJECT(self), "term");
404
238
GtkWidget *button_run = glade_xml_get_widget(xml, "button_run");
405
239
assert(button_run);
406
GtkWidget *button_next = glade_xml_get_widget(xml, "button_next");
409
// if show_next_hook() fails for some reason don't do anything
410
if(!show_next_hook(ta, hook_dialog->hook_files))
241
show_one_hook(ta, hook_dialog->hook_files->data);
413
244
int res = gtk_dialog_run(GTK_DIALOG(dia));
414
245
if(res == GTK_RESPONSE_CLOSE) {
415
// mark the currently current hookfile as seen
417
hf = (HookFile*)g_object_get_data(G_OBJECT(button_next),"HookFile");
419
g_warning("show_hooks called without HookFile\n");
424
hook_file_mark_as_seen(hook_dialog, hf);
425
check_update_hooks(ta);
246
/* mark question as seen */
428
248
gtk_widget_hide(dia);
432
button_release_cb (GtkWidget *widget,
433
GdkEventButton *event,
436
if (event->button == 1 && event->type == GDK_BUTTON_RELEASE) {
437
//g_debug("left click on hook applet\n");
443
gboolean is_hook_relevant(const gchar *hook_file)
445
g_debug("is_hook_relevant()\n");
448
char *filename = g_strdup_printf("%s%s",HOOKS_DIR,hook_file);
449
FILE *f = fopen(filename, "r");
451
// can't open, can't be relevant
454
struct rfc822_header *rfc822 = rfc822_parse_stanza(f);
456
// check the DontShowAfterReboot flag
457
gchar *b = rfc822_header_lookup(rfc822, "DontShowAfterReboot");
458
if(b != NULL && g_ascii_strncasecmp(b, "true",-1) == 0) {
460
// read the uptime information
461
double uptime=0, idle=0;
463
int fd = open("/proc/uptime", 0);
464
int local_n = read(fd, buf, sizeof(buf)-1);
466
char *savelocale = setlocale(LC_NUMERIC, NULL);
467
setlocale(LC_NUMERIC,"C");
468
sscanf(buf, "%lf %lf", &uptime,&idle);
470
setlocale(LC_NUMERIC,savelocale);
472
time_t mtime = hook_file_time(hook_file);
473
time_t now = time(NULL);
475
g_debug("now: %i mtime: %i uptime: %f\n",now,mtime,uptime);
476
g_debug("diff: %i uptime: %f\n",now-mtime,uptime);
477
if((int)uptime > 0 && (now - mtime) > (int)uptime) {
478
g_debug("not relevant because of reboot: %s\n",hook_file);
486
rfc822_header_free_all(rfc822);
490
253
gboolean check_update_hooks(TrayApplet *ta)
492
g_debug("check_update_hooks()");
255
//g_print("check_update_hooks()\n");
494
257
HookDialog *hook_dialog = (HookDialog*)ta->user_data;
501
264
g_critical("can't read %s directory\n",HOOKS_DIR);
503
int unseen_count = 0;
504
266
while((hook_file=g_dir_read_name(dir)) != NULL) {
506
// check if the hook still applies (for e.g. DontShowAfterReboot)
507
if(!is_hook_relevant(hook_file))
510
// see if we already know about this hook filename
511
267
GList *elm = g_list_find_custom(hook_dialog->hook_files,hook_file,
514
// not seen before, add to the list
516
g_debug("never seen before: %s",hook_file);
517
HookFile *t = g_new0(HookFile, 1);
518
t->filename = strdup(hook_file);
519
t->mtime = hook_file_time(hook_file);
520
hook_file_md5(hook_file, t->md5);
523
hook_dialog->hook_files = g_list_append(hook_dialog->hook_files, t);
524
// init elm with the just added record (will be needed below)
525
elm = g_list_find_custom(hook_dialog->hook_files,hook_file,
530
// this is the hook file information we have (either because it was
531
// availabe already or because we added it)
532
HookFile *hf = (HookFile*)elm->data;
535
// file has changed since we last saw it
536
time_t new_mtime = hook_file_time(hook_file);
537
if(new_mtime > hf->mtime) {
538
g_debug("newer mtime: %s (%i > %i))",hook_file, new_mtime, hf->mtime);
542
// we have not seen it yet (because e.g. it was just added)
543
if(hf->seen == FALSE) {
545
// update mtime (because we haven't seen the old one and there
547
hf->mtime = new_mtime;
549
// check if there is already another notification that is
553
// if so, we don't increase the unseen count as all identical
554
// ones will maked as unseen at onces
555
gboolean md5match = FALSE;
556
GList *x = g_list_first(hook_dialog->hook_files);
558
HookFile *e = (HookFile*)x->data;
560
(e->seen == FALSE) &&
561
(memcmp(hf->md5,e->md5,16)==0) )
563
g_debug("%s (%s) was seen also in %s (%s)\n",
564
hf->filename, hf->md5, e->filename, e->md5);
570
g_debug("%s increases unseen_count\n",hf->filename);
269
if(hook_file_is_new(hook_dialog, hook_file)) {
271
//g_print("check_update_hooks: appending: %s\n",hook_file);
272
hook_dialog->hook_files = g_list_append(hook_dialog->hook_files,
273
g_strdup(hook_file));
277
//g_print("check_update_hooks: removing (seen): %s\n", hook_file);
278
hook_dialog->hook_files = g_list_remove(hook_dialog->hook_files,
577
283
g_dir_close(dir);
579
//g_debug("hooks: %i (new: %i)", g_list_length(hook_files), unseen_count);
285
int num = g_list_length(hook_dialog->hook_files);
286
// g_print("check_update_hook() hook_files: %p %i \n",
287
// hook_dialog->hook_files, num);
581
289
GladeXML *xml = hook_dialog->glade_xml;
582
290
GtkWidget *button_next = glade_xml_get_widget(xml, "button_next");
585
switch(unseen_count) {
293
//g_print("gtk_widget_hide() called on tray_icon\n");
587
294
gtk_widget_hide(GTK_WIDGET(ta->tray_icon));
588
295
gtk_widget_hide(button_next);
591
gtk_widget_show(GTK_WIDGET(ta->tray_icon));
592
gtk_widget_hide(button_next);
297
//g_print("gtk_widget_show() called on tray_icon\n");
298
hooks_trayicon_update_tooltip (ta, num);
595
299
gtk_widget_show(GTK_WIDGET(ta->tray_icon));
596
300
gtk_widget_show(button_next);
599
hooks_trayicon_update_tooltip (ta, unseen_count);
604
void hook_read_seen_file(HookDialog *hook_dialog, const char* filename)
308
button_release_cb (GtkWidget *widget,
309
GdkEventButton *event,
312
if (event->button == 1 && event->type == GDK_BUTTON_RELEASE) {
313
//g_print("left click on hook applet\n");
319
gboolean init_already_seen_hooks(TrayApplet *ta)
321
HookDialog* hook_dialog = (HookDialog*)ta->user_data;
323
GList *seen = hook_dialog->hook_files_seen;
324
char *filename = g_strdup_printf("%s/"HOOKS_SEEN,g_get_home_dir());
607
326
int time, was_run;
608
327
FILE *f = fopen(filename, "r");
612
g_debug("reading hook_file: %s \n", filename);
614
330
while(fscanf(f, "%s %i %i",buf,&time,&was_run) == 3) {
616
// first check if the file actually exists, if not skip it
617
// (may already be delete when e.g. a package was removed)
618
char *filename = g_strdup_printf("%s%s",HOOKS_DIR,buf);
619
gboolean res = g_file_test(filename, G_FILE_TEST_EXISTS);
624
// now check if we already have that filename in the list
625
GList *elm = g_list_find_custom(hook_dialog->hook_files,buf,
628
g_debug("hookfile: %s already in the list", buf);
630
// we have that file already in the list with a newer mtime,
631
// than the file in the config file. ignore the config file
632
HookFile *exisiting = (HookFile *)elm->data;
633
// and it's more current
634
if(exisiting->mtime > time) {
635
g_debug("existing is newer, ignoring the read one\n");
639
// we have it in the list, but the the file in the list is older
640
// than the one in the config file. use this config-file instead
641
// and update the values in the list
642
exisiting->cmd_run = was_run;
643
exisiting->seen = TRUE;
644
exisiting->mtime = time;
645
hook_file_md5(exisiting->filename,exisiting->md5);
648
// not in the list yet
649
// add the just read hook file to the list
650
g_debug("got: %s %i %i ",buf,time,was_run);
651
HookFile *t = g_new0(HookFile, 1);
652
t->filename = strdup(buf);
654
t->cmd_run = was_run;
656
hook_file_md5(t->filename,t->md5);
658
hook_dialog->hook_files = g_list_append(hook_dialog->hook_files, t);
331
HookFileSeen *t = g_new0(HookFileSeen, 1);
332
t->filename = strdup(buf);
334
t->cmd_run = was_run;
335
seen = g_list_append(seen, t);
337
//g_print("got: %s %i %i \n",buf,time,was_run);
664
gboolean init_already_seen_hooks(TrayApplet *ta)
666
g_debug("init_already_seen_hooks");
668
HookDialog* hook_dialog = (HookDialog*)ta->user_data;
671
// init with default value
672
hook_dialog->hook_files = NULL;
674
// read global hook file
675
hook_read_seen_file(hook_dialog,GLOBAL_HOOKS_SEEN);
677
// read user hook file
678
filename = g_strdup_printf("%s/%s", g_get_home_dir(),HOOKS_SEEN);
679
hook_read_seen_file(hook_dialog,filename);
342
hook_dialog->hook_files_seen = seen;