unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

api-history-backend.c (7045B)

      1 /************************************************************************
      2  * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/api-history-backend.c
      3  * (c) 2019- Bram Matthys and The UnrealIRCd team
      4  *
      5  * See file AUTHORS in IRC package for additional names of
      6  * the programmers. 
      7  *
      8  * This program is free software; you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation; either version 1, or (at your option)
     11  * any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program; if not, write to the Free Software
     20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     21  */
     22 
     23 #include "unrealircd.h"
     24 
     25 MODVAR HistoryBackend *historybackends = NULL; /**< List of registered history backends */
     26 
     27 void history_backend_init(void)
     28 {
     29 }
     30 
     31 /**
     32  * Returns a history backend based on the given token name.
     33  *
     34  * @param name The name of the history backend.
     35  * @return Returns the handle to the history backend,
     36  *         or NULL if not found.
     37  */
     38 HistoryBackend *HistoryBackendFind(const char *name)
     39 {
     40 	HistoryBackend *m;
     41 
     42 	for (m = historybackends; m; m = m->next)
     43 	{
     44 		if (!strcasecmp(name, m->name))
     45 			return m;
     46 	}
     47 	return NULL;
     48 }
     49 
     50 /**
     51  * Adds a new history backend.
     52  *
     53  * @param module The module which provides this history backend.
     54  * @param mreq   The details of the request such as token name, access check handler, etc.
     55  * @return Returns the handle to the new token if successful, otherwise NULL.
     56  *         The module's error code contains specific information about the
     57  *         error.
     58  */
     59 HistoryBackend *HistoryBackendAdd(Module *module, HistoryBackendInfo *mreq)
     60 {
     61 	HistoryBackend *m;
     62 	int exists = 0;
     63 	ModuleObject *mobj;
     64 
     65 	if (!mreq->history_add || !mreq->history_request ||
     66 	    !mreq->history_destroy || !mreq->history_set_limit)
     67 	{
     68 		module->errorcode = MODERR_INVALID;
     69 		unreal_log(ULOG_ERROR, "module", "HISTORYBACKENDADD_API_ERROR", NULL,
     70 			   "HistoryBackendAdd(): missing a handler for add/del/request/destroy/set_limit. Module: $module_name",
     71 			   log_data_string("module_name", module->header->name));
     72 		return NULL;
     73 	}
     74 	m = HistoryBackendFind(mreq->name);
     75 	if (m)
     76 	{
     77 		exists = 1;
     78 		if (m->unloaded)
     79 		{
     80 			m->unloaded = 0;
     81 		} else {
     82 			module->errorcode = MODERR_EXISTS;
     83 			return NULL;
     84 		}
     85 	} else {
     86 		/* New history backend */
     87 		m = safe_alloc(sizeof(HistoryBackend));
     88 		safe_strdup(m->name, mreq->name);
     89 	}
     90 
     91 	/* Add or update the following fields: */
     92 	m->owner = module;
     93 	m->history_add = mreq->history_add;
     94 	m->history_request = mreq->history_request;
     95 	m->history_destroy = mreq->history_destroy;
     96 	m->history_set_limit = mreq->history_set_limit;
     97 
     98 	if (!exists)
     99 		AddListItem(m, historybackends);
    100 
    101 	mobj = safe_alloc(sizeof(ModuleObject));
    102 	mobj->type = MOBJ_HISTORY_BACKEND;
    103 	mobj->object.history_backend = m;
    104 	AddListItem(mobj, module->objects);
    105 	module->errorcode = MODERR_NOERROR;
    106 
    107 	return m;
    108 }
    109 
    110 void unload_history_backend_commit(HistoryBackend *m)
    111 {
    112 	/* Destroy the object */
    113 	DelListItem(m, historybackends);
    114 	safe_free(m->name);
    115 	safe_free(m);
    116 }
    117 
    118 /**
    119  * Removes the specified history backend.
    120  *
    121  * @param m The history backend to remove.
    122  */
    123 void HistoryBackendDel(HistoryBackend *m)
    124 {
    125 	if (m->owner)
    126 	{
    127 		ModuleObject *mobj;
    128 		for (mobj = m->owner->objects; mobj; mobj = mobj->next) {
    129 			if (mobj->type == MOBJ_HISTORY_BACKEND && mobj->object.history_backend == m)
    130 			{
    131 				DelListItem(mobj, m->owner->objects);
    132 				safe_free(mobj);
    133 				break;
    134 			}
    135 		}
    136 		m->owner = NULL;
    137 	}
    138 
    139 	if (loop.rehashing)
    140 		m->unloaded = 1;
    141 	else
    142 		unload_history_backend_commit(m);
    143 }
    144 
    145 void unload_all_unused_history_backends(void)
    146 {
    147 	HistoryBackend *m, *m_next;
    148 
    149 	for (m = historybackends; m; m = m_next)
    150 	{
    151 		m_next = m->next;
    152 		if (m->unloaded)
    153 			unload_history_backend_commit(m);
    154 	}
    155 }
    156 
    157 int history_add(const char *object, MessageTag *mtags, const char *line)
    158 {
    159 	HistoryBackend *hb;
    160 
    161 	for (hb = historybackends; hb; hb=hb->next)
    162 		hb->history_add(object, mtags, line);
    163 
    164 	return 1;
    165 }
    166 
    167 HistoryResult *history_request(const char *object, HistoryFilter *filter)
    168 {
    169 	HistoryBackend *hb = historybackends;
    170 	HistoryResult *r;
    171 	HistoryLogLine *l;
    172 
    173 	if (!hb)
    174 		return 0; /* no history backend loaded */
    175 
    176 	/* Right now we return whenever the first backend has a result. */
    177 	for (hb = historybackends; hb; hb = hb->next)
    178 		if ((r = hb->history_request(object, filter)))
    179 			return r;
    180 
    181 	return NULL;
    182 }
    183 
    184 int history_destroy(const char *object)
    185 {
    186 	HistoryBackend *hb;
    187 
    188 	for (hb = historybackends; hb; hb=hb->next)
    189 		hb->history_destroy(object);
    190 
    191 	return 1;
    192 }
    193 
    194 int history_set_limit(const char *object, int max_lines, long max_t)
    195 {
    196 	HistoryBackend *hb;
    197 
    198 	for (hb = historybackends; hb; hb=hb->next)
    199 		hb->history_set_limit(object, max_lines, max_t);
    200 
    201 	return 1;
    202 }
    203 
    204 /** Free a HistoryResult object that was returned from request_result() earlier */
    205 void free_history_result(HistoryResult *r)
    206 {
    207 	HistoryLogLine *l, *l_next;
    208 	for (l = r->log; l; l = l_next)
    209 	{
    210 		l_next = l->next;
    211 		free_message_tags(l->mtags);
    212 		safe_free(l);
    213 	}
    214 	safe_free(r->object);
    215 	safe_free(r);
    216 }
    217 
    218 /** Returns 1 if the client can receive channel history, 0 if not.
    219  * @param client	The client to check.
    220  * @note It is recommend to call this function BEFORE trying to
    221  *       retrieve channel history via history_request(),
    222  *       as to not waste useless resources.
    223  */
    224 int can_receive_history(Client *client)
    225 {
    226 	if (HasCapability(client, "server-time"))
    227 		return 1;
    228 	return 0;
    229 }
    230 
    231 static void history_send_result_line(Client *client, HistoryLogLine *l, const char *batchid)
    232 {
    233 	if (BadPtr(batchid))
    234 	{
    235 		sendto_one(client, l->mtags, "%s", l->line);
    236 	} else {
    237 		MessageTag *m = safe_alloc(sizeof(MessageTag));
    238 		m->name = "batch";
    239 		m->value = strdup(batchid);
    240 		AddListItem(m, l->mtags);
    241 		sendto_one(client, l->mtags, "%s", l->line);
    242 		safe_free(m->value);
    243 		DelListItem(m, l->mtags);
    244 		safe_free(m);
    245 	}
    246 }
    247 
    248 /** Send the result of a history_request() to the client.
    249  * @param client	The client to send to.
    250  * @param r		The history result retrieved via history_request().
    251  */
    252 void history_send_result(Client *client, HistoryResult *r)
    253 {
    254 	char batch[BATCHLEN+1];
    255 	HistoryLogLine *l;
    256 
    257 	if (!can_receive_history(client))
    258 		return;
    259 
    260 	batch[0] = '\0';
    261 	if (HasCapability(client, "batch"))
    262 	{
    263 		/* Start a new batch */
    264 		generate_batch_id(batch);
    265 		sendto_one(client, NULL, ":%s BATCH +%s chathistory %s", me.name, batch, r->object);
    266 	}
    267 
    268 	for (l = r->log; l; l = l->next)
    269 		history_send_result_line(client, l, batch);
    270 
    271 	/* End of batch */
    272 	if (*batch)
    273 		sendto_one(client, NULL, ":%s BATCH -%s", me.name, batch);
    274 }
    275 
    276 void free_history_filter(HistoryFilter *f)
    277 {
    278 	safe_free(f->timestamp_a);
    279 	safe_free(f->msgid_a);
    280 	safe_free(f->timestamp_b);
    281 	safe_free(f->msgid_b);
    282 	safe_free(f);
    283 }