How I can decode TIdMessage from koi8-r to windows-1251? - indy

I use Embarcadero C++Builder 10.2 Tokyo with Indy 10.
The task is to check emails via POP3. I can get emails, but I don't know how to decode their subjects, attachment file name, etc from koi8-r to windows-1251 or UTF-8.
Can anybody help me?
The code is:
int nMess = pop3->CheckMessages();
if (nMess == 0) return;
for (int i = 1; i <= nMess; i++) {
pop3->UIDL(sl,i); // check for UID
UnicodeString sUID = sl->Strings[0]; // get one of them
if (isLetterYetRead(sUID,_idBox)) continue;
// if new, read it
pop3->Retrieve(i,IdMessage1);
int mSize = pop3->RetrieveMsgSize(i);
// parsing
for (int j = 0; j < IdMessage1->MessageParts->Count; j++) {
TIdMessagePart *mp = IdMessage1->MessageParts->Items[j];
// attacnets later
if (mp->PartType() == mptAttachment) continue;
TIdText *pt = (TIdText*) mp;
if (pt->ContentType == "text/plain" )
plain = pt->Body->Text;
else if (pt->ContentType == "text/html" ) {
html = pt->Body->Text;
// ??? bool g = NeedConvertToUnicode(IdMessage1->,pt->);
}
}
}
How to decode non-english fields?

The email is:
` Return-path: <babichev#meccanosoft.com>
Envelope-to: ear#meccanosoft.com
Delivery-date: Thu, 12 Jul 2018 14:00:33 +0300
Received: from [213.79.95.226] (helo=pccomerc)
by server120.hosting.reg.ru with esmtpa (Exim 4.90_1)
(envelope-from <babichev#meccanosoft.com>)
id 1fdZKr-0002FS-1c
for ear#meccanosoft.com; Thu, 12 Jul 2018 14:00:33 +0300
From: =?KOI8-R?B?4sHCyd7F1yDzxdLHxco=?= <babichev#meccanosoft.com>
To: ear#meccanosoft.com
Subject: test8
Date: Thu, 12 Jul 2018 14:00:29 +0300
Message-Id: <006901d419cf$8e2aeed0$aa80cc70$#meccanosoft.com>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="UMwhv1yjwbAJnDKFK8wyCH0CYSe2=_553g"
X-Mailer: Microsoft Outlook 16.0
Thread-Index: AdQZz4RBq8MEckA1Rq6W+ide2hSKlw==
Content-Language: ru
In-Reply-To: <006901d419cf$8e2aeed0$aa80cc70$#meccanosoft.com>
This is a multipart message in MIME format.
--UMwhv1yjwbAJnDKFK8wyCH0CYSe2=_553g
Content-Type: multipart/alternative;
boundary="jEgWjeMCC35bF=_m8mr8EoHknZfRtWVKzB"
--jEgWjeMCC35bF=_m8mr8EoHknZfRtWVKzB
Content-Type: text/plain; charset="koi8-r"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
TEST8
--jEgWjeMCC35bF=_m8mr8EoHknZfRtWVKzB
Content-Type: text/html; charset="koi8-r"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
<html xmlns:v=3D"urn:schemas-microsoft-com:vml" xmlns:o=3D"urn:schemas=
-microsoft-com:office:office" xmlns:w=3D"urn:schemas-microsoft-com:off=
ice:word" xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml"=
xmlns=3D"http://www.w3.org/TR/REC-html40"><head><meta http-equiv=3DCo=
ntent-Type content=3D"text/html; charset=3Dkoi8-r"><meta name=3DGenera=
tor content=3D"Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
#font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
#font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
margin-bottom:.0001pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif;
mso-fareast-language:EN-US;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:#0563C1;
text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
{mso-style-priority:99;
color:#954F72;
text-decoration:underline;}
span.EmailStyle17
{mso-style-type:personal-compose;
font-family:"Calibri",sans-serif;
color:windowtext;}
=2EMsoChpDefault
{mso-style-type:export-only;
font-family:"Calibri",sans-serif;
mso-fareast-language:EN-US;}
#page WordSection1
{size:612.0pt 792.0pt;
margin:2.0cm 42.5pt 2.0cm 3.0cm;}
div.WordSection1
{page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext=3D"edit" spidmax=3D"1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext=3D"edit">
<o:idmap v:ext=3D"edit" data=3D"1" />
</o:shapelayout></xml><![endif]--></head><body lang=3DRU link=3D"#0563=
C1" vlink=3D"#954F72"><div class=3DWordSection1><p class=3DMsoNormal><=
span lang=3DEN-US>TEST8<o:p></o:p></span></p></div></body></html>
--jEgWjeMCC35bF=_m8mr8EoHknZfRtWVKzB--
--UMwhv1yjwbAJnDKFK8wyCH0CYSe2=_553g
Content-Type: text/plain;
name="=?KOI8-R?B?0M/e1MExLnR4dA==?="
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="=?KOI8-R?B?0M/e1MExLnR4dA==?="
................................................
--UMwhv1yjwbAJnDKFK8wyCH0CYSe2=_553g--
`
Result of receiving is:
List of letters after receiving
Code is:
` TIdPOP3 *pop3 = new TIdPOP3 ();
TIdMessage *IdMessage1 = new TIdMessage();
pop3->Host = qPop3->FieldByName("pop3server")->AsString;
pop3->Port = qPop3->FieldByName("pop3port")->AsInteger;
pop3->Username = qPop3->FieldByName("pop3login")->AsString;
pop3->Password = password;
pop3->Connect();
int nMess = pop3->CheckMessages();
if (nMess == 0) return;
for (int i = 1; i <= nMess; i++) {
pop3->UIDL(sl,i); // ask for UID
UnicodeString sUID = sl->Strings[0];
pop3->Retrieve(i,IdMessage1);
int mSize = pop3->RetrieveMsgSize(i);
UnicodeString s = L"c:\\Temp\\"+sUID+".eml";
IdMessage1->SaveToFile(s,false);
// Parsing
for (int j = 0; j < IdMessage1->MessageParts->Count; j++) {
TIdMessagePart *mp = IdMessage1->MessageParts->Items[j];
// -------- and etc -------------
}
}
`
Sure I do something wrong, but what?

