Hello, cyberpals.
I started to record a video yesterday and got interrupted part way through it
and then tried to continue and then realized
halfway through it that I was no longer recording. So I'm starting
a new video today. The work that I did yesterday was to start
displaying a message which seems kind
of basic but it's somewhat challenging.
because most e-mails
are quite large even if the body of them is small, and we
don't really want to download all of that over what
is basically a dialup modem connection.
And if we have like, a MIME multipart
message that has like a text/plain part
also a text/html part, we are
downloading stuff we can't use. And so
what we're going to do is basically use the you
can fetch like an entire message by running
fetch and then the message UID and then RFC822
which fetches the entire message in that format.
Or I guess the original format. But if you do fetch
the UID RFC822.text,
it will give you just the plaintext part
of the message, which is what we
want because it will be small. We won't have all the headers that we're
not going to display anyway and the
server will do all the hard work for us.
So I started implementing that yesterday and
one other thing that I did off-camera
I guess was just start cleaning
up the data types. I was reading
a Programming C with THINK C whateve
book and it basically
advised to never use the int data type in a
THINK C program for a Mac and the reason
for that is because each project can
define what an int is. So it
could either be two bytes or four bytes depending on whether you
have this checked or not. And somebody
brought this up in the #cyberpals IRC channel because they had downloaded
an archive of THINK C from
somebody else's machine where this was checked.
And I think this was checked. So they were getting like weird results.
And so if you have done that, make sure that
these two are off. If you are on a, like a Mac Plus
which is just a 68000 and
you probably want two byte ints.
So, basically everywhere that you're using an int, you have to
decide whether you wanted it two bytes and then call it a
short or four bytes and call it a long. So I basically done
that here everywhere in the code that it was using int
I clarified whether it should have been a short or a long.
Then one other thing that I ran into was that in format
strings. In like a modern
platform like OpenBSD, I would
typically use %ul for an unsigned long.
But that is not recognized by the THINK C
C library and the scanf and printf
functions, so you have to use lu
instead of ul. So if this was like
a signed long, you would use ld and lu
for unsigned long. So just something to be aware of.
If you are like printing something and it prints like
a wrong value, check the format string.
So the work that I did yesterday was basically to
hook up the click event on the
message list. And that is here.
It selects the new
message in the list that you clicked and if it was something different,
it calls LGetCell on the list
and set this global variable imap_message_to_load.
And then just like the mailbox list
sets imap_mailbox_to_open, the
IMAP loop, when it is idle and in a good
state for doing something, looks for
imap_message_to_load being not null and then,
where would it be...
How about we just find it?
So if we open the mailbox,
we would do that. And then if message_to_load
is set, we load the message.
And load_message calls
the fetch function on our UID and
fetches the RFC822.text and
then it parses the response and then sees how--
this should actually be an unsigned.
Because, I mean,
I guess it's possible that a server could give us a negative value
to try and trick us. But we would just fail malloc
I guess at that point. So let's make this unsigned.
It parses out the size of the message and
then it's like this continuation thing. So the next bunch of lines
that are up to this byte count will be our body.
So it does that, mallocs that length,
and then it starts to read the message and copies
it into our malloced
message body. And then I haven't finished this yet. So I need to run
this and see that this loop works properly
and breaks properly and all that. And then once
that's done, the next line that we get should be
the final OK, which will set done
and then break out of the loop. And then
it will call browser_view_message,
which is passed a message.
And it just sets the text of the TextEdit
control that we already have in the browser, sets--
adjusts the scroll bar, basically. And that
should be it. So let's jump into
this function and see that this is
working correctly.
Also, I created a new page on my website
at jcs.org/system6c
and it basically gives you all of the episodes in chronological
order. So if you are trying to link my videos
somewhere, link to my videos somewhere, you can
use that link instead of just like my home page or something, where all
the videos are in reverse chronological order and
somebody can view like an overview of what
I'm makin' here.
Alright, so let's do this
[ mumbling ]
Okay. So now, if we click on a message,
we should get thrown into the-- yep,
that's fine.
Okay, so now we're going to load the message.
We could probably just skip to our
breakpoint.
Retrieving message one.
Okay, so we have a length of only 4
bytes that's a small message that I sent and...
Okay, so we're going to call imap_get_line
which should hopefully have a line for us.
Let me just see that imap_line
is blank.
Okay, and that
is the entire message body. So that's 4 bytes. So copy
it into the body.
And then read should now,
be equal to len.
Cool, cool.
Oh, so this is the continuation of
the response to the fetch command, which is kind
of weird because
I guess if there were no new lines or anything
in the message body that could have all been on one line.
Seems unlikely, but...
Right, so it's this and then the continuation and then after
all of the message body, we get the closing parentheses
and then a new line.
So I guess we can just eat
the next line.
Also, somebody in the #cyberpals channel asked
why there was no shortcut by default on ExitToShell.
And I have made one. So I will
explain how you do that in case you want
to do it which you can actually do for any
classic Mac program. Just take a quick detour here.
You just open up a program in-- oops, that
wasn't the program we wanted-- in ResEdit,
and you can see all the program's resources.
So for this we're going to open up the THINK C debugger.
And because the application has a resource fork, you
can just open the resource fork of the
program. So we will open the debugger and
open up its menus.
And then the-- I believe
it was the debug menu-- and then by
default, ExitToShell
had no shortcut.
So you can just put whatever you want here and make a
new shortcut and the
system toolbox will set
all that up for you. So if you
hit that shortcut, it will act as if
you clicked on the menu so you can
edit any program and change any command shortcut
here and it should just work. You don't have to mess around
with the program internals or anything. So you would just after
changing that hit Save and you're good
to go. So what
did I want to do? We need to eat one more line here
which should be the final continuation so we can just do
imap_get_line and then we'll just say that we're discarding it.
And maybe we'll cast it to void just to make it perfectly clear what we are doing.
Okay, and then that will break.
So we need to create
[ child yelling in background ]
that as well.
Just in case it returned something
bogus-- actually, I guess we should wait until that returns
something more than zero.
Is less than zero or...
Ugh, that's annoying.
Alright, we will assign that to len.
Nope, it's signed.
[ child yelling in background ]
If it's less than one, we have
an error. If it's zero there was no line and if it's
one or more, we have a line. So, if it's
not zero, we have an error,
right? Right,
So while it's zero do nothing.
And then we clear it.
Ok, I think is what we want that to
look like. Let us try it.
It's pretty remarkable that on a simple, text/plain
message of 5 or 10 lines,
that the headers, like if
you send from an external server
to your server,
like, I just got this message from
somebody, it has like a five line body
and it has 164 lines of header.
And most of them are like,
you know, anti-spam junk.
It's crazy. Anyway.
Okay. And then click on a message
and we can skip to where we know it
works. Here.
My son is getting a bath and he does not like getting
his hair washed.
Okay, so now we just have our continuation line.
Right? Yep.
And then we can probably
just go again and
we will break here again.
And then we should have just the final OK.
And we do. We get the OK to complete it.
So now we will break and
break out of the loop, that is.
And then show the message.
And that sets... let's make sure this is right.
Message booty!
Hi.
Cool. So that sets the text.
And then this sets up the scroll bar for
the TextEdit control because we have to do that
manually, it's kind of annoying.
But like for lists, they have their own scroll bars.
vlines should be 1.
What this basically does is, counts how
many lines are being...
Oh, how many lines are visible. And then
this is automatically calculated for us when you set
the text. That is the number of lines in the TextEdit
control. Or not.
What is this value?
Should be 1.
Or is it also 17?
Two?!
visible lines is greater... okay,
that's just clamping it and then so
the maximum value and
then the current value of the scrollbar. So would
that work? I did, except it doesn't know how to show a
newline character. Cool.
So now what we probably need to do is free that body
because these can be quite long.
See if this works, this is a longer message.
Okay,
Nope, we got a weird error. Okay, let's
figure out why that had a weird error.
Maybe my assumptions for this
line continuation stuff are not right.
So I'm still
not sure what the caching strategy is going to be. Because obviously you
don't want to have to keep re-downloading messages over a dialup connection.
But I'm not sure if I should make my
own file on the file system and like, just cram
text in there or if I should if they're supposed to be like paged in
and out of the resource fork.
I'll have to look into that.
Just make sure that I have a breakpoint set up here.
Okay, and then we will click on it.
Okay, so the length
is going to be 1929 and then...
We'll go into our reading loop.
Yup.
Okay. And then we
read 1929 in length. Okay,
so then the next line should just be our
continuation line, the end
of it. Yep.
Okay. And this
should be the final OK. And it is.
[ child yelling in background ]
I wonder if there's another line after
that. And that's why that failed because it was, oh, okay.
So it looks like we need to strip out newlines
because the TextEdit
control just wants the \r. Cool.
That's pretty cool. And the scrolling works and
everything.
Alright, I just wanted to quickly show the results
of this. So we're taking a copy
of the line length and then if the last character line is a newline,
do we just strip it out?
And then only copy up to that decreased
count. But still consider that we've
read everything.
In the browser code. When we view
a message, it just
erases the message rectangle now.
And with that, viewing
different messages should be working properly.
So we can view a long message.
And then view a short message and
another. [ sniff ] Okay, so that works.
I'm going to do that tab expansion, I guess because I can't
find a way to do it directly in the TextEdit
control. So I guess I have to expand those manually into tabs
myself so I will do that maybe off-camera.
I don't know it's not very fun, but, yeah,
stay tuned for the next video. Bye.