14 September, 2005

Non-machanic keyboard is much secure!

I read a interesting post on /. today. All it is talking about is, what you type on your keyboard (including the password), when recorded and analysis properly, can be discovered.

By the way, it is some kind of cryptanalysis, and with a quick search, I learn a little more (Acoustic cryptanalysis)!

Well, this remind me, there are some non-mechanical keybowrd, like using some kind of light-projected keyboard. Is it more secure in this sense?

13 September, 2005

Scripting XUL with Python? Great!

Just have a great news from Daily Python. Mozilla forks are working to make XUL scriptable by Python. That's means, when the work is done, I can write extension in Python, or develop XUL based apps in Python!

I've also noticed that, PyXPCOM, yes, writing XPCOM component in Python!!

01 September, 2005

Clarification of current state of X and Graphics on Linux

Today on slashdot.org, I found this article, it feed me the updated info about the state of Linux Graphics at the moment. Thanks a lot jonsmirl :)

It also provide a lot of resources / links, like fresco, Xgl, Xegl, OpenGL ES, etc. It's great!

23 July, 2005

ID3 tag data and encoding issue

I finally spend some time to look into the encoding issue about ID3 tag. The following is what I have studied so far!

ID3v1 and ID3v2 are two totally different thing despite the common purpose. ID3v1 (and ID3v1.1) is very simple, 128 bytes appended at the end of the media file. While ID3v2 is a more formal specification.tag is an informal standard.

ID3v1 and ID3v1.1
ID3v2 website has two very good articles for the background and format
ID3v2 (version 2.2.0, 2.2.1, 2.3.0 and v2.4.0)
Much more mature specification for msuic tag data. The documents can be found here. (ID3v2.0)

What encoding is used to encode text data?

With ID3v1, there is no place for storing the encoding / codepage of text data (title, artist name, etc). In practice, locale dependent encoding is used!

With ID3v2, text data is stored inside a 'text frame'. Within each text frame, there is a frame header, followed by a "frame len byte", which followed by a "text encoding description byte". However, according the specification, if text data is not stored as Unicode, it must be stored as ISO8859-1.

For ID3v2.3.0, the "text encoding description byte" has two possible values:

0x00 : ISO8859-1 (latin1)
0x01 : Unicode (UCS2?) (MUST BEGIN with BOM and END with UTF16 0x0000)

For ID3v2.4.0, the "text encoding description byte" has four possible values:

0x00 : ISO8859-1 (latin1)
0x01 : UTF-16 (BEGIN with BOM and END with 0x0000)
0x02 : UTF-16BE (DO NOT BEGIN with BOM and END with 0x0000)
0x03 : UTF-8

Issues:

ID3v2 provide much more functionality and is far better than the old ID3v1. However, there is a problem plaguing the usability of ID3v2. To comply with the specification, text data inside the ID3v2 tag MUST be encoded as Unicode, if it cannot be encoded as ISO8859-1. Some software will just encode the text data with the legacy encoding (which is used as in ID3v1), and set the "text encoding description byte" as 0x00 (ISO8859-1). When user use another truly standard compliant software to open the music file, the tag will not be rendered correctly, as the software will treat the text data as ISO8859-1, even though the text data is actually encoded with legacy encoding. Clearly, the problem will not go away in the near future, as most users will have hundreds (if not thousand) of music files in their harddisk, is it impractical to convice everyone converting the old files just for standard conformance.

This problem is discussed a few times in various mailing list / bugzilla.

  1. Rhythmbox mailing lists
  2. id3lib mailing lists
  3. Gstreamer bugzilla

Workaround:

To overcome the problem, existing software have to provide some workaround. For example, by setting 'GST_ID3_TAG_ENCODING', gst-plugins-mad will interprete non-unicode text data as legacy encoded strings instead of ISO8859-1 encoded strings. $ export GST_ID3_TAG_ENCODING=big5hkscs
$ rhythmbox
For eyeD3, I have made a patch myself to do something similar, you can use '--legacy-encoding' to specify the legacy encoding to use. $ eyeD3 --legacy-encoding big5hkscs [other options...]