Related

ESP32 S2 - reading wrong time after manual set up

The issue:
I have made a WEB app for setting. It has 2 options for RTC - SNTP and manual set up.
SNTP works well. But manual not...
It received GET request like - rtc_datetime=2022-12-06T07%3A02
if(httpd_query_key_value(qry, "rtc_datetime", temp, MAX_GET_QRY_VAL_LEN) == ESP_OK) {
// decoding %3A to : etc
decode(temp,value);
//strcpy(value,temp);
ESP_LOGI(TAG, "Time is: %s", value);
if(value[0]=='\0') {
char resp[] = "Set manual time mode";
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
//tm.tm_year = (value[0] - '0')*1000 + (value[1] - '0')*100 + (value[2] - '0')*10 + (value[3] - '0') - 1900;//year
tm.tm_year = string2int(value,4) - 1900;
ESP_LOGI(TAG, "year is: %d", tm.tm_year);
//tm.tm_mon = (value[5] - '0')*10 + (value[6] - '0'); //month
tm.tm_mon = string2int(value+5,2) - 1;
ESP_LOGI(TAG, "mon is: %d", tm.tm_mon);
//tm.tm_mday = (value[8] - '0')*10 + (value[9] - '0');; // day
tm.tm_mday = string2int(value+8,2);
ESP_LOGI(TAG, "day is: %d", tm.tm_mday);
//tm.tm_hour = (value[11] - '0')*10 + (value[12] - '0'); // hour
tm.tm_hour = string2int(value+11,2);
ESP_LOGI(TAG, "hour is: %d", tm.tm_hour);
//tm.tm_min = (value[14] - '0')*10 + (value[15] - '0'); // minute
tm.tm_min = string2int(value+14,2);
ESP_LOGI(TAG, "min is: %d", tm.tm_min);
tm.tm_sec = 0;
time_t t = mktime(&tm);
ESP_LOGI(TAG, "The local date and time is: %s", asctime(&tm));
struct timeval new;
new.tv_usec = 0;
new.tv_sec = t;
settimeofday(&new, NULL);
//localtime(&t);
struct tm timeinfo2;
timeinfo2.tm_sec = 0;
timeinfo2.tm_min = 0;
time_t now2;// = time(NULL);
time(&now2);
localtime_r(&now2, &timeinfo2);
ESP_LOGI(TAG, "curr time - %4d-%2d-%2d %2d:%2d", timeinfo2.tm_year+1900, timeinfo2.tm_mon+1, timeinfo2.tm_mday, timeinfo2.tm_hour, timeinfo2.tm_min);
ESP_LOGI(TAG, "The local date and time is: %s", asctime(&timeinfo2));
char resp[] = "Time is set";
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
asctime(&tm) shows correct time from URL.
But time in timeinfo2 has difference 17 minutes and 53 seconds...
Line by line log:
I (16452) web server: GET request: form=rtc_form&rtc_datetime=2022-12-06T19%3A21&rtc_tz=458
I (16452) web server: r0_form
I (16452) web server: r1_form
I (16452) web server: rtc_form
I (16462) system config: sntp: 0
I (16462) system config: rtc_tz: 458
I (16462) system config: Save /spiffs/rtc.json
I (16492) system config: Set: {"rtc_sntp":0,"rtc_tz":458}
I (16492) web server: Time is: 2022-12-06T19:21
I (16492) web server: year is: 122
I (16492) web server: mon is: 11
I (16502) web server: day is: 6
I (16502) web server: hour is: 19
I (16512) web server: min is: 21
I (16512) web server: The local date and time is: Tue Dec 6 19:21:00 2022
I (16522) web server: curr time - 2022-12- 6 19:38
I (16522) web server: The local date and time is: Tue Dec 6 19:38:53 2022
I have no idea what I do wrong....
tm.tm_year = 2022 - 1900;
tm.tm_mon = 0;
tm.tm_mday = 0;
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
time_t t = mktime(&tm);
ESP_LOGI(TAG, "The local date and time is: %s", asctime(&tm));
struct timeval new;
new.tv_usec = 0;
new.tv_sec = t;
settimeofday(&new, NULL);
struct tm timeinfo2;
timeinfo2.tm_sec = 0;
timeinfo2.tm_min = 0;
time_t now2;
time(&now2);
localtime_r(&now2, &timeinfo2);
ESP_LOGI(TAG, "The local date and time is: %s", asctime(&timeinfo2));
So, this is the code without any data from GET request. Hm... What is a correct initialization for tm struct? I thing true is near...
mktime() "makes" this issue or tm struct is wrong initialized.
The same problem:
I (22892) web server: The local date and time is: Fri Dec 31 00:00:00 2021
I (22902) web server: The local date and time is: Fri Dec 31 00:17:53 2021
I add ctime() log for checking mktime() output, so it showes correct
time_t t = mktime(&tm);
ESP_LOGI(TAG, "Time from epoch t: %s", ctime(&t));
ESP_LOGI(TAG, "Time from tm: %s", asctime(&tm));
Console log:
I (18012) web server: Time from epoch t: Thu Dec 8 09:13:00 2022
I (18022) web server: Time from tm: Thu Dec 8 09:13:00 2022
I've found the same problem in espressif GitHub issues.
It looks like the issue is in the settimeofday() function.
So, I implemented the same workaround:
settimeofday(&new, NULL);
struct timeval get_set_time;
gettimeofday(&get_set_time,NULL);
int time_diff = get_set_time.tv_sec - new.tv_sec;
ESP_LOGI(TAG, "Difference in time: %d", time_diff);
new.tv_sec -= time_diff;
settimeofday(&new, NULL);
Log:
I (15322) web server: Difference in time: 1073
Moreover, the project was migrated to IDF 5.0 version — the bug is no longer there in the settimeofday(). So, the problem was solved.

<Windows Hello WBDI/EngineAdapter development> A question about Adapter Workflow

I got a problem when I developed the WBDI and EngineAdapter with my fingerprint module on Win10 OS.
The problem is why the EngineAdapter doesn't go into below callback functions, after I finished Step 1 and Step 2 below?
Because the document of Microsoft mentioned that below functions will be called when enrollment workflow goes through.
(https://learn.microsoft.com/zh-tw/windows/win32/secbiomet/adapter-workflow)
Below are the callback functions not be called.
(1) "EngineAdapterCreateEnrollment"
(2) "EngineAdapterSetEnrollmentParameters"
Does anyone know how to solve it?
Many thanks.
Step1
Step2
Adapter Workflow
EngineAdapter log
DllMain, Wed Mar 17 14:22:37 2021
WbioQueryEngineInterface, Wed Mar 17 14:22:37 2021
EngineAdapterAttach, Wed Mar 17 14:22:37 2021
EngineAdapterSelectCalibrationFormat, Wed Mar 17 14:22:37 2021
EngineAdapterPipelineInit, Wed Mar 17 14:22:37 2021
DllMain, Wed Mar 17 14:22:42 2021
EngineAdapterActivate, Wed Mar 17 14:22:42 2021
EngineAdapterQueryExtendedInfo, Wed Mar 17 14:22:42 2021
EngineAdapterSetAccountPolicy, Wed Mar 17 14:22:42 2021
DllMain, Wed Mar 17 14:22:43 2021
EngineAdapterClearContext, Wed Mar 17 14:22:45 2021
EngineAdapterQueryPreferredFormat, Wed Mar 17 14:22:45 2021
EngineAdapterCreateKey, Wed Mar 17 14:22:46 2021
EngineAdapterCreateKey, Wed Mar 17 14:22:46 2021
EngineAdapterAcceptSampleData, Wed Mar 17 14:22:46 2021
EngineAdapterIdentifyFeatureSet, Wed Mar 17 14:22:46 2021
EngineAdapterClearContext, Wed Mar 17 14:22:46 2021
EngineAdapterClearContext, Wed Mar 17 14:22:46 2021
WBDI log
IOCTL_BIOMETRIC_GET_ATTRIBUTES, Wed Mar 17 14:22:37 2021
IOCTL_BIOMETRIC_GET_SENSOR_STATUS, Wed Mar 17 14:22:45 2021
IOCTL_BIOMETRIC_GET_ATTRIBUTES, Wed Mar 17 14:22:45 2021
IOCTL_BIOMETRIC_CAPTURE_DATA, Wed Mar 17 14:22:45 2021
IOCTL_BIOMETRIC_CAPTURE_DATA, Wed Mar 17 14:22:45 2021
IOCTL_BIOMETRIC_GET_SENSOR_STATUS, Wed Mar 17 14:22:46 2021
EngineAdapter code (EngineAdapter.cpp)
static WINBIO_ENGINE_INTERFACE g_EngineInterface = {
#if (NTDDI_VERSION >= NTDDI_WIN10_RS4)
WINBIO_ENGINE_INTERFACE_VERSION_6,
#elif(NTDDI_VERSION >= NTDDI_WIN10_RS3)
WINBIO_ENGINE_INTERFACE_VERSION_5,
#elif(NTDDI_VERSION >= NTDDI_WIN10_RS1)
WINBIO_ENGINE_INTERFACE_VERSION_4,
#elif(NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
WINBIO_ENGINE_INTERFACE_VERSION_3,
#elif(NTDDI_VERSION >= NTDDI_WIN8)
WINBIO_ENGINE_INTERFACE_VERSION_2,
#elif
WINBIO_ENGINE_INTERFACE_VERSION_1,
#endif
WINBIO_ADAPTER_TYPE_ENGINE,
sizeof(WINBIO_ENGINE_INTERFACE),
{0xb876fdc8, 0x34e7, 0x471a, {0x82, 0xc8, 0x9c, 0xba, 0x6a, 0x35, 0x38, 0xec}},
EngineAdapterAttach,
EngineAdapterDetach,
EngineAdapterClearContext,
EngineAdapterQueryPreferredFormat,
EngineAdapterQueryIndexVectorSize,
EngineAdapterQueryHashAlgorithms,
EngineAdapterSetHashAlgorithm,
EngineAdapterAcceptSampleHint,
EngineAdapterAcceptSampleData,
EngineAdapterExportEngineData,
EngineAdapterVerifyFeatureSet,
EngineAdapterIdentifyFeatureSet,
EngineAdapterCreateEnrollment,
EngineAdapterUpdateEnrollment,
EngineAdapterGetEnrollmentStatus,
EngineAdapterGetEnrollmentHash,
EngineAdapterCheckForDuplicate,
EngineAdapterCommitEnrollment,
EngineAdapterDiscardEnrollment,
EngineAdapterControlUnit,
EngineAdapterControlUnitPrivileged,
#if(NTDDI_VERSION >= NTDDI_WIN8)
EngineAdapterNotifyPowerChange,
EngineAdapter_RESERVED,
//EngineAdapterReserved_1;
#endif//(NTDDI_VERSION >= NTDDI_WIN8)
#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
//
// V3.0 methods begin here...
//
EngineAdapterPipelineInit,
EngineAdapterPipelineCleanup,
EngineAdapterActivate,
EngineAdapterDeactivate,
EngineAdapterQueryExtendedInfo,
EngineAdapterIdentifyAll,
EngineAdapterSetEnrollmentSelector,
EngineAdapterSetEnrollmentParameters,
EngineAdapterQueryExtendedEnrollmentStatus,
EngineAdapterRefreshCache,
EngineAdapterSelectCalibrationFormat,
EngineAdapterQueryCalibrationData,
EngineAdapterSetAccountPolicy,
#endif //(NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
#if(NTDDI_VERSION >= NTDDI_WIN10_RS1)
//
// V4.0 methods begin here...
//
EngineAdapterCreateKey,
EngineAdapterIdentifyFeatureSetSecure,
#endif//(NTDDI_VERSION >= NTDDI_WIN10_RS1)
#if(NTDDI_VERSION >= NTDDI_WIN10_RS3)
//
// V5.0 methods begin here...
//
EngineAdapterAcceptPrivateSensorTypeInfo,
#endif(NTDDI_VERSION >= NTDDI_WIN10_RS3)
#if(NTDDI_VERSION >= NTDDI_WIN10_RS4)
//
// V6.0 methods begin here...
//
EngineAdapterCreateEnrollmentAuthenticated,
EngineAdapterIdentifyFeatureSetAuthenticated
#endif//(NTDDI_VERSION >= NTDDI_WIN10_RS4)
};
WBDI code
switch (ControlCode) {
//
// Mandatory IOCTLs
//
case IOCTL_BIOMETRIC_GET_ATTRIBUTES:
m_BiometricDevice->OnGetAttributes(FxRequest);
break;
case IOCTL_BIOMETRIC_RESET:
m_BiometricDevice->OnReset(FxRequest);
break;
case IOCTL_BIOMETRIC_CALIBRATE:
m_BiometricDevice->OnCalibrate(FxRequest);
break;
case IOCTL_BIOMETRIC_GET_SENSOR_STATUS:
m_BiometricDevice->OnGetSensorStatus(FxRequest);
break;
case IOCTL_BIOMETRIC_CAPTURE_DATA:
m_BiometricDevice->OnCaptureData(FxRequest);
break;
}
void
CBiometricDevice::OnGetAttributes(
_Inout_ IWDFIoRequest *FxRequest
)
{
CRequestHelper MyRequest(FxRequest); // RAII helper class
ULONG controlCode = 0;
PUCHAR inputBuffer= NULL;
SIZE_T inputBufferSize = 0;
PWINBIO_SENSOR_ATTRIBUTES sensorAttributes = NULL;
SIZE_T outputBufferSize;
//
// Get the request parameters
//
GetIoRequestParams(FxRequest,
&controlCode,
&inputBuffer,
&inputBufferSize,
(PUCHAR *)&sensorAttributes,
&outputBufferSize);
//
// Make sure we have an output buffer big enough
//
if (sensorAttributes == NULL || outputBufferSize < sizeof(DWORD))
{
// We cannot return size information.
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Output buffer NULL or too small to return size information.");
MyRequest.SetCompletionHr(E_INVALIDARG);
return;
}
// We only have one supported format, so sizeof (WINBIO_SENSOR_ATTRIBUTES) is sufficient.
if (outputBufferSize < sizeof(WINBIO_SENSOR_ATTRIBUTES))
{
// Buffer too small.
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Buffer too small - return size necessary in PayloadSize - 0x%x.", sizeof(WINBIO_SENSOR_ATTRIBUTES));
sensorAttributes->PayloadSize = (DWORD) sizeof(WINBIO_SENSOR_ATTRIBUTES);
MyRequest.SetInformation(sizeof(DWORD));
MyRequest.SetCompletionHr(S_OK);
return;
}
//
// Fill in the attribute payload structure
//
RtlZeroMemory(sensorAttributes, outputBufferSize);
sensorAttributes->PayloadSize = (DWORD) sizeof(WINBIO_SENSOR_ATTRIBUTES);
sensorAttributes->WinBioHresult = S_OK;
sensorAttributes->WinBioVersion.MajorVersion = WINBIO_WBDI_MAJOR_VERSION;
sensorAttributes->WinBioVersion.MinorVersion = WINBIO_WBDI_MINOR_VERSION;
sensorAttributes->SensorType = WINBIO_TYPE_FINGERPRINT;
sensorAttributes->SensorSubType = WINBIO_FP_SENSOR_SUBTYPE_TOUCH;// WINBIO_FP_SENSOR_SUBTYPE_SWIPE;//WINBIO_FP_SENSOR_SUBTYPE_TOUCH;
sensorAttributes->Capabilities = WINBIO_CAPABILITY_SENSOR | WINBIO_CAPABILITY_MATCHING | WINBIO_CAPABILITY_DATABASE;// WINBIO_CAPABILITY_SENSOR | WINBIO_CAPABILITY_MATCHING | WINBIO_CAPABILITY_DATABASE | WINBIO_CAPABILITY_PROCESSING;// WINBIO_CAPABILITY_SENSOR;
sensorAttributes->SupportedFormatEntries = 1;
sensorAttributes->SupportedFormat[0].Owner = WINBIO_ANSI_381_FORMAT_OWNER;
sensorAttributes->SupportedFormat[0].Type= WINBIO_ANSI_381_FORMAT_TYPE;
RtlCopyMemory(sensorAttributes->ManufacturerName, SAMPLE_MANUFACTURER_NAME, (wcslen(SAMPLE_MANUFACTURER_NAME)+1)*sizeof(WCHAR));
RtlCopyMemory(sensorAttributes->ModelName, SAMPLE_MODEL_NAME, (wcslen(SAMPLE_MODEL_NAME)+1)*sizeof(WCHAR));
RtlCopyMemory(sensorAttributes->SerialNumber, SAMPLE_SERIAL_NUMBER, (wcslen(SAMPLE_SERIAL_NUMBER)+1)*sizeof(WCHAR));
sensorAttributes->FirmwareVersion.MajorVersion = 1;
sensorAttributes->FirmwareVersion.MinorVersion = 0;
MyRequest.SetInformation(sensorAttributes->PayloadSize);
MyRequest.SetCompletionHr(S_OK);
}
void
CBiometricDevice::OnGetSensorStatus(
_Inout_ IWDFIoRequest *FxRequest
)
{
CRequestHelper MyRequest(FxRequest); // RAII helper class
ULONG controlCode = 0;
PUCHAR inputBuffer= NULL;
SIZE_T inputBufferSize = 0;
PWINBIO_DIAGNOSTICS diagnostics = NULL;
SIZE_T outputBufferSize;
//
// Get the request parameters
//
GetIoRequestParams(FxRequest,
&controlCode,
&inputBuffer,
&inputBufferSize,
(PUCHAR *)&diagnostics,
&outputBufferSize);
//
// Make sure we have an output buffer big enough
//
if (diagnostics == NULL || outputBufferSize < sizeof(DWORD))
{
// We cannot return size information.
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Output buffer NULL or too small to return size information.");
MyRequest.SetCompletionHr(E_INVALIDARG);
return;
}
if (outputBufferSize < sizeof(WINBIO_DIAGNOSTICS))
{
// Buffer too small.
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Buffer too small - return size necessary in PayloadSize - 0x%x.", sizeof(WINBIO_DIAGNOSTICS));
diagnostics->PayloadSize = (DWORD)sizeof(WINBIO_DIAGNOSTICS);
MyRequest.SetInformation(sizeof(DWORD));
MyRequest.SetCompletionHr(S_OK);
return;
}
//
// Fill in the OUT payload structure
//
RtlZeroMemory(diagnostics, outputBufferSize);
diagnostics->PayloadSize = (DWORD) sizeof(WINBIO_DIAGNOSTICS);
diagnostics->WinBioHresult = S_OK;
//diagnostics->SensorStatus = WINBIO_SENSOR_READY;
if (SensorStatusFlag)
{
SensorStatusFlag = 0;
diagnostics->SensorStatus = WINBIO_SENSOR_ACCEPT;
}
else
{
diagnostics->SensorStatus = WINBIO_SENSOR_READY;
}
MyRequest.SetInformation(diagnostics->PayloadSize);
MyRequest.SetCompletionHr(S_OK);
}
void
CBiometricDevice::OnCaptureData(
_Inout_ IWDFIoRequest *FxRequest
)
{
ULONG controlCode = 0;
PWINBIO_CAPTURE_PARAMETERS captureParams = NULL;
SIZE_T inputBufferSize = 0;
PWINBIO_CAPTURE_DATA captureData = NULL;
SIZE_T outputBufferSize = 0;
//
// We can only have one outstanding data capture request at a time.
// Check to see if we have a request pending.
//
bool requestPending = false;
EnterCriticalSection(&m_RequestLock);
if (m_PendingRequest == NULL)
{
//
// See if we have an active sleep thread.
// If so, tell it to exit.
// Wait for it to exit.
//
if (m_SleepThread != INVALID_HANDLE_VALUE)
{
LeaveCriticalSection(&m_RequestLock);
// TODO: Add code to signal thread to exit.
// NOTE: Sleeping for INFINITE time is dangerous. A real driver
// should be able to handle the case where the thread does
// not exit.
WaitForSingleObject(m_SleepThread, INFINITE);
CloseHandle(m_SleepThread);
m_SleepThread = INVALID_HANDLE_VALUE;
EnterCriticalSection(&m_RequestLock);
}
//
// We might have had to leave the CS to wait for the sleep thread.
// Double check that the pending request is still NULL.
//
if (m_PendingRequest == NULL)
{
// Save the request.
m_PendingRequest = FxRequest;
// Mark the request as cancellable.
m_PendingRequest->MarkCancelable(this);
}
else
{
requestPending = true;
}
}
else
{
requestPending = true;
}
LeaveCriticalSection(&m_RequestLock);
if (requestPending)
{
// Complete the request to tell the app that there is already
// a pending data collection request.
FxRequest->Complete(WINBIO_E_DATA_COLLECTION_IN_PROGRESS);
return;
}
//
// Get the request parameters
//
GetIoRequestParams(FxRequest,
&controlCode,
(PUCHAR *)&captureParams,
&inputBufferSize,
(PUCHAR *)&captureData,
&outputBufferSize);
//
// Check input parameters.
//
if (inputBufferSize < sizeof (WINBIO_CAPTURE_PARAMETERS))
{
// Invalid arguments
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Invalid argument(s).");
CompletePendingRequest(E_INVALIDARG, 0);
return;
}
//
// Make sure we have an output buffer big enough
//
if (outputBufferSize < sizeof(DWORD))
{
// We cannot return size information.
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Output buffer NULL or too small to return size information.");
CompletePendingRequest(E_INVALIDARG, 0);
return;
}
//
// Check output buffer size.
//
if (outputBufferSize < sizeof (WINBIO_CAPTURE_DATA))
{
// Buffer too small.
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Buffer too small - must be at least 0x%x.", sizeof (WINBIO_CAPTURE_DATA));
//
// NOTE: The output buffer size necessary for this sample is sizeof(WINBIO_CAPTURE_DATA).
// Real devices will need additional space to handle a typical capture.
// The value that should be returned here is sizeof(WINBIO_CAPTURE_DATA) + CaptureBufferSize.
//
captureData->PayloadSize = (DWORD)sizeof(WINBIO_CAPTURE_DATA)+12;
CompletePendingRequest(S_OK, sizeof(DWORD));
return;
}
//
// NOTE: This call always fails in this sample since it is not
// written for a real device.
//
//
// Set default values in output buffer.
//
SensorStatusFlag = 1;
RtlZeroMemory(captureData, outputBufferSize);
captureData->PayloadSize = (DWORD)outputBufferSize;
captureData->WinBioHresult = S_OK;
captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
captureData->RejectDetail = 0;
captureData->CaptureData.Size = (DWORD)12;
UCHAR szBuffer[] = { 0x01, 0x02, 0x03, 0x04 ,0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04 };
RtlCopyMemory(captureData->CaptureData.Data, szBuffer,12);
//
// Check purpose, format and type.
//
if (captureParams->Purpose == WINBIO_NO_PURPOSE_AVAILABLE)
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_PURPOSE;
}
else if ((captureParams->Format.Type != WINBIO_ANSI_381_FORMAT_TYPE) ||
(captureParams->Format.Owner != WINBIO_ANSI_381_FORMAT_OWNER))
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_FORMAT;
}
else if (captureParams->Flags != WINBIO_DATA_FLAG_RAW)
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_TYPE;
}
//
// Create thread to sleep 1 seconds before completing the request.
//
m_SleepParams.SleepValue = 1;
m_SleepParams.Hr = S_OK;
m_SleepParams.Information = captureData->PayloadSize;
m_SleepThread = CreateThread(NULL, // default security attributes
0, // use default stack size
CaptureSleepThread, // thread function name
this, // argument to thread function
0, // use default creation flags
NULL); // returns the thread identifier
}
DWORD WINAPI
CaptureSleepThread(
LPVOID lpParam
)
{
CBiometricDevice *device = (CBiometricDevice *) lpParam;
PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams();
//
// Make sure it is less than or equal to 1 minute.
//
if (sleepParams->SleepValue > 60)
{
sleepParams->SleepValue = 60;
}
Sleep(sleepParams->SleepValue * 1000);
device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information);
return 0;
}

