Tuesday, March 02, 2010

Send HTML email from Cocoa. Take 2.

This is an alternate solution to sending HTML email from Cocoa, that takes advantage of python's email capabilities.



The python email package allows a much cleaner source when writing the code for sending the HTML email:

==================== htmlemail.py ==================

from email.mime import Multipart, Text, Image
import smtplib

email_from = "sender@domain.com"
email_to = "recipient@domain.com"
msg = Multipart.MIMEMultipart('alternative')
msg['Subject'] = "HTML email"
msg['From'] = email_from
msg['To'] = email_to

text = "Plain text version"
html = "<html><p>A little bit in <b>bold</b> and some <i>italic</i></p><p><img src="cid:55665566" /></p><p>The end</p></html>"

part1 = Text.MIMEText(text, 'plain')

# the embedded image
fp = open("/Users/diciu/Desktop/test.png")

# assemble the message based on the diagram
part2 = Multipart.MIMEMultipart('related')
part21 = Text.MIMEText(html, 'html')
part22 = Image.MIMEImage(fp.read(), _subtype='PNG')
part22.add_header('Content-ID', '55665566')

msg.attach(part1)
part2.attach(part21)
part2.attach(part22)
msg.attach(part2)

s = smtplib.SMTP('some.smtp.server')
s.sendmail(email_from, email_to, msg.as_string())

s.quit()


To use the Python code from Cocoa, we use the Python framework:

==================== htmlemail.m ==================

// build with:
// gcc htmlemail.m -framework Cocoa -framework Python
//
#import <Cocoa/Cocoa.h>
#import <Python/Python.h>

int main()
{
NSAutoreleasePool * m = [NSAutoreleasePool new];
NSError * err = nil;
NSString * pycode = [NSString stringWithContentsOfFile:@"./htmlemail.py"
encoding:NSASCIIStringEncoding error:&err];

Py_Initialize();
PyRun_SimpleString([pycode cStringUsingEncoding:NSASCIIStringEncoding]);
Py_Finalize();

[m release];

return 0;
}

Sunday, February 28, 2010

BizBazar canceled

I've never written an opinion piece on this here blog, partly because this blog is more of a technical knowledge base I keep for myself and also because I don't like blurting out my thoughts in the forever archived searchable medium that is the internet.

I will make an exception because one of the things I care deeply about is gone. I find it surprising that I'm writing I care about a TV show, but this show I'm talking about was a special show, an example of a great team working hard for what I can only describe as true journalism.

Television in Romania


I don't really know how TV is elsewhere these days, but in Romania it's in a state of suffering. News shows are based on violence, be it physical or psychological violence. The viewer is fed 30 minutes of car accidents, rapes, killings, stories on raising taxes, raising prices, corruption in politics, repeated ad nauseam.

News are written to stir up feelings, with passion rather then detachment, thus it's a common occurrence for the news anchor to pass judgment when presenting the news. A suspect is more often than not shown as guilty, stories are written in such a manner that the viewer is gratified with a speedy one minute trial of the scene unraveled before him.

If it's a car accident, the reporter will interview a couple of eye witnesses that will describe how the driver involved in the accident was doing at least 100 KPH just before the accident. It's common knowledge that a human can estimate car speeds by just looking, right? The reporter will never fail to mention that the driver's license is less then 5 years old, not-so-subtly implying that lack of experience is a major contributing factor to the crash.


BizBazar


The show that's gone is called BizBazar and what made it special was that it always tried to present both sides of the story. It was refreshing to see a conversation between a host and his guest where the host is presenting the guest with an opportunity to speak his mind, although it's obvious their views on the matter diverge greatly.

BizBazar was one of the rare shows whose team did not claim expertise in every conceivable subject, and this was a very welcome change compared to the rest of the shows out there, whose hosts will prefer talking nonsense to admitting the subject at hand is a big unknown.

BizBazar has been a fresh take on the news, light years away from the paranoia that characterizes so much of our media these days.

My hat's off to the team, good luck in whatever you will try next, and thanks for all the fish.

Thursday, February 25, 2010

vim - replace command conditioned by preceding and following atoms

Going from:

John has one hundred apples
Jane has twelve hundred and fifty seven apples
Mary has thirty seven apples


to

John has one_hundred apples
Jane has twelve_hundred_and_fifty_seven apples
Mary has thirty_seven apples


in vim:

.,$s/\(has .*\)\@<= \(.* apples\)\@=/_/g

Wednesday, February 24, 2010

Compiling Valgrind on Snow Leopard

Checkout the valgrind sources:

svn co svn://svn.valgrind.org/valgrind/tags/VALGRIND_3_5_0 valgrind


