static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { char *line, *str; g_return_if_fail(data != NULL); if (item == NULL) return; line = settings_get_bool("expand_escapes") ? expand_escapes(data, server, item) : g_strdup(data); /* check for automatic nick completion */ if (completion_auto && IS_CHANNEL(item)) { str = auto_complete(CHANNEL(item), line); if (str != NULL) { g_free(line); line = str; } } str = g_strdup_printf(IS_CHANNEL(item) ? "-channel %s %s" : IS_QUERY(item) ? "-nick %s %s" : "%s %s", item->name, line); signal_emit("command msg", 3, str, server, item); g_free(str); g_free(line); signal_stop(); }
/* Send the auto send command to channel */ void channel_send_autocommands(CHANNEL_REC *channel) { CHANNEL_SETUP_REC *rec; NICK_REC *nick; char **bots, **bot; g_return_if_fail(IS_CHANNEL(channel)); rec = channel_setup_find(channel->name, channel->server->connrec->chatnet); if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd) return; if (rec->botmasks == NULL || !*rec->botmasks) { /* just send the command. */ eval_special_string(rec->autosendcmd, "", channel->server, channel); return; } /* find first available bot.. */ bots = g_strsplit(rec->botmasks, " ", -1); for (bot = bots; *bot != NULL; bot++) { const char *botnick = *bot; nick = nicklist_find_mask(channel, channel->server->isnickflag(*botnick) ? botnick+1 : botnick); if (nick != NULL && match_nick_flags(channel->server, nick, *botnick)) { eval_special_string(rec->autosendcmd, nick->nick, channel->server, channel); break; } } g_strfreev(bots); }
static void sig_layout_save_item(WINDOW_REC *window, WI_ITEM_REC *item, CONFIG_NODE *node) { CONFIG_NODE *subnode; CHAT_PROTOCOL_REC *proto; const char *type; WINDOW_BIND_REC *rec; type = module_find_id_str("WINDOW ITEM TYPE", item->type); if (type == NULL) return; subnode = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK); iconfig_node_set_str(subnode, "type", type); proto = item->chat_type == 0 ? NULL : chat_protocol_find_id(item->chat_type); if (proto != NULL) iconfig_node_set_str(subnode, "chat_type", proto->name); iconfig_node_set_str(subnode, "name", item->visible_name); if (item->server != NULL) { iconfig_node_set_str(subnode, "tag", item->server->tag); if (IS_CHANNEL(item)) { rec = window_bind_add(window, item->server->tag, item->visible_name); if (rec != NULL) rec->sticky = TRUE; } } else if (IS_QUERY(item)) { iconfig_node_set_str(subnode, "tag", QUERY(item)->server_tag); } }
static void sig_channel_created(CHANNEL_REC *channel) { g_return_if_fail(IS_CHANNEL(channel)); channel->nicks = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); }
void channel_destroy(CHANNEL_REC *channel) { g_return_if_fail(IS_CHANNEL(channel)); if (channel->destroying) return; channel->destroying = TRUE; channels = g_slist_remove(channels, channel); channel->server->channels = g_slist_remove(channel->server->channels, channel); signal_emit("channel destroyed", 1, channel); MODULE_DATA_DEINIT(channel); g_free_not_null(channel->hilight_color); g_free_not_null(channel->topic); g_free_not_null(channel->topic_by); g_free_not_null(channel->key); g_free(channel->mode); g_free(channel->name); g_free(channel->visible_name); channel->type = 0; g_free(channel); }
/* Find nick mask, wildcards allowed */ NICK_REC *nicklist_find_mask(CHANNEL_REC *channel, const char *mask) { NICK_REC *nickrec; char *nick, *host; g_return_val_if_fail(IS_CHANNEL(channel), NULL); g_return_val_if_fail(mask != NULL, NULL); nick = g_strdup(mask); host = strchr(nick, '!'); if (host != NULL) *host++ = '\0'; if (strchr(nick, '*') || strchr(nick, '?')) { g_free(nick); return nicklist_find_wildcards(channel, mask); } nickrec = g_hash_table_lookup(channel->nicks, nick); if (host != NULL) { while (nickrec != NULL) { if (nickrec->host != NULL && match_wildcards(host, nickrec->host)) break; /* match */ nickrec = nickrec->next; } } g_free(nick); return nickrec; }
static void sig_get_active_channel(const char **name) { *name = IS_XMPP_SERVER(active_win->active_server) && IS_CHANNEL(active_win->active) ? ((CHANNEL_REC *)active_win->active)->name : NULL; }
static void autolog_open_check(TEXT_DEST_REC *dest) { const char *deftarget; SERVER_REC *server = dest->server; const char *server_tag = dest->server_tag; const char *target = dest->target; int level = dest->level; /* FIXME: kind of a kludge, but we don't want to reopen logs when we're parting the channel with /WINDOW CLOSE.. Maybe a small timeout would be nice instead of immediately closing the log file after "window item destroyed" */ if (level == MSGLEVEL_PARTS || (autolog_level & level) == 0 || target == NULL || *target == '\0') return; deftarget = server ? server->nick : "unknown"; /* log only channels that have been saved to the config */ if (settings_get_bool("autolog_only_saved_channels") && IS_CHANNEL(window_item_find(server, target)) && channel_setup_find(target, server_tag) == NULL) return; if (autolog_ignore_targets != NULL && strarray_find_dest(autolog_ignore_targets, dest)) return; if (target != NULL) autolog_open(server, server_tag, g_strcmp0(target, "*") ? target : deftarget); }
/* Find nick */ NICK_REC *nicklist_find(CHANNEL_REC *channel, const char *nick) { g_return_val_if_fail(IS_CHANNEL(channel), NULL); g_return_val_if_fail(nick != NULL, NULL); return g_hash_table_lookup(channel->nicks, nick); }
static void sig_channel_destroyed(CHANNEL_REC *channel) { g_return_if_fail(IS_CHANNEL(channel)); g_hash_table_foreach(channel->nicks, (GHFunc) nicklist_remove_hash, channel); g_hash_table_destroy(channel->nicks); }
/* Remove nick from list */ void nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick) { g_return_if_fail(IS_CHANNEL(channel)); g_return_if_fail(nick != NULL); nick_hash_remove(channel, nick); nicklist_destroy(channel, nick); }
/* expands to your usermode on channel, op '@', halfop '%', "+" voice */ static char *expando_cumode(SERVER_REC *server, void *item, int *free_ret) { if (IS_CHANNEL(item) && CHANNEL(item)->ownnick) { return NICK(CHANNEL(item)->ownnick)->op ? "@" : NICK(CHANNEL(item)->ownnick)->halfop ? "%" : NICK(CHANNEL(item)->ownnick)->voice ? "+" : ""; } return ""; }
void channel_change_visible_name(CHANNEL_REC *channel, const char *name) { g_return_if_fail(IS_CHANNEL(channel)); g_free(channel->visible_name); channel->visible_name = g_strdup(name); signal_emit("window item name changed", 1, channel); }
/* Get list of nicks */ GSList *nicklist_getnicks(CHANNEL_REC *channel) { GSList *list; g_return_val_if_fail(IS_CHANNEL(channel), NULL); list = NULL; g_hash_table_foreach(channel->nicks, (GHFunc) get_nicks_hash, &list); return list; }
static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { char *line, *str, *target; g_return_if_fail(data != NULL); if (item == NULL) return; if (*data == '\0') { /* empty line, forget it. */ signal_stop(); return; } line = settings_get_bool("expand_escapes") ? expand_escapes(data, server, item) : g_strdup(data); /* check for automatic nick completion */ if (completion_auto && IS_CHANNEL(item)) { str = auto_complete(CHANNEL(item), line); if (str != NULL) { g_free(line); line = str; } } /* the nick is quoted in case it contains '-' character. also spaces should work too now :) The nick is also escaped in case it contains '\' characters */ target = escape_string(window_item_get_target(item)); str = g_strdup_printf(IS_CHANNEL(item) ? "-channel \"%s\" %s" : IS_QUERY(item) ? "-nick \"%s\" %s" : "%s %s", target, line); g_free(target); signal_emit("command msg", 3, str, server, item); g_free(str); g_free(line); signal_stop(); }
static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item) { g_return_if_fail(window != NULL); if (item == NULL) return; if (g_slist_length(window->items) > 1 && IS_CHANNEL(item)) { printformat(item->server, item->name, MSGLEVEL_CLIENTNOTICE, IRCTXT_TALKING_IN, item->name); signal_stop(); } }
NICK_REC *nicklist_find_unique(CHANNEL_REC *channel, const char *nick, void *id) { NICK_REC *rec; g_return_val_if_fail(IS_CHANNEL(channel), NULL); g_return_val_if_fail(nick != NULL, NULL); rec = g_hash_table_lookup(channel->nicks, nick); while (rec != NULL && rec->unique_id != id) rec = rec->next; return rec; }
char * xmpp_get_dest(const char *cmd_dest, XMPP_SERVER_REC *server, WI_ITEM_REC *item) { NICK_REC *nick; char *dest; if (cmd_dest == NULL || *cmd_dest == '\0') return IS_QUERY(item) ? g_strdup(QUERY(item)->name) : g_strconcat(server->jid, "/", server->resource, (void *)NULL); if (IS_CHANNEL(item) && (nick = nicklist_find(CHANNEL(item), cmd_dest)) != NULL) return g_strdup(nick->host); if ((dest = rosters_resolve_name(server, cmd_dest)) != NULL) return dest; return g_strdup(cmd_dest); }
static void sig_complete_topic(GList **list, WINDOW_REC *window, const char *word, const char *line, int *want_space) { const char *topic; g_return_if_fail(list != NULL); g_return_if_fail(word != NULL); if (*word == '\0' && IS_CHANNEL(window->active)) { topic = CHANNEL(window->active)->topic; if (topic != NULL) { *list = g_list_append(NULL, g_strdup(topic)); signal_stop(); } } }
GSList *nicklist_find_multiple(CHANNEL_REC *channel, const char *mask) { GSList *nicks, *tmp, *next; g_return_val_if_fail(IS_CHANNEL(channel), NULL); g_return_val_if_fail(mask != NULL, NULL); nicks = nicklist_getnicks(channel); for (tmp = nicks; tmp != NULL; tmp = next) { NICK_REC *nick = tmp->data; next = tmp->next; if (!mask_match_address(channel->server, mask, nick->nick, nick->host)) nicks = g_slist_remove(nicks, tmp->data); } return nicks; }
static void sig_complete_word(GList **list, WINDOW_REC *window, const char *word, const char *linestart, int *want_space) { XMPP_SERVER_REC *server; g_return_if_fail(list != NULL); g_return_if_fail(window != NULL); g_return_if_fail(word != NULL); if ((server = XMPP_SERVER(window->active_server)) == NULL) return; if (g_ascii_strncasecmp(linestart, settings_get_str("cmdchars"), 1) == 0) { *list = g_list_concat(*list, get_nicks(server, *word == '"' ? word+1 : word , TRUE, TRUE)); } else if (!IS_CHANNEL(window->active)) *list = g_list_concat(*list, get_nicks(server, word, FALSE, TRUE)); }
static void sig_complete_topic_plus(GList **list, WINDOW_REC *window, const char *word, const char *line, int *want_space) { char *p; char *topic; int topic_len; const char *mark; int mark_len; g_return_if_fail(list != NULL); g_return_if_fail(word != NULL); if (*word == '\0' && IS_CHANNEL(window->active)) { topic = g_strdup(CHANNEL(window->active)->topic); if (topic != NULL) { mark = settings_get_str("mark_encrypted"); if (!IsNULLorEmpty(mark)) { topic_len = strlen(topic); mark_len = strlen(mark); if (settings_get_int("mark_position") == 0) { // suffix p = topic + (topic_len - mark_len); if (strncmp(p, mark, mark_len) == 0) { *p = '\0'; // Remove mark } } else { // prefix if (strncmp(topic, mark, mark_len) == 0) { g_memmove(topic, topic + mark_len, topic_len - mark_len); } } } *list = g_list_append(NULL, topic); signal_stop(); } } }
/* SYNTAX: ME <message> */ static void cmd_me(const char *data, XMPP_SERVER_REC *server, WI_ITEM_REC *item) { const char *target; char *str, *recoded; int type; CMD_XMPP_SERVER(server); if (*data == '\0') return; g_strstrip((char *)data); if (*data == '\0') return; target = window_item_get_target(item); type = IS_CHANNEL(item) ? SEND_TARGET_CHANNEL : SEND_TARGET_NICK; if (type == SEND_TARGET_NICK) signal_emit("message xmpp own_action", 4, server, data, target, SEND_TARGET_NICK); str = g_strconcat("/me ", data, (void *)NULL); recoded = recode_out(SERVER(server), str, target); g_free(str); server->send_message(SERVER(server), target, recoded, type); g_free(recoded); }
/* modes of current channel, if any */ static char *expando_chanmode(SERVER_REC *server, void *item, int *free_ret) { return !IS_CHANNEL(item) ? NULL : CHANNEL(item)->mode; }
/* convert _underlined_, /italics/, and *bold* words (and phrases) to use real underlining or bolding */ char *expand_emphasis(WI_ITEM_REC *item, const char *text) { GString *str; char *ret; int pos; int emphasis_italics; g_return_val_if_fail(text != NULL, NULL); emphasis_italics = settings_get_bool("emphasis_italics"); str = g_string_new(text); for (pos = 0; pos < str->len; pos++) { char type, *bgn, *end; bgn = str->str + pos; if (*bgn == '*') type = 2; /* bold */ else if (*bgn == '/' && emphasis_italics) type = 29; /* italics */ else if (*bgn == '_') type = 31; /* underlined */ else continue; /* check that the beginning marker starts a word, and that the matching end marker ends a word */ if ((pos > 0 && bgn[-1] != ' ') || !ishighalnum(bgn[1])) continue; if ((end = strchr(bgn+1, *bgn)) == NULL) continue; if (!ishighalnum(end[-1]) || ishighalnum(end[1]) || end[1] == type || end[1] == '*' || end[1] == '_' || /* special case for italics to not emphasise common paths by skipping /.../.X */ (type == 29 && i_ispunct(end[1]) && ishighalnum(end[2]))) continue; if (IS_CHANNEL(item)) { /* check that this isn't a _nick_, we don't want to use emphasis on them. */ int found; char c; char *end2; /* check if _foo_ is a nick */ c = end[1]; end[1] = '\0'; found = nicklist_find(CHANNEL(item), bgn) != NULL; end[1] = c; if (found) continue; /* check if the whole 'word' (e.g. "_foo_^") is a nick in "_foo_^ ", end will be the second _, end2 the ^ */ end2 = end; while (isnickchar(end2[1])) end2++; c = end2[1]; end2[1] = '\0'; found = nicklist_find(CHANNEL(item), bgn) != NULL; end2[1] = c; if (found) continue; } /* allow only *word* emphasis, not *multiple words* */ if (!settings_get_bool("emphasis_multiword")) { char *c; for (c = bgn+1; c != end; c++) { if (!ishighalnum(*c)) break; } if (c != end) continue; } if (settings_get_bool("emphasis_replace")) { *bgn = *end = type; pos += (end-bgn); } else { g_string_insert_c(str, pos, type); pos += (end - bgn) + 2; g_string_insert_c(str, pos++, type); } } ret = str->str; g_string_free(str, FALSE); return ret; }
/* if you are a channel operator in $C, expands to a '@' */ static char *expando_chanop(SERVER_REC *server, void *item, int *free_ret) { return IS_CHANNEL(item) && CHANNEL(item)->chanop ? "@" : ""; }
/* SYNTAX: NAMES [-count | -ops -halfops -voices -normal] [<channels> | **] */ static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { CHANNEL_REC *chanrec; GHashTable *optlist; GString *unknowns; char *channel, **channels, **tmp; int flags; void *free_arg; g_return_if_fail(data != NULL); if (!IS_SERVER(server) || !server->connected) cmd_return_error(CMDERR_NOT_CONNECTED); if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, "names", &optlist, &channel)) return; if (strcmp(channel, "*") == 0 || *channel == '\0') { if (!IS_CHANNEL(item)) cmd_param_error(CMDERR_NOT_JOINED); channel = CHANNEL(item)->name; } flags = 0; if (g_hash_table_lookup(optlist, "ops") != NULL) flags |= CHANNEL_NICKLIST_FLAG_OPS; if (g_hash_table_lookup(optlist, "halfops") != NULL) flags |= CHANNEL_NICKLIST_FLAG_HALFOPS; if (g_hash_table_lookup(optlist, "voices") != NULL) flags |= CHANNEL_NICKLIST_FLAG_VOICES; if (g_hash_table_lookup(optlist, "normal") != NULL) flags |= CHANNEL_NICKLIST_FLAG_NORMAL; if (g_hash_table_lookup(optlist, "count") != NULL) flags |= CHANNEL_NICKLIST_FLAG_COUNT; if (flags == 0) flags = CHANNEL_NICKLIST_FLAG_ALL; unknowns = g_string_new(NULL); channels = g_strsplit(channel, ",", -1); for (tmp = channels; *tmp != NULL; tmp++) { chanrec = channel_find(server, *tmp); if (chanrec == NULL) g_string_sprintfa(unknowns, "%s,", *tmp); else { fe_channels_nicklist(chanrec, flags); signal_stop(); } } g_strfreev(channels); if (unknowns->len > 1) g_string_truncate(unknowns, unknowns->len-1); if (unknowns->len > 0 && strcmp(channel, unknowns->str) != 0) signal_emit("command names", 3, unknowns->str, server, item); g_string_free(unknowns, TRUE); cmd_params_free(free_arg); }
/* redraw channel */ static void statusbar_channel(SBAR_ITEM_REC *item, int ypos) { WINDOW_REC *window; WI_ITEM_REC *witem; CHANNEL_REC *channel; SERVER_REC *server; gchar channame[21], winnum[MAX_INT_STRLEN], *tmpname; int size_needed; int mode_size; window = item->bar->pos != STATUSBAR_POS_MIDDLE ? active_win : mainwindow_find_sbar(item); server = window == NULL ? NULL : window->active_server; ltoa(winnum, window == NULL ? 0 : window->refnum); witem = window != NULL && (IS_CHANNEL(window->active) || IS_QUERY(window->active)) ? window->active : NULL; if (witem == NULL) { /* display server tag */ channame[0] = '\0'; mode_size = 0; channel = NULL; size_needed = 3 + strlen(winnum) + (server == NULL ? 0 : (17+strlen(server->tag))); } else { /* display channel + mode */ tmpname = show_lowascii(witem->name); strncpy(channame, tmpname, 20); channame[20] = '\0'; g_free(tmpname); channel = CHANNEL(witem); if (channel == NULL) { mode_size = 0; } else { mode_size = strlen(channel->mode); if (mode_size > 0) mode_size += 3; /* (+) */ } size_needed = 3 + strlen(winnum) + strlen(channame) + mode_size; } if (item->size != size_needed) { /* we need more (or less..) space! */ statusbar_item_resize(item, size_needed); return; } move(ypos, item->xpos); set_color(stdscr, sbar_color_dim); addch('['); /* window number */ set_color(stdscr, sbar_color_normal); addstr(winnum); set_color(stdscr, sbar_color_dim); addch(':'); if (channame[0] == '\0' && server != NULL) { /* server tag */ set_color(stdscr, sbar_color_normal); addstr(server->tag); addstr(" (change with ^X)"); } else if (channame[0] != '\0') { /* channel + mode */ set_color(stdscr, sbar_color_normal); addstr(channame); if (mode_size) { set_color(stdscr, sbar_color_bold); addch('('); set_color(stdscr, sbar_color_dim); addch('+'); set_color(stdscr, sbar_color_normal); addstr(channel->mode); set_color(stdscr, sbar_color_bold); addch(')'); } } set_color(stdscr, sbar_color_dim); addch(']'); screen_refresh(NULL); }
/* convert _underlined_ and *bold* words (and phrases) to use real underlining or bolding */ char *expand_emphasis(WI_ITEM_REC *item, const char *text) { GString *str; char *ret; int pos; g_return_val_if_fail(text != NULL, NULL); str = g_string_new(text); for (pos = 0; pos < str->len; pos++) { char type, *bgn, *end; bgn = str->str + pos; if (*bgn == '*') type = 2; /* bold */ else if (*bgn == '_') type = 31; /* underlined */ else continue; /* check that the beginning marker starts a word, and that the matching end marker ends a word */ if ((pos > 0 && !i_isspace(bgn[-1])) || !ishighalnum(bgn[1])) continue; if ((end = strchr(bgn+1, *bgn)) == NULL) continue; if (!ishighalnum(end[-1]) || ishighalnum(end[1]) || end[1] == type || end[1] == '*' || end[1] == '_') continue; if (IS_CHANNEL(item)) { /* check that this isn't a _nick_, we don't want to use emphasis on them. */ int found; char c; c = end[1]; end[1] = '\0'; found = nicklist_find(CHANNEL(item), bgn) != NULL; end[1] = c; if (found) continue; } /* allow only *word* emphasis, not *multiple words* */ if (!settings_get_bool("emphasis_multiword")) { char *c; for (c = bgn+1; c != end; c++) { if (!ishighalnum(*c)) break; } if (c != end) continue; } if (settings_get_bool("emphasis_replace")) { *bgn = *end = type; pos += (end-bgn); } else { g_string_insert_c(str, pos, type); pos += (end - bgn) + 2; g_string_insert_c(str, pos++, type); } } ret = str->str; g_string_free(str, FALSE); return ret; }
/* SYNTAX: MSG [-<server tag>] [-channel | -nick] <targets> <message> */ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { GHashTable *optlist; char *target, *origtarget, *msg; void *free_arg; int free_ret, target_type = SEND_TARGET_NICK; g_return_if_fail(data != NULL); if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, "msg", &optlist, &target, &msg)) return; if (*target == '\0' || *msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); server = cmd_options_get_server("msg", optlist, server); if (server == NULL || !server->connected) cmd_param_error(CMDERR_NOT_CONNECTED); origtarget = target; free_ret = FALSE; if (strcmp(target, ",") == 0 || strcmp(target, ".") == 0) { target = parse_special(&target, server, item, NULL, &free_ret, NULL, 0); if (target != NULL && *target == '\0') { if (free_ret) g_free(target); target = NULL; free_ret = FALSE; } } if (target != NULL) { if (strcmp(target, "*") == 0) { /* send to active channel/query */ if (item == NULL) cmd_param_error(CMDERR_NOT_JOINED); target_type = IS_CHANNEL(item) ? SEND_TARGET_CHANNEL : SEND_TARGET_NICK; target = (char *) window_item_get_target(item); } else if (g_hash_table_lookup(optlist, "channel") != NULL) target_type = SEND_TARGET_CHANNEL; else if (g_hash_table_lookup(optlist, "nick") != NULL) target_type = SEND_TARGET_NICK; else { /* Need to rely on server_ischannel(). If the protocol doesn't really know if it's channel or nick based on the name, it should just assume it's nick, because when typing text to channels it's always sent with /MSG -channel. */ target_type = server_ischannel(server, target) ? SEND_TARGET_CHANNEL : SEND_TARGET_NICK; } } if (target != NULL) { char **splitmsgs; char **tmp = NULL; char *singlemsg[] = { msg, NULL }; char *m; int n = 0; /* * If split_message is NULL, the server doesn't need to split * long messages. */ if (server->split_message != NULL) splitmsgs = tmp = server->split_message(server, target, msg); else splitmsgs = singlemsg; while ((m = splitmsgs[n++])) { signal_emit("server sendmsg", 4, server, target, m, GINT_TO_POINTER(target_type)); signal_emit(target_type == SEND_TARGET_CHANNEL ? "message own_public" : "message own_private", 4, server, m, target, origtarget); } g_strfreev(tmp); } else { signal_emit("message own_private", 4, server, msg, target, origtarget); } if (free_ret && target != NULL) g_free(target); cmd_params_free(free_arg); }