changeset 1437:071c8170b7de

Add option 'statefile' to keep track of unread messages across restarts (Suggested by micressor)
author Mikael Berthe <mikael@lilotux.net>
date Mon, 25 Feb 2008 20:27:56 +0100
parents 6fb141142388
children c5d937d5530b
files mcabber/mcabberrc.example mcabber/src/histolog.c mcabber/src/histolog.h mcabber/src/main.c mcabber/src/roster.c mcabber/src/roster.h
diffstat 6 files changed, 158 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/mcabber/mcabberrc.example	Sun Feb 24 17:46:13 2008 +0100
+++ b/mcabber/mcabberrc.example	Mon Feb 25 20:27:56 2008 +0100
@@ -146,7 +146,12 @@
 # Default = 0 (disabled -- everything is loaded)
 # Note: this option is only used when reading history files, not later.
 #set max_history_age = 0
-#
+
+# mcabber can store the list of unread messages in a state file,
+# so that the message flags are set back at next startup.
+# Note that 'logging' must be enabled for this feature to work.
+#set statefile = ~/.mcabber/mcabber.state
+
 # You can specify a maximum number of data blocks per buffer (1 block contains
 # about 8kB).  The default is 0 (unlimited).  If set, this value must be > 2.
 #set max_history_blocks = 8
--- a/mcabber/src/histolog.c	Sun Feb 24 17:46:13 2008 +0100
+++ b/mcabber/src/histolog.c	Mon Feb 25 20:27:56 2008 +0100
@@ -26,14 +26,16 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <unistd.h>
 
 #include "histolog.h"
 #include "hbuf.h"
 #include "jabglue.h"
 #include "utils.h"
-#include "logprint.h"
+#include "screen.h"
 #include "settings.h"
 #include "utils.h"
+#include "roster.h"
 
 static guint UseFileLogging;
 static guint FileLoadLogs;
@@ -391,6 +393,11 @@
   }
 }
 
+guint hlog_is_enabled(void)
+{
+  return UseFileLogging;
+}
+
 inline void hlog_write_message(const char *bjid, time_t timestamp, int sent,
         const char *msg)
 {
@@ -416,4 +423,115 @@
           status_msg);
 }
 
+
+//  hlog_save_state()
+// If enabled, save the current state of the roster
+// (i.e. pending messages) to a temporary file.
+void hlog_save_state(void)
+{
+  gpointer unread_ptr, first_unread;
+  const char *bjid;
+  char *statefile_xp;
+  FILE *fp;
+  const char *statefile = settings_opt_get("statefile");
+
+  if (!statefile || !UseFileLogging)
+    return;
+
+  statefile_xp = expand_filename(statefile);
+  fp = fopen(statefile_xp, "w");
+  if (!fp) {
+    scr_LogPrint(LPRINT_NORMAL, "Cannot open state file [%s]",
+                 strerror(errno));
+    goto hlog_save_state_return;
+  }
+
+  if (!jb_getonline()) {
+    // We're not connected.  Let's use the unread_jids hash.
+    GList *unread_jid = unread_jid_get_list();
+    unread_ptr = unread_jid;
+    for ( ; unread_jid ; unread_jid = g_list_next(unread_jid))
+      fprintf(fp, "%s\n", (char*)unread_jid->data);
+    g_list_free(unread_ptr);
+    goto hlog_save_state_return;
+  }
+
+  if (!current_buddy) // Safety check -- shouldn't happen.
+    goto hlog_save_state_return;
+
+  // We're connected.  Let's use unread_msg().
+  unread_ptr = first_unread = unread_msg(NULL);
+  if (!first_unread)
+    goto hlog_save_state_return;
+
+  do {
+    guint type = buddy_gettype(unread_ptr);
+    if (type & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) {
+      bjid = buddy_getjid(unread_ptr);
+      if (bjid)
+        fprintf(fp, "%s\n", bjid);
+    }
+    unread_ptr = unread_msg(unread_ptr);
+  } while (unread_ptr && unread_ptr != first_unread);
+
+hlog_save_state_return:
+  if (fp) {
+    long filelen = ftell(fp);
+    fclose(fp);
+    if (!filelen)
+      unlink(statefile_xp);
+  }
+  g_free(statefile_xp);
+}
+
+//  hlog_load_state()
+// If enabled, load the current state of the roster
+// (i.e. pending messages) from a temporary file.
+// This function adds the JIDs to the unread_jids hash table,
+// so it should only be called at startup.
+void hlog_load_state(void)
+{
+  char bjid[1024];
+  char *statefile_xp;
+  FILE *fp;
+  const char *statefile = settings_opt_get("statefile");
+
+  if (!statefile || !UseFileLogging)
+    return;
+
+  statefile_xp = expand_filename(statefile);
+  fp = fopen(statefile_xp, "r");
+  if (fp) {
+    char *eol;
+    while (!feof(fp)) {
+      if (fgets(bjid, sizeof bjid, fp) == NULL)
+        break;
+      // Let's remove the trailing newline.
+      // Also remove whitespace, if the file as been (badly) manually modified.
+      for (eol = bjid; *eol; eol++) ;
+      for (eol--; eol >= bjid && (*eol == '\n' || *eol == ' '); *eol-- = 0) ;
+      // Safety checks...
+      if (!bjid[0])
+        continue;
+      if (check_jid_syntax(bjid)) {
+        scr_LogPrint(LPRINT_LOGNORM,
+                     "ERROR: Invalid JID in state file.  Corrupted file?");
+        break;
+      }
+      // Display a warning if there are pending messages but the user
+      // won't see them because load_log isn't set.
+      if (!FileLoadLogs) {
+        scr_LogPrint(LPRINT_LOGNORM, "WARNING: unread message from <%s>.",
+                     bjid);
+        scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
+      }
+      // Add the JID to unread_jids.  It will be used when the contact is
+      // added to the roster.
+      unread_jid_add(bjid);
+    }
+    fclose(fp);
+  }
+  g_free(statefile_xp);
+}
+
 /* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- a/mcabber/src/histolog.h	Sun Feb 24 17:46:13 2008 +0100
+++ b/mcabber/src/histolog.h	Mon Feb 25 20:27:56 2008 +0100
@@ -12,6 +12,8 @@
                         const char *msg);
 void hlog_write_status(const char *bjid, time_t timestamp,
                        enum imstatus status, const char *status_msg);
+void hlog_save_state(void);
+void hlog_load_state(void);
 
 #endif /* __HISTOLOG_H__ */
 