Download Greg Parker's 10.6 patch
curl http://bugsfiles.kde.org/attachment.cgi?id=36999 > 10.6.patch
mv 10.6.patch ./valgrind


Apply the patch

cd valgrind
patch -p0 < 10.6.patch


Compile valgrind (if you want to run it on 64 bit binaries, use "./configure --build=amd64-darwin", otherwise you'll build it for 32 bits)
./autogen.sh
./configure
make


Install it
sudo make install


Run it
cristi:tmp diciu$ cat /tmp/test.c
int main()
{
char * t = malloc(1024);
char * w = "Ana";

t = w;
}
cristi:tmp diciu$ gcc -arch i386 /tmp/test.c
cristi:tmp diciu$ valgrind --leak-check=full /tmp/a.out
[..]
==938== HEAP SUMMARY:
==938== in use at exit: 1,344 bytes in 8 blocks
==938== total heap usage: 8 allocs, 0 frees, 1,344 bytes allocated
==938==
==938== 1,024 bytes in 1 blocks are definitely lost in loss record 4 of 4
==938== at 0xF666: malloc (vg_replace_malloc.c:195)
==938== by 0x1F66: main (in /tmp/a.out)

Wednesday, January 06, 2010

Using the Snow Leopard sandbox

First and foremost, a warning that's prominently displayed in all sandbox definition files:


WARNING: The sandbox rules [..] currently constitute Apple System Private Interface and are subject to change at any time and
without notice. The contents of this file are also auto-generated and not user editable; it may be overwritten at any time.


The sandbox being a private interface is probably the reason why the sandbox policy files are un-documented. However, in the standard Snow Leopard install there are a bunch of files that define sandboxing policies ( under /System/Library/Sandbox/Profiles/ and /usr/share/sandbox/) that can be used to derive new policy files.

When creating a policy file for HTTP, the most verbose section out of our sandbox definition turns out to be the section for allowing DNS queries.

http.sb



(version 1)
(debug deny)

;; DNS
(allow file-read-metadata
(literal "/var")
(literal "/etc"))

(allow ipc-posix-shm)

(allow mach-lookup
(global-name "com.apple.SystemConfiguration.configd")
(global-name "com.apple.system.notification_center"))


(allow network-outbound
(remote unix-socket (path-literal "/private/var/run/mDNSResponder")))

;; HTTP traffic
(allow network-outbound (remote ip4 "*:80"))

(deny default)



Our test program runs a couple of functions - a curl based HTTP client and a file read operation.

test.c



#include <sandbox.h>
#include <stdio.h>
#include <curl/curl.h>

size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
int i=0;
//exit after 80 chars
for(;i<size * nmemb && i < 80;i++)
fprintf(stderr, "%c", (unsigned char)((char *)buffer)[i]);
fprintf(stderr, "[..]\n");
}
int httpRequest()
{
CURL * easyhandle = curl_easy_init();
curl_easy_setopt(easyhandle, CURLOPT_URL, "http://loudhush.ro/changelog.txt");
curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_perform(easyhandle);
}
int readFile()
{
FILE * f = fopen("/etc/passwd", "r");
if(f == NULL)
{
perror("Error opening file");
return 1;
}

char line[1024];
while(fgets(line, 1023, f) != NULL)
fprintf(stdout, "Line: %s", line);

fclose(f);
return 0;
}
int main()
{
char * errbuf;
int retval = sandbox_init("/Users/diciu/Programming/sandbox/http.sb", SANDBOX_NAMED, &errbuf);
if(retval)
{
fprintf(stderr, "Sandbox init error: %s\n", errbuf);
return 1;
}
fprintf(stderr, "--- HTTP request ---\n");
httpRequest();
fprintf(stderr, "\n--- File read request ---\n");
readFile();
}



As expected, once the sandbox is initialized, HTTP requests go through but the file access operation is denied:


cristi:sandbox diciu$ ./a.out
--- HTTP request ---
LoudHush 1.3.21

- Fixed a couple of leaks in AccountController/Management
- Fix[..]

--- File read request ---
Error opening file: Operation not permitted


Attempts to go out of the sandbox are logged in syslog:

Jan 6 14:13:37 cristi sandboxd[59224]: a.out(59222) deny file-read-data /private/etc/passwd

Monday, January 04, 2010

LoudHush compatibility with Asterisk 1.4.26.2+ requires configuration on server

With recent versions of Asterisk, a security triggered upgrade broke the IAX protocol as LoudHush knows it, by adding a new IAX IE called CALLTOKEN (see IAX2 Security).

When trying to register with an IAX account with CALLTOKEN enabled, LoudHush will display "Registration rejected" and the Asterisk server will log:

chan_iax2.c: Call rejected, CallToken Support required.


