Files
znc/src/Utils.cpp
2014-12-31 11:28:38 +01:00

840 lines
21 KiB
C++

/*
* Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <znc/Utils.h>
#include <znc/ZNCDebug.h>
#include <znc/FileUtils.h>
#ifdef HAVE_LIBSSL
#include <openssl/ssl.h>
#endif /* HAVE_LIBSSL */
#include <unistd.h>
#ifdef HAVE_ICU
#include <unicode/ucnv.h>
#include <unicode/errorcode.h>
#endif
// Required with GCC 4.3+ if openssl is disabled
#include <cstring>
#include <cstdlib>
#include <iomanip>
using std::map;
using std::vector;
CUtils::CUtils() {}
CUtils::~CUtils() {}
#ifdef HAVE_LIBSSL
// Generated by "openssl dhparam 2048"
constexpr const char* szDefaultDH2048 =
"-----BEGIN DH PARAMETERS-----\n"
"MIIBCAKCAQEAtS/K3TMY8IHzcCATQSjUF3rDidjDDQmT+mLxyxRORmzMPjFIFkKH\n"
"MOmxZvyCBArdaoCCEBBOzrldl/bBLn5TOeZb+MW7mpBLANTuQSOu97DDM7EzbnqC\n"
"b6z3QgixZ2+UqxdmQAu4nBPLFwym6W/XPFEHpz6iHISSvjzzo4cfI0xwWTcoAvFQ\n"
"r/ZU5BXSXp7XuDxSyyAqaaKUxquElf+x56QWrpNJypjzPpslg5ViAKwWQS0TnCrU\n"
"sVuhFtbNlZjqW1tMSBxiWFltS1HoEaaI79MEpf1Ps25OrQl8xqqCGKkZcHlNo4oF\n"
"cvUyzAEcCQYHmiYjp2hoZbSa8b690TQaAwIBAg==\n"
"-----END DH PARAMETERS-----\n";
void CUtils::GenerateCert(FILE *pOut, const CString& sHost) {
EVP_PKEY *pKey = NULL;
X509 *pCert = NULL;
X509_NAME *pName = NULL;
const int days = 365;
const int years = 10;
unsigned int uSeed = (unsigned int)time(NULL);
int serial = (rand_r(&uSeed) % 9999);
RSA *pRSA = RSA_generate_key(2048, 0x10001, NULL, NULL);
if ((pKey = EVP_PKEY_new())) {
if (!EVP_PKEY_assign_RSA(pKey, pRSA)) {
EVP_PKEY_free(pKey);
return;
}
PEM_write_RSAPrivateKey(pOut, pRSA, NULL, NULL, 0, NULL, NULL);
if (!(pCert = X509_new())) {
EVP_PKEY_free(pKey);
return;
}
X509_set_version(pCert, 2);
ASN1_INTEGER_set(X509_get_serialNumber(pCert), serial);
X509_gmtime_adj(X509_get_notBefore(pCert), 0);
X509_gmtime_adj(X509_get_notAfter(pCert), (long)60*60*24*days*years);
X509_set_pubkey(pCert, pKey);
pName = X509_get_subject_name(pCert);
const char *pLogName = getenv("LOGNAME");
const char *pHostName = NULL;
if (!sHost.empty()) {
pHostName = sHost.c_str();
}
if (!pHostName) {
pHostName = getenv("HOSTNAME");
}
if (!pLogName) {
pLogName = "Unknown";
}
if (!pHostName) {
pHostName = "host.unknown";
}
CString sEmailAddr = pLogName;
sEmailAddr += "@";
sEmailAddr += pHostName;
X509_NAME_add_entry_by_txt(pName, "OU", MBSTRING_ASC, (unsigned char *)pLogName, -1, -1, 0);
X509_NAME_add_entry_by_txt(pName, "CN", MBSTRING_ASC, (unsigned char *)pHostName, -1, -1, 0);
X509_NAME_add_entry_by_txt(pName, "emailAddress", MBSTRING_ASC, (unsigned char *)sEmailAddr.c_str(), -1, -1, 0);
X509_set_subject_name(pCert, pName);
X509_set_issuer_name(pCert, pName);
if (!X509_sign(pCert, pKey, EVP_sha256())) {
X509_free(pCert);
EVP_PKEY_free(pKey);
return;
}
PEM_write_X509(pOut, pCert);
X509_free(pCert);
EVP_PKEY_free(pKey);
fprintf(pOut, "%s", szDefaultDH2048);
}
}
#endif /* HAVE_LIBSSL */
CString CUtils::GetIP(unsigned long addr) {
char szBuf[16];
memset((char*) szBuf, 0, 16);
if (addr >= (1 << 24)) {
unsigned long ip[4];
ip[0] = addr >> 24 & 255;
ip[1] = addr >> 16 & 255;
ip[2] = addr >> 8 & 255;
ip[3] = addr & 255;
sprintf(szBuf, "%lu.%lu.%lu.%lu", ip[0], ip[1], ip[2], ip[3]);
}
return szBuf;
}
unsigned long CUtils::GetLongIP(const CString& sIP) {
unsigned long ret;
char ip[4][4];
unsigned int i;
i = sscanf(sIP.c_str(), "%3[0-9].%3[0-9].%3[0-9].%3[0-9]",
ip[0], ip[1], ip[2], ip[3]);
if (i != 4)
return 0;
// Beware that atoi("200") << 24 would overflow and turn negative!
ret = atol(ip[0]) << 24;
ret += atol(ip[1]) << 16;
ret += atol(ip[2]) << 8;
ret += atol(ip[3]) << 0;
return ret;
}
// If you change this here and in GetSaltedHashPass(),
// don't forget CUser::HASH_DEFAULT!
// TODO refactor this
const CString CUtils::sDefaultHash = "sha256";
CString CUtils::GetSaltedHashPass(CString& sSalt) {
sSalt = GetSalt();
while (true) {
CString pass1;
do {
pass1 = CUtils::GetPass("Enter password");
} while (pass1.empty());
CString pass2 = CUtils::GetPass("Confirm password");
if (!pass1.Equals(pass2, true)) {
CUtils::PrintError("The supplied passwords did not match");
} else {
// Construct the salted pass
return SaltedSHA256Hash(pass1, sSalt);
}
}
}
CString CUtils::GetSalt() {
return CString::RandomString(20);
}
CString CUtils::SaltedMD5Hash(const CString& sPass, const CString& sSalt) {
return CString(sPass + sSalt).MD5();
}
CString CUtils::SaltedSHA256Hash(const CString& sPass, const CString& sSalt) {
return CString(sPass + sSalt).SHA256();
}
CString CUtils::GetPass(const CString& sPrompt) {
PrintPrompt(sPrompt);
#ifdef HAVE_GETPASSPHRASE
return getpassphrase("");
#else
return getpass("");
#endif
}
bool CUtils::GetBoolInput(const CString& sPrompt, bool bDefault) {
return CUtils::GetBoolInput(sPrompt, &bDefault);
}
bool CUtils::GetBoolInput(const CString& sPrompt, bool *pbDefault) {
CString sRet, sDefault;
if (pbDefault) {
sDefault = (*pbDefault) ? "yes" : "no";
}
while (true) {
GetInput(sPrompt, sRet, sDefault, "yes/no");
if (sRet.Equals("y") || sRet.Equals("yes")) {
return true;
} else if (sRet.Equals("n") || sRet.Equals("no")) {
return false;
}
}
}
bool CUtils::GetNumInput(const CString& sPrompt, unsigned int& uRet, unsigned int uMin, unsigned int uMax, unsigned int uDefault) {
if (uMin > uMax) {
return false;
}
CString sDefault = (uDefault != (unsigned int) ~0) ? CString(uDefault) : "";
CString sNum, sHint;
if (uMax != (unsigned int) ~0) {
sHint = CString(uMin) + " to " + CString(uMax);
} else if (uMin > 0) {
sHint = CString(uMin) + " and up";
}
while (true) {
GetInput(sPrompt, sNum, sDefault, sHint);
if (sNum.empty()) {
return false;
}
uRet = sNum.ToUInt();
if ((uRet >= uMin && uRet <= uMax)) {
break;
}
CUtils::PrintError("Number must be " + sHint);
}
return true;
}
bool CUtils::GetInput(const CString& sPrompt, CString& sRet, const CString& sDefault, const CString& sHint) {
CString sExtra;
CString sInput;
sExtra += (!sHint.empty()) ? (" (" + sHint + ")") : "";
sExtra += (!sDefault.empty()) ? (" [" + sDefault + "]") : "";
PrintPrompt(sPrompt + sExtra);
char szBuf[1024];
memset(szBuf, 0, 1024);
if (fgets(szBuf, 1024, stdin) == NULL) {
// Reading failed (Error? EOF?)
PrintError("Error while reading from stdin. Exiting...");
exit(-1);
}
sInput = szBuf;
if (sInput.Right(1) == "\n") {
sInput.RightChomp();
}
if (sInput.empty()) {
sRet = sDefault;
} else {
sRet = sInput;
}
return !sRet.empty();
}
#define BOLD "\033[1m"
#define NORM "\033[22m"
#define RED "\033[31m"
#define GRN "\033[32m"
#define YEL "\033[33m"
#define BLU "\033[34m"
#define DFL "\033[39m"
void CUtils::PrintError(const CString& sMessage) {
if (CDebug::StdoutIsTTY())
fprintf(stdout, BOLD BLU "[" RED " ** " BLU "]" DFL NORM" %s\n", sMessage.c_str());
else
fprintf(stdout, "%s\n", sMessage.c_str());
fflush(stdout);
}
void CUtils::PrintPrompt(const CString& sMessage) {
if (CDebug::StdoutIsTTY())
fprintf(stdout, BOLD BLU "[" YEL " ?? " BLU "]" DFL NORM " %s: ", sMessage.c_str());
else
fprintf(stdout, "[ ?? ] %s: ", sMessage.c_str());
fflush(stdout);
}
void CUtils::PrintMessage(const CString& sMessage, bool bStrong) {
if (CDebug::StdoutIsTTY()) {
if (bStrong)
fprintf(stdout, BOLD BLU "[" YEL " ** " BLU "]" DFL BOLD " %s" NORM "\n",
sMessage.c_str());
else
fprintf(stdout, BOLD BLU "[" YEL " ** " BLU "]" DFL NORM " %s\n",
sMessage.c_str());
} else
fprintf(stdout, "%s\n", sMessage.c_str());
fflush(stdout);
}
void CUtils::PrintAction(const CString& sMessage) {
if (CDebug::StdoutIsTTY())
fprintf(stdout, BOLD BLU "[ .. " BLU "]" DFL NORM " %s...\n", sMessage.c_str());
else
fprintf(stdout, "%s... ", sMessage.c_str());
fflush(stdout);
}
void CUtils::PrintStatus(bool bSuccess, const CString& sMessage) {
if (CDebug::StdoutIsTTY()) {
if (bSuccess) {
fprintf(stdout, BOLD BLU "[" GRN " >> " BLU "]" DFL NORM);
fprintf(stdout, " %s\n", sMessage.empty() ? "ok" : sMessage.c_str());
} else {
fprintf(stdout, BOLD BLU "[" RED " !! " BLU "]" DFL NORM);
fprintf(stdout, BOLD RED " %s" DFL NORM "\n", sMessage.empty() ? "failed" : sMessage.c_str());
}
} else {
if (bSuccess) {
fprintf(stdout, "%s\n", sMessage.c_str());
} else {
if (!sMessage.empty()) {
fprintf(stdout, "[ %s ]", sMessage.c_str());
}
fprintf(stdout, "\n");
}
}
fflush(stdout);
}
namespace {
/* Switch GMT-X and GMT+X
*
* See https://en.wikipedia.org/wiki/Tz_database#Area
*
* "In order to conform with the POSIX style, those zone names beginning
* with "Etc/GMT" have their sign reversed from what most people expect.
* In this style, zones west of GMT have a positive sign and those east
* have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours
* ahead/east of GMT.)"
*/
inline CString FixGMT(CString sTZ) {
if (sTZ.length() >= 4 && sTZ.Left(3) == "GMT") {
if (sTZ[3] == '+') {
sTZ[3] = '-';
} else if (sTZ[3] == '-') {
sTZ[3] = '+';
}
}
return sTZ;
}
}
CString CUtils::CTime(time_t t, const CString& sTimezone) {
char s[30] = {}; // should have at least 26 bytes
if (sTimezone.empty()) {
ctime_r(&t, s);
// ctime() adds a trailing newline
return CString(s).Trim_n();
}
CString sTZ = FixGMT(sTimezone);
// backup old value
char* oldTZ = getenv("TZ");
if (oldTZ) oldTZ = strdup(oldTZ);
setenv("TZ", sTZ.c_str(), 1);
tzset();
ctime_r(&t, s);
// restore old value
if (oldTZ) {
setenv("TZ", oldTZ, 1);
free(oldTZ);
} else {
unsetenv("TZ");
}
tzset();
return CString(s).Trim_n();
}
CString CUtils::FormatTime(time_t t, const CString& sFormat, const CString& sTimezone) {
char s[1024] = {};
tm m;
if (sTimezone.empty()) {
localtime_r(&t, &m);
strftime(s, sizeof(s), sFormat.c_str(), &m);
return s;
}
CString sTZ = FixGMT(sTimezone);
// backup old value
char* oldTZ = getenv("TZ");
if (oldTZ) oldTZ = strdup(oldTZ);
setenv("TZ", sTZ.c_str(), 1);
tzset();
localtime_r(&t, &m);
strftime(s, sizeof(s), sFormat.c_str(), &m);
// restore old value
if (oldTZ) {
setenv("TZ", oldTZ, 1);
free(oldTZ);
} else {
unsetenv("TZ");
}
tzset();
return s;
}
CString CUtils::FormatServerTime(const timeval& tv) {
CString s_msec(tv.tv_usec / 1000);
while (s_msec.length() < 3) {
s_msec = "0" + s_msec;
}
// TODO support leap seconds properly
// TODO support message-tags properly
struct tm stm;
memset(&stm, 0, sizeof(stm));
const time_t secs = tv.tv_sec; // OpenBSD has tv_sec as int, so explicitly convert it to time_t to make gmtime_r() happy
gmtime_r(&secs, &stm);
char sTime[20] = {};
strftime(sTime, sizeof(sTime), "%Y-%m-%dT%H:%M:%S", &stm);
return CString(sTime) + "." + s_msec + "Z";
}
namespace {
void FillTimezones(const CString& sPath, SCString& result, const CString& sPrefix) {
CDir Dir;
Dir.Fill(sPath);
for (unsigned int a = 0; a < Dir.size(); ++a) {
CFile& File = *Dir[a];
CString sName = File.GetShortName();
CString sFile = File.GetLongName();
if (sName == "posix" || sName == "right") continue; // these 2 dirs contain the same filenames
if (sName.Right(4) == ".tab" || sName == "posixrules" || sName == "localtime") continue;
if (File.IsDir()) {
if (sName == "Etc") {
FillTimezones(sFile, result, sPrefix);
} else {
FillTimezones(sFile, result, sPrefix + sName + "/");
}
} else {
result.insert(sPrefix + sName);
}
}
}
}
SCString CUtils::GetTimezones() {
static SCString result;
if (result.empty()) {
FillTimezones("/usr/share/zoneinfo", result, "");
}
return result;
}
SCString CUtils::GetEncodings() {
static SCString ssResult;
#ifdef HAVE_ICU
if (ssResult.empty()) {
for (int i = 0; i < ucnv_countAvailable(); ++i) {
const char* pConvName = ucnv_getAvailableName(i);
ssResult.insert(pConvName);
icu::ErrorCode e;
for (int st = 0; st < ucnv_countStandards(); ++st) {
const char* pStdName = ucnv_getStandard(st, e);
icu::LocalUEnumerationPointer ue(ucnv_openStandardNames(pConvName, pStdName, e));
while (const char* pStdConvNameEnum = uenum_next(ue.getAlias(), NULL, e)) {
ssResult.insert(pStdConvNameEnum);
}
}
}
}
#endif
return ssResult;
}
MCString CUtils::GetMessageTags(const CString& sLine) {
if (sLine.StartsWith("@")) {
VCString vsTags;
sLine.Token(0).TrimPrefix_n("@").Split(";", vsTags, false);
MCString mssTags;
for (VCString::const_iterator it = vsTags.begin(); it != vsTags.end(); ++it) {
CString sKey = it->Token(0, false, "=", true);
CString sValue = it->Token(1, true, "=", true);
mssTags[sKey] = sValue.Escape(CString::EMSGTAG, CString::CString::EASCII);
}
return mssTags;
}
return MCString::EmptyMap;
}
void CUtils::SetMessageTags(CString& sLine, const MCString& mssTags) {
if (sLine.StartsWith("@")) {
sLine.LeftChomp(sLine.Token(0).length() + 1);
}
if (!mssTags.empty()) {
CString sTags;
for (MCString::const_iterator it = mssTags.begin(); it != mssTags.end(); ++it) {
if (!sTags.empty()) {
sTags += ";";
}
sTags += it->first;
if (!it->second.empty())
sTags += "=" + it->second.Escape_n(CString::EMSGTAG);
}
sLine = "@" + sTags + " " + sLine;
}
}
bool CTable::AddColumn(const CString& sName, bool bWrappable) {
for (unsigned int a = 0; a < m_vsHeaders.size(); a++) {
if (m_vsHeaders[a].Equals(sName)) {
return false;
}
}
m_vsHeaders.push_back(sName);
m_vuMaxWidths.push_back(sName.size());
// TODO: Maybe headers can be wrapped too?
m_vuMinWidths.push_back(sName.size());
m_vbWrappable.push_back(bWrappable);
return true;
}
CTable::size_type CTable::AddRow() {
// Don't add a row if no headers are defined
if (m_vsHeaders.empty()) {
return (size_type) -1;
}
// Add a vector with enough space for each column
push_back(vector<CString>(m_vsHeaders.size()));
return size() - 1;
}
bool CTable::SetCell(const CString& sColumn, const CString& sValue, size_type uRowIdx) {
if (uRowIdx == (size_type) ~0) {
if (empty()) {
return false;
}
uRowIdx = size() -1;
}
unsigned int uColIdx = GetColumnIndex(sColumn);
if (uColIdx == (unsigned int) -1)
return false;
(*this)[uRowIdx][uColIdx] = sValue;
if (sValue.length() > m_vuMaxWidths[uColIdx]) {
m_vuMaxWidths[uColIdx] = sValue.length();
}
if (m_vbWrappable[uColIdx]) {
VCString vsWords;
sValue.Split(" ", vsWords);
size_type uMaxWord = 0;
for (const CString& sWord : vsWords) {
if (sWord.length() > uMaxWord) {
uMaxWord = sWord.length();
}
}
// We can't shrink column further than the longest word in it
if (uMaxWord > m_vuMinWidths[uColIdx]) {
m_vuMinWidths[uColIdx] = uMaxWord;
}
} else {
m_vuMinWidths[uColIdx] = m_vuMaxWidths[uColIdx];
}
return true;
}
bool CTable::GetLine(unsigned int uIdx, CString& sLine) const {
if (empty()) {
return false;
}
if (m_vsOutput.empty()) {
m_vsOutput = Render();
}
if (uIdx >= m_vsOutput.size()) {
return false;
}
sLine = m_vsOutput[uIdx];
return true;
}
VCString CTable::Render() const {
size_type uTotalWidth = 1; // '|'
for (size_type uWidth : m_vuMaxWidths) {
uTotalWidth += uWidth + 3; // '|', ' 'x2
}
std::vector<size_type> vuWidth = m_vuMaxWidths;
std::map<int, int> miColumnSpace;
for (unsigned int i = 0; i < m_vsHeaders.size(); ++i) {
int iSpace = m_vuMaxWidths[i] - m_vuMinWidths[i];
if (iSpace > 0) {
miColumnSpace[i] = iSpace;
}
}
// Not very efficient algorithm, and doesn't produce very good results...
while (uTotalWidth > m_uPreferredWidth) {
std::vector<int> viToErase;
for (auto& i : miColumnSpace) {
uTotalWidth--;
i.second--;
vuWidth[i.first]--;
if (i.second == 0) {
viToErase.push_back(i.first);
}
if (uTotalWidth == m_uPreferredWidth) {
break;
}
}
for (int iCol : viToErase) {
miColumnSpace.erase(iCol);
}
if (miColumnSpace.empty()) {
// Every column is at its minimum width now, but total width is still more than preferred width
break;
}
}
CString sHorizontal;
{
std::ostringstream ssLine;
ssLine << std::setfill('-');
ssLine << "+";
for (size_type uWidth : vuWidth) {
ssLine << std::setw(uWidth + 2) << std::left << "-";
ssLine << "+";
}
sHorizontal = ssLine.str();
}
VCString vsOutput;
vsOutput.emplace_back(sHorizontal.Replace_n("-", "="));
{
std::ostringstream ssLine;
ssLine << "|";
for (unsigned int iCol = 0; iCol < vuWidth.size(); ++iCol) {
ssLine << " ";
ssLine << std::setw(vuWidth[iCol]) << std::left;
ssLine << m_vsHeaders[iCol] << " |";
}
vsOutput.emplace_back(ssLine.str());
}
vsOutput.emplace_back(vsOutput[0]);
for (const VCString& vsRow : *this) {
// Wrap words
std::vector<VCString> vvsColumns;
vvsColumns.reserve(m_vsHeaders.size());
unsigned int uRowNum = 1;
for (unsigned int iCol = 0; iCol < vuWidth.size(); ++iCol) {
if (m_vbWrappable[iCol]) {
vvsColumns.emplace_back(WrapWords(vsRow[iCol], vuWidth[iCol]));
} else {
vvsColumns.push_back({vsRow[iCol]});
}
if (vvsColumns.back().size() > uRowNum) {
uRowNum = vvsColumns.back().size();
}
}
CString sEmpty;
for (size_type uCurrentLine = 0; uCurrentLine < uRowNum; ++uCurrentLine) {
std::ostringstream ssLine;
ssLine << "|";
for (unsigned int iCol = 0; iCol < vvsColumns.size(); ++iCol) {
const CString& sData = uCurrentLine < vvsColumns[iCol].size() ? vvsColumns[iCol][uCurrentLine] : sEmpty;
ssLine << " ";
ssLine << std::setw(vuWidth[iCol]) << std::left;
ssLine << sData << " |";
}
vsOutput.emplace_back(ssLine.str());
}
vsOutput.emplace_back(sHorizontal);
}
vsOutput.pop_back();
vsOutput.emplace_back(vsOutput[0]);
return vsOutput;
}
VCString CTable::WrapWords(const CString& s, size_type uWidth) {
VCString vsWords;
s.Split(" ", vsWords);
VCString vsResult;
vsResult.emplace_back("");
for (const CString& sWord : vsWords) {
size_type uOldLen = vsResult.back().length();
if (uOldLen != 0) {
uOldLen++; // ' '
}
if (uOldLen + sWord.length() > uWidth) {
vsResult.emplace_back(sWord);
} else {
if (uOldLen != 0) {
vsResult.back() += " ";
}
vsResult.back() += sWord;
}
}
return vsResult;
}
unsigned int CTable::GetColumnIndex(const CString& sName) const {
for (unsigned int i = 0; i < m_vsHeaders.size(); i++) {
if (m_vsHeaders[i] == sName)
return i;
}
DEBUG("CTable::GetColumnIndex(" + sName + ") failed");
return (unsigned int) -1;
}
CString::size_type CTable::GetColumnWidth(unsigned int uIdx) const {
if (uIdx >= m_vsHeaders.size()) {
return 0;
}
return m_vuMaxWidths[uIdx];
}
void CTable::Clear() {
clear();
m_vsHeaders.clear();
m_vuMaxWidths.clear();
m_vuMinWidths.clear();
m_vbWrappable.clear();
m_vsOutput.clear();
}
#ifdef HAVE_LIBSSL
CBlowfish::CBlowfish(const CString & sPassword, int iEncrypt, const CString & sIvec) {
m_iEncrypt = iEncrypt;
m_ivec = (unsigned char *)calloc(sizeof(unsigned char), 8);
m_num = 0;
if (sIvec.length() >= 8) {
memcpy(m_ivec, sIvec.data(), 8);
}
BF_set_key(&m_bkey, (unsigned int)sPassword.length(), (unsigned char *)sPassword.data());
}
CBlowfish::~CBlowfish() {
free(m_ivec);
}
//! output must be freed
unsigned char *CBlowfish::MD5(const unsigned char *input, u_int ilen) {
unsigned char *output = (unsigned char *)malloc(MD5_DIGEST_LENGTH);
::MD5(input, ilen, output);
return output;
}
//! returns an md5 of the CString (not hex encoded)
CString CBlowfish::MD5(const CString & sInput, bool bHexEncode) {
CString sRet;
unsigned char *data = MD5((const unsigned char *)sInput.data(), (unsigned int)sInput.length());
if (!bHexEncode) {
sRet.append((const char *)data, MD5_DIGEST_LENGTH);
} else {
for (int a = 0; a < MD5_DIGEST_LENGTH; a++) {
sRet += g_HexDigits[data[a] >> 4];
sRet += g_HexDigits[data[a] & 0xf];
}
}
free(data);
return sRet;
}
//! output must be the same size as input
void CBlowfish::Crypt(unsigned char *input, unsigned char *output, u_int uBytes) {
BF_cfb64_encrypt(input, output, uBytes, &m_bkey, m_ivec, &m_num, m_iEncrypt);
}
//! must free result
unsigned char * CBlowfish::Crypt(unsigned char *input, u_int uBytes) {
unsigned char *buff = (unsigned char *)malloc(uBytes);
Crypt(input, buff, uBytes);
return buff;
}
CString CBlowfish::Crypt(const CString & sData) {
unsigned char *buff = Crypt((unsigned char *)sData.data(), (unsigned int)sData.length());
CString sOutput;
sOutput.append((const char *)buff, sData.length());
free(buff);
return sOutput;
}
#endif // HAVE_LIBSSL