Thu, 30 Sep 2010

2:29 PM - How to test for UTF-8 characters

One of the problems on the web is all the different character encodings.  Computers represent information in different ways.  Some of these approaches handle multiple languages, others do not.  One such encoding is UTF-8.  You can test for UTF-8 in your web applications using this regular expression:

http://www.w3.org/International/questions/qa-forms-utf-8

 

$field =~
  m/A(
     [x09x0Ax0Dx20-x7E]            # ASCII
   | [xC2-xDF][x80-xBF]             # non-overlong 2-byte
   |  xE0[xA0-xBF][x80-xBF]        # excluding overlongs
   | [xE1-xECxEExEF][x80-xBF]{2}  # straight 3-byte
   |  xED[x80-x9F][x80-xBF]        # excluding surrogates
   |  xF0[x90-xBF][x80-xBF]{2}     # planes 1-3
   | [xF1-xF3][x80-xBF]{3}          # planes 4-15
   |  xF4[x80-x8F][x80-xBF]{2}     # plane 16
  )*z/x;

tags: character encoding

()

2:23 PM - PHP is broken

http://www.phpwact.org/php/i18n/charsets

The latest in zend bugs.. strlen reports 27 characters for a 10 length string.

()

Sun, 26 Sep 2010

11:17 AM - Project progress

I've been working on a new web application to download and mix rss feeds.  The idea is to cache feed items into a database and allow mixing of content from many sources.  I have about 1300 rss feeds so far on a variety of topics.  It's rather interesting to do like queries on the tables and find all sort of information.  

My long term plan is to have it index all public just journal entries, and content from other sites like twitter.   (just popular stuff)

There are services like this already, but many of them charge a lot of money for the information.  It's not like a typical search engine because one wants to access the information repeatedly and google tends to block that.  It's an interesting problem we stumbled onto at work.  Most likely content will go pay down the road, but I think there will still be free content as well.  

The first version of the rss fetcher is complete.  It's populated about 444,000 articles so far (rss items), 56000 categories (per rss 2 spec), and 55000 enclosures (file attachments like podcasts and images).  

One service actually charges 75,000 dollars for this functionality. They have a lot more content than I do, but most of it is garbage from twitter.  If you do a keyword search for say clorox, it ends up with posts about bleaching blood out, throwing it on people, cleaning tips and other crazy things.  There's obviously a need for good filters and smart content searching.  I only know of one method to do this right now and I'm not going to buy IBM Omnifind :) 

This is one of the many problem domains I deal with at work.  Thing is, they have no interest in getting the content themselves whereas I see a lot of potential in it.  This doesn't really overlap with work but it's related to what I do right now.  Scary isn't it? 

The biggest hurdles are:

1. Bandwidth.  Downloading RSS feeds takes a long time.

2. Storage capacity.  I'm not sure how long I can retain content. 

3. Blocks.  I might get black listed harvesting so care has to be taken in fetching content.  The java libraries I'm using right now don't honor rss feed intervals, but I'm limiting to at least 60 minutes for now.

4. Legal.  I'm not planning on charging for this data now and effectively i'm acting like a search engine.  I spider content, collect it and cache it for a period of time.  The only difference is how one accesses it.

The next step in the project is creating the website.  I've got help with that phase.

()

Sat, 25 Sep 2010

6:46 PM - cvs

A friend of mine has been working on merging useful patches from Debian and MirBSD for CVS into a new port for mirports.  This is interesting and we might look at it soon.  It adds a number of useful additions to CVS.

MidnightBSD CVS is based on a patchset from DragonFly BSD. It's newer than FreeBSD's archaic CVS version. Newer versions have some issues with CVSWeb, but ViewVC works fine. 

()

6:38 PM - Work, Work

This week I reorganized part of the server room.  We had one 42u IBM rack that my old boss purchased at surplus (U of M) for $50.  It's never been great and he paid to have sides put on it which look quite tacky.  

Last time the CEO was in town, he freaked about how ugly the room was.  He wants to impress clients with this storage room that happens to have servers in it.  It's supposed to be like the "water cooler" for the office.  I don't see why anyone would want to talk in a room with loud computers in it.  

I ended up purchasing two enclosures from Apple. The seal around the front doors sticked, but other than that they were nice.  I used similar units at EMU.  It does cut down the noise and it's better looking than the IBM rack.  I've moved all the external servers (client facing) to the first rack.  The second rack is a bit of a problem.  We have to migrate wiring for all the phone and network ports into the back of it which means redoing all the patch panels.  I figure it's at least an eight hour job and I know he'll hate the room next time he's there anyway.  

Of course, I wonder why I get into these things when I'm supposed to be a software engineer.  Last I knew, that meant I write programs for a living.  

To top it off, one of the guys in germany is working on a stupid idea to put articles in CouchDB and then display them in our portals.  Problem is we import the articles from Joomla into another system and those will not be available for newsletters.  I just wish people would think... 

()

6:32 PM - Advertising in software

Today, I got an ad from my antivirus software for a useless utility product.  I hate ads in software I have purchased.  I can deal with it in games when it's product placement to a degree to cover costs like servers for multiplayer.  However, when I bought antivirus software from a SECURITY company, I expect it to be free of bullshit.  

My product expires in October so I'm going to try out the free Microsoft security essentials software then instead of buying an AV program.  I haven't found one virus the entire year which means either I'm at a low thread level or the software didn't work.  Either way, I don't see the point of spending $50 to protect our 3 inferior Windows boxes. 

tags: sucks av ads security lack spam

()

Sun, 19 Sep 2010

1:00 AM - Intel disables features in CPUs

Intel has a new product line that includes a $50 software upgrade to unlock all the features of your CPU.  This unlock only works in Windows 7.  If you're running any other operating system, it will not function.  This sucks if you're using Linux, *BSD, Solaris, Haiku, EComstation, or even older versions of WIndows.

  http://hardware.slashdot.org/story/10/09/19/0050203/Intel-Wants-To-Charge-50-To-Unlock-Your-CPUs-Full-Capabilities?from=rss

I hope this doesn't happen across their product line because it will mean I'm limited to AMD CPUs.  Maybe my next laptop will have an AMD chip in it. 

tags: disable cpu amd intel

()

Sun, 5 Sep 2010

7:51 PM - Release Engineering: CVS

I've previously created a wiki document about the process to build a MidnightBSD release, but I haven't talked much about what goes on in version control.  

MidnightBSD uses CVS for our source repository.  Today, I created a new tag RELENG_0_3_BP.  This is the branch point, or start of the 0.3 branch that will become 0.3-RELEASE.  

