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 }