00001
00013 #include <assert.h>
00014 #include <stdarg.h>
00015 #include <stddef.h>
00016 #include <stdio.h>
00017 #include <stdlib.h>
00018 #include <string.h>
00019 #include "boris.h"
00020
00021
00022
00023
00024 #define CHANNEL_SEND_MAX 1024
00025 #define DEBUG(msg, ...) fprintf(stderr, "%s():%d:" msg "\n", __func__, __LINE__, __VA_ARGS__)
00026 #if 0
00027 #define DEBUG(msg, ...) b_log(B_LOG_DEBUG, "channel", "%s():%d:" msg, __func__, __LINE__, __VA_ARGS__)
00028 #endif
00029
00030
00031
00032
00033
00037 struct plugin_channel_class {
00038 struct plugin_basic_class base_class;
00039 struct plugin_channel_interface channel_interface;
00040 };
00041
00042 struct channel {
00043
00044 unsigned nr_member;
00045 struct channel_member **member;
00046 };
00047
00048 struct channel_public {
00049 struct channel channel;
00050 LIST_ENTRY(struct channel_public) list;
00051 char *name;
00052 };
00053
00054 LIST_HEAD(struct channel_public_list, struct channel_public);
00055
00056
00057
00058
00059 extern const struct plugin_channel_class plugin_class;
00060
00061
00062
00063
00064
00065 static struct channel_public_list channel_public_list;
00066
00067
00068
00069
00070 static void channel_init(struct channel *ch) {
00071 ch->nr_member=0;
00072 ch->member=NULL;
00073 }
00074
00078 static struct channel_member **channel_find_member(struct channel *ch, struct channel_member *cm) {
00079 unsigned i;
00080
00081 DEBUG("looking for channel member %p(p=%p)", cm, cm?cm->p:NULL);
00082
00083 if(!ch) return NULL;
00084
00085 for(i=0;i<ch->nr_member;i++) {
00086 DEBUG("looking at %p...", ch->member[i]);
00087 if(ch->member[i]==cm) return &ch->member[i];
00088
00089 }
00090 DEBUG("not found %p(p=%p)", cm, cm?cm->p:NULL);
00091 return NULL;
00092 }
00093
00097 static int channel_add_member(struct channel *ch, struct channel_member *cm) {
00098 struct channel_member **newlist;
00099
00100 assert(ch != NULL);
00101 assert(cm != NULL);
00102
00103 if(!cm) return 1;
00104 if(channel_find_member(ch, cm)) return 0;
00105
00106 newlist=realloc(ch->member, sizeof *ch->member * (ch->nr_member+1));
00107 if(!newlist) {
00108 b_log(B_LOG_ERROR, "channel", "could not add member to channel.");
00109 return 0;
00110 }
00111
00112 ch->member=newlist;
00113 ch->member[ch->nr_member++]=cm;
00114 return 1;
00115 }
00116
00120 static int channel_delete_member(struct channel *ch, struct channel_member *cm) {
00121 struct channel_member **d;
00122
00123 assert(ch != NULL);
00124 assert(cm != NULL);
00125
00126 if(!ch) return 0;
00127
00128 d=channel_find_member(ch, cm);
00129 if(!d) return 0;
00130
00131 DEBUG("found channel member %p at %p", cm, d);
00132
00133 assert(ch->nr_member > 0);
00134
00135
00136 *d=NULL;
00137 *d=ch->member[--ch->nr_member];
00138
00139
00140 if(!ch->nr_member) {
00141 free(ch->member);
00142 ch->member=NULL;
00143 }
00144 return 1;
00145 }
00146
00147 static struct channel_public *channel_public_find(const char *name) {
00148 struct channel_public *curr;
00149
00150 for(curr=LIST_TOP(channel_public_list);curr;curr=LIST_NEXT(curr, list)) {
00151
00152
00153
00154 if((name && !strcasecmp(name, curr->name)) || (!name && !curr->name)) {
00155 return curr;
00156 }
00157 }
00158 return NULL;
00159 }
00160
00164 static int channel_public_add(const char *name) {
00165 struct channel_public *newch;
00166
00167 if(channel_public_find(name)) {
00168 return 0;
00169 }
00170
00171 newch=calloc(1, sizeof *newch);
00172 if(!newch) {
00173 perror("calloc()");
00174 b_log(B_LOG_ERROR, "channel", "could not allocate channel.");
00175 return 0;
00176 }
00177
00178 if(name) {
00179 newch->name=strdup(name);
00180 if(!newch->name) {
00181 perror("strdup()");
00182 b_log(B_LOG_ERROR, "channel", "could not allocate channel.");
00183 free(newch);
00184 return 0;
00185 }
00186 } else {
00187 newch->name=NULL;
00188 }
00189
00190 channel_init(&newch->channel);
00191 LIST_INSERT_HEAD(&channel_public_list, newch, list);
00192
00193 return 1;
00194 }
00195
00199 static struct channel *channel_public(const char *name) {
00200 struct channel_public *cp;
00201 cp=channel_public_find(name);
00202 if(cp) {
00203 return &cp->channel;
00204 }
00205 return NULL;
00206 }
00207
00211 static int initialize(void) {
00212 b_log(B_LOG_INFO, "channel", "channel plugin loaded (" __FILE__ " compiled " __TIME__ " " __DATE__ ")");
00213 channel_public_add("Wiz");
00214 channel_public_add("OOC");
00215 channel_public_add(NULL);
00216 service_attach_channel(&plugin_class.base_class, &plugin_class.channel_interface);
00217 return 1;
00218 }
00219
00223 static int shutdown(void) {
00224 b_log(B_LOG_INFO, "channel", "channel plugin shutting down...");
00225 service_detach_channel(&plugin_class.base_class);
00226 b_log(B_LOG_INFO, "channel", "channel plugin ended.");
00227 return 1;
00228 }
00229
00233 static int channel_join(struct channel *ch, struct channel_member *cm) {
00234 b_log(B_LOG_TRACE, "channel", "someone(%p) joined\n", cm?cm->p:NULL);
00235 return channel_add_member(ch, cm);
00236 }
00237
00241 static void channel_part(struct channel *ch, struct channel_member *cm) {
00242 b_log(B_LOG_TRACE, "channel", "someone(%p) parted\n", cm?cm->p:NULL);
00243 if(!channel_delete_member(ch, cm)) {
00244 b_log(B_LOG_WARN, "channel", "could not find channel member %p", cm);
00245 }
00246 }
00247
00251 static int is_on_list(const struct channel_member *cm, struct channel_member **exclude_list, unsigned exclude_list_len) {
00252 unsigned i;
00253 for(i=0;i<exclude_list_len;i++) {
00254 if(cm==exclude_list[i]) return 1;
00255 }
00256 return 0;
00257 }
00258
00262 static int channel_broadcast(struct channel *ch, struct channel_member **exclude_list, unsigned exclude_list_len, const char *fmt, ...) {
00263 va_list ap;
00264 unsigned i;
00265 char buf[CHANNEL_SEND_MAX];
00266
00267 va_start(ap, fmt);
00268 vsnprintf(buf, sizeof buf, fmt, ap);
00269 va_end(ap);
00270
00271 for(i=0;i<ch->nr_member;i++) {
00272 struct channel_member *cm=ch->member[i];
00273 DEBUG("cm=%p p=%p\n", cm, cm?cm->p:NULL);
00274 if(cm && cm->send && !is_on_list(cm, exclude_list, exclude_list_len)) {
00275 cm->send(cm, ch, buf);
00276 }
00277 }
00278 return 0;
00279 }
00280
00281
00282
00283
00284
00288 const struct plugin_channel_class plugin_class = {
00289 .base_class = { PLUGIN_API, "channel", initialize, shutdown },
00290 .channel_interface = {
00291 channel_join, channel_part,
00292 channel_public,
00293 channel_broadcast,
00294 },
00295 };