Related software

The following list is only a small subset of existing software, but this will be those of which I have/will play attention to :)

24 June, 2005

Mechanize, pullparser, and python-spidermonkey

I found this site accidentally, and quite surprised! Especially python-spidermonkey, the python binding of the mozilla js engine. I shall try this out if I have time!

For the mechanize and pullparser, here is an example.

TCP port 32768?

When I use netstat to check about the opened ports, I found this:

netstat -tlnpe Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name tcp 0 0 0.0.0.0:32768 0.0.0.0:* LISTEN 0 16493 -

I'm shocked, what's this port? Hacked?

Then, I checked /proc/net/tcp:

sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode 0: 00000000:8000 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16493 1 cd672000 3000 0 0 2 -1

I think, may be just opened by kernel, but why?

Just for save, I goggled about this and found this interesting thread! hm... rpc

What happen?

I used to mount a few NFS shared directory from another box, hence, the nlockmgr of RPC is always running and listening of port 32768. nlockmgr is a RPC service which is used for NFS lock recovery when machine crashed or reboot.

This time, I learn the usefulness of this commands :)

$ lsof -i tcp:32768 $ fuser -v -n tcp 32768 $ rpcinfo -p 127.0.0.1

22 June, 2005

My frist try on GIMP script-fu!

I have just read the tutorial on GIMP about script-fu!

Scripting in GIMP is pretty easy, but debuging is quite trouble. This tutorial help me a bit, at least, I leart to use 'print' to do basic debug.

Though scheme is not difficult, It would be better if I can do it in python, but I can't found any python-fu tutorial, where is it?

So what I've done? I'm managed to resize a set of images to another fixed size, I try to do it first with ImageMagick by "convert -resize", but ImageMagick doesn't convert the image properly. When I say -resize 130x41, the resulting image is 128x41 (Am I misunderstand this option?). May be it is only some rounding problem but, not acceptable in this case. On the other hand, GIMP do it great, so I try to script it!

The solution is simple. First write a small script-fu, and run gimp in batch mode.

The script is like this: ; It will resize image in batch (define (bresize filename width height) ; load (set! img (car(gimp-file-load RUN-NONINTERACTIVE filename filename))) ; not really used, but gimp-file-save need it (set! lyr (car(gimp-image-get-active-layer img))) ; scale (gimp-image-scale img width height) ; save (file-png-save-defaults RUN-NONINTERACTIVE img lyr filename filename) ) (script-fu-register "bresize" "/Xtns/Script-Fu/byMe/Batch resize" "Batch resize image" "Zarick Lau " "Zarick Lau" "2005-06-22" "" SF-VALUE "filename" "a.png" SF-VALUE "width" "130" SF-VALUE "height" "41")

And to run it in a batch, easy!

$ for f in *.png; do \ gimp-2.0 -i -b "(bresize \"$f\" 130 41)" '(gimp-quit 0)'; \ done

14 June, 2005

Building Redhat Enterpris Linux from Source

I have to make some time to try this out, quite interesting!, By the way, RHEL4 source is here.

03 June, 2005

My OFBiz Journey

The following my advice, do it first!

  • Read existing documents and wiki
  • OFBiz is a "big" software, the best way to learn it, is to set it up, and try to use it
  • After setting it up, try to use the webtools, it will let you access most of the administrative functions
  • Inside the "applications" directory, the ecommerce application is a web store, which use a lots of OFBiz functionality, by studying this application you can have a more solid understanding about OFBiz, for example, how a HTTP request is processed, and how a view composed, also you can have a taste on how to use the entity engine.

This document record what I have learned when I try to use 'ecommerce' module as a webstore for a chocolate company