Websocket handshake: Incorrect 'Sec-WebSocket-Accept' header value

I am writing a websocket server in C++ and am not able to get the handshake to work. Chrome reports the error is due to a bad accept header, but I believe the value to be correct.
As one example exchange, the client sends the following key:
Sec-WebSocket-Key: ypX0m2zum/pt80mxlVo8PA==
and my server sends back:
Sec-WebSocket-Accept: Kl4mnqm5QA6bBmGf3EAN0nyGXws=
I have tested my server against the example in the RFC and it checks out. I don't know why its not being accepted. My theory is that I must be doing something else that generates the same error as a bad accept value.
Here is different request from a wireshark capture:
Hypertext Transfer Protocol
GET /websocket HTTP/1.1\r\n
Host: 127.0.0.1:8443\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
Upgrade: websocket\r\n
Origin: chrome-extension://eajaahbjpnhghjcdaclbkeamlkepinbl\r\n
Sec-WebSocket-Version: 13\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: en-US,en;q=0.9\r\n
Sec-WebSocket-Key: +zJ3/KI/Zrumgh+AjxopRQ==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n
\r\n
[Full request URI: http://127.0.0.1:8443/websocket]
[HTTP request 1/1]
[Response in frame: 6]
And here is the response:
Hypertext Transfer Protocol
HTTP/1.1 101 Switching Protocols\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: anTEIFyI/gTepr8Q3okBj81M2/4=\r\n
\r\n
[HTTP response 1/1]
[Time since request: 0.000245010 seconds]
[Request in frame: 4]
Can someone tell me what is wrong with the response? Is my accept value incorrect?
EDIT 1:
The code I use to create the response value. The websocket_key is grabbed from the request prior to this.
const char *magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
int pre_hash_size = 36 + websocket_key.size();
char pre_hash[pre_hash_size];
memcpy(pre_hash, websocket_key.c_str(), websocket_key.size());
memcpy(pre_hash + websocket_key.size(), magic_string, 36);
unique_ptr<Botan::HashFunction> hash1(Botan::HashFunction::create("SHA-1"));
Botan::secure_vector<uint8_t> post_hash = hash1->process(reinterpret_cast<const uint8_t *>(pre_hash), pre_hash_size);
string accept_response = base64_encode(post_hash.data(), post_hash.size());
Here is the base 64 function:
/*
base64.cpp and base64.h
base64 encoding and decoding with C++.
Version: 1.01.00
Copyright (C) 2004-2017 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger#adp-gmbh.ch
*/
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string base64_encode(unsigned char const *bytes_to_encode, unsigned int in_len)
{
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--)
{
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3)
{
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
}
The problem was that when I concatenated the pre_hash string from the websocket key (sent by client) and the magic string (constant), I didn't account for the null terminator that the size() function includes in it's count. an extra space I had inadvertently added when parsing the request header.
Remember kiddies, C++ strings are null terminated and size() reflects that.

How can I properly read the sequence of bytes from a hyper::client::Request and print it to the console as a UTF-8 string?

I am exploring Rust and trying to make a simple HTTP request (using the hyper crate) and print the response body to the console. The response implements std::io::Read. Reading various documentation sources and basic tutorials, I have arrived at the following code, which I compile & execute using RUST_BACKTRACE=1 cargo run:
use hyper::client::Client;
use std::io::Read;
pub fn print_html(url: &str) {
let client = Client::new();
let req = client.get(url).send();
match req {
Ok(mut res) => {
println!("{}", res.status);
let mut body = String::new();
match res.read_to_string(&mut body) {
Ok(body) => println!("{:?}", body),
Err(why) => panic!("String conversion failure: {:?}", why)
}
},
Err(why) => panic!("{:?}", why)
}
}
Expected:
A nice, human-readable HTML content of the body, as delivered by the HTTP server, is printed to the console.
Actual:
200 OK
thread '<main>' panicked at 'String conversion failure: Error { repr: Custom(Custom { kind: InvalidData, error: StringError("stream did not contain valid UTF-8") }) }', src/printer.rs:16
stack backtrace:
1: 0x109e1faeb - std::sys::backtrace::tracing::imp::write::h3800f45f421043b8
2: 0x109e21565 - std::panicking::default_hook::_$u7b$$u7b$closure$u7d$$u7d$::h0ef6c8db532f55dc
3: 0x109e2119e - std::panicking::default_hook::hf3839060ccbb8764
4: 0x109e177f7 - std::panicking::rust_panic_with_hook::h5dd7da6bb3d06020
5: 0x109e21b26 - std::panicking::begin_panic::h9bf160aee246b9f6
6: 0x109e18248 - std::panicking::begin_panic_fmt::haf08a9a70a097ee1
7: 0x109d54378 - libplayground::printer::print_html::hff00c339aa28fde4
8: 0x109d53d76 - playground::main::h0b7387c23270ba52
9: 0x109e20d8d - std::panicking::try::call::hbbf4746cba890ca7
10: 0x109e23fcb - __rust_try
11: 0x109e23f65 - __rust_maybe_catch_panic
12: 0x109e20bb1 - std::rt::lang_start::hbcefdc316c2fbd45
13: 0x109d53da9 - main
error: Process didn't exit successfully: `target/debug/playground` (exit code: 101)
Thoughts
Since I received 200 OK from the server, I believe I have received a valid response from the server (I can also empirically prove this by doing the same request in a more familiar programming language). Therefore, the error must be caused by me incorrectly converting the byte sequence into an UTF-8 string.
Alternatives
I also attempted the following solution, which gets me to a point where I can print the bytes to the console as a series of hex strings, but I know that this is fundamentally wrong because a UTF-8 character can have 1-4 bytes. Therefore, attempting to convert individual bytes into UTF-8 characters in this example will work only for a very limited (255, to be exact) subset of UTF-8 characters.
use hyper::client::Client;
use std::io::Read;
pub fn print_html(url: &str) {
let client = Client::new();
let req = client.get(url).send();
match req {
Ok(res) => {
println!("{}", res.status);
for byte in res.bytes() {
print!("{:x}", byte.unwrap());
}
},
Err(why) => panic!("{:?}", why)
}
}
We can confirm with the iconv command that the data returned from http://www.google.com is not valid UTF-8:
$ wget http://google.com -O page.html
$ iconv -f utf-8 page.html > /dev/null
iconv: illegal input sequence at position 5591
For some other urls (like http://www.reddit.com) the code works fine.
If we assume that the most part of the data is valid UTF-8, we can use String::from_utf8_lossy to workaround the problem:
pub fn print_html(url: &str) {
let client = Client::new();
let req = client.get(url).send();
match req {
Ok(mut res) => {
println!("{}", res.status);
let mut body = Vec::new();
match res.read_to_end(&mut body) {
Ok(_) => println!("{:?}", String::from_utf8_lossy(&*body)),
Err(why) => panic!("String conversion failure: {:?}", why),
}
}
Err(why) => panic!("{:?}", why),
}
}
Note that that Read::read_to_string and Read::read_to_end return Ok with the number of read bytes on success, not the read data.
If you actually look at the headers that Google returns:
HTTP/1.1 200 OK
Date: Fri, 22 Jul 2016 20:45:54 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info."
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Set-Cookie: NID=82=YwAD4Rj09u6gUA8OtQH73BUz6UlNdeRc9Z_iGjyaDqFdRGMdslypu1zsSDWQ4xRJFyEn9-UtR7U6G7HKehoyxvy9HItnDlg8iLsxzlhNcg01luW3_-HWs3l9S3dmHIVh; expires=Sat, 21-Jan-2017 20:45:54 GMT; path=/; domain=.google.ca; HttpOnly
Alternate-Protocol: 443:quic
Alt-Svc: quic=":443"; ma=2592000; v="36,35,34,33,32,31,30,29,28,27,26,25"
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked
You can see
Content-Type: text/html; charset=ISO-8859-1
Additionally
Therefore, the error must be caused by me incorrectly converting the byte sequence into an UTF-8 string.
There is no conversion to UTF-8 happening. read_to_string simply ensures that the data is UTF-8.
Simply put, assuming that an arbitrary HTML page is encoded in UTF-8 is completely incorrect. At best, you have to parse the headers to find the encoding and then convert the data. This is complicated because there's no real definition for what encoding the headers are in.
Once you have found the correct encoding, you can use a crate such as encoding to properly transform the result into UTF-8, if the result is even text! Remember that HTTP can return binary files such as images.

Why the gmail API sends html emails as plain text?

I'm trying to send a html email using the gmail API but for some reasons it randomly sends the email as plain/text. It seems that Google alters the content type header I set. Is there any reason for that? The email content is exactly same all the time (as I test it). Is the API still experimental?
Sometimes when it works it also adds Content-Type: multipart/alternative; (although I never set it).
The encoding process looks as below. The code is Go but I guess it self explanatory and the process is language agnostic.
header := make(map[string]string)
header["From"] = em.From.String()
header["To"] = em.To.String()
// header["Subject"] = encodeRFC2047(em.Subject)
header["Subject"] = em.Subject
header["MIME-Version"] = "1.0"
header["Content-Type"] = "text/html; charset=\"utf-8\""
// header["Content-Transfer-Encoding"] = "base64"
header["Content-Transfer-Encoding"] = "quoted-printable"
var msg string
for k, v := range header {
msg += fmt.Sprintf("%s: %s\r\n", k, v)
}
msg += "\r\n" + em.Message
gmsg := gmail.Message{
Raw: encodeWeb64String([]byte(msg)),
}
_, err = gmailService.Users.Messages.Send("me", &gmsg).Do()
Hmm, are you sure it's not a bug in your program? Can you print out the entire string and paste it here?
I just used the Gmail API to send an email like:
To: <redacted>
Subject: test html email 2015-01-14 09:45:40
Content-type: text/html
<html><body><b>hello</b>world</body></html>
and it looked as expected by the recipient's end in Gmail. Well, actually looks like it got wrapped it in a multipart/alternative and added a text/plain part as well (good thing IMO):
<random trace headers>
MIME-Version: 1.0
From: <redacted>
Date: Wed, 14 Jan 2015 09:46:41 -0800
Message-ID:
Subject: test html email 2015-01-14 09:45:40
To: <redacted>
Content-Type: multipart/alternative; boundary=089e0141a9a2875c38050ca05201
--089e0141a9a2875c38050ca05201
Content-Type: text/plain; charset=UTF-8
*hello*world
--089e0141a9a2875c38050ca05201
Content-Type: text/html; charset=UTF-8
<html><body><b>hello</b>world</body></html>
--089e0141a9a2875c38050ca05201--
In any case, it's doing some parsing/sanitizing but does allow sending text/html email.

Resources