Event.isTrusted and simulate mouse click

Event.isTrusted не работает в ИЕ используем (!arguments[0].screenY && !arguments[0].screenX) для симуляции клика $(this).closest(‘a’)[0].click()

The HTMLElement.click() method simulates a mouse click on an element.

The isTrusted read-only property of the Event interface is a boolean value that is true when the event was generated by a user action, and false when the event was created or modified by a script or dispatched via EventTarget.dispatchEvent().

    <a onclick="if(!arguments[0].isTrusted || (!arguments[0].screenY && !arguments[0].screenX)) return true; $(this).find('select').removeClass('hidden'); return false;" href="/performedworks/report1/allscan/zzz" class="btn btn-primary pull-right" title="Архив скан-копий реестров (zip) за год" target="_blank" style="padding: 3px;">
        <i style="font-size: 26px; width: 40px;" class="far fa-file-archive"></i>
        <select class="form-control hidden" onchange="$(this).addClass('hidden'); var val = $(this).val(); var href = $(this).closest('a').attr('href', function(){ return $(this).attr('href').replace(/\/[^\/]+$/, '/' + val); }); $(this).closest('a')[0].click();">
            <option value="" disabled selected></option>
            <?php
            $y = date('Y');
            $i = -1;
            while(++$i < 7) { ?>
            <option value="<?= $y - $i ?>"><?= $y - $i ?></option>
            <?php } ?>
        </select>
    </a>
    <div class="clearfix"></div>

«Вечная светодиодная лампа»

Лампочки «Космос» 15, 20, 25 Вт. Во всех трех лампах драйвер — стабилизатор тока, на разных микросхемах. Изменил номиналы токозадающих резисторов, в разных вариантах, 15->20, 15->25, 20->33, 25->47, посмотрим сколько теперь проживут, насколько они будут «вечными».

У 25-ватной колпак с бортиками, держится очень крепко, пришлось ножом подковырнуть. Остальные легко отрываются руками, туда-сюда качнул и готово.

Подогревать плату нечем, в наличии только паяльник, потому небольшой лайфхак, чуток заливаю оловом контакты резистора, дабы жало грело сразу двух сторон.

IMG_20210421_205730 IMG_20210422_105532 IMG_20210422_105349 IMG_20210422_105115 IMG_20210421_205851 IMG_20210421_214020 Screenshot_2021-04-21-21-00-14-764_com.miui.cit Screenshot_2021-04-21-21-41-04-753_com.miui.cit IMG_20210421_215504 IMG_20210422_030913 IMG_20210422_102302

modify resources of an exe