Datasource
Use the latest version from subversion repository directly $ svn co http://svn.ofbiz.org/svn/ofbiz/trunk ofbiz-trunk

By default, ofbiz use 'derby' a Java based standalone database, you can change to use other data source. All you have to do is:

  1. Create a database 'ofbiz' (or whatever you want)
  2. Create user 'ofbizUserName' and 'ofbizPassword' and grant the permission appropriately.
  3. Then you can chagne the setting so that ofbiz will use your database.
    The file should be changed is: [ofbiz-trunk/framework/entity/config/entityengine.xml]

For all the delegator element, change the attr. 'datasource-name'. For example, I use MySQL, I will change this attr to 'localmysql'

Why localmysql, because, more detail data source parameter is defined at the lower half that XML config file. The name (or ID) for the MySQL config is 'localmysql'.

Then you should also change the setting for the datasource. So found the 'datasource' element and change the following entry:

  • jdbc-uri
  • jdbc-username
  • jdbc-password

NOTE: The "table-type" attr will make ofbiz generate wrong SQL to create table. So comment it out.
Seed Data

After setting the datasource, it is ready to install the seed data Seed data is a set of basic data will is required for ofbiz to run normally, for example, some UOM, and Geograpical data. To install, you can run $ ant run-install-seed

OFBiz will startup in 'install mode'. In this mode, it will found out all 'seed data' and install (insert) it into the datasource

This seed data is defined in XML file with root element named 'entity-engine-xml'. Each element in the XML correspsond to a ROW in the datasource.

The list of all seed data file is defined in ofbiz-component.xml. Each component in OFBiz will have one such file. It is read by the component loader when OFBiz starting up (as install mode).

For example, under framework/common, ofbiz-component.xml contains these lines: <entity-resource type="data" reader-name="seed" loader="main" location="data/CommonTypeData.xml"/>

Demo data

Demo data is similar to seed data, they are defined in the same way. The differnce are the data is not 'required' by OFBiz operation, rather, they are used as demo. For example, "applications/ecommerce" has provided a set of demo data, if the data is installed, you can see a 'fake' product catalog when you asscess the ecommerce website

Another difference between seed and demo data is, how it is defined inside ofbiz-component.xml; For demo data, the element is look like this: <entity-resource type="data" reader-name="demo" loader="main" location="data/DemoProduct.xml"/> Please note that reader-name is now "demo", but no "seed"

To install all demo data, you can $ java -jar ofbiz.jar install readers=demo

However, if you are now going to 'customize', there is no reason to install the "stock" demo data. You have at least two choice.

Simply remove all those demo data xml or comment out the relevant lines in various ofbiz-component.xml

Other configuration

Under base/config directory, you can found some base configuration. Among the list, the most important files:

  • ofbiz-containers.xml
  • cache.properties
  • debug.properties

The meaning of cache.properties and debug.properties is obvious. For the ofbiz-containers.xml, it is the core config file for OFBiz OFBiz will parse this during normal startup. Inside this file you can found some some definition for various container which is started up by OFBiz at the very first moment.

The most import should be cataline-container (tomcat). You can adjust various tomcat properties at there. For example, changing the listening ports, and comment out ajp stuff, if not really used, etc

30 April, 2005

Ant has so much (optional) dependency!

I have just found that my ant is pretty old, and try to upgrade it. But emerge show me this:

Calculating dependencies ...done! [ebuild N ] dev-java/ant-core-1.6.2-r2 -doc 6,134 kB [ebuild N ] dev-java/jakarta-regexp-1.3-r2 -doc -jikes -source 124 kB [ebuild N ] dev-java/bsh-2.0_beta1-r1 +gnome -kde 303 kB [ebuild N ] dev-java/log4j-1.2.8-r1 -doc 2,454 kB [ebuild N ] dev-java/avalon-logkit-bin-1.2.2 -doc 409 kB [ebuild N ] dev-java/commons-logging-1.0.4 -doc -jikes -junit 98 kB [ebuild N ] dev-java/libreadline-java-0.8.0-r1 -doc 75 kB [ebuild N ] dev-java/jython-2.1-r5 -jikes +readline 2,715 kB [ebuild N ] dev-java/jakarta-oro-2.0.8-r1 -doc -examples -jikes -source 337 kB [ebuild N ] dev-java/xerces-2.6.2-r1 -doc 5,200 kB [ebuild N ] dev-java/commons-net-1.2.2-r1 -doc -jikes -source 174 kB [ebuild N ] dev-java/jdepend-2.8.1 -doc -jikes 377 kB [ebuild N ] dev-java/jzlib-1.0.5 -doc -jikes 48 kB [ebuild N ] dev-java/gnu-crypto-2.0.1 -doc 3,804 kB [ebuild N ] dev-java/jsch-0.1.18 -doc -jikes 190 kB [ebuild N ] dev-java/bcel-5.1-r1 -doc -jikes -source 12,338 kB [ebuild N ] dev-java/servletapi-2.3-r1 -doc -jikes 124 kB [ebuild N ] dev-java/bsf-2.3.0-r2 -doc -jikes -jython -rhino 1,021 kB [ebuild N ] dev-java/javacup-0.10k 186 kB [ebuild N ] dev-java/xalan-2.6.0-r2 -doc -jikes -source 5,737 kB [ebuild N ] dev-java/junit-3.8.1-r1 -doc -jikes 431 kB [ebuild N ] dev-java/antlr-2.7.3 1,316 kB [ebuild N ] dev-java/commons-collections-3.1 -doc -jikes 1,110 kB [ebuild N ] dev-java/commons-beanutils-1.6.1-r1 -doc -jikes 144 kB [ebuild N ] dev-java/rhino-1.5.5-r1 -doc -jikes 1,505 kB [ebuild N ] dev-java/ant-tasks-1.6.2-r5 -javamail -noantlr -nobcel -nobeanutils -nobsh -nocommonslogging -nocommonsnet -nojdepend -nojsch -nojython -nolog4j -nooro -noregexp -norhino -noxalan -noxerces 0 kB [ebuild U ] dev-java/ant-1.6.2-r6 [1.5.4-r1] 0 kB Total size of downloads: 46,366 kB Do you want me to merge these packages? [Yes/No]

Ant has so many deps? Anyway, it is building now ~

24 January, 2005

Playing with Thunderbird code

Modify thunderbird so it will append some text at the end of mail body

It seems a easy task, but in fact, I can only achieve via modifying the code (Not just by using preference/cmdline argument). The modification is just a few lines, but it does cost me a lot of time to study. Meanwhile, I've learn quite a lot about Mozilla. In this post, I emphasis on how I modify the code. Other related stuff (build system, localization, etc) should be in anohter post :)

Current situation:

Mozilla already accept a cmdline argument which will do similar stuff $ thunderbird -compose 'to=foo@no.net,attachment=file:///tmp/a.txt' However, it isn't exactly what I want. In mail composer, user can have a preference called "signature", you can define a file which will be automatically appended to the mail body, whenever you compose a new mail. This doesn't solve my problem though, because this options is per user profile. Also, if I use this signature as the "mail footer' as I want, user will be able to alter it easily. On the other hand, user can't use both the "signature" and the "mailfooter" at the same time. Hence, I decide to make thunderbird accept one more parameters: mailfooter=/tmp/text.txt So, if I execute the cmdline like this: $ thunderbird -compose 'to=foo@nowhere.net,mailfooter=/tmp/text.txt' Thunderbird will start the mail composer and append /tmp/text.txt to the mail body, just like how it due with "signature".

So, how to archieve this?

