/*
Miranda Crash Dumper Plugin
Copyright (C) 2008 - 2012 Boris Krasnovskiy All Rights Reserved
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "utils.h"
HANDLE hNetlibUser;
static void arrayToHex(BYTE* data, size_t datasz, char* res)
{
char* resptr = res;
for (unsigned i=0; i> 4);
*resptr++ = (char)((ch0 <= 9) ? ('0' + ch0) : (('a' - 10) + ch0));
const char ch1 = (char)(ch & 0xF);
*resptr++ = (char)((ch1 <= 9) ? ('0' + ch1) : (('a' - 10) + ch1));
}
*resptr = '\0';
}
void GetLoginStr(char* user, size_t szuser, char* pass)
{
DBVARIANT dbv;
if (db_get_s(NULL, PluginName, "Username", &dbv) == 0)
{
mir_snprintf(user, szuser, "%s", dbv.pszVal);
db_free(&dbv);
}
else
user[0] = 0;
if (db_get_s(NULL, PluginName, "Password", &dbv) == 0)
{
CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal)+1, (LPARAM)dbv.pszVal);
mir_md5_byte_t hash[16];
mir_md5_state_t context;
mir_md5_init(&context);
mir_md5_append(&context, (BYTE*)dbv.pszVal, (int)strlen(dbv.pszVal));
mir_md5_finish(&context, hash);
arrayToHex(hash, sizeof(hash), pass);
db_free(&dbv);
}
else
pass[0] = 0;
}
void OpenAuthUrl(const char* url)
{
char user[64], pass[40];
GetLoginStr(user, sizeof(user), pass);
if (user[0] && pass[0]) {
char str[256];
mir_snprintf(str, sizeof(str), url, user);
mir_snprintf(str, sizeof(str), "http://www.miranda-vi.org/cdlogin?name=%s&pass=%s&redir=%s", user, pass, mir_ptr( mir_urlEncode(str)));
CallService(MS_UTILS_OPENURL, OUF_NEWWINDOW, (LPARAM)str);
}
else CallService(MS_UTILS_OPENURL, OUF_NEWWINDOW, (LPARAM)"http://www.miranda-vi.org/");
}
void CreateAuthString(char* auth)
{
char user[64], pass[40];
GetLoginStr(user, sizeof(user), pass);
char str[110];
int len = mir_snprintf(str, sizeof(str), "%s:%s", user, pass);
strcpy(auth, "Basic ");
NETLIBBASE64 nlb = { auth+6, 250, (PBYTE)str, len };
CallService(MS_NETLIB_BASE64ENCODE, 0, LPARAM(&nlb));
}
bool InternetDownloadFile(const char *szUrl, VerTrnsfr* szReq)
{
int result = 0xBADBAD;
char* szRedirUrl = NULL;
NETLIBHTTPREQUEST nlhr = {0};
// initialize the netlib request
nlhr.cbSize = sizeof(nlhr);
nlhr.requestType = REQUEST_POST;
nlhr.flags = NLHRF_HTTP11 | NLHRF_NODUMP;
nlhr.szUrl = (char*)szUrl;
nlhr.headersCount = 6;
nlhr.headers=(NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER)*nlhr.headersCount);
nlhr.headers[0].szName = "Connection";
nlhr.headers[0].szValue = "close";
nlhr.headers[1].szName = "Cache-Control";
nlhr.headers[1].szValue = "no-cache";
nlhr.headers[2].szName = "Pragma";
nlhr.headers[2].szValue = "no-cache";
nlhr.headers[3].szName = "Content-Type";
nlhr.headers[3].szValue = "text/plain; charset=utf-8";
nlhr.headers[4].szName = "AutoUpload";
nlhr.headers[4].szValue = (char*)(szReq->autot ? "1" : "0");
nlhr.headers[5].szName = "Authorization";
char auth[256];
CreateAuthString(auth);
nlhr.headers[5].szValue = auth;
nlhr.pData = szReq->buf;
nlhr.dataLength = (int)strlen(szReq->buf);
while (result == 0xBADBAD)
{
// download the page
NETLIBHTTPREQUEST *nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION,
(WPARAM)hNetlibUser,(LPARAM)&nlhr);
if (nlhrReply)
{
int i;
// if the recieved code is 200 OK
switch(nlhrReply->resultCode)
{
case 200:
if (db_get_b(NULL, PluginName, "UploadChanged", 0))
ProcessVIHash(true);
for (i=nlhrReply->headersCount; i--; )
{
if (_stricmp(nlhrReply->headers[i].szName, "OldPlugins") == 0)
{
i = atoi(nlhrReply->headers[i].szValue);
break;
}
}
ShowMessage(1, TranslateT("VersionInfo upload successful,\n %d old plugins"), i);
result = 0;
break;
case 401:
ShowMessage(0, TranslateT("Cannot upload VersionInfo. Incorrect username or password"));
result = 1;
break;
case 510:
ShowMessage(0, TranslateT("Cannot upload VersionInfo. User is banned"));
result = 1;
break;
case 511:
ShowMessage(0, TranslateT("Cannot upload VersionInfo. Daily upload limit exceeded"));
result = 1;
break;
case 301:
case 302:
case 307:
// get the url for the new location and save it to szInfo
// look for the reply header "Location"
for (i=0; iheadersCount; i++)
{
if (!strcmp(nlhrReply->headers[i].szName, "Location"))
{
size_t rlen = 0;
if (nlhrReply->headers[i].szValue[0] == '/')
{
const char* szPath;
const char* szPref = strstr(szUrl, "://");
szPref = szPref ? szPref + 3 : szUrl;
szPath = strchr(szPref, '/');
rlen = szPath != NULL ? szPath - szUrl : strlen(szUrl);
}
szRedirUrl = (char*)mir_realloc(szRedirUrl,
rlen + strlen(nlhrReply->headers[i].szValue)*3 + 1);
strncpy(szRedirUrl, szUrl, rlen);
strcpy(szRedirUrl+rlen, nlhrReply->headers[i].szValue);
nlhr.szUrl = szRedirUrl;
break;
}
}
break;
default:
result = 1;
ShowMessage(0, TranslateT("Cannot upload VersionInfo. Unknown error"));
}
}
else
{
result = 1;
ShowMessage(0, TranslateT("Cannot upload VersionInfo. Host unreachable."));
}
CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,(LPARAM)nlhrReply);
}
mir_free(szRedirUrl);
mir_free(nlhr.headers);
return result == 0;
}
void __cdecl VersionInfoUploadThread(void* arg)
{
VerTrnsfr* trn = (VerTrnsfr*)arg;
InternetDownloadFile("http://www.miranda-vi.org/uploadpost", trn);
mir_free(trn->buf);
mir_free(trn);
}
void UploadInit(void)
{
NETLIBUSER nlu = {0};
nlu.cbSize = sizeof(nlu);
nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_NOHTTPSOPTION | NUF_TCHAR;
nlu.szSettingsModule = (char*)PluginName;
nlu.ptszDescriptiveName = TranslateT("Crash Dumper HTTP connections");
hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
}
void UploadClose(void)
{
Netlib_CloseHandle(hNetlibUser);
}
bool ProcessVIHash(bool store)
{
bkstring buffer;
buffer.reserve(0x1800);
PrintVersionInfo(buffer, 0);
mir_md5_byte_t hash[16];
mir_md5_state_t context;
mir_md5_init(&context);
mir_md5_append(&context, (PBYTE)buffer.c_str(), (int)buffer.sizebytes());
mir_md5_finish(&context, hash);
char hashstr[40];
arrayToHex(hash, sizeof(hash), hashstr);
bool result;
if (store)
{
db_set_s(NULL, PluginName, "VIHash", hashstr);
result = true;
}
else
{
DBVARIANT dbv;
if (db_get_s(NULL, PluginName, "VIHash", &dbv) == 0)
{
result = strcmp(hashstr, dbv.pszVal) == 0;
db_free(&dbv);
}
else
result = false;
}
return result;
}