One creates a tag like this:

/usr/src
# 
cvs update -P -d

The next step is to create a branch point  tag , so that diffs against the start of the branch are easier with CVS:

/usr/src
# 
cvs rtag RELENG_0_3_BP src

And then a new branch tag is created with:

/usr/src
# 
cvs rtag -b -rRELENG_0_3_BP RELENG_0_3 src


 

 

tags: release engineering releng midnightbsd cvs tag branch

()

Thu, 2 Sep 2010

Thu, 12 Aug 2010

12:27 AM - Gaming thursday

Well i decided today was make-up day for losing a good part of my sunday to work.  I just bought the QuakeCon2010 pack on steam.  It includes fallout 3, quake *, Doom *, Hexen *,  RTCW, Spear of destiny, elder scrolls 3 & 4, heretic, commander keen and call of cthulhu: dark corners of the earth.  I figured that fallout 3 is close to the same price, and i got a lot of old games.  

I got a magus run started on ds9 and i'm answering questions via jabber, otherwise i'm just relaxing today. 

()

Wed, 4 Aug 2010

5:37 PM - More fun with LDAP

I haven't written about LDAP for a few years.  I've been asked to do a samba setup at work with LDAP.  I decided to document some of the steps required on FreeBSD.

Install samba port (i used 3.4.x)

install smbldap-tools (command line tools to help with user management)

install nss_ldap and pam_ldap ports.  Make sure you can resolve accounts from ldap using id and getent group or else samba will not authenticate.