Mozilla is component based. The core technology is called 'xpcom' which stands for 'Cross Platform COM'. Basically most of the functionality as packages as component. The interface of the component are defined in .idl files. On the other hand, all UI related stuff are packaged inside "chrome", eg: translation, UI, etc. The file 'mail/components/compose/MsgComposeCommand.js' is the file responsible for parsing the -compose argument and control the UI interaction (handling UI event.). In mozilla, the UI is defined in XUL, the core logic is component which are actually some shared object (eg libmail.so). "MsgComposeCommand.js" is the glue between the XUL and the components. The modification of "MsgComposeCommand.js":
# Pass 'mailfooter' argument to nsIMsgComposeParams object, (then pass to nsIMsgCompose)
@@ -1206,6 +1206,8 @@
        params.originalMsgURI = args.originalMsg;
        if (args.preselectid)
          params.identity = getIdentityForKey(args.preselectid);
+       if (args.mailfooter)
+         params.mailFooterPath = args.mailfooter;
        if (args.to)
          composeFields.to = args.to;
        if (args.cc)

extremem trivial:
In the function ComposeStartup. "params" is a nsIMsgComposeParams, we just put "args.mailfooter" to this object. The nsIMsgCompose object will use this piece of data. Well, nsIMsgComposeParams is one of the components, which need to be modified, so that we can set/get the attributes "mailFooterPath". In order to achieve this, modify these files:
  • mailnews/compose/public/nsIMsgComposeParams.idl (the interface should have the new attr)
  • mailnews/compose/src/nsMsgComposeParams.h (the component should have the new attr)
  • mailnews/compose/src/nsMsgComposeParams.cpp (need to provide the getter/setter func)
And the following is the diff:
--- nsIMsgComposeParams.idl     2001-09-29 04:06:51.000000000 +0800
+++ nsIMsgComposeParams.idl     2005-01-24 10:57:40.000000000 +0800
@@ -76,6 +76,7 @@
   attribute MSG_ComposeType       type;
   attribute MSG_ComposeFormat     format;
   attribute string                originalMsgURI;
+  attribute string                mailFooterPath;
   attribute nsIMsgIdentity        identity;
                                                                               
   attribute nsIMsgCompFields      composeFields;

--- nsMsgComposeParams.h      2001-09-29 04:06:54.000000000 +0800
+++ nsMsgComposeParams.h   2005-01-24 10:57:40.000000000 +0800
@@ -51,6 +51,7 @@
   MSG_ComposeType               mType;
   MSG_ComposeFormat             mFormat;
   nsCString                     mOriginalMsgUri;
+  nsCString                     mMailFooterPath;
   nsCOMPtr      mIdentity;
   nsCOMPtr    mComposeFields;
   PRBool                        mBodyIsLink;

--- nsMsgComposeParams.cpp    2003-09-08 06:55:30.000000000 +0800
+++ nsMsgComposeParams.cpp 2005-01-24 10:57:40.000000000 +0800
@@ -95,6 +95,20 @@
     return NS_OK;
   }

+  /* attribute string mailFooterMail; */
+  NS_IMETHODIMP nsMsgComposeParams::GetMailFooterPath(char * *aMailFooterPath)
+  {
+    NS_ENSURE_ARG_POINTER(aMailFooterPath);
+
+    *aMailFooterPath = ToNewCString(mMailFooterPath);
+    return NS_OK;
+  }
+  NS_IMETHODIMP nsMsgComposeParams::SetMailFooterPath(const char * aMailFooterPath)
+  {
+    mMailFooterPath = aMailFooterPath;
+    return NS_OK;
+  }
+
   /* attribute nsIMsgIdentity identity; */
   NS_IMETHODIMP nsMsgComposeParams::GetIdentity(nsIMsgIdentity * *aIdentity)
   {
Finally, I need to modify nsMsgCompose.cpp and nsMsgCompose.h. This component will responsible to represent a mail composition. During nsMsgCompose::Initialize() will shall extract relevant info from the input parameter nsMsgComposeParams, so it should extract the 'mailfooter' parameter and store it inside the object. Then, when nsMsgCompose::InitEditor() is called. The call trace will be something like:
> InitEditor()
   > BuildBodyMessageAndSignature()
      > ProcessSignature()
      > ConvertAndLoadComposeWindow()
The modification will be: after calling ProcessSignature(), I made another call "ProcessFooter()", will function need to be implement (but it should be similar to ProcessSignature), after that, append the output of ProcessFooter into tSignature (string object) which will further handled by ConvertAndLoadComposeWindow(). The following is the final piece of modification:
--- nsIMsgCompose.idl   2004-05-17 14:14:57.000000000 +0800
+++ nsIMsgCompose.idl   2005-01-24 10:57:40.000000000 +0800
@@ -234,6 +234,10 @@
                                   in boolean aQuoted,
                                   inout nsString aMsgBody);