To disable the CALLTOKEN functionality from the server, allowing LoudHush to register, the following lines need to be added in the iax configuration:

calltokenoptional = 0.0.0.0/0.0.0.0
maxcallnumbers = 16382

Friday, December 25, 2009

Sending HTML email from Cocoa

In a previous post I described sending email using c-client. This post takes things a little bit further and describes sending an email containing HTML content using the same c-client API.

First things first - people usually call HTML email an HTML fragment that refers inline images. These images are part of the email too and they are referred in the HTML source by their content id (using a special construct on the source reference, i.e. img src="cid:image_content_id").

Mail clients (at least Snow Leopard's Mail.app) expect the HTML fragment and the images they refer to be part of the same container. This container is a MIME multipart/related container.

The simplest HTML message that we can send is based on the following MIME multipart structure:




#include <c-client.h>

#include "c-client-interface.c"
int main()
{
#include "linkage.c"

char * hostlist[] = { "smtp.gmail.com/ssl/user=user@gmail.com/smtp", NULL };

SENDSTREAM * sndstream = smtp_open(hostlist, 0);
if(sndstream == NULL)
{
fprintf(stderr, "Can't open SMTP connection\n");
return 1;
}

BODY * body = mail_newbody();

ENVELOPE * env = mail_newenvelope();
ADDRESS * to = mail_newaddr();

env->from = mail_newaddr();
env->from->personal = strdup("User Name");
env->from->mailbox = strdup("username");
env->from->host = strdup("gmail.com");
env->subject = strdup("subject");

rfc822_parse_adrlist(&env->to, strdup("username"), strdup("destination.dom"));

char *text = (char *) fs_get (8*MAILTMPLEN);
strcpy(text, "test message \015\012\0");

#define HTMLfragment "<HTML><p>A little bit in <b>bold</b> and some <i>italic</i><br/><br/> </p><p><img src=\"cid:55665566\"></p><p>The end</p></HTML>"

/* We need three parts, a part for HTML, a part for the image and
a part that groups them (called related). */



PART * htmlpart, *imagepart, *related;
related = mail_newbody_part();
related->body.type = TYPEMULTIPART;
related->body.subtype = cpystr("related");

htmlpart = mail_newbody_part();
htmlpart->body.encoding = ENCBASE64;
htmlpart->body.contents.text.data = (char *) rfc822_binary (HTMLfragment, strlen(HTMLfragment), &htmlpart->body.contents.text.size);

htmlpart->body.parameter = mail_newbody_parameter ();
htmlpart->body.parameter->attribute = cpystr ("CHARSET");
htmlpart->body.parameter->value = cpystr ("US-ASCII");
htmlpart->body.type = TYPETEXT;
htmlpart->body.subtype = cpystr ("HTML");
htmlpart->body.description = cpystr ("html version");

//for brevity, we define a GIF file inline. in real code, you would probably load this from a file
unsigned char buf[] = {0x47, 0x49, 0x46,0x38,0x37,0x61,0x0c,0x00,0x0c,0x00,0xc2,0x04,0x00,0x00,0x24,0xff,0xff,0x00,0x00,0x00,0xa0,0x0b,0xf6,0x7a
,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x0c,0x00,0x0c,0x00,0x00,0x03,0x23,0x18,0xba,0xd3,0xbe,0xcc,0
x35,0xa8,0xe4,0xa4,0x76,0x50,0x42,0x2c,0xe4,0x9c,0x24,0x8c,0x02,0x48,0x00,0x28,0x40,0x8e,0x5c,0x8a,0xae,0xa3,0xfb,0xc2,0xb2,0x4a,0xcb,0xb0,0x50,0x27,0x0
0,0x3b};

/* allocate a MIME part for the image content at the end of the HTML part */
imagepart = (htmlpart->next = mail_newbody_part());
/* the image is binary so we encode in base64 */
imagepart->body.encoding = ENCBASE64;
imagepart->body.contents.text.data = (char *) rfc822_binary(buf, 86, &imagepart->body.contents.text.size);
imagepart->body.type = TYPEIMAGE;
imagepart->body.subtype = cpystr ("GIF");
imagepart->body.id = cpystr("<55665566>");

/* Our body's content is of type multipart and it contains a nested part
that is in fact the related part that we have created above */

body->type = TYPEMULTIPART;
body->subtype = cpystr("alternative");
body->nested.part = mail_newbody_part ();
body->nested.part->body.contents.text.data = text;
body->nested.part->body.contents.text.size = strlen (text);


related->body.nested.part = htmlpart;
body->nested.part->next = related;

long result = smtp_mail(sndstream, "MAIL", env, body);

if(sndstream)
smtp_close(sndstream);

mail_free_envelope(&env);
mail_free_body(&body);
}



Our message in Mail.app: