Add option 'statefile' to keep track of unread messages across restarts
(Suggested by micressor)
--- 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__ */