diff --git a/src/common.h b/src/common.h index bb339f1f..39045de7 100644 --- a/src/common.h +++ b/src/common.h @@ -6,7 +6,7 @@ #define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */ #define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */ -#define IRSSI_ABI_VERSION 33 +#define IRSSI_ABI_VERSION 34 #define DEFAULT_SERVER_ADD_PORT 6667 #define DEFAULT_SERVER_ADD_TLS_PORT 6697 diff --git a/src/core/expandos.c b/src/core/expandos.c index 9280b6f3..0f317e51 100644 --- a/src/core/expandos.c +++ b/src/core/expandos.c @@ -48,6 +48,7 @@ typedef struct { } EXPANDO_REC; const char *current_expando = NULL; +time_t reference_time = (time_t) -1; time_t current_time = (time_t)-1; static int timer_tag; @@ -59,6 +60,7 @@ static char *last_privmsg_from, *last_public_from; static char *sysname, *sysrelease, *sysarch; static char *timestamp_format; +static char *timestamp_format_alt; static int timestamp_seconds; static time_t last_timestamp; @@ -441,12 +443,24 @@ static char *expando_time(SERVER_REC *server, void *item, int *free_ret) time_t now; struct tm *tm; char str[256]; + char *format; - now = current_time != (time_t)-1 ? current_time : time(NULL); + now = current_time != (time_t) -1 ? current_time : time(NULL); tm = localtime(&now); + format = timestamp_format; - if (strftime(str, sizeof(str), timestamp_format, tm) == 0) - return ""; + if (reference_time != (time_t) -1) { + time_t ref = reference_time; + struct tm tm_ref; + if (localtime_r(&ref, &tm_ref)) { + if (tm_ref.tm_yday != tm->tm_yday || tm_ref.tm_year != tm->tm_year) { + format = timestamp_format_alt; + } + } + } + + if (strftime(str, sizeof(str), format, tm) == 0) + return ""; *free_ret = TRUE; return g_strdup(str); @@ -576,7 +590,9 @@ static int sig_timer(void) static void read_settings(void) { g_free_not_null(timestamp_format); + g_free_not_null(timestamp_format_alt); timestamp_format = g_strdup(settings_get_str("timestamp_format")); + timestamp_format_alt = g_strdup(settings_get_str("timestamp_format_alt")); timestamp_seconds = strstr(timestamp_format, "%r") != NULL || @@ -594,6 +610,7 @@ void expandos_init(void) #endif settings_add_str("misc", "STATUS_OPER", "*"); settings_add_str("lookandfeel", "timestamp_format", "%H:%M"); + settings_add_str("lookandfeel", "timestamp_format_alt", "%a %e %b %H:%M"); settings_add_bool("lookandfeel", "chanmode_expando_strip", FALSE); last_sent_msg = NULL; last_sent_msg_body = NULL; @@ -730,6 +747,7 @@ void expandos_deinit(void) g_free_not_null(sysrelease); g_free_not_null(sysarch); g_free_not_null(timestamp_format); + g_free_not_null(timestamp_format_alt); g_source_remove(timer_tag); signal_remove("message public", (SIGNAL_FUNC) sig_message_public); diff --git a/src/core/expandos.h b/src/core/expandos.h index c03333a7..17cee608 100644 --- a/src/core/expandos.h +++ b/src/core/expandos.h @@ -18,6 +18,7 @@ typedef char* (*EXPANDO_FUNC) extern const char *current_expando; extern time_t current_time; +extern time_t reference_time; /* Create expando - overrides any existing ones. ... = signal, type, ..., NULL - list of signals that might change the diff --git a/src/core/refstrings.c b/src/core/refstrings.c index 4b675fa5..8f220f6f 100644 --- a/src/core/refstrings.c +++ b/src/core/refstrings.c @@ -1,9 +1,44 @@ +#include #include #include #if GLIB_CHECK_VERSION(2, 58, 0) -/* nothing */ + +void i_refstr_init(void) +{ + /* nothing */ +} + +char *i_refstr_intern(const char *str) +{ + if (str == NULL) { + return NULL; + } + + return g_ref_string_new_intern(str); +} + +void i_refstr_release(char *str) +{ + if (str == NULL) { + return; + } + + g_ref_string_release(str); +} + +void i_refstr_deinit(void) +{ + /* nothing */ +} + +char *i_refstr_table_size_info(void) +{ + /* not available */ + return NULL; +} + #else GHashTable *i_refstr_table; diff --git a/src/core/refstrings.h b/src/core/refstrings.h index 79036a20..89ef84d1 100644 --- a/src/core/refstrings.h +++ b/src/core/refstrings.h @@ -1,18 +1,6 @@ #ifndef IRSSI_CORE_REFSTRINGS_H #define IRSSI_CORE_REFSTRINGS_H -#include - -#if GLIB_CHECK_VERSION(2, 58, 0) - -#define i_refstr_init() /* nothing */ -#define i_refstr_release(str) ((str) == NULL ? NULL : g_ref_string_release(str)) -#define i_refstr_intern(str) ((str) == NULL ? NULL : g_ref_string_new_intern(str)) -#define i_refstr_deinit() /* nothing */ -#define i_refstr_table_size_info() NULL - -#else - void i_refstr_init(void); char *i_refstr_intern(const char *str); void i_refstr_release(char *str); @@ -20,5 +8,3 @@ void i_refstr_deinit(void); char *i_refstr_table_size_info(void); #endif - -#endif diff --git a/src/core/server-rec.h b/src/core/server-rec.h index c36c8abb..6c7c63e3 100644 --- a/src/core/server-rec.h +++ b/src/core/server-rec.h @@ -44,6 +44,9 @@ int lag; /* server lag in milliseconds */ GSList *channels; GSList *queries; +/* transient meta data stash */ +GHashTable *current_incoming_meta; + /* -- support for multiple server types -- */ /* -- must not be NULL: -- */ diff --git a/src/core/servers.c b/src/core/servers.c index 841700d4..a20d5f4a 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -19,14 +19,15 @@ */ #include "module.h" -#include #include +#include #include #include #include -#include #include +#include #include +#include #include #include @@ -354,6 +355,9 @@ void server_connect_init(SERVER_REC *server) MODULE_DATA_INIT(server); server->type = module_get_uniq_id("SERVER", 0); server_ref(server); + server->current_incoming_meta = + g_hash_table_new_full(g_str_hash, (GEqualFunc) g_strcmp0, + (GDestroyNotify) i_refstr_release, (GDestroyNotify) g_free); server->nick = g_strdup(server->connrec->nick); if (server->connrec->username == NULL || *server->connrec->username == '\0') { @@ -535,6 +539,7 @@ int server_unref(SERVER_REC *server) g_free(server->away_reason); g_free(server->nick); g_free(server->tag); + g_hash_table_destroy(server->current_incoming_meta); server->type = 0; g_free(server); @@ -655,6 +660,22 @@ void server_change_nick(SERVER_REC *server, const char *nick) signal_emit("server nick changed", 1, server); } +void server_meta_stash(SERVER_REC *server, const char *meta_key, const char *meta_value) +{ + g_hash_table_replace(server->current_incoming_meta, i_refstr_intern(meta_key), + g_strdup(meta_value)); +} + +const char *server_meta_stash_find(SERVER_REC *server, const char *meta_key) +{ + return g_hash_table_lookup(server->current_incoming_meta, meta_key); +} + +void server_meta_clear_all(SERVER_REC *server) +{ + g_hash_table_remove_all(server->current_incoming_meta); +} + /* Update own IPv4 and IPv6 records */ void server_connect_own_ip_save(SERVER_CONNECT_REC *conn, IPADDR *ip4, IPADDR *ip6) diff --git a/src/core/servers.h b/src/core/servers.h index 7b998c8d..d52603eb 100644 --- a/src/core/servers.h +++ b/src/core/servers.h @@ -66,6 +66,13 @@ void server_connect_failed(SERVER_REC *server, const char *msg); /* Change your nick */ void server_change_nick(SERVER_REC *server, const char *nick); +/* Push meta data onto the server stash */ +void server_meta_stash(SERVER_REC *server, const char *meta_key, const char *meta_value); +/* Get a value from the stash */ +const char *server_meta_stash_find(SERVER_REC *server, const char *meta_key); +/* clear meta stash */ +void server_meta_clear_all(SERVER_REC *server); + /* Update own IPv4 and IPv6 records */ void server_connect_own_ip_save(SERVER_CONNECT_REC *conn, IPADDR *ip4, IPADDR *ip6); diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index eb47646e..de3898ee 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -406,16 +406,10 @@ void format_read_arglist(va_list va, FORMAT_REC *format, } } } -void format_create_dest(TEXT_DEST_REC *dest, - void *server, const char *target, - int level, WINDOW_REC *window) -{ - format_create_dest_tag(dest, server, NULL, target, level, window); -} -void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, - const char *server_tag, const char *target, - int level, WINDOW_REC *window) +void format_create_dest_tag_meta(TEXT_DEST_REC *dest, void *server, const char *server_tag, + const char *target, int level, WINDOW_REC *window, + GHashTable *meta) { memset(dest, 0, sizeof(TEXT_DEST_REC)); @@ -425,6 +419,20 @@ void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, dest->level = level; dest->window = window != NULL ? window : window_find_closest(server, target, level); + dest->meta = meta; +} + +void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, const char *server_tag, + const char *target, int level, WINDOW_REC *window) +{ + format_create_dest_tag_meta(dest, server, server_tag, target, level, window, + server != NULL ? SERVER(server)->current_incoming_meta : NULL); +} + +void format_create_dest(TEXT_DEST_REC *dest, void *server, const char *target, int level, + WINDOW_REC *window) +{ + format_create_dest_tag(dest, server, NULL, target, level, window); } /* Return length of text part in string (ie. without % codes) */ diff --git a/src/fe-common/core/formats.h b/src/fe-common/core/formats.h index 226348de..530f42bf 100644 --- a/src/fe-common/core/formats.h +++ b/src/fe-common/core/formats.h @@ -64,6 +64,7 @@ typedef struct _TEXT_DEST_REC { int hilight_priority; char *hilight_color; int flags; + GHashTable *meta; } TEXT_DEST_REC; #define window_get_theme(window) \ diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c index a92c6f3b..d317456b 100644 --- a/src/fe-text/gui-printtext.c +++ b/src/fe-text/gui-printtext.c @@ -348,14 +348,15 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, return; } - lineinfo.level = dest == NULL ? 0 : dest->level; gui = WINDOW_GUI(window); - lineinfo.time = (gui->use_insert_after && gui->insert_after_time) ? - gui->insert_after_time : time(NULL); + view = gui->view; + + lineinfo.level = dest == NULL ? 0 : dest->level; + lineinfo.time = + (gui->use_insert_after && gui->insert_after_time) ? gui->insert_after_time : time(NULL); lineinfo.format = dest != NULL && dest->flags & PRINT_FLAG_FORMAT ? LINE_INFO_FORMAT_SET : NULL; - view = gui->view; insert_after = gui->use_insert_after ? gui->insert_after : view->buffer->cur_line; diff --git a/src/fe-text/textbuffer-formats.c b/src/fe-text/textbuffer-formats.c index 84aaebb9..e3d331f1 100644 --- a/src/fe-text/textbuffer-formats.c +++ b/src/fe-text/textbuffer-formats.c @@ -14,7 +14,23 @@ #include TEXT_BUFFER_REC *color_buf; -int scrollback_format; +gboolean scrollback_format; +gboolean show_server_time; + +#if GLIB_CHECK_VERSION(2, 56, 0) +/* nothing */ +#else +/* compatibility code for old GLib */ +static GDateTime *g_date_time_new_from_iso8601(const gchar *iso_date, GTimeZone *default_tz) +{ + GTimeVal time; + if (g_time_val_from_iso8601(iso_date, &time)) { + return g_date_time_new_from_timeval_utc(&time); + } else { + return NULL; + } +} +#endif static void collector_free(GSList **collector) { @@ -86,6 +102,55 @@ static void format_rec_set_dest(TEXT_BUFFER_FORMAT_REC *rec, const TEXT_DEST_REC rec->flags = dest->flags & ~PRINT_FLAG_FORMAT; } +void textbuffer_meta_rec_free(TEXT_BUFFER_META_REC *rec) +{ + if (rec == NULL) + return; + + if (rec->hash != NULL) + g_hash_table_destroy(rec->hash); + + g_free(rec); +} + +static void meta_hash_create(struct _TEXT_BUFFER_META_REC *meta) +{ + if (meta->hash == NULL) { + meta->hash = g_hash_table_new_full(g_str_hash, (GEqualFunc) g_strcmp0, + (GDestroyNotify) i_refstr_release, + (GDestroyNotify) g_free); + } +} + +static TEXT_BUFFER_META_REC *line_meta_create(GHashTable *meta_hash) +{ + struct _TEXT_BUFFER_META_REC *meta; + GHashTableIter iter; + const char *key; + const char *val; + + if (meta_hash == NULL || g_hash_table_size(meta_hash) == 0) + return NULL; + + meta = g_new0(struct _TEXT_BUFFER_META_REC, 1); + + g_hash_table_iter_init(&iter, meta_hash); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { + if (g_strcmp0("time", key) == 0) { + GDateTime *time; + if ((time = g_date_time_new_from_iso8601(val, NULL)) != NULL) { + meta->server_time = g_date_time_to_unix(time); + g_date_time_unref(time); + } + } else { + meta_hash_create(meta); + g_hash_table_replace(meta->hash, i_refstr_intern(key), g_strdup(val)); + } + } + + return meta; +} + static LINE_INFO_REC *store_lineinfo_tmp(TEXT_DEST_REC *dest) { GUI_WINDOW_REC *gui; @@ -120,7 +185,7 @@ static void free_lineinfo_tmp(WINDOW_REC *window) info = buffer->cur_info->data; buffer->cur_info = g_slist_delete_link(buffer->cur_info, buffer->cur_info); - textbuffer_format_rec_free(info->format); + textbuffer_line_info_free1(info); g_free(info); } @@ -211,14 +276,17 @@ static void sig_gui_print_text_finished(WINDOW_REC *window, TEXT_DEST_REC *dest) info->format->expando_cache = reverse_collector(info->format->expando_cache); format_rec_set_dest(info->format, dest); + info->meta = line_meta_create(dest->meta); + info->level |= MSGLEVEL_FORMAT; /* the line will be inserted into the view with textbuffer_view_insert_line by gui-printtext.c:view_add_eol */ insert_after = textbuffer_insert(buffer, insert_after, (const unsigned char[]){}, 0, info); - /* the TEXT_BUFFER_FORMAT_REC pointer is now owned by the textbuffer */ + /* the TEXT_BUFFER_FORMAT_REC and meta pointer is now owned by the textbuffer */ info->format = LINE_INFO_FORMAT_SET; + info->meta = NULL; if (gui->use_insert_after) gui->insert_after = insert_after; @@ -294,12 +362,14 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) THEME_REC *theme; int formatnum; TEXT_BUFFER_FORMAT_REC *format_rec; + TEXT_BUFFER_META_REC *meta; char *str; curr = line; line = NULL; - format_rec = curr->info.format; + meta = curr->info.meta; + format_create_dest_tag( &dest, format_rec->server_tag != NULL ? server_find_tag(format_rec->server_tag) : NULL, @@ -326,7 +396,12 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) } if (text != NULL && *text != '\0') { - current_time = curr->info.time; + reference_time = curr->info.time; + if (show_server_time && meta != NULL && meta->server_time != 0) { + current_time = meta->server_time; + } else { + current_time = curr->info.time; + } tmp = format_get_level_tag(theme, &dest); str = !theme->info_eol ? format_add_linestart(text, tmp) : @@ -334,7 +409,7 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) g_free_not_null(tmp); g_free_not_null(text); text = str; - tmp = format_get_line_start(theme, &dest, curr->info.time); + tmp = format_get_line_start(theme, &dest, current_time); str = !theme->info_eol ? format_add_linestart(text, tmp) : format_add_lineend(text, tmp); g_free_not_null(tmp); @@ -345,7 +420,7 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) dest.flags |= PRINT_FLAG_FORMAT; - current_time = (time_t) -1; + reference_time = current_time = (time_t) -1; } else if (format_rec->format != NULL) { g_free(text); text = NULL; @@ -363,11 +438,13 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) static void read_settings(void) { scrollback_format = settings_get_bool("scrollback_format"); + show_server_time = settings_get_bool("show_server_time"); } void textbuffer_formats_init(void) { settings_add_bool("lookandfeel", "scrollback_format", TRUE); + settings_add_bool("lookandfeel", "show_server_time", FALSE); read_settings(); signal_add("print format", (SIGNAL_FUNC) sig_print_format); diff --git a/src/fe-text/textbuffer-formats.h b/src/fe-text/textbuffer-formats.h index b57f61c9..d595df8a 100644 --- a/src/fe-text/textbuffer-formats.h +++ b/src/fe-text/textbuffer-formats.h @@ -3,6 +3,11 @@ #include +typedef struct _TEXT_BUFFER_META_REC { + gint64 server_time; + GHashTable *hash; +} TEXT_BUFFER_META_REC; + typedef struct _TEXT_BUFFER_FORMAT_REC { char *module; char *format; @@ -17,6 +22,7 @@ typedef struct _TEXT_BUFFER_FORMAT_REC { } TEXT_BUFFER_FORMAT_REC; void textbuffer_format_rec_free(TEXT_BUFFER_FORMAT_REC *rec); +void textbuffer_meta_rec_free(TEXT_BUFFER_META_REC *rec); char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line); void textbuffer_formats_init(void); void textbuffer_formats_deinit(void); diff --git a/src/fe-text/textbuffer.c b/src/fe-text/textbuffer.c index b8fd2090..8761507b 100644 --- a/src/fe-text/textbuffer.c +++ b/src/fe-text/textbuffer.c @@ -54,8 +54,7 @@ void textbuffer_destroy(TEXT_BUFFER_REC *buffer) g_string_free(buffer->cur_text, TRUE); for (tmp = buffer->cur_info; tmp != NULL; tmp = tmp->next) { LINE_INFO_REC *info = buffer->cur_info->data; - textbuffer_format_rec_free(info->format); - g_free(info->text); + textbuffer_line_info_free1(info); g_free(info); } g_slist_free(buffer->cur_info); @@ -64,6 +63,13 @@ void textbuffer_destroy(TEXT_BUFFER_REC *buffer) g_slice_free(TEXT_BUFFER_REC, buffer); } +void textbuffer_line_info_free1(LINE_INFO_REC *info) +{ + textbuffer_format_rec_free(info->format); + textbuffer_meta_rec_free(info->meta); + g_free(info->text); +} + static void text_chunk_append(TEXT_BUFFER_REC *buffer, const unsigned char *data, int len) { @@ -198,8 +204,7 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line) line->prev = line->next = NULL; buffer->lines_count--; - g_free(line->info.text); - textbuffer_format_rec_free(line->info.format); + textbuffer_line_info_free1(&line->info); g_slice_free(LINE_REC, line); } @@ -212,10 +217,9 @@ void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer) while (buffer->first_line != NULL) { line = buffer->first_line->next; - g_free(buffer->first_line->info.text); - textbuffer_format_rec_free(buffer->first_line->info.format); + textbuffer_line_info_free1(&buffer->first_line->info); g_slice_free(LINE_REC, buffer->first_line); - buffer->first_line = line; + buffer->first_line = line; } buffer->lines_count = 0; diff --git a/src/fe-text/textbuffer.h b/src/fe-text/textbuffer.h index 5aaa817c..bb4e820a 100644 --- a/src/fe-text/textbuffer.h +++ b/src/fe-text/textbuffer.h @@ -17,6 +17,7 @@ typedef struct { int level; time_t time; char *text; + struct _TEXT_BUFFER_META_REC *meta; struct _TEXT_BUFFER_FORMAT_REC *format; } LINE_INFO_REC; @@ -84,6 +85,7 @@ LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after, void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line); /* Removes all lines from buffer, ignoring reference counters */ void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer); +void textbuffer_line_info_free1(LINE_INFO_REC *info); void textbuffer_line2text(TEXT_BUFFER_REC *buffer, LINE_REC *line, int coloring, GString *str); GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index cde6a4fc..f29a8f91 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -242,6 +242,7 @@ static void server_init(IRC_SERVER_REC *server) irc_cap_toggle(server, CAP_CHGHOST, TRUE); irc_cap_toggle(server, CAP_ACCOUNT_NOTIFY, TRUE); irc_cap_toggle(server, CAP_SELF_MESSAGE, TRUE); + irc_cap_toggle(server, CAP_SERVER_TIME, TRUE); irc_send_cmd_now(server, "CAP LS " CAP_LS_VERSION); diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h index e775e505..4833972a 100644 --- a/src/irc/core/irc-servers.h +++ b/src/irc/core/irc-servers.h @@ -26,6 +26,7 @@ #define CAP_CHGHOST "chghost" #define CAP_ACCOUNT_NOTIFY "account-notify" #define CAP_SELF_MESSAGE "znc.in/self-message" +#define CAP_SERVER_TIME "server-time" /* returns IRC_SERVER_REC if it's IRC server, NULL if it isn't */ #define IRC_SERVER(server) \ diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c index f3f9f307..96a09333 100644 --- a/src/irc/core/irc.c +++ b/src/irc/core/irc.c @@ -19,14 +19,15 @@ */ #include "module.h" -#include -#include -#include -#include #include +#include +#include +#include +#include +#include -#include #include +#include #include char *current_server_event; @@ -383,11 +384,83 @@ static void irc_server_event(IRC_SERVER_REC *server, const char *line, g_free(event); } -static void irc_server_event_tags(IRC_SERVER_REC *server, const char *line, - const char *nick, const char *address, const char *tags) +static void unescape_tag(char *tag) { + char *tmp; + + if (tag == NULL) + return; + + tmp = tag; + for (; *tmp != '\0'; tmp++, tag++) { + if (*tmp == '\\') { + tmp++; + switch (*tmp) { + case ':': + *tag = ';'; + break; + case 'n': + *tag = '\n'; + break; + case 'r': + *tag = '\r'; + break; + case 's': + *tag = ' '; + break; + default: + *tag = *tmp; + break; + } + } else { + *tag = *tmp; + } + } + *tag = '\0'; +} + +static gboolean i_str0_equal(const char *s1, const char *s2) +{ + return g_strcmp0(s1, s2) == 0; +} + +GHashTable *irc_parse_message_tags(const char *tags) +{ + char **split, **tmp, **kv; + GHashTable *hash; + + hash = g_hash_table_new_full(g_str_hash, (GEqualFunc) i_str0_equal, + (GDestroyNotify) i_refstr_release, (GDestroyNotify) g_free); + split = g_strsplit(tags, ";", -1); + for (tmp = split; *tmp != NULL; tmp++) { + kv = g_strsplit(*tmp, "=", 2); + unescape_tag(kv[1]); + g_hash_table_replace(hash, i_refstr_intern(kv[0]), + g_strdup(kv[1] == NULL ? "" : kv[1])); + g_strfreev(kv); + } + g_strfreev(split); + return hash; +} + +static void irc_server_event_tags(IRC_SERVER_REC *server, const char *line, const char *nick, + const char *address, const char *tags) +{ + char *timestr; + GHashTable *tags_hash = NULL; + + if (tags != NULL && *tags != '\0') { + tags_hash = irc_parse_message_tags(tags); + if ((timestr = g_hash_table_lookup(tags_hash, "time")) != NULL) { + server_meta_stash(SERVER(server), "time", timestr); + } + } + if (*line != '\0') signal_emit_id(signal_server_event, 4, server, line, nick, address); + + if (tags_hash != NULL) + g_hash_table_destroy(tags_hash); } static char *irc_parse_prefix(char *line, char **nick, char **address, char **tags) @@ -449,6 +522,8 @@ static void irc_parse_incoming_line(IRC_SERVER_REC *server, char *line) line = irc_parse_prefix(line, &nick, &address, &tags); if (*line != '\0' || tags != NULL) signal_emit_id(signal_server_event_tags, 5, server, line, nick, address, tags); + + server_meta_clear_all(SERVER(server)); } /* input function: handle incoming server messages */ diff --git a/src/irc/core/irc.h b/src/irc/core/irc.h index 3f1a9887..8803f662 100644 --- a/src/irc/core/irc.h +++ b/src/irc/core/irc.h @@ -48,6 +48,9 @@ void irc_send_cmd_first(IRC_SERVER_REC *server, const char *cmd); void irc_send_cmd_full(IRC_SERVER_REC *server, const char *cmd, int send_now, int immediate, int raw); +/* Extract a tag value from tags */ +GHashTable *irc_parse_message_tags(const char *tags); + /* Get count parameters from data */ #include char *event_get_param(char **data); diff --git a/src/perl/common/Server.xs b/src/perl/common/Server.xs index 7ed41109..60878a6a 100644 --- a/src/perl/common/Server.xs +++ b/src/perl/common/Server.xs @@ -103,3 +103,17 @@ send_message(server, target, msg, target_type) CODE: server->send_message(server, target, msg, target_type); +void +server_meta_stash(server, meta_key, meta_value) + Irssi::Server server + char *meta_key + char *meta_value + +char * +server_meta_stash_find(server, meta_key) + Irssi::Server server + char *meta_key +CODE: + RETVAL = (char *) server_meta_stash_find(server, meta_key); +OUTPUT: + RETVAL diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs index f64d9728..f973da22 100644 --- a/src/perl/irc/Irc.xs +++ b/src/perl/irc/Irc.xs @@ -199,10 +199,29 @@ static PLAIN_OBJECT_INIT_REC irc_plains[] = { { NULL, NULL } }; -MODULE = Irssi::Irc PACKAGE = Irssi::Irc +MODULE = Irssi::Irc PACKAGE = Irssi::Irc PREFIX = irc_ PROTOTYPES: ENABLE +void +irc_parse_message_tags(tags) + char *tags +PREINIT: + HV *hv; + GHashTable *hash; + GHashTableIter iter; + char *key; + char *val; +PPCODE: + hv = newHV(); + hash = irc_parse_message_tags(tags); + g_hash_table_iter_init(&iter, hash); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { + (void) hv_store(hv, key, strlen(key), new_pv(val), 0); + } + g_hash_table_destroy(hash); + XPUSHs(sv_2mortal(newRV_noinc((SV *) hv))); + void init() PREINIT: diff --git a/src/perl/textui/TextBuffer.xs b/src/perl/textui/TextBuffer.xs index ae3973c8..4859f3da 100644 --- a/src/perl/textui/TextBuffer.xs +++ b/src/perl/textui/TextBuffer.xs @@ -68,3 +68,29 @@ PPCODE: (void) hv_store(hv, "text", 4, new_pv(l->info.text), 0); } XPUSHs(sv_2mortal(newRV_noinc((SV *) hv))); + +void +textbuffer_line_get_meta(line) + Irssi::TextUI::Line line +PREINIT: + HV *hv; + LINE_REC *l; + TEXT_BUFFER_META_REC *m; + GHashTableIter iter; + char *key; + char *val; +PPCODE: + hv = newHV(); + l = line->line; + if (l->info.meta != NULL) { + m = l->info.meta; + if (m->hash != NULL) { + g_hash_table_iter_init(&iter, m->hash); + while ( + g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { + (void) hv_store(hv, key, strlen(key), new_pv(val), 0); + } + } + (void) hv_store(hv, "server_time", 11, newSViv(m->server_time), 0); + } + XPUSHs(sv_2mortal(newRV_noinc((SV *) hv)));