+  /* Append the footer defined in the cmdline argument */
+  [noscript] void processFooter(in nsStringRef aPath,
+                                inout nsString aMsgBody);
+
   /* set any reply flags on the original message's folder */
   [noscript] void processReplyFlags();
   [noscript] void rememberQueuedDisposition();

--- nsMsgCompose.cpp       2004-10-29 14:51:35.000000000 +0800
+++ nsMsgCompose.cpp       2005-01-24 11:00:23.000000000 +0800
@@ -706,6 +706,10 @@
   nsXPIDLCString originalMsgURI;
   params->GetOriginalMsgURI(getter_Copies(originalMsgURI));
                                                                                
+  nsXPIDLCString mailFooterPath;
+  params->GetMailFooterPath(getter_Copies(mailFooterPath));
+  mMailFooterPath = mailFooterPath;
+
   nsCOMPtr composeFields;
   params->GetComposeFields(getter_AddRefs(composeFields));
                                                                                
@@ -3322,6 +3326,134 @@
 }
                                                                                
 //
+// This will process the mail footer file for the user. This method
+// will always append the results to the mMsgBody member variable.
+//
+nsresult
+nsMsgCompose::ProcessFooter(nsString& aPath, nsString *aMsgBody)
+{
+  nsresult      rv = NS_OK;
+
+  // Note: We will have intelligent signature behavior in that we
+  // look at the signature file first...if the extension is .htm or
+  // .html, we assume its HTML, otherwise, we assume it is plain text
+  PRBool        useSigFile = PR_FALSE;
+  PRBool        htmlSig = PR_FALSE;
+  nsAutoString  sigData;
+  nsAutoString  sigOutput;
+
+  useSigFile = PR_FALSE;
+  if (!aPath.IsEmpty())
+  {
+    // XXX create nsILocalFile from 'path'
+    nsCOMPtr sigFile;
+    rv = NS_NewLocalFile (aPath, PR_TRUE, getter_AddRefs(sigFile));
+    //rv = sigFile->InitWithPath(aPath);
+    if (NS_SUCCEEDED(rv)) {
+      useSigFile = PR_TRUE; // ok, there's a signature file
+      // Now, most importantly, we need to figure out what the content type is for
+      // this signature...if we can't, we assume text
+      nsXPIDLCString sigContentType;
+      nsresult rv2; // don't want to clobber the other rv
+      nsCOMPtr mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv2));
+      if (NS_SUCCEEDED(rv2)) {
+        rv2 = mimeFinder->GetTypeFromFile(sigFile, getter_Copies(sigContentType));
+        if (NS_SUCCEEDED(rv2)) {
+          if (sigContentType.Equals(TEXT_HTML, nsCaseInsensitiveCStringComparator()))
+            htmlSig = PR_TRUE;
+        }
+      }
+    }
+  }
+
+  // Now, if they didn't even want to use a signature, we should
+  // just return nicely.
+  if (!useSigFile || NS_FAILED(rv))
+    return NS_OK;
+
+  nsFileSpec    testSpec(aPath);
+  //nsFileSpec    testSpec("/tmp/tmp.txt", 12);
+
+  // If this file doesn't really exist, just bail!
+  if (!testSpec.Exists())
+    return NS_OK;
+
+  static const char      htmlBreak[] = "
"; + static const char dashes[] = "-- "; + static const char htmlsigopen[] = "
"; + static const char htmlsigclose[] = "
"; /* XXX: Due to a bug in + 4.x' HTML editor, it will not be able to + break this HTML sig, if quoted (for the user to + interleave a comment). */ + static const char _preopen[] = "
";
+  char*                  preopen;
+  static const char      preclose[] = "
"; + + PRInt32 wrapLength = 72; // setup default value in case GetWrapLength failed + GetWrapLength(&wrapLength); + preopen = PR_smprintf(_preopen, wrapLength); + if (!preopen) + return NS_ERROR_OUT_OF_MEMORY; + + // is this a text sig with an HTML editor? + if ( (m_composeHTML) && (!htmlSig) ) + ConvertTextToHTML(testSpec, sigData); + // is this a HTML sig with a text window? + else if ( (!m_composeHTML) && (htmlSig) ) + ConvertHTMLToText(testSpec, sigData); + else // We have a match... + LoadDataFromFile(testSpec, sigData); // Get the data! + + // Now that sigData holds data...if any, append it to the body in a nice + // looking manner + if (!sigData.IsEmpty()) + { + if (m_composeHTML) + { + sigOutput.AppendWithConversion(htmlBreak); + if (htmlSig) + sigOutput.AppendWithConversion(htmlsigopen); + else + sigOutput.AppendWithConversion(preopen); + } + else + sigOutput.AppendWithConversion(CRLF); + + if (sigData.Find("\r-- \r", PR_TRUE) < 0 && + sigData.Find("\n-- \n", PR_TRUE) < 0 && + sigData.Find("\n-- \r", PR_TRUE) < 0) + { + nsDependentSubstring firstFourChars(sigData, 0, 4); + + if (!(firstFourChars.Equals(NS_LITERAL_STRING("-- \n")) || + firstFourChars.Equals(NS_LITERAL_STRING("-- \r")))) + { + sigOutput.AppendWithConversion(dashes); + + if (!m_composeHTML || !htmlSig) + sigOutput.AppendWithConversion(CRLF); + else if (m_composeHTML) + sigOutput.AppendWithConversion(htmlBreak); + } + } + + sigOutput.Append(sigData); + + if (m_composeHTML) + { + if (htmlSig) + sigOutput.AppendWithConversion(htmlsigclose); + else + sigOutput.AppendWithConversion(preclose); + } + } + + aMsgBody->Append(sigOutput); + PR_Free(preopen); + return NS_OK; +} + +// // This will process the signature file for the user. This method // will always append the results to the mMsgBody member variable. // @@ -3555,6 +3687,17 @@ if (addSignature) ProcessSignature(m_identity, PR_FALSE, &tSignature); + // Footer is 'special feature' added for ThizMII + PRBool addFooter = PR_TRUE; + nsAutoString tFooter; + + if (!mMailFooterPath.IsEmpty()) { + nsAutoString tFooterPath = NS_ConvertASCIItoUTF16(mMailFooterPath); + ProcessFooter(tFooterPath, &tFooter); + } + + tSignature.Append(tFooter); + // if type is new, but we have body, this is probably a mapi send, so we need to // replace '\n' with
so that the line breaks won't be lost by html. // if mailtourl, do the same.
To conclude, I have edited the following:
  • MsgComposeCommand.js
  • nsIMsgComposeParams.idl
  • nsMsgComposeParams.cpp
  • nsMsgComposeParams.h
  • nsIMsgCompose.idl
  • nsMsgCompose.cpp
  • nsMsgCompose.h
And the basic logic is to let MsgComposeCommand.js parse the 'mailfooter=XXX' parameters, and inside MsgCompose object, actually read the file and append to the mail body.