Save email message as eml using C# in Lotus Notes - c#

I need to export (save to) hard drive my Lotus Notes emails.
I figured out the way how to save attachments to HDD, but I can't figure out the way of how to save the whole email.
The code below shows how I export attachments. Can you suggest how can I modify it to save emails?
PS- I am new to programming.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Domino;
using System.Collections;
namespace ExportLotusAttachments
{
class Class1
{
public void ScanForEmails()
{
String textBox1 = "c:\\1";
NotesSession session = new NotesSession();
session.Initialize("");
NotesDbDirectory dir = null;
dir = session.GetDbDirectory("");
NotesDatabase db = null;
db = dir.OpenMailDatabase();
NotesDatabase NDb = dir.OpenMailDatabase(); //Database connection
//ArrayList that will hold names of the folders
ArrayList LotusViews2 = new ArrayList();
foreach (NotesView V in NDb.Views)
{
if (V.IsFolder && !(V.Name.Equals("($All)")))
{
NotesView getS = V;
LotusViews2.Add(getS.Name);
}
}
foreach (String obj in LotusViews2)
{
NotesDocument NDoc;
NotesView nInboxDocs = NDb.GetView(obj);
NDoc = nInboxDocs.GetFirstDocument();
String pAttachment;
while (NDoc != null)
{
if (NDoc.HasEmbedded && NDoc.HasItem("$File"))
{
object[] AllDocItems = (object[])NDoc.Items;
foreach (object CurItem in AllDocItems)
{
NotesItem nItem = (NotesItem)CurItem;
if (IT_TYPE.ATTACHMENT == nItem.type)
{
String path = textBox1;
pAttachment = ((object[])nItem.Values)[0].ToString();
if (!System.IO.Directory.Exists(path))
{
System.IO.Directory.CreateDirectory(textBox1);
}
try
{
NDoc.GetAttachment(pAttachment).ExtractFile(#path + pAttachment);
}
catch { }
}
}
}
NDoc = nInboxDocs.GetNextDocument(NDoc);
}
}
}
}
}

This post by Bob Babalan explains how to export lotus documents using Java. The same principle should work in C# or VB. The document is cnverted into MIME and written to the disk.
Or in version 8.5.3 (I think it started witn 8.5.1) you can just drag and drop it from the mail file to the file system.

I know it is a bit late, but this is, what I did. (Based on Bob Babalan)
Bobs Solution helped me alot to understand NotesMIMEEntities, but in his solution, he only traversed the MIME-Tree to the second "layer". This will traverse multiple layers.
public static void GetMIME(StreamWriter writer, NotesMIMEEntity mimeEntity)
{
try
{
string contentType = null;
string headers = null;
string content = null;
string preamble = null;
MIME_ENCODING encoding;
contentType = mimeEntity.ContentType;
headers = mimeEntity.Headers;
encoding = mimeEntity.Encoding;
// message envelope. If no MIME-Version header, add one
if (!headers.Contains("MIME-Version:"))
writer.WriteLine("MIME-Version: 1.0");
writer.WriteLine(headers);
// for multipart, usually no main-msg content...
content = mimeEntity.ContentAsText;
if (content != null && content.Trim().Length > 0)
writer.WriteLine(content);
writer.Flush();
if (contentType.StartsWith("multipart"))
{
preamble = mimeEntity.Preamble;
NotesMIMEEntity mimeChild = mimeEntity.GetFirstChildEntity();
while (mimeChild != null)
{
GetMimeChild(writer, mimeChild);
mimeChild = mimeChild.GetNextSibling();
}
}
writer.WriteLine(mimeEntity.BoundaryEnd);
writer.Flush();
}
catch (Exception ex)
{
Logging.Log(ex.ToString());
}
}
private void GetMimeChild(StreamWriter writer, NotesMIMEEntity mimeEntity)
{
string contentType = null;
string headers = null;
string content = null;
string preamble = null;
MIME_ENCODING encoding;
contentType = mimeEntity.ContentType;
headers = mimeEntity.Headers;
encoding = mimeEntity.Encoding;
if (encoding == MIME_ENCODING.ENC_IDENTITY_BINARY)
{
mimeEntity.EncodeContent(MIME_ENCODING.ENC_BASE64);
headers = mimeEntity.Headers;
}
preamble = mimeEntity.Preamble;
writer.Write(mimeEntity.BoundaryStart);
if (!content.EndsWith("\n"))
writer.WriteLine("");
writer.WriteLine(headers);
writer.WriteLine();
writer.Write(mimeEntity.ContentAsText);
if (contentType.StartsWith("multipart"))
{
preamble = mimeEntity.Preamble;
NotesMIMEEntity mimeChild = mimeEntity.GetFirstChildEntity();
while (mimeChild != null)
{
GetMimeChild(writer, mimeChild);
mimeChild = mimeChild.GetNextSibling();
}
}
writer.Write(mimeEntity.BoundaryEnd);
writer.Flush();
}
I would call this methods like this, to save the EML-File to a given path.
using (FileStream fs = new FileStream (path,FileMode.Create,FileAccess.ReadWrite,FileShare.None))
{
using (StreamWriter writer = new StreamWriter(fs))
{
NotesMimeEntity mimeEntity = notesDocument.GetMIMEEntity();
if (mimeEntity != null)
GetMIME(writer, mimeEntity);
}
}

Related

Cannot access disposed object application connecting to WebAPI

I have content of video and object being created an pass into a http client web api. When ever I pass the image to the client it works find it gets to the post method, but when it comes to the video the client has trouble posting the video. I checked the video size length to make sure it meets the content length and it well under the specific ranges. The error that I receive is that the object has been disposed. If you look at the code the object is never disposed.
Here's the code on the app
public async Task<bool> AddToQueueAsync(Incident i, ContentPage page, MediaFile file)
{
HttpResponseMessage result = null;
Uri webserviceURL = i.IncidentType == IncidentType.Trooper ? trooperURL : gspURL;
var fileStream = File.Open(file.Path, FileMode.Open);
try
{
using (var client = new HttpClient())
{
using (fileStream)
{
using (var stream = new StreamContent(fileStream))
{
using (var content = new MultipartFormDataContent("----MyBoundary"))
{
if(i.MediaType == "Video")
{
content.Add(stream,"file", Guid.NewGuid().ToString() + ".mp4");
}
else
{
content.Add(stream, "file", Guid.NewGuid().ToString() + ".png");
}
content.Add(new StringContent(JsonConvert.SerializeObject(i)), "metadata");
result = await client.PostAsync(webserviceURL, content);
}
}
}
}
Here is the code on the web api:
[HttpPost]
public IHttpActionResult StarGSPDATA() {
try {
if(!Request.Content.IsMimeMultipartContent()) {
Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
starGSPDATAinfo suspicousInfo;
string homeDir = AppDomain.CurrentDomain.BaseDirectory;
string dir = $"{homeDir}/uploads/";
Directory.CreateDirectory(dir);
var file = HttpContext.Current.Request.Files.Count > 0 ?
HttpContext.Current.Request.Files[0] : null;
if(HttpContext.Current.Request.Form.Count > 0) {
suspicousInfo = MetaDataFromRequest(HttpContext.Current.Request.Form);
} else {
suspicousInfo = new starGSPDATAinfo();
}
if(file != null && file.ContentLength > 0) {
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(dir, fileName);
suspicousInfo.MediaFilePath = fileName;
try {
file.SaveAs(path);
} catch(Exception e) {
Console.WriteLine($"not saving: {e.ToString()}");
}
} else {
throw new HttpResponseException(
new HttpResponseMessage(
HttpStatusCode.NoContent));
}
CleanData(suspicousInfo);
db.starGSPDATAinfoes.Add(suspicousInfo);
db.SaveChanges();
return Created("http://localhost:50641/api/StarGSPDATA/", JsonConvert.SerializeObject(suspicousInfo));
} catch(Exception e) {
return InternalServerError(e);
}
}
It works for an image but not for a video Please help thank you!
Here is a picture of the error

File used by another process ; PDF.JS ; iText

I'm using PDF.JS to display document that I upload to the server in canvas element using PDF.JS that's working perfectely. That time i'm using iTextSharp to digitally sign the document. When i try to sign the document an Exception is throwed (Exception.IO.Exception) The file is already used by another process. here is my Code for uploding the file :)
[HttpPost]
public async Task<JsonResult> Upload()
{
string fileName = null;
try
{
foreach (string item in Request.Files)
{
var fileContent = Request.Files[item];
if(fileContent != null && fileContent.ContentLength > 0)
{
var inputStream = fileContent.InputStream;
fileName = fileContent.FileName;
string path = Path.Combine(Server.MapPath("~/UploadFolder"), fileName);
using (fileContent.InputStream)
{
using (var stream = new FileStream(path, FileMode.Create))
{
await inputStream.CopyToAsync(stream);
}
}
}
}
}
catch (Exception e)
{
return Json("Upload failed");
}
return Json(fileName);
}
There's how i display PDF in canvas
$(document).ready(function () {
$("#btn2").click(function () {
var url = document.getElementById("document-to-sign").getAttribute("required-document");
if (url != "" && url != null) {
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 1.5,
canvas = document.getElementById('document-to-sign'),
ctx = canvas.getContext('2d');
function renderPage(num) {
pageRendering = true;
pdfDoc.getPage(num).then(function (page) {
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function () {
pageRendering = false;
if (pageNumPending !== null) {
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
document.getElementById('page_num').textContent = pageNum;
}
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('next').addEventListener('click', onNextPage);
PDFJS.getDocument(url).then(function (pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;
renderPage(pageNum);
});
PDFJS.disableStream = true;
$("#document-to-sign").removeAttr("required-document");
}
});
I finally that's how i'm signing the document (Adding the empty field to sign)
public static void AddField(string src,
Double x1X, Double x1Y, Double x2X, Double x2Y, int page,
string User)
{
try
{
PdfReader reader = new PdfReader(src);
using (PdfStamper s = new PdfStamper(reader, new FileStream(src, FileMode.Open)))
{
PdfFormField field = PdfFormField.CreateSignature(s.Writer);
field.FieldName = "Signature de " + User;
field.SetWidget(new Rectangle(Convert.ToSingle(x1X), Convert.ToSingle(x1Y), Convert.ToSingle(x2X), Convert.ToSingle(x2Y)), PdfAnnotation.HIGHLIGHT_PUSH);
field.Flags = PdfAnnotation.FLAGS_PRINT;
s.AddAnnotation(field, page);
}
}
catch (Exception e)
{
logger.Fatal(e.ToString());
throw e;
}
}
I'm stacked in this line
using (PdfStamper s = new PdfStamper(reader, new FileStream(src, FileMode.Open)))
EDIT:
I'm just adding the siging field in this step. Signing the document will be the next task, in console application i'm singing the document with a self-certificate.
Upload the document, and adding the signing field and signing it will be further :)
Sorry for the confussion.
Thanks a lot. :)
I just found what i'm missing in reading the file
refer to this
Cannot access the file because it is being used by another process
i was passing the url of the file instead of reading the all bytes from stream
PdfReader reader = new PdfReader(src);
PdfReader reader = new PdfReader(System.IO.File.ReadAllBytes(filePath))

C# Null reference exception and StreamReader

I am getting null reference exception when reading data from my txt file.
public class Appointments : List<Appointment>
{
Appointment appointment;
public Appointments()
{
}
public bool Load(string fileName)
{
string appointmentData = string.Empty;
using (StreamReader reader = new StreamReader(fileName))
{
while((appointmentData = reader.ReadLine()) != null)
{
appointmentData = reader.ReadLine();
//**this is where null ref. exception is thrown** (line below)
if(appointmentData[0] == 'R')
{
appointment = new RecurringAppointment(appointmentData);
}
else
{
appointment = new Appointment(appointmentData);
}
this.Add(appointment);
}
return true;
}
}
RecurringAppointment inherits from Appointments. File exists, file location is correct. Funny thing is that program was working 30 min ago I've only changed Load method from below to what u can see above :
public bool Load(string fileName)
{
string appointmentData = string.Empty;
using (StreamReader reader = new StreamReader(fileName))
{
while((appointmentData = reader.ReadLine()) != null)
{
appointmentData = reader.ReadLine();
if(appointmentData[0] == 'R')
{
this.Add(appointment = new RecurringAppointment(appointmentData));
}
else
{
this.Add(appointment = new Appointment(appointmentData));
}
}
return true;
}
}
Now it does not work in either case.
Your code reads two times at each loop. This means that, if your file has an odd number of rows when you read the last line of the file, the check against null inside the while statement allows your code to enter the loop but the following ReadLine returns a null string. Of course trying to read the char at index zero of a null string will throw the NRE exception.
There is also the problem of empty lines in your file. If there is an empty line then, again reading at index zero will throw an Index out of range exception
You could fix your code in this way
public bool Load(string fileName)
{
string appointmentData = string.Empty;
using (StreamReader reader = new StreamReader(fileName))
{
while((appointmentData = reader.ReadLine()) != null)
{
if(!string.IsNullOrWhiteSpace(appointmentData))
{
if(appointmentData[0] == 'R')
this.Add(appointment = new RecurringAppointment(appointmentData));
else
this.Add(appointment = new Appointment(appointmentData));
}
}
return true;
}
}

Load a xml file using XDocument.Load from FileSystemWatcher. Error "The process cannot access the file..etc"

Is there a way to fix the error "The process cannot access the file..etc". The flow is that the filesystemwatcher will watch for a xml file when I detects a xml file i need to read a specific node from the xml file.
How can I fix this? Any ideas or suggestions will be a big help. Thanks
Here is the filesystemwatcher code
private void fileSystemWatcher_Created(object sender, System.IO.FileSystemEventArgs e)
{
try
{
string type = GetType(e.FullPath).ToUpper();
if (type == "CC")
{
if (Global.pc_flag)
{
ProcessPC(e.FullPath);
}
else if (Global.mw_flag)
{
ProcessMW(e.FullPath);
}
else
{
ProcessXML(e.FullPath);
}
}
else if (type == "GC")
{
ProcessMW(e.FullPath);
}
//Process(e.FullPath);
}
catch(Exception ex)
{
error++;
lblErrors.Text = error.ToString();
MessageBox.Show(ex.Message);
}
}
Here what contains of GetType
private string GetType(string file)
{
string type = string.Empty;
using (var stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var request = XDocument.Load(stream);
var get_command = from r in request.Descendants("Transaction")
select new
{
Type = r.Element("Type").Value
};
foreach (var c in get_command)
{
type = c.Type;
}
}
return type;
}
You don't use your stream in code and while the stream is open you can not access the file in XDocument.Load(file)
private string GetType(string file)
{
string type = string.Empty;
var request = XDocument.Load(file);
var get_command = from r in request.Descendants("Transaction")
select new
{
Type = r.Element("Type").Value
};
foreach (var c in get_command)
{
type = c.Type;
}
return type;
}

PgP Encryption and Decryption using BouncyCastle c#

I've seen a number of posts, followed a number of tutorials but none seems to work. Sometimes, they make reference to some classes which are not found. Can I be pointed to a place where I can get a simple tutorial showing how to encrypt and decrypt a file.
I'm very new to Pgp and any assistance is welcomed.
I know this question is years old but it is still #1 or #2 in Google for searches related to PGP Decryption using Bouncy Castle. Since it seems hard to find a complete, succinct example I wanted to share my working solution here for decrypting a PGP file. This is simply a modified version of the Bouncy Castle example included with their source files.
using System;
using System.IO;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Utilities.IO;
namespace PGPDecrypt
{
class Program
{
static void Main(string[] args)
{
DecryptFile(
#"path_to_encrypted_file.pgp",
#"path_to_secret_key.asc",
"your_password_here".ToCharArray(),
"output.txt"
);
}
private static void DecryptFile(
string inputFileName,
string keyFileName,
char[] passwd,
string defaultFileName)
{
using (Stream input = File.OpenRead(inputFileName),
keyIn = File.OpenRead(keyFileName))
{
DecryptFile(input, keyIn, passwd, defaultFileName);
}
}
private static void DecryptFile(
Stream inputStream,
Stream keyIn,
char[] passwd,
string defaultFileName)
{
inputStream = PgpUtilities.GetDecoderStream(inputStream);
try
{
PgpObjectFactory pgpF = new PgpObjectFactory(inputStream);
PgpEncryptedDataList enc;
PgpObject o = pgpF.NextPgpObject();
//
// the first object might be a PGP marker packet.
//
if (o is PgpEncryptedDataList)
{
enc = (PgpEncryptedDataList)o;
}
else
{
enc = (PgpEncryptedDataList)pgpF.NextPgpObject();
}
//
// find the secret key
//
PgpPrivateKey sKey = null;
PgpPublicKeyEncryptedData pbe = null;
PgpSecretKeyRingBundle pgpSec = new PgpSecretKeyRingBundle(
PgpUtilities.GetDecoderStream(keyIn));
foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
{
sKey = FindSecretKey(pgpSec, pked.KeyId, passwd);
if (sKey != null)
{
pbe = pked;
break;
}
}
if (sKey == null)
{
throw new ArgumentException("secret key for message not found.");
}
Stream clear = pbe.GetDataStream(sKey);
PgpObjectFactory plainFact = new PgpObjectFactory(clear);
PgpObject message = plainFact.NextPgpObject();
if (message is PgpCompressedData)
{
PgpCompressedData cData = (PgpCompressedData)message;
PgpObjectFactory pgpFact = new PgpObjectFactory(cData.GetDataStream());
message = pgpFact.NextPgpObject();
}
if (message is PgpLiteralData)
{
PgpLiteralData ld = (PgpLiteralData)message;
string outFileName = ld.FileName;
if (outFileName.Length == 0)
{
outFileName = defaultFileName;
}
Stream fOut = File.Create(outFileName);
Stream unc = ld.GetInputStream();
Streams.PipeAll(unc, fOut);
fOut.Close();
}
else if (message is PgpOnePassSignatureList)
{
throw new PgpException("encrypted message contains a signed message - not literal data.");
}
else
{
throw new PgpException("message is not a simple encrypted file - type unknown.");
}
if (pbe.IsIntegrityProtected())
{
if (!pbe.Verify())
{
Console.Error.WriteLine("message failed integrity check");
}
else
{
Console.Error.WriteLine("message integrity check passed");
}
}
else
{
Console.Error.WriteLine("no message integrity check");
}
}
catch (PgpException e)
{
Console.Error.WriteLine(e);
Exception underlyingException = e.InnerException;
if (underlyingException != null)
{
Console.Error.WriteLine(underlyingException.Message);
Console.Error.WriteLine(underlyingException.StackTrace);
}
}
}
private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyID, char[] pass)
{
PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyID);
if (pgpSecKey == null)
{
return null;
}
return pgpSecKey.ExtractPrivateKey(pass);
}
}
}
I have used PgpCore package which is a wrapper around Portable.BouncyCastle.
It is very clean and simple to use. Multiple examples available here.
How's this:
PartialInputStream during Bouncycastle PGP decryption
Also, the zip contains examples here:
http://www.bouncycastle.org/csharp/
Hope this helps. If you're still stuck, post some more detail about what classes the compiler is complaining about and the community will take a look.
Now, in 2021, Nikhil's answer is probably best, since it abstracts out the need for working with BouncyCastle directly. Go give him an upvote.
If you want to work with BouncyCastle directly for some reason, I've got a modern implementation of Dan's answer, and the examples he's working from, that uses BouncyCastle directly in NET5. Take a look:
using Org.BouncyCastle.Bcpg;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
Installed is Nuget Package Portable.BouncyCastle 1.8.10.
public class EncryptionService
{
public static void EncryptPGPFile(FileInfo inFile, FileInfo keyFile, FileInfo outFile, bool withIntegrityCheck = false, bool withArmor = false)
{
PgpPublicKeyRingBundle keyRing = null;
using (var keyStream = keyFile.OpenRead())
{
keyRing = new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
}
var publicKey = keyRing.GetKeyRings()
.Cast<PgpPublicKeyRing>()
.FirstOrDefault()
?.GetPublicKeys()
.Cast<PgpPublicKey>()
.FirstOrDefault(x => x.IsEncryptionKey);
using var outFileStream = outFile.Open(FileMode.Create);
using var armoredStream = new ArmoredOutputStream(outFileStream);
Stream outStream = withArmor ? armoredStream : outFileStream;
byte[] compressedBytes;
var compressor = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
using (var byteStream = new MemoryStream())
{
// Annoyingly, this is necessary. The compressorStream needs to be closed before the byteStream is read from, otherwise
// data will be left in the buffer and not written to the byteStream. It would be nice if compressorStream exposed a "Flush"
// method. - AJS
using (var compressorStream = compressor.Open(byteStream))
{
PgpUtilities.WriteFileToLiteralData(compressorStream, PgpLiteralData.Binary, inFile);
}
compressedBytes = byteStream.ToArray();
};
var encrypter = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
encrypter.AddMethod(publicKey);
using var finalOutputStream = encrypter.Open(outStream, compressedBytes.Length);
finalOutputStream.Write(compressedBytes, 0, compressedBytes.Length);
}
public static void DecryptPGPFile(FileInfo inFile, FileInfo keyFile, string password, FileInfo outFile)
{
using var inputFile = inFile.OpenRead();
using var input = PgpUtilities.GetDecoderStream(inputFile);
var pgpFactory = new PgpObjectFactory(input);
var firstObject = pgpFactory.NextPgpObject();
if (firstObject is not PgpEncryptedDataList)
{
firstObject = pgpFactory.NextPgpObject();
}
PgpPrivateKey keyToUse = null;
PgpSecretKeyRingBundle keyRing = null;
using (var keyStream = keyFile.OpenRead())
{
keyRing = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
}
var encryptedData = ((PgpEncryptedDataList)firstObject).GetEncryptedDataObjects()
.Cast<PgpPublicKeyEncryptedData>()
.FirstOrDefault(x =>
{
var key = keyRing.GetSecretKey(x.KeyId);
if (key != null)
{
keyToUse = key.ExtractPrivateKey(password.ToCharArray());
return true;
}
return false;
});
if (keyToUse == null)
{
throw new PgpException("Cannot find secret key for message.");
}
Stream clearText = encryptedData.GetDataStream(keyToUse);
PgpObject message = new PgpObjectFactory(clearText).NextPgpObject();
if (message is PgpCompressedData data)
{
message = new PgpObjectFactory(inputStream: data.GetDataStream()).NextPgpObject();
}
if (message is PgpLiteralData literalData)
{
using var outputFileStream = outFile.Open(FileMode.Create);
Streams.PipeAll(literalData.GetInputStream(), outputFileStream);
}
else
{
throw new PgpException("message is not encoded correctly.");
}
if (encryptedData.IsIntegrityProtected() && !encryptedData.Verify())
{
throw new Exception("message failed integrity check!");
}
}
}

Categories