reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
PostgreSqlDb.cxx
Go to the documentation of this file.
1#include <cassert>
2#include <fcntl.h>
3
4#ifdef HAVE_CONFIG_H
5#include "config.h"
6#endif
7
8#ifdef USE_POSTGRESQL
9
10#include "rutil/ResipAssert.h"
11#include "rutil/Data.hxx"
12#include "rutil/DataStream.hxx"
13#include "rutil/Logger.hxx"
14#include "rutil/ParseBuffer.hxx"
15
16#include "repro/AbstractDb.hxx"
18#include "repro/UserStore.hxx"
19
21
22using namespace resip;
23using namespace repro;
24using namespace std;
25
26#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
27
28extern "C"
29{
30 void postgresqlThreadEnd(void*)
31 {
32 //mysql_thread_end(); // FIXME - PostgreSQL equivalent?
33 }
34}
35
36// This class helps ensure that each thread using the PostgreSQL API's
37// initialize by calling mysql_thread_init before calling any mySQL functions
39{
40 public:
42 {
43 ThreadIf::tlsKeyCreate(mThreadStorage, postgresqlThreadEnd);
44 }
46 {
47 ThreadIf::tlsKeyDelete(mThreadStorage);
48 }
49 void setInitialized()
50 {
51 ThreadIf::tlsSetValue(mThreadStorage, (void*) true);
52 }
53 bool isInitialized()
54 {
55 // Note: if value is not set yet then 0 (false) is returned
56 return ThreadIf::tlsGetValue(mThreadStorage) != 0;
57 }
58
59 private:
61};
63
64PostgreSqlDb::PostgreSqlDb(const resip::ConfigParse& config,
65 const Data& connInfo,
66 const Data& server,
67 const Data& user,
68 const Data& password,
69 const Data& databaseName,
70 unsigned int port,
73 mDBConnInfo(connInfo),
74 mDBServer(server),
75 mDBUser(user),
76 mDBPassword(password),
77 mDBName(databaseName),
78 mDBPort(port),
79 mCustomUserAuthQuery(customUserAuthQuery),
80 mConn(0)
81{
82 InfoLog( << "Using PostgreSQL DB with server=" << server << ", user=" << user << ", dbName=" << databaseName << ", port=" << port);
83
84 for (int i=0;i<MaxTable;i++)
85 {
86 mResult[i]=0;
87 mRow[i]=0;
88 }
89
90 if(!PQisthreadsafe())
91 {
92 ErrLog( << "Repro uses PostgreSQL from multiple threads - you MUST link with a thread safe version of the PostgreSQL client library (libpq)!");
93 }
94 else
95 {
96 connectToDatabase();
97 }
98}
99
100
101PostgreSqlDb::~PostgreSqlDb()
102{
103 disconnectFromDatabase();
104}
105
106void
107PostgreSqlDb::initialize() const
108{
109 if(!g_PostgreSQLInitializer.isInitialized())
110 {
111 g_PostgreSQLInitializer.setInitialized();
112 //mysql_thread_init(); // FIXME - PostgreSQL equivalent?
113 }
114}
115
116void
117PostgreSqlDb::disconnectFromDatabase() const
118{
119 if(mConn)
120 {
121 for (int i=0;i<MaxTable;i++)
122 {
123 if (mResult[i])
124 {
125 PQclear(mResult[i]);
126 mResult[i]=0;
127 mRow[i]=0;
128 }
129 }
130
131 PQfinish(mConn);
132 mConn = 0;
133 setConnected(false);
134 }
135}
136
137int
138PostgreSqlDb::connectToDatabase() const
139{
140 // Disconnect from database first (if required)
141 disconnectFromDatabase();
142
143 // Now try to connect
144 resip_assert(mConn == 0);
145 resip_assert(isConnected() == false);
146
147 Data connInfo(mDBConnInfo);
148 if(!mDBServer.empty())
149 {
150 connInfo = connInfo + " host=" + mDBServer;
151 }
152 if(mDBPort > 0)
153 {
154 connInfo = connInfo + " port=" + Data((uint32_t)mDBPort);
155 }
156 if(!mDBName.empty())
157 {
158 connInfo = connInfo + " dbname=" + mDBName;
159 }
160 if(!mDBUser.empty())
161 {
162 connInfo = connInfo + " user=" + mDBUser;
163 }
165 if(!mDBPassword.empty())
166 {
167 connInfo = connInfo + " password=" + mDBPassword;
168 connInfoLogString = connInfoLogString + " password=<hidden>";
169 }
170
171 DebugLog(<<"Trying to connect to PostgreSQL server with conninfo string: " << connInfoLogString);
172 mConn = PQconnectdb(connInfo.c_str());
173
174 int rc = PQstatus(mConn);
175 if (rc != CONNECTION_OK)
176 {
177 ErrLog( << "PostgreSQL connect failed: " << PQerrorMessage(mConn));
178 mConn = 0;
179 setConnected(false);
180 return -1;
181 }
182 else
183 {
184 setConnected(true);
185 return 0;
186 }
187}
188
189inline int pqOK(const PGresult *result)
190{
192 return (t == PGRES_COMMAND_OK || t == PGRES_TUPLES_OK ? 0 : 1);
193}
194
195int
196PostgreSqlDb::query(const Data& queryCommand, PGresult** result) const
197{
198 int rc = 0;
200
201 initialize();
202
203 DebugLog( << "PostgreSqlDb::query: executing query: " << queryCommand);
204
205 Lock lock(mMutex);
206 if(mConn == 0 || !isConnected())
207 {
208 rc = connectToDatabase();
209 }
210 if(rc == 0)
211 {
212 resip_assert(mConn!=0);
213 resip_assert(isConnected());
214 _result = PQexec(mConn, queryCommand.c_str());
215 rc = pqOK(_result);
216 if(rc != 0)
217 {
219 if(rc == PGRES_FATAL_ERROR)
220 {
221 // First failure may be a connection error - try to re-connect and then try again
222 rc = connectToDatabase();
223 if(rc == 0)
224 {
225 // OK - we reconnected - try query again
226 _result = PQexec(mConn,queryCommand.c_str());
227 rc = pqOK(_result);
228 if( rc != 0)
229 {
230 ErrLog( << "PostgreSQL query failed (twice): " << PQerrorMessage(mConn));
231 }
232 }
233 }
234 else
235 {
236 ErrLog( << "PostgreSQL query failed: " << PQerrorMessage(mConn));
237 }
238 }
239 }
240
241 // Now store result - if pointer to result pointer was supplied and no errors
242 if(rc == 0 && result)
243 {
244 *result = _result;
245 }
246
247 if(rc != 0)
248 {
249 ErrLog( << " SQL Command was: " << queryCommand) ;
250 }
251 return rc;
252}
253
254int
255PostgreSqlDb::query(const Data& queryCommand) const
256{
257 return query(queryCommand, 0);
258}
259
260int
261PostgreSqlDb::singleResultQuery(const Data& queryCommand, std::vector<Data>& fields) const
262{
263 StackLog(<<"executing query: " << queryCommand);
264 PGresult* result=0;
265 int rc = query(queryCommand, &result);
266
267 if(rc == 0)
268 {
269 if(result == 0)
270 {
271 ErrLog(<<"singleResultQuery: query failed, rc = " << rc);
272 return rc;
273 }
274
275 if(PQntuples(result) > 0)
276 {
277 for(int i = 0; i < PQnfields(result); i++)
278 {
279 fields.push_back(Data(PQgetvalue(result, 0, i)));
280 }
281 }
282 else
283 {
284 DebugLog(<<"singleResultQuery: no rows returned by query");
285 }
287 }
288 return rc;
289}
290
292PostgreSqlDb::escapeString(const resip::Data& str, resip::Data& escapedStr) const
293{
294 int rc = 0;
295 escapedStr.truncate2(PQescapeStringConn(mConn, (char*)escapedStr.getBuf(str.size()*2+1), str.c_str(), str.size(), &rc));
296 if(rc != 0)
297 {
298 ErrLog(<< "PostgreSQL string escaping failed: " << PQerrorMessage(mConn));
299 // FIXME - should probably throw here. According to the docs, there is a value in
300 // the output buffer even after failure so we'll try to use it and fail later.
301 }
302 return escapedStr;
303}
304
305bool
306PostgreSqlDb::addUser(const AbstractDb::Key& key, const AbstractDb::UserRecord& rec)
307{
308 Data command;
309 {
310 DataStream ds(command);
311 // Use two queries together to simulate UPSERT
312 // Real UPSERT is coming in PostgreSQL 9.5
313 ds << "UPDATE " << tableName(UserTable) << " SET"
314 << " realm='" << rec.realm
315 << "', passwordHash='" << rec.passwordHash
316 << "', passwordHashAlt='" << rec.passwordHashAlt
317 << "', name='" << rec.name
318 << "', email='" << rec.email
319 << "', forwardAddress='" << rec.forwardAddress
320 << "' WHERE username = '" << rec.user
321 << "' AND domain='" << rec.domain
322 << "'; "
323 << "INSERT INTO " << tableName(UserTable) << " (username, domain, realm, passwordHash, passwordHashAlt, name, email, forwardAddress)"
324 << " SELECT '"
325 << rec.user << "', '"
326 << rec.domain << "', '"
327 << rec.realm << "', '"
328 << rec.passwordHash << "', '"
329 << rec.passwordHashAlt << "', '"
330 << rec.name << "', '"
331 << rec.email << "', '"
332 << rec.forwardAddress << "'"
333 << " WHERE NOT EXISTS (SELECT 1 FROM " << tableName(UserTable) << " WHERE "
334 << "username = '" << rec.user << "' AND domain = '" << rec.domain << "')";
335 }
336 return query(command, 0) == 0;
337}
338
339
341PostgreSqlDb::getUser( const AbstractDb::Key& key ) const
342{
344
345 Data command;
346 {
347 DataStream ds(command);
348 ds << "SELECT username, domain, realm, passwordHash, passwordHashAlt, name, email, forwardAddress FROM " << tableName(UserTable) << " ";
349 userWhereClauseToDataStream(key, ds);
350 }
351
352 PGresult* result=0;
353 if(query(command, &result) != 0)
354 {
355 return ret;
356 }
357
358 if (result==0)
359 {
360 ErrLog( << "PostgreSQL failed: " << PQerrorMessage(mConn));
361 return ret;
362 }
363
364 if (PQntuples(result) > 0)
365 {
366 int col = 0;
367 ret.user = Data(PQgetvalue(result, 0, col++));
368 ret.domain = Data(PQgetvalue(result, 0, col++));
369 ret.realm = Data(PQgetvalue(result, 0, col++));
370 ret.passwordHash = Data(PQgetvalue(result, 0, col++));
371 ret.passwordHashAlt = Data(PQgetvalue(result, 0, col++));
372 ret.name = Data(PQgetvalue(result, 0, col++));
373 ret.email = Data(PQgetvalue(result, 0, col++));
374 ret.forwardAddress = Data(PQgetvalue(result, 0, col++));
375 }
376
378
379 return ret;
380}
381
382
384PostgreSqlDb::getUserAuthInfo( const AbstractDb::Key& key ) const
385{
386 std::vector<Data> ret;
387
388 Data command;
389 {
390 DataStream ds(command);
391 Data user;
392 Data domain;
393 UserStore::getUserAndDomainFromKey(key, user, domain);
394 ds << "SELECT passwordHash FROM " << tableName(UserTable) << " WHERE username = '" << user << "' AND domain = '" << domain << "' ";
395
396 // Note: domain is empty when querying for HTTP admin user - for this special user,
397 // we will only check the repro db, by not adding the UNION statement below
398 if(!mCustomUserAuthQuery.empty() && !domain.empty())
399 {
400 ds << " UNION " << mCustomUserAuthQuery;
401 ds.flush();
402 command.replace("$user", user);
403 command.replace("$domain", domain);
404 }
405 }
406
407 if(singleResultQuery(command, ret) != 0 || ret.size() == 0)
408 {
409 return Data::Empty;
410 }
411
412 DebugLog( << "Auth password is " << ret.front());
413
414 return ret.front();
415}
416
417
419PostgreSqlDb::firstUserKey()
420{
421 // free memory from previous search
422 if (mResult[UserTable])
423 {
424 PQclear(mResult[UserTable]);
425 mResult[UserTable] = 0;
426 mRow[UserTable] = 0;
427 }
428
429 Data command;
430 {
431 DataStream ds(command);
432 ds << "SELECT username, domain FROM " << tableName(UserTable);
433 }
434
435 if(query(command, &mResult[UserTable]) != 0)
436 {
437 return Data::Empty;
438 }
439
440 if(mResult[UserTable] == 0)
441 {
442 ErrLog( << "PostgreSQL failed: " << PQerrorMessage(mConn));
443 return Data::Empty;
444 }
445
446 return nextUserKey();
447}
448
449
451PostgreSqlDb::nextUserKey()
452{
453 if(mResult[UserTable] == 0)
454 {
455 return Data::Empty;
456 }
457
458 PGresult *result = mResult[UserTable];
459 if (mRow[UserTable] >= PQntuples(result))
460 {
462 mResult[UserTable] = 0;
463 mRow[UserTable] = 0;
464 return Data::Empty;
465 }
466 Data user(PQgetvalue(result, mRow[UserTable], 0));
467 Data domain(PQgetvalue(result, mRow[UserTable]++, 1));
468
469 return UserStore::buildKey(user, domain);
470}
471
472
473bool
474PostgreSqlDb::addTlsPeerIdentity(const AbstractDb::Key& key, const AbstractDb::TlsPeerIdentityRecord& rec)
475{
476 Data command;
477 {
478 DataStream ds(command);
479 // Use two queries together to simulate UPSERT
480 // Real UPSERT is coming in PostgreSQL 9.5
481 ds /* << "UPDATE " << tableName(TlsPeerIdentityTable) << " SET"
482 << "' foo='" << rec.foo
483 << "' WHERE peerName = '" << rec.peerName
484 << "' AND authorizedIdentity ='" << rec.authorizedIdentity
485 << "'; " */
486 << "INSERT INTO " << tableName(TlsPeerIdentityTable) << " (peerName, authorizedIdentity)"
487 << " SELECT '"
488 << rec.peerName << "', '"
489 << rec.authorizedIdentity << "'"
490 << " WHERE NOT EXISTS (SELECT 1 FROM " << tableName(TlsPeerIdentityTable) << " WHERE "
491 << "peerName = '" << rec.peerName << "' AND authorizedIdentity = '" << rec.authorizedIdentity << "')";
492 }
493 return query(command, 0) == 0;
494}
495
496
498PostgreSqlDb::getTlsPeerIdentity( const AbstractDb::Key& key ) const
499{
501
502 Data command;
503 {
504 DataStream ds(command);
505 ds << "SELECT peerName, authorizedIdentity FROM " << tableName(TlsPeerIdentityTable);
506 tlsPeerIdentityWhereClauseToDataStream(key, ds);
507 }
508
509 PGresult* result=0;
510 if(query(command, &result) != 0)
511 {
512 return ret;
513 }
514
515 if (result==0)
516 {
517 ErrLog( << "PostgreSQL failed: " << PQerrorMessage(mConn));
518 return ret;
519 }
520
521 if (PQntuples(result) > 0)
522 {
523 int col = 0;
524 ret.peerName = Data(PQgetvalue(result, 0, col++));
525 ret.authorizedIdentity = Data(PQgetvalue(result, 0, col++));
526 }
527
529
530 return ret;
531}
532
533
535PostgreSqlDb::firstTlsPeerIdentityKey()
536{
537 // free memory from previous search
538 if (mResult[TlsPeerIdentityTable])
539 {
540 PQclear(mResult[TlsPeerIdentityTable]);
541 mResult[TlsPeerIdentityTable] = 0;
542 mRow[TlsPeerIdentityTable] = 0;
543 }
544
545 Data command;
546 {
547 DataStream ds(command);
548 ds << "SELECT peerName, authorizedIdentity FROM " << tableName(TlsPeerIdentityTable);
549 }
550
551 if(query(command, &mResult[TlsPeerIdentityTable]) != 0)
552 {
553 return Data::Empty;
554 }
555
556 if(mResult[TlsPeerIdentityTable] == 0)
557 {
558 ErrLog( << "PostgreSQL failed: " << PQerrorMessage(mConn));
559 return Data::Empty;
560 }
561
562 return nextTlsPeerIdentityKey();
563}
564
565
567PostgreSqlDb::nextTlsPeerIdentityKey()
568{
569 if(mResult[TlsPeerIdentityTable] == 0)
570 {
571 return Data::Empty;
572 }
573
574 PGresult *result = mResult[TlsPeerIdentityTable];
575 if (mRow[TlsPeerIdentityTable] >= PQntuples(result))
576 {
578 mResult[TlsPeerIdentityTable] = 0;
579 mRow[TlsPeerIdentityTable] = 0;
580 return Data::Empty;
581 }
582 Data peerName(PQgetvalue(result, mRow[TlsPeerIdentityTable], 0));
583 Data authorizedIdentity(PQgetvalue(result, mRow[TlsPeerIdentityTable]++, 1));
584
585 return TlsPeerIdentityStore::buildKey(peerName, authorizedIdentity);
586}
587
588
589bool
590PostgreSqlDb::dbWriteRecord(const Table table,
591 const resip::Data& pKey,
592 const resip::Data& pData)
593{
594 Data command;
595
596 // Check if there is a secondary key or not and get it's value
597 char* secondaryKey;
598 unsigned int secondaryKeyLen;
600 if(AbstractDb::getSecondaryKey(table, pKey, pData, (void**)&secondaryKey, &secondaryKeyLen) == 0)
601 {
603 Data sKey(Data::Share, secondaryKey, secondaryKeyLen);
604 DataStream ds(command);
605 ds << "DELETE FROM " << tableName(table)
606 << " WHERE attr='" << escapeString(pKey, escapedKey)
607 << "' AND attr2='" << escapeString(sKey, escapedSKey)
608 << "';"
609 << " INSERT INTO " << tableName(table)
610 << " (attr, attr2, value) VALUES ("
611 << "'" << escapeString(pKey, escapedKey)
612 << "', '" << escapeString(sKey, escapedSKey)
613 << "', '" << pData.base64encode()
614 << "')";
615 }
616 else
617 {
618 DataStream ds(command);
619 ds << "DELETE FROM " << tableName(table)
620 << " WHERE attr='" << escapeString(pKey, escapedKey)
621 << "';"
622 << " INSERT INTO " << tableName(table)
623 << " (attr, value) VALUES ("
624 << "'" << escapeString(pKey, escapedKey)
625 << "', '" << pData.base64encode()
626 << "')";
627 }
628
629 return query(command, 0) == 0;
630}
631
632bool
633PostgreSqlDb::dbReadRecord(const Table table,
634 const resip::Data& pKey,
635 resip::Data& pData) const
636{
637 Data command;
639 {
640 DataStream ds(command);
641 ds << "SELECT value FROM " << tableName(table)
642 << " WHERE attr='" << escapeString(pKey, escapedKey)
643 << "'";
644 }
645
646 PGresult* result = 0;
647 if(query(command, &result) != 0)
648 {
649 return false;
650 }
651
652 if (result == 0)
653 {
654 ErrLog( << "PostgreSQL result failed: " << PQerrorMessage(mConn));
655 return false;
656 }
657 else
658 {
659 bool success = false;
660 if(PQntuples(result) > 0)
661 {
662 char *value = PQgetvalue(result, 0, 0);
663 pData = Data(Data::Share, value, (Data::size_type)strlen(value)).base64decode();
664 success = true;
665 }
667 StackLog(<<"query result: " << success);
668 return success;
669 }
670}
671
672
674PostgreSqlDb::dbNextKey(const Table table, bool first)
675{
676 if(first)
677 {
678 // free memory from previous search
679 if (mResult[table])
680 {
681 PQclear(mResult[table]);
682 mResult[table] = 0;
683 mRow[table] = 0;
684 }
685
686 Data command;
687 {
688 DataStream ds(command);
689 ds << "SELECT attr FROM " << tableName(table);
690 }
691
692 if(query(command, &mResult[table]) != 0)
693 {
694 return Data::Empty;
695 }
696
697 if (mResult[table] == 0)
698 {
699 ErrLog( << "PostgreSQL failed: " << PQerrorMessage(mConn));
700 return Data::Empty;
701 }
702 }
703 else
704 {
705 if (mResult[table] == 0)
706 {
707 return Data::Empty;
708 }
709 }
710
711 PGresult *result = mResult[table];
712 if (mRow[table] >= PQntuples(result))
713 {
715 mResult[table] = 0;
716 return Data::Empty;
717 }
718
719 return Data(PQgetvalue(result, mRow[table]++, 0));
720}
721
722
723bool
724PostgreSqlDb::dbNextRecord(const Table table,
725 const resip::Data& key,
726 resip::Data& data,
727 bool forUpdate, // specifying to add SELECT ... FOR UPDATE so the rows are locked
728 bool first) // return false if no more
729{
730 if(first)
731 {
732 // free memory from previous search
733 if (mResult[table])
734 {
735 PQclear(mResult[table]);
736 mResult[table] = 0;
737 mRow[table] = 0;
738 }
739
740 Data command;
741 {
742 DataStream ds(command);
743 ds << "SELECT value FROM " << tableName(table);
744 if(!key.empty())
745 {
747 // dbNextRecord is used to iterator through database tables that support duplication records
748 // it is only appropriate for PostgreSQL tables that contain the attr2 non-unique index (secondary key)
749 ds << " WHERE attr2='" << escapeString(key, escapedKey) << "'";
750 }
751 if(forUpdate)
752 {
753 ds << " FOR UPDATE";
754 }
755 }
756
757 if(query(command, &mResult[table]) != 0)
758 {
759 return false;
760 }
761
762 if (mResult[table] == 0)
763 {
764 ErrLog( << "PostgreSQL failed: " << PQerrorMessage(mConn));
765 return false;
766 }
767 }
768
769 if (mResult[table] == 0)
770 {
771 return false;
772 }
773
774 PGresult *result = mResult[table];
775 if (mRow[table] >= PQntuples(result))
776 {
778 mResult[table] = 0;
779 return false;
780 }
781
782 char *s = PQgetvalue(result, mRow[table]++, 0);
783 data = Data(Data::Share, s, (Data::size_type)strlen(s)).base64decode();
784
785 return true;
786}
787
788bool
789PostgreSqlDb::dbBeginTransaction(const Table table)
790{
791 Data command("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ");
792 if(query(command, 0))
793 {
794 command = "BEGIN";
795 return query(command, 0);
796 }
797 return false;
798}
799
800void
801PostgreSqlDb::userWhereClauseToDataStream(const Key& key, DataStream& ds) const
802{
803 Data user;
804 Data domain;
805 UserStore::getUserAndDomainFromKey(key, user, domain);
806 ds << " WHERE username='" << user
807 << "' AND domain='" << domain
808 << "'";
809}
810
811void
812PostgreSqlDb::tlsPeerIdentityWhereClauseToDataStream(const Key& key, DataStream& ds) const
813{
814 Data peerName;
815 Data authorizedIdentity;
816 TlsPeerIdentityStore::getTlsPeerIdentityFromKey(key, peerName, authorizedIdentity);
817 ds << " WHERE peerName='" << peerName
818 << "' AND authorizedIdentity='" << authorizedIdentity
819 << "'";
820}
821
822#endif // USE_POSTGRESQL
823
824/* ====================================================================
825 *
826 * Copyright (c) 2015 Daniel Pocock. All rights reserved.
827 *
828 * Redistribution and use in source and binary forms, with or without
829 * modification, are permitted provided that the following conditions
830 * are met:
831 *
832 * 1. Redistributions of source code must retain the above copyright
833 * notice, this list of conditions and the following disclaimer.
834 *
835 * 2. Redistributions in binary form must reproduce the above copyright
836 * notice, this list of conditions and the following disclaimer in
837 * the documentation and/or other materials provided with the
838 * distribution.
839 *
840 * 3. Neither the name of the author(s) nor the names of any contributors
841 * may be used to endorse or promote products derived from this software
842 * without specific prior written permission.
843 *
844 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS "AS IS" AND
845 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
846 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
847 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
848 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
849 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
850 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
851 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
852 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
853 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
854 * SUCH DAMAGE.
855 *
856 * ====================================================================
857 */
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
An iostream that operates on an existing Data.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
Data base64decode() const
Performs RFC 3548 Base 64 decoding of the contents of this data.
Definition Data.cxx:2607
int replace(const Data &match, const Data &target, int max=INT_MAX)
Replaces up to max occurrences of the bytes match with target.
Definition Data.cxx:1964
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_t size_type
Definition Data.hxx:94
Data & truncate2(size_type len)
Shortens the size of this Data so length is at most of {len}.
Definition Data.cxx:940
pthread_key_t TlsKey
Definition ThreadIf.hxx:85
This Class is used to implement a primitive form of RPC using loose XML formatting.
dcm! – add UnusedChecking(_enum) below;
std::unique_lock< Mutex > Lock
A convenience class to lock a Lockable (such as a Mutex) on object creation, and unlock on destructio...
Definition Lock.hxx:28
unsigned int uint32_t
Definition stdint.h:85
void list()
Definition userAdmin.cxx:66