This project is read-only.

Error Converting Attachment

Apr 6, 2010 at 7:35 PM

Often, when downloading MS Word attachments and converting them from base 64, I get a "Invalid character in a Base-64 string" error.  When I attempt to download the same attachment again, everything works fine.  It appears to be a timing issue -- perhaps the attachment is still downloading, but the convertion process doesn't want to wait.  It occurs more often with larger attachments and/or on remote servers.  I'm using C#, MS Exchange and Koolwired.  Is there a way to know that the attachment has completely downloaded before converting? 

Thanks for any help. 

ImapMailbox mailbox = command.Select(_EmailInboxFolder);
mailbox = command.Fetch(mailbox, 1, 1);

int nMsgs = mailbox.Exist;

if (nMsgs > 0)
{
	for (int i = 1; i <= Math.Min(mailbox.Exist, MaxEmailsPerRun); i++)
	{
		ImapMailboxMessage message = new ImapMailboxMessage();
		message.ID = i;

		message = command.FetchHeaders(i);
		message = command.FetchBodyStructure(message);

		for (int j = 0; j < message.BodyParts.Count; j++)
		{
			ImapMessageBodyPart part = message.BodyParts[j];

			if (part.Attachment)
			{
				message = command.FetchBodyPart(message, j);

				// LINE BELOW THROWS THE EXCEPTION: "Invalid character in a Base-64 string"
				byte[] bytes = Convert.FromBase64String(part.Data);
						:
			}
		}
	}
}

 

Apr 22, 2010 at 4:19 PM

Try using DataBinary Member to save attachment !

string strSavePath = Path.Combine(strFolderPath, message.BodyParts[j].FileName);

FileStream fs = new FileStream(strSavePath, FileMode.Create);

fs.Write(attachment.BodyParts[j].DataBinary, 0, (int)message.BodyParts[j].DataBinary.Length);

Mar 8, 2011 at 12:54 AM

I gotten the same error message. When I checked the Data property of the IMapMessageBodyPart, it contains " FLAGS (/SEEN)" at the end. I am not really looking at the specs but it seems that the flags are getting added to the attachment. I added the code in bolded red to fix my problem. Not sure if this applies to all encoding types too.

ImapCommand.cs

string ParseBodyPart(Imap.BodyPartEncoding encoding, Encoding en)
        {
            string response;
            StringBuilder sb = new StringBuilder("");
            do
            {
                response = Connection.Read();
                if (Regex.IsMatch(response, OK_COMPLETE) || Regex.IsMatch(response, OK_SUCCESS))
                    break;
		if (encoding == Imap.BodyPartEncoding.BASE64 && response.Contains("FLAGS"))
		    continue;
                if (encoding == Imap.BodyPartEncoding.BASE64)
                    sb.Append(response);
                else if (encoding == Imap.BodyPartEncoding.QUOTEDPRINTABLE)
                    if (response.EndsWith("=") || response.EndsWith(")"))
                        sb.Append(response.Substring(0, response.Length - 1));
                    else
                        sb.AppendLine(response);
                else
                    sb.AppendLine(response);
            } while (true);
            //} while (!(response.EndsWith("==") || response == ")"));
            if (sb.ToString().Trim().EndsWith(")"))
                sb = sb.Remove(sb.ToString().LastIndexOf(")"), 1);
            if (encoding != BodyPartEncoding.BASE64)
                return ImapDecode.Decode(sb.ToString(), en);
            return sb.ToString();
        }
Sep 2, 2011 at 1:40 AM
Edited Sep 2, 2011 at 7:44 PM

Yes, that's an error but i found another one (this happens with Exchange when sending multiple commands, it seems the library messes it up and doesn't read all messages).

ImapCommand.cs

string ParseBodyPart(Imap.BodyPartEncoding encoding, Encoding en, string response, bool application)
        {
            StringBuilder sb = new StringBuilder("");
            while (!response.Contains("FETCH (BODY")) response = Connection.Read();
            do
            {
                response = Connection.Read();
                if (Regex.IsMatch(response, OK_COMPLETE) || Regex.IsMatch(response, OK_SUCCESS))
                    break;
                if (encoding == Imap.BodyPartEncoding.BASE64 && response.Contains("FLAGS"))
                    continue;
                if (encoding == Imap.BodyPartEncoding.BASE64)
                    sb.Append(response);
                else if (encoding == Imap.BodyPartEncoding.QUOTEDPRINTABLE)
                    if (response.EndsWith("=") || response.EndsWith(")"))
                        sb.Append(response.Substring(0, response.Length - 1));
                    else
                        sb.AppendLine(response);
                else
                    sb.AppendLine(response);
            } while (true);
            //} while (!(response.EndsWith("==") || response == ")"));
            if(encoding != BodyPartEncoding.BASE64)
            {
                sb = new StringBuilder(sb.ToString().Substring(0, sb.ToString().LastIndexOf("FLAGS (")));
            }
            if (sb.ToString().Trim().EndsWith(")"))
                sb = sb.Remove(sb.ToString().LastIndexOf(")"), 1);
            if (encoding != BodyPartEncoding.BASE64)
            {
                if (application)
                {
                    Encoding enc = Encoding.Default;
                    switch (encoding)
                        {
                            case BodyPartEncoding.UTF7:
                                enc = System.Text.Encoding.UTF7;
                                break;
                            case BodyPartEncoding.UTF8:
                                enc = System.Text.Encoding.UTF8;
                                break;
                        }
                    byte[] convert = Encoding.Convert(enc, Encoding.Default,
                                                      enc.GetBytes(sb.ToString().ToCharArray()));
                    string s = Encoding.Default.GetString(convert);
                    return s;
                }
                return ImapDecode.Decode(sb.ToString(), en);
            }
            return sb.ToString();
        }


That's it because after the fetch answer it comes the data. A pretty good library, i've learned a lot and i'm thankful for it.

Sure, it can be done via regex but this way it should also work. (Still, some mail client sends PDF's as UTF7 and it can't be opened with this library (though they actually can if you access via webmail).

This version has a fix for horde mail, it seems it likes to send PDF attachments (don't know if it does with other sort of attachment) as UTF7-encoded, brought me some trobule to finally realize this. It also has a fix to remove the annoying FLAGS on non base64 encoded parts (as text).

This is the calling function (FetchBodyPart):

public ImapMailboxMessage FetchBodyPart(ImapMailboxMessage message, ImapMessageBodyPart bodyPart)
        {
            if (!(Connection.ConnectionState == ConnectionState.Open))
                NoOpenConnection();
            Connection.Write(string.Format("FETCH {0} BODY[{1}]\r\n", message.ID, bodyPart.BodyPart));
            string response = Connection.Read();
            if (response.StartsWith("*"))
                bodyPart.Data = ParseBodyPart(bodyPart.ContentEncoding, bodyPart.Encoding, response, bodyPart.ContentType.MediaType.Contains("application"));
            return message;
        }

As you can see the application boolean is made this way because the ones most likely troubled with this are application files (i haven't tried on other files, because i do not have an horde mail account), if this is done to text one wouldn't have it decoded to mime-type (and if you try to decode an application file you may run into trouble).