Configure /usr/local/etc/ldap.conf (and make sure it's linked to nss_ldap.conf)

Modify /etc/nsswtich.conf ; on freebsd 8 remove the compat entries and switch it to files ldap for passwd, group and shadow.  When the compat keyword is used, nothing else works according to nsswitch.conf(5). 

Setup your LDAP directory.  If you configure the smbldap-tools properly, you can use the smbldap-populate command to create all the required parts of the ldap database.

Configure your shares and other settings in smb.conf (although part of this must be done before smbldap-tools works right)

optionally setup swat out of inetd via /etc/inetd.conf

This is by far not complete and should not be considered a "secure" setup but it does seem to work so far. 

In smb.conf you would have something like:

passdb backend = ldapsam:"ldap://ipaddresshere ldap://secondipaddress"
 

ldap user suffix = ou=people
ldap machine suffix = ou=Computers
ldap group suffix = ou=Groups
 

 

 

()

Mon, 19 Jul 2010

1:54 PM - LAMP = PIA

Yuck.. linux server ran out of disk space. installing new raid volumes now

status of raid 1:

mdadm --misc -D /dev/md3

installation howto from ubuntu:

https://help.ubuntu.com/community/Installation/RAID1%2BLVM

useful site on lvm:

http://www.linuxconfig.org/Linux_lvm_-_Logical_Volume_Manager

()

Thu, 1 Jul 2010

3:57 PM - Centos redhat fedora, sendmail and thunderbird

yum install cyrus-sasl-plain  is your friend.  THis fixes the mail vrfy blah blah error in the logs when you setup sendmail + saslauthd and you try to get thunderbird to auth properly. 

This bugged me for two days and there are hundreds of incorrect posts about using RELAY rules.  It's insane.

 did not issue MAIL/EXPN/VRFY/ETRN during connection to TLSMTA  fix is again to install the right cyrus sasl auth plugin from yum. 
 

()

Sun, 13 Jun 2010

7:01 PM - HL2 Episode 2

I finally got a chance to beat Half Life 2 Episode 2.  I've been half way through it since last year.  

()

Sat, 12 Jun 2010

1:16 PM - Wireless Security

One of my coworkers pointed out the inherit insecurity of WPA on Wifi devices the other day.  I knew that WEP was insecure, and I figured there were problems with WPA, but I didn't realize how easy it is to break it. 

WPA (and WPA2 -PSK / personal in apple lingo) use a secret key that can be up to 63 characters.  Many people don't use a full length key.  Others use a dictionary word or some other easy to deduce key.  

Apparently, in the protocol for WPA, the initial handshake (communication between your pc/mac and the wifi router) transmits something (well the hash of the key effectively) that can be grabbed in the air.  An attacker can grab this and then run a program offline to determine your WPA key.  He can later join your network.  

At this point, assume all wireless networking is insecure if you weren't already.  

Tips to make it harder to crack:

1. Use the maximum length or as close as you can for the private key

2. Use random characters, a generated key from one of the many online sites is better.  Don't use something obvious like "luke & caryn's wireless".   No, i've never used that.

3. Enable MAC address filtering (not to be confused with Mac computers)

4. Change your key periodically.  You never know who's on your Internet connection.

5. Use AES with WPA if it's available on your router. It's a little harder to crack.

Reasons you should care:

1. Someone can download illegal or offensive content and it came from your ip address.  You could go to jail or have an unpleasant search situation.  

2. You're paying good money for your internet connection, why should your neighbors freeload? Do you let your neighbor steal your cable too?

3. War driving.  Just google it.

()

Thu, 10 Jun 2010

9:33 PM - R00t through Excel.. how l33t

 " The flaws in Excel allowed hackers to create tainted spreadsheets that infect a user's PC once they are opened, ..."

http://www.msnbc.msn.com/id/37579736/ns/technology_and_science-security/

This has got to be the dumbest security message I've seen to date.

()

Mon, 24 May 2010

9:31 PM - Shooting yourself in the foot

(shamelessly stolen from http://apple.slashdot.org/comments.pl?sid=1660824&cid=32302054) 

Are you a programmer and willing to shoot only an appendage?

C
You shoot yourself in the foot.

C++
You accidentally create a dozen clones of yourself and shoot them all in the foot. Providing emergency medical assistance is impossible since you can't tell which are bitwise copies and which are just pointing at others and saying, "That's me, over there."

JAVA
After importing java.awt.right.foot.* and java.awt.gun.right.hand.*, and writing the classes and methods of those classes needed, you've forgotten what the hell you're doing.

Ruby
Your foot is ready to be shot in roughly five minutes, but you just can't find anywhere to shoot it.

PHP
You shoot yourself in the foot with a gun made with pieces from 300 other guns.

ASP.NET
Find a gun, it falls apart. Put it back together, it falls apart again. You try using the   .GUN Framework, it falls apart. You stab yourself in the foot instead.

SQL
SELECT @ammo:=bullet FROM gun WHERE trigger = 'PULLED';
INSERT INTO leg (foot) VALUES (@ammo);

Perl
You shoot yourself in the foot, but nobody can understand how you did it. Six months later, neither can you.

Javascript
You've perfected a robust, rich user experience for shooting yourself in the foot. You then find that bullets are disabled on your gun.

CSS
You shoot your right foot with one hand, then switch hands to shoot your left foot but you realize that the gun has turned into a banana.

FORTRAN
You shoot yourself in each toe, iteratively, until you run out of toes, then you read in the next foot and repeat. If you run out of bullets, you continue anyway because you have no exception-handling ability.

COBOL
Using a COLT 45 HANDGUN, AIM gun at LEG.FOOT, THEN place ARM.HAND.FINGER. on HANDGUN.TRIGGER and SQUEEZE. THEN return HANDGUN to HOLSTER. CHECK whether shoelace needs to be retied.

LISP
You shoot yourself in the appendage which holds the gun with which
you shoot yourself in the appendage which holds the gun with which
you shoot yourself in the appendage which holds the gun with which
you shoot yourself in the appendage which holds the gun with which
you shoot yourself in the appendage which holds   ....

BASIC
Shoot yourself in the foot with a water pistol. On big systems, continue until entire lower body is waterlogged.

Pascal
The compiler won't let you shoot yourself in the foot.

Unix
% ls
foot.c foot.h foot.o toe.c toe.o
% rm *   .o
rm:   .o: No such file or directory
% ls
%

Visual Basic
You'll shoot yourself in the foot, but you'll have so much fun doing it that you won't care.

Ada
After correctly packaging your foot, you attempt to concurrently load the gun, pull the trigger, scream and shoot yourself in the foot. When you try, however, you discover that your foot is of the wrong type.

Assembly
You try to shoot yourself in the foot only to discover you must first reinvent the gun, the bullet, and your foot. After that's done, you pull the trigger, the gun beeps several times, then crashes.

Python
You try to shoot yourself in the foot but you just keep hitting the whitespace between your toes.

Etc...



()

9:11 PM - Oh the humanity.. 2200 lines

  /**

     * Convert strings with umlaut and other named character entities into utf8 number based character entities.
     * http://code.google.com/p/doctype/wiki/CharacterEntities
     * @param str input string with ä and friends
     * @return real letters
     */
    public static String convertEntitiesToNumeric( final String str )
    {
        String s = str;

        if (s.isEmpty())
        {
            return s;
        }

        // German and other umlaut based letters
        s = StringUtils.replace( s, "ä", "ä" );  // ä
        s = StringUtils.replace( s, "Ä", "Ä" );  // Ä
        s = StringUtils.replace( s, "ü", "ü" );  // ü
        s = StringUtils.replace( s, "Ü", "Ü" );  // Ü
        s = StringUtils.replace( s, "ö", "ö" );  // ö
        s = StringUtils.replace( s, "Ö", "Ö" );  // Ö
        s = StringUtils.replace( s, "ß", "ß" ); // ß
        s = StringUtils.replace( s, "ï", "ï" );  // ï
        s = StringUtils.replace( s, "Ï", "Ï" );  // Ï
        s = StringUtils.replace( s, "ë", "ë" );  // ë
        s = StringUtils.replace( s, "Ë", "Ë" );  // Ë
        s = StringUtils.replace( s, "ÿ", "ÿ" );  // ÿ
        s = StringUtils.replace( s, "Ÿ", "Ÿ" );  // Ÿ

        // A's
        s = StringUtils.replace( s, "á", "á" ); // á
        s = StringUtils.replace( s, "Á", "Á" ); // Á
        s = StringUtils.replace( s, "ă", "ă" ); // ă
        s = StringUtils.replace( s, "Ă", "Ă" ); // Ä‚
        s = StringUtils.replace( s, "∾", "∾" );     // ∾
        s = StringUtils.replace( s, "∿", "∿" );    // ∿
        s = StringUtils.replace( s, "â", "â" );  // â
        s = StringUtils.replace( s, "´", "´" );  // ´
        s = StringUtils.replace( s, "а", "а" );
        s = StringUtils.replace( s, "А", "А" );
        s = StringUtils.replace( s, "Æ", "Æ" );  // Æ
        s = StringUtils.replace( s, "æ", "æ" );  // æ
        s = StringUtils.replace( s, "⁡", "&#x02061" );
        s = StringUtils.replace( s, "𝔞", "𝔞" );
        s = StringUtils.replace( s, "𝔄", "𝔄" );
        s = StringUtils.replace( s, "à", "à" ); // à
        s = StringUtils.replace( s, "ℵ", "ℵ" );// ℵ
        s = StringUtils.replace( s, "ℵ", "ℵ" );  // ℵ
        s = StringUtils.replace( s, "α", "α" );  // α
        s = StringUtils.replace( s, "ā", "ā" );
        s = StringUtils.replace( s, "⨿", "⨿" );
        // skip & and &
        s = StringUtils.replace( s, "∧", "∧" );    // ∧
        s = StringUtils.replace( s, "⩓", "⩓" );
        s = StringUtils.replace( s, "⩕", "⩕" );
        s = StringUtils.replace( s, "⩜", "⩜" );
        s = StringUtils.replace( s, "⩘", "⩘" );
        s = StringUtils.replace( s, "⩚", "⩚" );
        s = StringUtils.replace( s, "∠", "∠" );    // ∠
        s = StringUtils.replace( s, "⦤", "⦤" );
        s = StringUtils.replace( s, "∠", "∠" );
        s = StringUtils.replace( s, "∡", "∡" ); // ∡
        s = StringUtils.replace( s, "⦨", "⦨" );
        s = StringUtils.replace( s, "⦩", "⦩" );
        s = StringUtils.replace( s, "⦪", "⦪" );
        s = StringUtils.replace( s, "⦫", "⦫" );
        s = StringUtils.replace( s, "⦬", "⦬" );
        s = StringUtils.replace( s, "⦭", "⦭" );
        s = StringUtils.replace( s, "⦮", "⦮" );
        s = StringUtils.replace( s, "⦯", "⦯" );
        s = StringUtils.replace( s, "∟", "∟" ); // ∟
        s = StringUtils.replace( s, "⊾", "⊾" );
        s = StringUtils.replace( s, "⦝", "⦝" );
        s = StringUtils.replace( s, "∢", "∢" ); // ∢
        s = StringUtils.replace( s, "Å", "Å" ); // Å
        s = StringUtils.replace( s, "⍼", "⍼" );
        s = StringUtils.replace( s, "ą", "ą" );
        s = StringUtils.replace( s, "Ą", "Ą" );
        s = StringUtils.replace( s, "𝕒", "𝕒" );
        s = StringUtils.replace( s, "𝔸", "𝔸" );
        s = StringUtils.replace( s, "≈", "≈" );
        s = StringUtils.replace( s, "⩯", "⩯" );
        s = StringUtils.replace( s, "≊", "≊" );
        s = StringUtils.replace( s, "⩰", "⩰" );
        s = StringUtils.replace( s, "≋", "≋" );
        // skip '
        s = StringUtils.replace( s, "≈", "≈" ); // ≈
        s = StringUtils.replace( s, "≊", "≊" );
        s = StringUtils.replace( s, "å", "å" ); // å
        s = StringUtils.replace( s, "Å", "Å" ); // Å
        s = StringUtils.replace( s, "𝒶", "𝒶" );
        s = StringUtils.replace( s, "𝒜", "𝒜" );
        s = StringUtils.replace( s, "≔", "≔" );
        s = StringUtils.replace( s, "*", "*" );
        s = StringUtils.replace( s, "≈", "≈" ); // ≈
        s = StringUtils.replace( s, "≍", "≍" ); // ≍
        s = StringUtils.replace( s, "ã", "ã" ); // ã
        s = StringUtils.replace( s, "Ã", "Ã" ); // Ã
        // auml covered above
        s = StringUtils.replace( s, "∳", "∳" );
        s = StringUtils.replace( s, "⨑", "⨑" );

        // b's
        s = StringUtils.replace( s, "∖", "∖" );
        s = StringUtils.replace( s, "⫧", "⫧" );
        s = StringUtils.replace( s, "⌆", "⌆" );
        s = StringUtils.replace( s, "Б", "Б" );
        s = StringUtils.replace( s, "∵", "∵" ); // ∵
        s = StringUtils.replace( s, "ℬ", "ℬ" ); // ℬ
        s = StringUtils.replace( s, "Β", "Β" ); // Β
        s = StringUtils.replace( s, "𝔅", "𝔅" );
        s = StringUtils.replace( s, "𝔹", "𝔹" );
        s = StringUtils.replace( s, "˘", "˘" );
        s = StringUtils.replace( s, "ℬ", "ℬ" );
        s = StringUtils.replace( s, "≎", "≎" );
        s = StringUtils.replace( s, "⫭", "⫭" );
        s = StringUtils.replace( s, "≌", "≌" );
        s = StringUtils.replace( s, "϶", "϶" );
        s = StringUtils.replace( s, "‵", "‵" );
        s = StringUtils.replace( s, "∽", "∽" );
        s = StringUtils.replace( s, "⋍", "⋍" );
        s = StringUtils.replace( s, "⊽", "⊽" );
        s = StringUtils.replace( s, "⌅", "⌅" );
        s = StringUtils.replace( s, "⌅", "⌅" );
        s = StringUtils.replace( s, "⎵", "⎵" );
        s = StringUtils.replace( s, "⎶", "⎶" );
        s = StringUtils.replace( s, "≌", "≌" );
        s = StringUtils.replace( s, "б", "б" );
        s = StringUtils.replace( s, "„", "„" ); // "
        s = StringUtils.replace( s, "∵", "∵" );
        s = StringUtils.replace( s, "∵", "∵" );
        s = StringUtils.replace( s, "⦰", "⦰" );
        s = StringUtils.replace( s, "϶", "϶" );
        s = StringUtils.replace( s, "ℬ", "ℬ" );
        s = StringUtils.replace( s, "β", "β" ); // β
        s = StringUtils.replace( s, "ℶ", "ℶ" );
        s = StringUtils.replace( s, "≬", "≬" );
        s = StringUtils.replace( s, "𝔟", "𝔟" );
        s = StringUtils.replace( s, "⋂", "⋂" );
        s = StringUtils.replace( s, "◯", "◯" );
        s = StringUtils.replace( s, "⋃", "⋃" );
        s = StringUtils.replace( s, "⨀", "⨀" );
        s = StringUtils.replace( s, "⨁", "⨁" );
        s = StringUtils.replace( s, "bigotimes;", "⨂" );
        s = StringUtils.replace( s, "⨆", "⨆" );
        s = StringUtils.replace( s, "★", "★" ); //★
        s = StringUtils.replace( s, "▽", "▽" );
        s = StringUtils.replace( s, "△", "△" );
        s = StringUtils.replace( s, "⨄", "⨄" );
        s = StringUtils.replace( s, "⋁", "⋁" );
        s = StringUtils.replace( s, "⋀", "⋀" );
        s = StringUtils.replace( s, "⤍", "⤍" );
        s = StringUtils.replace( s, "⧫", "⧫" );
        s = StringUtils.replace( s, "▪", "▪" );
        s = StringUtils.replace( s, "▴", "▴" );
        s = StringUtils.replace( s, "▾", "▾" );
        s = StringUtils.replace( s, "◂", "◂" );
        s = StringUtils.replace( s, "▸", "▸" );
        s = StringUtils.replace( s, "␣", "␣" );
        s = StringUtils.replace( s, "▒", "▒" );
        s = StringUtils.replace( s, "░", "░" );
        s = StringUtils.replace( s, "▓", "▓" );
        s = StringUtils.replace( s, "╨", "╨" );
        s = StringUtils.replace( s, "╩", "╩" );
        s = StringUtils.replace( s, "┘", "┘" );
        s = StringUtils.replace( s, "╛", "╛" );
        s = StringUtils.replace( s, "╜", "╜" );
        s = StringUtils.replace( s, "╝", "╝" );
        s = StringUtils.replace( s, "└", "└" );
        s = StringUtils.replace( s, "╘", "╘" );
        s = StringUtils.replace( s, "╙", "╙" );
        s = StringUtils.replace( s, "╚", "╚" );
        s = StringUtils.replace( s, "│", "│" );
        s = StringUtils.replace( s, "║", "║" );
        s = StringUtils.replace( s, "┼", "┼" );
        s = StringUtils.replace( s, "╪", "╪" );
        s = StringUtils.replace( s, "╫", "╫" );
        s = StringUtils.replace( s, "█", "█" );
        s = StringUtils.replace( s, "⌐", "⌐" );
        s = StringUtils.replace( s, "𝕓", "𝕓" );
        s = StringUtils.replace( s, "⊥", "⊥" );
        s = StringUtils.replace( s, "⊥", "⊥" );
        s = StringUtils.replace( s, "⋈", "⋈" );
        s = StringUtils.replace( s, "╗", "╗" );
        s = StringUtils.replace( s, "╔", "╔" );
        s = StringUtils.replace( s, "┌", "┌" );
        s = StringUtils.replace( s, "╖", "╖" );
        s = StringUtils.replace( s, "┐", "┐" );
        s = StringUtils.replace( s, "─", "─" );
        s = StringUtils.replace( s, "┬", "┬" );
        s = StringUtils.replace( s, "╤", "╤" );
        s = StringUtils.replace( s, "╥", "╥" );
        s = StringUtils.replace( s, "╦", "╦" );
        s = StringUtils.replace( s, "┴", "┴" );
        s = StringUtils.replace( s, "╧", "╧" );
        s = StringUtils.replace( s, "╬", "╬" );
        s = StringUtils.replace( s, "┤", "┤" );
        s = StringUtils.replace( s, "╡", "╡" );
        s = StringUtils.replace( s, "╢", "╢" );
        s = StringUtils.replace( s, "╣", "╣" );
        s = StringUtils.replace( s, "├", "├" );
        s = StringUtils.replace( s, "╞", "╞" );
        s = StringUtils.replace( s, "╟", "╟" );
        s = StringUtils.replace( s, "╠", "╠" );
        s = StringUtils.replace( s, "⧉", "⧉" );
        s = StringUtils.replace( s, "⊟", "⊟" );
        s = StringUtils.replace( s, "⊞", "⊞" );
        s = StringUtils.replace( s, "⊠", "⊠" );
        s = StringUtils.replace( s, "‵", "‵" );
        s = StringUtils.replace( s, "˘", "˘" );
        s = StringUtils.replace( s, "¦", "¦" ); // ¦
        s = StringUtils.replace( s, "&brvbar", "¦" );  // ¦  , special case
        s = StringUtils.replace( s, "𝒷", "𝒷" );
        s = StringUtils.replace( s, "⁏", "⁏" );
        s = StringUtils.replace( s, "∽", "∽" );
        s = StringUtils.replace( s, "⋍", "⋍" );
        s = StringUtils.replace( s, "\", "\" );
        s = StringUtils.replace( s, "⧅", "⧅" );
        s = StringUtils.replace( s, "•", "•" ); // •
        s = StringUtils.replace( s, "•", "•" );
        s = StringUtils.replace( s, "≎", "≎" );
        s = StringUtils.replace( s, "≏", "≏" );
        s = StringUtils.replace( s, "⪮", "⪮" );

        // C's
        s = StringUtils.replace( s, "Ч", "Ч" );
        s = StringUtils.replace( s, "ч", "ч" );
        s = StringUtils.replace( s, "©", "©" );
        s = StringUtils.replace( s, "©", "©" );
        s = StringUtils.replace( s, "&COPY", "©" );  // special
        s = StringUtils.replace( s, "&copy", "©" );  // special
        s = StringUtils.replace( s, "ć", "ć" );
        s = StringUtils.replace( s, "Ć", "Ć" );
        s = StringUtils.replace( s, "∩", "∩" ); // ∩
        s = StringUtils.replace( s, "⋒", "⋒" ); // â‹’
        s = StringUtils.replace( s, "ⅅ", "ⅅ" );
        s = StringUtils.replace( s, "ℭ", "ℭ" );
        s = StringUtils.replace( s, "č", "č" );
        s = StringUtils.replace( s, "Č", "Č" );
        s = StringUtils.replace( s, "ç", "ç" ); // ç
        s = StringUtils.replace( s, "Ccedil;", "Ç" );  // Ç
        s = StringUtils.replace( s, "&Ccedil", "Ç" );  // Ç
        s = StringUtils.replace( s, "&ccedil", "ç" );
        s = StringUtils.replace( s, "ĉ", "ĉ" );
        s = StringUtils.replace( s, "Ĉ", "Ĉ" );
        s = StringUtils.replace( s, "∰", "∰" );
        s = StringUtils.replace( s, "ċ", "ċ" );
        s = StringUtils.replace( s, "Ċ", "Ċ" );
        s = StringUtils.replace( s, "¸", "¸" );
        s = StringUtils.replace( s, "·", "·" );
        s = StringUtils.replace( s, "·", "·" );
        s = StringUtils.replace( s, "𝔠", "𝔠" );
        s = StringUtils.replace( s, "𝔠", "𝔠" );
        s = StringUtils.replace( s, "ℭ", "ℭ" );
        s = StringUtils.replace( s, "χ", "χ" ); // χ
        s = StringUtils.replace( s, "Χ", "Χ" ); // Χ
        s = StringUtils.replace( s, "⊙", "⊙" );
        s = StringUtils.replace( s, "⊖", "⊖" );
        s = StringUtils.replace( s, "⊕", "⊕" );
        s = StringUtils.replace( s, "⊗", "⊗" );
        s = StringUtils.replace( s, "∲", "∲" );
        s = StringUtils.replace( s, "”", "”" );
        s = StringUtils.replace( s, "’", "’" );
        s = StringUtils.replace( s, ":", ":" );
        s = StringUtils.replace( s, "∷", "∷" );
        s = StringUtils.replace( s, "≔", "≔" );
        s = StringUtils.replace( s, "⩴", "⩴" );
        s = StringUtils.replace( s, "≡", "≡" );
        s = StringUtils.replace( s, "∮", "∮" );
        s = StringUtils.replace( s, "∯", "∯" );
        s = StringUtils.replace( s, "∮", "∮" );
        s = StringUtils.replace( s, "𝕔", "𝕔" );
        s = StringUtils.replace( s, "ℂ", "ℂ" );
        s = StringUtils.replace( s, "∐", "∐" );
        s = StringUtils.replace( s, "&CounterClockwiseContourIntegral;", "∳" );
        s = StringUtils.replace( s, "✗", "✗" );
        s = StringUtils.replace( s, "⨯", "⨯" );
        s = StringUtils.replace( s, "𝒸", "𝒸" );
        s = StringUtils.replace( s, "𝒞", "𝒞" );
        s = StringUtils.replace( s, "∪", "∪" ); // ∪
        s = StringUtils.replace( s, "⋓", "⋓" );
        s = StringUtils.replace( s, "≍", "≍" );
        s = StringUtils.replace( s, "⩆", "⩆" );
        s = StringUtils.replace( s, "⩄", "⩄" );
        s = StringUtils.replace( s, "⩉", "⩉" );
        s = StringUtils.replace( s, "⩋", "⩋" );
        s = StringUtils.replace( s, "⩇", "⩇" );
        s = StringUtils.replace( s, "⩀", "⩀" );
        s = StringUtils.replace( s, "⁁", "⁁" );
        s = StringUtils.replace( s, "⩌", "⩌" );
        s = StringUtils.replace( s, "ˇ", "ˇ" );
        s = StringUtils.replace( s, "⩍", "⩍" );
        s = StringUtils.replace( s, "⩐", "⩐" );
        s = StringUtils.replace( s, "¸", "¸" );
        s = StringUtils.replace( s, "&cedil", "¸" );
        s = StringUtils.replace( s, "⦲", "⦲" );
        s = StringUtils.replace( s, "¢", "¢" ); // ¢
        s = StringUtils.replace( s, "&cent", "¢" );
        s = StringUtils.replace( s, "✓", "✓" );
        s = StringUtils.replace( s, "✓", "✓" );
        s = StringUtils.replace( s, "○", "○" );
        s = StringUtils.replace( s, "≗", "≗" );
        s = StringUtils.replace( s, "⧃", "⧃" );
        s = StringUtils.replace( s, "ˆ", "#x002C6;" );
        s = StringUtils.replace( s, "≗", "≗" );
        s = StringUtils.replace( s, "↺", "↺" );
        s = StringUtils.replace( s, "↻", "↻" );
        s = StringUtils.replace( s, "®", "®" );
        s = StringUtils.replace( s, "Ⓢ", "Ⓢ" );
        s = StringUtils.replace( s, "⊛", "⊛" );
        s = StringUtils.replace( s, "⊚", "⊚" );
        s = StringUtils.replace( s, "&circleddash;", "&#x0229D;<" );
        s = StringUtils.replace( s, "&cirfnint;", "&#x02A10;" );
        s = StringUtils.replace( s, "&cirmid;", "&#x02AEF;" );
        s = StringUtils.replace( s, "&cirscir;", "&#x029C2;" );
        s = StringUtils.replace( s, "&clubs;", "&#x02663;" ); // ♣
        s = StringUtils.replace( s, "&clubsuit;", "&#x02663;" );
        s = StringUtils.replace( s, "&coloneq;", "&#x02254;" );
        s = StringUtils.replace( s, "&comma;", "&#x0002C;" );
        s = StringUtils.replace( s, "&commat;", "&#x00040;" );
        s = StringUtils.replace( s, "&comp;", "&#x02201;" );
        s = StringUtils.replace( s, "&compfn;", "&#x02218;" );
        s = StringUtils.replace( s, "&complement;", "&#x02201;" );
        s = StringUtils.replace( s, "&complexes;", "&#x02102;" );
        s = StringUtils.replace( s, "&cong;", "&#x02245;" );
        s = StringUtils.replace( s, "&congdot;", "&#x02A6D;" );
        s = StringUtils.replace( s, "&coprod;", "&#x02210;" );
        s = StringUtils.replace( s, "&copysr;", "&#x02117;" );
        s = StringUtils.replace( s, "&crarr;", "&#x021B5;" );
        s = StringUtils.replace( s, "&csub;", "&#x02ACF;" );
        s = StringUtils.replace( s, "&csube;", "&#x02AD1;" );
        s = StringUtils.replace( s, "&csup;", "&#x02AD0;" );
        s = StringUtils.replace( s, "&csupe;", "&#x02AD2;" );
        s = StringUtils.replace( s, "&ctdot;", "&#x022EF;" );
        s = StringUtils.replace( s, "&cudarrl;", "&#x02938;" );
        s = StringUtils.replace( s, "&cudarrr;", "&#x02935;" );
        s = StringUtils.replace( s, "&cuepr;", "&#x022DE;" );
        s = StringUtils.replace( s, "&cuesc;", "&#x022DF;" );
        s = StringUtils.replace( s, "&cularr;", "&#x021B6;" );
        s = StringUtils.replace( s, "&cularrp;", "&#x0293D;" );
        s = StringUtils.replace( s, "&cupbrcap;", "&#x02A48;" );
        s = StringUtils.replace( s, "&cupcup;", "&#x02A4A;" );
        s = StringUtils.replace( s, "&cupdot;", "&#x0228D;" );
        s = StringUtils.replace( s, "&cupor;", "&#x02A45;" );
        s = StringUtils.replace( s, "&curarr;", "&#x021B7;" );
        s = StringUtils.replace( s, "&curarrm;", "&#x0293C;" );
        s = StringUtils.replace( s, "&curlyeqprec;", "&#x022DE;" );
        s = StringUtils.replace( s, "&curlyeqsucc;", "&#x022DF;" );
        s = StringUtils.replace( s, "&curlyvee;", "&#x022CE;" );
        s = StringUtils.replace( s, "&curlywedge;", "&#x022CF;" );
        s = StringUtils.replace( s, "&curren;", "&#x000A4;" );
        s = StringUtils.replace( s, "&curren", "&#x000A4;" );
        s = StringUtils.replace( s, "&curvearrowleft;", "&#x021B6;" );
        s = StringUtils.replace( s, "&curvearrowright;", "&#x021B7;" );
        s = StringUtils.replace( s, "&cuvee;", "&#x022CE;" );
        s = StringUtils.replace( s, "&cuwed;", "&#x022CF;" );
        s = StringUtils.replace( s, "&cwconint;", "&#x02232;" );
        s = StringUtils.replace( s, "&cwint;", "&#x02231;<" );
        s = StringUtils.replace( s, "&cylcty;", "&#x0232D;" );

        // D's
        s = StringUtils.replace( s, "&dd;", "&#x02146;" );
        s = StringUtils.replace( s, "&DD;", "&#x02145;" );
        s = StringUtils.replace( s, "&DDotrahd;", "&#x02911;" );
        s = StringUtils.replace( s, "&DJcy;", "&#x00402;" );
        s = StringUtils.replace( s, "&djcy;", "&#x00452;" );
        s = StringUtils.replace( s, "&DScy;", "&#x00405;" );
        s = StringUtils.replace( s, "&dscy;", "&#x00455;" );
        s = StringUtils.replace( s, "&DZcy;", "&#x0040F;" );
        s = StringUtils.replace( s, "&dzcy;", "&#x0045F;" );
        s = StringUtils.replace( s, "&dagger;", "&#x02020;" );
        s = StringUtils.replace( s, "&Dagger;", "&#x02021;" );
        s = StringUtils.replace( s, "&darr;", "&#x02193;" );
        s = StringUtils.replace( s, "&dArr;", "&#x021D3;" );
        s = StringUtils.replace( s, "&Darr;", "&#x021A1;" );
        s = StringUtils.replace( s, "&dashv;", "&#x022A3;" );
        s = StringUtils.replace( s, "&Dashv;", "&#x02AE4" );
        s = StringUtils.replace( s, "&dcaron;", "&#x0010F;" );
        s = StringUtils.replace( s, "&Dcaron;", "&#x0010E;" );
        s = StringUtils.replace( s, "&dcy;", "&#x00434;" );
        s = StringUtils.replace( s, "&Dcy;", "&#x00414;" );
        s = StringUtils.replace( s, "&Del;", "&#x02207;" );
        s = StringUtils.replace( s, "&delta;", "&#x003B4;" ); // δ
        s = StringUtils.replace( s, "&Delta;", "&#x00394;" ); // Δ
        s = StringUtils.replace( s, "&dfr;", "&#x1D521;" );
        s = StringUtils.replace( s, "&Dfr;", "&#x1D507;" );
        s = StringUtils.replace( s, "&DiacriticalAcute;", "&#x000B4;" );
        s = StringUtils.replace( s, "&DiacriticalDot;", "&#x002D9;" );
        s = StringUtils.replace( s, "&DiacriticalDoubleAcute;", "&#x002DD;" );
        s = StringUtils.replace( s, "&DiacriticalGrave;", "&#x00060;" );
        s = StringUtils.replace( s, "&DiacriticalTilde;", "&#x002DC;" );
        s = StringUtils.replace( s, "&diamond;", "&#x022C4;" );
        s = StringUtils.replace( s, "&Diamond;", "&#x022C4;" );
        s = StringUtils.replace( s, "&DifferentialD;", "&#x02146;" );
        s = StringUtils.replace( s, "&dopf;", "&#x1D555;" );
        s = StringUtils.replace( s, "&Dopf;", "&#x1D53B;" );
        s = StringUtils.replace( s, "&dot;", "&#x002D9;" );
        s = StringUtils.replace( s, "&Dot;", "&#x000A8;" );
        s = StringUtils.replace( s, "&DotDot;", "&#x020DC;" );
        s = StringUtils.replace( s, "&DotEqual;", "&#x02250;" );
        s = StringUtils.replace( s, "&DoubleContourIntegral;", "&#x0222F;" );
        s = StringUtils.replace( s, "&DoubleDot;", "&#x000A8;" );
        s = StringUtils.replace( s, "&DoubleDownArrow;", "&#x021D3;" );
        s = StringUtils.replace( s, "&DoubleLeftArrow;", "&#x021D0;" );
        s = StringUtils.replace( s, "&DoubleLeftRightArrow;", "&#x021D4;" );
        s = StringUtils.replace( s, "&DoubleLeftTee;", "&#x02AE4;" );
        s = StringUtils.replace( s, "&DoubleLongLeftArrow;", "&#x027F8;" );
        s = StringUtils.replace( s, "&DoubleLongRightArrow;", "&#x027F9;" );
        s = StringUtils.replace( s, "&DoubleRightArrow;", "&#x021D2;" );
        s = StringUtils.replace( s, "&DoubleRightTee;", "&#x022A8;" );
        s = StringUtils.replace( s, "&DoubleUpArrow;", "&#x021D1;" );
        s = StringUtils.replace( s, "&DoubleUpDownArrow;", "&#x021D5;" );
        s = StringUtils.replace( s, "&DoubleVerticalBar;", "&#x02225;" );
        s = StringUtils.replace( s, "&downarrow;", "&#x02193;" );
        s = StringUtils.replace( s, "&DownArrow;", "&#x02193;" );
        s = StringUtils.replace( s, "&Downarrow;", "&#x021D3;" );
        s = StringUtils.replace( s, "&DownArrowBar;", "&#x02913;" );
        s = StringUtils.replace( s, "&DownArrowUpArrow;", "&#x021F5;" );
        s = StringUtils.replace( s, "&DownBreve;", "&#x00311;" );
        s = StringUtils.replace( s, "&DownLeftRightVector;", "&#x02950;" );
        s = StringUtils.replace( s, "&DownLeftTeeVector;", "&#x0295E;" );
        s = StringUtils.replace( s, "&DownLeftVector;", "&#x021BD;" );
        s = StringUtils.replace( s, "&DownLeftVectorBar;", "&#x02956;" );
        s = StringUtils.replace( s, "&DownRightTeeVector;", "&#x0295F;" );
        s = StringUtils.replace( s, "&DownRightVector;", "&#x021C1;" );
        s = StringUtils.replace( s, "&DownRightVectorBar;", "&#x02957;" );
        s = StringUtils.replace( s, "&DownTee;", "&#x022A4;" );
        s = StringUtils.replace( s, "&DownTeeArrow;", "&#x021A7;" );
        s = StringUtils.replace( s, "&dscr;", "&#x1D4B9;" );
        s = StringUtils.replace( s, "&Dscr;", "&#x1D49F;" );
        s = StringUtils.replace( s, "&dstrok;", "&#x00111;" );
        s = StringUtils.replace( s, "&Dstrok;", "&#x00110;" );
        s = StringUtils.replace( s, "&dHar;", "&#x02965;" );
        s = StringUtils.replace( s, "&daleth;", "&#x02138;" );
        s = StringUtils.replace( s, "&dash;", "&#x02010;" );
        s = StringUtils.replace( s, "&dbkarow;", "&#x0290F;" );
        s = StringUtils.replace( s, "&dblac;", "&#x002DD;" );
        s = StringUtils.replace( s, "&ddagger;", "&#x02021;" );
        s = StringUtils.replace( s, "&ddarr;", "&#x021CA;" );
        s = StringUtils.replace( s, "&ddotseq;", "&#x02A77;" );
        s = StringUtils.replace( s, "&deg;", "&#x000B0;" );
        s = StringUtils.replace( s, "&deg", "&#x000B0;" );
        s = StringUtils.replace( s, "&demptyv;", "&#x029B1;" );
        s = StringUtils.replace( s, "&dfisht;", "&#x0297F;" );
        s = StringUtils.replace( s, "&dharl;", "&#x021C3;" );
        s = StringUtils.replace( s, "&dharr;", "&#x021C2;" );
        s = StringUtils.replace( s, "&diam;", "&#x022C4;" );
        s = StringUtils.replace( s, "&diamondsuit;", "&#x02666;" );
        s = StringUtils.replace( s, "&diams;", "&#x02666;" );
        s = StringUtils.replace( s, "&die;", "&#x000A8;" );
        s = StringUtils.replace( s, "&digamma;", "&#x003DD;" );
        s = StringUtils.replace( s, "&disin;", "&#x022F2;" );
        s = StringUtils.replace( s, "&div;", "&#x000F7;" );
        s = StringUtils.replace( s, "&divide;", "&#x000F7;" );
        s = StringUtils.replace( s, "&divide", "&#x000F7;" );
        s = StringUtils.replace( s, "&divideontimes;", "&#x022C7;" );
        s = StringUtils.replace( s, "&divonx;", "&#x022C7;" );
        s = StringUtils.replace( s, "&dlcorn;", "&#x0231E;" );
        s = StringUtils.replace( s, "&dlcrop;", "&#x0230D;" );
        s = StringUtils.replace( s, "&dollar;", "&#x00024;" );
        s = StringUtils.replace( s, "&doteq;", "&#x02250;" );
        s = StringUtils.replace( s, "&doteqdot;", "&#x02251;" );
        s = StringUtils.replace( s, "&dotminus;", "&#x02238;" );
        s = StringUtils.replace( s, "&dotplus;", "&#x02214;" );
        s = StringUtils.replace( s, "&dotsquare;", "&#x022A1;" );
        s = StringUtils.replace( s, "&doublebarwedge;", "&#x02306;" );
        s = StringUtils.replace( s, "&downdownarrows;", "&#x021CA;" );
        s = StringUtils.replace( s, "&downharpoonleft;", "&#x021C3;" );
        s = StringUtils.replace( s, "&downharpoonright;", "&#x021C2;" );
        s = StringUtils.replace( s, "&drbkarow;", "&#x02910;" );
        s = StringUtils.replace( s, "&drcorn;", "&#x0231F;" );
        s = StringUtils.replace( s, "&drcrop;", "&#x0230C;" );
        s = StringUtils.replace( s, "&dsol;", "&#x029F6;" );
        s = StringUtils.replace( s, "&dtdot;", "&#x022F1;" );
        s = StringUtils.replace( s, "&dtri;", "&#x025BF;" );
        s = StringUtils.replace( s, "&dtrif;", "&#x025BE;" );
        s = StringUtils.replace( s, "&duarr;", "&#x021F5;" );
        s = StringUtils.replace( s, "&duhar;", "&#x0296F;" );
        s = StringUtils.replace( s, "&dwangle;", "&#x029A6;" );
        s = StringUtils.replace( s, "&dzigrarr;", "&#x027FF;" );

        // E's
        s = StringUtils.replace( s, "&eng;", "&#x0014B;" );
        s = StringUtils.replace( s, "&ENG;", "&#x0014A;" );
        s = StringUtils.replace( s, "&eth;", "&#x000F0;" ); // ð
        s = StringUtils.replace( s, "&ETH;", "&#x000D0;" ); // Ð
        s = StringUtils.replace( s, "&ETH", "&#x000D0;" );  // Ð
        s = StringUtils.replace( s, "&eth", "&#x000F0;" );  // ð
        s = StringUtils.replace( s, "&eacute;", "&#x000E9;" ); // é
        s = StringUtils.replace( s, "&Eacute;", "&#x000C9;" ); // É
        s = StringUtils.replace( s, "&Eacute", "&#x000C9;" );  // É
        s = StringUtils.replace( s, "&eacute", "&#x000E9;" );  // é
        s = StringUtils.replace( s, "&ee;", "&#x02147;" );
        s = StringUtils.replace( s, "&ecaron;", "&#x0011B;" );
        s = StringUtils.replace( s, "&Ecaron;", "&#x0011A;" );
        s = StringUtils.replace( s, "&ecirc;", "&#x000EA;" ); // ê
        s = StringUtils.replace( s, "&Ecirc;", "&#x000CA;" ); // Ê
        s = StringUtils.replace( s, "&Ecirc", "&#x000CA;" );  // Ê
        s = StringUtils.replace( s, "&ecirc", "&#x000EA;" );  // ê
        s = StringUtils.replace( s, "&ecy;", "&#x0044D;" );
        s = StringUtils.replace( s, "&Ecy;", "&#x0042D;" );
        s = StringUtils.replace( s, "&edot;", "&#x00117;" );
        s = StringUtils.replace( s, "&eDot;", "&#x02251;" );
        s = StringUtils.replace( s, "&Edot;", "&#x00116;" );
        s = StringUtils.replace( s, "&efr;", "&#x1D522;" );
        s = StringUtils.replace( s, "&Efr;", "&#x1D508;" );
        s = StringUtils.replace( s, "&egrave;", "&#x000E8;" ); // è
        s = StringUtils.replace( s, "&Egrave;", "&#x000C8;" ); // È
        s = StringUtils.replace( s, "&Egrave", "&#x000C8;" );  // È
        s = StringUtils.replace( s, "&egrave", "&#x000E8;" );  // è
        s = StringUtils.replace( s, "&Element;", "&#x02208;" );
        s = StringUtils.replace( s, "&emacr;", "&#x00113;" );
        s = StringUtils.replace( s, "&Emacr;", "&#x00112;" );
        s = StringUtils.replace( s, "&EmptySmallSquare;", "&#x025FB;" );
        s = StringUtils.replace( s, "&EmptyVerySmallSquare;", "&#x025AB;" );
        s = StringUtils.replace( s, "&eogon;", "&#x00119;" );
        s = StringUtils.replace( s, "&Eogon;", "&#x00118;" );
        s = StringUtils.replace( s, "&eopf;", "&#x1D556;" );
        s = StringUtils.replace( s, "&Eopf;", "&#x1D53C;" );
      

()

Fri, 21 May 2010

3:06 PM - Mozilla Thunderbird bug

Unlike every other email program on the planet, Mozilla Thunderbird requires one to use mime related instead of mixed mode to embed images within email attachments using the cid stuff.  This has caused me great pain this week.  Outlook worked, iphones worked, windows mail in vista, web based mail, gmail, ... everything except Thunderbird. 

Our implementation sends embedded mails using the javamail api.  Use  final Multipart mp = new MimeMultipart("related");


        // now do the image file attachments
            Set keys;
            keys = images.keySet();

            for (Object key : keys)
            {
                String hashkey = (String) key;
                final MimeBodyPart mbp2 = new MimeBodyPart();
                final DataSource source = new FileDataSource( images.get( hashkey ) ) {

                    @Override
                    public String getContentType()
                    {
                        return "image/jpeg";
                    }
                };
                mbp2.setDataHandler( new DataHandler( source ) );
                mbp2.setHeader( "Content-ID", "<i" + hashkey + ">" );
                mbp2.setFileName( hashkey + ".jpg" );  // temporary hack
                mbp2.setDisposition( Part.INLINE );
                mp.addBodyPart( mbp2 );
            }

()