NovaYoshi

bot.c

Oct 13th, 2013
231
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 45.48 KB | None | 0 0
  1. #include "../main/bot.h"
  2. #include <fnmatch.h>
  3.  
  4. /* global variables of bot state */
  5. NetworkInfo *FirstNetwork = NULL;
  6. EventHook *FirstEvent = NULL;
  7. PluginInfo *FirstPlugin = NULL;
  8. ConfigPair *FirstConfig = NULL;
  9. AsyncEventInfo *FirstAsyncEvent = NULL;
  10. SDLNet_SocketSet SocketSet;
  11. PluginInfo CorePlugin;
  12.  
  13. char *ConfigPath = "bot.ini";
  14. int RecurseLevel = 0;
  15.  
  16. int NeedShutdown = 0;
  17. int RebootAfterShutdown = 0;
  18.  
  19. /* core functions */
  20. void Bot_StrCpy(char *Destination, const char *Source, int MaxLength) {
  21.   // MaxLength is directly from sizeof() so it includes the zero
  22.   int SourceLen = strlen(Source);
  23.   if((SourceLen+1) < MaxLength)
  24.     MaxLength = SourceLen + 1;
  25.   memcpy(Destination, Source, MaxLength-1);
  26.   Destination[MaxLength-1] = 0;
  27. }
  28.  
  29. int Bot_MemCaseCmp(const char *Text1, const char *Text2, int Length) {
  30.   for(;Length;Length--)
  31.     if(tolower(*(Text1++)) != tolower(*(Text2++)))
  32.       return 1;
  33.   return 0;
  34. }
  35.  
  36. void *Bot_GetGlobalPointer(const char *Name) {
  37.   if(!strcasecmp(Name, "FirstNetwork")) return FirstNetwork;
  38.   else if(!strcasecmp(Name, "FirstConfig")) return FirstConfig;
  39.   return NULL;
  40. }
  41.  
  42. int Bot_WildMatch(const char *TestMe, const char *Wild) {
  43.   char NewWild[strlen(Wild)+1];
  44.   char NewTest[strlen(TestMe)+1];
  45.   strcpy(NewTest, TestMe);
  46.   strcpy(NewWild, Wild);
  47.   int i;
  48.   for(i=0;NewWild[i];i++)
  49.     NewWild[i] = tolower(NewWild[i]);
  50.   for(i=0;NewTest[i];i++)
  51.     NewTest[i] = tolower(NewTest[i]);
  52.   return !fnmatch(NewWild, NewTest, FNM_NOESCAPE);
  53. }
  54.  
  55. PluginInfo *Bot_LoadPlugin(const char *Name) {
  56.   for(PluginInfo *P = FirstPlugin;P;P=P->Next) {
  57.     // don't load plugins that are already loaded
  58.     if(!strcasecmp(P->Path, Name))
  59.       return P;
  60.   }
  61.   void *Module;
  62.   int (*Init)(PluginInfo *NewPlugin, char **Name, char **FancyName, char **Version);
  63.   PluginInfo *NewPlugin = (PluginInfo *)malloc(sizeof(PluginInfo));
  64.   Module = dlopen(Name, RTLD_NOW);
  65.   if(Module == NULL) {
  66.     fprintf(stderr, "Can't open plugin \"%s\" - %s\n", Name, dlerror()?:"");
  67.     free(NewPlugin);
  68.     return NULL;
  69.   }
  70.   NewPlugin->Prev = NULL;
  71.   NewPlugin->Next = NULL;
  72.   strcpy(NewPlugin->Path, Name);
  73.   NewPlugin->DL = Module;
  74.   *NewPlugin->Tag = 0;
  75.   NewPlugin->ChannelPluginData_Free = NULL;
  76.   NewPlugin->ChannelPluginData_Alloc = NULL;
  77.  
  78.   Init = dlsym(Module, "Plugin_Init");
  79.   if(!Init) {
  80.     fprintf(stderr, "Can't find Plugin_Init() in plugin \"%s\"- %s\n", Name, dlerror()?:"");
  81.     free(NewPlugin);
  82.     dlclose(Module);
  83.     return NULL;
  84.   }
  85.  
  86.   char *PluginName = NULL, *PluginFancyName = NULL, *PluginVersion = NULL;
  87.   if(!Init(NewPlugin, &PluginName, &PluginFancyName, &PluginVersion)) {
  88.     Bot_UnloadPlugin(NewPlugin);
  89.     dlclose(Module);
  90.     return NULL;
  91.   }
  92.   if(PluginName) Bot_StrCpy(NewPlugin->Name, PluginName, sizeof(NewPlugin->Name));
  93.   if(PluginFancyName) Bot_StrCpy(NewPlugin->FancyName, PluginFancyName, sizeof(NewPlugin->FancyName));
  94.   if(PluginVersion) Bot_StrCpy(NewPlugin->Version, PluginVersion, sizeof(NewPlugin->Version));
  95.  
  96.   if(!FirstPlugin) FirstPlugin = NewPlugin;
  97.   else {
  98.     PluginInfo *Find = FirstPlugin;
  99.     while(Find->Next)
  100.       Find = Find->Next;
  101.     Find->Next = NewPlugin;
  102.   }
  103.   return NewPlugin;
  104. }
  105.  
  106. int Bot_UnloadPlugin(PluginInfo *Plugin) {
  107.   if(!Plugin) return 0;
  108.  
  109.   if(Plugin->Prev) Plugin->Prev->Next = Plugin->Next;
  110.   if(Plugin->Next) Plugin->Next->Prev = Plugin->Prev;
  111.   if(Plugin == FirstPlugin) FirstPlugin = Plugin->Next;
  112.  
  113.   if(Plugin!=&CorePlugin) {
  114.     void (*Deinit)() = dlsym(Plugin->DL, "Plugin_Deinit");
  115.     if(Deinit) Deinit();
  116.   }
  117.  
  118.   for(int i=0;i<NUM_TIMERS;i++) {
  119.     TimerInfo *Timer = Plugin->Timers[i];
  120.     if(Timer) {
  121.       if((Timer->Flags & TIMER_RUN_ON_UNLOAD) && Bot_ContextIsValid(&Timer->Context))
  122.         Bot_StartEvent(Plugin, Timer->EventType, &Timer->Context, Timer->Flags, "%s early=1", Timer->EventText);
  123.       free(Timer);
  124.     }
  125.   }
  126.  
  127.   for(EventHook *FreeHook = FirstEvent;FreeHook;) {
  128.     EventHook *Next = FreeHook->NextType;
  129.     for(EventHook *FreeHook2 = FreeHook;FreeHook2;) {
  130.       EventHook *Next2 = FreeHook2->Next;
  131.       if(FreeHook2->Plugin == Plugin) Bot_DelEventHook(FreeHook2);
  132.       FreeHook2=Next2;
  133.     }
  134.     FreeHook = Next;
  135.   }
  136.  
  137.   if(Plugin!=&CorePlugin) {
  138.     dlclose(Plugin->DL);
  139.     free(Plugin);
  140.   }
  141.   return 1;
  142. }
  143.  
  144. PluginInfo *Bot_FindPlugin(char *Name) {
  145.   PluginInfo *Cur = FirstPlugin;
  146.   while(Cur) {
  147.     if(!strcasecmp(Name, Cur->Name))
  148.     Cur=Cur->Next;
  149.   }
  150.   return NULL;
  151. }
  152.  
  153. int Bot_PluginIsPresent(char *Name) {
  154.   return Bot_FindPlugin(Name)!=NULL;
  155. }
  156.  
  157. void *Bot_PluginSymbol(char *Module, char *Name) {
  158.   PluginInfo *Plugin = Bot_FindPlugin(Module);
  159.   if(!Plugin) return NULL;
  160.   return dlsym(Plugin->DL, Name);
  161. }
  162.  
  163. int Bot_PluginSetHandler(PluginInfo *Plugin, const char *Name, void *Handler) {
  164.   if(!strcasecmp(Name, "CPD Alloc")) {
  165.     Plugin->ChannelPluginData_Alloc = Handler;
  166.     return 1;
  167.   } else if(!strcasecmp(Name, "CPD Free")) {
  168.     Plugin->ChannelPluginData_Free = Handler;
  169.     return 1;
  170.   }
  171.   return 0;
  172. }
  173.  
  174. void *Bot_ChannelPluginData(PluginInfo *Plugin, ChannelInfo *Channel, unsigned int Flags) {
  175.   unsigned char Type = Flags & 255;
  176.   int AutoMake = Flags & CPD_AUTO_CREATE;
  177.   void *(*Alloc)(void *, unsigned char) = Plugin->ChannelPluginData_Alloc;
  178.   void *New;
  179.   ChannelPluginData *CPD = Channel->PluginData;
  180.   if(!CPD) {
  181.     if(!AutoMake) return NULL;
  182.     CPD = (ChannelPluginData*)malloc(sizeof(ChannelPluginData));
  183.     CPD->Type = Type;
  184.     CPD->Plugin = Plugin;
  185.     CPD->Next = NULL;
  186.     New = Alloc((void*)Channel, Type);
  187.     if(!New) { free(CPD); return NULL;}
  188.     CPD->Data = New;
  189.     Channel->PluginData=CPD;
  190.     return New;
  191.   } else while(1) {
  192.     if(CPD->Plugin == Plugin && CPD->Type == Type)
  193.       return CPD->Data;
  194.     if(CPD->Next) // keep looking
  195.       CPD=CPD->Next;
  196.     else { // end of the list
  197.       if(AutoMake) {
  198.         ChannelPluginData *NewCPD = (ChannelPluginData*)malloc(sizeof(ChannelPluginData));
  199.         NewCPD->Type = Type;
  200.         NewCPD->Plugin = Plugin;
  201.         NewCPD->Next = NULL;
  202.         New = Alloc((void*)Channel, Type);
  203.         if(!New) { free(NewCPD); return NULL;}
  204.         NewCPD->Data = New;
  205.         CPD->Next=NewCPD;
  206.         return New;
  207.       }
  208.       break;
  209.     }
  210.   }
  211.   return NULL;
  212. }
  213.  
  214. void Bot_FreeChannel(ChannelInfo *Channel) {
  215.   ChannelPluginData *CPD = Channel->PluginData;
  216.   while(CPD) {
  217.     ChannelPluginData *Next = CPD->Next;
  218.     PluginInfo *Plugin = CPD->Plugin;
  219.     if(!Plugin->ChannelPluginData_Free)
  220.       free(CPD->Data);
  221.     else
  222.       Plugin->ChannelPluginData_Free(CPD->Data, CPD->Type);
  223.     free(CPD);
  224.     CPD = Next;
  225.   }
  226. }
  227.  
  228. void Bot_FreeNetwork(NetworkInfo *Network) {
  229.   ChannelInfo *Chan = (ChannelInfo*)Network->FirstChannel;
  230.   while(Chan) {
  231.     ChannelInfo *Next = Chan->Next;
  232.     Bot_FreeChannel(Chan);
  233.     free(Chan);
  234.     Chan = Next;
  235.   }
  236. }
  237.  
  238. NetworkInfo *Bot_ConnectNetwork(const char *NetTag) {
  239.   for(NetworkInfo *Net = FirstNetwork;Net;Net=Net->Next)
  240.     if(!strcasecmp(Net->Tag, NetTag))
  241.       return Net;
  242.   char GroupName[64];
  243.   sprintf(GroupName, "Network %s%%Network Default", NetTag);
  244.  
  245.   const char *Nick = Bot_GetConfigStr(GroupName, "Nick", "ToasterBot");
  246.   const char *User = Bot_GetConfigStr(GroupName, "Username", "toaster");
  247.   const char *Realname = Bot_GetConfigStr(GroupName, "Realname", "Test");
  248.   const char *Server = Bot_GetConfigStr(GroupName, "Server", "irc.novasquirrel.com");
  249.   const char *PortStr = Bot_GetConfigStr(GroupName, "Port", "6667");
  250.   int Port = strtol(PortStr, NULL, 10);
  251.  
  252.   IPaddress ip;
  253.   TCPsocket tcpsock;
  254.  
  255.   if(SDLNet_ResolveHost(&ip,Server,Port) < 0) {
  256.     printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
  257.     return NULL;
  258.   }
  259.  
  260.   tcpsock=SDLNet_TCP_Open(&ip);
  261.   if(!tcpsock) {
  262.     printf("SDLNet_TCP_Open: %s\n", SDLNet_GetError());
  263.     return NULL;
  264.   }
  265.  
  266.   char ConnectMsg[512], PassMsg[128]="";
  267.   const char *ConnectPass = Bot_GetConfigStr(GroupName, "ConnectPass", NULL);
  268.   if(ConnectPass)
  269.     sprintf(PassMsg, "PASS %s\r\n", ConnectPass);
  270.   sprintf(ConnectMsg, "%sNICK %s\r\nUSER %s 8 * :%s\r\n", PassMsg, Nick, User, Realname);
  271.   int Len = strlen(ConnectMsg);
  272.   if(SDLNet_TCP_Send(tcpsock,ConnectMsg,Len)<Len) {
  273.     printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError());
  274.   }
  275.  
  276.   if(SDLNet_TCP_AddSocket(SocketSet,tcpsock)==-1) {
  277.     SDLNet_TCP_Close(tcpsock);
  278.     printf("SDLNet_AddSocket: %s\n", SDLNet_GetError());
  279.     return NULL;
  280.   }
  281.  
  282.   // add network to list
  283.   NetworkInfo *NewNet = (NetworkInfo*)malloc(sizeof(NetworkInfo));
  284.   if(!NewNet) {
  285.     SDLNet_TCP_Close(tcpsock);
  286.     return NULL;
  287.   }
  288.  
  289.   *NewNet->Name = 0;
  290.   Bot_StrCpy(NewNet->Nick, Nick, sizeof(NewNet->Nick));
  291.   Bot_StrCpy(NewNet->Username, User, sizeof(NewNet->Username));
  292.   Bot_StrCpy(NewNet->ConnectURL, Server, sizeof(NewNet->ConnectURL));
  293.   Bot_StrCpy(NewNet->Tag, NetTag, sizeof(NewNet->Tag));
  294.   *NewNet->NickPass = 0;
  295.   *NewNet->ServerName = 0;
  296.   *NewNet->ConnectPass = 0;
  297.   *NewNet->FullHost = 0;
  298.   NewNet->IP = ip;
  299.   NewNet->Port = Port;
  300.   NewNet->Socket = tcpsock;
  301.   NewNet->FirstChannel = NULL;
  302.   NewNet->Prev = NULL;
  303.   NewNet->Next = NULL;
  304.  
  305.   NetworkInfo *FindNet = FirstNetwork;
  306.   if(!FindNet) {
  307.     FirstNetwork = NewNet;
  308.   } else {
  309.     while(FindNet->Next)
  310.       FindNet = FindNet->Next;
  311.     NewNet->Prev = FindNet;
  312.     FindNet->Next = NewNet;
  313.   }
  314.   return NewNet;
  315. }
  316.  
  317. void Bot_DisconnectNetwork(NetworkInfo *Network, const char *QuitMessage) {
  318.   if(!Network) return;
  319.   ContextInfo NetContext;
  320.   Bot_MakeContext(&NetContext, CONTEXT_NETWORK, Network);
  321.   if(QuitMessage)
  322.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "QUIT :%s", QuitMessage);
  323.  
  324.   Bot_FreeNetwork(Network);
  325.   SDLNet_TCP_Close(Network->Socket);
  326.   if(FirstNetwork == Network) FirstNetwork = Network->Next;
  327.   if(Network->Prev) Network->Prev->Next = Network->Next;
  328.   if(Network->Next) Network->Next->Prev = Network->Prev;
  329.   free(Network);
  330. }
  331.  
  332. int Bot_FindEventParam(BotEvent *Event, const char *Param) {
  333.   char LookFor[512];
  334.   // allow equals in param name, but ignore value given
  335.   if(!strchr(Param, '='))
  336.     sprintf(LookFor, "%s=", Param);
  337.   else {
  338.     strcpy(LookFor, Param);
  339.     char *Temp = strchr(LookFor, '=');
  340.     Temp[1] = 0;
  341.   }
  342.   int i;
  343.   for(i=0;i<Event->WordLen;i++) {
  344.     const char *A = LookFor;
  345.     const char *B = Event->Word[i];
  346.     int Okay = 1;
  347.     for(;*A;A++,B++)
  348.       if(tolower(*A)!=tolower(*B)) {
  349.         Okay = 0;
  350.         break;
  351.       }
  352.     if(Okay)
  353.       return i;
  354.   }
  355.   return -1;
  356. }
  357.  
  358. NetworkInfo *Bot_FindNetwork(const char *Name) {
  359.   if(Name == NULL) return NULL;
  360.   NetworkInfo *Cur;
  361.   for(Cur = FirstNetwork;Cur;Cur=Cur->Next)
  362.     if(!strcasecmp(Name, Cur->Tag))
  363.       return Cur;
  364.   return NULL;
  365. }
  366.  
  367. ChannelInfo *Bot_FindChannel(NetworkInfo *Network, const char *Name) {
  368.   if(!Network || !Name) return NULL;
  369.   for(ChannelInfo *Cur = Network->FirstChannel;Cur;Cur=Cur->Next)
  370.     if(!strcasecmp(Name, Cur->Name))
  371.       return Cur;
  372.   return NULL;
  373. }
  374.  
  375. int Bot_GetEventInt(BotEvent *Event, const char *Param, int Default) {    
  376.   int i = Bot_FindEventParam(Event, Param);
  377.   if(i == -1) return Default;
  378.   char *Value = strchr(Event->Word[i], '=');
  379.   if(!Value) return Default; // shouldn't be possible
  380.   return strtol(Value+1, NULL, 0);
  381. }
  382.  
  383. const char *Bot_GetEventStr(BotEvent *Event, const char *Param, const char *Default) {
  384.   int i = Bot_FindEventParam(Event, Param);
  385.   if(i == -1) return Default;
  386.   char *Value = strchr(Event->Word[i], '=');
  387.   if(!Value) return Default; // shouldn't be possible
  388.   return Value+1;
  389. }
  390.  
  391. void Bot_TokenizeEvent(BotEvent *Event) {
  392.   if(!Event || Event->Flags & EF_NO_TOKENIZATION) return;
  393.  
  394.   *Event->TextWord = 0;
  395.   strcpy(Event->TextWord, Event->Text);
  396.   char *Peek = Event->TextWord;
  397.   Event->WordLen = 0;
  398.   Event->WordEol[0] = Event->Text;
  399.   Event->Word[0] = Event->TextWord;
  400.  
  401.   int i;
  402.   for(i=0;i<BOTEVENT_WORDSIZE;i++) {
  403.     // init with "" for crash protection
  404.     Event->Word[i] = "";
  405.     Event->WordEol[i] = "";
  406.   }
  407.  
  408.   while(1) {
  409.     char *Next;
  410.  
  411.     if(*Peek != '\t' || (Event->Flags & EF_NO_MULTIWORD))
  412.       Next = strchr(Peek+1, ' ');
  413.     else // multi word
  414.       Next = strchr(Peek+1, '\t');
  415.     while(*Peek == '\t') Peek++;
  416.     Event->Word[Event->WordLen++] = Peek;
  417.     if(!(Event->Flags & EF_KEEP_COLONS) && Event->Word[Event->WordLen-1][0] == ':')
  418.       Event->Word[Event->WordLen-1]++;
  419.  
  420.     if(Event->WordLen >= BOTEVENT_WORDSIZE-1)
  421.       break;
  422.  
  423.     if(!Next) break;
  424.     *Next = 0;
  425.  
  426.     Peek = Next+1;
  427.     while(*Peek == ' ') Peek++;
  428.   }
  429.   if(!Event->Word[Event->WordLen-1][0])
  430.     Event->WordLen--;
  431.  
  432.   for(i=0;i<Event->WordLen;i++) {
  433.     Event->WordEol[i] = Event->Text + (Event->Word[i] - Event->TextWord);
  434.   }
  435. }
  436.  
  437. int Bot_ChangeParams(BotEvent *NewEvent, BotEvent *Event, const char *NewParams) {
  438.   BotEvent TempEvent;
  439.   Bot_ZeroStruct(TempEvent);
  440.  
  441.   strcpy(TempEvent.Text, NewParams);
  442.   Bot_TokenizeEvent(&TempEvent);
  443.   char New[BOTEVENT_TEXTSIZE]=""; int i;
  444.   for(i=0;i<Event->WordLen;i++)
  445.     // add to new list if not in the change-to list
  446.     if(-1 == Bot_FindEventParam(&TempEvent, Event->Word[i]))
  447.       sprintf(strrchr(New, 0), "%s\t%s\t", ((*New)?" ":""), Event->Word[i]);
  448.   for(i=0;i<TempEvent.WordLen;i++)
  449.     sprintf(strrchr(New, 0), "%s\t%s\t", ((*New)?" ":""), TempEvent.Word[i]);
  450.   strcpy(NewEvent->Text, New);
  451.   Bot_TokenizeEvent(NewEvent);
  452.   return 0;
  453. }
  454.  
  455. void Bot_XChatTokenize(const char *Input, char *WordBuff, int WordBuffSize, const char **Word, const char **WordEol, int WordSize) {
  456.   int i;
  457.   for(i=0;i<WordSize;i++) {
  458.     Word[i] = "";
  459.     WordEol[i] = "";
  460.   }
  461.   strcpy(WordBuff, Input);
  462.  
  463.   for(i=0;WordBuff[i];i++) // filter tabs because I use them
  464.     if(WordBuff[i]=='\t')
  465.       WordBuff[i] = ' ';
  466.  
  467.   i = 0;
  468.   char *Peek = WordBuff;
  469.   while(1) {
  470.     char *Next;
  471.     Next = strchr(Peek+1, ' ');
  472.  
  473.     while(*Peek == '\t') Peek++;
  474.     Word[i] = Peek;
  475.  
  476.     if(i >= (WordSize-1))
  477.       break;
  478.  
  479.     if(!Next) break;
  480.     *Next = 0;
  481.  
  482.     Peek = Next+1;
  483.     while(*Peek == ' ') Peek++;
  484.   }
  485.  
  486.   for(int j=0;j<i;j++) {
  487.     WordEol[j] = Input + (Word[i] - WordBuff);
  488.   }
  489. }
  490.  
  491. EventHook *Bot_AddEventHook(PluginInfo *Plugin, const char *Type, int Priority, int Flags, int Need0, int Need1, void *Handler) {
  492.   EventHook *Find = FirstEvent, *Alloc;
  493.   EventHook NewHook;
  494.   Bot_ZeroStruct(NewHook); // makes all pointers NULL
  495.   strcpy(NewHook.Type, Type);
  496.   NewHook.Priority = Priority;
  497.   NewHook.Flags = Flags;
  498.   NewHook.EventFlagsNeed0 = Need0;
  499.   NewHook.EventFlagsNeed1 = Need1;
  500.   NewHook.Handler = Handler;
  501.   NewHook.Prev = NULL;
  502.   NewHook.PrevType = NULL;
  503.   NewHook.Next = NULL;
  504.   NewHook.NextType = NULL;
  505.   NewHook.Plugin = Plugin;
  506.  
  507.   if(!Find) { // create first node
  508.     FirstEvent = (EventHook *)malloc(sizeof(EventHook));
  509.     if(!FirstEvent) return NULL;
  510.     *FirstEvent = NewHook;
  511.     return FirstEvent;
  512.   }
  513.   for(;Find;Find=Find->NextType) {
  514.     if(!strcasecmp(Find->Type, NewHook.Type)) {
  515.       // found hook type in list
  516.       if(NewHook.Priority > Find->Priority) {
  517.         // replace in types list
  518.         Alloc = (EventHook *)malloc(sizeof(EventHook));
  519.         if(!Alloc) return NULL;
  520.         *Alloc = NewHook;
  521.         Alloc->PrevType = Find->PrevType;
  522.         if(Alloc->PrevType) Alloc->PrevType->NextType = Alloc;
  523.         Alloc->NextType = Find->NextType;
  524.         if(Alloc->NextType) Alloc->NextType->PrevType = Alloc;
  525.         Alloc->Next = Find;
  526.         Find->Prev = Alloc;
  527.       } else {
  528.         while(Find) { // add before item
  529.           if(NewHook.Priority > Find->Priority) {
  530.             Alloc = (EventHook *)malloc(sizeof(EventHook));
  531.             if(!Alloc) return NULL;
  532.             *Alloc = NewHook;
  533.             Alloc->Prev = Find->Prev;
  534.             Alloc->Next = Find;
  535.             Find->Prev = Alloc;
  536.             if(Alloc->Prev) Alloc->Prev->Next = Alloc;
  537.             return Alloc;
  538.           }
  539.           if(!Find->Next) { // add onto end
  540.             Alloc = (EventHook *)malloc(sizeof(EventHook));
  541.             if(!Alloc) return NULL;
  542.             *Alloc = NewHook;
  543.             Find->Next = Alloc;
  544.             Alloc->Prev = Find;
  545.             return Alloc;
  546.           }
  547.           Find = Find->Next;
  548.         }
  549.         return NULL;
  550.       }
  551.     } else if(Find->NextType == NULL) {
  552.       // add onto the end of the hook types
  553.       Alloc = (EventHook *)malloc(sizeof(EventHook));
  554.       if(!Alloc) return NULL;
  555.       *Alloc = NewHook;
  556.       Find->NextType = Alloc;
  557.       Alloc->PrevType = Find;
  558.       return Alloc;
  559.     }
  560.   }
  561.  
  562.   return NULL;
  563. }
  564.  
  565. int Bot_DelEventHook(EventHook *Hook) {
  566.   if(!Hook) return 0;
  567.   EventHook *Find = FirstEvent;
  568.   if(FirstEvent == Hook) {
  569.     if(Hook->Next) {
  570.       FirstEvent = Hook->Next;
  571.       FirstEvent->NextType = Hook->NextType;
  572.       if(FirstEvent->NextType) FirstEvent->NextType->PrevType = FirstEvent;
  573.       FirstEvent->Prev = NULL;
  574.     } else {
  575.       FirstEvent = Hook->NextType;
  576.     }
  577.     free(Hook);
  578.     return 1;
  579.   }
  580.  
  581.   for(;Find;Find=Find->NextType)
  582.     if(!strcasecmp(Find->Type, Hook->Type)) {
  583.       for(;Find;Find=Find->Next)
  584.         if(Find == Hook) {
  585.           if(!Find->Prev) { // first entry in list?
  586.             if(!Find->Next) { // delete event type altogether?
  587.               if(Find->PrevType) Find->PrevType->NextType = Find->NextType;
  588.               if(Find->NextType) Find->NextType->PrevType = Find->PrevType;
  589.             } else { // only delete first entry, but keep second one
  590.               if(Find->PrevType) Find->PrevType->NextType = Find->Next;
  591.               if(Find->NextType) Find->NextType->PrevType = Find->Next;
  592.               Find->Next->PrevType = Find->PrevType;
  593.               Find->Next->NextType = Find->NextType;
  594.               Find->Next->Prev = NULL;
  595.             }
  596.             free(Find);
  597.             return 1;
  598.           }
  599.           if(Find->Prev) Find->Prev->Next = Find->Next;
  600.           if(Find->Next) Find->Next->Prev = Find->Prev;
  601.           free(Find);
  602.           return 1;
  603.         }
  604.       return 0;
  605.     }
  606.   return 0;
  607. }
  608.  
  609. void Bot_FreeAsyncEvents(int Join) {
  610. // todo: write
  611.   AsyncEventInfo *Info = FirstAsyncEvent;
  612.   while(Info) {
  613.     AsyncEventInfo *Next = Info->Next;
  614.     if(Join)
  615.       pthread_join(Info->Thread,NULL);
  616.     if(Info->Finished) {
  617.       if(Info == FirstAsyncEvent)
  618.         FirstAsyncEvent = Info->Next;
  619.       if(Info->Prev) Info->Prev->Next = Info->Next;
  620.       if(Info->Next) Info->Next->Prev = Info->Prev;
  621.       free(Info);
  622.     }
  623.     Info = Next;
  624.   }
  625. }
  626.  
  627. void *Bot_AsyncRunEvent(void *Info2) {
  628. //  puts("Running async event");
  629.   AsyncEventInfo *Info = (AsyncEventInfo*)Info2;
  630.   BotEvent *Event = &Info->Event;
  631.   if(!Event) return NULL;
  632.   EventHook *Hook = FirstEvent;
  633.   for(;Hook;Hook=Hook->NextType)
  634.     if(!strcasecmp(Hook->Type, Event->Type)) {
  635.       for(;Hook;Hook=Hook->Next) {
  636.         int Return = Hook->Handler(Event);
  637.         switch(Return) {
  638.           case ER_NORMAL:
  639.             break;
  640.           case ER_HANDLED:
  641.             Event->Flags |= EF_ALREADY_HANDLED;
  642.             break;
  643.           case ER_DELETE:
  644.             Info->Finished = 1;
  645.             pthread_exit(NULL);
  646.             return NULL;
  647.         }
  648.       }
  649.       Info->Finished = 1;
  650.       pthread_exit(NULL);
  651.       return NULL;
  652.     }
  653.   Info->Finished = 1;
  654.   pthread_exit(NULL);
  655.   return NULL;
  656. }
  657.  
  658. int Bot_RunEvent(BotEvent *Event) {
  659.   if(!Event) return -1;
  660.   if(Event->Flags & EF_ASYNCHRONOUS) {
  661.     AsyncEventInfo Info;
  662.     Bot_ZeroStruct(Info);
  663.     Info.Event = *Event;
  664.     Info.Finished = 0;
  665.     Info.Prev = NULL;
  666.     Info.Next = NULL;
  667.     AsyncEventInfo *Find = FirstAsyncEvent;
  668.     if(!Find) {
  669.       Find = (AsyncEventInfo *)malloc(sizeof(AsyncEventInfo));
  670.       FirstAsyncEvent = Find;
  671.       *Find = Info;
  672.     } else {
  673.       while(Find->Next)
  674.         Find=Find->Next;
  675.       Find->Next = (AsyncEventInfo *)malloc(sizeof(AsyncEventInfo));
  676.       *Find->Next = Info;
  677.       Find->Next->Prev = Find;
  678.     }
  679.     if(pthread_create(&Find->Thread, NULL, Bot_AsyncRunEvent, Find)) {
  680.       fprintf(stderr, "Thread creation failed\n");
  681.       return 0;
  682.     }
  683.     return 1;
  684.   }
  685.   EventHook *Hook = FirstEvent;
  686.   for(;Hook;Hook=Hook->NextType)
  687.     if(!strcasecmp(Hook->Type, Event->Type)) {
  688.       for(;Hook;Hook=Hook->Next) {
  689.         if((Event->Flags & Hook->EventFlagsNeed1) != Hook->EventFlagsNeed1)
  690.           continue;
  691.         if(Event->Flags & Hook->EventFlagsNeed0)
  692.           continue;
  693.         int Return = Hook->Handler(Event);
  694.         switch(Return) {
  695.           case ER_NORMAL:
  696.             break;
  697.           case ER_HANDLED:
  698.             Event->Flags |= EF_ALREADY_HANDLED;
  699.             break;
  700.           case ER_DELETE:
  701.             return 1;
  702.         }
  703.       }
  704.       return 1;
  705.     }
  706.   return 0;
  707. }
  708.  
  709. int Bot_IndexInStrList(const char *Search, const char* List, ...) {
  710.   va_list Args;
  711.   va_start(Args, List);
  712.   for(int i=0;; i++) {
  713.     const char *A = va_arg(Args, const char*);
  714.     if(!strcasecmp(Search, A)) {
  715.       va_end(Args);
  716.       return i;
  717.     }
  718.   }
  719.   va_end(Args);
  720.   return -1;
  721. }
  722.  
  723. int Bot_StartEvent(PluginInfo *Plugin, const char *Type, ContextInfo *Context, unsigned long Flags, const char* Format, ...) {
  724.   BotEvent Event;
  725.   Bot_ZeroStruct(Event);
  726.   va_list Args;
  727.   va_start(Args, Format);
  728.   vsprintf(Event.Text, Format, Args);
  729.  
  730.   va_end(Args);
  731.   Event.Plugin = Plugin;
  732.   Event.Flags = Flags;
  733.   Event.Context = Context;
  734.   strcpy(Event.Type, Type);
  735.   Bot_TokenizeEvent(&Event);
  736.   RecurseLevel++;
  737.   int Return;
  738.   Return = Bot_RunEvent(&Event);
  739.   RecurseLevel--;
  740.   return Return;
  741. }
  742.  
  743. int Bot_StartDelayedEvent(PluginInfo *Plugin, const char *Type, ContextInfo *Context, unsigned long Flags, unsigned long Seconds, BotEvent *Base, const char* Format, ...) {
  744.   TimerInfo Timer;
  745.   Bot_ZeroStruct(Timer);
  746.   Timer.Plugin = Plugin;
  747.   Timer.Seconds = Seconds;
  748.   Timer.EventFlags = Flags;
  749.   Timer.Context = *Context;
  750.   time(&Timer.StartTime);
  751.   Timer.EndTime = Timer.StartTime + Seconds;
  752.  
  753.   Bot_StrCpy(Timer.EventType, Type, sizeof(Timer.EventType));
  754.  
  755.   va_list Args;
  756.   va_start(Args, Format);
  757.   vsprintf(Timer.EventText, Format, Args);
  758.   va_end(Args);
  759.  
  760.   if(Base) {
  761.     BotEvent TempEvent = *Base;
  762.     Bot_ChangeParams(&TempEvent, &TempEvent, Timer.EventText);
  763.     Bot_StrCpy(Timer.EventText, TempEvent.Text, sizeof(Timer.EventText));
  764.   }
  765.  
  766.   for(int i=0;i<NUM_TIMERS;i++)
  767.     if(!Plugin->Timers[i]) {
  768.       TimerInfo *Alloc = (TimerInfo*)malloc(sizeof(TimerInfo));
  769.       if(!Alloc) return -1;
  770.       *Alloc = Timer;
  771.       Plugin->Timers[i] = Alloc;
  772.       return i;
  773.     }
  774.   return -1;
  775. }
  776.  
  777. int Bot_ParamCopyEvent(PluginInfo *Plugin, const char *Type, ContextInfo *Context, unsigned long Flags, BotEvent *BaseEvent, const char* Format, ...) {
  778.   BotEvent Event;
  779.   Bot_ZeroStruct(Event);
  780.   Event.Flags = 0;
  781.   *Event.Text = 0;
  782.   va_list Args;
  783.   char NewArg[BOTEVENT_TEXTSIZE]="";
  784.   va_start(Args, Format);
  785.   vsprintf(NewArg, Format, Args);
  786.   va_end(Args);
  787.  
  788.   strcpy(Event.Text, BaseEvent->Text);
  789.   Bot_ChangeParams(&Event, BaseEvent, NewArg);
  790.  
  791.   Event.Plugin = Plugin;
  792.   Event.Flags = Flags;
  793.   Event.Context = Context;
  794.   strcpy(Event.Type, Type);
  795.   RecurseLevel++;
  796.   int Return;
  797.   Return = Bot_RunEvent(&Event);
  798.   RecurseLevel--;
  799.   return Return;
  800. }
  801.  
  802. int Bot_ParseINI(FILE *File, void (*Handler)(const char *Group, const char *Item, const char *Value)) {
  803.   char Group[512]="", *Item, *Value, Line[512]="", c, *Poke = NULL;
  804.   if(File == NULL)
  805.     return 0;
  806.   int i;
  807.   while(!feof(File)) {
  808.     for(i=0,c=1;;i++) {
  809.       c = fgetc(File);
  810.       if(c=='\r'||c=='\n') {
  811.         Line[i]=0;
  812.         break;
  813.       }
  814.       Line[i] = c;
  815.     }
  816.     while(c=='\r'||c=='\n')
  817.       c = fgetc(File);
  818.     fseek(File, -1 , SEEK_CUR);
  819.     if(!*Line)
  820.       break;
  821.     else if(*Line == ';'); // comment
  822.     else if(*Line == '[') { // group
  823.       Poke = strchr(Line, ']');
  824.       if(Poke) *Poke = 0;
  825.       strcpy(Group, Line+1);
  826.     } else { // item
  827.       Poke = strchr(Line, '=');
  828.       if(Poke) {
  829.         *Poke = 0;
  830.         Item = Line;
  831.         Value = Poke+1;
  832.         Handler(Group, Item, Value);
  833.       }
  834.     }
  835.   }
  836.   fclose(File);
  837.   return 1;
  838. }
  839.  
  840. ConfigPair *Bot_FindConfigPair(ConfigPair *Find, const char *Name) {
  841.   for(;Find;Find=Find->Next)
  842.     if(Find->Name && Name && !strcasecmp(Find->Name, Name))
  843.       return Find;
  844.   return NULL;
  845. }
  846.  
  847. const char *Bot_GetConfigStr(const char *GroupName, const char *ItemName, const char *DefaultValue) {
  848.   char GroupName2[strlen(GroupName)+1];
  849.   char *AltGroup = NULL;
  850.   if(strchr(GroupName, '%')) {
  851.     strcpy(GroupName2, GroupName);
  852.     AltGroup = strchr(GroupName2, '%');
  853.     *(AltGroup++) = 0;
  854.     GroupName = GroupName2;
  855.   }
  856.  
  857.   ConfigPair *Group = Bot_FindConfigPair(FirstConfig, GroupName);
  858.   if(!Group) {
  859.     if(!AltGroup) return DefaultValue;
  860.     return Bot_GetConfigStr(AltGroup, ItemName, DefaultValue);
  861.   }
  862.  
  863.   ConfigPair *Item = Bot_FindConfigPair(Group->Item, ItemName);
  864.   if(!Item) {
  865.     if(!AltGroup) return DefaultValue;
  866.     return Bot_GetConfigStr(AltGroup, ItemName, DefaultValue);
  867.   }
  868.   return Item->Value;
  869. }
  870.  
  871. int Bot_GetConfigInt(const char *GroupName, const char *ItemName, int DefaultValue) {
  872.   const char *Get = Bot_GetConfigStr(GroupName, ItemName, NULL);
  873.   if(Get == NULL) return DefaultValue;
  874.   return strtol(Get, NULL, 0);
  875. }
  876.  
  877. void Bot_AddConfigPair(const char *GroupName, const char *ItemName, const char *Value) {
  878.   ConfigPair NewPair, *FindEnd;
  879.   Bot_StrCpy(NewPair.Name, ItemName, sizeof(NewPair.Name));
  880.   NewPair.Value = NULL;
  881.   NewPair.Next = NULL;
  882.   NewPair.Item = NULL;
  883.   // find group, and create it if it doesn't already exist
  884.   ConfigPair *Group = Bot_FindConfigPair(FirstConfig, GroupName);
  885.   if(!Group) {
  886.     Group = (ConfigPair *)malloc(sizeof(ConfigPair));
  887.     if(!Group) return;
  888.     if(!FirstConfig)
  889.       FirstConfig = Group;
  890.     else {
  891.       for(FindEnd = FirstConfig; FindEnd->Next; FindEnd=FindEnd->Next);
  892.       FindEnd->Next = Group;
  893.     }
  894.     Bot_StrCpy(Group->Name, GroupName, sizeof(Group->Name));
  895.     Group->Value = NULL;
  896.     Group->Next = NULL;
  897.     Group->Item = NULL;
  898.   }
  899.  
  900.   // find item, and create it if it doesn't already exist
  901.   ConfigPair *Alloc = Bot_FindConfigPair(Group, ItemName);
  902.   if(!Alloc) {
  903.     Alloc = (ConfigPair *)malloc(sizeof(ConfigPair));
  904.     if(!Alloc) return;
  905.     if(!Group->Item) {
  906.       Group->Item = Alloc;
  907.     } else {
  908.       for(FindEnd = Group->Item; FindEnd->Next; FindEnd=FindEnd->Next);
  909.       FindEnd->Next = Alloc;
  910.     }
  911.     Bot_StrCpy(Alloc->Name, ItemName, sizeof(Alloc->Name));
  912.     Alloc->Next = NULL;
  913.     Alloc->Item = NULL;
  914.     Alloc->Value = NULL;
  915.   }
  916.   if(Alloc->Value) free(Alloc->Value);
  917.   Alloc->Value = (char *)malloc(strlen(Value)+1);
  918.   strcpy(Alloc->Value, Value);
  919. }
  920.  
  921. void Bot_FreeConfig(ConfigPair *Free) {
  922.   while(Free) {
  923.     ConfigPair *Next = Free->Next;
  924.     if(Free->Item) Bot_FreeConfig(Free->Item);
  925.     if(Free->Value) free(Free->Value);
  926.     free(Free);
  927.     Free = Next;
  928.   }
  929. }
  930.  
  931. void Bot_ReloadConfig(const char *Filename) {
  932.   Bot_StartEvent(&CorePlugin, "Config Reload", NULL, 0, "%s", Filename);
  933.   Bot_FreeConfig(FirstConfig);
  934.   FirstConfig = NULL;
  935.   Bot_ParseINI(fopen(Filename, "rb"), Bot_AddConfigPair);
  936.  
  937.   ConfigPair *Pair = Bot_FindConfigPair(FirstConfig, "Modules");
  938.   if(Pair) {
  939.     Pair = Pair->Item;
  940.     while(Pair) {
  941.       PluginInfo *P = Bot_LoadPlugin(Pair->Value);
  942.       if(P) strcpy(P->Tag, Pair->Name);
  943.       Pair=Pair->Next;
  944.     }
  945.   } else {
  946.     fprintf(stderr, "No [Modules] group found\n");
  947.   }
  948.  
  949.   ConfigPair *Networks = FirstConfig;
  950.   while(Networks) {
  951.     if(!Bot_MemCaseCmp(Networks->Name, "Network ", 8)) {
  952.       if(Bot_GetConfigInt(Networks->Name, "Autoconnect", 0))
  953.         Bot_ConnectNetwork(Networks->Name+8);
  954.     }
  955.     Networks = Networks->Next;
  956.   }
  957. }
  958.  
  959. int Bot_SplitUserHost(const char *FullHost, char *Buffer, char **User, char **Host) {
  960.   *User = NULL;
  961.   *Host = NULL;
  962.   strcpy(Buffer, FullHost);
  963.   if(strchr(Buffer, '!') && strchr(Buffer, '@')) {
  964.     *Host = strchr(Buffer, '@') + 1;
  965.     Host[0][-1] = 0;
  966.     *User = strchr(Buffer, '!') + 1;
  967.     User[0][-1] = 0;
  968.     return 1;
  969.   }
  970.   return 0;
  971. }
  972.  
  973. const char *Bot_GetInfo(ContextInfo *Context, const char *Info) {
  974. // http://xchat.org/docs/plugin20.html#xchat_get_info
  975.   if(!Context) return NULL;
  976.   NetworkInfo *Network = Context->Target.Network;
  977.   if(Context->Type == CONTEXT_CHANNEL)
  978.     Network = Context->Target.Channel->Network;
  979.   if(!strcasecmp(Info, "Network")) return Network->Name;
  980.   if(!strcasecmp(Info, "Server")) return Network->ServerName;
  981.   if(!strcasecmp(Info, "Host")) return Network->ConnectURL;
  982.   if(!strcasecmp(Info, "NetTag")) return Network->Tag;
  983.   if(!strcasecmp(Info, "Nick")) return Network->Nick;
  984.   if(!strcasecmp(Info, "Username")) return Network->Username;
  985.   if(!strcasecmp(Info, "NickServ")) return Network->NickPass;
  986.   if(!strcasecmp(Info, "ConnectPass")) return Network->ConnectPass;
  987.   if(!strcasecmp(Info, "FullHost")) return Network->FullHost;
  988.  
  989.   if(Context->Type != CONTEXT_CHANNEL) return NULL;
  990.   ChannelInfo *Channel = Context->Target.Channel;
  991.   if(!strcasecmp(Info, "Channel")) return Channel->Name;
  992.   return NULL;
  993. }
  994.  
  995. ContextInfo *Bot_MakeContext(ContextInfo *Context, int Type, void *Pointer) {
  996.   Context->Type = Type;
  997.   Context->Target.Generic = Pointer;
  998.   Context->User = NULL;
  999.   return Context;
  1000. }
  1001.  
  1002. int Bot_ContextIsValid(ContextInfo *Context) {
  1003.   for(NetworkInfo *Net = FirstNetwork; Net; Net=Net->Next) {
  1004.     if(Context->Target.Network == Net)
  1005.       return 1;
  1006.     for(ChannelInfo *Chan = Net->FirstChannel; Chan; Chan=Chan->Next) {
  1007.       if(Context->Target.Channel == Chan)
  1008.         return 1;
  1009.     }
  1010.   }
  1011.   return 0;
  1012. }
  1013.  
  1014. int Default_YouJoin(BotEvent *Event) {
  1015.   NetworkInfo *Network = Event->Context->Target.Network;
  1016.   const char *ChannelName = Bot_GetEventStr(Event, "Channel", NULL);
  1017.   if(!ChannelName) return ER_NORMAL;
  1018.  
  1019.   ChannelInfo NewChannel;
  1020.   Bot_StrCpy(NewChannel.Name, ChannelName, sizeof(NewChannel.Name));
  1021.   NewChannel.Network = Network;
  1022.   NewChannel.Prev = NULL;
  1023.   NewChannel.Next = NULL;
  1024.   NewChannel.Scrollback = NULL;
  1025.   NewChannel.PluginData = NULL;
  1026.  
  1027.   ChannelInfo *FindChannel = Network->FirstChannel;
  1028.   if(!FindChannel) {
  1029.     ChannelInfo *Alloc = (ChannelInfo*)malloc(sizeof(ChannelInfo));
  1030.     if(!Alloc) return ER_NORMAL;
  1031.     *Alloc = NewChannel;
  1032.     Network->FirstChannel = Alloc;
  1033.     return ER_NORMAL;
  1034.   }
  1035.   while(FindChannel) {
  1036.     if(!strcasecmp(FindChannel->Name, ChannelName))
  1037.       return ER_NORMAL;
  1038.     if(!FindChannel->Next) {
  1039.       ChannelInfo *Alloc = (ChannelInfo*)malloc(sizeof(ChannelInfo));
  1040.       if(!Alloc) return ER_NORMAL;
  1041.       *Alloc = NewChannel;
  1042.       FindChannel->Next = Alloc;
  1043.       Alloc->Prev = FindChannel;
  1044.       return ER_NORMAL;
  1045.     }
  1046.     FindChannel = FindChannel->Next;
  1047.   }
  1048.   return ER_NORMAL;
  1049. }
  1050. int Default_YouPartKick(BotEvent *Event) {
  1051.   NetworkInfo *Network = Event->Context->Target.Channel->Network;
  1052.   const char *ChannelName = Bot_GetEventStr(Event, "Channel", NULL);
  1053.   if(!ChannelName) return ER_NORMAL;
  1054.  
  1055.   ChannelInfo *FindChannel = Network->FirstChannel;
  1056.   while(FindChannel) {
  1057.     if(!strcasecmp(FindChannel->Name, ChannelName)) {
  1058.       if(Network->FirstChannel == FindChannel)
  1059.         Network->FirstChannel = FindChannel->Next;
  1060.       if(FindChannel->Prev) FindChannel->Prev->Next = FindChannel->Next;
  1061.       if(FindChannel->Next) FindChannel->Next->Prev = FindChannel->Prev;
  1062.       Bot_FreeChannel(FindChannel);
  1063.       free(FindChannel);
  1064.       return ER_NORMAL;
  1065.     }
  1066.     FindChannel = FindChannel->Next;
  1067.   }
  1068.   return ER_NORMAL;
  1069. }
  1070.  
  1071. /* core handlers for stuff */
  1072. int Default_ServerOutput(BotEvent *Event) {
  1073.   if(Event->Context->Type != CONTEXT_NETWORK) {
  1074.     fprintf(stderr, "Server Output with non-network context\n");
  1075.     return ER_NORMAL; // ???
  1076.   }
  1077.   char Text[strlen(Event->Text)+5];
  1078.   sprintf(Text, "%s\n", Event->Text);
  1079.   printf(">> %s", Text);
  1080.   NetworkInfo *Network = Event->Context->Target.Network;
  1081.   SDLNet_TCP_Send(Network->Socket,Text,strlen(Text));
  1082.   return ER_HANDLED;
  1083. }
  1084.  
  1085. const char *Bot_NameFromContext(ContextInfo *Info, char *Write) {
  1086.   switch(Info->Type) {
  1087.     case CONTEXT_NETWORK:
  1088.       return Info->Target.Network->Tag;
  1089.     case CONTEXT_CHANNEL:
  1090.       return Info->Target.Channel->Name;
  1091.     case CONTEXT_USER:
  1092.       strcpy(Write, Info->User);
  1093.       char *Poke = strchr(Write, '!');
  1094.       if(Poke) *Poke = 0;
  1095.       return Write;
  1096.     default:
  1097.       return NULL;
  1098.   }
  1099. }
  1100.  
  1101. int Default_ClientCommand(BotEvent *Event) {
  1102.   NetworkInfo *Network = Event->Context->Target.Network;
  1103.   if(Event->Context->Type == CONTEXT_CHANNEL) Network = Event->Context->Target.Channel->Network;
  1104.   ContextInfo NetContext;
  1105.   Bot_MakeContext(&NetContext, CONTEXT_NETWORK, Network);
  1106.   char SourceBuf[64];
  1107.  
  1108.   const char *Cmd = Event->Word[0];
  1109.   const char *Arg = Event->WordEol[1];
  1110.   char NewArg[strlen(Arg)+16];
  1111.  
  1112.   if(!strcasecmp(Cmd, "me")) {
  1113.     Cmd = "say";
  1114.     sprintf(NewArg, "\1ACTION %s\1", Arg);
  1115.     Arg = NewArg;
  1116.   }
  1117.  
  1118.   if(!strcasecmp(Cmd, "say")) {
  1119.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "PRIVMSG %s :%s", Bot_NameFromContext(Event->Context, SourceBuf), Arg);
  1120.     return ER_HANDLED;
  1121.   } else if(!strcasecmp(Cmd, "msg")) {
  1122.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "PRIVMSG %s :%s", Event->Word[1], Event->WordEol[2]);
  1123.     return ER_HANDLED;
  1124.   } else if(!strcasecmp(Cmd, "join")) {
  1125.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "JOIN %s", Event->WordEol[1]);
  1126.     return ER_HANDLED;
  1127.   } else if(!strcasecmp(Cmd, "part")) {
  1128.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "PART %s :%s", Event->Word[1], Event->WordEol[2]);
  1129.     return ER_HANDLED;
  1130.   } else if(!strcasecmp(Cmd, "kick")) {
  1131.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "KICK %s :%s", Event->Word[1], Event->WordEol[2]);
  1132.     return ER_HANDLED;
  1133.   } else if(!strcasecmp(Cmd, "mode")) {
  1134.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "MODE %s", Event->WordEol[1]);
  1135.     return ER_HANDLED;
  1136.   } else if(!strcasecmp(Cmd, "notice")) {
  1137.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "NOTICE %s :%s", Event->Word[1], Event->WordEol[2]);
  1138.     return ER_HANDLED;
  1139.   } else if(!strcasecmp(Cmd, "topic")) {
  1140.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "%s", Event->WordEol[1]);
  1141.     return ER_HANDLED;
  1142.   } else if(!strcasecmp(Cmd, "quote")) {
  1143.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "%s", Event->WordEol[1]);
  1144.     return ER_HANDLED;
  1145.   } else if(!strcasecmp(Cmd, "shutdown")) {
  1146.     NeedShutdown = 1;
  1147.     RebootAfterShutdown = 0;
  1148.     return ER_HANDLED;
  1149.   } else if(!strcasecmp(Cmd, "reboot")) {
  1150.     NeedShutdown = 1;
  1151.     RebootAfterShutdown = 1;
  1152.     return ER_HANDLED;
  1153.   } else if(!strcasecmp(Cmd, "modload")) {
  1154.     PluginInfo *P = Bot_LoadPlugin(Event->WordEol[2]);
  1155.     if(P) Bot_StrCpy(P->Tag, Event->Word[1], sizeof(P->Tag));
  1156.     return ER_HANDLED;
  1157.   } else if(!strcasecmp(Cmd, "modunload")) {
  1158.     for(PluginInfo *P = FirstPlugin;P;)
  1159.       if(!strcasecmp(Event->WordEol[1], P->Tag))
  1160.         Bot_UnloadPlugin(P);
  1161.     return ER_HANDLED;
  1162.   } else if(!strcasecmp(Cmd, "modreload")) {
  1163.     for(PluginInfo *P = FirstPlugin;P;)
  1164.       if(!strcasecmp(Event->WordEol[1], P->Tag)) {
  1165.         char Path[sizeof(P->Path)+1];
  1166.         Bot_UnloadPlugin(P);
  1167.         P = Bot_LoadPlugin(Path);
  1168.         if(P) Bot_StrCpy(P->Tag, Event->Word[1], sizeof(P->Tag));
  1169.       }
  1170.     return ER_HANDLED;
  1171.   } else if(!strcasecmp(Cmd, "connect")) {
  1172.     Bot_ConnectNetwork(Event->WordEol[1]);
  1173.     return ER_HANDLED;
  1174.   } else if(!strcasecmp(Cmd, "disconnect")) {
  1175.     for(NetworkInfo *Net = FirstNetwork;Net;Net=Net->Next)
  1176.       if(!strcasecmp(Net->Tag,Event->Word[1]))
  1177.         Bot_DisconnectNetwork(Net, Event->WordEol[2]);
  1178.     return ER_HANDLED;
  1179.   } else if(!strcasecmp(Cmd, "reconnect")) {
  1180.     for(NetworkInfo *Net = FirstNetwork;Net;Net=Net->Next)
  1181.       if(!strcasecmp(Net->Tag,Event->Word[1]))
  1182.         Bot_DisconnectNetwork(Net, Event->WordEol[2]);
  1183.     Bot_ConnectNetwork(Event->WordEol[1]);
  1184.     return ER_HANDLED;
  1185.   } else if(!strcasecmp(Cmd, "rehash")) {
  1186.     Bot_ReloadConfig(ConfigPath);
  1187.     return ER_HANDLED;
  1188.   } else if(!strcasecmp(Cmd, "r")) {
  1189.     NetworkInfo *Net = Bot_FindNetwork(Event->Word[0]);
  1190.     ContextInfo RContext;
  1191.     if(Net) {
  1192.       if(Event->Word[1][0] == '#') {
  1193.          Bot_MakeContext(&RContext, CONTEXT_CHANNEL, Bot_FindChannel(Net, Event->Word[1]));
  1194.          if(RContext.Target.Channel)
  1195.            Bot_StartEvent(&CorePlugin, "Client Command", &RContext, 0, "%s", Event->WordEol[2]);
  1196.       } else {
  1197.          Bot_MakeContext(&RContext, CONTEXT_NETWORK, Network);
  1198.          Bot_StartEvent(&CorePlugin, "Client Command", &RContext, 0, "%s", Event->WordEol[2]);
  1199.       }
  1200.     }
  1201.     return ER_HANDLED;
  1202.   } else {
  1203.     Bot_StartEvent(&CorePlugin, "Server Output", &NetContext, 0, "%s", Event->WordEol[0]);
  1204.     return ER_HANDLED;
  1205.   }
  1206.   return ER_NORMAL;
  1207. }
  1208.  
  1209. int Default_ServerInput(BotEvent *Event) {
  1210.   char *RawSource = Event->Word[0];
  1211.   char *Command = Event->Word[1];
  1212.   char Source[256]="", *User=NULL, *Host=NULL;
  1213.   char Temp[768];
  1214.   char *MyNick = Event->Context->Target.Network->Nick;
  1215.   NetworkInfo *Network = Event->Context->Target.Network;;
  1216.   ContextInfo NewContext, *SameContext = Event->Context;
  1217.   printf("<< %s\n", Event->Text);
  1218.  
  1219.   char GroupName[64], *Peek, *Peek2;
  1220.   sprintf(GroupName, "Network %s%%Network Default", Network->Tag);
  1221.   Bot_SplitUserHost(RawSource, Source, &User, &Host);
  1222.   int SourceIsMe = !strcasecmp(Source, MyNick);
  1223.  
  1224.   if((strlen(Command) == 3) && isdigit(*Command)) { // numeric reply
  1225.     switch(strtol(Command, NULL, 10)) {
  1226.       case RPL_WELCOME: // "Welcome to the Internet Relay Network <nick>!<user>@<host>"
  1227.         Bot_StrCpy(Network->Nick, Event->Word[2], sizeof(Network->Nick));
  1228.         Bot_StrCpy(Network->FullHost, Event->Word[Event->WordLen-1], sizeof(Network->FullHost));
  1229.         Bot_StartEvent(&CorePlugin, "Server Output", SameContext, 0, "MODE %s +B", Network->Nick);
  1230.  
  1231.         Bot_StrCpy(Temp, Bot_GetConfigStr(GroupName, "Channels", ""), sizeof(Temp));
  1232.         Peek = strtok_r(Temp," ", &Peek2);
  1233.         while(Peek != NULL) {
  1234.           Bot_StartEvent(&CorePlugin, "Server Output", SameContext, 0, "JOIN %s", Peek);
  1235.           Peek = strtok_r(NULL, " ", &Peek2);
  1236.         }
  1237.         Bot_StartEvent(&CorePlugin, "Server Connected", SameContext, 0, "fullhost=%s", Network->FullHost);
  1238.         break;
  1239.       case RPL_YOURHOST: // "Your host is <servername>, running version <ver>"
  1240.         break;
  1241.       case RPL_MYINFO: // "<servername> <version> <available user modes> <available channel modes>"
  1242.         break;
  1243.      case RPL_CHANNELMODEIS: // "<channel> <mode> <mode params>"
  1244.        break;
  1245.      case RPL_NOTOPIC: // "<channel> :No topic is set"
  1246.        break;
  1247.      case RPL_TOPIC: // "<channel> :<topic>"
  1248.        Bot_MakeContext(&NewContext, CONTEXT_CHANNEL, Bot_FindChannel(Network, Event->Word[2]));
  1249.        if(NewContext.Target.Channel)
  1250.          Bot_StartEvent(&CorePlugin, "Channel Topic Is", &NewContext, 0, "channel=%s \ttext=%s\t", Event->Word[2], Event->WordEol[3]);
  1251.        break;
  1252.      case RPL_WHOREPLY: // "<channel> <user> <host> <server> <nick> ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>"
  1253.        break;
  1254.      case RPL_ENDOFWHO: // "<name> :End of WHO list"
  1255.        break;
  1256.      case RPL_NAMREPLY: // "( "=" / "*" / "@" ) <channel> :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
  1257.        break;
  1258.      case RPL_ENDOFNAMES: // <channel>
  1259.        break;
  1260.      case RPL_YOUREOPER:
  1261.        break;
  1262.      case ERR_NOSUCHNICK: // <nickname>
  1263.        break;
  1264.      case ERR_NOSUCHSERVER: // <server name>
  1265.        break;
  1266.      case ERR_NOSUCHCHANNEL: // <channel name>
  1267.        break;
  1268.      case ERR_CANNOTSENDTOCHAN: // <channel name>
  1269.        break;
  1270.      case ERR_TOOMANYCHANNELS: // <channel name>
  1271.        break;
  1272.      case ERR_UNKNOWNCOMMAND: // <command>
  1273.        break;
  1274.      case ERR_ERRONEUSNICKNAME: // <nickname>
  1275.        break;
  1276.      case ERR_NICKNAMEINUSE: // <nickname>
  1277.        break;
  1278.     }
  1279.   }
  1280.   if(!strcasecmp("PING",Event->Word[0])) {
  1281.     Bot_StartEvent(&CorePlugin, "Server Output", SameContext, 0, "PONG :%s", Event->WordEol[1]);
  1282.   } else if(!strcasecmp(Command, "PRIVMSG")) {
  1283.     if(Event->Word[2][0] == '#') {
  1284.       Bot_MakeContext(&NewContext, CONTEXT_CHANNEL, Bot_FindChannel(Network, Event->Word[2]));
  1285.        if(NewContext.Target.Channel)
  1286.          Bot_StartEvent(&CorePlugin, "Channel Message", &NewContext, 0, "channel=%s nick=%s fullhost=%s \ttext=%s\t", Event->Word[2], Source, RawSource, Event->WordEol[3]);
  1287.        else
  1288.          fprintf(stderr, "Privmsg on channel that doesn't exist (%s)", Event->Word[2]);
  1289.     } else {
  1290.       Bot_MakeContext(&NewContext, CONTEXT_USER, Network);
  1291.       NewContext.User = RawSource;
  1292.       Bot_StartEvent(&CorePlugin, "Private Message", &NewContext, 0, "nick=%s fullhost=%s \ttext=%s\t", Source, RawSource, Event->WordEol[3]);
  1293.     }
  1294.   } else if(!strcasecmp(Command, "JOIN")) {
  1295.     if(!SourceIsMe)
  1296.       Bot_StartEvent(&CorePlugin, "Channel Join", Bot_MakeContext(&NewContext, CONTEXT_CHANNEL, Bot_FindChannel(Network, Event->Word[2])), 0, "channel=%s nick=%s fullhost=%s", Event->Word[2], Source, RawSource);
  1297.     else
  1298.       Bot_StartEvent(&CorePlugin, "Server You Join", SameContext, 0, "channel=%s", Event->Word[2]);
  1299.   } else if(!strcasecmp(Command, "PART")) {
  1300.     Bot_StartEvent(&CorePlugin, (!SourceIsMe)?"Channel Part":"Channel You Part", Bot_MakeContext(&NewContext, CONTEXT_CHANNEL, Bot_FindChannel(Network, Event->Word[2])), 0, "channel=%s nick=%s fullhost=%s text=\t%s\t", Event->Word[2], Source, RawSource, Event->WordEol[3]);
  1301.   } else if(!strcasecmp(Command, "NICK")) {
  1302.     Bot_StartEvent(&CorePlugin, (!SourceIsMe)?"Server Nick":"Server You Nick", SameContext, 0, "nick=%s newnick=%s fullhost=%s", Source, Event->Word[2], RawSource);
  1303.   } else if(!strcasecmp(Command, "TOPIC")) {
  1304.     Bot_StartEvent(&CorePlugin, (!SourceIsMe)?"Channel Topic":"Channel You Topic", Bot_MakeContext(&NewContext, CONTEXT_CHANNEL, Bot_FindChannel(Network, Event->Word[2])), 0, "nick=%s channel=%s fullhost=%s \ttext=%s\t", Source, Event->Word[2], RawSource, Event->WordEol[3]);
  1305.   } else if(!strcasecmp(Command, "KICK")) {
  1306.     Bot_StartEvent(&CorePlugin, (!strcasecmp(Event->Word[3], MyNick))?"Channel Kick":"Channel You Kicked", Bot_MakeContext(&NewContext, CONTEXT_CHANNEL, Bot_FindChannel(Network, Event->Word[2])), 0, "nick=%s channel=%s fullhost=%s target=%s \ttest=%s\t", Source, Event->Word[2], RawSource, Event->Word[3], Event->WordEol[4]);
  1307.   } else if(!strcasecmp(Command, "QUIT")) {
  1308.     Bot_StartEvent(&CorePlugin, (!SourceIsMe)?"Server Quit":"Server You Quit", SameContext, 0, "nick=%s fullhost=%s \ttext=%s\t", Source, RawSource, Event->WordEol[2]);
  1309.   }
  1310.   return ER_NORMAL;
  1311. }
  1312.  
  1313. /* everything else */
  1314. int main(int argc, char *argv[]) {
  1315.   if(argc>=2)
  1316.     ConfigPath = argv[1];
  1317.   if(SDLNet_Init() < 0) {
  1318.     fprintf(stderr, "SDLNet_Init: %s\n", SDLNet_GetError());
  1319.     exit(EXIT_FAILURE);
  1320.   }
  1321.  
  1322.   SocketSet = SDLNet_AllocSocketSet(Bot_GetConfigInt("Bot", "SocketSetSize", 10));
  1323.   if(!SocketSet) {
  1324.     printf("SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
  1325.     return 0;
  1326.   }
  1327.  
  1328.   FirstEvent = NULL;
  1329.   FirstPlugin = NULL;
  1330.   FirstConfig = NULL;
  1331.   FirstAsyncEvent = NULL;
  1332.   FirstNetwork = NULL;
  1333.   Bot_ReloadConfig(ConfigPath);
  1334.   Bot_ZeroStruct(CorePlugin);
  1335.  
  1336.   ContextInfo Context;
  1337.   Bot_AddEventHook(&CorePlugin, "Server Input", PRI_LOWEST, 0, 0, 0, Default_ServerInput);
  1338.   Bot_AddEventHook(&CorePlugin, "Server You Join", PRI_HIGHER, 0, 0, 0, Default_YouJoin);
  1339.   Bot_AddEventHook(&CorePlugin, "Channel You Part", PRI_LOWEST, 0, 0, 0, Default_YouPartKick);
  1340.   Bot_AddEventHook(&CorePlugin, "Channel You Kicked", PRI_LOWEST, 0, 0, 0, Default_YouPartKick);
  1341.   Bot_AddEventHook(&CorePlugin, "Client Command", PRI_LOWEST, 0, EF_ALREADY_HANDLED, 0, Default_ClientCommand);
  1342.   Bot_AddEventHook(&CorePlugin, "Server Output", PRI_LOWEST, 0, EF_ALREADY_HANDLED, 0, Default_ServerOutput);
  1343.  
  1344.   while(!NeedShutdown) {
  1345.     Bot_FreeAsyncEvents(0);
  1346.     for(PluginInfo *Mod=FirstPlugin;Mod;Mod=Mod->Next) {
  1347.       for(int i=0;i<NUM_TIMERS;i++) {
  1348.         TimerInfo *Timer = Mod->Timers[i];
  1349.         if(Timer && (Timer->EndTime <= time(NULL))) {
  1350.           if(Bot_ContextIsValid(&Timer->Context))
  1351.             Bot_StartEvent(Mod, Timer->EventType, &Timer->Context, Timer->Flags, "%s", Timer->EventText);
  1352.           else
  1353.             fprintf(stderr, "Invalid context for a timer\n");
  1354.           free(Timer);
  1355.           Mod->Timers[i] = NULL;
  1356.         }
  1357.       }
  1358.     }
  1359.     for(NetworkInfo *Net=FirstNetwork;Net;Net=Net->Next) {
  1360.  
  1361.     }
  1362.     if(SDLNet_CheckSockets(SocketSet, 500))
  1363.       for(NetworkInfo *Nets = FirstNetwork;Nets;Nets=Nets->Next)
  1364.         if(SDLNet_SocketReady(Nets->Socket)) {
  1365.           char ServerRead[1<<14];
  1366.           int Size = SDLNet_TCP_Recv(Nets->Socket,ServerRead,sizeof(ServerRead)-1);
  1367.           if(Size) {
  1368.             ServerRead[Size] = 0;
  1369.             for(char *Peek = ServerRead;Peek;) {
  1370.               char *Next = strchr(Peek, '\n');
  1371.               if(Next) {
  1372.                 if(Next[-1] == '\r') Next[-1] = 0;
  1373.                 *Next = 0;
  1374.                 Next++;
  1375.               }
  1376.               if(*Peek)
  1377.                 Bot_StartEvent(&CorePlugin, "Server Input", Bot_MakeContext(&Context, CONTEXT_NETWORK, Nets), 0, "%s", Peek);
  1378.               Peek = Next;
  1379.             }
  1380.           }
  1381.         }
  1382.   }
  1383.  
  1384.   for(NetworkInfo *Net = FirstNetwork;Net;) {
  1385.     NetworkInfo *Next = Net->Next;
  1386.     Bot_DisconnectNetwork(Net, "shutting down");
  1387.     Net = Next;
  1388.   }
  1389.  
  1390.   for(PluginInfo *P = FirstPlugin;P;) {
  1391.     PluginInfo *Next = P->Next;
  1392.     Bot_UnloadPlugin(P);
  1393.     P = Next;
  1394.   }
  1395.   Bot_FreeAsyncEvents(1);
  1396.  
  1397.   Bot_UnloadPlugin(&CorePlugin);
  1398.   Bot_FreeConfig(FirstConfig);
  1399.   FirstConfig = NULL;
  1400.   SDLNet_Quit();
  1401.  
  1402.   SDLNet_FreeSocketSet(SocketSet);
  1403.   return EXIT_SUCCESS;
  1404. }
Add Comment
Please, Sign In to add comment