--- a/mcabber/src/main.c	Sun Feb 24 17:46:13 2008 +0100
+++ b/mcabber/src/main.c	Mon Feb 25 20:27:56 2008 +0100
@@ -499,6 +499,9 @@
   fifo_init(settings_opt_get("fifo_name"));
 #endif
 
+  /* Load previous roster state */
+  hlog_load_state();
+
   if (ret < 0) {
     scr_LogPrint(LPRINT_NORMAL, "No configuration file has been found.");
     scr_ShowBuddyWindow();
@@ -539,10 +542,11 @@
 #endif
 #ifdef HAVE_ASPELL_H
   /* Deinitialize aspell */
-  if (settings_opt_get_int("aspell_enable")) {
+  if (settings_opt_get_int("aspell_enable"))
     spellcheck_deinit();
-  }
 #endif
+  /* Save pending message state */
+  hlog_save_state();
 
   printf("\n\nThanks for using mcabber!\n");
 
--- a/mcabber/src/roster.c	Sun Feb 24 17:46:13 2008 +0100
+++ b/mcabber/src/roster.c	Mon Feb 25 20:27:56 2008 +0100
@@ -24,6 +24,7 @@
 #include "roster.h"
 #include "utils.h"
 
+extern void hlog_save_state(void);
 
 char *strrole[] = {   /* Should match enum in roster.h */
   "none",
@@ -118,8 +119,7 @@
 
 static roster roster_special;
 
-void unread_jid_add(const char *jid);
-int  unread_jid_del(const char *jid);
+static int  unread_jid_del(const char *jid);
 
 #define DFILTER_ALL     63
 #define DFILTER_ONLINE  62
@@ -590,6 +590,7 @@
   GSList *sl_user;
   roster *roster_usr, *roster_grp;
   int new_roster_item = FALSE;
+  guint unread_list_modified = FALSE;
 
   if (special) {
     //sl_user = roster_find(jid, namesearch, ROSTER_TYPE_SPECIAL);
@@ -622,6 +623,8 @@
   roster_usr = (roster*)sl_user->data;
   roster_grp = (roster*)roster_usr->list->data;
   if (value) {
+    if (!(roster_usr->flags & ROSTER_FLAG_MSG))
+      unread_list_modified = TRUE;
     // Message flag is TRUE.  This is easy, we just have to set both flags
     // to TRUE...
     roster_usr->flags |= ROSTER_FLAG_MSG;
@@ -632,6 +635,8 @@
   } else {
     // Message flag is FALSE.
     guint msg = FALSE;
+    if (roster_usr->flags & ROSTER_FLAG_MSG)
+      unread_list_modified = TRUE;
     roster_usr->flags &= ~ROSTER_FLAG_MSG;
     if (unread_list) {
       GSList *node = g_slist_find(unread_list, roster_usr);
@@ -660,6 +665,9 @@
 
   if (buddylist && (new_roster_item || !g_list_find(buddylist, roster_usr)))
     buddylist_build();
+
+  if (unread_list_modified)
+    hlog_save_state();
 }
 
 const char *roster_getname(const char *jid)
@@ -1541,11 +1549,22 @@
 
 //  unread_jid_del(jid)
 // Return TRUE if jid is found in the table (and remove it), FALSE if not
-int unread_jid_del(const char *jid)
+static int unread_jid_del(const char *jid)
 {
   if (!unread_jids)
     return FALSE;
   return g_hash_table_remove(unread_jids, jid);
 }
 
+//  unread_jid_get_list()
+// Return the JID list.
+// The content of the list should not be modified or freed.
+// The caller should call g_list_free() after use.
+GList *unread_jid_get_list(void)
+{
+  if (!unread_jids)
+    return NULL;
+  return g_hash_table_get_keys(unread_jids);
+}
+
 /* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- a/mcabber/src/roster.h	Sun Feb 24 17:46:13 2008 +0100
+++ b/mcabber/src/roster.h	Mon Feb 25 20:27:56 2008 +0100
@@ -232,6 +232,9 @@
                              void *param);
 gpointer unread_msg(gpointer rosterdata);
 
+void   unread_jid_add(const char *jid);
+GList *unread_jid_get_list(void);
+
 GSList *compl_list(guint type);
 
 #endif /* __ROSTER_H__ */