]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_mssql.cpp
3083dcec2f105e876dc1a1507ba08b869942f412
[user/henk/code/inspircd.git] / src / modules / extra / m_mssql.cpp
1 /*               +------------------------------------+
2  *               | Inspire Internet Relay Chat Daemon |
3  *               +------------------------------------+
4  *
5  *      InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *                        the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include <tds.h>
16 #include <tdsconvert.h>
17 #include "users.h"
18 #include "channels.h"
19 #include "modules.h"
20
21 #include "m_sqlv2.h"
22
23 /* $ModDesc: MsSQL provider */
24 /* $CompileFlags: exec("grep VERSION_NO /usr/include/tdsver.h 2>/dev/null | perl -e 'print "-D_TDSVER=".((<> =~ /freetds v(\d+\.\d+)/i) ? $1*100 : 0);'") */
25 /* $LinkerFlags: -ltds */
26 /* $ModDep: m_sqlv2.h */
27
28 class SQLConn;
29 class MsSQLResult;
30 class ResultNotifier;
31 class MsSQLListener;
32 class ModuleMsSQL;
33
34 typedef std::map<std::string, SQLConn*> ConnMap;
35 typedef std::deque<MsSQLResult*> ResultQueue;
36
37 ResultNotifier* notifier = NULL;
38 MsSQLListener* listener = NULL;
39 int QueueFD = -1;
40
41 ConnMap connections;
42 Mutex* QueueMutex;
43 Mutex* ResultsMutex;
44 Mutex* LoggingMutex;
45
46 class QueryThread : public Thread
47 {
48   private:
49         ModuleMsSQL* Parent;
50         InspIRCd* ServerInstance;
51   public:
52         QueryThread(InspIRCd* si, ModuleMsSQL* mod)
53         : Thread(), Parent(mod), ServerInstance(si)
54         {
55         }
56         ~QueryThread() { }
57         virtual void Run();
58 };
59
60 class ResultNotifier : public BufferedSocket
61 {
62         ModuleMsSQL* mod;
63
64  public:
65         ResultNotifier(ModuleMsSQL* m, InspIRCd* SI, int newfd, char* ip) : BufferedSocket(SI, newfd, ip), mod(m)
66         {
67         }
68
69         virtual bool OnDataReady()
70         {
71                 char data = 0;
72                 if (ServerInstance->SE->Recv(this, &data, 1, 0) > 0)
73                 {
74                         Dispatch();
75                         return true;
76                 }
77                 return false;
78         }
79
80         void Dispatch();
81 };
82
83 class MsSQLListener : public ListenSocketBase
84 {
85         ModuleMsSQL* Parent;
86         irc::sockets::insp_sockaddr sock_us;
87         socklen_t uslen;
88         FileReader* index;
89
90  public:
91         MsSQLListener(ModuleMsSQL* P, InspIRCd* Instance, int port, const std::string &addr) : ListenSocketBase(Instance, port, addr), Parent(P)
92         {
93                 uslen = sizeof(sock_us);
94                 if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen))
95                 {
96                         throw ModuleException("Could not getsockname() to find out port number for ITC port");
97                 }
98         }
99
100         virtual void OnAcceptReady(const std::string &ipconnectedto, int nfd, const std::string &incomingip)
101         {
102                 new ResultNotifier(this->Parent, this->ServerInstance, nfd, (char *)ipconnectedto.c_str()); // XXX unsafe casts suck
103         }
104
105         /* Using getsockname and ntohs, we can determine which port number we were allocated */
106         int GetPort()
107         {
108 #ifdef IPV6
109                 return ntohs(sock_us.sin6_port);
110 #else
111                 return ntohs(sock_us.sin_port);
112 #endif
113         }
114 };
115
116
117 class MsSQLResult : public SQLresult
118 {
119  private:
120         int currentrow;
121         int rows;
122         int cols;
123
124         std::vector<std::string> colnames;
125         std::vector<SQLfieldList> fieldlists;
126         SQLfieldList emptyfieldlist;
127
128         SQLfieldList* fieldlist;
129         SQLfieldMap* fieldmap;
130
131  public:
132         MsSQLResult(Module* self, Module* to, unsigned int rid)
133         : SQLresult(self, to, rid), currentrow(0), rows(0), cols(0), fieldlist(NULL), fieldmap(NULL)
134         {
135         }
136
137         ~MsSQLResult()
138         {
139         }
140
141         void AddRow(int colsnum, char **dat, char **colname)
142         {
143                 colnames.clear();
144                 cols = colsnum;
145                 for (int i = 0; i < colsnum; i++)
146                 {
147                         fieldlists.resize(fieldlists.size()+1);
148                         colnames.push_back(colname[i]);
149                         SQLfield sf(dat[i] ? dat[i] : "", dat[i] ? false : true);
150                         fieldlists[rows].push_back(sf);
151                 }
152                 rows++;
153         }
154
155         void UpdateAffectedCount()
156         {
157                 rows++;
158         }
159
160         virtual int Rows()
161         {
162                 return rows;
163         }
164
165         virtual int Cols()
166         {
167                 return cols;
168         }
169
170         virtual std::string ColName(int column)
171         {
172                 if (column < (int)colnames.size())
173                 {
174                         return colnames[column];
175                 }
176                 else
177                 {
178                         throw SQLbadColName();
179                 }
180                 return "";
181         }
182
183         virtual int ColNum(const std::string &column)
184         {
185                 for (unsigned int i = 0; i < colnames.size(); i++)
186                 {
187                         if (column == colnames[i])
188                                 return i;
189                 }
190                 throw SQLbadColName();
191                 return 0;
192         }
193
194         virtual SQLfield GetValue(int row, int column)
195         {
196                 if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
197                 {
198                         return fieldlists[row][column];
199                 }
200
201                 throw SQLbadColName();
202
203                 /* XXX: We never actually get here because of the throw */
204                 return SQLfield("",true);
205         }
206
207         virtual SQLfieldList& GetRow()
208         {
209                 if (currentrow < rows)
210                         return fieldlists[currentrow];
211                 else
212                         return emptyfieldlist;
213         }
214
215         virtual SQLfieldMap& GetRowMap()
216         {
217                 /* In an effort to reduce overhead we don't actually allocate the map
218                  * until the first time it's needed...so...
219                  */
220                 if(fieldmap)
221                 {
222                         fieldmap->clear();
223                 }
224                 else
225                 {
226                         fieldmap = new SQLfieldMap;
227                 }
228
229                 if (currentrow < rows)
230                 {
231                         for (int i = 0; i < Cols(); i++)
232                         {
233                                 fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
234                         }
235                         currentrow++;
236                 }
237
238                 return *fieldmap;
239         }
240
241         virtual SQLfieldList* GetRowPtr()
242         {
243                 fieldlist = new SQLfieldList();
244
245                 if (currentrow < rows)
246                 {
247                         for (int i = 0; i < Rows(); i++)
248                         {
249                                 fieldlist->push_back(fieldlists[currentrow][i]);
250                         }
251                         currentrow++;
252                 }
253                 return fieldlist;
254         }
255
256         virtual SQLfieldMap* GetRowMapPtr()
257         {
258                 fieldmap = new SQLfieldMap();
259
260                 if (currentrow < rows)
261                 {
262                         for (int i = 0; i < Cols(); i++)
263                         {
264                                 fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
265                         }
266                         currentrow++;
267                 }
268
269                 return fieldmap;
270         }
271
272         virtual void Free(SQLfieldMap* fm)
273         {
274                 delete fm;
275         }
276
277         virtual void Free(SQLfieldList* fl)
278         {
279                 delete fl;
280         }
281
282
283 };
284
285 class SQLConn : public classbase
286 {
287  private:
288         ResultQueue results;
289         InspIRCd* ServerInstance;
290         Module* mod;
291         SQLhost host;
292         TDSLOGIN* login;
293         TDSSOCKET* sock;
294         TDSCONTEXT* context;
295
296  public:
297         QueryQueue queue;
298
299         SQLConn(InspIRCd* SI, Module* m, const SQLhost& hi)
300         : ServerInstance(SI), mod(m), host(hi), login(NULL), sock(NULL), context(NULL)
301         {
302                 if (OpenDB())
303                 {
304                         std::string query("USE " + host.name);
305                         if (tds_submit_query(sock, query.c_str()) == TDS_SUCCEED)
306                         {
307                                 if (tds_process_simple_query(sock) != TDS_SUCCEED)
308                                 {
309                                         LoggingMutex->Lock();
310                                         ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
311                                         LoggingMutex->Unlock();
312                                         CloseDB();
313                                 }
314                         }
315                         else
316                         {
317                                 LoggingMutex->Lock();
318                                 ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
319                                 LoggingMutex->Unlock();
320                                 CloseDB();
321                         }
322                 }
323                 else
324                 {
325                         LoggingMutex->Lock();
326                         ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not connect to DB with id: " + host.id);
327                         LoggingMutex->Unlock();
328                         CloseDB();
329                 }
330         }
331
332         ~SQLConn()
333         {
334                 CloseDB();
335         }
336
337         SQLerror Query(SQLrequest &req)
338         {
339                 if (!sock)
340                         return SQLerror(SQL_BAD_CONN, "Socket was NULL, check if SQL server is running.");
341
342                 /* Pointer to the buffer we screw around with substitution in */
343                 char* query;
344
345                 /* Pointer to the current end of query, where we append new stuff */
346                 char* queryend;
347
348                 /* Total length of the unescaped parameters */
349                 unsigned long paramlen;
350
351                 /* Total length of query, used for binary-safety */
352                 unsigned long querylength = 0;
353
354                 paramlen = 0;
355                 for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++)
356                 {
357                         paramlen += i->size();
358                 }
359
360                 /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
361                  * sizeofquery + (totalparamlength*2) + 1
362                  *
363                  * The +1 is for null-terminating the string
364                  */
365                 query = new char[req.query.q.length() + (paramlen*2) + 1];
366                 queryend = query;
367
368                 for(unsigned long i = 0; i < req.query.q.length(); i++)
369                 {
370                         if(req.query.q[i] == '?')
371                         {
372                                 if(req.query.p.size())
373                                 {
374                                         /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :]
375                                          */
376                                         char* escaped = new char[(req.query.p.front().length() * 2) + 1];
377                                         char* escend = escaped;
378                                         for (std::string::iterator p = req.query.p.front().begin(); p < req.query.p.front().end(); p++)
379                                         {
380                                                 if (*p == '\'')
381                                                 {
382                                                         *escend = *p;
383                                                         escend++;
384                                                         *escend = *p;
385                                                 }
386                                                 *escend = *p;
387                                                 escend++;
388                                         }
389                                         *escend = 0;
390
391                                         for (char* n = escaped; *n; n++)
392                                         {
393                                                 *queryend = *n;
394                                                 queryend++;
395                                         }
396                                         delete[] escaped;
397                                         req.query.p.pop_front();
398                                 }
399                                 else
400                                         break;
401                         }
402                         else
403                         {
404                                 *queryend = req.query.q[i];
405                                 queryend++;
406                         }
407                         querylength++;
408                 }
409                 *queryend = 0;
410                 req.query.q = query;
411
412                 MsSQLResult* res = new MsSQLResult((Module*)mod, req.GetSource(), req.id);
413                 res->dbid = host.id;
414                 res->query = req.query.q;
415
416                 char* msquery = strdup(req.query.q.data());
417                 LoggingMutex->Lock();
418                 ServerInstance->Logs->Log("m_mssql",DEBUG,"doing Query: %s",msquery);
419                 LoggingMutex->Unlock();
420                 if (tds_submit_query(sock, msquery) != TDS_SUCCEED)
421                 {
422                         std::string error("failed to execute: "+std::string(req.query.q.data()));
423                         delete[] query;
424                         delete res;
425                         free(msquery);
426                         return SQLerror(SQL_QSEND_FAIL, error);
427                 }
428                 delete[] query;
429                 free(msquery);
430
431                 int tds_res;
432                 while (tds_process_tokens(sock, &tds_res, NULL, TDS_TOKEN_RESULTS) == TDS_SUCCEED)
433                 {
434                         //ServerInstance->Logs->Log("m_mssql",DEBUG,"<******> result type: %d", tds_res);
435                         //ServerInstance->Logs->Log("m_mssql",DEBUG,"AFFECTED ROWS: %d", sock->rows_affected);
436                         switch (tds_res)
437                         {
438                                 case TDS_ROWFMT_RESULT:
439                                         break;
440
441                                 case TDS_DONE_RESULT:
442                                         if (sock->rows_affected > -1)
443                                         {
444                                                 for (int c = 0; c < sock->rows_affected; c++)  res->UpdateAffectedCount();
445                                                 continue;
446                                         }
447                                         break;
448
449                                 case TDS_ROW_RESULT:
450                                         while (tds_process_tokens(sock, &tds_res, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW) == TDS_SUCCEED)
451                                         {
452                                                 if (tds_res != TDS_ROW_RESULT)
453                                                         break;
454
455                                                 if (!sock->current_results)
456                                                         continue;
457
458                                                 if (sock->res_info->row_count > 0)
459                                                 {
460                                                         int cols = sock->res_info->num_cols;
461                                                         char** name = new char*[MAXBUF];
462                                                         char** data = new char*[MAXBUF];
463                                                         for (int j=0; j<cols; j++)
464                                                         {
465                                                                 TDSCOLUMN* col = sock->current_results->columns[j];
466                                                                 name[j] = col->column_name;
467
468                                                                 int ctype;
469                                                                 int srclen;
470                                                                 unsigned char* src;
471                                                                 CONV_RESULT dres;
472                                                                 ctype = tds_get_conversion_type(col->column_type, col->column_size);
473 #if _TDSVER >= 82
474                                                                         src = col->column_data;
475 #else
476                                                                         src = &(sock->current_results->current_row[col->column_offset]);
477 #endif
478                                                                 srclen = col->column_cur_size;
479                                                                 tds_convert(sock->tds_ctx, ctype, (TDS_CHAR *) src, srclen, SYBCHAR, &dres);
480                                                                 data[j] = (char*)dres.ib;
481                                                         }
482                                                         ResultReady(res, cols, data, name);
483                                                 }
484                                         }
485                                         break;
486
487                                 default:
488                                         break;
489                         }
490                 }
491                 ResultsMutex->Lock();
492                 results.push_back(res);
493                 ResultsMutex->Unlock();
494                 SendNotify();
495                 return SQLerror();
496         }
497
498         static int HandleMessage(const TDSCONTEXT * pContext, TDSSOCKET * pTdsSocket, TDSMESSAGE * pMessage)
499         {
500                 SQLConn* sc = (SQLConn*)pContext->parent;
501                 LoggingMutex->Lock();
502                 sc->ServerInstance->Logs->Log("m_mssql", DEBUG, "Message for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
503                 LoggingMutex->Unlock();
504                 return 0;
505         }
506
507         static int HandleError(const TDSCONTEXT * pContext, TDSSOCKET * pTdsSocket, TDSMESSAGE * pMessage)
508         {
509                 SQLConn* sc = (SQLConn*)pContext->parent;
510                 LoggingMutex->Lock();
511                 sc->ServerInstance->Logs->Log("m_mssql", DEFAULT, "Error for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
512                 LoggingMutex->Unlock();
513                 return 0;
514         }
515
516         void ResultReady(MsSQLResult *res, int cols, char **data, char **colnames)
517         {
518                 res->AddRow(cols, data, colnames);
519         }
520
521         void AffectedReady(MsSQLResult *res)
522         {
523                 res->UpdateAffectedCount();
524         }
525
526         bool OpenDB()
527         {
528                 CloseDB();
529
530                 TDSCONNECTION* conn = NULL;
531
532                 login = tds_alloc_login();
533                 tds_set_app(login, "TSQL");
534                 tds_set_library(login,"TDS-Library");
535                 tds_set_host(login, "");
536                 tds_set_server(login, host.host.c_str());
537                 tds_set_server_addr(login, host.host.c_str());
538                 tds_set_user(login, host.user.c_str());
539                 tds_set_passwd(login, host.pass.c_str());
540                 tds_set_port(login, host.port);
541                 tds_set_packet(login, 512);
542
543                 context = tds_alloc_context(this);
544                 context->msg_handler = HandleMessage;
545                 context->err_handler = HandleError;
546
547                 sock = tds_alloc_socket(context, 512);
548                 tds_set_parent(sock, NULL);
549
550                 conn = tds_read_config_info(NULL, login, context->locale);
551
552                 if (tds_connect(sock, conn) == TDS_SUCCEED)
553                 {
554                         tds_free_connection(conn);
555                         return 1;
556                 }
557                 tds_free_connection(conn);
558                 return 0;
559         }
560
561         void CloseDB()
562         {
563                 if (sock)
564                 {
565                         tds_free_socket(sock);
566                         sock = NULL;
567                 }
568                 if (context)
569                 {
570                         tds_free_context(context);
571                         context = NULL;
572                 }
573                 if (login)
574                 {
575                         tds_free_login(login);
576                         login = NULL;
577                 }
578         }
579
580         SQLhost GetConfHost()
581         {
582                 return host;
583         }
584
585         void SendResults()
586         {
587                 while (results.size())
588                 {
589                         MsSQLResult* res = results[0];
590                         ResultsMutex->Lock();
591                         if (res->GetDest())
592                         {
593                                 res->Send();
594                         }
595                         else
596                         {
597                                 /* If the client module is unloaded partway through a query then the provider will set
598                                  * the pointer to NULL. We cannot just cancel the query as the result will still come
599                                  * through at some point...and it could get messy if we play with invalid pointers...
600                                  */
601                                 delete res;
602                         }
603                         results.pop_front();
604                         ResultsMutex->Unlock();
605                 }
606         }
607
608         void ClearResults()
609         {
610                 while (results.size())
611                 {
612                         MsSQLResult* res = results[0];
613                         delete res;
614                         results.pop_front();
615                 }
616         }
617
618         void SendNotify()
619         {
620                 if (QueueFD < 0)
621                 {
622                         if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1)
623                         {
624                                 /* crap, we're out of sockets... */
625                                 return;
626                         }
627
628                         irc::sockets::insp_sockaddr addr;
629
630 #ifdef IPV6
631                         irc::sockets::insp_aton("::1", &addr.sin6_addr);
632                         addr.sin6_family = AF_FAMILY;
633                         addr.sin6_port = htons(listener->GetPort());
634 #else
635                         irc::sockets::insp_inaddr ia;
636                         irc::sockets::insp_aton("127.0.0.1", &ia);
637                         addr.sin_family = AF_FAMILY;
638                         addr.sin_addr = ia;
639                         addr.sin_port = htons(listener->GetPort());
640 #endif
641
642                         if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1)
643                         {
644                                 /* wtf, we cant connect to it, but we just created it! */
645                                 return;
646                         }
647                 }
648                 char id = 0;
649                 send(QueueFD, &id, 1, 0);
650         }
651
652         void DoLeadingQuery()
653         {
654                 SQLrequest& req = queue.front();
655                 req.error = Query(req);
656         }
657
658 };
659
660
661 class ModuleMsSQL : public Module
662 {
663  private:
664         unsigned long currid;
665         QueryThread* queryDispatcher;
666
667  public:
668         ModuleMsSQL(InspIRCd* Me)
669         : Module(Me), currid(0)
670         {
671                 LoggingMutex = ServerInstance->Mutexes->CreateMutex();
672                 ResultsMutex = ServerInstance->Mutexes->CreateMutex();
673                 QueueMutex = ServerInstance->Mutexes->CreateMutex();
674
675                 ServerInstance->Modules->UseInterface("SQLutils");
676
677                 if (!ServerInstance->Modules->PublishFeature("SQL", this))
678                 {
679                         throw ModuleException("m_mssql: Unable to publish feature 'SQL'");
680                 }
681
682                 /* Create a socket on a random port. Let the tcp stack allocate us an available port */
683 #ifdef IPV6
684                 listener = new MsSQLListener(this, ServerInstance, 0, "::1");
685 #else
686                 listener = new MsSQLListener(this, ServerInstance, 0, "127.0.0.1");
687 #endif
688
689                 if (listener->GetFd() == -1)
690                 {
691                         ServerInstance->Modules->DoneWithInterface("SQLutils");
692                         throw ModuleException("m_mssql: unable to create ITC pipe");
693                 }
694                 else
695                 {
696                         LoggingMutex->Lock();
697                         ServerInstance->Logs->Log("m_mssql", DEBUG, "MsSQL: Interthread comms port is %d", listener->GetPort());
698                         LoggingMutex->Unlock();
699                 }
700
701                 ReadConf();
702
703                 queryDispatcher = new QueryThread(ServerInstance, this);
704                 ServerInstance->Threads->Create(queryDispatcher);
705
706                 ServerInstance->Modules->PublishInterface("SQL", this);
707                 Implementation eventlist[] = { I_OnRequest, I_OnRehash };
708                 ServerInstance->Modules->Attach(eventlist, this, 2);
709         }
710
711         virtual ~ModuleMsSQL()
712         {
713                 delete queryDispatcher;
714                 ClearQueue();
715                 ClearAllConnections();
716
717                 ServerInstance->SE->DelFd(listener);
718                 ServerInstance->BufferedSocketCull();
719
720                 if (QueueFD >= 0)
721                 {
722                         shutdown(QueueFD, 2);
723                         close(QueueFD);
724                 }
725
726                 if (notifier)
727                 {
728                         ServerInstance->SE->DelFd(notifier);
729                         notifier->Close();
730                         ServerInstance->BufferedSocketCull();
731                 }
732
733                 ServerInstance->Modules->UnpublishInterface("SQL", this);
734                 ServerInstance->Modules->UnpublishFeature("SQL");
735                 ServerInstance->Modules->DoneWithInterface("SQLutils");
736
737                 delete LoggingMutex;
738                 delete ResultsMutex;
739                 delete QueueMutex;
740         }
741
742
743         void SendQueue()
744         {
745                 for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
746                 {
747                         iter->second->SendResults();
748                 }
749         }
750
751         void ClearQueue()
752         {
753                 for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
754                 {
755                         iter->second->ClearResults();
756                 }
757         }
758
759         bool HasHost(const SQLhost &host)
760         {
761                 for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
762                 {
763                         if (host == iter->second->GetConfHost())
764                                 return true;
765                 }
766                 return false;
767         }
768
769         bool HostInConf(const SQLhost &h)
770         {
771                 ConfigReader conf(ServerInstance);
772                 for(int i = 0; i < conf.Enumerate("database"); i++)
773                 {
774                         SQLhost host;
775                         host.id         = conf.ReadValue("database", "id", i);
776                         host.host       = conf.ReadValue("database", "hostname", i);
777                         host.port       = conf.ReadInteger("database", "port", "1433", i, true);
778                         host.name       = conf.ReadValue("database", "name", i);
779                         host.user       = conf.ReadValue("database", "username", i);
780                         host.pass       = conf.ReadValue("database", "password", i);
781                         if (h == host)
782                                 return true;
783                 }
784                 return false;
785         }
786
787         void ReadConf()
788         {
789                 ClearOldConnections();
790
791                 ConfigReader conf(ServerInstance);
792                 for(int i = 0; i < conf.Enumerate("database"); i++)
793                 {
794                         SQLhost host;
795
796                         host.id         = conf.ReadValue("database", "id", i);
797                         host.host       = conf.ReadValue("database", "hostname", i);
798                         host.port       = conf.ReadInteger("database", "port", "1433", i, true);
799                         host.name       = conf.ReadValue("database", "name", i);
800                         host.user       = conf.ReadValue("database", "username", i);
801                         host.pass       = conf.ReadValue("database", "password", i);
802
803                         if (HasHost(host))
804                                 continue;
805
806                         this->AddConn(host);
807                 }
808         }
809
810         void AddConn(const SQLhost& hi)
811         {
812                 if (HasHost(hi))
813                 {
814                         LoggingMutex->Lock();
815                         ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: A MsSQL connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
816                         LoggingMutex->Unlock();
817                         return;
818                 }
819
820                 SQLConn* newconn;
821
822                 newconn = new SQLConn(ServerInstance, this, hi);
823
824                 connections.insert(std::make_pair(hi.id, newconn));
825         }
826
827         void ClearOldConnections()
828         {
829                 ConnMap::iterator iter,safei;
830                 for (iter = connections.begin(); iter != connections.end(); iter++)
831                 {
832                         if (!HostInConf(iter->second->GetConfHost()))
833                         {
834                                 delete iter->second;
835                                 safei = iter;
836                                 --iter;
837                                 connections.erase(safei);
838                         }
839                 }
840         }
841
842         void ClearAllConnections()
843         {
844                 ConnMap::iterator i;
845                 while ((i = connections.begin()) != connections.end())
846                 {
847                         connections.erase(i);
848                         delete i->second;
849                 }
850         }
851
852         virtual void OnRehash(User* user, const std::string &parameter)
853         {
854                 QueueMutex->Lock();
855                 ReadConf();
856                 QueueMutex->Unlock();
857         }
858
859         virtual const char* OnRequest(Request* request)
860         {
861                 if(strcmp(SQLREQID, request->GetId()) == 0)
862                 {
863                         SQLrequest* req = (SQLrequest*)request;
864
865                         QueueMutex->Lock();
866
867                         ConnMap::iterator iter;
868
869                         const char* returnval = NULL;
870
871                         if((iter = connections.find(req->dbid)) != connections.end())
872                         {
873                                 req->id = NewID();
874                                 iter->second->queue.push(*req);
875                                 returnval= SQLSUCCESS;
876                         }
877                         else
878                         {
879                                 req->error.Id(SQL_BAD_DBID);
880                         }
881
882                         QueueMutex->Unlock();
883
884                         return returnval;
885                 }
886                 return NULL;
887         }
888
889         unsigned long NewID()
890         {
891                 if (currid+1 == 0)
892                         currid++;
893
894                 return ++currid;
895         }
896
897         virtual Version GetVersion()
898         {
899                 return Version("$Id$", VF_VENDOR | VF_SERVICEPROVIDER, API_VERSION);
900         }
901
902 };
903
904 void ResultNotifier::Dispatch()
905 {
906         mod->SendQueue();
907 }
908
909 void QueryThread::Run()
910 {
911         while (this->GetExitFlag() == false)
912         {
913                 SQLConn* conn = NULL;
914                 QueueMutex->Lock();
915                 for (ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
916                 {
917                         if (i->second->queue.totalsize())
918                         {
919                                 conn = i->second;
920                                 break;
921                         }
922                 }
923                 QueueMutex->Unlock();
924                 if (conn)
925                 {
926                         conn->DoLeadingQuery();
927                         QueueMutex->Lock();
928                         conn->queue.pop();
929                         QueueMutex->Unlock();
930                 }
931                 usleep(1000);
932         }
933 }
934
935 MODULE_INIT(ModuleMsSQL)