Конфигурация по умолчанию в exe файле, ее изменение и сохранение в ресурсе типа RT_RCDATA, всё официальными функциями BeginUpdateResource, UpdateResource, EndUpdateResource, без хакерства.
	path = GetCommandLine();
	path = path.Mid(1); // убираем "
	path = path.Left(path.ReverseFind('\\'));
	path += L"\\server.exe";

	try {
		// проверяем наличие экзешника, в моем случае должен лежать в одном каталоге с патчилкой
		CFileException* e = new CFileException();
		CFile cfile;
		if (!cfile.Open(path, CFile::modeReadWrite, e))
		{
			throw e;
		}
		cfile.Close();

		LPBYTE bomPos = 0;
		// загрузка ресурса
		HMODULE hLibrary = 0;
		HRSRC hResource = 0;
		HGLOBAL hResourceLoaded = 0;
		DWORD  resourceSize = 0;
		LPBYTE lpBuffer = 0;
		hLibrary = LoadLibrary(path);
		if (NULL != hLibrary)
		{
			hResource = FindResource(hLibrary, MAKEINTRESOURCE(paramsResId), RT_RCDATA); // !!! paramsResId == 139 ИД ресурса в экзешнике
			if (NULL != hResource)
			{
				hResourceLoaded = LoadResource(hLibrary, hResource);
				resourceSize = SizeofResource(hLibrary, hResource);
				if (NULL != hResourceLoaded)
				{
					lpBuffer = (LPBYTE)LockResource(hResourceLoaded);
					bomPos = new BYTE[resourceSize];
					memcpy_s(bomPos, resourceSize, lpBuffer, resourceSize);
				}
			}
			FreeLibrary(hLibrary);
		}

		if (bomPos == 0) {
			AfxThrowArchiveException(CArchiveException::endOfFile, _T("signature"));
		}

		// попытка загрузить ранее записанные параметры		
		int sizeOfParams = (int)*(bomPos + bomSize);
		if (sizeOfParams > 0 && sizeOfParams < 800) {
			BYTE kk[] = { 'q', 10, 'e', 7, 'a', 'z', 'Q' };
			encryptDecrypt((BYTE*)(bomPos + bomSize + sizeof(0)), sizeOfParams, kk, sizeof(kk));
			int realLength = wcslen((TCHAR*)(bomPos + bomSize + sizeof(0)));
			TCHAR* decryptData = (TCHAR*)(bomPos + bomSize + sizeof(0));
			if (realLength > 2 && decryptData[0] == _T('Z') && decryptData[1] == _T('A')) { // проверяем на ZA, значит всё раскодировалось
				decryptData += 2; // пропустим ZA
				TCHAR* s_ssh_host = 0, *s_ssh_port = 0, *s_ssh_user = 0, *s_ssh_pwd = 0, *s_ssh_add = 0;
				s_ssh_host = decryptData;
				s_ssh_port = _tcschr(s_ssh_host, _T('\t')) + 1;
				if (s_ssh_port)
					s_ssh_user = _tcschr(s_ssh_port, _T('\t')) + 1;
				if (s_ssh_user)
					s_ssh_pwd = _tcschr(s_ssh_user, _T('\t')) + 1;
				if (s_ssh_pwd)
					s_ssh_add = _tcschr(s_ssh_pwd, _T('\t')) + 1;
				if (s_ssh_port)
					*(s_ssh_port - 1) = 0;
				if (s_ssh_user)
					*(s_ssh_user - 1) = 0;
				if (s_ssh_pwd)
					*(s_ssh_pwd - 1) = 0;
				if (s_ssh_add)
					*(s_ssh_add - 1) = 0;
			
				//
			}
		}
		delete[] bomPos;
	CString s_ssh_host;
	ssh_host.GetWindowText(s_ssh_host);
	CString s_ssh_port;
	ssh_port.GetWindowText(s_ssh_port);
	CString s_ssh_user;
	ssh_user.GetWindowText(s_ssh_user);
	CString s_ssh_pwd;
	ssh_pwd.GetWindowText(s_ssh_pwd);
	CString s_ssh_add;
	ssh_add.GetWindowText(s_ssh_add);
	CString s_full;
	s_full.Format(_T("ZA%s\t%s\t%s\t%s\t%s\0"), s_ssh_host.GetBuffer(), s_ssh_port.GetBuffer(), s_ssh_user.GetBuffer(), s_ssh_pwd.GetBuffer(), s_ssh_add.GetBuffer());

	HANDLE hResource;
	hResource = BeginUpdateResource(path, FALSE);
	if (NULL != hResource)
	{
		int sizeOfParams = (s_full.GetLength() + 1) * sizeof(_T('\0'));
		if (sizeOfParams > 700) {
			::AfxMessageBox(_T("Only 700 bytes allowed for writing."));
			FreeResource(hResource);
			return;
		}
		LPBYTE lpBuffer = new BYTE[bomSize + sizeof(0) + sizeOfParams];
		memcpy(lpBuffer, bom, bomSize);
		memcpy(lpBuffer + bomSize, &sizeOfParams, sizeof(0));
		memcpy(lpBuffer + bomSize + sizeof(0), (BYTE*)s_full.GetBuffer(), sizeOfParams);
		
		BYTE kk[] = { 'q', 10, 'e', 7, 'a', 'z', 'Q' };
		encryptDecrypt(lpBuffer + bomSize + sizeof(0), sizeOfParams, kk, sizeof(kk));
		
		if (UpdateResource(hResource,
			RT_RCDATA,
			MAKEINTRESOURCE(paramsResId),
			0,
			(LPVOID)lpBuffer,
			bomSize + sizeof(0) + sizeOfParams) != FALSE)
		{
			EndUpdateResource(hResource, FALSE);
		}
		delete[] lpBuffer;
	}

ssh over https proxy

Очередной раз из разряда, «век живи, век учись» или «если бы мы читали документацию».
Проброс портов с помощью PUTTY и туда и обратно и все это через корпоративный прокси сервер (TMG, NAT закрыт совсем).
Благо прокси разрешает Basic авторизацию, PUTTY только с ней и может, иначе было бы сложнее.
Wireshark По умолчанию в Threat Management Gateway для туннелирования с использованием HTTP метода CONNECT открыты всего два порта:
By default on TMG/ISA, the following tunnel ranges are configured:
NNTP (single port): 563
ssl (single port): 443
Воспользуемся портом 563 (443 у меня занят) и поднимем SSH сервер во вне (в моем случае проброс на домашнем роутере в одну из виртуалок)
Не забываем на SSH сервере разрешить AllowTcpForwarding yes и GatewayPorts yes
putty putty putty
По какой-то причине PUTTY принимает только 127.0.0.1 при обратном пробросе, пришлось воспользоваться помощью администратора
netsh interface portproxy add v4tov4 listenport=3393 listenaddress=0.0.0.0 connectport=3389 connectaddress=10.19.80.152
netsh interface portproxy add v4tov4 listenport=3397 listenaddress=0.0.0.0 connectport=80 connectaddress=10.19.80.76

Итого, с рабочего доступна RDP домашнего, с домашнего, RDP одного из серверов и веб-сервер из корпоративной сети.

Node, Puppeteer: автоматизация Google Chrome. Сохраняем ts файлы и преобразуем в mp4.

Используем именно Google Chrome, так как в Chromium не все работает, например, Shaka player (Google).
После установки Node.js
sudo dnf module list nodejs
sudo dnf module enable nodejs:12
sudo dnf install nodejs
Устанавливаем puppeteer без Chromium (350 МВ)
echo "export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true && npm install i puppeteer" | sudo sh
При запуске puppeteer указываем путь к исполняемому файлу Google Chrome
 executablePath: '/opt/google/chrome/google-chrome',
Выключаем CORS
        '--disable-web-security',
        '--disable-site-isolation-trials',
Из интересного для сохранения «.m3u8» и «.ts» используем page.evaluate, далее fetch в контексте браузера, затем передаем содержимое файла в puppeteer
let dataString = '[' + (new Uint8Array(data)).toString() + ']';
и сохраняем файл определенной с помощью page.exposeFunction функцией writefile.
'use strict';
const puppeteer = require('puppeteer');
const fs = require('fs');

(async function main() {
  var browser;
  try {

    var myArgs = process.argv.slice(2);
    if (!myArgs.length) {
        throw('need dir');        
    }
    console.log('dir: ', myArgs[0]);


    browser = await puppeteer.launch({
      headless: true,
      slowMo: 250, // slow down by 250ms
//      devtools: true,
      executablePath: '/opt/google/chrome/google-chrome',
      defaultViewport: null, // иначе не работает --window-size=1920,1080
      args:[
        '--window-size=1920,1080',
        '--disable-setuid-sandbox',
	'--no-sandbox',
        '--disable-infobars',
        '--disable-web-security',
        '--disable-site-isolation-trials',
        ],
    });

/*
    var lessonUrl = myArgs[0]

    const crypto = require('crypto');
    const hash = crypto.createHash('sha256');
    hash.update(lessonUrl);

    var dir = 'fdb' + '/' + hash.digest('hex');
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir);
    }
    var dirOriginal = dir + '/' + 'original';

    if (!fs.existsSync(dirOriginal)) {
        fs.mkdirSync(dirOriginal);
    }
*/

    var lessonUrl = myArgs[1];
    var dir = myArgs[0];
    var dirOriginal = dir + '/' + 'original';


    const regUrl = 'https://zzz.ru/user/registration'
    const page = await browser.newPage()
    page.on('console', msg => console.log('PAGE LOG:', msg.text()));
//    await page.setDefaultNavigationTimeout(60000);
    await page.goto(regUrl, { waitUntil: 'load' })
    await page.waitForTimeout(1000);
    await page.screenshot({ path: dir + '/' + 'screenshot01.png' })

    let checkUrl = await page.evaluate(() => location.href);
    if (regUrl == checkUrl) {
        await page.click('[data-qa="__authEmailButton"]')
        await page.type('#email', 'username@mail.ru')
        await page.type('#password', '12wdnj4ss7ty')
        await page.screenshot({ path: dir + '/' + 'screenshot02.png' })

        await Promise.all([
          page.click('[data-qa="__authEmailSubmitButton"]'),
          page.waitForNavigation(), // if "await page.waitForNavigation" then "TimeoutError: Navigation timeout of 30000 ms exceeded", причем на ноде поновее всё работало
        ]);
    }
    await page.waitForTimeout(2000);
    await page.screenshot({ path: dir + '/' + 'screenshot1.png' })
    console.log(lessonUrl);

    await page.goto(lessonUrl, { waitUntil: 'load' })
    await page.waitForTimeout(2000);
    await page.screenshot({ path: dir + '/' + 'screenshot2.png' })
/*
// forEach и async не очень дружат
    var videoFrame;
    page.frames().forEach(async (frame) => {
        let tt = await frame.$('#shaka-video');
        if (tt) {
            videoFrame = frame;
        }
    });
*/

    var videoFrame;
    for (const frame of page.frames()) {
        let tt = await frame.$('#shaka-video');
        if (tt) {
            videoFrame = frame;
        }
    }
    if (!videoFrame) {
            throw('#shaka-video not found!');
    }
    if (videoFrame) {
        console.log(videoFrame.url());
        await page.goto(videoFrame.url(), { waitUntil: 'load' })
        await page.waitForTimeout(5000);
        await page.screenshot({ path: dir + '/' + 'screenshot3.png' })
        const filesUrls = await page.evaluate(() => {
            var urls = [];
            var ui0 = document.getElementById('shaka-player').ui;
            urls.push(ui0.b.Fa);
            // "https://storage.svc.services/api/v2/backends/yandex/sets/hls.webinar::198a2db0-e74b-44c8-ac22-7cec61972e8a/objects/long.v2.yandex.master.m3u8"
            // ссылка после редиректа получаем подписанные (pre-signed) URL
            // ui0.b.s.K
            // "https://storage-lb.services/hls.webinar/198a2db0-e74b-44c8-ac22-7cec61972e8a.long.v2.yandex.master.m3u8?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=DNmtuhzj5w4VFKS-9BUA%2F20201228%2Fru-central1%2Fs3%2Faws4_request&X-Amz-Date=20201228T142459Z&X-Amz-Expires=300&X-Amz-Signature=0a10db545d16abdfd81c988d7f6e488dfc36502aab60456da78afa9f1ed5bac8&X-Amz-SignedHeaders=host"
            var a0=v0=undefined;
            // select audio and video streams
            ui0.b.s.a.forEach((v,k)=>{ 
                if (!a0 && v.stream.type=='audio') {a0=v;} 
                if (v.stream.type=='video' && (!v0 || v.stream.width > v0.stream.width)) {v0=v;} 
            });
            // load and save selected audio and video segments description files
            urls.push(a0.Wc);
            urls.push(v0.Wc);
//      ["https://storage.svc.services/api/v2/backends/yandex/sets/hls.webinar::198a2db0-e74b-44c8-ac22-7cec61972e8a/objects/long.audio.32kbps.1.1608056896.ts" length: 1 __proto__: Array(0)

            a0.stream.segmentIndex.a.forEach((v,k)=>{ 
                urls = urls.concat(v.c());
            });
            v0.stream.segmentIndex.a.forEach((v,k)=>{ 
                urls = urls.concat(v.c());
            });

            return urls;
        });        
        console.log(filesUrls[0], filesUrls[1], filesUrls[2], filesUrls[3], filesUrls.length);

        var filesUrlsSaved = 0;

        await page.exposeFunction('writefile', async (filePath, data) => {
//          let dataBuffer = new TextEncoder().encode(data);
          filesUrlsSaved++;
          let dataBuffer = new Uint8Array(JSON.parse(data));

          return new Promise((resolve, reject) => {
            fs.writeFile(filePath, dataBuffer, {encoding: null}, (err) => {
              if (err)
                reject(err);
              else
                resolve();
            });
          });
        });

        if (filesUrls.length) {
            fs.writeFileSync(dirOriginal + '/loading', filesUrls[1] + '\n' + filesUrls[2] + '\n', {flag: 'w'});
        }

        for (var loadFile of filesUrls) {
            await page.evaluate(async (dirOriginal, loadFile) => {
                let fileName = loadFile.match('[^/]*$')[0].match('^[^#?]*');
                await fetch(loadFile, {
                    method: "GET",
                    redirect: 'follow',
                }).then((response) => {
                    console.log('url with auth', response.url);
                    return response.arrayBuffer();
                }).then(async function(data) {
//                    let dataView = new Uint8Array(data);
//                    let dataString = new TextDecoder().decode(data);
                    let dataString = '[' + (new Uint8Array(data)).toString() + ']';
                    await window.writefile(dirOriginal + '/' + fileName, dataString);
                });
            }, dirOriginal, loadFile);

/*
            // error 403
           var viewSource = await page.goto(loadFile);
           fs.writeFile(dirOriginal + '/' + fileName, await viewSource.buffer(), function (err) {
               if (err) {
                   return console.log(err);
               }
           });
*/
        }

//        if (filesUrls.length) {
//            fs.writeFileSync(dirOriginal + '/loaded', '', {flag: 'w'});
//        }

        if (filesUrls.length != filesUrlsSaved) {
            console.error('filesUrls:', filesUrls.length, filesUrlsSaved);
            throw('Not all files saved!');
        }

    }

    browser.close()
    console.log('See screenshot in: ' + dir)
  } catch (err) {
    console.error(err);
    browser.close()
  }
})();
Еще чуток бэкенда на php
<?php
...
    // выполнение внешней программы и получение stdout, stderr 
    function exec_return($cmd, $input = '') {
        $descriptorspec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
        $proc = proc_open($cmd . "\n", $descriptorspec, $pipes);
        foreach ($pipes as $pipe) {
            stream_set_blocking($pipe, false);
        }
        fwrite($pipes[0], $input); fclose($pipes[0]);
//        $stdout = stream_get_contents($pipes[1]); 
//        $stderr = stream_get_contents($pipes[2]); 
        $stdout = ''; 
        $stderr = ''; 
        while (!feof($pipes[1])) $stdout .= fgets($pipes[1],1024);
        while (!feof($pipes[2])) $stderr .= fgets($pipes[2],1024);
        fclose($pipes[1]);
        fclose($pipes[2]);
        $return = proc_close($proc);
        return [
            'stdout' => $stdout,
            'stderr' => $stderr,
            'return' => $return
        ];
    }
...
                    // копируем все ts в один файл output.mp4 без преобразования, очень быстро
                    // при склейке обычно "убегает" звук, "-itsoffset 0.5", сдвигает видео дорожку на 500 мс
                    $command = sprintf('/usr/bin/ffmpeg -loglevel repeat+level+error -nostats -y -i "%s" -itsoffset 0.147978 -i "%s" -vcodec copy -c:a copy "output.mp4"', $m3u8files[0], $m3u8files[1]);
                    $ret = exec_return($command);
                    file_put_contents("{$forConvert->full}/converted/error", $ret['stderr']);
                    chmod("{$forConvert->full}/converted/error", 0666);                        
                    file_put_contents("{$forConvert->full}/converted/converted", $ret['stdout']);
                    chmod("{$forConvert->full}/converted/converted", 0666);

...

Generating Random Number In Each Row

Случайное число в каждой строке выборки.
Проблемка с SQL Server, в каждой строке выборки одно и то же значение, у PostgreSQL, MySQL, Oracle, SQLite всё нормально.
Выкручиваемся создав представление и функцию.
create view m_vw_randView
as
select rand() as random_number
go

create function getRand()
returns float
as
begin
    declare @returnValue float
	select @returnValue = random_number from m_vw_randView
    return @returnValue
end
go

select rand() [rand], dbo.getRand() [getRand], * from (values(1),(2),(3))t(c1)

rand			getRand			c1
0,63453611784667	0,96496883196861	1
0,63453611784667	0,0616408952549143	2
0,63453611784667	0,280773494594934	3
PostgreSQL v12
select *, random() from generate_series(1,3) t1(c1);
| c1  | random              |
| --- | ------------------- |
| 1   | 0.28302426362094124 |
| 2   | 0.38171190130398003 |
| 3   | 0.35524341401745474 |
MySQL v8.0
with recursive t1(c1) AS
(
  select 1
  union all
  select c1 + 1 from t1 where c1 + 1 <= 3
)
select *, rand() from t1;
| c1  | rand()             |
| --- | ------------------ |
| 1   | 0.8239167740791261 |
| 2   | 0.6890084265628909 |
| 3   | 0.9732918413107208 |
Oracle Database 11g
select level c1, dbms_random.value(0,1) random from dual connect by level <= 3
C1	RANDOM
1	0,457075791676919
2	0,885762460182671
3	0,555152770728942

C++ include SQLite


1. Navigate to https://www.sqlite.org/download.html and download latest `amalgamation` source version of SQLite.
2. Extract all the files into your project directory, or your include path, or separate path that you will add/added as include path in your project properties.
3. Run `Developer Command Prompt for VS ****` which is usually available at `Start -> Programs -> Visual Studio **** -> Visual Studio Tools`.
4. Navigate with command prompt to that directory where we extracted our SQLite.
5. Run next command to compile: `cl /c /EHsc sqlite3.c`
6. Run next command to create static library: `lib sqlite3.obj`
7. Open properties of your project and add `sqlite3.lib` to `Linker -> Input -> Additional Dependencies`.
Now you ready to include and use `sqlite3.h` in your project.

Простой сервис родительского контроля (simple service parental control)

По просьбе трудящихся наваял небольшой сервис, выполняет выход из сеанса пользователя (log off) или его отключение (disconnect) в определенное время с временной блокировкой учетной записи, если необходимо. Периодичность проверки попадания в график 1 минута.

Действия по блокировке или отключению учетки применяются только если сессия активна. Учетной запись разблокируется при условиях: учетная запись присутствует в графике, она заблокирована, и в этот момент не попадает в график по отключению. (раз в минуту)

Версия 1.3 — добавлена возможность отключения записи графика. У отключенных текст подкрашен серым.

UPD 09.10.2020 Версия 1.4 — добавлена группировка записей графика и множественное выделение и выключение/включение записей.

chado.zip

chado

Необходимо настроить график отключений, установить и запустить сервис. Данные графика сохраняются в SQLite. После изменения графика необходим перезапуск сервиса, так как сервисом данные считываются только один раз, при запуске. Сервис и GUI-приложение один и тот же бинарный файл, при установке сервиса приложение не копируется, сервис запускается с места нахождения chado.exe

Для disconnect и log off используются WTSDisconnectSession и WTSLogoffSession


for (auto& user : theApp.DBUsers)
{
	if (user.userName.Compare(userName) == 0 
		&& user.dayOfWeek == dayOfWeek
		&& curentSeconds >= user.allowedFrom
		&& curentSeconds <= user.allowedTo
		) {
		if (user.disconnect == 0) {
			WTSDisconnectSession(WTS_CURRENT_SERVER_HANDLE, pInfos[i].SessionId, FALSE);
			theApp.log.WriteString((date.Format(L"%Y-%m-%d %T") + L" disconnect: " + user.userName + L"\r\n").GetString());
		}
		else {
			WTSLogoffSession(WTS_CURRENT_SERVER_HANDLE, pInfos[i].SessionId, FALSE);
			theApp.log.WriteString((date.Format(L"%Y-%m-%d %T") + L" logoff: " + user.userName + L"\r\n").GetString());
		}
		if (user.lock == 0) {
			theApp.unlockAccount(user.userName.GetString(), 1);
		}
	}
}

Для блокировки/разблокировки учетной записи: NetUserGetInfo и NetUserSetInfo


	if (!lock && (ui4->usri4_flags & UF_ACCOUNTDISABLE) == UF_ACCOUNTDISABLE) {
		ui.usri1008_flags = ui4->usri4_flags ^ UF_ACCOUNTDISABLE;
		need = 2;
	}
	if (lock && (ui4->usri4_flags & UF_ACCOUNTDISABLE) != UF_ACCOUNTDISABLE) {
		ui.usri1008_flags = ui4->usri4_flags | UF_ACCOUNTDISABLE;
		need = 1;
	}

	// Call the NetUserSetInfo function
	//  to disable the account, specifying level 1008.
	if (need) {
		nStatus = NetUserSetInfo(NULL, username, 1008, (LPBYTE)&ui, NULL);
		// Display the result of the call.
		if (nStatus == NERR_Success) {
#ifdef _DEBUG
			theApp.log.WriteString((date.Format(L"%Y-%m-%d %T") + (need == 1 ? L" lock " : L" unlock ") + L"account: " + username + L"\r\n").GetString());
#endif
		}
		else {
			theApp.log.WriteString((date.Format(L"%Y-%m-%d %T") + L" error lock/unlock account: " + username + L"\r\n").GetString());
		}
	}