summaryrefslogtreecommitdiff
path: root/src/modules/netlib/netliblog.cpp
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
commit48540940b6c28bb4378abfeb500ec45a625b37b6 (patch)
tree2ef294c0763e802f91d868bdef4229b6868527de /src/modules/netlib/netliblog.cpp
parent5c350913f011e119127baeb32a6aedeb4f0d33bc (diff)
initial commit
git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'src/modules/netlib/netliblog.cpp')
-rw-r--r--src/modules/netlib/netliblog.cpp637
1 files changed, 637 insertions, 0 deletions
diff --git a/src/modules/netlib/netliblog.cpp b/src/modules/netlib/netliblog.cpp
new file mode 100644
index 0000000000..fb3fe1d5f2
--- /dev/null
+++ b/src/modules/netlib/netliblog.cpp
@@ -0,0 +1,637 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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; either version 2
+of the License, or (at your option) any later version.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "netlib.h"
+#include "../srfile/file.h"
+
+#define MS_NETLIB_LOGWIN "Netlib/Log/Win"
+
+extern HANDLE hConnectionHeaderMutex;
+
+#define TIMEFORMAT_NONE 0
+#define TIMEFORMAT_HHMMSS 1
+#define TIMEFORMAT_MILLISECONDS 2
+#define TIMEFORMAT_MICROSECONDS 3
+struct {
+ HWND hwndOpts;
+ int toOutputDebugString;
+ int toFile;
+ int toLog;
+ TCHAR* szFile;
+ TCHAR* szUserFile;
+ int timeFormat;
+ int showUser;
+ int dumpSent,dumpRecv,dumpProxy,dumpSsl;
+ int textDumps,autoDetectText;
+ CRITICAL_SECTION cs;
+ int save;
+} logOptions = {0};
+
+typedef struct {
+ const char* pszHead;
+ const char* pszMsg;
+} LOGMSG;
+
+static __int64 mirandaStartTime,perfCounterFreq;
+static int bIsActive = TRUE;
+static HANDLE hLogEvent = NULL;
+
+static const TCHAR* szTimeFormats[] =
+{
+ _T( "No times" ),
+ _T( "Standard hh:mm:ss times" ),
+ _T( "Times in milliseconds" ),
+ _T( "Times in microseconds" )
+};
+
+static INT_PTR CALLBACK LogOptionsDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ logOptions.hwndOpts=hwndDlg;
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg,IDC_DUMPRECV,logOptions.dumpRecv?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_DUMPSENT,logOptions.dumpSent?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_DUMPPROXY,logOptions.dumpProxy?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_DUMPSSL,logOptions.dumpSsl?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_TEXTDUMPS,logOptions.textDumps?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_AUTODETECTTEXT,logOptions.autoDetectText?BST_CHECKED:BST_UNCHECKED);
+ { int i;
+ for( i=0; i < SIZEOF(szTimeFormats); i++ )
+ SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_ADDSTRING,0,(LPARAM)TranslateTS( szTimeFormats[i] ));
+ }
+ SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_SETCURSEL,logOptions.timeFormat,0);
+ CheckDlgButton(hwndDlg,IDC_SHOWNAMES,logOptions.showUser?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_TOOUTPUTDEBUGSTRING,logOptions.toOutputDebugString?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_TOFILE,logOptions.toFile?BST_CHECKED:BST_UNCHECKED);
+ SetDlgItemText(hwndDlg,IDC_FILENAME,logOptions.szUserFile);
+ SetDlgItemText(hwndDlg,IDC_PATH,logOptions.szFile);
+ CheckDlgButton(hwndDlg,IDC_SHOWTHISDLGATSTART,DBGetContactSettingByte(NULL, "Netlib", "ShowLogOptsAtStart",0)?BST_CHECKED:BST_UNCHECKED);
+ { DBVARIANT dbv;
+ if(!DBGetContactSettingString(NULL, "Netlib", "RunAtStart",&dbv)) {
+ SetDlgItemTextA(hwndDlg,IDC_RUNATSTART,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ logOptions.save = 0;
+ {
+ TVINSERTSTRUCT tvis = {0};
+ int i;
+ HWND hwndFilter = GetDlgItem(hwndDlg,IDC_FILTER);
+
+ SetWindowLongPtr(hwndFilter, GWL_STYLE, GetWindowLongPtr(hwndFilter, GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+ tvis.hParent=NULL;
+ tvis.hInsertAfter=TVI_SORT;
+ tvis.item.mask=TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
+ tvis.item.stateMask=TVIS_STATEIMAGEMASK;
+
+ for (i = 0; i < netlibUser.getCount(); ++i)
+ {
+ tvis.item.pszText=netlibUser[i]->user.ptszDescriptiveName;
+ tvis.item.lParam=i;
+ tvis.item.state=INDEXTOSTATEIMAGEMASK( (netlibUser[i]->toLog) ? 2 : 1 );
+ TreeView_InsertItem(hwndFilter, &tvis);
+ }
+ tvis.item.lParam=-1;
+ tvis.item.pszText=TranslateT("(Miranda Core Logging)");
+ tvis.item.state=INDEXTOSTATEIMAGEMASK( (logOptions.toLog) ? 2 : 1 );
+ TreeView_InsertItem(hwndFilter, &tvis);
+ }
+ return TRUE;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+/*
+ case IDC_DUMPRECV:
+ case IDC_DUMPSENT:
+ case IDC_DUMPPROXY:
+ case IDC_TEXTDUMPS:
+ case IDC_AUTODETECTTEXT:
+ case IDC_TIMEFORMAT:
+ case IDC_SHOWNAMES:
+ case IDC_TOOUTPUTDEBUGSTRING:
+ case IDC_TOFILE:
+ case IDC_SHOWTHISDLGATSTART:
+ case IDC_RUNATSTART:
+ break;
+*/
+ case IDC_FILENAME:
+ if(HIWORD(wParam)!=EN_CHANGE) break;
+ if((HWND)lParam==GetFocus())
+ CheckDlgButton(hwndDlg,IDC_TOFILE,BST_CHECKED);
+
+ {
+ TCHAR path[MAX_PATH];
+ GetWindowText((HWND)lParam, path, MAX_PATH);
+
+ TCHAR *pszNewPath = Utils_ReplaceVarsT(path);
+ pathToAbsoluteT(pszNewPath, path, NULL);
+ SetDlgItemText(hwndDlg, IDC_PATH, path);
+ mir_free(pszNewPath);
+ }
+ break;
+ case IDC_FILENAMEBROWSE:
+ case IDC_RUNATSTARTBROWSE:
+ { TCHAR str[MAX_PATH+2];
+ OPENFILENAME ofn={0};
+ TCHAR filter[512],*pfilter;
+
+ GetWindowText(GetWindow((HWND)lParam,GW_HWNDPREV),str,SIZEOF(str));
+ ofn.lStructSize=OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner=hwndDlg;
+ ofn.Flags=OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ if (LOWORD(wParam)==IDC_FILENAMEBROWSE) {
+ ofn.lpstrTitle=TranslateT("Select where log file will be created");
+ } else {
+ ofn.Flags|=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST;
+ ofn.lpstrTitle=TranslateT("Select program to be run");
+ }
+ _tcscpy(filter,TranslateT("All Files"));
+ _tcscat(filter,_T(" (*)"));
+ pfilter=filter+lstrlen(filter)+1;
+ _tcscpy(pfilter,_T("*"));
+ pfilter=pfilter+lstrlen(pfilter)+1;
+ *pfilter='\0';
+ ofn.lpstrFilter=filter;
+ ofn.lpstrFile=str;
+ ofn.nMaxFile=SIZEOF(str)-2;
+ ofn.nMaxFileTitle=MAX_PATH;
+ if (LOWORD(wParam)==IDC_FILENAMEBROWSE) {
+ if(!GetSaveFileName(&ofn)) return 1;
+ } else {
+ if(!GetOpenFileName(&ofn)) return 1;
+ }
+ if(LOWORD(wParam)==IDC_RUNATSTARTBROWSE && _tcschr(str,' ')!=NULL) {
+ MoveMemory(str+1,str,SIZEOF(str)-2);
+ str[0]='"';
+ lstrcat(str,_T("\""));
+ }
+ SetWindowText(GetWindow((HWND)lParam,GW_HWNDPREV),str);
+ break;
+ }
+ case IDC_RUNNOW:
+ { TCHAR str[MAX_PATH+1];
+ STARTUPINFO si={0};
+ PROCESS_INFORMATION pi;
+ GetDlgItemText(hwndDlg,IDC_RUNATSTART,str,MAX_PATH);
+ si.cb=sizeof(si);
+ if(str[0]) CreateProcess(NULL,str,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
+ }
+ break;
+ case IDC_SAVE:
+ logOptions.save = 1;
+ //
+ case IDOK:
+ {
+ TCHAR str[MAX_PATH];
+
+ GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, MAX_PATH);
+ DBWriteContactSettingTString(NULL, "Netlib", "RunAtStart",str);
+ DBWriteContactSettingByte(NULL, "Netlib", "ShowLogOptsAtStart",(BYTE)IsDlgButtonChecked(hwndDlg,IDC_SHOWTHISDLGATSTART));
+
+ EnterCriticalSection(&logOptions.cs);
+
+ mir_free(logOptions.szUserFile);
+ GetWindowText(GetDlgItem(hwndDlg,IDC_FILENAME), str, MAX_PATH );
+ logOptions.szUserFile = mir_tstrdup(str);
+
+ mir_free(logOptions.szFile);
+ GetWindowText(GetDlgItem(hwndDlg,IDC_PATH), str, MAX_PATH );
+ logOptions.szFile = mir_tstrdup(str);
+
+ logOptions.dumpRecv=IsDlgButtonChecked(hwndDlg,IDC_DUMPRECV);
+ logOptions.dumpSent=IsDlgButtonChecked(hwndDlg,IDC_DUMPSENT);
+ logOptions.dumpProxy=IsDlgButtonChecked(hwndDlg,IDC_DUMPPROXY);
+ logOptions.dumpSsl=IsDlgButtonChecked(hwndDlg,IDC_DUMPSSL);
+ logOptions.textDumps=IsDlgButtonChecked(hwndDlg,IDC_TEXTDUMPS);
+ logOptions.autoDetectText=IsDlgButtonChecked(hwndDlg,IDC_AUTODETECTTEXT);
+ logOptions.timeFormat=SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_GETCURSEL,0,0);
+ logOptions.showUser=IsDlgButtonChecked(hwndDlg,IDC_SHOWNAMES);
+ logOptions.toOutputDebugString=IsDlgButtonChecked(hwndDlg,IDC_TOOUTPUTDEBUGSTRING);
+ logOptions.toFile=IsDlgButtonChecked(hwndDlg,IDC_TOFILE);
+
+ LeaveCriticalSection(&logOptions.cs);
+ }
+ {
+ HWND hwndFilter = GetDlgItem(logOptions.hwndOpts, IDC_FILTER);
+ TVITEM tvi={0};
+ BOOL checked;
+
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT;
+ tvi.hItem=TreeView_GetRoot(hwndFilter);
+
+ while(tvi.hItem)
+ {
+ TreeView_GetItem(hwndFilter,&tvi);
+ checked = ((tvi.state&TVIS_STATEIMAGEMASK)>>12==2);
+
+ if (tvi.lParam == -1) {
+ logOptions.toLog = checked;
+ if ( logOptions.save )
+ DBWriteContactSettingDword(NULL, "Netlib", "NLlog",checked);
+ }
+ else
+ if (tvi.lParam < netlibUser.getCount()) {
+ netlibUser[tvi.lParam]->toLog = checked;
+ if ( logOptions.save )
+ DBWriteContactSettingDword(NULL,netlibUser[tvi.lParam]->user.szSettingsModule,"NLlog",checked);
+ }
+
+ tvi.hItem=TreeView_GetNextSibling(hwndFilter,tvi.hItem);
+ }
+ }
+
+ if ( logOptions.save ) {
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpRecv",(BYTE)logOptions.dumpRecv);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpSent",(BYTE)logOptions.dumpSent);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpProxy",(BYTE)logOptions.dumpProxy);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpSsl",(BYTE)logOptions.dumpSsl);
+ DBWriteContactSettingByte(NULL, "Netlib", "TextDumps",(BYTE)logOptions.textDumps);
+ DBWriteContactSettingByte(NULL, "Netlib", "AutoDetectText",(BYTE)logOptions.autoDetectText);
+ DBWriteContactSettingByte(NULL, "Netlib", "TimeFormat",(BYTE)logOptions.timeFormat);
+ DBWriteContactSettingByte(NULL, "Netlib", "ShowUser",(BYTE)logOptions.showUser);
+ DBWriteContactSettingByte(NULL, "Netlib", "ToOutputDebugString",(BYTE)logOptions.toOutputDebugString);
+ DBWriteContactSettingByte(NULL, "Netlib", "ToFile",(BYTE)logOptions.toFile);
+ DBWriteContactSettingTString(NULL, "Netlib", "File", logOptions.szFile ? logOptions.szUserFile: _T(""));
+ logOptions.save = 0;
+ }
+ else
+ DestroyWindow(hwndDlg);
+
+ break;
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+ case WM_DESTROY:
+ ImageList_Destroy(TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_FILTER), TVSIL_STATE));
+ logOptions.hwndOpts=NULL;
+ break;
+ }
+ return FALSE;
+}
+
+void NetlibLogShowOptions(void)
+{
+ if(logOptions.hwndOpts==NULL)
+ logOptions.hwndOpts=CreateDialog(hMirandaInst,MAKEINTRESOURCE(IDD_NETLIBLOGOPTS),NULL,LogOptionsDlgProc);
+ SetForegroundWindow(logOptions.hwndOpts);
+}
+
+static INT_PTR ShowOptions(WPARAM, LPARAM)
+{
+ NetlibLogShowOptions();
+ return 0;
+}
+
+static INT_PTR NetlibLog(WPARAM wParam, LPARAM lParam)
+{
+ struct NetlibUser *nlu = (struct NetlibUser*)wParam;
+ struct NetlibUser nludummy;
+ const char *pszMsg = (const char*)lParam;
+ char szTime[32], szHead[128];
+ LARGE_INTEGER liTimeNow;
+ DWORD dwOriginalLastError;
+
+ if (!bIsActive)
+ return 0;
+
+ if ((nlu != NULL && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if (nlu == NULL) /* if the Netlib user handle is NULL, just pretend its not */
+ {
+ if (!logOptions.toLog)
+ return 1;
+ nlu = &nludummy;
+ nlu->user.szSettingsModule = "(NULL)";
+ }
+ else if (!nlu->toLog)
+ return 1;
+
+ dwOriginalLastError = GetLastError();
+ switch (logOptions.timeFormat)
+ {
+ case TIMEFORMAT_HHMMSS:
+ GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER,
+ NULL, NULL, szTime, SIZEOF(szTime));
+ break;
+
+ case TIMEFORMAT_MILLISECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, SIZEOF(szTime), "%I64u.%03I64u", liTimeNow.QuadPart / perfCounterFreq,
+ 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ case TIMEFORMAT_MICROSECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, SIZEOF(szTime), "%I64u.%06I64u", liTimeNow.QuadPart / perfCounterFreq,
+ 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ default:
+ szTime[0] = '\0';
+ break;
+ }
+ if(logOptions.timeFormat || logOptions.showUser)
+ mir_snprintf(szHead, SIZEOF(szHead) - 1, "[%s%s%s] ", szTime,
+ (logOptions.showUser && logOptions.timeFormat) ? " " : "",
+ logOptions.showUser ? nlu->user.szSettingsModule : "");
+ else
+ szHead[0]=0;
+
+ if(logOptions.toOutputDebugString)
+ {
+ if (szHead[0])
+ OutputDebugStringA(szHead);
+ OutputDebugStringA(pszMsg);
+ OutputDebugStringA("\n");
+ }
+
+ if (logOptions.toFile && logOptions.szFile[0])
+ {
+ EnterCriticalSection(&logOptions.cs);
+
+ FILE *fp;
+ fp = _tfopen(logOptions.szFile, _T("ab"));
+ if (!fp)
+ {
+ CreatePathToFileT(logOptions.szFile);
+ fp = _tfopen(logOptions.szFile, _T("at"));
+ }
+ if (fp)
+ {
+ size_t len = strlen(pszMsg);
+ fprintf(fp,"%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n");
+ fclose(fp);
+ }
+ LeaveCriticalSection(&logOptions.cs);
+ }
+
+ if (((THook*)hLogEvent)->subscriberCount)
+ {
+ LOGMSG logMsg = { szHead, pszMsg };
+ CallHookSubscribers(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg);
+ }
+
+ SetLastError(dwOriginalLastError);
+ return 1;
+}
+
+static INT_PTR NetlibLogW(WPARAM wParam, LPARAM lParam)
+{
+ const wchar_t *pszMsg = (const wchar_t*)lParam;
+ char* szMsg = Utf8EncodeUcs2(pszMsg);
+ INT_PTR res = NetlibLog(wParam, (LPARAM)szMsg);
+ mir_free(szMsg);
+ return res;
+}
+
+void NetlibLogf(NetlibUser* nlu, const char *fmt, ...)
+{
+ if (nlu == NULL)
+ {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ va_list va;
+ char szText[1024];
+
+ va_start(va,fmt);
+ mir_vsnprintf(szText, sizeof(szText), fmt, va);
+ va_end(va);
+
+ NetlibLog((WPARAM)nlu, (LPARAM)szText);
+}
+
+
+void NetlibDumpData(struct NetlibConnection *nlc,PBYTE buf,int len,int sent,int flags)
+{
+ int isText=1;
+ char szTitleLine[128];
+ char *szBuf;
+ int titleLineLen;
+ struct NetlibUser *nlu;
+ bool useStack = false;
+
+ // This section checks a number of conditions and aborts
+ // the dump if the data should not be written to the log
+
+ // Check packet flags
+ if (flags & (MSG_PEEK | MSG_NODUMP))
+ return;
+
+ // Check user's log settings
+ if (!(logOptions.toOutputDebugString ||
+ ((THook*)hLogEvent)->subscriberCount ||
+ (logOptions.toFile && logOptions.szFile[0])))
+ return;
+ if ((sent && !logOptions.dumpSent) ||
+ (!sent && !logOptions.dumpRecv))
+ return;
+ if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy)
+ return;
+ if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl)
+ return;
+
+ WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
+ nlu = nlc ? nlc->nlu : NULL;
+ titleLineLen = mir_snprintf(szTitleLine, SIZEOF(szTitleLine), "(%p:%u) Data %s%s\r\n",
+ nlc, nlc ? nlc->s : 0, sent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : "");
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ // check filter settings
+ if (nlu == NULL)
+ {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ if (!logOptions.textDumps)
+ isText = 0;
+ else if (!(flags&MSG_DUMPASTEXT))
+ {
+ if (logOptions.autoDetectText)
+ {
+ int i;
+ for(i = 0; i<len; i++)
+ {
+ if ((buf[i]<' ' && buf[i]!='\t' && buf[i]!='\r' && buf[i]!='\n') || buf[i]>=0x80)
+ {
+ isText = 0;
+ break;
+ }
+ }
+ }
+ else
+ isText = 0;
+ }
+
+ // Text data
+ if ( isText ) {
+ int sz = titleLineLen + len + 1;
+ useStack = sz <= 8192;
+ szBuf = (char*)(useStack ? alloca(sz) : mir_alloc(sz));
+ CopyMemory( szBuf, szTitleLine, titleLineLen );
+ CopyMemory( szBuf + titleLineLen, (const char*)buf, len );
+ szBuf[titleLineLen + len] = '\0';
+ }
+ // Binary data
+ else {
+ int line, col, colsInLine;
+ char *pszBuf;
+ int sz = titleLineLen + ((len+16)>>4) * 78 + 1;
+ useStack = sz <= 8192;
+
+ szBuf = (char*)(useStack ? alloca(sz) : mir_alloc(sz));
+ CopyMemory(szBuf, szTitleLine, titleLineLen);
+ pszBuf = szBuf + titleLineLen;
+ for ( line = 0; ; line += 16 ) {
+ colsInLine = min(16, len - line);
+
+ if (colsInLine == 16) {
+ PBYTE p = buf + line;
+ pszBuf += wsprintfA(
+ pszBuf, "%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ",
+ line, p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15] );
+ }
+ else {
+ pszBuf += wsprintfA(pszBuf, "%08X: ", line);
+ // Dump data as hex
+ for (col = 0; col < colsInLine; col++)
+ pszBuf += wsprintfA(pszBuf, "%02X%c", buf[line + col], ((col&3)==3 && col != 15)?'-':' ');
+ // Fill out last line with blanks
+ for ( ; col<16; col++)
+ {
+ lstrcpyA(pszBuf, " ");
+ pszBuf += 3;
+ }
+ *pszBuf++ = ' ';
+ }
+
+ for (col = 0; col < colsInLine; col++)
+ *pszBuf++ = buf[line+col]<' '?'.':(char)buf[line+col];
+
+ if (len-line<=16)
+ break;
+
+ *pszBuf++ = '\r'; // End each line with a break
+ *pszBuf++ = '\n'; // End each line with a break
+ }
+ *pszBuf = '\0';
+ }
+
+ NetlibLog((WPARAM)nlu,(LPARAM)szBuf);
+ if (!useStack) mir_free(szBuf);
+}
+
+void NetlibLogInit(void)
+{
+ DBVARIANT dbv;
+ LARGE_INTEGER li;
+
+ QueryPerformanceFrequency( &li );
+ perfCounterFreq = li.QuadPart;
+ QueryPerformanceCounter( &li );
+ mirandaStartTime = li.QuadPart;
+
+ CreateServiceFunction( MS_NETLIB_LOGWIN, ShowOptions );
+ CreateServiceFunction( MS_NETLIB_LOG, NetlibLog );
+ CreateServiceFunction( MS_NETLIB_LOGW, NetlibLogW );
+ hLogEvent = CreateHookableEvent( ME_NETLIB_FASTDUMP );
+
+ InitializeCriticalSection(&logOptions.cs);
+ logOptions.dumpRecv = DBGetContactSettingByte( NULL, "Netlib", "DumpRecv", 1 );
+ logOptions.dumpSent = DBGetContactSettingByte( NULL, "Netlib", "DumpSent", 1 );
+ logOptions.dumpProxy = DBGetContactSettingByte( NULL, "Netlib", "DumpProxy", 1 );
+ logOptions.dumpSsl = DBGetContactSettingByte( NULL, "Netlib", "DumpSsl", 0 );
+ logOptions.textDumps = DBGetContactSettingByte( NULL, "Netlib", "TextDumps", 1 );
+ logOptions.autoDetectText = DBGetContactSettingByte( NULL, "Netlib", "AutoDetectText", 1 );
+ logOptions.timeFormat = DBGetContactSettingByte( NULL, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS );
+ logOptions.showUser = DBGetContactSettingByte( NULL, "Netlib", "ShowUser", 1 );
+ logOptions.toOutputDebugString = DBGetContactSettingByte( NULL, "Netlib", "ToOutputDebugString", 0 );
+ logOptions.toFile = DBGetContactSettingByte( NULL, "Netlib", "ToFile", 0 );
+ logOptions.toLog = DBGetContactSettingDword( NULL, "Netlib", "NLlog", 1 );
+
+ if (!DBGetContactSettingTString(NULL, "Netlib", "File", &dbv))
+ {
+ logOptions.szUserFile = mir_tstrdup(dbv.ptszVal);
+ TCHAR *pszNewPath = Utils_ReplaceVarsT(dbv.ptszVal);
+
+ TCHAR path[MAX_PATH];
+ pathToAbsoluteT(pszNewPath, path, NULL);
+ logOptions.szFile = mir_tstrdup(path);
+
+ mir_free(pszNewPath);
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ logOptions.szUserFile = mir_tstrdup(_T("%miranda_logpath%\\netlog.txt"));
+ logOptions.szFile = Utils_ReplaceVarsT(logOptions.szUserFile);
+ }
+
+ if ( logOptions.toFile && logOptions.szFile[0] ) {
+ FILE *fp;
+ fp = _tfopen( logOptions.szFile, _T("wt"));
+ if ( fp )
+ fclose(fp);
+ }
+
+ if ( DBGetContactSettingByte( NULL, "Netlib", "ShowLogOptsAtStart", 0 ))
+ NetlibLogShowOptions();
+
+ if ( !DBGetContactSettingTString( NULL, "Netlib", "RunAtStart", &dbv )) {
+ STARTUPINFO si = { 0 };
+ PROCESS_INFORMATION pi;
+ si.cb = sizeof( si );
+ if ( dbv.ptszVal[0] )
+ CreateProcess( NULL, dbv.ptszVal, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
+ DBFreeVariant( &dbv );
+ }
+}
+
+void NetlibLogShutdown(void)
+{
+ bIsActive = FALSE;
+ DestroyHookableEvent( hLogEvent ); hLogEvent = NULL;
+ if ( IsWindow( logOptions.hwndOpts ))
+ DestroyWindow( logOptions.hwndOpts );
+ DeleteCriticalSection( &logOptions.cs );
+ mir_free( logOptions.szFile );
+ mir_free( logOptions.szUserFile );
+}